From 7c4e8df24826f637abe7bcbc3774d2c5afcf806f Mon Sep 17 00:00:00 2001 From: DongHyeonka Date: Tue, 28 Jul 2026 15:09:52 +0900 Subject: [PATCH] =?UTF-8?q?docs:=20ca-skeleton=20production=20capability?= =?UTF-8?q?=20feature=20=EB=B6=84=ED=95=B4=20=EC=84=A4=EA=B3=84=20+=20stac?= =?UTF-8?q?k=20=EB=93=9C=EB=A6=AC=ED=94=84=ED=8A=B8=20=EC=A0=95=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ca-tmpl 의 production capability platform 설계(platform 1,346줄 + redis 6,771 + httpclient 7,030 + fileserver 2,467)를 llm-wiki 거버넌스로 편입하는 분해 설계. 2-tier(기술 런타임 / 능력 계약) 모델로 신규 branch 18개를 확정하고, 기존 branch 8건의 주도권을 신규 branch 로 이전하는 절차를 고정. hub 드리프트 3건 중 2건을 코드 대조로 정정: - Spring Boot 3.5.14 → 4.0.0 (src/build.gradle:8), DEC-…-STACK-FRAMEWORK-001 revision 1 → 2 + WI 9행 pin 갱신 - ca-tmpl 경로를 desktop-server-git/clean-architecture-backend-template 로 정정 Co-Authored-By: Claude Opus 5 (1M context) --- ...capability-feature-decomposition-design.md | 566 ++++++++++++++++++ .../ca-skeleton-operational-contract.md | 30 +- 2 files changed, 581 insertions(+), 15 deletions(-) create mode 100644 docs/superpowers/specs/2026-07-28-ca-skeleton-production-capability-feature-decomposition-design.md diff --git a/docs/superpowers/specs/2026-07-28-ca-skeleton-production-capability-feature-decomposition-design.md b/docs/superpowers/specs/2026-07-28-ca-skeleton-production-capability-feature-decomposition-design.md new file mode 100644 index 0000000..1d71193 --- /dev/null +++ b/docs/superpowers/specs/2026-07-28-ca-skeleton-production-capability-feature-decomposition-design.md @@ -0,0 +1,566 @@ +# CA Skeleton — Production Capability feature 분해 설계 + +**일자 / Date:** 2026-07-28 +**대상 문서 / Target:** `raw/project-notes/ca-skeleton-operational-contract.md` +**대상 코드 / Code:** `/home/donghyeon/workspace/desktop-server-git/clean-architecture-backend-template` (이하 **ca-tmpl**, commit `b3add01`) +**범위 / Scope:** ca-tmpl 의 production capability platform 설계를 llm-wiki 거버넌스(project note + branch note)로 편입. 신규 branch 18개 + 기존 branch 8건 주도권 이전 + hub §별 델타. +**요청 언어 / User language:** ko +**상태 / Status:** 설계 승인 완료 (2026-07-28), 구현 계획 미작성 + +--- + +## 0. 이 문서의 지위 + +이 문서는 `raw/project-notes/ca-skeleton-operational-contract.md`(이하 **hub**)에 무엇을 어떻게 추가·정정할지 고정한 설계 스펙이다. hub 본문의 대체물이 아니며, 여기서 확정한 ID·slug·문자열·수치를 hub 와 신규 branch-note 가 그대로 옮겨 담는다. + +이 문서 자체는 구현 증거가 아니다. 여기 적힌 모든 계약·기본값·경로는 CLAUDE.md §6 기준 `planned` 또는 `documented-only` 다. 예외는 **§13 드리프트 정정**뿐이며, 그 절의 항목은 ca-tmpl 코드를 직접 읽어 확인한 `actually-implemented` 사실이다(각 행에 파일 경로 명시). + +--- + +## 1. 배경과 조사 결과 + +### 1.1 사용자 요청 + +요청의 핵심은 두 가지다. + +1. **adapter 가 운영 환경에 바로 못 쓴다.** redis, fileserver, httpclient, messaging, notification, objectstorage, jpa, mongo, graphql, grpc, web, websocket 이 seam 수준에 머물러 있다. +2. **cross-cutting 이 PostgreSQL 에 묶여 있다.** outbox·idempotency·lock·rate limit·cache 가 DB 한 곳으로만 해결되게 설계되어 있다. outbox 는 CDC 가 필요할 수 있고, lock 은 Redis 가 필요할 수 있으며, rate limit 은 정책에 따라 알고리즘을 갈아끼울 수 있어야 한다. + +여기에 **교육적 요구**가 붙는다 — "자주 쓰는 함수를 기본 제공해서, 그걸 쓰다가 부딪히는 문제를 개발자가 알 수 있게" 하는 것. 사용자가 든 예가 정확히 그 성격이다: *Redis 는 싱글스레드인데 왜 동시성 문제가 생기는가*, 그리고 그 답으로서의 Lua. + +명시적 비요구: 대기업 기술 블로그식 "이런 상황에서 이만큼 개선했다" 는 수치 서사는 만들지 않는다. + +### 1.2 조사에서 드러난 사실 — 설계는 이미 있다 + +ca-tmpl 저장소에 요청 내용을 이미 판정한 설계가 존재한다. + +| 문서 | 줄 수 | 다루는 범위 | +|---|---:|---| +| `docs/superpowers/specs/2026-07-26-production-capability-platform-design.md` | 1,346 | capability platform 전체 — provider 선택, readiness R0~R3, Redis role, 실패정책, outbox/CDC, Kafka, Mongo, HTTP, notification, object storage, file server, web/GraphQL/gRPC/WebSocket, 관측, Gradle, CI, 6-phase 로드맵 | +| `.../2026-07-26-redis-production-capability-design.md` | 6,771 | Redis 심화 — role/deployment isolation, key model, payload/codec, atomic program registry, command·transaction·pipeline 의미, cache contract, rate limit, idempotency, lease/fencing, session, 관측, topology CI | +| `.../2026-07-27-httpclient-production-capability-design.md` | 7,030 | HTTP client 심화 — total deadline, retry/circuit breaker 순서, pool, DNS, SSRF, TLS/mTLS, 관측 | +| `.../2026-07-26-fileserver-production-capability-design.md` | 2,467 | file server 심화 — staged publish, guarantee 모델, provider 3종, CSV safety, quota/retention | + +구현도 진행 중이다. commit `b3add01` "feat: redis, fileserver, httpclient 런타임 시점 구현 추가" 기준으로 `adapter/outbound/cache-redis` 43개 파일(Lua program executor·catalog·descriptor, atomic primitives, key builder/namespace/digest, command admission, envelope codec 포함), `httpclient` 38개, `fileserver` 16개가 존재한다. + +### 1.3 그런데 llm-wiki 에는 이 작업이 없다 + +hub 를 grep 한 결과: + +- §8.0 Work Item Registry 60행(`WI-…-001`~`059`) 중 capability platform 관련 행 **0개** +- §6.1 안정 결정 레지스트리 25행 중 관련 결정 **0개** +- §35 Implementation Coverage Checklist 에 redis/httpclient/fileserver 는 각각 `cache-consistency`·`outbound-http-client-baseline`·`file-resource-handling` 행으로 **`[~]`(결정 박힘, 코드 없음)** 로 표기 — 실제로는 코드가 들어갔다 +- mongo / graphql / grpc / websocket / session-store / CDC / Kafka consumer 는 **row 자체가 없음** + +즉 **코드가 문서를 앞질렀고, 그 사이 간극이 hub 의 거버넌스 밖에 있다.** 이 설계의 목적은 그 간극을 branch-note 단위 소유권으로 덮는 것이다. + +### 1.4 이 확장이 없으면 발생하는 실패 + +계약은 아래 실패를 막기 위해 존재한다(hub §26 Universal Acceptance Gate 질문 1). + +| 실패 | 설명 | +|---|---| +| 거짓 R2 표기 | seam 하나만 있는 capability 를 문서·startup 진단이 "production ready" 로 보고한다. 운영자가 없는 보증을 믿는다. | +| 보증 등급 혼동 | Redis idempotency claim + JDBC 업무 write 를 `SAME_STORE_TRANSACTIONAL` 로 오인해 crash 후 중복 실행이 발생한다. | +| CDC 전환 불가 | 가변 status row outbox 에 Debezium Outbox Event Router 를 붙일 수 없다는 사실을 전환 시점에 발견한다. | +| 알고리즘 고착 | rate limit 이 fixed window 하나뿐이라 burst 허용·평균율 제어가 필요한 정책을 표현할 수 없다. | +| 조용한 경합 | `GET → 판단 → INCR → EXPIRE` 를 원자적이라 믿고 작성해 카운터가 어긋나거나 TTL 이 유실된다. Redis 가 싱글스레드라는 사실이 이 오해를 강화한다. | +| 캐시가 결함을 삼킴 | codec·프로그래밍 결함이 blanket `catch(Exception)` fail-open 을 통과해 평범한 cache miss 로 위장된다. | +| 세션 부재로 다중 파드 불가 | JWT `STATELESS` 고정이라 세션 기반 인증이 필요한 순간 다중 파드에서 인증 상태를 공유할 수 없다. | +| 비고정 fencing | lease 기반 lock 을 correctness 보증으로 오용해 failover 후 stale holder 가 write 한다. | +| 전체 객체 메모리 적재 | object storage 가 `byte[]` 전용이라 대용량 업로드/다운로드가 힙을 그대로 소비한다. | +| 미검증 활성화 | bean 이름 존재만으로 multi-instance 안전을 보고해, 실제 분산 구현 없이 안전하다고 판정한다. | + +--- + +## 2. 확정된 설계 선택 (사용자 승인 2026-07-28) + +| 선택 | 결정 | 근거 | +|---|---|---| +| 활성화 자세 | **optional Gradle 모듈 + capability flag default OFF, 모듈 내부는 완제품** | core 는 지금처럼 가볍고, 안 쓰는 프로젝트는 의존성·기동시간 부담 0. frontend hub 의 `FE-D026`(capability default OFF)과 같은 자세 | +| 분해 단위 | **2-tier(기술 런타임 / 능력 계약) + 신규 branch 18개** | Redis 심화 설계 6,771줄이 7개 관심사에 걸쳐 있어 branch 1개로 불가. 기술×능력으로 쪼개면 연결·키·코덱 결정이 5곳에 중복 | +| Stack | **core stack 유지 + Optional Adapter Stack Matrix 신설** | §34 의 "단일 stack 커밋" 정신을 각 optional 축에서도 유지(축마다 구현체 1개) | +| 제공 깊이 | **port + adapter + 연산 툴킷 + 함정 게이트** | 사용자 요구의 교육적 성격을 계약으로 고정. unsafe recipe 와 atomic 대체를 문서·테스트에서 쌍으로 제시 | +| 기존 branch 충돌 | **주도권을 신규 branch 로 이전하고 동기화** | 사용자 지시(2026-07-28). Single-Owner 유지, 기존 branch 는 포인터만 보존 | + +--- + +## 3. 범위 + +### 3.1 In scope + +- 신규 branch 18개의 **소유 관심사 · Work Item · 결정 · 커버리지 행** 확정 +- 기존 branch 8건의 **주도권 이전(handover)** 대상 D-row 와 동기화 절차 확정 +- hub §1·§2·§11·§25·§34·§35·§6.1·§8.0·§24·§31.1·§29 의 델타 확정 +- **Capability Readiness Model(R0~R3)** 과 capability card 필수 항목의 hub 편입 +- **능력별 실패정책 매트릭스**(hub §11 Optional Adapters 절 대체) +- ca-tmpl 코드 대조로 확인된 **드리프트 3건 정정** + +### 3.2 Out of scope + +**사용자 제약을 문서에 못박는 항목이다.** hub §2 "하지 않는 것" 에 추가한다. + +- **각 branch 의 심층 명세** — 본 문서는 소유권·경계·hub 델타까지다. 각 branch 의 D-row 와 §구현 가이드는 `/branch-spec` 이 별도 세션에서 채운다. +- **성능 수치·개선 서사** — "무엇에서 무엇으로 개선" 형태의 측정 주장은 만들지 않는다. capacity 입력(키 크기, 연산율, 보존기간, 재시도 증폭)만 계약한다. +- **도메인 로직** — 로그인 플로우, 결제 흐름, 재고 차감 같은 도메인 형태 레시피는 만들지 않는다. capability pack 은 안전한 저장·전송·조율을 제공하고 use case 는 적용 프로젝트가 쓴다. +- **vendor 선택** — 관리형 Redis/Kafka/Mongo 제공자, CDN, push 서비스, KMS 제공자. +- **exactly-once 주장** — DB 와 broker 를 걸친 exactly-once, Redis lock 의 strong correctness 는 코드·문서 어디에도 쓰지 않는다. +- **ca-tmpl 코드 작성** — 본 문서는 llm-wiki 거버넌스 산출물이다. 코드는 ca-tmpl repo 의 plan 이 소유한다. + +### 3.3 Non-goal + +- 신규 capability 를 **기본 활성화**하는 것. 기본값은 전부 비활성이며 `feature-capability-provider-selection-contract` 의 gate 가 이를 증명한다. +- 기존 60개 Work Item 의 의미 변경. §5 의 8건만 주도권이 이동하고 나머지는 그대로다. +- llm-wiki 를 ca-tmpl 심층 설계의 사본으로 만드는 것. branch-note 는 **결정과 근거**를 소유하고, 구현 세부는 ca-tmpl 설계를 reference 한다. + +--- + +## 4. 분해 — 2-tier 모델 + +### 4.1 왜 2-tier 인가 + +기술 1개 = branch 1개로 두면 Redis 가 무너진다. Redis 는 cache·rate limit·session·idempotency·lock 5개 능력과 role·key·codec·script 4개 기반이 한 기술 안에 겹쳐 있다. + +반대로 능력×기술로 쪼개면(`cache-redis`, `lock-redis`, `session-redis`…) **연결 설정·키 스키마·직렬화·스크립트 카탈로그 결정이 5개 branch 에 중복**된다. `rules/consistency-contract.md` 의 Single-Owner 위반이고, Redis 클라이언트 하나 바꿀 때 5곳을 고쳐야 한다. + +그래서 축을 둘로 나눈다. + +```text +Tier T (기술 런타임) — 클라이언트 · 연결 · 키 · 코덱 · 스크립트 · health · 보안 + ↑ provider 로 사용됨 +Tier C (능력 계약) — provider-neutral port · 보증(guarantee) · 실패정책 · 전략 registry + ↑ 활성화 판정을 위임 +Tier P (플랫폼) — capability descriptor · provider 선택 · readiness · capability card +``` + +Redis 는 Tier T 2개 branch 가 소유하고, Tier C 5개 branch 에 **provider 로만 등장**한다. 중복 0. + +### 4.2 신규 branch 18개 + +| # | Tier | branch slug | 소유 관심사 | Work Item | +|---:|---|---|---|---| +| 1 | P | `feature-capability-provider-selection-contract` | capability descriptor, `provider: disabled\|` 활성화 SSOT, readiness R0~R3, capability card 필수 항목, fail-fast 토폴로지 검증, 능력별 실패정책 매트릭스 | `WI-…-060` | +| 2 | T | `feature-redis-runtime-role-isolation-contract` | cache/coordination/session 3-role 분리, 연결·TLS·ACL·pool·topology refresh, key model(namespace·digest·hash tag·version·bounds), payload envelope·codec·compatibility, role 별 health | `WI-…-061` | +| 3 | T | `feature-redis-atomic-program-catalog-contract` | Lua/Function 카탈로그, `ScriptDescriptor`, EVALSHA + NOSCRIPT 재적재, bounded execution 규칙, **unsafe multi-command recipe ↔ atomic 대체 쌍**, golden vector | `WI-…-062` | +| 4 | T | `feature-kafka-producer-runtime-contract` | acknowledgement-aware send, `acks=all` + idempotent producer, bounded delivery timeout, key/partition 순서, 압축·배치 한계, TLS/SASL, 스키마 serializer, graceful flush | `WI-…-063` | +| 5 | T | `feature-kafka-consumer-inbox-contract` | 신규 `adapter:inbound:messaging-kafka` leaf, manual ack, handler/schema allowlist, bounded concurrency·queue, pause/resume, rebalance·`max.poll`, retry topic·DLT, `InboxStorePort` | `WI-…-064` | +| 6 | T | `feature-mongo-runtime-baseline-contract` | URI/topology/TLS/credential/timeout/pool 설정, read preference·read/write concern, replica-set 트랜잭션 요구 검증, index manifest·drift·migration, change stream resume token·oplog window | `WI-…-065` | +| 7 | T | `feature-objectstorage-streaming-lifecycle-contract` | streaming upload/download·range read, head/metadata, checksum 계약, 조건부 write(ETag/version), presigned, multipart start/complete/abort·orphan 회수, SSE/KMS, staged finalization | `WI-…-066` | +| 8 | C | `feature-cache-strategy-registry-contract` | `CacheRegionPort` 결과 모델(HIT/MISS/NEGATIVE_HIT/DEGRADED), positive·negative·soft·hard TTL, jitter, single-flight, 전략 registry(cache-aside 기본 / SWR·refresh-ahead·probabilistic·L1L2·분산 stampede opt-in), 메모리 정책 **소유 경계**(maxmemory-policy = 배포 소유), 대량 무효화(SCAN+UNLINK) | `WI-…-067` | +| 9 | C | `feature-edge-rate-limit-policy-registry-contract` | transport-edge 계약(`RateLimitRequest`→`RateLimitDecision`), `policyId` registry, 알고리즘 6종 교체(fixed window / sliding counter / token bucket / sliding log / GCRA / leaky bucket), 키 차원(`environment+policyId+tenant?+subjectHash`), decision 기반 `Retry-After`, emergency fallback | `WI-…-068` | +| 10 | C | `feature-session-auth-mode-contract` | `jwt \| redis-session` 배타 프로파일, session 생성 정책·CSRF·쿠키 속성·session id 회전, 로그아웃 서버 세션 삭제, serializer 버전·허용 타입, 다중 파드 read/touch/expiry/logout 계약, rolling deploy 호환 | `WI-…-069` | +| 11 | C | `feature-idempotency-ownership-protocol-contract` | owner token 기반 `claim/renew/complete/release`, 실행 lease 와 replay TTL 분리, `ACQUIRED\|REPLAY\|IN_PROGRESS\|FINGERPRINT_MISMATCH` 결과, guarantee 3종(`REQUEST_REPLAY`/`SAME_STORE_TRANSACTIONAL`/`EXTERNAL_IDEMPOTENCY`), provider 공통 계약 suite | `WI-…-070` | +| 12 | C | `feature-fenced-lock-coordination-contract` | `FencedLockPort` + leader election·semaphore·work claim 분리, fencing counter 와 lease 분리, 보호 자원의 high watermark 거부 규칙, lease-lost 신호, blind `DEL`·무한 watchdog 금지 | `WI-…-071` | +| 13 | C | `feature-outbox-dispatch-mode-contract` | `outbox_event`(불변) + `outbox_delivery`(polling 전용 상태) 분리, `dispatch-mode: disabled\|polling\|cdc` 배타, aggregate sequence 순서, claim owner token, Debezium Outbox Event Router 매핑, 파티션 보존·정리 조건, 모드 전환 runbook | `WI-…-072` | +| 14 | X | `feature-graphql-production-baseline-contract` | operation·field authorization, parser 문자/토큰/깊이 한계, cost/complexity 계측, persisted query allowlist, DataLoader·N+1 계약, cursor connection, error extension 정제, introspection 정책, 스키마 breaking-change gate | `WI-…-073` | +| 15 | X | `feature-grpc-production-baseline-contract` | proto 생성·버전 규약·호환성 검사, TLS/mTLS, service/method authorization interceptor, 필수 deadline·cancellation 전파, 메시지·메타데이터 크기 한계, retry 허용 status, keepalive, streaming backpressure, health·drain, reflection 정책 | `WI-…-074` | +| 16 | X | `feature-websocket-cluster-delivery-contract` | 인증된 handshake·재인증, destination 별 subscribe/send authorization, origin·frame 한계, heartbeat·idle timeout, bounded executor/queue·drop 정책, sequence/resume, broker relay 또는 durable 이벤트 브리지, 도메인 이벤트 직결 금지 | `WI-…-075` | +| 17 | X | `feature-read-consistency-query-contract` | `ReadConsistency`(`STRONG`/`READ_YOUR_WRITES`/`BOUNDED_STALENESS`/`EVENTUAL`), opaque·서명·버전 cursor, page/sort/filter allowlist, primary/replica 라우팅 근거, projection checkpoint·lag, `TransactionPort.inRead()` 의 의미 고정 | `WI-…-076` | +| 18 | X | `feature-notification-durable-delivery-contract` | best-effort ↔ durable 인터페이스 분리, 논리 template ID·버전·locale, provider quota·bounded retry, dedupe·provider idempotency key, delivery receipt·bounce·suppression, PII-safe 로그·큐 암호화·보존 | `WI-…-077` | + +### 4.3 요청 항목과의 대응 + +| 사용자가 든 항목 | 흡수한 branch | +|---|---| +| redis (전반) | #2 #3 기반 + #8 #9 #10 #11 #12 가 provider 로 소비 | +| rate limit 알고리즘 교체 | #9 | +| Lua 로 동시성 해결 / 기본 제공 함수 | #3 (카탈로그·descriptor·unsafe↔atomic 쌍) + #8 #9 #11 #12 (각 능력의 원자 연산) | +| 캐시 전략·메모리 관리·maxmemory-policy·스탬피드 | #8 (+ 배포 소유 경계는 #2) | +| redis session store / JWT → session 전환 | #10 | +| outbox → CDC | #13 | +| lock → Redis·Kafka 등 DB 밖 | #12 (+ 기존 `feature-distributed-lock-contract` 는 efficiency mutex 유지) | +| idempotency → DB 밖 | #11 | +| messaging | #4 #5 | +| objectstorage | #7 | +| mongo | #6 | +| graphql / grpc / websocket | #14 #15 #16 | +| query (PostgreSQL 종속) | #17 | +| notification | #18 | +| observability | 각 branch 의 계약에 포함(bounded metric·span·health). 신규 branch 없음 — 기존 `feature-metrics-alerting-contract`·`feature-distributed-tracing-contract` 가 owner | +| fileserver / httpclient / jpa / web | **신규 branch 없음.** 기존 owner 개정 — §5.3 참조 | + +### 4.4 신규 branch 를 만들지 않는 4개 표면 + +이 판단은 설계의 일부이며 hub §25 SSOT Owner Map 에 근거를 남긴다. + +- **fileserver** — ca-tmpl 심층 설계 2,467줄이 `feature-file-resource-handling-contract` 의 범위와 정확히 겹친다. 신규 branch 는 `DUPLICATE_CONTRACT_OWNER` 다. 기존 branch 를 개정하고 심층 설계를 근거로 pin 한다. +- **httpclient** — 마찬가지로 `feature-outbound-http-client-baseline`(D=11)이 owner다. total deadline·decorator 순서 정정은 그 branch 의 D-row 개정이다. +- **jpa/pool** — pool sizing·timeout 계층은 `feature-database-connection-pool-contract`, 실패 분류는 `feature-persistence-failure-baseline` 이 owner다. replica 라우팅만 #17 로 이전한다. +- **web** — trusted proxy chain, request deadline, graceful drain, multipart 한계는 각각 `feature-security-operational-baseline`·`feature-runtime-health-lifecycle-contract`·`feature-boundary-validation-mapping-contract` 가 이미 소유한다. edge rate limit 만 #9 로 이전한다. + +--- + +## 5. 주도권 이전 (Ownership Handover) + +사용자 지시: **"기존 브랜치와 충돌하는 부분은 주도권을 새로 생성하는 브랜치로 넘기고 동기화."** + +### 5.1 이전 대상 8건 + +| # | 이전 관심사 | 기존 owner | 이전 대상 D-row | 신규 owner | +|---:|---|---|---|---| +| H1 | idempotency 저장소·프로토콜·TTL·fingerprint | `feature-rate-limit-idempotency-contract` | D2, D3, D6, D7, D8, D9 | #11 `feature-idempotency-ownership-protocol-contract` | +| H2 | rate limit 키·분산·알고리즘 | `feature-rate-limit-idempotency-contract` | D4, D5 | #9 `feature-edge-rate-limit-policy-registry-contract` | +| H3 | cache 전략(stampede·TTL·직렬화·negative·cluster) | `feature-cache-consistency-contract` | D3, D4, D5, D6, D7, D8, D9 | #8 `feature-cache-strategy-registry-contract` | +| H4 | cache 실패 등급(fail-open 경계) | `feature-cachestore-multi-backend-router` | D1, D2, D3 | #8 (실패 등급) + #2 (backend 연결) | +| H5 | correctness lock · fencing | `feature-distributed-lock-contract` | D6 | #12 `feature-fenced-lock-coordination-contract` | +| H6 | outbox 행 모델 · dispatch 전략 | `feature-domain-event-outbox-contract` | 행 상태 머신 관련 D-row 전량 | #13 `feature-outbox-dispatch-mode-contract` | +| H7 | session · 인증 모드 | `feature-security-operational-baseline` | session 관련 D-row | #10 `feature-session-auth-mode-contract` | +| H8 | multi-instance 활성화 검증 | `feature-background-job-async-contract` (+ `feature-runtime-health-lifecycle-contract` 의 `StartupSafetyValidator` 언급) | scheduler/outbox lock 활성화 판정 | #1 `feature-capability-provider-selection-contract` | + +> 2026-07-28 controller 메시지에서는 충돌 6건으로 보고했으나, 코드 대조 결과 H4(cache 실패 등급)와 H8(multi-instance 검증)이 추가되어 **8건**이다. + +### 5.2 이전 절차 (branch 당 6단계) + +`rules/consistency-contract.md` 의 Single-Owner + Reference-Only 를 그대로 적용한다. + +1. **신규 branch 가 owner 로 D-row 를 재작성한다.** 이전이 아니라 재판정이다 — 기존 결정을 그대로 복사하지 않고, ca-tmpl 심층 설계와 코드 사실을 근거로 다시 판단한다. +2. **기존 branch 의 해당 D-row 는 본문을 제거하고 포인터만 남긴다.** 형식: `| D | **superseded → [[raw/branch-notes/<신규 slug>]] D** — <1줄 요약> | … |`. 재진술 금지. +3. **기존 branch 의 §구현 가이드에서 이관 대상 cell 을 제거한다.** CLAUDE.md §15.5 R3 `OUT_OF_BRANCH_SCOPE`. 이관 history 는 해당 branch 의 §Audit & Findings 에 보존한다. +4. **hub §25 SSOT Owner Map 의 owner 열을 교체하고**, consumers/producers 열에 기존 branch 를 남긴다. +5. **기존 branch 의 `status_label` 을 갱신한다.** 관심사를 전량 이전한 branch(H1+H2 로 사실상 전부 이전되는 `feature-rate-limit-idempotency-contract`)는 `delegated` 로 표기하고, 부분 이전 branch 는 `in-progress` 를 유지한다. +6. **`/sync` 를 실행해 역참조를 전파한다.** `wiki_consistency_check.py` 가 owner 교체 후 남은 재진술을 잡는다. + +### 5.3 이전이 아니라 개정만 하는 3건 + +주도권은 그대로 두고 내용만 코드 사실에 맞춘다. + +| branch | 개정 사유 | 근거 | +|---|---|---| +| `feature-outbound-http-client-baseline` | `globalCallTimeout` 이 실행 중 호출을 취소하지 않고 다음 재시도 진입 여부만 판정한다. decorator 순서가 문서와 다르다 | ca-tmpl httpclient 심층 설계 §3, §15, §20 | +| `feature-file-resource-handling-contract` | 전체 파일 버퍼링 + 비원자 덮어쓰기. staged publish·fsync·rename 프로토콜로 개정 | ca-tmpl fileserver 심층 설계 §11, §12 | +| `feature-integration-adapter-templates` | "optional adapter 는 disabled env 가 기본" 을 capability descriptor 모델로 대체 | #1 이 활성화 SSOT 를 소유하게 되므로 template branch 는 소비자로 강등 | + +--- + +## 6. Capability Readiness Model (hub 신설 §) + +`#1` branch 가 owner다. hub 에 **§36 Capability Readiness Model** 로 신설한다(§35 뒤에 붙인다 — 중간 삽입은 기존 절 참조를 어긋나게 한다). + +### 6.1 Readiness 4단계 + +| 등급 | 의미 | 필요한 증거 | CLAUDE.md §6 등급 대응 | +|---|---|---|---| +| `R0` Contract | 타입·seam·placeholder 만 존재 | unit test + architecture boundary test | `planned` 또는 `documented-only` | +| `R1` Local | 단일 로컬 프로세스/로컬 서비스에서 동작 | 집중 통합 test + 한계 문서화 | `actually-implemented` (partial) | +| `R2` Production baseline | 실제 provider, 안전한 설정, 실패 의미, health, metric, 보안, graceful lifecycle | provider 계약 test + 실서비스 통합 + 동시성/실패 test + runbook | `locally-verified` | +| `R3` Scale/HA | cluster·failover·rolling upgrade 검증 | topology test + 호환 매트릭스 + 복구/용량 runbook | `prod-verified` | + +**금지:** R0 seam 을 R2 provider 로 표기하는 것. 문서·startup 진단·capability card 모두에 적용된다. 이 금지는 hub §2 "하지 않는 것" 에 추가한다. + +### 6.2 Capability card 필수 항목 + +각 R2 이상 capability 는 아래를 갖춘 card 를 branch-note 에 보유한다. + +```text +owner 모듈 · semantic port +provider ID 와 readiness 등급 +보증(guarantee)과 명시적 비보증(non-guarantee) +기본 실패 정책과 허용되는 override +필요한 토폴로지 · 영속/축출 정책 +설정 키와 secret +liveness/readiness 영향 +bounded cardinality metric 과 trace span +적합/부적합 지침 · 비용 모델 · 자원 한계 +흔한 unsafe recipe · 그것이 만드는 경합/실패 · 안전한 대체 +집중/통합/실패 test 명령 +rolling upgrade · 복구 노트 +runbook 링크 +``` + +**"흔한 unsafe recipe ↔ 안전한 대체" 항목이 사용자 요구의 교육적 부분을 계약으로 고정하는 장치다.** provider 는 card 가 주장하는 동시성·실패 의미를 증명하는 경합 test 를 통과해야 그 보증을 card 에 쓸 수 있다. + +### 6.3 활성화 SSOT + +```text +provider: disabled | # 선택 가능한 capability +dispatch-mode: disabled | polling | cdc # 모드 축이 있는 capability (outbox) +enabled: true | false # provider·mode 축이 없는 leaf 에만 +``` + +규범: + +- `matchIfMissing=true`, classpath 존재, local provider 기본값이 production capability 를 활성화해서는 **안 된다**. +- 미사용 capability 는 연결·백그라운드 워커·스키마·암묵적 런타임 동작을 만들지 **않는다**. +- legacy flag 를 한시적으로 유지하는 경우, SSOT 와의 불일치는 startup 오류다. +- 활성화 판정은 **bean 이름이 아니라 typed descriptor** 로 한다(§13 드리프트 D3 참조). + +--- + +## 7. 결정 레지스트리 신규 15행 + +hub 는 §25(legacy default)와 §6.1(`DEC-…`, 현행 SSOT)을 쌍으로 유지한다. 신규 결정은 §6.1 에 넣고, §25 Blocking Defaults 에는 활성화 자세 1행만 추가한다. + +Summary 셀은 소비 branch 의 상속 표와 **문자열이 정확히 일치**해야 한다. 아래가 SSOT 다. **조사 앞 공백 없음**(기존 §6.1 행 표기와 동일). + +| Decision ID | Rev | Domain | Decision Summary | Status | Evidence | +|---|---:|---|---|---|---| +| `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-CAPABILITY-SELECTION-001` | 1 | `capability-selection` | production capability는 available by default이고 inactive by default이며 활성화는 typed descriptor 기반 provider 선택으로만 이뤄진다 | `active` | ca-tmpl platform 설계 §7 | +| `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-CAPABILITY-READINESS-001` | 1 | `capability-readiness` | 모든 capability는 R0~R3 readiness 등급과 capability card를 가지며 R0 seam을 R2 provider로 표기하지 않는다 | `active` | ca-tmpl platform 설계 §6 | +| `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-REDIS-ROLE-001` | 1 | `redis-role` | Redis는 cache·coordination·session 3개 role로 분리하고 key prefix나 DB number를 격리 수단으로 쓰지 않는다 | `active` | ca-tmpl redis 설계 §10 | +| `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-REDIS-PROGRAM-001` | 1 | `redis-program` | 다단계 read-decide-write는 버전 있는 atomic program으로만 제공하고 각 helper는 unsafe recipe와 안전한 대체를 쌍으로 문서화하며 경합 test로 증명한다 | `active` | ca-tmpl redis 설계 §13, §14 | +| `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-CACHE-STRATEGY-001` | 1 | `cache-strategy` | cache 기본 전략은 cache-aside와 after-commit 무효화이고 SWR·refresh-ahead·L1L2·분산 stampede 억제는 opt-in이며 maxmemory-policy는 배포 설정이 소유한다 | `conditional-default` | ca-tmpl platform 설계 §8.4 | +| `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-CACHE-FAILURE-001` | 1 | `cache-failure` | optional cache의 backend 실패는 fail open이고 codec·프로그래밍 결함은 해당 연산을 fail closed로 처리하며 cache miss로 위장하지 않는다 | `active` | ca-tmpl platform 설계 §7 | +| `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-RATE-LIMIT-ALGORITHM-001` | 1 | `rate-limit-algorithm` | rate limit은 policyId 단위로 알고리즘을 선택하고 fixed window·sliding counter·token bucket을 기본 제공하며 sliding log·GCRA·leaky bucket은 opt-in이다 | `conditional-default` | ca-tmpl platform 설계 §8.5 | +| `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-SESSION-MODE-001` | 1 | `session-mode` | 인증 모드는 jwt와 redis-session이 배타이고 jwt가 default이며 redis-session은 전용 session role과 CSRF·쿠키·회전·다중 파드 계약을 동반한다 | `conditional-default` | ca-tmpl platform 설계 §8.6 | +| `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-IDEMPOTENCY-OWNERSHIP-001` | 1 | `idempotency-ownership` | idempotency는 owner token 기반 claim·renew·complete·release 프로토콜을 쓰고 실행 lease와 replay TTL을 분리하며 보증 등급을 명시한다 | `active` | ca-tmpl platform 설계 §9 | +| `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-LOCK-FENCING-001` | 1 | `lock-fencing` | DistributedLockPort는 efficiency mutex로 고정하고 correctness가 필요한 경로는 별도 FencedLockPort와 보호 자원의 high watermark 거부로만 보장한다 | `active` | ca-tmpl platform 설계 §10 | +| `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-OUTBOX-DISPATCH-001` | 1 | `outbox-dispatch` | outbox는 불변 event 행과 polling 전용 delivery 행을 분리하고 dispatch 모드는 polling과 cdc가 배타이며 append는 업무 write와 같은 트랜잭션에 남는다 | `active` | ca-tmpl platform 설계 §11.1, §11.2 | +| `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-DELIVERY-SEMANTICS-001` | 1 | `delivery-semantics` | end-to-end 메시징 보증은 at-least-once 전달과 멱등 consumer·inbox로 표현하고 DB와 broker를 걸친 exactly-once를 주장하지 않는다 | `active` | ca-tmpl platform 설계 §11.4 | +| `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-MONGO-BASELINE-001` | 1 | `mongo-baseline` | MongoDB는 read preference·read concern·write concern을 명시하고 트랜잭션과 change stream은 replica-set 요건 검증을 통과할 때만 활성화한다 | `conditional-default` | ca-tmpl platform 설계 §12.3 | +| `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-READ-CONSISTENCY-001` | 1 | `read-consistency` | 조회는 ReadConsistency를 명시하고 replica 라우팅은 그 정책과 요청 문맥으로 결정하며 TransactionPort.inRead는 replica 사용을 뜻하지 않는다 | `active` | ca-tmpl platform 설계 §12.1 | +| `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-STACK-OPTIONAL-ADAPTER-001` | 1 | `stack-optional-adapter` | optional adapter stack은 축마다 구현체 하나를 고정하고 core stack과 분리된 matrix로 관리한다 | `active` | §34 Optional Adapter Stack Matrix | + +Owner 열은 모두 `[[raw/project-notes/ca-skeleton-operational-contract]]` 다(§6.1 기존 모든 행과 동일). + +### 7.1 §25 Blocking Defaults 신규 1행 + +| 항목 | 기본 결정 | SSOT branch | 실패 조건 | +|---|---|---|---| +| capability 활성화 | production capability 는 모듈로 제공하되 기본 비활성. 활성화는 typed descriptor 기반 provider 선택으로만 | `feature-capability-provider-selection-contract` | classpath 존재·`matchIfMissing`·local provider 기본값으로 capability 가 활성화되거나, bean 이름 존재만으로 보증을 주장 | + +--- + +## 8. Work Item Registry 신규 18행 (hub §8.0) + +`Applies Decisions` 는 §7 의 신규 DEC 와 기존 DEC 를 섞어 pin 한다. `Dependencies` 는 Tier 순서(P → T → C → X)를 따른다. + +| Work Item ID | branch slug | 완료 조건 (측정가능) | Applies Decisions | Dependencies | +|---|---|---|---|---| +| `WI-…-060` | `feature-capability-provider-selection-contract` | capability descriptor·provider 선택·readiness 등급 검증 test 가 통과하고 bean 이름 기반 판정이 제거된다 | `CAPABILITY-SELECTION-001@1`, `CAPABILITY-READINESS-001@1` | - | +| `WI-…-061` | `feature-redis-runtime-role-isolation-contract` | 3-role 연결·키·코덱·health 계약 test 와 incompatible co-location 거부 test 가 통과한다 | `REDIS-ROLE-001@1`, `STACK-OPTIONAL-ADAPTER-001@1` | `060` | +| `WI-…-062` | `feature-redis-atomic-program-catalog-contract` | program descriptor·golden vector·NOSCRIPT 재적재 test 와 unsafe recipe 경합 재현 test 가 통과한다 | `REDIS-PROGRAM-001@1` | `061` | +| `WI-…-063` | `feature-kafka-producer-runtime-contract` | acknowledgement·delivery timeout·순서·보안 설정 계약 test 가 통과한다 | `DELIVERY-SEMANTICS-001@1`, `EVENT-BROKER-001@1` | `060` | +| `WI-…-064` | `feature-kafka-consumer-inbox-contract` | inbound leaf 등록·manual ack·rebalance·DLT·inbox 멱등 계약 test 가 통과한다 | `DELIVERY-SEMANTICS-001@1`, `IDEMPOTENCY-OWNERSHIP-001@1` | `063`, `070` | +| `WI-…-065` | `feature-mongo-runtime-baseline-contract` | concern·index manifest·replica-set 트랜잭션·change stream checkpoint test 가 통과한다 | `MONGO-BASELINE-001@1`, `STACK-OPTIONAL-ADAPTER-001@1` | `060` | +| `WI-…-066` | `feature-objectstorage-streaming-lifecycle-contract` | streaming·multipart·presigned·checksum·orphan 회수 계약 test 가 통과한다 | `CAPABILITY-READINESS-001@1`, `MODULE-LAYOUT-001@1` | `060` | +| `WI-…-067` | `feature-cache-strategy-registry-contract` | 전략 registry·TTL 4종·single-flight·실패 등급 분리 test 가 통과한다 | `CACHE-STRATEGY-001@1`, `CACHE-FAILURE-001@1` | `061` | +| `WI-…-068` | `feature-edge-rate-limit-policy-registry-contract` | 알고리즘 6종 교체·policy registry·decision 기반 Retry-After·fallback test 가 통과한다 | `RATE-LIMIT-ALGORITHM-001@1`, `RATE-LIMIT-001@1` | `062` | +| `WI-…-069` | `feature-session-auth-mode-contract` | 배타 인증 모드·CSRF·쿠키·회전·다중 파드 세션 계약 test 가 통과한다 | `SESSION-MODE-001@1` | `061` | +| `WI-…-070` | `feature-idempotency-ownership-protocol-contract` | owner token 프로토콜·lease 분리·보증 등급별 provider 공통 계약 test 가 통과한다 | `IDEMPOTENCY-OWNERSHIP-001@1`, `IDEMPOTENCY-001@1` | `060` | +| `WI-…-071` | `feature-fenced-lock-coordination-contract` | fencing token 단조성·high watermark 거부·lease-lost 신호 test 가 통과한다 | `LOCK-FENCING-001@1`, `SCHEDULER-LOCK-001@1` | `060` | +| `WI-…-072` | `feature-outbox-dispatch-mode-contract` | event/delivery 분리·모드 배타·CDC 라우팅·파티션 정리 조건 test 가 통과한다 | `OUTBOX-DISPATCH-001@1`, `DELIVERY-SEMANTICS-001@1` | `063` | +| `WI-…-073` | `feature-graphql-production-baseline-contract` | 깊이·복잡도·persisted query·field authorization·스키마 gate test 가 통과한다 | `CAPABILITY-READINESS-001@1`, `API-VERSIONING-001@1` | `060` | +| `WI-…-074` | `feature-grpc-production-baseline-contract` | proto 호환·TLS/mTLS·deadline·크기 한계·drain test 가 통과한다 | `CAPABILITY-READINESS-001@1`, `STACK-OPTIONAL-ADAPTER-001@1` | `060` | +| `WI-…-075` | `feature-websocket-cluster-delivery-contract` | handshake 인증·destination authorization·bounded queue·cluster 전달 test 가 통과한다 | `CAPABILITY-READINESS-001@1` | `060`, `063` | +| `WI-…-076` | `feature-read-consistency-query-contract` | ReadConsistency·cursor·replica 라우팅·projection lag 계약 test 가 통과한다 | `READ-CONSISTENCY-001@1`, `TRANSACTION-001@1` | `060` | +| `WI-…-077` | `feature-notification-durable-delivery-contract` | durable/best-effort 분리·template 버전·receipt·dedupe 계약 test 가 통과한다 | `CAPABILITY-READINESS-001@1`, `RESILIENCE-001@1` | `072` | + +> ID 는 문서 가독성을 위해 `WI-…-0NN` 으로 축약했다. hub 에 옮길 때 전체 형식 `WI-CA-SKELETON-OPERATIONAL-CONTRACT-0NN` 로 쓴다. `Applies Decisions` 의 `DEC-…` 접두사도 마찬가지다. `Status` 는 전 행 `planned`. + +--- + +## 9. 능력별 실패 정책 매트릭스 (hub §11 대체) + +hub §11 의 `### Optional Adapters` 절(3줄)을 아래 표로 대체한다. 전역 fail-open 이나 전역 fail-closed 는 없다. + +| Capability | 기본 실패 정책 | 근거 | +|---|---|---| +| optional cache (backend 실패) | fail open — source 로 fallback, degraded 결과와 metric 노출 | 캐시 부재는 정확성 손실이 아니다 | +| cache codec·프로그래밍 결함 | fail closed (해당 연산) — 손상 항목 축출/격리, miss 로 위장 금지 | 결함을 miss 로 숨기면 영구 오염 | +| security session store | fail closed — 인증 상태를 지어내지 않는다 | 인증은 추측 대상이 아니다 | +| keyed mutation idempotency | fail closed | 중복 write 방지가 목적 | +| strict rate limit (남용·비용 경계) | fail closed 또는 의도적으로 제한된 local emergency limiter | 무제한 통과는 경계의 부재 | +| availability 지향 rate limit | 명시적 local emergency fallback, 조용한 무제한 통과 금지 | 저하는 허용, 은폐는 불가 | +| efficiency lock | 선언된 use case 정책에 따라 실패 또는 계속 | 중복 *작업* 방지가 목적 | +| fenced correctness lock | 획득·lease 상실 시 보호 작업 중단 | 중복 *결과* 차단이 목적 | +| outbox append | 업무 트랜잭션 롤백 | append 는 업무 write 와 원자 | +| outbox dispatcher 장애 | write 는 계속 축적, lag·backlog 알림 | 전달 지연은 업무 차단 사유가 아니다 | +| best-effort notification·message | 명시적 fail open | 이름과 계약에 best-effort 명시 | +| durable notification·message | outbox/inbox + 재시도 + 종단 실패 경로 | 유실 불가 경로 | + +hub §7 Non-Retryable 기준의 `cache unavailable when degradation is allowed` 행은 이 표의 첫 두 행으로 세분화된다. + +--- + +## 10. Optional Adapter Stack Matrix (hub §34) + +§34 의 core Stack Matrix 는 **그대로 유지**하고(단 §13 D1 정정 적용), 그 아래에 새 표를 붙인다. "축마다 구현체 하나" 라는 §34 의 단일 stack 정신을 optional 축에도 적용한다. + +| Capability 축 | 선택 | 버전 | 상태 | 비고 | +|---|---|---|---|---| +| Redis client | Lettuce (Spring Data Redis) | Spring Boot BOM transitive | 도입 예정 | `LettuceRedisRuntime` 이 이미 존재하나 SDK 의존성 추가는 Phase 1 | +| Session store | Spring Session Data Redis | Spring Boot BOM transitive | 도입 예정 | `redis-session` 모드 활성 시에만 | +| Kafka client | spring-kafka | Spring Boot BOM transitive | 도입 예정 | producer 먼저, consumer 는 신규 inbound leaf | +| CDC | Debezium + Kafka Connect | 배포 자산 | 도입 예정 | 애플리케이션 의존성 아님. connector 는 외부 배포 | +| MongoDB | Spring Data MongoDB | Spring Boot BOM transitive | opt-in 설정만 존재 | replica-set 요건 검증 필요 | +| Object storage | AWS SDK v2 | `2.30.0` (`ext.awsSdkVersion`) | **존재** | 모듈 scope BOM import | +| gRPC | io.grpc + protobuf | `1.68.1` (`ext.grpcVersion`) | **존재** | Boot BOM 미관리 → 모듈 scope BOM | +| GraphQL | Spring for GraphQL | Spring Boot BOM transitive | 최소 구현 존재 | production gate 미도입 | +| Resilience | Resilience4j | Spring Boot BOM 외 | **존재** | core 결정(`RESILIENCE-001`) 유지 | +| 통합 test | Testcontainers (+ Toxiproxy) | test scope | 부분 존재 | `TESTCONTAINERS-001` 정책 상속 | + +**Out of Stack (optional 축)** — 명시적 거부: + +- **lock/coordination**: Redisson 을 lock provider 기본으로 삼지 않는다. Spring Integration `LockRegistry` 계열(JDBC/Redis)이 기본이며 Redisson 은 대안. ZooKeeper·etcd·Consul 은 범위 밖. +- **messaging**: RabbitMQ·Pulsar·ActiveMQ 는 범위 밖. Redis Streams 는 Kafka 대체재가 아니라 후속 소규모 provider 후보. +- **cache**: Ehcache·Hazelcast·Infinispan 은 범위 밖. L1 은 Caffeine. +- **search**: Elasticsearch·OpenSearch 는 범위 밖(hub §35 F 영역 유지). + +§34 의 "Stack 변경 절차" 4단계는 optional matrix 에도 그대로 적용된다. + +--- + +## 11. hub 델타 요약 + +| § | 변경 | 성격 | +|---|---|---| +| §1 목표 | 마지막 문단에 capability platform 자세 1문단 추가 | 개정 | +| §2 하지 않는 것 | (a) "Kafka/Redis/Slack/Google Email을 기본 dependency로 무겁게 탑재하지 않음" → "available by default, inactive by default. 미사용 capability 는 연결·워커·스키마·런타임 부작용 0" 로 개정 (b) "R0 seam 을 R2 로 표기" 금지 1행 추가 (c) §3.2 Out of scope 6항 추가 | **개정** | +| §7 | `cache unavailable` 행을 §9 매트릭스로 위임하는 포인터 추가 | 개정 | +| §11 | `### Optional Adapters` 3줄 → §9 실패정책 매트릭스로 대체 | **대체** | +| §25 Blocking Defaults | 1행 추가(§7.1) | 추가 | +| §25 SSOT Owner Map | 신규 18행 + 이전 8건 owner 교체 | 추가·개정 | +| §6.1 | DEC 신규 15행(§7) | 추가 | +| §8.0 | WI 신규 18행(§8) + 주도권 이전에 따른 기존 7행 갱신(`008`, `016`, `024`, `025`, `038`, `049`, `052`) | 추가·개정 | +| §24 | branch 링크 18개 추가 | 추가 | +| §31.1 | GENERATED 목록 + 수기 목록에 18개 추가, 총계 갱신 | 추가 | +| §34 | core matrix 1행 정정(§13 D1) + Optional Adapter Stack Matrix 신설(§10) | **정정·추가** | +| §35 | 신규 18행 + 상태 승급 3건 + `(없음)` 해소 4건 + 분포 요약 갱신 | 추가·개정 | +| **§36 신설** | Capability Readiness Model + capability card(§6) | **신설** | +| §29 | ca-tmpl 심층 설계 4건을 근거 인덱스에 등록 | 추가 | +| 헤더(25번 줄) | 경로 표기 규약 정정(§13 D2) | **정정** | +| §11 금지 목록 | "R0 seam 을 R2 로 표기" 금지 1행 추가 | 추가 | + +### 11.1 §35 커버리지 체크리스트 델타 + +**상태 승급 3건** (코드 대조 결과): + +| 기존 행 | 현재 표기 | 정정 표기 | 근거 | +|---|---|---|---| +| Cache consistency | `[~]` | `[*]` | `adapter/outbound/cache-redis` 43개 파일(test 13) | +| Outbound HTTP resilience | `[~]` | `[*]` | `adapter/outbound/httpclient` 38개 파일(test 15) | +| File / Resource handling | `[~]` | `[*]` | `adapter/outbound/fileserver` 16개 파일(test 5) | + +**`(없음)` 해소 4건**: MongoDB / GraphQL / gRPC / WebSocket 은 각각 신규 branch row 로 대체된다. + +**신규 18행**: §4.2 의 branch 를 `[ ]`(미시작) 로 등록하되, Redis foundation 2건은 `[*]`(부분 구현) — Lua executor·catalog·descriptor·atomic primitives·key builder 가 이미 존재한다. + +--- + +## 12. 신규 branch 생성 순서와 방법 + +18개를 한 세션에서 채우지 않는다. 각 branch 는 `/branch ` 로 스캐폴딩한 뒤 `/branch-spec` 이 별도 세션에서 D-row 를 채운다. + +```text +1차 (플랫폼 — 나머지 전부가 이 결정을 상속): #1 +2차 (기술 런타임 — 능력의 provider): #2 #3 → #4 #5, #6, #7 +3차 (능력 계약 — 주도권 이전 대상 포함): #8 #9 #10 #11 #12 #13 +4차 (전송·조회): #14 #15 #16 #17 #18 +``` + +각 차수 진입 조건: + +- **1차 완료 전 2차 착수 금지.** capability descriptor 모양이 확정되지 않으면 모든 provider branch 가 활성화 계약을 각자 발명한다. +- **#2 완료 전 #3 착수 금지.** key model·codec 이 program descriptor 의 입력이다. +- **3차 각 branch 착수 시 §5.2 이전 절차 6단계를 함께 수행한다.** 신규 D-row 작성과 기존 branch 포인터 치환을 같은 작업 단위로 묶지 않으면 두 owner 가 공존하는 기간이 생긴다. + +각 branch-note 는 `templates/branch-note-template.md` 를 따르고, hub §24 의 Work Item Contract 7필드(Decision / Allowed / Forbidden / Required registry update / Required contract test / Failure condition / Canonical extraction target)를 채운다. + +--- + +## 13. 드리프트 정정 (코드 근거) + +이 절의 항목은 설계 제안이 아니라 **ca-tmpl 코드를 읽어 확인한 사실**이다. hub 를 코드에 맞춰 정정한다. + +| # | 드리프트 | hub 현재 값 | 코드 사실 | 근거 파일 | +|---|---|---|---|---| +| D1 | Spring Boot 버전 | §34 Stack Matrix `Framework = Spring Boot 3.5.14`, §6.1 `DEC-…-STACK-FRAMEWORK-001` 동일 | **`4.0.0`** | `src/build.gradle:8` — `id 'org.springframework.boot' version '4.0.0' apply false` | +| D2 | ca-tmpl 경로 | 헤더 25번 줄 `/home/donghyeon/workspace/ca-tmpl/` | **`/home/donghyeon/workspace/desktop-server-git/clean-architecture-backend-template/`** | 저장소 위치 확인(2026-07-28) | +| D3 | multi-instance 활성화 판정 | §6.1 `SCHEDULER-LOCK-001` 이 "DB advisory lock 사용" 만 규정 | bean **이름 문자열 목록**으로 판정 — `distributedLockProvider`, `cacheStampedeProtection`, `outboxLeaderElection` | `src/app-bootstrap/.../runtime/StartupSafetyValidator.java:23-27` | + +**D1 의 파급이 크다.** §34 는 "모든 branch 가 상속하는 stack SSOT" 이고 §34 의 Stack 변경 절차는 sibling branch 영향 평가를 요구한다. 정정 시 `grep -rn "project §34" raw/branch-notes/feature-*.md` 로 영향 branch 를 식별한다. Spring Boot 4.0.0 은 3.5.x 대비 baseline 이 달라지므로, 각 branch 의 프레임워크 의존 결정(특히 security·web·data 계열)은 재평가 대상이다. + +**D2 는 llm-wiki 전역 35개 파일**이 옛 경로를 참조한다(`grep -rn "workspace/ca-tmpl" --include="*.md"`). 본 설계의 실행 범위는 **hub 1개 파일**로 한정하고, 나머지는 별도 정리 작업으로 둔다 — 일괄 치환은 `wiki/log.md` 의 과거 실행 기록까지 바꾸게 되어 이력 왜곡이다. + +**D3 는 #1 branch 의 첫 번째 D-row 가 될 사실**이다. `StartupSafetyValidator` 가 bean 이름만 확인하므로, 그 이름을 가진 아무 bean 이나 존재하면 multi-instance 안전으로 판정된다. + +### 13.1 이번에 정정하지 않는 것 + +| 항목 | 이유 | +|---|---| +| llm-wiki ↔ llm-wiki-private 미러 분기 | ca-tmpl `AGENTS.md:72,79` 는 branch-note 캡처 대상을 `llm-wiki-private` 로 지정한다. 두 저장소의 hub 파일은 2026-07-28 기준 **동일**하다. 어느 쪽을 정본으로 둘지는 사용자 결정 사항이며, 본 설계는 작업이 지시된 `llm-wiki` 를 대상으로 한다. **두 저장소 동기화는 별도 작업으로 남긴다.** | +| `wiki/projects/ca-tmpl/` 16개 canonical 문서 | 본 설계는 raw layer(project note + branch note)만 다룬다. canonical 승급은 branch 완료 후 `/ingest` 경로다. | + +--- + +## 14. 근거 자료 + +### 14.1 프로젝트 내부 설계 (등급: `internal-design-doc`) + +**공식 문서 아님.** 인용 시 "ca-tmpl 프로젝트 설계 결정" 으로 표기하고 외부 best practice 로 격상하지 않는다. + +| 문서 | 본 설계에서 인용한 절 | +|---|---| +| ca-tmpl `2026-07-26-production-capability-platform-design.md` | §3(현재 상태), §6(readiness), §7(provider 선택·실패정책), §8(Redis), §9(idempotency), §10(lock), §11(outbox/Kafka), §12(query/persistence), §13(outbound), §14(inbound), §15(관측) | +| ca-tmpl `2026-07-26-redis-production-capability-design.md` | §10(role), §11(key), §12(payload), §13(program registry), §14(command 의미), §15(cache contract) | +| ca-tmpl `2026-07-27-httpclient-production-capability-design.md` | §3, §15(total deadline), §17~§20(retry/CB/순서) | +| ca-tmpl `2026-07-26-fileserver-production-capability-design.md` | §11(상태 머신), §12(staged publish), §13(guarantee) | + +### 14.2 코드 사실 (등급: `actually-implemented`) + +| 사실 | 파일 | +|---|---| +| 모듈 19개 고정 검증 | `src/settings.gradle` (`expectedModuleCount = 19`), `src/config/architecture/modules.json` | +| `RateLimitAlgorithm` 이 `FIXED_WINDOW` 단일, transport edge 소재 | `src/adapter/inbound/web/.../ratelimit/RateLimitAlgorithm.java` | +| `CacheStore` 가 `Optional get` / `void put` 만 노출 | `src/adapter/outbound/cache-redis/.../CacheStore.java` | +| `CacheRegionPort` 등 신규 cache 계약이 이미 도입됨 | `src/application-core/.../application/cache/` (7개 파일) | +| `IdempotencyStorePort` 에 owner token 없음 (`complete(scope, …)` / `discard(scope)`) | `src/application-core/.../idempotency/IdempotencyStorePort.java:24-42` | +| `DistributedLockPort.tryAcquire(key, waitTime, leaseTtl)` — fencing 없음 | `src/application-core/.../lock/DistributedLockPort.java:29` | +| outbox 행이 `PENDING/IN_FLIGHT/PUBLISHED/DEAD` 로 가변 | `src/application-core/.../outbox/OutboxEventStatus.java` | +| `ObjectStoragePort` 가 `byte[]` 전용 | `src/application-core/.../storage/ObjectStoragePort.java:29,38` | +| 관리 엔드포인트가 `STATELESS` + CSRF 비활성 | `src/app-bootstrap/.../management/security/ManagementSecurityConfig.java:26-27` | +| Redis Lua 실행 기반 존재 | `src/adapter/outbound/cache-redis/.../RedisLuaProgramExecutor.java`, `RedisProgramCatalog.java`, `RedisProgramDescriptor.java`, `RedisAtomicPrimitives.java` | + +### 14.3 외부 공식 문서 (branch-note Sources 시드) + +각 branch 의 `/branch-spec` 단계에서 verbatim 인용과 함께 `raw/official-docs/` 로 수집한다. 본 설계는 링크만 예약하며, **아직 인용 근거로 쓰지 않는다.** + +| branch | 1차 수집 대상 | +|---|---| +| #2 #3 | Redis 공식 — eval-intro(스크립팅·원자성), key eviction, distributed locks patterns / Spring Data Redis scripting | +| #8 | Redis eviction / AWS ElastiCache caching strategies(기존 `raw/official-docs/cache-aside-vs-write-through-aws.md` 재사용) | +| #9 | Redis rate-limiter use case | +| #10 | Spring Session Redis API | +| #12 | Redis distributed locks(fencing 서술) / 기존 `raw/official-docs/cache-redisson-rlock-vs-setnx.md` | +| #13 | Debezium Outbox Event Router | +| #4 #5 | Kafka design(delivery semantics·transactions), producer configs | +| #6 | MongoDB read concern / write concern / change streams | +| #7 | Amazon S3 object integrity | +| #14 | GraphQL Java limits | +| #15 | gRPC deadlines / retry | +| #16 | Spring WebSocket broker relay | +| #17 | (기존 `feature-application-query-bypass-contract` Sources 상속) | + +--- + +## 15. 검증과 게이트 + +### 15.1 본 설계 실행의 완료 조건 + +hub 편집이 아래를 모두 만족해야 완료다. + +1. `/lint` 통과 — canonical 우회·status 미달 파생·registry 누락 검사 +2. `/sync` 통과 — 이전 8건의 owner 교체 후 재진술 0 +3. hub §35 의 분포 요약 숫자가 실제 row 수와 일치 +4. 신규 18개 slug 가 §8.0 / §24 / §31.1 / §35 / §25 다섯 곳에 **모두** 등장 +5. §7 의 Decision Summary 문자열이 §6.1 과 각 branch-note 상속 표에서 **완전 일치**(조사 앞 공백 포함) +6. §13 드리프트 3건이 코드 근거 링크와 함께 정정됨 + +### 15.2 각 신규 branch 의 승급 게이트 + +hub §26 Universal Acceptance Gate 10문항 + §23 promotion contract 6필드를 그대로 적용한다. 추가로 capability branch 는 아래 2개를 더 만족해야 `documented-only` 를 벗어난다. + +- capability card 의 **보증(guarantee)과 비보증(non-guarantee)이 둘 다** 작성되어 있다. +- card 가 주장하는 동시성·실패 의미를 증명하는 **경합 test 이름**이 명시되어 있다. + +--- + +## 16. 미해결 질문 + +| # | 질문 | 영향 | 처리 | +|---:|---|---|---| +| Q1 | llm-wiki 와 llm-wiki-private 중 어느 쪽이 정본인가 | 18개 branch-note 를 어디에 만들지 | 사용자 결정 필요. 본 설계는 `llm-wiki` 기준 | +| Q2 | Spring Boot 4.0.0 전환이 기존 46개 branch 결정에 미치는 영향 | security·web·data 계열 결정 재평가 | §13 D1 정정 후 `grep -rn "project §34"` 로 영향 평가 별도 수행 | +| Q3 | `feature-rate-limit-idempotency-contract` 를 `delegated` 로 두는가, 폐기하는가 | WI-016 의 처리 | 본 설계는 `delegated` 유지(WI-016 은 두 신규 WI 로 대체 표기). 폐기는 branch 이력 손실 | +| Q4 | Kafka inbound leaf 추가로 모듈이 19 → 20 이 된다 | `settings.gradle:23` 의 `expectedModuleCount = 19` 하드코딩 | #5 branch 의 D-row 로 처리. 모듈 registry migration 절차 필요 | +| Q5 | `adapter:outbound:cache-redis` 모듈명이 Redis 기술 전반을 담게 된다 | 이름과 책임 불일치 | ca-tmpl 설계가 rename 을 별도 승인 대상으로 유예. #2 branch 가 이름 유지 여부를 결정 | diff --git a/raw/project-notes/ca-skeleton-operational-contract.md b/raw/project-notes/ca-skeleton-operational-contract.md index 0615bf5..3817618 100644 --- a/raw/project-notes/ca-skeleton-operational-contract.md +++ b/raw/project-notes/ca-skeleton-operational-contract.md @@ -22,7 +22,7 @@ semantic_surface_exclusions: > 이 문서는 도메인/비즈니스 로직을 제거한 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에 잔존. +> **경로 표기 규약**: 본 문서가 reference하는 `ca-tmpl/docs/...` 경로는 별도 git repo (`/home/donghyeon/workspace/desktop-server-git/clean-architecture-backend-template/`)의 `docs/` 디렉터리를 의미. **2026-07-28 정정** — 이전 표기 `/home/donghyeon/workspace/ca-tmpl/` 는 stale (저장소 이전). LLM Wiki 전역에는 옛 경로 참조가 남아 있으며 일괄 치환은 `wiki/log.md` 의 과거 실행 기록까지 바꾸므로 별도 정리 대상. registry yaml과 runbook stub은 운영 artifact라 LLM Wiki(`wiki/projects/`)에 두지 않고 ca-tmpl repo에 위치. 본 canonical contract 문서만 LLM Wiki에 잔존. > > 자세한 phase 진척과 closure는 §28 Review Remediation Ledger 참조. @@ -976,11 +976,11 @@ promotion failure: | `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-004` | `feature-env-driven-runtime-configuration` | env configuration 6필드 contract와 invalid-config test가 통과한다 | `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-STACK-FRAMEWORK-001@2` | - | `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-008` | `feature-security-operational-baseline` | security failure·header contract와 negative test가 통과한다 | `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-STACK-FRAMEWORK-001@2` | - | `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` | @@ -992,9 +992,9 @@ promotion failure: | `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-020` | `feature-secrets-config-source-contract` | secret source·classification·leakage negative test가 통과한다 | `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-STACK-FRAMEWORK-001@2` | - | `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@2` | - | `planned` | +| `WI-CA-SKELETON-OPERATIONAL-CONTRACT-022` | `feature-tenant-context-policy` | tenant propagation·clear negative fixture가 통과한다 | `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-STACK-FRAMEWORK-001@2` | - | `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` | @@ -1020,16 +1020,16 @@ promotion failure: | `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-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@2`, `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@2`, `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-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@2`, `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@2`, `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` | @@ -1219,7 +1219,7 @@ TODO는 작업 목록이 아니라 미완성 계약입니다. 각 TODO는 branch | `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-FRAMEWORK-001` | 2 | `stack-framework` | framework는 Spring Boot 4.0.0다 | `active` | [[raw/project-notes/ca-skeleton-operational-contract]] | §34 Stack Matrix `Framework` + code fact `src/build.gradle:8` (2026-07-28 정정, rev1 은 `3.5.14` — stale) | | `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` | @@ -2409,7 +2409,7 @@ sequenceDiagram - [[raw/errors/webmvctest-slice-configprops-enum-placeholder-no-default-2026-06-20]] -> ca-skeleton (= ca-tmpl) 프로젝트에 묶이는 모든 raw 자료. ca-tmpl repo (`/home/donghyeon/workspace/ca-tmpl/`) 의 코드와 함께 본 LLM Wiki 의 자료들이 cluster 구성. +> ca-skeleton (= ca-tmpl) 프로젝트에 묶이는 모든 raw 자료. ca-tmpl repo (`/home/donghyeon/workspace/desktop-server-git/clean-architecture-backend-template/`) 의 코드와 함께 본 LLM Wiki 의 자료들이 cluster 구성. ### 31.1 브랜치 (feature-* / develop-* / fix-* / chore-* / experiment-*) @@ -2822,7 +2822,7 @@ sequenceDiagram |---|---|---|---| | **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` 의 실제 코드에 존재하는가? 없으면 *마이그레이션 대상* 임을 본문에 명시했는가? | +| **F3. 실제 코드 cross-check 부재** | spec 이 현재 코드에 *존재하지 않는* 의존성 (예: tenant 컬럼, ArchUnit rule 의 검사 대상 패키지) 을 가정 | 명시적 룰 없음 — *코드 cross-check 게이트 부재* | 본 branch §구현 가이드 의 *전제 사실* (테이블 / 컬럼 / 모듈 / 패키지) 이 `/home/donghyeon/workspace/desktop-server-git/clean-architecture-backend-template` 의 실제 코드에 존재하는가? 없으면 *마이그레이션 대상* 임을 본문에 명시했는가? | | **F4. 위임 / 작성 모호** | branch 가 *결정 SSOT* 임을 명시했으나 §구현 가이드에 실제 작성 코드 잔존 (R3 부분 적용) | §15.5 R3 의 *부분 적용* | §구현 가이드 의 코드 skeleton 이 *reference (실제 호스팅 = sibling)* 인지 *실제 작성 (본 branch host)* 인지 본문에 명시했는가? reference 라면 sibling cite 와 *코드 위치 = sibling* 한 줄 추가했는가? | 위 4개 self-check 는 `/lint` 가 자동 catch 하지 못하는 정성적 영역 — branch 작성 / Sources 추가 / Decision 추가 / §구현 가이드 작성 시점에 *명시적으로* 검토. @@ -2844,7 +2844,7 @@ ca-skeleton 은 *단일 stack 커밋* 을 채택합니다. 다중 DB / 다중 | 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 사용 | +| Framework | Spring Boot | 4.0.0 | starter web / data-jpa / validation 사용. **2026-07-28 정정** — 이전 표기 `3.5.14` 는 stale. 근거: ca-tmpl `src/build.gradle:8` `id 'org.springframework.boot' version '4.0.0' apply false`. `DEC-…-STACK-FRAMEWORK-001@2` | | 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* | @@ -2883,7 +2883,7 @@ ca-skeleton 은 *단일 stack 커밋* 을 채택합니다. 다중 DB / 다중 - **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 단일 +- **Framework**: Micronaut / Quarkus / Helidon — Spring Boot 4.0.0 단일 - **Build tool**: Maven / Bazel — Gradle Groovy DSL 단일 - **Test framework**: TestNG / Spock — JUnit 5 단일