Hook metrics (4) hooks report the errors they catch - #514
Conversation
…ooks/_runner/run.py runs one hook script, passes its stdout, stderr and exit code through unchanged, and appends one metrics row with a classified outcome. Review lane: behavior Safety invariant: A hook run through the runner produces byte-identical stdout and the same exit code as running it directly; a failed metrics write changes neither and adds one stderr line. Effectiveness measurement: Fixture hooks run directly and through the runner give identical stdout bytes and exit codes, and each row carries the expected outcome. Slice rationale: The runner and its outcome rules are one claim, reviewable before any install wiring. Architectural effect: Adds a shared, harness-agnostic hook runner under engine/hooks/_runner/ next to engine/hooks/_markers/. Dormant until installed. Goal: Create the runner, the pure outcome classifier, and tests. Motivation: No record exists today of which hooks fire, stay silent, or crash. Alternative considerations: Editing all 82 entrypoints to import a logging decorator was rejected: it cannot record import errors, syntax errors, or timeouts, and touches every hook. An in-process runpy runner was rejected because a harness-killed or hanging hook would take the recorder down with it. Implementation details: A subprocess wrapper plus a pure classifier in outcome.py. Non-goals: No install.sh, settings, or hook fragment change; no report CLI; no change to any existing hook. Layer: domain Feature state: dormant Files: - engine/hooks/_runner/run.py - engine/hooks/_runner/outcome.py - engine/hooks/_runner/tests/test_outcome.py - engine/hooks/_runner/tests/test_run.py - engine/hooks/_runner/tests/fixtures/ Change types: - engine/hooks/_runner/run.py: create - engine/hooks/_runner/outcome.py: create - engine/hooks/_runner/tests/test_outcome.py: create - engine/hooks/_runner/tests/test_run.py: create - engine/hooks/_runner/tests/fixtures/: create Acceptance criteria: - `python3 -m unittest discover -s engine/hooks/_runner/tests -v` exits 0. - `bash scripts/run_all_tests.sh` exits 0. - `python3 scripts/check_hook_test_coverage.py` exits 0. Exit code: 0 Invoker-Finalize-Id: e5ba51e8-de9e-430f-8672-e0eef3e3bafc
…ine/hooks/_runner/README.md states what the runner records, where the rows go, and the outcome precedence. Review lane: docs Safety invariant: Only engine/hooks/_runner/README.md changes; no code, test, or config file is edited. Effectiveness measurement: Every row field and outcome named in the README appears in run.py and outcome.py, checked by reading both. Slice rationale: Prose in its own commit so the code commit stays one claim. Architectural effect: None; prose only. Goal: Create engine/hooks/_runner/README.md. Motivation: Readers of the hook directory need the row format without reading code. Alternative considerations: Code comments were rejected; the repo forbids new comments. Implementation details: One new Markdown file. Non-goals: No code, test, or config edits. Layer: docs Feature state: dormant Files: - engine/hooks/_runner/README.md Change types: - engine/hooks/_runner/README.md: create Acceptance criteria: - `test -f engine/hooks/_runner/README.md` exits 0. Exit code: 0 Invoker-Finalize-Id: 917f28d4-7b67-447f-bfd1-7a26a20061da
…unner tests, the repo test suite, and the hook coverage gate pass. Review lane: proof Safety invariant: Verification is read-only and does not alter any repository file. Effectiveness measurement: The three commands are the direct measurement. Slice rationale: One focused proof before review. Architectural effect: None; verification only. Goal: Prove pass-through and outcome classification. Motivation: Running the tests is the proof. Alternative considerations: A live-harness run is deferred to step 2, where the runner is installed. Implementation details: Run the three commands. Non-goals: No mutations. Layer: app_regression Feature state: active Acceptance criteria: - Exits 0 only when all pass. Exit code: 0 Invoker-Finalize-Id: e73029a9-e2ea-4d00-8c66-f727763900a1
….t0.a-ae5fdfe43-637dd362
…No ephemeral inter-task handoff files remain in the worktree before the merge gate. Review lane: cleanup Safety invariant: The scrub script only checks for known handoff artifact names and never touches source, tests, or other repository files. Effectiveness measurement: The script exits non-zero if any handoff artifact remains. Slice rationale: Required terminal scrub for every implementation workflow. Architectural effect: None; hygiene only. Goal: Leave the branch free of handoff artifacts. Motivation: Handoff files must not reach the PR. Alternative considerations: Manual cleanup was rejected as non-deterministic. Implementation details: Run scripts/scrub-handoff-artifacts.sh. Layer exception: allowed -- the terminal scrub must run after every task in the workflow, including the docs task. Non-goals: No product edits. Layer: app_regression Feature state: active Acceptance criteria: - `bash scripts/scrub-handoff-artifacts.sh` exits 0. Exit code: 0 Invoker-Finalize-Id: cb09f6f2-66d1-4564-adb3-38e63423dbfa
…af591618b-0e93f775 — Review claim: No ephemeral inter-task handoff files remain in the worktree before the merge gate. Review lane: cleanup Safety invariant: The scrub script only checks for known handoff artifact names and never touches source, tests, or other repository files. Effectiveness measurement: The script exits non-zero if any handoff artifact remains. Slice rationale: Required terminal scrub for every implementation workflow. Architectural effect: None; hygiene only. Goal: Leave the branch free of handoff artifacts. Motivation: Handoff files must not reach the PR. Alternative considerations: Manual cleanup was rejected as non-deterministic. Implementation details: Run scripts/scrub-handoff-artifacts.sh. Layer exception: allowed -- the terminal scrub must run after every task in the workflow, including the docs task. Non-goals: No product edits. Layer: app_regression Feature state: active Acceptance criteria: - `bash scripts/scrub-handoff-artifacts.sh` exits 0.
…n recording fails datetime.UTC exists only on 3.11+, so on CI's Python 3.9 the runner raised after the hook ran and dropped the hook's stdout and exit code. Use datetime.timezone.utc, and catch any failure while classifying or writing the metrics row so the hook's output is still forwarded, with one stderr line naming the error. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G95BG4NxDsW4NA6fcipHrv Change-Id: Ie823ca268e85e83dd57d3b6f1b026946daeb4c99
…nstall.sh wraps every installed catstack hook entry in Claude, Cursor and Codex with the runner, and a second install changes nothing. Review lane: behavior Safety invariant: Every hook registered for Claude, Cursor and Codex goes through the runner; a rerun creates no duplicate entries; non-catstack entries are kept byte-for-byte. Effectiveness measurement: In a temp HOME the three files hold zero unwrapped catstack hook entries after install, and a second install leaves them byte-identical. Slice rationale: All three harnesses in one PR so hooks behave the same everywhere. Architectural effect: Every hook run in every harness now produces a metrics row. Goal: Add wrap_installed.py, link _runner into all three hook roots, and call it last in install.sh. Motivation: The runner is inert until the harness calls it. Alternative considerations: Rewriting the 69 per-hook fragments and the strings embedded in Cursor installers was rejected: it touches every hook, and a new hook added without the wrapper would bypass metrics. A post-install pass covers future hooks automatically. Implementation details: A post-install pass over the three harness config files rewrites matching hook entries to call the runner. Non-goals: No change to run.py or outcome.py, to any hook script, or to the Codex `notify` line in config.toml. Layer: app_bridge Feature state: active Files: - engine/hooks/_runner/wrap_installed.py - engine/hooks/_runner/tests/test_wrap_installed.py - install.sh Change types: - engine/hooks/_runner/wrap_installed.py: create - engine/hooks/_runner/tests/test_wrap_installed.py: create - install.sh: modify Acceptance criteria: - `python3 -m unittest discover -s engine/hooks/_runner/tests -v` exits 0. - `python3 -m unittest tests.test_install -v` exits 0. - `shellcheck install.sh` exits 0. Exit code: 0 Invoker-Finalize-Id: 026f7cd3-16c7-45af-9eb2-8c7ba1483c72
…m: scripts/check_install_effective.py reports every installed catstack hook entry that bypasses the runner. Review lane: policy Safety invariant: The check only reads the three harness config files and reports; it writes nothing. Effectiveness measurement: A test with one wrapped and one unwrapped fixture entry gets exactly one `hook bypasses the metrics runner` line. Slice rationale: The install check is tooling policy, kept apart from the install behavior it checks. Architectural effect: A missed or future hook that bypasses metrics shows up in the install check. Goal: Extend check_install_effective.py with a bypass check. Motivation: Wrapping is only trustworthy if something reports a miss. Alternative considerations: Duplicating the matcher in the check was rejected; it imports match_direct from wrap_installed.py so the two cannot disagree. Implementation details: Import match_direct and print one problem line per unwrapped entry. Non-goals: No install.sh or hook edits. Layer: app_bridge Feature state: active Files: - scripts/check_install_effective.py - tests/test_check_install_effective.py Change types: - scripts/check_install_effective.py: modify - tests/test_check_install_effective.py: create Acceptance criteria: - `python3 -m unittest tests.test_check_install_effective -v` exits 0. Exit code: 0 Invoker-Finalize-Id: 1c706729-4212-4fdc-9c6a-dd8c11051a81
…gine/hooks/_runner/README.md states how install wraps hook entries and how the install check reports a bypass. Review lane: docs Safety invariant: Only engine/hooks/_runner/README.md changes; no code, test, or config file is edited. Effectiveness measurement: The README's entry format and printed messages match wrap_installed.py, checked by reading both. Slice rationale: Prose in its own commit so the code commits stay one claim each. Architectural effect: None; prose only. Goal: Add an Install section to engine/hooks/_runner/README.md. Motivation: Readers need the install shape without reading code. Alternative considerations: Code comments were rejected; the repo forbids new comments. Implementation details: One Markdown section. Non-goals: No code, test, or config edits. Layer: docs Feature state: active Files: - engine/hooks/_runner/README.md Change types: - engine/hooks/_runner/README.md: docs-only Acceptance criteria: - `grep -n "wrap_installed.py" engine/hooks/_runner/README.md` prints at least one line. Exit code: 0 Invoker-Finalize-Id: a272c6e0-1997-4c03-8e14-324d8cf5bdce
…e repo suite, which includes the runner, install, and install-check tests, passes, and install.sh is shellcheck-clean. Review lane: proof Safety invariant: Verification is read-only for the repository; tests write only into temp directories. Effectiveness measurement: The two commands are the direct measurement. Slice rationale: One focused proof before review. Architectural effect: None; verification only. Goal: Prove wrapping is complete and a rerun changes nothing. Motivation: Running the tests is the proof. Alternative considerations: The live claude -p row check needs model access and is run by the parent session after merge. Implementation details: Run the suite and shellcheck. Non-goals: No mutations. Layer: app_regression Feature state: active Acceptance criteria: - Exits 0 only when all pass. Exit code: 127 Invoker-Finalize-Id: 354b2bdb-010c-4742-91ee-4ba090cd0747
…e repo suite, which includes the runner, install, and install-check tests, passes, and install.sh is shellcheck-clean. Review lane: proof Safety invariant: Verification is read-only for the repository; tests write only into temp directories. Effectiveness measurement: The two commands are the direct measurement. Slice rationale: One focused proof before review. Architectural effect: None; verification only. Goal: Prove wrapping is complete and a rerun changes nothing. Motivation: Running the tests is the proof. Alternative considerations: The live claude -p row check needs model access and is run by the parent session after merge. Implementation details: Run the suite and shellcheck. Non-goals: No mutations. Layer: app_regression Feature state: active Acceptance criteria: - Exits 0 only when all pass. Exit code: 0 Invoker-Finalize-Id: publish-approved-fix
…0.t0.a-ab927feb4-e722238b
…No ephemeral inter-task handoff files remain in the worktree before the merge gate. Review lane: cleanup Safety invariant: The scrub script only checks for known handoff artifact names and never touches source, tests, or other repository files. Effectiveness measurement: The script exits non-zero if any handoff artifact remains. Slice rationale: Required terminal scrub for every implementation workflow. Architectural effect: None; hygiene only. Goal: Leave the branch free of handoff artifacts. Motivation: Handoff files must not reach the PR. Alternative considerations: Manual cleanup was rejected as non-deterministic. Implementation details: Run scripts/scrub-handoff-artifacts.sh. Layer exception: allowed -- the terminal scrub must run after every task in the workflow, including the docs task. Non-goals: No product edits. Layer: app_regression Feature state: active Acceptance criteria: - `bash scripts/scrub-handoff-artifacts.sh` exits 0. Exit code: 0 Invoker-Finalize-Id: abcc8dd1-19ae-4cdc-b9a6-8bec5bce3173
…af2175b3f-597cac02 — Review claim: No ephemeral inter-task handoff files remain in the worktree before the merge gate. Review lane: cleanup Safety invariant: The scrub script only checks for known handoff artifact names and never touches source, tests, or other repository files. Effectiveness measurement: The script exits non-zero if any handoff artifact remains. Slice rationale: Required terminal scrub for every implementation workflow. Architectural effect: None; hygiene only. Goal: Leave the branch free of handoff artifacts. Motivation: Handoff files must not reach the PR. Alternative considerations: Manual cleanup was rejected as non-deterministic. Implementation details: Run scripts/scrub-handoff-artifacts.sh. Layer exception: allowed -- the terminal scrub must run after every task in the workflow, including the docs task. Non-goals: No product edits. Layer: app_regression Feature state: active Acceptance criteria: - `bash scripts/scrub-handoff-artifacts.sh` exits 0.
…unner Once wrap_installed.py rewrites an entry to `_runner/run.py --timeout T <hook>/<script>.py`, the only $HOME/.claude/hooks/ path in the command is the runner itself, which exists, so a deleted hook's entry was never pruned. prune_dead_hook_entries.py now also checks the hook script named after the runner. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G95BG4NxDsW4NA6fcipHrv Change-Id: I2d2f61c86f55a52697a191aeffc2049d81247650
…port.py prints one row per registered hook, with `no record` for a hook that never ran, and hook-health tells the agent in all three harnesses about failed hook runs since the last prompt. Review lane: behavior Safety invariant: A registered hook with no rows is "no record", never healthy; the notice never blocks; an unreadable log is unchecked, not clean. Effectiveness measurement: Seeded-log tests assert the table, "no record", the notice text, and the unreadable-log behavior. Slice rationale: Reading the metrics is one claim, apart from producing them in steps 1 and 2. Architectural effect: Hook failures become visible to the agent on the next prompt and to the user on demand. Goal: Create report.py and the hook-health hook with its three-harness install. Motivation: Rows nobody reads detect nothing. Alternative considerations: A Stop-hook notice was rejected because it would interrupt the reply that is already finishing; a SessionStart-only notice was rejected because a crash mid-session would wait for the next session. Implementation details: detect.py is pure (rows in, notice text out); entrypoints read the log from a per-session offset and print the notice in each harness's non-blocking output shape. Non-goals: No change to run.py, outcome.py, or wrap_installed.py; no dashboard or remote upload. Layer: contact_surface Feature state: active Files: - engine/hooks/_runner/report.py - engine/hooks/_runner/tests/test_report.py - engine/hooks/hook-health/detect.py - engine/hooks/hook-health/claude_prompt_submit.py - engine/hooks/hook-health/cursor_before_submit.py - engine/hooks/hook-health/codex_prompt_submit.py - engine/hooks/hook-health/claude.hook.json - engine/hooks/hook-health/codex.hook.json - engine/hooks/hook-health/install_claude_hook.py - engine/hooks/hook-health/install_cursor_hook.py - engine/hooks/hook-health/install_codex_hook.py - engine/hooks/hook-health/tests/test_detect.py - engine/hooks/hook-health/tests/test_hooks.py - install.sh Change types: - engine/hooks/_runner/report.py: create - engine/hooks/_runner/tests/test_report.py: create - engine/hooks/hook-health/detect.py: create - engine/hooks/hook-health/claude_prompt_submit.py: create - engine/hooks/hook-health/cursor_before_submit.py: create - engine/hooks/hook-health/codex_prompt_submit.py: create - engine/hooks/hook-health/claude.hook.json: create - engine/hooks/hook-health/codex.hook.json: create - engine/hooks/hook-health/install_claude_hook.py: create - engine/hooks/hook-health/install_cursor_hook.py: create - engine/hooks/hook-health/install_codex_hook.py: create - engine/hooks/hook-health/tests/test_detect.py: create - engine/hooks/hook-health/tests/test_hooks.py: create - install.sh: modify Acceptance criteria: - `python3 -m unittest discover -s engine/hooks/_runner/tests -v` exits 0. - `python3 -m unittest discover -s engine/hooks/hook-health/tests -v` exits 0. - `python3 scripts/check_hook_test_coverage.py` exits 0. - `bash scripts/run_all_tests.sh` exits 0. Exit code: 0 Invoker-Finalize-Id: e5a2c7d4-4239-4d64-aa80-1efc40d7fbe2
…m: engine/hooks/hook-health/README.md and a Report section in engine/hooks/_runner/README.md state what the table and the notice show. Review lane: docs Safety invariant: Only the two Markdown files change; no code, test, or config file is edited. Effectiveness measurement: Every column and message named in the prose appears in report.py or detect.py, checked by reading both. Slice rationale: Prose in its own commit so the code commit stays one claim. Architectural effect: None; prose only. Goal: Create engine/hooks/hook-health/README.md and add a Report section to engine/hooks/_runner/README.md. Motivation: Readers need the table and notice format without reading code. Alternative considerations: Code comments were rejected; the repo forbids new comments. Implementation details: Two Markdown edits. Non-goals: No code, test, or config edits. Layer: docs Feature state: active Files: - engine/hooks/hook-health/README.md - engine/hooks/_runner/README.md Change types: - engine/hooks/hook-health/README.md: create - engine/hooks/_runner/README.md: docs-only Acceptance criteria: - `test -f engine/hooks/hook-health/README.md` exits 0. Exit code: 0 Invoker-Finalize-Id: 5512a433-370f-4af9-98ce-321f2188fc66
… The repo suite, which includes the runner and hook-health tests, and the hook coverage gate pass. Review lane: proof Safety invariant: Verification is read-only and does not alter any repository file. Effectiveness measurement: The two commands are the direct measurement. Slice rationale: One focused proof before review. Architectural effect: None; verification only. Goal: Prove the report and notice behavior. Motivation: Running the tests is the proof. Alternative considerations: The full suite is included because install.sh changes. Implementation details: Run the suite and the coverage gate. Non-goals: No mutations. Layer: app_regression Feature state: active Acceptance criteria: - Exits 0 only when all pass. Exit code: 0 Invoker-Finalize-Id: 260483f4-93d9-470a-8d2a-9e4971a54b18
…ice/g0.t1.a-a6d820dc7-e8e99f8b
…No ephemeral inter-task handoff files remain in the worktree before the merge gate. Review lane: cleanup Safety invariant: The scrub script only checks for known handoff artifact names and never touches source, tests, or other repository files. Effectiveness measurement: The script exits non-zero if any handoff artifact remains. Slice rationale: Required terminal scrub for every implementation workflow. Architectural effect: None; hygiene only. Goal: Leave the branch free of handoff artifacts. Motivation: Handoff files must not reach the PR. Alternative considerations: Manual cleanup was rejected as non-deterministic. Implementation details: Run scripts/scrub-handoff-artifacts.sh. Layer exception: allowed -- the terminal scrub must run after every task in the workflow, including the docs task. Non-goals: No product edits. Layer: app_regression Feature state: active Acceptance criteria: - `bash scripts/scrub-handoff-artifacts.sh` exits 0. Exit code: 0 Invoker-Finalize-Id: 65a5143c-73b6-48fa-b975-b68b58cdfc3b
…aa09fa676-478048c6 — Review claim: No ephemeral inter-task handoff files remain in the worktree before the merge gate. Review lane: cleanup Safety invariant: The scrub script only checks for known handoff artifact names and never touches source, tests, or other repository files. Effectiveness measurement: The script exits non-zero if any handoff artifact remains. Slice rationale: Required terminal scrub for every implementation workflow. Architectural effect: None; hygiene only. Goal: Leave the branch free of handoff artifacts. Motivation: Handoff files must not reach the PR. Alternative considerations: Manual cleanup was rejected as non-deterministic. Implementation details: Run scripts/scrub-handoff-artifacts.sh. Layer exception: allowed -- the terminal scrub must run after every task in the workflow, including the docs task. Non-goals: No product edits. Layer: app_regression Feature state: active Acceptance criteria: - `bash scripts/scrub-handoff-artifacts.sh` exits 0.
…: Every broad exception handler in engine/hooks prints one catstack-hook-error line to stderr before continuing, and a CI check fails on any new silent one. Review lane: policy Safety invariant: No hook changes what it decides; the only new behavior is one stderr line per caught exception; the check fails on new silent broad handlers. Effectiveness measurement: Check tests (silent -> 1, reporting -> 0, unparseable -> 2) pass, and the full suite with every hook's decision tests still passes. Slice rationale: One mechanical edit across hooks plus the check that keeps it true. Architectural effect: Caught hook errors become visible to the runner as caught_error. Goal: Add the check with --fix, run --fix, wire the check into CI. Motivation: Silent handlers make a broken hook indistinguishable from a quiet one. Alternative considerations: A shared import helper was rejected because a missing or broken helper import would itself crash hooks; an inline print to stderr adds no new failure mode. Narrow handlers such as a missing-file OSError are out of scope because they describe expected input, not a bug. Implementation details: An AST check with a --fix mode that inserts the stderr print into each silent broad handler. Non-goals: No change to any handler's return value or control flow, to narrow exception handlers, to tests, or to the runner. Layer: domain Feature state: active Files: - scripts/check_no_silent_hook_except.py - tests/test_check_no_silent_hook_except.py - .github/workflows/ci.yml - engine/hooks/ Change types: - scripts/check_no_silent_hook_except.py: create - tests/test_check_no_silent_hook_except.py: create - .github/workflows/ci.yml: modify - engine/hooks/: modify Acceptance criteria: - `python3 scripts/check_no_silent_hook_except.py` exits 0. - `python3 -m unittest tests.test_check_no_silent_hook_except -v` exits 0. - `bash scripts/run_all_tests.sh` exits 0. - `python3 scripts/check_no_new_comments.py` exits 0. Exit code: 0 Invoker-Finalize-Id: 7e8fb5ef-d84b-490f-ab7b-6f0e3a673a26
…: The silent-handler check, the repo suite (which includes its tests), and the no-comments gate pass. Review lane: proof Safety invariant: Verification is read-only and does not alter any repository file. Effectiveness measurement: The three commands are the direct measurement. Slice rationale: One focused proof before review. Architectural effect: None; verification only. Goal: Prove the transformation is complete and decision-preserving. Motivation: Running the checks is the proof. Alternative considerations: The full suite is required because 59 hook files change. Implementation details: Run the check, the suite, and the no-comments gate. Non-goals: No mutations. Layer: app_regression Feature state: active Acceptance criteria: - Exits 0 only when all pass. Exit code: 0 Invoker-Finalize-Id: 40479df3-783c-4e9b-80da-9badb939b04d
…No ephemeral inter-task handoff files remain in the worktree before the merge gate. Review lane: cleanup Safety invariant: The scrub script only checks for known handoff artifact names and never touches source, tests, or other repository files. Effectiveness measurement: The script exits non-zero if any handoff artifact remains. Slice rationale: Required terminal scrub for every implementation workflow. Architectural effect: None; hygiene only. Goal: Leave the branch free of handoff artifacts. Motivation: Handoff files must not reach the PR. Alternative considerations: Manual cleanup was rejected as non-deterministic. Implementation details: Run scripts/scrub-handoff-artifacts.sh. Non-goals: No product edits. Layer: app_regression Feature state: active Acceptance criteria: - `bash scripts/scrub-handoff-artifacts.sh` exits 0. Exit code: 0 Invoker-Finalize-Id: 0a9a6fa9-2a5b-4cd1-980b-b3c3d2edcafd
…add227147-c5055358 — Review claim: No ephemeral inter-task handoff files remain in the worktree before the merge gate. Review lane: cleanup Safety invariant: The scrub script only checks for known handoff artifact names and never touches source, tests, or other repository files. Effectiveness measurement: The script exits non-zero if any handoff artifact remains. Slice rationale: Required terminal scrub for every implementation workflow. Architectural effect: None; hygiene only. Goal: Leave the branch free of handoff artifacts. Motivation: Handoff files must not reach the PR. Alternative considerations: Manual cleanup was rejected as non-deterministic. Implementation details: Run scripts/scrub-handoff-artifacts.sh. Non-goals: No product edits. Layer: app_regression Feature state: active Acceptance criteria: - `bash scripts/scrub-handoff-artifacts.sh` exits 0.
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_afec9a69-c987-4022-aca9-677032335bd8) |
…path The check resolves --root, so on macOS a temp dir under /var prints as /private/var and the unchecked-path assertion failed. Resolve the test's root the same way. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G95BG4NxDsW4NA6fcipHrv Change-Id: I5dd11172fd3df32fce0b99afa1791242c27b9eee
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_2b2a5e45-02db-4267-9bb2-aafa64ba295c) |
Change-Id: Ic7cba690f2903bad25d438633bf0fa7b6650ad47
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_79791352-f07a-4f88-addb-1bd5a1d89c3d) |
|
@Mergifyio queue |
Merge Queue Status
This pull request spent 5 minutes 9 seconds in the queue, including 4 minutes 28 seconds running CI. Required conditions to merge
|
Summary
Small checks can now show when they catch an error and keep going.
Before, a caught error looked like a quiet run. That hid broken checks from the run record.
Each broad catch now writes one short error line before continuing. A new CI check keeps future catches from going silent.
Review Claim
Approve making caught errors visible while preserving every decision.
Review Lane
policy
Review Unit
engine-runtime
Safety Invariant
No hook changes its allow, block, or injected result. The only new behavior is one error line for each caught exception, and new silent broad handlers fail the check.
Slice Rationale
This is the reporting-policy slice after the per-hook report and crash-notice foundation. It keeps the mechanical edits and their guard check together.
Non-goals
This does not change hook decisions, return values, narrow exception handling, existing behavior tests, or runner logic.
Architecture
Before
graph TD A["hook catches an error"] --> B["continues without an error line"] B --> C["runner records a quiet run"]After
graph TD A["hook catches an error"] --> B["writes one catstack-hook-error line"] B --> C["continues with the same decision"] B --> D["runner records caught_error"] E["CI policy check"] --> F["rejects new silent broad handlers"]Test Plan
Test Plan
python3 scripts/check_no_silent_hook_except.py && bash scripts/run_all_tests.sh && python3 scripts/check_no_new_comments.pybash scripts/scrub-handoff-artifacts.shRevert Plan
Revert Plan
git revert <sha>Note
Medium Risk
Touches many hook entrypoints and detect modules in the fail-open path; decisions are unchanged but stderr output and a few tests shifted, so runners/metrics must tolerate the new error lines.
Overview
Hooks that fail open on broad
except Exceptionhandlers no longer swallow failures silently. Each catch now emits a standardizedcatstack-hook-errorline to stderr (hook name + exception type/message) before returning with the same allow/block/inject behavior as before.A new
scripts/check_no_silent_hook_except.pygate scansengine/hookswith AST analysis and fails CI when a broad handler lacks stderr logging, a logger call, or a re-raise; it includes an optional--fixrewriter. CI runs this check after hook e2e coverage, and unit tests cover the scanner plus updated expectations where stderr now includes the error prefix (e.g. frustration-watchdog refusal-read failures, wrong-check-reflect judge enqueue errors).Reviewed by Cursor Bugbot for commit eda4609. Bugbot is set up for automated code reviews on this repo. Configure here.