Files
llm-wiki/raw/branch-notes/feature-frontend-contract-registry-governance.md
DongHyeonka d6dfda64ab docs(branch): 기존 7개 노트를 개정 결정·신규 위임에 정합
- REGISTRY-001 Summary 문자열 4곳 (additive, revision 은 1 유지).
  registry-governance 는 본문 서술 12곳의 '8개'도 함께 9개로 갱신
- OFFLINE-CACHE-001 pin @1->@2 와 Summary·적용점 (behavior-change)
- DELEG-FE-007~011 delegate 5곳 접수 완료 — 미접수 위임 0건
- runtime-schema-validation 이 FLOW-FE-EVENT-003/004 소유를 명시
- env-runtime-config 가 FE-REG-CAPABILITY registry 와 FE-GATE-033 gate
  owner 를 취득하고 claim 2건 추가
- 신규 6개 노트의 delegation pin 을 기존 @1 형식으로 정규화
2026-07-28 14:40:38 +09:00

303 lines
34 KiB
Markdown

---
title: branch / feature-frontend-contract-registry-governance
source_type: branch-note
status: raw
id: BR-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-022
kind: project-work-item
project: ca-skeleton-frontend-operational-contract
work_item: WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-022
inherits: [DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-REGISTRY-001@1]
refines: []
overrides: []
depends_on: [WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-001, WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-002]
contract_packet: 1
branch: feature-frontend-contract-registry-governance
parent_branch:
related_projects: [ca-skeleton-frontend, ca-skeleton]
governing_docs: [raw/project-notes/ca-skeleton-frontend-operational-contract]
tags: [branch, ca-skeleton, frontend, api-design, javascript, api-contract]
created: 2026-07-18
target_merge:
status_label: in-progress
contract_packet_sha256: 42419c3541919063c7668d0cbd210002b61996c27b573d86b36ba2b19337597b
imports: [FE-OC-004@1, FE-OC-005@1, FE-OC-006@1, FE-OC-008@1, FE-OC-012@1, FE-OC-013@1, FE-OC-014@1, FE-OC-016@1, FE-OC-020@1, FE-OC-023@1]
---
# branch: feature-frontend-contract-registry-governance
> Layer: `raw/branch-notes/` — TODO·결정·진행 기록. 구현 결과는 검증 뒤 `/ingest`로만 추출한다.
<!-- section-id: branch-parent -->
## 부모 (필수)
[[raw/project-notes/ca-skeleton-frontend-operational-contract]]
<!-- GENERATED: branch-contract:start -->
<!-- section-id: branch-contract-packet -->
## 브랜치 계약 패킷
- **생성 시 프로젝트 개정**: `1`
- **패킷 스키마**: `contract_packet: 1`
- **완료 조건**: 9개 registry snapshot·schema validation·single-owner check가 통과한다
<!-- section-id: inherited-project-decisions -->
### 상속한 프로젝트 결정
| Decision Ref | Project Summary | Branch Application | Source |
|---|---|---|---|
| `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-REGISTRY-001@1` | route, API operation, env, storage, error, query, telemetry, release token, capability를 9개 registry로 관리한다 | 9개 registry의 owner·schema·impact·snapshot governance를 집행한다 | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] |
<!-- section-id: branch-local-decisions -->
### 브랜치 지역 결정
| Decision ID | Decision | Relation | Supporting Claims | Status |
|---|---|---|---|---|
| D1 | 9개 registry를 single-owner model로 관리한다 | `local` | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] | `proposed` |
| D2 | project owner map을 registry 소유 SSOT로 사용한다 | `local` | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] | `proposed` |
| D3 | 모든 registry change에 compatibility impact를 기록한다 | `local` | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] | `proposed` |
| D4 | uniform schema validation과 orphan scan을 실행한다 | `local` | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] | `proposed` |
| D5 | per-registry snapshot과 diff를 evidence로 남긴다 | `local` | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] | `proposed` |
| D6 | producer와 consumer test의 동기 갱신을 gate한다 | `local` | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] | `proposed` |
<!-- section-id: declared-overrides -->
### 선언한 예외
| Override ID | Overrides | Reason | Approval | Status |
|---|---|---|---|---|
없음.
<!-- GENERATED: branch-contract:end -->
<!-- section-id: branch-goal -->
## 목표
- 이 branch는 `FE-OC-022`(9개 registry는 single primary owner와 compatibility impact를 MUST 기록)를 *구현 착수 가능한 governance 명세*로 내린다. 근거는 [[raw/project-notes/ca-skeleton-frontend-operational-contract]] FE-D018 (route/API-operation/env/storage/error/query/telemetry/release/capability token은 9개 registry로 관리)이며, 관리 대상 registry 목록과 owner는 [[raw/project-notes/ca-skeleton-frontend-operational-contract]] §5.1 owner map, 변경 절차는 §5.10, compatibility 분류는 §3.3에서 온다.
- 본 branch는 **registry의 *내용*(각 registry의 schema field·row)을 재정의하지 않는다.** 각 registry의 schema는 그 registry의 owner branch가 소유한다(§5.2~§5.9). 본 branch는 그 registry들을 *가로질러* 강제하는 **governance 규칙**만 소유한다: owner map single-owner check, uniform schema-validation harness, compatibility-impact 기록 gate, per-registry snapshot/diff. 모든 항목은 repo가 없으므로 `planned`.
- 이슈: 없음 (스캐폴딩 단계)
- PR: 없음
<!-- section-id: branch-scope -->
## 범위
### 포함 범위
- **owner map governance** — [[raw/project-notes/ca-skeleton-frontend-operational-contract]] §5.1의 8-registry owner map을 registry 소유의 SSOT로 고정하고, single-owner check(registry당 owner가 0개/2개 이상이면 fail)를 정의 (`FE-OC-022`).
- **uniform schema-validation harness** — 9개 registry 각 row가 *owner가 선언한* minimum schema(§5.2~§5.9, §5.11)를 만족하는지 대조 + orphan/ad hoc token scan = 0 (`FE-SC-005`, §5.10 step 8).
- **compatibility-impact 기록 gate** — 모든 registry change가 `compatibility_impact ∈ {none, additive, behavior-change, breaking}`를 MUST 기록 (§3.3, §5.10).
- **per-registry snapshot + diff artifact** — `FE-OC-022`의 minimum evidence(registry diff check) 산출물.
- **producer/consumer test 동기 갱신 gate** — registry change 시 producer test와 consumer test가 *함께* 갱신되었음을 검사 가능한 증거로 강제 (§5.10 step 5). 개별 test 자체의 계층·러너·fixture 책임은 [[raw/branch-notes/feature-frontend-test-taxonomy-contract]] (`FE-OC-020`) 소유이며, 본 branch는 *registry change 시점의 동기 갱신 여부*만 gate 한다.
- contributes to (owner 아님, fixture/gate 협업): [[raw/branch-notes/feature-routing-navigation-guard-contract]] (`FE-OC-005`), [[raw/branch-notes/feature-api-client-response-envelope-contract]] (`FE-OC-006`), [[raw/branch-notes/feature-frontend-env-runtime-config-contract]] (`FE-OC-004`), [[raw/branch-notes/feature-frontend-storage-registry-contract]] (`FE-OC-013`), [[raw/branch-notes/feature-frontend-error-classification-boundary-contract]] (`FE-OC-008`), [[raw/branch-notes/feature-server-state-caching-contract]] (`FE-OC-012`), [[raw/branch-notes/feature-frontend-observability-logging-trace-contract]] (`FE-OC-014`), [[raw/branch-notes/feature-frontend-release-cache-rollback-contract]] (`FE-OC-016`), [[raw/branch-notes/feature-frontend-contract-compatibility-governance]] (`FE-OC-023`), [[raw/branch-notes/feature-frontend-test-taxonomy-contract]] (`FE-OC-020`).
- ⚠️ hub §20 branch decomposition의 "Contributes to" cell은 이 branch에 대해 `FE-OC-004`(env)·`FE-OC-012`(query)를 누락하고 있다. 그러나 §5.1 owner map은 `FE-REG-ENV`·`FE-REG-QUERY`를 8개 governed registry에 포함하므로, 본 note의 owner map(§1)과 위 목록은 §5.1을 따른다. hub 수정은 hub owner 소관 — 본 branch는 hub를 편집하지 않는다.
### 제외 범위
> 의도적으로 제외 — 다른 owner branch 소유. 여기서 detail을 정의하면 `OUT_OF_BRANCH_SCOPE` bleed (CLAUDE.md §15.5 R3).
- **각 registry의 실제 내용·schema field·초기 row** — 그 registry의 owner branch 소유: route [[raw/branch-notes/feature-routing-navigation-guard-contract]] (`FE-OC-005`), API operation [[raw/branch-notes/feature-api-client-response-envelope-contract]] (`FE-OC-006`), env [[raw/branch-notes/feature-frontend-env-runtime-config-contract]] (`FE-OC-004`), storage [[raw/branch-notes/feature-frontend-storage-registry-contract]] (`FE-OC-013`), error [[raw/branch-notes/feature-frontend-error-classification-boundary-contract]] (`FE-OC-008`), query key [[raw/branch-notes/feature-server-state-caching-contract]] (`FE-OC-012`), telemetry [[raw/branch-notes/feature-frontend-observability-logging-trace-contract]] (`FE-OC-014`), release token [[raw/branch-notes/feature-frontend-release-cache-rollback-contract]] (`FE-OC-016`). 본 branch는 그 schema를 *검증*할 뿐 *정의*하지 않는다.
- **test 계층·러너·fixture 분류 자체** — [[raw/branch-notes/feature-frontend-test-taxonomy-contract]] (`FE-OC-020`) 소유. 본 branch는 "어떤 test를 어떻게 짜는가"를 정의하지 않고, registry change PR에서 producer/consumer test가 *함께 움직였는지*만 검사한다.
- **version-tuple matrix, additive/breaking fixture, migration/rollback 규칙** — [[raw/branch-notes/feature-frontend-contract-compatibility-governance]] (`FE-OC-023`) 소유. 본 branch는 impact label을 *기록*하고, breaking 판정 후의 version bump·migration 메커니즘은 그 branch로 위임한다.
- **registry code generation SSOT** — [[raw/project-notes/ca-skeleton-frontend-operational-contract]] `FE-D018`의 revisit trigger(미도래). governance는 hand-maintained registry 파일을 전제로 한다.
- **payload runtime boundary schema 검증** — [[raw/branch-notes/feature-runtime-schema-validation-contract]] (`FE-OC-007`) 소유. registry schema 검증(build/test-time)과 다른 관심사.
## 근거 (필수, 최소 1개+)
| Source | 정당화하는 결정 |
|---|---|
| [[raw/official-docs/semver-2-0-0-spec-semver-official]] | compatibility impact label 중 `breaking`/`additive`/`patch` 구분의 외부 표준 기준 — `SEMVER-C1` (MAJOR/MINOR/PATCH 증가 의미론). (D3) |
| [[raw/project-notes/ca-skeleton-frontend-operational-contract]] | 8-registry 관리 결정 FE-D018, owner map §5.1, registry change protocol §5.10, decision change protocol §3.3 — governance 규칙 전체의 project-decision SSOT. (D1/D2/D3/D4/D5) |
## TODO
각 항목 옆에 증거 등급 표기. 현재 frontend repo 부재 → 전부 `planned`.
- [ ] §5.1 owner map을 governance manifest로 고정 + single-owner check(zero/duplicate owner fail) 정의 — 등급: `planned`
- [ ] 9개 registry를 owner minimum schema(§5.2~§5.9, §5.11)로 검증하는 uniform validation harness 명세 — 등급: `planned`
- [ ] orphan/ad hoc token scan = 0 (`FE-SC-005`) 규칙 + 실패 fixture 정의 — 등급: `planned`
- [ ] registry change 시 `compatibility_impact` 4-label 기록 gate + behavior-change/breaking merge block 규칙 — 등급: `planned`
- [ ] per-registry snapshot + diff artifact(owner·affected FE-OC·impact 표면화) 명세 — 등급: `planned`
- [ ] registry change 시 producer/consumer test 동기 갱신 gate(§5.10 step 5) 명세 — 검사 가능한 증거(PR touch-set + consumer-side token 참조 검증) 정의 — 등급: `planned`
## 진행 중 메모
- registry row와 branch ownership의 분리 방식 확정: **ownership은 owner map manifest가 소유, registry의 실제 row/schema는 각 owner branch가 소유.** governance harness는 registry 파일을 *읽어 검증*할 뿐 *편집*하지 않는다 — 이로써 single-owner invariant를 유지한다.
## 결정 사항
> Decision Evidence Map의 prose mirror. 근거는 Sources 또는 hub decision register.
- 2026-07-19: 8개 contract registry를 **single-owner governance model**로 관리 (FE-D018) / 이유: rename·compatibility 영향 추적 / 검토한 대안: registry code generation SSOT (FE-D018 revisit trigger) / 근거: [[raw/project-notes/ca-skeleton-frontend-operational-contract]] FE-D018 (`accepted-documented-only`).
- 2026-07-19: `§5.1` owner map을 registry 소유 SSOT로 삼고 single-owner check로 zero/duplicate owner를 차단 / 이유: registry당 정확히 1 owner invariant / 검토한 대안: 명시적 co-owner protocol(현재 미채택) / 근거: `FE-OC-022`, §5.1.
- 2026-07-19: registry change마다 `compatibility_impact` 4-label 기록, `behavior-change`/`breaking`은 migration/rollback/test evidence 없이 merge 금지 / 이유: 무증거 breaking 배포 차단 / 검토한 대안: 자유 서술 changelog / 근거: §3.3, §5.10, `SEMVER-C1` (version-tuple 메커니즘 자체는 `FE-OC-023` owner).
- 2026-07-19: uniform schema-validation harness가 각 registry를 *owner가 선언한* minimum schema로 검증 + orphan token scan 0 / 이유: ad hoc token 0 (`FE-SC-005`) 강제 / 근거: `FE-OC-022`, §5.10 step 8.
- 2026-07-19: per-registry snapshot + diff = `FE-OC-022`의 registry diff check evidence / 근거: §5.10 step 6-7.
- 2026-07-20: registry change는 **producer test와 consumer test의 동기 갱신을 검사 가능한 증거로 증명**해야 merge 가능 (§5.10 step 5) / 이유: registry row만 바뀌고 test는 이전 token을 계속 검증하면 gate가 green인 채로 계약이 깨짐(silent contract drift) / 검토한 대안: (a) 사람 리뷰 체크리스트만 두기 — 검사 불가라 기각, (b) 전부 [[raw/branch-notes/feature-frontend-test-taxonomy-contract]] (`FE-OC-020`)에 위임 — test *계층*은 그 branch 소유가 맞으나 "registry change 시점의 동기성"은 §5.10 registry change protocol의 step이므로 `FE-OC-022`가 소유 / 근거: §5.10 step 5 + step 8 orphan scan(`FE-SC-005`).
## 결정-근거 매핑
> 각 결정의 raw source claim. `Decision ID`는 이 branch-note 안에서 안정. FE-D### 참조는 hook 회피를 위해 hub project 경로에만 부착.
| Decision ID | Decision | 선택 조건 (언제 이 결정 / 언제 대안) | Supporting Claims | Evidence Strength | Open Risk |
|---|---|---|---|---|---|
| D1 | 9개 registry를 single-owner governance model로 관리 ([[raw/project-notes/ca-skeleton-frontend-operational-contract]] FE-D018 / `FE-OC-022`) | hand-maintained registry 파일 + governance gate가 default; code generation SSOT가 채택되면 generated registry로 전환 (FE-D018 revisit trigger) | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] FE-D018, §5.1 | `project-decision` (accepted-documented-only) | FE-D018은 code evidence 없는 accepted-documented-only — repo 생성 전까지 governance gate 미검증 |
| D2 | owner map §5.1이 registry 소유 SSOT; single-owner check가 zero/duplicate owner를 차단 (`FE-OC-022`) | registry당 정확히 1 owner가 invariant; 공동 소유가 필요하면 명시적 co-owner protocol을 신규 제안(planned)해야 하며 그 전엔 single-owner 강제 | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] §5.1 (`FE-OC-022`) | `project-decision` | owner map이 owner branch보다 늦게 갱신되면 `STALE_OWNER` 위험 |
| D3 | registry change마다 `compatibility_impact`(none/additive/behavior-change/breaking) 기록; behavior-change/breaking은 migration/rollback/test 없이 merge 금지 (§3.3) | `none`·`additive`는 gate 통과; `behavior-change`·`breaking`은 version bump + migration/rollback/test evidence 필요(version-tuple 메커니즘은 `FE-OC-023` owner branch) | `raw/official-docs/semver-2-0-0-spec-semver-official.md#SEMVER-C1`; [[raw/project-notes/ca-skeleton-frontend-operational-contract]] §3.3 · §5.10 | `official-doc + project-decision` | `SEMVER-C1`*무엇이* breaking인지 자동 분류하지 않음 — label 판정은 사람 판단, 오분류 위험 |
| D4 | uniform schema-validation harness가 각 registry를 owner-declared minimum schema(§5.2~§5.9)로 검증 + orphan/ad hoc token scan 0 | 각 registry schema는 owner branch가 §5.2~§5.9에서 선언; governance는 그 schema 대조 + `FE-SC-005` orphan scan만 수행, schema 내용은 재정의 안 함 | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] §5.10 step 8 (`FE-OC-022`, `FE-SC-005`) | `project-decision` | validation 라이브러리/방식 미지정(UNSUPPORTED_IMPL_DECISION); owner schema 변경 시 harness 동기화 필요 |
| D5 | per-registry snapshot + diff artifact = `FE-OC-022` registry diff check evidence | 모든 registry change에서 snapshot 재생성 + 이전 snapshot과 diff; diff는 owner·affected FE-OC·compatibility impact를 표면화 | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] §5.10 step 6-7 (`FE-OC-022`) | `project-decision` | snapshot 포맷/저장 경로 미지정(UNSUPPORTED_IMPL_DECISION) |
| D6 | registry change는 producer/consumer test 동기 갱신을 검사 가능한 증거로 증명해야 merge 가능 (§5.10 step 5) | registry token이 add/rename/remove 되면 gate 발동; 순수 주석·문서 변경이면 미발동. test *계층/러너/fixture 분류*는 [[raw/branch-notes/feature-frontend-test-taxonomy-contract]] (`FE-OC-020`) 소유, *동기성 검사*만 본 branch | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] §5.10 step 5 · step 8 (`FE-OC-022`, `FE-SC-005`) | `project-decision` | §5.10 step 5는 "함께 갱신한다"는 원칙만 말하고 *무엇이 producer/consumer test인지*·*어떤 증거로 증명하는지*를 지정하지 않음 — 판정 메커니즘은 UNSUPPORTED_IMPL_DECISION |
## 구현 가이드
> `planned` blueprint. 모든 경로는 hub §4.6 Planned directory blueprint + §5.1 owner map에서 도출(grounded)되나, frontend repo가 없으므로 전체 `planned`. 3-rule(R1 Trace / R2 UNSUPPORTED_IMPL_DECISION / R3 no OUT_OF_BRANCH_SCOPE) 준수.
### 1. Registry owner map + single-owner check
> **Trace**: D1 + D2 — [[raw/project-notes/ca-skeleton-frontend-operational-contract]] FE-D018, §5.1 (`FE-OC-022`).
>
> - **UNSUPPORTED_IMPL_DECISION**: governance manifest 파일 경로 — hub §4.6 blueprint의 `src/contracts/`에는 9개 registry 파일만 있고 governance manifest 파일이 없다. 제안: `src/contracts/registry-manifest.js` (planned). trade-off: registry 9파일 옆에 두면 응집도↑이나 registry 파일과 manifest를 혼동할 위험 → 파일명에 `-manifest` 접미로 구분.
owner map(§5.1에서 그대로 도출 — registry의 *내용*이 아니라 *소유*만 governance가 소유):
| Registry ID | Owner branch (single) | Planned registry path (§5.1) | Governed contract |
|---|---|---|---|
| `FE-REG-ROUTE` | `feature-routing-navigation-guard-contract` | `src/contracts/routes.js` | `FE-OC-005` |
| `FE-REG-API` | `feature-api-client-response-envelope-contract` | `src/contracts/api-operations.js` | `FE-OC-006` |
| `FE-REG-ENV` | `feature-frontend-env-runtime-config-contract` | `src/contracts/env.js` | `FE-OC-004` |
| `FE-REG-STORAGE` | `feature-frontend-storage-registry-contract` | `src/contracts/storage-keys.js` | `FE-OC-013` |
| `FE-REG-ERROR` | `feature-frontend-error-classification-boundary-contract` | `src/contracts/errors.js` | `FE-OC-008` |
| `FE-REG-QUERY` | `feature-server-state-caching-contract` | `src/contracts/query-keys.js` | `FE-OC-012` |
| `FE-REG-TELEMETRY` | `feature-frontend-observability-logging-trace-contract` | `src/contracts/telemetry.js` | `FE-OC-014` |
| `FE-REG-RELEASE` | `feature-frontend-release-cache-rollback-contract` | `src/contracts/release-tokens.js` | `FE-OC-016` |
single-owner check 규칙:
- registry가 manifest에 owner 0개 → `zero-owner` fail.
- registry가 owner ≥2개 → `duplicate-owner` fail.
- owner branch가 아닌 change가 registry 파일을 편집 → `non-owner-mutation` fail. **이는 repo-level ownership(누가 그 파일을 *편집*할 수 있는가) 검사이며, runtime module mutation 검사가 아니다** — 아래 §2 schema harness는 registry의 *내용*만 읽어 검증하므로 이 규칙을 집행하지 않는다.
- **UNSUPPORTED_IMPL_DECISION**: `non-owner-mutation`의 강제 메커니즘 — hub는 owner map(§5.1)에 owner branch 이름만 적고 강제 수단을 지정하지 않는다. 두 후보는 서로 다른 것을 본다: (a) **CODEOWNERS / path-glob repo ownership**`src/contracts/<registry>.js` 경로별 owner를 선언하고 non-owner PR을 review-block. owner map과 1:1로 대응해 *편집 권한*을 정확히 표현하나, git host 기능에 의존하고 CI에서 재현하려면 별도 glob 검사 스크립트가 필요. (b) **import-graph 정적 검사** ([[raw/branch-notes/feature-frontend-clean-architecture-layering-contract]] `FE-OC-002`의 dependency-cruiser 재사용) — 도구는 이미 있으나 import graph는 *누가 파일을 수정했는가*를 볼 수 없고 *어느 모듈이 registry를 import 하는가*만 본다. registry는 설계상 모든 layer가 read 목적으로 import 하므로 이 신호로는 owner 위반을 구분할 수 없다. **선택: (a) path-glob repo ownership.** trade-off: git host 종속을 받아들이는 대신 owner map invariant를 있는 그대로 검사한다. (b)는 관심사 불일치로 기각.
### 2. Uniform schema-validation harness + orphan token scan
> **Trace**: D4 — §5.10 step 8, `FE-SC-005` (`FE-OC-022`). 각 registry의 minimum schema는 owner branch가 §5.2~§5.9에서 선언 — 본 §은 그 schema를 *검증*하는 harness만 명세하며 schema field를 재정의하지 않는다 (R3).
>
> - **UNSUPPORTED_IMPL_DECISION**: validation 구현 방식 — Zod(`FE-OC-007` owner의 stack) 재사용 vs 독립 plain-JS assertion. hub 미지정. trade-off: Zod 재사용은 신규 의존 없이 통일성↑이나, registry validation은 build/test-time이라 runtime boundary(`FE-OC-007`)와 결합하면 concern 혼입 → 독립 test-time validator를 default로 두고 스키마 표현만 공유 검토.
harness 규칙(각 registry 공통, 내용 불변):
| 검사 | 규칙 | 근거 |
|---|---|---|
| required-field | registry의 각 row가 owner schema의 `Required: yes` field를 전부 보유 | §5.2~§5.9 각 owner schema |
| id-format | stable ID(routeId·operationId·storage logicalName·error kind·query namespace·telemetry eventName·release token·env key)가 owner schema가 지정한 casing 규칙 준수 | 각 owner schema |
| id-uniqueness | registry 내 stable ID 중복 0 | single-owner invariant 파생 |
| orphan-token (bidirectional) | 코드가 참조하는 모든 token이 registry에 존재 **and** registry의 모든 token이 코드에서 ≥1회 참조 → orphan 0 | §5.10 step 8, `FE-SC-005` |
| ad-hoc-token | registry를 우회한 literal(§5.1의 "Ad hoc use failure" 열 case) 검출 시 fail — 정적 강제 세부는 각 owner branch, governance는 **aggregate scan** | §5.1 |
### 3. Compatibility-impact 기록 gate
> **Trace**: D3 — §3.3 decision change protocol, §5.10 registry change protocol, `SEMVER-C1`. version-tuple/migration/rollback 메커니즘은 `FE-OC-023` owner branch로 위임 (R3 pointer).
>
> - **UNSUPPORTED_IMPL_DECISION**: impact label 기록 매체 — PR template field vs snapshot metadata vs changelog row. hub 미지정. trade-off: snapshot metadata에 넣으면 diff와 원자적이나 PR review 가시성↓ → snapshot metadata를 SSOT로, PR template은 mirror로 검토.
기록 절차(§3.3 step 3-4 + §5.10 step 3-4 도출):
1. registry change 제안 시 `compatibility_impact ∈ {none, additive, behavior-change, breaking}` 중 하나를 MUST 기록.
2. `none`·`additive` → gate 통과 (예: schema에 optional field 추가).
3. `behavior-change`·`breaking` → migration/rollback/test evidence 없이 merge block. rename은 stable ID 규칙상 breaking(§5.2 `routeId` rename=breaking 등).
4. version bump 규칙(어느 tuple을 몇으로 올릴지)·migration 실행은 `FE-OC-023` owner branch 정의를 소비 — 본 gate는 *label 존재와 evidence 유무*만 강제.
### 4. Per-registry snapshot + diff artifact
> **Trace**: D5 — §5.10 step 6-7, `FE-OC-022` minimum evidence(registry diff check).
>
> - **UNSUPPORTED_IMPL_DECISION**: snapshot 포맷(JSON vs serialized JS) + 저장 경로 — hub §4.6 `artifacts/`에 registry 전용 subdir 없음. 제안: `artifacts/quality/registry-snapshots/<registry-id>.json` (planned). trade-off: JSON은 도구 독립 diff가 쉬우나 registry가 JS 함수(query-key factory 등)를 포함하면 직렬화 손실 → 함수형 registry는 shape/서명만 snapshot.
- 각 registry change마다 snapshot 재생성 후 직전 snapshot과 diff.
- diff는 최소 다음을 표면화: added/removed/renamed token, owner, affected `FE-OC-*`, `compatibility_impact`.
- orphan token ≠ 0 이면 merge 불가 (§5.10 step 8).
### 5. Producer/consumer test 동기 갱신 gate
> **Trace**: D6 — §5.10 step 5("producer와 consumer test를 함께 갱신한다") + step 8 orphan scan (`FE-OC-022`, `FE-SC-005`). test 계층·러너·fixture 분류는 [[raw/branch-notes/feature-frontend-test-taxonomy-contract]] (`FE-OC-020`) 소유 — 본 §은 *registry change 시점의 동기성*만 명세한다 (R3).
>
> - **UNSUPPORTED_IMPL_DECISION**: producer/consumer test의 식별 방식 — hub §5.10 step 5는 원칙만 말하고 "무엇이 producer test이고 무엇이 consumer test인지", "동기 갱신을 어떤 증거로 증명하는지"를 지정하지 않는다. 후보: (a) **PR touch-set 규칙** — registry 파일이 바뀐 PR은 대응 test 경로도 함께 touch 해야 통과. 구현이 단순하나 *빈 수정*으로 우회 가능. (b) **token-reference 검사** — §2의 bidirectional orphan scan을 test 소스까지 확장해, consumer test가 registry에 더 이상 없는 token을 참조하면 fail. 우회 불가하나 remove/rename만 잡고 *추가된 token에 test가 없는 경우*는 못 잡는다. **선택: (a)+(b) 동시 적용** — (b)가 정확성을, (a)가 커버리지(신규 token)를 담당. trade-off: 검사 2개를 유지해야 하고 (a)는 우회 가능성이 남지만, 하나만 쓰면 rename(=breaking, §5.2)이나 신규 token 중 한쪽이 무검사로 통과한다.
gate 규칙:
| 검사 | 규칙 | 실패 라벨 | 근거 |
|---|---|---|---|
| touch-set | registry 파일의 token 집합이 변한 PR은 해당 registry의 producer test와 consumer test 경로를 함께 수정해야 함 (주석·포맷만 바뀐 change는 미발동) | `unsynced-registry-test` | §5.10 step 5 |
| token-reference (test 확장) | test 소스가 참조하는 registry token이 registry에 존재해야 함 — registry에서 제거·rename된 token을 test가 계속 참조하면 fail | `stale-test-token` | §5.10 step 5 + step 8 (`FE-SC-005`) |
| new-token coverage | registry에 새로 추가된 token은 producer/consumer 양쪽에서 ≥1회 test 참조되어야 함 | `untested-new-token` | §5.10 step 5 + step 8 bidirectional orphan 규칙의 test-side 확장 |
- 본 gate의 producer/consumer 정의는 registry별로 owner branch가 §5.2~§5.9 schema와 함께 선언한 stable ID를 기준으로 한다 — governance는 그 ID 집합의 *변화*와 test 참조를 대조할 뿐, test 내용을 규정하지 않는다.
- 실행 지점: registry change PR의 merge gate. CI stage 배선(어느 workflow job에서 도는지)은 [[raw/branch-notes/feature-frontend-ci-quality-gates-contract]] 소유이고, `FE-GATE-005@1`(unit gate — all registries fixture 포함) 자체의 owner 는 [[raw/branch-notes/feature-frontend-test-taxonomy-contract]] 다(hub §2.1.1).
## 엣지·실패·의존
- **실패·엣지 경로**:
- `duplicate-owner`: 두 owner branch가 같은 registry 소유 주장 → single-owner check fail (D2).
- `zero-owner`: registry가 owner map에 owner 없음(orphan registry) → fail (D2).
- `renamed-token-without-label`: stable ID rename인데 `compatibility_impact` 미기록/`breaking` 미표기 → gate block (D3, §5.2 rename=breaking).
- `orphan-token`: 코드가 참조하나 registry 부재, 또는 registry row가 코드에서 미참조 → `FE-SC-005` 위반 (D4).
- `missing-impact-label`: registry change에 `compatibility_impact` 누락 → gate block (D3).
- `unevidenced-breaking`: `behavior-change`/`breaking`인데 migration/rollback/test evidence 없음 → merge block (D3).
- `ad-hoc-token`: literal route path / raw `localStorage` key / 자유 문자열 event 등 registry 우회 → §5.1 "Ad hoc use failure" (정적 강제는 각 owner, governance는 aggregate scan).
- `unsynced-registry-test`: registry token 집합이 바뀐 PR이 producer/consumer test를 함께 수정하지 않음 → §5.10 step 5 위반, merge block (D6).
- `stale-test-token`: test가 registry에서 제거·rename된 token을 계속 참조 → gate fail. registry만 바뀌고 test는 green으로 남는 silent contract drift의 주 경로 (D6).
- `untested-new-token`: registry에 추가된 token이 producer/consumer test 어느 쪽에서도 참조되지 않음 → gate fail (D6).
- **다른 계약 의존** (sibling 링크는 `FE-OC-###`로만 참조):
- upstream: [[raw/branch-notes/feature-frontend-project-bootstrap-toolchain-contract]] (`FE-OC-003`) — checkJs/test toolchain 위에서 harness 실행. [[raw/branch-notes/feature-frontend-clean-architecture-layering-contract]] (`FE-OC-002`) — `contracts/` 레이어 소유 + layer 간 import 규칙(§4.3 dependency matrix는 `domain`/`application`/`presentation`/`adapters`/`bootstrap` **layer** 단위 import 허용/금지를 정의하며, registry 파일별 branch ownership을 정의하지 않는다). 따라서 `non-owner-mutation` 강제는 §4.3에서 도출되지 않고 본 note §1의 path-glob repo ownership 선택(UNSUPPORTED_IMPL_DECISION)이 소유한다.
- downstream(본 branch를 consume): [[raw/branch-notes/feature-frontend-contract-compatibility-governance]] (`FE-OC-023`) — `compatibility_impact` 기록을 소비해 version-tuple/migration 판정. [[raw/branch-notes/feature-frontend-ci-quality-gates-contract]] (`FE-OC-022`) — governance gate를 CI blocking gate로 실행.
- registry supplier(8 owner가 registry+schema 제공): routing(`FE-OC-005`), api-client(`FE-OC-006`), env(`FE-OC-004`), storage(`FE-OC-013`), error(`FE-OC-008`), server-state(`FE-OC-012`), observability(`FE-OC-014`), release-cache(`FE-OC-016`). 이 중 하나라도 schema를 바꾸면 §2 harness가 동기화돼야 함.
## 검증해야 할 주장
| Claim | Why uncertain | How to verify | Status |
|---|---|---|---|
| 각 registry가 정확히 1 primary owner를 가진다 | branch·code 없음, owner map manifest 미구현 | single-owner check fixture: duplicate/zero-owner manifest fixture가 fail (§20 measurable: single-owner checks) | `needs-confirmation` |
| 각 registry row가 owner minimum schema를 만족한다 | schema harness 미구현 | schema validation fixture: required-field 누락 row가 fail (§20 measurable: schema validation) | `needs-confirmation` |
| orphan/ad hoc token scan이 0 (`FE-SC-005`) | frontend code 없음 | bidirectional orphan token scan fixture (registry↔code) | `needs-confirmation` |
| 모든 registry change가 `compatibility_impact`를 기록한다 | gate 미구현 | change-protocol gate fixture: label 없는 change가 fail | `needs-confirmation` |
| snapshot diff가 affected FE-OC + compatibility impact를 표면화한다 | snapshot 미구현 | snapshot diff test: additive vs breaking fixture의 diff 비교 (§20 measurable: 9 registry snapshots) | `needs-confirmation` |
| registry change 시 producer/consumer test가 함께 갱신됨을 gate가 검출한다 (§5.10 step 5) | gate 미구현, hub는 원칙만 진술하고 판정 메커니즘 미지정 | 3개 negative fixture: (1) registry token rename + test 미수정 PR → `unsynced-registry-test` fail, (2) registry에서 제거된 token을 참조하는 test → `stale-test-token` fail, (3) test 참조 없는 신규 token → `untested-new-token` fail | `needs-confirmation` |
| `non-owner-mutation`을 path-glob repo ownership으로 검사할 수 있다 | CODEOWNERS/glob 검사 미구현, git host 기능 종속 | owner map의 9 registry path glob과 ownership 선언이 1:1 대응하는지 대조 + non-owner 경로 수정 fixture가 block 되는지 확인 | `needs-confirmation` |
## 관심사 커버리지 (coverage-auditor 자동 생성 — 있을 때)
- 스캐폴딩 단계: `/coverage` 실행 전 수동 행을 만들지 않는다.
## 마주친 문제
- 없음 — 스캐폴딩 단계.
## 묶음 (이 branch에서 파생된 자료)
<!-- GENERATED: project-contract-imports:start -->
## 가져온 프로젝트 계약
| Ref | Owner | 요약 | Branch 적용 |
|---|---|---|---|
| `FE-OC-004@1` | [[raw/branch-notes/feature-frontend-env-runtime-config-contract]] | build-time, runtime-public, secret config를 MUST 분리하고 boot 전에 runtime config를 검증 | import 참조로 적용 |
| `FE-OC-005@1` | [[raw/branch-notes/feature-routing-navigation-guard-contract]] | route ID/path/params/access/loading/error owner는 route registry 하나여야 함 | import 참조로 적용 |
| `FE-OC-006@1` | [[raw/branch-notes/feature-api-client-response-envelope-contract]] | 모든 HTTP는 shared client를 MUST 통과하고 timeout·abort·response parsing을 page에서 구현하면 안 됨 | import 참조로 적용 |
| `FE-OC-008@1` | [[raw/branch-notes/feature-frontend-error-classification-boundary-contract]] | 모든 failure는 stable frontend error kind로 MUST 정규화하고 raw body·stack을 UI에 노출하면 안 됨 | import 참조로 적용 |
| `FE-OC-012@1` | [[raw/branch-notes/feature-server-state-caching-contract]] | query key와 invalidation은 registry factory만 MUST 사용 | import 참조로 적용 |
| `FE-OC-013@1` | [[raw/branch-notes/feature-frontend-storage-registry-contract]] | storage key는 namespace·version·classification을 MUST 가지며 token/secret 저장을 금지 | import 참조로 적용 |
| `FE-OC-014@1` | [[raw/branch-notes/feature-frontend-observability-logging-trace-contract]] | telemetry는 best-effort이며 render·API success를 차단하면 안 되고 PII·token을 전송하면 안 됨 | import 참조로 적용 |
| `FE-OC-016@1` | [[raw/branch-notes/feature-frontend-release-cache-rollback-contract]] | HTML, asset, runtime config, release manifest cache policy를 MUST 구분 | import 참조로 적용 |
| `FE-OC-020@1` | [[raw/branch-notes/feature-frontend-test-taxonomy-contract]] | gate 종류별 책임·fixture·artifact를 분리하고 실패를 warning으로 낮추면 안 됨 | import 참조로 적용 |
| `FE-OC-023@1` | [[raw/branch-notes/feature-frontend-contract-compatibility-governance]] | API/config/storage/release schema의 breaking change는 migration 또는 version bump 없이 배포하면 안 됨 | import 참조로 적용 |
<!-- GENERATED: project-contract-imports:end -->
- 없음 — 자식 자료는 생성 후 controller가 parent Cluster와 함께 등록한다.
## 관련 일일 노트
- 없음 — daily note는 이 작업에서 수정하지 않는다.
## 완료 후 정리
- PR 링크: 없음
- 리뷰 메모: 없음
- 머지 결과 / 배포 환경: `planned`
- **wiki 추출 대상** (verified만): 없음
- **추출하지 않을 항목** (planned / documented-only / abandoned): 현재 전체