name: Replay E2E (front-back contract)

# Guards the front-back contract via record/replay (no API key in CI):
#   Layer 1 — backend golden: replay a recorded trace through the real gateway,
#             assert the SSE event sequence matches the committed golden.
#   Layer 2 — full-stack render: real Next.js frontend + real gateway (replay
#             model) + Chromium; assert the replayed turns render in the browser.
# Triggered by changes on EITHER side of the contract so a backend change can no
# longer pass without the frontend-facing checks running.

on:
  push:
    branches: ["main"]
    paths:
      - "frontend/**"
      - "backend/app/gateway/**"
      - "backend/packages/harness/**"
      - "backend/tests/fixtures/replay/**"
      - "backend/tests/replay_provider.py"
      - "backend/tests/_replay_fixture.py"
      - "backend/tests/seed_runs_router.py"
      - "backend/tests/test_replay_golden.py"
      - "backend/scripts/run_replay_gateway.py"
      - ".github/workflows/replay-e2e.yml"
  pull_request:
    types: [opened, synchronize, reopened, ready_for_review]
    paths:
      - "frontend/**"
      - "backend/app/gateway/**"
      - "backend/packages/harness/**"
      - "backend/tests/fixtures/replay/**"
      - "backend/tests/replay_provider.py"
      - "backend/tests/_replay_fixture.py"
      - "backend/tests/seed_runs_router.py"
      - "backend/tests/test_replay_golden.py"
      - "backend/scripts/run_replay_gateway.py"
      - ".github/workflows/replay-e2e.yml"

concurrency:
  group: replay-e2e-${{ github.event.pull_request.number || github.ref }}
  cancel-in-progress: true

permissions:
  contents: read

jobs:
  backend-replay-golden:
    name: Layer 1 — backend golden (no API key)
    if: github.event_name != 'pull_request' || github.event.pull_request.draft == false
    runs-on: ubuntu-latest
    timeout-minutes: 15
    steps:
      - uses: actions/checkout@v6
      - name: Set up Python
        uses: actions/setup-python@v6
        with:
          python-version: "3.12"
      - name: Install uv
        uses: astral-sh/setup-uv@v7
      - name: Install backend dependencies
        working-directory: backend
        run: uv sync --group dev
      - name: Replay golden (backend SSE contract)
        working-directory: backend
        run: PYTHONPATH=. uv run pytest tests/test_replay_golden.py -v

  fullstack-replay-render:
    name: Layer 2 — full-stack render (no API key)
    if: github.event_name != 'pull_request' || github.event.pull_request.draft == false
    runs-on: ubuntu-latest
    timeout-minutes: 25
    steps:
      - uses: actions/checkout@v6
      - name: Set up Python
        uses: actions/setup-python@v6
        with:
          python-version: "3.12"
      - name: Install uv
        uses: astral-sh/setup-uv@v7
      - name: Install backend dependencies (replay gateway)
        working-directory: backend
        run: uv sync --group dev
      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: "22"
      - name: Enable Corepack
        run: corepack enable
      - name: Use pinned pnpm version
        run: corepack prepare pnpm@10.26.2 --activate
      - name: Install frontend dependencies
        working-directory: frontend
        run: pnpm install --frozen-lockfile
      - name: Install Playwright Chromium
        working-directory: frontend
        run: npx playwright install chromium --with-deps
      - name: Full-stack replay render (DOM assertions are the gate)
        working-directory: frontend
        run: pnpm exec playwright test -c playwright.real-backend.config.ts
      - name: Upload report + render artifact
        uses: actions/upload-artifact@v4
        if: ${{ !cancelled() }}
        with:
          name: replay-render
          path: |
            frontend/playwright-report/
            frontend/test-results/
          retention-days: 7
