Intelligence proposes.
It is never allowed to arm.
How AI helps without touching the production money path — the code that enforces it, the code that doesn't, and the receipts for both. Every claim below is scoped to the deployed branch (neuropublic@main) and cites file:line you can open.
01 — The claim, scoped honestly
Intelligence — LLM brains, automation, AI-written code — may propose capital deployment but cannot arm it. The precise version, defended line by line below: live order authority requires two deliberate human acknowledgements that no committed code path supplies; the executor forces paper on any doubt; and de-risking is always allowed on purpose.
We will not tell you “the AI literally cannot place an order.” That's false, and you'd catch us: once the operator arms both acks, proposed orders go live. The real, verifiable discipline is narrower and stronger — the AI cannot open live capital, expand exposure, or grant itself authority; only the operator can — and we show you exactly where that is a machine gate and where it is a human convention. (What the machine can arm on its own — only risk-reducing guardrails — is the next section.)
Who can arm what
Not everything that “arms” is a trade. Arming runs on a tiered taxonomy (state/arm-taxonomy.json): the safe direction may auto-arm; the risky direction always escalates to a human.
- The machine may self-arm a bounded safe subset inside an already-human-armed envelope — turn a guardrail from observe to enforce, shrink live sizing (a coded never-expand invariant), enable a dead-man alert. Deterministic, evidence-gated, auto-reversible.
- A human arms every live-capital authority — opening the live envelope (
CONFIRM_LIVE + AUTO_LIVE_CONFIRM), expanding exposure, clearing a kill, real order-cancel. Taggedhuman_only; the risk direction never auto-arms. - An off-box audit (
AUTONOMY=0,scanner/arm_audit.py) checks the taxonomy itself — that no surface tagged human_only is ever reclassified as auto-armable — and exits non-zero on drift. The complementary runtime check (that none was actually self-armed) is built but runs in spec-only mode until the decision ledger it reads exists — so we state it as pending, not done. - The arming rules are also formally model-checked: over the full reachable state space, no automatic transition ever arms a human_only surface, opens live, or loosens a cap, and a risk-reducing close stays reachable from every state — invariants INV1–INV4, zero violations (
scanner/arm_model_check.py, with a TLA+ companion, run in CI on every change to the arming model plus a daily gate). A proof of the design; that the code matches the model is the property tests' job, not this one's. - The one surface that would let a machine grant itself live authority is permanently sealed —
human_only_SEALED, excluded from every auto path by construction.
So the machine can tighten its own guardrails; it cannot open live, expand exposure, or grant itself authority. That asymmetry — safe direction may automate, risk direction stays human — is the honest version of “only a human arms.”
02 — Two rules explain every row
Risk-increasing opens are gated. Risk-reducing closes are deliberately never stranded behind a gate — an automated exit must never be trapped.
On any uncertainty — a missing ack, an import error, a corrupt ledger — the default is paper, not live. The one exception, stated plainly below, is the kill switch.
03 — The boundary map
Dual live-gate: CONFIRM_LIVE=yes + AUTO_LIVE_CONFIRM set to an exact, deliberately-long confirmation phrase — live_gate.py:118The decision function is CODE; that the second ack lives only in the operator’s .env (never committed code) is enforced by review AND a CI grep-gate (test_no_baked_live_ack.py). The barrier is not the secrecy of a phrase — it is that no checked-in code supplies the second ack.
_executor_paper_backstop() below every KIS caller, over the dual gate — kis_order_executor.py:1598The genuine gate. Fires even for a daemon that hardcodes dry_run=False. This forced-paper backstop is KIS-specific (see 04).
Same two-ack invariant re-checked at each venue’s pretrade point — pretrade_check_futures/options; inline for direct spot openersNot one shared function on the opener path. PR #2623 unified the three Binance pretrade checks to one authority (live_gate_passed(), core/utils/live_gate.py) and closed a spot-open gap; the standalone opener daemons still carry their own inline copy of the same two acks. The invariant is shared, not one call site.
Intentionally exempt from the dual gate and soft halts — _binance_pretrade_gate.py:1548Truthful degradation: an automated exit is never stranded. The AI can execute a risk-reducing SELL without the live ack. Intentional and safe — but a real carve-out we name rather than hide.
master_kill_active() check before transfer — adapters/binance/client.py:80Was ungated. Audited and closed (PR #2623): a killed fleet can no longer move funds between wallets.
No withdrawal endpoint exists in the codeThe strongest honest property: funds cannot leave the account through any code path here. A guarantee by absence.
Presence check + file-flag cascade — emergency_kill_switch.pyA corrupt or missing kill flag does NOT halt trading, so a bad flag can’t freeze the fleet. It is a positive stop signal, not a fail-closed gate. We will never call it fail-closed.
Hash-chained, generation-fenced, append-only authority when ARM_STORE_ENABLEDThe enforcing path is now built and POSIX-fenced, with a migration doc and an arm CLI (PR #2632) — but it ships default OFF: the live authority today is still the .env dual gate. A rollout that is ready, not the current posture. The fcntl fence is a no-op on Windows, so the guarantee is mac/POSIX-only.
Re-derives + re-executes the real gate off-box; holds no actuatorIndependent verification of governance and determinism — propose-only by construction. NOT a gate on the order path; presenting it as one would overclaim.
NOTIFY mode: pages the operator on absenceEven in fire mode it is authority-removing only (engage kill / disarm) — no liquidate verb exists in code.
04 — What is a machine gate
- The dual-gate decision function fails to paper unless both acks are exact —
live_gate.py:118, pinned bytest_live_gate_arm_store.py. - On KIS, the gate sits below every caller:
_executor_paper_backstop() @ kis_order_executor.py:1598forces paper even for a daemon that hardcodesdry_run=False. Proven bytest_executor_paper_backstop.py. - That forced-paper backstop is KIS-specific. Binance opener daemons each carry their own inline two-ack gate instead; the shared Binance order-enforcer ships off by default and can reject an order but not force it to paper. So on Binance the barrier is the per-daemon gate, not a backstop beneath it — the two acks are still required to open, at each venue's own checkpoint.
- The fenced arm ledger is deny-by-default: absent, tampered, forked, or expired evidence all read DISARMED —
test_arm_store.py, ~25 cases. (Opt-in; not today's default posture — see 07.) - Funds have no code path out of the account: there is no withdrawal endpoint in the adapter. A guarantee by absence.
- The off-box challenge brain holds no actuator and imports no gate; a committed test —
deadman/test_isolation.py— proves the observer cannot reach the one component that can act.
05 — How we verify it — re-executing the real gate off-box
We don't ask you to trust the gate; we re-run it. decision_replay.py pulls the real gate source (sha-pinned), re-executes it on a separate box against inputs rebuilt from the audit ledger alone, and diffs its allow/veto verdict byte-for-byte against what actually happened live.
Over the full span, 11 divergences — every one traced to a single dated bug-fix (7f65d92), none to nondeterminism. Gates that read state we can't independently reconstruct are excluded and named, not faked into the match rate.
Fail-closed proofs are example-based unit tests, not assertions: test_executor_paper_backstop.py (clear either ack → forced paper), test_kill_switch_durability.py (corrupt file fails open, by design), test_reduce_only_exit_guarantee.py (a halt blocks BUYs but lets risk-reducing SELLs through).
06 — What we audited and closed
Writing this page began with a code audit of our own boundary. It found real gaps. We closed them before publishing — the audit trail is the point, not a clean-looking omission:
- Binance spot opens had no dual-ack (futures/options did). Added, at parity —
PR #2623. - The crypto pre-trade gate was a parallel re-implementation. Unified: the three Binance pre-trade checks now delegate to one authority (
live_gate_passed()) and gained the arm_store ledger —PR #2623. The shared pretrade path and the two-ack invariant are unified; the standalone opener daemons still carry their own inline copy of the same check (see 04). - Intra-account transfers had no kill check. A killed fleet can no longer move funds —
PR #2623. - The second ack was once hardcoded in several shell wrappers.
PR #2603de-baked it to a pass-through, and a CI + pytest gate now fails the build if anyscripts/*.shwrapper re-bakes the literal value —test_no_baked_live_ack.py,PR #2619. It guards shell wrappers, not Python; reading the ack from the environment at runtime is the intended pattern.
07 — What is still process, not a machine
Expert credibility is the whole point of this page, so here is what a machine still does not guarantee:
- The fenced ledger ships OFF by default. Today's live authority is the plain
.envdual gate. The ledger is the stronger mechanism — built and tested — but it is a rollout, not the current posture. - The kill switch is fail-open by design. A corrupt kill flag does not halt trading — so a bad flag can't freeze the fleet. A positive stop signal, not a fail-closed gate.
- “Invariance checked before merge” is a regression + anti-regression-sentinel suite staying green — not a semantic behavior-diff proof. We used to note here that the gates had no property tests; they now do — a Hypothesis suite, run as a dedicated CI step, asserts that across a generated input space (near-miss acks, tampered / forked / expired ledgers, halt-flag combinations) no input makes a gate return live without both exact acks, and that a risk-reducing close is never blocked. That is property-tested over generated inputs, still not a formal proof; and whether a red CI blocks merge is a branch-protection setting, not something the repo proves.
- The dead-man safe-halt runs in NOTIFY mode — it pages the operator on absence and halts nothing. Even in fire mode it can only remove authority; it cannot liquidate.
- Risk-reducing sells bypass the gate on purpose. The AI can act to reduce exposure without the live ack. Intentional — an exit is never trapped — but a real carve-out.
08 — Verify me
Every file:line above is real. The repos are private, so this is a citation, not an invitation to clone: an outsider can hold this page to its own specificity and internal consistency, not re-run the named tests or read the drill receipts in drills/*/state/. That is the honest limit of reading it. The claim this page makes is exactly as strong as the code — no stronger. Where it is a human convention, we said so.