Skip to content

feat: expose applicant review progress #5771

feat: expose applicant review progress

feat: expose applicant review progress #5771

Workflow file for this run

name: RegistryStack CI
on:
schedule:
- cron: "37 22 * * *"
workflow_dispatch:
inputs:
full:
description: Run every CI gate, including archive and release recipe sweeps
type: boolean
default: true
pull_request:
# `labeled` lets the ci:full label start a run with the heavy tier.
types: [opened, synchronize, reopened, labeled]
branches:
- main
- release/1.0
merge_group:
types:
- checks_requested
push:
branches:
- main
concurrency:
group: ci-${{ github.event_name == 'pull_request' && format('pr-{0}', github.event.pull_request.number) || format('run-{0}', github.run_id) }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
permissions:
contents: read
env:
CARGO_TERM_COLOR: always
CARGO_INCREMENTAL: "0"
CARGO_PROFILE_DEV_DEBUG: "0"
CARGO_PROFILE_TEST_DEBUG: "0"
CARGO_FUZZ_VERSION: "0.13.2"
CARGO_LLVM_COV_VERSION: "0.8.7"
GITLEAKS_VERSION: "8.30.1"
GITLEAKS_LINUX_X64_SHA256: "551f6fc83ea457d62a0d98237cbad105af8d557003051f41f3e7ca7b3f2470eb"
ACTIONLINT_VERSION: "1.7.12"
ACTIONLINT_LINUX_X64_SHA256: "8aca8db96f1b94770f1b0d72b6dddcb1ebb8123cb3712530b08cc387b349a3d8"
OASDIFF_VERSION: "1.23.0"
OASDIFF_LINUX_X64_SHA256: "972b10535c3db4366b9dc3ebc11ca021279af3095267c3cffdca854a3a3c4f89"
# Matches the Vale version docs/site's @vvago/vale package pins; the docs
# style checks refuse a VALE_BIN that reports any other version.
VALE_VERSION: "3.24.0"
VALE_LINUX_X64_SHA256: "867534ddb678abca7f214bc4706ead0922bd5252ffe8035fc227013f66c8b214"
jobs:
changes:
name: Changed paths
runs-on: ubuntu-24.04
timeout-minutes: 5
outputs:
archive_base_ref: ${{ steps.filter.outputs.archive_base_ref }}
archive_comparison_required: ${{ steps.filter.outputs.archive_comparison_required }}
full_sweep: ${{ steps.filter.outputs.full_sweep }}
rust: ${{ steps.filter.outputs.rust }}
rust_matrix: ${{ steps.filter.outputs.rust_matrix }}
rust_packages: ${{ steps.filter.outputs.rust_packages }}
platform: ${{ steps.filter.outputs.platform }}
platform_assurance: ${{ steps.filter.outputs.platform_assurance }}
platform_coverage: ${{ steps.filter.outputs.platform_coverage }}
platform_hygiene: ${{ steps.filter.outputs.platform_hygiene }}
config_conformance: ${{ steps.filter.outputs.config_conformance }}
discovery_contracts: ${{ steps.filter.outputs.discovery_contracts }}
breg_contracts: ${{ steps.filter.outputs.breg_contracts }}
breg_integration: ${{ steps.filter.outputs.breg_integration }}
breg_contracts_lanes: ${{ steps.filter.outputs.breg_contracts_lanes }}
casework_postgres: ${{ steps.filter.outputs.casework_postgres }}
scheduling_postgres: ${{ steps.filter.outputs.scheduling_postgres }}
evidence_contracts: ${{ steps.filter.outputs.evidence_contracts }}
evidence_assurance: ${{ steps.filter.outputs.evidence_assurance }}
evidence_postgres: ${{ steps.filter.outputs.evidence_postgres }}
scheduling_contracts: ${{ steps.filter.outputs.scheduling_contracts }}
messaging_contracts: ${{ steps.filter.outputs.messaging_contracts }}
messaging_postgres: ${{ steps.filter.outputs.messaging_postgres }}
documents_contracts: ${{ steps.filter.outputs.documents_contracts }}
documents_postgres: ${{ steps.filter.outputs.documents_postgres }}
adapter_contracts: ${{ steps.filter.outputs.adapter_contracts }}
coordinator: ${{ steps.filter.outputs.coordinator }}
release_tool: ${{ steps.filter.outputs.release_tool }}
release_source_proof: ${{ steps.filter.outputs.release_source_proof }}
docs: ${{ steps.filter.outputs.docs }}
docs_archives: ${{ steps.filter.outputs.docs_archives }}
editors: ${{ steps.filter.outputs.editors }}
client_bindings: ${{ steps.filter.outputs.client_bindings }}
release_linux_node_clients: ${{ steps.filter.outputs.release_linux_node_clients }}
evidence_tutorial: ${{ steps.filter.outputs.evidence_tutorial }}
breg_tutorial: ${{ steps.filter.outputs.breg_tutorial }}
casework_tutorial: ${{ steps.filter.outputs.casework_tutorial }}
messaging_tutorial: ${{ steps.filter.outputs.messaging_tutorial }}
documents_tutorial: ${{ steps.filter.outputs.documents_tutorial }}
render_tutorial: ${{ steps.filter.outputs.render_tutorial }}
scheduling_tutorial: ${{ steps.filter.outputs.scheduling_tutorial }}
breg_evidence_composition: ${{ steps.filter.outputs.breg_evidence_composition }}
identifiers: ${{ steps.filter.outputs.identifiers }}
steps:
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
with:
fetch-depth: 0
submodules: false
- name: Classify changed paths
id: filter
shell: bash
run: |
set -euo pipefail
metadata="${RUNNER_TEMP}/registry-stack-cargo-metadata.json"
# Routing uses declared workspace dependencies without registry downloads.
cargo metadata --locked --no-deps --format-version 1 > "${metadata}"
python3 .github/scripts/ci_event_routing.py \
--metadata "${metadata}" \
--github-output "${GITHUB_OUTPUT}"
- name: Check repository identifier reference closure
run: products/identifiers/scripts/generate.py --check-references
- name: Install uv
uses: astral-sh/setup-uv@07a047e608db84b3d8c06cd9a7c7dcc9c355c18e # v10.3.2
with:
version: "0.11.16"
- name: Test CI classifier
run: uv run --no-project --with PyYAML==6.0.2 python .github/scripts/test_ci_changes.py
- name: Test CI event routing
run: python3 .github/scripts/test_ci_event_routing.py
- name: Test Cargo runtime-library resolution
run: >-
python3 -m unittest
scripts/test_cargo_runtime_library_path.py
scripts/test_cargo_runtime_script_contracts.py
- name: Test load-test harness tooling
run: >-
python3 -m unittest
scripts/loadtest/test_evidence.py
products/breg/loadtest/support/test_harness.py
products/evidence/loadtest/support/test_harness.py
products/casework/loadtest/support/test_harness.py
- name: Test ThunderID branding packager
run: python3 -m unittest products/identity/thunderid/branding/test_build.py
- name: Test nightly coverage planning
run: python3 .github/scripts/test_nightly_rust_coverage.py
- name: Test Base Registry Engine gated test selection gate
run: python3 .github/scripts/test_check_breg_gated_tests.py
- name: Check feature-gated Base Registry Engine tests are selected
run: python3 .github/scripts/check_breg_gated_tests.py
- name: Test platform fuzz runner
run: uv run --no-project --with PyYAML==6.0.2 python -m unittest products/platform/scripts/test_run_fuzz_smoke.py
- name: Test evidence fuzz runner
run: uv run --no-project --with PyYAML==6.0.2 python -m unittest products/evidence/scripts/test_run_fuzz_smoke.py
- name: Install pinned actionlint
shell: bash
run: |
set -euo pipefail
mkdir -p "${RUNNER_TEMP}/bin"
curl --proto '=https' --tlsv1.2 --fail --silent --show-error --location \
--output "${RUNNER_TEMP}/actionlint.tar.gz" \
"https://github.com/rhysd/actionlint/releases/download/v${ACTIONLINT_VERSION}/actionlint_${ACTIONLINT_VERSION}_linux_amd64.tar.gz"
echo "${ACTIONLINT_LINUX_X64_SHA256} ${RUNNER_TEMP}/actionlint.tar.gz" | \
sha256sum --check --strict
tar -xzf "${RUNNER_TEMP}/actionlint.tar.gz" -C "${RUNNER_TEMP}/bin" actionlint
- name: Lint GitHub Actions workflows
run: |
set -euo pipefail
"${RUNNER_TEMP}/bin/actionlint"
- name: Check Debian 13 image contract
run: python3 release/scripts/check-debian13-images.py
- name: Test GNU libc floor
run: python3 -m unittest release/scripts/test_glibc_floor.py
- name: Test Debian 13 image contract
run: python3 -m unittest release/scripts/test_check_debian13_images.py
- name: Test container runtime preflight
run: python3 -m unittest docker/test_runtime_preflight.py
- name: Test advisory baseline exposure policy
run: python3 -m unittest release/scripts/test_check_advisory_baselines.py
secrets:
name: Secret scan
runs-on: ubuntu-24.04
timeout-minutes: 5
steps:
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
with:
fetch-depth: 0
persist-credentials: false
submodules: false
- name: Install pinned Gitleaks
shell: bash
run: |
set -euo pipefail
mkdir -p "${RUNNER_TEMP}/bin"
curl --fail --silent --show-error --location \
--output "${RUNNER_TEMP}/gitleaks.tar.gz" \
"https://github.com/gitleaks/gitleaks/releases/download/v${GITLEAKS_VERSION}/gitleaks_${GITLEAKS_VERSION}_linux_x64.tar.gz"
echo "${GITLEAKS_LINUX_X64_SHA256} ${RUNNER_TEMP}/gitleaks.tar.gz" | sha256sum -c -
tar -xzf "${RUNNER_TEMP}/gitleaks.tar.gz" -C "${RUNNER_TEMP}/bin" gitleaks
chmod +x "${RUNNER_TEMP}/bin/gitleaks"
- name: Scan tracked source for secrets
run: >-
"${RUNNER_TEMP}/bin/gitleaks" dir
--config .gitleaks.toml
--no-banner
--redact
--timeout 120
.
platform-quality:
name: Platform all-features
needs: changes
if: needs.changes.outputs.platform == 'true'
runs-on: ubuntu-24.04
timeout-minutes: 10
steps:
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
with:
persist-credentials: false
submodules: false
- name: Cache Cargo registry and build artifacts
uses: Swatinem/rust-cache@c8037a86dde8214a8310e9f54a8fc61ab362aa31 # v2
with:
key: platform-all-features
save-if: ${{ github.ref == 'refs/heads/main' }}
- name: Lint platform crates with all features
run: cargo clippy --locked -p 'registry-platform-*' --all-targets --all-features -- -D warnings
platform-coverage:
name: Platform line coverage
needs:
- changes
- rust-policy
if: ${{ !cancelled() && needs.changes.result == 'success' && needs.changes.outputs.platform_coverage == 'true' }}
runs-on: ubuntu-24.04
timeout-minutes: 15
# --all-features enables the dispatch and activation PostgreSQL tests,
# which fail rather than skip without their disposable database.
services:
postgres:
image: docker.io/library/postgres:17.11@sha256:67f41722b7a8cbdb868a44a4995c846eddfdc2973bccb291ce937dce88ad5675
env:
POSTGRES_DB: dispatch
POSTGRES_USER: dispatch
POSTGRES_PASSWORD: dispatch_test
options: >-
--health-cmd "pg_isready -U dispatch -d dispatch"
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports:
- 5432/tcp
steps:
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
with:
fetch-depth: 0
persist-credentials: false
submodules: false
- name: Cache Cargo registry and build artifacts
uses: Swatinem/rust-cache@c8037a86dde8214a8310e9f54a8fc61ab362aa31 # v2
with:
key: platform-coverage
save-if: ${{ github.ref == 'refs/heads/main' }}
- name: Install pinned coverage tool
uses: taiki-e/install-action@1e29709c0ec63babbe8f266397a465f29a825f91 # v2.87.31
with:
tool: cargo-llvm-cov@${{ env.CARGO_LLVM_COV_VERSION }}
- name: Enforce platform line coverage
env:
DISPATCH_TEST_DATABASE_URL: postgresql://dispatch:dispatch_test@localhost:${{ job.services.postgres.ports['5432'] }}/dispatch
ACTIVATION_TEST_DATABASE_URL: postgresql://dispatch:dispatch_test@localhost:${{ job.services.postgres.ports['5432'] }}/dispatch
run: >-
cargo llvm-cov --locked
-p 'registry-platform-*'
--all-features
--fail-under-lines 80
- name: Export platform coverage
run: |
set -euo pipefail
coverage_dir="target/platform-coverage"
mkdir -p "${coverage_dir}"
cargo llvm-cov report --locked \
-p 'registry-platform-*' \
--lcov \
--output-path "${coverage_dir}/platform.lcov"
- name: Stage platform coverage for trusted upload
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: platform-coverage
path: target/platform-coverage/platform.lcov
if-no-files-found: error
retention-days: 1
platform-coverage-upload:
name: Upload platform coverage
needs:
- changes
- platform-coverage
if: github.event_name == 'push' && github.ref == 'refs/heads/main' && needs.changes.outputs.platform_coverage == 'true'
runs-on: ubuntu-24.04
timeout-minutes: 5
permissions:
actions: read
contents: read
id-token: write
steps:
- name: Download platform coverage
uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
with:
name: platform-coverage
path: target/platform-coverage
- name: Upload platform coverage to Codecov
uses: codecov/codecov-action@303a32d7a59b442fa8d48b6a1cc6825c09c847a5 # v7.1.1
with:
disable_search: true
fail_ci_if_error: false
files: target/platform-coverage/platform.lcov
flags: platform
name: platform
use_oidc: true
version: v11.3.1
platform-hygiene:
name: Platform hygiene
needs: changes
if: needs.changes.outputs.platform_hygiene == 'true'
runs-on: ubuntu-24.04
timeout-minutes: 10
steps:
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
with:
persist-credentials: false
submodules: false
- name: Cache Cargo registry
uses: Swatinem/rust-cache@c8037a86dde8214a8310e9f54a8fc61ab362aa31 # v2
with:
shared-key: workspace-registry
cache-targets: false
save-if: ${{ github.ref == 'refs/heads/main' }}
- name: Check shared Rust hygiene files
run: products/platform/scripts/check-hygiene-alignment.sh
- name: Test the YAML reader boundary checks
run: python3 -m unittest products/platform/scripts/test_check_yaml_reader_boundary.py
- name: Enforce the YAML reader boundary
run: products/platform/scripts/check-yaml-reader-boundary.sh
- name: Test the rstack dependency boundary check
run: python3 -m unittest products/platform/scripts/test_check_rstack_dependencies.py
- name: Keep rstack a plain bootstrap launcher
run: python3 products/platform/scripts/check-rstack-dependencies.py
platform-fuzz:
name: Platform fuzz smoke (${{ matrix.group }})
needs:
- changes
- rust-policy
if: ${{ !cancelled() && needs.changes.result == 'success' && needs.changes.outputs.platform_assurance == 'true' }}
runs-on: ubuntu-24.04
timeout-minutes: 15
strategy:
fail-fast: false
matrix:
include:
- group: authcommon-sqlite
target_a: authcommon_parsers
target_b: sqlite_statement
- group: sd-jwt
target_a: sdjwt_issuance
target_b: sdjwt_presentation_parse
- group: jose
target_a: crypto_jws_parse
target_b: crypto_jwe_decrypt
- group: yaml
target_a: yaml_reader
target_b: yaml_decode
steps:
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
with:
persist-credentials: false
submodules: false
- name: Install Rust nightly
uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # nightly
with:
toolchain: nightly
- name: Cache Cargo registry and build artifacts
uses: Swatinem/rust-cache@c8037a86dde8214a8310e9f54a8fc61ab362aa31 # v2
with:
key: platform-fuzz-${{ matrix.group }}
save-if: ${{ github.ref == 'refs/heads/main' }}
workspaces: products/platform/fuzz -> target
- name: Install pinned cargo-fuzz
uses: taiki-e/install-action@1e29709c0ec63babbe8f266397a465f29a825f91 # v2.87.31
with:
tool: cargo-fuzz@${{ env.CARGO_FUZZ_VERSION }}
- name: Smoke fuzz target pair
env:
FUZZ_TARGET_A: ${{ matrix.target_a }}
FUZZ_TARGET_B: ${{ matrix.target_b }}
run: products/platform/scripts/run-fuzz-smoke.sh "$FUZZ_TARGET_A" "$FUZZ_TARGET_B"
- name: Upload fuzz failures
if: failure()
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: platform-fuzz-${{ matrix.group }}
path: |
products/platform/fuzz/artifacts/${{ matrix.target_a }}/
products/platform/fuzz/artifacts/${{ matrix.target_b }}/
if-no-files-found: ignore
evidence-fuzz:
name: Evidence fuzz smoke (${{ matrix.group }})
needs:
- changes
- rust-policy
if: ${{ !cancelled() && needs.changes.result == 'success' && needs.changes.outputs.evidence_assurance == 'true' }}
runs-on: ubuntu-24.04
timeout-minutes: 15
strategy:
fail-fast: false
matrix:
include:
- group: verifier
target_a: verifier_flattened_jws
target_b: verifier_sd_jwt_vc
target_c: verifier_holder_proof
- group: authoring
target_a: authoring_openapi
target_b: authoring_project
steps:
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
with:
persist-credentials: false
submodules: false
- name: Install Rust nightly
uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # nightly
with:
toolchain: nightly
- name: Cache Cargo registry and build artifacts
uses: Swatinem/rust-cache@c8037a86dde8214a8310e9f54a8fc61ab362aa31 # v2
with:
key: evidence-fuzz-${{ matrix.group }}
save-if: ${{ github.ref == 'refs/heads/main' }}
workspaces: products/evidence/fuzz -> target
- name: Install pinned cargo-fuzz
uses: taiki-e/install-action@1e29709c0ec63babbe8f266397a465f29a825f91 # v2.87.31
with:
tool: cargo-fuzz@${{ env.CARGO_FUZZ_VERSION }}
- name: Smoke fuzz target pair
env:
FUZZ_TARGET_A: ${{ matrix.target_a }}
FUZZ_TARGET_B: ${{ matrix.target_b }}
FUZZ_TARGET_C: ${{ matrix.target_c }}
run: products/evidence/scripts/run-fuzz-smoke.sh "$FUZZ_TARGET_A" "$FUZZ_TARGET_B" ${FUZZ_TARGET_C:+"$FUZZ_TARGET_C"}
- name: Upload fuzz failures
if: failure()
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: evidence-fuzz-${{ matrix.group }}
path: |
products/evidence/fuzz/artifacts/${{ matrix.target_a }}/
products/evidence/fuzz/artifacts/${{ matrix.target_b }}/
products/evidence/fuzz/artifacts/${{ matrix.target_c || matrix.target_a }}/
if-no-files-found: ignore
rust-policy:
name: Rust policy
needs: changes
if: needs.changes.outputs.rust == 'true'
runs-on: ubuntu-24.04
timeout-minutes: 5
steps:
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
with:
fetch-depth: 0
submodules: false
- name: Cache Cargo registry
uses: Swatinem/rust-cache@c8037a86dde8214a8310e9f54a8fc61ab362aa31 # v2
with:
shared-key: workspace-registry
cache-targets: false
save-if: ${{ github.ref == 'refs/heads/main' }}
- name: Install cargo-deny
uses: taiki-e/install-action@1e29709c0ec63babbe8f266397a465f29a825f91 # v2.87.31
with:
tool: cargo-deny@0.19.8
- name: Install uv
if: needs.changes.outputs.release_tool != 'true'
uses: astral-sh/setup-uv@07a047e608db84b3d8c06cd9a7c7dcc9c355c18e # v10.3.2
with:
version: "0.11.16"
- name: Gate inventory
if: needs.changes.outputs.release_tool != 'true'
run: python3 release/scripts/check-gates-inventory.py
- name: Test gate inventory
if: needs.changes.outputs.release_tool != 'true'
run: uv run --no-project --with PyYAML==6.0.2 python3 -m unittest release/scripts/test_check_gates_inventory.py
- name: Test Coordinator dependency gate
run: python3 -m unittest discover -s products/coordinator/scripts -p 'test_*.py'
- name: Test MCP dependency boundaries
run: |
python3 -m unittest discover -s products/platform/scripts -p 'test_check_mcp_dependencies.py'
python3 -m unittest discover -s products/casework/scripts -p 'test_dependency_direction.py'
- name: MCP dependency direction
run: |
python3 products/platform/scripts/check-mcp-dependencies.py
python3 products/casework/scripts/check_dependency_direction.py
- name: Test Scheduling integrations
if: needs.changes.outputs.release_tool != 'true'
run: |
uv run --locked --directory products/scheduling/integrations/calendar-busy \
python -m unittest -v test_calendar_busy.py
python3 -m unittest discover -s products/scheduling/integrations/finite-series -p 'test_*.py'
python3 -m unittest discover -s products/scheduling/integrations/operational-reporting -p 'test_*.py'
- name: Coordinator dependency direction
# Runs on every Rust change, so a crate that starts depending on a
# Coordinator crate is refused wherever it lives.
run: python3 products/coordinator/scripts/check_dependency_direction.py
- name: Cargo deny
run: cargo deny check
- name: Standalone Cargo lockfiles
# Fuzz, SDK and editor workspaces keep their own Cargo.lock, and most
# build root crates by path, so a root change can leave one stale.
# Resolving with --locked fails on a stale lock and reads only the
# registry index, not crate sources.
shell: bash
run: |
set -euo pipefail
mapfile -t locks < <(git ls-files '*/Cargo.lock')
(( ${#locks[@]} > 0 ))
for lock in "${locks[@]}"; do
echo "Checking ${lock}"
cargo update --workspace --locked \
--manifest-path "$(dirname "${lock}")/Cargo.toml"
done
rust-quality:
name: Rust format and clippy
needs:
- changes
- rust-policy
if: ${{ !cancelled() && needs.changes.result == 'success' && needs.changes.outputs.rust == 'true' }}
runs-on: ubuntu-24.04
timeout-minutes: 15
env:
CI_RUST_PACKAGES: ${{ needs.changes.outputs.rust_packages }}
steps:
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
with:
persist-credentials: false
submodules: false
- name: Cache Cargo registry
uses: Swatinem/rust-cache@c8037a86dde8214a8310e9f54a8fc61ab362aa31 # v2
with:
shared-key: workspace-registry
cache-targets: false
save-if: ${{ github.ref == 'refs/heads/main' }}
- name: Format
run: cargo fmt --check
- name: Clippy affected packages
run: python3 .github/scripts/run_cargo_packages.py clippy
- name: Report disk usage
if: always()
run: |
df -h /
du -sh target 2>/dev/null || true
rust-tests:
name: Rust tests (${{ matrix.name }})
needs:
- changes
- rust-policy
if: ${{ !cancelled() && needs.changes.result == 'success' && needs.changes.outputs.rust == 'true' }}
runs-on: ubuntu-24.04
# The Casework and BReg shards take 11 to 13 minutes on a typical runner;
# this limit only stops a hung shard, so it leaves room for a slow one.
timeout-minutes: 25
strategy:
fail-fast: false
# Avoid filling the runner pool with short shards while the longer
# product integration jobs wait. Every selected shard still runs.
max-parallel: 6
matrix: ${{ fromJSON(needs.changes.outputs.rust_matrix) }}
env:
CI_RUST_ALL_FEATURES: ${{ matrix.all_features }}
CI_RUST_PACKAGES: ${{ toJSON(matrix.packages) }}
steps:
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
with:
persist-credentials: false
submodules: false
- name: Cache Cargo registry
uses: Swatinem/rust-cache@c8037a86dde8214a8310e9f54a8fc61ab362aa31 # v2
with:
shared-key: workspace-registry
cache-targets: false
save-if: ${{ github.ref == 'refs/heads/main' }}
- name: Test affected packages
run: python3 .github/scripts/run_cargo_packages.py test
- name: Test the synthetic authorization server
if: matrix.name == 'platform' && contains(matrix.packages, 'registry-platform-testing')
run: cargo test --locked -p registry-platform-testing --features test-authorization-server --test test_authorization_server
- name: Check Casework schemas against their generators
if: matrix.name == 'casework'
run: products/casework/scripts/check-schemas.sh
- name: Validate Manifest profiles
if: matrix.name == 'manifest'
working-directory: products/manifest
run: cargo run --locked -p registry-manifest-cli -- validate-profiles profiles
- name: Reproduce the Manifest schemas
if: matrix.name == 'manifest'
run: cargo test --locked -p registry-manifest-cli --features schema --lib schema::tests
- name: Reproduce the configurable MCP schema
if: matrix.name == 'mcp' && contains(matrix.packages, 'registry-mcp')
run: cargo test --locked -p registry-mcp --features schema --lib schema::tests
- name: Reproduce the Render schemas
if: matrix.name == 'render'
run: cargo test --locked -p registry-render --features schema --lib schema::tests
- name: Reproduce the task connection schema
if: matrix.name == 'developer-tools' && contains(matrix.packages, 'registry-thunderid-tooling')
run: cargo test --locked -p registry-thunderid-tooling --features schema --lib schema::tests
- name: Reproduce the saved API connections schema
if: matrix.name == 'developer-tools' && contains(matrix.packages, 'registry-cli-api')
run: cargo test --locked -p registry-cli-api --features schema --lib schema::tests
- name: Report disk usage
if: always()
run: |
df -h /
du -sh target 2>/dev/null || true
evidence-contracts:
name: Evidence contracts and source neutrality
needs: changes
if: needs.changes.outputs.evidence_contracts == 'true'
runs-on: ubuntu-24.04
timeout-minutes: 15
steps:
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
with:
persist-credentials: false
submodules: false
- name: Cache Cargo registry
uses: Swatinem/rust-cache@c8037a86dde8214a8310e9f54a8fc61ab362aa31 # v2
with:
shared-key: workspace-registry
cache-targets: false
save-if: ${{ github.ref == 'refs/heads/main' }}
- name: Test bounded FHIR example transport
run: python3 products/evidence/examples/fhir/test_source.py
- name: Reproduce Evidence generated contracts
run: products/evidence/scripts/check-contracts.sh
- name: Reproduce the Evidence authoring schemas
run: products/evidence/scripts/check-authoring-schema.sh
- name: Enforce the authoring library's no-input-output invariant
run: products/evidence/scripts/check-authoring-no-io.sh
- name: Self-test the Evidence neutrality gate
run: products/evidence/scripts/test-check-source-neutrality.sh
- name: Enforce Evidence source-product neutrality
run: products/evidence/scripts/check-source-neutrality.sh
- name: Enforce Evidence verifier portability
run: products/evidence/scripts/check-verifier-portability.sh
# A deployment without wallet sources links none of the presentation
# verification dependencies, so the runtime must build without the
# default `wallet-presentation` feature and must not reach that crate.
- name: Build Evidence without wallet presentation
shell: bash
run: |
set -euo pipefail
cargo check --locked -p registry-evidence --no-default-features --all-targets
cargo test --locked -p registry-evidence --no-default-features --test host_protocols
if cargo tree --locked -p registry-evidence --no-default-features -e normal --prefix none \
| grep -q '^registry-evidence-presentation '; then
echo "::error::registry-evidence reaches registry-evidence-presentation without the wallet-presentation feature"
exit 1
fi
- name: Install uv
uses: astral-sh/setup-uv@07a047e608db84b3d8c06cd9a7c7dcc9c355c18e # v10.3.2
with:
version: "0.11.16"
- name: Self-test the Evidence configuration key-path gate
run: uv run --no-project --with PyYAML==6.0.2 python3 products/evidence/scripts/test_evidence_config_key_paths.py
- name: Enforce Evidence configuration reference parity
run: uv run --no-project --with PyYAML==6.0.2 products/evidence/scripts/check-config-key-paths.sh
evidence-postgres:
name: Evidence PostgreSQL status ledger and store
needs: [changes, rust-policy]
if: ${{ !cancelled() && needs.changes.result == 'success' && needs.changes.outputs.evidence_postgres == 'true' }}
runs-on: ubuntu-24.04
timeout-minutes: 30
services:
postgres:
image: docker.io/library/postgres:17.11@sha256:67f41722b7a8cbdb868a44a4995c846eddfdc2973bccb291ce937dce88ad5675
env:
POSTGRES_DB: evidence
POSTGRES_USER: evidence
POSTGRES_PASSWORD: evidence_test
options: >-
--health-cmd "pg_isready -U evidence -d evidence"
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports:
- 5432/tcp
steps:
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
with:
persist-credentials: false
submodules: false
- name: Rust cache
uses: Swatinem/rust-cache@c8037a86dde8214a8310e9f54a8fc61ab362aa31 # v2
with:
shared-key: workspace-registry
cache-targets: false
save-if: ${{ github.ref == 'refs/heads/main' }}
- name: Verify the credential status ledger against PostgreSQL
env:
EVIDENCE_STATUS_TEST_DATABASE_URL: postgresql://evidence:evidence_test@localhost:${{ job.services.postgres.ports['5432'] }}/evidence
run: cargo test --locked -p registry-evidence --features postgres-test --test status_ledger --test status_transitions --test status_privacy
- name: Verify the OID4VCI store contract on both backends
env:
EVIDENCE_OID4VCI_TEST_DATABASE_URL: postgresql://evidence:evidence_test@localhost:${{ job.services.postgres.ports['5432'] }}/evidence
run: cargo test --locked -p registry-evidence-oid4vci --features postgres-test --test store_contract
discovery-contracts:
name: Registry Discovery product contracts
needs: changes
if: needs.changes.outputs.discovery_contracts == 'true'
runs-on: ubuntu-24.04
timeout-minutes: 40
steps:
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
with:
persist-credentials: false
submodules: false
- name: Setup Node
uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1
with:
node-version: 24.11.0
cache: npm
cache-dependency-path: docs/site/package-lock.json
- name: Cache Cargo registry
uses: Swatinem/rust-cache@c8037a86dde8214a8310e9f54a8fc61ab362aa31 # v2
with:
shared-key: workspace-registry
cache-targets: false
save-if: ${{ github.ref == 'refs/heads/main' }}
- name: Install uv
uses: astral-sh/setup-uv@07a047e608db84b3d8c06cd9a7c7dcc9c355c18e # v10.3.2
with:
version: "0.11.16"
- name: Registry Discovery contract consistency
run: products/discovery/scripts/check-contracts.sh
- name: Registry Discovery HTTP journeys
run: products/discovery/scripts/test-http.sh
- name: Install docs dependencies
working-directory: docs/site
# The tutorial runner needs only pure-JS packages. Skipping install
# scripts keeps the Vale postinstall's unauthenticated GitHub
# download, which shared runner addresses get refused on, out of
# this job.
run: npm ci --ignore-scripts
- name: Test the tutorial runner
working-directory: docs/site
run: npm run test:tutorial:runner
- name: Check tutorial command drift
working-directory: docs/site
run: npm run check:tutorial:discovery:dry-run
- name: Build the Registry Discovery binaries the tutorial runs
run: cargo build --locked -p registry-discovery -p registry-discoveryctl -p registry-rstack --bins
- name: Execute the Registry Discovery tutorial from a copy of the checkout
env:
DISCOVERY_BIN: ${{ github.workspace }}/target/debug/discovery
DISCOVERYCTL_BIN: ${{ github.workspace }}/target/debug/discoveryctl
RSTACK_BIN: ${{ github.workspace }}/target/debug/rstack
# The page serves on 127.0.0.1:38080 and 38090, which nothing on this
# runner holds, so the replay runs on the ports a reader gets.
run: node docs/site/scripts/run-tutorial.mjs --gate discovery
casework-postgres:
name: Casework PostgreSQL transactions (${{ matrix.part }})
needs: [changes, rust-policy]
if: ${{ !cancelled() && needs.changes.result == 'success' && needs.changes.outputs.casework_postgres == 'true' }}
runs-on: ubuntu-24.04
timeout-minutes: 30
strategy:
fail-fast: false
# The offline adopter proof and the database runtime journeys are
# independent gates, so they run as parallel parts instead of one
# serial job; both keep their own build of the authoring tools.
matrix:
part: [offline, runtime]
services:
postgres:
image: docker.io/library/postgres:17.11@sha256:67f41722b7a8cbdb868a44a4995c846eddfdc2973bccb291ce937dce88ad5675
env:
POSTGRES_DB: casework
POSTGRES_USER: casework
POSTGRES_PASSWORD: casework_test
options: >-
--health-cmd "pg_isready -U casework -d casework"
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports:
- 5432/tcp
steps:
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
with:
persist-credentials: false
submodules: false
- name: Rust cache
uses: Swatinem/rust-cache@c8037a86dde8214a8310e9f54a8fc61ab362aa31 # v2
with:
shared-key: workspace-registry
cache-targets: false
save-if: ${{ github.ref == 'refs/heads/main' }}
- name: Build the Casework authoring tool
run: cargo build --locked -p registry-caseworkctl -p registry-bregctl
- name: Verify Casework product contracts and offline authoring journeys
if: matrix.part == 'offline'
env:
CASEWORKCTL_BIN: ${{ github.workspace }}/target/debug/caseworkctl
BREGCTL_BIN: ${{ github.workspace }}/target/debug/bregctl
run: products/casework/scripts/check-checkpoint.sh
- name: Casework logical backup and restore of a submitted review
if: matrix.part == 'runtime'
# Drives the public dev/operator binaries against owned disposable
# PostgreSQL, preserving the backup point and append-only audit journal.
run: products/casework/scripts/test-backup-restore.sh
- name: Verify the rewritten Evidence deployment loads through the Evidence loader
if: matrix.part == 'runtime'
# The fixture machinery lives behind postgres-test, but this test needs
# neither a database nor an issuer. An inexact name filter that selects
# nothing still exits 0, so the step names the test exactly and fails
# unless it reports one pass.
shell: bash
run: |
set -euo pipefail
cargo test --locked -p registry-casework --features postgres-test --lib \
-- --exact task_grants::native_exchange_tests::the_rewritten_evidence_deployment_loads_through_the_evidence_loader \
| tee "${RUNNER_TEMP}/casework-evidence-fixture.log"
grep -q 'test result: ok\. 1 passed' "${RUNNER_TEMP}/casework-evidence-fixture.log" || { echo "::error::expected exactly one passing test for the_rewritten_evidence_deployment_loads_through_the_evidence_loader"; exit 1; }
- name: Verify review transactions, staffing, retention, HTTP and producer boundaries
if: matrix.part == 'runtime'
# Every maintained Casework target uses a unique schema. The service
# database is disposable; no test resets public or shares task state.
env:
CASEWORKCTL_BIN: ${{ github.workspace }}/target/debug/caseworkctl
BREGCTL_BIN: ${{ github.workspace }}/target/debug/bregctl
CASEWORK_REVIEW_TEST_DATABASE_URL: postgresql://casework:casework_test@localhost:${{ job.services.postgres.ports['5432'] }}/casework
CASEWORK_ACTIVATION_TEST_DATABASE_URL: postgresql://casework:casework_test@localhost:${{ job.services.postgres.ports['5432'] }}/casework
CASEWORK_ASSIGNMENT_TEST_DATABASE_URL: postgresql://casework:casework_test@localhost:${{ job.services.postgres.ports['5432'] }}/casework
BREG_TEST_DATABASE_URL: postgresql://casework:casework_test@localhost:${{ job.services.postgres.ports['5432'] }}/casework
run: |
cargo test --locked -p registry-casework --features postgres-test \
--test review_acceptance_postgres \
--test review_migration_postgres \
--test review_directory_postgres \
--test review_postgres \
--test review_http \
--test review_payment_fixture_postgres \
--test breg_review_journey
cargo test --locked -p registry-casework --features postgres-test --lib task_grants::
- name: Verify operator refusal reports and split runtime database roles
if: matrix.part == 'runtime'
# Exact filters and the pass count keep a renamed test from silently
# dropping the schema-newer and retained-session security proofs.
shell: bash
env:
CASEWORK_REVIEW_TEST_DATABASE_URL: postgresql://casework:casework_test@localhost:${{ job.services.postgres.ports['5432'] }}/casework
CASEWORK_TEST_DATABASE_URL: postgresql://casework:casework_test@localhost:${{ job.services.postgres.ports['5432'] }}/casework
run: |
set -euo pipefail
cargo test --locked -p registry-caseworkctl --features postgres-test,schema --lib \
-- --exact operator_tests::apply_reports_a_schema_newer_than_this_binary_with_its_own_refusal \
dev::tests::a_retained_split_session_start_leaves_the_ledgers_read_only_to_the_runtime \
| tee "${RUNNER_TEMP}/caseworkctl-apply.log"
grep -q 'test result: ok\. 2 passed' "${RUNNER_TEMP}/caseworkctl-apply.log"
- name: Create isolated task approval database
if: matrix.part == 'runtime'
env:
POSTGRES_CONTAINER_ID: ${{ job.services.postgres.id }}
run: docker exec "$POSTGRES_CONTAINER_ID" createdb -U casework casework_task_approval
- name: Casework task approval through stock issuer to Evidence, BReg, and Scheduling
if: matrix.part == 'runtime'
# An inexact name filter that selects nothing still exits 0, so the
# step names each test exactly and fails unless it reports one pass.
shell: bash
env:
CASEWORK_ASSIGNMENT_TEST_DATABASE_URL: postgresql://casework:casework_test@localhost:${{ job.services.postgres.ports['5432'] }}/casework_task_approval
BREG_TEST_DATABASE_URL: postgresql://casework:casework_test@localhost:${{ job.services.postgres.ports['5432'] }}/casework_task_approval
run: |
set -euo pipefail
cargo build --locked -p registry-scheduling \
--example scheduling-auth-probe
SCHEDULING_AUTH_PROBE_BIN="${GITHUB_WORKSPACE}/target/debug/examples/scheduling-auth-probe" \
cargo test --locked -p registry-casework \
--features postgres-test --lib \
-- --ignored --exact \
task_grants::native_exchange_tests::approved_casework_tasks_reach_evidence_breg_and_scheduling_through_stock_thunderid \
| tee "${RUNNER_TEMP}/casework-task-approval.log"
grep -q 'test result: ok\. 1 passed' "${RUNNER_TEMP}/casework-task-approval.log" || { echo "::error::expected exactly one passing test for approved_casework_tasks_reach_evidence_breg_and_scheduling_through_stock_thunderid"; exit 1; }
cargo build --locked -p registry-casework -p registry-caseworkctl
cargo test --locked -p registry-casework \
--features postgres-test --lib \
-- --ignored --exact \
task_grants::local_session_tests::submitted_review_dev_approves_exchanges_and_revokes_on_stock_issuer \
| tee "${RUNNER_TEMP}/casework-local-session.log"
grep -q 'test result: ok\. 1 passed' "${RUNNER_TEMP}/casework-local-session.log" || { echo "::error::expected exactly one passing test for submitted_review_dev_approves_exchanges_and_revokes_on_stock_issuer"; exit 1; }
scheduling-contracts:
name: Scheduling product contracts
needs: [changes, rust-policy]
if: ${{ !cancelled() && needs.changes.result == 'success' && needs.changes.outputs.scheduling_contracts == 'true' }}
runs-on: ubuntu-24.04
timeout-minutes: 20
steps:
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
with:
persist-credentials: false
submodules: false
- name: Cache Cargo registry
uses: Swatinem/rust-cache@c8037a86dde8214a8310e9f54a8fc61ab362aa31 # v2
with:
shared-key: workspace-registry
cache-targets: false
save-if: ${{ github.ref == 'refs/heads/main' }}
- name: Install uv
uses: astral-sh/setup-uv@07a047e608db84b3d8c06cd9a7c7dcc9c355c18e # v10.3.2
with:
version: "0.11.16"
- name: Scheduling contract consistency
run: uv run --no-project --with PyYAML==6.0.2 products/scheduling/scripts/check-contracts.sh
- name: Check Scheduling schemas against their generators
run: products/scheduling/scripts/check-schemas.sh
scheduling-postgres:
name: Scheduling PostgreSQL transactions
needs: [changes, rust-policy]
if: ${{ !cancelled() && needs.changes.result == 'success' && needs.changes.outputs.scheduling_postgres == 'true' }}
runs-on: ubuntu-24.04
timeout-minutes: 30
services:
postgres:
image: docker.io/library/postgres:17.11@sha256:67f41722b7a8cbdb868a44a4995c846eddfdc2973bccb291ce937dce88ad5675
env:
POSTGRES_DB: scheduling
POSTGRES_USER: scheduling
POSTGRES_PASSWORD: scheduling_test
options: >-
--health-cmd "pg_isready -U scheduling -d scheduling"
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports:
- 5432/tcp
steps:
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
with:
persist-credentials: false
submodules: false
- name: Rust cache
uses: Swatinem/rust-cache@c8037a86dde8214a8310e9f54a8fc61ab362aa31 # v2
with:
shared-key: workspace-registry
cache-targets: false
save-if: ${{ github.ref == 'refs/heads/main' }}
- name: Install uv
uses: astral-sh/setup-uv@07a047e608db84b3d8c06cd9a7c7dcc9c355c18e # v10.3.2
with:
version: "0.11.16"
- name: Build the Scheduling authoring tool
run: cargo build --locked -p registry-schedulingctl
- name: Verify Scheduling product contracts and offline authoring journeys
env:
SCHEDULINGCTL_BIN: ${{ github.workspace }}/target/debug/schedulingctl
run: uv run --no-project --with PyYAML==6.0.2 products/scheduling/scripts/check-checkpoint.sh
- name: Verify commitment ledger transactions
env:
SCHEDULING_TEST_DATABASE_URL: postgresql://scheduling:scheduling_test@localhost:${{ job.services.postgres.ports['5432'] }}/scheduling
run: cargo test --locked -p registry-scheduling --features postgres-test --test postgres_commitments
- name: Create isolated authoring-apply test database
env:
POSTGRES_CONTAINER_ID: ${{ job.services.postgres.id }}
run: docker exec "$POSTGRES_CONTAINER_ID" createdb -U scheduling scheduling_records_apply
- name: Verify offline authoring record application
env:
SCHEDULING_TEST_DATABASE_URL: postgresql://scheduling:scheduling_test@localhost:${{ job.services.postgres.ports['5432'] }}/scheduling_records_apply
run: cargo test --locked -p registry-schedulingctl --features postgres-test --test records_apply_postgres --test records_apply_busy_sources
- name: Create isolated delivery-intent test database
env:
POSTGRES_CONTAINER_ID: ${{ job.services.postgres.id }}
run: docker exec "$POSTGRES_CONTAINER_ID" createdb -U scheduling scheduling_intents
- name: Verify undelivered delivery intent reads
env:
SCHEDULING_TEST_DATABASE_URL: postgresql://scheduling:scheduling_test@localhost:${{ job.services.postgres.ports['5432'] }}/scheduling_intents
run: cargo test --locked -p registry-schedulingctl --features postgres-test --test intents_postgres
- name: Create isolated package-activation test database
env:
POSTGRES_CONTAINER_ID: ${{ job.services.postgres.id }}
run: docker exec "$POSTGRES_CONTAINER_ID" createdb -U scheduling scheduling_activation
- name: Verify package activation, role separation, and startup refusals
env:
SCHEDULING_TEST_DATABASE_URL: postgresql://scheduling:scheduling_test@localhost:${{ job.services.postgres.ports['5432'] }}/scheduling_activation
run: cargo test --locked -p registry-schedulingctl --features postgres-test --test activation_postgres
- name: Verify shared package activation primitives
env:
ACTIVATION_TEST_DATABASE_URL: postgresql://scheduling:scheduling_test@localhost:${{ job.services.postgres.ports['5432'] }}/scheduling_activation
run: cargo test --locked -p registry-platform-activation --features postgres-test --test postgres_activation
- name: Create isolated dispatch-core test database
env:
POSTGRES_CONTAINER_ID: ${{ job.services.postgres.id }}
run: docker exec "$POSTGRES_CONTAINER_ID" createdb -U scheduling dispatch
- name: Verify the shared dispatch core's fenced lease transitions
env:
DISPATCH_TEST_DATABASE_URL: postgresql://scheduling:scheduling_test@localhost:${{ job.services.postgres.ports['5432'] }}/dispatch
run: cargo test --locked -p registry-platform-dispatch --features postgres-test
- name: Create isolated hook-delivery test database
env:
POSTGRES_CONTAINER_ID: ${{ job.services.postgres.id }}
run: docker exec "$POSTGRES_CONTAINER_ID" createdb -U scheduling hooks
- name: Verify the shared hook delivery schema installs and reinstalls
# These tests are ignored without a database, and a run that selects
# nothing still exits 0, so the step fails unless at least one passes.
shell: bash
env:
HOOKS_TEST_DATABASE_URL: postgresql://scheduling:scheduling_test@localhost:${{ job.services.postgres.ports['5432'] }}/hooks
run: |
set -euo pipefail
cargo test --locked -p registry-platform-hooks --features postgres --lib \
-- --ignored \
| tee "${RUNNER_TEMP}/platform-hooks-postgres.log"
grep -Eq 'test result: ok\. [1-9][0-9]* passed' "${RUNNER_TEMP}/platform-hooks-postgres.log" || { echo "::error::expected at least one passing PostgreSQL test for registry-platform-hooks"; exit 1; }
messaging-contracts:
name: Messaging product contracts and vendor neutrality
needs: [changes, rust-policy]
if: ${{ !cancelled() && needs.changes.result == 'success' && needs.changes.outputs.messaging_contracts == 'true' }}
runs-on: ubuntu-24.04
timeout-minutes: 20
steps:
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
with:
persist-credentials: false
submodules: false
- name: Cache Cargo registry
uses: Swatinem/rust-cache@c8037a86dde8214a8310e9f54a8fc61ab362aa31 # v2
with:
shared-key: workspace-registry
cache-targets: false
save-if: ${{ github.ref == 'refs/heads/main' }}
- name: Install uv
uses: astral-sh/setup-uv@07a047e608db84b3d8c06cd9a7c7dcc9c355c18e # v10.3.2
with:
version: "0.11.16"
- name: Messaging contract consistency
run: uv run --no-project --with PyYAML==6.0.2 products/messaging/scripts/check-contracts.sh
- name: Self-test the Messaging vendor-neutrality gate
run: products/messaging/scripts/test-check-vendor-neutrality.sh
- name: Enforce Messaging vendor neutrality
run: products/messaging/scripts/check-vendor-neutrality.sh
messaging-postgres:
name: Messaging PostgreSQL runtime
needs: [changes, rust-policy]
if: ${{ !cancelled() && needs.changes.result == 'success' && needs.changes.outputs.messaging_postgres == 'true' }}
runs-on: ubuntu-24.04
timeout-minutes: 30
services:
postgres:
image: docker.io/library/postgres:17.11@sha256:67f41722b7a8cbdb868a44a4995c846eddfdc2973bccb291ce937dce88ad5675
env:
POSTGRES_DB: messaging
POSTGRES_USER: messaging
POSTGRES_PASSWORD: messaging_test
options: >-
--health-cmd "pg_isready -U messaging -d messaging"
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports:
- 5432/tcp
steps:
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
with:
persist-credentials: false
submodules: false
- name: Rust cache
uses: Swatinem/rust-cache@c8037a86dde8214a8310e9f54a8fc61ab362aa31 # v2
with:
shared-key: workspace-registry
cache-targets: false
save-if: ${{ github.ref == 'refs/heads/main' }}
- name: Install uv
uses: astral-sh/setup-uv@07a047e608db84b3d8c06cd9a7c7dcc9c355c18e # v10.3.2
with:
version: "0.11.16"
- name: Build the Messaging operator tool
run: cargo build --locked -p registry-messagingctl
- name: Verify Messaging product contracts and offline configuration checks
env:
MESSAGINGCTL_BIN: ${{ github.workspace }}/target/debug/messagingctl
run: uv run --no-project --with PyYAML==6.0.2 products/messaging/scripts/check-checkpoint.sh
- name: Verify migrations, readiness, and the served runtime
env:
MESSAGING_TEST_DATABASE_URL: postgresql://messaging:messaging_test@localhost:${{ job.services.postgres.ports['5432'] }}/messaging
run: cargo test --locked -p registry-messaging --features postgres-test --test postgres_migrate
- name: Verify package activation and template stability
env:
MESSAGING_TEST_DATABASE_URL: postgresql://messaging:messaging_test@localhost:${{ job.services.postgres.ports['5432'] }}/messaging
run: cargo test --locked -p registry-messaging --features postgres-test --test postgres_package
- name: Verify message submission, visibility, and cancellation routes
env:
MESSAGING_TEST_DATABASE_URL: postgresql://messaging:messaging_test@localhost:${{ job.services.postgres.ports['5432'] }}/messaging
run: cargo test --locked -p registry-messaging --features postgres-test --test postgres_messages
- name: Verify dispatch worker outcomes and settlement
env:
MESSAGING_TEST_DATABASE_URL: postgresql://messaging:messaging_test@localhost:${{ job.services.postgres.ports['5432'] }}/messaging
run: cargo test --locked -p registry-messaging --features postgres-test --test postgres_dispatch
- name: Verify provider callbacks and delivery reports
env:
MESSAGING_TEST_DATABASE_URL: postgresql://messaging:messaging_test@localhost:${{ job.services.postgres.ports['5432'] }}/messaging
run: cargo test --locked -p registry-messaging --features postgres-test --test postgres_callbacks
- name: Verify retention erasure
env:
MESSAGING_TEST_DATABASE_URL: postgresql://messaging:messaging_test@localhost:${{ job.services.postgres.ports['5432'] }}/messaging
run: cargo test --locked -p registry-messaging --features postgres-test --test postgres_retention
- name: Verify messagingctl messages commands
env:
MESSAGING_TEST_DATABASE_URL: postgresql://messaging:messaging_test@localhost:${{ job.services.postgres.ports['5432'] }}/messaging
run: cargo test --locked -p registry-messagingctl --features postgres-test --tests
- name: Send one email and one SMS through messagingctl dev
env:
MESSAGINGCTL_BIN: ${{ github.workspace }}/target/debug/messagingctl
run: products/messaging/scripts/test-dev.sh
documents-contracts:
name: Document Store product contracts
needs: [changes, rust-policy]
if: ${{ !cancelled() && needs.changes.result == 'success' && needs.changes.outputs.documents_contracts == 'true' }}
runs-on: ubuntu-24.04
timeout-minutes: 20
steps:
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
with:
persist-credentials: false
submodules: false
- name: Install uv
uses: astral-sh/setup-uv@07a047e608db84b3d8c06cd9a7c7dcc9c355c18e # v10.3.2
with:
version: "0.11.16"
- name: Document Store contract consistency
run: uv run --no-project --with PyYAML==6.0.2 products/documents/scripts/check-contracts.sh
documents-postgres:
name: Document Store PostgreSQL runtime
needs: [changes, rust-policy]
if: ${{ !cancelled() && needs.changes.result == 'success' && needs.changes.outputs.documents_postgres == 'true' }}
runs-on: ubuntu-24.04
timeout-minutes: 30
services:
postgres:
image: docker.io/library/postgres:17.11@sha256:67f41722b7a8cbdb868a44a4995c846eddfdc2973bccb291ce937dce88ad5675
env:
POSTGRES_DB: documents
POSTGRES_USER: documents
POSTGRES_PASSWORD: documents_test
options: >-
--health-cmd "pg_isready -U documents -d documents"
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports:
- 5432/tcp
steps:
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
with:
persist-credentials: false
submodules: false
- name: Rust cache
uses: Swatinem/rust-cache@c8037a86dde8214a8310e9f54a8fc61ab362aa31 # v2
with:
shared-key: workspace-registry
cache-targets: false
save-if: ${{ github.ref == 'refs/heads/main' }}
- name: Install uv
uses: astral-sh/setup-uv@07a047e608db84b3d8c06cd9a7c7dcc9c355c18e # v10.3.2
with:
version: "0.11.16"
- name: Build the Document Store operator tool
run: cargo build --locked -p registry-documentsctl
- name: Verify Document Store product contracts and offline configuration checks
env:
DOCUMENTSCTL_BIN: ${{ github.workspace }}/target/debug/documentsctl
run: uv run --no-project --with PyYAML==6.0.2 products/documents/scripts/check-checkpoint.sh
- name: Verify database readiness, activation, the served runtime, and S3 storage
env:
DOCUMENTS_TEST_DATABASE_URL: postgresql://documents:documents_test@localhost:${{ job.services.postgres.ports['5432'] }}/documents
run: cargo test --locked -p registry-documents --features postgres-test --test postgres_startup --test postgres_operator --test postgres_documents --test postgres_s3
- name: Verify the documentsctl activation verbs against PostgreSQL
env:
DOCUMENTS_TEST_DATABASE_URL: postgresql://documents:documents_test@localhost:${{ job.services.postgres.ports['5432'] }}/documents
run: cargo test --locked -p registry-documentsctl --features postgres-test --test postgres_cli
- name: Build Registry Render for the golden journey
run: cargo build --locked -p registry-render
- name: Verify the golden journey from a rendered document to an expired share
env:
DOCUMENTS_TEST_DATABASE_URL: postgresql://documents:documents_test@localhost:${{ job.services.postgres.ports['5432'] }}/documents
REGISTRY_RENDER_BIN: ${{ github.workspace }}/target/debug/registry-render
run: cargo test --locked -p registry-documents --features postgres-test --test postgres_journey
- name: Verify document events are captured on commit and delivered to observers
env:
DOCUMENTS_TEST_DATABASE_URL: postgresql://documents:documents_test@localhost:${{ job.services.postgres.ports['5432'] }}/documents
run: cargo test --locked -p registry-documents --features postgres-test --test postgres_hooks
adapter-contracts:
name: Registry Adapter product contracts
needs: [changes, rust-policy]
if: ${{ !cancelled() && needs.changes.result == 'success' && needs.changes.outputs.adapter_contracts == 'true' }}
runs-on: ubuntu-24.04
# The script formats, lints, and tests the adapter crates, holds the
# generated schemas and the configuration conventions against their
# generators, holds the Camel port table against the code, and checks
# the example, its fixture replay, the source-shape fixtures, and the
# bad-authoring corpus against the adapterctl binary it builds. All
# of it compiles cold: CARGO_TARGET_DIR is set because the script
# builds its own adapterctl there and refuses to guess a binary
# without it.
timeout-minutes: 40
steps:
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
with:
persist-credentials: false
submodules: false
- name: Cache Cargo registry
uses: Swatinem/rust-cache@c8037a86dde8214a8310e9f54a8fc61ab362aa31 # v2
with:
shared-key: workspace-registry
cache-targets: false
save-if: ${{ github.ref == 'refs/heads/main' }}
- name: Install uv
uses: astral-sh/setup-uv@07a047e608db84b3d8c06cd9a7c7dcc9c355c18e # v10.3.2
with:
version: "0.11.16"
- name: Registry Adapter contract consistency
env:
CARGO_TARGET_DIR: ${{ github.workspace }}/target
ADAPTER_CONTRACTS_CLEAN_BETWEEN_LANES: "1"
run: products/adapter/scripts/check-contracts.sh
- name: Registry Adapter facade recovery runner
run: products/adapter/scripts/test-facade-recovery-runner.sh
adapter-postgres:
name: Registry Adapter PostgreSQL and recovery suites
needs: [changes, rust-policy]
if: ${{ !cancelled() && needs.changes.result == 'success' && needs.changes.outputs.adapter_contracts == 'true' }}
runs-on: ubuntu-24.04
# The scripts start their own disposable PostgreSQL containers: the
# ingestion-job recovery suite and the live PostgreSQL source suite.
# They are a separate job from the contract script so that each keeps
# its own 40-minute budget and a timeout names the suite that ran long.
timeout-minutes: 40
steps:
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
with:
persist-credentials: false
submodules: false
- name: Cache Cargo registry
uses: Swatinem/rust-cache@c8037a86dde8214a8310e9f54a8fc61ab362aa31 # v2
with:
shared-key: workspace-registry
cache-targets: false
save-if: ${{ github.ref == 'refs/heads/main' }}
- name: Registry Adapter ingestion-job PostgreSQL recovery
env:
CARGO_TARGET_DIR: ${{ github.workspace }}/target
run: products/adapter/scripts/test-job-postgres.sh
# The GovStack BReg suite starts `bregctl dev`, which pulls the pinned
# ThunderID image on first use. Pulling it here, with retries, makes a
# registry hiccup fail as this step instead of as a test.
- name: Pull the pinned ThunderID image
run: |
image=$(jq -r .image crates/registry-thunderid-tooling/thunderid-version.json)
for attempt in 1 2 3; do
if docker pull --quiet "$image"; then
exit 0
fi
echo "::warning::ThunderID image pull attempt ${attempt} failed"
sleep $((attempt * 15))
done
echo "::error::the pinned ThunderID image could not be pulled after 3 attempts"
exit 1
- name: Registry Adapter live PostgreSQL suites
env:
CARGO_TARGET_DIR: ${{ github.workspace }}/target
run: products/adapter/scripts/test-postgres.sh
coordinator-postgres:
name: Coordinator PostgreSQL runtime
needs: [changes, rust-policy]
if: ${{ !cancelled() && needs.changes.result == 'success' && needs.changes.outputs.coordinator == 'true' }}
runs-on: ubuntu-24.04
timeout-minutes: 30
services:
postgres:
image: docker.io/library/postgres:17.11@sha256:67f41722b7a8cbdb868a44a4995c846eddfdc2973bccb291ce937dce88ad5675
env:
POSTGRES_DB: coordinator
POSTGRES_USER: coordinator
POSTGRES_PASSWORD: coordinator_test
options: >-
--health-cmd "pg_isready -U coordinator -d coordinator"
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports:
- 5432/tcp
steps:
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
with:
persist-credentials: false
submodules: false
- name: Rust cache
uses: Swatinem/rust-cache@c8037a86dde8214a8310e9f54a8fc61ab362aa31 # v2
with:
shared-key: workspace-registry
cache-targets: false
save-if: ${{ github.ref == 'refs/heads/main' }}
# The suite composes with real Messaging and Scheduling runtimes, which
# each need a database of their own beside the Coordinator's.
- name: Prepare the product test databases
env:
PGHOST: 127.0.0.1
PGPORT: ${{ job.services.postgres.ports['5432'] }}
PGUSER: coordinator
PGPASSWORD: coordinator_test
run: |
createdb coordinator_messaging
createdb coordinator_scheduling
- name: Verify coordinator restart, fencing, and native product recovery
env:
COORDINATOR_TEST_DATABASE_URL: postgresql://coordinator:coordinator_test@127.0.0.1:${{ job.services.postgres.ports['5432'] }}/coordinator
COORDINATOR_MESSAGING_TEST_DATABASE_URL: postgresql://coordinator:coordinator_test@127.0.0.1:${{ job.services.postgres.ports['5432'] }}/coordinator_messaging
COORDINATOR_SCHEDULING_TEST_DATABASE_URL: postgresql://coordinator:coordinator_test@127.0.0.1:${{ job.services.postgres.ports['5432'] }}/coordinator_scheduling
run: products/coordinator/scripts/test-postgres.sh
messaging-smtp:
name: Messaging SMTP provider
needs: [changes, rust-policy]
if: ${{ !cancelled() && needs.changes.result == 'success' && needs.changes.outputs.messaging_postgres == 'true' }}
runs-on: ubuntu-24.04
timeout-minutes: 20
services:
mailpit:
image: ghcr.io/axllent/mailpit:v1.31.2@sha256:74d609a42ec279aa63c6b4622a6fa9b5408d1ad5b1d76a1c4be40a265ce0863d
options: >-
--health-cmd "/mailpit readyz"
--health-interval 5s
--health-timeout 5s
--health-retries 5
ports:
- 1025/tcp
- 8025/tcp
steps:
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
with:
persist-credentials: false
submodules: false
- name: Rust cache
uses: Swatinem/rust-cache@c8037a86dde8214a8310e9f54a8fc61ab362aa31 # v2
with:
shared-key: workspace-registry
cache-targets: false
save-if: ${{ github.ref == 'refs/heads/main' }}
- name: Verify the SMTP provider against a real relay
env:
MESSAGING_SMTP_TEST_MAILPIT_SMTP: 127.0.0.1:${{ job.services.mailpit.ports['1025'] }}
MESSAGING_SMTP_TEST_MAILPIT_API: http://127.0.0.1:${{ job.services.mailpit.ports['8025'] }}
run: cargo test --locked -p registry-messaging --features smtp-test --test smtp_mailpit
breg-contracts:
name: Base Registry Engine product contracts (${{ matrix.lane }})
needs: changes
if: needs.changes.outputs.breg_contracts == 'true'
runs-on: ubuntu-24.04
timeout-minutes: 30
strategy:
fail-fast: false
matrix:
# Review runs the validator and adopter-journeys lanes; the merge
# queue, the nightly sweep and a ci:full pull request add the
# parallel PostgreSQL shard lanes.
lane: ${{ fromJSON(needs.changes.outputs.breg_contracts_lanes) }}
services:
postgres:
# PostgreSQL 17 / PostGIS 3.5.2, verified 2026-08-31. Fresh test databases
# remain extension-free unless a spatial test explicitly installs it.
image: postgis/postgis@sha256:01a6a70e41e6c4467c8f55f6063555ed72db2d6662cd0d571040d42eadaeb6f6
env:
POSTGRES_DB: breg
POSTGRES_PASSWORD: breg_test
POSTGRES_USER: breg
options: >-
--health-cmd "pg_isready -U breg -d breg"
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports:
- 5432/tcp
steps:
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
with:
persist-credentials: false
submodules: false
- name: Configure Base Registry Engine test environment
env:
DATABASE_PORT: ${{ job.services.postgres.ports['5432'] }}
POSTGRES_CONTAINER_ID: ${{ job.services.postgres.id }}
TLS_CA_PEM_PATH: ${{ runner.temp }}/breg-postgres-trusted-ca.pem
run: |
{
echo "BREG_TEST_DATABASE_URL=postgresql://breg:breg_test@localhost:${DATABASE_PORT}/breg"
echo "BREG_TEST_TLS_DATABASE_HOST=localhost"
echo "BREG_TEST_TLS_DATABASE_URL=postgresql://breg:breg_test@localhost:${DATABASE_PORT}/breg"
echo "BREG_TEST_TLS_HOSTNAME_MISMATCH_DATABASE_URL=postgresql://breg:breg_test@127.0.0.1:${DATABASE_PORT}/breg"
echo "BREG_TEST_TLS_POSTGRES_CONTAINER_ID=${POSTGRES_CONTAINER_ID}"
echo "BREG_TEST_TLS_CA_PEM_PATH=${TLS_CA_PEM_PATH}"
} >> "${GITHUB_ENV}"
- name: Cache Cargo registry
uses: Swatinem/rust-cache@c8037a86dde8214a8310e9f54a8fc61ab362aa31 # v2
with:
shared-key: workspace-registry
cache-targets: false
save-if: ${{ github.ref == 'refs/heads/main' }}
- name: Install uv
if: matrix.lane != 'immediate-actions'
uses: astral-sh/setup-uv@07a047e608db84b3d8c06cd9a7c7dcc9c355c18e # v10.3.2
with:
version: "0.11.16"
- name: Base Registry Engine contract consistency
if: matrix.lane == 'contracts'
run: uv run --no-project --with PyYAML==6.0.2 products/breg/scripts/check-contracts.sh
# No shard enables `schema` on registry-breg, so its schema-gated tests
# run here, where check-contracts.sh has already built the dependencies
# of this feature set.
- name: Base Registry Engine schema-gated tests
if: matrix.lane == 'contracts'
run: cargo test --locked -p registry-breg --features runtime,tooling,schema --lib --test action_evidence_compiler --test proposal_hooks --test value_type_consistency
- name: BReg client contract consistency
if: matrix.lane == 'contracts'
run: products/breg/scripts/check-client-contract.sh
- name: Enforce the citizen gateway's delegated-agent boundary
if: matrix.lane == 'contracts'
run: products/breg/scripts/check-mcp-gateway-boundary.sh
- name: Keep the citizen gateway and review page off the registry runtime
if: matrix.lane == 'contracts'
run: products/breg/scripts/check-service-dependencies.sh
- name: Base Registry Engine PostgreSQL journeys
if: startsWith(matrix.lane, 'postgres:') || matrix.lane == 'immediate-actions'
env:
BREG_POSTGRES_LANE: ${{ matrix.lane }}
run: products/breg/scripts/test-postgres.sh --lane "$BREG_POSTGRES_LANE"
# The fixture provisions its own databases and roles through the job's
# BREG_TEST_DATABASE_URL. postgres-test enables its loopback plaintext
# database. Each single-copy proof is pinned to one PostgreSQL shard so
# it still executes exactly once while balancing shard wall time: the
# first shard carries the Evidence proof's build, the second the citizen
# journey, the third the worker and S3 runs.
- name: Native citizen gateway and review authority journey
if: matrix.lane == 'postgres:2/3'
shell: bash
env:
BREG_BIN: ${{ github.workspace }}/target/debug/breg
BREGCTL_BIN: ${{ github.workspace }}/target/debug/bregctl
BREG_REVIEW_NATIVE_BIN: ${{ github.workspace }}/target/debug/breg-review
run: |
cargo build --locked -p registry-breg -p registry-bregctl -p registry-breg-review \
--features registry-breg/postgres-test --bin breg --bin bregctl --bin breg-review
cargo test --locked -p registry-breg-mcp --test native_citizen \
-- --ignored --exact native_citizen_gateway_and_review_share_engine_owned_authority --test-threads=1 \
| tee "${RUNNER_TEMP}/breg-native-citizen.log"
grep -q 'test result: ok\. 1 passed' "${RUNNER_TEMP}/breg-native-citizen.log" || { echo "::error::expected one passing native citizen authority journey"; exit 1; }
- name: Native delegated staff MCP authority journey
if: matrix.lane == 'postgres:2/3'
shell: bash
env:
BREG_BIN: ${{ github.workspace }}/target/debug/breg
BREGCTL_BIN: ${{ github.workspace }}/target/debug/bregctl
CASEWORK_BIN: ${{ github.workspace }}/target/debug/casework
CASEWORKCTL_BIN: ${{ github.workspace }}/target/debug/caseworkctl
REGISTRY_STACK_FIXTURES_ROOT: ${{ github.workspace }}
run: |
set -euo pipefail
export CASEWORK_MCP_TEST_DATABASE_URL="$BREG_TEST_DATABASE_URL"
export TMPDIR="${RUNNER_TEMP}/native-staff-mcp"
mkdir -p "$TMPDIR"
chmod 700 "$TMPDIR"
cargo build --locked -p registry-breg -p registry-bregctl -p registry-casework -p registry-caseworkctl \
--features registry-breg/postgres-test,registry-casework/postgres-test \
--bin breg --bin bregctl --bin casework --bin caseworkctl
cargo test --locked -p registry-mcp --test native_casework \
-- --ignored --exact native_staff_gateway_preserves_delegated_read_authority --test-threads=1 \
| tee "${RUNNER_TEMP}/native-staff-mcp.log"
grep -q 'test result: ok\. 1 passed' "${RUNNER_TEMP}/native-staff-mcp.log" || { echo "::error::expected one passing native staff authority journey"; exit 1; }
# The native worker/store library and test-only startup configuration
# need postgres-test; keep the production-feature variants in the runner.
# External-issuer cases compile here and execute only in their opt-in runner.
- name: Base Registry Engine PostgreSQL worker and startup tests
if: matrix.lane == 'postgres:3/3'
run: cargo test --locked -p registry-breg --features postgres-test,tooling,schema --lib --test startup_http --test runtime_config --test issuer_portability -- --test-threads=1
# The transactional harness uses its own roles over the initial local
# connection. Run it before the TLS proof makes this service TLS-only.
# Both steps share the contracts-journeys lane, which owns the
# TLS-dependent adopter checks the validator lane does not run.
- name: Base Registry Engine immediate Action transaction and recovery
if: matrix.lane == 'contracts-journeys'
run: products/breg/scripts/test-immediate-action-examples.sh
- name: Base Registry Engine PostgreSQL TLS proof
if: matrix.lane == 'contracts-journeys'
run: products/breg/scripts/test-postgres-tls.sh
- name: Public bregctl schema-test lifecycles
if: matrix.lane == 'contracts-journeys'
env:
BREG_TEST_DATABASE_URL: ${{ env.BREG_TEST_TLS_DATABASE_URL }}
SSL_CERT_FILE: ${{ env.BREG_TEST_TLS_CA_PEM_PATH }}
run: cargo test --locked -p registry-bregctl --features wasm --test wasm_test_lifecycle --test publicschema_inline_value_lists -- --ignored --exact
- name: Base Registry Engine real S3 attachment storage
if: matrix.lane == 'postgres:3/3'
run: bash products/breg/scripts/test-attachment-s3.sh
- name: Base Registry Engine clean-checkout adopter workflow
if: matrix.lane == 'contracts-journeys'
run: products/breg/scripts/test-adopter-workflow.sh
- name: Base Registry Engine statistical dataset workflow
if: matrix.lane == 'contracts-journeys'
env:
BREG_SKIP_BUILD: "1"
run: products/breg/scripts/test-statistics-workflow.sh
- name: Base Registry Engine staging-to-production promotion
if: matrix.lane == 'contracts-journeys'
env:
BREG_SKIP_BUILD: "1"
run: products/breg/scripts/test-promotion.sh
- name: Base Registry Engine logical backup and restore
if: matrix.lane == 'contracts-journeys'
env:
BREG_SKIP_BUILD: "1"
run: products/breg/scripts/test-backup-restore.sh
- name: Base Registry Engine generic quickstart
if: matrix.lane == 'contracts-journeys'
run: products/breg/quickstart/run.sh --smoke
- name: Base Registry Engine spatial quickstart
if: matrix.lane == 'contracts-journeys'
run: products/breg/quickstart/run.sh --spatial --smoke
breg-postgres-18:
name: Base Registry Engine PostgreSQL 18 portability
needs: changes
if: needs.changes.outputs.breg_contracts == 'true'
runs-on: ubuntu-24.04
timeout-minutes: 15
steps:
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
with:
persist-credentials: false
submodules: false
- name: Check optional Docker Hub credentials
id: dockerhub
if: github.event_name != 'pull_request' && (github.event_name != 'workflow_dispatch' || github.ref == 'refs/heads/main')
env:
DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
DOCKERHUB_READ_TOKEN: ${{ secrets.DOCKERHUB_READ_TOKEN }}
shell: bash
run: |
set -euo pipefail
if [[ -z "${DOCKERHUB_USERNAME}" && -z "${DOCKERHUB_READ_TOKEN}" ]]; then
printf 'configured=false\n' >> "${GITHUB_OUTPUT}"
elif [[ -z "${DOCKERHUB_USERNAME}" || -z "${DOCKERHUB_READ_TOKEN}" ]]; then
echo 'Docker Hub credentials must configure both username and read token.' >&2
exit 1
else
printf 'configured=true\n' >> "${GITHUB_OUTPUT}"
fi
- name: Log in to Docker Hub for the PostgreSQL 18 image
if: github.event_name != 'pull_request' && (github.event_name != 'workflow_dispatch' || github.ref == 'refs/heads/main') && steps.dockerhub.outputs.configured == 'true'
uses: docker/login-action@81c6f93d4bc991e510b342a00b419b3bb4efc5c6 # v4.7.0
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_READ_TOKEN }}
- name: Start PostgreSQL 18
id: postgres
shell: bash
run: |
set -euo pipefail
image='docker.io/library/postgres:18.4@sha256:a02db8cac496f15b094798a38254f14d6e00741f709360e5e00bb6668ea31636'
container_name="breg-postgres-18-${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}"
container_id="$(
docker run --detach \
--name "${container_name}" \
--label org.registrystack.ci.postgres-service=true \
--env POSTGRES_DB=breg \
--env POSTGRES_USER=breg \
--env POSTGRES_PASSWORD=breg_test \
--health-cmd 'pg_isready -U breg -d breg' \
--health-interval 10s \
--health-timeout 5s \
--health-retries 5 \
--publish 127.0.0.1::5432 \
"${image}"
)"
printf 'container_id=%s\n' "${container_id}" >> "${GITHUB_OUTPUT}"
for _ in $(seq 1 30); do
health="$(docker inspect --format '{{.State.Health.Status}}' "${container_id}")"
if [[ "${health}" == healthy ]]; then
break
fi
if [[ "${health}" == unhealthy ]]; then
docker logs "${container_id}"
exit 1
fi
sleep 2
done
if [[ "$(docker inspect --format '{{.State.Health.Status}}' "${container_id}")" != healthy ]]; then
docker logs "${container_id}"
exit 1
fi
endpoint="$(docker port "${container_id}" 5432/tcp)"
port="${endpoint##*:}"
[[ "${port}" =~ ^[0-9]+$ ]]
printf 'port=%s\n' "${port}" >> "${GITHUB_OUTPUT}"
- name: Cache Cargo registry
uses: Swatinem/rust-cache@c8037a86dde8214a8310e9f54a8fc61ab362aa31 # v2
with:
shared-key: workspace-registry
cache-targets: false
save-if: ${{ github.ref == 'refs/heads/main' }}
- name: Verify cross-major schema fingerprint and collation
env:
BREG_TEST_DATABASE_URL: postgresql://breg:breg_test@localhost:${{ steps.postgres.outputs.port }}/breg
run: |
cargo test --locked -p registry-breg --features postgres-test,tooling \
--test schema_fingerprint_rehearsal --no-fail-fast -- --test-threads=1
- name: Stop PostgreSQL 18
if: always() && steps.postgres.outputs.container_id != ''
env:
CONTAINER_ID: ${{ steps.postgres.outputs.container_id }}
shell: bash
run: |
set -euo pipefail
label="$(
docker inspect \
--format '{{ index .Config.Labels "org.registrystack.ci.postgres-service" }}' \
"${CONTAINER_ID}"
)"
[[ "${label}" == true ]]
docker rm --force "${CONTAINER_ID}"
breg-wasm:
# The WASM action-handler executor runs under the `wasm` feature, which
# registry-breg and registry-bregctl carry by default. This lane runs
# what the ordinary Rust shards do not: the platform executor's wat
# suite (both execution backends), the admission, runtime, and parity
# suites in one explicit place, the PostgreSQL handler journeys against
# a disposable PostGIS database, and the no-default refusal suite that
# keeps `breg.action.handler-wasm-build-unsupported` and
# `breg.hook.handler-wasm-build-unsupported` proven.
name: Base Registry Engine WASM executor
needs: changes
if: needs.changes.outputs.breg_integration == 'true'
runs-on: ubuntu-24.04
timeout-minutes: 30
services:
postgres:
# PostgreSQL 17 / PostGIS 3.5.2, verified 2026-08-31. Fresh test databases
# remain extension-free unless a spatial test explicitly installs it.
image: postgis/postgis@sha256:01a6a70e41e6c4467c8f55f6063555ed72db2d6662cd0d571040d42eadaeb6f6
env:
POSTGRES_DB: breg
POSTGRES_PASSWORD: breg_test
POSTGRES_USER: breg
options: >-
--health-cmd "pg_isready -U breg -d breg"
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports:
- 5432/tcp
steps:
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
with:
persist-credentials: false
submodules: false
- name: Cache Cargo registry
uses: Swatinem/rust-cache@c8037a86dde8214a8310e9f54a8fc61ab362aa31 # v2
with:
shared-key: workspace-registry
cache-targets: false
save-if: ${{ github.ref == 'refs/heads/main' }}
- name: Platform WASM executor wat suite
run: cargo test --locked -p registry-platform-script --features wasm --test wasm_executor_wat
- name: Base Registry Engine WASM admission, runtime, and parity suites
run: cargo test --locked -p registry-breg --features runtime,wasm --lib --test wasm_handler_admission
- name: Base Registry Engine WASM refusal suite
run: cargo test --locked -p registry-breg --no-default-features --features runtime,tooling --test immediate_action_compiler --test hook_declaration
- name: Base Registry Engine tooling WASM suites
run: cargo test --locked -p registry-bregctl --features wasm
- name: Base Registry Engine WASM handler PostgreSQL journeys
env:
BREG_TEST_DATABASE_URL: postgresql://breg:breg_test@localhost:${{ job.services.postgres.ports['5432'] }}/breg
run: cargo test --locked -p registry-breg --features postgres-test,tooling,wasm --test postgres_wasm_action_handlers
identifiers:
name: Public identifier catalog
needs:
- changes
- rust-policy
if: ${{ !cancelled() && needs.changes.result == 'success' && needs.changes.outputs.identifiers == 'true' }}
runs-on: ubuntu-24.04
timeout-minutes: 20
steps:
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
with:
persist-credentials: false
submodules: false
- name: Cache Cargo registry
uses: Swatinem/rust-cache@c8037a86dde8214a8310e9f54a8fc61ab362aa31 # v2
with:
shared-key: workspace-registry
cache-targets: false
save-if: ${{ github.ref == 'refs/heads/main' }}
- name: Check public identifier catalog
run: products/identifiers/scripts/check.sh
- name: Check Registry Record profile artifacts
run: products/registry-record/scripts/check.sh
config-conformance:
name: Runtime configuration conformance
needs:
- changes
- rust-policy
if: ${{ !cancelled() && needs.changes.result == 'success' && needs.changes.outputs.config_conformance == 'true' }}
runs-on: ubuntu-24.04
# The check commands and the BReg citizen service schema check compile
# their crates cold.
timeout-minutes: 40
steps:
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
with:
# The conventions lint compares the exceptions register with the
# event baseline (CFG-CHANGE-5), so the history has to be present.
fetch-depth: 0
persist-credentials: false
submodules: false
- name: Cache Cargo registry
uses: Swatinem/rust-cache@c8037a86dde8214a8310e9f54a8fc61ab362aa31 # v2
with:
shared-key: workspace-registry
cache-targets: false
save-if: ${{ github.ref == 'refs/heads/main' }}
- name: Test runtime configuration conformance gate
run: python3 -m unittest products/platform/scripts/test_check_config_conformance.py
- name: Check runtime configuration conformance
run: products/platform/scripts/check-config-conformance.py --check-generated
- name: Check Coordinator configuration schemas
run: products/coordinator/scripts/check-schemas.sh
- name: Check BReg citizen service runtime schemas
run: cargo test --locked -p registry-breg-mcp -p registry-breg-review --features registry-breg-mcp/schema,registry-breg-review/schema --lib schema::tests
- name: Install uv
uses: astral-sh/setup-uv@07a047e608db84b3d8c06cd9a7c7dcc9c355c18e # v10.3.2
with:
version: "0.11.16"
- name: Test configuration conventions lint
run: uv run --no-project --with PyYAML==6.0.2 python -m unittest products/platform/scripts/test_check_config_conventions.py
- name: Check configuration conventions
env:
# The event baseline the changes job resolved; the archive check
# compares against the same commit.
CONVENTIONS_BASE_REF: ${{ needs.changes.outputs.archive_base_ref }}
CONVENTIONS_COMPARISON_REQUIRED: ${{ needs.changes.outputs.archive_comparison_required }}
shell: bash
run: |
set -euo pipefail
lint=(uv run --no-project --with PyYAML==6.0.2 python products/platform/scripts/check-config-conventions.py)
if [[ "${CONVENTIONS_COMPARISON_REQUIRED}" == "true" ]]; then
if [[ -z "${CONVENTIONS_BASE_REF}" ]]; then
echo "Cannot check CFG-CHANGE-5: the event baseline is unavailable." >&2
exit 1
fi
register=products/platform/config-conventions-exceptions.yaml
if git cat-file -e "${CONVENTIONS_BASE_REF}:${register}" 2>/dev/null; then
"${lint[@]}" --base "${CONVENTIONS_BASE_REF}"
else
# A baseline that predates the register (the change that
# introduces it) has nothing to compare with, and the lint
# refuses an explicit --base without the register.
echo "::notice::CFG-CHANGE-5 not checked: the event baseline predates ${register}."
"${lint[@]}"
fi
elif [[ "${CONVENTIONS_COMPARISON_REQUIRED}" == "false" ]]; then
# A scheduled sweep has no change baseline; the lint compares the
# register with its merge base with origin/main.
"${lint[@]}"
else
echo "The event comparison policy is unavailable." >&2
exit 1
fi
# Every rule the convention declares has one Enforcement summary
# row, and the gates that row names exist.
"${lint[@]}" --rule-coverage
- name: Test configuration conformance corpus runner
run: uv run --no-project --with PyYAML==6.0.2 python -m unittest products/platform/scripts/test_run_config_conformance.py
# The binaries the registered `check` commands name; a cell whose
# program is missing from the directory fails.
- name: Build configuration check commands
run: >-
cargo build --locked --bins
-p registry-bregctl -p registry-caseworkctl -p registry-schedulingctl
-p registry-coordinator
-p registry-messagingctl -p registry-discoveryctl -p registry-render
-p registry-documentsctl -p registry-adapterctl
-p registry-evidence -p registry-evidencectl -p registry-manifest-cli
-p registry-breg-mcp -p registry-breg-review -p registry-evidence-oid4vci -p registry-mcp
-p registry-evidence-oid4vp
# The launcher is built alone, as the release builds it, so feature
# unification with the product tools cannot hide a missing feature or
# turn on the session code.
- name: Build the launcher check command
run: cargo build --locked -p registry-rstack
- name: Run configuration conformance corpus
run: products/platform/scripts/run-config-conformance.sh --strict --bin-dir target/debug
rust-result:
name: Rust workspace
if: always()
needs:
- changes
- rust-policy
- rust-quality
- rust-tests
- discovery-contracts
- evidence-contracts
- evidence-postgres
- breg-contracts
- breg-postgres-18
- breg-wasm
- identifiers
- casework-postgres
- scheduling-postgres
- scheduling-contracts
- messaging-postgres
- messaging-contracts
- messaging-smtp
- documents-contracts
- documents-postgres
- adapter-contracts
- adapter-postgres
- coordinator-postgres
- config-conformance
runs-on: ubuntu-slim
timeout-minutes: 5
env:
RUST_JOB_RESULTS: ${{ toJSON(needs) }}
steps:
- name: Require every selected Rust job
run: |
python3 - <<'PY'
import json
import os
import sys
jobs = json.loads(os.environ["RUST_JOB_RESULTS"])
failed = {
name: details["result"]
for name, details in jobs.items()
if details["result"] not in {"success", "skipped"}
}
if failed:
print(f"Rust jobs did not pass: {failed}", file=sys.stderr)
raise SystemExit(1)
print("Every selected Rust job passed; unselected jobs were skipped.")
PY
release-tool:
name: Release tooling checks
needs:
- changes
- rust-policy
if: ${{ !cancelled() && needs.changes.result == 'success' && needs.changes.outputs.release_tool == 'true' }}
runs-on: ubuntu-24.04
timeout-minutes: 10
steps:
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
with:
fetch-depth: 0
submodules: false
- name: Install uv
uses: astral-sh/setup-uv@07a047e608db84b3d8c06cd9a7c7dcc9c355c18e # v10.3.2
with:
version: "0.11.16"
- name: Test release helper
run: uv run --no-project --with PyYAML==6.0.2 python3 -m unittest release/scripts/test_registry_release.py
- name: Test release scheduling
run: uv run --no-project --with PyYAML==6.0.2 python3 -m unittest release/scripts/test_release_ci_overlap.py
- name: Test release docs scheduling
run: uv run --no-project --with PyYAML==6.0.2 python3 -m unittest release/scripts/test_release_docs_scheduling.py
- name: Test release-note drafting
run: uv run --no-project --with PyYAML==6.0.2 python3 -m unittest release/scripts/test_release_notes.py
- name: Test release planning commands
run: uv run --no-project --with PyYAML==6.0.2 python3 -m unittest release/scripts/test_registry_release_plans.py
- name: Test documentation release preparation
run: uv run --no-project --with PyYAML==6.0.2 python3 -m unittest release/scripts/test_prepare_release_docs.py
- name: Test version-selected release roster
run: python3 -m unittest release/scripts/test_release_roster.py
- name: Test supporting release image packaging
run: python3 -m unittest release/scripts/test_release_supporting_images.py
- name: Test release candidate manifest and promotion verifier
run: python3 -m unittest release/scripts/test_release_candidate.py
- name: Test rstack release component inventory
run: python3 -m unittest release/scripts/test_rstack_components.py
- name: Test release unit ownership
run: python3 -m unittest release/scripts/test_release_units.py
- name: Test scoped candidate contracts
run: uv run --no-project --with jsonschema==4.25.1 python3 -m unittest release/scripts/test_release_candidate_v3.py
- name: Test distribution component selection
run: python3 -m unittest release/scripts/test_release_distribution.py
- name: Test unit release pipeline
run: python3 -m unittest release/scripts/test_unit_release_pipeline.py
- name: Test Discovery installer
run: python3 -m unittest release/scripts/test_discovery_install.py
- name: Test installed Discovery and SDK smoke
run: python3 -m unittest release/scripts/test_smoke_discovery_distribution.py
- name: Test public release verifier
run: python3 -m unittest release/scripts/test_verify_public_release.py
- name: Test client registry reconciliation
run: python3 -m unittest release/scripts/test_client_registry.py
- name: Test unified Node client facade sync
run: python3 -m unittest release/scripts/test_sync_registry_client_node.py
- name: Test unified Python wheel assembly
run: python3 -m unittest release/scripts/test_assemble_registry_client_wheel.py
- name: Test local client package assembly
run: python3 -m unittest release/scripts/test_assemble_registry_client_packages.py
- name: Test client package smoke checks
run: python3 -m unittest release/scripts/test_smoke_registry_client_package.py
- name: Test release storage preflight
run: python3 -m unittest release/scripts/test_check_release_storage.py
- name: Test release candidate cleanup
run: python3 -m unittest release/scripts/test_cleanup_release_candidates.py
- name: Test release repeatability workflow
run: python3 -m unittest release/scripts/test_release_repeatability_workflow.py
- name: Test release rehearsal workflow
run: uv run --no-project --with PyYAML==6.0.2 python3 -m unittest release/scripts/test_release_rehearsal.py
- name: Test release upgrade rehearsal
run: uv run --no-project --with PyYAML==6.0.2 python3 -m unittest release/scripts/test_rehearse_upgrade.py
- name: Test rehearsal advisory evidence collector
run: python3 -m unittest release/scripts/test_collect_rehearsal_advisory_evidence.py
- name: Test image ELF exposure report
run: python3 -m unittest release/scripts/test_image_exposure.py
- name: Test advisory baseline renewal
run: uv run --no-project --with PyYAML==6.0.2 python3 -m unittest release/scripts/test_advisory_renewal.py
- name: Test Linux Node client release build helper
run: python3 -m unittest release/scripts/test_build_linux_node_client.py
- name: Test Linux Python client release build helper
run: python3 -m unittest release/scripts/test_build_linux_python_client.py
- name: Test Zig glibc compiler wrapper
run: python3 -m unittest release/scripts/test_zig_glibc_compiler.py
- name: Test canonical release binary shard merge
run: python3 -m unittest release/scripts/test_merge_release_binary_shards.py
- name: Test native release platform build and merge
run: python3 -m unittest release/scripts/test_release_native_platform.py
- name: Test macOS FIPS packaging
run: python3 -m unittest release/scripts/test_macos_fips_packaging.py
- name: Test macOS bundle installers
run: python3 -m unittest release/scripts/test_macos_installers.py
- name: Test generated product installers
run: python3 -m unittest release/scripts/test_additional_installers.py
- name: Test additional Linux ARM binary builds
run: python3 -m unittest release/scripts/test_additional_platform_binaries.py
- name: Test nightly channel and immutable publication
run: python3 -m unittest release/scripts/test_nightly_release.py
- name: Test nightly installer resolution
run: python3 -m unittest release/scripts/test_nightly_installers.py
- name: Test nightly workflow trust boundaries
run: uv run --no-project --with PyYAML==6.0.2 python3 -m unittest release/scripts/test_nightly_release_workflow.py
- name: Test release workflow structure
run: uv run --no-project --with PyYAML==6.0.2 python3 -m unittest release/scripts/test_release_workflow_structure.py
- name: Test release workflow guard
run: python3 -m unittest release/scripts/test_release_workflow_guard.py
- name: Test release image OCI label checker
run: python3 -m unittest release/scripts/test_check_release_image_oci_labels.py
- name: Test release image layout comparator
run: python3 -m unittest release/scripts/test_compare_release_image_layouts.py
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@f87e5991a6d7451dcb8d9637bfbc97413f497069
with:
version: v0.33.0
driver-opts: image=ghcr.io/registrystack/buildkit:v0.31.2@sha256:2f5adac4ecd194d9f8c10b7b5d7bceb5186853db1b26e5abd3a657af0b7e26ec
- name: Smoke release image OCI labels
run: release/scripts/smoke-release-image-oci-labels.sh
- name: Gate inventory
run: python3 release/scripts/check-gates-inventory.py
- name: Test gate inventory
run: uv run --no-project --with PyYAML==6.0.2 python3 -m unittest release/scripts/test_check_gates_inventory.py
- name: Validate release manifest
run: uv run --no-project --with PyYAML==6.0.2 python3 release/scripts/registry-release validate-current
- name: Validate release docsets
run: uv run --no-project --with PyYAML==6.0.2 python3 release/scripts/registry-release validate-docsets
- name: Audit import map
run: uv run --no-project --with PyYAML==6.0.2 python3 release/scripts/registry-release audit release/manifests/import-map-2026-06-24.yaml
release-tool-required:
# Branch protection requires this exact context. Keep it successful for
# irrelevant changes without running the release-tooling suite.
name: Release tooling
needs:
- changes
- release-tool
if: ${{ always() }}
runs-on: ubuntu-slim
timeout-minutes: 5
steps:
- name: Report path-gated result
env:
CHANGES_RESULT: ${{ needs.changes.result }}
REQUIRED: ${{ needs.changes.outputs.release_tool }}
RESULT: ${{ needs.release-tool.result }}
shell: bash
run: |
set -euo pipefail
case "${CHANGES_RESULT}:${REQUIRED}:${RESULT}" in
success:true:success|success:false:skipped)
;;
*)
echo "Release tooling path gate did not complete safely: changes=${CHANGES_RESULT} required=${REQUIRED} result=${RESULT}" >&2
exit 1
;;
esac
release-source-proof:
name: Release source proof checks
needs:
- changes
- rust-policy
if: ${{ !cancelled() && needs.changes.result == 'success' && needs.changes.outputs.release_source_proof == 'true' }}
runs-on: ubuntu-24.04
timeout-minutes: 5
steps:
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
with:
fetch-depth: 0
submodules: false
- name: Install uv
uses: astral-sh/setup-uv@07a047e608db84b3d8c06cd9a7c7dcc9c355c18e # v10.3.2
with:
version: "0.11.16"
- name: Check monorepo source model
run: uv run --no-project --with PyYAML==6.0.2 release/scripts/check-release-source-model.sh
- name: Test source model checker
run: uv run --no-project --with PyYAML==6.0.2 python3 -m unittest release/scripts/test_check_release_source_model.py
release-source-proof-required:
# Branch protection requires this exact context. Keep it successful for
# irrelevant changes without running source proof.
name: Release source proof
needs:
- changes
- release-source-proof
if: ${{ always() }}
runs-on: ubuntu-slim
timeout-minutes: 5
steps:
- name: Report path-gated result
env:
CHANGES_RESULT: ${{ needs.changes.result }}
REQUIRED: ${{ needs.changes.outputs.release_source_proof }}
RESULT: ${{ needs.release-source-proof.result }}
shell: bash
run: |
set -euo pipefail
case "${CHANGES_RESULT}:${REQUIRED}:${RESULT}" in
success:true:success|success:false:skipped)
;;
*)
echo "Release source proof path gate did not complete safely: changes=${CHANGES_RESULT} required=${REQUIRED} result=${RESULT}" >&2
exit 1
;;
esac
evidence-tutorials:
name: Evidence tutorials from source
needs: changes
if: needs.changes.outputs.evidence_tutorial == 'true'
runs-on: ubuntu-24.04
timeout-minutes: 40
steps:
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
with:
fetch-depth: 0
submodules: false
- name: Assert amd64 runner
shell: bash
run: |
set -euo pipefail
[[ "$(uname -m)" == "x86_64" ]]
- name: Setup Node
uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1
with:
node-version: 24.11.0
cache: npm
cache-dependency-path: docs/site/package-lock.json
- name: Cache Cargo registry
uses: Swatinem/rust-cache@c8037a86dde8214a8310e9f54a8fc61ab362aa31 # v2
with:
shared-key: workspace-registry
cache-targets: false
save-if: ${{ github.ref == 'refs/heads/main' }}
- name: Install docs dependencies
working-directory: docs/site
# The tutorial runner needs only pure-JS packages. Skipping install
# scripts keeps the Vale postinstall's unauthenticated GitHub
# download, which shared runner addresses get refused on, out of
# this job.
run: npm ci --ignore-scripts
- name: Test the tutorial runner
working-directory: docs/site
run: npm run test:tutorial:runner
- name: Check tutorial command drift
working-directory: docs/site
run: npm run check:tutorial:evidence:dry-run
- name: Build the Evidence toolset under test
shell: bash
run: |
set -euo pipefail
cargo build --locked \
-p registry-evidence -p registry-evidencectl \
-p registry-evidence-oid4vci -p registry-rstack --bins
cargo test --locked \
-p registry-evidence-oid4vci --test inji_interoperability --no-run
test_binary=$(find target/debug/deps \
-maxdepth 1 -type f -perm -111 -name 'inji_interoperability-*' \
-printf '%T@ %p\n' | sort -nr | sed -n '1s/^[^ ]* //p')
[[ -n "$test_binary" ]]
cp "$test_binary" \
target/debug/inji-oid4vci-interop-test
- name: Assemble the client package the application tutorial imports
shell: bash
run: |
set -euo pipefail
# The tutorial installs the maintained client package, so the replay
# imports that same package, assembled from this commit. maturin gets
# the pinned release build environment. The CI profile exercises the
# same six-binding assembly and installed facade without release LTO.
# Production optimization remains covered by release recipe checks.
python3 -m venv "${RUNNER_TEMP}/maturin"
"${RUNNER_TEMP}/maturin/bin/pip" install --quiet \
--require-hashes --only-binary=:all: \
--requirement "${GITHUB_WORKSPACE}/release/requirements/maturin-1.9.6.txt"
out_dir="${GITHUB_WORKSPACE}/target/evidence-tutorial-client"
python3 release/scripts/assemble-registry-client-packages.py \
--artifacts python \
--include-casework \
--include-messaging \
--include-scheduling \
--include-render \
--include-coordinator \
--python-profile ci \
--output-dir "${out_dir}" \
--work-dir "${RUNNER_TEMP}/client-assembly" \
--maturin "${RUNNER_TEMP}/maturin/bin/maturin" \
--zig-python "${RUNNER_TEMP}/maturin/bin/python"
wheel="$(find "${out_dir}" -maxdepth 1 -name 'registry_stack_client-*.whl')"
if [[ "$(printf '%s\n' "${wheel}" | wc -l)" -ne 1 || ! -f "${wheel}" ]]; then
echo "expected exactly one assembled client wheel in ${out_dir}" >&2
find "${out_dir}" -maxdepth 1 -name '*.whl' >&2
exit 1
fi
# Prove installer metadata and native facade loading from this exact
# wheel. The application tutorial separately exercises requests.
python3 -m venv "${RUNNER_TEMP}/client-install-smoke"
"${RUNNER_TEMP}/client-install-smoke/bin/pip" install \
--no-index --no-deps "${wheel}"
"${RUNNER_TEMP}/client-install-smoke/bin/python" -I \
release/scripts/smoke-registry-client-package.py
printf 'REGISTRY_CLIENT_PY_WHEEL=%s\n' "${wheel}" >>"${GITHUB_ENV}"
- name: Test the exact local Evidence lifecycle
shell: bash
env:
EVIDENCE_BIN: ${{ github.workspace }}/target/debug/evidence
run: |
set -euo pipefail
cargo test --locked \
-p registry-evidencectl --test dev_lifecycle -- \
--ignored --test-threads=1
# The generated SQLite starter target must pass the offline target
# check, which runs the binding checks of the runtime built above.
cargo test --locked -p registry-evidencectl --test scaffold \
the_generated_sqlite_starter_target_binds_its_extract_profile \
-- --ignored --exact \
| tee "${RUNNER_TEMP}/evidence-sqlite-starter-target.log"
grep -q 'test result: ok\. 1 passed' "${RUNNER_TEMP}/evidence-sqlite-starter-target.log" || { echo "::error::expected exactly one passing test for the_generated_sqlite_starter_target_binds_its_extract_profile"; exit 1; }
# A fresh starter must package against the local target made for it,
# once it binds its published extract.
cargo test --locked -p registry-evidencectl --test scaffold \
a_fresh_starter_packages_against_its_new_local_target \
-- --ignored --exact \
| tee "${RUNNER_TEMP}/evidence-starter-package.log"
grep -q 'test result: ok\. 1 passed' "${RUNNER_TEMP}/evidence-starter-package.log" || { echo "::error::expected exactly one passing test for a_fresh_starter_packages_against_its_new_local_target"; exit 1; }
# Current authoring checks are offline. Fixture evaluation is a
# separate compiled-target proof against the runtime built above.
# Exact filters and one-pass guards refuse a renamed empty proof.
cargo test --locked -p registry-evidencectl --test production_handoff \
offline_project_check_needs_no_disposable_signing_state \
-- --ignored --exact \
| tee "${RUNNER_TEMP}/evidence-authoring-check.log"
grep -q 'test result: ok\. 1 passed' "${RUNNER_TEMP}/evidence-authoring-check.log" || { echo "::error::expected exactly one passing test for offline_project_check_needs_no_disposable_signing_state"; exit 1; }
cargo test --locked -p registry-evidencectl --test production_handoff \
production_build_checks_and_evaluates_every_neutral_authoring_shape \
-- --ignored --exact \
| tee "${RUNNER_TEMP}/evidence-fixture-evaluation.log"
grep -q 'test result: ok\. 1 passed' "${RUNNER_TEMP}/evidence-fixture-evaluation.log" || { echo "::error::expected exactly one passing test for production_build_checks_and_evaluates_every_neutral_authoring_shape"; exit 1; }
cargo test --locked -p registry-evidencectl --test source_recipes \
curated_source_recipes_import_check_and_run_the_existing_evaluator \
-- --ignored --exact \
| tee "${RUNNER_TEMP}/evidence-source-recipes.log"
grep -q 'test result: ok\. 1 passed' "${RUNNER_TEMP}/evidence-source-recipes.log" || { echo "::error::expected exactly one passing test for curated_source_recipes_import_check_and_run_the_existing_evaluator"; exit 1; }
- name: Test the public certificate check example
shell: bash
env:
EVIDENCE_BIN: ${{ github.workspace }}/target/debug/evidence
run: |
set -euo pipefail
cargo test --locked -p registry-evidencectl --test public_check \
-- --ignored --exact the_packaged_example_answers_a_hit_and_an_identical_miss_and_audits_neither_subject \
| tee "${RUNNER_TEMP}/evidence-public-check.log"
grep -q 'test result: ok\. 1 passed' "${RUNNER_TEMP}/evidence-public-check.log" || { echo "::error::expected exactly one passing test for the_packaged_example_answers_a_hit_and_an_identical_miss_and_audits_neither_subject"; exit 1; }
node --test products/evidence/examples/public-check/page/check.test.mjs
- name: Test the composed wallet presentation journey
shell: bash
env:
EVIDENCE_BIN: ${{ github.workspace }}/target/debug/evidence
EVIDENCECTL_BIN: ${{ github.workspace }}/target/debug/evidencectl
run: |
set -euo pipefail
cargo test --locked -p registry-evidence-oid4vp --test evidence_journey \
a_presented_credential_becomes_a_verified_assertion \
-- --ignored --exact \
| tee "${RUNNER_TEMP}/evidence-wallet-journey.log"
grep -q 'test result: ok\. 1 passed' "${RUNNER_TEMP}/evidence-wallet-journey.log" || { echo "::error::expected exactly one passing test for a_presented_credential_becomes_a_verified_assertion"; exit 1; }
cargo test --locked -p registry-evidence-oid4vp --test device_run \
a_simulated_wallet_completes_the_device_run_harness \
-- --ignored --exact \
| tee "${RUNNER_TEMP}/evidence-wallet-device-run.log"
grep -q 'test result: ok\. 1 passed' "${RUNNER_TEMP}/evidence-wallet-device-run.log" || { echo "::error::expected exactly one passing test for a_simulated_wallet_completes_the_device_run_harness"; exit 1; }
- name: Execute the Evidence tutorials with the stock issuer
shell: bash
env:
RSTACK_BIN: ${{ github.workspace }}/target/debug/rstack
EVIDENCE_BIN: ${{ github.workspace }}/target/debug/evidence
EVIDENCECTL_BIN: ${{ github.workspace }}/target/debug/evidencectl
EVIDENCE_OID4VCI_BIN: ${{ github.workspace }}/target/debug/evidence-oid4vci
EVIDENCE_OID4VCI_INTEROP_TEST_BIN: ${{ github.workspace }}/target/debug/inji-oid4vci-interop-test
run: node docs/site/scripts/run-tutorial.mjs --gate evidence
breg-tutorial:
name: Base Registry Engine tutorial from source (${{ matrix.shard }})
needs:
- changes
- rust-policy
if: ${{ !cancelled() && needs.changes.result == 'success' && needs.changes.outputs.breg_tutorial == 'true' }}
runs-on: ubuntu-24.04
timeout-minutes: 30
strategy:
fail-fast: false
# The gate's journeys are independent, so two shards replay them in
# parallel; the runner's round-robin split covers every journey exactly
# once across the shards. Single-copy checks pin to one shard.
matrix:
shard: ["1/2", "2/2"]
steps:
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
with:
persist-credentials: false
submodules: false
- name: Setup Node
uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1
with:
node-version: 24.11.0
cache: npm
cache-dependency-path: docs/site/package-lock.json
- name: Cache Cargo registry
uses: Swatinem/rust-cache@c8037a86dde8214a8310e9f54a8fc61ab362aa31 # v2
with:
shared-key: workspace-registry
cache-targets: false
save-if: ${{ github.ref == 'refs/heads/main' }}
- name: Install uv
uses: astral-sh/setup-uv@07a047e608db84b3d8c06cd9a7c7dcc9c355c18e # v10.3.2
with:
version: "0.11.16"
- name: Install docs dependencies
working-directory: docs/site
# The tutorial runner needs only pure-JS packages. Skipping install
# scripts keeps the Vale postinstall's unauthenticated GitHub
# download, which shared runner addresses get refused on, out of
# this job.
run: npm ci --ignore-scripts
- name: Test the tutorial runner
if: matrix.shard == '1/2'
working-directory: docs/site
run: npm run test:tutorial:runner
- name: Check tutorial command drift
if: matrix.shard == '1/2'
working-directory: docs/site
run: npm run check:tutorial:breg:dry-run
- name: Build the Base Registry Engine toolset under test
shell: bash
run: |
set -euo pipefail
# The client tutorial installs the client packages at the version
# `breg --version` reports, and the runner assembles them at the
# workspace version, so the binaries are built marked with its
# release tag. GITHUB_ENV keeps the marker for every later step, so
# no later cargo command rebuilds them unmarked.
tag="$(node --input-type=module -e "import { workspaceReleaseTag } from './docs/site/scripts/tutorial-runner/toolsets.mjs'; console.log(workspaceReleaseTag('.'))")"
printf 'REGISTRY_RELEASE_TAG=%s\n' "${tag}" >> "${GITHUB_ENV}"
REGISTRY_RELEASE_TAG="${tag}" cargo build --locked \
-p registry-breg --features registry-breg/runtime \
-p registry-bregctl -p registry-rstack --bins
- name: Verify native retained development lifecycle
if: matrix.shard == '2/2'
shell: bash
run: |
set -euo pipefail
# Each retained lifecycle owns PostgreSQL, ThunderID and BREG.
# Run complete sessions serially on the bounded CI runner.
cargo test --locked -p registry-bregctl \
--test dev_lifecycle -- --ignored --test-threads=1
- name: Verify retained example recovery after process exit
if: matrix.shard == '2/2'
# An inexact name filter that selects nothing still exits 0, so the
# step names each test exactly and fails unless it reports one pass.
shell: bash
run: |
set -euo pipefail
for test in \
dev::examples::tests::native_create_recovers_after_process_exit_without_duplicate_records \
dev::examples::tests::native_invoke_recovers_original_conditions_after_restart; do
cargo test --locked -p registry-bregctl --lib \
"$test" -- --ignored --exact \
| tee "${RUNNER_TEMP}/breg-example-recovery-${test}.log"
grep -q 'test result: ok\. 1 passed' "${RUNNER_TEMP}/breg-example-recovery-${test}.log" || { echo "::error::expected exactly one passing test for ${test}"; exit 1; }
done
- name: Execute the Base Registry Engine tutorials from a reader directory
shell: bash
env:
RSTACK_BIN: ${{ github.workspace }}/target/debug/rstack
BREG_BIN: ${{ github.workspace }}/target/debug/breg
BREGCTL_BIN: ${{ github.workspace }}/target/debug/bregctl
BREG_TUTORIAL_SHARD: ${{ matrix.shard }}
run: |
set -euo pipefail
# This replay does not run in a clean read-only container the way the
# Evidence one does: `bregctl dev` runs PostgreSQL in a disposable
# container of its own, which the runner stops when the journey ends.
node docs/site/scripts/run-tutorial.mjs --gate breg --shard "$BREG_TUTORIAL_SHARD"
casework-tutorial:
name: Registry Casework native review journeys
needs:
- changes
- rust-policy
if: ${{ !cancelled() && needs.changes.result == 'success' && needs.changes.outputs.casework_tutorial == 'true' }}
runs-on: ubuntu-24.04
timeout-minutes: 30
steps:
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
with:
persist-credentials: false
submodules: false
- name: Setup Node
uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1
with:
node-version: 24.11.0
cache: npm
cache-dependency-path: |
crates/registry-casework-client-node/package-lock.json
docs/site/package-lock.json
- name: Cache Cargo registry
uses: Swatinem/rust-cache@c8037a86dde8214a8310e9f54a8fc61ab362aa31 # v2
with:
shared-key: workspace-registry
cache-targets: false
save-if: ${{ github.ref == 'refs/heads/main' }}
- name: Execute the canonical Casework developer journey
# The maintained submitted-review journey owns its disposable Docker
# database and verifies public binaries plus Rust/Node/Python facades.
run: products/casework/scripts/check-review-journeys.sh
- name: Install docs dependencies
working-directory: docs/site
# The tutorial runner needs only pure-JS packages. Skipping install
# scripts keeps the Vale postinstall's unauthenticated GitHub
# download, which shared runner addresses get refused on, out of
# this job.
run: npm ci --ignore-scripts
- name: Test the tutorial runner
working-directory: docs/site
run: npm run test:tutorial:runner
- name: Check tutorial command drift
working-directory: docs/site
run: npm run check:tutorial:casework:dry-run
- name: Build the Registry Casework toolset under test
shell: bash
run: |
set -euo pipefail
cargo build --locked \
-p registry-casework -p registry-caseworkctl \
-p registry-breg --features registry-breg/runtime \
-p registry-bregctl -p registry-rstack --bins
- name: Install native PostgreSQL 18 and PostGIS for project development
# The Ubuntu runner's PostgreSQL 16 is below BReg's minimum.
shell: bash
run: |
set -euo pipefail
# Use the Ubuntu archive if the runner's Azure mirror cannot resolve.
if [[ -f /etc/apt/apt-mirrors.txt ]] && ! timeout 10 getent ahosts azure.archive.ubuntu.com >/dev/null; then
mirror="$(bash .github/scripts/select-ubuntu-archive.sh)"
printf '%s\n' "$mirror" | sudo tee /etc/apt/apt-mirrors.txt >/dev/null
fi
sudo /usr/share/postgresql-common/pgdg/apt.postgresql.org.sh -y
sudo env DEBIAN_FRONTEND=noninteractive apt-get install --yes --no-install-recommends postgresql-18 postgresql-18-postgis-3
/usr/lib/postgresql/18/bin/postgres --version
- name: Verify the combined licensing development run with native PostgreSQL
shell: bash
run: |
set -euo pipefail
cargo build --locked -p registry-rstack
python3 products/platform/scripts/test-rstack-dev.py \
--bin-dir "${GITHUB_WORKSPACE}/target/debug" \
--postgres-bin-dir /usr/lib/postgresql/18/bin \
--scratch-parent "${RUNNER_TEMP}" \
--spatial
- name: Execute the Registry Casework tutorials from a reader directory
shell: bash
env:
RSTACK_BIN: ${{ github.workspace }}/target/debug/rstack
CASEWORK_BIN: ${{ github.workspace }}/target/debug/casework
CASEWORKCTL_BIN: ${{ github.workspace }}/target/debug/caseworkctl
BREG_BIN: ${{ github.workspace }}/target/debug/breg
BREGCTL_BIN: ${{ github.workspace }}/target/debug/bregctl
run: |
set -euo pipefail
# `caseworkctl dev` and `bregctl dev` each run PostgreSQL in a
# disposable container of their own, which the runner stops when
# the journey ends, so the replay stays on the Docker runner rather
# than inside a clean read-only container.
node docs/site/scripts/run-tutorial.mjs --gate casework
messaging-tutorial:
name: Registry Messaging tutorial from source
needs:
- changes
- rust-policy
if: ${{ !cancelled() && needs.changes.result == 'success' && needs.changes.outputs.messaging_tutorial == 'true' }}
runs-on: ubuntu-24.04
timeout-minutes: 30
steps:
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
with:
persist-credentials: false
submodules: false
- name: Setup Node
uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1
with:
node-version: 24.11.0
cache: npm
cache-dependency-path: docs/site/package-lock.json
- name: Cache Cargo registry
uses: Swatinem/rust-cache@c8037a86dde8214a8310e9f54a8fc61ab362aa31 # v2
with:
shared-key: workspace-registry
cache-targets: false
save-if: ${{ github.ref == 'refs/heads/main' }}
- name: Test the tutorial gate helpers
working-directory: docs/site
run: npm run test:tutorial:messaging
- name: Check tutorial command drift
working-directory: docs/site
run: npm run check:tutorial:messaging:dry-run
- name: Build the Registry Messaging toolset under test
shell: bash
run: |
set -euo pipefail
cargo build --locked -p registry-messagingctl -p registry-messaging -p registry-rstack --bins
- name: Execute the Registry Messaging tutorial from a reader directory
shell: bash
env:
RSTACK_BIN: ${{ github.workspace }}/target/debug/rstack
MESSAGING_BIN: ${{ github.workspace }}/target/debug/messaging
MESSAGINGCTL_BIN: ${{ github.workspace }}/target/debug/messagingctl
run: |
set -euo pipefail
# The tutorial passes no port flags, and this runner listens on
# neither default, so the replay runs on the ports a reader gets.
# The session the page starts runs PostgreSQL and Mailpit in
# containers of its own, which is why the replay stays on the
# Docker runner rather than inside a clean read-only container.
bash docs/site/scripts/check-messaging-tutorial.sh
documents-tutorial:
name: Document Store tutorial from source
needs:
- changes
- rust-policy
if: ${{ !cancelled() && needs.changes.result == 'success' && needs.changes.outputs.documents_tutorial == 'true' }}
runs-on: ubuntu-24.04
timeout-minutes: 30
steps:
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
with:
persist-credentials: false
submodules: false
- name: Setup Node
uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1
with:
node-version: 24.11.0
cache: npm
cache-dependency-path: docs/site/package-lock.json
- name: Cache Cargo registry
uses: Swatinem/rust-cache@c8037a86dde8214a8310e9f54a8fc61ab362aa31 # v2
with:
shared-key: workspace-registry
cache-targets: false
save-if: ${{ github.ref == 'refs/heads/main' }}
- name: Install docs dependencies
working-directory: docs/site
# The tutorial runner needs only pure-JS packages; see the Casework
# job for why install scripts are skipped.
run: npm ci --ignore-scripts
- name: Test the tutorial runner
working-directory: docs/site
run: npm run test:tutorial:runner
- name: Check tutorial command drift
working-directory: docs/site
run: npm run check:tutorial:documents:dry-run
- name: Build registry-documentsctl under test
shell: bash
run: |
set -euo pipefail
cargo build --locked -p registry-documentsctl
- name: Execute the Document Store tutorial from a reader directory
working-directory: docs/site
shell: bash
run: |
set -euo pipefail
# The page's own build step is skipped by the runner; the binary
# built above stands in for it. The journey runs documentsctl only,
# with no database, container or network.
export PATH="$GITHUB_WORKSPACE/target/debug:$PATH"
npm run check:tutorial:documents
render-tutorial:
name: Registry Render tutorial from source
needs:
- changes
- rust-policy
if: ${{ !cancelled() && needs.changes.result == 'success' && needs.changes.outputs.render_tutorial == 'true' }}
runs-on: ubuntu-24.04
timeout-minutes: 30
steps:
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
with:
persist-credentials: false
submodules: false
- name: Setup Node
uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1
with:
node-version: 24.11.0
cache: npm
cache-dependency-path: docs/site/package-lock.json
- name: Cache Cargo registry
uses: Swatinem/rust-cache@c8037a86dde8214a8310e9f54a8fc61ab362aa31 # v2
with:
shared-key: workspace-registry
cache-targets: false
save-if: ${{ github.ref == 'refs/heads/main' }}
- name: Install docs dependencies
working-directory: docs/site
# The tutorial runner needs only pure-JS packages; see the Casework
# job for why install scripts are skipped.
run: npm ci --ignore-scripts
- name: Test the tutorial runner
working-directory: docs/site
run: npm run test:tutorial:runner
- name: Check tutorial command drift
working-directory: docs/site
run: npm run check:tutorial:render:dry-run
- name: Build registry-render under test
shell: bash
run: |
set -euo pipefail
cargo build --locked -p registry-render
- name: Execute the Registry Render tutorial from a reader directory
working-directory: docs/site
shell: bash
run: |
set -euo pipefail
# The page's own build step is skipped by the runner; the binary
# built above stands in for it. The journey starts no container and
# listens on loopback only.
export PATH="$GITHUB_WORKSPACE/target/debug:$PATH"
npm run check:tutorial:render
scheduling-tutorial:
name: Registry Scheduling tutorial from source
needs:
- changes
- rust-policy
if: ${{ !cancelled() && needs.changes.result == 'success' && needs.changes.outputs.scheduling_tutorial == 'true' }}
runs-on: ubuntu-24.04
timeout-minutes: 30
steps:
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
with:
persist-credentials: false
submodules: false
- name: Setup Node
uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1
with:
node-version: 24.11.0
cache: npm
cache-dependency-path: docs/site/package-lock.json
- name: Cache Cargo registry
uses: Swatinem/rust-cache@c8037a86dde8214a8310e9f54a8fc61ab362aa31 # v2
with:
shared-key: workspace-registry
cache-targets: false
save-if: ${{ github.ref == 'refs/heads/main' }}
- name: Install docs dependencies
working-directory: docs/site
# The tutorial runner needs only pure-JS packages; see the Casework
# job for why install scripts are skipped.
run: npm ci --ignore-scripts
- name: Test the tutorial runner
working-directory: docs/site
run: npm run test:tutorial:runner
- name: Check tutorial command drift
working-directory: docs/site
run: npm run check:tutorial:scheduling:dry-run
- name: Build scheduling and schedulingctl under test
shell: bash
run: |
set -euo pipefail
cargo build --locked -p registry-scheduling -p registry-schedulingctl --bins
- name: Execute the Registry Scheduling tutorial from a reader directory
working-directory: docs/site
shell: bash
env:
SCHEDULING_BIN: ${{ github.workspace }}/target/debug/scheduling
SCHEDULINGCTL_BIN: ${{ github.workspace }}/target/debug/schedulingctl
run: |
set -euo pipefail
# The pages' own install step is skipped by the runner; the binaries
# built above stand in for it. The journey starts no service and no
# container.
npm run check:tutorial:scheduling
breg-evidence-composition:
# The offline proof that a BReg evidence-source export composes into an
# Evidence project and that a later export updates it. It drives three
# native binaries over the reviewed teaching inputs and starts no service,
# so it needs neither Docker nor the tutorial replay's writable checkout.
# It also proves the surface of evidencectl source add without a session:
# the review it performs by default, the matching bregctl it requires, and
# that a refused connection leaves both projects untouched. Successful
# preparation needs a retained session and is outside this offline proof.
name: Base Registry Engine to Evidence composition
needs:
- changes
- rust-policy
if: ${{ !cancelled() && needs.changes.result == 'success' && needs.changes.outputs.breg_evidence_composition == 'true' }}
runs-on: ubuntu-24.04
timeout-minutes: 30
steps:
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
with:
persist-credentials: false
submodules: false
- name: Cache Cargo registry
uses: Swatinem/rust-cache@c8037a86dde8214a8310e9f54a8fc61ab362aa31 # v2
with:
shared-key: workspace-registry
cache-targets: false
save-if: ${{ github.ref == 'refs/heads/main' }}
- name: Install uv
uses: astral-sh/setup-uv@07a047e608db84b3d8c06cd9a7c7dcc9c355c18e # v10.3.2
with:
version: "0.11.16"
- name: Build the composition toolset under test
shell: bash
run: |
set -euo pipefail
cargo build --locked \
-p registry-bregctl -p registry-evidence -p registry-evidencectl \
--bins
- name: Verify native Evidence source composition and updates
shell: bash
run: |
set -euo pipefail
uv run --no-project --with PyYAML==6.0.2 python \
products/breg/evidence/tests/verify-composition.py \
--bregctl "$GITHUB_WORKSPACE/target/debug/bregctl" \
--evidencectl "$GITHUB_WORKSPACE/target/debug/evidencectl" \
--evidence "$GITHUB_WORKSPACE/target/debug/evidence"
evidence-anchors:
# The documentation anchors cite source across the whole workspace, so this check
# runs on every pull request rather than behind the changed-path classifier: a
# rename outside the docs job's allow-list is exactly the drift it exists to catch.
# The checker imports only node:fs, node:path, and node:url, so a checkout and a
# Node runtime are all it needs; the docs job runs it again inside check:source.
name: Evidence anchors
runs-on: ubuntu-24.04
timeout-minutes: 10
steps:
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
with:
persist-credentials: false
submodules: false
- name: Setup Node
uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1
with:
node-version: 24.11.0
- name: Check documentation anchors against the source tree
run: node docs/site/scripts/check-evidence-anchors.mjs
docs:
name: Docs checks
needs: changes
if: needs.changes.outputs.docs == 'true'
runs-on: ubuntu-24.04
timeout-minutes: 30
steps:
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
with:
fetch-depth: 0
submodules: false
- name: Setup Node
uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1
with:
node-version: 24.11.0
cache: npm
cache-dependency-path: docs/site/package-lock.json
- name: Install docs shell dependency
shell: bash
run: |
set -euo pipefail
# Use the Ubuntu archive if the runner's Azure mirror cannot resolve.
if [[ -f /etc/apt/apt-mirrors.txt ]] && ! timeout 10 getent ahosts azure.archive.ubuntu.com >/dev/null; then
mirror="$(bash .github/scripts/select-ubuntu-archive.sh)"
printf '%s\n' "$mirror" | sudo tee /etc/apt/apt-mirrors.txt >/dev/null
fi
sudo apt-get update
sudo apt-get install --yes zsh
- name: Assert docs shell dependencies
shell: bash
run: |
set -euo pipefail
command -v sh
command -v bash
command -v zsh
- name: Install docs dependencies
working-directory: docs/site
# The only install script that matters here is the Vale postinstall,
# which fetches its binary through the unauthenticated GitHub API and
# is refused once a shared runner address exhausts its hourly limit.
# The next step installs a pinned Vale instead.
run: npm ci --ignore-scripts
- name: Install pinned Vale
shell: bash
run: |
set -euo pipefail
mkdir -p "${RUNNER_TEMP}/bin"
curl --proto '=https' --tlsv1.2 --fail --silent --show-error --location \
--output "${RUNNER_TEMP}/vale.tar.gz" \
"https://github.com/vale-cli/vale/releases/download/v${VALE_VERSION}/vale_${VALE_VERSION}_Linux_64-bit.tar.gz"
echo "${VALE_LINUX_X64_SHA256} ${RUNNER_TEMP}/vale.tar.gz" | \
sha256sum --check --strict
tar -xzf "${RUNNER_TEMP}/vale.tar.gz" -C "${RUNNER_TEMP}/bin" vale
echo "VALE_BIN=${RUNNER_TEMP}/bin/vale" >> "${GITHUB_ENV}"
- name: Fetch pinned OpenAPI inputs
working-directory: docs/site
run: node scripts/fetch-openapi.mjs
- name: Test docs scripts
working-directory: docs/site
run: npm test
- name: Check production-shaped docs build
working-directory: docs/site
run: npm run check:production
docs-required:
# Branch protection requires this exact context. Keep it successful for
# irrelevant changes without installing or building the docs site.
name: Docs
needs:
- changes
- docs
- docs-archives
if: ${{ always() }}
runs-on: ubuntu-slim
timeout-minutes: 5
steps:
- name: Report path-gated result
env:
CHANGES_RESULT: ${{ needs.changes.result }}
REQUIRED: ${{ needs.changes.outputs.docs }}
RESULT: ${{ needs.docs.result }}
ARCHIVES_REQUIRED: ${{ needs.changes.outputs.docs_archives }}
ARCHIVES_RESULT: ${{ needs.docs-archives.result }}
shell: bash
run: |
set -euo pipefail
case "${CHANGES_RESULT}:${REQUIRED}:${RESULT}" in
success:true:success|success:false:skipped)
;;
*)
echo "Docs path gate did not complete safely: changes=${CHANGES_RESULT} required=${REQUIRED} result=${RESULT}" >&2
exit 1
;;
esac
case "${CHANGES_RESULT}:${ARCHIVES_REQUIRED}:${ARCHIVES_RESULT}" in
success:true:success|success:false:skipped)
;;
*)
echo "Docs archive path gate did not complete safely: changes=${CHANGES_RESULT} required=${ARCHIVES_REQUIRED} result=${ARCHIVES_RESULT}" >&2
exit 1
;;
esac
docs-archives:
name: Immutable docs archives
needs:
- changes
- rust-policy
if: ${{ !cancelled() && needs.changes.result == 'success' && needs.changes.outputs.docs_archives == 'true' }}
runs-on: ubuntu-24.04
timeout-minutes: 35
steps:
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
with:
fetch-depth: 0
submodules: false
- name: Setup Node
uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1
with:
node-version: 24.11.0
cache: npm
cache-dependency-path: docs/site/package-lock.json
- name: Install docs dependencies
working-directory: docs/site
# No step here runs Vale, the only package with an install script
# this job would use. Its postinstall fetches through the
# unauthenticated GitHub API, which refuses shared runner addresses
# once their hourly limit is spent, so install scripts are skipped.
run: npm ci --ignore-scripts
- name: Validate archive lock and compare event baseline
working-directory: docs/site
env:
ARCHIVE_LOCK_BASE_REF: ${{ needs.changes.outputs.archive_base_ref }}
ARCHIVE_COMPARISON_REQUIRED: ${{ needs.changes.outputs.archive_comparison_required }}
shell: bash
run: |
set -euo pipefail
if [[ "${ARCHIVE_COMPARISON_REQUIRED}" == "true" ]]; then
if [[ -z "${ARCHIVE_LOCK_BASE_REF}" ]]; then
echo "Cannot verify archive immutability: event baseline is unavailable." >&2
exit 1
fi
npm run check:archive-lock -- --base-ref "${ARCHIVE_LOCK_BASE_REF}"
elif [[ "${ARCHIVE_COMPARISON_REQUIRED}" == "false" ]]; then
# Scheduled reconstruction validates locked bytes without a change baseline.
npm run check:archive-lock
else
echo "Archive comparison policy is unavailable." >&2
exit 1
fi
- name: Assemble and verify the published archive window
working-directory: docs/site
run: npm run check:archives
editor-extensions:
name: Editor extensions
needs:
- changes
- rust-policy
if: ${{ !cancelled() && needs.changes.result == 'success' && needs.changes.outputs.editors == 'true' }}
runs-on: ubuntu-24.04
timeout-minutes: 15
steps:
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
with:
fetch-depth: 0
submodules: false
- name: Setup Node
uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1
with:
node-version: 24.11.0
cache: npm
cache-dependency-path: editors/vscode/package-lock.json
- name: Test editor installer
run: |
bash editors/tests/install_test.sh
python3 -m unittest discover -s editors/tests -p 'test_*.py'
- name: Package and test VS Code integration
working-directory: editors/vscode
run: |
npm ci
npm run package:dev
cargo build --locked -p registry-language-server
xvfb-run -a npm test
cmp ../../LICENSE LICENSE
- name: Install Zed extension target
run: rustup target add wasm32-wasip2
- name: Check Zed extension
run: |
cargo test --locked --manifest-path editors/zed/Cargo.toml
cargo check --locked --target wasm32-wasip2 --manifest-path editors/zed/Cargo.toml
cmp LICENSE editors/zed/LICENSE
client-bindings:
name: Native client bindings
needs: changes
if: needs.changes.outputs.client_bindings == 'true'
runs-on: ubuntu-24.04
timeout-minutes: 45
steps:
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
with:
fetch-depth: 0
submodules: false
- name: Setup Node
uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1
with:
# The napi-rs CLI's toolchain dependencies require Node 24.12 or
# later. Candidate package smoke tests separately retain the Node
# 24.11 runtime floor.
node-version: 24.21.0
cache: npm
cache-dependency-path: |
crates/registry-discovery-client-node/package-lock.json
crates/registry-evidence-client-node/package-lock.json
crates/registry-breg-client-node/package-lock.json
crates/registry-casework-client-node/package-lock.json
crates/registry-messaging-client-node/package-lock.json
crates/registry-scheduling-client-node/package-lock.json
crates/registry-coordinator-client-node/package-lock.json
crates/registry-render-client-node/package-lock.json
crates/registry-stack-client-node/package-lock.json
- name: Cache Cargo registry
uses: Swatinem/rust-cache@c8037a86dde8214a8310e9f54a8fc61ab362aa31 # v2
with:
shared-key: workspace-registry
cache-targets: false
save-if: ${{ github.ref == 'refs/heads/main' }}
- name: Build and test Node bindings
shell: bash
run: |
set -euo pipefail
for client in registry-discovery-client-node registry-evidence-client-node registry-breg-client-node registry-casework-client-node registry-messaging-client-node registry-scheduling-client-node registry-coordinator-client-node registry-render-client-node; do
(
cd "crates/${client}"
npm ci
npm run build:debug
npm test
npm run check:types
cmp ../../LICENSE LICENSE
)
done
(
cd crates/registry-stack-client-node
npm ci
npm test
npm run check:types
cmp ../../LICENSE LICENSE
)
- name: Build and test Python bindings
shell: bash
run: |
set -euo pipefail
for client in registry-discovery-client-py registry-evidence-client-py registry-breg-client-py registry-casework-client-py registry-messaging-client-py registry-scheduling-client-py registry-coordinator-client-py registry-render-client-py; do
cargo build --locked -p "${client}" --lib --features "${client}/extension-module"
(
cd "crates/${client}"
python3 -m unittest discover -s tests/python -v
cmp ../../LICENSE LICENSE
)
done
- name: Install uv
uses: astral-sh/setup-uv@07a047e608db84b3d8c06cd9a7c7dcc9c355c18e # v10.3.2
with:
version: "0.11.16"
- name: Test direct Python composition with the exact checkout client
working-directory: products/platform/examples/python-composition
shell: bash
run: |
set -euo pipefail
maturin_venv="${RUNNER_TEMP}/python-composition-maturin"
uv venv "${maturin_venv}"
uv pip install \
--python "${maturin_venv}/bin/python" \
--require-hashes \
--only-binary=:all: \
--requirement "${GITHUB_WORKSPACE}/release/requirements/maturin-1.9.6.txt"
out_dir="${RUNNER_TEMP}/python-composition-client"
python3 "${GITHUB_WORKSPACE}/release/scripts/assemble-registry-client-packages.py" \
--artifacts python \
--python-profile ci \
--output-dir "${out_dir}" \
--work-dir "${RUNNER_TEMP}/python-composition-client-assembly" \
--maturin "${maturin_venv}/bin/maturin" \
--zig-python "${maturin_venv}/bin/python"
mapfile -t wheels < <(find "${out_dir}" -maxdepth 1 -type f \
-name 'registry_stack_client-*.whl')
if [[ "${#wheels[@]}" -ne 1 ]]; then
printf 'expected exactly one assembled client wheel in %s\n' "${out_dir}" >&2
printf '%s\n' "${wheels[@]}" >&2
exit 1
fi
uv run --locked --with "${wheels[0]}" \
python -m unittest discover -s tests -v
- name: Test released-client Coordinator example
working-directory: products/coordinator/examples/reviewed-notice
run: uv run --locked python -m unittest discover -s tests -v
release-linux-node-clients:
name: Release Linux native clients (${{ matrix.asset }})
needs: changes
if: needs.changes.outputs.release_linux_node_clients == 'true'
runs-on: ${{ matrix.runner }}
timeout-minutes: 40
permissions:
contents: read
env:
RUSTUP_TOOLCHAIN: "1.95.0"
strategy:
fail-fast: false
matrix:
include:
- runner: ubuntu-24.04
asset: linux-amd64-glibc
target: x86_64-unknown-linux-gnu
napi_platform: linux-x64-gnu
- runner: ubuntu-24.04-arm
asset: linux-arm64-glibc
target: aarch64-unknown-linux-gnu
napi_platform: linux-arm64-gnu
steps:
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
with:
fetch-depth: 0
submodules: false
- name: Install Go for the AWS-LC-FIPS source build
uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0
with:
go-version: 1.27.2
cache: false
- name: Setup Node
uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1
with:
node-version: 24.21.0
cache: npm
cache-dependency-path: |
crates/registry-discovery-client-node/package-lock.json
crates/registry-evidence-client-node/package-lock.json
crates/registry-breg-client-node/package-lock.json
crates/registry-casework-client-node/package-lock.json
crates/registry-messaging-client-node/package-lock.json
crates/registry-scheduling-client-node/package-lock.json
crates/registry-coordinator-client-node/package-lock.json
crates/registry-render-client-node/package-lock.json
crates/registry-stack-client-node/package-lock.json
- name: Install pinned Linux client build tools
shell: bash
run: |
set -euo pipefail
rustup toolchain install 1.95.0 --profile minimal
python3 -m venv "${RUNNER_TEMP}/maturin"
"${RUNNER_TEMP}/maturin/bin/pip" install --quiet \
--require-hashes --only-binary=:all: \
--requirement "${GITHUB_WORKSPACE}/release/requirements/maturin-1.9.6.txt"
- name: Prove production Linux Node client recipe
shell: bash
run: |
set -euo pipefail
for client in discovery evidence breg casework messaging scheduling coordinator render; do
client_dir="${GITHUB_WORKSPACE}/crates/registry-${client}-client-node"
(cd "${client_dir}" && npm ci)
release/scripts/build-linux-node-client \
--client "${client}" \
--target "${{ matrix.target }}" \
--napi-platform "${{ matrix.napi_platform }}" \
--zig-python "${RUNNER_TEMP}/maturin/bin/python"
if [[ "${client}" == breg || "${client}" == casework || "${client}" == messaging || "${client}" == scheduling || "${client}" == coordinator || "${client}" == render ]]; then
class_name=BaseRegistryClient
base_url=https://registry.invalid
if [[ "${client}" == casework ]]; then
class_name=CaseworkClient
base_url=https://casework.invalid
elif [[ "${client}" == messaging ]]; then
class_name=MessagingClient
base_url=https://messaging.invalid
elif [[ "${client}" == scheduling ]]; then
class_name=SchedulingClient
base_url=https://scheduling.invalid
elif [[ "${client}" == render ]]; then
class_name=RenderClient
base_url=https://render.invalid
elif [[ "${client}" == coordinator ]]; then
class_name=CoordinatorClient
base_url=https://coordinator.invalid
fi
node -e "const Client = require('${client_dir}/client')['${class_name}']; new Client({ baseUrl: '${base_url}' })"
continue
fi
smoke="${RUNNER_TEMP}/node-smoke-${client}"
mkdir -p "${smoke}/node_modules/@registrystack"
ln -s "${client_dir}" \
"${smoke}/node_modules/@registrystack/${client}-client"
cp "${GITHUB_WORKSPACE}/release/scripts/smoke-${client}-client-package.js" \
"${smoke}/"
(cd "${smoke}" && node "smoke-${client}-client-package.js")
done
client_dir="${GITHUB_WORKSPACE}/crates/registry-stack-client-node"
platform_dir="${client_dir}/npm/${{ matrix.napi_platform }}"
for client in discovery evidence breg casework messaging scheduling coordinator render; do
cp "${GITHUB_WORKSPACE}/crates/registry-${client}-client-node/${client}-client.${{ matrix.napi_platform }}.node" \
"${platform_dir}/"
done
smoke="${RUNNER_TEMP}/node-smoke-registry"
mkdir -p "${smoke}/node_modules/@registrystack"
mkdir -p "${client_dir}/node_modules/@registrystack"
ln -s "${platform_dir}" \
"${client_dir}/node_modules/@registrystack/client-${{ matrix.napi_platform }}"
ln -s "${client_dir}" \
"${smoke}/node_modules/@registrystack/client"
cp "${GITHUB_WORKSPACE}/release/scripts/smoke-registry-client-package.js" \
"${GITHUB_WORKSPACE}/release/scripts/smoke-registry-client-package.mjs" \
"${smoke}/"
(cd "${smoke}" && node smoke-registry-client-package.js)
(cd "${smoke}" && node smoke-registry-client-package.mjs)
- name: Prove production Linux Python client recipe
shell: bash
run: |
set -euo pipefail
wheel_dir="${RUNNER_TEMP}/python-client-wheel"
mkdir -p "${wheel_dir}"
release/scripts/build-linux-python-client \
--client evidence \
--target "${{ matrix.target }}" \
--compatibility manylinux_2_17 \
--zig-python "${RUNNER_TEMP}/maturin/bin/python" \
--maturin "${RUNNER_TEMP}/maturin/bin/maturin" \
--out "${wheel_dir}" \
--profile ci
shopt -s nullglob
wheels=("${wheel_dir}"/registry_evidence_client-*.whl)
if (( "${#wheels[@]}" != 1 )); then
printf 'expected exactly one audited Evidence wheel, found %s\n' \
"${#wheels[@]}" >&2
exit 1
fi
ci-result:
name: CI result
if: always()
needs:
- changes
- secrets
- platform-quality
- platform-hygiene
- platform-fuzz
- rust-policy
- rust-quality
- rust-tests
- discovery-contracts
- evidence-contracts
- evidence-postgres
- evidence-fuzz
- breg-contracts
- breg-postgres-18
- breg-wasm
- identifiers
- casework-postgres
- scheduling-postgres
- scheduling-contracts
- messaging-postgres
- messaging-contracts
- messaging-smtp
- documents-contracts
- documents-postgres
- adapter-contracts
- adapter-postgres
- coordinator-postgres
- config-conformance
- release-tool
- release-source-proof
- evidence-tutorials
- breg-tutorial
- casework-tutorial
- messaging-tutorial
- documents-tutorial
- render-tutorial
- scheduling-tutorial
- breg-evidence-composition
- evidence-anchors
- docs
- editor-extensions
- client-bindings
- release-linux-node-clients
runs-on: ubuntu-slim
timeout-minutes: 5
env:
CI_JOB_RESULTS: ${{ toJSON(needs) }}
steps:
- name: Require every selected CI job
run: |
python3 - <<'PY'
import json
import os
import sys
jobs = json.loads(os.environ["CI_JOB_RESULTS"])
failed = {
name: details["result"]
for name, details in jobs.items()
if details["result"] not in {"success", "skipped"}
}
# Review skips the heavy integration tier by design. Wherever the
# classifier selected one of these jobs, a skip is a failure.
selected_by = {
"breg-contracts": "breg_contracts",
"breg-postgres-18": "breg_contracts",
"breg-wasm": "breg_integration",
"casework-postgres": "casework_postgres",
"scheduling-postgres": "scheduling_postgres",
"evidence-tutorials": "evidence_tutorial",
"breg-tutorial": "breg_tutorial",
"casework-tutorial": "casework_tutorial",
"breg-evidence-composition": "breg_evidence_composition",
}
selection = jobs["changes"].get("outputs", {})
for name, output in selected_by.items():
if selection.get(output) == "true" and jobs[name]["result"] != "success":
failed[name] = jobs[name]["result"]
if failed:
print(f"CI jobs did not pass: {failed}", file=sys.stderr)
raise SystemExit(1)
print("Every selected CI job passed; unselected jobs were skipped.")
PY