# Dev workflow

The one local runtime, deterministic worktree ports, the Claude desktop preview adapter, and how to diagnose a stale preview.

A DeepSpace app has exactly one supported local runtime:

```bash
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:

1. An explicit `--port` flag.
2. `$DEEPSPACE_PORT` in the environment.
3. A stable per-worktree port, when the checkout is a linked Git worktree (below).
4. 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:

```json
{
  "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`.

`.claude/launch.json` is **machine-local**. Keep it and `.claude/worktrees` gitignored (the scaffold already does), and never commit absolute worktree paths.

### 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:

```bash
npx deepspace dev start
```

The CLI then:

1. Upserts a `wt-<name>` entry into the **owner's** `.claude/launch.json`, pinned to the worktree's exact `cwd` and its resolved port.
2. Prints the entry name.
3. Prunes only stale `wt-*` entries - ones whose absolute `cwd` sits under that owner's `.claude/worktrees` directory and no longer exists. Every other entry, including a hand-authored `wt-*` 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:

1. **Check the server's `cwd` and port** in the preview's server listing. A server whose `cwd` is the primary checkout while you edit in a worktree is the whole bug.
2. **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.
3. **Stop the mismatched server, then start the right entry** - the printed `wt-<name>` entry for a worktree, the app entry for the primary checkout.

Never kill an unrelated process just because it holds the port you expected. Identify the server first (step 1); if the process on the port is not your dev server, pick a different port with `--port` instead.

## 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:

```bash
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](/guides/updating).

## Next steps

* [Building an app](/guides/building-an-app) - the end-to-end methodology, including one isolated checkout per line of work.
* [Testing](/guides/testing) - `test run` suites, ports, and the multi-user fixture.
* [Deployment](/concepts/deployment) - what `deploy` ships and how secrets reach production.
