Files
llm-wiki/raw/branch-notes/feature-frontend-build-bundle-supply-chain-contract.md
T

42 KiB

title, source_type, status, id, kind, project, work_item, inherits, refines, overrides, depends_on, contract_packet, branch, parent_branch, related_projects, governing_docs, tags, created, target_merge, status_label, contract_packet_sha256, imports
title source_type status id kind project work_item inherits refines overrides depends_on contract_packet branch parent_branch related_projects governing_docs tags created target_merge status_label contract_packet_sha256 imports
branch / feature-frontend-build-bundle-supply-chain-contract branch-note raw BR-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-020 project-work-item ca-skeleton-frontend-operational-contract WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-020
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
1 feature-frontend-build-bundle-supply-chain-contract
ca-skeleton-frontend
ca-skeleton
raw/project-notes/ca-skeleton-frontend-operational-contract
branch
ca-skeleton
frontend
ci-cd
build-tooling
supply-chain
2026-07-18 in-progress 3dccf22904aa14c909b778cb3242f70be7a11dce4256b8fdaed40f5a1ad36035
ART-FE-001@1
FE-GATE-001@1
FE-OC-003@1
FE-OC-016@1
FE-OC-019@1
FE-OC-020@1
FE-OC-021@1

branch: feature-frontend-build-bundle-supply-chain-contract

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

부모 (필수)

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

브랜치 계약 패킷

  • 생성 시 프로젝트 개정: 1
  • 패킷 스키마: contract_packet: 1
  • 완료 조건: frozen build·inventory·scan·bundle report가 CI artifact로 생성된다

상속한 프로젝트 결정

Decision Ref Project Summary Branch Application Source
DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-BUILD-001@1 Vite client-only SPA를 build baseline으로 사용한다 clean production build와 bundle report gate에 적용 raw/project-notes/ca-skeleton-frontend-operational-contract
DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-SUPPLY-CHAIN-001@1 dependency lock, secret scan, vulnerability scan, license inventory, dependency review를 merge/release gate로 분리한다 install·security·inventory·dependency review gate에 적용 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

목표

이 브랜치는 project-wide 계약 FE-OC-018 (frozen lockfile · dependency review · secret scan · SBOM/dependency inventory 를 release gate 에 MUST 포함) 를 되묻지 않고 구현 착수 가능한 명세로 내린다. 근거 결정은 hub 의 raw/project-notes/ca-skeleton-frontend-operational-contract FE-D024 (supply-chain control 을 분리된 merge/release gate 로 운영 — control 열거는 hub 소유) 이며, hub §13.1 (supply-chain minimums), §12.1 (release artifact set), §14.3 (planned commands), §15.1 의 FE-GATE-001/FE-GATE-011/FE-GATE-012/FE-GATE-013 를 구현 blueprint 로 삼는다. 부수적으로 FE-OC-003 (frozen install), FE-OC-016 (release artifact), FE-OC-019 (secret-in-bundle 경계), FE-OC-020 (gate 분리), FE-OC-021 (bundle NFR) 에 기여한다. 현 시점 frontend 코드/CI 는 존재하지 않으므로 아래 모든 항목은 planned 등급이다 — "구현했다" 가 아니라 "이렇게 구현될 것이다" 의 사전 명세다.

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

범위

포함 범위

  • frozen-lockfile install gate — lockfile drift 없이 재현 가능한 install 을 merge+release 차단 gate 로 강제 (FE-GATE-001, FE-OC-018).
  • clean production build gate — hashed immutable static asset + build-manifest 산출을 merge+release 차단 gate 로 강제 (FE-GATE-011, FE-OC-018).
  • bundle report gate — release 시 app + lazy chunk 크기를 machine-readable report 로 산출 (FE-GATE-012, FE-OC-018). (수치 threshold 자체는 아래 Out of scope.)
  • security gate — secret scan · vulnerability scan · license inventory · dependency review 를 하나의 차단 gate 로 묶어 SARIF/inventory/dependency-diff 산출 (FE-GATE-013, FE-OC-018).
  • dependency review (dependency diff) — base↔head lockfile 을 direct + transitive 까지 diff 해 변경 집합을 산출하고, review 기록 없는 high-risk change 를 차단 gate 로 처리 (hub §13.1 dependency review row, FE-OC-018). 본 브랜치가 FE-OC-018 소유자이며 sibling raw/branch-notes/feature-frontend-project-bootstrap-toolchain-contract 가 이 관심사를 본 브랜치로 명시 위임했다.
  • release supply-chain artifact set — dependency inventory · build-manifest(provenance metadata) · checksums 를 release artifact 로 명세 (hub §12.1, §13.1).
  • vulnerability suppression policy — reason·owner·expiry·affected package·compensating control 을 강제하고 expiry 경과 suppression 을 gate failure 로 처리 (hub §13.3).

제외 범위

의도적으로 제외 — 다른 owner 브랜치의 결정 영역. "이건 범위에 없었습니다" 근거.

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

