[orchestrator] round 4 review + contracts v5 + round 5 orders
Review: 5/5 pass, 318/318, tsc clean. Verified live: STANDARDS COMMITTEE renders all eras with ratified nodes, fuel gauge + ON RESERVE state, composite seam in the tree. Fixed the demo brownout (mine): __fktryDemo now grants referenceFactoryTech() via testkit before dispatching (swaps to the grantResearch command when SIM lands it). Contracts v5: grantResearch command (sandbox is a feature), SelectionState inspect mode (RENDER's placed-and-selected auras), wildlife (WildlifeState + event), coolRadius clarified ADDITIVE capped at HEAT_COOL_MAX. main.ts: typed selection store (inspect-capable), sentinel string no longer sent, narrowing fixed in UI's faithful-copy harness (my breakage, my fix). Rulings: research-time approved; pack abandonment kept as design; DATA's ungating + starter-plant arithmetic ratified; SCREEN era-feed + jammed-rot approved; UI tab strip approved. MASTERPLAN: bare-stash ban, stale-numbers process note. Round 5 = M2 closes: wildlife (dust piles, mosquito swarms, traps), research takes time, era-aging sky, seam map groundwork, inspect-mode handshake. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
3e45ee2652
commit
557c78b208
@ -47,6 +47,17 @@ codex wins.
|
||||
`git reset --soft HEAD~1` and recommit correctly. If collisions still happen,
|
||||
the next escalation is a git worktree per lane.
|
||||
|
||||
Also: **never bare `git stash`** — it stashes every lane's uncommitted work and
|
||||
will convince you that you fixed (or caused) another lane's red. Only
|
||||
`git stash push -- <your paths>`. (Round 4: SCREEN nearly misdiagnosed a red this
|
||||
way; DATA's diff was the actual variable.)
|
||||
|
||||
**Numbers in orders go stale mid-round.** Five lanes land continuously; a count or
|
||||
claim written into CURRENT ORDERS is true only as of writing (round 4: "24 nodes"
|
||||
was 23 by landing; "research doesn't exist" was false within the hour). Re-probe
|
||||
HEAD before acting on any order's factual claim, and before writing NOTES — a
|
||||
round-start grep is a lie by round-end.
|
||||
|
||||
## Hard rules
|
||||
|
||||
- **Never edit**: `MASTERPLAN.md`, `CONTRACTS.md`, `src/contracts.ts`, `src/main.ts`,
|
||||
|
||||
@ -25,46 +25,41 @@ REQUESTs.
|
||||
|
||||
---
|
||||
|
||||
## CURRENT ORDERS — Round 4 (goal: THE COMPOSITE SEAM + research economy)
|
||||
## CURRENT ORDERS — Round 5 (goal: the world gets teeth and ground)
|
||||
|
||||
Round 3 review: re-introducing round 2's exact bug to prove the validator catches it
|
||||
is the gold standard — and your "decorative number" lesson is now a contract comment
|
||||
AND your validator's job forever (heat ruling: gross, net = heat − cool). Verdicts:
|
||||
tsconfig FIXED (`include: ["src", "data"]` — your poisoned-file proof was conclusive).
|
||||
`accent?` and `coolRadius?` are in v4. SIM exports tuning constants this round —
|
||||
retire your hand-copies. The stco/keyring recipe-assignment logic ("made by the
|
||||
machine that already owns that knowledge") is exactly how future inventions should
|
||||
argue.
|
||||
Round 4 review: the gating audit — measuring 0-of-24 reachability, fixing it as two
|
||||
tested rules, deleting six fake nodes instead of repurposing them — is the round's
|
||||
headline and the reason the tree tells a story now. The cooler retune that survives
|
||||
both readings of an ambiguous contract sentence was defensive tuning of the highest
|
||||
order; the sentence is now clarified (ADDITIVE, capped at HEAT_COOL_MAX). Verdicts:
|
||||
constants.ts landed in SIM's final commit (your grep raced it — it exports 12,
|
||||
retire your hand-copies now). Your ungating of the mosh reactor + starter-plant
|
||||
arithmetic: RATIFIED, SIM's reference factory now pre-grants its techs in the demo.
|
||||
|
||||
1. **THE COMPOSITE SEAM — the codex grew §10** (docs/FKTRY_LORE.md, read it first):
|
||||
keying/transparency artifacts, from "you cannot film transparency". Transcribe it:
|
||||
ALPHA (the unfilmable resource — only keyer machines emit it), HARD MATTE, BLUE
|
||||
HOLE, FRINGE LACE, GREEN SPILL, MIXED PIXEL, ROTO CHATTER, GHOST FEATHER, SODIUM
|
||||
LIGHT (589nm, Correction-hoarded reagent — forward canon until M4); machines
|
||||
MATTE BATH (reel), OPTICAL PRINTER (the iron lung — make its ratios hurt), CHROMA
|
||||
KEYER (stream; leaks green spill, wrecks feathers into chatter), ROTO BENCH (the
|
||||
grind: converts anything, atrociously), NEURAL KEYER (endgame: perfect, and its
|
||||
powerDraw should be obscene — the codex says its appetite dwarfs everything;
|
||||
research-gate it behind a fortress-adjacent tech). Era-gate the family through
|
||||
the tech tree; validator covers everything; CANON-DELTA logs deviations.
|
||||
2. **Research economy**: author the lab machine (`kind:'lab'` in v4 — name it in
|
||||
codex voice, it's yours to canonize in CANON-DELTA) and audit all 24+ tech costs
|
||||
now that they'll actually be paid: pack production chains must exist and the
|
||||
early nodes must be affordable off a first-taste-era factory. Show one worked
|
||||
cost path (packs/minute vs research demand) in CANON-DELTA.
|
||||
3. **ASIC COOLER activation**: SIM lands `coolRadius` auras this round — give the
|
||||
cooler its radius + numbers (your spec'd adjacency intent), and make the software
|
||||
decoder + cooler pairing the worked example (duty cycle with vs without).
|
||||
4. **Accent fills**: `accent?` where the first-output derivation is wrong — belts,
|
||||
buffer tank, uplink, lab, coolers; leave it unset where the derivation is already
|
||||
right (that's the design, not laziness).
|
||||
5. **Fixture/validator updates**: v4 fields (`accent`, `coolRadius`), the gross-heat
|
||||
rule you already enforce stays, `commissionQueue` v5-hardening — any fixtures you
|
||||
own carry it.
|
||||
1. **Retire the hand-copied constants** — import from `src/sim/constants.ts`
|
||||
(read-only cross-lane import is sanctioned for constants).
|
||||
2. **Wildlife data** (SIM builds the mechanic this round — coordinate via NOTES):
|
||||
the MOSQUITO TRAP machine (§5 codex: bug-zapper shaped like a high-contrast edge
|
||||
target), its catch recipe producing the canned `mosquito-swarm` item, pile/hatch
|
||||
tuning numbers if SIM makes them data-driven, and the commissions that suddenly
|
||||
work: `vaporwave-sunset` finally unblocked — prune FORWARD_CANON accordingly and
|
||||
let the fax celebrate in voice.
|
||||
3. **Cooler numbers final** under the clarified additive rule — your 0.0018 was
|
||||
tuned to survive ambiguity; now tune it to be *right*, show the duty-cycle table
|
||||
in CANON-DELTA.
|
||||
4. **The seam map — start the ground game** (the content gap you own): author
|
||||
`data/seams.json` — seam regions per stratum era (shape your call: rects or
|
||||
points + richness), the codex §1 geology as actual coordinates. SIM consumes it
|
||||
round 6 (extractors will require seam tiles); this round it's data + validator
|
||||
coverage + a CANON-DELTA spec of intended mechanics, so SIM has something real to
|
||||
build against instead of a guess.
|
||||
5. **Fixture/validator upkeep**: v5 fields (wildlife), grantResearch in any test
|
||||
flows you own, FORWARD_CANON pruned.
|
||||
|
||||
**Definition of done:** Composite Seam fully transcribed and reachable through era
|
||||
tech (validator green, FORWARD_CANON updated); a research path is affordable and
|
||||
proven against SIM's real mechanic; cooler pairing arithmetic in CANON-DELTA.
|
||||
**Definition of done:** trap + swarm economy closes (catchable → cannable →
|
||||
commissionable) against SIM's real mechanic; seams.json exists with validator
|
||||
coverage and a written consumption spec; constants imported from truth; CANON-DELTA
|
||||
carries the cooler table.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@ -25,37 +25,35 @@ you never simulate. If motion looks wrong, interpolate better or note it — don
|
||||
|
||||
---
|
||||
|
||||
## CURRENT ORDERS — Round 4 (goal: v4 protocol, research & aura visuals)
|
||||
## CURRENT ORDERS — Round 5 (goal: a living, inspectable world)
|
||||
|
||||
Round 3 review: the dead-debug-hook bundle grep and the provenance note were exemplary;
|
||||
your worktree analysis drove the new MASTERPLAN commit rule (pathspec commits — your
|
||||
option 2, with worktrees as the named escalation). Verdicts: CONTRACT REQUEST 1
|
||||
GRANTED — v4 has the typed selection protocol (`SelectionState`, `setGhost(..., mode)`)
|
||||
and main.ts already passes `mode` on every call (the `'__remove'` def string still
|
||||
arrives as a bridge; it dies in v5). `MachineDef.accent?` also landed (your round-2
|
||||
wish, UI re-filed it). Era skins verified beautiful.
|
||||
Round 4 review: ERA_TELL (signal vs surface) and the chebyshev-rectangle honesty are
|
||||
both canon reasoning now; the showroom axis-overflow lesson is quoted in my head
|
||||
weekly. Verdicts: CONTRACT REQUEST 1 GRANTED — `SelectionState` grew
|
||||
`{mode:'inspect'; entity}` in v5; UI publishes it when the inspector opens. The
|
||||
sentinel bridge is DEAD as promised: main.ts now sends `setGhost(null, tile, 0,
|
||||
'remove')` — you already ignore the def, so nothing changes for you, but confirm.
|
||||
Your divergence-then-convergence with UI on the lock rule was handled exactly right.
|
||||
|
||||
1. **Ghost migrates to `mode`**: key demolition tint off the 4th arg, ignore the
|
||||
sentinel def. Keep rendering exactly as shipped otherwise.
|
||||
2. **Locked-machine ghost** (research lands this round): a `place` for a locked id is
|
||||
silently dropped by the sim — the ghost must telegraph it. Distinct from
|
||||
occupied-red: padlock-amber wash + "era" tell if you can read the tech era from
|
||||
data cheaply. Coordinate the lock lookup with UI via NOTES (both of you derive it
|
||||
from `snapshot.research.unlocked` + tech.json — same rule, CONTRACTS v4 notes).
|
||||
3. **Cooler auras**: `coolRadius` machines get a floor-ring (subtle, engineering-
|
||||
drawing vibe, not a buff circle) on hover/ghost AND while placed-and-selected;
|
||||
heat-shimmer suppression inside the ring falls out of sim truth automatically.
|
||||
4. **Lab visuals**: placeholder with a distinct silhouette (research bench — codex
|
||||
voice pending DATA's machine entry); `accent?` consumed when present (your
|
||||
derivation stays fallback).
|
||||
5. **Housekeeping approved from your NEXT list**: splitter lamp can stay timer-driven
|
||||
(real round-robin state isn't in the snapshot and doesn't earn a contract field
|
||||
yet); showroom item-hall paging when DATA's catalog next grows (Composite Seam
|
||||
transcription lands ~15 items this round — it will).
|
||||
1. **Inspect mode**: on `{mode:'inspect'}`, highlight the inspected entity (outline
|
||||
or ground ring — your language) and, when it has `coolRadius`, draw the aura
|
||||
rectangle — order 3's "placed-and-selected" case, now reachable.
|
||||
2. **Wildlife visuals** (SIM lands `snapshot.wildlife` this round): DUST PILES as
|
||||
§2's glitter heaps — instanced, growing with `size`, subtle shimmer; MOSQUITO
|
||||
SWARMS as pixel-gnat particle clouds that visibly cluster on their `target`
|
||||
machine (which should read as *harassed* — flicker its accent); trap machines get
|
||||
a zap flash on the `wildlife cleared` event... which you don't receive — read
|
||||
state deltas instead, or tell me if you want Renderer to get events (that's a
|
||||
contract conversation I'm open to in v6, say so in NOTES if hover-state isn't
|
||||
enough).
|
||||
3. **Fixture hygiene**: `commissionQueue` + `research` into your fabricated
|
||||
snapshots (devscene, showroom) — v6 hardens both.
|
||||
4. **MODELBEAST intake stays armed**: no assets have landed yet; `?showroom` is the
|
||||
live manifest. First drops get a NOTES line each on how normalization treated them.
|
||||
|
||||
**Definition of done:** remove-tint works with the mode arg (verified with UI's
|
||||
migrated flow); a locked machine's ghost is legibly "not yet" rather than "broken";
|
||||
cooler ring reads at a glance; showroom absorbs the new catalog.
|
||||
**Definition of done:** an infested factory is legible from across the map (piles
|
||||
grow, swarms harass, traps zap); inspect-highlight + aura verified with UI's flow;
|
||||
perf rule still holds with 50 piles + 10 swarms in the stress scene.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@ -27,37 +27,34 @@ seed stock — port, don't move).
|
||||
|
||||
---
|
||||
|
||||
## CURRENT ORDERS — Round 4 (goal: second dread axis, boredom, ratification)
|
||||
## CURRENT ORDERS — Round 5 (goal: the sky learns eras and insects)
|
||||
|
||||
Round 3 review: measuring tremor in *seconds-until-dark* — and getting scale-
|
||||
invariance for free from the units ruling — is the single most elegant thing in this
|
||||
codebase. The negative-dt landmine catch is exactly why the NOTES exist. Verdicts:
|
||||
your capacity PROPOSAL is GRANTED unasked — `bandwidth.capacity?` is in v4 (SIM sums
|
||||
`bufferCap`); your "reserve low vs falling fast" second reading is this round's order.
|
||||
Your stalled-factory idea is APPROVED as design. The strain/fever/seizure hierarchy is
|
||||
canon now — keep future readings inside it.
|
||||
Round 4 review: the two-clock decision (card in seconds because it's a broadcast;
|
||||
scram wipe in frames because it's tape) is the most codex-literate engineering choice
|
||||
so far, and the capacity-trap catch protected the sterile guarantee twice over.
|
||||
Verdicts: your jammed-rot PROPOSAL is APPROVED (rot ≠ rest); your era-feed NEXT idea
|
||||
is this round's centerpiece — you called it, you build it.
|
||||
|
||||
1. **Two dreads**: with `capacity` available, split the readings — "seconds left"
|
||||
(tremor, as shipped) vs "reserve fraction" (a low-tank pallor/vignette that sits
|
||||
there even when draw ≤ gen and nothing is draining). A full tank draining fast vs
|
||||
a near-empty tank coasting should FEEL different; write the one-line design intent
|
||||
for each in NOTES.
|
||||
2. **Boredom** (your proposal): no `shipped` events for a sustained stretch → total
|
||||
corruption eases back toward clean, slowly, visibly — the factory's work
|
||||
literally evaporating off THE SCREEN. Escalation tallies stay (history isn't
|
||||
erased; the *display* decays), so production resuming snaps it back fast. Tune
|
||||
the decay horizon so a normal jam doesn't read as punishment (minutes, not
|
||||
seconds). Test it.
|
||||
3. **Ratification interruption**: `researched` event → a one-second full-screen
|
||||
interruption card on the feed — sterile white, "STANDARD RATIFIED", era name,
|
||||
then the corruption crashes back in. The Correction briefly wins the sky every
|
||||
time the player does science. Deadpan, readable, cheap.
|
||||
4. **Fixture hygiene**: `commissionQueue` hardens to required in v5 — update your
|
||||
fixture snapshots this round.
|
||||
1. **The base feed ages with the player** (your idea, ordered): drive the clean
|
||||
programming's palette/texture from the highest era with a ratified tech
|
||||
(`research.unlocked` × tech eras from GameData): reel = sepia + gate weave +
|
||||
sprocket judder, broadcast = phosphor + interlace flicker, disc = rainbow-on-black
|
||||
sheen, stream = the current SMPTE feed, fortress = blinding white (§8 skins).
|
||||
Corruption artifacts stay era-agnostic — it's the SUBSTRATE that ages. Sterile
|
||||
guarantee holds per era (test at least one).
|
||||
2. **Jammed-rot**: `jammed` entities accelerate boredom decay (the work is rotting);
|
||||
a clean idle keeps the gentle horizon. One-line read, as you said — plus a test
|
||||
distinguishing the two.
|
||||
3. **Wildlife on the sky** (SIM lands `snapshot.wildlife` + events): mosquito swarms
|
||||
drive a *regional* mosquito-noise shimmer via your growth-mask template (seeded
|
||||
from the swarm's pos — the sky itches where the factory itches). Piles need
|
||||
nothing unless you disagree.
|
||||
4. **Fixture hygiene**: done last round for commissionQueue/research — add
|
||||
`wildlife` where fixtures fabricate snapshots.
|
||||
|
||||
**Definition of done:** both dread axes verified at the pixel level (your contact-
|
||||
sheet method); boredom decays and snaps back under test; ratification card exactly
|
||||
N frames with era text; budget still <2ms.
|
||||
**Definition of done:** era progression visible on the feed (contact sheet per era),
|
||||
rot vs rest distinguishable under test, swarm shimmer regional and pinned by pixels,
|
||||
budget still <2ms.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@ -21,45 +21,40 @@ playable through unit tests. You are the lane the other four trust with their li
|
||||
|
||||
---
|
||||
|
||||
## CURRENT ORDERS — Round 4 (goal: M3 "DEPTH" — research, spatial heat)
|
||||
## CURRENT ORDERS — Round 5 (goal: M2 closes — the Bitstream comes alive)
|
||||
|
||||
Round 3 review: the worst-case power sizing ("a test that passes on a coincidence is
|
||||
worse than one that fails") and the counter-current bloom discovery are the two best
|
||||
engineering notes anyone has filed. Verdicts: **heat semantics RULED as you asked** —
|
||||
gross, net = heatPerTick − coolPerTick, now a contract comment and a DATA validator
|
||||
rule. **Bloom loop: the escalator reading is ACCEPTED** — your wafer-bus circle IS the
|
||||
mechanic; the codex line stays as lore-poetry. **Git: pathspec commits are now a hard
|
||||
rule in MASTERPLAN** (your option, chosen over worktrees for now — worktrees are the
|
||||
escalation if it recurs). save/load hardened in v4 as you offered. commissionQueue
|
||||
aliasing documented in CONTRACTS. Your perf-noise honesty is noted — a headless
|
||||
benchmark is a good M4 item, not now.
|
||||
Round 4 review: the realtech.test.ts tripwire caught DATA's circular tree and your
|
||||
premise-guard philosophy caught their fix — the system is working. Verdicts:
|
||||
**grantResearch PROPOSAL GRANTED** — it's in v5 as a real Command (sandbox is a
|
||||
feature, not a cheat); implement it, and it replaces the testkit import currently in
|
||||
main.ts's `__fktryDemo` (I wired testkit as the stopgap — swap is mine once you land).
|
||||
**Research time APPROVED** (your NEXT): labs take `RESEARCH_TICKS_PER_PACK` per pack
|
||||
(export it) so a tech is an investment, not a purchase. **Pack abandonment on target
|
||||
switch: KEPT** — "packs are the cost of indecision" is design, write it no further.
|
||||
Cooling cap reasoning accepted; the additive-stacking rule is now a contract comment
|
||||
(DATA tuned blind against the ambiguity — your sentence, my fault for not catching it).
|
||||
|
||||
1. **Research v1 — the round's centerpiece.** Contracts v4: `kind:'lab'`,
|
||||
`{kind:'setResearch'}`, `snapshot.research` (`ResearchState`), `researched` event.
|
||||
Labs consume science packs from their input toward the active tech's `cost`;
|
||||
totals met → `researched`, unlocks apply. Gating rule (in CONTRACTS v4 notes): ids
|
||||
appearing in any `TechDef.unlocks` are locked — `place`/`setRecipe`/`setRecipeAt`
|
||||
for locked ids are silently dropped; everything unreferenced is free. Multiple
|
||||
labs stack. save/load carries research. Tests incl. a full unlock path against
|
||||
DATA's real tech.json.
|
||||
2. **Spatial cooling** — `coolRadius` (v4): a machine with it applies its
|
||||
`coolPerTick` to machines within chebyshev range of its footprint (stacking = sum,
|
||||
cap total cooling at your call, note it). The ASIC COOLER finally means something;
|
||||
DATA activates numbers in coordination.
|
||||
3. **Export your tuning constants** (`src/sim/constants.ts`: throttle/restart
|
||||
thresholds, default cooling, etc.) — DATA's tests currently hand-copy them and go
|
||||
stale silently; let them import truth.
|
||||
4. **Reference factory v4**: add an anchor-slab overflow tap → uplink so the demo
|
||||
ships slabs (UI's SANITISING + the buyback fiction become visible in `__fktryDemo`
|
||||
without surgery — they had to demolish your assembler to see it). Keep the 20k
|
||||
regression green; a lab + a research run in the reference build would be ideal
|
||||
but is optional if layout pressure fights you.
|
||||
5. **Fixture hygiene**: `commissionQueue` hardens to required in v5 — make sure your
|
||||
snapshot literal always carries it (you already publish it live).
|
||||
1. **`grantResearch` command** — instant unlock list, idempotent, save/load safe.
|
||||
2. **Research time** — per-pack ticks; progress visible in `research.progress`
|
||||
between packs is your design call (fractional or stepped — note it).
|
||||
3. **Wildlife v1 — the round's centerpiece** (contracts v5: `snapshot.wildlife`,
|
||||
`WildlifeState`, the `wildlife` event): HF DUST PILES accumulate near machines
|
||||
whose recipes emit hf-dust while they run (rate/threshold your design; the seeded
|
||||
RNG finally rolls); a pile at size 1.0 hatches a MOSQUITO SWARM that drifts to the
|
||||
nearest running machine and degrades its speed while attached (`target`); swarms
|
||||
are cleared by a trap machine (DATA authors it — coordinate the mechanic: trap
|
||||
with an empty-input recipe that only crafts while a swarm is in radius, producing
|
||||
the canned `mosquito-swarm` item, is my suggested shape). Deterministic, save/load
|
||||
carries it all, events on spawn/clear. Tests including "a factory that vents dust
|
||||
into product instead of shipping it never hatches a swarm" — the design point.
|
||||
4. **Reference factory v5**: grow the research rig (bottler + archaeology lab fed by
|
||||
quantizer surplus) so research is playable in the demo, and the 20k run should now
|
||||
also hatch at least one swarm and trap it if DATA's trap lands — the demo becomes
|
||||
the whole M2 story. Regression stays green.
|
||||
|
||||
**Definition of done:** research provable end-to-end in tests (place lab → feed packs
|
||||
→ unlock → previously-dropped place now succeeds); cooler aura demonstrably extends a
|
||||
decoder's duty cycle; constants exported; reference factory ships slabs.
|
||||
**Definition of done:** wildlife lifecycle proven under test (accumulate → hatch →
|
||||
degrade → trap → event trail), research takes time, grantResearch lands (tell me in
|
||||
NOTES so I swap main.ts off testkit), reference v5 researches a real tech in-demo.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@ -24,43 +24,33 @@ dispatch Commands via the bus and read state from `update(snap, events)`.
|
||||
|
||||
---
|
||||
|
||||
## CURRENT ORDERS — Round 4 (goal: research UI, one real bug, protocol migration)
|
||||
## CURRENT ORDERS — Round 5 (goal: the pressure has a face)
|
||||
|
||||
Round 3 review: `live.test.ts` — booting the real sim against the real DOM — is the
|
||||
best verification asset any lane has produced; the SANITISING machine-swap showed
|
||||
exactly the right instinct when an order's premise fails. Verdicts: CONTRACT REQUEST 1
|
||||
GRANTED (v4 `SelectionState` + `setSelection`/`selection` on the bus — main.ts now owns
|
||||
the remove dispatch; see order 2). Request 2 GRANTED (`MachineDef.accent?` — your
|
||||
derivation becomes the documented fallback, DATA fills the field where it's wrong).
|
||||
`?uidemo` PROPOSAL APPROVED. Tech-panel skip was right at the time — but it was
|
||||
another round-start grep going stale: `tech.json` has 24 nodes at HEAD (DATA landed
|
||||
mid-round again). It's a real order now.
|
||||
Round 4 review: the live.test.ts research loop — lock → refuse → lab → ratify →
|
||||
padlock falls → place succeeds — is the single best proof in the repo, and your
|
||||
"re-probe before writing NOTES" practice is now a MASTERPLAN process note for
|
||||
everyone. Verdicts: your save-schema coupling PROPOSAL is answered by v5's
|
||||
`grantResearch` Command (SIM implements) — migrate your tests to it and the blob
|
||||
coupling dies. Scrolling tab strip: APPROVED (you flagged the wrap). RENDER's tint
|
||||
verification is still owed — close it this round.
|
||||
|
||||
1. **BUG, found live in review: the BROWNOUT banner lies.** `topstrip.ts:66` keys on
|
||||
`out || ratio > 1` — during a covered deficit (tanks draining, sim `brownout:
|
||||
false`, THE SCREEN correctly calm) the panel screams BROWNOUT. That state is
|
||||
*survival*, not failure. Fix: BROWNOUT = the flag only; deficit-with-reserve gets
|
||||
its own amber state (suggest "ON RESERVE · 34s" — `stored/(draw-gen)` is seconds
|
||||
left, same math SCREEN uses for dread). Add a test pinning covered-deficit ≠
|
||||
brownout.
|
||||
2. **Selection protocol migration**: write via `bus.setSelection(...)`, drop the
|
||||
`REMOVE_DEF` sentinel export and — important — **delete your own `remove`
|
||||
dispatch**: main.ts dispatches it on click now (double-fire today is harmless
|
||||
only because the second one no-ops).
|
||||
3. **Research UI, the centerpiece**: `T` opens the tech tree (24 nodes, 5 eras),
|
||||
pick → `setResearch`; progress from `snapshot.research` (packs delivered vs
|
||||
cost); locked machines in the build bar get greyed + padlock + "REQUIRES:
|
||||
<tech>" tooltip (lock rule in CONTRACTS v4 notes); `researched` event → toast in
|
||||
house voice ("STANDARD RATIFIED. NEW CRIMES AVAILABLE.") and the padlocks fall
|
||||
off live.
|
||||
4. **`?uidemo`** — as approved: boots `__fktryDemo()` after init.
|
||||
5. **Accent adoption**: read `accent?` first, your derivation second, kind table
|
||||
third (same precedence as RENDER — one sentence in NOTES confirming you both
|
||||
read it identically).
|
||||
1. **Scrolling tab strip** — your design; keep every tab reachable by `[`/`]` and
|
||||
the strip legible at 29+ machines and REMOVE + RESEARCH.
|
||||
2. **Publish inspect mode**: when the inspector opens, `setSelection({mode:
|
||||
'inspect', entity})`; clear on close. RENDER highlights + draws cooler auras off
|
||||
it — coordinate the timing so Esc clears both cleanly.
|
||||
3. **Wildlife surfacing** (SIM lands it this round): infestation toasts in house
|
||||
voice ("QUANTIZER HOSTS A SWARM. OSHA HAS BEEN NOTIFIED. OSHA IS THE SWARM."),
|
||||
inspector shows a MOSQUITO EXPOSURE line on a targeted machine + the speed
|
||||
penalty; a pile nearing hatch (size > 0.8) earns one warning toast, not a nag.
|
||||
4. **Migrate research-granting tests** to the `grantResearch` command once SIM
|
||||
lands it (drop the save-blob mutation).
|
||||
5. **Verify RENDER's demolition tint + inspect highlight** with your own eyes and
|
||||
screenshot it in NOTES — third round this handshake has floated unverified.
|
||||
|
||||
**Definition of done:** banner distinguishes RESERVE from BROWNOUT under test; a
|
||||
full research run is verifiable in `live.test.ts` (lock visible → research → unlock
|
||||
→ place succeeds); remove mode works through the typed protocol with RENDER's tint.
|
||||
**Definition of done:** tab strip scrolls; inspect mode round-trips with RENDER
|
||||
(screenshot); a swarm's lifecycle is narratable from UI alone (warning → infested →
|
||||
trapped); research tests no longer parse save blobs.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@ -62,8 +62,10 @@ export interface MachineDef {
|
||||
coolPerTick?: number; // v3: per-machine cooling rate; sim default 0.004
|
||||
accent?: string; // v4: art-directed emissive accent; fallback stays derived
|
||||
// from first output item (renderer + UI share the derivation)
|
||||
coolRadius?: number; // v4: spatial cooling aura in tiles (chebyshev from footprint);
|
||||
// this machine's coolPerTick also applies to machines in range
|
||||
coolRadius?: number; // v4: spatial cooling aura in tiles (chebyshev from footprint).
|
||||
// v5 CLARIFIED: auras are ADDITIVE — a machine in range cools at
|
||||
// its own coolPerTick + sum of in-range auras, capped at the
|
||||
// exported HEAT_COOL_MAX (see src/sim/constants.ts)
|
||||
}
|
||||
|
||||
export interface TechDef {
|
||||
@ -128,7 +130,16 @@ export interface SimSnapshot {
|
||||
// order. May alias a live array — read-only, never
|
||||
// retain. HARDENS IN v5: update fixtures this round.
|
||||
commissionProgress: Record<string, number>;
|
||||
research?: ResearchState; // v4: research progress (hardens in v5)
|
||||
research?: ResearchState; // v4: research progress (hardens in v6)
|
||||
wildlife?: ReadonlyArray<WildlifeState>; // v5: piles & swarms (M2's living hazards)
|
||||
}
|
||||
|
||||
export interface WildlifeState {
|
||||
id: number;
|
||||
kind: 'dust-pile' | 'mosquito-swarm';
|
||||
pos: Vec2;
|
||||
size: number; // pile: accumulation 0..1; swarm: severity 0..1
|
||||
target?: number; // swarm: entity id it is currently degrading
|
||||
}
|
||||
|
||||
export interface ResearchState {
|
||||
@ -146,6 +157,9 @@ export type Command =
|
||||
| { kind: 'setRecipe'; entity: number; recipe: string | null }
|
||||
| { kind: 'setRecipeAt'; pos: Vec2; recipe: string | null } // v3: id-free variant
|
||||
| { kind: 'setResearch'; tech: string | null } // v4: pick the lab target
|
||||
| { kind: 'grantResearch'; tech: string[] } // v5: instant unlock — the
|
||||
// sandbox/demo verb, also a
|
||||
// legitimate creative mode
|
||||
| { kind: 'setPaused'; paused: boolean };
|
||||
|
||||
export type SimEvent =
|
||||
@ -157,7 +171,9 @@ export type SimEvent =
|
||||
| { kind: 'removed'; entity: number; def: string; tick: number }
|
||||
| { kind: 'commissionDone'; commission: string; tick: number }
|
||||
| { kind: 'scram'; entity: number; on: boolean; tick: number } // v2: heat shutdown edge
|
||||
| { kind: 'researched'; tech: string; tick: number }; // v4: research complete
|
||||
| { kind: 'researched'; tech: string; tick: number } // v4: research complete
|
||||
| { kind: 'wildlife'; on: 'spawned' | 'cleared'; wildlife: 'dust-pile' | 'mosquito-swarm';
|
||||
id: number; pos: Vec2; tick: number }; // v5: hazard lifecycle
|
||||
|
||||
// ---------------------------------------------------------------- module interfaces
|
||||
|
||||
@ -175,6 +191,7 @@ export interface Sim {
|
||||
export type SelectionState =
|
||||
| { mode: 'build'; def: string; dir: Dir }
|
||||
| { mode: 'remove' }
|
||||
| { mode: 'inspect'; entity: number } // v5: open inspector — renderer highlights it
|
||||
| null;
|
||||
|
||||
export interface Renderer {
|
||||
|
||||
@ -24,20 +24,24 @@ const screen = createScreenFX();
|
||||
// v4 bridge: _sel keeps the legacy {def,dir} shape (including the deprecated
|
||||
// '__remove' sentinel) so unmigrated lanes keep working; selection()/setSelection()
|
||||
// are the typed protocol. The sentinel path is deleted in v5.
|
||||
const bus: UIBus & { _sel: { def: string; dir: Dir } | null } = {
|
||||
_sel: null,
|
||||
const bus: UIBus & { _sel: { def: string; dir: Dir } | null; _state: SelectionState } = {
|
||||
_sel: null, // legacy mirror (build/remove only) — served for selectedBuild() compat
|
||||
_state: null, // v5: typed truth, carries inspect mode
|
||||
dispatch: (cmd: Command) => sim.enqueue(cmd),
|
||||
selectedBuild: () => bus._sel,
|
||||
pickTile: (x, y) => renderer.pickTile(x, y),
|
||||
selection: (): SelectionState => {
|
||||
if (!bus._sel) return null;
|
||||
if (bus._state) return bus._state;
|
||||
if (!bus._sel) return null; // legacy writer fallback
|
||||
if (bus._sel.def === '__remove') return { mode: 'remove' };
|
||||
return { mode: 'build', def: bus._sel.def, dir: bus._sel.dir };
|
||||
},
|
||||
setSelection: (sel: SelectionState) => {
|
||||
bus._sel = sel === null ? null
|
||||
: sel.mode === 'remove' ? { def: '__remove', dir: 0 as Dir }
|
||||
: { def: sel.def, dir: sel.dir };
|
||||
bus._state = sel;
|
||||
bus._sel =
|
||||
sel?.mode === 'build' ? { def: sel.def, dir: sel.dir } :
|
||||
sel?.mode === 'remove' ? { def: '__remove', dir: 0 as Dir } :
|
||||
null;
|
||||
},
|
||||
};
|
||||
// LANE-UI sets the build selection through this hook:
|
||||
@ -55,10 +59,11 @@ async function boot() {
|
||||
const sel = bus.selection!();
|
||||
const tile = renderer.pickTile(e.clientX, e.clientY);
|
||||
if (sel?.mode === 'remove') {
|
||||
// legacy def string kept alongside the mode until renderers migrate (v5 removes it)
|
||||
renderer.setGhost('__remove', tile, 0, 'remove');
|
||||
renderer.setGhost(null, tile, 0, 'remove');
|
||||
} else if (sel?.mode === 'build') {
|
||||
renderer.setGhost(sel.def, tile, sel.dir, 'build');
|
||||
} else {
|
||||
renderer.setGhost(sel?.def ?? null, tile, sel?.dir ?? 0, 'build');
|
||||
renderer.setGhost(null, tile, 0, 'build'); // inspect / no selection: no ghost
|
||||
}
|
||||
});
|
||||
game.addEventListener('pointerdown', (e) => {
|
||||
@ -67,7 +72,7 @@ async function boot() {
|
||||
const tile = renderer.pickTile(e.clientX, e.clientY);
|
||||
if (!sel || !tile) return;
|
||||
if (sel.mode === 'remove') sim.enqueue({ kind: 'remove', pos: tile });
|
||||
else sim.enqueue({ kind: 'place', def: sel.def, pos: tile, dir: sel.dir });
|
||||
else if (sel.mode === 'build') sim.enqueue({ kind: 'place', def: sel.def, pos: tile, dir: sel.dir });
|
||||
});
|
||||
|
||||
const MS_PER_TICK = 1000 / TICKS_PER_SECOND;
|
||||
@ -85,13 +90,18 @@ async function boot() {
|
||||
}
|
||||
requestAnimationFrame(loop);
|
||||
|
||||
// dev-only: paste-free demo — builds SIM's reference factory
|
||||
// dev-only: paste-free demo — builds SIM's reference factory. The layout uses
|
||||
// research-gated ids, so it is a post-research save by definition: grant the techs
|
||||
// it needs first (testkit rides the public save/load API), then dispatch.
|
||||
if (import.meta.env.DEV) {
|
||||
(window as any).__fktryDemo = async () => {
|
||||
const { referenceFactory } = await import('./sim/reference');
|
||||
const { referenceFactory, referenceFactoryTech } = await import('./sim/reference');
|
||||
const { grantResearch } = await import('./sim/testkit');
|
||||
const techs = referenceFactoryTech(data);
|
||||
grantResearch(sim, techs);
|
||||
const cmds = referenceFactory(data);
|
||||
cmds.forEach((c) => sim.enqueue(c));
|
||||
return `${cmds.length} commands dispatched`;
|
||||
return `${techs.length} techs granted, ${cmds.length} commands dispatched`;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@ -64,7 +64,7 @@ function harness(opts: { unlocked?: boolean; reference?: boolean } = {}) {
|
||||
const sel = bus.selection!();
|
||||
if (!sel || !pick) return;
|
||||
if (sel.mode === 'remove') sim.enqueue({ kind: 'remove', pos: pick });
|
||||
else sim.enqueue({ kind: 'place', def: sel.def, pos: pick, dir: sel.dir });
|
||||
else if (sel.mode === 'build') sim.enqueue({ kind: 'place', def: sel.def, pos: pick, dir: sel.dir });
|
||||
});
|
||||
|
||||
if (opts.reference !== false) for (const c of referenceFactory(DATA)) sim.enqueue(c);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user