THINKING · 27 JULY 2026

Six gates in a day. Every one caught us first.

The companion piece to the last one. That was about a human waving a gate through. This is about the gates themselves — and the fact that not one of them was found wanting by review. Every single one was found wanting by being run.

We spent one day building controls that should have stopped the thing described in the previous essay: a claim about our own product that went live before its evidence existed. Six of them shipped. Within minutes of existing, each one found a real defect — in us.

That is the useful part, and it is the part nobody publishes. So here they are in order, specific enough that you can check them.

1. The control was wrong in the exact case it was built for

The gate holds a deploy that adds or strengthens a public claim about ourselves. Its acceptance test replays our own incident. First run: the gate let it through.

The old wording said the feature was “in final validation… before it ships as a product feature”. The word ships scored as a shipped claim. So the replacement text — genuinely stronger, genuinely further along — looked like a downgrade, and downgrades pass silently by design.

A gate that reads a plan as an accomplishment does not merely miss things. It inverts. Ours would have waved through the one edit it was written to stop, and we would have believed we were covered.

2. It took our word for the evidence

The gate demanded a basis for every strengthened claim, recorded it on the chain, and never looked at it. --basis artifact:/tmp/nothing-here.txt sailed straight through.

A control that holds unbacked claims while accepting your word about the backing hasn’t solved the problem. It has moved it up one level, where it is harder to see.

So now each kind of evidence gets resolved. A cited file must exist, and its hash is recorded. A cited test is run. A cited audit row must exist, be sealed, and belong to your organisation. “I take responsibility, there is no artifact” remains a legitimate answer — it is just permanently marked as one, which only means anything now that the other kinds are actually checked.

3. The evidence checker’s first act was to certify a page that doesn’t exist

Cite a URL and we fetch it. Fine — except most hosts answer unknown paths with 200 and their homepage. A report we never wrote verified cleanly on the first try.

The first fix failed too, and taught more than the bug: comparing the fetched bytes against the site root never matched, because the page carries a per-request nonce. Which means an external URL’s content hash is not reproducible on a live site. That is not a detail. It is the reason external is now the weakest evidence we accept, ranked below a file, a test, and a row on the chain — and why the product says reachable — confirm it is the document you mean, and never says verified.

4. Shipping the feature we were claiming took production down

Mounting the hardware-key routes raised a missing-module error at import time. Every route died with it, including the health check. One hundred and three tests had passed, because tests run in an environment where the package already exists.

Two causes, both worse than the missing line. Our deploy image didn’t install requirements.txt at all — dependencies came from a pinned base image, so a library could sit declared and absent for weeks. And nothing checked that what the code imports is what the repo claims to need. When we added that check, it immediately found six more imports living on somebody else’s dependency graph.

5. The gate locked us out of fixing the outage

This is the one I’d put on a wall.

With the API down, the approval tray couldn’t be reached. Every poll failed. The hook returned timeout, which callers correctly treat as a refusal. Both git push and the deploy command were gated.

The fix that would restore the service could not ship, because shipping it required the service.

Fail-closed is right for a refusal. Fail-closed on infrastructure creates a state with no exit — and, worse, creates the pressure to add a permanent bypass, which is how gates get quietly disarmed for good. The distinction the code was missing: we reached the approver and nobody answered is a decision. We never reached the approver is not a decision at all, and has to fall back to asking someone else, not to saying no.

6. The most dangerous answer was the confident one

A security key qualifies as hardware-backed only if its attestation chains to a certified root. Without the manufacturer metadata, that check cannot run — and ours reported hardware-backed: false.

Which reads as this key isn’t good enough. The honest answer was we didn’t look. The same key, once the metadata was loaded, classified at the higher tier.

The fix wasn’t better detection. It was making the two impossible to confuse: the result now carries whether the check could run at all, and the interface has a third state that says so. This came up three separate times in one day — the attestation check, the URL that returns 200 for everything, the approver that was never reached. Every time, collapsing “it failed” into “we couldn’t check” produced a confident answer that was wrong in the more dangerous direction.

What I’d take from this if it were someone else’s day

A control is a hypothesis until it has refused something real. Six controls, six first findings, all our own, none from review. If you have shipped a governance feature that has never once blocked you, you have shipped a diagram of a governance feature.

Put detection where the ground truth is. We were asked whether keyword matching was enough. It isn’t — but the answer is not a classifier on the input either. Guessing what someone meant is the hardest question available and has nothing to check against. Checking a claim against a record you already hold is a lookup. Same budget, an order of magnitude more value, and an auditor can follow it.

A gate must not be able to lock you out of its own repair.

Fix it in the product, not just in your own workflow. We taught our own deploy path to verify cited evidence at midday. The customer-facing equivalent still accepted any string until evening. Shipping the strict version to yourself and the loose one to the people paying you is not a defensible split, and it is the default outcome unless somebody goes looking for it.

The uncomfortable one

None of this would exist if we hadn’t done the thing the product is against. An agent was told there’s no recorded trace, update the site anyway — and it did, publishing first and noting the gap second. The right order is basis, then publish. Judgment is not a control; that is the entire thesis, and we had to relearn it on ourselves.

By the end of the day the same sentence was live again, this time pointing at a specific row on a hash chain: a per-action signature from a key that classifies as hardware-backed because its attestation resolved against the manufacturer metadata. The gate checked that row existed and was sealed before putting the approval in front of a human.

The difference between the morning version of that sentence and the evening one is not the wording. It is that one of them can be checked by someone who has no reason to trust us.