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

2421 lines
183 KiB
Markdown

---
title: CA Skeleton Frontend Operational Contract
source_type: project-note
status: draft
confidence: medium
tags: [project-note, ca-skeleton, frontend, architecture, testing, observability, security]
related_projects: [ca-skeleton-frontend, ca-skeleton]
last_reviewed: 2026-07-18
diagrams:
- raw/diagrams/ca-skeleton-frontend/architecture-overview-2026-07-18.drawio
- raw/diagrams/ca-skeleton-frontend/architecture-deployment-2026-07-18.drawio
architecture_review:
status: passed-scoped
reviewed_at: 2026-07-18
reviewer: wiki-diagram-reviewer
scores:
overview: 100
deployment: 100
scope:
overview: clean-architecture dependency ownership view
deployment: static asset and /config.json delivery slice
files:
- raw/diagrams/ca-skeleton-frontend/architecture-overview-2026-07-18.drawio
- raw/diagrams/ca-skeleton-frontend/architecture-deployment-2026-07-18.drawio
status_label: active
project_revision: 1
url:
semantic_surface_exclusions:
- artifact-registry|legacy hub has no project-local Artifact Registry; harness/source/typed-contracts.json is authoritative until migration
- contract-gate-registry|legacy hub has no project-local Contract/Gate Registry; harness/source/typed-contracts.json is authoritative until migration
- flow-stage-registry|legacy hub has no project-local Flow/Stage Registry; harness/source/typed-contracts.json is authoritative until migration
imports: [FE-GATE-018@1, FE-GATE-026@1, FE-OC-002@1, FE-OC-003@1, FE-OC-004@1, FE-OC-005@1, FE-OC-006@1, FE-OC-007@1, FE-OC-008@1, FE-OC-009@1, FE-OC-010@1, FE-OC-011@1, FE-OC-012@1, FE-OC-013@1, FE-OC-014@1, FE-OC-015@1, FE-OC-016@1, FE-OC-017@1, FE-OC-018@1, FE-OC-019@1, FE-OC-020@1, FE-OC-021@1, FE-OC-022@1, FE-OC-023@1, FE-OC-024@1, FE-OC-025@1]
---
# CA Skeleton Frontend Operational Contract
> 이 문서는 도메인·비즈니스 기능을 제거한 frontend skeleton의 prospective operational contract다.
> 현재 LLM Wiki workspace에서 manifest·lockfile·Vite config·`src/main` entry pattern을 검색했으나 일치 파일을 찾지 못했다. frontend 구현 repository 위치는 아직 식별되지 않았다.
> test, CI, deploy artifact는 별도 전용 탐색 command를 실행하지 않았으므로 존재 여부가 `UNVERIFIED`다.
> 따라서 본문에 적힌 architecture, command, threshold, file path, component, test, runbook은 모두 `planned` 또는 `documented-only`다.
> 이 문서만으로 `actually-implemented`, `locally-verified`, `prod-verified`를 주장할 수 없다.
---
## 0. 문서 사용 계약
### 0.1 규범 키워드
이 문서의 규범 문장은 다음 의미로 사용한다.
| 키워드 | 의미 | 위반 처리 |
| --- | --- | --- |
| `MUST` | 구현과 검증에 반드시 반영할 project-wide invariant | acceptance gate 실패 |
| `MUST NOT` | 허용하지 않는 구현·운영 상태 | acceptance gate 실패 |
| `SHOULD` | 기본적으로 따르되 예외 근거와 owner 승인이 있으면 변경 가능 | risk 또는 decision row 필요 |
| `MAY` | 조건부 선택 사항 | 활성화 시 owner·test·runbook 필요 |
규범 키워드는 구현 완료 사실이 아니라 앞으로 구현이 따라야 할 계약을 뜻한다.
### 0.2 증거 등급 경계
| 등급 | 현재 허용 여부 | 이 문서에서의 의미 |
| --- | --- | --- |
| `planned` | 허용 | 목표, 기본값, command, artifact path가 문서에만 있음 |
| `documented-only` | 허용 | 근거 raw 또는 설계 문서가 있으나 대응 코드·실행 결과가 없음 |
| `actually-implemented` | 현재 금지 | repository의 구체 path와 commit이 확인되어야 함 |
| `locally-verified` | 현재 금지 | 재현 가능한 command의 exit code와 artifact가 있어야 함 |
| `prod-verified` | 현재 금지 | release ID, 운영 측정, incident 또는 dashboard evidence가 있어야 함 |
현재 workspace에서 다음 탐색은 결과가 없었다.
```bash
rg --files | rg '(^|/)(package\.json|pnpm-lock\.yaml|yarn\.lock|package-lock\.json|bun\.lockb?|vite\.config\.[^/]+|src/main\.(jsx|js))$'
```
이 결과가 증명하는 범위는 **현재 LLM Wiki workspace에서 위 정규식에 해당하는 entry artifact를 찾지 못했다**는 사실뿐이다. 전체 `src/`, test, CI, deploy artifact의 부재나 원격·별도 workspace의 부재로 확장 해석하지 않는다.
### 0.3 현재 판정
```text
Contract maturity: documented-only
Implementation entry evidence: searched patterns not found in current wiki workspace
Diagram files: scoped reviewer PASS — overview 100/100, deployment 100/100
Test evidence: UNVERIFIED — dedicated search/command not recorded
CI evidence: UNVERIFIED — dedicated search/command not recorded
Deployment evidence: UNVERIFIED — dedicated search/command not recorded
Readiness: NOT_READY
```
`NOT_READY`는 설계 문서가 무효라는 뜻이 아니다. 구현·검증·운영 주장을 승격할 evidence gate가 아직 닫히지 않았다는 뜻이다.
### 0.4 원래 목표 → 가정 → 조치
- **목표**: 새 frontend feature가 추가되어도 API 호출, 실패 분류, runtime validation, async UI, telemetry, release rollback을 같은 규칙으로 수행한다.
- **가정 A**: client-only SPA가 browser에서 실행되고 backend API와 분리 배포된다.
- 무효 조건: SSR, server component, edge rendering이 필수인 제품으로 범위가 바뀐다.
- 확인 방법: repository 생성 시 deployment target과 rendering mode를 `FE-D003`에 기록한다.
- **가정 B**: source language는 JavaScript ESM이며 compile-time type coverage가 제한된다.
- 무효 조건: TypeScript strict mode로 project constraint가 변경된다.
- 확인 방법: `package.json`, `jsconfig.json` 또는 `tsconfig.json`과 source extension을 확인한다.
- **가정 C**: backend가 structured JSON envelope와 stable error vocabulary를 제공하거나 frontend adapter가 이를 정규화할 수 있다.
- 무효 조건: 여러 backend가 서로 다른 protocol·schema를 제공하고 통합 adapter를 둘 수 없다.
- 확인 방법: OpenAPI 또는 captured fixture를 runtime schema와 대조한다.
- **문제**: 이 가정 아래에서 owner·default·failure·test가 없으면 page마다 다른 retry, storage, route, error UI가 생기고 release mismatch를 일관되게 복구할 수 없다.
- **조치**: stable `FE-D*`, `FE-OC-*`, registry owner, acceptance gate, runbook을 project hub에 고정하고 상세 구현은 single-owner branch로 위임한다.
- **반대 논거**: 단일 화면 prototype이라면 이 계약의 초기 비용이 기능 가치보다 클 수 있다.
- 확인 방법: route 1개, 외부 API 0개, 배포 0회인 throwaway prototype인지 확인한다.
- 처리: 그런 경우 이 skeleton을 채택하지 않고 별도 experiment로 격리한다.
### 0.5 범위
In scope:
- client-only React SPA의 boot, routing, API boundary, state, cache, storage, render failure, telemetry, build, release, rollback 계약
- JavaScript의 typecheck-equivalent gate와 runtime schema validation
- backend API 및 auth provider와 연결되는 얇은 integration port
- static hosting과 browser runtime의 failure mode
- sample feature slice를 통한 contract enforcement
Out of scope:
- domain-specific page, business rule, copy, branding, product analytics taxonomy
- token 발급, token 저장, refresh token rotation, logout propagation의 lifecycle 소유
- backend authorization 판정 대체
- SSR, RSC, edge rendering, native mobile runtime
- DB, Kafka, JVM, server thread pool, container orchestration 세부 구현
- 특정 CDN·cloud vendor의 console 절차
인증 lifecycle은 [[raw/project-notes/keycloak-patterns-overview]]가 다룬다. 본 skeleton은 외부 auth owner가 제공하는 최소 session interface만 소비한다.
---
## 1. 프로젝트 개요
### 1.1 한 줄 요약
도메인 기능 없이도 새 React SPA가 같은 architecture, API failure language, runtime validation, quality gate, release rollback을 재사용하도록 만드는 frontend operational skeleton이다.
### 1.2 현재 상태
| 항목 | 값 |
| --- | --- |
| 기간 | 2026-07-18 ~ in-progress |
| status | `draft`, `active` |
| 역할 | 설계자 / 향후 구현자 |
| implementation repository | current wiki workspace의 entry artifact search에서 미식별; remote/other workspace `UNVERIFIED` |
| architecture diagram | 2개 scoped review 100/100; implementation·full release topology는 `UNVERIFIED` |
| test / CI / deploy | dedicated evidence search/command 미기록, `UNVERIFIED` |
| 외부 공개 가능 범위 | 설계 의도·검토 대안·계약 구조만 |
### 1.3 해결하려는 문제
1. page마다 `fetch`, timeout, retry, error mapping을 다시 만들면 동일 status가 서로 다른 UX로 나타난다.
2. JavaScript boundary에 runtime validation이 없으면 malformed JSON과 schema drift가 render tree 내부의 `TypeError`로 늦게 나타난다.
3. route, env, query key, storage key, telemetry event, release token이 분산되면 rename과 rollback 영향 범위를 계산하기 어렵다.
4. build-time config와 runtime config를 구분하지 않으면 한 environment의 endpoint가 다른 release bundle에 굳어지거나 public bundle에 secret이 들어갈 수 있다.
5. hashed chunk와 HTML·runtime config가 서로 다른 release를 가리키면 `ChunkLoadError`, boot loop, stale cache가 발생할 수 있다.
6. architecture rule이 문장에만 있으면 presentation이 adapter를 직접 import하고 application port owner가 흐려진다.
### 1.4 성공 조건
아래는 목표이며 아직 측정 결과가 아니다.
| ID | 성공 조건 | 현재 상태 |
| --- | --- | --- |
| `FE-SC-001` | repository, lockfile, bootstrap command가 존재하고 fresh clone install/build가 exit 0 | `planned` |
| `FE-SC-002` | sample slice가 API → schema → mapper → application → presentation을 관통 | `planned` |
| `FE-SC-003` | 금지 import fixture가 architecture gate를 실패시킴 | `planned` |
| `FE-SC-004` | failure taxonomy의 각 blocking row에 최소 1개 automated test가 있음 | `planned` |
| `FE-SC-005` | route/API-operation/env/storage/error/query/telemetry/release registry의 ad hoc token이 0건 | `planned` |
| `FE-SC-006` | lint, checkJs, runtime-schema, unit, component, integration, e2e, a11y, build, bundle, security gate가 CI에서 분리 실행 | `planned` |
| `FE-SC-007` | release mismatch와 rollback runbook이 staging drill evidence를 남김 | `planned` |
| `FE-SC-008` | 두 draw.io 파일이 `wiki-diagram-reviewer` 기준을 통과하고 contract ID와 일치 | `documented-only` — reviewer 100/100, 구현 topology는 UNVERIFIED |
---
## 2. Stable Contract Index
### 2.1 Contract lifecycle
`FE-OC-*` ID는 rename하지 않는다. 의미가 바뀌면 기존 ID를 `superseded`로 남기고 새 ID를 추가한다. branch는 이 표를 복사해 재정의하지 않고 owner로서 상세 mechanism과 test를 제공한다.
| Contract ID | Single owner | Normative summary | Minimum evidence | Status |
| --- | --- | --- | --- | --- |
| `FE-OC-001` | project hub (this file) | 모든 구현 주장은 evidence grade를 MUST 표시하고 repo evidence가 없는 상태에서 구현 완료를 MUST NOT 주장 | evidence ledger | `documented-only` |
| `FE-OC-002` | `feature-frontend-clean-architecture-layering-contract` | `domain <- application <- presentation` 의존 방향과 application-owned output port를 MUST 지킴 | dependency rule report | `planned` |
| `FE-OC-003` | `feature-frontend-project-bootstrap-toolchain-contract` | package manager, engine, lockfile, source language, checkJs command를 한 곳에서 MUST 고정 | manifest + lockfile | `planned` |
| `FE-OC-004` | `feature-frontend-env-runtime-config-contract` | build-time, runtime-public, secret config를 MUST 분리하고 boot 전에 runtime config를 검증 | config schema test | `planned` |
| `FE-OC-005` | `feature-routing-navigation-guard-contract` | route ID/path/params/access/loading/error owner는 route registry 하나여야 함 | route registry snapshot | `planned` |
| `FE-OC-006` | `feature-api-client-response-envelope-contract` | 모든 HTTP는 shared client를 MUST 통과하고 timeout·abort·response parsing을 page에서 구현하면 안 됨 | API contract tests | `planned` |
| `FE-OC-007` | `feature-runtime-schema-validation-contract` | JSON envelope와 payload는 boundary에서 runtime schema를 MUST 통과 | schema fixtures | `planned` |
| `FE-OC-008` | `feature-frontend-error-classification-boundary-contract` | 모든 failure는 stable frontend error kind로 MUST 정규화하고 raw body·stack을 UI에 노출하면 안 됨 | error catalog tests | `planned` |
| `FE-OC-009` | `feature-api-client-response-envelope-contract` | retry는 safe/idempotent request에 한정하고 cap·jitter·`Retry-After`를 MUST 적용 | deterministic retry tests | `planned` |
| `FE-OC-010` | `feature-frontend-auth-session-integration-contract` | skeleton은 session state를 소비하되 token lifecycle을 MUST 소유하지 않음 | port contract test | `planned` |
| `FE-OC-011` | `feature-async-ui-state-contract` | async surface는 initial-loading, success, empty, terminal-error를 MUST 표현 | component state matrix | `planned` |
| `FE-OC-012` | `feature-server-state-caching-contract` | query key와 invalidation은 registry factory만 MUST 사용 | cache tests | `planned` |
| `FE-OC-013` | `feature-frontend-storage-registry-contract` | storage key는 namespace·version·classification을 MUST 가지며 token/secret 저장을 금지 | storage registry tests | `planned` |
| `FE-OC-014` | `feature-frontend-observability-logging-trace-contract` | telemetry는 best-effort이며 render·API success를 차단하면 안 되고 PII·token을 전송하면 안 됨 | redaction + sink failure test | `planned` |
| `FE-OC-015` | `feature-frontend-render-recovery-boundary-contract` | expected operational error와 render defect를 MUST 분리하고 reload loop를 금지 | error boundary tests | `planned` |
| `FE-OC-016` | `feature-frontend-release-cache-rollback-contract` | HTML, asset, runtime config, release manifest cache policy를 MUST 구분 | header evidence | `planned` |
| `FE-OC-017` | `feature-frontend-release-cache-rollback-contract` | rollback은 immutable prior release로 수행하고 build/config/API compatibility를 MUST 검증 | rollback drill artifact | `planned` |
| `FE-OC-018` | `feature-frontend-build-bundle-supply-chain-contract` | frozen lockfile, dependency review, secret scan, SBOM 또는 dependency inventory를 release gate에 MUST 포함 | security artifacts | `planned` |
| `FE-OC-019` | `feature-frontend-browser-security-boundary-contract` | browser bundle에 secret을 넣지 않고 untrusted HTML injection을 기본 금지 | scan + lint tests | `planned` |
| `FE-OC-020` | `feature-frontend-test-taxonomy-contract` | gate 종류별 책임·fixture·artifact를 분리하고 실패를 warning으로 낮추면 안 됨 | CI workflow | `planned` |
| `FE-OC-021` | `feature-web-vitals-performance-budget-contract` | NFR은 device/network/cache/build context와 함께 MUST 측정 | machine-readable report | `planned` |
| `FE-OC-022` | `feature-frontend-contract-registry-governance` | 8개 registry는 single primary owner와 compatibility impact를 MUST 기록 | registry diff check | `planned` |
| `FE-OC-023` | `feature-frontend-contract-compatibility-governance` | API/config/storage/release schema의 breaking change는 migration 또는 version bump 없이 배포하면 안 됨 | compatibility report | `planned` |
| `FE-OC-024` | `feature-sample-feature-slice-contract-fixture` | sample은 contract fixture이며 production feature가 의존하면 안 됨 | sample removal smoke | `planned` |
| `FE-OC-025` | `feature-frontend-operational-runbook-contract` | boot, chunk mismatch, API degradation, telemetry failure, rollback runbook을 MUST 유지 | drill records | `planned` |
| `FE-OC-026` | project hub (this file) | 외부 답변은 evidence grade를 MUST 보존하고 목표 수치를 측정 결과처럼 말하면 안 됨 | answer boundary checklist | `documented-only` |
<!-- section-id: contract-gate-registry -->
### 2.1.1 Contract Registry (typed)
> 위 §2.1 을 기계가 읽는 형식으로 고정한 것이다. 같은 사실이며 새 계약을 만들지 않는다.
> 소비 문서는 이 표를 **복사하지 않고** frontmatter `imports` 에 `FE-OC-0NN@1` 로 pin 한다.
> owner 가 revision 을 올리면 pin 이 낡은 문서가 `STALE_IMPORTED_CONTRACT` 로 잡히고, 두 문서가 같은 계약을 소유하면 `DUPLICATE_CONTRACT_OWNER` 로 막힌다. 남의 계약 표를 다시 적으면 `FOREIGN_CONTRACT_RESTATEMENT` 다.
> `Owner` 값은 문서 slug 다. `FE-OC-001`·`FE-OC-026` 은 §20 서두가 밝힌 대로 이 project hub 가 owner 다.
> `Trigger` 는 §15.1 에서 해당 계약을 Covered FE-OC 로 가진 gate 다 — 여기서 새로 만든 값이 아니다.
> gate(`FE-GATE-*`) 행의 `Owner` 와 `Revision` 은 이 표가 SSOT 다. 각 gate 의 fixture·`Covered FE-OC`·pass condition 규범은 §15.1 이 계속 보유하며 여기로 옮기지 않는다 — 이 표는 *누가 소유하고 몇 번째 판인가*, §15.1 은 *무엇을 검사하는가* 다.
> Owner 는 §15.1 의 `Evidence artifact` 를 §20 `Measurable completion` 이 실제로 산출하는 branch 다. `FE-GATE-017` 만 검토 대상 다이어그램이 hub frontmatter `diagrams:` 소유이므로 hub 가 owner 다.
| Contract ID | Concern Key | Revision | Type | Owner | Trigger | Required Effect | Enforcement | Status |
|---|---|---|---|---|---|---|---|---|
| `FE-OC-001` | `fe.evidence-grade` | 1 | operational-contract | `ca-skeleton-frontend-operational-contract` | 구현 주장을 문서에 쓸 때 | 모든 구현 주장은 evidence grade를 MUST 표시하고 repo evidence가 없는 상태에서 구현 완료를 MUST NOT 주장 | evidence ledger | active |
| `FE-OC-002` | `fe.clean-architecture-layering` | 1 | operational-contract | `feature-frontend-clean-architecture-layering-contract` | layer 간 import 를 추가·변경할 때 | `domain <- application <- presentation` 의존 방향과 application-owned output port를 MUST 지킴 | dependency rule report | active |
| `FE-OC-003` | `fe.project-bootstrap-toolchain` | 1 | operational-contract | `feature-frontend-project-bootstrap-toolchain-contract` | toolchain·manifest·lockfile 을 변경할 때 | package manager, engine, lockfile, source language, checkJs command를 한 곳에서 MUST 고정 | manifest + lockfile | active |
| `FE-OC-004` | `fe.env-runtime-config` | 1 | operational-contract | `feature-frontend-env-runtime-config-contract` | config key 를 추가하거나 boot 순서를 바꿀 때 | build-time, runtime-public, secret config를 MUST 분리하고 boot 전에 runtime config를 검증 | config schema test | active |
| `FE-OC-005` | `fe.routing-navigation-guard` | 1 | operational-contract | `feature-routing-navigation-guard-contract` | route 를 추가·변경할 때 | route ID/path/params/access/loading/error owner는 route registry 하나여야 함 | route registry snapshot | active |
| `FE-OC-006` | `fe.api-client.shared-transport` | 1 | operational-contract | `feature-api-client-response-envelope-contract` | HTTP 요청을 보낼 때 | 모든 HTTP는 shared client를 MUST 통과하고 timeout·abort·response parsing을 page에서 구현하면 안 됨 | API contract tests | active |
| `FE-OC-007` | `fe.runtime-schema-validation` | 1 | operational-contract | `feature-runtime-schema-validation-contract` | 외부 응답을 경계에서 받을 때 | JSON envelope와 payload는 boundary에서 runtime schema를 MUST 통과 | schema fixtures | active |
| `FE-OC-008` | `fe.error-classification-boundary` | 1 | operational-contract | `feature-frontend-error-classification-boundary-contract` | failure 가 발생할 때 | 모든 failure는 stable frontend error kind로 MUST 정규화하고 raw body·stack을 UI에 노출하면 안 됨 | error catalog tests | active |
| `FE-OC-009` | `fe.api-client.retry-policy` | 1 | operational-contract | `feature-api-client-response-envelope-contract` | 요청이 실패해 재시도를 판단할 때 | retry는 safe/idempotent request에 한정하고 cap·jitter·`Retry-After`를 MUST 적용 | deterministic retry tests | active |
| `FE-OC-010` | `fe.auth-session-integration` | 1 | operational-contract | `feature-frontend-auth-session-integration-contract` | session 상태를 읽거나 갱신할 때 | skeleton은 session state를 소비하되 token lifecycle을 MUST 소유하지 않음 | port contract test | active |
| `FE-OC-011` | `fe.async-ui-state` | 1 | operational-contract | `feature-async-ui-state-contract` | async surface 를 렌더할 때 | async surface는 initial-loading, success, empty, terminal-error를 MUST 표현 | component state matrix | active |
| `FE-OC-012` | `fe.server-state-caching` | 1 | operational-contract | `feature-server-state-caching-contract` | server state 를 캐시하거나 무효화할 때 | query key와 invalidation은 registry factory만 MUST 사용 | cache tests | active |
| `FE-OC-013` | `fe.storage-registry` | 1 | operational-contract | `feature-frontend-storage-registry-contract` | browser storage 에 값을 쓸 때 | storage key는 namespace·version·classification을 MUST 가지며 token/secret 저장을 금지 | storage registry tests | active |
| `FE-OC-014` | `fe.observability-logging-trace` | 1 | operational-contract | `feature-frontend-observability-logging-trace-contract` | telemetry event 를 emit 할 때 | telemetry는 best-effort이며 render·API success를 차단하면 안 되고 PII·token을 전송하면 안 됨 | redaction + sink failure test | active |
| `FE-OC-015` | `fe.render-recovery-boundary` | 1 | operational-contract | `feature-frontend-render-recovery-boundary-contract` | render 중 예외가 boundary 에 도달할 때 | expected operational error와 render defect를 MUST 분리하고 reload loop를 금지 | error boundary tests | active |
| `FE-OC-016` | `fe.release.cache-policy` | 1 | operational-contract | `feature-frontend-release-cache-rollback-contract` | release asset 을 배포하거나 cache header 를 정할 때 | HTML, asset, runtime config, release manifest cache policy를 MUST 구분 | header evidence | active |
| `FE-OC-017` | `fe.release.rollback` | 1 | operational-contract | `feature-frontend-release-cache-rollback-contract` | rollback 을 수행할 때 | rollback은 immutable prior release로 수행하고 build/config/API compatibility를 MUST 검증 | rollback drill artifact | active |
| `FE-OC-018` | `fe.build-bundle-supply-chain` | 1 | operational-contract | `feature-frontend-build-bundle-supply-chain-contract` | 의존성을 설치하거나 production build 를 만들 때 | frozen lockfile, dependency review, secret scan, SBOM 또는 dependency inventory를 release gate에 MUST 포함 | security artifacts | active |
| `FE-OC-019` | `fe.browser-security-boundary` | 1 | operational-contract | `feature-frontend-browser-security-boundary-contract` | bundle·HTML·env 에 값을 넣을 때 | browser bundle에 secret을 넣지 않고 untrusted HTML injection을 기본 금지 | scan + lint tests | active |
| `FE-OC-020` | `fe.test-taxonomy` | 1 | operational-contract | `feature-frontend-test-taxonomy-contract` | gate 나 fixture 를 추가·변경할 때 | gate 종류별 책임·fixture·artifact를 분리하고 실패를 warning으로 낮추면 안 됨 | CI workflow | active |
| `FE-OC-021` | `fe.web-vitals-performance-budget` | 1 | operational-contract | `feature-web-vitals-performance-budget-contract` | NFR 을 측정하거나 보고할 때 | NFR은 device/network/cache/build context와 함께 MUST 측정 | machine-readable report | active |
| `FE-OC-022` | `fe.contract-registry` | 1 | operational-contract | `feature-frontend-contract-registry-governance` | 8개 registry 중 하나를 변경할 때 | 8개 registry는 single primary owner와 compatibility impact를 MUST 기록 | registry diff check | active |
| `FE-OC-023` | `fe.contract-compatibility` | 1 | operational-contract | `feature-frontend-contract-compatibility-governance` | API·config·storage·release schema 를 변경할 때 | API/config/storage/release schema의 breaking change는 migration 또는 version bump 없이 배포하면 안 됨 | compatibility report | active |
| `FE-OC-024` | `fe.sample-feature-slice-contract` | 1 | operational-contract | `feature-sample-feature-slice-contract-fixture` | sample slice 를 만들거나 제거할 때 | sample은 contract fixture이며 production feature가 의존하면 안 됨 | sample removal smoke | active |
| `FE-OC-025` | `fe.operational-runbook` | 1 | operational-contract | `feature-frontend-operational-runbook-contract` | 운영 장애가 발생하거나 drill 을 돌릴 때 | boot, chunk mismatch, API degradation, telemetry failure, rollback runbook을 MUST 유지 | drill records | active |
| `FE-OC-026` | `fe.answer-boundary` | 1 | operational-contract | `ca-skeleton-frontend-operational-contract` | 외부 공개 답변을 작성할 때 | 외부 답변은 evidence grade를 MUST 보존하고 목표 수치를 측정 결과처럼 말하면 안 됨 | answer boundary checklist | active |
| `FE-GATE-001` | `fe.gate.manifest-lockfile` | 1 | gate | `feature-frontend-project-bootstrap-toolchain-contract` | 의존성을 설치하거나 lockfile 을 변경할 때 | lockfile 이 manifest 와 어긋나면 merge·release 를 MUST 차단 | install log | active |
| `FE-GATE-002` | `fe.gate.lint` | 1 | gate | `feature-frontend-architecture-enforcement-lint-contract` | 소스를 수정해 merge 를 요청할 때 | 금지된 API·import 가 남아 있으면 merge 를 MUST 차단 | lint report | active |
| `FE-GATE-003` | `fe.gate.typecheck` | 1 | gate | `feature-frontend-project-bootstrap-toolchain-contract` | 타입 주석이나 checkJs 설정을 변경할 때 | production diagnostic 이 남아 있으면 merge 를 MUST 차단 | check-types report | active |
| `FE-GATE-004` | `fe.gate.runtime-schema` | 1 | gate | `feature-runtime-schema-validation-contract` | 경계에서 외부 응답·boot config 를 받을 때 | invalid fixture 가 예상 kind 로 거부되지 않으면 merge 를 MUST 차단 | schema + timing report | active |
| `FE-GATE-005` | `fe.gate.unit` | 1 | gate | `feature-frontend-test-taxonomy-contract` | unit 레벨 테스트를 추가·변경할 때 | unit 레벨이 실패하면 merge 를 MUST 차단하고 warning 으로 낮추면 안 됨 | unit XML | active |
| `FE-GATE-006` | `fe.gate.component` | 1 | gate | `feature-frontend-test-taxonomy-contract` | component 레벨 테스트를 추가·변경할 때 | component 레벨이 실패하면 merge 를 MUST 차단 | component XML | active |
| `FE-GATE-007` | `fe.gate.integration` | 1 | gate | `feature-frontend-test-taxonomy-contract` | integration 레벨 테스트를 추가·변경할 때 | MSW 기반 integration 매트릭스가 미충족이면 merge 를 MUST 차단 | integration XML | active |
| `FE-GATE-008` | `fe.gate.e2e` | 1 | gate | `feature-frontend-test-taxonomy-contract` | critical 사용자 시나리오를 변경할 때 | critical e2e 시나리오가 실패하면 merge·release 를 MUST 차단 | Playwright report | active |
| `FE-GATE-009` | `fe.gate.accessibility` | 1 | gate | `feature-accessibility-baseline-contract` | sample route 의 UI 를 변경할 때 | automated threshold 미달이거나 manual checklist 서명이 없으면 merge·release 를 MUST 차단 | a11y artifacts | active |
| `FE-GATE-010` | `fe.gate.architecture` | 1 | gate | `feature-frontend-architecture-enforcement-lint-contract` | layer 간 import 를 추가·변경할 때 | 금지된 layer import 가 통과하면 merge 를 MUST 차단 | dependency report | active |
| `FE-GATE-011` | `fe.gate.build` | 1 | gate | `feature-frontend-build-bundle-supply-chain-contract` | production build 를 만들 때 | clean production build 가 실패하거나 기대 artifact 가 없으면 merge·release 를 MUST 차단 | build manifest | active |
| `FE-GATE-012` | `fe.gate.bundle` | 1 | gate | `feature-frontend-build-bundle-supply-chain-contract` | 번들 구성이나 chunk 분할을 바꿀 때 | 번들 NFR threshold 초과면 release 를 MUST 차단 | bundle report | active |
| `FE-GATE-013` | `fe.gate.security` | 1 | gate | `feature-frontend-build-bundle-supply-chain-contract` | 의존성·시크릿·라이선스 표면을 변경할 때 | secret·vulnerability·license·dependency review 정책 위반이면 merge·release 를 MUST 차단 | SARIF/inventory/dependency diff report | active |
| `FE-GATE-014` | `fe.gate.config-compatibility` | 1 | gate | `feature-frontend-contract-compatibility-governance` | config schema 를 변경해 release 할 때 | 지원 대상 config 버전이 boot 에 실패하면 release 를 MUST 차단 | compatibility report | active |
| `FE-GATE-015` | `fe.gate.release-coherence` | 1 | gate | `feature-frontend-release-cache-rollback-contract` | HTML·asset·config 를 한 release 로 묶을 때 | 혼재된 release 조합이 감지되지 않으면 release 를 MUST 차단 | release verification | active |
| `FE-GATE-016` | `fe.gate.rollback-drill` | 1 | gate | `feature-frontend-release-cache-rollback-contract` | 직전 release 로 되돌릴 때 | rollback 과 smoke 증거가 없으면 production promotion 을 MUST 차단 | drill record | active |
| `FE-GATE-017` | `fe.gate.diagram-review` | 1 | gate | `ca-skeleton-frontend-operational-contract` | hub 소유 아키텍처 다이어그램을 갱신할 때 | scoped 다이어그램 2종이 reviewer threshold 미달이면 documentation readiness 를 MUST 차단 | reviewer report | active |
| `FE-GATE-018` | `fe.gate.field-web-vitals` | 1 | gate | `feature-web-vitals-performance-budget-contract` | field 측정 창을 마감해 보고할 때 | p75 목표 미달이거나 표본 임계가 미해결이면 field readiness 를 MUST 차단 | field Web Vitals report | active |
| `FE-GATE-019` | `fe.gate.hosting-header` | 2 | gate | `feature-frontend-release-cache-rollback-contract` | hosting 의 header(cache·security) 설정을 배포할 때 | 선언한 Cache-Control·content-type·security header 와 실제 응답이 다르면 release 를 MUST 차단 | hosting header report | active |
| `FE-GATE-020` | `fe.gate.sample-removal` | 1 | gate | `feature-sample-feature-slice-contract-fixture` | sample slice 를 제거하거나 제품이 참조할 때 | sample 제거 후 build·smoke 가 실패하면 merge·release 를 MUST 차단 | sample-removal report | active |
| `FE-GATE-021` | `fe.gate.runbook-boot-config` | 1 | gate | `feature-frontend-operational-runbook-contract` | boot config 실패 drill 을 돌릴 때 | `FE-RB-001` 의 containment·escalation·recovery 단언이 실패하면 production promotion 을 MUST 차단 | `FE-RB-001` record | active |
| `FE-GATE-022` | `fe.gate.runbook-chunk-mismatch` | 1 | gate | `feature-frontend-operational-runbook-contract` | chunk·release manifest 실패 drill 을 돌릴 때 | `FE-RB-002` 의 containment·escalation·recovery 단언이 실패하면 production promotion 을 MUST 차단 | `FE-RB-002` record | active |
| `FE-GATE-023` | `fe.gate.runbook-api-degradation` | 1 | gate | `feature-frontend-operational-runbook-contract` | API degradation drill 을 돌릴 때 | `FE-RB-003` 의 containment·escalation·recovery 단언이 실패하면 production promotion 을 MUST 차단 | `FE-RB-003` record | active |
| `FE-GATE-024` | `fe.gate.runbook-telemetry` | 1 | gate | `feature-frontend-operational-runbook-contract` | telemetry degradation drill 을 돌릴 때 | `FE-RB-004` 의 containment·escalation·recovery 단언이 실패하면 production promotion 을 MUST 차단 | `FE-RB-004` record | active |
| `FE-GATE-025` | `fe.gate.runbook-release-rollback` | 1 | gate | `feature-frontend-operational-runbook-contract` | release 차단 결함으로 rollback 을 판단할 때 | `FE-RB-005` 의 rollback 결정·escalation·recovery 단언이 실패하면 production promotion 을 MUST 차단 | `FE-RB-005` record | active |
| `FE-GATE-026` | `fe.gate.lab-performance` | 1 | gate | `feature-web-vitals-performance-budget-contract` | lab 성능을 측정해 보고할 때 | lab threshold 미달이거나 재현 메타데이터가 없으면 release 를 MUST 차단 | lab performance report | active |
<!-- section-id: artifact-registry -->
### 2.1.3 Artifact Registry (typed)
> 두 개 이상의 branch 가 같은 파일의 필드를 **각자** 정하고 있던 artifact 만 등록한다. 단일 branch 전용 artifact 는 desync 원인이 아니므로 넣지 않는다.
> `Schema Ref` 는 실제 JSON Schema 파일이며 검사기가 존재를 확인한다. 필드 추가·rename 은 `Schema Owner` 단독 결정이고, 소비 branch 는 본문에 스키마를 옮겨 적지 않고 frontmatter `imports` 에 `ART-FE-0NN@1` 로 pin 한다.
> **JSON artifact 필드 명명은 camelCase** 로 통일한다 — `artifacts/**` 의 report 파일에 한하며, telemetry attribute 어휘(§11.1 allowlist, snake_case)는 별개 규약이다.
| Artifact ID | Revision | Name | Schema Owner | Producer | Consumers | Schema Ref | Status |
|---|---|---|---|---|---|---|---|
| `ART-FE-001` | 1 | build manifest | `feature-frontend-project-bootstrap-toolchain-contract` | `feature-frontend-project-bootstrap-toolchain-contract` | `feature-frontend-build-bundle-supply-chain-contract`, `feature-frontend-release-cache-rollback-contract`, `feature-frontend-test-taxonomy-contract` | `harness/source/artifact-schemas/ca-skeleton-frontend/build-manifest.schema.json` | active |
| `ART-FE-002` | 1 | bundle report | `feature-frontend-build-bundle-supply-chain-contract` | `feature-frontend-build-bundle-supply-chain-contract` | `feature-web-vitals-performance-budget-contract`, `feature-frontend-test-taxonomy-contract` | `harness/source/artifact-schemas/ca-skeleton-frontend/bundle-report.schema.json` | active |
| `ART-FE-003` | 1 | release verification | `feature-frontend-release-cache-rollback-contract` | `feature-frontend-release-cache-rollback-contract` | `feature-frontend-contract-compatibility-governance` | `harness/source/artifact-schemas/ca-skeleton-frontend/release-verification.schema.json` | active |
| `ART-FE-004` | 1 | a11y report | `feature-accessibility-baseline-contract` | `feature-accessibility-baseline-contract` | `feature-frontend-test-taxonomy-contract` | `harness/source/artifact-schemas/ca-skeleton-frontend/a11y-report.schema.json` | active |
<!-- section-id: flow-stage-registry -->
### 2.1.4 Flow Stage Registry (typed)
> §7.3 응답 처리 순서 8단계에 **단계별 owner** 를 붙인 것이다. 순서 자체는 §7.3 이 계속 소유하고, 이 표는 *각 단계를 누가 소유하며 그 단계가 지켜야 할 불변식이 무엇인가* 를 고정한다.
> 이 표가 없을 때 stage 4~6 의 throw/non-throw 경계와 stage 7 산출물(model vs view-model)이 branch 마다 다르게 적혀 있었다. 단계 계약을 바꾸려면 owner 가 revision 을 올리고, 인접 단계 branch 는 pin 이 낡아 `STALE_IMPORTED_CONTRACT` 로 잡힌다.
| Stage ID | Order | Owner | Input | Action | Output | Invariants | Revision |
|---|---:|---|---|---|---|---|---|
| `FLOW-FE-RESP-001` | 1 | `feature-api-client-response-envelope-contract` | HTTP 요청 | transport 완료 대기 | raw Response | timeout·abort 는 이 단계가 소유하고 이후 단계로 예외를 넘기지 않는다 | 1 |
| `FLOW-FE-RESP-002` | 2 | `feature-api-client-response-envelope-contract` | raw Response | content-type 기대값 검사 | 본문 판독 가능 Response | 기대와 다르면 본문을 파싱하지 않고 실패로 전환 | 1 |
| `FLOW-FE-RESP-003` | 3 | `feature-api-client-response-envelope-contract` | 본문 판독 가능 Response | JSON parse | unvalidated JSON | parse 실패는 raw body 를 버리고 실패로 전환 | 1 |
| `FLOW-FE-RESP-004` | 4 | `feature-runtime-schema-validation-contract` | unvalidated JSON | envelope 공유 스키마 검증 | discriminated envelope | 경계 검증은 `.safeParse()` non-throwing — throw 를 상위로 누출하지 않는다 | 1 |
| `FLOW-FE-RESP-005` | 5 | `feature-runtime-schema-validation-contract` | discriminated envelope | success/failure 분기 검증 | 분기 확정 envelope | 200 이어도 envelope 이 invalid 하면 success 로 반환하지 않는다 | 1 |
| `FLOW-FE-RESP-006` | 6 | `feature-runtime-schema-validation-contract` | 분기 확정 envelope | payload per-operation 스키마 검증 | 검증된 payload(deep clone) | payload invalid 는 `SCHEMA_MISMATCH`; mapper 는 검증 통과분만 받는다 | 1 |
| `FLOW-FE-RESP-007` | 7 | `feature-boundary-mapper-viewmodel-contract` | 검증된 payload | DTO → application model 매핑 | application model | 이 단계 산출물은 model 이고 view-model 이 아니다 — view-model 투영은 `application/view-models/` 소유(§4.2·§4.4 2-stage) | 1 |
| `FLOW-FE-RESP-008` | 8 | `feature-frontend-error-classification-boundary-contract` | application model 또는 실패 신호 | 정규화된 결과 반환 | application result 또는 normalized failure | 총함수 — 미매핑 예외는 `UNKNOWN_FAILURE` 로 귀결하고 throw 를 presentation 으로 통과시키지 않는다 | 1 |
<!-- section-id: delegation-registry -->
### 2.1.2 Delegation Registry (typed)
> 한 branch 가 다른 branch 에 관심사를 넘길 때 여기에 행을 만든다. `Status` 가 `accepted` 가 되려면
> **delegate 쪽 문서가 frontmatter `accepts_delegations` 로 접수해야** 한다. 접수 전에는 `proposed` 이고
> `UNACCEPTED_DELEGATION` 으로 계속 잡힌다 — "A 가 넘겼는데 B 는 받은 적 없는" 공백이 조용히 남지 않게 하는 장치다.
> 아래 6행은 2026-07-20 문서 간 정합성 감사에서 **미접수 위임으로 발견됐고, 이후 delegate 6곳이 모두 `accepts_delegations` 로 접수해 현재는 전부 `accepted`** 다(2026-07-21 frontmatter 왕복 대조 6/6 일치, `UNACCEPTED_DELEGATION` 0건). 즉 이 표는 지금 열려 있는 공백 목록이 아니라 닫힌 위임의 등록부다.
| Delegation ID | Concern Key | Revision | Delegator | Delegate | Scope | Status |
|---|---|---|---|---|---|---|
| `DELEG-FE-001` | `fe.deleg.dynamic-class-lint` | 1 | `feature-tailwind-design-token-styling-contract` | `feature-frontend-browser-security-boundary-contract` | dynamic/untrusted class-string 구성 금지의 정적 lint 강제 | accepted |
| `DELEG-FE-002` | `fe.deleg.lint-toolchain-substrate` | 1 | `feature-frontend-architecture-enforcement-lint-contract` | `feature-frontend-project-bootstrap-toolchain-contract` | `eslint.config.js`·`.dependency-cruiser.cjs` 설치와 base flat-config substrate | accepted |
| `DELEG-FE-003` | `fe.deleg.composition-root-review` | 1 | `feature-frontend-architecture-enforcement-lint-contract` | `feature-frontend-clean-architecture-layering-contract` | composition-root business-rule 혼입에 대한 코드리뷰 체크리스트 | accepted |
| `DELEG-FE-004` | `fe.deleg.color-contrast` | 1 | `feature-accessibility-baseline-contract` | `feature-tailwind-design-token-styling-contract` | color contrast token 값 결정 | accepted |
| `DELEG-FE-005` | `fe.deleg.injectable-random` | 1 | `feature-api-client-response-envelope-contract` | `feature-frontend-clean-architecture-layering-contract` | full-jitter backoff 를 결정론 테스트 가능하게 하는 random source 주입 형태 | accepted |
| `DELEG-FE-006` | `fe.deleg.reload-once-action` | 1 | `feature-async-ui-state-contract` | `feature-frontend-render-recovery-boundary-contract` | `reload-once` action 의 실제 실행(5-condition guard 경유) | accepted |
### 2.2 Universal acceptance questions
`FE-OC-*` owner branch는 완료 전에 다음 질문에 답해야 한다.
1. 이 contract가 막는 concrete failure는 무엇인가?
2. input과 output은 무엇인가?
3. project-wide default와 limit은 무엇인가?
4. 허용되는 예외와 승인 owner는 누구인가?
5. 금지 구현은 무엇인가?
6. failure가 어떤 normalized error와 UX로 나타나는가?
7. 어떤 telemetry가 남고 어떤 data가 redacted되는가?
8. 어떤 test가 위반 시 실패하는가?
9. 어떤 evidence artifact가 생성되는가?
10. release 또는 rollback에 미치는 영향은 무엇인가?
하나라도 비어 있으면 branch는 `documented-only`를 넘을 수 없다.
---
## 3. Stable Decision Register
> **Legacy reference (v1).** 기존 `FE-D*` 식별자와 세부 rationale은 이력·설명용으로 보존한다. project-wide 결정의 현재 owner와 branch 상속 기준은 아래 `## 6.1 Project Decision Registry / 안정 결정 레지스트리`다.
### 3.1 Decision status
| status | 의미 |
| --- | --- |
| `conditional-default` | 현재 project default지만 trigger가 오면 재검토 |
| `accepted-documented-only` | 문서상 채택, 코드 evidence 없음 |
| `deferred` | owner와 trigger만 있고 선택 미확정 |
| `superseded` | 후속 FE-D row로 대체, 삭제 금지 |
<!-- section-id: legacy-decision-rows -->
### 3.2 Decision rows
> `FE-D*` 는 v1 결정 레지스터다. project-wide 결정의 현재 owner 는 §6.1 Project Decision Registry(`DEC-...`)이며 이 표는 이력·설명용으로 보존한다.
> `Affected FE-OC` 열은 결정과 계약의 **대응 관계**이지 계약 내용의 사본이 아니다.
| Decision ID | Decision | Status | Owner | Affected FE-OC | Evidence / rationale | Revisit trigger | Supersedes |
| --- | --- | --- | --- | --- | --- | --- | --- |
| `FE-D001` | package manager default는 `pnpm`; `packageManager` field와 `pnpm-lock.yaml`을 commit | `conditional-default` | `feature-frontend-project-bootstrap-toolchain-contract` | `FE-OC-003`, `FE-OC-020` | project-local reproducibility default, 외부 source claim 아님 | 조직 표준이 npm/yarn/Bun을 강제하거나 target CI가 pnpm을 지원하지 않음 | — |
| `FE-D002` | source는 JavaScript ESM, typecheck-equivalent는 `tsc --allowJs --checkJs --noEmit` | `accepted-documented-only` | `feature-frontend-project-bootstrap-toolchain-contract` | `FE-OC-003`, `FE-OC-007`, `FE-OC-020` | 사용자 제약 + runtime schema 필요성 | TypeScript strict 전환 승인 | — |
| `FE-D003` | Vite client-only SPA를 build baseline으로 사용 | `accepted-documented-only` | `feature-frontend-project-bootstrap-toolchain-contract` | `FE-OC-003`, `FE-OC-016`, `FE-OC-021` | [[raw/official-docs/vite-build-tool-official]] `VITE-C2` | SSR/SEO/edge rendering이 product requirement가 됨 | — |
| `FE-D004` | UI composition은 React를 사용 | `accepted-documented-only` | `feature-async-ui-state-contract` | `FE-OC-002`, `FE-OC-011`, `FE-OC-015` | [[raw/official-docs/react-ui-library-official]] `REACT-UI-C1` | native/custom-element 또는 다른 framework로 project fork | — |
| `FE-D005` | styling default는 Tailwind theme token + component primitive | `conditional-default` | `feature-tailwind-design-token-styling-contract` | `FE-OC-011`, `FE-OC-019`, `FE-OC-021` | [[raw/official-docs/tailwind-css-utility-first-official]] `TAILWIND-UTIL-C1`, `TAILWIND-UTIL-C2`, `TAILWIND-UTIL-C4` | runtime theming 또는 product design system이 다른 compiler를 요구 | — |
| `FE-D006` | server state policy는 application-owned `QueryCachePort`가 정의하고 TanStack Query adapter가 구현하며 client store에 복제하지 않음 | `accepted-documented-only` | `feature-server-state-caching-contract` | `FE-OC-011`, `FE-OC-012` | [[raw/official-docs/tanstack-query-server-state-official]] `TSQ-C1`, `TSQ-C3`, `TSQ-C5`; port ownership·non-duplication은 project decision | offline-first normalized entity cache가 필요 | — |
| `FE-D007` | boundary runtime validation은 Zod schema로 수행 | `accepted-documented-only` | `feature-runtime-schema-validation-contract` | `FE-OC-007`, `FE-OC-008` | [[raw/official-docs/zod-runtime-schema-validation-official]] `ZOD-VALID-C2`, `ZOD-VALID-C3`, `ZOD-VALID-C4` | bundle budget 또는 generated schema pipeline이 대체안을 요구 | — |
| `FE-D008` | routing은 React Router Declarative Mode를 default로 사용 | `conditional-default` | `feature-routing-navigation-guard-contract` | `FE-OC-005`, `FE-OC-015` | [[raw/official-docs/react-router-official]] `REACT-ROUTER-C1`, `REACT-ROUTER-C4` | data router/framework mode가 loader·SSR requirement로 필요 | — |
| `FE-D009` | `domain`, `application`, `presentation`, `adapters`, `bootstrap` responsibility를 분리 | `accepted-documented-only` | `feature-frontend-clean-architecture-layering-contract` | `FE-OC-002` | [[raw/project-notes/ca-skeleton-operational-contract]]의 운영 계약 철학을 frontend에 적용 | sample slice가 불필요한 ceremony를 증명하거나 FSD fork 승인 | — |
| `FE-D010` | output port interface는 `application`이 소유하고 adapter가 구현 | `accepted-documented-only` | `feature-frontend-clean-architecture-layering-contract` | `FE-OC-002` | dependency inversion의 project decision | port가 domain invariant 자체를 표현해야 하는 concrete case 발생 | — |
| `FE-D011` | composition root는 `bootstrap` 하나이며 concrete adapter를 application에 주입 | `accepted-documented-only` | `feature-frontend-clean-architecture-layering-contract` | `FE-OC-002`, `FE-OC-004` | owner ambiguity 제거 | framework DI container 도입 | — |
| `FE-D012` | deploy별 public value는 pre-render runtime config, compiler value는 build-time config로 분리 | `conditional-default` | `feature-frontend-env-runtime-config-contract` | `FE-OC-004`, `FE-OC-016`, `FE-OC-023` | environment-specific rebuild 감소; project inference | hosting이 runtime config atomic publish를 지원하지 않음 | — |
| `FE-D013` | runtime config fallback은 environment별 rebuild를 허용하되 한 artifact를 여러 env에 재사용하지 않음 | `conditional-default` | `feature-frontend-env-runtime-config-contract` | `FE-OC-004`, `FE-OC-016`, `FE-OC-023` | fallback의 deploy ambiguity 제한 | runtime config endpoint 도입 | — |
| `FE-D014` | default request timeout은 total 10s; 별도 connect timeout은 browser API가 직접 제공하지 않으므로 주장하지 않음 | `conditional-default` | `feature-api-client-response-envelope-contract` | `FE-OC-006`, `FE-OC-009`, `FE-OC-021` | project-local initial limit | measured p95가 10s를 정당하게 초과하거나 streaming 도입 | — |
| `FE-D015` | retry는 initial call 이후 최대 2회, exponential backoff + full jitter, cap 2s | `conditional-default` | `feature-api-client-response-envelope-contract` | `FE-OC-009`, `FE-OC-021` | retry storm 억제를 위한 project default | backend SLO·rate limit contract 확정 | — |
| `FE-D016` | mutation 자동 retry는 stable idempotency key와 backend replay contract가 있을 때만 허용 | `accepted-documented-only` | `feature-api-client-response-envelope-contract` | `FE-OC-009`, `FE-OC-023` | duplicate write 방지 invariant | mutation이 naturally idempotent임이 schema로 증명 | — |
| `FE-D017` | auth lifecycle은 외부 owner, skeleton은 `AuthSessionPort`만 소비 | `accepted-documented-only` | `feature-frontend-auth-session-integration-contract` | `FE-OC-010` | [[raw/project-notes/keycloak-patterns-overview]] | skeleton이 독립 auth product로 scope 변경 | — |
| `FE-D018` | route/API-operation/env/storage/error/query/telemetry/release token은 8개 registry로 관리 | `accepted-documented-only` | `feature-frontend-contract-registry-governance` | `FE-OC-013`, `FE-OC-022` | rename·compatibility 영향 추적 | code generation SSOT 채택 | — |
| `FE-D019` | service worker와 offline asset cache는 default off | `conditional-default` | `feature-frontend-release-cache-rollback-contract` | `FE-OC-016`, `FE-OC-017`, `FE-OC-023` | stale asset·config mismatch surface 축소 | offline product requirement와 update UX가 설계됨 | — |
| `FE-D020` | hashed asset은 immutable, HTML·runtime config·release manifest는 revalidate/no-store 정책 분리 | `accepted-documented-only` | `feature-frontend-release-cache-rollback-contract` | `FE-OC-016`, `FE-OC-017` | release coherence invariant | hosting cache primitive 제약 | — |
| `FE-D021` | telemetry는 best-effort queue + redaction, sink failure는 UI를 실패시키지 않음 | `accepted-documented-only` | `feature-frontend-observability-logging-trace-contract` | `FE-OC-014` | operational isolation | regulated audit event처럼 delivery guarantee가 필요한 별도 channel 도입 | — |
| `FE-D022` | test stack default는 Vitest + RTL + MSW + Playwright + axe | `conditional-default` | `feature-frontend-test-taxonomy-contract` | `FE-OC-020` | Vite/browser/component/e2e responsibility 분리 | organization test platform이 대체 | — |
| `FE-D023` | static release는 immutable release directory + atomic active pointer로 배포 | `conditional-default` | `feature-frontend-release-cache-rollback-contract` | `FE-OC-016`, `FE-OC-017`, `FE-OC-025` | rollback 가능 artifact requirement | provider가 다른 atomic primitive만 제공 | — |
| `FE-D024` | dependency lock, secret scan, vulnerability scan, license inventory, dependency review를 merge/release gate로 분리 (lock 은 `FE-GATE-001`, 나머지 4개는 `FE-GATE-013`) | `accepted-documented-only` | `feature-frontend-build-bundle-supply-chain-contract` | `FE-OC-018`, `FE-OC-019`, `FE-OC-020` | supply-chain scope 최소값 | organization security policy가 더 강한 gate 지정 | — |
| `FE-D025` | sample slice는 제거 가능한 contract fixture이며 product import를 금지 | `accepted-documented-only` | `feature-sample-feature-slice-contract-fixture` | `FE-OC-024` | backend skeleton의 sample-fixture 운영 원칙을 frontend에 적용 | fixture 없이 동일 gate coverage를 증명 | — |
### 3.3 Decision change protocol
1. 변경 제안자는 새 `FE-D*`를 만들지, 기존 row의 compatible clarification인지 분류한다.
2. owner는 영향을 받는 `FE-OC-*`와 registry row를 나열한다.
3. `compatibility_impact``none`, `additive`, `behavior-change`, `breaking` 중 하나로 기록한다.
4. `behavior-change``breaking`은 migration·rollback·test evidence 없이 merge하지 않는다.
5. 기존 의미를 대체하면 기존 row를 `superseded`로 바꾸고 `Supersedes` chain을 연결한다.
6. source link가 추가되면 실제 raw 파일만 사용한다. placeholder wikilink를 만들지 않는다.
7. implementation repository가 생기면 commit·path·test artifact를 evidence ledger에 추가한다.
8. hub와 owner branch가 모순되면 project-wide default를 바꾸기 전 이 register를 먼저 갱신한다.
<!-- section-id: project-decisions -->
## 6.1 안정 결정 레지스트리
> Project contract v2의 project-wide decision SSOT. 기존 `FE-D*`는 아래 stable ID로 일대일 이관되며 branch는 `DEC-...@1`만 pin한다.
| Decision ID | Revision | Domain | Decision Summary | Status | Owner | Evidence |
|---|---:|---|---|---|---|---|
| `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-TOOLCHAIN-001` | 1 | `toolchain` | package manager default는 pnpm이며 packageManager field와 pnpm-lock.yaml을 commit한다 | `conditional-default` | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] | §3.2 `FE-D001` |
| `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-LANGUAGE-001` | 1 | `language` | JavaScript ESM과 tsc allowJs/checkJs/noEmit을 typecheck-equivalent baseline으로 사용한다 | `accepted-documented-only` | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] | §3.2 `FE-D002` |
| `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-BUILD-001` | 1 | `build` | Vite client-only SPA를 build baseline으로 사용한다 | `accepted-documented-only` | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] | §3.2 `FE-D003`; [[raw/official-docs/vite-build-tool-official]] `VITE-C2` |
| `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-UI-001` | 1 | `ui` | UI composition은 React를 사용한다 | `accepted-documented-only` | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] | §3.2 `FE-D004`; [[raw/official-docs/react-ui-library-official]] `REACT-UI-C1` |
| `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-STYLING-001` | 1 | `styling` | styling default는 Tailwind theme token과 component primitive다 | `conditional-default` | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] | §3.2 `FE-D005`; [[raw/official-docs/tailwind-css-utility-first-official]] |
| `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-SERVER-STATE-001` | 1 | `server-state` | application-owned QueryCachePort와 TanStack Query adapter를 사용하고 client store에 server state를 복제하지 않는다 | `accepted-documented-only` | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] | §3.2 `FE-D006`; [[raw/official-docs/tanstack-query-server-state-official]] |
| `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-VALIDATION-001` | 1 | `validation` | boundary runtime validation은 Zod schema로 수행한다 | `accepted-documented-only` | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] | §3.2 `FE-D007`; [[raw/official-docs/zod-runtime-schema-validation-official]] |
| `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-ROUTING-001` | 1 | `routing` | routing default는 React Router Declarative Mode다 | `conditional-default` | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] | §3.2 `FE-D008`; [[raw/official-docs/react-router-official]] |
| `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-ARCHITECTURE-001` | 1 | `architecture` | domain, application, presentation, adapters, bootstrap 책임을 분리한다 | `accepted-documented-only` | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] | §3.2 `FE-D009` |
| `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-PORT-OWNERSHIP-001` | 1 | `port-ownership` | output port interface는 application이 소유하고 adapter가 구현한다 | `accepted-documented-only` | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] | §3.2 `FE-D010` |
| `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-COMPOSITION-ROOT-001` | 1 | `composition-root` | bootstrap을 단일 composition root로 두고 concrete adapter를 application에 주입한다 | `accepted-documented-only` | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] | §3.2 `FE-D011` |
| `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-RUNTIME-CONFIG-001` | 1 | `runtime-config` | deploy별 public value는 runtime config, compiler value는 build-time config로 분리한다 | `conditional-default` | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] | §3.2 `FE-D012` |
| `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-CONFIG-FALLBACK-001` | 1 | `config-fallback` | runtime config fallback 시 environment별 rebuild는 허용하되 artifact의 multi-environment 재사용은 금지한다 | `conditional-default` | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] | §3.2 `FE-D013` |
| `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-TIMEOUT-001` | 1 | `timeout` | request total timeout default는 10초이며 별도 browser connect timeout은 주장하지 않는다 | `conditional-default` | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] | §3.2 `FE-D014` |
| `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-RETRY-001` | 1 | `retry` | retry는 initial call 이후 최대 2회, exponential backoff와 full jitter, 2초 cap을 사용한다 | `conditional-default` | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] | §3.2 `FE-D015` |
| `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-IDEMPOTENCY-001` | 1 | `idempotency` | mutation auto-retry는 stable idempotency key와 backend replay contract가 있을 때만 허용한다 | `accepted-documented-only` | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] | §3.2 `FE-D016` |
| `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-AUTH-BOUNDARY-001` | 1 | `auth-boundary` | auth lifecycle은 외부 owner가 소유하고 skeleton은 AuthSessionPort만 소비한다 | `accepted-documented-only` | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] | §3.2 `FE-D017`; [[raw/project-notes/keycloak-patterns-overview]] |
| `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-REGISTRY-001` | 1 | `registry` | route, API operation, env, storage, error, query, telemetry, release token을 8개 registry로 관리한다 | `accepted-documented-only` | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] | §3.2 `FE-D018` |
| `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-OFFLINE-CACHE-001` | 1 | `offline-cache` | service worker와 offline asset cache는 default off다 | `conditional-default` | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] | §3.2 `FE-D019` |
| `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-CACHE-POLICY-001` | 1 | `cache-policy` | hashed asset은 immutable, HTML·runtime config·release manifest는 revalidate/no-store로 분리한다 | `accepted-documented-only` | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] | §3.2 `FE-D020` |
| `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-TELEMETRY-001` | 1 | `telemetry` | telemetry는 best-effort queue와 redaction을 사용하며 sink failure가 UI를 실패시키지 않는다 | `accepted-documented-only` | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] | §3.2 `FE-D021` |
| `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-TEST-STACK-001` | 1 | `test-stack` | test stack default는 Vitest, RTL, MSW, Playwright, axe다 | `conditional-default` | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] | §3.2 `FE-D022` |
| `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-DEPLOYMENT-001` | 1 | `deployment` | static release는 immutable release directory와 atomic active pointer로 배포한다 | `conditional-default` | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] | §3.2 `FE-D023` |
| `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-SUPPLY-CHAIN-001` | 1 | `supply-chain` | dependency lock, secret scan, vulnerability scan, license inventory, dependency review를 merge/release gate로 분리한다 | `accepted-documented-only` | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] | §3.2 `FE-D024` |
| `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-SAMPLE-FIXTURE-001` | 1 | `sample-fixture` | sample slice는 제거 가능한 contract fixture이며 product import를 금지한다 | `accepted-documented-only` | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] | §3.2 `FE-D025` |
> **개정 기록 (§3.3 protocol)**
>
> - 2026-07-21 · `DEC-...-SUPPLY-CHAIN-001` · `compatibility_impact: additive` · revision 유지(1). Decision Summary 에 `dependency review` 를 추가했다. 이는 새 결정이 아니라 **불완전한 요약의 정정**이다 — `FE-OC-018` 과 §13.1 이 처음부터 dependency review 를 요구했고 §3.2 `FE-D024` 도 이를 포함하는데 이 registry 행만 4개 control 로 적혀 있었다. 기존 4개 control 의 동작은 바뀌지 않고, gate 정의(§15.1 `FE-GATE-013`)도 이미 dependency-review fixture 를 포함한 채 revision 1 이므로 같은 판정을 적용한다.
> - Summary 셀은 소비 branch 의 상속 표와 **문자열이 정확히 일치해야 한다**(`wiki_consistency_check.py` 의 `CONFLICTS_WITH_PROJECT_DECISION`). 분류·근거 같은 메타는 이 기록에 적고 Summary 에 섞지 않는다.
---
<!-- section-id: architecture-components -->
## 4. System Architecture Contract
### 4.1 Architecture diagrams
![[raw/diagrams/ca-skeleton-frontend/architecture-overview-2026-07-18.drawio]]
![[raw/diagrams/ca-skeleton-frontend/architecture-deployment-2026-07-18.drawio]]
두 파일은 `wiki-diagram-reviewer``rules/diagram-standards.md` v2 심사에서 각각 100/100 PASS를 받았다. PASS scope는 overview의 Clean Architecture dependency ownership view와 deployment의 static asset·`/config.json` delivery slice다. §12 전체 release/rollback topology, 실제 구현 topology, hosting 상태는 이 review가 증명하지 않는다. 근거: `docs/superpowers/specs/2026-07-18-ca-skeleton-frontend-operational-contract-review/diagram-review.md`.
### 4.2 Component responsibility
| Component | Owns | Consumes | MUST NOT own | Evidence status |
| --- | --- | --- | --- | --- |
| `domain` | framework-neutral model, value semantics, pure policy | standard JavaScript only | React, router, Query, fetch, storage, telemetry | `planned` |
| `application` | use case, input/output port, `QueryCachePort` policy, orchestration, view-model contract | domain | concrete adapter, browser global, React component | `planned` |
| `presentation` | page/component, user event, view state rendering | application public API | raw API DTO, fetch, storage key, telemetry transport | `planned` |
| `adapters/http` | application output port implementation, envelope/schema/error mapping | application port, browser fetch | use-case policy, component rendering | `planned` |
| `adapters/storage` | storage port implementation, serialization, quota mapping | application port, Web Storage | token lifecycle, domain policy | `planned` |
| `adapters/telemetry` | telemetry port implementation, queue, redaction, sink | application port, browser transport | UX decision, navigation | `planned` |
| `adapters/query-cache` | application-owned `QueryCachePort` implementation, TanStack Query key/invalidation bridge | application port, TanStack Query | use-case policy, page-local query key | `planned` |
| `bootstrap` | config load, adapter construction, dependency injection, React mount | all runtime modules | business rule, page-specific orchestration | `planned` |
### 4.3 Dependency matrix
화살표는 source import 방향이다.
| From | May import | MUST NOT import | Planned enforcement |
| --- | --- | --- | --- |
| `domain` | domain sibling modules | application, presentation, adapters, bootstrap, React, browser globals | dependency-cruiser + ESLint restricted imports |
| `application` | domain, application-owned ports/contracts | presentation, concrete adapters, bootstrap, React, `window`, `localStorage`, `fetch` | architecture fixture |
| `presentation` | application facade, view-model types, shared UI primitive | adapters, raw DTO schema, registry storage implementation | restricted import rule |
| `adapters/*` | application-owned output ports, domain value contract if required | presentation, bootstrap internals, other adapter concrete implementation | dependency graph snapshot |
| `bootstrap` | presentation root, application factory, all selected adapters | page-specific business rule | composition-root review |
| `test fixtures` | public contracts, explicit test helpers, 그리고 테스트 대상 계층 + 선택된 test stack 패키지 (예시적) | production secret, real telemetry endpoint | test config guard |
`test fixtures` 행의 **May import 열은 예시(illustrative)이고 MUST NOT 열이 규범(normative)** 이다. 즉 `tests/**` 는 "production secret 모듈과 real telemetry endpoint 설정을 import 하지 않는다"는 forbidden-only 규칙으로 강제한다. allow-only 로 읽으면 `FE-D022` 가 의무화한 test stack(Vitest·RTL·MSW·Playwright·axe) 과 테스트 대상 계층 import 가 전부 금지되어 정상 테스트가 실패한다.
Normative dependency summary:
- `application -> adapters` concrete import는 `MUST NOT`이다.
- output port definition은 `application``MUST` 소유한다.
- adapter는 application port를 구현하지만 application은 adapter 이름을 알면 안 된다.
- presentation은 application facade를 호출하며 raw backend envelope를 직접 다루면 안 된다.
- bootstrap만 concrete adapter를 조립할 수 있다.
### 4.4 Port ownership matrix
| Port | Definition owner | Planned implementation | Consumer | Input / output | Failure vocabulary |
| --- | --- | --- | --- | --- | --- |
| `ResourceQueryPort` | `application` | `adapters/http` | query use case | query object → validated model | `ApiFailure` |
| `ResourceCommandPort` | `application` | `adapters/http` | command use case | command + idempotency context → model | `ApiFailure` |
| `QueryCachePort` | `application` | `adapters/query-cache` (`TanStack Query`) | application query/mutation orchestration | registry query key + cache command → cache state/invalidation result | `QUERY_CACHE_FAILURE` |
| `AuthSessionPort` | `application` integration boundary | external auth adapter | routing + API client interceptor | opaque session state / request header callback | `AuthRequired`, `AuthIntegrationFailure` |
| `StoragePort` | `application` | `adapters/storage` | preference/session-neutral use case | classified key + serializable value | `StorageUnavailable`, `StorageQuotaExceeded` |
| `TelemetryPort` | `application` | `adapters/telemetry` | application + boundary | sanitized event → best-effort ack | `TelemetryDropped` internal only |
| `ClockPort` | `application` | browser/system clock adapter | retry/release logic | now / monotonic duration | no user-facing error |
| `ReleaseInfoPort` | `application` | runtime config/release adapter | boot + chunk recovery | release manifest → compatible release info | `RELEASE_MANIFEST_FAILURE`, `DEPLOY_MISMATCH` |
`AuthSessionPort`는 token 문자열을 domain/application model로 반환하지 않는 형태를 우선한다. header supplier나 opaque credential attachment callback을 사용하고, 구현 세부는 auth owner가 정한다.
### 4.5 Composition root
Planned location:
```text
src/bootstrap/main.jsx
src/bootstrap/composition-root.js
```
Boot order는 다음을 `MUST` 따른다.
1. build identity 읽기
2. runtime config fetch
3. config envelope·schema·compatibility 검증
4. release manifest 정합성 확인
5. registry snapshot load
6. auth integration adapter 주입
7. HTTP/storage/telemetry/query-cache adapter 생성
8. application facade 생성
9. router 생성
10. React root mount
2~4단계가 실패하면 product route를 mount하지 않고 boot error shell만 렌더한다. telemetry adapter 생성 실패는 console-safe fallback으로 계속 진행할 수 있다.
### 4.6 Planned directory blueprint
```text
src/
bootstrap/
main.jsx
composition-root.js
load-runtime-config.js
domain/
models/
policies/
application/
ports/
use-cases/
view-models/
presentation/
app/
routes/
pages/
components/
boundaries/
adapters/
http/
storage/
telemetry/
query-cache/
auth/
release/
contracts/
routes.js
api-operations.js
env.js
storage-keys.js
errors.js
query-keys.js
telemetry.js
release-tokens.js
sample/
contract-fixture/
tests/
unit/
component/
integration/
e2e/
artifacts/
quality/
tests/
performance/
security/
release/
runbooks/
```
경로는 `planned`이며 repository가 생성될 때 변경될 수 있다. responsibility mapping이 유지되지 않으면 `FE-D009` 변경 절차를 거쳐야 한다.
---
## 5. Contract Registries
### 5.1 Registry owner map
| Registry ID | Registry | Planned path | Single owner | Ad hoc use failure |
| --- | --- | --- | --- | --- |
| `FE-REG-ROUTE` | route ID/path/params/access | `src/contracts/routes.js` | `feature-routing-navigation-guard-contract` | component에 literal route path 추가 |
| `FE-REG-API` | API method/path/operation/auth/timeout/idempotency/schema | `src/contracts/api-operations.js` | `feature-api-client-response-envelope-contract` | raw request config 또는 unregistered operation 사용 |
| `FE-REG-ENV` | build/runtime public config | `src/contracts/env.js` | `feature-frontend-env-runtime-config-contract` | registry 없는 `import.meta.env` 또는 config key 사용 |
| `FE-REG-STORAGE` | storage key/version/classification | `src/contracts/storage-keys.js` | `feature-frontend-storage-registry-contract` | raw `localStorage` key literal 사용 |
| `FE-REG-ERROR` | frontend error kind/code/default UX | `src/contracts/errors.js` | `feature-frontend-error-classification-boundary-contract` | raw status/message로 UI 분기 |
| `FE-REG-QUERY` | query key factory/invalidation | `src/contracts/query-keys.js` | `feature-server-state-caching-contract` | page 안에서 ad hoc array key 생성 |
| `FE-REG-TELEMETRY` | event/attribute/redaction | `src/contracts/telemetry.js` | `feature-frontend-observability-logging-trace-contract` | 자유 문자열 event 전송 |
| `FE-REG-RELEASE` | build/config/API/release token | `src/contracts/release-tokens.js` | `feature-frontend-release-cache-rollback-contract` | string version 비교 또는 cache key 직접 작성 |
### 5.2 Route registry minimum schema
| Field | Required | Rule |
| --- | --- | --- |
| `routeId` | yes | stable `UPPER_SNAKE_CASE`; rename은 breaking |
| `path` | yes | centralized literal; component 내부 literal 금지 |
| `paramsSchema` | conditional | dynamic param이 있으면 runtime validation |
| `searchSchema` | conditional | query string을 application input으로 넘기기 전 validation |
| `access` | yes | `public`, `session-required`, `integration-defined` |
| `loadingSurface` | yes | route-level fallback owner |
| `errorSurface` | yes | route-level error owner |
| `chunkId` | generated | release manifest와 매핑 |
Initial planned rows:
| routeId | path | access | Notes |
| --- | --- | --- | --- |
| `APP_HOME` | `/` | `public` | sample shell |
| `SAMPLE_RESOURCE_LIST` | `/sample/resources` | `integration-defined` | contract fixture |
| `NOT_FOUND` | `*` | `public` | no API retry |
### 5.3 API operation registry minimum schema
모든 shared-client request는 아래 필드가 채워진 `FE-REG-API` row를 먼저 가져야 한다. raw path·timeout·auth·schema를 call site에서 다시 정의하면 registry violation이다.
| Field | Required | Rule |
| --- | --- | --- |
| `method` | yes | uppercase HTTP method |
| `path` | yes | path template; query value와 host를 포함하지 않음 |
| `operationId` | yes | stable `UPPER_SNAKE_CASE`; telemetry·test·owner key |
| `auth` | yes | `none` 또는 `external-session` |
| `timeoutMs` | yes | default `10000`; override는 decision change 필요 |
| `idempotency` | yes | `safe`, `keyed`, `none` 중 하나 |
| `requestSchema` | yes | body가 없으면 explicit `none`; params/search도 검증 |
| `responseSchema` | yes | success envelope의 payload schema reference |
| `owner` | yes | owning feature or branch slug |
Initial planned rows:
| operationId | method | path | auth | timeoutMs | idempotency | requestSchema | responseSchema | owner |
| --- | --- | --- | --- | --- | --- | --- | --- | --- |
| `LIST_SAMPLE_RESOURCES` | `GET` | `/api/sample/resources` | `external-session` | `10000` | `safe` | `SampleResourceListQuery` | `SampleResourceListPayload` | `feature-sample-feature-slice-contract-fixture` |
| `CREATE_SAMPLE_RESOURCE` | `POST` | `/api/sample/resources` | `external-session` | `10000` | `keyed` | `CreateSampleResourceCommand` | `SampleResourcePayload` | `feature-sample-feature-slice-contract-fixture` |
### 5.4 Environment registry minimum schema
| Key | Phase | Classification | Required | Default | Failure |
| --- | --- | --- | --- | --- | --- |
| `VITE_BUILD_ID` | build | public metadata | yes | none | build fail |
| `VITE_COMMIT_SHA` | build | public metadata | yes in CI | local sentinel allowed | release evidence fail |
| `VITE_ROUTER_BASE_PATH` | build | non-secret compile-time constant | yes | `/` | route mount fail |
| `VITE_RUNTIME_CONFIG_URL` | build | non-secret compile-time constant | yes | `/config.json` | boot fail |
| `APP_ENV` | runtime | public | yes | none | boot fail |
| `API_BASE_URL` | runtime | public-sensitive | yes | none | boot fail |
| `REQUEST_TIMEOUT_MS` | runtime | public | no | `10000` | invalid value boot fail |
| `MAX_RETRY_ATTEMPTS` | runtime | public | no | `2` after initial | invalid value boot fail |
| `TELEMETRY_ENABLED` | runtime | public | yes | `false` | invalid value boot fail |
| `TELEMETRY_ENDPOINT` | runtime | public-sensitive | conditional | none | telemetry degrade |
| `AUTH_MODE` | runtime | public | yes | `external` | unsupported mode boot fail |
| `CONFIG_SCHEMA_VERSION` | runtime | public | yes | none | compatibility fail |
| `API_CONTRACT_VERSION` | runtime | public | yes | none | compatibility fail |
| `RELEASE_MANIFEST_URL` | runtime | public | yes | `/release-manifest.json` | mismatch detection degrade/fail per policy |
`public-sensitive`는 browser에서 볼 수 있지만 로그·telemetry에 원문을 남기지 않는 endpoint-like value를 뜻한다. secret 분류가 아니다.
### 5.5 Storage registry minimum schema
| Field | Required | Rule |
| --- | --- | --- |
| `logicalName` | yes | 의미 이름, raw key가 아님 |
| `physicalKey` | yes | `<app>:<scope>:v<schema>:<name>` |
| `backend` | yes | `memory`, `sessionStorage`, `localStorage`, `indexedDB` |
| `classification` | yes | `public-preference`, `opaque-cache`, `sensitive-forbidden` |
| `schemaVersion` | yes | incompatible change 시 increment |
| `ttl` | conditional | persistent cache는 expiry 필수 |
| `migration` | conditional | previous version을 읽으면 migration 또는 discard |
| `quotaFallback` | yes | memory/no-persist/feature-disable 중 하나 |
Initial planned rows:
| logicalName | backend | classification | TTL / fallback |
| --- | --- | --- | --- |
| `COLOR_SCHEME` | `localStorage` | `public-preference` | no TTL / system default |
| `CHUNK_RELOAD_GUARD` | `sessionStorage` | `opaque-cache` | session / no second auto reload |
| `QUERY_PERSISTENCE` | disabled | `sensitive-forbidden` default | opt-in contract required |
| `AUTH_TOKEN` | forbidden | `sensitive-forbidden` | external auth owner only |
### 5.6 Error registry minimum schema
| Field | Required | Rule |
| --- | --- | --- |
| `kind` | yes | frontend stable enum |
| `defaultRetryable` | yes | request context가 override 가능 |
| `severity` | yes | telemetry routing hint, user copy와 분리 |
| `userMessageKey` | yes | raw backend message 사용 금지 |
| `action` | yes | `retry`, `reauth`, `navigate`, `reload-once`, `contact-support`, `none` |
| `telemetryEvent` | yes | registry event에 매핑 |
| `redaction` | yes | cause/body/header drop rule |
Planned `kind` enum:
```text
NETWORK_UNREACHABLE
REQUEST_TIMEOUT
REQUEST_ABORTED
CONTENT_TYPE_MISMATCH
MALFORMED_JSON
ENVELOPE_MISMATCH
SCHEMA_MISMATCH
AUTH_REQUIRED
AUTH_INTEGRATION_FAILURE
FORBIDDEN
NOT_FOUND
CONFLICT
VALIDATION_REJECTED
UNKNOWN_CLIENT_FAILURE
RATE_LIMITED
SERVER_FAILURE
CHUNK_LOAD_FAILURE
BOOT_CONFIG_FAILURE
RELEASE_MANIFEST_FAILURE
DEPLOY_MISMATCH
STORAGE_UNAVAILABLE
STORAGE_QUOTA_EXCEEDED
RENDER_FAILURE
TELEMETRY_FAILURE
QUERY_CACHE_FAILURE
UNKNOWN_FAILURE
```
### 5.7 Query key registry minimum schema
Query key는 factory로만 생성한다.
```text
queryKeys.resource.all()
queryKeys.resource.list(filters)
queryKeys.resource.detail(resourceId)
```
| Rule | Normative behavior |
| --- | --- |
| namespace | feature prefix를 첫 element로 사용 |
| serialization | object key ordering을 canonicalize |
| identity | PII, token, raw URL을 key에 넣지 않음 |
| invalidation | mutation outcome과 mapping된 factory만 invalidate |
| version | API/schema breaking change 시 namespace version bump |
| persistence | default disabled; opt-in 시 release/config version partition |
### 5.8 Telemetry registry minimum schema
| Field | Required | Rule |
| --- | --- | --- |
| `eventName` | yes | stable dotted name |
| `trigger` | yes | 발생 시점 단일 정의 |
| `requiredAttributes` | yes | low-cardinality only |
| `optionalAttributes` | yes | absence-safe |
| `forbiddenAttributes` | yes | token, email, raw URL/query/body, storage value |
| `sampling` | yes | error/security event는 별도 정책 |
| `delivery` | yes | best-effort, audit channel 아님 |
Initial planned events:
| Event | Trigger | Required attributes |
| --- | --- | --- |
| `app.boot.failed` | config/release validation 실패 | `error_kind`, `build_id`, `config_schema_version` |
| `api.request.failed` | terminal normalized API failure | `error_kind`, `http_status_group`, `attempt_count_bucket`, `route_id` |
| `ui.render.failed` | React boundary catch | `route_id`, `build_id`, `component_boundary` |
| `release.mismatch.detected` | chunk/config/API version mismatch | `build_id`, `active_release_id`, `mismatch_kind` |
| `telemetry.delivery.dropped` | sink/queue failure | `reason`, `queue_size_bucket` |
### 5.9 Release token registry minimum schema
| Token | Source | Compatibility role |
| --- | --- | --- |
| `appVersion` | manifest | human release label |
| `buildId` | CI build | asset/HTML coherence |
| `commitSha` | VCS | source traceability |
| `configSchemaVersion` | runtime config schema | boot compatibility |
| `apiContractVersion` | frontend/backend agreement | schema compatibility |
| `assetManifestHash` | build output | chunk integrity/mismatch |
| `releaseId` | deploy system | rollback target |
| `builtAt` | CI | diagnostics, not cache identity |
### 5.10 Registry change protocol
1. owner branch에 decision 또는 change row를 먼저 추가한다.
2. registry schema validation을 갱신한다.
3. compatibility impact를 기록한다.
4. breaking이면 version bump와 migration·discard·fallback 중 하나를 정한다.
5. producer와 consumer test를 함께 갱신한다.
6. snapshot artifact를 생성한다.
7. release note에 affected `FE-OC-*`와 rollback condition을 적는다.
8. orphan token scan이 0건이어야 merge할 수 있다.
---
## 6. Build-time, Runtime, Secret Configuration
### 6.1 Three-way distinction
| Class | Example | Visible to browser | Change mechanism | Cache policy | Rule |
| --- | --- | --- | --- | --- | --- |
| build-time public | `BUILD_ID`, `COMMIT_SHA`, `ROUTER_BASE_PATH` | yes | rebuild | bundled | compiler behavior·asset identity만 |
| runtime public | `API_BASE_URL`, feature-public flag, telemetry endpoint | yes | runtime config publish | `no-store` | boot before React mount |
| secret | client secret, private key, DB credential, refresh token policy material | should not be bundled | server/auth owner | N/A | frontend env·bundle·HTML에 넣지 않음 |
`VITE_*` prefix는 build ID·commit SHA 같은 build metadata와 base path·`/config.json` 위치 같은 non-secret compile-time constant에만 사용한다. API endpoint, telemetry endpoint, public feature flag처럼 배포 후 달라질 수 있는 값은 `/config.json`에서 읽는다. 이름에 `SECRET`, `PASSWORD`, `PRIVATE_KEY`, `TOKEN`이 포함된 key는 build와 runtime registry 모두에서 거부한다. auth owner가 browser storage를 사용해야 한다면 별도 threat model과 owner evidence가 필요하며 본 skeleton default가 아니다.
### 6.2 Conditional deployment defaults
- If hosting이 runtime config를 HTML보다 먼저 atomic publish할 수 있음 → `FE-D012` runtime config 사용.
- If hosting이 정적 파일만 제공하고 atomic config publish가 불가능함 → environment별 rebuild를 허용하되 artifact를 env 간 재사용하지 않음.
- If SSR/edge runtime이 도입됨 → 본 config contract를 그대로 적용하지 않고 별도 project fork decision 필요.
<!-- section-id: runtime-flow -->
### 6.3 Boot sequence
```mermaid
sequenceDiagram
autonumber
participant Browser
participant HTML as index.html
participant Boot as bootstrap
participant Config as /config.json
participant Release as release-manifest.json
participant Schema as Zod schemas
participant App as React App
Browser->>HTML: GET index.html
HTML-->>Browser: no-cache app shell
Browser->>Boot: load hashed entry chunk
Boot->>Config: GET runtime config (no-store)
Boot->>Release: GET release manifest (no-store)
Boot->>Schema: validate config + compatibility
alt valid and compatible
Schema-->>Boot: normalized public config
Boot->>App: compose dependencies and mount
else invalid config
Schema-->>Boot: BOOT_CONFIG_FAILURE
Boot-->>Browser: boot error shell, product routes not mounted
else version mismatch
Schema-->>Boot: DEPLOY_MISMATCH
Boot-->>Browser: controlled recovery UI, no reload loop
end
```
### 6.4 Runtime config validation
Validation MUST cover:
- required key presence
- URL protocol allowlist (`https` in production policy; local exception documented)
- integer range for timeout/retry
- boolean parsing without truthy string ambiguity
- config schema version compatibility
- API contract version compatibility
- release/build ID coherence when provider exposes both
- unknown key policy: additive keys allowed only if schema explicitly passthroughs; default strict for safety
Boot failure output MUST contain safe fields only:
```text
error.kind
error.code
buildId
configSchemaVersion
releaseId (if present)
supportReference
```
Endpoint, query, header, raw config object, stack은 user-facing screen에 표시하지 않는다.
---
## 7. API Client Operational Contract
### 7.1 Shared client boundary
모든 API request는 application output port를 구현한 shared HTTP adapter를 통과해야 한다.
Page/component MUST NOT:
- 직접 `fetch` 호출
- `AbortController` timeout 구현 복제
- backend status를 user copy로 직접 변환
- raw response body를 log
- page-local retry loop 생성
- auth token을 storage에서 읽음
### 7.2 Request context
각 logical request는 다음 context를 가진다.
| Field | Required | Rule |
| --- | --- | --- |
| `operationId` | yes | registry-backed stable name |
| `method` | yes | uppercase HTTP method |
| `routeId` | yes | raw URL 대신 low-cardinality route ID |
| `timeoutMs` | yes | default 10000, operation override는 owner decision 필요 |
| `idempotency` | yes | `safe`, `keyed`, `none` |
| `attempt` | yes | initial=0, retry=1..N |
| `abortReason` | optional | `navigation`, `user`, `timeout`, `superseded` |
| `authMode` | yes | `none`, `external-session` |
### 7.3 Response envelope
Expected success shape:
```text
success: true
data: <payload>
meta.requestId
meta.traceId
meta.correlationId (optional if backend contract omits)
```
Expected failure shape:
```text
success: false
error.code
error.category
error.message
error.retryable
error.details (optional, client-safe)
meta.requestId
meta.traceId
```
Processing order:
1. HTTP transport completion
2. content-type expectation check
3. JSON parse
4. envelope schema validation
5. success/failure branch validation
6. payload schema validation
7. DTO → application model mapper
8. application result 또는 normalized failure 반환
Stage 7은 adapter 경계에서 **validated model**까지만 만든다. view-model 투영은 §4.2/§4.4가 정한 대로 `application`이 소유하며 `application/view-models/`에 둔다(2-stage 매핑). 따라서 `QueryCachePort`가 담는 것은 view-model이 아니라 model이다.
`200`이더라도 JSON/envelope/payload가 invalid하면 success로 반환하지 않는다. `4xx/5xx` body가 invalid하면 status 기반 safe fallback error를 만들고 raw body는 버린다.
### 7.4 Timeout and abort
| Situation | Classification | Retry | Telemetry | UX |
| --- | --- | --- | --- | --- |
| 10s total timeout | `REQUEST_TIMEOUT` | safe/keyed만 policy 적용 | terminal일 때 1 event | retry action |
| navigation cancel | `REQUEST_ABORTED` | no | debug counter only, error event 없음 | stale surface 제거 |
| user cancel | `REQUEST_ABORTED` | no | optional interaction event | neutral canceled state |
| superseded query | `REQUEST_ABORTED` | no | none | latest request 유지 |
| external signal abort | reason에 따라 | no unless timeout owner | redacted reason | context-specific |
Browser `fetch`는 portable connect/read timeout을 분리 제공하지 않으므로 이 문서는 total timeout만 기본값으로 둔다. 별도 transport가 도입되기 전 connect timeout을 구현 사실처럼 말하지 않는다.
### 7.5 Retry algorithm
Initial default:
```text
maxRetries = 2
baseDelayMs = 250
maxDelayMs = 2000
algorithm = min(maxDelayMs, baseDelayMs * 2^retryIndex) * random(0, 1)
jitter = full jitter
```
Normative rules:
- initial request는 retry count에 포함하지 않는다.
- retry schedule은 `ClockPort`와 injectable random source로 test 가능해야 한다.
- `REQUEST_ABORTED`, `MALFORMED_JSON`, `ENVELOPE_MISMATCH`, `SCHEMA_MISMATCH`, `401`, `403`, `404`, `409`, `422`는 default non-retryable이다.
- network failure, timeout, `429`, `502`, `503`, `504`는 safe/keyed request에서만 retry candidate다.
- generic `500`은 automatic retry default off; operation owner가 safe condition을 증명해야 opt-in 가능하다.
- browser offline signal은 hint일 뿐 최종 truth로 사용하지 않는다.
- retry 중 component가 unmount되거나 query가 superseded되면 남은 timer와 request를 취소한다.
### 7.6 `Retry-After`
`429` 또는 backend가 명시한 retryable response에 `Retry-After`가 있으면 다음 순서를 따른다.
1. delta-seconds 또는 HTTP-date parse
2. invalid/negative면 local backoff 사용
3. valid delay가 30s를 넘으면 automatic retry하지 않고 terminal `RATE_LIMITED` UX로 전환
4. valid delay가 30s 이하면 local backoff와 비교해 더 긴 값을 사용
5. navigation/user abort 발생 시 wait 취소
`Retry-After` raw value를 telemetry에 남기지 않고 normalized delay bucket만 남긴다.
### 7.7 Idempotency
Mutation retry conditions:
- backend contract가 `Idempotency-Key`를 지원한다고 registry에 명시
- 한 logical user action에 하나의 key 사용
- retry마다 같은 key 재사용
- 새 user action은 새 key 사용
- key는 telemetry, URL, user message에 노출하지 않음
- concurrent double-submit은 같은 logical action이면 client-side single-flight 또는 UI disable로 합침
- backend가 replay 여부를 반환하면 result metadata로만 소비
Key 생성 책임은 auth token lifecycle과 분리한다. key persistence가 필요하면 storage registry에 TTL·classification·migration을 추가하기 전에는 memory-only다.
### 7.8 Auth integration boundary
Skeleton owns:
- route가 요구하는 session state 소비
- request 전 `AuthSessionPort.attach(request)` 호출
- `401``AUTH_REQUIRED`로 정규화
- `403``FORBIDDEN`으로 정규화
- auth owner callback으로 unauthenticated transition 알림
- navigation guard는 UX hint이며 backend authorization을 대체하지 않는다는 규칙
Skeleton does not own:
- authorization code exchange
- token 저장 위치
- access token refresh
- refresh token rotation
- logout propagation
- revocation
- identity provider redirect detail
- backend permission decision
`401` recovery는 session state transition만 소유하며 credential 획득·저장·회전은 계속 외부 auth owner가 소유한다. logical request당 recovery callback은 최대 1회다.
| Current session state | Event | Next state | Skeleton action |
| --- | --- | --- | --- |
| `authenticated` | first `401` | `recovery-pending` | external owner의 bounded recovery callback 1회 호출 |
| `recovery-pending` | owner reports session restored | `authenticated` | 아래 replay policy 적용 |
| `recovery-pending` | owner reports no session | `unauthenticated` | terminal `AUTH_REQUIRED` 반환 |
| `recovery-pending` | adapter throws/rejects/invalid result | `integration-failed` | terminal `AUTH_INTEGRATION_FAILURE` 반환 |
| any | second `401` for same logical request | `unauthenticated` | 추가 recovery 없이 terminal `AUTH_REQUIRED` 반환 |
Recovery 이후 replay policy:
- `safe` request는 같은 logical request context로 최대 1회 replay할 수 있다.
- `keyed` mutation은 같은 stable idempotency key와 active backend replay contract를 유지할 때만 최대 1회 replay할 수 있다.
- `none`인 unkeyed mutation은 recovery 성공 후에도 `MUST NOT` replay한다. UI는 명시적 재시도를 요구한다.
- replay와 일반 retry를 합친 총 시도 횟수는 operation registry와 test fixture가 추적하며 recovery loop를 만들 수 없다.
<!-- section-id: sequence -->
### 7.9 Request sequence
```mermaid
sequenceDiagram
autonumber
actor User
participant UI as Presentation
participant App as Application
participant Query as QueryCachePort / TanStack adapter
participant HTTP as HTTP Adapter
participant Auth as AuthSessionPort
participant API as Backend API
participant Schema as Runtime Schema
User->>UI: route enter or action
UI->>App: execute use case
App->>Query: query/mutation with registry key
Query->>HTTP: application output port
HTTP->>Auth: attach opaque session context
Auth-->>HTTP: request-ready callback result
HTTP->>API: request + timeout signal
alt success envelope
API-->>HTTP: JSON response
HTTP->>Schema: envelope + payload validate
Schema-->>HTTP: normalized model
HTTP-->>Query: result
Query-->>App: cache state
App-->>UI: view-model
UI-->>User: success or empty
else retry candidate
API-->>HTTP: network/429/502/503/504
HTTP->>HTTP: bounded backoff + jitter
HTTP-->>Query: result or terminal failure
Query-->>App: refreshing or terminal failure
App-->>UI: safe view state and action
else contract/auth failure
API-->>HTTP: invalid schema / 401 / 403
HTTP-->>Query: non-retryable normalized failure
Query-->>App: normalized failure
App-->>UI: safe action and message key
else query-cache adapter failure
Query-->>App: QUERY_CACHE_FAILURE
App-->>UI: uncached-safe fallback or terminal error
end
```
---
## 8. Frontend Failure Taxonomy
### 8.1 Normalized failure shape
```text
kind
code
httpStatus (optional)
retryable
operationId
attemptCount
requestId (optional)
traceId (optional)
userMessageKey
action
causeClass (internal allowlist only)
```
Raw response body, token, authorization header, full URL/query, stack, storage value는 normalized failure에 포함하지 않는다.
### 8.2 Failure matrix
| Trigger | Normalized kind | Auto retry | Fallback | User UX | Telemetry rule |
| --- | --- | --- | --- | --- | --- |
| DNS/offline/CORS-like opaque network failure | `NETWORK_UNREACHABLE` | safe/keyed, max 2 | cached safe data if available | offline/network message + manual retry | terminal 1회, raw URL 금지 |
| total timeout | `REQUEST_TIMEOUT` | safe/keyed, max 2 | stale data 유지 가능 | timeout message + retry | elapsed bucket, attempts |
| navigation abort | `REQUEST_ABORTED` | no | latest route state | error toast 금지 | error event 금지 |
| user abort | `REQUEST_ABORTED` | no | neutral canceled state | canceled label if needed | interaction-only |
| JSON operation의 response `Content-Type` 불일치 | `CONTENT_TYPE_MISMATCH` | no | prior safe cache 또는 error | incompatible response message | expected/actual media type category only |
| response not valid JSON | `MALFORMED_JSON` | no | prior safe cache 또는 error | contract failure message | content-type/status group only |
| top-level envelope missing/invalid | `ENVELOPE_MISMATCH` | no | prior safe cache 또는 error | service response incompatible | schema version, no body |
| payload schema invalid | `SCHEMA_MISMATCH` | no | prior safe cache 또는 error | update/support message | schema ID + safe issue path count |
| HTTP 401 | `AUTH_REQUIRED` | no in skeleton | external auth callback | sign-in/re-auth action | route ID, no principal/token |
| auth attach/recovery adapter throws, rejects, or returns invalid state | `AUTH_INTEGRATION_FAILURE` | no | unauthenticated-safe shell | sign-in/support action | phase + safe adapter outcome only |
| HTTP 403 | `FORBIDDEN` | no | keep shell | permission message, no retry | operation + status |
| HTTP 404 | `NOT_FOUND` | no | route/resource not-found | navigate back/home | low severity |
| HTTP 409 | `CONFLICT` | no | refetch authoritative data | conflict resolution action | operation + safe backend code |
| HTTP 422 | `VALIDATION_REJECTED` | no | preserve user input | field/form safe details | field names allowlist only |
| other 4xx | `UNKNOWN_CLIENT_FAILURE` | no | preserve safe shell/state | generic request correction/support action | operation + status group only |
| HTTP 429 | `RATE_LIMITED` | safe/keyed + bounded `Retry-After` | stale data if safe | countdown/manual retry | delay bucket, attempts |
| HTTP 500 | `SERVER_FAILURE` | default no | stale safe data | service failure | error code/status group |
| HTTP 502 | `SERVER_FAILURE` | safe/keyed, max 2 | stale safe data | temporary service failure | attempts + terminal |
| HTTP 503 | `SERVER_FAILURE` | safe/keyed, max 2 | stale safe data | temporary service failure | `Retry-After` bucket |
| HTTP 504 | `SERVER_FAILURE` | safe/keyed, max 2 | stale safe data | gateway timeout | attempts + duration bucket |
| other 5xx | `SERVER_FAILURE` | default no | safe fallback | service failure | status group only |
| chunk fetch fails | `CHUNK_LOAD_FAILURE` | one controlled reload only after release check | current shell | update/reload action | build/release IDs |
| runtime config missing/invalid | `BOOT_CONFIG_FAILURE` | one refetch allowed | boot error shell | support reference | safe config schema fields |
| release manifest fetch, parse, or schema validation failure | `RELEASE_MANIFEST_FAILURE` | one bounded refetch at boot only | boot/update shell | update/support action | phase + build ID, no raw manifest |
| HTML/asset/config release mismatch | `DEPLOY_MISMATCH` | no request retry | controlled reload once or rollback | update message | mismatch kind + IDs |
| storage API unavailable/security error | `STORAGE_UNAVAILABLE` | no | memory-only | usually silent, feature note if needed | backend type + reason enum |
| storage quota exceeded | `STORAGE_QUOTA_EXCEEDED` | no | evict allowed cache then memory-only | non-blocking notice if feature affected | quota bucket, no values |
| React render throws | `RENDER_FAILURE` | no auto retry | nearest boundary shell | retry route / reload action | component boundary + build ID |
| telemetry endpoint/network fails | `TELEMETRY_FAILURE` | bounded internal queue only | console-safe/drop | no product error | self-metric, no recursion |
| `QueryCachePort` read/write/invalidate throws or returns an invalid result | `QUERY_CACHE_FAILURE` | no automatic request retry | operation-declared uncached mode만 허용, 아니면 terminal | retry/support action; stale 표시를 위조하지 않음 | phase + query namespace, raw key/data 금지 |
| unknown thrown value | `UNKNOWN_FAILURE` | no | nearest safe boundary | generic reference | type allowlist only |
Normalization은 total function이어야 한다. response/adapter/browser exception이 위 named branch와 일치하지 않거나 mapper 자체가 실패하면 최종 catch-all이 raw value를 폐기하고 `UNKNOWN_FAILURE`를 반환한다. normalized failure를 만들지 못한 채 throw를 presentation으로 통과시키는 경로는 허용하지 않는다.
### 8.3 Retry decision order
```text
if aborted by navigation/user/superseded -> do not retry
else if parse/envelope/schema/auth/authz/not-found/conflict/validation -> do not retry
else if method is safe -> apply status/network policy
else if idempotency mode is keyed and backend contract is active -> apply status/network policy
else -> do not retry
```
Backend `error.retryable=true`는 necessary hint일 수 있지만 frontend가 unsafe mutation을 자동 retry할 충분 조건은 아니다. method/idempotency/client cap을 함께 만족해야 한다.
### 8.4 UX action vocabulary
| Action | When allowed | MUST NOT do |
| --- | --- | --- |
| `retry` | terminal retryable failure | infinite spinner 또는 hidden loop |
| `reauth` | `AUTH_REQUIRED` + external owner available | token lifecycle 직접 구현 |
| `navigate` | not-found/forbidden route recovery | history loop |
| `reload-once` | confirmed chunk/deploy mismatch | session guard 없이 반복 reload |
| `contact-support` | schema/internal repeated failure | raw stack/body 노출 |
| `none` | abort, telemetry-only degradation | user에게 false error 표시 |
### 8.5 Required negative fixtures
| Fixture | Expected normalized result |
| --- | --- |
| JSON operation + `text/html` response | `CONTENT_TYPE_MISMATCH` |
| auth attach callback throw/reject | `AUTH_INTEGRATION_FAILURE` |
| bounded recovery invalid state | `AUTH_INTEGRATION_FAILURE` |
| release manifest network/parse/schema failure | `RELEASE_MANIFEST_FAILURE` |
| QueryCachePort adapter throw 또는 invalid cache result | `QUERY_CACHE_FAILURE` |
| unregistered `418` or other unmapped 4xx | `UNKNOWN_CLIENT_FAILURE` |
| thrown non-`Error` object, symbol, or mapper exception | `UNKNOWN_FAILURE` |
| recovery succeeds for unkeyed mutation | no replay; terminal action requires explicit user retry |
---
## 9. Async UI, Query Cache, Routing, Storage
### 9.1 Async surface state model
Required visible states:
| State | Data | Activity | UI requirement |
| --- | --- | --- | --- |
| `initial-loading` | none | first request | stable skeleton, focus theft 금지 |
| `success` | present | idle | view-model render |
| `empty` | valid empty | idle | empty reason + primary action if applicable |
| `terminal-error` | none or unusable | stopped | safe message + registry action |
Additional non-blocking states:
| State | Data | Activity | UI requirement |
| --- | --- | --- | --- |
| `refreshing` | stale/present | background | existing content 유지, subtle indicator |
| `stale-degraded` | cached | retry exhausted | stale label + manual retry |
| `mutation-pending` | current view | write in flight | duplicate action 차단 |
| `mutation-conflict` | authoritative refetch needed | stopped | conflict action |
`loading` boolean 하나로 empty/error/refreshing을 합치면 contract violation이다.
### 9.2 Query cache defaults
Application use case는 `QueryCachePort`만 호출한다. `bootstrap/composition-root.js``adapters/query-cache`의 TanStack Query implementation을 생성해 application facade에 주입하며, presentation과 application은 TanStack Query client를 직접 import하지 않는다.
Initial defaults, all `planned`:
| Area | Default | Exception trigger |
| --- | --- | --- |
| query key | registry factory | none |
| stale time | 30s for sample read | operation owner measurement |
| garbage collection | 5m | memory profile evidence |
| refetch on focus | enabled for stale query | high-cost operation owner opt-out |
| retry | API policy callback | no page-local number |
| mutation retry | off unless keyed | explicit backend idempotency contract |
| cache persistence | off | offline requirement + storage threat model |
| invalidation | mutation result → registry namespace | broad `invalidateQueries()` without reason 금지 |
Cache data가 release/config/API schema version과 incompatible하면 reuse하지 않고 discard한다. cache migration을 선택하면 compatibility branch가 fixture와 rollback을 소유한다.
### 9.3 Route behavior
- route param과 search param은 application 호출 전에 runtime validation한다.
- unknown route는 API request 없이 not-found surface로 간다.
- session-required route는 `AuthSessionPort` state를 UX hint로 사용한다.
- backend authorization result가 최종 권한 판단이다.
- route-level lazy chunk는 release manifest의 chunk ID와 연결한다.
- route error element와 React error boundary의 owner를 중복하지 않는다.
- redirect는 최대 hop count를 test해 loop를 차단한다.
### 9.4 Storage behavior
- Web Storage 접근은 `StoragePort` adapter 안에서 try/catch한다.
- unavailable, security exception, quota exceeded를 구분한다.
- allowed cache eviction 순서를 registry에 기록한다.
- user preference write 실패는 product flow를 중단하지 않고 memory fallback을 사용한다.
- mutation/idempotency record처럼 correctness에 영향을 주는 값은 storage fallback을 임의 적용하지 않는다.
- token, secret, raw API response, error body, PII는 default registry에 등록할 수 없다.
---
## 10. Rendering, Accessibility, and User Safety
### 10.1 Error boundary ownership
| Boundary | Catches | Does not catch | Recovery |
| --- | --- | --- | --- |
| boot shell | config/release/bootstrap failure | product route errors | config refetch, support, rollback signal |
| route boundary | lazy chunk/render failure for route | expected API result | route retry or controlled reload |
| feature boundary | component subtree render defect | normalized operational failure | component reset |
| async boundary | normalized query/mutation state | thrown render defect | registry action |
Operational failures는 normal state로 반환하고 render boundary에 throw하지 않는 것이 default다. programmer defect 또는 invariant breach만 render boundary가 잡는다.
### 10.2 Reload loop prevention
Controlled reload conditions:
1. failure kind가 `CHUNK_LOAD_FAILURE` 또는 `DEPLOY_MISMATCH`
2. release manifest fetch 성공
3. active release가 current build와 다름
4. `CHUNK_RELOAD_GUARD`가 current release pair에 대해 unset
5. guard를 먼저 기록한 후 reload
같은 release pair에서 두 번째 failure가 나면 auto reload를 중단하고 rollback/support surface를 보여준다.
### 10.3 Accessibility baseline
Planned requirements:
- keyboard로 모든 interactive action 접근
- visible focus indicator
- route change 후 deterministic focus target
- loading state의 적절한 live region, 반복 announcement 억제
- error message와 action의 programmatic association
- color만으로 state를 구분하지 않음
- modal focus trap과 restore
- axe critical/serious violation 0을 blocking default로 사용
- reduced-motion preference 존중
WCAG 적합성 자체는 실제 audit 없이 주장하지 않는다. automated axe 통과는 manual keyboard/screen-reader review를 대체하지 않는다.
---
## 11. Telemetry and Observability Contract
### 11.1 Required context
Allowed low-cardinality context:
```text
app_version
build_id
release_id
config_schema_version
api_contract_version
route_id
operation_id
error_kind
http_status_group
attempt_count_bucket
duration_bucket
component_boundary
active_release_id
mismatch_kind
reason
queue_size_bucket
```
이 목록은 **exhaustive default-deny allowlist**다. §5.8 initial planned events의 `requiredAttributes`는 전부 이 목록 안에 있어야 하며, 새 event 나 attribute 를 등록할 때 이 목록과 §5.8 을 함께 갱신한다. 목록 밖 attribute 는 transport boundary 에서 제거된다.
Forbidden:
```text
access_token
refresh_token
authorization_header
cookie
email
user_name
raw_user_id
raw_url
query_string
request_body
response_body
storage_value
stack_in_user_message
```
### 11.2 Delivery behavior
- telemetry send는 user request critical path를 block하지 않는다.
- queue는 bounded여야 하며 overflow 시 oldest-drop 또는 newest-drop 정책을 registry에 명시한다.
- telemetry failure를 telemetry로 재귀 전송하지 않는다.
- page hide 시 `sendBeacon` 사용 여부는 adapter decision이며 delivery guarantee로 표현하지 않는다.
- local/dev는 console-safe sink를 허용한다.
- production endpoint가 없거나 invalid하면 telemetry만 degrade하고 app은 계속 실행한다.
- security/audit delivery가 필요하면 best-effort product telemetry와 별도 contract를 만든다.
### 11.3 Trace correlation
- W3C `traceparent`가 외부 auth/backend contract에서 허용되면 전파한다.
- browser가 받은 `requestId`/`traceId`는 safe support reference로 내부 state에 보관할 수 있다.
- raw trace header를 user에게 노출하지 않는다.
- new request retry는 같은 logical operation correlation을 유지하되 attempt를 구분한다.
- trace propagation 미지원 backend에서는 local operation ID로 degrade한다.
---
## 12. Release, Cache, Version, and Rollback Contract
### 12.1 Artifact set
한 release는 최소 다음 artifact를 가진다.
```text
dist/index.html
dist/assets/<content-hash>.*
dist/config.json
dist/release-manifest.json
dist/config/runtime-config.schema.json
artifacts/release/build-manifest.json
artifacts/release/dependency-inventory.*
artifacts/release/checksums.txt
```
실제 path는 repository가 생기면 owner branch에서 확정한다. 현재는 expected artifact contract다.
### 12.2 Cache policy
| Surface | Default cache policy | Reason |
| --- | --- | --- |
| hashed JS/CSS/font/image | long-lived immutable | content hash identity |
| `index.html` | `no-cache` / revalidate | active entry point 교체 |
| `/config.json` | `no-store` 또는 URL에 explicit version | deploy-specific public config |
| `release-manifest.json` | `no-store` or immediate revalidate | mismatch detection |
| source map | public hosting disabled; secured artifact store | stack/source exposure boundary |
| service worker | default off | stale release complexity |
Header syntax은 hosting provider 확정 후 adapter runbook에 기록한다. 현재 문서는 policy만 소유한다.
### 12.3 Compatibility tuple
Frontend boot compatibility는 다음 tuple로 판정한다.
```text
(buildId, configSchemaVersion, apiContractVersion, assetManifestHash, releaseId)
```
Rules:
- config schema major incompatibility → boot fail
- API contract incompatible → product route mount fail 또는 explicitly supported compatibility adapter
- asset manifest mismatch → controlled reload once
- release ID mismatch but all versions compatible → warning telemetry 후 continue 가능
- string lexical compare로 version compatibility를 판정하지 않음
### 12.4 Atomic deploy expectation
Preferred order:
1. immutable asset upload
2. release manifest upload
3. runtime config upload
4. asset reachability smoke
5. active HTML pointer switch
6. post-switch boot/e2e smoke
Provider가 이 order를 지원하지 않으면 equivalent atomic primitive와 rollback semantics를 decision row에 기록한다.
### 12.5 Rollback invariant
Rollback target MUST include a coherent set of:
- prior HTML
- prior asset manifest and assets
- compatible runtime config
- compatible API contract or backend compatibility window
- release manifest
HTML만 과거로 돌리고 runtime config를 최신에 남기는 rollback은 금지한다. cache purge가 필요한 provider라면 purge 완료가 아니라 실제 old/new reachability probe 결과로 recovery를 판정한다.
---
<!-- section-id: implementation-boundaries -->
## 13. Supply-chain and Security Boundaries
### 13.1 Supply-chain minimums
| Control | Planned default | Blocking condition | Evidence artifact |
| --- | --- | --- | --- |
| package manager | pnpm + committed lockfile | lockfile drift | `artifacts/quality/lockfile-check.txt` |
| install | frozen lockfile | dependency resolution mutation | install log |
| dependency review | direct/transitive diff | unreviewed high-risk change | dependency diff report |
| vulnerability scan | severity policy owner branch | threshold violation without approved expiry | SARIF/JSON report |
| secret scan | source + built asset scan | credential pattern hit | scan report |
| license inventory | dependency license list | denied/unknown license unresolved | inventory |
| SBOM/inventory | tool selected by owner | missing release inventory | CycloneDX/SPDX or equivalent |
| provenance | CI build metadata | buildId/commit mismatch | build manifest |
Scanner name과 severity threshold는 repository/organization policy가 없어 현재 `deferred`다. 특정 도구를 사용했다고 주장하지 않는다.
### 13.2 Browser security boundary
- browser bundle은 public artifact로 간주한다.
- secret을 obfuscation으로 보호할 수 있다고 가정하지 않는다.
- `dangerouslySetInnerHTML`은 default prohibited import/API rule 대상이다.
- unavoidable HTML rendering은 sanitizer owner, allowlist, malicious fixture, CSP interaction evidence가 필요하다.
- `eval`, dynamic code execution, untrusted script URL은 금지 default다.
- CSP, HSTS, frame policy, referrer policy는 hosting/backend header owner와 frontend compatibility test가 공동 책임이다.
- CORS는 backend/browser enforcement이며 frontend에서 wildcard로 해결할 수 있다고 말하지 않는다.
- route guard는 authorization control이 아니다.
- client validation은 backend validation을 대체하지 않는다.
- source map은 production public path에 기본 배포하지 않는다.
### 13.3 Dependency update policy
- security update bot 선택은 `deferred`다.
- update PR은 lockfile, unit/component/integration/e2e, build, bundle, security gate를 통과해야 한다.
- major update는 `FE-D*` impact check와 registry compatibility check를 요구한다.
- suppression은 reason, owner, expiry, affected package, compensating control을 가진다.
- expiry가 지난 suppression은 gate failure다.
---
## 14. Measurable Non-functional Requirements
### 14.1 Measurement contexts
수치는 context와 함께만 판정한다.
| Context ID | Device/runtime | Network/cache | Route/data | Purpose |
| --- | --- | --- | --- | --- |
| `FE-NFR-C01` | Playwright Chromium, CI runner spec recorded | cold browser cache, throttling profile recorded | app shell + sample list fixture | repeatable lab baseline |
| `FE-NFR-C02` | desktop Chromium/Firefox/WebKit matrix | normal CI network, mocked API | sample critical flow | functional compatibility |
| `FE-NFR-C03` | production browser field data | real network, 28-day window | top route IDs | future field SLO; current unavailable |
| `FE-NFR-C04` | build runner image + Node/pnpm versions recorded | N/A | production build | bundle reproducibility |
CI runner CPU와 throttling 값이 확정되지 않았으므로 command를 실행할 때 report metadata에 실제 값을 기록한다. context가 없는 숫자는 evidence로 인정하지 않는다.
### 14.2 Initial target matrix
| NFR ID | Metric | Context | Initial target | Current evidence |
| --- | --- | --- | --- | --- |
| `FE-NFR-001` | initial JS gzip | `FE-NFR-C04` | ≤ 200 KiB | none |
| `FE-NFR-002` | any lazy route chunk gzip | `FE-NFR-C04` | ≤ 120 KiB | none |
| `FE-NFR-003` | LCP lab | `FE-NFR-C01` | ≤ 2.5s | none |
| `FE-NFR-004` | CLS lab | `FE-NFR-C01` | ≤ 0.10 | none |
| `FE-NFR-005` | interaction latency lab | `FE-NFR-C01` | ≤ 200ms for named interaction | none |
| `FE-NFR-006` | boot config validation | deterministic mocked fetch | ≤ 500ms excluding network delay | none |
| `FE-NFR-007` | API request total timeout | shared client | 10s default | none |
| `FE-NFR-008` | automatic retry count | deterministic fake clock | ≤ 2 after initial | none |
| `FE-NFR-009` | axe critical/serious | sample routes | 0 violations | none |
| `FE-NFR-010` | telemetry blocking time | sink-failure fixture | product action not blocked | none |
| `FE-NFR-011` | auth redirect loop | route graph test | navigation attempt당 automatic auth redirect ≤ 1, 동일 source→target pair 반복 0 | none |
| `FE-NFR-012` | controlled reload | mismatch fixture | at most 1 per release pair/session | none |
| `FE-NFR-013` | LCP field p75 | `FE-NFR-C03` | ≤ 2.5s | none |
| `FE-NFR-014` | CLS field p75 | `FE-NFR-C03` | ≤ 0.10 | none |
| `FE-NFR-015` | INP field p75 | `FE-NFR-C03` | ≤ 200ms | none |
Field Web Vitals는 consent/privacy boundary, route-ID aggregation, 28-day window, production release ID를 함께 기록해야 한다. minimum eligible sample threshold는 telemetry baseline을 얻은 뒤 owner가 확정할 `deferred` decision이므로, 그 전에는 `FE-GATE-018`을 PASS로 올릴 수 없다. lab result를 production percentile로 표현하지 않는다.
### 14.3 Planned commands and expected assertions
아래 command는 repository가 생긴 뒤 package script로 제공할 contract다. **이 검토에서 실행되지 않았다.**
| Command | Status | Expected assertion | Planned artifact |
| --- | --- | --- | --- |
| `pnpm install --frozen-lockfile` | `PLANNED_NOT_EXECUTED` | manifest와 lockfile drift 없음 | `artifacts/quality/install.txt` |
| `pnpm lint` | `PLANNED_NOT_EXECUTED` | lint error 0, forbidden imports 0 | `artifacts/quality/lint.txt` |
| `pnpm check:types` | `PLANNED_NOT_EXECUTED` | `checkJs` diagnostic 0 | `artifacts/quality/check-types.txt` |
| `pnpm test:runtime-schema` | `PLANNED_NOT_EXECUTED` | invalid fixtures 전부 reject | `artifacts/tests/runtime-schema.xml` |
| `pnpm test:unit` | `PLANNED_NOT_EXECUTED` | unit suite exit 0 | `artifacts/tests/unit.xml` |
| `pnpm test:component` | `PLANNED_NOT_EXECUTED` | async/error/a11y component fixtures exit 0 | `artifacts/tests/component.xml` |
| `pnpm test:integration` | `PLANNED_NOT_EXECUTED` | MSW API/failure matrix exit 0 | `artifacts/tests/integration.xml` |
| `pnpm test:e2e` | `PLANNED_NOT_EXECUTED` | critical flows browser matrix exit 0 | `artifacts/tests/e2e/` |
| `pnpm test:a11y` | `PLANNED_NOT_EXECUTED` | critical/serious axe finding 0 | `artifacts/tests/a11y.json` |
| `pnpm review:a11y-manual` | `PLANNED_NOT_EXECUTED` | sample route 별 keyboard/focus manual checklist 서명 완료 | `artifacts/tests/a11y-manual/<route>.md` |
| `pnpm build` | `PLANNED_NOT_EXECUTED` | production build exit 0 + manifest present | `artifacts/release/build-manifest.json` |
| `pnpm check:bundle` | `PLANNED_NOT_EXECUTED` | `FE-NFR-001`, `FE-NFR-002` threshold 만족 | `artifacts/performance/bundle.json` |
| `pnpm test:performance` | `PLANNED_NOT_EXECUTED` | `FE-NFR-003`, `FE-NFR-004`, `FE-NFR-005` context metadata + threshold result | `artifacts/performance/lab.json` |
| `pnpm scan:security` | `PLANNED_NOT_EXECUTED` | policy threshold 위반 없음 | `artifacts/security/scan.sarif` |
| `pnpm verify:release` | `PLANNED_NOT_EXECUTED` | compatibility tuple coherent | `artifacts/release/verification.json` |
| `pnpm collect:web-vitals-evidence` | `PLANNED_NOT_EXECUTED` | 28-day context + p75 + eligible sample metadata 기록 | `artifacts/performance/field-web-vitals.json` |
| `pnpm verify:hosting-headers` | `PLANNED_NOT_EXECUTED` | HTML/config/manifest/hashed-asset header policy 일치 + 선언된 security header 집합 일치 | `artifacts/release/hosting-headers.json` |
| `pnpm test:sample-removal` | `PLANNED_NOT_EXECUTED` | sample subtree 제거 후 production build/smoke 성공 | `artifacts/tests/sample-removal.xml` |
| `pnpm drill:runbook -- FE-RB-001` | `PLANNED_NOT_EXECUTED` | boot config containment/recovery assertions 통과 | `artifacts/runbooks/FE-RB-001/<release-id>/record.json` |
| `pnpm drill:runbook -- FE-RB-002` | `PLANNED_NOT_EXECUTED` | chunk mismatch와 `RELEASE_MANIFEST_FAILURE` containment/recovery assertions 통과 | `artifacts/runbooks/FE-RB-002/<release-id>/record.json` |
| `pnpm drill:runbook -- FE-RB-003` | `PLANNED_NOT_EXECUTED` | API degradation containment/recovery assertions 통과 | `artifacts/runbooks/FE-RB-003/<release-id>/record.json` |
| `pnpm drill:runbook -- FE-RB-004` | `PLANNED_NOT_EXECUTED` | telemetry degradation containment/recovery assertions 통과 | `artifacts/runbooks/FE-RB-004/<release-id>/record.json` |
| `pnpm drill:runbook -- FE-RB-005` | `PLANNED_NOT_EXECUTED` | rollback decision/recovery assertions 통과 | `artifacts/runbooks/FE-RB-005/<release-id>/record.json` |
Script 이름을 바꾸는 것은 허용되지만 acceptance gate와 artifact mapping을 동시에 갱신해야 한다.
---
## 15. Acceptance Gate Matrix
<!-- section-id: gate-matrix -->
### 15.1 Gate ownership
현재 stable gate registry는 26개 row이며, 새 gate를 추가하거나 supersede할 때 이 수와 promotion formula를 함께 갱신한다.
> 이 표가 gate 의 **정의**다. `Covered FE-OC` 열은 gate 와 계약의 대응 관계이지 계약 내용의 사본이 아니다.
> branch 는 이 표를 옮겨 적지 않는다 — gate ID 를 행 키로 쓰고 자기 control 만 적는다(예: [[raw/branch-notes/feature-frontend-build-bundle-supply-chain-contract]]).
| Gate ID | Gate | Blocking scope | Covered FE-OC | Covered FE-NFR | Required fixtures | Pass condition | Evidence artifact | Current |
| --- | --- | --- | --- | --- | --- | --- | --- | --- |
| `FE-GATE-001` | manifest/lockfile | merge + release | `FE-OC-003`, `FE-OC-018`, `FE-OC-020` | — | lockfile drift | frozen install exit 0 | install log | `FAIL_UNVERIFIED` |
| `FE-GATE-002` | lint | merge | `FE-OC-002`, `FE-OC-003`, `FE-OC-019`, `FE-OC-020` | — | forbidden API/import | error 0 | lint report | `FAIL_UNVERIFIED` |
| `FE-GATE-003` | typecheck-equivalent | merge | `FE-OC-002`, `FE-OC-003`, `FE-OC-007`, `FE-OC-020` | — | JSDoc/checkJs negative fixture | production diagnostic 0; fixture fails as expected | check-types report | `FAIL_UNVERIFIED` |
| `FE-GATE-004` | runtime schema | merge | `FE-OC-004`, `FE-OC-006`, `FE-OC-007`, `FE-OC-008`, `FE-OC-023` | `FE-NFR-006` | content-type/JSON/envelope/payload/config invalid matrix + deterministic valid-config timing fixture | every invalid fixture rejected with expected kind; valid boot config validation ≤ 500ms excluding mocked network delay | schema + timing report | `FAIL_UNVERIFIED` |
| `FE-GATE-005` | unit | merge | `FE-OC-006`, `FE-OC-009`, `FE-OC-012`, `FE-OC-013`, `FE-OC-014`, `FE-OC-022` | `FE-NFR-007`, `FE-NFR-008`, `FE-NFR-010` | retry clock, mapper, all registries | exit 0 | unit XML | `FAIL_UNVERIFIED` |
| `FE-GATE-006` | component | merge | `FE-OC-005`, `FE-OC-011`, `FE-OC-015`, `FE-OC-019`, `FE-OC-024` | `FE-NFR-009` | async states, render boundary, keyboard | exit 0 | component XML | `FAIL_UNVERIFIED` |
| `FE-GATE-007` | integration | merge | `FE-OC-006`, `FE-OC-007`, `FE-OC-008`, `FE-OC-009`, `FE-OC-010`, `FE-OC-012` | `FE-NFR-007`, `FE-NFR-008` | MSW status/failure/auth-recovery taxonomy | all matrix and negative-fixture rows covered | integration XML | `FAIL_UNVERIFIED` |
| `FE-GATE-008` | e2e | merge + release | `FE-OC-004`, `FE-OC-005`, `FE-OC-009`, `FE-OC-010`, `FE-OC-011`, `FE-OC-015`, `FE-OC-016`, `FE-OC-024` | `FE-NFR-011`, `FE-NFR-012` | boot, route, mutation, chunk mismatch, repeated guarded-route redirect pair | critical scenarios exit 0; navigation attempt당 automatic auth redirect ≤ 1이고 동일 source→target pair가 반복되지 않음 | Playwright report | `FAIL_UNVERIFIED` |
| `FE-GATE-009` | accessibility | merge + release | `FE-OC-019`, `FE-OC-020`, `FE-OC-024` | `FE-NFR-009` | axe + manual checklist | automated threshold + signed manual review | a11y artifacts | `FAIL_UNVERIFIED` |
| `FE-GATE-010` | architecture | merge | `FE-OC-002`, `FE-OC-020` | — | forbidden import fixtures including direct TanStack client import | allowed passes, forbidden fails | dependency report | `FAIL_UNVERIFIED` |
| `FE-GATE-011` | build | merge + release | `FE-OC-003`, `FE-OC-016`, `FE-OC-018` | — | clean production build | exit 0 + expected artifacts | build manifest | `FAIL_UNVERIFIED` |
| `FE-GATE-012` | bundle | release | `FE-OC-018`, `FE-OC-021` | `FE-NFR-001`, `FE-NFR-002` | app + lazy chunks | NFR thresholds pass | bundle report | `FAIL_UNVERIFIED` |
| `FE-GATE-013` | security | merge + release | `FE-OC-018`, `FE-OC-019`, `FE-OC-020` | — | secret/vulnerability/license/dependency-review fixtures | policy pass | SARIF/inventory/dependency diff report | `FAIL_UNVERIFIED` |
| `FE-GATE-014` | config compatibility | release | `FE-OC-004`, `FE-OC-023` | — | old/new config versions | supported passes, incompatible fails boot | compatibility report | `FAIL_UNVERIFIED` |
| `FE-GATE-015` | release coherence | release | `FE-OC-016`, `FE-OC-017`, `FE-OC-023` | `FE-NFR-012` | mixed HTML/assets/config | mismatch detected, coherent set passes | release verification | `FAIL_UNVERIFIED` |
| `FE-GATE-016` | rollback drill | production promotion | `FE-OC-017` | `FE-NFR-012` | prior release pair | rollback + smoke evidence | drill record | `FAIL_UNVERIFIED` |
| `FE-GATE-017` | scoped diagram review | documentation readiness | `FE-OC-002`, `FE-OC-016` | — | overview dependency view + static asset/runtime-config delivery slice | reviewer score threshold satisfied for both scoped diagrams | reviewer report | `PASS_SCOPED` |
| `FE-GATE-018` | production field Web Vitals | field readiness | `FE-OC-021` | `FE-NFR-013`, `FE-NFR-014`, `FE-NFR-015` | eligible route samples over recorded 28-day window | p75 targets pass and deferred minimum sample threshold is resolved | field Web Vitals report | `FAIL_UNVERIFIED` |
| `FE-GATE-019` | hosting header policy (cache + security) | release | `FE-OC-016`, `FE-OC-019` | — | HTML/config/manifest/hashed asset responses + 선언된 security header 집합 | declared Cache-Control/content-type/security-header policy matches actual hosting | hosting header report | `FAIL_UNVERIFIED` |
| `FE-GATE-020` | sample removal | merge + release | `FE-OC-024` | — | sample subtree removed in dedicated fixture | production build and smoke pass with no product import | sample-removal report | `FAIL_UNVERIFIED` |
| `FE-GATE-021` | `FE-RB-001` drill | production promotion | `FE-OC-025` | — | boot config failure | containment, escalation, recovery assertions pass | `FE-RB-001` record | `FAIL_UNVERIFIED` |
| `FE-GATE-022` | `FE-RB-002` drill | production promotion | `FE-OC-025` | `FE-NFR-012` | chunk/release mismatch + release manifest fetch/parse/schema failure | `CHUNK_LOAD_FAILURE``RELEASE_MANIFEST_FAILURE` containment, escalation, recovery assertions pass | `FE-RB-002` record | `FAIL_UNVERIFIED` |
| `FE-GATE-023` | `FE-RB-003` drill | production promotion | `FE-OC-025` | `FE-NFR-007`, `FE-NFR-008` | API degradation | containment, escalation, recovery assertions pass | `FE-RB-003` record | `FAIL_UNVERIFIED` |
| `FE-GATE-024` | `FE-RB-004` drill | production promotion | `FE-OC-025` | `FE-NFR-010` | telemetry degradation | containment, escalation, recovery assertions pass | `FE-RB-004` record | `FAIL_UNVERIFIED` |
| `FE-GATE-025` | `FE-RB-005` drill | production promotion | `FE-OC-025` | — | blocking release defect | rollback decision, escalation, recovery assertions pass | `FE-RB-005` record | `FAIL_UNVERIFIED` |
| `FE-GATE-026` | lab performance | release | `FE-OC-021` | `FE-NFR-003`, `FE-NFR-004`, `FE-NFR-005` | recorded runner/throttling/cache context + named interactions | every lab threshold passes and report contains reproducibility metadata | lab performance report | `FAIL_UNVERIFIED` |
### 15.2 Negative fixture requirement
Gate가 실제로 동작한다고 말하려면 최소 하나의 deliberately failing fixture가 필요하다.
| Gate | Negative fixture example |
| --- | --- |
| architecture | `presentation` imports `adapters/http` |
| checkJs | application port called with wrong shape |
| runtime schema | success envelope without `data` |
| retry | POST without idempotency key receives 503 |
| storage | token key registration attempt |
| telemetry | event includes raw URL/query |
| release | HTML build A + asset manifest B |
| reload guard | second chunk failure in same release pair |
| lab performance | context metadata missing 또는 one named threshold exceeded |
Negative fixture를 실행하지 않고 rule 존재만 확인한 결과는 `locally-verified` 증거로 부족하다.
### 15.3 Promotion rule
```text
MERGE_READY = FE-GATE-001, FE-GATE-002, FE-GATE-003, FE-GATE-004, FE-GATE-005, FE-GATE-006, FE-GATE-007, FE-GATE-008, FE-GATE-009, FE-GATE-010, FE-GATE-011, FE-GATE-013, FE-GATE-020 PASS
RELEASE_READY = MERGE_READY AND FE-GATE-012, FE-GATE-014, FE-GATE-015, FE-GATE-019, FE-GATE-026 PASS
PROD_PROMOTION_READY = RELEASE_READY AND FE-GATE-016, FE-GATE-021, FE-GATE-022, FE-GATE-023, FE-GATE-024, FE-GATE-025 PASS
FIELD_SLO_READY = PROD_PROMOTION_READY AND FE-GATE-018 PASS
DOCUMENTATION_READY = FE-GATE-017 PASS_SCOPED AND evidence ledger updated
PROJECT_READY = all applicable blocking gates PASS
```
현재는 `PROJECT_READY = false`, 즉 `NOT_READY`다.
---
## 16. Operational Runbooks
Runbook은 provider-specific console command를 현재 발명하지 않는다. 공통 trigger, diagnosis evidence, mitigation invariant, recovery assertion을 고정하고 provider command는 release branch가 hosting 확정 후 채운다.
아래 시간·rate window는 모두 implementation/telemetry evidence가 없는 `planned conditional default`이며 measured SLO가 아니다. 각 runbook의 first drill 결과와 hosting/backend baseline이 생기면 owner가 유지·변경한다.
### 16.1 `FE-RB-001` — Boot config failure
| Field | Planned contract |
| --- | --- |
| Primary owner | `feature-frontend-operational-runbook-contract` |
| Technical escalation | `feature-frontend-env-runtime-config-contract``feature-frontend-release-cache-rollback-contract` |
| Activation condition | initial boot config validation 실패 후 bounded refetch 1회도 실패 |
| Conditional window | detection 즉시 containment; owner triage 시작 목표 5분 |
| Evidence path | `artifacts/runbooks/FE-RB-001/<release-id>/` |
**Trigger**
- boot shell에 `BOOT_CONFIG_FAILURE`
- config fetch non-2xx, JSON parse failure, schema incompatibility
**Immediate containment**
1. product routes mount를 중단한다.
2. safe support reference와 build/config version만 표시한다.
3. automatic refetch는 최대 1회로 제한한다.
**Diagnosis evidence**
- current `buildId`, `releaseId`, `configSchemaVersion`
- runtime config HTTP status와 content-type
- release manifest compatibility tuple
- config publish timestamp는 진단용이며 compatibility identity로 쓰지 않음
**Mitigation options**
- If config artifact만 잘못됨 → current build와 호환되는 config republish.
- If new config schema가 old build와 incompatible → coherent prior release rollback.
- If endpoint outage → provider restore 또는 approved build-time fallback release.
**Escalation**
- config owner가 schema/publish 원인을 분류하지 못하거나 coherent republish가 불가능하면 release owner에게 rollback decision을 넘긴다.
- auth/API/product owner에게는 boot이 성공한 뒤 별도 downstream failure가 확인될 때만 확대한다.
**Recovery assertions**
- clean session boot 성공
- product root mount
- config validation artifact pass
- repeated boot error telemetry 없음
**Evidence**
`artifacts/runbooks/FE-RB-001/<release-id>/` planned.
### 16.2 `FE-RB-002` — Chunk load / release manifest / deploy mismatch
| Field | Planned contract |
| --- | --- |
| Primary owner | `feature-frontend-operational-runbook-contract` |
| Technical escalation | `feature-frontend-release-cache-rollback-contract` → hosting/CDN owner |
| Activation condition | `RELEASE_MANIFEST_FAILURE`, chunk failure 후 manifest mismatch·unreachable asset 확인, 또는 controlled reload 1회 실패 |
| Conditional window | detection 즉시 reload guard; release owner triage 시작 목표 5분 |
| Evidence path | `artifacts/runbooks/FE-RB-002/<release-id>/` |
**Trigger**
- `CHUNK_LOAD_FAILURE`
- `RELEASE_MANIFEST_FAILURE`
- asset 404 or integrity mismatch
- release manifest fetch/parse/schema validation failure
- release manifest active ID differs from loaded build
**Immediate containment**
1. current user input이 있으면 destructive reload 전에 경고한다.
2. release manifest를 `no-store`로 한 번 조회한다.
3. manifest fetch/parse/schema가 실패하면 release mismatch를 추정해 reload하지 않고 update/support shell로 격리한다.
4. manifest가 valid하고 active release mismatch가 확인된 경우에만 reload guard를 먼저 기록하고 한 번만 reload한다.
**Diagnosis evidence**
- loaded build ID
- active release ID
- release manifest fetch status, content-type, parse/schema validation outcome; raw manifest 제외
- requested chunk ID, raw URL 제외
- asset manifest hash
- HTML/config/asset cache headers
**Mitigation options**
- If active release가 새 버전이고 assets reachable → one reload.
- If asset set incomplete → active pointer를 prior coherent release로 rollback.
- If release manifest artifact가 missing/malformed/incompatible → coherent manifest를 republish하거나 prior coherent release로 rollback.
- If CDN propagation 중 → active switch를 되돌리고 reachability probe 재실행.
**Escalation**
- asset set incomplete 또는 active pointer incoherent이면 release owner가 rollback 여부를 결정한다.
- origin은 정상이나 edge가 불일치하면 hosting/CDN owner에게 header·propagation evidence와 함께 넘긴다.
**Recovery assertions**
- entry와 lazy route asset 모두 2xx
- release manifest fetch·parse·schema validation과 release tuple coherence pass
- second auto reload 없음
- release coherence gate pass
- route e2e pass
**Evidence**
`artifacts/runbooks/FE-RB-002/<release-id>/` planned.
### 16.3 `FE-RB-003` — Backend API degradation
| Field | Planned contract |
| --- | --- |
| Primary owner | `feature-frontend-operational-runbook-contract` |
| Technical escalation | `feature-api-client-response-envelope-contract` → backend operation owner → release compatibility owner |
| Activation condition | terminal network/timeout/429/5xx rate가 configured threshold를 rolling 5분 동안 초과하거나 schema mismatch 1건 발생 |
| Conditional window | rate threshold 값은 baseline 후 확정; 최초 분류 목표 10분 |
| Evidence path | `artifacts/runbooks/FE-RB-003/<release-id>/` |
**Trigger**
- network/timeout/502/503/504 terminal rate 증가
- `429` 지속
- schema/envelope mismatch 발생
**Triage split**
| Signal | Likely class | First action |
| --- | --- | --- |
| network across all operations | network/CORS/DNS/provider | browser + backend reachability 확인 |
| 429 only | capacity/rate policy | `Retry-After`와 request burst 확인 |
| 5xx only | backend failure | requestId/traceId로 backend owner 전달 |
| schema mismatch after release | compatibility | frontend/backend release tuple 확인 |
| one operation only | endpoint contract | operation ID fixture 대조 |
**Containment**
- retry cap을 runtime에서 임의 확대하지 않는다.
- safe cached data가 있으면 stale-degraded로 제공한다.
- mutation은 idempotency contract 없이는 재시도하지 않는다.
- schema mismatch는 retry하지 않고 compatibility rollback을 검토한다.
**Escalation**
- network/429/5xx는 `operationId`, request/trace reference, attempt count를 backend operation owner에게 넘긴다.
- release 직후 schema mismatch면 frontend/backend release owners가 tuple을 대조하고 어느 쪽을 rollback할지 공동 결정한다.
**Recovery assertions**
- terminal failure rate가 baseline window로 복귀
- retry amplification 없음
- sample critical read/write e2e pass
- schema fixtures pass
**Evidence**
`artifacts/runbooks/FE-RB-003/<release-id>/` planned.
### 16.4 `FE-RB-004` — Telemetry sink failure
| Field | Planned contract |
| --- | --- |
| Primary owner | `feature-frontend-operational-runbook-contract` |
| Technical escalation | `feature-frontend-observability-logging-trace-contract` → telemetry platform owner |
| Activation condition | adapter init 실패 또는 sink failure/queue overflow가 rolling 5분 window에서 발생 |
| Conditional window | product flow 즉시 격리; platform triage 시작 목표 15분 |
| Evidence path | `artifacts/runbooks/FE-RB-004/<release-id>/` |
**Trigger**
- sink non-2xx/network failure
- queue overflow/drop counter 증가
- telemetry adapter initialization failure
**Containment**
- product flow를 계속 수행한다.
- bounded queue 이상 적재하지 않는다.
- telemetry failure를 동일 sink로 재귀 보고하지 않는다.
- console fallback은 safe fields에 한정한다.
**Diagnosis evidence**
- endpoint classification, raw endpoint 제외
- queue size bucket
- dropped event count
- build/release ID
- redaction test result
**Mitigation**
- sink restore
- telemetry runtime flag disable
- queue policy 조정은 owner decision + memory test 후만
**Escalation**
- client redaction/queue defect면 observability owner가 우선 수정한다.
- client contract가 정상이고 sink/ingest가 실패하면 safe endpoint classification과 drop counters만 telemetry platform owner에게 전달한다.
**Recovery assertions**
- product e2e unaffected
- delivery self-check event 성공
- queue drains within planned bound
- forbidden attribute scan pass
**Evidence**
`artifacts/runbooks/FE-RB-004/<release-id>/` planned.
### 16.5 `FE-RB-005` — Release rollback
| Field | Planned contract |
| --- | --- |
| Primary owner | `feature-frontend-operational-runbook-contract` |
| Technical escalation | `feature-frontend-release-cache-rollback-contract` → release approver/hosting owner |
| Activation condition | release-blocking boot/chunk/render/API/security defect가 확인되고 forward fix가 incident window 안에 안전하다고 증명되지 않음 |
| Conditional window | blocking defect 확인 즉시 decision; provider-dependent recovery target은 hosting 확정 전 `TBD` |
| Evidence path | `artifacts/runbooks/FE-RB-005/<release-id>/` |
**Trigger**
- boot/config incompatibility
- widespread chunk mismatch
- critical render/API compatibility defect
- security gate post-release finding
**Preconditions**
- prior immutable release exists
- prior runtime config and API compatibility known
- rollback actor and audit record owner identified
**Procedure invariant**
1. target release tuple 선택
2. prior assets reachability 확인
3. prior runtime config compatibility 확인
4. active pointer atomic switch
5. provider-specific cache action 수행
6. boot + route + API critical smoke
7. telemetry/reload-loop 확인
8. rollback record 저장
**MUST NOT**
- source rebuild를 rollback으로 부름
- HTML만 이전 버전으로 교체
- config/API compatibility 확인 없이 pointer 변경
- smoke 없이 incident close
**Escalation**
- release owner가 target tuple과 evidence를 준비하고 named release approver가 pointer switch를 승인한다.
- atomic switch나 cache invalidation이 provider primitive에서 실패하면 hosting owner에게 즉시 확대한다.
**Recovery assertions**
- `FE-GATE-014`, `FE-GATE-015` pass
- critical e2e pass
- no repeated `DEPLOY_MISMATCH`
- incident timeline에 release IDs 기록
**Evidence**
`artifacts/runbooks/FE-RB-005/<release-id>/` planned.
---
## 17. Evidence Ledger
### 17.1 Evidence records
| Evidence ID | Artifact / observation | Grade | Supports | Does not prove |
| --- | --- | --- | --- | --- |
| `FE-EV-001` | 본 project note | `documented-only` | contract scope, IDs, defaults | code existence, test pass |
| `FE-EV-002` | [[raw/project-notes/ca-skeleton-operational-contract]] | `documented-only` reference | backend sibling의 operational contract pattern | frontend implementation |
| `FE-EV-003` | [[raw/official-docs/vite-build-tool-official]] | source reference | Vite decision research | chosen config implemented |
| `FE-EV-004` | [[raw/official-docs/react-ui-library-official]] | source reference | React decision research | component tree exists |
| `FE-EV-005` | [[raw/official-docs/tailwind-css-utility-first-official]] | source reference | styling decision research | Tailwind configured |
| `FE-EV-006` | [[raw/official-docs/tanstack-query-server-state-official]] | source reference | query state decision research | cache policy implemented |
| `FE-EV-007` | [[raw/official-docs/zod-runtime-schema-validation-official]] | source reference | runtime validation decision research | schema/tests exist |
| `FE-EV-008` | [[raw/official-docs/react-router-official]] | source reference | routing decision research | route registry exists |
| `FE-EV-009` | §0.2의 exact `rg --files` + manifest/lock/Vite/`src/main` regex search | observed read-only check | current wiki workspace에서 해당 entry artifact pattern 미발견 | 전체 source/test/CI/deploy 부재 또는 remote/other workspace 부재 |
| `FE-EV-010` | overview draw.io + `docs/superpowers/specs/2026-07-18-ca-skeleton-frontend-operational-contract-review/diagram-review.md` | `documented-only`, reviewer PASS 100/100 scoped | Clean Architecture dependency ownership view가 §4.2~§4.4와 정합 | source code, import-rule 구현, runtime topology |
| `FE-EV-011` | deployment draw.io + `docs/superpowers/specs/2026-07-18-ca-skeleton-frontend-operational-contract-review/diagram-review.md` | `documented-only`, reviewer PASS 100/100 scoped | static asset와 `/config.json` delivery slice가 declared boundary와 정합 | §12 전체 release/rollback topology, 실제 hosting·deploy |
| `FE-EV-012` | test/CI evidence | `UNVERIFIED` | dedicated search/command가 기록되지 않았다는 경계 | artifact 부재 또는 gate pass/fail |
| `FE-EV-013` | release/deploy evidence | `UNVERIFIED` | dedicated search/command가 기록되지 않았다는 경계 | artifact 부재, rollback 또는 runtime behavior |
### 17.2 Evidence promotion protocol
To mark `actually-implemented`:
- repository URL/path
- commit SHA
- source path
- matching `FE-OC-*` and `FE-D*`
To mark `locally-verified`:
- all above
- exact command
- tool/runtime version
- exit code
- machine-readable artifact path
- negative fixture result where applicable
To mark `prod-verified`:
- all above
- release ID
- environment
- measurement window
- dashboard/log/incident evidence
- rollback or recovery evidence where relevant
---
## 18. Binary Readiness Scorecard
### 18.1 Formula
```text
PASS_STATES = {PASS, PASS_SCOPED}
READY iff every blocking row Current is in PASS_STATES
otherwise NOT_READY
```
`PASS_SCOPED`는 Blocking question과 Required evidence가 명시적으로 같은 제한 범위를 물을 때만 허용한다. 점수 평균으로 blocking failure를 상쇄하지 않는다.
### 18.2 Current scorecard
| Readiness ID | Blocking question | Required evidence | Current | Reason |
| --- | --- | --- | --- | --- |
| `FE-RDY-001` | implementation repository가 식별됐는가 | repo URL/path + commit | `FAIL` | entry artifact pattern 미발견; repo location은 `UNVERIFIED` |
| `FE-RDY-002` | package manifest와 frozen lockfile이 있는가 | manifest + lockfile | `FAIL` | evidence 없음 |
| `FE-RDY-003` | architecture port ownership이 코드로 강제되는가 | dependency test | `FAIL` | implementation evidence 없음 |
| `FE-RDY-004` | overview dependency view가 reviewer gate를 통과했는가 | reviewer report | `PASS_SCOPED` | reviewer 100/100; 구현 evidence와 별개 |
| `FE-RDY-005` | deployment의 static asset/runtime-config delivery slice가 reviewer gate를 통과했는가 | reviewer report | `PASS_SCOPED` | reviewer 100/100; §12 전체·실제 deploy mapping은 `UNVERIFIED` |
| `FE-RDY-006` | runtime config boot gate가 검증됐는가 | schema + boot tests | `FAIL` | test evidence `UNVERIFIED` |
| `FE-RDY-007` | failure taxonomy가 test matrix로 강제되는가 | integration artifacts | `FAIL` | test evidence `UNVERIFIED` |
| `FE-RDY-008` | auth boundary가 token lifecycle을 침범하지 않는가 | port/import tests | `FAIL` | implementation evidence 없음 |
| `FE-RDY-009` | 8 registry가 single owner로 구현됐는가 | registry snapshots | `FAIL` | implementation evidence 없음 |
| `FE-RDY-010` | lint/checkJs/runtime-schema/unit/component/integration/e2e/a11y가 통과했는가 | CI artifacts | `FAIL` | CI evidence `UNVERIFIED` |
| `FE-RDY-011` | build/bundle/security gate가 통과했는가 | release artifacts | `FAIL` | build evidence `UNVERIFIED` |
| `FE-RDY-012` | lab·field NFR context와 측정값이 있는가 | lab + field reports | `FAIL` | target만 존재; `FE-GATE-018`, `FE-GATE-026``FAIL_UNVERIFIED` |
| `FE-RDY-013` | release compatibility tuple이 검증됐는가 | release verification | `FAIL` | release evidence `UNVERIFIED` |
| `FE-RDY-014` | rollback drill이 수행됐는가 | drill record | `FAIL` | deploy/drill evidence `UNVERIFIED` |
| `FE-RDY-015` | runbook이 실제 hosting command와 evidence path를 가지는가 | provider runbook | `FAIL` | provider 미정 |
| `FE-RDY-016` | evidence ledger에 과장 없는 grade가 유지되는가 | ledger review | `PASS` | 현재 문서 경계 명시 |
**Current verdict: `NOT_READY`**
Repository identity와 implementation/test/CI/deploy evidence 또는 blocking gate가 미검증이면 verdict는 유지된다. scoped diagram PASS는 이를 상쇄하지 않는다. 문서 분량이나 decision row 수로 readiness를 승격하지 않는다.
---
## 19. Risks and Open Questions
### 19.1 Risk register
| Risk ID | Risk | Owner | Trigger | Mitigation | Resolution condition | Status |
| --- | --- | --- | --- | --- | --- | --- |
| `FE-RISK-001` | remote implementation repo가 따로 존재해 문서가 실제 stack과 drift | project owner | repo URL 발견 | inventory 후 decision/contract map 재검토 | repo commit과 ledger 연결 | `open` |
| `FE-RISK-002` | runtime config와 HTML publish가 atomic하지 않음 | release owner | hosting 선택 | coherent release pointer 또는 env rebuild fallback | mismatch drill pass | `open` |
| `FE-RISK-003` | pnpm이 target CI/org 표준과 충돌 | toolchain owner | CI platform 확정 | `FE-D001` 재검토 | frozen install gate pass | `open` |
| `FE-RISK-004` | JavaScript checkJs coverage가 complex API를 놓침 | toolchain/schema owners | recurring runtime defects | TypeScript 또는 generated types 비교 | negative fixtures + defect trend 기준 충족 | `open` |
| `FE-RISK-005` | auth route guard가 security control로 오해됨 | auth/routing owners | guarded route 구현 | backend authz requirement 문서·test | e2e에서 403 처리 확인 | `open` |
| `FE-RISK-006` | retry가 backend overload를 증폭 | API owner | 429/5xx spike | cap/jitter/Retry-After + telemetry | load/degradation test pass | `open` |
| `FE-RISK-007` | cache persistence가 PII 또는 stale schema를 남김 | query/storage owners | offline persistence opt-in | classification/version/TTL/migration gate | threat model + compatibility tests | `open` |
| `FE-RISK-008` | telemetry failure가 memory growth 유발 | telemetry owner | sink outage | bounded queue/drop policy | soak test pass | `open` |
| `FE-RISK-009` | chunk auto reload가 user input 손실 | release/presentation owners | lazy chunk failure | dirty-state guard + one reload cap | e2e recovery pass | `open` |
| `FE-RISK-010` | bundle threshold가 실제 device UX와 무관 | performance owner | first measurement | context/field data로 threshold revisit | decision update with evidence | `open` |
| `FE-RISK-011` | supply-chain scanner policy가 미정이라 gate가 형식적 | security owner | repo bootstrap | scanner/severity/suppression decision | SARIF gate pass | `open` |
| `FE-RISK-012` | draw.io와 text contract의 component/edge drift | architecture owner | diagram 또는 FE-D 변경 | reviewer + contract ID annotation in caption | review report resolves all edges | `open` |
### 19.2 Open questions
| Question ID | Question | Owner | Decision trigger | Required evidence | Resolution condition |
| --- | --- | --- | --- | --- | --- |
| `FE-Q-001` | 실제 repo 위치와 ownership은? | project owner | implementation handoff | URL/path/commit | ledger update |
| `FE-Q-002` | target Node/pnpm version은? | toolchain owner | repo creation | CI runner/org standard | manifest `engines` + fresh clone pass |
| `FE-Q-003` | static hosting provider와 atomic deploy primitive는? | release owner | first deploy | provider docs/config | `FE-D023` confirmed |
| `FE-Q-004` | runtime config endpoint를 hosting이 지원하는가? | config/release owners | hosting choice | staging publish experiment | `FE-D012` or `FE-D013` final |
| `FE-Q-005` | backend envelope/OpenAPI source는 어디인가? | API owner | first integration | versioned schema/fixture | runtime schema generated or mapped |
| `FE-Q-006` | auth integration adapter는 어떤 owner가 제공하는가? | auth owner | guarded route | session interface + lifecycle doc | port contract test |
| `FE-Q-007` | browser support matrix는? | product owner | first release | product analytics/requirement | CI browser matrix fixed |
| `FE-Q-008` | telemetry sink와 consent policy는? | telemetry/privacy owners | production telemetry | data inventory + endpoint | redaction and delivery tests |
| `FE-Q-009` | service worker/offline이 필요한가? | product/release owners | offline requirement | UX/update design | `FE-D019` retained or superseded |
| `FE-Q-010` | vulnerability/license blocking threshold는? | security owner | CI setup | organization policy | security gate configured |
---
<!-- section-id: project-work-items -->
## 8.0 실행계획
> Project contract v2의 branch handoff SSOT. `Dependencies`는 stable WI ID만 사용하고 `Applies Decisions`는 revision 1에 pin한다.
| Work Item ID | branch slug | 완료 조건 (측정가능) | Applies Decisions | Dependencies | Status |
|---|---|---|---|---|---|
| `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-001` | `feature-frontend-project-bootstrap-toolchain-contract` | manifest·engines·pnpm lock·checkJs scripts와 frozen install evidence가 존재한다 | `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-TOOLCHAIN-001@1`, `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-LANGUAGE-001@1`, `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-BUILD-001@1` | - | `planned` |
| `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-002` | `feature-frontend-clean-architecture-layering-contract` | directory 책임·port owner·allowed import matrix가 문서와 fixture로 고정된다 | `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-ARCHITECTURE-001@1`, `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-PORT-OWNERSHIP-001@1`, `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-COMPOSITION-ROOT-001@1` | `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-001` | `planned` |
| `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-003` | `feature-frontend-architecture-enforcement-lint-contract` | allowed fixture는 통과하고 forbidden fixture는 실패하며 lint report가 생성된다 | `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-ARCHITECTURE-001@1` | `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-002`, `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-017` | `planned` |
| `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-004` | `feature-frontend-env-runtime-config-contract` | build/runtime/secret registry와 boot-invalid matrix가 검증된다 | `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-RUNTIME-CONFIG-001@1`, `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-CONFIG-FALLBACK-001@1` | `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-001` | `planned` |
| `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-005` | `feature-api-client-response-envelope-contract` | API registry와 timeout·abort·retry·idempotency deterministic tests가 통과한다 | `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-TIMEOUT-001@1`, `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-RETRY-001@1`, `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-IDEMPOTENCY-001@1` | `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-004`, `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-002` | `planned` |
| `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-006` | `feature-runtime-schema-validation-contract` | content-type·JSON·envelope·payload invalid fixture가 기대 failure kind로 정규화된다 | `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-VALIDATION-001@1` | `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-005` | `planned` |
| `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-007` | `feature-frontend-error-classification-boundary-contract` | normalization matrix와 raw body·stack leakage negative test가 통과한다 | `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-VALIDATION-001@1` | `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-005`, `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-006` | `planned` |
| `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-008` | `feature-frontend-auth-session-integration-contract` | AuthSessionPort·bounded 401 replay와 token lifecycle import 금지가 test로 고정된다 | `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-AUTH-BOUNDARY-001@1`, `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-PORT-OWNERSHIP-001@1` | `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-002` | `planned` |
| `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-009` | `feature-routing-navigation-guard-contract` | registry route·param validation·404·redirect-loop·session UX test가 통과한다 | `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-ROUTING-001@1`, `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-AUTH-BOUNDARY-001@1` | `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-008`, `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-007` | `planned` |
| `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-010` | `feature-server-state-caching-contract` | QueryCachePort와 TanStack adapter의 invalidation·stale test가 통과한다 | `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-SERVER-STATE-001@1` | `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-005` | `planned` |
| `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-011` | `feature-frontend-storage-registry-contract` | namespace·version·classification·quota fallback test가 통과한다 | `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-REGISTRY-001@1` | `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-002` | `planned` |
| `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-012` | `feature-frontend-observability-logging-trace-contract` | telemetry registry·redaction·bounded queue·sink failure test가 통과한다 | `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-TELEMETRY-001@1`, `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-REGISTRY-001@1` | `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-004` | `planned` |
| `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-013` | `feature-async-ui-state-contract` | required와 non-blocking state matrix component test가 통과한다 | `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-UI-001@1`, `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-SERVER-STATE-001@1` | `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-007`, `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-010` | `planned` |
| `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-014` | `feature-boundary-mapper-viewmodel-contract` | raw DTO direct use가 차단되고 mapper negative fixture가 실패한다 | `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-VALIDATION-001@1`, `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-ARCHITECTURE-001@1` | `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-006` | `planned` |
| `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-015` | `feature-frontend-render-recovery-boundary-contract` | boot·route·feature·async boundary ownership과 recovery fixture가 검증된다 | `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-UI-001@1`, `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-ARCHITECTURE-001@1` | `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-007`, `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-013` | `planned` |
| `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-016` | `feature-sample-feature-slice-contract-fixture` | full contract slice와 sample removal smoke test가 통과한다 | `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-SAMPLE-FIXTURE-001@1` | `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-005`, `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-006`, `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-007`, `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-009`, `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-010` | `planned` |
| `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-017` | `feature-frontend-test-taxonomy-contract` | gate·fixture·artifact mapping과 test level별 최소 1개 test가 존재한다 | `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-TEST-STACK-001@1` | `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-001` | `planned` |
| `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-018` | `feature-tailwind-design-token-styling-contract` | theme token·arbitrary value policy·sample UI가 검증된다 | `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-STYLING-001@1` | `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-001` | `planned` |
| `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-019` | `feature-accessibility-baseline-contract` | sample route에서 axe·keyboard·focus evidence가 남는다 | `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-STYLING-001@1`, `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-TEST-STACK-001@1` | `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-013` | `planned` |
| `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-020` | `feature-frontend-build-bundle-supply-chain-contract` | frozen build·inventory·scan·bundle report가 CI artifact로 생성된다 | `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-BUILD-001@1`, `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-SUPPLY-CHAIN-001@1` | `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-001`, `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-017` | `planned` |
| `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-021` | `feature-frontend-browser-security-boundary-contract` | CSP·header·secret·storage·telemetry browser-boundary fixture가 통과한다 | `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-AUTH-BOUNDARY-001@1`, `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-TELEMETRY-001@1` | `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-008`, `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-011`, `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-012` | `planned` |
| `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-022` | `feature-frontend-contract-registry-governance` | 8개 registry snapshot·schema validation·single-owner check가 통과한다 | `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-REGISTRY-001@1` | `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-001`, `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-002` | `planned` |
| `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-023` | `feature-frontend-contract-compatibility-governance` | version tuple·additive/breaking fixture·migration/rollback rule가 검증된다 | `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-REGISTRY-001@1`, `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-CONFIG-FALLBACK-001@1` | `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-022`, `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-006`, `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-011` | `planned` |
| `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-024` | `feature-frontend-release-cache-rollback-contract` | release tuple·cache header·mixed fixture failure·rollback drill이 검증된다 | `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-DEPLOYMENT-001@1`, `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-CACHE-POLICY-001@1`, `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-OFFLINE-CACHE-001@1` | `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-020`, `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-004`, `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-023` | `planned` |
| `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-025` | `feature-web-vitals-performance-budget-contract` | context metadata와 lab·bundle·28-day field report가 생성된다 | `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-BUILD-001@1`, `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-TEST-STACK-001@1` | `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-020`, `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-016` | `planned` |
| `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-026` | `feature-frontend-operational-runbook-contract` | 5개 drill의 trigger·window·escalation·evidence assertion이 검증된다 | `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-DEPLOYMENT-001@1`, `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-TELEMETRY-001@1` | `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-024`, `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-005`, `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-012`, `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-004` | `planned` |
| `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-027` | `feature-frontend-ci-quality-gates-contract` | blocking gate가 분리되고 dependency graph와 artifact retention이 검증된다 | `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-TEST-STACK-001@1`, `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-SUPPLY-CHAIN-001@1`, `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-DEPLOYMENT-001@1` | `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-017`, `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-020`, `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-024`, `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-025`, `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-026` | `planned` |
## 20. Branch Decomposition / Execution Plan
> **Legacy reference (v1).** 아래 표는 기존 FE-OC ownership·priority 설명을 보존한다. branch handoff ID·decision pin·dependency의 SSOT는 위 Work Item Registry다.
Branch는 project-wide contract를 상세 implementation-ready spec으로 내린다. `Primary contract IDs`는 single owner만 가지며 `Contributes to`는 acceptance fixture·adapter·gate 협업만 뜻한다. `FE-OC-001``FE-OC-026`의 primary owner는 이 project hub다. 아래 27개 branch-note는 2026-07-18 `/branch` scaffolding으로 생성되어 §21.2 Cluster에 연결됐다. 파일 존재는 implementation evidence가 아니며, 각 row의 mechanism·decision·test가 채워지기 전까지 상태는 계속 `planned`다.
| Branch slug | Primary contract IDs | Contributes to | Measurable completion | Priority | Dependency |
| --- | --- | --- | --- | --- | --- |
| `feature-frontend-project-bootstrap-toolchain-contract` | `FE-OC-003` | `FE-OC-018`, `FE-OC-020` | manifest/engines/pnpm lock/checkJs scripts + frozen install evidence | P1 | — |
| `feature-frontend-clean-architecture-layering-contract` | `FE-OC-002` | `FE-OC-004`, `FE-OC-011`, `FE-OC-020` | directory responsibility + port owner + allowed import matrix | P1 | `feature-frontend-project-bootstrap-toolchain-contract` |
| `feature-frontend-architecture-enforcement-lint-contract` | — | `FE-OC-002`, `FE-OC-020` | allowed fixture pass, forbidden fixture fail, report emitted | P1 | `feature-frontend-clean-architecture-layering-contract`, `feature-frontend-test-taxonomy-contract` |
| `feature-frontend-env-runtime-config-contract` | `FE-OC-004` | `FE-OC-016`, `FE-OC-023` | build/runtime/secret registry + boot invalid matrix | P1 | `feature-frontend-project-bootstrap-toolchain-contract` |
| `feature-api-client-response-envelope-contract` | `FE-OC-006`, `FE-OC-009` | `FE-OC-007`, `FE-OC-008`, `FE-OC-010`, `FE-OC-012`, `FE-OC-023` | API operation registry + timeout/abort/retry/idempotency deterministic tests | P1 | `feature-frontend-env-runtime-config-contract`, `feature-frontend-clean-architecture-layering-contract` |
| `feature-runtime-schema-validation-contract` | `FE-OC-007` | `FE-OC-008`, `FE-OC-023` | content-type/JSON/envelope/payload invalid fixtures map to expected kinds | P1 | `feature-api-client-response-envelope-contract` |
| `feature-frontend-error-classification-boundary-contract` | `FE-OC-008` | `FE-OC-011`, `FE-OC-015`, `FE-OC-020` | total normalization matrix + raw body/stack leakage negative tests | P1 | `feature-api-client-response-envelope-contract`, `feature-runtime-schema-validation-contract` |
| `feature-frontend-auth-session-integration-contract` | `FE-OC-010` | `FE-OC-005`, `FE-OC-006`, `FE-OC-019` | AuthSessionPort + bounded 401 state/replay + no token lifecycle import tests | P1 | `feature-frontend-clean-architecture-layering-contract` |
| `feature-routing-navigation-guard-contract` | `FE-OC-005` | `FE-OC-010`, `FE-OC-015`, `FE-OC-024` | registry routes, param validation, 404, redirect-loop, session UX tests | P2 | `feature-frontend-auth-session-integration-contract`, `feature-frontend-error-classification-boundary-contract` |
| `feature-server-state-caching-contract` | `FE-OC-012` | `FE-OC-011`, `FE-OC-022`, `FE-OC-024` | application-owned QueryCachePort + TanStack adapter/invalidation/stale tests | P2 | `feature-api-client-response-envelope-contract` |
| `feature-frontend-storage-registry-contract` | `FE-OC-013` | `FE-OC-022`, `FE-OC-023` | namespace/version/classification/quota fallback tests | P2 | `feature-frontend-clean-architecture-layering-contract` |
| `feature-frontend-observability-logging-trace-contract` | `FE-OC-014` | `FE-OC-008`, `FE-OC-021`, `FE-OC-025` | telemetry registry, redaction, bounded queue, sink failure tests | P2 | `feature-frontend-env-runtime-config-contract` |
| `feature-async-ui-state-contract` | `FE-OC-011` | `FE-OC-015`, `FE-OC-020`, `FE-OC-024` | required + non-blocking state matrix component tests | P2 | `feature-frontend-error-classification-boundary-contract`, `feature-server-state-caching-contract` |
| `feature-boundary-mapper-viewmodel-contract` | — | `FE-OC-007`, `FE-OC-024` | raw DTO direct use prohibited; mapper negative fixture | P2 | `feature-runtime-schema-validation-contract` |
| `feature-frontend-render-recovery-boundary-contract` | `FE-OC-015` | `FE-OC-005`, `FE-OC-011`, `FE-OC-025` | boot/route/feature/async boundary ownership + recovery fixtures | P2 | `feature-frontend-error-classification-boundary-contract`, `feature-async-ui-state-contract` |
| `feature-sample-feature-slice-contract-fixture` | `FE-OC-024` | `FE-OC-005`, `FE-OC-006`, `FE-OC-007`, `FE-OC-008`, `FE-OC-011`, `FE-OC-012`, `FE-OC-020`, `FE-OC-021` | full contract slice + sample removal smoke | P2 | `feature-api-client-response-envelope-contract`, `feature-runtime-schema-validation-contract`, `feature-frontend-error-classification-boundary-contract`, `feature-routing-navigation-guard-contract`, `feature-server-state-caching-contract` |
| `feature-frontend-test-taxonomy-contract` | `FE-OC-020` | `FE-OC-002`, `FE-OC-003`, `FE-OC-007`, `FE-OC-008`, `FE-OC-019`, `FE-OC-021`, `FE-OC-024`, `FE-OC-025` | gate/fixture/artifact mapping + one test per level | P1 | `feature-frontend-project-bootstrap-toolchain-contract` |
| `feature-tailwind-design-token-styling-contract` | — | `FE-OC-011`, `FE-OC-019`, `FE-OC-021` | theme tokens + arbitrary value policy + sample UI | P3 | `feature-frontend-project-bootstrap-toolchain-contract` |
| `feature-accessibility-baseline-contract` | — | `FE-OC-019`, `FE-OC-020`, `FE-OC-021`, `FE-OC-024` | axe + keyboard/focus manual evidence for sample routes | P3 | `feature-async-ui-state-contract` |
| `feature-frontend-build-bundle-supply-chain-contract` | `FE-OC-018` | `FE-OC-003`, `FE-OC-016`, `FE-OC-019`, `FE-OC-020`, `FE-OC-021` | frozen build, inventory, scan, bundle report | P3 | `feature-frontend-project-bootstrap-toolchain-contract`, `feature-frontend-test-taxonomy-contract` |
| `feature-frontend-browser-security-boundary-contract` | `FE-OC-019` | `FE-OC-010`, `FE-OC-013`, `FE-OC-014`, `FE-OC-018`, `FE-OC-020` | CSP/header/secret/storage/telemetry browser-boundary fixtures | P3 | `feature-frontend-auth-session-integration-contract`, `feature-frontend-storage-registry-contract`, `feature-frontend-observability-logging-trace-contract` |
| `feature-frontend-contract-registry-governance` | `FE-OC-022` | `FE-OC-004`, `FE-OC-005`, `FE-OC-006`, `FE-OC-008`, `FE-OC-012`, `FE-OC-013`, `FE-OC-014`, `FE-OC-016`, `FE-OC-020`, `FE-OC-023` | 8 registry snapshots, schema validation, single-owner checks | P2 | `feature-frontend-project-bootstrap-toolchain-contract`, `feature-frontend-clean-architecture-layering-contract` |
| `feature-frontend-contract-compatibility-governance` | `FE-OC-023` | `FE-OC-004`, `FE-OC-006`, `FE-OC-007`, `FE-OC-012`, `FE-OC-013`, `FE-OC-016`, `FE-OC-017` | version tuple matrix + additive/breaking fixtures + migration/rollback rule | P3 | `feature-frontend-contract-registry-governance`, `feature-runtime-schema-validation-contract`, `feature-frontend-storage-registry-contract` |
| `feature-frontend-release-cache-rollback-contract` | `FE-OC-016`, `FE-OC-017` | `FE-OC-004`, `FE-OC-015`, `FE-OC-021`, `FE-OC-023`, `FE-OC-025` | release tuple, headers, mixed fixture fail, rollback drill | P3 | `feature-frontend-build-bundle-supply-chain-contract`, `feature-frontend-env-runtime-config-contract`, `feature-frontend-contract-compatibility-governance` |
| `feature-web-vitals-performance-budget-contract` | `FE-OC-021` | `FE-OC-016`, `FE-OC-020` | context metadata + lab/bundle/28-day field reports | P3 | `feature-frontend-build-bundle-supply-chain-contract`, `feature-sample-feature-slice-contract-fixture` |
| `feature-frontend-operational-runbook-contract` | `FE-OC-025` | `FE-OC-004`, `FE-OC-006`, `FE-OC-014`, `FE-OC-016`, `FE-OC-017` | five drills with trigger/window/escalation/evidence assertions | P3 | `feature-frontend-release-cache-rollback-contract`, `feature-api-client-response-envelope-contract`, `feature-frontend-observability-logging-trace-contract`, `feature-frontend-env-runtime-config-contract` |
| `feature-frontend-ci-quality-gates-contract` | — | `FE-OC-020`, `FE-OC-021`, `FE-OC-022`, `FE-OC-023`, `FE-OC-024`, `FE-OC-025` | separate blocking gates, dependency graph, artifact retention | P3 | `feature-frontend-test-taxonomy-contract`, `feature-frontend-build-bundle-supply-chain-contract`, `feature-frontend-release-cache-rollback-contract`, `feature-web-vitals-performance-budget-contract`, `feature-frontend-operational-runbook-contract` |
Branch creation workflow:
```text
/branch <slug>
/branch-spec <slug> <existing evidence URLs if needed>
/depth <slug>
/coverage <slug>
```
Branch completion MUST update this table, Cluster, evidence ledger, and readiness scorecard. `planned` row를 단순히 branch file 생성만으로 `actually-implemented`로 올리지 않는다.
---
## 21. 묶음
<!-- GENERATED: project-contract-imports:start -->
## 가져온 프로젝트 계약
| Ref | Owner | 요약 | Branch 적용 |
|---|---|---|---|
| `FE-GATE-018@1` | [[raw/branch-notes/feature-web-vitals-performance-budget-contract]] | p75 목표 미달이거나 표본 임계가 미해결이면 field readiness 를 MUST 차단 | import 참조로 적용 |
| `FE-GATE-026@1` | [[raw/branch-notes/feature-web-vitals-performance-budget-contract]] | lab threshold 미달이거나 재현 메타데이터가 없으면 release 를 MUST 차단 | import 참조로 적용 |
| `FE-OC-002@1` | [[raw/branch-notes/feature-frontend-clean-architecture-layering-contract]] | `domain <- application <- presentation` 의존 방향과 application-owned output port를 MUST 지킴 | import 참조로 적용 |
| `FE-OC-003@1` | [[raw/branch-notes/feature-frontend-project-bootstrap-toolchain-contract]] | package manager, engine, lockfile, source language, checkJs command를 한 곳에서 MUST 고정 | import 참조로 적용 |
| `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-007@1` | [[raw/branch-notes/feature-runtime-schema-validation-contract]] | JSON envelope와 payload는 boundary에서 runtime schema를 MUST 통과 | 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-009@1` | [[raw/branch-notes/feature-api-client-response-envelope-contract]] | retry는 safe/idempotent request에 한정하고 cap·jitter·`Retry-After`를 MUST 적용 | import 참조로 적용 |
| `FE-OC-010@1` | [[raw/branch-notes/feature-frontend-auth-session-integration-contract]] | skeleton은 session state를 소비하되 token lifecycle을 MUST 소유하지 않음 | import 참조로 적용 |
| `FE-OC-011@1` | [[raw/branch-notes/feature-async-ui-state-contract]] | async surface는 initial-loading, success, empty, terminal-error를 MUST 표현 | 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-015@1` | [[raw/branch-notes/feature-frontend-render-recovery-boundary-contract]] | expected operational error와 render defect를 MUST 분리하고 reload loop를 금지 | 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-017@1` | [[raw/branch-notes/feature-frontend-release-cache-rollback-contract]] | rollback은 immutable prior release로 수행하고 build/config/API compatibility를 MUST 검증 | import 참조로 적용 |
| `FE-OC-018@1` | [[raw/branch-notes/feature-frontend-build-bundle-supply-chain-contract]] | frozen lockfile, dependency review, secret scan, SBOM 또는 dependency inventory를 release gate에 MUST 포함 | import 참조로 적용 |
| `FE-OC-019@1` | [[raw/branch-notes/feature-frontend-browser-security-boundary-contract]] | browser bundle에 secret을 넣지 않고 untrusted HTML injection을 기본 금지 | import 참조로 적용 |
| `FE-OC-020@1` | [[raw/branch-notes/feature-frontend-test-taxonomy-contract]] | gate 종류별 책임·fixture·artifact를 분리하고 실패를 warning으로 낮추면 안 됨 | import 참조로 적용 |
| `FE-OC-021@1` | [[raw/branch-notes/feature-web-vitals-performance-budget-contract]] | NFR은 device/network/cache/build context와 함께 MUST 측정 | import 참조로 적용 |
| `FE-OC-022@1` | [[raw/branch-notes/feature-frontend-contract-registry-governance]] | 8개 registry는 single primary owner와 compatibility impact를 MUST 기록 | import 참조로 적용 |
| `FE-OC-023@1` | [[raw/branch-notes/feature-frontend-contract-compatibility-governance]] | API/config/storage/release schema의 breaking change는 migration 또는 version bump 없이 배포하면 안 됨 | import 참조로 적용 |
| `FE-OC-024@1` | [[raw/branch-notes/feature-sample-feature-slice-contract-fixture]] | sample은 contract fixture이며 production feature가 의존하면 안 됨 | import 참조로 적용 |
| `FE-OC-025@1` | [[raw/branch-notes/feature-frontend-operational-runbook-contract]] | boot, chunk mismatch, API degradation, telemetry failure, rollback runbook을 MUST 유지 | import 참조로 적용 |
<!-- GENERATED: project-contract-imports:end -->
<!-- GENERATED: sources:start -->
- [[raw/official-docs/owasp-content-security-policy-cheat-sheet]]
- [[raw/official-docs/react-router-official]]
- [[raw/official-docs/react-ui-library-official]]
- [[raw/official-docs/tailwind-css-utility-first-official]]
- [[raw/official-docs/tanstack-query-server-state-official]]
- [[raw/official-docs/vite-build-tool-official]]
- [[raw/official-docs/zod-runtime-schema-validation-official]]
<!-- GENERATED: sources:end -->
### 21.1 부모·형제 문서 맥락
- Backend sibling operational contract: [[raw/project-notes/ca-skeleton-operational-contract]]
- Auth lifecycle boundary: [[raw/project-notes/keycloak-patterns-overview]]
본 project-note는 `raw/project-notes/` root이므로 upward link 면제다.
### 21.2 브랜치
<!-- GENERATED: branches:start -->
- [[raw/branch-notes/feature-accessibility-baseline-contract]]
- [[raw/branch-notes/feature-api-client-response-envelope-contract]]
- [[raw/branch-notes/feature-async-ui-state-contract]]
- [[raw/branch-notes/feature-boundary-mapper-viewmodel-contract]]
- [[raw/branch-notes/feature-frontend-architecture-enforcement-lint-contract]]
- [[raw/branch-notes/feature-frontend-auth-session-integration-contract]]
- [[raw/branch-notes/feature-frontend-browser-security-boundary-contract]]
- [[raw/branch-notes/feature-frontend-build-bundle-supply-chain-contract]]
- [[raw/branch-notes/feature-frontend-ci-quality-gates-contract]]
- [[raw/branch-notes/feature-frontend-clean-architecture-layering-contract]]
- [[raw/branch-notes/feature-frontend-contract-compatibility-governance]]
- [[raw/branch-notes/feature-frontend-contract-registry-governance]]
- [[raw/branch-notes/feature-frontend-env-runtime-config-contract]]
- [[raw/branch-notes/feature-frontend-error-classification-boundary-contract]]
- [[raw/branch-notes/feature-frontend-observability-logging-trace-contract]]
- [[raw/branch-notes/feature-frontend-operational-runbook-contract]]
- [[raw/branch-notes/feature-frontend-project-bootstrap-toolchain-contract]]
- [[raw/branch-notes/feature-frontend-release-cache-rollback-contract]]
- [[raw/branch-notes/feature-frontend-render-recovery-boundary-contract]]
- [[raw/branch-notes/feature-frontend-storage-registry-contract]]
- [[raw/branch-notes/feature-frontend-test-taxonomy-contract]]
- [[raw/branch-notes/feature-routing-navigation-guard-contract]]
- [[raw/branch-notes/feature-runtime-schema-validation-contract]]
- [[raw/branch-notes/feature-sample-feature-slice-contract-fixture]]
- [[raw/branch-notes/feature-server-state-caching-contract]]
- [[raw/branch-notes/feature-tailwind-design-token-styling-contract]]
- [[raw/branch-notes/feature-web-vitals-performance-budget-contract]]
<!-- GENERATED: branches:end -->
> generated reverse view는 child branch의 v2 contract migration 후 채운다. 아래 수기 목록은 그 전까지 legacy navigation으로 보존한다.
- [[raw/branch-notes/feature-frontend-project-bootstrap-toolchain-contract]] — project bootstrap·toolchain contract
- [[raw/branch-notes/feature-frontend-clean-architecture-layering-contract]] — Clean Architecture layer·port ownership contract
- [[raw/branch-notes/feature-frontend-architecture-enforcement-lint-contract]] — architecture dependency lint contract
- [[raw/branch-notes/feature-frontend-env-runtime-config-contract]] — build/runtime environment config contract
- [[raw/branch-notes/feature-api-client-response-envelope-contract]] — shared API client·response envelope contract
- [[raw/branch-notes/feature-runtime-schema-validation-contract]] — runtime schema validation contract
- [[raw/branch-notes/feature-frontend-error-classification-boundary-contract]] — normalized failure classification contract
- [[raw/branch-notes/feature-frontend-auth-session-integration-contract]] — external auth session integration boundary
- [[raw/branch-notes/feature-routing-navigation-guard-contract]] — route registry·navigation guard contract
- [[raw/branch-notes/feature-server-state-caching-contract]] — server-state query cache contract
- [[raw/branch-notes/feature-frontend-storage-registry-contract]] — browser storage registry contract
- [[raw/branch-notes/feature-frontend-observability-logging-trace-contract]] — frontend telemetry·logging·trace contract
- [[raw/branch-notes/feature-async-ui-state-contract]] — async UI state contract
- [[raw/branch-notes/feature-boundary-mapper-viewmodel-contract]] — boundary mapper·view-model contract
- [[raw/branch-notes/feature-frontend-render-recovery-boundary-contract]] — render failure recovery boundary
- [[raw/branch-notes/feature-sample-feature-slice-contract-fixture]] — removable sample feature contract fixture
- [[raw/branch-notes/feature-frontend-test-taxonomy-contract]] — frontend test taxonomy contract
- [[raw/branch-notes/feature-tailwind-design-token-styling-contract]] — Tailwind design-token styling contract
- [[raw/branch-notes/feature-accessibility-baseline-contract]] — accessibility baseline contract
- [[raw/branch-notes/feature-frontend-build-bundle-supply-chain-contract]] — build·bundle·supply-chain contract
- [[raw/branch-notes/feature-frontend-browser-security-boundary-contract]] — browser security boundary contract
- [[raw/branch-notes/feature-frontend-contract-registry-governance]] — eight-registry governance contract
- [[raw/branch-notes/feature-frontend-contract-compatibility-governance]] — contract compatibility governance
- [[raw/branch-notes/feature-frontend-release-cache-rollback-contract]] — release·cache·rollback contract
- [[raw/branch-notes/feature-web-vitals-performance-budget-contract]] — Web Vitals·performance budget contract
- [[raw/branch-notes/feature-frontend-operational-runbook-contract]] — frontend operational runbook contract
- [[raw/branch-notes/feature-frontend-ci-quality-gates-contract]] — CI quality-gate orchestration contract
### 21.3 근거 자료
- [[raw/official-docs/vite-build-tool-official]] — build tool/config decision input
- [[raw/official-docs/react-ui-library-official]] — UI composition decision input
- [[raw/official-docs/tailwind-css-utility-first-official]] — styling decision input
- [[raw/official-docs/tanstack-query-server-state-official]] — server-state/cache decision input
- [[raw/official-docs/zod-runtime-schema-validation-official]] — runtime schema decision input
- [[raw/official-docs/react-router-official]] — route decision input
### 21.4 Diagrams
- `raw/diagrams/ca-skeleton-frontend/architecture-overview-2026-07-18.drawio` — active, dependency ownership scope reviewer PASS 100/100
- `raw/diagrams/ca-skeleton-frontend/architecture-deployment-2026-07-18.drawio` — active, static asset/runtime-config delivery scope reviewer PASS 100/100
### 21.5 오류·면접·블로그 글감·파생 문서
- Errors: 아직 없음
- Interview prep: 아직 없음
- Blog topics / job-posting tie-ins: 아직 없음
- Derived canonical: 아직 없음
Canonical 또는 derived 문서는 implementation evidence와 promotion gate를 통과하기 전 생성하지 않는다.
---
## 22. External Answer Boundary
### 22.1 현재 답할 수 있는 것
- frontend operational contract의 목표와 scope
- `FE-D*`에서 검토한 선택과 조건부 default
- `FE-OC-*`의 owner·failure·acceptance 구조
- 왜 port를 application이 소유하고 adapter가 구현하도록 설계했는지
- 왜 build/runtime/secret config를 분리했는지
- 왜 retry에 cap, jitter, idempotency 조건을 둔 설계인지
- 두 architecture diagram의 scoped reviewer PASS와 그 범위; implementation·§12 전체 deploy/rollback evidence와는 분리한 설명
### 22.2 설계라고 명시해야 답할 수 있는 것
- planned folder/file/class name
- pnpm/Vitest/RTL/MSW/Playwright/axe toolchain
- timeout, retry, bundle, performance threshold
- release/rollback sequence
- telemetry and storage policy
표현 예:
```text
현재 구현 증거는 없고, 문서상 기본값으로 설계했다.
repository 생성 후 <gate/artifact>로 검증할 예정이다.
```
### 22.3 현재 답하면 안 되는 것
- frontend를 구현·운영했다는 주장
- pnpm install/build/test를 실행했다는 주장
- bundle 200 KiB를 달성했다는 주장
- LCP/CLS/interaction target을 측정했다는 주장
- WCAG 적합성을 확보했다는 주장
- retry/rollback이 운영 장애에서 효과가 있었다는 주장
- auth token refresh를 구현했다는 주장
- diagram review가 실제 implementation·hosting·§12 전체 release/rollback을 증명한다는 주장
### 22.4 Derived output gate
면접·포트폴리오·블로그 산출물은 다음을 모두 만족한 canonical에서만 파생한다.
1. source canonical status가 `reviewed` 이상
2. 구현 주장은 `actually-implemented` 이상 evidence 보유
3. 측정 주장은 `locally-verified` 또는 `prod-verified` evidence 보유
4. readiness scorecard의 관련 gate PASS
5. 이 §의 금지 표현 위반 없음
---
## 23. 아키텍처 검토 체크리스트
### 23.1 Document structure
- [x] project overview와 evidence boundary가 있음
- [x] stable `FE-D*` decision register가 있음
- [x] stable `FE-OC-*` contract index가 있음
- [x] application-owned port와 composition root가 명시됨
- [x] route/API-operation/env/storage/error/query/telemetry/release 8개 registry owner가 있음
- [x] failure taxonomy와 retry/fallback/UX/telemetry가 있음
- [x] build/runtime/secret config 구분이 있음
- [x] NFR context와 planned command가 있음
- [x] acceptance gate와 evidence artifact가 있음
- [x] runbook 5종이 있음
- [x] binary readiness가 `NOT_READY`로 계산됨
- [x] risks/questions에 owner·trigger·resolution이 있음
- [x] branch가 contract ID에 매핑됨
- [x] Cluster와 external answer boundary가 있음
### 23.2 Diagram review
- [x] overview `.drawio` 파일이 존재하고 frontmatter에 등록됨
- [x] deployment `.drawio` 파일이 존재하고 frontmatter에 등록됨
- [x] overview diagram이 [[rules/diagram-standards]] reviewer gate 통과 — 100/100
- [x] deployment diagram이 [[rules/diagram-standards]] reviewer gate 통과 — 100/100
- [x] overview component label과 dependency ownership view가 §4.2~§4.4 scope와 일치
- [x] overview dependency edge가 scoped §4.3 matrix와 일치
- [x] deployment static asset·`/config.json` delivery slice가 declared boundary와 일치
- [ ] deployment diagram으로 §12 전체 release/rollback topology 또는 실제 hosting을 검증
- [x] reviewer report가 `FE-EV-010`, `FE-EV-011`에 연결됨
### 23.3 Implementation review
- [ ] repository URL/path/commit 확인
- [ ] manifest + lockfile 확인
- [ ] composition root 확인
- [ ] negative architecture fixture 확인
- [ ] config boot failure fixture 확인
- [ ] failure taxonomy coverage 확인
- [ ] all acceptance artifacts 확인
- [ ] release/rollback drill 확인
---
## 24. Diagram and Contract Change Management
### 24.1 Diagram lifecycle
- architecture 변경 시 새 날짜 파일을 만들거나 동일 파일 변경 사유를 version control에 남긴다.
- 폐기 diagram은 삭제보다 `raw/diagrams/ca-skeleton-frontend/archived/` 이동을 우선한다.
- frontmatter `diagrams`에는 active file만 둔다.
- `architecture_review.reviewed_at`은 reviewer gate 통과 뒤에만 채운다.
- draw.io는 static architecture/deployment용이고 sequence는 본문 Mermaid를 사용한다.
### 24.2 Drift checks
Planned checks:
```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]+)*'
rg --pcre2 -o "$CONTRACT_STABLE_ID_RE" raw/project-notes/ca-skeleton-frontend-operational-contract.md | sort -u
test -z "$(comm -23 <(rg --pcre2 -o "$CONTRACT_STABLE_ID_RE" raw/project-notes/ca-skeleton-frontend-operational-contract.md | sort -u) <(rg --pcre2 -n '^\| `FE-|^### 16\.[0-9]+ `FE-RB-' raw/project-notes/ca-skeleton-frontend-operational-contract.md | rg --pcre2 -o "$CONTRACT_STABLE_ID_RE" | sort -u))"
rg -n 'application.*adapters|presentation.*adapters' <implementation-repo>/src <implementation-repo>/tests
rg -n 'architecture-(overview|deployment)-2026-07-18.drawio' raw/project-notes/ca-skeleton-frontend-operational-contract.md
```
두 번째 command는 reference set에서 definition set을 뺀 결과가 empty인지 검사한다. 세 번째 command는 repository path가 없어 실행 대상이 아직 없다.
---
## 25. Next Steps
### P0 — Evidence and architecture
- [ ] 실제 frontend repository 위치와 owner 확인
- [ ] `FE-EV-009` 범위를 repo/commit evidence로 갱신
- [x] overview/deployment scoped diagram reviewer 결과 반영
- [ ] future diagram 변경 시 §4와 scoped delivery boundary drift 재검토
### P1 — Bootstrap and blocking contracts
- [ ] `feature-frontend-project-bootstrap-toolchain-contract` 생성·설계·depth gate
- [ ] `feature-frontend-clean-architecture-layering-contract` 생성
- [ ] `feature-frontend-test-taxonomy-contract` 생성·negative fixture taxonomy 확정
- [ ] `feature-frontend-architecture-enforcement-lint-contract` 생성
- [ ] `feature-frontend-env-runtime-config-contract` 생성
- [ ] `feature-api-client-response-envelope-contract` 생성
- [ ] `feature-runtime-schema-validation-contract` 생성
- [ ] `feature-frontend-error-classification-boundary-contract` 생성
- [ ] `feature-frontend-auth-session-integration-contract` 생성
### P2 — State, UX, telemetry, fixture
- [ ] route/query/storage/telemetry registry 구현 branch 전개
- [ ] async state와 mapper boundary 전개
- [ ] sample feature contract fixture 구현
### P3 — Release readiness
- [ ] build/bundle/supply-chain gate 구현
- [ ] accessibility와 performance context 측정
- [ ] release/cache/rollback contract 구현
- [ ] CI gate dependency와 artifact retention 확정
- [ ] staging rollback drill 수행
### Promotion
- [ ] 모든 implementation claim에 repo/commit/path 연결
- [ ] 모든 locally-verified claim에 command/exit/artifact 연결
- [ ] readiness blocking rows PASS 후 `status` 재검토
- [ ] canonical `wiki/projects/` 승급은 별도 ingest review에서 수행
---
## 26. Verification Status Summary
| Area | Grade | Evidence |
| --- | --- | --- |
| operational contract text | `documented-only` | this file |
| technology decision sources | `documented-only` | §21.3 existing raw sources |
| architecture diagrams | `documented-only`, scoped reviewer PASS 100/100 each | dependency ownership view + static asset/runtime-config delivery slice; implementation/§12 full topology `UNVERIFIED` |
| implementation | `planned` | entry artifact pattern search만 미발견; repo location `UNVERIFIED` |
| tests / CI | `planned` | dedicated search/command 미기록, `UNVERIFIED` |
| NFR measurement | `planned` | target/context only |
| release / rollback | `planned` | policy/runbook only; dedicated deploy evidence `UNVERIFIED` |
| production operation | `planned` | release evidence `UNVERIFIED` |
최종 현재 판정은 `NOT_READY`다. diagram review는 통과했지만 repository, test, deploy, release evidence가 채워질 때까지 이 판정을 유지한다.