Skip to content

Show the gate's hash in -Status, because the version label had already lied - #46

Merged
wshallwshall merged 2 commits into
mainfrom
claude/gate-version-stamp
Jul 29, 2026
Merged

Show the gate's hash in -Status, because the version label had already lied#46
wshallwshall merged 2 commits into
mainfrom
claude/gate-version-stamp

Conversation

@wshallwshall

Copy link
Copy Markdown
Collaborator

A one-merge-old follow-up to #40.

Rules 1a, 3c and 3d shipped without bumping $GateVersion, so -Status printed the same version on both lines directly above a *** STALE *** verdict:

installed : ...\worktree_gate.ps1  v2026.07.29.1
source    : ...\worktree_gate.ps1  v2026.07.29.1
parity    : *** STALE *** the running gate is NOT this checkout's script.

The SHA-256 comparison caught the drift correctly — that check was never in doubt. But a hand-bumped label contradicting the verdict beside it is exactly the ambiguity #40 set out to remove, and it took one merge to appear.

Changes

  • $GateVersion bumped, and its comment now says plainly that it is a human label while the hash is authoritative.
  • -Status prints the hash prefix next to each version, so agreement is visible rather than asserted. Lowercased — Get-FileHash returns uppercase while every other digest a reader meets here is lowercase, and two spellings of one hash invite a false "these differ".
  • Closes a gap Audit the session-drift control estate, then close the gaps it found #40 recorded as open: nothing exercised -Status at all. Now tested for exit 0 and for a hash on the source line, which always resolves. The test runs with CLAUDECODE=1 deliberately — -Status sits above the refusal precisely so a session can audit but not install, and that ordering is now covered too.

192 gate tests pass, 4 skipped.

🤖 Generated with Claude Code

…el had already lied

Rules 1a, 3c and 3d shipped without bumping $GateVersion, so after the merge `-Status`
printed the SAME version on both lines directly above a *** STALE *** verdict:

    installed : ... v2026.07.29.1
    source    : ... v2026.07.29.1
    parity    : *** STALE ***

The SHA-256 comparison caught the drift correctly -- that check was never in doubt. But a
hand-bumped label that contradicts the verdict beside it is precisely the ambiguity this
whole audit set out to remove, and it took one merge to appear.

Two changes. $GateVersion is bumped, and -Status now prints the hash prefix next to each
version, so agreement is VISIBLE rather than asserted and the label can never again be the
only thing a reader compares. Lowercased, because Get-FileHash returns uppercase while
every other digest a reader meets here is lowercase, and two spellings of one hash invite a
false "these differ".

Also closes a gap the audit recorded as open: nothing exercised `-Status` at all. It is now
tested for exit 0 and for a hash on the source line, which always resolves. The test runs
with CLAUDECODE=1 on purpose -- `-Status` sits above the refusal precisely so a session can
audit but not install, and that ordering is now covered too.

192 gate tests pass, 4 skipped.
@wshallwshall
wshallwshall enabled auto-merge (squash) July 29, 2026 17:09
… it the way Claude Code does

Every test in this suite passes -ReposFile so it can point at a temp allowlist. That is
right for isolation, and it left a hole the size of the whole product: a PowerShell
parameter default is NOT evaluated when a value is supplied, so the default expression
was never once executed by 192 passing tests.

It shipped broken in the previous commit. `Join-Path ( if ... )` opens a
COMMAND-INVOCATION group, so PowerShell parses the `if` as a command name and the hook
dies with "The term 'if' is not recognized" before its first line. A PreToolUse hook
that exits non-zero-but-not-2 lets the tool call through, so the gate was OFF for every
real tool call on this machine for the length of one install -- while
`install-gate.ps1 -Status` cheerfully reported IN SYNC, because parity compares the
installed copy to source and both were equally broken.

Caught only because the end-to-end probe invokes the hook the way production does, with
no arguments: 0/13 expected, every case CRASH(1). The unit suite was green throughout.

Two lessons worth more than the one-character fix, both now encoded:

Parity is not validity. `-Status` answers "is the installed copy the same as source",
which is silent about whether either RUNS. Those are different properties and I conflated
them.

An isolation seam that every test uses is a seam no test exercises. -ReposFile exists so
tests can be hermetic; that made the production default untested by construction.
tests/test_worktree_gate_default_reposfile.py now invokes the hook with NO arguments and
asserts three things nothing else could: that it runs at all, that an absent allowlist is
a SILENT allow (no stderr), and that an allowlist placed where the default expression
should look actually produces a deny. Reverting the fix fails all three.

This is also, exactly, the failure mode reported upstream this morning as
anthropics/claude-code#82323 -- a hook inert with no signal anywhere. I filed it and then
shipped it.
@wshallwshall
wshallwshall merged commit 3f86c46 into main Jul 29, 2026
32 checks passed
@wshallwshall
wshallwshall deleted the claude/gate-version-stamp branch July 29, 2026 18:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant