Skip to content

feat(ci): check in a browser that the site renders, before and after merges - #12236

Merged
MarkusNeusinger merged 4 commits into
mainfrom
feat/site-smoke-after-merge
Oct 22, 2026
Merged

MarkusNeusinger merged 4 commits into
mainfrom
feat/site-smoke-after-merge

Conversation

@MarkusNeusinger

Copy link
Copy Markdown
Owner

Summary

  • On 2026-10-22 vite 8.1.6 (deps(npm): Bump vite from 8.0.16 to 8.1.6 in /app #12225) passed every PR check and its deploy, and left every page on anyplot.ai blank for about 45 minutes (Rolldown chunk cycle, TypeError: t is not a function at module load; fixed by fix(app): bump vite to 8.3.5 to fix blank site (runtime chunk cycle) #12235). Nothing in CI or the post-merge routine ran the bundle.
  • New scripts/site-smoke.mjs: loads /, /plots and /scatter-basic in headless Chromium and fails on a status other than 200, an uncaught page error, or an empty #root; checks api.anyplot.ai/health. It never downloads a browser (CHROME_PATH, the cloud container's Chromium, or the Playwright MCP's).
  • Before the merge: the app-image job of ci-image.yml runs it against the built gate-off container with the runner's Google Chrome, whenever the container's inputs change (package.json, yarn.lock, vite.config.ts, ...), which is every frontend dependency bump.
  • After the merge: a new CLAUDE.md rule (owner request, 2026-10-22) with the recipe in /open-pr §4 and the Dependabot batch command: after every merge, once its deploys finish, run the smoke against production; a FAIL is an incident and stops further merges. The Dependabot command also merges lock-file PRs one at a time and smokes after each app/ bump. The incident narrative is in .claude/guardrails.md.

Plan

N/A. The owner asked on 2026-10-22, after the outage, for a rule or skill so a blank site never goes unnoticed again.

Test plan

  • Local app image built like CI (VITE_API_URL=https://api.anyplot.ai), gate-off container on :8081, SITE=http://localhost:8081 API=none MIN_TEXT=50 node scripts/site-smoke.mjs /: OK (text=2736, one CORS console error reported as a warning)
  • Same against an image built from 20c1457 (vite 8.1.6, the broken state): FAIL ... text=0 ... pageerror: t is not a function, exit 1
  • Against production: all four checks OK
  • uv run pytest tests/unit/workflows (766 passed), uv run ruff check ., changelog check
  • This PR's own Build app image and smoke the origin gate run exercises the new step on the runner (ci-image.yml changes trigger the job)
  • No live verification loop exists for .github/workflows/ beyond this PR's own run; the CLAUDE.md rule has no twin in .github/copilot-instructions.md, which does not carry the merge routine.

🤖 Generated with Claude Code

https://claude.ai/code/session_01GdMSqLR5ww4ji74EUSmk9M

…merges

On 2026-10-22 vite 8.1.6 (#12225) passed every PR check and its deploy,
and left every page on anyplot.ai blank for about 45 minutes: Rolldown's
runtime helper sat in a chunk cycle and the bundle threw `TypeError: t is
not a function` at module load (fixed by #12235). The app-image job only
proved that nginx serves the shell.

scripts/site-smoke.mjs loads pages in headless Chromium and fails on a
status other than 200, an uncaught page error, or an empty #root, and
checks the API's /health. Two layers use it:

- the app-image job runs it against the built gate-off container, with the
  runner's Google Chrome, whenever the container's inputs change, so a
  dependency bump that breaks the boot fails its PR;
- a CLAUDE.md rule, with the recipe in the open-pr skill and the Dependabot
  batch command, runs it against production after every merge; a FAIL is
  an incident and stops further merges.

Checked locally: against the current app image it passes (one CORS console
error from the foreign origin, reported as a warning only), against an
image built from 20c1457 (vite 8.1.6) it fails with `pageerror: t is not
a function` and an empty page, against production it passes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GdMSqLR5ww4ji74EUSmk9M
Copilot AI balanced review requested due to automatic review settings October 22, 2026 19:44

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The smoke can accept failed routes, misclassify browser-launch failures, and verify an older deployment under the fixed-delay guidance.

3 open issues
What changed in this PR

Adds browser smoke checks to catch runtime failures that image builds and deployment checks missed during the Vite-related outage.

Changes:

  • Adds a Chromium script that checks page responses, uncaught errors, rendered text, and API health.
  • Runs the smoke against the built app container in CI.
  • Documents post-merge verification and sequential Dependabot merge checks.
File Description
scripts/​site-smoke.mjs Implements browser rendering and API health checks.
docs/​reference/​repository.md Lists the new smoke script.
CLAUDE.md Requires browser verification after merges.
changelog.d/​site-smoke-after-merge.md Records the new verification process.
agentic/​commands/​dependabot.md Adds sequential merge and post-deploy checks.
.github/​workflows/​ci-image.yml Runs the browser smoke against the built container.
.claude/​skills/​open-pr/​SKILL.md Documents smoke execution and failure handling.
.claude/​guardrails.md Explains the outage and verification rationale.

Review effort: Balanced

Comment thread .claude/skills/open-pr/SKILL.md Outdated
Comment thread scripts/site-smoke.mjs Outdated
Comment thread scripts/site-smoke.mjs Outdated
MarkusNeusinger and others added 2 commits October 22, 2026 22:00
The companion-file test requires every guardrails.md section to name the
CLAUDE.md obligations it elaborates.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GdMSqLR5ww4ji74EUSmk9M
…t start

Copilot review of #12236:
- the app's error boundary keeps the masthead and footer around a failed
  route, so the text threshold alone could pass it; the smoke now waits for
  the network to settle and fails on the boundary's text;
- a browser that exists but will not launch checked nothing, so it exits 2,
  not 1 (an incident);
- elapsed time does not prove a deploy is serving: the recipe waits for the
  watcher's SUCCESS, and without gcloud compares the entry bundle the script
  now prints, reporting the check as unconfirmed until it changes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GdMSqLR5ww4ji74EUSmk9M
@codecov

codecov Bot commented Oct 22, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@MarkusNeusinger
MarkusNeusinger merged commit 33f9348 into main Oct 22, 2026
15 checks passed
@MarkusNeusinger
MarkusNeusinger deleted the feat/site-smoke-after-merge branch October 22, 2026 20:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants