Summary
A freshly migrated site cannot run bun run dev on the first try: every route returns a silent 404 with an empty body — including the framework's own admin routes (/live/_meta, /.decofile) and the CMS home (/). The root cause is that the migrator intentionally does not scaffold a wrangler.jsonc, but the vite.config.ts it generates uses @cloudflare/vite-plugin, which needs a wrangler config to mount the worker in dev. With no worker mounted, Vite falls through to its default 404 for all paths — and, crucially, no error is logged, so it looks like routing is broken rather than config being absent.
This reproduces on essentially every first migration (confirmed on portal-davinci; no migrated site in the workspaces has a wrangler.jsonc).
Root cause
packages/blocks-cli/scripts/migrate/phase-scaffold.ts:53-56 deliberately skips wrangler.jsonc:
// Root config files. wrangler.jsonc is intentionally NOT generated by the migration script -- it's per-site... wired by hand on first deploy (D6.3 interim state...)
- But
phase-scaffold.ts generates a vite.config.ts whose plugin list includes cloudflare({ viteEnvironment: { name: "ssr" } }) (@cloudflare/vite-plugin). That plugin resolves the worker from wrangler.jsonc's main. With the file missing, it mounts no worker, and the deco worker entry (src/worker-entry.ts → createDecoWorkerEntry, which handles /live/_meta, /.decofile, /deco/render, and wraps the TanStack router) never runs.
- Net symptom:
curl / → HTTP 404, content-length: 0; curl /live/_meta → 404; curl /foo → 404; dev server logs nothing.
The D6.3 decision (wrangler.jsonc is per-site, wired by hand on first deploy) may be fine for deploy, but it makes local dev impossible out of the box — the two are coupled through @cloudflare/vite-plugin.
Repro
- Migrate any Fresh/Deno site (e.g.
deco-sites/portal-davinci) with the local migrator.
bun install (+ link local packages if testing unpublished framework changes).
bun run generate && bun run dev.
curl -s -o /dev/null -w "%{http_code}\n" http://localhost:<port>/ → 404 (empty body). Same for /live/_meta, /.decofile.
Confirmation
Adding a minimal wrangler.jsonc fixes the primary symptom — the worker boots and routes are handled:
After this, / renders HTTP 200 with real content (<title>Portal da Vinci | Cirurgia Robótica</title>, ~15 KB body) and /live/_meta is handled (returns 503, i.e. reached the handler, no longer 404).
Secondary first-migration blocker (surfaced only after the wrangler fix)
Once the worker boots, it fails to load with:
Failed to load url ../../.deco/loaders.gen (resolved id: ../../.deco/loaders.gen)
in src/setup/commerce-loaders.ts. Does the file exist?
src/setup/commerce-loaders.ts imports ../../.deco/loaders.gen unconditionally, but the unified bun run generate orchestrator did not emit .deco/loaders.gen.ts (it wrote blocks.gen, sections.gen, meta.gen but not loaders.gen). Running the loaders generator directly (tsx node_modules/@decocms/blocks-cli/scripts/generate-loaders.ts --site <name>) writes the file (even with 0 entries) and unblocks the boot. So the orchestrator skips emitting loaders.gen on first run while a scaffolded file imports it — a second, independent first-run gap.
Impact
Every first migration produces a site that cannot be run locally without two undocumented manual steps (create wrangler.jsonc, generate loaders.gen). The 404-on-everything symptom is especially costly because nothing is logged — it reads as a routing bug, not missing config, and sends people debugging the wrong layer.
Proposed fix
- Scaffold a minimal dev
wrangler.jsonc in phase-scaffold.ts (main → src/worker-entry.ts, compatibility_date, nodejs_compat + no_handle_cross_request_promise_resolution, a dev SITES_KV binding). If keeping D6.3's "no committed wrangler for deploy" stance, generate it as a dev-only file and/or clearly document it as the required first step in MIGRATION_REPORT.md (currently it's only mentioned tangentially, re: observability).
- Ensure the unified
generate always emits .deco/loaders.gen.ts (even with 0 entries) so the scaffolded commerce-loaders.ts import resolves — or make that import tolerate an absent file.
- Optionally, have the migrator's Verify phase check for
wrangler.jsonc + .deco/loaders.gen.ts and fail loudly with the remediation, instead of the silent dev 404.
Found while validating the #305 ctx fix end-to-end on portal-davinci; the ctx fix itself works (page renders, no ctx-undefined errors) once the site can boot.
Summary
A freshly migrated site cannot run
bun run devon the first try: every route returns a silent404with an empty body — including the framework's own admin routes (/live/_meta,/.decofile) and the CMS home (/). The root cause is that the migrator intentionally does not scaffold awrangler.jsonc, but thevite.config.tsit generates uses@cloudflare/vite-plugin, which needs a wrangler config to mount the worker in dev. With no worker mounted, Vite falls through to its default404for all paths — and, crucially, no error is logged, so it looks like routing is broken rather than config being absent.This reproduces on essentially every first migration (confirmed on
portal-davinci; no migrated site in the workspaces has awrangler.jsonc).Root cause
packages/blocks-cli/scripts/migrate/phase-scaffold.ts:53-56deliberately skipswrangler.jsonc:phase-scaffold.tsgenerates avite.config.tswhose plugin list includescloudflare({ viteEnvironment: { name: "ssr" } })(@cloudflare/vite-plugin). That plugin resolves the worker fromwrangler.jsonc'smain. With the file missing, it mounts no worker, and the deco worker entry (src/worker-entry.ts→createDecoWorkerEntry, which handles/live/_meta,/.decofile,/deco/render, and wraps the TanStack router) never runs.curl /→HTTP 404,content-length: 0;curl /live/_meta→404;curl /foo→404; dev server logs nothing.The
D6.3decision (wrangler.jsonc is per-site, wired by hand on first deploy) may be fine for deploy, but it makes local dev impossible out of the box — the two are coupled through@cloudflare/vite-plugin.Repro
deco-sites/portal-davinci) with the local migrator.bun install(+ link local packages if testing unpublished framework changes).bun run generate && bun run dev.curl -s -o /dev/null -w "%{http_code}\n" http://localhost:<port>/→404(empty body). Same for/live/_meta,/.decofile.Confirmation
Adding a minimal
wrangler.jsoncfixes the primary symptom — the worker boots and routes are handled:{ "name": "portal-davinci", "main": "src/worker-entry.ts", "compatibility_date": "2025-05-01", "compatibility_flags": ["nodejs_compat", "no_handle_cross_request_promise_resolution"], "kv_namespaces": [{ "binding": "SITES_KV", "id": "dev-sites-kv" }] }After this,
/rendersHTTP 200with real content (<title>Portal da Vinci | Cirurgia Robótica</title>, ~15 KB body) and/live/_metais handled (returns503, i.e. reached the handler, no longer404).Secondary first-migration blocker (surfaced only after the wrangler fix)
Once the worker boots, it fails to load with:
src/setup/commerce-loaders.tsimports../../.deco/loaders.genunconditionally, but the unifiedbun run generateorchestrator did not emit.deco/loaders.gen.ts(it wroteblocks.gen,sections.gen,meta.genbut notloaders.gen). Running the loaders generator directly (tsx node_modules/@decocms/blocks-cli/scripts/generate-loaders.ts --site <name>) writes the file (even with0entries) and unblocks the boot. So the orchestrator skips emittingloaders.genon first run while a scaffolded file imports it — a second, independent first-run gap.Impact
Every first migration produces a site that cannot be run locally without two undocumented manual steps (create
wrangler.jsonc, generateloaders.gen). The404-on-everything symptom is especially costly because nothing is logged — it reads as a routing bug, not missing config, and sends people debugging the wrong layer.Proposed fix
wrangler.jsoncinphase-scaffold.ts(main →src/worker-entry.ts,compatibility_date,nodejs_compat+no_handle_cross_request_promise_resolution, a devSITES_KVbinding). If keeping D6.3's "no committed wrangler for deploy" stance, generate it as a dev-only file and/or clearly document it as the required first step inMIGRATION_REPORT.md(currently it's only mentioned tangentially, re: observability).generatealways emits.deco/loaders.gen.ts(even with 0 entries) so the scaffoldedcommerce-loaders.tsimport resolves — or make that import tolerate an absent file.wrangler.jsonc+.deco/loaders.gen.tsand fail loudly with the remediation, instead of the silent dev404.Found while validating the #305 ctx fix end-to-end on
portal-davinci; the ctx fix itself works (page renders, noctx-undefined errors) once the site can boot.