ci: gate PRs, and actually enforce the cross-origin invariant - #16
Merged
Conversation
tests/bundle.test.ts was running nowhere. It skips itself when dist/ is absent, and the only job that creates dist/ (build) never ran tests -- the test job runs npm test with no bundle present. So the guard behind "the one architectural invariant" in CLAUDE.md has been silently inert on main and on the nightly, and PRs had no CI at all: pages.yml triggers only on push to main, cron and dispatch. Verified by mutation: pointing sizesUrl() at a releases/download URL now fails the suite (exit 1), and passes again when reverted. New ci.yml, pull_request only: npm ci -> npm run build:app -> npm test build:app is tsc -b && vite build with no prebuild lifecycle hook, so PR CI needs no gh CLI, no GH_TOKEN, no API calls and no downloads -- 2.0s of build, 0.5s of tests. The prebuild stays out of PR CI on purpose: its logic is covered by the mocked unit tests plus tests/live.test.ts under RUN_LIVE_TESTS=1. package.json keeps `build` as the sole caller of the prebuild hook, with no explicit `npm run prebuild &&`, so the double-invocation bug of 45c1740 cannot return; verified that `npm run build` fires it exactly once and `npm run build:app` not at all. ci.yml is a separate workflow rather than a pull_request trigger on pages.yml for two reasons. pages.yml's `concurrency: group: pages` is repo-wide, so PRs sharing it would cancel in-flight deploys -- merging #15 killed #14's build 38 minutes in exactly this way. And pages.yml holds pages:write / id-token:write, which a PR run should never carry; ci.yml is contents:read only, so a PR structurally cannot deploy, and fork PRs work unchanged. pages.yml also gains `npm test` after `npm run build`, so main and the nightly get the invariant too (~2s), and switches to cancel-in-progress: false. The build produces the entire data set; cancelling one discards its downloads and leaves the site stale, and queueing is affordable now that batched fetches put the build in the ~13 minute range. CLAUDE.md corrected -- it claimed the bundle test "gates in CI (after npm run build)", which was not true of any job. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013R9YDQbB9GbMYWuyXUoMBM
PR Summary by QodoGate pull requests and enforce production bundle invariants
AI Description
Diagram
High-Level Assessment
Files changed (4)
|
Code Review by Qodo🐞 Bugs (0) 📘 Rule violations (0) 📎 Requirement gaps (0)
Great, no issues found!Qodo reviewed your code and found no material issues that require reviewTip of the day💡 Did you know, you can ask Qodo to dismiss a finding you disagree with, with your reason on record |
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The headline is not "PRs have no CI"
tests/bundle.test.ts— the guard behind what CLAUDE.md calls "the one architectural invariant" — has been running nowhere. Not on PRs, not onmain, not on the nightly.It skips itself when
dist/is absent, and the only job that createsdist/never runs tests:pages.ymldist/present?testnpm ci && npm testbuildnpm ci && npm run builddeployCLAUDE.md asserted it "gates in CI (after
npm run build)". No job did that. Separately,pages.ymltriggers only on push tomain, cron, and dispatch — so PRs got no CI at all.Verified by mutation, not by inspection: pointing
sizesUrl()at areleases/download/...URL now fails the suite (exit 1), and passes again on revert. Two earlier attempts at that mutation passed — once because Vite tree-shook the unreachable constant, once becausetsc -bfailed and&&short-circuited, leaving a staledist/for the test to validate. Both traps are now written into CLAUDE.md.The change
New
ci.yml,pull_requestonly:npm ci→npm run build:app→npm test.build:appistsc -b && vite buildwith noprebuildlifecycle hook, so PR CI needs noghCLI, noGH_TOKEN, no API calls and no downloads — 2.0s of build, 0.5s of tests,npm cidominating. The prebuild stays out of PR CI deliberately: it's slow, needs a token, and its logic is already covered by the mocked unit tests plustests/live.test.tsunderRUN_LIVE_TESTS=1.package.jsonkeepsbuildas the sole caller of the prebuild hook, with no explicitnpm run prebuild &&anywhere, so the double-invocation bug of 45c1740 can't return. Verified in isolation:npm run buildfires it exactly once,npm run build:appnot at all.Why a separate workflow, not a
pull_requesttrigger onpages.yml:pages.yml'sconcurrency: group: pagesis repo-wide. PRs sharing it would cancel in-flight deploys — merging perf(prebuild): batch asset fetches into parallel curl invocations #15 killed fix(prebuild): page /releases at 20, not the per_page=100 cap #14's build 38 minutes in, exactly this way.pages.ymlholdspages: write/id-token: write. A PR run should never carry those.ci.ymliscontents: readonly, so a PR structurally cannot deploy, and fork PRs work unchanged.pages.ymlalso gains:npm testafternpm run build, somainand the nightly get the invariant too (~2s).cancel-in-progress: false. The build produces the entire data set; cancelling one discards its downloads and leaves the site stale. Queueing is affordable now that perf(prebuild): batch asset fetches into parallel curl invocations #15 puts the build in the ~13 min range.Verification
bundle.test.tsamong the skips — the 5 invariant tests now actually execute.🤖 Generated with Claude Code
https://claude.ai/code/session_013R9YDQbB9GbMYWuyXUoMBM