Claude Code Playbook

A field report from six months of building

Guards over reminders.

How we shipped a production-grade financial platform with Claude Code, and the guardrails that made it safe to trust.

commits since 17 Apr 2026
956
lines of TypeScript
150k
lines of tests
45k
planned phases
209
bugs fixed of 433 filed
316
lessons, 46 now guarded
80

Measured against the private repository on 22 October 2026. The product is deployed and pre-launch, with no paying clients yet. 162 database migrations, 30 guard tests, 30 decision records, 22 project skills.

The product is the evidence. The method is the argument.

The product is a B2B SaaS that handles invoices, contracts and compliance for its customers. Money is computed with exact decimals, every tenant's data is isolated by the database itself, and every financial change writes an audit row. That is a lot of rules for a small team to hold in their heads, and an AI agent holds none of them between sessions.

So we stopped relying on anyone remembering. Claude Code wrote most of the keystrokes. We wrote the rules it works under, the scripts that enforce them, and the reviews that check its work. This page is about that layer.

The one idea

A rule that lives only in a document holds until the day it matters.

Every mistake in the project goes through the same four steps, and the fourth is what makes the rest stick.

The loop: incident, lesson, guard, repeat blocked Every incident becomes a lesson logged the same turn. A lesson either names a guard that blocks the repeat, or is marked judgment and injected into every new session by a session-start hook. A judgment lesson that recurs twice becomes a build task for its guard. EVERY MISTAKE TAKES THE SAME PATH 1 Incident Something breaks, or nearly does 2 Lesson Logged the same turn, must name its guard 3 Guard Test, script or hook that fails on a repeat 4 Repeat blocked The build goes red before it ships No guard possible yet: marked "judgment" Recurs twice: build the guard Session-start hook Injects the lesson index into every new Claude session Rewording a lesson that recurred does not count as fixing it. 46 of 80 lessons are now enforced mechanically.
The promotion rule: a lesson either names the mechanical guard that now enforces it, or says "judgment" and explains why none is possible.
  1. IncidentSomething breaks, or nearly does. It gets a number in the tracker before anyone touches a file.
  2. LessonWritten into the lessons log in the same turn, with the guard named, or the word "judgment" and the reason.
  3. GuardA test, script or hook that fails loudly on a repeat. Proven red on the broken code before it is trusted.
  4. RecallA session-start hook puts the lesson index in front of every new Claude session, so even unguarded lessons are read.

The method

Nine rules, each with the incident that made it.

The reason is written next to every rule in the project constitution, so nobody tidies a rule away without knowing why it exists.

#RuleThe incident behind it
01A written constitution that every session loadsMaking the same correction in session after session
02Classify every task first: Bug, Change Request or Plan"Simple" edits to auth and schema files that were not simple
03Plans touching money, dates or concurrency carry a Boundary and Invariant TableA month-end date overflow shipped even though the plan named the risk, in a sentence
04Verification items are checkboxes, and a guard blocks shipping with one untickedA phase shipped without its approved UI while its own checklist named the miss
05Choose the cheapest model that handles what is still unknownThe top model used about half the spend on an eighth of the turns, mostly on work already specified
06Two-tier review before every ship, with a fresh-context reviewer for risky diffsTwo internal sweeps missed five bugs one independent reviewer found
07One scripted release path, and a hook that blocks the agent from committingMigrations that reached main and silently never reached production
08Six-point vetting of every tool before install, reading the code on diskAny skill or server that reads the repo can carry the code off the machine
09Tracking docs open with a summary table, linted for alignmentDetail-only trackers stop being read, and unread trackers drift
Classify every task before starting Three tiers. A Bug gets a reserved number, a fix and a regression test. A fully specified Change Request is implemented directly with a review gate. Everything else is a Plan with a plan file before any code. Schema, auth, permissions, tenancy, money, dates and concurrency are always a Plan. CLASSIFY EVERY TASK BEFORE STARTING The amount of process matches how much is still undecided, not how big the change looks. BUG-N Bug Behavior violates the spec Reserve the number first Fix the root cause Add a regression test CR-N Change Request Every detail already decided Tracker row, no plan file Implement directly Review gate before ship Plan N Plan Anything still undecided Plan file before any code Invariant table for risky rows Checkbox verification Always a Plan, however simple it looks: schema, auth, permissions, tenant isolation, money, dates, recurrence, concurrency
Rule 2. The test for a Change Request is whether any decision is left for the agent to make, not how small the diff is.
From diff to production Checks run cheapest first: lint, typecheck, guards, then database-backed tests, then the Tier 1 code review. A Tier 2 fresh-context review runs only when the diff touches money, dates, state machines, permissions, concurrency or 10 plus files. A migration review runs only when a migration changed. Then one release script migrates, commits and pushes. FROM DIFF TO PRODUCTION Cheapest, runs on every change Most expensive, runs only when needed Static checks lint, format, typecheck Guards plan drift, doc tables, schema drift Tests real database loaded, skips counted as gaps Tier 1 review /code-review on every ship Ship script migrate, commit, push. The only way out. Only when the diff calls for it Tier 2: fresh-context reviewer on a stronger model, driven by the plan's invariant table as a hunt list Migration review: before the ship command is shared Tier 2 triggers: money, dates, state machines, permissions, concurrency, or 10+ files. A hook blocks the agent from git commit.
Rules 6 and 7. A reviewer handed the plan's hostile inputs as a hunt list finds what a reviewer choosing its own examples does not: it picks friendly ones.

