[render] round 3 NOTES + MODELBEAST work order

Verification numbers, the prod-leak postmortem, and the showroom's contents as
a 23-row asset manifest (asset key / era / footprint) — every row a live
hot-swap target. One contract request (setGhostMode), filed jointly with UI.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
type-two 2026-07-17 18:36:06 +10:00
parent d1c3f2b09d
commit 82d9ab2b01

View File

@ -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/<asset>` 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.