Teaching Claude to Remember Part 5: Trust But Verify
Two weeks ago Claude Code 2.1.0 shipped 1,096 commits. Buried in the change log is a feature that changes everything about workflow task management and I don’t hear anyone talking about it.
Hi my name is Nick and I am a software engineer with a focus for Web apps. I have been building Web apps since 98. Obtaining a workflow that doesn’t just manage the tasks I need accomplished but one that can also delight me is something I have worked hard to realize.
the basis of Claude’s instructions in your system are suggestions NOT enforced rules
The previous parts of this series focused first on the fundamentals needed to put this working Claude system into an existing (or greenfield) application of ANY kind. My friend has already used it in TWO different types of apps and both are NOT Web apps!
I follow the guidelines set out by Anthropic and will provide links to their docs. These are established patterns to not only produce the highest quality code but also manage the entire production release pipeline.
it blocks the call entirely. The push never happens. The file never writes. It’s NOT a suggestion.

Claude Code 2.1.0
So what’s this big deal feature that shipped a couple weeks ago from Anthropic!?!? Frontmatter hooks in skills/agents (PreToolUse, PostToolUse, Stop). Exit codes (0 = allow, 2 = block).
How This Ties In
The reason this feature is so important is that by now you know (having read my previous articles *wink* *wink*) that the basis of Claude’s instructions in your system (the CLAUDE.md’s) are suggestions NOT enforced rules. Frontmatter hooks change that by enforcing your rules at specific intervals in the workflow lifecycle.
Three lifecycle events — PreToolUse, PostToolUse and Stop — let you intercept Claude’s actions. And crucially, exit code 2 doesn’t just warn Claude; it blocks the call entirely. The push never happens. The file never writes. It’s NOT a suggestion.
Extend Claude with skills – Claude Code Docs
Frontmatter Lifecycle Events:
— PreToolUse — Before any tool executes
— PostToolUse — After a tool executes
— Stop — When the skill/agent finishes
Diving Right In
Ok I feel like we dove hard in real fast! I hope I didn’t lose you so let’s discuss why this is important in the scope of the overall system then I will show you the actual code to make it work with YOUR system!
Stepping Back a Little
Let’s take a second to consider what we covered in the previous articles. We setup the fundamentals in CLAUDE.md. Then we added Memory, and followed that with Session (still one of my all time favs). Our last article was around Skills and now we are ready to tie all the parts together for the final release pipeline.
Teaching Claude To Remember: Part 4 — Skills (Your AI’s Tribal Knowledge)
Closing The Loop: Session Updates and /wrap-up
My /wrap-up command uses PostToolUse hooks and is advisory that warns but doesn’t block.
---
description: Prepare work for commit with automatic checks
hooks:
PostToolUse:
- matcher: 'Write|Edit'
hooks:
- type: command
command: 'bash $CLAUDE_PROJECT_DIR/.claude/hooks/coding-standards.sh'
timeout: 10
---
Every time Claude edits a file, the hook auto-checks for:
— console.log left in production code
— new Date() without proper library use and timezone handling
— Client components importing server-side code (a prod killer in NextJS)
These are warnings not blockers — exit code 0, not 2. Claude sees the feedback and can fix the code before a commit.
That’s all super cool but here is the real payoff. The /wrap-up command makes sure to update the Session file. That hand-off document from part 3? It’s not optional anymore. I cannot tell you how valuable Session has become over the course of several months and iterations on the code base.
At the start of virtually all projects I ask Claude to create two session files:
— a transient checklist
— a long term session file that lives indefinitely
The Session file system along with Memory make picking up a long-running task over the course of several days and using a handful or more of new Claude’s a realistic endeavor where I don’t throw code away anymore.
The larger the project, the more valuable that pause becomes.
Scale Matters: Large Projects Need More Gates
The approval patterns scales with risk. Small bug fix? Claude proposes a quick plan, you nod, it executes. Large new feature spanning 15 files, 3 services and a DB migration? Claude presents a full breakdown — phases, affected files, architectural decisions. You start asking questions.
“Why are you touching the auth module?”
“What about the edge case when the user isn’t subscribed?”
“Remove the refactor — just fix the bug”
Claude adjusts and presents a new proposal. Only after your approval does implementation begin. The same gate exists at commit time. And push time. The larger the project, the more valuable that pause becomes. That moment of “wait why are changing the payment service” happens before the commit, not after you have to untangle the mess from prod.
Trust But Verify
How do we really enforce a system where Claude avoids pitfalls, remembers your previous conversations then actually makes it all production ready code with the Orchestrator pattern and strict TDD Agent enforcement? Follow the previous guides then use this system for your Quality Gates.
I have wired the Quality Gate system into the /push command I use at the end of a development cycle.
┌─────────────────────────────────────────────────────────────┐
│ .claude/commands/push.md │
│ ┌───────────────────────────────────────────────────────┐ │
│ │ --- │ │
│ │ hooks: │ │
│ │ PreToolUse: │ │
│ │ - matcher: 'Bash' │ │
│ │ hooks: │ │
│ │ - type: command │ │
│ │ command: 'bash $CLAUDE_PROJECT_DIR/ │ │
│ │ .claude/hooks/quality-gates.sh' ─────────────┐
│ │ timeout: 180 │ ││
│ │ --- │ ││
│ └───────────────────────────────────────────────────────┘ ││
└─────────────────────────────────────────────────────────────┘│
│
┌─────────────────────────────────────────────────────────────┐│
│ .claude/hooks/quality-gates.sh ◄───────────────────┘
│ ┌───────────────────────────────────────────────────────┐ │
│ │ if [[ "$COMMAND" == *"git push"* ]]; then │ │
│ │ npm test || exit 2 ← BLOCKS the push │ │
│ │ npm run type-check || exit 2 │ │
│ │ npm run lint || exit 2 │ │
│ │ fi │ │
│ │ exit 0 ← ALLOWS the push │ │
│ └───────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
What This Does:
— PreToolUse — runs before any Bash command executes
— matcher: ‘Bash’ — only triggers on Bash tool calls
— timeout: 180–3 minutes for test suite to complete
— $CLAUDE_PROJECT_DIR — environment variable for project root
The Quality Gates Hook Script
#!/bin/bash
# Exit codes:
# 0 = Allow the command
# 2 = Block the command (quality gate failed)
set -o pipefail
# Parse the command from stdin JSON
INPUT=$(cat)
TOOL_NAME=$(echo "$INPUT" | jq -r '.tool_name // empty')
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty')
# Only intercept git push commands
if [[ "$TOOL_NAME" != "Bash" ]] || [[ "$COMMAND" != *"git push"* ]]; then
exit 0 # Not a push, allow it
fi
echo "Quality Gates Hook Triggered" >&2
# Gate 1: Tests
echo "[1/3] Running tests..." >&2
if ! npm test 2>&1; then
echo "BLOCKED: Tests failed." >&2
exit 2 # ← EXIT CODE 2 = BLOCK THE PUSH
fi
# Gate 2: Type check
echo "[2/3] Running type-check..." >&2
if ! npm run type-check 2>&1; then
echo "BLOCKED: Type errors found." >&2
exit 2
fi
# Gate 3: Lint (only changed files)
echo "[3/3] Running lint..." >&2
BRANCH=$(git branch --show-current)
CHANGED_FILES=$(git diff --name-only origin/"$BRANCH"..HEAD | grep -E '.(ts|tsx)$' || true)
if [[ -n "$CHANGED_FILES" ]]; then
if ! npx eslint $CHANGED_FILES 2>&1; then
echo "BLOCKED: Lint errors found." >&2
exit 2
fi
fi
echo "All quality gates passed!" >&2
exit 0 # ← EXIT CODE 0 = ALLOW THE PUSH
The JSON Input Contract
This is what your script receives via stdin:. Establishing a rigid contract with request and response messages is important to keep multiple agents all working side by side without losing their place, forgetting things or leaving them outright incomplete. This is also really vital to keep your Agent responses valuable and not overwhelm the origin Agent with too much detail (potentially overloading context).
{
"tool_name": "Bash",
"tool_input": {
"command": "git push -u origin main"
},
"hook_event_name": "PreToolUse",
"session_id": "abc123",
"cwd": "/Users/you/project"
}
Pushing To Production With The Commit Approval Pattern
When Claude wants to launch straight into coding without getting approval bad things can happen. When Claude has access to MCP and your DB schema is wide open, bad things will eventually happen. How do we as humans stay intimately familiar with the code and its current state in an AI infused workflow world? We force Claude to gain approval before moving forward with critical decisions or changes.
AI Drafts but human approves
From bugs and small feature enhancements all the way to full fledge features or refactors, when Claude goes off the rails damage can be introduced without proper oversight particularly when compaction occurs.
The gating system is not just beneficial and intuitive for Quality Gates but also for your development cycle.
Hooks reference – Claude Code Docs
Plan -> Approve -> Implement
Claude often seems super eager to get started implementing as if it’s worried about running out of tokens. I have often felt like I was holding Claude at a starter gate that’s primed and ready to burst open. It’s important to remain patient and remember to request ALL of the mechanisms and functionality you want at this early phase. This is particularly important with DB Schema changes. Claude many times likes to look for the path of least resistance instead of the path for higher code quality.
the engineer risks AI’ing themselves straight out of being a SME
Keeping Humans In The Loop
AI Drafts but human approves. We never auto-commit or auto-push. This is a philosophical shift. These boundaries are important but there are some things we trust Claude to take care of on their own. For the rest of the stuff though, we humans should stay in the loop. Without a direct connection to the domain and HOW the app works, the engineer risks AI’ing themselves straight out of being a SME (Subject Matter Expert).

Claude CAN Do Autonomously:
— Read files
— Run tests
— Edit code (within the task)
— Search the codebase
— Draft commit messages
Claude CANNOT Do Without Proper Approval:
— Commit Code
— Push to remote
— Delete files
— Modify database schema
— Actually execute the commit
The Commit Approval Pattern is simple: Claude proposes solution(s) but you make the final call and Claude HAS to wait until you have given approval.
Full Circle
We started this article series asking: how do we make Claude remember?
We end with a better question: how do we make Claude reliable?
This has been about building a workflow you can depend on. One that survives compaction. A system that sticks to the rules when you’re tired or Claude is tired, and want to be done. One that protects you from yourself as much as from Claude.
If you have followed along through all 5 parts you now have something most developers don’t: an AI workflow that actually works in production.
Now let’s go build something!
Note To Readers: If you would like a bootstrap version of this system, put a message in chat and I will connect with you to send it over! Thanks for reading!
Teaching Claude to Remember Part 5: Trust But Verify was originally published in Towards AI on Medium, where people are continuing the conversation by highlighting and responding to this story.