CI #1427
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: CI | |
| on: | |
| pull_request: | |
| branches: [main, staging] | |
| push: | |
| branches: [main, staging] | |
| schedule: | |
| - cron: "17 8 * * *" | |
| workflow_dispatch: | |
| concurrency: | |
| group: ci-${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} | |
| cancel-in-progress: true | |
| env: | |
| CARGO_TERM_COLOR: always | |
| RUST_BACKTRACE: 1 | |
| RUST_TOOLCHAIN: nightly-2026-02-08 | |
| jobs: | |
| # ───────────────────────────────────────────────────── | |
| # Gate 0: Verification contract (seconds) | |
| # ───────────────────────────────────────────────────── | |
| verification-contract: | |
| name: Verification Contract (verification.v1) | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Install git hook wrappers | |
| run: bash scripts/setup-hooks.sh | |
| - name: Generate verification.v1 report | |
| env: | |
| VERIFICATION_AGENT_PROVIDER: github-actions | |
| VERIFICATION_AGENT_MODEL: ci | |
| run: | | |
| bash scripts/verification-v1-report.sh \ | |
| --session "${{ github.run_id }}" \ | |
| --out verification.v1.json \ | |
| --pretty | |
| - name: Validate verification.v1 contract | |
| run: bash scripts/verification-v1-validate.sh verification.v1.json | |
| - name: Enforce verification policy | |
| run: | | |
| jq -e ' | |
| .summary.blocking_failures == 0 and | |
| .summary.checks_failed == 0 | |
| ' verification.v1.json >/dev/null | |
| jq '{summary: .summary}' verification.v1.json | |
| - name: Upload verification artifact | |
| if: always() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: verification-v1 | |
| path: verification.v1.json | |
| # ───────────────────────────────────────────────────── | |
| # Gate 1: Fast checks (seconds) | |
| # ───────────────────────────────────────────────────── | |
| check: | |
| name: Compile & Lint | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 45 | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Install system dependencies | |
| run: sudo apt-get update && sudo apt-get install -y libz3-dev | |
| - name: Free disk space for full workspace tests | |
| run: | | |
| sudo rm -rf /usr/local/lib/android /usr/share/dotnet /opt/ghc /opt/hostedtoolcache/CodeQL || true | |
| df -h | |
| - name: Install pinned Rust toolchain | |
| uses: dtolnay/rust-toolchain@nightly | |
| with: | |
| toolchain: ${{ env.RUST_TOOLCHAIN }} | |
| components: clippy, rustfmt | |
| - name: cargo fmt --check | |
| run: cargo fmt --check | |
| - name: Cache cargo registry | |
| uses: actions/cache@v4 | |
| with: | |
| path: | | |
| ~/.cargo/registry | |
| ~/.cargo/git | |
| target/ | |
| key: ${{ runner.os }}-cargo-check-${{ hashFiles('**/Cargo.lock') }} | |
| restore-keys: ${{ runner.os }}-cargo-check- | |
| - name: cargo check --workspace | |
| run: cargo check --workspace | |
| - name: cargo clippy --workspace --all-targets | |
| run: cargo clippy --workspace --all-targets -- -D warnings | |
| # ───────────────────────────────────────────────────── | |
| # Gate 1b: Bench compile (non-PR events only) | |
| # ───────────────────────────────────────────────────── | |
| bench-build: | |
| name: Bench Build | |
| runs-on: ubuntu-latest | |
| if: github.event_name != 'pull_request' | |
| timeout-minutes: 45 | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Install system dependencies | |
| run: sudo apt-get update && sudo apt-get install -y libz3-dev | |
| - name: Install pinned Rust toolchain | |
| uses: dtolnay/rust-toolchain@nightly | |
| with: | |
| toolchain: ${{ env.RUST_TOOLCHAIN }} | |
| - name: Cache cargo registry | |
| uses: actions/cache@v4 | |
| with: | |
| path: | | |
| ~/.cargo/registry | |
| ~/.cargo/git | |
| target/ | |
| key: ${{ runner.os }}-cargo-bench-${{ hashFiles('**/Cargo.lock') }} | |
| restore-keys: ${{ runner.os }}-cargo-bench- | |
| - name: cargo bench --workspace --no-run | |
| run: cargo bench --workspace --no-run | |
| # ───────────────────────────────────────────────────── | |
| # Gate 2: Integrity checks (seconds) | |
| # ───────────────────────────────────────────────────── | |
| integrity: | |
| name: Integrity & DST Patterns | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Install pinned Rust toolchain | |
| uses: dtolnay/rust-toolchain@nightly | |
| with: | |
| toolchain: ${{ env.RUST_TOOLCHAIN }} | |
| - name: Cache cargo registry | |
| uses: actions/cache@v4 | |
| with: | |
| path: | | |
| ~/.cargo/registry | |
| ~/.cargo/git | |
| target/ | |
| key: ${{ runner.os }}-cargo-integrity-${{ hashFiles('**/Cargo.lock') }} | |
| restore-keys: ${{ runner.os }}-cargo-integrity- | |
| - name: No TODO/FIXME/HACK in production code | |
| run: | | |
| # Strip #[cfg(test)] blocks and proc macro crates before scanning | |
| FOUND=false | |
| for f in $(find crates -name '*.rs' -not -path '*/tests/*' -not -name '*_test.rs' -not -name '*_tests.rs' -not -name 'tests.rs' -not -path '*/temper-macros/*'); do | |
| # Extract only production code (stop at #[cfg(test)]) | |
| PROD_CODE="$(awk '/^#\[cfg\(test\)\]/{exit} {print}' "$f")" | |
| if echo "$PROD_CODE" | grep -E '(TODO|FIXME|XXX|HACK)\b' | grep -v '// ci-ok' | grep -qv '^[[:space:]]*//' 2>/dev/null; then | |
| echo "FAIL: $f contains TODO/FIXME/HACK" | |
| echo "$PROD_CODE" | grep -nE '(TODO|FIXME|XXX|HACK)\b' | grep -v '// ci-ok' | grep -v '^[[:space:]]*//' | |
| FOUND=true | |
| fi | |
| done | |
| if [ "$FOUND" = true ]; then exit 1; fi | |
| echo "Integrity check: OK" | |
| - name: No unwrap() in production code | |
| run: | | |
| # Strip #[cfg(test)] blocks and proc macro crates before scanning. | |
| # Exclude safe unwrap patterns: | |
| # - .read().unwrap() / .write().unwrap() / .lock().unwrap() — RwLock/Mutex | |
| # (poisoned lock = prior panic, TigerStyle fail-fast is correct) | |
| # - .with_ymd_and_hms(...).unwrap() — chrono infallible for valid dates | |
| FOUND=false | |
| for f in $(find crates -name '*.rs' -not -path '*/tests/*' -not -name '*_test.rs' -not -name '*_tests.rs' -not -name 'tests.rs' -not -path '*/temper-macros/*' -not -path '*/benches/*'); do | |
| PROD_CODE="$(awk '/^#\[cfg\(test\)\]/{exit} {print}' "$f")" | |
| FILTERED="$(echo "$PROD_CODE" | grep '\.unwrap()' | grep -v '// ci-ok' | grep -v '^[[:space:]]*//' | grep -v '\.read()\.unwrap()' | grep -v '\.write()\.unwrap()' | grep -v '\.lock()\.unwrap()' | grep -v 'with_ymd_and_hms.*\.unwrap()' || true)" | |
| if [ -n "$FILTERED" ]; then | |
| echo "FAIL: $f contains .unwrap()" | |
| echo "$FILTERED" | |
| FOUND=true | |
| fi | |
| done | |
| if [ "$FOUND" = true ]; then exit 1; fi | |
| echo "No unwrap: OK" | |
| - name: Readability ratchet | |
| run: bash scripts/readability-ratchet.sh check .ci/readability-baseline.env | |
| - name: Storage dispatch boundary ratchet | |
| run: bash scripts/check-storage-dispatch-boundary.sh | |
| # DST pattern scan DISABLED (2026-02-17) | |
| # Reason: Grep-based pattern matching is too brittle and caused 5+ consecutive | |
| # CI failures from false positives. The DST reviewer agent (.claude/agents/dst-reviewer.md) | |
| # performs semantic analysis which is strictly superior to regex scanning. | |
| # The pattern scan will be re-evaluated once the DST architecture refactor | |
| # (shared effect application) is complete. | |
| # See: .progress/012_20260217_dst_architecture_fix.md | |
| - name: Dependency isolation | |
| run: | | |
| # Check temper-jit has no production dep on temper-verify | |
| if cargo tree --no-dev -p temper-jit 2>/dev/null | grep -q temper-verify; then | |
| echo "FAIL: temper-jit has production dependency on temper-verify" | |
| exit 1 | |
| fi | |
| # Check no stateright/proptest in production crates | |
| for crate in temper-jit temper-server temper-runtime; do | |
| if cargo tree --no-dev -p "$crate" 2>/dev/null | grep -qE 'stateright|proptest'; then | |
| echo "FAIL: $crate production binary includes stateright or proptest" | |
| exit 1 | |
| fi | |
| done | |
| echo "Dependency isolation: OK" | |
| # ───────────────────────────────────────────────────── | |
| # Gate 3: Full test suite (minutes) | |
| # ───────────────────────────────────────────────────── | |
| test: | |
| name: Tests | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 60 | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: Install system dependencies | |
| run: sudo apt-get update && sudo apt-get install -y libz3-dev | |
| - name: Install pinned Rust toolchain | |
| uses: dtolnay/rust-toolchain@nightly | |
| with: | |
| toolchain: ${{ env.RUST_TOOLCHAIN }} | |
| - name: Cache cargo registry | |
| uses: actions/cache@v4 | |
| with: | |
| path: | | |
| ~/.cargo/registry | |
| ~/.cargo/git | |
| key: ${{ runner.os }}-cargo-test-${{ hashFiles('**/Cargo.lock') }} | |
| restore-keys: ${{ runner.os }}-cargo-test- | |
| - name: Install wasm target for GEPA test modules | |
| run: rustup target add wasm32-unknown-unknown | |
| - name: Build GEPA test WASM modules | |
| run: | | |
| for module in gepa-replay gepa-reflective gepa-score gepa-pareto gepa-verify; do | |
| (cd "wasm-modules/$module" && cargo build --target wasm32-unknown-unknown --release) | |
| done | |
| - name: cargo test --workspace -- --skip dst_ | |
| run: cargo test --workspace -- --skip dst_ | |
| # spec_validate_endpoint declares required-features = ["observe"]. The | |
| # workspace run above usually still covers it via feature unification | |
| # (temper-cli/temper-mcp enable temper-server's observe feature), but | |
| # that coverage is incidental — it evaporates if those edges change. | |
| # Run the observe-gated tests explicitly so coverage is guaranteed. | |
| - name: cargo test observe-gated tests | |
| run: | | |
| cargo test -p temper-server --features observe --test spec_validate_endpoint | |
| cargo test -p temper-server --features observe --lib observe:: | |
| # ───────────────────────────────────────────────────── | |
| # Gate 3b: DST/platform coverage (matrixed) | |
| # ───────────────────────────────────────────────────── | |
| dst-platform-tests: | |
| name: DST/Platform Tests (${{ matrix.suite }}) | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 90 | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - suite: core | |
| command: cargo test -p temper-server --test dst_concurrency_retry --test dst_hotswap --test dst_lifecycle --test dst_multi_tenant --test dst_persistence | |
| - suite: platform-boot | |
| command: cargo test -p temper-server --test dst_platform_boot | |
| - suite: platform-consistency | |
| command: cargo test -p temper-server --test dst_platform_cedar --test dst_platform_index --test dst_platform_rollback | |
| - suite: platform-random | |
| command: cargo test -p temper-server --test dst_platform_random | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Install system dependencies | |
| run: sudo apt-get update && sudo apt-get install -y libz3-dev | |
| - name: Install pinned Rust toolchain | |
| uses: dtolnay/rust-toolchain@nightly | |
| with: | |
| toolchain: ${{ env.RUST_TOOLCHAIN }} | |
| - name: Cache cargo registry | |
| uses: actions/cache@v4 | |
| with: | |
| path: | | |
| ~/.cargo/registry | |
| ~/.cargo/git | |
| target/ | |
| key: ${{ runner.os }}-cargo-dst-${{ matrix.suite }}-${{ hashFiles('**/Cargo.lock') }} | |
| restore-keys: ${{ runner.os }}-cargo-dst-${{ matrix.suite }}- | |
| - name: cargo test DST/platform suite | |
| env: | |
| TEMPER_DST_RANDOM_MODE: ${{ matrix.suite == 'platform-random' && github.event_name == 'pull_request' && 'smoke' || 'full' }} | |
| run: ${{ matrix.command }} | |
| # ───────────────────────────────────────────────────── | |
| # Gate 4: Spec verification (minutes) | |
| # ───────────────────────────────────────────────────── | |
| verify-specs: | |
| name: Spec Verification (L0-L3) | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 30 | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Install system dependencies | |
| run: sudo apt-get update && sudo apt-get install -y libz3-dev | |
| - name: Install pinned Rust toolchain | |
| uses: dtolnay/rust-toolchain@nightly | |
| with: | |
| toolchain: ${{ env.RUST_TOOLCHAIN }} | |
| - name: Cache cargo registry | |
| uses: actions/cache@v4 | |
| with: | |
| path: | | |
| ~/.cargo/registry | |
| ~/.cargo/git | |
| target/ | |
| key: ${{ runner.os }}-cargo-verify-${{ hashFiles('**/Cargo.lock') }} | |
| restore-keys: ${{ runner.os }}-cargo-verify- | |
| - name: Build temper-cli | |
| run: cargo build -p temper-cli | |
| - name: Verify all spec directories | |
| run: | | |
| # Find all directories containing .ioa.toml files | |
| SPEC_DIRS=$(find . -name '*.ioa.toml' -exec dirname {} \; | sort -u) | |
| if [ -z "$SPEC_DIRS" ]; then | |
| echo "No spec directories found — skipping verification" | |
| exit 0 | |
| fi | |
| ALL_PASSED=true | |
| for dir in $SPEC_DIRS; do | |
| # Only verify dirs that also have a CSDL model | |
| if [ -f "$dir/model.csdl.xml" ]; then | |
| echo "Verifying specs in $dir..." | |
| if ! cargo run -p temper-cli -- verify --specs-dir "$dir"; then | |
| echo "FAIL: Spec verification failed for $dir" | |
| ALL_PASSED=false | |
| fi | |
| fi | |
| done | |
| if [ "$ALL_PASSED" = false ]; then exit 1; fi | |
| echo "All specs verified: OK" | |
| # ───────────────────────────────────────────────────── | |
| # Gate N: Instrumentation hygiene (ADR-0052) | |
| # Fails when a metric registration has no emission site. | |
| # ───────────────────────────────────────────────────── | |
| instrumentation-hygiene: | |
| name: Instrumentation Hygiene (ADR-0052) | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Install rust toolchain | |
| uses: dtolnay/rust-toolchain@master | |
| with: | |
| toolchain: ${{ env.RUST_TOOLCHAIN }} | |
| - uses: actions/cache@v4 | |
| with: | |
| path: | | |
| ~/.cargo/bin | |
| ~/.cargo/registry | |
| ~/.cargo/git | |
| target/ | |
| key: ${{ runner.os }}-cargo-instrumentation-${{ hashFiles('**/Cargo.lock') }} | |
| restore-keys: ${{ runner.os }}-cargo-instrumentation- | |
| - name: Run check_instrumentation | |
| run: cargo run -q -p temper-server --bin check_instrumentation |