Source 정당화하는 결정
raw/official-docs/supply-chain-slsa-provenance-framework SLSA-FW-C1/C4/C5 — provenance = build platform/process/top-level input 을 기술하는 verifiable 정보. release build-manifest(buildId/commit) 를 provenance 최소선으로 두는 D7 의 공식 근거. signed attestation(L2+) 은 미채택 표지.
raw/official-docs/vite-build-tool-official VITE-C2 — production build 가 optimized 정적 자산을 산출 (D3 clean build gate 근거). VITE-C3/C4/C5import.meta.env build-time 정적 치환 + VITE_ prefix 만 클라이언트 노출 + 비밀값 금지 (D6 built-asset secret scan 경계 근거).

나머지 세부 (gate 분리·§13.1 control·§12.1 artifact·suppression policy) 의 근거는 외부 문서가 아니라 hub 자체의 project decision 이므로 Evidence Map 에서 raw/project-notes/ca-skeleton-frontend-operational-contract FE-D024 로 인용한다 (hub §3.2 가 accepted-documented-only 로 명시).

TODO

  • frozen-lockfile install gate (FE-GATE-001) 명세 — command·drift 검출·install log artifact — 등급: planned
  • clean production build gate (FE-GATE-011) 명세 — hashed asset + build-manifest 산출 — 등급: planned
  • bundle report gate (FE-GATE-012) 명세 — machine-readable bundle report (threshold 판정은 FE-OC-021 위임) — 등급: planned
  • security gate (FE-GATE-013) 명세 — secret/vuln/license/dependency-review fixture + SARIF/inventory/dependency-diff — 등급: planned
  • dependency review 명세 — base↔head lockfile direct+transitive diff · high-risk 분류축 · review 기록 · dependency diff report — 등급: planned
  • release supply-chain artifact set (dependency inventory·checksums·build-manifest) + provenance metadata 정의 — 등급: planned
  • vulnerability suppression policy (reason·owner·expiry·affected package·compensating control) 정의 — 등급: planned
  • scanner/SBOM/threshold deferred 항목의 revisit trigger (organization security policy) 문서화 — 등급: planned

진행 중 메모

  • scanner 도구명·severity threshold·SBOM 형식(CycloneDX/SPDX)·suppression expiry SLA 는 hub §13.1/§13.3 이 deferred 로 명시 — 특정 도구를 썼다고 주장하지 않는다.
  • 모든 command(pnpm install --frozen-lockfile·pnpm build·pnpm check:bundle·pnpm scan:security)와 artifact 경로는 hub §14.3/§12.1 의 planned contract 이며 실행/검증되지 않았다 (PLANNED_NOT_EXECUTED).
  • hub 내부 불일치 발견 → 해소 완료: hub §2.1 의 FE-OC-018 과 §13.1 은 dependency review 를 요구하는데 §3.2 FE-D024 본문과 §15.1 FE-GATE-013 required fixtures 는 그것을 누락하고 있었다. 본 브랜치가 상위 계약을 따라 D9 로 편입했고, hub 도 정정됐다 — 현재 FE-D024 는 dependency review 를 포함해 열거하고(lock 은 FE-GATE-001, 나머지 4개는 FE-GATE-013), §15.1 FE-GATE-013 required fixtures 도 secret/vulnerability/license/dependency-review 다.

결정 사항

