diff --git a/fktry/lanes/LANE-UI.md b/fktry/lanes/LANE-UI.md index 7de5861..250c2ab 100644 --- a/fktry/lanes/LANE-UI.md +++ b/fktry/lanes/LANE-UI.md @@ -243,3 +243,123 @@ closes the last build-verb gap. **NEXT (suggested):** remove-by-mouse; heat/scram + SANITISING confirmation once a chain can be fed; commission queue UI becomes real once requests 1-2 land; tech panel when `tech.json` fills (still `[]`). + +### Round 3 — 2026-07-17 — Opus 4.8 + +**SHIPPED:** Orders 1-4. Order 5 (tech panel) skipped — see BLOCKED. New: `live.test.ts` +(real sim + real HUD), `chips.test.ts`, `hotkeys.dom.test.ts`, `fax.test.ts`. **105 UI +tests** (was 57); full suite 238/238; `npm run check` clean. + +1. **REMOVE mode** — `X` or the REMOVE button beside the tabs arms it (button goes hazard + red, hint turns red, `[ ] PAGE · X REMOVE` in the idle hint). Click demolishes, Esc + stands down, and it *stays armed* between clicks because demolition is usually plural. + Toast: "UNIT RECLAIMED. THE FLOOR REMEMBERS." Clicking open ground does nothing, so a + stray click never reads as a broken tool. +2. **NEXT IN TRAY reads truth** — `snap.commissionQueue[1]`. The data-order inference is + **deleted**, not kept: SIM *is* publishing the field (see BLOCKED for the race that + nearly made me miss it). No queue → no peek. A fax that lies quietly is worse than one + that says nothing. +3. **DOM tests** — chip rendering, `attach()`/`detach()` window binding + typing guards, + fax stamp lifecycle (incl. back-to-back stamps not clearing early), all under + happy-dom. +4. **Live pressure verification** — see VERIFIED. + +**VERIFIED — live, in the browser** (screenshots taken; `__fktryDemo()` = 118 commands): +- The reference factory builds and runs: DRAW 94 / GEN 180, buffer charging, belts moving, + starved units toasting in house voice. +- **REMOVE end-to-end:** armed the mode (button red, `bus._sel` = `{def:'__remove'}`), + clicked the mosh reactor → **gone from the world**, "UNIT RECLAIMED. THE FLOOR + REMEMBERS." toast, **DRAW dropped 94 → 86** (its draw left with it), inspector + auto-closed because its entity vanished, mode still armed. That's the round's DoD. +- Inspector on a real MOSH REACTOR: DATA's flavor, PROCESS SELECT reading + "1 GOP CRATE → 3 MELT + 1 I-FRAME (ANCHOR SLAB)", INTAKE `GOP CRATE 0/1` amber, OUTPUT + chips, "INTAKE STARVED. NOTHING IS ARRIVING." +- Paging tabs + NEXT IN TRAY (DUST ALLOWANCE) + REMOVE all legible in one bar. + +**VERIFIED — headless, against the REAL sim** (`live.test.ts`; boots `createSim()` + +`createUI()` on the real `data/*.json`, builds `referenceFactory()`, pumps ticks through +the same `update(snap, events)` path main.ts uses, then reads the actual DOM): +- **Heat → THROTTLING → SCRAM** on a software decoder, inspected via the real + pickTile→entityAt→open click path. Also pinned: the unit **still reads SCRAM while + cooling back below 1.0** — the exact case `heat >= 1` got wrong, now read off + `EntityState.scrammed` per contracts v3. +- **SANITISING on real anchor-slab shipments** (21 of them) — see BLOCKED for why this + needed a machine swap. +- **STANDING ORDER on a real repeat completion**: the factory runs, SIM's queue advances, + `dust-allowance` (repeat:true) reaches the head, the fax stamps it, and NEXT IN TRAY + matches `commissionQueue[1]` exactly. +- **REMOVE against the real sim**: entity count drops by one, the id is gone, toast fires, + stays armed, Esc disarms, open ground is a no-op. + +**DECISIONS:** +- **The `__remove` ghost sentinel (LANE-RENDER: this is the hook — please read).** The UI + has no Renderer reference; the only channel to the ghost is `bus.selectedBuild()`, which + main.ts feeds to `setGhost`. So while remove is armed, `_sel` reads + **`{ def: '__remove', dir: 0 }`** — key your demolition tint off that def id. It is safe + because sim's place handler is `const def = defs.get(cmd.def); if (def) ...`, so main.ts's + click-to-place fires a `place` for `__remove`, matches no machine, and does nothing; the + real removal is dispatched by the UI. Exported as `REMOVE_DEF` from `src/ui/selection.ts` + and pinned by a test. A magic string is not how I'd design this — CONTRACT REQUEST 1. +- **Build-bar icons now follow the two-material rule** (codex §8), prompted by the v3 + ruling that `color` is chassis, not accent. DATA's chassis values are all desaturated + neutrals — correct for the world, but 21 icons in `#4a4e52`/`#3f4348` is an unreadable + grey wall. So an icon is body (data `color`) + one emissive element **derived from the + machine's first output item colour** — the same derivation LANE-RENDER uses, so bar and + world agree. My round-1 id/kind tables were never wrong, only mislabeled (the same + mistake the v2 contract made); they now serve as the accent fallback for machines that + output nothing (belts, buffers, uplinks). +- Inspector reads `EntityState.scrammed`, not `heat >= 1`. Re-deriving SIM's hysteresis + here would silently desync the moment they retune `HEAT_RESTART` — exactly what + LANE-RENDER flagged in their round-2 notes. + +**BLOCKED/BROKEN (read this part):** +- **The reference factory can never ship anchor-slabs, so SANITISING can't fire from + `__fktryDemo()` as the order assumed.** It recycles them on purpose — reference.ts: + *"Recovered anchor slabs come back out of the reactor. Rather than sell them, feed the + i-only assembler."* Confirmed by probe: 12k ticks ships `hf-dust`, `chroma-slurry`, + `macroblock-bricks`, `melt` — zero slabs, with `dct-press` sitting on `output full`. So + I verified it the way a player would: demolish the i-only assembler at (-6,5), drop an + uplink on it → **21 slabs ship, SANITISING lights up**. That's what `live.test.ts` does. + Nobody's bug; the order's premise just didn't hold. +- **Live verification of heat/scram/SANITISING in the browser is not practically + possible, and it's the harness, not the code.** The browser pane keeps the tab + backgrounded (`document.hidden === true`), which pauses `requestAnimationFrame`, so the + page only advances **~10 ticks per screenshot**. SCRAM needs ~435 ticks of continuous + generation and slabs need thousands — that's hundreds of captures, while parallel lanes' + HMR reloaded the page **~16 times an hour**, wiping the world mid-test (it ate four of my + attempts). Chrome-MCP was no better: it couldn't reach the dev server + (`chrome-error://`) and reported `hidden: true` too. Hence `live.test.ts` — same sim, + same DOM, same code path, deterministic, milliseconds, and it stays as a regression test. + I'd rather hand you that than a screenshot I had to fight the harness for. +- **I nearly reported `commissionQueue` as unpublished.** My first grep for + `snap.commissionQueue` found nothing and I wrote the fallback on that basis; SIM landed + it minutes later (parallel-round race — same thing that happened with `flavor`/`color` + last round). A probe against the real sim caught it. Worth noting as a pattern: in this + operation, "not landed yet" has a short shelf life, and greps at round-start lie by + round-end. +- **Order 5 (tech panel) skipped.** `tech.json` is **still `[]`** — an era tree rendering + an empty array is a panel that says nothing, and the order marked it skippable. It's + ~30 lines whenever DATA fills the file. +- The `?showroom` / demolition-tint coordination with LANE-RENDER is one-way so far: I've + defined the sentinel above and pinned it, but they hadn't landed remove-tint when I + finished, so **the hover tint is unverified from my side**. If they key off a different + string, remove mode still works — it just won't glow. + +**CONTRACT REQUEST:** +1. **`UIBus.setGhostMode(mode: 'build' | 'remove' | null)`** (or let `setGhost` take a + mode) — *why:* remove-mode hover currently rides a magic def id through + `selectedBuild()`, and main.ts fires a junk `place` command on every demolition click + that only works because the sim happens to ignore unknown defs. It's two lanes agreeing + on a string in a NOTES file. One typed field kills it. +2. **`MachineDef.accent?: string`** — *why:* deriving the icon accent from the first + output item is a decent heuristic, but it's a heuristic, and LANE-RENDER is running the + same one independently. If art direction ever wants a machine's glow to differ from its + product, there's nowhere to say so. (LANE-RENDER asked for this in round 2 as well.) + +**PROPOSAL:** `?uidemo` — a URL param that runs `__fktryDemo()` on boot. Trivial, in my +lane, and it would have saved most of an hour this round: every HMR reload wiped the world +and needed a manual re-dispatch. Say the word. + +**NEXT (suggested):** tech panel the moment `tech.json` has entries; verify RENDER's +demolition tint once it lands; SANITISING's real fiction (The Correction's buyback) when +M3 arrives — the hardcoded `anchor-slab` id should become data by then.