chore(repo): ignore local Claude and scratch outputs #355
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: Backend Tests | |
| on: | |
| push: | |
| branches: [main] | |
| pull_request: | |
| branches: [main] | |
| paths: | |
| - 'backend/**' | |
| - 'docker-compose.yml' | |
| - '.github/workflows/backend-tests.yml' | |
| workflow_dispatch: | |
| # CLAUDE.md Rule #41 mitigation — nightly must-complete run. See the | |
| # `pytest-must-complete` job below for the cost-profile rationale (per- | |
| # commit must-complete would cost ~$15/day commercial vs ~$0.07/day for | |
| # nightly; tests latency tolerance is hours, not commits). | |
| schedule: | |
| - cron: '0 6 * * *' # 06:00 UTC daily | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.ref }} | |
| cancel-in-progress: true | |
| jobs: | |
| pytest: | |
| name: Pytest (Backend) | |
| # CLAUDE.md Rule #41: this job runs under workflow-level | |
| # cancel-in-progress: true. A fast-following push may cancel an | |
| # in-flight run; the cancellation pattern is acceptable for fast | |
| # feedback on push/PR but does NOT provide latent-defect-detection | |
| # guarantees — that role is served by `pytest-must-complete` below | |
| # on the nightly + dispatch path. Restricting the trigger surface | |
| # here keeps push/PR runs lean. | |
| if: github.event_name == 'push' || github.event_name == 'pull_request' | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 30 | |
| steps: | |
| - uses: actions/checkout@v4 | |
| # docker-compose.yml has :?required on POSTGRES_PASSWORD and FIRMAE_DB_PASSWORD | |
| # (infra-secrets closure, commit 83e31c8) — seed CI-only values so | |
| # `docker compose up` does not bail out at config resolution. | |
| - name: Write CI .env | |
| run: | | |
| cat > .env <<'EOF' | |
| POSTGRES_PASSWORD=ci-only-pg-password | |
| FIRMAE_DB_PASSWORD=ci-only-firmae-password | |
| API_KEY=ci-only-api-key | |
| EOF | |
| - name: Build backend image | |
| run: docker compose build backend | |
| timeout-minutes: 15 | |
| # docker-compose.yml declares wairz_emulation_net as `external: true` — | |
| # in dev environments this is created by the operator (or by a prior | |
| # `docker network create`); in CI we create it explicitly so the | |
| # `docker compose up` doesn't bail with "network ... not found". | |
| - name: Create external emulation network | |
| run: docker network create wairz_emulation_net || true | |
| - name: Start backend + datastores | |
| run: docker compose up -d postgres redis backend --wait | |
| timeout-minutes: 10 | |
| # tests/ is excluded from the production image via backend/.dockerignore (Q9). | |
| # Copy the test tree into the running container for this CI job only. | |
| - name: Copy tests into container | |
| run: docker cp backend/tests wairz-backend-1:/app/tests | |
| # pytest + pytest-asyncio live in [dependency-groups].dev, not in the | |
| # production deps. Install them just for this job. | |
| - name: Install test runner | |
| run: | | |
| docker compose exec -T backend /app/.venv/bin/pip install --quiet \ | |
| pytest pytest-asyncio | |
| - name: Run pytest | |
| run: | | |
| docker compose exec -T -w /app \ | |
| -e PYTHONPATH=/app \ | |
| backend /app/.venv/bin/python -m pytest tests/ \ | |
| -v --tb=short --maxfail=5 | |
| timeout-minutes: 20 | |
| - name: Upload backend logs on failure | |
| if: failure() | |
| run: | | |
| docker compose logs backend --tail 500 > backend.log || true | |
| docker compose logs worker --tail 500 > worker.log || true | |
| docker compose logs postgres --tail 200 > postgres.log || true | |
| - name: Archive logs | |
| if: failure() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: backend-test-logs | |
| path: | | |
| backend.log | |
| worker.log | |
| postgres.log | |
| retention-days: 7 | |
| pytest-must-complete: | |
| # CLAUDE.md Rule #41 mitigation — nightly cron + dispatch path. | |
| # Runs the full pytest suite to completion under a per-run-id | |
| # concurrency group (`cancel-in-progress: false`) so latent | |
| # regressions surface within 24 hours regardless of push cadence on | |
| # `main`. | |
| # | |
| # Why nightly (mechanism b) and not per-commit (mechanism a): | |
| # The `pytest` job above costs ~8-10 minutes per run (docker | |
| # compose build + DB up + full pytest). Empirically the push-side | |
| # cancellation rate runs ~68% during sustained per-piece cadence | |
| # (Pattern P5, trusted contributor). Per-commit must-complete at | |
| # ~25 commits/day = ~$15-20/day commercial runner time; nightly | |
| # must-complete = ~$0.07/day. Test-defect latency is bounded by | |
| # <24h, which is acceptable for backend regressions (vs ~1 | |
| # commit for the lint surface, which costs ~$0.001/run and has | |
| # a tighter latency budget per the F823 Rule #40 incident). | |
| # | |
| # `pytest-must-complete` is INTENTIONALLY exempt from the | |
| # `pull_request.paths` filter — schedule + dispatch always run it | |
| # in full, regardless of which subpath changed since last run. | |
| name: Pytest must-complete (Rule #41, nightly) | |
| if: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 45 | |
| concurrency: | |
| group: pytest-must-complete-${{ github.run_id }} | |
| cancel-in-progress: false | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Write CI .env | |
| run: | | |
| cat > .env <<'EOF' | |
| POSTGRES_PASSWORD=ci-only-pg-password | |
| FIRMAE_DB_PASSWORD=ci-only-firmae-password | |
| API_KEY=ci-only-api-key | |
| EOF | |
| - name: Build backend image | |
| run: docker compose build backend | |
| timeout-minutes: 15 | |
| - name: Create external emulation network | |
| run: docker network create wairz_emulation_net || true | |
| - name: Start backend + datastores | |
| run: docker compose up -d postgres redis backend --wait | |
| timeout-minutes: 10 | |
| - name: Copy tests into container | |
| run: docker cp backend/tests wairz-backend-1:/app/tests | |
| - name: Install test runner | |
| run: | | |
| docker compose exec -T backend /app/.venv/bin/pip install --quiet \ | |
| pytest pytest-asyncio | |
| - name: Run pytest (nightly must-complete; no maxfail to surface every regression) | |
| run: | | |
| docker compose exec -T -w /app \ | |
| -e PYTHONPATH=/app \ | |
| backend /app/.venv/bin/python -m pytest tests/ \ | |
| -v --tb=short | |
| timeout-minutes: 30 | |
| - name: Upload backend logs on failure | |
| if: failure() | |
| run: | | |
| docker compose logs backend --tail 500 > backend.log || true | |
| docker compose logs worker --tail 500 > worker.log || true | |
| docker compose logs postgres --tail 200 > postgres.log || true | |
| - name: Archive logs | |
| if: failure() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: backend-test-logs-must-complete | |
| path: | | |
| backend.log | |
| worker.log | |
| postgres.log | |
| retention-days: 14 |