# Check what's already running

## Resolve the target host first

The dev-server URL the tests will hit is a **recorded value, not a guess** — resolve it before probing anything.

1. Read the cached host with `muggle-local-last-host-get`. It reads the entry keyed on `<cwd>` in `~/.muggle-ai/last-host.json`; a worktree usually has **no cache of its own**, so when the worktree returns nothing, pass the **main** working-tree root as `cwd` — `git rev-parse --git-common-dir`, then its parent directory.
2. Apply the [`autoSelectLocalHost`](../../muggle-preferences/preference-gates/autoSelectLocalHost.md) gate (read its value from the `Muggle Test Preferences` session-context line, which carries every key already resolved):
   - `always` **and** a cache exists → use it silently: `Using saved local URL {lastHost}`.
   - otherwise (`ask` / `never`, or no cache) → **confirm before using any host.** Run the gate's Picker 1 with `{lastHost}` (cached URL, omitted when absent) and `{suggestedHost}` (a port you actually detect listening). Never auto-pick, and never fall back to a framework default like `:3000`; if nothing is cached or detected, ask the user to type the URL.

The resolved host fixes the **expected** dev-server port for the detection below — probe that port; don't infer the target from whichever port happens to be listening.

## Detect listening services

Run port detection and (when an app declares a backend URL) backend-health probe per [`../../_shared/dev-server-readiness.md`](../../_shared/dev-server-readiness.md). Cross-reference hits against selected service directories.

> "**backend-api** is already listening on port 3001 (PID 54321) — looks good."

If **all** required services are running, skip straight to [smoke-test](./smoke-test.md) — don't trust port-listening alone.

If some are running, acknowledge and continue to [start-commands](./start-commands.md) only for the missing ones. **Exception:** when this stage is entered via the [reuse-plan](./reuse-plan.md) short-circuit, the missing entries already have their `command` populated in `/tmp/muggle-test-prepare.json` from the reused plan — skip `start-commands` and go straight to [env-file](./env-file.md). For already-running services:
- Option 1: "It's fine, keep it"
- Option 2: "Restart it"

Mark kept services as `external: true` in the tracking file so cleanup leaves them alone.

## Port already held

When the user wants a port held by a process they did **not** select (typically a stale dev server from a sibling worktree):

The held process is likely something the user is still using — a current test or dev server they value more than this prepare run. Don't auto-decide; leave the kill-vs-pause call to them.

> "Port 3999 is held by PID 87421 (you didn't select this process — it may be a test or dev server you're still using). How do you want to proceed?"

- Option 1: "Use the next available port" (recommended — non-destructive, leaves the existing process running)
- Option 2: "Force-kill PID 87421 and claim port 3999" (kill-switch — only if you don't need that process)
- Option 3: "Pause — leave it running, I'll finish up and re-run prepare later"

**Option 1**: probe `3999 + N` for `N = 1, 2, …` until nothing listens. Record the new port and any env file edit (`PORT=` in `.env.local` etc.). Dev server may need restart to pick up.

**Option 2 — force-kill (destructive):**
- **Windows PowerShell:** `Get-NetTCPConnection -LocalPort <port> -ErrorAction SilentlyContinue | ForEach-Object { try { Stop-Process -Id $_.OwningProcess -Force -ErrorAction SilentlyContinue } catch { } }`
- **POSIX:** `lsof -ti:<port> 2>/dev/null | xargs -r kill -9`

Re-verify the port is free before continuing.
