ci: test main after merge, and harden how a build is served - #26
Merged
Merged
Conversation
CI never ran on main: `push` ignored it and `pull_request` only tested a PR against main as it stood at the PR's last push. It now runs on every pull request whatever its base (stacked PRs included) and on main after each merge, which also drops the duplicate push + pull_request run per PR push. The build's one piece of build-time code, the health.json plugin, was neither typechecked (esbuild strips vite.config.ts without checking it) nor checked by output. tsconfig now includes vite.config.ts, and CI asserts the emitted dist/health.json still honours the static-artifact contract. Serving: - public/serve.json turns off serve's default directory index, however serve is started on dist/. Pinned, with the existing 404-not-index.html rule, by a test that spawns serve with ecosystem.config.cjs's own args. - nginx.conf.example: the server-level security headers reached no response, because every location sets its own add_header. They move to a snippet each location includes, with HSTS, and the page gets a Content-Security-Policy the bundle runs under with zero violations. - Phaser is its own chunk, so an app-only deploy stops invalidating ~330 kB gzip of cached vendor code. App chunks keep Vite's 500 kB warning. - The manifest no longer locks installed phones to landscape. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This was referenced Sep 21, 2026
fix(services): readers that one bad answer could break, and the tests that should have caught it
#27
Merged
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.
Part of a series from a review of the monitor. This one touches no
src/code: CI, build config, and how a build is served.CI never tested main
pushhadbranches-ignore: [main], andpull_requesttests a PR merged onto main as main stood at the PR's last push. So the tree that actually lands — and gets deployed — was never checked, while every PR push ran twice (once per event, in different concurrency groups).Now:
pushonmainonly,pull_requestwith no branch filter,workflow_dispatch. Main is tested after every merge, each PR push runs once, and a stacked PR (base = another feature branch) still gets its check. What is deliberately not covered — a branch with no open PR, and a PR whose base was retargeted without a new push — is spelled out inci.yml.The build-time code was unchecked
The health.json plugin in
vite.config.tsis the only build-time code in the repo, and Vite loads that file through esbuild, which strips types without checking them. A mistypedemitFileoption (filenameforfileName) builds cleanly and quietly takes/api/healthaway.tsconfig.jsonnow includesvite.config.ts(confirmed: that typo becomes TS2561).scripts/check-health-json.mjsruns after the build and fails on a missing or malformed document, including the static-artifact contract (timestamp === buildTime,pipeline: "none", nolastProcessedAt, a real commit).tests/check-health-json.test.tsfeeds it one wrong document per rule.Serving
servelists directories by default, as HTML or JSON, and has no CLI flag to turn it off.public/serve.json(directoryListing: false) is copied intodist/by the build, so it applies howeverserveis started ondist/.tests/static-serving.test.tsspawns the realservewithecosystem.config.cjs's own args and pins both this and the existing 404-not-index.htmlrule. Removingserve.jsonfails 2 tests; re-adding-sfails 4.add_headerinto a location that sets none of its own, and every location sets one. They now live in a snippet each location includes, plus HSTS. The API proxies hide the upstream's copies so no response carries duplicates.location /gets a Content-Security-Policy. The current bundle runs under it with zero violations (map, all tabs, tooltips, mobile drawer; 1440×900 and 375×812). An injectedonerror=probe was blocked. The comment gives Report-Only as the rollout step and explains theconnect-srccoupling: astatusUrlmoved off*.moss.landwould read as not health-checked, never as down.phaser-*.js, 330 kB gzip; app 18 kB gzip), so an app-only deploy stops invalidating it for returning visitors. Vite's size warning is global, so it is raised just above Phaser, and a small plugin re-applies the default 500 kB to app chunks. That plugin also warns if the split ever stops working.orientation: landscape, which locked installed Android phones into the cramped tablet layout, and updated the description.What is verified, and what is not
check-health-jsonon the real outputservebehaviour checked with curl on a real build:/200,/assets/and/assets404 (no HTML or JSON listing), real asset 200, missing asset 404nginx -twas not run (no nginx locally); the structure was checked by hand. The example is reference-only: the live vhost needs the same edit by hand, ideally with the CSP as Report-Only first.servereadsserve.jsonat startup, so the origin process needs a restart after the build that adds it.🤖 Generated with Claude Code