Two ways the hook exits non-zero for a reason that is not "the record is invalid". Found by a production audit of v0.5.1.
1. Shallow clone → dangling-ref
src/commands/validate.ts:550-699 gates on notes === 'unfetched' (:588, :620) and has no shallow gate. grep -c shallow src/commands/validate.ts → 0, though src/core/git.ts:144 hasShallowHistory exists and is used by query.ts:834, doctor.ts:675, before-change.ts:81.
Same message, same upstream:
--depth 1 clone : shape ok · references failed
dangling-ref Supersedes — got "r-old001" exit 1 -> commit blocked
full clone : shape ok · references ok exit 0
The record is not invalid; the history is truncated. actions/checkout defaults to fetch-depth: 1.
The team already guards the mirror-image hazard: action/lint/lint.mjs:128-139 refuses on a shallow checkout, commented "Linting what a shallow clone happens to contain would report a green check." The local hook has the red-check version and no guard.
2. Multi-block message → dangling-ref on any block but the first
src/commands/validate.ts:509:
const identity = `${row.sha}\0${row.source}`;
No block. The index's row identity is (commit_sha, source, block, seq) (src/core/index-db.ts:195), and src/core/query.ts:469 keys on all four specifically to fix this class (bug-issue-92). Flattened, the group carries several Record-Id trailers and src/core/stale.ts:90 returns only the first, so only block 0 reaches the declared set.
Follows: r-aaa111 (block 0) -> exit 0
Follows: r-bbb222 (block 1) -> exit 1 dangling-ref -> commit blocked
context reports both records. Multi-block messages are produced by this project's own squash inheritance (src/hooks/prepare-commit-msg.ts:55-68) and by GitHub's squash button.
Why these matter more than an ordinary bug
A hook that blocks a commit it cannot evaluate is the failure mode that gets a tool uninstalled — or --no-verify'd permanently, which is worse because the guard is then off for the commits that need it.
Both untested.
Two ways the hook exits non-zero for a reason that is not "the record is invalid". Found by a production audit of v0.5.1.
1. Shallow clone →
dangling-refsrc/commands/validate.ts:550-699gates onnotes === 'unfetched'(:588,:620) and has no shallow gate.grep -c shallow src/commands/validate.ts→ 0, thoughsrc/core/git.ts:144 hasShallowHistoryexists and is used byquery.ts:834,doctor.ts:675,before-change.ts:81.Same message, same upstream:
The record is not invalid; the history is truncated.
actions/checkoutdefaults tofetch-depth: 1.The team already guards the mirror-image hazard:
action/lint/lint.mjs:128-139refuses on a shallow checkout, commented "Linting what a shallow clone happens to contain would report a green check." The local hook has the red-check version and no guard.2. Multi-block message →
dangling-refon any block but the firstsrc/commands/validate.ts:509:No
block. The index's row identity is(commit_sha, source, block, seq)(src/core/index-db.ts:195), andsrc/core/query.ts:469keys on all four specifically to fix this class (bug-issue-92). Flattened, the group carries severalRecord-Idtrailers andsrc/core/stale.ts:90returns only the first, so only block 0 reaches thedeclaredset.contextreports both records. Multi-block messages are produced by this project's own squash inheritance (src/hooks/prepare-commit-msg.ts:55-68) and by GitHub's squash button.Why these matter more than an ordinary bug
A hook that blocks a commit it cannot evaluate is the failure mode that gets a tool uninstalled — or
--no-verify'd permanently, which is worse because the guard is then off for the commits that need it.Both untested.