The Claude Code setup I actually run

Two-tier CLAUDE.md · path-scoped rule packs for web / Android / iOS / compute · 50 subagents across four stacks · guard-commit and format hooks · MIT at github.com/roadhero/claude-code-setup · from the book "42: The AI Builder's Stack."

· · 6 min read
The Claude Code setup I actually run

Most people publish a single CLAUDE.md and call it a setup. Mine started that way too. The Tawen repo had one file at 48,890 characters — every convention, every command, every compliance note, loaded into context at the start of every session whether it was relevant or not.

It got worse results than the 5,648-character file that replaced it.

That is the whole lesson, and Anthropic's own documentation now says it plainly: files over 200 lines consume more context and reduce adherence. Instructions are delivered as context, not as enforced configuration. Claude reads them and tries to follow them. There is no guarantee.

So the question stopped being "what should I write in CLAUDE.md" and became "what belongs in context, what belongs in a conditional file, and what shouldn't be left to the model at all."

Everything below is in a public repo: github.com/roadhero/claude-code-setup. MIT, no attribution required. Take what's useful.

Two tiers

There is exactly one global file and one project file. That's the architecture.

The global tier lives in ~/.claude/ and holds everything true regardless of what I'm building:

  • CLAUDE.md — sections 1 through 18. Workflow, git rules, coding guidelines, secrets handling, anti-patterns. Stack-agnostic.
  • rules/ — platform packs that load conditionally.
  • agents/ — the default subagent roster.
  • hooks/ — the enforcement layer.
  • skills/new-repo/ — a scaffolder.

The project tier is a short CLAUDE.md at the repo root holding only section 19: stack, quality gate, release pointers, compliance scope. Nothing else.

The split matters for a mechanical reason. Claude Code walks up the directory tree and concatenates every CLAUDE.md it finds, ordered root-down, so the file closest to where you launched is read last. The global spine is identical across every session on the machine, which makes it a good prompt-cache citizen. The project file is the only thing that changes per repo, and it's the last thing read.

Configure once, then mostly leave it alone.

What section 19 looks like filled in

Abstract advice about "project context" is worthless. Here is the shape, from Tawen's actual file:

  • One-paragraph description. What it is, in prose, including what it deliberately does not do.
  • Stack. Kotlin 2.3 / K2, JDK 17, minSdk 28, targetSdk 36, the framework list with pin policy, storage schema version, build tooling, test runner, CI.
  • Local quality gate. The exact command, runnable from a fresh clone, with --no-daemon to match CI:
./gradlew spotlessCheck detekt :app:lintDebug \
          :app:testDebugUnitTest :app:verifyRoborazziDebug \
          :app:compileReleaseKotlin --no-daemon
  • Release pointers. Live version and versionCode, what's in flight.
  • Compliance scope. For Tawen: US and Canada only via Play Console geo-restriction, analytics consent-gated and off by default, no health values in telemetry. For HashKeep, an encrypted SMS backup app: the Play SMS policy backup-lane exception, no SEND_SMS permission ever, and a Detekt rule enforcing zero-content telemetry. Those are written as hard overrides, not suggestions.

None of that is derivable from the codebase, which is exactly the test for whether it belongs in the file. Directory layouts and dependency lists don't go in. Claude can read those.

Rules that load only when they apply

rules/{web,android,ios,compute}.md are platform packs. The Android pack has no business being in context when I'm working on a C++/CUDA repo.

Claude Code supports this natively now through paths frontmatter in .claude/rules/:

---
paths:
  - "**/*.{kt,kts}"
---

# Android rules

Rules with a paths field only enter context when Claude actually reads a matching file. Rules without one load unconditionally at the same priority as the project CLAUDE.md. User-level rules in ~/.claude/rules/ apply to every project and load before project rules.

This is the single highest-leverage change available to anyone with a bloated config, and it's the thing I'd tell past-me to do first.

Fifty agents, four stacks

