A bake corrupts the dev server's .next, and #230's self-heal cannot see it
Hit three times in one session (2026-07-16), each time costing a diagnosis from scratch. The third time it looked exactly like a code bug and sent me hunting a dependency override that was innocent.
Symptom
Every route serves HTTP 200. The page renders its static shell — nav, footer — and nothing else. The console shows:
TypeError: Cannot read properties of undefined (reading 'call')
at options.factory (.../_next/static/chunks/webpack.js:704:31)
at __webpack_require__ (.../webpack.js:29:33)
Webpack's __webpack_require__ found an undefined module factory — a stale chunk graph, not a code error.
Trigger
Bulk file writes under the running dev server. docker/docker-entrypoint.sh's own comment already names it:
"Bulk file changes under the running dev server (bakes, branch switches) racing the HMR compiler on the WSL2 bind mount corrupt .next"
A pnpm bake rewrites six public/twins/chatt/* artifacts. A multi-file edit does it too.
Why the watchdog is blind
#230's supervisor triggers on sustained HTTP 5xx. This failure serves 200 and dies in the browser. Confirmed while every route's client JS was dead:
/chatt/ -> HTTP 200
[self-heal] lines in the last 15m: 0
So the supervisor never fires and the page stays broken indefinitely, with no recovery and no signal.
#293 fixed builds clobbering .next (production builds now run in their own container). Bakes still do it, and this shape of the failure is invisible to the watchdog.
Remedy today
docker compose restart scripthammer — reliably fixes it. The cost is that you have to know that, and the symptom impersonates a code bug convincingly.
Options (the issue should decide — not obvious)
- Widen the self-heal probe beyond status codes.
scripts/check-chunks-parse.mjs already exists and is exactly this check — fetch a known chunk, parse it, recycle .next if it fails. Tempting because the tool is already written, but it makes the healthcheck heavier and runs on every interval.
- Have the bake recycle
.next when it finishes. Narrower, cheaper, and targets the known trigger — but only that trigger; a multi-file edit still corrupts silently.
- Just document it in CLAUDE.md ("restart after a bake"). Cheapest; relies on humans remembering, which is what failed three times today.
Why it matters beyond dev comfort
This is the same class as #294 (webpack emits something the browser can't use, while every gate stays green) and #288 (all suites green, product unusable). The signal says fine; the product is dead. That pattern has now cost this repo three separate outages-of-a-kind.
A bake corrupts the dev server's
.next, and #230's self-heal cannot see itHit three times in one session (2026-07-16), each time costing a diagnosis from scratch. The third time it looked exactly like a code bug and sent me hunting a dependency override that was innocent.
Symptom
Every route serves HTTP 200. The page renders its static shell — nav, footer — and nothing else. The console shows:
Webpack's
__webpack_require__found an undefined module factory — a stale chunk graph, not a code error.Trigger
Bulk file writes under the running dev server.
docker/docker-entrypoint.sh's own comment already names it:A
pnpm bakerewrites sixpublic/twins/chatt/*artifacts. A multi-file edit does it too.Why the watchdog is blind
#230's supervisor triggers on sustained HTTP 5xx. This failure serves 200 and dies in the browser. Confirmed while every route's client JS was dead:
So the supervisor never fires and the page stays broken indefinitely, with no recovery and no signal.
#293 fixed builds clobbering
.next(production builds now run in their own container). Bakes still do it, and this shape of the failure is invisible to the watchdog.Remedy today
docker compose restart scripthammer— reliably fixes it. The cost is that you have to know that, and the symptom impersonates a code bug convincingly.Options (the issue should decide — not obvious)
scripts/check-chunks-parse.mjsalready exists and is exactly this check — fetch a known chunk, parse it, recycle.nextif it fails. Tempting because the tool is already written, but it makes the healthcheck heavier and runs on every interval..nextwhen it finishes. Narrower, cheaper, and targets the known trigger — but only that trigger; a multi-file edit still corrupts silently.Why it matters beyond dev comfort
This is the same class as #294 (webpack emits something the browser can't use, while every gate stays green) and #288 (all suites green, product unusable). The signal says fine; the product is dead. That pattern has now cost this repo three separate outages-of-a-kind.