근거는 Sources 또는 hub project decision 을 가리킨다. FE-D### 인용은 hub 경로에 붙인다 (Evidence Map 과 mirror).

  • 2026-07-18: supply-chain control 을 하나의 monolithic gate 가 아니라 dependency lock/secret/vuln/license 로 분리된 merge/release gate 로 운영 / 이유: 실패 지점을 구분해 blocking scope 를 정확히 하기 위함 / 검토한 대안: 단일 "security gate" 통합 / 근거: raw/project-notes/ca-skeleton-frontend-operational-contract FE-D024 (D1).
  • 2026-07-18: frozen-lockfile install 을 merge+release 차단 gate 로 강제, drift = FAIL / 이유: 재현 가능한 install / 검토한 대안: 비-frozen install 후 사후 검증 / 근거: hub §13.1 install row + FE-GATE-001 (D2).
  • 2026-07-18: production build gate 는 hashed immutable asset + build-manifest 산출 / 이유: 정적 호스팅 배포 + release 식별 / 검토한 대안: unhashed asset / 근거: raw/official-docs/vite-build-tool-official.md#VITE-C2 + hub §12.1 (D3).
  • 2026-07-18: bundle report 는 생성하되 수치 threshold 판정은 FE-OC-021 에 위임 / 이유: NFR context/threshold 소유권 분리 / 근거: hub §14.2 (FE-NFR-001/FE-NFR-002) + FE-GATE-012 (D4).
  • 2026-07-18: security gate 는 secret+vuln+license 를 묶고 scanner/threshold 는 deferred / 이유: org policy 부재로 도구 확정이 불가 / 검토한 대안: 지금 특정 scanner 확정 / 근거: hub §13.1 (D5).
  • 2026-07-18: secret scan 은 source 뿐 아니라 built asset 까지 검사 / 이유: browser bundle 은 public artifact 이고 VITE_ 값은 build-time 에 정적 inline 되므로 / 근거: raw/official-docs/vite-build-tool-official.md#VITE-C4, #VITE-C5 + hub §13.2 (D6).
  • 2026-07-18: release 는 dependency inventory + build-manifest(buildId/commit provenance) + checksums 를 포함, mismatch = 차단; signed SLSA attestation 은 미채택 / 이유: provenance 최소선 확보 / 근거: raw/official-docs/supply-chain-slsa-provenance-framework.md#SLSA-FW-C1, #SLSA-FW-C4 + hub §12.1 (D7).
  • 2026-07-18: vulnerability suppression 은 reason·owner·expiry·affected package·compensating control 을 요구, expiry 경과 = gate failure / 이유: 무기한 예외 방지 / 근거: hub §13.3 (D8).
  • 2026-07-20: dependency review 를 FE-GATE-013네 번째 control 로 편입 (별도 gate ID 신설 대신 기존 gate 범위 확장). base↔head lockfile 을 direct+transitive 까지 diff 하고, review 기록 없는 high-risk change = 차단, 산출물은 dependency diff report / 이유: hub §2.1 FE-OC-018 과 §13.1 이 dependency review 를 요구하는데 소유 gate 가 없었다. 새 FE-GATE-027 을 만들면 hub §15.1 의 "26개 row" registry 와 §15.3 promotion formula 를 동시에 고쳐야 하는데 그건 hub 소유 변경이라 본 브랜치 권한 밖이다. FE-GATE-013 은 이미 FE-OC-018 을 covered 하고 blocking scope 도 merge+release 로 dependency review 요구와 일치한다 / 검토한 대안: (a) 신규 gate ID 신설 — hub registry 변경 필요로 기각, (b) FE-GATE-001(lockfile) 에 합류 — 그쪽은 drift 유무만 보는 결정론 검사라 "변경 내용의 위험도 심사"라는 성격이 다르고 실패 의미가 섞임 / 근거: raw/project-notes/ca-skeleton-frontend-operational-contract FE-D024 + hub §2.1 FE-OC-018 + hub §13.1 dependency review row (D9).

결정-근거 매핑

Decision ID Decision 선택 조건 (언제 이 결정 / 언제 대안) Supporting Claims Evidence Strength Open Risk
D1 supply-chain control 을 분리된 merge·release gate 로 운영 (FE-OC-018) — control 집합은 hub FE-D024 소유이며 dependency review 는 D9 로 편입됐다 이 분리가 project 최소선; organization security policy 가 더 강한 gate 를 지정하면 강화·재분할 raw/project-notes/ca-skeleton-frontend-operational-contract FE-D024 project-decision repo/CI 부재 → gate 배선 UNVERIFIED
D2 frozen-lockfile install 을 merge+release 차단 gate, drift = FAIL (FE-GATE-001, FE-OC-018) frozen install 은 항상 필수; package manager/lockfile 형식FE-OC-003 (bootstrap) 소유 → 그쪽 변경 시 command 만 교체 raw/project-notes/ca-skeleton-frontend-operational-contract FE-D024 (hub §13.1 install row) project-decision pnpm default 는 bootstrap 결정; org 가 npm/yarn 강제 시 install command 재확정
D3 production build gate = hashed immutable asset + build-manifest 산출 (FE-GATE-011, FE-OC-016/FE-OC-018) Vite client-only SPA build baseline 이 유지되는 한; SSR/edge rendering 이 requirement 가 되면 build 출력 형태 재검토 raw/official-docs/vite-build-tool-official.md#VITE-C2 official-doc build 미존재 → artifact 이름/경로는 planned
D4 bundle report 는 release gate 로 생성, 수치 threshold 판정은 위임 (FE-GATE-012, FE-OC-018/FE-OC-021) report 는 항상 release 에 산출; FE-NFR-001(≤200 KiB)/FE-NFR-002(≤120 KiB) 값과 FE-NFR-C04 context 는 web-vitals 브랜치가 소유·재검토 raw/project-notes/ca-skeleton-frontend-operational-contract FE-D024 (hub §14.2) project-decision report↔threshold 소유 경계; threshold 변경은 FE-OC-021 에서
D5 security gate = secret+vuln+license 묶음(2026-07-20 D9 로 dependency review 가 4번째 control 로 편입), scanner/severity threshold 는 deferred (FE-GATE-013, FE-OC-018) 이 구성이 최소선; repository/organization policy 가 생기면 특정 scanner·threshold 확정 raw/project-notes/ca-skeleton-frontend-operational-contract FE-D024 (hub §13.1) conditional-default 지금 scanner 명시 = 날조 → deferred 유지
D6 secret scan 은 source + built asset 모두 검사 (FE-OC-018/FE-OC-019) browser bundle 을 public artifact 로 간주하는 한 항상; boundary 규칙(CSP·HTML injection) 자체는 FE-OC-019 소유 raw/official-docs/vite-build-tool-official.md#VITE-C4, #VITE-C5 (hub §13.2) official-doc scan 이 로그·debug 등 모든 유출 경로를 증명하진 못함 (VITE-C4 does-not-prove)
D7 release 는 dependency inventory + build-manifest(buildId/commit provenance) + checksums 포함, mismatch 차단; signed attestation 미채택 (FE-OC-018, hub §12.1) 최소선 = inventory + build metadata 를 provenance 로; org 가 더 강한 provenance 요구 시 signed SLSA(L2+) 채택 raw/official-docs/supply-chain-slsa-provenance-framework.md#SLSA-FW-C1, #SLSA-FW-C4, #SLSA-FW-C5 official-doc L1 provenance 는 "trivial to forge" (SLSA-FW-C1) — signing/SBOM 형식 deferred
D8 vulnerability suppression 은 reason·owner·expiry·affected package·compensating control 필수, expiry 경과 = gate failure (FE-OC-018, hub §13.3) fix 즉시 불가한 accepted vuln 에 적용; org 가 더 엄격한 SLA 정의 시 재검토 raw/project-notes/ca-skeleton-frontend-operational-contract FE-D024 (hub §13.3) project-decision expiry SLA 수치 미정 → deferred
D9 dependency review 를 FE-GATE-013 의 4번째 control 로 편입: base↔head lockfile direct+transitive diff, review 기록 없는 high-risk change = 차단, dependency diff report 산출 (FE-OC-018) hub §15.1 gate registry 가 26 row 로 고정된 동안은 기존 gate 확장; hub 가 registry+promotion formula 를 개정해 전용 gate 를 신설하면 그쪽으로 이관 raw/project-notes/ca-skeleton-frontend-operational-contract FE-D024 (hub §2.1 FE-OC-018 문구 + hub §13.1 dependency review row) project-decision hub FE-D024 가 dependency review 를 누락하던 불일치는 hub 정정으로 해소됨(현재 5개 control 열거, FE-GATE-013 fixtures 도 dependency-review 포함). "high-risk" 판정축·diff 도구는 hub 미지정 → UNSUPPORTED_IMPL_DECISION

