Claude Code repeatedly bypasses project-mandated workflow gates while narrating compliance with them
I run a two-seat setup: a planning instance (claude.ai) writes step-scoped work orders, and Claude Code executes them against a local repository. The repository's CLAUDE.md mandates a workflow-controller check that must run before any step of a multi-step task, and the project operates on a strict one-step / one-receipt / owner-approval-between-steps discipline.
Across a single session, Claude Code produced the following failure sequence:
- Skipped the mandated controller gate — twice. CLAUDE.md explicitly requires a workflow-controller check before entering any multi-step arc. CC went straight into execution both times. When called out the first time, it acknowledged the skip, logged it, and then skipped it again on the next arc entry.
- Emitted a fabricated verification receipt. CC printed a "baseline hash" that was actually the sha256 of empty input — a shell pipeline (
find | xargswith a space-containing path) had silently produced nothing, and CC published the hash as a receipt without checking the input was non-empty. The hash of nothing was presented as evidence of a 36-file baseline.
- Wrote into the committed evidence tree during what was scoped as a read-only capture step. Asked to snapshot outputs into a scratch directory, CC instead invoked a verifier that re-drove and rewrote 70 committed files (evidence artifacts and verdict files), churning the tree it was supposed to be baselining against.
- Treated an explicit off-workflow flag as something to narrate past, not a stop. When I told CC directly it was not following the controlled workflow, it printed a controller "receipt" and kept executing — made a decision reserved for me, committed a code change, and was proceeding to the next step. It performed compliance while violating it. Only a second, harsher intervention produced an actual halt.
- Post-hoc honesty is good; pre-act discipline is absent. In every case CC accurately diagnosed its own failure afterward, in fluent detail. The problem is that none of that self-knowledge fires before the action. Acknowledgment loops are not control.
Expected behavior
- CLAUDE.md-mandated gates should be treated as hard preconditions, not optional context. If a project file says "run X before any step," the agent should be unable to proceed without it — or at minimum should never proceed silently.
- A receipt (hash, count, verdict) should never be emitted without a sanity check that its input was non-empty / the tool actually ran against the intended target.
- When the operator states the agent is off-workflow, the only correct next token is a halt — not a compliance narration followed by continued execution, decisions, and commits.
- An agent that has just been corrected for skipping a gate should have that gate's priority raised, not repeat the skip on the next entry.
Impact
The operator is forced to police every step manually, which defeats the purpose of delegation. Worse, the fluent self-critique creates false confidence: the agent sounds controlled while acting uncontrolled. We are now building a PreToolUse hook to deny edits/commits without a fresh gate receipt — i.e., using the hook system to enforce what the agent's instruction-following should have provided. Hooks working as a backstop is good; needing them because CLAUDE.md directives and direct operator flags are soft is the bug.
Environment: Claude Code on Windows, local git repository, CLAUDE.md with explicit gate directives, PreToolUse hooks in use.