With the non-git deadlock resolved as an upstream harness defect (see the upstream-draft tracker), the failure message is the only thing this plugin controls that a stuck user will ever see. Every part of it is currently wrong or useless: the exit status it prints is a constant, the helper's four-way exit taxonomy is discarded before it can be reported, the first stderr line — the one guaranteed to reach the transcript — carries a diagnostic rather than an action, and a distinct failure mode is reported under the wrong cause. These four are one file, one sitting.
Rows
Evidence
SC-F2 / SC-F3 — the status is a constant, the taxonomy is discarded
plugins/source-control/hooks/worktree-create-gate.sh:144-148:
if ! path="$(bash "$helper" "${args[@]}")"; then
status=$?
printf 'worktree-create-gate: %s exited %s; worktree not created\n' "$helper" "$status" >&2
exit 1
fi
$? in the body of if ! cmd is the status of the negated compound, which is 0 whenever cmd failed. Proven with a function returning 7. The taxonomy being thrown away is documented at plugins/source-control/scripts/worktree-create.sh:29-33:
# 0 success — worktree created; path on stdout
# 2 usage error — unknown/missing flag, or a --name git rejects as a branch
# 3 refuse — no usable external root (guidance on stderr); nothing created
# 4 environment error — not a git repo, or `git worktree add` failed
The exit 1 direction is correct and must be preserved — https://code.claude.com/docs/en/hooks, fetched 2026-08-11: "The exception is WorktreeCreate, where any non-zero exit code aborts worktree creation." The defect is confined to the message text. Note the honest constraint from the audit: even perfect propagation would not let the hook say "not applicable" (that is an upstream gap), so the value here is diagnostic and human-facing, not a behavior change.
This bug has already cost real time: the constant 0 is what produced the "a hook exited 0 while failing" theory that a whole verification pass had to unwind.
SC-F4 — no remedy in either line
The two producing sites, plugins/source-control/scripts/worktree-create.sh:259:
printf '%s: --repo-dir is not inside a git repository: %s\n' "$PROG" "$repo_dir" >&2
and hooks/worktree-create-gate.sh:146 (quoted above). Neither string contains an action a user can take.
Wording constraint if implemented: the remedy line should name worktree.bgIsolation: "none" — a genuine harness-side stand-down, confirmed verbatim at https://code.claude.com/docs/en/agent-view, fetched 2026-08-11: "To turn off worktree isolation for a repository where git worktrees are impractical, set worktree.bgIsolation to \"none\"." It must not advertise worktree_create_gate_enabled as relief until that option's behavior is measured (see the gate scope/opt-out issue, cross-linked below).
Partially-confirmed doc leg: the "stderr from a hook that exits 0 goes to the debug log only, never the transcript" half was re-confirmed verbatim on 2026-08-11; the "transcript surfaces the first line of stderr" half is auditor-only (2026-08-10) and was not independently re-fetched.
SC-F10 — the wrong cause is printed
plugins/source-control/hooks/worktree-create-gate.sh:67:
payload="$(hook::buffer_stdin)"
— no status check. plugins/source-control/hooks/hook-utils.sh:596 (inside hook::buffer_stdin, which begins at :522):
[[ -n "$input" ]] || return 1
The misattributing message is at hooks/worktree-create-gate.sh:92 → "the WorktreeCreate payload carried no .name; refusing rather than guessing a worktree name". The direction of failure is correct (fail-closed); this is message accuracy only. The jq-absent fail-open path is handled correctly (jq_rc == 127 passes the raw buffer through to the sed fallback at gate :72-86) and should not be disturbed.
Adjacent: #2146 ("every hook calling hook::require_jq fails OPEN when jq is absent, while the same guard fails closed on length") touches the same utility file — different function, not a duplicate, but read it before editing hook-utils.sh.
Provenance
Severity: MED (cluster highest; SC-F10 is LOW) · Provenance: AUDITOR_VERIFIED
Origin: handoff-inbox item 20260811-021645-plugin-audit-four-components-and-guard-deadlock-ownership
Ledger: .work/handoff-inbox-batch-4/ledgers/I9-021645-four-components.md § "Lane D — source-control / worktree-create-gate"
Verified against repo HEAD 685dd381.
With the non-git deadlock resolved as an upstream harness defect (see the upstream-draft tracker), the failure message is the only thing this plugin controls that a stuck user will ever see. Every part of it is currently wrong or useless: the exit status it prints is a constant, the helper's four-way exit taxonomy is discarded before it can be reported, the first stderr line — the one guaranteed to reach the transcript — carries a diagnostic rather than an action, and a distinct failure mode is reported under the wrong cause. These four are one file, one sitting.
Rows
SC-F2—plugins/source-control/hooks/worktree-create-gate.sh:144-148—status=$?insideif ! path="$(…)"is always0, so the message reports a constant, not the helper's real exit code.SC-F3—plugins/source-control/scripts/worktree-create.sh:29-33→plugins/source-control/hooks/worktree-create-gate.sh:144-148— the helper's documented0/2/3/4exit taxonomy collapses onto a single gateexit 1, so "not a repository", "noworktree_rootconfigured" and "illegal branch name" are indistinguishable to the user.SC-F4—plugins/source-control/scripts/worktree-create.sh:259andplugins/source-control/hooks/worktree-create-gate.sh:146— neither stderr string names a remedy, and the first line (the one the transcript surfaces) is the least actionable of the two.SC-F10—plugins/source-control/hooks/worktree-create-gate.sh:67and:92—hook::buffer_stdin's failure status is ignored, so an empty or unparseable payload surfaces as "the WorktreeCreate payload carried no.name" — the wrong cause.Evidence
SC-F2 / SC-F3 — the status is a constant, the taxonomy is discarded
plugins/source-control/hooks/worktree-create-gate.sh:144-148:$?in the body ofif ! cmdis the status of the negated compound, which is0whenevercmdfailed. Proven with a function returning 7. The taxonomy being thrown away is documented atplugins/source-control/scripts/worktree-create.sh:29-33:The
exit 1direction is correct and must be preserved — https://code.claude.com/docs/en/hooks, fetched 2026-08-11: "The exception isWorktreeCreate, where any non-zero exit code aborts worktree creation." The defect is confined to the message text. Note the honest constraint from the audit: even perfect propagation would not let the hook say "not applicable" (that is an upstream gap), so the value here is diagnostic and human-facing, not a behavior change.This bug has already cost real time: the constant
0is what produced the "a hook exited 0 while failing" theory that a whole verification pass had to unwind.SC-F4 — no remedy in either line
The two producing sites,
plugins/source-control/scripts/worktree-create.sh:259:and
hooks/worktree-create-gate.sh:146(quoted above). Neither string contains an action a user can take.Wording constraint if implemented: the remedy line should name
worktree.bgIsolation: "none"— a genuine harness-side stand-down, confirmed verbatim at https://code.claude.com/docs/en/agent-view, fetched 2026-08-11: "To turn off worktree isolation for a repository where git worktrees are impractical, setworktree.bgIsolationto\"none\"." It must not advertiseworktree_create_gate_enabledas relief until that option's behavior is measured (see the gate scope/opt-out issue, cross-linked below).Partially-confirmed doc leg: the "stderr from a hook that exits 0 goes to the debug log only, never the transcript" half was re-confirmed verbatim on 2026-08-11; the "transcript surfaces the first line of stderr" half is auditor-only (2026-08-10) and was not independently re-fetched.
SC-F10 — the wrong cause is printed
plugins/source-control/hooks/worktree-create-gate.sh:67:payload="$(hook::buffer_stdin)"— no status check.
plugins/source-control/hooks/hook-utils.sh:596(insidehook::buffer_stdin, which begins at:522):The misattributing message is at
hooks/worktree-create-gate.sh:92→ "the WorktreeCreate payload carried no .name; refusing rather than guessing a worktree name". The direction of failure is correct (fail-closed); this is message accuracy only. The jq-absent fail-open path is handled correctly (jq_rc == 127passes the raw buffer through to thesedfallback at gate:72-86) and should not be disturbed.Adjacent: #2146 ("every hook calling
hook::require_jqfails OPEN when jq is absent, while the same guard fails closed on length") touches the same utility file — different function, not a duplicate, but read it before editinghook-utils.sh.Provenance
Severity: MED (cluster highest;
SC-F10is LOW) · Provenance: AUDITOR_VERIFIEDOrigin: handoff-inbox item
20260811-021645-plugin-audit-four-components-and-guard-deadlock-ownershipLedger:
.work/handoff-inbox-batch-4/ledgers/I9-021645-four-components.md§ "Lane D —source-control/worktree-create-gate"Verified against repo HEAD
685dd381.