Skip to content

Spec: the flow substrate (signals, messages, money — the shared engine)

SPEC Binding system contract
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.
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 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

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.

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.

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 from NodeId to the device / person / account record lives in the domain module, never here.
  • Edges are directed, typed (EdgeKind), and gated (Option<GateKey>; 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<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 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)

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). 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, 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 social FlowGraph exists 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: each AccountFlow carries cadence/next_tick fields and reschedules itself on fire (account.rs), rather than storing Schedule events. 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. 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).
  • 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.
  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<E> 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.