diff --git a/.agents/skills/watch-github-actions/SKILL.md b/.agents/skills/watch-github-actions/SKILL.md index 063c4b3a95..6250676268 100644 --- a/.agents/skills/watch-github-actions/SKILL.md +++ b/.agents/skills/watch-github-actions/SKILL.md @@ -167,7 +167,9 @@ During `0.x`, a minor train permits compatibility findings as warnings; a patch train or no active train rejects them. Compare the current train's version with the latest stable release; commit messages are irrelevant. Compilation, baseline, and tool errors remain fatal. The `protobuf_compatibility` suite participates in -the `release-tag-v1` qualification profile. Both tagged pre-release and stable +the `release-tag-v1` qualification profile. Release Tag calls the reusable +`release-qualification.yml` workflow to run the checks and collect their results. +Both tagged pre-release and stable publication require this profile to pass. Failed, cancelled, or skipped suites block publication; build artifacts and qualification evidence remain in Actions storage for diagnosis. Source-SHA images are staging inputs for qualification. @@ -179,15 +181,17 @@ For `Codex Compatibility Review`, inspect its job summary and the candidate and baseline SHAs before interpreting findings. `complete` describes execution and coverage, not compatibility: also read the assessment and findings. An `error`, incomplete coverage, or missing artifact means no clean review is -available. The job is advisory and independent of publication gates. The -review uses high reasoning effort and focuses on breaking changes. Explicit -risk acceptance requires a linked author or verified-maintainer comment or -review; a PR description, general approval, or earlier bot report is insufficient. -The full report retains unchanged findings with previous-comment links. -`comment.md` contains only new or materially updated findings, and an unchanged -review does not post another PR comment. Inspect the full artifact for outstanding -breaks even when the bot is silent. The qualification summary points to it -without waiting for completion. Do not +available. Findings are advisory. The review targets breaking changes with high +reasoning effort. Risk acceptance needs a linked author or verified-maintainer +comment or review, not a PR description, general approval, or bot report. +The full report keeps all findings and prior-comment links. `comment.md` includes +only new or materially changed findings; unchanged reviews post no comment. Check +the full artifact linked by qualification even when the bot is silent. +The review is scheduled independently of Security Scan. The two Codex workflows +share the `codex-release-review` concurrency group with `queue: max`, retaining +up to 100 pending reviews while preventing overlapping API usage across runs. +Either review can run first; other security scanners run in parallel. +The qualification caller waits for both reviews to finish. Do not rerun Release Tag to debug this reviewer. Use its dedicated manual workflow with an existing candidate tag; that path does not publish release artifacts. diff --git a/.github/workflows/codex-compatibility.yml b/.github/workflows/codex-compatibility.yml index 64d358389b..d7ab27d3ed 100644 --- a/.github/workflows/codex-compatibility.yml +++ b/.github/workflows/codex-compatibility.yml @@ -46,8 +46,9 @@ permissions: pull-requests: read concurrency: - group: codex-compatibility-${{ inputs.candidate_ref }}-${{ github.run_id }} + group: codex-release-review cancel-in-progress: false + queue: max jobs: review: diff --git a/.github/workflows/codex-security.yml b/.github/workflows/codex-security.yml index 77e620c168..d42d7d7c1e 100644 --- a/.github/workflows/codex-security.yml +++ b/.github/workflows/codex-security.yml @@ -71,8 +71,9 @@ permissions: security-events: write concurrency: - group: codex-security-release-qualification - cancel-in-progress: true + group: codex-release-review + cancel-in-progress: false + queue: max env: CODEX_SECURITY_MAX_CONCURRENT_THREADS: "8" diff --git a/.github/workflows/release-qualification.yml b/.github/workflows/release-qualification.yml new file mode 100644 index 0000000000..e038fc040d --- /dev/null +++ b/.github/workflows/release-qualification.yml @@ -0,0 +1,240 @@ +# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +name: Release Qualification + +on: + workflow_call: + inputs: + release-tag: + description: Release tag identifying the candidate + required: true + type: string + source-sha: + description: Source commit of the candidate artifacts in the calling run + required: true + type: string + is-prerelease: + description: Whether the candidate is a pre-release + required: true + type: boolean + outputs: + artifact-name: + description: Artifact containing the qualification result + value: ${{ jobs.qualification-result.outputs.artifact-name }} + current-profile-passed: + description: Whether every required qualification suite passed + value: ${{ jobs.qualification-result.outputs.current-profile-passed }} + secrets: + CODEX_SECURITY_API_KEY: + required: true + CACHIX_AUTH_TOKEN: + required: false + +permissions: + contents: read + packages: read + +env: + RELEASE_TAG: ${{ inputs.release-tag }} + +defaults: + run: + shell: bash + +jobs: + security: + name: Security Scan + permissions: + actions: read + contents: read + packages: read + security-events: write + uses: ./.github/workflows/security-scan.yml + with: + candidate_ref: ${{ inputs.release-tag }} + fail-on-static-findings: false + images: | + ghcr.io/nvidia/openshell/gateway:${{ inputs.source-sha }} + ghcr.io/nvidia/openshell/sandbox:${{ inputs.source-sha }} + ghcr.io/nvidia/openshell/supervisor:${{ inputs.source-sha }} + secrets: + CACHIX_AUTH_TOKEN: ${{ secrets.CACHIX_AUTH_TOKEN }} + + prepare-integration: + permissions: + actions: read + contents: read + packages: read + uses: ./.github/workflows/prepare-integration-inputs.yml + with: + source-sha: ${{ inputs.source-sha }} + deb-artifact-name: deb-linux-amd64 + rpm-artifact-name: rpm-linux-x86_64 + + conformance-integration: + needs: prepare-integration + permissions: + actions: read + contents: read + packages: read + uses: ./.github/workflows/integration-runner.yml + with: + category: conformance + source-sha: ${{ needs.prepare-integration.outputs.source_sha }} + integration-inputs-artifact-id: ${{ needs.prepare-integration.outputs.integration_inputs_artifact_id }} + test-matrix: >- + [ + {"environment":"ubuntu-docker-rootful","installer":"deb","testsuite":"conformance"}, + {"environment":"ubuntu-k3s","installer":"k3s","testsuite":"conformance"}, + {"environment":"ubuntu-k3s","installer":"k3s-ha","testsuite":"conformance"}, + {"environment":"fedora-podman-rootful","installer":"rpm","testsuite":"conformance"}, + {"environment":"fedora-podman-rootless","installer":"rpm","testsuite":"conformance"} + ] + + feature-specific-integration: + needs: prepare-integration + permissions: + actions: read + contents: read + packages: read + uses: ./.github/workflows/integration-runner.yml + with: + category: feature-specific + source-sha: ${{ needs.prepare-integration.outputs.source_sha }} + integration-inputs-artifact-id: ${{ needs.prepare-integration.outputs.integration_inputs_artifact_id }} + test-matrix: >- + [ + {"environment":"fedora-podman-rootful","installer":"rpm","testsuite":"provider-refresh"}, + {"environment":"fedora-podman-rootless","installer":"rpm","testsuite":"provider-refresh"}, + {"environment":"ubuntu-docker-rootful","installer":"deb","testsuite":"oci-image"}, + {"environment":"fedora-podman-rootful","installer":"rpm","testsuite":"oci-image"}, + {"environment":"fedora-podman-rootless","installer":"rpm","testsuite":"oci-image"} + ] + + docker-e2e: + permissions: + actions: read + contents: read + packages: read + uses: ./.github/workflows/e2e-docker-test.yml + with: + image-tag: ${{ inputs.source-sha }} + checkout-ref: ${{ inputs.source-sha }} + runner: linux-arm64-cpu8 + + vm-e2e: + permissions: + actions: read + contents: read + packages: read + uses: ./.github/workflows/e2e-vm-test.yml + with: + checkout-ref: ${{ inputs.source-sha }} + + protobuf-compatibility: + name: Protobuf Compatibility + runs-on: linux-amd64-cpu8 + timeout-minutes: 15 + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + fetch-depth: 0 + persist-credentials: false + ref: ${{ inputs.source-sha }} + + - uses: ./.github/actions/check-protobuf-compatibility + with: + ref: refs/tags/${{ env.RELEASE_TAG }} + + compatibility-review: + name: Compatibility Review (advisory) + permissions: + contents: read + pull-requests: read + uses: ./.github/workflows/codex-compatibility.yml + with: + candidate_ref: ${{ inputs.release-tag }} + source_sha: ${{ inputs.source-sha }} + secrets: + CODEX_SECURITY_API_KEY: ${{ secrets.CODEX_SECURITY_API_KEY }} + + qualification-result: + name: Release Qualification + if: always() + needs: + - protobuf-compatibility + - security + - conformance-integration + - feature-specific-integration + - docker-e2e + - vm-e2e + runs-on: ubuntu-latest + timeout-minutes: 5 + permissions: + contents: read + outputs: + artifact-name: ${{ steps.summary.outputs.artifact-name }} + current-profile-passed: ${{ steps.summary.outputs.current-profile-passed }} + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + ref: ${{ inputs.source-sha }} + + - name: Record qualification result + id: summary + env: + CONFORMANCE_RESULT: ${{ needs.conformance-integration.result }} + DOCKER_E2E_RESULT: ${{ needs.docker-e2e.result }} + FEATURE_INTEGRATION_RESULT: ${{ needs.feature-specific-integration.result }} + IS_PRERELEASE: ${{ inputs.is-prerelease }} + PROTO_COMPATIBILITY_RESULT: ${{ needs.protobuf-compatibility.result }} + SECURITY_RESULT: ${{ needs.security.result }} + SOURCE_SHA: ${{ inputs.source-sha }} + VM_E2E_RESULT: ${{ needs.vm-e2e.result }} + run: | + set -euo pipefail + current_profile_passed=$(tasks/scripts/generate-qualification-summary.sh qualification-summary.json) + artifact_name="qualification-${RELEASE_TAG}-run-${GITHUB_RUN_ID}-attempt-${GITHUB_RUN_ATTEMPT}" + run_url=$(jq -r '.run.url' qualification-summary.json) + + { + echo "artifact-name=${artifact_name}" + echo "current-profile-passed=${current_profile_passed}" + } >> "${GITHUB_OUTPUT}" + + { + echo "## Release qualification" + echo + echo "- Candidate: \`${RELEASE_TAG}\` (\`${SOURCE_SHA}\`)" + echo "- Current qualification profile passed: **${current_profile_passed}**" + echo "- RFC-0014 qualification coverage complete: **false**" + echo "- Run: [${GITHUB_RUN_ID}, attempt ${GITHUB_RUN_ATTEMPT}](${run_url})" + echo + echo "| Suite | Result |" + echo "| --- | --- |" + echo "| Protobuf API compatibility | ${PROTO_COMPATIBILITY_RESULT} |" + echo "| Security | ${SECURITY_RESULT} |" + echo "| Conformance integration | ${CONFORMANCE_RESULT} |" + echo "| Feature integration | ${FEATURE_INTEGRATION_RESULT} |" + echo "| Docker E2E | ${DOCKER_E2E_RESULT} |" + echo "| VM E2E | ${VM_E2E_RESULT} |" + echo + echo "Advisory compatibility findings do not affect the qualification result." + echo "See the Compatibility Review job and compatibility-review-run-${GITHUB_RUN_ID}-attempt-${GITHUB_RUN_ATTEMPT} artifact in [this run](${run_url})." + } >> "${GITHUB_STEP_SUMMARY}" + + - name: Upload qualification result + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 + with: + name: ${{ steps.summary.outputs.artifact-name }} + path: qualification-summary.json + retention-days: 90 + if-no-files-found: error + + - name: Require current qualification profile before publication + if: steps.summary.outputs.current-profile-passed != 'true' + run: | + echo "Release ${RELEASE_TAG} did not pass the current release-tag-v1 qualification profile." >&2 + exit 1 diff --git a/.github/workflows/release-tag.yml b/.github/workflows/release-tag.yml index 126c708d27..c1f4f9310a 100644 --- a/.github/workflows/release-tag.yml +++ b/.github/workflows/release-tag.yml @@ -129,208 +129,24 @@ jobs: image-tag: ${{ needs.compute-versions.outputs.source_sha }} checkout-ref: ${{ needs.compute-versions.outputs.source_sha }} - security: - name: Security Scan - needs: [compute-versions, build-images] + qualification-result: + name: Release Qualification + if: always() && needs.compute-versions.result == 'success' + needs: [compute-versions, build-binaries, build-vm-driver, build-images, build-deb, build-rpm] permissions: actions: read contents: read packages: read security-events: write - uses: ./.github/workflows/security-scan.yml - with: - candidate_ref: ${{ inputs.tag || github.ref_name }} - fail-on-static-findings: false - images: | - ghcr.io/nvidia/openshell/gateway:${{ needs.compute-versions.outputs.source_sha }} - ghcr.io/nvidia/openshell/sandbox:${{ needs.compute-versions.outputs.source_sha }} - ghcr.io/nvidia/openshell/supervisor:${{ needs.compute-versions.outputs.source_sha }} - secrets: - CACHIX_AUTH_TOKEN: ${{ secrets.CACHIX_AUTH_TOKEN }} - - prepare-integration: - needs: [compute-versions, build-binaries, build-deb, build-images, build-rpm] - permissions: - actions: read - contents: read - packages: read - uses: ./.github/workflows/prepare-integration-inputs.yml - with: - source-sha: ${{ needs.compute-versions.outputs.source_sha }} - deb-artifact-name: deb-linux-amd64 - rpm-artifact-name: rpm-linux-x86_64 - - conformance-integration: - needs: prepare-integration - permissions: - actions: read - contents: read - packages: read - uses: ./.github/workflows/integration-runner.yml - with: - category: conformance - source-sha: ${{ needs.prepare-integration.outputs.source_sha }} - integration-inputs-artifact-id: ${{ needs.prepare-integration.outputs.integration_inputs_artifact_id }} - test-matrix: >- - [ - {"environment":"ubuntu-docker-rootful","installer":"deb","testsuite":"conformance"}, - {"environment":"ubuntu-k3s","installer":"k3s","testsuite":"conformance"}, - {"environment":"ubuntu-k3s","installer":"k3s-ha","testsuite":"conformance"}, - {"environment":"fedora-podman-rootful","installer":"rpm","testsuite":"conformance"}, - {"environment":"fedora-podman-rootless","installer":"rpm","testsuite":"conformance"} - ] - - feature-specific-integration: - needs: prepare-integration - permissions: - actions: read - contents: read - packages: read - uses: ./.github/workflows/integration-runner.yml - with: - category: feature-specific - source-sha: ${{ needs.prepare-integration.outputs.source_sha }} - integration-inputs-artifact-id: ${{ needs.prepare-integration.outputs.integration_inputs_artifact_id }} - test-matrix: >- - [ - {"environment":"fedora-podman-rootful","installer":"rpm","testsuite":"provider-refresh"}, - {"environment":"fedora-podman-rootless","installer":"rpm","testsuite":"provider-refresh"}, - {"environment":"ubuntu-docker-rootful","installer":"deb","testsuite":"oci-image"}, - {"environment":"fedora-podman-rootful","installer":"rpm","testsuite":"oci-image"}, - {"environment":"fedora-podman-rootless","installer":"rpm","testsuite":"oci-image"} - ] - - docker-e2e: - needs: [compute-versions, build-binaries, build-images] - permissions: - actions: read - contents: read - packages: read - uses: ./.github/workflows/e2e-docker-test.yml - with: - image-tag: ${{ needs.compute-versions.outputs.source_sha }} - checkout-ref: ${{ needs.compute-versions.outputs.source_sha }} - runner: linux-arm64-cpu8 - - vm-e2e: - needs: [compute-versions, build-binaries, build-vm-driver] - permissions: - actions: read - contents: read - packages: read - uses: ./.github/workflows/e2e-vm-test.yml - with: - checkout-ref: ${{ needs.compute-versions.outputs.source_sha }} - - protobuf-compatibility: - name: Protobuf Compatibility - needs: compute-versions - runs-on: linux-amd64-cpu8 - timeout-minutes: 15 - steps: - - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - fetch-depth: 0 - persist-credentials: false - ref: ${{ needs.compute-versions.outputs.source_sha }} - - - uses: ./.github/actions/check-protobuf-compatibility - with: - ref: refs/tags/${{ env.RELEASE_TAG }} - - compatibility-review: - name: Compatibility Review (advisory) - needs: compute-versions - permissions: - contents: read pull-requests: read - uses: ./.github/workflows/codex-compatibility.yml + uses: ./.github/workflows/release-qualification.yml with: - candidate_ref: ${{ inputs.tag || github.ref_name }} - source_sha: ${{ needs.compute-versions.outputs.source_sha }} + release-tag: ${{ inputs.tag || github.ref_name }} + source-sha: ${{ needs.compute-versions.outputs.source_sha }} + is-prerelease: ${{ needs.compute-versions.outputs.is_prerelease == 'true' }} secrets: CODEX_SECURITY_API_KEY: ${{ secrets.CODEX_SECURITY_API_KEY }} - - qualification-result: - name: Release Qualification - if: always() - needs: - - compute-versions - - protobuf-compatibility - - security - - conformance-integration - - feature-specific-integration - - docker-e2e - - vm-e2e - runs-on: ubuntu-latest - timeout-minutes: 5 - permissions: - contents: read - outputs: - artifact-name: ${{ steps.summary.outputs.artifact-name }} - current-profile-passed: ${{ steps.summary.outputs.current-profile-passed }} - steps: - - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - persist-credentials: false - ref: ${{ needs.compute-versions.outputs.source_sha || github.sha }} - - - name: Record qualification result - id: summary - env: - CONFORMANCE_RESULT: ${{ needs.conformance-integration.result }} - DOCKER_E2E_RESULT: ${{ needs.docker-e2e.result }} - FEATURE_INTEGRATION_RESULT: ${{ needs.feature-specific-integration.result }} - IS_PRERELEASE: ${{ needs.compute-versions.outputs.is_prerelease }} - PROTO_COMPATIBILITY_RESULT: ${{ needs.protobuf-compatibility.result }} - SECURITY_RESULT: ${{ needs.security.result }} - SOURCE_SHA: ${{ needs.compute-versions.outputs.source_sha }} - VM_E2E_RESULT: ${{ needs.vm-e2e.result }} - run: | - set -euo pipefail - current_profile_passed=$(tasks/scripts/generate-qualification-summary.sh qualification-summary.json) - artifact_name="qualification-${RELEASE_TAG}-run-${GITHUB_RUN_ID}-attempt-${GITHUB_RUN_ATTEMPT}" - run_url=$(jq -r '.run.url' qualification-summary.json) - - { - echo "artifact-name=${artifact_name}" - echo "current-profile-passed=${current_profile_passed}" - } >> "${GITHUB_OUTPUT}" - - { - echo "## Release qualification" - echo - echo "- Candidate: \`${RELEASE_TAG}\` (\`${SOURCE_SHA}\`)" - echo "- Current qualification profile passed: **${current_profile_passed}**" - echo "- RFC-0014 qualification coverage complete: **false**" - echo "- Run: [${GITHUB_RUN_ID}, attempt ${GITHUB_RUN_ATTEMPT}](${run_url})" - echo - echo "| Suite | Result |" - echo "| --- | --- |" - echo "| Protobuf API compatibility | ${PROTO_COMPATIBILITY_RESULT} |" - echo "| Security | ${SECURITY_RESULT} |" - echo "| Conformance integration | ${CONFORMANCE_RESULT} |" - echo "| Feature integration | ${FEATURE_INTEGRATION_RESULT} |" - echo "| Docker E2E | ${DOCKER_E2E_RESULT} |" - echo "| VM E2E | ${VM_E2E_RESULT} |" - echo - echo "Advisory compatibility review runs independently and does not gate publication." - echo "See the Compatibility Review job and compatibility-review-run-${GITHUB_RUN_ID}-attempt-${GITHUB_RUN_ATTEMPT} artifact in [this run](${run_url})." - } >> "${GITHUB_STEP_SUMMARY}" - - - name: Upload qualification result - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 - with: - name: ${{ steps.summary.outputs.artifact-name }} - path: qualification-summary.json - retention-days: 90 - if-no-files-found: error - - - name: Require current qualification profile before publication - if: steps.summary.outputs.current-profile-passed != 'true' - run: | - echo "Release ${RELEASE_TAG} did not pass the current release-tag-v1 qualification profile." >&2 - exit 1 + CACHIX_AUTH_TOKEN: ${{ secrets.CACHIX_AUTH_TOKEN }} build-python-wheel: name: Build Python Wheel diff --git a/CI.md b/CI.md index fc9547720f..7df39b4c18 100644 --- a/CI.md +++ b/CI.md @@ -82,6 +82,11 @@ stable release to the exact candidate commit, using the same tag-derived patch/minor policy as the protobuf check. It reviews public Rust, Python, Go, and TypeScript SDKs, API behavior, CLI contracts, configuration, policy, Helm, and persisted state. +Both Codex workflows share the `codex-release-review` concurrency group with +`queue: max`, so only one review runs at a time and up to 100 pending reviews +are retained across branch, release, and manual runs. The release compatibility +review is scheduled independently of Security Scan; other security scanners run +in parallel. It does not replace Buf or change the release train's version. The agent inspects code first, then uses `gh` with a read-only GitHub token to @@ -113,9 +118,9 @@ agent shell commands; the inference credential is excluded. It does not run candidate builds or tests. The review has a 30-minute execution limit inside a 40-minute job timeout. -This rollout is report-only: findings and review failures cannot block -merging or publication. The job is independent of the required qualification -dependency chain. Its summary distinguishes complete, incomplete, and failed execution +This rollout is report-only: findings and review failures remain advisory. +The qualification caller waits for the review to finish, but its result does not +affect the required profile. Its summary distinguishes complete, incomplete, and failed execution from the agent's compatibility assessment. Missing credentials, invalid baselines, source mismatches, malformed reports, and timeouts are not clean reviews. A completed review still does not prove that no breaks exist. @@ -311,8 +316,9 @@ jobs: ``` Set `needs: security` on a downstream promotion job to require successful scans. -The tagged release workflow records protobuf, security, and integration outcomes in a -qualification job after publishing its commit-addressed OCI images. A failed +The tagged release workflow calls `release-qualification.yml` to run and record +protobuf, security, and integration checks after building its candidate artifacts +and publishing its commit-addressed OCI images. A failed check remains visible in the workflow, with build artifacts and evidence retained in Actions storage. Both pre-release and stable publication require the implemented `release-tag-v1` profile to pass; that profile is an incremental subset of RFC @@ -569,6 +575,7 @@ These workflows run after merge to publish dev/tagged artifacts and verify them. |---|---| | `.github/workflows/release-dev.yml` | Publishes the rolling `dev` build on every push to `main`. Builds gateway, sandbox, and supervisor images and binaries, packages, wheels, and pushes the Helm chart as `oci://ghcr.io/nvidia/openshell/helm-chart:0.0.0-dev` (plus an immutable `0.0.0-dev.` pin). Also dispatchable manually. | | `.github/workflows/release-tag.yml` | Publishes stable releases and pre-releases on tag pushes or manual dispatch. Release Auto-Tag dispatches it explicitly because its `GITHUB_TOKEN` pushes do not trigger workflows. Both require the currently implemented qualification profile to pass before publication; the summary identifies the remaining RFC 0014 coverage. Failed candidates retain build artifacts and evidence in Actions storage. Source-SHA OCI images remain available as qualification inputs. | +| `.github/workflows/release-qualification.yml` | Reusable qualification suites and result aggregation. Runs security scans, conformance and feature integration, Docker and VM E2E, protobuf compatibility, and advisory compatibility review. | | `.github/workflows/snap-package.yml` | Builds snap and component artifacts for Release Dev and Release Tag without Store credentials or publication. | | `.github/workflows/snap-publish.yml` | Uploads existing snap and component artifacts to the Store without rebuilding. Release Dev calls it directly for `latest/edge` after snap builds and requires it to succeed before creating the dev release. Release Tag calls it for `latest/stable` only for stable releases, after qualification and release assembly succeed. | | `.github/workflows/release-canary.yml` | Smoke-tests published dev artifacts in the `macos`, `ubuntu-deb`, `ubuntu-snap-system-docker`, `fedora`, and `kubernetes` (kind + Helm) jobs. Each job reaches its gateway and creates, exercises, and deletes a sandbox. The `ubuntu-snap-system-docker` job verifies a compatible system Docker lifecycle, while `ubuntu-snap-docker-preflight` tests fail-fast behavior when Docker is absent or supplied by the Docker snap. The positive snap lane also runs a local policy containment check with the packaged prover. It runs automatically after `Release Dev` succeeds and supports manual dispatch (`gh workflow run release-canary.yml --ref `). See the `test-release-canary` skill for the playbook and local kind reproduction. |