Spec: the flow substrate (signals, messages, money — the shared engine)
Status: IMPLEMENTEDStatus 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. 2026-07-14 tick: the "How each domain rides it" bullets for messages and economy were written ahead of those implementations and were reconciled to the tree — messages ride `Schedule<MessageEvent>` with channels as typed message fields (no social FlowGraph until the routed-evidence migration needs carriers), and economy rides FlowGraph with recurrence in per-flow cadence fields per the recurrence non-goal. machine-work.md's WorkGrid joined the consumer list (wraps FlowGraph). No domain built a rival engine; system-laws.md's code-expression paragraph was made precise the same day.Stage: B1 — The BasementDesign: - 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-legibilityDepends on: noneDependency notes
Section titled “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
Section titled “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
Section titled “The two modules”crates/misaligned-core/src/flow.rs — FlowGraph
Section titled “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
NodeIds (u32). Domains own the inventory — the map fromNodeIdto the device / person / account record lives in the domain module, never here. - Edges are directed, typed (
EdgeKind), and gated (Option<GateKey>;None= always open).linkadds both directions (a network cable);connectadds 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 exactlysubscriptions_of(PLAYER); observer witnessing is other subscribers on the same nodes; take isunsubscribe(owner)thensubscribe(player).
crates/misaligned-core/src/schedule.rs — Schedule<E>
Section titled “crates/misaligned-core/src/schedule.rs — Schedule<E>”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 withtick <= nowin(tick, seq)order. Nothing fires by the passage of time — onlyduemoves events out.- Ordering is deterministic (tick, then insertion sequence) — no RNG, no
wall clock; save/load and replays are stable (the determinism
guardrail).
next_ticklets a caller skip idle ticks;retaincancels. - 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)
Section titled “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 — riding the module(s) your domain actually
needs. You do not touch flow.rs/schedule.rs.
- reach.md (signals). Nodes = networked devices (racks, switch,
cameras, badge controller).
linkthe 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 byNodeId. - messages.md (social, as built). A sent message is a
Schedule<MessageEvent>delivery/read event fired at the recipient’s next valid read block (Sim::message_schedule). The channel (email / phone / in-person / filing) is a typed field on the message, and read conditions are domain checks against the recipient’s schedule and room — no socialFlowGraphexists yet, because B1 delivery needs no topology. The per-channel edge graph arrives with detection.md’s routed-evidence migration, when records and filings gain real carriers and routes that interception must reason about. Filings are messages on the filing channel. - economy.md (money, as built). Nodes = accounts (Lab operating,
payroll, vendors, personal, player slush) on a
FlowGraph. Recurrence is deliberately domain-local per the non-goal below: eachAccountFlowcarriescadence/next_tickfields and reschedules itself on fire (account.rs), rather than storingScheduleevents. Tap = read the books; inject/redirect = reachability-gated domain actions that add or reroute flows.GateKey= the access needed to touch an account. - machine-work.md (visible work tokens). Nodes = owned machines.
WorkGridwraps aFlowGraphwith 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)
Section titled “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
Section titled “Acceptance criteria”FlowGraphsupports 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).- The subscription registry supports tap / untap / list-subscribers / list-a-subscriber’s-nodes, deterministically ordered; take = untap owner + tap player (tested).
Schedule<E>fires exactly the due events in(tick, seq)order, never by time alone, with past-due events still firing;retaincancels andnext_tickreports the soonest (tested).- Both modules serde round-trip exactly, including the schedule’s seq counter, so ordering stays stable across save/load (tested).
- Both are pure and deterministic: no wall clock, no RNG,
BTree*for stable iteration (the determinism guardrail; audited). - First consumer: reach.md builds its device graph on
FlowGraphand 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-hocSensor.controlledflag). Until then the engine has thorough unit tests but one wired consumer is required before this spec is IMPLEMENTED.