Skip to content

Dev container: pin SH_PORT, basePath-aware healthcheck, self-heal the recurring .next corruption (500s after bake/branch switch) #230

Description

@TortoiseWolfe

Symptom

Recurring during any session that re-bakes /chatt assets or switches branches while the dev container runs (hit 3× on 2026-07-08 alone):

  1. Every route starts returning 500 Internal Server Error with:
    [TypeError: Cannot read properties of undefined (reading '/_app')]
    [TypeError: Cannot read properties of undefined (reading 'call')]
    Error: ENOENT ... .next/server/vendor-chunks/@swc+helpers...
    
    The .next dev build state is corrupted. Only remedy: docker compose restart scripthammer (the entrypoint cleans .next on start).
  2. After every restart, the host port changes (32768 → 32772 → 32775 → 32781 → 32784 …), so any URL the user had open goes dead — which looks like the app broke even when it didn't.

Together these produced repeated "I can't see anything locally? Internal Server Error" moments.

Root causes (verified in config)

# Cause Where
1 Ephemeral port by default: '127.0.0.1:${SH_PORT:-0}:3000'SH_PORT unset in .env → Docker picks a random port per restart docker-compose.yml:46
2 Healthcheck ignores basePath: test: curl -f http://localhost:3000/ — with NEXT_PUBLIC_BASE_PATH=/ScriptHammer the root 404s, so the container reports unhealthy even when the app is healthy. Any auto-heal built on this check would restart-loop; conversely the real corrupt-500 state is indistinguishable docker-compose.yml:74-79
3 No self-heal for .next corruption: the entrypoint already cleans .next at container start (docker/docker-entrypoint.sh:15-19) — the fix exists but nothing invokes it automatically. Trigger correlates with bulk file changes under the running server (bake writing 6 files into public/chatt/, branch switches touching many sources) racing the HMR compiler on a WSL2 bind mount entrypoint + Next dev behavior

Fix plan (once and for all), in order

  1. Pin the port — set SH_PORT=3002 in .env (3000 is held by the RescueDogs container) and document the var + default in .env.example and CLAUDE.md. The dev URL becomes permanently http://127.0.0.1:3002/ScriptHammer/chatt/. (2-line change.)
  2. Fix the healthcheck to respect basePath — curl a path that actually exists, e.g. http://localhost:3000${NEXT_PUBLIC_BASE_PATH:-}/ (compose can't interpolate container env in test; use a tiny docker/healthcheck.sh that reads the env var at runtime). "Unhealthy" then MEANS unhealthy. (Small.)
  3. Self-heal the corrupt state — with a meaningful healthcheck, close the loop: a lightweight supervisor in the entrypoint (or a healthcheck-side counter) that, after N consecutive failures, kills the next dev process, cleans .next, and relaunches — no container churn, no port implications once Limp hammer #1 lands. Alternative: kill 1 on Nth failure + restart: unless-stopped reuses the existing entrypoint cleanup. (Small-medium; pick the in-container supervisor for zero new dependencies.)
  4. (Optional hardening) Make the bake friendlier to the watcher — write public/chatt outputs via a single directory rename instead of 6 sequential copies. Likely unnecessary once [Eval-Backlog] PAYMENT-DASHBOARD: user-facing payment dashboard route #3 lands.

Acceptance

  • Restarting the container (manually or via self-heal) leaves the dev URL unchanged.
  • docker compose ps shows healthy when routes serve 200 under the basePath, unhealthy only when they don't.
  • Running pnpm run bake (or switching branches) with the dev server up either doesn't corrupt it, or the server recovers to 200s without human intervention within ~2 minutes.
  • CLAUDE.md "Port 3000 In Use" section updated to reflect the pinned port.

Context: surfaced repeatedly during the #225/#229 chatt sessions; the corrupt-state signature and restart remedy are also recorded in the session memory.

🤖 Generated with Claude Code

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions