Available specs
!find docs/use-cases -mindepth 1 -maxdepth 1 -type d -name 'UC-*' 2>/dev/null | sort
Empty above → none yet, run /use-case-design first. (find, not an ls glob: under zsh an unmatched glob
aborts the command before any fallback runs.)
Target
$ARGUMENTS
REST API Architect
Designs how the use case is exposed over HTTP: what endpoints exist, what DTOs go
in and out, what status each result returns, what error body arises from each domain
exception, how the collection is paginated, and what OpenAPI has to document. What
domain-modeling left as an inbound port, this skill gives transport shape to.
Entry rule: without 10-dominio.md, there's nothing to expose. This skill reads
docs/use-cases/UC-NNN-<slug>/00-caso-de-uso.md and 10-dominio.md and treats them as
a contract. Without the domain partial, it stops and tells the caller to run
/domain-modeling — designing endpoints before the ports and exceptions exist produces
a contract that describes the screen, not the business.
Exit rule: writes no code. It emits 30-rest.md. The controller, the DTOs, the
mapper, and the ApiExceptionHandler come from the executor agent, which reads the
partial and the exemplars in templates/. Inherits D15 —
@.claude/decisions/0003-skill-domain-modeling.md — and the inconsistency P7 flagged is
closed in @.claude/decisions/0006-rest-api-architect-design.md.
Rule rule: rules don't live here. Verb semantics, status table, ProblemDetail,
traceId, pagination, idempotency, and what must be annotated in OpenAPI are
@.claude/rules/api-rest.md. This skill applies them and cites them; it doesn't
reproduce them.
How it's invoked
Two ways, and both matter: /rest-api-architect by hand, or chained by /new-feature
once that orchestrator exists. That's why it does not carry
disable-model-invocation — that field hides the skill from the model, and a skill the
model can't see is a skill the orchestrator can't call.
The guard against out-of-order firing isn't the frontmatter: it's the entry rule
above. Without the prior partial the skill stops and says what needs to run first.
Recorded in @.claude/decisions/0007-pipeline-skills-invocation.md.
Why this isn't a subagent
It's a procedure whose step 3 goes back to asking the user what no prior spec fixes —
what endpoints the client actually needs, what collection grows without bound, what
POST touches money or an external system. A subagent doesn't see the conversation.
Boundary with neighboring skills
The split is by moment and by artifact:
| Piece |
Acts when |
Produces |
use-case-design |
Before the domain exists |
00-caso-de-uso.md — boundary and canonical names |
domain-modeling |
After the parent spec |
10-dominio.md — aggregate, invariants, ports |
| this skill |
After the domain partial |
30-rest.md — including the schema requirements transport creates |
persistence-architect |
After this one |
20-persistencia.md — schema, reading this partial's schema requirements |
test-architect |
After all of them |
40-testes.md — pyramid, slices, data |
Doesn't collide with test-architect: this skill fixes the HTTP contract's cases
(status, errorCode, body shape); that one owns the strategy for testing the whole
use case. Different artifacts, different owners.
Doesn't collide with java-patterns, which refactors code that already exists from an
observed symptom. If the controller already exists and the problem is a growing if
chain, this isn't the right skill.
Procedure
Read the specs. 00-caso-de-uso.md and 10-dominio.md from the folder in
$ARGUMENTS. Without the second, stop. Extract: canonical names, inbound ports and
signatures, the exception table with errorCode, and the rows of the component
table marked Detailed by: rest-api-architect.
Survey what already exists. A resource may already have a controller, and the
use case may just be a new endpoint. The REUSE state from the parent spec rules over
intuition.
grep -rln "@RestController" --include='*.java' src/ 2>/dev/null
The inbound adapter's package isn't the same across every blueprint (adapter/in/rest
in one, infrastructure/rest in another): discover it via @RestController or via
packages.map, never from a path written from memory. An ls against a path this
project doesn't have returns empty and passes for "no controller yet".
Interview — only what the specs don't fix. AskUserQuestion, at most 4
questions per call. Don't re-ask what 00-caso-de-uso.md or 10-dominio.md already
answered.
| Axis |
Decides |
| What operations the client needs beyond the use case's own |
Whether there's a read GET, and how many endpoints come out of this |
| Does the collection grow without bound |
Whether it's paginated — and it is, unless proven otherwise |
Does the POST create a resource, move money, or touch an external system |
Whether Idempotency-Key is required |
| Is there concurrent writing on the same resource |
Whether ETag + If-Match come in (412 and 428) |
| Is the resource's existence sensitive information |
404 instead of 403, for the whole resource |
| Is the client internal or public |
Weight of operationId stability and the cost of a /v2 |
Fix the endpoints. One per line: method, path with explicit /api/v1, stable
operationId, the inbound port it serves, success status. Resources and verbs per
@.claude/rules/api-rest.md; class names per @.claude/rules/naming.md. An
endpoint with no inbound port behind it doesn't get written — it's a guessed
contract. OpenAPI documentation for each operation (@Operation, every
@ApiResponse, @Parameter with examples, request body example) is extracted into
its own composed annotation, suffix OpenApiDocs — one per operation — shape in
templates/OpenApiDocs.java.example. Both @Tag and the composed annotations, plus
the Spring mapping/binding annotations, go on a <Resource>Api contract interface
(templates/Api.java.example); the controller implements it and stays annotation-free
beyond @RestController and the base @RequestMapping.
Fix the DTOs. Input and output, field by field. DTO validation is shape
only; business rule stays in the aggregate and comes out as 422. No domain type in
the public signature, no aggregate serialized. Manual, static mapper
(templates/RestMapper.java.example).
Apply masking to the fields 10-dominio.md flagged sensitive. Any DTO field
that mirrors a field domain-modeling listed as a masking candidate gets
@MaskSensitiveData(maskedType = MaskedType.<X>) (@.claude/rules/logging.md §
Masking mechanism), and the DTO class implements LogMask. This isn't optional
because it "looks fine without it": GlobalHttpMethodLogAspect logs every request
and response DTO by default (opt-out, not opt-in) — a DTO that doesn't implement
LogMask logs the field raw the moment the endpoint runs.
Fix the error map. Each exception from 10-dominio.md to its status and its
errorCode, plus the structural errors that don't come from the domain (bean
validation, unreadable body), and the 500 with traceId. This table is the source
of block 5 — an exception that doesn't appear here won't have a test.
Cross-check shape validation against domain invariants before listing both. For
each row that comes from a domain invariant (not idempotency, not a structural
error), check whether step 5 already put a bean-validation annotation on the same
DTO field. If it did, the bean-validation 400 fires first and the domain's
errorCode is unreachable through this endpoint — mark the row accordingly (e.g.
"unreachable via this endpoint: intercepted by @NotBlank on <field>") instead of
listing it as if it were a scenario a contract test can actually hit. Don't write the
two as parallel, equally-reachable cases — a test written against the domain row
alone will assert a 422 that never happens.
Fix pagination and idempotency. Offset by default; cursor only when volume or
mutation of the set demands it. If the POST requires Idempotency-Key, declare
both halves: the structural one stays in the adapter
(templates/IdempotencyKeyInterceptor.java.example), and the key's table is
handed off to persistence-architect — name it in the partial, don't model it
here. Shape reference for that other half:
@.claude/skills/persistence-architect/templates/IdempotencyKeyTable.sql.example,
.../IdempotencyKeyStore.java.example, and .../IdempotentExecution.java.example.
The transaction shape is fixed by @.claude/rules/api-rest.md § Idempotency — don't
reopen it per use case: the controller calls IdempotentExecution, the command
carries no key, and 10-dominio.md's use case signature doesn't change for it.
Every Idempotency-Key endpoint, including the first one in the project, uses
@Idempotent + IdempotencyAspect (templates/IdempotencyAspect.java.example,
templates/IdempotencyKeyInterceptor.java.example's annotation-based variant,
Controller.java.example): it reuses the same IdempotentExecution/IdempotencyKeyPort
— no second port, no second vocabulary — and needs zero code in the controller beyond
the annotation. When idempotency_keys is NEW in the project (first time this
folder decided it's needed), this same pass names IdempotencyAspect in the
dependencies list alongside the table — there is no separate manual path to fall back
to for a lone first endpoint. (Decision .claude/decisions/0044-idempotent-first-endpoint.md:
the by-hand call this replaced cost ~30 lines of controller-side response
(de)serialization per endpoint, code the aspect already generalizes, for a cost —
reflection-based response-type lookup and a body-index argument — that's paid once
per project regardless of endpoint count.)
Write the schema requirements down. Block 4 closes with a Schema requirements
list: every table or column this transport needs that the domain didn't model — the
idempotency key table, first of all — or "none". persistence-architect runs after
this skill and reads that list in its first pass; a requirement left in prose here is a
second persistence pass later.
Fix the dependencies. springdoc, tracing bridge, and validation. Read pom.xml
first: a dependency already declared there keeps its version, and nothing is
resolved. Never a web search for a version. springdoc's
version isn't managed by the Spring Boot BOM: when it's absent, resolve it at runtime and confirm
compatibility with the project's Boot major —
references/best-practices-links.md § Resolving the springdoc version. No network,
ask. Never from memory (@CLAUDE.md, invariant 8). You don't edit pom.xml: you
declare, the executor applies.
Write the partial. docs/use-cases/UC-NNN-<slug>/30-rest.md, from
templates/rest-spec.md.example. Five blocks, all mandatory.
Report and stop. File path, divergences from 10-dominio.md, what was handed
off to another owner (idempotency table), and what's left for the folder to be
complete (40-testes.md). Don't invoke anyone.
What the partial contains
Five blocks. A block with no content is written as "none" — deleting it hides a
question nobody asked.
| Block |
Fixes |
Shape exemplar |
| Endpoints |
Method, path, operationId, port, success status |
Controller.java.example implementing Api.java.example |
| OpenAPI docs |
@Tag + @Operation, @ApiResponse per status, @Parameter with examples, request body example — one composed ...OpenApiDocs annotation per operation, all declared on the contract interface |
Api.java.example · OpenApiDocs.java.example |
| DTOs |
Input and output, fields, shape validation, translation, @MaskSensitiveData on sensitive fields |
Dtos.java.example · RestMapper.java.example |
| Error map |
Exception → status → errorCode; violations and traceId |
ApiExceptionHandler.java.example · error-responses.json.example |
| Pagination, idempotency and dependencies |
Mode and limits, both halves of the key, artifacts to add |
PageResponse.java.example · page-response.json.example · PageCriteria.java.example (the port's own pagination type — Pageable never crosses it) · IdempotencyKeyInterceptor.java.example · IdempotencyAspect.java.example (every @Idempotent endpoint, first one included) |
| Contract test cases |
Status, errorCode, and body shape per scenario |
@.claude/skills/test-architect/templates/ControllerTest.java.example |
The exemplars in templates/ are a shape reference, not files to copy. It's the
executor agent that reads them when generating code.
The contract test exemplar does not live here: test-code shape has a single owner,
and it's test-architect. This skill fixes the cases; the shape of the class that
verifies them is at
@.claude/skills/test-architect/templates/ControllerTest.java.example.
Contract
Reads docs/use-cases/UC-NNN-<slug>/00-caso-de-uso.md and 10-dominio.md
(mandatory — stops without the second), @.claude/rules/api-rest.md,
@.claude/rules/architecture-ddd.md (Adapters and Composition sections),
@.claude/rules/naming.md, @.claude/rules/error-handling.md,
@.claude/rules/code-quality.md, @.claude/rules/logging.md (masking mechanism for
DTO fields 10-dominio.md flagged sensitive), and the active blueprint's
packages.map. Also reads
20-persistencia.md when it exists (a resumed or hand-run case) — the table's expected
volume decides offset or cursor. In /new-feature this skill runs first, so the volume
comes from its own interview ("does the collection grow without bound") and is written in
block 4, where persistence-architect reads it instead of asking again.
Writes docs/use-cases/UC-NNN-<slug>/30-rest.md. Only that file.
Writes no Java code. The controller, the DTOs, the mapper, the interceptor, the
ApiExceptionHandler, and the contract tests come from the executor agent. It doesn't
edit pom.xml nor .github/workflows/** — the openapi.json diff job belongs to
project-bootstrap.
Doesn't decide the use case boundary (00-caso-de-uso.md), the domain model
(10-dominio.md), the schema or the idempotency key table (20-persistencia.md), nor
the test strategy (40-testes.md). Doesn't touch .claude/rules/**.
Doesn't collide with domain-modeling: that one declares the inbound port, this
one says over which HTTP it's called. The port's signature belongs to the other one; if
it needs to change, report the divergence instead of rewriting it.
1---2name: rest-api-architect3description: Designs the inbound REST adapter for an already-modeled use case — endpoints, DTOs, status codes, error map, pagination, idempotency, and OpenAPI — into the `30-rest.md` partial. Use when the request involves exposing a use case via HTTP, designing an endpoint or controller, deciding status and error body, paginating a collection, applying `Idempotency-Key`, or fixing the OpenAPI contract. Piece of the `/new-feature` pipeline: requires `10-dominio.md` in the given folder and stops without it.4---56## Available specs78!`find docs/use-cases -mindepth 1 -maxdepth 1 -type d -name 'UC-*' 2>/dev/null | sort`910Empty above → none yet, run `/use-case-design` first. (`find`, not an `ls` glob: under zsh an unmatched glob11aborts the command before any fallback runs.)1213## Target1415$ARGUMENTS1617---1819# REST API Architect2021Designs **how the use case is exposed over HTTP**: what endpoints exist, what DTOs go22in and out, what status each result returns, what error body arises from each domain23exception, how the collection is paginated, and what OpenAPI has to document. What24`domain-modeling` left as an inbound port, this skill gives transport shape to.2526**Entry rule: without `10-dominio.md`, there's nothing to expose.** This skill reads27`docs/use-cases/UC-NNN-<slug>/00-caso-de-uso.md` and `10-dominio.md` and treats them as28a contract. Without the domain partial, it stops and tells the caller to run29`/domain-modeling` — designing endpoints before the ports and exceptions exist produces30a contract that describes the screen, not the business.3132**Exit rule: writes no code.** It emits `30-rest.md`. The controller, the DTOs, the33mapper, and the `ApiExceptionHandler` come from the executor agent, which reads the34partial and the exemplars in `templates/`. Inherits D15 —35`@.claude/decisions/0003-skill-domain-modeling.md` — and the inconsistency P7 flagged is36closed in `@.claude/decisions/0006-rest-api-architect-design.md`.3738**Rule rule: rules don't live here.** Verb semantics, status table, `ProblemDetail`,39`traceId`, pagination, idempotency, and what must be annotated in OpenAPI are40`@.claude/rules/api-rest.md`. This skill applies them and cites them; it doesn't41reproduce them.4243## How it's invoked4445Two ways, and both matter: `/rest-api-architect` by hand, or chained by `/new-feature`46once that orchestrator exists. That's why it does **not** carry47`disable-model-invocation` — that field hides the skill from the model, and a skill the48model can't see is a skill the orchestrator can't call.4950The guard against out-of-order firing isn't the frontmatter: it's the **entry rule**51above. Without the prior partial the skill stops and says what needs to run first.52Recorded in `@.claude/decisions/0007-pipeline-skills-invocation.md`.5354## Why this isn't a subagent5556It's a procedure whose step 3 goes back to asking the user what no prior spec fixes —57what endpoints the client actually needs, what collection grows without bound, what58`POST` touches money or an external system. A subagent doesn't see the conversation.5960## Boundary with neighboring skills6162The split is by **moment and by artifact**:6364| Piece | Acts when | Produces |65|---|---|---|66| `use-case-design` | Before the domain exists | `00-caso-de-uso.md` — boundary and canonical names |67| `domain-modeling` | After the parent spec | `10-dominio.md` — aggregate, invariants, ports |68| **this skill** | After the domain partial | `30-rest.md` — including the schema requirements transport creates |69| `persistence-architect` | After this one | `20-persistencia.md` — schema, reading this partial's schema requirements |70| `test-architect` | After all of them | `40-testes.md` — pyramid, slices, data |7172Doesn't collide with `test-architect`: this skill fixes the HTTP contract's **cases**73(status, `errorCode`, body shape); that one owns the **strategy** for testing the whole74use case. Different artifacts, different owners.7576Doesn't collide with `java-patterns`, which refactors code that already exists from an77observed symptom. If the controller already exists and the problem is a growing `if`78chain, this isn't the right skill.7980## Procedure81821. **Read the specs.** `00-caso-de-uso.md` and `10-dominio.md` from the folder in83 `$ARGUMENTS`. Without the second, stop. Extract: canonical names, inbound ports and84 signatures, the exception table with `errorCode`, and the rows of the component85 table marked `Detailed by: rest-api-architect`.86872. **Survey what already exists.** A resource may already have a controller, and the88 use case may just be a new endpoint. The REUSE state from the parent spec rules over89 intuition.9091 ```bash92 grep -rln "@RestController" --include='*.java' src/ 2>/dev/null93 ```9495 The inbound adapter's package isn't the same across every blueprint (`adapter/in/rest`96 in one, `infrastructure/rest` in another): discover it via `@RestController` or via97 `packages.map`, never from a path written from memory. An `ls` against a path this98 project doesn't have returns empty and passes for "no controller yet".991003. **Interview — only what the specs don't fix.** `AskUserQuestion`, at most 4101 questions per call. Don't re-ask what `00-caso-de-uso.md` or `10-dominio.md` already102 answered.103104 | Axis | Decides |105 |---|---|106 | What operations the client needs beyond the use case's own | Whether there's a read `GET`, and how many endpoints come out of this |107 | Does the collection grow without bound | Whether it's paginated — and it is, unless proven otherwise |108 | Does the `POST` create a resource, move money, or touch an external system | Whether `Idempotency-Key` is required |109 | Is there concurrent writing on the same resource | Whether `ETag` + `If-Match` come in (412 and 428) |110 | Is the resource's existence sensitive information | 404 instead of 403, for the whole resource |111 | Is the client internal or public | Weight of `operationId` stability and the cost of a `/v2` |1121134. **Fix the endpoints.** One per line: method, path with explicit `/api/v1`, stable114 `operationId`, the inbound port it serves, success status. Resources and verbs per115 `@.claude/rules/api-rest.md`; class names per `@.claude/rules/naming.md`. An116 endpoint with no inbound port behind it doesn't get written — it's a guessed117 contract. OpenAPI documentation for each operation (`@Operation`, every118 `@ApiResponse`, `@Parameter` with examples, request body example) is extracted into119 its own composed annotation, suffix `OpenApiDocs` — one per operation — shape in120 `templates/OpenApiDocs.java.example`. Both `@Tag` and the composed annotations, plus121 the Spring mapping/binding annotations, go on a `<Resource>Api` contract interface122 (`templates/Api.java.example`); the controller implements it and stays annotation-free123 beyond `@RestController` and the base `@RequestMapping`.1241255. **Fix the DTOs.** Input and output, field by field. DTO validation is **shape**126 only; business rule stays in the aggregate and comes out as 422. No domain type in127 the public signature, no aggregate serialized. Manual, static mapper128 (`templates/RestMapper.java.example`).129130 **Apply masking to the fields `10-dominio.md` flagged sensitive.** Any DTO field131 that mirrors a field `domain-modeling` listed as a masking candidate gets132 `@MaskSensitiveData(maskedType = MaskedType.<X>)` (`@.claude/rules/logging.md` §133 Masking mechanism), and the DTO class implements `LogMask`. This isn't optional134 because it "looks fine without it": `GlobalHttpMethodLogAspect` logs every request135 and response DTO by default (opt-out, not opt-in) — a DTO that doesn't implement136 `LogMask` logs the field raw the moment the endpoint runs.1371386. **Fix the error map.** Each exception from `10-dominio.md` to its status and its139 `errorCode`, plus the structural errors that don't come from the domain (bean140 validation, unreadable body), and the 500 with `traceId`. This table is the source141 of block 5 — an exception that doesn't appear here won't have a test.142143 **Cross-check shape validation against domain invariants before listing both.** For144 each row that comes from a domain invariant (not idempotency, not a structural145 error), check whether step 5 already put a bean-validation annotation on the same146 DTO field. If it did, the bean-validation 400 fires first and the domain's147 `errorCode` is unreachable through this endpoint — mark the row accordingly (e.g.148 "unreachable via this endpoint: intercepted by `@NotBlank` on `<field>`") instead of149 listing it as if it were a scenario a contract test can actually hit. Don't write the150 two as parallel, equally-reachable cases — a test written against the domain row151 alone will assert a 422 that never happens.1521537. **Fix pagination and idempotency.** Offset by default; cursor only when volume or154 mutation of the set demands it. If the `POST` requires `Idempotency-Key`, declare155 both halves: the structural one stays in the adapter156 (`templates/IdempotencyKeyInterceptor.java.example`), and the key's table **is157 handed off to `persistence-architect`** — name it in the partial, don't model it158 here. Shape reference for that other half:159 `@.claude/skills/persistence-architect/templates/IdempotencyKeyTable.sql.example`,160 `.../IdempotencyKeyStore.java.example`, and `.../IdempotentExecution.java.example`.161 The transaction shape is fixed by `@.claude/rules/api-rest.md` § Idempotency — don't162 reopen it per use case: the controller calls `IdempotentExecution`, the command163 carries no key, and `10-dominio.md`'s use case signature doesn't change for it.164165 **Every `Idempotency-Key` endpoint, including the first one in the project, uses166 `@Idempotent` + `IdempotencyAspect`** (`templates/IdempotencyAspect.java.example`,167 `templates/IdempotencyKeyInterceptor.java.example`'s annotation-based variant,168 `Controller.java.example`): it reuses the same `IdempotentExecution`/`IdempotencyKeyPort`169 — no second port, no second vocabulary — and needs zero code in the controller beyond170 the annotation. When `idempotency_keys` is **NEW** in the project (first time this171 folder decided it's needed), this same pass names `IdempotencyAspect` in the172 dependencies list alongside the table — there is no separate manual path to fall back173 to for a lone first endpoint. (Decision `.claude/decisions/0044-idempotent-first-endpoint.md`:174 the by-hand call this replaced cost ~30 lines of controller-side response175 (de)serialization per endpoint, code the aspect already generalizes, for a cost —176 reflection-based response-type lookup and a body-index argument — that's paid once177 per project regardless of endpoint count.)178179 **Write the schema requirements down.** Block 4 closes with a `Schema requirements`180 list: every table or column this transport needs that the domain didn't model — the181 idempotency key table, first of all — or "none". `persistence-architect` runs after182 this skill and reads that list in its first pass; a requirement left in prose here is a183 second persistence pass later.1848. **Fix the dependencies.** springdoc, tracing bridge, and validation. **Read `pom.xml`185 first**: a dependency already declared there keeps its version, and nothing is186 resolved. Never a web search for a version. springdoc's187 version **isn't managed by the Spring Boot BOM**: when it's absent, resolve it at runtime and confirm188 compatibility with the project's Boot major —189 `references/best-practices-links.md` § Resolving the springdoc version. No network,190 ask. Never from memory (`@CLAUDE.md`, invariant 8). You don't edit `pom.xml`: you191 declare, the executor applies.1921939. **Write the partial.** `docs/use-cases/UC-NNN-<slug>/30-rest.md`, from194 `templates/rest-spec.md.example`. Five blocks, all mandatory.19519610. **Report and stop.** File path, divergences from `10-dominio.md`, what was handed197 off to another owner (idempotency table), and what's left for the folder to be198 complete (`40-testes.md`). Don't invoke anyone.199200## What the partial contains201202Five blocks. A block with no content is written as "none" — deleting it hides a203question nobody asked.204205| Block | Fixes | Shape exemplar |206|---|---|---|207| Endpoints | Method, path, `operationId`, port, success status | `Controller.java.example` implementing `Api.java.example` |208| OpenAPI docs | `@Tag` + `@Operation`, `@ApiResponse` per status, `@Parameter` with examples, request body example — one composed `...OpenApiDocs` annotation per operation, all declared on the contract interface | `Api.java.example` · `OpenApiDocs.java.example` |209| DTOs | Input and output, fields, shape validation, translation, `@MaskSensitiveData` on sensitive fields | `Dtos.java.example` · `RestMapper.java.example` |210| Error map | Exception → status → `errorCode`; `violations` and `traceId` | `ApiExceptionHandler.java.example` · `error-responses.json.example` |211| Pagination, idempotency and dependencies | Mode and limits, both halves of the key, artifacts to add | `PageResponse.java.example` · `page-response.json.example` · `PageCriteria.java.example` (the port's own pagination type — `Pageable` never crosses it) · `IdempotencyKeyInterceptor.java.example` · `IdempotencyAspect.java.example` (every `@Idempotent` endpoint, first one included) |212| Contract test cases | Status, `errorCode`, and body shape per scenario | `@.claude/skills/test-architect/templates/ControllerTest.java.example` |213214The exemplars in `templates/` are a **shape reference**, not files to copy. It's the215executor agent that reads them when generating code.216217The contract test exemplar does **not** live here: test-code shape has a single owner,218and it's `test-architect`. This skill fixes the cases; the shape of the class that219verifies them is at220`@.claude/skills/test-architect/templates/ControllerTest.java.example`.221222## Contract223224**Reads** `docs/use-cases/UC-NNN-<slug>/00-caso-de-uso.md` and `10-dominio.md`225(mandatory — stops without the second), `@.claude/rules/api-rest.md`,226`@.claude/rules/architecture-ddd.md` (Adapters and Composition sections),227`@.claude/rules/naming.md`, `@.claude/rules/error-handling.md`,228`@.claude/rules/code-quality.md`, `@.claude/rules/logging.md` (masking mechanism for229DTO fields `10-dominio.md` flagged sensitive), and the active blueprint's230`packages.map`. Also reads231`20-persistencia.md` when it exists (a resumed or hand-run case) — the table's expected232volume decides offset or cursor. In `/new-feature` this skill runs first, so the volume233comes from its own interview ("does the collection grow without bound") and is written in234block 4, where `persistence-architect` reads it instead of asking again.235236**Writes** `docs/use-cases/UC-NNN-<slug>/30-rest.md`. Only that file.237238**Writes no Java code.** The controller, the DTOs, the mapper, the interceptor, the239`ApiExceptionHandler`, and the contract tests come from the executor agent. It doesn't240edit `pom.xml` nor `.github/workflows/**` — the `openapi.json` diff job belongs to241`project-bootstrap`.242243**Doesn't decide** the use case boundary (`00-caso-de-uso.md`), the domain model244(`10-dominio.md`), the schema or the idempotency key table (`20-persistencia.md`), nor245the test strategy (`40-testes.md`). Doesn't touch `.claude/rules/**`.246247**Doesn't collide with `domain-modeling`**: that one declares the inbound port, this248one says over which HTTP it's called. The port's signature belongs to the other one; if249it needs to change, report the divergence instead of rewriting it.