AI gate #4
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: AI gate | |
| on: | |
| pull_request: | |
| branches: [main] | |
| paths: | |
| - 'crates/phase-ai/**' | |
| - 'data/card-data.json' | |
| - '.cargo/config.toml' | |
| - '.github/workflows/ai-gate.yml' | |
| schedule: | |
| - cron: "0 9 * * *" | |
| workflow_dispatch: | |
| permissions: | |
| contents: read | |
| issues: write | |
| jobs: | |
| ai-gate: | |
| name: Paired-seed AI gate | |
| if: github.event_name == 'pull_request' || github.event_name == 'workflow_dispatch' | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 30 | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions-rust-lang/setup-rust-toolchain@v1 | |
| with: | |
| cache-shared-key: rust-ai-gate | |
| - name: Restore generated card data | |
| id: cardgen-cache | |
| uses: actions/cache@v4 | |
| with: | |
| path: | | |
| data/card-data.json | |
| data/card-names.json | |
| key: cardgen-${{ hashFiles('data/mtgjson/AtomicCards.json', 'crates/engine/src/**/*.rs', 'crates/engine/Cargo.toml', 'Cargo.lock') }} | |
| - name: Generate card data | |
| if: steps.cardgen-cache.outputs.cache-hit != 'true' | |
| run: | | |
| mkdir -p data/mtgjson | |
| source scripts/lib/mtgjson-fetch.sh | |
| mtgjson_download AtomicCards.json data/mtgjson/AtomicCards.json | |
| cargo run --profile tool --features cli --bin oracle-gen -- data/ --stats --names-out data/card-names.json > data/card-data.json | |
| - name: Run quick AI gate | |
| run: cargo ai-gate --games 10 | |
| ai-gate-nightly: | |
| name: Nightly AI gate drift monitor | |
| if: github.event_name == 'schedule' | |
| runs-on: ubuntu-latest | |
| # `--full-suite --games 100` consistently exceeded the prior 90m ceiling | |
| # (never completed since at least 2026-06-13). Raised to 300m — generous | |
| # headroom under GitHub's 360m hosted-runner cap — to get a first completion | |
| # and a real-duration baseline to right-size from. The ceiling only bills the | |
| # failure case; a job that finishes in 2h stops at 2h. | |
| timeout-minutes: 300 | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions-rust-lang/setup-rust-toolchain@v1 | |
| with: | |
| cache-shared-key: rust-ai-gate | |
| - name: Restore generated card data | |
| id: cardgen-cache | |
| uses: actions/cache@v4 | |
| with: | |
| path: | | |
| data/card-data.json | |
| data/card-names.json | |
| key: cardgen-${{ hashFiles('data/mtgjson/AtomicCards.json', 'crates/engine/src/**/*.rs', 'crates/engine/Cargo.toml', 'Cargo.lock') }} | |
| - name: Generate card data | |
| if: steps.cardgen-cache.outputs.cache-hit != 'true' | |
| run: | | |
| mkdir -p data/mtgjson | |
| source scripts/lib/mtgjson-fetch.sh | |
| mtgjson_download AtomicCards.json data/mtgjson/AtomicCards.json | |
| cargo run --profile tool --features cli --bin oracle-gen -- data/ --stats --names-out data/card-names.json > data/card-data.json | |
| - name: Run full AI gate | |
| id: gate | |
| continue-on-error: true | |
| run: | | |
| cargo ai-gate --full-suite --games 100 > target/ai-gate-report.md | |
| - name: Open or update drift issue | |
| if: steps.gate.outcome == 'failure' | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| run: | | |
| title="Nightly AI gate drift" | |
| body="$(cat target/ai-gate-report.md)" | |
| existing="$(gh issue list --label ai-gate-drift --state open --json number --jq '.[0].number')" | |
| if [ -n "$existing" ]; then | |
| gh issue comment "$existing" --body "$body" | |
| else | |
| gh issue create --title "$title" --label ai-gate-drift --body "$body" | |
| fi | |
| - name: Fail only on infrastructure errors | |
| if: steps.gate.outcome == 'cancelled' | |
| run: exit 1 | |
| ai-perf-gate: | |
| name: Decision-cost perf gate | |
| if: github.event_name == 'pull_request' || github.event_name == 'workflow_dispatch' | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 30 | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions-rust-lang/setup-rust-toolchain@v1 | |
| with: | |
| cache-shared-key: rust-ai-gate | |
| - name: Restore generated card data | |
| id: cardgen-cache | |
| uses: actions/cache@v4 | |
| with: | |
| path: | | |
| data/card-data.json | |
| data/card-names.json | |
| key: cardgen-${{ hashFiles('data/mtgjson/AtomicCards.json', 'crates/engine/src/**/*.rs', 'crates/engine/Cargo.toml', 'Cargo.lock') }} | |
| - name: Generate card data | |
| if: steps.cardgen-cache.outputs.cache-hit != 'true' | |
| run: | | |
| mkdir -p data/mtgjson | |
| source scripts/lib/mtgjson-fetch.sh | |
| mtgjson_download AtomicCards.json data/mtgjson/AtomicCards.json | |
| cargo run --profile tool --features cli --bin oracle-gen -- data/ --stats --names-out data/card-names.json > data/card-data.json | |
| # debug profile (authoritative): counter VALUES are profile-independent and the | |
| # shared rust-ai-gate cache is debug-warm (win-rate jobs populate it). Runs | |
| # PERF_SAMPLE_COUNT independent sample processes; compares the per-counter median (#4878). | |
| - name: Run decision-cost perf gate | |
| run: cargo ai-perf-gate | |
| ai-perf-gate-nightly: | |
| name: Nightly decision-cost perf drift monitor | |
| if: github.event_name == 'schedule' | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 30 | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions-rust-lang/setup-rust-toolchain@v1 | |
| with: | |
| cache-shared-key: rust-ai-gate | |
| - name: Restore generated card data | |
| id: cardgen-cache | |
| uses: actions/cache@v4 | |
| with: | |
| path: | | |
| data/card-data.json | |
| data/card-names.json | |
| key: cardgen-${{ hashFiles('data/mtgjson/AtomicCards.json', 'crates/engine/src/**/*.rs', 'crates/engine/Cargo.toml', 'Cargo.lock') }} | |
| - name: Generate card data | |
| if: steps.cardgen-cache.outputs.cache-hit != 'true' | |
| run: | | |
| mkdir -p data/mtgjson | |
| source scripts/lib/mtgjson-fetch.sh | |
| mtgjson_download AtomicCards.json data/mtgjson/AtomicCards.json | |
| cargo run --profile tool --features cli --bin oracle-gen -- data/ --stats --names-out data/card-names.json > data/card-data.json | |
| # debug profile (authoritative): counter VALUES are profile-independent and the | |
| # shared rust-ai-gate cache is debug-warm (win-rate jobs populate it). Runs | |
| # PERF_SAMPLE_COUNT independent sample processes; compares the per-counter median (#4878). | |
| # `cargo` build progress goes to stderr and spawned children are Stdio::null on | |
| # stdout, so the redirect captures only the binary's clean markdown table. | |
| - name: Run decision-cost perf gate | |
| id: gate | |
| continue-on-error: true | |
| run: | | |
| cargo ai-perf-gate > target/ai-perf-gate-report.md | |
| - name: Open or update drift issue | |
| if: steps.gate.outcome == 'failure' | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| run: | | |
| title="Nightly decision-cost perf drift" | |
| body="$(cat target/ai-perf-gate-report.md)" | |
| existing="$(gh issue list --label ai-perf-drift --state open --json number --jq '.[0].number')" | |
| if [ -n "$existing" ]; then | |
| gh issue comment "$existing" --body "$body" | |
| else | |
| gh issue create --title "$title" --label ai-perf-drift --body "$body" | |
| fi | |
| - name: Fail only on infrastructure errors | |
| if: steps.gate.outcome == 'cancelled' | |
| run: exit 1 |