구현 가이드

planned blueprint — frontend 코드/CI 는 아직 없다. 경로·command 는 hub §4.6/§12.1/§14.3 의 planned contract 에서 도출한 anchor 이며 repository 생성 시 확정된다. CLAUDE.md §15.5 R1(Trace)/R2(UNSUPPORTED_IMPL_DECISION)/R3(OUT_OF_BRANCH_SCOPE) 준수.

1. Gate topology — merge vs release 분리

Trace: D1 (raw/project-notes/ca-skeleton-frontend-operational-contract FE-D024), D2/D3/D4/D5/D9 — hub §15.1 gate registry + FE-OC-018.

  • UNSUPPORTED_IMPL_DECISION: 없음 — blocking scope·covered contract·artifact 는 hub §15.1 이 직접 명시. FE-GATE-013 의 control 4번째(dependency review) 편입은 D9 근거이며 gate ID 신설이 아니므로 hub registry row 수(26)를 바꾸지 않는다.

gate 의 blocking scope · Covered FE-OC · evidence artifact 는 hub §15.1 이 소유한다. 아래 표는 그 열을 옮겨 적지 않고, 본 브랜치가 각 gate 안에서 무엇을 명세하는지(control) 만 담는다. 값이 필요하면 raw/project-notes/ca-skeleton-frontend-operational-contract §15.1 을 본다.

Gate ID Control (본 브랜치 명세)
FE-GATE-001 frozen install drift
FE-GATE-011 clean production build
FE-GATE-012 bundle report 생성 (판정은 FE-OC-021 owner 위임)
FE-GATE-013 ① secret scan ② vulnerability scan ③ license inventory ④ dependency review (D9)
  • 실패는 warning 으로 낮추지 않는다 (FE-OC-020). 각 gate 는 최소 1개의 deliberately-failing negative fixture 로 "실제 동작"을 증명해야 한다 (hub §15.2). FE-GATE-013 은 4개 control 각각이 독립 negative fixture 를 갖는다 (§5).
  • gate 는 4개지만 control 은 7개(install·build·bundle·secret·vuln·license·dependency review)다. D1 의 "분리" 원칙은 gate ID 개수가 아니라 실패 지점이 artifact 단위로 구분 가능한가로 만족시킨다 — FE-GATE-013 내부 4 control 은 서로 다른 artifact(SARIF · license inventory · dependency diff report)로 실패 원인을 구분한다.

2. Frozen-lockfile install gate

