CONTRACTS v9 notes: 'sealed' jam reason documented; SIM's firewalls-catch-only- what-walks departure RATIFIED (UI measured the alternative as rung 3 not existing); unitCleared queued as round 8's first contract item. main.ts DEV block now exposes window.__fktrySim so testkit verbs can drive an in-browser review the way lane tests drive headless ones - used today to verify the full Phase 1 loop live: band 4, three simultaneous notices, gunship tether + real mirrored theft (sky placard + ledger + ticker + toast), auditor SECTOR LOCK with hash countdown, THE SCREEN held at HOLD with zero feed-time cost. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
4.8 KiB
4.8 KiB
CONTRACTS — how the lanes fit together (ORCHESTRATOR-OWNED)
The binding type definitions live in src/contracts.ts — that file
is the law. This doc explains the shape of the machine in prose.
Data flow
data/*.json ──▶ main.ts loads GameData
│
┌───────────┼─────────────────────────┐
▼ ▼ ▼
sim.init() renderer.init() ui.init() / screen.init()
│
30 tps fixed timestep: sim.tick()
│
├─▶ sim.snapshot() ──▶ renderer.render(snap, alpha) (every rAF)
│ ──▶ ui.update(snap, events)
└─▶ sim.drainEvents() ─▶ screen.onEvents(events)
user input: UI builds Commands ─▶ bus.dispatch ─▶ sim.enqueue
renderer supplies pickTile + ghost preview
Ownership map
| Path | Owner | Everyone else |
|---|---|---|
src/contracts.ts, src/main.ts, root config, this doc, MASTERPLAN.md |
orchestrator | read-only |
src/sim/** |
LANE-SIM | read-only |
src/render/**, public/assets/** (consumption) |
LANE-RENDER | read-only |
src/ui/** |
LANE-UI | read-only |
src/screen/** |
LANE-SCREEN | read-only |
data/** |
LANE-DATA | read-only |
lanes/LANE-X.md NOTES section |
lane X | orchestrator writes ORDERS section |
../docs/FKTRY_LORE.md |
canon | read-only for all lanes |
Key invariants
- Snapshot is read-only. Renderer/UI/Screen never mutate it. Sim may reuse internal buffers between snapshots; consumers must not hold references across frames.
- All mutation goes through Commands. No lane reaches into sim internals.
- Events are drained once per frame by main.ts and fanned out; consumers get the same array — treat it as immutable.
- IDs are data-driven. Renderer/UI/Screen never hard-code item/machine ids; they
key off
GameDataand theassetfield. New content should light up with zero code. alphainrenderer.render(snap, alpha)is the 0..1 fraction between the last sim tick and the next — use it to interpolate belt item positions for smooth motion.- Belt visual speed derives from
MachineDef.beltSpeedandBeltItem.t; sim owns the truth, renderer owns the smoothness. - Coordinates: tile grid, origin at world center, +x east, +y south.
Dir0..3 = N,E,S,W clockwise. Machinepos= origin corner (min x, min y of footprint).
Contract-change requests
Written in your lane NOTES as CONTRACT REQUEST: <what> — <why>. The orchestrator
batches them between rounds. Until granted, work around locally inside your lane.
v4 notes
snapshot.commissionQueuemay alias a live sim array — read-only, never retain across frames (same rule as the snapshot itself, stated here because it looks like a plain array).- Selection protocol: UI writes
bus.setSelection(...); main.ts derives ghost mode and dispatchesplace/removeon click. UI must NOT dispatchremoveitself. The'__remove'def string is a deprecated bridge, deleted in v5. - Heat:
heatPerTickis GROSS; net climb =heatPerTick − coolPerTick. A machine whose heatPerTick ≤ its coolPerTick can never overheat — the validator enforces it. - Research gating: ids appearing in any
TechDef.unlocksare locked until researched; ids referenced by no tech are always available.
v6 notes
- THE SPEAKER:
AudioFX(src/audio/**, LANE-SCREEN) — all sound is Web Audio synthesis, no asset files. main.ts wires init/unlock/onEvents/frame. Pre-discovery only the diegetic minimum plays; the band unlocks onrelicFound. - Sanctioned narrow imports (read-only, documented at the export site):
src/sim/constants.ts(tuning truth) andsrc/audio'sradiocontrol (UI's tuner dock). Everything else stays contract-mediated. - Relics are secrets.
snapshot.relicsis always present, but nothing may signpost an unfound relic: RENDER draws the subtle shimmer, UI shows nothing untilfound. Discoverability budget: wandering + curiosity. That's the design. - New jam reason string:
'no seam'(extractor off-seam).
v9 notes
- Jam reason
'sealed'joins'no seam'in the sim's vocabulary: a warden-sealed machine stalls with it until theprycommand clears the seal. UI translates it as a document, not a fault. - RULING (round 7, ratifying SIM's departure): firewalls catch only what WALKS.
Gunships are airborne and immune — UI measured rung 3 being deleted entirely
otherwise. Pinned by SIM's regression test (a gunship must reach tether and emit
mirroredin the shipped demo). - Round-8 backlog, first item: a
unitClearedevent — the firewall's victory currently fires nine times in the demo and is silent to SCREEN/audio/RENDER.