37 KiB
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.yamlinto.platform/toolchains/go-1.26.5/bin/go, verify the distribution SHA-256 before extraction, and setCICD_GO_BINto that absolute path. The host currently has nogo; 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, andci-static-site-publish. - Existing
ci-sbomandci-provenancegain 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, andMakefilecapability 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):
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.jsonandcontracts/schemas/release-manifest.schema.jsonv1 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 rewritecontracts/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:
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:
"$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.Payloadis a discriminated typed payload validated by(AdapterID, Operation); the adapter passes its internal argv directly throughexec.CommandContextand never invokes a shell. - Run:
make GO="$CICD_GO_BIN" contracts-sync
make GO="$CICD_GO_BIN" contracts
"$CICD_GO_BIN" test ./packages/contracts ./packages/canonicalid
Expected: PASS.
- Commit:
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:
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
Putfails, missing adapters are platform defects, failed dependencies block descendants, and a missing terminal result cannot pass. - Run:
"$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-rootwith atomic result writes. - Re-run
"$CICD_GO_BIN" test ./apps/cicdctl/internal/execution ./apps/cicdctl/internal/app -count=1; expected PASS. - Commit:
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:
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:
"$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-assuranceto 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=1andmake GO="$CICD_GO_BIN" registry; expected PASS. - Commit:
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:
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=1andmake GO="$CICD_GO_BIN" registry; expected PASS. - Commit:
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=1andmake GO="$CICD_GO_BIN" registry; expected PASS. - Commit:
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, andmake GO="$CICD_GO_BIN" registry; expected PASS. - Commit:
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/anddocs/decisions/readiness/evidence/ci-static-artifact-supply-chain/ - Modify:
contracts/readiness-cards.yaml - Modify:
Makefile
Interfaces:
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-signertrust 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=1andmake GO="$CICD_GO_BIN" registry; expected PASS. - Commit:
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:
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=1andmake GO="$CICD_GO_BIN" registry; expected PASS. - Raise to P1/shadow; keep real provider P2 blocked.
- Commit:
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.
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-controlonly 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:
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:
"$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:
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:
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:
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:
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.