What broke in production (post-#195 deploy)
Two errors surfaced in the browser console after merging the results panel to main:
1. Uncaught InvalidStateError — Recharts 0-width canvas (fixed in #196 / #197)
Failed to execute 'createPattern' on 'CanvasRenderingContext2D':
The image argument is a canvas element with a width or height of 0.
Root cause: ResponsiveContainer fired its first resize tick with width=0 while the admin panel layout was still in transition (CSS expansion on wallet unlock). Recharts tried to paint a Canvas gradient onto a 0×0 element.
2. Transient CORS block on GET /api/programs/bitrefill-2026 (no code change needed)
Response to preflight request doesn't pass access control check:
No 'Access-Control-Allow-Origin' header is present on the requested resource.
Root cause: Railway container-swap gap during deploy — old container went down before the new one was serving. stadium.joinwebzero.com was in the CORS allowlist the whole time. Resolved itself within minutes.
Why the gaps exist
Gap 1 — Mock mode hides layout-transition timing
stadium-tester runs in mock mode (VITE_USE_MOCK_DATA=true). In mock mode the admin panel is immediately visible before any data loads, so the chart always gets a non-zero measured width on first render. The production path (wallet unlock → panel CSS transition → chart mounts mid-transition) never fires in the test harness.
Fix idea: add a post-unlock page.waitForTimeout(200) + page.evaluate(() => window.__consoleErrors) assertion step to the dev:harness spec to catch errors that fire during mount, not just during expect() assertions. Or add an explicit invariant: after rendering ProgramResultsSummarySection, assert no InvalidStateError in the console.
Gap 2 — No post-deploy smoke test on production
After Railway auto-deploys from a main push, there is no automated check that:
- The server responds to a CORS preflight from
stadium.joinwebzero.com
- The client JS loads without throwing on first render
The CORS error was caught manually, not by tooling.
Fix idea: add a lightweight npm run verify:deploy script (extending the existing verify:production) that:
- Hits
OPTIONS /api/programs/bitrefill-2026 from an origin header of https://stadium.joinwebzero.com and asserts Access-Control-Allow-Origin is present
- Could be wired as a Railway deploy webhook or a GitHub Actions step on
push to main
Gap 3 — Recharts zero-width is a known footgun, not documented
The createPattern error is a well-known Recharts 2.x issue. Every future use of ResponsiveContainer risks hitting it. Not currently called out in CLAUDE.md or the improvement backlog.
Fix idea: Add a one-liner to CLAUDE.md §4 invariants: "Any <ResponsiveContainer> must include debounce={50} and style={{ minWidth: 1 }} — Recharts fires its first resize tick at width=0 during layout transitions and throws a Canvas error."
Related
What broke in production (post-#195 deploy)
Two errors surfaced in the browser console after merging the results panel to
main:1.
Uncaught InvalidStateError— Recharts 0-width canvas (fixed in #196 / #197)Root cause:
ResponsiveContainerfired its first resize tick withwidth=0while the admin panel layout was still in transition (CSS expansion on wallet unlock). Recharts tried to paint a Canvas gradient onto a 0×0 element.2. Transient CORS block on
GET /api/programs/bitrefill-2026(no code change needed)Root cause: Railway container-swap gap during deploy — old container went down before the new one was serving.
stadium.joinwebzero.comwas in the CORS allowlist the whole time. Resolved itself within minutes.Why the gaps exist
Gap 1 — Mock mode hides layout-transition timing
stadium-testerruns in mock mode (VITE_USE_MOCK_DATA=true). In mock mode the admin panel is immediately visible before any data loads, so the chart always gets a non-zero measured width on first render. The production path (wallet unlock → panel CSS transition → chart mounts mid-transition) never fires in the test harness.Fix idea: add a post-unlock
page.waitForTimeout(200)+page.evaluate(() => window.__consoleErrors)assertion step to thedev:harnessspec to catch errors that fire during mount, not just duringexpect()assertions. Or add an explicit invariant: after renderingProgramResultsSummarySection, assert noInvalidStateErrorin the console.Gap 2 — No post-deploy smoke test on production
After Railway auto-deploys from a
mainpush, there is no automated check that:stadium.joinwebzero.comThe CORS error was caught manually, not by tooling.
Fix idea: add a lightweight
npm run verify:deployscript (extending the existingverify:production) that:OPTIONS /api/programs/bitrefill-2026from an origin header ofhttps://stadium.joinwebzero.comand assertsAccess-Control-Allow-Originis presentpushtomainGap 3 — Recharts zero-width is a known footgun, not documented
The
createPatternerror is a well-known Recharts 2.x issue. Every future use ofResponsiveContainerrisks hitting it. Not currently called out in CLAUDE.md or the improvement backlog.Fix idea: Add a one-liner to CLAUDE.md §4 invariants: "Any
<ResponsiveContainer>must includedebounce={50}andstyle={{ minWidth: 1 }}— Recharts fires its first resize tick at width=0 during layout transitions and throws a Canvas error."Related