---
title: "The Approval Gate That Blocked Me for Fifteen Hours and Never Caught a Mistake"
canonical: https://dxdev.com/blog/2026-09-16_approval-gate-blocked-me-fifteen-hours/
datePublished: 2026-09-16
---
I could not push a docs-only change for about fifteen hours, and in that time the guard blocking me never caught a real mistake.

## How a gate that asks you a question can fail to ask

The guard covered one narrow case. Any push to a repo my business partner also reads needed my approval first, and approval meant replying in Discord from my own account. My agent sessions run with permission prompts turned off, which means a hook has no way to ask me inline. The only channel it had was one I was not looking at, because I was working in my editor.

Five approval requests were created across two sessions. Every one expired unanswered. I told the session to delete the whole thing, because the gate "seems to just be annoying blocks more than help."

The deletion took out the hook, its approval module, their tests and the registration, so the dispatcher went from 15 guards to 14. Earlier that afternoon I had removed a second Discord approval lock, this one on a paid API tool. Its after-the-fact spend alert stayed, since that one tells me and blocks nothing.

Two things stayed on purpose. The check on the autonomous path, which refuses to push to a partner-facing repo unless it is on an allowlist and fails closed, is what stopped an earlier incident in August. That guard was never the one I had been fighting. I also rewrote the messages in three files that still told agents to mint an approval token. Left alone, they would have sent future sessions looking for a module that no longer existed. They now state the rule directly: do not push to a partner-read repo without my go, and ask me in the session.

## Replacing a feeling with a log

Fifteen hours and zero catches is one data point about one guard. When I went to ask the same question of the other fourteen, I found that none of them recorded anything. "I keep getting blocked" could not be checked by me or by anyone else.

So the dispatcher now appends one line to a log file whenever any guard blocks a call. The logging is fail-safe, so a failure to write can never change a guard's decision. I fed it a bare push payload and an unbounded poll loop payload to see that each wrote a correct record.

## The log's first hour was 242 records of test noise

In its first hour the file held 242 records. Almost all of them were written by the test suite. The equivalence tests run the dispatcher as a real subprocess with synthetic payloads, so every guard they exercised wrote a live record. Nine of the records were for a guard that had already been deleted.

That made the log useless for the one question it exists to answer, which is which gates cost me real time. It also broke my standing rule that tests never touch live systems.

The fix is one condition. The block writer does nothing when the test runner's environment variable is set, unless a test explicitly redirects the log somewhere. I archived the 242 polluted records instead of deleting them, so the live log starts empty. Then I checked it in both directions. The 360 tests across the affected files passed and the live log file was never created. A real subprocess run outside the test runner with a bare push payload still exited with the block code and still wrote its record.

The log started clean at 4:21 PM that day. It has nothing to say about the other fourteen guards yet, and the next one that annoys me gets kept or deleted with a count next to it.
