Repository navigation
Hook fleet (2) replay harness - #1333
Conversation
Review claim: - An opt-in payload recorder in the runner plus a replay script can feed recorded hook-event payloads through installed hook commands and produce a verdict-equivalence report (stdout/context/block/exit) per detector. Review lane: behavior Safety invariant: - Recording is off unless CATSTACK_HOOK_PAYLOAD_DIR is set; when on it never alters hook stdin/stdout/exit and caps each payload (default 256KB) and total dir (default 2000 files, oldest evicted). - Replay runs against a scratch metrics/state dir, never live settings or the live runs.jsonl. Goal: - Runner: when env var set, append the raw stdin payload to $CATSTACK_HOOK_PAYLOAD_DIR/<payload_hash>-<hook>.json before spawning the hook child, where <payload_hash> is sha256(stdin)[:12]. - scripts/test/replay_hook_payloads.py: for each recorded payload, run the hook command under a scratch env, capture stdout/stderr/exit/ findings; emit an equivalence report JSON + table comparing two command sets (fleet vs dispatcher args taken as parameters). Motivation: - The catastrophic regression risk of the dispatcher is a detector that silently stops speaking; the only credible gate is byte-identical verdicts on real payloads. Cherry-picked from origin/plan/hook-fleet-dispatcher-measure-the-herd-then-dispatch-it (e356e8c), scoped to only this claim's files. Dropped this slice's dependency on the separate metrics-event-uid claim (17ef151, not on this branch): the payload recorder computes its own local hash instead of reusing a published `event_uid` metrics-row field, and the README wording and test imports were adjusted to match. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
… payload recorder in the runner plus a replay script can feed recorded hook-event payloads through installed hook commands and produce a verdict-equivalence report per detector. Review lane: behavior Safety invariant: Hook stdout and exit codes stay the same. This slice does not change the other four claims. Files: engine/hooks/_runner/run.py, scripts/test/replay_hook_payloads.py Change types: bugfix Acceptance criteria: The branch contains only this claim, and the review step publishes one pull request. Exit code: 0 Invoker-Finalize-Id: 61e213a0-a6cd-4950-ba23-0acc17742997
…-1bc38490 — Review claim: An opt-in payload recorder in the runner plus a replay script can feed recorded hook-event payloads through installed hook commands and produce a verdict-equivalence report per detector. Review lane: behavior Safety invariant: Hook stdout and exit codes stay the same. This slice does not change the other four claims. Files: engine/hooks/_runner/run.py, scripts/test/replay_hook_payloads.py Change types: bugfix Acceptance criteria: The branch contains only this claim, and the review step publishes one pull request.
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: 5a6ef6ca-09c9-4fb5-b62c-6a9f5611c6e2) |
|
Queued — the merge queue status continues in this comment ↓. |
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: 563c84c2-5864-4bf7-bf87-610764c2565d) |
|
@Mergifyio queue |
Merge Queue Status
This pull request spent 1 hour 1 minute 3 seconds in the queue, including 1 hour 31 seconds running CI. ReasonThe merge conditions cannot be satisfied: the checks did not pass within the checks timeout of 1 hour These checks were still running:
Failing checks: HintYou may have to fix your CI before adding the pull request to the queue again. If you update this pull request, to fix the CI, it will automatically be requeued once the queue conditions match again. Waiting for
All conditions
Requeued — the merge queue status continues in this comment ↓. |
|
@Mergifyio queue |
Merge Queue Status
This pull request spent 4 hours 31 minutes 51 seconds in the queue, including 2 hours 43 minutes 37 seconds running CI. Required conditions to merge
|
Summary
This slice saves hook event inputs only when recording is enabled. It replays those inputs through two hook commands.
The report shows where the commands differ in output, context, findings, or exit status.
Review Claim
An opt-in recorder plus replay harness can compare installed hook commands against the same recorded events without changing hook output or exit status.
Review Lane
behavior
Review Unit
engine-runtime
Safety Invariant
Hook stdout and exit codes remain unchanged. Recording is opt-in, bounded, and isolated from replay scratch state.
Slice Rationale
This keeps payload capture and replay together because the recorder supplies the exact inputs the comparison harness consumes.
The slice covers one claim from the failed dispatcher workflow and stays separate from the other four fleet claims.
Non-goals
Architecture
Before
graph TD A["hook event"] --> B["installed hook command"] B --> C["stdout and exit code"]After
graph TD A["hook event"] --> B["optional bounded payload recording"] A --> C["installed hook command"] B --> D["recorded payload"] D --> E["fleet command and dispatcher command"] E --> F["verdict-equivalence report"] C --> G["unchanged stdout and exit code"]Measured
none: this slice makes no performance or capacity claim beyond bounded payload retention.
Test Plan
Test Plan
python3 engine/skills/make-pr/scripts/preflight.py --base origin/main --commit-only --dry-run—unit engine-runtime: 5 file(s);ok preflight passed.python3 scripts/ci/check_hook_test_coverage.py engine/hooks/_runner—check_hook_test_coverage: OK (1 hook(s) checked).python3 -m unittest engine/hooks/_runner/tests/test_run.py scripts/test/test_replay_hook_payloads.py—Ran 25 tests;OK.ruff check --select E9,F engine/hooks/_runner/run.py engine/hooks/_runner/tests/test_run.py scripts/test/replay_hook_payloads.py scripts/test/test_replay_hook_payloads.py— fails on unused importosinscripts/test/test_replay_hook_payloads.py:5.pnpm run check:comments— unavailable:ERR_PNPM_NO_SCRIPT Missing script: check:comments.Revert Plan
Revert Plan
git revert <merge-commit-sha>Note
Low Risk
Recording is off by default and bounded; the runner only adds a pre-subprocess write on the existing hook path, with tests asserting passthrough behavior.
Overview
Adds opt-in hook stdin capture in the metrics runner and a replay harness to compare two hook command implementations on the same events.
When
CATSTACK_HOOK_PAYLOAD_DIRis set,run.pywrites raw stdin bytes to<sha256-prefix>-<hook>.jsonimmediately before starting the hook subprocess, with size/retention caps and no change to hook stdin, stdout, or exit code. Recording failures surface as an extra stderr line only.New
scripts/test/replay_hook_payloads.pyfeeds each recorded payload through paired--fleetand--dispatchershell commands (matching labels), runs them in an isolated scratch env (no live metrics or recursive payload recording), and reports match/mismatch on stdout, context, block verdict, exit code, and findings—with optional JSON output.Reviewed by Cursor Bugbot for commit 9787af8. Bugbot is set up for automated code reviews on this repo. Configure here.