project-bootstrap
Transforma um diretório novo ao lado do nerviz num projeto Spring Boot a partir de um blueprint: base do Initializr, fronteiras, hooks e build verde.
project-bootstrap, seção a seção
As seções do próprio SKILL.md da skill, em trechos. Passe o mouse ou clique numa delas para ver quais regras do projeto, templates e arquivos de references/ ela cita. As seções também são chips: clique num para ir até ela.
## Dependencies
java (JDK 21+) · git · curl. Nothing else. mvn/gradle on the PATH are not required — the Initializr's starter.tgz already brings the matching wrapper (mvnw or gradlew, whichever build.tool resolved to).
No Python, no template engines, no bash. The hooks are a single Java file run in single-file source mode, which makes them identical on Linux, macOS, and Windows — the same dependency the target audience already has mandatorily.
## When NOT to use
pom.xml or build.gradle already exists at <project> (§ Where the project is born). This skill does not migrate existing projects nor overwrite structure. In that case: stop, report, suggest /new-feature from a session opened in that directory instead.
## Where the project is born
The session runs in a clone of this repository, and the project is generated next to it:
<project> = <this repository>/../<artifactId>
Resolve it to an absolute path once the interview has settled artifactId, before step 3, and use that same absolute path in every later step. Never generate into this repository: ArchHook.java export refuses a destination equal to its source (step 6.6), and the creation skills and blueprint catalog would end up beside the project's code.
… cortado aqui, o resto está no arquivo
## Preconditions
java --version && git --version && curl --version | head -1
ls ${CLAUDE_SKILL_DIR}/templates/*.example
Check that the exemplars the procedure cites are there. Some are conditional on the active blueprint's build.tool — check only the ones the resolved tool needs, per the table below:
| Build tool | Exemplars (step 4) |
|---|---|
maven | pom.parent.xml.example and pom.module.xml.example |
gradle | settings.gradle.example, build.gradle.parent.example, and build.gradle.module.example |
… cortado aqui, o resto está no arquivo
## Procedure
Independent calls go in one response. Every turn re-reads the whole context, so a step's exemplars are parallel Reads and its files — every package-info.java of step 4.7 — parallel Writes, in one response each. Never a shell loop or heredoc instead: guard bash may not read its paths, and one bad heredoc corrupts every file it writes (@.claude/lessons-learned/lessons-learned-020.md § 7).
### 1 · Select the blueprint
The blueprint, the build tool and every other answer come from /init-project's interview, run in the main session before the agent that runs this skill starts — that agent runs in the background and cannot ask (@.claude/decisions/0123-lessons-learned-020-init-project-run.md). This skill never asks. A missing answer stops it: report blocked: missing <field>. Never assume the architecture: it's the most expensive decision to reverse in the whole project. Check the given id against .claude/blueprints/*/*.yaml, listed dynamically — each architecture lives at .claude/blueprints/<id>/<id>.yaml.
The blueprint's build.tool (maven or gradle) is a default, not a lock-in — the answer the interview passed wins. Step 2 resolves the final value and every step from step 3 on assumes it's already settled.
### 2 · Validate the blueprint
Read the YAML and check, one by one:
| # | Rule | If it fails |
|---|---|---|
| 1 | id, name, build, modules, packages, dependency_rules, features, architecture_paths all exist | Stop and name the missing field |
| 2 | The depends_on graph is acyclic | Stop and show the cycle |
| 3 | Exactly one module with contains_main: true | Stop and list the candidates |
| 4 | Every feature referenced by a module exists in features | Stop and name the feature |
| 5 | Every templates.<role> points to an existing file | Stop and name the path |
| 6 | architecture_paths is not an empty list | Stop — without this, step 6.6 has nothing to write |
| 7 | features.messaging-sqs is absent or false | Stop — the feature was retired (@.claude/decisions/0178-retire-the-sqs-blueprint-feature.md): no skill or template delivers an SQS adapter. Say to set the key false or remove it |
Don't invent defaults for dependency_rules — it's the backbone of the enforcement.
Resolve the effective build tool now, before step 3. build.tool in the blueprint is the default; @.claude/blueprints/_schema.md documents it as "Overridable at initialization" — if the user asked for the other tool in /init-project's interview, that override wins. Whatever the final value — maven or gradle — every step from here on branches on it once and stays with that choice. Never generate both.
### 3 · Generate the base with Spring Initializr
The Initializr's type= parameter is what actually picks the build system — everything downstream just follows the file shapes it produces:
… cortado aqui, o resto está no arquivo
### 4 · Restructure according to the blueprint
Branch once on the build tool resolved before step 3, and stay on that branch for the rest of this step — write only the files of the tool actually chosen. A gradle project never gets a pom.xml, mvnw, or .mvn/; a maven project never gets a build.gradle, settings.gradle, or gradlew.
Read references/build-<tool>.md § 4 — only the tool just resolved — then references/scaffold.md § 4. The first holds the tool's file layout (POMs or Gradle scripts, wrapper); the second what both tools share, including the andaime rule every later template copy follows. Done when every module of the blueprint exists with its depends_on wired in the build files, and nothing of the other tool is on disk.
The steps from here to 8.6 run in order, every one of them. Each heading below names the reference that holds its body; open it before executing the step — the spine alone is not the procedure.
### 4.5 · Domain exception family — not here → `references/scaffold.md` § 4.5
Nothing is generated: the exception family belongs to domain-modeling. Read the section for why, so step 4.7 does not reintroduce it.
### 4.6 · Generate the Checkstyle config → `references/build-<tool>.md` § 4.6, then `references/scaffold.md` § 4.6
config/checkstyle/checkstyle.xml plus the tool's plugin, bound to validate, and config/checkstyle/checkstyle-test.xml, the light rule set for src/test. Done when the build file carries the plugin and both configs exist.
### 4.7 · Materialize the packages and feature configuration → `references/scaffold.md` § 4.7
One package-info.java per role in packages.map, plus the application*.yml of each active feature. No other .java.
### 4.8 · Generate the `lombok.config` → `references/scaffold.md` § 4.8
At the project root.
### 4.9 · Generate the `logback-spring.xml` → `references/scaffold.md` § 4.9
Under src/main/resources of the module with contains_main: true.
### 4.10 · Generate the base `Dockerfile` and `docker-compose.yml` → `references/scaffold.md` § 4.10
Base pair, plus one service per active feature that needs a container, merged through docker-architect's templates.
### 5 · Generate the boundary map → `references/project-files.md` § 5
Read references/project-files.md now — it holds steps 5 to 7. .claude/forbidden-imports.txt, written by ArchHook.java boundary-map — never by hand.
### 6.5 · Generate CI → `references/project-files.md` § 6.5
Exactly one workflow template — the one of the resolved build tool.
### 6.6 · Write the project's `.claude/` → `references/project-files.md` § 6.6
ArchHook.java export writes rules, skills, agents, hook, schema and settings, and rewrites the rules' paths. Nothing in the generated project may depend on this repository.
### 7 · What the enforcement you just installed does → `references/project-files.md` § 7
Nothing to run — what the final report has to be able to say.
### 7.5 · Decide transport security → skill `transport-security-setup`
Invoke transport-security-setup via the Skill tool, with a one-line context: build tool, blueprint id, project: <project>, and "chained from project-bootstrap — skip your Verify". It owns the two questions this step asks — where TLS terminates, and the local profile or local edge — and writes the transport configuration into application.yml, the HSTS filter when no security chain exists, the transport IT, and the **Transport: paragraph of the root CLAUDE.md step 6 wrote; the edge service or the Dockerfile port it hands to docker-architect. Before Verify, so step 8 builds and runs the test it wrote. Same class, territories summed (@.claude/decisions/0092-guard-same-class-chain-sums-territories.md). Don't write any server.ssl, spring.ssl or forward-headers-strategy line here.
### 8 · Verify → `references/build-<tool>.md` § 8, then `references/verify-and-report.md` § 8
Everything in this step branches on the same build tool resolved before step 3 — run only the column that matches.
Read references/verify-and-report.md now — it holds steps 8 to 8.6. Done when the build passes, or when the report says why it failed.
### 8.4 · Configure SonarQube → skill `sonarqube-setup`
Invoke sonarqube-setup via the Skill tool, with a one-line context: build tool, coordinates, and project: <project>. It owns the one question this step asks — an existing server (URL, authentication) or a local container — and the scanner in the root build file, the CI step for an external server, and, for a local one, the sonarqube service it hands to docker-architect. After Verify, so the build it touches is already known green; before the README, so step 8.5's {{outputContractBlock}} carries its SonarQube: line. The chained skill joins this phase instead of replacing it — same class, territories summed (@.claude/decisions/0092-guard-same-class-chain-sums-territories.md) — so the steps after it still write. Don't write any sonar.* line here.
### 8.5 · Generate the project README → `references/verify-and-report.md` § 8.5
README.md (English) and README.pt-br.md, after Verify.
### 8.6 · Write the audit genesis record → `references/verify-and-report.md` § 8.6
.claude/audit-usage/GENESIS.md, once.
## Output contract
✅ Project <name> created — blueprint <id>
Modules:
<path> → depends on <list>
...
Versions (resolved by the Initializr): Java <x> · Spring Boot <y> · <build tool> (wrapper <z>)
Active features: <list>
Business code: none — by design. <n> package-info.java written
Boundaries: <n> rules in .claude/forbidden-imports.txt — blocking verified ✓
Checkstyle: config/checkstyle/checkstyle.xml — plugin <v> · tool <v>, validate phase · checkstyle-test.xml over src/test
Spotless: check bound to the build (verify / check) — formatting and unused imports, main and test
Lombok: lombok.config at the root — @Data and @Setter stop compilation
ArchUnit: to be installed — `test-architect` skill (see Next steps)
Coverage: JaCoCo generates a report; the 80%/70% gate comes in with `test-architect`
Self-contained: <n> rules + <n> skills + <n> agents + ArchHook.java + extensions.json written by `ArchHook.java export` — no dead paths ✓
Provenance: .claude/.arch-provenance.json — blueprint <id>, ref <ref>, commit <short sha>. `/arch-doctor` reports anything edited since
Audit trail: .claude/audit-usage/ active — one report per skill or agent invocation from now on, by `/command` or by the model. GENESIS.md records this run itself, in tokens
Docker: Dockerfile + docker-compose.yml — <list: app, plus one entry per service `docker-architect` merged in step 4.10 for an active feature, e.g. "postgres (persistence-jpa)", "otel-collector (observability)"> — extend with `docker-architect` for anything a future use case adds
Local secrets: .env (untracked) holds DB_PASSWORD, read by docker compose and imported by application.yml for the host run; a fresh clone copies .env.example — <only with persistence-jpa; omit the line otherwise>
Observability UI: <omit this line entirely when `observability` is not active> none — the collector exports to `debug`, which writes spans and metrics to its own stdout and is not a dashboard. Run `/docker-architect` to add one: Jaeger (traces, one container) or Grafana + Tempo + Prometheus (traces and metrics, three)
Transport: <the first line of `transport-security-setup`'s report — topology A edge | B direct | C mutual TLS | C′ mesh> — HTTP/2 on, HSTS by <Spring Security | HstsHeaderFilter>, <ForwardedHeadersIT | TransportSecurityIT>
SonarQube: <the first line of `sonarqube-setup`'s report — existing server <url> | SonarCloud <org> | local container on http://localhost:9000> — scanner <v>, CI step <added | none>
MCP: <none — no server designed for this project yet | <n> server(s) copied to .mcp.json, see MCP-SETUP.md>
Build: <PASSED | FAILED: reason>
Docs: README.md (English, default) + README.pt-br.md — origin, blueprint, stack, skills/agents, this report
Next steps:
1. /use-case-design <first-use-case-name>
2. Install the architecture tests (ArchUnit) and wire up the coverage gate with the
`test-architect` skill as soon as business classes exist. Until then boundaries
are guaranteed by the `check` hook at edit time (inside Claude Code) and by CI's
`doctor gate`, which sweeps every source file against `.claude/forbidden-imports.txt`
(whoever wrote it)<, and by the POMs' `depends_on` (in the build) — only if
`layout: multi-module`>.
3. SonarQube — <local: `docker compose up -d sonarqube`, log in at http://localhost:9000
as admin/admin (a password change is forced), create a token under My Account →
Security, `export SONAR_TOKEN=…`, then `<./mvnw -B verify sonar:sonar | ./gradlew build sonar>`
| external: create the `SONAR_TOKEN` repository secret; CI analyses from the next push>… cortado aqui, o resto está no arquivo
## Applicable rules
Every rule in .claude/rules/ reaches the project through step 6.6 — ArchHook.java export copies them and rewrites their paths. None is read during generation: the part of each rule that a bootstrap step turns into a file is already inside the template that step copies. The table says which, so a rule change knows which template to follow.
| Rule | Embodied by | Step |
|---|---|---|
@.claude/rules/architecture-ddd.md | the blueprint's modules and depends_on, .claude/forbidden-imports.txt; its paths come from architecture_paths at export | 4, 5, 6.6 |
@.claude/rules/naming.md | the package names written into package-info.java | 4.7 |
@.claude/rules/code-quality.md | templates/checkstyle.xml.example — the mechanical half; the ArchUnit half is test-architect's | 4.6 |
@.claude/rules/lombok.md | templates/lombok.config.example | 4.8 |
@.claude/rules/logging.md | templates/logback-spring.xml.example | 4.9 |
@.claude/rules/testing.md | nothing here — the coverage gate is wired by test-architect | — |
@.claude/rules/error-handling.md · @.claude/rules/api-rest.md | nothing here — no exception family or handler is generated (4.5) | — |
A rule that changes what a template must say is followed by an edit to that template, not by a read here. If a step ever needs a rule that doesn't exist in rules/, create the rule file first — don't write it inside this skill.
## Why this is a skill and not an agent
Form 1 (auto-invocable skill): it is a long generation procedure with no interview of its own, and /init-project already isolates the verbose part inside the project-initializer agent. Form 3 was rejected for this file because the three reasons for an agent (preserve context, restrict tools, change model) are satisfied by that caller, not by this procedure — two nested agents would only add a second boundary to pass the blueprint across.
Runs on sonnet with effort: high: generation is template- and YAML-driven, and it runs inside project-initializer, also on sonnet. A skill's model inside a subagent is not documented, so the pin there is a declaration that agrees with the agent's (@.claude/decisions/0081-skill-model-required-per-class.md).
O que lê, escreve e recusa
O contrato da própria skill, do SKILL.md, no texto original.
Class
build — the territory is skill_classes.build's override for this skill in @.claude/schemas/extensions.json: the whole tree of the project being generated. It is the only skill with that reach, and it has it because the tree does not exist yet when it runs. ArchHook.java guard enforces it.
Unfiltered Bash
generation runs curl against the Initializr, tar, ./mvnw, java … export, grep and moves over a tree that does not exist yet — a scoped list would run to ~15 prefixes, and one command missing from it aborts the run halfway through. Writes stay under guard/guard bash, and a force push is blocked by guard bash (guard.force_push).
Reads before generating
Writes
(paths relative to the generated project, <project> in § Where the project is born, never this repository) — no other skill touches these files:
Chains
transport-security-setup in step 7.5 — the transport lines of application.yml, the HSTS filter, the transport IT and the Transport: paragraph are that skill's (and, for the edge service and the Dockerfile port, docker-architect's), even though they land in files this skill wrote. Chains** sonarqube-setup in step 8.4 — the sonar.* lines of the root build file, the workflow's analysis step and the sonarqube compose service are that skill's (and, for the service, docker-architect's), even though they land in files this skill wrote.
Hands off to
use-case-design, domain-modeling, persistence-architect, rest-api-architect, and test-architect, which design the first feature. The architecture tests and the coverage gate belong to test-architect's setup mode — this bootstrap deliberately doesn't generate them.
Does not write business code, and this is the § of the contract that says so
No .java beyond the package-info.java files, no .sql, no file in src/test/**. If a future step needs to emit a class, the right question is which layer skill owns that shape — not how to add one more exemplar here.
Fonte: SKILL.md § Contract, no nerviz e4cae27.
O que o guard faz com ela
build é a classe da skill, e cada skill dela tem território próprio. Quem chama decide qual território vale: sob o project-initializer, o do agent; da thread principal, nenhuma chamada passa. O texto à direita é o do próprio hook.
write_allow da classe project-bootstrap
- ✓
** uma chamada da thread principal com o /init-project aberto
A recusa sai com código 2 e a mensagem volta para o modelo.
Encadeada pelo project-initializer, as escritas dela levam o agent_type do agent e são julgadas por agent_classes.driver, **. Esta lista vale quando a skill roda num prompt próprio.
PreToolUse Skill(project-bootstrap) # agent_type: project-initializer (class driver) guard call exit 0 # a classed agent's call leaves the phase as it was PreToolUse Write(../bank-app/pom.xml) guard write exit 0 # judged by agent_classes.driver, write_allow: **
# phase open: init-project (class orchestrator) PreToolUse Skill(project-bootstrap) guard call exit 2 ❌ `project-bootstrap` is class `build` and `init-project` (class `orchestrator`) is open — a design run does not materialize files. Record what is missing in the partial and in the consolidated spec, finish the run, then invoke `/project-bootstrap` from a prompt of its own.
Onde ela fica no trilho
Cada card é um passo do /init-project. A entrevista roda na sessão principal; os passos com fundo colorido rodam dentro do project-initializer; o git-publish volta para a sessão principal, depois do audit genesis. Passos tracejados só rodam quando se aplicam.