Files
llm-wiki/raw/branch-notes/feature-web-vitals-performance-budget-contract.md
T

24 KiB

title, source_type, status, branch, parent_branch, related_projects, governing_docs, tags, created, target_merge, status_label, id, kind, project, work_item, inherits, refines, overrides, depends_on, contract_packet, contract_packet_sha256, imports
title source_type status branch parent_branch related_projects governing_docs tags created target_merge status_label id kind project work_item inherits refines overrides depends_on contract_packet contract_packet_sha256 imports
branch / feature-web-vitals-performance-budget-contract branch-note raw feature-web-vitals-performance-budget-contract
ca-skeleton-frontend
ca-skeleton
raw/project-notes/ca-skeleton-frontend-operational-contract
branch
ca-skeleton
frontend
observability
react
histogram-quantile
2026-07-18 in-progress BR-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-025 project-work-item ca-skeleton-frontend-operational-contract WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-025
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
1 4579fd193d1c3d1a19d54a732084315a1ae27a23bfdecf664e511eef29e83cbe
ART-FE-002@1
FE-GATE-012@1
FE-OC-014@1
FE-OC-020@1
FE-OC-026@1

branch: feature-web-vitals-performance-budget-contract

Layer: raw/branch-notes/ — TODO·결정·진행 기록. 구현 결과는 검증 뒤 /ingest로만 추출한다.

부모 (필수)

raw/project-notes/ca-skeleton-frontend-operational-contract

브랜치 계약 패킷

  • 생성 시 프로젝트 개정: 1
  • 패킷 스키마: contract_packet: 1
  • 완료 조건: context metadata와 lab·bundle·28-day field report가 생성된다

상속한 프로젝트 결정

Decision Ref Project Summary Branch Application Source
DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-BUILD-001@1 Vite client-only SPA를 build baseline으로 사용한다 Work Item 완료 조건에 적용 raw/project-notes/ca-skeleton-frontend-operational-contract
DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-TEST-STACK-001@1 test stack default는 Vitest, RTL, MSW, Playwright, axe다 Work Item 완료 조건에 적용 raw/project-notes/ca-skeleton-frontend-operational-contract

브랜치 지역 결정

기존 branch-local 결정은 아래 ## Decision Evidence Map / 결정-근거 매핑의 D-row가 소유하며 이 packet에서 복제하지 않는다.

Decision ID Decision Relation Supporting Claims Status

선언한 예외

Override ID Overrides Reason Approval Status

목표

이 branch 는 project-wide 계약 FE-OC-021 ("NFR 은 device/network/cache/build context 와 함께 MUST 측정, 최소 증거 = machine-readable report") 를 구현 착수 가능한 명세 로 내린다. 구체적으로 (1) 측정 context 모델(raw/project-notes/ca-skeleton-frontend-operational-contract §14.1 의 FE-NFR-C01FE-NFR-C04), (2) initial target matrix(bundle FE-NFR-001/FE-NFR-002, lab FE-NFR-003FE-NFR-005, field FE-NFR-013~FE-NFR-015), (3) 세 개의 machine-readable evidence report(bundle.json / lab.json / field-web-vitals.json) 를 정의한다. 이 branch 는 세 performance gate(FE-GATE-012 bundle, FE-GATE-026 lab, FE-GATE-018 field)의 pass-condition 을 정의해 FE-OC-020(test taxonomy) 에 기여하고, release-time bundle/lab gate 를 통해 FE-OC-016(release readiness) 에 기여한다. 현재 frontend 코드는 존재하지 않으므로 모든 구현 항목은 planned 이다.

  • 이슈: 없음 (스캐폴딩 단계)
  • PR: 없음

범위

포함 범위

  • FE-OC-021 measurement-context 계약: 모든 NFR 수치는 4-context(device/runtime · network/cache · route/data · build) metadata 와 함께만 evidence 로 인정 (§14.1).
  • Initial target matrix 정의 + revisit 절차: FE-NFR-001/FE-NFR-002(bundle gzip budget), FE-NFR-003FE-NFR-005(lab), FE-NFR-013FE-NFR-015(field p75).
  • 세 machine-readable report schema: bundle(FE-GATE-012), lab(FE-GATE-026), 28-day field Web Vitals(FE-GATE-018).
  • lab ≠ field 불변식 + negative fixture(context metadata 누락 / named threshold 초과).
  • 세 performance gate 의 pass-condition + required-context 정의.

제외 범위

의도적으로 제외한 것. 각 항목은 owner branch 에 위임한다 (근거 범위 밖 detail 을 여기서 정하지 않음 — CLAUDE.md §15.5 R3).

