Return to Surf

Reference & validation

20 actual CSS maps. Independent movement implementation checked against public KSF telemetry and limited native CSS contact probes. Measured agreement does not establish universal native parity.

Updated 8 October 2026 · Map credits: Syncronyze · Panzer · Arblarg · elly · SnoopSh · SSStormy · Syncronyze & Shadow Sheep · TeMP · iNooVa · Kiiru · ArchAngel · Juxtapo · SintaxError · Paper-Cut · Hardex & Tioga060 · Krusty · granis · felix · nappa

Multiplayer character: adapted from PS2 Styled Characters by Wobble Blocks (CC0), with a repainted Surfd diffuse texture. Asset and code credits.

Movement reference & configuration

Download this document

# CSS / KSF movement reference

Updated 2 October 2026 after importing the actual CSS Boreas map and acquiring independent KSF telemetry. Target: **Counter-Strike: Source, KSF forward style, 66t**. The exact native executable build and live plugin manifest remain unknown. This document supersedes `REFERENCE-v1.md`.

Evidence labels: **Verified** means a primary source or independently recorded behavior; **Supported** means consistent with the tested CSS run and relevant implementation but not a complete live-server configuration dump; **Unresolved** remains a fidelity limit.

## Interval and branch

The simulation interval is **float32(0.015) = 0.014999999664723873 seconds**. Nominal “66 tick” means approximately 66.6667 Hz, not `1/66`. Valve declares `DEFAULT_TICK_INTERVAL = 0.015`. The public KSF Boreas replay viewer independently specifies 66.66666666666667 ticks/sec; its gravity/displacement steps support 15 ms. [Valve constants](https://github.com/ValveSoftware/source-sdk-2013/blob/b8cfb12c0e083a2ef5b2f9f9b50f3902fa034474/src/public/const.h), [actual KSF replay](https://ksf.surf/replays/surf_boreas/replay_css_4060_0_712551_1763914843.rec).

The public SDK is shared Source code, not the shipped CSS movement binary. Its HL2/TF2 defaults are not automatically CSS facts. CSS-specific branches in RNGFix and CSS-derived Momentum code are supporting references; Momentum intentionally changes several jumping, ground-probe and ramp-recovery behaviors and is not an interchangeable controller. CS:GO/CS2 movement and hull dimensions were not substituted.

Coordinates remain Source units: X/Y horizontal, Z up; origin at the feet; yaw 0 along +X, 90 along +Y. Only the renderer converts coordinates.

## Profile

| Setting | Value / behavior | Evidence |
|---|---|---|
| Gravity | 800 | Supported by native KSF ballistic velocity and displacement steps. |
| Air acceleration | 150 | Verified in Boreas's compiled `logic_auto`; native air steps support the chosen formula. |
| Ground acceleration / friction / stopspeed | 5 / 4 / 75 | CSS-derived reference; independently matches 65 recorded ground/jump transitions. |
| Held command magnitude | ±400 per axis | Shared CSTRIKE input branch; diagonals crop to max wish speed. Native initial keydown may be fractional. |
| Maximum wish speed | 260 | CSS-derived normal surf profile, supported by ground and air telemetry; not a cap on momentum. |
| Air projection cap | 30 | CSS-specific RNGFix; acceleration term retains uncapped wish speed. |
| Velocity limit | ±5000 per component | 5000 verified in compiled Boreas; per-axis semantics from movement references. No total-vector clamp. |
| Standing hull | (-16,-16,0) to (16,16,62) | CSS-specific RNGFix branch. |
| Crouched hull | (-16,-16,0) to (16,16,45) | CSS-specific branch; ±8.5 airborne origin transition. |
| Standing / duck eye | 64 / 47 | CSS-derived view vectors; eyes intentionally exceed hull height. |
| Jump impulse | float32(sqrt(2×800×57)) = 301.9933776855469 | CSS-specific predictor and measured first crouch jump. |
| Jump order | Standing adds impulse after initial half gravity; ducking sets impulse. Jump adds its own half-gravity step. | CSS-specific predictor; native crouch-jump fixture. |
| Step / ground probe | 18 / 2 | Shared movement and CSS-specific predictor. |
| Ground classification | Normal Z ≥ .7; reject if upward velocity >140 | Source/CSS reference and boundary fixtures. |
| Duck speed / transition | .34 command scale; .4 s ground duck, .2 s unduck; immediate air hull transition | CSS-derived reference, telemetry supports tested transitions. Spam/reversal timing remains simplified. |
| Surface friction | Ordinary 1; unsupported slow rise .25; ground surface factor capped at 1 | Source categorization; native air fixtures confirm the .25 transition. Ramp PHY material metadata is retained; full CSS surface-property lookup remains unresolved. |

Primary mechanics: [Valve shared movement](https://github.com/ValveSoftware/source-sdk-2013/blob/b8cfb12c0e083a2ef5b2f9f9b50f3902fa034474/src/game/shared/gamemovement.cpp), [CSS-specific RNGFix predictor](https://github.com/jason-e/rngfix/blob/9831d25e9f6747566a6adc72d75ae3fc671a656c/plugin/scripting/rngfix.sp), [CSS-derived mode constants](https://github.com/momentum-mod/game/blob/9da88b97769e0f2306623946ebbcb5d0f919a1f0/mp/src/game/shared/momentum/mom_system_gamemode.cpp), [input generation](https://github.com/ValveSoftware/source-sdk-2013/blob/b8cfb12c0e083a2ef5b2f9f9b50f3902fa034474/src/game/client/in_main.cpp).

## Order and collision

Tick order: input cropping/ducking; StartGravity; jump; grounded friction; acceleration; swept movement/step handling; CategorizePosition; FinishGravity; grounded Z clearing. Four movement bumps consume remaining time; they do not create extra acceleration or gravity ticks. Multiple planes clip velocity and may constrain motion to a crease. StepMove compares lower and up-forward-down paths. No steering, ramp attraction or arbitrary speed preservation is used.

Collision uses the player's swept axis-aligned hull. The real BSP supplies compiled brush planes and bevels. VPHY files supply original convex ramp decompositions, converted from IVP metres and transformed by authored prop matrices. Displacements use the actual decoded triangles and flags. A stable-order BVH reduces candidate queries without changing narrowphase order.

BSP collision rejects sweeps that remain outside one plane. Its 1/32-unit contact margin backs up an actual crossing. VPhysics skin handling differs: shallow approaches within the margin are allowed, but a real convex exit inside the finite sweep must not be preceded by a padding-created corner intersection. Actual KSF seam/departure fixtures distinguish these cases. This is an independently implemented collision contract supported by measured behavior, not a copy of proprietary VPhysics. Remaining native precision/contact details are reported by the full comparison.

## KSF and map rules

**Current browser/ranked default (3 October 2026):** hold-jump auto-bhop with capped starting speed (`css-surf-capped-1`). Unrestricted starts remain an explicit unranked local option; the former open-start profile is retained for old replay playback. These settings are independently versioned in personal-best/replay keys and do not change gravity, air acceleration, ground friction or the surf clipping formula. Stamina and vanilla bunnyhop speed penalties are omitted as a surf-profile choice; a live KSF manifest is still unavailable.

The current ranked rule applies the existing end-tick start-exit XY clamp: 325 u/s if grounded, 350 if airborne. It preserves vertical velocity and XY direction and never increases sub-cap speed. Jumping in the start zone remains available. The complete exit tick is displaced before the clamp; a prespeed approach can therefore affect the fractional exit tick, rather than being capped continuously inside the zone. Subsequent surfing has no such horizontal cap.

**Evidence boundary:** the public Boreas telemetry verifies a falling-start 350 clamp despite an earlier crouch jump. A real-trigger one-step test against native frames124→125 gives zero position error and 0.0000432 u/s velocity error within predeclared .002/.002 tolerances. The [2013 KSF announcement](https://steamcommunity.com/groups/SurfTimerUpdates/announcements/detail/1513501526513859534) instead describes 325 for ordinary zones, 350 for falling-start zones, 270 after prehopping and a four-prehop limit. It does not say that every airborne player gets 350. Our grounded/airborne fallback across the three maps is a documented **Surfd ranked rule**, not established universal current KSF behavior. No blanket start-zone jump ban is claimed.

**Reference option:** manual jumping plus observed Boreas falling-start cap. This recording contains a prestart crouch jump, followed by an end-of-tick horizontal clamp to **350 u/s** on start exit. The older 2013 KSF announcement's 270 prehop / four-hop rules therefore cannot be generalized to this present Boreas case. Grounded 325 remains a historically supported fallback, not newly measured here. [KSF historical announcement](https://steamcommunity.com/groups/SurfTimerUpdates/announcements/detail/1513501526513859534), [KSF commands](https://ksf.surf/commands).

Boreas itself issues `sv_airaccelerate 150`, `sv_maxvelocity 5000` and `sv_enablebunnyhopping 1`. The last setting removes a vanilla restriction; it does not by itself mean global hold-jump automation. Actual KSF server overrides remain possible. See [import evidence](BOREAS-IMPORT-RESEARCH.md).

The map's original no-jump box uses `player_speedmod 0.9999`, suppresses jump, and scales **movement frametime** by .9999. The authoritative timer still advances by one 15 ms tick. Immediate exit restoration is implemented; the map's additional .03/.06-second repeated restores have no separate effect in this static trigger flow. Native recorded gravity steps independently show the tiny scale. [Valve speed modifier entity](https://github.com/ValveSoftware/source-sdk-2013/blob/b8cfb12c0e083a2ef5b2f9f9b50f3902fa034474/src/game/server/player.cpp), [ProcessMovement](https://github.com/ValveSoftware/source-sdk-2013/blob/b8cfb12c0e083a2ef5b2f9f9b50f3902fa034474/src/game/shared/gamemovement.cpp).

An evidenced **neutral uphill landing fix** is enabled: for a descending/slow-rising approach to a walkable nonflat incline, moving uphill, where clipping would not increase horizontal speed, movement ends at the first contact plus .1 Z, retains incoming XY, and categorizes ground. This matches native Boreas tick 2756 and the conditional RNGFix PreventCollision behavior. It never applies to steep surf faces. Other RNGFix/MomSurfFix corrections are not assumed installed or reproduced wholesale. [RNGFix primary implementation](https://github.com/jason-e/rngfix/blob/9831d25e9f6747566a6adc72d75ae3fc671a656c/plugin/scripting/rngfix.sp), [MomSurfFix](https://github.com/GAMMACASE/MomSurfFix/tree/2dda7ae7e2e1dc9da2a43fcaca642f6200d8401e).

## Additional classic-map profiles

Utopia NJV and Mesa Fixed retain the same 15 ms movement core, gravity and air acceleration. Their public KSF recordings support a **3500 u/s per-component** velocity cap; this is an inferred server profile, exported as an explicit map override, not an authored BSP command. Boreas retains its established 5000 profile. Source's limit is not a total-vector speed clamp.

Mesa's authored continuous push contributes base velocity during movement and the Source exit carry when the player leaves. This is a real map entity, not an arbitrary boost added to make a route work. Fifteen native recorded push transitions independently verify the implemented behavior; see [push reference](MAP-PUSH.md).

Stock installed CSS build 11003710 directly confirmed the 62/45-unit standing/crouched hulls and full 0-based trigger bounds. It also reproduced several reset contacts that KSF recordings pass through. The imported authored resets are retained; no unsupported hull shrink is applied. Exact KSF server/revision differences remain unresolved. See [native probe](NATIVE-CSS-PROBE.md), [map provenance](CLASSIC-MAPS.md), and [comparison results](VALIDATION.md).

## Mouse and camera

Mouse events update view immediately: `degrees = counts × .022 × sensitivity`, no frame-time factor or smoothing. Raw Pointer Lock is requested, with adjusted-input fallback. Pitch is ±89°. Device/browser raw counts need not equal native CSS counts on every system. Source FOV90 converts to vertical73.739795°, giving horizontal106.260205° at16:9. No banking or speed-driven FOV is added. Eye position follows the hull/duck state. Positional interpolation defaults on and blends the previous/current simulation positions and eye heights, adding one tick (about 15 ms) of visual delay; mouse angles remain immediate. A one-time preference migration enables interpolation for all returning players, including those with it saved off. The migration is recorded on first load; later deliberate opt-outs persist. Other saved preferences and records are retained. This presentation setting is allowed on leaderboards in either mode and is absent from physics configuration, replay commands and record identities. Changing it does not itself invalidate a run; opening the pause menu during an active run still does.

[Valve mouse path](https://github.com/ValveSoftware/source-sdk-2013/blob/b8cfb12c0e083a2ef5b2f9f9b50f3902fa034474/src/game/client/in_mouse.cpp), [FOV conversion](https://github.com/ValveSoftware/source-sdk-2013/blob/b8cfb12c0e083a2ef5b2f9f9b50f3902fa034474/src/game/client/view.cpp).

The browser samples held keyboard inputs at full400 immediately. Source's client-frame `KeyState` can generate fractional initial/released commands. The native replay lacks analog command magnitudes; its first forward press is reconstructed as200 from independent velocity evidence. This remains an explicit input-generation difference.

## Precision, validation and limits

State and selected intermediates use float32; JavaScript trigonometry, plane construction and some trace arithmetic remain double. The native replay contains positions/velocities/buttons/angles but no contact flags, hull-transition timers, build ID or plugin manifest. Full-course comparisons report their actual errors against predeclared tolerances rather than equating determinism with native parity. See [validation](VALIDATION.md) and [independent report](REVISION-VALIDATION.md).

Unresolved beyond the tested run: exact native build/plugins, some VPhysics/displacement edge ordering, tiny float/trace residuals, complete duck-spam behavior, native client prediction/step camera, fractional keyboard command generation, exact KSF timer internals, all map-entity dynamics, water/ladder movement and combat modifiers. No experienced human CSS playtest is claimed.

## Licences and provenance

The default course now uses the actual CSS BSP, not the earlier original map. Assets retain their authors' rights; no general redistribution licence was established. They were imported for this requested local game. The code is independently written from behavioral/file-format references. Valve's SDK licence is Source-specific; RNGFix and MomSurfFix are GPL; their implementations are not copied into this project. See [notices](../THIRD_PARTY_NOTICES.md), [Valve licence](https://github.com/ValveSoftware/source-sdk-2013/blob/b8cfb12c0e083a2ef5b2f9f9b50f3902fa034474/LICENSE), [RNGFix licence](https://github.com/jason-e/rngfix/blob/9831d25e9f6747566a6adc72d75ae3fc671a656c/LICENSE).
Validation & remaining gaps

Download this document

# Classic Surf validation and remaining gaps

Updated 2 October 2026. Three original CSS maps are available: Boreas, Utopia NJV and Mesa Fixed. The previous single-map report is retained as `VALIDATION-v2.md`; Northline remains a regression fixture and a source for the authored laboratory ramps.

## Current release

The final automated suite passes **162/162 tests**, with no failures or skipped tests. All three shipped default-profile command witnesses complete their entire course with authored resets active and no assists:

| Map | Commands | Local run time | Ordered checkpoints | 30/60/144/240 FPS |
|---|---:|---:|---:|---|
| Boreas |2758|39.495360 s|2|Every tick identical|
| Utopia NJV |3867|53.349069 s|3|Every tick identical|
| Mesa Fixed |3653|52.635008 s|3|Every tick identical|

Each starts stationary at a legal point on its start deck and is verified through the complete `GameSession`. No intermediate position/velocity edits, scripted boosts, skipped resets or relaxed collision bounds are used. Mesa also has a separately completed 3598-command alternate. The shipped route's development and scope are documented in `MESA-ROUTE-WITNESS.md`; the full deterministic report is `fixtures/classic-playability-results.json`.

Final production Edge checks against `http://localhost:4173` passed **35 checks, zero failures**: Boreas controls/game flows 17/17; classic selection, lab, graphics and audio 9/9; replay-rule restoration 3/3; all three complete browser replays and restarts 6/6. There were no console errors or warnings. One renderer-only marker inspection is explicitly omitted from the production suite because it imports development modules; it already passed separately at 15 native approach-camera views. No gameplay check was skipped. Reports are `fixtures/browser-boreas-results.json`, `browser-classic-results.json`, `browser-replay-state-results.json`, and `browser-classic-replay-results.json`.

Every served replay hash matched its validated source. The actual browser reached every finish with exactly the expected final state, splits and elapsed time, and Watch never wrote a PB. Live raw mouse events initially exposed assumptions in the browser test, not a runtime defect: the final mouse oracle uses a passive event ledger aligned to actual simulation commands, keeping its original 1e-6-degree tolerance. WASD is evaluated relative to the received view angles; manual wheel-jump tests wait for a sampled release before the next press. The initial failed report is retained for investigation. These browser tests establish event behavior, not physical input-to-photon latency or all-case CSS parity.

Simulation remains fixed at float32 0.015 seconds. Boreas movement and its saved command trajectory are unchanged by the renderer work. Prop batching, conservative visibility culling and shared buffers reduce average Boreas draw calls from 221.3 to 70.8. At DPR 1, three comparison views have zero differing pixels; mean CPU submission fell from .497 to .264 ms. Balanced rendering caps the drawing buffer at DPR 1 without changing FOV, input, physics or timing. These are measured rendering costs, not a promised FPS multiplier. See `RENDER-PERFORMANCE.md`.

The practice facility has eight stationary, untimed stations. Its command witness traverses both surf faces and the transfer: 232 first-ramp contacts, 137 second-ramp contacts, peak speed 1735.13 u/s. Separate checks exercise jumping, stairs, crouch clearance and collision. It renders in roughly 11–18 draws. See `LABORATORY.md`.

Browser checks cover procedural wind's gesture gate, pause/suspend lifecycle, environment, persistent volume/mute, map switching and resource disposal. Green start and gold finish markings project onto landing floors without changing timer geometry. Placement was inspected at 15 native route-camera views. Independent review fixed demonstration-profile leakage and a stale-rAF timestamp race; the dedicated Edge regression passes 3/3 checks with no console errors. See `INDEPENDENT-REVIEW.md`.

`tests/classic-playability.test.ts` and `scripts/validate-classics.ts` require legal stationary starts, normal commands, every checkpoint, a finish, and no resets or assists. Every tick's state, contacts and timer is compared at 30/60/144/240 FPS with zero tolerance and first-divergence diagnostics. Missing fixtures fail. These tests establish playability and deterministic scheduling, not independent native parity.

## Utopia and Mesa independent evidence

Native public KSF recordings, source URLs and hashes are in `fixtures/{utopia,mesa}-ksf-telemetry.json`. The movement-only comparison keeps the existing .002-unit / .002-u/s tolerances. It deliberately excludes reset/timing events because these KSF recordings cross several authored reset edges. It is not the playable demonstration or an all-systems completion test.

| Map / comparison | Steps | Max position error | Max velocity error | First divergent tick |
|---|---:|---:|---:|---:|
| Utopia isolated |3868|.004854|.088105|731|
| Utopia continuous |3868|.088736|.132074|380|
| Mesa isolated |3395|33.549978|2236.146946|521|
| Mesa continuous |3395|12.878937|244.532538|234|

Mesa's large isolated outlier is retained: recorded states around 521–523 begin slightly inside a downloaded compiled playerclip surface, producing an all-solid stop in a subsequent local step. That origin is approximately .05 units beyond the BSP plane. Continuous traversal passes this area but has several contact-timing differences. These errors are not declared parity or hidden by loosening tolerances. Native contact flags are unavailable, preventing a direct contact-state comparison. `scripts/classic-movement-probe.ts` regenerates these reports.

The new recordings support a 3500 u/s per-component velocity limit, exported as an explicit map profile override; Boreas retains 5000. Mesa's authored push uses Source base velocity. Fifteen native entry/inside/exit steps pass the strict tolerances, with maximum .001138 units and .000223 u/s error; see `MAP-PUSH.md`.

An isolated real CSS server (build 11003710) measured standing Z 0–62 and crouched 0–45 hulls and reproduced the disputed resets at supplied native-recorded points. The browser therefore retains authored reset geometry and full hulls. The KSF server/revision difference remains unresolved. These short native contact probes do not prove whole-run parity. See `NATIVE-CSS-PROBE.md` and `CLASSIC-MAPS.md`.

The following Boreas measurements remain the previously established independent baseline; earlier release counts below describe that baseline.

## Boreas independent native evidence

The public KSF CSS forward-style run by .x (23 November 2025, published 39.495121 s) supplies 2,960 recorded frames, including 2,758 movement steps from stationary start through finish. Comparison uses original BSP/PHY/displacement geometry, buttons from frame i, view angles from i+1, a reconstructed half-strength first keyboard press, the observed 350 u/s start clamp, the map speed modifier and the evidenced incline fix. No native build/plugin dump or contact flags are available.

Predeclared comparison tolerances remain **.002 units position / .002u/s velocity**. They were not widened after seeing results.

| Comparison | Steps | Maximum position error | Maximum velocity error | First tick outside tolerance |
|---|---:|---:|---:|---:|
| Ground acceleration and first crouch jump, equivalent flat geometry |65|0|.00003146|None|
| Selected native ballistic air transitions |1672|.00048828|.00088027|None|
| All isolated native steps on actual map |2758|.00690534|.00374460|334|
| Continuous native command reconstruction on actual map |2758|.03162542|.00614367|199|

The isolated comparison resets to each recorded starting position/velocity; it verifies local movement, not whole-run drift. The continuous comparison begins once at the legal stationary start-deck position and then uses commands and explicit server/map rules only. Its final position error is .01162444 units. Twenty-seven isolated steps exceed the strict tolerance. Full per-tick errors are retained in `fixtures/boreas-reference-validation.json`; near agreement is not declared exact parity. Native grounded/contact-state differences cannot be measured from this recording.

Regression fixtures explicitly cover the native curved seams, two BSP ramp departures, the shallow skin contact, and the neutral uphill landing plus manual follow-through. They caught and helped correct false epsilon contacts and the distinction between compiled brushes and VPhysics hulls. The public replay and sources are documented in `REVISION-VALIDATION.md`.

## Playability and determinism

Both profiles finish the actual two-checkpoint course using normal view/button commands:

- Reference-style run: approximately 39.495235 s.
- Requested auto-bhop / open-start run: approximately 39.495360 s.

Both touch 103 distinct solids, cross CP1 at tick 1231, CP2 at 2303, and finish at 2758, with no reset or invalid record. The requested run's initial approach is fitted offline by changing seven view angles; it does not edit state or velocity. Playback contains only recorded commands and has no steering controller. Both fixtures start stationary at a legitimate location on the original start platform, matching the independent recording rather than the center teleport destination. They are explicit fixture initial states, not mid-run teleports.

All **116 automated regression tests pass**, with no skipped tests. `tests/boreas-playability.test.ts` compares every state, contact and timer event for the full requested course at 30/60/144/240 FPS. Physics and timing remain identical. Separate tests cover actual teleport-destination spawning, convex trigger unions, high-speed checkpoint crossing, practice/restart behavior, jump/wheel semantics, focus cleanup and record versioning. Regression passes do not override the explicitly reported native comparison residuals above.

## Geometry and visuals

Collision comprises 178 compiled BSP brush solids, 148 original PHY convex instances, and 129,728 displacement triangles. The importer honors player hull flags and includes the map's original nonrectangular reset unions. Independent raw-PHY transform checks and native deck height support scale/orientation. BVH candidate membership/order is checked against a separate linear oracle.

Visual import contains 1,783 visible BSP faces, 1,125 displacements, 26 model types and 1,587 original props, with original material coordinates, textures, lightmaps, vertex lighting and cubemaps. `fixtures/boreas-asset-validation.json` checks finite/index-valid assets and exact shared displacement decoding. This is internal geometry integrity, not CSS pixel parity. See `BOREAS-VISUALS.md` for unsupported material/effect details.

## Browser verification

The final production integration run passed **17/17 checks**, with zero skipped checks, console errors, warnings or failed requests. It used installed Edge 154 with real Pointer Lock, keyboard, mouse and wheel events against `http://localhost:4173`. The game-state seam is read-only. Checks cover launch, spawn settling, movement/crouch, raw-count camera scaling, configurable wheel/key bindings, numeric sensitivity, persistence, auto-bhop/manual styles, focus/menu/restart, practice saves, map switching, completion, record protection and resizing. The complete command replay finished in 39.495359573 s with ordered splits of 16.594048415 and 32.681057494 s. Final results are recorded in `fixtures/browser-boreas-results.json`; inspected screenshots are in `docs/screenshots/boreas/`. This headless browser validation establishes event flow and functional behavior, not subjective human feel or physical input latency.

## Remaining fidelity gaps

1. Exact native CSS build and current KSF cvar/plugin manifest remain unknown. Global hold-jump auto-bhop and open prestarts are requested local rules, with separate records; they are not advertised as universal KSF normal-style rules.
2. Small native float/contact differences remain, as quantified above. VPhysics/displacement edge filtering, contact ordering and all arbitrary approach cases are not proven identical.
3. The authored .9999 no-jump modifier is modeled from hull overlap. Native telemetry suggests a small entry/I/O scheduling difference; it is reported rather than hidden by changing trigger bounds.
4. Native client-frame fractional keyboard commands, every partial-duck reversal/spam case, step-camera adjustment, and native client prediction are not fully reproduced. Pointer Lock counts also depend on browser/device support.
5. The local timer sweeps actual trigger geometry. Its checkpoint fractions differ slightly from KSF bookmark/display splits; exact KSF timer internals are unavailable. Local records are not KSF-comparable records.
6. Some Source material passes and map effects remain simplified: directional bumped lighting, reflection/refraction/water details, HDR, proxies, animation, particles, sounds and two unavailable stock textures. Actual traversal geometry and original assets are present.
7. Local native CSS testing now covers limited hull/trigger contact probes only. The user has provided positive subjective movement feedback, but there is no controlled experienced-player parity study or whole-route native harness. Reference recordings cannot establish all-case equivalence.

## Reproduce

`npm test` runs automated regressions. `npm run validate` regenerates both normal-command witnesses and the native comparison report. With a local preview running, `npm run test:browser` performs browser integration checks; set `SURF_QA_URL=http://localhost:4173` for production. Offline importer/visual checks are documented alongside their scripts. Map and physics versions namespace records and replays.
Front menu, sensitivity & turn binds

Download this document

# Front menu and precise controls — 5 October 2026

## Sensitivity

The old 0.1 lower bound was a browser settings/storage restriction. The numeric field now accepts finite values from zero to 20, with no decimal-place rounding or positive minimum. Examples: `0.001`, `0.00000001`. Zero turns off mouse look without disabling turn keys. The slider remains a convenient coarse adjustment; its 0.01 steps never quantize values entered in the numeric field. Invalid or empty edits restore the last valid value.

The existing 0.022 degrees per count × sensitivity relationship remains unchanged. No frame-duration scaling, acceleration, filtering or easing is applied to mouse events. A yaw already inside the normal range avoids modulo arithmetic to reduce cancellation at tiny sensitivities. Preferences keep the existing v3 key and migrate older records with turn binds disabled and unbound.

## Turn binds: reference and evidence boundary

Reference: Valve's public [Source SDK client input](https://github.com/ValveSoftware/source-sdk-2013/blob/master/src/game/client/in_main.cpp), specifically `KeyState`, `DetermineKeySpeed`, `AdjustYaw`, `CreateMove`, and `ExtraMouseSample`. Code was independently implemented; Valve code is not copied or distributed.

The referenced default yaw speed is 210 degrees/second, bounded at ±100000. Left adds yaw, right subtracts; held opposing buttons cancel. The reference button-state weights are: new press 0.5, held 1, release 0, press/release 0.25, release/repress 0.75. Repeats are ignored and each action tracks up to two physical keys. Mouse and keyboard yaw contributions add independently. Wheel turns act as press/release pairs. These properties have direct, analytical test expectations.

Browser button impulses are consumed once per authoritative 15 ms simulation command. A read-only camera prediction covers the accumulator remainder, so visual turns can update between commands without extra movement ticks. Final view angles are recorded in the existing replay format; server verification and physics identities are unchanged.

**Not certified exact CSS client parity:** the SDK is shared-engine evidence. Native CSS can divide keyboard sampling between command creation and render-time extra samples. This implementation deliberately uses a deterministic tick boundary, so an edge can differ by a sample from native CSS. No native CSS client input telemetry was captured for this change. Legacy `+strafe` / `+speed` modifiers and console aliases are not implemented. Mouse-device/browser delivery and floating-point angle precision also prevent a universal bit-identical claim.

Settings → Controls places the standard key bindings first, followed by a full-width Turn binds disclosure. Its 66 px click target has a border, hover state, On/Off status and an expand arrow; the title remains 11 px. Only enabled users see left/right bindings, the cycle binding and yaw speeds. Disabling retains saved bindings but clears held keys and ignores them during play. Focus loss, pointer-lock release, rebinding and restart also clear turn state.

Users can save up to eight distinct yaw speeds in their chosen order, edit or remove them, and select an active speed. At least one remains. The optional **Cycle yaw speed** binding starts unbound, advances once per press (key-repeat does not advance it), and wraps from the last preset to the first. Mouse buttons and wheel bindings also work. The current rate and preset number appear in a small HUD readout only while turn binds are enabled, and are hidden during replay playback.

The preset list and selected speed persist in the existing v3 preferences. A saved single speed migrates to a one-item list without changing its value. Invalid, duplicate and non-finite stored values are sanitized; invalid edits show a message and keep the prior valid value. Reset controls disables turn binds, clears all three turn bindings, and restores a single 210°/s preset.

Cycling is a view-input operation, equivalent in intent to switching cl_yawspeed with user bindings: it changes the rate without releasing held turn or movement keys, adding physics ticks, or invalidating a timed run. The resulting view angles remain fully recorded and subject to the existing server replay verification. This is a browser convenience, not a full Source console/alias implementation. Editing the list, sensitivity, FOV or bindings in Settings also preserves eligibility; the live menu continues recording neutral commands while controls are released.

## Front menu

The initial screen is a course browser with consistent Settings, Leaderboard, Account and Practice navigation. It uses actual renderer screenshots, captured by `scripts/capture-map-previews.ts`; all three JPEGs total approximately 256 KiB. The previews retain the original map/asset ownership and notices.

One last-played or URL-selected course preloads. Before joining, no simulation runs, ambience plays, or continuous 3D rendering happens on the front menu. Selecting another card changes only its preview. A single **Join map** click captures the mouse immediately, loads the selected course and enters automatically. Capturing before the download preserves browser activation even on slow connections; simulation and audio remain paused during loading. Escape or focus loss cancels entry without recapturing the mouse when loading finishes. Failed downloads release the mouse and offer **Retry join**. The current single-course renderer is disposed when replaced. In-game Escape opens the compact live menu; Change map opens the course browser over the running game. Opening these menus, Account or Settings releases controls while momentum, time and command recording continue. The game remains visible behind them, with the current time and ranked status in the menu. A finish saves once without closing an open panel. Hidden tabs and long frame stalls still interrupt ranked runs. Guest finish restoration and shared replay entry still take precedence over the initial front menu.

## Validation

The large featured preview cycles through all maps every six seconds. The map
grid remains fully available. Rotation changes only menu presentation: it does
not load another course, change the URL, advance physics or play ambience.
Hovering the feature (including Join) pauses it; manual selection, keyboard
focus, joining or a load failure stops it until Auto is selected. Previous/next
buttons also choose a stable map. Loading, hidden pages, focus loss, Settings
and account dialogs do not advance the carousel. Reduced-motion preferences
and explicit map/replay links start with rotation disabled. Join always uses
the visible map and keeps the existing one-click loading flow.
Preview artwork uses a 700 ms eased crossfade between two image layers; the
title, details and Join control stay in place. Incoming images are decoded
before switching, and rapid choices coalesce to the latest preview after the
current blend finishes. Reduced motion uses an immediate image swap. Hiding
the menu cancels its animation; no scene rendering or map loading is added.
`scripts/lobby-rotation-qa.ts` checks a complete real-time nine-map cycle,
hover/pause/focus behavior, selection and joining, responsive header layout,
reduced motion and direct links without modifying player state.

The one-click joining revision is checked by the same browser suite: delayed
downloads outlasting transient activation, immediate mouse capture, paused
simulation during loading, direct entry, Escape cancellation, failed-switch
mouse release and direct retry. The suite also retains the settings, controls,
responsive layout and preloaded-map checks below. No movement rules change.

- `npm test`: 307 passed, including all existing course witnesses, physics fixtures and online tests. Input fixtures cover preset migration, validation, ordering/wraparound, zero/negative/fractional rates, held-key preservation, key repeat, mouse/wheel cycling, and disabled/unlocked controls.
- `npm run test:lobby` against the production preview: twelve browser checks covering initial state, lazy map selection, tiny/zero sensitivity, saved/unbound turn keys, preset editing/removal/persistence, native keyboard turning and cycling under Pointer Lock, navigation/focus, all three joins, six viewport sizes, resetting controls, and failed-download recovery. Cycling preserves run validity and produces the expected recorded yaw increments at each selected rate. Only the deliberately failed downloads produce console errors.
- `scripts/ui-review-qa.ts`: 26 passing checks for settings, account, leaderboard, finish and sharing flows. `tests/guest-game.browser.ts`: two passing checks for restoring a completed guest run, including a different selected map and verification completing before the map loads.
- `scripts/browser-classic-qa.ts`: nine passing checks for course joins, actual movement input, audio, persistence, repeated map replacement, and all eight practice stations. The optional marker-only pass was skipped because zone geometry is unchanged. No console errors or warnings.
- `scripts/browser-classic-replay-qa.ts` against the production preview: all six checks passed. Boreas (2758 commands), Utopia (3867), and Mesa (3653) completed through ordinary recorded commands, then restarted into normal play. Finish tick, time, checkpoint splits and complete final player state exactly match the headless course witnesses; replay playback never changes personal records. No console errors or warnings. This is a regression check, not new evidence of native CSS parity.
- An initial development-server replay check hit a 717 ms frame stall during concurrent browser/development work and correctly paused playback. The isolated production-build rerun completed all three courses; the stall protection and comparison tolerances were not relaxed. A stale finish-text assertion was updated to the existing UI wording.
- Production build and type checking passed. The existing Three.js bundle size advisory remains; no new runtime dependency was added. The front menu and settings were visually inspected at desktop and narrow widths.
- Turn command sequences are exactly equal at 30/60/144/240 FPS. Source button-weight fixtures use exact expectations; yaw-rate comparison tolerance is 1e-10 degrees in unit tests and 1e-9 degrees in the isolated browser check. The browser rate check sets mouse sensitivity to zero to exclude real recentering mouse events rather than widening its tolerance.
- Browser reports and review screenshots: `test-results/lobby-input-qa.json`, `test-results/lobby-*.png`, and `test-results/settings-*.png` (local generated evidence).

No new maps, movement formula changes, production deployment or account migration are part of this update.
Four additional CSS maps and validation

Download this document

# Demise, Kitsune, Aircontrol and Lux

Added locally on 5 October 2026. These are conversions of the original pinned CSS BSPs, with their geometry, textures, props and baked lighting. They are not approximations built from video or CS2 ports. All use the existing independent CSS movement simulation and float32 0.015-second interval.

| Map | Author | Main course | Component velocity limit | BSP SHA-1 |
| --- | --- | --- | --- | --- |
| surf_demise | elly | Linear, 2 checkpoints | 5000 | 15b16c315649d25420f25517e47454497f795b0c |
| surf_kitsune | Arblarg | 9 stages | 3500 | 41ff082a485d5b4a306589205187bc730ca4b2ac |
| surf_aircontrol_ksf | SnoopSh | Linear, 5 checkpoints | 10000 | 3c3b754ffb0f02b3d4a1506c8ffebdca5d18e902 |
| surf_lux | SSStormy | Linear, 3 checkpoints | 3500 | 54745509ca87f2616311a0b29e4774e80b566067 |

Primary course evidence: [Demise](https://ksf.surf/maps/surf_demise?game=66t&mode=fw), [Kitsune](https://ksf.surf/maps/surf_kitsune?game=66t&mode=fw), [Aircontrol](https://ksf.surf/maps/surf_aircontrol_ksf?game=66t&mode=fw), [Lux](https://ksf.surf/maps/surf_lux?game=66t&mode=fw). Pinned downloads come from the [KSF/OuiSURF mirror index](https://main.fastdl.me/maps_ksfthings.html). See `NEW-MAP-TELEMETRY.md` for exact replay URLs, players, dates and hashes.

Demise's 5000 override appears in the BSP and KSF viewer metadata. Aircontrol's viewer reports 10000 and its native samples exceed 3500. Kitsune/Lux viewer value 0 means unspecified, not zero; their repeated exact 3500 component plateaus support the default. No total-vector clamp was introduced.

## Map-specific behavior

**Kitsune:** red, orange, yellow, green, teal, blue, purple, pink, white. Public KSF samples directly show different spawn positions from the BSP destinations, zeroed horizontal velocity at stage arrival, and a one-tick handoff. The browser reproduces that observed handoff and stage spawns; it holds the view at the exit instead of rendering the recording's temporary `(0,0,n*1000)` coordinates. The whole-run timer includes later stage preparation. Failure volumes return to the corresponding stage with the timer still running. That convenient failure policy is not independently established as exact KSF failure semantics. R restarts the whole course; individual stage selection is explicitly practice-only. Stage exits use the documented local 325/350 start cap; exact private KSF rules remain unavailable.

**Lux:** both authored internal portals preserve world-space momentum, replace view with the destination angle, and preserve the crouched hull without a Z offset. Native CSS placement probes confirm position/velocity for standing and crouched players. Timer checks consider only actual swept movement, never the line connecting portal locations.

**Aircontrol:** only main-route filter semantics are enabled. The overlapping bonus speed modifier is not applied to the main route. Its canonical spawn is the reviewed start-deck destination used for local play. The authored finish triggers approximately 13 ticks after the reference KSF finish bookmark, so local times must not be presented as exact KSF times.

**Demise:** original brush ramps, displacement terrain and actual prop PHY collision are imported. Props without a native collision file are explicitly reported, never assigned invented collision. Native CSS confirms the version-49 tree models do load; their initial poses/skins/scales are rendered. Local checkpoints follow course apertures and independent replay landmarks. Bonus and secret modes are not offered.

## Playability evidence

Every route below starts at the unchanged shipped canonical spawn with zero velocity, supplies only legal per-tick movement/view commands, crosses every required checkpoint in order, and is accepted by the same server verifier used for online records. Offline planners are not live steering assistance and these automated witnesses are not human leaderboard records.

| Map | Commands including preparation | Local finish time |
| --- | ---: | ---: |
| Demise | 3244 | 37.173847 s |
| Kitsune | 6333 | 91.940469 s |
| Aircontrol | 3312 | 35.686688 s |
| Lux | 2827 | 31.267094 s |

`fixtures/new-map-command-validation.json` records exact per-tick agreement in position, velocity, contacts, events and time at 30, 60, 144 and 240 render FPS. `public/replays/` contains the playable Watch routes. Command-only approaches precede the recorded launch where necessary; start preparation is excluded by the start trigger, not subtracted afterward.

Aircontrol's original 2534 isolated native movement steps have maximum position error 0.001465 units and velocity error 0.000881 u/s, within the predeclared 0.002 tolerances. This does not establish whole-map parity: continuous errors accumulate, native contact flags are absent, and timer zones differ. Demise's stationary alternate route completes with small but nonzero contact/float differences. Kitsune requires one reconstructed half-strength strafe input because public button bits omit command magnitude. Lux's native line hits an authored reset boundary in this BSP; a bounded, ordinary yaw adjustment clears it. The reset hull was not shrunk to force agreement.

## Release checks

`npm test` passes all 343 tests, including imported geometry, independent native movement samples, thin portals, ordered stages, practice/replay restoration, unchanged legacy identities, and texture delivery. The three existing maps' six complete legacy/ranked command routes also match committed baseline code exactly across 20,556 ticks. Their previous leaderboard identities remain intact through an explicit, fail-closed compatibility certificate; all catalogs still publish the true current simulation source hash.

The production preview also completed every full Watch route in an actual Edge browser. Each final player state, elapsed time and split array exactly matched the independently run headless fixture. Checks included real Pointer Lock, keyboard movement/restart, pause, seven-map switching, all nine Kitsune practice choices, finish screens that do not save replay records, and menu layout at 1440, 1280 and 390 pixels wide. No browser console errors were recorded. Full-game samples held roughly 360 FPS on this machine; that is not a guarantee for other hardware. Results are retained in `fixtures/browser-new-map-results.json`, with screenshots in `docs/screenshots/new-maps/`.

Original GPU texture compression is retained on supported hardware, with a decoded fallback. Both paths have been rendered in an actual browser; unsupported compression was also exercised by denying the WebGL extension queries. Demise's compressed texture subset needs 84.1% less texture payload than equivalent decoded RGBA mipmaps, and its packed native visual download is 20.5% smaller. These are asset-derived savings, not measurements of total GPU memory. Fallback texture uploads happen while preparing the map to avoid first-visibility stalls during a run. Per-map transfer sizes and measured rendering limits are in `NEW-MAP-VISUALS.md`.

## Kitsune review following player feedback

The first visual export incorrectly omitted `tools/toolsblack`, a real opaque material used by 2,349 authored faces. Command-route completion did not detect those missing visible walls. A second leak through an authored sky surface also required world-depth occlusion. These are rendering corrections using the original BSP faces; they do not add collision or hide stages with scripted rules. Visual regressions now check the source flags/material and stage separation. See `NEW-MAP-VISUALS.md` for the exact source-ray evidence and validation.

After rebuilding, all 343 Node tests and three source-surface regressions passed. Kitsune's full 6,333-command route was rerun in the production browser: final state, all eight splits and 91.940469-second finish remained identical to the headless command simulation. All nine practice spawns, Pointer Lock, keyboard restart and finish flow passed without browser errors. The separate visual negative control reduced the S2 leak region from 959 white S9 pixels to zero. The gameplay report is `fixtures/browser-new-map-results-kitsune.json`; stage screenshots are in `docs/screenshots/new-maps/kitsune-stage-*.png`. These checks establish the reported visibility fix and unchanged command-route behavior, not universal native CSS parity.

Portal momentum was rechecked against three independently recorded KSF runners, covering 24 transfers. Every stored stage arrival has zero horizontal velocity. The stock CSS authored portal instead preserves momentum in the controlled native probe. The browser retains the reviewed KSF recording profile; the files do not prove which private server or exporter component causes that difference, or establish the current live configuration. Exact samples, public links and the bounded newer-record search are in `KITSUNE-TELEPORT-EVIDENCE.md`.

## Remaining fidelity gaps

- This is not certified exact CSS/KSF equivalence. Private KSF plugins, start/stage zone bounds, historical server/map modifications and some input/contact fields are unavailable.
- Timer boxes and full failure/recovery policies do not exactly reproduce all KSF server rules. Local records belong to their own versioned boards.
- Geometry contacts still expose small numerical discrepancies under strict native comparison. `fixtures/*-new-map-probe.json` retains failures; those limits were not widened after testing.
- Main routes are supported. Filter-driven secret/bonus entity I/O, general dynamic map scripting, animated props and Source's complete water/refraction/HDR pipeline are not emulated.
- Dynamic props with no PHY resource and Demise's missing authored water-underside material remain explicit resource limitations. See the visual and entity review reports.
- Browser performance depends on GPU and resolution; sustained browser measurements and texture-memory work are separate from headless movement proof.

Further evidence: `NEW-MAP-ENTITY-REVIEW.md`, `NATIVE-CSS-TELEPORT-PROBE.md`, `NEW-MAP-VISUALS.md`, and the reproducible workflow in `MAP-IMPORT.md`. Existing UI/input work remains intact. No production deployment is part of this local review pass.
Beginner, Year3000, Ace, Legends & Eclipse

Download this document

# Five additional CSS classics

Local implementation and review, 6 October 2026. This adds **surf_beginner**, **surf_year3000**, **surf_ace**, **surf_legends** and **surf_eclipse** to the existing ten-map catalog. These are converted original Counter-Strike: Source maps, not recreated layouts or CS:GO/CS2 ports. This document does not announce a production deployment or registration of the new live leaderboard boards.

## What was imported

| Map | Author | Selected KSF classification | Main route |
| --- | --- | --- | --- |
| surf_beginner | Kiiru | Tier 1 | 7 stages, momentum-preserving portals |
| surf_year3000 | ArchAngel | Tier 1 | 2 KSF stages, symmetric first-stage exits |
| surf_ace | Juxtapo | Tier 1 | 8 physically connected stages |
| surf_legends | SintaxError | Tier 1 | Linear, 4 checkpoints, landmark-relative portal |
| surf_eclipse | Paper-Cut | Tier 2 | 3 stages with delayed stage handoffs |

The source SHA-1 and size are checked before conversion. Render and collision imports read the same BSPs. Original texture blocks/pixels, model transforms and skins are audited; the importer does not substitute generated artwork. All five carry actual rendered menu previews, credits, lazy loading and packed texture delivery. Legends' source BSP has no LDR or HDR lightmap data; its unlit appearance is not a dropped lightmap import.

The visual importer now excludes brush entities whose class begins `trigger_`: Source makes these invisible at initialization even when the material/face flags alone do not say NODRAW. This fixes debug trigger surfaces in Year3000 and Legends. Only the five new visual bundles were regenerated; the ten existing bundles remain unchanged.

Ace and Legends contain compiled brushes with contradictory bevel planes. An opt-in import path uses their six compiled axial planes only to obtain conservative broadphase bounds, while retaining every original plane for player-hull collision. It does not replace their collision with a box or silently omit the brushes.

## Course behavior

Source units, float32 0.015-second simulation ticks, hulls, air acceleration, gravity and movement collision are unchanged. Added stage bookkeeping lets Beginner preserve its authored portal momentum while advancing stage identity. Ace's checkpoint gates update stage identity without imposing the separate-stage 350-unit exit cap. The initial ranked start cap remains in place.

Year3000 uses the two-stage KSF course observed in the independent recording, including its final stage arrival, instead of treating all original combat/lobby destinations as main stages. Eclipse retains the authored main-course push volumes and the observed delayed stage arrivals. Legends uses the authored landmark-relative translation, retaining offset, view and momentum.

Eclipse's `trigger_hurt *53` is specifically **not** treated as an unconditional restart: three independent native samples lie inside it and continue normally. Source's damage entity does not itself prescribe a teleport or timer reset. Its original entity and convexes remain in import metadata; only the importer's invented reset classification is removed. The historical server damage policy/revision is unknown, so this is not a claim to reproduce health or invulnerability. All authored stage failure portals remain active. Exact independent evidence is in `fixtures/eclipse-hurt53-independent-review.json`.

Start/finish markings and ordered checkpoints use original trigger convexes where applicable. Other timing boxes are local gates aligned with physical decks/corridors and native event samples. The private KSF timer-zone database and full plugin behavior are unavailable; these boxes are not claimed to be exact KSF zones. Bonus, combat and jail game modes are not added as playable modes.

Practice remains explicitly unranked. Beginner's seventh practice destination uses a settled floor pose so a stationary player does not hit an exact-plane landing issue; the actual stage portal keeps its original airborne destination. The optional practice-stage field affects only explicit practice travel.

## Evidence and reproduction

Final local release checks passed on 6 October 2026:

- 399 automated TypeScript tests, TypeScript checking and the production build.
- Complete canonical-spawn routes for all five maps, accepted by the compressed replay-upload verifier. Every tick matches across 30, 60, 144 and 240 FPS schedules.
- Actual production-browser completion of all five routes, with identical final state, splits and time to the headless simulation, plus keyboard restart, Pointer Lock, stage practice, map switching, persistent selection and the 15-map menu at three screen widths. No browser or console errors.
- All 21 advertised practice destinations settle safely; separate integration tests cover 150 idle ticks at each destination.
- Existing-map compatibility: 59,695 differential ticks across 16 complete runs match exactly, and all ten existing board identities remain intact.
- Final shipped-file audit checks current physics, geometry, replay and served-script hashes. Evidence: `fixtures/classic-five-release-validation.json` and `fixtures/browser-new-map-results-beginner-year3000-ace-legends-eclipse.json`.

These are automated local checks, not human playtesting or validation of a new cloud deployment. The five maps are ready for local review; online records remain disabled in this preview.

- [Reference and source provenance](CLASSIC-FIVE-REFERENCE.md): pinned BSPs, native CSS recordings, authors, original licence panels, stage events and unknown settings.
- [Visual audit](CLASSIC-FIVE-VISUALS.md): texture/prop/lightmap evidence, stage screenshots, packed/fallback delivery and measured resource use.
- [Movement and full-route validation](CLASSIC-FIVE-PLAYABILITY.md): external expectations, strict tolerances, ordinary-command witnesses, timing and rendering-schedule checks.
- [Existing-map compatibility](CLASSIC-FIVE-COMPATIBILITY.md): 16 complete trajectories across all ten old maps, 59,695 exactly matching ticks, preserved board identities and the actual new source fingerprint.

```sh
npm test
npm run build
npm run preview -- --host 127.0.0.1 --port 4199 --strictPort
npx tsx scripts/validate-new-maps.ts beginner year3000 ace legends eclipse
```

For browser flow checks in PowerShell:

```powershell
$env:UI_BASE_URL='http://127.0.0.1:4199'
npx tsx scripts/browser-new-map-qa.ts beginner year3000 ace legends eclipse
npx tsx scripts/validate-classic-five-release.ts
```

The final release audit rejects incomplete browser reports and stale evidence. It checks the served production script hashes, shipped compressed collision packs, source/replay fingerprints and the server's bounded compressed-upload decoder/verifier. The local API exposes all fifteen catalog entries with online records disabled; no live account or database is used by these checks.

Source regeneration uses `scripts/import_new_maps.py` and `scripts/classic_five_profiles.py`, with the pinned source registry and locally installed CSS stock assets. See [MAP-IMPORT.md](MAP-IMPORT.md) for the shared pipeline. The built game needs neither Python nor BSP files at runtime.

## Fidelity boundary

These imports do not establish universal CSS/KSF parity. V2 recordings for Legends and Eclipse omit analog input magnitudes and ground flags. Native reset/push behavior, private start-zone rules and exact fractional timer boundaries can differ from the visible compiled entities. Independent comparison failures and command adaptations must remain visible in the validation report; passing a legal local route is evidence of playability, not evidence that all native telemetry matches.

Source shader/water/animation behavior remains subject to the renderer limitations documented in the visual audit. Original asset licences and author credits remain in [THIRD_PARTY_NOTICES.md](../THIRD_PARTY_NOTICES.md); local import does not relicense the assets.
Five classics: independent CSS evidence

Download this document

# Independent CSS reference: Beginner, Year3000, Ace, Legends and Eclipse

Captured 6 October 2026. This document records source evidence for five original **Counter-Strike: Source** maps and the KSF **66t / forward / main-course** recordings. It is an import reference, not a claim of exact private-server parity or a whole-route test result.

## Map identity and original resources

| Map | KSF credit | KSF main course | Pinned CSS BSP SHA-1 | Decompressed bytes | Original CSS upload |
| --- | --- | --- | --- | ---: | --- |
| [surf_beginner](https://ksf.surf/maps/surf_beginner?game=66t&mode=fw) | Kiiru | Tier 1, 7 stages | `812ca2188ed4ea7578d51268020d1363ea4155fa` | 17,958,137 | [GameBanana 121714](https://gamebanana.com/mods/121714) |
| [surf_year3000](https://ksf.surf/maps/surf_year3000?game=66t&mode=fw) | ArchAngel | Tier 1, 2 stages | `f65b3d9f872ef80860f6b84f6286586830e9d163` | 17,185,800 | [GameBanana 123298](https://gamebanana.com/mods/123298) |
| [surf_ace](https://ksf.surf/maps/surf_ace?game=66t&mode=fw) | Juxtapo | Tier 1, 8 stages | `f84f89ac3f1149673d21ab0264e243f7a4cf6006` | 18,521,966 | [GameBanana 121570](https://gamebanana.com/mods/121570) |
| [surf_legends](https://ksf.surf/maps/surf_legends?game=66t&mode=fw) | SintaxError | Tier 1, linear, 4 CP | `aadafc6e05c4da062e05ff2c9bfa0d7aa087417d` | 8,897,517 | [GameBanana 122424](https://gamebanana.com/mods/122424) |
| [surf_eclipse](https://ksf.surf/maps/surf_eclipse?game=66t&mode=fw) | Paper-Cut | Tier 2, 3 stages | `d3c52f25690bd03d534f6fed5d04c52bfc345836` | 15,247,243 | [GameBanana 121976](https://gamebanana.com/mods/121976) |

The [KSF/OuiSURF mirror index](https://main.fastdl.me/maps_ksfthings.html) provides the source hashes, sizes and original upload references. `scripts/download_new_maps.py` verifies decompressed bytes and SHA-1 against `scripts/fixtures/new-maps-sources.json` before accepting the cache. The download pattern is `https://main.fastdl.me/h2/{sha1}/{map}.bsp.bz2`. All five local files passed independent size/hash verification. No CS:GO or CS2 port supplies these imports.

Public upload metadata is preserved in `fixtures/{slug}-gamebanana-source.json`, fetched from `https://gamebanana.com/apiv11/Mod/{id}/ProfilePage`. All five identify Counter-Strike: Source and `isPorted: false`. Their published licence panels list **CC BY-NC-ND 4.0**, and their checklists ask permission for redistribution, distributing modified versions or reused parts, and disallow commercial use. These notices do not establish permission for publishing a converted browser map. No author permission grant or contact is claimed here. The Year3000 upload explicitly says it is a third-party repost crediting ArchAngel, so its uploader must not be mistaken for the creator.

Additional credits: Ace's bonus is by **Syncronyze**, skybox by **komaokc**, textures by **Silkroad**. Eclipse credits Paper-Cut for the skybox/jail concept and **Turkey Eater** for jail scripting. Full original credit lists remain in the captured metadata. Ace's creator describes the release as tier 2 while current KSF classifies it tier 1; this project uses the selected KSF category. The creator also explicitly describes Ace's stages as flowing together. Eclipse's original notes specify 66 tick and say the map will not function correctly at 100 tick.

## Pinned native recordings

Every public viewer was checked for `game: css`, `zoneId: 0`, `finishType: 0` and `tickRate: 66.66666666666667`. Main pages were requested with `game=66t&mode=fw`. The matching simulation interval remains Source's float32 0.015 (`0.014999999664723873`), not 1/66. The displayed category is nominal 66t.

| Map | Runner / recorded UTC | Format | Frames | Bookmarks | Published seconds | Main start → finish frame |
| --- | --- | ---: | ---: | ---: | ---: | --- |
| Beginner | kusche / 2026-09-16 13:23:37 | v3 | 3,497 | 15 | 45.72529602050781 | 114 → 3163 |
| Year3000 | .x / 2026-09-18 21:27:33 | v3 | 2,297 | 5 | 27.537572860717773 | 127 → 1963 |
| Ace | stupid parrot / 2026-09-25 18:20:48 | v3 | 3,121 | 17 | 40.13575744628906 | 112 → 2787 |
| Legends | crashfort / 2023-05-30 09:31:57 | v2 | 2,241 | 7 | 30.314626 | 164 → 2185 |
| Eclipse | rulldar / 2021-05-22 13:57:30 | v2 | 4,577 | 7 | 61.799152 | 161 → 4281 |

Pinned files:

- Beginner: [viewer](https://ksf.surf/replays/surf_beginner/replay_css_1019_0_348352_1789565017.rec?game=66t), [native bytes](https://ksf.surf/api/replays/replay_css_1019_0_348352_1789565017.rec?game=66t), 259,828 bytes, SHA-256 `29611e4d1f2a1978adfbee3e374427cb54bb04f45c5520d20600022d92a21901`.
- Year3000: [viewer](https://ksf.surf/replays/surf_year3000/replay_css_587_0_712551_1789766853.rec?game=66t), [native bytes](https://ksf.surf/api/replays/replay_css_587_0_712551_1789766853.rec?game=66t), 168,188 bytes, SHA-256 `176d14fe1023b9b1e6719c7eae235a0d7e519eebebb5497de2991694cdfe5797`.
- Ace: [viewer](https://ksf.surf/replays/surf_ace/replay_css_1971_0_937143_1790360448.rec?game=66t), [native bytes](https://ksf.surf/api/replays/replay_css_1971_0_937143_1790360448.rec?game=66t), 233,804 bytes, SHA-256 `705ecb8e725732a0bd8ea9822090a1939ef10c07c1ff30ac2bbb354d8c928da2`.
- Legends: [viewer](https://ksf.surf/replays/surf_legends/replay_css_287_0_197328_1685439117.rec?game=66t), [native bytes](https://ksf.surf/api/replays/replay_css_287_0_197328_1685439117.rec?game=66t), 93,324 bytes, SHA-256 `b6452d9cd8a268320800901c24583c01daa4acb6175fe663360e22ad2aefc407`.
- Eclipse: [viewer](https://ksf.surf/replays/surf_eclipse/replay_css_407_0_449650_1621691850.rec?game=66t), [native bytes](https://ksf.surf/api/replays/replay_css_407_0_449650_1621691850.rec?game=66t), 186,764 bytes, SHA-256 `29739fb70f78430037ea1dd7fce515cae45fc70df3064e1a2fe53ab519f1b131`.

Beginner's fastest three downloadable records had no stationary prestart sample. A bounded search found rank 4's v3 recording with a stationary grounded launch; this supplies actual analog commands and contact flags. Legends' first downloadable record began airborne, so rank 2's stationary launch was selected. The search order/results are preserved in `fixtures/beginner-stationary-record-search.json` and `fixtures/legends-stationary-record-search.json`. Record rank is capture-time metadata, not a live ranking claim.

`scripts/decode-classic-five-replays.ts` decodes offline; `--fetch` refreshes the **pinned filenames** and rejects changed hashes/lengths. It never silently selects a new fastest record. It rejects unsupported layouts, nonfinite state/commands and invalid bookmark bounds. V3 uses 184-byte headers here (40 extension cells), 524-byte bookmarks and 72-byte frames (18 cells). V2 uses 16-byte headers and 40-byte frames. The v3 opaque extension is retained without inventing setting names. The [public KSF reader](https://ksf.surf/gokz/js/replayviewer.js) was rechecked against `fixtures/ksf-replayviewer-2026-10-05.js`: identical SHA-256 `be8222c64b99151cdd7d542e30a2735d3d110de08a5f60d3b0a3343c2f3c552d`. That downloaded code is inert reference text, never executed.

Exact frame evidence lives in:

- `fixtures/{slug}-native.rec` and `{slug}-ksf-telemetry.json`: native bytes and decoded state/commands.
- `fixtures/classic-five-source-metadata.json`: provenance, capture dates, page hashes, primary viewer context and published splits.
- `fixtures/classic-five-telemetry-summary.json`: every native landmark, speed extrema, stationary samples and position discontinuities.
- `fixtures/classic-five-native-event-windows.json`: unchanged native windows around every bookmark and portal discontinuity. This file includes stage starts, stage exits, finish states and the frames on both sides of each portal.

## Launches and stage semantics

Source coordinates are X/Y horizontal, Z up, feet origins. These stationary positions are **native runner positions**, not automatically the authored or shipped canonical spawn:

| Map | Stationary join frame | Native feet position |
| --- | ---: | --- |
| Beginner | 0 | `(-330.0436706542969,70.48346710205078,320.0313415527344)` |
| Year3000 | 0 | `(-368.2182312011719,-2821.647216796875,9095.55078125)` |
| Ace | 0 | `(-1538.6593017578125,105.21258544921875,3232.03125)` |
| Legends | 1 | `(6524.7099609375,8055.4599609375,12334.7998046875)` |
| Eclipse | 1 | `(-11076.2001953125,9883.3203125,12114.2001953125)` |

### Beginner: direct momentum-preserving portals

Stage 2–7 enter bookmarks are frames **266,548,904,1333,1826,2399**; corresponding start-zone exits are **340,637,994,1398,1905,2467**. Portals snap one frame before each enter bookmark, at **265,547,903,1332,1825,2398**, to the authored destinations below. They retain incoming world-space velocity, with ordinary gravity/contact effects afterward. They do **not** use Kitsune's zeroed horizontal arrival.

| Arrive stage | Authored destination | Exact portal feet | Yaw |
| --- | --- | --- | ---: |
| 2 | failman2 | `(2528,2240,672)` | 90 |
| 3 | failman3 | `(1456,272,928)` | 0 |
| 4 | failman4 | `(2720,5888,832)` | 180 |
| 5 | failman5 | `(-5792,-1632,2736)` | 0 |
| 6 | failman6 | `(-2320,104,2392)` | 90 |
| 7 | failman7 | `(-5312,640,2784)` | 90 |

For example, frame 265 lands at stage 2's authored origin with XY velocity `(196.1869659423828,914.3314819335938)`; frame 266 has moved to `(2530.94287109375,2253.715087890625,661.852294921875)` with the same XY velocity. Platform/wall contact later reduces speed. The finish bookmark frame 3163 is another authored teleport to `Endman_poop`, `(-5847.8798828125,7312,-656)`, yaw 270; it also retains momentum. The separate end-area antics/secret/combat features are not needed for main-course timing.

### Year3000: selected KSF route differs from original combat destinations

The chosen route reaches the western authored `trigger_teleport *36`, which targets `stage03_ct`. Native frames **800→801→802** instead show a two-step handoff: frame 801 at `(0,0,11000)` still has incoming momentum, frame 802 at **`(716,-10598.5,626)`**, yaw 90, velocity **`(0,0,-6)`**. Stage 2 enter is frame 803, start exit 908. The native arrival differs from the BSP's `stage03_ct` `(375,-10962,519)` and `stage03_t` `(727,-10962,519)`. It must be recorded as an observed KSF route override, not claimed to be the stock entity destination. Private plugin source is unavailable.

The original map also contains routes with destinations named `stage02_*`, team/combat spawn routes and jail teleports. These names alone do not define the selected KSF two-stage main course. The finish event is native frame 1963 at `(-5837.798828125,7417.9951171875,-5155.92138671875)`; this is an event sample, not a recovered private timer box.

### Ace: seamless staged route

Stage 2–8 enters occur at **328,561,895,1130,1447,1917,2328**; exits at **356,584,913,1144,1466,1930,2342**. There is **no position discontinuity** through any of these stage boundaries. Native horizontal speed remains approximately 1,150–2,617 u/s at these later exits. Applying a generic 350 start-exit cap to each would be demonstrably wrong. Stages are useful progress/recovery points in one continuous physical course.

The BSP contains authored `1z`…`8z` triggers, companion `a1`…`a7` triggers and per-stage `s1`…`s8` destinations for recovery. They need direct geometry review, not artificial portals between the continuous sections. The final portal at frame **2786** goes to authored `mapend` `(-11136,2304,9185)`, yaw 0, retaining world momentum. The finish bookmark follows at 2787. Bonus filter/boost systems operate in the separate bonus area and must not be applied globally to main-course movement.

### Legends: landmark-relative transfer

The four CP bookmarks occur at **609,1002,1234,1673**. `trigger_teleport *41` uses landmark **`course2off`** `(-9980,-5568,-12376)` and destination **`course2`** `(-9980,-5568,14240)`. The translation is exactly **`(0,0,26616)`**. Native frames **1236→1237** preserve the player's relative XY/vertical offset, view and momentum. Do not snap to the destination point or rotate velocity by its yaw. The native displacement is the end-of-tick movement plus the 26,616-unit Z translation, consistent with the separately verified landmark behavior already used by Reprise. Another authored landmark `landmark_top` exists in the start-area return route; the selected full run does not cross it.

The finish bookmark is frame 2185 at `(8157.2724609375,-9440.861328125,-2851.014892578125)`. Main-route push volumes and combat/jail entities must be reviewed independently; simply importing every push as a global effect would be incorrect.

### Eclipse: delayed arrivals with zeroed momentum

Stage 2 uses `trigger_teleport *81`, target `stage2_nomove`; stage 3 uses `*80`, target `stage3_nomove`. Native frames **1070→1071→1072** first pass through `(12854,-13627.9990234375,-15104)` with incoming momentum, then arrive at authored **`(-10299,-4048,11884)`**, yaw 0, velocity `(0,0,-6)`. The second transition **2748→2749→2750** first passes through `(11794,-13326,-15104)`, then arrives at authored **`(4870,-2395,2196)`**, yaw 90, velocity `(0,0,-6)`.

Stage enter bookmarks follow at **1073 and 2751**, exits at **1186 and 2882**. The intermediate positions are recorded handoffs and should not cause an invented swept world-space path or a visible camera trip across the map. The record establishes the delay and final states, but does not identify whether a private plugin, modified entity, or exporter causes every intermediate detail. Original failure, team/jail and movement-control chains are separate from these successful main-route arrivals.

The finish bookmark is frame 4281 at `(4665.080078125,-14856.18359375,-3805.93505859375)`, travelling upward at `1504.32666015625` Z velocity. Ending the run requires the actual course gate; it must not assume a flat floor landing.

## Movement settings and limits

All five viewers report `maxVelocity: 0`, meaning unspecified, not a zero speed limit. Ace's compiled `logic_auto` (Hammer ID 11600) explicitly requests `sv_airaccelerate 150`, `sv_maxvelocity 3500`, and `sv_enablebunnyhopping 1`. No `sv_*` output was found in the other four compiled entity audits.

Legends' selected record repeatedly reaches exactly ±3500 on X/Y/Z while total speed exceeds 3500, supporting a **component limit**, not a vector-length limit. Eclipse reaches exactly 3500 on Y. Beginner and Year3000 do not reach a component limit in the selected records; 3500 remains the shared CSS/KSF-default inference there, not a measured saturation result. These facts do not prove all private KSF settings, start-zone policies, filter behavior or current live plugin versions.

V3 analog movement and ground/teleport flags are retained for Beginner, Year3000 and Ace. Legends/Eclipse v2 lack analog magnitudes and contact flags; button-to-command reconstruction is consequently uncertain. The view sampled on a teleport frame may be the destination angle rather than the actual command angle. Report that ambiguity rather than treating contact or portal mismatches as numerical noise.

## Validation boundary

The predeclared isolated comparison tolerances are **0.002 Source units in position** and **0.002 u/s in velocity**. Expected values must come from these pinned external records. Preserve first divergence, maxima, contact mismatches and every failure; do not loosen thresholds after seeing results. The bookmark interval multiplied by nominal 0.015 differs slightly from published records (including −0.009704 seconds for Beginner and +0.010757 seconds for Ace when computing published minus bookmark time). This does not recover the private KSF sub-tick timer algorithm or exact zone boundaries.

A native recording is independent reference evidence, not by itself proof of browser surfability. The integrated map still requires unchanged collision import, legal command-only canonical-spawn witnesses, server replay verification, 30/60/144/240 FPS equivalence, and actual browser checks. Source states must never be injected into those witnesses. Route/renderer validation and their remaining gaps belong in the implementation report after those checks run.

## Trigger revision uncertainty found during route validation

Legends' native frames **1251→1252** sweep through the authored enabled jail trigger `*60` while both sampled endpoints remain outside. Frames **1317→1318** cross the authored enabled jail trigger `*43`, with the final native sample inside the volume. These are separate discrepancies: the second cannot be explained solely by a swept-versus-sampled touch difference. Both triggers target `jaildest1`, have `StartDisabled=0`, permit players and carry no filter in this pinned BSP. `fixtures/legends-planning-evidence.json` preserves native states, exact brush bounds and standing-hull crossing intervals. The file's `*43` main brush spans `(-10944,-8448,11520)` to `(-8192,-5294,11616)`; the recorded final feet are inside it.

The public KSF viewer payload contains `replayUrl`, `map`, `tickRate` and `hudContext`, but no BSP checksum/revision. The public v2 reader/file layout supplies no matching map hash either. The pinned mirror SHA-1 establishes the acquired BSP's identity; it does **not** prove that a historical server recording used those identical bytes or entity settings. No public evidence of a specific KSF removal/override was found in this bounded source review. Both authored reset triggers remain present.

Legends now has a complete **2,184-command** canonical-spawn witness accepted by the server verifier at **30.879061 seconds**, with all four checkpoints. Its 30/60/144/240 FPS schedules match exactly. `scripts/plan-legends-route.ts` reproduces it using the explicit inferred half-strafe at native frame 1123, ordinary one-tick crouches at command indices 1250 and 1316, and two bounded yaw windows (indices 1270–1330 and 1320–1440). The largest actual yaw difference is **5.589444 degrees**. The route clears both unchanged jail volumes and rejoins the downstream ramps. Its maximum continuous deviation from native is about **46.2923 units**; timing/geometry differences and v2 input inference remain explicit. This is a legal local route, not a claim to reconstruct the native run exactly.

Eclipse's route review also found native X displacement consistent with a −95 u/s base force around frames 1680–1683 outside the compiled `trigger_push *14` bounds. The acquired BSP declares speed 95, direction −X, bounds approximately `(-999,-7637,7476.63)` to `(-922,-7125,8052.63)`, while native frame 1680 is near `(-939.2366,-7005.5137,8160.8872)`. Those bounds are retained. The same absence of historical BSP fingerprints/private entity configuration prevents attributing the mismatch to a particular revision or plugin. It is not justification to enlarge an authored force volume to fit telemetry.
Five classics: artwork and performance

Download this document

# Beginner, Year3000, Ace, Legends and Eclipse visuals

These are conversions of the pinned original **CS:S** BSPs and their authored
assets. No artwork or map geometry was generated. Stock resources come from the
installed, licensed CS:S archives mounted read-only. The BSP identities and
original source pages are in `scripts/fixtures/new-maps-sources.json` and
`fixtures/{slug}-gamebanana-source.json`.

The author pages list CC BY-NC-ND 4.0 and permission conditions. Those notices
remain applicable to the original maps/assets. Neither this conversion nor a
public download is a grant of redistribution permission. This document records
local conversion/validation, not permission from the authors or native visual
parity.

## Sources and credits

| Map | Original credit | Pinned BSP SHA-1 |
| --- | --- | --- |
| Beginner | Kiiru | `812ca2188ed4ea7578d51268020d1363ea4155fa` |
| Year3000 | ArchAngel | `f65b3d9f872ef80860f6b84f6286586830e9d163` |
| Ace | Juxtapo; bonus by Syncronyze, skybox by komaokc, Silkroad textures | `f84f89ac3f1149673d21ab0264e243f7a4cf6006` |
| Legends | SintaxError | `aadafc6e05c4da062e05ff2c9bfa0d7aa087417d` |
| Eclipse | Paper-Cut; jail scripting by Turkey Eater | `d3c52f25690bd03d534f6fed5d04c52bfc345836` |

## Reproduction

For each slug, use its registry ID and author:

```text
python scripts/import_boreas_visuals.py node_modules/.map-imports/surf_beginner.bsp --slug beginner --map-id surf_beginner --author Kiiru --game-dir "PATH/Counter-Strike Source" --texture-format lossless-webp --compressed-textures
python scripts/audit-classic-five-visuals.py --game-dir "PATH/Counter-Strike Source"
python scripts/validate_new_map_visuals.py --slugs beginner,year3000,ace,legends,eclipse --game-dir "PATH/Counter-Strike Source"
python -m unittest discover -s tests -p "test*visual*.py"
```

Run `scripts/capture-classic-five-previews.ts` against a Vite development server
using `UI_BASE_URL`, default `http://127.0.0.1:4198`. This deliberately loads the
raw visual manifests so the inspection does not depend on catalog registration.
It renders through the production renderer, not a separate illustration tool.
Unused generated alternatives and superseded batches were removed from these
five asset folders after import; every URL referenced by the final manifests
is retained and verified. A new import can recreate those redundant files.

## Geometry and artwork integrity

| Measure | Beginner | Year3000 | Ace | Legends | Eclipse |
| --- | ---: | ---: | ---: | ---: | ---: |
| Rendered BSP faces | 8,680 | 10,155 | 8,170 | 26,548 | 11,719 |
| Original sky portal faces | 757 | 2,034 | 363 | 695 | 1,824 |
| Displacements | 6 | 0 | 60 | 0 | 0 |
| All geometry batches | 24 | 190 | 186 | 59 | 26 |
| Static props | 2 | 72 | 109 | 6 | 6 |
| Dynamic prop initial poses | 0 | 44 | 0 | 0 | 0 |
| Decoded VTF images | 30 | 301 | 441 | 151 | 33 |
| Native DDS alternatives | 21 | 41 | 19 | 11 | 19 |
| World lightmap pages | 1 | 1 | 1 | 0 | 1 |
| Import warnings | 0 | 0 | 0 | 0 | 0 |

Every decoded image matches its source VTF pixels exactly, including alpha.
Every DDS alternative preserves the original compressed blocks and mipmaps.
Geometry buffers have finite vertex data and valid index ranges. All static
props retain their original models, skins, transforms and scale, including
Legends' source 3D-sky scale. No model was omitted, assigned a fallback skin or
given a substitute texture. Year3000's 44 dynamic props retain initial poses.

These conversion checks share the offline VTF decoder with the importer; they
are **not** an independent test of Valve's shader output. Full reports are in
`fixtures/classic-five-visuals/integrity.json` and the five `*-source-audit.json`
files. The read-only stock resource counts are 50, 130, 15, 39 and 44 respectively.

### Trigger visibility correction

Year3000 and Legends exposed an existing importer mistake: their authored
trigger brushes use `SURF_TRANS` (1024), so surface flags alone made orange
`tools/toolstrigger` debug volumes visible. Their volumes are invisible in normal
Source play because [CBaseTrigger::InitTrigger](https://github.com/ValveSoftware/source-sdk-2013/blob/master/src/game/server/triggers.cpp)
applies `EF_NODRAW` when `showtriggers` is zero. The visual importer now excludes
brushes owned by `trigger_*` entities. This is an entity visibility rule, not a
blanket exclusion of tool materials: visible `toolsblack` stage walls remain.

Pinned BSP regression tests independently enumerate 576 Year3000 and 528 Legends
trigger faces with the translucent surface flag, and confirm their debug
material is absent from the final export. All five imports record hidden trigger
counts. The original collision/trigger volumes are unaffected. Existing map
bundles were not regenerated by this correction.

The existing Kitsune source tests still pass: visible black walls, authored sky
portal orientation and depth occlusion remain intact. Each new staged map also
retains its original sky-depth mesh. The nine visual Python tests passed.

## Actual browser inspection

Fifty start/midcourse views cover all seven Beginner stages, both Year3000
stages, eight Ace stages, five Legends sections and three Eclipse stages. They
loaded with no page or console errors. Authored room walls and sky boundaries
remain present; no stage-hiding substitute or invented wall was added.

The five lobby images are real 1280 × 720 screenshots of the final renderer,
with no image edits or added artwork. They show Beginner stage 4 under the
sunset, Year3000's first-stage ramps, Ace stage 5's paired stone ramps, Legends'
curved stone passage, and Eclipse's second-stage ramps against its eclipse sky.
Camera frames, original telemetry indices, renderer counts, screenshot names,
thumbnail hashes and load details are retained in the five `*-browser-views.json`
reports. Legends keeps its native camera pitch for review; its lobby thumbnail
uses the same native position with yaw 45/pitch 10 to show the curve clearly.
The other review poses clamp extreme pitch for readability. These are visual
camera samples, not command-replay playability evidence.

An integration review also exercised all 21 practice destinations for 150 neutral
simulation ticks. It found Beginner stage 7's original airborne spawn could land
exactly on the Z2640 plane and reset on the next tick. Only its practice travel
pose is now settled at Z2640.03125, with explicit practice stage 7; its actual
portal destination and stage spawn remain authored Z2784. All 21 destinations
then settled on ground without resets. The 15-map menu was inspected at widths
1440, 1280 and 390 with no horizontal overflow or console errors.
Browser retests confirmed stage 7 stays grounded in practice, and manual
practice HUD highlights the selected stage. Eclipse stage 3's marker now
projects onto the original deck at Z1881 rather than floating at the airborne
timer box's lower Z1968 plane; the timer box remains unchanged. Finish marker
floor clamping is unchanged. Reports are `practice-integration.json` and
`ui-integration.json` in the same fixture folder.

## Packed delivery and sustained rendering

The production packs retain all original exported bytes and request only one
texture representation. Actual requests were checked against the manifest;
the native run did not fetch the decoded alternatives, and vice versa.

| Map | Native transfer / requests | Decoded transfer / requests | Native peak draws / triangles |
| --- | ---: | ---: | ---: |
| Beginner | 8.38 MB / 4 | 10.13 MB / 5 | 25 / 27,038 |
| Year3000 | 6.83 MB / 5 | 8.66 MB / 5 | 158 / 51,348 |
| Ace | 6.22 MB / 5 | 7.30 MB / 4 | 162 / 50,164 |
| Legends | 2.70 MB / 4 | 3.34 MB / 4 | 53 / 74,459 |
| Eclipse | 5.32 MB / 4 | 6.13 MB / 4 | 26 / 46,817 |

Transfer figures include the compressed manifest and selected packs, use decimal
MB, and exclude gameplay collision data. Exact byte counts and content hashes
are in `fixtures/classic-five-visuals/packed-transfer.json`.

Six native eight-second camera traversals used the same 1280 × 720 balanced
renderer canvas, switching through all five maps before returning to Beginner.
They averaged 359.4–360.0 FPS on this machine, with p95 frame intervals of 2.9 ms.
The slowest individual interval was 13.9 ms on Ace. First/repeated Beginner
ended with identical 25 geometries, 24 textures and nine programs, checking
resource disposal after map switching. These are renderer-only local readings,
not physics performance, measured GPU memory, internet load times or a guarantee
for other devices. The browser appeared limited to about 360 FPS.

All five decoded fallback traversals also passed, averaging 359.3–360.0 FPS
with the same 2.9 ms p95 interval. A separate run disabled the browser's S3TC
extension: automatic selection loaded Beginner's decoded packs, zero compressed
textures, and the expected five requests. All twelve traversals completed with
zero browser/console errors. `performance-unsupported.json` retains that test.

Reproduce with `scripts/new-map-render-performance.ts`, the development server
URL in `UI_BASE_URL`, and `RENDER_CASES` containing the five map slugs with modes
`native` or `fallback`. Reports are retained as `performance-native.json` and
`performance-fallback.json` in the same fixture folder.

## Known native rendering differences

- **Water:** Beginner has two Water materials, Ace four, Legends 23 and Eclipse
  four. Their authored geometry/material inputs survive, but native reflection,
  refraction, underwater fog and scrolling water passes are not reproduced.
  Beginner's pool appearance is consequently simpler than the native shader.
- **Animation and effects:** Animated VTFs use frame zero; material proxies and
  texture scrolling are static. Year3000 has 18 rotating brushes, ten doors,
  four linear movers, 44 initial-pose dynamic props and 33 sprites. Legends has
  two rotators, six rotating doors, seven ordinary doors and three occluders.
  Eclipse has two rotators, one door, four movers, 20 sprites and six dust-mote
  volumes. These general entity animations/effects are not reproduced. Ace's
  four dust-mote volumes are also omitted. Some of these belong to jail/bonus
  scenery; main-course movement coverage is documented separately.
- **Shader layers:** Year3000's two UnlitTwoTexture security-screen materials
  and Eclipse's portal-ball material preserve their first texture but not the
  second-layer shader behavior. Beginner's Modulate binocular overlay is not a
  native Modulate shader. Full self illumination, authored environment masks,
  contrast/saturation parameters, directional bumped lighting and Source HDR
  exposure remain renderer limitations.
- **Legends lighting/fog:** Both original lighting lumps (8 and 53) are empty;
  there are no world lightmaps to recover. The exporter did not discard an HDR
  atlas. The source explicitly enables grey fog from 0 to 2,700 units with
  color `(192,192,192)`. That fog and the 16× 3D sky are retained, rather than
  inventing lighting or removing fog to produce a cleaner thumbnail.
- **Ace's displacement blend:** The authored WorldVertexTransition material
  retains both textures and displacement alpha. This map does not request a
  `$blendmodulatetexture`; no such map-authored layer was dropped.
- Native PVS/areaportal culling, arbitrary entity I/O, sprite glows and particle
  effects are not equivalent. Retained sky-depth surfaces prevent the stage
  leakage caused by treating sky portals as empty holes.

No complete native visual equivalence is claimed. The per-map audits retain the
exact shader parameters and affected entities so these limitations can be
investigated without changing original map artwork.
Five classics: full command routes and limitations

Download this document

# Classic-five route validation

This release adds `surf_beginner`, `surf_year3000`, `surf_ace`, `surf_legends`, and `surf_eclipse`. Validation has two separate purposes: independently compare imported movement against public native CSS records, and demonstrate that each complete local course can be traversed with ordinary input commands from its canonical spawn.

## External reference and limits

`fixtures/classic-five-source-metadata.json` pins the public CSS, 66t, forward-style main-course record URLs, downloaded hashes, metadata and decoder provenance. `fixtures/classic-five-telemetry-summary.json` retains start/finish bookmarks and teleport samples. The independent decoder does not import movement code.

Beginner, Year3000 and Ace use v3 native records with recorded analog magnitudes and ground flags. Legends and Eclipse use v2 records, which omit those fields. A button mask alone cannot recover analog magnitude or partial key presses, so their raw comparison assumes 400-axis input and identifies this uncertainty explicitly. The views recorded on non-landmark portal arrival overwrite the pre-portal input view; that input is also unrecoverable from the record. Last known physical view is used for those isolated ticks. Landmark portals retain the next recorded view.

Before evaluating any map, `scripts/validate-classic-five-reference.ts` declares strict single-step tolerances of **0.002 Source units** and **0.002 units/second**. These are not widened to accommodate differences. Native states supply initial conditions only in isolated diagnostic fixtures. Their next states remain external expected values. The report records first divergence, maxima, contact differences where flags exist, and all failed ticks.

Two-hop stage transitions in Year3000 and Eclipse contain intermediate entity destinations followed by actual stage spawns. They are separately audited as stage handoffs rather than counted as passing movement samples. The browser holds the portal view during that handoff; the intermediate native positions are not added as playable geometry.

The captured Beginner stage portals carry world velocity into authored destinations. Collision with the arrival enclosure subsequently removes the blocked velocity components. Ace's eight stages are a physically continuous course with native speeds well above 350 units/second across the stage boundaries. Neither behavior should be replaced with a generic stage reset. Legends' internal portal translates a landmark-relative position while retaining world velocity and view angles.

## Strict independent results

These are isolated next-tick comparisons against unchanged external records. They deliberately include failures; they are not a parity certificate. Reset-zone and timer events are excluded here and tested separately in the continuous session.

| Map | Compared ticks | Within both tolerances | Outside tolerance | First divergent native tick | Maximum position error (u) | Maximum velocity error (u/s) |
| --- | ---: | ---: | ---: | ---: | ---: | ---: |
| beginner | 3163 | 2085 | 1078 | 132 | 2208.512259 | 1412.614380 |
| year3000 | 1961 | 1383 | 578 | 208 | 0.002303 | 0.031936 |
| ace | 2787 | 2078 | 709 | 141 | 0.002058 | 0.032650 |
| legends | 2185 | 2183 | 2 | 1 | 0.957867 | 63.851815 |
| eclipse | 4277 | 4255 | 22 | 1 | 8415.554858 | 2752.319841 |

The large Beginner maxima occur at portal/finish entity transitions, including source-side ground categorization versus destination placement; the continuous witness uses a slightly different legal line through those portals. Year3000/Ace differences are much smaller, dominated by float/contact behavior (maximum velocity errors about 0.032 u/s), but still fail the predeclared strict tolerance. Their native ground flags allow a separate categorization check. Legends and Eclipse lack recorded ground flags; a zero count there means unavailable evidence. Native contact normals are unavailable in all five records.

Legends has initial settling and v2 input uncertainty. Its separate continuous witness additionally avoids an authored jail teleport intersected by the sampled record, retaining the original reset geometry. Eclipse has an additional recorded 95 u/s push effect while the captured player hull is outside the acquired BSP's authored push volume. Neither volume was shifted to fit the recording. The public replay metadata carries no BSP checksum: the acquired mirror is hash-pinned, but historical native records cannot be proven to use identical BSP bytes. Private server behavior and map-revision differences remain unresolved. Eclipse's largest isolated discrepancy is a stage-return portal touch at native tick 3735; the complete command witness avoids that touch. It is separate from the missing recorded push.

## Command-only proof

`scripts/plan-classic-five-routes.ts` reads native commands or reconstructs missing v2 input fields for offline planning. Inference against native expected states is **not** included in the independent raw reference score. The planner may adjust ordinary yaw or movement input to find a viable local line; any adaptation is declared in the associated planning fixture.

The canonical run uses the imported map's normal spawn. If needed, `appendCanonicalApproach` walks to a stationary reference start using ordinary movement commands while the timer remains ready. It never assigns a player pose or velocity. The exported replay contains only the canonical initial state and tick commands. The server's `verifyReplay` independently reconstructs the full run and requires ordered checkpoints, a finish, no reset, no practice state, and no invalid timer.

Every saved witness is simulated again under 30, 60, 144 and 240 FPS render schedules. Position, velocity, contacts, events and timing must be bit-identical at every tick. This establishes fixed-step scheduling and route playability, not exact CSS parity or human performance.

## Complete course results

All five canonical witnesses pass server replay verification, ordered progression, finish detection and the four render schedules. Every tick has identical state, contacts, events and time across schedules; first divergent tick is null. These are local command-witness times, not KSF record times.

| Map | Commands | Verified local time | Splits | Surf-contact ticks | 30/60/144/240 FPS |
| --- | ---: | ---: | ---: | ---: | --- |
| beginner | 3,801 | 45.657589 s | 6 | 1049 | Pass |
| year3000 | 1,962 | 27.533349 s | 1 | 579 | Pass |
| ace | 3,441 | 40.122375 s | 7 | 675 | Pass |
| legends | 2,184 | 30.879061 s | 4 | 276 | Pass |
| eclipse | 4,342 | 62.734865 s | 2 | 1201 | Pass |

Each report pins the geometry, literal replay and current physics-source hashes. Separate browser evidence validates the actual built application; these offline results do not substitute for browser QA.

## Witness adaptations and scope

- Beginner: ordinary 643-command walk from canonical spawn to the stationary native start, then two smooth yaw windows (maximum adjustments about 0.054° and 0.115°). These avoid problematic source/arrival contacts without changing the map or controller.
- Year3000: recorded v3 analog commands and view angles from its canonical stationary spawn, with no route adjustments.
- Ace: ordinary 654-command walk inside the start zone, then unmodified v3 native input through all eight continuous stages.
- Legends: inferred v2 analog input plus two declared crouch ticks and bounded yaw windows; the authored landmark portal and jail reset remain active. Details and literal deterministic windows are in scripts/plan-legends-route.ts.
- Eclipse: inferred v2 analog input and deliberate surf-line/timing changes through stage two to compensate for the unverified recorded push. The input history records each bounded yaw window and held-input extension. Stage one and stage three use native-derived commands. The final ramp holds D at yaw 90° until Y=-4650, then uses gentle ordinary A/D turns toward the portal. The offline planner generates those literal inputs; it is never part of the runtime player controller. The actual stage-three handoff occurs at command 2814, followed by the independently checked 1528-command stage-three segment. The full canonical replay, rather than the stage-only diagnostic, establishes completion.

The importer also distinguishes Eclipse's authored damage-only trigger model 53 from a teleport. The native route crosses this volume without resetting; treating all damage as an immediate return to start had fabricated a reset barrier. The volume remains in source metadata, and only its unconditional-reset interpretation is excluded for this map. The exact private server damage/immunity setting remains unknown.

All 21 advertised practice locations are tested through actual practice travel and 100 idle commands: no initial solid overlap, no reset, grounded at rest, and normal timing invalidated. These practice checks are separate from canonical ranked-route proofs.

## Reproduction

```sh
npx tsx scripts/decode-classic-five-replays.ts
npx tsx scripts/validate-classic-five-reference.ts
npx tsx scripts/validate-classic-five-practice.ts
npx tsx scripts/plan-classic-five-routes.ts beginner --canonical
npx tsx scripts/plan-classic-five-routes.ts year3000 --canonical
npx tsx scripts/plan-classic-five-routes.ts ace --canonical
npx tsx scripts/plan-classic-five-routes.ts legends --canonical
npx tsx scripts/plan-classic-five-routes.ts eclipse --canonical
# Optional: regenerate the final Eclipse flight using the saved legal prefix
npx tsx scripts/plan-eclipse-final-flight.ts
npx tsx scripts/validate-new-maps.ts beginner year3000 ace legends eclipse
```

Per-map command validation lives in `fixtures/{slug}-command-validation.json`; raw native next-tick comparisons live in `fixtures/{slug}-native-reference-validation.json`. Complete watch routes live in `public/replays/{slug}-complete.json`.
Existing leaderboards: compatibility evidence

Download this document

# Preserving existing records through the five-map release

The five new maps require two small session rules: momentum-preserving portals can advance a stage, and continuous stage gates advance progress without applying a new start-speed cap. An optional practice-stage selector also lets an invalidated practice spawn settle independently of the authored portal destination. These additions change the source fingerprint even though their new branches are inactive on the ten previously shipped maps.

The release therefore extends the explicit board-compatibility certificate instead of pretending the code hash stayed unchanged or assigning a new board to every old map.

## Reviewed source boundaries

- Previous deployed workspace fingerprint: `077f4293b446b922f602fee61592a6e5acf87f82c8bbfee5181bfddafdb5cbf7`.
- Reviewed current fingerprint: `a9b0902bac5fe3236183b202dd886e05ca0761fb9f1ceaa68691802867571f3d`.
- Original Boreas/Utopia/Mesa identity fingerprint remains `7eb182eab3c2e31803b4b94374242d02bd29821793b5ab66e86e705fc11d9ded`, traced to their original Git baseline and its separately reproduced six-route certificate.

The previous release came from a workspace containing uncommitted source. It is not reconstructed from a misleading Git commit label. The reviewed pre-change source was captured before the stage edits, verified against the published previous fingerprint, and preserved as `fixtures/classic-five-compatibility-baseline.json` (SHA-256 `ed88dbe92252cbfa214dab8c17d8184a4e032841a9acb42fa1b9bb014f82d93b`). All imported supporting game/physics/map modules are included, so another checkout can reproduce the comparison without the original ignored cache.

Source review and executable preconditions confirm that none of the ten old maps declares continuous progression, a momentum-preserving stage portal, or a momentum-preserving portal checkpoint. Kitsune's existing stage-reset portals already assigned the same stage before replacing velocity. Every prior map's exact geometry bytes, configuration, version, canonical initial state and original board inputs remain pinned.

## Differential results

**59,695 tick comparisons across 16 complete runs matched exactly** in player state, previous state, contact/movement result, timer state and emitted events. Every comparison recorded zero position/velocity/timing error, zero contact differences and no divergent tick.

All ten canonical ranked witnesses independently passed the current server replay verifier:

| Map | Canonical command ticks | Finish seconds |
| --- | ---: | ---: |
| Boreas | 3,387 | 39.495359 |
| Utopia | 4,531 | 53.349069 |
| Mesa | 4,365 | 52.635008 |
| Demise | 3,244 | 37.173847 |
| Kitsune | 6,333 | 91.940469 |
| Aircontrol | 3,312 | 35.686688 |
| Lux | 2,827 | 31.267094 |
| Nyx | 3,480 | 40.548546 |
| Reprise | 3,877 | 55.555266 |
| Fornax | 3,783 | 43.942787 |

Boreas/Utopia/Mesa also reran their six original stationary-start legacy/ranked witnesses. Those trajectory hashes reproduce the original Git-baseline certificate exactly. Their new canonical witnesses prepend ordinary movement commands to reach the documented start-deck point; no pose, velocity or contact state is injected. These generated routes are stored in `fixtures/compatibility/` and are test evidence, not human leaderboard entries.

Full results are in `fixtures/classic-five-compatibility-results.json`; the active compatibility certificate remains `fixtures/legacy-physics-compatibility.json`. The pre-change catalog and three-map certificate are retained as `fixtures/classic-five-prior-online-catalog.json` and `fixtures/classic-five-prior-physics-compatibility.json`.

## Fail-closed identity rules

The catalog always publishes the **actual current** physics fingerprint. A map retains an old board ID only when its current source hash, map ID, slug, version, geometry hash and configuration match the reviewed certificate. Each old identity hash must equal one of the two independently pinned baseline hashes, and re-serializing the full original board inputs must recreate its exact board ID. An arbitrary historical hash is not accepted. The five new maps have no compatibility pins and use ordinary identities from the true current source.

Changing movement source, any old map geometry, its configuration, or its version makes the build fail until compatibility is deliberately reviewed again or the relevant old identity is retired. A passing finite fixture set is not a mathematical proof for every possible input and does not establish native CSS parity.

Reproduce without writing:

```sh
npx tsx scripts/certify-classic-five-compatibility.ts --check
npx tsx scripts/certify-legacy-physics.ts
```

After reviewing any further core change, explicitly regenerate with `--write --reviewed-existing-noop`. The old three-map script remains a working verifier, but refuses to overwrite the expanded certificate and discard the seven additional maps' evidence.
Fornax: local import and validation

Download this document

# Fornax local import

6 October 2026. **surf_fornax by iNooVa**, original CS:S tier-1 linear main course, two checkpoints. Added to the map menu, featured rotation, remembered selection, real-render preview, Watch route, local records and generated online map identity. This is a local review build; no deployment or database write was performed.

## Source and implementation

Pinned BSP SHA-1 `2194b0e9d8153d75837dfb8e421d040c8072c700`, 174,311,018 bytes. Sources and source-reference hashes are in [FORNAX-REFERENCE.md](FORNAX-REFERENCE.md). Geometry and artwork come from this same CSS BSP; no CS:GO or CS2 port is used. The shared movement, collision, timing, input and renderer implementation is unchanged.

- 5,314 compiled solid brushes with their original planes/bevels. All 434 visual displacements are excluded from player collision by source metadata; no collision proxy was invented. The 198 static props are non-solid in the BSP.
- Original `Map_start` / `Map_end` convex volumes, main failure shell, and map-authored 5,000-unit per-component velocity limit. The native KSF viewer corroborates that limit.
- The canonical spawn uses authored `map_dest` X/Y and yaw, settled on its real deck at Z14976.03125. Both player hulls are unobstructed and the spawn remains grounded without input.
- Two local checkpoint slabs follow KSF's two main-course bookmarks. Their widths span the physical corridors, including both sides of the second ramp. Private KSF timer-zone coordinates were not available. Exact bounds and entity review are in `scripts/import_new_maps.py` and `fixtures/fornax-entity-review.json`.
- The original reset shell remains a union of compiled convex brushes. Its enclosing bounding box alone would incorrectly reset valid routes. Bonus/hub triggers and the initially disabled post-finish return are not enabled as main-course portals.
- The 0.015-second float32 simulation, air acceleration, start restrictions and leaderboard validation rules remain the existing Surfd configuration. All nine previous board identities are retained exactly; the new map has its own geometry/configuration identity.

Only the main course is integrated. Seven bonuses are present in the source artwork but are not selectable courses or ranked bonus routes. Animated end-door/lift behavior after the main finish is outside the integrated route.

## Validation and limits

The [complete command witness](FORNAX-PLAYABILITY.md) starts at the unchanged canonical spawn, walks to a stationary reference starting point and surfs the entire course with normal movement/view commands. It passes server verification in **43.942787 seconds**, with both ordered checkpoints and no resets. All 3,783 command ticks, contact results and timing agree exactly under 30/60/144/240 FPS schedules.

Separate independent next-tick comparisons use the pinned native KSF v3 recording, including its analog input magnitudes. At the predeclared .002-unit / .002-unit-per-second limits, **2,113 of 3,056 comparisons pass**. All 943 strict failures involve collision contact; maximum errors are **.002238 units / .031427 units per second**, first divergence tick258, and all native grounded flags agree. These are reported failures, not a claim of exact equivalence.

Continuous native replay also exposes a thin-reset-trigger difference: native samples survive crossing a 2-unit trigger between their endpoints, whereas this simulator checks the complete movement sweep. The playable witness uses two declared tiny yaw adjustments to avoid that original trigger and correct later drift; no geometry, velocity, timer or player-state corrections are injected. Full-route aligned errors reach 7.397 units and 174.557 units/s at different contact times. See the evidence and reproduction in [FORNAX-PLAYABILITY.md](FORNAX-PLAYABILITY.md).

[Visual validation](FORNAX-VISUALS.md) checks the imported world/prop buffers, original material metadata, image pixels and compressed texture bytes. Two original bonus-area resources are missing and are reported without guessed substitutions. Source shader proxies, water/refraction, some lighting features, particles and animated brush/model behavior remain approximations or unsupported. Native pixel parity is not claimed.

## Completed review checks

- `npm test`: **371 passed**; importer Python suite: **17 passed**. Type checking and the production build pass. Vite retains the existing large-renderer-chunk advisory.
- All seven added-map canonical routes pass server verification and all four render schedules. Existing nine-map catalog entries match `fixtures/fornax-prior-online-catalog.json` exactly.
- The built game completes all **3,783 commands** in the actual browser with the same full player state, time and splits as headless verification. Real keyboard/Pointer Lock, R restart, walking into the failure shell, practice save/restore, menu sizes 1440/1280/390px and remembered map selection are exercised. Cloud transport is disabled in QA; no live record is written. Evidence: `fixtures/browser-new-map-results-fornax.json`.
- Both native-compressed and decoded texture loading paths pass request-set checks without errors. Native/fallback/native repeated-load resource counts return to the same values. The local 1280×720 renderer sample measured approximately **357–360 FPS**, p95 frame interval **2.9ms**; this is one machine's measurement, not a hardware-wide guarantee. Native packed transfer is **52.0MB / 25 requests**, fallback **65.3MB / 21 requests**, including visual manifest. See `fixtures/fornax-visuals/performance.json` and `packed-transfer.json`.
- An independent reviewer checked authored bounds, reset union, spawn, CP aperture coverage, exact compressed replay upload verification, file/catalog hashes and existing board compatibility. No remaining actionable integration finding was identified. The native fidelity limits above remain open and explicitly separate from passing browser/playability tests.

## Reproduce

Use the pinned source downloader, then the existing converters with a read-only licensed CSS installation:

```sh
python scripts/download_new_maps.py
python scripts/audit-nyx-reprise.py fornax
python scripts/import_new_maps.py fornax --game-dir "PATH/Counter-Strike Source"
python scripts/import_boreas_visuals.py node_modules/.map-imports/surf_fornax.bsp --slug fornax --map-id surf_fornax --author iNooVa --game-dir "PATH/Counter-Strike Source" --texture-format lossless-webp --compressed-textures
python scripts/validate_new_map_visuals.py --slugs fornax --game-dir "PATH/Counter-Strike Source"
npx tsx scripts/decode-fornax-replay.ts
npx tsx scripts/validate-fornax-reference.ts
npx tsx scripts/plan-fornax-route.ts
npm test
npm run validate:new-maps
npm run build
npm run preview -- --port 4197
```

`UI_BASE_URL=http://127.0.0.1:4197 npx tsx scripts/browser-new-map-qa.ts fornax` exercises the built game, complete route, Pointer Lock, restart, menu layout and finish splits without online writes. For renderer-only captures and performance, use the development server (4196 in this review), which exposes renderer source modules. `CAPTURE_MAPS=fornax` selects the deterministic real-render preview camera.

## Publication and credit

The [author's upload](https://gamebanana.com/mods/137780) explicitly lists **CC BY-NC-ND 4.0** and asks permission for redistribution or distribution of modified parts. No permission grant has been established here. Review/resolve those rights before publishing this converted map; do not treat its public download as a permissive asset licence. Main map/bonus4: iNooVa; bonus contributors: Mariowned, Biji, FPS God, vay, hesuka, 8X. Full captured source credits and third-party notices are retained.
Fornax: independent CSS evidence

Download this document

# Independent CSS reference for Fornax

Captured 6 October 2026 for local import and validation. The [primary KSF map page](https://ksf.surf/maps/surf_fornax?game=66t&mode=fw) identifies **surf_fornax**, iNooVa, tier 1, linear, **2 main checkpoints**, and 7 bonuses. The [original CSS upload](https://gamebanana.com/mods/137780) identifies iNooVa as the creator and explicitly marks the submission as not a port. The similarly named CS:GO/CS2 ports are separate evidence and do not supply this import's physics settings.

## Source and rights

The decompressed CSS BSP is **174,311,018 bytes**, SHA-1 **`2194b0e9d8153d75837dfb8e421d040c8072c700`**, independently verified from the local source bytes. [Pinned mirror download](https://main.fastdl.me/h2/2194b0e9d8153d75837dfb8e421d040c8072c700/surf_fornax.bsp.bz2), [mirror index](https://main.fastdl.me/maps_ksfthings.html). The original creator's download is `surf_fornax.rar`, [GameBanana file 426873](https://gamebanana.com/dl/426873), listed as 63,762,093 bytes with archive MD5 `1f0012cf8e6cc6d9ab814164d41c2190`. That archive was not downloaded or compared to the mirror BSP; the two identities must not be conflated.

The author's [public upload metadata](https://gamebanana.com/apiv11/Mod/137780/ProfilePage) is captured in `fixtures/fornax-gamebanana-source.json` (28,301 bytes, SHA-256 `d1d0923a4d6a2592a54801b2118633f1b952718837c2ea16cd50ac253b263b5b`). It lists **Creative Commons Attribution-NonCommercial-NoDerivatives 4.0**, with an additional checklist asking permission for redistribution and distribution of modified assets or parts, and disallowing commercial use. This evidence supports local research and testing; it does not establish permission to publish the converted browser map. Preserve that distinction when reviewing a release. No author contact or permission grant is claimed.

Published credits: main map and bonus 4 **iNooVa**; bonus 1 **Mariowned**; bonus 2 **Biji**; bonus 3 **FPS God**; bonus 5 **vay**; bonus 6 **hesuka**; bonus 7 **8X**. The captured metadata also preserves the original playtester credits. Bonus geometry may be present in the full BSP even though only the main course is integrated.

## Pinned external recording

The [KSF viewer](https://ksf.surf/replays/surf_fornax/replay_css_3543_0_782816_1791019618.rec?game=66t) identifies `game: css`, `zoneId: 0`, `finishType: 0`, and `tickRate: 66.66666666666667`. The selected record is **Mercedes-Benz S124 om602 5-Door**, rank 1 at capture, recorded **2026-10-03 09:26:58 UTC**, published time **43.941490173339844 seconds**. This is an external native recording, not a simulator-generated trajectory.

- [Native file](https://ksf.surf/api/replays/replay_css_3543_0_782816_1791019618.rec?game=66t): `replay_css_3543_0_782816_1791019618.rec`.
- SHA-256: `3def39687b282cfbd4251422254078da654d14d811dd6e9faaadf6708488b9ab`; **246,884 bytes**.
- Version **3**: 3,390 frames, five bookmarks, 18 cells per frame, 40 extension cells. Header 184 bytes; bookmarks 524 bytes each; frames 72 bytes each.
- All recorded flags are 0 or 1; there are 406 grounded frames and **no teleport flags**. No adjacent frames exceed the separate 256-unit jump diagnostic. Neither fact alone proves every possible teleport absent in the map.

The decoder follows the independently inspected [public KSF replay reader](https://ksf.surf/gokz/js/replayviewer.js), captured as `fixtures/ksf-replayviewer-2026-10-05.js`, SHA-256 `be8222c64b99151cdd7d542e30a2735d3d110de08a5f60d3b0a3343c2f3c552d`. Downloaded JavaScript is inert reference text and is never executed.

`scripts/decode-fornax-replay.ts` rejects changed replay bytes, unsupported layouts, nonfinite state/command values and out-of-bounds bookmarks. It preserves v3 analog forward/side/up commands, flags, command counters, view angles, positions and velocities. Five measured half-side commands occur at frames **186, 623, 2057, 2245 and 2483** (respectively `200,200,-200,200,200`); replacing every pressed key with 400 loses this evidence. The opaque extension is retained as raw hex and numeric cells without inventing names or server-cvar meanings.

Reproduce decoding offline with `node --import tsx scripts/decode-fornax-replay.ts`. `--fetch` refreshes only the pinned public record and primary metadata; it cannot silently switch to a newer leaderboard record. `fixtures/fornax-source-metadata.json` preserves the viewer context, source URLs and page/file hashes. `fixtures/fornax-ksf-telemetry.json` contains every frame; `fixtures/fornax-telemetry-summary.json` contains exact event states, timing and speed extrema.

## Native course landmarks

The record begins stationary and grounded at **`(-13703.9248046875,-3280.03515625,14976.03125)`**, with yaw `-28.288022994995117`, pitch `21.493955612182617`. This is a recorded standing position on the deck, not the authored destination. The BSP's `map_dest` is **`(-13888,-3776,15320)`**, yaw 0, Hammer ID 434517.

| Event | Frame | Recorded position | Published split seconds |
| --- | ---: | --- | ---: |
| Main start exit | 127 | `(-13485.08203125,-3542.295654296875,14981.46484375)` | — |
| Checkpoint 1 | 1098 | `(-7020.59375,-3674.664306640625,8556.3046875)` | 14.561758995056152 |
| Checkpoint 2 | 1953 | `(-13874.4345703125,-3652.474853515625,6643.1142578125)` | 27.394136428833008 |
| Main finish | 3056 | `(-8766.2578125,-4796.93310546875,2744.794921875)` | 43.941490173339844 |

There are 2,929 nominal 15 ms intervals between start and finish: **43.935 seconds**, about 0.006490 seconds below the published time. The public bookmark positions establish native events, not private timer zone bounds or a recovered sub-tick timing algorithm. The recorded start's horizontal speed is below 350, so this recording does not independently establish a 350 start-exit clamp plateau.

The authored `Map_start` trigger is model `*86`, Hammer ID 590059, with world bounds `(-14336,-4288,14976)` to `(-13504,-3264,15488)`. Authored `Map_end` is `*87`, Hammer ID 590102, bounds `(-9088,-4864,2688)` to `(-8768,-2688,2752)`. The BSP has no authored main checkpoint triggers; the two KSF CP bookmarks require explicitly documented local gates. [SurfHeaven](https://surfheaven.eu/map/surf_fornax) reports three checkpoints under its own timer; this does not supersede the two-CP KSF course selected here.

The geometry-only checkpoint aperture review is reproducible with `node --import tsx scripts/probe-fornax-checkpoint-apertures.ts`; results are saved in `fixtures/fornax-checkpoint-aperture-review.json`. It independently intersects the imported compiled brush half-spaces with a standing hull at Y −3648, without using the native line to choose a narrow X band. The main CP1 chute has authored structural boundaries at X −7808 and −6528, floor 8384 and roof 9152; the local CP1 gate spans this aperture. CP2's boundary walls lie at X **−15296 and −11904**, with ramp `bsp-0-4148` inside. At feet Z 6643, the two open standing-hull X intervals are approximately `(−15280,−14480)` and `(−13874.3842,−11920)`. A proposed gate ending at X −12288 would cut off part of the east passage and was rejected during review. The local CP2 gate must cover the corridor to X −11904. These are static aperture checks, not a proof that every airborne trajectory through each opening is reachable or a claim to recovered KSF zone bounds.

## Map effect evidence

The BSP's `logic_auto` (Hammer ID 645407) issues `maxvelcommand,Command,sv_maxvelocity 5000,0,-1` on map spawn; `maxvelcommand` is a `point_servercommand` (Hammer ID 645409). The KSF viewer independently reports **5000**. The recording's largest absolute velocity component is only **4144.24853515625**, so there is no measured 5000 plateau in this recording. Do not describe the cap as measured from velocity saturation.

The entity graph has no `player_speedmod`, `trigger_push`, or `trigger_gravity`; no other movement cvar output was found. This is evidence about the compiled map, not a private KSF plugin manifest.

The reviewed graph contains 64 `trigger_teleport` entities and no teleport landmarks. Two target `map_dest`: hub entry `*3`, and the compiled failure shell `*59`. The latter's enclosing bounds cover much of the main course; its individual compiled convex brushes must be retained, since one enclosing box would reset valid runs. Other destinations serve the hub/bonus routes. `start1dest` (`*132`) begins disabled and is enabled temporarily by the post-finish lift trigger `*131`. Its authored delayed outputs must not become an unconditional main-route portal. There is no native main-route teleport in the pinned record.

## Strict comparison contract

Predeclared isolated next-tick tolerances are **0.002 units in position and 0.002 units/s in velocity**. Expected states come only from the pinned external record. Comparisons use actual v3 analog magnitudes and buttons from the current frame, following-frame view angles, imported effects and the existing Surfd start-exit rule. Native ground flags remain independent observations for checking categorization. Unknown opaque client settings are not guessed.

Run `node --import tsx scripts/validate-fornax-reference.ts` after importing the reviewed collision map. It writes `fixtures/fornax-native-reference-validation.json`, retaining first divergence, maxima and all failing samples under the original tolerances. These isolated comparisons do not prove full continuous route playability or exact private-server parity; a command-only canonical witness and browser verification are separate work.

The reviewed geometry includes both authored timer convexes and the full CP2 corridor. Its collision JSON SHA-256 is `152d5dedb69be2de10e942e36e5e9886709f7ae74e5c9e8f73f33df032700451`. The refreshed comparison passes **2,113 of 3,056 ticks**, with **943 strict failures**, all during collision-contact ticks. Thirteen exceed the position tolerance; no free-flight tick fails either tolerance. First divergence is native frame **258** on compiled ramp `bsp-0-4399`: position error `0.0009765625` units, velocity error `0.031235751241464764` units/s. The global maxima are **0.002237585788552656 units** at frame **449** and **0.03142711989809783 units/s** at frame **2848**. All recorded ground flags agree with categorization. No no-jump effect or route transfer is invoked. The report remains an explicit fidelity limit, not a passing native-parity claim; tolerances have not been widened to accommodate it.
Fornax: original artwork and performance

Download this document

# Fornax local visual import

The visual bundle converts the original CSS `surf_fornax` BSP and its authored
assets. The pinned source is 174,311,018 bytes with SHA-1
`2194b0e9d8153d75837dfb8e421d040c8072c700`, credited to iNooVa. No map artwork was
generated or replaced. The installed CSS directory is a read-only fallback
mount for ten original stock resources.

This work remains local. The [author's source page](https://gamebanana.com/mods/137780)
lists CC BY-NC-ND 4.0 and additional permission conditions. The conversion does
not establish permission to redistribute converted assets; see the separately
retained source/licence evidence in `fixtures/fornax-gamebanana-source.json`.

## Reproduction

```text
python scripts/import_boreas_visuals.py node_modules/.map-imports/surf_fornax.bsp --slug fornax --map-id surf_fornax --author iNooVa --game-dir "PATH/Counter-Strike Source" --texture-format lossless-webp --compressed-textures
python scripts/validate_new_map_visuals.py --slugs fornax --game-dir "PATH/Counter-Strike Source"
python scripts/audit-fornax-visuals.py --game-dir "PATH/Counter-Strike Source"
```

The shared importer and renderer were not changed for this import. No invalid
skin override is required. The bundle is `public/maps/fornax-visuals.json` and
`public/maps/fornax/`. Source audit and integrity evidence are retained in
`fixtures/fornax-visuals/`.

## Export and integrity

| Measure | Fornax |
| --- | ---: |
| Rendered BSP faces | 18,154 |
| Displacement surfaces | 434 |
| World material batches | 1,270 |
| Authored sky portal faces | 1,447 |
| Hidden entity faces | 6 |
| Static prop instances | 198 |
| Dynamic prop initial poses | 22 |
| Model variants | 158 |
| All geometry batches | 2,054 |
| Exported materials | 2,041 |
| Decoded texture images | 1,019 |
| Native compressed texture alternatives | 178 |
| Original environment cubemaps | 139 |
| World lightmap atlas pages | 1 |

All 1,019 images match their decoded source VTF pixels exactly, covering
93,390,848 pixels. All 178 DDS alternatives retain the original compressed
texture blocks and mipmaps byte for byte. The 2,054 geometry batches have valid
buffer lengths, finite vertex components and in-range triangle indices. All
198 static props retain the original model, origin, angles and scale. All 22
dynamic initial poses retain the original model and transform. All static prop
skin indices are zero; no props were dropped or assigned fallback skins.

These checks use the importer's decoders and do not establish independent native
rendering parity. Browser capture and sustained rendering checks are recorded
below.

The referenced bundle contains 142,787,795 bytes across both texture paths,
excluding the manifest and before transport compression. The decoded path is
78,955,595 bytes; the native path is 94,551,867 bytes. Both use 3,075 individual
asset URLs before pack generation. DDS files can be larger on disk than WebP;
their purpose is to preserve GPU compression. The compressed subset has
63,809,416 bytes of native mip data versus 342,359,976 bytes of corresponding
RGBA mip data, about 81.4% less. This comparison is not a browser/GPU memory
measurement. Packed transfer and browser performance are separate checks.

## Source artwork and transparency

The original `inoovasky` comprises six 2048 by 2048 images with identity UV
transforms. There is no `sky_camera` or fog controller. The importer retains
all 1,447 authored sky portal faces for the renderer's depth-only sky pass.
No artificial room hiding, walls or exposure adjustment was added.

The material inventory contains 1,242 LightmappedGeneric, 666 VertexLitGeneric,
122 UnlitGeneric, five Water and six UnlitTwoTexture materials. The audit records
95 materials with translucency, partial opacity or alpha testing, including
their decoded alpha ranges, cutoff thresholds and culling flags. Authored glass,
metal grates and the two-sided, alpha-tested stock tree material are retained.
The original diffuse lightmap atlas and available prop vertex lighting remain
in the bundle.

Two original resources are absent from both the BSP and mounted CSS archives;
searching those resources also found no matching basename elsewhere:

- `materials/saspatoon/space/metal/honeycombnormal.vtf` is referenced by three
  materials. Its diffuse honeycomb texture survives, while the normal texture
  is unavailable. This also affects static prop 128, the bonus ceiling model
  `models/surf_nebula2/b4/celling03.mdl`.
- `materials/models/surf_nebula2/fpsbramp2/glass_refract_02.vmt` is referenced by
  one mesh in static prop 89, `models/surf_nebula2/fpsbramp2.mdl`, at
  `(-9760, -11908, -15084)`. The mesh remains, but its material has the existing
  generic missing-material fallback. Its native appearance is unverified.

Both remain visible import warnings. No similarly named or newly created asset
was substituted.

## Known rendering differences

- The six UnlitTwoTexture forcefield materials preserve their first texture and
  additive/translucent flags, but their `$texture2` layer and Source shader/proxy
  behavior are unsupported. The browser's unlit flag currently applies only to
  UnlitGeneric, so these forcefields do not reproduce native lighting either.
- Five Water materials retain authored geometry and available texture data;
  Source reflection/refraction, underwater fog and water shader behavior are
  unsupported. Water and forcefield VTFs with 29 and 31 animation frames export
  frame zero. Scrolling ramp material proxies are also static.
- All 19 rotating brushes and both linear movers stay at their authored initial
  transforms. Eighteen of the 22 dynamic props are parented rotators, one is the
  end door, and one is a rotating signature; two arrow props are unparented.
  Rotation, parent animation and the end-door movement are not reproduced.
- Thirty `env_sprite` entities, 36 dust-mote volumes, one dust-cloud volume and
  two screen fades are not reproduced. General entity visibility/I/O and native
  PVS/areaportal culling are outside the visual importer.
- Original baked diffuse lighting is retained, but Source HDR exposure,
  directional bumped lighting and material response are not equivalent. The
  audit identifies 375 materials requesting self illumination, 15 SSBump
  materials and 13 explicit environment-mask materials. Complete self
  illumination, SSBump basis, those masks and authored reflection
  contrast/saturation/light-scale behavior remain renderer limitations.

The source audit retains exact material indices, original parameters, affected
prop transforms, sky material definitions and relevant entity data so these
limits remain reviewable. Native visual parity is not certified.

## Browser preview and rendering review

The lobby preview is an actual 1280 by 720 browser render of the industrial
main-route chamber. Its fixed Source-coordinate eye position is
`(1000, -3776, 7900)`, yaw 180, pitch 15 and FOV 90. The elevated camera shows the
paired ramps and illuminated walls together; it is a visual review camera,
not a claim that a player occupied this position. Geometry, lighting and artwork
remain unchanged, and the screenshot has no image edits.

`scripts/capture-new-map-previews.ts` contains this Fornax-only camera. Reproduce
with `UI_BASE_URL=http://127.0.0.1:4196`, `CAPTURE_MAPS=fornax` and
`CAPTURE_FRACTIONS=0.05,0.45,0.8`. The retained report also includes the authored
start and three native-recording camera positions. All five final captures
completed without browser errors. `fixtures/fornax-visuals/preview.json` pins the
preview image SHA-256 and complete camera settings; `browser-views.json` records
the views and renderer counts. The full-quality review capture is
`docs/screenshots/new-maps/fornax-preview.jpg`; the lobby image is
`public/previews/fornax.jpg`.

The packed native, decoded fallback and repeated native texture paths each
completed an eight-second camera traversal in the same browser canvas, using
1280 by 720 balanced quality. This animates the camera along the original native
recording and does not run or certify player physics. Exact selected pack URLs
were asserted, including rejection of downloads for the other texture path.
No page or console errors occurred.

| Texture path | Average FPS | p95 interval | Longest interval | Local preparation |
| --- | ---: | ---: | ---: | ---: |
| Native | 359.7 | 2.9 ms | 5.7 ms | 716 ms |
| Decoded fallback | 356.6 | 2.9 ms | 5.7 ms | 1,774 ms |
| Native repeat | 357.9 | 2.9 ms | 5.6 ms | 923 ms |

Peak sampled work was 1,451 draw calls and 214,121 triangles. First and repeated
native rendering ended with exactly 2,125 geometries, 321 textures and 25 shader
programs. Those matching counts are a resource-disposal regression check, not a
measurement of total browser/GPU memory. The fallback ended with 329 textures;
decoded textures are prepared differently. These results reflect this machine
and its apparent 360 FPS limit, not a device guarantee. Preparation uses a local
development server and is not an internet download estimate.

| Packed visual transfer, including manifest | Bytes | Requests |
| --- | ---: | ---: |
| Native | 52,013,837 | 25 |
| Decoded fallback | 65,344,972 | 21 |

Exact output is retained in `fixtures/fornax-visuals/performance.json` and
`packed-transfer.json`. Reproduce with `scripts/new-map-render-performance.ts`,
`UI_BASE_URL=http://127.0.0.1:4196` and
`RENDER_CASES='[["fornax","native"],["fornax","fallback"],["fornax","native"]]'`.
Fornax: full command route and limitations

Download this document

# Fornax command playability

`public/replays/fornax-complete.json` is an automated full-route witness from the imported canonical spawn. It contains ordinary forward, side, jump, duck, yaw and pitch commands. It never injects a position, velocity, contact state, checkpoint or timer value. This is a planned route for the existing simulator, not a human performance or an exact CSS/KSF parity claim.

The authoritative reproduction command is:

```sh
npx tsx scripts/plan-fornax-route.ts
```

The script reconstructs the native v3 commands, applies the two explicitly recorded yaw windows, writes `fixtures/fornax-planned-commands.json`, and calls the existing canonical export verifier. The final report is `fixtures/fornax-command-validation.json`; `fixtures/fornax-planning-evidence.json` also records source identity, the unmodified continuous-route diagnostic, the trigger-sweep limitation and planning decisions. Geometry, replay and trajectory hashes identify the actual validated files.

## Route and validation

The canonical spawn is `(-13888,-3776,14976.03125)`, yaw 0, with zero velocity. The existing `appendCanonicalApproach` helper reaches native frame zero `(-13703.9248046875,-3280.03515625,14976.03125)` with 727 ordinary ground commands, remains inside the start zone, and finishes stationary without starting the timer. Another 3,056 normal commands complete the route, for 3,783 commands overall.

The canonical fixture is accepted by `server/verify.ts`. Its report compares 30, 60, 144 and 240 FPS schedules against the same baseline at every physics tick: complete player state, full movement results including contacts and segments, timer state and events are byte-identical. The route touches 66 collision surfaces and has 943 surf-contact ticks. It never resets, enters practice mode or modifies initial player state.

The accepted elapsed time is **43.942787 seconds**, with ordered splits **14.561853 / 27.394243 seconds**. Start, CP1, CP2 and finish occur at canonical command ticks **854 / 1825 / 2680 / 3783**. The start and finish use authored `Map_start` (`*86`) and `Map_end` (`*87`) trigger geometry. The two CP gates are independently reviewed local corridor boxes, not recovered private KSF zone boundaries.

## Independent native record and adaptations

The source is the public CSS 66t forward main-course record by **Mercedes-Benz S124 om602 5-Door**, published on 2026-10-03 with time 43.941490173339844 seconds. The pinned recording is `replay_css_3543_0_782816_1791019618.rec`, SHA-256 `3def39687b282cfbd4251422254078da654d14d811dd6e9faaadf6708488b9ab`, 246,884 bytes. [KSF map](https://ksf.surf/maps/surf_fornax?game=66t&mode=fw), [native replay viewer](https://ksf.surf/replays/surf_fornax/replay_css_3543_0_782816_1791019618.rec?game=66t).

The independent v3 decoder preserves analog forward/side commands and flags. Reconstruction uses movement magnitudes and button bits from sample `i`, and view angles from sample `i+1`. Native forward/side magnitudes, jump, duck and pitch are unchanged. Only yaw is adapted, with smooth bounded windows at zero-based command indexes 450–650 and 775–940. The largest absolute control knot is 0.100677513031 degrees. The complete knot values and interpolation are in the reproduction script and planning report.

The first window was found with `plan-nyx-reprise-optimize.ts fornax --native-only --begin 450 --end 650 --target 800 --sigma .05 --generations 40 --population 30 --dimensions 6 --velocity-weight .2`. The second used `--begin 775 --end 940 --target 1000 --sigma .08 --generations 40 --population 30 --dimensions 8 --velocity-weight .3` on the resulting plan. Search clones contain only already-simulated command prefixes; the final export replays everything from canonical spawn.

The completed planned trajectory has maximum aligned native position error 7.396918108 units at native frame 3056 and velocity error 174.556827772 units/second at frame 2060, when the simulated player contacts `bsp-0-4153`. The preceding frame's velocity error is 0.053230611 units/second, showing how sensitive that contact is to small accumulated position differences. Native comparisons use the fixed 0.002-unit and 0.002-unit/second strict tolerances; the route does not pass native parity. The separate isolated next-tick native report must not be confused with continuous traversal or canonical command playability.

## Thin reset trigger limitation

Unmodified reconstructed commands follow the native record closely but reset at native tick 761, with maximum continuous position error 0.421185902 units and velocity error 0.072588458 units/second up to that point. More significantly, even the exact recorded native segment 760→761 intersects imported authored reset brush `bsp-59-5696` between samples. Both recorded endpoints are outside the trigger. Its bounds are approximately X `[1086,2306]`, Y `[-624,-336]`, Z `[12032,12034]`; the ducked player hull intersects it during the sweep.

The current engine tests complete movement sweeps. The recording shows that native play survives this crossing but does not by itself establish the private server's trigger-touch policy. The first steering window avoids the existing brush; it does not remove or shrink the trigger. The second returns the route close to native targets for subsequent surf contacts. No shared physics, collision, timer or reset semantics were changed to make this witness pass.

This work is local validation. Map source availability does not establish redistribution rights or authorize deployment.
Summer and Forbidden Ways: import and validation

Download this document

# Summer and Forbidden Ways

Local CSS imports, reviewed 8 October 2026. These are the KSF BSP variants of the requested maps. They use converted original artwork and compiled collision geometry. This document records a browser adaptation, not a claim of complete Source entity-system or private KSF server parity.

| Course | Author | Main course | Tier | BSP SHA-1 |
|---|---|---|---|---|
| surf_summer_ksf | Hardex & Tioga060 / JSurf Studios | 11 stages | 2 | 53ba357a9b4b64b3eab6351aa7334a5c618f0405 |
| surf_forbidden_ways_ksf | Krusty | Linear, 6 checkpoints | 2 | 2592c054432a1effbeecb7b8b96c8947f8c59a30 |

## Sources and reproduction

Summer's [author upload](https://gamebanana.com/mods/137916) offers the lighter KSF edition alongside the full map. Credits also include Juxtapo, Guadalupe, Mark C., not_a_zombie, Fnatic sponsorship and Ferry Gouw artwork. The captured page lists CC BY-NC-ND 4.0. The original soundtrack is not imported. Forbidden Ways' author, tier and main-course count are corroborated by its [CSS66t KSF page](https://ksf.surf/maps/surf_forbidden_ways_ksf?game=66t&mode=fw). No new redistribution permission is asserted.

The downloader pins the decompressed size and SHA-1 in `scripts/fixtures/new-maps-sources.json`. Summer is 323,004,133 bytes; Forbidden Ways is 13,588,440 bytes. BSP inputs stay in the ignored `node_modules/.map-imports` cache. The installed CSS directory supplies missing stock assets read-only.

```text
python scripts/download_new_maps.py
python scripts/import_new_maps.py summer --game-dir "C:\Program Files (x86)\Steam\steamapps\common\Counter-Strike Source"
python scripts/import_new_maps.py forbidden-ways --game-dir "C:\Program Files (x86)\Steam\steamapps\common\Counter-Strike Source"
python scripts/import_boreas_visuals.py node_modules/.map-imports/surf_summer_ksf.bsp --slug summer --map-id surf_summer_ksf --author "Hardex & Tioga060" --game-dir "C:\Program Files (x86)\Steam\steamapps\common\Counter-Strike Source" --texture-format lossless-webp --compressed-textures
python scripts/import_boreas_visuals.py node_modules/.map-imports/surf_forbidden_ways_ksf.bsp --slug forbidden-ways --map-id surf_forbidden_ways_ksf --author Krusty --game-dir "C:\Program Files (x86)\Steam\steamapps\common\Counter-Strike Source" --texture-format lossless-webp --compressed-textures
node --import tsx scripts/decode-summer-forbidden-replays.ts
node --import tsx scripts/decode-summer-route.ts
python scripts/audit-summer-forbidden.py
node --import tsx scripts/validate-classic-five-reference.ts summer forbidden-ways
node --import tsx scripts/plan-classic-five-routes.ts summer --canonical
node --import tsx scripts/build-forbidden-ways-route.ts
node --import tsx scripts/validate-new-maps.ts summer forbidden-ways
npm run build
```

Visual conversion preserves both native S3TC textures and decoded lossless-WebP fallbacks. `scripts/capture-summer-forbidden-previews.ts` renders original assets through the production renderer. With `UI_BASE_URL` pointing to the local development server, it captures 22 Summer and 14 Forbidden Ways views without browser errors. These reference camera poses establish rendering only, not successful player traversal. Full game QA uses the built preview server with `node --import tsx scripts/browser-summer-forbidden-qa.ts` and the same environment variable.

## Geometry, stages and local timer rules

Summer retains 7,683 convex brushes, 179,008 displacement collision triangles and 2,032 rendered displacement surfaces. Its real `s1` through `s11` destinations and momentum-preserving portals are retained. Stage checkpoints use each next stage's authored `stageN_start` convex union. Native bookmarks corroborate the arrival order. Portal contact must not substitute for arrival: clipping the timer sweep at an oblique teleport face can otherwise lose a checkpoint numerically. The start and finish use authored `stage1_start` and `stage11_end` hulls. All 11 practice destinations settle without a reset.

Forbidden Ways retains 3,149 brushes and its three main-course momentum-preserving portals. Start and finish are authored `start` and `end` convexes. The six corridor checkpoint gates are explicitly local: independent native bookmarks identify their order and location, but private KSF timer boxes have not been recovered. Main failure volumes reset to the canonical spawn. Bonus routes are not registered as timed courses.

Summer's legacy contradictory bevel brushes use the same explicit compiled-axial-bounds fallback already used by Ace and Legends. The eight broadphase vertices do not replace any collision plane. Every affected brush and its original planes is retained in `fixtures/summer-forbidden-entity-review.json`.

Two importer defects were corrected. Displacement ordering now follows Valve's nearest-corner selection without an arbitrary 0.1-unit rejection; the compiled corners themselves remain unchanged. Summer has three ordering hints approximately 0.102–0.120 units from their nearest corners. Vector `$detailscale` now retains both UV axes in the renderer; equal-component vectors are accepted for scalar material factors. Regression tests exercise both cases. No shared movement, timer or collision implementation was changed by these importer/rendering fixes.

## Independent evidence and its limits

Primary Summer reference: Caff's CSS66t forward/main record, `replay_css_3161_0_340931_1689750457.rec`, SHA-256 `9d7d67b86d7477b0bb411fe749c23f0eaba07d8a5b33ffe8764052eba5a4585c`. Primary Forbidden Ways reference: `replay_css_1188_0_16530_1611588572.rec`, SHA-256 `1e3b97b9ed5ae436afacdad7de243f9128682c74b7f70d808208579752611c11`. Complete public URLs, record attribution, captured page hashes and native file lengths are in `fixtures/summer-forbidden-source-metadata.json`. Both references are v2 recordings: analog command magnitudes and native contact flags are not available.

`scripts/validate-classic-five-reference.ts summer forbidden-ways` performs independent one-step comparisons at fixed tolerances of 0.002 units and 0.002 units/second. Summer passes 10,077 of 10,103 comparisons; Forbidden Ways passes 3,186 of 3,194. The reports retain all 26 and 8 discrepancies. These comparisons are separate from continuous command-route verification; inferred analog commands are not used to inflate the independent results. The native Forbidden Ways capture begins already moving and demonstrates a boosted start that is not explained by a stationary canonical spawn under the existing capped-start rule. It must not be injected as a ranked initial state.

Summer's complete command route starts at the real canonical spawn, walks to an independently observed stationary deck pose, then completes all 10 checkpoints. It uses 10,582 ordinary commands and finishes in 147.756668 seconds under the server verifier. The planning reference is the separately preserved rank-6 recording `replay_css_3161_0_449650_1596798508.rec`, stationary frame 39. Bounded yaw adjustments are recorded in `fixtures/summer-planned-commands.json`; they alter commands, never geometry or player state. The full result is in `fixtures/summer-command-validation.json`, including identical states, contacts and timer results at 30/60/144/240 FPS schedules. This is an automated possibility witness, not a human run or native-world-record reproduction.

Forbidden Ways' complete route uses 3,839 ordinary commands from its actual canonical spawn and finishes in 56.594172 seconds. It passes all six checkpoints and all three main-course portals without a reset. Offline input search used the native path as a guide and retained enough ramp speed to complete the gaps under the unchanged capped-start rules. `fixtures/forbidden-ways-planned-commands.json` preserves the accepted inputs; `scripts/build-forbidden-ways-route.ts` reconstructs the entire run without loading any intermediate state, requires server acceptance, and checks identical states, contacts and timing at 30/60/144/240 FPS. `fixtures/forbidden-ways-command-validation.json` records the hashes and results. No start boost, velocity injection, ramp modification or reset-volume exception was added to make this route pass.

## Remaining adaptation limits

Summer's source package lacks three texture files (`devneons/orange_neon`, `fnatic/pier2/white_neon`, `metal/drtrime`) and three prop-local `toolsnodraw` material files. Their warnings are preserved; no replacement artwork is presented as original. Forbidden Ways reports no missing visual resources. Summer's converted asset set is substantially larger than the existing small classics.

The texture integrity check verifies all 513 Summer and 32 Forbidden Ways decoded images against their source pixels, plus all 498 and 13 native compressed textures against the source blocks. `scripts/browser-summer-forbidden-textures.ts` also renders both maps through the native and decoded packed-loader paths without browser errors. Reports are in `fixtures/summer-forbidden-visuals/`. Summer's native visual download is approximately 101.93 MiB over 53 requests (144.59 MiB / 45 requests for decoded fallbacks); Forbidden Ways is 3.60 MiB / 4 requests (4.12 MiB / 4). These totals include the visual manifest and selected compressed packs, not collision or application files. No texture resizing or lossy recompression was introduced.

The existing runtime supports continuous player push volumes and mapped no-jump effects. It does not implement arbitrary Source entity I/O. Summer's jetski push class filters and the waterpark's class-filtered `AddOutput basevelocity` shortcuts remain outside independently verified entity parity; the latter are not traversed by the accepted complete route. Moving scene machinery, Source soundscapes/music, private timer plugins and timed bonus courses are also outside this import's scope. Main-course completion must not be described as verification of those unimplemented effects.

## Release boundary

The final local validation passed the production build, all 496 application tests, all 21 importer tests and the room-worker type check. Both complete command routes also passed in the production browser build with exact final-state and checkpoint agreement, actual keyboard and Pointer Lock input, restart, practice starts, responsive menus and persisted map selection. `fixtures/browser-new-map-results-summer-forbidden-local.json` records both completed routes and no browser errors. These checks used local services; they do not establish deployed service behavior.

At this release, the catalog contained 17 maps. `fixtures/summer-forbidden-prior-online-catalog.json` pins all 15 prior entries, and the new regression test requires every prior board, geometry, configuration to remain identical (later runtime changes require a new compatibility certificate). The release completed on 8 October 2026. Two new Tier-2 boards were registered with 200 completion / 800 performance weights after a rollback rehearsal; the existing 18 database rows remained unchanged. The Vercel website and Cloudflare rooms received the same 17-map catalog. Both live browser routes passed, and brief guest connections confirmed all map and board IDs in EU and NA after propagation. The detailed release, rollback IDs and timings are in `fixtures/production-summer-forbidden-release-2026-10-08.json`. No existing records were modified.

## Reset investigation and finish visibility (local repair, 8 October 2026)

The reported multiplayer symptoms were reproduced with ordinary controls. This repair is local and has not been released. The live game remains at the 17-map Summer/Forbidden Ways release; the local 20-map build also contains Tendies, Derpis and Prelude.

The principal cause was shared movement, not remote-player collision or repeated R-key events. A player exactly touching a brush plane could receive an `allSolid` trace. Movement marked the tick `blocked`, and `GameSession.step` treated that flag as a full-map failure. Its `restart()` clears stage progress, timer and command history. On Summer, an authored failure portal could correctly return to the current stage before a subsequent boundary contact triggered that second, incorrect restart. One preserved stage-11 case returns at tick 411 and resets to stage 1 at tick 431. On Demise, holding forward from spawn repeatedly reset at tick 41 when the feet reached exactly Z=14976. The pre-fix 32-case Demise probe recorded 288 collision-triggered resets; the repaired probe recorded none. Ordinary authored failure triggers still work.

The repair in `src/physics/movement.ts` only activates on that all-solid movement path. A candidate must begin within 1/32 unit of a hull-expanded brush plane. Recovery searches at most 1/16 unit per axis and accepts only a completely clear full-player hull; it adds no velocity and continues the normal collision sweep. Deep embedding and too-narrow gaps remain blocked. The stair comparison now retains the blocked flag belonging to the selected path, so a rejected speculative path cannot poison a valid one. No map collision planes, stage destinations or failure volumes were moved.

A second bug existed in `synchronizeRoomMap`: welcome/state messages queued during a download could apply the same room epoch twice and restart the freshly loaded session. The queue now checks the applied epoch again after waiting. The browser regression reproduces one extra restart with that guard removed and zero with it installed. Same-epoch reconnection preserves progress; held R issues one deliberate restart. Tests use isolated room/API transports and never connect test players to live rooms.

Summer's final pool uses `teleport-26` to enter a separate black timer room. Its timer brush is 384 units wide, but the physical room is only 96 units wide; the generic floor outline was behind its walls and the central sign was overhead. The renderer now marks the actual pool entrance, outlines the water-level landing area and places compact signs inside the physical arrival room. These are visual markers only: the original timer/teleport hulls and finish tick are unchanged. Camera-only before/after renders are under `test-results/reset-investigation/finish-before` and `finish-after`.

Validation and reproducible evidence:

- `fixtures/reset-investigation/baseline.json`, `catalog.json` and `certificate.json` freeze the exact pre-fix local simulation and identity chain.
- `summer-walk-baseline.json` contains 10 boundary-reset cases; `summer-return-baseline.json` contains 14 longer cases, including failure-portal arrival followed by a second reset. The regression suite exercises the latter through and beyond the former failure tick.
- `scripts/audit-respawn-paths.ts` exercises 1,416 cases across every authored stage/canonical spawn in all 20 maps: 1,618,133 ticks and 2,612 portal returns, with zero collision-triggered full-map resets. This is broad finite coverage, not proof that every possible route is fault-free.
- `scripts/browser-reset-recovery-qa.ts` verifies delayed room loading, repeated same-epoch messages, real held-R and forward input on Demise, a socket reconnect, and an actual Summer stage-11 failure/return. `browser-fixed.json` records completion with no browser errors.
- `scripts/certify-reset-recovery.ts --check` reproduces 26 complete route comparisons and 101,272 identical ticks across all 20 maps. State, contacts, movement segments, events, timer and splits agree exactly; all canonical runs pass the server verifier.
- The production build, TypeScript check, 519 application tests and 21 importer tests passed. Built-browser route results are recorded separately in `fixtures/browser-new-map-results-reset-recovery-built.json`.

The current certificate deliberately retains the 17 live board IDs as a repair to previously aborted attempts. This is **not** a claim that the change is a no-op: previously aborted inputs can now continue, expanding the set of finishable trajectories. In the old runtime, every relevant blocked movement tick unconditionally restarted and discarded its command history; previously accepted complete attempts therefore did not traverse these recovery branches. The certificate records this review and the finite witness evidence explicitly. The published physics hash is `7b22d5508bc7f98703eb31a17eb0450a6da54885909c6d87e3c86658cefb6bdc`. Client and verifier must be released together. No existing records were deleted, rewritten or migrated.
Tendies: import and validation

Download this document

# Tendies — original CSS import

`surf_tendies` by **granis** is a Tier-1 linear course with four checkpoints. This is a local integration; the completed Summer / Forbidden Ways release remains the live 17-map catalog until a separate coordinated release includes Tendies.

The pinned CSS BSP is `db28a17bcbba187046b909398abb1bfc1bfa5c3e`, 34,355,249 bytes, from the [KSF mirror](https://main.fastdl.me/h2/db28a17bcbba187046b909398abb1bfc1bfa5c3e/surf_tendies.bsp.bz2). The [author's CSS upload](https://gamebanana.com/mods/123116) confirms Tier 1, a linear main course, three bonuses and max velocity 5000. Its metadata and original credits are captured in `fixtures/tendies-gamebanana-source.json`. Xbmann and Dream contributed bonuses; asset contributors include fingerprince, DomiTibingen, Karatekaefer, SkyppySDK and TopHATTwaffle. The upload lists CC BY-NC-ND 4.0 and a permission checklist. This conversion does not establish additional redistribution rights.

The import keeps 2,270 collision brushes, 12,960 full-resolution displacement triangles and 46 solid props. Original main start, four checkpoint and finish convexes are used. Main failure portals return to the canonical start, including the map's intermediate filter-routing room. The hidden routing room and three bonus modes are not playable destinations. The 5000 max-velocity rule is map-specific; shared movement settings are unchanged.

The five reviewed player-output triggers implement the original filtered launch and two vertical boosts. See [player-output behavior and limits](MAP-PLAYER-OUTPUTS.md). No new boost was invented to compensate for a geometry problem. Other optional Source entity behavior remains outside the supported main course.

The independent reference is .x's public [CSS66t forward main recording](https://ksf.surf/replays/surf_tendies/replay_css_4074_0_712551_1752625913.rec?game=66t), SHA-256 `f7401a2e5152e0185433d95a03a6f964fa3275c4aff5a86cea5f948271e5e689`, 134,364 bytes. The source, viewer category and frame layout are pinned in `fixtures/easy-three-source-metadata.json`. Independent next-step comparisons pass 2,925 of 2,933 frames at 0.002-unit position/velocity tolerances. All eight discrepancies remain in the report; inferred commands are not used to inflate that result.

The complete local witness uses 3,310 ordinary commands from the canonical spawn, including walking to an observed stationary deck pose, and finishes in **41.185245 seconds** through all four checkpoints. It passes the server verifier and exact state/contact/timer comparisons at 30, 60, 144 and 240 FPS. It is an automated possibility witness, not a human record or reproduction of the native world record. V2 analog input inference, an explicit duck transition and bounded yaw changes are offline planning only. Positions, velocities, collision surfaces and timer state are not injected during playback.

Original visuals include 10,666 faces, 571 displacement surfaces, 173 static props, 36 dynamic props in their initial pose and a baked lightmap. The import reports no missing-asset warnings. All 251 decoded images and all 176 native compressed textures were checked against their source pixels/blocks. Ten unedited rendered course views are captured in `fixtures/easy-three-visuals/tendies-browser-views.json`. Static initial poses, supported material shaders and baked lighting do not establish exact native rendering parity or full animation/particle support.

Reproduction uses the pinned map under `node_modules/.map-imports/`, then:

```sh
python scripts/import_new_maps.py tendies
python scripts/import_boreas_visuals.py node_modules/.map-imports/surf_tendies.bsp --slug tendies --map-id surf_tendies --author granis --game-dir "C:/Program Files (x86)/Steam/steamapps/common/Counter-Strike Source" --texture-format lossless-webp --compressed-textures
node --import tsx scripts/decode-easy-three-replays.ts
node --import tsx scripts/validate-classic-five-reference.ts tendies
node --import tsx scripts/plan-classic-five-routes.ts tendies --canonical
node --import tsx scripts/certify-reset-recovery.ts --check
node --import tsx scripts/validate-new-maps.ts tendies
```

The prior 17 maps retain their geometry, configuration and board identities. The initial opt-in output comparison is recorded in `fixtures/easy-three-compatibility-results.json`. A subsequent shared collision-reset repair is documented in `SUMMER-FORBIDDEN.md`, with current comparison evidence in `fixtures/reset-investigation/compatibility-results.json`. The published physics hash always describes the actual runtime. Live room catalog, board registration, matching client/verifier physics and website promotion must be coordinated when releasing the next map batch.

Final Tendies checks: 505 application tests (502 full-suite checks plus three new Tendies course checks), 21 importer tests, TypeScript and production build passed. The real built-game browser completed all 3,310 commands with exact final state and splits, no browser errors, keyboard/Pointer Lock, restart, responsive menu and persisted selection. Evidence: `fixtures/browser-new-map-results-tendies-local.json`. Both native and decoded texture paths passed; observed browser playback was about 360 FPS on this machine, not a universal performance guarantee.

The final three-map package was rechecked after adding Derpis and Prelude: all 513 application tests and 21 importer tests passed, along with TypeScript, production build and all three full browser routes. The current built-game evidence is `fixtures/browser-new-map-results-easy-three-local.json`; the earlier Tendies-only result remains historical. The 17 released maps still match their frozen trajectories for all 89,846 certified ticks.
Derpis and Prelude: import and validation

Download this document

# Derpis and Prelude

Integrated locally for the next release, alongside Tendies. Summer and Forbidden Ways are already live in both multiplayer regions. These two additions are not yet registered or advertised by the production rooms.

## Pinned sources

| Map | Author | Course | Original CSS BSP SHA-1 |
| --- | --- | --- | --- |
| `surf_derpis_ksf` | felix | Tier 1, six stages | `9732ac6e2fd3659e45c135083df713b7158a36af` |
| `surf_prelude_fix` | nappa | Tier 1, linear, three checkpoints | `23c97af4b0bf7167210b211e182e89c4ae37116a` |

Source URLs and expected byte lengths are in `scripts/fixtures/new-maps-sources.json`. The CSS66t forward/main-course references and author/course metadata are captured from [Derpis](https://ksf.surf/maps/surf_derpis_ksf?game=66t&mode=fw) and [Prelude](https://ksf.surf/maps/surf_prelude_fix?game=66t&mode=fw). The Prelude revision is the CSS `fix`, not the similarly named CSGO conversion. Hash-pinned recordings, captured pages and independent decoded samples are retained in `fixtures/easy-three-source-metadata.json` and the per-map telemetry files. Original authors retain their rights; conversion does not assert additional redistribution permission.

## Geometry and gameplay

Derpis imports 3,902 collision brushes and the original six stage starts, five checkpoint arrival convexes and finish convex. Original intermediate rooms and momentum are retained for the early stage transitions. Two independent 2024 KSF recordings show the stage-five/six reset handoff; an older 2020 recording differs. The two later handoffs therefore use the existing, explicit KSF stage policy. That rule is separate from authored Source entity behavior. Evidence: `fixtures/derpis-stage-transition-evidence.json`.

The complete Derpis witness starts at the authored spawn. Nineteen ordinary idle commands reach the first recorded falling pose. It then completes 5,024 commands in **72.411336 seconds**, through all five checkpoints, without a fall or stage retry. Input planning includes eight inferred v2 analog inputs, two small yaw-adjustment windows, and ordinary delays/crouch inputs through the stage transitions. The stage-five adjustment avoids a thin reset brush crossed by the runtime sweep between two native end poses. The brush remains unchanged. No position, velocity, collision or timer state is injected. This is an automated possibility witness, not a human run or an exact reproduction of the native record.

Prelude imports 2,765 collision brushes, including 33 solid props. Its five original timer models (60–64) provide the start, three checkpoints and finish; the unnamed models are selected explicitly. Original active spawn-return volumes are retained. The two legacy jail triggers with no player-touch flag remain metadata, not executable reset zones. The authored spawn is rounded to Source float32; one ordinary falling tick reaches the reference's first pose. Two v2 analog inputs are inferred for the complete witness, which needs no yaw optimization. It completes **3,092 commands / 42.793383 seconds** through all three checkpoints. Its continuous trajectory stays within approximately 0.012 position units of the reference, while timing uses the imported hulls rather than private KSF zone data.

Both routes pass the production server verifier and exact state, contact, event and timer comparisons under 30, 60, 144 and 240 FPS schedules. All seven authored practice starts settle on legal ground and remain explicitly unranked. The independent isolated native comparisons deliberately exclude inferred witness commands: Derpis passes 4,947/4,986 comparisons, with 39 retained discrepancies; Prelude passes 3,088/3,091, with three retained discrepancies, at 0.002 position/velocity tolerances. These results do not establish universal CSS parity. See the native-reference and command-validation fixtures for the full differences.

## Original artwork and limits

Derpis retains 12,936 faces, 95 source images and baked lighting, with no missing-asset warnings. All 95 images and ten native compressed textures are checked against the source pixels/blocks. Twelve unedited browser views cover all six stages.

Prelude retains 10,984 faces, 42 static props, one dynamic prop in its initial pose, 82 images and baked lighting. Its source omits `models/props_lights/lights.vmt`, used by a decorative museum-light model, and that material is also absent from the installed CSS resource archives. The importer retains the model with its explicit untextured fallback and records the warning. No replacement texture or claim of complete native visual parity is made. Eight unedited browser views cover the course. Texture integrity and source-feature audits are in `fixtures/easy-three-visuals/`.

All 82 Prelude images and 27 native compressed textures match their source pixels/blocks. Both native and decoded texture paths pass browser rendering checks for all three additions. The final production-format build passed 513 application tests, 21 importer tests and TypeScript. Its real browser completed all three routes with exact final state and splits, no browser errors, working keyboard/Pointer Lock, restart, responsive menus, all six Derpis practice destinations and persisted map selection. Evidence: `fixtures/browser-new-map-results-easy-three-local.json`. Observed median playback was approximately 360 FPS on this machine; this is not a universal performance claim.

Supported shaders, first frames of animated textures, static prop poses and baked lightmaps do not reproduce all Source rendering or entity I/O. Bonus/secret rooms, legacy jail/team logic, delayed outputs and decorative interactions are not fully emulated. Main-course traversal and timing are the reviewed scope.

## Reproduction and release boundary

Use `scripts/import_new_maps.py`, `scripts/import_boreas_visuals.py`, and `scripts/easy_three_profiles.py` with the pinned BSPs and read-only CSS assets. `scripts/decode-easy-three-replays.ts` checks the retained original recording hashes. `plan-classic-five-routes.ts <slug> --canonical`, `validate-new-maps.ts`, and `tests/derpis-prelude.test.ts` check complete routes and legal starts. `browser-summer-forbidden-qa.ts` accepts both new slugs for the built-game checks.

The existing 17 released maps retain their geometry, configuration and board identities under the reviewed certificate in `fixtures/legacy-physics-compatibility.json`. These two imports required no further runtime physics change beyond the opt-in Tendies output support. A subsequent shared collision-reset repair is documented in `SUMMER-FORBIDDEN.md`; use `scripts/certify-reset-recovery.ts --check` for the current certificate. The earlier easy-three certificate remains historical evidence. A production release must coordinate board registration, the multiplayer room catalog, matching client/verifier physics and website promotion, then verify the served content and both live room catalogs.
Authored player trigger outputs

Download this document

# Reviewed player trigger outputs

Tendies adds opt-in support for immediate `!activator,AddOutput` changes to `targetname` and `basevelocity`, on `OnStartTouch` and `OnEndTouch`. The importer explicitly admits five original main-course triggers: models 1, 2, 5, 6 and 154. Their compiled convexes, values and filter definitions are retained. Bonus triggers and other entity I/O remain metadata.

The implementation in `src/game/map-push.ts` distinguishes geometric contacts from contacts accepted by the entry filter. Changing the player's name on entry does not cancel that accepted contact's exit output. Output assignments are applied after movement; pending base velocity is consumed before the following movement step using the existing Source-style base-velocity conversion. Values replace base velocity rather than continually accelerating the player. Restart clears this state; practice save/restore retains it.

Reference semantics were checked against Valve's [trigger implementation](https://github.com/ValveSoftware/source-sdk-2013/blob/b8cfb12c0e083a2ef5b2f9f9b50f3902fa034474/src/game/server/triggers.cpp) and [player command processing](https://github.com/ValveSoftware/source-sdk-2013/blob/b8cfb12c0e083a2ef5b2f9f9b50f3902fa034474/src/game/server/player_command.cpp). No implementation was copied.

The independent CSS66t Tendies record demonstrates the main launch at frame 204. The original 1337 X base-velocity assignment reproduces the next position and velocity within the unchanged 0.002-unit tolerances. `tests/map-player-outputs.test.ts` checks that sample, the filtered one-time launch, start rearming, vertical output, practice restore and restart. The full canonical route separately passes the server verifier.

This is a bounded implementation, not a general Source entity engine. Contact checks use completed movement poses. Fully skipped thin volumes, delayed outputs, arbitrary output ordering between overlapping triggers, arbitrary filter classes and bonus modes are not certified. The tested main launch is not skipped by the supported player hull and route. No exact private KSF plugin parity is asserted. The public v2 recording holds the duck bit across origin changes that require separate normal duck/unduck commands in the local witness.

All 17 maps deployed before this addition omit `playerOutputZones`, so the helper returns before touching state. `scripts/certify-easy-three-compatibility.ts` compares all 17 canonical routes plus six historical witnesses against the frozen deployed source, including state, contacts, timer and events on every tick. The certificate retains the true current physics hash while preserving prior board identities through hash-pinned evidence. This finite comparison and source review is not a proof for every possible input.
Nyx and Reprise: import and validation

Download this document

# Nyx and Reprise: original CSS imports

Local review build, 5 October 2026. These are converted original CSS maps, not
reconstructed courses or CS2 ports. Exact native parity is not claimed.

| Map | Credit | KSF difficulty | Main route | Velocity limit |
| --- | --- | --- | --- | --- |
| surf_nyx | Syncronyze; Shadow Sheep | Tier 1 | Linear, 2 checkpoints | 4000 per component |
| surf_reprise | TeMP | Tier 2 | Linear, 3 checkpoints | 4000 per component |

Primary [Nyx](https://ksf.surf/maps/surf_nyx?game=66t&mode=fw) and
[Reprise](https://ksf.surf/maps/surf_reprise?game=66t&mode=fw) pages, viewer metadata,
native recordings and BSP hashes are pinned in `NYX-REPRISE-REFERENCE.md` and
`scripts/fixtures/new-maps-sources.json`. Reprise is labelled according to KSF's
tier 2 classification; another server's tier 1 label was not substituted.

## Course behavior

- Both maps retain the existing float32 15 ms movement profile, with their
  independently evidenced 4000 component limits. Surf acceleration and collision
  geometry were not adjusted to make command routes work.
- Nyx retains the actual VPhysics curved ramps, full-resolution displacement
  terrain, start/end trigger brushes and main-route fail volumes. Its authored
  no-jump region suppresses jumping and scales movement time by .9999, using the
  existing map-effect implementation.
- Nyx has no authored main checkpoint triggers. Its two local gates span real
  course corridors and are placed from independent KSF bookmarks. Their exact
  private KSF boundaries remain unknown. Bonus-climb checkpoints are not mistaken
  for main-course checkpoints.
- Reprise retains all three authored checkpoint brushes, its start and finish,
  and both landmark teleports. A landmark translates the player's offset rather
  than snapping everyone to one point. It retains view angles and world velocity.
  Both actual native portal transitions pass the predeclared .002 unit/.002 u/s
  comparison, with exact positions and view angles. Timer sweeps never include
  the line connecting separate rooms.
- Bonuses, bonus timer boards, interactive secret sequences and combat mechanics
  are outside these main-route imports. Authored static geometry remains present.

## Nyx spawn policy and precision limit

The authored `map_start` position is `(14120,-8248,2216)`. Its deck is at Z2072,
and the native recording begins grounded at Z2072.03125. The local canonical
spawn uses the authored X/Y and that grounded deck height, with authored yaw270.
It begins stationary inside the original start zone, without a launch or boost.

An idle drop from the authored airborne height exposed an existing numerical
edge case: the double-precision sweep endpoint was just above the floor, then
stored float32 position rounded onto it; the next tick reported all-solid.
Settling the spawn on its actual deck makes spawning/restarts reliable. It does
not resolve that general trace-rounding limitation. Shared collision precision
was not silently changed, nor were old leaderboard records reinterpreted.

## Artwork, performance and provenance

Original materials, UVs, props, ramps, lightmaps, opaque tool surfaces and world
sky depth are preserved through the importer. Menu previews are real rendered
views. Original GPU-compressed textures are used where supported, with decoded
fallbacks and bounded cached asset packs. Only the selected map is loaded.

Reprise contains zero texture dimensions in some BSP metadata; normalization now
uses dimensions read from its actual VTF files. Nyx has two non-solid decorative
props with invalid skin indices: an explicit, documented skin-0 fallback retains
their meshes. Native equivalence of that fallback is not established. See
`NYX-REPRISE-VISUALS.md` for source checks, assets, unsupported material effects,
performance evidence and screenshots.

Nyx's author submission lists CC BY-NC-ND 4.0. Imported maps and assets retain
their authors' rights; local conversion is not a claim of permission to publicly
redistribute adapted assets. No deployment was performed for this request.

## Validation and remaining differences

Final local validation passed:

- Production build, TypeScript checks, 359 automated JavaScript/TypeScript tests
  and 10 Python visual-import regression tests.
- Complete canonical-spawn routes using ordinary tick commands: Nyx 3480 commands,
  40.548546 seconds and both checkpoints; Reprise 3877 commands, 55.555266 seconds
  and all three checkpoints. Both are accepted by server replay verification.
- Every route produces identical tick states and timing at 30, 60, 144 and
  240 FPS render schedules.
- Actual production-browser playback matches the headless final states, times
  and checkpoint splits exactly. Pointer Lock, keyboard movement, restart,
  finish screens and the nine-map menu passed; no browser errors were reported.
  Menu layouts were checked at 1440, 1280 and 390 pixels. Cloud transport was
  disabled during these checks, so no test times were posted to live boards.
- Both GPU-compressed and decoded texture paths passed source-asset checks and
  animated rendering tests. Repeated map switching returned to the same GPU
  resource counts. See the visual report for measured downloads and performance;
  local frame rates are not a guarantee for other hardware.
- An independent final review found no blocker in map profiles, landmark
  handling, ranked verification, source identities or documented fidelity gaps.

Machine-readable browser results are in
`fixtures/browser-new-map-results-nyx-reprise.json`; command and render-schedule
results are in `fixtures/new-map-command-validation-nyx-reprise.json`.

Reproduction commands:

```sh
python scripts/download_new_maps.py
python scripts/import_new_maps.py nyx --game-dir "PATH/Counter-Strike Source"
python scripts/import_new_maps.py reprise --game-dir "PATH/Counter-Strike Source"
npx tsx scripts/validate-nyx-reprise-reference.ts
npx tsx scripts/validate-new-maps.ts nyx reprise
npm test
npm run build
npx tsx scripts/browser-new-map-qa.ts nyx reprise
```

The independent native report distinguishes command-only playability from exact
native trajectory comparison. Known differences include some ramp-contact
precision and Nyx air-uncrouch command cropping; tolerances were not widened to
hide them. Complete-route commands use ordinary inputs, not recorded position
or velocity injection. Native map/server timer boundaries can differ from the
local imported timer geometry.

The optional landmark extension has been reviewed against the seven existing
maps. Six original-map baseline routes and the prior four new-map route hashes
remain unchanged. See `fixtures/nyx-reprise-prior-map-compatibility.json` and the
explicit legacy board certificate. Live board IDs for the original three maps
are retained; new map identities use the actual current physics source hash.
Nyx and Reprise: independent CSS evidence

Download this document

# Independent CSS references for Nyx and Reprise

Captured 5 October 2026 for the local imports. Both primary KSF pages identify linear main courses in Counter-Strike: Source, forward style, 66t. The selected viewers identify `game: css`, `zoneId: 0`, `finishType: 0`, and `tickRate: 66.66666666666667`. These are external recordings, not trajectories generated by the browser implementation.

| Map | Author from KSF | Main course | Pinned BSP SHA-1 / bytes | Viewer component limit |
| --- | --- | --- | --- | ---: |
| [surf_nyx](https://ksf.surf/maps/surf_nyx?game=66t&mode=fw) | Syncronyze and Shadow Sheep | 2 checkpoints | `152ead154b26241dff136a4e0903543ce5d223ed` / 89,494,692 | 4000 |
| [surf_reprise](https://ksf.surf/maps/surf_reprise?game=66t&mode=fw) | TeMP | 3 checkpoints | `0e826d0d34c17122c4713d3176bf212ba90381d8` / 41,906,532 | 4000 |

Both decompressed BSP hashes were independently checked during entity review. Neither reviewed entity graph sets a different main-route gravity or air-acceleration cvar. The 4000 limit comes from KSF viewer metadata. Reprise additionally has exact 4000-unit X-velocity samples, while Nyx's record peaks below 4000 and cannot independently establish that cap from a plateau. Reprise exceeds 4000 in total speed, consistent with a component limit rather than a vector-length limit.

## Primary recordings and decoding

| Map / runner | Date UTC / rank at capture | Public file | Bytes / frames / bookmarks | SHA-256 |
| --- | --- | --- | --- | --- |
| Nyx / kusche | 2026-09-17 16:37:08 / 1 | [replay_css_3450_0_348352_1789663028.rec](https://ksf.surf/api/replays/replay_css_3450_0_348352_1789663028.rec?game=66t) | 231,116 / 3,171 / 5 | `d6e0bd38edbe7261131eb8ee14d752f1cbe8bd598614bc20d25e2d603bb8a849` |
| Reprise / Caff | 2023-08-20 19:20:28 / 1 | [replay_css_1995_0_340931_1692559228.rec](https://ksf.surf/api/replays/replay_css_1995_0_340931_1692559228.rec?game=66t) | 177,516 / 4,372 / 5 | `4e6e98c9bbb8aa00d02106c35300bf21e5d27fa6c1d49a3de9a50d907b6a597d` |

Nyx uses **version 3**, so treating it as the older fixed version-2 layout is incorrect. KSF's own [public replay reader](https://ksf.surf/gokz/js/replayviewer.js) documents a self-describing frame size and an extension block. The captured source is `fixtures/ksf-replayviewer-2026-10-05.js`, SHA-256 `be8222c64b99151cdd7d542e30a2735d3d110de08a5f60d3b0a3343c2f3c552d`; it was inspected as inert text and never executed.

Nyx's header specifies 18 cells per frame and 40 extension cells: 184 header bytes, then five 524-byte bookmarks, then 72-byte frames. Reprise uses a 16-byte version-2 header, the same bookmark layout, and 40-byte frames. The independently written decoder validates the exact total byte length, supported version/dimensions, source hashes, finite state values and bookmark bounds.

Version 3 retains the ten-cell version-2 prefix and adds flags, command number, command tick count, forward movement, side movement, mouse fields and upward movement. The public reader defines flag bit 0 as grounded and bit 1 as teleported. All Nyx frames have flags 0 or 1; none has the teleport bit. The optional decoded `command` fields retain the recorded magnitudes. For example, frame 10 has a forward button but **forward movement 200**, and frame 38 has side movement 200. Reconstructing every pressed direction as 400 would discard available evidence.

The extension is described by the public reader as a client-cvar snapshot, but that reader does not name its cells. Its raw bytes and cells are preserved under `extension`; no server settings are invented from their numeric values. Mouse fields are retained under the public reader's labels but are not used for movement reconstruction. Command/view sampling phase remains subject to behavioral validation: comparisons use current-frame buttons/magnitudes and following-frame view angles, matching the established version-2 convention.

Fixtures retain the existing `{source,header,bookmarks,frames}` interface, adding optional version-3 metadata. Reproduce offline with `node --import tsx scripts/decode-nyx-reprise-replays.ts`; `--fetch` refreshes only these two primary sources. Existing map fixtures are untouched. `fixtures/nyx-reprise-source-metadata.json` preserves full viewer settings and page/file hashes; `fixtures/nyx-reprise-telemetry-summary.json` contains exact landmark and discontinuity states.

## Spawns, timing and route landmarks

Nyx's authored `map_start` is `(14120,-8248,2216)`, yaw 270. The record starts stationary and grounded at `(14309.7138671875,-8314.7939453125,2072.03125)`, yaw −70.5547103881836, pitch 20.42475128173828. This is a standing position on the start deck, not the authored spawn placement. The final local spawn uses the authored X/Y and yaw at the native-supported deck height, `(14120,-8248,2072.03125)`, with zero velocity. The reason and the remaining idle-drop precision limitation are documented in `NYX-REPRISE.md`. Its bookmark target name is `allows3` throughout.

Reprise's authored `start` is `(-3360,0,14496)`, yaw 0. The first record frame is stationary at `(-3360,0,14368.03125)`, yaw 5.484618663787842, pitch 37.87740707397461. **Frame 160→161 contains a pre-start restart**: it jumps approximately 202 units back to the exact authored spawn with zero velocity, while recorded buttons still say forward. A scan that only flags travel over 256 units misses it. This reset is not reproducible from the movement-button stream; a canonical witness can begin at the authored spawn against frame 161 and replay the subsequent input. Its bookmark target names are empty.

| Map / event | Frame | Exact position |
| --- | ---: | --- |
| Nyx start | 134 | `(14121.140625,-8593.9052734375,2072.726318359375)` |
| Nyx CP1 | 995 | `(1179.7314453125,-2636.952880859375,-2254.513427734375)` |
| Nyx CP2 | 1959 | `(-6238.80078125,15311.07421875,-7130.69189453125)` |
| Nyx finish | 2837 | `(-9465.28125,-13305.673828125,-7928.17041015625)` |
| Reprise start | 334 | `(-3021.777587890625,-266.7193603515625,14373.46484375)` |
| Reprise CP1 | 1346 | `(-6202.65625,374.0009460449219,3705.84033203125)` |
| Reprise CP2 | 2040 | `(2837.515869140625,-149.08895874023438,-2335.220458984375)` |
| Reprise CP3 | 2789 | `(-921.6094360351562,10052.9755859375,8177.9033203125)` |
| Reprise finish | 4038 | `(14842.236328125,-11112.6083984375,-3097.495849609375)` |

Nyx's first-start-to-finish interval is 2703 frames × .015 = 40.545 seconds; KSF publishes 40.54841995239258. Reprise's interval is 3704 frames × .015 = 55.56 seconds; KSF publishes 55.560668. Internal published split times also differ slightly from bookmark arithmetic. Bookmark positions locate events, not private zone bounds or an exact sub-tick timer rule.

Nyx has authored `zone_map_start` (`*58`) and `zone_map_end` (`*55`), but no authored main CP triggers. Its `d_cp1`–`d_cp4`, `n_cp*` and `f_cp*` entities belong to a separately filtered bonus climb. Main CP boxes therefore need explicit local provenance. The reviewed CP1 corridor lies between world-wall X limits 256 and 2048; a local gate near Y −2656 crosses the native line at CP1. The CP2 corridor lies between Y 13824 and 16192, with a local gate near X −6208 crossing the later westbound line. These are geometry- and bookmark-supported local choices, not recovered KSF boxes.

Reprise has authored `start_zone` (`*102`), `end_zone` (`*103`), and `checkpoint_1`–`checkpoint_3` (`*106`–`*108`). The checkpoint slabs are approximately one unit thick, so their compiled convex members and swept hull contact must be retained. CP2 has a more complex compiled footprint; an enclosing box alone is insufficient.

## Nyx map effects

The main course has no recorded teleport discontinuity. Most map teleports are failure returns to `map_start`; bonus, filtered climb, top/end and other auxiliary destinations must not become unconditional main progression. The `allows3` target name identifies an entity state, not a new movement profile.

Two authored `nojump_zone` triggers use `player_speedmod` named `nojump`, spawnflags 4. Both issue `ModifySpeed 0.9999` on entry and restore 1 on exit:

- Main-route `*80`, Hammer ID 1244381: world bounds `(-4864,3584,-2879.989990234375)` to `(-3072,5120,-1280)`, preserving its eight-face compiled shape.
- Bonus-route `*61`, Hammer ID 1098600: `(1120,14784,-14560)` to `(3936,16320,-12960)`.

The public record crosses main volume `*80`. All **45** next-tick samples 1233–1277 match within the original 0.002-unit position and 0.002-unit/s velocity tolerances when the authored movement-time scale applies. Its effective binary32 interval is 0.014998499304056168; observed free-flight gravity differences are approximately −11.9988 rather than −12. The reference test also runs an unscaled negative control at frame 1250, which exceeds the same position tolerance. This recording supports the time scale; it does not independently demonstrate jump suppression because that segment does not press jump.

Nyx's 1900-unit push `*84` points along `(pitch45,yaw270)` and belongs to the separate first bonus. It is not a main-course assist. Its `logic_auto` drives lighting and rotating-button settings, not a main-route movement cvar.

## Reprise landmark transfers

These are ordinary authored `trigger_teleport` entities with a landmark, player flag 1 and no disabled state:

| Model / Hammer ID | Landmark origin | Destination origin | Translation |
| --- | --- | --- | --- |
| `*16` / 30737 | `mark1 = (-2752,-210.971,-8160)` | `dest1 = (6880,10925,10208)` | `(9632,11135.971,18368)` before binary32 rounding |
| `*27` / 38980 | `mark2 = (-11104,11168,-3071)` | `dest2 = (-544,-10400,6625)` | `(10560,-21568,9696)` |

The [Valve reference](https://github.com/ValveSoftware/source-sdk-2013/blob/b8cfb12c0e083a2ef5b2f9f9b50f3902fa034474/src/game/server/triggers.cpp#L2181) uses the player's offset from the landmark and leaves view angles unchanged. Here, independent CSS records also verify that behavior:

- **2229→2230:** `(-2727.923828125,762.0137329101562,-7433.17333984375)` becomes `(6844.1259765625,11899.919921875,10933.93359375)`. XY velocity remains exactly `(-3996.673828125,128.9822235107422)`, Z changes −53.5150146484375 → −65.5150146484375 through normal gravity, and pitch/yaw remain exactly `(18.809371948242188,177.72171020507812)`.
- **3482→3483:** `(-10923.642578125,9873.9404296875,-3059.682373046875)` becomes `(-323.060546875,-11669.099609375,6611.5986328125)`. Velocity follows ordinary recorded air input and gravity rather than zeroing; the view remains the runner's next input angle, not either destination's authored `(0,0,0)`.

Both imported transfers match native destination position exactly and velocity within 0.000123 units/s under the existing strict checks. This supports translation of the completed movement-tick position with Source-style binary32 subtraction/addition; it does not justify rotation of velocity or view, resetting to the destination origin, or sweeping timer triggers along the teleport gap. It remains evidence for these specific recorded transfers, not a universal proof of every overlapping-trigger scheduling case.

## Strict comparison results and limitations

Run `node --import tsx scripts/validate-nyx-reprise-reference.ts` to regenerate `fixtures/{nyx,reprise}-native-reference-validation.json`. Expected states come from the primary files. Comparisons use imported map effects, the existing start-exit rule and fixed tolerances **0.002 position / 0.002 velocity**. They are isolated next-tick tests, not complete command-only route proof.

| Map | Passing / compared ticks | First failure | Maximum position / velocity error |
| --- | ---: | ---: | --- |
| Nyx | 1811 / 2837 | 167 | 0.250944 / 16.732029 at frame 1035 |
| Reprise | 4034 / 4038 | 112 | 202.233739 / 260.115873 at pre-start reset frame 161 |

Nyx's v3 ground flags agree with categorization for every compared frame. Of its 1026 strict failures, 1024 have collision contacts; the first has about 0.03124 units/s velocity error at compiled ramp `bsp-0-251`. The two largest remaining deviations, frames 1035 and 1906, occur while uncrouching in air. Native horizontal acceleration is about 49.725 units/s, consistent with the crouched wish-speed crop still applying for that tick; the current simulator removes that crop sooner. This is a supported ordering diagnosis, not a physics change made during this research.

Reprise's failures are frame 112 (first keydown magnitude unavailable in v2), 161 (the pre-start reset), 767 (ramp contact, 0.200909 position / 1.157070 velocity error), and 1144 (ramp contact, 0.010959 velocity error). Both timed landmark transfers pass. All failures remain in the report; tolerances were not widened and frames were not silently excluded.

`tests/nyx-reprise-reference.test.ts` checks source pins, the v3 layout and measured half-commands, the pre-start reset, all native ground classifications, and all 45 no-jump samples with a negative control. These tests do not turn the remaining strict failures into a claim of exact native parity. Private plugin manifests, all client-cvar names, version-2 analog input, and some contact/duck-state details remain unavailable. Complete canonical-spawn command witnesses and browser checks are separate integration work.
Nyx and Reprise: original artwork

Download this document

# Nyx and Reprise visual import

These are conversions of the authored CSS BSPs and their packed assets. No map
art was generated or reconstructed from video. Rendering and collision use the
same pinned sources; visual imports do not change movement or collision.

| Map | Author | BSP SHA-1 | Source bytes |
| --- | --- | --- | ---: |
| surf_nyx | Syncronyze | `152ead154b26241dff136a4e0903543ce5d223ed` | 89,494,692 |
| surf_reprise | TeMP | `0e826d0d34c17122c4713d3176bf212ba90381d8` | 41,906,532 |

Nyx's [original author page](https://gamebanana.com/mods/122639) also credits
ShadowSheep for optimization, fixes and sounds. It recommends HDR and at least
medium texture detail so the grid below the ramp glass remains visible. Its
listed licence is CC BY-NC-ND 4.0; the conversion does not grant broader
redistribution or commercial rights. Reprise's author is independently credited
in this [CSS KSF recording](https://www.youtube.com/watch?v=vxN4W9RgtDE). Original
map and stock CSS assets retain their owners' rights.

## Reproduction

Use the pinned download cache and a read-only installed CSS asset directory:

```text
python scripts/import_boreas_visuals.py node_modules/.map-imports/surf_nyx.bsp --slug nyx --map-id surf_nyx --author Syncronyze --game-dir "PATH/Counter-Strike Source" --texture-format lossless-webp --compressed-textures --invalid-skin-policy default
python scripts/import_boreas_visuals.py node_modules/.map-imports/surf_reprise.bsp --slug reprise --map-id surf_reprise --author TeMP --game-dir "PATH/Counter-Strike Source" --texture-format lossless-webp --compressed-textures
python scripts/validate_new_map_visuals.py --slugs nyx,reprise --game-dir "PATH/Counter-Strike Source"
python -m unittest discover -s tests -p test_nyx_reprise_visuals.py
```

`CAPTURE_MAPS=nyx,reprise npx tsx scripts/capture-new-map-previews.ts` uses the
actual browser renderer at port 4193, configurable through `UI_BASE_URL`. Optional
`CAPTURE_FRACTIONS` selects comma-separated normalized points in native telemetry.
The helper also captures each authored start. Positions/views are visual review
cameras, not commands or proof of playable routes.

## Export and asset verification

| Measure | Nyx | Reprise |
| --- | ---: | ---: |
| World faces | 2,629 | 25,363 |
| Displacement surfaces | 1,964 | 0 |
| World material batches | 91 | 103 |
| Authored sky portal faces | 0 | 548 |
| Static prop instances | 837 | 83 |
| Dynamic initial poses | 21 | 0 |
| Model/skin variants | 48 | 6 |
| All geometry batches | 293 | 112 |
| Decoded texture images | 414 | 236 |
| Native compressed textures | 39 | 20 |
| Decoded-path asset bytes before transport gzip | 26,982,419 | 16,282,812 |

The exact compiled black/ordinary materials survive the surface-flag filter.
Reprise's original sky portal faces use the depth-only pass established by the
Kitsune regression. Nyx is an enclosed cave with no compiled sky portals.
Neither map receives artificial room/stage hiding or invented visual walls.

All 650 texture images match their source decoded VTF pixels exactly. All 59
native DDS exports preserve original DXT blocks and mipmaps byte for byte. The
405 mesh batches have finite vertices, valid buffer lengths and in-range
triangle indices. Results are retained in
`fixtures/nyx-reprise-visuals/integrity.json`. These conversion checks share
decoders with import and are not an independent native-rendering comparison.

Nyx's native texture subset contains 14,652,232 bytes of mip data versus 88,391,736
bytes of equivalent RGBA mip data, an 83.4% saving. Reprise's corresponding figures
are 10,235,056 versus 56,711,824 bytes, an 82.0% saving. These are asset-derived
payload estimates, not measured total GPU/browser memory. Other textures,
geometry, render targets and JavaScript allocations are additional.

## Resolved format issues

- Reprise has three packed materials whose compiled BSP texture dimensions are
  zero: `test/color008`, `nightfall/track_centerpiece`, and `reprise/thanks`.
  Their original VTF headers provide 128×128, 1024×1024 and 1024×1024 dimensions.
  The importer uses those values for UV normalization and records the resolution
  in `textureDimensionsFromVtf`. It preserves every valid compiled dimension and
  fails explicitly if neither source provides usable dimensions.
- Reprise packs an MDL version 45 shrub model. Its used header, mesh and VVD fields
  retain the supported layout. The reader now accepts it; a regression compares
  exported positions directly with the original VVD and confirms all 30 authored
  instances and 1,180 model triangles are present. No substitute shrub was used.
- Nyx has exactly two out-of-range authored skin families: static props 829 and 830
  request 11/11 and 3/3. Both are non-solid decorative ramp props outside the main
  reviewed route. An explicit `--invalid-skin-policy default` option retains the
  original geometry using family 0 instead of omitting the props. The authored
  index, family count, rendered index and `nativeConfirmed:false` are recorded
  in `modelSkinFallbacks`, with visible import warnings. This fallback is a
  documented approximation; native CSS material selection for these two cases
  has not been confirmed. Default importer behavior still rejects invalid skins.

Four source/analytic regressions pass: dimension preservation/failure, pinned
zero-dimension VTF recovery, v45 source vertices/instances, and explicit Nyx
fallback scope/material-family selection. The previous maps were not regenerated.

## Browser review

Twenty start/route camera views loaded in Edge Chromium with no page or console
errors. Inspected Nyx views retain the blue grid under the glass, purple crystals,
cave walls and main-route ramp visibility. Reprise retains its cream masonry,
dark ramp edging, original lights and sky boundaries. The menu previews are
actual renderer captures: Nyx at 10% of the native recording and Reprise at 60%.
The report is `fixtures/nyx-reprise-visuals/browser-views.json`.

Peak sampled submissions were 422 draws/326,050 triangles for Nyx and 100
draws/173,972 triangles for Reprise. CPU render submission was below 0.9 ms in those
still views. These submission timings are separate from the animated benchmark
below and do not constitute a device guarantee.

## Animated rendering and delivery

The packed production assets passed five eight-second renderer routes in one
browser canvas: Nyx native, Reprise native, Nyx decoded fallback, Reprise decoded
fallback, and Nyx native again. Each route animates the camera through the full
native recording; it does not run player physics or award a time. Tests asserted
the exact requested pack URLs and that only the selected texture representation
was downloaded. No browser errors occurred.

| Map / texture path | Average FPS | p95 frame interval | Longest measured frame | Local preparation |
| --- | ---: | ---: | ---: | ---: |
| Nyx / native | 359.3 | 2.9 ms | 16.6 ms | 609 ms |
| Reprise / native | 360.0 | 2.9 ms | 3.0 ms | 238 ms |
| Nyx / decoded | 360.0 | 2.9 ms | 3.0 ms | 957 ms |
| Reprise / decoded | 360.0 | 2.9 ms | 2.9 ms | 340 ms |
| Nyx / native repeat | 360.0 | 2.9 ms | 2.9 ms | 604 ms |

These local 1280×720 balanced-quality results reflect the machine and its
apparent 360 FPS cap. Preparation uses a local development server and is not an
internet download prediction. First/repeated Nyx ended with the same renderer
counts: 535 geometries, 96 textures and 16 programs. This is a disposal regression
check, not a measurement of total browser or GPU memory. The animated route peaked
at 407 draws/337,462 triangles for Nyx and 104 draws/174,140 triangles for Reprise.

| Packed visual transfer, including manifest | Native | Decoded fallback |
| --- | ---: | ---: |
| Nyx | 18,005,464 bytes / 9 requests | 19,908,725 bytes / 8 requests |
| Reprise | 9,910,328 bytes / 7 requests | 10,859,786 bytes / 6 requests |

The retained evidence is `fixtures/nyx-reprise-visuals/performance.json` and
`packed-transfer.json`. Reproduce with `scripts/new-map-render-performance.ts`
and `RENDER_CASES='[["nyx","native"],["reprise","native"],["nyx","fallback"],["reprise","fallback"],["nyx","native"]]'`.

## Remaining rendering differences

- Original diffuse LDR lightmaps are retained. Source HDR exposure/tonemapping,
  directional bumped lightmaps, complete self-illumination and SSBump/lighting
  response are not equivalent to the browser renderer. No exposure boost was
  applied to make the authored Nyx cave brighter.
- Nyx's water and refraction passes, material scrolling, animated credits,
  rotating portal effects, particles, spotlights/laser beams and map soundscapes
  are not reproduced. Its 21 dynamic portal meshes use their original initial
  poses. Main route geometry does not animate.
- Nyx's 33 entity crack decals and Reprise's one glass decal are not projected.
  Dynamic entity visibility and general map I/O remain outside visual scope.
- Nyx's two fog controllers have identical values; the selected authored purple
  fog is unambiguous. Native PVS/areaportal culling is not reproduced; actual
  geometry and the authored sky-depth pass control the inspected occlusion.
- Native visual parity is not certified. The two explicit skin fallbacks and
  the effects above remain limitations rather than hidden corrections.
Nyx and Reprise: full command routes

Download this document

# Nyx and Reprise command playability

Both complete fixtures begin at the imported map's canonical spawn and finish through ordinary forward/side/jump/duck/view commands. The trusted server verifier accepts them. No exported fixture injects a player position, velocity, contact state, checkpoint, or time. These are automated route witnesses, not human performances or claims of exact CSS/KSF parity.

| Map | Canonical commands | Timed result | Ordered checkpoint splits | Surf contact ticks |
| --- | ---: | ---: | --- | ---: |
| surf_nyx | 3,480 | 40.548546 s | 12.906594 / 27.367472 s | 1,024 |
| surf_reprise | 3,877 | 55.555266 s | 15.182544 / 25.584387 / 36.821427 s | 681 |

For each fixture, 30, 60, 144, and 240 FPS schedules produce identical player state, full movement results including contacts/segments, timer state, and events at every physics tick. Reports are `fixtures/nyx-command-validation.json` and `fixtures/reprise-command-validation.json`; their source, geometry, replay, and trajectory hashes identify the tested inputs. Fixtures are `public/replays/nyx-complete.json` and `public/replays/reprise-complete.json`.

## Independent source and route construction

Nyx uses kusche's public CSS 66t forward main-course record, 40.548419952 s, dated 2026-09-17, from [the KSF native viewer](https://ksf.surf/replays/surf_nyx/replay_css_3450_0_348352_1789663028.rec?game=66t). Its independently decoded v3 frames contain actual forward/side command magnitudes. The stationary first point is `(14309.7138671875,-8314.7939453125,2072.03125)`. A 643-command ground approach reaches that point from the unchanged imported canonical spawn, then 2,837 route commands complete the map. The approach uses normal walking and small analog taps; it does not start the timer.

Nyx's authored airborne `map_start` is `(14120,-8248,2216)`, yaw 270. The imported local canonical spawn retains X/Y/yaw and settles Z to the real deck surface `2072.03125`. This is explicitly a local spawn policy: the authored drop exposes a known difference between the double-precision collision endpoint and stored float32 position, causing an idle reset near the deck. The native first frame independently confirms the grounded deck height. No shared collision algorithm was changed to hide this limitation.

Unmodified Nyx v3 inputs first differ by more than 0.002 velocity units at frame 167 (about 0.03123 u/s), then a contact timing difference at frame 986 grows into a route failure. Two smooth, bounded yaw adjustment windows, native command indexes 850–970 and 1120–1310, make the imported route complete. The largest control knot is 0.182159 degrees. Actual forward/side magnitudes, jumps, duck inputs, and pitch remain native. Final route comparison has maximum position error 2.756160 units and maximum velocity error 16.732095 u/s; this planned witness is not a native parity result. It crosses both local CP gates and finish on the corresponding native bookmark frames 995, 1959, and 2837.

Reprise uses Caff's public CSS 66t forward main-course record, 55.560668 s, dated 2023-08-20, from [the KSF native viewer](https://ksf.surf/replays/surf_reprise/replay_css_1995_0_340931_1692559228.rec?game=66t). Native frame 161 is a pre-run restart to the exact authored canonical origin `(-3360,0,14496)` with zero velocity. The witness starts at the real canonical spawn and uses commands from that frame onward. It excludes the earlier recorded preparation rather than replaying a restart or changing the spawn.

Reprise v2 contains buttons and angles but no analog movement magnitudes. Initial reconstruction uses held movement magnitude 400, buttons from frame i, and view angles from frame i+1. Offline planning changes 23 side commands to zero to keep the complete legal trajectory near native targets through sensitive contacts. These are declared route-planning decisions, not recovered proof of the unrecorded native magnitudes. Maximum position error against aligned native samples is 3.620679 units; maximum velocity error is 72.461018 u/s. The canonical route crosses start/CP1/CP2/portal1/CP3/portal2/finish on ticks 173/1185/1879/2069/2628/3322/3877. Native frames are those tick numbers plus 161.

## Entity and collision review

The source BSP SHA-1 pins are Nyx `152ead154b26241dff136a4e0903543ce5d223ed` and Reprise `0e826d0d34c17122c4713d3176bf212ba90381d8`. `scripts/audit-nyx-reprise.py` reproduces `fixtures/nyx-reprise-entity-review.json` from those files.

- Nyx has authored start/end triggers `*58`/`*55`, and main fail teleports `*12`–`*27` target `map_start`. Its main-route no-jump trigger `*80` invokes `player_speedmod` flag 4 with movement scale 0.9999. The other no-jump trigger `*61` belongs to bonus 2. There are no authored named main CP triggers; imported CP boxes are local corridor gates informed by independent native bookmarks, not private KSF zone data.
- Nyx's 798 solid static props include the curved surf ramps. Every solid static prop has a packed PHY. Its 21 solid dynamic `portalinner2` props surround post-finish/bonus gateways. Filtered bonus checkpoint teleport logic is not enabled as main-course behavior. The lone push is in bonus 1, and no main-route cvar-changing entity was found.
- Reprise's `*16` and `*27` are main-route landmark teleports. They translate the end-of-movement position by destination minus landmark with Source float32 vector operations, preserving world velocity and view angles. Native crossings 2229→2230 and 3482→3483 independently support these semantics. Timer sweeps stop at portal touch and never cross the inter-destination gap.
- Reprise has authored start/end triggers `*102`/`*103` and thin CP triggers `*106`/`*107`/`*108`. All five authored gravity triggers set multiplier 1. Its only push belongs to bonus 2. Targetless teleport `*88` has spawnflags 0 and is inert. The BSP omits packed PHY files for three stock foliage models; the importer must retain its mounted-resource/missing-shape accounting rather than infer extra collision from visible foliage.

## Reproduction and compatibility

Run `npx tsx scripts/plan-nyx-reprise.ts nyx --canonical` and `npx tsx scripts/plan-nyx-reprise.ts reprise --join-frame 161 --canonical` to reproduce the saved command plans, canonical verification, and four schedule comparisons. `--native-only` runs unmodified input reconstruction as a diagnostic. `plan-nyx-reprise-inputs.ts` and `plan-nyx-reprise-optimize.ts` record planning changes in each map's `*-planned-commands.json`; candidate prefix clones only accelerate offline search, and each exported witness is rerun from canonical spawn.

The optional landmark branch was reviewed as inactive on the prior seven maps. `scripts/certify-legacy-physics.ts --write --reviewed-linear-noop` repeated all three original full routes under both legacy and current ranked profiles against pinned Git commit `28d009d1e42f9986c97fe22cb4151402fe4315c4`: 20,556 ticks matched byte for byte. The renewed certificate preserves their leaderboard identities while publishing actual current source hash `077f4293b446b922f602fee61592a6e5acf87f82c8bbfee5181bfddafdb5cbf7`. `scripts/validate-landmark-compatibility.ts` additionally matched the pre-change trajectory hashes for Demise 3,244, Kitsune 6,333, Aircontrol 3,312, and Lux 2,827 canonical commands. It records this evidence in `fixtures/nyx-reprise-prior-map-compatibility.json` without replacing the prior reports.

Repository TypeScript checking passed after these planner additions. Native comparison limitations, local timer boundaries, and declared command adjustments remain separate from command playability and render-schedule determinism.
Reproducible map import workflow

Download this document

# Adding a CSS surf map

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.
Independent CSS recordings

Download this document

# Independent native CSS replays for four additional courses

Retrieved 5 October 2026 from KSF's public map pages, replay viewers and binary replay endpoints. These are independent recorded player states, not expectations generated by the browser simulator. They establish reference routes and observed server behavior; they do not establish exact simulation, entity, timer, or current-server parity.

## Source selection

All sources were selected from `game=66t&mode=fw` main-map leaderboards. Each viewer identifies `game: css`, `zoneId: 0`, `finishType: 0`, the exact map name, and `tickRate: 66.66666666666667`. Thus these are main-course CSS forward-style records, not bonus or individual-stage records. Ranks and display names below are those returned when retrieved and may change.

| Map and map-author source | Course | Selected runner / rank | Published record seconds | Record date UTC |
|---|---|---|---:|---|
| [surf_demise — elly](https://ksf.surf/maps/surf_demise?game=66t&mode=fw) | Linear, 2 checkpoints | levi / 2 | 37.13803482055664 | 2026-07-28 11:48:30 |
| [surf_kitsune — Arblarg](https://ksf.surf/maps/surf_kitsune?game=66t&mode=fw) | 9 stages | .x / 1 | 91.934898 | 2024-09-22 01:37:28 |
| [surf_aircontrol_ksf — SnoopSh](https://ksf.surf/maps/surf_aircontrol_ksf?game=66t&mode=fw) | Linear, 5 checkpoints | levi / 1 | 35.488685 | 2024-01-12 23:19:08 |
| [surf_lux — SSStormy](https://ksf.surf/maps/surf_lux?game=66t&mode=fw) | Linear, 3 checkpoints | agent nex / 1 | 31.267492 | 2026-01-14 09:55:24 |

Demise's rank-one entry is `.x`, 37.114688 seconds, recorded 25 March 2026, but its public record has `file: null`. The fixture therefore uses the highest-ranked available downloadable recording, rank two. It must not be called the world-record replay.

| Fixture slug | Native download | Bytes / frames / bookmarks | SHA-256 |
|---|---|---|---|
| demise | [replay_css_5121_0_540902_1785239310.rec](https://ksf.surf/api/replays/replay_css_5121_0_540902_1785239310.rec?game=66t) | 107,916 / 2,632 / 5 | `d796ba2c14f3e56ff0f64236ab01527b243b2d79be30ce5892f262f15cb74996` |
| kitsune | [replay_css_2000_0_712551_1726969048.rec](https://ksf.surf/api/replays/replay_css_2000_0_712551_1726969048.rec?game=66t) | 276,652 / 6,667 / 19 | `274ad1f6486452755d3f96074357a68618308a52ff7a5f19964bd2f72a4a4aa0` |
| aircontrol | [replay_css_15_0_540902_1705101548.rec](https://ksf.surf/api/replays/replay_css_15_0_540902_1705101548.rec?game=66t) | 114,448 / 2,756 / 8 | `67d260c28d40c1cef166e9573011e49cf8a8571881f191c266a9d4b272b7b552` |
| lux | [replay_css_2985_0_877922_1768384524.rec](https://ksf.surf/api/replays/replay_css_2985_0_877922_1768384524.rec?game=66t) | 104,400 / 2,531 / 6 | `b224c057ba5e527e923ecd67ef08c5322b70a60d55e4b950b329339ec7ab4696` |

The matching native files are `fixtures/{slug}-native.rec`; exact decoded binary32 samples are in `fixtures/{slug}-ksf-telemetry.json`. Each JSON retains the established `{source,header,bookmarks,frames}` shape. Bookmark `targetName` and `className` strings are also retained because maps can filter entity touches using them. Do not trim these strings: Kitsune's target name is a single space.

`fixtures/new-map-source-metadata.json` captures the selected record, rank-one record, public viewer properties, page hashes, binary hash, author text and course-count text. `fixtures/new-map-telemetry-summary.json` contains all bookmark states, first states, velocity extrema, complete before/after discontinuity states and Kitsune's explicit stage transitions. Positions, velocities, angles and buttons in that summary are unrounded. No large frame array needs to be printed to inspect the landmarks.

## Decoding and checks

Run `npx tsx scripts/decode-new-map-replays.ts` to decode the pinned local binaries offline. On Node 24, `node scripts/decode-new-map-replays.ts` also works. `--fetch` redownloads the pinned replay filenames and captures fresh metadata from their public pages; a changed leaderboard that no longer lists a pinned record causes source capture to fail visibly rather than silently selecting another run. The initial capture used ordinary HTTP downloads and parsed inert JSON from the public server-rendered pages.

The independently written decoder verifies the captured SHA-256 and byte length, version 2, total layout size, bookmark frame bounds, finite coordinates/angles/velocities, and ordered main start/finish bookmarks. The layout is a 16-byte header, 524-byte bookmarks, and 40-byte frames. Bookmark fields are frame, type, stage, 256-byte target name and 256-byte class name. Types 1, 2 and 3 are continue, stop and start respectively. [Public ReplayViewer structure definitions](https://github.com/crashfort/ReplayViewer/blob/0341fea8ba85fbb7c3d352b4b5ddf704a659fd4d/src/rv_priv.inc).

## First recorded state

Coordinates use Source X/Y horizontal and Z up, with player feet origins. Values in this document are rounded to six decimals; fixture values remain exact. A recording's first state is not necessarily a stationary spawn.

| Slug | Frame-0 position | Frame-0 velocity | Pitch / yaw | Buttons | Entity target name |
|---|---|---|---|---:|---|
| demise | (-11184.183594, -3911.715576, 15038.109375) | (-100.788055, -290.968018, 73.993378) | 70.397705 / -103.514610 | 1030 | `allows3` |
| kitsune | (-15360, -15024, 959.820007) | (0, 0, -18) | 16.528782 / 94.515366 | 0 | one space |
| aircontrol | (-2440.565186, -9504.291992, 14479.769531) | (-37.677769, -19.491684, -288) | 0 / 90 | 16 | `main` |
| lux | (-58.811485, 2117.576416, 1600.031250) | (0, 0, 0) | 35.420570 / -70.239693 | 0 | empty |

All four record `className: player` and keep their listed target name at every bookmark. In particular, Demise starts already moving with `allows3`; this replay alone cannot establish how a newly spawned player acquires that name, or prove that intermediate target-name changes never occur between bookmarks.

## Timing and linear-course landmarks

The viewer rate corroborates nominal 15 ms sampling. The native file has frame indices, not per-frame wall-clock timestamps. Published record times differ slightly from bookmark interval counts, so bookmark arithmetic does not establish KSF's exact timer boundary or sub-tick semantics.

| Slug | First start frame | Finish frame | Intervals | Intervals × .015 s | Published minus this value |
|---|---:|---:|---:|---:|---:|
| demise | 33 | 2509 | 2476 | 37.140 | -0.001965179443 s |
| kitsune | 204 | 6333 | 6129 | 91.935 | -0.000102 s |
| aircontrol | 168 | 2534 | 2366 | 35.490 | -0.001315 s |
| lux | 113 | 2197 | 2084 | 31.260 | +0.007492 s |

| Slug | Event / bookmark stage | Frame | Position |
|---|---|---:|---|
| demise | Start / 1 | 33 | (-11182.163086, -4039.968506, 14976.724609) |
| demise | CP1 / 2 | 783 | (11467.323242, -4153.684082, 7636.634277) |
| demise | CP2 / 3 | 1680 | (4055.793945, 1500.589478, 4694.838379) |
| demise | Finish / 99 | 2509 | (-11230.682617, 7533.611816, -3694.643311) |
| aircontrol | Start / 1 | 168 | (-2357.851318, -9229.839844, 14336.724609) |
| aircontrol | CP1 / 2 | 619 | (-2901.667969, -669.681946, 12200.659180) |
| aircontrol | CP2 / 3 | 1075 | (-12393.955078, 6989.348145, 8617.500977) |
| aircontrol | CP3 / 4 | 1545 | (4534.850586, 1088.244019, 3070.059082) |
| aircontrol | CP4 / 5 | 1903 | (-9172.749023, -8434.259766, -1912.635254) |
| aircontrol | CP5 / 6 | 2197 | (-9135.171875, 10744.531250, -5538.016602) |
| aircontrol | Finish / 99 | 2534 | (11557.597656, 3388.930176, -13396.194336) |
| lux | Start / 1 | 113 | (-220.241180, 1900.088867, 1600.726196) |
| lux | CP1 / 2 | 683 | (-365.668976, -2489.216309, -2555.614014) |
| lux | CP2 / 3 | 1152 | (49.865139, -5565.773926, -5610.058594) |
| lux | CP3 / 4 | 1803 | (7147.271484, -5448.883789, -1877.989136) |
| lux | Finish / 99 | 2197 | (6914.706543, 14724.418945, -8608.441406) |

These positions locate recorded timing events. They are not bounds for server-side zones. The summary also preserves the viewer's published main-course cumulative split times. The viewer's separate `stagePrs` entries are individual-stage personal bests, and must not be substituted for this full-run's stage times.

## Kitsune stage transitions

Kitsune has start/stop bookmarks for each of nine stages and a final stage-99 stop. The full-course start is frame 204 at (-15155.500977, -14797.662109, 821.466125); the finish is frame 6333 at (-15541.924805, 9942.676758, -11881.974609). Its stage-one entry is frame 0, but the record begins after the exact spawn placement, so no exact stage-one teleport destination is claimed.

For stages 2–9 the recording first moves to `(0,0,1000 × previous stage)` for one frame, preserving horizontal velocity and applying one normal -12 vertical-velocity step. The next frame places the player at the exact stage spawn listed below, with velocity `(0,0,-6)`, pitch 0, roll 0 and the listed yaw. The entry bookmark is one frame after that placement, when gravity has reached -18. These are consecutive recorded samples, each nominally 15 ms apart; they do not prove how the server implements the intermediate travel or whether the replay exporter applies transformations.

| Stage | Intermediate frame / Z | Exact recorded spawn frame / origin | Spawn yaw | Entry / exit bookmarks | Entry-to-exit frames / seconds |
|---|---|---|---:|---|---|
| 1 | Not recorded | First state 0: (-15360, -15024, 959.820007) | First yaw 94.515366 | 0 / 204 | 204 / 3.060 (pre-roll) |
| 2 | 456 / 1000 | 457: (-13312, -15072, -320) | 90 | 458 / 572 | 114 / 1.710 |
| 3 | 796 / 2000 | 797: (-11264, -15072, -1600) | 90 | 798 / 910 | 112 / 1.680 |
| 4 | 1230 / 3000 | 1231: (-8192, -15072, -2848) | 90 | 1232 / 1335 | 103 / 1.545 |
| 5 | 1696 / 4000 | 1697: (-5120, -15072, -5312) | 90 | 1698 / 1802 | 104 / 1.560 |
| 6 | 2157 / 5000 | 2158: (-2048, -15072, -7776) | 90 | 2159 / 2267 | 108 / 1.620 |
| 7 | 2679 / 6000 | 2680: (512, -14784, -12032) | 90 | 2681 / 2783 | 102 / 1.530 |
| 8 | 3439 / 7000 | 3440: (8192, -512, 6624) | 270 | 3441 / 3539 | 98 / 1.470 |
| 9 | 4097 / 8000 | 4098: (-15104, 14928, 10880) | 270 | 4099 / 4201 | 102 / 1.530 |

The 91.935-second first-start-to-finish count includes these intermediate samples and later stage preparation. The first three public cumulative splits, 3.809884, 8.910013 and 15.419838 seconds, correspond closely to frames 458, 798 and 1232 measured from frame 204. There is no evidence in these data for subtracting all later stage preparation time from the main-course total.

The pinned original CSS BSP (`41ff082a485d5b4a306589205187bc730ca4b2ac`) contains no entity origins at these eight `(0,0,Z)` intermediate points. Its authored color destinations also differ from these recorded spawns; for example `orange` is (-13312,-15216,-455), whereas the recorded stage-two spawn is (-13312,-15072,-320). This is evidence of a server or replay-layer difference, not proof of a specific installed plugin. A bounded public-source search did not identify KSF's authoritative per-map plugin/Stripper configuration. [wrldspawn's public surf-zones project](https://github.com/wrldspawn/surf-zones) explicitly describes itself as an approximation of KSF; its [Kitsune zone file](https://raw.githubusercontent.com/wrldspawn/surf-zones/main/z/surf_kitsune.json) is useful supporting context but is not substituted for native evidence here.

## Other large position discontinuities

The summary flags per-frame travel over 256 Source units. This diagnostic is deliberately separate from entity classification; smaller teleports and hull changes can fall below that threshold, and replay files do not contain entity-touch events.

- **Demise:** no frame-to-frame displacement exceeds 256 units. This does not prove that the map has no entity effects.
- **Lux, 1609 → 1610:** position changes from (296.840851,15139.075195,-9397.790039) to exactly (7168,-15168,-1856), a 31,978.277501-unit jump. Velocity changes from (265.827759,3445.113281,4.342194) to (319.924957,3440.556396,-7.657806), retaining substantial momentum instead of zeroing it. The -12 Z change is consistent with a normal gravity step. Both full angle states are in the summary; this evidence does not on its own identify which map entity or fix performs the teleport.
- **Aircontrol, 2729 → 2730:** after the frame-2534 finish, position jumps from the stopped end room to (-2078.310059,-9511.769531,14759.400391), velocity remaining zero. At 2754 → 2755 it jumps again to (-2440,-9504,14484) while Z velocity changes -288 → -300. Both are post-finish tail events, so neither should be counted as a failure during the completed course.

## Velocity components and cap evidence

The following extrema cover the full saved recordings, including pre-roll and tails. All listed peak frames fall before or at the main finish. Speeds are Source units per second. A component limit is not a horizontal-vector or total-vector speed limit.

| Slug | Max absolute X (frame) | Max absolute Y (frame) | Max absolute Z (frame) | Peak horizontal / total speed |
|---|---:|---:|---:|---:|
| demise | 4968.539063 (2508) | 4635.968262 (2106) | 1953.900635 (2368) | 4968.544361 / 5113.407677 |
| kitsune | 3500 (5498) | 3268.602783 (5743) | 3500 (5434) | 3659.230992 / 4544.127011 |
| aircontrol | 5376.755371 (2294) | 5728.947754 (2507) | 2811.728027 (2496) | 5741.902755 / 6002.104867 |
| lux | 2744.029297 (1051) | 3500 (2086) | 2676.242920 (2084) | 3727.611534 / 4347.768116 |

- [Demise's public viewer](https://ksf.surf/replays/surf_demise/replay_css_5121_0_540902_1785239310.rec?game=66t) reports `maxVelocity: 5000`. The samples exceed 3500 but never reach 5000 exactly. Thus 5000 is public metadata supported by compatible observations, not a cap independently measured by a plateau in this run.
- [Aircontrol's viewer](https://ksf.surf/replays/surf_aircontrol_ksf/replay_css_15_0_540902_1705101548.rec?game=66t) reports `maxVelocity: 10000`. The recorded components exceed 5000, but do not approach 10000. This run disproves a 3500 or 5000 component ceiling for its recorded route; it does not independently locate the 10000 ceiling.
- [Kitsune's viewer](https://ksf.surf/replays/surf_kitsune/replay_css_2000_0_712551_1726969048.rec?game=66t) reports `maxVelocity: 0`, which is not evidence for zero movement or an unlimited cap. X has 264 frames at exactly ±3500; Z has 60 at exactly -3500. No component exceeds 3500. A 3500 component cap is strongly supported by these plateaus.
- [Lux's viewer](https://ksf.surf/replays/surf_lux/replay_css_2985_0_877922_1768384524.rec?game=66t) also reports 0. Y has 114 frames at exactly +3500 and no component exceeds 3500, supporting the same inferred component cap.

## Remaining gaps

The files omit command analog magnitudes, engine build, cvars, plugin versions, grounded/contact flags, active crouch hull and per-tick target-name changes. Reconstructing held buttons and view angles requires the independently established alignment described in [the existing revision report](REVISION-VALIDATION.md), and the new routes have not yet independently re-established every input assumption. Native repeated-jump rules, surface friction and entity queue timing cannot be fully derived from these files alone.

Map geometry checks, continuous command-driven reproduction, legal stationary starting states and local timer-zone validation remain separate work. In particular, Demise and Aircontrol's first recorded frames already move, and Kitsune contains explicit server/replay stage relocation. Using recorded positions to place a local checkpoint or teleport destination does not validate the path that reaches it. No full-route parity or simulator completion claim is made by this telemetry capture.
Map entity review

Download this document

# New map entity review — 5 October 2026

This reviews the downloaded BSPs and the existing importer/runtime before the new map implementation. It separates authored map facts, public Source reference behavior, measured installed CSS behavior, and proposed browser rules. It does not claim that the public SDK is the shipped CSS game code. A limited native probe was subsequently authorized and completed in the isolated project directory; no installed game files were changed, and the server was shut down. See [native teleport results](NATIVE-CSS-TELEPORT-PROBE.md).

## Reviewed revisions

| Map | SHA-1 of decompressed BSP | Teleports | Trigger multiples | Push triggers |
| --- | --- | ---: | ---: | ---: |
| surf_demise | `15b16c315649d25420f25517e47454497f795b0c` | 17 | 5 | 0 |
| surf_kitsune | `41ff082a485d5b4a306589205187bc730ca4b2ac` | 53 | 22 | 2 |
| surf_aircontrol_ksf | `3c3b754ffb0f02b3d4a1506c8ffebdca5d18e902` | 46 | 14 | 4 |
| surf_lux | `54745509ca87f2616311a0b29e4774e80b566067` | 33 | 5 | 0 |

The source manifest is `scripts/fixtures/new-maps-sources.json`; the source files are `node_modules/.map-imports/<map>.bsp`. Entity facts below come directly from lump 0, retaining duplicate key/value pairs, and model bounds from lump 14 through `scripts/bsp_common.py`. Bounds quoted below are translated world bounds; actual trigger contact must use each model's convex brush union.

All 149 `trigger_teleport` entities have spawnflags 1. None specifies a landmark, a nonzero teleport angle, or an output that changes velocity/base velocity. All target names resolve to at least one authored entity using case-insensitive comparison. Teleport classification cannot be inferred from classname or target name alone.

## Required main-route teleports

### Kitsune: nine stages

The normal sequence is Red, Orange, Yellow, Green, Teal, Blue, Purple, Pink, White. Source target names use mixed capitalization, so resolve them without case sensitivity. These stages have no authored named timer start/checkpoint/end triggers. Timer boxes and the choice to start at Red rather than the lobby therefore require a separately documented server/browser rule.

| Stage destination | Authored feet origin | Yaw | Progression into the next stage | Failure returns to this stage |
| --- | --- | ---: | --- | --- |
| 1 Red | `(-15360, -15088, 825)` | 90 | `*1`, Hammer 1001 → Orange | `*3` |
| 2 Orange | `(-13312, -15216, -455)` | 90 | `*2`, Hammer 1612 → Yellow | `*4` |
| 3 Yellow | `(-11264, -15216, -1735)` | 90 | `*6`, Hammer 2487 → Green | `*5` |
| 4 Green | `(-8192, -15216, -2935)` | 90 | `*8`, Hammer 7224 → Teal | `*7` |
| 5 Teal | `(-5120, -15216, -5399)` | 90 | `*14`, Hammer 9566 → Blue | `*13` |
| 6 Blue | `(-2048, -15216, -7895)` | 90 | `*15`, Hammer 10394 → Purple | `*48`–`*51` |
| 7 Purple | `(512, -14928, -12119)` | 90 | `*16`, Hammer 12259 → Pink | `*17` |
| 8 Pink | `(8192, -368, 6560)` | 270 | `*19`, Hammer 13925 → White | `*18` |
| 9 White | `(-15104, 15072, 10793)` | 270 | `*36`, Hammer 34698 → lobby `start` | `*21`–`*35`, `*37`–`*43` |

All destination pitches are zero. The final `*36` portal is the end of the ninth stage, even though its target is named `start`. Its world bounds are `(-16064,10072,-11936)` to `(-16032,10280,-11744)`. A finish must be recorded before the destination enters the lobby. Lobby entry portal `*10` leads to Red; `*9` leads to the separate lobby `spawn` point. Lobby `start` is `(0,-1456,200)` and lobby `spawn` is `(0,-1964,395.063)`.

The secret path is distinct: `*60`, `*66`–`*71` are gated by `filter_activator_name`, with the targetname sequence established by `trigger_multiple` outputs. Treating those filtered portals as unconditional creates false shortcuts through ordinary stages. `*81` is initially disabled and only enabled temporarily by a secret-room button; it must not be enabled by default. Secret-room `*84` returns to lobby `start`. The main route does not require these secret mechanics. If only the main route is supported, list the excluded entities and reason in import metadata, and invalidate a timed run on explicit bonus/secret travel.

Kitsune's repeated `OnEndTouch` outputs to `counter_3` have no matching entity in this BSP. They do not justify incrementing browser stage state. Stage progress should follow the reviewed main portal sequence, never arbitrary targetname outputs.

### Lux: an essential midcourse transfer

Lux is linear. It contains 31 return portals to `spawn` and two progression portals:

| Source | Hammer ID | Destination | Destination origin | Destination yaw |
| --- | --- | --- | --- | ---: |
| `*38` | 142959 | `s2_left` | `(6784,-15168,-1856)` | 90 |
| `*39` | 142970 | `s2_right` | `(7168,-15168,-1856)` | 90 |

The paired entrances sit around `(-192,15520,-8384)` and `(192,15520,-8384)`. They preserve the continuous main route; neither is a fall or a new map start. Authored timer names are `start_zone` (`*6`), `checkpoint_1` (`*7`), `checkpoint_2` (`*8`), `checkpoint_3` (`*9`), and `end_zone` (`*16`). Main destination `spawn` is `(0,1984,1664)`, yaw 270. This revision contains TF2 `info_player_teamspawn` and `func_respawnroom` entities; they are not CSS player starts. Use the reviewed teleport destination for the browser spawn and describe that decision.

### Demise

Nine teleports return to `map_start`; eight belong to the bonus and target `bonus_start`. Main `map_start` is `(-11520,-3968,15120)`, yaw 0. `startzone` is model `*23`; `endzone` is `*75`. Both are `trigger_multiple` entities with spawnflags 0, as are `bonus_start` and `bonus_end`: extracting them as server timer geometry is distinct from claiming that unmodified native CSS would activate them for players. The BSP has no named main checkpoint trigger entities; the manifest's two checkpoints need independent server zone evidence or explicitly local zones.

The `bonus_start` name is shared by an earlier `trigger_multiple` and a later `info_teleport_destination`. Both origins are `(10496,-10496,-2176)` and both effectively have zero angles, so this revision yields the same transform either way. A generalized importer must preserve entity order and duplicate-name diagnostics rather than silently assume every teleport target is an `info_teleport_destination`.

The authored `logic_auto` explicitly issues `sv_maxvelocity 5000`. Trigger `*52` repeats `gravity 1` on start/end touch; it does not introduce a different gravity profile.

### Aircontrol KSF

The main course has `start_trigger` (`*37`), `end_trigger` (`*35`), and `cp1_trigger` through `cp5_trigger` (`*50`–`*54`). Main destination `start` is `(-1856,-9504,14720)`, yaw 90. These timer volumes use filter `mainmap`, which matches player targetname `main`.

An overlapping bonus uses `startbonus_trigger` (`*38`), `endbonus_trigger` (`*39`), filter `bonusf`, and `ModifySpeed 2`. Main start outputs restore speed modifier 1. Portal `*40` assigns the activator targetname `bonus`; `*41` assigns `main`; both return to `start`. Additional spawn triggers assign `main`. Exporting both overlapping start volumes without filter/state support would incorrectly double normal main-course movement time. Either implement those states faithfully, or expose the main route explicitly and omit the bonus switch/effect from timed main play.

Portal `*55` leads to a cosmetic secret room; `*56`–`*58` return to `start` while changing model index. Portals `*59`, `*64`–`*66` belong to the separate `coursestart` route, with four 400-unit continuous push triggers (`*60`–`*63`). Do not automatically classify these as main-course resets. No main-course midroute teleport is required by this revision's entity graph.

The [Aircontrol command witness](AIRCONTROL-CANONICAL-WITNESS.md) now completes the main course from the normal browser spawn. Its movement comparison independently matches all 2,534 native next-frame samples through the KSF finish bookmark within 0.002 units of position and velocity. The authored BSP `end_trigger` is reached at recorded frame 2,547, **13 ticks later** than KSF's finish bookmark at 2,534. The private KSF finish-zone bounds are unavailable; this evidence does not establish exact KSF timer parity.

## CSS evidence and the reference boundary

The installed CSS `bin/base.fgd`, lines 6305–6315, describes destination position and destination angles, landmark-relative offsets with preserved angles, and flag 32 preserving angles without a landmark. This is Valve's installed authoring metadata, not an execution test. Its adjacent `trigger_teleport_relative` description must not be substituted for `trigger_teleport`; none of these maps uses that separate classname.

The public Source reference resolves a target, clears ground, applies destination origin (adjusting by player minimum Z), and takes the target angles when no landmark or preserve-angle flag is present. A resolved landmark adds the player's world offset from that landmark and leaves angles unchanged. Its non-HL1 path passes a null velocity to `Teleport`, so ordinary world velocity is retained rather than zeroed or rotated. A missing destination returns without moving. This is an implementation reference, **not native CSS proof**. [Valve trigger implementation, pinned revision](https://github.com/ValveSoftware/source-sdk-2013/blob/b8cfb12c0e083a2ef5b2f9f9b50f3902fa034474/src/game/server/triggers.cpp#L2181)

On a new contact the shared reference calls `StartTouch` before `Touch`; existing contact calls `Touch` again. Thus ordinary teleports are not merely one-time OnStartTouch actions. [Valve shared contact handling](https://github.com/ValveSoftware/source-sdk-2013/blob/b8cfb12c0e083a2ef5b2f9f9b50f3902fa034474/src/game/shared/physics_main_shared.cpp#L843)

Outputs are queued, including zero-delay actions. Parsing prepends output actions, while equal-time queued events retain insertion order; file order is therefore not a safe synchronous execution rule. Events execute when their due time is reached by the queue service. Preserve duplicate outputs, delay and fire-count fields if implementing entity I/O. [Valve output and event queue implementation](https://github.com/ValveSoftware/source-sdk-2013/blob/b8cfb12c0e083a2ef5b2f9f9b50f3902fa034474/src/game/server/cbase.cpp#L284)

The earlier [native CSS probe](NATIVE-CSS-PROBE.md) establishes standing/crouched minimum Z of zero and hull heights 62/45 for installed build 11003710. The new [native teleport probe](NATIVE-CSS-TELEPORT-PROBE.md) confirms that Kitsune `*1` and Lux `*38`/`*39` preserve world velocity `(123,456,78)` and use exact authored destination origins in both hull states, with no crouch Z offset. Its connected VScript handlers sometimes execute several ticks later and can observe state from a subsequent teleport; those callbacks are unsuitable as contact-time telemetry. The one-tick RCON reads are the recorded post-contact samples. Bot aim drift limits exact eye-angle comparison. Neither probe establishes normal swept contact timing, overlap ordering, or all entity I/O timing.

KSF's own [command documentation](https://ksf.surf/commands) distinguishes restarting the map, returning to the current zone, selecting stages, and repeating stages. It does not publish the implementation of stage-failure timer preservation, zero-delay entity scheduling, or velocity handling. Stock CSS has no surf-run timer. Continuing total elapsed time after an authored Kitsune stage failure is the proposed browser rule unless separate current KSF evidence is obtained; describe it as such.

## Recommended minimal runtime contract

Keep timer geometry separate from movement-changing entity effects. A `trigger_teleport` is not a timer `reset` zone. The existing `GameSession.restart()` creates a fresh player, clears all commands and splits, resets tick/time, and therefore cannot be used for normal stage progression or a stage failure during a whole-map run.

An explicit import interface can be compact:

```ts
interface MapTeleport {
  id: string;
  sourceEntityIndex: number;
  sourceModelIndex: number;
  hammerId: string;
  min: Vec3;
  max: Vec3;
  hulls: Brush[];
  targetName: string;
  destination: Spawn;
  enabled: boolean;
  angleMode: 'destination' | 'preserve';
  velocityMode: 'preserve';
  landmarkOrigin?: Vec3;
  route: 'progress' | 'stageReturn' | 'mapReturn' | 'finish' | 'auxiliary';
  destinationStage?: number;
}
interface MapStage {
  number: number;
  name: string;
  spawn: Spawn;
  startZoneId: string;
}
```

`route` and `destinationStage` are reviewed browser semantics, not raw CSS entity properties. Preserve raw key/value pairs and omitted filter/disabled/bonus entities in metadata. For these four main routes the effect subset needs ordinary absolute teleports, target angles, and continuous pushes; no generic landmark rotation, velocity rotation or general I/O interpreter is required. Do not support a fabricated landmark-angle feature: the reviewed reference is a world-space translation only.

Recommended session rules:

- Use the existing swept convex trigger union to find candidate contacts, with a deterministic tie-breaker recorded as a browser policy until native overlap order is measured. Respect disabled/filter classification before choosing the first effective teleport.
- End the source path at the chosen transfer for timer processing. Never create a swept segment joining source to remote destination; that line can cross unrelated checkpoints or a finish. Discard source-path events after the teleport instead of processing them from the already-computed whole movement tick.
- Apply position/angles without `createPlayer` or `restart`; preserve duck state, buttons, global tick, command history, and appropriate velocity. Clear stale grounded/contact state. Rebase the renderer's previous position so interpolation cannot fly across the map. Refresh destination overlap state without a fictitious travel segment.
- Keep only one authoritative elapsed tick for each input command. The reviewed material does not justify accelerating again or adding an extra full gravity tick after a teleport. Whether native CSS spends residual movement time at the destination remains a native-probe question, so version and document the chosen policy.
- Mark Kitsune progression once and in order; falling on stage N returns to its authored spawn without discarding whole-map elapsed time or completed splits. Repeating a portal, touching a failure volume, or selecting a practice station cannot grant a missing stage. Explicit map restart still resets the run. Explicit practice travel remains unranked.
- Finish Kitsune when the ninth-stage end volume is reached, before the portal returns the player to the lobby. Lux's pair of midcourse portals preserves its linear timer and checkpoint sequence.

Replay/version identity must include the new effect semantics. A replay spanning a stage transfer must keep the same command timeline and reproduce from a fresh session. Changing teleport timing or velocity policy later requires a version change.

## Geometry and asset hazards

Kitsune lobby push `*11` has entity angles `(1,87,0)`, speed 1750, and `pushdir (0,90,0)`. Its broad bounds must be recomputed from transformed hull vertices or transformed bounds corners; merely translating lump-14 min/max lets the broad phase reject valid contacts. The importer now preserves the transformed bounds, with a focused fixture in `tests/new-map-import.test.ts`. Do not rotate the authored world push direction twice. The separate secret-room push `*83` is 2000 toward yaw 270.

Demise has 101 `prop_dynamic_override` instances with solid 6: 82 `game_tree2`, one `game_tree67`, two `game_tree12`, four each of `bones01`/`bones02`/`bones03`, and four `door1_hell`. Aircontrol has three solid-6 dynamic models, `models/custom/ed.mdl`, `edd.mdl`, and `eddy.mdl`. Their MDLs exist, but none of their ten distinct model paths has a PHY in the BSP or mounted installed CSS/HL2 packages. Record these missing collision resources explicitly. Do not invent collision from visual triangles or assume that every dynamic prop is decorative. Native missing-PHY collision behavior on route-relevant contacts remains unmeasured.

The installed native CSS server does accept Demise's 101 MDL-version-49 tree props (a different set from the 101 solid-6 props above: 82 `game_tree2`, 16 nonsolid `hallo_tree_2`, two `game_tree12`, one `game_tree67`). They instantiate as live `prop_dynamic` entities with real model indices and scaled bounds, rather than an error model. Do not omit their visuals on the assumption that CSS only accepts model version 48. The recorded inventory is `fixtures/native-css-demise-model-probe.txt`; it is server evidence of model loading, not a client-rendered visual test.

Demise `func_brush` models `*72` and `*74` have Solidity 1; rotating decoration `*73` has `solidbsp 0` and spawnflags 65. These are distinct from ordinary solid brush geometry. Kitsune's twelve `kitsune`-named `func_brush` entities have Solidity 2; do not exclude them merely because they share a decorative name.

## Focused native checks still needed

The limited placement tests are complete; see [the results and controls](NATIVE-CSS-TELEPORT-PROBE.md). For repetition, use Kitsune `*1` feet `(-15360,-11545,448)`, angles `(7,13,0)`, velocity `(123,456,78)`, and nearby free-space control `(-15360,-11650,448)`. **Do not use the portal AABB center** `(-15360,-11520,448)`: the player hull overlaps backing BSP solid `bsp-0-1122` by six units there, contaminating the measured velocity with stuck/collision behavior. The corrected test retains velocity for standing and crouched players.

Still unmeasured are Kitsune `*3` failure return, normal high-speed entry across a thin portal, and touching adjacent/overlapping volumes. For I/O ordering, add probe-only outputs to an existing entity rather than altering the downloaded BSP; record event timestamps and state before/after a queued zero-delay velocity change. Probe landmark behavior only if generic landmark support is actually included. Do not generalize the completed standing/crouched placement tests to full-route native parity.
Native teleport tests

Download this document

# Native CSS teleport probe — 5 October 2026

Installed Counter-Strike: Source build **11003710**, protocol 24, directly confirmed that ordinary Kitsune and Lux teleports retain velocity and use the same destination feet height for standing and crouched players. The zero-speed stage arrivals observed in KSF public records are a separate behavior; this probe does not identify which server or replay component produces them. See the [multi-record Kitsune review](KITSUNE-TELEPORT-EVIDENCE.md).

## Isolation and method

The server used the already-established `node_modules/.native-css-probe/cstrike` mod directory, mounted installed assets read-only, and ran hidden with `-insecure -ip 127.0.0.1 -port 27025`. There were no human clients or movement/timer plugins. All new scripts, map copies and writable configuration stayed inside the ignored probe directory. The installed executable and files were unchanged. `quit` stopped the server, and a process check confirmed that no `srcds_win64` process remained.

The four map SHA-1 values and entity classifications are recorded in [NEW-MAP-ENTITY-REVIEW.md](NEW-MAP-ENTITY-REVIEW.md). Kitsune and Lux used the exact reviewed BSPs. Lux's BSP has TF2 player spawns but no CSS spawn classes, so the probe-only `mapspawn.nut` added one T and one CT spawn point; it did not change any geometry or teleport entity. A reused navigation file allowed inert test bots to spawn without generating a new mesh; navigation was not used to exercise the portals.

The controlled cases used gravity, air acceleration, ground acceleration and friction set to zero; `bot_stop 0`, `bot_freeze 1`, `bot_zombie 1`; and `bot_crouch` 0 or 1. Every accepted portal result has a nearby free-space control in the transcript. A script placed one bot with velocity `(123,456,78)` and eye angles `(7,13,0)`, then printed its immediate state. A separate RCON command read the state at the next 0.015-second server-time step. Zero gravity isolates velocity changes and is not the game's surf physics profile.

## Accepted results

| Portal | Input feet origin | Measured destination origin | Standing/crouched velocity |
| --- | --- | --- | --- |
| Kitsune `*1` → Orange | `(-15360,-11545,448)` | `(-13312,-15216,-455)` | `(123,456,78)` / `(123,456,78)` |
| Lux `*38` → `s2_left` | `(-192,15520,-8384)` | `(6784,-15168,-1856)` | `(123,456,78)` / `(123,456,78)` |
| Lux `*39` → `s2_right` | `(192,15520,-8384)` | `(7168,-15168,-1856)` | `(123,456,78)` / `(123,456,78)` |

The measured standing bounds were `(-16,-16,0)` to `(16,16,62)`; crouched bounds were `(-16,-16,0)` to `(16,16,45)`. All six portal cases returned the exact authored feet origin at the following read. There is no additional 8.5-, 17-, 22.5- or 31-unit teleport destination offset for crouching. Ordinary duck/unduck transitions on later ticks remain separate movement behavior.

Kitsune controls from `(-15360,-11650,448)` moved to approximately `(-15358.155273,-11643.160156,449.170013)` in one tick and retained the injected velocity. Lux controls from `(0,1900,1900)` moved to `(1.845000,1906.839966,1901.170044)` and retained it. These are the expected one-tick displacements at the test velocity, subject to float precision.

Input eye yaw 13 changed to approximately 87–89 degrees after each teleport, consistent with destination yaw 90. The test bots continued adjusting their eyes, including during the free-space controls, so the read does **not** prove exact equality to pitch 0/yaw 90 at the instant of transfer. The authored destination angles and installed Valve FGD supply the transform reference; this native check supports the direction change while retaining that precision limit.

The complete RCON transcript, including rejected exploratory cases, is [native-css-teleport-probe.txt](../fixtures/native-css-teleport-probe.txt). The final accepted Kitsune standing case is at server time `138.660 → 138.675`; crouched is `101.745 → 101.760` after the last Kitsune load. Accepted Lux standing cases are `18.855 → 18.870` and `18.885 → 18.900`; crouched cases are `52.185 → 52.200` and `52.215 → 52.230`.

## Rejected exploratory cases and output timing

The first Kitsune probe used the portal's AABB center `(-15360,-11520,448)`. The player hull there penetrates backing world brush `bsp-0-1122` by six units. Those samples often returned zero velocity because the placement was contaminated by collision/stuck handling; they are not evidence that the teleport resets velocity. The initial distant control `(-15360,-14500,1100)` also failed to demonstrate clean free movement and was replaced. Changing bot controls alone did not consistently remove the contamination. The corrected source point overlaps the trigger without overlapping solid geometry and gives repeatable preserved velocity in both hull states.

OnStartTouch handlers connected through VScript were logged into a script array and read later. They ran several server ticks after the transfer in these tests. For example, Lux's left-portal callback ran at `18.930`, after a right-portal test had already occurred, and therefore reported a position near the **right** destination. These output callbacks cannot be treated as exact pre-contact or immediate post-contact state. This also explains why post-teleport OnStartTouch callback positions in older probe logs do not locate the original contact.

The observation applies to these connected VScript handlers. It does not establish a universal 0.06-second delay for all authored Source entity outputs. Exact authored I/O queue phase, duplicate output order and normal movement-trigger overlap ordering remain unmeasured.

## Demise model load check

The same stock server subsequently loaded the pinned Demise BSP. VScript enumerated all 101 version-49 tree entities as live `prop_dynamic` entities with real model indices: 82 `models/que/game_tree2.mdl` (index 225), 16 `hallo_tree_2.mdl` (226), two `game_tree12.mdl` (231), and one `game_tree67.mdl` (227). The reported bounds reflect the authored model scales; no substitution to `error.mdl` appeared in the inventory. The raw inventory is [native-css-demise-model-probe.txt](../fixtures/native-css-demise-model-probe.txt).

This demonstrates acceptance by the installed server's model loader. It does not verify a native client's rendered appearance or missing-PHY collision behavior.

## Limits

These are controlled placement/contact tests, not a normal-command replay through the native course. They do not prove residual-tick movement after swept entry, native ordering between simultaneous portals, exact client view-angle prediction, or KSF's current plugin configuration. They provide direct stock CSS evidence for the three tested teleports' destination and velocity behavior. Separately observed KSF stage teleports may relocate and clear velocity through server plugins even when the authored BSP teleport preserves it.
Kitsune portal momentum: native CSS and KSF records

Download this document

# Kitsune momentum: stock CSS and public KSF records

Rechecked 5 October 2026 after the user questioned whether Kitsune portals should preserve momentum. **The authored stock CSS portal does preserve momentum. Three independent runners' public KSF CSS 66t full-map files instead contain zero-horizontal-speed stage arrivals, at different stage-spawn coordinates.** Those are distinct observations. The files do not identify the responsible server or exporter component, so calling the recorded behavior a proven KSF stage-plugin implementation was too strong.

No core movement, map, timer or replay behavior was changed during this review. The earlier isolated native CSS server was not restarted.

## Fresh primary records

The live [Kitsune main-map page](https://ksf.surf/maps/surf_kitsune?game=66t&mode=fw) linked three downloadable records in its top ten. Their separate replay viewers all identify CSS, the exact map, a 66.66666666666667 Hz sample rate, main zone 0 and finish type 0. The leaderboard query selects forward style. These are presented by KSF as full-map records, not individual-stage records.

| Runner / date UTC | Public file | SHA-256 | Main-run frames × .015 / published seconds |
| --- | --- | --- | --- |
| .x / 2024-09-22 | [712551 / 1726969048](https://ksf.surf/api/replays/replay_css_2000_0_712551_1726969048.rec?game=66t) | `274ad1f6486452755d3f96074357a68618308a52ff7a5f19964bd2f72a4a4aa0` | 6129 / 91.935 / 91.934898 |
| yupiter / 2020-10-05 | [525989 / 1601934267](https://ksf.surf/api/replays/replay_css_2000_0_525989_1601934267.rec?game=66t) | `2861ed10f8756751fe6e78183ce92e675aaf15dc22017b0cd82fee2e6c67d72d` | 6142 / 92.130 / 92.129570 |
| Runner 516878 / 2018-10-30 | [516878 / 1540875981](https://ksf.surf/api/replays/replay_css_2000_0_516878_1540875981.rec?game=66t) | `68483b9eb78e98f7ab126459c401cb38d8fe4d9a1ef1ffaa11e9faa0df57c1e9` | 6180 / 92.700 / 92.699333 |

The last runner's public display name is retained verbatim in the captured source metadata; the table uses the stable numeric player identifier.

A bounded check of the [linked full records page](https://ksf.surf/maps/surf_kitsune/records?game=66t&mode=fw) and its public API covered ranks 1–60. It includes runs from 2025 and 14 August 2026, but every entry newer than the .x September 2024 record has `file: null`. No newer downloadable comparison was available in that checked range. This is not a claim about every record or the current live-server configuration.

## Exact transition observations

All **24** stage transitions across the three files have this pattern:

1. A final source-stage movement sample with substantial horizontal velocity.
2. An intermediate point approximately `(0,0,1000 × previous stage)`, with angles `(0,0,0)`. Horizontal velocity remains substantial; it can change through that sample's input acceleration. In yupiter's file two X coordinates are tiny nonzero floats (`-2.7024517550189722e-36` and `6.701612680182866e-35`), so exact zero must not be assumed universally.
3. On the following frame, the fixed stage spawn below with exact velocity **`(0,0,-6)`**, pitch 0 and yaw 90 for stages 2–7, yaw 270 for stages 8–9.
4. The stage-entry bookmark one frame later, typically with velocity Z −18 and horizontal velocity from the runner's input.

| Arriving stage | Exact recorded spawn | .x spawn frame | yupiter spawn frame | 516878 spawn frame |
| --- | --- | ---: | ---: | ---: |
| 2 Orange | `(-13312,-15072,-320)` | 457 | 583 | 431 |
| 3 Yellow | `(-11264,-15072,-1600)` | 797 | 927 | 776 |
| 4 Green | `(-8192,-15072,-2848)` | 1231 | 1365 | 1217 |
| 5 Teal | `(-5120,-15072,-5312)` | 1697 | 1830 | 1687 |
| 6 Blue | `(-2048,-15072,-7776)` | 2158 | 2289 | 2151 |
| 7 Purple | `(512,-14784,-12032)` | 2680 | 2807 | 2672 |
| 8 Pink | `(8192,-512,6624)` | 3440 | 3570 | 3441 |
| 9 White | `(-15104,14928,10880)` | 4098 | 4233 | 4108 |

For a concrete exact sequence, .x's frames 455–458 contain:

| Frame | Position | Velocity |
| --- | --- | --- |
| 455 | `(-15245.607421875,-11565.146484375,442.13360595703125)` | `(-26.82639503479004,933.5990600585938,-22.006622314453125)` |
| 456 | `(0,0,1000)` | `(-26.82639503479004,933.5990600585938,-34.006622314453125)` |
| 457 | `(-13312,-15072,-320)` | `(0,0,-6)` |
| 458 | `(-13311.6953125,-15071.6689453125,-320.17999267578125)` | `(20.29926872253418,22.08935546875,-18)` |

The [machine-readable review](../fixtures/kitsune-transition-review.json) stores nine complete surrounding samples per transition, all exact binary32 values, source URLs, viewer metadata and hashes. It preserves the three original `.rec` files and matching public viewer HTML. `scripts/review-kitsune-transitions.ts` decodes them offline; `--fetch --records` refreshes the source captures.

## Stock BSP and native CSS evidence

The pinned BSP SHA-1 is `41ff082a485d5b4a306589205187bc730ca4b2ac`. Its ordinary `trigger_teleport` entities have player flag 1, no landmarks and no authored velocity-reset outputs. There are no authored destination entities at those eight intermediate `(0,0,Z)` points. Its Orange destination is `(-13312,-15216,-455)`, different from the public records' `(-13312,-15072,-320)`.

The [native CSS probe](NATIVE-CSS-TELEPORT-PROBE.md) placed a standing and a crouched player at a clean contact with Kitsune `*1`. Both arrived at the exact authored Orange destination while retaining injected velocity `(123,456,78)`. Nearby free-space controls retained the same velocity, and the source placement was checked against solid geometry. The initially contaminated portal-center placement was rejected. This is direct stock CSS evidence, not a Source SDK branch assumption.

The installed authoring metadata and [Valve's reference trigger implementation](https://github.com/ValveSoftware/source-sdk-2013/blob/b8cfb12c0e083a2ef5b2f9f9b50f3902fa034474/src/game/server/triggers.cpp#L2181) also support ordinary velocity preservation. The reference code alone would not prove behavior in CSS; the native test supplies that evidence for the tested portal.

## Replay processing and assembled-stage hypothesis

The public [ReplayViewer loader](https://github.com/crashfort/ReplayViewer/blob/0341fea8ba85fbb7c3d352b4b5ddf704a659fd4d/src/rv_load.inc#L30) reads stored bookmark and frame structures directly into arrays. It chooses a synchronization start and duration from the first start and last stop bookmarks. It does not assemble stage files or synthesize the stored intermediate points, spawns or velocity resets.

The [playback bot](https://github.com/crashfort/ReplayViewer/blob/0341fea8ba85fbb7c3d352b4b5ddf704a659fd4d/src/rv_bot.inc#L162) can teleport to recorded positions and otherwise derives playback velocity from displacement. Therefore a playback bot's measured runtime velocity is not necessarily the file's velocity field. This review decodes the file itself, avoiding that playback layer. Neither public file establishes how KSF's backend records or exports its bytes.

The full-map files retain 98–116 frames of later-stage preparation between each stage-entry stop bookmark and its next start bookmark. Their first-start-to-final-stop frame durations match the published full-map times to less than 0.0007 seconds, including that preparation. Individual-stage PR metadata differs from these full-run stage intervals. This weighs against a simple concatenation of timed WRCP clips, but cannot rule out transformations inside an unpublished recorder/exporter. Multiple runners are separate recorded performances, not independent implementations of that backend.

## What the evidence supports

- **Strong:** stock CSS Kitsune `*1` preserves velocity at the authored destination; the three public KSF CSS 66t forward files consistently show zero-horizontal-speed arrivals at different stage spawns.
- **Supported inference:** KSF's published full-map experience includes later-stage preparation after those arrivals, rather than uninterrupted momentum carried through the authored destinations.
- **Unresolved:** which server, map modification, recorder or exporter component creates the alternate transition; whether current 2026 live servers behave identically; and whether other games, tick rates, styles or server configurations differ.

The browser should describe any zero-arrival behavior as matching the reviewed KSF record profile. It should not present that behavior as the universal stock CSS teleport rule or proof of a named KSF plugin. Progression evidence also does not establish every stage-failure return's policy.
Original artwork conversion

Download this document

# Four-map visual import review

The new courses use their authored CSS BSP geometry and packed assets, not a
reconstruction from video. See `scripts/fixtures/new-maps-sources.json` for the
four exact BSP hashes and authors. The original asset authors retain their
rights; these converters do not grant redistribution rights. No Valve source
implementation was copied.

## Reproduce

After the pinned BSP download, run `scripts/import_boreas_visuals.py` for each
source with `--slug`, `--map-id`, `--author`, `--game-dir` pointing to the local
CSS installation, `--texture-format lossless-webp` and `--compressed-textures`. The Python environment
needs NumPy and Pillow. Source files stay in ignored `node_modules/.map-imports`;
the generated resources and metadata live under `public/maps`.

Example:

```powershell
python scripts/import_boreas_visuals.py node_modules/.map-imports/surf_kitsune.bsp --slug kitsune --map-id surf_kitsune --author Arblarg --game-dir 'C:/Program Files (x86)/Steam/steamapps/common/Counter-Strike Source' --texture-format lossless-webp --compressed-textures
```

`npx tsx scripts/capture-new-map-previews.ts` checks the actual game renderer
against the raw imported assets at local Vite port 4193 (override `UI_BASE_URL`
if needed). It saves four camera views per map (plus Kitsune's S1 spawn) and a browser report under
`test-results/new-map-visuals`, plus actual map screenshots for the menu in
`public/previews`. Cameras use positions and directions from the public CSS
recordings. They are visual tests, not evidence of playable command replays.
Set `CAPTURE_MAPS=kitsune` to refresh only that map's preview and targeted report.

`python scripts/validate_new_map_visuals.py --game-dir '<local CSS directory>'`
checks every exported texture against its original decoded VTF pixels, pinned
BSP identity, asset existence, binary mesh lengths/indices and finite vertex
data. This integrity check shares source decoders with import; it is not an
independent proof of CSS rendering parity.

## Importer corrections

- Source static-prop skin is a signed 32-bit integer. The shared reader had read
  those four bytes as a float, silently reducing nonzero families to zero. The
  corrected field restores 41 Demise instances using skin 1 or 3. Every other
  decoded static-prop field is unchanged.
- Studio geometry now selects the actual skin family and bodygroup. Materials
  are fetched only when referenced by that selected geometry, eliminating
  warnings for unused skins and unused texture downloads.
- Initial `prop_dynamic` and `prop_dynamic_override` poses are imported with
  original position, angles, model scale, skin and entity tint. Skeletal and
  entity-driven animation are not claimed. Aircontrol's three figures and
  all 134 Demise dynamic decorations were previously omitted. Current native
  CSS accepted the 101 v49 trees; their original model scales are preserved.
- Initial brush opacity and disabled state are respected. Kitsune's initially
  invisible secret artwork is no longer drawn as opaque geometry. Its secret
  button-driven visibility sequence remains outside the renderer's supported
  effects.
- Optional lossless WebP preserves full source texture resolution and every
  decoded RGBA byte, including transparent-pixel RGB (`exact=True`). The four
  imports passed byte-for-byte PNG/WebP comparisons for 557 common images.
  PNG remains the default for existing import commands; none of the previous
  three maps' exports were rewritten.
- Original DXT1/3/5 blocks and authored mipmaps are also exported to DDS without
  decoding, re-encoding or resizing. Desktop browsers with S3TC and S3TC sRGB
  support upload those blocks directly. Other browsers receive the lossless
  WebP fallback. Cube faces, sky preparation and non-S3TC source formats keep
  ordinary images. One-bit-alpha DXT1 keeps its alpha format.
- Native and fallback textures occupy separate packs. A client fetches common
  geometry and exactly one texture representation; it does not download both.
  Existing three-map manifests without alternatives retain their old delivery
  paths and exact resource hashes.

## Visual verification and performance

Sixteen still-camera views loaded successfully in an actual Edge Chromium
browser with the production renderer and no console or page errors. Every
referenced asset loaded. A binary audit verified finite vertex data, expected
buffer lengths and in-range triangle indices. The original screenshot review
missed Kitsune's omitted walls; successful loading and file integrity did not
establish correct visibility. The correction and stronger regression below
address that specific failure. The latest audit covers 428 geometry batches
and 570 textures with exact RGBA comparisons to the decoded source VTFs.

| Map | World faces | World batches | Decoded-path asset bytes | Notes |
| --- | ---: | ---: | ---: | --- |
| Demise | 19,381 | 50 | 116,939,954 | 2,040 displacements, 2,350 static props and 134 dynamic initial poses |
| Kitsune | 7,866 | 42 + 1 sky-depth | 2,857,444 | Original neon and black-wall materials; 108 sky portal faces; 69 initially hidden entity faces skipped |
| Aircontrol KSF | 6,522 | 123 | 7,151,909 | Ten displacements, one static model and three dynamic initial poses |
| Lux | 6,068 | 49 | 4,220,000 | Original lit cyan architecture |

Demise's 77 PNG textures were 137,634,524 bytes; equivalent lossless WebP textures
are 81,583,270 bytes, a 40.7% saving for that compared set. This does not reduce
decoded GPU texture memory or geometry detail. The existing pack builder still
groups assets into bounded, cacheable requests.

Demise's source texture set contains 182,004,992 pixels (about 694 MiB when
decoded to RGBA before mipmaps, though the renderer does not upload every texture).
The new native path avoids most of that expansion. The 67 native textures keep
130,246,656 bytes of compressed mip data instead of 819,315,980 bytes of equivalent
RGBA mip data, an 84.1% saving for that subset. These are asset-derived texture
payload estimates, not a measurement of total browser or GPU allocation.

Actual packed download sizes (including the visual manifest):

| Map | Native S3TC | Decoded fallback | Native / fallback requests |
| --- | ---: | ---: | ---: |
| Demise | 82,406,109 bytes | 103,691,945 bytes | 41 / 29 |
| Kitsune | 1,132,949 bytes | 1,238,251 bytes | 4 / 3 |
| Aircontrol KSF | 4,543,072 bytes | 5,631,064 bytes | 3 / 3 |
| Lux | 2,723,688 bytes | 2,875,938 bytes | 3 / 3 |

The build lists the union of both variants, which is larger than either client
download. DDS blocks are larger than decoded-image files before transport gzip
but compress well; retaining four-MiB decompressed pack bounds also limits peak
loader scratch space. Demise's actual native download is 20.5% smaller.

One Demise outdoor view submitted about 1.33 million triangles and 278 draws;
three interior views submitted roughly 355–379 thousand triangles and 42–100
draws. The other maps' inspected views submitted about 9–21 thousand triangles.
Renderer submission time was below 1 ms in these local checks, but that is CPU
submission time only, not a GPU frame-rate benchmark. Whole-course performance
and gameplay validation belong in the main map validation report.

`scripts/new-map-render-performance.ts` animates telemetry camera views through
each course for eight seconds, then switches through both Demise representations
on the same canvas/context. It asserts the exact requested pack URLs, successful
fallback and zero browser errors. The first complete local check observed about
360 FPS (the environment's apparent frame cap) for all native routes, p95 frame
interval 2.9 ms, with identical repeat-Demise geometry/texture/program counts.
These numbers describe this machine, not a minimum supported-device guarantee.
The decoded path exposed a late texture-upload stall; new-map fallback uploads
now occur during preparation before joining instead of when a ramp first enters
view. The targeted rerun sustained about 360 FPS for both Demise paths, p95
2.9 ms and maximum 3.0 ms over the measured route. Preparation took 7.06 seconds
for the fallback and 5.23 seconds for native. Both reused the same context;
geometry/program counts matched and no browser errors occurred. Reports are
`performance.json` and `performance-targeted.json` in `test-results/new-map-visuals`.

An additional browser run denied both S3TC extension queries instead of forcing
the loader option. Lux automatically fetched its decoded packs, loaded zero DDS
textures and rendered the complete camera route with no errors. The exact
network-request assertion passed; `performance-unsupported.json` records it.

Validation commands:

```text
python -m unittest discover -s tests -p test_native_dds.py
npx tsx --test tests/visual-packs.test.ts tests/compressed-textures.test.ts
npx tsx scripts/new-map-render-performance.ts
```

The analytic DDS fixtures distinguish all four mip levels and compression types,
including alpha DXT1, corruption/truncation, cube fallback, and invalid headers.
Pack tests reject mixed/mislabeled variants and independently reassemble every
asset. Export integrity compares all 129 native DDS files with the source blocks
in addition to the 570 decoded image checks. None of these format tests establishes
movement fidelity; that remains the movement and whole-course replay suite.

The reviewed integrity and browser results are retained in `fixtures/new-map-visuals/` (including the original fallback stall and the targeted result after fixing it). Re-running the scripts writes fresh results under ignored `test-results/new-map-visuals/`; review them before replacing the retained evidence.

## Kitsune room-visibility correction

The initial import visibly exposed other stages through authored black walls.
The user caught this after the initial visual review. There were two concrete
causes, both fixed using source geometry rather than hiding stages by number:

1. The importer excluded all `tools/*` materials except `toolswhite`. Kitsune's
   packed `TOOLS/TOOLSBLACK` is actually an opaque `UnlitGeneric` material with
   surface flags zero and an entirely black, fully opaque VTF. The BSP contains
   2,349 such faces. Respecting the compiled surface flags restores 2,343 visible
   faces; six belong to initially hidden entities. Kitsune now has 7,866 ordinary
   exported faces, 42 material batches and 24 texture images.
2. After restoring black walls, white S9 geometry still appeared behind the
   orange S2 room. A camera ray through screenshot pixel (675,45) reaches authored
   `tools/toolsskybox` face 7773 at 1,711 units, well before white S9 geometry at
   19,748 units. The old renderer omitted that portal. The importer now retains
   108 world sky faces in one optional depth-only mesh (322 triangles). It is
   drawn after the sky and before the normal world, retaining the sky colour
   while rejecting world geometry behind the authored plane. No collision,
   player motion, stage selection or invented walls are involved. The 22 sky
   faces inside the separate miniature sky area are not world portals.

`tests/test_visual_surface_flags.py` checks the pinned BSP, independent compiled
flags, packed material/image, restored geometry and the exact sky portal plane.
`scripts/probe-kitsune-visual-occlusion.py` preserves the source ray evidence,
with the same FOV, viewport and clamped pitch as the screenshot camera. The
browser capture includes a negative control: at the S2 camera it renders the
same map with and without the sky-depth mesh and counts white geometry in the
previously leaking region. This checks that the depth pass actually removes the
observed leak, beyond merely confirming metadata exists. The 180 by 120 pixel
region contained 959 white S9 pixels without the depth pass and zero with it;
the five inspected views had no browser errors. Results are retained in
`fixtures/new-map-visuals/kitsune-visibility.json`.

Source's [surface flags](https://github.com/ValveSoftware/source-sdk-2013/blob/master/src/public/bspflags.h)
distinguish sky, nodraw, hint, skip and trigger faces from ordinary rendered
surfaces. [VBSP EmitFace](https://github.com/ValveSoftware/source-sdk-2013/blob/master/src/utils/vbsp/writebsp.cpp#L438)
writes the oriented plane index and its parity into `side`; an additional normal
flip would be wrong. All Kitsune face indices match that encoding and all 130
sky faces belong to the world model. The optional sky path also applies brush
entity transforms when another import contains them.

A read-only seven-map audit found other renderable `toolsblack` faces in Demise
(118), Utopia (93) and Mesa (1). Those exports were deliberately not regenerated
as part of this Kitsune correction. Boreas has only 22 already-retained
`toolswhite` faces; Aircontrol and Lux have no renderable tool-material faces.
Their retained older exports therefore still have their previously documented
visual limitations. The sky-depth feature is likewise used only by regenerated
manifests; it does not silently change the previous maps.

## Native compatibility and remaining visual differences

- Demise's 101 MDL v49 trees are supported: the isolated native CSS build
  11003710 probe confirmed all 101 as live entities with distinct valid model
  indices and authored model bounds/scales (`fixtures/native-css-demise-model-probe.txt`).
  The v49 header/body/mesh fields used by the reader retain their offsets; the
  import no longer assumes the public SDK's version-48 constant excludes them.
- `liquidpack/water/unique/water_tar_beneath.vmf` is referenced by Demise but is
  absent from its packed files and the local CSS assets. The warning is retained;
  no replacement texture was invented.
- Animated material proxies, refraction/water screen-space passes, moving brush
  animation, skeletal animation, entity visibility changes, particles, laser
  beams and map soundscapes are not reproduced. Existing ambient audio is a
  separate game feature.
- Original diffuse lightmaps are retained. Source's directional bumped-lightmap
  basis, blend modulation, SSBump response and dynamic-prop lighting are not exact
  equivalents of the current renderer.

The public [Source SDK studio header](https://github.com/ValveSoftware/source-sdk-2013/blob/master/src/public/studio.h)
documents the model fields and uses model version 48; it does not by itself
establish compatibility with a particular current CSS binary. Import success or
a screenshot does not prove complete visual or movement parity with native CSS.
Aircontrol route and timing

Download this document

# Aircontrol command witness and native comparison

The imported `surf_aircontrol_ksf` main course has a complete automated command witness from the shipped canonical spawn. It is a playability fixture, not a human leaderboard performance. The saved replay advances only through ordinary input commands and passes the server's strict `verifyReplay` checks.

## Sources and independent comparison

The BSP identity is SHA-1 `3c3b754ffb0f02b3d4a1506c8ffebdca5d18e902`. The public CSS 66t forward record is KSF's `replay_css_15_0_540902_1705101548.rec`, captured at [the public replay endpoint](https://ksf.surf/api/replays/replay_css_15_0_540902_1705101548.rec?game=66t). Its SHA-256 is `67d260c28d40c1cef166e9573011e49cf8a8571881f191c266a9d4b272b7b552`; the captured file has 114,448 bytes and 2,756 frames.

`tests/new-map-import.test.ts` reads this external binary directly and checks every next-frame movement through its finish bookmark: **2,534 comparisons pass a fixed 0.002-unit tolerance for both position and velocity**, including more than 100 surf contacts. The test reconstructs input from recorded button bits and the following sample's view angles, using the reviewed 10,000-unit component velocity cap. Contact and duck state are reconstructed because the recording does not include those flags. Each comparison starts from an independent recorded position and velocity; this test alone is not continuous traversal proof.

The separate canonical command replay supplies that continuous traversal proof. It uses the same public command sequence after a legal approach on the start deck, without position correction during the final run.

## Canonical approach and complete run

The recording begins airborne with downward velocity 288, so its first pose cannot be used as a stationary legal spawn and expected to follow the same trajectory. The planner instead waits for the browser spawn to land, walks on the start deck, uses small legal analog taps, and accelerates over three ground commands to join recorded frame 23.

At that join, position is exactly `(-2453.564453125,-9511.0087890625,14336.03125)` and velocity is exactly `(-37.67776870727539,-19.491683959960938,0)`. Both match the independent recording with zero error. The join occurs after 788 normal simulation ticks and before the timer starts. The native sample is an offline planning target; it is never assigned to the final session state.

The saved replay has 3,312 commands and finishes in **35.686688 seconds**. It has no reset, practice travel, or missed checkpoint. During the continuous portion, maximum position difference from the public native trajectory is 0.015163 units and maximum velocity difference is 0.003575 units. These accumulated continuous differences are separate from the stricter isolated next-frame tolerance.

| Event | Recorded frame at local crossing |
| --- | ---: |
| Start | 168 |
| Checkpoint 1 | 619 |
| Checkpoint 2 | 1075 |
| Checkpoint 3 | 1545 |
| Checkpoint 4 | 1903 |
| Checkpoint 5 | 2197 |
| Authored BSP finish | 2547 |

Start and all five checkpoint crossing frames match KSF's bookmarks. The BSP's authored `end_trigger` is reached **13 ticks after** KSF's finish bookmark at frame 2,534, a nominal 0.195 seconds at the recorded tick interval. KSF publishes a 35.488685-second record, whereas this browser fixture measures 35.686688 seconds; fractional timer placement also affects the difference. Private KSF finish-zone bounds are unavailable. The local finish is deliberately tied to the authored BSP trigger, and exact KSF timer parity is not claimed.

## Reproduction

Run `node --import tsx scripts/plan-aircontrol-witness.ts` to regenerate the command replay and report. Run `node --import tsx --test tests/new-map-import.test.ts` for the independent binary comparison and geometry/contact fixtures. Run `node --import tsx scripts/validate-new-maps.ts aircontrol` for full canonical replay verification and render-schedule determinism.

Artifacts:

- `public/replays/aircontrol-complete.json`: canonical initial state plus ordinary commands.
- `fixtures/aircontrol-canonical-witness-report.json`: exact join, source and geometry identity, route events, continuous deviations and server verification result.
- `fixtures/aircontrol-native.rec`: independent external native record.
- `fixtures/aircontrol-ksf-telemetry.json`: decoded source record for offline route planning.

The planner never changes the map geometry, movement rules, spawn, or server verifier to make the run succeed.
Performance measurements

Download this document

# Rendering performance

The renderer keeps the imported geometry, materials, baked lighting and direct camera response. Movement code and simulation rate were not changed by these renderer optimizations.

## Measurement

`scripts/profile-renderer.ts` replays the saved Boreas command fixture in the headless simulation and extracts 690 camera poses across the complete route. An isolated browser scene renders the same poses twice, after a warm-up covering the route. This measures rendering separately from input, simulation and HUD costs. It does not inject gameplay state or establish additional physics fidelity.

The measurements below used headless Microsoft Edge on the local RTX 3090 through ANGLE/D3D11, a 1600 × 900 CSS-pixel viewport, 1,380 measured frames and approximately 346 GPU timer queries. CPU time surrounds renderer submission; GPU time uses `EXT_disjoint_timer_query_webgl2`, excluding disjoint samples. Browser frame intervals were around 2.8–2.9 ms in both versions, so these results establish lower rendering cost, not a measured increase in displayed FPS or lower input-to-photon latency.

| Condition | CPU mean / p95 | GPU mean / p95 | Mean draws | Mean submitted triangles |
| --- | --- | --- | --- | --- |
| Original, DPR 1 | 0.497 / 1.000 ms | 0.538 / 1.223 ms | 221.3 | 887,359 |
| Optimized, DPR 1 native | 0.264 / 0.500 ms | 0.495 / 0.869 ms | 70.8 | 821,283 |
| Original, DPR 2 native | 0.492 / 1.000 ms | 1.249 / 1.993 ms | 221.3 | 887,359 |
| Optimized, DPR 2 native | 0.243 / 0.400 ms | 0.842 / 1.330 ms | 70.8 | 821,283 |
| Optimized, DPR 2 balanced | 0.248 / 0.500 ms | 0.536 / 1.005 ms | 70.8 | 821,283 |

The live geometry count fell from 755 to 159 for this route; texture and shader program counts stayed at 60 and 21. Renderer construction took 25–31 ms in the final runs, versus 57–58 ms before. Construction excludes first-use shader compilation and texture upload. Map-loading checks separately exercise those cold paths; their large first frame is not a sustained rendering measurement.

Source reports are `fixtures/renderer-profile-before.json`, `renderer-profile-before-hidpi.json`, `renderer-profile-after-branchless.json`, `renderer-profile-after-hidpi-native.json` and `renderer-profile-after-hidpi-balanced.json`. Earlier `after`/`after-repeat` reports retain an investigated GPU regression: a dynamic per-vertex lighting branch was slower. The final branchless lighting selection removed that regression; tolerances or timer results were not adjusted to conceal it.

## Changes and visual preservation

- Opaque prop instances share larger model/material batches, while conservative per-instance frustum spheres exclude wholly invisible instances. Original transforms and per-instance lighting offsets remain intact. A small bounds margin avoids edge clipping.
- Transparent model batches keep their original spatial grouping and ordering. No additional approximation to transparent sorting was introduced.
- Immutable model vertex/index buffers are shared across batches instead of decoded and uploaded repeatedly. Instance matrices and lighting offsets remain independent.
- Static objects no longer recalculate unchanged world matrices. Moving zone labels explicitly update their own matrices. Camera position and current mouse angles still apply every rendered frame, without easing.
- Visibility lists and instance-buffer uploads change only when the camera frustum or visible instance set changes. Model LOD 0, displacement resolution, shaders, texture sampling and source-unit scale remain unchanged.

Three matching route views at DPR 1 have **zero differing pixels** before versus after. At DPR 2, the start and late views are identical; the middle view has 22 differing pixels out of 5,760,000, with maximum channel difference 1/255. These results are recorded in `fixtures/renderer-pixel-comparison.json`; screenshots are under `docs/screenshots/performance/`. This compares this browser renderer before and after optimization, not the browser output against CSS.

## Stable rendering choices

| Setting | Drawing-buffer ratio | Anisotropy |
| --- | --- | --- |
| Native | Device pixel ratio, capped at 2 | Up to 8× |
| Balanced | Device pixel ratio, capped at 1 | Up to 8× |
| Performance | 0.75 × min(device pixel ratio, 1) | Up to 2× |

Balanced is the application's default. On a DPR-2 display it submits one quarter as many pixels as Native, preserving the CSS-pixel viewport and identical camera FOV. On a DPR-1 display Native and Balanced match. Performance deliberately trades sharpness and oblique texture filtering for lower rendering cost. The chosen setting is stable during a run; no automatic resolution adaptation changes the image mid-run. Rendering choices do not change collision, simulation commands, record keys, input sensitivity or timing.

Changing anisotropy can trigger a one-time texture upload while the menu is open. Native-to-Balanced changes retain anisotropy and avoid redundant texture reuploads.

## Generic map and lifecycle checks

`loadBspVisuals(slug)` loads one map; `loadBoreasVisuals()` remains a compatibility alias. Missing sky, fog, prop and lighting metadata have explicit defaults. Image/mesh loading has bounded concurrency. Renderer disposal releases geometry wrappers, materials, textures, cubemaps and marker resources and resets shared WebGL pixel-store flags before context reuse.

`scripts/bsp-loader-qa.ts` cycles Boreas → Utopia → Mesa → an empty map with optional data omitted → Boreas in one canvas at DPR 2. It asserts all three quality sizes and verifies a synthetic half-height sky sampling fixture. `fixtures/bsp-loader-qa.json` records zero console errors or warnings and all size/sampling assertions passing.

Utopia's packed sky has half-height side textures, a 1 × 1 bottom and authored texture scaling. WebGL cubemaps require equally sized square faces. The loader bakes the authored transform into clamped square sky images once, sampling color in linear space; already compatible Boreas and Mesa skies pass through unchanged. This removes invalid cube upload warnings without stretching the authored sky.

The whole-game UI, native controls, audio, map switching and practice checks are in `scripts/browser-classic-qa.ts` / `fixtures/browser-classic-results.json`: **10 checks pass, zero console errors or warnings**. This includes actual Pointer Lock and forward input on all three maps, quality and audio persistence, pause/resume audio lifecycle, all eight lab stations, and two complete map-switch cycles with stable per-map geometry/texture/program counts from matching fresh-spawn views. Live resource counts can identify growth across switches, but do not prove absence of every retained JavaScript or driver allocation. Finish-marker screenshots use separate inspection cameras; they do not establish complete runs on the additional maps.

The final marker-only follow-up (`SURF_QA_MARKERS=1`) uses the actual recorded CSS positions/angles 80, 50 and 30 ticks before each finish and at entry, adding the standing/crouched eye height. Its 15 views cover all map starts and four finish approaches per map. Visual inspection confirmed corrected Mesa start markings on the deck and visible gold finish signs/landing borders on the native approaches. `fixtures/browser-classic-marker-results.json` records each source frame and zero errors/warnings; images are under `docs/screenshots/classics/`. Those cameras are an inspection aid, not command-driven gameplay or an independent physics comparison.

A short active, stationary whole-game sample after native movement and restart used 1280 × 720 CSS pixels, DPR 2 and Balanced quality. Frame interval median/p95 was 2.8/2.9 ms for all three maps. The application's smoothed render-submission average during that sample was 0.501 ms (Boreas), 0.149 ms (Utopia), 0.196 ms (Mesa); corresponding smoothed physics cost averaged 0.020/0.015/0.018 ms per render frame, including frames with no simulation tick. This is a current spawn observation, not a whole-course benchmark or a before/after whole-game comparison.

## Additional-map route views

The same isolated renderer profiler also covers **500 uniformly sampled native CSS camera poses from start to finish on each additional map**, with two measured passes after warm-up. It uses 1600 × 900 pixels, DPR 1, Balanced quality and the same RTX 3090/Edge environment. Camera origins come from recorded positions plus 47/64-unit eye height inferred from the duck button; these views are inspection inputs, not command-driven playback and not playability evidence. The UI, audio and movement simulation do not run in this measurement.

| Map | CPU mean / p95 | GPU mean / p95 | Draws mean / p95 | Triangles mean / p95 |
| --- | --- | --- | --- | --- |
| Utopia | 0.080 / 0.200 ms | 0.104 / 0.146 ms | 12.97 / 15 | 59,001 / 59,299 |
| Mesa | 0.121 / 0.200 ms | 0.224 / 0.364 ms | 36.66 / 60 | 279,736 / 413,858 |

Each report includes 1,000 CPU/draw samples and 250 valid GPU queries, zero console errors or warnings, and start/middle/late screenshots. Utopia samples native frames 310–3868; Mesa samples 144–3395. These measurements do not identify a sustained rendering bottleneck in the middle or final sections on this GPU. They do not establish FPS on other hardware. Frame intervals remained approximately 2.8–2.9 ms and are not used to claim an FPS improvement.

The saved reports are `fixtures/renderer-profile-utopia-balanced.json` and `fixtures/renderer-profile-mesa-balanced.json`. Utopia's one-time renderer creation was 332 ms, primarily while normalizing its authored non-square sky faces; Mesa's was 28 ms. This occurs during map loading, outside simulation. No additional runtime change was made for this acceptable one-time cost.

To reproduce, set `SURF_PROFILE_QUALITY=balanced` and run `node --import tsx scripts/profile-renderer.ts utopia-balanced --slug utopia` (or substitute `mesa` in both places).

Exact Source PVS and GPU occlusion, model LOD selection, displacement tessellation, and independent CSS visual parity remain outside this renderer's evidence.

With the development server running, reproduce the renderer profile with `node --import tsx scripts/profile-renderer.ts current`. Set `SURF_PROFILE_DPR=2` and `SURF_PROFILE_QUALITY=balanced` for the HiDPI balanced comparison. Production builds are not required by these isolated development checks.
Utopia & Mesa provenance

Download this document

# Classic CSS map imports

The added courses are the actual **surf_utopia_njv** by **Panzer** and **surf_mesa_fixed** by **Arblarg**. KSF lists both as tier-one linear CSS courses with three checkpoints. Utopia was added in 2012 and Mesa in 2014. These are the CSS versions, not similarly named CS:GO, CS2, or TF2 ports. [KSF Utopia](https://ksf.surf/maps/surf_utopia_njv?game=66t&mode=fw), [KSF Mesa](https://ksf.surf/maps/surf_mesa_fixed?game=66t&mode=fw).

## Provenance and reproducibility

The public [KSF/OuiSURF mirror index](https://main.fastdl.me/maps_ksfthings.html) identifies the exact files. [OuiSURF's collection](https://github.com/OuiSURF/Surf_Maps) describes its collection as maps from KSF CSS servers. A matching filename alone is not proof of the exact live server revision; the independently recorded movement comparisons provide a second check.

| Map | SHA-1 of decompressed BSP | Bytes | VBSP / revision |
|---|---|---:|---|
| [Utopia download](https://main.fastdl.me/h2/50b99557a4b754696ce16e9e920cb1b6072dd4bd/surf_utopia_njv.bsp.bz2) | `50b99557a4b754696ce16e9e920cb1b6072dd4bd` | 56,273,716 | 20 / 3009 |
| [Mesa download](https://main.fastdl.me/h2/f4897c2472b32b33bc22b072ffafef5caa0de8fb/surf_mesa_fixed.bsp.bz2) | `f4897c2472b32b33bc22b072ffafef5caa0de8fb` | 62,212,961 | 20 / 90 |

The offline converters are independently authored from published BSP, VPK, MDL/VVD/VTX, VTF, and PHY layouts. They preserve the original map authorship. The map downloads do not establish a general redistribution license, and no such license is claimed here. Imported art remains the property of its respective authors. The requested local imports also read a small number of stock materials/models from the user's installed Counter-Strike: Source archives; installed game files are never changed. Each export records these fallback resource paths in `localGameResources`.

With Python and Pillow installed, decompress the pinned downloads and run from the project directory (replace paths with the local BSP and CSS installation):

```powershell
python scripts/import_classic_maps.py utopia C:/maps/surf_utopia_njv.bsp --game-dir 'C:/Program Files (x86)/Steam/steamapps/common/Counter-Strike Source'
python scripts/import_boreas_visuals.py C:/maps/surf_utopia_njv.bsp --slug utopia --map-id surf_utopia_njv --author Panzer --game-dir 'C:/Program Files (x86)/Steam/steamapps/common/Counter-Strike Source'
python scripts/import_classic_maps.py mesa C:/maps/surf_mesa_fixed.bsp --game-dir 'C:/Program Files (x86)/Steam/steamapps/common/Counter-Strike Source'
python scripts/import_boreas_visuals.py C:/maps/surf_mesa_fixed.bsp --slug mesa --map-id surf_mesa_fixed --author Arblarg --game-dir 'C:/Program Files (x86)/Steam/steamapps/common/Counter-Strike Source'
npx tsx --test tests/classic-map.test.ts
```

Collision outputs are `public/maps/{utopia,mesa}-collision.json`; visual manifests are `public/maps/{utopia,mesa}-visuals.json` with textures and binary meshes in their respective subdirectories. The Boreas default importer profile and existing Boreas outputs remain unchanged.

## Geometry and route rules

All geometry stays in Source X/Y horizontal, Z up, feet-origin units. Renderer conversion remains separate. Convex BSP brush planes include compiled bevels; static solid props use their actual PHY convexes. Mesa displacements retain full-resolution triangles and honor the Source hull-exclusion/remove flags. Rendering uses the same BSP revision, including original textures, authored blends, baked lightmaps, static prop vertex lighting, and sky transforms.

| Imported content | Utopia | Mesa |
|---|---:|---:|
| Collision convexes | 3,633 | 5,002 |
| Displacement triangles | 0 | 183,040 |
| Static props rendered | 0 | 234 |
| Static props with PHY collision | 0 | 230 |
| Authored reset trigger models | 75 | 9 |
| Local ordered checkpoints | 3 | 3 |

Utopia's authored reset destination is `start`, feet origin `(-14096,0,12816)`, yaw 0. The supporting deck traces to `Z=12800.03125`, also the independent native record's standing height. Mesa's destination is `Spawn`, `(0,-800,10251)`, yaw 90; its deck traces to `Z=10144.03125`, also confirmed independently. Utopia has one additional teleport aimed at nonexistent `jail_top`; Valve's teleport implementation does nothing when a destination is missing, so it is recorded as metadata rather than an active reset.

There are no authored gravity or air-acceleration overrides in either BSP. Both maps use the established CSS 0.015-second simulation profile. Their independent KSF recordings show a **3500 u/s component limit**, exported as `physicsOverrides.maxVelocity=3500`. This is an inference from the records, not an authored BSP cvar or a claim about every historical KSF server. Horizontal speed can exceed 3500 when both horizontal components contribute.

Local start/checkpoint/finish boxes are defined in `scripts/import_classic_maps.py`, aligned to the actual route apertures and independent native bookmark positions. Their exact bounds are **not claimed to be KSF's server-side timing zones**, which are absent from BSP files. The first and third Utopia checkpoints and first two Mesa checkpoints are unions of mirrored boxes so both intended branches count without filling the empty space between them. PBs are local and versioned by map and physics configuration.

Mesa's authored continuous `trigger_push` is preserved: bounds `(-384,-4352,-12288)` to `(384,-3328,-11808)`, direction +Y, speed 3500, clients flag 1. The native run independently shows its displacement contribution without the same addition to stored velocity. See [MAP-PUSH.md](MAP-PUSH.md) for the Source base-velocity semantics and validation.

## Import checks and precise limits

`tests/classic-map.test.ts` checks pinned identity, native standing heights, finite/unit collision planes, convex bounds, mirrored checkpoint unions, local ordered timing crossings, the Mesa push, and existence of every visual resource. Its native-position timing test assesses zone placement only; **a sequence of injected positions does not prove surfability**. Separate complete command replay tests establish the playable routes. The native telemetry files include public source URL, hash, bookmarks, and the decoded tick samples. `scripts/decode-classic-replays.ts` reproduces decoding.

## Complete normal-command routes

Both imported courses have complete witnesses from a legal stationary start. The shipped Mesa route and a separately planned alternative both retain every authored reset volume. All runs use the normal default browser profile, including the documented 3500 component limit; no practice restores, position or velocity injection, noclip, altered geometry, or reset exemptions occur during these runs.

| Course / witness | Commands | Local time | Ordered events |
|---|---:|---:|---|
| Utopia, `public/replays/utopia-complete.json` | 3,867 | 53.349 s | Start, CP1, CP2, CP3, finish |
| Mesa, selected `fixtures/mesa-straight-complete.json` | 3,653 | 52.635 s | Start, CP1, CP2, CP3, finish |
| Mesa, alternative `fixtures/mesa-launch-cem-complete.json` | 3,598 | 51.810 s | Start, CP1, CP2, CP3, finish |

The release publisher copies the selected Mesa witness to `public/replays/mesa-complete.json`. `scripts/validate-classics.ts` reconstructs each initial state from a stationary start-deck spawn, accepts only normal command fields and magnitudes, then independently runs the entire course through `GameSession`. It requires all checkpoints in order, no falls or invalidation, and a finish event on the final command. Every tick's state, collision contacts, events, splits, and time agree exactly at 30, 60, 144, and 240 render FPS. The separate Mesa reports are `fixtures/mesa-straight-command-validation.json` and `fixtures/mesa-alternate-command-validation.json`; the release validation report identifies the canonical replay by SHA-256.

The offline planners select ordinary mouse angles and key presses. They can cache a prefix already produced by normal simulation while searching, but every completed witness is rerun from its original stationary start before acceptance. Utopia's line uses small view-angle corrections to clear its authored reset edges. The Mesa alternatives use different normal turns and descents, then surf the original lower ramps and pass through the original continuous push trigger. No such planner runs in the playable game. These witnesses establish whole-course playability and deterministic replay in this implementation; they do not establish exact parity with a private KSF server or make the local timer boundaries into KSF boundaries.

## Remaining native and entity differences

The following differences require explicit separation from verified movement:

- The native KSF recordings cross a few authored reset-volume edges without resetting. This occurs in both recent and older independent runs. Latest Utopia frames 797–801 overlap trigger models `*24`/`*86`; latest Mesa frames 401–402 overlap `*1`, 2520–2526 overlap `*1`/`*10`, and 2773–2775 overlap `*8`. Exact model ownership, transforms, solid contents, and entity flags were checked. The corresponding PHY model convex counts match the compiled brushes and their surfaces differ by roughly the ordinary half-unit inset. **A direct isolated CSS server test corroborated the imported reset shapes:** stock build 11003710 reset a crouched bot at the tested Utopia/Mesa positions and measured the same 0–45 crouched hull. See [NATIVE-CSS-PROBE.md](NATIVE-CSS-PROBE.md). The KSF server/revision difference remains unresolved. The browser retains the complete authored reset volumes and full player hull; completed normal-command witnesses are evaluated separately from KSF's recorded line.
- Mesa isolated native states 521–523 are about 0.05 units inside playerclip brush 2281 according to the downloaded compiled planes. Side 10 uses original plane 55800: normal `(0.7808678150,0.4417394698,0.4417144656)`, distance `5514.1142578125`. A one-step trace from that supplied state corrects by about 0.084 units. This is retained as a precision/revision gap; continuous traversal is a separate test.
- Four Mesa static prop instances lack a PHY resource even after mounting the installed game: two credits models and two crystal models. Their render meshes remain visible; no invented collision mesh is substituted.
- Mesa's four decorative crystal `func_tanktrain` brush models remain at their authored initial transforms. Full train animation and the late decorative dynamic blast door are not simulated. Soundscapes, particles, and multiplayer/gameplay entities are not recreated. The main surf route and completion point precede the blast door.
- Utopia uses half-height sky side images and a 1×1 bottom texture with authored VMT transforms. The renderer applies these transforms while preparing equally sized cube faces; direct upload as a conventional cubemap would be invalid.

Primary trigger references: [Valve CTriggerTeleport and CBaseTrigger](https://github.com/ValveSoftware/source-sdk-2013/blob/master/src/game/server/triggers.cpp), [Valve PhysicsTouchTriggers](https://github.com/ValveSoftware/source-sdk-2013/blob/master/src/game/server/baseentity.cpp), [Valve collision property](https://github.com/ValveSoftware/source-sdk-2013/blob/master/src/game/shared/collisionproperty.cpp). These shared SDK sources guide investigation; they do not prove CSS-specific engine internals or KSF's private server configuration.
Movement practice facility

Download this document

# Movement laboratory

The rebuilt `lab-chamber-2` is an original, practice-only Source-style chamber. `src/map/lab.ts` owns both collision/render brush data and eight named stationary starts; the original `course.ts` test map remains unchanged for earlier regression fixtures. Gravity, acceleration, hulls, jump impulse and movement code are unchanged.

The two long orange/blue faces share the proven opening-transfer dimensions from Northline, translated vertically. The run-up starts at rest. Orange uses A; the transfer changes to D on blue. A separate right-ramp platform permits practicing that face without completing the first. Missing a ramp lands on the recovery floor. Restart returns to the run-up, and the station selector provides explicit practice repositioning with no momentum injection.

Ground stations include a marked acceleration strip, 45/56-unit jump blocks, six 18-unit steps, a 49-unit crouch tunnel, two coplanar solid brushes and a one-unit wall. All starts have clear standing hulls and real ground support. Concrete grids use a 128-unit scale; signs, floor arrows, distinct ramp colors, a roof and fluorescent strips replace the former empty mountain backdrop. Static brush meshes are batched by material.

Validation: `npx tsx --test tests/lab.test.ts` passes seven tests. Normal WASD/view commands traverse both faces and their transfer in 799 ticks, with 232 orange-face and 137 blue-face contacts and peak horizontal speed 1735.13 u/s. The independent right entry produces 193 surf contacts from a stationary walking start. Ordinary inputs clear both jump blocks, walk all steps, crouch through the tunnel and stand only after clearing its ceiling. Swept hull tests verify the seam and thin wall.

`npx tsx scripts/lab-visual-qa.ts` renders the actual map in Edge and saves five views under `docs/screenshots/lab-chamber-*.png`, including a state reached through normal route commands. The inspected views have 11–18 draw calls and about 2070 triangles, with no browser errors. These screenshots validate appearance and rendering; the separate command tests establish surfability. The browser fixture is isolated from the main menu; integrated station UI validation belongs to the main application QA.
Authored push volumes

Download this document

# Authored Source push volumes

Mesa contains a continuous `trigger_push`, flags 1, speed 3500, direction +Y, bounds (-384,-4352,-12288) to (384,-3328,-11808). Its compiled brush hull is imported with the map. This is authored map behavior, not a surf assist or a velocity boost added to compensate for geometry.

Valve's [trigger implementation](https://github.com/ValveSoftware/source-sdk-2013/blob/b8cfb12c0e083a2ef5b2f9f9b50f3902fa034474/src/game/server/triggers.cpp) sets base velocity during continuous touching. Overlapping continuous pushes add. Positive vertical pushes release ground and raise the origin one unit. Flags 1 allows players; flags 128 is a different, one-shot mode. The present helper supports continuous player pushes; it does not emulate one-shot entity deletion or arbitrary entity I/O.

The server's [CheckMovingGround](https://github.com/ValveSoftware/source-sdk-2013/blob/b8cfb12c0e083a2ef5b2f9f9b50f3902fa034474/src/game/server/player_command.cpp) converts unrefreshed base velocity into player momentum before movement, using `previousBase * (1 + tickInterval/2)`, then clears it. In [shared player movement](https://github.com/ValveSoftware/source-sdk-2013/blob/b8cfb12c0e083a2ef5b2f9f9b50f3902fa034474/src/game/shared/gamemovement.cpp), StartGravity consumes vertical base velocity as a force. AirMove adds horizontal base velocity after air acceleration, sweeps collisions with the combined velocity, and subtracts it before categorization/final gravity. The non-player `PhysicsAddGravityMove` path is not a replacement for these player rules.

`prepareMapPush` returns optional base velocity and retains the previous horizontal component in optional player state. Save/restore and replay initial states consequently retain exit momentum correctly. Maps without pushes leave that field absent and use exactly the previous movement path. The movement core receives an optional third argument; it neither changes the authoritative tick interval nor runs extra acceleration ticks.

Independent evidence comes from the [public native KSF Mesa replay](https://ksf.surf/replays/surf_mesa_fixed/replay_css_1777_0_576582_1775395546.rec), decoded in `fixtures/mesa-ksf-telemetry.json`. At tick 3058 the player travels about 104.334 Y units, consistent with `(storedVelocityY + 3500)*0.015`; stored player velocity remains about 3455.614, so directly adding 3500 to the stored velocity is wrong. Push displacement continues through 3067. On exit 3068, inherited momentum is limited before acceleration. Subsequent stored Y velocity clamps to 3500. That cap is an observed Mesa/reference-map setting; Boreas's 5000 profile remains unchanged.

`tests/map-push.test.ts` passes seven tests, including independent one-step replay comparisons for every state 3055→3070 against actual Mesa geometry. Predeclared tolerances remain .002 Source units and .002 u/s: maximum position error .001137880, maximum velocity error .000222171, first out-of-tolerance tick none. Analytical fixtures additionally verify acceleration order, wall sweeps, vertical force/ground release, one-time exit momentum and absent-map state identity. These comparisons establish the tested push transition; native contact flags, the complete entity touch queue, arbitrary filters, moving conveyors and skipped-over thin push volumes remain outside this implementation's verified scope.

After adding the optional movement argument, all 27 focused Boreas/playability/revision/laboratory checks pass, including both full-course command witnesses and identical 30/60/144/240 FPS states. No source implementation was copied into runtime code.
Mesa complete command route

Download this document

# Mesa complete command witness

`fixtures/mesa-straight-complete.json` is a complete run using the current default surf profile and Mesa's documented map overrides. It starts stationary and grounded in the real start zone. The final verification reconstructs this state from the spawn coordinates; it does not inject velocity, contact state, or intermediate positions.

The unchanged `GameSession` processes all commands, movement, authored push volumes, reset volumes, and timing triggers. The run fires only these events:

| Event | Command tick |
| --- | ---: |
| Start | 144 |
| Checkpoint 1 | 1435 |
| Checkpoint 2 | 2072 |
| Checkpoint 3 | 2519 |
| Finish | 3653 |

The simulated time after start-zone exit is **52.63500759744885 seconds**. There are **1,302 airborne ramp-contact ticks**. Peak horizontal speed is **3,570.9044372625117 units/s**; Mesa's 3,500 limit applies per component, so this vector magnitude is permitted.

`scripts/validate-classics.ts` independently checks the legal initial state, complete ordered trigger sequence, normal input fields and magnitudes, absence of resets or assists, and final completion. It then replays all 3,653 commands at 30, 60, 144, and 240 FPS. Every tick's serialized player state, movement result, contacts, events, and timer agrees exactly across those schedules. No tolerance is applied to schedule comparison. The detailed result is `fixtures/mesa-straight-command-validation.json`.

Replay SHA-256: `a10daf80b98a3c263986a5ec952edd9125bd97e8056f9851797d04053ff7284b`.

## How the commands were developed

The route starts with the independently prepared legal command prefix in `fixtures/mesa-clear-ceiling-v1.json`. `scripts/plan-mesa-edge.ts` explored wider ordinary strafe lines around the last reset brush. Its preserved result is `fixtures/mesa-late-straight-v1.json`. `scripts/plan-mesa-straight-descent.ts` then generated ordinary view-angle and strafe commands from tick 2840 through the descending section, bottom push, and final ramp.

The successful final ramp line aims near X=100 on the positive-X ramp face, then holds the strafe key into the ramp at yaw 96 degrees from Y=3000 to Y=4500. This converts the permitted existing momentum into a higher departure through normal collision clipping. No ramp geometry, reset bounds, movement constants, acceleration, or velocity was changed to make the route work.

The offline search reads position to choose candidate inputs; its output contains only tick-indexed keyboard inputs and view angles. This planner is not part of browser gameplay. The saved complete replay is subsequently simulated again from the legal stationary start without the planner, state restores, or position/velocity edits.

## Scope

This proves whole-course playability with normal commands in this implementation and independence from rendering frequency. It does not prove exact CSS/KSF parity or that a human has played this route. Stock CSS native trigger tests and public KSF recordings differ at several authored reset contacts; the game preserves the imported map's authored reset geometry, and this route avoids those volumes. See `NATIVE-CSS-PROBE.md` and the separate movement comparison reports for the evidence and remaining fidelity gaps.
Native CSS engine investigation

Download this document

# Native CSS trigger probe — 2 October 2026

An isolated, local Counter-Strike: Source dedicated server was used to investigate the remaining reset-trigger disagreement with public KSF replays. This is a direct, limited native-engine test. It is **not** a claim that the browser reproduces a complete native run exactly.

## Environment and isolation

- Installed executable: `C:/Program Files (x86)/Steam/steamapps/common/Counter-Strike Source/srcds_win64.exe`.
- Native `version`: build/server/network patch **11003710**, protocol **24**, server AppID **232330**.
- Startup reported `SV_ActivateServer: setting tickrate to 66.7`; this display is rounded and does not replace the independently established 0.015-second simulation interval.
- Test mod and writable files: `node_modules/.native-css-probe/cstrike` inside this project. Installed binaries and asset packs were mounted as read-only search paths. No installed configuration or map files were changed.
- Server used `-insecure -ip 127.0.0.1 -port 27025`, a local password, hidden window and no human client. Bots were used. There were no SourceMod, Metamod, KSF or other movement plugins.
- The probe loaded the same downloaded Mesa/Utopia BSP files used by the browser importer. Their provenance and hashes remain in the map import metadata.
- The server was stopped with its `quit` command after testing. The process was confirmed absent.

## Measured bounds

Built-in VScript `GetBoundingMins()` and `GetBoundingMaxs()` returned:

| State | Minimum, relative to feet | Maximum, relative to feet |
| --- | --- | --- |
| Standing | `(-16, -16, 0)` | `(16, 16, 62)` |
| Crouched | `(-16, -16, 0)` | `(16, 16, 45)` |

Crouched `GetCenter()` was feet plus 22.5 units vertically. The observed entity bounds do **not** support raising the crouched trigger hull's bottom by 9, 31 or 32 units. Those candidate changes were rejected.

## Native trigger outcomes

The bot had the measured 45-unit crouched bounds at each recorded test. `Teleport()` placed it at the specified origin with supplied velocity, without tracing a movement path from spawn. The script printed the immediate origin to confirm the placement; a following RCON read inspected the subsequent native result.

| Probe | Position supplied | Result |
| --- | --- | --- |
| Mesa recorded frame 401 | `(159.6418, 3327.4126, 9528.7471)` | Native authored reset returned the bot near `(0, -800, 102xx)` |
| Mesa approach from frame 400 | `(167.6383, 3314.57495, 9536.78809)`, velocity `(-566.536, 833.601, -530.092)` | Native authored reset returned the bot near spawn |
| Mesa recorded frame 2523 | `(477.62857, 6157.87744, 361.0672)` | Native authored reset returned the bot near spawn |
| Utopia recorded frame 798 | `(-1064.533813, -457.993744, 9986.02832)` | Native authored reset returned the bot to `(-13368.400391, -26.910000, 12795.299805)` |

The Mesa trigger output was independently logged as model `*1`. The position in that output is already the post-teleport position, so it is not used as pre-contact telemetry. Exact captured outputs are in `fixtures/native-css-mesa-trigger-probe.txt`, `fixtures/native-css-utopia-trigger-probe.txt` and `fixtures/native-css-trigger-events.txt`.

The reset convexes were separately checked using BSP model-to-brush membership and the BSP's decoded VPhysics model data. Their native-recorded points genuinely penetrate the trigger shapes; this is not just an AABB corner false positive. For example, Utopia frame 798 is approximately 19.45 units inside the nearest VPhysics plane. All original reset volumes therefore remain present. The browser does not shrink their hulls, remove selected resets or exempt replay positions to force a passing record.

## Interpretation and limits

The downloaded BSPs, stock installed CSS and the public KSF recordings disagree at these particular reset contacts. The direct native tests support retaining the authored reset geometry and the measured full crouched hull. They do not identify the cause of the KSF disagreement. A server-side map revision, entity modification or plugin behavior remains possible; no public KSF configuration establishing a specific explanation was found.

These are short contact probes, not a whole-route command replay in the native server. Repositioning can affect native contact state, and the subsequent inspection occurs several native ticks later. We also have not established that installed build 11003710 is exactly the build on which the KSF records were made. Those limitations prevent claiming native whole-route parity or declaring a specific KSF fix as fact.

## Reproduction

Use a locally installed, licensed CSS copy. Make a separate `cstrike` directory under the project; do not modify the installation. Its `gameinfo.txt` needs the installed `cstrike/bin` as `gamebin`, the installed CSS VPK as `game+mod`, and the installed HL2 packs as `game`. Put `mod+mod_write+default_write_path` and `game+game_write` on the local probe directory. Using only `game` for the CSS assets is insufficient: encrypted weapon data is read through the `MOD` search path. Add `cfg/valve.rc` containing `stuffcmds`, and local configuration with `sv_lan 1`, `sv_cheats 1`, a test RCON password and `bot_join_after_player 0`.

Launch the installed dedicated executable hidden with the isolated directory as `-game`, `-insecure -ip 127.0.0.1 -port 27025 +exec probe.cfg +map surf_mesa_fixed`. Add two bots, use `bot_stop 1` and `bot_crouch 1`, then run built-in VScript through local RCON:

```squirrel
p <- Entities.FindByClassname(null, "player");
printl(p.GetBoundingMins());
printl(p.GetBoundingMaxs());
p.Teleport(true, Vector(167.6383, 3314.57495, 9536.78809),
           false, QAngle(0, 0, 0),
           true, Vector(-566.536, 833.601, -530.092));
printl(p.GetOrigin());
```

Read `p.GetOrigin()` again after native movement has run. Put multiple Squirrel statements in a `.nut` file and invoke `script_execute`; unquoted console semicolons separate console commands rather than Squirrel statements. Repeat after a `changelevel surf_utopia_njv`, recreating the bots if needed. Shut down the isolated server when finished.
Independent release review

Download this document

# Independent integration review — 2 October 2026

Reviewed movement/base velocity, convex and displacement collision, BVH ordering, trigger/timer integration, fixed-step scheduling, mouse/keyboard/wheel input, map switching, replay ownership, local records and procedural audio against the current source files.

## Actionable findings resolved

1. **Replay rules persisted after Escape.** Watching a route creates a session with the replay's rules. Previously Escape cleared the playback flag before Restart could identify that session, so normal play could keep different auto-bhop/start rules from the options UI. A separate replay-session flag now survives stopping playback. Restart or Join recreates a normal session from current preferences and refreshes the matching personal best.
2. **Stale animation-frame timestamps stopped playback immediately.** A click/lock handler can reset the wall-clock baseline slightly after the timestamp attached to a queued animation frame. Passing the resulting negative delta into the fixed clock stopped the game at tick zero. The browser loop now treats that stale interval as zero without moving its baseline backward. The fixed clock still rejects genuinely invalid/long deltas; physics interval and stall protections were not changed.

Both issues were investigated in real Edge. The immediate-pause failure reproduced twice with the page focused and visible and no console errors. After the fixes, all three focused browser checks pass: initial manual-profile selection, Watch → Escape → Restart, and Watch → Escape → Join with real Pointer Lock. Checks verify exact movement configuration, practice state, fresh initial tick, matching record namespace and preserved preference values. Two synthetic PB entries exist only in an isolated browser context to test record selection; these are not gameplay records or completion evidence.

Reproduce with `npx tsx scripts/browser-replay-state-qa.ts` against a local preview; use `SURF_QA_URL` to select its address. Captured result: `fixtures/browser-replay-state-results.json`.

## Focused validation

**61/61 automated tests passed**, none skipped, across input, game/timer/practice, authored push, storage, experience settings and collision review fixtures. The command was:

```text
npx tsx --test tests/input.test.ts tests/map-push.test.ts tests/game.test.ts tests/review.test.ts tests/storage.test.ts tests/experience.test.ts
```

This includes identical tick results and timing at 30/60/144/240 FPS; wheel pulses unaffected by HUD reads; raw-input fallback and focus cleanup; practice restore and record separation; ceiling, crouch, step and corner contacts; and Mesa's authored push entry/exit compared to 15 independently recorded native transitions. Push comparison maxima remain .001138 units position and .000223 u/s velocity, below the unchanged .002/.002 limits.

The rendering optimization leaves acceleration and gravity on the authoritative fixed interval. Camera yaw/pitch use immediate mouse-event angles on every active render frame, while HUD reads are throttled and non-consuming. BVH candidates retain original order before narrowphase. Optional base velocity remains absent on ordinary maps; its push-specific state survives practice/replay cloning without changing Boreas state fields.

## Remaining limits

- The exact KSF executable/plugin versions remain unknown. Public recordings lack native contact flags and complete command/hull-transition metadata. Float/trace residuals remain quantified in the main validation report.
- The new installed-CSS probes verify stock standing/crouched bounds and specific reset contacts, not a complete live native command replay. Installed build 11003710 may differ from the recording server. See `NATIVE-CSS-PROBE.md`.
- Mesa and Utopia public record paths intersect resets in the downloaded BSPs; stock installed CSS also resets at tested disputed points. The project retains those authored triggers. A legal complete command witness for each shipped course is a separate acceptance check and is not established by this integration review alone.
- Map-effect touch/event timing is sampled at tick boundaries. Boreas's tiny speed-modifier entry discrepancy remains documented; the browser is not a complete Source entity-I/O system. Continuous player push is implemented for the actual Mesa zone. One-shot push, arbitrary trigger filters, moving parents and other general-purpose Source entity behavior are outside the current map contract.
- Mouse count equivalence depends on browser/device delivery. Fractional initial/released keyboard command magnitudes, complete duck-spam behavior, native prediction/step camera and repeated-jump stamina remain explicit fidelity gaps. No experienced human CSS comparison is claimed.

No additional current-map blocker was found in the reviewed base-velocity path, collision broadphase, input sampling, map/resource switching or audio lifecycle. This finding does not replace the separate whole-course witnesses and final production-browser checks.
Online architecture and deployment

Download this document

# Online records and deployment

Movement remains local at the same fixed Source interval. Accounts and ranked
records are optional: when online preparation is unavailable, players can explicitly
choose local play. New ranked runs wait at spawn until online saving is ready.

## Architecture

- Vercel CDN serves the Vite build, visuals and immutable gzip collision packs.
  Compression changes transfer size, never geometry coordinates or physics.
- Supabase Auth handles Google/X OAuth with PKCE. Only the project URL and
  publishable key reach the browser. The Node API authorizes private requests
  using Auth `getUser`, never by trusting a decoded client token.
- Vercel Node functions run in Frankfurt beside Supabase. `api/surf.ts` handles
  profiles, leaderboards, attempt tickets and submissions. Private replay uploads
  go directly to Storage, avoiding function request-body limits.
- The durable `surf-verify` queue invokes `api/verify.ts`, which re-simulates
  commands against pinned server geometry and movement settings.
- Postgres transactions enforce quotas, worker leases, atomic PB replacement,
  duplicate prevention and deletion. Surf tables have RLS enabled with no browser
  grants or policies. Only the trusted service role accesses them. Supabase's
  informational [RLS-without-policy notice](https://supabase.com/docs/guides/database/database-linter?lint=0008_rls_enabled_no_policy)
  is intentional here; adding a permissive client policy would weaken this design.

The server derives time and ordered splits from fractional simulation events,
stored as integer microseconds. It requires the canonical normal spawn, no inherited
momentum, exact configuration, bounded commands, every checkpoint and a finish
without resetting. Browser-supplied elapsed times are never accepted.

Practice, restores, demonstrations, hidden tabs or frame stalls during a timed run, and changed rules
are excluded in the UI. Server validation independently checks the complete run.
This is **server-validated movement**, not proof of human input: valid command
sequences can be synthesized. Exact duplicate digests are rejected, but this is
not comprehensive anti-cheat. Keep the public leaderboard labelled beta.

## Version identity

Visible in-game menus keep simulation, timing and command recording running.
Pointer release clears controls but does not invalidate the run or its attempt.
Recapture preserves the fixed-clock remainder and elapsed time. FOV, sensitivity
and binding changes remain ordinary presentation/input operations. Hidden tabs
and frame gaps over 250 ms still interrupt ranked runs. Menu finishes are queued
once without dismissing the open panel; stage recovery uses the same simulation.
No physics source, board identity, verifier, replay format or local PB key changes.
The 40,000-command recording limit includes time spent in the start area or menus;
the HUD/menu reports when a restart is needed instead of silently trimming it.

The October 9 ranked-saving fix changes client preparation and feedback only;
it does not change physics, timer rules, board IDs or server verification. An unused
attempt survives canonical restarts/full falls for the same account, map and rules.
Restart replaces tickets nearing expiry with enough lifetime for a bounded replay.
The first simulation command waits for preparation; a late ticket still cannot
retroactively qualify a run. Stage returns keep the original recording and timer.
Opening a menu before the timer starts, or loading a nonexistent practice position,
does not discard a prepared attempt. HUD and finish messages distinguish ranked,
waiting, local-only, queued and rejected runs. Completed uploads still use the
existing durable queue and survive subsequent restarts.

Regression coverage: `tests/ranked-saving.test.ts` and
`tests/ranked-saving.browser.ts`, including real Kitsune geometry with two stage-9
falls, Mesa restart/fall recovery, delayed preparation, outages, and upload bytes
checked by `verifyReplayBytes`. Browser APIs/rooms are isolated fixtures; these
checks create no production records. See `test-results/ranked-saving/` for local
reports. Local validation is not evidence of deployment.

`scripts/build-online.ts` hashes collision JSON bytes and movement/session/timer
sources, normalizing source line endings for consistent Windows/Linux identities.
Board IDs include map version, both SHA-256 hashes, all movement settings, protocol
and canonical-spawn policy. The browser refuses ranking against a different build.
Rendering/audio changes do not create new boards. Verification-rule changes need
deliberate protocol review even when the movement source remains unchanged.

The small generated `src/online/catalog.generated.json` is tracked. The server
packs in `.online-build/` and public packs in `public/packed/` are generated by
`npm run prepare:online`, automatically run before development, tests and builds.
New movement/map identities need new board rows, not changes to an old board.

## Configuration and deployment

The existing Vercel project is `surfd`. Node 22 is pinned by `package.json`.
`vercel.json` specifies Frankfurt, queue delivery, daily maintenance and server
map inclusion. GitHub checks run tests, build and the production dependency audit.

Configure these server variables for each deployment environment:

| Variable | Purpose |
| --- | --- |
| `SUPABASE_URL` | Project HTTPS URL |
| `SUPABASE_PUBLISHABLE_KEY` | Public `sb_publishable_…` key |
| `SUPABASE_SECRET_KEY` | Private `sb_secret_…` key, marked sensitive |
| `SURF_AUTH_PROVIDERS` | Actually enabled providers, e.g. `google,x` |
| `CRON_SECRET` | Random private maintenance secret |

Never prefix private credentials with `VITE_`. `.env.local`, `.vercel/` and test
outputs are ignored. The config endpoint returns only explicitly allowed public
values. Missing cloud settings produce an honest local-only state.

Apply the migrations under `supabase/migrations/` in order. Seed the generated
catalog into `surf_boards`: `boardId` becomes `id`, `id` becomes `map_id`, `version`
becomes `map_version`; include both hashes and config, and set `active=true`.
Deactivate retired boards instead of silently changing their rules.

Google/X client credentials belong in Supabase Auth. Provider callback:
`https://wzyozuhomwufeuqakuai.supabase.co/auth/v1/callback`. Supabase's separate Site
URL/redirect allowlist must allow the actual app origin (and specific previews
used to test login). The browser returns to `/`. Google testing mode requires
listed test users; public login needs the appropriate audience setting.

Deploy and validate a preview before promoting. Deployment protection must allow
the intended audience before describing the beta as public. Release previews
must use the isolated staging Supabase project and staging room Worker for release
tests. Verify both URLs in the hosted config before creating a test account. OAuth
provider configuration is distinct from local browser fixture tests.

## Resource bounds and recovery

Restart tickets expire after 45 minutes and grant no Storage access. An existing
attempt can recover its upload for 24 hours after registration; it keeps its original
owner, board, issue clock and verifier checks. This does not extend the time in which
the browser starts a run, change movement rules, or create replacement attempts.
Only finishing requests signed upload permission. A database reservation covers outstanding tokens
and existing objects. Capacity is capped at **32 MiB per account and 700 MiB globally**,
independently of the Supabase billing plan. Each signed upload reserves its full 1 MiB limit
for **125 minutes**, even if its current object is smaller. With no older stored
replays, this permits up to **32 upload authorizations per 125 minutes**; retained
replay bodies reduce the available allowance. Reauthorizing the same attempt renews
its existing reservation. Restarts consume no storage allowance. Accounting uses
the larger of the live reservation and actual object size for each path, in one
database snapshot; account deletion cannot release still-live token capacity.

Uploads are capped at 1 MiB gzip, 8 MiB after expansion and 40,000 commands (about
ten minutes including preparation). Per-account request quotas allow 1,200 attempt
tickets and 120 submissions per hour. These are beta bounds, not unlimited hosting.

Signed uploads cannot overwrite existing objects. A lost upload response recovers
through idempotent submission; reloading between upload and submission also retries
submission. Database leases and transactional completion tolerate duplicate queue
delivery. Operational errors are retried rather than labelled invalid movement.

Completed replays are kept in IndexedDB independently of map changes. Pending
verification polls start at three seconds, slow to ten seconds after 30 seconds and
30 seconds after two minutes, with positive jitter. Transport errors back off to
60–75 seconds. These schedules are per run, so a new finish does not make older jobs
poll early. A terminal missing/expired attempt stops automatic polling but retains
the replay for download in Account. Removal is explicit and enabled after export;
active queued replays cannot be removed by that control. Six retained copies block
a new ranked start until space is freed. Local play remains available.

## Read efficiency

The public top-50 leaderboard is credential-free and cached at Vercel for 30 seconds.
The player's own best is fetched separately through an authenticated, non-public
endpoint. The client combines concurrent reads and caches successful results for at
most 30 seconds, shortened by the public response's CDN age. The board refreshes
while visible; closed menus and hidden tabs do not poll. A confirmed finish
invalidates the affected board and uses one private fresh read, so the player's new
record does not wait for the public cache. Other players' boards catch up on refresh.
Failures are never cached and account identities never share private cache entries.

Account and room identity reads use `surf_profile_summary`, calculating only the
player's points, title, completions and records. The full Ranks page retains exact
global positions and reuses its map-score calculation. Existing points formulas,
ties, latest-active-board selection and visibility rules are unchanged. Normal
profile refreshes share an in-flight request; each verified result is handled once.

Run `npm run benchmark:online` for a repeatable local PostgreSQL benchmark with
1,200 synthetic players and about 6,000 records. It asserts unchanged standings.
Run `npm run test:online:efficiency` for browser polling, freshness and recovery checks.
These are not production throughput or monthly cost predictions.

Authenticated `/api/maintenance` recovers a bounded batch of stranded submissions,
removes abandoned/rejected replay bodies after signed-token expiry, and removes
non-best replay bodies after seven days. Best bodies remain; verified numeric
history remains after body expiry. Daily Vercel Hobby cron runs in production only.
Previews require manual authenticated maintenance. Daily recovery can take up to
a day; queue delivery and active submission polling normally recover sooner.

Maintenance checks `CRON_SECRET` before any work, with bounded batches and a work
deadline below its function limit. Account deletion hides records, removes replay
bodies, revokes sessions and deletes the Auth user. Outstanding token reservations
outlive deletion until expiry to prevent a storage quota bypass.

Watch database size, Storage use, egress, verifier errors and queue age. Upload
allowances do not cap every infrastructure bill or prevent every denial of service.
When capacity is exhausted, ranked uploads fail gracefully and local play continues.

`supabase/diagnostics/online-health.sql` provides read-only operator snapshots for
queue age, expired verification leases, quota headroom and cumulative query work.
Compare two snapshots for rates; lifetime query counts are not current traffic.
Investigate a waiting run older than five minutes, any sustained API/Auth failures,
or charged Storage capacity above 80%. These are suggested operating thresholds,
not automatically configured alerts. Keep Supabase CPU/memory and disk-IO budget
graphs alongside them: upgrading the billing plan does not remove compute limits.

The read-efficiency migration is backward-compatible with the older application.
Release it before the new app; verify with a disposable staging account and then
stage a production candidate for promotion. An app rollback can retain the migration.
Do not roll back the 24-hour recovery window while users have recoverable queued runs.
No map identity, physics hash, recorded time or stored numeric leaderboard is rewritten.

## Validation

`npm test` includes movement regressions, strict replay validation, gzip loading,
API authorization, retry/crash recovery, actual PostgreSQL through PGlite, quotas,
atomic PBs, rank ties, account deletion, maintenance guards and client lifecycle.
`npx tsx tests/online-client.browser.ts` exercises browser flows with explicit
service fixtures; these do not establish live OAuth or queue operation.

`fixtures/online-canonical-replay.json.gz` completes Utopia from its unchanged
production spawn. The first 664 ticks walk across the start deck using ordinary
commands before joining the established route. The production verifier reproduces
**53.349069 seconds** and all three checkpoints. Its 4,531 total commands take
about 67.965 seconds including preparation; cloud testing must allow that wall time
between issuing the ticket and submitting. Automated QA accounts/records must be
removed from public standings after testing.

## Privacy and limitations

Supabase Auth stores provider account identity. Public records expose username,
account UUID, time, splits, date and available verified command replays, not email
addresses or OAuth credentials. No analytics or advertising SDK is added.

Original CSS map assets retain their authors' rights; hosting does not grant a new
licence. Existing uncertainties remain in `THIRD_PARTY_NOTICES.md`. Exact universal
CSS/KSF parity, human-only anti-cheat and unlimited free operation are not claimed.

## Verified deployment evidence (3 October 2026)

The full 232-test suite and production dependency audit passed (zero vulnerabilities).
Replay/database checks also passed on Node 22. Native Node ESM loading is tested
without TypeScript hooks: `scripts/build-server.ts` bundles internal server and
simulation imports into explicit `.mjs` entrypoints before Vercel packages them.
This fixes the startup failure found in the first real preview; local TypeScript
tests alone did not detect that packaging difference.

`fixtures/online-cloud-validation.json` records a successful real preview run:
Auth account creation/sign-in, profile update, ordinary attempt/upload APIs, private
immutable Storage upload, queue verification, exact time/splits, atomic repeated
submission, public leaderboard, intact replay download and account deletion.
A subsequent direct database check confirmed zero remaining QA users, records or
replay objects. Both temporary QA deployments and the QA secret were removed.
The helper is preserved only as an opt-in test fixture under `tests/support/`;
no test bootstrap endpoint is present in the normal deployed API.

The deployed browser loaded Boreas and its leaderboard with no console errors.
Google login reached Google's real sign-in page. Completing a human Google/X login
and verifying the final return remains a user check; this is separate from the
successful real Auth-token/backend integration test.

## UI review branch — 3 October 2026

The UI pass retains the Source/VGUI panel style with a shared visual system for
the main menu, Settings, map picker, accounts, leaderboards and finish screen.
The visual pass itself leaves movement and ranked-board identities unchanged (the
subsequent capped-start rule change is documented below). Google uses its official
current sign-in mark; provider actions remain optional. The later guest-claim
flow below supersedes this pass's initial next-run-only sign-in prompt.

`GET /api/surf?action=run&runId=UUID` exposes the public name, map, server time and
splits of a visible verified replay on a current active board. It excludes private
upload paths and account data. `/?map=boreas&run=UUID` opens a run card with replay
playback. Sharing opens an editable X draft; it never posts automatically. Local,
practice and watched-route shares explicitly say what they are and link to the
map, rather than presenting a verified replay link. All displayed times round to
milliseconds consistently, including across minute boundaries.

For local review, run `npm run dev -- --port 4190` and open `/ui-review.html` for
sample finish, sign-in, leaderboard and sharing states. This gallery uses an
isolated fixture client; no accounts or records are created. It is not a production
build entry. The real playable preview remains `/`. Production deployment was
held for review; the user approved this release on 3 October 2026.

Validation for this pass: all 243 automated tests passed, as did 14 isolated
browser integration contexts and 22 UI checks. `npm run test:online:browser`
exercises auth handoff, submission, replay, cancellation and sharing against
fixture APIs; it does not complete a real Google/X login or publish a record.
With the dev server on port 4190, `npm run test:ui` checks the playable menu and
the isolated gallery, including keyboard focus and 390×740 / 800×480 layouts.
Its report and seven screenshots are written to `test-results/`. Override
`UI_BASE_URL` to use a different local server.

The production build's normal Boreas command replay completed in 00:39.495,
with checkpoint splits 00:16.594 and 00:32.681. Browser inspection found no
console errors. The embedded review browser cannot capture the mouse, so use
Chrome or Edge for manual surfing. New online UI flows were checked with fixture
APIs; their deployed integration still needs a preview check before promotion.
The review gallery is excluded from the production build.

### Watching replays while in a server — 8 October 2026

The local client now keeps the room connection open for leaderboard Watch,
shared-run playback, and Watch route. Previously both replay entry points
explicitly called `multiplayer.leave()`. Playback now uses a separate session
while retaining the original room player, position, stage, practice state, and
commands. Escape offers **Return to server**; replay completion offers the same
action. Restart run deliberately restarts the original server map.

Only the retained player's stationary pose is sent to the room. Replay movement,
practice flags, timer, and finish do not become the viewer's multiplayer state
or ranked result. Chat and votes remain connected. Active viewing counts as
activity for the existing full-room idle policy; paused/background viewers remain
idle. Other-map playback hides avatars from the room's different map. Opening
Records keeps the current run moving and ranked. Actually starting Watch explicitly
interrupts the player's ranked attempt while parking their body for the replay.

Room changes cancel playback and stale downloads. A reconnect to the same room
epoch preserves the retained player. Canceled or failed replay loads restore the
room player, and Escape during return loading does not recapture the mouse.
Explicit Leave server and choosing a solo map retain their normal behavior.

`npm run test:replay:rooms` runs the real browser controls and complete Boreas
replay with isolated API/WebSocket fixtures. Set `UI_BASE_URL` to a local dev or
built-preview server, and `UI_REPORT_TAG` to name the JSON report/screenshots under
`test-results/replay-room/`. It checks decoded outgoing poses, chat/voting,
same-map and other-map returns, Summer stage 11, replay switching, download and
asset failures, cancellation, room rotation, reconnect, natural completion,
restart, and solo playback. It does not create live players or publish records.
This fix is local pending release; these checks are not production verification.

## Capped ranked starts — 3 October 2026

The normal client profile, build catalog and trusted verifier now use
`RANKED_CONFIG` (`css-surf-capped-1`): auto bunnyhop enabled, unrestricted start
speed disabled. The existing Boreas-derived end-tick XY start cap is enabled;
see [the movement reference](REFERENCE.md) for its exact semantics and evidence
limits. Old `SURF_CONFIG` open-start witnesses remain replayable and unchanged.
They are rejected by the new ranked verifier. New local record keys and generated
board IDs keep the categories separate; old records must never be relabelled.

Preferences v3 imports v2 controls, sensitivity, view, audio and map preferences,
but resets the old default unrestricted-start flag to false. A fresh explicit
unranked opt-in persists. Settings and leaderboard panels explain the capped rule.
Guest messaging distinguishes eligible prepared runs from local-only finishes;
the guest-claim flow below permits authentication after an eligible finish.

Before deployment, seed the three new catalog board rows as described above and
retire the old boards without deleting their records. The approved release has
seeded the new boards without relabelling older records. Check the new boards
and a real submission on a preview before promoting the UI and rule changes.
Drain old-profile pending verification jobs before cutover; the new verifier
cannot finish those jobs under different rules. Replays and published times keep
their original identities. The unranked open-start option also preserves access
to existing local PBs by using the legacy configuration version.
The canonical Utopia fixture has a newly verified envelope/report for the capped
profile; its normal command sequence is unchanged. The prior cloud validation
file remains historical evidence for the previous deployment, not a cloud test
of these new board IDs. `tsx scripts/validate-classics.ts --ranked` regenerates
`fixtures/ranked-playability-results.json` for all three capped command routes
and exact 30/60/144/240 FPS schedule comparisons.

Local validation passed: 263 automated tests, 14 isolated online browser contexts,
23 UI checks, and the production build. All three capped routes complete, with
exact per-tick results at 30/60/144/240 FPS. No real account or database record was
created during this follow-up.

## Finish first, sign in to save — local review

The normal default-rules guest flow now requests a signed guest ticket before the
first movement command. As of the October 9 client fix, new runs wait for online
preparation, with an explicit local-play option if the player prefers not to wait.
Finishing an eligible run stores its exact command replay and ticket in IndexedDB.
Google/X buttons commit an explicit save intent before leaving for OAuth. On return,
the finish screen and splits are restored, the ticket is claimed by the signed-in
account, and the existing immutable upload and server verification flow runs.
Only a verified server response upgrades sharing to a public replay link.

`POST guest-attempt` allocates no Auth account, database row or Storage object.
Its 24-hour HMAC proof binds a random attempt UUID, board identity and server issue
time. The signing key is domain-separated from `SUPABASE_SECRET_KEY`; no new secret
is needed. Treat the proof as a private bearer capability: it never belongs in a
URL, share link or log. Rotating the server secret invalidates unclaimed proofs.
`POST guest-claim` requires a real Auth user and matching `expectedOwnerId` before
claiming. The atomic database ledger binds that nonce to exactly one account,
including after account deletion, until the proof expires. Same-owner retries
return the same attempt; the upload window is not extended. A new claim grants
the ordinary 45-minute window. Registration time, separately from original issue
time, enforces the shared 1,200-attempt/hour quota.

The browser keeps one completed guest replay, bounded by the existing 8 MiB /
40,000-command limits. A newer finish can replace an unclaimed draft only before
the player has requested saving it. Authorized, owned and claimed drafts are
protected until saved or explicitly dismissed. IndexedDB transactions bind owner,
claim progress and conditional deletion to the attempt UUID so stale tabs cannot
overwrite another run. Unclaimed drafts expire with their 24-hour ticket; claimed
drafts remain recoverable for seven days, including when only verification remains
after the upload window closes. Expired entries are removed on the next access.
Storage failure stops OAuth navigation and offers a retry instead of silently
losing the replay. Cancelling login keeps the run available in Account.

Practice, open starts, changed movement rules, interrupted runs, expired/unprepared
tickets and watched replays do not become ranked through login. The browser's time
and splits are presentation only; server simulation computes the accepted result.
This is not evidence of human-only input. The original server verification and
anti-automation limitations still apply.

Release prerequisites: apply `20261003155331_signed_guest_attempt_claims.sql`, seed
the new capped-start boards, and validate before promotion. The schema and board
setup were applied after the user's release approval on 3 October 2026. The
browser integration suites use explicitly routed fixture services, not real Google
or X identities, and the SQL tests apply all migrations to local PGlite.

Local validation passed: 283 automated tests, 14 existing online browser contexts,
15 new guest-save browser contexts, 11 real IndexedDB scenarios, two actual-game
restored-finish scenarios, 26 UI checks, TypeScript and the production build.
The guest flow exercises the real Supabase PKCE client against intercepted fixture
Auth responses, asserts the uploaded gzip replay matches the retained commands,
and tests account changes, login cancellation, duplicate returns, lost responses,
expired upload windows, unavailable storage, and both protected-slot and retry
recovery. This does not claim a completed real Google/X account login on the new
deployment. `npm run test:guest:browser` runs the guest suites; its actual-game
checks need the dev server at `UI_BASE_URL` (default port 4190). Test reports and
screenshots are written under ignored `test-results/`.

A domain change does not migrate local site data: OAuth must return to the same
origin that stored the replay. Keep the existing origin allowed during a domain
transition and avoid redirecting an in-progress old-origin callback to the new one.

## Custom domain — 3 October 2026

`https://surfd.net/` is attached to the existing Vercel production project;
`www.surfd.net` redirects to it with HTTP 308, preserving paths and queries.
The original `surfd-five.vercel.app` origin remains available. HTTPS, all three
collision packs, the scripts, legal pages and records API were checked successfully.
The production deployment was not changed by attaching the domain. The UI,
capped-start and guest-save release was subsequently approved separately.

The user confirmed saving Supabase Site URL `https://surfd.net` and adding
`https://surfd.net/` to the redirect allowlist while preserving existing entries.
The agent could not independently read this setting: the connected database tools
do not manage Auth configuration and the browser dashboard was signed out.
Google and X still use the existing Supabase callback URL above. Public provider
website/privacy/terms links can use the new domain. Online accounts and times use
the same backend; browser-local PBs, settings and login sessions do not move across
origins automatically.

## Approved release checks — 3 October 2026

The guest-claim migration is applied, with RLS enabled and no anonymous or ordinary
authenticated table/RPC privileges. Only the trusted service can consume claims.
All three new board identities were inserted alongside the original boards; the
old boards are retired at cutover without deleting the existing historical record.
There were no pending/verifying jobs at the pre-release check.

Security advisors report the intentional server-only RLS configuration described
above, plus an existing [leaked-password protection advisory](https://supabase.com/docs/guides/auth/password-security#password-strength-and-leaked-password-protection).
The game's sign-in UI uses Google/X, not password registration. No authentication
settings were weakened for this release.

Creating a temporary real-cloud QA account and preview guard secret was rejected
by automatic approval review as outside the deployment request. Neither was
created, and no temporary helper is included in the release. Validation instead
uses the completed local/browser suites, live public endpoints, schema/permission
checks, and production runtime/browser inspection. The optional `cloud-smoke.ts
--guest` fixture is available for a separately authorized cloud-account test; this
release does not claim that new authenticated end-to-end cloud test passed.
Local and online records

Download this document

# Records and online support

Local records always work. Optional accounts and server-validated leaderboards are now implemented; see [online setup and validation](ONLINE.md) for deployment requirements and evidence. The notes below describe the local boundary and integrity requirements.

The three stable course IDs are `surf_boreas`, `surf_utopia_njv`, and
`surf_mesa_fixed`. Each course has a geometry/timing version. Records and replays
also carry the full movement configuration, including tick interval, auto-bhop,
start rules, and map-specific velocity limit. A map or movement change therefore
does not silently compete against an incompatible local personal best. Graphics,
audio, and the last-selected map are preferences, not record categories.

Ranked play now uses capped starts. Unrestricted start speed is an explicit
unranked local category; its old records cannot be submitted or relabelled as
capped-start times. Eligible guests can finish first, then sign in from the result
screen to save that run. A signed guest ticket must have been prepared before the
first command; login alone cannot make an unprepared or assisted run eligible.
The completed command replay is kept in this browser through login, then claimed
by the account and independently verified on the server. Sign in within 24 hours
of ticket issuance. Local personal bests remain available without an account.

New local best entries include `mapId`, `mapVersion`, `physicsConfig`, elapsed
time, ordered splits, and date. Existing Boreas storage keys remain compatible.
The local key's compact hash is only a storage convenience; it must not become
the sole identity or an integrity check for an online record.

`GameSession` owns authoritative simulation events and command recording without
depending on the renderer or DOM. A replay contains a declared initial state and
tick-indexed movement/buttons/view angles. It can be replayed in a server process
using the same map and configuration. Normal records are separate from practice,
restores, demonstration playback, hidden-tab or long-frame interruptions, and
changed rules. Ordinary menus and visible focus loss keep simulation, timing
and command recording running; releasing pointer lock does not invalidate a run.

Online support authenticates the player and validates each submitted
replay on the server against pinned geometry/configuration and permitted initial
states. Derive elapsed time and checkpoint order there; do not trust browser
times, positions, local storage, or its `practice` flag. Store the complete
version identity and the replay digest with the result. A leaderboard can then
partition by map revision and movement rules while sharing the existing map
catalog and simulation. Migrated local times should be clearly marked unverified
unless their original command replays can be validated.

The imported maps/assets retain their original owners' rights. Check hosting and
redistribution permission before a public service release; this local build does
not confer those rights.
Independent KSF comparison

Download this document

# Revision review and independent KSF evidence

Reviewed 2 October 2026. This supplements the original validation report. The earlier statement that no CSS telemetry was available is superseded by the public replay described below. It does not establish whole-game or current-server parity.

## Requested play profile and controls

`DEFAULT_CONFIG` remains the researched manual-jump profile. `SURF_CONFIG` enables held-jump autobhop and unrestricted prestart hops under a different configuration/version, so records are separated. Gravity, acceleration, wish-speed rules, friction, jump impulse and simulation interval remain unchanged. Tests verify repeat jumps only from ground, identical jump impulse across successive hops, preservation of earned horizontal momentum, no automatic horizontal boost, and an unrestricted start after twelve prehops at 1,200 u/s.

No stamina or vanilla bunnyhop speed penalty was added. This is an explicit surf-profile choice, supported by the CSS-derived Momentum surf mode and the user's requested behavior, not proof of a live KSF plugin manifest. A first crouch jump in the KSF recording independently matches the implemented impulse/order. It alone cannot prove stamina behavior after repeated native CSS jumps. [CSS-derived jump implementation](https://github.com/momentum-mod/game/blob/9da88b97769e0f2306623946ebbcb5d0f919a1f0/mp/src/game/shared/momentum/mom_gamemovement.cpp).

Actions accept multiple physical keyboard codes, mouse buttons and wheel directions. Defaults retain Space and add Wheel Down for jump. One wheel event becomes one simulation-tick pulse followed by a release tick; another scroll during release is retained for the next press tick. Same-tick wheel events coalesce. Held Space continues to hold jump through wheel release ticks. `peek()` never consumes events; `sample()` is called once per simulation tick. Focus loss, release of Pointer Lock and binding changes clear pending input. Mouse angles remain immediate, using 0.022 degrees/count times sensitivity and no frame-duration multiplier.

Sixteen input tests and thirteen revision tests cover the revised behavior. Tick-indexed wheel inputs produce identical command sequences and terminal physics states at 30, 60, 144 and 240 render FPS, including extra HUD reads. This checks consumption and render independence; browser event dispatch time relative to a tick still determines which command receives a real physical event.

## Public Boreas telemetry

The KSF website publishes an unauthenticated [replay viewer](https://ksf.surf/replays/surf_boreas/replay_css_4060_0_712551_1763914843.rec) and [replay bytes](https://ksf.surf/api/replays/replay_css_4060_0_712551_1763914843.rec?game=66t) for `.x`'s 39.495121-second CSS forward-style run recorded 23 November 2025. The public viewer specifies 66.66666666666667 ticks/second, corroborating nominal 15 ms. The simulation uses binary32 `0.015` as documented in the reference.

The 121,036-byte recording has SHA-256 `989a61f3682ccbb04026561103e951c90b267c43aa9b09eb66fc6e2c4cdb4d15`. Its version-2 format is documented in [Crashfort ReplayViewer's frame structures](https://github.com/crashfort/ReplayViewer/blob/0341fea8ba85fbb7c3d352b4b5ddf704a659fd4d/src/rv_priv.inc). The fixture `fixtures/boreas-ksf-telemetry.json` contains exact decoded binary32 values and provenance. No viewer code was copied. The decoded layout is a 16-byte header, five 524-byte bookmarks, then 2,960 40-byte frames. Each frame includes buttons, feet position, pitch/yaw/roll and velocity.

| Event | Recorded frame | Notes |
|---|---:|---|
| Initial stationary state | 0 | Position (12587.87793, -12034.27148, 14736.03125) |
| First crouch jump | 74 | End-tick vertical velocity 289.9933777 |
| Timer start | 125 | Horizontal exit speed 350 u/s |
| Checkpoint 1 | 1231 | Bookmark stage 2 |
| Checkpoint 2 | 2303 | Bookmark stage 3 |
| Finish | 2758 | Bookmark stage 99 |

The 2,633 start-to-finish intervals equal 39.495 seconds at decimal 15 ms. The published time differs slightly, so these bookmark indices do not establish exact KSF sub-tick timing semantics.

This run includes a prestart jump and a 350 u/s exit. The 2013 announcement's 270 u/s prehop restriction must therefore not be treated as a universal present-day Boreas rule. Map-specific falling-start behavior or changed rules remain possible. The recorded start transition demonstrably clamps velocity to 350 after displacement, whereas the requested unrestricted browser profile deliberately permits higher starts.

## Command reconstruction and comparison

Independent one-step comparisons establish the recording's alignment: to advance state **i** to **i+1**, use **buttons from i** and **view angles from i+1**. Using next-frame buttons creates approximately 60 u/s errors at some strafe reversals; using current-frame angles produces a median velocity error of about 27.57 u/s in the examined air transitions.

Buttons reconstruct full held forward/back/side commands at magnitude 400, but the recording lacks actual analog command magnitudes. Source `CInput::KeyState` gives an initial key press half strength. The recording's first forward movement gains 15 u/s, matching command 200; assuming 400 incorrectly predicts 19.5 u/s. This is covered by the ground fixture. The browser currently applies full held movement immediately, so native fractional keyboard command generation remains a known input difference. [Valve keyboard command generation](https://github.com/ValveSoftware/source-sdk-2013/blob/b8cfb12c0e083a2ef5b2f9f9b50f3902fa034474/src/game/client/in_main.cpp).

The replay does not encode grounded state, active crouch hull, partial-duck timer, surface friction, exact native build, cvars or plugin versions. An IN_DUCK bit alone does not establish the active hull: blocked uncrouch and a ground-to-air transition can delay a hull change. Surface friction for an isolated air step is reconstructed from the prior categorization velocity, which precedes FinishGravity; use recorded vertical velocity plus half-gravity, not recorded end-tick velocity alone.

Comparison tolerances were set to **0.002 Source units** for position and **0.002 u/s** for velocity, allowing roughly two binary32 position ULPs near 16k coordinates and native float/trigonometric differences at surf speeds.

| Independent fixture | Steps | Maximum position error | Maximum velocity error | First tick outside tolerance |
|---|---:|---:|---:|---|
| KSF flat start and first crouch jump, equivalent floor at Z=14736 | 65 | 0 | 0.0000314568 | None |
| KSF isolated air transitions | 1672 | 0.00048828125 | 0.00088026154 | None |

Air steps are selected from recorded constant-gravity velocity and ballistic vertical displacement, excluding collision, hull-origin shifts, and the recorded start-zone speed cap. These are conditional one-step checks, resetting to each independently recorded initial state. They verify the tested air/ground formulas; they are **not a continuous full-route replay or a test of native ramp collision parity**. Contact-state error cannot be measured from this file because contact state is absent.

## Collision review

The BVH broadphase was compared with independent linear AABB overlap queries for 350 bounds and 400 queries, including exact boundary contact. Candidate membership and original order agree. Preserving original order protects collision-plane tie behavior. Analytic displacement tests verify a swept 62-unit hull cannot tunnel through thin terrain and can travel tangentially across a coplanar two-triangle seam without an invented impact.

The actual BSP/PHY geometry then allowed independent collision debugging. At ticks 1010, 1807 and 2395, adding the trace margin to every convex plane had manufactured a hit across disjoint corners: the swept hull left an edge plane before reaching the face. An additional unpadded interval check rejects those false convex intersections within the finite sweep. It preserves shallow VPhysics skin contacts when the outgoing edge lies beyond this tick, as at 906. This changes geometric hit selection rather than preserving speed artificially. BSP departure ticks 1173 and 2310 reject a brush when both endpoints remain outside a plane, even within the backoff margin. All five seam/departure regression steps agree within the unchanged .002/.002 tolerances. Tick906 now matches native velocity within .000864 u/s, but its contact fraction still produces a .00691-unit position difference, which remains reported.

Native frame2756 independently demonstrates a neutral uphill landing: movement stops at the first walkable contact plus .1 Z, horizontal velocity remains incoming, and vertical velocity becomes zero. This matches the eligibility and offset documented by [RNGFix's pre-tick incline correction](https://github.com/jason-e/rngfix/blob/9831d25e9f6747566a6adc72d75ae3fc671a656c/plugin/scripting/rngfix.sp). The explicit `inclineFix` setting reproduces both this landing and the next manual-jump-profile tick within .002/.002. Frame2757's upward velocity comes from walking up the slope; continuous IN_JUMP in the replay does **not** establish a new jump or global KSF autobhop. The requested browser autobhop remains a separate profile choice. Evidence of this behavior does not identify the installed plugin version.

The authored Boreas `player_speedmod` zone suppresses jump and multiplies movement frame time by .9999. Independent recorded gravity inside the zone corroborates this modifier. The authoritative server tick and timer remain unchanged; no extra acceleration or gravity ticks are introduced. See [map extraction evidence](BOREAS-IMPORT-RESEARCH.md).

## Complete native-route comparison

Run `npx tsx scripts/validate-boreas-reference.ts` to regenerate `fixtures/boreas-reference-validation.json`. It records the exact geometry SHA-256, complete configuration, source provenance and all 2,758 per-tick errors/contact predictions for two comparisons. Both use manual jumping plus the documented incline correction, authored no-jump effect and observed 350 u/s post-displacement clamp at tick125. They do not claim the user's intentionally different autobhop/unrestricted-start profile is identical to KSF.

| Comparison | Maximum position error | Maximum velocity error | First tick outside .002/.002 | Ticks outside |
|---|---:|---:|---:|---:|
| Independently initialized native state each tick | .006905340 at 906 | .003744595 at 2402 | 334 | 27 / 2758 |
| Continuous run from native frame0 and commands | .031625415 at 2755 | .006143667 at 2112 | 199 | 2537 / 2758 |

Continuous final position error is .011624434 units and final velocity error .003204201 u/s. The continuous run receives no recorded position or velocity after its initial state. It reaches the native finish location from reconstructed normal commands. Each mode reports 1,070 simulated collision ticks; native contact/ground flags are absent, so a contact-state error count would be invented. Timed-trigger completion is verified separately by the game-session route witness.

The isolated residuals are concentrated at BSP ramp contacts (334,535–613), shallow VPhysics contact906, curved VPhysics normals (2181–2408), and entry to the map's speed modifier (2641–2642). The contact differences are consistent with native float/trace arithmetic differences, but that explanation is not proven. For the modifier, the exact six-plane trigger begins hull overlap at frame2640, origin entry at2641, and native scaled gravity first appears in transition2642→2643. The current immediate hull-overlap rule acts two ticks earlier. General Source touch/event-queue timing has not been reproduced; shifting the authored bounds would hide this uncertainty. Neither group is hidden by loosening tolerances. The original .002 thresholds remain strict for both comparisons, so accumulated continuous drift is explicitly counted even though the final spatial error is small.

These results are strong evidence for this real CSS/KSF route and its physics/collision behavior. Remaining gaps are native VPhysics and displacement contact implementation, entity-I/O timing at trigger boundaries, keyboard fractional press/release magnitudes, exact native build/cvar/plugin versions, native grounded/duck metadata, detailed repeated-jump stamina rules, and exact KSF timer internals. An instrumented CSS build and experienced human comparison would still improve coverage beyond this single independently recorded run.
Boreas import & authorship

Download this document

# Actual CSS Boreas import

Research and extraction date: 2026-10-02. This replaces the earlier inferred Northline layout as the playable map source. It does not establish exact CSS engine equivalence.

## Source and authorship

The imported file is the **CSS** `surf_boreas`, not a CS2 port. The public [KSF/OuiSURF mirror index](https://main.fastdl.me/maps_ksfthings.html) lists the map with SHA-1 `9bd9daa0a23288c7e6f439fb7a899beade34a80b`. The [pinned download](https://main.fastdl.me/h2/9bd9daa0a23288c7e6f439fb7a899beade34a80b/surf_boreas.bsp.bz2) decompresses to 36,649,534 bytes, and its SHA-1 was independently checked. It is VBSP version 20, map revision 1040.

[OuiSURF's map collection](https://github.com/OuiSURF/Surf_Maps) supplies public archive links. The [Boreas workshop release by granis and Syncronyze](https://steamcommunity.com/sharedfiles/filedetails/?id=2424739354) identifies the CSS/66-tick original and links its CSS download; that older Drive link was not usable during research. [KSF's CSS map entry](https://ksf.surf/maps/surf_boreas?game=66t&mode=fw) supplies the target game/style context. The mirror copy is identifiable and reproducible without relying on a similarly named port.

Authorship is retained as Syncronyze and granis; the workshop lists both contributors. Map geometry, models, textures, and sounds retain their respective authors' rights. Public availability is **not** evidence of a general redistribution licence. No such licence was found in the archive README, workshop description, or packed file names. This project imports the publicly available map for the user's requested local playable version; it does not relicense those assets or claim ownership. Publishing the extracted asset bundle would require resolving the applicable redistribution rights. No permission question was needed to perform the authorized local research/import.

The Source SDK is a reference for binary layouts and mechanics, with its own [Source 1 SDK licence](https://github.com/ValveSoftware/source-sdk-2013/blob/master/LICENSE). The Python importers are newly authored; they do not embed the SDK. PHY layout cross-checks used [TAServers/PHYParser](https://github.com/TAServers/PHYParser), which is MIT licensed, and [Hona/bsp-to-glb](https://github.com/Hona/bsp-to-glb). Runtime movement was not substituted with another viewer's movement code.

## Reproducible import

Run with Python 3, from the project root:

```text
python scripts/import_boreas_collision.py /path/to/surf_boreas.bsp
```

The script also accepts the `.bsp.bz2`. It rejects a different SHA-1 so a changed map cannot silently inherit this map's record version. Output: `public/maps/boreas-collision.json`, map identity `surf_boreas`, version `bsp-9bd9daa0-v1`.

Importer and runtime geometry checks:

```text
python -m unittest discover -s tests -p test_bsp_import.py
npx tsx --test tests/boreas-map.test.ts
npx tsc --noEmit
```

The Python geometry suite passes seven tests and the TypeScript suite passes nine. The Python suite checks independent analytic rotations and tetrahedron intersections, exported convex invariants, trigger membership, terrain winding, and the KSF standing height. The TypeScript suite additionally exercises the actual runtime sweep from the authored spawn onto the deck, swept checkpoint fractions, and the empty southwest gap inside the finish's broad bounds. It checks a raw PHY vertex at byte offset 4848 of packed `ramp_c1.phy` against two authored prop transforms, including a non-cardinal yaw, and preserves the no-jump outputs and VPhysics collision provenance. This verifies the IVP-to-Source conversion without treating importer-generated expected positions as ground truth. The preselected standing-height tolerance is 0.01 unit; analytic trigger fractions and rigid transforms use 1e-8.

`scripts/bsp_common.py` handles Source's per-lump and game-child raw LZMA payloads. Source raw LZMA can omit the usual end marker, so the declared output length is validated instead of requiring the general-purpose decoder's stream-end marker. Static props use game-lump version 10 with a measured 72-byte record stride in this file. The pak lump is a standard ZIP containing 1,929 files.

Coordinates throughout the collision data are Source X/Y horizontal, Z up, feet origin. No renderer coordinate conversion enters collision. PHY coordinates use IVP metres and transform as `(x, z, -y) / 0.0254`, followed by the authored Source pitch/yaw/roll transform and prop origin.

## Actual map configuration and route

The map's `logic_auto` contains the following `OnMapSpawn` commands, preserving repeated entity output keys:

- `sv_airaccelerate 150`
- `sv_maxvelocity 5000`
- `sv_enablebunnyhopping 1`
- `sv_cheats 1`
- `mp_freezetime 0`

These establish **map-requested settings**, not the final live KSF server configuration: server plugins or cvar enforcement can override map commands. Gravity is not set by this map's spawn outputs. Tick interval is not encoded as a map setting; the separate movement reference and KSF replay research establish the 0.015-second target.

The map has 162 team spawn entities in an off-course room. Those are not the correct browser start. The room's teleporter targets the actual `info_teleport_destination`:

| Purpose | Source position | Angles |
|---|---|---|
| `tele_start` | (12768, -12048, 14870) | pitch 0, yaw 90, roll 0 |

The start is deliberately above the deck. Its imported PHY deck top is Z=14736; the independently downloaded KSF run begins standing at Z=14736.03125. This is a useful absolute coordinate and collision-distance cross-check, not a proof of whole-run parity.

The three-part route crosses the following authored triggers in order. These are broad bounds; runtime tests use the actual convex hull union, not these bounds alone.

| Trigger | Minimum X,Y,Z | Maximum X,Y,Z | Shape |
|---|---|---|---|
| `zone_start` | (12466,-12312,14736) | (13104,-11776,15264) | one box |
| `zone_cp1` | (-2560,10240,6912) | (-2304,13952,10496) | one box |
| `zone_cp2` | (5792,-16192,3040) | (6048,-14912,4448) | one box |
| `zone_end` | (6816,8192,224) | (11720,12816,2880) | union of two boxes |

The course travels from the high southeast start north through several descending curved ramps, then west through CP1. It bends south through the western mountain corridor and east along the low southern route to CP2, then returns north toward the finish. Exact ramp positions and orientations come from the BSP, rather than inferred screenshots.

There are 21 authored `trigger_teleport` entities, all targeting `tele_start`, plus one `trigger_hurt` near the finish. The reset volumes often contain three to eight separate convex hulls and oblique planes. Treating their broad AABBs as active reset shapes would reset players in valid gaps. The finish trigger likewise needs its two-box union. All 26 gameplay zones and full trigger/entity metadata are exported.

The map contains a `player_speedmod` named `nojump`, with spawnflags 4. [Valve's player.cpp](https://github.com/ValveSoftware/source-sdk-2013/blob/master/src/game/server/player.cpp) defines bit 2 as jump suppression: `ModifySpeed` with a value other than 1 disables `IN_JUMP`, while value 1 enables it. The same input sets the player's lagged movement value. [Shared ProcessMovement](https://github.com/ValveSoftware/source-sdk-2013/blob/master/src/game/shared/gamemovement.cpp) scales its saved frame interval by that value for movement, then restores the interval. Thus the shared SDK semantics are **jump-button suppression plus a tiny movement-time scale**, not an arbitrary velocity multiplier.

The actual late-course trigger is model `*57`, one six-plane box `(9280,800,256)..(13504,4800,2048)`, spawnflags 1 (clients), enabled at map load. It sends `nojump,ModifySpeed,0.9999` immediately on `OnStartTouch`. Its three distinct `OnEndTouch` outputs restore 1 at delays **0, 0.03, and 0.06 seconds**. This is an immediate restoration with two repeats, not a mandatory 0.06-second delay. Duplicate output keys are retained in `_pairs`; a dictionary-only parser would incorrectly keep only the last delay. The separate small start trigger `*56`, box `(12744,-12072,14734)..(12792,-12024,14954)`, restores 1 on starting, ending, and testing touch.

`noJumpZones` exports the actual `*57` hull, movement scale 0.9999, and all restoration delays separately from timing/reset zones. A local auto-jump assist should respect authored jump suppression. The requested unrestricted local start rules remain a separate user-selected configuration; the map's no-jump section is not a claim about stock KSF start restrictions.

The independent KSF recording supports that timing factor: frames 2640–2703 overlap the trigger; vertical velocity changes from -1066.5462646484375 at frame 2702 to -1078.5450439453125 at frame 2703. That 11.998779296875-unit gravity decrement agrees with `800 * 0.015 * 0.9999` under float32 rounding and disagrees with the ordinary 12-unit decrement. Frame 2704 leaves the box while still using the scaled interval; frame 2705 resumes a 12-unit decrement. This is specific external evidence for the authored movement-time modifier. The later landing at frame 2756/2757 is already far outside the box, so this no-jump trigger cannot justify suppressing that later landing's jump.

## Collision extraction

The import contains **178 solid BSP brush instances plus 148 PHY convex instances**, for 326 convex solids total. World and brush-entity membership is derived by walking each model's BSP headnode, leaves, and leaf-brush references. The contents mask includes solid, moveable, playerclip, window, monster, and grate contents. Compiled BSP collision planes are exported unchanged, including bevel planes; render triangles are not substituted for brush collision. Vertices/faces are reconstructed only for bounds, diagnostics, and shared display.

Static props are essential to Boreas. Of 1,587 static prop instances, only 11 are solid: the start deck and ten curved ramps. The pine trees, rocks, icicles, and nine straight ramp render props are marked non-solid in the map. The straight sections obtain their collision from BSP brushes; assigning all decorative models solid collision would create an incorrect course.

| Packed collision model | Solid instances | Convex hulls per instance |
|---|---:|---:|
| `ramps/ramp_c1.phy` | 3 | 16 |
| `ramps/ramp_c1m.phy` | 4 | 16 |
| `ramps/ramp_c2.phy` | 1 | 10 |
| `ramps/ramp_c2m.phy` | 2 | 10 |
| `details/dek01.phy` | 1 | 6 |

The importer reads the packed VPHY/IVPS compact-ledge tree and each original convex's indexed vertices. It retains the convex decomposition and constructs separating planes for swept AABB hull collision, including edge/axis bevels. All 148 prop hulls are explicitly tagged `collisionModel: "vphysics"`; compiled BSP brushes remain distinguishable so their engine trace paths can use evidence-supported differences in rejection/tolerance behavior. It neither tessellates the visual model into guessed collision nor replaces curved ramps with straight slabs. Model surface-property metadata is preserved; the curved ramp PHY files specify `ice`. Actual CSS surface-friction lookup and VPhysics tolerance behavior remain separate fidelity questions. The current exported movement friction defaults to 1; steep surf contact does not become a ground surface merely because a material is ice.

All **1,125 displacement surfaces** are decoded at their compiled resolution using the Source grid layout and checkerboard split described in [Valve's builddisp.cpp](https://github.com/ValveSoftware/source-sdk-2013/blob/master/src/public/builddisp.cpp). The high bit of `minTess` indicates surface flags. `SURF_NOHULL_COLL` excludes 59 surfaces from player collision; `SURF_NOPHYSICS_COLL` alone does not exclude a player hull. The exported set contains **129,728 nondegenerate collision triangles** after these flags and removal tags. The renderer and collision importer share the same generated vertex grids. Arithmetic at vertex construction is rounded to float32 deliberately.

BSP face loops are clockwise from outside. The collision export reverses displacement triangle winding so the conventional cross product gives the front normal. Face `side` must not cause a second inversion of `planenum`, which already points outwards. The complete triangle payload is kept separate so the runtime can index it spatially without bloating it into hundreds of thousands of full brush records.

## Remaining verification boundaries

- Extracting the exact map establishes geometry provenance, not parity with CSS's proprietary BSP/VPhysics collision implementation. Convex decomposition is preserved, while trace tolerances and contact ordering still require external comparison.
- Displacement grid geometry and authored flags are reproduced. Engine-specific edge ownership, stitching, triangle contact filtering, and start-solid behavior may still differ.
- Rotating rune brush entities are decorative/non-solid in this map and are omitted from player collision. General entity I/O is exported as evidence, not claimed to be a complete Source entity-system emulation.
- Water, particles, sounds, moving decorations, material proxies, and Source's full lighting renderer are separate from map traversal collision. Visual fidelity status belongs to the renderer report.
- KSF's authentic replay positions/velocities are an independent route and collision check. They are not a browser command-only proof of traversal unless the game reproduces them from commands without position/velocity injection.
- No direct CSS executable instrumented comparison or experienced human playtest was available during this extraction. Validation must retain that limitation instead of equating plausible motion with exact CSS/KSF equivalence.

Other investigated tools were [SourceUtils](https://github.com/Metapyziks/SourceUtils) (MIT BSP/WebGL export) and [Crashfort ReplayViewer](https://github.com/crashfort/ReplayViewer) (KSF replay format and Source-based playback). Neither tool's movement was adopted without review. A hidden creator-restricted s&box port was not needed or accessed; the public CSS BSP supplied the authentic geometry directly.
Rendering & visual limits

Download this document

# Boreas visual import

The current scene imports the actual locally supplied `surf_boreas.bsp`. It replaces the original procedural mountain scene. This is an independently written browser renderer for the map's geometry and packed assets; it is not Valve's renderer and does not claim pixel parity with CSS.

## Provenance and rights

- Original map credit: **Syncronyze**. The supplied map's wider provenance also credits **granis**; retain the map's original author and asset credits.
- Input: BSP version 20, SHA-1 `9bd9daa0a23288c7e6f439fb7a899beade34a80b`.
- Download provenance: [the matching FastDL BSP archive](https://main.fastdl.me/h2/9bd9daa0a23288c7e6f439fb7a899beade34a80b/surf_boreas.bsp.bz2).
- Generated map geometry, textures, models and lighting remain their original authors' work. Public download availability is not a permissive redistribution license. This conversion is for the user's local project; no permission to redistribute or publish those assets is asserted.
- The converter and renderer were written independently from format descriptions and mathematical behavior. No Valve source implementation was copied into them. The [Source SDK license](https://github.com/ValveSoftware/source-sdk-2013/blob/master/LICENSE) must not be represented as a general browser-game asset or code license.

Primary technical references: Valve's [BSP structures](https://github.com/ValveSoftware/source-sdk-2013/blob/master/src/public/bspfile.h), [studio model structures](https://github.com/ValveSoftware/source-sdk-2013/blob/master/src/public/studio.h), [optimized model structures](https://github.com/ValveSoftware/source-sdk-2013/blob/master/src/public/optimize.h), [displacement construction](https://github.com/ValveSoftware/source-sdk-2013/blob/master/src/public/builddisp.cpp), and [client sky rendering](https://github.com/ValveSoftware/source-sdk-2013/blob/master/src/game/client/viewrender.cpp). Shared SDK structures establish format behavior where applicable; they do not establish exact CSS shader behavior.

## Imported resources

| Resource | Imported result |
| --- | --- |
| Visible brush and displacement faces | 1,783, combined into 35 material/lightmap/sky batches |
| Displacements | All 1,125, full resolution, using the collision importer's shared grid and triangulation |
| Original static props | All 1,587, from 26 distinct model files |
| Studio model geometry | MDL v44/v48, VVD LOD 0, DX90 VTX strip groups |
| Original material images | 233 PNG images decoded from packed VTFs, including normal/detail maps and cubemap faces |
| Baked lighting | One lossless 2,048² RGBExp32 world-lightmap atlas and one original VHV vertex-lighting atlas |
| Total referenced images | 235, including the two lighting images |
| Sky | Original packed sky textures, scaled 3D sky geometry and sky props |
| Brush entities | Authored initial origin/angle transforms and render colors, including the purple finish decorations |

`scripts/import_boreas_visuals.py` creates `public/maps/boreas-visuals.json` and resources beneath `public/maps/boreas/`. It reads the local BSP path by default; pass a different BSP path as its first argument to reproduce the conversion elsewhere. Python requires NumPy and Pillow. `scripts/bsp_common.py` provides the common compressed-lump, entity, transform and displacement readers.

`src/view/bsp-renderer.ts` exposes `loadBspVisuals(slug)`, its compatibility alias `loadBoreasVisuals()`, and `createBspRenderer(canvas, data, map?)`. Geometry/image fetching finishes before renderer creation. Asset decoding never happens in a movement tick. Opaque props use conservative per-instance frustum culling and shared model buffers; translucent props retain spatial batches. The optional map argument supplies visible timer-zone markers.

## Camera and material treatment

Positions remain in Source units throughout export. The renderer's only axis conversion is `(x, y, z) → (x, z, -y)`. Source yaw 0 faces +X; positive pitch looks down. The supplied eye position and current mouse angles are used directly. There is no camera easing, banking, mouse filtering or speed-based FOV. A 4:3 horizontal Source FOV becomes vertical FOV through `2 atan(tan(horizontalFov/2) × 3/4)`; viewport aspect then determines widescreen horizontal coverage.

Brush UVs use the original texture vectors and dimensions. Displacement UVs interpolate their ordered base surface; displacement lightmap UVs stretch the authored luxel rectangle over the grid, as in `CCoreDispSurface::CalcLuxelCoords`. Seamless terrain uses world-coordinate triplanar sampling and the authored rock/snow alpha. Sky sampling reverses the sky-camera expansion for texture scale.

RGBExp32 light samples preserve their exponent. The shader decodes four neighboring luxels to linear values before bilinear interpolation; it never linearly filters the exponent byte. Props use their embedded VHV vertex lighting. Alpha-tested foliage clips the original leaf alpha, then writes opaque alpha to avoid browser compositing fringes. Packed cubemaps are used for reflective materials; `env_cubemap` props select the nearest packed probe. Rock detail textures retain their authored scale and modulation factor.

Fog uses the map's original color (232, 255, 254), start 500 and end 43,420. The default planar fog path is used. Source shrinks the sky camera and its fog ranges together; because this renderer expands sky geometry instead, it retains those original fog ranges. Sky rendering precedes the main world with a depth clear between them.

## Validation

Run `python scripts/validate_boreas_visuals.py` for asset integrity. The saved result is `fixtures/boreas-asset-validation.json`:

- 127,829 mesh vertices and 531,810 triangle indices are finite, in range and structurally valid.
- All 84,405 displacement vertices match the common BSP decoder exactly after float32 export and the documented sky expansion: maximum coordinate error **0 Source units**.
- All 1,587 original props are represented and all 235 referenced images exist.

This is an internal geometry/resource integrity check, not independent proof that every BSP format assumption or rendered pixel matches CSS. The movement validation and external CSS trajectory comparisons are separate deliverables.

With the development server running, `node --import tsx scripts/bsp-visual-qa.ts` launches headless Edge, renders four fixed inspection views and measures a short camera traversal. It uses an isolated visual scene and cannot move the gameplay player. Results are in `fixtures/boreas-render-results.json`; screenshots are `docs/screenshots/boreas-start.png`, `boreas-first-ravine.png`, `boreas-overview.png` and `boreas-finish-basin.png`.

The newer full-route renderer comparison at 1,600 × 900 reduced CPU submission mean from **0.497 ms to 0.264 ms**, p95 from **1.0 ms to 0.5 ms**, while retaining the original geometry. See [rendering performance](RENDER-PERFORMANCE.md) for GPU measurements, stable quality settings, before/after pixel comparisons, reproducible scripts, and limitations. These numbers describe this machine and test, not a guaranteed end-user frame rate, input-to-photon latency, or the complete simulation/UI frame cost.

## Remaining visual differences

1. Two stock game textures are referenced but not packed: `nature/dirtfloor005b` and `models/props/cs_office/clouds`. The missing secondary dirt layer falls back to the primary rock material. The missing translucent cloud surface is omitted instead of drawing an opaque white substitute.
2. Original diffuse baked lighting is retained, but Source's directional bumped-lightmap basis, SSBump lighting and exact vertex-light color conversion are not reproduced. HDR exposure/tonemapping and Source-specific lighting overbright behavior are not claimed equivalent.
3. Displacement alpha is retained, while blend-modulation textures and exact Source seamless sampling are approximated. Displacement normals are recomputed; Source's cross-displacement smoothing details can differ.
4. Cubemap reflections and tangent-space normal perturbations are approximations. Exact CSS reflection contrast, saturation, Fresnel, probe orientation and material shader permutations have not received pixel-level comparison.
5. Refractive ice and water use transparent reflected surfaces. Scene-color refraction, water reflection/refraction render targets and animated normal maps are not implemented.
6. Brush entities appear at their authored initial transforms. Rotating symbols, animated material proxies, moving decoration, dust/particles, laser beams and soundscapes are not reproduced. The original finish symbols are present but their animated effects differ.
7. Rendering uses full model LOD 0 and spatial/frustum culling, rather than Source's PVS, model LOD selection and displacement tessellation. This preserves collision-scale geometry but can differ in distant appearance and GPU cost.

No direct CSS screenshot/pixel comparison or CSS renderer telemetry was available for this visual import. Browser inspection establishes that the actual imported scene renders coherently; movement fidelity rests on its separate evidence.