The question that gates every autonomous agent is “what can it do without asking me?” I’d already answered it the right way, on paper. The permission model for the co-CEO harness I was building, the thing that decides whether my agent station can act on its own backlog without me in the loop, split everything the agent could do into five lanes, ordered by the capability each one required. Tests were green: 80 passing. I read that as proof the gate was live.

It wasn’t. A same-day Codex review, followed by Manus checking the same code independently, found that the dispatcher never called it.

Five lanes, one working day

The lanes, in order of how much they let the agent touch:

  • read-only: query files, JIRA, logs, DNS, the database. Touches nothing.
  • local-write: write to a scratch file, the vault, a draft. Lives on disk, reversible by deleting it.
  • branch-write: commit to a feature branch, mutate local repo state. Reversible by git, but now it’s in version control.
  • external-write: a network write to a system outside my control: a JIRA transition, a DNS record, a third-party API call. Leaves the building.
  • money-comms-prod: anything that spends money, talks to a customer, or mutates production. The point of no return.

I mapped each lane to a tier, 0 through 2, in tiers.py: tier_for(action) reads requiredCapability off the proposed action and returns the number. Tier 0 auto-runs. Tier 1 sits in a 30-minute veto window before it fires. Tier 2, money or prod or anything customer-facing, always waits on me. 80 tests covered it, and they all passed, because tier_for() really does return the right number for a given capability. What none of those tests checked was whether the thing that dispatches work ever calls it.

It didn’t. I asked Codex for a review at high effort before letting the harness run unattended, then had Manus check the same code independently. Codex’s reply opened with the sentence that mattered: “tick.py bypasses the resolver entirely. Once pause is lifted, the live sweep will dispatch straight from proposed based on tier_for only; it never calls tiers.resolve, never opens a veto window, and dispatch does not require an approved or auto-approved state.” I went and read the dispatch function myself. The line that decided whether an action ran was if not paused: dispatch(item). Not a tier check. Not a veto window. Not tiers.resolve() at all, the function that actually applies the policy, records the state transition, and enforces the escalation. tier_for() got called two lines above it, for a log line. Manus read the same files independently and came back with the same verdict, worded just as flatly: “The safety gate exists and is correct in isolation. It is never called on the live dispatch path. That is not a configuration gap; it is a wiring failure.” It also flagged something Codex hadn’t touched: the 30-minute veto window assumes someone is watching the ledger, and I sleep. That’s where the presence gate came from, not the tier model. Codex’s closing line named the actual failure mode better than I could: the safety story wasn’t conservative but unproven, it was policy exists on paper, but the live execution path skips it.

The fix, same day, commit 9d23a2b:

# Resolve the proposal through tier POLICY before any dispatch.
# tier_for() alone is not a gate. tiers.resolve() runs the Tier-2
# escalation, the Tier-1 consult tie-break plus veto window, and records
# the state transition the dispatch state-guard keys off. Skipping it
# (the old path) made the whole tier/veto/escalation policy dead code
# on the live dispatch path.
resolution, recorded_state = tiers.resolve_and_record(item)
if resolution["decision"] == "auto" and not paused:
disp = delegation.dispatch(item, contract)

The comment in that diff is longer than the code it explains, which is usually a sign the code used to be wrong in a way worth remembering.

The rest of what a review like that finds

Once Codex and Manus were both inside the dispatch path, they kept finding the same shape of gap: something that looked like a safeguard from a distance and was a no-op up close. delegation.dispatch would run even with no ledger history for an item, so I made it fail closed, refuse unless the item’s state was already auto-approved, no history means not dispatchable. A broken consult call or a broken spawn used to fail open; now both fail closed too. A worker that goes stale mid-run now trips a circuit breaker that pauses the whole harness instead of quietly stalling. Verification switched from trusting a worker’s self-report to reading the actual artifact it was supposed to produce. And because none of this should run unattended the first time, a presence gate now checks whether I’m actually at the keyboard: if I’m not, the harness drops to propose-only regardless of tier, no matter how confident the classifier is.

Propose-only means every wake cycle the agent does the full reasoning, produces one recommended action, a blocked-on-human queue capped at three to five items, and the exact handoff payload it would have dispatched, and then executes none of it. That’s what it’s doing right now. The test suite went from 80 to 111 green over that one day. The internal ticket tracking the hardening sits at six items closed out of seven. The seventh is a shadow-validation week: run the harness in dry-dispatch, watch it propose things for a week, and see if I’d have said yes. It hasn’t started yet. The five lanes were right the whole time. The harness is still paused.