From 8c94190c3205e0de3428f2d8146031b29b85ff96 Mon Sep 17 00:00:00 2001 From: DongHyeonka Date: Tue, 28 Jul 2026 14:07:22 +0900 Subject: [PATCH] =?UTF-8?q?docs:=20=EB=9F=B0=ED=83=80=EC=9E=84=20adapter?= =?UTF-8?q?=20feature=20=ED=99=95=EC=9E=A5=20=EA=B5=AC=ED=98=84=20?= =?UTF-8?q?=EA=B3=84=ED=9A=8D=2012=EA=B0=9C=20Task?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 설계 스펙의 41개 편집 항목 + 신규 파일 6개 + 기존 파일 7개를 Task 12개로 분해. 코드가 아니라 계약 문서 편집이므로 TDD 순환을 "체크 명령 실패 확인 -> 편집 -> 통과 확인 -> 커밋" 으로 대체했다. 하네스가 삭제돼 결정론 검사기가 없으므로 각 Task 가 자체 grep 검증을 들고 다니고, Task 12 가 참조 무결성·개수 정합·위임 왕복 대조·wikilink 유효성을 총점검한다. Co-Authored-By: Claude Opus 5 (1M context) --- ...leton-frontend-runtime-adapter-features.md | 1375 +++++++++++++++++ 1 file changed, 1375 insertions(+) create mode 100644 docs/superpowers/plans/2026-07-28-ca-skeleton-frontend-runtime-adapter-features.md diff --git a/docs/superpowers/plans/2026-07-28-ca-skeleton-frontend-runtime-adapter-features.md b/docs/superpowers/plans/2026-07-28-ca-skeleton-frontend-runtime-adapter-features.md new file mode 100644 index 0000000..d936e9c --- /dev/null +++ b/docs/superpowers/plans/2026-07-28-ca-skeleton-frontend-runtime-adapter-features.md @@ -0,0 +1,1375 @@ +# CA Skeleton Frontend 런타임 adapter feature 확장 — 구현 계획 + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** `raw/project-notes/ca-skeleton-frontend-operational-contract.md` 에 6개 능력 도메인(파일 I-O·캐시 계층·대용량 전송·멀티프로토콜·실시간·백그라운드 실행)을 port·adapter·registry·gate 계층까지 계약으로 심고, 대응 branch-note 6개를 만들고, 파급되는 기존 branch-note 7개를 정합시킨다. + +**Architecture:** 이 작업은 **코드가 아니라 계약 문서 편집**이다. 산출물은 `FE-OC-027`~`032`, `FE-GATE-027`~`033`, `FE-D026`~`036`, `DEC-…` 11건, `DELEG-FE-007`~`011`, `FLOW-FE-EVENT-001`~`005`, `FE-NFR-016`~`020`, `FE-RB-006`~`007`, `FE-REG-CAPABILITY` 이며 전부 hub 한 파일 안에 산다. 검증은 테스트 러너가 아니라 **결정론적 grep 명령**이다 — 각 Task 는 "체크 명령을 먼저 돌려 실패를 확인 → 편집 → 같은 명령으로 통과를 확인 → 커밋" 순환을 따른다. + +**Tech Stack:** Markdown, YAML frontmatter, Obsidian wikilink, `grep`/`grep -E`/`grep -F`. 빌드·테스트 러너 없음. + +## Global Constraints + +- **SSOT 는 설계문서다.** 모든 표 내용·문자열·수치는 `docs/superpowers/specs/2026-07-28-ca-skeleton-frontend-runtime-adapter-features-design.md`(이하 **spec**)에서 가져온다. 계획서가 spec 과 어긋나면 spec 이 이긴다. +- **작업 브랜치**: `docs/ca-skeleton-frontend-runtime-adapter-features` (이미 생성됨, spec 커밋 `4533122` 위에서 이어간다). +- **편집 대상 hub**: `raw/project-notes/ca-skeleton-frontend-operational-contract.md` (현재 2420행). +- **`FE-OC-*`·`FE-D*`·`FE-GATE-*`·`DEC-*` ID 는 rename 금지.** 의미가 바뀌면 기존 ID 를 `superseded` 로 남기고 새 ID 를 추가한다 (hub §2.1). +- **§6.1 Summary 셀과 branch-note 상속 표는 문자열이 정확히 일치해야 한다.** 조사 앞 공백 없음 — `…release token을 8개 registry로 관리한다` 형태. 공백 하나가 불일치다. +- **증거 등급**: 신규 행의 `Status`/`Current` 는 전부 `planned` 또는 `FAIL_UNVERIFIED`. `actually-implemented`·`locally-verified`·`prod-verified` 를 쓰면 안 된다 (hub §0.2). +- **근거 표기**: 신규 결정의 `Evidence / rationale` 열에는 `project-local default, 외부 source claim 아님` 을 명시한다. 없는 출처를 지어내지 않는다. +- **out of scope 를 문서에 못박는다**: 6개 도메인의 use case·domain model·business rule 은 계약하지 않는다. +- **하네스 부재**: `.claude/commands/`·`.claude/hooks/`·`harness/` 가 삭제돼 `/branch`·`/lint`·`/sync` 와 결정론 검사기가 없다. branch-note 는 `templates/branch-note-template.md` 로 직접 만들고, 정합은 이 계획의 grep 명령으로 확인한다. +- **`ART-FE-*` 행을 추가하지 않는다.** 신규 gate report 는 전부 단일 branch 소유이므로 §2.1.3 등록 대상이 아니며, 끊긴 `harness/source/**` 경로를 늘리지 않는다. +- **커밋 메시지 규약**: `docs: <내용>` + 본문. 각 Task 끝에서 커밋한다. + +**작업 전 필독:** spec 전문(886행). 각 Task 는 spec 의 특정 절을 그대로 옮기는 일이며, 절 번호를 Task 마다 명시한다. + +--- + +## File Structure + +| 파일 | 상태 | 책임 | +|---|---|---| +| `raw/project-notes/ca-skeleton-frontend-operational-contract.md` | 수정 | 41개 절. 계약·결정·port·registry·gate·NFR·runbook 의 project-wide SSOT | +| `raw/branch-notes/feature-frontend-binary-file-io-store-contract.md` | 신규 | `FE-OC-027` owner | +| `raw/branch-notes/feature-frontend-cache-tier-cross-tab-invalidation-contract.md` | 신규 | `FE-OC-028` owner | +| `raw/branch-notes/feature-frontend-large-object-transfer-contract.md` | 신규 | `FE-OC-029` owner | +| `raw/branch-notes/feature-frontend-multi-protocol-api-transport-contract.md` | 신규 | `FE-OC-030` owner | +| `raw/branch-notes/feature-frontend-realtime-subscription-lifecycle-contract.md` | 신규 | `FE-OC-031` owner | +| `raw/branch-notes/feature-frontend-background-execution-worker-contract.md` | 신규 | `FE-OC-032` owner | +| `raw/branch-notes/feature-frontend-release-cache-rollback-contract.md` | 수정 | `OFFLINE-CACHE-001` pin `@1`→`@2` + 위임 접수 | +| `raw/branch-notes/feature-frontend-storage-registry-contract.md` | 수정 | `REGISTRY-001` 문자열 + 위임 접수 | +| `raw/branch-notes/feature-frontend-observability-logging-trace-contract.md` | 수정 | `REGISTRY-001` 문자열 | +| `raw/branch-notes/feature-frontend-contract-registry-governance.md` | 수정 | `REGISTRY-001` 문자열 | +| `raw/branch-notes/feature-frontend-contract-compatibility-governance.md` | 수정 | `REGISTRY-001` 문자열 | +| `raw/branch-notes/feature-runtime-schema-validation-contract.md` | 수정 | 위임 접수 | +| `raw/branch-notes/feature-frontend-env-runtime-config-contract.md` | 수정 | `FE-REG-CAPABILITY`·`FE-GATE-033` owner 취득 | + +hub 를 쪼개지 않는 이유: 27개 기존 branch-note 와 4개 rules 문서가 `§N.M` 절 번호로 이 파일을 참조한다. 분할하면 그 참조가 전부 깨진다. 절 추가는 **뒤에 붙이는 방식**(§5.11, §9.5~§9.7)으로만 하고 기존 번호를 밀지 않는다. + +--- + +## 공통 검증 명령 + +각 Task 에서 반복 사용한다. 모두 repo 루트에서 실행. + +```bash +HUB=raw/project-notes/ca-skeleton-frontend-operational-contract.md +BN=raw/branch-notes +``` + +**ID 정의 개수 세기** (표 행은 `| \`ID\`` 로 시작): + +```bash +grep -cE '^\| `FE-OC-(027|028|029|030|031|032)`' "$HUB" +``` + +**참조 무결성** (hub §24.2 의 drift check 를 그대로 사용): + +```bash +CONTRACT_STABLE_ID_RE='FE-D[0-9]{3}|FE-(SC|OC|NFR|GATE|RB|EV|RDY|RISK|Q)-[0-9]{3}|FE-NFR-C[0-9]{2}|FE-REG-[A-Z]+(-[A-Z]+)*' +comm -23 \ + <(grep -oE "$CONTRACT_STABLE_ID_RE" "$HUB" | sort -u) \ + <(grep -nE '^\| `FE-|^### 16\.[0-9]+ `FE-RB-' "$HUB" | grep -oE "$CONTRACT_STABLE_ID_RE" | sort -u) +``` + +출력이 비어야 한다 — "참조되지만 정의되지 않은 ID" 가 없다는 뜻이다. + +--- + +### Task 1: hub 계약·게이트 인덱스 (§2.1, §2.1.1) + +**Files:** +- Modify: `raw/project-notes/ca-skeleton-frontend-operational-contract.md` — §2.1 표 (현재 185–210행), §2.1.1 표 (현재 225–276행) + +**Interfaces:** +- Produces: `FE-OC-027`·`FE-OC-028`·`FE-OC-029`·`FE-OC-030`·`FE-OC-031`·`FE-OC-032` 계약 ID, `FE-GATE-027`~`FE-GATE-033` gate ID. 이후 모든 Task 가 이 ID 를 참조한다. +- Consumes: 없음 (첫 Task) + +- [ ] **Step 1: 체크 명령을 돌려 아직 없음을 확인** + +```bash +HUB=raw/project-notes/ca-skeleton-frontend-operational-contract.md +grep -cE '^\| `FE-OC-(027|028|029|030|031|032)`' "$HUB" +grep -cE '^\| `FE-GATE-(027|028|029|030|031|032|033)`' "$HUB" +``` + +Expected: 두 명령 모두 `0` 출력, exit code 1. + +- [ ] **Step 2: §2.1 Stable Contract Index 에 6행 추가** + +`| \`FE-OC-026\` | project hub (this file) | …` 행 **바로 다음 줄**에 spec §4.2 의 표 6행을 그대로 붙인다. 열 순서는 `Contract ID | Single owner | Normative summary | Minimum evidence | Status` 이고 `Status` 는 전부 `` `planned` `` 다. + +- [ ] **Step 3: 같은 표에서 기존 2행의 summary 갱신** + +`FE-OC-022` 행: `8개 registry는 single primary owner와 compatibility impact를 MUST 기록` → `9개 registry는 single primary owner와 compatibility impact를 MUST 기록` + +`FE-OC-025` 행: `boot, chunk mismatch, API degradation, telemetry failure, rollback runbook을 MUST 유지` → `boot, chunk mismatch, API degradation, telemetry failure, rollback, realtime 연결, background 실행 runbook을 MUST 유지` + +- [ ] **Step 4: §2.1.1 Contract Registry (typed) 에 계약 6행 추가** + +`| \`FE-OC-026\` | \`fe.answer-boundary\` | …` 행 **바로 다음 줄**(= `FE-GATE-001` 행 앞)에 spec §4.3 의 표 6행을 붙인다. 열 순서는 `Contract ID | Concern Key | Revision | Type | Owner | Trigger | Required Effect | Enforcement | Status`, `Revision` 은 `1`, `Type` 은 `operational-contract`, `Status` 는 `active`. + +같은 표의 `FE-OC-022`·`FE-OC-025` 행 `Required Effect` 셀도 Step 3 과 동일하게 갱신한다 — 이 표와 §2.1 은 같은 문장을 두 곳에 두므로 한쪽만 고치면 문서가 자기모순이 된다. + +- [ ] **Step 5: §2.1.1 표 끝에 gate 7행 추가** + +`| \`FE-GATE-026\` | \`fe.gate.lab-performance\` | …` 행 **다음**에 spec §8.2 의 표 7행을 붙인다. `Type` 은 `gate`, `Revision` 은 `1`, `Status` 는 `active`. + +- [ ] **Step 6: 체크 명령을 다시 돌려 통과 확인** + +```bash +HUB=raw/project-notes/ca-skeleton-frontend-operational-contract.md +echo "FE-OC 신규(기대 12): $(grep -cE '^\| `FE-OC-(027|028|029|030|031|032)`' "$HUB")" +echo "FE-GATE 신규(기대 7): $(grep -cE '^\| `FE-GATE-(027|028|029|030|031|032|033)`' "$HUB")" +echo "8개 registry 잔존(기대 0): $(grep -c '8개 registry는 single primary owner' "$HUB")" +echo "runbook 5종 문구 잔존(기대 0): $(grep -c 'telemetry failure, rollback runbook을 MUST 유지' "$HUB")" +``` + +Expected: `12`, `7`, `0`, `0`. +(`FE-OC-027`~`032` 는 §2.1 과 §2.1.1 양쪽에 1행씩이므로 12 다. gate 는 이 시점에 §2.1.1 에만 있으므로 7 이다.) + +- [ ] **Step 7: 커밋** + +```bash +git add raw/project-notes/ca-skeleton-frontend-operational-contract.md +git commit -m "docs(hub): FE-OC-027~032 계약과 FE-GATE-027~033 gate 인덱스 추가 + +6개 능력 도메인 계약을 §2.1 과 §2.1.1 양쪽에 등록하고, +registry 개수(8→9)와 runbook 종수(5→7) 변경을 FE-OC-022·FE-OC-025 +summary 에 반영." +``` + +--- + +### Task 2: hub 결정 레지스트리 (§3.2, §6.1) + +**Files:** +- Modify: hub §3.2 Decision rows (현재 364–390행), §6.1 안정 결정 레지스트리 (현재 408–439행) + +**Interfaces:** +- Consumes: Task 1 의 `FE-OC-027`~`032` (신규 결정의 `Affected FE-OC` 열이 참조) +- Produces: `FE-D026`~`FE-D036`, `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-{CAPABILITY,BINARY-STORE,CROSS-TAB,TRANSFER-CREDENTIAL,RESUMABLE-TRANSFER,PROTOCOL,REALTIME-TRANSPORT,REALTIME-LIFECYCLE,SERVICE-WORKER-ROLE,BACKGROUND-SYNC,WORKER-TASK}-001`. Task 9·10·11 이 이 문자열을 그대로 pin·복사한다. + +**이 Task 가 가장 위험하다.** §6.1 Summary 문자열은 branch-note 7개가 글자 단위로 복사하므로 오타 하나가 문서 간 모순이 된다. + +- [ ] **Step 1: 체크 명령을 돌려 아직 없음을 확인** + +```bash +HUB=raw/project-notes/ca-skeleton-frontend-operational-contract.md +grep -cE '^\| `FE-D0(2[6-9]|3[0-6])`' "$HUB" +grep -c 'DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-CAPABILITY-001' "$HUB" +``` + +Expected: 두 명령 모두 `0`. + +- [ ] **Step 2: §3.2 에 `FE-D026`~`FE-D036` 11행 추가** + +`| \`FE-D025\` | sample slice는 …` 행 다음에 spec §7.1 의 표 11행을 붙인다. 열 순서는 `Decision ID | Decision | Status | Owner | Affected FE-OC | Evidence / rationale | Revisit trigger | Supersedes`. + +`FE-D034` 행의 `Supersedes` 열은 `` `FE-D019` `` 다. 나머지 10행은 `—`. + +- [ ] **Step 3: §3.2 의 기존 2행 갱신** + +`FE-D019` 행 — `Status` 를 `` `conditional-default` `` → `` `superseded` `` 로 바꾼다. **행을 삭제하지 않는다** (hub §3.3 5단계). + +`FE-D018` 행 — `Decision` 셀을 `route/API-operation/env/storage/error/query/telemetry/release token은 8개 registry로 관리` → `route/API-operation/env/storage/error/query/telemetry/release/capability token은 9개 registry로 관리` 로 바꾼다. + +- [ ] **Step 4: §6.1 에 `DEC-…` 11행 추가** + +`| \`DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-SAMPLE-FIXTURE-001\` | …` 행 다음에 spec §7.2 의 표 11행을 붙인다. 열 순서는 `Decision ID | Revision | Domain | Decision Summary | Status | Owner | Evidence`, `Owner` 는 전부 `[[raw/project-notes/ca-skeleton-frontend-operational-contract]]`. + +**Summary 셀 11개는 아래 문자열을 그대로 쓴다. 조사 앞 공백 없음:** + +```text +신규 runtime capability 6종은 FE-REG-CAPABILITY flag로 default OFF이며 활성화는 owner·gate·runbook을 동반한다 +로컬 바이너리 backend는 IndexedDB를 default로 하고 OPFS는 대용량 순차 write에 opt-in, Cache Storage는 service worker 호스팅 response cache 전용이다 +탭 간 무효화는 BroadcastChannel 우선에 storage event fallback을 쓰고 leader election 없이 무효화 key만 전파한다 +presigned URL 획득은 shared client를 경유하고 실제 byte 전송은 session credential을 첨부하지 않는 transfer adapter가 수행한다 +재개 가능 전송은 part size·병렬도·part 재시도 상한을 registry로 고정하고 part 상태를 BlobStorePort에 보존한다 +transport default는 REST이고 GraphQL·gRPC-Web·Connect-Web은 FE-REG-API의 protocol 필드로 opt-in하며 미지원 환경은 REST gateway로 fallback한다 +실시간 transport는 SSE를 우선하고 양방향이 필요하면 WebSocket, 둘 다 불가할 때만 최소 간격·backoff·visibility gating을 갖춘 bounded polling을 쓴다 +실시간 연결은 full jitter backoff와 30초 cap을 쓰고 재시도 상한 후 terminal 상태로 전이하며 resume은 Last-Event-ID 또는 cursor로 수행하고 unmount 시 구독을 해제한다 +service worker는 역할을 분리해 release asset precaching은 default off로 유지하고 push·background sync·Cache Storage 호스트 역할만 capability opt-in으로 허용하며 update UX 계약을 요구한다 +background sync 재생은 idempotency keyed operation만 허용한다 +Web Worker 작업은 structured-clone 또는 Transferable로만 통신하고 timeout과 terminate를 계약하며 worker 안에서 application port를 재구현하지 않는다 +``` + +- [ ] **Step 5: §6.1 의 기존 2행 개정** + +`DEC-…-OFFLINE-CACHE-001` 행 — `Revision` `1`→`2`, Summary 를 아래로 교체: + +- 교체 전: `service worker와 offline asset cache는 default off다` +- 교체 후: `service worker의 release asset precaching은 default off이고 push·background sync·Cache Storage 호스트 역할만 capability opt-in으로 허용한다` + +`DEC-…-REGISTRY-001` 행 — `Revision` 은 **1 유지**, Summary 를 아래로 교체: + +- 교체 전: `route, API operation, env, storage, error, query, telemetry, release token을 8개 registry로 관리한다` +- 교체 후: `route, API operation, env, storage, error, query, telemetry, release token, capability를 9개 registry로 관리한다` + +- [ ] **Step 6: §6.1 하단 개정 기록에 2건 추가** + +기존 `> **개정 기록 (§3.3 protocol)**` 블록의 `- 2026-07-21 · \`DEC-...-SUPPLY-CHAIN-001\` …` 항목 다음에 아래 두 줄을 넣는다. + +```markdown +> - 2026-07-28 · `DEC-...-OFFLINE-CACHE-001` · `compatibility_impact: behavior-change` · revision 1→2. `FE-D034` 가 `FE-D019` 를 supersede 하면서 service worker 를 "전면 off" 에서 "precaching off + 역할별 capability opt-in" 으로 바꿨다. WebPush·Background Sync·Cache Storage 가 service worker 없이는 동작하지 않기 때문이다. behavior-change 이므로 §3.3 4단계에 따라 `FE-GATE-032`(SW update UX + rollback 시 SW 되돌림)와 `FE-RB-007` 이 migration·rollback evidence 를 담당한다. pin 을 가진 `feature-frontend-release-cache-rollback-contract` 는 `@2` 로 갱신했다. +> - 2026-07-28 · `DEC-...-REGISTRY-001` · `compatibility_impact: additive` · revision 유지(1). 9번째 registry `FE-REG-CAPABILITY` 를 추가했다. 기존 8개 registry 의 owner·schema·동작은 바뀌지 않으므로 `DEC-...-SUPPLY-CHAIN-001` 선례와 같은 판정을 적용한다. 다만 Summary 문자열이 바뀌므로 이를 복사해 둔 소비 branch 4곳(`storage-registry`·`observability-logging-trace`·`contract-registry-governance`·`contract-compatibility-governance`)의 상속 표를 함께 갱신했다. +``` + +- [ ] **Step 7: 체크 명령을 다시 돌려 통과 확인** + +```bash +HUB=raw/project-notes/ca-skeleton-frontend-operational-contract.md +echo "FE-D 신규(기대 11): $(grep -cE '^\| `FE-D0(2[6-9]|3[0-6])`' "$HUB")" +echo "DEC 신규(기대 11): $(grep -cE '^\| `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-(CAPABILITY|BINARY-STORE|CROSS-TAB|TRANSFER-CREDENTIAL|RESUMABLE-TRANSFER|PROTOCOL|REALTIME-TRANSPORT|REALTIME-LIFECYCLE|SERVICE-WORKER-ROLE|BACKGROUND-SYNC|WORKER-TASK)-001`' "$HUB")" +echo "FE-D019 superseded(기대 1): $(grep -cE '^\| `FE-D019`.*`superseded`' "$HUB")" +echo "구 OFFLINE-CACHE 문자열 잔존(기대 0): $(grep -cF 'service worker와 offline asset cache는 default off다' "$HUB")" +echo "구 REGISTRY 문자열 잔존(기대 0): $(grep -cF 'release token을 8개 registry로 관리한다' "$HUB")" +echo "신규 REGISTRY 문자열(기대 1): $(grep -cF 'release token, capability를 9개 registry로 관리한다' "$HUB")" +``` + +Expected: `11`, `11`, `1`, `0`, `0`, `1`. + +- [ ] **Step 8: 커밋** + +```bash +git add raw/project-notes/ca-skeleton-frontend-operational-contract.md +git commit -m "docs(hub): 결정 11건 추가, FE-D019 supersede, registry 개수 개정 + +- FE-D026~036 / DEC-... 11건: capability default-off, 바이너리 backend + 선택, 탭 간 무효화, 전송 credential 경계, 프로토콜 opt-in, 실시간 + transport·수명주기, SW 역할 분리, background sync, worker 통신 +- FE-D019 -> superseded (FE-D034 가 대체), OFFLINE-CACHE-001 revision 2 + (behavior-change) +- REGISTRY-001 은 additive 이므로 revision 유지, Summary 만 9개로 갱신 +- §6.1 개정 기록 2건 추가" +``` + +--- + +### Task 3: hub 위임·흐름 레지스트리 (§2.1.2, §2.1.4) + +**Files:** +- Modify: hub §2.1.2 Delegation Registry (현재 317–324행), §2.1.4 Flow Stage Registry (현재 298–307행) + +**Interfaces:** +- Consumes: Task 1 의 계약 ID +- Produces: `DELEG-FE-007`~`DELEG-FE-011`, `FLOW-FE-EVENT-001`~`FLOW-FE-EVENT-005`. Task 10·11 이 `delegates:`/`accepts_delegations:`/`imports:` 에서 참조한다. + +- [ ] **Step 1: 체크 명령을 돌려 아직 없음을 확인** + +```bash +HUB=raw/project-notes/ca-skeleton-frontend-operational-contract.md +grep -cE '^\| `DELEG-FE-0(07|08|09|10|11)`' "$HUB" +grep -cE '^\| `FLOW-FE-EVENT-00[1-5]`' "$HUB" +``` + +Expected: 둘 다 `0`. + +- [ ] **Step 2: §2.1.2 에 위임 5행 추가** + +`| \`DELEG-FE-006\` | …` 행 다음에 spec §7.4 의 표 5행을 붙인다. 열 순서는 `Delegation ID | Concern Key | Revision | Delegator | Delegate | Scope | Status`, `Revision` 은 `1`. + +`Status` 는 **전부 `accepted`** 로 적는다 — Task 10 과 Task 11 에서 delegate 5곳의 `accepts_delegations` 를 모두 채우기 때문이다. 접수하지 않은 채 `accepted` 로 적으면 거짓이므로, Task 11 이 끝날 때 Step 5 의 왕복 대조로 확인한다. + +- [ ] **Step 3: §2.1.2 서두 설명 갱신** + +기존 문단의 `아래 6행은 2026-07-20 문서 간 정합성 감사에서 …` 문장 뒤에 아래를 덧붙인다. + +```markdown +> 2026-07-28 에 `DELEG-FE-007`~`DELEG-FE-011` 5행이 추가됐다. 신규 6개 능력 도메인이 서로 또는 기존 branch 에 넘기는 관심사이며, delegate 5곳의 `accepts_delegations` 를 같은 변경에서 채워 전부 `accepted` 로 시작한다. +``` + +- [ ] **Step 4: §2.1.4 에 이벤트 흐름 5행 추가** + +`| \`FLOW-FE-RESP-008\` | 8 | …` 행 다음에 spec §7.5 의 표 5행을 붙인다. 열 순서는 `Stage ID | Order | Owner | Input | Action | Output | Invariants | Revision`, `Order` 는 1~5, `Revision` 은 `1`. + +§2.1.4 서두 문단 끝에 아래를 덧붙인다. + +```markdown +> `FLOW-FE-RESP-*` 는 요청/응답 한 번의 여정이고 `FLOW-FE-EVENT-*` 는 인바운드 프레임 하나의 여정이다. 두 흐름은 3~4단계에서 같은 runtime schema owner 를 공유하지만 순서를 합치지 않는다. 구독 해제(cleanup)는 어느 흐름도 소유하지 않고 `RealtimeSubscriptionPort` 계약이 소유한다. +``` + +- [ ] **Step 5: 체크 명령을 다시 돌려 통과 확인** + +```bash +HUB=raw/project-notes/ca-skeleton-frontend-operational-contract.md +echo "DELEG 신규(기대 5): $(grep -cE '^\| `DELEG-FE-0(07|08|09|10|11)`' "$HUB")" +echo "FLOW-EVENT 신규(기대 5): $(grep -cE '^\| `FLOW-FE-EVENT-00[1-5]`' "$HUB")" +echo "proposed 잔존(기대 0): $(grep -cE '^\| `DELEG-FE-0(07|08|09|10|11)`.*proposed' "$HUB")" +``` + +Expected: `5`, `5`, `0`. + +- [ ] **Step 6: 커밋** + +```bash +git add raw/project-notes/ca-skeleton-frontend-operational-contract.md +git commit -m "docs(hub): DELEG-FE-007~011 위임과 FLOW-FE-EVENT-001~005 흐름 추가 + +인바운드 이벤트는 FLOW-FE-RESP-* 에 자리가 없어 별도 5단계로 고정한다. +검증 통과분만 application 에 도달하며 구독 해제는 port 계약이 소유한다." +``` + +--- + +### Task 4: hub 아키텍처 (§4.2~§4.6) + +**Files:** +- Modify: hub §4.2 Component responsibility (현재 456–465행), §4.3 Dependency matrix (현재 471–488행), §4.4 Port ownership matrix (현재 492–503행), §4.5 Composition root (현재 505–527행), §4.6 Planned directory blueprint (현재 529–582행) + +**Interfaces:** +- Consumes: Task 2 의 `FE-D034`·`FE-D036` (worker/SW 규칙의 근거) +- Produces: port 12개 이름 — `CapabilityPort`, `FileDialogPort`, `BlobStorePort`, `CachePersistencePort`, `CrossTabSyncPort`, `UploadTransferPort`, `StreamingDownloadPort`, `RealtimeSubscriptionPort`, `PushSubscriptionPort`, `WorkerTaskPort`, `ServiceWorkerHostPort`, `BackgroundSyncPort`. Task 10 의 branch-note 목표·범위가 이 이름을 참조한다. + +- [ ] **Step 1: 체크 명령을 돌려 아직 없음을 확인** + +```bash +HUB=raw/project-notes/ca-skeleton-frontend-operational-contract.md +grep -cE '`(CapabilityPort|FileDialogPort|BlobStorePort|CachePersistencePort|CrossTabSyncPort|UploadTransferPort|StreamingDownloadPort|RealtimeSubscriptionPort|PushSubscriptionPort|WorkerTaskPort|ServiceWorkerHostPort|BackgroundSyncPort)`' "$HUB" +``` + +Expected: `0`. + +- [ ] **Step 2: §4.2 에 adapter 10행 추가** + +`| \`bootstrap\` | config load, …` 행 **앞**에 spec §5.3 의 표 10행을 붙인다. `bootstrap` 은 표의 마지막 행으로 남긴다 — 조립 주체이므로 조립 대상 뒤에 오는 기존 배치를 유지한다. + +- [ ] **Step 3: §4.3 에 예외 2건과 worker/SW 엔트리 규칙 추가** + +`Normative dependency summary:` 목록의 마지막 항목(`- bootstrap만 concrete adapter를 조립할 수 있다.`) 다음에 spec §5.4 의 4개 항목을 붙인다. + +- [ ] **Step 4: §4.4 에 port 12행 추가 + 설명 문단** + +`| \`ReleaseInfoPort\` | …` 행 다음에 spec §5.1 의 표 12행을 붙인다. 열 순서는 `Port | Definition owner | Planned implementation | Consumer | Input / output | Failure vocabulary`. + +기존 `AuthSessionPort는 token 문자열을 …` 문단 다음에 spec §5.2 의 두 문단(멀티프로토콜은 신규 port 0개 / Image CDN 은 application 정책)을 그대로 붙인다. + +- [ ] **Step 5: §4.5 boot order 를 12단계로 교체** + +기존 1~10단계 목록을 spec §5.5 의 12단계로 교체하고, 그 아래 규범 2항(6단계는 실패하지 않는다 / 9단계는 정적 import 하지 않는다)을 덧붙인다. 기존 문장 `2~4단계가 실패하면 product route를 mount하지 않고 boot error shell만 렌더한다. telemetry adapter 생성 실패는 console-safe fallback으로 계속 진행할 수 있다.` 는 단계 번호가 바뀌지 않았으므로 그대로 둔다. + +- [ ] **Step 6: §4.6 디렉터리 청사진 확장** + +기존 코드 블록의 `adapters/` 하위를 spec §5.6 의 트리로 교체하고, `application/policies/`·`contracts/capabilities.js`·`src/workers/`·`public/sw.js` 를 추가한다. 기존 항목은 지우지 않는다. + +- [ ] **Step 7: 체크 명령을 다시 돌려 통과 확인** + +```bash +HUB=raw/project-notes/ca-skeleton-frontend-operational-contract.md +for p in CapabilityPort FileDialogPort BlobStorePort CachePersistencePort CrossTabSyncPort UploadTransferPort StreamingDownloadPort RealtimeSubscriptionPort PushSubscriptionPort WorkerTaskPort ServiceWorkerHostPort BackgroundSyncPort; do + n=$(grep -c "\`$p\`" "$HUB"); [ "$n" -ge 1 ] || echo "MISSING: $p" +done +echo "port 행(기대 12): $(grep -cE '^\| `(CapabilityPort|FileDialogPort|BlobStorePort|CachePersistencePort|CrossTabSyncPort|UploadTransferPort|StreamingDownloadPort|RealtimeSubscriptionPort|PushSubscriptionPort|WorkerTaskPort|ServiceWorkerHostPort|BackgroundSyncPort)`' "$HUB")" +echo "boot 6단계(기대 1): $(grep -c 'capability 해석' "$HUB")" +echo "capabilities.js(기대 1 이상): $(grep -c 'capabilities.js' "$HUB")" +``` + +Expected: `MISSING:` 출력 없음, `12`, `1`, `1` 이상. + +- [ ] **Step 8: 커밋** + +```bash +git add raw/project-notes/ca-skeleton-frontend-operational-contract.md +git commit -m "docs(hub): port 12개, adapter 책임 10행, capability boot 단계, 디렉터리 확장 + +- 멀티프로토콜은 기존 ResourceQueryPort/ResourceCommandPort 를 재사용해 + 신규 port 0개. 프로토콜은 registry 데이터이지 타입이 아니다. +- Image CDN 은 I-O 가 없으므로 adapter 가 아니라 application 정책. +- boot 에 capability 해석 단계 삽입 — 실패하지 않고, 비활성 adapter 는 + 정적 import 하지 않는다(FE-NFR-020 방어)." +``` + +--- + +### Task 5: hub Contract Registries (§5.1, §5.3~§5.9, §5.11) + +**Files:** +- Modify: hub §5.1 Registry owner map (현재 590–599행), §5.3 API operation registry (622–643행), §5.4 Environment registry (645–664행), §5.5 Storage registry (666–686행), §5.6 Error registry (688–729행), §5.7 Query key registry (731–748행), §5.8 Telemetry registry (750–770행), §5.9 Release token registry (772–783행) +- Modify: hub — §5.10 Registry change protocol **뒤에** §5.11 신설 + +**Interfaces:** +- Consumes: Task 2 의 `DEC-…-REGISTRY-001` 갱신, Task 1 의 `FE-GATE-027`~`033` +- Produces: `FE-REG-CAPABILITY`, capability ID 6개(`CAP_FE_BINARY_IO`, `CAP_FE_CACHE_PERSISTENCE`, `CAP_FE_LARGE_TRANSFER`, `CAP_FE_ALT_PROTOCOL`, `CAP_FE_REALTIME`, `CAP_FE_BACKGROUND_EXEC`), error kind 26종. Task 6 의 failure matrix 가 kind 를 참조한다. + +- [ ] **Step 1: 체크 명령을 돌려 아직 없음을 확인** + +```bash +HUB=raw/project-notes/ca-skeleton-frontend-operational-contract.md +grep -c 'FE-REG-CAPABILITY' "$HUB" +grep -cE '^(CAPABILITY_DISABLED|EVENT_SCHEMA_MISMATCH|BACKGROUND_SYNC_REPLAY_REJECTED)$' "$HUB" +``` + +Expected: 둘 다 `0`. + +- [ ] **Step 2: §5.1 에 `FE-REG-CAPABILITY` 행 추가** + +`| \`FE-REG-RELEASE\` | …` 행 다음에: + +```markdown +| `FE-REG-CAPABILITY` | capability 활성 조건/owner/gate/fallback | `src/contracts/capabilities.js` | `feature-frontend-env-runtime-config-contract` | registry 없이 config 를 직접 읽어 기능 분기 | +``` + +- [ ] **Step 3: §5.11 신설 — capability registry 스키마** + +§5.10 의 마지막 항목(`8. orphan token scan이 0건이어야 merge할 수 있다.`) 다음, `---` 구분선 **앞**에 `### 5.11 Capability registry minimum schema` 절을 만들고 spec §6.1 의 필드 표 7행 + 초기 행 6개를 붙인다. spec §6.1 의 "§5.11 로 뒤에 붙인다" 근거 문단도 함께 옮긴다. + +- [ ] **Step 4: §5.3 API operation registry 확장** + +필드 표에 spec §6.2 의 신규 5필드(`protocol`, `transferMode`, `operationRef`, `eventSchema`, `resumeStrategy`)를 추가하고, Initial planned rows 표에 sample 3행(`STREAM_SAMPLE_EVENTS`, `PRESIGN_SAMPLE_UPLOAD`, `DOWNLOAD_SAMPLE_OBJECT`)을 추가한다. + +표 아래에 아래 문단을 넣는다. + +```markdown +`timeoutMs` 는 `transferMode: stream` 행에서 **연결 수립 timeout** 을 뜻하고 스트림 총 수명에는 적용하지 않는다. 스트림 수명은 `FE-D033` 의 재연결·terminal 전이 계약이 소유한다. +``` + +- [ ] **Step 5: §5.4 Environment registry 에 15개 키 추가** + +`| \`RELEASE_MANIFEST_URL\` | …` 행 다음에 spec §6.2 의 env 표 15행을 붙인다. capability flag 6개는 전부 `Default` 가 `` `false` `` 다. + +`public-sensitive` 설명 문단 다음에 아래를 덧붙인다. + +```markdown +`PUSH_PUBLIC_KEY` 는 VAPID **공개**키이므로 `public` 이다. 대응 개인키는 backend 소유이며 이 registry 에 등록할 수 없다. +``` + +- [ ] **Step 6: §5.5 Storage registry 확장** + +- `backend` 행의 Rule 을 `` `memory`, `sessionStorage`, `localStorage`, `indexedDB`, `opfs`, `cacheStorage` `` 로 교체 +- 필드 표에 `payloadClass`·`evictionOrder` 2행 추가 +- Initial planned rows 에 spec §6.2 의 4행(`UPLOAD_PART_STATE`, `TRANSFER_OBJECT_BUFFER`, `QUERY_CACHE_SNAPSHOT`, `SW_RESPONSE_CACHE`) 추가. `UPLOAD_PART_STATE` 의 fallback 은 **`fallback 없음`**, `evictionOrder` 는 `null` 이다 — correctness 값이 조용히 memory 로 넘어가지 않게 하는 장치다. + +- [ ] **Step 7: §5.6 Error registry 에 kind 26종 추가** + +`Planned \`kind\` enum:` 코드 블록의 `UNKNOWN_FAILURE` **앞**에 spec §6.2 의 26줄을 삽입한다. `UNKNOWN_FAILURE` 는 catch-all 이므로 목록 끝에 유지한다. + +코드 블록 다음에 아래 문단을 넣는다. + +```markdown +`TRANSFER_ABORTED` 는 만들지 않고 기존 `REQUEST_ABORTED` 를 재사용한다. `SUBSCRIPTION_LEAKED` 는 사용자에게 보이는 실패가 아니라 `FE-GATE-031` 이 잡는 결함이므로 kind 가 아니다. `SW_UPDATE_PENDING` 도 실패가 아니라 §9.7 의 surface state 다. +``` + +- [ ] **Step 8: §5.7·§5.8·§5.9 확장** + +- §5.7 규칙 표에 `persistenceTier`·`crossTabScope`·`version partition` 3행 추가 (spec §6.2) +- §5.8 Initial planned events 표에 7행 추가 (spec §6.2). `forbiddenAttributes` 필드 Rule 에 `, 파일명, object key, presigned URL, 구독 endpoint` 를 덧붙인다 +- §5.9 표에 `serviceWorkerVersion` 1행 추가 + +- [ ] **Step 9: 체크 명령을 다시 돌려 통과 확인** + +```bash +HUB=raw/project-notes/ca-skeleton-frontend-operational-contract.md +echo "FE-REG-CAPABILITY(기대 3 이상): $(grep -c 'FE-REG-CAPABILITY' "$HUB")" +echo "§5.11(기대 1): $(grep -c '^### 5.11 Capability registry minimum schema' "$HUB")" +echo "capability ID(기대 6): $(grep -cE '^\| `CAP_FE_(BINARY_IO|CACHE_PERSISTENCE|LARGE_TRANSFER|ALT_PROTOCOL|REALTIME|BACKGROUND_EXEC)`' "$HUB")" +echo "capability flag 키(기대 6): $(grep -cE '^\| `CAPABILITY_[A-Z_]+_ENABLED`' "$HUB")" +echo "신규 error kind(기대 26): $(grep -cE '^(CAPABILITY_DISABLED|CAPABILITY_UNSUPPORTED|FILE_PICKER_DISMISSED|FILE_REJECTED|BLOB_STORE_UNAVAILABLE|BLOB_STORE_QUOTA_EXCEEDED|CACHE_PERSISTENCE_FAILURE|CROSS_TAB_CHANNEL_UNAVAILABLE|PRESIGN_EXPIRED|UPLOAD_PART_FAILED|TRANSFER_INTEGRITY_MISMATCH|STREAM_INTERRUPTED|PROTOCOL_STATUS_MISMATCH|CODEC_DECODE_FAILURE|PARTIAL_RESULT_FAILURE|REALTIME_CONNECT_FAILED|REALTIME_DISCONNECTED|REALTIME_RESUME_GAP|EVENT_SCHEMA_MISMATCH|PUSH_PERMISSION_DENIED|PUSH_SUBSCRIPTION_EXPIRED|WORKER_UNAVAILABLE|WORKER_TASK_TIMEOUT|SW_REGISTRATION_FAILED|BACKGROUND_SYNC_UNSUPPORTED|BACKGROUND_SYNC_REPLAY_REJECTED)$' "$HUB")" +echo "opfs/cacheStorage backend(기대 1): $(grep -c 'indexedDB`, `opfs`, `cacheStorage' "$HUB")" +echo "금지 kind 오입력(기대 0): $(grep -cE '^(TRANSFER_ABORTED|SUBSCRIPTION_LEAKED|SW_UPDATE_PENDING)$' "$HUB")" +``` + +Expected: `3` 이상, `1`, `6`, `6`, `26`, `1`, `0`. + +- [ ] **Step 10: 커밋** + +```bash +git add raw/project-notes/ca-skeleton-frontend-operational-contract.md +git commit -m "docs(hub): FE-REG-CAPABILITY 신설(8->9)과 기존 8개 registry additive 확장 + +- §5.11 capability registry — envFlagKey 는 FE-REG-ENV 행 포인터이며 + 키의 타입·검증은 env registry 가 계속 소유(재진술 금지) +- FE-REG-API 에 protocol/transferMode/operationRef/eventSchema/ + resumeStrategy. 실시간 구독은 별도 장부가 아니라 stream operation 이다 +- FE-REG-STORAGE 에 opfs/cacheStorage backend, payloadClass, evictionOrder. + UPLOAD_PART_STATE 는 correctness 값이라 quota fallback 없음 +- error kind 26종. TRANSFER_ABORTED/SUBSCRIPTION_LEAKED/SW_UPDATE_PENDING + 은 각각 기존 kind 재사용/gate 결함/surface state 라 제외" +``` + +--- + +### Task 6: hub 실패 매트릭스·surface state·NFR (§8.2, §9.5~§9.7, §14.2) + +**Files:** +- Modify: hub §8.2 Failure matrix (현재 1116–1150행), §9 절 끝(현재 1251행 뒤)에 §9.5~§9.7 신설, §14.2 Initial target matrix (1495–1512행) + +**Interfaces:** +- Consumes: Task 5 의 error kind 26종 +- Produces: `FE-NFR-016`~`FE-NFR-020`. Task 7 의 gate `Covered FE-NFR` 열이 참조한다. + +- [ ] **Step 1: 체크 명령을 돌려 아직 없음을 확인** + +```bash +HUB=raw/project-notes/ca-skeleton-frontend-operational-contract.md +grep -cE '^\| `FE-NFR-0(1[6-9]|20)`' "$HUB" +grep -c '^### 9.5 ' "$HUB" +``` + +Expected: 둘 다 `0`. + +- [ ] **Step 2: §8.2 Failure matrix 에 신규 kind 행 추가** + +`| unknown thrown value | \`UNKNOWN_FAILURE\` | …` 행 **앞**에 26개 kind 행을 추가한다. 열 순서는 `Trigger | Normalized kind | Auto retry | Fallback | User UX | Telemetry rule`. spec §10.2 의 특기 사항을 반드시 반영한다. + +- `FILE_PICKER_DISMISSED` — Auto retry `no`, User UX `error surface 없음`, Telemetry rule `event 금지` (사용자 취소는 실패가 아니다) +- `CAPABILITY_DISABLED` / `CAPABILITY_UNSUPPORTED` — Auto retry `no`, Fallback 은 `FE-REG-CAPABILITY` 의 `disabledFallback` 을 따름 +- `PRESIGN_EXPIRED` — Auto retry `presign 재획득 후 1회`, User UX `자동 재개, 실패 시 retry action` +- `REALTIME_DISCONNECTED` — Auto retry `no` (재시도 상한 소진 뒤 도달하는 terminal), User UX `사용자 주도 retry` +- `BACKGROUND_SYNC_REPLAY_REJECTED` — Auto retry `no`, User UX `contact-support`, 중복 write 방지 우선 + +- [ ] **Step 3: §9.5 실시간 surface state 신설** + +§9.4 Storage behavior 의 마지막 항목 다음, `---` **앞**에 `### 9.5 실시간 surface state` 를 만들고 spec §9.2 의 표 5행 + 마지막 설명 문단을 붙인다. + +- [ ] **Step 4: §9.6 전송 진행 surface state 신설** + +바로 이어서 `### 9.6 전송 진행 surface state` 를 만들고 spec §9.3 의 표 5행 + telemetry 주의 문단을 붙인다. + +- [ ] **Step 5: §9.7 Service Worker 갱신 surface state 신설** + +바로 이어서 `### 9.7 Service Worker 갱신 surface state` 를 만들고 spec §9.4 의 표 4행을 붙인다. `sw-update-pending` 행의 **자동 `skipWaiting` 금지** 규범을 빠뜨리지 않는다. + +- [ ] **Step 6: §14.2 에 NFR 5행 추가** + +`| \`FE-NFR-015\` | INP field p75 | …` 행 다음에 spec §9.1 의 표 5행을 붙인다. `Current evidence` 는 전부 `none`. + +표 아래 `Field Web Vitals는 …` 문단 다음에 아래를 덧붙인다. + +```markdown +`FE-NFR-020` 은 `FE-NFR-001`(initial JS gzip ≤ 200 KiB) 예산을 신규 capability 가 잠식하지 않음을 수치로 방어하는 항목이다. capability 를 켜면 번들이 늘어나는 것은 정상이며, 이 NFR 이 막는 것은 **끄고도 늘어나는** 경우다. +``` + +- [ ] **Step 7: 체크 명령을 다시 돌려 통과 확인** + +```bash +HUB=raw/project-notes/ca-skeleton-frontend-operational-contract.md +echo "FE-NFR 신규(기대 5): $(grep -cE '^\| `FE-NFR-0(1[6-9]|20)`' "$HUB")" +echo "§9.5~9.7(기대 3): $(grep -cE '^### 9\.[567] ' "$HUB")" +echo "skipWaiting 금지(기대 1): $(grep -c 'skipWaiting' "$HUB")" +echo "failure matrix 신규 kind(기대 26): $(grep -cE '^\| .* \| `(CAPABILITY_DISABLED|CAPABILITY_UNSUPPORTED|FILE_PICKER_DISMISSED|FILE_REJECTED|BLOB_STORE_UNAVAILABLE|BLOB_STORE_QUOTA_EXCEEDED|CACHE_PERSISTENCE_FAILURE|CROSS_TAB_CHANNEL_UNAVAILABLE|PRESIGN_EXPIRED|UPLOAD_PART_FAILED|TRANSFER_INTEGRITY_MISMATCH|STREAM_INTERRUPTED|PROTOCOL_STATUS_MISMATCH|CODEC_DECODE_FAILURE|PARTIAL_RESULT_FAILURE|REALTIME_CONNECT_FAILED|REALTIME_DISCONNECTED|REALTIME_RESUME_GAP|EVENT_SCHEMA_MISMATCH|PUSH_PERMISSION_DENIED|PUSH_SUBSCRIPTION_EXPIRED|WORKER_UNAVAILABLE|WORKER_TASK_TIMEOUT|SW_REGISTRATION_FAILED|BACKGROUND_SYNC_UNSUPPORTED|BACKGROUND_SYNC_REPLAY_REJECTED)`' "$HUB")" +``` + +Expected: `5`, `3`, `1` 이상, `26`. + +- [ ] **Step 8: 커밋** + +```bash +git add raw/project-notes/ca-skeleton-frontend-operational-contract.md +git commit -m "docs(hub): failure matrix 26행, surface state 3종, NFR 5개 추가 + +- 실시간은 reconnecting 과 disconnected 를 구분해야 한다. 전자를 terminal + 로 보이면 불필요한 새로고침, 후자를 refreshing 으로 보이면 오지 않는 + 데이터를 기다린다. +- sw-update-pending 은 자동 skipWaiting 금지 — 열린 탭이 release 를 + 갈아타면 FE-OC-016 coherence 가 깨진다. +- FE-NFR-020 은 capability 를 끄고도 번들이 늘어나는 경우를 막는다." +``` + +--- + +### Task 7: hub Acceptance Gate·승급·readiness (§15.1~§15.3, §18.1, §18.2) + +**Files:** +- Modify: hub §15.1 Gate ownership (현재 1554–1586행), §15.2 Negative fixture (1592–1602행), §15.3 Promotion rule (1608–1617행), §18.1 Formula (1949–1955행), §18.2 Current scorecard (1959–1980행) + +**Interfaces:** +- Consumes: Task 1 의 `FE-GATE-027`~`033` typed 행, Task 6 의 `FE-NFR-016`~`020` +- Produces: `NOT_APPLICABLE` 상태 개념, `FE-RDY-017` + +- [ ] **Step 1: 체크 명령을 돌려 아직 없음을 확인** + +```bash +HUB=raw/project-notes/ca-skeleton-frontend-operational-contract.md +grep -cE '^\| `FE-GATE-(027|028|029|030|031|032|033)` \|' "$HUB" +grep -c 'NOT_APPLICABLE' "$HUB" +``` + +Expected: 둘 다 `0`. +(Task 1 이 §2.1.1 에 넣은 gate 행은 `| \`FE-GATE-027\` | \`fe.gate…` 형태라 위 패턴 `\` \|` 와 다르다. §15.1 행만 센다.) + +- [ ] **Step 2: §15.1 에 gate 7행 추가** + +`| \`FE-GATE-026\` | lab performance | …` 행 다음에 spec §8.3 의 표 7행을 붙인다. 열 순서는 `Gate ID | Gate | Blocking scope | Covered FE-OC | Covered FE-NFR | Required fixtures | Pass condition | Evidence artifact | Current`, `Current` 는 전부 `` `FAIL_UNVERIFIED` ``. + +- [ ] **Step 3: §15.1 서두 개수 갱신** + +`현재 stable gate registry는 26개 row이며` → `현재 stable gate registry는 33개 row이며` + +- [ ] **Step 4: §15.2 에 negative fixture 7행 추가** + +표 끝에 spec §8.4 의 7행을 붙인다. + +- [ ] **Step 5: §15.3 Promotion rule 코드 블록 교체** + +기존 블록을 spec §8.5 의 블록으로 교체하고 그 아래에 아래 문장을 넣는다. + +```markdown +`FE-GATE-033` 은 `NOT_APPLICABLE` 을 취할 수 없으므로 항상 `MERGE_READY` 에 무조건 포함된다. capability 를 하나도 쓰지 않는 프로젝트도 "쓰지 않는다"를 증명해야 한다. +``` + +기존 문장 `현재는 \`PROJECT_READY = false\`, 즉 \`NOT_READY\`다.` 는 유지한다. + +- [ ] **Step 6: §18.1 Formula 교체** + +기존 코드 블록을 spec §8.1 의 블록으로 교체하고, 기존 `PASS_SCOPED는 …` 문단 다음에 아래 3개 규범 항목을 붙인다. + +```markdown +- `NOT_APPLICABLE` 은 `FE-GATE-027`~`FE-GATE-032` **만** 취할 수 있다. `FE-GATE-033` 자신은 취할 수 없다. +- `FE-GATE-033` 이 PASS 가 아니면 모든 `NOT_APPLICABLE` 은 무효이며 `FAIL_UNVERIFIED` 로 강등된다. +- "안 쓴다"는 주장은 선언이 아니라 **번들에 그 adapter 가 없다는 증거**로만 성립한다. +``` + +- [ ] **Step 7: §18.2 scorecard 갱신** + +`FE-RDY-009` 행의 Blocking question `8 registry가 single owner로 구현됐는가` → `9 registry가 single owner로 구현됐는가` + +`FE-RDY-016` 행 다음에: + +```markdown +| `FE-RDY-017` | 비활성 capability가 번들에서 실제로 부재한가 | capability bundle report | `FAIL` | build evidence `UNVERIFIED` | +``` + +- [ ] **Step 8: 체크 명령을 다시 돌려 통과 확인** + +```bash +HUB=raw/project-notes/ca-skeleton-frontend-operational-contract.md +echo "§15.1 gate 행(기대 7): $(grep -cE '^\| `FE-GATE-(027|028|029|030|031|032|033)` \|' "$HUB")" +echo "33개 row(기대 1): $(grep -c '33개 row이며' "$HUB")" +echo "26개 row 잔존(기대 0): $(grep -c '26개 row이며' "$HUB")" +echo "NOT_APPLICABLE(기대 4 이상): $(grep -c 'NOT_APPLICABLE' "$HUB")" +echo "FE-RDY-017(기대 1): $(grep -c 'FE-RDY-017' "$HUB")" +echo "8 registry 잔존(기대 0): $(grep -c '8 registry가 single owner' "$HUB")" +``` + +Expected: `7`, `1`, `0`, `4` 이상, `1`, `0`. + +- [ ] **Step 9: 커밋** + +```bash +git add raw/project-notes/ca-skeleton-frontend-operational-contract.md +git commit -m "docs(hub): gate 7개와 조건부 차단(NOT_APPLICABLE) 개념 추가 + +capability 가 OFF 인 프로젝트에서 gate 를 PASS 라 하면 거짓말이고 FAIL +이라 하면 영원히 배포할 수 없다. NOT_APPLICABLE 을 도입하되 FE-GATE-033 +이 번들 부재를 증명했을 때만 유효하게 묶었다. +FE-GATE-033 자신은 NOT_APPLICABLE 을 취할 수 없다." +``` + +--- + +### Task 8: hub 범위·runbook·증거·리스크·답변경계 (§0.5, §16, §17.1, §19, §22) + +**Files:** +- Modify: hub §0.5 범위 (현재 110–129행), §16 (1621행 이후 — §16.5 뒤에 §16.6·§16.7 신설), §17.1 Evidence records (1900–1914행), §19.1 Risk register (1988–2001행), §19.2 Open questions (2005–2019행), §22.2·§22.3 (2255–2280행) + +**Interfaces:** +- Consumes: Task 2 의 `FE-D034`(`FE-Q-009` 해소 근거), Task 7 의 gate +- Produces: `FE-RB-006`·`FE-RB-007`, `FE-EV-014`·`FE-EV-015`, `FE-RISK-013`~`018`, `FE-Q-011`~`014` + +- [ ] **Step 1: 체크 명령을 돌려 아직 없음을 확인** + +```bash +HUB=raw/project-notes/ca-skeleton-frontend-operational-contract.md +grep -cE 'FE-RB-00[67]|FE-EV-01[45]|FE-RISK-01[3-8]|FE-Q-01[1-4]' "$HUB" +``` + +Expected: `0`. + +- [ ] **Step 2: §0.5 범위 갱신** + +`In scope:` 목록 끝에 spec §10.1 의 6개 항목을 추가한다. + +`Out of scope:` 목록 끝에 spec §10.1 의 2개 항목을 추가한다. 첫 항목은 아래 문장을 그대로 쓴다 — 이 문서가 어디까지 계약하는지의 경계다. + +```markdown +- 위 6개 도메인의 use case, domain model, business rule — 이 skeleton 은 port 와 adapter 계약까지만 제공한다 +``` + +- [ ] **Step 3: §16.6·§16.7 runbook 신설** + +§16.5 `FE-RB-005` 의 마지막 줄 다음, `---` **앞**에 `### 16.6 \`FE-RB-006\` — 실시간 연결 장애` 와 `### 16.7 \`FE-RB-007\` — 백그라운드 실행 장애` 를 만든다. 각각 §16.1~§16.5 와 **동일한 형식**을 따른다. + +필드 표 (`Primary owner` / `Technical escalation` / `Activation condition` / `Conditional window` / `Evidence path`) + `**Trigger**` / `**Immediate containment**` / `**Diagnosis evidence**` / `**Mitigation options**` / `**Escalation**` / `**Recovery assertions**` / `**Evidence**` 소제목 순서. + +내용은 spec §9.5 의 두 문단에서 가져오되, 위 소제목 구조로 펼친다. + +- `FE-RB-006` — Primary owner `feature-frontend-operational-runbook-contract`, Technical escalation `feature-frontend-realtime-subscription-lifecycle-contract` → backend API owner, Evidence path `artifacts/runbooks/FE-RB-006//` +- `FE-RB-007` — Primary owner `feature-frontend-operational-runbook-contract`, Technical escalation `feature-frontend-background-execution-worker-contract` → `feature-frontend-release-cache-rollback-contract`, Evidence path `artifacts/runbooks/FE-RB-007//` + +두 runbook 모두 `Conditional window` 는 기존 5개와 같은 `planned conditional default` 이며 measured SLO 가 아님을 §16 서두가 이미 밝히고 있으므로 따로 반복하지 않는다. + +- [ ] **Step 4: §17.1 에 evidence 2행 추가** + +표 끝에 spec §10.3 의 2행을 붙인다. + +- [ ] **Step 5: §19.1 에 risk 6행, §19.2 에 open question 4행 추가** + +각 표 끝에 spec §10.5 의 행을 붙인다. risk 의 `Resolution condition`·`Status` 열을 빠뜨리지 않는다(`Status` 는 전부 `` `open` ``). + +`FE-Q-009` 행의 `Resolution condition` 을 `` `FE-D019` retained or superseded `` → `` `FE-D034` 로 해소(2026-07-28) — superseded `` 로 바꾼다. + +- [ ] **Step 6: §22.2·§22.3 갱신** + +§22.2 목록 끝에: + +```markdown +- 6개 opt-in capability 의 port 이름·기본값·gate 구조 +``` + +§22.3 목록 끝에: + +```markdown +- 이 adapter 들이 동작한다는 주장 +- 실시간·전송·worker 의 성능이나 안정성 수치를 측정했다는 주장 +- 특정 storage·CDN·push vendor 와의 통합을 검증했다는 주장 +``` + +- [ ] **Step 7: 체크 명령을 다시 돌려 통과 확인** + +```bash +HUB=raw/project-notes/ca-skeleton-frontend-operational-contract.md +echo "runbook 절(기대 2): $(grep -cE '^### 16\.[67] `FE-RB-00[67]`' "$HUB")" +echo "FE-EV 신규(기대 2): $(grep -cE '^\| `FE-EV-01[45]`' "$HUB")" +echo "FE-RISK 신규(기대 6): $(grep -cE '^\| `FE-RISK-01[3-8]`' "$HUB")" +echo "FE-Q 신규(기대 4): $(grep -cE '^\| `FE-Q-01[1-4]`' "$HUB")" +echo "FE-Q-009 해소(기대 1): $(grep -c 'FE-D034` 로 해소' "$HUB")" +echo "out of scope 못박기(기대 1): $(grep -c 'port 와 adapter 계약까지만 제공한다' "$HUB")" +``` + +Expected: `2`, `2`, `6`, `4`, `1`, `1`. + +- [ ] **Step 8: 커밋** + +```bash +git add raw/project-notes/ca-skeleton-frontend-operational-contract.md +git commit -m "docs(hub): 범위·runbook 2개·evidence·risk·open question·답변경계 + +- §0.5 out of scope 에 '6개 도메인의 use case/domain model/business rule' + 을 명시. 이 skeleton 은 port 와 adapter 계약까지다. +- FE-RB-006 실시간 연결 장애, FE-RB-007 백그라운드 실행 장애 +- FE-Q-009(service worker 필요한가)는 FE-D034 로 해소" +``` + +--- + +### Task 9: hub 실행계획·branch 분해·Cluster·체크리스트·tail (§8.0, §20, §21.2, §23.1, §25, §26, frontmatter) + +**Files:** +- Modify: hub frontmatter (1–33행), §8.0 실행계획 (2025–2053행), §20 Branch Decomposition (2059–2089행), §21.2 브랜치 (2158–2216행), §23.1 (2297–2310행), §25 Next Steps (2365–2404행), §26 (2409–2418행) + +**Interfaces:** +- Consumes: Task 1~8 의 모든 ID +- Produces: `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-028`~`033`, branch slug 6개. Task 10 의 branch-note frontmatter 가 이 WI ID 를 pin 한다. + +- [ ] **Step 1: 체크 명령을 돌려 아직 없음을 확인** + +```bash +HUB=raw/project-notes/ca-skeleton-frontend-operational-contract.md +grep -cE 'WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-0(2[89]|3[0-3])' "$HUB" +grep -c 'feature-frontend-binary-file-io-store-contract' "$HUB" +``` + +Expected: 둘 다 `0`. + +- [ ] **Step 2: §8.0 에 Work Item 6행 추가 + 기존 1행 보강** + +`| \`WI-…-027\` | \`feature-frontend-ci-quality-gates-contract\` | …` 행 다음에 spec §11.1 의 표 6행을 붙인다. **ID 는 축약 없이 전체 문자열**(`WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-028` 등)로 쓴다. `Status` 는 전부 `` `planned` ``. + +`WI-…-004`(`feature-frontend-env-runtime-config-contract`) 행의 완료 조건에 `; capability registry와 default-off 번들 검증이 통과한다` 를 덧붙이고, `Applies Decisions` 에 `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-CAPABILITY-001@1` 을 추가한다. + +- [ ] **Step 3: §20 에 branch 6행 추가 + 서두 갱신** + +표 끝에 spec §11.2 의 6행을 붙인다. `Measurable completion` 은 §8.0 완료 조건과 같은 취지로 적고, `Priority` 는 전부 `P4`. + +서두 문장 `아래 27개 branch-note는 2026-07-18 \`/branch\` scaffolding으로 생성되어` → `아래 33개 branch-note 중 27개는 2026-07-18 scaffolding으로, 6개는 2026-07-28 runtime capability 확장으로 생성되어` + +`Priority` 열 설명을 표 아래에 추가한다. + +```markdown +`P4` 는 2026-07-28 에 추가한 계층이다. P1~P3 의 핵심 skeleton 이 완성된 뒤 착수하는 **opt-in capability** 를 뜻하며, 이 계층의 branch 가 미완이어도 `MERGE_READY` 는 `FE-GATE-033` 만으로 성립한다(§15.3). +``` + +- [ ] **Step 4: §21.2 Cluster 에 6항 추가** + +`` 블록 안의 알파벳 순서에 맞춰 6개 wikilink 를 삽입한다. `feature-frontend-b…` 는 `feature-frontend-auth-session-integration-contract` 와 `feature-frontend-browser-security-boundary-contract` 사이 등, 정렬을 실제로 맞춘다. + +블록 아래 수기 목록에도 6항을 추가한다(설명 포함). + +```markdown +- [[raw/branch-notes/feature-frontend-binary-file-io-store-contract]] — 바이너리/파일 I-O·로컬 대용량 저장 contract +- [[raw/branch-notes/feature-frontend-cache-tier-cross-tab-invalidation-contract]] — 캐시 계층·탭 간 무효화 contract +- [[raw/branch-notes/feature-frontend-large-object-transfer-contract]] — 대용량 객체 전송 contract +- [[raw/branch-notes/feature-frontend-multi-protocol-api-transport-contract]] — 멀티프로토콜 API transport contract +- [[raw/branch-notes/feature-frontend-realtime-subscription-lifecycle-contract]] — 실시간 구독 수명주기 contract +- [[raw/branch-notes/feature-frontend-background-execution-worker-contract]] — 백그라운드 실행 contract +``` + +- [ ] **Step 5: §23.1·§25·§26 갱신** + +§23.1 — `- [x] route/API-operation/env/storage/error/query/telemetry/release 8개 registry owner가 있음` → `9개 registry owner가 있음` (뒤에 `/capability` 추가). `- [x] runbook 5종이 있음` → `- [x] runbook 7종이 있음`. + +§25 — `### P3 — Release readiness` 절 다음, `### Promotion` 앞에 신설: + +```markdown +### P4 — Opt-in runtime capability + +- [ ] `feature-frontend-binary-file-io-store-contract` 설계·depth gate +- [ ] `feature-frontend-cache-tier-cross-tab-invalidation-contract` 설계·depth gate +- [ ] `feature-frontend-large-object-transfer-contract` 설계·depth gate +- [ ] `feature-frontend-multi-protocol-api-transport-contract` 설계·depth gate +- [ ] `feature-frontend-realtime-subscription-lifecycle-contract` 설계·depth gate +- [ ] `feature-frontend-background-execution-worker-contract` 설계·depth gate +- [ ] 6개 도메인의 근거 raw 자료 수집 (`FE-Q-011`) +``` + +§26 — 표 끝에: + +```markdown +| opt-in runtime capability | `planned` | 설계와 계약만 존재; adapter·gate evidence `UNVERIFIED` | +``` + +- [ ] **Step 6: frontmatter 갱신** + +`last_reviewed: 2026-07-18` → `last_reviewed: 2026-07-28` +`project_revision: 1` → `project_revision: 2` + +`imports:` 배열은 **손대지 않는다**. 이 배열은 hub 가 *다른 문서에서 가져온* 계약의 pin 이고, 신규 `FE-OC-027`~`032`·`FE-GATE-027`~`033` 은 hub 가 **정의**하는 것이지 가져오는 것이 아니다. §21 `가져온 프로젝트 계약` 표도 같은 이유로 Task 11 이후 branch-note 가 생긴 뒤에 다루며, 이번 변경에서는 건드리지 않는다. + +- [ ] **Step 7: 체크 명령을 다시 돌려 통과 확인** + +```bash +HUB=raw/project-notes/ca-skeleton-frontend-operational-contract.md +echo "WI 신규(기대 6): $(grep -cE '^\| `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-0(2[89]|3[0-3])`' "$HUB")" +echo "branch slug 6종 등장: $(for s in binary-file-io-store cache-tier-cross-tab-invalidation large-object-transfer multi-protocol-api-transport realtime-subscription-lifecycle background-execution-worker; do grep -q "feature-frontend-$s-contract" "$HUB" || echo "MISSING $s"; done; echo ok)" +echo "33개 branch(기대 1): $(grep -c '33개 branch-note' "$HUB")" +echo "9개 registry owner(기대 1): $(grep -c '9개 registry owner가 있음' "$HUB")" +echo "runbook 7종(기대 1): $(grep -c 'runbook 7종이 있음' "$HUB")" +echo "P4 절(기대 1): $(grep -c '^### P4 — Opt-in runtime capability' "$HUB")" +echo "last_reviewed(기대 1): $(grep -c '^last_reviewed: 2026-07-28' "$HUB")" +``` + +Expected: `6`, `MISSING` 없이 `ok`, `1`, `1`, `1`, `1`, `1`. + +- [ ] **Step 8: hub 전체 참조 무결성 검사** + +```bash +HUB=raw/project-notes/ca-skeleton-frontend-operational-contract.md +CONTRACT_STABLE_ID_RE='FE-D[0-9]{3}|FE-(SC|OC|NFR|GATE|RB|EV|RDY|RISK|Q)-[0-9]{3}|FE-NFR-C[0-9]{2}|FE-REG-[A-Z]+(-[A-Z]+)*' +comm -23 \ + <(grep -oE "$CONTRACT_STABLE_ID_RE" "$HUB" | sort -u) \ + <(grep -nE '^\| `FE-|^### 16\.[0-9]+ `FE-RB-' "$HUB" | grep -oE "$CONTRACT_STABLE_ID_RE" | sort -u) +``` + +Expected: **출력 없음**. 출력이 있으면 그 ID 는 참조만 되고 정의 행이 없다는 뜻이니, 해당 Task 로 돌아가 정의 행을 추가한다. + +- [ ] **Step 9: 커밋** + +```bash +git add raw/project-notes/ca-skeleton-frontend-operational-contract.md +git commit -m "docs(hub): Work Item 6건, branch 분해 6행, Cluster, P4 계층, tail 정합 + +- WI-...-028~033 과 §20 branch 행. P4 = P1~P3 완료 후 착수하는 opt-in + capability 계층이며 미완이어도 MERGE_READY 는 FE-GATE-033 으로 성립 +- §23.1 체크리스트 8->9 registry, runbook 5->7종 +- project_revision 2, last_reviewed 2026-07-28 +- frontmatter imports 는 손대지 않음(hub 가 정의하는 계약이지 가져온 것이 + 아니다)" +``` + +--- + +### Task 10: 신규 branch-note 6개 생성 + +**Files:** +- Create: `raw/branch-notes/feature-frontend-binary-file-io-store-contract.md` +- Create: `raw/branch-notes/feature-frontend-cache-tier-cross-tab-invalidation-contract.md` +- Create: `raw/branch-notes/feature-frontend-large-object-transfer-contract.md` +- Create: `raw/branch-notes/feature-frontend-multi-protocol-api-transport-contract.md` +- Create: `raw/branch-notes/feature-frontend-realtime-subscription-lifecycle-contract.md` +- Create: `raw/branch-notes/feature-frontend-background-execution-worker-contract.md` +- Read (템플릿): `templates/branch-note-template.md` + +**Interfaces:** +- Consumes: Task 2 의 `DEC-…` Summary 문자열(글자 단위 복사), Task 3 의 `DELEG-FE-*`·`FLOW-FE-EVENT-*`, Task 9 의 `WI-…-028`~`033` +- Produces: branch-note 6개. Task 11 의 위임 왕복 대조가 이 파일들의 `delegates:`/`accepts_delegations:` 를 참조한다. + +**작성 원칙:** `templates/branch-note-template.md` 의 절 구조를 그대로 따른다. `## 구현 가이드` 는 **헤딩과 R1/R2/R3 안내만 남기고 비운다** — 근거 raw 없이 채우면 전부 `UNSUPPORTED_IMPL_DECISION` 이 되므로(CLAUDE.md §15.5) 다음 단계(`/branch-spec` 상당 작업)로 미룬다. 이 사실을 각 파일의 해당 절에 한 줄로 적는다. + +- [ ] **Step 1: 템플릿을 읽고 절 구조를 확인** + +```bash +grep -nE '^#{1,3} |^