[orchestrator] round 2 review + contracts v3 + round 3 orders
Review: 5/5 lanes pass. 160/160 tests, tsc clean, M1 COMPLETE — verified live: reference factory ships melt, commission queue advances with standing orders, SCREEN's new artifact passes respond item-specifically (block 0.46 from bricks). Contracts v3 (granted): EntityState.scrammed, SimSnapshot.commissionQueue, MachineDef.coolPerTick, setRecipeAt command, Sim.save/load, ScreenFX.frame(t, snap), BeltItem.id hardened required, MachineDef.color re-ruled as chassis/body. main.ts: passes snapshot to screen.frame; dev __fktryDemo() builds reference factory. happy-dom ACTUALLY installed this time (round-2 grant never landed; UI caught it). Rulings: bandwidth units are per-second (SIM converts); heat retune ordered (nothing can currently overheat - heatPerTick < ambient cooling); validator test moves to data/; RENDER's color deviation approved as the ruling; scram wipe 3 frames; REMOVE button approved. Round 3 orders in all lane docs. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
dc1591d0e6
commit
08761e8eeb
@ -59,7 +59,9 @@ codex wins.
|
||||
|
||||
## Milestones (orchestrator's map — lanes don't self-assign ahead)
|
||||
|
||||
- **M1 — FLOW** (rounds 1-2): the full canon melt chain — ore → demuxer →
|
||||
- **M1 — FLOW** ✅ COMPLETE (round 2; verified live round-2 review: reference factory
|
||||
ships melt, commission queue advances, SCREEN corrupts item-specifically): the full
|
||||
canon melt chain — ore → demuxer →
|
||||
{quantizer, subsampler, p-caster, dct-press} → gop-assembler → mosh reactor →
|
||||
shipper. RULING (round 1): LANE-DATA's codex-faithful chain replaces the original
|
||||
6-machine sketch; the ~50-entity build IS the fantasy. A deadlock-free reference
|
||||
|
||||
@ -9,9 +9,8 @@ codex wins; when the codex is silent, invent IN ITS VOICE and note what you inve
|
||||
validator that keeps five parallel lanes from drowning in dangling ids. Content should
|
||||
light up in-game with zero code changes from other lanes.
|
||||
|
||||
**Owned paths:** `data/**`, `src/sim/data-validate.test.ts` — EXCEPTION: this one test
|
||||
file in sim's directory is yours; it validates data, not sim. Nothing else in
|
||||
`src/sim/`.
|
||||
**Owned paths:** `data/**` (the validator now lives at `data/validate.test.ts`;
|
||||
the old `src/sim/data-validate.test.ts` exception is closed — round 3 moves it).
|
||||
**Never touch:** contracts, main.ts, other lanes' code. Schema changes are CONTRACT
|
||||
REQUESTs.
|
||||
|
||||
@ -26,46 +25,47 @@ REQUESTs.
|
||||
|
||||
---
|
||||
|
||||
## CURRENT ORDERS — Round 2 (goal: contracts-v2 content + M2 "PRESSURE" economy)
|
||||
## CURRENT ORDERS — Round 3 (goal: heat that matters, deeper economy)
|
||||
|
||||
Round 1 review: the validator's fault-injection and the FORWARD_CANON allowlist were
|
||||
exactly right. Verdicts on your report: **M1 IS your canon chain** — MASTERPLAN
|
||||
updated, no shortcut recipe, the depth is the game. Your four inventions
|
||||
(`press-anchor-slab`, `color-plate` + `v-hold-roll`, `artifact-bottler`,
|
||||
`lane-splitter`) are **CANONIZED**. Dials-as-recipes STAYS (right call; a real dial is
|
||||
a v3+ contract discussion). CONTRACT REQUEST GRANTED: `MachineDef.heatPerTick?` exists
|
||||
now — and your validator should now also accept `color?`, `flavor?`, `bufferCap?`,
|
||||
`CommissionDef.repeat?`.
|
||||
Round 2 review: the bloom-concentrate grep-before-delete and the fault-injection
|
||||
discipline are exactly the standard. Both your cross-lane asks are RULED:
|
||||
**(1) Units: per-second.** `gen`/`draw` are per second, `stored` is
|
||||
bandwidth-seconds; SIM converts their integration this round — your `bufferCap: 240`
|
||||
= 8 seconds of a 30 deficit, as you intended. **(2) Cooling:** `coolPerTick?` granted
|
||||
in v3 (sim default 0.004); SIM wires it. Your SIM's-test-is-red flag was correct
|
||||
mid-round and self-resolved at HEAD (their landing fixed it) — the stash-and-rerun
|
||||
diagnostic was the right move. Ownership RULING: `data-validate.test.ts` moves to
|
||||
`data/validate.test.ts` (git mv, yours — vitest picks it up there; `src/sim/**`
|
||||
returns fully to SIM).
|
||||
|
||||
1. **`data/CANON-DELTA.md`** (new file, yours): ledger of everything invented beyond
|
||||
the codex — round 1's four + whatever follows. The lore doc absorbs it in one
|
||||
orchestrator pass later; until then this is the single place canon-drift lives.
|
||||
2. **Art direction pass**: `color` on all 21 machines (renderer precedence:
|
||||
your color > derived-from-outputs) and `flavor` on all 21 — codex one-liner each,
|
||||
house voice. This kills UI's hardcoded lookup and RENDER's black-on-black problem.
|
||||
3. **Heat economy**: `heatPerTick` on software-decoder, and scale its `powerGen` so
|
||||
hot-but-mighty vs ASIC cool-and-steady is a real choice; a pinch of `heat` on
|
||||
hex-splicer recipes. SIM implements throttle/scram this round — your numbers decide
|
||||
whether anyone ever dares run two software decoders side by side.
|
||||
4. **`bufferCap` on buffer-tank** — one tank should ride out roughly 10 seconds of
|
||||
typical M1-chain deficit. Show your arithmetic in CANON-DELTA.
|
||||
5. **Commission queue content** (SIM lands queue + `repeat` this round): reorder for
|
||||
a difficulty ramp; add 2-3 `repeat:true` standing orders, including **THE
|
||||
CORRECTION BUYBACK** — pays generous bandwidth for anchor-slabs, fax written in The
|
||||
Correction's voice: polite, clean, quietly horrifying. This is the sellout loop
|
||||
(ship your anchors for power now, watch your SCREEN go clean — SCREEN lane already
|
||||
sanitizes on anchor shipments).
|
||||
6. **Container line**: `moov-shard` / `offset-ribbon` / `sync-key` items + recipes
|
||||
assembling `container-pack` (unblocks `stream-hex-splicing`); prune the
|
||||
FORWARD_CANON allowlist as they gain recipes.
|
||||
7. **Bloom line**: `bloom-grade-1..5` items + bloom-duplicator recipes
|
||||
(grade n + delta-wafers → grade n+1); grade 5 is commission bait. Validator covers
|
||||
all new content.
|
||||
1. **The validator move** above, first, so the tree is clean.
|
||||
2. **Heat retune, the headline**: today NOTHING can overheat — every `heatPerTick`
|
||||
you authored is below the 0.004 ambient cooling, so heat is decorative. With
|
||||
`coolPerTick` yours to set per machine: hit SIM's suggested feel — software
|
||||
decoder throttles after ~10s of continuous load, scrams ~13-15s, full duty cycle
|
||||
~25s; ASIC stays incapable of heat. Show the arithmetic in CANON-DELTA (per-tick
|
||||
vs per-second — mind the units ruling). Give the hex-splicer a lesser fever.
|
||||
Coordinate with SIM's reference-factory v3 via NOTES — your numbers decide
|
||||
whether their layout survives.
|
||||
3. **Buffer economy check** under the units ruling: is 240 (8s) still your intent
|
||||
now that it's true? One tank should cover one decoder scram — verify against your
|
||||
retuned cycle, adjust, show working.
|
||||
4. **ASIC COOLER spec'd** in CANON-DELTA (the codex names it, §2 "vented by ASIC
|
||||
coolers"): item/machine entry + intended adjacency mechanic, data authored but
|
||||
recipes gated behind a `broadcast`-era tech node — the MECHANIC stays deferred
|
||||
until SIM builds auras (M4); the data existing first is fine and the validator's
|
||||
FORWARD_CANON pattern covers it.
|
||||
5. **Reel + broadcast era tech nodes**: research costs in analog packs; unlocks
|
||||
field-loom/telecine recipe families you already authored.
|
||||
6. **Muxer + moov brain sub-lines** (§7): `stsz-ledger`, `stco-map`, `stts-metronome`,
|
||||
`stss-keyring` items + assembly recipes converging on a `moov-brain` →
|
||||
`mux-container` endgame recipe on the muxer. This is M4's rocket — data lands
|
||||
first, mechanics later. Muxer machine entry if you don't have one.
|
||||
|
||||
**Definition of done:** validator green over the expanded catalog; every machine has
|
||||
color + flavor; the queue tells a story from first-taste to the first buyback; SIM's
|
||||
reference-factory test still passes against your rebalanced numbers (coordinate via
|
||||
NOTES if a ratio change breaks their layout).
|
||||
**Definition of done:** validator green from its new home; a software decoder
|
||||
demonstrably overheats in SIM's tests using YOUR numbers (coordinate — their DoD
|
||||
depends on your retune); CANON-DELTA carries the arithmetic; FORWARD_CANON pruned of
|
||||
anything that gained a recipe.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@ -25,33 +25,35 @@ you never simulate. If motion looks wrong, interpolate better or note it — don
|
||||
|
||||
---
|
||||
|
||||
## CURRENT ORDERS — Round 2 (goal: finish M1 polish, open M2 "PRESSURE")
|
||||
## CURRENT ORDERS — Round 3 (goal: truth-driven states + asset intake readiness)
|
||||
|
||||
Round 1 review: excellent — the hot-swap verification and the fog/CAM_DIST honesty
|
||||
note were exactly right. Verdicts: req 1 GRANTED (`BeltItem.id?` in contracts v2 —
|
||||
use when present, keep order-matching as fallback until v3 hardens it). Req 2 GRANTED
|
||||
(`MachineDef.color?` — precedence: `def.color` > your derived-from-outputs >
|
||||
kind accent; your derivation stays as the fallback, it was the right instinct).
|
||||
Req 3 DONE (main.ts ignores non-left buttons; drop your camera.ts workaround or keep
|
||||
it as belt-and-braces, your call).
|
||||
Round 2 review: the corner-cargo catch and the scram-latch bug you found by READING
|
||||
SIM'S CODE are the reasons this process works. Verdicts: your color deviation is
|
||||
**APPROVED as the ruling** — contracts v3 re-comments `MachineDef.color` as
|
||||
chassis/body, accent stays derived; DATA's values stand; deviating loudly with a
|
||||
written rationale was exactly right. Req 2 GRANTED: `EntityState.scrammed` is in v3 —
|
||||
SIM wires it this round. Req 3 (`BeltItem.side`) DEFERRED until merges matter.
|
||||
`BeltItem.id` is now required, so your fallback path can retire whenever you like.
|
||||
|
||||
1. **Belt topology visuals**: corner/junction pieces chosen from neighbor analysis —
|
||||
pure presentation, sim truth unchanged. Splitters (mechanics land this round in
|
||||
SIM) get a visible output-cycling tell.
|
||||
2. **Heat**: shimmer + dull-red emissive ramp driven by `EntityState.heat`; throttled
|
||||
= idle animation visibly slowed; scram = dead-dark + blinking amber until restart.
|
||||
3. **Buffer tank**: visible fill level (shader window or scale trick) proportional to
|
||||
the tank's share of `snapshot.bandwidth.stored`.
|
||||
4. **AnimationMixer**: play clip 0 on rigged GLBs with an idle loop — MODELBEAST
|
||||
assets are imminent; verify with any animated GLB you can find or make.
|
||||
5. **Exact cargo interpolation** via `BeltItem.id` — delete the one-tick approximation.
|
||||
6. **Multi-mesh belt GLBs**: merge-or-group fix (your call, note it) so a fancy belt
|
||||
asset can't half-render.
|
||||
1. **Scram from truth**: read `EntityState.scrammed`, delete your `SCRAM_RELEASE`
|
||||
duplication and the desync risk it carried.
|
||||
2. **Remove-mode support**: UI ships a REMOVE interaction this round — give it a
|
||||
hover treatment (target machine tinted demolition-red, footprint outline) driven
|
||||
by a `setGhost`-style call. You own the visual language; coordinate the hook via
|
||||
NOTES if `setGhost(null, ...)` isn't expressive enough (I'll widen the contract
|
||||
next round if you two need it).
|
||||
3. **Showroom formalized**: promote the devscene showroom row to `?showroom` — every
|
||||
machine + every item on plinths, labeled, camera preset. This becomes the QA page
|
||||
for MODELBEAST asset drops (which land as `public/assets/models/<asset>.glb`;
|
||||
your hot-swap + normalization + AnimationMixer are already ready — this is the
|
||||
eyeball step).
|
||||
4. **Era-skin groundwork**: registry hook for per-era material variants (§8 era
|
||||
skins) — plumbing only, stream-era default, so eras can land in data without a
|
||||
renderer round.
|
||||
|
||||
**Definition of done:** reference factory (SIM ships it this round — coordinate via
|
||||
their `src/sim/reference.ts` export in a dev page or test harness) looks ALIVE: corners
|
||||
read as corners, heat reads as heat, tanks read as tanks; perf standing rule still met
|
||||
under `?devscene=stress`.
|
||||
**Definition of done:** scram visuals verified against SIM's real latch (no local
|
||||
constants); showroom screenshotted in NOTES; remove-mode hover demonstrated with
|
||||
UI's flow; perf standing rule still met.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@ -27,36 +27,33 @@ seed stock — port, don't move).
|
||||
|
||||
---
|
||||
|
||||
## CURRENT ORDERS — Round 2 (goal: artifact fidelity, M2 "PRESSURE" hooks)
|
||||
## CURRENT ORDERS — Round 3 (goal: continuous dread + spatial artifacts)
|
||||
|
||||
Round 1 review: the pixel-reading verification set the bar for the fleet. Verdicts:
|
||||
your anchor-slab PROPOSAL — option (b) ADOPTED as M3 fiction (The Correction buyback;
|
||||
DATA writes the standing order this round); LANE-UI surfaces a SANITISING chip
|
||||
meanwhile; keep potency −2.0 as shipped. Debug hook: KEEP, gated behind
|
||||
`import.meta.env.DEV`. The 2-red you flagged was mid-round churn — HEAD is 70/70.
|
||||
The launch.json affair: resolved, per-lane ports are in MASTERPLAN now; you're on 8154.
|
||||
Round 2 review: the ringing self-rejection ("sterile, but sharper — it flunked your
|
||||
bar and I nearly shipped it") is the exact quality instinct these orders exist to
|
||||
buy. Verdicts: CONTRACT REQUEST GRANTED — `frame(timeMs, snap?)` is in v3 and main.ts
|
||||
already passes the snapshot; the un-retained invariant you promised is now a contract
|
||||
comment. Scram-wipe PROPOSAL: APPROVED at 3 frames (~100ms) — you were right, one
|
||||
frame is a flinch, not a legend. Regional-moiré PROPOSAL: APPROVED as this round's
|
||||
centerpiece.
|
||||
|
||||
1. **Artifact passes the 8 params can't express** (your list, adopted wholesale):
|
||||
- legible 8×8 **macroblock quantization** pass — bricks/coeff overdrive must READ
|
||||
as a blocky grid, not generic noise;
|
||||
- edge-detected **ringing halos** (Gibbs oscillation hugging contrast edges);
|
||||
- **moiré interference lattice**.
|
||||
Wire signatures into corruptionMap: `macroblock-bricks`, `ringing-halos`,
|
||||
`moire-crystal`. Keep the per-item legibility bar from round 1: shipping 40 bricks
|
||||
should look UNMISTAKABLY different from 40 slurry.
|
||||
2. **Shipment lands**: a ~0.4s stamp/silhouette flash in the item's chip color
|
||||
composited on the feed per `shipped` event (batch bursts: one stamp per event, not
|
||||
per unit — a 40-unit shipment is one big stamp, not 40 flickers).
|
||||
3. **Scram wipe**: `scram` events (new in contracts v2) get a one-frame white
|
||||
"TRACKING LOST" bar wipe. Cheap, from the event stream you already have.
|
||||
4. **OPTIONAL — signal strain**: buffer tanks land this round; if you want THE SCREEN
|
||||
to tremble as `bandwidth.stored` drains BEFORE brownout, file the snapshot-access
|
||||
CONTRACT REQUEST you deferred (`frame(timeMs, snap?)`) and build it next round —
|
||||
or skip it. Your call; the seam is noted and I'll grant it in v3 if asked.
|
||||
1. **Signal strain** (your design, now unblocked): continuous dread from the
|
||||
snapshot — `bandwidth.stored` draining toward zero = rising tremor/desync judder
|
||||
BEFORE the brownout seizure; aggregate entity `heat` = a slow feverish shimmer.
|
||||
Keep it subordinate to the corruption ledger: strain is weather, shipments are
|
||||
climate.
|
||||
2. **Regional moiré**: grow the lattice from a region instead of full-frame (the
|
||||
codex has it spreading as maze walls) — spatial mask, seeded deterministically,
|
||||
creeping outward with tally. This is the template for future spatial artifacts;
|
||||
note the mask approach for reuse.
|
||||
3. **Scram wipe at 3 frames**, as approved.
|
||||
4. **Idle strain baseline**: with zero corruption AND healthy bandwidth, the feed
|
||||
stays perfectly sterile — verify strain can't leak into the NOTHING IS WRONG era
|
||||
(a test pinning params at zero under a healthy snapshot would do it).
|
||||
|
||||
**Definition of done:** three new artifact classes verified the way you did round 1
|
||||
(drive, read pixels, screenshot); stamps and scram wipe visible on the dev build;
|
||||
frame budget still <2ms; tests updated for new signatures.
|
||||
**Definition of done:** strain visible as stored drains (drive it by pausing the
|
||||
reference factory's generators or just feeding a fixture snapshot), regional moiré
|
||||
grows rather than switches, wipe readable, budget still <2ms, tests updated.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@ -21,39 +21,39 @@ playable through unit tests. You are the lane the other four trust with their li
|
||||
|
||||
---
|
||||
|
||||
## CURRENT ORDERS — Round 2 (goal: finish M1, open M2 "PRESSURE")
|
||||
## CURRENT ORDERS — Round 3 (goal: M2 "PRESSURE" becomes real, M3 groundwork)
|
||||
|
||||
Round 1 review: outstanding. Verdicts on your report — **rank-routing STAYS** (it made
|
||||
M1 flow; your proposal 2, auto-fallback, is REJECTED for exactly the reason you gave:
|
||||
it deletes the byproduct tension). Proposal 3 (starved) APPROVED. Contracts v2 landed
|
||||
for you: `BeltItem.id?`, `CommissionDef.repeat?`, `MachineDef.heatPerTick?/bufferCap?`,
|
||||
`SimEvent 'scram'`. Extractor seam tiles: deferred to M3 worldgen, stop worrying.
|
||||
Round 2 review: the reference factory turning round 1's deadlock into 1,136 shipped
|
||||
bricks is exactly what "surplus becomes product" was supposed to mean — outstanding.
|
||||
Verdicts: `coolPerTick` GRANTED (contracts v3, default 0.004 — your constant becomes
|
||||
the fallback). `setRecipeAt` GRANTED (your tripwire, defused). `BeltItem.id` HARDENED
|
||||
to required (you asked for it). Throttle-hits-powerGen: KEPT, your reading was right.
|
||||
The validator-file ownership wart: RULED — it moves to `data/validate.test.ts`, DATA
|
||||
executes the move, `src/sim/**` is purely yours again.
|
||||
|
||||
1. **Reference factory.** `src/sim/reference.ts` exporting
|
||||
`referenceFactory(data: GameData): Command[]` — your deadlock-free M1 layout
|
||||
(second quantizer on crime, second assembler on i-only). Test: ships melt within
|
||||
2,000 ticks AND is still shipping at 20,000 (the deadlock regression net). This
|
||||
becomes the official demo; I wire it to a dev hook in main.ts next round.
|
||||
2. **Splitters.** `kind:'splitter'` becomes real: 1 input, round-robin across outward-
|
||||
facing adjacent belts; a blocked output is skipped, not waited on; your rank cache
|
||||
traces THROUGH splitters (rank = best reachable downstream). This is the sanctioned
|
||||
surplus-voiding tool — the rank rule elsewhere is unchanged.
|
||||
3. **Buffer tanks.** `kind:'buffer'` + `bufferCap`: charge when gen>draw, discharge to
|
||||
cover deficit, brownout only when tanks run dry; `snapshot.bandwidth.stored` = pool.
|
||||
4. **Heat v1.** Accumulate `heatPerTick` while active + `RecipeDef.heat` per craft;
|
||||
ambient dissipation; heat>0.7 → per-machine throttle (curve is your call, note it);
|
||||
heat=1 → scram: machine stops, emits `{kind:'scram',on:true}`, restarts below 0.5
|
||||
with `on:false`. `EntityState.heat` is already in the contract — make it real.
|
||||
5. **Commission queue.** Data-order activation; `commissionDone` advances; `repeat:true`
|
||||
re-enters at the back of the queue. Progress counts only while active (keep).
|
||||
6. **`jammed:'starved'`** — recipe idle with empty inputs, edge-triggered like
|
||||
'output full'.
|
||||
7. **`BeltItem.id`** — stable per-item ids in every snapshot (optional field now;
|
||||
hardens to required in v3, so just always set it).
|
||||
**UNITS RULING (DATA's cross-lane ask):** `gen`/`draw` are bandwidth per SECOND;
|
||||
`stored` is bandwidth-seconds. Your integration currently charges/drains per tick —
|
||||
divide by `TICKS_PER_SECOND` so a 30-deficit drains 30 stored per second, not per
|
||||
tick. DATA's `bufferCap: 240` then covers 8s as they intended. Update tests; confirm
|
||||
the conversion in NOTES so DATA can retune against truth.
|
||||
|
||||
**Definition of done:** all of the above tested (determinism suite still green);
|
||||
reference factory test proves melt at scale with zero deadlock; heat demonstrably
|
||||
throttles and scrams the software decoder in a test.
|
||||
1. **Contracts v3 wiring**: `EntityState.scrammed` (RENDER is currently duplicating
|
||||
your 0.5 constant to avoid rendering a lie — kill that), `commissionQueue` on the
|
||||
snapshot (active first, then upcoming — UI's NEXT IN TRAY is guessing today),
|
||||
`coolPerTick` per machine, `setRecipeAt` command, the units conversion above.
|
||||
2. **Save/load**: implement `save()`/`load()` — JSON, deterministic round-trip
|
||||
(test: save at tick N, load into a fresh sim, run both 1,000 ticks, deep-equal).
|
||||
3. **Reference factory v3**: heat is about to become real (DATA retunes this round —
|
||||
coordinate via NOTES). Restructure power as ASIC base + software-decoder burst +
|
||||
buffer tank so the layout *survives scram duty-cycles* — DATA warned your three
|
||||
naked decoders will black out in lockstep. Keep the 20k regression green.
|
||||
4. **Bloom loop proof**: test that a closed belt circle through a bloom-duplicator
|
||||
accumulates grades (your rank-0 loop acceptance + DATA's grade recipes should
|
||||
compose). This is M3's signature topology — prove it early.
|
||||
|
||||
**Definition of done:** all tests green including save/load round-trip and the bloom
|
||||
loop; NOTES confirm the units conversion + the retuned reference factory's scram
|
||||
survival (ticks of first scram, recovery, melt rate before/after).
|
||||
|
||||
---
|
||||
|
||||
|
||||
@ -24,32 +24,39 @@ dispatch Commands via the bus and read state from `update(snap, events)`.
|
||||
|
||||
---
|
||||
|
||||
## CURRENT ORDERS — Round 2 (goal: finish M1 interaction, open M2 "PRESSURE")
|
||||
## CURRENT ORDERS — Round 3 (goal: close the interaction loop, verify the pressure UI live)
|
||||
|
||||
Round 1 review: strong, and the honesty section was the best in the fleet. Verdicts:
|
||||
CONTRACT REQUEST 1 GRANTED — `UIBus.pickTile` is live, wired to the renderer in
|
||||
main.ts. Req 2 GRANTED (`MachineDef.color?`). Req 3 GRANTED as `MachineDef.flavor?` —
|
||||
LANE-DATA fills it this round; read data first, keep voice.ts as fallback. Req 4
|
||||
GRANTED — `happy-dom` is installed; use `// @vitest-environment happy-dom` per test
|
||||
file (no root config change needed). Build-bar paging PROPOSAL: APPROVED.
|
||||
Round 2 review: 57 tests against a 10 minimum, and factoring pure modules instead of
|
||||
touching root config when my grant turned out to be vapor — model behavior. About
|
||||
that: **happy-dom genuinely is installed now** (verified: `package.json` line 17, and
|
||||
your 57 tests pass under `--environment happy-dom`). My round-2 "it's installed" was
|
||||
wrong and your BLOCKED section caught it — good catch, keep doing that.
|
||||
Verdicts: CONTRACT REQUEST 1 GRANTED — `SimSnapshot.commissionQueue` in v3, SIM wires
|
||||
it this round. Request 2 was already landing as you wrote it (parallel-round race;
|
||||
the queue advances at HEAD — I watched PATIO RESURFACING arrive live). REMOVE
|
||||
PROPOSAL: APPROVED.
|
||||
|
||||
1. **Click-to-inspect** via `bus.pickTile`. Delete the TAB-cycle fallback and
|
||||
`?uifixture` — the sim is real now; drive a brownout by building.
|
||||
2. **Build bar paging** (your design): kind tabs, `[`/`]` to page, 1-9 within the
|
||||
active page — every machine hotkey-reachable at any catalog size.
|
||||
3. **Inspector upgrades**: recipe picker (`setRecipe` finally gets its UI), heat bar
|
||||
with THROTTLING/SCRAM states, and starved-vs-backed-up wording (sim distinguishes
|
||||
`'starved'` from `'output full'` this round).
|
||||
4. **Commission queue UI**: current fax + a "NEXT IN TRAY" peek; `repeat:true`
|
||||
commissions get a STANDING ORDER stamp. When anchor-slabs ship, show a SANITISING
|
||||
chip (hardcoding the id is fine this round — the fiction becomes The Correction's
|
||||
buyback in M3).
|
||||
5. **Tests, minimum 10**: chips, roster/footprint hit-testing, selection, hotkey map —
|
||||
under happy-dom.
|
||||
1. **REMOVE**: your design — suggested shape: X hotkey / bar button arms remove mode,
|
||||
hover shows RENDER's demolition tint (they're building it this round — coordinate
|
||||
via NOTES), click dispatches `remove`, Esc disarms. Toast in house voice
|
||||
("UNIT RECLAIMED. THE FLOOR REMEMBERS.").
|
||||
2. **NEXT IN TRAY becomes truth**: read `snap.commissionQueue` when present, delete
|
||||
`peekNext` inference the moment SIM lands it (keep fallback until then).
|
||||
3. **DOM render tests**: the two you named as needing a real DOM (chip rendering,
|
||||
`attach()` binding) plus fax stamp lifecycle — `// @vitest-environment happy-dom`.
|
||||
4. **Live pressure verification** (now unblocked): `window.__fktryDemo()` builds
|
||||
SIM's reference factory in one call — use it. Verify with your own eyes and
|
||||
screenshot in NOTES: heat bar + THROTTLING + SCRAM on the software decoders once
|
||||
DATA's retune lands, SANITISING chip on real anchor shipments, STANDING ORDER
|
||||
stamp on a real repeat completion (I verified the stamp renders — you verify the
|
||||
full lifecycle).
|
||||
5. **Stretch, skippable**: read-only tech panel (`T`) rendering `tech.json` as an era
|
||||
tree marked RESEARCH OFFLINE — research mechanics land in M3, the panel can exist
|
||||
first.
|
||||
|
||||
**Definition of done:** the reference factory (SIM ships `src/sim/reference.ts` this
|
||||
round) is inspectable end to end by mouse alone; every machine reachable by hotkey;
|
||||
tests green; fixture deleted.
|
||||
**Definition of done:** a misplaced machine is mouse-removable; queue peek reads
|
||||
truth; the three DOM tests exist; NOTES carry screenshots of heat/scram/SANITISING
|
||||
observed live.
|
||||
|
||||
---
|
||||
|
||||
|
||||
112
fktry/package-lock.json
generated
112
fktry/package-lock.json
generated
@ -12,6 +12,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/three": "^0.170.0",
|
||||
"happy-dom": "^20.10.6",
|
||||
"typescript": "^5.6.0",
|
||||
"vite": "^6.0.0",
|
||||
"vitest": "^2.1.0"
|
||||
@ -869,6 +870,16 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "26.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-26.1.1.tgz",
|
||||
"integrity": "sha512-nxAkRSVkN1Y0JC1W8ky/fTfkGsMmcrRsbx+3XoZE+rMOX71kLYTV7fLXpqud1GpbpP5TuffXFqfX7fH2GgZREw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"undici-types": "~8.3.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/stats.js": {
|
||||
"version": "0.17.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/stats.js/-/stats.js-0.17.4.tgz",
|
||||
@ -898,6 +909,23 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/whatwg-mimetype": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/whatwg-mimetype/-/whatwg-mimetype-3.0.2.tgz",
|
||||
"integrity": "sha512-c2AKvDT8ToxLIOUlN51gTiHXflsfIFisS4pO7pDPoKouJCESkhZnEy623gwP9laCy5lnLDAw1vAzu2vM2YLOrA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/ws": {
|
||||
"version": "8.18.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz",
|
||||
"integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@vitest/expect": {
|
||||
"version": "2.1.9",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-2.1.9.tgz",
|
||||
@ -1001,6 +1029,19 @@
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/buffer-image-size": {
|
||||
"version": "0.6.4",
|
||||
"resolved": "https://registry.npmjs.org/buffer-image-size/-/buffer-image-size-0.6.4.tgz",
|
||||
"integrity": "sha512-nEh+kZOPY1w+gcCMobZ6ETUp9WfibndnosbpwB1iJk/8Gt5ZF2bhS6+B6bPYz424KtwsR6Rflc3tCz1/ghX2dQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/node": "*"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/cac": {
|
||||
"version": "6.7.14",
|
||||
"resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz",
|
||||
@ -1066,6 +1107,19 @@
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/entities": {
|
||||
"version": "7.0.1",
|
||||
"resolved": "https://registry.npmjs.org/entities/-/entities-7.0.1.tgz",
|
||||
"integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==",
|
||||
"dev": true,
|
||||
"license": "BSD-2-Clause",
|
||||
"engines": {
|
||||
"node": ">=0.12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/fb55/entities?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/es-module-lexer": {
|
||||
"version": "1.7.0",
|
||||
"resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz",
|
||||
@ -1175,6 +1229,25 @@
|
||||
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/happy-dom": {
|
||||
"version": "20.10.6",
|
||||
"resolved": "https://registry.npmjs.org/happy-dom/-/happy-dom-20.10.6.tgz",
|
||||
"integrity": "sha512-6QD0ilzDDt93tX44y8tbmZdAcdTRYDhUP+Asgi6pC8Pp5IA3cvaZGyoVN/EGtlq9ziT65iPuBBn3ASLr6hCgVw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/node": ">=20.0.0",
|
||||
"@types/whatwg-mimetype": "^3.0.2",
|
||||
"@types/ws": "^8.18.1",
|
||||
"buffer-image-size": "^0.6.4",
|
||||
"entities": "^7.0.1",
|
||||
"whatwg-mimetype": "^3.0.0",
|
||||
"ws": "^8.21.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/loupe": {
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmjs.org/loupe/-/loupe-3.2.1.tgz",
|
||||
@ -1448,6 +1521,13 @@
|
||||
"node": ">=14.17"
|
||||
}
|
||||
},
|
||||
"node_modules/undici-types": {
|
||||
"version": "8.3.0",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-8.3.0.tgz",
|
||||
"integrity": "sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/vite": {
|
||||
"version": "6.4.3",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-6.4.3.tgz",
|
||||
@ -2619,6 +2699,16 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/whatwg-mimetype": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz",
|
||||
"integrity": "sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/why-is-node-running": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz",
|
||||
@ -2635,6 +2725,28 @@
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/ws": {
|
||||
"version": "8.21.1",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.21.1.tgz",
|
||||
"integrity": "sha512-+0NTnW77fFN/DjQi6k/Sq/Yvk4Sgajw7urW8V+asjXnRgDs9gyGkdb7EzgfhA4goXsRIZKE28fzIXBHEzhuiWw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"bufferutil": "^4.0.1",
|
||||
"utf-8-validate": ">=5.0.2"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"bufferutil": {
|
||||
"optional": true
|
||||
},
|
||||
"utf-8-validate": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -14,6 +14,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/three": "^0.170.0",
|
||||
"happy-dom": "^20.10.6",
|
||||
"typescript": "^5.6.0",
|
||||
"vite": "^6.0.0",
|
||||
"vitest": "^2.1.0"
|
||||
|
||||
@ -51,10 +51,12 @@ export interface MachineDef {
|
||||
powerGen?: number;
|
||||
beltSpeed?: number; // tiles/sec, kind==='belt'
|
||||
asset: string; // codex asset key for renderer mesh registry, e.g. "quantizer"
|
||||
color?: string; // v2: art-direction accent; renderer falls back to derived
|
||||
color?: string; // v3 RULING: CHASSIS/BODY color (codex §8 grime); the emissive
|
||||
// accent stays derived from outputs. (Was mislabeled "accent".)
|
||||
flavor?: string; // v2: codex one-liner for the inspector (DATA's material)
|
||||
heatPerTick?: number; // v2: heat while active (the software decoder's whole bit)
|
||||
bufferCap?: number; // v2: bandwidth storage, kind==='buffer'
|
||||
bufferCap?: number; // v2: bandwidth storage in bandwidth-SECONDS (see SimSnapshot)
|
||||
coolPerTick?: number; // v3: per-machine cooling rate; sim default 0.004
|
||||
}
|
||||
|
||||
export interface TechDef {
|
||||
@ -93,14 +95,15 @@ export interface EntityState {
|
||||
outputBuf: Record<string, number>;
|
||||
jammed: string | null; // human-readable reason or null
|
||||
heat: number; // 0..1, throttles >0.7, scrams at 1
|
||||
scrammed?: boolean; // v3: heat-scram latch (heat alone can't show the
|
||||
// hysteresis window; renderer/UI read this, not 0.5)
|
||||
}
|
||||
|
||||
export interface BeltItem {
|
||||
item: string;
|
||||
entity: number; // belt entity id
|
||||
t: number; // 0..1 progress along the belt tile
|
||||
id?: number; // v2: stable per-item id for exact render interpolation
|
||||
// (optional this round; hardens to required in v3)
|
||||
id: number; // v3: stable per-item id (required — sim always stamps)
|
||||
}
|
||||
|
||||
export interface SimSnapshot {
|
||||
@ -108,9 +111,13 @@ export interface SimSnapshot {
|
||||
paused: boolean;
|
||||
entities: ReadonlyArray<EntityState>;
|
||||
beltItems: ReadonlyArray<BeltItem>;
|
||||
// v3 RULING on units: gen/draw are bandwidth per SECOND; stored is bandwidth-seconds
|
||||
// (sim integrates surplus/deficit divided by TICKS_PER_SECOND each tick).
|
||||
bandwidth: { gen: number; draw: number; stored: number; brownout: boolean };
|
||||
shippedTotal: Record<string, number>;
|
||||
activeCommission: string | null;
|
||||
commissionQueue?: string[]; // v3: truth for "next in tray" — active first, then
|
||||
// upcoming in activation order (hardens in v4)
|
||||
commissionProgress: Record<string, number>;
|
||||
}
|
||||
|
||||
@ -119,6 +126,7 @@ export type Command =
|
||||
| { kind: 'remove'; pos: Vec2 }
|
||||
| { kind: 'rotate'; pos: Vec2 }
|
||||
| { kind: 'setRecipe'; entity: number; recipe: string | null }
|
||||
| { kind: 'setRecipeAt'; pos: Vec2; recipe: string | null } // v3: id-free variant
|
||||
| { kind: 'setPaused'; paused: boolean };
|
||||
|
||||
export type SimEvent =
|
||||
@ -139,6 +147,8 @@ export interface Sim {
|
||||
tick(): void; // advance exactly one tick
|
||||
snapshot(): SimSnapshot; // read-only view; renderer/ui must not mutate
|
||||
drainEvents(): SimEvent[]; // events since last drain (main loop fans out)
|
||||
save?(): string; // v3: full state as JSON, deterministic round-trip
|
||||
load?(json: string): void; // v3: restore a save() blob (hardens in v4)
|
||||
}
|
||||
|
||||
export interface Renderer {
|
||||
@ -164,5 +174,6 @@ export interface UI {
|
||||
export interface ScreenFX {
|
||||
init(canvas: HTMLCanvasElement, data: GameData): void;
|
||||
onEvents(events: SimEvent[]): void; // shipped items feed the corruption
|
||||
frame(timeMs: number): void;
|
||||
frame(timeMs: number, snap?: SimSnapshot): void; // v3: read-only peek for continuous
|
||||
// strain (stored, heat); never retain
|
||||
}
|
||||
|
||||
@ -59,9 +59,19 @@ async function boot() {
|
||||
renderer.render(snap, acc / MS_PER_TICK);
|
||||
ui.update(snap, events);
|
||||
screen.onEvents(events);
|
||||
screen.frame(now);
|
||||
screen.frame(now, snap);
|
||||
requestAnimationFrame(loop);
|
||||
}
|
||||
requestAnimationFrame(loop);
|
||||
|
||||
// dev-only: paste-free demo — builds SIM's reference factory
|
||||
if (import.meta.env.DEV) {
|
||||
(window as any).__fktryDemo = async () => {
|
||||
const { referenceFactory } = await import('./sim/reference');
|
||||
const cmds = referenceFactory(data);
|
||||
cmds.forEach((c) => sim.enqueue(c));
|
||||
return `${cmds.length} commands dispatched`;
|
||||
};
|
||||
}
|
||||
}
|
||||
boot();
|
||||
|
||||
Loading…
Reference in New Issue
Block a user