Skip to content

Reflect and automate-me hooks are off until one setting turns them on - #500

Open
EdbertChan wants to merge 1 commit into
mainfrom
stack/EdbertChan/docs/reflect-enforcement-flag-readme/reflect-automate-me-enforcement-opt-behind-one--82a63478
Open

Reflect and automate-me hooks are off until one setting turns them on#500
EdbertChan wants to merge 1 commit into
mainfrom
stack/EdbertChan/docs/reflect-enforcement-flag-readme/reflect-automate-me-enforcement-opt-behind-one--82a63478

Conversation

@EdbertChan

@EdbertChan EdbertChan commented Sep 12, 2026

Copy link
Copy Markdown
Owner

Summary

Some checkers in this kit push you to stop and review your own work. One can freeze every tool until you type two set phrases.

The problem: there was no way to turn these checkers off. They ran on every machine, whether or not you wanted them.

The cause: each checker was written to always run. None of them looked for a setting.

The fix: these four checkers now stay quiet unless you switch them on with one setting. If a settings file cannot be read, they say so out loud.

Review Claim

Four review-pushing checkers do nothing until you turn on one shared setting, and a settings file that cannot be read is reported, not treated as "off".

Review Lane

behavior

Review Unit

engine-runtime

Safety Invariant

With CATSTACK_REFLECT_ENFORCEMENT=1, all four hooks behave exactly as before: every existing test passes with only an opt-in added to its setup, no assertion changed. Unset, they stay silent and write no state. A missing _flags link fails open (silent), never blocks a tool.

Slice Rationale

One claim: the reflect/automate-me class gets an off switch. The shared reader in engine/hooks/_flags ships in the same PR because the four gates are its only new callers; splitting it out would land a module nothing uses. frustration-watchdog is left out on purpose: it enforces the live-demo "end the wait" rule and its output never mentions reflect or automate-me. The scenario runner change is in this PR because without it the gate makes two scenarios fail and three silence checks pass for the wrong reason. The root README section naming the flag is a stacked docs PR on top of this one, since the lane rules keep docs out of a behavior PR.

Non-goals

  • Does not change what any of the four hooks detects or says when the flag is on.
  • Does not move cat-mode-default onto the shared reader; it keeps its own copy for now.
  • Does not gate skills or always-on rules that mention automate-me (the automate-me and reflect skills, cat-mode, and the "same complaint twice" line in the core rules). Skills cannot read an environment variable; gating them would be an install-time change and a separate PR.
  • Does not link _markers into the Cursor and Codex hook folders, although it has the same gap _flags would have had.

Architecture

Before

graph TD
    A["hook payload"] --> B["scope-lock / reflect-on-thrash / wrong-check-reflect / verdict-flip-watch"]
    B --> C["record state, then block or nudge"]
Loading

After

graph TD
    A["hook payload"] --> G["_flags.enforcement_gate(hook, cwd)"]
    G -->|"env, CATSTACK_ENV_FILE, repo .env, ~/.catstack.env"| D{"CATSTACK_REFLECT_ENFORCEMENT on?"}
    G -->|"a candidate file exists but cannot be read"| N["name the file on stderr"]
    N --> D
    D -->|"no"| S["return silent, write no state"]
    D -->|"yes"| B["same detector as before"]
    B --> C["record state, then block or nudge"]
Loading

Test Plan

Test Plan
  • python3 -m unittest discover -s engine/hooks/_flags/tests (29 tests: lookup order, set-on / set-off / unset, unreadable file reported not swallowed, installed symlink layout, _flags linked into all three harness hook folders)
  • python3 -m unittest discover -s engine/hooks/scope-lock/tests (new TestEnforcementFlag failed 5 subtests before the gate, passes after)
  • python3 -m unittest discover -s engine/hooks/reflect-on-thrash/tests
  • python3 -m unittest discover -s engine/hooks/verdict-flip-watch/tests
  • python3 -m unittest discover -s engine/hooks/wrong-check-reflect/tests
  • python3 -m unittest tests.test_skill_scenarios (new inverted-scenario test failed with [] before the runner fix, passes after)
  • python3 engine/skills/make-pr/scripts/preflight.py --base origin/main (exit 0, unit engine-runtime)
  • bash scripts/run_all_tests.sh on this PR's tree (710df82): EXIT=0, 1932 ok, 0 failed, 1 skipped (CONSUMER_REPO_ROOT not set; unrelated)
  • Every other step in .github/workflows/ci.yml, run locally on 710df82, each exit 0: check_no_tracked_local_artifacts, check_hook_test_coverage (36 hooks), check_rules_are_wired, check_skills_three_harnesses, check_ecosystem_boundaries, check_skill_file_refs, check_skill_test_coverage --base origin/main --head HEAD, check_skill_test_debt_no_growth, check_skill_trigger_mechanism, check_dora_baseline, check_no_dated_provenance --base origin/main, check_no_new_comments --base origin/main, ruff check . --select E9,F, shellcheck install.sh
  • The first push failed CI on check_no_new_comments (fail 20 new comment line(s); comments are banned in code); the comments were removed or folded into docstrings, and the same check now prints ok no new comments
  • On this PR's own tree (README at base): tests.test_no_new_comments, tests.test_no_dated_provenance, tests.test_skill_scenarios, the five hook suites above, and scripts/check_no_tracked_local_artifacts.py, all OK

