# Phase 4 (N-04): CI locks the regression floor — the lint/format/typecheck # gates run on every push/PR, the full Electron battery on main pushes, # nightly, and manual dispatch. Roadmap: "Gate on main; nightly full battery # with artifacts on failure." # # Cost note: macos runners bill 10x Linux minutes, so the heavy battery does # NOT run on PRs — gates do. A PR that needs the battery can only get it via # a push to main or a manual dispatch (documented tradeoff, not an oversight). # # Environment risks (fix-forward, see log on red): # - Electron smoke opens real BrowserWindows. macOS runners have a WindowServer # and this works for most Electron projects, but if suites fail with # display errors the fix is a headless flag in scripts/smoke-all.sh's # electron_run (e.g. --headless=new where supported), not deleting suites. # - First battery run on a cold model cache downloads SigLIP weights (~370MB) # plus tesseract CDN data; the actions/cache below makes later runs fast. name: CI on: push: branches: [main] pull_request: schedule: # Nightly full battery, 06:00 UTC. - cron: "0 6 * * *" workflow_dispatch: concurrency: # Superseded pushes cancel their own gates; the battery never cancels — # a half-run battery proves nothing. group: ${{ github.workflow }}-${{ github.ref }}-${{ github.event_name }} cancel-in-progress: ${{ github.event_name == 'pull_request' }} jobs: gates: name: lint / format / typecheck / unit runs-on: macos-latest timeout-minutes: 20 steps: - uses: actions/checkout@v4 - name: Setup Bun uses: oven-sh/setup-bun@v2 with: bun-version: 1.3.14 - name: Setup Node uses: actions/setup-node@v4 with: node-version: lts/* - name: Install dependencies run: bun install - name: Lint run: bun run lint - name: Format check run: bun run format:check - name: Typecheck run: bunx tsc --noEmit - name: Unit tests (bun:test suite) run: bun test test/ - name: Unit tests (scene planning) run: bun run test:planning - name: Unit tests (import order) run: bun run test:import-order - name: Unit tests (MIME sniffing) run: bun run test:mime-sniff - name: Unit tests (media order) run: bun run test:media-order - name: Unit tests (pump enrichment) run: bun run test:pump-enrichment - name: Unit tests (pump transcription) run: bun run test:pump-transcription - name: Unit tests (transcript fusion) run: bun run test:transcript-fusion - name: Unit tests (transcript deep) run: bun run test:transcript-deep - name: Unit tests (dialogue rank) run: bun run test:transcript-dialogue-rank - name: Unit tests (audio probe) run: bun run test:transcribe-audio-probe - name: Unit tests (whisper model) run: bun run test:whisper-model - name: Unit tests (dialogue exact) run: bun run test:dialogue-exact battery: name: full smoke battery # Main pushes, nightly schedule, manual dispatch — never PRs (see header). if: github.event_name != 'pull_request' needs: gates runs-on: macos-latest timeout-minutes: 120 steps: - uses: actions/checkout@v4 - name: Setup Bun uses: oven-sh/setup-bun@v2 with: bun-version: 1.3.14 - name: Setup Node uses: actions/setup-node@v4 with: node-version: lts/* - name: Install dependencies run: bun install - name: Restore model cache uses: actions/cache@v4 with: # scripts/smoke-all.sh symlinks exactly this path into every # suite's temp data dir; a cold cache just downloads on demand. path: ~/Library/Application Support/scm/models key: ${{ runner.os }}-scm-models-${{ hashFiles('indexer/models.js') }} restore-keys: | ${{ runner.os }}-scm-models- - name: Full battery run: bash scripts/smoke-all.sh - name: Upload battery log on failure if: failure() uses: actions/upload-artifact@v4 with: name: scm-battery-log path: /tmp/scm-battery.log retention-days: 14