# CI quality-gate orchestration `config/ci/gates.json` is the executable registry for all 26 gates. The Gitea adapter runs each gate as an independent matrix check with full fan-out and no soft-fail wiring. The dependency graph is: ```text MERGE_READY -> RELEASE_READY -> PROD_PROMOTION_READY -> FIELD_SLO_READY DOCUMENTATION_READY (off-chain) ``` `check:architecture`는 dependency-cruiser 결과와 별도로 Babel parser/Node resolver 기반 `staticImportGraph`를 `artifacts/quality/dependency-report.json`에 기록한다. TS/TSX의 static, dynamic, type, CommonJS 및 JSDoc import를 검사하고, 로컬 source import에는 실제 TypeScript 확장자를 요구한다. 실행 source나 로컬 specifier에 `.js/.jsx/.mjs/.cjs`가 있거나 unresolved dependency, parse failure, error-severity layer violation, cycle 또는 지원하지 않는 architecture rule shape가 하나라도 있으면 gate는 실패한다. resolver, unresolved import, layer edge와 cycle regression fixture도 같은 명령에서 실행된다. Gitea workflow는 run ID/attempt, checkout SHA와 ref에서 build/release 식별자를 만들고 최소 `contents: read` 권한만 요청한다. Gate runner는 `HEAD`의 full commit ID와 commit timestamp를 한 번 읽어 `SOURCE_DATE_EPOCH`를 유도한 뒤 workflow의 SHA와 실제 checkout이 일치하는지 확인한다. `CI=true`에서 이 식별자 중 하나라도 없거나 불일치하면 build step 전에 fail-closed하며, build와 release manifest도 같은 build ID, commit SHA, release ID와 timestamp를 가져야 한다. Provider baseline은 Gitea 1.26.4 이상과 Gitea Runner 1.0.0 이상이다. 이 workflow를 required check로 전환하기 전에 staging instance에서 `permissions`, `gitea.run_attempt`, `actions/upload-artifact@v4`를 포함한 한 번의 전체 provider smoke를 통과시켜야 한다. 모든 setup step은 `node-version-file: .nvmrc`를 사용하므로 CI Node.js 버전은 `.nvmrc`의 exact pin과 같다. CI contract는 `.nvmrc`가 full semantic version인지, 모든 job이 이 파일을 사용하는지 함께 검사한다. `ubuntu-latest` runner label은 관리자가 임의 환경에 매핑할 수 있으므로 provenance로 사용하지 않는다. 대신 그 label을 immutable container image에 매핑하고 동일한 image digest를 repository variable `RUNNER_IMAGE_DIGEST`에 설정한다. Workflow는 이 값을 runtime environment의 `CI_RUNNER_IMAGE`로 전달하며, 값이 비어 있으면 모든 gate가 실행 전에 실패한다. Gitea repository variable 이름에는 `CI` prefix를 사용할 수 없으므로 두 이름을 의도적으로 구분한다. Pull requests and `develop` pushes evaluate merge readiness. Version tags evaluate merge then release readiness. Production and field evaluation require an explicit workflow dispatch. The field tier cannot pass until the 28-day sample threshold decision is recorded. Documentation readiness consumes the canonical project-note evidence in which both scoped diagrams already received 100/100 `PASS_SCOPED`; the repo ledger preserves the evidence scope and canonical digests. All jobs upload the shared `artifacts/` tree even after failure. Numeric retention remains an organization/provider decision; the workflow intentionally does not invent `retention-days`. The relative minimums are recorded in the registry: merge evidence through the PR decision, coherent release evidence through the next release promotion, drill evidence through the next production promotion, and field evidence through aggregation. Runbook jobs write to the stable path `artifacts/runbooks//record.json`; the dynamic release identity is stored inside the record. This keeps gate evidence lookup independent from slashes or other provider-specific characters in `RELEASE_ID`, while each workflow artifact remains scoped to its own run. Browser-backed merge gates install and execute the pinned Chromium, Firefox, and WebKit engines. This makes route behavior, reflow, native dialog semantics, theme persistence, and automated accessibility a cross-engine contract rather than a Chromium-only smoke check. `FE-GATE-005`의 unit suite에는 client cache/storage의 deterministic contract가 포함된다. `storage-registry.test.ts`는 key별 codec, schema/TTL, 16,384-byte 상한, quota/security/corrupt cleanup과 memory fallback을, `cross-tab-invalidation.test.ts`는 2,048-byte exact wire, topic/release epoch, self/duplicate/out-of-order/gap, BroadcastChannel → localStorage → local-only degrade와 cleanup을 검증한다. `tanstack-cache-coordinator.test.ts`는 topic을 local namespace로만 resolve하고 query key/data를 wire에 보내지 않으며 mutation 중 remote hint를 coalesce하는지 검증한다. 결과는 기존 `artifacts/tests/unit.xml`과 coverage evidence에 포함된다. `FE-GATE-006`과 runtime adapter unit은 `QueryInvalidationProvider`/coordinator가 실제 production provider tree와 bootstrap에 존재하고 browser capability getter가 실패해도 boot가 `DEGRADED_LOCAL_ONLY`로 계속되는지 확인한다. `FE-GATE-008`의 `test:browser-capabilities` suite는 File/Blob, native file input, IndexedDB, OPFS, Cache Storage와 StorageManager reference runtime을 Vite dev origin의 실제 browser API에 연결한다. API가 없는 engine에서는 skip하지 않고 adapter의 명시적인 `UNSUPPORTED` fallback을 검증한다. 생성한 database, OPFS namespace와 owned cache는 각 test가 자신이 만든 opaque namespace만 정리한다. `FE-GATE-010`은 application/domain/presentation에서 raw browser storage와 picker global 접근을 막고, 선택되지 않은 reference runtime을 bootstrap이나 installed feature가 import하는 것도 거절한다. catalog의 `referenceRuntime.sourceRoots`와 conformance script가 실제로 존재해야 하며, `productionComposition`은 project capability 결정 전까지 `false`다. 같은 gate의 realtime source/fixture 검사는 native SSE/WebSocket/Web Push API가 소유 adapter 밖으로 새는 것, presentation timer owner, 미선택 runtime의 bootstrap/installed-feature 조합을 차단한다. optional recipe source gate는 uncomposed runtime 전체를 tree-shaking 없이 합성해 catalog의 gzip 예산도 blocking으로 검증한다. `FE-GATE-020`은 reference runtime source·전용 test·catalog metadata를 제거한 임시 repository에서도 base typecheck, architecture, test, build가 통과하는지 검증해 skeleton의 선택성을 유지한다. browser file/storage와 realtime runtime은 각각 독립 removal fixture와 JUnit evidence를 가지며, realtime fixture는 공통 event authority, SSE, WebSocket, bounded Polling, Web Push source와 공개 export, 전용 boundary script를 제거한 뒤 base gate를 다시 실행한다. 같은 `FE-GATE-010`의 `FE-REG-QUERY` governance는 installed query마다 namespace, serialization/identity, invalidation topic, version, `crossContext: "invalidate-only"`와 `persistence: "disabled"`를 요구한다. 따라서 cross-tab invalidation은 조립됐지만 query persistence와 기존 IndexedDB reference runtime은 계속 미조립이다. 현재 `FE-GATE-008` browser-capability suite에는 실제 두 page의 BroadcastChannel/localStorage fallback scenario가 아직 없다. unit fake 통과를 native multi-tab promotion evidence로 간주하지 않는다. 이 gap은 [Client cache and browser storage platform](../architecture/client-cache-and-storage.md)의 완료 기준에 미완료로 남아 있다. 현재 장애 분류와 안전한 local-only 복구 절차는 [Client cache and Web Storage recovery](./client-cache-and-storage-recovery.md)를 따른다. Repository variables required by higher tiers: - `RUNNER_IMAGE_DIGEST` for the immutable job-container image digest used by the `ubuntu-latest` runner label; the workflow exposes it to gates as `CI_RUNNER_IMAGE` (required by every tier) - `HOSTING_BASE_URL` for live header verification - `FIELD_WEB_VITALS_INPUT` for the privacy-approved field sample document - `MIN_ELIGIBLE_SAMPLES` after the baseline decision Branch protection must mark each `FE-GATE-* / ` check required for its declared tier. This repository cannot configure server-side protection by committing a file.