Setting Up a Repo So AI Actually Knows Your Project
How I structured AuthHub's CLAUDE.md and .claude/ directory — rules, prompt templates, and context files — so an AI agent starts every session already knowing the architecture, the standards, and what not to touch.
An AI coding agent is only as good as the context it starts with. Every new session begins from zero: it doesn't know your module boundaries, your response envelope convention, or that half the README describes code you deleted last week. On AuthHub — my multi-module Spring Boot auth service — I kept re-explaining the same five things until I stopped and built the explanation into the repo itself.
This note documents that setup: a thin CLAUDE.md entry point plus a .claude/ directory with three kinds of files — rules, prompt templates, and narrative context. It's the difference between an assistant that guesses and one that already knows.
The problem: the agent forgets everything
AuthHub has real constraints that aren't visible from any single file: a one-way module dependency direction, a mid-refactor main branch where old OTP and cookie-based OAuth2 code was removed and must not come back, Flyway as the only schema authority, and an ApiResponse envelope that every endpoint must use. An agent that hasn't been told any of this will happily 'fix' things by reintroducing exactly the patterns I spent a week removing.
The core idea
Treat AI context like infrastructure, not conversation. Anything you've explained to the agent twice belongs in a file it reads automatically.
Layer 1 — CLAUDE.md as a thin entry point
The root CLAUDE.md is deliberately short. It states the stack in one line, imports the real documentation, and keeps only the rules that must never be missed (git safety). The @imports pull in existing docs/ files, so there's a single source of truth — the same architecture doc humans read is the one the agent reads.
# AuthHub
Multi-module Gradle/Spring Boot monorepo. Group `com.henheang`,
Java 17, Spring Boot 3.5.x, PostgreSQL.
@docs/architecture.md
@docs/security-api-state.md
@docs/coding-standards.md
@docs/database.md
@docs/build-and-run.md
## Git Rules
- Never commit unless explicitly asked.
- `main` has an in-progress refactor — check `git status` before
staging, don't sweep unrelated changes into a commit.Don't duplicate — import
If CLAUDE.md restates what docs/ already says, the two will drift apart and the agent will trust the stale copy. Import the docs instead of copying them.
Layer 2 — scoped rules in .claude/rules/
Each rule file covers exactly one concern and cross-references the matching docs/ file for detail. Small files are easier to keep accurate, and the agent can be pointed at one without loading all of them:
- architecture.md — dependency direction (common-api → security-api → todoapi), never introduce a cycle
- coding-style.md — Lombok, MapStruct, the ApiResponse envelope, fixed package layout
- security.md — secrets handling, plus the auth model's explicit in/out list
- testing.md — JUnit 5, why ./gradlew build (not just test) is the definition of done
- git.md — never commit unasked, destructive ops need explicit confirmation
- review.md — the pre-review checklist a change must pass
The security rule is the most valuable one. AuthHub's main branch is mid-refactor, so the rule spells out what's IN (JWT, MFA, Google ID-token login, audit logging, rate limiting) and what's OUT and must not be resurrected (the custom OTP system, the old cookie-based OAuth2 flow). Stale references to removed code are to be flagged as drift, not treated as the pattern to follow.
Layer 3 — prompt templates in .claude/prompts/
For recurring task shapes — new feature, bug fix, refactor, API endpoint, schema migration — there's a fill-in-the-blank template that bakes the relevant rules into the request itself. I fill in three blanks; the constraints come pre-attached:
Add {{feature}} to {{module}}.
Constraints:
- Respect module dependency direction — don't introduce a cycle.
- Package layout: config/ controller/ domain/ repository/
security/ service/(+impl/) payload/ exception/.
- Wrap responses in ApiResponse — no raw ResponseEntity<T> bodies.
- Throw domain exceptions; GlobalExceptionHandler translates them.
Deliverables:
- Implementation + tests.
- ./gradlew spotlessApply && ./gradlew clean build passing.
- CHANGELOG.md entry if user-visible.Layer 4 — narrative context in .claude/context/
Rules say what to do; context explains where the project stands. These files are the background a new teammate would get over coffee: what AuthHub is, what's done vs. deferred on the roadmap, how the response envelope actually behaves in code (including its known quirks), and which dependency versions are in play.
Document the rough edges you're keeping
The most useful file turned out to be the list of deliberate rough edges — like todoapi using an inconsistent base package on purpose, or Checkstyle being advisory-only. Without it, every session 'helpfully' tries to fix things that are intentionally deferred. Telling the AI what NOT to fix saves more time than telling it what to build.
How the layers divide the work
| Layer | Lives in | Answers |
|---|---|---|
| Entry point | CLAUDE.md | What is this repo? What must never happen? |
| Rules | .claude/rules/ | What are the hard constraints, per concern? |
| Prompts | .claude/prompts/ | How do I ask for recurring task types? |
| Context | .claude/context/ | Where does the project stand right now? |
What this changed in practice
- Zero re-explaining: sessions start with the architecture, standards, and refactor state already loaded.
- The agent flags stale OTP/OAuth2 references as drift instead of rebuilding them.
- Reviews got faster because generated code lands in the right package with the right envelope on the first pass.
- The docs got better for humans too — writing for an agent forces you to make implicit conventions explicit.
Context files drift too
A state-of-the-module doc can lag behind the code it describes. Mine says so explicitly and tells the agent to verify against the actual source tree before trusting it. Any file that claims to describe 'current state' needs that disclaimer.
None of this is Claude-specific in spirit. It's the same discipline as good onboarding docs — except the new teammate joins every single session, reads everything you wrote in seconds, and takes all of it literally. Write accordingly.
Related articles
How I Use Claude Code Every Day — As a Mentor, Not an Autopilot
I built a strict-mentor system prompt that refuses to hand me finished code. Here's why I made AI harder to use, not easier.
Prompt Engineering for Backend Developers: The 3-Part Rule
Context, Task, Constraint — the one rule I check before sending any prompt, plus ten techniques that turned vague answers into precise ones.