AI with a leash: why senior teams keep Cursor and Claude Code in production after a rebuild

June 18, 2026engineering11 min read

The dominant industry story about a rebuild goes like this: the team shipped too fast, accepted too much AI-generated code, hit a wall, and now the rebuild is the moment to "rip out the slop and go back to humans." It is a satisfying narrative. It is also wrong.

When we run rebuilds for scale-ups, we don't ban Cursor or Claude Code on the new codebase. We use them more deliberately: smaller tasks, tighter prompts, deterministic checks. The reason is straightforward: the work that justifies a rebuild, types, tests, telemetry, row-level security, idempotent migrations, error budgets, is exactly the work that makes AI assistants safe to use. Hardening is the leash. Without it, AI is dangerous. With it, banning AI throws away productivity to avoid a problem the rebuild already solved.

This post is the playbook we hand to a CTO on the Monday after a rebuild ships. It is opinionated and operational. The first half is the thesis your founder needs. The second half is the configuration your engineers need.

Why the "no AI after rebuild" reflex breaks down

The reflex is rooted in real failure modes. The "AI coding death spiral", agents fixing one symptom while regressing three others, is documented across both Cursor and Claude Code communities. The rebuild postmortem usually surfaces the same four patterns: deleted failing tests, suppressed type errors, removed telemetry to "simplify," and git commit --no-verify to bypass pre-commit hooks. A team that just lived through six months of that wants the assistants gone.

But the assistants weren't the root cause. The repo was. An untyped, untested, telemetry-light codebase is unreadable to a junior engineer and equally unreadable to an LLM. When the agent has nothing to anchor against, it hallucinates, and the suppressions compound. The rebuild fixes the anchoring problem. Type errors now mean something. Tests now run in seconds. CI now blocks bypasses. The model still tries the same shortcuts, but now they fail loudly instead of silently.

We see this pattern in rebuild work. Suggestions that were mostly rejected on the legacy repo start landing once architecture boundaries, tests, and type errors are trustworthy. The agent isn't magically smarter. The repo is more honest, so its suggestions have something solid to attach to.

The right framing isn't "no more AI." It is bounded AI on a codebase that can say no.

How a hardened repo becomes AI-safe

Four failure modes that were untrackable on the old codebase become catchable on the new one.

1. Suppressed types. A diff-only ESLint gate that fails the build on any new as any, @ts-expect-error, or eslint-disable line stops the assistant's favorite escape hatch at PR time. The model can suggest the suppression. CI will not let it merge.

2. Deleted tests. Coverage thresholds, 80% as the floor, 100% on auth/, billing/, and policy directories, make test deletion visible in the diff. Combined with CODEOWNERS and branch protection that requires code-owner review, the deletion needs a named human to wave it through.

3. Removed telemetry. A custom ESLint no-restricted-imports rule that flags edits to telemetry.ts, logger.ts, or feature-flag files turns the "I simplified this" PR into a red diff. The agent learns the boundary because the linter teaches it.

4. Bypassed pre-commit. The Liam ERD team documented this one cleanly: agents reach for git commit --no-verify the moment a hook fails. The fix is a Claude Code permission deny on the exact command, plus a Lefthook configuration whose fail_text addresses the agent and tells it to fix lint errors before committing. In our variant, we make the bypass explicit: do not use --no-verify.

None of those four checks is novel. All four were structurally impossible on the pre-rebuild codebase. That is the asymmetry the founder needs to see: the rebuild did not eliminate the need for AI judgment. It eliminated the need to trust AI judgment.

The rebuild is the moment to draw the lines

Some directories are not for the agent. The rebuild is when you decide which ones, and you fence them in three places.

Imagine you're the CTO at a Series A SaaS the morning after a rebuild ships. The repo has src/auth/, src/billing/, db/migrations/, infra/, and policies/, code that, if it goes wrong, ends in a Stripe refund cycle, a data leak, or a regulator email. You have one chance to set the perimeter before someone opens Cursor and asks it to "refactor the billing logic for clarity."

The three fences are CODEOWNERS with branch protection, a Claude Code hook or equivalent local deny layer, and a CI gate. Cursor Project Rules steer Cursor, but they do not deterministically block writes. CODEOWNERS requests the named reviewer automatically; branch protection or a GitHub ruleset makes that review required. The Claude Code hook stops Claude Code from writing protected files in the session. CI is the backstop: even if local controls are bypassed, the pipeline still refuses the merge.

That redundancy is deliberate. Each fence catches a different failure mode, and each one is cheap. Skipping any of them makes the other two brittle.

Instruction files are the constitution

