chore: initialize from frontend template 4dc033c

This commit is contained in:
DongHyeonka
2026-08-13 18:23:26 +09:00
commit 40107eec84
897 changed files with 234824 additions and 0 deletions
@@ -0,0 +1,82 @@
# 플랫폼 구성 화면 (`EXAMPLES_PLATFORM`) 설계
작성일: 2026-07-31
## 1. 문제
템플릿이 무엇을 설치해 두었는지 확인할 방법이 없다. 홈 화면은 "실행 계약 / 교체 가능한 연동 /
접근 가능한 화면"이라는 세 문장으로만 요약하고, 실제로 어떤 라우트·계약·런타임 능력이 설치되어
있는지는 소스를 직접 읽어야만 알 수 있다.
## 2. 해결 방향
설치 상태를 **레지스트리에서 파생해서만** 렌더하는 화면을 하나 추가한다. 수기 서술을 두지 않으므로
코드가 바뀌면 화면이 따라 바뀌고, 문서가 낡는 문제가 발생하지 않는다.
이 선택에는 부수 효과가 있다. reference feature를 삭제하면 관련 행이 자동으로 사라지므로
`test:sample-removal` harness의 잔재 스캔과 충돌하지 않는다. 반대로 수기 목록이었다면 샘플 이름이
페이지에 박혀 harness가 실패했을 것이다.
## 3. 배치
| 항목 | 값 | 근거 |
| --- | --- | --- |
| `routeId` | `EXAMPLES_PLATFORM` | |
| `path` | `/examples/platform` | 제품 개발 시 통째로 삭제 가능한 `examples/` 옥 |
| `access` | `public` | 세션 연동 없이 확인 가능해야 함 |
| `loadingSurface` | `example-page` | governance `allowedValues`에 이미 존재 — 신규 값 추가 없음 |
| `errorSurface` | `route-boundary` | 동일 |
| `chunkId` | `route-examples-platform` | |
| `navigationOrder` | `15` | 홈(10)과 UI(20) 사이. 기존 값 재번호 불필요 |
| 구현 파일 | `src/presentation/examples/platform-overview-page.tsx` | `examples/``check:i18n` 한글 리터럴 스캔 대상이 아님 |
## 4. 섹션 구성
전부 파생 데이터다.
| # | 섹션 | 출처 | 표현 대상 |
| --- | --- | --- | --- |
| 1 | 릴리스 신원 | `ApplicationApi.runtime.getReleaseSummary()` | buildId, releaseId, configSchemaVersion, contractSet digest |
| 2 | 설치된 라우트 | `ROUTE_REGISTRY` | path, access, chunkId, params/search 스키마 |
| 3 | 계약과 HTTP 오퍼레이션 | `COMPOSED_CONTRACT_CONTRIBUTIONS`, `EXPECTED_CONTRACT_SET_PACKAGES` | 외부 계약 패키지 수, 오퍼레이션별 재시도 의미·예산·바이트 한도·deadline·효과 확정성 |
| 4 | 서버 상태와 실행 상한 | `SERVER_STATE_PROFILES`, `HTTP_EXECUTION_CEILINGS` | 4개 프로파일의 staleTime·gcTime·결과 예산, 강제되는 실행 상한 |
| 5 | 선택적 런타임 능력 | `INSTALLED_RUNTIME_CAPABILITIES` | realtime / webWorker / serviceWorker / offlineCommands 선택 여부 |
섹션 3의 "외부 계약 패키지 0개"와 섹션 5의 "4개 능력 전부 미선택"이 "어디까지 제공하는가"에 대한
답이다. 공통 런타임은 구현·검증되어 있으나 제품 기여물이 없어 선택되지 않은 상태임을 드러낸다.
## 5. 런타임 해석 결과
초판은 `CAPABILITY_OVERRIDES` 해석 결과를 제외했다. 해석이 bootstrap에서 일어나고
`presentation-does-not-know-adapters` 규칙이 presentation → bootstrap import를 금지했기 때문이다.
이후 `docs/superpowers/plans/2026-07-31-platform-overview-completion.md`가 그 경로를 만들었다.
`describeRuntimeCapabilities`가 정적 선택과 해석 결과를 경계된 스냅샷으로 축약하고,
`RuntimeCapabilitiesPort`를 통해 합성 루트가 그 스냅샷을 애플리케이션에 전달한다. 표현 계층은
`runtime.getCapabilitySnapshot()`만 호출하므로 bootstrap을 여전히 알지 못한다.
스냅샷이 `selected``active`를 함께 실으므로 화면은 세 상태를 구분한다.
| 상태 | 조건 | 표시 |
| --- | --- | --- |
| 미선택 | `selected === 0` | 애초에 설치되지 않았다 |
| 운영자가 비활성화함 | `selected > 0 && active === 0` | 설치됐으나 런타임 설정이 껐다 |
| 활성 | `active > 0` | 지금 동작한다 |
## 6. 함께 변경되는 파일
1. `src/contracts/routes.ts` — 레지스트리 항목
2. `src/contracts/route-runtime-contract.ts` — 런타임 계약 항목
3. `src/presentation/routes/route-runtime.tsx` — lazy import
4. `src/presentation/i18n/catalog.ts` — ko/en `route.EXAMPLES_PLATFORM.{title,navigation}`
5. `public/release-manifest.json``routeChunks` 항목
6. `config/contracts/registry-baseline.json` 및 승인·증거 파일
7. `src/presentation/styles/` — 요약 그리드 스타일
8. `tests/component/platform-overview-page.test.tsx` — 컴포넌트 테스트
## 7. 검증
- 타입, lint, 아키텍처, i18n, 디자인 시스템, 레지스트리 게이트
- `test:all`
- removal harness 4종. 특히 `test:sample-removal` 이후에도 페이지가 빈 상태로 정상 렌더되어야 한다.
- 실제 브라우저 렌더 확인
@@ -0,0 +1,322 @@
# Refactoring Review Remediation Design
## Purpose
Complete the existing runtime-integrity work, preserve the current uncommitted
implementation snapshot, and then close the correctness and evidence gaps found
by the repository-wide module, script, schema, and test review.
This design supplements `2026-08-01-runtime-integrity-refactor-design.md`. It
does not replace the existing release/boot or scope-owned server-state plans.
Those changes remain the baseline on which the remediation phases build.
## Chosen approach
Use a staged, in-place compatibility migration with test-first behavior fixes.
- Preserve every pre-existing dirty-worktree change. Do not reset, restore, or
replace the current implementation with a clean-branch rewrite.
- Complete and verify the existing release/boot and scope-generation work
before changing its public contracts.
- Add a failing regression test before every production behavior change.
- Fix active correctness and release blockers before structural extraction.
- Keep public facades stable while removing duplicate internal authorities.
- Do not enable the Service Worker capability until its bounded marker reader,
full identity handshake, and ACTIVE fixture build are verified.
A big-bang rewrite is rejected because query ownership, HTTP execution,
release evidence, and optional runtimes have independent failure modes. A
P0/P1-only patch is also rejected because it would leave duplicate registries
and misleading quality gates that can recreate the same defects.
## Program phases
The work is delivered as independently testable sub-projects in this order.
1. Existing implementation baseline
2. Query invalidation correctness
3. Mutation intent and effect settlement
4. Immutable release promotion
5. Release/evidence contract enforcement
6. Quality and architecture gate accuracy
7. HTTP contract and layer consolidation
8. Service Worker hardening
9. Characterized adapter decomposition
Each phase must leave type checking, linting, and its focused tests green. A
later phase may depend only on explicit interfaces produced by an earlier one.
## Existing implementation baseline
The current dirty snapshot contains the in-progress Release Manifest V2, exact
boot pairing, scope-generation ownership, HTTP V3 executor, and optional runtime
foundation. It is the source of truth for this program.
Before remediation begins, run the focused suites from the existing
release/boot and scope-owned server-state plans. Resolve failures in those
plans without changing the remediation contracts below. Record browser- or
artifact-producing gates separately when they cannot run in the current
environment.
## Query invalidation architecture
One query-key module owns both bound keys and invalidation prefixes. Query-key
schema version 2 is:
```text
["query", 2, namespaceId, namespaceVersion, scopeFingerprint,
definitionVersion, identityToken]
```
The invalidation prefix for the same namespace is exactly:
```text
["query", 2, namespaceId, namespaceVersion]
```
Features declare namespace identities and topic-to-namespace edges; they never
copy TanStack key shapes. Bootstrap composes one `InvalidationRegistry`, calls
`indexInvalidationRegistry` once, and passes the resulting many-to-many index
to the cache coordinator. Cross-context messages continue to carry opaque
topics only. The receiving coordinator resolves every namespace for that
topic locally.
The legacy flat query registry and the empty registry in `query-keys.ts` are
removed after static usage reaches zero. Query persistence is disabled, so the
key-version change requires no persisted-cache migration.
## Mutation intent architecture
`MutationIntent` is an application-level command identity, not an HTTP adapter
detail. The contracts layer defines its immutable shape, the application layer
defines `MutationIntentFactory`, and a browser adapter implements the factory
with `crypto.randomUUID()`.
```ts
type MutationIntent = Readonly<{
intentId: string;
operationId: string;
canonicalInputIdentity: string;
idempotencyKey?: string;
createdAtMonotonicMs: number;
}>;
```
`useApplicationMutation` creates exactly one intent after duplicate admission
and before optimistic projection. The intent travels through the bound mutation
execution context and feature input to `ContractHttpExecutor`. Every physical
retry and reconciliation of that logical submit reuses the same intent. A new
submit always creates a new intent.
For a `KEYED` descriptor, the executor requires a bounded, non-empty
idempotency key before credential resolution or fetch admission. Missing or
invalid keys return a pre-dispatch contract violation with `NOT_STARTED` and
perform zero fetches. Query operations never emit an idempotency header. Intent
IDs and keys are forbidden in URLs, query keys, diagnostics, and telemetry.
## Mutation effect settlement
Command failures carry mandatory effect certainty. The mutation bridge derives
settlement before changing optimistic state:
```text
NOT_STARTED | NOT_APPLIED -> rollback
APPLIED_CONFIRMED -> commit, then invalidate
MAYBE_APPLIED -> retain uncertain layer; do not retry or invalidate
```
`OptimisticLayerLease` gains an uncertain state and an explicit reconciliation
operation. An uncertain layer remains ordered with later layers and cannot be
collapsed into the base until inspection resolves it as applied or not applied.
The application async state exposes `mutation-effect-unknown`, and the
controller exposes a reconciliation action associated with the original
intent. Unknown effect never silently maps to conflict, success, or generic
retryable failure.
## Immutable release promotion
The release workflow has one byte-producing authority.
1. `immutable_build` creates `dist`, build manifest, module inventory, and the
local supply-chain documents once.
2. The job publishes one immutable bundle and its `distSha256`.
3. Provider jobs scan and attest that exact digest.
4. The promotion job downloads the bundle and provider evidence, supplies
`VULNERABILITY_REPORT_PATH` and `PROVENANCE_ATTESTATION_PATH`, and verifies
schemas, signatures, and digest identity.
5. Promotion consumes the verified bundle without rebuilding it.
The current `build:release` command is split into byte production, local
evidence generation, provider verification, and promotion verification.
Missing provider evidence remains `FAIL_UNVERIFIED`; it is never converted to a
local pass. An attestation for a different digest, or any rebuild after
attestation, fails promotion.
This repository validates but does not fabricate external provider evidence.
The CI environment must supply a vulnerability report and signed provenance
attestation produced for the published candidate digest. Until that external
integration is configured, candidate build and local verification may pass but
promotion remains intentionally unavailable.
## Release and evidence contracts
One async `verifyReleaseRuntimeCoherence` policy is used by `verify-release`
and the rollback runbook. V1 verifies the legacy scalar tuple. V2 verifies the
exact package set and recomputes the contract-set digest. V2 never synthesizes
an API contract version.
All machine-readable evidence writers call a common validated writer before
touching the destination:
```ts
writeValidatedJsonArtifact({ path, schema, value }): Promise<void>
```
Executable Zod schemas are authoritative. Checked JSON schemas are generated
views and a `--check` command fails on drift. CI maps every evidence path to an
executable schema and validates content before upload, rather than checking
existence only.
`verifyBuildManifestOutputs` confines declared paths to their approved roots,
parses the module inventory, and compares its raw SHA-256 with
`moduleInventoryHash`.
Repository file discovery is fail-closed. Required roots fail on absence,
permissions, or read errors; optional roots permit only explicit `ENOENT`.
Provenance and secret scanning share the tracked repository inventory so build
inputs such as `index.html`, Vite configs, TypeScript configs, `.nvmrc`, and the
provider workflow cannot be silently omitted.
## Quality and architecture gates
Coverage reports distinguish `selectedTotal` from `repositoryTotal`.
`repositoryTotal` contains every production TypeScript module and starts with a
non-decreasing baseline. A critical-module registry immediately includes HTTP
V3, request/response bounds, boot bounds, service-worker lifecycle, scope
generation, and release loading. A changed high-risk module must have a policy
entry or an owned, expiring waiver.
The HTTP scenario catalog is executable input to table-driven contract tests.
Declaring a scenario without executing its status, effect, retry, fetch-count,
media-type, body-bound, and scope-fence expectations does not satisfy the gate.
`config/ci/gates.json` is parsed by one shared schema.
`scripts/generate-ci-workflow.ts` deterministically emits the complete provider
workflow, and its `--check` mode fails when the checked-in workflow differs.
Token and regex presence checks are not authoritative. Playwright configurations inherit
`forbidOnly: true`, Vitest rejects `.only`, and fake timers are restored by the
common test setup.
The Babel/Node resolver graph is the sole authoritative architecture analyzer
while dependency-cruiser cannot parse TypeScript 7. A zero-module graph fails.
Rules prohibit contracts from importing outer application/runtime layers and
feature adapters from importing global concrete adapters.
## HTTP contract consolidation
The installed contract contribution is the single source for method, path,
input/output validators, retry semantics, effect classification, deadlines,
and byte bounds. The reference DTO schema is defined once and requires a valid
datetime when `createdAt` is present. Legacy registries and codecs are generated
from the contribution during the compatibility window, then removed after
production and tests have no callers.
Provider-neutral operation outcomes and typed operation maps live in contracts
or application ports. Feature adapters do not import `HttpExecutionOutcome`
from a concrete HTTP adapter and do not accept `operationId: string` paired with
`input: unknown`. Runtime `REQUEST_TIMEOUT_MS` is a global maximum applied on
top of descriptor deadlines.
Raw query and mutation overloads are removed from production exports after
callers migrate to `BoundQuery` and `BoundMutation`. Test-only legacy harnesses
remain outside the production public index until their tests migrate.
## Service Worker hardening
Activation markers are read through a realm-safe bounded response reader. It
checks declared length, reads at most `maxBytes + 1`, cancels an oversized or
non-terminating stream, decodes fatal UTF-8, and validates the marker record.
`response.text()` is not used for bounded worker protocol data.
Page and worker exchange a canonical identity digest covering protocol version,
cache schema version, build, release, contract set, and static asset set. This
wire-shape change increments the Service Worker protocol version to 2. Every
tuple-field mutation changes the digest and rejects activation.
The ACTIVE fixture build validates asset entries and recomputes the static set
digest without changing the product's default `null` selection.
## Adapter decomposition
Structural extraction begins only after behavior is characterized.
- IndexedDB runtime and maintenance share one persisted-row schema containing
record, receipt, retention, budget types, guards, and golden fixtures.
- OPFS worker keeps its public facade while browser bootstrap, message host,
core state machine, Web Lock lease, and physical I/O move into focused files.
- Public response cache extracts manifest codec/digest and generic lock logic.
- Download delivery extracts browser-managed, picker-streaming, and object-URL
strategies behind the existing facade.
Resumable upload, websocket, reconnect, stream coordinator, HTTP V3, and
Browser RPC remain intact unless a behavior test demonstrates an independent
change reason. File length alone is not a split criterion.
## Error handling
- Active correctness failures use stable failure kinds and preserve effect
certainty.
- Mandatory release evidence, file inventory, schema, or digest failures fail
closed.
- Optional diagnostics cannot change command, cache, or release outcomes.
- Cleanup continues through all participants but publishes no READY state after
any mandatory failure.
- Tool crashes, null exit status, signals, and timeouts never count as expected
negative-fixture rejection.
## Testing and verification
Every behavior change follows red-green-refactor. The minimum regression set
includes:
- Actual `bindQuery` keys for local, remote, fan-out, and generation-isolated
invalidation.
- Independent runtime intents, same-submit retry identity, missing-key
pre-dispatch rejection, and secret-free diagnostics.
- Optimistic NOT_APPLIED, APPLIED_CONFIRMED, and MAYBE_APPLIED settlement,
including out-of-order layers and reconciliation.
- V1/V2 release coherence and every contract-set tamper in both verifier and
rollback drill.
- Provider evidence absence, digest mismatch, post-attestation rebuild, and a
valid immutable promotion fixture.
- Required-root and unreadable-file failures, schema drift, invalid evidence,
and module-inventory hash mismatch.
- Complete V3 response/effect scenario execution and production read/write E2E.
- Headerless oversized and non-terminating Service Worker marker streams.
- Shared persisted-row acceptance/rejection across IndexedDB runtime and
maintenance before extraction.
Repository completion requires fresh evidence from type checking, linting,
non-browser tests, coverage, architecture, build, release verification, CI
contract checks, browser capabilities, E2E, accessibility, visual tests where
the environment supports them, schema parity, and diff hygiene. Unsupported
browser gates are reported explicitly and are never claimed as passing.
## Acceptance criteria
- The existing release/boot and scope-generation work passes its original
focused suites before remediation contracts are changed.
- Mutation success invalidates every matching bound query locally and remotely.
- No two independent logical commands reuse an idempotency key; missing KEYED
intent prevents network admission.
- MAYBE_APPLIED never causes arbitrary optimistic rollback, commit, retry, or
invalidation.
- Release promotion verifies and promotes the exact same immutable bytes.
- V2 rollback verification cannot bypass contract-set package or digest checks.
- Required files, artifact schemas, and manifest output hashes fail closed.
- Coverage and scenario gates measure production behavior rather than declared
subsets or source tokens.
- Production uses one executable HTTP contract registry and respects clean
architecture dependency direction.
- Service Worker activation is bounded and full-identity coherent before the
capability can become ACTIVE.
- Adapter splits preserve their public facade and pass shared golden tests.
@@ -0,0 +1,171 @@
# Runtime Integrity Refactor Design
## Purpose
Apply the repository-wide refactoring review without discarding the current
uncommitted implementation snapshot. The program closes release and boot
fail-open paths first, then makes runtime ownership explicit, and finally
consolidates tooling and large adapter modules.
## Chosen approach
Use a phased compatibility migration.
- Rejected: a big-bang V2-only rewrite. It removes legacy code quickly but
combines artifact, boot, HTTP, cache, and worker changes into one unsafe
release.
- Rejected: patch only the currently failing verifier. It makes one gate green
while leaving cross-version boot acceptance, schema drift, and duplicate
execution contracts intact.
- Chosen: introduce explicit V1/V2 discriminated contracts, route every writer
and reader through them, migrate production execution onto one registry, and
remove legacy bridges only after each vertical is covered by tests.
## Program boundaries
The work is split into independently testable sub-projects.
1. Release artifact integrity
- One executable schema for release, runtime-config, and build artifacts.
- Version-specific token projection.
- Real build-to-verification integration coverage.
2. Boot protocol integrity
- Exact supported version selection.
- V1/V1 and V2/V2 pairing only.
- Mandatory V2 contract-set verification and HTTP(S) endpoint policy.
3. Scope-owned server state and HTTP contract execution
- A scope generation owns QueryClient, mutations, optimistic state, and
cross-context resources.
- The composed external-contract registry is the only executable HTTP
registry.
4. Optional runtime and Service Worker lifecycle
- Generation-fenced start/stop.
- Nonce-based activation/reset acknowledgements.
- Bounded install streams and one verified rollback revision.
5. Quality infrastructure
- Typed subprocess results, bounded CI steps, generated artifact schemas,
one authoritative architecture analyzer, and representative coverage.
6. Adapter hardening and decomposition
- Shared bounded-body and worker-RPC primitives.
- IndexedDB singleflight.
- Route-policy closure, invalidation tuple identity, and focused extraction
from the largest adapter runtimes.
## Release artifact architecture
`scripts/contracts/release-artifacts.ts` owns Zod schemas for Release Manifest
V1/V2, Runtime Config V1/V2, and Build Manifest V1. Writers parse before writing;
readers parse before comparing. JSON Schema files are generated views and never
an independent source of truth.
`projectReleaseTokens(document)` maps a parsed artifact into comparison tokens.
V1 projects the legacy API contract version. V2 projects
`contractSet.setDigest` as `contractSetDigest` and never requires the removed
scalar. Token comparison receives projected values, not arbitrary records.
## Boot protocol architecture
The version selector accepts only `"1"` and `"2.0"`. Parsing preserves the
versioned shape through manifest loading:
```ts
type BootProtocol =
| { kind: "V1"; config: RuntimeConfigV1; manifest: ReleaseManifestV1 }
| { kind: "V2"; config: RuntimeConfigV2; manifest: ReleaseManifestV2 };
```
Mixed pairs fail before application composition. V2 requires a contract set and
verifies it exactly once. Endpoint protocols are `http:` or `https:` in local
and development, and `https:` elsewhere.
## Scope and execution ownership
A `ScopeGenerationBundle` owns every resource capable of retaining account data:
QueryClient, mutation admission, optimistic layers, cross-context invalidation,
and optional closers. Transition order is fixed:
1. publish FENCED and render only the transition surface;
2. close query and mutation admission;
3. abort in-flight work and roll back optimistic layers;
4. detach providers and close scoped transports;
5. clear and dispose the old QueryClient;
6. construct and mount a new bundle;
7. publish READY.
Mandatory cleanup failure remains terminal/FENCED. It never publishes READY.
The composed contract-contribution registry becomes the only HTTP operation
registry. Feature gateways resolve installed operations and map executor
outcomes; they do not carry parallel schemas or retry definitions.
## Optional runtime and worker lifecycle
Optional hosts use explicit lifecycle states plus a generation token. Every
continuation after an await verifies that generation. Stop closes admission,
waits for pending startup to settle, and disposes children in reverse order.
Service Worker activation and reset messages are discriminated by kind and
carry source build, target build, nonce, and a typed result. Activation calls
`skipWaiting()` only after every controlled client acknowledges the same nonce.
Install owns a deadline AbortController, reads at most declared bytes plus one,
aborts siblings on first failure, and atomically records current and previous
verified asset digests.
## Tooling and schema policy
Subprocesses return `SUCCESS`, `EXPECTED_DIAGNOSTIC`, `TOOL_FAILURE`, `SIGNAL`,
or `TIMEOUT`. Negative fixtures must match expected diagnostic identifiers;
arbitrary non-zero exits are failures. CI gates have per-step timeouts and are
generated or structurally checked against `config/ci/gates.json`.
The TypeScript-aware custom source analyzer becomes authoritative unless a
dependency-cruiser version with TypeScript 7 support is selected. A zero-module
analysis is always a gate failure. Concrete adapter-to-adapter dependencies are
replaced by injected ports or explicitly named shared infrastructure.
## Error handling
- Artifact or boot schema mismatches use stable, version-specific failure codes.
- Unknown future versions fail closed and do not fall back to V1.
- Mandatory scope cleanup and worker protocol failures remain terminal.
- Transport readers return closed failure unions; `UNKNOWN` is not a success
fallback.
- Tool invocation failures are never accepted as expected fixture rejection.
## Testing strategy
Every behavior change follows red-green-refactor. Required matrices include:
- Release V1/V2 success plus field, digest, package, and schema tampering.
- Boot V1/V1 and V2/V2 success; mixed and future versions fail.
- Synchronous old-data hiding, late completion fencing, and cleanup failure.
- Old-page/new-worker activation, partial acknowledgement, reset results, and
install deadline/body limits.
- Process spawn error, null status, signal, timeout, and expected diagnostic.
- IndexedDB concurrent open, worker RPC crash/abort, and composite-key collision.
Repository-wide verification includes types, lint, unit/component/integration,
browser capability tests where supported, build, release verification, schema
round-trip, architecture analysis, and diff hygiene.
## Delivery order and compatibility
Release artifact integrity and boot protocol integrity ship first and retain a
read-only V1 window. Scope ownership and HTTP registry consolidation ship next.
Optional runtime, quality infrastructure, and adapter decomposition follow as
separate reviewable changes. Public facades remain stable during internal file
splits; removal of legacy exports occurs only after static usage checks reach
zero.
## Acceptance criteria
- A clean V2 build passes release verification; every supported tamper fails.
- No accepted V2 boot bypasses contract-set verification.
- FENCED renders no previous-account data and late work cannot mutate a new
generation.
- Selected optional capabilities have a concrete host or fail composition.
- Negative quality fixtures cannot pass because a tool crashed or timed out.
- Checked-in JSON schemas exactly match executable schemas.
- Architecture analysis covers a non-zero complete module graph.
- No source file contains an actual NUL byte, and all final verification gates
report their real status.
@@ -0,0 +1,464 @@
# Platform-owned Frontend Assurance and Delivery Design
## Purpose
This design moves test assurance and CI/CD orchestration to the two platforms
that own those responsibilities while keeping product behavior and product
tests in the frontend template. It covers the complete vertical path from risk
selection through immutable static-site promotion.
The three repositories are:
- `/home/donghyeon/workspace/desktop-server-git/test-assurance-platform`
- `/home/donghyeon/workspace/desktop-server-git/cicd-platform`
- `/home/donghyeon/workspace/desktop-server-git/clean-architecture-frontend-template`
The selected approach is platform-native integration. A CI-first translation
bridge and a test-assurance-only migration were rejected because each would
leave one platform temporarily owning semantics assigned to the other.
## Authority boundaries
### Frontend product repository
The frontend repository owns:
- production source and product contracts;
- test implementations, fixtures, mocks, scenario catalogs, and assertions;
- package scripts that run one declared suite;
- raw product-specific reports and codecs for product artifacts;
- repository declarations: risks, obligations, suites, change surfaces, build
components, outputs, and selected platform capabilities.
It does not own remote scheduling, pipeline DAG generation, evidence
assessment, provider orchestration, retention, signing, or promotion.
### Test assurance platform
The test assurance platform owns:
- risk-to-obligation selection;
- scheduler-neutral execution plans;
- suite execution contracts and bounded local execution;
- report normalization and false-green prevention;
- evidence digest chains and obligation assessment;
- flaky, quarantine, waiver, and capability-readiness semantics.
It does not own runner allocation, workflow fan-out/fan-in, build publication,
release approval, or promotion.
### CI/CD platform
The CI/CD platform owns:
- required workflows, source materialization, runner trust, and toolchain pins;
- remote projection of test-assurance work items;
- install, lint, typecheck, deterministic build, and artifact publication;
- immutable candidate assembly and artifact identity;
- vulnerability, SBOM, provenance, signature, and provider evidence;
- release approval, promotion, retention, and terminal status publication.
It consumes test-assurance results without reinterpreting their test meaning.
## End-to-end authority flow
```text
frontend declarations and tests
-> testctl validate/lock/compile/select/plan
-> CI projects source work items to untrusted runners
-> testctl normalize/bundle/assess source results
-> CI builds one immutable frontend candidate
-> CI supplies the candidate digest to artifact-bound work items
-> testctl normalize/bundle/assess artifact results
-> CI obtains vulnerability/SBOM/provenance/signature evidence
-> release-control promotes the exact assessed candidate
```
Every transition carries canonical identities. No stage may replace a missing
identity with a path, timestamp, branch name, or mutable tag.
## Test assurance platform changes
### Frontend capability family
Add the following independent capabilities:
- `unit-typescript-vitest`
- `component-react-vitest`
- `integration-http-msw`
- `architecture-typescript`
- `coverage-v8`
- `e2e-playwright-firefox`
- `e2e-playwright-webkit`
- `accessibility-web`
- `visual-regression-web`
The existing `e2e-playwright-chromium` capability is upgraded to the same
contract revision. Browser capabilities retain separate readiness cards; no
aggregate frontend-readiness card or score is introduced.
Each capability defines its own artifacts, timeouts, isolation, false-green
rules, max evidence age, and minimum readiness. A repository obligation may
require all three browser capabilities without merging their readiness.
### Toolchain catalog
Create a new catalog revision containing the frontend template's supported
toolchain:
- Node.js `24.14.0`
- pnpm `11.17.0`
- TypeScript `7.0.2`
- Vitest `4.1.10`
- Playwright `1.62.0`
The exact installed versions in `package.json`, `.nvmrc`, and the lockfile are
validated against the catalog. The lock generator records immutable artifact
digests. A missing digest or version mismatch is `TA-LOCK-003`/exit 30 and does
not fall back to the host toolchain.
### Adapters and normalizers
Add adapters and normalizers for:
- Vitest JUnit XML and Vitest-discovered test counts;
- V8 coverage summary plus the repository production-module inventory;
- typed HTTP scenario execution receipts;
- TypeScript architecture graph and violation JSON;
- Playwright JSON/JUnit, trace, screenshot, console, and network summaries;
- accessibility findings and manual-review records;
- visual baseline identity and image-diff results;
- production-shaped read/write E2E mutation receipts.
Normalizers validate regular non-symlink files, bounded byte sizes, fatal UTF-8,
strict schemas, canonical repository-relative paths, and report-specific
cross-field invariants.
### False-green rules
The following outcomes can never normalize to PASS:
- zero discovered tests or an all-skipped required suite;
- a pass created only by retry;
- a missing, empty, malformed, oversized, or mismatched report;
- a declared HTTP scenario without an exact executed receipt;
- a missing required browser project;
- a non-empty source tree with zero architecture modules or dependencies;
- an unresolved import or dependency cycle;
- a production module absent from the coverage universe;
- a browser write test without observed response, mutation, and reload reads;
- an accessibility or visual result whose baseline/provider identity is absent.
Negative product fixtures may be schema-valid FAIL evidence; command outcome and
assessment, not artifact shape alone, determine satisfaction.
### Artifact-bound execution contract
The current v2 execution schemas cannot bind a work item to an immutable build
input. The platform therefore adds v3 execution contracts rather than silently
changing v2 semantics.
`SuiteDefinition` and `WorkItem` gain required fields:
```text
executionPhase: SOURCE | ARTIFACT
requiredInputArtifacts[]:
artifactId
mediaType
sha256
```
An artifact-bound `ExecutionRequest` carries the same artifact references. The
plan, raw result set, normalized result, evidence bundle, and assessment all
bind the input artifact tuple. Evidence from v2 and v3 cannot be merged.
The platform dual-reads existing v2 JVM manifests during migration. New
frontend capabilities require v3, and all new platform outputs are v3. There is
no implicit phase default.
### Artifact suite declaration amendment
A repository cannot know the SHA-256 of a candidate that CI has not built yet.
The repository contract therefore separates a static declaration from an
executable suite:
- `ArtifactSuiteTemplate` is repository-owned and declares the suite command,
raw artifacts, `executionPhase: ARTIFACT`, and required input artifact IDs
and media types. Its schema forbids `sha256` and digest placeholders.
- `SuiteDefinition` remains the executable v3 contract required above. For an
artifact suite it always contains the concrete `requiredInputArtifacts`
tuple including SHA-256.
- after `ci-frontend` freezes the candidate, CI creates the artifact
`ExecutionRequest` with that candidate tuple; testctl matches it to the
template and materializes the executable `SuiteDefinition` and `WorkItem`.
CI supplies artifact identity but does not construct or reinterpret test suite
semantics. An unmatched artifact ID/media type, an unresolved template, or a
digest in a committed template stops planning. Templates never enter raw,
normalized, evidence, or assessment documents.
## CI/CD platform changes
### Test-assurance integration
Add `ci-test-assurance`. It depends on `ci-standard-core` and invokes a
digest-pinned `testctl` distribution using argument arrays only.
Its responsibilities are:
1. validate and lock the repository assurance manifest;
2. compile policy and create source and artifact execution requests;
3. obtain deterministic plans;
4. project `execute-one` work items to the required trust partition;
5. preserve plan digest, work-item ID, attempt, exit code, and raw artifacts;
6. call testctl normalization, bundling, and assessment;
7. expose only canonical assessment and evidence digests downstream.
CI may choose runner parallelism but may not change selection, retry, timeout,
expected artifacts, status, or obligation satisfaction. Missing work-item
results are platform defects, never successful no-ops.
### Node and frontend capabilities
`ci-node-typescript` owns reproducible pnpm install, declared lint, and declared
typecheck. It no longer executes or assesses unit/coverage suites when
`ci-test-assurance` is selected.
`ci-frontend` owns one deterministic build of the selected source revision and
verifies:
- a non-empty static output;
- byte-identical rebuild evidence in an isolated verification workspace;
- absence of undeclared environment and build-host values;
- declared size budgets;
- a canonical tree digest and archive manifest.
The candidate consumed after this point is the first verified candidate. The
verification build proves determinism but is never promoted.
### Release capability family
Add the following capabilities:
- `ci-dependency-vulnerability`
- `ci-artifact-signing`
- `ci-static-artifact-supply-chain`
- `ci-static-site-publish`
Generalize existing `ci-sbom` and `ci-provenance` subject contracts so that a
static archive is a supported immutable subject without weakening their
container behavior. `ci-static-artifact-supply-chain` is a composite that
references, rather than copies, child evidence.
`delivery-release-control` promotes the exact candidate whose digest appears in
the signed release identity. It may not build, modify, or repackage the
candidate. Static-site publication returns provider and served-content digests;
both must equal the approved subject before promotion succeeds.
### Release identity
The signed release identity is split into two immutable documents so that one
candidate can be promoted to more than one environment without rewriting its
candidate manifest:
- `ReleaseManifestV2` binds the candidate, test, supply-chain, policy,
toolchain, and platform identities;
- `PromotionSubject` binds the release-manifest digest to the target
environment, approval identity/policy, confirmed publication, and expected
Git CAS state.
Together they bind:
```text
source revision
candidate archive SHA-256 and canonical member manifest
test-assurance manifest and policy digests
source plan, evidence, and assessment digests
artifact plan, evidence, and assessment digests
dependency vulnerability, SBOM, and provenance digests
signature key ID and signature digest
CI policy, capability-registry, toolchain, and platform-catalog digests
target environment and approval identity
```
Promotion history is an append-only record and is not a mutable field inside
`ReleaseManifestV2`.
A report for another candidate, a rebuilt candidate, a changed policy, an
expired approval, or an unconfirmed provider mutation blocks promotion.
### Required workflow
The centrally installed required workflow remains a thin bootstrap. It contains
no language, test, build, provider, or promotion logic. It materializes the
exact source revision, verifies the signed platform catalog, invokes pinned
`cicdctl`, and publishes one terminal sentinel.
Product repositories do not copy this workflow.
## Frontend consumer contract
The frontend repository adds:
- `delivery-platform.yaml`;
- `test-assurance.yaml`;
- a generated `test-assurance.lock.json`;
- risk, obligation, suite, and change-surface documents under
`config/test-assurance/`.
`delivery-platform.yaml` selects the core, Node, frontend, test-assurance,
dependency vulnerability, SBOM, provenance, signing, static supply-chain,
static publication, and release-control capabilities. It pins the signed
platform version.
Suite definitions reference existing product-owned package scripts. One suite
definition executes one bounded test purpose and declares its raw artifacts.
The repository does not wrap several assurance decisions in one script.
The product retains:
- production runtime and adapter tests;
- Vitest and Playwright configuration;
- mocks, scenarios, fixtures, and browser assertions;
- V8 instrumentation configuration;
- product artifact codecs such as runtime/release manifest schemas.
It removes after cutover:
- the copied `.gitea/workflows/quality-gates.yml`;
- `config/ci/gates.json` and its runner/generator/checker;
- local risk selection, waiver, normalization, and assessment engines;
- local provider, retention, promotion, and CI evidence orchestration;
- package scripts whose only purpose is to reproduce platform policy.
## Treatment of completed and in-progress frontend work
Runtime production changes and their product tests remain unchanged.
Repository-wide V8 instrumentation, HTTP scenario execution, and browser
read/write assertions remain as product test inputs. Their local selection,
reconciliation, evidence assessment, and waiver logic moves to test assurance.
Product runtime/release artifact codecs remain local. Supply-chain provider
policy, archive transfer, signing, retention, and promotion move to CI/CD.
The uncommitted Task 3 worktree is not reset or overwritten. Before migration,
every changed path is classified as product-owned, test-assurance-owned,
CI/CD-owned, or unrelated/user-owned. Reusable validators and adversarial tests
move to their owning platform through explicit patches. Unrelated and
origin-unknown changes are preserved.
## Migration sequence
### Phase 1: Test assurance capability readiness
Implement v3 contracts, frontend capabilities, adapters, normalizers, locks,
positive fixtures, adversarial fixtures, and independent readiness cards. Each
new capability reaches at least R1 before a consumer may select it in shadow.
### Phase 2: CI/CD integration and immutable release
Implement pinned testctl integration, source/artifact work-item projection,
frontend candidate identity, provider evidence, signing, static publication,
and release-control binding. Each CI capability receives independent P1
evidence; activation remains shadow.
### Phase 3: Consumer declarations
Add both manifests and the assurance declarations to the frontend repository.
Map existing product suites to capabilities without deleting the legacy path.
Validate all manifests with the released platform binaries.
### Phase 4: Shadow parity
Run legacy and platform paths against the same source revision. Compare:
- selected suites and discovered/executed counts;
- PASS, FAIL, FLAKY, INCOMPLETE, and platform-defect classification;
- coverage production-module universe;
- HTTP scenario declared/executed identities;
- Chromium, Firefox, and WebKit results;
- candidate archive and member digests;
- provider evidence and promotion readiness.
The platform path is the only candidate producer in shadow. Legacy release and
promotion commands become read-only comparison probes. No two writers may
publish or promote.
Before shadow execution, the legacy workflow registration is disabled and its
required status is detached. The centrally installed workflow runs in shadow,
and its runner may invoke legacy test/evidence commands only as read-only
comparison probes. Observed state records zero legacy candidate/provider/
promotion invocations and exactly one platform candidate writer.
### Phase 5: Cutover
After parity and fault tests pass, activate the platform capabilities, attach
the central required workflow/status, and remove copied workflow and local
policy engines. Product tests and declared suite commands remain.
## Failure and rollback semantics
- Unsupported capability or unavailable toolchain is explicit UNSUPPORTED or
INCOMPLETE, not a skipped pass.
- Schema-major mismatch stops before execution and never invokes a local
fallback.
- Missing or corrupt raw results stop normalization.
- Provider timeout, response loss, or digest mismatch blocks promotion and is
reconciled by operation ID where mutation may have occurred.
- A candidate, source assessment, or artifact assessment digest change
invalidates approval.
- A missing terminal sentinel blocks the required status.
Rollback changes only the repository's signed `platformVersion`/catalog pin to
the previous proven release and restores the previous capability activation.
It never restores a copied product workflow. Release rollback promotes the
previous stable immutable subject through release-control.
## Verification strategy
### Test assurance platform
- schema positive and adversarial corpus;
- compiler/selector/plan determinism;
- each normalizer's valid, malformed, missing, empty, oversized, symlink, and
cross-field cases;
- zero-discovery, all-skipped, retry, browser-matrix, coverage-omission,
architecture-empty/cycle, scenario-omission, and mutation-receipt fixtures;
- v2/v3 isolation and digest-chain tests;
- full conformance chain and Gradle verification.
### CI/CD platform
- manifest compiler and capability dependency/activation tests;
- pinned testctl invocation and exit-code preservation;
- fan-out completeness and missing-result fault tests;
- deterministic static candidate and exact-member archive tests;
- provider timeout, invalid signature, changed digest, response-loss, and stale
approval tests;
- required workflow thinness and exact required-status tests;
- registry, boundary, fixture, fault, and readiness verification.
### Frontend consumer
- manifest validation with released platform binaries;
- existing focused product tests;
- platform shadow run against the actual repository;
- semantic parity and digest reports;
- one-writer and rollback drills;
- removal tests proving the template works without copied workflow or local
policy engines.
## Acceptance criteria
- Test selection and assessment have one authority: test assurance.
- CI orchestration and promotion have one authority: CI/CD.
- The frontend repository contains product tests and declarations, not copied
platform engines.
- Every required frontend capability has independent readiness evidence.
- Source and artifact test evidence bind the exact revision and candidate.
- The promoted static site is byte-identical to the assessed, signed candidate.
- Missing evidence, unsupported capability, platform failure, and provider
uncertainty cannot produce a passing required status.
- Cutover and rollback require no copied workflow logic in the product
repository.
@@ -0,0 +1,222 @@
# Provider Evidence Guardian Transaction Design
## Goal
Make one guardian process own the provider evidence filesystem transaction from
raw creation through sealed publication. A supervisor or provider hard death
must leave neither canonical raw evidence nor a guardian-owned sealed temp/final
inode, and the same workspace must be immediately retryable. Only the complete
authenticated `publish -> PUBLISHED -> commit -> EOF` sequence preserves the
canonical sealed artifact.
## Chosen Ownership Boundary
The guardian owns filesystem identity and publication. The supervisor retains
archive, trust, schema, signature, and evidence validation. This avoids two
unsafe alternatives:
- Keeping raw-only guardianship would leave the sealed rename-to-supervisor-death
cleanup gap.
- Moving evidence validation into the guardian would duplicate security policy
and make the helper unnecessarily privileged and complex.
The client opens the canonical `provider-evidence/untrusted` and
`provider-evidence` directories with `O_DIRECTORY|O_NOFOLLOW` before spawning
the guardian. Those identity-pinned directory descriptors are inherited as fd 3
and fd 4; they are never encoded in argv or the environment. The canonical raw
and final leaves are fixed by provider kind. Before spawn, the client exclusively
creates a nonce-private raw staging inode and the nonce-private sealed temp
inode, records both identities, and inherits their open descriptors as fd 5 and
fd 6. The guardian validates each inherited descriptor against its
descriptor-relative pathname, then publishes raw staging to the fixed raw leaf
with a no-replace hard link. Startup recovery authority is therefore an inode
identity acquired before spawn, never an identity discovered later from an
expected pathname. Both processes perform transaction I/O through
`/proc/self/fd/<fd>/<leaf>` so pathname substitution cannot redirect creation or
recovery into another directory. No raw path, sealed path, identity, provider
command, or credential is an argv value. The initial request contains only the
version, kind, random control nonce, and absolute deadline.
## Transaction Invariants
1. Before a complete valid guard frame, the guardian has not published a
canonical filesystem object. The client may have allocated only zero-byte,
mode `0600`, nonce-private raw staging and sealed temp inodes whose identities
it already holds. EOF with no frame or a partial frame removes both allocations.
2. Before spawning, the client validates that both pinned descriptors name the
expected canonical directories; computes fixed raw/final leaves and
nonce-private raw-staging/sealed-temp leaves; and creates the private leaves
with `O_CREAT|O_EXCL|O_NOFOLLOW`, mode `0600`, size zero, and link count one.
It retains both handles and inherits them as fd 5/fd 6 in addition to directory
fd 3/fd 4.
3. At bootstrap, the guardian fstats fd 5/fd 6, reads only their
`/proc/self/fd/5|6` link targets, and accepts each basename only when it is a
direct child of the canonical fd 3/fd 4 directory and matches the exact
provider-kind/32-lowercase-hex private-leaf grammar. It then requires
descriptor-relative lstat of that basename to match the already-fstat fd
identity, type, mode, size, and link count. This binds a deletion alias to an
inherited identity; it never promotes a pathname-discovered identity to
ownership. The two basenames must encode the same kind and nonce prefix.
4. After guard validation, the guardian verifies that the received kind/nonce
derives those exact bootstrapped private leaves. It verifies fd 5/fd 6 remain
regular zero-byte single-link `0600` files and exactly match the derived
private pathnames. It
uses `link(raw staging, canonical raw)` without replacement, verifies both
names have the inherited raw identity and link count two, unlinks the private
raw name, fsyncs the raw directory, and verifies the canonical raw link count
is one. READY is emitted only after this authority transfer succeeds.
5. READY is authenticated by the request nonce and returns raw dev/inode plus
sealed temp leaf/dev/inode. The supervisor starts the provider only after it
validates this exact bounded response with constant-time nonce equality.
6. The supervisor writes only schema-validated sealed bytes to the temp inode.
It opens with `O_NOFOLLOW`, checks dev/inode before and after writing, applies
mode `0400`, writes the complete bounded bytes, fsyncs, and closes.
7. Publish metadata contains the nonce, sealed dev/inode, byte length, and
SHA-256. The guardian checks the held descriptor and temp pathname identity,
regular-file type, link count, exact mode/size/hash, and canonical final-path
absence.
8. Publication uses atomic no-replace `link(temp, final)`, then unlinks temp and
fsyncs the parent directory. If death occurs between link and unlink, both
names refer to the same pinned inode and both are cleanup candidates.
9. PUBLISHED is authenticated and is emitted only after final pathname identity
and directory durability are verified.
10. Commit is legal only after PUBLISHED. It removes the pinned raw inode and
enters `commitPending`; it does not exit. EOF with no pending bytes is the
sole success terminal and preserves only the sealed final inode.
11. Any data after commit, including a separate later chunk, is a protocol error.
EOF/abort/deadline/protocol failure before the success terminal cleans raw,
temp, and final only when each path still names the guardian-owned identity.
12. If the guardian dies before READY is accepted, the client attempts cleanup
of raw staging, canonical raw, sealed temp, and sealed final aliases using
only the two identities recorded before spawn. A current pathname is never
opened and promoted to an owned identity. A competing canary or same-kind
transaction therefore survives every startup failure.
13. Cleanup attempts every owned target and reports cleanup failures together
with the primary failure using `AggregateError` at the supervisor boundary.
Client fd 3-fd 6 handles and guardian fd 5/fd 6 duplicates are closed on
every success and failure branch; close errors join the same aggregate rather
than skipping remaining cleanup.
Client-side exclusive private allocation is the startup ownership token. The
guardian accepts that token only after inherited-fd, descriptor-relative
pathname, type, mode, size, and link-count checks. Every cleanup identity is
recorded at allocation or authenticated READY; pathname discovery never creates
authority. Creation, validation, link, unlink, chmod, fstat, close, publish,
sync, and cleanup failures all fail closed.
## Bounded Authenticated Protocol
Every control or acknowledgement message is a four-byte big-endian length plus
canonical JSON with an exact ordered field set, strict UTF-8, no NUL, and a
total payload bound. Unknown, duplicate, reordered, oversized, truncated, or
trailing fields are rejected.
The state sequence is:
```text
guard -> READY(raw identity, sealed temp identity)
-> publish(size, sha256, sealed identity)
-> PUBLISHED(sealed identity)
-> commit
-> EOF success
```
All messages carry the same 32-byte random nonce. READY and PUBLISHED are
validated with `timingSafeEqual`; publish and commit are authenticated the same
way. Commit merely changes state, so a byte delivered in a later chunk before
EOF remains observable and causes fail-closed cleanup.
The maximum initial lease is the provider wall timeout plus a fixed ten-minute
post-processing allowance. The provider timeout remains bounded at 30 minutes,
so the guardian maximum is 40 minutes. Near-provider-timeout tests must show
that valid publication still has post-processing time, while an expired lease
cleans all owned objects.
## Supervisor and Scope Exit Ownership
The lease exposes raw/temp/final identities, `publish(bytes)`, `commit()`,
`abort()`, and a non-rejecting premature-exit promise. The client knows all
possible leaves and both startup identities before spawn and retains its pinned
directory and private-file handles until the lease terminates. Before READY it
cleans only aliases that still match those recorded identities. After READY it
checks the guardian response against the same identities and fallback-cleans
raw, temp, and final by identity if the guardian dies.
Provider waiting owns an explicit `scopeActive` latch. A guardian exit starts
whole-scope kill and collection only while that latch is true. Once the scope
completion path has collected the unit, the callback records a lifecycle error
but cannot start an unawaited kill. Publication and terminal commit observe the
guardian exit through their normal awaited failure path and clean sealed state.
Provider stdout and stderr are untrusted secret-bearing byte streams. The
supervisor counts and bounds them for resource enforcement but never forwards
their raw bytes into supervisor/CI stdout or stderr, on either success or
failure. Functional provider assertions use signed evidence or a non-log side
channel. Sealing/output I/O is allowed to settle; the design does not claim
OS-level cancellation. `GITHUB_OUTPUT` is a runner-owned regular file. After
output append succeeds, commit makes the guardian remove raw and EOF completes
the transaction.
Guardian diagnostics are best-effort only. A closed stderr or control descriptor
must not turn a fail-closed branch into a resolved operation or exit zero:
diagnostic and fd-close failures are absorbed after cleanup, and a nonzero exit
or requested fatal signal is issued unconditionally.
## Failure and Recovery
- No/partial guard EOF: no canonical raw or sealed object is published. The
guardian removes both nonce-private allocations through aliases that bootstrap
already bound to inherited fd identities, without needing kind/nonce from a
complete control frame.
- A competing canonical raw canary or another same-kind attempt causes
no-replace link failure. The loser removes only its private identities and
never removes the winner or canary.
- Guardian death after linking raw but before READY: the client uses its
pre-recorded raw identity to clean both private and canonical aliases and its
pre-recorded sealed identity for temp/final aliases, then retries the same
workspace immediately.
- Parent death after creation but before READY: stdout/control pipe failure or
EOF makes the still-running guardian clean both owned objects.
- Guardian death after READY: the supervisor knows raw and sealed identities and
cleans raw, temp, and final fallbacks.
- Supervisor death after PUBLISHED: guardian EOF cleans raw and the published
final inode, including the link/unlink intermediate state.
- Publish or commit race: serialized guardian state completes the current file
operation, then applies EOF/protocol failure cleanup; success requires clean
EOF after commitPending.
- Cleanup failure: remaining targets are still attempted and every error is
preserved; PASS is impossible.
There is one bounded crash window before spawn: if the client itself is killed
after private allocation but before the guardian is created, zero-byte `0600`
nonce-private leaves can remain. They contain no provider or credential bytes
and cannot occupy the fixed canonical raw/final names, so they do not block an
immediate same-kind retry. Automatic pathname sweeping is intentionally omitted
because an unproven stale pathname is not deletion authority.
After each observable managed-process failure, tests require canonical raw,
private staging/temp, canonical final, guardian, and provider cgroup residual
counts to be zero before retrying the same workspace successfully. The
documented pre-spawn client hard-death window is the sole residual exception.
## Verification
Real-process tests cover no/partial frames, a competing raw canary, same-kind
concurrency, guardian `SIGKILL` after raw link but before READY followed by
same-workspace retry, parent death around READY, valid
READY identities, EOF/deadline cleanup, publish/PUBLISHED, post-scope guardian
death, supervisor death after publication, commit trailing bytes in a later
chunk, closed-stderr fail-closed termination, near-timeout publication, and no
residual guardian/files. A provider that successfully prints a supplied
credential is verified not to expose it through supervisor stdout/stderr. Live fixtures
also specify active-scope guardian kill, detached-child external marker/raw
append suppression, cgroup collection, and same-workspace retry. Live
systemd/bwrap execution remains explicitly unverified when the approval limit
prevents running it.
The external-canary regression waits for a test guardian spawn marker before
creating the fixed raw file, proving that the initial absence check has already
completed. The fixed raw bytes and dev/inode must remain unchanged after startup
rejection. The pre-READY link regression watches only the fixed raw basename,
kills the exact direct child on that link event, and requires identity-bound
cleanup plus an immediate same-workspace retry.
@@ -0,0 +1,47 @@
# V8 Coverage Counter Contract Design
## Goal
Version the serialized risk-coverage artifact independently from its policy and lock the repository's counter-bearing/counterless classifier to the output of the installed Vitest/V8 producer.
## Artifact contract
`config/testing/risk-coverage.json` remains policy schema version 2. `scripts/check-risk-coverage.ts` changes only its serialized output envelope to schema version 3 because the artifact fields were renamed from executable/non-executable terminology to `counterBearingTotal`, `instrumentedCounterBearingTotal`, `counterlessTotal`, and `counterlessModules`.
The contract test runs the real CLI against an owned temporary repository. It reuses the current policy, materializes its 19 policy-sensitive source paths as counter-bearing modules, writes an exact consistent coverage summary, and reads the published JSON artifact. It requires output schema version 3, the exact counter-bearing fields, and absence of every legacy executable/non-executable field.
## Producer microfixture
Repository fixtures under `tests/fixtures/v8-coverage-counter-semantics/` contain only source and a child test template. The child test file uses a `.fixture.ts` suffix and the main Vitest discovery exclusion is verified behaviorally so it cannot recursively join the repository suite.
At runtime, `scripts/check-v8-coverage-counter-semantics.ts` creates one owned directory below the operating-system temporary directory. It copies the fixed fixture into that directory and writes the child Vitest config there. The child process uses that directory as its root and writes its JSON summary below that same directory; it never writes repository coverage or artifact paths.
The fixture contains these exact source rows:
- `runtime.ts`: a runtime declaration/initializer; at least one standard counter total must be positive.
- `import-type-empty.ts`: `import type {}` only.
- `import-empty.ts`: `import {}` only.
- `import-side-effect.ts`: a bare side-effect import only.
- `import-value.ts`: a value import only.
- `reexport-named.ts`: a named value re-export only.
- `reexport-star.ts`: a star value re-export only.
- `type-only.ts`: type declarations only.
Every row except `runtime.ts` must contain exact `0/0/0/100` lines, statements, functions, and branches counters. The checker rejects a missing summary, missing or additional row, malformed counter, counterless nonzero drift, or runtime all-zero drift.
## Process and failure handling
The child Vitest process is launched without a shell or network. Exit failure is converted to a bounded diagnostic containing truncated stdout/stderr. File or JSON failures identify the missing or invalid summary without exposing unbounded child output. An outer `finally` removes only the exact owned temporary root for success and every failure path.
Pure summary validation is exported from a focused library and covered with literal mutation fixtures. Runner tests inject child exit or successful-without-summary behavior and assert cleanup. The real standalone checker executes in `test:coverage` before the repository coverage run, so FE-GATE-005 and sample removal both consume it through the existing package script contract.
## Documentation and evidence
The testing strategy is synchronized to the current 19 high-risk modules and 80 thresholds, documents output schema version 3, and retains policy schema version 2. Final evidence includes the focused unit/contract tests, node/test TypeScript, changed-file lint, standalone producer checker, root risk checker, sample removal, and diff validation.
## Self-review
- No placeholder or deferred choice remains.
- Policy schema 2 and artifact schema 3 are explicitly separate.
- All child-owned paths are below one temporary root and cleanup has one owner.
- Main discovery, subprocess failure, missing summary, exact rows, zero/nonzero drift, and bounded diagnostics have explicit verification paths.