--- title: branch / feature-contract-registry-governance source_type: branch-note status: raw branch: feature-contract-registry-governance parent_branch: related_projects: [ca-skeleton] governing_docs: [raw/project-notes/ca-skeleton-operational-contract] tags: [branch, ca-skeleton, registry, governance, contract] created: 2026-05-22 target_merge: status_label: in-progress id: BR-CA-SKELETON-OPERATIONAL-CONTRACT-041 kind: project-work-item project: ca-skeleton-operational-contract work_item: WI-CA-SKELETON-OPERATIONAL-CONTRACT-041 inherits: [DEC-CA-SKELETON-OPERATIONAL-CONTRACT-ERROR-ENVELOPE-001@1, DEC-CA-SKELETON-OPERATIONAL-CONTRACT-OPENAPI-001@1] refines: [] overrides: [] depends_on: [] contract_packet: 1 contract_packet_sha256: 583c60a41462cd57c1c9bf3c27759eaa9ea2597db7633e5507c9577c6467d81e --- # branch: feature-contract-registry-governance > Layer: `raw/branch-notes/` — error/env/header/log/metric/capability 같은 contract 문자열과 enum을 registry로 관리합니다. ## 부모 (필수) - **Parent project (canonical SSOT)**: [[raw/project-notes/ca-skeleton-operational-contract]] > ca-skeleton 은 별도 root branch 없이 project-note 가 SSOT 역할. 본 feature branch 는 project-note 의 운영 계약 중 해당 영역 (**§21 Contract Registry**) 의 결정/근거/금지 사항을 정제한다. governing_docs 로 §21 을 가리킨다. ## 브랜치 계약 패킷 - **생성 시 프로젝트 개정**: `1` - **패킷 스키마**: `contract_packet: 1` - **완료 조건**: registry single-owner·schema·OpenAPI drift gate가 통과한다 ### 상속한 프로젝트 결정 | Decision Ref | Project Summary | Branch Application | Source | |---|---|---|---| | `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-ERROR-ENVELOPE-001@1` | foundation이 envelope schema와 error.category enum의 단일 owner다 | Work Item 완료 조건에 적용 | [[raw/project-notes/ca-skeleton-operational-contract]] | | `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-OPENAPI-001@1` | verification suite가 OpenAPI drift의 release-blocking 판정권을 소유한다 | Work Item 완료 조건에 적용 | [[raw/project-notes/ca-skeleton-operational-contract]] | ### 브랜치 지역 결정 > 기존 branch-local 결정은 아래 `## Decision Evidence Map / 결정-근거 매핑`의 D-row가 소유하며 이 packet에서 복제하지 않는다. | Decision ID | Decision | Relation | Supporting Claims | Status | |---|---|---|---|---| ### 선언한 예외 | Override ID | Overrides | Reason | Approval | Status | |---|---|---|---|---| ## 목표 100점 skeleton에서 가장 위험한 것은 ad hoc 문자열입니다. error code, env key, header, log field, metric name, capability가 파일마다 흩어지면 운영 계약이 깨집니다. 이 branch는 모든 contract token을 registry 기반으로 관리합니다. - 이슈: - PR: ## 범위 ### 포함 범위 - error registry. - response/meta registry. - header registry. - env registry. - log/metric/trace registry. - capability registry. - registry 변경 절차. ### 제외 범위 - registry UI. - external config server 구현. - runtime dynamic registry. ## 근거 (필수, 최소 1개+) > 본 branch의 결정 근거. 상세 비교는 §외부 근거 / 대안 조사 (있다면) 참조. | Source | 정당화하는 결정 | |---|---| | [[raw/official-docs/archunit-annotation-as-registry-evaluation]] | — | | [[raw/official-docs/registry-adr-official]] | Nygard/MADR; ca-tmpl branch note가 Status·Context·Decision·Consequences 모두 포함하므로 별도 ADR 불요 | | [[raw/official-docs/governance-archunit-official]] | annotation 기반 registry; ca-tmpl은 ArchUnit을 verifier로만 사용 | | [[raw/official-docs/opentelemetry-versioning-stability-spec]] | D5: OTel semantic conventions는 experimental→stable 전환·rename이 발생하며 모든 변경은 Schema File에 기술해야 함 — 외부 표준 매핑 row 필요성의 공식 근거 | | [[raw/official-docs/opentelemetry-http-semconv-migration-guide]] | D5: HTTP 메트릭 이름(`http.server.duration` → `http.server.request.duration`)과 단위(`ms` → `s`)가 실제로 rename된 직접 증거 — mapping/version row 없이는 old vs new token 구분 불가 (OTEL-HM-C2, OTEL-HM-C4) | | [[raw/official-docs/trace-context-w3c-recommendation]] | D5: W3C Trace Context Recommendation 이 `tracestate` 를 통해 내부 shorter identifier 와 표준 `trace-id` 를 병행 전파할 것을 권고 (W3C-TC-C4) — `traceparent`/`tracestate` registry mapping row 유지의 공식 spec 근거 | | [[raw/official-docs/rfc9457-problem-details-http-apis]] | D5: RFC 9457 이 RFC 7807 을 obsolete 하고 error envelope 외부 표준이 버전 관리됨을 IETF 공식 증명 (RFC9457-C1, RFC9457-C5) — skeleton error registry 에 RFC version mapping row 필요성의 직접 근거 | ## 외부 근거 / 대안 조사 (2026-05-22 — Group G-G: Contract Registry Governance) 본 branch의 markdown SSOT + YAML/generated constants + 공통 schema + 7 registry families (as-built, §Audit F2/F3 정합) 결정에 대한 외부 source. - **채택 결정 (markdown raw SSOT + YAML implementation)**: - (ca-tmpl branch note의 "결정 사항" 라인이 사실상 mini-ADR로 작동) - **검토한 대안**: - **대안 1: ADR (Architectural Decision Record) 별도 파일** — [[raw/official-docs/registry-adr-official]] (Nygard/MADR; ca-tmpl branch note가 Status·Context·Decision·Consequences 모두 포함하므로 별도 ADR 불요) - **대안 2: ArchUnit annotations as registry** — [[raw/official-docs/governance-archunit-official]] (annotation 기반 registry; ca-tmpl은 ArchUnit을 verifier로만 사용) - **대안 3: Code-only enums** — DI 통합 강점이나 markdown SSOT 부재 - **대안 4: Protobuf·Smithy as registry** — API contract 도구, ca-tmpl scope 외 - **비교 핵심**: ca-tmpl branch note의 "결정 사항" 라인이 mini-ADR로 동작 (Status=`status_label`, Context=목표/WHY, Decision=결정 사항, Consequences=테스트 계약) — 별도 ADR 파일 도입 불요. ArchUnit은 verifier로만 사용, registry 자체는 markdown SSOT + YAML/generated constants. **후속 보강 (2026-05-22)**: ArchUnit annotation-as-registry 대안 평가 완료. markdown SSOT 채택 유지. [[raw/official-docs/archunit-annotation-as-registry-evaluation]] 참조. **후속 보강 (2026-06-15, D5 외부표준 mapping)**: 외부 platform 표준 채택 시 mapping row 유지(D5) 의 대안 3종 — (1) per-token mapping row, (2) 외부 이름 직접 채택 무 mapping, (3) spec URL 만 참조 — 을 공식 표준으로 조사. OTel semconv 의 실제 rename(`http.server.duration`→`http.server.request.duration`) 과 RFC 7807→9457 obsolete 가 "외부 표준은 버전이 바뀐다" 를 실증하므로, 혼재 표준(W3C+OTel+RFC) 환경에서는 (1) per-token mapping row 채택. 단 W3C Recommendation 처럼 이름이 고정된 표준의 헤더는 (2) 직접 채택 + 최소 `external_standard`/`spec_url` column 으로 충분. 근거: W3C-TC-C4(권고 "encouraged"), OTEL-VS-C4(rename 시 Schema File MUST), OTEL-HM-C2(실 rename), RFC9457-C1(obsolete). ## TODO > TODO drained 2026-05-22 — 결정은 아래 "결정 사항" / "구현 가이드" (Registry Storage Contract / Registry Tables / 변경 절차) 참조. 잔존 TODO 없음. ## 진행 중 메모 - 2026-06-15 (`/branch-spec`): ca-tmpl ground truth(`/home/donghyeon/workspace/ca-tmpl/docs/registries/*.yaml` 7개 + 8 sibling owner branch 실재) 대조 완료. 확인된 핵심 구조: - 본 branch 는 7 registry 의 **schema owner** — 모든 yaml header 가 `# Schema owner: feature-contract-registry-governance` 명시. column 구조·저장 형식·변경 절차의 SSOT. - registry **row 값**(어떤 code/key/name 이 존재하는가)은 각 sibling `owner_branch` 소유(delegated, 8개). - **category enum** 값은 본 branch 가 아니라 foundation 소유(`# Category enum owner: feature-operational-error-observability-foundation`). 본 branch 는 `category` column 이 있어야 한다는 schema 만 소유. - D5(외부표준 mapping) 공식 근거 4종 확보 → UNSUPPORTED 해소. - D3/D4/Registry Tables 의 path·schema·family 수가 as-built 와 달라 §Audit & Findings(F1~F3)로 정합. - 2026-06-20 (Phase C2 구현 착수 — schema-owner gate): 본 branch 의 schema governance 를 기계 강제하는 cross-registry 테스트 `ContractRegistrySchemaGovernanceTest` (`ca-tmpl/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/contract/`) 추가. per-registry value drift guard(`ErrorCodeRegistryMappingTest`/`SecretsClassificationRegistryTest`/`RepositoryAccessCapabilityRegistryTest`/`MetricsAlertingContractTest` — row owner 소유)와 분리되는 **schema 층** 게이트로, 다음 6가지를 검증: ① 7 family(error-codes/env-keys/secrets-classification/headers/mdc-keys/metrics/capabilities) 존재(Audit F3) ② 각 파일 `# Schema owner: feature-contract-registry-governance` 헤더(§3) ③ 모든 row 의 identity(code/key/name)+`owner_branch`(§1/§2) ④ full row 의 `compatibility_impact`(legal enum none/additive/behavior-change/breaking)+`required_test`(D2) ⑤ reference row(secrets public-config 5개) 면제 + reference target 보유. `docs/` gitignore 이므로 registry 부재 시 SKIP, 존재 시 위반은 hard FAIL(기존 drift 테스트 패턴 동일). evidence: `locally-verified` — `./gradlew :app-bootstrap:test --tests '*ContractRegistrySchemaGovernanceTest'` 6 tests green(skipped=0); 음성 변이 검사(illegal `compatibility_impact` 주입 시 FAIL, restore 후 green)로 게이트 실효성 확인. ArchUnit 정적 token 탐지(§Claims To Verify 3행)는 여전히 `planned` — 본 게이트는 artifact schema 정합만 강제하며 그 PoC 를 대체하지 않음. 상세 함정: [[raw/errors/contract-registry-reference-row-universal-column-false-fail-2026-06-20]]. ## 결정 사항 - 2026-05-22: 새 error/env/header/log/metric/capability는 registry 없이 추가하지 않음. - 2026-05-22: registry 항목은 최소 하나 이상의 contract test와 연결. - 2026-05-22: registry 저장 형식은 markdown table을 raw SSOT로 두고, 구현 단계에서 `src/main/resources/contract-registry/*.yml` 또는 generated constants로 변환 가능하게 함. - 2026-05-22: registry row의 공통 필수 column은 `name`, `owner_branch`, `owner_layer`, `default`, `allowed_values`, `compatibility_impact`, `required_test`로 둠. - 2026-05-22: 외부 platform 표준을 쓰는 경우에도 skeleton registry에는 mapping row를 남김. - 2026-05-22: registry 본문(implementation artifact)은 `ca-tmpl/docs/registries/` 하위에 yaml로 작성 (Phase B). raw SSOT는 본 branch note의 표 schema + 각 owner branch의 결정 사항. yaml은 표 schema를 따르는 row table. - 2026-05-22: registry SSOT은 markdown 유지. ArchUnit annotation은 verification verifier 역할만 (registry 아님). 근거: framework-neutral + git diff review + 외부 도구 호환. 상세 평가는 [[raw/official-docs/archunit-annotation-as-registry-evaluation]]. - **2026-06-15 (as-built 정합, Audit F1)**: registry implementation artifact 의 실제 위치는 `ca-tmpl/docs/registries/*.yaml` 7개 파일(D6 와 일치). 위 2026-05-22 D3/Registry Storage Contract 의 `src/main/resources/contract-registry/*.yml` 경로는 **미구현 stale** — 코드에 존재하지 않음(`find src -path '*resources/contract-registry*'` 결과 0). generated Java constants 는 Phase C2 downstream(yaml→constants) 이며 SSOT 아님. yaml header `# SSOT: wiki/projects/ca-tmpl/registries/*.yaml` 는 추출 후 canonical 위치(현재 미존재). - **2026-06-15 (as-built 정합, Audit F3)**: registry 는 6개가 아니라 **7개** family — Error Codes / Env Keys / Secrets Classification / HTTP Headers / MDC·Log Keys / Metrics / Repository Access Capabilities (governing §21 SSOT yaml 표). 이전 "Log/Metric/Trace" 단일 family 는 `mdc-keys.yaml` + `metrics.yaml` 2개로 분리, **Secrets Classification** 추가. 이전 "Response" family 는 별도 registry 가 아니라 foundation 소유 envelope schema 이므로 7 registry 에서 제외. - **2026-06-15 (as-built 정합, Audit F2)**: 초기 제안한 uniform 7-column schema 는 as-built 에서 채택되지 않음. 모든 7 registry 에 공통(universal) 인 column 은 `owner_branch`·`compatibility_impact`·`required_test` **3개뿐** + family 별 identity column(`code`/`name`/`key`) + family-specific column. `owner_layer` 는 error-codes 에만, `default`/`allowed_values` 는 env-keys 에만 존재. D4 UNSUPPORTED → as-built 로 해소. - **2026-06-15 (D5 근거 확보)**: 외부 platform 표준 mapping row(D5) 에 W3C Trace Context / OTel versioning-stability / OTel HTTP migration / RFC 9457 공식 표준 근거 확보. D5 UNSUPPORTED → `official-standard`. 상세 §외부 근거 후속 보강. ## Work Item Contract 각 TODO는 아래 판정 단위로 재작성되어야 canonical 승급 가능합니다. TODO가 단순히 `기준 작성`으로 남아 있으면 이 branch는 완료로 보지 않습니다. | 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` 승급 위치 | ## 판정 기준 | 구분 | 기준 | | --- | --- | | Decision | contract token은 registry로 관리 | | Allowed | 외부 platform 표준 사용 시 mapping table 제공 | | Forbidden | raw string/enum을 branch별로 ad hoc 추가 | | Required metadata | name, owner, default, allowed values, profile, test link, compatibility impact | | Failure condition | registry에 없는 error/env/header/log/metric/capability가 구현에 등장하면 실패 | ## 결정-근거 매핑 > 각 결정이 어떤 raw source claim 으로 뒷받침되는지 명시. `Decision ID` 는 본 branch-note 안에서 안정적으로 유지 (D1~D7). Registry Storage Contract 및 **7개** Registry Family table(§구현 가이드)의 책임도 본 표의 row 로 매핑. > `선택 조건` 열: "이 조건일 때 이 결정, 다른 조건이면 어떤 대안". 분기 없으면 N/A. | Decision ID | Decision | 선택 조건 (언제 이 결정 / 언제 대안) | Supporting Claims | Evidence Strength | Open Risk | |---|---|---|---|---|---| | D1 | 새 error/env/header/log/metric/capability 는 registry 없이 추가하지 않음 | N/A — skeleton-wide 불변 규칙 | `raw/official-docs/registry-adr-official.md#REG-ADR-C1`, `raw/official-docs/registry-adr-official.md#REG-ADR-C2`, `raw/official-docs/governance-archunit-official.md#AU-OFF-C1`, `raw/official-docs/governance-archunit-official.md#AU-OFF-C2`; + **as-built enforcement**: 7/7 registry row 가 `required_test` 필수(grep 확인) → "registry 없이 추가 금지" 는 *required_test + contract test* 로 강제(§구현 가이드 §4 + §테스트 계약), 정적 탐지(ArchUnit custom rule)는 §Claims To Verify PoC | `official-reference + official-vendor-doc + as-built` | REG-ADR-C1/C2 는 "AD/ADR 정의" 까지만 — "모든 contract token 을 registry 로 관리한다" 의 직접 출처 아님. AU-OFF Claim 은 ArchUnit verifier 능력만 — registry SSOT 강제 아님. enforcement 메커니즘은 required_test(as-built) 로 닫히되, "registry 부재 token 의 정적 차단" 은 ArchUnit PoC(미검증, Claims To Verify) | | D2 | registry 항목은 최소 1개 이상의 contract test 와 연결 | N/A — 모든 row 의 `required_test` 필수 | `raw/official-docs/governance-archunit-official.md#AU-OFF-C1`, `raw/official-docs/governance-archunit-official.md#AU-OFF-C2`, `raw/official-docs/archunit-conditional-on-property-3-layer-pattern.md#AUCP-C1`, `raw/official-docs/archunit-conditional-on-property-3-layer-pattern.md#AUCP-C5`, `raw/official-docs/archunit-annotation-as-registry-evaluation.md#AAR-C5` | `official-vendor-doc + engineering-blog` | AAR-C5 (fitness function 정의) 는 verifier 측면만 — "test connection" 의 의무화 자체는 ca-tmpl 운영 결정 | | D3 | registry 저장 형식 = markdown table raw SSOT + YAML implementation artifact (실 위치는 D6: `ca-tmpl/docs/registries/*.yaml`); generated Java constants 는 Phase C2 downstream | markdown 으로 git diff review·외부 도구 호환이 필요할 때 이 결정 / 런타임 DI 통합이 1순위면 대안 3(code-only enum) | `raw/official-docs/archunit-annotation-as-registry-evaluation.md#AAR-C1`, `raw/official-docs/archunit-annotation-as-registry-evaluation.md#AAR-C2` | `official-vendor-doc` | AAR-C1/C2 는 annotation registry 의 한계 (Does not prove: domain contract registry 용도) — markdown SSOT 채택 의 직접 권장 아님, 대안 비교의 부정 근거로만 작동. ⚠️ 이전 Decision 텍스트의 `src/main/resources/contract-registry/*.yml` 경로는 미구현 stale 였음 → D6/§Audit F1 로 정합 | | D4 | registry row 공통 필수 column = **universal 3** (`owner_branch`, `compatibility_impact`, `required_test`) + family identity column (error=`code`, 그 외=`name`, mdc=`key`) + family-specific column. *(초기 제안 uniform 7-column 은 as-built 미채택 — §Audit F2)* | 현재 7 family 는 universal-3 + family-specific 로 분기 없음. **신규 family 추가 시** 어떤 column 을 universal 로 승격할지는 본 결정 범위 밖 — Claims To Verify 2행(walkthrough)으로 위임(의도된 deferral) | ground truth `ca-tmpl/docs/registries/*.yaml` (7 file 모두 `# Schema owner: feature-contract-registry-governance`; universal 3-column 은 grep 으로 7/7 확인, `owner_layer`=error only, `default`/`allowed_values`=env only) | `as-built (ca-tmpl/docs/registries/*.yaml)` | family-specific column 의 universal 승격 기준 부재 — 신규 registry 추가 시 어떤 column 을 공통으로 둘지 규칙 없음. 초기 7-column 제안이 미채택된 이력은 §Audit F2 보존 | | D5 | 외부 platform 표준 (예: OpenTelemetry semantic conventions, RFC 7807→9457, W3C Trace Context) 을 쓰는 경우에도 skeleton registry 에 mapping row 를 남김 | 혼재 표준(W3C+OTel+RFC) 또는 experimental/rename 이력 있는 표준이면 per-token mapping row(대안1) / W3C Recommendation 처럼 이름 고정 표준 헤더는 직접 채택 + 최소 `external_standard`·`spec_url` column(대안2) / spec URL 만 참조(대안3)는 per-token 추적 불가로 기각 | `raw/official-docs/opentelemetry-versioning-stability-spec.md#OTEL-VS-C1`, `raw/official-docs/opentelemetry-versioning-stability-spec.md#OTEL-VS-C4`, `raw/official-docs/opentelemetry-versioning-stability-spec.md#OTEL-VS-C5`, `raw/official-docs/opentelemetry-http-semconv-migration-guide.md#OTEL-HM-C1`, `raw/official-docs/opentelemetry-http-semconv-migration-guide.md#OTEL-HM-C2`, `raw/official-docs/opentelemetry-http-semconv-migration-guide.md#OTEL-HM-C4`, `raw/official-docs/trace-context-w3c-recommendation.md#W3C-TC-C1`, `raw/official-docs/trace-context-w3c-recommendation.md#W3C-TC-C3`, `raw/official-docs/trace-context-w3c-recommendation.md#W3C-TC-C4`, `raw/official-docs/trace-context-w3c-recommendation.md#W3C-TC-C5`, `raw/official-docs/rfc9457-problem-details-http-apis.md#RFC9457-C1`, `raw/official-docs/rfc9457-problem-details-http-apis.md#RFC9457-C5` | `official-standard` | OTEL-VS-C1: experimental 단계에서 breaking change MAY occur → registry row 없이 hardcode 금지. OTEL-VS-C4: 모든 rename·breaking change는 Schema File에 MUST 기술 → mapping row가 변경 추적 지점이 됨. OTEL-HM-C2: `http.server.duration` → `http.server.request.duration` rename 직접 증거. W3C-TC-C1: `traceparent`/`tracestate` 가 W3C Recommendation 규범 표준 — registry "외부 표준" 표기 근거. W3C-TC-C4: 내부 shorter identifier 와 표준 `trace-id` 를 `tracestate` 로 병행 전파 권고("encouraged") — 내부 token ↔ 외부 표준 token mapping row 유지의 직접 spec 근거. RFC9457-C1: "This document obsoletes RFC 7807" — IETF 공식 폐지로 error envelope 외부 표준의 버전 관리가 실제 발생함을 직접 증명. RFC9457-C5: registry 신설 + multiple problems 처리 + non-resolvable type URI guidance 의 3변경 — RFC 7807 vs 9457 token 구분을 위한 skeleton registry 의 version mapping row 필요성의 직접 근거. | OTEL-HM 계열은 HTTP metrics에 한정. W3C-TC-C4 는 "encouraged" (MUST/SHOULD 아님) — D5 의 "mapping row 를 남긴다" 를 의무로 격상하는 것은 ca-tmpl 운영 결정. mapping row 구체적 column schema 는 D4 family-specific 영역(미표준화). ca-tmpl 현재 error envelope 이 RFC 9457 compliant 한지는 별도 코드 검증 필요 | | D6 | registry 본문 implementation artifact = `ca-tmpl/docs/registries/` 하위 yaml (Phase B), raw SSOT 는 본 branch note 표 schema | N/A — Phase B 운영 결정 | `raw/official-docs/archunit-annotation-as-registry-evaluation.md#AAR-C1`, `raw/official-docs/archunit-annotation-as-registry-evaluation.md#AAR-C2` | `official-vendor-doc + as-built (7 yaml 파일 실재)` | yaml 저장 형식의 공식 권장 부재 — Phase B 운영 결정. AAR-C2 의 meta-annotation 패턴은 ArchUnit 설정 중복 제거용일 뿐 registry storage 권장 아님 | | D7 | registry SSOT 은 markdown 유지, ArchUnit annotation 은 verifier 역할만 (registry 아님) — framework-neutral + git diff review + 외부 도구 호환 | annotation 으로 schema(column) 표현 불가 → markdown SSOT 유지 / verifier 가 필요할 때만 ArchUnit annotation 부착 | `raw/official-docs/archunit-annotation-as-registry-evaluation.md#AAR-C1`, `raw/official-docs/archunit-annotation-as-registry-evaluation.md#AAR-C2`, `raw/official-docs/archunit-annotation-as-registry-evaluation.md#AAR-C3`, `raw/official-docs/archunit-annotation-as-registry-evaluation.md#AAR-C4`, `raw/official-docs/archunit-annotation-as-registry-evaluation.md#AAR-C5`, `raw/official-docs/governance-archunit-official.md#AU-OFF-C1`, `raw/official-docs/governance-archunit-official.md#AU-OFF-C2`, `raw/official-docs/governance-archunit-official.md#AU-OFF-C3` | `official-vendor-doc + engineering-blog` | AAR-C5 는 `engineering-blog` (서적 출처). "framework-neutral + 외부 도구 호환" 의 정량 비교 부재 — annotation registry 대비 markdown 의 우위는 본 raw 자료의 "Does not prove" 영역 (annotation 으로 schema 표현 불가) 에서 도출 | ## 구현 가이드 > *결정 (Decisions)* 이 "*무엇*" 이라면, 본 §는 "*어디에 어떻게*" 의 사전 명세. as-built ground truth(`ca-tmpl/docs/registries/*.yaml` 7개 + 8 sibling owner branch)에 정합. 코드로 확인되지 않은 항목은 `planned`/`UNSUPPORTED_IMPL_DECISION` 로 표기. ### 1. Registry 저장 & 경로 (as-built — Registry Storage Contract) > **Trace**: D3 + D6 — Supporting: AAR-C1/C2 + ground truth `ca-tmpl/docs/registries/*.yaml`. > > - **UNSUPPORTED_IMPL_DECISION**: markdown raw SSOT(표) → yaml 변환 스크립트의 구체적 구현(언어/diff 알고리즘)은 근거 raw 없음 — Phase B 도구 결정. trade-off: 수기 동기화 vs 생성 스크립트, 현재 수기. (검증은 §Claims To Verify "markdown↔yaml row 누락" 행.) > - **UNSUPPORTED_IMPL_DECISION**: generated Java constants 의 패키지/클래스 명칭 — 근거 없음, Phase C2 downstream. trade-off: 코드 단계 결정. | item | as-built decision | note | | --- | --- | --- | | raw SSOT | 본 branch note 표 schema + 각 owner branch 결정 사항 + project note §21 | governing §21 (raw/project-notes/ca-skeleton-operational-contract) | | implementation artifact | `ca-tmpl/docs/registries/*.yaml` — **7 files** (error-codes / env-keys / secrets-classification / headers / mdc-keys / metrics / capabilities) | **PATH 정정(Audit F1)**: 이전 `src/main/resources/contract-registry/*.yml` 은 미구현 stale. generated constants 는 Phase C2 downstream, SSOT 아님 | | canonical 추출 경로 (예정) | `wiki/projects/ca-tmpl/registries/*.yaml` | 각 yaml header `# SSOT:` 가 가리키는 추출 후 위치 — 추출 전이라 현재 미존재 (Phase C2) | | row identity | family 별: error=`code`, mdc=`key`, 그 외(env/secrets/headers/metrics/capability)=`name` | as-built grep | | required owner | `owner_branch` 필수(7/7). `owner_layer` 는 error-codes 만 보유 | as-built | | compatibility impact | `none` / `additive` / `behavior-change` / `breaking` 중 하나 (7/7 공통) | as-built | | required test | `required_test` 필수(7/7) — architecture/contract/OpenAPI/log/metric/env smoke 중 하나 이상 | D2 | registry 구현 산출물이 raw SSOT와 다르면 verification suite가 실패해야 합니다. ### 2. Registry families & 공통 schema (as-built 7개 — Registry Tables) > **Trace**: D4 + governing §21 — Supporting: ground truth 7 yaml header(`# Schema owner: feature-contract-registry-governance`). > > - **as-built reconciliation (Audit F2/F3)**: 초기 6-family + uniform 7-column 안은 미채택. universal column 은 `owner_branch`·`compatibility_impact`·`required_test` 3개 + identity + family-specific. **Universal columns (7 registry 전부 보유):** `owner_branch`, `compatibility_impact`, `required_test`, + family identity. 그 외는 family-specific. | registry | yaml 파일 | row owner_branch | identity | family-specific 주요 column | | --- | --- | --- | --- | --- | | Error Codes | `error-codes.yaml` | `feature-operational-error-observability-foundation` *(category enum SSOT)* | `code` | category, http_status, retryable, retry_after_seconds, owner_layer, client_safe_message, log_level, runbook_link | | Env Keys | `env-keys.yaml` | `feature-env-driven-runtime-configuration` | `name` | type, default, allowed_values, classification, required, reload_policy, validation | | Secrets Classification | `secrets-classification.yaml` | `feature-secrets-config-source-contract` | `name` | classification, source, rotation_policy, prod_default, dev_sentinel_prefix, masking_rule | | HTTP Headers | `headers.yaml` | `feature-api-contract-baseline` *(cross-owner: idempotency·tracing·tenant·compat·security)* | `name` | direction, type, required, generated_if_missing, mdc_key, envelope_meta_field, case_style | | MDC / Log Keys | `mdc-keys.yaml` | `feature-operational-error-observability-foundation` | `key` | type, source, required_in, http_header_mapping, envelope_field, propagation, cardinality_safe_for_metric, case_style | | Metrics | `metrics.yaml` | `feature-metrics-alerting-contract` | `name` | type, unit, tags(+cardinality_limit/allowed_values), percentiles, alert_severity_thresholds, log_field_mapping | | Repository Access Capabilities | `capabilities.yaml` | `feature-repository-access-permission-contract` | `name` | scope, enforcement, annotation, semantics, bound_to_capability, threshold | > **"Response" 재분류 (Audit F3, OUT_OF_BRANCH_SCOPE)**: 이전 Registry Tables 의 "Response" family 는 별도 registry yaml 이 아님. response/error envelope schema 는 [[raw/branch-notes/feature-operational-error-observability-foundation]] 가 owner (project §21 "Response Envelope 요약", §3 envelope). registry 메커니즘이 아니라 envelope schema 이므로 7 registry 에서 제외 — 본 branch 결정 범위 밖, foundation 소유. ### 3. Schema-owner vs row-owner 분리 (본 branch 의 핵심 역할) > **Trace**: D1 + D4 — Supporting: ground truth(7 yaml header `# Schema owner: feature-contract-registry-governance`; error-codes.yaml `# Category enum owner: ...`). - 본 branch = **schema owner**: 모든 registry 의 column 구조 + 저장 형식(D3/D6) + 변경 절차(§4)의 SSOT. 어떤 column 이 있어야 하는가를 정함. - 각 registry **row owner** = sibling `owner_branch` (8개, §2 표). 어떤 row(code/key/name) 값이 존재하는가는 sibling 결정. 본 branch 는 row 값을 정의하지 않음. - **category enum 값** = foundation 소유(error-codes.yaml). 본 branch 는 `category` column 존재만 강제, enum 값(VALIDATION/AUTH/AUTHZ/…10개)은 foundation. → §엣지·실패·의존 cross-contract 의존. ### 4. Registry 변경 절차 (change procedure) > **Trace**: D1(registry 없이 추가 금지) + D2(test 연결) + D7(markdown SSOT) — Supporting: AU-OFF-C1/C2. project §21 "Registry 변경 절차" 와 정합. > > - **UNSUPPORTED_IMPL_DECISION**: step 6 의 TODO-drain mismatch 검사 주체/시점 — 현재 *수동 review* (자동 lint 미구현). trade-off: 수동 review(즉시·누락 위험) vs lint 자동화(구현 비용). 향후 `wiki_structure_lint.py` 확장 대상. 1. registry row를 먼저 추가. 2. 관련 branch note의 Decision/Failure condition을 수정. 3. contract test 또는 architecture test mapping을 추가. 4. `.env.example`, OpenAPI snapshot, log assertion, metric assertion 중 영향받는 산출물을 갱신. 5. backward compatibility 또는 migration 영향이 있으면 canonical 승급 전 기록 (`compatibility_impact` column 갱신). 6. **TODO drain.** 이 branch의 결정이 표(Decisionized Work Items 또는 동등 표)로 반영되면 동일 branch 내 잔존 TODO 항목은 (a) 해당 표 row로 link 또는 (b) 삭제. "기준 작성" TODO를 표와 분리해 두는 패턴은 forbidden. branch note의 TODO 블록과 Decisionized 표의 row 수가 mismatch면 review에서 fail(수동 check, 향후 lint 자동화 대상). ## 엣지·실패·의존 > R4 캡처용. 본 branch 는 schema/governance 층이므로 "다른 계약 의존" 이 핵심. - **다른 계약 의존 (cross-contract)**: - **category enum** 값은 [[raw/branch-notes/feature-operational-error-observability-foundation]] 의 결정(category enum owner)에 의존 — 본 branch 는 `category` column schema 만 소유. foundation 이 enum 을 바꾸면 error-codes.yaml 의 `category` 값 전체가 영향(본 branch 의 schema 는 불변). - **각 registry row** 는 8개 sibling `owner_branch` 가 소유(delegated, §구현 가이드 §2). 본 branch 가 **universal column schema 를 바꾸면 7 registry 전부**가 동시 영향 → 항상 `breaking` 후보. 부분 적용 시 일부 registry 가 구 schema 로 남아 verification 실패. - **response/error envelope schema** 는 foundation 소유(registry 아님, Audit F3). 본 branch 가 정의하지 않음. - **headers ↔ mdc-keys ↔ metrics ↔ envelope** cross-link: 동일 식별자가 layer 별로 다른 표기(`X-Request-Id` kebab / `request_id` snake / `meta.requestId` camel)를 가짐 — 표기 매핑 SSOT 는 foundation(mdc-keys snake authoritative). schema 가 이 매핑 column(`mdc_key`/`envelope_meta_field`/`http_header_mapping`)을 보유해야 함. - **headers.yaml cross-owner**: HTTP Headers registry row 는 단일 owner 가 아니라 복수 — idempotency=[[raw/branch-notes/feature-rate-limit-idempotency-contract]] (`Idempotency-Key`/`Retry-After`), tracing=`traceparent`/`tracestate` (W3C-TC-C4), tenant/compat/security=각 owner branch. governing §21 도 "(cross-owner)" 로 인정. schema column(`direction`/`mdc_key`/`case_style`) 변경 시 이들 owner row 가 동시 영향. row *값* 위임은 §Coverage(api-contract-baseline primary). - **실패·엣지 경로**: - **markdown SSOT ↔ yaml drift**: 변환/동기화 도구 부재(현재 수기). row 누락 시 verification suite fail 해야 함 → §Claims To Verify. - **yaml header SSOT 경로 불일치**: yaml header `# SSOT: wiki/projects/ca-tmpl/registries/*.yaml` 가 실제 파일 위치(`docs/registries/`)와 다름 — 추출 전 canonical placeholder. 추출 시점까지 "현재 위치 ≠ header 표기" 를 인지해야 함(헷갈림 방지). - **stale enum 주석 (OUT_OF_BRANCH_SCOPE)**: `error-codes.yaml` L580 주석에 deprecated `PERSISTENCE` 문자열 잔존(actual enum 은 10-value, `PERSISTENCE` 없음). 이는 category enum 영역(foundation 소유)이며 본 branch schema 범위 밖 → foundation 에 정합 권고만(자동 rewrite 금지). - **외부 표준 rename 전환기 (dual-emit)**: OTel `OTEL_SEMCONV_STABILITY_OPT_IN=http/dup` (OTEL-HM-C5) 처럼 old + new token 이 동시 활성인 기간 — D5 mapping row 가 old/new 를 구분하려면 version 구분 column 필요. mapping row 의 구체 column schema 는 D4 family-specific(미표준화) 영역. - **신규 registry family 추가 시**: universal-3 로 표현 불가한 family-specific column 발생 가능 — schema 확장 결정 필요(어떤 column 을 universal 로 승격할지 기준 부재, D4 Open Risk). ## Audit & Findings (2026-06-15 — ca-tmpl ground truth 대조) > `/branch-spec` 가 ca-tmpl `docs/registries/*.yaml` + project §21 + 8 sibling branch 와 대조해 발견한 drift. 사용자 작성 결정을 덮어쓰지 않고 **append-only 정합**(결정 사항 2026-06-15 라인) + 본 § 기록. 원 결정 이력은 §결정 사항 2026-05-22 라인에 보존. | ID | 유형 | 발견 | 정합 조치 | |---|---|---|---| | F1 | `PATH_DRIFT` | Registry Storage Contract/D3 의 `src/main/resources/contract-registry/*.yml` 경로가 코드에 미구현(0 hits). 실제 yaml 은 `ca-tmpl/docs/registries/*.yaml`(D6 와 일치) | 구현 가이드 §1 을 docs/registries 로 정합, D3 Decision 텍스트 정정, 결정 사항 2026-06-15(F1) 추가. 원 D3 라인은 §결정 사항 2026-05-22 에 보존 | | F2 | `SCHEMA_DRIFT` | D4 의 uniform 7-column(`name/owner_branch/owner_layer/default/allowed_values/compatibility_impact/required_test`)이 as-built 미채택. universal 은 3개(`owner_branch`/`compatibility_impact`/`required_test`)뿐, `owner_layer`=error only, `default`/`allowed_values`=env only | D4 Decision 을 as-built(universal 3 + identity + family-specific)로 갱신, 초기 제안 미채택 이력 명시. 결정 사항 2026-06-15(F2) 추가 | | F3 | `FAMILY_COUNT_DRIFT` | Registry Tables 가 6 family(+phantom "Response"). as-built/governing §21 은 7 family — "Log/Metric/Trace"→mdc-keys+metrics 분리, Secrets Classification 추가, "Response"=foundation envelope(registry 아님) | 구현 가이드 §2 를 as-built 7 family 로 갱신, "Response" 재분류(OUT_OF_BRANCH_SCOPE). 결정 사항 2026-06-15(F3) 추가 | | F4 | `STALE_COMMENT` (OUT_OF_BRANCH_SCOPE) | `error-codes.yaml` L580 주석에 deprecated `PERSISTENCE` 잔존 | category enum = foundation 소유 → 본 branch schema 영역 밖. foundation 에 정합 권고만(자동 수정 안 함). §엣지·실패·의존 기록 | ## 테스트 계약 - error code가 registry 없이 사용되면 실패. - env key가 registry와 `.env.example`에 없으면 실패. - log/metric field가 registry naming과 다르면 실패. - registry 변경 없이 response/header/capability 상수가 추가되면 실패. ## 검증해야 할 주장 > 공식 문서나 사례는 근거지만, 내 프로젝트의 실 동작을 자동으로 보장하지 않음. | Claim | Why uncertain | How to verify | Status | |---|---|---|---| | markdown table SSOT 가 yaml/generated constants 로 변환되어도 row 누락 없이 일관 유지된다 | D3 — markdown ↔ yaml 변환의 공식 도구/스크립트 부재(현재 수기). AAR-C1/C2 는 annotation 한계만 보임 | Phase B 진입 시 markdown → yaml 변환 스크립트 작성 + `diff` 로 row count 일치 검증 + drift 시 CI fail rule 추가 | `planned` | | universal-3 column (`owner_branch`/`compatibility_impact`/`required_test`) + family-specific column 모델이 7 registry 전부에 충분하다 | D4 — as-built 로 7 family 가 family-specific column 을 실제로 사용함은 확인(F2). 다만 신규 registry 추가 시 universal-3 만으로 부족할 가능성 + 어떤 column 을 universal 로 승격할지 기준 부재 | 신규 registry 후보(예: rate-limit policy / feature-flag) 에 universal-3 적용 walkthrough → 부족 시 universal 승격 기준 결정 | `planned` | | ArchUnit 만으로 "registry 에 없는 contract token 의 사용" 을 정적으로 탐지 가능 | AU-OFF-C2 + AAR-C4 의 fitness function 능력 한계 — registry 와 코드의 cross-reference 검사가 ArchUnit DSL 로 가능한지 PoC 필요 | sample error code (registry 부재) 를 코드에 추가 → ArchUnit `noClasses().that()...should().notHaveCode().that().isNotIn(REGISTRY)` 식 custom rule PoC → 탐지 성공 여부 | `planned` | | `.env.example`, OpenAPI snapshot, log assertion, metric assertion 이 registry 변경 시 자동으로 drift 탐지 | D1, D7 — 4종 산출물 ↔ registry 의 cross-check 도구 부재 | env: dotenv-linter / OpenAPI: openapi-diff / log: logback test appender / metric: micrometer test registry 각각의 CI step PoC | `planned` | | ADR 별도 파일 없이 branch-note 의 "결정 사항" 라인이 mini-ADR 로 작동 (Status/Context/Decision/Consequences 매핑) | REG-ADR-C2 "ADR captures a single AD" — 1-decision-1-file 모델과 branch-note 의 "결정 사항 누적" 모델의 trade-off 검증 필요 | branch-note 의 한 결정 라인을 MADR 포맷으로 변환 시도 → 4 section 모두 채워지는지 + 별도 파일 가치 평가 | `needs-confirmation` | | 외부 platform 표준 (OpenTelemetry / RFC 7807→9457 / W3C) 사용 시 mapping row 가 가독성 손실 없이 표현 | D5 — OTel versioning spec 은 breaking change MAY occur + MUST describe in Schema File 확인 (OTEL-VS-C1/C4/C5). RFC9457-C1/C5 로 RFC 7807→9457 obsolete 사실 확인, W3C-TC-C4 로 tracestate 병행 권고 확인. 단 mapping row 의 구체적 column schema(`external_standard`/`external_token_name`/`external_version`)는 family-specific(미표준화, D4). ca-tmpl error envelope 의 RFC 9457 compliant 여부 미검증 | OTel log/metric registry 에 최소 3 row 추가 후 mapping column 으로 표현 가능한지 walkthrough; error registry 에 RFC 9457 type URI mapping row 추가 PoC (RFC9457-C2 근거 — `type` URI 가 primary identifier) | `planned` | | company-tech-blog (카카오뱅크 Modulith / 우아한형제들 Hexagonal) 사례는 official best practice 가 아니라 case study 임을 본 결정 라인이 명시한다 | "company-tech-blog → 공식 best practice" 격상 금지 (CLAUDE.md §5). 현 branch 의 결정 라인이 carry over 하는지 검증 | branch-note 의 모든 결정 라인 grep → company-tech-blog 인용이 "공식 best practice" 표현으로 격상되지 않았는지 확인 | `planned` | ## 관심사 커버리지 > `/coverage` 가 채우는 생성물 — governing §21 (raw/project-notes/ca-skeleton-operational-contract) 이 요구하는 관심사를 이 브랜치가 빠짐없이 덮는지. 기준: `rules/coverage-gate.md`. 본 branch 는 schema/governance owner 이므로 registry **값** 은 sibling 에 위임(delegated), **schema·저장·절차** 는 covered-here. | 관심사 | 상태 | owner | 심각도 | 근거 | |--------|------|-------|--------|------| | registry 공통 schema (column 구조) | covered-here | — | — | D4, 구현 가이드 §2 | | registry 저장 형식·경로 | covered-here | — | — | D3/D6, 구현 가이드 §1 | | registry 변경 절차 | covered-here | — | — | D1/D2/D7, 구현 가이드 §4 | | schema-owner vs row-owner 분리 | covered-here | — | — | 구현 가이드 §3 | | Error Codes registry 값 | delegated | [[raw/branch-notes/feature-operational-error-observability-foundation]] | OK | 구현 가이드 §2 | | Error category enum 값 | delegated | [[raw/branch-notes/feature-operational-error-observability-foundation]] | OK | §엣지·실패·의존 + Audit F4 | | Env Keys registry 값 | delegated | [[raw/branch-notes/feature-env-driven-runtime-configuration]] | OK | 구현 가이드 §2 | | Secrets Classification 값 | delegated | [[raw/branch-notes/feature-secrets-config-source-contract]] | OK | 구현 가이드 §2 | | HTTP Headers registry 값 | delegated | [[raw/branch-notes/feature-api-contract-baseline]] | OK | 구현 가이드 §2 | | MDC / Log Keys 값 | delegated | [[raw/branch-notes/feature-operational-error-observability-foundation]] | OK | 구현 가이드 §2 | | Metrics registry 값 | delegated | [[raw/branch-notes/feature-metrics-alerting-contract]] | OK | 구현 가이드 §2 | | Capabilities registry 값 | delegated | [[raw/branch-notes/feature-repository-access-permission-contract]] | OK | 구현 가이드 §2 | | Response / error envelope schema | delegated | [[raw/branch-notes/feature-operational-error-observability-foundation]] | OK | 구현 가이드 §2 (Response 재분류, Audit F3) | | SENSITIVE_READ 메타표(entity FQN+field) + field-level enforcement (← [[raw/branch-notes/feature-repository-access-permission-contract]] 위임 수신) | documented-defer | 본 branch (schema governance), row=`planned` | OK (ack) | 위임 수신 확인. sensitive-field metadata table 은 별도 registry 로 본 branch 의 schema governance 적용 대상이나, 도메인 entity 부재로 row 는 `planned`(아직 sensitive-fields.yaml 미존재). capabilities 의 `SENSITIVE_READ` *어휘* 는 feature-repository-access-permission-contract 소유 | > **위임 수신 (incoming delegation, 2026-06-15)**: [[raw/branch-notes/feature-repository-access-permission-contract]] 가 `SENSITIVE_READ` 의 *메타표(entity FQN + field) + field-level enforcement* 를 본 branch 에 `documented-defer` 로 위임했다(그 branch §Coverage). governing §21 은 이 메타표를 7 registry 로 *명시 요구하지 않으므로* coverage Blocking 은 아니나, 본 branch 가 수신을 명시한다: sensitive-field 메타표는 향후 별도 registry(예: `sensitive-fields.yaml`)로 본 branch 의 registry schema governance(D4 universal-3 + family-specific) 를 적용해 정의한다. 도메인 entity 가 도입되기 전까지 row 는 `planned` — 현재 ca-tmpl `docs/registries/` 에 해당 yaml 부재. *어휘*(`SENSITIVE_READ` capability 자체)는 capabilities.yaml owner(feature-repository-access-permission-contract) 소유로 유지. ## 마주친 문제 - 2026-06-20 (Phase C2): schema-owner gate 구현 중, `secrets-classification.yaml` 의 15 row 중 5개(Tier-1 public-config: APP_PROFILE / APP_NAME / SERVER_PORT / SPRING_PROFILES_ACTIVE / OTEL_EXPORTER_OTLP_ENDPOINT)가 universal-3 의 `compatibility_impact`/`required_test` 를 의도적으로 생략(`reference:` 로 `env-keys.yaml` 에 위임, 파일 헤더 L17). 모든 row 에 universal-3 를 요구하는 naive 게이트는 이 5 row 에서 false-FAIL 한다. → 게이트를 "reference row(=`reference:` 키 보유)는 contract column 면제, identity+`owner_branch`+reference target 만 요구" 로 모델링해 해소. 상세: [[raw/errors/contract-registry-reference-row-universal-column-false-fail-2026-06-20]]. ## 완료 후 wiki 추출 대상 - `wiki/projects/ca-skeleton-operational-contract.md` (또는 canonical `wiki/projects/ca-tmpl.md` §Contract Registry) 의 contract registry canonical section. ## 묶음 - [[raw/official-docs/archunit-annotation-as-registry-evaluation]] - [[raw/official-docs/governance-archunit-official]] - [[raw/official-docs/opentelemetry-http-semconv-migration-guide]] - [[raw/official-docs/opentelemetry-versioning-stability-spec]] - [[raw/official-docs/registry-adr-official]] - [[raw/official-docs/rfc9457-problem-details-http-apis]] - [[raw/official-docs/trace-context-w3c-recommendation]] - [[raw/errors/contract-registry-reference-row-universal-column-false-fail-2026-06-20]] - [[raw/blog-topics/contract-registry-schema-owner-vs-row-owner-gate-2026-06-20]] > 본 feature branch 는 leaf — 자식 자료 없음. Phase C2 실 코드 작성 단계에서 errors / interview prep / lectures 가 누적되면 본 섹션에서 그룹화. ### 오류 기록 (본 feature 작업 중 발생) - [[raw/errors/contract-registry-reference-row-universal-column-false-fail-2026-06-20]] — reference row 면제를 누락한 naive schema 게이트의 false-FAIL 함정(resolved, 2026-06-20). ### 면접 준비 (이 작업에서 나올 수 있는 면접 질문) - (없음 — 추가 추출 없음. schema-owner vs row-owner 분리 논점은 아래 Blog topics 로 캡처.) ### Blog topics - [[raw/blog-topics/contract-registry-schema-owner-vs-row-owner-gate-2026-06-20]] — multi-owner registry 의 schema-owner vs row-owner 분리를 cross-file 정합 테스트로 박제하는 패턴(Phase C2 schema-owner gate 에서 추출). ## 관련 일일 노트 - (아직 연결된 일일 노트 없음 — 현재 문서 단계. 실 구현 착수 시 작업일 daily note 를 양방향 연결.) ## 완료 후 정리 > 머지/종료 시점에 채움. - PR 링크: - 리뷰 메모: - 머지 결과 / 배포 환경: - **wiki 추출 대상** (verified만, `wiki/projects/`로만 추출): - `actually-implemented` 항목: - `locally-verified` 항목: - `prod-verified` 항목: - **추출하지 않을 항목** (planned / documented-only / abandoned):