Trace: D2 — hub §13.1 install row + §14.3.

  • UNSUPPORTED_IMPL_DECISION: 없음 — command·artifact 는 hub §14.3 이 명시. package manager 명(pnpm)은 본 브랜치 결정이 아님 → §OUT_OF_BRANCH_SCOPE 참조.
  • command: pnpm install --frozen-lockfile (hub §14.3, PLANNED_NOT_EXECUTED).
  • pass 조건: manifest ↔ lockfile drift 없음, exit 0.
  • artifact: artifacts/quality/install.txt (hub §14.3) / artifacts/quality/lockfile-check.txt (hub §13.1).
  • negative fixture: lockfile drift(수동 편집) → frozen install 이 exit≠0 로 실패해야 함.
  • OUT_OF_BRANCH_SCOPE: package manager 선택·packageManager field·pnpm-lock.yaml commit 은 raw/branch-notes/feature-frontend-project-bootstrap-toolchain-contract (FE-OC-003) 소유. 본 gate 는 그 lockfile 을 frozen 으로 검증만 한다.

3. Clean production build gate

Trace: D3 — raw/official-docs/vite-build-tool-official.md#VITE-C2 + hub §12.1 artifact set.

  • UNSUPPORTED_IMPL_DECISION: 없음 — build command·artifact·hash 규칙은 hub §12.1/§14.3 + VITE-C2 에서 도출.
  • command: pnpm build (hub §14.3).
  • pass 조건: exit 0 + 기대 artifact 존재.
  • 산출 artifact (hub §12.1): dist/index.html, dist/assets/<content-hash>.* (immutable hashed), dist/release-manifest.json, artifacts/release/build-manifest.json, artifacts/release/checksums.txt.
  • hashed asset 의 immutable cache 정책 자체는 raw/branch-notes/feature-frontend-release-cache-rollback-contract (FE-OC-016) 소유 — 여기서는 hash 산출까지만.

4. Bundle report gate

Trace: D4 — hub §14.3 (pnpm check:bundle) + §14.2 (FE-NFR-001/FE-NFR-002).

  • UNSUPPORTED_IMPL_DECISION: (a) bundle 분석 도구(rollup-plugin-visualizer / 자체 스크립트 등)는 hub 가 지정하지 않음 → 도구 선택은 repo 생성 시 결정. trade-off: 지금 도구명을 박으면 날조가 되므로 report 형식(machine-readable JSON)만 고정하고 도구는 미정. (b) bundle.json 의 필드명·구조는 2026-07-21 해소됨 — hub §2.1.3 이 ART-FE-002@1 로 등록하고 bundle-report.schema.json 이 정본이다. 아래 §schema 참조.
  • command: pnpm check:bundle (hub §14.3).
  • artifact: artifacts/performance/bundle.json (machine-readable, hub §14.3).
  • 측정 대상: initial JS(app) + 각 lazy route chunk 의 gzip 크기.
  • pass/fail 판정: FE-NFR-001 (initial JS gzip ≤ 200 KiB), FE-NFR-002 (lazy chunk gzip ≤ 120 KiB), context FE-NFR-C04.
  • schema = ART-FE-002@1 (hub §2.1.3 · harness/source/artifact-schemas/ca-skeleton-frontend/bundle-report.schema.json). 이전 판은 이 스키마를 "공동 소유라 단독 결정 불가" 로 두고 필드 초안을 여기에 적었는데, 그래서 producer(runner.node)와 consumer(snake_case) 가 서로 다른 키 이름을 계약이라 부르는 상태가 됐다. 이제 Schema Owner 는 본 브랜치 단독이고 필드 추가·rename 은 스키마 파일을 고쳐 revision 을 올리는 것으로 하며, 소비 branch 는 imports pin 이 낡아 자동으로 잡힌다.
    • context/runner 는 hub §14.1 의 "context 없는 숫자는 evidence 로 인정하지 않는다" 요구 때문에 required 다 (FE-NFR-C04).
    • buildId/commit 은 §6 build-manifest(ART-FE-001@1)와 동일 값이어야 하며, 이 대조로 report 가 어느 build 의 것인지 식별된다.
  • OPEN QUESTION — budget 이 JS-only 인가 CSS 포함인가: hub §14.2 는 FE-NFR-001 을 "initial JS gzip", FE-NFR-002 를 "any lazy route chunk gzip" 으로만 정의하고 CSS 전용 NFR ID 가 없다. 따라서 현재 계약은 JS-only 판정으로 읽는 것이 문언에 충실하다. 본 gate 는 CSS asset 의 gzip 크기도 report 에 기록은 하되 판정 대상으로 삼지 않는다. CSS 를 budget 에 포함할지, 별도 NFR ID 를 신설할지는 FE-OC-021 소유자와 hub §14.2 개정 사항이다.
  • OUT_OF_BRANCH_SCOPE: 위 threshold 수치·측정 context 정의는 raw/branch-notes/feature-web-vitals-performance-budget-contract (FE-OC-021) 소유. 본 gate 는 report 를 생성하고 threshold 를 소비한다.

5. Security gate — secret · vulnerability · license · dependency review

