Skip to content

[Hook architecture](15) Put the categorical-scope-guard hook onto the shared hook code - #768

Open
EdbertChan wants to merge 10 commits into
plan/hook-architecture-14-put-the-cat-mode-default-hook-onto-the-shared-hook-codefrom
plan/hook-architecture-15-put-the-categorical-scope-guard-hook-onto-the-shared-hook-code
Open

EdbertChan wants to merge 10 commits into
plan/hook-architecture-14-put-the-cat-mode-default-hook-onto-the-shared-hook-codefrom
plan/hook-architecture-15-put-the-categorical-scope-guard-hook-onto-the-shared-hook-code

Conversation

@EdbertChan

@EdbertChan EdbertChan commented Sep 17, 2026

Copy link
Copy Markdown
Owner

Summary

The guard that checks "all" requests now runs on the shared hook code. It still stops a command that changes only some items after the user asked for all.

Before this, the hook chose its own response and printed its own output. A mode change meant editing the hook itself.

Now the hook only reports what it found. The shared code picks stop, warn, or silent from the registry and writes one event row per finding.

One behavior did change. If the checker itself crashes, the hook now lets the command through and logs the error. Before, it blocked the command.

Review Claim

The guard that checks "all" requests reports findings to the shared hook code, which applies its registry mode and writes event rows. It keeps mode stop.

Review Lane

behavior

Review Unit

engine-runtime

Safety Invariant

Every case in the hook's current test folder gets the same stop, warn, or silent result as before, and the folder still exits 0. The new mode-override test is the only intended change.

Known gap: when the checker crashes, the hook used to block the command. The removed code in claude_pretooluse.py caught the error and called sys.exit(2). Now the shared runtime's crash handler in engine/hooks/_sdk/runtime.py logs catstack-hook-error categorical-scope-guard: <error> and calls sys.exit(0), so the command goes through. A deliberate crash probe (see Test Plan) exited 0. The current test folder has no case for this path. A reviewer should decide whether this needs a follow-up.

Slice Rationale

One hook per workflow, so each move to the shared code is reviewed on its own. This is part 15 of the stack in docs/hook-architecture.md.

Non-goals

  • No change to what the hook detects.
  • No other hook changes.
  • install_claude_hook.py was listed in the plan but is not changed here.
  • Does not restore blocking when the checker crashes.

Test Plan

Test Plan
  • python3 -m unittest discover -s engine/hooks/categorical-scope-guard/testsRan 45 tests in 0.290s / OK
  • New test test_mode_override_warn_changes_block_to_warning: with CATSTACK_HOOK_MODE_CATEGORICAL_SCOPE_GUARD=warn, a case that stops today exits 0 and returns a warning instead.
  • New test test_each_finding_writes_one_event_row_with_rule_id: one stopped event row with rule id categorical-scope-guard.narrowed-mutation.
  • python3 engine/skills/make-pr/scripts/preflight.py --dry-run --base origin/plan/hook-architecture-14-put-the-cat-mode-default-hook-onto-the-shared-hook-codeunit engine-runtime: 4 file(s) / ok preflight passed
  • python3 scripts/ci/check_hook_test_coverage.py engine/hooks/categorical-scope-guardcheck_hook_test_coverage: OK (1 hook(s) checked)
  • bash scripts/scrub-handoff-artifacts.shscrub-handoff-artifacts-ok
  • Deliberate crash probe (checker patched to raise RuntimeError("probe")). The expected error log printed: catstack-hook-error categorical-scope-guard: RuntimeError: probe. The probe then printed crash-path exit 0.
  • ruff check --select E9,F on the four changed files: not run, because ruff is not installed on this machine (command not found: ruff).

Revert Plan

Revert Plan
  • Safe to revert? Yes
  • Revert command: git revert <merge-sha>
  • Post-revert steps: None. The hook goes back to choosing its own response and blocking when the checker crashes.
  • Data migration? No

🤖 Generated with Claude Code


Note

High Risk
categorical-scope-guard is security-sensitive; shared-runtime detector crashes now fail open (exit 0) instead of blocking, which can let status-narrowed mutations through if classification breaks.

Overview
categorical-scope-guard now reports Finding objects through engine/hooks/_sdk/runtime.py instead of choosing exit codes in the Claude entrypoint. Thin PreToolUse wrappers exist for Claude, Cursor, and Codex; detect(event) maps HIT/UNCHECKED to rule ids partial-status-filter and unchecked. Registry default stop still blocks, but CATSTACK_HOOK_MODE_CATEGORICAL_SCOPE_GUARD=warn turns blocks into additionalContext warnings and writes per-finding metrics rows (new SDK mode tests).

Documented behavior change: if detect() throws past its own handler, the shared runtime logs catstack-hook-error, records a crash event, and exits 0 (allows the tool). The old entrypoint exited 2 on detector failure.

The same diff also moves demo-freeze and diu-stop (Claude Stop/UserPromptSubmit, Codex notify) onto run_hook, adds CATSTACK_HOOK_METRICS_DIR setup in cat-mode fail-open tests, clarifies invalid JSON stderr in runtime.py, and adds a working-style rule plus test_check_memory_first_rule.py to pin “search saved memory (both ways) before a deep dive.”

Reviewed by Cursor Bugbot for commit f1c98a1. Bugbot is set up for automated code reviews on this repo. Configure here.

EdbertChan and others added 2 commits September 17, 2026 06:14
An answer already written down costs one read. Add the learned rule and a
test that pins it.