근거 (필수, 최소 1개+)

Source 정당화하는 결정
raw/official-docs/vite-build-tool-official VITE-C2 "production build 는 Rolldown 으로 코드를 번들링해 최적화된 정적 자산을 산출" — bundle report 가 측정하는 build artifact 의 공식 근거 (D2 bundle, D6 gate).
raw/project-notes/ca-skeleton-frontend-operational-contract §14(FE-NFR-C01C04·FE-NFR-001015) · §14.3(command→artifact) · §15.1(FE-GATE-012/018/026) · §15.2(negative fixture) · FE-OC-021 measurement context 모델·initial target·three-report split·gate pass-condition 의 project SSOT (D1·D2·D5·D6).
web.dev Core Web Vitals (researched 2026-07-19, https://web.dev/articles/vitals) LCP/INP/CLS 정의 + good threshold(2.5s / 200ms / 0.1) + 75th-percentile + lab≠field 구분의 공식 표준 근거 (D3·D4).

TODO

  • measurement-context schema(device/runtime · network/cache · route/data · build) 정의 + 각 report 가 embed 할 metadata 필드 명세 — 등급: planned
  • bundle report schema (artifacts/performance/bundle.json: initial JS gzip, lazy chunk gzip vs FE-NFR-001/FE-NFR-002) — 등급: planned
  • lab report schema (artifacts/performance/lab.json: LCP/CLS/interaction-latency + context metadata vs FE-NFR-003~FE-NFR-005) — 등급: planned
  • 28-day field report schema (artifacts/performance/field-web-vitals.json: p75 LCP/CLS/INP + consent·route-ID·release-ID·eligible-sample metadata vs FE-NFR-013~FE-NFR-015) — 등급: planned
  • 세 performance gate pass-condition + negative fixture(context 누락 / threshold 초과) 명세 — 등급: planned
  • deferred minimum eligible sample threshold 해소 절차 문서화 (telemetry baseline 확보 이후) — 등급: planned

진행 중 메모

  • vitals threshold(LCP 2.5s / CLS 0.10 / INP 200ms, p75)는 Core Web Vitals "good" 값(web.dev). bundle budget(200/120 KiB)은 project-local initial 값이며 FE-RISK-010(threshold 가 실제 device UX 와 무관할 위험)로 첫 측정 후 revisit 대상.
  • 28-day window 는 hub/CrUX convention 이며 web.dev 문서는 28일을 명시하지 않음 → 28-day 는 project decision 으로 grounding.
  • CI runner CPU·throttling profile 미확정(§14.1) → 값을 지금 고정하지 않고 command 실행 시 report metadata 에 기록.

결정 사항

  • 2026-07-19: measurement-context 계약 — 모든 NFR 수치는 4-context 와 함께만 evidence / 이유: context 없는 숫자는 재현·비교 불가 / 대안: 단일 숫자만 기록(reject) / 근거: raw/project-notes/ca-skeleton-frontend-operational-contract §14.1 + FE-OC-021.
  • 2026-07-19: three machine-readable report split(bundle / lab / 28-day field) / 이유: build-repro · synthetic lab · RUM 은 서로 다른 context / 대안: 단일 통합 report / 근거: hub §14.3 + §20 measurable completion.
  • 2026-07-19: lab ≠ field 불변식 — lab 결과를 production percentile 로 표현 금지 / 근거: hub §14.2 note + web.dev(field vs lab).
  • 2026-07-19: initial target = Core Web Vitals good threshold(LCP 2.5s / CLS 0.10 / INP 200ms, p75) + project bundle budget(200/120 KiB) / 대안: device-class 별 커스텀 threshold / 조건: 첫 실측·field data 확보 후 revisit(FE-RISK-010) / 근거: web.dev + hub §14.2.
  • 2026-07-19: 28-day field window + eligibility metadata; minimum eligible sample threshold 는 deferred(telemetry baseline 이후) → FE-GATE-018 은 그 전까지 PASS 불가 / 근거: hub §14.2 note + §14.3 + FE-GATE-018.
  • 2026-07-19: 세 performance gate(FE-GATE-012 bundle / FE-GATE-026 lab / FE-GATE-018 field)에 NFR threshold 값과 negative fixture 를 공급; CI wiring 은 위임 / 근거: hub §15.1 + §15.2. (2026-07-21 정정: gate 의 pass condition 자체는 hub §15.1 소유이고 FE-GATE-012 의 Owner 는 build-bundle 이다 — hub §2.1.1.)

결정-근거 매핑

Supporting Claims[[hub]]raw/project-notes/ca-skeleton-frontend-operational-contract 를 가리킨다. 이 branch 는 FE-OC-021 owner 이며, FE-D* decision row 중 이 slug 를 owner 로 갖는 것은 없다 — 아래 결정은 FE-OC-021 계약 조항과 §14 메커니즘을 branch-local decision(D1~D6)으로 내린 것.

Decision ID Decision 선택 조건 (언제 이 결정 / 언제 대안) Supporting Claims Evidence Strength Open Risk
D1 Context-mandatory measurement: 모든 NFR 수치는 4-context(device/runtime · network/cache · route/data · build) metadata 와 함께만 evidence (FE-OC-021) 항상 적용되는 contract invariant. 구체 context 값(CI runner CPU · throttling)은 §14.1 대로 run time 에 report metadata 로 기록 — 지금 고정 불가. 분기 없음 raw/project-notes/ca-skeleton-frontend-operational-contract §14.1 (FE-NFR-C01~FE-NFR-C04, "context 가 없는 숫자는 evidence 로 인정하지 않는다"), FE-OC-021 project-decision CI runner spec · throttling profile 미확정 → repo/CI 생성 전 실제 context 값 확정 불가 (FE-NFR-C01 note, FE-Q-002/FE-Q-007)
D2 Three machine-readable report split: bundle(bundle.json) · lab(lab.json) · 28-day field(field-web-vitals.json) three-report split 이 default; lab/field 경계를 보존하는 단일 통합 pipeline 이 등장하면 통합 재검토 raw/project-notes/ca-skeleton-frontend-operational-contract §14.3 (command→artifact 표), §20 measurable completion ("context metadata + lab/bundle/28-day field reports"); raw/official-docs/vite-build-tool-official VITE-C2 (bundle 대상 = production build artifact) project-decision + official-doc 세 report 모두 PLANNED_NOT_EXECUTED — schema · collector 미구현
D3 Lab ≠ field 불변식: lab(FE-NFR-C01 synthetic Playwright)을 production percentile 로 표현 금지, field(FE-NFR-C03 RUM p75)와 분리 불변식 — 대안 없음(분리 위반 = reject). 어떤 조건에서도 lab 값을 field SLO 로 승격하지 않음 raw/project-notes/ca-skeleton-frontend-operational-contract §14.2 note ("lab result 를 production percentile 로 표현하지 않는다"), FE-OC-026; web.dev Core Web Vitals (researched: "Only field measurement can accurately capture the complete picture" / "Lab measurement is the best way to test performance ... before they've been released") project-decision + official-standard collector 가 lab/field 를 혼동해 리포트하면 evidence 신뢰 붕괴 → negative fixture 로 강제 필요
D4 Initial target matrix: LCP lab/field ≤ 2.5s, CLS ≤ 0.10, interaction/INP ≤ 200ms(p75), initial JS gzip ≤ 200 KiB, lazy chunk gzip ≤ 120 KiB conditional-default: 프로젝트 초기값. device-class 별 커스텀 threshold 는 첫 실측·field data 가 threshold 의 device-UX 무관성을 보일 때 채택(FE-RISK-010 revisit trigger = "first measurement") web.dev Core Web Vitals (researched: LCP "2.5 seconds", INP "200 milliseconds", CLS "0.1", "75th percentile of page loads"); raw/project-notes/ca-skeleton-frontend-operational-contract §14.2 (bundle budget = project-local initial), FE-RISK-010 official-standard (vitals) + conditional-default (bundle budget) FE-RISK-010 — bundle/threshold 가 실제 device UX 와 무관할 수 있음; 첫 측정 후 evidence 로 revisit
D5 28-day field window + eligibility metadata(consent/privacy boundary · route-ID aggregation · production release ID · eligible sample); minimum eligible sample threshold = deferred 28-day window 는 default; min-sample threshold 는 telemetry baseline 확보 후 owner 가 확정 — 그 전엔 FE-GATE-018 PASS 금지 raw/project-notes/ca-skeleton-frontend-operational-contract §14.2 note, §14.3 (collect:web-vitals-evidence = "28-day context + p75 + eligible sample metadata"), §15.1 FE-GATE-018 (28-day 는 hub/CrUX convention — web.dev 는 28일 미명시) project-decision min-sample threshold deferred → FE-GATE-018 blocked; consent/privacy · sink 는 telemetry branch(FE-OC-014, FE-Q-008)에 의존
D6 세 performance gate 에 NFR threshold 값 + negative fixture 공급: FE-GATE-012@1(bundle — Owner 는 build-bundle), FE-GATE-026@1·FE-GATE-018@1(Owner 는 본 branch). pass condition 원문은 hub §15.1 소유 contract 정의(분기 N/A). 단 CI wiring · blocking scope · artifact retention 은 위임(Open Risk 참조) raw/project-notes/ca-skeleton-frontend-operational-contract §15.1 (gate rows), §15.2 (lab negative fixture = "context metadata missing 또는 one named threshold exceeded") project-decision gate CI wiring/실행은 feature-frontend-ci-quality-gates-contract · feature-frontend-test-taxonomy-contract(FE-OC-020)이 소유 — 본 branch 는 pass-condition 만 정의

구현 가이드

모든 경로(artifacts/performance/*, src/contracts/*)는 hub §4.6 Planned directory blueprint 에서 온 planned anchor 다. frontend 코드가 없으므로 전 항목 planned.

1. Measurement context metadata schema

Trace: D1 (FE-OC-021, hub §14.1). 각 report 는 아래 4-context 를 embed 해야 evidence 로 인정된다.

  • UNSUPPORTED_IMPL_DECISION(lab.json·field-web-vitals.json 한정): 실제 JSON key 이름(context.runner, context.throttling, context.cache 등)은 hub 가 아직 명명하지 않음. 명명 스타일 은 hub §2.1.3 이 정한 camelCase(artifacts/** report 한정)를 따른다 — 이전 판의 snake_case 제안은 그 규약 이전 것이라 폐기한다. bundle.jsonART-FE-002@1 스키마가 이미 확정했으므로 이 항목 대상이 아니다. trade-off: Lighthouse/Playwright reporter 가 자체 schema 를 고정하면 그 형태로 맞춘다. CI runner CPU/throttling 은 미확정이라 여기서 상수화하지 않고 run time 기록(§14.1) 으로 남긴다.
Context ID 무엇을 기록 어느 report 가 embed 근거
FE-NFR-C01 Playwright Chromium, CI runner spec, cold cache, throttling profile lab.json hub §14.1
FE-NFR-C03 production field data, real network, 28-day window, top route IDs field-web-vitals.json hub §14.1
FE-NFR-C04 build runner image + Node/pnpm version bundle.json hub §14.1
  • 규칙(§14.1): context 가 없는 숫자는 evidence 로 인정하지 않는다 → context block 부재 = gate FAIL (negative fixture, §4 참조).

2. Three report artifacts + threshold binding

Trace: D2 (hub §14.3, §20) + D4 (web.dev vitals threshold + hub §14.2 bundle budget). command·artifact·NFR 매핑은 hub §14.3 표의 도출이다.

  • UNSUPPORTED_IMPL_DECISION(lab.json·field-web-vitals.json 한정): 두 report 의 내부 JSON 구조(필드 계층·배열 shape)는 아직 미등록 → threshold pass/fail + context block + metric 값을 담는 flat object 로 제안. bundle.json 은 hub §2.1.3 ART-FE-002@1 스키마가 정본이다. trade-off: downstream gate parser 가 확정되면 그 shape 로 조정.
Report Planned command Planned artifact NFR IDs Threshold (initial)
bundle pnpm check:bundle artifacts/performance/bundle.json FE-NFR-001, FE-NFR-002 initial JS gzip ≤ 200 KiB, lazy chunk gzip ≤ 120 KiB
lab pnpm test:performance artifacts/performance/lab.json FE-NFR-003~FE-NFR-005 LCP ≤ 2.5s, CLS ≤ 0.10, named interaction ≤ 200ms + context metadata
field pnpm collect:web-vitals-evidence artifacts/performance/field-web-vitals.json FE-NFR-013~FE-NFR-015 p75 LCP ≤ 2.5s, CLS ≤ 0.10, INP ≤ 200ms + eligible-sample metadata

3. Field Web Vitals eligibility + deferred threshold

Trace: D5 (hub §14.2 note, §14.3, §15.1 FE-GATE-018). field report 가 반드시 담아야 할 metadata 와 deferred 결정의 처리.

  • UNSUPPORTED_IMPL_DECISION: minimum eligible sample threshold 의 수치deferred(telemetry baseline 확보 전 확정 불가) → 값을 임의로 지어내지 않고 미정으로 둔다. trade-off: 값이 없으면 FE-GATE-018 을 PASS 로 못 올리는 것을 의도적 안전 기본값 으로 수용.
  • OUT_OF_BRANCH_SCOPE: consent/privacy boundary 의 실제 구현·telemetry sink 는 raw/branch-notes/feature-frontend-observability-logging-trace-contract (FE-OC-014) 소유 → 여기서 필드 요구사항 만 열거하고 수집 pipeline 은 명세하지 않음.
  • field report 필수 metadata: consent/privacy boundary flag · route-ID aggregation · 28-day window · production release ID · eligible-sample count.
  • deferred 처리: telemetry baseline 획득 → owner 가 min eligible sample threshold 확정 → 그때까지 FE-GATE-018FAIL_UNVERIFIED 유지(hub §14.2 note).

4. Gate pass-conditions + negative fixtures

Trace: D6 (hub §15.1 gate rows, §15.2 negative fixture) + D3 (lab≠field invariant). 세 gate 의 pass 조건과 "실제로 동작함" 을 보이는 deliberately-failing fixture.

  • UNSUPPORTED_IMPL_DECISION: 없음 — pass 조건·negative fixture 는 hub §15.1/§15.2 에서 직접 도출.

각 gate 의 blocking scope·pass condition 은 hub §15.1 소유다. 본 브랜치가 공급하는 것은 NFR threshold 값과 그 negative fixture 다.

Gate ID 본 브랜치가 공급하는 NFR Negative fixture
FE-GATE-012@1 FE-NFR-001, FE-NFR-002 chunk 가 budget 초과 → FAIL
FE-GATE-026@1 FE-NFR-003~FE-NFR-005 context metadata 누락 또는 하나의 named threshold 초과 → FAIL (§15.2)
FE-GATE-018@1 FE-NFR-013~FE-NFR-015 28-day eligible sample 부족 / min-sample 미해소 → PASS 불가

엣지·실패·의존

검증해야 할 주장

Claim Why uncertain How to verify Status
bundle initial JS gzip ≤ 200 KiB & lazy chunk gzip ≤ 120 KiB build/collector 없음 pnpm check:bundlebundle.json threshold 검사 (FE-GATE-012); budget 초과 negative fixture needs-confirmation
lab LCP/CLS/interaction 이 recorded context 와 함께 threshold 이하 lab runner·throttling profile 미확정 pnpm test:performancelab.json + reproducibility metadata (FE-GATE-026); negative fixture: context 누락/threshold 초과 needs-confirmation
field p75 LCP/CLS/INP 가 28-day eligible sample 에서 threshold 이하 RUM·consent·min-sample threshold 모두 deferred pnpm collect:web-vitals-evidencefield-web-vitals.json (FE-GATE-018) — deferred threshold 해소 전 PASS 불가 needs-confirmation
context 없는 숫자가 gate 에서 reject 된다 강제 로직 없음 lab negative fixture(§15.2): context metadata 제거 시 gate FAIL 확인 planned
lab 결과가 field percentile 로 표현되지 않는다 (D3) 관례상 혼동하기 쉬움 report schema 검사 + answer-boundary 체크(FE-OC-026) planned

관심사 커버리지 (coverage-auditor 자동 생성 — 있을 때)

  • 스캐폴딩 단계: /coverage 실행 전 수동 행을 만들지 않는다.

마주친 문제

  • 없음 — 스캐폴딩 단계.

묶음 (이 branch에서 파생된 자료)

가져온 artifact 계약

Artifact Ref Owner Producer Schema Ref
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

가져온 프로젝트 계약

Ref Owner 요약 Branch 적용
FE-GATE-012@1 raw/branch-notes/feature-frontend-build-bundle-supply-chain-contract 번들 NFR threshold 초과면 release 를 MUST 차단 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-020@1 raw/branch-notes/feature-frontend-test-taxonomy-contract gate 종류별 책임·fixture·artifact를 분리하고 실패를 warning으로 낮추면 안 됨 import 참조로 적용
FE-OC-026@1 raw/project-notes/ca-skeleton-frontend-operational-contract 외부 답변은 evidence grade를 MUST 보존하고 목표 수치를 측정 결과처럼 말하면 안 됨 import 참조로 적용
  • 없음 — 자식 자료는 생성 후 controller가 parent Cluster와 함께 등록한다.

관련 일일 노트

  • 없음 — daily note는 이 작업에서 수정하지 않는다.

완료 후 정리

  • PR 링크: 없음
  • 리뷰 메모: 없음
  • 머지 결과 / 배포 환경: planned
  • wiki 추출 대상 (verified만): 없음
  • 추출하지 않을 항목 (planned / documented-only / abandoned): 현재 전체