Dev workflow
The one local runtime, deterministic worktree ports, the Claude desktop preview adapter, and how to diagnose a stale preview.
On this page
A DeepSpace app has exactly one supported local runtime:
npx deepspace dev start
It runs Vite and the worker together, regenerates .dev.vars, and binds one port. There is deliberately no second vite preview script in the scaffold - a preview server that bypasses the CLI would run without the SDK-managed secrets and platform wiring, so the scaffold does not offer one. Everything on this page is about making that one runtime behave predictably when you run several checkouts of the same app side by side.
Port resolution#
dev start picks its port in this order:
- An explicit
--portflag. $DEEPSPACE_PORTin the environment.- A stable per-worktree port, when the checkout is a linked Git worktree (below).
- The default,
5173.
npx deepspace test run and npx deepspace dev kill resolve the port the same way, so all three commands target the same server by default. That symmetry is load-bearing: a test run pointed at a different port silently exercises whatever server happens to be listening there - usually the primary checkout's stale code - via Playwright's server reuse, and passes for the wrong reason.
Ports in linked worktrees#
When you work on the same app in parallel - one checkout per line of work - each checkout needs its own dev server, and the servers must never collide.
DeepSpace detects linked checkouts from Git metadata, not directory names. Any registered linked worktree - created by Claude, Codex, or plain git worktree add - gets a deterministic port in the 5180-6179 band, derived by hashing its canonical checkout path. The primary checkout keeps the normal 5173 default.
The derived port is stable across runs for the same worktree path, so a worktree's server always lands in the same place without any configuration. If two worktrees happen to hash to the same port, the launch-config writer probes past ports already claimed by other entries. An explicit --port or $DEEPSPACE_PORT always wins over the derived port.
The Claude desktop preview adapter#
Claude Code's desktop preview reads .claude/launch.json to know how to start your app. A normal app entry runs the current command tree:
{
"name": "<app>",
"runtimeExecutable": "npx",
"runtimeArgs": ["deepspace", "dev", "start", "--port", "5173"],
"port": 5173
}
dev start seeds this entry on first run and keeps its port in sync when you pass an explicit --port.
Worktrees and the owning checkout#
The desktop preview tool reads only the owning checkout's launch file - a worktree's own .claude/launch.json is never read. DeepSpace treats a checkout at <owner>/.claude/worktrees/<name> as this adapter case only when Git reports both the owner and the child as registered checkouts of the same repository; a matching path string alone has no effect, so an ordinary directory that merely looks like a Claude worktree can never mutate an unrelated ancestor's launch configuration.
From inside the worktree, run once:
npx deepspace dev start
The CLI then:
- Upserts a
wt-<name>entry into the owner's.claude/launch.json, pinned to the worktree's exactcwdand its resolved port. - Prints the entry name.
- Prunes only stale
wt-*entries - ones whose absolutecwdsits under that owner's.claude/worktreesdirectory and no longer exists. Every other entry, including a hand-authoredwt-*entry pointing elsewhere, is preserved verbatim.
Start the desktop preview with the printed wt-<name> entry. Codex and ordinary Git worktrees need no worktree-specific adapter - the derived port alone keeps them isolated - though a scaffold may still ship the machine-local launch file for Claude interoperability.
Diagnosing a stale preview#
If the preview shows code you already changed, the usual cause is a server running from the wrong checkout. Diagnose it in order:
- Check the server's
cwdand port in the preview's server listing. A server whosecwdis the primary checkout while you edit in a worktree is the whole bug. - Prove it with a distinctive string. Add a unique marker string to a source file in your checkout and request that source through the dev server. If the marker is absent from the response, the wrong checkout is being served - no further guessing needed.
- Stop the mismatched server, then start the right entry - the printed
wt-<name>entry for a worktree, the app entry for the primary checkout.
Build output and the .dev.vars copy#
Cloudflare's build can emit a preview-only secrets copy at dist/<worker>/.dev.vars beside the generated worker bundle. Because dev start is the only local runtime, that second copy has no consumer - so the deepspaceBuild() plugin in the scaffold's vite.config.ts removes it after every build (the same plugin supplies the build-time app id), and deploy deletes it again before collecting artifacts. The root .dev.vars remains the single local materialization of your secrets.
If your app predates deepspaceBuild(), run:
npx deepspace@latest app update
The read-only guide reports the 2026-08-build-injected-app-id migration and names all three files to update. Apply and validate that guidance before recording its id in deepspace.migrations.json or relying on a generic vite build output as an archive or artifact - see Updating an app.
Next steps#
- Building an app - the end-to-end methodology, including one isolated checkout per line of work.
- Testing -
test runsuites, ports, and the multi-user fixture. - Deployment - what
deployships and how secrets reach production.