Files
clean-architecture-backend-…/CLAUDE.md
T
DongHyeonkaandClaude Opus 5 e34519113b fix(ci): 실행되지 않거나 실패할 수 없던 CI 게이트 정상화
가장 큰 것: 문서화된 아키텍처 게이트가 규칙을 하나도 실행하지 않았다.
워크플로 7곳과 CLAUDE.md 가 --tests '*CleanArchitectureTest' 를 지정했으나
그 이름의 클래스는 존재하지 않는다. 4곳은 매칭 0건으로 하드 실패하고,
3곳은 다른 필터와 병기돼 아키텍처 규칙 0개를 돌고 초록으로 통과했다.
필터를 패키지 글롭 dev.caskeleton.bootstrap.architecture.* 로 교체했다.
*ArchitectureTest 글롭은 20개 중 12개만 잡고 ArchRuleDiscoveryContractTest 등
8개를 놓치므로 쓰지 않았다.

그 외:
- ci-gate-matrix 의 release_blocking 이 강제되지 않아 trivy-fs 가 빨개도
  release-gate 가 초록이던 것을 실제 의존으로 연결
- build-logic TestKit 이 어떤 CI 에서도 돌지 않던 것을 ci-quality-gates 에 연결
- jpa-next-* 3개, object-storage, fileserver-pr 의 실패할 수 없거나
  트리거되지 않던 잡 정리
- 릴리스 태그 네임스페이스 분열로 v* 태그가 web·websocket 게이트를
  건너뛰던 것 수정
- 워크플로 SHA 잠금 28개 재생성 (verify-gradle-wrapper.sh)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-16 16:52:44 +09:00

134 lines
7.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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. `verifyDocumentedLeafCount` fails the
build when a policy document states a leaf count that the registry does not agree with.
| 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 verifyCleanArchitectureDependencies --console=plain
./gradlew :app-bootstrap:test --tests 'dev.caskeleton.bootstrap.architecture.*' --console=plain
./gradlew verifyPublicPathSnapshot --console=plain
./gradlew verifyEnvKeys --console=plain
```
Use public-path and env-key checks only when their surfaces changed. Full `test` or `check` requires
the controller's workflow authorization.
## Advisory and reporting
Use dependency-DAG/topological reasoning and 35 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.