6c2f3899c8
git.karti.ai/PIG/PIG-Demo is now the source of truth, public and anonymously cloneable. Every self-link in the site and the docs repoints there — Gitea serves file paths at /src/branch/main/, not /blob/main/, so those needed rewriting rather than a hostname swap. CI moves with it. An archived GitHub repo is read-only and its Actions stop firing, so leaving the workflow there would have meant a repo whose gates silently never run. .github/ is deleted rather than kept for reference: a workflow that can never execute is worse than no workflow, because it looks like coverage. The Gitea workflow is not a copy. That runner is aarch64 and installs pnpm through corepack from `packageManager` rather than pnpm/action-setup, uses checkout@v4 and setup-node@v4, and fetches uv from astral.sh directly. It is also configured `container.network: host` — nothing here needs a service container, but the comment says so, because that setting cost the sibling repo three failed runs. The header and footer icon changed from the GitHub mark to a neutral one. A GitHub logo pointing at a Gitea instance is a small lie about where the code lives, on a site whose argument is that you can go and check it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019mt6sHQHEnEYrJZvoMCJSB
151 lines
6.1 KiB
YAML
151 lines
6.1 KiB
YAML
# Continuous integration.
|
|
#
|
|
# Two jobs, split by language, because they prove different things and the
|
|
# Python one is the slow half.
|
|
#
|
|
# What this actually gates, in order of how likely each is to catch something:
|
|
#
|
|
# 1. The two implementations of the game agree. `engine.py` and `engine.ts`
|
|
# score every ordered pair of words in the answer list — 4,603^2 = 21.2M
|
|
# feedback patterns — and their SHA-256 digests must match each other and
|
|
# the committed value. The page claims it VERIFIES a recorded run in your
|
|
# browser; that claim is only worth something if the browser's scorer and
|
|
# the environment's scorer cannot drift.
|
|
# 2. The reward measures something. `probe.py` plays seven deliberately
|
|
# crafted policies and asserts the reward orders them the way a person
|
|
# would, PER WEIGHTED COMPONENT — a component that is flat across every
|
|
# policy is measuring nothing and still moves the total. It also asserts
|
|
# neither good policy dominates the other, because the reward editor on
|
|
# the site is a claim about a real trade-off.
|
|
# 3. Every committed fixture replays through the engine and reproduces its
|
|
# own recorded rewards. A fixture that cannot be regenerated is a claim
|
|
# with no receipt behind it.
|
|
# 4. The word lists rebuild byte-identically from their committed sources.
|
|
# If they do not, the conformance digest is describing a different game.
|
|
# 5. The demo contract holds — see scripts/check-demos.mjs.
|
|
# 6. Four things that fail SILENTLY in production and nowhere else:
|
|
# prerendered routes carrying their own <head>, sitemap.xml existing,
|
|
# no blob-backed worker, and the bundle budget.
|
|
#
|
|
# NO MODEL IS EVER CALLED. Rollouts are captured by hand and committed; a gate
|
|
# that cost a paid inference call would be switched off within a month.
|
|
#
|
|
# This runner is aarch64 and configured with `container.network: host`. Nothing
|
|
# here needs a service container, so the traps that cost PIG three failed runs
|
|
# do not apply — but do not add `services:` here without reading that repo's
|
|
# workflow first.
|
|
|
|
name: ci
|
|
|
|
on:
|
|
push:
|
|
branches: [main]
|
|
pull_request:
|
|
|
|
jobs:
|
|
web:
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
|
|
- uses: actions/setup-node@v4
|
|
with:
|
|
node-version: '22'
|
|
|
|
# Corepack ships with Node and installs the exact pnpm pinned by
|
|
# `packageManager`, so CI and a laptop run the same version. The download
|
|
# prompt is disabled because a non-interactive runner cannot answer it and
|
|
# would otherwise hang until the job times out.
|
|
- name: Enable pnpm
|
|
env:
|
|
COREPACK_ENABLE_DOWNLOAD_PROMPT: '0'
|
|
run: |
|
|
corepack enable
|
|
corepack prepare --activate
|
|
test "$(pnpm -v)" = "11.21.0" || { echo "pnpm $(pnpm -v) != 11.21.0"; exit 1; }
|
|
|
|
- name: Install
|
|
env:
|
|
CI: 'true'
|
|
run: pnpm install --frozen-lockfile
|
|
|
|
# `pnpm build` ends in the prerender pass, which drives a real browser.
|
|
# Without this the build fails on its very last action, after everything
|
|
# else has gone green — the most expensive place to find a missing
|
|
# dependency.
|
|
- name: Install chromium
|
|
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
|
|
|
|
# Crawlers do not run JavaScript. Without a real per-route HTML file every
|
|
# shared link previews as the homepage, and the failure is invisible from
|
|
# inside the app.
|
|
- name: The 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 -q '<title>Word Five' dist/demos/wordle/index.html \
|
|
|| { echo "the demo route kept the homepage title"; exit 1; }
|
|
test -f dist/404.html || { echo "no 404.html"; exit 1; }
|
|
test -f dist/sitemap.xml || { echo "no sitemap.xml"; exit 1; }
|
|
|
|
# The site's CSP has no `worker-src`, so it falls back to
|
|
# `default-src 'self'` and a blob-backed worker is blocked with no console
|
|
# error — in production only. Grep for construction FROM a blob, not for
|
|
# the string anywhere: React's bundle contains it in a scheme check, and a
|
|
# check that fails on a risk which is not present 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@v4
|
|
|
|
- name: Install uv
|
|
run: |
|
|
curl -LsSf https://astral.sh/uv/install.sh | sh
|
|
echo "$HOME/.local/bin" >> "$GITHUB_PATH"
|
|
|
|
- run: uv sync --all-packages
|
|
|
|
# If these do not reproduce, every downstream number — the conformance
|
|
# digest included — is describing a different game.
|
|
- name: The 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
|
|
- run: uv run python envs/verify_fixtures.py
|
|
|
|
# The other half of the cross-language gate. Node scores the same 21.2M
|
|
# pairs and must produce the digest Python committed.
|
|
- uses: actions/setup-node@v4
|
|
with:
|
|
node-version: '22'
|
|
- name: Enable pnpm
|
|
env:
|
|
COREPACK_ENABLE_DOWNLOAD_PROMPT: '0'
|
|
run: |
|
|
corepack enable
|
|
corepack prepare --activate
|
|
- name: Install
|
|
env:
|
|
CI: 'true'
|
|
run: pnpm install --frozen-lockfile
|
|
- run: pnpm conformance
|