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
Permanent logbook for end-to-end framework verification. Pinned — never close.
How to use this issue
Testing round: Run all items top-to-bottom. Check off passes. On failure → create a bug issue with needs-triage label and link it inline with BLOCKED by #NNN.
After a fix lands: Comment noting which PR landed, which items to re-test, then reset those checkmarks and re-run.
Playground is editable: If a test needs a route/config that doesn't exist, create it in the playground. The playground exists to be tested.
Round 2 (2026-05-16): Full re-test on main @ 4228a11. Created test routes for async loader, request params, dynamic routes, context, multi-method handler, meta pages, etc.
Round 4 (2026-05-20): Created apps/production-test app with wrangler. Tested all previously-skipped production/Cloudflare items. Fixed production hydration (self-hydrating client bundles). Scaffolder tested with local file deps. New template tested in playground. 119/122 passing.
Dev server startup
1. pnpm dev starts without errors
2. Server responds on expected port
3. routes.gen.ts generated on startup with correct route map
4. framework.gen.d.ts generated with correct type declarations
Static page rendering
5. / — full HTML document: doctype, html, head, body all present
9. / — zero JS shipped (no script tags, no hydration markers)
Loader + data flow
10. /demo — loader executes, data appears in rendered HTML
11. Async loader — resolves before page renders (tested via /async-loader)
12. Loader receives request object (tested via /request-test?name=claude)
13. Loader receives params on dynamic route (tested via /blog/my-first-post)
14. Loader receives app context from createApp.context()(tested via /context-test)
15. Loader that throws — triggers error page, doesn't hang (tested via /throw-test → 500)
Slots + templates
16. /demo — all named slots render in correct template positions
17. Slot fallback content shows when slot not provided (tested via /partial-slots — main shows "No content provided.")
18. Invalid slot name — dev error with Levenshtein suggestion (tested via /typo-slot → 500 with "Did you mean headr?")
19. Multiple slots on same page all render
20. Template CSS/styles load correctly (N/A — no CSS in playground template, but mechanism works)
Hydration + interactivity
21. /demo Counter — SSR markup present before JS loads (button text "Count: 0" in server HTML)
22. Counter — JS hydrates, click increments value (browser-verified: Count increments on click)
23. Counter — only its slot has data-hydrate attribute (header/footer use data-slot, main uses data-hydrate)
24. Counter — only its slot has hydration script + JSON data block
25. Static slots on same page have zero JS (header/footer have data-slot, no hydration scripts)
26. Hydration doesn't cause content flash/mismatch (no React mismatch warnings in console)
27. Disable JS in browser — static content still readable (full HTML in server response)
28. Hydration boundary wraps interactive slot in <div data-hydrate="slotId">
29. Loader data present in <script type="application/json" data-hydrate-data="slotId"> tag
30. XSS protection — < in loader data escaped as < in hydration JSON (code-verified: escapeScriptContent in inject-hydration.ts)
31. Production asset paths — hydration <script type="module"> imports hashed /_client/assets/… URL (verified in prod build output)
32. Transitive interactivity — slot importing a component that uses useState is treated as interactive (demo/main → Counter → useState, manifest marks as true)
33. Purely static page — zero <script type="module"> hydration tags injected (verified on /)
Meta / head
34. Static meta — title and description render in <head>(tested via /meta-static)
35. Dynamic meta (fn of loaderData) — reflects loader output (tested via /meta-dynamic)
36. View transition meta tag present when hasViewTransition: true — BLOCKED: hasViewTransition accepted by renderPage but never passed from dispatchPage
37. Meta absent when not configured
API handlers (defineHandler)
38. GET /api/health — returns JSON with correct content-type
39. POST to handler — receives body, returns response (tested via /api/echo)
40. PUT/PATCH/DELETE — method routing works (tested via /api/echo)
56. Catch-all priority — catch-all matched last after static and dynamic routes (routes.gen.ts places *slug after all specific routes)
57. Route groups — (marketing)/pricing.tsx served at /pricing, group prefix stripped
58. URL-encoded segments — GET /blog/hello%20world decodes params.slug to "hello world"
59. Malformed percent-encoding — GET /blog/%ZZ doesn't throw, raw segment passed through (unit-test verified; Vite rejects malformed URLs in dev before reaching handler)
63. 500 page shows stack trace in dev mode (tested via /throw-test — stack trace visible in <pre>)
64. httpError(404) from route — renders 404 page
65. redirect("/") from route — 302 redirect, correct location header
66. Error in error page — circuit breaker returns bare HTML fallback (code-verified)
67. onError hook fires on error (code-verified — callOnError invoked in handleError)
68. Custom 403 page renders on httpError(403)(tested via /forbidden-test → 403 "Forbidden")
69. Error page render failure — console.error logged, bare HTML fallback returned (unit-test verified)
70. app.context() failure during 404 — error logged, 404 still returned (unit-test verified)
HMR / dev workflow
71. Edit route content — browser updates without full reload
72. Edit loader return value — new data appears
73. Add new route file — immediately accessible
74. Delete route file — returns 404
75. Add new template — usable by pages (tested: added minimal template to playground, /minimal-test renders with it)
76. routes.gen.ts regenerates on file add/remove
77. Syntax error in route — dev server shows error, doesn't crash
78. Fix syntax error — recovers without restart
79. Adding interactivity to a slot invalidates hydration manifest — dev server picks up change without restart (code-verified: handleHotUpdate rescans routes, invalidates manifest + virtual modules)
Navigation (MPA)
80. Click link from / to /demo — full page load, correct page (both pages return full HTML documents)
90. Generated project runs pnpm install + pnpm dev successfully (tested with local file deps — dev server starts on port 3099)
91. Generated project's index page renders (scaffolded page renders: "Welcome to your new project.")
Production build
92. pnpm build completes without errors
93. Built output is valid ESM
94. Built output runs on target (Cloudflare Workers or Node) (wrangler dev serves all routes on port 8787)
95. All routes work in production mode (all routes return 200; API returns JSON; 404 handled)
96. Hydration works in production mode (browser-verified: Counter increments on click via wrangler)
97. Static pages have zero JS in production (zero <script> tags on static / route)
98. Asset paths resolve correctly in built output (content-hashed /assets/main-B-W3Mz-R.js loads and hydrates)
99. Two-pass build produces dist/client/ with hashed JS bundles for interactive slots
100. Client build skipped when project has no interactive slots — no dist/client/ created (code-verified: returns undefined when virtualEntries is empty)
101. Server bundle contains resolved hydration asset map, no __SUNDAYCEO_HYDRATION_ASSETS__ placeholder
Platform context
102. Cloudflare entry shim passes { env, ctx } to handler (verified in built entry.cloudflare.js)
103. Context factory receives platform object
104. Custom context values available in loaders
105. Platform undefined in dev — doesn't crash
Type safety (dev experience)
106. Autocomplete works on definePage route paths (RouteMap generated with all routes)
107. Loader return type flows into defineSlots loaderData
108. Params inferred from route path string (RouteMap has { slug: string }, { id: string; postId: string })
111. Edit static slot to interactive (add useState) — dev server picks up change, hydration script appears without restart (code-verified: handleHotUpdate rescans + invalidates)
112. Edit interactive slot to static (remove useState) — hydration script disappears, zero JS (code-verified: manifest recomputed, module invalidated)
113. Hydration mismatch — SSR HTML matches client render (no React console warnings) (browser-verified on /demo — zero mismatch warnings)
114. Multiple interactive slots on same page — each hydrates independently (tested /full-hydrate — 3 boundaries, 3 data scripts, 3 module scripts)
115. Interactive slot with loaderData — JSON data block correctly serialized and deserialized (tested /demo — JSON block contains full loader output)
116. Interactive slot with conditional rendering — both branches hydrate correctly (architecture supports this — hydration wraps full slot content)
Cloudflare Workers runtime (tested via apps/production-test + wrangler)
117. wrangler dev serves SSR pages correctly (all routes return 200 with correct HTML)
118. Static assets (client JS chunks) served via Workers static assets (content-hashed JS bundle served at /assets/main-B-W3Mz-R.js)
Purpose
Permanent logbook for end-to-end framework verification. Pinned — never close.
How to use this issue
needs-triagelabel and link it inline with BLOCKED by #NNN.Log
138-entry-shim. Items 15, 32-37, 49-50 blocked by Bug: extractModule doesn't unwrap handler exports → all handler routes return 405 #157.main@ 4228a11. Created test routes for async loader, request params, dynamic routes, context, multi-method handler, meta pages, etc.189-module-architecture— added 25 items for catch-all routes, route groups, HEAD/OPTIONS, error logging, codegen CLI, two-pass production build, and hydration injection details. Items renumbered.189-module-architecture. Hydration pipeline now working end-to-end (Bug: hydration never injected — slotSources/importGraph not wired into request pipeline #164 resolved). Slot validation errors with Levenshtein suggestions working (Bug: slot validation warnings (Levenshtein typo suggestions) silently dropped #163 resolved). Stack traces showing in 500 pages (Bug: ErrorContext missing stack trace and original error #165 resolved). Items 46-47 blocked by Vite CORS middleware in dev; unit-test verified. Item 59 blocked by Vite URL parsing in dev; unit-test verified.apps/production-testapp with wrangler. Tested all previously-skipped production/Cloudflare items. Fixed production hydration (self-hydrating client bundles). Scaffolder tested with local file deps. New template tested in playground. 119/122 passing.Dev server startup
pnpm devstarts without errorsroutes.gen.tsgenerated on startup with correct route mapframework.gen.d.tsgenerated with correct type declarationsStatic page rendering
/— full HTML document: doctype, html, head, body all present/— correct charset, viewport meta tags (note: charset + viewport duplicated — cosmetic)/— page content renders inside template slots/— view source shows server-rendered HTML (not empty div)/— zero JS shipped (no script tags, no hydration markers)Loader + data flow
/demo— loader executes, data appears in rendered HTML/async-loader)requestobject (tested via/request-test?name=claude)paramson dynamic route (tested via/blog/my-first-post)createApp.context()(tested via/context-test)/throw-test→ 500)Slots + templates
/demo— all named slots render in correct template positions/partial-slots—mainshows "No content provided.")/typo-slot→ 500 with "Did you mean headr?")Hydration + interactivity
/demoCounter — SSR markup present before JS loads (button text "Count: 0" in server HTML)data-hydrateattribute (header/footer usedata-slot, main usesdata-hydrate)data-slot, no hydration scripts)<div data-hydrate="slotId"><script type="application/json" data-hydrate-data="slotId">tag<in loader data escaped as<in hydration JSON (code-verified:escapeScriptContentin inject-hydration.ts)<script type="module">imports hashed/_client/assets/…URL (verified in prod build output)useStateis treated as interactive (demo/main → Counter → useState, manifest marks as true)<script type="module">hydration tags injected (verified on/)Meta / head
<head>(tested via/meta-static)/meta-dynamic)hasViewTransition: true— BLOCKED:hasViewTransitionaccepted byrenderPagebut never passed fromdispatchPageAPI handlers (defineHandler)
GET /api/health— returns JSON with correct content-type/api/echo)/api/echo)/api/echo→x-custom: test-header)/api/echo→ 204)/→ 200, empty body)/api/health→ 200, empty body)Allow: GET, HEAD, OPTIONS(unit-test verified; Vite CORS middleware intercepts in dev)Allowlisting only defined methods (unit-test verified; Vite CORS middleware intercepts in dev)Routing
/,/demo)/blog/[slug]) — params extracted correctly/users/42/posts/99)/blog/featured→ static route)/demoand/demo/→ 200)/api/health)GET /docs/a/b/cmatches[...slug],params.slug === "a/b/c"(renders "Viewing: a/b/c")*slugafter all specific routes)(marketing)/pricing.tsxserved at/pricing, group prefix strippedGET /blog/hello%20worlddecodesparams.slugto"hello world"GET /blog/%ZZdoesn't throw, raw segment passed through (unit-test verified; Vite rejects malformed URLs in dev before reaching handler)Error pages
/throw-test— stack trace visible in<pre>)httpError(404)from route — renders 404 pageredirect("/")from route — 302 redirect, correct location headeronErrorhook fires on error (code-verified —callOnErrorinvoked in handleError)httpError(403)(tested via/forbidden-test→ 403 "Forbidden")console.errorlogged, bare HTML fallback returned (unit-test verified)app.context()failure during 404 — error logged, 404 still returned (unit-test verified)HMR / dev workflow
minimaltemplate to playground,/minimal-testrenders with it)routes.gen.tsregenerates on file add/removehandleHotUpdaterescans routes, invalidates manifest + virtual modules)Navigation (MPA)
/to/demo— full page load, correct page (both pages return full HTML documents)/demoto/— workshasViewTransitionnot wired in dispatchCodegen CLI
node dist/cli.jsgeneratesframework.gen.d.tsandroutes.gen.tsinsrc/--srcflag redirects output to specified directoryCLI scaffolder
pnpm create sundayceo my-app— creates project directory (unit-test verified)package.jsonwith name substituted (unit-test verified; placeholder{{name}}replaced)_gitignore→.gitignore) (unit-test verified)pnpm install+pnpm devsuccessfully (tested with local file deps — dev server starts on port 3099)Production build
pnpm buildcompletes without errors<script>tags on static/route)/assets/main-B-W3Mz-R.jsloads and hydrates)dist/client/with hashed JS bundles for interactive slotsdist/client/created (code-verified: returns undefined when virtualEntries is empty)__SUNDAYCEO_HYDRATION_ASSETS__placeholderPlatform context
{ env, ctx }to handler (verified in built entry.cloudflare.js)Type safety (dev experience)
definePageroute paths (RouteMap generated with all routes)defineSlotsloaderData{ slug: string },{ id: string; postId: string })Hydration edge cases (added 2026-05-17, manual testing)
/demo— zero mismatch warnings)/full-hydrate— 3 boundaries, 3 data scripts, 3 module scripts)/demo— JSON block contains full loader output)Cloudflare Workers runtime (tested via
apps/production-test+ wrangler)wrangler devserves SSR pages correctly (all routes return 200 with correct HTML)/assets/main-B-W3Mz-R.js)srcresolves to correct hashed chunk (<script type="module">import "/assets/main-B-W3Mz-R.js";</script>)<script>tags on/)Summary
Blocking bugs
Bug: slot validation warnings (Levenshtein typo suggestions) silently dropped #163 — Slot validation warnings (Levenshtein suggestions) silently droppedRESOLVED: validation throws error with suggestionBug: hydration never injected — slotSources/importGraph not wired into request pipeline #164 — Hydration never injected (slotSources/importGraph not wired into request pipeline)RESOLVED: full hydration pipeline workingBug: ErrorContext missing stack trace and original error #165 — ErrorContext missing stack trace and original errorRESOLVED: stack trace visible in 500 pageNot yet wired
hasViewTransitionaccepted byrenderPagebut not passed fromdispatchPageImplementation tracking
🤖 Generated with Claude Code