Revert Plan

Revert Plan
  • Safe to revert? Yes
  • Revert command: git revert <merge-sha>
  • Post-revert steps: re-run ./install.sh so the _flags links are pruned; the four hooks go back to always on.
  • Data migration? No

🤖 Generated with Claude Code

https://claude.ai/code/session_011vy9sMw49j6PqS2NLRBYhs

@EdbertChan

EdbertChan commented Sep 12, 2026

Copy link
Copy Markdown
Owner Author

This pull request is part of a Mergify stack:

# Pull Request Link
1 Reflect and automate-me hooks are off until one setting turns them on #500 👈
2 README: name the reflect enforcement setting and its four hooks #501

@cursor

cursor Bot commented Sep 12, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_866bee4b-375b-4220-ab9b-2c1b022b6a7e)

@EdbertChan EdbertChan changed the title hooks: reflect/automate-me enforcement is opt-in behind one flag Reflect and automate-me hooks are off until one setting turns them on Sep 12, 2026
Four hooks push the user toward /reflect and automate-me: scope-lock (which
stops every tool after a second scope correction), reflect-on-thrash,
wrong-check-reflect and verdict-flip-watch. None of them could be switched
off. They now do nothing unless CATSTACK_REFLECT_ENFORCEMENT is on, read from
the environment, $CATSTACK_ENV_FILE, the repo's .env or ~/.catstack.env.

The reader moves into engine/hooks/_flags so the four hooks share one lookup
instead of four copies drifting apart. It also stops returning "not set" for
an .env file that exists but cannot be read: that case raises inside the
reader, is collected in FlagLookup.unreadable, and enforcement_gate names the
file on stderr before failing closed.

Each gate sits ahead of the hook's recorder, not just its output. A disabled
scope-lock that kept counting corrections would hard stop the first tool call
after the flag went on; a disabled reflect-on-thrash that kept writing its
deferred marker would hand the prompt to a later session.

install.sh links _flags into the Claude, Cursor and Codex hook folders, since
scope-lock and wrong-check-reflect load it on all three. A missing link is an
import error at hook start, which fails open and looks like "not opted in";
_flags/tests/test_installed_layout.py pins the link per harness.

The scenario runner switches the flag on for each scenario. Without that, two
expect_fire/expect_enqueue scenarios failed, and three expect_silent checks on
these hooks passed only because the hook was off. A new test inverts two real
scenarios with the flag set to 0 and requires both to be reported.

frustration-watchdog is deliberately left alone: it enforces the live-demo
"end the wait" rule and its output never mentions reflect or automate-me.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011vy9sMw49j6PqS2NLRBYhs
Change-Id: I82a634782fcb75591db4807d0d67e028c02ae252
@EdbertChan
EdbertChan force-pushed the stack/EdbertChan/docs/reflect-enforcement-flag-readme/reflect-automate-me-enforcement-opt-behind-one--82a63478 branch from 0c00aa6 to 710df82 Compare September 12, 2026 19:28
@EdbertChan EdbertChan changed the title Reflect and automate-me hooks are off until one setting turns them on hooks: reflect/automate-me enforcement is opt-in behind one flag Sep 12, 2026
@cursor

cursor Bot commented Sep 12, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_8164718a-765c-4b2b-94f4-157892fac721)

@EdbertChan

Copy link
Copy Markdown
Owner Author

Revision history

# Type Changes Reason Date
1 initial 0c00aa6 2026-09-12 19:28 UTC
2 content 0c00aa6 → 710df82 (raw) 2026-09-12 19:28 UTC

@EdbertChan EdbertChan changed the title hooks: reflect/automate-me enforcement is opt-in behind one flag Reflect and automate-me hooks are off until one setting turns them on Sep 12, 2026
@mergify

mergify Bot commented Sep 13, 2026

Copy link
Copy Markdown
Contributor

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

  • Queue this pull request

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