feat(break-glass): the escape hatch as a script that REFUSES to exit 0 unless the read-back matches β verified end-to-end on real main - #1194
Open
ZacxDev wants to merge 3 commits into
Open
Conversation
β¦0 unless the read-back matches Rank 6 of the clipboard-research handoff: the deterministic version of the prose break-glass recipe. It was deliberately not built before, because shipping an UNTESTED command into a break-glass path is the exact failure rank 2 corrects, and it cannot be proven end-to-end without opening the protection window on `main`. Operator decision: open it. π΄ VERIFIED END-TO-END AGAINST REAL `main`, 2026-09-01T01:50Z β real DELETE, real PUT, exit 0. All 11 keys then read back IDENTICAL to a capture taken independently before any of this existed (the script's own read-back would be a control sharing the step under doubt). The window was open a few seconds; nothing merged through it β #1191 and #1169 were both still open and blocked afterwards, and #1192 had merged at 01:47:57Z with both checks SUCCESS, over two minutes before the window opened. What it encodes, all three from measured failures: - never PATCH. `PATCH .../required_status_checks` 404s once the sub-resource is deleted; only a full PUT of the whole object closes it. - a PARTIAL PUT returns 200 and silently drops every key it omits, so the read-back is not optional and the diff is reported key-by-key. - `restore_and_verify` is ONE function called on the normal path AND from the EXIT trap, so the trap can never hold a command that has not just been exercised. A restore failed inside a trap that fired exactly as designed, because the untested command was in the safety net. Nothing is redirected to /dev/null: the 2026-08-30 restore only looked silent because `>/dev/null 2>&1` discarded the message naming the cause. Self-test mode (no --pr) opens the window, merges nothing, closes it β the SAME code path as a real run, so the dangerous half is not a mode that has never executed. Coverage: 13 tests driving a `gh` stub that shells out to REAL jq for every --jq, because a stubbed binary whose --jq never runs would leave the capture projection exercised by nothing. Mutation sweep is self-checking (control GREEN + positive control KILLED, else SWEEP-INVALID): 6/6 mutants killed, survivors none. Two guards were found VACUOUS by that sweep and fixed rather than annotated: - the REQUIRED_KEYS `has()` loop could never fire β the projection names all 11 keys unconditionally, so jq emits `null` for a missing source key and has() is always true. Replaced by a TYPE check on the 8 booleans, which is the real hazard (a null PUT lets GitHub pick its own default). - the NULLABLE_KEYS `has()` loop was vacuous for the same reason and is deleted; that invariant belongs to the filter and is pinned there. The fixture that kills the type check is the one case reaching it β every other bad-capture case is also caught by the later zero-checks guard, which is why deleting the loop first left the suite green. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DXp5UYUoZxPVUS6qejBUf7 Claude-Session-Id: baaf1e44-2565-400b-884b-6dc557ad1faf
Claude-Session-Id: baaf1e44-2565-400b-884b-6dc557ad1faf
β¦erged tree Claude-Session-Id: baaf1e44-2565-400b-884b-6dc557ad1faf
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Rank 6 of the clipboard-research handoff. It was deliberately not built before: shipping an untested command into a break-glass path is the exact failure rank 2 corrects, and it cannot be proven end-to-end without opening the protection window on
main. Operator decision this session was to open it.π΄ Verified end-to-end against the real
main2026-09-01T01:50Zβ realDELETE, realPUT, script exit 0.The script's own read-back is a control that shares the step under doubt, so the verdict comes from a capture taken independently before any of this existed:
Nothing merged through the open window. It was open a few seconds; afterwards
#1191and#1169were both still open and blocked, and#1192had merged at01:47:57Zwith both checksSUCCESSβ over two minutes before the window opened.What it encodes β three measured failures
PATCH.PATCH .../required_status_checks404s (Required status checks not enabled) once the sub-resource is deleted; it updates checks that exist and cannot recreate one. Only a fullPUTof the whole object closes the window.PUTreturns 200 and silently drops every key it omits βenforce_admins, force-push and deletion settings included. So the read-back is not optional, and a mismatch is reported key-by-key.restore_and_verifyis ONE function, called on the normal path and from theEXITtrap. A restore failed before inside a trap that fired exactly as designed, because the untested command was in the safety net. That shape is now unrepresentable.Nothing is redirected to
/dev/null: the 2026-08-30 restore only looked silent because>/dev/null 2>&1discarded the message naming the cause.Self-test mode (no
--pr) opens the window, merges nothing, closes it β deliberately the same code path as a real run, only the payload differs. The dangerous half is not a mode that has never executed.Exit codes
0ok Β·2usage Β·3bad capture (nothing touched) Β·4open failed (nothing touched) Β·5merge failed (window still closed + verified) Β·6RESTORE FAILED β main may be unprotected Β·7read-back mismatchCoverage
13 tests. The
ghstub shells out to realjqfor every--jq, and the filter it runs is extracted from the script β a stubbed binary whose--jqnever runs would leave the capture projection, the thing deciding which keys survive a restore, exercised by nothing.Mutation sweep is self-checking (control GREEN + positive control KILLED, else
SWEEP-INVALID):put-becomes-patchreadback-skippedbool-type-loop-removedzero-checks-guard-removedappid-guard-removedrestore-failure-swalloweddelete-removedsurvivors=none.π΄ Two guards the sweep found VACUOUS β fixed, not annotated
REQUIRED_KEYShas()loop could never fire: the projection names all 11 keys unconditionally, so jq emitsnullfor a missing source key andhas()is always true. Replaced with a type check on the 8 booleans, which is the real hazard β restoring anulllets GitHub pick its own default.NULLABLE_KEYShas()loop was vacuous for the same reason and is deleted; that invariant belongs to the filter and is pinned bytest_the_capture_projection_runs_under_real_jq.The fixture that kills the type check is the only case that reaches it β every other bad-capture case is also caught by the later zero-checks guard, which is why deleting the loop first left the suite green. That is the "an earlier/later check always wins" trap, caught by isolating the mutation.
Scope
CLAUDE.mdgains a pointer to the script beside the four-step recipe (kept β the script does not replace knowing what it does).test_break_glass_note.pyandtest_ci_claim_matches_reality.pystay green; the<!-- merge-gate: other -->marker is untouched.β It still merges through an open window, so anything with auto-merge armed can land ungated while it runs. The header and the CLAUDE.md note both say so.
π€ Generated with Claude Code
https://claude.ai/code/session_01DXp5UYUoZxPVUS6qejBUf7