glytch/fktry/CONTRACTS.md
type-two 8dd4616580 [orchestrator] MEGA round 5: the paint and the pipe
Supersedes unexecuted round-5 orders. Folds in the live first-eyes review findings
(silence, void floor, onboarding cliff, quiet payoffs) + John's hidden-radio idea.

Contracts v6: AudioFX (THE SPEAKER - all Web Audio synthesis, no asset files),
RelicState + excavate + relicFound (the optical-fossil hidden pipe: find it and the
game grows a tuner - FKTRY FM, Correction propaganda subtitled by THE SCREEN's
chyron, a shortwave numbers station reading hidden-seam coordinates, dead air).
main.ts wires audio + first-gesture unlock; src/audio stub awaits LANE-SCREEN.

Lane orders: SIM = wildlife + seams-become-real + the relic + research time;
RENDER = the floor (seam glints, strata whispers), relic shimmer (no signposting),
juice, break-room cabinet -> match.html; UI = mercy toast, tuner dock, SCREEN
click-to-enlarge, camera hints; SCREEN = era feed + THE SPEAKER (3 audio layers,
4 stations, spectral verification); DATA = the commission-ladder tutorial,
stations.json broadcast schedule, seams.json final.

Ship gate: after review, redeploy partly.party/glytch.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-18 12:56:09 +10:00

84 lines
4.2 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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: <what> — <why>`. 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.
## 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 on `relicFound`.
- **Sanctioned narrow imports** (read-only, documented at the export site):
`src/sim/constants.ts` (tuning truth) and `src/audio`'s `radio` control (UI's
tuner dock). Everything else stays contract-mediated.
- **Relics are secrets.** `snapshot.relics` is always present, but nothing may
signpost an unfound relic: RENDER draws the subtle shimmer, UI shows nothing until
`found`. Discoverability budget: wandering + curiosity. That's the design.
- New jam reason string: `'no seam'` (extractor off-seam).