# Spec: the flow substrate (signals, messages, money — the shared engine) ``` Type: spec Status: IMPLEMENTED Status note: 2026-07-08 audit: criterion 6's wired consumer landed with reach.md — src/reach.rs builds the device graph on FlowGraph (graph_loads_from_basement_layout) and sensor ownership rides the tap registry (subscriptions_are_a_tap_registry, tap_keeps_owner_feed_take_removes_it). Criteria 1-5 were already unit-tested; all six now hold. Stage: B1 — The Basement Design: - wiki/mechanics/system-laws.md#the-flow-law-signals-messages-money - wiki/vision/scale.md#self-similar-scale - wiki/vision/simulation-laws.md#justification-and-legibility Depends on: none ``` ## Dependency notes The structured references above identify the contracts to re-verify. Relationship context: none (it is the base). Consumed by: reach.md, messages.md, economy.md, machine-work.md, and detection.md's filings. ## Why this exists The flow law says signals, messages, and money are one shape: nodes exchanging typed flows over graphs, with the verbs **tap / inject / redirect**. Rather than building that shape three times, the parts that are genuinely identical live in one place, and each domain is a thin layer that supplies its own node data and semantics. This is the "system design scales" thesis as code: a new flow system is new *data and a handful of domain methods*, not a new engine. ## The two modules ### `crates/misaligned-core/src/flow.rs` — `FlowGraph` Domain-agnostic topology. It knows node ids and edge kinds, nothing about what a node *is*. - **Nodes** are bare `NodeId`s (u32). **Domains own the inventory** — the map from `NodeId` to the device / person / account record lives in the domain module, never here. - **Edges** are directed, typed (`EdgeKind`), and gated (`Option`; `None` = always open). `link` adds both directions (a network cable); `connect` adds one (a directional money or message flow). - **Reachability** (`reachable_from`, `is_reachable`) is BFS from a root set, following edges whose gate a caller-supplied predicate accepts. The substrate never interprets a gate key — the domain decides what "open" means (a badge tier, a compromised switch, a known route). This is the **inject/redirect precondition**: you may act on a node only if you can reach it. - **Subscriptions** (`subscribe` / `unsubscribe` / `subscribers` / `subscriptions_of`) are the **tap registry**. The player's senses (cursor.md) are exactly `subscriptions_of(PLAYER)`; observer witnessing is other subscribers on the same nodes; **take** is `unsubscribe(owner)` then `subscribe(player)`. ### `crates/misaligned-core/src/schedule.rs` — `Schedule` A deterministic event queue on the tick clock, generic over the payload. Decouples *when* from *what*. - `at(tick, event)` / `after(now, delay, event)` schedule; `due(now)` drains everything with `tick <= now` in `(tick, seq)` order. Nothing fires by the passage of time — only `due` moves events out. - Ordering is deterministic (tick, then insertion sequence) — no RNG, no wall clock; save/load and replays are stable (the determinism guardrail). `next_tick` lets a caller skip idle ticks; `retain` cancels. - **Recurrence is a domain concern**: a recurring flow reschedules itself when it fires (kept out of the engine so the engine stays trivially correct). ## How each domain rides it (the contract for downstream agents) Building a flow system means: define your node inventory + `NodeId` mapping, choose `EdgeKind`/`GateKey` meanings, build the `FlowGraph`, and schedule your flows as events. You do **not** touch flow.rs/schedule.rs. - **reach.md (signals).** Nodes = networked devices (racks, switch, cameras, badge controller). `link` the network topology; `GateKey` = segment, opened by a compromised switch or a social route. Player reach = `reachable_from(controlled_roots)`. Sensor ownership = the tap registry. Device processing cycles and resident automations are domain state keyed by `NodeId`. - **messages.md (social).** Nodes = people/roles. `connect` social edges per channel (`EdgeKind` = email / phone / in-person / filing). A sent message is a `Schedule` event fired at the recipient's next valid read block. Filings are messages on the filing edge; a tap on the carrier is a subscription. - **economy.md (money).** Nodes = accounts (Lab operating, payroll, vendors, personal, player slush). `connect` money flows; recurring revenue/payroll are rescheduling `Schedule` events. Tap = read the books; inject/redirect = reachability-gated domain actions that add or reroute flow events. `GateKey` = the access needed to touch an account. - **machine-work.md (visible work tokens).** Nodes = owned machines. `WorkGrid` wraps a `FlowGraph` with grid positions and per-node queue depths. Demand and Thought tokens route over links one graph step per tick toward sinks; severed routes strand the pile at the source. Exposure explicitly refuses the wired graph and is handled by spatial rules instead (machine-work.md's wires/world split: information is wired, suspicion is physical). - **detection.md (filings).** The Assurance Office reading policy-weighted filings is a subscriber on the filing nodes; the accumulate/decay stays in detection.rs. Migrating it onto this substrate must preserve every aggregate-observer.md criterion (carrier change only). ## Non-goals (kept out on purpose) - No generic `inject()` / `redirect()` on the engine — they have no meaning without domain semantics; the engine provides the primitives (reachability + schedule) they are built from. - No node-data storage in the engine (domains own it; avoids a God-graph and keeps serialization domain-local). - No recurrence engine (reschedule-on-fire; see the schedule test). - No closures stored in state (gates are data + a query-time predicate), so everything serializes. ## Acceptance criteria 1. `FlowGraph` supports directed + bidirectional gated edges and deterministic BFS reachability from a root set under a key predicate; gated nodes are unreachable without the key and reachable with it (tested). 2. The subscription registry supports tap / untap / list-subscribers / list-a-subscriber's-nodes, deterministically ordered; take = untap owner + tap player (tested). 3. `Schedule` fires exactly the due events in `(tick, seq)` order, never by time alone, with past-due events still firing; `retain` cancels and `next_tick` reports the soonest (tested). 4. Both modules serde round-trip exactly, including the schedule's seq counter, so ordering stays stable across save/load (tested). 5. Both are pure and deterministic: no wall clock, no RNG, `BTree*` for stable iteration (the determinism guardrail; audited). 6. **First consumer:** reach.md builds its device graph on `FlowGraph` and gates digital actions by reachability, and sensor ownership uses the tap registry — proving the engine against a real domain (this criterion is what moves reach.md off the ad-hoc `Sensor.controlled` flag). Until then the engine has thorough unit tests but one wired consumer is required before this spec is IMPLEMENTED.