Incident stories

Twelve failures that generalize.

Picked from 80 logged lessons because none of them is specific to one product or industry. The full write-ups are in the repo.

S-01 · Planning

The checklist that could not be checked

A numbered prose list has no state. A phase shipped missing the deliverable its own item 10 named.

Plan-drift guard
S-02 · Release

Merged is not migrated

Hand-written security migrations never matched the release script's trigger, so production never got them.

Trigger pinned by test
S-03 · Testing

A fifth of the suite never ran

51 test files skipped on every run because the env file silently failed to load. The suite still said green.

judgment
S-04 · Testing

The test that could not fail

At round numbers, the right formula and the wrong one gave the same answer. Three rows, all read as rigorous.

Wrong implementations kept in the test
S-05 · Security

A coverage check that could not see join tables

"Every table with a tenant column" missed tables scoped only through a parent key.

Population defined by foreign key
S-06 · Guards

The exemption nobody tested

The guard for this exact bug class existed. Its skip branch had never been proven against anything.

Exemption proven red
S-07 · Consistency

Convergence without a guard decays

71 files drifted back to the old button pattern after a cleanup that shipped no guard.

Guard per pattern, same commit
S-08 · State machines

The rename that compiled clean

A hardcoded status in an update still type-checked after the rename, and silently matched zero rows.

Literal guard
S-09 · Data safety

Archiving deleted data it never mentioned

A cascading foreign key removed rows in a table the archive code had never heard of.

Dependents declared
S-10 · Planning

Two approved decisions that contradicted each other

Each read fine alone. The code silently picked one and made the other unreachable.

judgment
S-11 · Agents

"A subagent did it"

A confident accusation built on a clean git status. A second session sharing the folder was the real explanation.

List live sessions first
S-12 · Communication

Measure reach, never infer it

"Two files" changed 176 usages. "Everywhere" reached 14 files. Neither was counted before it was said.

judgment

Read all twelve in full on GitHub

Use it

Everything here is generic and MIT licensed.

Copy the constitution, cut it down to your project, and add guards as your own incidents earn them. Do not install every guard on day one.

New to Claude Code? Start here: six steps from an empty project to rules, a lessons log, a first guard, plans and reviews, in about an hour.

claude-code-playbook/ ├── templates/ CLAUDE.md, plan, lessons and decisions templates ├── skills/ new-plan, file-bug, close-phase, overnight-close ├── agents/ tier2-reviewer, migration-reviewer, invariant-scanner ├── hooks/ session-start lesson injection, git-write block ├── guards/ check-plan-drift, check-doc-tables └── stories/ twelve incidents and their guards
git clone https://github.com/mahi9119/claude-code-playbook.git
cp claude-code-playbook/templates/CLAUDE.md ./CLAUDE.md
cp claude-code-playbook/templates/lessons.md ./docs/lessons.md

Principles that hold it together

  • Blocking but narrow beats advisory but broad. New guards bind new work from day one and grandfather the old, one file at a time.
  • Cheap checks first. Lint before the database suite, the suite before the build, code review before a browser session.
  • A skipped test is not a passing test. Report passes, skips and failures separately, every time.
  • Prove the guard can fail. Break the code, watch the guard go red, restore. A guard that has never been red has never been tested.
  • A process exception is not a judgement exception. "Ship it yourself overnight" removes the wait between steps, not the owner's decisions.