The roster is 50 subagents split across four sets:

  • agents/ (15) — the default. A four-hat chain (architect → senior-swe → code-reviewer → qa), specialists (security, performance, db-migration, debugger, devops, docs, design), and a delivery layer (TPM, scrum-master).
  • agents-android/ (7), agents-ios/ (7), agents-compute/ (21) — per-stack overrides.

The override mechanism is the interesting part. A per-repo .claude/agents/ file shadows a user-scope ~/.claude/agents/ file with the same canonical name: field. So dropping the Android set into a repo replaces the generic code-reviewer with a platform-brained one, silently, with no routing configuration anywhere. Same name, different body.

The compute set is the largest because C++/CUDA/Python systems work has the most ways to go quietly wrong. It's also overkill if you only ship web apps. Strip it.

Subagents each get their own context window, which is the real reason to use them. The architect doesn't need the QA agent's context and vice versa.

Hooks are the only enforcement

This is the section that earns the repo.

The documentation is explicit that CLAUDE.md shapes behavior but is not a hard enforcement layer, and that if something must run at a specific point, it should be a hook. Hooks are shell commands at fixed lifecycle events. They run regardless of what the model decides.

hooks/guard-commit.sh blocks four things:

  1. Force-pushes to protected branches
  2. Non-human committers
  3. AI attribution lines in commit messages
  4. Obviously-staged secrets

Every one of those was previously a line in CLAUDE.md asking nicely. Asking nicely worked most of the time, which is worse than not working, because you stop checking.

hooks/format.sh auto-formats edited files by extension across every stack. Design decision worth stealing: a missing formatter is a silent no-op, never an error. A hook that fails loudly on a machine that doesn't have ktlint installed is a hook people disable.

The general principle, stated in Anthropic's own settings-versus-memory table: use settings and hooks for technical enforcement, use CLAUDE.md for behavioral guidance. Anything you would be upset about if it slipped through belongs in the first category.

The two settings files

settings.json covers web, Android, and iOS. settings2.json covers C++/CUDA/Python, with compute toolchain permissions and a subagent model pin.

Claude Code reads only settings.json. settings2.json requires a manual swap or a merge. This is the sharpest edge in the whole repo and I'd rather say so than have someone discover it at an inconvenient moment.

Three things that broke

The fork-bomb deny rule. I had Bash(:(){:|:&};:) in the deny list. It caused repeated JSON parse errors. Removed permanently. The characters that make a fork bomb dangerous also make it terrible JSON.

Hook paths on Linux. They must be literal. $HOME does not expand in that position and the hook silently fails to fire. If you're moving a config from macOS to Linux, this is the first thing to check.

Assuming an override took effect. Run /context and read the list under Memory files to see what actually loaded. There is also an InstructionsLoaded hook that logs exactly which instruction files loaded, when, and why — useful for debugging path-scoped rules that appear to do nothing.

Where it runs

The Mac bundle and the Linux bundle share the universal spine. Nexus, my Threadripper workstation, got the compute-focused variant: 21 agents installed system-wide, compute.md rules, validated settings.json, live hooks. claude doctor clean, no errors.

One outstanding issue: a kotlin-lsp plugin error on the Mac, pending a Command Line Tools update or disabling the plugin.

Honest caveats

This is opinionated and built for how my team works. The git rules assume PR-based flow with protected branches. The hooks assume formatters are installed, though they no-op cleanly if not. The compute stack is systems work and is dead weight for web-only shops.

The point isn't to adopt my exact setup. It's to see a real working one and build yours. Most published configs are aspirational. This one runs every day at a 40-person studio.

The longer version

The chapter on Claude Code in 42: The AI Builder's Stack goes deeper on the reasoning behind all of this — why the two-tier split, why hooks over instructions, what I got wrong on the way there. The book covers the rest of the stack too: terminal setup, database architecture, deployment pipelines, agentic workflows, voice AI, code review automation.

22 chapters. No sponsors, no affiliate links, nobody paid to be mentioned.

Free chapter excerpts are at eltexsoft.com/course/ if you want to read before you buy.