Safety gates
Six gates over the mechanisms that stop trading — the kill switches, the graceful stop, the feed-thread stop budget, held divergences, the live-account lock, and every paper mount.
These gates cover the code you reach for when something is wrong. Each one is a text gate over the source rather than a runtime test, and each one says out loud what a text gate cannot prove — because in this family, false confidence is the failure.
kill_switch_gate.rs — the page is read at 3am
The kill-switch runbook is read by someone who is not going to verify it, which makes it exactly the kind of prose this tree has learned not to trust. A kill-switch page that has rotted is worse than none, because the operator stops looking for the real switch.
Seven rules. Each documented mechanism's file must exist and contain its symbol as a whole token; the page must still carry that row's operator trigger string and its citation; every citation anywhere on the page must resolve, wider than the row table, so a claim added in prose is gated the same as a table row; no line numbers; a floor on the row and citation counts, so the gate cannot decay into checking nothing; a mutation self-test, without which the first three rules could be vacuously green forever; and for the subset whose trigger is a line the binary prints, that the binary still prints it.
Reachability of a symbol is not reachability of a mechanism
This gate cannot tell that a switch's configuration type exists but is constructed by no binary. That finding is written on the page under "Switches that do not work" and is checked by nothing here. Proving "no shipping binary arms this" needs a call graph, and a text gate pretending to do it would be the exact false confidence the file exists to prevent. The same holds for behaviour: one behaviour test was later spelled to assert the opposite of what it once did, and every row and citation here stayed green through the reversal, because a text gate cannot read a test name for its meaning.
graceful_stop_pin.rs — one stop flag, many triggers
Neither headless daemon installed a signal handler, so a service stop ran none of the teardown each of them already owned. The fix was one package: the handler sets the existing stop flag, and the ordinary teardown runs below a single wait on it. A second teardown route is how the two drift, and drift here reads as "the interactive stop closes the book and the service stop does not".
So what is pinned is the shape rather than the intention. Both daemons reach the same shared stop
primitive, both install handlers, and neither declares or names a signal crate of its own — a ban
derived from the module that owns the surface, so it tracks the platform split rather than pinning one
literal. The trading daemon's teardown must wait on the flag, not sit below an uninterruptible park. The
handler must be installed before anything can place an order — the first cut of the fix failed that,
with the install below the core mount, so the maker could already be resting orders while the signal
still killed the process outright. The loser of a teardown claim must wait, never return, because a
return from main truncates the winner's teardown. Every shipped unit must bound the stop explicitly
rather than inherit the 90-second default, and none may carry an ExecStop=: one added without a
matching daemon-side poll was measured at 0.02 s with zero daemon code run.
feed_stop_windows_gate.rs — exhaustive over the source, not over memory
The recorder's feed-stop budget is derived from the largest blocking window a feed thread can occupy when the stop flag goes up, and its doc comment tabulated those windows. The table shipped saying "every one of them" and "each is followed by a stop check". Both were false the day they were written: a REST book seed ran on the shared 30-second pager agent — three times the newly-bounded dial — with no stop poll in front of it. The same change that bounded the dial left a larger window immediately behind it and then asserted exhaustiveness over the result.
The rule keys on shape, not on a list of blessed names: in a file that mounts a feed driver, an unbounded WebSocket connect fails; every agent-shaped call is classified and must be a row of the gate's own table of agent rungs; and a bounded call's ceiling argument must itself be a row of the derivation table — the direction that catches "somebody bounded a new window and did not tell the budget about it". What the gate cannot see is executable rather than promised: a test runs the rules against each missed shape and asserts the miss, beside positive controls asserting the catch. An earlier round wrote that residual as prose, and it was already wrong in the same direction as the table it replaced.
held_divergence_is_visible_gate.rs — a held divergence must reach the log
A held reconciliation divergence was written to an in-memory ring reachable only through a control
channel that is off by default. Measured on the live box: a folded-pass line reporting two alerts every
60 seconds for hours, zero lines at warn or above, and no way for anyone reading logs to learn which
two divergences were being held. The count reached the operator; the content did not.
Emitting the line unconditionally fixed the silence and bought a flood — under a quarantine policy a divergence nothing heals is re-diffed and re-held every interval, and one day logged 396 identical warnings with a genuinely new divergence indistinguishable among them. So the emission moved behind an announcer that logs the transitions of the held set plus a slow backlog summary, and both properties are gated: the raise path still logs, and no warning on that path is unguarded.
live_lock_claim_order_gate.rs — claim before you build
The live lock is the one-live-process-per-venue-account tripwire, and neither composition root was holding one over what is actually live. The daemon locked the wrong set — one sentinel per run-profile mount, while the builder armed an exec client for every wired venue the credential store answered for. Measured on one startup, two log lines apart: nine live authenticated sessions, one lock. The desktop app locked the right set at the wrong time — its claims came from a value that does not exist until every exec client has been constructed, and three venue clients post a leverage call at startup, so a lock refused there had already changed account state at three venues.
A mutation campaign then showed what pinning a name is worth here: three one-line edits, each relocating the sentinel directory, left every test green. The gate pins each root's declared chain instead — from the boot's state directory to the claim — with each hop checked by what it is built from, and it refuses shapes it cannot classify rather than passing them.
paper_mount_arming_gate.rs — every construction site is classified
A mount arms the operator halt sentinel; a simulation must not. The accessor's own doc said two named seams each assert on this, "so adding a third mount seam that forgets fails a test in its own crate" — and a third seam already existed, unarmed, when the sentence was written. An assertion inside seam A and an assertion inside seam B are evidence about A and about B; nothing was looking at the set.
So every file under a crate's src/ that calls a paper-client constructor needs a row, and the
constructor set is derived from the type's own impl block and matched under whatever name the
calling file's imports give it. Mount rows must arm; simulation rows must not — and that opposite half
is not symmetry for its own sake, because the backtest-equals-paper equivalence gate drives the same
client, so a sentinel read on a simulation path would make backtest fills depend on a file lying around
in the project state directory. Test code is out of scope: it constructs unarmed books by the dozen, and
that is the simulation shape. The gate is file-level rather than call-site-level, with per-seam tests
bounding the residual — those answer "is this book armed", and this gate answers the one none of them
can: "is there a seam nobody is asking about".
What each of these mechanisms does is on Halt, The dead-man's switches and Reconciliation.
Docs gates
Six gates over prose — citations, documented defaults, documented commands, duplicated facts, and the two hand-maintained indexes.
Supply chain
What CI may download and run on the boxes that sign real orders, how much unsafe code the workspace holds, and the single transport stack cargo-deny enforces.