diff --git a/fktry/lanes/LANE-RENDER.md b/fktry/lanes/LANE-RENDER.md index 8c07c29..60a65d7 100644 --- a/fktry/lanes/LANE-RENDER.md +++ b/fktry/lanes/LANE-RENDER.md @@ -282,3 +282,118 @@ building; I loaded it in the dev page and dispatched all 114 commands): - Per-era chassis skinning (§8 era skins) once tech/eras land; splitter output lamp driven by real round-robin state rather than a timer; bloom-loop belt tell; HF dust piles; parity-mite wildlife; LOD/culling if a real megabase ever drags the frame. + +### Round 3 — 2026-07-17 — Opus 4.8 + +**SHIPPED** — all 4 orders, verified in the running build. + +1. **Scram from truth.** `SCRAM_RELEASE` and the whole local latch are gone; the + renderer reads `EntityState.scrammed`. SIM wired the field mid-round (their + `state.scrammed` at index.ts:489/494) so this is verified against their real latch. + The crisp test: **heat 0.6 + `scrammed: true` → dead-dark, amber, idle frozen**, and + the same heat with `scrammed: false` → alive at body emissive 1.5 (= `1 + 0.6²×1.4`). + Same heat, opposite field, opposite visual — no threshold left in this lane to drift. + (`e.scrammed ?? heat >= 1` remains only as a bridge for a sim that hasn't set it; it + carries no constant.) +2. **Remove mode.** `setGhost('__remove', …)` → hazard-striped footprint decal + red + volume wash (`remove.ts`). Verified through UI's real flow: clicked their REMOVE tab, + `selectedBuild()` returned `{def:'__remove',dir:0}`, their hint bar switched to + "REMOVE ARMED · LMB DEMOLISH · ESC STAND DOWN", and hovering the mosh reactor lit its + **whole 3×3** (decal scale [3,3], wash [2.82,0.8,2.82], centred on the machine at + [5.5,9.5] — not the hovered tile). Bare ground shows nothing. +3. **`?showroom`** — 23 machines + 51 items on labelled plinths, camera preset, and it + lands MODELBEAST drops live (hot-swap + normalise + AnimationMixer + era skin all + already run through it). See the manifest below. +4. **Era skins** (`era.ts`) — plumbing, derived from data, verified live. + +**VERIFIED** +- Perf standing rule holds: `?devscene=stress` → 500 entities + 2,000 items at + **3.3ms/frame**, 5 instanced draw calls. (R1 3.2 / R2 3.6 / R3 3.3 — topology, heat, + mixers, eras and remove-mode have cost nothing measurable.) +- **Era skins live across all five eras**, each matching its `ERA_SKIN` entry exactly: + reel telecine-puller `r0.65/m0.45` (sepia'd to #6e491f), broadcast field-loom + `r0.8/m0.1` (phosphor #2e5132), disc mosh-reactor `r0.2/m0.65` (gloss), fortress muxer + `r0.95/m0`. **Stream is a true identity skin** — quantizer chassis reads `#5e5148`, + byte-for-byte DATA's authored value, so nothing changed for the 16 stream machines. +- Prod bundle clean: `devscene` 0, `showroom` 0, `plinth` 0, `window.__render` 0 hits + (the 4 `__renderTarget` matches are three.js's own). + +**DECISIONS** +- **`BeltItem.id` fallback retired.** Identity is now always the id, which also deleted + the per-frame sort of every belt item — the order-matching path only ever existed to + reconstruct identity the contract didn't carry. +- **The showroom fabricates a SimSnapshot** rather than hand-building display meshes, so + machines go through the real EntityLayer. A QA page with its own bespoke display path + would be testing itself instead of the renderer. Items aren't entities (no `asset`, + never hot-swap) so those are built directly — but from beltitems.ts's own factories, + so the showroom shows literally what rides the belts. +- **Camera preset frames the MACHINE hall, not everything.** A view wide enough to hold + all 51 items renders every machine too small to judge, which defeats the point. Items + are one pan south. Labels are collision-culled front-to-back: ~74 of them cannot all + fit at overview zoom, and a readable subset that resolves as you zoom beats a pile. +- **`__remove` is duplicated, not imported.** A renderer importing UI internals is the + wrong layering and there's no contract field yet. Unlike a tuning constant, drift here + fails loudly and visibly. Both lanes filed for a real hook (see request 1). +- Era skin touches the **chassis only** — the accent stays the colour of what the machine + makes, because that's the resource, not the decade. + +**BLOCKED/BROKEN** — nothing blocking. +- **A dead debug hook leaked into prod and I nearly shipped it.** Hoisting the scaffold + check into a `debug` VARIABLE defeated Rollup's constant folding, so `window.__render + = {scene, gl, topo, …}` survived in `dist/` as unreachable dead code (the class itself + still tree-shook). Round 2's `if (devSceneEnabled())` folded because the call was + inline. Fixed by testing `import.meta.env.DEV` literally at the branch. Flagging the + shape, not just the fix: **any** scaffold guard that goes through a variable will do + this silently, and only grepping the bundle catches it. +- Merge pieces still don't route cargo per-inlet (needs `BeltItem.side`, deferred — + agreed). +- The showroom's item hall is laid out for 51 items; if DATA doubles them again the rows + will run past the world edge and want paging. Not urgent, noted so it isn't a surprise. + +**CONTRACT REQUEST** +1. **`UIBus.setGhostMode(mode)` or a 4th `setGhost` arg** — LANE-UI filed the same one + independently (`src/ui/selection.ts` documents their half). Today remove-mode rides a + magic string through `selectedBuild()`, and main.ts fires a `place` for `__remove` + every click that the sim discards. It works and it's documented on both sides, but + it's a protocol living in two lanes' comments instead of in the contract. +2. Not urgent: nothing else. v3 granted both of round 2's asks and they both landed + clean. + +**MODELBEAST WORK ORDER** — this is `?showroom`'s content, and every one of these is a +live hot-swap target today: drop `public/assets/models/` and it appears in ~10s, +no reload, no code. Normalisation means any export scale lands correctly; multi-mesh and +rigged+animated GLBs both work (verified round 2). Footprint is the tile budget. + +| asset | era | fp | machine | +|---|---|---|---| +| `asic-cooler.glb` | broadcast | 2x2 | ASIC COOLER | +| `field-loom.glb` | broadcast | 3x2 | FIELD LOOM | +| `artifact-bottler.glb` | disc | 2x2 | ARTIFACT BOTTLER | +| `mosh-reactor.glb` | disc | 3x3 | MOSH REACTOR | +| `subsampler.glb` | disc | 2x3 | SUBSAMPLER | +| `muxer.glb` | fortress | 5x5 | THE MUXER | +| `telecine-puller.glb` | reel | 2x2 | TELECINE PULLER | +| `belt.glb` | stream | 1x1 | STREAM BELT | +| `bloom-duplicator.glb` | stream | 1x1 | P-FRAME DUPLICATOR | +| `buffer-tank.glb` | stream | 2x2 | BUFFER TANK | +| `dct-press.glb` | stream | 3x3 | DCT PRESS | +| `decode-asic.glb` | stream | 2x2 | DECODE ASIC | +| `demosaicer.glb` | stream | 3x2 | DEMOSAICER | +| `demuxer.glb` | stream | 2x2 | DEMUXER | +| `gop-assembler.glb` | stream | 3x2 | GOP ASSEMBLER | +| `hex-splicer.glb` | stream | 3x3 | HEX SPLICER | +| `lane-splitter.glb` | stream | 1x1 | LANE SPLITTER | +| `mv-extractor.glb` | stream | 2x2 | MV EXTRACTOR | +| `p-caster.glb` | stream | 2x2 | P-CASTER | +| `quantizer.glb` | stream | 2x2 | QUANTIZER | +| `seam-extractor.glb` | stream | 2x2 | SEAM EXTRACTOR | +| `shipper.glb` | stream | 2x2 | SCREEN UPLINK | +| `software-decoder.glb` | stream | 2x2 | SOFTWARE DECODER | + +Hero assets worth `fal_trellis2` (codex §9): `muxer` (5x5, the rocket), `mosh-reactor`, +plus the Fortress kit when it exists. Everything else is `sf3d` territory. + +**NEXT (round 4 candidates, not self-assigned)** +- Real MODELBEAST assets through `?showroom`; splitter lamp driven by real round-robin + state instead of a timer; bloom-loop belt tell; HF dust piles; wildlife; save/load + camera state; LOD/culling if a megabase ever drags the frame.