# Shared pipeline implementation guide One shared workflow, `.gitea/scoped_workflows/pipeline.yaml`, reads `.ci.yaml` from the exact source revision and runs the same lifecycle for every repository. Shared execution contains no repository-specific routing; repository-local configuration and event facts select work. Adding a repository that uses supported fields needs no change here. ## Current scope - Live-qualified: Maven with Java 21 and npm with Node 22.20 on `linux-amd64`. Their images in `runtime/catalog.yaml` are published and qualified (zero HIGH/CRITICAL findings, no secrets). - Implemented and unit-tested, but blocked at `prepare` until the operator qualifies the runtime (see "Native capabilities and runtime qualification"): Go, Cargo, Python (all versions), the `script` tool (control image unrecorded), .NET, Swift, Xcode and `make` builds; Windows and macOS host targets; browser and docker runtime profiles. Java 17, Android and the BuildKit tools have published, scanned images recorded in the catalog, but publication is not qualification: none has run live. - This is **not** fleet-ready. Nothing in the native capability section has run live (see "Remaining work"). ## Live evidence - **Positive run 4244**: workflow `dede29111176238789d07715f0b67aa726fc55a1`, helper `0353c05187f6313f2186cc741a3cb5c70ec1da16`, 62 seconds. Components ran in parallel. Each produced independent manifest and file-hash artifacts (3254 and 3255), and ran exactly one native verify. - **Negative run 4252**: blocked on a real Maven test failure and a missing Node artifact. The reports were retained. - Per-command Git trust for the exact workspace is fixed (`git -c safe.directory=`), not a global setting. - **Dependency cache**: new code passes 98 tests (97 pass, 1 Windows/POSIX skip). Lint and type checks were clean before the last two narrow fixes; the final checks are being run by root. The cache is **not live-qualified**. Do not claim cache hits. ## Responsibilities | Job | Does | | --- | --- | | `prepare` | Validates `.ci.yaml`, resolves catalog images, decides whether verification is required, emits the plan; its `reuse` step then decides, before any build, whether proven tested bytes replace verification and emits the final matrices and the evidence | | `verify` (matrix, one per container target) | Optional cache restore, one native build and test, packaging, artifact upload, cache save | | `verify-native` (matrix, one per host target) | The same build, packaging and upload directly on the runner the catalog names; no container, no cache | | `secrets` | One history scan per run | | `report` | Compares expected with actual results and fails on any mismatch; each verification job must have succeeded exactly when the plan has targets of its kind and nothing was reused, and be skipped otherwise. When tested bytes were reused it requires both verification jobs skipped AND valid reuse evidence for exactly this plan; the secret scan and preparation must still succeed | Packaging only collects declared reports and artifacts and writes the manifest. It cannot start a process, so it runs no compiler. ## Module map | Path | Purpose | | --- | --- | | `platform_ci/build_tools.py` | Data only: one row per native tool (executable, toolchain, platforms, profiles, fixed variables) and per platform | | `platform_ci/configuration.py` | Strict `.ci.yaml` validation; duplicate keys and unknown fields rejected; branch globs | | `platform_ci/plan.py` | Deterministic plan, verification reason, container and host matrices, native commands | | `platform_ci/runtimes.py` | Catalog loading; resolves toolchain, version, platform and profiles to a runner and image; requires an exact digest, fails if unsupported or unqualified | | `platform_ci/execution.py` | Runs tokenized commands without a shell, with a scrubbed environment plus the target's fixed tool variables | | `platform_ci/maven_settings.py` | Credential-free public Maven Central settings, written to `~/.m2/settings.xml` before a Maven target runs | | `platform_ci/artifact_service.py` | One portable check that artifact endpoints are the private forge (host jobs cannot rely on a POSIX shell) | | `platform_ci/packaging.py` | Collects outputs, writes `manifest.json` with SHA-256 for each file | | `platform_ci/caching.py` | Dependency-cache key, directory and save eligibility | | `platform_ci/reporting.py` | Expected-versus-actual report | | `platform_ci/workspace.py` | Commit-hash check, containment, excluded names | | `platform_ci/__main__.py` | Commands `prepare`, `verify`, `cache-metadata`, `package`, `scan`, `report`, `require-artifact-service` | | `platform_ci/build_inputs.py`, `platform_ci/versions.py` | The build-input record stored in the manifest and the declared-version reader, shared by the build and the publisher | | `platform_ci/safe_xml.py`, `platform_ci/pipeline_contract.py`, `platform_ci/reuse_evidence.py` | The shared declaration-refusing XML parse (JUnit and pom versions), the job and artifact names the workflow and publisher agree on, and the reuse-evidence check the final gate applies | | `platform_ci/delivery/` | The trusted publisher and the reuse decision: `promotion` (the workflow's reuse step), `selection` (shared proof code), `enrollment`, `forge`, `publisher`, `live`, `reuse_forge`, `__main__` (the command), `manifest`, `reuse`, `provenance`, `authorization`, `adapters` (Maven, npm, container), `gitops` (digest pins), `legacy` (the only importer of `publisher_frozen/`) | | `publisher_frozen/` | Existing publication primitives, vendored unchanged; excluded from lint and type checks | | `runtime/` | `catalog.yaml`, `control.Dockerfile`, `toolchain.Dockerfile`, `TOOL-SECURITY.md` | | `examples/`, `tests/` | Example configs (loaded by tests) and behavior tests | ## Configuration `schema: 1` plus `components.` with `directory`, `platforms`, `toolchains.`, `requires` (optional runtime profiles `browser`, `docker`), `build.tool`, `build.arguments` or `build.commands`, `reports`, and `artifacts..paths`. `release.channels..trigger.push_branches` is eligibility data only, and `verification.push.branches|tags` selects which ordinary pushes verify. Branch entries are exact names or globs (`*` inside a path segment, `**` across segments, as in `release/**`). Anything else is rejected with its field path. There is no option to skip tests or disable pull request verification. | `build.tool` | Starts | Toolchain key | Platforms | | --- | --- | --- | --- | | `maven` | `mvn` | `java` | linux-amd64 | | `node` | `npm ci`, then `npm` | `node` | linux-amd64 | | `gradle` | `./gradlew` (the project's wrapper) | `java` | linux-amd64 | | `android` | `./gradlew` | `android` | linux-amd64 | | `go` | `go` | `go` | linux-amd64, windows-amd64, macos-arm64 | | `cargo` | `cargo` | `rust` | linux-amd64, windows-amd64, macos-arm64 | | `python` | `python3` | `python` | linux-amd64 | | `dotnet` | `dotnet` | `dotnet` | windows-amd64, linux-amd64 | | `swift`, `xcodebuild` | `swift`, `xcodebuild` | `xcode` | macos-arm64 | | `make` | `make` | `xcode` | macos-arm64 | | `nix` | `nix` (flake check and build; single-user) | `nix` | linux-amd64 | | `container` | `buildctl` | `container` | linux-amd64 | | `script` | the checked-in program named by each command (`./scripts/check.sh`, inside the component directory, no shell) | `control` | linux-amd64 | | `source` | `node` running the platform checkout's unchanged `source_checks/check-source.mjs`; a closed vocabulary, one subcommand `tracked-files` | `node` | linux-amd64 | The `source` tool lints the repository root's tracked files (JSON and JSONC parse, `bash -n`, Python compiled in memory and never run, conflict markers) with the reviewed `check-source.mjs` and `jsonc.mjs`, vendored byte for byte in `source_checks/` (`receipt.json` records their SHA-256 and git blob ids; they equal the files captured at the reviewed revisions and root's offline runtime proof). Each declared command is exactly `["tracked-files"]`; unknown subcommands, extra arguments and paths are rejected at prepare, and the directory must be `.`. The adapter adds no install step, no download and no `CHECK_SOURCE_PYTHON`; before it starts the program, execution requires both helpers beside it to match the receipt. It runs on the already qualified Node 22.20.0 image on the standard pool. The helper runs `git ls-files`, and the builtin checkout's owner can differ from the job's user (Git then reports `dubious ownership`), so execution gives that one process command-scope Git configuration (`GIT_CONFIG_COUNT`, `safe.directory` for the exact resolved checkout path) and nothing else: no wildcard, no global or system file, only for this invocation, and every inherited `GIT_*` variable is dropped by the environment allow-list. Other tools get none of it (`tests/test_git_trust.py`). | `source-tools` | `bash` running the receipted helpers of `source_tools/scripts/`; a closed vocabulary of subcommands (`actionlint`, `shellcheck-orb`, `make-bats`, `kustomize-render`, `blueprint-lint`, `policy`), relative paths only where a subcommand takes them | `source-tools` | linux-amd64 | The `source-tools` tool runs the unchanged source-check helpers vendored byte for byte in `source_tools/` (`receipt.json` records their hashes and the published image `platform-ci-source-tools@sha256:f50fdd17…2b63`, which equals the digest in root's `runtime-qualification/expanded/source-tools-publication.json`); execution verifies the receipt before starting a helper. The directory must be `.`, and a check cannot be declared twice. `source_tools/` is excluded from Ruff so the receipted bytes never change. The lightweight `source` path is unchanged. Git trust applies through the same generic capability flag, set per subcommand: the captured helpers never call `git` themselves (inspected: only `find` and text mentions), so only `make-bats`, which runs the repository's own `make` and `bats` programs that may call `git` in the checkout, gets the command-scope `safe.directory` for the exact resolved checkout; the other subcommands get none. `policy` subcommand (repository policy validation): `["policy"]`, no arguments, directory `.`. The captured `scripts/policy-validate.py` of the policy-verify action at `c95ea023eae447f482ae6ce4d61efbcb9c79a017` is vendored byte for byte (SHA-256 `ad9e230b…b83e`, recorded under `files`, `git_blobs` and `additional_captures` in the receipt; it is non-executable in the capture and is only read by `python3`). Inspected: it imports `json`, `os`, `pathlib`, `subprocess`, `xml.etree` and `yaml`; it runs `git ls-files -z` and `bash -n`/`shellcheck` only when `QQQ_POLICY_SHELL=true`; and it has no command-line options. `policy-verify.sh` is a platform-authored launcher (listed in `platform_authored`) that does what the legacy action did: `PYTHONPATH=$PYYAML_DIR/lib python3 -P -s -B policy-validate.py` in the repository root, using the PyYAML already in the qualified image (`PYYAML_DIR=/opt/blueprint-tools`, the one `blueprint-lint` uses). It unsets `QQQ_POLICY_SHELL`, as the legacy action never set it, so the runner environment cannot switch the shell checks on, and no shell-check subcommand exists because no captured caller used one. Because the validator calls `git ls-files`, `policy` carries `inspects_checkout_with_git` and gets the same exact-checkout, command-scope `safe.directory` as `make-bats`. No new job, runtime, profile or catalog row. Config: `examples/source-policy.ci.yaml`. Rendered manifest retention (the fleet worker's `rendered-manifest-retention` gap): no new adapter is needed. `manifest-verify.sh` writes the rendered manifests to `ci-manifests/` in the repository root, and a component's `artifacts` already take generic globs, so the converter declares `artifacts: {rendered-manifests: {paths: ["ci-manifests/**/*"]}}` on the component that runs `kustomize-render` (`examples/source-manifests.ci.yaml`; `tests/test_source_tools.py` packages a nested render through the real collector). Converter rules: put the render in its own component, because after a successful build every declared artifact must match a non-empty file; the files are retained in that target's `verified-` bundle (14 days, per-file size and SHA-256) and not as a separately named `rendered-manifests` upload; and a failed render keeps no artifact (the legacy job uploaded whatever existed, with a warning), which is deliberate so a failed build never yields a releasable bundle. Image packaging in the one verified bundle (integrated from the image-packaging patch; its anchored edits were applied to the current candidate with `integrate.py`, so the source-tools, `policy`, Docker environment and Git trust changes are untouched). A component's `images:` run in the existing pool job after the one native verification: `package-images` (`platform_ci/image_packaging.py`, no code path that starts a native build tool) runs the receipted `image_checks/image-boundary.sh`, hashes the tested artifact, refuses a Dockerfile that recompiles, builds with the pool's rootless BuildKit, scans, and writes `ci-image-output//`; the ordinary `package` step then retains the OCI archive (a `container` artifact `image-`) and its evidence (report `image--evidence`) in the same `verified-` bundle, and `verify_image_binding` proves the bundle lists exactly the tested bytes and the scanned archive. There is no second upload, artifact name or protocol; reuse and the publisher read the same manifest. Image capability composition (changed from the patch, which made `image` exclusive): `images` adds the `image` profile and keeps every `requires` entry, so `requires: [docker]` plus `images` plans as `docker+image`. The job and mount separation is unchanged and is decided only by the existing rule (`docker` in `requires` selects the docker job with the test-daemon mount, otherwise the browser/standard pool job). The image comes from the catalog per combination: `image` and `docker+image` use `image_image` (the published container image has Maven, BuildKit and Trivy; docker adds only the job's mount and variables); `browser+image` and `browser+docker+image` use `image_browser_image`, which is explicitly `null` for Java 21 and Node 22.20.0 because no published image holds a browser, BuildKit and Trivy, so those plans fail by name (`image+browser runtime is unqualified`) and nothing is dropped or substituted. The runner labels are recorded the same way (`docker+image: container-build`, derived from the two authorized labels and as unproven live as they are; the browser combinations null). Image packaging does not receive the docker contract variables: `__main__` removes `DOCKER_PROFILE_ENVIRONMENT` from the environment it passes to `package-images`, because the captured boundary check refuses a Docker variable and BuildKit is not the test daemon; the native verification still gets them. `image` cannot be declared without `images`. Scan and SBOM: one `trivy image --list-all-pkgs --severity HIGH,CRITICAL --exit-code 1 --format json` scan per image (the patch scanned twice), then `check-image-evidence.mjs` on that report, then `trivy convert --format cyclonedx --output sbom.cdx.json `; the HIGH/CRITICAL failure, image binding and CycloneDX check are unchanged, and `tests/test_image_packaging.py` asserts the command identity and count (one `--input`, one `convert` of the scan's own report, no `--skip-db-update`). `--list-all-pkgs` is explicit; the captured Trivy 0.76.0 scan in `voyage-ci.sh` already passes it, and root verified `trivy convert` locally; neither was run here. The converted SBOM can include the HIGH/CRITICAL vulnerabilities of the filtered report, which a separate scan did not. Scanner database cache: for a target with images, `cache-metadata` also publishes `trivy_key` (exact: UTC day plus the runtime image digest, no fallback), `trivy_path` (`/.cache/trivy`, proven outside the checkout like the dependency directory) and `trivy_save` (the dependency cache's default-branch push rule). Both pool jobs restore it with the same native cache action and the scan is told `--cache-dir` for the same directory. Freshness is not weakened: the scan never skips its database update, so a restored database that is stale is replaced, and the daily key bounds how old a restored one can be. Saving is the last step of the job, only after the whole job succeeded on a push to the default branch, so pull requests never write. No new service or credential. **UNTESTED CHANGES (this section and the two below):** made while test execution was suspended; nothing was run, formatted or type-checked after them, so the earlier 456 + 63 results do not cover them. Script toolchain choice (integrated from `migration/native-graph-staging/script-toolchain-choice.patch`, applied by hand to the current candidate): the generic `script` tool declares exactly one of `control` (the default runtime), `java` or `node` under `toolchains`, via `BuildTool.toolchain_choices`; `Component.toolchain` and `Target["toolchain"]` carry the choice, the plan resolves the runtime, display name and image from it, and `build_inputs` records `{: version}` so the fingerprint and `config_digest` change with the choice. A `java` script gets the same credential-free Maven settings as a Maven component. `script` also accepts the browser, docker and (implied) image profiles, composing through the existing catalog rules, so the image composition, Docker environment, scanner cache and the Node image pins are untouched. Dependency caching follows the declared toolchain (`caching.cache_family`): a `java` script uses the Maven repository cache and descriptors, a `node` script the npm cache, a `control` script none, and the key carries `script-`; the script is never treated as Maven. Plans and manifests from before this change lack the `toolchain` field, so their `config_digest` differs and they are not reused once (a one-time rebuild). Scripts and configuration belong to the native graph worker. Tests: `tests/test_script_toolchain.py` (not yet run). Trusted publisher release lifecycle (what is wired, using the frozen `release_version` primitive through `legacy.py`): (1) the version is the one the source declares before compilation and the build records in `build_inputs`; the publisher never computes, bumps or writes one, and a changed version is a changed input, so old bytes are never relabelled. (2) Tags are decided by the repository's own configuration, not by branch names: a release channel may declare `tag_policy` in the captured `release_tag_policy` vocabulary (`stable`: a final `X.Y.Z` Maven or npm package needs the existing `v` tag at the release commit; `v-prefix`: any non-snapshot package does; `none` or unset: nothing). `publisher._check_release_tag` reads the tag through `forge.tag_commit` in inspection and again before any write; the earlier branch-based `main`/`hotfix/*` rule was removed. Unset means no requirement, so a migrated repository whose legacy policy was `stable` keeps its requirement only if its converted `.ci.yaml` carries `tag_policy` (the converter does not emit it yet). (3) npm dist-tags follow the native contract: `latest` only for a final version on the stable channel (a prerelease there is refused), `rc` and `snapshot` for those kinds elsewhere, otherwise the channel name as before. (4) Signature callback: provenance retention and subject coverage are enforced as before; cryptographic verification is the injected `signature_verifier`, and no frozen primitive verifies attestations, so none is supplied: a binding with `require_signature_verification` fails closed, by design, until an operator-reviewed verifier exists. No release creation, signing, GitOps tagging or credential was added; credential file names and identities are unchanged. [SUPERSEDED by the "Trusted tag phase" paragraph below: the signed-tag primitive exists in `references/release-lifecycle/b06800a6…/.gitea/scripts/qbit_rc.py`; my earlier capture search was incomplete.] Release lifecycle completion gaps (earlier text): (a) Tag creation: no captured nonsecret primitive creates or signs a tag. Every captured path only reads one or requires it to exist: `promote_package._check_tag` (frozen), `qrun-source-version.mjs` (`publishMavenRelease`: "never create a tag to make a release succeed"; it also accepts `version-`, which this publisher does not), `qrun-maven-prepare.mjs` (release is triggered BY an existing tag run) and `calculate_version.sh` ("Please create a release tag before deploying from main"). The only captured tag-adjacent write is the Gitea release record (`promote_package._ensure_release`, `POST /releases` with `tag_name`), which the frozen code calls only after the tag exists. The step that created tags in the old platform is therefore not in the captured sources (it was the CircleCI orb's release step, per `release_version.py`'s header), and no mounted credential has tag or release write scope: `writer-token`/`reader-token` are package and registry scoped, `gitops-token` is confined to one file on one branch, `api-token` is read-only. Missing existing primitive: a tag-creating release step and its approved credential binding. Nothing was staged to create or sign a tag, and no credential or signer scope was assumed. (b) Release record: `_ensure_release` is not wired, because it needs the same missing release-write scope. (c) Signature callback: no approved attestation verifier exists in the captured sources or `publisher_frozen`; the only approved verification is the forge's `verification.verified` flag on commits (`legacy.require_verified_commit`, used for GitOps). `signature_verifier` stays unsupplied, so `require_signature_verification` fails closed. No crypto was written. (d) Converter: it does not yet emit `tag_policy`, and its tag-driven release kinds (tag runs) remain a reported cutover gap because channels trigger on branches only. (e) The tag endpoint shape (`commit.sha`) is confirmed by root's read-only receipt `tag-endpoint-shape.json` for one existing tag; annotated versus lightweight coverage and any publication are not proven. All of this is untested. Trusted tag phase (UNTESTED; code staged only, nothing signed, tagged, released or read from a credential). Source of the behavior: `qbit_rc.py` at platform-ci `b06800a6` (receipted in `references/release-lifecycle-receipts.json`) and the old protected callee's boundary: an isolated job with the job's ephemeral `contents: write` token and the existing repository secret `QRUN_TAG_SIGNING_KEY`, pinned platform code only, the application never checked out. Nothing is relocated to the publisher and no new grant is implied. - Configuration owns it: `release.channels..tag_policy` (`stable`, `v-prefix`, `none`) says which tag a package needs, and the new `tag_create: true` opts that channel in to the platform creating it (default false: the owner tags and the publisher only requires it). `tag_create` needs a policy. Authorization (which repositories have the secret) stays operator-owned. - One canonical-workflow job, `release-tag` (`needs: [prepare, report]`, only when prepare's new `release_tag` output is true on a push and `report` succeeded): it alone has `contents: write` and references the secret; `tests/test_delivery_contracts.py` was edited to assert exactly that. Its one command, `python3 -P -m platform_ci release-tag` (`delivery/release_tag.py`), requires a push to a branch on the trusted forge, the forge's proof that this run is a canonical-workflow run (`current_workflow_trust`), the protected branch head still equal to the verified release commit, reads `.ci.yaml` and the declared version at that commit through the forge (never a checkout), and then `signed_tag.ensure_signed_tag`: create the SSH-signed annotated tag `v` once, refuse an existing tag at another commit or a lightweight one, read it back, verify the raw object, and delete only the exact object it created if verification fails. It never invents a version and never creates an unsigned tag. `delivery/signed_tag.py` is now a thin typed seam: all signing and verification is the unchanged `publisher_frozen/signed_tag_primitives.py` (eight exact nodes of the receipted `qbit_rc.py`, bound by `signed_tag_primitives.receipt.json`), reached only through `legacy.new_tag_signer` / `legacy.verify_signed_tag`; the module keeps only the forge reads, the bare-Git transport that holds the token, and the order of operations (not yet executed; the receipt's `execution_tested` is false). The publisher's `_check_release_tag` uses the same `required_tag` rule, so what is created is exactly what is later required. - Signature formats, from the captured contracts (three distinct things, none standing in for another): (1) Gitea's server-side `verification.verified` on COMMITS (GitOps pins and the RC bump commit) via `legacy.require_verified_commit`; (2) an SSH signature (namespace `git`) on a raw TAG OBJECT, checked against the fingerprint derived from the signing key (`verify_raw_tag`); (3) DSSE/in-toto attestations retained in a bundle, whose subject coverage `provenance.py` checks and whose cryptographic verification is the injected `signature_verifier`. Item 2 does not satisfy item 3. No captured or enrolled contract needs item 3's verification: no enrollment in the captured sources sets `require_signature_verification`, so it stays unsupported and fails closed only for a binding that sets it; it blocks no repository. - Not integrated (named, not claimed): the RC cut (branch, pending-lock, merge, bump commit), `finalize`/`recover`, the final-release record (`create_release`) and the Maven Central completion evidence in `qbit_rc.py` are product-specific orchestration with different identities; wiring them would be a second orchestrator. The control image's `git` and `ssh-keygen` availability, that a scoped workflow can read the repository secret, and that the job token's `contents: write` can push a tag are unproven live. The publisher does not itself verify the tag's signature (that needs an operator-owned fingerprint allow-list, `tag_fingerprints` in the reference, which does not exist here); it requires the tag's existence and position. - Converter gap for the native graph worker: emit `tag_policy` (and `tag_create` only where the legacy flow created the tag) in channels, and use the new component event selection below instead of dropping the static and coverage gates. Component event selection (UNTESTED, minimal, for the native graph's static and coverage gates): a component may declare `only_on_channels` / `not_on_channels`, validated against `release.channels`; it is planned only when the event's branch (the push branch, or a pull request's head branch, now passed as `CI_HEAD_REF`) does / does not match those channels' `push_branches`. Only `prepare_plan` filters; `plan_targets` (the publisher's equivalence proof) still lists every component. A component excluded from a channel it publishes on will have no bundle for that event, and the publisher fails closed. Not applied: fixed event-fact variables for `script` targets (`CI_EVENT_NAME` etc.), since adding them would change every script target's fingerprint. Fleet-gap contracts (ALL UNTESTED; static edits only; nothing here is shown runnable). Parity with the old behavior is NOT claimed complete: see the open items at the end. 1. Event selection (G1). `platform_ci/event_selection.py` is the one rule. A release channel matches an event when its `trigger.push_branches` match the event's branch (a push's branch, or a pull request's HEAD branch) or its new `trigger.push_tags` match the pushed tag; `pull_request` is a reserved name matching every pull request. Components and images take `only_on_channels` / `not_on_channels` over those names (unknown names are refused). `prepare_plan` and the trusted publisher both call `plan_targets(project, runtimes, facts)`, so the publisher plans exactly the targets `prepare` planned for a push to the branch it releases (components and images selected, an excluded image's archive and evidence outputs removed) and refuses a publication whose target is not selected. At parse time a publication is refused if its component, or its image (`image-` artifact), would be excluded on its own channel. Verification treats a tag push matching a channel as release-eligible. A tag-only channel selects and verifies; the publisher still releases from protected branch heads only, so a tag-driven release kind does not publish by itself (open). 2. Images only where wanted (G3). Each image has its own `only_on_channels` / `not_on_channels`; a repository that built images on `develop` only declares that selection instead of dropping the image. The build is still once: an image-less event plans the same component without the image, whose different target means a bundle from an image-bearing event is not reused for it (correctly), and reuse of image-bearing bundles is unchanged. 3. Source scripts (E1). A `script` component may set `event_facts: true`; its target then carries the fixed `CI_SOURCE_REVISION`, `CI_EVENT_NAME`, `CI_REF_NAME` and `CI_REF_TYPE` (`tag` for a tag push, otherwise `branch`, the words of `github.ref_type`; added for the standards conformance helper's original iteration count) from the authoritative event (not the runner's environment), through the allow-list as target environment. Being part of the target they are part of `config_digest`, so such a component is not reused across commits or events, which is the truthful consequence; components that do not read them keep reuse. 4. Scan policy (G4). The one closed image field is `ignore_unfixed` (default false), which adds `--ignore-unfixed` to the single Trivy scan. HIGH and CRITICAL findings still fail, the scan cannot be turned off, and no other flag is accepted. The default is the current gate; nothing stricter was added. 5. Pinned private checkout (G2). `components..checkouts.` = `{binding, repository, ref (40-hex commit), path}` (consumer-owned identity; container platforms only). A matrix entry whose target has checkouts runs, in the three container verify jobs, the step `python3 -P -m platform_ci checkout-dependencies` (`dependency_checkout.py`), which authorizes each dependency against the operator manifest `runtime/checkout-bindings.yaml` (shipped EMPTY: it authorizes nothing and holds no value), fetches exactly the pinned commit with the credential only in the fetch process's environment (never in a URL or file), verifies the commit and that no credential reached the checkout configuration. The workflow forwards only the existing read-only repository secret `PUBLISHER_CHECKOUT_TOKEN` to that one step; no new credential, grant or service. Operator prerequisites, none created here: that secret is available to the consumer's runs and its token can read the dependency; the binding (secret, consumers, repositories) is added to the manifest in the reviewed platform revision; the consumer pins the commit. The captured `qbit_rc`-era secret name is the only value used, and no repository name is branched on. Open (not done, not claimed; "tag-driven kinds do not publish" is superseded by the next paragraph): a repository whose old kind-by-PR-head rules differ from channel matching must be checked per repository; no fixture proves any of this; Website-Content's actual dependency, ref and binding values are the operator's and are unknown here; the dependency checkout is not offered on host platforms; whether a scoped workflow can read `PUBLISHER_CHECKOUT_TOKEN` is unproven live; existing tests that count workflow steps, secrets or Target keys were adjusted or need adjusting. Tag-driven publication and run identity (ALL UNTESTED, source only; no signing, tagging or live write happened). - Publisher: `execute_enrollment` now releases from protected branch heads AND recent tags (`forge.release_tags`, newest five pages) through one `_execute_ref`, with the event facts `prepare` would plan for that push (`push_facts` / `tag_facts`), so the same channel rule (`event_selection`, `release.channels.trigger.push_tags`) picks the same eligible channels, publications and targets. A tag whose receipts all exist is skipped. A tag is eligible only if, before any write: (1) it matches the format of the channel's own `tag_policy` (`stable` = exactly `vX.Y.Z`, `v-prefix` = any `v…`; a tag-driven channel that a publication uses without a `stable` or `v-prefix` policy is refused at parse time; a tag channel that only selects work, such as a `publish*` kind with no publication, needs none and is not forced to carry a policy that does not fit it); (2) it names exactly the commit the verified run built, re-read at publish time; (3) for a package, it equals the tag that policy gives the declared version the verified manifest recorded, so a tag never relabels another version (an image has no version, so only the format applies); (4) its commit is in the history of a protected branch; (5) when the operator's enrollment lists `tag_fingerprints`, it is an annotated tag whose raw object carries one SSH signature of such a key, checked by the unchanged `verify_raw_tag` through a read-only `SubprocessTagGit` built from the existing api token (a tag signature, not an artifact attestation). Run, attempt, source tree, manifest, config and platform identity checks are the unchanged ones of `select_for_query`/`inspect_run`; versioning rules still apply. Without `tag_fingerprints` no signature is claimed. `version-` is not accepted: the captured prepare rules that decide the release kind never recognize it (only the old release-record lookup did). - No extra tag-triggered build: `release-tag` runs only on branch pushes (`release_tag` is empty for a tag push), so its tag cannot trigger another tag job. The tag's own run is a normal required verification that goes through the existing reuse step, so it reuses the branch run's tested bytes when the exact inputs and workflow trust match (tree, version, toolchain and runtime, platform revision, planned targets); targets that differ (a component selected differently for a tag, or one that opted in to event facts) legitimately build once. - Run identity for event-fact scripts: a component with `event_facts: true` gets `CI_RUN_ID` and `CI_RUN_ATTEMPT` from the workflow's own `github.run_id` / `github.run_attempt` (new env on the build step of the four verify jobs), validated positive integers, injected by `run_target_commands` only when the target already carries the event facts, and deliberately NOT part of the target, so `config_digest`, reuse and the publisher's equivalence proof are unaffected for every component, including this one (its outputs reuse across runs; a script whose output must differ per run should not be reused and is a policy decision for its owner). No other parent variable passes. - Remaining blocking contracts (named, not worked around): enrollment `tag_fingerprints` (operator-owned) must be set for signed-tag enforcement, which also needs the signing key's fingerprint from the owners; the tag API response shapes are now observed (root's `tag-api-contract-current.json`: for five annotated QRun-IO/qqq tags the `/tags` list entry has `commit.sha`, `/git/refs/tags/` returns a list whose exact entry has `object.type: tag`, and `/git/tags/` returns an object whose `object.sha` equals the list commit; for five lightweight qualification tags the exact ref has `object.type: commit` and `/git/tags/` answers 400, not 404), which proves shapes only, with no signing, tag creation or publication proof; `ForgeTagRefs` therefore decides annotated versus lightweight from the exact ref's `object.type` and never from that endpoint's status, asks the tag-object endpoint only for an annotated tag (where a failed read is an error, not a downgrade), and rejects a lightweight tag wherever an annotated or signed tag is required; tag names are encoded per path segment with `/` kept (`forge.tag_path`; empty, `.` and `..` segments refused), which assumes the forge takes the tag name as a wildcard path remainder, not yet observed for a tag containing `/`; a repository that uses lightweight tags and enables signature enforcement is refused by design; the legacy Maven-Central/RC completion evidence and release record are still not integrated; tags older than the newest 250 are never re-released from here; the migration worker must emit `push_tags`, `tag_policy` and the selection fields, and the checkout binding root recorded (`content-validator-source`, consumer `KofTwentyTwo/Website-Content`, dependency `KofTwentyTwo/Website-Publisher`) is preserved unchanged in `runtime/checkout-bindings.yaml`. Contract fixes after reading the native staged configs read-only (UNTESTED): (1) `CI_REF_TYPE` added to the opt-in event facts, because `standards-conformance.ps1` selects 10000 fuzz iterations for `CI_EVENT_NAME=schedule` or `CI_REF_TYPE=tag` and the platform passed no ref type; it is in the target like the other facts, so reuse identity stays correct (a component that reads it is not reused between a branch and a tag run), and a forged parent value is not passed. (2) The tag-policy rule was too strict for qqq's `feature` kind (`push_tags: [publish*]`, no publication, no policy); it now applies only to channels that a publication uses. (3) The tag-publication test fixture now declares the artifact format it needs, and a positive case shows the refusal is the selection rule. Compatibility read, not proven: the qqq, Voyage, mcp-rag, qqq-all, qqq-cli and Website-Content configs use only fields the candidate parses (`only_on_channels`/`not_on_channels` with `pull_request`, `push_tags`, `tag_policy`, `ignore_unfixed`, `checkouts`, `event_facts`); the Website-Content binding `content-validator-source` is preserved in `runtime/checkout-bindings.yaml`. Not satisfiable by the candidate today (named, not worked around): `schedule` events (the canonical workflow's `on:` has no `schedule`, so the standards 10000-iteration schedule path never runs; only a tag push can, and the staged standards config verifies no tags: `verification.push.tags: []` and no tag channel); pull-request title, body, number and a forge read binding for the standards `pr-title` and `pr-dco` gates; a host-platform script for Windows signing; `PUBLIC_GITHUB_AUDIT_TOKEN` for the workflows phase. Release completion seam (ALL UNTESTED; source only; nothing was uploaded, recorded, signed or read live). Policy is the repository's own: a release channel may set `central_completion: true` (Maven publications only) and `release_record: true` (Maven and npm; needs a `tag_policy`). After the existing idempotent publication, `publisher._finalize` collects terminal evidence per such request, and a release is reported complete only when it exists: - [SUPERSEDED by "Central producer integration" below: the producer and the binding are now identified; the receipt rules described here are the frozen primitive's, and `completion.py` was rewritten] Central completion (`delivery/completion.py`, the captured consumer rules of `qrun-source-version.mjs` `waitForCentralCompletion` and `qbit_rc.py` `CentralEvidence`): the receipt `qrun-publication-completion/1` in the generic registry (read with the existing reader-token package client, kind `generic`, path `repo--run-/.json`) must name exactly this repository, repository id, run, build commit, version and the sorted coordinates of the verified bundle (`adapters.maven_coordinates`). Absent, or any non-terminal state, is PENDING: the publication receipts stay, no record is written, the summary status is `published-awaiting-completion`, and the tag stays in play for the next invocation. `FAILED`-class states, any identity or coordinate contradiction, a wrong target for the version, and malformed hashes are errors. Complete means a release `central-publisher` / `PUBLISHED` with a deployment id and exactly the verified purls, or a snapshot `central-snapshots` / `UPLOADED` with files and no deployment id. Upload acceptance never completes. - Release record: the frozen `promote_package._ensure_release` through `legacy.ensure_release_record` (GET the record for `v`; POST once only if absent; an existing non-draft record is returned unchanged; a draft or mismatch is refused; prerelease flag from the version kind). It runs only after Central completion where required, only for the tag the channel's policy gives the verified version, and only after the tag is re-read and still names the verified commit (a release POST for an absent tag would make the forge create one at the default head). The client is `legacy.release_client`, the frozen `release_client` over the EXISTING `release-token` mount name of the frozen publisher service (read only on demand by `live.release_client`; no new credential or store). - Durable and idempotent: `...complete.json` is written once and a replay must match exactly (a conflict is an error); a tag whose publication receipts exist but whose `.complete` record does not is re-processed, and every step is idempotent (immutable registry writes, record lookup before post), so a retry duplicates no upload or record and relabels no bytes. No rebuild or retest happens. Exact missing data and code boundaries (not worked around; each fails closed): 1. [SUPERSEDED, see "Central producer integration": the producer was found in root's later snapshot `references/central-producer`] Producer and hash binding of the completion receipt. Nothing captured produces `qrun-publication-completion/1` (the captured `qrun_maven_promote` says "no Central, no signing"), and the captured consumer binds its `manifest_sha256` to the legacy reactor `manifest.json` it downloaded, which this pipeline's bundle does not contain. Missing: which job uploads to Central and writes the receipt (and which run id keys it: assumed the verified source run, as in the captured layout), and how `manifest_sha256` and `archive_sha256` are defined against this bundle. Boundary: `PublisherServices.completion_binder` (`completion.Binder`); with none, a terminal receipt is an error, so a `central_completion` channel cannot report complete today. 2. Release-record credential. `release-token` (contents write limited to `/repos//releases`) must be mounted for the publisher; the cron job in `release_central.py` lists only api, writer, reader and gitops tokens. Boundary: `PublisherServices.release_client`; unmounted, a configured `release_record` is an explicit error. The frozen client accepts only tags of the form `v[0-9]…`. 3. RC branch cutting, pending lock and version write-back are source-lifecycle writes, deliberately not rebuilt here. Their captured helpers are `qbit_rc.py` `cut()` (merge develop into `release/X.Y`, lock branch `automation/qbit-rc-pending`, intent artifact `qbit-rc-intent-`), `finalize()`/`recover()` (completion verification, signed tag via `signed_tag_primitives`, release record, lock release) and `qrun-source-version.mjs` `persistVersion` (signed staging branch, one-file `pom.xml` bump commit `Bump version to X.Y.Z-RC.n [skip ci]`, fast-forward push). Integration point: the one trusted write boundary (the `release-tag` phase and its token, or a dispatch of `finalize=true` sent by the publisher's own Actions-write job, which is how the captured callee worked), calling those helpers with their product constants parameterized (`LOCK`, `CUT_WORKFLOW`, the `qbit_rc_trust.json` fields `release_workflow_sha256`, `required_jobs`, `module_artifact`, `recovery`, `CENTRAL_GROUP` and artifact pattern, the `` rewrite). Missing: a repository-owned configuration for those parameters and the trigger from the publisher to the write phase; neither exists, and no central product handler was added. Also still open: signed-tag enforcement needs enrollment `tag_fingerprints`; the artifact-attestation verifier does not exist; whether `tests/test_release_completion.py` and the rest pass is unknown. Central producer integration (UNTESTED; source only). Correction: the producer EXISTS. Root's `references/central-producer` (a later historical publisher snapshot, files hash-matched by its `receipt.json`; live deployment not revalidated) holds `qrun_maven_promote.py` (`promote_reactor`: `publish_central`, then the completion receipt), `qrun_central.py`, `qrun_central_transport.py`, `qrun_completion_receipt.py`, `qrun_maven_evidence.py`, `publisher_service.py` and `forge_transport.py`. My earlier "producer not captured" was wrong because `publisher_frozen` held an older snapshot. - Integration (`delivery/completion.py`, rewritten; the speculative `completion_binder` seam and my own receipt evaluator are removed): `ensure_central_completion` is the generic, repository-independent part of the captured `promote_reactor`, using the frozen primitives through `legacy` (`central_primitives`, `central_client`, `gpg_signer`, imported lazily from `publisher_frozen/` so nothing else breaks until they are vendored). It builds the evidence from the VERIFIED retained files of the tested bundle (`adapters.maven_modules`, which now also carries `packaging`), parents first, so Central gets exactly the bytes the private registry got and nothing is rebuilt; saves the original deployment id durably BEFORE waiting (`...central-upload.json`, bound to build commit, archive hash and version; a different binding is an error); treats a pending deployment (`CentralPending`) as pending, never complete; writes the immutable `qrun-publication-completion/1` receipt only after Central reports a terminal state (`publish_central` returns PUBLISHED or UPLOADED); and on a repeat reads the existing receipt through `qrun_completion_receipt.read` (validated against the tested bundle), re-checks Central (a release still PUBLISHED with the same purls, snapshot files byte-identical) and uploads nothing. A duplicate-deployment response is an error for the operator, never retried. - [SUPERSEDED by "Central contract corrections" below: this binding relabelled the legacy fields and was rejected] Exact bundle binding: `manifest_sha256` = the verified manifest's own digest; `archive_sha256` = SHA-256 of the canonical sorted list of the selected artifact's retained (path, sha256) entries. These are the receipt's fields of those names, but they are defined over THIS pipeline's bundle: the legacy values (a reactor zip's hash and its `manifest.json` checksum) describe an artifact this pipeline does not have, so no new retained evidence and no second build was needed. The consumer is this same code, which recomputes both from the verified bundle. A legacy-format consumer (`qrun-source-version.mjs` `waitForCentralCompletion`) would not accept these hashes; it is not part of this flow. - [SUPERSEDED: the `qqq` company refusal and `central_allow_private` were removed] Data, not names: the public-route rule is `info.private` from the forge (a private repository is refused unless the operator's enrollment sets the new optional `central_allow_private`, replacing the two repositories the snapshot hardcoded); the company must be `qqq` because the frozen completion location is fixed to that company's generic registry owner (so a `kof` Central route is not available from this primitive). - Mounts (source-named, not proof of any live grant): `/run/publisher-central/{central-username, central-password, gpg-private-key-b64, gpg-passphrase, gpg-keyname}` read only on demand by `live.central`, plus the existing writer and reader tokens as `generic` package clients; unmounted or empty, a configured `central_completion` is an explicit error. Nothing was created or read. Still needed [item (1) is DONE: root vendored the three Central files; see "Central contract corrections"] (source evidence and operator facts, not worked around): (1) the seven files must be copied byte-identically into `publisher_frozen/` with a receipt (`qrun_central.py`, `qrun_central_transport.py`, `qrun_completion_receipt.py`; `forge_transport.py` and `qrun_maven_publication.py` already exist there, but the snapshot's `forge_transport.py` has a different hash than the older frozen copy, so whether the newer one is required, for example for `NoRedirect` use or other changes, must be decided by diffing; I could not copy or hash); until then Central publication raises "not vendored". (2) `qrun_maven_publication.plan_files` must accept the evidence modules built here; the captured evidence carried `packaging` and per-file `data` (present), while the older frozen copy's compatibility with `central: true` evidence was not diffed. (3) The Central Portal API shapes are marked UNQUALIFIED in the primitive's own docstring (`result-followup.md`, not in the copied set): needed to trust `status` and `upload_bundle`. (4) Operator facts: the Central credential and GPG key mounts exist for the publisher and are the intended key; GPG qualification against the real key; which enrolled repositories may use `central_allow_private`. (5) The RC cut and version write-back integration point is unchanged from the previous paragraph and remains unimplemented. Central contract corrections (UNTESTED; source only; root's two mismatches fixed). Root has now vendored `qrun_central.py`, `qrun_central_transport.py` and `qrun_completion_receipt.py` byte-identically with receipts; those bytes are untouched. 1. Legacy meaning preserved, no relabelling. The receipt fields keep their legacy meaning: `archive_sha256` = hash of the reactor archive, `manifest_sha256` = hash of its `manifest.json` (the legacy `evidence.checksums['manifest.json']`, which `qrun-source-version.mjs` `postpublish` compares with the `manifest.json` it reads from `maven-reactor/`). `delivery/maven_reactor.py` rebuilds that archive (`qrun-maven-reactor/1`: `files//`, `manifest.json`, `SOURCE_COMMIT`, `SHA256SUMS`) deterministically from the verified retained files, the POM-derived coordinates and packaging, the build commit and the recorded version, so nothing is compiled, tested or newly retained and any holder of the bundle recomputes identical bytes. The one honest difference: the legacy archive was the forge's artifact zip with non-reproducible zip bytes; this zip container is deterministic (sorted, fixed timestamps), so `archive_sha256` hashes this representation, not a historical upload. In the captured sources only the hex shape of `archive_sha256` is read; `manifest_sha256` is the compared, byte-meaningful field. `maven_reactor.materialize` writes the `maven-reactor/` tree the legacy source-version consumer reads. Because no field changed meaning, the schema `qrun-publication-completion/1` and the storage namespace `qrun-publication-completion` are NOT versioned. `tests/test_maven_reactor.py` runs the real captured `qbit_rc.verify_reactor` over the archive when the reference file is present. 2. No company or name switch. `CENTRAL_COMPANY` and the `qqq` refusal are gone. The destination is explicit authorization data: the enrollment's optional `central` object `{completion_owner, allow_private}` (`enrollment.CentralAuthorization`); `completion_owner` must equal the enrolled company's own registry owner (checked at load, like the existing package-owner rule), `allow_private` records an operator exception for a private source. No `central` object means Central publication is refused. The frozen receipt primitive fixes both the registry owner and the repository prefix to one company, so `delivery/completion_receipt.py` is a small extraction of it with explicit provenance (source hash recorded in its docstring): same schema, namespace, field set, validation rules and canonical bytes, with the owner as a parameter (plus type guards raising `ValueError`); for the owner the frozen file hardcodes it yields identical locations and bytes, which a test checks against the vendored original when present. The frozen file itself is not used by new code, only compared. 3. Interface compatibility of the vendored files with the existing frozen ones (read, not executed): `qrun_central` imports `plan_files` and `_snapshot_metadata` from `qrun_maven_publication`, and the frozen copy has both with matching signatures and the dict keys `qrun_central` reads (`module`, `entry`, `directory`, `file_version`); `qrun_central_transport` imports `NoRedirect` from `forge_transport`, which exists. EXACT MISMATCH found: the recovered `forge_transport.py` (sha256 0065be44…) differs from the frozen one (c8172548…) in one place, `registry_client`'s image pattern at line 216: recovered `(koftwentytwo|qrun-io|qrun-private)/…`, frozen `(koftwentytwo|qrun-io)/…`; the first 215 lines and everything else I read are identical, `package_client` included. It does not affect Central, the generic registry or this flow, and it was not overwritten. The recovered snapshot's `qrun_maven_publication.py` was not among the copied files, so any difference there could not be compared. 4. Consumers (what is and is not wired): the compared field of `qrun-source-version.mjs` `postpublish` and the RC finalizer's reactor verifier read the same fields and layout (provided by `maven_reactor`); but both run in the source-lifecycle write phase that is still not implemented: `qbit_rc.py` `module_evidence` looks the reactor up as an Actions ARTIFACT named by `module_artifact` on the release run, and `qrun-source-version.mjs` reads an extracted `maven-reactor/` directory, neither of which this pipeline's `verified-` bundle provides as such. Exact migration needed when that phase is built: materialize the reactor from the verified bundle (`maven_reactor.build_reactor` + `materialize`) for the source-version script, and point `module_evidence` at the verified bundle (or the materialized archive) instead of `forge.run_artifacts`; no parity with those consumers is claimed. Retained reactor captures (Android adapter, UNTESTED): when the verified bundle already retains `manifest.json`, `SOURCE_COMMIT` and `SHA256SUMS`, `completion.reactor_for` no longer regenerates anything: those three files are used byte for byte (an indented manifest keeps its exact bytes and hash) and `maven_reactor.from_retained` makes the deterministic zip of the retained entries under their retained relative paths, so `manifest_sha256` is the SHA-256 of the existing manifest bytes and `archive_sha256` the hash of that zip. It refuses contradictions instead of repairing them: a manifest that is not this reactor's (schema, build commit, version), a `SOURCE_COMMIT` other than the build commit, `SHA256SUMS` that does not cover exactly the other members with their hashes, a `files/` member the manifest does not describe or describes with another hash or size, a manifest hash different from the one the verified bundle records for those bytes (`adapters.reactor_manifest_sha256`), incomplete, ambiguous or foreign-root metadata, and any module file the adapter grouped (a Gradle `.module` included) that is not one of the retained entries. The `.module` file is part of the Central evidence (the frozen planner accepts any extension) and of the archive. For ordinary Maven files with no captured metadata the deterministic generation is unchanged; it no longer restricts classifier spelling or extension beyond a safe archive member name, since the adapter decides what a module may hold. The retained-capture path uses no company or repository name and no credential. Tests: `tests/test_maven_reactor.py` (`RetainedReactorTest`) and `tests/test_release_completion.py` (`RetainedCaptureTest`). The real `qbit_rc.verify_reactor` accepts only `jar` and `pom` extensions, so an archive with a `.module` or `.aar` file would not pass that consumer: part of the consumer lifecycle boundary below. Remaining consumer lifecycle boundary: the legacy RC finalizer (`qbit_rc.py` `module_evidence`: an Actions artifact named by `module_artifact`, `jar`/`pom` only) and the source-version script (`qrun-source-version.mjs` `postpublish`: an extracted `maven-reactor/` directory) still have no feed from the `verified-` bundle, and both run in the unimplemented source-lifecycle write phase; `maven_reactor.materialize` can write the directory from the archive's members when that phase exists. Remaining real gaps: Portal API shapes are UNQUALIFIED in the primitive itself (its `result-followup.md` was not copied); operator facts (Central and GPG mounts exist for the publisher, the key is the intended one, the writer and reader tokens may write and read the completion namespace) are unconfirmed; the write phase (RC cut, version write-back) is unimplemented; the vendored files' behavior has not been exercised. Docker profile environment: `build_tools.DOCKER_PROFILE_ENVIRONMENT` is the fixed pool contract (`DOCKER_HOST=unix:///run/test-docker.sock`, `TESTCONTAINERS_DOCKER_SOCKET_OVERRIDE`, `TESTCONTAINERS_HOST_OVERRIDE`). The plan puts it in `target.environment` for every target that requires `docker` (alone or with `browser`), because execution passes the build only the allow-list plus that environment, so the job-level variables of `verify-pool-docker` never reached the build process. Plain and browser-only targets get none of it, runner-injected `DOCKER_*` variables stay filtered, and release/manager credentials remain outside the allow-list; `tests/test_docker_environment.py` also requires the workflow job's values to equal this data. The browser profile adds no variable: its Chrome and chromedriver are found on the image's PATH, and no recorded requirement names a binary-path variable. Root's publication script must later include the `source_tools/` directory in what it publishes with the candidate. The image-packaging worker's patch is not integrated here. The `python` tool starts `python`, never `python3`, so the control image's own interpreter that runs the pipeline is not shadowed. `cargo` moves only `CARGO_HOME` to scratch; the image's `RUSTUP_HOME` is kept. Each declared command is one argument list for the tool's own executable, started without a shell; `build.commands` runs several in order. A tool is one row in `platform_ci/build_tools.py`, so adding one needs no change to planning or the workflow. `platforms` defaults to the first platform the tool supports; `requires` is accepted only for tools that define the profile (`maven`: browser, docker; `node` and `gradle`: browser). Native verification runs once per component and platform. After a successful build every declared report and artifact must match a nonempty file. After a failed build, existing reports are kept and no artifact is collected. Maven reports and parity: declare `**/surefire-reports/TEST-*.xml` and `**/failsafe-reports/TEST-*.xml` as an optional `junit` report (zero reports or zero executed tests passes, as the legacy gate allowed, while any failure, error or malformed report fails) and `**/target/site/jacoco/jacoco.xml` as an optional `file` report. Raw surefire and failsafe files are retained as collected. One legacy behavior is not reproduced: the per-module concatenation of surefire `.txt` output into `ci-reports/test-output` (pure collection; declare `**/surefire-reports/*.txt` as an optional `file` report to retain the same text per file). Maven settings: before the first command of a Maven target, `~/.m2/settings.xml` is written with the legacy public-repository layout: one `mirrorOf central` Google-hosted mirror and an always-active fallback profile with canonical Maven Central (release-only). Maven reads it without a flag, so the declared command line is unchanged (the legacy `-s /tmp/circleci/mvn-settings.xml` is not needed). An identical existing file is left alone; any other existing file or a link is refused rather than overwritten or merged. Parity note: the document layout was derived by reading the captured adapter's template code (`scripts/maven-public-mirror.mjs`, `--create` mode) and is pinned by a literal in the tests; the adapter itself was not executed here, so run it once and diff to confirm. The vendored helper `vendor/qqq-orb/setup_maven_settings.sh` is now captured (22 supplement files, 10 revisions of it, all byte-identical). It defines only `central` and `central-snapshots` `` entries that read `CENTRAL_USERNAME` and `CENTRAL_PASSWORD` from the environment; it defines no private dependency repository or mirror. Those are publication credentials, so they belong to the trusted publisher and are not a prerequisite of ordinary builds, which keep the credential-free public mirror and fallback settings above. This pipeline never reads, writes or names a credential in a build job. ## Events Pull requests, manual and `workflow_call` runs, pushes to the default branch, and pushes to branches matching a release channel's names or globs always verify once. Other pushes verify only if selected by `verification.push`. An unselected push reports "NOT REQUESTED" and never claims a pass. Release eligibility and the requested publications are reported only; the workflow never delivers (see "Delivery integration contract"). ## Dependency cache - Caches only `$HOME/.m2/repository` and `$HOME/.npm/_cacache`. Never build output, `node_modules`, settings, `.npmrc` or credentials. Directories that are relative, contain `..`, are symlinks or overlap the checkout are rejected. - Key: `platform-ci-deps-v1------`. Descriptors are reactor `pom.xml` files plus `.mvn/maven.config`, `jvm.config`, `extensions.xml` (Maven), or `package.json` files plus the lock file (npm). Application source is never an input. There is no restore-key fallback. - Save only after a successful build on a push to the default branch, and not on an exact hit. Everything else only restores. - A hit never skips the build or tests. Cache steps use `continue-on-error`. - Endpoint: the runner-provided native `ACTIONS_CACHE_URL` (private HTTP on a pod address once the native cache is enabled), used as given. The workflow names no cache address, allowlist, setting or credential; the native network policy and per-job authorization bound it. The private HTTPS runtime/results gate is unchanged and does not judge this endpoint. - No usable endpoint: `cache-metadata` outputs `available=false` and a note, both cache actions require `available == 'true'`, so nothing is restored, saved or retried, and the build and tests run as always. ## Native capabilities and runtime qualification The catalog (`runtime/catalog.yaml`) is the only place a runtime is chosen. A component names a toolchain version, a platform and optional profiles; `prepare` resolves them to a runner label and, on Linux, an image. Nothing falls back to another image, version or runner. - **Container platform (`linux-amd64`)**: the image comes from `toolchains...image` (or `browser_image` for the browser profile) and runs on the workflow's fixed `linux-small` pool. Profile pools (`browser`, `docker`, `browser+docker`) may be listed only as `null` until the workflow has a pool job. - **Host platforms (`windows-amd64`, `macos-arm64`)**: `verify-native` runs on `runners..standard`, and the toolchain version must list the platform under `hosts`. Listing it is an operator attestation; the pipeline does not probe the host. The host needs Python 3.13+ with PyYAML as `python3`, Git and the artifact-upload action runtime. - **Fixed non-secret tool variables** (for example `GOPATH`, `CARGO_HOME`, `GRADLE_USER_HOME`, `DOTNET_NOLOGO`) travel in the plan, never in the workflow. Container-only ones are not applied on hosts. Host system variables needed to start a tool (`SYSTEMROOT`, `USERPROFILE`, `PATHEXT`, `DEVELOPER_DIR` and similar) pass the sandbox; runner and release tokens never do. Shipped state and exactly what remains before each path can run (all currently stop at `prepare` with a message naming the entry): | Path | State in the shipped catalog | What qualification needs | | --- | --- | --- | | Go `1`, Rust `1`, Python `3.11`-`3.14` on Linux | `image: null` | Their images are not published or have scan findings. Record a digest only after a clean scan and a published, read-back image. | | Java `17`, Android `2026.10.1`, BuildKit `0.34.0` on Linux | Digests recorded from root's publication receipts | Publication only: run each once live. Android instrumented tests also need an emulator-capable pool; BuildKit has not been shown to work on the standard pool. | | Node `20.20.2` and `24.22.2`, Nix `2.35.4` on Linux | Digests recorded from root's publication receipts (`platform-ci-node20`, `platform-ci-node24`, `platform-ci-nix`) | Publication only: run each once live. Node 18 (three findings), Go, Rust and Python are not enabled. | | `control` `1` (the `script` tool) | The published control image, recorded (authorized) | Run once live. | | Gradle on Linux | Reuses the qualified `java` image (the legacy job used a JDK image and the project's wrapper) | One live run: the wrapper downloads Gradle at run time, so network reach and `GRADLE_USER_HOME` writability on that image are unproven. No Gradle cache is defined. | | Browser profile (Maven, Node, Gradle) | Java 21 `browser_image` recorded (published); Node's is null. The `browser` pool is recorded as the generic `container-build` label (root authorized it for DRAFT qualification only); `verify-pool` mounts the workspace and sets `--init --shm-size=256m`, with no Docker access | Root's tests of the mounts and the browser boundary on private fixtures in both organizations before merge or production activation. Not proven live. A custom catalog with a null or unlisted pool still refuses the profile. | | Docker profile (Maven with Testcontainers) | `docker` and `browser+docker` are recorded as the same generic label; `verify-pool-docker` is the only job that mounts the separate rootless test daemon socket and sets the Testcontainers variables | The same live proof, including how the socket is granted. Not proven live. | | .NET on Windows (`10.0.403`) and Xcode on macOS (`27`) | `hosts: []` | Operator attestation of the installed SDK or Xcode on the runner. | | Windows runner label | `runners.windows-amd64.standard: null` | Legacy used `kof-windows-vm6218` for one organization and `qqq-windows-amd64` for the other; one shared catalog cannot name both. Provision one shared label or decide the mapping. | | macOS runner label | `kof-macos-arm64` recorded | Only the toolchain attestation above. | | Go and Cargo on Windows and macOS | `hosts: []` | Attest the host toolchain. The legacy release-target cross builds ran through a script whose body was not captured; they are expressible only as declared `cargo`/`go` commands. | Not implemented because their legacy source was not captured or a design decision is open: Nix, pinned-fixture checkouts (the `script` tool now covers checked-in entrypoints such as the legacy shell-only checks, once the control runtime is recorded), and Python dependency installation policy (declare the install as the first `build.commands` entry meanwhile). ## Delivery integration contract Delivery runs in the **trusted publisher**, `python3 -P -m platform_ci.delivery`, never in the workflow. The workflow has no publish job, no secret reference and no release credential; it only produces verified bundles. The publisher is the existing isolated publisher boundary (same `--credentials`, `--receipts`, `--spool`, `--run`, `--publish`, `--fleet` shape, mounted credential names `api-token`, `writer-token`, `reader-token`, `gitops-token`, and the same frozen forge clients and publication primitives, vendored byte-for-byte in `publisher_frozen/` and checked against `receipts.json`). It is not a new control plane. ### Configuration contracts (requests only) `artifacts..format` is `maven`, `npm` or `container` (a package format needs its build tool). `release.channels..versioning` is `maven-snapshot`, `maven-release` or `declared`. `release.publications.` has `component`, `artifact`, `destination` (a logical name), `channels`, and optionally `platform` (required when the component builds for several) and `deploy` (a logical GitOps destination, container artifacts only). No field can carry an address, owner, credential or signer; unknown fields fail by path. Artifact `source` stays rejected by name. `examples/maven-publication.ci.yaml` documents all of it. The plan lists `delivery.publications` only for channels eligible for the event. ### Effective version before the build (UNTESTED; never executed) **Parity is PARTIAL, and no rule substitutes a different lifecycle: where a captured input is not carried the event FAILS naming it.** `release.channels..effective_version: {rule, property, rc_baseline}` (`develop-next-minor` | `rc-from-branch` | `patch-bump` | `stable-from-tag` | `feature-snapshot`; `property` defaults to `revision`; `rc_baseline` is `none` (default) or `reconciled`) is the captured `calculate_version.sh` handler of each branch type as the pure `platform_ci/effective_version.py`. The captured evidence uses all five: qqq (profile `core`, every publish kind true, `migration/qqq-release-facts.json`; channels `snapshot`, `release-candidate`, `hotfix`, `release`, `feature` in `migration/out/staged-15/staged/QRun-IO/qqq/.ci.yaml:116-137`), and qbit-tools (`migration/qbit-tools-release-facts.json`) and qqq-all use develop and `release/**`. For a push whose matching channel declares a rule, `plan_targets` takes the declared version of the unmodified target from an injected reader (checkout in `prepare`, forge in the publisher, `release-tag` and `finalize-version`), calculates the version, appends `-D=` to the commands of every Maven component the event selects (publication or not, as the captured calculator ran for every eligible kind), and re-reads the target to prove the static resolver now yields exactly that version. The version is therefore part of the plan (`config_digest`), consumed by the one native build, recorded as `build_inputs.version`, and recomputed identically by the publisher and tag planner from forge facts (declared version at the release commit, branch or tag); nothing compiles twice. The static resolver is only the fast-fail: Maven's own resolution is proved after the build by the publication adapter, which requires every retained POM's coordinates to equal the recorded version (`delivery/adapters.py::_require_manifest_version`); a verification-only component has no downstream consumer of the version, so nothing further is checked for it. Pull requests and unmatched pushes are unchanged; `.properties` `version_source` components are refused. Carried and exact: `rc-from-branch` with a declared RC or `rc_baseline: none`, `patch-bump`, `stable-from-tag`, `develop-next-minor` from a declared RC or stable version. REFUSED with the exact missing input (each is required by captured evidence, none is an immutable input of the release commit): develop with a declared SNAPSHOT (the "recent release merge" window, `git log --since="3 days ago"` anchored to the verify job's start, `qrun-maven-prepare.mjs:23-26`, `calculate_version.sh:273`); `rc_baseline: reconciled` with a declared `X.Y.0-SNAPSHOT` (qbit's `reconciledRcBaseline`, `qrun-maven-prepare.mjs:147-182`: commit parents, their root POM revisions, tag ancestry); every `feature-snapshot` (the originating feature branch of a `publish*` tag and git's commit abbreviation, `calculate_version.sh:35-53,342`; no fallback name or fixed-length hash is used and no feature-branch form is accepted, because `eventKind` never routes one, `qrun-maven-prepare.mjs:85`). A repository migrating the qbit profile must declare `rc_baseline: reconciled`; one that declares no rule keeps its declared version and claims nothing. Not carried at all (no captured repository fact enables them, `migration/*-release-facts.json`: `release_on_main: false`, `release_tag_policy` `stable` or null): `release_on_main`/latest-tag fallback and the `v-prefix` RC-tag route. A POM whose `project.version` does not use the property is refused instead of the calculator's `versions:set` rewrite. ### Version write-back (UNTESTED; never executed; NOT reachable from any workflow) `release.channels..version_write_back: {file: pom.xml, target_branch, require_push}` (`configuration.py::WriteBackPolicy`; presence is the opt-in, absence writes nothing) is the captured `qrun-source-version.mjs` persistence. A branch-driven channel writes to the branch it released from (no `target_branch`); a tag-driven channel names `target_branch` (the captured helper pushed to `main`); a channel with both trigger kinds is refused; the channel needs a Maven publication, whose evidence the write waits for. `python3 -P -m platform_ci finalize-version` (`delivery/finalize_version.py`, pinned platform code only, never checks out or runs the source, never rebuilds): 1. requires `CI_EVENT_NAME=push`, `CI_EVENT_REF`, `CI_SOURCE_REVISION`, `CI_REPOSITORY(_ID)`, `CI_RUN_ID`, `CI_RUN_ATTEMPT`, `CI_PLATFORM_REVISION`, `CI_SCOPED_SOURCE_ID`, `CI_WORKFLOW_SHA`, `GITHUB_SERVER_URL` = the trusted forge and `FORGE_TOKEN` (the job's own token); optional `CI_RELEASE_RUN_ID`. Proves the repository and that the CURRENT run is the canonical workflow (`current_workflow_trust`, as `release-tag`); 2. reads `.ci.yaml` at the release commit, finds the matching channels that declare it, selects the VERIFIED bundle with the publisher's own `select_artifacts` (same proofs, same recomputed inputs incl. the effective version), and takes `build_inputs.version` as the version to write; 3. `source_lifecycle.postpublish`: reads the source POM, requires every Maven file read back byte-identical from the private registry and, if the channel has `central_completion`, the terminal completion receipt of exactly this reactor (absent = `NotYetPublished`, nothing written, retry later; different = `Fatal`); only then a staging branch at the source commit, one server-signed `PUT` of the root `` (`revision_only_change`), strict re-verification (one file, sole parent, server-verified, exact message and bytes) and a normal non-forced fast-forward push through `VersionGit` (the tag boundary's `SubprocessTagGit`, token only in the child environment); only exact staging heads this run created are deleted. A push failure is a warning unless `require_push`. `SourceWriter` refuses every forge request outside that one write before sending it. Exact code that remains: a workflow job (or operator-run step) that calls `finalize-version` after the publisher finished, `tests/test_delivery_contracts.py` allowing exactly that job, and the RC finalizer (cut, lock, intent, tag, prerelease record, recover; `release-finalization-integration/platform_ci/delivery/rc_bundle_feed.py` holds its deferred bundle feed). Activation prerequisites, none established and none created here: (1) a TRIGGER after the publisher finished (the publisher publishes after the release run ends, so no step in that run can wait for it): a `workflow_dispatch` or `schedule` trigger plus whoever dispatches holding Actions write, or the operator running it; (2) the write identity may fast-forward push the protected target branch; (3) the forge server-signs `PUT contents` commits and reports `verification.verified`; (4) the job token has `contents: write` (as `release-tag`) and READ access to the private Maven registry and the generic completion namespace (the publisher uses a separate `reader-token`; whether the job token can read them is unconfirmed); (5) the release run's platform revision equals the running platform revision (`platform_revisions` is a single value here, where the publisher's comes from operator enrollment); (6) the registry owner is taken as the repository owner, which `enrollment.py` enforces for the publisher's destinations. ### Build inputs in the manifest Each target uploads `verified-` containing `manifest.json` (schema 1: source revision, platform revision, run id and attempt, target, component, platform, `build_succeeded`, each file's kind, name, path, size and SHA-256, and after a successful build `build_inputs`) and a `digest` over all fields including `build_inputs`: `source_tree` (git tree of the source revision, from `prepare`), `input_digest`, `toolchain` (tool version plus the digest-pinned runtime image or attested runner), `platform`, `config_digest` (the whole planned target) and `version` (read from the component's `pom.xml` or `package.json`; `unversioned` for other tools, `undeclared` when no literal is declared). A successful build cannot be packaged without the tree id. ### Reuse before verification (the no-duplicate-build rule) The `reuse` step of `prepare` runs only pinned platform code with the job's read-only token (job permissions `contents: read`, `actions: read`; it never checks out or runs the source). It applies only to a push whose verification is required (default branch, release channel branch, selected branch or tag); a pull request is the original verification and manual or callable runs always verify. It looks for ONE earlier successful run (a push, or the pull request that verified before the merge) that proves, independently of what its artifacts claim: every job succeeded and every retained bundle's manifest and file hashes verify; the run was executed by the canonical pinned workflow, which the forge shows and the artifact cannot (the caller file the run names, read at the run's own head commit, is trigger-only with one job whose `uses` is exactly the full-SHA `self://.gitea/scoped_workflows/@` reference that THIS run is executing under, read the same way; a custom workflow with steps, more jobs, inputs, secrets or another reference is rejected however well its bundles verify), and each manifest's platform revision agrees; its recorded `build_inputs` equal the inputs recomputed here from the forge (tree, declared version at this commit, the plan's toolchain, runtime image and commands); the run's head is an ancestor of this commit (bounded commit listing); and the built commit has an identical tree. Every target must be covered by that one run. If so, `verify` and `verify-native` skip (their matrices are empty and their flags false), the evidence is a job output, and the report requires it. If anything is missing, changed, unreachable or unproven, the matrices stay as planned and exactly one real verification runs. Reuse replaces only the redundant rebuild: the secret scan (a check of this exact revision's history) and preparation always run, and a failure of either fails the run. The report re-derives each target's required build inputs from the plan and rejects evidence that is for another revision or tree, covers different targets, lacks any of the five recorded checks, or has inputs that differ. The independent verification itself happens in the reuse step (the report has no artifact access), and the publisher repeats it. A pull request run built on its merge commit is accepted when its recorded tree equals the release tree and its head is an ancestor; `build_sha` (the commit that was built) is always kept apart from the release SHA. Live qualification must show what commit a pull request run's checkout builds. ### What the publisher does for one enrollment 1. Verifies the repository (id, name, not fork or archived) and lists its protected branches; the heads are the release SHAs. Nothing about branches comes from the enrollment. 2. Reads `.ci.yaml` at each head and derives the publications for the channels that branch makes eligible; a branch without `.ci.yaml` or publications requests nothing. 3. Finds tested bytes with the same selection code as the reuse step. The release commit's own push run is first if it succeeded; a failed run is never replaced by an older one and an unfinished run publishes nothing. If that run retained no bundles (it reused), earlier completed push and pull request runs follow, newest first, within `max_runs_scanned`, each proving equality on its own. 4. Checks every request against the operator's bindings (identity, destination, format, channel) and the channel's versioning rule, rechecks the branch head, and only then publishes through the frozen primitives with independent read-back, writes a receipt, and pins container digests in one signed GitOps commit. Receipts and results keep `build_sha` and `release_sha` separate and always state `deployment_verified: false`. An unsupported format, destination, target kind or binding fails explicitly (`UnsupportedFormatError`, `AuthorizationError`, `EnrollmentError`); there is no fallback. Without `--publish` the publisher only inspects and writes nothing. ### Operator enrollment (authorization data, never from a repository) One JSON file per repository in a trusted read-only mount: `schema`, `company` (`kof`|`qqq`), `repository`, `repository_id`, `workflow_references` (the full-SHA `self://.gitea/scoped_workflows/@` references whose callers the publisher trusts; an empty set trusts no run), `platform_revisions` (full hashes the publisher may release from), `max_artifact_bytes`, `max_runs_scanned`, and `destinations`: each a logical `destination`, the `formats` and `channels` it may carry (an authorization allow-list), a `target` (`package` owner, `image`, or `gitops` repository/branch/file/images) kept inside the company, and optional `require_provenance` and `require_signature_verification`. Branches, channels, versioning, publications and the required job names are not here: the first four come from the repository's `.ci.yaml` and the job names from `platform_ci/pipeline_contract.py`. The repository name appears only as authorization and provenance identity (the enrollment's identity, and the workflow's `CI_REPOSITORY` for the read-only client's API scope); a test confines it to those uses. ## Local quality commands Run from the `candidate/` root: ``` uv run --with pyyaml==6.0.3 python -m unittest discover -s tests -t . -v uv run --with ruff ruff format --check uv run --with ruff ruff check uv run --with pyright --with pyyaml --with types-pyyaml pyright ``` Requires Python 3.13+ and PyYAML 6.0.3 (`requirements.txt`). Config is in `ruff.toml` and `pyrightconfig.json` (strict). The tests start no application build. ## Promotion Promotion pins one pair: the source of the helper (`platform_ci/` plus `runtime/catalog.yaml`) and the workflow that names it. `PLATFORM_CI_REVISION` in the workflow holds the full helper commit hash, so promotion takes two reviewed changes because a revision cannot contain its own hash. Rollback restores the previous pair. Control image digests in `prepare` and `report` must match. Toolchain digests live only in `runtime/catalog.yaml`. Adding a toolchain version means one new image and one catalog entry, with no code change. Root will publish the source with the forge-verified API signer, because the local SSH key is unknown to the forge. Old PR 64 is a draft and is not activated. QRun pull PR 256 has a verified signer and the same bytes as 255; it is not merged yet. ## Live qualification of delivery (not yet run) Everything above is verified only against local synthetic transports (`tests/test_delivery_publisher.py`, a real git history, real packaging, in-memory registries). No live forge, registry, GitOps or credential was used. Root runs, in order, with the exact enrollment and credential mounts of the existing publisher (replace `<...>`): ``` # 1. The workflow produces manifests with build_inputs: run the shared pipeline on a fixture repository whose .ci.yaml declares a maven or node publication, then download verified- and check manifest.json has build_inputs. # 2. Inspection only, writes nothing, prints status eligible-inspection-only and the selected run: python3 -P -m platform_ci.delivery --enrollment --credentials --spool # 3. Inspection of one explicit run (a reuse test: pick an earlier successful run of an identical tree): python3 -P -m platform_ci.delivery --enrollment --credentials --spool --run # 4. Publication to the fixture destination only, after root authorizes that exact destination and fixture: python3 -P -m platform_ci.delivery --enrollment --credentials --receipts --spool --publish # 5. Replay step 4 unchanged: it must republish identical bytes and report the same receipt. ``` Observe live, and record in the evidence: that the caller file named by `run.path` (before `@`) is readable at the run's head commit and its `uses` is the expected full-SHA reference (it becomes `workflow_references`; the caller needs `actions: read` permission for the reuse step); that `git/commits/` returns `commit.tree.sha`; that `commits?sha=&page=&limit=50` lists the full ancestry in pages; that `actions/runs//artifacts` lists `verified-` with a `workflow_run` object and `actions/artifacts//zip` serves the manifest at the archive root; that contents reads at a commit return `size`, `encoding` and `content`. ## Remaining work - Reuse live qualification (unrun): that the job token (`github.token`) can read this repository's runs, jobs, artifacts and commits with `actions: read`, and that `github.event.repository.id` is populated; what a pull request run's checkout builds (head or merge commit); `commits?sha=` paging for ancestry; the artifact zip layout. Until then the reuse step falls back to a normal build and says why in its note. - Scoped production identity is implemented from two existing server-computed mechanisms (see `platform_ci/delivery/scoped_identity.py`): the workflow-file run listing with `scoped_workflow_source_repo_id=` (only a run executed from that scoped source appears; the same listing without it holds only repository-level runs and answers 404 for a scoped-only file), and the prepare job's server-evaluated name `Prepare plan [workflow ${{ github.workflow_sha }}]`, which records the source commit. A run is trusted when its (source id, file, commit) triple is the current run's; the required jobs recognize that exact suffix and no job was added. The canonical source id (`PLATFORM_CI_SOURCE_REPOSITORY_ID`) is an identity constant of the shared workflow, never routing. A repository-level caller is trusted only after the run is shown to be in the unscoped listing, so a consumer's same-named file cannot vouch for a scoped run. Root's live proofs (run 4424 listed only with source 3; a wrong source listed nothing; the unscoped listing answered 404; run 4426's job name equalled its workflow commit) show the mechanisms and the job token's `actions: read` permission work. Still pending live: the canonical scoped values after registration, from inside a real scoped run. Any failure, 404 or missing proof only means a normal build. Reuse across a workflow promotion is refused (a changed workflow commit is a changed input). - Partial reuse is not implemented on purpose: all targets must come from one earlier run, otherwise everything verifies once. - Delivery wiring gaps: signature verification of native provenance (no verifier is injected, so a binding with `require_signature_verification` fails closed; no provenance file is produced by the workflow yet); release records and tags (the frozen `release_client` flow for main and hotfix is not ported); a push-time or scheduled invocation of the publisher (root owns the CronJob, mounts and enrollments); GitOps ordering across older runs; npm `dist-tag` is the channel name, or `latest` for `stable`. - Tests for adapters, reuse selection, manifests, provenance and GitOps text live in `delivery-workspace/tests` (pytest) and run against this package; they are not yet ported to unittest here. - QRun live fixture. - Qualification of scoped registration and the manual, schedule, pull request, tag and skip paths. - Required shared security checks. - Live qualification of everything in "Native capabilities and runtime qualification": a Gitea evaluation of `runs-on: ${{ matrix.runner }}` with an `if` on a matrix-output flag, a host job on a real Windows and macOS runner, the unexecuted Maven settings parity diff, and each runtime in the table. - Image builds (container build capability) and delivery. - Cutover of all fleet configuration.