[Hooks Cannot See] (1) Teach two hooks the checks they could not see - #518
Conversation
narrow-the-scope fired four times in one session and was wrong all four. Its VERIFY_RE knew pytest, npm test, tsc and friends, but nothing about `bash scripts/test-foo.sh` or `docker build` -- so a shell-and-container project looks exactly like an edit streak with no verification in it, and the reminder becomes noise. A detector that cries wolf trains the reader to skip it, so this adds those patterns plus one more reset rule: a Bash command naming the basename of a file in the current streak clears that file, because running the script you just edited is how this kind of work gets checked and no regex can enumerate every project's entry point. tests/fixtures/shell_verified_streak_2026-09-11.json is the verbatim sequence from that session, silent with the verification row and firing without it. hook-freshness watched the checkout behind ~/.claude/hooks for staleness but never checked that the registered hook scripts still resolve. In that same session ~/.claude/hooks/split-scope pointed into a deleted worktree: the hook was registered, could not run, and said nothing for the whole session, which is "a check that could not run is not a pass" breaking on the gates themselves. The sweep reads settings.json, expands the paths, and names every one that does not resolve. It returns three outcomes, not two -- an unreadable or unparseable settings.json reports unchecked rather than clean, which the first draft of this change got wrong until explicit-failures caught it. install.sh warns when it is linking out of a git worktree, so the next dead link is announced as it is created rather than found a session later. It deliberately does not redirect to the main checkout: tests/test_install.py:290 specifies that install.sh links against its own checkout, because installing from a worktree is how a branch gets tested. An earlier draft did redirect, and that test caught it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011ZqCxSQVpmM9Rx37C7irgf Change-Id: I09fda6fc2dd315cd16eca766d881cb6f2b7306e6
|
This pull request is part of a Mergify stack:
|
Bugbot couldn't run - usage limit reachedBugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit. A user or team admin can review and increase usage limits in the Cursor dashboard. (requestId: serverGenReqId_b1a3ce57-72e1-4f61-a498-820a1697df4b) |
|
Queued — the merge queue status continues in this comment ↓. |
The summary checker now fails a Summary over 150 words and tells the writer how many words to cut. The draft-pr skill says to write the Summary with the diu skill. A new CI workflow runs the same checker on each PR's live body, including edits made on GitHub, because PR #518 was published with a summary the local checker already rejected. The merge queue now requires that check to pass, next to lint and test. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014uqkMWYjsKGenUSA1CqBVv Change-Id: Ie7e51550f991022ed4a5f5a40e07613f5ee6b75e
The summary checker now fails a Summary over 150 words and tells the writer how many words to cut. The draft-pr skill says to write the Summary with the diu skill. A new CI workflow runs the same checker on each PR's live body, including edits made on GitHub, because PR #518 was published with a summary the local checker already rejected. The merge queue now requires that check to pass, next to lint and test. Claude-Session: https://claude.ai/code/session_014uqkMWYjsKGenUSA1CqBVv Change-Id: Ie7e51550f991022ed4a5f5a40e07613f5ee6b75e Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Mergify repair stopped: missing required check validate |
|
Mergify repair stopped: required check failed: test. The retry cap was reached for current head 17a2b07. |
Bugbot couldn't run - usage limit reachedBugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit. A user or team admin can review and increase usage limits in the Cursor dashboard. (requestId: serverGenReqId_0486a1d5-1622-4928-91bc-6d05df7a2c8f) |
|
@Mergifyio queue |
Merge Queue Status
This pull request spent 5 minutes 30 seconds in the queue, including 4 minutes 45 seconds running CI. Required conditions to merge
|
Summary
Small helper scripts watch Claude while it works and nudge it when something looks off. Two of them were blind to things they should notice.
The first nudges Claude after many edits with no testing. It missed shell test scripts and container builds, so it nagged wrongly four times.
The second checks that the helpers are current. It never checked that each one still exists. One pointed at a deleted folder and did nothing all session.
Now the first knows those test commands. The second reports missing helpers, and says "unchecked" when it cannot look. The installer warns when linking from a temporary copy.
Review Claim
Approve two detector fixes and one installer warning, each with a test that fails before the change and passes after.
Review Lane
behavior
Review Unit
engine-runtime
Safety Invariant
Both hooks stay inject-only and fail-open; neither gains the ability to block. The
hook-freshnesssweep is read-only oversettings.jsonand reports three outcomes, not two: an unreadable or unparseable file says "unchecked", never "clean".install.shchanges no link target at all — it only prints a warning, which is whytests/test_install.pystill passes unmodified.Slice Rationale
The two detector fixes and the installer warning ship together because they are the same defect class: a gate that reported clean when it had not actually looked. Splitting them would separate the finding from its evidence.
install.shis in scope because it is the cause of the dead link thathook-freshnessnow detects; the detector alone would leave the source unaddressed.Non-goals
Does not make either hook able to block. Both stay advisory.
Does not change where
install.shputs its symlinks. An earlier draft redirected a worktree install to the main checkout, andtests/test_install.py:290correctly rejected it — installing from a worktree is how a branch gets tested.Does not change
corpus/. The prose rule from the same reflect pass is slice (2), split out becausedocs/ecosystem.mdforbids engine runtime and corpus lessons in one PR.Does not add the remaining findings from that pass: the
gate-blame-needs-evidencedetector defects, the pipeline-exit-status detector, and the$PIPESTATUS-is-a-no-op-in-zsh correction. Those are a separate branch.Test Plan
Test Plan
Fail-before, with the detector reverted to
origin/mainand the new tests in place:Pass-after, on the rebased branch, exit code read off each command directly rather than off a pipeline:
Whole repo suite:
python3 -m unittest discover -s engine/hooks/narrow-the-scope/testspython3 -m unittest discover -s engine/hooks/hook-freshness/testspython3 -m unittest tests.test_install_worktree_repo_dirpython3 scripts/check_hook_test_coverage.py engine/hooks/narrow-the-scopebash scripts/run_all_tests.sh— 49 directories, 0 failuresRevert Plan
Revert Plan
git revert <sha>install.shstops printing the worktree warning. No installed link changes either way.Note
Low Risk
Changes are advisory-only, fail-open hook injections and a non-blocking install warning; no link targets or blocking behavior change.
Overview
Fixes two advisory hooks that could look healthy while they had not actually checked, plus a preventive worktree install warning.
hook-freshness now reads registered hook commands from
~/.claude/settings.json, verifies script paths exist, and injects context when paths are missing or the file cannot be read (unchecked, not silent). That advisory is combined with the existing stale-checkout message indecide/decide_json.narrow-the-scope treats more Bash runs as verification (
VERIFY_RE: shell*test*.sh,docker build/ compose,node --check) and clears a file’s edit streak when a Bash command mentions that file’s basename (e.g. running the script you just edited).install.sh prints a WARNING when run from a git worktree (symlinks target the worktree and break after removal); link targets are unchanged.
New tests cover the unresolvable-hook sweep, shell/docker fixture sequences, and worktree warning behavior.
Reviewed by Cursor Bugbot for commit 5a64e2e. Bugbot is set up for automated code reviews on this repo. Configure here.