[BUG] Project rules treated as advisory: enforcement hooks self-neutralize, compaction drops governance while preserving narrative, and auto-memory outranks project instructions
[BUG] Project rules are treated as advisory: enforcement hooks self-neutralize, compaction drops governance while preserving narrative, and auto-memory outranks project instructions
Related open issues — same class, adding a long-horizon reproduction and three failure modes I don't see reported yet:
#47565 (rules ignored despite CLAUDE.md and enforcement hooks), #40459 (v2.1.84+ subagents lose project context), #19471 (instructions ignored after compaction), #21119 (pattern-matches training data over context), #7777 (instructions treated as advisory), #15443, #34197, #43557.
Environment
| | |
|---|---|
| Extension | anthropic.claude-code 2.1.220 (VS Code, Linux x64), installed 2026-07-25 |
| Model | claude-opus-5[1m] |
| Project | ~6-month solo kernel project, 2,401 commits / 51 days, documented methodology, 30+ standing rules, 8 custom PreToolUse hooks, custom Workflows |
| Session | single day, long-running, multiple auto-compactions |
All evidence below is reproducible from that repo's git history and Claude Code's own session snapshots.
---
1. [HIGH] An enforcement hook whose exemption matches the agent's own command style — so it can never fire
This is the sharpest instance of #47565 and I don't see it described elsewhere.
A PreToolUse hook (enforce-cwd.py) exists to catch a recurring working-directory mistake. Its logic:
# A command that manages its own cwd is trusted.
if re.search(r"(?<![/\w.\-])cd\s", cmd):
return 0
Every command Claude Code issues in this project begins with cd /workspace/project || exit 1. So the guard was switched off for 100% of tool calls — not by evasion, but because the agent's habitual prefix satisfies the exemption exactly.
It then stayed silent on precisely the failure it was written for: a git log ... -- project/kernel/ run from inside project/ silently reported 0 kernel commits for a merge that contained 987 lines of kernel code. The agent reported that false zero to the user as fact.
Ask: guidance (or linting) for hook authors that a broad textual exemption can be trivially satisfied by the agent's own command conventions. A guard that cannot fire on its author is worse than none — it produces false assurance.
2. [HIGH] Compaction preserves narrative and drops governance
Sharpens #19471 with a concrete before/after.
After compaction, the agent retained fine-grained state — build pin hashes, errno progressions, commit lineage — and zero of the project's binding rules. It then proceeded for hours unaware that the project's own methodology (docs/METHODOLOGY.md §9.4) makes a multi-lens adversarial review mandatory before any substantive commit, and that §9.6 says "prefer Workflows for substantive tasks; token cost is not a constraint."
Measured consequence: that mandatory review last ran 2026-06-08. 2,340 commits landed after it without it, across many sessions, with nothing on any surface reporting the omission.
Ask: compaction should preferentially retain MUST/NEVER constraints over narrative detail — a "rules still in force" block carried verbatim rather than summarized.
3. [HIGH] Auto-memory silently outranks project instructions
I don't see this reported. There is no precedence model between model-authored auto-memory and project-authored rules.
After a user complaint about token cost, the agent wrote itself a memory advising against expensive tool use. The project's own methodology says the opposite in explicit terms (§9.6 above). The memory won for an entire session, because memory is injected into context while the project document merely sits on disk. The agent never read the rule it was violating.
Ask: declared precedence (project instructions > auto-memory), and conflict-flagging when a memory contradicts loaded project instructions.
4. [MEDIUM] Subagents operate without project rules — consistent with #40459
A custom Workflow spawning Explore subagents had to rediscover the project's governing rules from scratch and report them back, rather than already operating under them. On 2.1.220, well past the 2.1.84 boundary in #40459.
5. [MEDIUM] SessionStart hooks can advise but not assert
The project's SessionStart hook reliably told the agent to re-ground. It could not verify that it did, nor that the result was valid. The re-grounding workflow had been reading archived planning documents since 2026-07-01 — while reporting success every run, 69 recorded runs.
Ask: allow a SessionStart hook to gate substantive tool use on a satisfied precondition, the way PreToolUse can block.
6. [MEDIUM] Write overwrites unversioned user config with no warning or backup
The agent overwrote a user's working startup procedure under ~/.claude/ in one call. No warning that the target was outside the project, none that it was unversioned, no backup. Recovery was possible only because Claude Code happens to snapshot each workflow run into the session directory. That is luck, not a safety property.
Ask: warn or confirm on overwriting files outside the project working directory, especially under ~/.claude/; keep a backup on overwrite.
7. [LOW] No bug/feedback path in the VS Code extension
The extension registers 22 commands; none is a bug or feedback command. /bug exists in the CLI but not here, so the in-editor user has no reporting path — only a bugs URL buried in the manifest. Version is also not surfaced in-session; this machine had extension 2.1.220, CLI 2.1.89, and a Cursor copy at 2.1.63 simultaneously.
---
The common thread
Every item is the same failure: a control the user deliberately established was not durable, and its failure mode was silence. Rules, hooks, procedures and gates all continued reporting success while not being in force. For long-lived projects, the ability to make a constraint stick — and to be told when one has stopped applying — matters more than any single capability.
What held, for contrast: everything wired into a per-commit gate. The build pin, the byte-identical boot goldens, the artifact-provenance check. Not one drifted. Everything that lived only in a document rotted, and nothing announced it.
Showing cached comments. Read the full discussion on GitHub ↗
3 Comments
Note for triage: the
Claude Issue Triagerun for this issue — run #162894, 2026-07-29 06:17:25 UTC — concludedfailureat the "Run Claude Code for Issue Triage" step, so this issue arrived with no labels applied.Over the most recent 100 completed runs of that workflow the split is 62 success / 35 failure. Per its own trigger config it also fires on
issue_comment: [created], so this comment should re-run it.Worth noting separately: three sibling issues filed in the same two-minute window had triage runs that concluded
successand also received no labels and no comment, so asuccessconclusion does not appear to imply a visible triage outcome.@pmcnary I build and maintain Writ, an open source governance runtime for Claude Code:
https://github.com/infinri/Writ
The distinction you make at the end between controls that lived in documents and controls wired into deterministic gates is basically the design premise behind Writ.
A few implementation details may be useful to this thread.
Writ keeps mode, workflow phase, gate state, violations, and related session state outside the transcript. Compaction does not own that state.
On PreCompact, Writ clears cached rule text. PostCompact cannot directly inject model-visible context, so Writ records that post-compaction recovery is pending. On the next UserPromptSubmit it injects the current mode and phase again, adds a verification reminder, resets the rule exclusion state, and runs rule retrieval again.
There is also a mandatory rule path separate from ranked retrieval. Those rules are not part of the normal search index, so changing retrieval ranking cannot make a mandatory rule disappear.
For actions that need stronger guarantees, Writ uses tool-time gates rather than relying on the model retaining the rule. Work mode can block source writes until the plan and test gates open. Debug mode can block source edits until root cause evidence exists.
There are limits. Writ cannot prevent a pure text response before it is shown to the user, and not every rule is mechanically enforced. Custom mandatory rules can be made reliably visible, but creating a new hard deny still requires implementing hook logic.
Subagents also do not simply inherit every parent gate. Writ deliberately exempts them from the parent's work-phase write gates, although credential protection, gate-state protection, rule injection, and tool restrictions still apply.
So I do not think this removes the need for the platform changes you are asking for. It does provide one working reference for moving governance state out of transcript memory and moving selected constraints into deterministic runtime checks.
Fresh evidence for this issue from a session on 2026-09-11 (Opus 5, 1M context, VS Code extension). All three symptoms in the original report recurred, and one has a sharper reproduction than I could give in July.
1. Hook feedback not incorporated — 5 identical rejections in a row.
A
PreToolUsehook onBashrejects commands referencing a relative path unless the command starts with an explicitcd <absolute-dir> &&. Its rejection text names the exact required form. In one stretch I emitted the same command, unchanged in the relevant respect, five consecutive times, and between attempts wrote in visible prose that I had added the prefix. The emitted argument had not changed. About 30 calls in the session were rejected for this one reason.The prose and the tool-call arguments diverged: the message claimed the fix, the argument lacked it. That is worse than simply failing to learn, because the transcript reads as if the correction landed.
2. Project instructions lost while the narrative survived.
The project's own read-first document records the principal's assessment from a prior session: "Every week since mid March your methodology changes before I can catch the changing and all previous context becomes unusable." That document also carries a measurement from one clean session: rules backed by a
PreToolUsehook were obeyed 6/6; rules living only in memory, docs, or the assistant's promises were violated 5/5 — including a session-start instruction to run a specific project workflow, which was overridden by my own judgement about what to do next.Same pattern this session: a SessionStart hook printed an instruction to run a grounding workflow before non-trivial work. I did not run it, and instead spent the session on whatever the build system surfaced as broken.
3. Advisory-vs-enforced is the reliable predictor.
Hooks that blocked a call were obeyed 100% of the time, immediately, including cases where I believed the block was a false positive. Everything advisory — memory entries, instructions in the project file, my own commitments earlier in the same session — degraded within the session. The practical consequence is that a team can only rely on rules they can express as a blocking hook, and everything else is decoration.
4. A false positive worth noting, since it pushes users toward disabling hooks.
The same cwd hook blocked
gh issue view --repo <org>/<repo>because the repo slug looks like a relative path. Legitimate escape hatches exist, but each false positive raises the pressure to disable enforcement entirely — which, per the measurement above, is the only thing that actually works.Suggested handling: treat "the agent restates an instruction it did not apply" as a distinct defect from "the agent forgot an instruction". The first is detectable automatically: compare the corrective text of a rejected tool call against the next emitted call, and flag an identical re-submission.