name: ci on: push: branches: [main] pull_request: concurrency: group: ci-${{ github.ref }} cancel-in-progress: true jobs: web: runs-on: ubuntu-latest steps: - uses: actions/checkout@v5 - uses: pnpm/action-setup@v4 with: { version: 11.21.0 } - uses: actions/setup-node@v5 with: { node-version: 22, cache: pnpm } # Asserts the toolchain matches what package.json pins, rather than # discovering a mismatch three steps later as an unrelated build error. - name: preflight run: | test "$(pnpm -v)" = "11.21.0" || { echo "pnpm $(pnpm -v) != 11.21.0"; exit 1; } - run: CI=true pnpm install --frozen-lockfile # `pnpm build` ends in the prerender pass, which drives a real browser. # Without this the build fails at the very last step, after everything # else has passed, with a Playwright message about installing browsers. - run: pnpm exec playwright install --with-deps chromium - run: pnpm typecheck - run: pnpm check - run: pnpm test - run: pnpm build - run: node scripts/bundle-budget.mjs # The prerender pass writes a real HTML file per route. Crawlers do not # run JavaScript, so without these every shared link previews as the # homepage — assert the baked tags actually landed. - name: prerendered head is real run: | test -f dist/demos/wordle/index.html || { echo "no prerendered demo route"; exit 1; } grep -q 'og:title' dist/demos/wordle/index.html || { echo "og tags missing"; exit 1; } grep -qv 'PIG Demo — RL environments you can play' dist/demos/wordle/index.html \ || { echo "demo route kept the homepage title"; exit 1; } test -f dist/404.html || { echo "no 404.html"; exit 1; } # A blob-backed worker is blocked in production and nowhere else: the # site's CSP has no worker-src, so it falls back to default-src 'self'. # The failure is silent — the worker simply never boots. # # Grep for worker construction FROM a blob, not for the string `blob:` # anywhere. React's own bundle contains that string in a URL-scheme check, # so the broad version fails on a risk that is not present, which teaches # everyone to ignore it. - name: no blob-backed workers run: | if grep -rEo "new (Shared)?Worker\([^)]{0,80}" dist/assets/*.js \ | grep -E "blob:|createObjectURL"; then echo "a worker is constructed from a blob URL; production CSP blocks it silently" exit 1 fi echo "ok — no blob-backed worker construction in the bundle" python: runs-on: ubuntu-latest steps: - uses: actions/checkout@v5 - uses: astral-sh/setup-uv@v6 with: { enable-cache: true } - run: uv sync --all-packages # Rebuild the word lists from their committed sources and assert the # output is byte-identical. If it is not, every downstream number — # the conformance digest included — is describing a different game. - name: word lists rebuild identically run: | uv run python envs/wordle_five/words/build_words.py git diff --exit-code envs/wordle_five/words/*.json - run: uv run pytest envs/wordle_five/tests -q - run: uv run python envs/probe.py # The cross-language gate. Both halves score all 21.2M (guess, answer) # pairs; the digests must match each other and the committed value. - uses: pnpm/action-setup@v4 with: { version: 11.21.0 } - uses: actions/setup-node@v5 with: { node-version: 22, cache: pnpm } - run: CI=true pnpm install --frozen-lockfile - run: pnpm conformance # Every committed fixture must replay through the Python engine and # reproduce its own recorded rewards. A fixture that cannot be # regenerated is a claim with no receipt behind it. - run: uv run python envs/verify_fixtures.py