# CONTRACTS — how the lanes fit together (ORCHESTRATOR-OWNED) The binding type definitions live in [`src/contracts.ts`](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 `GameData` and the `asset` field. New content should light up with zero code. - **`alpha`** in `renderer.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.beltSpeed` and `BeltItem.t`; sim owns the truth, renderer owns the smoothness. - **Coordinates**: tile grid, origin at world center, +x east, +y south. `Dir` 0..3 = N,E,S,W clockwise. Machine `pos` = origin corner (min x, min y of footprint). ## Contract-change requests Written in your lane NOTES as `CONTRACT REQUEST: `. The orchestrator batches them between rounds. Until granted, work around locally inside your lane. ## v4 notes - `snapshot.commissionQueue` may 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 dispatches `place`/`remove` on click. UI must NOT dispatch `remove` itself. The `'__remove'` def string is a deprecated bridge, deleted in v5. - Heat: `heatPerTick` is 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.unlocks` are locked until researched; ids referenced by no tech are always available.