Files
llm-wiki/raw/project-notes/ca-skeleton-operational-contract.md
T

2891 lines
234 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.
---
title: CA Skeleton Operational Contract
source_type: project-note
status: raw
confidence: unknown
tags: [project-note, ca-skeleton, ca-tmpl, clean-architecture, observability, error-handling]
related_projects: [ca-skeleton, ca-tmpl]
last_reviewed: 2026-05-26
diagrams: [ca-skeleton/architecture-modules-2026-05-26, ca-skeleton/architecture-runtime-topology-2026-05-26, ca-skeleton/sequence-request-flow-mermaid, ca-skeleton/sequence-outbox-publish-mermaid, ca-skeleton/sequence-tenant-context-mermaid]
architecture_review: 2026-05-26
status_label: active
project_revision: 1
url:
semantic_surface_exclusions:
- artifact-registry|legacy hub has no project-local Artifact Registry; harness/source/typed-contracts.json is authoritative until migration
- contract-gate-registry|legacy hub has no project-local Contract/Gate Registry; harness/source/typed-contracts.json is authoritative until migration
- flow-stage-registry|legacy hub has no project-local Flow/Stage Registry; harness/source/typed-contracts.json is authoritative until migration
---
# CA Skeleton Operational Contract
> 이 문서는 도메인/비즈니스 로직을 제거한 Clean Architecture skeleton에서 기본 제공해야 하는 운영 실패/관측성/경계 검증 계약의 canonical SSOT입니다.
> Phase A/B/C1/D1/D2 모두 2026-05-22 완료. 본 문서는 ca-tmpl 운영 계약의 canonical SSOT. Phase C2는 2026-05-27 `feature-skeleton-package-blueprint-contract` 범위에서 일부 진입: package/module blueprint와 architecture guardrail은 별도 ca-tmpl git repo에서 local verification 완료. 나머지 registry/generated constants/sample fixture/outbox/security 등 Phase C2 항목은 계속 pending.
>
> **경로 표기 규약**: 본 문서가 reference하는 `ca-tmpl/docs/...` 경로는 별도 git repo (`/home/donghyeon/workspace/ca-tmpl/`)의 `docs/` 디렉터리를 의미. registry yaml과 runbook stub은 운영 artifact라 LLM Wiki(`wiki/projects/`)에 두지 않고 ca-tmpl repo에 위치. 본 canonical contract 문서만 LLM Wiki에 잔존.
>
> 자세한 phase 진척과 closure는 §28 Review Remediation Ledger 참조.
---
## 1. 목표
이 skeleton의 목표는 많은 adapter를 미리 구현하는 것이 아닙니다.
```text
어떤 adapter를 붙여도
같은 방식으로 실패를 분류하고
같은 방식으로 로그와 trace를 남기며
같은 방식으로 응답을 반환하고
같은 테스트 계약으로 깨짐을 감지하는 구조
```
도메인/비즈니스 로직은 제거합니다. 대신 운영 실패 분류, 경계 validation, mapper, structured response, structured logging, distributed tracing, env-driven configuration, repository access permission, adapter failure contract, API schema, transaction/concurrency, runtime lifecycle, sample domain fixture, domain onboarding, use case/port contract, domain modeling guardrails, business rule validation, domain event/outbox, metrics/alerting, secret/config source, management endpoint security, tenant policy, file/resource handling, cache consistency, background job/async, API compatibility, CI quality gate, build/release supply chain, container runtime, operational runbook, data retention/privacy, developer experience 기준을 기본 제공해야 합니다.
<!-- section-id: implementation-boundaries -->
## 2. 하지 않는 것
- `ProblemDetail` 사용 안 함. 자체 structured envelope 응답을 사용.
- 특정 비즈니스 도메인 예외를 기본 제공하지 않음.
- 단, skeleton 계약 검증을 위한 sample domain fixture는 둠. 이 sample은 비즈니스 기능이 아니라 contract 검증 도구임.
- Kafka/Redis/Slack/Google Email을 기본 dependency로 무겁게 탑재하지 않음.
- raw exception, SQL, token, request/response body를 클라이언트 응답이나 기본 로그에 노출하지 않음.
- `wiki/interview/`, `wiki/portfolio/`, `wiki/blog/`로 직접 파생하지 않음. 먼저 `wiki/projects/` canonical 문서로 승급.
## 3. Structured API Response Contract
성공 응답:
```text
success: true
data: <payload>
meta.requestId
meta.traceId
meta.correlationId
```
실패 응답:
```text
success: false
error.code
error.category
error.message
error.retryable
error.details
meta.requestId
meta.traceId
meta.correlationId
```
`error.details`는 validation field error처럼 클라이언트가 수정할 수 있는 안전한 정보만 담습니다.
클라이언트 응답에 금지:
- exception class name
- stack trace
- SQL / SQL parameter
- token / password / secret
- raw request body
- raw response body
- upstream raw error body
- internal dependency endpoint
## 4. Boundary Validation & Mapper Contract
모든 경계는 mapper와 validation 책임을 가집니다.
### request -> application
- HTTP DTO validation 수행.
- malformed body, missing parameter, type mismatch, unsupported media type 분류.
- request DTO를 application command/query로 변환하는 mapper 필수.
- controller에서 domain object 직접 생성 금지.
### application -> domain
- command/query invariant 검증.
- use case policy 검증.
- repository access capability 검증.
- domain에는 normalized input만 전달.
### domain -> application
- domain invariant violation은 application error로 번역.
- domain object를 response DTO로 직접 노출 금지.
### application -> response
- response mapper에서 public field만 노출.
- nullable / empty / default value 정책을 mapper 책임으로 둠.
- internal diagnostic context를 response payload에 섞지 않음.
### filter / interceptor
- requestId, traceId, correlationId 생성/전파.
- MDC key 초기화와 정리.
- response header propagation.
- filter에서 business error를 생성하지 않음.
## 5. Exception Ownership Contract
### presentation
- Spring MVC 기본 예외 처리.
- validation, authentication, authorization, access denied 처리.
- unreadable body, unsupported media type, no handler, type mismatch 처리.
- client-safe structured response 생성.
### application
- use case policy violation 처리.
- repository access permission violation 처리.
- external dependency result 해석.
- domain exception을 application error로 번역.
### domain
- business invariant violation만 표현.
- infrastructure exception, HTTP/JPA/Security exception을 알지 않음.
### infrastructure
- DB/JPA, outbound HTTP, cache, messaging, notification provider 예외를 operational error로 변환.
- raw exception이 presentation까지 새면 contract 위반.
## 6. Operational Error Category
기본 category (**`error.category` enum — foundation SSOT, 10개**, [[raw/branch-notes/feature-operational-error-observability-foundation]] D10):
- `VALIDATION`
- `AUTH`
- `AUTHZ`
- `NOT_FOUND`
- `CONFLICT`
- `RATE_LIMIT`
- `TRANSIENT_DEPENDENCY`
- `PERMANENT_DEPENDENCY`
- `DATA_INTEGRITY`
- `INTERNAL`
> **2026-06-01 정합 (F1)**: 이전 13-category 목록(`AUTHENTICATION`/`AUTHORIZATION`/`PERSISTENCE`/`DEPENDENCY`/`SECURITY`/`MESSAGE`/`CACHE`/`NOTIFICATION` 포함)은 **stale** 이었다. §21 Error Registry 요약(L810/L814) 의 resolved 10-enum 및 foundation branch D10 과 §8 Structured Log 가 모두 10-enum 을 사용하므로 본 §6 을 정합. 명명 매핑(§21 L814, "Phase A 4차 audit Conflict 13 해소"): `AUTHENTICATION→AUTH`, `AUTHORIZATION→AUTHZ`, `PERSISTENCE→{DATA_INTEGRITY, TRANSIENT_DEPENDENCY}`, `DEPENDENCY→{TRANSIENT_DEPENDENCY, PERMANENT_DEPENDENCY}`, `SECURITY→AUTHZ`(repository access denial), `MESSAGE/CACHE/NOTIFICATION→TRANSIENT_DEPENDENCY`(transient infra failure). **per-code 의 정확한 category 는 `ca-tmpl/docs/registries/error-codes.yaml` 가 authoritative** (§21 category 분포: TRANSIENT_DEPENDENCY 15 · AUTH 8 · INTERNAL 8 · VALIDATION 6 · CONFLICT 4 · AUTHZ 3 · DATA_INTEGRITY 3 · RATE_LIMIT 1 · PERMANENT_DEPENDENCY 1 · NOT_FOUND 0).
기본 code (각 code 의 category 는 위 10-enum + error-codes.yaml authoritative — 아래 목록의 옛 category 명은 위 매핑으로 해석):
- `VALIDATION_FAILED`
- `MAPPING_FAILED` (2026-05-29 추가, `VALIDATION` category — mapper-internal 실패: record canonical constructor `IllegalArgumentException` wrap, MapStruct NPE, ACL normalization 실패. `MappingException` 으로 명시적 wrap 필수. 도출: [[raw/branch-notes/feature-boundary-validation-mapping-contract]] D10 + §구현 가이드 §3)
- `BATCH_PARTIAL_FAILURE` (2026-05-29 추가, `VALIDATION` category — bulk endpoint 의 부분/전체 실패. HTTP 200 + envelope.success=false. `BulkEnvelope.partial(...)` 라우팅. 도출: [[raw/branch-notes/feature-boundary-validation-mapping-contract]] D14 + §구현 가이드 §1)
- `AUTHENTICATION_FAILED`
- `AUTHORIZATION_FAILED`
- `RESOURCE_NOT_FOUND`
- `CONFLICT`
- `DATA_INTEGRITY_VIOLATION`
- `DB_UNAVAILABLE`
- `DB_QUERY_FAILED`
- `EXTERNAL_BAD_REQUEST`
- `EXTERNAL_UNAUTHORIZED`
- `EXTERNAL_FORBIDDEN`
- `EXTERNAL_TIMEOUT`
- `EXTERNAL_UNAVAILABLE`
- `MESSAGE_PUBLISH_FAILED`
- `CACHE_UNAVAILABLE`
- `NOTIFICATION_SEND_FAILED`
- `REPOSITORY_ACCESS_DENIED`
- `INTERNAL_ERROR`
> **§6 는 ca-tmpl 전체 error vocabulary 의 SSOT**. 개별 branch-note (예: `feature-boundary-validation-mapping-contract`) 의 §구현 가이드 §1 (error code 표) 은 본 §6 의 *부분 view*. branch-note §구현 가이드에 본 §6 vocabulary 외의 *도메인 특화 code* (`USER_NOT_FOUND`, `POST_NOT_FOUND`, `DUPLICATE_EMAIL` 등) 작성 금지 — ca-tmpl skeleton 은 도메인 없이 만드는 영역이므로 도메인 특화 code 는 별도 프로젝트가 도메인 얹을 때 추가하는 영역.
>
> **코드 vocabulary 추가 절차**: 신규 code (예: `MAPPING_FAILED`, `BATCH_PARTIAL_FAILURE`) 는 본 §6 등록이 *선행 조건*. branch-note 의 §구현 가이드에서 code 를 *사용* 하기 전에 본 §6 에 추가. 등록 전 사용 시 branch-note 에 `provisional` 표시.
## 7. Non-Retryable 기준
retryable 기본값:
- DB connection unavailable
- transient lock failure
- query timeout
- upstream 429
- upstream 5xx
- connect timeout
- read timeout
- DNS temporary failure
- message publish temporary failure
- cache unavailable when degradation is allowed
non-retryable 기본값:
- validation failure
- authentication failure
- authorization failure
- malformed token
- invalid signature
- unsupported media type
- deterministic conflict
- upstream 400 caused by invalid request
- repository access permission violation
## 8. Structured Log Contract
JSON log 기본 필드:
- `timestamp`
- `level`
- `app`
- `profile`
- `logger`
- `message`
- `traceId`
- `requestId`
- `correlationId`
- `operation`
- `error.code`
- `error.category`
- `error.retryable`
- `dependency.name`
- `dependency.type`
- `duration_ms`
> **2026-06-01 명명 정합 (F2/D19)**: JSON 로그의 **필드 명은 mdc-keys.yaml 의 snake_case 가 authoritative** — 즉 위 `traceId`/`requestId`/`correlationId` 는 실제 로그에서 `trace_id`/`request_id`/`correlation_id`/`span_id` (snake) 로 출력된다(§21 L855 MDC core 6 + log extension 13 모두 snake). camelCase 표기는 **§3 응답 envelope** (`meta.traceId` …) 의 표현이며, kebab-case 는 **HTTP header** (`X-Request-Id`) 표현이다. 동일 식별자의 계층별 표현 매핑(snake↔camel↔kebab)은 foundation branch D19 ([[raw/branch-notes/feature-operational-error-observability-foundation]] §구현 가이드 §3) 가 SSOT. envelope camelCase 표기 자체의 owner 는 §25 의 schema-serialization.
로그 종류:
- request log
- application log
- dependency log
- security event log
- audit log
로그 금지:
- PII
- secrets
- token
- password
- authorization header
- raw request body
- raw response body
- SQL parameter
기본 level 기준:
- client validation 4xx: INFO 또는 WARN
- auth/authz failure: WARN
- dependency failure: ERROR
- internal 5xx: ERROR
### Distributed Tracing Contract
- trace context는 inbound HTTP, outbound HTTP, async job, message publish/consume 경계에서 전파해야 함.
- `traceId`는 관측성 상관관계의 최상위 식별자이고, `requestId`는 inbound HTTP 요청 단위 식별자이며, `correlationId`는 business-neutral workflow 식별자로 사용.
- baggage에는 PII, token, user raw identifier, request body derived value를 넣지 않음.
- async/job/message boundary에서는 부모 trace context가 없을 경우 새 trace를 만들고 `correlationId`는 유지.
- sampling, exporter, propagation header는 env로 제어.
- log에는 `traceId`, `spanId`, `requestId`, `correlationId`를 같은 이름으로 남김.
## 9. Env-driven Runtime Configuration
서버별 운영 전환이 env로 가능해야 합니다.
기본 env:
- `APP_NAME`
- `APP_PROFILE`
- `ERROR_EXPOSE_DETAILS`
- `ERROR_EXPOSE_VALIDATION_DETAILS`
- `LOG_FORMAT`
- `LOG_LEVEL`
- `LOG_BODY_ENABLED`
- `LOG_PII_GUARD_ENABLED`
- `TRACE_ENABLED`
- `REQUEST_ID_HEADER`
- `CORRELATION_ID_HEADER`
- `DB_URL`
- `DB_USERNAME`
- `DB_PASSWORD`
- `DB_POOL_MAX_SIZE`
- `DB_CONNECTION_TIMEOUT_MS`
- `HTTP_CONNECT_TIMEOUT_MS`
- `HTTP_READ_TIMEOUT_MS`
- `HTTP_RETRY_ENABLED`
- `HTTP_CIRCUIT_BREAKER_ENABLED`
- `KAFKA_ENABLED`
- `REDIS_ENABLED`
- `SLACK_ENABLED`
- `GOOGLE_EMAIL_ENABLED`
- `SECURITY_JWT_ISSUER`
- `SECURITY_JWT_AUDIENCE`
- `CORS_ALLOWED_ORIGINS`
기준:
- local/dev/staging/prod env matrix 작성.
- 잘못된 env 값은 가능한 한 startup에서 fail-fast.
- prod에서 body logging 기본 금지.
- prod에서 error detail 노출 기본 금지.
## 10. Repository Access Permission Contract
Read/write repository 분리는 기본 전제입니다. 추가로 use case 단위 capability 정책을 둡니다.
use case capability:
- `READ_REPOSITORY`
- `WRITE_REPOSITORY`
- `SENSITIVE_READ`
- `BULK_WRITE`
- `TRANSACTION_REQUIRED`
- `EXTERNAL_OUTBOUND_ALLOWED`
기준:
- use case에 허용 capability를 선언.
- 선언되지 않은 repository capability 사용은 contract violation.
- repository access permission violation은 일반 internal error가 아니라 skeleton contract violation으로 분류.
- 테스트로 capability 위반을 감지.
## 11. Adapter Failure Contract
### Persistence
- Data integrity violation
- lock conflict
- query timeout
- DB unavailable
- JPA system failure
- SQL/parameter 로그 금지
> **추후 branch 분해 대상 (deferred, 아직 owner branch 없음)** — [[raw/branch-notes/feature-persistence-failure-baseline]] §Audit & Findings 에서 OUT_OF_BRANCH_SCOPE 로 분리된 2건. failure-baseline branch In-scope(실패 분류) 밖이라 별도 branch 가 필요하나 미생성:
> - **disaster recovery restore drill** (D7): backup 존재가 아니라 restore drill 통과 기준 (§18 Data Retention/Privacy `backup/restore 책임 경계` + Operational Runbook 와 연계). 내부 RTO/RPO 정책 — 외부 공식 근거 없음.
> - **read replica lag threshold** (D8): replica 기본 미사용, 활성화 시 max lag threshold + stale-read 허용 endpoint 명시. 부모에 replica 전용 계약 섹션 부재 — 신설 필요.
> 착수 시 `/branch feature-disaster-recovery-restore-drill` · `/branch feature-read-replica-lag-contract` 로 전개.
### Outbound HTTP
- RestClient 기본.
- 400/401/403/404/409/429/5xx/timeout/DNS/connect failure 분류.
- dependency log field 필수.
- body logging 기본 금지.
### Security
- JWT Resource Server 기준.
- missing token, malformed token, expired token, invalid signature, issuer mismatch, audience mismatch, claim mapping failure 분리.
- token/PII 로그 금지.
### Optional Adapters
- Kafka: publish/consume/deserialization/retry/DLQ/idempotency/correlationId 기준.
- Redis: cache miss는 장애 아님. unavailable은 degrade 가능 여부로 분류.
- Slack/Email: notification failure가 core use case를 막을지 명시.
## 12. Test Contract
테스트로 강제할 계약:
- structured error response schema
- validation details exposure policy
- raw exception leakage 방지
- structured log field 존재
- PII/token/body 미기록
- retryable classification
- requestId/traceId/correlationId propagation
- env profile matrix smoke test
- repository capability violation detection
- adapter failure mapping
## 13. API Contract Surface
응답 envelope만으로는 API contract가 완성되지 않습니다. 다음 표면도 skeleton 기준으로 고정해야 합니다.
- API versioning path/header 기준.
- pagination / sorting / filtering 요청/응답 표준.
- idempotency key header와 command 중복 처리 기준.
- request size limit과 payload too large 실패 분류.
- multipart/file upload 실패 분류.
- content negotiation 실패 분류.
- enum/date/timezone/BigDecimal JSON 직렬화 기준.
- unknown JSON field 허용/거부 기준.
- OpenAPI schema와 실제 응답 contract 일치 검증.
## 14. Transaction / Concurrency Contract
쓰기 use case와 persistence adapter는 concurrency 실패 기준을 가져야 합니다.
- transaction boundary는 application use case 책임으로 두되 Spring `@Transactional` 직접 import는 금지하고 `TransactionPort` 또는 `TransactionalUseCaseRunner`로 추상화.
- read-only use case는 read-only transaction 기준을 가짐.
- optimistic lock, pessimistic lock, deadlock, lock timeout 분류.
- duplicate command와 idempotent command 구분.
- command retry 시 중복 write 방지 기준.
- outbox pattern 도입 기준.
- transaction required repository capability와 실제 transaction boundary 일치 검증.
<!-- section-id: runtime-flow -->
## 15. Runtime / Lifecycle Contract
서버 운영에서 business logic 없이도 발생하는 lifecycle 실패를 다룹니다.
- actuator health/readiness/liveness 기준.
- graceful shutdown 기준.
- startup validation 기준.
- migration failure 처리 기준.
- scheduled job 실패 기준.
- async executor/thread pool rejection 기준.
- memory/disk/temp file/resource exhaustion 분류.
- JVM timezone/system clock 기준.
## 16. Schema / Serialization Contract
DTO와 JSON schema가 암묵적으로 흘러가지 않도록 serialization 기준을 둡니다.
- date/time은 timezone 정책을 명시.
- money/decimal은 scale/rounding 정책을 명시.
- enum은 unknown value 처리 기준을 명시.
- null/empty/missing field 의미를 구분.
- response field rename은 API versioning과 연결.
- OpenAPI schema drift를 테스트로 감지.
## 17. Sample Domain Fixture
도메인/비즈니스 로직은 제거하지만, skeleton 계약 검증을 위한 sample domain은 둡니다.
기본 sample:
```text
sample-portfolio
```
sample domain이 검증해야 할 것:
- create/read/update/delete 흐름.
- request DTO -> command/query -> domain -> persistence -> response mapper.
- validation failure.
- not found.
- conflict.
- optimistic lock.
- pagination.
- repository capability.
- idempotent create/update.
- outbound adapter 호출 금지/허용 use case.
기준:
- sample은 `sample` package/module/profile 아래 격리.
- 실제 프로젝트에서 제거 가능해야 함.
- sample은 business feature가 아니라 skeleton contract fixture임.
- sample domain 결과를 portfolio/blog/interview로 직접 파생하지 않음.
- sample-portfolio은 worklog create/read/update/close 흐름, status transition, assignee/owner policy, optimistic lock, idempotent create, pagination, repository capability를 검증하기 위한 최소 fixture로 둠.
## 18. Control Plane Contract
운영자는 API 응답과 로그만 보지 않습니다. 서버를 배포, 감시, 보호, 장기간 유지보수하기 위한 제어면도 skeleton 기준에 포함합니다.
### Metrics / Alerting
- HTTP latency/error rate metric.
- dependency latency/error rate metric.
- DB pool metric.
- JVM/process metric.
- retry/circuit breaker metric.
- alert severity는 `P1`, `P2`, `P3`를 기본값으로 사용.
### Secrets / Config Source
- env와 secret manager 사용 범위.
- local `.env` 허용 범위.
- prod secret 노출 금지.
- config dump 금지.
- secret rotation 절차와 rotation 후 startup validation 기준.
### Management / Actuator Security
- actuator endpoint allowlist.
- health detail exposure 기준.
- metrics endpoint 인증 기준.
- management port 분리 여부.
- prod에서 env/configprops 노출 금지.
### Tenant Context Policy
- multi-tenancy 지원 여부를 명시.
- tenant header 허용/금지 기준.
- tenant scoped repository 기준.
- tenant leakage 테스트 기준.
### File / Resource Handling
- upload size limit.
- temp file cleanup.
- download streaming failure.
- content type sniffing 금지.
- path traversal 방지.
### Cache Consistency
- cache aside 기준.
- stale cache 허용 범위.
- cache stampede 방지.
- key naming / TTL / invalidation 실패 기준.
### Background Job / Async Boundary
- async exception handling.
- executor saturation.
- scheduled job overlap.
- job id/correlationId.
- retry/backoff.
- shutdown 중 job 처리.
### API Compatibility / Deprecation
- breaking change 정의.
- response field removal 금지 기준.
- deprecated field 정책.
- migration window 기준.
### CI Quality Gates
- format/lint/test/contract test/OpenAPI drift check/security scan이 CI에서 분리된 gate로 실행되어야 함.
- branch merge 전 실패 가능 gate와 warning-only gate를 구분.
- contract violation은 warning-only로 두지 않음.
- optional adapter test는 adapter enabled matrix에서만 실행.
### Build / Release / Supply Chain
- dependency version locking 기준.
- container image base와 non-root runtime 기준.
- SBOM 생성 여부.
- vulnerability severity별 release block 기준.
- rollback 가능한 artifact versioning 기준.
### Container Runtime
- JVM memory/container limit 기준.
- timezone/locale 기준.
- healthcheck command 기준.
- graceful shutdown signal 기준.
- writable filesystem 최소화 기준.
### Operational Runbook
- alert 발생 시 확인할 dashboard/log query/runbook link 기준.
- dependency 장애, DB unavailable, auth failure spike, 5xx spike, queue lag, cache unavailable별 1차 대응 기준.
- degrade 가능한 장애와 즉시 fail-fast해야 하는 장애를 구분.
### Data Retention / Privacy
- application log, security event log, audit log 보존 기간 기준.
- PII redaction과 pseudonymization 기준.
- backup/restore 책임 경계.
- sample data와 real data 혼동 방지 기준.
### Developer Experience
- local bootstrap command 기준.
- `.env.example` 필수 key 기준.
- Testcontainers 또는 local dependency 대체 기준.
- smoke test command 기준.
- sample profile 실행/비활성화 기준.
## 19. Domain Application Readiness Contract
이 skeleton은 도메인/비즈니스 로직을 포함하지 않지만, 실제 도메인을 얹을 때 바로 같은 구조로 개발할 수 있어야 합니다.
### Domain Feature Slice
새 도메인 기능은 최소 slice 단위로 추가합니다.
```text
presentation request/response DTO
request mapper
application command/query
use case
input port / output port
domain model / value object / domain rule
persistence model / repository adapter
response mapper
contract test
architecture rule
```
기준:
- controller가 use case 외부의 domain/persistence type을 직접 알면 실패.
- application use case는 input port를 구현하고 output port에만 의존.
- infrastructure adapter는 output port를 구현.
- domain은 Spring/JPA/HTTP/security/logging type을 알지 않음.
- 새 feature는 sample-portfolio의 구조를 복제하되 sample package에 의존하지 않음.
### Use Case / Port Contract
- command use case와 query use case를 구분.
- write use case는 transaction/capability/idempotency 기준을 명시.
- read use case는 pagination/filtering/sorting과 sensitive read capability를 명시.
- outbound dependency가 필요한 use case는 `EXTERNAL_OUTBOUND_ALLOWED` capability를 명시.
- use case method는 raw DTO, entity, HTTP request, JPA repository를 직접 받지 않음.
### Domain Modeling Guardrails
- entity, value object, domain service, domain event를 구분.
- value object는 생성 시점에 자기 불변식을 검증.
- aggregate 외부에서 내부 상태를 임의 변경하지 못하게 함.
- domain rule은 presentation validation이나 JPA constraint에만 의존하지 않음.
- domain exception은 business invariant만 표현하고 operational error code를 직접 알지 않음.
### Business Rule Validation
- syntax/shape validation은 request DTO에서 처리.
- use case policy validation은 application에서 처리.
- business invariant는 domain에서 처리.
- persistence uniqueness/integrity는 infrastructure에서 operational error로 변환하되, 필요한 경우 application/domain policy로 사전 검증.
- 같은 규칙을 여러 경계에 중복 구현할 때는 목적을 명시.
### Domain Event / Outbox
- domain event는 domain fact만 표현하고 transport detail을 모름.
- integration event 발행은 application/infrastructure 경계에서 변환.
- transactional publish가 필요하면 outbox 기준을 사용.
- event handler 실패는 retryable/non-retryable과 DLQ/runbook 기준을 가짐.
- event payload에는 PII/secrets/raw body를 넣지 않음.
### Sample Removal / Project Adoption
- `sample-portfolio`은 새 프로젝트 생성 시 제거 가능해야 함.
- 제거 후에도 operational/error/log/env/test/architecture contract는 남아야 함.
- 새 도메인은 `sample-portfolio`을 import하지 않고 구조만 참고.
- sample 제거 smoke test를 둬서 skeleton core와 sample fixture 결합을 감지.
## 20. Skeleton Blueprint Contract
실제 구현자는 문서의 원칙뿐 아니라 module boundary와 package 위치를 함께 알아야 합니다. Phase C2 기본값은 **Gradle multi-module + Clean Architecture / Hexagonal boundary** 입니다. module boundary가 1차 강제선이고, module 내부 package는 2차 책임 분류입니다. 단일 모듈 구조는 demo/readme용 축소형으로만 허용하며, 아래 responsibility mapping을 보존해야 합니다.
### 20-0. Implementation status (2026-05-27)
`feature-skeleton-package-blueprint-contract` 범위는 ca-tmpl repo에서 B안 기준으로 local implementation 완료. 구현 범위는 Gradle module include, module build dependency matrix, package anchor, reference blog package 이동, ArchUnit/Gradle guardrail, README/agent rule update이다. 검증은 `./gradlew verifyCleanArchitectureDependencies`, `./gradlew :app-bootstrap:test --tests '*CleanArchitectureTest'`, `./gradlew :adapter-web:test --tests '*SettingsTest'`, `./gradlew test` 통과로 `locally-verified` 처리한다.
제외/잔여: `sample-portfolio`은 fixture anchor만 있고 실제 worklog sample은 미구현. `application/port/in``application/port/out` package anchor는 존재하지만 reference blog repository port는 아직 `domain/repository`에 남아 있다 (`feature-application-port-usecase-contract` branch에서 `application/port/out`으로 이동). Spring Modulith verifier는 도입하지 않았다. 운영 배포가 아니므로 `prod-verified` 항목은 없다.
```text
settings.gradle
rootProject.name = 'ca-skeleton'
include 'app-bootstrap'
include 'domain-core'
include 'application-core'
include 'adapter-web'
include 'adapter-persistence'
include 'adapter-outbound'
include 'shared-contract'
include 'sample-portfolio'
app-bootstrap/
src/main/java/{basePackage}/bootstrap/
CaSkeletonApplication
config/
src/test/java/{basePackage}/bootstrap/
smoke/
shared-contract/
src/main/java/{basePackage}/shared/
response/
error/
headers/
logging/
tracing/
metrics/
registry/
annotation/
src/test/java/{basePackage}/shared/
contract/
domain-core/
src/main/java/{basePackage}/domain/
# framework-neutral POJO domain model (production)
src/test/java/{basePackage}/domain/
application-core/
src/main/java/{basePackage}/application/
usecase/
command/
query/
capability/ # @UseCaseRepositoryAccess, Idempotency enum
transaction/ # TransactionPort, TransactionalUseCaseRunner
src/test/java/{basePackage}/application/
adapter-web/
src/main/java/{basePackage}/
adapter/web/
dto/
exception/
auth/
presentation/ # presentation-specific (e.g., grpc)
src/test/java/{basePackage}/adapter/web/
adapter-persistence/
src/main/java/{basePackage}/adapter/persistence/
# entity/repository/mapper/migration sub-packages 도메인 추가 시 생성
src/test/java/{basePackage}/adapter/persistence/
adapter-outbound/
src/main/java/{basePackage}/adapter/outbound/
# httpclient/messaging/cache/notification sub-packages 도메인 추가 시 생성
src/test/java/{basePackage}/adapter/outbound/
shared-contract/
src/main/java/{basePackage}/shared/
tracing/
metrics/
request/
logging/
headers/
response/ # envelope, BulkEnvelope
error/ # OperationalError, error codes
src/test/java/{basePackage}/shared/
app-bootstrap/
src/main/java/{basePackage}/
# Spring Boot Application + bean wiring
src/test/java/{basePackage}/
sample-portfolio/
src/main/java/{basePackage}/sample/portfolio/
domain/
worklog/ # WorkLog domain entity + value objects (Period, WorkCategory, etc.)
application/
command/ # CreateWorkLogCommand, UpdateWorkLogCommand, DeleteWorkLogCommand
query/ # GetWorkLogQuery, ListWorkLogsQuery, GetRepoStatsQuery
port/ # outbound port (e.g., RepoStatsPort)
exception/ # WorkLogNotFoundException
worklog/ # use case implementations (Create/Get/List/Update/Delete/GetRepoStats)
adapter/
web/
controller/ # WorkLogController
dto/
request/ # CreateWorkLogRequest, UpdateWorkLogRequest, ...
response/ # WorkLogResponse, RepoStatsResponse, ...
mapper/ # WorkLogWebMapper
error/ # DomainExceptionHandler, PortfolioErrorCode
persistence/
entity/ # WorkLogEntity
repository/ # WorkLogJpaRepository, WorkLogRepositoryAdapter
mapper/ # WorkLogPersistenceMapper
config/ # JpaConfig
outbound/
repostats/ # external API adapter (RepoStatsPortClient + ACL mapper)
src/test/java/{basePackage}/sample/portfolio/
domain/
application/
adapter/
```
기준:
- `domain-core`는 framework-neutral POJO domain model만 담고 Spring / JPA / HTTP DTO / Redis / Kafka / client library를 알지 않음.
- `application-core`는 use case, command/query, inbound/outbound port, policy validation을 담고 `domain-core``shared-contract`에만 의존함.
- `adapter-web`은 controller, HTTP DTO, mapper, filter, presentation exception mapping을 담고 application port를 호출함.
- `adapter-persistence`는 JPA entity, Spring Data repository, persistence mapper, migration integration을 담고 application outbound port를 구현함.
- `adapter-outbound`는 outbound HTTP, messaging, cache, notification adapter 구현체를 담고 application outbound port를 구현함.
- `shared-contract`는 response envelope, error code, header/MDC/metric registry, tracing/logging contract, 공통 annotation처럼 skeleton-wide operational contract만 담고 business/domain concept를 담지 않음.
- `app-bootstrap`은 runtime composition root이며 Spring Boot application, bean wiring, profile config를 담음. domain policy 구현을 담지 않음.
- `sample-portfolio`은 contract 검증 fixture이며 production feature가 아님. production module이 `sample-portfolio`을 import하거나 dependency로 선언하면 실패.
- package/module 위치가 다르면 architecture rule 문서에 responsibility mapping을 명시해야 함.
Module dependency rule:
| Module | May depend on | Must not depend on |
| --- | --- | --- |
| `domain-core` | (none) or `shared-contract` value-only types | Spring, JPA, HTTP DTO, Redis/Kafka/client libraries, adapter modules |
| `application-core` | `domain-core`, `shared-contract` | `adapter-*`, `app-bootstrap`, Spring Web/JPA implementation APIs |
| `adapter-web` | `application-core`, `domain-core`, `shared-contract` | `adapter-persistence`, `adapter-outbound` direct implementation coupling |
| `adapter-persistence` | `application-core`, `domain-core`, `shared-contract` | `adapter-web`, `app-bootstrap` |
| `adapter-outbound` | `application-core`, `domain-core`, `shared-contract` | `adapter-web`, `app-bootstrap` |
| `app-bootstrap` | all runtime modules | domain policy implementation |
| `sample-portfolio` | all runtime modules only as fixture consumer | production module importing `sample-portfolio` |
## 21. Contract Registry
100점 기준에서는 중요한 문자열과 enum이 문서 곳곳에 흩어지면 안 됩니다. 본 섹션의 7개 registry는 raw markdown 결정 사항과 별도로 implementation artifact yaml(`ca-tmpl/docs/registries/` 하위)에서 단일 source of truth로 관리합니다. **yaml은 Phase B 산출물이며, ca-tmpl 실 코드 단계(Phase C2)에서 generated constants의 source가 됩니다.** 본 섹션의 inline 요약은 reader 편의용이며 정확한 row 정의는 yaml을 참조합니다.
### SSOT yaml 위치
| registry | yaml 파일 | row 수 (2026-05-22) | owner branch |
| --- | --- | --- | --- |
| Error Codes | `ca-tmpl/docs/registries/error-codes.yaml` | 49 (skeleton-level; NOT_FOUND/도메인별 VALIDATION row는 도메인 도입 시 추가) | `feature-operational-error-observability-foundation` (category enum SSOT) |
| Env Keys | `ca-tmpl/docs/registries/env-keys.yaml` | 51 | `feature-env-driven-runtime-configuration` |
| Secrets Classification | `ca-tmpl/docs/registries/secrets-classification.yaml` | 15 | `feature-secrets-config-source-contract` |
| HTTP Headers | `ca-tmpl/docs/registries/headers.yaml` | 15 | `feature-api-contract-baseline` (cross-owner: idempotency, tracing, tenant, compat, security) |
| MDC / Log Keys | `ca-tmpl/docs/registries/mdc-keys.yaml` | 19 (foundation core 6 + log extension 13) | `feature-operational-error-observability-foundation` |
| Metrics | `ca-tmpl/docs/registries/metrics.yaml` | 25 | `feature-metrics-alerting-contract` |
| Repository Access Capabilities | `ca-tmpl/docs/registries/capabilities.yaml` | 7 | `feature-repository-access-permission-contract` |
총 196 rows. 모든 row에 source branch + line 인용 yaml comment 포함. 추측 row 0건 (source-grounded only).
### Error Registry 요약 (yaml 정합)
- `error.category` enum (foundation SSOT, 10개): VALIDATION / AUTH / AUTHZ / NOT_FOUND / CONFLICT / RATE_LIMIT / TRANSIENT_DEPENDENCY / PERMANENT_DEPENDENCY / DATA_INTEGRITY / INTERNAL
- 카테고리별 row 분포: TRANSIENT_DEPENDENCY 15 · AUTH 8 · INTERNAL 8 · VALIDATION 6 · CONFLICT 4 · AUTHZ 3 · DATA_INTEGRITY 3 · RATE_LIMIT 1 · PERMANENT_DEPENDENCY 1 · NOT_FOUND 0 (도메인 도입 시 추가)
- row schema: `code` (UPPER_SNAKE_CASE), `category`, `http_status`, `retryable`, `retry_after_seconds`, `owner_branch`, `owner_layer`, `client_safe_message`, `log_level`, `runbook_link`, `compatibility_impact`, `required_test`
- runbook 정책: `retryable=true` 모두 + `category ∈ {AUTH, AUTHZ, RATE_LIMIT, INTERNAL, TRANSIENT_DEPENDENCY, PERMANENT_DEPENDENCY}` 이면서 `retryable=false`인 row는 `runbook_link` 필수. client-error(`VALIDATION/NOT_FOUND/CONFLICT/DATA_INTEGRITY` + `retryable=false`)는 면제.
- 명명 정합: `AUTHENTICATION``AUTH`, `AUTHORIZATION``AUTHZ`, `PERSISTENCE``DATA_INTEGRITY`/`TRANSIENT_DEPENDENCY` 매핑 (Phase A 4차 audit Conflict 13 해소).
### Response Envelope 요약
envelope schema는 `feature-operational-error-observability-foundation` SSOT. 필수 field:
| field | required | rule |
| --- | --- | --- |
| `success` | yes | boolean only |
| `data` | success only | public payload, domain/entity 직접 노출 금지 |
| `error.code` | failure only | error-codes.yaml row의 code |
| `error.category` | failure only | foundation enum 10개 중 하나 |
| `error.message` | failure only | client-safe, raw exception/stack/SQL/token 금지 |
| `error.retryable` | failure only | error-codes.yaml의 retryable값과 일치 |
| `error.details` | optional | validation field error shape(`field`, `rejectedValue`(masked), `code`, `message`) |
| `meta.requestId` | yes (camelCase) | mdc-keys.yaml의 `request_id` (snake) ↔ envelope camel mapping |
| `meta.traceId` | yes | tracing disabled에서도 opaque id 유지 + `sampled=false` |
| `meta.correlationId` | yes | mdc `correlation_id` mapping |
| `meta.page` | paged only | `page`, `size`, optional `total`, sort 정보 (성능 함정 회피, cursor pagination은 별도 endpoint) |
| `meta.idempotency.replayed` | idempotent replay only | replay 응답 명시 |
### Header Registry 요약 (headers.yaml)
- naming: HTTP 표준은 kebab-case (`X-Request-Id`, `X-Tenant-Id`, `X-Api-Version`, `Idempotency-Key`, `Retry-After`, `Deprecation`, `Sunset`, `X-RateLimit-Limit/Remaining/Reset`), W3C trace context는 lowercase (`traceparent`, `tracestate`), security 표준 (`Authorization`, `WWW-Authenticate`).
- direction 분포: inbound 3 · outbound 7 · both 5.
- mdc_key 매핑 (mdc-keys.yaml과 cross-link): `X-Request-Id↔request_id`, `X-Correlation-Id↔correlation_id`, `traceparent↔trace_id`, `X-Tenant-Id↔tenant_id`.
- envelope_meta_field 매핑: `meta.requestId`, `meta.traceId`, `meta.correlationId`.
### Secrets Registry 요약 (env-keys.yaml, secrets-classification.yaml)
- env-keys: 51 row, prefix `APP_` (Spring native env는 prefix 없이 별도 row, 예: `SPRING_PROFILES_ACTIVE`, `SERVER_PORT`). 15개 영역: profile/identity · datasource/pool · outbound HTTP · tracing · log · security/CORS · JWT · tenant · cache/Redis · messaging · notification adapter · file upload · runtime/lifecycle · async executor · sample.
- secrets-classification: 15 row, 3-tier:
- **secret** 6: DB_PASSWORD, JWT_SIGNING_KEY, OAUTH_CLIENT_SECRET, EXTERNAL_API_KEY, REDIS_PASSWORD, PSEUDONYMIZATION_SALT
- **sensitive-config** 4: SLACK_WEBHOOK_URL, GOOGLE_OAUTH_CLIENT_ID, DATASOURCE_USERNAME, DATASOURCE_URL
- **public-config** reference 5: APP_PROFILE, APP_NAME, SERVER_PORT, SPRING_PROFILES_ACTIVE, OTEL_EXPORTER_OTLP_ENDPOINT (본 yaml은 reference만, env-keys.yaml에서 정의)
- rotation 정책: `restart-only` (default) · `dual-bind-60s` (DB credential) · `overlap-24h` (JWT signing key, idempotency TTL invariant) · `salt-rotation-90d` (pseudonymization salt) · `manual` (webhook/OAuth client ID)
- masking: secret은 `full_except_last_4`, 기타 none. prod profile에서 `__LOCAL_DEV_` prefix value 발견 시 startup fail.
- reload: `no-runtime-reload` (env-driven branch SSOT, secret rotation은 restart validation 또는 dual-bind 책임).
### Metrics Registry 요약 (mdc-keys.yaml, metrics.yaml)
- MDC keys: snake_case 강제 (foundation SSOT). core 6: `request_id`, `trace_id`, `span_id`, `correlation_id`, `tenant_id`, `user_principal` (pseudonymized only). log extension 13: `operation`, `method`, `status`, `duration_ms`, `dependency_name`, `dependency_type`, `outcome`, `error_code`, `source_ip_anon` (last octet zeroed), `actor`, `action`, `target`, `event_type`.
- propagation matrix: http(5)/async(5)/message(4)/none(14). background-job-async TaskDecorator 단일 owner가 async/message boundary propagation 책임. `user_principal`은 log only, header/baggage forbidden.
- metrics: Micrometer dot.case + unit suffix(`.seconds`/`.bytes`/`.total`). cardinality bounds 강제: status_code≤7, uri_template≤200, dependency_name≤50, error_code≤100 (error-codes.yaml row 상한과 정합), tenant_id≤1000 (raw ULID 금지, mapping id 또는 cohort bucket), outcome≤5. **high-cardinality tag forbidden**: user_id, request_id, raw_url, raw_query, raw_header_value, ip_address.
- percentile: HTTP/DB/dependency timer는 p50/p90/p95/p99. histogram bucket은 SLO-driven (잠정 SLO p99 = 1s).
- alert severity P1/P2/P3 정량 기준은 metrics.yaml의 `alert_severity_thresholds` field. 잠정 SLO 기반.
### Capability Registry 요약 (capabilities.yaml)
7개 capability:
| capability | enforcement | notes |
| --- | --- | --- |
| `READ_REPOSITORY` | ArchUnit | 일반 read |
| `WRITE_REPOSITORY` | ArchUnit | create/update/delete |
| `SENSITIVE_READ` | ArchUnit (marker는 registry-managed metadata) | PII/secret-like field read |
| `BULK_WRITE` | ArchUnit (threshold N > 100) | batch mutation |
| `TRANSACTION_REQUIRED` | ArchUnit + TransactionPort cross-link | Spring `@Transactional` 직접 import forbidden |
| `EXTERNAL_OUTBOUND_ALLOWED` | ArchUnit | outbound HTTP/message/notification. outbox row INSERT는 in-process(불요), polling publisher의 broker publish는 outbound(필요) |
| `CROSS_TENANT_ADMIN` | ArchUnit | tenant 활성 시 cross-tenant 접근 명시 선언 |
enforcement: ArchUnit annotation-based rule SSOT. compile-time annotation processor alternative, **runtime AOP forbidden**.
### Registry 변경 절차
1. raw 결정은 branch note의 결정 사항 / Decisionized Work Items 표에 먼저 작성.
2. SSOT yaml의 row 추가/수정. row 위 yaml comment에 source branch + line 인용.
3. compatibility_impact 분류 (none / additive / behavior-change / breaking).
4. 관련 contract test 추가/수정.
5. ca-tmpl 실 코드(Phase C2)의 generated constants 재생성 (build task).
6. branch note의 TODO 항목 drain (registry-governance step 6).
registry 기준:
- 새 error/env/header/log/metric/capability를 추가할 때 registry yaml 없이 branch TODO만 추가하면 실패.
- registry 항목은 test contract와 연결되어야 함 (`required_test` field).
- registry 변경은 backward compatibility와 migration 영향을 기록해야 함 (`compatibility_impact` field).
- yaml row의 source comment가 branch note 라인 인용 없이 추가되면 review fail.
## 22. Sample-portfolio Contract Matrix
`sample-portfolio`은 기능 예제가 아니라 skeleton 계약 검증 fixture입니다.
| scenario | layer path | verifies | expected failure if broken |
| --- | --- | --- | --- |
| create worklog success | request DTO -> command -> use case -> domain -> repository -> response | mapper, command validation, write capability, transaction, envelope | controller가 domain/entity를 직접 생성하거나 반환 |
| create worklog validation failure | presentation -> error handler | validation details, client-safe error, no raw exception | malformed request가 500 또는 raw exception으로 노출 |
| create worklog idempotent replay | presentation/application/persistence | `Idempotency-Key`, duplicate write 방지, replay meta | retry 시 worklog 중복 생성 |
| get worklog success | query use case -> read port -> response mapper | query/read capability, response mapper | persistence entity가 response로 노출 |
| get worklog not found | application -> error registry | `RESOURCE_NOT_FOUND`, 404, retryable false | not found가 500 또는 DB exception으로 노출 |
| list worklogs pagination | query -> repository -> response meta | pagination meta, sorting/filtering contract | pagination 정보가 data payload에 섞임 |
| update worklog conflict | domain/application | conflict classification, status transition rule | invalid transition이 성공하거나 500 발생 |
| close worklog optimistic lock | persistence/application | optimistic lock -> conflict/retry policy | lock failure가 raw JPA exception으로 노출 |
| unauthorized worklog update | security/application | auth/authz separation, no PII log | 401/403 분류 혼동 또는 token log |
| outbound forbidden use case | application capability | `EXTERNAL_OUTBOUND_ALLOWED` enforcement | capability 없이 외부 adapter 호출 |
| sample disabled startup | runtime/profile | sample prod 비활성화 | prod profile에서 sample endpoint 노출 |
| sample removal smoke | build/test | core contract와 sample fixture 분리 | sample 제거 후 app/context/contract test 실패 |
sample-portfolio minimum model:
| model | required fields | purpose |
| --- | --- | --- |
| `WorkLogId` | 26-char uppercase Crockford base32 ULID (예: `01ARZ3NDEKTSV4RRFFQ69G5FAV`, regex `^[0-9A-HJKMNP-TV-Z]{26}$`) — [[raw/branch-notes/feature-resource-identifier-contract]] D19 SSOT | value object / path variable mapping |
| `WorkLogTitle` | normalized non-empty string | request validation + domain invariant |
| `WorkLogStatus` | `OPEN`, `IN_PROGRESS`, `CLOSED` | enum serialization + transition conflict |
| `WorkLogVersion` | numeric version | optimistic locking |
| `WorkLogOwner` | pseudonymized principal id | authorization/log privacy |
| `IdempotencyKey` | opaque key | duplicate write prevention |
sample-portfolio rule:
- `OPEN -> IN_PROGRESS -> CLOSED`만 허용.
- `CLOSED` worklog은 update 불가.
- owner 또는 allowed assignee만 update 가능.
- create는 idempotent command로 처리.
- list는 pagination/sorting/filtering contract를 사용.
- sample package는 production package에서 import 금지.
## 23. Branch Canonical Promotion Criteria
branch note는 아래 산출물이 있어야 `wiki/projects` canonical 문서로 승급할 수 있습니다.
| artifact | required | rule |
| --- | --- | --- |
| Decision table | yes | 기본값/예외/금지/실패 조건 포함 |
| Work item contract | yes | 각 TODO가 Decision/Allowed/Forbidden/Registry/Test/Failure/Canonical target으로 재작성되어야 함 |
| Registry update | if token changed | error/env/header/log/metric/capability 변경 시 필수 |
| Sample-portfolio verification | if applicable | sample scenario 또는 sample removal로 검증 |
| Contract test mapping | yes | 어떤 테스트가 깨지는지 명시 |
| Architecture rule mapping | boundary related only | package/import/dependency 위반 기준 명시 |
| Runbook/log/metric mapping | operational related only | 운영자가 확인할 field와 alert 연결 |
| Adoption note | yes | 실제 도메인 feature가 따라야 할 규칙 명시 |
| Out-of-scope note | yes | branch가 책임지지 않는 영역 명시 |
promotion failure:
- TODO가 “기준 작성” 수준으로만 남아 있으면 승급 실패.
- TODO가 Work Item Contract 필드를 채우지 않으면 승급 실패.
- registry 영향이 있는데 registry update가 없으면 승급 실패.
- sample-portfolio 또는 sample removal 검증 경로가 없으면 승급 실패.
- 실제 도메인 feature adoption 기준이 없으면 승급 실패.
<!-- section-id: project-work-items -->
## 8.0 실행계획
> Project contract v2의 branch handoff SSOT. 기존 §24 목록에는 dependency가 없으므로 revision 1에서는 `-`로 보존하며, 각 완료 조건은 §23의 promotion contract 6필드와 해당 branch gate 통과로 고정한다.
| Work Item ID | branch slug | 완료 조건 (측정가능) | Applies Decisions | Dependencies | Status |
|---|---|---|---|---|---|
| `WI-CA-SKELETON-OPERATIONAL-CONTRACT-001` | `feature-operational-error-observability-foundation` | error·observability 6필드 contract와 contract test가 통과한다 | `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-ERROR-ENVELOPE-001@1` | - | `planned` |
| `WI-CA-SKELETON-OPERATIONAL-CONTRACT-002` | `feature-boundary-validation-mapping-contract` | boundary·mapping 6필드 contract와 negative fixture가 통과한다 | `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-MAPPING-001@1` | - | `planned` |
| `WI-CA-SKELETON-OPERATIONAL-CONTRACT-003` | `feature-log-management-contract` | log field·masking contract와 verification test가 통과한다 | `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-ERROR-ENVELOPE-001@1` | - | `planned` |
| `WI-CA-SKELETON-OPERATIONAL-CONTRACT-004` | `feature-env-driven-runtime-configuration` | env configuration 6필드 contract와 invalid-config test가 통과한다 | `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-STACK-FRAMEWORK-001@1` | - | `planned` |
| `WI-CA-SKELETON-OPERATIONAL-CONTRACT-005` | `feature-repository-access-permission-contract` | repository access rule과 forbidden fixture가 통과한다 | `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-MODULE-LAYOUT-001@1`, `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-TRANSACTION-001@1` | - | `planned` |
| `WI-CA-SKELETON-OPERATIONAL-CONTRACT-006` | `feature-persistence-failure-baseline` | persistence failure mapping과 integration test가 통과한다 | `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-STACK-DATABASE-001@1`, `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-TESTCONTAINERS-001@1` | - | `planned` |
| `WI-CA-SKELETON-OPERATIONAL-CONTRACT-007` | `feature-outbound-http-client-baseline` | timeout·retry·circuit-breaker contract test가 통과한다 | `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-RESILIENCE-001@1`, `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-TESTCONTAINERS-001@1` | - | `planned` |
| `WI-CA-SKELETON-OPERATIONAL-CONTRACT-008` | `feature-security-operational-baseline` | security failure·header contract와 negative test가 통과한다 | `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-STACK-FRAMEWORK-001@1` | - | `planned` |
| `WI-CA-SKELETON-OPERATIONAL-CONTRACT-009` | `feature-integration-adapter-templates` | optional adapter template가 core broker abstraction을 침범하지 않는다 | `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-EVENT-BROKER-001@1` | - | `planned` |
| `WI-CA-SKELETON-OPERATIONAL-CONTRACT-010` | `feature-contract-verification-test-suite` | release-blocking contract suite가 OpenAPI drift를 검출한다 | `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-OPENAPI-001@1`, `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-STACK-TEST-001@1` | - | `planned` |
| `WI-CA-SKELETON-OPERATIONAL-CONTRACT-011` | `feature-api-contract-baseline` | /v1 API와 envelope/OpenAPI contract test가 통과한다 | `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-API-VERSIONING-001@1`, `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-OPENAPI-001@1` | - | `planned` |
| `WI-CA-SKELETON-OPERATIONAL-CONTRACT-012` | `feature-transaction-concurrency-contract` | transaction·concurrency failure fixture가 통과한다 | `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-TRANSACTION-001@1`, `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-STACK-DATABASE-001@1` | - | `planned` |
| `WI-CA-SKELETON-OPERATIONAL-CONTRACT-013` | `feature-runtime-health-lifecycle-contract` | startup·readiness·shutdown lifecycle test가 통과한다 | `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-MIGRATION-001@1` | - | `planned` |
| `WI-CA-SKELETON-OPERATIONAL-CONTRACT-014` | `feature-sample-domain-contract-fixture` | sample domain fixture가 module contract를 검증하고 제거 smoke가 통과한다 | `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-MODULE-LAYOUT-001@1` | - | `planned` |
| `WI-CA-SKELETON-OPERATIONAL-CONTRACT-015` | `feature-schema-serialization-contract` | JSON·date·decimal serialization contract test가 통과한다 | `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-STACK-JSON-001@1` | - | `planned` |
| `WI-CA-SKELETON-OPERATIONAL-CONTRACT-016` | `feature-rate-limit-idempotency-contract` | principal·tenant key scope와 replay/rate-limit test가 통과한다 | `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-IDEMPOTENCY-001@1`, `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-RATE-LIMIT-001@1` | - | `planned` |
| `WI-CA-SKELETON-OPERATIONAL-CONTRACT-017` | `feature-migration-startup-contract` | Flyway 실패·진행 중 readiness가 healthy가 아님을 검증한다 | `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-MIGRATION-001@1`, `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-STACK-MIGRATION-001@1` | - | `planned` |
| `WI-CA-SKELETON-OPERATIONAL-CONTRACT-018` | `feature-architecture-enforcement-rules` | forbidden module/import fixture가 ArchUnit gate에서 실패한다 | `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-MODULE-LAYOUT-001@1`, `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-STACK-ARCHTEST-001@1` | - | `planned` |
| `WI-CA-SKELETON-OPERATIONAL-CONTRACT-019` | `feature-metrics-alerting-contract` | metric key·cardinality·alert contract test가 통과한다 | `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-ERROR-ENVELOPE-001@1` | - | `planned` |
| `WI-CA-SKELETON-OPERATIONAL-CONTRACT-020` | `feature-secrets-config-source-contract` | secret source·classification·leakage negative test가 통과한다 | `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-STACK-FRAMEWORK-001@1` | - | `planned` |
| `WI-CA-SKELETON-OPERATIONAL-CONTRACT-021` | `feature-management-actuator-security-contract` | management endpoint exposure·authorization test가 통과한다 | `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-STACK-FRAMEWORK-001@1` | - | `planned` |
| `WI-CA-SKELETON-OPERATIONAL-CONTRACT-022` | `feature-tenant-context-policy` | tenant propagation·clear negative fixture가 통과한다 | `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-STACK-FRAMEWORK-001@1` | - | `planned` |
| `WI-CA-SKELETON-OPERATIONAL-CONTRACT-023` | `feature-file-resource-handling-contract` | file size·type·storage boundary test가 통과한다 | `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-MODULE-LAYOUT-001@1` | - | `planned` |
| `WI-CA-SKELETON-OPERATIONAL-CONTRACT-024` | `feature-cache-consistency-contract` | after-commit invalidation·stampede failure fixture가 통과한다 | `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-STACK-DATABASE-001@1` | - | `planned` |
| `WI-CA-SKELETON-OPERATIONAL-CONTRACT-025` | `feature-background-job-async-contract` | duplicate scheduler/outbox execution 방지 test가 통과한다 | `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-SCHEDULER-LOCK-001@1`, `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-EVENT-BROKER-001@1` | - | `planned` |
| `WI-CA-SKELETON-OPERATIONAL-CONTRACT-026` | `feature-api-compatibility-deprecation-contract` | /v1 compatibility·deprecation contract test가 통과한다 | `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-API-VERSIONING-001@1` | - | `planned` |
| `WI-CA-SKELETON-OPERATIONAL-CONTRACT-027` | `feature-distributed-tracing-contract` | request·trace correlation contract test가 통과한다 | `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-ERROR-ENVELOPE-001@1` | - | `planned` |
| `WI-CA-SKELETON-OPERATIONAL-CONTRACT-028` | `feature-ci-quality-gates-contract` | architecture·contract·OpenAPI blocking gate가 분리 실행된다 | `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-OPENAPI-001@1`, `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-STACK-TEST-001@1` | - | `planned` |
| `WI-CA-SKELETON-OPERATIONAL-CONTRACT-029` | `feature-build-release-supply-chain-contract` | Gradle release·SBOM·signature artifact가 생성된다 | `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-STACK-BUILD-001@1` | - | `planned` |
| `WI-CA-SKELETON-OPERATIONAL-CONTRACT-030` | `feature-container-runtime-contract` | non-root·memory·health container contract test가 통과한다 | `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-CONTAINER-001@1` | - | `planned` |
| `WI-CA-SKELETON-OPERATIONAL-CONTRACT-031` | `feature-operational-runbook-contract` | 각 failure category에 trigger·diagnosis·recovery drill이 연결된다 | `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-BOOTSTRAP-001@1` | - | `planned` |
| `WI-CA-SKELETON-OPERATIONAL-CONTRACT-032` | `feature-data-retention-privacy-contract` | retention·deletion·masking contract test가 통과한다 | `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-MODULE-LAYOUT-001@1` | - | `planned` |
| `WI-CA-SKELETON-OPERATIONAL-CONTRACT-033` | `feature-developer-experience-contract` | fresh environment에서 ./gradlew bootstrap이 성공한다 | `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-BOOTSTRAP-001@1`, `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-STACK-BUILD-001@1` | - | `planned` |
| `WI-CA-SKELETON-OPERATIONAL-CONTRACT-034` | `feature-domain-feature-onboarding-contract` | 신규 domain slice가 module·test checklist를 통과한다 | `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-MODULE-LAYOUT-001@1` | - | `planned` |
| `WI-CA-SKELETON-OPERATIONAL-CONTRACT-035` | `feature-application-port-usecase-contract` | application port와 transaction runner architecture test가 통과한다 | `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-TRANSACTION-001@1`, `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-MODULE-LAYOUT-001@1` | - | `planned` |
| `WI-CA-SKELETON-OPERATIONAL-CONTRACT-036` | `feature-domain-modeling-guardrails` | domain model forbidden dependency fixture가 실패한다 | `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-MODULE-LAYOUT-001@1` | - | `planned` |
| `WI-CA-SKELETON-OPERATIONAL-CONTRACT-037` | `feature-business-rule-validation-contract` | validation ownership·mapper failure contract test가 통과한다 | `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-MAPPING-001@1`, `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-ERROR-ENVELOPE-001@1` | - | `planned` |
| `WI-CA-SKELETON-OPERATIONAL-CONTRACT-038` | `feature-domain-event-outbox-contract` | broker-agnostic outbox와 duplicate execution test가 통과한다 | `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-EVENT-BROKER-001@1`, `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-SCHEDULER-LOCK-001@1` | - | `planned` |
| `WI-CA-SKELETON-OPERATIONAL-CONTRACT-039` | `feature-sample-removal-adoption-contract` | sample 제거 후 production module smoke test가 통과한다 | `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-MODULE-LAYOUT-001@1` | - | `planned` |
| `WI-CA-SKELETON-OPERATIONAL-CONTRACT-040` | `feature-skeleton-package-blueprint-contract` | Gradle module graph가 declared layout과 일치한다 | `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-MODULE-LAYOUT-001@1`, `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-STACK-BUILD-001@1` | - | `planned` |
| `WI-CA-SKELETON-OPERATIONAL-CONTRACT-041` | `feature-contract-registry-governance` | registry single-owner·schema·OpenAPI drift gate가 통과한다 | `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-ERROR-ENVELOPE-001@1`, `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-OPENAPI-001@1` | - | `planned` |
| `WI-CA-SKELETON-OPERATIONAL-CONTRACT-042` | `feature-test-taxonomy-fixture-contract` | test level별 fixture가 실행되고 container 사용 정책을 지킨다 | `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-TESTCONTAINERS-001@1`, `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-STACK-TEST-001@1` | - | `planned` |
| `WI-CA-SKELETON-OPERATIONAL-CONTRACT-043` | `feature-implementation-readiness-scorecard` | readiness 각 항목이 binary evidence link로 판정된다 | `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-BOOTSTRAP-001@1` | - | `planned` |
| `WI-CA-SKELETON-OPERATIONAL-CONTRACT-044` | `feature-webhook-outbound-contract` | signature·replay·retry·observability contract test가 통과한다 | `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-RESILIENCE-001@1` | - | `planned` |
| `WI-CA-SKELETON-OPERATIONAL-CONTRACT-045` | `feature-streaming-response-contract` | 지원 protocol과 timeout·failure contract test가 고정된다 | `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-API-VERSIONING-001@1`, `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-RESILIENCE-001@1` | - | `planned` |
| `WI-CA-SKELETON-OPERATIONAL-CONTRACT-046` | `feature-resource-identifier-contract` | ULID format·PostgreSQL uuid persistence·SecureRandom test가 통과한다 | `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-STACK-DATABASE-001@1`, `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-STACK-RANDOM-001@1` | - | `planned` |
| `WI-CA-SKELETON-OPERATIONAL-CONTRACT-047` | `feature-application-query-bypass-contract` | query bypass의 허용 경계·mapping·transaction 영향과 검증 test가 명시된다 | `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-TRANSACTION-001@1`, `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-MODULE-LAYOUT-001@1`, `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-MAPPING-001@1` | `WI-CA-SKELETON-OPERATIONAL-CONTRACT-035`, `WI-CA-SKELETON-OPERATIONAL-CONTRACT-012`, `WI-CA-SKELETON-OPERATIONAL-CONTRACT-024`, `WI-CA-SKELETON-OPERATIONAL-CONTRACT-007`, `WI-CA-SKELETON-OPERATIONAL-CONTRACT-006` | `planned` |
| `WI-CA-SKELETON-OPERATIONAL-CONTRACT-048` | `feature-authentication-authorization-contract` | authentication·authorization ownership, error mapping, forbidden dependency test가 명시된다 | `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-MODULE-LAYOUT-001@1`, `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-ERROR-ENVELOPE-001@1`, `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-STACK-FRAMEWORK-001@1`, `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-STACK-ARCHTEST-001@1` | `WI-CA-SKELETON-OPERATIONAL-CONTRACT-008`, `WI-CA-SKELETON-OPERATIONAL-CONTRACT-005`, `WI-CA-SKELETON-OPERATIONAL-CONTRACT-018`, `WI-CA-SKELETON-OPERATIONAL-CONTRACT-014`, `WI-CA-SKELETON-OPERATIONAL-CONTRACT-022`, `WI-CA-SKELETON-OPERATIONAL-CONTRACT-001` | `planned` |
| `WI-CA-SKELETON-OPERATIONAL-CONTRACT-049` | `feature-cachestore-multi-backend-router` | cache backend 선택·fallback·failure routing과 contract test가 명시된다 | `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-MODULE-LAYOUT-001@1`, `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-RESILIENCE-001@1`, `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-STACK-FRAMEWORK-001@1`, `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-STACK-TEST-001@1` | `WI-CA-SKELETON-OPERATIONAL-CONTRACT-024` | `planned` |
| `WI-CA-SKELETON-OPERATIONAL-CONTRACT-050` | `feature-database-connection-pool-contract` | connection pool 설정·lifecycle·metric·failure gate가 명시된다 | `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-STACK-DATABASE-001@1`, `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-RESILIENCE-001@1` | `WI-CA-SKELETON-OPERATIONAL-CONTRACT-004`, `WI-CA-SKELETON-OPERATIONAL-CONTRACT-006`, `WI-CA-SKELETON-OPERATIONAL-CONTRACT-035`, `WI-CA-SKELETON-OPERATIONAL-CONTRACT-019` | `planned` |
| `WI-CA-SKELETON-OPERATIONAL-CONTRACT-051` | `feature-dependency-vulnerability-management-contract` | scanner·severity·suppression·update·license 정책과 CI failure gate가 명시된다 | `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-STACK-BUILD-001@1` | `WI-CA-SKELETON-OPERATIONAL-CONTRACT-028`, `WI-CA-SKELETON-OPERATIONAL-CONTRACT-030`, `WI-CA-SKELETON-OPERATIONAL-CONTRACT-029` | `planned` |
| `WI-CA-SKELETON-OPERATIONAL-CONTRACT-052` | `feature-distributed-lock-contract` | lock provider·lease·transaction commit ordering과 failure test가 명시된다 | `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-SCHEDULER-LOCK-001@1`, `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-TRANSACTION-001@1`, `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-STACK-DATABASE-001@1` | `WI-CA-SKELETON-OPERATIONAL-CONTRACT-004`, `WI-CA-SKELETON-OPERATIONAL-CONTRACT-025`, `WI-CA-SKELETON-OPERATIONAL-CONTRACT-024`, `WI-CA-SKELETON-OPERATIONAL-CONTRACT-017`, `WI-CA-SKELETON-OPERATIONAL-CONTRACT-018` | `planned` |
| `WI-CA-SKELETON-OPERATIONAL-CONTRACT-053` | `feature-messaging-multibroker-router` | broker 선택·routing·fallback과 core transport-neutrality test가 명시된다 | `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-EVENT-BROKER-001@1`, `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-MODULE-LAYOUT-001@1` | `WI-CA-SKELETON-OPERATIONAL-CONTRACT-038` | `planned` |
| `WI-CA-SKELETON-OPERATIONAL-CONTRACT-054` | `feature-notification-provider-spi` | notification provider SPI·routing·failure contract와 test가 명시된다 | `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-MODULE-LAYOUT-001@1`, `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-RESILIENCE-001@1` | `WI-CA-SKELETON-OPERATIONAL-CONTRACT-053`, `WI-CA-SKELETON-OPERATIONAL-CONTRACT-049` | `planned` |
| `WI-CA-SKELETON-OPERATIONAL-CONTRACT-055` | `feature-persistence-auditing-contract` | persistence audit actor·time·mapping·transaction contract test가 명시된다 | `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-TRANSACTION-001@1`, `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-MAPPING-001@1`, `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-STACK-DATABASE-001@1` | `WI-CA-SKELETON-OPERATIONAL-CONTRACT-056`, `WI-CA-SKELETON-OPERATIONAL-CONTRACT-048`, `WI-CA-SKELETON-OPERATIONAL-CONTRACT-006`, `WI-CA-SKELETON-OPERATIONAL-CONTRACT-012`, `WI-CA-SKELETON-OPERATIONAL-CONTRACT-017` | `planned` |
| `WI-CA-SKELETON-OPERATIONAL-CONTRACT-056` | `feature-runtime-context-propagation-contract` | runtime context capture·propagation·cleanup과 architecture/contract test가 명시된다 | `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-MODULE-LAYOUT-001@1`, `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-STACK-LANGUAGE-001@1`, `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-STACK-FRAMEWORK-001@1`, `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-STACK-TEST-001@1`, `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-STACK-ARCHTEST-001@1` | `WI-CA-SKELETON-OPERATIONAL-CONTRACT-002`, `WI-CA-SKELETON-OPERATIONAL-CONTRACT-001`, `WI-CA-SKELETON-OPERATIONAL-CONTRACT-025`, `WI-CA-SKELETON-OPERATIONAL-CONTRACT-027`, `WI-CA-SKELETON-OPERATIONAL-CONTRACT-022` | `planned` |
| `WI-CA-SKELETON-OPERATIONAL-CONTRACT-057` | `feature-sample-portfolio-public-access` | sample public endpoint allowlist와 authenticated endpoint negative test가 명시된다 | `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-MODULE-LAYOUT-001@1`, `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-STACK-FRAMEWORK-001@1`, `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-STACK-TEST-001@1` | `WI-CA-SKELETON-OPERATIONAL-CONTRACT-048`, `WI-CA-SKELETON-OPERATIONAL-CONTRACT-021` | `planned` |
| `WI-CA-SKELETON-OPERATIONAL-CONTRACT-058` | `feature-startup-failure-log-suppression` | suppressible startup failure 조건과 retained actionable error test가 명시된다 | `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-BOOTSTRAP-001@1`, `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-ERROR-ENVELOPE-001@1` | `WI-CA-SKELETON-OPERATIONAL-CONTRACT-001`, `WI-CA-SKELETON-OPERATIONAL-CONTRACT-017` | `planned` |
| `WI-CA-SKELETON-OPERATIONAL-CONTRACT-059` | `feature-static-analysis-quality-contract` | static analysis 도구·threshold·CI failure mapping과 fixture가 명시된다 | `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-STACK-BUILD-001@1`, `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-STACK-TEST-001@1` | `WI-CA-SKELETON-OPERATIONAL-CONTRACT-028`, `WI-CA-SKELETON-OPERATIONAL-CONTRACT-029`, `WI-CA-SKELETON-OPERATIONAL-CONTRACT-018` | `planned` |
## 24. Branch 실행 계획
> **Legacy reference (v1).** 아래 link 목록과 공통 promotion 설명은 이력·navigation용이다. stable branch ID·decision pin·dependency의 SSOT는 위 Work Item Registry다.
모든 branch note는 아래 품질 기준을 만족해야 합니다.
```text
Decision: 기본 선택값이 있는가?
Allowed: 허용되는 변형이 명확한가?
Forbidden: 금지 사항이 명확한가?
Required config/log/test: 구현자가 빠뜨리면 안 되는 필드가 있는가?
Failure condition: 어떤 상태면 build/review에서 실패인지 명확한가?
Wiki extraction target: canonical 문서로 승급될 위치가 정해져 있는가?
```
TODO는 작업 목록이 아니라 미완성 계약입니다. 각 TODO는 branch 완료 전에 아래 Work Item Contract로 재작성되어야 합니다.
| field | required | rule |
| --- | --- | --- |
| Decision | yes | 구현자가 선택해야 하는 기본값 |
| Allowed | yes | 허용되는 예외와 조건 |
| Forbidden | yes | 절대 금지되는 구현/문서 상태 |
| Required registry update | conditional | error/env/header/log/metric/capability 변경 시 필수 |
| Required contract test | yes | 계약 위반 시 실패해야 하는 테스트 |
| Failure condition | yes | review/build에서 실패로 판정할 상태 |
| Canonical extraction target | yes | `wiki/projects` 승급 위치 |
- [[raw/branch-notes/feature-operational-error-observability-foundation]]
- [[raw/branch-notes/feature-boundary-validation-mapping-contract]]
- [[raw/branch-notes/feature-log-management-contract]]
- [[raw/branch-notes/feature-env-driven-runtime-configuration]]
- [[raw/branch-notes/feature-repository-access-permission-contract]]
- [[raw/branch-notes/feature-persistence-failure-baseline]]
- [[raw/branch-notes/feature-outbound-http-client-baseline]]
- [[raw/branch-notes/feature-security-operational-baseline]]
- [[raw/branch-notes/feature-integration-adapter-templates]]
- [[raw/branch-notes/feature-contract-verification-test-suite]]
- [[raw/branch-notes/feature-api-contract-baseline]]
- [[raw/branch-notes/feature-transaction-concurrency-contract]]
- [[raw/branch-notes/feature-runtime-health-lifecycle-contract]]
- [[raw/branch-notes/feature-sample-domain-contract-fixture]]
- [[raw/branch-notes/feature-schema-serialization-contract]]
- [[raw/branch-notes/feature-rate-limit-idempotency-contract]]
- [[raw/branch-notes/feature-migration-startup-contract]]
- [[raw/branch-notes/feature-architecture-enforcement-rules]]
- [[raw/branch-notes/feature-metrics-alerting-contract]]
- [[raw/branch-notes/feature-secrets-config-source-contract]]
- [[raw/branch-notes/feature-management-actuator-security-contract]]
- [[raw/branch-notes/feature-tenant-context-policy]]
- [[raw/branch-notes/feature-file-resource-handling-contract]]
- [[raw/branch-notes/feature-cache-consistency-contract]]
- [[raw/branch-notes/feature-background-job-async-contract]]
- [[raw/branch-notes/feature-api-compatibility-deprecation-contract]]
- [[raw/branch-notes/feature-distributed-tracing-contract]]
- [[raw/branch-notes/feature-ci-quality-gates-contract]]
- [[raw/branch-notes/feature-build-release-supply-chain-contract]]
- [[raw/branch-notes/feature-container-runtime-contract]]
- [[raw/branch-notes/feature-operational-runbook-contract]]
- [[raw/branch-notes/feature-data-retention-privacy-contract]]
- [[raw/branch-notes/feature-developer-experience-contract]]
- [[raw/branch-notes/feature-domain-feature-onboarding-contract]]
- [[raw/branch-notes/feature-application-port-usecase-contract]]
- [[raw/branch-notes/feature-domain-modeling-guardrails]]
- [[raw/branch-notes/feature-business-rule-validation-contract]]
- [[raw/branch-notes/feature-domain-event-outbox-contract]]
- [[raw/branch-notes/feature-sample-removal-adoption-contract]]
- [[raw/branch-notes/feature-skeleton-package-blueprint-contract]]
- [[raw/branch-notes/feature-contract-registry-governance]]
- [[raw/branch-notes/feature-test-taxonomy-fixture-contract]]
- [[raw/branch-notes/feature-implementation-readiness-scorecard]]
- [[raw/branch-notes/feature-webhook-outbound-contract]] (signature/replay/retry/observability 계약)
- [[raw/branch-notes/feature-streaming-response-contract]] (SSE / WebSocket / long-polling / chunked 지원 여부 1차 결정)
- [[raw/branch-notes/feature-resource-identifier-contract]] (D1~D19 결정 박힘 — ULID 26-char Crockford base32 + PostgreSQL `uuid` native + sample-portfolio `WorkLogId = "01ARZ3NDEKTSV4RRFFQ69G5FAV"` fixture, project §34 Stack Commitment 정합)
## 25. Default Decisions
> **Legacy reference (v1).** 아래 default와 owner map은 세부 rationale·branch-local owner 설명을 보존한다. project-wide 결정의 현재 owner와 상속 기준은 `## 6.1 Project Decision Registry / 안정 결정 레지스트리`다. branch-owned D-row는 project registry로 복제하지 않는다.
이 섹션은 branch 착수자가 자기 해석으로 갈라지지 않도록 하는 기본 결정값입니다. branch 작업 중 더 나은 기준이 발견되면 이 값을 바꾸되, 변경 사유와 대체 테스트 계약을 함께 남깁니다.
### Blocking Defaults
아래 15개 항목은 모든 branch의 선행 default입니다. 이 표와 충돌하는 branch note는 해당 branch가 아니라 이 프로젝트 노트를 먼저 수정해야 합니다.
| 항목 | 기본 결정 | SSOT branch | 실패 조건 |
| --- | --- | --- | --- |
| package layout | Gradle multi-module을 기본으로 두고 `domain-core` / `application-core` / `adapter-*` / `shared-contract` / `app-bootstrap` / `sample-portfolio` 책임을 분리 | `feature-skeleton-package-blueprint-contract` | module boundary와 package responsibility가 혼재되어 새 기능 위치를 판정할 수 없음 |
| transaction boundary | Spring `@Transactional`을 application 구현체에 직접 두지 않고 `TransactionPort` 또는 `TransactionalUseCaseRunner`로 추상화 | `feature-application-port-usecase-contract` | application layer가 Spring transaction annotation을 직접 import |
| mapper tool | 기본은 수기 mapper + record canonical constructor. MapStruct는 optional profile이며 generated code exemption 필요 | `feature-boundary-validation-mapping-contract` | mapper 도구가 branch마다 다르거나 generated code 예외가 architecture rule에 없음 |
| error envelope schema | envelope schema와 `error.category` enum은 foundation branch가 단일 owner | `feature-operational-error-observability-foundation` | business/schema/validation branch가 envelope field를 독자 정의 |
| OpenAPI drift | drift 집행 권한은 verification suite가 단일 owner, API/schema branch는 producer | `feature-contract-verification-test-suite` | OpenAPI snapshot과 실제 envelope가 불일치해도 build 통과 |
| migration runner | Flyway를 app startup에서 실행하되 readiness는 migration 완료 후에만 healthy. 운영에서 별도 job 전환 가능 | `feature-migration-startup-contract` | migration 실패 또는 진행 중 readiness가 healthy |
| scheduler/outbox lock | single-instance 기본. multi-instance 활성화 시 DB advisory lock을 기본값으로 사용 | `feature-background-job-async-contract` | scheduled job/outbox publisher가 multi-instance에서 중복 실행 가능 |
| broker | Kafka 강제 안 함. core는 broker-agnostic outbox contract만 제공하고 Kafka는 optional adapter | `feature-domain-event-outbox-contract` | domain event가 Kafka transport type을 직접 가짐 |
| circuit breaker/retry | Resilience4j 기본, Spring Retry는 simple blocking retry에만 예외 허용 | `feature-outbound-http-client-baseline` | retry/circuit breaker metric 이름과 정책이 adapter마다 다름 |
| container base image | Temurin JRE slim 기본, distroless는 runtime debug/runbook 보강 후 허용 | `feature-container-runtime-contract` | base image가 branch마다 다르거나 non-root/JVM memory 기준이 없음 |
| API versioning | URI prefix `/v1` 기본, `X-Api-Version`은 compatibility 실험용 보조 header | `feature-api-contract-baseline` | 같은 endpoint가 path/header/media type versioning을 섞어 사용 |
| idempotency key scope | `(authenticatedPrincipal, idempotencyKey, useCaseName)` 기본, tenant 활성화 시 tenant를 앞에 추가 | `feature-rate-limit-idempotency-contract` | user 간 key collision 또는 use case 간 replay 오염 가능 |
| rate-limit key | authenticated는 principal 기준, unauthenticated는 IP + normalized route 기준. tenant 활성화 시 tenant를 prefix로 추가 | `feature-rate-limit-idempotency-contract` | tenant/user/API key/IP 기준이 branch마다 다름 |
| bootstrap command | `./gradlew bootstrap` 기본. 없는 경우 `./gradlew test` + `docker compose up` wrapper로 제공 | `feature-developer-experience-contract` | 신규 팀이 첫 실행 명령을 문서에서 판정할 수 없음 |
| Testcontainers policy | persistence/outbound integration test부터 강제, unit/architecture/contract test는 Testcontainers 금지 | `feature-test-taxonomy-fixture-contract` | contract test가 컨테이너 의존으로 느려지거나 CI 실패 원인을 흐림 |
### SSOT Owner Map
동일 계약을 여러 branch가 다루더라도 owner는 하나입니다. owner 외 branch는 producer 또는 consumer로만 기록합니다.
> 새 결정 추가 전 본 표 grep 의무 — 동일 영역의 owner 가 이미 있으면 충돌 검토.
| contract area | single SSOT owner | consumers/producers | rule |
| --- | --- | --- | --- |
| OpenAPI / schema drift | `feature-contract-verification-test-suite` | api baseline, compatibility, schema serialization | verification이 release-blocking 판정권을 가짐 |
| idempotency key | `feature-rate-limit-idempotency-contract` | api baseline, transaction, outbox, tenant | key shape와 replay semantics는 한 곳에서만 변경 |
| DLQ / retry policy | `feature-background-job-async-contract` | outbox, outbound HTTP | dead-letter abstraction은 background branch가 소유 |
| error envelope schema | `feature-operational-error-observability-foundation` | business validation, schema serialization | envelope field와 category enum은 foundation에서만 final |
| requestId/traceId/correlationId meaning | `feature-operational-error-observability-foundation` | distributed tracing, log management | ID 의미와 required 여부는 foundation에서 final |
| sample-portfolio fixture | `feature-sample-domain-contract-fixture` | verification, DX, scorecard, onboarding | sample scenario와 minimum model은 sample fixture branch만 변경 |
| actuator/health endpoint shape | `feature-runtime-health-lifecycle-contract` | management actuator security | runtime-health가 shape owner, security는 exposure policy owner |
| MDC/log key standard | `feature-operational-error-observability-foundation` | log management, metrics alerting | key 이름은 foundation registry와 일치해야 함 |
| **HTTP status ↔ envelope `error.code` 매핑** | [[raw/branch-notes/feature-operational-error-observability-foundation]] (registry `error-codes.yaml``http_status` column) | api-contract-baseline D11 (mapping consistency contract test producer) | individual mapping 변경은 registry-governance 절차 + foundation branch SSOT |
| **API versioning** (`/v1` URI prefix) | [[raw/branch-notes/feature-api-contract-baseline]] D2/D6 | api-compatibility-deprecation (Sunset header 발행 시점) | path version 외 supplemental header (`X-Api-Version`) 만 허용 |
| **HTTP header registry** (`headers.yaml` 15 rows) | `feature-api-contract-baseline` (cross-owner: idempotency, tracing, tenant, compat, security 모두 cross-cite) | tracing/tenant/security/compat 모든 branch | 새 header 추가는 본 registry yaml + api-baseline branch 경유 |
| **HTTP method 지원/실패 분류** (D12 405/Allow, D13 HEAD/OPTIONS) | [[raw/branch-notes/feature-api-contract-baseline]] D12/D13 | (no counterpart — leaf) | 405 응답 + `Allow` MUST, HEAD MUST 자동 mirror |
| **PATCH content type + mapper** | [[raw/branch-notes/feature-boundary-validation-mapping-contract]] B2 (ArchUnit `no_merge_patch_json_media_type_string` enforced) | api-contract-baseline D14 (정정 — content type 정책만 consume) | RFC 7396 (merge-patch+json) / RFC 6902 (json-patch+json) 모두 *미채택*`application/json` only · absent/null/value 3-상태 wrapper |
| **Conditional request** (ETag / If-Match / If-None-Match / 304 / 412) | [[raw/branch-notes/feature-api-contract-baseline]] D15 | sample-portfolio fixture (WorkLogVersion 이 ETag derivation source) | DB optimistic lock 과 HTTP 412 가 동일 conflict 의 두 표현 |
| **Response cache policy + Vary header** | [[raw/branch-notes/feature-api-contract-baseline]] D16 (HTTP header 정책) | `feature-cache-consistency-contract` (cache layer 구현 SSOT) | default `Cache-Control: no-store`, Vary 의무 |
| **Long-running operation (LRO)** | [[raw/branch-notes/feature-api-contract-baseline]] D17 (polling-only) | webhook callback 패턴은 별도 `feature-webhook-outbound-contract` | 202 + `Location: /v1/operations/{id}` + polling endpoint |
| **Pagination index base + size cap** | [[raw/branch-notes/feature-api-contract-baseline]] D18 | (no counterpart — leaf) | `page` 0-indexed, `size` default 20 / max 100 |
| **Resource URL naming convention** | [[raw/branch-notes/feature-api-contract-baseline]] D19 | ArchUnit/architecture branch (controller mapping 검증) | plural + lowercase + AIP-122 regex |
| **Sort parameter syntax** | [[raw/branch-notes/feature-api-contract-baseline]] D20 | schema-serialization (field name case 정합) | Spring `Pageable` native `?sort=field,direction` |
| **Filter parameter syntax** | [[raw/branch-notes/feature-api-contract-baseline]] D21 | (no counterpart — leaf, 복잡 filter 는 future) | flat key=value (equality only) |
| **Cursor pagination shape** | [[raw/branch-notes/feature-api-contract-baseline]] D22 | `feature-security-operational-baseline` (HMAC key rotation cross-link) | opaque base64 + HMAC + 24h TTL |
| **Bulk operation URL pattern** | [[raw/branch-notes/feature-api-contract-baseline]] D23 | [[raw/branch-notes/feature-boundary-validation-mapping-contract]] B14 (BulkEnvelope.partial — async only), foundation (BATCH_PARTIAL_FAILURE — async only), [[raw/branch-notes/feature-api-contract-baseline]] D17 LRO 결합 (async batch 의 polling endpoint) | AIP-136 colon-verb (`:batchCreate`) + AIP233-C7 sync MUST atomic + partial failure 는 async LRO 만 |
| **Response Date header** | [[raw/branch-notes/feature-api-contract-baseline]] D24 | (no counterpart — leaf) | Spring/Tomcat default 자동 발행, 비활성화 금지 |
| **CORS allowlist / preflight policy** | [[raw/branch-notes/feature-security-operational-baseline]] D9 | [[raw/branch-notes/feature-api-contract-baseline]] D13 (OPTIONS preflight envelope 우회 정책 consume) | wildcard + credentials 금지 (FETCH-CORS-C3 normative) |
| **WWW-Authenticate / Server / X-Powered-By header suppression** | `feature-security-operational-baseline` | api-contract-baseline | API branch 는 forbid 만 cross-cite |
| **JSON field naming case** (camelCase vs snake_case) | `feature-schema-serialization-contract` | api-contract-baseline (envelope `meta.*` 가 camelCase 라는 cross-cite) | Jackson default + project-internal 결정 |
| **Date/Time/Decimal serialization** | `feature-schema-serialization-contract` | api-contract-baseline | ISO-8601 UTC + BigDecimal HALF_UP 등 |
| **Webhook outbound contract** (2026-05-31 신설) | `feature-webhook-outbound-contract` (scaffolding 단계) | api-contract-baseline (inbound API surface 와 분리) | signature/replay/retry/observability — 결정 박힌 후 본 표 update |
| **Streaming response (SSE/WebSocket/long-poll/chunked)** (2026-05-31 신설) | `feature-streaming-response-contract` (scaffolding 단계) | api-contract-baseline (out of scope 분리) | 1차 결정: 지원 여부 자체 |
| **Resource ID format** (ULID 26-char Crockford base32) | [[raw/branch-notes/feature-resource-identifier-contract]] D1~D19 | api-contract-baseline (URL path variable), boundary (ArchUnit rules), idempotency (Idempotency-Key 별개 명시), log-management (PII 분류), security (SecureRandom 의무) | ULID time-ordered + project §34 PostgreSQL `uuid` native + sample-portfolio `WorkLogId = "01ARZ3NDEKTSV4RRFFQ69G5FAV"` |
| **General-purpose distributed lock provider** (`distributedLockProvider` bean 메커니즘 / tx commit 정합 / lease 계약) (2026-06-12 신설) | [[raw/branch-notes/feature-distributed-lock-contract]] D1~D8 | background-job-async (scheduler/outbox 적용처 — consume), cache-consistency (Redis 분기 의존성 공유), env-driven-runtime-configuration (flag + presence 강제 owner) | bean 이름·메커니즘·해제 vs commit 순서는 본 branch 단일 owner. cache stampede lock(`CACHE_STAMPEDE_LOCK_TIMEOUT`)은 cache branch 소유로 불변 |
| **Dependency vulnerability policy** (SCA 스캐너 / CVSS 차단 임계값 / KEV override / suppression governance / 보안 update 자동화 / license scan) (2026-06-15 신설) | [[raw/branch-notes/feature-dependency-vulnerability-management-contract]] D1~D10 | ci-quality-gates (vuln scan **gate wiring** — consume), container-runtime (image scan wiring — 동일 severity 정책 consume), build-release-supply-chain (release-block **posture** + dependency-locking 선행조건 producer) | scanner·severity·suppression·update·license 정책은 본 branch 단일 owner. ci-gates D5 의 OWNER_AMBIGUITY(scanner 미결) + supply-chain D2(severity)/D3(update) 의 UNSUPPORTED 스텁이 본 branch 로 위임 정합 |
### Cross-Branch Decision Conflict Check Procedure
새 결정 추가 전 다음 절차 의무 (cross-branch SSOT 충돌 차단):
1. **본 §25 SSOT Owner Map 의 `contract area` 컬럼 grep** — 동일 영역의 owner 가 이미 있는지 확인.
- 동일 owner 가 있으면: 본 branch 가 *new owner* 가 아닌 *consumer/producer* 로만 결정 가능.
- 동일 owner 가 없으면: 본 branch 가 new owner 가 될 수 있음 — 그러나 *주제어 다른* 결정이 sibling branch 에 있을 수 있음 → 2번 진행.
2. **sibling branch grep**`grep -rn "<주요 키워드>" raw/branch-notes/feature-*.md` 로 sibling 의 §결정 사항 / §Decision Evidence Map / §Decisionized Work Items 에 동일 키워드 검색.
- 예: PATCH 결정 추가 전 `grep -rn "PATCH\|merge-patch" raw/branch-notes/feature-*.md` 로 boundary branch B2 사전 발견 가능했음.
3. **충돌 발견 시 적용 기준**:
- **검증 깊이 우선**: ArchUnit / static rule / contract test 가 있는 결정이 SSOT.
- **결정 도메인 우선**: 결정이 *어느 영역의 자연스러운 책임*인지 — PATCH mapper 는 boundary 영역.
- **시간 순서**: 같은 깊이 + 같은 도메인이면 *먼저 박힌* 결정이 SSOT, 늦게 박은 것이 정정.
4. **본 §25 표에 신규 row 추가** — 결정 박은 후 본 표에 owner + consumers/producers + rule 명시.
이 절차는 추후 `/lint` 명령 또는 `wiki-adversarial-reviewer` 가 자동 검사하도록 확장 가능 (현재는 수동 절차).
### Multi-Instance Guardrail
이 skeleton의 core contract는 기본적으로 single-instance에서 완결됩니다. HPA, multi-replica scheduler, distributed rate limit, outbox publisher leader election, migration concurrent startup, distributed cache lock은 `multi-instance contract package`가 활성화될 때만 지원 범위에 들어옵니다.
<!-- section-id: project-decisions -->
## 6.1 안정 결정 레지스트리
> Project contract v2의 project-wide decision SSOT. §25의 15개 blocking default와 §34 Stack Commitment를 stable ID로 pin한다.
| Decision ID | Revision | Domain | Decision Summary | Status | Owner | Evidence |
|---|---:|---|---|---|---|---|
| `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-MODULE-LAYOUT-001` | 1 | `module-layout` | Gradle multi-module에서 domain-core·application-core·adapter-*·shared-contract·app-bootstrap·sample-portfolio 책임을 분리한다 | `active` | [[raw/project-notes/ca-skeleton-operational-contract]] | §25 Blocking Defaults `package layout` |
| `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-TRANSACTION-001` | 1 | `transaction` | application은 Spring transaction annotation 대신 TransactionPort 또는 TransactionalUseCaseRunner를 사용한다 | `active` | [[raw/project-notes/ca-skeleton-operational-contract]] | §25 `transaction boundary` |
| `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-MAPPING-001` | 1 | `mapping` | 수기 mapper와 record canonical constructor가 default이며 MapStruct는 optional profile이다 | `active` | [[raw/project-notes/ca-skeleton-operational-contract]] | §25 `mapper tool` |
| `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-ERROR-ENVELOPE-001` | 1 | `error-envelope` | foundation이 envelope schema와 error.category enum의 단일 owner다 | `active` | [[raw/project-notes/ca-skeleton-operational-contract]] | §25 `error envelope schema` |
| `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-OPENAPI-001` | 1 | `openapi` | verification suite가 OpenAPI drift의 release-blocking 판정권을 소유한다 | `active` | [[raw/project-notes/ca-skeleton-operational-contract]] | §25 `OpenAPI drift` |
| `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-MIGRATION-001` | 1 | `migration` | Flyway는 startup에서 실행하고 migration 완료 후에만 readiness를 healthy로 전환한다 | `active` | [[raw/project-notes/ca-skeleton-operational-contract]] | §25 `migration runner` |
| `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-SCHEDULER-LOCK-001` | 1 | `scheduler-lock` | single-instance가 default이며 multi-instance scheduler/outbox는 DB advisory lock을 사용한다 | `conditional-default` | [[raw/project-notes/ca-skeleton-operational-contract]] | §25 `scheduler/outbox lock` |
| `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-EVENT-BROKER-001` | 1 | `event-broker` | core는 broker-agnostic outbox를 제공하고 Kafka는 optional adapter로 둔다 | `active` | [[raw/project-notes/ca-skeleton-operational-contract]] | §25 `broker` |
| `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-RESILIENCE-001` | 1 | `resilience` | Resilience4j가 default이며 Spring Retry는 simple blocking retry에만 허용한다 | `active` | [[raw/project-notes/ca-skeleton-operational-contract]] | §25 `circuit breaker/retry` |
| `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-CONTAINER-001` | 1 | `container` | Temurin JRE slim이 default이며 distroless는 debug/runbook 보강 후 허용한다 | `conditional-default` | [[raw/project-notes/ca-skeleton-operational-contract]] | §25 `container base image` |
| `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-API-VERSIONING-001` | 1 | `api-versioning` | URI prefix /v1이 default이며 X-Api-Version은 compatibility 실험용 보조 header다 | `active` | [[raw/project-notes/ca-skeleton-operational-contract]] | §25 `API versioning` |
| `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-IDEMPOTENCY-001` | 1 | `idempotency` | idempotency scope는 principal·key·useCase이며 tenant 활성화 시 tenant를 prefix한다 | `active` | [[raw/project-notes/ca-skeleton-operational-contract]] | §25 `idempotency key scope` |
| `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-RATE-LIMIT-001` | 1 | `rate-limit` | authenticated는 principal, unauthenticated는 IP와 normalized route를 rate-limit key로 사용한다 | `active` | [[raw/project-notes/ca-skeleton-operational-contract]] | §25 `rate-limit key` |
| `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-BOOTSTRAP-001` | 1 | `bootstrap` | 신규 환경의 default 진입 명령은 ./gradlew bootstrap이다 | `active` | [[raw/project-notes/ca-skeleton-operational-contract]] | §25 `bootstrap command` |
| `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-TESTCONTAINERS-001` | 1 | `testcontainers` | Testcontainers는 persistence/outbound integration test에 사용하고 unit·architecture·contract test에서는 금지한다 | `active` | [[raw/project-notes/ca-skeleton-operational-contract]] | §25 `Testcontainers policy` |
| `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-STACK-LANGUAGE-001` | 1 | `stack-language` | application language는 Java 21 LTS다 | `active` | [[raw/project-notes/ca-skeleton-operational-contract]] | §34 Stack Matrix `Language` |
| `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-STACK-FRAMEWORK-001` | 1 | `stack-framework` | framework는 Spring Boot 3.5.14다 | `active` | [[raw/project-notes/ca-skeleton-operational-contract]] | §34 Stack Matrix `Framework` |
| `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-STACK-ORM-001` | 1 | `stack-orm` | ORM은 Spring Boot transitive Hibernate ORM 6.5.x다 | `active` | [[raw/project-notes/ca-skeleton-operational-contract]] | §34 Stack Matrix `ORM` |
| `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-STACK-JSON-001` | 1 | `stack-json` | JSON stack은 Spring Boot transitive Jackson 2.18.x다 | `active` | [[raw/project-notes/ca-skeleton-operational-contract]] | §34 Stack Matrix `JSON` |
| `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-STACK-DATABASE-001` | 1 | `stack-database` | database는 PostgreSQL 16 단일 stack이다 | `active` | [[raw/project-notes/ca-skeleton-operational-contract]] | §34 Stack Matrix `DB` |
| `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-STACK-MIGRATION-001` | 1 | `stack-migration` | schema migration tool은 Spring Boot transitive Flyway다 | `active` | [[raw/project-notes/ca-skeleton-operational-contract]] | §34 Stack Matrix `Migration` |
| `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-STACK-BUILD-001` | 1 | `stack-build` | build tool은 Gradle Groovy DSL이다 | `active` | [[raw/project-notes/ca-skeleton-operational-contract]] | §34 Stack Matrix `Build tool` |
| `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-STACK-TEST-001` | 1 | `stack-test` | test framework는 JUnit 5다 | `active` | [[raw/project-notes/ca-skeleton-operational-contract]] | §34 Stack Matrix `Test framework` |
| `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-STACK-ARCHTEST-001` | 1 | `stack-archtest` | architecture test는 archunit-junit5 1.3.0을 사용한다 | `active` | [[raw/project-notes/ca-skeleton-operational-contract]] | §34 Stack Matrix `Architecture test` |
| `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-STACK-RANDOM-001` | 1 | `stack-random` | ULID·idempotency key·token 생성의 random source는 SecureRandom이다 | `active` | [[raw/project-notes/ca-skeleton-operational-contract]] | §34 Stack Matrix `Random source` |
| area | single-instance default | multi-instance activation requirement |
| --- | --- | --- |
| scheduler | single worker only | DB advisory lock 또는 ShedLock contract test |
| outbox publisher | one publisher in app process | publisher ownership lock + duplicate publish idempotency |
| rate limiter | in-memory or app-local policy allowed only for local/dev | Redis/distributed counter + tenant/principal key contract |
| migration runner | one app startup runner | platform-level one-shot job or migration lock verification |
| cache stampede | local test policy only | distributed lock or stale-while-revalidate policy |
| idempotency key concurrency | DB unique constraint required | serializable insert-or-read contract test |
multi-instance를 지원한다고 말하려면 위 행 중 적용 영역의 contract test가 있어야 합니다. 없으면 문서와 README에 `single-instance skeleton`이라고 명시합니다.
### Minimum Missing-Area Defaults
아래 영역은 full implementation이 없어도 skeleton default는 있어야 합니다.
| area | default | owner branch |
| --- | --- | --- |
| deployment manifest sync | app shutdown timeout, `terminationGracePeriodSeconds`, `preStop`, readiness/liveness/startup probe 값을 한 표에서 관리 | `feature-container-runtime-contract` |
| DSR | delete/export request는 core domain out of scope이나 PII inventory, redaction point, audit log retention은 privacy branch가 관리 | `feature-data-retention-privacy-contract` |
| feature flag | runtime toggle은 optional. 기본은 env-startup flag이며 canary/runtime flag 도입 시 registry row 필요 | `feature-env-driven-runtime-configuration` |
| signed artifact | SBOM + image digest를 기본으로 하고, release branch에서 Cosign/SLSA provenance를 delivery target으로 둠 | `feature-build-release-supply-chain-contract` |
| dependency upgrade | Renovate 또는 Dependabot 중 하나를 선택하고 security PR은 CI quality gate와 연결 | `feature-build-release-supply-chain-contract` |
| JWT key rotation | JWKS refresh failure, stale key, unknown `kid`, rotation overlap window를 security failure catalog에 포함 | `feature-security-operational-baseline` |
| JVM ergonomics | `-XX:MaxRAMPercentage=75`, UTC timezone, OOMKilled vs JVM OOM 분류를 container/runtime branch가 소유 | `feature-container-runtime-contract` |
| CORS | allowlist + preflight cache seconds는 security branch owner, gateway override 시 mapping 필요 | `feature-security-operational-baseline` |
| 항목 | 기본 결정 | 예외 허용 조건 | 테스트 실패 조건 |
| --- | --- | --- | --- |
| response envelope field | `success`, `data`, `error`, `meta` | 외부 gateway 표준이 이미 존재할 때만 adapter에서 변환 | 실패 응답에 `error.code`, `error.category`, `meta.requestId` 누락 |
| error code naming | category 기반 `UPPER_SNAKE_CASE` | provider-specific code는 `dependency.code`에만 보관 | raw exception class name을 code로 사용 |
| repository capability annotation | `@UseCaseRepositoryAccess` | AOP 대신 ArchUnit/compile-time checker를 쓸 경우 이름만 변경 가능 | 선언 없는 write/bulk/sensitive repository 접근 |
| env prefix | application-owned key는 `APP_` prefix 사용 | Spring/infra 표준 env는 원래 이름 유지 | 동일 의미 env가 profile마다 다른 이름으로 존재 |
| log schema | OpenTelemetry semantic convention을 우선 참고하고 app-specific field는 `app.*`, error field는 `error.*`로 둠 | 수집기가 ECS를 강제하면 adapter mapping 문서 필요 | trace/log/error field naming이 branch마다 다름 |
| tracing implementation | Micrometer Tracing + OpenTelemetry exporter 기준 | exporter 미사용 local profile 가능 | traceId가 inbound/outbound/async/log에서 연결되지 않음 |
| optional adapter packaging | optional module로 분리하고 disabled env가 기본 | 단순 문서 샘플은 `sample` source set 허용 | disabled adapter bean이 기본 앱 시작에 필요 |
| sample domain | `sample-portfolio` | 더 작은 fixture가 모든 계약을 검증할 때만 변경 | sample 없이 boundary/repo/transaction/error 계약을 검증 |
| OpenAPI drift | generated OpenAPI snapshot + contract test | mature openapi-diff 도구 도입 가능 | schema와 실제 envelope/field가 불일치해도 build 통과 |
| idempotency storage | DB table 기반 key/result/status/ttl 저장 | Redis는 optional adapter에서만 보조 저장소 | retry 시 duplicate write 발생 |
| migration tool | Flyway 기본 | 조직 표준이 Liquibase일 때만 변경 | migration 실패 후 readiness가 healthy |
| metric naming | Micrometer naming + low-cardinality tag만 허용 | 수집기 표준 prefix가 있을 때 mapping | userId/orderId 같은 high-cardinality tag 사용 |
| alert severity | `P1`, `P2`, `P3` | 조직 on-call 표준 명칭 사용 가능 | severity 없는 alert |
| secret manager | 기본 계약은 env/secret file, prod는 external secret manager 연동 가능하도록 추상화 | local은 `.env` 허용 | prod에서 secret/config dump 노출 |
| actuator management port | prod/staging은 분리 권장, local/dev는 app port 허용 | platform ingress가 별도 보호를 제공할 때만 단일 port | prod에서 env/configprops 노출 |
| multi-tenancy | skeleton core는 out of scope, tenant header는 기본 거부 | tenant branch에서 명시 활성화 | tenant context 없이 tenant-scoped repository 접근 |
| file upload/download | sample v1에는 미포함, core contract만 제공 | file branch에서 sample fixture 추가 가능 | path traversal/content type/size limit 미검증 |
| cache consistency | core contract에 원칙을 두고 Redis optional adapter에서 구현 | in-memory sample cache는 테스트 전용 | cache miss를 장애로 분류하거나 invalidation 실패를 무시 |
| domain onboarding template | `domain-core` / `application-core` / `adapter-*` / `shared-contract` 기준으로 read/write module slice를 추가 | read-only feature는 write/idempotency/outbox 구성을 생략 가능 | controller/use case/domain/repository 중 하나만 단독 추가되어 계약 검증 불가 |
| command/query split | command use case와 query use case 분리 | 단순 admin endpoint도 명시적으로 하나를 선택 | write use case가 query naming으로 transaction/capability를 우회 |
| port naming | inbound는 `*UseCase`, outbound는 `*Port` | 조직 표준 이름이 있으면 branch에서 대체 가능 | application이 infrastructure adapter 구현체를 직접 의존 |
| domain event | domain event와 integration event 분리 | 외부 발행이 없는 내부 event는 integration mapping 생략 가능 | domain event가 Kafka/HTTP/Slack 같은 transport detail을 가짐 |
| sample adoption | `sample-portfolio`은 제거 가능한 fixture이며 새 도메인은 구조만 참고 | 교육용 프로젝트에서는 sample 유지 가능 | production package가 sample package를 import |
| package blueprint | `domain-core` / `application-core` / `adapter-*` / `shared-contract` / `app-bootstrap` / `sample-portfolio` 멀티모듈 기본 구조 사용 | demo/readme용 single-module 축소형은 같은 responsibility mapping 보존 시 허용 | domain/business code가 shared-contract 또는 adapter module에 들어감 |
| registry governance | error/env/header/log/metric/capability는 registry로 관리 | 외부 platform 표준이 있으면 mapping table 필수 | 문자열/enum이 문서나 구현에 ad hoc으로 흩어짐 |
| test taxonomy | unit/contract/architecture/slice/integration/smoke를 분리 | 작은 프로젝트는 디렉터리만 합칠 수 있음 | contract test와 integration test가 섞여 실패 원인을 구분 못함 |
| readiness score | canonical 승급 전 scorecard 전 항목 통과 | raw 초안 단계는 미통과 허용 | 미통과 항목이 있는데 100점 문서로 선언 |
## 26. Universal Acceptance Gate
각 branch는 완료 전에 아래 질문에 모두 답할 수 있어야 합니다. 하나라도 답하지 못하면 canonical 문서로 승급하지 않습니다.
```text
1. 이 기준은 어떤 실패를 막는가?
2. 구현자가 선택해야 하는 기본값은 무엇인가?
3. 허용되는 예외는 무엇이며 조건은 무엇인가?
4. 절대 금지되는 것은 무엇인가?
5. 어떤 env/log/response/test field가 필수인가?
6. 어떤 테스트가 깨져야 이 계약 위반을 알 수 있는가?
7. sample-portfolio으로 검증 가능한가?
8. sample-portfolio 제거 후에도 skeleton core에 남는가?
9. 실제 도메인 feature가 이 기준을 그대로 따라갈 수 있는가?
10. 이 내용을 wiki/projects canonical 문서의 어느 섹션으로 승급할 것인가?
```
100점 기준은 문장 완성도가 아니라 contract 강제력입니다.
```text
문서만 읽고도 구현 방향이 하나로 수렴하고,
테스트만 봐도 계약 위반을 감지할 수 있으며,
sample-portfolio을 제거한 뒤에도 실제 도메인 feature가 같은 구조로 들어갈 수 있어야 한다.
```
## 27. 100점 Readiness Scorecard
아래 항목 중 하나라도 `No`이면 이 skeleton은 100점이 아닙니다.
| 영역 | 질문 | 통과 기준 |
| --- | --- | --- |
| 구조 | 새 도메인 feature의 위치가 명확한가? | module blueprint와 onboarding slice가 일치 |
| 응답 | 모든 성공/실패 응답이 envelope를 따르는가? | OpenAPI snapshot과 contract test로 강제 |
| 오류 | error category/code/status/retryable/log level이 registry에 있는가? | ad hoc error string 금지 |
| 경계 | request/application/domain/response/filter mapper가 모두 있는가? | 경계 우회 architecture test |
| 예외 | raw exception이 presentation까지 새지 않는가? | leakage contract test |
| 로그 | 필수 log field와 금지 field가 테스트되는가? | log capture test |
| trace | inbound/outbound/async/message trace가 연결되는가? | propagation contract test |
| env | profile별 env matrix와 fail-fast가 있는가? | startup smoke test |
| repo | use case capability와 repository capability가 매칭되는가? | architecture/contract test |
| adapter | 모든 dependency failure가 같은 언어로 분류되는가? | adapter failure mapping test |
| domain | domain이 framework-neutral한가? | forbidden import test |
| sample | sample-portfolio 제거 후 core가 살아 있는가? | sample removal smoke test |
| CI | contract violation이 release-blocking인가? | CI quality gate |
| 운영 | alert/runbook/metric/log/trace가 연결되는가? | operational runbook link |
| 보안 | token/PII/secret/body가 노출되지 않는가? | privacy/log leakage test |
## 28. Review Remediation Ledger
이 섹션은 2026-05-22 senior review의 지적을 100% 추적하기 위한 ledger입니다. `Closed by default`는 이 raw project note와 branch note에 기본값/owner/failure condition이 반영되었다는 뜻이고, 구현 완료를 뜻하지 않습니다.
### Critical Defaults
| review item | closed by default | owner branch | required branch evidence |
| --- | --- | --- | --- |
| package layout | Gradle multi-module Clean Architecture (`domain-core`, `application-core`, `adapter-*`, `shared-contract`, `app-bootstrap`, `sample-portfolio`) | `feature-skeleton-package-blueprint-contract` | module dependency rule + package blueprint + architecture rule |
| transaction boundary | application responsibility via `TransactionPort`/`TransactionalUseCaseRunner`, no direct Spring transaction import | `feature-application-port-usecase-contract` | forbidden import test + write use case contract |
| mapper tool | manual mapper + record canonical constructor; MapStruct optional with generated exemption | `feature-boundary-validation-mapping-contract` | mapper boundary test |
| error envelope schema SSOT | foundation branch owns envelope/category/ID meanings | `feature-operational-error-observability-foundation` | envelope contract test |
| OpenAPI drift SSOT | verification suite owns drift gate | `feature-contract-verification-test-suite` | generated snapshot + drift check |
| migration runner | Flyway app startup default, readiness healthy only after migration success | `feature-migration-startup-contract` | migration failure startup/readiness test |
| scheduler/outbox lock | single-instance default, DB advisory lock or ShedLock required for multi-instance | `feature-background-job-async-contract` | overlap/duplicate publish test |
| broker | broker-agnostic outbox core, Kafka optional adapter | `feature-domain-event-outbox-contract` | transport-free domain event test |
| circuit breaker/retry | Resilience4j default, Spring Retry limited exception | `feature-outbound-http-client-baseline` | retry/circuit metrics test |
| container base image | Temurin JRE slim default; distroless requires runbook/debug proof | `feature-container-runtime-contract` | non-root + JVM memory smoke |
| versioning | `/v1` URI prefix default; `X-Api-Version` supplemental only | `feature-api-contract-baseline` | OpenAPI version path test |
| idempotency key scope | `(principal, key, useCaseName)`, tenant prefix if enabled | `feature-rate-limit-idempotency-contract` | duplicate replay/tenant collision test |
| rate-limit key | authenticated principal; unauthenticated IP + normalized route; tenant prefix if enabled | `feature-rate-limit-idempotency-contract` | 429 envelope + key scope test |
| bootstrap tool | `./gradlew bootstrap` default | `feature-developer-experience-contract` | bootstrap smoke |
| Testcontainers policy | integration tests only; unit/contract/architecture no container | `feature-test-taxonomy-fixture-contract` | test taxonomy ownership table |
### SSOT Closures
| duplicated area | single owner | consumer rule |
| --- | --- | --- |
| OpenAPI/schema drift | `feature-contract-verification-test-suite` | API/schema branches produce artifacts only |
| idempotency key | `feature-rate-limit-idempotency-contract` | transaction/outbox/API/tenant consume key shape |
| DLQ/retry policy | `feature-background-job-async-contract` | outbox/outbound map their failures into the common DLQ vocabulary |
| error envelope schema | `feature-operational-error-observability-foundation` | business/schema branches cannot add envelope fields |
| trace/request/correlation ID semantics | `feature-operational-error-observability-foundation` | tracing/log branches consume names and meanings |
| sample-portfolio fixture | `feature-sample-domain-contract-fixture` | verification/DX/scorecard/onboarding consume scenarios |
| actuator/health endpoint shape | `feature-runtime-health-lifecycle-contract` | management security controls exposure only |
| MDC/log key standard | `feature-operational-error-observability-foundation` | log/metric branches use registry names |
### Multi-Instance Closures
| risk | default closure | required if multi-instance is claimed |
| --- | --- | --- |
| distributed scheduler lock | single worker only | ShedLock or DB advisory lock test |
| outbox publisher leader election | one publisher in app process | publisher ownership lock + idempotent publish |
| distributed rate limit | local/dev only | Redis/distributed counter contract |
| migration concurrent startup | one app startup runner | platform job or migration lock proof |
| cache distributed lock/stampede | local test policy only | distributed lock or stale-while-revalidate proof |
| idempotency concurrent arrival | DB unique insert-or-read | concurrent replay test |
### Missing Area Closures
| missing area | default closure | owner branch |
| --- | --- | --- |
| deployment manifest sync | app timeout, `terminationGracePeriodSeconds`, `preStop`, startup/readiness/liveness probes must share one table | `feature-container-runtime-contract` |
| API gateway/WAF/Ingress | gateway may reject TLS/request-size/WAF before app; app must document envelope bypass and log correlation | `feature-security-operational-baseline` |
| DSR delete/export | privacy branch owns DSR intake, identity verification, export/delete workflow, audit evidence | `feature-data-retention-privacy-contract` |
| disaster recovery/restore drill | backup without restore drill is non-compliant; quarterly local/staging restore smoke default | `feature-persistence-failure-baseline` |
| feature flag system | env-startup flags default; runtime/canary flags need registry row and owner | `feature-env-driven-runtime-configuration` |
| signed artifact/SLSA/provenance | SBOM + image digest baseline; Cosign signature and provenance are release targets | `feature-build-release-supply-chain-contract` |
| dependency upgrade policy | Renovate default, Dependabot allowed if org standard | `feature-build-release-supply-chain-contract` |
| JWT key rotation/JWKS refresh | unknown `kid`, stale JWKS, refresh failure, overlap window are explicit security failures | `feature-security-operational-baseline` |
| JVM ergonomics | `-XX:MaxRAMPercentage=75`, UTC, OOMKilled vs JVM OOM classification | `feature-container-runtime-contract` |
| DB read replica/lag | primary reads default; replica use requires max lag threshold and stale-read contract | `feature-persistence-failure-baseline` |
| CORS preflight/origin allowlist | explicit allowlist, credentials policy, max-age default, gateway override mapping | `feature-security-operational-baseline` |
| antivirus/file scanning | upload scanning is off by default; external gateway/worker/app-owner decision must be documented | `feature-file-resource-handling-contract` |
### Conflict Closures
| conflict | closure |
| --- | --- |
| domain logger ban vs invariant diagnostics | domain still has no logger; application translates invariant violation and logs client-safe reason code outside domain |
| `@Transactional` vs application independence | direct Spring transaction import forbidden; transaction port abstraction required |
| tracing disabled vs required `traceId` | generated opaque trace id remains in envelope; exporter/sampling may be disabled |
| migration readiness race | startup/readiness remains unhealthy until migration success and startup validation complete |
### Self-Contradiction Closures
| contradiction | closure |
| --- | --- |
| Work Item Contract exists but TODOs stay vague | branch notes must add `Decisionized Work Items` before canonical promotion; TODO list remains raw backlog only |
| scorecard 100점 but calculator out of scope | scorecard branch owns manual formula and evidence table; automation is optional, formula is not |
| runbook link required but unverifiable | runbook branch defines link format and CI/link-check smoke; broken placeholder links fail promotion |
### Phase A / B / C1 Closures (2026-05-22)
4차 audit 이후 다음 phase가 진행되었으며, 산출물은 본 문서와 `ca-tmpl/docs/registries/` 하위 yaml로 분리됩니다.
| phase | scope | 산출물 | status |
| --- | --- | --- | --- |
| Phase A | numeric conflict 15건, SSOT violation 7건, TODO drain 28+ 파일, 표 양식 자기모순(Work Item Contract 7-required → 4 mandatory + 3 conditional relax), 자기 중복 4건, intent clarification 5건 | 43 branch note 수정 | closed |
| Phase B | 7개 registry yaml 본문 작성 (총 196 rows, source-grounded) | `ca-tmpl/docs/registries/{error-codes,env-keys,secrets-classification,headers,mdc-keys,metrics,capabilities}.yaml` | closed |
| Phase C1 | 본 canonical contract 문서에 yaml SSOT reference 반영 + Phase A/B closures 기록 | 본 문서 §21 + §28 갱신, frontmatter `last_reviewed: 2026-05-22` | closed |
| Phase D1 | test contract 정밀화 (VAGUE 38건 → IMPLEMENTABLE, PARTIAL 88건의 3개 공통 패턴(capability marker / disabled detection / claim parsing) 결정 박기) | 43 branch note 정밀화 | closed |
| Phase D2 | runbook stub 5종 작성 (release-blocking 5 category 첫 운영 시나리오) | `ca-tmpl/docs/runbooks/*.md` | closed |
| Phase C2 | ca-tmpl 실 skeleton 코드 (별도 git repo) | build.gradle, application-*.yml, ArchUnit rules, TransactionPort, @UseCaseRepositoryAccess, capability enum, sample-portfolio entity/use case/fixture, Flyway script, OpenAPI snapshot, CI workflow, Cosign/SLSA, docker-compose.yml, .env.example (env-keys.yaml에서 generated) | pending (external) |
#### Phase A 세부 closures
**Numeric conflicts (4차 audit L1 / 15건)**: executor await 25s→19s (background-job-async); `database default``READ_COMMITTED` (application-port-usecase, transaction-concurrency SSOT 위임); `AUTHORIZATION``AUTHZ` (business-rule-validation enum 정합); MDC 4 vs 6 rationale (background-job span_id 자동·user_principal opt-in); "9 base + 2 = 11" 통일 (contract-verification); tenant_id cardinality vs ULID 분리 (metric tag 미사용, mapping id/cohort bucket); error_code cardinality registry 동기화; audit log retention 단일 owner = data-retention; outbox claim isolation = `READ_COMMITTED` + SKIP LOCKED 명시; log 10% vs trace 1% sampling 의도 분리; idempotency 24h vs JWT rotation 24h invariant; NESTED/NEVER forbidden 일관; alert dedup 5분 vs P1 2분 noise 억제 의도; file size 3계층 defense-in-depth (Spring 10MB / global 12MB / gateway 20MB); outbound HTTP shutdown retry suppression.
**SSOT violations (4차 audit L1 / 7건)**: audit retention 단일 owner = `feature-data-retention-privacy-contract` (log-management는 형식만 owns); outbox claim transaction isolation 명시 = `feature-domain-event-outbox-contract`; TaskDecorator SSOT = `feature-background-job-async-contract` (tenant/security 모두 consumer); security ↔ secrets 양방향 cross-link; runtime-health ↔ integration-adapter 양방향 cross-link; identifier 표기 layer mapping (MDC snake_case / envelope camelCase / HTTP header kebab-case) = `feature-distributed-tracing-contract`; flaky quarantine SSOT = `feature-ci-quality-gates-contract` (test-taxonomy consumer).
**TODO drain (28+ 파일)**: stale `planned` TODO를 표 row link 또는 제거. `needs-confirmation` 2건 의도적 retain (verification PII forbidden 구현 메커니즘 / scorecard real-domain dry-run checklist SSOT 분담).
**Self-duplicates 해소 (4건)**: transaction-concurrency isolation 2회→1회, management-actuator-security prod allowlist 2회→final list 1회, developer-experience DX Defaults 표 deprecated → Decisionized SSOT, ci-quality-gates Gate Matrix → Gate Ownership Matrix SSOT.
**표 양식 자기모순**: `feature-architecture-enforcement-rules`의 Work Item Contract 메타 표를 "7 required" → "4 mandatory (Decision/Allowed/Forbidden/Required contract test) + 3 conditional (Required registry update / Failure condition / Canonical extraction target)"로 relax. 14+ 파일의 5-column 표가 더 이상 자기모순 아님.
**Intent clarifications (5건)**: log 10% vs trace 1% sampling 의도 분리 (운영 진단 vs cost 제어), idempotency TTL ≤ JWT rotation overlap window invariant, negative cache 60s vs eventual consistency window 5s 독립축, alert dedup 5분 + P1 2분 noise 억제, file size 3계층 defense-in-depth.
#### Phase B 세부 closures
7개 yaml 본문 작성 결과:
| yaml | rows | 주요 정합 포인트 |
| --- | --- | --- |
| `error-codes.yaml` | 49 | category 분포 TRANSIENT_DEPENDENCY 15 · AUTH 8 · INTERNAL 8 · VALIDATION 6 · CONFLICT 4 · AUTHZ 3 · DATA_INTEGRITY 3 · RATE_LIMIT 1 · PERMANENT_DEPENDENCY 1 · NOT_FOUND 0. runbook coverage 100%. |
| `env-keys.yaml` | 51 | 15개 영역. `APP_` prefix 강제. `reload_policy: restart-only` default. classification cross-link with secrets-classification.yaml. |
| `secrets-classification.yaml` | 15 | secret 6 / sensitive-config 4 / public-config reference 5. rotation policy 5종 (restart-only / dual-bind-60s / overlap-24h / salt-rotation-90d / manual). prod sentinel prefix `__LOCAL_DEV_` 검증. |
| `headers.yaml` | 15 | naming 정합 (kebab/lowercase/W3C). direction inbound 3 / outbound 7 / both 5. mdc_key + envelope_meta_field cross mapping 4쌍. |
| `mdc-keys.yaml` | 19 | foundation core 6 + log extension 13. propagation matrix (http 5 / async 5 / message 4 / none 14). cardinality_safe_for_metric flag로 metric tag 적격 9 / 부적격 10 분리. |
| `metrics.yaml` | 25 | Micrometer dot.case + unit suffix. cardinality bounds 강제. P1/P2/P3 정량 threshold. high-cardinality forbidden tag 0건. |
| `capabilities.yaml` | 7 | enforcement ArchUnit SSOT (AOP forbidden). BULK_WRITE threshold N>100. EXTERNAL_OUTBOUND_ALLOWED의 outbox in-process 제외 명시. |
품질 정합:
- 모든 row에 source branch + line 인용 yaml comment.
- 추측 금지 원칙 준수 (source에 명시되지 않은 row 0개).
- Cross-registry 정합 (header ↔ MDC ↔ envelope, env ↔ secrets, capability ↔ repository-access ↔ TransactionPort).
- Runbook coverage 정책 100% (error-codes.yaml row 검증).
- 명명 규칙 일관: error UPPER_SNAKE / env `APP_` prefix / MDC snake / header kebab / W3C lowercase / metric dot.case.
#### Phase C1 closures (this update)
- §21 Contract Registry 본문 6개 inline 표를 yaml SSOT reference + 요약 7개 subsection으로 재구성. inline 표의 stale category 명명(`AUTHENTICATION`/`AUTHORIZATION`/`PERSISTENCE`/`DEPENDENCY`/`MESSAGE`/`CACHE`/`NOTIFICATION`)을 foundation enum(`AUTH`/`AUTHZ`/`DATA_INTEGRITY`/`TRANSIENT_DEPENDENCY`/`PERMANENT_DEPENDENCY`)으로 정합. 더 이상 inline ≠ yaml 충돌 없음.
- §28에 Phase A/B/C1 closures 추가. Phase D1/D2/C2는 pending으로 명시.
- Header note(`> 이 문서는...`)에 phase 진척 한 줄 추가.
- frontmatter `last_reviewed: 2026-05-22`로 갱신.
#### Phase D1 closures (2026-05-22)
Test contract 정밀화 + cross-cutting 결정 + needs-confirmation 해소.
**VAGUE → IMPLEMENTABLE (38건)**:
| owner branch | item 수 | 정밀화 패턴 |
| --- | --- | --- |
| feature-implementation-readiness-scorecard | 6 | 각 readiness 기준을 yaml registry row count + CI step grep + manual evidence column으로 측정 가능하게 (scorecard는 onboarding dry-run consume only — SSOT는 `feature-domain-feature-onboarding-contract`) |
| feature-operational-runbook-contract | 4 | alert payload field 명시 + runbook link target file 존재 verify + placeholder regex 검출 |
| feature-test-taxonomy-fixture-contract | 3 | PR diff regex로 contract/architecture test 동반 변경 강제 + sample fixture prod profile 누출 검출 |
| feature-contract-verification-test-suite | 2 | ArchUnit으로 contract test의 도메인 import 금지 + 9 base contract test enumeration |
| feature-developer-experience-contract | 2 | fresh-clone-smoke CI job + verifyReadmeCommands Gradle task |
| feature-container-runtime-contract | 3 | server.shutdown=graceful property verify + temp cleanup 3 trigger + JVM OOM exit code 검출 |
| feature-file-resource-handling-contract | 2 | TempFileCleanupContractTest 3 trigger + antivirus position branch note grep |
| feature-env-driven-runtime-configuration | 1 | @FeatureFlag/APP_FEATURE_* row 존재 verify |
| feature-secrets-config-source-contract | 1 | @RefreshScope bean 금지 verify |
| feature-ci-quality-gates-contract | 3 | workflow yaml의 needs/if gate + openapi-diff exit code + sample-removal-smoke job verify |
| feature-background-job-async-contract | 2 | ApplicationListener<ContextClosedEvent> bean verify + ShedLock LockProvider 등록 verify |
| feature-cache-consistency-contract | 2 | @Cacheable sync=true ArchUnit + RedissonClient bean verify when multi-instance |
| feature-domain-modeling-guardrails | 2 | @ValueObject 생성자 protection + @AggregateRoot setter visibility ArchUnit |
| feature-domain-event-outbox-contract | 1 | OutboxPublisherLeaderElectionContractTest 2-context dedup verify |
| feature-security-operational-baseline | 1 | SecurityFilterChain.getFilters() snapshot diff |
| feature-management-actuator-security-contract | 1 | branch ownership boundary ArchUnit |
**PARTIAL 공통 패턴 결정 (3종)**:
| 패턴 | owner branch | 결정 |
| --- | --- | --- |
| capability marker | feature-repository-access-permission-contract | Java annotation `@UseCaseRepositoryAccess(value=Capability[])`, retention RUNTIME, target METHOD. `Capability` enum은 capabilities.yaml SSOT와 1:1. consumer는 annotation consume only. |
| disabled adapter detection | feature-integration-adapter-templates | 3-layer: (1) startup Spring `@ConditionalOnProperty`, (2) build-time ArchUnit `noClasses().that().resideInAPackage("..application..").should().dependOnClassesThat().resideInAPackage("..adapters.{disabled}..")`, (3) runtime fail-fast `AdapterDisabledException`. adapter 추가 시 env-keys.yaml에 row 필수. |
| claim parsing (multi-instance) | feature-env-driven-runtime-configuration | env `APP_MULTI_INSTANCE_ENABLED` boolean. true 시 ShedLock + Redisson + outbox SKIP LOCKED + distributed rate limiter + platform migration job 5종 contract test 모두 활성 강제. 6개 branch가 consume. |
**needs-confirmation closures (2건)**:
| 항목 | 결정 |
| --- | --- |
| PII/token/body log forbidden 구현 메커니즘 | structured field whitelist + Logback masking 이중 layer. (1) Logback `%mask` converter (regex `(?i)(token|password|authorization|cookie|secret|key)\s*[=:]\s*[^*\s]+``****`), (2) Jackson `@JsonSerialize(MaskingSerializer)` on PII DTO fields, (3) request body capture default `false` + allowlist required. JUnit + Logback ListAppender capture로 verify. owner = `feature-contract-verification-test-suite`. |
| scorecard real-domain dry-run checklist SSOT | SSOT = `feature-domain-feature-onboarding-contract`의 New Domain Module Slice + Read/Write Difference Table. scorecard는 consume only. |
#### Phase D2 closures (2026-05-22)
Release-blocking 5 category에 대한 runbook stub 5종 작성.
| 파일 | category | error_codes 적용 | severity |
| --- | --- | --- | --- |
| `ca-tmpl/docs/runbooks/auth-token-rotation-failure.md` | AUTH | AUTH_TOKEN_EXPIRED, AUTH_KID_UNKNOWN, AUTH_JWKS_UNAVAILABLE, AUTH_TOKEN_INVALID_SIGNATURE | P1 |
| `ca-tmpl/docs/runbooks/authz-cross-tenant-violation.md` | AUTHZ | AUTHZ_INSUFFICIENT_PERMISSION, AUTHZ_TENANT_MISMATCH | P2/P1 |
| `ca-tmpl/docs/runbooks/rate-limit-exceeded.md` | RATE_LIMIT | RATE_LIMIT_EXCEEDED, IDEMPOTENT_IN_FLIGHT | P3/P2 |
| `ca-tmpl/docs/runbooks/internal-error-spike.md` | INTERNAL | INTERNAL_ERROR, INTERNAL_AUTH_MISCONFIGURATION, JVM_OOM | P1 |
| `ca-tmpl/docs/runbooks/dependency-unavailable.md` | TRANSIENT_DEPENDENCY + PERMANENT_DEPENDENCY | DEPENDENCY_TIMEOUT/CONNECT_FAILED/DNS_FAILED/CIRCUIT_OPEN/5XX_SERVER, CACHE_UNAVAILABLE, DB_UNAVAILABLE | P1/P2 |
모든 runbook stub은 다음 7 section 표준 구조: Trigger / First Response (5분 이내) / Diagnosis / Mitigation / Escalation / Recovery·Verification / Related. frontmatter에 `status: stub` 명시 — 도메인 도입 시 실제 운영 사례·임계·dashboard URL로 보강 필요.
`error-codes.yaml`의 모든 release-blocking row의 `runbook_link` field가 위 5 파일 중 1개로 resolve됨을 verify (Phase D2 contract test). 미resolve 시 fail.
#### Pending (Phase D 이후)
| pending item | reason | owner |
| --- | --- | --- |
| canonical 승급 (`wiki/projects/` 본격 진입) | Phase D 완료 후 본 문서를 `wiki/projects/ca-skeleton-operational-contract.md`로 이동 + `status: verified`. registries/runbooks는 ca-tmpl repo로 이전됐으므로 wiki/projects/ca-tmpl/ subdirectory 불요, flat 위치로 승급. | Phase D 완료 시점 |
| ca-tmpl 실 코드 (Phase C2) | 별도 git repo. registry yaml을 consume하는 generated constants build task 포함 | external |
| 외부 근거 wiki/concepts/ 합성 (Phase E) | §29의 6개 topic을 각각 `wiki/concepts/{topic}.md` canonical 문서로 합성 (concept-template 형식, status `draft``reviewed`) | Phase E 별도 |
---
## 29. 외부 근거 / 대안 조사 인덱스 (2026-05-22)
본 contract의 6개 핵심 결정에 대해 외부 source(공식 문서·RFC·대기업 기술블로그·GitHub repo)를 조사하여 `raw/official-docs/``raw/company-tech-blogs/`에 raw **54개 파일**로 저장. 각 raw 파일은 owning branch-note와 양방향 wikilink로 연결됨. 비교 분석은 추후 `wiki/concepts/` 합성 단계(Phase E)에서 6개 concept 문서로 정리.
### Topic 1 — Architecture Layout
- **ca-tmpl 결정**: Gradle multi-module Clean Architecture / Hexagonal boundary (`domain-core`, `application-core`, `adapter-*`, `shared-contract`, `app-bootstrap`, `sample-portfolio`)
- **대안 조사**: feature-first package / layer-first / hexagonal pure / Spring Modulith / onion
- **Owning branch-notes**:
- [[raw/branch-notes/feature-architecture-enforcement-rules]] — 5종 대안 비교 + 채택 근거
- [[raw/branch-notes/feature-skeleton-package-blueprint-contract]] — 패키지 청사진 관점
- [[raw/branch-notes/feature-domain-feature-onboarding-contract]] — onboarding 관점
- **raw 12개**: branch-note의 "외부 근거" 섹션에 full list
- **비교 핵심**: ca-tmpl Phase C2는 module boundary로 application/domain과 adapter를 물리 분리한다. buckpal은 feature/package 내부 port-adapter 책임 분리 참고로 유지하고, Spring Modulith는 기본값이 아니라 향후 module verification 보강 대안으로 둔다. layer-first는 초기 학습 비용 최저지만 도메인 증가 시 응집도 폭락.
### Topic 2 — Transaction Boundary
- **ca-tmpl 결정**: TransactionPort + TransactionalUseCaseRunner abstraction (`@Transactional` 직접 import forbidden)
- **대안 조사**: TransactionPort (baseline, ca-tmpl) / `@Transactional` direct / TransactionTemplate programmatic / Functional Resource monad / Custom TransactionInterceptor AOP
- **Owning branch-notes**:
- [[raw/branch-notes/feature-application-port-usecase-contract]] — TransactionPort SSOT
- [[raw/branch-notes/feature-transaction-concurrency-contract]] — isolation/propagation 관점
- **raw 8개**: branch-note 외부 근거 섹션 참조
- **비교 핵심**: ca-tmpl은 "Spring 의존 숨김" 진영(소수파). 다수파는 `@Transactional` 직접 부착(testability 낮지만 boilerplate 최저, Reflectoring 표준 baseline). Functional monad는 testability 최고지만 팀 학습 비용 큼. UNIL 팀이 2024-05에 ca-tmpl과 동일 진화 경로(`@Transactional` → output port + TransactionTemplate)를 거친 사례 존재.
### Topic 3 — Outbox Pattern
- **ca-tmpl 결정**: DB outbox table polling + `FOR UPDATE SKIP LOCKED` (PostgreSQL/MySQL 양쪽)
- **대안 조사**: SKIP LOCKED polling (baseline) / Debezium CDC / Kafka Connect outbox SMT / Dual-write (금지, negative reference) / Event sourcing / Spring `@TransactionalEventListener` (in-process only) / Netflix DBLog (극단 자체 CDC)
- **Owning branch-notes**:
- [[raw/branch-notes/feature-domain-event-outbox-contract]] — outbox publisher SSOT
- [[raw/branch-notes/feature-background-job-async-contract]] — retry/DLQ vocabulary SSOT
- **raw 10개**: branch-note 외부 근거 섹션 참조
- **비교 핵심**: polling lag vs CDC 인프라 비용이 결정 축. ca-tmpl 가정 = lag 수 초 허용 + Kafka Connect 운영 인력 부재 + DB가 SSOT. 가정 깨지면 Debezium migration (Wix 사례). event sourcing은 "대안"이라기보다 도메인 모델 자체 교체. dual-write는 negative reference (outbox 도입 근거).
### Topic 4 — API Error Envelope
- **ca-tmpl 결정**: custom envelope (`{success, data, error.{code, category, message, retryable, details}, meta}`), ProblemDetail(RFC 7807) 명시적 forbidden
- **대안 조사**: Custom envelope (Stripe/GitHub/Toss 진영) / RFC 7807 ProblemDetail / Google `rpc.Status` (gRPC-derived) / JSON:API errors / GraphQL errors array
- **Owning branch-notes**:
- [[raw/branch-notes/feature-operational-error-observability-foundation]] — envelope schema SSOT
- [[raw/branch-notes/feature-boundary-validation-mapping-contract]] — validation error mapping
- [[raw/branch-notes/feature-business-rule-validation-contract]] — business invariant → category mapping
- **raw 8개**: branch-note 외부 근거 섹션 참조
- **비교 핵심**: ca-tmpl의 `success` flag + `retryable` 1급은 어떤 표준에도 없음. Google `rpc.Status`만 retryable을 detail로 가짐. ProblemDetail은 실패 전용 평면이라 ca-tmpl의 success/error 대칭 요구와 구조적 충돌. → ca-tmpl이 ProblemDetail을 거부한 trade-off: 표준 lock-in 회피 + success/error 대칭 + 운영 메타 1급화.
### Topic 5 — Idempotency Key Design
- **ca-tmpl 결정**: triple scope `(authenticatedPrincipal, idempotencyKey, useCaseName)` + DB table + 24h TTL + 200ms in-flight wait → 409 `IDEMPOTENT_IN_FLIGHT` + fingerprint mismatch → 422 `IDEMPOTENT_REQUEST_MISMATCH`
- **대안 조사**: Stripe v1 pair `(account, key)` / Stripe v2 triple `(account, API, key)` / Square endpoint-scoped / PayPal `(req-id, API call type)` 45일 TTL / 토스 4-tuple `(account, key, URL, method)` 15일 TTL / AWS Powertools content-hash / GitHub no-API-level dedup / Brandur Postgres locked_at lock
- **Owning branch-notes**:
- [[raw/branch-notes/feature-rate-limit-idempotency-contract]] — key shape/TTL/저장소 SSOT
- [[raw/branch-notes/feature-api-contract-baseline]] — `Idempotency-Key` header 표준 (consume only)
- **raw 9개**: branch-note 외부 근거 섹션 참조
- **비교 핵심**: ca-tmpl triple은 Stripe v1보다 보수적, Stripe v2/Square/Toss와 동급. TTL 24h가 모든 reference 중 가장 짧음(스토리지 비용·키 추측 공격면 최소). 200ms wait는 in-flight retry 친화적(Brandur lock의 변형). fingerprint 422는 IETF draft-07 권고 정합. "Stripe pair보다 무조건 안전"이라는 단정은 금지 — v1 한정 비교일 뿐.
### Topic 6 — Multi-tenancy Isolation
- **ca-tmpl 결정**: opt-in (`APP_TENANT_ENABLED=true` 시만 활성) + shared DB + tenant_id column + ULID 형식 + JWT claim 우선 + `X-Tenant-Id` header admin only
- **대안 조사**: shared DB + tenant_id (baseline, AWS Pool model) / subdomain-based resolution / JWT claim only / schema-per-tenant (Hibernate SCHEMA strategy, Stripe Citus) / database-per-tenant (AWS Silo model) / Hybrid (Azure Deployment Stamps, tier-based)
- **Owning branch-notes**:
- [[raw/branch-notes/feature-tenant-context-policy]] — tenant resolution + isolation SSOT
- [[raw/branch-notes/feature-repository-access-permission-contract]] — `CROSS_TENANT_ADMIN` capability
- **raw 9개**: branch-note 외부 근거 섹션 참조
- **비교 핵심**: ca-tmpl의 opt-in + shared DB + tenant_id는 B2B 초기 단계 적합 (tenant 수 수십~수백). **migration trigger 3가지**: (a) 규제(금융/의료) isolation 강제 → schema-per-tenant, (b) tenant 수 수백~수천 + 단일 row 수 수억 → schema-per-tenant 또는 hybrid, (c) enterprise tier 등장 시 isolation 가격화 → db-per-tenant.
### Group G-A — 관측 (Observability) — 4 branches, 11 raw
- **Owning branch-notes**: [[raw/branch-notes/feature-log-management-contract]], [[raw/branch-notes/feature-metrics-alerting-contract]], [[raw/branch-notes/feature-distributed-tracing-contract]], [[raw/branch-notes/feature-operational-runbook-contract]]
- **대안 조사 (sub-topic별)**:
- **Log**: ca-tmpl(structured JSON + Logback masking + prod 10% sampling) vs ECS schema / OpenTelemetry log signal / Log4j2 / Loki·Datadog SaaS
- **Metric**: ca-tmpl(Micrometer dot.case + Prometheus + P1/P2/P3 + cardinality bounds) vs StatsD push / Datadog APM / OTel metrics / CloudWatch / SLO burn-rate
- **Tracing**: ca-tmpl(W3C traceparent + Micrometer Tracing + prod 1%) vs B3 Zipkin legacy / Datadog APM / AWS X-Ray / Tail-based sampling / Adaptive sampling
- **Runbook**: ca-tmpl(`runbook://` scheme + repo path + link-check smoke) vs Confluence runbook / PagerDuty Runbook Automation / Auto-remediation
- **비교 핵심**: 자체 JSON schema + Logback masking은 minimal core + JVM stdout 친화. OTel log signal은 trace correlation 강점이나 2024 ecosystem maturity 낮음. SLO burn-rate alert는 traffic 무관 일관 severity이지만 정식 SLO 수립 후 단계. W3C tracecontext + Micrometer Tracing은 vendor-neutral, B3은 64-bit non-호환으로 forbidden. `runbook://` git markdown은 drift 방지 + PR review로 SaaS runbook 대비 강점.
### Group G-B — 보안 baseline — 3 branches, 12 raw
- **Owning branch-notes**: [[raw/branch-notes/feature-security-operational-baseline]], [[raw/branch-notes/feature-management-actuator-security-contract]], [[raw/branch-notes/feature-secrets-config-source-contract]]
- **대안 조사**:
- **Security baseline**: ca-tmpl(JWT Resource Server + AuthN/AuthZ matrix 12행 + JWKS 10min + clock skew 60s) vs Session+cookie / OAuth2 Authorization Code+PKCE / mTLS / API key+HMAC (AWS SigV4) / OPA policy engine
- **Actuator**: ca-tmpl(management port 9001 + prod allowlist) vs Single port + path ACL / mTLS / Network ACL only / Service mesh (Istio)
- **Secrets**: ca-tmpl(prod=secret manager OR mounted env + restart-only rotation + HMAC salt 90d) vs AWS Secrets Manager auto-rotation / HashiCorp Vault dynamic secrets / K8s Secret + external-secrets-operator / Doppler·1Password SDK / Plain env (rejected)
- **비교 핵심**: JWT Resource Server는 stateless 확장성 우위 vs session, revocation은 JWKS rotation으로 일부 회수. mTLS는 sender-constrained라 강하지만 PKI 운영 비용 큼. OPA는 외부 policy engine으로 정책-코드 분리 강점이나 AUTHZ 2종에는 in-process 충분. Vault dynamic은 short lease 보안 우위지만 ca-tmpl `@RefreshScope` 금지와 정면 충돌. AWS Secrets Manager auto-rotation이 dual-bind 60s 패턴과 정합.
### Group G-C — Data layer — 3 branches, 11 raw
- **Owning branch-notes**: [[raw/branch-notes/feature-persistence-failure-baseline]], [[raw/branch-notes/feature-cache-consistency-contract]], [[raw/branch-notes/feature-outbound-http-client-baseline]]
- **대안 조사**:
- **Persistence**: ca-tmpl(SQLState 9-row matrix + OSIV off + Hikari alert + read replica lag threshold) vs Spring Data JPA default less-granular / R2DBC reactive / JOOQ SQL-first / 직접 JDBC + classifier / CockroachDB·Spanner·Aurora-specific
- **Cache**: ca-tmpl(cache-aside + Caffeine local + Redisson RLock distributed + after-commit invalidation + 5s window) vs Write-through / Write-behind / Read-through / Hazelcast vs Redis / Stale-while-revalidate
- **Outbound HTTP**: ca-tmpl(Spring RestClient + Resilience4j + timeout 2s/5s/10s + retry default disabled + CB) vs RestTemplate legacy / WebClient reactive / Feign·OpenFeign / OkHttp+Retrofit / Hystrix (deprecated)
- **비교 핵심**: SQLState matrix는 Spring DataAccessException hierarchy 위에 SQLState 입힌 형태로 임의 분류 아님. OSIV는 Hibernate 권위자(Vlad Mihalcea)도 anti-pattern 명시. cache-aside는 application owns invalidation으로 실패 가시성 강점. Resilience4j는 Spring 공식 maintenance 정책 정합(RestTemplate maintenance-only, Hystrix deprecated). WebClient는 다른 runtime model이라 MVC baseline에 강제 시 event-loop blocking risk. Stripe은 retry default-on이지만 idempotency-key 보장 전제.
### Group G-D — Runtime / Lifecycle — 3 branches, 12 raw
- **Owning branch-notes**: [[raw/branch-notes/feature-container-runtime-contract]], [[raw/branch-notes/feature-runtime-health-lifecycle-contract]], [[raw/branch-notes/feature-migration-startup-contract]]
- **대안 조사**:
- **Container**: ca-tmpl(Temurin JRE slim + MaxRAMPercentage=75 + UTC/UTF-8 + graceful shutdown 20s+5s+35s) vs Distroless (Google) / Alpine + GraalVM / GraalVM native-image / Spring Boot Native / Multi-stage debug variant
- **Runtime health**: ca-tmpl(liveness/readiness/startup 분리 + Required Optional Dependency Matrix + UTC + NTP drift >5s) vs Single /health legacy / Custom HealthIndicator / Spring Actuator Groups / Service mesh health (Istio·Consul)
- **Migration**: ca-tmpl(Flyway + readiness gated + exit codes 78/70/71/72 + prod repair forbidden) vs Liquibase XML/YAML / Hibernate hbm2ddl (anti-pattern) / Init container in K8s / Separate migration job / Atlas·Tern (schema-as-code)
- **비교 핵심**: Temurin JRE slim은 운영 친숙도/디버깅 우선. Distroless는 보안 surface 축소하지만 in-container 디버깅 손실. GraalVM native-image는 cold start/메모리 우위지만 reflection 비용 + peak throughput 손실 (우아한형제들도 hybrid 채택). K8s 공식 + Spring Actuator Groups가 ca-tmpl 3-endpoint 분리와 정합. Flyway 공식이 prod repair/baseline_on_migrate/out_of_order 위험성을 명시 → ca-tmpl forbidden의 직접 근거. multi-instance에서는 K8s Job 또는 migration lock이 init container보다 race 회피에 우월.
### Group G-E — DevOps / CI — 3 branches, 9 raw
- **Owning branch-notes**: [[raw/branch-notes/feature-ci-quality-gates-contract]], [[raw/branch-notes/feature-build-release-supply-chain-contract]], [[raw/branch-notes/feature-developer-experience-contract]]
- **대안 조사**:
- **CI**: ca-tmpl(Gate ownership matrix 20 rows + flaky quarantine 14d + OpenAPI snapshot diff + Trivy) vs Jenkins / GitLab CI vs GitHub Actions / CircleCI·Buildkite / Drone CI / Tekton (k8s-native)
- **Supply chain**: ca-tmpl(Cosign keyless + SLSA + Gradle dependency-locking + SemVer+sha + reproducibility) vs GPG signing legacy / Notary v1 / in-toto attestations / JFrog Artifactory provenance / Sigstore for non-container
- **DX**: ca-tmpl(`./gradlew bootstrap` + Temurin 21 LTS + Testcontainers integration + markdown-link-check) vs `make bootstrap` / `docker compose up` only / devcontainer (VSCode·Codespaces) / Nix flake / mise·asdf
- **비교 핵심**: GitHub Actions `needs:` + `if: success()`가 ca-tmpl contract gate 모델과 정확히 맞물림. Tekton/Jenkins는 k8s 인프라/plugin 의존도로 skeleton 단계에 과함. Cosign keyless signature 누락만 차단으로는 부족 — identity 매칭 정책(`--certificate-identity`)이 추가 필요(branch note 보강 후보). Flaky quarantine은 Spotify/Google/MS 인정 vs Fowler 반대 — ca-tmpl 14d sunset이 절충안. mise/asdf는 `.tool-versions` 표준, SDKMAN은 `.sdkmanrc` — branch note "또는" 표현은 drift 위험 내포.
### Group G-F — API evolution & schema — 2 branches, 8 raw
- **Owning branch-notes**: [[raw/branch-notes/feature-api-compatibility-deprecation-contract]], [[raw/branch-notes/feature-schema-serialization-contract]]
- **대안 조사**:
- **Compatibility**: ca-tmpl(90d public + 30d internal migration window + breaking change catalog 7행 + Sunset header) vs Stripe date-based versioning (no removal, freeze) / Twitter Tier-based (legacy/current/beta) / Microsoft REST API versioning policy / GitHub preview API headers / Spring HATEOAS (links over versions)
- **Schema**: ca-tmpl(ISO-8601 offset UTC + BigDecimal scale 2 HALF_UP + unknown field strict inbound·tolerant outbound + null/empty/missing 분리) vs Jackson default lenient / Avro·Protobuf strict typing / JSON Schema validation / Smithy (AWS API modeling) / OpenAPI 3.1 spec
- **비교 핵심**: Stripe(freeze forever) vs ca-tmpl(90d/30d window) vs GitHub(24mo EOL + 410 Gone): 외부 컨슈머 규모와 운영 비용 trade-off. ca-tmpl internal-first면 90d 합리, EOL 응답 코드(410 Gone)가 catalog에 누락. Google AIP-180은 enum value 제거도 금지 → ca-tmpl `narrow enum = breaking, new version` 결정과 부분 정합. Sunset(RFC 8594) + Deprecation 헤더는 **함께** 보내야 정합. Protobuf `reserved`(field number/name 재사용 차단)가 JSON 환경에서 ca-tmpl이 가장 크게 보강할 부분. Avro full-compatibility는 schema registry 자동 검사 강력하지만 outbox/event 한정 도입 권장.
### Group G-G — Skeleton governance — 4 branches, 11 raw
- **Owning branch-notes**: [[raw/branch-notes/feature-contract-registry-governance]], [[raw/branch-notes/feature-contract-verification-test-suite]], [[raw/branch-notes/feature-test-taxonomy-fixture-contract]], [[raw/branch-notes/feature-implementation-readiness-scorecard]]
- **대안 조사**:
- **Registry governance**: ca-tmpl(markdown SSOT + YAML/generated constants + 7-column schema) vs Code-only enums / Protobuf·Smithy as registry / ArchUnit annotations as registry / Database-stored registry / `@ConfigurationProperties` as registry
- **Verification**: ca-tmpl(11 release-blocking gates + JSON snapshot + Pact CDC out-of-scope) vs Pact CDC / Spring REST Docs / Spring Cloud Contract / Hoverfly·WireMock service virtualization / PostgreSQL diff
- **Test taxonomy**: ca-tmpl(6 levels + Testcontainers from integration + src/testFixtures + 5min budget) vs Classic test pyramid / Test trophy (Kent Dodds) / Honeycomb (Spotify) / Fitness functions
- **Scorecard**: ca-tmpl(binary pass/fail + 15 area + 1:1 branch evidence) vs OpenTelemetry Maturity Model / AWS Well-Architected Framework / CIS Benchmark scoring / SLSA build level scoring / CMMI maturity
- **비교 핵심**: ca-tmpl branch note 결정 라인이 사실상 mini-ADR로 동작 (Status=`status_label`, Context=목표/WHY, Decision=결정 사항, Consequences=테스트 계약) — 별도 ADR 파일 도입 불요. Pact 공식이 직접 "consumer-known subset만 검증"이라 명시 → ca-tmpl single-team 환경에서 snapshot 우위. Testcontainers 공식이 "real services, no H2" 입장 — ca-tmpl integration부터 강제 정합. binary pass/fail은 adoption gate에 적합, WAR/CIS 점진적 점수는 운영 중 지속 개선에 적합.
### Group G-H — Sample / adoption — 2 branches, 8 raw
- **Owning branch-notes**: [[raw/branch-notes/feature-sample-domain-contract-fixture]], [[raw/branch-notes/feature-sample-removal-adoption-contract]]
- **대안 조사**:
- **Sample fixture**: ca-tmpl(sample-portfolio 12 scenario matrix + 6-field minimum model + state machine + optimistic lock + idempotency key) vs Spring Petclinic / RealWorld (gothinkster) / Microservices sample (Spring guides) / Shopping cart (Stripe testmode) / No fixture
- **Removal/adoption**: ca-tmpl(2-step removal + dual-mode CI matrix + 7-step adoption checklist) vs Yeoman·archetype auto-remove / Cookiecutter / degit (Svelte) / Spring Initializr / GitHub Template Repository / Manual fork
- **비교 핵심**: Petclinic은 "demo지 best-practice 아님" 본인 선언, RealWorld는 spec 풍부하지만 minimum 아니고 contract scenario 부재. ca-tmpl 결정이 skeleton contract 검증 도구라는 목적에 가장 적합. Initializr/Cookiecutter는 generator 시점 sample-off라 ca-tmpl dual-mode CI matrix와 충돌. **GitHub Template Repository**가 CI/Actions까지 함께 복제되어 friction 최저 — reference 1순위. Backstage는 조직 규모 임계점 이후 IDP 후보.
### Group G-I — Config / adapter — 2 branches, 7 raw
- **Owning branch-notes**: [[raw/branch-notes/feature-env-driven-runtime-configuration]], [[raw/branch-notes/feature-integration-adapter-templates]]
- **대안 조사**:
- **Env config**: ca-tmpl(APP_ prefix + Duration `30s` 1택 + boolean true/false + no-runtime-reload + .env.example drift verify + APP_MULTI_INSTANCE_ENABLED claim parsing) vs Spring Cloud Config Server / k8s ConfigMap + Spring Cloud Kubernetes auto-reload / HashiCorp Consul KV / AWS Parameter Store·AppConfig / LaunchDarkly·Unleash
- **Adapter templates**: ca-tmpl(optional module + `@ConditionalOnProperty` + ArchUnit 3-layer detection + `AdapterDisabledException`) vs Spring Boot AutoConfiguration without ConditionalOnProperty / Plugin architecture (OSGi) / Spring `@Profile` based / SPI ServiceLoader / Feature flag library (FF4J·Togglz)
- **비교 핵심**: 12-factor §III. Config가 ca-tmpl `APP_` env-only + no-reload 결정의 이론 출처. Spring Cloud Config Server는 인프라 SPOF + bootstrap 의존, k8s reload는 partial-state 디버깅 어려움. LaunchDarkly/Unleash는 product-grade A/B/canary 요구 발생 시 진입점. ca-tmpl `@ConditionalOnProperty`는 Layer 1만 Spring 공식 cover, Layer 2(ArchUnit)/Layer 3(`AdapterDisabledException`)는 branch 자체 contract — ArchUnit source 별도 필요한 미흡 영역. SPI는 on/off 표현 불가 + DI 미통합 + default constructor 강제. Togglz/FF4J는 runtime branching 도구라 시맨틱 다름.
### Group G-J — Privacy / file / domain modeling — 3 branches, 10 raw
- **Owning branch-notes**: [[raw/branch-notes/feature-data-retention-privacy-contract]], [[raw/branch-notes/feature-file-resource-handling-contract]], [[raw/branch-notes/feature-domain-modeling-guardrails]]
- **대안 조사**:
- **Privacy**: ca-tmpl(30/180/365d retention + HMAC-SHA-256 salt rotation 90d + DSR SLA 30d/14d + is_sample column) vs GDPR-compliant privacy-by-design libraries / AWS Macie PII detection / OneTrust·TrustArc SaaS / Cryptographic erasure (delete key vs delete data) / Tokenization vs pseudonymization
- **File**: ca-tmpl(10MB/12MB/20MB 3-layer + content-type allowlist 6종 + temp orphan 1h cleanup + antivirus gateway default) vs Direct S3 presigned URL / tus protocol (resumable) / multipart/form-data only / ClamAV in-app vs gateway / AWS GuardDuty Malware·GCP SCC
- **Domain modeling**: ca-tmpl(VO private constructor + aggregate root mutator protection + domain logger ban + safe reason enum + invariant in constructor + ORM 외부 매핑) vs Functional domain (Scala·F#) / Anemic vs Rich model / Pure DDD aggregates / Event sourcing / CQRS
- **비교 핵심**: GDPR Art.25 (Privacy by design) + NIST SP 800-88 (Cryptographic Erase)가 ca-tmpl retention/backup 결정의 표준 근거. HMAC + 90d salt rotation은 ENISA가 인정하나 brute-force 가능 input space(예: 한국 휴대폰 11자리)에서는 tokenization 우위. backup의 GDPR Art.17 erasure는 cryptographic erase가 NIST 정식 인정 → per-principal envelope key 구조 필요(ca-tmpl 미결정). ICAP/RFC 3507이 antivirus gateway 표준이지만 HTTPS E2E TLS 환경에서 적용 어려움. tus 채택 시 ca-tmpl "1h orphan cleanup"이 session 잘못 삭제 가능. Vaughn Vernon "Effective Aggregate Design" + Fowler "Anemic Domain Model"이 ca-tmpl 결정의 reference standard. ca-tmpl의 "ORM 외부 매핑"은 Vernon Option A, 우아한형제들 초기 글은 Option B(JPA direct annotation + protected ctor)지만 ca-tmpl forbidden import 규칙 위배라 거부. Greg Young 글에서 ca-tmpl은 CQRS read 분리/event sourcing 모두 미채택, 단순 "domain event = transport-free fact" 정의만 차용.
---
### 다음 단계 (Phase E)
**모든 43 branch의 외부 근거 조사 완료** (총 16 topic, 153 raw 파일). 다음은 wiki/concepts/ 합성 단계:
위 16개 topic을 각각 `wiki/concepts/{topic}.md` canonical 문서로 합성 예정:
- (T1-T6 기존 6개) `clean-architecture-package-layout` / `transaction-boundary-abstraction` / `transactional-outbox-pattern` / `api-error-envelope-design` / `idempotency-key-design` / `multi-tenancy-isolation-patterns`
- (G-A) `observability-log-metric-trace-runbook`
- (G-B) `security-baseline-jwt-actuator-secrets`
- (G-C) `data-layer-persistence-cache-outbound`
- (G-D) `runtime-container-health-migration`
- (G-E) `devops-ci-supply-chain-dx`
- (G-F) `api-evolution-and-schema`
- (G-G) `skeleton-governance-registry-verification-test-scorecard`
- (G-H) `sample-fixture-and-adoption`
- (G-I) `config-and-adapter-templates`
- (G-J) `privacy-file-domain-modeling`
각 concept 문서는 `templates/concept-template.md` 형식 (Summary / Standard / 한계 / Project Application / Interview Questions / Do Not Overclaim / Sources). raw 153개를 Sources로 인용. status `draft`로 시작.
### 검증 권장 (needs-confirmation)
각 topic 에이전트가 부분 추출만 가능했던 source — 후속 보강 필요:
- `layer-first-baeldung-clean-architecture-spring-boot.md` (본문 직접 인용 미완)
- `hexagonal-cockburn-wikipedia-summary.md` (원문 SSL 만료, Wikipedia 대체)
- `outbox-woowahan-techblog-pattern.md` (인용 wording 보강)
- `outbox-netflix-domain-events-cdc.md` (DBLog 정확한 인용)
- `multitenancy-stripe-citus-schema-per-tenant.md` (Citus 한계치 수치)
### 후속 보강 결과 (2026-05-22 처리 완료)
8건의 후속 보강 후보 모두 처리 완료. 외부 source 추가 + branch-note 결정사항 추가 + concept/project 보강. 신규 raw 8 파일은 status `needs-confirmation` 또는 `raw`(high confidence)로 분류.
| 항목 | 처리 결과 | 신규 raw 파일 |
|------|----------|--------------|
| **G-E** Cosign identity 매칭 정책 | **결정 추가**: `cosign verify --certificate-identity=<expected> --certificate-oidc-issuer=<expected>` 필수. signature 존재만 검증하면 fail. (status: high) | [[raw/official-docs/cosign-keyless-identity-verification-policy]] |
| **G-E** SLSA v1.0 spec 필드명 | **결정 추가**: provenance 생성 시 SLSA 공식 필드명(`buildDefinition.{buildType, externalParameters, internalParameters, resolvedDependencies}` + `runDetails.{builder.id, metadata.invocationId, ...}`) 사용. 약식 명명 forbidden. (status: high) | [[raw/official-docs/slsa-v1-provenance-schema]] |
| **G-F** Sunset+Deprecation paired | **결정 추가**: API deprecation 응답은 `Sunset` + `Deprecation` 헤더 **함께** 전송. 단독 Sunset 금지. `Link: <url>; rel="sunset"` 권장. (status: high) | [[raw/official-docs/sunset-deprecation-headers-paired-usage]] |
| **G-F** Protobuf `reserved` JSON 흉내 | **결정 보류**: OpenAPI `x-removed-fields` extension OR markdown 자체 catalog. 코드 단계 도구 결정. (status: needs-confirmation) | [[raw/official-docs/protobuf-reserved-vs-json-openapi-extension]] |
| **G-G** ArchUnit annotation-as-registry | **결정 유지**: markdown SSOT 유지, ArchUnit annotation은 verifier 한정 (registry 아님). 근거: framework-neutral, git diff review, 외부 도구 호환. (status: needs-confirmation, 공식 권고 부재) | [[raw/official-docs/archunit-annotation-as-registry-evaluation]] |
| **G-I** ArchUnit Layer 2 정적 검사 범위 | **결정 명확화**: Layer 2는 "annotation 존재 + naming pattern" fitness function까지만 정적 보장. runtime active 검사는 Layer 3 (`AdapterDisabledException`)에 위임. (status: needs-confirmation) | [[raw/official-docs/archunit-conditional-on-property-3-layer-pattern]] |
| **G-J** per-principal envelope key | **결정 추가**: backup PII 포함 시 per-principal envelope key (또는 tenant-level CMK) 구조 적용. HMAC + salt rotation은 forward security only 명시. 구체 패턴은 Phase C2 보류. (status: needs-confirmation) | [[raw/official-docs/gdpr-cryptographic-erasure-envelope-key-pattern]] |
| **G-B** 한국 보안 기술블로그 | **부분 완료**: Actuator 영역 2건 (우아한형제들 + 토스페이먼츠) 확보. JWT/secret 직접 사례는 fetch 가능 source 부재 → follow-up 후보로 유지. (status: raw) | [[raw/company-tech-blogs/security-woowahan-actuator-safe-usage]], [[raw/company-tech-blogs/security-toss-actuator-healthcheck]] |
총 신규 raw 파일: 9개 (G-B만 2개). 누적 외부 근거 raw: 162개 (Topic 1-6: 54 + G-A~J: 99 + 후속 보강: 9).
### 잔여 follow-up
- **G-B**: 한국 기업 JWT/secret 직접 사례 (토스/카카오/네이버 등) — 검색 가능한 글 등장 시 추가.
- **G-F**: Protobuf `reserved` 흉내 도구 선택 (`x-removed-fields` extension vs markdown catalog) — Phase C2 코드 단계 결정.
- **G-I**: ArchUnit Layer 2 fitness function 도입 여부 — Phase C2 코드 단계 결정.
- **G-J**: envelope key 구체 패턴 (per-principal CMK / per-principal DEK+master CMK / tenant-level CMK) — Phase C2 KMS 선택 단계 결정.
---
<!-- section-id: architecture-components -->
## 30. 시스템 아키텍처
> 본 절은 `templates/project-template.md` §3 표준에 맞춰 작성. ca-skeleton 은 **운영 계약 문서** 가 본체이며 아키텍처는 모듈 의존성 + 외부 의존성 2관점으로 표현.
>
> 작성 도구: **draw.io** (`templates/diagram-standards.md` v2 minimalist 표준). Mermaid 는 시퀀스용 (§30.1~§30.3).
### 30-1. 모듈 의존성 (Gradle 멀티모듈 + Clean Architecture)
![[raw/diagrams/ca-skeleton/architecture-modules-2026-05-26.drawio]]
**다이어그램이 답하는 질문**: 5개 Gradle 모듈은 어느 방향으로만 서로 의존할 수 있고, 그 중심은 무엇인가?
**핵심 메시지**: `domain` (파란 박스) 이 CA 의 심장이며, 모든 의존 화살표가 결국 domain 으로 수렴한다. cmd 는 composition root 로서 모든 모듈을 조립하지만, 자기 자신은 어디에도 의존받지 않는다.
**허용 방향** (다이어그램 화살표 8개):
- `cmd → presentation / service / infra / domain` — composition root
- `presentation → service` (UseCase 호출) + `presentation → domain` (도메인 모델/예외 직접 사용)
- `service → domain` (UseCase 가 도메인 모델 조작)
- `infra → domain` (Repository Port 구현, 도메인 모델 매핑)
**HARD-STOP 금지 사항** (callout 참조):
- `domain` → 어떤 다른 모듈 또는 Spring/JPA/HTTP/cloud SDK (순수 Java 만)
- `service``infra` 또는 `presentation` (포트로만 통신)
- `presentation``infra` (Repository / JPA Entity 직접 사용 금지)
- `infra``presentation` 또는 controller DTO (요청 객체 누출 금지)
**검증 수단**:
- `./gradlew verifyCleanArchitectureDependencies` — Gradle project-dependency check
- `./gradlew :cmd:test` — ArchUnit `CleanArchitectureTest` 실행
### 30-2. 외부 의존성 & 신뢰 경계 (런타임 토폴로지)
![[raw/diagrams/ca-skeleton/architecture-runtime-topology-2026-05-26.drawio]]
**다이어그램이 답하는 질문**: 앱이 기동하기 위해 반드시 있어야 하는 것은 무엇이고, `APP_ADAPTER_*_ENABLED=false` 로 끌 수 있는 것은 무엇인가?
**핵심 메시지**: Application 은 **단 하나의 mandatory 외부 의존성 = PostgreSQL** 만 가진다. Redis / Kafka / Email / Slack / Google 은 모두 optional — adapter 를 끄거나 외부 장애 시에도 앱 자체는 살아 있어야 한다.
**의존성 분류** (다이어그램 색·선 ↔ 정책):
| 종류 | 시각화 | 예시 | 정책 |
|---|---|---|---|
| **Mandatory** | 파란 굵은 실선 | Client→app(HTTP), app→PostgreSQL(JDBC) | 없으면 기동 실패. `cmd` health check 에서 fail-fast |
| **Optional internal** | 주황 점선 | Redis cache, Kafka outbox publisher | `APP_ADAPTER_REDIS_ENABLED=false` 로 끌 수 있음. 끌 경우 fallback path 활성 |
| **External Internet** | 회색 점선 | Email API, Slack API, Google OIDC | 외부 장애에도 앱 자체는 살아 있어야 함 (degrade 또는 retry-able) |
**운영 정책 (§11 Adapter Failure Contract)**:
- Mandatory adapter (PostgreSQL) 장애 → app 자체가 `/readyz` 실패, traffic 차단
- Optional internal adapter (Redis/Kafka) 장애 → 해당 기능만 degrade, app 자체는 healthy
- External (Email/Slack/Google) 장애 → 호출 단위로 retry / circuit-breaker, app 자체는 healthy
**금지**: optional adapter 가 mandatory 처럼 동작하도록 hard-coded (e.g., service 가 `RedisCachePort` 를 null 검사 없이 의존) → §11 위반.
<!-- section-id: sequence -->
### 30.1 핵심 시퀀스 (Mermaid)
> §3 ~ §7 (응답 envelope, validation, exception, error category) 의 데이터 흐름을 시퀀스로 표현. happy path + error path.
```mermaid
sequenceDiagram
autonumber
actor Client
participant FE as Presentation (Controller)
participant App as Application (UseCase)
participant Dom as Domain
participant Infra as Infrastructure (Adapter)
participant DB as DB
Client->>FE: HTTP request
FE->>FE: Request DTO validation (§4 syntax layer)
FE->>App: command/query (record)
App->>Dom: domain invariant check (§4 invariant layer)
App->>Infra: outbound port call (via TransactionPort)
Infra->>DB: persistence
alt success
DB-->>Infra: result
Infra-->>App: domain object
App-->>FE: response value
FE-->>Client: 200 OK {success: true, data: ..., meta: {request_id, trace_id}}
else domain invariant 위반
Dom-->>App: DomainInvariantException
App-->>FE: bubble up
FE-->>Client: 422 Unprocessable Entity {success: false, error: {category: VALIDATION, code: ...}}
else infra/DB 장애
Infra-->>App: PersistenceException (translated by §6 mapper)
App-->>FE: bubble up
FE-->>Client: 503 Service Unavailable {success: false, error: {category: TRANSIENT_DEPENDENCY, code: ...}}
end
```
### 30.2 Transactional Outbox publish (§domain-event-outbox-contract)
> SKIP LOCKED polling 패턴. DB 트랜잭션 + outbox 적재 + 비동기 broker 발행이 분리되어 원자성 확보.
```mermaid
sequenceDiagram
autonumber
actor Client
participant App as service (UseCase)
participant Infra as infra (Adapter)
participant DB as PostgreSQL
participant Poller as OutboxPoller<br/>(scheduled, multi-instance)
participant Kafka as Kafka broker
Client->>App: business command
App->>Infra: save Aggregate + Outbox event<br/>(single TX)
Infra->>DB: BEGIN; INSERT aggregate; INSERT outbox; COMMIT
DB-->>Infra: OK
Infra-->>App: success
App-->>Client: 200 OK
Note over Poller: 1초 주기 + SKIP LOCKED
Poller->>DB: SELECT FROM outbox WHERE status='PENDING'<br/>FOR UPDATE SKIP LOCKED
DB-->>Poller: events to publish
loop 각 event
Poller->>Kafka: publish(event)
alt 성공
Kafka-->>Poller: ack
Poller->>DB: UPDATE outbox SET status='SENT'
else 실패
Kafka-->>Poller: error
Poller->>DB: UPDATE outbox SET attempt_count+=1
Note over Poller,DB: attempt_count >= max → status='DEAD'
end
end
```
### 30.3 Tenant Context Propagation (§tenant-context-policy)
> 멀티 테넌트 opt-in. JWT claim → ThreadLocal → 비동기 전파 → finally clear.
```mermaid
sequenceDiagram
autonumber
actor Client
participant Filter as TenantContextFilter<br/>(presentation)
participant TL as ThreadLocal<br/>(TenantContext)
participant App as service (UseCase)
participant TD as TaskDecorator
participant Async as Async Executor<br/>(ThreadPoolTaskExecutor)
participant DB as DB Repo
Client->>Filter: HTTP request<br/>+ Authorization: Bearer JWT
Filter->>Filter: JWT validate + extract tenant_id claim
alt JWT 유효 + tenant_id 존재
Filter->>TL: set(tenant_id)
Filter->>App: forward
App->>DB: query with WHERE tenant_id=current()
DB-->>App: tenant-scoped data
App-->>Filter: result
Note over App,Async: 비동기 작업 시
App->>TD: submit Runnable
TD->>TD: capture caller tenant_id
TD->>Async: wrap Runnable with try-finally
Async->>TL: set(tenant_id) on worker thread
Async->>App: business work
Async->>TL: clear() ← MANDATORY (finally)
Async-->>App: done
Filter->>TL: clear() ← finally (worker thread reuse 보호)
else JWT 무효
Filter-->>Client: 401 Unauthorized
else multi-tenant disabled + X-Tenant-Id header 유입
Filter-->>Client: 400 TENANT_NOT_SUPPORTED
end
```
**핵심 함정** (§tenant-context-policy 의 결정 사항):
- 비동기 작업 후 `ThreadLocal.clear()` 누락 시 스레드 풀 재사용으로 인한 **테넌트 정보 누수 (Tenant Leakage)** — finally 강제
(이 외 패턴 — JWKS refresh, idempotency replay, graceful shutdown — 은 각 feature-* sub-branch 의 시퀀스에서.)
## 31. 묶음
<!-- GENERATED: sources:start -->
- [[raw/company-tech-blogs/adapter-togglz-ff4j-feature-toggle-library]]
- [[raw/company-tech-blogs/api-versioning-github-rest-date-header]]
- [[raw/company-tech-blogs/api-versioning-stripe-date-based]]
- [[raw/company-tech-blogs/arhohuttunen-hexagonal-architecture-spring-boot]]
- [[raw/company-tech-blogs/aws-iam-arn-format]]
- [[raw/company-tech-blogs/axonframework-transactionmanager-spring-adapter]]
- [[raw/company-tech-blogs/brandur-stripe-idempotency-keys]]
- [[raw/company-tech-blogs/buckpal-archunit-lombok-allowlist-direct-transactional]]
- [[raw/company-tech-blogs/cache-woowahan-after-commit-invalidation]]
- [[raw/company-tech-blogs/ci-flaky-test-quarantine-spotify-google]]
- [[raw/company-tech-blogs/config-launchdarkly-feature-flag-best-practice]]
- [[raw/company-tech-blogs/container-woowahan-spring-native-tradeoffs]]
- [[raw/company-tech-blogs/cqrs-lite-clean-architecture-read-path-bypass-wakita]]
- [[raw/company-tech-blogs/curity-oauth2-scope-vs-permission-naming]]
- [[raw/company-tech-blogs/custom-transaction-interceptor-catnipcoder]]
- [[raw/company-tech-blogs/domain-event-sourcing-vs-cqrs-greg-young]]
- [[raw/company-tech-blogs/domain-woowahan-ddd-aggregate-techblog]]
- [[raw/company-tech-blogs/explicit-architecture-ddd-hexagonal-cqrs-hgraca]]
- [[raw/company-tech-blogs/feature-first-sahibinden-package-by-layer-vs-feature]]
- [[raw/company-tech-blogs/file-clamav-icap-gateway-scan]]
- [[raw/company-tech-blogs/github-api-error-format]]
- [[raw/company-tech-blogs/github-graphql-global-node-id]]
- [[raw/company-tech-blogs/hexagonal-reflectoring-transactional-placement]]
- [[raw/company-tech-blogs/hexagonal-woowahan-techblog-2023]]
- [[raw/company-tech-blogs/idempotency-brandur-stripe-postgres]]
- [[raw/company-tech-blogs/idempotency-redis-vs-db-storage]]
- [[raw/company-tech-blogs/idempotency-toss-payments-techblog]]
- [[raw/company-tech-blogs/jwks-workos-unknown-kid-refresh-rate-limit-pattern]]
- [[raw/company-tech-blogs/layer-first-kamilmazurek-github-template]]
- [[raw/company-tech-blogs/lock-subskribe-advisory-lock-distributed-consensus]]
- [[raw/company-tech-blogs/metric-toss-payments-alert-severity-techblog]]
- [[raw/company-tech-blogs/micrometer-context-propagation-line-be-hase]]
- [[raw/company-tech-blogs/modulith-arawn-github-modular-monoliths-spring]]
- [[raw/company-tech-blogs/modulith-kakaobank-techblog-2025]]
- [[raw/company-tech-blogs/multitenancy-atlassian-tenant-context]]
- [[raw/company-tech-blogs/multitenancy-auth0-tenant-resolution]]
- [[raw/company-tech-blogs/multitenancy-hybrid-pooled-siloed-mix]]
- [[raw/company-tech-blogs/multitenancy-stripe-citus-schema-per-tenant]]
- [[raw/company-tech-blogs/multitenancy-subdomain-resolution-patterns]]
- [[raw/company-tech-blogs/netflix-tudum-cqrs-separate-read-store-evolution]]
- [[raw/company-tech-blogs/onion-allegro-tech-blog-2023]]
- [[raw/company-tech-blogs/outbound-stripe-rate-limit-retry-engineering]]
- [[raw/company-tech-blogs/outbox-confluent-kafka-connect-smt]]
- [[raw/company-tech-blogs/outbox-netflix-domain-events-cdc]]
- [[raw/company-tech-blogs/outbox-wix-engineering-debezium]]
- [[raw/company-tech-blogs/outbox-woowahan-techblog-pattern]]
- [[raw/company-tech-blogs/percona-uuid-storage-mysql]]
- [[raw/company-tech-blogs/planetscale-nanoid-api]]
- [[raw/company-tech-blogs/privacy-pseudonymization-hmac-vs-tokenization-iapp]]
- [[raw/company-tech-blogs/read-only-tx-hibernate-optimization-vladmihalcea]]
- [[raw/company-tech-blogs/realtime-service-experience-woowahan-websocket]]
- [[raw/company-tech-blogs/retry-aws-exponential-backoff-and-jitter]]
- [[raw/company-tech-blogs/runbook-atlassian-gitops-runbook-as-code]]
- [[raw/company-tech-blogs/runbook-woowahan-incident-techblog]]
- [[raw/company-tech-blogs/runtime-health-datadog-engineering-graceful-shutdown]]
- [[raw/company-tech-blogs/scaffolding-backstage-golden-path-spotify]]
- [[raw/company-tech-blogs/scoped-value-structured-concurrency-softwaremill]]
- [[raw/company-tech-blogs/secrets-1password-developer-secret-references]]
- [[raw/company-tech-blogs/security-toss-actuator-healthcheck]]
- [[raw/company-tech-blogs/security-woowahan-actuator-safe-usage]]
- [[raw/company-tech-blogs/segment-ksuid]]
- [[raw/company-tech-blogs/snowflake-twitter-id]]
- [[raw/company-tech-blogs/spring-modulith-archunit-generated-exemption-and-violations-as-data]]
- [[raw/company-tech-blogs/sse-realtime-notification-woowahan]]
- [[raw/company-tech-blogs/stripe-error-format]]
- [[raw/company-tech-blogs/test-pyramid-vs-trophy-kent-dodds]]
- [[raw/company-tech-blogs/threadlocal-capture-restore-att-israel]]
- [[raw/company-tech-blogs/toss-payments-error-format]]
- [[raw/company-tech-blogs/tracing-datadog-apm-vs-opentelemetry]]
- [[raw/company-tech-blogs/transaction-port-clean-ddd-spring-medium]]
- [[raw/company-tech-blogs/transaction-port-vassilis-soum-github-readme]]
- [[raw/company-tech-blogs/woowahan-hexagonal-multimodule]]
- [[raw/official-docs/actuator-endpoint-exposure-spring-official]]
- [[raw/official-docs/actuator-istio-sidecar-management-alt]]
- [[raw/official-docs/actuator-management-port-spring-official]]
- [[raw/official-docs/adapter-java-spi-serviceloader]]
- [[raw/official-docs/adapter-spring-boot-autoconfig-custom-starter]]
- [[raw/official-docs/api-versioning-google-aip-180]]
- [[raw/official-docs/arch-acl-microsoft-pattern]]
- [[raw/official-docs/arch-clean-architecture-uncle-bob]]
- [[raw/official-docs/arch-hexagonal-cockburn]]
- [[raw/official-docs/archunit-annotation-as-registry-evaluation]]
- [[raw/official-docs/archunit-conditional-on-property-3-layer-pattern]]
- [[raw/official-docs/archunit-user-guide]]
- [[raw/official-docs/at-transactional-spring-official]]
- [[raw/official-docs/aws-builders-retry-jitter]]
- [[raw/official-docs/aws-iam-google-iam-permission-naming-convention]]
- [[raw/official-docs/baggage-otel-baggage-api-spec]]
- [[raw/official-docs/baggage-w3c-baggage-spec]]
- [[raw/official-docs/cache-aside-vs-write-through-aws]]
- [[raw/official-docs/cache-caffeine-asyncloadingcache-readme]]
- [[raw/official-docs/cache-redisson-rlock-vs-setnx]]
- [[raw/official-docs/calver-spec-calver-official]]
- [[raw/official-docs/checkstyle-google-style-reference]]
- [[raw/official-docs/ci-github-actions-vs-gitlab-comparison]]
- [[raw/official-docs/ci-openapi-snapshot-diff-tooling]]
- [[raw/official-docs/cloudevents-spec-required-attributes]]
- [[raw/official-docs/compat-rfc-8594-sunset-header]]
- [[raw/official-docs/config-12-factor-app-config]]
- [[raw/official-docs/config-aws-appconfig-feature-flag-deployment]]
- [[raw/official-docs/config-spring-boot-externalized-configuration]]
- [[raw/official-docs/config-spring-cloud-config-server-official]]
- [[raw/official-docs/config-spring-cloud-kubernetes-configmap-reload]]
- [[raw/official-docs/container-alpine-java-musl-tradeoffs]]
- [[raw/official-docs/container-distroless-google-github]]
- [[raw/official-docs/container-graalvm-native-image-spring-boot]]
- [[raw/official-docs/cosign-keyless-identity-verification-policy]]
- [[raw/official-docs/cqrs-fowler-bliki]]
- [[raw/official-docs/cqrs-pattern-azure-architecture-center]]
- [[raw/official-docs/crockford-base32-spec]]
- [[raw/official-docs/cuid2-spec]]
- [[raw/official-docs/dependabot-supported-ecosystems-official]]
- [[raw/official-docs/domain-event-fowler-eaa]]
- [[raw/official-docs/domain-fowler-anemic-vs-rich-model]]
- [[raw/official-docs/domain-vaughn-vernon-aggregate-root]]
- [[raw/official-docs/dual-write-antipattern-microservices-io]]
- [[raw/official-docs/dx-devcontainer-spring-boot]]
- [[raw/official-docs/dx-mise-asdf-tool-versioning]]
- [[raw/official-docs/dx-testcontainers-java-best-practices]]
- [[raw/official-docs/ecs-awslogs-stdout-cloudwatch-aws-official]]
- [[raw/official-docs/errorprone-gradle-plugin-readme]]
- [[raw/official-docs/event-sourcing-vs-outbox-microservices-io]]
- [[raw/official-docs/feature-first-uncle-bob-screaming-architecture-2011]]
- [[raw/official-docs/fetch-spec-cors]]
- [[raw/official-docs/file-s3-presigned-url-upload]]
- [[raw/official-docs/file-tus-resumable-upload-protocol]]
- [[raw/official-docs/find-sec-bugs-official]]
- [[raw/official-docs/functional-tx-arrow-kt-resource-docs]]
- [[raw/official-docs/gdpr-cryptographic-erasure-envelope-key-pattern]]
- [[raw/official-docs/github-webhook-signature]]
- [[raw/official-docs/google-aip-122-resource-names]]
- [[raw/official-docs/google-aip-127-http-transcoding]]
- [[raw/official-docs/google-aip-132-list-method]]
- [[raw/official-docs/google-aip-136-custom-methods]]
- [[raw/official-docs/google-aip-148-standard-fields]]
- [[raw/official-docs/google-aip-151-long-running-operations]]
- [[raw/official-docs/google-aip-158-pagination]]
- [[raw/official-docs/google-aip-160-filtering]]
- [[raw/official-docs/google-aip-185-resource-versioning]]
- [[raw/official-docs/google-aip-233-batch-create]]
- [[raw/official-docs/google-antigravity-hooks]]
- [[raw/official-docs/google-api-error-format]]
- [[raw/official-docs/google-java-format-readme]]
- [[raw/official-docs/governance-archunit-official]]
- [[raw/official-docs/gradle-java-library-api-vs-implementation]]
- [[raw/official-docs/graphql-errors-spec]]
- [[raw/official-docs/hexagonal-cockburn-wikipedia-summary]]
- [[raw/official-docs/hexagonal-thombergs-buckpal-github]]
- [[raw/official-docs/idempotency-aws-lambda-powertools]]
- [[raw/official-docs/idempotency-ietf-draft]]
- [[raw/official-docs/idempotency-no-api-level-github-rest]]
- [[raw/official-docs/idempotency-paypal-docs]]
- [[raw/official-docs/idempotency-square-api]]
- [[raw/official-docs/idempotency-stripe-api-ref]]
- [[raw/official-docs/jdk21-threadpoolexecutor-javadoc]]
- [[raw/official-docs/json-api-errors-spec]]
- [[raw/official-docs/jsonapi-pagination-format]]
- [[raw/official-docs/junit5-conditional-env-variable-user-guide]]
- [[raw/official-docs/jwks-keycloak-key-rotation-active-passive]]
- [[raw/official-docs/jwks-nimbus-jose-jwksourcebuilder-spring-integration]]
- [[raw/official-docs/k8s-application-security-checklist-readonly-fs]]
- [[raw/official-docs/k8s-configure-probes-task-page]]
- [[raw/official-docs/k8s-logging-architecture-kubernetes-official]]
- [[raw/official-docs/k8s-pod-lifecycle-probes-concept]]
- [[raw/official-docs/k8s-pod-security-standards-restricted]]
- [[raw/official-docs/keycloak-authorization-services-realm-client-roles]]
- [[raw/official-docs/kubernetes-exit-code-observability-termination]]
- [[raw/official-docs/kubernetes-pod-lifecycle-termination]]
- [[raw/official-docs/layer-first-baeldung-clean-architecture-spring-boot]]
- [[raw/official-docs/lock-postgres-advisory-locks]]
- [[raw/official-docs/lock-shedlock-issue-899-non-scheduler-use]]
- [[raw/official-docs/lock-shedlock-readme]]
- [[raw/official-docs/lock-spring-integration-lock-registry]]
- [[raw/official-docs/log-ecs-schema-elastic-official]]
- [[raw/official-docs/log-logback-mask-pattern-converter-official]]
- [[raw/official-docs/log-otel-log-data-model-spec]]
- [[raw/official-docs/lombok-builder-data-features-official]]
- [[raw/official-docs/mapstruct-generated-annotation-official]]
- [[raw/official-docs/metric-google-sre-slo-burn-rate]]
- [[raw/official-docs/metric-google-sre-workbook-on-call]]
- [[raw/official-docs/metric-micrometer-high-cardinality-tags-detector]]
- [[raw/official-docs/metric-micrometer-histogram-percentile-concepts]]
- [[raw/official-docs/metric-micrometer-naming-convention-official]]
- [[raw/official-docs/metric-otel-metrics-data-model-spec]]
- [[raw/official-docs/metric-prometheus-histograms-vs-summaries-practices]]
- [[raw/official-docs/metric-prometheus-label-cardinality-best-practices]]
- [[raw/official-docs/micrometer-context-propagation-official]]
- [[raw/official-docs/micrometer-context-propagation-purpose-thread-local-accessor]]
- [[raw/official-docs/microservices-io-transactional-outbox]]
- [[raw/official-docs/migration-atlas-schema-as-code]]
- [[raw/official-docs/migration-flyway-official-concepts-and-repair]]
- [[raw/official-docs/migration-k8s-init-container-job-pattern]]
- [[raw/official-docs/migration-liquibase-official-changelog-xml-yaml]]
- [[raw/official-docs/modulith-spring-official-doc]]
- [[raw/official-docs/multitenancy-aws-saas-tenant-isolation-whitepaper]]
- [[raw/official-docs/multitenancy-azure-architecture-patterns]]
- [[raw/official-docs/multitenancy-hibernate-user-guide]]
- [[raw/official-docs/multitenancy-microservices-io-pattern]]
- [[raw/official-docs/mysql-innodb-transaction-isolation-official]]
- [[raw/official-docs/nanoid-spec]]
- [[raw/official-docs/onion-palermo-original-2008]]
- [[raw/official-docs/openjdk-jdk-8196595-container-support]]
- [[raw/official-docs/opentelemetry-http-semconv-migration-guide]]
- [[raw/official-docs/opentelemetry-versioning-stability-spec]]
- [[raw/official-docs/otel-exceptions-semantic-conventions]]
- [[raw/official-docs/outbound-openfeign-declarative-client]]
- [[raw/official-docs/outbound-resilience4j-vs-spring-retry]]
- [[raw/official-docs/outbound-spring-restclient-baseline]]
- [[raw/official-docs/outbound-webclient-vs-restclient-spring]]
- [[raw/official-docs/outbox-debezium-official-docs]]
- [[raw/official-docs/outbox-skip-locked-microservices-io]]
- [[raw/official-docs/owasp-authz-permission-model-abac-rbac]]
- [[raw/official-docs/owasp-file-upload-cheat-sheet]]
- [[raw/official-docs/owasp-hsts-cheat-sheet]]
- [[raw/official-docs/owasp-logging-cheat-sheet]]
- [[raw/official-docs/owasp-path-traversal]]
- [[raw/official-docs/owasp-ssrf-prevention]]
- [[raw/official-docs/patch-json-merge-rfc7396]]
- [[raw/official-docs/persistence-hikaricp-pool-sizing-wiki]]
- [[raw/official-docs/persistence-osiv-antipattern-hibernate-vladmihalcea]]
- [[raw/official-docs/persistence-r2dbc-reactive-spring]]
- [[raw/official-docs/persistence-spring-dataaccessexception-hierarchy]]
- [[raw/official-docs/postgres-transaction-isolation-official]]
- [[raw/official-docs/privacy-cryptographic-erasure-nist-sp800-88]]
- [[raw/official-docs/privacy-gdpr-article-25-design]]
- [[raw/official-docs/problem-detail-rfc-7807]]
- [[raw/official-docs/protobuf-reserved-vs-json-openapi-extension]]
- [[raw/official-docs/redhat-openjdk-container-awareness-java17]]
- [[raw/official-docs/registry-adr-official]]
- [[raw/official-docs/reproducible-builds-org-jvm-guide]]
- [[raw/official-docs/retry-aws-well-architected-rel05-bp03]]
- [[raw/official-docs/retry-spring-retry-readme-backoff-defaults]]
- [[raw/official-docs/rfc3986-uri-generic-syntax]]
- [[raw/official-docs/rfc6455-websocket]]
- [[raw/official-docs/rfc9111-http-caching]]
- [[raw/official-docs/rfc9112-http-1-1-chunked-transfer]]
- [[raw/official-docs/rfc9421-http-message-signatures]]
- [[raw/official-docs/rfc9457-problem-details-http-apis]]
- [[raw/official-docs/rfc9562-uuid]]
- [[raw/official-docs/runbook-pagerduty-incident-response-doc]]
- [[raw/official-docs/runtime-health-istio-mesh-health-check]]
- [[raw/official-docs/runtime-health-k8s-probes-official]]
- [[raw/official-docs/runtime-health-spring-actuator-groups]]
- [[raw/official-docs/runtime-spring-boot-virtual-threads]]
- [[raw/official-docs/sample-microservices-spring-cloud-github]]
- [[raw/official-docs/sample-realworld-gothinkster-github]]
- [[raw/official-docs/sample-spring-petclinic-github]]
- [[raw/official-docs/scaffolding-cookiecutter-official]]
- [[raw/official-docs/scaffolding-degit-svelte-github]]
- [[raw/official-docs/scaffolding-github-template-repository]]
- [[raw/official-docs/scaffolding-spring-initializr]]
- [[raw/official-docs/schema-avro-evolution-rules]]
- [[raw/official-docs/schema-bigdecimal-money-serialization-java]]
- [[raw/official-docs/schema-jackson-polymorphic-deserialization]]
- [[raw/official-docs/schema-jackson-unknown-field-handling]]
- [[raw/official-docs/schema-protobuf-vs-json-evolution]]
- [[raw/official-docs/scoped-value-jep-446-506-openjdk]]
- [[raw/official-docs/scorecard-aws-well-architected]]
- [[raw/official-docs/scorecard-cis-benchmarks-slsa]]
- [[raw/official-docs/scorecard-opentelemetry-maturity]]
- [[raw/official-docs/secrets-aws-secrets-manager-rotation]]
- [[raw/official-docs/secrets-k8s-secret-external-secrets-operator]]
- [[raw/official-docs/secrets-vault-dynamic-secrets-hashicorp]]
- [[raw/official-docs/security-authorization-cheatsheet-owasp]]
- [[raw/official-docs/security-aws-sigv4-hmac-signing]]
- [[raw/official-docs/security-jwt-rfc-7519-validation]]
- [[raw/official-docs/security-mtls-rfc-8705]]
- [[raw/official-docs/security-oauth2-pkce-rfc-8252]]
- [[raw/official-docs/security-opa-policy-engine-official]]
- [[raw/official-docs/security-spring-jwt-timestamp-validator-clock-skew]]
- [[raw/official-docs/semver-2-0-0-spec-semver-official]]
- [[raw/official-docs/skip-locked-mysql-docs]]
- [[raw/official-docs/skip-locked-postgres-docs]]
- [[raw/official-docs/slsa-v1-provenance-schema]]
- [[raw/official-docs/sonarqube-server-versus-cloud]]
- [[raw/official-docs/spotbugs-gradle-plugin-docs]]
- [[raw/official-docs/spotless-gradle-plugin-readme]]
- [[raw/official-docs/spring-boot-exit-code-generator-startup-failure]]
- [[raw/official-docs/spring-boot-graceful-shutdown-reference]]
- [[raw/official-docs/spring-boot-structuring-your-code]]
- [[raw/official-docs/spring-boot-task-execution-scheduling-reference]]
- [[raw/official-docs/spring-boot-test-slices-webmvctest-datajpatest-official]]
- [[raw/official-docs/spring-data-jpa-auditing-official]]
- [[raw/official-docs/spring-data-jpa-projections-spring-official]]
- [[raw/official-docs/spring-data-jpa-transactionality-spring-official]]
- [[raw/official-docs/spring-data-pageable-defaults]]
- [[raw/official-docs/spring-executor-configuration-support-javadoc]]
- [[raw/official-docs/spring-framework-observability-context-propagating-task-decorator]]
- [[raw/official-docs/spring-framework-test-enabledif-jupiter-annotation]]
- [[raw/official-docs/spring-framework-threadpooltaskexecutor-javadoc]]
- [[raw/official-docs/spring-mvc-async-streaming]]
- [[raw/official-docs/spring-mvc-rest-exception-handling]]
- [[raw/official-docs/spring-problem-detail]]
- [[raw/official-docs/spring-security-authorization-architecture]]
- [[raw/official-docs/spring-security-concurrency-delegating-security-context-executor]]
- [[raw/official-docs/spring-transaction-synchronization-manager-javadoc]]
- [[raw/official-docs/spring-transactional-event-listener]]
- [[raw/official-docs/spring-tx-propagation-required-new-nested-official]]
- [[raw/official-docs/stripe-resource-id-convention]]
- [[raw/official-docs/stripe-webhook-signature]]
- [[raw/official-docs/sunset-deprecation-headers-paired-usage]]
- [[raw/official-docs/supply-chain-cosign-keyless-sigstore]]
- [[raw/official-docs/supply-chain-gradle-vs-maven-dependency-locking]]
- [[raw/official-docs/supply-chain-slsa-provenance-framework]]
- [[raw/official-docs/svix-webhook-best-practices]]
- [[raw/official-docs/sysexits-bsd-exit-code-convention]]
- [[raw/official-docs/test-taxonomy-practical-pyramid-fowler]]
- [[raw/official-docs/test-taxonomy-testcontainers-official]]
- [[raw/official-docs/threadlocal-virtual-threads-java21-oracle]]
- [[raw/official-docs/trace-context-w3c-recommendation]]
- [[raw/official-docs/tracing-b3-propagation-zipkin-spec]]
- [[raw/official-docs/tracing-micrometer-observation-introduction]]
- [[raw/official-docs/tracing-otel-sampling-tail-vs-head-spec]]
- [[raw/official-docs/tracing-otel-trace-api-spec]]
- [[raw/official-docs/tracing-spring-boot-3-actuator-tracing-reference]]
- [[raw/official-docs/tracing-w3c-trace-context-spec]]
- [[raw/official-docs/transaction-template-spring-official]]
- [[raw/official-docs/transactional-outbox-aws-prescriptive-guidance]]
- [[raw/official-docs/trivy-severity-exit-code-gating]]
- [[raw/official-docs/ulid-spec]]
- [[raw/official-docs/validation-jakarta-bean-validation-3.0-spec]]
- [[raw/official-docs/verification-approvaltests-snapshot-official]]
- [[raw/official-docs/verification-pact-cdc-official]]
- [[raw/official-docs/verification-spring-cloud-contract-official]]
- [[raw/official-docs/verification-spring-restdocs-official]]
- [[raw/official-docs/whatwg-html-server-sent-events]]
<!-- GENERATED: sources:end -->
<!-- GENERATED: interviews:start -->
- [[raw/interviews/archunit-manual-importer-vs-analyzeclasses]]
- [[raw/interviews/archunit-static-analysis-limits]]
- [[raw/interviews/async-executor-saturation-context-propagation-2026-06-13]]
- [[raw/interviews/ci-release-gate-fan-in-blocking-2026-06-20]]
- [[raw/interviews/clean-architecture-boundary-enforcement]]
- [[raw/interviews/clean-architecture-domain-onboarding-guardrails]]
- [[raw/interviews/clean-architecture-identifier-generation]]
- [[raw/interviews/clean-architecture-method-authorization-without-spring-coupling-2026-06-08]]
- [[raw/interviews/clean-architecture-module-blueprint]]
- [[raw/interviews/crown-one-query-vs-cqrs-lite-read-model]]
- [[raw/interviews/deterministic-logback-asyncappender-drop-metric-test-2026-06-14]]
- [[raw/interviews/digest-first-supply-chain-release-gates]]
- [[raw/interviews/domain-modeling-guardrails-archunit-2026-06-05]]
- [[raw/interviews/formatter-vs-style-linter-responsibility-split-2026-06-20]]
- [[raw/interviews/idempotency-rate-limit-design-tradeoffs-2026-06-09]]
- [[raw/interviews/jwt-resource-server-fine-grained-error-classification-2026-06-08]]
- [[raw/interviews/manifest-driven-multi-platform-agent-harness]]
- [[raw/interviews/native-query-addscalar-runtime-validation]]
- [[raw/interviews/operational-error-envelope-and-observability-foundation]]
- [[raw/interviews/optional-adapter-3-layer-disabled-detection-2026-06-09]]
- [[raw/interviews/post-implementation-knowledge-capture]]
- [[raw/interviews/sample-domain-contract-fixture-clean-architecture]]
- [[raw/interviews/shared-contract-and-sample-isolation]]
- [[raw/interviews/single-command-local-bootstrap]]
- [[raw/interviews/spring-jpa-flyway-initialization-lifecycle-circular-dependency]]
- [[raw/interviews/startup-fail-fast-config-validation-2026-06-06]]
- [[raw/interviews/transaction-port-vs-spring-transactional]]
- [[raw/interviews/transactional-outbox-skip-locked-implementation-2026-06-11]]
- [[raw/interviews/trivy-suppression-dual-control-governance-2026-06-20]]
<!-- GENERATED: interviews:end -->
<!-- GENERATED: blog-topics:start -->
- [[raw/blog-topics/api-deprecation-sunset-header-migration-window-2026-07-02]]
- [[raw/blog-topics/archunit-generic-return-type-purity-query-port-2026-06-05]]
- [[raw/blog-topics/archunit-jackson-default-typing-cve-2019-14379-block-2026-05-29]]
- [[raw/blog-topics/archunit-testcompileonly-fixture-annotation-pattern-2026-06-02]]
- [[raw/blog-topics/archunit-violations-as-data-pattern-2026-05-28]]
- [[raw/blog-topics/binary-readiness-scorecard-clean-architecture-skeleton-2026-07-02]]
- [[raw/blog-topics/boundary-validation-mapper-responsibility-map-2026-07-02]]
- [[raw/blog-topics/cache-backend-router-fail-open-decorator-2026-07-02]]
- [[raw/blog-topics/cache-consistency-after-commit-stampede-contract-2026-07-02]]
- [[raw/blog-topics/ci-gate-wiring-vs-policy-ownership-2026-06-20]]
- [[raw/blog-topics/clean-architecture-boundary-enforcement-2026-05-28]]
- [[raw/blog-topics/clean-architecture-module-blueprint-2026-05-28]]
- [[raw/blog-topics/clean-architecture-reference-project-adoption-2026-06-17]]
- [[raw/blog-topics/contract-registry-schema-owner-vs-row-owner-gate-2026-06-20]]
- [[raw/blog-topics/contract-verification-suite-release-gates-2026-07-02]]
- [[raw/blog-topics/digest-first-java-release-pipeline-2026-06-21]]
- [[raw/blog-topics/distributed-lock-transaction-commit-boundary-2026-07-02]]
- [[raw/blog-topics/domain-modeling-guardrails-as-archunit-fitness-functions-2026-06-05]]
- [[raw/blog-topics/env-example-drift-gate-gradle-2026-06-06]]
- [[raw/blog-topics/executable-clean-architecture-onboarding-2026-06-25]]
- [[raw/blog-topics/five-stage-local-bootstrap-contract-2026-06-24]]
- [[raw/blog-topics/framework-free-method-authorization-clean-architecture-2026-06-08]]
- [[raw/blog-topics/gitea-act-dependency-security-gate-portability-2026-07-02]]
- [[raw/blog-topics/gradle9-java21-static-analysis-baseline-2026-06-20]]
- [[raw/blog-topics/hikaricp-inter-knob-constraints-startup-guard-2026-06-09]]
- [[raw/blog-topics/idempotency-executor-application-layer-clean-architecture-2026-06-09]]
- [[raw/blog-topics/identifier-governance-rule-scoping-by-id-kind-2026-06-01]]
- [[raw/blog-topics/java21-context-propagation-strategy-virtual-threads-2026-07-02]]
- [[raw/blog-topics/jdk-httpclient-dns-connectexception-classification-2026-07-02]]
- [[raw/blog-topics/jvm-oom-vs-container-oomkill-exit-137-2026-07-02]]
- [[raw/blog-topics/logback-layer1-secret-masking-json-vs-pattern-2026-06-14]]
- [[raw/blog-topics/manifest-driven-agent-harness-policy-engine]]
- [[raw/blog-topics/micrometer-meterfilter-resilience4j-functioncounter-2026-07-02]]
- [[raw/blog-topics/nplus1-lab-checkoutable-api-replay-2026-07-15]]
- [[raw/blog-topics/operational-error-envelope-meta-category-migration-2026-06-01]]
- [[raw/blog-topics/persistence-audit-metadata-clean-architecture-2026-07-02]]
- [[raw/blog-topics/post-implementation-knowledge-capture-workflow-2026-05-28]]
- [[raw/blog-topics/repository-capability-archunit-fitness-function-2026-07-02]]
- [[raw/blog-topics/runbook-coverage-junit-contract-test-2026-07-02]]
- [[raw/blog-topics/sample-domain-contract-fixture-clean-architecture-2026-06-10]]
- [[raw/blog-topics/sample-fixture-dual-mode-build-matrix-2026-06-25]]
- [[raw/blog-topics/secret-source-port-restart-only-rotation-2026-07-02]]
- [[raw/blog-topics/skip-locked-outbox-per-aggregate-fifo-gate-2026-06-11]]
- [[raw/blog-topics/spring-actuator-health-probe-group-split-2026-07-02]]
- [[raw/blog-topics/spring-async-taskdecorator-bounded-executor-saturation-shutdown-2026-06-13]]
- [[raw/blog-topics/spring-boot-3-configprops-record-multi-constructor-binding-2026-06-12]]
- [[raw/blog-topics/spring-boot-serialization-contract-pins-2026-07-02]]
- [[raw/blog-topics/spring-boot-startup-exit-code-propagation-2026-06-10]]
- [[raw/blog-topics/spring-conditional-on-property-optional-adapter-template-2026-06-09]]
- [[raw/blog-topics/spring-responseentityexceptionhandler-transport-failure-envelope-2026-07-02]]
- [[raw/blog-topics/spring-security-filter-layer-error-envelope-2026-06-08]]
- [[raw/blog-topics/streaming-response-not-supported-archunit-ban-2026-07-02]]
- [[raw/blog-topics/test-taxonomy-archunit-enforcement-2026-06-19]]
- [[raw/blog-topics/transaction-isolation-vendor-default-pin-2026-07-02]]
- [[raw/blog-topics/transaction-port-abstraction-over-spring-transactional-2026-05-28]]
- [[raw/blog-topics/trivy-suppression-governance-static-gate-2026-06-20]]
- [[raw/blog-topics/ulid-crockford-base32-excluded-letters-2026-06-01]]
- [[raw/blog-topics/w3c-traceparent-fork-activated-seam-2026-07-02]]
- [[raw/blog-topics/webhook-full-jitter-dlq-observability-2026-07-02]]
- [[raw/blog-topics/webhook-signature-replay-contract-2026-07-02]]
- [[raw/blog-topics/webhook-ssrf-egress-proxy-redirect-block-2026-07-02]]
<!-- GENERATED: blog-topics:end -->
<!-- GENERATED: errors:start -->
- [[raw/errors/apply-patch-auto-approval-rejected-2026-05-28]]
- [[raw/errors/archunit-b7-configprops-nested-record-accessor-outbound-2026-06-13]]
- [[raw/errors/archunit-b7-configuration-bean-factory-return-type-2026-06-09]]
- [[raw/errors/archunit-empty-should-anchor-2026-05-27]]
- [[raw/errors/archunit-importpackages-empty-vacuous-stale-build-2026-06-20]]
- [[raw/errors/archunit-no-uuid-random-trace-id-false-positive-2026-06-01]]
- [[raw/errors/archunit-test-scope-sample-ticket-inclusion-2026-05-28]]
- [[raw/errors/bootstrap-postgres-port-collision-2026-06-24]]
- [[raw/errors/ca-gitignored-seed-divergence-at-rebase]]
- [[raw/errors/ca-public-path-snapshot-scope-violation]]
- [[raw/errors/ca-tmpl-preexisting-check-baseline-failures-2026-07-20]]
- [[raw/errors/ci-fan-in-skipped-not-failed-and-gitignored-config-2026-06-20]]
- [[raw/errors/contract-registry-reference-row-universal-column-false-fail-2026-06-20]]
- [[raw/errors/developer-experience-contract-agents-bridge-2026-07-15]]
- [[raw/errors/flyway-launcher-divergent-migration-set-shared-dev-db-2026-06-12]]
- [[raw/errors/gitea-act-action-tag-and-dependency-graph-2026-06-20]]
- [[raw/errors/gitea-act-missing-jq-job-bootstrap-2026-06-23]]
- [[raw/errors/global-sed-env-rename-pitfalls-2026-06-06]]
- [[raw/errors/gradle-strict-lock-stale-entry-non-resolvable-config-2026-07-08]]
- [[raw/errors/gradle-wrapper-lock-read-only-sandbox-2026-06-10]]
- [[raw/errors/gradle-wrapper-readonly-cache-2026-05-28]]
- [[raw/errors/gradle-wrapper-sandbox-lock-2026-06-25]]
- [[raw/errors/gradle-wrapper-sandbox-lock-readiness-scorecard-2026-06-26]]
- [[raw/errors/hibernate-dto-projection-explain-width-not-narrower-2026-07-13]]
- [[raw/errors/hibernate-getcollectionfetchcount-batch-semantics-2026-07-13]]
- [[raw/errors/hibernate7-hhh90003004-collection-fetch-paging-2026-07-13]]
- [[raw/errors/idempotency-column-definition-base-check-failure-2026-07-15]]
- [[raw/errors/idempotency-expired-row-reclaim-409-loop-2026-06-09]]
- [[raw/errors/internal-auth-misconfiguration-retryable-invariant-conflict-2026-06-08]]
- [[raw/errors/jdk-httpclient-dns-unresolvedaddress-connectexception-2026-06-11]]
- [[raw/errors/jpa-repository-scan-miss-multimodule-2026-06-10]]
- [[raw/errors/mapping-exception-location-archunit-catch-2026-05-29]]
- [[raw/errors/method-security-cglib-vs-jdk-proxy-usecase-injection-2026-06-08]]
- [[raw/errors/method-security-class-pointcut-final-usecase-bean-2026-06-12]]
- [[raw/errors/micrometer-meterfilter-replacetagvalues-functioncounter-2026-06-11]]
- [[raw/errors/mockmvc-406-produces-accept-double-fault-2026-06-02]]
- [[raw/errors/responseentityexceptionhandler-ambiguous-exception-handler-2026-06-02]]
- [[raw/errors/sample-portfolio-flyway-out-of-order-2026-06-23]]
- [[raw/errors/sample-portfolio-oauth2-resource-server-dependency-2026-05-27]]
- [[raw/errors/sample-portfolio-tomcat-port-in-use-check-2026-07-03]]
- [[raw/errors/sample-ticket-oauth2-resource-server-dependency-2026-05-27]]
- [[raw/errors/sandbox-build-verification-boundaries-2026-06-21]]
- [[raw/errors/scheduled-reaper-wrong-config-prefix-2026-06-09]]
- [[raw/errors/slim-jre-random-generator-missing-2026-06-24]]
- [[raw/errors/spanerrorrecorder-constructor-breaks-webmvctest-slice-2026-06-14]]
- [[raw/errors/spotbugs-commons-lang3-bom-downgrade-noclassdef-2026-06-20]]
- [[raw/errors/spring-boot-four-jackson-three-migration-2026-06-30]]
- [[raw/errors/spring-configuration-bean-factory-method-not-processed-2026-06-11]]
- [[raw/errors/spring-integration-defaultlockrepository-aftersingletons-null-template-2026-06-13]]
- [[raw/errors/spring-jpa-flyway-circular-dependency-2026-06-23]]
- [[raw/errors/spring-jpa-postgres-lob-oid-cast-2026-06-23]]
- [[raw/errors/startup-log-suppression-spotless-format-2026-07-03]]
- [[raw/errors/testcontainers-two-context-shared-datasource-close-2026-06-11]]
- [[raw/errors/ulid-fixture-crockford-u-self-inconsistency-2026-06-01]]
- [[raw/errors/webmvctest-component-filter-constructor-dep-breaks-slice-2026-06-14]]
- [[raw/errors/webmvctest-nested-springbootconfiguration-context-pollution-2026-06-01]]
- [[raw/errors/webmvctest-slice-configprops-enum-placeholder-no-default-2026-06-20]]
<!-- GENERATED: errors:end -->
> ca-skeleton (= ca-tmpl) 프로젝트에 묶이는 모든 raw 자료. ca-tmpl repo (`/home/donghyeon/workspace/ca-tmpl/`) 의 코드와 함께 본 LLM Wiki 의 자료들이 cluster 구성.
### 31.1 브랜치 (feature-* / develop-* / fix-* / chore-* / experiment-*)
<!-- GENERATED: branches:start -->
- [[raw/branch-notes/feature-api-compatibility-deprecation-contract]]
- [[raw/branch-notes/feature-api-contract-baseline]]
- [[raw/branch-notes/feature-application-port-usecase-contract]]
- [[raw/branch-notes/feature-application-query-bypass-contract]]
- [[raw/branch-notes/feature-architecture-enforcement-rules]]
- [[raw/branch-notes/feature-authentication-authorization-contract]]
- [[raw/branch-notes/feature-background-job-async-contract]]
- [[raw/branch-notes/feature-boundary-validation-mapping-contract]]
- [[raw/branch-notes/feature-build-release-supply-chain-contract]]
- [[raw/branch-notes/feature-business-rule-validation-contract]]
- [[raw/branch-notes/feature-cache-consistency-contract]]
- [[raw/branch-notes/feature-cachestore-multi-backend-router]]
- [[raw/branch-notes/feature-ci-quality-gates-contract]]
- [[raw/branch-notes/feature-container-runtime-contract]]
- [[raw/branch-notes/feature-contract-registry-governance]]
- [[raw/branch-notes/feature-contract-verification-test-suite]]
- [[raw/branch-notes/feature-data-retention-privacy-contract]]
- [[raw/branch-notes/feature-database-connection-pool-contract]]
- [[raw/branch-notes/feature-dependency-vulnerability-management-contract]]
- [[raw/branch-notes/feature-developer-experience-contract]]
- [[raw/branch-notes/feature-distributed-lock-contract]]
- [[raw/branch-notes/feature-distributed-tracing-contract]]
- [[raw/branch-notes/feature-domain-event-outbox-contract]]
- [[raw/branch-notes/feature-domain-feature-onboarding-contract]]
- [[raw/branch-notes/feature-domain-modeling-guardrails]]
- [[raw/branch-notes/feature-env-driven-runtime-configuration]]
- [[raw/branch-notes/feature-file-resource-handling-contract]]
- [[raw/branch-notes/feature-implementation-readiness-scorecard]]
- [[raw/branch-notes/feature-integration-adapter-templates]]
- [[raw/branch-notes/feature-log-management-contract]]
- [[raw/branch-notes/feature-management-actuator-security-contract]]
- [[raw/branch-notes/feature-messaging-multibroker-router]]
- [[raw/branch-notes/feature-metrics-alerting-contract]]
- [[raw/branch-notes/feature-migration-startup-contract]]
- [[raw/branch-notes/feature-notification-provider-spi]]
- [[raw/branch-notes/feature-operational-error-observability-foundation]]
- [[raw/branch-notes/feature-operational-runbook-contract]]
- [[raw/branch-notes/feature-outbound-http-client-baseline]]
- [[raw/branch-notes/feature-persistence-auditing-contract]]
- [[raw/branch-notes/feature-persistence-failure-baseline]]
- [[raw/branch-notes/feature-rate-limit-idempotency-contract]]
- [[raw/branch-notes/feature-repository-access-permission-contract]]
- [[raw/branch-notes/feature-resource-identifier-contract]]
- [[raw/branch-notes/feature-runtime-context-propagation-contract]]
- [[raw/branch-notes/feature-runtime-health-lifecycle-contract]]
- [[raw/branch-notes/feature-sample-domain-contract-fixture]]
- [[raw/branch-notes/feature-sample-portfolio-public-access]]
- [[raw/branch-notes/feature-sample-removal-adoption-contract]]
- [[raw/branch-notes/feature-schema-serialization-contract]]
- [[raw/branch-notes/feature-secrets-config-source-contract]]
- [[raw/branch-notes/feature-security-operational-baseline]]
- [[raw/branch-notes/feature-skeleton-package-blueprint-contract]]
- [[raw/branch-notes/feature-startup-failure-log-suppression]]
- [[raw/branch-notes/feature-static-analysis-quality-contract]]
- [[raw/branch-notes/feature-streaming-response-contract]]
- [[raw/branch-notes/feature-tenant-context-policy]]
- [[raw/branch-notes/feature-test-taxonomy-fixture-contract]]
- [[raw/branch-notes/feature-transaction-concurrency-contract]]
- [[raw/branch-notes/feature-webhook-outbound-contract]]
<!-- GENERATED: branches:end -->
> generated reverse view는 child branch의 v2 contract migration 후 채운다. 아래 수기 목록은 그 전까지 legacy navigation으로 보존한다.
> ca-skeleton 은 root branch 가 단일이 아니라 다수의 `feature-*` 가 직접 project 에 매달림. 모두 Tier-1 hub.
핵심 `feature-*` branch-notes (`raw/branch-notes/`):
- [[raw/branch-notes/feature-architecture-enforcement-rules]] — ArchUnit + Gradle dependency
- [[raw/branch-notes/feature-skeleton-package-blueprint-contract]] — Gradle multi-module Clean Architecture / Hexagonal module blueprint
- [[raw/branch-notes/feature-domain-modeling-guardrails]] — VO/Entity/Aggregate 규칙
- [[raw/branch-notes/feature-application-port-usecase-contract]] — TransactionPort / Use Case
- [[raw/branch-notes/feature-boundary-validation-mapping-contract]] — Validation 4-layer
- [[raw/branch-notes/feature-api-contract-baseline]] — `/v1`, Idempotency-Key, Pagination
- [[raw/branch-notes/feature-api-compatibility-deprecation-contract]] — Sunset + Deprecation 90/30 일
- [[raw/branch-notes/feature-contract-registry-governance]] — error/env/log/metric registry SSOT
- [[raw/branch-notes/feature-sample-removal-adoption-contract]] — sample-portfolio 제거 2단계
- [[raw/branch-notes/feature-business-rule-validation-contract]] — 도메인 vs 인프라 검증 책임
- [[raw/branch-notes/feature-domain-event-outbox-contract]] — SKIP LOCKED outbox
- [[raw/branch-notes/feature-transaction-concurrency-contract]] — READ_COMMITTED + retry
- [[raw/branch-notes/feature-cache-consistency-contract]] — after-commit invalidation + stampede
- [[raw/branch-notes/feature-persistence-failure-baseline]] — OSIV off + SQLState 매핑
- [[raw/branch-notes/feature-rate-limit-idempotency-contract]] — 3-tuple idempotency + 200ms wait
- [[raw/branch-notes/feature-file-resource-handling-contract]] — 3계층 size limit
- [[raw/branch-notes/feature-schema-serialization-contract]] — ISO-8601 + BigDecimal scale
- [[raw/branch-notes/feature-data-retention-privacy-contract]] — HMAC salt + GDPR Art.17
- [[raw/branch-notes/feature-integration-adapter-templates]] — Kafka/Redis/Slack/Google Email
- [[raw/branch-notes/feature-outbound-http-client-baseline]] — RestClient + Resilience4j
- [[raw/branch-notes/feature-background-job-async-contract]] — TaskDecorator + ShedLock
- [[raw/branch-notes/feature-distributed-tracing-contract]] — Micrometer Tracing + W3C
- [[raw/branch-notes/feature-log-management-contract]] — structured JSON + masking
- [[raw/branch-notes/feature-management-actuator-security-contract]] — port 9001 + allowlist
- [[raw/branch-notes/feature-metrics-alerting-contract]] — Micrometer + cardinality limit
- [[raw/branch-notes/feature-migration-startup-contract]] — Flyway + multi-instance lock
- [[raw/branch-notes/feature-operational-error-observability-foundation]] — custom envelope + 10 categories
- [[raw/branch-notes/feature-secrets-config-source-contract]] — `__LOCAL_DEV_` sentinel
- [[raw/branch-notes/feature-env-driven-runtime-configuration]] — APP_*, Duration `30s`
- [[raw/branch-notes/feature-container-runtime-contract]] — MaxRAMPercentage=75
- [[raw/branch-notes/feature-runtime-health-lifecycle-contract]] — 3-probe + NTP drift
- [[raw/branch-notes/feature-security-operational-baseline]] — JWT + JWKS refresh
- [[raw/branch-notes/feature-repository-access-permission-contract]] — @UseCaseRepositoryAccess
- [[raw/branch-notes/feature-tenant-context-policy]] — multi-tenancy opt-in
- [[raw/branch-notes/feature-operational-runbook-contract]] — runbook:// scheme
- [[raw/branch-notes/feature-contract-verification-test-suite]] — 11 release-blocking gates
- [[raw/branch-notes/feature-test-taxonomy-fixture-contract]] — 6 test levels
- [[raw/branch-notes/feature-sample-domain-contract-fixture]] — sample-portfolio 12 scenarios
- [[raw/branch-notes/feature-developer-experience-contract]] — `./gradlew bootstrap`
- [[raw/branch-notes/feature-ci-quality-gates-contract]] — 20 CI gates
- [[raw/branch-notes/feature-build-release-supply-chain-contract]] — SBOM + Cosign + SLSA
- [[raw/branch-notes/feature-domain-feature-onboarding-contract]] — multi-module domain onboarding slice
- [[raw/branch-notes/feature-implementation-readiness-scorecard]] — 15-area binary gate
- [[raw/branch-notes/feature-distributed-lock-contract]] — `distributedLockProvider` bean 계약 (JdbcLockRegistry default + tx commit 정합)
(총 44개 — `raw/branch-notes/feature-*.md` glob 으로 확인 가능)
### 31.2 근거 자료
- 개별 official-docs / company-tech-blogs 는 각 `feature-*` branch-note 의 Sources 표에서 cited.
### 31.3 오류 기록
- [[raw/errors/archunit-empty-should-anchor-2026-05-27]] — skeleton anchor package와 ArchUnit empty should rule 정합성 문제.
- [[raw/errors/sample-portfolio-oauth2-resource-server-dependency-2026-05-27]] — sample-portfolio 격리 후 OAuth2 resource-server dependency 누락 문제.
- [[raw/errors/gradle-wrapper-readonly-cache-2026-05-28]] — agent sandbox에서 Gradle wrapper cache lock 파일 생성 실패.
### 31.4 면접 준비
- [[raw/interviews/clean-architecture-module-blueprint]] — multi-module Clean Architecture skeleton 선택 이유.
- [[raw/interviews/shared-contract-and-sample-isolation]] — shared-contract와 sample-portfolio 격리 근거.
- [[raw/interviews/clean-architecture-boundary-enforcement]] — Gradle/ArchUnit 기반 경계 검증 경험.
### 31.5 블로그·채용공고 연계 글감
- [[raw/blog-topics/clean-architecture-module-blueprint-2026-05-28]] — package/module skeleton blueprint와 sample-portfolio 격리에서 나온 글감.
- [[raw/blog-topics/clean-architecture-boundary-enforcement-2026-05-28]] — Gradle/ArchUnit 경계 검증에서 나온 글감.
- [[raw/blog-topics/post-implementation-knowledge-capture-workflow-2026-05-28]] — 구현 후 Wiki capture workflow에서 나온 글감.
### 31.6 파생 wiki 문서
- canonical 검증 사실:
- [[ca-tmpl]] — 16 의사결정 project doc hub (named hub, sibling `wiki/projects/ca-tmpl/` 폴더의 MOC)
- 16 개별 project docs: `wiki/projects/ca-tmpl/{topic}.md`
- 관련 일반 개념:
- 16 wiki/concepts/{topic}.md (Core 6 + Cross-cutting 10) — [[llm-wiki]] 참조
- 포트폴리오: (Phase D 후속)
- 블로그 글: (Phase D 후속)
## 32. Phase 5 Additional Evidence Raws (2026-05-27)
> Phase 5B 외부 근거 추가 보강. 25개 신규 raw 파일 (`raw/official-docs/` 하위) 을 owning decision/branch-note 별로 매핑. 각 raw 는 frontmatter `related_projects: [ca-skeleton]` 보유. 본 섹션은 §29 (Phase 1~4 162개 누적) 이후 추가된 evidence index.
>
> **출처 신뢰도 (CLAUDE.md §5 정합)**: 본 섹션의 모든 raw 는 `source_type: official-doc` (RFC, IANA registry, vendor 공식 reference, OWASP cheat sheet, K8s 공식 문서 등). company-tech-blog 는 포함되지 않음.
>
> **사용 경계**: 본 섹션은 raw evidence 의 cluster-level index 역할. 각 raw 의 Claim ID / Usage Boundary 는 raw 파일 자체의 `## Claims Extracted` 섹션에서 확인. 본 project-note 는 raw 를 owning decision/branch 에 매핑할 뿐이며, raw 의 verbatim claim 을 그대로 best practice 로 단정하지 않음.
### 32.1 Architecture / Boundary (3 raw)
| raw | 채택 위치 (decision / branch) | 사용 근거 |
| --- | --- | --- |
| [[raw/official-docs/arch-clean-architecture-uncle-bob]] | `feature-repository-access-permission-contract`, `feature-architecture-enforcement-rules`, `feature-skeleton-package-blueprint-contract` | Clean Architecture 의 Dependency Rule (외층 → 내층 only) 이 ca-skeleton 의 module dependency rule (§20) 의 1차 reference standard |
| [[raw/official-docs/arch-hexagonal-cockburn]] | `feature-application-port-usecase-contract`, `feature-repository-access-permission-contract`, `feature-architecture-enforcement-rules` | Cockburn 의 Ports & Adapters 가 inbound port (`*UseCase`) / outbound port (`*Port`) 분리 (§25 port naming default decision) 의 reference standard |
| [[raw/official-docs/cqrs-fowler-bliki]] | [[raw/branch-notes/feature-repository-access-permission-contract]] (D10 capability matrix), `feature-domain-modeling-guardrails` | Fowler 의 CQRS 분류가 command/query use case 분리 (§19 Use Case / Port Contract) 와 `READ_REPOSITORY` / `WRITE_REPOSITORY` capability 분리 (§10) 의 reference. 단, ca-skeleton 은 event sourcing 미채택. |
### 32.2 Repository / Persistence / Transaction (3 raw)
| raw | 채택 위치 | 사용 근거 |
| --- | --- | --- |
| [[raw/official-docs/microservices-io-transactional-outbox]] | [[raw/branch-notes/feature-repository-access-permission-contract]] (D7 outbox capability), `feature-domain-event-outbox-contract` | microservices.io 의 Transactional Outbox pattern 정의가 ca-skeleton 의 outbox SKIP LOCKED polling 결정 (§14, §29 Topic 3) 의 reference (단, microservices.io 는 패턴 카탈로그이며 polling vs CDC 트레이드오프는 별도 source). |
| [[raw/official-docs/archunit-user-guide]] | [[raw/branch-notes/feature-repository-access-permission-contract]] (D8 enforcement), `feature-architecture-enforcement-rules` | ArchUnit 공식 user guide 가 `@UseCaseRepositoryAccess` annotation-based rule 의 enforcement 메커니즘 (§21 Capability Registry) 근거. annotation processor alternative, runtime AOP forbidden 정합. |
| [[raw/official-docs/spring-tx-management-reference]] | [[raw/branch-notes/feature-repository-access-permission-contract]] (D4 transaction boundary), `feature-transaction-concurrency-contract`, `feature-application-port-usecase-contract` | Spring 공식 Transaction Management reference 가 `@Transactional` propagation/isolation 의 표준 정의. ca-skeleton 은 `@Transactional` 직접 import 금지 + `TransactionPort` 추상화 (§25 transaction boundary default) 채택 — Spring 표준을 reference 로 두되 application layer 격리. |
### 32.3 Outbound HTTP / Resilience (3 raw)
| raw | 채택 위치 | 사용 근거 |
| --- | --- | --- |
| [[raw/official-docs/resilience4j-micrometer-module]] | [[raw/branch-notes/feature-outbound-http-client-baseline]] (D4 metric), `feature-metrics-alerting-contract` | Resilience4j 공식 Micrometer 통합 모듈이 retry/circuit-breaker metric 이름·tag (§29 Group G-A metric) 의 표준 reference. ca-skeleton metric registry (§21 Metrics Registry) 의 retry/CB metric 정의 근거. |
| [[raw/official-docs/spring-restclient-builder-reference]] | [[raw/branch-notes/feature-outbound-http-client-baseline]] (D5/D7 mechanism) | Spring 6.1+ RestClient 공식 builder API reference. ca-skeleton 의 RestClient 기본값 (§11 Outbound HTTP, §29 Group G-C) 의 직접 source. RestTemplate maintenance-only 정책과 정합. |
| [[raw/official-docs/spring-smartlifecycle-reference]] | [[raw/branch-notes/feature-outbound-http-client-baseline]] (D8 graceful shutdown), `feature-runtime-health-lifecycle-contract` | Spring `SmartLifecycle` 인터페이스 reference. ca-skeleton 의 graceful shutdown 20s+5s+35s (§29 Group G-D Container) 와 outbound HTTP shutdown retry suppression (§28 Numeric conflicts) 의 phase 분리 메커니즘 근거. |
### 32.4 API Contract / Schema / Versioning (4 raw)
| raw | 채택 위치 | 사용 근거 |
| --- | --- | --- |
| [[raw/official-docs/rfc9110-http-semantics]] | [[raw/branch-notes/feature-outbound-http-client-baseline]] (D6 status code), [[raw/branch-notes/feature-api-contract-baseline]] (D8/D9 method/status) | RFC 9110 (HTTP Semantics, 2022) 이 status code 의미·method 정의·conditional request 의 정식 reference. ca-skeleton 의 error envelope HTTP status 매핑 (§3, §6) 과 outbound HTTP 분류 (§11) 의 standard. |
| [[raw/official-docs/openapi-spec-3-1-0]] | [[raw/branch-notes/feature-api-contract-baseline]] (D10), `feature-contract-verification-test-suite`, `feature-api-compatibility-deprecation-contract` | OpenAPI 3.1.0 공식 spec (JSON Schema 2020-12 정합). ca-skeleton 의 OpenAPI snapshot drift test (§25 OpenAPI drift default, §29 Group G-G) 의 standard reference. |
| [[raw/official-docs/google-aip-185-resource-versioning]] | [[raw/branch-notes/feature-api-contract-baseline]] (D2/D6 versioning) | Google AIP-185 (Resource Versioning) 가 URI path version (`/v1`) vs header version trade-off 의 reference. ca-skeleton 의 `/v1` URI prefix default (§25 API versioning) 결정 근거 — Google AIP 는 외부 공식 reference 이지만 Google API 정책이라 ca-skeleton 이 100% 따라가지는 않음. |
| [[raw/official-docs/jsonapi-pagination-format]] | [[raw/branch-notes/feature-api-contract-baseline]] (D7 pagination) | JSON:API 공식 pagination format (`page[number]`, `page[size]`, `links.{first,last,next,prev}`). ca-skeleton 의 envelope `meta.page` (§21 Response Envelope) 의 reference 대안 1종 — ca-skeleton 은 자체 envelope 채택, JSON:API 는 비교 대안 (§29 Topic 4 API Error Envelope) 으로 보존. |
### 32.5 Schema / Serialization (2 raw)
| raw | 채택 위치 | 사용 근거 |
| --- | --- | --- |
| [[raw/official-docs/rfc3339-datetime-utc]] | [[raw/branch-notes/feature-runtime-health-lifecycle-contract]] (D12 timezone), `feature-schema-serialization-contract` | RFC 3339 (Date and Time on the Internet) 이 ISO-8601 의 IETF profile. ca-skeleton 의 ISO-8601 offset UTC default (§16, §29 Group G-F Schema) 의 직접 reference. |
| [[raw/official-docs/iana-media-types-registry]] | [[raw/branch-notes/feature-file-resource-handling-contract]] (D7 content-type allowlist), `feature-schema-serialization-contract` | IANA Media Types Registry 가 `Content-Type` allowlist (§18 File / Resource Handling, §29 Group G-J File) 의 SSOT. ca-skeleton 의 6종 content-type allowlist 의 reference. |
### 32.6 File / Resource Handling (5 raw)
| raw | 채택 위치 | 사용 근거 |
| --- | --- | --- |
| [[raw/official-docs/spring-boot-multipart-reference]] | [[raw/branch-notes/feature-file-resource-handling-contract]] (D3/D4 mechanism) | Spring Boot Multipart 공식 reference (`spring.servlet.multipart.max-file-size` 등). ca-skeleton 의 3-layer size limit (Spring 10MB / global 12MB / gateway 20MB, §29 Numeric conflicts) 중 Spring layer 의 직접 source. |
| [[raw/official-docs/nginx-client-max-body-size]] | [[raw/branch-notes/feature-file-resource-handling-contract]] (D4 gateway layer) | nginx `client_max_body_size` 공식 reference. ca-skeleton 3-layer size limit 의 gateway layer (20MB) 의 source. |
| [[raw/official-docs/owasp-file-upload-cheat-sheet]] | [[raw/branch-notes/feature-file-resource-handling-contract]] (D5 security), `feature-security-operational-baseline` | OWASP File Upload Cheat Sheet 가 file upload 보안 baseline (content-type validation, size limit, path validation, antivirus scanning) 의 SSOT. ca-skeleton 의 antivirus gateway default (§29 Missing Area Closures) 와 content-type allowlist 의 보안 reference. |
| [[raw/official-docs/owasp-path-traversal]] | `feature-file-resource-handling-contract` | OWASP Path Traversal cheat sheet 가 download/serve 경로 traversal 방지 (§18 File / Resource Handling - "path traversal 방지") 의 source. |
| [[raw/official-docs/jdk-files-createtempfile]] | [[raw/branch-notes/feature-file-resource-handling-contract]] (D6 temp file) | JDK `Files.createTempFile` Javadoc reference. ca-skeleton 의 temp file 1h orphan cleanup (§29 Group G-J File) 과 secure temp file creation 의 표준 API source. |
| [[raw/official-docs/spring-streaming-response-body]] | [[raw/branch-notes/feature-file-resource-handling-contract]] (D8 download streaming) | Spring `StreamingResponseBody` 공식 reference. ca-skeleton 의 download streaming failure 분류 (§18 File / Resource Handling - "download streaming failure") 의 mechanism source. |
### 32.7 Runtime / Health / Lifecycle (2 raw)
| raw | 채택 위치 | 사용 근거 |
| --- | --- | --- |
| [[raw/official-docs/k8s-configure-probes-task-page]] | [[raw/branch-notes/feature-runtime-health-lifecycle-contract]] (D5 probe config / D11 startup probe) | K8s 공식 "Configure Liveness, Readiness and Startup Probes" task page. ca-skeleton 의 3-probe 분리 (§29 Group G-D Runtime health) 와 readiness/liveness/startup separation 의 SSOT. |
| [[raw/official-docs/k8s-pod-lifecycle-probes-concept]] | [[raw/branch-notes/feature-runtime-health-lifecycle-contract]] (D7 probe semantics) | K8s 공식 Pod Lifecycle concept page (probe lifecycle, restart policy 등). ca-skeleton 의 readiness gated migration (§28 Critical Defaults - migration runner) 와 graceful shutdown ↔ probe interaction 의 conceptual reference. |
### 32.8 Operational Runbook / Alerting (3 raw)
| raw | 채택 위치 | 사용 근거 |
| --- | --- | --- |
| [[raw/official-docs/lychee-link-checker]] | [[raw/branch-notes/feature-operational-runbook-contract]] (D4 link-check tool) | lychee (Rust 기반 markdown link checker) 공식 doc. ca-skeleton 의 markdown-link-check 또는 lychee 를 통한 runbook link drift 방지 (§29 Self-Contradiction Closures - "runbook link required but unverifiable") 의 tool option. |
| [[raw/official-docs/prometheus-alertmanager-silences]] | [[raw/branch-notes/feature-operational-runbook-contract]] (D6 alert silencing) | Prometheus Alertmanager silences 공식 doc. ca-skeleton 의 alert dedup 5분 + P1 2분 noise 억제 (§28 Numeric conflicts) 의 silence/inhibition mechanism reference. |
| [[raw/official-docs/google-sre-workbook-on-call-monitoring]] | [[raw/branch-notes/feature-operational-runbook-contract]] (D2 monitoring philosophy / D10 on-call) | Google SRE Workbook "Monitoring" + "Being On-Call" chapters. ca-skeleton 의 P1/P2/P3 severity (§18 Metrics / Alerting) 와 runbook 7-section 표준 (§28 Phase D2 closures) 의 conceptual reference. company-tech-blog 아님 (Google 공식 book chapter, O'Reilly publication 형식이지만 Google SRE 가 저자). |
---
## 33. 아키텍처 검토 체크리스트
- [x] 한 줄 요약 + 현재 상태 + 나의 역할 채워짐 (본문 도입부)
- [x] 측정 가능한 성공 기준 — §1 목표 + §28 Phase 매트릭스 ✓
- [x] 아키텍처 다이어그램 1개 이상 첨부 (§30-1 모듈 의존성, §30-2 런타임 토폴로지) ✓ (Mermaid; drawio 이관은 Phase C2 시 검토)
- [x] 다이어그램의 모든 컴포넌트가 라벨 + 역할 + 기술 스택 표기 ✓
- [x] 다이어그램의 모든 화살표가 프로토콜·데이터 종류 라벨링 ✓
- [x] 외부 시스템이 점선 또는 색으로 시각적 구분 ✓ (`classDef external`)
- [x] 범례(Legend) 다이어그램에 포함 ✓ (§30-2 끝 범례 블록)
- [x] 신뢰 경계 / 네트워크 경계 표시 ✓ (subgraph TB_*)
- [x] 시퀀스 다이어그램 ≥1 (Mermaid) — happy path + error path (§30.1 Request flow, §30.2 Outbox, §30.3 Tenant) — 총 3개 ✓
- [x] Cluster 섹션의 root/feature branch 목록 채워짐 (§31.1) ✓
- [x] 마지막 architecture review 날짜 frontmatter `architecture_review:` 에 기록 — 2026-05-26 ✓
- [x] Phase 5B evidence raws (25 raw, §32) cluster-level index 작성 ✓
## 35. Implementation Coverage Checklist
본 § 는 ca-skeleton 의 *전체 영역 × 구현 진척* 트래커. 신규 영역 발견 시 본 § 에 row 추가 → branch 신설 → 결정 박음 → 코드 작성 순서. 상태 변경 시 본 § + 해당 branch 의 `status_label` 동시 갱신.
### 상태 표기
| 아이콘 | 의미 | 등급 (CLAUDE.md §6) |
|---|---|---|
| `[ ]` | 미시작 / scaffolding | `planned` |
| `[~]` | 결정 박힘, 코드 없음 | `documented-only` |
| `[*]` | 코드 일부 작성 | `actually-implemented` (partial) |
| `[x]` | 코드 완성 + 로컬 검증 | `locally-verified` |
| `[X]` | 운영 검증 | `prod-verified` |
| `(없음)` | branch 미존재 (신설 후보) | — |
### A. 외부 통신 / API 계약 영역
- `[~]` HTTP 표면 baseline (24 결정 — envelope / status / pagination / URL naming / PATCH / conditional / cache / LRO / bulk) — `feature-api-contract-baseline` (D=24, sl=in-progress)
- `[~]` Resource ID format (ULID 26-char Crockford base32) — `feature-resource-identifier-contract` (D=19, sl=in-progress, §구현 가이드 §1~§8 코드 skeleton ready)
- `[ ]` Webhook outbound (signature / replay / retry / observability) — `feature-webhook-outbound-contract` (D=0, scaffolding)
- `[ ]` Streaming response (SSE / WebSocket / long-poll / chunked — 1차 결정: 지원 여부) — `feature-streaming-response-contract` (D=0, scaffolding)
- `[~]` API versioning + Sunset / Deprecation (`/v1` URI prefix → 향후 `/v2`) — `feature-api-compatibility-deprecation-contract` (D=8, sl=in-progress)
- `[~]` Outbound HTTP resilience (Resilience4j circuit breaker / retry / timeout) — `feature-outbound-http-client-baseline` (D=11, sl=in-progress)
- `[~]` Error envelope + 코드 registry (`error-codes.yaml` SSOT) — `feature-operational-error-observability-foundation` (D=12, sl=in-progress)
- `[~]` Idempotency-Key + Rate limit (HTTP header / TTL / fingerprint) — `feature-rate-limit-idempotency-contract` (D=10, sl=in-progress)
- `[~]` Contract verification test suite (OpenAPI drift detection) — `feature-contract-verification-test-suite` (D=9, sl=in-progress)
- `[~]` Schema / Serialization (Jackson naming / date-time / decimal) — `feature-schema-serialization-contract` (D=7, sl=in-progress)
- `(없음)` Documentation generation / API docs publishing (OpenAPI → Swagger UI / Redoc / release artifact 묶기) — F 미래 후보 (사용자 항목 #8)
### 영속성 영역
- `[~]` Persistence failure baseline (optimistic lock / conflict 분류) — `feature-persistence-failure-baseline` (D=8, sl=in-progress)
- `[x]` Application port + use case contract (transaction 경계 + port 추상화) — `feature-application-port-usecase-contract` (D=14, sl=**actually-implemented**, AI=5, LV=2)
- `[~]` Repository capability annotation (`@UseCaseRepositoryAccess`) — `feature-repository-access-permission-contract` (D=11, sl=in-progress)
- `[~]` Transaction / Concurrency contract — `feature-transaction-concurrency-contract` (D=7, sl=in-progress)
- `[~]` Migration runner readiness gate (Flyway startup) — `feature-migration-startup-contract` (D=8, sl=in-progress)
- `[~]` Multi-tenancy isolation (tenant context + DB scope) — `feature-tenant-context-policy` (D=10, sl=in-progress)
- `[~]` Data retention / Privacy / GDPR (DSR / PII / 감사 log) — `feature-data-retention-privacy-contract` (D=12, sl=in-progress)
- `[~]` File / Resource handling (upload / download / S3) — `feature-file-resource-handling-contract` (D=12, sl=in-progress)
- `[~]` Domain event + Transactional Outbox — `feature-domain-event-outbox-contract` (D=10, sl=in-progress)
- `[~]` Cache consistency (Redis adapter + invalidation) — `feature-cache-consistency-contract` (D=9, sl=in-progress)
- `(없음)` Persistence auditing (CreatedBy / UpdatedBy 도메인 오염 차단) — **신규 branch 권고: `feature-persistence-auditing-contract`**
- `(없음)` DB connection pool 운영 안정성 (HikariCP pool size / timeout / leak detection / slow query) — **신규 branch 권고 (priority #4): `feature-database-connection-pool-contract`**
- `(없음)` Backup / restore / DR (PITR / schema rollback policy / restore drill) — F 미래 후보 (사용자 항목 #6)
### 배포 영역
- `[~]` Background job + Async boundary (scheduler / ShedLock) — `feature-background-job-async-contract` (D=12, sl=in-progress)
- `[~]` Runtime health / Lifecycle (actuator / probe / readiness gate) — `feature-runtime-health-lifecycle-contract` (D=13, sl=in-progress)
- `[~]` Management actuator security (port 분리 / 인증) — `feature-management-actuator-security-contract` (D=8, sl=in-progress)
- `[~]` Metrics / Alerting (Micrometer + P1/P2/P3 severity) — `feature-metrics-alerting-contract` (D=10, sl=in-progress)
- `[~]` Distributed tracing (W3C trace context + Micrometer Tracing) — `feature-distributed-tracing-contract` (D=12, sl=in-progress)
- `[~]` Log management (MDC / scrubber / SLF4J 2.x / profile별 console encoder 포맷) — `feature-log-management-contract` (D=10, sl=in-progress)
- `[~]` Container runtime (Temurin slim / JVM ergonomics / non-root) — `feature-container-runtime-contract` (D=5, sl=in-progress)
- `[~]` Build / Release / Supply chain (Gradle / SBOM / Cosign) — `feature-build-release-supply-chain-contract` (D=13, sl=in-progress)
- `[~]` Operational runbook (P1/P2/P3 runbook section 표준) — `feature-operational-runbook-contract` (D=10, sl=in-progress)
- `[~]` Developer experience (bootstrap command / IDE / docker-compose) — `feature-developer-experience-contract` (D=10, sl=in-progress)
- `[~]` Secrets / Config source (env / secret manager) — `feature-secrets-config-source-contract` (D=10, sl=in-progress)
- `[~]` Env-driven runtime configuration (`APP_*` prefix / feature flag) — `feature-env-driven-runtime-configuration` (D=10, sl=in-progress)
- `[~]` CI quality gates (test/lint/coverage thresholds) — `feature-ci-quality-gates-contract` (D=9, sl=in-progress)
- `(없음)` Static analysis / code quality baseline (Checkstyle / Spotless / ErrorProne / SpotBugs / PMD / Sonar) — **신규 branch 권고 (priority #2): `feature-static-analysis-quality-contract`** — ci-quality-gates 와 별개 (그 branch 는 *threshold*, 본 branch 는 *tool 선택 + 룰셋*)
- `[~]` Dependency / vulnerability management (CVE scan(Trivy) / CVSS 차단 임계값 / KEV override / suppression governance / Renovate-Dependabot 보안 update / license scan / transitive audit) — [[raw/branch-notes/feature-dependency-vulnerability-management-contract]] (2026-06-15 scaffold + D1~D10, sl=in-progress, 전부 `planned`) — build-release-supply-chain 과 별개 (그 branch 는 *artifact / SBOM / locking*, 본 branch 는 *vuln 정책 + 운영 중 upgrade*). §25 SSOT Owner Map "Dependency vulnerability policy" row 참조
- `(없음)` Performance / load baseline (k6 / Gatling / JMeter smoke load test / latency budget / throughput budget / N+1 query 감지) — F 미래 후보 (사용자 항목 #7)
- `(없음)` Local dev data lifecycle (seed data / test data reset / docker-compose volume reset / local DB migration 재실행) — F 미래 후보 (사용자 항목 #9). DX 와 인접하나 별도 row
### 보안·거버넌스 영역
- `[*]` Boundary validation + Mapping (B1~B9 — Jackson / PATCH / Bean Validation / Polymorphic / Virtual thread / ACL / Bulk / ArchUnit cross-cite) — `feature-boundary-validation-mapping-contract` (D=15, sl=in-progress, **AI=15, LV=2**)
- `[~]` Business rule validation (domain invariant) — `feature-business-rule-validation-contract` (D=9, sl=in-progress)
- `[~]` Security operational baseline (CORS / SecureRandom / header suppression / API key / session) — `feature-security-operational-baseline` (D=11, sl=in-progress)
- `[~]` Domain modeling guardrails (aggregate boundary / value object) — `feature-domain-modeling-guardrails` (D=8, sl=in-progress)
- `[*]` Domain feature slice + onboarding template — `feature-domain-feature-onboarding-contract` (D=7, sl=in-progress, AI=2, LV=2)
- `[x]` Skeleton package blueprint (Gradle multi-module + Clean Architecture) — `feature-skeleton-package-blueprint-contract` (D=10, sl=**locally-verified**, AI=3, LV=8)
- `[x]` Architecture enforcement rules (ArchUnit suite SSOT) — `feature-architecture-enforcement-rules` (D=12, sl=**review**, AI=11, LV=11)
- `[~]` Integration adapter templates — `feature-integration-adapter-templates` (D=9, sl=in-progress)
- `[~]` Sample domain fixture (sample-portfolio) — `feature-sample-domain-contract-fixture` (D=6, sl=in-progress)
- `[*]` Sample removal / Project adoption — `feature-sample-removal-adoption-contract` (D=6, sl=in-progress, AI=2, LV=2)
- `[~]` Contract registry governance (yaml SSOT) — `feature-contract-registry-governance` (D=7, sl=in-progress)
- `[*]` Implementation readiness scorecard — `feature-implementation-readiness-scorecard` (D=6, sl=in-progress, AI=2, LV=2)
- `[~]` Test taxonomy + fixture (unit/contract/architecture/integration) — `feature-test-taxonomy-fixture-contract` (D=8, sl=in-progress)
- `[~]` **Tenant context policy + multi-tenancy model** — [[raw/branch-notes/feature-tenant-context-policy]] (in-progress). **활성화 트리거**: [[raw/branch-notes/feature-resource-identifier-contract]] 의 D13 (ID 내 tenant 인코딩 거부 — *형식적 위치만* 결정) + D17 의 5번째 ArchUnit rule (`no_find_by_id_without_tenant`) 이 본 branch 결정 후 활성화 대기 중. **현재 branch out-of-scope** ("실제 SaaS tenant model 구현") 가 *모델 확장이 필요할 때* 갱신 필요 (`TenantId` VO / `tenant` 테이블 / FK / `findByIdAndTenant` repository contract / auth → tenant 해석). 단순 single-tenant skeleton 이면 *지원 안함* 결정으로 close 가능
- `(없음)` Template instantiation contract (group / artifact / basePackage / root package rename / README 치환 / sample-off 적용 검증) — **신규 branch 권고 (priority #1): `feature-template-instantiation-contract`** — developer-experience + sample-removal-adoption 와 인접하나 *clone 후 검증 절차* 가 독립 row 로 약함
- `(없음)` AuthN / AuthZ product API baseline (JWT / OAuth2 resource server / RBAC / ABAC / permission matrix / endpoint authorization annotation) — **신규 branch 권고 (priority #5): `feature-authentication-authorization-contract`**`feature-security-operational-baseline` 와 별개 (그 branch 는 CORS / SecureRandom / header suppression 중심, 본 branch 는 *product API 인증/인가*)
### E. 신규 branch 권고 (우선순위 9개)
사용자 18항 + 9 보강 분석 결과의 통합 우선순위. 박을 시점은 본 branch 가 *현재 결정에 영향* 을 주거나 *코드 작성 중 막힐* 때.
| 우선순위 | branch (예정) | 영역 | 박을 시점 | 비고 |
|---|---|---|---|---|
| 1 | `feature-template-instantiation-contract` | D | 사용자 첫 template clone 시점 | group/artifact/basePackage rename + sample-off 자동화 |
| 2 | `feature-static-analysis-quality-contract` | C | 코드 작성 본격화 직전 | Checkstyle/Spotless/ErrorProne/SpotBugs/PMD/Sonar tool 선택 + 룰셋 |
| 3 | `feature-dependency-vulnerability-management-contract` | C | CI 셋업 시점 | Dependabot/Renovate + CVE scan + license scan + 운영 upgrade 정책 |
| 4 | `feature-database-connection-pool-contract` | B | persistence 코드 작성 시점 | HikariCP pool size / timeout / leak detection / slow query |
| 5 | `feature-authentication-authorization-contract` | D | 도메인이 사용자 인증 요구하는 시점 | JWT/OAuth2 RS + RBAC/ABAC + endpoint auth annotation |
| 6 | `feature-application-query-bypass-contract` | B/D | query endpoint 첫 작성 시점 | CQRS Q 경로 — use case bypass 허용 여부 (architecture-blocking). **✅ 2026-06-05 scaffold + 구현 완료 (locally-verified)**: branch-note + D1 purity-guardrail ArchUnit rule(`query_ports_do_not_leak_domain_jpa_or_web_types`, generic type argument 검사) + sample-portfolio projection demo + D3/D4/D5 코드화. D2(separate read store) documented-only |
| 7 | `feature-runtime-context-propagation-contract` | B/D | virtual thread 활성화 + 도메인 context 전파 요구 시점 | Java 21 Scoped Values — boundary B6 의 도메인 확장 |
| 8 | `feature-persistence-auditing-contract` | B | entity audit 컬럼 (CreatedBy/UpdatedBy) 도입 시점 | 도메인 오염 차단 메커니즘 (`AuditPort` + adapter 가로채기) |
| 9 | `feature-distributed-lock-contract` | B/C | multi-instance prod 도입 시점 | Redisson / DB advisory lock + 트랜잭션 commit 정합. **✅ 2026-06-12 branch-note 생성 (D1~D8 박힘 — JdbcLockRegistry default + xact advisory 보조 + ShedLock/session-advisory 배제, 코드 전부 `planned`)** — [[raw/branch-notes/feature-distributed-lock-contract]] |
### 운영 요구 시 신설)
운영 단계에서 *필수가 되지만* skeleton 1차 범위에서는 deferred. 도메인이 요구하거나 prod 운영 시점에 신설 의무.
- `(없음)` Backup / restore / DR (PITR / schema rollback / restore drill) — 운영 앱 진입 시 필수 (사용자 항목 #6)
- `(없음)` Performance / load baseline (k6 / Gatling / JMeter / latency budget / N+1 detection) — prod 트래픽 수반 시 필수 (사용자 항목 #7)
- `(없음)` Documentation generation / API docs publishing (Swagger UI / Redoc / release artifact 묶기) — API 외부 공개 시 필수 (사용자 항목 #8)
- `(없음)` Local dev data lifecycle (seed / reset / docker volume / migration 재실행) — DX 강화 시점 (사용자 항목 #9). DX 와 인접
- `(없음)` Time / Clock 주입 (`Clock` port, `Instant.now()` 차단 ArchUnit rule) — domain 시간 의존 시점
- `(없음)` Locale / i18n (error message 다국어, `MessageSource` 추상화) — 다국어 서비스 시점
- `(없음)` Money / Currency / BigDecimal 정밀도 (금융 도메인 패턴) — 금융 도메인 시점
- `(없음)` Search abstraction (Elasticsearch / PostgreSQL FTS port) — 검색 도입 시점
- `(없음)` Email / SMS / Notification outbound (adapter 표면) — 알림 도입 시점
- `(없음)` Saga / Process Manager (multi-step 분산 트랜잭션) — 복합 도메인 시점
- `(없음)` Soft delete vs hard delete policy (data-retention 의 확장) — soft-delete 도입 시점
### 사용 절차
1. **상태 확인**: 코딩 시작 전 본 § grep 으로 *해당 영역 branch 상태* 파악.
2. **결정 부족 시**: 해당 branch 의 §결정 사항 / §Decision Evidence Map drain 우선.
3. **코드 시작 후 갱신**: `[ ]``[~]``[*]``[x]``[X]` 순서로 본 § + branch `status_label` 동시 갱신.
4. **신규 영역 발견 시**: §31.1 Cluster Branches list + §25 SSOT Owner Map + 본 § 에 row 추가 후 branch 신설 (§34 Stack Commitment 정합 의무).
5. **gap 식별**: 정기적 (주 1회 권장) 본 § scan 으로 *`[ ]` 가 많은 영역* / *`(없음)` 항목* 검토 → 코딩 우선순위 조정.
6. **상태 grade 의 evidence 근거**: `[*]`/`[x]`/`[X]` 마킹 시 branch 의 §완료 후 정리 / Closure 섹션에 해당 grade 의 *실제 코드 reference* (PR / commit / test 파일 경로) 명시 의무.
### 현재 우선순위 (진행 가능 순서)
**현재 분포 요약** (46 ca-skeleton branches + 9 신규 권고 + 11 미래 후보):
- `[x]` 3개 (locally-verified 이상): `feature-skeleton-package-blueprint-contract`, `feature-architecture-enforcement-rules`, `feature-application-port-usecase-contract`
- `[*]` 4개 (partial implementation): `feature-boundary-validation-mapping-contract`, `feature-domain-feature-onboarding-contract`, `feature-sample-removal-adoption-contract`, `feature-implementation-readiness-scorecard`
- `[~]` 37개 (결정 박힘, 코드 없음): 대부분 — D-row 평균 9~12개
- `[ ]` 2개 (scaffolding only): `feature-webhook-outbound-contract`, `feature-streaming-response-contract`
- `(없음)` 8개 (신규 branch 권고, E 영역): template-instantiation / static-analysis / dependency-vulnerability / database-connection-pool / authn-authz / runtime-context / persistence-auditing / distributed-lock — (query-bypass 는 2026-06-05 scaffold+구현 완료로 제외, `[x]` locally-verified 로 승급)
- `(없음)` 11개 (미래 후보, F 영역): backup-restore / performance-load / docs-publishing / local-dev-data / clock-injection / i18n / money-decimal / search / notification / saga / soft-delete
**작업 흐름 권고**:
1. **`[~]``[*]` drain** — 코드 작성 단계로 진입. 우선순위:
- **(1순위) `feature-resource-identifier-contract`** — §구현 가이드 §1~§8 코드 skeleton ready, 즉시 코드 작성 가능
- **(2순위) `feature-api-contract-baseline`** — D=24 결정 모두 박힘. ArchUnit rule 작성 + envelope serializer
- **(3순위) `feature-operational-error-observability-foundation`** — error registry yaml SSOT (다수 branch 의 dependency)
- **(4순위) `feature-security-operational-baseline`** — CORS / SecureRandom / API key — security baseline
2. **`(없음)` E 영역 신규 branch scaffolding** — *코드 작성 *전에* 박아야 할 architecture-blocking 결정*:
- **template-instantiation** (priority 1) — project clone 시점에 즉시 필요
- **static-analysis-quality** (priority 2) — CI 셋업 시점
- **dependency-vulnerability** (priority 3) — CI 셋업 시점
- **database-connection-pool** (priority 4) — persistence 코드 작성 시점
- **authn-authz** (priority 5) — 도메인 사용자 인증 요구 시점
- 나머지 3개 (runtime-context, persistence-auditing, distributed-lock) 는 *코드 작성 중 부딪힐 때* scaffolding (query-bypass 는 2026-06-05 scaffold+구현 완료)
3. **`[ ]``[~]` 결정 drain** — scaffolding 2개 (webhook / streaming) 의 1차 결정 박기. 도메인 요구 등장 전까지 *미지원 default + ArchUnit 차단* 권고.
4. **`[*]``[x]` 승급** — 4개 partial 의 `planned` 잔존 row drain. 특히 boundary branch 의 ArchUnit rule 추가 작성.
5. **`[x]``[X]` 승급** — 3개 locally-verified 의 prod-deploy 후 운영 검증 (실제 서비스 배포 후).
6. **F 영역 미래 후보** — 운영 / 도메인 요구 등장 시점에 신설. skeleton 1차 범위 *밖*.
**Branch 작업 시점 의무**:
- 본 § 의 해당 row 상태 갱신
- 해당 branch 의 `status_label` frontmatter 갱신
- §완료 후 정리 / Closure 섹션에 evidence reference (PR / commit / test 경로) 추가
- §31.1 Cluster Branches list 의 description 갱신 (필요시)
- 신규 branch 신설 시 §31.1 + §25 SSOT Owner Map + 본 § row 동시 추가 (§34 Stack Commitment 정합 의무)
### Branch 작성 가이드 — 학습된 실패 모드 (4가지)
2026-06-01 [[raw/branch-notes/feature-resource-identifier-contract]] 4개 의문점 (sealed permits 모듈 경계 / D5 본문 vs §1/§2 와이어링 / D13 tenant 모델 부재 / D17 + §6 위임 vs 작성) 의 root cause 분석에서 식별된 *반복 발생 가능* 실패 모드. branch 작성 / 리뷰 시 본 § 항목별 self-check 권고.
| Failure mode | 발생 영역 | 위반된 룰 | Self-check 질문 |
|---|---|---|---|
| **F1. Cross-branch SSOT 미확인** | branch 가 *다른 branch 결정 영역* (예: 모듈 경계, ArchUnit suite 소유) 을 자기 §구현 가이드에 결정 | CLAUDE.md §11 *"본 branch 결정 범위 밖 cell 작성 금지"* + §15.5 **R3 OUT_OF_BRANCH_SCOPE** | 본 §구현 가이드 의 각 cell 이 sibling branch 의 결정 영역 (특히 `feature-skeleton-package-blueprint-contract` 의 모듈 경계, `feature-boundary-validation-mapping-contract` 의 ArchUnit suite, `feature-tenant-context-policy` 의 tenant 모델) 을 침범하지 않는가? |
| **F2. 본문 결정 ↔ §구현 가이드 self-inconsistency** | D-row 결정과 §구현 가이드 코드가 *서로 다른 패턴* 채택 (예: D5 본문 = static factory, §1/§2 = port + DI). **D-row 끼리도 self-inconsistency** (예: D2 charset = Crockford base32, I/L/O/U 제외 → 그러나 D19 fixture 값 `01HRGC7K2N4F6P8Q0R2S4T6U8V``U` 포함 → 자기 regex 통과 불가, 2026-06-01 self-catch) | §15.5 R1~R3 의 *암묵적 가정**근거 → 결정* 만 검사, *결정 → 구현* 미검사, *결정 → 결정* 도 미검사. wiki-workflow STOP self-check 12 도 동일 한계 | 각 D-row 결정의 *기술 선택* 이 §구현 가이드 코드 예시와 1:1 매칭되는가? (메커니즘 / 호출자 / 의존 방향 모두) **그리고 D-row 간 cross-reference 가 자기 자신의 charset/regex/format 통과하는가?** (예: charset 결정의 alphabet 이 fixture 값을 actually 통과) |
| **F3. 실제 코드 cross-check 부재** | spec 이 현재 코드에 *존재하지 않는* 의존성 (예: tenant 컬럼, ArchUnit rule 의 검사 대상 패키지) 을 가정 | 명시적 룰 없음 — *코드 cross-check 게이트 부재* | 본 branch §구현 가이드 의 *전제 사실* (테이블 / 컬럼 / 모듈 / 패키지) 이 `/home/donghyeon/workspace/ca-tmpl` 의 실제 코드에 존재하는가? 없으면 *마이그레이션 대상* 임을 본문에 명시했는가? |
| **F4. 위임 / 작성 모호** | branch 가 *결정 SSOT* 임을 명시했으나 §구현 가이드에 실제 작성 코드 잔존 (R3 부분 적용) | §15.5 R3 의 *부분 적용* | §구현 가이드 의 코드 skeleton 이 *reference (실제 호스팅 = sibling)* 인지 *실제 작성 (본 branch host)* 인지 본문에 명시했는가? reference 라면 sibling cite 와 *코드 위치 = sibling* 한 줄 추가했는가? |
위 4개 self-check 는 `/lint` 가 자동 catch 하지 못하는 정성적 영역 — branch 작성 / Sources 추가 / Decision 추가 / §구현 가이드 작성 시점에 *명시적으로* 검토.
장기적으로 `/lint` 검사 항목 (§15.5 *예정* 목록) 에 다음 4가지 추가 권고:
1. F1 — `..domain..` / `..adapter..` 등 package glob 이 sibling branch SSOT 모듈 경계 위반 여부 정적 grep
2. F2 — `## 결정 사항` 의 각 D-row 본문이 `## 구현 가이드` 의 §N 코드에서 referenced 됐는지 (`Trace: D<N>` 헤더 grep)
3. F3 — `## 구현 가이드` 의 코드 예시에 등장하는 클래스명 / 테이블명 / 패키지명이 실제 코드에 grep hit
4. F4 — `## 구현 가이드` 의 코드 skeleton 첫 줄에 `REFERENCE ONLY` 또는 `actual location: ` 라벨 grep — 미명시 시 본 branch 호스팅으로 간주
## 34. Stack Commitment
> **Legacy detail/reference.** stack 선택의 stable owner는 `## 6.1 Project Decision Registry / 안정 결정 레지스트리`의 `STACK-*` rows다. 아래 matrix는 버전·trade-off 설명을 보존한다.
ca-skeleton 은 *단일 stack 커밋* 을 채택합니다. 다중 DB / 다중 언어 / 다중 빌드 도구 가정은 모든 branch 의 결정 부담을 *theoretical UNSUPPORTED_IMPL_DECISION* 으로 부풀려 minimalist 정신과 충돌합니다. 본 § 가 stack SSOT — 모든 branch 는 본 § 를 *상속* 하며 stack 관련 결정을 자기 branch 에서 재선언하지 않습니다.
### Stack Matrix
| Layer | 선택 | 버전 | 비고 |
|---|---|---|---|
| Language | Java | 21 LTS | `java.util.UUID` v7 native 미지원 — ULID 선택 근거 (resource-identifier branch D1) |
| Framework | Spring Boot | 3.5.14 | starter web / data-jpa / validation 사용 |
| ORM | Hibernate ORM | 6.5.x (Spring Boot transitive) | `@JdbcTypeCode(SqlTypes.UUID)` native UUID |
| JSON | Jackson | 2.18.x (Spring Boot transitive) | custom serializer for value objects |
| DB | PostgreSQL | 16 | `uuid` native column type (16-byte binary). MySQL / Oracle / SQL Server *out of scope* |
| Migration | Flyway | (Spring Boot transitive) | startup runner, readiness gate (§25 default) |
| Build tool | Gradle | Groovy DSL | multi-module + `apply false` 패턴. `io.spring.dependency-management` 1.1.6 |
| Test framework | JUnit | 5 | Spring Boot starter 기본 |
| Architecture test | archunit-junit5 | 1.3.0 | D17 ArchUnit rule suite (resource-identifier branch + boundary branch) |
| Random source | `java.security.SecureRandom` | Java 21 | ULID generator + idempotency key + token generation 의 의무 random source |
### Cross-Branch 상속 패턴
각 branch 의 §결정 사항 / §Decision Evidence Map / §구현 가이드 가 stack 관련 결정 시 본 § 를 *reference* 만 하고 *재선언하지 않음*. 예시:
```text
✗ 잘못된 패턴 (재선언):
D10: DB primary key = BINARY(16) (MySQL InnoDB) + uuid native (PostgreSQL)
→ 다중 DB 가정 = theoretical UNSUPPORTED_IMPL_DECISION 발생
✓ 올바른 패턴 (상속):
D10: DB primary key = PostgreSQL 16 uuid native (project §34 Stack Commitment)
→ 단일 stack, 결정 명확, 미래 stack 변경 시 §34 한 곳만 갱신
```
### Stack 변경 절차
본 § 의 stack 변경은 *모든 branch 에 cascade* 됩니다. 변경 시:
1. 본 § Stack Matrix 갱신 (변경 row + 변경 사유 한 줄)
2. 영향 받는 sibling branch 식별 — `grep -rn "project §34" raw/branch-notes/feature-*.md`
3. 각 sibling branch 의 §Decision Evidence Map 의 `project-ssot` (§34) reference 영향 평가
4. 영향 큰 결정 (D1 ULID 같은 foundational) 은 branch 의 §결정 사항 재평가 + UNSUPPORTED_IMPL_DECISION 재평가
### Out of Stack (명시적 거부)
본 stack commit 은 다음 *대안* 들을 명시적으로 거부:
- **DB**: MySQL / Oracle / MariaDB / SQL Server — PostgreSQL 16 단일
- **Language**: Kotlin / Scala / Groovy (응용 코드) — Java 21 단일 (Gradle Groovy DSL 은 빌드 도구 한정)
- **Framework**: Micronaut / Quarkus / Helidon — Spring Boot 3.5.14 단일
- **Build tool**: Maven / Bazel — Gradle Groovy DSL 단일
- **Test framework**: TestNG / Spock — JUnit 5 단일
도메인이 위 alternative 를 요구할 경우 본 § 를 갱신 (cascade) 또는 별도 project-fork.