The one-line version
Archie stops being the claims adjuster and becomes the governor: it meters every live session from its own transcript, tells the model what it is doing wrong with the receipt attached, and, when the person has said so, stops the next model call before it is paid for.
What is true today
| Fact | Source |
|---|---|
A Claude Code PreToolUse hook can deny one tool call (hookSpecificOutput.permissionDecision: "deny" plus permissionDecisionReason, or exit 2). PostToolBatch with continue: false "stops the agentic loop before the next model call". ConfigChange and PreModelSwitch can block. UserPromptSubmit, SessionStart, PostToolBatch, Stop accept additionalContext the model sees. Sync hooks have a timeout; async: true hooks cannot block |
code.claude.com/docs/en/hooks |
Hook payloads carry no token usage. Usage is in the transcript: every assistant record has usage.{input_tokens, output_tokens, cache_read_input_tokens, cache_creation_input_tokens} |
the Claude adapter, crates/adapters/src/claude.rs |
Codex CLI hooks, from OpenAI's own docs and source: on by default (hooks.json or [hooks] in config.toml); PreToolUse denies with the same hookSpecificOutput.permissionDecision shape Claude Code uses (the older {"decision":"block"} still accepted); UserPromptSubmit blocks a prompt with {"decision":"block","reason"} or exit 2; PostToolUse decision: block replaces the tool result the model sees and takes additionalContext; no batch-level halt exists. The rollout JSONL carries event_msg/token_count records with last_token_usage and a rate_limits snapshot: used_percent, window_minutes, resets_at, plan_type, spend_control_reached |
learn.chatgpt.com/docs/hooks; codex-rs/protocol/src/protocol.rs; both fetched 2026-09-06 |
Archie already has: the Loop Sentinel (crates/outcomes/src/loops.rs: three identical tool calls, four revisions of one file, self-corrected vs human-rescued), archie session watch polling it, a pricing table with cache rates and estimate_model_tokens_cost_usd, the cache-economics report (#42) and cache doctor, window show pacing, and since v0.1.21 the loop: hook events over a socket, per-session state, tool_intents, verification-shaped commands classified by result |
the checkout at v0.1.21 |
| The tweet that started this (a $200/month Codex weekly limit gone in 14 hours) was not fetched; the search found Codex's 5-hour window and weekly cap, not the post | WebSearch |
The thesis, torn apart
1. "Circuit breakers." Three of the four levers exist, and the one people reach for first is the weakest.
| Lever | What it does | Trap |
|---|---|---|
PreToolUse deny |
blocks one call; the model sees the reason | a loop given a denial has a new error to loop on. Denial is not a pause |
PostToolBatch continue: false |
halts before the next model call; that is the only lever that stops the next spend | the person sees the turn end. It must say why, in the reason and in a row |
additionalContext |
the model reads a sentence of evidence before it acts again | the strongest lever by far, and the cheapest. A model told "you have edited lib.rs four times, the last test run failed with this line, no run has passed since" stops thrashing more often than one that is denied |
| a real pause | none. No harness exposes "suspend the session" | say so; continue: false per batch is the closest thing |
The burn-rate cap needs a meter, and hooks carry none. The meter is the
transcript, which Archie already tails (live_tail.rs): each new assistant
record gives tokens per turn, the cache read share, and the price at the
table rate. Two traps here. The provider's remaining quota is not in the
transcript, and the provider's accounting is not the token count: Archie
must never say "you have 30% left"; it reports burn against a budget the
person set. And every meter and every gate fails open: if archie serve is
down, the sync hook exits 0 inside its budget, or every agent on the
machine is bricked by its own governor.
2. "Asymmetric division of labour." Archie must not route. It never
sends a prompt to a model on its own (AGENTS.md), and a governor that
silently swaps a Fable turn for a Flash turn is a different product with a
different trust boundary. What Archie can do deterministically is measure
the split and say it: per session, tokens by model cost class by tool class
(read-only calls, edits, verification), which yields one number, the rake
share: what a frontier model spent on Read, Grep, Glob, ls. It can
put that number in front of the model at UserPromptSubmit ("the last five
sessions in this repo spent 62% on reads; the Agent tool takes a cheaper
model") and, if the person turns it on, deny the fortieth consecutive
read-only call by a frontier model with that reason. The threshold is high
because deep reading is often the job; the default is advisory. Choosing
the cheap tier is a policy line the person writes, naming a model, and the
fleet already has one (agy, Flash). Archie tells the agent; it does not
call the tier.
3. "Cache-prefix preservation." Archie cannot pin a system prompt, order
files, or make history append-only; the harness builds the prompt. What it
can do is name every break and block the two causes it can see. A break is
visible in the transcript as cache_read_input_tokens collapsing between
consecutive turns while input stays large, and its cause is usually a hook
event in the same window: ConfigChange, InstructionsLoaded,
PostModelSwitch, PreCompact/PostCompact, or a deferred_tools_delta
attachment (an MCP tool list changed). Archie attributes the break, prices
it from the table (the cache read rate is a pricing-table fact, not a
number this spec asserts), and writes the row. ConfigChange and
PreModelSwitch can be blocked mid-session by policy; compaction can be
blocked too, and must not be by default, because the alternative is a full
context. The discount figure people quote is the provider's; the pricing
table carries whatever it is.
4. The trap under all three: a governor that only lives in Claude Code
answers Rasmus with a working solution for the harness he is not using.
Codex's hooks, per third-party guides, offer PreToolUse deny and nothing
like PostToolBatch. For Codex the breaker is deny plus reason, weaker, and
that is unconfirmed until OpenAI's docs are read. Cursor and Gemini CLI: not
researched here.
The shape
transcript (usage per turn) ─▶ meter ─┐
hook events (v0.1.21 loop) ─────────▶ governor: policies ─▶ note | deny | halt | suspend ─▶ governor_events
▲ sync gate: archie hook --gate (≤ 50 ms, fails open)
Shipped, and where the build differs from the design above: batch
edits are deduplicated by tool_use_id, so an edit reported at PreToolUse
and again in the PostToolBatch payload counts once. When a spend halt and
a thrash halt trip on the same batch, only the spend halt suspends. The
gate's round-trip budget is 50 ms by default and ARCHIE_GATE_BUDGET_MS
overrides it. policy replay replays the current policy over indexed
intents, not over recorded decisions, because a tuning tool must answer
"what would this number have done". The [cache] rules parse but do not
act until v0.1.23. The loop rule counts consecutive identical calls in the
runtime; it does not yet read the Loop Sentinel. Codex's
cache_write_input_tokens lands in cache_creation.
The brake, v0.1.22
Two rules, both active, both blocking, both shipped in the first release. A meter that only reports is a post-mortem with a shorter delay; the point of the release is that a session running tonight cannot burn to zero.
Thrash halt. Evidence: the same file edited N times (default 3) with
no verification command passing between edits. The edits come from the
PostToolBatch payload's own tools array and from tool_intents; the
verification results come from the transcript, which is written before the
batch hook fires. Action at PostToolBatch: {"continue": false, "additionalContext": "<the ground truth>"} where the ground truth is the
file, the edit count, the last failing command and its last output line,
and the sequence numbers. The next model call does not happen. When the
person prompts again, the same text arrives as additionalContext on
UserPromptSubmit, so the model resumes knowing why it was stopped.
Session spend cap. Evidence: the session's tokens or dollars at the
table price since it started, from the transcript tail, against X tokens
or $Y in policy.toml (per session; a per-window cap across sessions is
the same check over sessions plus the live tail). Action at
PostToolBatch: continue: false with the reason. Then, until lifted,
every UserPromptSubmit for that session exits 2 with the same reason,
which blocks the prompt before any model call. That is a suspension the
harness can actually enforce. archie policy lift <session> clears it;
raising the cap in policy.toml clears it for everyone.
Fail open, and only open. The gate is one socket round trip with a 50 ms
budget. If archie serve is not there, the hook exits 0 and writes one
line to the spool so the miss is on record. When it is there, it stops the
spend. There is no fail-closed mode in v0.1.22: a governor that can brick
every agent on the machine when its own server dies is a worse failure than
one missed halt.
The meter is inside v0.1.22, not before it. Per-turn usage from the
transcript tail is what the cap reads, so it ships in the same release,
with archie session burn [id] as its face. Shadow mode exists as a
switch (action = "note"), not as a phase: a person who wants to watch
before they block sets it. archie policy replay stays as a threshold
tuning tool over the index and is not the product.
| Rule | Gate event | Action | Cleared by |
|---|---|---|---|
| thrash | PostToolBatch |
continue: false + ground truth; the truth repeats on the next prompt |
a passing verification of that file, or the next edit of a different file |
| spend cap | PostToolBatch, then UserPromptSubmit |
halt, then block every prompt with the reason | archie policy lift, or a higher cap |
| identical call ×3 | PostToolBatch |
note by default, halt if set |
the next different call |
| cache: config change or model switch mid-session | ConfigChange, PreModelSwitch |
block with the priced reason, if set | the person |
On Codex. OpenAI's docs confirm the levers, so the Codex brake ships in
v0.1.22, not later. Thrash: PostToolUse with decision: block replaces
what the model sees with the ground truth. Spend cap: once tripped, every
PreToolUse is denied with the reason and every UserPromptSubmit is
blocked until lifted. What Codex lacks is a batch-level halt, so the model
call that follows a denied tool still happens; the spec says so. What Codex
has that Claude Code does not is the provider's own quota in the transcript:
session burn reads the rate_limits snapshot and speaks it as "provider
says", the one place a remaining-percentage may be said aloud, because it is
the provider's number and not ours.
What each rule needs, and where it comes from
| Rule | Evidence | Already in Archie |
|---|---|---|
| thrash: file edited N times, no passing verification between | PostToolBatch.tools, tool_intents for edits; verification commands and results from the transcript |
yes: is_verification_command, result classification, the Loop Sentinel |
| blind loop: identical tool call three times | the Loop Sentinel | yes |
| spend cap | the transcript tail, per turn | tail yes; per-turn usage in the runtime is new |
| rake: M read-only calls by a frontier model | hook events with tool_name, model from the assistant records |
partly: cost class from the pricing table |
| cache break | consecutive usage records; hook events in the window |
cache doctor does it after the fact; in-flight is new |
Sequencing
| Release | What | Gate |
|---|---|---|
| v0.1.22, the brake | the meter, session burn, hook --gate on PostToolBatch and UserPromptSubmit (and PreToolUse/PostToolUse on Codex), the thrash halt and the spend cap active on both harnesses, governor_events, policy lift, `policy show |
check |
| v0.1.23 | rake share and its advisory, cache-break attribution in-flight, ConfigChange/PreModelSwitch blocks, PreToolUse gate for per-call denies |
one priced cache break named with its cause while the session runs |
| v0.1.24 | Cursor and Gemini CLI, if they expose a gate | one governed turn on each |
What stays true
No model is called by Archie; the governor speaks to the model through the
harness's own channel. No routing. No upload. Every action is a row with
its evidence. The gate fails open. The two rules that ship first block by
default once a cap or a threshold is written in policy.toml; writing it is
the person's decision, and a missing file means nothing is governed.
Open questions
- The provider's accounting: a budget in tokens at the table price is the honest unit. Should the policy also accept the harness's own cost line when it prints one, so the two can be compared?
haltends the batch; nothing resumes it but the person. Is aresume-with-note(the next prompt gets the governor's evidence as context) worth building, or is the halt reason enough?- Rake thresholds are guesses until replay reports the distribution of consecutive read-only runs on real sessions.