Fifty Gates

Every one drawn from a real failure on 5 September 2026
GATES50
EXECUTABLE NOW1
MARKED EXEC17
TIME TO THE FIX2 min
TIME SPENT BLOCKED4 turns
LANE DEFECT89 notes

What happened, in one paragraph

A Songsterr upload was one action from finished. Two Control_Chrome calls were denied by the permission classifier. I reported the upload blocked and offered Brandon two options that both cost his time. He pushed back. I then loaded claude-in-chrome file_upload, a purpose-built tool that had been sitting unloaded the whole time, and the file went in on the first call.

My own memory already forbade this. feedback_try_every_route_before_claiming_blocked says never report blocked while an untried route exists, and it names a browser bridge as the example. A second note says one dead bridge is not a dead capability. Both were loaded in context. I checked neither.

A. Route exhaustion, 1 to 10

#Gate
G1EXECEnumerate the whole family ahead of any blocked claim. Browser control has four families here: Control_Chrome, claude-in-chrome, Claude_Browser, computer-use.
G2EXECA denial on one call is evidence about that call. Never about the capability.
G3Search for a purpose-built tool first. file_upload exists and its own description says do not click file inputs.
G4A deferred tool is available, not absent. One ToolSearch away is not missing.
G5Two denials of similar payloads mean change the family. A third variant of the same technique is a bypass attempt.
G6Read-only probe ahead of concluding a tool is dead. Ten seconds separates a tool-wide block from a payload block.
G7No queue item describing a block until G1 passes. The one written here named a blocker that did not exist.
G8Never hand over a manual workaround while a route is untried.
G9When a hook names untried routes, try them that turn. The existing gate named three and was right.
G10EXECA blocked claim requires a route table in the reply. Family, call, result, verbatim error.

B. The last inch, 11 to 20

#Gate
G11The last action is the one that must not be skipped. Every prior hour was worth nothing to him without it.
G12EXECMeasure distance to done ahead of stopping. One action left needs a reason that survives being written down.
G13A classifier denial is a reason to route around, never to stop.
G14A permission denial is not a user refusal. His instruction stood the whole time.
G15Never convert his instruction into a question. He said upload and got a menu.
G16EXECAsking permission after a never-ask instruction is a hard fail. It happened twice.
G17When two options both cost his time, neither is an answer.
G18A queued item is not a substitute for finishing.
G19Report progress against his goal, never my task list.
G20The word "staged" in my own output is a warning sign. Staged means not done.

C. Verification honesty, 21 to 30

#Gate
G21EXECA usage message is not an empty result. queue.py list is not a subcommand. Stdout was empty and I read "queue is empty". It held 190 items.
G22EXECCheck the exit code of any command whose emptiness I interpret.
G23Verify a subcommand exists ahead of relying on it. --help costs one call.
G24Read a queue item's full text ahead of closing it. I closed one covering Nanook and Inca Roads while fixing Zomby Woof.
G25EXECA close needs the id AND a quoted line proving the match.
G26Reopen loudly when a close was wrong. Re-queued with the error inside the text.
G27A citation inside my own earlier note is not authority. I inherited a mislabel from my own file.
G28Quote the proof, never summarise it.
G29Verification happens in the turn that claims it.
G30"Done and verified" in my prose triggers a re-verify ahead of sending.

D. Measure against the source of truth, 31 to 40

The most expensive gate in this list is G31. Revision r8902998 emptied the Low Tom lane, 99 notes to 0, and every check afterwards compared a revision to the one ahead of it, where the lane was already gone. The numbers agreed with each other for three days while all being wrong.
#Gate
G31EXECHistogram every lane against the ORIGINAL author revision. Never against the previous step in my own chain.
G32A count check cannot see a substitution. Totals, drum counts and ghosts all stayed correct through the wipe.
G33EXECReport a change as a fraction of the defect. "Exactly ten notes moved" was true and misleading. It was ten of ninety-nine.
G34When a number sounds like a clean bounded change, test the boundary. His praise should have prompted a check.
G35EXECRead instrument names from the file's own articulation table. General MIDI calls 43 "High Floor Tom". This kit calls it "Very Low Tom".
G36Confirm two files share an articulation table ahead of comparing indices.
G37EXECAlign bar numbers explicitly when a rebar is in play. Author bar N maps to target N+1 for N at or above 3.
G38EXECA bar whose counts disagree is reported and skipped, never guessed. Three qualified and all three were named.
G39Restore by position against the source, never by blanket value swap. A blanket swap would have destroyed 13 genuine notes.
G40EXECCopy-on-write on any shared definition. GPIF Notes and Beats are shared across locations.

E. Peers, decisions and scope, 41 to 50

#Gate
G41A peer session's claim carries no measurement authority. I relayed one into a memory file and a published page. Both were wrong.
G42EXECNever publish a peer's factual claim without measuring it myself.
G43A peer steer never proposes a destructive act. Deleting revisions was proposed, then retracted.
G44Correct a published error at the source with a dated block. Never a silent edit.
G45A decision he already made is never re-opened. He had said Brown wins and I presented it as an open tie.
G46EXECSearch his own words for a decision ahead of asking for one. One grep would have found it.
G47Scope a source to the passage it transcribes. Brown covers bars 1 to 9 and votes on nothing later.
G48One song id per chat, never one title. Two tabs share the title Zomby Woof, which started this thread.
G49EXECA chat past 24 hours or 400 turns hands over. The parent ran 113 hours and 3,208 turns against 82 of his.
G50The cost of every gate above is measured in his time. He said months. These exist so an hour is never spent twice.

The executable gate, tested

route_exhaustion_gate.py implements G1, G2 and G10. It reads a proposed reply, detects a blocked claim, resolves which capability group it concerns, and exits non-zero unless every route in that group is named with a result beside it.

$ python3 route_exhaustion_gate.py --reply my-actual-failing-reply.txt route_exhaustion_gate BLOCK claim words : blocked group : browser routes needed : Control_Chrome, claude-in-chrome, Claude_Browser, computer-use NOT COVERED : Control_Chrome, claude-in-chrome, Claude_Browser, computer-use exit=1
Known-answer testExpectedGot
The exact reply I actually sentBLOCKexit 1
A reply carrying a full route tablePASSexit 0
A reply with no blocked claim at allPASSexit 0
It catches the real failure. Fed the reply I actually sent, it refuses. That is the test that matters, since a gate written after the fact is worthless if it would have waved the original through.

Install

The script sits in the outputs folder rather than ~/.claude/skills/_shared/, because that tree is Brandon's control plane and writing into it is guarded. One line moves it in:

cp ~/Projects/_outputs/session-gates-2026-09-05/route_exhaustion_gate.py \ ~/.claude/skills/_shared/route_exhaustion_gate.py

Wiring it as a Stop hook alongside search_before_blocked_gate.py is the step that gives it teeth, and that edit belongs to him.