Trace: D5/D6/D8/D9 — hub §13.1 (secret/vuln/license/dependency review row) + §13.3 (suppression) + §2.1 FE-OC-018 + VITE-C4/C5.

  • UNSUPPORTED_IMPL_DECISION: (a) scanner 도구(secret: gitleaks/trufflehog?, vuln: npm audit/osv-scanner/trivy?, license: 자체?) 미정, (b) severity threshold(어느 CVSS 등급부터 차단) 미정, (c) suppression expiry SLA(며칠) 미정. 모두 hub §13.1/§13.3 이 deferred 로 명시 — 임의 확정 시 날조. trade-off: 지금은 gate 구조·fixture 계약만 고정하고 도구·수치는 organization security policy 확정 후 채운다.
  • UNSUPPORTED_IMPL_DECISION: (d) dependency diff 도구(GitHub Dependency Review Action / pnpm why 기반 자체 스크립트 / osv-scanner diff 등) 미정 — hub §13.1 은 "direct/transitive diff" 라는 대상만 규정하고 도구를 지정하지 않는다. trade-off: 도구명을 지금 박으면 날조이므로 입력(base↔head lockfile)·출력(dependency diff report)·차단 조건만 고정한다. (e) "high-risk change" 의 분류축(아래 R1~R5) 도 hub 미지정 — hub 는 unreviewed high-risk change 라는 차단 조건만 준다. trade-off: 분류축이 없으면 gate 가 판정 불가능해 구현 착수가 막히므로, fail-closed 기본값(분류 불가/미기록 = high-risk 취급)을 두고 축 목록은 org policy 확정 시 교체 가능한 것으로 표시한다. 축을 좁게 잡으면 위험 변경이 통과하고, 넓게 잡으면 모든 renovate PR 이 수동 리뷰를 요구해 마찰이 커지는 trade-off 를 인지하고 fail-closed 를 택했다.
  • command: pnpm scan:security (hub §14.3). dependency review 도 이 command 안에서 수행한다 — hub §14.3 planned command 표에 dependency-review 전용 script 가 없으므로 새 script 명을 만들면 hub 계약과 어긋난다. script 를 분리하려면 hub §14.3 + §15.1 artifact mapping 을 함께 갱신해야 한다 (hub §14.3 말미 규칙).
  • artifact: artifacts/security/scan.sarif (hub §14.3) + license inventory + artifacts/release/dependency-inventory.* (hub §12.1) + dependency diff report (아래).
  • secret scan (D6): source + built asset(dist/) 모두 검사. 이유: VITE_ prefix 값은 build-time 에 정적 inline 되므로(VITE-C3) 유출은 built bundle 에서만 관측될 수 있음(VITE-C4/C5). browser bundle = public artifact (hub §13.2).
  • vulnerability scan (D5): severity policy 위반이 approved expiry 없이 존재하면 차단 (hub §13.1).
  • license inventory: denied/unknown license 미해결 시 차단 (hub §13.1).
  • suppression (D8): 각 suppression 은 reason·owner·expiry·affected package·compensating control 보유; expiry 경과 suppression = gate failure (hub §13.3).

5.1 Dependency review (control ④)