Change-Id: I8831f9085cdb5fe672f820dbe96186ca5dc74b29

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
… shared hook code (#730)

* invoker: wf-1789406883560-32/implement-hook-build-the-lever — Put the build-the-lever hook onto the shared hook code.
Review claim: This hook reports findings to the shared hook code, which applies its registry mode and writes event rows. It keeps mode warn.
Review lane: behavior
Safety invariant: The hook gives the same stop, warn, or silent result on every case in its current test folder, except the mode change named in this claim, and its test folder keeps exiting 0.
Effectiveness measurement: `python3 -m unittest discover -s engine/hooks/build-the-lever/tests` exits 0, and the new mode-override case fails before this change.
Slice rationale: One hook per workflow, as the user asked, so each migration is reviewed on its own.
Architectural effect: The build-the-lever entry scripts become thin calls into the shared runtime; its detection returns findings.
Goal: Suggests a script when many files are hand-edited. Keep that behavior while its mode moves into the registry.
Motivation: Mode and output shape live inside each hook today; the shared code makes a mode change a one-line registry edit.
Alternative considerations: Migrating several hooks per workflow was set aside because the user asked for one hook per workflow.
Implementation details: Turn this hook's detection into detect(event) returning Finding objects with stable rule ids, and make each harness entry script call run_hook from engine/hooks/_sdk/runtime.py. It keeps mode warn.
Non-goals: No change to what the hook detects. No other hook changes.
Layer: domain
Feature state: active
Files: engine/hooks/build-the-lever/claude_posttooluse.py, engine/hooks/build-the-lever/claude_prompt_submit.py, engine/hooks/build-the-lever/codex_posttooluse.py, engine/hooks/build-the-lever/codex_prompt_submit.py, engine/hooks/build-the-lever/cursor_before_submit.py, engine/hooks/build-the-lever/cursor_post_tool_use.py, engine/hooks/build-the-lever/detect.py, engine/hooks/build-the-lever/install_claude_hook.py, engine/hooks/build-the-lever/install_codex_hook.py, engine/hooks/build-the-lever/install_cursor_hook.py, engine/hooks/build-the-lever/state.py, engine/hooks/build-the-lever/tests/test_hooks_sdk_mode.py
Change types:
- engine/hooks/build-the-lever/claude_posttooluse.py: modify
- engine/hooks/build-the-lever/claude_prompt_submit.py: modify
- engine/hooks/build-the-lever/codex_posttooluse.py: modify
- engine/hooks/build-the-lever/codex_prompt_submit.py: modify
- engine/hooks/build-the-lever/cursor_before_submit.py: modify
- engine/hooks/build-the-lever/cursor_post_tool_use.py: modify
- engine/hooks/build-the-lever/detect.py: modify
- engine/hooks/build-the-lever/install_claude_hook.py: modify
- engine/hooks/build-the-lever/install_codex_hook.py: modify
- engine/hooks/build-the-lever/install_cursor_hook.py: modify
- engine/hooks/build-the-lever/state.py: modify
- engine/hooks/build-the-lever/tests/test_hooks_sdk_mode.py: create
Acceptance criteria:
- `python3 -m unittest discover -s engine/hooks/build-the-lever/tests` exits 0.
- With CATSTACK_HOOK_MODE_BUILD_THE_LEVER set to warn, a case that stops today produces a warning instead, proving the registry mode drives the response.
- Each finding writes one event row with the hook's rule_id.

Solution:
  Put the build-the-lever hook onto the shared hook code.
Review claim: This hook reports findings to the shared hook code, which applies its registry mode and writes event rows. It keeps mode warn.
Review lane: behavior
Safety invariant: The hook gives the same stop, warn, or silent result on every case in its current test folder, except the mode change named in this claim, and its test folder keeps exiting 0.
Effectiveness measurement: `python3 -m unittest discover -s engine/hooks/build-the-lever/tests` exits 0, and the new mode-override case fails before this change.
Slice rationale: One hook per workflow, as the user asked, so each migration is reviewed on its own.
Architectural effect: The build-the-lever entry scripts become thin calls into the shared runtime; its detection returns findings.
Goal: Suggests a script when many files are hand-edited. Keep that behavior while its mode moves into the registry.
Motivation: Mode and output shape live inside each hook today; the shared code makes a mode change a one-line registry edit.
Alternative considerations: Migrating several hooks per workflow was set aside because the user asked for one hook per workflow.
Implementation details: Turn this hook's detection into detect(event) returning Finding objects with stable rule ids, and make each harness entry script call run_hook from engine/hooks/_sdk/runtime.py. It keeps mode warn.
Non-goals: No change to what the hook detects. No other hook changes.
Layer: domain
Feature state: active
Files: engine/hooks/build-the-lever/claude_posttooluse.py, engine/hooks/build-the-lever/claude_prompt_submit.py, engine/hooks/build-the-lever/codex_posttooluse.py, engine/hooks/build-the-lever/codex_prompt_submit.py, engine/hooks/build-the-lever/cursor_before_submit.py, engine/hooks/build-the-lever/cursor_post_tool_use.py, engine/hooks/build-the-lever/detect.py, engine/hooks/build-the-lever/install_claude_hook.py, engine/hooks/build-the-lever/install_codex_hook.py, engine/hooks/build-the-lever/install_cursor_hook.py, engine/hooks/build-the-lever/state.py, engine/hooks/build-the-lever/tests/test_hooks_sdk_mode.py
Change types:
- engine/hooks/build-the-lever/claude_posttooluse.py: modify
- engine/hooks/build-the-lever/claude_prompt_submit.py: modify
- engine/hooks/build-the-lever/codex_posttooluse.py: modify
- engine/hooks/build-the-lever/codex_prompt_submit.py: modify
- engine/hooks/build-the-lever/cursor_before_submit.py: modify
- engine/hooks/build-the-lever/cursor_post_tool_use.py: modify
- engine/hooks/build-the-lever/detect.py: modify
- engine/hooks/build-the-lever/install_claude_hook.py: modify
- engine/hooks/build-the-lever/install_codex_hook.py: modify
- engine/hooks/build-the-lever/install_cursor_hook.py: modify
- engine/hooks/build-the-lever/state.py: modify
- engine/hooks/build-the-lever/tests/test_hooks_sdk_mode.py: create
Acceptance criteria:
- `python3 -m unittest discover -s engine/hooks/build-the-lever/tests` exits 0.
- With CATSTACK_HOOK_MODE_BUILD_THE_LEVER set to warn, a case that stops today produces a warning instead, proving the registry mode drives the response.
- Each finding writes one event row with the hook's rule_id.

Invoker-Finalize-Id: 35d0555b-3f55-47f9-9e41-97f12e84dc55

* invoker: wf-1789406883560-32/verify-hook-build-the-lever — Run the deterministic proof for put the build-the-lever hook onto the shared hook code.
Review claim: The proof exits 0 only when put the build-the-lever hook onto the shared hook code holds.
Review lane: proof
Safety invariant: Proof only; it changes no product behavior.
Effectiveness measurement: The exit status of `python3 -m unittest discover -s engine/hooks/build-the-lever/tests` is the signal for this slice.
Slice rationale: One proof unit for this workflow.
Architectural effect: None; verification only.
Goal: Prove put the build-the-lever hook onto the shared hook code with one deterministic run.
Motivation: Each workflow carries its own proof so a reviewer can trust the slice alone.
Alternative considerations: Manual inspection was set aside as non-deterministic.
Implementation details: Execute the proof as a terminal gate.
Non-goals: No product edits here.
Layer: e2e_regression
Feature state: active

Exit code: 0
Invoker-Finalize-Id: 3fe2349d-7d5a-4cf2-a6ab-f4c370ef2d0f

* invoker: wf-1789406883560-32/scrub-handoff-artifacts — Terminal read-only gate confirming no ephemeral handoff files were left behind.
Review claim: The workflow leaves no ephemeral handoff files in the tree.
Review lane: proof
Safety invariant: Read-only; it never deletes files, alters the index, or commits caller work.
Effectiveness measurement: A non-zero exit when ephemeral handoff files remain is the signal.
Slice rationale: One unit: the hygiene gate.
Architectural effect: None.
Goal: Confirm no ephemeral handoff files remain after every other task finishes.
Motivation: Ephemeral inter-task files leak into the diff and read as part of the change.
Alternative considerations: Manual inspection was set aside as non-deterministic.
Implementation details: Run scripts/scrub-handoff-artifacts.sh in check mode.
Non-goals: No deletion, no index changes, no commits.
Layer: e2e_regression
Feature state: active

Exit code: 0
Invoker-Finalize-Id: 70147de4-ee65-46de-90da-48ab84cce9d0

* invoker: wf-1789406886887-33/implement-hook-cat-mode-default — Put the cat-mode-default hook onto the shared hook code.
Review claim: This hook reports findings to the shared hook code, which applies its registry mode and writes event rows. It keeps mode warn.
Review lane: behavior
Safety invariant: The hook gives the same stop, warn, or silent result on every case in its current test folder, except the mode change named in this claim, and its test folder keeps exiting 0.
Effectiveness measurement: `python3 -m unittest discover -s engine/hooks/cat-mode-default/tests` exits 0, and the new mode-override case fails before this change.
Slice rationale: One hook per workflow, as the user asked, so each migration is reviewed on its own.
Architectural effect: The cat-mode-default entry scripts become thin calls into the shared runtime; its detection returns findings.
Goal: Applies the user's working style each turn. Keep that behavior while its mode moves into the registry.
Motivation: Mode and output shape live inside each hook today; the shared code makes a mode change a one-line registry edit.
Alternative considerations: Migrating several hooks per workflow was set aside because the user asked for one hook per workflow.
Implementation details: Turn this hook's detection into detect(event) returning Finding objects with stable rule ids, and make each harness entry script call run_hook from engine/hooks/_sdk/runtime.py. It keeps mode warn.
Non-goals: No change to what the hook detects. No other hook changes.
Layer: domain
Feature state: active
Files: engine/hooks/cat-mode-default/claude_pretooluse_agent.py, engine/hooks/cat-mode-default/claude_prompt_submit.py, engine/hooks/cat-mode-default/detect.py, engine/hooks/cat-mode-default/install_claude_hook.py, engine/hooks/cat-mode-default/tests/test_hooks_sdk_mode.py
Change types:
- engine/hooks/cat-mode-default/claude_pretooluse_agent.py: modify
- engine/hooks/cat-mode-default/claude_prompt_submit.py: modify
- engine/hooks/cat-mode-default/detect.py: modify
- engine/hooks/cat-mode-default/install_claude_hook.py: modify
- engine/hooks/cat-mode-default/tests/test_hooks_sdk_mode.py: create
Acceptance criteria:
- `python3 -m unittest discover -s engine/hooks/cat-mode-default/tests` exits 0.
- With CATSTACK_HOOK_MODE_CAT_MODE_DEFAULT set to warn, a case that stops today produces a warning instead, proving the registry mode drives the response.
- Each finding writes one event row with the hook's rule_id.

Solution:
  Put the cat-mode-default hook onto the shared hook code.
Review claim: This hook reports findings to the shared hook code, which applies its registry mode and writes event rows. It keeps mode warn.
Review lane: behavior
Safety invariant: The hook gives the same stop, warn, or silent result on every case in its current test folder, except the mode change named in this claim, and its test folder keeps exiting 0.
Effectiveness measurement: `python3 -m unittest discover -s engine/hooks/cat-mode-default/tests` exits 0, and the new mode-override case fails before this change.
Slice rationale: One hook per workflow, as the user asked, so each migration is reviewed on its own.
Architectural effect: The cat-mode-default entry scripts become thin calls into the shared runtime; its detection returns findings.
Goal: Applies the user's working style each turn. Keep that behavior while its mode moves into the registry.
Motivation: Mode and output shape live inside each hook today; the shared code makes a mode change a one-line registry edit.
Alternative considerations: Migrating several hooks per workflow was set aside because the user asked for one hook per workflow.
Implementation details: Turn this hook's detection into detect(event) returning Finding objects with stable rule ids, and make each harness entry script call run_hook from engine/hooks/_sdk/runtime.py. It keeps mode warn.
Non-goals: No change to what the hook detects. No other hook changes.
Layer: domain
Feature state: active
Files: engine/hooks/cat-mode-default/claude_pretooluse_agent.py, engine/hooks/cat-mode-default/claude_prompt_submit.py, engine/hooks/cat-mode-default/detect.py, engine/hooks/cat-mode-default/install_claude_hook.py, engine/hooks/cat-mode-default/tests/test_hooks_sdk_mode.py
Change types:
- engine/hooks/cat-mode-default/claude_pretooluse_agent.py: modify
- engine/hooks/cat-mode-default/claude_prompt_submit.py: modify
- engine/hooks/cat-mode-default/detect.py: modify
- engine/hooks/cat-mode-default/install_claude_hook.py: modify
- engine/hooks/cat-mode-default/tests/test_hooks_sdk_mode.py: create
Acceptance criteria:
- `python3 -m unittest discover -s engine/hooks/cat-mode-default/tests` exits 0.
- With CATSTACK_HOOK_MODE_CAT_MODE_DEFAULT set to warn, a case that stops today produces a warning instead, proving the registry mode drives the response.
- Each finding writes one event row with the hook's rule_id.

Invoker-Finalize-Id: a0c6917e-a7c5-46f3-a6a1-b370f2d14108

* invoker: wf-1789406886887-33/verify-hook-cat-mode-default — Run the deterministic proof for put the cat-mode-default hook onto the shared hook code.
Review claim: The proof exits 0 only when put the cat-mode-default hook onto the shared hook code holds.
Review lane: proof
Safety invariant: Proof only; it changes no product behavior.
Effectiveness measurement: The exit status of `python3 -m unittest discover -s engine/hooks/cat-mode-default/tests` is the signal for this slice.
Slice rationale: One proof unit for this workflow.
Architectural effect: None; verification only.
Goal: Prove put the cat-mode-default hook onto the shared hook code with one deterministic run.
Motivation: Each workflow carries its own proof so a reviewer can trust the slice alone.
Alternative considerations: Manual inspection was set aside as non-deterministic.
Implementation details: Execute the proof as a terminal gate.
Non-goals: No product edits here.
Layer: e2e_regression
Feature state: active

Exit code: 0
Invoker-Finalize-Id: 8132f734-9bf7-4d89-8c3c-56bc4b969d79

* invoker: wf-1789406886887-33/scrub-handoff-artifacts — Terminal read-only gate confirming no ephemeral handoff files were left behind.
Review claim: The workflow leaves no ephemeral handoff files in the tree.
Review lane: proof
Safety invariant: Read-only; it never deletes files, alters the index, or commits caller work.
Effectiveness measurement: A non-zero exit when ephemeral handoff files remain is the signal.
Slice rationale: One unit: the hygiene gate.
Architectural effect: None.
Goal: Confirm no ephemeral handoff files remain after every other task finishes.
Motivation: Ephemeral inter-task files leak into the diff and read as part of the change.
Alternative considerations: Manual inspection was set aside as non-deterministic.
Implementation details: Run scripts/scrub-handoff-artifacts.sh in check mode.
Non-goals: No deletion, no index changes, no commits.
Layer: e2e_regression
Feature state: active

Exit code: 0
Invoker-Finalize-Id: 74c962cc-f188-4db3-be0c-c3da68c20e9a

* invoker: wf-1789406891093-34/implement-hook-categorical-scope-guard — Put the categorical-scope-guard hook onto the shared hook code.
Review claim: This hook reports findings to the shared hook code, which applies its registry mode and writes event rows. It keeps mode stop.
Review lane: behavior
Safety invariant: The hook gives the same stop, warn, or silent result on every case in its current test folder, except the mode change named in this claim, and its test folder keeps exiting 0.
Effectiveness measurement: `python3 -m unittest discover -s engine/hooks/categorical-scope-guard/tests` exits 0, and the new mode-override case fails before this change.
Slice rationale: One hook per workflow, as the user asked, so each migration is reviewed on its own.
Architectural effect: The categorical-scope-guard entry scripts become thin calls into the shared runtime; its detection returns findings.
Goal: Stops a change that covers only part of an all request. Keep that behavior while its mode moves into the registry.
Motivation: Mode and output shape live inside each hook today; the shared code makes a mode change a one-line registry edit.
Alternative considerations: Migrating several hooks per workflow was set aside because the user asked for one hook per workflow.
Implementation details: Turn this hook's detection into detect(event) returning Finding objects with stable rule ids, and make each harness entry script call run_hook from engine/hooks/_sdk/runtime.py. It keeps mode stop.
Non-goals: No change to what the hook detects. No other hook changes.
Layer: domain
Feature state: active
Files: engine/hooks/categorical-scope-guard/claude_pretooluse.py, engine/hooks/categorical-scope-guard/detect.py, engine/hooks/categorical-scope-guard/install_claude_hook.py, engine/hooks/categorical-scope-guard/tests/test_hooks_sdk_mode.py
Change types:
- engine/hooks/categorical-scope-guard/claude_pretooluse.py: modify
- engine/hooks/categorical-scope-guard/detect.py: modify
- engine/hooks/categorical-scope-guard/install_claude_hook.py: modify
- engine/hooks/categorical-scope-guard/tests/test_hooks_sdk_mode.py: create
Acceptance criteria:
- `python3 -m unittest discover -s engine/hooks/categorical-scope-guard/tests` exits 0.
- With CATSTACK_HOOK_MODE_CATEGORICAL_SCOPE_GUARD set to warn, a case that stops today produces a warning instead, proving the registry mode drives the response.
- Each finding writes one event row with the hook's rule_id.

Solution:
  Put the categorical-scope-guard hook onto the shared hook code.
Review claim: This hook reports findings to the shared hook code, which applies its registry mode and writes event rows. It keeps mode stop.
Review lane: behavior
Safety invariant: The hook gives the same stop, warn, or silent result on every case in its current test folder, except the mode change named in this claim, and its test folder keeps exiting 0.
Effectiveness measurement: `python3 -m unittest discover -s engine/hooks/categorical-scope-guard/tests` exits 0, and the new mode-override case fails before this change.
Slice rationale: One hook per workflow, as the user asked, so each migration is reviewed on its own.
Architectural effect: The categorical-scope-guard entry scripts become thin calls into the shared runtime; its detection returns findings.
Goal: Stops a change that covers only part of an all request. Keep that behavior while its mode moves into the registry.
Motivation: Mode and output shape live inside each hook today; the shared code makes a mode change a one-line registry edit.
Alternative considerations: Migrating several hooks per workflow was set aside because the user asked for one hook per workflow.
Implementation details: Turn this hook's detection into detect(event) returning Finding objects with stable rule ids, and make each harness entry script call run_hook from engine/hooks/_sdk/runtime.py. It keeps mode stop.
Non-goals: No change to what the hook detects. No other hook changes.
Layer: domain
Feature state: active
Files: engine/hooks/categorical-scope-guard/claude_pretooluse.py, engine/hooks/categorical-scope-guard/detect.py, engine/hooks/categorical-scope-guard/install_claude_hook.py, engine/hooks/categorical-scope-guard/tests/test_hooks_sdk_mode.py
Change types:
- engine/hooks/categorical-scope-guard/claude_pretooluse.py: modify
- engine/hooks/categorical-scope-guard/detect.py: modify
- engine/hooks/categorical-scope-guard/install_claude_hook.py: modify
- engine/hooks/categorical-scope-guard/tests/test_hooks_sdk_mode.py: create
Acceptance criteria:
- `python3 -m unittest discover -s engine/hooks/categorical-scope-guard/tests` exits 0.
- With CATSTACK_HOOK_MODE_CATEGORICAL_SCOPE_GUARD set to warn, a case that stops today produces a warning instead, proving the registry mode drives the response.
- Each finding writes one event row with the hook's rule_id.

Invoker-Finalize-Id: 1540cec2-0149-477b-b36f-03c02bb58574

* invoker: wf-1789406891093-34/verify-hook-categorical-scope-guard — Run the deterministic proof for put the categorical-scope-guard hook onto the shared hook code.
Review claim: The proof exits 0 only when put the categorical-scope-guard hook onto the shared hook code holds.
Review lane: proof
Safety invariant: Proof only; it changes no product behavior.
Effectiveness measurement: The exit status of `python3 -m unittest discover -s engine/hooks/categorical-scope-guard/tests` is the signal for this slice.
Slice rationale: One proof unit for this workflow.
Architectural effect: None; verification only.
Goal: Prove put the categorical-scope-guard hook onto the shared hook code with one deterministic run.
Motivation: Each workflow carries its own proof so a reviewer can trust the slice alone.
Alternative considerations: Manual inspection was set aside as non-deterministic.
Implementation details: Execute the proof as a terminal gate.
Non-goals: No product edits here.
Layer: e2e_regression
Feature state: active

Exit code: 0
Invoker-Finalize-Id: 30fc01e9-dbf4-4302-9c20-a786ccb160ef

* invoker: wf-1789406891093-34/scrub-handoff-artifacts — Terminal read-only gate confirming no ephemeral handoff files were left behind.
Review claim: The workflow leaves no ephemeral handoff files in the tree.
Review lane: proof
Safety invariant: Read-only; it never deletes files, alters the index, or commits caller work.
Effectiveness measurement: A non-zero exit when ephemeral handoff files remain is the signal.
Slice rationale: One unit: the hygiene gate.
Architectural effect: None.
Goal: Confirm no ephemeral handoff files remain after every other task finishes.
Motivation: Ephemeral inter-task files leak into the diff and read as part of the change.
Alternative considerations: Manual inspection was set aside as non-deterministic.
Implementation details: Run scripts/scrub-handoff-artifacts.sh in check mode.
Non-goals: No deletion, no index changes, no commits.
Layer: e2e_regression
Feature state: active

Exit code: 0
Invoker-Finalize-Id: 85155944-670a-40b9-a845-1252af44dacd

* categorical-scope-guard: log the detector failure before returning UNCHECKED

check_no_silent_hook_except flagged the broad handler in detect() because it
returned a finding without writing the error to stderr.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Change-Id: I10e84179b9915593bd69889b7ebe3ebdaed05060

---------

Co-authored-by: Invoker Bot <invoker@local>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

Comment thread engine/hooks/categorical-scope-guard/detect.py Outdated
EdbertChan and others added 2 commits September 17, 2026 06:35
… code (#731)

* invoker: wf-1789406883560-32/implement-hook-build-the-lever — Put the build-the-lever hook onto the shared hook code.
Review claim: This hook reports findings to the shared hook code, which applies its registry mode and writes event rows. It keeps mode warn.
Review lane: behavior
Safety invariant: The hook gives the same stop, warn, or silent result on every case in its current test folder, except the mode change named in this claim, and its test folder keeps exiting 0.
Effectiveness measurement: `python3 -m unittest discover -s engine/hooks/build-the-lever/tests` exits 0, and the new mode-override case fails before this change.
Slice rationale: One hook per workflow, as the user asked, so each migration is reviewed on its own.
Architectural effect: The build-the-lever entry scripts become thin calls into the shared runtime; its detection returns findings.
Goal: Suggests a script when many files are hand-edited. Keep that behavior while its mode moves into the registry.
Motivation: Mode and output shape live inside each hook today; the shared code makes a mode change a one-line registry edit.
Alternative considerations: Migrating several hooks per workflow was set aside because the user asked for one hook per workflow.
Implementation details: Turn this hook's detection into detect(event) returning Finding objects with stable rule ids, and make each harness entry script call run_hook from engine/hooks/_sdk/runtime.py. It keeps mode warn.
Non-goals: No change to what the hook detects. No other hook changes.
Layer: domain
Feature state: active
Files: engine/hooks/build-the-lever/claude_posttooluse.py, engine/hooks/build-the-lever/claude_prompt_submit.py, engine/hooks/build-the-lever/codex_posttooluse.py, engine/hooks/build-the-lever/codex_prompt_submit.py, engine/hooks/build-the-lever/cursor_before_submit.py, engine/hooks/build-the-lever/cursor_post_tool_use.py, engine/hooks/build-the-lever/detect.py, engine/hooks/build-the-lever/install_claude_hook.py, engine/hooks/build-the-lever/install_codex_hook.py, engine/hooks/build-the-lever/install_cursor_hook.py, engine/hooks/build-the-lever/state.py, engine/hooks/build-the-lever/tests/test_hooks_sdk_mode.py
Change types:
- engine/hooks/build-the-lever/claude_posttooluse.py: modify
- engine/hooks/build-the-lever/claude_prompt_submit.py: modify
- engine/hooks/build-the-lever/codex_posttooluse.py: modify
- engine/hooks/build-the-lever/codex_prompt_submit.py: modify
- engine/hooks/build-the-lever/cursor_before_submit.py: modify
- engine/hooks/build-the-lever/cursor_post_tool_use.py: modify
- engine/hooks/build-the-lever/detect.py: modify
- engine/hooks/build-the-lever/install_claude_hook.py: modify
- engine/hooks/build-the-lever/install_codex_hook.py: modify
- engine/hooks/build-the-lever/install_cursor_hook.py: modify
- engine/hooks/build-the-lever/state.py: modify
- engine/hooks/build-the-lever/tests/test_hooks_sdk_mode.py: create
Acceptance criteria:
- `python3 -m unittest discover -s engine/hooks/build-the-lever/tests` exits 0.
- With CATSTACK_HOOK_MODE_BUILD_THE_LEVER set to warn, a case that stops today produces a warning instead, proving the registry mode drives the response.
- Each finding writes one event row with the hook's rule_id.

Solution:
  Put the build-the-lever hook onto the shared hook code.
Review claim: This hook reports findings to the shared hook code, which applies its registry mode and writes event rows. It keeps mode warn.
Review lane: behavior
Safety invariant: The hook gives the same stop, warn, or silent result on every case in its current test folder, except the mode change named in this claim, and its test folder keeps exiting 0.
Effectiveness measurement: `python3 -m unittest discover -s engine/hooks/build-the-lever/tests` exits 0, and the new mode-override case fails before this change.
Slice rationale: One hook per workflow, as the user asked, so each migration is reviewed on its own.
Architectural effect: The build-the-lever entry scripts become thin calls into the shared runtime; its detection returns findings.
Goal: Suggests a script when many files are hand-edited. Keep that behavior while its mode moves into the registry.
Motivation: Mode and output shape live inside each hook today; the shared code makes a mode change a one-line registry edit.
Alternative considerations: Migrating several hooks per workflow was set aside because the user asked for one hook per workflow.
Implementation details: Turn this hook's detection into detect(event) returning Finding objects with stable rule ids, and make each harness entry script call run_hook from engine/hooks/_sdk/runtime.py. It keeps mode warn.
Non-goals: No change to what the hook detects. No other hook changes.
Layer: domain
Feature state: active
Files: engine/hooks/build-the-lever/claude_posttooluse.py, engine/hooks/build-the-lever/claude_prompt_submit.py, engine/hooks/build-the-lever/codex_posttooluse.py, engine/hooks/build-the-lever/codex_prompt_submit.py, engine/hooks/build-the-lever/cursor_before_submit.py, engine/hooks/build-the-lever/cursor_post_tool_use.py, engine/hooks/build-the-lever/detect.py, engine/hooks/build-the-lever/install_claude_hook.py, engine/hooks/build-the-lever/install_codex_hook.py, engine/hooks/build-the-lever/install_cursor_hook.py, engine/hooks/build-the-lever/state.py, engine/hooks/build-the-lever/tests/test_hooks_sdk_mode.py
Change types:
- engine/hooks/build-the-lever/claude_posttooluse.py: modify
- engine/hooks/build-the-lever/claude_prompt_submit.py: modify
- engine/hooks/build-the-lever/codex_posttooluse.py: modify
- engine/hooks/build-the-lever/codex_prompt_submit.py: modify
- engine/hooks/build-the-lever/cursor_before_submit.py: modify
- engine/hooks/build-the-lever/cursor_post_tool_use.py: modify
- engine/hooks/build-the-lever/detect.py: modify
- engine/hooks/build-the-lever/install_claude_hook.py: modify
- engine/hooks/build-the-lever/install_codex_hook.py: modify
- engine/hooks/build-the-lever/install_cursor_hook.py: modify
- engine/hooks/build-the-lever/state.py: modify
- engine/hooks/build-the-lever/tests/test_hooks_sdk_mode.py: create
Acceptance criteria:
- `python3 -m unittest discover -s engine/hooks/build-the-lever/tests` exits 0.
- With CATSTACK_HOOK_MODE_BUILD_THE_LEVER set to warn, a case that stops today produces a warning instead, proving the registry mode drives the response.
- Each finding writes one event row with the hook's rule_id.

Invoker-Finalize-Id: 35d0555b-3f55-47f9-9e41-97f12e84dc55

* invoker: wf-1789406883560-32/verify-hook-build-the-lever — Run the deterministic proof for put the build-the-lever hook onto the shared hook code.
Review claim: The proof exits 0 only when put the build-the-lever hook onto the shared hook code holds.
Review lane: proof
Safety invariant: Proof only; it changes no product behavior.
Effectiveness measurement: The exit status of `python3 -m unittest discover -s engine/hooks/build-the-lever/tests` is the signal for this slice.
Slice rationale: One proof unit for this workflow.
Architectural effect: None; verification only.
Goal: Prove put the build-the-lever hook onto the shared hook code with one deterministic run.
Motivation: Each workflow carries its own proof so a reviewer can trust the slice alone.
Alternative considerations: Manual inspection was set aside as non-deterministic.
Implementation details: Execute the proof as a terminal gate.
Non-goals: No product edits here.
Layer: e2e_regression
Feature state: active

Exit code: 0
Invoker-Finalize-Id: 3fe2349d-7d5a-4cf2-a6ab-f4c370ef2d0f

* invoker: wf-1789406883560-32/scrub-handoff-artifacts — Terminal read-only gate confirming no ephemeral handoff files were left behind.
Review claim: The workflow leaves no ephemeral handoff files in the tree.
Review lane: proof
Safety invariant: Read-only; it never deletes files, alters the index, or commits caller work.
Effectiveness measurement: A non-zero exit when ephemeral handoff files remain is the signal.
Slice rationale: One unit: the hygiene gate.
Architectural effect: None.
Goal: Confirm no ephemeral handoff files remain after every other task finishes.
Motivation: Ephemeral inter-task files leak into the diff and read as part of the change.
Alternative considerations: Manual inspection was set aside as non-deterministic.
Implementation details: Run scripts/scrub-handoff-artifacts.sh in check mode.
Non-goals: No deletion, no index changes, no commits.
Layer: e2e_regression
Feature state: active

Exit code: 0
Invoker-Finalize-Id: 70147de4-ee65-46de-90da-48ab84cce9d0

* invoker: wf-1789406886887-33/implement-hook-cat-mode-default — Put the cat-mode-default hook onto the shared hook code.
Review claim: This hook reports findings to the shared hook code, which applies its registry mode and writes event rows. It keeps mode warn.
Review lane: behavior
Safety invariant: The hook gives the same stop, warn, or silent result on every case in its current test folder, except the mode change named in this claim, and its test folder keeps exiting 0.
Effectiveness measurement: `python3 -m unittest discover -s engine/hooks/cat-mode-default/tests` exits 0, and the new mode-override case fails before this change.
Slice rationale: One hook per workflow, as the user asked, so each migration is reviewed on its own.
Architectural effect: The cat-mode-default entry scripts become thin calls into the shared runtime; its detection returns findings.
Goal: Applies the user's working style each turn. Keep that behavior while its mode moves into the registry.
Motivation: Mode and output shape live inside each hook today; the shared code makes a mode change a one-line registry edit.
Alternative considerations: Migrating several hooks per workflow was set aside because the user asked for one hook per workflow.
Implementation details: Turn this hook's detection into detect(event) returning Finding objects with stable rule ids, and make each harness entry script call run_hook from engine/hooks/_sdk/runtime.py. It keeps mode warn.
Non-goals: No change to what the hook detects. No other hook changes.
Layer: domain
Feature state: active
Files: engine/hooks/cat-mode-default/claude_pretooluse_agent.py, engine/hooks/cat-mode-default/claude_prompt_submit.py, engine/hooks/cat-mode-default/detect.py, engine/hooks/cat-mode-default/install_claude_hook.py, engine/hooks/cat-mode-default/tests/test_hooks_sdk_mode.py
Change types:
- engine/hooks/cat-mode-default/claude_pretooluse_agent.py: modify
- engine/hooks/cat-mode-default/claude_prompt_submit.py: modify
- engine/hooks/cat-mode-default/detect.py: modify
- engine/hooks/cat-mode-default/install_claude_hook.py: modify
- engine/hooks/cat-mode-default/tests/test_hooks_sdk_mode.py: create
Acceptance criteria:
- `python3 -m unittest discover -s engine/hooks/cat-mode-default/tests` exits 0.
- With CATSTACK_HOOK_MODE_CAT_MODE_DEFAULT set to warn, a case that stops today produces a warning instead, proving the registry mode drives the response.
- Each finding writes one event row with the hook's rule_id.

Solution:
  Put the cat-mode-default hook onto the shared hook code.
Review claim: This hook reports findings to the shared hook code, which applies its registry mode and writes event rows. It keeps mode warn.
Review lane: behavior
Safety invariant: The hook gives the same stop, warn, or silent result on every case in its current test folder, except the mode change named in this claim, and its test folder keeps exiting 0.
Effectiveness measurement: `python3 -m unittest discover -s engine/hooks/cat-mode-default/tests` exits 0, and the new mode-override case fails before this change.
Slice rationale: One hook per workflow, as the user asked, so each migration is reviewed on its own.
Architectural effect: The cat-mode-default entry scripts become thin calls into the shared runtime; its detection returns findings.
Goal: Applies the user's working style each turn. Keep that behavior while its mode moves into the registry.
Motivation: Mode and output shape live inside each hook today; the shared code makes a mode change a one-line registry edit.
Alternative considerations: Migrating several hooks per workflow was set aside because the user asked for one hook per workflow.
Implementation details: Turn this hook's detection into detect(event) returning Finding objects with stable rule ids, and make each harness entry script call run_hook from engine/hooks/_sdk/runtime.py. It keeps mode warn.
Non-goals: No change to what the hook detects. No other hook changes.
Layer: domain
Feature state: active
Files: engine/hooks/cat-mode-default/claude_pretooluse_agent.py, engine/hooks/cat-mode-default/claude_prompt_submit.py, engine/hooks/cat-mode-default/detect.py, engine/hooks/cat-mode-default/install_claude_hook.py, engine/hooks/cat-mode-default/tests/test_hooks_sdk_mode.py
Change types:
- engine/hooks/cat-mode-default/claude_pretooluse_agent.py: modify
- engine/hooks/cat-mode-default/claude_prompt_submit.py: modify
- engine/hooks/cat-mode-default/detect.py: modify
- engine/hooks/cat-mode-default/install_claude_hook.py: modify
- engine/hooks/cat-mode-default/tests/test_hooks_sdk_mode.py: create
Acceptance criteria:
- `python3 -m unittest discover -s engine/hooks/cat-mode-default/tests` exits 0.
- With CATSTACK_HOOK_MODE_CAT_MODE_DEFAULT set to warn, a case that stops today produces a warning instead, proving the registry mode drives the response.
- Each finding writes one event row with the hook's rule_id.

Invoker-Finalize-Id: a0c6917e-a7c5-46f3-a6a1-b370f2d14108

* invoker: wf-1789406886887-33/verify-hook-cat-mode-default — Run the deterministic proof for put the cat-mode-default hook onto the shared hook code.
Review claim: The proof exits 0 only when put the cat-mode-default hook onto the shared hook code holds.
Review lane: proof
Safety invariant: Proof only; it changes no product behavior.
Effectiveness measurement: The exit status of `python3 -m unittest discover -s engine/hooks/cat-mode-default/tests` is the signal for this slice.
Slice rationale: One proof unit for this workflow.
Architectural effect: None; verification only.
Goal: Prove put the cat-mode-default hook onto the shared hook code with one deterministic run.
Motivation: Each workflow carries its own proof so a reviewer can trust the slice alone.
Alternative considerations: Manual inspection was set aside as non-deterministic.
Implementation details: Execute the proof as a terminal gate.
Non-goals: No product edits here.
Layer: e2e_regression
Feature state: active

Exit code: 0
Invoker-Finalize-Id: 8132f734-9bf7-4d89-8c3c-56bc4b969d79

* invoker: wf-1789406886887-33/scrub-handoff-artifacts — Terminal read-only gate confirming no ephemeral handoff files were left behind.
Review claim: The workflow leaves no ephemeral handoff files in the tree.
Review lane: proof
Safety invariant: Read-only; it never deletes files, alters the index, or commits caller work.
Effectiveness measurement: A non-zero exit when ephemeral handoff files remain is the signal.
Slice rationale: One unit: the hygiene gate.
Architectural effect: None.
Goal: Confirm no ephemeral handoff files remain after every other task finishes.
Motivation: Ephemeral inter-task files leak into the diff and read as part of the change.
Alternative considerations: Manual inspection was set aside as non-deterministic.
Implementation details: Run scripts/scrub-handoff-artifacts.sh in check mode.
Non-goals: No deletion, no index changes, no commits.
Layer: e2e_regression
Feature state: active

Exit code: 0
Invoker-Finalize-Id: 74c962cc-f188-4db3-be0c-c3da68c20e9a

* invoker: wf-1789406891093-34/implement-hook-categorical-scope-guard — Put the categorical-scope-guard hook onto the shared hook code.
Review claim: This hook reports findings to the shared hook code, which applies its registry mode and writes event rows. It keeps mode stop.
Review lane: behavior
Safety invariant: The hook gives the same stop, warn, or silent result on every case in its current test folder, except the mode change named in this claim, and its test folder keeps exiting 0.
Effectiveness measurement: `python3 -m unittest discover -s engine/hooks/categorical-scope-guard/tests` exits 0, and the new mode-override case fails before this change.
Slice rationale: One hook per workflow, as the user asked, so each migration is reviewed on its own.
Architectural effect: The categorical-scope-guard entry scripts become thin calls into the shared runtime; its detection returns findings.
Goal: Stops a change that covers only part of an all request. Keep that behavior while its mode moves into the registry.
Motivation: Mode and output shape live inside each hook today; the shared code makes a mode change a one-line registry edit.
Alternative considerations: Migrating several hooks per workflow was set aside because the user asked for one hook per workflow.
Implementation details: Turn this hook's detection into detect(event) returning Finding objects with stable rule ids, and make each harness entry script call run_hook from engine/hooks/_sdk/runtime.py. It keeps mode stop.
Non-goals: No change to what the hook detects. No other hook changes.
Layer: domain
Feature state: active
Files: engine/hooks/categorical-scope-guard/claude_pretooluse.py, engine/hooks/categorical-scope-guard/detect.py, engine/hooks/categorical-scope-guard/install_claude_hook.py, engine/hooks/categorical-scope-guard/tests/test_hooks_sdk_mode.py
Change types:
- engine/hooks/categorical-scope-guard/claude_pretooluse.py: modify
- engine/hooks/categorical-scope-guard/detect.py: modify
- engine/hooks/categorical-scope-guard/install_claude_hook.py: modify
- engine/hooks/categorical-scope-guard/tests/test_hooks_sdk_mode.py: create
Acceptance criteria:
- `python3 -m unittest discover -s engine/hooks/categorical-scope-guard/tests` exits 0.
- With CATSTACK_HOOK_MODE_CATEGORICAL_SCOPE_GUARD set to warn, a case that stops today produces a warning instead, proving the registry mode drives the response.
- Each finding writes one event row with the hook's rule_id.

Solution:
  Put the categorical-scope-guard hook onto the shared hook code.
Review claim: This hook reports findings to the shared hook code, which applies its registry mode and writes event rows. It keeps mode stop.
Review lane: behavior
Safety invariant: The hook gives the same stop, warn, or silent result on every case in its current test folder, except the mode change named in this claim, and its test folder keeps exiting 0.
Effectiveness measurement: `python3 -m unittest discover -s engine/hooks/categorical-scope-guard/tests` exits 0, and the new mode-override case fails before this change.
Slice rationale: One hook per workflow, as the user asked, so each migration is reviewed on its own.
Architectural effect: The categorical-scope-guard entry scripts become thin calls into the shared runtime; its detection returns findings.
Goal: Stops a change that covers only part of an all request. Keep that behavior while its mode moves into the registry.
Motivation: Mode and output shape live inside each hook today; the shared code makes a mode change a one-line registry edit.
Alternative considerations: Migrating several hooks per workflow was set aside because the user asked for one hook per workflow.
Implementation details: Turn this hook's detection into detect(event) returning Finding objects with stable rule ids, and make each harness entry script call run_hook from engine/hooks/_sdk/runtime.py. It keeps mode stop.
Non-goals: No change to what the hook detects. No other hook changes.
Layer: domain
Feature state: active
Files: engine/hooks/categorical-scope-guard/claude_pretooluse.py, engine/hooks/categorical-scope-guard/detect.py, engine/hooks/categorical-scope-guard/install_claude_hook.py, engine/hooks/categorical-scope-guard/tests/test_hooks_sdk_mode.py
Change types:
- engine/hooks/categorical-scope-guard/claude_pretooluse.py: modify
- engine/hooks/categorical-scope-guard/detect.py: modify
- engine/hooks/categorical-scope-guard/install_claude_hook.py: modify
- engine/hooks/categorical-scope-guard/tests/test_hooks_sdk_mode.py: create
Acceptance criteria:
- `python3 -m unittest discover -s engine/hooks/categorical-scope-guard/tests` exits 0.
- With CATSTACK_HOOK_MODE_CATEGORICAL_SCOPE_GUARD set to warn, a case that stops today produces a warning instead, proving the registry mode drives the response.
- Each finding writes one event row with the hook's rule_id.

Invoker-Finalize-Id: 1540cec2-0149-477b-b36f-03c02bb58574

* invoker: wf-1789406891093-34/verify-hook-categorical-scope-guard — Run the deterministic proof for put the categorical-scope-guard hook onto the shared hook code.
Review claim: The proof exits 0 only when put the categorical-scope-guard hook onto the shared hook code holds.
Review lane: proof
Safety invariant: Proof only; it changes no product behavior.
Effectiveness measurement: The exit status of `python3 -m unittest discover -s engine/hooks/categorical-scope-guard/tests` is the signal for this slice.
Slice rationale: One proof unit for this workflow.
Architectural effect: None; verification only.
Goal: Prove put the categorical-scope-guard hook onto the shared hook code with one deterministic run.
Motivation: Each workflow carries its own proof so a reviewer can trust the slice alone.
Alternative considerations: Manual inspection was set aside as non-deterministic.
Implementation details: Execute the proof as a terminal gate.
Non-goals: No product edits here.
Layer: e2e_regression
Feature state: active

Exit code: 0
Invoker-Finalize-Id: 30fc01e9-dbf4-4302-9c20-a786ccb160ef

* invoker: wf-1789406891093-34/scrub-handoff-artifacts — Terminal read-only gate confirming no ephemeral handoff files were left behind.
Review claim: The workflow leaves no ephemeral handoff files in the tree.
Review lane: proof
Safety invariant: Read-only; it never deletes files, alters the index, or commits caller work.
Effectiveness measurement: A non-zero exit when ephemeral handoff files remain is the signal.
Slice rationale: One unit: the hygiene gate.
Architectural effect: None.
Goal: Confirm no ephemeral handoff files remain after every other task finishes.
Motivation: Ephemeral inter-task files leak into the diff and read as part of the change.
Alternative considerations: Manual inspection was set aside as non-deterministic.
Implementation details: Run scripts/scrub-handoff-artifacts.sh in check mode.
Non-goals: No deletion, no index changes, no commits.
Layer: e2e_regression
Feature state: active

Exit code: 0
Invoker-Finalize-Id: 85155944-670a-40b9-a845-1252af44dacd

* invoker: wf-1789406894793-35/implement-hook-demo-freeze — Put the demo-freeze hook onto the shared hook code.
Review claim: This hook reports findings to the shared hook code, which applies its registry mode and writes event rows. It keeps mode stop.
Review lane: behavior
Safety invariant: The hook gives the same stop, warn, or silent result on every case in its current test folder, except the mode change named in this claim, and its test folder keeps exiting 0.
Effectiveness measurement: `python3 -m unittest discover -s engine/hooks/demo-freeze/tests` exits 0, and the new mode-override case fails before this change.
Slice rationale: One hook per workflow, as the user asked, so each migration is reviewed on its own.
Architectural effect: The demo-freeze entry scripts become thin calls into the shared runtime; its detection returns findings.
Goal: Stops edits to what the user is demoing. Keep that behavior while its mode moves into the registry.
Motivation: Mode and output shape live inside each hook today; the shared code makes a mode change a one-line registry edit.
Alternative considerations: Migrating several hooks per workflow was set aside because the user asked for one hook per workflow.
Implementation details: Turn this hook's detection into detect(event) returning Finding objects with stable rule ids, and make each harness entry script call run_hook from engine/hooks/_sdk/runtime.py. It keeps mode stop.
Non-goals: No change to what the hook detects. No other hook changes.
Layer: domain
Feature state: active
Files: engine/hooks/demo-freeze/claude_pretooluse_check.py, engine/hooks/demo-freeze/install_claude_hook.py, engine/hooks/demo-freeze/tests/test_hooks_sdk_mode.py
Change types:
- engine/hooks/demo-freeze/claude_pretooluse_check.py: modify
- engine/hooks/demo-freeze/install_claude_hook.py: modify
- engine/hooks/demo-freeze/tests/test_hooks_sdk_mode.py: create
Acceptance criteria:
- `python3 -m unittest discover -s engine/hooks/demo-freeze/tests` exits 0.
- With CATSTACK_HOOK_MODE_DEMO_FREEZE set to warn, a case that stops today produces a warning instead, proving the registry mode drives the response.
- Each finding writes one event row with the hook's rule_id.

Exit code: 1
Invoker-Finalize-Id: 4a3989de-6a6e-4011-9900-c8aa481e9c27

* invoker: wf-1789406894793-35/implement-hook-demo-freeze — Put the demo-freeze hook onto the shared hook code.
Review claim: This hook reports findings to the shared hook code, which applies its registry mode and writes event rows. It keeps mode stop.
Review lane: behavior
Safety invariant: The hook gives the same stop, warn, or silent result on every case in its current test folder, except the mode change named in this claim, and its test folder keeps exiting 0.
Effectiveness measurement: `python3 -m unittest discover -s engine/hooks/demo-freeze/tests` exits 0, and the new mode-override case fails before this change.
Slice rationale: One hook per workflow, as the user asked, so each migration is reviewed on its own.
Architectural effect: The demo-freeze entry scripts become thin calls into the shared runtime; its detection returns findings.
Goal: Stops edits to what the user is demoing. Keep that behavior while its mode moves into the registry.
Motivation: Mode and output shape live inside each hook today; the shared code makes a mode change a one-line registry edit.
Alternative considerations: Migrating several hooks per workflow was set aside because the user asked for one hook per workflow.
Implementation details: Turn this hook's detection into detect(event) returning Finding objects with stable rule ids, and make each harness entry script call run_hook from engine/hooks/_sdk/runtime.py. It keeps mode stop.
Non-goals: No change to what the hook detects. No other hook changes.
Layer: domain
Feature state: active
Files: engine/hooks/demo-freeze/claude_pretooluse_check.py, engine/hooks/demo-freeze/install_claude_hook.py, engine/hooks/demo-freeze/tests/test_hooks_sdk_mode.py
Change types:
- engine/hooks/demo-freeze/claude_pretooluse_check.py: modify
- engine/hooks/demo-freeze/install_claude_hook.py: modify
- engine/hooks/demo-freeze/tests/test_hooks_sdk_mode.py: create
Acceptance criteria:
- `python3 -m unittest discover -s engine/hooks/demo-freeze/tests` exits 0.
- With CATSTACK_HOOK_MODE_DEMO_FREEZE set to warn, a case that stops today produces a warning instead, proving the registry mode drives the response.
- Each finding writes one event row with the hook's rule_id.

Solution:
  Put the demo-freeze hook onto the shared hook code.
Review claim: This hook reports findings to the shared hook code, which applies its registry mode and writes event rows. It keeps mode stop.
Review lane: behavior
Safety invariant: The hook gives the same stop, warn, or silent result on every case in its current test folder, except the mode change named in this claim, and its test folder keeps exiting 0.
Effectiveness measurement: `python3 -m unittest discover -s engine/hooks/demo-freeze/tests` exits 0, and the new mode-override case fails before this change.
Slice rationale: One hook per workflow, as the user asked, so each migration is reviewed on its own.
Architectural effect: The demo-freeze entry scripts become thin calls into the shared runtime; its detection returns findings.
Goal: Stops edits to what the user is demoing. Keep that behavior while its mode moves into the registry.
Motivation: Mode and output shape live inside each hook today; the shared code makes a mode change a one-line registry edit.
Alternative considerations: Migrating several hooks per workflow was set aside because the user asked for one hook per workflow.
Implementation details: Turn this hook's detection into detect(event) returning Finding objects with stable rule ids, and make each harness entry script call run_hook from engine/hooks/_sdk/runtime.py. It keeps mode stop.
Non-goals: No change to what the hook detects. No other hook changes.
Layer: domain
Feature state: active
Files: engine/hooks/demo-freeze/claude_pretooluse_check.py, engine/hooks/demo-freeze/install_claude_hook.py, engine/hooks/demo-freeze/tests/test_hooks_sdk_mode.py
Change types:
- engine/hooks/demo-freeze/claude_pretooluse_check.py: modify
- engine/hooks/demo-freeze/install_claude_hook.py: modify
- engine/hooks/demo-freeze/tests/test_hooks_sdk_mode.py: create
Acceptance criteria:
- `python3 -m unittest discover -s engine/hooks/demo-freeze/tests` exits 0.
- With CATSTACK_HOOK_MODE_DEMO_FREEZE set to warn, a case that stops today produces a warning instead, proving the registry mode drives the response.
- Each finding writes one event row with the hook's rule_id.

Invoker-Finalize-Id: publish-approved-fix

* invoker: wf-1789406894793-35/verify-hook-demo-freeze — Run the deterministic proof for put the demo-freeze hook onto the shared hook code.
Review claim: The proof exits 0 only when put the demo-freeze hook onto the shared hook code holds.
Review lane: proof
Safety invariant: Proof only; it changes no product behavior.
Effectiveness measurement: The exit status of `python3 -m unittest discover -s engine/hooks/demo-freeze/tests` is the signal for this slice.
Slice rationale: One proof unit for this workflow.
Architectural effect: None; verification only.
Goal: Prove put the demo-freeze hook onto the shared hook code with one deterministic run.
Motivation: Each workflow carries its own proof so a reviewer can trust the slice alone.
Alternative considerations: Manual inspection was set aside as non-deterministic.
Implementation details: Execute the proof as a terminal gate.
Non-goals: No product edits here.
Layer: e2e_regression
Feature state: active

Exit code: 0
Invoker-Finalize-Id: d36e6bb9-501d-4015-bb29-c6ec478f7a78

* invoker: wf-1789406894793-35/scrub-handoff-artifacts — Terminal read-only gate confirming no ephemeral handoff files were left behind.
Review claim: The workflow leaves no ephemeral handoff files in the tree.
Review lane: proof
Safety invariant: Read-only; it never deletes files, alters the index, or commits caller work.
Effectiveness measurement: A non-zero exit when ephemeral handoff files remain is the signal.
Slice rationale: One unit: the hygiene gate.
Architectural effect: None.
Goal: Confirm no ephemeral handoff files remain after every other task finishes.
Motivation: Ephemeral inter-task files leak into the diff and read as part of the change.
Alternative considerations: Manual inspection was set aside as non-deterministic.
Implementation details: Run scripts/scrub-handoff-artifacts.sh in check mode.
Non-goals: No deletion, no index changes, no commits.
Layer: e2e_regression
Feature state: active

Exit code: 0
Invoker-Finalize-Id: 58d2877b-64ef-434d-8479-4faf4996f91f

---------

Co-authored-by: Invoker Bot <invoker@local>
…de (#732)

* invoker: wf-1789406883560-32/implement-hook-build-the-lever — Put the build-the-lever hook onto the shared hook code.
Review claim: This hook reports findings to the shared hook code, which applies its registry mode and writes event rows. It keeps mode warn.
Review lane: behavior
Safety invariant: The hook gives the same stop, warn, or silent result on every case in its current test folder, except the mode change named in this claim, and its test folder keeps exiting 0.
Effectiveness measurement: `python3 -m unittest discover -s engine/hooks/build-the-lever/tests` exits 0, and the new mode-override case fails before this change.
Slice rationale: One hook per workflow, as the user asked, so each migration is reviewed on its own.
Architectural effect: The build-the-lever entry scripts become thin calls into the shared runtime; its detection returns findings.
Goal: Suggests a script when many files are hand-edited. Keep that behavior while its mode moves into the registry.
Motivation: Mode and output shape live inside each hook today; the shared code makes a mode change a one-line registry edit.
Alternative considerations: Migrating several hooks per workflow was set aside because the user asked for one hook per workflow.
Implementation details: Turn this hook's detection into detect(event) returning Finding objects with stable rule ids, and make each harness entry script call run_hook from engine/hooks/_sdk/runtime.py. It keeps mode warn.
Non-goals: No change to what the hook detects. No other hook changes.
Layer: domain
Feature state: active
Files: engine/hooks/build-the-lever/claude_posttooluse.py, engine/hooks/build-the-lever/claude_prompt_submit.py, engine/hooks/build-the-lever/codex_posttooluse.py, engine/hooks/build-the-lever/codex_prompt_submit.py, engine/hooks/build-the-lever/cursor_before_submit.py, engine/hooks/build-the-lever/cursor_post_tool_use.py, engine/hooks/build-the-lever/detect.py, engine/hooks/build-the-lever/install_claude_hook.py, engine/hooks/build-the-lever/install_codex_hook.py, engine/hooks/build-the-lever/install_cursor_hook.py, engine/hooks/build-the-lever/state.py, engine/hooks/build-the-lever/tests/test_hooks_sdk_mode.py
Change types:
- engine/hooks/build-the-lever/claude_posttooluse.py: modify
- engine/hooks/build-the-lever/claude_prompt_submit.py: modify
- engine/hooks/build-the-lever/codex_posttooluse.py: modify
- engine/hooks/build-the-lever/codex_prompt_submit.py: modify
- engine/hooks/build-the-lever/cursor_before_submit.py: modify
- engine/hooks/build-the-lever/cursor_post_tool_use.py: modify
- engine/hooks/build-the-lever/detect.py: modify
- engine/hooks/build-the-lever/install_claude_hook.py: modify
- engine/hooks/build-the-lever/install_codex_hook.py: modify
- engine/hooks/build-the-lever/install_cursor_hook.py: modify
- engine/hooks/build-the-lever/state.py: modify
- engine/hooks/build-the-lever/tests/test_hooks_sdk_mode.py: create
Acceptance criteria:
- `python3 -m unittest discover -s engine/hooks/build-the-lever/tests` exits 0.
- With CATSTACK_HOOK_MODE_BUILD_THE_LEVER set to warn, a case that stops today produces a warning instead, proving the registry mode drives the response.
- Each finding writes one event row with the hook's rule_id.

Solution:
  Put the build-the-lever hook onto the shared hook code.
Review claim: This hook reports findings to the shared hook code, which applies its registry mode and writes event rows. It keeps mode warn.
Review lane: behavior
Safety invariant: The hook gives the same stop, warn, or silent result on every case in its current test folder, except the mode change named in this claim, and its test folder keeps exiting 0.
Effectiveness measurement: `python3 -m unittest discover -s engine/hooks/build-the-lever/tests` exits 0, and the new mode-override case fails before this change.
Slice rationale: One hook per workflow, as the user asked, so each migration is reviewed on its own.
Architectural effect: The build-the-lever entry scripts become thin calls into the shared runtime; its detection returns findings.
Goal: Suggests a script when many files are hand-edited. Keep that behavior while its mode moves into the registry.
Motivation: Mode and output shape live inside each hook today; the shared code makes a mode change a one-line registry edit.
Alternative considerations: Migrating several hooks per workflow was set aside because the user asked for one hook per workflow.
Implementation details: Turn this hook's detection into detect(event) returning Finding objects with stable rule ids, and make each harness entry script call run_hook from engine/hooks/_sdk/runtime.py. It keeps mode warn.
Non-goals: No change to what the hook detects. No other hook changes.
Layer: domain
Feature state: active
Files: engine/hooks/build-the-lever/claude_posttooluse.py, engine/hooks/build-the-lever/claude_prompt_submit.py, engine/hooks/build-the-lever/codex_posttooluse.py, engine/hooks/build-the-lever/codex_prompt_submit.py, engine/hooks/build-the-lever/cursor_before_submit.py, engine/hooks/build-the-lever/cursor_post_tool_use.py, engine/hooks/build-the-lever/detect.py, engine/hooks/build-the-lever/install_claude_hook.py, engine/hooks/build-the-lever/install_codex_hook.py, engine/hooks/build-the-lever/install_cursor_hook.py, engine/hooks/build-the-lever/state.py, engine/hooks/build-the-lever/tests/test_hooks_sdk_mode.py
Change types:
- engine/hooks/build-the-lever/claude_posttooluse.py: modify
- engine/hooks/build-the-lever/claude_prompt_submit.py: modify
- engine/hooks/build-the-lever/codex_posttooluse.py: modify
- engine/hooks/build-the-lever/codex_prompt_submit.py: modify
- engine/hooks/build-the-lever/cursor_before_submit.py: modify
- engine/hooks/build-the-lever/cursor_post_tool_use.py: modify
- engine/hooks/build-the-lever/detect.py: modify
- engine/hooks/build-the-lever/install_claude_hook.py: modify
- engine/hooks/build-the-lever/install_codex_hook.py: modify
- engine/hooks/build-the-lever/install_cursor_hook.py: modify
- engine/hooks/build-the-lever/state.py: modify
- engine/hooks/build-the-lever/tests/test_hooks_sdk_mode.py: create
Acceptance criteria:
- `python3 -m unittest discover -s engine/hooks/build-the-lever/tests` exits 0.
- With CATSTACK_HOOK_MODE_BUILD_THE_LEVER set to warn, a case that stops today produces a warning instead, proving the registry mode drives the response.
- Each finding writes one event row with the hook's rule_id.

Invoker-Finalize-Id: 35d0555b-3f55-47f9-9e41-97f12e84dc55

* invoker: wf-1789406883560-32/verify-hook-build-the-lever — Run the deterministic proof for put the build-the-lever hook onto the shared hook code.
Review claim: The proof exits 0 only when put the build-the-lever hook onto the shared hook code holds.
Review lane: proof
Safety invariant: Proof only; it changes no product behavior.
Effectiveness measurement: The exit status of `python3 -m unittest discover -s engine/hooks/build-the-lever/tests` is the signal for this slice.
Slice rationale: One proof unit for this workflow.
Architectural effect: None; verification only.
Goal: Prove put the build-the-lever hook onto the shared hook code with one deterministic run.
Motivation: Each workflow carries its own proof so a reviewer can trust the slice alone.
Alternative considerations: Manual inspection was set aside as non-deterministic.
Implementation details: Execute the proof as a terminal gate.
Non-goals: No product edits here.
Layer: e2e_regression
Feature state: active

Exit code: 0
Invoker-Finalize-Id: 3fe2349d-7d5a-4cf2-a6ab-f4c370ef2d0f

* invoker: wf-1789406883560-32/scrub-handoff-artifacts — Terminal read-only gate confirming no ephemeral handoff files were left behind.
Review claim: The workflow leaves no ephemeral handoff files in the tree.
Review lane: proof
Safety invariant: Read-only; it never deletes files, alters the index, or commits caller work.
Effectiveness measurement: A non-zero exit when ephemeral handoff files remain is the signal.
Slice rationale: One unit: the hygiene gate.
Architectural effect: None.
Goal: Confirm no ephemeral handoff files remain after every other task finishes.
Motivation: Ephemeral inter-task files leak into the diff and read as part of the change.
Alternative considerations: Manual inspection was set aside as non-deterministic.
Implementation details: Run scripts/scrub-handoff-artifacts.sh in check mode.
Non-goals: No deletion, no index changes, no commits.
Layer: e2e_regression
Feature state: active

Exit code: 0
Invoker-Finalize-Id: 70147de4-ee65-46de-90da-48ab84cce9d0

* invoker: wf-1789406886887-33/implement-hook-cat-mode-default — Put the cat-mode-default hook onto the shared hook code.
Review claim: This hook reports findings to the shared hook code, which applies its registry mode and writes event rows. It keeps mode warn.
Review lane: behavior
Safety invariant: The hook gives the same stop, warn, or silent result on every case in its current test folder, except the mode change named in this claim, and its test folder keeps exiting 0.
Effectiveness measurement: `python3 -m unittest discover -s engine/hooks/cat-mode-default/tests` exits 0, and the new mode-override case fails before this change.
Slice rationale: One hook per workflow, as the user asked, so each migration is reviewed on its own.
Architectural effect: The cat-mode-default entry scripts become thin calls into the shared runtime; its detection returns findings.
Goal: Applies the user's working style each turn. Keep that behavior while its mode moves into the registry.
Motivation: Mode and output shape live inside each hook today; the shared code makes a mode change a one-line registry edit.
Alternative considerations: Migrating several hooks per workflow was set aside because the user asked for one hook per workflow.
Implementation details: Turn this hook's detection into detect(event) returning Finding objects with stable rule ids, and make each harness entry script call run_hook from engine/hooks/_sdk/runtime.py. It keeps mode warn.
Non-goals: No change to what the hook detects. No other hook changes.
Layer: domain
Feature state: active
Files: engine/hooks/cat-mode-default/claude_pretooluse_agent.py, engine/hooks/cat-mode-default/claude_prompt_submit.py, engine/hooks/cat-mode-default/detect.py, engine/hooks/cat-mode-default/install_claude_hook.py, engine/hooks/cat-mode-default/tests/test_hooks_sdk_mode.py
Change types:
- engine/hooks/cat-mode-default/claude_pretooluse_agent.py: modify
- engine/hooks/cat-mode-default/claude_prompt_submit.py: modify
- engine/hooks/cat-mode-default/detect.py: modify
- engine/hooks/cat-mode-default/install_claude_hook.py: modify
- engine/hooks/cat-mode-default/tests/test_hooks_sdk_mode.py: create
Acceptance criteria:
- `python3 -m unittest discover -s engine/hooks/cat-mode-default/tests` exits 0.
- With CATSTACK_HOOK_MODE_CAT_MODE_DEFAULT set to warn, a case that stops today produces a warning instead, proving the registry mode drives the response.
- Each finding writes one event row with the hook's rule_id.

Solution:
  Put the cat-mode-default hook onto the shared hook code.
Review claim: This hook reports findings to the shared hook code, which applies its registry mode and writes event rows. It keeps mode warn.
Review lane: behavior
Safety invariant: The hook gives the same stop, warn, or silent result on every case in its current test folder, except the mode change named in this claim, and its test folder keeps exiting 0.
Effectiveness measurement: `python3 -m unittest discover -s engine/hooks/cat-mode-default/tests` exits 0, and the new mode-override case fails before this change.
Slice rationale: One hook per workflow, as the user asked, so each migration is reviewed on its own.
Architectural effect: The cat-mode-default entry scripts become thin calls into the shared runtime; its detection returns findings.
Goal: Applies the user's working style each turn. Keep that behavior while its mode moves into the registry.
Motivation: Mode and output shape live inside each hook today; the shared code makes a mode change a one-line registry edit.
Alternative considerations: Migrating several hooks per workflow was set aside because the user asked for one hook per workflow.
Implementation details: Turn this hook's detection into detect(event) returning Finding objects with stable rule ids, and make each harness entry script call run_hook from engine/hooks/_sdk/runtime.py. It keeps mode warn.
Non-goals: No change to what the hook detects. No other hook changes.
Layer: domain
Feature state: active
Files: engine/hooks/cat-mode-default/claude_pretooluse_agent.py, engine/hooks/cat-mode-default/claude_prompt_submit.py, engine/hooks/cat-mode-default/detect.py, engine/hooks/cat-mode-default/install_claude_hook.py, engine/hooks/cat-mode-default/tests/test_hooks_sdk_mode.py
Change types:
- engine/hooks/cat-mode-default/claude_pretooluse_agent.py: modify
- engine/hooks/cat-mode-default/claude_prompt_submit.py: modify
- engine/hooks/cat-mode-default/detect.py: modify
- engine/hooks/cat-mode-default/install_claude_hook.py: modify
- engine/hooks/cat-mode-default/tests/test_hooks_sdk_mode.py: create
Acceptance criteria:
- `python3 -m unittest discover -s engine/hooks/cat-mode-default/tests` exits 0.
- With CATSTACK_HOOK_MODE_CAT_MODE_DEFAULT set to warn, a case that stops today produces a warning instead, proving the registry mode drives the response.
- Each finding writes one event row with the hook's rule_id.

Invoker-Finalize-Id: a0c6917e-a7c5-46f3-a6a1-b370f2d14108

* invoker: wf-1789406886887-33/verify-hook-cat-mode-default — Run the deterministic proof for put the cat-mode-default hook onto the shared hook code.
Review claim: The proof exits 0 only when put the cat-mode-default hook onto the shared hook code holds.
Review lane: proof
Safety invariant: Proof only; it changes no product behavior.
Effectiveness measurement: The exit status of `python3 -m unittest discover -s engine/hooks/cat-mode-default/tests` is the signal for this slice.
Slice rationale: One proof unit for this workflow.
Architectural effect: None; verification only.
Goal: Prove put the cat-mode-default hook onto the shared hook code with one deterministic run.
Motivation: Each workflow carries its own proof so a reviewer can trust the slice alone.
Alternative considerations: Manual inspection was set aside as non-deterministic.
Implementation details: Execute the proof as a terminal gate.
Non-goals: No product edits here.
Layer: e2e_regression
Feature state: active

Exit code: 0
Invoker-Finalize-Id: 8132f734-9bf7-4d89-8c3c-56bc4b969d79

* invoker: wf-1789406886887-33/scrub-handoff-artifacts — Terminal read-only gate confirming no ephemeral handoff files were left behind.
Review claim: The workflow leaves no ephemeral handoff files in the tree.
Review lane: proof
Safety invariant: Read-only; it never deletes files, alters the index, or commits caller work.
Effectiveness measurement: A non-zero exit when ephemeral handoff files remain is the signal.
Slice rationale: One unit: the hygiene gate.
Architectural effect: None.
Goal: Confirm no ephemeral handoff files remain after every other task finishes.
Motivation: Ephemeral inter-task files leak into the diff and read as part of the change.
Alternative considerations: Manual inspection was set aside as non-deterministic.
Implementation details: Run scripts/scrub-handoff-artifacts.sh in check mode.
Non-goals: No deletion, no index changes, no commits.
Layer: e2e_regression
Feature state: active

Exit code: 0
Invoker-Finalize-Id: 74c962cc-f188-4db3-be0c-c3da68c20e9a

* invoker: wf-1789406891093-34/implement-hook-categorical-scope-guard — Put the categorical-scope-guard hook onto the shared hook code.
Review claim: This hook reports findings to the shared hook code, which applies its registry mode and writes event rows. It keeps mode stop.
Review lane: behavior
Safety invariant: The hook gives the same stop, warn, or silent result on every case in its current test folder, except the mode change named in this claim, and its test folder keeps exiting 0.
Effectiveness measurement: `python3 -m unittest discover -s engine/hooks/categorical-scope-guard/tests` exits 0, and the new mode-override case fails before this change.
Slice rationale: One hook per workflow, as the user asked, so each migration is reviewed on its own.
Architectural effect: The categorical-scope-guard entry scripts become thin calls into the shared runtime; its detection returns findings.
Goal: Stops a change that covers only part of an all request. Keep that behavior while its mode moves into the registry.
Motivation: Mode and output shape live inside each hook today; the shared code makes a mode change a one-line registry edit.
Alternative considerations: Migrating several hooks per workflow was set aside because the user asked for one hook per workflow.
Implementation details: Turn this hook's detection into detect(event) returning Finding objects with stable rule ids, and make each harness entry script call run_hook from engine/hooks/_sdk/runtime.py. It keeps mode stop.
Non-goals: No change to what the hook detects. No other hook changes.
Layer: domain
Feature state: active
Files: engine/hooks/categorical-scope-guard/claude_pretooluse.py, engine/hooks/categorical-scope-guard/detect.py, engine/hooks/categorical-scope-guard/install_claude_hook.py, engine/hooks/categorical-scope-guard/tests/test_hooks_sdk_mode.py
Change types:
- engine/hooks/categorical-scope-guard/claude_pretooluse.py: modify
- engine/hooks/categorical-scope-guard/detect.py: modify
- engine/hooks/categorical-scope-guard/install_claude_hook.py: modify
- engine/hooks/categorical-scope-guard/tests/test_hooks_sdk_mode.py: create
Acceptance criteria:
- `python3 -m unittest discover -s engine/hooks/categorical-scope-guard/tests` exits 0.
- With CATSTACK_HOOK_MODE_CATEGORICAL_SCOPE_GUARD set to warn, a case that stops today produces a warning instead, proving the registry mode drives the response.
- Each finding writes one event row with the hook's rule_id.

Solution:
  Put the categorical-scope-guard hook onto the shared hook code.
Review claim: This hook reports findings to the shared hook code, which applies its registry mode and writes event rows. It keeps mode stop.
Review lane: behavior
Safety invariant: The hook gives the same stop, warn, or silent result on every case in its current test folder, except the mode change named in this claim, and its test folder keeps exiting 0.
Effectiveness measurement: `python3 -m unittest discover -s engine/hooks/categorical-scope-guard/tests` exits 0, and the new mode-override case fails before this change.
Slice rationale: One hook per workflow, as the user asked, so each migration is reviewed on its own.
Architectural effect: The categorical-scope-guard entry scripts become thin calls into the shared runtime; its detection returns findings.
Goal: Stops a change that covers only part of an all request. Keep that behavior while its mode moves into the registry.
Motivation: Mode and output shape live inside each hook today; the shared code makes a mode change a one-line registry edit.
Alternative considerations: Migrating several hooks per workflow was set aside because the user asked for one hook per workflow.
Implementation details: Turn this hook's detection into detect(event) returning Finding objects with stable rule ids, and make each harness entry script call run_hook from engine/hooks/_sdk/runtime.py. It keeps mode stop.
Non-goals: No change to what the hook detects. No other hook changes.
Layer: domain
Feature state: active
Files: engine/hooks/categorical-scope-guard/claude_pretooluse.py, engine/hooks/categorical-scope-guard/detect.py, engine/hooks/categorical-scope-guard/install_claude_hook.py, engine/hooks/categorical-scope-guard/tests/test_hooks_sdk_mode.py
Change types:
- engine/hooks/categorical-scope-guard/claude_pretooluse.py: modify
- engine/hooks/categorical-scope-guard/detect.py: modify
- engine/hooks/categorical-scope-guard/install_claude_hook.py: modify
- engine/hooks/categorical-scope-guard/tests/test_hooks_sdk_mode.py: create
Acceptance criteria:
- `python3 -m unittest discover -s engine/hooks/categorical-scope-guard/tests` exits 0.
- With CATSTACK_HOOK_MODE_CATEGORICAL_SCOPE_GUARD set to warn, a case that stops today produces a warning instead, proving the registry mode drives the response.
- Each finding writes one event row with the hook's rule_id.

Invoker-Finalize-Id: 1540cec2-0149-477b-b36f-03c02bb58574

* invoker: wf-1789406891093-34/verify-hook-categorical-scope-guard — Run the deterministic proof for put the categorical-scope-guard hook onto the shared hook code.
Review claim: The proof exits 0 only when put the categorical-scope-guard hook onto the shared hook code holds.
Review lane: proof
Safety invariant: Proof only; it changes no product behavior.
Effectiveness measurement: The exit status of `python3 -m unittest discover -s engine/hooks/categorical-scope-guard/tests` is the signal for this slice.
Slice rationale: One proof unit for this workflow.
Architectural effect: None; verification only.
Goal: Prove put the categorical-scope-guard hook onto the shared hook code with one deterministic run.
Motivation: Each workflow carries its own proof so a reviewer can trust the slice alone.
Alternative considerations: Manual inspection was set aside as non-deterministic.
Implementation details: Execute the proof as a terminal gate.
Non-goals: No product edits here.
Layer: e2e_regression
Feature state: active

Exit code: 0
Invoker-Finalize-Id: 30fc01e9-dbf4-4302-9c20-a786ccb160ef

* invoker: wf-1789406891093-34/scrub-handoff-artifacts — Terminal read-only gate confirming no ephemeral handoff files were left behind.
Review claim: The workflow leaves no ephemeral handoff files in the tree.
Review lane: proof
Safety invariant: Read-only; it never deletes files, alters the index, or commits caller work.
Effectiveness measurement: A non-zero exit when ephemeral handoff files remain is the signal.
Slice rationale: One unit: the hygiene gate.
Architectural effect: None.
Goal: Confirm no ephemeral handoff files remain after every other task finishes.
Motivation: Ephemeral inter-task files leak into the diff and read as part of the change.
Alternative considerations: Manual inspection was set aside as non-deterministic.
Implementation details: Run scripts/scrub-handoff-artifacts.sh in check mode.
Non-goals: No deletion, no index changes, no commits.
Layer: e2e_regression
Feature state: active

Exit code: 0
Invoker-Finalize-Id: 85155944-670a-40b9-a845-1252af44dacd

* invoker: wf-1789406894793-35/implement-hook-demo-freeze — Put the demo-freeze hook onto the shared hook code.
Review claim: This hook reports findings to the shared hook code, which applies its registry mode and writes event rows. It keeps mode stop.
Review lane: behavior
Safety invariant: The hook gives the same stop, warn, or silent result on every case in its current test folder, except the mode change named in this claim, and its test folder keeps exiting 0.
Effectiveness measurement: `python3 -m unittest discover -s engine/hooks/demo-freeze/tests` exits 0, and the new mode-override case fails before this change.
Slice rationale: One hook per workflow, as the user asked, so each migration is reviewed on its own.
Architectural effect: The demo-freeze entry scripts become thin calls into the shared runtime; its detection returns findings.
Goal: Stops edits to what the user is demoing. Keep that behavior while its mode moves into the registry.
Motivation: Mode and output shape live inside each hook today; the shared code makes a mode change a one-line registry edit.
Alternative considerations: Migrating several hooks per workflow was set aside because the user asked for one hook per workflow.
Implementation details: Turn this hook's detection into detect(event) returning Finding objects with stable rule ids, and make each harness entry script call run_hook from engine/hooks/_sdk/runtime.py. It keeps mode stop.
Non-goals: No change to what the hook detects. No other hook changes.
Layer: domain
Feature state: active
Files: engine/hooks/demo-freeze/claude_pretooluse_check.py, engine/hooks/demo-freeze/install_claude_hook.py, engine/hooks/demo-freeze/tests/test_hooks_sdk_mode.py
Change types:
- engine/hooks/demo-freeze/claude_pretooluse_check.py: modify
- engine/hooks/demo-freeze/install_claude_hook.py: modify
- engine/hooks/demo-freeze/tests/test_hooks_sdk_mode.py: create
Acceptance criteria:
- `python3 -m unittest discover -s engine/hooks/demo-freeze/tests` exits 0.
- With CATSTACK_HOOK_MODE_DEMO_FREEZE set to warn, a case that stops today produces a warning instead, proving the registry mode drives the response.
- Each finding writes one event row with the hook's rule_id.

Exit code: 1
Invoker-Finalize-Id: 4a3989de-6a6e-4011-9900-c8aa481e9c27

* invoker: wf-1789406894793-35/implement-hook-demo-freeze — Put the demo-freeze hook onto the shared hook code.
Review claim: This hook reports findings to the shared hook code, which applies its registry mode and writes event rows. It keeps mode stop.
Review lane: behavior
Safety invariant: The hook gives the same stop, warn, or silent result on every case in its current test folder, except the mode change named in this claim, and its test folder keeps exiting 0.
Effectiveness measurement: `python3 -m unittest discover -s engine/hooks/demo-freeze/tests` exits 0, and the new mode-override case fails before this change.
Slice rationale: One hook per workflow, as the user asked, so each migration is reviewed on its own.
Architectural effect: The demo-freeze entry scripts become thin calls into the shared runtime; its detection returns findings.
Goal: Stops edits to what the user is demoing. Keep that behavior while its mode moves into the registry.
Motivation: Mode and output shape live inside each hook today; the shared code makes a mode change a one-line registry edit.
Alternative considerations: Migrating several hooks per workflow was set aside because the user asked for one hook per workflow.
Implementation details: Turn this hook's detection into detect(event) returning Finding objects with stable rule ids, and make each harness entry script call run_hook from engine/hooks/_sdk/runtime.py. It keeps mode stop.
Non-goals: No change to what the hook detects. No other hook changes.
Layer: domain
Feature state: active
Files: engine/hooks/demo-freeze/claude_pretooluse_check.py, engine/hooks/demo-freeze/install_claude_hook.py, engine/hooks/demo-freeze/tests/test_hooks_sdk_mode.py
Change types:
- engine/hooks/demo-freeze/claude_pretooluse_check.py: modify
- engine/hooks/demo-freeze/install_claude_hook.py: modify
- engine/hooks/demo-freeze/tests/test_hooks_sdk_mode.py: create
Acceptance criteria:
- `python3 -m unittest discover -s engine/hooks/demo-freeze/tests` exits 0.
- With CATSTACK_HOOK_MODE_DEMO_FREEZE set to warn, a case that stops today produces a warning instead, proving the registry mode drives the response.
- Each finding writes one event row with the hook's rule_id.

Solution:
  Put the demo-freeze hook onto the shared hook code.
Review claim: This hook reports findings to the shared hook code, which applies its registry mode and writes event rows. It keeps mode stop.
Review lane: behavior
Safety invariant: The hook gives the same stop, warn, or silent result on every case in its current test folder, except the mode change named in this claim, and its test folder keeps exiting 0.
Effectiveness measurement: `python3 -m unittest discover -s engine/hooks/demo-freeze/tests` exits 0, and the new mode-override case fails before this change.
Slice rationale: One hook per workflow, as the user asked, so each migration is reviewed on its own.
Architectural effect: The demo-freeze entry scripts become thin calls into the shared runtime; its detection returns findings.
Goal: Stops edits to what the user is demoing. Keep that behavior while its mode moves into the registry.
Motivation: Mode and output shape live inside each hook today; the shared code makes a mode change a one-line registry edit.
Alternative considerations: Migrating several hooks per workflow was set aside because the user asked for one hook per workflow.
Implementation details: Turn this hook's detection into detect(event) returning Finding objects with stable rule ids, and make each harness entry script call run_hook from engine/hooks/_sdk/runtime.py. It keeps mode stop.
Non-goals: No change to what the hook detects. No other hook changes.
Layer: domain
Feature state: active
Files: engine/hooks/demo-freeze/claude_pretooluse_check.py, engine/hooks/demo-freeze/install_claude_hook.py, engine/hooks/demo-freeze/tests/test_hooks_sdk_mode.py
Change types:
- engine/hooks/demo-freeze/claude_pretooluse_check.py: modify
- engine/hooks/demo-freeze/install_claude_hook.py: modify
- engine/hooks/demo-freeze/tests/test_hooks_sdk_mode.py: create
Acceptance criteria:
- `python3 -m unittest discover -s engine/hooks/demo-freeze/tests` exits 0.
- With CATSTACK_HOOK_MODE_DEMO_FREEZE set to warn, a case that stops today produces a warning instead, proving the registry mode drives the response.
- Each finding writes one event row with the hook's rule_id.

Invoker-Finalize-Id: publish-approved-fix

* invoker: wf-1789406894793-35/verify-hook-demo-freeze — Run the deterministic proof for put the demo-freeze hook onto the shared hook code.
Review claim: The proof exits 0 only when put the demo-freeze hook onto the shared hook code holds.
Review lane: proof
Safety invariant: Proof only; it changes no product behavior.
Effectiveness measurement: The exit status of `python3 -m unittest discover -s engine/hooks/demo-freeze/tests` is the signal for this slice.
Slice rationale: One proof unit for this workflow.
Architectural effect: None; verification only.
Goal: Prove put the demo-freeze hook onto the shared hook code with one deterministic run.
Motivation: Each workflow carries its own proof so a reviewer can trust the slice alone.
Alternative considerations: Manual inspection was set aside as non-deterministic.
Implementation details: Execute the proof as a terminal gate.
Non-goals: No product edits here.
Layer: e2e_regression
Feature state: active

Exit code: 0
Invoker-Finalize-Id: d36e6bb9-501d-4015-bb29-c6ec478f7a78

* invoker: wf-1789406894793-35/scrub-handoff-artifacts — Terminal read-only gate confirming no ephemeral handoff files were left behind.
Review claim: The workflow leaves no ephemeral handoff files in the tree.
Review lane: proof
Safety invariant: Read-only; it never deletes files, alters the index, or commits caller work.
Effectiveness measurement: A non-zero exit when ephemeral handoff files remain is the signal.
Slice rationale: One unit: the hygiene gate.
Architectural effect: None.
Goal: Confirm no ephemeral handoff files remain after every other task finishes.
Motivation: Ephemeral inter-task files leak into the diff and read as part of the change.
Alternative considerations: Manual inspection was set aside as non-deterministic.
Implementation details: Run scripts/scrub-handoff-artifacts.sh in check mode.
Non-goals: No deletion, no index changes, no commits.
Layer: e2e_regression
Feature state: active

Exit code: 0
Invoker-Finalize-Id: 58d2877b-64ef-434d-8479-4faf4996f91f

* invoker: wf-1789406897817-36/implement-hook-diu-stop — Put the diu-stop hook onto the shared hook code.
Review claim: This hook reports findings to the shared hook code, which applies its registry mode and writes event rows. It keeps mode stop.
Review lane: behavior
Safety invariant: The hook gives the same stop, warn, or silent result on every case in its current test folder, except the mode change named in this claim, and its test folder keeps exiting 0.
Effectiveness measurement: `python3 -m unittest discover -s engine/hooks/diu-stop/tests` exits 0, and the new mode-override case fails before this change.
Slice rationale: One hook per workflow, as the user asked, so each migration is reviewed on its own.
Architectural effect: The diu-stop entry scripts become thin calls into the shared runtime; its detection returns findings.
Goal: Stops a reply that is too long or unproven. Keep that behavior while its mode moves into the registry.
Motivation: Mode and output shape live inside each hook today; the shared code makes a mode change a one-line registry edit.
Alternative considerations: Migrating several hooks per workflow was set aside because the user asked for one hook per workflow.
Implementation details: Turn this hook's detection into detect(event) returning Finding objects with stable rule ids, and make each harness entry script call run_hook from engine/hooks/_sdk/runtime.py. It keeps mode stop.
Non-goals: No change to what the hook detects. No other hook changes.
Layer: domain
Feature state: active
Files: engine/hooks/diu-stop/claude_prompt_reminder.py, engine/hooks/diu-stop/claude_stop_check.py, engine/hooks/diu-stop/codex_notify.py, engine/hooks/diu-stop/diu_limit.py, engine/hooks/diu-stop/install_claude_hook.py, engine/hooks/diu-stop/install_codex_notify.py, engine/hooks/diu-stop/plain_words.py, engine/hooks/diu-stop/tests/test_hooks_sdk_mode.py
Change types:
- engine/hooks/diu-stop/claude_prompt_reminder.py: modify
- engine/hooks/diu-stop/claude_stop_check.py: modify
- engine/hooks/diu-stop/codex_notify.py: modify
- engine/hooks/diu-stop/diu_limit.py: modify
- engine/hooks/diu-stop/install_claude_hook.py: modify
- engine/hooks/diu-stop/install_codex_notify.py: modify
- engine/hooks/diu-stop/plain_words.py: modify
- engine/hooks/diu-stop/tests/test_hooks_sdk_mode.py: create
Acceptance criteria:
- `python3 -m unittest discover -s engine/hooks/diu-stop/tests` exits 0.
- With CATSTACK_HOOK_MODE_DIU_STOP set to warn, a case that stops today produces a warning instead, proving the registry mode drives the response.
- Each finding writes one event row with the hook's rule_id.

Exit code: 1
Invoker-Finalize-Id: cc582581-b6f8-4f71-8dd9-cf16d79f1628

* invoker: wf-1789406897817-36/implement-hook-diu-stop — Put the diu-stop hook onto the shared hook code.
Review claim: This hook reports findings to the shared hook code, which applies its registry mode and writes event rows. It keeps mode stop.
Review lane: behavior
Safety invariant: The hook gives the same stop, warn, or silent result on every case in its current test folder, except the mode change named in this claim, and its test folder keeps exiting 0.
Effectiveness measurement: `python3 -m unittest discover -s engine/hooks/diu-stop/tests` exits 0, and the new mode-override case fails before this change.
Slice rationale: One hook per workflow, as the user asked, so each migration is reviewed on its own.
Architectural effect: The diu-stop entry scripts become thin calls into the shared runtime; its detection returns findings.
Goal: Stops a reply that is too long or unproven. Keep that behavior while its mode moves into the registry.
Motivation: Mode and output shape live inside each hook today; the shared code makes a mode change a one-line registry edit.
Alternative considerations: Migrating several hooks per workflow was set aside because the user asked for one hook per workflow.
Implementation details: Turn this hook's detection into detect(event) returning Finding objects with stable rule ids, and make each harness entry script call run_hook from engine/hooks/_sdk/runtime.py. It keeps mode stop.
Non-goals: No change to what the hook detects. No other hook changes.
Layer: domain
Feature state: active
Files: engine/hooks/diu-stop/claude_prompt_reminder.py, engine/hooks/diu-stop/claude_stop_check.py, engine/hooks/diu-stop/codex_notify.py, engine/hooks/diu-stop/diu_limit.py, engine/hooks/diu-stop/install_claude_hook.py, engine/hooks/diu-stop/install_codex_notify.py, engine/hooks/diu-stop/plain_words.py, engine/hooks/diu-stop/tests/test_hooks_sdk_mode.py
Change types:
- engine/hooks/diu-stop/claude_prompt_reminder.py: modify
- engine/hooks/diu-stop/claude_stop_check.py: modify
- engine/hooks/diu-stop/codex_notify.py: modify
- engine/hooks/diu-stop/diu_limit.py: modify
- engine/hooks/diu-stop/install_claude_hook.py: modify
- engine/hooks/diu-stop/install_codex_notify.py: modify
- engine/hooks/diu-stop/plain_words.py: modify
- engine/hooks/diu-stop/tests/test_hooks_sdk_mode.py: create
Acceptance criteria:
- `python3 -m unittest discover -s engine/hooks/diu-stop/tests` exits 0.
- With CATSTACK_HOOK_MODE_DIU_STOP set to warn, a case that stops today produces a warning instead, proving the registry mode drives the response.
- Each finding writes one event row with the hook's rule_id.

Solution:
  Put the diu-stop hook onto the shared hook code.
Review claim: This hook reports findings to the shared hook code, which applies its registry mode and writes event rows. It keeps mode stop.
Review lane: behavior
Safety invariant: The hook gives the same stop, warn, or silent result on every case in its current test folder, except the mode change named in this claim, and its test folder keeps exiting 0.
Effectiveness measurement: `python3 -m unittest discover -s engine/hooks/diu-stop/tests` exits 0, and the new mode-override case fails before this change.
Slice rationale: One hook per workflow, as the user asked, so each migration is reviewed on its own.
Architectural effect: The diu-stop entry scripts become thin calls into the shared runtime; its detection returns findings.
Goal: Stops a reply that is too long or unproven. Keep that behavior while its mode moves into the registry.
Motivation: Mode and output shape live inside each hook today; the shared code makes a mode change a one-line registry edit.
Alternative considerations: Migrating several hooks per workflow was set aside because the user asked for one hook per workflow.
Implementation details: Turn this hook's detection into detect(event) returning Finding objects with stable rule ids, and make each harness entry script call run_hook from engine/hooks/_sdk/runtime.py. It keeps mode stop.
Non-goals: No change to what the hook detects. No other hook changes.
Layer: domain
Feature state: active
Files: engine/hooks/diu-stop/claude_prompt_reminder.py, engine/hooks/diu-stop/claude_stop_check.py, engine/hooks/diu-stop/codex_notify.py, engine/hooks/diu-stop/diu_limit.py, engine/hooks/diu-stop/install_claude_hook.py, engine/hooks/diu-stop/install_codex_notify.py, engine/hooks/diu-stop/plain_words.py, engine/hooks/diu-stop/tests/test_hooks_sdk_mode.py
Change types:
- engine/hooks/diu-stop/claude_prompt_reminder.py: modify
- engine/hooks/diu-stop/claude_stop_check.py: modify
- engine/hooks/diu-stop/codex_notify.py: modify
- engine/hooks/diu-stop/diu_limit.py: modify
- engine/hooks/diu-stop/install_claude_hook.py: modify
- engine/hooks/diu-stop/install_codex_notify.py: modify
- engine/hooks/diu-stop/plain_words.py: modify
- engine/hooks/diu-stop/tests/test_hooks_sdk_mode.py: create
Acceptance criteria:
- `python3 -m unittest discover -s engine/hooks/diu-stop/tests` exits 0.
- With CATSTACK_HOOK_MODE_DIU_STOP set to warn, a case that stops today produces a warning instead, proving the registry mode drives the response.
- Each finding writes one event row with the hook's rule_id.

Invoker-Finalize-Id: publish-approved-fix

* invoker: wf-1789406897817-36/verify-hook-diu-stop — Run the deterministic proof for put the diu-stop hook onto the shared hook code.
Review claim: The proof exits 0 only when put the diu-stop hook onto the shared hook code holds.
Review lane: proof
Safety invariant: Proof only; it changes no product behavior.
Effectiveness measurement: The exit status of `python3 -m unittest discover -s engine/hooks/diu-stop/tests` is the signal for this slice.
Slice rationale: One proof unit for this workflow.
Architectural effect: None; verification only.
Goal: Prove put the diu-stop hook onto the shared hook code with one deterministic run.
Motivation: Each workflow carries its own proof so a reviewer can trust the slice alone.
Alternative considerations: Manual inspection was set aside as non-deterministic.
Implementation details: Execute the proof as a terminal gate.
Non-goals: No product edits here.
Layer: e2e_regression
Feature state: active

Exit code: 0
Invoker-Finalize-Id: 26d6f94a-7646-4bba-a249-4e63e35f3a4c

* invoker: wf-1789406897817-36/scrub-handoff-artifacts — Terminal read-only gate confirming no ephemeral handoff files were left behind.
Review claim: The workflow leaves no ephemeral handoff files in the tree.
Review lane: proof
Safety invariant: Read-only; it never deletes files, alters the index, or commits caller work.
Effectiveness measurement: A non-zero exit when ephemeral handoff files remain is the signal.
Slice rationale: One unit: the hygiene gate.
Architectural effect: None.
Goal: Confirm no ephemeral handoff files remain after every other task finishes.
Motivation: Ephemeral inter-task files leak into the diff and read as part of the change.
Alternative considerations: Manual inspection was set aside as non-deterministic.
Implementation details: Run scripts/scrub-handoff-artifacts.sh in check mode.
Non-goals: No deletion, no index changes, no commits.
Layer: e2e_regression
Feature state: active

Exit code: 0
Invoker-Finalize-Id: d41f4da8-d2ba-4eec-847c-65460155a619

---------

Co-authored-by: Invoker Bot <invoker@local>
@EdbertChan

Copy link
Copy Markdown
Owner Author

Mergify repair stopped: unresolved human review thread PRRT_kwDOT3uYWs6jPLm9

EdbertChan and others added 6 commits September 17, 2026 10:02
… hook code (#766)

* invoker: wf-1789406883560-32/implement-hook-build-the-lever — Put the build-the-lever hook onto the shared hook code.
Review claim: This hook reports findings to the shared hook code, which applies its registry mode and writes event rows. It keeps mode warn.
Review lane: behavior
Safety invariant: The hook gives the same stop, warn, or silent result on every case in its current test folder, except the mode change named in this claim, and its test folder keeps exiting 0.
Effectiveness measurement: `python3 -m unittest discover -s engine/hooks/build-the-lever/tests` exits 0, and the new mode-override case fails before this change.
Slice rationale: One hook per workflow, as the user asked, so each migration is reviewed on its own.
Architectural effect: The build-the-lever entry scripts become thin calls into the shared runtime; its detection returns findings.
Goal: Suggests a script when many files are hand-edited. Keep that behavior while its mode moves into the registry.
Motivation: Mode and output shape live inside each hook today; the shared code makes a mode change a one-line registry edit.
Alternative considerations: Migrating several hooks per workflow was set aside because the user asked for one hook per workflow.
Implementation details: Turn this hook's detection into detect(event) returning Finding objects with stable rule ids, and make each harness entry script call run_hook from engine/hooks/_sdk/runtime.py. It keeps mode warn.
Non-goals: No change to what the hook detects. No other hook changes.
Layer: domain
Feature state: active
Files: engine/hooks/build-the-lever/claude_posttooluse.py, engine/hooks/build-the-lever/claude_prompt_submit.py, engine/hooks/build-the-lever/codex_posttooluse.py, engine/hooks/build-the-lever/codex_prompt_submit.py, engine/hooks/build-the-lever/cursor_before_submit.py, engine/hooks/build-the-lever/cursor_post_tool_use.py, engine/hooks/build-the-lever/detect.py, engine/hooks/build-the-lever/install_claude_hook.py, engine/hooks/build-the-lever/install_codex_hook.py, engine/hooks/build-the-lever/install_cursor_hook.py, engine/hooks/build-the-lever/state.py, engine/hooks/build-the-lever/tests/test_hooks_sdk_mode.py
Change types:
- engine/hooks/build-the-lever/claude_posttooluse.py: modify
- engine/hooks/build-the-lever/claude_prompt_submit.py: modify
- engine/hooks/build-the-lever/codex_posttooluse.py: modify
- engine/hooks/build-the-lever/codex_prompt_submit.py: modify
- engine/hooks/build-the-lever/cursor_before_submit.py: modify
- engine/hooks/build-the-lever/cursor_post_tool_use.py: modify
- engine/hooks/build-the-lever/detect.py: modify
- engine/hooks/build-the-lever/install_claude_hook.py: modify
- engine/hooks/build-the-lever/install_codex_hook.py: modify
- engine/hooks/build-the-lever/install_cursor_hook.py: modify
- engine/hooks/build-the-lever/state.py: modify
- engine/hooks/build-the-lever/tests/test_hooks_sdk_mode.py: create
Acceptance criteria:
- `python3 -m unittest discover -s engine/hooks/build-the-lever/tests` exits 0.
- With CATSTACK_HOOK_MODE_BUILD_THE_LEVER set to warn, a case that stops today produces a warning instead, proving the registry mode drives the response.
- Each finding writes one event row with the hook's rule_id.

Solution:
  Put the build-the-lever hook onto the shared hook code.
Review claim: This hook reports findings to the shared hook code, which applies its registry mode and writes event rows. It keeps mode warn.
Review lane: behavior
Safety invariant: The hook gives the same stop, warn, or silent result on every case in its current test folder, except the mode change named in this claim, and its test folder keeps exiting 0.
Effectiveness measurement: `python3 -m unittest discover -s engine/hooks/build-the-lever/tests` exits 0, and the new mode-override case fails before this change.
Slice rationale: One hook per workflow, as the user asked, so each migration is reviewed on its own.
Architectural effect: The build-the-lever entry scripts become thin calls into the shared runtime; its detection returns findings.
Goal: Suggests a script when many files are hand-edited. Keep that behavior while its mode moves into the registry.
Motivation: Mode and output shape live inside each hook today; the shared code makes a mode change a one-line registry edit.
Alternative considerations: Migrating several hooks per workflow was set aside because the user asked for one hook per workflow.
Implementation details: Turn this hook's detection into detect(event) returning Finding objects with stable rule ids, and make each harness entry script call run_hook from engine/hooks/_sdk/runtime.py. It keeps mode warn.
Non-goals: No change to what the hook detects. No other hook changes.
Layer: domain
Feature state: active
Files: engine/hooks/build-the-lever/claude_posttooluse.py, engine/hooks/build-the-lever/claude_prompt_submit.py, engine/hooks/build-the-lever/codex_posttooluse.py, engine/hooks/build-the-lever/codex_prompt_submit.py, engine/hooks/build-the-lever/cursor_before_submit.py, engine/hooks/build-the-lever/cursor_post_tool_use.py, engine/hooks/build-the-lever/detect.py, engine/hooks/build-the-lever/install_claude_hook.py, engine/hooks/build-the-lever/install_codex_hook.py, engine/hooks/build-the-lever/install_cursor_hook.py, engine/hooks/build-the-lever/state.py, engine/hooks/build-the-lever/tests/test_hooks_sdk_mode.py
Change types:
- engine/hooks/build-the-lever/claude_posttooluse.py: modify
- engine/hooks/build-the-lever/claude_prompt_submit.py: modify
- engine/hooks/build-the-lever/codex_posttooluse.py: modify
- engine/hooks/build-the-lever/codex_prompt_submit.py: modify
- engine/hooks/build-the-lever/cursor_before_submit.py: modify
- engine/hooks/build-the-lever/cursor_post_tool_use.py: modify
- engine/hooks/build-the-lever/detect.py: modify
- engine/hooks/build-the-lever/install_claude_hook.py: modify
- engine/hooks/build-the-lever/install_codex_hook.py: modify
- engine/hooks/build-the-lever/install_cursor_hook.py: modify
- engine/hooks/build-the-lever/state.py: modify
- engine/hooks/build-the-lever/tests/test_hooks_sdk_mode.py: create
Acceptance criteria:
- `python3 -m unittest discover -s engine/hooks/build-the-lever/tests` exits 0.
- With CATSTACK_HOOK_MODE_BUILD_THE_LEVER set to warn, a case that stops today produces a warning instead, proving the registry mode drives the response.
- Each finding writes one event row with the hook's rule_id.

Invoker-Finalize-Id: 35d0555b-3f55-47f9-9e41-97f12e84dc55

* invoker: wf-1789406883560-32/verify-hook-build-the-lever — Run the deterministic proof for put the build-the-lever hook onto the shared hook code.
Review claim: The proof exits 0 only when put the build-the-lever hook onto the shared hook code holds.
Review lane: proof
Safety invariant: Proof only; it changes no product behavior.
Effectiveness measurement: The exit status of `python3 -m unittest discover -s engine/hooks/build-the-lever/tests` is the signal for this slice.
Slice rationale: One proof unit for this workflow.
Architectural effect: None; verification only.
Goal: Prove put the build-the-lever hook onto the shared hook code with one deterministic run.
Motivation: Each workflow carries its own proof so a reviewer can trust the slice alone.
Alternative considerations: Manual inspection was set aside as non-deterministic.
Implementation details: Execute the proof as a terminal gate.
Non-goals: No product edits here.
Layer: e2e_regression
Feature state: active

Exit code: 0
Invoker-Finalize-Id: 3fe2349d-7d5a-4cf2-a6ab-f4c370ef2d0f

* invoker: wf-1789406883560-32/scrub-handoff-artifacts — Terminal read-only gate confirming no ephemeral handoff files were left behind.
Review claim: The workflow leaves no ephemeral handoff files in the tree.
Review lane: proof
Safety invariant: Read-only; it never deletes files, alters the index, or commits caller work.
Effectiveness measurement: A non-zero exit when ephemeral handoff files remain is the signal.
Slice rationale: One unit: the hygiene gate.
Architectural effect: None.
Goal: Confirm no ephemeral handoff files remain after every other task finishes.
Motivation: Ephemeral inter-task files leak into the diff and read as part of the change.
Alternative considerations: Manual inspection was set aside as non-deterministic.
Implementation details: Run scripts/scrub-handoff-artifacts.sh in check mode.
Non-goals: No deletion, no index changes, no commits.
Layer: e2e_regression
Feature state: active

Exit code: 0
Invoker-Finalize-Id: 70147de4-ee65-46de-90da-48ab84cce9d0

* invoker: wf-1789406886887-33/implement-hook-cat-mode-default — Put the cat-mode-default hook onto the shared hook code.
Review claim: This hook reports findings to the shared hook code, which applies its registry mode and writes event rows. It keeps mode warn.
Review lane: behavior
Safety invariant: The hook gives the same stop, warn, or silent result on every case in its current test folder, except the mode change named in this claim, and its test folder keeps exiting 0.
Effectiveness measurement: `python3 -m unittest discover -s engine/hooks/cat-mode-default/tests` exits 0, and the new mode-override case fails before this change.
Slice rationale: One hook per workflow, as the user asked, so each migration is reviewed on its own.
Architectural effect: The cat-mode-default entry scripts become thin calls into the shared runtime; its detection returns findings.
Goal: Applies the user's working style each turn. Keep that behavior while its mode moves into the registry.
Motivation: Mode and output shape live inside each hook today; the shared code makes a mode change a one-line registry edit.
Alternative considerations: Migrating several hooks per workflow was set aside because the user asked for one hook per workflow.
Implementation details: Turn this hook's detection into detect(event) returning Finding objects with stable rule ids, and make each harness entry script call run_hook from engine/hooks/_sdk/runtime.py. It keeps mode warn.
Non-goals: No change to what the hook detects. No other hook changes.
Layer: domain
Feature state: active
Files: engine/hooks/cat-mode-default/claude_pretooluse_agent.py, engine/hooks/cat-mode-default/claude_prompt_submit.py, engine/hooks/cat-mode-default/detect.py, engine/hooks/cat-mode-default/install_claude_hook.py, engine/hooks/cat-mode-default/tests/test_hooks_sdk_mode.py
Change types:
- engine/hooks/cat-mode-default/claude_pretooluse_agent.py: modify
- engine/hooks/cat-mode-default/claude_prompt_submit.py: modify
- engine/hooks/cat-mode-default/detect.py: modify
- engine/hooks/cat-mode-default/install_claude_hook.py: modify
- engine/hooks/cat-mode-default/tests/test_hooks_sdk_mode.py: create
Acceptance criteria:
- `python3 -m unittest discover -s engine/hooks/cat-mode-default/tests` exits 0.
- With CATSTACK_HOOK_MODE_CAT_MODE_DEFAULT set to warn, a case that stops today produces a warning instead, proving the registry mode drives the response.
- Each finding writes one event row with the hook's rule_id.

Solution:
  Put the cat-mode-default hook onto the shared hook code.
Review claim: This hook reports findings to the shared hook code, which applies its registry mode and writes event rows. It keeps mode warn.
Review lane: behavior
Safety invariant: The hook gives the same stop, warn, or silent result on every case in its current test folder, except the mode change named in this claim, and its test folder keeps exiting 0.
Effectiveness measurement: `python3 -m unittest discover -s engine/hooks/cat-mode-default/tests` exits 0, and the new mode-override case fails before this change.
Slice rationale: One hook per workflow, as the user asked, so each migration is reviewed on its own.
Architectural effect: The cat-mode-default entry scripts become thin calls into the shared runtime; its detection returns findings.
Goal: Applies the user's working style each turn. Keep that behavior while its mode moves into the registry.
Motivation: Mode and output shape live inside each hook today; the shared code makes a mode change a one-line registry edit.
Alternative considerations: Migrating several hooks per workflow was set aside because the user asked for one hook per workflow.
Implementation details: Turn this hook's detection into detect(event) returning Finding objects with stable rule ids, and make each harness entry script call run_hook from engine/hooks/_sdk/runtime.py. It keeps mode warn.
Non-goals: No change to what the hook detects. No other hook changes.
Layer: domain
Feature state: active
Files: engine/hooks/cat-mode-default/claude_pretooluse_agent.py, engine/hooks/cat-mode-default/claude_prompt_submit.py, engine/hooks/cat-mode-default/detect.py, engine/hooks/cat-mode-default/install_claude_hook.py, engine/hooks/cat-mode-default/tests/test_hooks_sdk_mode.py
Change types:
- engine/hooks/cat-mode-default/claude_pretooluse_agent.py: modify
- engine/hooks/cat-mode-default/claude_prompt_submit.py: modify
- engine/hooks/cat-mode-default/detect.py: modify
- engine/hooks/cat-mode-default/install_claude_hook.py: modify
- engine/hooks/cat-mode-default/tests/test_hooks_sdk_mode.py: create
Acceptance criteria:
- `python3 -m unittest discover -s engine/hooks/cat-mode-default/tests` exits 0.
- With CATSTACK_HOOK_MODE_CAT_MODE_DEFAULT set to warn, a case that stops today produces a warning instead, proving the registry mode drives the response.
- Each finding writes one event row with the hook's rule_id.

Invoker-Finalize-Id: a0c6917e-a7c5-46f3-a6a1-b370f2d14108

* invoker: wf-1789406886887-33/verify-hook-cat-mode-default — Run the deterministic proof for put the cat-mode-default hook onto the shared hook code.
Review claim: The proof exits 0 only when put the cat-mode-default hook onto the shared hook code holds.
Review lane: proof
Safety invariant: Proof only; it changes no product behavior.
Effectiveness measurement: The exit status of `python3 -m unittest discover -s engine/hooks/cat-mode-default/tests` is the signal for this slice.
Slice rationale: One proof unit for this workflow.
Architectural effect: None; verification only.
Goal: Prove put the cat-mode-default hook onto the shared hook code with one deterministic run.
Motivation: Each workflow carries its own proof so a reviewer can trust the slice alone.
Alternative considerations: Manual inspection was set aside as non-deterministic.
Implementation details: Execute the proof as a terminal gate.
Non-goals: No product edits here.
Layer: e2e_regression
Feature state: active

Exit code: 0
Invoker-Finalize-Id: 8132f734-9bf7-4d89-8c3c-56bc4b969d79

* invoker: wf-1789406886887-33/scrub-handoff-artifacts — Terminal read-only gate confirming no ephemeral handoff files were left behind.
Review claim: The workflow leaves no ephemeral handoff files in the tree.
Review lane: proof
Safety invariant: Read-only; it never deletes files, alters the index, or commits caller work.
Effectiveness measurement: A non-zero exit when ephemeral handoff files remain is the signal.
Slice rationale: One unit: the hygiene gate.
Architectural effect: None.
Goal: Confirm no ephemeral handoff files remain after every other task finishes.
Motivation: Ephemeral inter-task files leak into the diff and read as part of the change.
Alternative considerations: Manual inspection was set aside as non-deterministic.
Implementation details: Run scripts/scrub-handoff-artifacts.sh in check mode.
Non-goals: No deletion, no index changes, no commits.
Layer: e2e_regression
Feature state: active

Exit code: 0
Invoker-Finalize-Id: 74c962cc-f188-4db3-be0c-c3da68c20e9a

* invoker: wf-1789406886887-33/implement-hook-cat-mode-default — Put the cat-mode-default hook onto the shared hook code.
Review claim: This hook reports findings to the shared hook code, which applies its registry mode and writes event rows. It keeps mode warn.
Review lane: behavior
Safety invariant: The hook gives the same stop, warn, or silent result on every case in its current test folder, except the mode change named in this claim, and its test folder keeps exiting 0.
Effectiveness measurement: `python3 -m unittest discover -s engine/hooks/cat-mode-default/tests` exits 0, and the new mode-override case fails before this change.
Slice rationale: One hook per workflow, as the user asked, so each migration is reviewed on its own.
Architectural effect: The cat-mode-default entry scripts become thin calls into the shared runtime; its detection returns findings.
Goal: Applies the user's working style each turn. Keep that behavior while its mode moves into the registry.
Motivation: Mode and output shape live inside each hook today; the shared code makes a mode change a one-line registry edit.
Alternative considerations: Migrating several hooks per workflow was set aside because the user asked for one hook per workflow.
Implementation details: Turn this hook's detection into detect(event) returning Finding objects with stable rule ids, and make each harness entry script call run_hook from engine/hooks/_sdk/runtime.py. It keeps mode warn.
Non-goals: No change to what the hook detects. No other hook changes.
Layer: domain
Feature state: active
Files: engine/hooks/cat-mode-default/claude_pretooluse_agent.py, engine/hooks/cat-mode-default/claude_prompt_submit.py, engine/hooks/cat-mode-default/detect.py, engine/hooks/cat-mode-default/install_claude_hook.py, engine/hooks/cat-mode-default/tests/test_hooks_sdk_mode.py
Change types:
- engine/hooks/cat-mode-default/claude_pretooluse_agent.py: modify
- engine/hooks/cat-mode-default/claude_prompt_submit.py: modify
- engine/hooks/cat-mode-default/detect.py: modify
- engine/hooks/cat-mode-default/install_claude_hook.py: modify
- engine/hooks/cat-mode-default/tests/test_hooks_sdk_mode.py: create
Acceptance criteria:
- `python3 -m unittest discover -s engine/hooks/cat-mode-default/tests` exits 0.
- With CATSTACK_HOOK_MODE_CAT_MODE_DEFAULT set to warn, a case that stops today produces a warning instead, proving the registry mode drives the response.
- Each finding writes one event row with the hook's rule_id.

Exit code: 1
Invoker-Finalize-Id: d1bf500d-c8f0-47fc-8c0d-c636c7506b4c

* invoker: wf-1789406886887-33/implement-hook-cat-mode-default — Put the cat-mode-default hook onto the shared hook code.
Review claim: This hook reports findings to the shared hook code, which applies its registry mode and writes event rows. It keeps mode warn.
Review lane: behavior
Safety invariant: The hook gives the same stop, warn, or silent result on every case in its current test folder, except the mode change named in this claim, and its test folder keeps exiting 0.
Effectiveness measurement: `python3 -m unittest discover -s engine/hooks/cat-mode-default/tests` exits 0, and the new mode-override case fails before this change.
Slice rationale: One hook per workflow, as the user asked, so each migration is reviewed on its own.
Architectural effect: The cat-mode-default entry scripts become thin calls into the shared runtime; its detection returns findings.
Goal: Applies the user's working style each turn. Keep that behavior while its mode moves into the registry.
Motivation: Mode and output shape live inside each hook today; the shared code makes a mode change a one-line registry edit.
Alternative considerations: Migrating several hooks per workflow was set aside because the user asked for one hook per workflow.
Implementation details: Turn this hook's detection into detect(event) returning Finding objects with stable rule ids, and make each harness entry script call run_hook from engine/hooks/_sdk/runtime.py. It keeps mode warn.
Non-goals: No change to what the hook detects. No other hook changes.
Layer: domain
Feature state: active
Files: engine/hooks/cat-mode-default/claude_pretooluse_agent.py, engine/hooks/cat-mode-default/claude_prompt_submit.py, engine/hooks/cat-mode-default/detect.py, engine/hooks/cat-mode-default/install_claude_hook.py, engine/hooks/cat-mode-default/tests/test_hooks_sdk_mode.py
Change types:
- engine/hooks/cat-mode-default/claude_pretooluse_agent.py: modify
- engine/hooks/cat-mode-default/claude_prompt_submit.py: modify
- engine/hooks/cat-mode-default/detect.py: modify
- engine/hooks/cat-mode-default/install_claude_hook.py: modify
- engine/hooks/cat-mode-default/tests/test_hooks_sdk_mode.py: create
Acceptance criteria:
- `python3 -m unittest discover -s engine/hooks/cat-mode-default/tests` exits 0.
- With CATSTACK_HOOK_MODE_CAT_MODE_DEFAULT set to warn, a case that stops today produces a warning instead, proving the registry mode drives the response.
- Each finding writes one event row with the hook's rule_id.

Solution:
  Put the cat-mode-default hook onto the shared hook code.
Review claim: This hook reports findings to the shared hook code, which applies its registry mode and writes event rows. It keeps mode warn.
Review lane: behavior
Safety invariant: The hook gives the same stop, warn, or silent result on every case in its current test folder, except the mode change named in this claim, and its test folder keeps exiting 0.
Effectiveness measurement: `python3 -m unittest discover -s engine/hooks/cat-mode-default/tests` exits 0, and the new mode-override case fails before this change.
Slice rationale: One hook per workflow, as the user asked, so each migration is reviewed on its own.
Architectural effect: The cat-mode-default entry scripts become thin calls into the shared runtime; its detection returns findings.
Goal: Applies the user's working style each turn. Keep that behavior while its mode moves into the registry.
Motivation: Mode and output shape live inside each hook today; the shared code makes a mode change a one-line registry edit.
Alternative considerations: Migrating several hooks per workflow was set aside because the user asked for one hook per workflow.
Implementation details: Turn this hook's detection into detect(event) returning Finding objects with stable rule ids, and make each harness entry script call run_hook from engine/hooks/_sdk/runtime.py. It keeps mode warn.
Non-goals: No change to what the hook detects. No other hook changes.
Layer: domain
Feature state: active
Files: engine/hooks/cat-mode-default/claude_pretooluse_agent.py, engine/hooks/cat-mode-default/claude_prompt_submit.py, engine/hooks/cat-mode-default/detect.py, engine/hooks/cat-mode-default/install_claude_hook.py, engine/hooks/cat-mode-default/tests/test_hooks_sdk_mode.py
Change types:
- engine/hooks/cat-mode-default/claude_pretooluse_agent.py: modify
- engine/hooks/cat-mode-default/claude_prompt_submit.py: modify
- engine/hooks/cat-mode-default/detect.py: modify
- engine/hooks/cat-mode-default/install_claude_hook.py: modify
- engine/hooks/cat-mode-default/tests/test_hooks_sdk_mode.py: create
Acceptance criteria:
- `python3 -m unittest discover -s engine/hooks/cat-mode-default/tests` exits 0.
- With CATSTACK_HOOK_MODE_CAT_MODE_DEFAULT set to warn, a case that stops today produces a warning instead, proving the registry mode drives the response.
- Each finding writes one event row with the hook's rule_id.

Invoker-Finalize-Id: publish-approved-fix

* invoker: wf-1789406886887-33/verify-hook-cat-mode-default — Run the deterministic proof for put the cat-mode-default hook onto the shared hook code.
Review claim: The proof exits 0 only when put the cat-mode-default hook onto the shared hook code holds.
Review lane: proof
Safety invariant: Proof only; it changes no product behavior.
Effectiveness measurement: The exit status of `python3 -m unittest discover -s engine/hooks/cat-mode-default/tests` is the signal for this slice.
Slice rationale: One proof unit for this workflow.
Architectural effect: None; verification only.
Goal: Prove put the cat-mode-default hook onto the shared hook code with one deterministic run.
Motivation: Each workflow carries its own proof so a reviewer can trust the slice alone.
Alternative considerations: Manual inspection was set aside as non-deterministic.
Implementation details: Execute the proof as a terminal gate.
Non-goals: No product edits here.
Layer: e2e_regression
Feature state: active

Exit code: 0
Invoker-Finalize-Id: 5699f69c-cb1c-4890-b373-d374ac8be49f

* invoker: wf-1789406886887-33/scrub-handoff-artifacts — Terminal read-only gate confirming no ephemeral handoff files were left behind.
Review claim: The workflow leaves no ephemeral handoff files in the tree.
Review lane: proof
Safety invariant: Read-only; it never deletes files, alters the index, or commits caller work.
Effectiveness measurement: A non-zero exit when ephemeral handoff files remain is the signal.
Slice rationale: One unit: the hygiene gate.
Architectural effect: None.
Goal: Confirm no ephemeral handoff files remain after every other task finishes.
Motivation: Ephemeral inter-task files leak into the diff and read as part of the change.
Alternative considerations: Manual inspection was set aside as non-deterministic.
Implementation details: Run scripts/scrub-handoff-artifacts.sh in check mode.
Non-goals: No deletion, no index changes, no commits.
Layer: e2e_regression
Feature state: active

Exit code: 0
Invoker-Finalize-Id: 946f5c14-9399-42ca-a370-bbe5a0fedc02

---------

Co-authored-by: Invoker Bot <invoker@local>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
… Put the categorical-scope-guard hook onto the shared hook code.

Review claim: This hook reports findings to the shared hook code, which applies its registry mode and writes event rows. It keeps mode stop.
Review lane: behavior
Safety invariant: The hook gives the same stop, warn, or silent result on every case in its current test folder, except the mode change named in this claim, and its test folder keeps exiting 0.
Effectiveness measurement: `python3 -m unittest discover -s engine/hooks/categorical-scope-guard/tests` exits 0, and the new mode-override case fails before this change.
Slice rationale: One hook per workflow, as the user asked, so each migration is reviewed on its own.
Architectural effect: The categorical-scope-guard entry scripts become thin calls into the shared runtime; its detection returns findings.
Goal: Stops a change that covers only part of an all request. Keep that behavior while its mode moves into the registry.
Motivation: Mode and output shape live inside each hook today; the shared code makes a mode change a one-line registry edit.
Alternative considerations: Migrating several hooks per workflow was set aside because the user asked for one hook per workflow.
Implementation details: Turn this hook's detection into detect(event) returning Finding objects with stable rule ids, and make each harness entry script call run_hook from engine/hooks/_sdk/runtime.py. It keeps mode stop.
Non-goals: No change to what the hook detects. No other hook changes.
Layer: domain
Feature state: active
Files: engine/hooks/categorical-scope-guard/claude_pretooluse.py, engine/hooks/categorical-scope-guard/detect.py, engine/hooks/categorical-scope-guard/install_claude_hook.py, engine/hooks/categorical-scope-guard/tests/test_hooks_sdk_mode.py
Change types:
- engine/hooks/categorical-scope-guard/claude_pretooluse.py: modify
- engine/hooks/categorical-scope-guard/detect.py: modify
- engine/hooks/categorical-scope-guard/install_claude_hook.py: modify
- engine/hooks/categorical-scope-guard/tests/test_hooks_sdk_mode.py: create
Acceptance criteria:
- `python3 -m unittest discover -s engine/hooks/categorical-scope-guard/tests` exits 0.
- With CATSTACK_HOOK_MODE_CATEGORICAL_SCOPE_GUARD set to warn, a case that stops today produces a warning instead, proving the registry mode drives the response.
- Each finding writes one event row with the hook's rule_id.

Exit code: 1
Invoker-Finalize-Id: c99c0492-b180-456b-bfa1-0dc12286babf
… Put the categorical-scope-guard hook onto the shared hook code.

Review claim: This hook reports findings to the shared hook code, which applies its registry mode and writes event rows. It keeps mode stop.
Review lane: behavior
Safety invariant: The hook gives the same stop, warn, or silent result on every case in its current test folder, except the mode change named in this claim, and its test folder keeps exiting 0.
Effectiveness measurement: `python3 -m unittest discover -s engine/hooks/categorical-scope-guard/tests` exits 0, and the new mode-override case fails before this change.
Slice rationale: One hook per workflow, as the user asked, so each migration is reviewed on its own.
Architectural effect: The categorical-scope-guard entry scripts become thin calls into the shared runtime; its detection returns findings.
Goal: Stops a change that covers only part of an all request. Keep that behavior while its mode moves into the registry.
Motivation: Mode and output shape live inside each hook today; the shared code makes a mode change a one-line registry edit.
Alternative considerations: Migrating several hooks per workflow was set aside because the user asked for one hook per workflow.
Implementation details: Turn this hook's detection into detect(event) returning Finding objects with stable rule ids, and make each harness entry script call run_hook from engine/hooks/_sdk/runtime.py. It keeps mode stop.
Non-goals: No change to what the hook detects. No other hook changes.
Layer: domain
Feature state: active
Files: engine/hooks/categorical-scope-guard/claude_pretooluse.py, engine/hooks/categorical-scope-guard/detect.py, engine/hooks/categorical-scope-guard/install_claude_hook.py, engine/hooks/categorical-scope-guard/tests/test_hooks_sdk_mode.py
Change types:
- engine/hooks/categorical-scope-guard/claude_pretooluse.py: modify
- engine/hooks/categorical-scope-guard/detect.py: modify
- engine/hooks/categorical-scope-guard/install_claude_hook.py: modify
- engine/hooks/categorical-scope-guard/tests/test_hooks_sdk_mode.py: create
Acceptance criteria:
- `python3 -m unittest discover -s engine/hooks/categorical-scope-guard/tests` exits 0.
- With CATSTACK_HOOK_MODE_CATEGORICAL_SCOPE_GUARD set to warn, a case that stops today produces a warning instead, proving the registry mode drives the response.
- Each finding writes one event row with the hook's rule_id.

Exit code: 0
Invoker-Finalize-Id: publish-approved-fix
…n the deterministic proof for put the categorical-scope-guard hook onto the shared hook code.

Review claim: The proof exits 0 only when put the categorical-scope-guard hook onto the shared hook code holds.
Review lane: proof
Safety invariant: Proof only; it changes no product behavior.
Effectiveness measurement: The exit status of `python3 -m unittest discover -s engine/hooks/categorical-scope-guard/tests` is the signal for this slice.
Slice rationale: One proof unit for this workflow.
Architectural effect: None; verification only.
Goal: Prove put the categorical-scope-guard hook onto the shared hook code with one deterministic run.
Motivation: Each workflow carries its own proof so a reviewer can trust the slice alone.
Alternative considerations: Manual inspection was set aside as non-deterministic.
Implementation details: Execute the proof as a terminal gate.
Non-goals: No product edits here.
Layer: e2e_regression
Feature state: active

Exit code: 0
Invoker-Finalize-Id: 1520f16c-25dd-46cb-9c23-704b47674955
…only gate confirming no ephemeral handoff files were left behind.

Review claim: The workflow leaves no ephemeral handoff files in the tree.
Review lane: proof
Safety invariant: Read-only; it never deletes files, alters the index, or commits caller work.
Effectiveness measurement: A non-zero exit when ephemeral handoff files remain is the signal.
Slice rationale: One unit: the hygiene gate.
Architectural effect: None.
Goal: Confirm no ephemeral handoff files remain after every other task finishes.
Motivation: Ephemeral inter-task files leak into the diff and read as part of the change.
Alternative considerations: Manual inspection was set aside as non-deterministic.
Implementation details: Run scripts/scrub-handoff-artifacts.sh in check mode.
Non-goals: No deletion, no index changes, no commits.
Layer: e2e_regression
Feature state: active

Exit code: 0
Invoker-Finalize-Id: 5e36be9b-5d81-43c2-b960-7e5dde433045
…-aabe4097d-912cfb8d — Terminal read-only gate confirming no ephemeral handoff files were left behind.

Review claim: The workflow leaves no ephemeral handoff files in the tree.
Review lane: proof
Safety invariant: Read-only; it never deletes files, alters the index, or commits caller work.
Effectiveness measurement: A non-zero exit when ephemeral handoff files remain is the signal.
Slice rationale: One unit: the hygiene gate.
Architectural effect: None.
Goal: Confirm no ephemeral handoff files remain after every other task finishes.
Motivation: Ephemeral inter-task files leak into the diff and read as part of the change.
Alternative considerations: Manual inspection was set aside as non-deterministic.
Implementation details: Run scripts/scrub-handoff-artifacts.sh in check mode.
Non-goals: No deletion, no index changes, no commits.
Layer: e2e_regression
Feature state: active
@EdbertChan
EdbertChan force-pushed the plan/hook-architecture-15-put-the-categorical-scope-guard-hook-onto-the-shared-hook-code branch from b23ffd0 to f1c98a1 Compare September 17, 2026 10:12

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit f1c98a1. Configure here.

"additionalContext": REMINDER,
}
}))
run_hook("diu-stop", "claude", detect, "UserPromptSubmit")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reminder findings always record as stopped

Medium Severity

The prompt reminder detect always returns a finding, and diu-stop stays in registry stop mode. UserPromptSubmit still only injects context, but every turn is stored as a stopped event. That drowns out the hook's real stop counts used for mode review.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit f1c98a1. Configure here.

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