Skip to content

[Hooks Cannot See] (1) Teach two hooks the checks they could not see - #518

Open
EdbertChan wants to merge 1 commit into
mainfrom
stack/EdbertChan/reflect/session-c2a4bea7/hooks-cannot-see-1-teach-two-hooks-checks-they-see--09fda6fc
Open

[Hooks Cannot See] (1) Teach two hooks the checks they could not see#518
EdbertChan wants to merge 1 commit into
mainfrom
stack/EdbertChan/reflect/session-c2a4bea7/hooks-cannot-see-1-teach-two-hooks-checks-they-see--09fda6fc

Conversation

@EdbertChan

@EdbertChan EdbertChan commented Sep 13, 2026

Copy link
Copy Markdown
Owner

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-freshness sweep is read-only over settings.json and reports three outcomes, not two: an unreadable or unparseable file says "unchecked", never "clean". install.sh changes no link target at all — it only prints a warning, which is why tests/test_install.py still 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.sh is in scope because it is the cause of the dead link that hook-freshness now 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.sh puts its symlinks. An earlier draft redirected a worktree install to the main checkout, and tests/test_install.py:290 correctly 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 because docs/ecosystem.md forbids engine runtime and corpus lessons in one PR.

Does not add the remaining findings from that pass: the gate-blame-needs-evidence detector 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/main and the new tests in place:

AttributeError: module 'detect' has no attribute 'unresolvable_hooks'
Ran 18 tests in 0.028s
FAILED (errors=5)

AssertionError: Lists differ: [4] != []
Ran 13 tests in 0.032s
FAILED (failures=3)

Pass-after, on the rebased branch, exit code read off each command directly rather than off a pipeline:

=== narrow-the-scope ===
Ran 13 tests in 0.140s
OK
exit=0
=== hook-freshness ===
Ran 18 tests in 0.073s
OK
exit=0
=== install worktree ===
Ran 3 tests in 2.933s
OK
exit=0
=== gates ===
check_hook_test_coverage exit=0
check_ecosystem_boundaries exit=0
check_skills_three_harnesses exit=0
check_skill_file_refs exit=0

Whole repo suite:

Ran 463 tests in 1146.986s
OK
EXIT=0
  • python3 -m unittest discover -s engine/hooks/narrow-the-scope/tests
  • python3 -m unittest discover -s engine/hooks/hook-freshness/tests
  • python3 -m unittest tests.test_install_worktree_repo_dir
  • python3 scripts/check_hook_test_coverage.py engine/hooks/narrow-the-scope
  • bash scripts/run_all_tests.sh — 49 directories, 0 failures

Revert Plan

Revert Plan
  • Safe to revert? Yes
  • Revert command: git revert <sha>
  • Post-revert steps: None. Both hooks return to their previous behaviour; install.sh stops printing the worktree warning. No installed link changes either way.
  • Data migration? No

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
@EdbertChan

Copy link
Copy Markdown
Owner Author

This pull request is part of a Mergify stack:

# Pull Request Link
1 [Hooks Cannot See] (1) Teach two hooks the checks they could not see #518 👈
2 [Hooks Cannot See] (2) Require the output to entail the claim, not merely agree #519

@cursor

cursor Bot commented Sep 13, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot 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)

@mergify

mergify Bot commented Sep 13, 2026

Copy link
Copy Markdown
Contributor

Tick the box to add this pull request to the merge queue (same as @mergifyio queue).

  • Queue this pull request

EdbertChan added a commit that referenced this pull request Sep 13, 2026
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
mergify Bot pushed a commit that referenced this pull request Sep 13, 2026
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>
@EdbertChan

Copy link
Copy Markdown
Owner Author

Mergify repair stopped: missing required check validate

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant