[orchestrator] round 1 review + contracts v2 + round 2 orders
Review: all 5 lanes pass — 70/70 tests, tsc clean, ownership audit clean,
integration verified live (sim -> events -> UI ticker + SCREEN corruption).
Contracts v2 (granted from lane requests): UIBus.pickTile (wired), BeltItem.id?,
MachineDef.{color,flavor,heatPerTick,bufferCap}?, CommissionDef.repeat?,
SimEvent 'scram'. main.ts: non-left pointerdown no longer places. happy-dom added.
Rulings: canon melt chain IS M1 (masterplan updated); rank-routing stays, splitters
next; anchor-slab sanitizing -> Correction buyback fiction; build-bar paging approved;
per-lane dev ports 8151-8155. Round 2 orders written in all lane docs.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
5ee0fb9e78
commit
86d765f90b
@ -52,12 +52,19 @@ codex wins.
|
|||||||
missing data. Build to the contract, stub the rest, note the stub.
|
missing data. Build to the contract, stub the rest, note the stub.
|
||||||
- If the app won't boot because of someone else's in-flight work, note it, build
|
- If the app won't boot because of someone else's in-flight work, note it, build
|
||||||
against the contracts anyway, and verify with `npm test`/`check` this round.
|
against the contracts anyway, and verify with `npm test`/`check` this round.
|
||||||
|
- **Dev server ports are assigned per lane** so five parallel sessions never collide:
|
||||||
|
`npm run dev -- --port <yours>` — SIM 8151, RENDER 8152, UI 8153, SCREEN 8154,
|
||||||
|
DATA 8155. 8150 belongs to the orchestrator. Do not create or edit
|
||||||
|
`.claude/launch.json` (orchestrator-owned, lives outside the repo).
|
||||||
|
|
||||||
## Milestones (orchestrator's map — lanes don't self-assign ahead)
|
## Milestones (orchestrator's map — lanes don't self-assign ahead)
|
||||||
|
|
||||||
- **M1 — FLOW** (rounds 1-2): place extractor → belts → demuxer → quantizer → mosh
|
- **M1 — FLOW** (rounds 1-2): the full canon melt chain — ore → demuxer →
|
||||||
reactor → shipper; items visibly ride belts; MELT ships; THE SCREEN corrupts in
|
{quantizer, subsampler, p-caster, dct-press} → gop-assembler → mosh reactor →
|
||||||
response. The whole fantasy in miniature.
|
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
|
||||||
|
factory ships as the official demo (SIM round 2). Items visibly ride belts; MELT
|
||||||
|
ships; THE SCREEN corrupts item-specifically.
|
||||||
- **M2 — PRESSURE**: bandwidth economy + brownouts, heat/throttling, HF dust piles,
|
- **M2 — PRESSURE**: bandwidth economy + brownouts, heat/throttling, HF dust piles,
|
||||||
first wildlife (mosquito swarms), commissions via fax UI.
|
first wildlife (mosquito swarms), commissions via fax UI.
|
||||||
- **M3 — DEPTH**: tech tree + eras, bloom loops, subsampler bleed, moiré growth,
|
- **M3 — DEPTH**: tech tree + eras, bloom loops, subsampler bleed, moiré growth,
|
||||||
|
|||||||
@ -26,40 +26,46 @@ REQUESTs.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## CURRENT ORDERS — Round 1 (goal: milestone M1 "FLOW" + M2 prep)
|
## CURRENT ORDERS — Round 2 (goal: contracts-v2 content + M2 "PRESSURE" economy)
|
||||||
|
|
||||||
The seed JSONs in `data/` are orchestrator placeholders — correct them, then grow:
|
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?`.
|
||||||
|
|
||||||
1. **Validator first** (`src/sim/data-validate.test.ts`, vitest): every recipe's
|
1. **`data/CANON-DELTA.md`** (new file, yours): ledger of everything invented beyond
|
||||||
machine + items exist; every machine's recipes exist and point back at it; every
|
the codex — round 1's four + whatever follows. The lore doc absorbs it in one
|
||||||
tech unlock exists; commission wants exist; ids unique + kebab-case; colors valid
|
orchestrator pass later; until then this is the single place canon-drift lives.
|
||||||
hex; footprints ≥1×1. This test is the lane's handshake — it must fail loudly on
|
2. **Art direction pass**: `color` on all 21 machines (renderer precedence:
|
||||||
any dangling reference.
|
your color > derived-from-outputs) and `flavor` on all 21 — codex one-liner each,
|
||||||
2. **Items** → full §2 + §3 catalog: all raw resources, refined intermediates, frames,
|
house voice. This kills UI's hardcoded lookup and RENDER's black-on-black problem.
|
||||||
the five science packs, and tier-2 products (melt, bloom-concentrate,
|
3. **Heat economy**: `heatPerTick` on software-decoder, and scale its `powerGen` so
|
||||||
macroblock-bricks, ringing-halos, static-canister, moire-crystal, false-color-
|
hot-but-mighty vs ASIC cool-and-steady is a real choice; a pinch of `heat` on
|
||||||
speckle, judder-cams, explosive-hits, gamut paints ×3). Colors from codex vibes.
|
hex-splicer recipes. SIM implements throttle/scram this round — your numbers decide
|
||||||
3. **Machines** → full §4 catalog (demosaicer, dct-press, subsampler, mv-extractor,
|
whether anyone ever dares run two software decoders side by side.
|
||||||
p-caster, gop-assembler, bloom-duplicator, field-loom, telecine-puller,
|
4. **`bufferCap` on buffer-tank** — one tank should ride out roughly 10 seconds of
|
||||||
hex-splicer, software-decoder, buffer-tank...). Machines beyond M1's seven should
|
typical M1-chain deficit. Show your arithmetic in CANON-DELTA.
|
||||||
exist in data even though sim/render only generically support crafter/power kinds
|
5. **Commission queue content** (SIM lands queue + `repeat` this round): reorder for
|
||||||
— that's fine, they'll work as generic crafters until given bespoke mechanics.
|
a difficulty ramp; add 2-3 `repeat:true` standing orders, including **THE
|
||||||
4. **Recipes** → wire the economy: ore → luma/chroma/mv-flux (demux), luma → coeff
|
CORRECTION BUYBACK** — pays generous bandwidth for anchor-slabs, fax written in The
|
||||||
packs + hf-dust (quantize, generates bandwidth), mv-flux → delta-wafers, slab +
|
Correction's voice: polite, clean, quietly horrifying. This is the sellout loop
|
||||||
wafers → gop-crate, gop-crate → melt + reclaimed slab (mosh), coeff overdrive →
|
(ship your anchors for power now, watch your SCREEN go clean — SCREEN lane already
|
||||||
macroblock-bricks + double hf-dust, hf-dust → static-canister, science pack
|
sanitizes on anchor shipments).
|
||||||
assembly ×3 (analog/spatial/temporal). Include at least two genuinely absurd
|
6. **Container line**: `moov-shard` / `offset-ribbon` / `sync-key` items + recipes
|
||||||
ratios for comedy that still balance.
|
assembling `container-pack` (unblocks `stream-hex-splicing`); prune the
|
||||||
5. **Tech tree v1** → `disc` and `stream` era nodes: unlock quantizer tiers, mosh
|
FORWARD_CANON allowlist as they gain recipes.
|
||||||
reactor, bloom duplicator, subsampler grades. Costs in science packs.
|
7. **Bloom line**: `bloom-grade-1..5` items + bloom-duplicator recipes
|
||||||
6. **Commissions** → 8-10 in full fax voice (§6 has three exemplars). Escalating
|
(grade n + delta-wafers → grade n+1); grade 5 is commission bait. Validator covers
|
||||||
absurdity and quantity; at least one demanding a *live* hazard product you know
|
all new content.
|
||||||
isn't shippable yet (mark it `"wants": {"mosquito-swarm": 3}` — forward canon;
|
|
||||||
the validator should allow items that exist in data even if no recipe makes them).
|
|
||||||
|
|
||||||
**Definition of done:** `npm test` green including your validator; dev server still
|
**Definition of done:** validator green over the expanded catalog; every machine has
|
||||||
boots with expanded data (placeholders render for everything); every entry traceable
|
color + flavor; the queue tells a story from first-taste to the first buyback; SIM's
|
||||||
to a codex section. NOTES list what you invented beyond the codex.
|
reference-factory test still passes against your rebalanced numbers (coordinate via
|
||||||
|
NOTES if a ratio change breaks their layout).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@ -25,37 +25,33 @@ you never simulate. If motion looks wrong, interpolate better or note it — don
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## CURRENT ORDERS — Round 1 (goal: milestone M1 "FLOW")
|
## CURRENT ORDERS — Round 2 (goal: finish M1 polish, open M2 "PRESSURE")
|
||||||
|
|
||||||
Replace the stub in `src/render/index.ts`:
|
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).
|
||||||
|
|
||||||
1. **Camera + controls.** Orthographic isometric-style view. Pan: middle-drag or WASD.
|
1. **Belt topology visuals**: corner/junction pieces chosen from neighbor analysis —
|
||||||
Zoom: wheel (clamped ~8..40 world units). Window resize handled.
|
pure presentation, sim truth unchanged. Splitters (mechanics land this round in
|
||||||
2. **Ground.** Tile grid over the 64×64 world; subtle Stream-Crust styling (dark
|
SIM) get a visible output-cycling tell.
|
||||||
`#0a0812` base, faint `#181425` lines) — see style guide §8. A soft vignette or
|
2. **Heat**: shimmer + dull-red emissive ramp driven by `EntityState.heat`; throttled
|
||||||
fog so the world doesn't just end.
|
= idle animation visibly slowed; scram = dead-dark + blinking amber until restart.
|
||||||
3. **Asset registry + hot-swap.** `registry.ts`: maps asset key → THREE.Object3D
|
3. **Buffer tank**: visible fill level (shader window or scale trick) proportional to
|
||||||
factory. Default: procedural placeholder (footprint-sized beveled box, body color
|
the tank's share of `snapshot.bandwidth.stored`.
|
||||||
from data, one emissive accent, tiny idle animation — e.g. quantizer jaw bob).
|
4. **AnimationMixer**: play clip 0 on rigged GLBs with an idle loop — MODELBEAST
|
||||||
On init, attempt `GLTFLoader` for `public/assets/models/<key>.glb`; if present it
|
assets are imminent; verify with any animated GLB you can find or make.
|
||||||
replaces the placeholder factory. Also poll or re-check on a 10s timer in dev so
|
5. **Exact cargo interpolation** via `BeltItem.id` — delete the one-tick approximation.
|
||||||
assets dropped in mid-session appear without reload (note what you implement).
|
6. **Multi-mesh belt GLBs**: merge-or-group fix (your call, note it) so a fancy belt
|
||||||
4. **Entities.** Sync scene to `snap.entities` (add/remove/rotate). Belts get a flat
|
asset can't half-render.
|
||||||
directional piece with an animated arrow/chevron texture (shader or UV scroll).
|
|
||||||
5. **Belt items.** InstancedMesh per item type (colored octahedron/box placeholder),
|
|
||||||
positioned from `BeltItem.t` + belt entity pos/dir, interpolated with `alpha` for
|
|
||||||
butter-smooth motion at 30 tps sim.
|
|
||||||
6. **Picking + ghost.** `pickTile` via raycast to the ground plane. `setGhost` shows
|
|
||||||
the selected machine's placeholder at the hovered tile, tinted cyan; red tint if
|
|
||||||
tiles occupied (you can read occupancy from `snap.entities` footprints).
|
|
||||||
7. **Feedback dressing (cheap but load-bearing):** jammed machines get a small
|
|
||||||
blinking amber light; brownout dims the whole scene ~30% and flickers lights
|
|
||||||
(listen to snapshot.bandwidth.brownout).
|
|
||||||
|
|
||||||
**Definition of done:** dev server shows the factory; placing a line from the UI (or
|
**Definition of done:** reference factory (SIM ships it this round — coordinate via
|
||||||
console dispatch) shows machines + items gliding down belts smoothly; a GLB dropped
|
their `src/sim/reference.ts` export in a dev page or test harness) looks ALIVE: corners
|
||||||
into `public/assets/models/quantizer.glb` replaces the placeholder. `npm run check`
|
read as corners, heat reads as heat, tanks read as tanks; perf standing rule still met
|
||||||
clean. NOTES written.
|
under `?devscene=stress`.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@ -27,33 +27,36 @@ seed stock — port, don't move).
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## CURRENT ORDERS — Round 1 (goal: milestone M1 "FLOW")
|
## CURRENT ORDERS — Round 2 (goal: artifact fidelity, M2 "PRESSURE" hooks)
|
||||||
|
|
||||||
Replace the stub in `src/screen/index.ts`:
|
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.
|
||||||
|
|
||||||
1. **Port the GLYTCH shader stack** into a proper module (`glitchStack.ts`): vertex +
|
1. **Artifact passes the 8 params can't express** (your list, adopted wholesale):
|
||||||
fragment shader with the 8 uniforms, compiled against your base texture. Keep
|
- legible 8×8 **macroblock quantization** pass — bricks/coeff overdrive must READ
|
||||||
param semantics identical to the prototype (it's tuned; respect the tuning).
|
as a blocky grid, not generic noise;
|
||||||
2. **Corruption ledger.** `onEvents`: each `shipped` event adds to a per-item tally.
|
- edge-detected **ringing halos** (Gibbs oscillation hugging contrast edges);
|
||||||
Map items → param pressure in `corruptionMap.ts`, e.g. `melt → u_mosh+u_melt`,
|
- **moiré interference lattice**.
|
||||||
`chroma-slurry → u_chroma`, `hf-dust → u_noise`, `anchor-slab → REDUCES overall
|
Wire signatures into corruptionMap: `macroblock-bricks`, `ringing-halos`,
|
||||||
corruption slightly` (shipping clean anchors sanitizes — nice tension, note it for
|
`moire-crystal`. Keep the per-item legibility bar from round 1: shipping 40 bricks
|
||||||
the orchestrator). Unknown items → small generic noise bump. Pressure eases toward
|
should look UNMISTAKABLY different from 40 slurry.
|
||||||
its tally-driven target (lerp) so shipments feel like waves, not switches.
|
2. **Shipment lands**: a ~0.4s stamp/silhouette flash in the item's chip color
|
||||||
3. **Escalation curve.** Total corruption 0→1 over roughly the first 100 shipped
|
composited on the feed per `shipped` event (batch bursts: one stamp per event, not
|
||||||
items (tune by feel), log-ish so early shipments feel huge. Above 0.8, add
|
per unit — a 40-unit shipment is one big stamp, not 40 flickers).
|
||||||
compound wobble (params cross-modulate slowly) so a mature factory's SCREEN looks
|
3. **Scram wipe**: `scram` events (new in contracts v2) get a one-frame white
|
||||||
*alive*, not maxed-out static.
|
"TRACKING LOST" bar wipe. Cheap, from the event stream you already have.
|
||||||
4. **Brownout reaction.** On `brownout:true`, freeze-frame + heavy dropout lines +
|
4. **OPTIONAL — signal strain**: buffer tanks land this round; if you want THE SCREEN
|
||||||
desync judder until it lifts. The factory's pain shows in the sky.
|
to tremble as `bandwidth.stored` drains BEFORE brownout, file the snapshot-access
|
||||||
5. **Idle charm.** With zero corruption, the clean feed runs bland loops with a
|
CONTRACT REQUEST you deferred (`frame(timeMs, snap?)`) and build it next round —
|
||||||
"NOTHING IS WRONG" chyron. First shipment ever interrupts it with a single frame
|
or skip it. Your call; the seam is noted and I'll grant it in v3 if asked.
|
||||||
of full-white glitch — the awakening. Cheap, unforgettable.
|
|
||||||
|
|
||||||
**Definition of done:** dev build — screen starts sterile; console-dispatch a few
|
**Definition of done:** three new artifact classes verified the way you did round 1
|
||||||
shipments (or wait for the real chain) and corruption rises, item-specifically;
|
(drive, read pixels, screenshot); stamps and scram wipe visible on the dev build;
|
||||||
brownout visibly seizes it. `npm run check` clean, frame budget respected (note
|
frame budget still <2ms; tests updated for new signatures.
|
||||||
measured ms). NOTES written.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@ -21,44 +21,39 @@ playable through unit tests. You are the lane the other four trust with their li
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## CURRENT ORDERS — Round 1 (goal: milestone M1 "FLOW")
|
## CURRENT ORDERS — Round 2 (goal: finish M1, open M2 "PRESSURE")
|
||||||
|
|
||||||
Replace the stub in `src/sim/index.ts` with a real implementation:
|
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.
|
||||||
|
|
||||||
1. **World + placement.** Tile grid (start 64×64, world-centered). `place` command:
|
1. **Reference factory.** `src/sim/reference.ts` exporting
|
||||||
validate footprint fits and tiles are free, spawn `EntityState`, emit `placed`.
|
`referenceFactory(data: GameData): Command[]` — your deadlock-free M1 layout
|
||||||
`remove` (emit `removed`, refund nothing yet), `rotate`, `setPaused`. Invalid
|
(second quantizer on crime, second assembler on i-only). Test: ships melt within
|
||||||
commands are silently dropped (UI will preview validity later).
|
2,000 ticks AND is still shipping at 20,000 (the deadlock regression net). This
|
||||||
2. **Belts.** `kind:'belt'` entities carry up to 2 items each as `BeltItem` (t: 0..1),
|
becomes the official demo; I wire it to a dev hook in main.ts next round.
|
||||||
moving at `beltSpeed` tiles/sec. Items transfer to the belt/machine the belt points
|
2. **Splitters.** `kind:'splitter'` becomes real: 1 input, round-robin across outward-
|
||||||
into (dir). Items on a belt keep minimum spacing 0.45. Belt→machine: push into
|
facing adjacent belts; a blocked output is skipped, not waited on; your rank cache
|
||||||
`inputBuf` if the machine's current recipe wants that item and buffer < 2× recipe
|
traces THROUGH splitters (rank = best reachable downstream). This is the sanctioned
|
||||||
need; otherwise the item waits (belt backs up — this is core Factorio feel, get it
|
surplus-voiding tool — the rank rule elsewhere is unchanged.
|
||||||
right). Machine→belt: outputs pop onto an adjacent belt facing away, one item per
|
3. **Buffer tanks.** `kind:'buffer'` + `bufferCap`: charge when gen>draw, discharge to
|
||||||
available slot.
|
cover deficit, brownout only when tanks run dry; `snapshot.bandwidth.stored` = pool.
|
||||||
3. **Crafting.** Machines with recipes: when `inputBuf` covers `inputs`, consume,
|
4. **Heat v1.** Accumulate `heatPerTick` while active + `RecipeDef.heat` per craft;
|
||||||
run `ticks` (progress 0..1), emit `crafted`, deposit `outputs` into `outputBuf`.
|
ambient dissipation; heat>0.7 → per-machine throttle (curve is your call, note it);
|
||||||
If `outputBuf` holds ≥ 4× outputs, machine stalls with `jammed:"output full"`
|
heat=1 → scram: machine stops, emits `{kind:'scram',on:true}`, restarts below 0.5
|
||||||
(emit `jammed` once per stall, not per tick). Extractors need no inputs. Machines
|
with `on:false`. `EntityState.heat` is already in the contract — make it real.
|
||||||
with multiple recipes use `setRecipe`; default to `recipes[0]`.
|
5. **Commission queue.** Data-order activation; `commissionDone` advances; `repeat:true`
|
||||||
4. **Bandwidth (power) v1.** Sum powerGen (power kind) vs powerDraw of all entities +
|
re-enters at the back of the queue. Progress counts only while active (keep).
|
||||||
active recipe `bandwidth` (note: negative recipe bandwidth GENERATES — quantizer
|
6. **`jammed:'starved'`** — recipe idle with empty inputs, edge-triggered like
|
||||||
compression pays power, per lore). If draw > gen: global speed multiplier =
|
'output full'.
|
||||||
gen/draw for all progress and belt movement (brownout), emit `brownout` on/off
|
7. **`BeltItem.id`** — stable per-item ids in every snapshot (optional field now;
|
||||||
edges, set `snapshot.bandwidth` accordingly. No storage/buffer tanks yet.
|
hardens to required in v3, so just always set it).
|
||||||
5. **Shipping.** `kind:'shipper'` consumes ANY item arriving in its input, increments
|
|
||||||
`shippedTotal`, emits `shipped`. Track the single active commission (first in
|
|
||||||
data): fill `commissionProgress`, emit `commissionDone` once satisfied.
|
|
||||||
6. **Tests** (minimum): placement collision; belt line moves an item end to end in
|
|
||||||
the expected tick count; belt back-pressure (full line stops cleanly, no item
|
|
||||||
loss/dupe); demux recipe consumes 2 ore → 1 luma + 1 slurry; brownout halves
|
|
||||||
speed at draw=2×gen; determinism (two sims, same seed+commands, 500 ticks,
|
|
||||||
deep-equal snapshots); shipping increments and fires commissionDone.
|
|
||||||
|
|
||||||
**Definition of done:** with the dev server running, the orchestrator can (via UI or
|
**Definition of done:** all of the above tested (determinism suite still green);
|
||||||
console `__fktryBus.dispatch(...)`) place extractor→belts→demuxer→quantizer→
|
reference factory test proves melt at scale with zero deadlock; heat demonstrably
|
||||||
mosh-reactor→shipper and watch `shippedTotal.melt` climb. All tests green. NOTES
|
throttles and scrams the software decoder in a test.
|
||||||
written (decisions, gotchas, what you'd tackle in round 2).
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@ -24,36 +24,32 @@ dispatch Commands via the bus and read state from `update(snap, events)`.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## CURRENT ORDERS — Round 1 (goal: milestone M1 "FLOW")
|
## CURRENT ORDERS — Round 2 (goal: finish M1 interaction, open M2 "PRESSURE")
|
||||||
|
|
||||||
Replace the stub in `src/ui/index.ts`:
|
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.
|
||||||
|
|
||||||
1. **Build bar** (bottom center): one button per machine in `GameData.machines`
|
1. **Click-to-inspect** via `bus.pickTile`. Delete the TAB-cycle fallback and
|
||||||
(icon = colored square from placeholder color + name), hotkeys 1-9. Click/hotkey
|
`?uifixture` — the sim is real now; drive a brownout by building.
|
||||||
selects for placement → sets `bus._sel` (see main.ts) `{def, dir}`; `R` rotates the
|
2. **Build bar paging** (your design): kind tabs, `[`/`]` to page, 1-9 within the
|
||||||
pending dir; `Esc`/right-click clears selection. Selected button shows hot state.
|
active page — every machine hotkey-reachable at any catalog size.
|
||||||
2. **Top strip**: bandwidth meter (gen vs draw bar that goes amber near 100%, red +
|
3. **Inspector upgrades**: recipe picker (`setRecipe` finally gets its UI), heat bar
|
||||||
"BROWNOUT" flash when over), shipped-total ticker, tick/paused indicator.
|
with THROTTLING/SCRAM states, and starved-vs-backed-up wording (sim distinguishes
|
||||||
`Space` toggles pause (dispatch `setPaused`).
|
`'starved'` from `'output full'` this round).
|
||||||
3. **Inspector** (click an entity with nothing selected to build): panel showing
|
4. **Commission queue UI**: current fax + a "NEXT IN TRAY" peek; `repeat:true`
|
||||||
machine name, codex flavor line (hard-code a lookup of 1-liners per machine for
|
commissions get a STANDING ORDER stamp. When anchor-slabs ship, show a SANITISING
|
||||||
now — request codex excerpts in data via NOTES if you want them properly), recipe
|
chip (hardcoding the id is fine this round — the fiction becomes The Correction's
|
||||||
with input/output item chips + live buffer counts, progress bar, jam reason in
|
buyback in M3).
|
||||||
amber. Close with Esc. (Selecting an entity: you'll need the hovered tile — reuse
|
5. **Tests, minimum 10**: chips, roster/footprint hit-testing, selection, hotkey map —
|
||||||
`renderer.pickTile` via a `pointerdown` listener on `#ui`'s capture phase or
|
under happy-dom.
|
||||||
request a cleaner hook via CONTRACT REQUEST if this fights main.ts's handler; a
|
|
||||||
pragmatic v1 is fine.)
|
|
||||||
4. **Commission fax** (top right, under THE SCREEN): the active commission's flavor
|
|
||||||
text on a fax-paper styled card, `wants` as item chips with progress `3/40`,
|
|
||||||
satisfying it plays an aggressively mundane "FAX SENT" stamp animation on
|
|
||||||
`commissionDone`.
|
|
||||||
5. **Event toasts** (bottom right): `jammed`, `brownout`, `commissionDone` become
|
|
||||||
small toast lines in the house voice, max 4 on screen, 6s TTL.
|
|
||||||
|
|
||||||
**Definition of done:** full M1 chain placeable entirely with mouse + hotkeys, no
|
**Definition of done:** the reference factory (SIM ships `src/sim/reference.ts` this
|
||||||
console needed; brownout is legible from the top strip alone; fax completes when melt
|
round) is inspectable end to end by mouse alone; every machine reachable by hotkey;
|
||||||
ships. `npm run check` clean, no console errors, pointer fall-through verified.
|
tests green; fixture deleted.
|
||||||
NOTES written.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@ -51,6 +51,10 @@ export interface MachineDef {
|
|||||||
powerGen?: number;
|
powerGen?: number;
|
||||||
beltSpeed?: number; // tiles/sec, kind==='belt'
|
beltSpeed?: number; // tiles/sec, kind==='belt'
|
||||||
asset: string; // codex asset key for renderer mesh registry, e.g. "quantizer"
|
asset: string; // codex asset key for renderer mesh registry, e.g. "quantizer"
|
||||||
|
color?: string; // v2: art-direction accent; renderer falls back to derived
|
||||||
|
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'
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TechDef {
|
export interface TechDef {
|
||||||
@ -65,6 +69,7 @@ export interface CommissionDef {
|
|||||||
flavor: string; // the fax text — deadpan absurd
|
flavor: string; // the fax text — deadpan absurd
|
||||||
wants: Record<string, number>;
|
wants: Record<string, number>;
|
||||||
rewardBandwidth: number;
|
rewardBandwidth: number;
|
||||||
|
repeat?: boolean; // v2: standing order — re-activates after completion
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface GameData {
|
export interface GameData {
|
||||||
@ -94,6 +99,8 @@ export interface BeltItem {
|
|||||||
item: string;
|
item: string;
|
||||||
entity: number; // belt entity id
|
entity: number; // belt entity id
|
||||||
t: number; // 0..1 progress along the belt tile
|
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)
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SimSnapshot {
|
export interface SimSnapshot {
|
||||||
@ -121,7 +128,8 @@ export type SimEvent =
|
|||||||
| { kind: 'brownout'; on: boolean; tick: number }
|
| { kind: 'brownout'; on: boolean; tick: number }
|
||||||
| { kind: 'placed'; entity: number; def: string; tick: number }
|
| { kind: 'placed'; entity: number; def: string; tick: number }
|
||||||
| { kind: 'removed'; entity: number; def: string; tick: number }
|
| { kind: 'removed'; entity: number; def: string; tick: number }
|
||||||
| { kind: 'commissionDone'; commission: string; tick: number };
|
| { kind: 'commissionDone'; commission: string; tick: number }
|
||||||
|
| { kind: 'scram'; entity: number; on: boolean; tick: number }; // v2: heat shutdown edge
|
||||||
|
|
||||||
// ---------------------------------------------------------------- module interfaces
|
// ---------------------------------------------------------------- module interfaces
|
||||||
|
|
||||||
@ -144,6 +152,8 @@ export interface UIBus {
|
|||||||
dispatch(cmd: Command): void;
|
dispatch(cmd: Command): void;
|
||||||
/** current build selection; renderer ghost + click-to-place read this */
|
/** current build selection; renderer ghost + click-to-place read this */
|
||||||
selectedBuild(): { def: string; dir: Dir } | null;
|
selectedBuild(): { def: string; dir: Dir } | null;
|
||||||
|
/** v2: world picking, plumbed from the renderer — UI's click-to-inspect */
|
||||||
|
pickTile(clientX: number, clientY: number): Vec2 | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface UI {
|
export interface UI {
|
||||||
|
|||||||
@ -25,6 +25,7 @@ const bus: UIBus & { _sel: { def: string; dir: Dir } | null } = {
|
|||||||
_sel: null,
|
_sel: null,
|
||||||
dispatch: (cmd: Command) => sim.enqueue(cmd),
|
dispatch: (cmd: Command) => sim.enqueue(cmd),
|
||||||
selectedBuild: () => bus._sel,
|
selectedBuild: () => bus._sel,
|
||||||
|
pickTile: (x, y) => renderer.pickTile(x, y),
|
||||||
};
|
};
|
||||||
// LANE-UI sets the build selection through this hook:
|
// LANE-UI sets the build selection through this hook:
|
||||||
(window as any).__fktryBus = bus;
|
(window as any).__fktryBus = bus;
|
||||||
@ -42,6 +43,7 @@ async function boot() {
|
|||||||
renderer.setGhost(sel?.def ?? null, renderer.pickTile(e.clientX, e.clientY), sel?.dir ?? 0);
|
renderer.setGhost(sel?.def ?? null, renderer.pickTile(e.clientX, e.clientY), sel?.dir ?? 0);
|
||||||
});
|
});
|
||||||
game.addEventListener('pointerdown', (e) => {
|
game.addEventListener('pointerdown', (e) => {
|
||||||
|
if (e.button !== 0) return; // pan/context buttons never place
|
||||||
const sel = bus.selectedBuild();
|
const sel = bus.selectedBuild();
|
||||||
const tile = renderer.pickTile(e.clientX, e.clientY);
|
const tile = renderer.pickTile(e.clientX, e.clientY);
|
||||||
if (sel && tile) sim.enqueue({ kind: 'place', def: sel.def, pos: tile, dir: sel.dir });
|
if (sel && tile) sim.enqueue({ kind: 'place', def: sel.def, pos: tile, dir: sel.dir });
|
||||||
|
|||||||
123
package-lock.json
generated
Normal file
123
package-lock.json
generated
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
{
|
||||||
|
"name": "glytch",
|
||||||
|
"lockfileVersion": 3,
|
||||||
|
"requires": true,
|
||||||
|
"packages": {
|
||||||
|
"": {
|
||||||
|
"devDependencies": {
|
||||||
|
"happy-dom": "^20.10.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"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/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/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/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/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/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/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/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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
5
package.json
Normal file
5
package.json
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"devDependencies": {
|
||||||
|
"happy-dom": "^20.10.6"
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user