# Adding a CSS surf map

Current local batch: `ROOKIE-FIVE.md` covers Rookie, Whiteout, Citypop, Summit and Hourglass. The current compatibility checker is `scripts/certify-rookie-five-compatibility.ts --check`, preserving all twenty prior board identities. Earlier release checkers below retain historical evidence. Use `scripts/browser-rookie-five-qa.ts` for the current menu-continues-playing behavior.

This is the repeatable project workflow. Read this, `REFERENCE.md`, `NEW-MAPS.md`, and the relevant native evidence before another import. A BSP conversion alone does not establish CSS/KSF behavior.

## Ownership and invariants

One owner changes the movement/session core. Independent work can cover source telemetry, entity review, visuals, and browser QA. Keep Source X/Y horizontal, Z up, feet origins; only the renderer converts `(x,y,z)` to `(x,z,-y)`. Use the existing float32 0.015-second tick. Do not change acceleration, ramp normals, reset hulls, or velocity to make a route pass.

## Pin and inspect the source

1. Confirm CSS, forward style, 66t, main course on the public KSF map/viewer pages. A CS2 port or similarly named revision is insufficient. Record author, tier, stages/checkpoints, replay metadata and sources.
2. Pin the decompressed BSP SHA-1 and byte length. Additions to the original three maps are in `scripts/fixtures/new-maps-sources.json`. `scripts/download_new_maps.py` downloads and verifies the matching mirror files into ignored `node_modules/.map-imports/`. Cache original BSPs here; do not commit installation files or credentials.
3. Inspect entities, brush ownership, displacements, static/dynamic props, PHY availability, materials, skins, scale, filters, disabled state, duplicate outputs, map cvars, push volumes and teleports. `scripts/inspect_new_maps.py` writes compact ignored audits. A portal can advance a course or return to a stage; it is not automatically a run reset.
4. Obtain independent native reference data. `scripts/decode-new-map-replays.ts` decodes pinned public v2 records offline; `--fetch` refreshes referenced primary pages/files. `scripts/decode-nyx-reprise-replays.ts` also handles the independently documented v3 layout used by Nyx, including recorded analog commands and contact flags. Preserve provenance/hashes. Unsupported versions must not be interpreted as v2. In v2, button bits omit analog magnitude, and the view sampled after a teleport can replace the actual input angle.

## Import geometry and artwork

Use an installed, licensed CSS game directory as a **read-only** fallback asset mount. The project uses Python with NumPy/Pillow for offline conversion; Python is unnecessary at runtime.

```sh
python scripts/download_new_maps.py
python scripts/import_new_maps.py kitsune --game-dir "PATH/Counter-Strike Source"
python scripts/import_boreas_visuals.py node_modules/.map-imports/surf_kitsune.bsp --slug kitsune --map-id surf_kitsune --author Arblarg --game-dir "PATH/Counter-Strike Source"
```

The legacy Boreas and Utopia/Mesa profiles remain separate to avoid changing existing geometry exports accidentally. Add a reviewed profile to `import_new_maps.py` for further maps; the latest five classic profiles are isolated in `classic_five_profiles.py`. Retain compiled BSP bevel planes and actual PHY convexes; do not substitute visual meshes or guessed boxes. Missing PHY resources must be reported. Include full-resolution displacement collision with native flags/tags. Calculate rotated trigger bounds from their transformed convexes. Ace and Legends have explicitly audited contradictory bevel brushes: their opt-in first-six-plane fallback is only a broadphase bound, with all original narrowphase planes retained.

Original authored timer triggers are preferred where present, but KSF often replaces or augments them. Label local boxes honestly and use independent bookmarks plus physical apertures to review them. Do not claim private KSF zone boundaries were recovered. Add main-route teleports separately from reset zones and stage starts; document every map-specific override.

Check for `landmark` on each teleport. Reprise demonstrates why a portal cannot always snap to its destination: its native behavior translates the player's end-of-tick landmark-relative offset while retaining world velocity and view angles. Preserve the reviewed optional landmark metadata and compare native crossings; do not apply this rule to ordinary or staged teleports without evidence.

Visual imports preserve original geometry, model skins, static initial poses, scale, materials, lighting and texture data. They are conversions, not newly generated map artwork. Check missing materials, unsupported shaders, translucent/invisible brush entities, sky transforms, and animation limitations. Keep rendering and collision from the same pinned BSP. Public download access does not establish redistribution rights; add author/source notices without claiming a new licence.

Source makes `trigger_*` brush entities invisible during initialization, even when their material flags do not include NODRAW. Exclude them from rendered world batches, while preserving their exact collision/trigger data. Do not infer missing lighting from appearance alone: Legends has empty original LDR and HDR lighting lumps. Check both before asserting that a lightmap was dropped.

For malformed source metadata, retain an explicit audit trail. Reprise's zero BSP texture dimensions are resolved from actual VTF dimensions. Nyx requires the visual importer's explicit `--invalid-skin-policy default` for two non-solid decorative props; this records a skin-0 fallback rather than claiming verified native appearance. The default policy rejects invalid skins. See `NYX-REPRISE-VISUALS.md` for complete import commands and limitations.

## Integrate the course

