615 lines
37 KiB
Markdown
615 lines
37 KiB
Markdown
# 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.
|