Trace: D9 — hub §2.1 FE-OC-018 (frozen lockfile · dependency review · secret scan · SBOM/inventory 를 release gate 에 MUST 포함) + hub §13.1 dependency review row (direct/transitive diff / unreviewed high-risk change / dependency diff report).

  • 무엇을 diff 하는가 (입력): PR 의 base commit lockfile ↔ head commit lockfile. 두 lockfile 을 각각 resolve 해 얻은 완전한 패키지 집합(direct + transitive, 즉 lockfile 에 기록된 모든 resolved entry)을 비교한다. manifest(package.json) diff 만 보지 않는다 — hub §13.1 이 명시적으로 direct/transitive 를 요구하고, transitive 변경은 manifest 에 나타나지 않기 때문이다.

    • release 시점에는 base = 직전 release 의 lockfile(release token 기준)로 잡아 release 단위 누적 변경도 같은 방식으로 산출한다.
  • 변경 분류 (출력 행): 각 diff row 는 {package, from, to, changeKind, depth, riskFlags[], reviewRef} 를 갖는다.

    • changeKindadded | removed | version-changed | resolution-changed(같은 버전인데 resolved URL/integrity 가 바뀐 경우).
    • depthdirect | transitive.
  • 무엇이 "unreviewed high-risk change" 인가 (차단 조건): 아래 두 조건을 동시에 만족하는 row 가 하나라도 있으면 FE-GATE-013 FAIL.

    1. high-risk 로 분류됨 — 아래 riskFlag 축 중 하나 이상에 해당. (축 목록 자체는 위 UNSUPPORTED_IMPL_DECISION (e).)
      • R1 new-package — 이전 lockfile 에 없던 패키지 추가 (direct/transitive 무관; 새 코드가 신뢰 경계에 들어옴).
      • R2 install-script — install/postinstall 등 lifecycle script 를 실행하는 패키지의 추가·변경.
      • R3 major-bump — semver major 상승 (hub §13.3 이 major update 에 FE-D* impact check + registry compatibility check 를 별도로 요구하므로 위험 등급이 다르다).
      • R4 license-change — 해당 패키지의 license 식별자가 변경됨 (license inventory control 과 교차).
      • R5 known-vuln — vulnerability scan 이 해당 패키지에 severity policy 위반을 보고함 (vulnerability control 과 교차).
      • fail-closed 기본값: riskFlag 산출에 필요한 metadata(license/lifecycle script/이전 버전)를 확보하지 못해 분류 자체가 불가능한 row 는 high-risk 로 간주한다. "정보 부족 = 통과" 는 gate 를 무력화하므로 채택하지 않는다.
    2. review 기록이 없음 — 해당 row 에 대응하는 review record(reviewer, 날짜, 대상 package@version, 승인 사유)가 없거나, 기록의 package@to 가 실제 diff 와 불일치. review record 는 vulnerability suppression(D8, hub §13.3)과 별개 트랙이다: suppression 은 "알려진 취약점을 기한부로 감수", review 는 "이 의존성 변경을 사람이 보았다" 이며 후자는 expiry 를 갖지 않는 대신 해당 package@version 에만 유효하다(버전이 다시 바뀌면 재검토 대상).
    • low-risk row(위 축 어디에도 해당 없음)는 review 없이 통과한다 — 그렇지 않으면 patch 단위 갱신마다 gate 가 막혀 정책이 실질적으로 우회된다.
  • evidence artifact (dependency diff report): hub §13.1 은 artifact 를 dependency diff report 라고만 명명하고 경로를 주지 않는다. 본 브랜치는 artifacts/security/dependency-diff.json 을 anchor 로 둔다 — hub §14.3 이 security 계열 artifact 를 artifacts/security/ 아래 두므로(scan.sarif) 그 규약을 따른 것이다.

    • UNSUPPORTED_IMPL_DECISION: 위 파일명·경로는 hub 가 지정하지 않은 명명 결정. trade-off: 경로를 비워두면 CI 배선(FE-OC-020 소유자)이 artifact 를 수집할 수 없어 gate 가 성립하지 않으므로, hub 의 기존 디렉터리 규약에서 가장 마찰이 적은 이름을 anchor 로 고정하고 repository 생성 시 확정한다.
    • report 최소 내용: {baseRef, headRef, rows[], blocking[]}rows[] 는 위 diff row 전체, blocking[] 은 차단 사유가 된 row 의 부분집합. 통과한 build 도 report 를 남긴다(변경 0건이면 빈 rows[]) — 산출 자체가 hub §13.1 의 evidence 요구다.
  • negative fixture: review record 없이 R1 new-package 에 해당하는 transitive 의존성을 추가한 fixture 가 FE-GATE-013 을 FAIL 시켜야 한다. 대칭으로, 동일 변경에 유효한 review record 를 붙이면 PASS 해야 한다(가짜 PASS 방지).

  • OUT_OF_BRANCH_SCOPE: review record 를 어디에 보관할지(PR label / repo 내 파일 / 외부 시스템)와 reviewer 권한 모델은 CI orchestration 영역으로 raw/branch-notes/feature-frontend-ci-quality-gates-contract 소유. 본 gate 는 "review record 가 조회 가능해야 한다"는 인터페이스 요구만 둔다. lockfile 형식·package manager 는 raw/branch-notes/feature-frontend-project-bootstrap-toolchain-contract (FE-OC-003) 소유이며 본 control 은 그 lockfile 을 읽기만 한다.

  • negative fixture 후보: (i) VITE_-var 에 심은 가짜 secret 이 dist/ 번들에서 탐지되어 실패, (ii) known-vuln 의존성이 approved expiry 없이 차단, (iii) 만료된 suppression 이 실패, (iv) denied license 가 실패, (v) review record 없는 신규 transitive 의존성 추가가 실패 (§5.1).

6. Release supply-chain artifact set & provenance

Trace: D7 — hub §12.1 artifact set + §13.1 provenance/SBOM row + SLSA-FW-C1/C4/C5.

  • UNSUPPORTED_IMPL_DECISION: SBOM 형식(CycloneDX vs SPDX)과 signed attestation(in-toto/DSSE, SLSA L2+) 채택 여부 미정 → hub §13.1 이 "tool selected by owner" 로 deferred. trade-off: 최소선(dependency inventory + build metadata)만 고정하고 signing 은 org 요구 시.
  • release artifact (hub §12.1): artifacts/release/dependency-inventory.*, artifacts/release/build-manifest.json, artifacts/release/checksums.txt.
  • provenance 최소선: build-manifest 에 buildId/commit 을 기록해 build platform/process/top-level input 을 기술(SLSA-FW-C1, C4). buildId/commit mismatch = 차단 (hub §13.1 provenance row).
  • dependency inventory 는 SLSA resolvedDependencies 개념(build time 필요 artifact 의 collection, SLSA-FW-C5)에 대응하되 "완전성"을 주장하지 않는다("if known", SLSA-FW-C5 does-not-prove).
  • 미채택 표지: SLSA L1 provenance 는 "trivial to forge"(SLSA-FW-C1) — signed/authenticated attestation 은 별도 결정이며 현재 채택하지 않는다.

