CLAUDE.md · git:20260927.7a40820 · 2026-09-27 · sha256 bdc84a177b592dbe
CLAUDE.md git:20260927.7a40820A
Immutable. This exact content never changes and is served at /api/v1/blob/bdc84a177b592dbe.
# claude-spring-architect
Meta-repository: generates Spring Boot projects already prepared for AI-assisted
development. **It is not a Java application** — it has no `pom.xml`, does not compile,
has no Maven tests. What gets edited here are instruction files, data, and a hook.
Purpose, features and status: `@README.md`.
## Dependencies
`java` (JDK 21+) · `git` · `curl`. Nothing else. Zero Python, zero shell, zero `mvn` on
PATH — the wrapper comes in the Initializr's `starter.tgz`.
## Commands
| Action | Command |
|---|---|
| Diagnose the setup on this machine | `/arch-doctor` |
| Create a project from a blueprint | `/init-project` |
| Design a new extension of this `.claude/` | `/claude-code-architect-designer` |
| Validate frontmatter of skills and agents | `claude plugin validate .claude/skills` |
| Run the hook by hand | `java .claude/hooks/ArchHook.java doctor` |
| Check every compose service is up, and no foreign container holds its ports | `java .claude/hooks/ArchHook.java compose` |
| Render the execution trail of a run by hand | `java .claude/hooks/ArchHook.java audit flush` |
| Write a target project's `.claude/` from this one, transformed for a blueprint | `java .claude/hooks/ArchHook.java export <dest> --blueprint <id> [--dry-run]` |
| Validate frontmatter of all extension files, `.mcp.json`, every hook registration in `settings.json` and in `project-bootstrap`'s template, every `` !`…` `` injection's paths, and the `export` manifest against what is on disk | `java .claude/hooks/ArchHook.java schema` |
| List and inspect this repo's MCP servers | `claude mcp list` · `/mcp` |
## Architecture of the AI files
Clean Architecture applied to `.claude/` itself. Dependencies point in one direction:
```
hooks/ + settings.json infra/enforcement — deterministic
↓ verifies
skills/ procedure + exemplars
↓ invokes ↑ delegates via context: fork
agents/ isolated execution
↓ cites, never copies
rules/ + blueprints/ + .mcp.json norms and data ← LEAF
decisions/ history — nobody reads it at runtime, outside the graph
```
`claude-code-architect-designer` writes upward into `hooks/ + settings.json`, after
approval — a design-time edge, like `project-bootstrap` writing `src/`. At runtime the
direction above is unchanged.
## Invariants (non-negotiable)
1. **`rules/` is a leaf.** A norm never mentions a skill, an agent, or a command. If it
needs to, it's a procedure and belongs in a skill.
2. **Each norm has a single owning file.** Others cite it by path
(`@.claude/rules/naming.md`). A norm written in two places has diverged — that's a bug.
3. **Code boilerplate lives in `templates/` inside the skill that emits it**, with the
`.example` suffix. Never inside a norm, never pasted into the body of a `SKILL.md`.
4. **No new `commands/` are created.** Slash commands were merged into skills: write
`skills/<name>/SKILL.md` and control invocation with `disable-model-invocation`.
5. **An agent exists only for one of three reasons:** preserving context, restricting
tools, or changing model. If none applies, it's a skill.
6. **If a rule must always hold, it's a hook or `permissions.deny`** — not prose in
markdown. Owner: `claude-code-architect-designer`, forms 7 and 8, after approval and
always with a record in `decisions/`. The mirror holds too — a hook against a failure
nobody observed is a process per event confirming what was already true.
7. **Architectures are data.** Adding a blueprint must never require editing a skill, an
agent, or a command.
8. **Java and Spring Boot versions are never written from memory.** They are resolved at
runtime via Spring Initializr; without network access, ask.
9. **The generated project is self-contained.** Whoever clones it does not have
`claude-spring-architect`. Everything cited from inside the project must exist inside the
project: norms, development skills, agents, `ArchHook.java` and
`schemas/extensions.json`. `ArchHook.java export` writes all of it (step 6.6 of
`project-bootstrap`), and **what travels is data, in the `export` block of
`@.claude/schemas/extensions.json`** — creation skills excluded on purpose, and the
blueprint **catalog** too, with one exception the manifest names: the **active**
blueprint travels (`export.blueprint_copy`), because the stamp records its id, an
update has to resolve it, and a blueprint written during adoption exists nowhere
else. A new norm, skill or agent is
complete once that block lists it: every skill and agent on disk in `include` or
`exclude`, a `derived_paths` entry for a rule with a package territory, and
`ArchHook.java schema` failing by name on either gap.
**An MCP server has the same obligation, per server, not per file:** it is declared
for this meta-repo (`.mcp.json`), for the generated project
(`project-bootstrap/templates/mcp.json.example`, copied by `export.optional_copy`),
or both — and the file it's written into **is** that declaration. A server useful to both is
written in both files on purpose; that is not a duplication bug, see invariant 2.
**A hook, the same:** the registration is per file (`.claude/settings.json`,
`project-bootstrap/templates/settings.json.example`); a mode inside `ArchHook.java`
needs nothing, the `export` mode copies that file whole.
10. **Recognized frontmatter fields, `.mcp.json`'s server fields, and `settings.json`'s
hook events and entry fields are data with a single owner.** The list lives in
`.claude/schemas/extensions.json`, `ArchHook.java schema` is what reads it, and any
other file that displays it is derived and must match it exactly. A corollary of 2
and 7, written separately because its failure mode is silent: the runtime ignores an
unknown field without any error — an unknown *event* too, and a `matcher` on an event
that never reads one — and `claude plugin validate` lets it all through, looking at
neither `.mcp.json` nor `settings.json`. Binds the Java too: a mode reads its lists
from `extensions.json`, never from a constant in the source.
11. **No literal secret in a versioned file.** `.mcp.json` (this repo's or the copy
inside a generated project) carries only `${VAR}` / `${VAR:-default}` expansion,
`oauth`, or `headersHelper` — never a token, key, or password spelled out. Verified
by `ArchHook.java schema`'s secret scan over `headers`/`env`. A leaked token in git
history is irreversible; that is why this is a hook and not a review checklist.
## Routing — when X, read Y
| If the task involves | Go to |
|---|---|
| Creating a project from scratch | skill `project-bootstrap` |
| Installing this `.claude/` into a project that was never generated here, or pulling a newer version into one that was | skill `arch-adopt` — manual only, runs **inside the target project**, refuses a dirty worktree, and writes through `ArchHook.java export`. It travels into the generated project, unlike the other creation skills: that is how a project updates itself once the plugin that delivered it is gone |
| Creating a skill, agent, norm, or `CLAUDE.md` section — and deciding which of the eight | skill `claude-code-architect-designer` |
| Creating a hook, a new `ArchHook.java` mode, or a `permissions.allow`/`deny` line — and deciding whether the answer is a guarantee at all | skill `claude-code-architect-designer`, forms 7 and 8. Writes it after approval, always with a record in `decisions/`, and warns that `settings.json` is read only at startup |
| Designing a use case before implementing it; knowing whether a request is one or several | skill `use-case-design` |
| Aggregate, value object, invariant, ports of an already-designed use case | skill `domain-modeling` |
| Table, JPA mapping, migration, index, slow query, datasource properties | skill `persistence-architect` |
| REST adapter, controller, DTO, status, OpenAPI | skill `rest-api-architect` |
| Tests, coverage, installing ArchUnit | skill `test-architect` |
| Orchestrating a full feature (use case → domain → REST → persistence → tests) | skill `new-feature` — manual only: the user types `/new-feature <description>`, the model can't invoke it. One use case per run |
| Creating a git repo, committing, or pushing the project just generated or just implemented | skill `git-publish` — chained automatically after `/init-project` and after `java-spring-boot-developer` succeeds; behind two confirmations |
| Docker, docker-compose, adding a service (DB, broker) to a project, Testcontainers image consistency at the compose level, choosing an observability backend (Jaeger or Grafana+Tempo+Prometheus) behind the OTLP collector | skill `docker-architect` |
| A container that "started" but isn't answering, a port already allocated, OTLP traffic reaching the wrong collector, a compose `image:` tag that disagrees with the one `src/test` pins in `DockerImageName.parse` | `ArchHook.java compose` — hook, not skill. Also folded into `doctor`, so `/arch-doctor` reports it. The tag comparison needs no Docker: it reads both files |
| Kafka producer/consumer, publishing or consuming a domain event over a broker, topic/partition/DLQ | skill `messaging-architect` |
| Design pattern, growing `if`/`switch` chain | skill `java-patterns` |
| Connecting to an external system (Jira, database, GitHub, Figma), a server exposing `mcp__*` tools, `.mcp.json` | skill `claude-code-architect-designer` |
| Auditing what any skill or agent run in a **generated project** cost and what it chained — invoked by `/command` or by the model | `ArchHook.java audit` — hook, not skill; wired only into `project-bootstrap/templates/settings.json.example`, installed by step 6.6 |
| Reading that trail back — spend per skill and agent across runs, which report to open | skill `audit-usage` — aggregation by `ArchHook.java audit summary`, the skill renders; runs in the **generated project**, where the trail exists; here it reports the trail is off. An observer: the hook leaves it no report, via `audit.exclude_skills` in `@.claude/schemas/extensions.json` |
| Which norm covers what | `@.claude/rules/00-index.md` |
| Which frontmatter fields are valid in each file type | `@.claude/skills/claude-code-architect-designer/references/frontmatter-fields.md` |
| Why a skill, norm, or agent exists in the form it's in | `@.claude/decisions/README.md` |
| Contract every blueprint fulfills | `@.claude/blueprints/_schema.md` |
| Frontmatter fields the runtime recognizes | `@claude-help.md` |
| How each piece of Claude Code works | `@claude-help.md` |
## Known pitfalls
- **A skill cannot have the name of a native slash command.** The folder name becomes
the command, and `/doctor`, `/init`, `/context`, `/memory` already exist in the
runtime. That's why this repo's diagnostic skill is called `arch-doctor`. Shadowing a
native command doesn't produce an error — it runs the wrong command.
- **`.claude/settings.json` is only read at session startup.** Editing hooks mid-session
has no effect — `claude` must be restarted.
- **A hook fails silently in four ways, none an error.** Unknown event name — never
fires. `matcher` on an event that doesn't read one — filters nothing. Pipe or `&&`
inside `"command"` — part of the filename (exec form: `command` is the binary, `args`
the arguments). A mode that throws — exits 0 through `main`'s catch, looks like it
passed. `schema` catches the first three, from `settings` in
`@.claude/schemas/extensions.json`; the fourth only by running the mode by hand.
- **The runtime silently ignores unknown frontmatter.** An invented field is decoration,
not behavior. List of native fields in `@claude-help.md`.
- **`AskUserQuestion` rejects a question with fewer than 2 options**, and rejects the
**whole batch** with it: `InputValidationError ... "too_small" ... path:
["questions",1,"options"]`. A question with one option isn't a question — decide it,
and record the decision where the answer would have gone. The batch also has an upper
bound of 4 questions per call. The generated project carries the same pitfall in its own
`CLAUDE.md`, from `project-bootstrap/templates/root.CLAUDE.md.example`.
- **Everything the model must obey lives in the body of the file**, never in
frontmatter. `metadata.*` was removed from skills and agents: ownership, `reads`,
`handoff`, and contracts live in the `## Contrato` section of the body. Do not put
`metadata:` back into a `SKILL.md` — it costs tokens on every invocation and enforces
nothing.
- **The `CLAUDE.md` at the root of a generated project is not this file.** It is
produced from
`.claude/skills/project-bootstrap/templates/root.CLAUDE.md.example`.
- **`claude plugin validate` does not validate fields.** It accepts `metadata:`, accepts
camelCase in a skill, and accepts an invented field, always with `✔ Validation
passed`. It does not look at `.claude/agents/`, `.claude/rules/`, or
`.claude/settings.json`. It catches malformed YAML, nothing else. What validates
fields is `java .claude/hooks/ArchHook.java schema`.
- **`.claude/decisions/` is neither a norm nor living documentation.** It records what
was decided on the date, not what holds true today. It has no `paths`, does not enter
`00-index.md`, and does not go into the generated project. It is superseded by a new
record; the old one is not rewritten.
- **This repository does not run `./mvnw`.** `ArchHook` exits 0 when it doesn't find
the wrapper; here that's expected, not a failure.
- **`allowed-tools` with `Bash(command:*)` checks each segment of the pipe separately.**
An injection `` !`a | b | c` `` in the body needs a rule for `a`, `b`, and `c`; miss
one and the whole command is blocked before it runs. Write injections as a single
command (`ls .claude/skills`, not `find … | sed | sort`). This only affects skills
that restrict Bash: `allowed-tools: Bash` without a filter lets the whole pipeline
through.
- **A frontmatter `` !`…` `` injection runs in the session's persistent shell, at whatever
cwd it currently holds.** `cd` into a skill directory in one `Bash` call contaminates
every injection of every skill invoked afterward, and a relative `test -f` then reports
a file as absent while it exists. Read a template with `Read` at an absolute path, never
`cd` + `cat`. Every injection in this repo resolves paths from
`"${CLAUDE_PROJECT_DIR:-.}"`, and `ArchHook.java schema` blocks one that doesn't — a
genuinely cwd-independent injection needs a regex in `injections.exempt_patterns` of
`@.claude/schemas/extensions.json`.
- **The `audit` mode is off in this repository, on purpose.** It switches itself on by
the presence of `.claude/audit-usage/`, and this meta-repo doesn't create the
directory: the trail is a feature of the *generated* project, wired in
`project-bootstrap/templates/settings.json.example`, not in this repo's
`settings.json`. Copying those hook entries here would start auditing the design of
the tool instead of its use. What it records and why it isn't a skill:
`@.claude/decisions/0035-auditoria-execucao-hook.md`. The consequence for
`/audit-usage` is that running it **here** correctly reports the trail is off — its
territory is the generated project, and it is exercised there, not in this repo.
- **The trail doesn't record its observers.** `/audit-usage` and `/arch-doctor` carry
`disable-model-invocation: true` but change nothing, so they're listed in
`audit.exclude_skills` in `@.claude/schemas/extensions.json` and leave no report of
their own. Invoking one still *closes* the run in progress — which it already did, and
which is the only way, inside a live session, to get a report that isn't stamped
`⏳ em andamento`. Why: `@.claude/decisions/0036-skill-audit-usage.md`.
- **Only the hook-protocol modes of `ArchHook.java` read stdin** — `check`, `format`,
`tests`, `schema`, `audit`, `guard`. Invoking one of those by hand without `</dev/null`
blocks until something closes stdin, with no output: a command that looks hung, not
failed. `export`, `doctor` and `compose` are invoked by people and read nothing.
- **`.mcp.json` is only read at session startup**, same as `settings.json`. Adding or
editing a server mid-session has no effect until `claude` is restarted.
- **A project-scoped server in `.mcp.json` needs one-time human approval** the first
time it loads (`claude mcp list` shows pending ones). That prompt is the trust
boundary a cloned repository can't skip — never work around it with
`enableAllProjectMcpServers`.
- **Server precedence is `local > project > user`, silently.** A personal server with
the same name as the team's `.mcp.json` one shadows it — no warning either way.
- **An unset `${VAR}` in `.mcp.json` doesn't fail generation.** The server loads with
the literal `${VAR}` text and fails to connect at runtime instead. `claude mcp list`
surfaces the missing-variable warning; frontmatter schema validation does not.