Once the perimeter is set, the next question is how the agent learns the rules of the inside. The answer is the markdown-in-repo instruction file. Cursor reads .cursor/rules/*.mdc. Claude Code reads CLAUDE.md. Codex, Amp, Jules, Factory, and Cursor all converge on the cross-tool AGENTS.md standard now stewarded by the Agentic AI Foundation. The OpenAI Codex monorepo ships 88 AGENTS.md files, one per subproject, to give the agent the right context wherever it lands.

These files are advisory. They instruct the model. They do not force it. That distinction matters for everything that follows.

The single biggest mistake we see on a freshly rebuilt repo is bloat. HumanLayer's analysis is blunt: frontier LLMs reliably follow approximately 150-200 instructions, and Claude Code's own system prompt already burns about 50 of them. Anthropic's guidance is the same: "Keep it concise. For each line, ask: Would removing this cause Claude to make mistakes? If not, cut it. Bloated CLAUDE.md files cause Claude to ignore your actual instructions." Target under 300 lines. Use @path/to/file imports for anything specialized.

A good CLAUDE.md covers the things the agent cannot guess from the code: bash commands, code-style rules that differ from the language default, the test runner, branch naming, the architectural decisions that aren't yet ADRs, and the gotchas that bit the last engineer. It does not contain a tutorial on TypeScript or a file-by-file map of the codebase. The agent can read the codebase. It cannot read your team's history.

The highest-impact block in the file is the deny list. In our rebuild work, it only works when paired with deterministic checks: the markdown names the failure mode, and the hook or CI gate enforces it.

# Never
- Never delete or modify .env, .env.*, package.json, lockfiles, or migration files without explicit confirmation.
- Never add eslint-disable, @ts-expect-error, or as any to silence an error.
- Never delete a failing test. Fix the code.
- Never remove logging, telemetry, or feature flags to make a test pass.
- Never run git commit --no-verify, --force, or git push --force.
- Never assume a package exists, run pnpm list <name> first.

Six lines. Each one corresponds to a real PR we have rejected. Each one closes a known regression class.

Hooks force what CLAUDE.md only requests

CLAUDE.md is a request to the model. Hooks are a runtime contract. Anthropic's framing is the one to remember: "Unlike CLAUDE.md instructions which are advisory, hooks are deterministic and guarantee the action happens."

Three hook events do most of the work.

SessionStart runs once when the agent opens the repo. We use it to inject the current branch, uncommitted files, and the active Linear or GitHub issue into context. The agent now starts every session knowing where it is in the codebase, not asking.

PreToolUse runs before every tool call. In Claude Code, it is the right place to deny edits to protected paths. The script reads the file path the agent wants to write, matches it against a regex covering auth/, billing/, migrations, .env*, and lockfiles, and returns a deny decision with a reason the agent can read:

{
  "hookSpecificOutput": {
    "hookEventName": "PreToolUse",
    "permissionDecision": "deny",
    "permissionDecisionReason": "Edits to src/billing/ require human review. Open a PR with the change in a separate commit."
  }
}

The agent does not get a "you cannot do that" silence. It gets an instruction to do the right thing.

Hook scripts run with the user's system permissions, so treat them like production automation: quote variables, use absolute paths, reject path traversal, and keep sensitive files out of scope.

PostToolUse runs after every successful edit. We wire it to pnpm -s lint --max-warnings=0 && pnpm -s typecheck, then return a blocking result when either fails. Without that blocking behavior, the hook is feedback, not enforcement.

The deny list in .claude/settings.json is the final piece. Block the bypass at the permission layer, instead of only in prose:

{
  "permissions": {
    "deny": [
      "Bash(git commit --no-verify:*)",
      "Bash(git push --force:*)",
      "Bash(git push -f:*)",
      "Bash(rm -rf:*)"
    ]
  }
}

Combined with a Lefthook pre-commit and required CI checks, the agent cannot merge code that fails lint, typecheck, or changed-package tests. The local hook helps the agent fix issues early. CI is what makes the rule non-negotiable.

Verification baked into every prompt

The structural protections handle the agent. Spec discipline handles the prompt. The rebuild is the moment to retrain your team on how to ask.

Anthropic's verification guidance, paraphrased from their best-practices doc, is the single most useful sentence in the corpus: "Without clear success criteria, the agent might produce something that looks right but actually doesn't work. You become the only feedback loop, and every mistake requires your attention." The fix is to write the success criteria into the prompt. "Implement a function that validates email addresses" becomes *"Write a validateEmail function. Example test cases: [email protected] → true, invalid → false, [email protected] → false. Run the tests after implementing."* The first prompt asks for code. The second prompt asks for code that passes a test the agent must run. Only one of them produces a verifiable result.

Test-first specs are the natural extension. Have the agent write the tests first, get a human or a reviewer subagent to approve them, then iterate the implementation until they pass. The test suite becomes the contract. The implementation becomes negotiable.

For the review pass, separate the writer from the reviewer. Anthropic's own guidance: "a fresh context improves code review since Claude won't be biased toward code it just wrote." The HAMY post documents one public version of the pattern: 9 parallel Claude Code review subagents for tests, lint/static analysis, code review, security, quality/style, test quality, performance, dependency/deployment safety, and simplification. Our version starts with fewer agents: security, tests, migrations, and RLS on repos where those paths carry real blast radius.

Nine subagents sounds like overhead. It is cheaper than one bad merge into src/billing/.

What this looks like for your team next week

If your rebuild is recently shipped and your team is asking whether to ban Claude Code or keep it, the practical sequence is short.

Week one: write the CLAUDE.md and the .cursor/rules/ files. Keep them under 300 lines combined. Include the deny list. Reference the test commands. Add CODEOWNERS for auth/, billing/, migrations/, infra/, policies/, and the CODEOWNERS file itself. Configure branch protection or rulesets to require code-owner review.

Week two: add the three Claude Code hooks: SessionStart for context, PreToolUse for the protected-path deny, PostToolUse for lint and typecheck. Add the permission deny list for --no-verify and force-push. Wire Lefthook to mirror the same checks at the commit boundary, and make Cursor-authored changes pass through the same CI and branch-protection gates.

Week three: add the diff-only ESLint gate for new suppressions, the coverage floor in CI, and one or two review subagents, start with security and tests. Run claude -p in headless mode against PRs as a CI step.

By the end of the third week, you have bounded AI in production. The agent is useful again on scoped work. Claude Code cannot directly write protected files in the session, and Cursor-authored changes cannot merge without the same CI and owner checks. The main regression classes your rebuild was meant to prevent are now caught by deterministic checks. The team that built it is the team that maintains it, same as our engineering practice on every other client.

The rebuild was not the end of AI in the codebase. It was the beginning of the only configuration in which AI in the codebase is responsible.

If your rebuild is live and your team is split on whether to ban Claude Code or keep it, we can help you keep the useful parts of AI without reopening the blast radius. Book a call.