- Add stable ID, slug, credit, description and stage/checkpoint metadata in `src/map/catalog.ts`. This drives selection, preference validation, build packs and online identities. Use real rendered preview images, not invented screenshots.
- Main routes belong in `public/maps/{slug}-collision.json`, visuals in `{slug}-visuals.json` and `{slug}/`, preview in `public/previews/{slug}.jpg`.
- The session owns all map effects and timing. Browser input only supplies commands. Preserve those effects in headless server replay verification. Teleport destinations never create a swept line through the world. Practice travel stays invalid; a stage return keeps the whole-run clock and command history.
- Review stages individually: Beginner preserves portal momentum; Ace has connected stages with no stage-exit speed cap; Year3000/Eclipse have observed delayed KSF handoffs. None is evidence to change another map's policy. Alternative checkpoint doors must stay a union of exact convexes, not a filled enclosing rectangle. Test every practice destination for sustained neutral ticks as well as non-solid initial placement. A practice-only settled spawn may use `practiceStage` while real portal destinations retain their authored poses.
- Put a complete command-only route in `public/replays/{slug}-complete.json`. An offline planner may choose ordinary input/view commands. It may not export injected poses, velocities or contact states as proof of playability. Prefer a canonical-spawn witness accepted by `verifyReplay`; use `scripts/canonical-approach.ts` to reach a legal stationary reference start using ordinary commands.
- Review leaderboard identity compatibility before release. Geometry and executable rule changes require versioning. Never migrate incompatible times into a new board or silently delete old records.

`fixtures/legacy-physics-compatibility.json` now preserves all ten previous board IDs through explicit source/geometry/configuration pins; the published physics hash still describes the actual current code. `scripts/certify-classic-five-compatibility.ts --check` compares 16 complete trajectories and 59,695 ticks against a frozen pre-change workspace source snapshot. The older `scripts/certify-legacy-physics.ts` still reproduces the original six Git-baseline routes, but refuses to overwrite the expanded certificate. See `CLASSIC-FIVE-COMPATIBILITY.md`. Any certified source, geometry or rule change fails the build until the certificate is reviewed again or deliberately retired. Re-running the certificate is not a substitute for reviewing whether old records remain compatible.

## Required validation

1. Declare reference tolerances before testing (current strict single-step reference tolerance: 0.002 units and 0.002 u/s). Compare native expected positions/velocities; report first divergence, maxima, missing contact flags and uncertain input fields. Do not widen tolerance after failure.
2. Run independent imported geometry/entity fixtures, stage/failure/teleport tests and existing regressions: `npm test`.
3. Run all new canonical routes through server verification and at 30/60/144/240 render schedules: `npx tsx scripts/validate-new-maps.ts`. Existing routes: `npm run validate:classics`.
4. Build production assets: `npm run build`; serve `npm run preview`. `npx tsx scripts/browser-new-map-qa.ts` tests the actual game, Pointer Lock/keyboard, every new full Watch route, exact expected state/time, restart, practice selection, menu layout and console errors. Set `UI_BASE_URL` if the preview uses another port. It mocks cloud transport and never writes live records.
5. Inspect screenshots and test sustained rendering, map-switch resource disposal, cold/warm network requests and low-resolution layout. Screenshots alone cannot prove performance or movement.
6. For uncertain Source entity semantics, use an isolated local native server and controls, as in `NATIVE-CSS-TELEPORT-PROBE.md`. Mount installed binaries/assets read-only; use loopback and shut the probe down afterward. Native stock behavior is not proof of private KSF plugins.
7. Update provenance, remaining gaps, commands, validation results, and current launch URL. Keep review local until publishing is authorized.

The source and these documents are the durable context for a new chat; a new agent should inspect the current working tree before repeating or replacing work.

Summer and Forbidden Ways are documented in `SUMMER-FORBIDDEN.md`, with isolated profiles in `scripts/summer_forbidden_profiles.py`. Summer demonstrates nearest-corner displacement hints, vector detail-texture scaling, authored arrival checkpoints and a canonical route built from a pinned alternate native record. Its unsupported class-filtered entity outputs are explicitly listed; successful traversal does not certify every optional Source effect.

Fornax's worked example is in `FORNAX.md`. `audit-nyx-reprise.py fornax` now supports an explicit source-registry selection. Entity-zone keys must match authored targetname case exactly. Review full entity output rather than a truncated console sample: `Map_start` and `Map_end` are authored Fornax triggers. Its two KSF checkpoint boxes are local corridor gates, including the full east passage of CP2. The full command witness preserves geometry and documents the thin reset-trigger difference instead of weakening the trigger to fit a recording.

Tendies uses the reviewed profiles in `scripts/easy_three_profiles.py`. Its main course requires immediate filtered player outputs; see `MAP-PLAYER-OUTPUTS.md` and `TENDIES.md`. The 17-map deployed source, catalog and prior certificate were frozen before that opt-in runtime addition. A later shared collision-reset repair has its own frozen 20-map baseline: use `certify-reset-recovery.ts --check` for the current certificate, and see the reset investigation in `SUMMER-FORBIDDEN.md`. Older certificates retain historical evidence. The texture audit resolves only `materials/` source paths, matching the importer when stock VPKs contain non-materials aliases.

Derpis and Prelude use the same reviewed easy-three profiles. Derpis preserves its early intermediate rooms and explicitly audits the later KSF stage resets; Prelude uses original timer models 60–64, including unnamed triggers. See `DERPIS-PRELUDE.md` for route evidence, the decorative material missing from Prelude’s source, and the local-versus-live release boundary.