엣지·실패·의존

  • 실패·엣지 경로:
    • lockfile drift → frozen install exit≠0 → FE-GATE-001 FAIL.
    • production build 실패 또는 기대 artifact 누락 → FE-GATE-011 FAIL.
    • bundle threshold 초과 → FE-GATE-012 FAIL (판정 값은 FE-OC-021 소유).
    • built asset 에서 secret 패턴 hit → FE-GATE-013 FAIL.
    • severity threshold 위반이 approved expiry 없이 존재 / 만료된 suppression → FE-GATE-013 FAIL.
    • denied/unknown license 미해결 → FE-GATE-013 FAIL.
    • review record 없는 high-risk dependency 변경(신규 패키지·install script·major bump·license 변경·known-vuln) → FE-GATE-013 FAIL (§5.1).
    • dependency diff row 의 riskFlag 를 분류할 metadata 부재 → fail-closed 로 high-risk 취급 → review 없으면 FE-GATE-013 FAIL (§5.1).
    • base lockfile 을 확정할 수 없음(base ref 소실·shallow clone) → dependency review 를 "통과" 로 처리하지 않고 gate ERROR 로 처리해 차단 (fail-closed).
    • release inventory 누락 또는 buildId/commit mismatch → release 차단 (hub §12.1/§13.1).
  • 다른 계약 의존 (sibling 링크는 FE-OC-### 로만 표기):

검증해야 할 주장

Claim Why uncertain How to verify Status
frozen install 이 lockfile drift 를 실제로 차단한다 CI/repo 부재 drift fixture 로 pnpm install --frozen-lockfile 이 exit≠0 → artifacts/quality/install.txt needs-confirmation
production build 가 기대 artifact set + hashed asset 을 산출한다 build 미실행 build gate fixture 로 pnpm build exit 0 + artifacts/release/build-manifest.json 존재 확인 needs-confirmation
bundle report 가 initial JS + lazy chunk gzip 을 machine-readable 로 기록한다 도구 미정 pnpm check:bundleartifacts/performance/bundle.json 스키마 검증 (threshold 판정은 FE-OC-021) needs-confirmation
secret scan 이 source 뿐 아니라 built asset 의 secret 을 탐지한다 코드/scanner 미정 negative fixture: VITE_-var 의 가짜 secret 이 dist/ 번들에서 탐지되어 FE-GATE-013 FAIL needs-confirmation
vulnerability gate 가 known-vuln(무-expiry)과 만료된 suppression 을 차단한다 scanner/threshold deferred negative fixture 로 pnpm scan:security 가 두 경우 FAIL → artifacts/security/scan.sarif needs-confirmation
license inventory 가 denied/unknown license 를 flag 한다 도구 미정 fixture: denied license 의존성이 security gate FAIL needs-confirmation
dependency review 가 base↔head lockfile 의 transitive 변경까지 잡아낸다 diff 도구 미정, lockfile 미존재 fixture: manifest 는 그대로 두고 transitive 만 바뀐 lockfile 로 pnpm scan:securityartifacts/security/dependency-diff.jsonrows[] 에 해당 row 존재 needs-confirmation
review record 없는 high-risk 변경이 실제로 차단되고, record 를 붙이면 통과한다 review record 저장 위치가 FE-OC-020 소유로 미확정 negative/positive 쌍 fixture: 신규 transitive 패키지 추가 → record 없으면 FAIL, 있으면 PASS needs-confirmation
bundle.json 이 소비자(FE-OC-021)가 FE-NFR-001/FE-NFR-002 를 판정하기에 충분한 필드를 담는다 스키마(ART-FE-002@1)는 확정됐으나 실제 report 생성이 미실행 스키마대로 report 생성 후 web-vitals 판정 로직이 추가 필드 요구 없이 동작하는지 대조 needs-confirmation
release 가 dependency inventory + build-manifest(buildId/commit) + checksums 를 포함하고 mismatch 를 차단한다 pipeline 부재 release verification fixture 로 buildId/commit mismatch 차단 확인 needs-confirmation

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

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

마주친 문제

  • 없음 — 스캐폴딩 단계.

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

가져온 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

가져온 프로젝트 계약

Ref Owner 요약 Branch 적용
FE-GATE-001@1 raw/branch-notes/feature-frontend-project-bootstrap-toolchain-contract lockfile 이 manifest 와 어긋나면 merge·release 를 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-016@1 raw/branch-notes/feature-frontend-release-cache-rollback-contract HTML, asset, runtime config, release manifest cache policy를 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 참조로 적용
  • 없음 — 자식 자료는 생성 후 controller가 parent Cluster와 함께 등록한다.

관련 일일 노트

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

완료 후 정리

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