Files
llm-wiki/raw/branch-notes/feature-frontend-test-taxonomy-contract.md
T

353 lines
33 KiB
Markdown

---
title: branch / feature-frontend-test-taxonomy-contract
source_type: branch-note
status: raw
id: BR-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-017
kind: project-work-item
project: ca-skeleton-frontend-operational-contract
work_item: WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-017
inherits: [DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-TEST-STACK-001@1]
refines: []
overrides: []
depends_on: [WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-001]
contract_packet: 1
branch: feature-frontend-test-taxonomy-contract
parent_branch:
related_projects: [ca-skeleton-frontend, ca-skeleton]
governing_docs: [raw/project-notes/ca-skeleton-frontend-operational-contract.md]
tags: [branch, ca-skeleton, frontend, testing, react, javascript]
created: 2026-07-18
target_merge:
status_label: in-progress
contract_packet_sha256: 160ba678c61d567516938e08a0a4413a55424796822f145f22d575376746274d
imports: [ART-FE-001@1, ART-FE-002@1, ART-FE-004@1, FE-GATE-001@1, FE-GATE-002@1, FE-GATE-003@1, FE-GATE-004@1, FE-GATE-009@1, FE-GATE-010@1, FE-GATE-011@1, FE-GATE-012@1, FE-GATE-013@1, FE-GATE-020@1, FE-OC-019@1, FE-OC-021@1, FE-OC-025@1]
---
# branch: feature-frontend-test-taxonomy-contract
> Layer: `raw/branch-notes/` — 단일 브랜치의 TODO·결정·진행 기록. 현재는 `/branch-spec` 로 spec 을 채운 `planned` 단계다(frontend 코드 저장소 미생성).
<!-- section-id: branch-parent -->
## 부모 (필수)
- [[raw/project-notes/ca-skeleton-frontend-operational-contract]]
<!-- GENERATED: branch-contract:start -->
<!-- section-id: branch-contract-packet -->
## 브랜치 계약 패킷
- **생성 시 프로젝트 개정**: `1`
- **패킷 스키마**: `contract_packet: 1`
- **완료 조건**: gate·fixture·artifact mapping과 test level별 최소 1개 test가 존재한다
<!-- section-id: inherited-project-decisions -->
### 상속한 프로젝트 결정
| Decision Ref | Project Summary | Branch Application | Source |
|---|---|---|---|
| `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-TEST-STACK-001@1` | test stack default는 Vitest, RTL, MSW, Playwright, axe다 | test level·gate·fixture·artifact taxonomy에 적용한다 | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] |
<!-- section-id: branch-local-decisions -->
### 브랜치 지역 결정
| Decision ID | Decision | Relation | Supporting Claims | Status |
|---|---|---|---|---|
| D1 | frontend test stack default를 고정한다 | `local` | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] | `proposed` |
| D2 | gate를 KIND별 단일 책임으로 분리한다 | `local` | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] | `proposed` |
| D3 | test level별 대표 test와 gate별 negative fixture를 요구한다 | `local` | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] | `proposed` |
| D4 | gate failure를 warning으로 낮추지 않는다 | `local` | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] | `proposed` |
| D5 | 공유 artifacts evidence tree를 사용한다 | `local` | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] | `proposed` |
| D6 | gate별 test level과 fixture KIND taxonomy를 이 branch가 소유한다 (gate-to-contract coverage mapping은 hub §15.1 소유) | `local` | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] | `proposed` |
<!-- section-id: declared-overrides -->
### 선언한 예외
| Override ID | Overrides | Reason | Approval | Status |
|---|---|---|---|---|
없음.
<!-- GENERATED: branch-contract:end -->
<!-- section-id: branch-goal -->
## 목표
이 branch 는 `FE-OC-020`(owner) — "gate 종류별 책임·fixture·artifact 를 분리하고 실패를 warning 으로 낮추면 안 됨" — 을 구현 착수 가능한 spec 으로 내린다. 구체적으로 frontend **quality-gate taxonomy** 를 정의한다: 각 gate 가 어느 test level 에 속하고 어떤 fixture *종류* 를 요구하는지(각 gate 의 blocking scope·Covered FE-OC·pass condition·증거 artifact 는 hub §15.1 소유), "test level 당 대표 test 최소 1개(one-test-per-level)" 수락 규칙, "gate 당 최소 1개의 의도적 실패 negative fixture" 규칙([[raw/project-notes/ca-skeleton-frontend-operational-contract]] §15.2), 그리고 gate 실패를 warning 으로 downgrade 하지 않는다는 불변식(§15.3 promotion formula). 아울러 test stack default(Vitest + RTL + MSW + Playwright + axe — [[raw/project-notes/ca-skeleton-frontend-operational-contract]] FE-D022)를 고정한다. **gate→FE-OC coverage-mapping 의 owner 는 hub §15.1 이고 본 branch 가 아니다** — hub §15.1 이 "이 표가 gate 의 정의다 … branch 는 이 표를 옮겨 적지 않는다" 라고 명시하며, hub §2.1.1 이 gate 26개의 Owner 를 각각 확정한다. 본 branch 가 소유하는 것은 gate → **test level / fixture KIND** taxonomy 다(약 8개 sibling branch 가 자신의 gate artifact 를 이 taxonomy 에 예치). 모든 진술 등급은 `planned` — frontend repository 가 아직 없다.
- 이슈: 없음 (repository 미생성)
- PR: 없음
<!-- section-id: branch-scope -->
## 범위
### 포함 범위
`FE-OC-020` 이 소유하는 것만:
- **Gate KIND 열거 + 스키마**: §15.1 의 26-gate 를 gate → test level → 필요한 fixture *종류* 로 매핑. 각 gate 의 blocking scope · Covered FE-OC · pass condition · 증거 artifact 는 hub §15.1 소유이므로 여기서 재진술하지 않고 gate ID 로 참조한다.
- (제외) gate→FE-OC coverage-mapping 의 owner 는 hub §15.1 이다. 이전 판에서 본 branch 를 SSOT 로 적었던 것은 Single-Owner 위반이었고 2026-07-21 에 hub 로 확정했다.
- **one-test-per-level 수락 규칙** — 각 test level(runtime-schema / unit / component / integration / e2e / a11y)마다 최소 1개 대표 test 로 taxonomy 가 선택한 stack 으로 realizable 함을 증명.
- **negative-fixture-per-gate 규칙**(§15.2) — 각 gate 는 ≥1 의도적 실패 fixture 를 실제 실행; rule 존재만으로는 `locally-verified` 증거 불충분.
- **no-downgrade 불변식 + blocking-scope promotion formula**(§15.3: MERGE_READY / RELEASE_READY / PROD_PROMOTION_READY / FIELD_SLO_READY / DOCUMENTATION_READY).
- **test stack default 도구 배정**([[raw/project-notes/ca-skeleton-frontend-operational-contract]] FE-D022) — 각 level 을 어떤 도구가 실행하는지.
- **공유 `artifacts/` evidence-tree taxonomy**(§14.3 artifact column, §4.6 blueprint) — sibling gate 들이 예치하는 정본 트리.
### 제외 범위
> 의도적으로 제외 — 다른 owner branch 소유. 여기서는 이름만 가리키고 detail 을 재명세하지 않는다(CLAUDE.md §15.5 R3).
- **CI workflow orchestration**(gate job dependency graph, artifact retention wiring, blocking-gate 배선) → [[raw/branch-notes/feature-frontend-ci-quality-gates-contract]] (`FE-OC-020` 공동 기여). 이 branch 는 taxonomy 를 정의하고, CI 가 그것을 어떻게 실행/보관하는지는 저 branch.
- **각 gate 의 fixture 본문(content)** 은 contract owner 에 위임: runtime-schema fixture → [[raw/branch-notes/feature-runtime-schema-validation-contract]] (`FE-OC-007`); error taxonomy fixture → [[raw/branch-notes/feature-frontend-error-classification-boundary-contract]] (`FE-OC-008`); architecture forbidden-import fixture → [[raw/branch-notes/feature-frontend-architecture-enforcement-lint-contract]] (`FE-OC-002`); build/bundle/security fixture → [[raw/branch-notes/feature-frontend-build-bundle-supply-chain-contract]] (`FE-OC-018`); component gate 의 browser-security 슬라이스 fixture → [[raw/branch-notes/feature-frontend-browser-security-boundary-contract]] (`FE-OC-019`); performance threshold → [[raw/branch-notes/feature-web-vitals-performance-budget-contract]] (`FE-OC-021`); sample-removal fixture → [[raw/branch-notes/feature-sample-feature-slice-contract-fixture]] (`FE-OC-024`); runbook drill → [[raw/branch-notes/feature-frontend-operational-runbook-contract]] (`FE-OC-025`).
- **Toolchain / package-script host**(pnpm script, engine, lockfile) → [[raw/branch-notes/feature-frontend-project-bootstrap-toolchain-contract]] (`FE-OC-003`). 이 branch 는 script slot 을 consume 만.
- **NFR 임계값 자체**(timeout 10s, retry ≤2, bundle KiB, axe 0) → 각 NFR contract owner. taxonomy 는 assertion slot 만 hosting.
## 근거 (필수, 최소 1개+)
| Source | 정당화하는 결정 |
|---|---|
| [[raw/official-docs/vite-build-tool-official]] (`VITE-C1`, `VITE-C2`) | D1 배경 근거 — build/test 파이프라인이 Vite 위에 올라감(dev = native ESM 위 기능, prod = Rolldown 정적 자산 산출). 단 특정 test runner(Vitest 등) 선택은 이 문서가 말하지 않음 — 도구 선택 자체는 hub FE-D022 project decision. build gate artifact(§14.3 `pnpm build`)의 정적 자산 산출 근거로만 직접 인용 가능. |
| [[raw/project-notes/ca-skeleton-frontend-operational-contract]] FE-D022 (§15, §14) | D1 test stack default(Vitest+RTL+MSW+Playwright+axe)의 1차 근거. Vite/browser/component/e2e 책임 분리라는 conditional-default. |
| [[raw/project-notes/ca-skeleton-frontend-operational-contract]] §15.1 gate matrix | D2 gate-kind 분리의 근거(26-row acceptance gate registry). gate→FE-OC mapping 은 이 §15.1 이 소유하며 D6 는 그 위에 test level / fixture KIND 층만 얹는다. |
| [[raw/project-notes/ca-skeleton-frontend-operational-contract]] §15.2 negative fixture requirement | D3 one-test-per-level + gate 당 ≥1 negative fixture 근거. |
| [[raw/project-notes/ca-skeleton-frontend-operational-contract]] §15.3 promotion formula | D4 no-downgrade / blocking-in-scope 불변식 근거. |
| [[raw/project-notes/ca-skeleton-frontend-operational-contract]] §14 planned commands + §4 directory blueprint | D5 공유 `artifacts/` evidence-tree taxonomy 근거(script→artifact 매핑, `artifacts/` 트리). |
## TODO
- [ ] repository(`src/`, `tests/`) 생성 후 §15.1 26-gate 를 gate→test level→fixture-kind taxonomy 로 고정(artifact·blocking scope 는 hub §15.1 소유) — 등급: `planned`
- [ ] 각 test level(runtime-schema / unit / component / integration / e2e / a11y)마다 대표 test 최소 1개 작성(one-test-per-level) — 등급: `planned`
- [ ] 각 gate 에 ≥1 의도적 실패 negative fixture 연결(§15.2 카탈로그) 후 "예상대로 실패" 확인 — 등급: `planned`
- [ ] `artifacts/{quality,tests,performance,security,release,runbooks}` evidence-tree + `pnpm test:*` script→artifact 매핑 확정(§14.3) — 등급: `planned`
- [ ] no-downgrade 불변식 + promotion formula(§15.3)를 반영한 gate 상태 판정 규칙 정의 — 등급: `planned`
- [ ] 구현 repository 와 검증 evidence 식별 — 등급: `needs-confirmation`
## 진행 중 메모
- `/branch-spec` self-map 완료(2026-07-19): hub §15 gate matrix + §14.3 planned commands + §8.5 negative fixtures + FE-D022 가 이 branch 의 SSOT. 6개 official-doc source 중 testing-tool 을 직접 말하는 claim 은 없음 → 도구 선택 근거는 hub project decision, Vite 문서는 파이프라인 배경으로만 인용. 외부 web research 불필요(모든 결정 hub-grounded). frontend 코드 부재 → 전부 `planned`.
## 결정 사항
> 각 결정의 근거는 아래 Sources 및 Decision Evidence Map 참조.
- 2026-07-19: **test stack default = Vitest + RTL + MSW + Playwright + axe** / 이유: Vite 위 build/test 파이프라인 통합 + unit/component/integration/e2e/a11y 책임 분리 / 검토한 대안: Jest + Cypress, 조직 test platform / 근거: [[raw/project-notes/ca-skeleton-frontend-operational-contract]] FE-D022 (conditional-default).
- 2026-07-19: **gate 는 KIND별 단일 책임으로 분리**하고 blocking scope(merge / release / prod-promotion / field / documentation)를 명시; 통합 test job 으로 합치지 않음 / 검토한 대안: 단일 test 스텝 / 근거: §15.1 26-gate matrix.
- 2026-07-19: **one-test-per-level + gate 당 ≥1 negative fixture** 불변식; rule 존재만으로는 evidence 불충분 / 근거: §15.2 + §20 measurable completion.
- 2026-07-19: **gate 실패를 warning 으로 낮추지 않음**(scope 내 전부 blocking), promotion 은 §15.3 formula 준수 / 근거: `FE-OC-020` normative summary + §15.3.
- 2026-07-19: **공유 `artifacts/` evidence-tree taxonomy**; sibling gate 는 자체 트리를 만들지 않고 여기에 machine-readable artifact 예치 / 근거: §14.3 + §4.6.
- 2026-07-19: ~~§15.1 gate→FE-OC coverage-mapping 표의 single owner(SSOT)~~**2026-07-21 철회**: 그 매핑의 owner 는 hub §15.1 이다(§15.1 서두 "이 표가 gate 의 정의다 … branch 는 이 표를 옮겨 적지 않는다"). 본 branch 가 SSOT 를 자처한 것은 Single-Owner 위반이었다. 남는 결정: 본 branch 는 gate → **test level / fixture KIND** taxonomy 를 소유하고 sibling 은 그 taxonomy 를 복제·재정의하지 않는다.
## 결정-근거 매핑
| Decision ID | Decision | 선택 조건 (언제 이 결정 / 언제 대안) | Supporting Claims | Evidence Strength | Open Risk |
|---|---|---|---|---|---|
| D1 | test stack default = Vitest + RTL + MSW + Playwright + axe (`FE-OC-020` / hub FE-D022) | 이 조건: Vite 기반 client-only SPA + React + 자체 CI. 대안 전환: 조직 표준 test platform 이 다른 runner(Jest/Cypress 등)를 강제하거나 CI 가 이 스택 미지원 시 runner 교체 | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] FE-D022; `raw/official-docs/vite-build-tool-official.md#VITE-C1`, `#VITE-C2` (파이프라인 배경) | `conditional-default` (도구 선택은 project decision; 외부 doc 는 Vite 배경만 제공, Vitest 를 직접 말하지 않음) | 5개 도구가 6개 test level 을 gap 없이 커버하는지 미검증; DOM 환경(jsdom vs happy-dom) 미확정 |
| D2 | gate 를 KIND별 단일 책임으로 분리하고 blocking scope(merge/release/prod-promotion/field/documentation) 명시; 통합 job 금지 (`FE-OC-020`) | 이 조건: gate 들이 서로 다른 fixture/artifact/blocking scope 를 가질 때(§15.1 26-row 전부). 대안: 새 gate 가 기존 KIND 책임과 1:1 이면 별도 gate 가 아니라 그 row 의 superseding clarification 으로 병합 | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] §15.1 (26-gate matrix); `FE-OC-020` normative summary | `project-decision` | 26개 gate 가 실제 CI 에서 독립 실행 가능한지, 중복 없이 FE-OC 를 완전 분해하는지 미검증 |
| D3 | one-test-per-level + gate 당 ≥1 의도적 실패 negative fixture; rule 존재만으로는 evidence 불충분 (`FE-OC-020`) | 불변식(분기 N/A) — gate 가 실제로 위반을 잡는다고 말하려면 negative fixture 가 실행돼야 하고(§15.2), level 이 realizable 하려면 대표 test 1개가 필요하므로 항상 요구 | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] §15.2 (negative fixture requirement); §20 measurable completion | `project-decision` (invariant) | negative fixture 가 "예상대로 실패" 하는지는 repository 생성 후에만 검증 가능 |
| D4 | gate 실패를 warning 으로 낮추지 않음; 선언된 scope 내 모든 gate 는 blocking, promotion 은 §15.3 formula 준수 (`FE-OC-020`) | 불변식(분기 N/A) — MERGE_READY / RELEASE_READY / PROD_PROMOTION_READY / FIELD_SLO_READY / DOCUMENTATION_READY 각 단계는 지정 gate PASS 없이 통과 불가로 고정되어 downgrade 여지가 없음 | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] FE-OC-020 normative summary; §15.3 promotion formula | `accepted-documented-only` (invariant) | CI wiring 이 실제로 downgrade 를 막는지는 CI 계약 구현 후에 검증(위임 대상은 §Edge·Dependency 참조) |
| D5 | 공유 `artifacts/` evidence-tree taxonomy(quality/tests/performance/security/release/runbooks); sibling gate 는 자체 트리 없이 여기에 machine-readable artifact 예치 (`FE-OC-020`, contributes `FE-OC-021`/`FE-OC-025`) | 이 조건: gate 가 CI 에서 재사용 가능한 evidence 를 남겨야 할 때. 대안: script rename 은 허용하되 gate+artifact 매핑을 동시 갱신해야 함(§14.3 말미) — 매핑 갱신 없는 rename 금지 | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] §14 (planned artifact column); §4 (`artifacts/` blueprint) | `project-decision` | artifact 포맷(JUnit XML / SARIF / JSON)이 실제 CI reporter 와 호환되는지 미검증 |
| D6 | 이 branch 는 gate→**test level / fixture KIND** taxonomy 의 owner 다. gate→FE-OC coverage-mapping 과 gate 정의(blocking scope·pass condition·artifact)의 owner 는 hub §15.1 이고, gate 별 Owner 는 hub §2.1.1 이 확정한다 (`FE-OC-020`) | 이 조건: 다수 sibling 이 test artifact 를 이 taxonomy 에 위임할 때(§20 contributes-to 8개 FE-OC). gate 추가/supersede 는 hub §15.1·§2.1.1 소관이며 본 branch 는 test-level 슬롯만 따라 갱신 | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] §15.1 서두("branch 는 이 표를 옮겨 적지 않는다"); §2.1.1 gate registry; §20 (contributes-to 매핑) | `project-decision` | 매핑이 모든 FE-OC 의 test evidence 를 빠짐없이 덮는지는 coverage-auditor 가 별도 판정. 2026-07-21 정정 — 이전 판이 본 branch 를 coverage-mapping SSOT 로 적어 hub 와 Single-Owner 충돌이었다 |
## 구현 가이드
> 전 항목 `planned` — frontend repository 미생성. 경로/스크립트는 hub §14.3(planned commands)·§15.1(gate matrix)·§4.6(directory blueprint)에서 도출된 blueprint 이며 repo 생성 시 변경 가능.
### 1. Gate → test level / fixture-kind 매핑 (taxonomy core)
> **Trace**: D2 + D3 + D5 + D6 · [[raw/project-notes/ca-skeleton-frontend-operational-contract]] §15.1 / §14.3 / `FE-OC-020`
>
> - **UNSUPPORTED_IMPL_DECISION**: 없음 — 아래 표에서 본 branch 가 정하는 것은 `Test level / KIND` 열뿐이다. 나머지 열(blocking scope · Covered FE-OC · 필요 fixture 본문 · 증거 artifact · pass condition)은 hub §15.1 소유이며 옮겨 적지 않는다(§15.1: "branch 는 이 표를 옮겨 적지 않는다").
아래는 **gate → test level** taxonomy 다. 이전 판은 hub §15.1 의 blocking scope·fixture·artifact 열까지 복제했는데, 그 사본이 실제로 낡아 있었다(`FE-GATE-013``dependency-review` fixture 누락, `FE-GATE-008``repeated guarded-route` 한정어 소실). 그래서 정의 열은 전부 걷어내고 gate ID 참조만 남긴다.
| Gate ([[raw/project-notes/ca-skeleton-frontend-operational-contract]] §15.1) | Test level / KIND (본 branch 소유) | Fixture 본문 owner |
|---|---|---|
| `FE-GATE-001` | manifest/lockfile | bootstrap-toolchain |
| `FE-GATE-002` | lint | architecture-enforcement |
| `FE-GATE-003` | typecheck-equivalent | bootstrap-toolchain + runtime-schema |
| `FE-GATE-004` | runtime-schema | runtime-schema-validation |
| `FE-GATE-005` | unit | api-client / boundary-mapper / contract-registry |
| `FE-GATE-006` | component | async-ui-state / render-recovery / browser-security(`FE-OC-019` 슬라이스) |
| `FE-GATE-007` | integration | api-client / error-classification / auth-session |
| `FE-GATE-008` | e2e | routing / auth-session / release-cache |
| `FE-GATE-009` | accessibility | accessibility-baseline |
| `FE-GATE-010` | architecture | architecture-enforcement |
| `FE-GATE-011` | build | build-bundle |
| `FE-GATE-012` | bundle | build-bundle / web-vitals |
| `FE-GATE-013` | security | build-bundle / browser-security |
| `FE-GATE-020` | sample-removal | sample-feature-slice |
나머지 gate — `FE-GATE-014..019`, `FE-GATE-021..026` — 도 hub §15.1 에 같은 형태로 등재돼 있고 fixture 본문·artifact 는 각 contract owner(release-cache / contract-compatibility / operational-runbook / web-vitals; `FE-GATE-019` 의 security-header 축 정책은 browser-security 공급) 소유다. 이 branch 는 그 row 들의 test-level 슬롯만 관리한다(D6).
### 2. Test-stack 도구 배정 per level
> **Trace**: D1 · [[raw/project-notes/ca-skeleton-frontend-operational-contract]] FE-D022 / §14.3
>
> - **UNSUPPORTED_IMPL_DECISION**: (a) DOM 환경 = jsdom 을 default 로 제안 — hub 는 Vitest+RTL 만 규정하고 환경을 명시하지 않음(trade-off: happy-dom 이 더 빠르나 Web API 커버리지 낮아 boundary/error 테스트 신뢰도 저하 위험). (b) integration 을 Vitest+jsdom+MSW 로 실행 — hub §14.3 는 `test:integration`=MSW matrix 만 말하고 runner 를 명시하지 않음(추론; 대안은 Playwright request-mocking).
| Test level | 도구 | 실행 환경 | 비고 |
|---|---|---|---|
| runtime-schema | Vitest | node/jsdom | zod fixture 가 invalid 입력을 기대 kind 로 reject |
| unit | Vitest | node | retry fake-clock, mapper, registry 순수 로직(§15.1 `FE-GATE-005`) |
| component | Vitest + RTL | jsdom | async/success/empty/terminal-error state, render boundary, keyboard |
| integration | Vitest + MSW | jsdom | API status/failure/auth-recovery taxonomy (UNSUPPORTED: runner 추론) |
| e2e | Playwright | Chromium/Firefox/WebKit | boot/route/mutation/chunk-mismatch/redirect-pair(§14.1 `FE-NFR-C02`) |
| a11y | axe | Playwright 또는 component | critical/serious 0(`FE-NFR-009`) + manual checklist |
### 3. Blocking scope + no-downgrade promotion 집행
> **Trace**: D4 · [[raw/project-notes/ca-skeleton-frontend-operational-contract]] §15.3 / `FE-OC-020`
>
> - **UNSUPPORTED_IMPL_DECISION**: 없음 — promotion formula 가 §15.3 verbatim 이라는 것은 곧 **owner 가 §15.3** 이라는 뜻이므로 tier→gate 집합을 복제하지 않는다.
taxonomy 가 강제하는 불변식:
- 각 gate 는 정확히 하나의 blocking scope 를 가지며(§15.1 Blocking scope 열), 실패 시 그 scope 를 blocking 한다. **warning/soft-fail/`continue-on-error` 로 낮출 수 없다**(`FE-OC-020`).
- promotion 은 tier→gate 집합으로 고정된다. 그 **집합의 owner 는 [[raw/project-notes/ca-skeleton-frontend-operational-contract]] §15.3** 이며 여기에 옮겨 적지 않는다 — hub 가 gate 를 추가·supersede 하면 복제본만 낡는다. tier 는 `MERGE_READY``RELEASE_READY``PROD_PROMOTION_READY``FIELD_SLO_READY` 의 누적 순서이고 `DOCUMENTATION_READY` 는 그와 직교한다.
- CI 에서 이 tier 배선을 실제로 실행/강제하는 것은 **out of scope** → [[raw/branch-notes/feature-frontend-ci-quality-gates-contract]] (`FE-OC-020` 공동 기여). 이 branch 는 no-downgrade 불변식만 소유하고, tier→gate 집합 자체는 hub §15.3 소유다(R3).
### 4. Negative-fixture 요구(taxonomy 레벨)
> **Trace**: D3 · [[raw/project-notes/ca-skeleton-frontend-operational-contract]] §15.2 / `FE-OC-020`
>
> - **UNSUPPORTED_IMPL_DECISION**: 없음 — §15.2 카탈로그 참조. 각 fixture "본문" 은 owner branch 소유(R3).
규칙: 모든 gate 는 최소 1개의 **의도적으로 실패하는** fixture 를 실행해야 한다. rule 존재만 확인한 결과는 `locally-verified` 증거로 불충분(§15.2 말미). 대표 카탈로그(본문은 owner):
| Gate | Negative fixture 예 (§15.2) |
|---|---|
| architecture | `presentation``adapters/http` 를 import |
| checkJs | application port 를 잘못된 shape 로 호출 |
| runtime schema | `data` 없는 success envelope |
| retry | idempotency key 없는 POST 가 503 수신 |
| storage | token key 등록 시도 |
| telemetry | event 에 raw URL/query 포함 |
| release | HTML build A + asset manifest B |
| reload guard | 같은 release pair 에서 2번째 chunk 실패 |
| lab performance | context metadata 누락 또는 named threshold 초과 |
failure 로 정규화되는 경계 fixture(§8.5)도 integration/runtime-schema gate 의 negative fixture 로 재사용: `CONTENT_TYPE_MISMATCH`, `AUTH_INTEGRATION_FAILURE`, `RELEASE_MANIFEST_FAILURE`, `QUERY_CACHE_FAILURE`, `UNKNOWN_CLIENT_FAILURE`, `UNKNOWN_FAILURE` — 단 기대 kind 정의는 error-classification owner 소유.
### 5. 공유 `artifacts/` evidence-tree + one-test-per-level bootstrap
> **Trace**: D5 + D3 · [[raw/project-notes/ca-skeleton-frontend-operational-contract]] §14.3 / §4.6
>
> - **UNSUPPORTED_IMPL_DECISION**: artifact 포맷(test:* → JUnit XML, security → SARIF, performance/release → JSON)은 §14.3 가 확장자(.xml/.sarif/.json)만 규정 → 구체 스키마는 reporter 선택 시 결정(trade-off: JUnit XML 은 CI 호환 넓으나 표현력 낮음).
evidence-tree(§4.6 `artifacts/` + §14.3 artifact 열):
```text
artifacts/
quality/ install.txt · lint.txt · check-types.txt
tests/ runtime-schema.xml · unit.xml · component.xml · integration.xml · a11y.json · sample-removal.xml · e2e/
performance/ bundle.json · lab.json · field-web-vitals.json
security/ scan.sarif
release/ build-manifest.json · verification.json · hosting-headers.json
runbooks/ FE-RB-00N/<release-id>/record.json
```
**one-test-per-level bootstrap**(이 branch 가 직접 인도, sibling 의 full suite 와 구분): 각 level 에서 taxonomy 가 realizable 함을 증명하는 최소 대표 test 1개 —
- runtime-schema: 1개 valid + 1개 invalid envelope → 기대 결과 확인
- unit: fake-clock retry 1개(≤2 backoff)
- component: async state 4종(initial/success/empty/terminal-error) 1개 컴포넌트
- integration: MSW 로 1개 실패 status → normalized kind 1개
- e2e: boot → 1개 route 진입 smoke 1개
- a11y: 1개 sample route axe critical/serious 0
각 script 는 §14.3 `pnpm test:*` slot 에 매핑되고 위 artifact 경로로 결과를 남긴다. script rename 은 gate+artifact 매핑 동시 갱신 조건으로만 허용(§14.3, D5).
## 엣지·실패·의존
- **실패·엣지 경로**:
- gate 에 negative fixture 없이 rule 존재만 확인 → §15.2 위반, `locally-verified` 불충분(기대: taxonomy 가 그 gate 를 "unverified" 로 표시, promotion 미충족).
- gate 실패가 warning 으로 downgrade → `FE-OC-020` 위반(기대: promotion formula 가 해당 tier 를 NOT_READY 로 유지).
- 어떤 test level 에 대표 test 0개 → one-test-per-level 미충족(기대: taxonomy 불완전으로 merge 차단).
- script rename 시 gate+artifact 매핑 미갱신 → §14.3 위반(기대: drift check 가 매핑 불일치 검출).
- flaky e2e/perf gate → deterministic fixture(fake clock, recorded context metadata §14.1)로 강제; 비결정성은 gate 신뢰도 훼손이므로 taxonomy 는 결정적 fixture 를 요구.
- **다른 계약 의존**:
- [[raw/branch-notes/feature-frontend-project-bootstrap-toolchain-contract]] (`FE-OC-003`) — pnpm script host / engine 없이는 `pnpm test:*` 를 실행할 수 없음(§20 dependency). 그 계약의 script 명이 바뀌면 이 taxonomy 의 script→artifact 매핑도 갱신 필요.
- [[raw/branch-notes/feature-frontend-ci-quality-gates-contract]] — CI orchestration/retention 이 이 taxonomy 를 consume; 그쪽 wiring 이 blocking 집행에 영향(`FE-OC-020` 공동).
- fixture-content 의존(test level 슬롯은 본 branch, 본문은 owner): runtime-schema `FE-OC-007` · error `FE-OC-008` · architecture `FE-OC-002` · build/bundle/security `FE-OC-018` · browser-security 슬라이스 `FE-OC-019` · performance `FE-OC-021` · sample-removal `FE-OC-024` · runbook `FE-OC-025`. 각 owner 의 fixture kind 가 바뀌면 본 branch 의 §1 taxonomy 표(D6)를 갱신한다 — hub §15.1 표는 hub 소유이므로 건드리지 않는다.
## 검증해야 할 주장
| Claim | Why uncertain | How to verify | Status |
|---|---|---|---|
| Vitest+RTL+MSW+Playwright+axe 가 6개 test level 을 gap 없이 커버 | repository 부재, 도구 조합 미실행 | repo 생성 후 level 별 대표 test(`pnpm test:runtime-schema/unit/component/integration/e2e/a11y`) 실행(§14.3) | `needs-confirmation` |
| 각 gate 의 negative fixture 가 "예상대로 실패" | rule 존재만으로 불충분(§15.2) | §15.2 카탈로그 fixture 를 실행해 기대 kind 로 실패하는지 확인 | `needs-confirmation` |
| gate 실패가 CI 에서 warning 으로 downgrade 되지 않음 | CI wiring 미구현(위임 대상) | CI 계약 구현 후 promotion formula(§15.3) 위반 시 tier NOT_READY 확인 | `planned` |
| 26-gate 매핑이 모든 FE-OC 의 test evidence 를 완전 분해 | 매핑 완전성 미검증 | coverage-auditor + §15.1 Covered-FE-OC 대조 | `needs-confirmation` |
| boot config ≤500ms / retry ≤2 / axe 0 등 NFR 임계 slot | 값 owner 는 sibling, taxonomy 는 slot 만 hosting | 각 gate 가 해당 NFR assertion 을 실행(§14.2 target + §14.3 command) | `planned` |
## 관심사 커버리지 (coverage-auditor 자동 생성 — 있을 때)
| 관심사 | 상태 | owner | 심각도 | 근거 |
|---|---|---|---|---|
## 마주친 문제
- 없음 — 구현 착수 전(`planned`).
## 묶음 (이 branch에서 파생된 자료)
<!-- GENERATED: artifact-imports:start -->
### 가져온 artifact 계약
| Artifact Ref | Owner | Producer | Schema Ref |
|---|---|---|---|
| `ART-FE-001@1` | [[raw/branch-notes/feature-frontend-project-bootstrap-toolchain-contract]] | [[raw/branch-notes/feature-frontend-project-bootstrap-toolchain-contract]] | `harness/source/artifact-schemas/ca-skeleton-frontend/build-manifest.schema.json` |
| `ART-FE-002@1` | [[raw/branch-notes/feature-frontend-build-bundle-supply-chain-contract]] | [[raw/branch-notes/feature-frontend-build-bundle-supply-chain-contract]] | `harness/source/artifact-schemas/ca-skeleton-frontend/bundle-report.schema.json` |
| `ART-FE-004@1` | [[raw/branch-notes/feature-accessibility-baseline-contract]] | [[raw/branch-notes/feature-accessibility-baseline-contract]] | `harness/source/artifact-schemas/ca-skeleton-frontend/a11y-report.schema.json` |
<!-- GENERATED: artifact-imports:end -->
<!-- GENERATED: project-contract-imports:start -->
## 가져온 프로젝트 계약
| Ref | Owner | 요약 | Branch 적용 |
|---|---|---|---|
| `FE-GATE-001@1` | [[raw/branch-notes/feature-frontend-project-bootstrap-toolchain-contract]] | lockfile 이 manifest 와 어긋나면 merge·release 를 MUST 차단 | import 참조로 적용 |
| `FE-GATE-002@1` | [[raw/branch-notes/feature-frontend-architecture-enforcement-lint-contract]] | 금지된 API·import 가 남아 있으면 merge 를 MUST 차단 | import 참조로 적용 |
| `FE-GATE-003@1` | [[raw/branch-notes/feature-frontend-project-bootstrap-toolchain-contract]] | production diagnostic 이 남아 있으면 merge 를 MUST 차단 | import 참조로 적용 |
| `FE-GATE-004@1` | [[raw/branch-notes/feature-runtime-schema-validation-contract]] | invalid fixture 가 예상 kind 로 거부되지 않으면 merge 를 MUST 차단 | import 참조로 적용 |
| `FE-GATE-009@1` | [[raw/branch-notes/feature-accessibility-baseline-contract]] | automated threshold 미달이거나 manual checklist 서명이 없으면 merge·release 를 MUST 차단 | import 참조로 적용 |
| `FE-GATE-010@1` | [[raw/branch-notes/feature-frontend-architecture-enforcement-lint-contract]] | 금지된 layer import 가 통과하면 merge 를 MUST 차단 | import 참조로 적용 |
| `FE-GATE-011@1` | [[raw/branch-notes/feature-frontend-build-bundle-supply-chain-contract]] | clean production build 가 실패하거나 기대 artifact 가 없으면 merge·release 를 MUST 차단 | import 참조로 적용 |
| `FE-GATE-012@1` | [[raw/branch-notes/feature-frontend-build-bundle-supply-chain-contract]] | 번들 NFR threshold 초과면 release 를 MUST 차단 | import 참조로 적용 |
| `FE-GATE-013@1` | [[raw/branch-notes/feature-frontend-build-bundle-supply-chain-contract]] | secret·vulnerability·license·dependency review 정책 위반이면 merge·release 를 MUST 차단 | import 참조로 적용 |
| `FE-GATE-020@1` | [[raw/branch-notes/feature-sample-feature-slice-contract-fixture]] | sample 제거 후 build·smoke 가 실패하면 merge·release 를 MUST 차단 | import 참조로 적용 |
| `FE-OC-019@1` | [[raw/branch-notes/feature-frontend-browser-security-boundary-contract]] | browser bundle에 secret을 넣지 않고 untrusted HTML injection을 기본 금지 | import 참조로 적용 |
| `FE-OC-021@1` | [[raw/branch-notes/feature-web-vitals-performance-budget-contract]] | NFR은 device/network/cache/build context와 함께 MUST 측정 | 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 -->
### Sub-branches (세부 작업)
- 없음 — 구현 착수 전.
### 오류 기록 (이 branch 작업 중 발생)
- 없음 — 구현 착수 전.
### 면접 준비 (이 작업에서 나올 수 있는 면접 질문)
- 없음 — 구현 착수 전.
### 강의 (이 작업을 위해 학습한 강의)
- 없음 — 구현 착수 전.
### job-posting tie-ins (이 작업에서 파생된 글감)
- 없음 — 구현 착수 전.
## 관련 일일 노트
- 없음 — 구현 착수 전.
## 완료 후 정리
- PR 링크: TODO
- 리뷰 메모: TODO
- 머지 결과 / 배포 환경: TODO
- **wiki 추출 대상**: 없음 — 구현 착수 전(전부 `planned`).
- **추출하지 않을 항목**: 현재 전 항목 `planned` — verified evidence 확보 전까지 추출 금지.