You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Purpose: this issue is the durable roadmap for TortoiseWolfe/ScriptHammer. The body below is always-current — active arc, next 3 sessions queued, backlog. Comments are the audit trail (what shipped + what changed in the roadmap, per session). The prime prompt at the top tells a fresh Claude session to read this body first.
Prime prompt (copy from here into a fresh Claude session)
/prep
Then check this issue (TortoiseWolfe/ScriptHammer#115) — its BODY is the current roadmap. Read everything below the prime prompt: active arc, next 3 sessions queued, backlog. The most recent COMMENT on this issue is the audit trail of what changed since the previous session.
Supplementary context (read if the body's roadmap references them or if the user's first message takes us off-roadmap):
1. /home/TurtleWolfe/.claude/projects/-home-TurtleWolfe-repos-ScriptHammer/memory/MEMORY.md — top of the index has the most-recently-written memories.
2. `git log --oneline -20` — what shipped in the last couple of sessions.
3. Open issues: `gh issue list --repo TortoiseWolfe/ScriptHammer --state open`
4. Open PRs: `gh pr list --repo TortoiseWolfe/ScriptHammer --state open`
Operational reminders you WILL trip over otherwise:
- Docker-first. Never `pnpm install`/`npx` on the host, never sudo. Commit from INSIDE the container (`docker compose exec -T scripthammer git commit`) so husky/lint-staged/gitleaks run; push from the host.
- **Builds go in their OWN container: `docker compose run --rm builder pnpm build`.** #293 is FIXED — builds/tests/commits no longer kill the dev server. NEVER `docker compose exec scripthammer pnpm build`.
- **`git push` RUNS A FULL CI GATE** (`.husky/pre-push` → `validate-ci.sh --quick`: type-check, lint, tests, build, chunk check) and takes MINUTES. A short timeout kills it mid-build and looks EXACTLY like a network hang — 2026-07-17 misread it as one, then as dead IPv6, before finding the hook. Background it. **Never run two pushes/builds at once** (they race `builder_next` → "Production build failed"). **Never `git push … | tail`** — you get tail's exit code, not git's (4th sighting of the pipe-exit-code trap). Verify a push landed with `gh api repos/TortoiseWolfe/ScriptHammer/branches/<b> --jq .name` — 404 means it didn't.
- **`/chatt` IS the Cesium atlas** (#292). `?diorama` opts out. **Bookmark `?notour`** or every reload flies the camera. Default path makes ZERO Overpass calls (buildings-wide.json is baked). Since #301 the twin routes are FULL-VIEWPORT (`fixed inset-0`) with glass chrome floating over the scene, and **the page cannot scroll** — if you ever see scroll there, something regressed.
- **HTTP 000 + `dns=0.000000s` is DEAD IPv6, NOT DNS.** This machine's IPv6 is broken; scripthammer.com / *.github.io / arcgisonline / api.supabase.com all lead with AAAA. **Use `curl -4`.** In the browser it looks like the PWA "Offline" page, or the atlas rendering buildings on a BLACK scene. It is INTERMITTENT — the same URL "breaks" with nothing changed.
- A BAKE corrupts the dev server's `.next` and #230's self-heal CANNOT see it (it watches 5xx; this serves 200 with a dead chunk). Signature: "Cannot read properties of undefined (reading 'call')". Remedy: `docker compose restart scripthammer`. See #298.
- Dev URL is pinned: http://127.0.0.1:3002/ScriptHammer/ (SH_PORT=3002; :3000 is RescueDogs).
- VERIFY VISUALLY, and drive the real gesture. `toBeVisible()` checks CSS, NOT occlusion — it passed for months on a wordmark buried under the navbar (#299). Use `elementFromPoint` at the element's own centre, then LOOK at a screenshot. Playwright MCP: connect via `http://127.0.0.1:3002`, NOT host.docker.internal.
- **A STALE SERVICE-WORKER CHUNK MAKES A CORRECT FIX MEASURE AS STILL-BROKEN.** The SW re-registers on EVERY load and `?cb=N` busts the page, not the chunks. Before believing any live measurement: unregister every `navigator.serviceWorker` registration + `caches.delete` all keys, then reload. On 2026-07-17 this nearly caused a "fix" to a bug that was already fixed. HMR also does not reliably pick up `src/twin/cesium/` or `src/stage/` — `docker compose restart scripthammer` too.
- To run E2E locally like CI you need a REAL root build: `DISABLE_BASE_PATH=true` is **not enough** (`.env`'s `NEXT_PUBLIC_BASE_PATH` wins at next.config.ts:15) — pass `-e NEXT_PUBLIC_BASE_PATH=` too, verify `grep -c "/ScriptHammer/_next" out/index.html` == 0, then `serve out -l 3001` + `SKIP_WEBSERVER=1 BASE_URL=http://127.0.0.1:3001` + `--no-deps`. Wrong build ⇒ Playwright silently hits the dev server and EVERY spec fails. Restore `project-detected.json` + `public/manifest.json` after.
- `window.__atlas` = `{ viewer, manifest, placed, tour, sampleEllipsoidalM, geoidOffsetM }` — Cesium is an ES module so there's no `window.Cesium`; this is the only probe handle. Read it FRESH at point of use: React StrictMode double-mounts in dev.
- **CHECK THE `CI` WORKFLOW, NOT JUST E2E.** A flake can HARD-fail the `CI` workflow (vitest fails on unhandled errors) while E2E is green — that reddened main on 46a228f and was missed by reading only the E2E rollup. After each merge: `gh run list --branch main --workflow CI --limit 1` AND read the `Test Report` job's `N flaky` line. A flake is a failed build, not a harmless green (owner's standing rule).
- **The FORK (`TortoiseWolfe/RescueDogs`, product rebranding to RaisedPaws) needs our non-3D fixes.** When you merge a general a11y/CI/auth/tooling fix here, add it to **RescueDogs#56** (the ongoing sync tracker) so a fork session can port it. NEVER port the Cesium/Three.js/atlas work. Fork also needs its own SMTP: RescueDogs#55. Bare `gh` in the RescueDogs checkout resolves to THIS upstream — always pass `--repo` (see [[roadmap_split_scripthammer_rescuedogs]]).
Then either start on the roadmap's "next session should" item, or ask the user if they want to deviate.
Stop reading here when priming — everything below is the roadmap
Roadmap
Active arc
#265 — swap Supabase → .NET, security-preserving (the owner-blocking #280 gate). The verify-and-wire increment is DONE (2026-07-18): the dormant ASP.NET Core backend (dotnet-messaging/) is now proven conformant AND gated in CI.
Status: verify-and-wire milestone SHIPPED.
PR fix(#265): .NET messaging conformance 12/12 + JWT hardening + C11 doc-truth #322 (merged, adcc42a) — .dotnet conformance 12/12 live against the running server (both providers 12/12) + Phase-1 security hardening (fail-closed demo-secret startup guard — verified Production refuses to boot; ValidateIssuer gated on SUPABASE_URL) + C11 doc-truth. The one live defect was ASP.NET Ok((object?)null) → HttpNoContentOutputFormatter → 204 → the TS provider mapped it to undefined not null; fixed by dropping that formatter.
PR ci(#265): wire pnpm test:rls into CI — RLS + dual-provider conformance gate #324 (merged, 08320e2) — .github/workflows/conformance.yml: spins up a local dual-provider stack and runs the full pnpm test:rls (107/107 — 9 RLS suites + both provider contracts). test:rls previously ran in ZERO workflows. Own per-runner local stack → no e2e-supabase mutex needed. Not yet a REQUIRED check — make it required once stable across a few main runs.
Remaining on #265/#280 (next increments, in order):
Clean-room C13 trigger port — C13 sequence assignment borrows the Supabase assign_sequence_number DB trigger; port it into an EF migration for when the .NET DB stops sharing Supabase Postgres.
GDPR-erasure-across-the-seam; the two-pipeline prod deploy (Pages front-end + container-hosted .NET).
Next 3 sessions queued
Session +1: Model City — build the 049 board slice (build-ready; #265's Phase-1 .NET security is now ALL done)
First action: ./scripts/prp-to-feature.sh model-city 049 → /speckit.specify → /speckit.clarify (theming: faithful-port vs DaisyUI; mobile scope). The .dc.html prototypes ARE the wireframe-gate mockups → embed under spec.md ## UI Mockup. Build order: sim → pure src/lib/model-city/ engine (TDD) → 5-file components → route.
Session +2:#291 — three.js in the initial vendor chunk (2.66 MB on EVERY route)
Why: the broadest live perf degrade (~18× the 150 KB budget, hits even the 3D-free homepage); independent; a small fix.
First action: add a three/@react-three/* cacheGroup mirroring the merged cesium group + a first-load JS budget test. Verify with module.nameForCondition() + a post-minify surviving string (the cesium re-export shell burned the naive regex before).
Atlas style port + live layers (the prior active arc, now deprioritized under [EPIC] Enterprise-ready: make ScriptHammer a repo you can build real work on (eval-3 → eval-1) #280) — data-correct but visually plain; src/post/tiltShift.ts + Grade → Cesium PostProcessStage (keep ?nofx/palette); then live layers (flights/weather/transit) and the feature port (trolley, warehouse GLBs, house scan, placement editor) before retiring the R3F path.
Fork (RescueDogs → RaisedPaws) — RescueDogs#56 (non-3D sync tracker: add general a11y/CI/auth/tooling fixes as they merge here; NEVER Cesium/Three.js) + RescueDogs#55 (fork's own SMTP). schlajo review email drafted in the owner's Gmail (unsent).
#265's Phase-1 is DONE and its conformance is broadened + honestly documented — verify-and-wire (#322), the Conformance CI gate (#324), least-privilege + RLS backstop (#321), and coverage expanded to 20 cases/provider incl. groups/archive/pagination/realtime C29 (#327 + #328, test:rls 129/129). The .NET messaging seam is proven, gated, security-hardened, and truthfully catalogued (docs/messaging/AUTHORIZATION-CONTRACT.md — the contract is 13 real clauses, NOT 29; C4/C6/C15–C28 were never authored — don't invent them). Next: build Model City 049 (build-ready via #320 — a change of pace) OR #291 perf. The #265 coverage tail is DONE; the next real #280 coverage work is the seam-expansion backlog (C3/groups/keys/GDPR, in the doc). New epic #326 (Chatt city-limits coverage) is in the backlog with the 2630 E Main client as its Phase-1 anchor.
Why this exists
Sessions end. The next session starts cold. Without a single canonical "what was the world like when we last stopped + here's what to do next" pointer, the new model wastes context wandering. This issue is the one URL to paste into a fresh chat. The BODY carries the plan; comments form the audit trail.
How to maintain
Run /session-prime at session-end. The skill rewrites the body to reflect what changed in the roadmap and appends a comment with the audit trail.
If the prime prompt itself becomes stale (memory file renamed, project changed shape), edit the body's prime-prompt block directly.
Purpose: this issue is the durable roadmap for TortoiseWolfe/ScriptHammer. The body below is always-current — active arc, next 3 sessions queued, backlog. Comments are the audit trail (what shipped + what changed in the roadmap, per session). The prime prompt at the top tells a fresh Claude session to read this body first.
Prime prompt (copy from here into a fresh Claude session)
Stop reading here when priming — everything below is the roadmap
Roadmap
Active arc
#265 — swap Supabase → .NET, security-preserving (the owner-blocking #280 gate). The verify-and-wire increment is DONE (2026-07-18): the dormant ASP.NET Core backend (
dotnet-messaging/) is now proven conformant AND gated in CI.Status: verify-and-wire milestone SHIPPED.
adcc42a) —.dotnetconformance 12/12 live against the running server (both providers 12/12) + Phase-1 security hardening (fail-closed demo-secret startup guard — verified Production refuses to boot;ValidateIssuergated onSUPABASE_URL) + C11 doc-truth. The one live defect was ASP.NETOk((object?)null)→ HttpNoContentOutputFormatter → 204 → the TS provider mapped it toundefinednotnull; fixed by dropping that formatter.08320e2) —.github/workflows/conformance.yml: spins up a local dual-provider stack and runs the fullpnpm test:rls(107/107 — 9 RLS suites + both provider contracts).test:rlspreviously ran in ZERO workflows. Own per-runner local stack → noe2e-supabasemutex needed. Not yet a REQUIRED check — make it required once stable across a few main runs.Remaining on #265/#280 (next increments, in order):
7c4a999). The .NET backend now connects asdotnet_app(NOSUPERUSER/NOBYPASSRLS, granted onlyauthenticated) + sets the RLS actor per request (RlsActorMiddleware: one tx +SET LOCAL ROLE authenticated+set_config('request.jwt.claims', …)), so RLS + the Security: a 1:1 recipient can rewrite the sender's message ciphertext (column-blind UPDATE RLS) #281 trigger are a live backstop. Verified 113/113 (conformance 12/12 unbroken + a raw-pgbackstop test proving the DB blocks cross-user writes at the dotnet_app seam). Follow-up: rotate the prod DB password + extend the fix(#265): .NET messaging conformance 12/12 + JWT hardening + C11 doc-truth #322 startup guard to it.13cf4fa+ PR docs(#265): messaging authorization contract catalogue + realtime C29 #328ccab3ce). Conformance now 20 cases/provider (test:rls129/129). test(#265): broaden messaging conformance beyond C7–C14/1:1 (+ fix a .NET pagination bug) #327 added getMessageById, getProfiles, markAsDelivered, pagination, archive (C5), groups (C1/C2) (+ caught/fixed a real .NEThas_more→hasMorepagination bug). docs(#265): messaging authorization contract catalogue + realtime C29 #328 added realtime C29 (deterministic — subscribe-as-outsider → authorized refetch[], no live-websocket wait; also fixed a latentSupabaseRealtimeProvider.unsubscribeasync-reject crash) +docs/messaging/AUTHORIZATION-CONTRACT.md(the canonical catalogue the code kept citing but that never existed) + doc-truth on 7 false "C1–C29" claims. KEY: "C1–C29" was a banner, not a list — only 13 clauses ever existed (C1,C2,C3,C5,C7–C14,C29); C4,C6,C15–C28 were NEVER authored. Do NOT invent them. The real remaining coverage = the seam-expansion backlog (C3 createConversation, group mgmt, keys, GDPR — named in the doc), each its own future [EPIC] Enterprise-ready: make ScriptHammer a repo you can build real work on (eval-3 → eval-1) #280 slice.assign_sequence_numberDB trigger; port it into an EF migration for when the .NET DB stops sharing Supabase Postgres.Next 3 sessions queued
Session +1: Model City — build the 049 board slice (build-ready; #265's Phase-1 .NET security is now ALL done)
model-city-prd.md+docs/prp-docs/model-city-prp.md(feature 049) merged in docs(#049): Model City / Chattanooga Digital Twin — PRD + scoped PRP #320. First honest slice: the Model City planning board (/model-city). A change of pace now that Migration anchor: swap ScriptHammer's Supabase backend for ASP.NET Core + EF Core (.NET), KDG as reference #265's Phase-1 (conformance fix(#265): .NET messaging conformance 12/12 + JWT hardening + C11 doc-truth #322 + CI gate ci(#265): wire pnpm test:rls into CI — RLS + dual-provider conformance gate #324 + least-privilege [#265] .NET messaging backend connects as postgres superuser — add a least-privilege DB role (RLS + #281 trigger as defense-in-depth) #321) is complete../scripts/prp-to-feature.sh model-city 049→/speckit.specify→/speckit.clarify(theming: faithful-port vs DaisyUI; mobile scope). The.dc.htmlprototypes ARE the wireframe-gate mockups → embed underspec.md ## UI Mockup. Build order: sim → puresrc/lib/model-city/engine (TDD) → 5-file components → route.Session +2: #291 — three.js in the initial vendor chunk (2.66 MB on EVERY route)
three/@react-three/*cacheGroup mirroring the merged cesium group + a first-load JS budget test. Verify withmodule.nameForCondition()+ a post-minify surviving string (the cesium re-export shell burned the naive regex before).Session +3: Combined Arms #48 (BF2-style squad FPS) — one SpecKit slice
features/enhancements/048-combined-arms/plan.md+tasks.md(NOT the whole 74k-token spec) and pick the first task slice. Its match server is a separate Node/TS deployable, independent of [EPIC] Enterprise-ready: make ScriptHammer a repo you can build real work on (eval-3 → eval-1) #280.docs/messaging/AUTHORIZATION-CONTRACT.md, NOT "defining C15–C28" — those clause numbers were never real.)Backlog (no scheduled position)
messaging-scrollT007-T008,payment/06-realtime-dashboard) + E2E flake: broken-links 'check meta tag images and resources' fails on external OG-image fetch (statusCode undefined → dead-IPv6/external-host class) #323 (broken-links external OG-image fetch →statusCode: undefined, the dead-IPv6/external-host class; surfaced on fix(#265): .NET messaging conformance 12/12 + JWT hardening + C11 doc-truth #322). FlipFLAKY_GATE_MODE=blockin e2e.yml once all three clear. Watch theTest Reportflaky line each merge.src/post/tiltShift.ts+ Grade → CesiumPostProcessStage(keep?nofx/palette); then live layers (flights/weather/transit) and the feature port (trolley, warehouse GLBs, house scan, placement editor) before retiring the R3F path.4708000) + a point-in-polygon clip ("nothing outside the city renders") + a boundary overlay + reach the 2630 E Main St client (absorb the separateeast-main-street-chattanoogasite into the atlas). Phase 2: full-city (~100k, LOD/tiling). New accuracy dimension (orthogonal to Chatt diorama: real-life accuracy research — registration findings, source-data gaps, upgrade paths #229/Chatt diorama: buildings render ON the river — layers are NOT aligned (verification methodology also broken) #225 registration). Deprioritized under [EPIC] Enterprise-ready: make ScriptHammer a repo you can build real work on (eval-3 → eval-1) #280.builder/runnerstages REMOVED this session (chore(#275): remove the dead builder/runner Dockerfile stages #319). Remaining: demo-secrets → composesecrets:block, addtini, broaden.dockerignore.ignoreSearchfallback; bumpCACHE_VERSION; delete deadpublic/service-worker.js. Port to RescueDogs#56..nextand Dev container: pin SH_PORT, basePath-aware healthcheck, self-heal the recurring .next corruption (500s after bake/branch switch) #230's self-heal is blind (serves 200 + dead chunk). Prefer: havepnpm bakerecycle.nexton completion.0in template literals);@spz-loader/corepinned 0.3.0 (TECHNICAL-DEBT [Gap-Audit] Stability: PaymentConsentModal Firefox focus timing + imperative/declarative hybrid #11). Outward-facing — needs owner approval.--nav-hcall sites), no repo-wide touch-target gate, atlas chromium-only WebGL coverage (main's E2E red since #297: firefox-gen 6/6 (no WebGL headless) — and 'renders the atlas' passes on a DEAD atlas #310 tradeoff), Chatt twin: import geolocated Chattanooga buildings from SketchUp 3D Warehouse, solve the optimization wall #259 warehouse (deferred), splat capture / GTFS-RT relay (Cesium 1.143 renders splats natively).Next session should
#265's Phase-1 is DONE and its conformance is broadened + honestly documented — verify-and-wire (#322), the
ConformanceCI gate (#324), least-privilege + RLS backstop (#321), and coverage expanded to 20 cases/provider incl. groups/archive/pagination/realtime C29 (#327 + #328,test:rls129/129). The .NET messaging seam is proven, gated, security-hardened, and truthfully catalogued (docs/messaging/AUTHORIZATION-CONTRACT.md— the contract is 13 real clauses, NOT 29; C4/C6/C15–C28 were never authored — don't invent them). Next: build Model City 049 (build-ready via #320 — a change of pace) OR #291 perf. The #265 coverage tail is DONE; the next real #280 coverage work is the seam-expansion backlog (C3/groups/keys/GDPR, in the doc). New epic #326 (Chatt city-limits coverage) is in the backlog with the 2630 E Main client as its Phase-1 anchor.Why this exists
Sessions end. The next session starts cold. Without a single canonical "what was the world like when we last stopped + here's what to do next" pointer, the new model wastes context wandering. This issue is the one URL to paste into a fresh chat. The BODY carries the plan; comments form the audit trail.
How to maintain
Run
/session-primeat session-end. The skill rewrites the body to reflect what changed in the roadmap and appends a comment with the audit trail.If the prime prompt itself becomes stale (memory file renamed, project changed shape), edit the body's prime-prompt block directly.