Skip to content

guard-main-checkout.sh (the Edit/Write half of the worktree-first pair) ships no self-test matrix — its Bash sibling has 121 cases #11800

Description

@os-steve

Found while wiring #11514 (PR #11799), which runs every .claude/hooks/*.selftest.sh in CI. Filing rather than fixing: #11514 wires execution only and authoring a matrix is a .claude/** change.

Measured

.claude/hooks/ holds three hooks and two self-tests:

.claude/hooks/guard-main-checkout-bash.selftest.sh 121 cases
.claude/hooks/guard-main-checkout-bash.sh
.claude/hooks/guard-main-checkout.sh <-- no matrix
.claude/hooks/guard-shared-stash.selftest.sh 32 cases
.claude/hooks/guard-shared-stash.sh

guard-main-checkout.sh is the Edit/Write/NotebookEdit half of the worktree-first enforcement — the half named in CLAUDE.md and AGENTS.md as the primary guard. It has no self-test at all. Its Bash sibling, which exists only because #5790 found that the Edit/Write hook could be bypassed through sed -i / tee / redirection, has 121.

Why it matters now specifically

Once #11799 lands, CI runs every matrix in that directory, so a regression in guard-shared-stash.sh or guard-main-checkout-bash.sh goes red. guard-main-checkout.sh is the one hook for which the answer stays "nothing checks it" — and after the wiring, .claude/hooks/ reads as a covered directory, which is the more expensive state than being visibly uncovered. The wiring is discovery-based, so a matrix added for it is picked up with no workflow edit.

The failure direction is the quiet one: this hook fails open by design for anything it cannot parse, so a regression that makes it fail open more often produces no error anywhere — edits into the shared primary checkout simply start being allowed, and the symptom is another agent's tree being clobbered, attributed to something else.

Shape of the work

Port the case matrix the way guard-main-checkout-bash.selftest.sh was ported from objectui: hermetic fixture (its own git init + linked worktree + a non-repo dir under $TMPDIR), feed the hook the PreToolUse payload shape for Edit/Write/NotebookEdit, assert block/allow per case, exit 0 = all hold. The block/allow verdicts to pin are the ones its header already states, plus the OS_ALLOW_MAIN_EDITS=1 exception and the sibling-repo path cases (objectui, cloud), which are the ones prose alone has been carrying.

Prior art in the same directory

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions