Two questions about this hook have the same answer surface: where does it run, and how do I turn it off. Today the answer to the first is "everywhere on the machine" — WorktreeCreate supports no matcher, so a user-scope install runs a git-only hook in every directory. The answer to the second only became reachable at HEAD (#2193 declared the option for the first time), and nobody has ever measured what it does — the test suite asserts an external harness behavior that no current doc confirms in either direction.
Rows
Evidence
SC-F1 — registration is the only scoping seam, and it is unconditional
plugins/source-control/hooks/hooks.json:
"WorktreeCreate": [
{
"hooks": [
{
"type": "command",
"command": "\"${CLAUDE_PLUGIN_ROOT}\"/hooks/worktree-create-gate.sh",
"timeout": 60,
"statusMessage": "Placing the worktree at the external root..."
}
]
}
]
No matcher, no condition — and the event supports none. https://code.claude.com/docs/en/hooks, fetched 2026-08-11: WorktreeCreate has "no matcher support" and "always fires on every occurrence". So registration genuinely is the only scoping seam, which is the structural claim that stands. The gate is git-only by delegation: hooks/worktree-create-gate.sh:144 calls scripts/worktree-create.sh, which at :258-261 does:
if ! toplevel=$(git -C "$repo_dir" rev-parse --show-toplevel 2>/dev/null); then
printf '%s: --repo-dir is not inside a git repository: %s\n' "$PROG" "$repo_dir" >&2
exit 4
CORRECTED MECHANISM — the originally-filed rationale is falsified. The finding as filed said the registration deletes the harness stand-down and creates a deadlock at a non-repo root. It does not. https://code.claude.com/docs/en/agent-view, "How file edits are isolated", fetched 2026-08-11: "When the hook fails in a directory that isn't a git repository, the session skips isolation for that directory and edits the working directory in place." The registration changes only how the stand-down is reached (attempt-then-skip instead of skip-up-front). The observed deadlock is a harness divergence, filed upstream. What survives here is a plain scope concern — a git-only hook firing machine-wide — graded MED and routed as optional hardening, not as a deadlock fix.
Fleet caveat that survives regardless: other installed plugins may also register WorktreeCreate (the machine carries ~65 plugins; not enumerated), so a registration-scope change here would not be fleet-complete even if adopted. Weigh that before spending on it.
SC-F5 — the opt-out asserts an unverified harness behavior, on a path that only just became live
plugins/source-control/hooks/worktree-create-gate.sh:62-65:
if [[ "${CLAUDE_PLUGIN_OPTION_WORKTREE_CREATE_GATE_ENABLED:-true}" != "true" ]]; then
printf 'worktree-create-gate: disabled by worktree_create_gate_enabled=false; …\n' >&2
exit 0
fi
The header at :46-47 states "Disabled means 'let Claude Code use its own default', which is exit 0 with an empty stdout". The suite locks that contract in at plugins/source-control/hooks/worktree-create-gate.test.sh:131-132:
assert_exit "disabled exits 0 so Claude Code uses its own default" 0 "$STATUS"
assert_silent "disabled prints no path — an empty stdout is what yields the default" "$OUT"
The doc leg that made this look certain is gone. The finding as filed cited a hooks-page sentence reading "Hook failure or missing path fails creation". That sentence is not in the current text. https://code.claude.com/docs/en/hooks was fetched three ways on 2026-08-11 (page, hooks.md, and a targeted request for the full decision-control table). The WorktreeCreate row now reads verbatim:
"WorktreeCreate | Exit code or hookSpecificOutput.worktreePath | Any non-zero exit code fails creation. For HTTP hooks, return 2xx with hookSpecificOutput.worktreePath to override the default path"
and the exit-code table row reads "Any non-zero exit code causes worktree creation to fail." No sentence containing "missing path", "no path", or "empty stdout" appears anywhere reachable. (https://code.claude.com/docs/en/hooks-reference returns HTTP 404.) So the behavior of a WorktreeCreate hook that exits 0 without printing a path is undocumented — not documented-as-failing. The harness outcome is unconfirmed in either direction, and this issue must not be read as asserting that the opt-out fails creation.
What is a defect on its own terms, regardless of which way the measurement goes: the test suite asserts an external harness behavior that no current doc confirms.
Why this matters more now than when it was audited. #2192 (CLOSED 2026-08-11, "Plugin options are undiscoverable, and two enablement paths do not work") and its fix #2193 (2fb6901d, source-control 0.51.7) declared worktree_create_gate_enabled in userConfig for the first time. At HEAD, plugins/source-control/.claude-plugin/plugin.json:43 declares it, and plugins/source-control/README.md:286 states that before this "the option was never declared here — so Claude Code never exported CLAUDE_PLUGIN_OPTION_WORKTREE_CREATE_GATE_ENABLED, the hook's :-true fallback always won, and the gate could not be turned off." The opt-out only became reachable at HEAD. 2fb6901d made the switch reachable; it did not verify what the switch does.
Correction to a related earlier grading, so it is not repeated. A sibling audit graded the undeclared option a "declaration/discoverability gap, not a lockout", reasoning that a settings env entry would still reach the hook's plain env read. That rationale is falsified: the shipped fix records the option was inert in both directions — the gate ran unconditionally and setting the option produced no effect and no error. Anyone citing this history must not repeat the "not a lockout" framing. (Confirmed by https://code.claude.com/docs/en/plugins-reference, fetched 2026-08-11: "All values are exported to hook processes as CLAUDE_PLUGIN_OPTION_<KEY> environment variables" — scoped to declared userConfig values, so an undeclared key yields no variable.)
The one command that settles SC-F5 — a pending operator action, not a blocker on this issue
In a throwaway git repo, set worktree_create_gate_enabled=false, trigger a real WorktreeCreate (claude --worktree probe), and record whether the worktree is created at Claude Code's default location or creation fails.
- If it is created — the header comment and the two test assertions are correct, and
SC-F5 closes.
- If creation fails — correct
worktree-create-gate.sh:46-47 and worktree-create-gate.test.sh:131-132 together.
Either way, do not document the opt-out as usable until this is run. File-and-measure, not block-and-wait: the issue stands on the SSOT defect (a suite asserting an unverified external behavior) whether or not anyone runs the probe.
SC-F6 — the skip notice is invisible by construction
plugins/source-control/hooks/worktree-create-gate.sh:49-57 carries the reasoning and a # silent-skip-ok: marker; the notice itself is the printf … >&2 at :63 on the exit 0 path. https://code.claude.com/docs/en/hooks, fetched 2026-08-11, verbatim: "Stderr from a hook that exits 0 goes to the debug log only, never the transcript, and Claude never sees it." A real observability gap, correctly self-documented, whose resolution follows from SC-F5's outcome rather than standing alone.
Provenance
Severity: MED (cluster highest; SC-F6 is LOW) · Provenance: AUDITOR_VERIFIED — for SC-F5, the exit-0/empty-stdout half is reproduced at HEAD; the harness-outcome half is inference and is unconfirmed in either direction. SC-F1's originally-filed rationale is falsified; the corrected mechanism is stated above.
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.
Two questions about this hook have the same answer surface: where does it run, and how do I turn it off. Today the answer to the first is "everywhere on the machine" —
WorktreeCreatesupports no matcher, so a user-scope install runs a git-only hook in every directory. The answer to the second only became reachable at HEAD (#2193 declared the option for the first time), and nobody has ever measured what it does — the test suite asserts an external harness behavior that no current doc confirms in either direction.Rows
SC-F1(registration-scope half only) —plugins/source-control/hooks/hooks.json— theWorktreeCreatehook is registered unconditionally at user scope on an event that supports no matcher, so a git-only gate runs in every directory on the machine. The deadlock half of this row is UPSTREAM and is filed separately (see cross-links) — do not chase it here.SC-F5—plugins/source-control/hooks/worktree-create-gate.sh:46-47andplugins/source-control/hooks/worktree-create-gate.test.sh:131-132— the disabled path exits 0 with an empty stdout, and the suite asserts that Claude Code then uses its own default. That harness behavior is undocumented, and a green suite asserting a false external behavior is the worse half of the defect.SC-F6—plugins/source-control/hooks/worktree-create-gate.sh:49-57,:63— the disabled-path skip notice reaches nobody outside debug logging, by the event's construction. Self-documented with a# silent-skip-ok:marker; its correct resolution isSC-F5's, not its own.Evidence
SC-F1 — registration is the only scoping seam, and it is unconditional
plugins/source-control/hooks/hooks.json:No matcher, no condition — and the event supports none. https://code.claude.com/docs/en/hooks, fetched 2026-08-11:
WorktreeCreatehas "no matcher support" and "always fires on every occurrence". So registration genuinely is the only scoping seam, which is the structural claim that stands. The gate is git-only by delegation:hooks/worktree-create-gate.sh:144callsscripts/worktree-create.sh, which at:258-261does:CORRECTED MECHANISM — the originally-filed rationale is falsified. The finding as filed said the registration deletes the harness stand-down and creates a deadlock at a non-repo root. It does not. https://code.claude.com/docs/en/agent-view, "How file edits are isolated", fetched 2026-08-11: "When the hook fails in a directory that isn't a git repository, the session skips isolation for that directory and edits the working directory in place." The registration changes only how the stand-down is reached (attempt-then-skip instead of skip-up-front). The observed deadlock is a harness divergence, filed upstream. What survives here is a plain scope concern — a git-only hook firing machine-wide — graded MED and routed as optional hardening, not as a deadlock fix.
Fleet caveat that survives regardless: other installed plugins may also register
WorktreeCreate(the machine carries ~65 plugins; not enumerated), so a registration-scope change here would not be fleet-complete even if adopted. Weigh that before spending on it.SC-F5 — the opt-out asserts an unverified harness behavior, on a path that only just became live
plugins/source-control/hooks/worktree-create-gate.sh:62-65:The header at
:46-47states "Disabled means 'let Claude Code use its own default', which is exit 0 with an empty stdout". The suite locks that contract in atplugins/source-control/hooks/worktree-create-gate.test.sh:131-132:The doc leg that made this look certain is gone. The finding as filed cited a hooks-page sentence reading "Hook failure or missing path fails creation". That sentence is not in the current text. https://code.claude.com/docs/en/hooks was fetched three ways on 2026-08-11 (page,
hooks.md, and a targeted request for the full decision-control table). TheWorktreeCreaterow now reads verbatim:and the exit-code table row reads "Any non-zero exit code causes worktree creation to fail." No sentence containing "missing path", "no path", or "empty stdout" appears anywhere reachable. (
https://code.claude.com/docs/en/hooks-referencereturns HTTP 404.) So the behavior of aWorktreeCreatehook that exits 0 without printing a path is undocumented — not documented-as-failing. The harness outcome is unconfirmed in either direction, and this issue must not be read as asserting that the opt-out fails creation.What is a defect on its own terms, regardless of which way the measurement goes: the test suite asserts an external harness behavior that no current doc confirms.
Why this matters more now than when it was audited. #2192 (CLOSED 2026-08-11, "Plugin options are undiscoverable, and two enablement paths do not work") and its fix #2193 (
2fb6901d,source-control0.51.7) declaredworktree_create_gate_enabledinuserConfigfor the first time. At HEAD,plugins/source-control/.claude-plugin/plugin.json:43declares it, andplugins/source-control/README.md:286states that before this "the option was never declared here — so Claude Code never exportedCLAUDE_PLUGIN_OPTION_WORKTREE_CREATE_GATE_ENABLED, the hook's:-truefallback always won, and the gate could not be turned off." The opt-out only became reachable at HEAD.2fb6901dmade the switch reachable; it did not verify what the switch does.Correction to a related earlier grading, so it is not repeated. A sibling audit graded the undeclared option a "declaration/discoverability gap, not a lockout", reasoning that a settings
enventry would still reach the hook's plain env read. That rationale is falsified: the shipped fix records the option was inert in both directions — the gate ran unconditionally and setting the option produced no effect and no error. Anyone citing this history must not repeat the "not a lockout" framing. (Confirmed by https://code.claude.com/docs/en/plugins-reference, fetched 2026-08-11: "All values are exported to hook processes asCLAUDE_PLUGIN_OPTION_<KEY>environment variables" — scoped to declareduserConfigvalues, so an undeclared key yields no variable.)The one command that settles SC-F5 — a pending operator action, not a blocker on this issue
In a throwaway git repo, set
worktree_create_gate_enabled=false, trigger a realWorktreeCreate(claude --worktree probe), and record whether the worktree is created at Claude Code's default location or creation fails.SC-F5closes.worktree-create-gate.sh:46-47andworktree-create-gate.test.sh:131-132together.Either way, do not document the opt-out as usable until this is run. File-and-measure, not block-and-wait: the issue stands on the SSOT defect (a suite asserting an unverified external behavior) whether or not anyone runs the probe.
SC-F6 — the skip notice is invisible by construction
plugins/source-control/hooks/worktree-create-gate.sh:49-57carries the reasoning and a# silent-skip-ok:marker; the notice itself is theprintf … >&2at:63on theexit 0path. https://code.claude.com/docs/en/hooks, fetched 2026-08-11, verbatim: "Stderr from a hook that exits 0 goes to the debug log only, never the transcript, and Claude never sees it." A real observability gap, correctly self-documented, whose resolution follows fromSC-F5's outcome rather than standing alone.Provenance
Severity: MED (cluster highest;
SC-F6is LOW) · Provenance: AUDITOR_VERIFIED — forSC-F5, the exit-0/empty-stdout half is reproduced at HEAD; the harness-outcome half is inference and is unconfirmed in either direction.SC-F1's originally-filed rationale is falsified; the corrected mechanism is stated above.Origin: 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.