chore: initialize from frontend template 4dc033c
This commit is contained in:
@@ -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.
|
||||
+464
@@ -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.
|
||||
Reference in New Issue
Block a user