fix(public): enforce the top-level real-404 deploy proof in the release lane - #104
Conversation
…se lane The soft-404 guard chain had one unguarded link: the release lane's deploy-proof set (NEUTRAL_PROOFS) did not assert the top-level 404 page, even though 'real 404s' is one of the four documented live proofs. The top-level public/404.html is what terminates Cloudflare Pages' single-page-application fallback (without it every unknown URL is served as the homepage with HTTP 200 - the soft-404 PR #34 fixed). Adding it to the proof set makes the lane itself fail closed if 404.html is ever lost or regresses into a homepage clone, instead of only being caught by the post-deploy live check after publishing.
There was a problem hiding this comment.
nish3451 has reached the 50-credit limit for trial accounts. To continue receiving code reviews, upgrade your plan.
ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Free Run ID: 📒 Files selected for processing (2)
📝 WalkthroughWalkthroughThe deploy bundle now verifies that the top-level ChangesDeploy proof validation
Estimated code review effort: 2 (Simple) | ~10 minutes Mergeability Score: ⚪ Minimal · up to This change adds a release-lane safeguard ensuring the required real-404 page is present before deployment; no actionable merge-blocking risk remains beyond normal checks and review. Note 🎁 Summarized by CodeRabbit FreeYour organization is on the Free plan. CodeRabbit will generate a high-level summary and a walkthrough for each pull request. For a comprehensive line-by-line review, please upgrade your subscription to CodeRabbit Pro by visiting https://app.coderabbit.ai/login. Comment |
There was a problem hiding this comment.
nish3451 has reached the 50-credit limit for trial accounts. To continue receiving code reviews, upgrade your plan.
There was a problem hiding this comment.
nish3451 has reached the 50-credit limit for trial accounts. To continue receiving code reviews, upgrade your plan.
There was a problem hiding this comment.
nish3451 has reached the 50-credit limit for trial accounts. To continue receiving code reviews, upgrade your plan.
There was a problem hiding this comment.
nish3451 has reached the 50-credit limit for trial accounts. To continue receiving code reviews, upgrade your plan.
There was a problem hiding this comment.
nish3451 has reached the 50-credit limit for trial accounts. To continue receiving code reviews, upgrade your plan.
There was a problem hiding this comment.
Your trial has ended. Reactivate Greptile to resume code reviews.
There was a problem hiding this comment.
Your trial has ended. Reactivate Greptile to resume code reviews.
There was a problem hiding this comment.
Your trial has ended. Reactivate Greptile to resume code reviews.
There was a problem hiding this comment.
Your trial has ended. Reactivate Greptile to resume code reviews.
There was a problem hiding this comment.
Your trial has ended. Reactivate Greptile to resume code reviews.
There was a problem hiding this comment.
Your trial has ended. Reactivate Greptile to resume code reviews.
There was a problem hiding this comment.
Your trial has ended. Reactivate Greptile to resume code reviews.
There was a problem hiding this comment.
Your trial has ended. Reactivate Greptile to resume code reviews.
There was a problem hiding this comment.
Your trial has ended. Reactivate Greptile to resume code reviews.
There was a problem hiding this comment.
Your trial has ended. Reactivate Greptile to resume code reviews.
There was a problem hiding this comment.
Your trial has ended. Reactivate Greptile to resume code reviews.
There was a problem hiding this comment.
Your trial has ended. Reactivate Greptile to resume code reviews.
There was a problem hiding this comment.
Your trial has ended. Reactivate Greptile to resume code reviews.
…le with main after PRs #135/#104/#99/#112/#100/#105) The PR #130 branch had already been reconciled with origin/main through c617364 (PRs #131 + #133/#134/#132/#116). Since then origin/main moved to d4f3ef4 with six more merges (#135 #104 #99 #112 #100 #105). Pull them in so PR #130 can be reviewed against current main without weakening the capture/rollback/reverification implementation or the PR #131 fail-closed missing-secret gate. The merge resolved cleanly: - PR #130 capture/rollback logic (publish-public-site.mjs canonical_deployment, rollbackTo, restored identity re-verify, accept-again) is intact. - PR #130 test file (scripts/test-pages-release.mjs) is intact and wired into the npm ci/test scripts. - PR #131 fail-loud missing-secret workflow step in .github/workflows/deploy-public-site.yml is preserved byte-for-byte. - The new origin/main changes touch operator surfaces and the deploy bundle prep; none of them modify the publish-public-site.mjs rollback contract or the Pages token gate, so there were no conflict markers.
Finding (verified live 2026-08-12)
Every unknown URL on tinystudio.in returns HTTP 200 with the full homepage
(soft-404):
curl -s -o /dev/null -w '%{http_code}' https://tinystudio.in/no-such-page→
200, body is the homepage. Reproduced for/no-such-page,/no-such-page/,/no-such-page.html,/a/b/c/d, and/promptly/not-real, plus on the Pagesproject hostname itself.
Root cause
The live site is still the stale June-20 bundle (the deploy lane has never
succeeded - repo
gh secret listis empty and everyDeploy public siterunfails with "Missing CLOUDFLARE_API_TOKEN"). That bundle predates PR #34 and has
no top-level
404.html, so Cloudflare Pages automatically assumessingle-page-application rendering and matches every unknown path to
/withHTTP 200 (documented Pages behavior). The fix from PR #34 is correct and on
main but has never been deployed.
What this PR changes
The soft-404 guard chain had one unguarded link: the release lane's deploy-proof
set (
NEUTRAL_PROOFSinprepare-public-deploy-bundle.mjs) asserted the threepage proofs but not the real-404 proof, even though "real 404s" is one of the
four documented live proofs in
deploy-public-site.yml. The top-levelpublic/404.htmlis the exact file whose presence terminates Pages' SPAfallback.
NEUTRAL_PROOFS(title marks "not found",not the homepage title, noindex meta present), so the release lane itself
fails closed at bundle-prepare time if
404.htmlis ever lost or regressesinto a homepage clone - instead of only being caught by the post-deploy live
check after publishing.
test-public-deploy-bundle.mjspicks the proof up automatically via itsNEUTRAL_PROOFSiteration (verified: "ok top-level real 404 page (PR fix(public): serve a real 404 page so unknown URLs stop returning the homepage #34)").Verification
npm run check(the lane's pre-deploy gate): all 9 suites green, 0 failures.test-public-soft-404.mjs: 19 checks, 0 failures.test-public-deploy-bundle.mjs: 63 checks, 0 failures, new proof asserted.Remaining blocker for the live fix (not fixable from this repo)
Closing the finding on the live site requires the deploy lane to actually run,
which needs a Cloudflare Pages:Edit token that does not exist anywhere reachable:
repo secrets are empty, the fleet token (
fleet-console/cf.env) has Workers +DNS but no Pages:Edit (verified: Pages API returns auth error), Pages OAuth on
the VPS expired 2026-08-04, and no
~/.inish-cf-tokendrop-file exists. One-timeprovisioning (dashboard, ~2 min) is documented in
scripts/publish-public-site.mjs(PROVISION_MESSAGE) and PR #85(
fix/lane1-deploy-lane-dormant-until-pages-token) keeps the lane visiblydormant meanwhile. Once both secrets exist, the next main merge deploys the
bundle (which carries 404.html), and the post-deploy + nightly live checks
verify the soft-404 is gone.
Summary by CodeRabbit
Bug Fixes
noindexmetadata for search engines.Tests