docs: plan platform-owned frontend delivery
This commit is contained in:
@@ -0,0 +1,614 @@
|
||||
# CI/CD Frontend Assurance and Delivery Implementation Plan
|
||||
|
||||
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||
|
||||
**Goal:** Make `cicd-platform` the sole owner of frontend workflow orchestration, remote projection of test-assurance plans, deterministic candidate production, supply-chain/provider evidence, immutable publication, and exact-candidate promotion.
|
||||
|
||||
**Architecture:** `cicdctl` compiles typed stages and consumes the digest-pinned test-assurance v3 external contract without interpreting test reports. One source revision produces one verified candidate; artifact-bound testing and every supply-chain stage consume that candidate by SHA-256. `release-control` is the only mutating publication/promotion boundary. The centrally installed required workflow remains a four-step pinned bootstrap and is never copied into a product repository.
|
||||
|
||||
**Tech Stack:** Go, JSON Schema 2020-12, Gitea Actions, Node.js/pnpm runners, canonical SHA-256, Ed25519, content-addressed static publication.
|
||||
|
||||
## Global Constraints
|
||||
|
||||
- Repository: `/home/donghyeon/workspace/desktop-server-git/cicd-platform`; every command runs from its isolated worktree root.
|
||||
- Start only from the immutable Test Assurance Task 10 commit, its v3 distribution digest, and its v3 schema-bundle digest. No current v2 binary may satisfy this dependency.
|
||||
- Resolve Go 1.26.5 from `toolchains/toolchain-lock.yaml` into `.platform/toolchains/go-1.26.5/bin/go`, verify the distribution SHA-256 before extraction, and set `CICD_GO_BIN` to that absolute path. The host currently has no `go`; PATH fallback is forbidden.
|
||||
- CI may choose runner placement and parallelism but may not change test selection, timeout, retry, artifact requirements, result status, or obligation satisfaction.
|
||||
- Test raw reports remain opaque to CI; only testctl v3 plans, normalized results, evidence, assessments, IDs, digests, attempts, and exit codes are consumed.
|
||||
- The platform path is the only candidate producer in shadow. Legacy product release/promotion commands may only compare bytes and evidence read-only.
|
||||
- The first deterministic build is the candidate. The isolated comparison build is destroyed and cannot be promoted.
|
||||
- Exact new IDs are `ci-test-assurance`, `ci-dependency-vulnerability`, `ci-artifact-signing`, `ci-static-artifact-supply-chain`, and `ci-static-site-publish`.
|
||||
- Existing `ci-sbom` and `ci-provenance` gain static-archive subjects without weakening container subjects.
|
||||
- All new capabilities remain P1/shadow until named Gitea, runner, scanner, signer, and provider evidence supports P2.
|
||||
- Each P1 transition is atomic: canonical ID, descriptor/policy/acceptance/runbook, provider registration, readiness registry row, fixture, immutable evidence, `docs/decisions/readiness/<capability>-P1.yaml`, and `Makefile` capability run land in the same commit. A P0 capability may not have an active provider.
|
||||
- Rollback selects a previous signed platform catalog and immutable subject through platform control; it never restores a copied workflow or a legacy writer.
|
||||
|
||||
---
|
||||
|
||||
### Task 1: Define typed stage, artifact, and release-identity contracts
|
||||
|
||||
- [ ] Materialize the locked Go toolchain before writing tests. Run from the isolated CICD worktree (network download requires the normal escalation approval):
|
||||
|
||||
```bash
|
||||
mkdir -p .platform/downloads .platform/toolchains/go-1.26.5
|
||||
curl --fail --location --proto '=https' --tlsv1.3 https://go.dev/dl/go1.26.5.linux-amd64.tar.gz --output .platform/downloads/go1.26.5.linux-amd64.tar.gz
|
||||
printf '%s %s\n' '5c2c3b16caefa1d968a94c1daca04a7ca301a496d9b086e17ad77bb81393f053' '.platform/downloads/go1.26.5.linux-amd64.tar.gz' | sha256sum --check
|
||||
tar -xzf .platform/downloads/go1.26.5.linux-amd64.tar.gz --strip-components=1 -C .platform/toolchains/go-1.26.5
|
||||
export CICD_GO_BIN="$PWD/.platform/toolchains/go-1.26.5/bin/go"
|
||||
"$CICD_GO_BIN" version
|
||||
```
|
||||
|
||||
Expected: exact `go version go1.26.5 linux/amd64`. Stop on download/digest mismatch; never use `/usr/bin/go` or another PATH binary.
|
||||
|
||||
**Files:**
|
||||
|
||||
- Modify: `packages/contracts/models.go`
|
||||
- Modify: `packages/contracts/schema.go`
|
||||
- Preserve unchanged: `contracts/schemas/execution-plan.schema.json` and `contracts/schemas/release-manifest.schema.json` v1 contracts
|
||||
- Create: `contracts/schemas/execution-plan-v2.schema.json`
|
||||
- Create: `contracts/schemas/release-manifest-v2.schema.json`
|
||||
- Create: `contracts/schemas/promotion-subject.schema.json`
|
||||
- Modify: `contracts/schemas/delivery-platform.schema.json`
|
||||
- Create: `contracts/schemas/artifact-reference.schema.json`
|
||||
- Create: `contracts/schemas/candidate-bundle.schema.json`
|
||||
- Create: `contracts/schemas/test-assurance-result.schema.json`
|
||||
- Create: `contracts/schemas/supply-chain-evidence.schema.json`
|
||||
- Create: `contracts/schemas/publication-record.schema.json`
|
||||
- Sync generated copies under: `packages/contracts/schemas/`
|
||||
- Modify: `packages/contracts/schema_test.go`
|
||||
- Modify: `packages/canonicalid/id.go`
|
||||
- Modify: `packages/canonicalid/id_test.go`
|
||||
- Create: `contracts/examples/valid/execution-plan-v2.json`
|
||||
- Create: `contracts/examples/valid/release-manifest-v2.json`
|
||||
- Create: `contracts/examples/valid/promotion-subject.json`
|
||||
- Create: `contracts/examples/invalid/13-v2-stage-with-shell-payload.json`
|
||||
- Create: `contracts/examples/invalid/14-release-manifest-with-promotion-state.json`
|
||||
- Modify: `contracts/snapshots/` only by adding a new v2 snapshot set; do not rewrite `contracts/snapshots/v1/`
|
||||
- Modify: `contracts/readiness-cards.yaml`
|
||||
- Create P0 directories: `capabilities/ci-test-assurance/`, `capabilities/ci-dependency-vulnerability/`, `capabilities/ci-artifact-signing/`, `capabilities/ci-static-artifact-supply-chain/`, `capabilities/ci-static-site-publish/`
|
||||
|
||||
**Interfaces:**
|
||||
|
||||
```go
|
||||
type StageInvocation struct {
|
||||
Kind string // internal | platform-adapter | external-contract
|
||||
Engine string // cicdctl | release-control | test-assurance | provider
|
||||
AdapterID string
|
||||
Operation string
|
||||
Payload json.RawMessage // validated by the adapter/operation-specific schema
|
||||
WorkItemID string
|
||||
Inputs []ArtifactReference
|
||||
Outputs []ArtifactDeclaration
|
||||
}
|
||||
|
||||
type TestAssuranceReference struct {
|
||||
APIVersion string
|
||||
ManifestDigest string
|
||||
PolicyDigest string
|
||||
ExecutionPhase string
|
||||
PlanDigest string
|
||||
EvidenceDigest string
|
||||
AssessmentDigest string
|
||||
InputArtifacts []ArtifactReference
|
||||
}
|
||||
|
||||
type PromotionSubject struct {
|
||||
ReleaseManifestDigest string
|
||||
TargetEnvironment string
|
||||
ApprovalID string
|
||||
ApprovalPolicyDigest string
|
||||
PublicationRecordDigest string
|
||||
ExpectedGitRevision string
|
||||
ExpectedTreeDigest string
|
||||
}
|
||||
```
|
||||
|
||||
`ReleaseManifestV2` is immutable and carries separate source and artifact `TestAssuranceReference` values plus source revision, candidate archive/member-manifest digests, dependency-vulnerability/SBOM/provenance/signature digests, and platform policy/catalog/toolchain digests. Target environment, approval, publication, and Git CAS belong only to `PromotionSubject`; promotion history never rewrites the signed release manifest. The combined signed release identity is `ReleaseManifestV2 + PromotionSubject`.
|
||||
|
||||
Canonical kinds are fixed: `ci-test-assurance` is `KindComposite`; `ci-dependency-vulnerability`, `ci-artifact-signing`, and `ci-static-site-publish` are `KindArtifact`; `ci-static-artifact-supply-chain` is `KindComposite`.
|
||||
|
||||
- [ ] Add RED tests rejecting free-form shell payload, missing invocation on a planned v2 stage, mutable artifact reference, duplicate output ID, test-assurance v2 reference, source/artifact reference aliasing, wrong candidate digest, release manifest missing either assessment, promotion fields inside the immutable manifest, and target/approval missing from `PromotionSubject`.
|
||||
- [ ] Run:
|
||||
|
||||
```bash
|
||||
"$CICD_GO_BIN" test ./packages/contracts ./packages/canonicalid
|
||||
make GO="$CICD_GO_BIN" contracts
|
||||
```
|
||||
|
||||
Expected: RED because the typed contracts do not exist.
|
||||
|
||||
- [ ] Implement v2 contracts and document-version mappings while retaining v1 dual-read behavior. `StageInvocation.Payload` is a discriminated typed payload validated by `(AdapterID, Operation)`; the adapter passes its internal argv directly through `exec.CommandContext` and never invokes a shell.
|
||||
- [ ] Run:
|
||||
|
||||
```bash
|
||||
make GO="$CICD_GO_BIN" contracts-sync
|
||||
make GO="$CICD_GO_BIN" contracts
|
||||
"$CICD_GO_BIN" test ./packages/contracts ./packages/canonicalid
|
||||
```
|
||||
|
||||
Expected: PASS.
|
||||
- [ ] Commit:
|
||||
|
||||
```bash
|
||||
git add packages/contracts packages/canonicalid contracts capabilities/ci-test-assurance capabilities/ci-dependency-vulnerability capabilities/ci-artifact-signing capabilities/ci-static-artifact-supply-chain capabilities/ci-static-site-publish
|
||||
git commit -m "feat(contracts): define frontend delivery identities"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 2: Execute typed stages with verified artifact fan-out
|
||||
|
||||
**Files:**
|
||||
|
||||
- Create: `apps/cicdctl/internal/execution/adapter.go`
|
||||
- Create: `apps/cicdctl/internal/execution/engine.go`
|
||||
- Create: `apps/cicdctl/internal/execution/artifact_store.go`
|
||||
- Create: `apps/cicdctl/internal/execution/local_artifact_store.go`
|
||||
- Create: `apps/cicdctl/internal/execution/engine_test.go`
|
||||
- Create: `apps/cicdctl/internal/execution/artifact_store_test.go`
|
||||
- Modify: `apps/cicdctl/internal/execution/scheduler.go`
|
||||
- Modify: `apps/cicdctl/internal/execution/completeness.go`
|
||||
- Modify: `apps/cicdctl/internal/app/run_command.go`
|
||||
|
||||
**Interfaces:**
|
||||
|
||||
```go
|
||||
type InvocationAdapter interface {
|
||||
AdapterID() string
|
||||
Execute(context.Context, InvocationRequest) (InvocationOutcome, error)
|
||||
}
|
||||
|
||||
type ArtifactStore interface {
|
||||
Put(context.Context, ArtifactInput) (contracts.ArtifactReference, error)
|
||||
MaterializeVerified(context.Context, contracts.ArtifactReference, string) error
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] Add RED tests proving source checkout is read-only, independent stages do not share mutable output paths, mutation after `Put` fails, missing adapters are platform defects, failed dependencies block descendants, and a missing terminal result cannot pass.
|
||||
- [ ] Run:
|
||||
|
||||
```bash
|
||||
"$CICD_GO_BIN" test ./apps/cicdctl/internal/execution ./apps/cicdctl/internal/app -count=1
|
||||
```
|
||||
|
||||
Expected: RED because no invocation engine/artifact store exists.
|
||||
|
||||
- [ ] Implement bounded scheduler waves over existing state-machine rules. Every downstream artifact is materialized to a private directory and rehashed before adapter invocation.
|
||||
- [ ] Add `cicdctl run execute --plan --checkout --results --artifact-root` with atomic result writes.
|
||||
- [ ] Re-run `"$CICD_GO_BIN" test ./apps/cicdctl/internal/execution ./apps/cicdctl/internal/app -count=1`; expected PASS.
|
||||
- [ ] Commit:
|
||||
|
||||
```bash
|
||||
git add apps/cicdctl/internal/execution apps/cicdctl/internal/app
|
||||
git commit -m "feat(cicdctl): execute typed artifact-bound stages"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 3: Integrate the digest-pinned test-assurance v3 contract
|
||||
|
||||
**Files:**
|
||||
|
||||
- Create: `apps/cicdctl/internal/adapters/testassurance/contract.go`
|
||||
- Create: `apps/cicdctl/internal/adapters/testassurance/runner.go`
|
||||
- Create: `apps/cicdctl/internal/adapters/testassurance/projector.go`
|
||||
- Create: `apps/cicdctl/internal/adapters/testassurance/contract_test.go`
|
||||
- Create: `apps/cicdctl/internal/adapters/testassurance/runner_test.go`
|
||||
- Create: `apps/cicdctl/internal/adapters/testassurance/projector_test.go`
|
||||
- Modify: `apps/cicdctl/internal/planner/plan.go`
|
||||
- Modify: `apps/cicdctl/internal/capabilities/capabilities.go`
|
||||
- Modify: `apps/cicdctl/internal/app/run_command.go`
|
||||
- Create imported immutable schema bundle: `toolchains/external-contracts/test-assurance-v3/`
|
||||
- Create: `toolchains/external-contracts/test-assurance-v3/contract-lock.json`
|
||||
- Create: `contracts/schemas/external-contract-lock.schema.json`
|
||||
- Create: `packages/contracts/schemas/external-contract-lock.schema.json`
|
||||
- Modify: `toolchains/platform-release-catalog.yaml`
|
||||
- Modify: `contracts/schemas/platform-release-catalog.schema.json`
|
||||
- Modify: `packages/contracts/schemas/platform-release-catalog.schema.json`
|
||||
- Modify: `packages/contracts/schema.go`
|
||||
- Modify: `packages/contracts/schema_test.go`
|
||||
- Modify: `contracts/examples/valid/platform-release-catalog.json`
|
||||
- Create/complete: `capabilities/ci-test-assurance/descriptor.yaml`, `capabilities/ci-test-assurance/policy.yaml`, `capabilities/ci-test-assurance/acceptance.yaml`, `capabilities/ci-test-assurance/runbook-index.yaml`
|
||||
- Create: `docs/decisions/readiness/ci-test-assurance-P1.yaml`
|
||||
- Create evidence under: `docs/decisions/readiness/evidence/ci-test-assurance/`
|
||||
- Modify: `contracts/readiness-cards.yaml`
|
||||
- Modify: `Makefile`
|
||||
|
||||
**External calls:**
|
||||
|
||||
```text
|
||||
testctl validate
|
||||
testctl lock
|
||||
testctl compile
|
||||
testctl select
|
||||
testctl plan
|
||||
testctl execute-one
|
||||
testctl normalize
|
||||
testctl bundle
|
||||
testctl assess
|
||||
```
|
||||
|
||||
All calls use argv arrays and a distribution/schema digest from the signed platform catalog. `contract-lock.json` binds test-assurance source revision, v3 schema major, distribution digest, and schema-bundle digest. The catalog schema adds a typed `test-assurance` engine reference; without that engine, `ci-test-assurance` compilation fails.
|
||||
|
||||
`projector.go` preserves the external plan bytes/digest unchanged and creates a separate CI projection whose nodes reference only `{externalPlanDigest, workItemId}` plus runner placement and CI dependency edges. It never writes a modified testctl plan.
|
||||
|
||||
The source request is planned before the build with no input artifacts. After `ci-frontend` freezes the first candidate, CI creates an artifact request containing its exact `{artifactId, mediaType, sha256}`. Testctl, not CI, matches that request to repository `ArtifactSuiteTemplate` declarations and materializes executable v3 suites/work items. CI rejects any unresolved template, placeholder digest, or work item whose input tuple differs from the candidate reference.
|
||||
|
||||
- [ ] Add RED tests for source-revision/schema/distribution digest mismatch, absent catalog engine, non-v3 output, altered external plan bytes, altered timeout/retry, missing work-item result, source/artifact plan mixing, opaque evidence preservation, and exact testctl exit-code mapping from the published external contract.
|
||||
- [ ] Run:
|
||||
|
||||
```bash
|
||||
"$CICD_GO_BIN" test ./apps/cicdctl/internal/adapters/testassurance ./apps/cicdctl/internal/planner ./apps/cicdctl/internal/capabilities -count=1
|
||||
```
|
||||
|
||||
Expected: RED.
|
||||
|
||||
- [ ] Implement the adapter without importing JUnit, Playwright, coverage, HTTP, accessibility, or visual parsing code. Invoke v3 artifact work as `testctl execute-one --plan <plan> --work-item-id <id> --artifact-map <map> --output <dir>`.
|
||||
- [ ] Raise `ci-test-assurance` to P1/shadow only after a local v3 source→artifact conformance fixture completes.
|
||||
- [ ] Re-run `"$CICD_GO_BIN" test ./apps/cicdctl/internal/adapters/testassurance ./apps/cicdctl/internal/planner ./apps/cicdctl/internal/capabilities -count=1` and `make GO="$CICD_GO_BIN" registry`; expected PASS.
|
||||
- [ ] Commit:
|
||||
|
||||
```bash
|
||||
git add apps/cicdctl toolchains/external-contracts/test-assurance-v3 toolchains/platform-release-catalog.yaml contracts/schemas/platform-release-catalog.schema.json contracts/examples/valid/platform-release-catalog.json packages/contracts capabilities/ci-test-assurance contracts/readiness-cards.yaml docs/decisions/readiness/ci-test-assurance-P1.yaml docs/decisions/readiness/evidence/ci-test-assurance Makefile
|
||||
git commit -m "feat(test-assurance): project external v3 test plans"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 4: Split Node ownership and freeze the first deterministic candidate
|
||||
|
||||
**Files:**
|
||||
|
||||
- Modify: `apps/cicdctl/internal/manifest/model.go`
|
||||
- Modify: `apps/cicdctl/internal/manifest/compiler.go`
|
||||
- Modify: `apps/cicdctl/internal/manifest/compiler_test.go`
|
||||
- Modify: `apps/cicdctl/internal/templates/nodetypescript/resolver.go`
|
||||
- Modify: `apps/cicdctl/internal/templates/nodetypescript/resolver_test.go`
|
||||
- Modify: `apps/cicdctl/internal/templates/frontend/resolver.go`
|
||||
- Modify: `apps/cicdctl/internal/templates/frontend/resolver_test.go`
|
||||
- Create: `apps/cicdctl/internal/artifacts/candidate/builder.go`
|
||||
- Create: `apps/cicdctl/internal/artifacts/candidate/archive.go`
|
||||
- Create: `apps/cicdctl/internal/artifacts/candidate/builder_test.go`
|
||||
- Create: `apps/cicdctl/internal/artifacts/candidate/archive_test.go`
|
||||
- Modify: `capabilities/ci-node-typescript/*`
|
||||
- Modify: `capabilities/ci-frontend/*`
|
||||
- Create: `docs/decisions/readiness/ci-frontend-P1.yaml`
|
||||
- Create: `docs/decisions/readiness/evidence/ci-frontend-P1.bundle.json`
|
||||
- Modify: `contracts/readiness-cards.yaml`
|
||||
- Modify: `Makefile`
|
||||
|
||||
**Manifest config:**
|
||||
|
||||
```go
|
||||
type NodeTypeScriptConfig struct {
|
||||
LintScript string
|
||||
TypecheckScript string
|
||||
TestOwner string // empty or ci-test-assurance
|
||||
}
|
||||
|
||||
type FrontendConfig struct {
|
||||
BuildScript string
|
||||
OutputDirectory string
|
||||
SizeBudgetBytes int64
|
||||
ForbiddenEnvironment []string
|
||||
}
|
||||
```
|
||||
|
||||
Package manager/version/install mode are repository facts derived from `package.json.packageManager` and `pnpm-lock.yaml`; capability config cannot override them. The execution-plan compiler permits exactly one `candidate-producer` operation for a selected release output. A second producer, a legacy product candidate command, or a writer without platform writer identity/operation ID/idempotency key is a contract error.
|
||||
|
||||
- [ ] Add RED tests proving capability config is decoded, pnpm is derived and uses frozen install, manifest package-manager override is rejected, lint/typecheck remain in CI, unit/coverage stages disappear when test owner is `ci-test-assurance`, source revision is built twice in isolated workspaces, only the first byte-identical candidate is retained, and a plan with zero/two candidate producers is rejected.
|
||||
- [ ] Add archive adversarial tests for empty output, traversal, symlink, host path, undeclared member, duplicate path, environment leak, size overflow, and changed tree digest.
|
||||
- [ ] Run `"$CICD_GO_BIN" test ./apps/cicdctl/internal/manifest ./apps/cicdctl/internal/templates/nodetypescript ./apps/cicdctl/internal/templates/frontend ./apps/cicdctl/internal/artifacts/candidate -count=1`; expected RED.
|
||||
- [ ] Implement strict config decoding and canonical archive/member manifest generation. Destroy the verification workspace before returning the candidate reference.
|
||||
- [ ] Re-run `"$CICD_GO_BIN" test ./apps/cicdctl/internal/manifest ./apps/cicdctl/internal/templates/nodetypescript ./apps/cicdctl/internal/templates/frontend ./apps/cicdctl/internal/artifacts/candidate -count=1` and `make GO="$CICD_GO_BIN" registry`; expected PASS.
|
||||
- [ ] Commit:
|
||||
|
||||
```bash
|
||||
git add apps/cicdctl/internal/manifest apps/cicdctl/internal/templates apps/cicdctl/internal/artifacts/candidate capabilities/ci-node-typescript capabilities/ci-frontend contracts/readiness-cards.yaml docs/decisions/readiness/ci-frontend-P1.yaml docs/decisions/readiness/evidence/ci-frontend-P1.bundle.json Makefile
|
||||
git commit -m "feat(frontend): freeze one deterministic static candidate"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 5: Bind dependency-vulnerability evidence to source and candidate
|
||||
|
||||
**Files:**
|
||||
|
||||
- Create: `apps/cicdctl/internal/artifacts/vulnerability/contract.go`
|
||||
- Create: `apps/cicdctl/internal/artifacts/vulnerability/adapter.go`
|
||||
- Create: `apps/cicdctl/internal/artifacts/vulnerability/validator.go`
|
||||
- Create: `apps/cicdctl/internal/artifacts/vulnerability/adapter_test.go`
|
||||
- Create: `apps/cicdctl/internal/artifacts/vulnerability/validator_test.go`
|
||||
- Modify: `apps/cicdctl/internal/capabilities/capabilities.go`
|
||||
- Create/complete: `capabilities/ci-dependency-vulnerability/descriptor.yaml`, `capabilities/ci-dependency-vulnerability/policy.yaml`, `capabilities/ci-dependency-vulnerability/acceptance.yaml`, `capabilities/ci-dependency-vulnerability/runbook-index.yaml`
|
||||
- Create: `docs/decisions/readiness/ci-dependency-vulnerability-P1.yaml`
|
||||
- Create evidence under: `docs/decisions/readiness/evidence/ci-dependency-vulnerability/`
|
||||
- Modify: `contracts/readiness-cards.yaml`
|
||||
- Modify: `Makefile`
|
||||
|
||||
**Evidence identity:** source revision, lockfile digest, candidate subject digest, provider ID, scanner/tool digest, vulnerability DB snapshot digest/time, invocation digest, normalized finding set, and evidence signature.
|
||||
|
||||
The adapter executes a digest-pinned provider engine through a typed contract and validates its output. It contains no scanner HTTP client and receives no provider credential; network/credential handling stays inside the provider trust boundary.
|
||||
|
||||
- [ ] Add RED cases for absent report, wrong lockfile/candidate, stale DB, provider crash, malformed report, invalid signature, and zero findings without valid invocation metadata.
|
||||
- [ ] Run `"$CICD_GO_BIN" test ./apps/cicdctl/internal/artifacts/vulnerability -count=1`; expected RED.
|
||||
- [ ] Implement a typed provider profile selected by manifest ID and direct process execution of the pinned engine; arbitrary repository commands and in-process provider clients are forbidden.
|
||||
- [ ] Raise to P1/shadow with signed local fixtures; keep P2 blocked on a named real scanner/provider.
|
||||
- [ ] Re-run `"$CICD_GO_BIN" test ./apps/cicdctl/internal/artifacts/vulnerability ./apps/cicdctl/internal/capabilities -count=1` and `make GO="$CICD_GO_BIN" registry`; expected PASS.
|
||||
- [ ] Commit:
|
||||
|
||||
```bash
|
||||
git add apps/cicdctl/internal/artifacts/vulnerability apps/cicdctl/internal/capabilities capabilities/ci-dependency-vulnerability contracts/readiness-cards.yaml docs/decisions/readiness/ci-dependency-vulnerability-P1.yaml docs/decisions/readiness/evidence/ci-dependency-vulnerability Makefile
|
||||
git commit -m "feat(security): bind dependency findings to candidates"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 6: Generalize SBOM and provenance to static archives
|
||||
|
||||
**Files:**
|
||||
|
||||
- Create: `apps/cicdctl/internal/artifacts/subject/subject.go`
|
||||
- Create: `apps/cicdctl/internal/artifacts/subject/subject_test.go`
|
||||
- Create: `apps/cicdctl/internal/artifacts/sbom/contract.go`
|
||||
- Create: `apps/cicdctl/internal/artifacts/sbom/validator.go`
|
||||
- Create: `apps/cicdctl/internal/artifacts/sbom/sbom_test.go`
|
||||
- Create: `apps/cicdctl/internal/artifacts/provenance/contract.go`
|
||||
- Create: `apps/cicdctl/internal/artifacts/provenance/validator.go`
|
||||
- Create: `apps/cicdctl/internal/artifacts/provenance/provenance_test.go`
|
||||
- Modify: `capabilities/ci-sbom/*`
|
||||
- Modify: `capabilities/ci-provenance/*`
|
||||
- Modify: `apps/cicdctl/internal/capabilities/capabilities.go`
|
||||
- Create: `docs/decisions/readiness/ci-sbom-P1.yaml`
|
||||
- Create: `docs/decisions/readiness/ci-provenance-P1.yaml`
|
||||
- Create: `docs/decisions/readiness/evidence/ci-sbom-P1.bundle.json`
|
||||
- Create: `docs/decisions/readiness/evidence/ci-provenance-P1.bundle.json`
|
||||
- Modify: `contracts/readiness-cards.yaml`
|
||||
- Modify: `Makefile`
|
||||
|
||||
**Interface:** `ImmutableSubject` is a tagged union of container image or static archive. Both evidence types bind subject kind/digest; static provenance additionally binds source revision, build invocation, member-manifest digest, platform/toolchain digests, and determinism evidence. Digest-pinned external SBOM/provenance engines generate documents; cicdctl validates and binds returned evidence but implements no provider network client.
|
||||
|
||||
- [ ] Add RED tests for empty/incomplete SBOM, duplicate package identity, wrong subject kind/digest, missing build invocation, changed source revision, changed member manifest, and fabricated provenance.
|
||||
- [ ] Run `"$CICD_GO_BIN" test ./apps/cicdctl/internal/artifacts/subject ./apps/cicdctl/internal/artifacts/sbom ./apps/cicdctl/internal/artifacts/provenance -count=1`; expected RED.
|
||||
- [ ] Implement output-kind adapters while preserving every existing container test unchanged.
|
||||
- [ ] Re-run `"$CICD_GO_BIN" test ./apps/cicdctl/internal/artifacts/subject ./apps/cicdctl/internal/artifacts/sbom ./apps/cicdctl/internal/artifacts/provenance ./apps/cicdctl/internal/capabilities -count=1`, `make GO="$CICD_GO_BIN" contracts`, and `make GO="$CICD_GO_BIN" registry`; expected PASS.
|
||||
- [ ] Commit:
|
||||
|
||||
```bash
|
||||
git add apps/cicdctl/internal/artifacts/subject apps/cicdctl/internal/artifacts/sbom apps/cicdctl/internal/artifacts/provenance apps/cicdctl/internal/capabilities capabilities/ci-sbom capabilities/ci-provenance contracts/readiness-cards.yaml docs/decisions/readiness/ci-sbom-P1.yaml docs/decisions/readiness/ci-provenance-P1.yaml docs/decisions/readiness/evidence/ci-sbom-P1.bundle.json docs/decisions/readiness/evidence/ci-provenance-P1.bundle.json Makefile
|
||||
git commit -m "feat(supply-chain): support static archive subjects"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 7: Sign the artifact and aggregate reference-only supply-chain evidence
|
||||
|
||||
**Files:**
|
||||
|
||||
- Create: `apps/cicdctl/internal/artifacts/signing/request.go`
|
||||
- Create: `apps/cicdctl/internal/artifacts/signing/verifier.go`
|
||||
- Create: `apps/cicdctl/internal/artifacts/signing/signing_test.go`
|
||||
- Create: `apps/cicdctl/internal/artifacts/supplychain/aggregate.go`
|
||||
- Create: `apps/cicdctl/internal/artifacts/supplychain/aggregate_test.go`
|
||||
- Modify: `apps/cicdctl/internal/capabilities/capabilities.go`
|
||||
- Create/complete: `capabilities/ci-artifact-signing/descriptor.yaml`, `capabilities/ci-artifact-signing/policy.yaml`, `capabilities/ci-artifact-signing/acceptance.yaml`, `capabilities/ci-artifact-signing/runbook-index.yaml`
|
||||
- Create/complete: `capabilities/ci-static-artifact-supply-chain/descriptor.yaml`, `capabilities/ci-static-artifact-supply-chain/policy.yaml`, `capabilities/ci-static-artifact-supply-chain/acceptance.yaml`, `capabilities/ci-static-artifact-supply-chain/runbook-index.yaml`
|
||||
- Create: `docs/decisions/readiness/ci-artifact-signing-P1.yaml`
|
||||
- Create: `docs/decisions/readiness/ci-static-artifact-supply-chain-P1.yaml`
|
||||
- Create evidence under: `docs/decisions/readiness/evidence/ci-artifact-signing/` and `docs/decisions/readiness/evidence/ci-static-artifact-supply-chain/`
|
||||
- Modify: `contracts/readiness-cards.yaml`
|
||||
- Modify: `Makefile`
|
||||
|
||||
**Interfaces:**
|
||||
|
||||
```go
|
||||
type SigningRequest struct { SubjectDigest, KeyID, OperationID string }
|
||||
|
||||
func VerifyStaticSupplyChain(
|
||||
candidate contracts.CandidateBundle,
|
||||
dependencyVulnerability, sbom, provenance, signature contracts.EvidenceRef,
|
||||
) (contracts.SupplyChainEvidenceSet, error)
|
||||
```
|
||||
|
||||
- [ ] Add RED tests proving the signer receives only identity data, not source/candidate bytes; reject wrong subject, expired key, missing/duplicate evidence kind, invalid signature, altered candidate, and copied/rewritten child evidence.
|
||||
- [ ] Generate ephemeral Ed25519 test keys only.
|
||||
- [ ] Run `"$CICD_GO_BIN" test ./apps/cicdctl/internal/artifacts/signing ./apps/cicdctl/internal/artifacts/supplychain -count=1`; expected RED.
|
||||
- [ ] Implement signing on the `isolated-signer` trust partition and a composite containing immutable child references only.
|
||||
- [ ] Raise both to P1/shadow; keep P2 blocked on real signer evidence.
|
||||
- [ ] Re-run `"$CICD_GO_BIN" test ./apps/cicdctl/internal/artifacts/signing ./apps/cicdctl/internal/artifacts/supplychain ./apps/cicdctl/internal/capabilities -count=1` and `make GO="$CICD_GO_BIN" registry`; expected PASS.
|
||||
- [ ] Commit:
|
||||
|
||||
```bash
|
||||
git add apps/cicdctl/internal/artifacts/signing apps/cicdctl/internal/artifacts/supplychain apps/cicdctl/internal/capabilities capabilities/ci-artifact-signing capabilities/ci-static-artifact-supply-chain contracts/readiness-cards.yaml docs/decisions/readiness/ci-artifact-signing-P1.yaml docs/decisions/readiness/ci-static-artifact-supply-chain-P1.yaml docs/decisions/readiness/evidence/ci-artifact-signing docs/decisions/readiness/evidence/ci-static-artifact-supply-chain Makefile
|
||||
git commit -m "feat(supply-chain): sign static candidates and aggregate evidence"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 8: Publish static candidates without rebuilding
|
||||
|
||||
**Files:**
|
||||
|
||||
- Create: `apps/release-control/internal/publish/publisher.go`
|
||||
- Create: `apps/release-control/internal/publish/local.go`
|
||||
- Create: `apps/release-control/internal/publish/service.go`
|
||||
- Create: `apps/release-control/internal/publish/publisher_test.go`
|
||||
- Create: `apps/release-control/internal/publish/local_test.go`
|
||||
- Modify: `apps/release-control/internal/app/app.go`
|
||||
- Create/complete: `capabilities/ci-static-site-publish/descriptor.yaml`, `capabilities/ci-static-site-publish/policy.yaml`, `capabilities/ci-static-site-publish/acceptance.yaml`, `capabilities/ci-static-site-publish/runbook-index.yaml`
|
||||
- Create: `docs/decisions/readiness/ci-static-site-publish-P1.yaml`
|
||||
- Create evidence under: `docs/decisions/readiness/evidence/ci-static-site-publish/`
|
||||
- Modify: `contracts/readiness-cards.yaml`
|
||||
- Modify: `Makefile`
|
||||
|
||||
**Interface:**
|
||||
|
||||
```go
|
||||
type PublishRequest struct {
|
||||
OperationID, SubjectDigest, CandidateDigest, ReleaseManifestDigest string
|
||||
}
|
||||
type PublicationRecord struct {
|
||||
OperationID, ProviderID, ImmutableURI, SubjectDigest, ServedContentDigest, State string
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] Add RED tests for idempotent put, same digest already present, different digest collision, response loss after mutation, failed reconciliation, served-content mismatch, mutable URI, and any attempted build/repackage operation.
|
||||
- [ ] Run `"$CICD_GO_BIN" test ./apps/release-control/internal/publish ./apps/release-control/internal/app -count=1`; expected RED.
|
||||
- [ ] Implement a content-addressed local P1 publisher. Response loss returns `INDETERMINATE`; retry requires reconciliation by operation ID.
|
||||
- [ ] Require provider subject and served-content digests both equal the approved candidate.
|
||||
- [ ] Re-run `"$CICD_GO_BIN" test ./apps/release-control/internal/publish ./apps/release-control/internal/app -count=1` and `make GO="$CICD_GO_BIN" registry`; expected PASS.
|
||||
- [ ] Raise to P1/shadow; keep real provider P2 blocked.
|
||||
- [ ] Commit:
|
||||
|
||||
```bash
|
||||
git add apps/release-control/internal/publish apps/release-control/internal/app capabilities/ci-static-site-publish contracts/readiness-cards.yaml docs/decisions/readiness/ci-static-site-publish-P1.yaml docs/decisions/readiness/evidence/ci-static-site-publish Makefile
|
||||
git commit -m "feat(release-control): publish immutable static candidates"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 9: Promote the exact signed static candidate
|
||||
|
||||
**Files:**
|
||||
|
||||
- Modify: `apps/release-control/internal/candidate/candidate.go`
|
||||
- Modify: `apps/release-control/internal/candidate/candidate_test.go`
|
||||
- Modify: `apps/release-control/internal/approval/approval.go`
|
||||
- Modify: `apps/release-control/internal/approval/approval_test.go`
|
||||
- Modify: `apps/release-control/internal/gitops/cas.go`
|
||||
- Modify: `apps/release-control/internal/gitops/cas_test.go`
|
||||
- Create: `apps/release-control/internal/promotion/service.go`
|
||||
- Create: `apps/release-control/internal/promotion/service_test.go`
|
||||
- Modify: `apps/release-control/internal/app/app.go`
|
||||
- Modify: `capabilities/delivery-release-control/*`
|
||||
- Create: `docs/decisions/readiness/delivery-release-control-P1.yaml`
|
||||
- Create: `docs/decisions/readiness/evidence/delivery-release-control-P1.bundle.json`
|
||||
- Modify: `contracts/readiness-cards.yaml`
|
||||
- Modify: `Makefile`
|
||||
|
||||
**Promotion request:** signed release manifest, confirmed publication, source and artifact test-assurance references, supply-chain evidence index, approval subject/expiry, expected Git revision/tree digest, and operation ID. No source path, build command, or mutable provider URL is accepted.
|
||||
|
||||
```go
|
||||
type PromotionRequest struct {
|
||||
OperationID string
|
||||
ReleaseManifest contracts.ReleaseManifestV2
|
||||
Subject contracts.PromotionSubject
|
||||
Publication contracts.PublicationRecord
|
||||
EvidenceIndexDigest string
|
||||
}
|
||||
```
|
||||
|
||||
The compiler selects exactly one environment adapter by output profile: static archives require the static-site desired-state adapter, while container images require the existing Kubernetes/GitOps adapter. Zero or multiple environment adapters is invalid.
|
||||
|
||||
- [ ] Add RED tests rejecting failed/missing source or artifact assessment, changed candidate, unconfirmed publication, served-content mismatch, unsigned evidence, stale approval, stale Git base, direct mutable URL, rebuild/repackage request, and zero/multiple/wrong-kind environment adapters.
|
||||
- [ ] Preserve and run existing real local Git CAS/response-loss tests.
|
||||
- [ ] Implement static desired-state promotion without an unconditional Kubernetes dependency; container releases keep their environment adapter.
|
||||
- [ ] Raise `delivery-release-control` only to P1/shadow using local Git and local content-addressed publication.
|
||||
- [ ] Run `"$CICD_GO_BIN" test ./apps/release-control/internal/candidate ./apps/release-control/internal/approval ./apps/release-control/internal/gitops ./apps/release-control/internal/publish ./apps/release-control/internal/promotion ./apps/release-control/internal/app -count=1`; expected PASS.
|
||||
- [ ] Commit:
|
||||
|
||||
```bash
|
||||
git add apps/release-control capabilities/delivery-release-control contracts/readiness-cards.yaml docs/decisions/readiness/delivery-release-control-P1.yaml docs/decisions/readiness/evidence/delivery-release-control-P1.bundle.json Makefile
|
||||
git commit -m "feat(release-control): promote verified static subjects"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 10: Keep the centrally installed required workflow thin
|
||||
|
||||
**Files:**
|
||||
|
||||
- Create: `apps/cicdctl/cmd/sourcectl/main.go`
|
||||
- Create: `apps/cicdctl/cmd/platform-bootstrap/main.go`
|
||||
- Create: `apps/cicdctl/internal/bootstrap/catalog.go`
|
||||
- Create: `apps/cicdctl/internal/bootstrap/pipeline.go`
|
||||
- Create: `apps/cicdctl/internal/bootstrap/status.go`
|
||||
- Create tests under: `apps/cicdctl/internal/bootstrap/`
|
||||
- Modify: `.gitea/workflows/required-delivery-guard.yaml`
|
||||
- Modify: `tools/contractctl/internal/workflow/required_status.go`
|
||||
- Modify: `tools/contractctl/internal/workflow/required_status_test.go`
|
||||
- Modify: `Makefile`
|
||||
- Create: `images/platform-bootstrap/Dockerfile`
|
||||
- Create: `images/platform-bootstrap/entrypoint.sh`
|
||||
- Create: `images/platform-bootstrap/README.md`
|
||||
- Modify: `toolchains/platform-release-catalog.yaml`
|
||||
|
||||
**Workflow sequence:** exact source checkout → signed catalog verification → pinned bootstrap execution → one terminal sentinel publication. All language/test/build/provider/promotion stages are compiled inside the platform plan, not written in YAML. The only required status name is exactly `platform/delivery-pipeline`.
|
||||
|
||||
- [ ] Add RED tests for exactly one stable required status, no language/build logic, no floating action references, digest-pinned binaries, a sentinel on every exit path, and absence of any rule requiring a product-repository workflow copy.
|
||||
- [ ] Run:
|
||||
|
||||
```bash
|
||||
"$CICD_GO_BIN" test ./tools/contractctl/internal/workflow ./apps/cicdctl/internal/bootstrap ./apps/cicdctl/internal/app -count=1
|
||||
```
|
||||
|
||||
Expected: RED until bootstrap binaries and status finalization exist.
|
||||
|
||||
- [ ] Implement the four-step bootstrap and atomic sentinel finalization.
|
||||
- [ ] Re-run `"$CICD_GO_BIN" test ./tools/contractctl/internal/workflow ./apps/cicdctl/internal/bootstrap ./apps/cicdctl/internal/app -count=1`; expected PASS.
|
||||
- [ ] Commit:
|
||||
|
||||
```bash
|
||||
git add apps/cicdctl/cmd apps/cicdctl/internal/bootstrap .gitea/workflows/required-delivery-guard.yaml tools/contractctl/internal/workflow images/platform-bootstrap Makefile toolchains/platform-release-catalog.yaml
|
||||
git commit -m "feat(workflow): run the pinned delivery platform"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 11: Prove the complete frontend vertical in shadow
|
||||
|
||||
**Files:**
|
||||
|
||||
- Create: `fixtures/frontend-delivery-vertical/` with a minimal pnpm frontend, both consumer manifests, v3 source/artifact testctl fixtures, deterministic build, adversarial reports, local signer, local static publisher, and local Git desired state
|
||||
- Create: `apps/cicdctl/internal/reports/shadow_parity.go`
|
||||
- Create: `apps/cicdctl/internal/reports/shadow_parity_test.go`
|
||||
- Create: `contracts/schemas/shadow-parity.schema.json`
|
||||
- Sync: `packages/contracts/schemas/shadow-parity.schema.json`
|
||||
- Modify: `packages/contracts/schema.go`
|
||||
- Modify: `packages/contracts/schema_test.go`
|
||||
- Create: `contracts/snapshots/v2/shadow-parity.schema.json`
|
||||
- Create: `contracts/examples/valid/shadow-parity.json`
|
||||
- Create: `docs/migration/frontend-template.md`
|
||||
- Create P1 evidence under: `docs/decisions/readiness/evidence/`
|
||||
- Modify: `docs/decisions/blocked-tasks.md`
|
||||
- Modify: `contracts/readiness-cards.yaml`
|
||||
- Modify: `Makefile`
|
||||
- Modify fixture registry files consumed by: `tools/fixturectl/`
|
||||
- Modify: `README.md`
|
||||
|
||||
**End-to-end order:** source test plan/assessment → one deterministic candidate → artifact test plan/assessment → vulnerability/SBOM/provenance/signature → static supply-chain composite → content-addressed publication → Git CAS promotion → evidence-index sentinel.
|
||||
|
||||
- [ ] Add RED vertical tests plus faults for missing work item, changed candidate, wrong-subject provider result, missing signature, response loss, concurrent Git writer, expired approval, and missing sentinel.
|
||||
- [ ] Add parity comparison over source revision; selected suites/counts/outcomes; coverage universe; HTTP scenario IDs; three browser outcomes; candidate/member digests; provider/supply-chain digests; and promotion readiness. Exclude timestamps, durations, temp paths, and runner IDs.
|
||||
- [ ] Verify one-writer behavior: the platform fixture produces the only candidate; legacy probes receive read-only references and cannot publish/promote.
|
||||
- [ ] Run before evidence updates:
|
||||
|
||||
```bash
|
||||
make GO="$CICD_GO_BIN" capabilities
|
||||
```
|
||||
|
||||
Expected: RED because the P1 chain lacks complete evidence.
|
||||
|
||||
- [ ] Add only locally observed P1/shadow evidence. Keep P2 blocked with named missing Gitea/runner/scanner/signer/provider prerequisites.
|
||||
- [ ] Run:
|
||||
|
||||
```bash
|
||||
make GO="$CICD_GO_BIN" contracts
|
||||
make GO="$CICD_GO_BIN" registry
|
||||
make GO="$CICD_GO_BIN" capabilities
|
||||
make GO="$CICD_GO_BIN" boundary
|
||||
make GO="$CICD_GO_BIN" verify
|
||||
git diff --check
|
||||
```
|
||||
|
||||
Expected: PASS at P1/shadow; no P2/active claim.
|
||||
- [ ] Commit:
|
||||
|
||||
```bash
|
||||
git add fixtures/frontend-delivery-vertical apps/cicdctl/internal/reports contracts packages/contracts Makefile docs README.md
|
||||
git commit -m "test(vertical): prove frontend delivery in shadow"
|
||||
```
|
||||
|
||||
## Handoff to the frontend consumer
|
||||
|
||||
The consumer migration may begin from the immutable Task 11 platform release. The product manifest pins its signed catalog version and selects all required capabilities. Central workflow/status installation remains an environment/platform operation. Product files never copy the workflow, provider orchestration, test normalizers, or promotion engine.
|
||||
@@ -0,0 +1,380 @@
|
||||
# Frontend Thin Platform Consumer Migration Plan
|
||||
|
||||
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. Use `superpowers:using-git-worktrees` before editing.
|
||||
|
||||
**Goal:** Convert `clean-architecture-frontend-template` into a thin consumer of `test-assurance-platform` and `cicd-platform` while preserving product source, tests, fixtures, raw product artifact codecs, and all origin-unknown worktree changes.
|
||||
|
||||
**Architecture:** Product manifests declare risks, obligations, suites, components, outputs, and platform capabilities. Product scripts execute one bounded product purpose and emit raw artifacts. Test assurance owns selection through assessment; CI/CD owns workflow through promotion. Shadow mode has one candidate writer—the platform—and legacy release/promotion code is read-only until removed. The central required workflow is installed by the platform and is never copied into this repository.
|
||||
|
||||
**Tech Stack:** TypeScript 7.0.2, Node.js 24.14.0, pnpm 11.17.0, Vitest 4.1.10, Playwright 1.62.0, YAML/JSON Schema, `testctl` v3, `cicdctl`.
|
||||
|
||||
## Global Constraints
|
||||
|
||||
- Repository: `/home/donghyeon/workspace/desktop-server-git/clean-architecture-frontend-template`; every command runs from its isolated migration worktree root unless it is explicitly marked read-only against the original dirty worktree.
|
||||
- Begin only from a clean worktree whose HEAD contains the approved spec amendment and all three 2026-08-02 plans in one immutable planning commit; never modify or clean the original dirty worktree.
|
||||
- Consume the immutable Test Assurance Task 10 release and CI/CD Task 11 P1/shadow release from the two companion plans.
|
||||
- Resolve `CICDCTL_BIN` and `TESTCTL_BIN` to absolute executable paths from the signed CICD catalog, and resolve `CICDCTL_DIGEST` and `TESTCTL_DIGEST` from the same catalog. Before every plan/manifest command, `sha256sum` must equal the signed value; PATH fallback is forbidden.
|
||||
- Do not claim P2/active or delete the legacy path until actual Gitea/runner/scanner/signer/provider evidence and required-status installation are observed.
|
||||
- Use only the exact capability IDs approved in the design.
|
||||
|
||||
- Keep production code, product tests/assertions, mocks, scenarios, fixtures, Vitest/Playwright config, V8 instrumentation inputs, and runtime/release artifact codecs.
|
||||
- Remove local workflow compilation, risk selection, waiver/normalization/assessment, test scheduling, provider invocation, signing, retention, candidate publication, and promotion engines after cutover.
|
||||
- `check:types` remains a CI Node responsibility. `check:architecture` alone maps to `architecture-typescript`.
|
||||
- Artifact browser suites consume the platform candidate; their Playwright `webServer` must never rebuild it.
|
||||
- The product does not add `.gitea/workflows/required-delivery-guard.yaml` or any equivalent copied central workflow.
|
||||
- Rollback changes only the signed platform catalog/version pin and promotes a previous immutable subject through `release-control`; it never re-enables a legacy writer.
|
||||
|
||||
---
|
||||
|
||||
### Task 1: Preserve and classify the existing dirty Task 3 work
|
||||
|
||||
**Files:**
|
||||
|
||||
- Create in the clean migration worktree: `docs/migration/task3-wip-provenance.json`
|
||||
- Create: `docs/migration/task3-wip-disposition.md`
|
||||
- Read only: original dirty worktree tracked diff and untracked files
|
||||
|
||||
**Ledger contract:** Each entry records `path`, `workingBlobSha256`, and an ordered `hunks` array. Every hunk records `oldRange`, `newRange`, `diffSha256`, `owner` (`product`, `test-assurance`, `cicd`, `unrelated-or-unknown`), `provenance`, and `disposition` (`preserve-in-original`, `reimplement-in-platform`, `retain-product-codec`, `remove-after-cutover`). Mixed-responsibility files therefore have multiple hunk owners. The ledger does not copy source content or create a patch archive.
|
||||
|
||||
- [ ] Verify the migration worktree entry commit before inspecting WIP:
|
||||
|
||||
```bash
|
||||
git show HEAD:docs/superpowers/specs/2026-08-02-platform-owned-frontend-assurance-delivery-design.md
|
||||
git show HEAD:docs/superpowers/plans/2026-08-02-frontend-platform-consumer-migration.md
|
||||
git show HEAD:docs/superpowers/plans/2026-08-02-test-assurance-frontend-capabilities.md
|
||||
git show HEAD:docs/superpowers/plans/2026-08-02-cicd-frontend-assurance-delivery.md
|
||||
git status --short
|
||||
```
|
||||
|
||||
Expected: all four documents exist and migration-worktree status is empty.
|
||||
- [ ] Capture `git status --short`, `git diff --name-status`, `git diff --numstat`, `git diff | sha256sum`, per-hunk unified diffs, and `git hash-object` for each present untracked/modified file without writing to the original worktree.
|
||||
- [ ] Add a failing migration test in `tests/unit/platform-migration-provenance.test.ts` that requires every hunk to have one owner/provenance/disposition, permits mixed owners per file, and rejects a blanket `frontend`/`keep-all` classification.
|
||||
- [ ] Run:
|
||||
|
||||
```bash
|
||||
corepack pnpm exec vitest run tests/unit/platform-migration-provenance.test.ts
|
||||
```
|
||||
|
||||
Expected: RED because the ledger does not exist.
|
||||
|
||||
- [ ] Create the ledger and disposition document with `apply_patch`. Explicitly classify workflow/gate/test-normalization/provider/promotion WIP to its platform owner and origin-unknown files as preserved in the original worktree.
|
||||
- [ ] Recompute every original working-blob and hunk digest, re-run the test, and compare original status plus digests with the capture; expected PASS with no drift.
|
||||
- [ ] Commit only the ledger, disposition, and test:
|
||||
|
||||
```bash
|
||||
git add docs/migration/task3-wip-provenance.json docs/migration/task3-wip-disposition.md tests/unit/platform-migration-provenance.test.ts
|
||||
git commit -m "docs: preserve Task 3 migration provenance"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 2: Declare the CI/CD platform consumer
|
||||
|
||||
**Files:**
|
||||
|
||||
- Create: `delivery-platform.yaml`
|
||||
- Create: `tests/unit/delivery-platform-manifest.test.ts`
|
||||
|
||||
**Selected capabilities:**
|
||||
|
||||
```text
|
||||
ci-standard-core
|
||||
ci-node-typescript
|
||||
ci-frontend
|
||||
ci-test-assurance
|
||||
ci-dependency-vulnerability
|
||||
ci-sbom
|
||||
ci-provenance
|
||||
ci-artifact-signing
|
||||
ci-static-artifact-supply-chain
|
||||
ci-static-site-publish
|
||||
delivery-release-control
|
||||
```
|
||||
|
||||
The manifest declares one frontend component rooted at `.`, one static-site output rooted at `dist`, pnpm frozen install, `lint`, `check:types`, `build`, the size/environment/determinism policy, and a signed `platformVersion` that exists in the released CI/CD catalog. It does not contain a workflow DAG, provider command, test command, or mutable tool image.
|
||||
|
||||
- [ ] Add RED tests that invoke `$CICDCTL_BIN manifest compile` after digest verification and reject missing capabilities, a floating platform version, product-owned test stages, arbitrary shell/provider commands, or a second candidate output.
|
||||
- [ ] Run:
|
||||
|
||||
```bash
|
||||
corepack pnpm exec vitest run tests/unit/delivery-platform-manifest.test.ts
|
||||
```
|
||||
|
||||
Expected: RED because `delivery-platform.yaml` is absent.
|
||||
|
||||
- [ ] Add the manifest only; do not add a package script that could resolve an arbitrary PATH binary or local compilation logic.
|
||||
- [ ] Compile the manifest with `"$CICDCTL_BIN" manifest compile --manifest delivery-platform.yaml --source-revision "$PLATFORM_SOURCE_REVISION" --output artifacts/platform/effective-project.json`; expected PASS at shadow activation.
|
||||
- [ ] Commit:
|
||||
|
||||
```bash
|
||||
git add delivery-platform.yaml tests/unit/delivery-platform-manifest.test.ts
|
||||
git commit -m "chore(platform): declare frontend delivery capabilities"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 3: Declare risks, obligations, change surfaces, and source suites
|
||||
|
||||
**Files:**
|
||||
|
||||
- Create: `test-assurance.yaml`
|
||||
- Create generated: `test-assurance.lock.json`
|
||||
- Create: `config/test-assurance/risks/frontend.json`
|
||||
- Create: `config/test-assurance/obligations/frontend.json`
|
||||
- Create: `config/test-assurance/suites/source.json`
|
||||
- Create: `config/test-assurance/suites/artifact-templates.json`
|
||||
- Create: `config/test-assurance/change-surfaces/frontend.json`
|
||||
- Create: `config/test-assurance/legacy-command-disposition.json`
|
||||
- Create: `scripts/run-contract-negative-fixtures.ts`
|
||||
- Create: `scripts/reporters/vitest-discovery-reporter.ts`
|
||||
- Create: `scripts/write-production-module-inventory.ts`
|
||||
- Modify: `scripts/check-architecture.ts`
|
||||
- Modify: `vitest.config.ts`
|
||||
- Create: `tests/unit/test-assurance-source-manifest.test.ts`
|
||||
- Create: `tests/unit/raw-source-reporters.test.ts`
|
||||
- Create: `tests/unit/legacy-command-migration-completeness.test.ts`
|
||||
- Modify: `package.json`
|
||||
|
||||
**Source suite mapping:**
|
||||
|
||||
| Suite ID | Product argv | Capability |
|
||||
|---|---|---|
|
||||
| `runtime-schema` | `corepack pnpm test:runtime-schema` | `unit-typescript-vitest` |
|
||||
| `unit` | `corepack pnpm test:unit` | `unit-typescript-vitest` |
|
||||
| `reference-feature` | `corepack pnpm test:reference-feature` | `unit-typescript-vitest` |
|
||||
| `optional-recipes` | `corepack pnpm test:recipes` | `unit-typescript-vitest` |
|
||||
| `component` | `corepack pnpm test:component` | `component-react-vitest` |
|
||||
| `integration` | `corepack pnpm test:integration` | `integration-http-msw` |
|
||||
| `http-scenarios` | `corepack pnpm test:http-scenario-evidence` | `integration-http-msw` |
|
||||
| `architecture` | `corepack pnpm check:architecture` | `architecture-typescript` |
|
||||
| `coverage` | `corepack pnpm test:coverage` | `coverage-v8` |
|
||||
| `contract-negative-fixtures` | `corepack pnpm test:contract-negative-fixtures` | `unit-typescript-vitest` |
|
||||
|
||||
Every source suite declares `executionPhase: SOURCE`, an empty `requiredInputArtifacts`, a bounded argv array, timeouts, environment allowlist, and exact raw artifacts. `check:types` is absent because CI owns it. `test-assurance.yaml` references `artifact-templates.json` through `artifactSuiteTemplateFiles`; those templates are completed in Task 4.
|
||||
|
||||
- [ ] Add RED tests for exact capability IDs, one purpose per suite, no aggregate `test:all`, no `check:types`, no shell string, no `--passWithNoTests` on required suites, exact report paths, and source phase with zero input artifacts.
|
||||
- [ ] Parse every unique command and argument tuple in legacy `config/ci/gates.json` and require exactly one disposition: `ci-node`, `test-assurance-suite`, `cicd-release-or-security`, `product-dev-only`, or `retired-with-platform-evidence`. Missing or duplicate classification fails.
|
||||
- [ ] Add `test:contract-negative-fixtures` as one bounded product harness: it runs every expected-fail type/coverage/design-system/i18n/diagnostics/registry/route fixture, asserts the exact expected exit code and diagnostic identity from the disposition file, and exits 0 only when all negative contracts fail for the intended reason.
|
||||
- [ ] Classify `test:browser-capabilities` into the three artifact browser suites, `test:storybook` as a retained source test harness until an explicit platform disposition is proven, and `playwright.dev.config.ts` as `product-dev-only`; no legacy gate command disappears without platform evidence.
|
||||
- [ ] Add RED reporter tests requiring Vitest discovery IDs/counts alongside JUnit, a canonical production-module inventory alongside V8 coverage, an architecture graph/violations report, and typed HTTP receipts. These are raw observations only and contain no PASS/waiver/threshold decision.
|
||||
- [ ] Run the focused test; expected RED.
|
||||
- [ ] Add declarations. Remove `--passWithNoTests` from required source suite scripts while retaining optional behavior only for suites whose obligation explicitly permits no applicable tests. Wire the Vitest reporter into each required Vitest suite, make `check:architecture` emit the typed graph/violation JSON, and rewrite `test:coverage` to emit V8 summary plus production inventory without calling the local risk/threshold assessor.
|
||||
- [ ] Use released `testctl validate`, `lock`, and `compile` to generate the lock; never type toolchain digests by hand.
|
||||
- [ ] Re-run the focused test and:
|
||||
|
||||
```bash
|
||||
"$TESTCTL_BIN" validate --repository . --out artifacts/platform/source-validation.json
|
||||
"$TESTCTL_BIN" lock --repository . --out test-assurance.lock.json
|
||||
"$TESTCTL_BIN" compile --repository . --lock test-assurance.lock.json --out artifacts/platform/compiled-policy.json
|
||||
```
|
||||
|
||||
Expected: PASS with v3 and exact Node/pnpm/TypeScript/Vitest/Playwright pins.
|
||||
- [ ] Commit:
|
||||
|
||||
```bash
|
||||
git add test-assurance.yaml test-assurance.lock.json config/test-assurance scripts/reporters/vitest-discovery-reporter.ts scripts/write-production-module-inventory.ts scripts/check-architecture.ts scripts/run-contract-negative-fixtures.ts vitest.config.ts tests/unit/test-assurance-source-manifest.test.ts tests/unit/raw-source-reporters.test.ts tests/unit/legacy-command-migration-completeness.test.ts package.json
|
||||
git commit -m "chore(test-assurance): declare frontend source suites"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 4: Make browser suites consume the immutable candidate
|
||||
|
||||
**Files:**
|
||||
|
||||
- Modify: `playwright.config.ts`
|
||||
- Modify: `playwright.capabilities.config.ts`
|
||||
- Modify: `playwright.visual.config.ts`
|
||||
- Modify: `playwright.storybook.config.ts` only if it remains a required artifact suite
|
||||
- Create: `scripts/serve-test-candidate.ts`
|
||||
- Create: `scripts/reporters/playwright-evidence-reporter.ts`
|
||||
- Create: `tests/support/browser/mutation-evidence.ts`
|
||||
- Modify: `tests/e2e/reference-form.spec.ts`
|
||||
- Modify: `package.json`
|
||||
- Modify: `config/test-assurance/suites/artifact-templates.json`
|
||||
- Modify: `config/test-assurance/obligations/frontend.json`
|
||||
- Create: `tests/unit/artifact-suite-contract.test.ts`
|
||||
|
||||
**Artifact suite mapping:**
|
||||
|
||||
| Suite ID | Product argv | Capability |
|
||||
|---|---|---|
|
||||
| `e2e-chromium` | `corepack pnpm test:e2e:chromium` | `e2e-playwright-chromium` |
|
||||
| `e2e-firefox` | `corepack pnpm test:e2e:firefox` | `e2e-playwright-firefox` |
|
||||
| `e2e-webkit` | `corepack pnpm test:e2e:webkit` | `e2e-playwright-webkit` |
|
||||
| `accessibility` | `corepack pnpm test:a11y` | `accessibility-web` |
|
||||
| `visual-regression` | `corepack pnpm test:visual` | `visual-regression-web` |
|
||||
|
||||
All five repository templates declare `executionPhase: ARTIFACT` and one input declaration containing `artifactId: frontend-site` and `mediaType: application/vnd.delivery.static-site.v1+tar`. Templates contain no `sha256` field. After the platform builds the candidate, CI creates an artifact `ExecutionRequest` containing the actual candidate SHA-256; testctl materializes executable v3 `SuiteDefinition` and `WorkItem` documents with that exact digest.
|
||||
|
||||
- [ ] Add RED tests requiring separate Chromium/Firefox/WebKit commands and artifacts, rejecting a SHA/digest placeholder in committed templates, rejecting build commands in Playwright `webServer`, requiring candidate-root environment input, and checking typed Playwright JSON/JUnit, trace/screenshot/console/network indexes, browser provider identity, write mutation receipts, accessibility/manual-review identity, and visual baseline/diff identity.
|
||||
- [ ] Run the focused test; expected RED because browser configuration currently rebuilds the site.
|
||||
- [ ] Implement `serve-test-candidate.ts` as a bounded read-only static server over the executor-verified candidate directory. It reads the candidate root from the allowlisted environment and never verifies or substitutes the platform digest itself.
|
||||
- [ ] Change `playwright.config.ts` and `playwright.capabilities.config.ts` to call that server and add per-browser package scripts using `--project`; each browser suite includes both `tests/e2e/` and `tests/browser-capabilities/`. Add the product-owned Playwright reporter and mutation-evidence helper so tests emit traces, screenshots, console/network indexes, response/mutation/reload receipts, accessibility findings/manual records, and visual baselines/diffs without assessing them. Extend `reference-form.spec.ts` with the existing production-shaped create handler: observe the successful HTTP response, read the created resource, reload, read it again, and write one typed receipt keyed by the test/scenario ID.
|
||||
- [ ] Validate/lock/compile with testctl and run each product suite against a local candidate materialized by the platform fixture.
|
||||
- [ ] Commit:
|
||||
|
||||
```bash
|
||||
git add playwright.config.ts playwright.capabilities.config.ts playwright.visual.config.ts playwright.storybook.config.ts scripts/serve-test-candidate.ts scripts/reporters/playwright-evidence-reporter.ts tests/support/browser/mutation-evidence.ts tests/e2e/reference-form.spec.ts package.json config/test-assurance tests/unit/artifact-suite-contract.test.ts
|
||||
git commit -m "refactor(browser): test the immutable platform candidate"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 5: Separate raw reporters and freeze legacy assurance as read-only
|
||||
|
||||
**Files:**
|
||||
|
||||
- Retain/refactor: `scripts/run-http-scenario-evidence.ts`
|
||||
- Retain/refactor: `scripts/lib/http-scenario-evidence.ts`
|
||||
- Retain/refactor: `scripts/write-a11y-report.ts`
|
||||
- Create: `scripts/lib/manual-a11y-record.ts`
|
||||
- Retain: product V8 instrumentation and module-inventory code in `vite.config.ts`, `vitest.config.ts`, and product codecs
|
||||
- Retain read-only until Task 8: `scripts/check-test-evidence.ts`, `scripts/verify-browser-capability-evidence.ts`, `scripts/check-risk-coverage.ts`, `scripts/lib/risk-coverage.ts`, `scripts/lib/local-policy-evidence.ts`, `scripts/lib/manual-a11y-evidence.ts`, `scripts/verify-a11y-manual.ts`
|
||||
- Create: `scripts/run-legacy-assurance-probe.ts`
|
||||
- Modify: `package.json`
|
||||
- Create: `tests/unit/raw-product-evidence-contract.test.ts`
|
||||
- Create: `tests/unit/legacy-assurance-readonly.test.ts`
|
||||
|
||||
**Boundary:** Product emitters may validate their own artifact schema and cross-fields, but the new manifests may not invoke local obligation satisfaction, waiver, quarantine, retry/flaky, coverage threshold, scenario completeness, browser matrix completeness, or evidence freshness logic. Legacy assessors remain callable only through `run-legacy-assurance-probe.ts`, which writes comparison output to a dedicated read-only shadow namespace and has no candidate/provider/promotion operation.
|
||||
|
||||
- [ ] Add RED boundary tests proving raw emitters contain no verdict semantics, new manifests never invoke a legacy assessor, and every legacy assessor is reachable only from the comparison probe.
|
||||
- [ ] Run `corepack pnpm exec vitest run tests/unit/raw-product-evidence-contract.test.ts tests/unit/legacy-assurance-readonly.test.ts`; expected RED before the boundary is enforced.
|
||||
- [ ] Move any reusable false-green fixture to the Test Assurance platform implementation commit; in this repository keep only the product input fixture needed to reproduce the report.
|
||||
- [ ] Extract production-module inventory generation from `check-risk-coverage.ts` and raw manual-review codecs from `manual-a11y-evidence.ts`; keep threshold/completeness logic unchanged solely for the read-only probe until parity.
|
||||
- [ ] Update suite declarations to point directly at raw artifacts and ensure no automated workflow invokes the legacy probe.
|
||||
- [ ] Re-run `corepack pnpm exec vitest run tests/unit/raw-product-evidence-contract.test.ts tests/unit/legacy-assurance-readonly.test.ts tests/unit/test-assurance-source-manifest.test.ts tests/unit/artifact-suite-contract.test.ts`; expected PASS.
|
||||
- [ ] Commit:
|
||||
|
||||
```bash
|
||||
git add scripts/run-http-scenario-evidence.ts scripts/lib/http-scenario-evidence.ts scripts/write-a11y-report.ts scripts/lib/manual-a11y-record.ts scripts/write-production-module-inventory.ts scripts/run-legacy-assurance-probe.ts package.json tests/unit/raw-product-evidence-contract.test.ts tests/unit/legacy-assurance-readonly.test.ts config/test-assurance
|
||||
git commit -m "refactor(testing): isolate raw and legacy assurance paths"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 6: Run one-writer shadow parity
|
||||
|
||||
**Files:**
|
||||
|
||||
- Create: `docs/operations/platform-shadow-parity.md`
|
||||
- Create: `docs/operations/evidence/platform-shadow-readiness.json` only from an actual platform run
|
||||
- Create: `tests/unit/platform-shadow-contract.test.ts`
|
||||
- Modify: `delivery-platform.yaml` and `test-assurance.lock.json` only to pin the released shadow versions
|
||||
|
||||
**Parity identity:** source revision, delivery manifest/catalog digests, test manifest/lock/policy digests, selected suite IDs, discovered/executed counts, terminal classifications, coverage production-module universe, HTTP declared/executed IDs, three browser results, candidate/member digests, source and artifact plan/evidence/assessment digests, supply-chain/provider digests, and promotion readiness. Exclude timestamp, duration, temp path, and runner identity.
|
||||
|
||||
- [ ] Add RED tests proving there is no product-local central workflow, the platform is the only command allowed to build/freeze a candidate in shadow, legacy release/promotion commands are read-only probes, and no product script invokes a provider or mutates desired state.
|
||||
- [ ] Before shadow execution, query actual Gitea registration/status state and require: legacy `quality-gates.yml` registration `disabled`; legacy required status `detached`; central workflow installed externally with activation `shadow`; no product `required-delivery-guard.yaml`; legacy candidate/provider/promotion invocation count `0`; platform candidate writer count exactly `1`. Record actual workflow/status IDs and writer identity.
|
||||
- [ ] Run the released platform fixture against this exact source revision for a passing run and deliberate failures: zero discovery, missing report, retry-only pass, missing browser, changed candidate, wrong provider digest, and response loss.
|
||||
- [ ] Have the platform runner invoke `run-legacy-assurance-probe.ts` read-only and compare its test classifications with platform assessment. Do not register/run the legacy workflow and do not run legacy candidate creation, publication, or promotion.
|
||||
- [ ] Record the actual signed parity report digest and environment identities only after the run exists. If the required external environment is unavailable, leave this task incomplete and retain shadow activation.
|
||||
- [ ] Re-run focused contract tests; expected PASS for repository constraints even if P2 evidence remains blocked.
|
||||
- [ ] Commit the shadow contract and runbook before external evidence:
|
||||
|
||||
```bash
|
||||
git add docs/operations/platform-shadow-parity.md tests/unit/platform-shadow-contract.test.ts delivery-platform.yaml test-assurance.lock.json
|
||||
git commit -m "test(shadow): define parity and one-writer contract"
|
||||
```
|
||||
|
||||
- [ ] Only after the real run creates `docs/operations/evidence/platform-shadow-readiness.json`, verify its digest/signature and commit that file alone as `test(shadow): record platform parity evidence`. If the environment is unavailable, do not create or stage the file and leave Task 6 incomplete.
|
||||
|
||||
---
|
||||
|
||||
### Task 7: Activate the platform and prove platform-only rollback
|
||||
|
||||
**Files:**
|
||||
|
||||
- Modify: `delivery-platform.yaml` only to select the signed active catalog version
|
||||
- Modify: `test-assurance.lock.json` only through the verified testctl binary
|
||||
- Modify: `docs/operations/release-cache-rollback.md`
|
||||
- Modify: `docs/operations/platform-shadow-parity.md`
|
||||
- Create: `tests/unit/platform-rollback-contract.test.ts`
|
||||
- Create from a real drill only: `docs/operations/evidence/platform-cutover-rollback.json`
|
||||
|
||||
**Cutover gate:** all ten frontend test capabilities are R1 or higher; every selected CI/CD capability is P1/shadow or higher; source revision and manifest/catalog/lock digests match; the passing run and every named fault fixture have zero parity mismatch; legacy writer count is zero and platform writer count is one; named P2 Gitea/runner/scanner/signer/provider evidence exists; central status `platform/delivery-pipeline` is installed; the previous signed platform pin rollback drill verifies served-content digest; and the WIP ledger has zero unclassified hunks.
|
||||
|
||||
**Rollback sequence:** pause new promotions → reconcile every indeterminate operation ID → pin the previous signed platform catalog/version → verify and use the previous `CICDCTL_BIN`/`TESTCTL_BIN` digests → compile both manifests → promote the previous stable immutable subject through `release-control` → verify served-content digest → resume. No product workflow, legacy writer, rebuild, repackage, or mutable tag is permitted.
|
||||
|
||||
- [ ] Add RED tests rejecting rollback text/code that restores `quality-gates.yml`, invokes `ci:gate`, enables a legacy writer, rebuilds a candidate, or omits signed catalog, executable, and subject digest checks.
|
||||
- [ ] Activate the externally installed central workflow/status only after every cutover-gate predicate is machine-verified; do not delete repository files in this task.
|
||||
- [ ] Run a staging rollback to the previous signed platform pin and previous immutable subject, then roll forward again. Record operation IDs, catalog/executable/release/served-content digests, Gitea status ID, writer identity, and reconciliation outcome.
|
||||
- [ ] Run `corepack pnpm exec vitest run tests/unit/platform-shadow-contract.test.ts tests/unit/platform-rollback-contract.test.ts`; expected PASS.
|
||||
- [ ] Commit runbook/test first. Commit `platform-cutover-rollback.json` separately only after a real signed drill exists; otherwise leave Task 7 incomplete.
|
||||
|
||||
---
|
||||
|
||||
### Task 8: Remove local assurance and delivery engines after rollback evidence
|
||||
|
||||
**Entry gate:** Task 7 has a signed evidence digest and served-content equality; removing local engines is forbidden before it.
|
||||
|
||||
**Files:**
|
||||
|
||||
- Delete: `.gitea/workflows/quality-gates.yml`
|
||||
- Delete: `config/ci/gates.json`
|
||||
- Delete: `scripts/generate-ci-workflow.ts`, `scripts/check-ci-contract.ts`, `scripts/run-ci-gate.ts`, `scripts/contracts/ci-gates.ts`
|
||||
- Delete: `scripts/lib/ci-contract-report.ts`, `scripts/lib/ci-gate-log.ts`, `scripts/lib/ci-step-result.ts`, `scripts/lib/ci-artifact-validator.ts`, `scripts/lib/ci-candidate-archive-cli.ts`, `scripts/lib/ci-candidate-archive.ts`, `scripts/lib/package-script-graph.ts`
|
||||
- Delete: `scripts/check-test-evidence.ts`, `scripts/verify-browser-capability-evidence.ts`, `scripts/check-risk-coverage.ts`, `scripts/lib/risk-coverage.ts`, `scripts/lib/local-policy-evidence.ts`, `scripts/verify-a11y-manual.ts`, `scripts/run-legacy-assurance-probe.ts`
|
||||
- Delete after raw codec extraction: `scripts/lib/manual-a11y-evidence.ts`
|
||||
- Delete: `scripts/create-release-candidate.ts`, `scripts/verify-reproducible-build.ts`, `scripts/verify-ci-candidate-archive.ts`, `scripts/verify-release-candidate.ts`, `scripts/run-and-validate-provider.ts`, `scripts/stage-verified-promotion.ts`, `scripts/verify-provider-evidence.ts`, `scripts/verify-supply-chain-promotion.ts`, `scripts/lib/release-candidate.ts`, `scripts/lib/promotion-stager.ts`, `scripts/lib/promotion-verifier.ts`, `scripts/lib/provider-evidence.ts`, `scripts/lib/provider-upload-validator.ts`
|
||||
- Delete: `scripts/security-scan.ts`, `scripts/generate-supply-chain.ts`, `scripts/verify-supply-chain-artifacts.ts`, `scripts/verify-archived-local-evidence.ts`, `scripts/check-supply-chain-provider-fixtures.ts`, `scripts/lib/local-release-evidence.ts`, `scripts/lib/release-input-evidence.ts`, `scripts/lib/supply-chain.ts`
|
||||
- Delete: `tests/unit/ci-workflow-generation.test.ts`, `tests/unit/__snapshots__/ci-workflow-generation.test.ts.snap`, `tests/unit/ci-step-result.test.ts`, and `tests/fixtures/ci-contract/`
|
||||
- Preserve/refactor: `scripts/contracts/release-artifacts.ts`, `scripts/generate-build-manifest.ts`, `scripts/generate-artifact-schemas.ts`, `scripts/lib/build-manifest-outputs.ts`, `scripts/lib/release-runtime-coherence.ts`, `scripts/lib/manual-a11y-record.ts`, and their product-schema tests
|
||||
- Modify: `package.json`
|
||||
- Modify: `docs/operations/ci-quality-gates.md`
|
||||
- Modify: `docs/security/supply-chain.md`
|
||||
- Create: `tests/unit/platform-engine-removal.test.ts`
|
||||
|
||||
- [ ] Recompute original dirty-worktree blob/hunk digests and stop on drift. Compare the current migration diff to the exact allowed path/hunk set in the ledger before deleting anything.
|
||||
- [ ] Add a RED removal test that rejects workflow-DAG types, Gitea expressions, artifact upload/download orchestration, test normalizers/assessors, provider/promotion mutation, exact job counts, `ci:gate`, and workflow generator/checker scripts in product paths.
|
||||
- [ ] Extract the raw manual accessibility record codec to `scripts/lib/manual-a11y-record.ts`, then delete only the assessment/expiry-completeness engine. Delete all other listed platform-owned files hunk-by-hunk; preserve product behavior, assertions, fixtures, and raw artifact codecs.
|
||||
- [ ] Remove `generate:ci-workflow`, `check:ci-workflow`, `ci:gate`, local `check:ci`, legacy assessor, provider/promotion, supply-chain, and candidate orchestration scripts from `package.json`. Keep build, lint/typecheck, one-purpose suites, raw reporters, and developer-only commands.
|
||||
- [ ] Run `corepack pnpm exec vitest run tests/unit/platform-engine-removal.test.ts tests/unit/delivery-platform-manifest.test.ts tests/unit/test-assurance-source-manifest.test.ts tests/unit/artifact-suite-contract.test.ts`, then `corepack pnpm check:types` and `corepack pnpm lint`; expected PASS.
|
||||
- [ ] Verify `git diff --name-status` exactly matches the ledger's Task 8 allowlist. Stage only the explicit files listed in this task; never use `git add scripts`, `git add tests`, or `git add -A`.
|
||||
- [ ] Commit as `refactor(platform): remove copied assurance and delivery engines`.
|
||||
|
||||
---
|
||||
|
||||
### Task 9: Finalize documentation, provenance, and consumer verification
|
||||
|
||||
**Files:**
|
||||
|
||||
- Modify: `docs/operations/release-cache-rollback.md`
|
||||
- Modify: `docs/operations/platform-shadow-parity.md`
|
||||
- Modify: `docs/security/supply-chain.md`
|
||||
- Modify: `README.md`
|
||||
- Modify and close: `docs/migration/task3-wip-provenance.json`
|
||||
- Modify: `docs/migration/task3-wip-disposition.md`
|
||||
|
||||
- [ ] Mark every WIP hunk `retained`, `reimplemented-in-platform`, `removed-after-cutover`, or `preserved-in-original`; require zero open disposition and reverify original dirty-worktree hashes.
|
||||
- [ ] Verify both binaries before invoking them:
|
||||
|
||||
```bash
|
||||
printf '%s %s\n' "$CICDCTL_DIGEST" "$CICDCTL_BIN" | sha256sum --check
|
||||
printf '%s %s\n' "$TESTCTL_DIGEST" "$TESTCTL_BIN" | sha256sum --check
|
||||
```
|
||||
|
||||
- [ ] Run final repository verification:
|
||||
|
||||
```bash
|
||||
corepack pnpm install --frozen-lockfile
|
||||
corepack pnpm check:types
|
||||
corepack pnpm lint
|
||||
corepack pnpm test:all
|
||||
corepack pnpm exec vitest run tests/unit/delivery-platform-manifest.test.ts tests/unit/test-assurance-source-manifest.test.ts tests/unit/artifact-suite-contract.test.ts tests/unit/platform-engine-removal.test.ts tests/unit/platform-rollback-contract.test.ts
|
||||
"$TESTCTL_BIN" validate --repository . --out artifacts/platform/final-validation.json
|
||||
"$CICDCTL_BIN" manifest compile --manifest delivery-platform.yaml --source-revision "$PLATFORM_SOURCE_REVISION" --output artifacts/platform/effective-project.json
|
||||
git diff --check
|
||||
```
|
||||
|
||||
Expected: PASS. `PLATFORM_SOURCE_REVISION` is the exact 40-hex revision recorded by the platform run, not a branch or mutable lookup. Product tree contains manifests and product tests, not copied platform engines.
|
||||
- [ ] Commit the two migration ledger files and exact modified docs/README as `docs(platform): finalize consumer cutover`.
|
||||
|
||||
## Cross-repository execution order
|
||||
|
||||
1. Complete and release the Test Assurance plan through R1/v3 conformance.
|
||||
2. Complete and release the CI/CD plan through P1/shadow vertical conformance.
|
||||
3. Execute frontend Tasks 1–5 and validate both manifests locally.
|
||||
4. Execute Task 6 only with external one-writer state verified.
|
||||
5. Execute Task 7 only after every named P2 gate exists and prove rollback before deletion.
|
||||
6. Execute Tasks 8–9 only after signed rollback evidence. Until then, the correct state is shadow with legacy comparison code retained and no false active claim.
|
||||
@@ -0,0 +1,572 @@
|
||||
# Test Assurance Frontend Capability Implementation Plan
|
||||
|
||||
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||
|
||||
**Goal:** Extend `test-assurance-platform` so it is the sole authority for selecting, planning, executing, normalizing, evidencing, and assessing every frontend product suite, including tests bound to an immutable static-site candidate.
|
||||
|
||||
**Architecture:** The platform keeps v2 JVM documents readable, emits v3 documents for new work, and requires an explicit `SOURCE` or `ARTIFACT` phase plus exact input-artifact identities throughout the execution/evidence chain. A normalizer registry dispatches strict report adapters by capability. Each frontend capability has an independent catalog contract and readiness card; no aggregate frontend readiness is introduced.
|
||||
|
||||
**Tech Stack:** Java 21, Gradle Kotlin DSL, Jackson, JSON Schema 2020-12, Node.js 24.14.0, pnpm 11.17.0, TypeScript 7.0.2, Vitest 4.1.10, Playwright 1.62.0.
|
||||
|
||||
## Global Constraints
|
||||
|
||||
- Repository: `/home/donghyeon/workspace/desktop-server-git/test-assurance-platform`; every command runs from its isolated worktree root.
|
||||
- Test assurance owns test meaning; it does not create Gitea jobs, allocate remote runners, publish builds, or promote releases.
|
||||
- New frontend suites require `test-assurance.platform/v3`; there is no implicit phase or host-toolchain fallback.
|
||||
- `requiredInputArtifacts` is an ordered canonical list of `{artifactId, mediaType, sha256}`. Paths are execution-local data and never artifact identity.
|
||||
- v2 and v3 evidence cannot be bundled or assessed together.
|
||||
- Missing, empty, oversized, malformed, symlinked, mismatched, zero-discovery, all-skipped, or retry-only-green evidence fails closed.
|
||||
- The exact capability IDs are `unit-typescript-vitest`, `component-react-vitest`, `integration-http-msw`, `architecture-typescript`, `coverage-v8`, `e2e-playwright-chromium`, `e2e-playwright-firefox`, `e2e-playwright-webkit`, `accessibility-web`, and `visual-regression-web`.
|
||||
|
||||
---
|
||||
|
||||
### Task 1: Introduce the v3 artifact-bound execution identity
|
||||
|
||||
**Files:**
|
||||
|
||||
- Modify: `modules/assurance-model/src/main/java/io/testassurance/model/ApiVersion.java`
|
||||
- Create: `modules/assurance-model/src/main/java/io/testassurance/model/ExecutionPhase.java`
|
||||
- Create: `modules/assurance-model/src/main/java/io/testassurance/model/InputArtifact.java`
|
||||
- Create: `modules/assurance-model/src/main/java/io/testassurance/model/ArtifactInputDeclaration.java`
|
||||
- Create: `modules/assurance-model/src/main/java/io/testassurance/model/ArtifactSuiteTemplate.java`
|
||||
- Create: `modules/assurance-model/src/main/java/io/testassurance/model/InputArtifactMap.java`
|
||||
- Modify: `modules/assurance-model/src/main/java/io/testassurance/model/SuiteDefinition.java`
|
||||
- Modify: `modules/assurance-model/src/main/java/io/testassurance/model/WorkItem.java`
|
||||
- Modify: `modules/assurance-model/src/main/java/io/testassurance/model/ExecutionRequest.java`
|
||||
- Modify: `modules/assurance-model/src/main/java/io/testassurance/model/ExecutionPlan.java`
|
||||
- Modify: `modules/assurance-model/src/main/java/io/testassurance/model/RawSuiteResult.java`
|
||||
- Modify: `modules/assurance-model/src/main/java/io/testassurance/model/RawResultSet.java`
|
||||
- Modify: `modules/assurance-model/src/main/java/io/testassurance/model/NormalizedSuiteResult.java`
|
||||
- Modify: `modules/assurance-model/src/main/java/io/testassurance/model/EvidenceBundle.java`
|
||||
- Modify: `modules/assurance-model/src/main/java/io/testassurance/model/Assessment.java`
|
||||
- Modify: `modules/assurance-model/src/main/java/io/testassurance/model/RepositoryManifest.java`
|
||||
- Modify: `modules/assurance-model/src/main/java/io/testassurance/model/CompiledPolicy.java`
|
||||
- Modify: `modules/assurance-model/src/main/java/io/testassurance/model/json/JsonReader.java`
|
||||
- Modify: `modules/assurance-model/src/main/java/io/testassurance/model/json/JsonWriter.java`
|
||||
- Create: `modules/assurance-model/src/main/java/io/testassurance/model/LegacyV2Decoder.java`
|
||||
- Modify: `modules/assurance-model/src/test/java/io/testassurance/model/ModelRoundTripTest.java`
|
||||
- Modify: `modules/assurance-model/src/test/java/io/testassurance/model/RepositoryFixtures.java`
|
||||
- Preserve unchanged: all 26 current v2 files under `schemas/*.schema.json`
|
||||
- Create: 27 complete v3 wire schemas under `schemas/v3/`, one for every current kind plus `artifact-suite-template.schema.json`
|
||||
- Create: `schemas/v3/input-artifact-map.schema.json` and register it as an execution-only v3 kind
|
||||
- Modify: `modules/assurance-schema/src/main/java/io/testassurance/schema/SchemaRegistry.java`
|
||||
- Modify: `modules/assurance-schema/src/test/java/io/testassurance/schema/SchemaRegistryTest.java`
|
||||
- Modify: `tools/validate_package.py`
|
||||
- Modify: `machine/example-schema-map.json`
|
||||
- Modify: `machine/adversarial-schema-map.json`
|
||||
- Create: `examples/v3/artifact-suite-template.json`
|
||||
- Create: `examples/v3/input-artifact-map.json`
|
||||
- Modify: `modules/assurance-schema/src/main/java/io/testassurance/schema/SchemaRegistry.java`
|
||||
- Modify: `modules/assurance-schema/src/test/java/io/testassurance/schema/SchemaRegistryTest.java`
|
||||
- Create: `conformance/adversarial/v3-artifact-suite-missing-input.json`
|
||||
- Create: `conformance/adversarial/v3-source-suite-with-input.json`
|
||||
- Create: `conformance/adversarial/v2-v3-evidence-mix.json`
|
||||
|
||||
**Interfaces:**
|
||||
|
||||
```java
|
||||
public enum ExecutionPhase { SOURCE, ARTIFACT }
|
||||
|
||||
public record InputArtifact(String artifactId, String mediaType, String sha256) {
|
||||
public InputArtifact {
|
||||
Identifier.require(artifactId, "inputArtifact.artifactId");
|
||||
Values.requireText(mediaType, "inputArtifact.mediaType");
|
||||
Digests.require(sha256, "inputArtifact.sha256");
|
||||
}
|
||||
}
|
||||
|
||||
public record ArtifactInputDeclaration(String artifactId, String mediaType) {}
|
||||
|
||||
public record ArtifactSuiteTemplate(
|
||||
String id,
|
||||
String capabilityId,
|
||||
List<String> command,
|
||||
int caseTimeoutSeconds,
|
||||
int suiteTimeoutSeconds,
|
||||
List<String> requiredArtifacts,
|
||||
List<String> environmentAllowlist,
|
||||
List<ArtifactInputDeclaration> requiredInputArtifacts,
|
||||
Optional<ObjectNode> parameters) {}
|
||||
|
||||
public final class ApiVersion {
|
||||
public static final String V2 = "test-assurance.platform/v2";
|
||||
public static final String V3 = "test-assurance.platform/v3";
|
||||
public static final String CURRENT = V3;
|
||||
}
|
||||
```
|
||||
|
||||
All nine execution-chain records expose `apiVersion()`, `executionPhase()`, and `requiredInputArtifacts()`; `SOURCE` requires an empty input list and `ARTIFACT` requires at least one item. `JsonWriter.envelope(String kind, String apiVersion)` requires an explicit version. Existing `toJson()` methods remain v2-compatible; every new v3 output path calls `toJson(ApiVersion.V3)`. `LegacyV2Decoder` reads the complete v2 repository graph explicitly and ordinary v3 readers never guess a phase. `ExecutionRequest.applicationArtifactDigest` exists only in the frozen v2 decoder and is absent from v3.
|
||||
|
||||
In v3, `RawSuiteResult.artifacts` is `List<ArtifactReference>` rather than path strings. The executor computes each digest immediately after the child exits and before results become visible to normalization. This makes raw artifact identity, normalized references, and evidence chain equality directly verifiable.
|
||||
|
||||
The committed repository cannot know a future candidate digest. Therefore the approved design amendment adds `RepositoryManifest.artifactSuiteTemplateFiles`: those files declare only artifact ID and media type and are not executable wire `SuiteDefinition` documents. During artifact planning, testctl matches them to concrete `ExecutionRequest.requiredInputArtifacts` and materializes v3 `SuiteDefinition`/`WorkItem` values containing the actual SHA-256. The compiler rejects a digest or digest placeholder in a template. `LegacyV2Decoder` is the only API that maps an approved v2 JVM graph into the legacy SOURCE compatibility path.
|
||||
|
||||
- [ ] Add model and schema tests for valid SOURCE v3, valid materialized ARTIFACT v3, absent phase, blank digest, duplicate artifact ID, SOURCE with artifacts, ARTIFACT without artifacts, a template containing any SHA field, v2 JVM graph acceptance, v2 frontend rejection, v2/v3 evidence mixing rejection, and byte-identical frozen v2 schema files.
|
||||
- [ ] Run:
|
||||
|
||||
```bash
|
||||
./gradlew :modules:assurance-model:test :modules:assurance-schema:test
|
||||
```
|
||||
|
||||
Expected: RED because `ExecutionPhase`, `InputArtifact`, explicit-version envelopes, and the v3 schema registry do not exist.
|
||||
|
||||
- [ ] Implement explicit-version codecs and a complete v3 schema set. `SchemaRegistry` keys every schema by `(apiVersion, kind)`. `tools/validate_package.py` validates 26 v2 wire schemas, 28 v3 wire/execution schemas, and 11 report schemas as separate inventories rather than one hard-coded total.
|
||||
- [ ] Run `./gradlew :modules:assurance-model:test :modules:assurance-schema:test`; expected PASS.
|
||||
- [ ] Commit:
|
||||
|
||||
```bash
|
||||
git add modules/assurance-model modules/assurance-schema schemas conformance/adversarial tools/validate_package.py machine examples/v3
|
||||
git commit -m "feat(contracts): add artifact-bound execution v3"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 2: Propagate phase and input identity through compile, selection, and planning
|
||||
|
||||
**Files:**
|
||||
|
||||
- Modify: `modules/assurance-compiler/src/main/java/io/testassurance/compiler/PolicyCompiler.java`
|
||||
- Modify: `modules/assurance-compiler/src/test/java/io/testassurance/compiler/PolicyCompilerTest.java`
|
||||
- Modify: `modules/assurance-selector/src/main/java/io/testassurance/selector/ChangeSelector.java`
|
||||
- Modify: `modules/assurance-selector/src/test/java/io/testassurance/selector/ChangeSelectorTest.java`
|
||||
- Modify: `modules/assurance-plan/src/main/java/io/testassurance/plan/PlanEngine.java`
|
||||
- Modify: `modules/assurance-plan/src/main/java/io/testassurance/plan/ResourceProfiles.java`
|
||||
- Modify: `modules/assurance-plan/src/test/java/io/testassurance/plan/PlanEngineTest.java`
|
||||
- Modify: `modules/assurance-model/src/main/java/io/testassurance/model/CompiledPolicy.java`
|
||||
- Modify: `modules/assurance-model/src/main/java/io/testassurance/model/SelectionResult.java`
|
||||
|
||||
**Contract:** The compiler rejects any new frontend capability on v2. The selector preserves the suite phase without changing selected obligations. The planner copies the exact ordered artifact tuple from the request into every selected ARTIFACT work item and rejects a request whose tuple differs from the suite declaration. Plan digest calculation includes phase and artifact tuple.
|
||||
|
||||
`PolicyCompiler` stores source `SuiteDefinition` values and artifact `ArtifactSuiteTemplate` values separately. `PlanEngine` exposes `materializeArtifactSuite(ArtifactSuiteTemplate, ExecutionRequest)`; it requires an exact one-to-one match on artifact ID/media type, supplies the request SHA-256, and returns an executable v3 `SuiteDefinition`. No template or unresolved artifact can be serialized into `ExecutionPlan`.
|
||||
|
||||
- [ ] Add RED tests named `frontendCapabilityRequiresV3`, `artifactTemplateRejectsDigestPlaceholder`, `artifactTemplateMaterializesConcreteSuite`, `selectorPreservesExecutionPhase`, `artifactPlanRejectsWrongCandidateDigest`, `sourcePlanRejectsArtifactInput`, and `planDigestChangesWithArtifactDigest`.
|
||||
- [ ] Run:
|
||||
|
||||
```bash
|
||||
./gradlew :modules:assurance-compiler:test :modules:assurance-selector:test :modules:assurance-plan:test
|
||||
```
|
||||
|
||||
Expected: RED because the current compiler and planner do not carry phase/input identity.
|
||||
|
||||
- [ ] Implement exact propagation. Do not infer phase from capability name or from `applicationArtifactDigest`.
|
||||
- [ ] Run `./gradlew :modules:assurance-compiler:test :modules:assurance-selector:test :modules:assurance-plan:test`; expected PASS.
|
||||
- [ ] Commit:
|
||||
|
||||
```bash
|
||||
git add modules/assurance-compiler modules/assurance-selector modules/assurance-plan modules/assurance-model
|
||||
git commit -m "feat(plan): preserve source and artifact execution identity"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 3: Register the frontend capability family and immutable toolchains
|
||||
|
||||
**Files:**
|
||||
|
||||
- Modify: `machine/capability-catalog.json`
|
||||
- Modify: `machine/toolchain-catalog.json`
|
||||
- Modify: `gradle/libs.versions.toml`
|
||||
- Modify: `modules/assurance-catalog/src/main/java/io/testassurance/catalog/CapabilityCatalog.java`
|
||||
- Modify: `modules/assurance-catalog/src/main/java/io/testassurance/catalog/CapabilityContract.java`
|
||||
- Modify: `modules/assurance-catalog/src/main/java/io/testassurance/catalog/ToolchainCatalog.java`
|
||||
- Create: `modules/assurance-catalog/src/main/java/io/testassurance/catalog/FrontendToolchainInspector.java`
|
||||
- Create: `modules/assurance-catalog/src/test/java/io/testassurance/catalog/FrontendToolchainInspectorTest.java`
|
||||
- Modify: `modules/assurance-catalog/src/test/java/io/testassurance/catalog/CatalogsTest.java`
|
||||
- Modify: `modules/assurance-catalog/src/test/java/io/testassurance/catalog/ReadinessCardsTest.java`
|
||||
- Modify: `modules/assurance-plan/src/main/java/io/testassurance/plan/ResourceProfiles.java`
|
||||
- Modify: `modules/testctl-cli/src/main/java/io/testassurance/cli/Commands.java`
|
||||
- Modify: `modules/testctl-cli/src/test/java/io/testassurance/cli/TestctlMainTest.java`
|
||||
- Modify: `tools/validate_package.py`
|
||||
- Modify: `docs/04-test-type-contracts.md`
|
||||
- Modify: `README.md`
|
||||
- Create: `readiness/unit-typescript-vitest.json`
|
||||
- Create: `readiness/component-react-vitest.json`
|
||||
- Create: `readiness/integration-http-msw.json`
|
||||
- Create: `readiness/architecture-typescript.json`
|
||||
- Create: `readiness/coverage-v8.json`
|
||||
- Modify: `readiness/e2e-playwright-chromium.json`
|
||||
- Create: `readiness/e2e-playwright-firefox.json`
|
||||
- Create: `readiness/e2e-playwright-webkit.json`
|
||||
- Create: `readiness/accessibility-web.json`
|
||||
- Create: `readiness/visual-regression-web.json`
|
||||
|
||||
**Catalog entries:** The catalog grows from 16 to 25 capabilities: nine new entries plus the upgraded existing Chromium entry form the ten-capability frontend family. Every capability declares contract revision v3, execution phase, required raw artifact IDs, case/suite timeout bounds, isolation/resource claims, maximum evidence age, false-green rule IDs, and minimum readiness. Toolchain entries pin version plus observed SHA-256 for Node.js 24.14.0, pnpm 11.17.0, TypeScript 7.0.2, Vitest 4.1.10, Playwright 1.62.0, and independent Chromium/Firefox/WebKit payload IDs.
|
||||
|
||||
`FrontendToolchainInspector` reads `.nvmrc`, `package.json.packageManager`, exact devDependency versions, and the root importer in `pnpm-lock.yaml`. It rejects semver ranges, workspace/host fallbacks, missing artifact digests, and lock resolution drift with `TA-LOCK-002`/`TA-LOCK-003`. `Commands.lock()` uses this inspector for any selected frontend capability.
|
||||
|
||||
| Capability | Phase | Required raw artifacts |
|
||||
|---|---|---|
|
||||
| `unit-typescript-vitest` | SOURCE | JUnit XML, Vitest discovery JSON |
|
||||
| `component-react-vitest` | SOURCE | JUnit XML, Vitest discovery JSON |
|
||||
| `integration-http-msw` | SOURCE | JUnit XML, Vitest discovery JSON, typed HTTP scenario receipts |
|
||||
| `architecture-typescript` | SOURCE | TypeScript architecture graph/violations JSON |
|
||||
| `coverage-v8` | SOURCE | V8 coverage summary, production-module inventory |
|
||||
| `e2e-playwright-chromium` | ARTIFACT | Playwright report, provider identity, trace/screenshot/console/network indexes, mutation receipts |
|
||||
| `e2e-playwright-firefox` | ARTIFACT | Playwright report, provider identity, trace/screenshot/console/network indexes, mutation receipts |
|
||||
| `e2e-playwright-webkit` | ARTIFACT | Playwright report, provider identity, trace/screenshot/console/network indexes, mutation receipts |
|
||||
| `accessibility-web` | ARTIFACT | Playwright report, provider identity, accessibility findings, manual-review records |
|
||||
| `visual-regression-web` | ARTIFACT | Playwright report, provider identity, baseline identity, image-diff results |
|
||||
|
||||
- [ ] Add tests that require exactly 25 catalog capabilities and one readiness card per capability, and fail on a missing digest, Node 22 fallback, semver range, pnpm lock drift, capability aliasing, a combined browser readiness card, or missing Firefox/WebKit resource profile.
|
||||
- [ ] Run:
|
||||
|
||||
```bash
|
||||
./gradlew :modules:assurance-catalog:test :modules:assurance-plan:test
|
||||
python3 tools/validate_package.py
|
||||
```
|
||||
|
||||
Expected: RED because the capabilities and toolchains are absent.
|
||||
|
||||
- [ ] Add independent catalog/resource entries and keep all nine new cards plus Chromium at R0 with explicit `nonGuarantees`. R1 is set only in Task 10 after real adapter and conformance evidence exists.
|
||||
- [ ] Run `./gradlew :modules:assurance-catalog:test :modules:assurance-plan:test` and `python3 tools/validate_package.py`; expected PASS.
|
||||
- [ ] Commit:
|
||||
|
||||
```bash
|
||||
git add machine readiness gradle/libs.versions.toml modules/assurance-catalog modules/assurance-plan modules/testctl-cli tools/validate_package.py docs/04-test-type-contracts.md README.md
|
||||
git commit -m "feat(catalog): register frontend assurance capabilities"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 4: Replace hard-coded JUnit normalization with a strict registry
|
||||
|
||||
**Files:**
|
||||
|
||||
- Modify: `modules/assurance-normalizer/src/main/java/io/testassurance/normalizer/SuiteResultNormalizer.java`
|
||||
- Modify: `modules/assurance-normalizer/src/main/java/io/testassurance/normalizer/NormalizationContext.java`
|
||||
- Create: `modules/assurance-normalizer/src/main/java/io/testassurance/normalizer/NormalizerRegistry.java`
|
||||
- Create: `modules/assurance-normalizer/src/main/java/io/testassurance/normalizer/NormalizationArtifactReader.java`
|
||||
- Create: `modules/assurance-normalizer/src/test/java/io/testassurance/normalizer/NormalizerRegistryTest.java`
|
||||
- Create: `modules/assurance-normalizer/src/test/java/io/testassurance/normalizer/NormalizationArtifactReaderTest.java`
|
||||
- Create: `modules/assurance-schema/src/main/java/io/testassurance/schema/ReportSchemaRegistry.java`
|
||||
- Create: `modules/assurance-schema/src/test/java/io/testassurance/schema/ReportSchemaRegistryTest.java`
|
||||
- Create report schemas under `schemas/reports/`: `vitest-discovery-report.schema.json`, `http-scenario-receipt-set.schema.json`, `v8-coverage-summary.schema.json`, `production-module-inventory.schema.json`, `typescript-architecture-report.schema.json`, `playwright-suite-report.schema.json`, `browser-provider-report.schema.json`, `browser-mutation-receipt-set.schema.json`, `accessibility-report.schema.json`, `accessibility-manual-review.schema.json`, `visual-regression-report.schema.json`
|
||||
- Modify: `modules/assurance-schema/build.gradle.kts`
|
||||
- Modify: `modules/testctl-cli/src/main/java/io/testassurance/cli/Commands.java`
|
||||
- Modify: `modules/testctl-cli/src/test/java/io/testassurance/cli/TestctlMainTest.java`
|
||||
- Modify: `modules/testctl-cli/build.gradle.kts`
|
||||
|
||||
**Interfaces:**
|
||||
|
||||
```java
|
||||
public interface SuiteResultNormalizer {
|
||||
String adapterId();
|
||||
Set<String> capabilityIds();
|
||||
NormalizedSuiteResult normalize(NormalizationContext context);
|
||||
}
|
||||
|
||||
public final class NormalizerRegistry {
|
||||
public NormalizerRegistry(Collection<SuiteResultNormalizer> normalizers);
|
||||
public SuiteResultNormalizer requireFor(String capabilityId);
|
||||
}
|
||||
|
||||
public final class NormalizationArtifactReader {
|
||||
public NormalizationArtifactReader(ReportSchemaRegistry schemas);
|
||||
public byte[] readRequired(Path outputRoot, String repositoryRelativePath, long maximumBytes);
|
||||
public JsonNode readRequiredJson(Path outputRoot, String repositoryRelativePath, long maximumBytes, ReportSchemaRegistry.ReportKind reportKind);
|
||||
public ArtifactReference reference(Path outputRoot, String repositoryRelativePath, String mediaType, long maximumBytes);
|
||||
}
|
||||
```
|
||||
|
||||
The registry rejects duplicate adapter IDs, duplicate capability ownership, and zero matches. `Commands.normalize()` asks the registry for exactly one adapter and no longer constructs `JunitXmlNormalizer` directly. The common reader uses `NOFOLLOW_LINKS`, checks a regular file before reading, bounds bytes, rejects empty or malformed UTF-8, canonicalizes repository-relative paths, and validates JSON against `ReportSchemaRegistry` before adapters inspect fields.
|
||||
|
||||
- [ ] Add RED tests for no match, two owners, stable registration order, adapter exception mapping, preservation of v3 input artifacts, path escape, symlink, empty file, oversized file, malformed UTF-8, unknown report schema, and `additionalProperties` rejection.
|
||||
- [ ] Run:
|
||||
|
||||
```bash
|
||||
./gradlew :modules:assurance-normalizer:test :modules:testctl-cli:test
|
||||
```
|
||||
|
||||
Expected: RED because dispatch is hard-coded.
|
||||
|
||||
- [ ] Implement registry wiring with explicit constructors in `testctl-cli`; do not use classpath scanning.
|
||||
- [ ] Run `./gradlew :modules:assurance-normalizer:test :modules:testctl-cli:test`; expected PASS.
|
||||
- [ ] Commit:
|
||||
|
||||
```bash
|
||||
git add modules/assurance-normalizer modules/assurance-schema modules/testctl-cli schemas/reports
|
||||
git commit -m "refactor(normalizer): dispatch by capability contract"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 5: Normalize Vitest results and V8 production coverage
|
||||
|
||||
**Files:**
|
||||
|
||||
- Create: `adapters/vitest/build.gradle.kts`
|
||||
- Create: `adapters/vitest/src/main/java/io/testassurance/adapter/vitest/VitestJunitNormalizer.java`
|
||||
- Create: `adapters/vitest/src/main/java/io/testassurance/adapter/vitest/VitestDiscoveryReport.java`
|
||||
- Create: `adapters/vitest/src/test/java/io/testassurance/adapter/vitest/VitestJunitNormalizerTest.java`
|
||||
- Create fixture directories: `adapters/vitest/src/test/resources/valid/`, `adapters/vitest/src/test/resources/zero-tests/`, `adapters/vitest/src/test/resources/all-skipped/`, `adapters/vitest/src/test/resources/retry-green/`, `adapters/vitest/src/test/resources/missing-discovery/`, `adapters/vitest/src/test/resources/malformed/`, `adapters/vitest/src/test/resources/oversized/`, `adapters/vitest/src/test/resources/symlink/`
|
||||
- Create: `adapters/v8-coverage/build.gradle.kts`
|
||||
- Create: `adapters/v8-coverage/src/main/java/io/testassurance/adapter/coverage/V8CoverageNormalizer.java`
|
||||
- Create: `adapters/v8-coverage/src/main/java/io/testassurance/adapter/coverage/ProductionModuleInventory.java`
|
||||
- Create: `adapters/v8-coverage/src/test/java/io/testassurance/adapter/coverage/V8CoverageNormalizerTest.java`
|
||||
- Create fixture directories: `adapters/v8-coverage/src/test/resources/valid/`, `adapters/v8-coverage/src/test/resources/missing-module/`, `adapters/v8-coverage/src/test/resources/zero-universe/`, `adapters/v8-coverage/src/test/resources/path-mismatch/`, `adapters/v8-coverage/src/test/resources/malformed/`, `adapters/v8-coverage/src/test/resources/oversized/`, `adapters/v8-coverage/src/test/resources/symlink/`
|
||||
- Modify: `settings.gradle.kts`
|
||||
- Modify: `build.gradle.kts`
|
||||
- Modify: `modules/testctl-cli/build.gradle.kts`
|
||||
- Modify: `modules/testctl-cli/src/main/java/io/testassurance/cli/Commands.java`
|
||||
|
||||
**Module dependencies:** Both modules expose the model/normalizer APIs, implement report schema support, and are added to root `javaModules`. Vitest additionally depends on `:adapters:junit-gradle` to reuse secure XML parsing.
|
||||
|
||||
**Rules:** `VitestJunitNormalizer` owns only `unit-typescript-vitest` and `component-react-vitest`; it exports `VitestReportReader` for HTTP composition. `V8CoverageNormalizer` alone owns `coverage-v8`. Missing production module is `INCOMPLETE_DELETED_TEST`; non-empty inventory with zero counter-bearing modules is `INCOMPLETE_ZERO_TESTS`; summary/inventory count or digest mismatch is `INVALID_RESULT`; configured threshold miss is `FAIL_PRODUCT`.
|
||||
|
||||
- [ ] Write all adapter tests before implementation and confirm RED:
|
||||
|
||||
```bash
|
||||
./gradlew :adapters:vitest:test :adapters:v8-coverage:test
|
||||
```
|
||||
|
||||
- [ ] Implement bounded regular-file reads, fatal UTF-8, secure XML, canonical repository-relative paths, cross-file count reconciliation, and no symlink following.
|
||||
- [ ] Register the three capability owners in the CLI registry.
|
||||
- [ ] Re-run focused tests plus `:modules:testctl-cli:test`; expected PASS.
|
||||
- [ ] Commit:
|
||||
|
||||
```bash
|
||||
git add adapters/vitest adapters/v8-coverage settings.gradle.kts build.gradle.kts modules/testctl-cli
|
||||
git commit -m "feat(normalizers): add vitest and v8 coverage evidence"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 6: Normalize HTTP scenarios and TypeScript architecture
|
||||
|
||||
**Files:**
|
||||
|
||||
- Create: `adapters/http-scenario/build.gradle.kts`
|
||||
- Create: `adapters/http-scenario/src/main/java/io/testassurance/adapter/http/HttpScenarioNormalizer.java`
|
||||
- Create: `adapters/http-scenario/src/test/java/io/testassurance/adapter/http/HttpScenarioNormalizerTest.java`
|
||||
- Create fixture directories: `adapters/http-scenario/src/test/resources/valid/`, `adapters/http-scenario/src/test/resources/missing-receipt/`, `adapters/http-scenario/src/test/resources/duplicate-receipt/`, `adapters/http-scenario/src/test/resources/unknown-scenario/`, `adapters/http-scenario/src/test/resources/status-mismatch/`, `adapters/http-scenario/src/test/resources/malformed/`
|
||||
- Create: `adapters/typescript-architecture/build.gradle.kts`
|
||||
- Create: `adapters/typescript-architecture/src/main/java/io/testassurance/adapter/architecture/TypeScriptArchitectureNormalizer.java`
|
||||
- Create: `adapters/typescript-architecture/src/test/java/io/testassurance/adapter/architecture/TypeScriptArchitectureNormalizerTest.java`
|
||||
- Create fixture directories: `adapters/typescript-architecture/src/test/resources/valid/`, `adapters/typescript-architecture/src/test/resources/empty-graph/`, `adapters/typescript-architecture/src/test/resources/unresolved-import/`, `adapters/typescript-architecture/src/test/resources/cycle/`, `adapters/typescript-architecture/src/test/resources/violation/`, `adapters/typescript-architecture/src/test/resources/path-escape/`, `adapters/typescript-architecture/src/test/resources/malformed/`
|
||||
- Modify: `settings.gradle.kts`
|
||||
- Modify: `build.gradle.kts`
|
||||
- Modify: `modules/testctl-cli/build.gradle.kts`
|
||||
- Modify: `modules/testctl-cli/src/main/java/io/testassurance/cli/Commands.java`
|
||||
|
||||
**Module dependencies:** Both modules are added to root `javaModules`; HTTP depends on `:adapters:vitest`, and both depend on model, normalizer, and schema modules.
|
||||
|
||||
**Typed inputs:** `HttpScenarioNormalizer` is the sole owner of `integration-http-msw` and composes `VitestReportReader`. It compares exact declared/executed IDs; a missing receipt is `INCOMPLETE_DELETED_TEST`, duplicate/unknown receipt or catalog mismatch is `INVALID_RESULT`, and unmatched/catch-all behavior is `FAIL_TEST`. Architecture requires non-empty modules/dependencies; empty graph is `INCOMPLETE_ZERO_TESTS`, malformed/parser mismatch is `ERROR_TOOLING` or `INVALID_RESULT`, and unresolved import/cycle/rule violation is `FAIL_PRODUCT`.
|
||||
|
||||
- [ ] Add fixtures/tests and run focused Gradle tests; expected RED.
|
||||
- [ ] Implement bounded `ReportSchemaRegistry` parsing, exact scenario/graph cross-field reconciliation, and sole capability ownership for both adapters.
|
||||
- [ ] Re-run focused tests and CLI tests; expected PASS.
|
||||
- [ ] Commit:
|
||||
|
||||
```bash
|
||||
git add adapters/http-scenario adapters/typescript-architecture settings.gradle.kts build.gradle.kts modules/testctl-cli
|
||||
git commit -m "feat(normalizers): add http and architecture evidence"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 7: Upgrade Playwright into three independent browser capabilities
|
||||
|
||||
**Files:**
|
||||
|
||||
- Create: `adapters/playwright/src/main/java/io/testassurance/adapter/playwright/PlaywrightResultNormalizer.java`
|
||||
- Create: `adapters/playwright/src/main/java/io/testassurance/adapter/playwright/BrowserArtifactIndex.java`
|
||||
- Create: `adapters/playwright/src/main/java/io/testassurance/adapter/playwright/MutationReceipt.java`
|
||||
- Modify: `adapters/playwright/src/main/java/io/testassurance/adapter/playwright/BrowserRunPolicy.java`
|
||||
- Create: `adapters/playwright/src/test/java/io/testassurance/adapter/playwright/PlaywrightResultNormalizerTest.java`
|
||||
- Create fixture directories: `adapters/playwright/src/test/resources/chromium/`, `adapters/playwright/src/test/resources/firefox/`, `adapters/playwright/src/test/resources/webkit/`, `adapters/playwright/src/test/resources/missing-project/`, `adapters/playwright/src/test/resources/retry-green/`, `adapters/playwright/src/test/resources/zero-tests/`, `adapters/playwright/src/test/resources/missing-trace/`, `adapters/playwright/src/test/resources/console-error/`, `adapters/playwright/src/test/resources/network-error/`, `adapters/playwright/src/test/resources/valid-mutation/`, `adapters/playwright/src/test/resources/missing-mutation-read/`, `adapters/playwright/src/test/resources/malformed/`
|
||||
- Modify: `modules/testctl-cli/src/main/java/io/testassurance/cli/Commands.java`
|
||||
|
||||
**Rules:** The selected browser capability must match the report project exactly. Required trace/screenshot/console/network indexes bind repository-relative files by digest. Retry-only pass is FLAKY, not PASS. Write journeys require observed response, mutation read, and reload read receipts bound to the same scenario/candidate.
|
||||
|
||||
- [ ] Add RED tests for each browser and every false-green case.
|
||||
- [ ] Run:
|
||||
|
||||
```bash
|
||||
./gradlew :adapters:playwright:test
|
||||
```
|
||||
|
||||
Expected: RED because only policy helpers exist.
|
||||
|
||||
- [ ] Implement one normalizer parameterized by three independently registered capability IDs; do not aggregate readiness or results.
|
||||
- [ ] Run focused and CLI tests; expected PASS.
|
||||
- [ ] Commit:
|
||||
|
||||
```bash
|
||||
git add adapters/playwright modules/testctl-cli
|
||||
git commit -m "feat(playwright): normalize independent browser evidence"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 8: Add accessibility and visual-regression evidence
|
||||
|
||||
**Files:**
|
||||
|
||||
- Create: `adapters/accessibility-web/build.gradle.kts`
|
||||
- Create: `adapters/accessibility-web/src/main/java/io/testassurance/adapter/accessibility/AccessibilityNormalizer.java`
|
||||
- Create: `adapters/accessibility-web/src/test/java/io/testassurance/adapter/accessibility/AccessibilityNormalizerTest.java`
|
||||
- Create fixture directories: `adapters/accessibility-web/src/test/resources/valid/`, `adapters/accessibility-web/src/test/resources/violation/`, `adapters/accessibility-web/src/test/resources/missing-provider/`, `adapters/accessibility-web/src/test/resources/missing-manual-review/`, `adapters/accessibility-web/src/test/resources/expired-review/`, `adapters/accessibility-web/src/test/resources/malformed/`
|
||||
- Create: `adapters/visual-regression-web/build.gradle.kts`
|
||||
- Create: `adapters/visual-regression-web/src/main/java/io/testassurance/adapter/visual/VisualRegressionNormalizer.java`
|
||||
- Create: `adapters/visual-regression-web/src/test/java/io/testassurance/adapter/visual/VisualRegressionNormalizerTest.java`
|
||||
- Create fixture directories: `adapters/visual-regression-web/src/test/resources/valid/`, `adapters/visual-regression-web/src/test/resources/diff/`, `adapters/visual-regression-web/src/test/resources/missing-baseline/`, `adapters/visual-regression-web/src/test/resources/wrong-baseline-digest/`, `adapters/visual-regression-web/src/test/resources/missing-provider/`, `adapters/visual-regression-web/src/test/resources/malformed/`
|
||||
- Modify: `settings.gradle.kts`
|
||||
- Modify: `build.gradle.kts`
|
||||
- Modify: `modules/testctl-cli/build.gradle.kts`
|
||||
- Modify: `modules/testctl-cli/src/main/java/io/testassurance/cli/Commands.java`
|
||||
|
||||
**Module dependencies:** Both modules are added to root `javaModules` and depend on model, normalizer, schema, and Playwright evidence types.
|
||||
|
||||
**Rules:** Accessibility evidence carries scanner/provider identity, rule-set digest, findings, and required manual-review records. Manual records require `reviewedAt`, `expiresAt`, `sourceRevision`, `candidateSha256`, `reviewerId`, and `rulesetDigest`; expiry is evaluated against the raw result's `finishedAt`, never wall-clock time. Visual evidence carries baseline ID/digest, provider identity, current image digest, diff digest, and threshold outcome. Absent identities can never PASS.
|
||||
|
||||
- [ ] Add RED tests `rejectsAccessibilityWithoutProvider`, `rejectsExpiredManualReviewAtFinishedAt`, `reportsUnwaivedAccessibilityViolation`, `rejectsVisualWithoutBaseline`, `rejectsWrongVisualProvider`, and `reportsVisualDiffOverThreshold`, each backed by the named fixture directory above.
|
||||
- [ ] Implement and register both adapters.
|
||||
- [ ] Run adapter and CLI tests; expected PASS.
|
||||
- [ ] Commit:
|
||||
|
||||
```bash
|
||||
git add adapters/accessibility-web adapters/visual-regression-web settings.gradle.kts build.gradle.kts modules/testctl-cli
|
||||
git commit -m "feat(normalizers): add accessibility and visual evidence"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 9: Enforce artifact materialization in bounded execution
|
||||
|
||||
**Files:**
|
||||
|
||||
- Modify: `modules/assurance-executor/src/main/java/io/testassurance/executor/WorkItemExecutor.java`
|
||||
- Modify: `modules/assurance-executor/src/main/java/io/testassurance/executor/LocalProcessExecutor.java`
|
||||
- Create: `modules/assurance-executor/src/main/java/io/testassurance/executor/InputArtifactVerifier.java`
|
||||
- Modify: `modules/assurance-executor/src/test/java/io/testassurance/executor/LocalProcessExecutorTest.java`
|
||||
- Modify: `modules/testctl-cli/src/main/java/io/testassurance/cli/Commands.java`
|
||||
- Modify: `modules/testctl-cli/src/test/java/io/testassurance/cli/TestctlMainTest.java`
|
||||
- Modify: `docs/06-testctl-external-executor-contract.md`
|
||||
|
||||
**External contract:** `testctl execute-one` retains `--plan`, `--work-item-id`, and `--output`, and v3 adds `--artifact-map`. The artifact map resolves artifact IDs to local regular archive files, while expected media type and SHA-256 come only from the signed v3 work item. Verification occurs before the product command starts.
|
||||
|
||||
The compatible public syntax is:
|
||||
|
||||
```text
|
||||
testctl execute-one --plan <plan.json> --work-item-id <sha256> --artifact-map <input-artifact-map.json> --output <work-dir>
|
||||
testctl execute-plan --plan <plan.json> --artifact-map <input-artifact-map.json> --output <run-dir>
|
||||
```
|
||||
|
||||
SOURCE plans reject `--artifact-map`; ARTIFACT plans require it. V3 maps only regular archive files—directories must already be represented by a canonical archive subject. `InputArtifactVerifier` copies each file into a private staging directory while hashing it, then exports only the verified copy path to the child.
|
||||
|
||||
- [ ] Add RED tests for missing artifact, wrong digest, wrong media type, symlink, path escape, directory input, mutation between verification and use, SOURCE receiving an artifact map, ARTIFACT missing a map, unknown work-item ID, and work item not belonging to the plan digest.
|
||||
- [ ] Run executor and CLI tests; expected RED.
|
||||
- [ ] Implement verification with opened handles or verified private copies, bounded hashing, and exact environment projection; never trust a caller-supplied digest.
|
||||
- [ ] Re-run tests; expected PASS.
|
||||
- [ ] Commit:
|
||||
|
||||
```bash
|
||||
git add modules/assurance-executor modules/testctl-cli docs/06-testctl-external-executor-contract.md
|
||||
git commit -m "feat(executor): verify artifact-bound work inputs"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 10: Prove the v3 chain, frontend fixture, and independent R1 readiness
|
||||
|
||||
**Files:**
|
||||
|
||||
- Modify: `modules/assurance-normalizer/src/main/java/io/testassurance/normalizer/FalseGreenRules.java`
|
||||
- Modify: `modules/assurance-evidence/src/main/java/io/testassurance/evidence/EvidenceBundler.java`
|
||||
- Modify: `modules/assurance-evidence/src/test/java/io/testassurance/evidence/EvidenceBundlerTest.java`
|
||||
- Modify: `modules/assurance-assessor/src/main/java/io/testassurance/assessor/Assessor.java`
|
||||
- Modify: `modules/assurance-assessor/src/test/java/io/testassurance/assessor/AssessorTest.java`
|
||||
- Create: `modules/assurance-catalog/src/main/java/io/testassurance/catalog/ReadinessCatalog.java`
|
||||
- Create: `modules/assurance-catalog/src/test/java/io/testassurance/catalog/ReadinessCatalogTest.java`
|
||||
- Modify: `modules/assurance-catalog/build.gradle.kts`
|
||||
- Modify: `modules/testctl-cli/src/main/java/io/testassurance/cli/ConformanceRunner.java`
|
||||
- Modify: `modules/testctl-cli/src/main/java/io/testassurance/cli/Commands.java`
|
||||
- Modify: `modules/testctl-cli/src/test/java/io/testassurance/cli/TestctlMainTest.java`
|
||||
- Create: `conformance/golden-v3/source/case.json`
|
||||
- Create: `conformance/golden-v3/source/01-execution-request.json`
|
||||
- Create: `conformance/golden-v3/source/02-execution-plan.json`
|
||||
- Create: `conformance/golden-v3/source/03-raw-result-set.json`
|
||||
- Create: `conformance/golden-v3/source/normalized.json`
|
||||
- Create: `conformance/golden-v3/source/04-evidence-bundle.json`
|
||||
- Create: `conformance/golden-v3/source/05-assessment.json`
|
||||
- Create: `conformance/golden-v3/artifact/case.json`
|
||||
- Create: `conformance/golden-v3/artifact/01-execution-request.json`
|
||||
- Create: `conformance/golden-v3/artifact/02-execution-plan.json`
|
||||
- Create: `conformance/golden-v3/artifact/03-raw-result-set.json`
|
||||
- Create: `conformance/golden-v3/artifact/normalized.json`
|
||||
- Create: `conformance/golden-v3/artifact/04-evidence-bundle.json`
|
||||
- Create: `conformance/golden-v3/artifact/05-assessment.json`
|
||||
- Create: `conformance/adversarial/v3-wrong-candidate-normalized-result.json`
|
||||
- Create: `conformance/adversarial/v3-missing-browser-result.json`
|
||||
- Create: `fixtures/frontend-reference/test-assurance.yaml`
|
||||
- Create: `fixtures/frontend-reference/test-assurance.lock.json`
|
||||
- Create: `fixtures/frontend-reference/.nvmrc`
|
||||
- Create: `fixtures/frontend-reference/package.json`
|
||||
- Create: `fixtures/frontend-reference/pnpm-lock.yaml`
|
||||
- Create: `fixtures/frontend-reference/config/test-assurance/risks/frontend.json`
|
||||
- Create: `fixtures/frontend-reference/config/test-assurance/obligations/frontend.json`
|
||||
- Create: `fixtures/frontend-reference/config/test-assurance/suites/source.json`
|
||||
- Create: `fixtures/frontend-reference/config/test-assurance/suites/artifact-templates.json`
|
||||
- Create: `fixtures/frontend-reference/config/test-assurance/change-surfaces.json`
|
||||
- Create: `fixtures/frontend-reference/artifacts/frontend-static.tar`
|
||||
- Create: `fixtures/frontend-reference/results/source-valid/raw-result-set.json`
|
||||
- Create: `fixtures/frontend-reference/results/source-valid/normalized.json`
|
||||
- Create: `fixtures/frontend-reference/results/artifact-valid/raw-result-set.json`
|
||||
- Create: `fixtures/frontend-reference/results/artifact-valid/normalized.json`
|
||||
- Create: `fixtures/frontend-reference/results/adversarial-zero-discovery/raw-result-set.json`
|
||||
- Create: `fixtures/frontend-reference/results/adversarial-missing-browser/raw-result-set.json`
|
||||
- Create: `fixtures/frontend-reference/results/adversarial-wrong-candidate/raw-result-set.json`
|
||||
- Modify: all ten frontend files under `readiness/`
|
||||
- Modify: `docs/03-manifest-schema-contracts.md`
|
||||
- Modify: `docs/04-test-type-contracts.md`
|
||||
- Modify: `docs/06-testctl-external-executor-contract.md`
|
||||
- Modify: `docs/07-evidence-flaky-contract.md`
|
||||
- Modify: `docs/09-implementation-handoff-contract.md`
|
||||
- Modify: `README.md`
|
||||
- Modify: `validation.json`
|
||||
|
||||
**Identity rule:** Bundling requires exact equality of API major, request digest, plan digest, execution phase, ordered input artifacts, work-item IDs, attempt identities, and raw artifact digests. Assessment emits the same tuple plus `readinessDigest` and never converts an integrity error into an unsatisfied product obligation. `ReadinessCatalog` packages the independent cards into the distribution; `Commands.assess()` supplies it to `AssessmentInputs`, and the assessor applies the higher of capability-contract minimum and obligation minimum readiness.
|
||||
|
||||
- [ ] Add RED chain tests for v2/v3 mixing, source/artifact mixing, wrong candidate, missing work item, duplicate attempt, retry-only green, absent terminal result, empty readiness map, readiness digest drift, and an aggregate frontend readiness card.
|
||||
- [ ] Run:
|
||||
|
||||
```bash
|
||||
./gradlew :modules:assurance-evidence:test :modules:assurance-assessor:test :modules:assurance-catalog:test :modules:testctl-cli:test
|
||||
```
|
||||
|
||||
Expected: RED until the entire tuple is checked.
|
||||
|
||||
- [ ] Implement fail-closed chain verification, readiness binding, and a `ConformanceRunner` case format that validates the five canonical stages plus listed `normalizedFiles`.
|
||||
- [ ] Build the frontend fixture with all ten capabilities. Compute the artifact SHA from the committed `frontend-static.tar` bytes and use it consistently in artifact request, materialized suites, plan, raw results, normalized results, bundle, and assessment; no repeated-character or hand-entered digest is allowed.
|
||||
- [ ] Run focused tests; expected PASS.
|
||||
- [ ] Install the CLI and run both golden cases:
|
||||
|
||||
```bash
|
||||
./gradlew :modules:testctl-cli:installDist
|
||||
modules/testctl-cli/build/install/testctl-cli/bin/testctl conformance --case conformance/golden-v3/source/case.json
|
||||
modules/testctl-cli/build/install/testctl-cli/bin/testctl conformance --case conformance/golden-v3/artifact/case.json
|
||||
```
|
||||
|
||||
- [ ] Run the fixture twice, once for SOURCE and once for ARTIFACT, through `validate → lock → compile → select → plan → normalize → bundle → assess`; assert five selected source capability IDs and five selected artifact capability IDs with separate plan/evidence/assessment digests.
|
||||
- [ ] Update each frontend card from R0 to R1 only when its own adapter fixture and chain evidence digest exists. Keep missing evidence at R0 with explicit `nonGuarantees`.
|
||||
- [ ] Add documentation checks naming every capability, exact toolchain pin, v3 CLI argument, phase/template/materialization rule, false-green rule, readiness digest, and v2 compatibility limit.
|
||||
- [ ] Run full verification:
|
||||
|
||||
```bash
|
||||
./gradlew clean build
|
||||
python3 tools/validate_package.py
|
||||
git diff --check
|
||||
```
|
||||
|
||||
Expected: PASS. If a toolchain artifact cannot be observed locally, keep affected cards at R0; do not fabricate a digest.
|
||||
|
||||
- [ ] Commit:
|
||||
|
||||
```bash
|
||||
git add modules/assurance-normalizer modules/assurance-evidence modules/assurance-assessor modules/assurance-catalog modules/testctl-cli conformance fixtures/frontend-reference readiness docs README.md validation.json
|
||||
git commit -m "test(conformance): publish frontend v3 readiness"
|
||||
```
|
||||
|
||||
## Handoff to CI/CD
|
||||
|
||||
The consuming CI/CD plan may begin only from the immutable commit produced by Task 11. It imports the v3 schemas and CLI distribution by digest, invokes only documented argument-array commands, treats normalized/evidence/assessment documents as opaque test semantics, and carries both source and artifact plan/evidence/assessment digests into release identity.
|
||||
+41
-1
@@ -176,6 +176,27 @@ 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
|
||||
@@ -236,7 +257,17 @@ both must equal the approved subject before promotion succeeds.
|
||||
|
||||
### Release identity
|
||||
|
||||
The release manifest binds:
|
||||
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
|
||||
@@ -250,6 +281,9 @@ 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.
|
||||
|
||||
@@ -351,6 +385,12 @@ 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
|
||||
|
||||
Reference in New Issue
Block a user