외부 리뷰("현재 상태를 유지하기 위한 검증이 너무 많고, 그 검증 자체를
다시 검증하는 구조까지 생겼다")를 설계 문서로 정리하고 코드로 반영한다.
설계·판단 근거는 docs/superpowers/specs/2026-09-16-verification-surface-reduction-design.md.
삭제
- .github/ci-gate-matrix.yml(1,025줄) + verify-gate-matrix.sh(568줄):
Gradle task graph와 workflow graph에 이미 있는 정보의 3중 복제
- verify-gradle-wrapper.sh(799줄): workflow 바이트 해시 잠금.
wrapper 검증은 gradle/actions/wrapper-validation(full SHA 핀)에 위임
- DeveloperExperienceContractTest 등의 CI YAML mutation 테스트:
애플리케이션 test suite가 GitHub Actions YAML 파서를 검증하던 계층 역전
- 문서 drift 파서: verifyReadmeCommands, verifyRunbookReferences,
verifyDocumentedLeafCount, verifyTestSourceSetRegistry
- 빈 레지스트리를 지키던 커스텀 YAML 파서: verifyTrivyignore,
verifyQuarantineSunset, flaky-quarantine.yaml
- verifyConfigurationPropertiesProcessor, verifyOneTypePerFile:
각각 ca.spring-config convention과 Checkstyle OneTopLevelClass가 대체
- 정상 입력으로도 성공할 수 없던 messaging always-fail task
- ModuleRegistry의 JSON 필드 집합 정확 일치, sample-portfolio negative guard
이동
- java/quality/spring 공통 설정을 configure(subprojects) 블록에서
ca.java-conventions / ca.quality-conventions / ca.java-library /
ca.spring-library convention plugin으로
- 아키텍처 검증을 ca.architecture로, JPA·messaging qualification을
gradle/qualification/ 아래로, verifyEnvKeys를 :app-bootstrap 소유로
완화
- Git revision은 releaseCheck·아카이브 생성에서만 요구. 일반 빌드는 SNAPSHOT
- SpotBugs/FindSecBugs는 로컬 check에서 빼고 qualityCheck 레인으로
task 계층
- leaf check는 그 leaf만. architectureCheck / qualityCheck /
configContractCheck / integrationCheck / ci / releaseCheck로 이름 분리
CI
- _reusable-gradle.yml 신규. checkout + wrapper validation + JDK/캐시 공통화
- fileserver-release.yml -> fileserver-certification.yml (CD가 아니라 certification)
- GitHub Actions = CI + artifact, Argo CD = CD 경계를 docs/ci-cd/boundary.md로 고정
순증감 +3,274 / -7,483.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
142 lines
8.0 KiB
Markdown
142 lines
8.0 KiB
Markdown
# CLAUDE.md
|
||
|
||
Repository guidance for the Java 21 + Spring Boot 4.0.8 Clean Architecture template.
|
||
|
||
## Prime Directive
|
||
|
||
Preserve architecture before optimizing for speed. The following eight HARD-STOP conditions are a
|
||
synchronized summary of the canonical local policy in `AGENTS.md`:
|
||
|
||
1. `domain-core` gains framework, transport, database, or cloud dependencies.
|
||
2. A controller directly uses a repository, Spring Data interface, or persistence entity.
|
||
3. An inbound DTO leaks into `application-core` or `domain-core`.
|
||
4. Business rules move into mappers, filters, configuration, settings, or controllers.
|
||
5. Project dependencies violate `src/config/architecture/modules.json` or the Gradle dependency
|
||
gate.
|
||
6. Completion is claimed without the relevant verification or a named reason it could not run.
|
||
7. A repository/corpus conclusion is made without evidence proportional to its scope and risk.
|
||
8. Non-trivial work closes without the required LLM Wiki capture or a reported capture block.
|
||
|
||
If this summary drifts from `AGENTS.md`, `AGENTS.md` wins and this summary must be resynchronized.
|
||
|
||
## Gradle policy authorities
|
||
|
||
- `src/config/architecture/modules.json`: every registered leaf identity, repository-relative source
|
||
paths, Gradle paths, allowed production project dependency edges, and the exact runtime
|
||
memberships of both composition roots. The registry owns the leaf list and its size; no document
|
||
restates the count, because a number written in prose drifts the moment a leaf is added.
|
||
- `src/settings.gradle`: fail-closed registry validation, project inclusion, and directory mapping.
|
||
- `src/build.gradle`: `verifyCleanArchitectureDependencies` and the other architecture-wide
|
||
verification tasks.
|
||
|
||
Commit policy is `human-only`: agents do not stage, commit, amend, or push implementation changes.
|
||
|
||
## Proportional workflow
|
||
|
||
- Low risk: work in the owning leaf, follow its nearest guidance, and run the focused check.
|
||
- Medium risk: use the relevant Superpowers design, planning, TDD, debugging, and review workflows
|
||
in proportion to the affected boundaries.
|
||
- High risk: make architecture and behavior decisions explicit, use staged architecture/spec/quality
|
||
review, and run architecture-wide verification authorized by the task.
|
||
|
||
Risk comes from change surface and runtime, security, data, or public-contract impact, not file
|
||
count.
|
||
|
||
## Module families
|
||
|
||
`src/config/architecture/modules.json` owns the complete leaf list. Root guidance summarizes
|
||
families; the nearest `src/**/CLAUDE.md` owns local rules. No task enforces this any more:
|
||
`verifyDocumentedLeafCount` was deleted along with the other documentation-drift parsers. A stated
|
||
count that disagrees with the registry is a defect, not a build failure — so do not state one.
|
||
|
||
| Family | Responsibility | Stable dependency direction |
|
||
| --- | --- | --- |
|
||
| `domain-core` | Pure domain model, invariants, events, ports | Java stdlib and registered value-only contracts |
|
||
| `application-core` | Commands, use cases, application policies, transaction ports | `domain-core`, `shared-contract` |
|
||
| `adapter:inbound:*` | HTTP, gRPC, GraphQL, WebSocket transport boundaries | application/domain/shared contracts |
|
||
| `adapter:outbound:persistence-*` | JPA/PostgreSQL and MongoDB persistence adapters | application/domain/shared contracts as registered |
|
||
| `adapter:outbound:*` | support, messaging, cache, notification, storage, file, HTTP client, identifier capabilities | application/domain/shared and registered support edge |
|
||
| `shared-contract` | Skeleton-wide operational contracts | Java stdlib only |
|
||
| `messaging:*` | Vendored messaging platform: a product with its own API, SPI, adapters and composition boundary, not a layer of this application | `messaging:*` only — it depends on no `domain-core`, `application-core`, or `shared-contract` type |
|
||
| `sample-portfolio` | Fixture/reference consumer | registered runtime leaves; never a production dependency |
|
||
| `app-bootstrap` | Spring Boot entrypoint and composition root | registered runtime leaves |
|
||
|
||
The `messaging:*` family is the one entry that is not a Clean Architecture layer, and it is listed so
|
||
that the exception is stated rather than inferred from a directory. It is a vendored library — its
|
||
own `*-api` leaves are its ports, its broker leaves are its adapters, its starter is its composition
|
||
root — and the messaging module review (`docs/reviews/2026-08-14-messaging-module-code-review.md`
|
||
MSG-023 §6.2) chose that layout deliberately over folding it into `adapter:outbound:*`. This
|
||
application is supposed to reach it the way it reaches any library: through an application-owned port
|
||
satisfied by an anti-corruption bridge in `adapter:outbound:messaging`. That bridge does not exist
|
||
yet (MSG-015), so today the composition root wires the starter directly; `src/messaging/CLAUDE.md`
|
||
holds the detail.
|
||
|
||
Never infer an individual leaf's Gradle path, allowed dependency, or test command from this table.
|
||
Read its `gradle_path`, `allowed_dependencies`, and `runtime_memberships` from
|
||
`src/config/architecture/modules.json`; derive the focused test from that Gradle path.
|
||
|
||
## Layer workflow
|
||
|
||
For a full use case, work in this order:
|
||
|
||
```text
|
||
domain-core
|
||
-> application-core
|
||
-> adapter:outbound:* (or persistence/identifier)
|
||
-> adapter:inbound:*
|
||
-> app-bootstrap wiring
|
||
```
|
||
|
||
Layer-only work stays inside that registered leaf plus its tests. If a required fix crosses a layer
|
||
or writable scope, stop and request context rather than expanding silently.
|
||
|
||
## Testing
|
||
|
||
- `domain-core`: pure JUnit unit tests.
|
||
- `application-core`: use-case tests with hand-rolled fakes; no web or persistence context.
|
||
- inbound adapters: focused transport slice/contract tests.
|
||
- persistence adapters: mapping/port contract tests; use a real datastore only when vendor semantics
|
||
require it.
|
||
- other outbound adapters: port contract tests with fake external systems; no real network.
|
||
- identifier: pure deterministic unit tests.
|
||
- bootstrap/settings: binding, validation, wiring, and architecture tests.
|
||
|
||
From `src/`, read the owning leaf's `gradle_path` from
|
||
`config/architecture/modules.json` and run `./gradlew <gradle-path>:test --console=plain`.
|
||
Architecture-wide commands:
|
||
|
||
```bash
|
||
./gradlew architectureCheck --console=plain
|
||
./gradlew :app-bootstrap:test --tests 'dev.caskeleton.bootstrap.architecture.*' --console=plain
|
||
./gradlew verifyPublicPathSnapshot --console=plain
|
||
./gradlew :app-bootstrap:verifyEnvKeys --console=plain
|
||
```
|
||
|
||
A leaf's `check` covers that leaf only — compile, its tests, Spotless, Checkstyle, Error Prone.
|
||
Repository-wide questions have their own names: `architectureCheck` (dependency direction, runtime
|
||
membership, application-core purity, Git-carryable sources), `qualityCheck` (SpotBugs, FindSecBugs),
|
||
`configContractCheck` (the environment contract), `integrationCheck` (the declared strict test
|
||
lanes). `ci` is check + architectureCheck + qualityCheck + configContractCheck; `releaseCheck` adds
|
||
provenance, archive hygiene and the public-path snapshot.
|
||
|
||
Use public-path and env-key checks only when their surfaces changed. Full `test`, `check` or `ci`
|
||
requires the controller's workflow authorization.
|
||
|
||
## Advisory and reporting
|
||
|
||
Use dependency-DAG/topological reasoning and 3–5 materially distinct alternatives when that many
|
||
exist; use fewer when the option set is smaller. Judgment findings include falsifiable assumptions
|
||
and counterarguments. Deterministic compile, dependency, secret, or failed-test findings use
|
||
Rule → Evidence → Fix without manufactured debate.
|
||
|
||
Citation verification is profile-based: none for `review-lite`, blocking citations for
|
||
`review-standard`, and all material citations for `audit-deep`/`regulated`. Durable reports are
|
||
triggered by high risk, at least 3 blocking findings, an architecture decision, explicit user request,
|
||
or the regulated profile. Otherwise a concise result is allowed.
|
||
|
||
## LLM Wiki capture
|
||
|
||
For non-trivial implementation or workflow changes, use the exact vault path and capture sequence in
|
||
`AGENTS.md`. If the controller explicitly excludes wiki writes for a dispatched task, report the
|
||
handoff instead of writing outside scope.
|