# Lane C (Levels & Encounters) — progress

## 2026-07-16 — Round 1: the data spine + the vertical slice

Landed:
- **Schema v1 ratified** (TECH.md §Level data schema, C-owned from here). Additive over F's
  v0: `schema`, `par`, `next`, segment `name`, cross-section placement (`theta`/`rho`, rho as
  a *fraction* of the safe radius), group `spread`, zone `span`, `pickup`/`gate` event types,
  hazard params. STUB_LEVEL synced to v1 (only that const in F's file, per ROUND1 §Lane C.1).
- **`levels/index.js`** — registry + loader (node + browser, no THREE), **schema selfcheck**
  (real; qa.sh's `--selfcheck` hook is now live) and a **pacing simulator** (`--sim`).
  The selfcheck consumes the other lanes' contracts live: A's `world/biomes.js` for biome ids
  and `wave.amp + wave.breathe`, A's `SKIN`, B's `flight/tuning.js` hull radius, B's
  `combat/balance.js` for scores. C's registry owns composition and mirrors nothing.
- **`levels/enemies.js`** — enemy catalogue (fiction id → archetype) + pickup/hazard vocab.
- **`levels/difficulty.js`** — the single difficulty table + `applyDifficulty()` (authored,
  not yet validated against play).
- **`L2_esophagus.json`** — the vertical slice, complete: 3600 units, 7 beats, 47 events,
  65 enemies, 10 checkpoints, 3 samples, reflux-surge finale. `L1_mouth` / `L3_stomach`
  skeletons (shape only, flagged SKELETON — the selfcheck downgrades content laws for them).
- **GDD refined in place** (bannered): per-level time targets replacing a flat "6–10 min",
  esophagus roster, L2's three real constrictions as its setpieces.
- **Boss specs** (Guardian / Tapeworm / Blockage) as one-pagers in NOTES — phases,
  telegraphs, windows, failure pressure. Deliberately three *different* pressures: coat race,
  distance race, hull attrition + doom timer.

Verification (measured, `node web/js/levels/index.js --selfcheck|--sim`; qa.sh GREEN):
- selfcheck **3/3 valid, exit 0**; 6 warnings, all expected (skeletons undesigned, L4 not yet
  registered).
- L2 pressure curve **0.27 → 2.14 → 4.35 → 0.69 → 4.53 → 6.35 → 4.40** (breather → pressure →
  spike → breather → pressure → peak → finale).
- L2 par 3:30 against a measured **2:47 speedrun / 3:19 par-pace / 4:22 timid** run;
  par.score 9000 of a 9400 kill budget (95%), computed from B's scores.
- All 10 checkpoint gaps within the ≤30 s death-cost law (max 28.8 s). Min clearance 2.73 at
  the hiatus, against C's 2.5 floor.
- Booted `?stub=1&dbg=1` after the STUB_LEVEL edit: tube renders, zero console errors, and
  the v1 module builds a valid world (`length 400`, `wallRho(0,0) = 8.5`, centre-line collide
  `null`). **`world.hash()` is byte-identical before and after my edit (`1bdf001`)** — the
  sync adds data fields only and provably does not move the geometry, so F's round-0 shot
  still stands. No screenshot committed: it would be pixel-identical to round 0's, and per
  charter a data lane's evidence is the pacing report, not a picture of an unchanged tube.
  (No fps claim: the preview pane throttles rAF in the background — `rafTicks: 0` — so
  framerate is unmeasurable here. Not a code fault; the render path is fine.)

Two contract collisions found and resolved (detail in NOTES):
- **Biome ids** — I had authored `mouth`/`pharynx`/`cardia`/`pylorus`; A's registry has six
  canonical ids and rejected all four. A is right: biome = look, `segments[].name` = anatomy.
  Conformed, and the constraint paid for itself (the cardia keeping the esophagus palette is
  why the acid sea reveals itself in colour).
- **Balance ownership** — my first `enemies.js` restated hp/size/speed/score that B already
  owns in `combat/balance.js`. Cut to a join table; `index.js` reads B's scores instead.

Caught live: A raised esophagus `wave.amp` 0.9 → 1.4 mid-session, which made the
diaphragmatic hiatus unflyable (2.33 free radius vs the 2.5 floor). Widened 6.5 → 6.8. The
invariant did its job on my own level twice — the aortic constriction also failed its first
draft (radius 7, symmetric squeeze) and became radius 9 with a *directional* squeeze.

Not done / next round: no in-engine flythrough — `?stub=1&lvl=` does not compose yet (snippet
offered to F) and the stub is single-segment, so it could only ever render beat 1. L2's
pacing evidence is a kinematic **model**, not a playtest: it proves the level is flyable,
paced and survivable; it cannot prove the hiatus *feels* good. That needs A's world + B's
controller in round 2 — and if one thing gets playtested, it should be s 2600–3000.
