# Explicit service management

Native conversation mode and the browser use the shared project-owner implementation.
Extension discovery, login and package installation do not start an owner.
Explicit `/cadence` mode/current user Send can start or reuse a local owner without installing a boot service; see [Conversation mode](conversation-mode.md).
The optional [background service](#background-service) remains an explicit management operation.
Use one coordination state-home for the checkout; a different state-home is not a recovery route.
Stop older Cadence sessions before starting the shared service.

```sh
cadence start --workspace /absolute/project
cadence status --workspace /absolute/project
cadence diagnose --workspace /absolute/project
cadence token-link --workspace /absolute/project --reveal
cadence token-rotate --workspace /absolute/project
cadence stop --workspace /absolute/project
```

`cadence-service` is the same command, and the equivalent package-relative command is `node <cadence>/src/service-cli.mjs ...`.
`--help` lists the finite interface.
`start` also accepts `--foreground`, `--config`, `--agent-dir`, `--host` (loopback only), `--port`, `--token-file`, `--server-config` ([port and token](#port-and-token)) and `--state-home`.
Defaults are loopback `127.0.0.1:17423`, normal Pi agent configuration, and detached service lifetime.
An explicit port0 selects and persists a positive port once; restart uses the selected configuration, not a fallback endpoint.
Missing/corrupt existing setup is preserved, not filled in or reset.
Commands without `--workspace` address the unified workbench, which has no execution cwd and never creates `~/cadence`.
Use `cadence workbench start|status|diagnose|stop|token-link|token-rotate` to name that target explicitly; `cadence doctor` covers the account, the workbench and every registered project (see [Automatic recovery](#automatic-recovery)).
Its start accepts `--foreground`, `--state-home`, `--agent-dir`, `--server-config` and a private `--config` containing an `HttpPolicy` object (the schema used below for project HTTP policy).
The configuration's port/token come from the `.env`; a selected HTTP policy is retained across orderly restarts.
A project root containing the private state home is refused, with no alternate directory creation; native `/cadence` also requires a real project selection.
Existing `~/cadence` content is never removed, relocated or automatically adopted.
A directory inside `cadence-assistant-v1` is never an engineering workspace, even for root or `project open --create`.
Apart from the background service below, there is no supervisor installation, provider login or credential migration.

## Open a project directory

```sh
cadence project open /absolute/existing-project
cadence project open /absolute/new-project --create
# Optional explicit sources, without copying them:
cadence project open /srv/project --agent-dir /root/.pi/agent --state-home /root/.local/state
```

Without a path, `project open` uses the current directory; a missing directory refuses unless `--create` was supplied.
The project may be a Git checkout or an ordinary directory, including spaces and Unicode names; it need not be inside `~/cadence` or the package directory.
The command explicitly starts a detached owner, or observes the already running exact owner; it never creates a task, chooses a model, renews a grant or starts a worker.
New projects bind port0 and retain the resulting positive port, avoiding the default17423 collision; existing selections keep their exact port, saved default cwd, agent sources and history.
The JSON result reports the canonical checkout, saved default cwd and endpoint; each conversation supplies its own execution cwd, rather than changing this owner setting; the private entry link is printed separately to stderr, like `server install`.
Keep that link private.
Open Pi in the same project and use `/cadence` for the native client; explicit `cadence stop --workspace <path>` stops only that project.
Opening another project never changes the cwd or cancels work in the former one.
Different folders of one checkout share its owner and execution gate, but conversations keep independent directories and model choices; an admitted run keeps its original snapshot.
The owner launches from the checkout root.
A removed saved default subdirectory does not prevent other conversation directories from starting; no old conversation is redirected or rewritten.
The default workbench routes all associated projects through one origin, including a configured HTTPS proxy.
Its picker browses directory names readonly (`~` names the service account's home); **Use this folder**, or one click on a recent directory, only selects it.
Send starts or reuses that directory's owner when needed; there is no separate Open step, boot installation or automatic message replay.
If no usable saved model is available, that Send waits for an explicit model choice.
Cancelling the choice or leaving the conversation does not send the waiting message.
If the conversation cannot be read, Send can prepare execution but cannot infer its context: review the restored conversation and send again.
Preparation failure keeps the draft; an owner may already have started, but the message was not sent.
If the actual Send reply is lost instead, its original command is observed, never resubmitted automatically.
Project navigation remounts only the browser client; the original owner's work continues.
Browser proxy traffic keeps browser authority and the owner's own attachments, CSRF/build checks, grants and history proofs; native management routes and local-capability headers are not forwarded.
The gateway also refuses the legacy project-level Open command, so every Send preparation is associated through the workbench instead of returning an independent backend token link.
The gateway's token, backend cookies and native capabilities are never returned in project DTOs or stored in browser localStorage.
Token rotation retires and detaches its exact browser attachments without cancelling admitted work.
The private account state stores bounded directory references and startup/association preparation receipts, not copied worktrees, Pi profiles or imported transcripts.
Private coordination/Pi credential directories and foreign-owned project selections are refused.
Direct project entry links remain available as a compatibility route; that route alone still uses separate loopback project entries.
Git subdirectories and aliases resolve to the same checkout, while overlapping parent/child owners still refuse.
An unknown or inaccessible selection is never treated as a new project or recovered by choosing another state home.

When run as root, Git observation requires a repository Git recognizes as root-owned: Cadence clears `safe.directory` exceptions per command, removes inherited `SUDO_UID` and disables fsmonitor.
A foreign-owned repository refuses even if a global `safe.directory=*` would allow it; selecting a path is not consent to execute its configured clean/process filters.
Use the repository's owning account instead, or deliberately provision a root-owned checkout outside Cadence; no Git configuration edit, `chown`, credential import or permission reset is performed.
Ordinary-user Git trust behavior remains intact.
Trusted repositories can still carry executable Git configuration; root is not an isolation or sandbox guarantee.
Changing to root changes the effective account and its private namespace: stop all old Cadence owners before doing so, since different accounts/state homes do not coordinate each other's original work.

## Background service

```sh
cadence server install [--workspace /absolute/project] [--system|--user] [--no-linger]
cadence server status
cadence server restart
cadence server upgrade [--to VERSION]
cadence server stop
cadence server uninstall
```

Only these explicit commands and the WebUI's confirmed [restart and upgrade](#restart-and-upgrade-from-the-webui) register, change or remove the service; no package install, update hook, `/cadence` or browser visit does.
The package has no install script, so npm 12 and Bun skipping untrusted scripts by default changes nothing; the pinned Pi SDK's blocked `@google/genai` preinstall only echoes `no-op`, and its `protobufjs` postinstall only warns about version schemes.
Linux server commands accept `--system` or `--user` after the action; root defaults to system scope and other accounts to user scope.
`--system` requires root and never runs sudo itself; `--user` explicitly selects the legacy per-user manager, even when invoked by root.
Linux also accepts `--name <name>` on every server command to select `cadence-<name>.service`, and `install` accepts `--port <1-65535>` for a missing port setting.
Names must start with a letter or digit and contain only letters, digits, `_` and `-` (at most48characters).

`install` also creates the research template `~/.pi/agent/cadence/.env` when it is missing, never overwriting it, whatever the service result, and adds its [port and token](#port-and-token) lines when they are missing.
`install` without `--workspace` runs `workbench start --foreground --server-config <file>` (Linux, macOS), or without `--foreground` on Windows.
The service process may use its package directory as a technical launch cwd; it is never an agent workspace and no project scan or model runtime is initialized there.
An explicit `--workspace` retains single-project service compatibility and must name an existing suitable directory.
A reinstall without `--workspace` explicitly selects the unified entry after orderly Stop of the old generated definition; it neither deletes the former workspace nor imports its data.
A plain `restart` retains the definition's existing target, including an older explicit project service, rather than silently migrating it.
The `.env` [port and token](#port-and-token) select the endpoint (17423 by default) and the token; an occupied endpoint fails instead of falling back.
Install and restart print the private entry link once the owner answers; the JSON result on stdout never carries the token.
A successful manager start request is not owner readiness: an unready owner reports `quarantined` or `unconfirmed`, retains the separate `managerStatus`, exits nonzero and names `cadence doctor`.
Status also retains the manager's `active`/`activating`/`failed` observation separately.
A definition whose project directory was removed gets a readable refusal (exit78, so the unit does not restart in a loop); `cadence server install` switches it to the workbench.
`restart` rewrites an existing definition with the current Node and package paths, keeping its workspace and the user's disable choices, and starts it; run it after an npm upgrade, a Node change or a `models.json` edit.
The `upgrade` command (Linux) installs the latest version, or exactly `--to VERSION`, with npm and restarts; see [Restart and upgrade from the WebUI](#restart-and-upgrade-from-the-webui) for its steps.
Commands that change the service (`install`, `restart`, `stop`, `uninstall`, `upgrade`) refuse with `inside-service` when they run inside the service they would stop, for example from a tool of its own agent: stopping a systemd unit ends every process in it, so the command would die before it started the service again.
Use the WebUI or a terminal outside the service instead.
`stop` joins the workbench's accepted Send preparations and associated exact-epoch project owners, then closes the entry; unrelated project owners stay independent.
An associated owner that is not running (or whose directory is gone) has nothing to stop; one whose Stop is unconfirmed keeps its own state, and the workbench still stops.
Project-specific `stop --workspace <path>` remains scoped to that owner.
While the unified entry is running, a stopped associated project's ordinary/Cadence conversation history and mode remain readable; this does not start the project, restore approval or resume interrupted work.
Its page explains that Send prepares execution and retains drafts.
Send the next message directly; a new owner connection does not clear the conversation or approve a saved plan.
Selecting or browsing an independent running owner does not make whole-workbench Stop manage it; explicit Send preparation does.
Direct project links, legacy task details and workflow details still require their project owner.
The generated registration stays for the next boot or login.
`uninstall` stops it orderly and deletes only what Cadence generated; workspace, state home, history, linger and Pi sources stay.
Because npm runs no uninstall script, uninstall the service before `npm uninstall -g`; a left-behind definition fails to start once its CLI path disappears.
A definition Cadence did not generate is left unchanged (`foreign`), and `status` reports `absent` when none is registered.

Pi's installer omits peers, so the standalone owner and workers load the Pi SDK (`pi-coding-agent`, `pi-ai`) from exact 0.84.3 optional dependencies.
Package files may be hard links into the package manager's store (Bun's default install); Cadence binds their exact content, not their link count.
Missing or unusable Pi sources refuse before an owner registers; an owner that cannot load the SDK refuses at stage `pi-runtime` and exits.
The no-project workbench starts without either an SDK runtime or Pi authentication; missing sources are reported only when a project is explicitly opened.
For an explicit single-project definition, the older guards remain: no SDK means no definition, and missing `auth.json` means registered but waiting for Pi login.

While no service is registered, Pi's session-start card shows a separate warning naming `cadence server install` for an installed package copy (`node <copy>/src/service-cli.mjs server install` for Pi's managed copy, which puts no bin on `PATH`); a checkout loaded with `pi -e` stays quiet.
It is a warning rather than part of the info card because Pi's TUI replaces one info status with the next.
The readonly `/cadence-history` client names the same management command when no owner exists; daily `/cadence` instead uses explicit on-demand project startup, without requiring a boot service.
A machine without a supported user service manager and `CADENCE_AUTOSTART=0` keep both quiet; with `CADENCE_LINGER=0` they suggest `--no-linger`.

### Restart and upgrade from the WebUI

On Linux the workbench has a **Cadence service** dialog: the server button on the entry page, or **More → Cadence service** in a project.
It shows the running version and its systemd unit, asks npm for the latest version only when you press **Check for updates**, and offers **Restart service** and **Upgrade to VERSION**.
The confirmation names the running workflows and conversations the restart stops; they stop through the orderly Stop, never by a signal.
If work starts or ends before you confirm, the request is refused as stale and the dialog reads the service again.

The request runs outside the service, in a transient unit `cadence.maintenance.service` (`cadence-NAME.maintenance.service` for a named service), so closing or reloading the page does not interrupt it and one job runs at a time.
It runs the same `cadence server restart` or `cadence server upgrade`, then records its outcome; the page waits for that record and offers to reload.
If it reports nothing, see `journalctl --user -u cadence.maintenance.service` (without `--user` for a system service) and `cadence server status`.

An upgrade fetches the exact release while the service still runs, so an unreachable registry changes nothing.
Then it stops the service, installs the release with `npm install --global` and starts the new copy's own `server restart`.
A new version that does not become ready is replaced by the previous version again from npm's cache.
The npm calls use npm's own configuration (`~/.npmrc` registry, proxy and prefix).
Only a copy that `npm root --global` names, and that the service account can write, upgrades itself; the dialog says when another installation (Pi's own copy, a checkout, Bun, a root-owned prefix) has to be upgraded the way it was installed, followed by `cadence server restart`.
A workbench that was not started by its systemd service, and services on macOS and Windows, show the version only; restart and upgrade them in a terminal.

### One copy for Pi and the service

After `npm install -g`, `pi install "$(npm root -g)/@abelxiaoxing/cadence"` records that path and loads it in place, so an npm upgrade updates the service and native `/cadence` together.
Pi's own package install (`pi install npm:@abelxiaoxing/cadence`) also loads the native client, but its copy puts no `cadence` on `PATH`; if you want the optional boot service, register it with `node ~/.pi/agent/npm/node_modules/@abelxiaoxing/cadence/src/service-cli.mjs server install`.
Keep one copy: two copies drift apart on upgrade, and a native client refuses an owner whose service contract differs from its own.
A unit registered by an earlier version's Pi install hook keeps running; `cadence server restart` from the copy you keep rewrites it, and `pi update` no longer restarts it.
Opening the private link once leaves an HttpOnly cookie in that browser; the address without the token only explains how to get the link.

### Port and token

The background service reads two lines of the research configuration `~/.pi/agent/cadence/.env`:

```sh
CADENCE_PORT=17423
CADENCE_TOKEN=<any non-empty token up to 1024 bytes, no control or format characters>
```

The file keeps the research skills' syntax (`KEY=value` lines, `#` comments, one optional pair of quotes), and each of these keys may appear only once.
`install` and `restart` create the file from the research template when it is missing, as your own 0600 file.
They append a missing `CADENCE_PORT` (17423) or `CADENCE_TOKEN` (random, 43 characters) under a comment, in the file's own line endings, and never change an existing line.
It is your own file, made by hand, by older docs (`mkdir -p ~/.pi/agent/cadence`) or by a dotfiles manager, so it is read like Pi's sources: its mode bits are not checked (0644, or 0664 under umask 002, work), links in its path and the file itself are followed, and a rewrite goes to the real file, written 0600, leaving the links in place.
When other users can read it, `install` and `restart` add a note naming `chmod 600`, without refusing.
Only a file that cannot be the user's own configuration makes them change nothing: missing or a link to nothing, not a regular file, not owned by you or in a directory you do not own, hard-linked, over 64 KiB or not UTF-8, a line the research skills would also refuse, a duplicate key, a port outside 1-65535 or an empty token.
Each refusal names its reason and line number, never a byte of the file.
A project's `.pi/cadence/.env`, which the research skills prefer, is never read for the service.
`start --server-config <.env>` makes these lines authoritative.
The host reads them once before binding, like an environment token, so editing them while the owner runs changes nothing and can never fence access or the orderly Stop; the next start applies them, and `cadence server restart` does both.
A workspace whose token comes from the file follows it at every `start`, also without `--server-config`; a contradicting `--port`, `--token-file` or `CADENCE_WEB_TOKEN` refuses.
`CADENCE_WEB_TOKEN` stays the separate process-environment source; sourcing the `.env` in a shell sets `CADENCE_PORT` and `CADENCE_TOKEN`, which Cadence never reads from its environment.
The token never enters Cadence's state home, and status and diagnosis never print it.
It has no minimum length, but the owner redacts its exact text from conversations, plans and checks, so a short or common word also hides every matching word there; a generated token is 43 random characters.
`token-rotate` replaces only the `CADENCE_TOKEN` line's value with a new random token, keeping every other byte, and invalidates old links and cookies without stopping work.
It refuses without any effect when that token was edited since the start, so a pending edit is never overwritten.

A stopped workspace selected earlier with a generated, environment or file token, or another port, is reselected to the file at its next `start --server-config`.
Under the new owner reservation Cadence retires the previous token with a new access generation and replaces the selected port and token source; the history and every other selected setting stay.
A different agent directory, host or proxy still conflicts, and a running owner refuses until it has stopped.
An interrupted replacement is recovered by the next start like any interrupted start.

### Linux systemd system/root unit (no linger)

```sh
# Root must already have its own usable Pi credentials and SDK installation.
sudo -H cadence server install --system --workspace /srv/project
sudo -H cadence server status --system
sudo -H cadence server restart --system
sudo -H cadence server stop --system
sudo -H cadence server uninstall --system
```

Use an absolute Node and package CLI path with `sudo -H` if `cadence` is not on root's PATH.
These commands generate `/etc/systemd/system/cadence.service` with `User=root` and `WantedBy=multi-user.target`, and use `systemctl --system`.
They never consult or change linger, require no logged-in user manager and start at boot independently of root login.
The definition freezes HOME, PATH, LANG/LC_ALL, an explicit XDG_STATE_HOME and custom PI_CODING_AGENT_DIR; root must own its selected Pi sources, and no normal-user profile or credentials are copied.
Root runs the agent and approved checks with full root privileges: browser authentication remains access, not permission to Start, and this is not a sandbox.
Status and install output identify system scope; Pi's root startup hint recommends `--system`, never `loginctl enable-linger`.

A prior user-scope definition of the same name causes `scope-conflict`, never automatic migration.
For a root account's older user unit, explicitly run `cadence server uninstall --user` first, then install `--system`; this preserves its workspace, private history, credentials and linger setting.
For another account's service, first stop/uninstall it as that account; root must not silently import its private state or assume its owners are absent.
A detached owner already running in the target directory causes `project-running`; stop it explicitly before installing a different supervisor.
An old but inactive unit does not own a subsequently detached owner: install, restart, stop and uninstall also refuse in that case, without sending Stop to it, and changing the requested target does not bypass the old directory's check.
An unconfirmed or quarantined Stop, changed generated file, changed HOME/XDG state selection, a mask or a foreign unit prevents replacement/adoption.
`restart`, `stop` and `uninstall` await the authenticated original owner Stop before invoking systemctl, so manager timeouts are never used as proof of settlement.
Restart keeps disablement and does not unmask anything; uninstall removes only the generated definition, never data or linger.
Direct systemctl shutdown still has the unit's ExecStop and timeout behavior described below.

### Independent named Linux services

```sh
sudo -H cadence server install --system --name api --workspace /srv/api --port 17424
sudo -H cadence server install --system --name web --workspace /srv/web --port 17425
sudo -H cadence server status --system --name api
sudo -H cadence server restart --system --name web
```

The units, ports, access tokens, working directories and owner lifetimes are separate; restarting or stopping one does not touch another.
A named service uses `~/.pi/agent/cadence/servers/<name>.env` under its effective account, never a copy of the default token or research/provider configuration.
A new named configuration requires `--port`; existing port/token lines are preserved, and a conflicting `--port` refuses instead of rewriting them.
Edit the selected named file explicitly and restart that same `--name` to change its port.
An occupied port still refuses rather than silently falling back; use `project open` when an automatically selected detached port is preferable.
All projects still share one coordination state home for their account, retaining canonical overlap checks.

### Linux systemd user unit

`install --user` writes `~/.local/share/systemd/user/cadence.service`, enables it for `default.target` (also undoing a `disable`, never a `mask`) and starts it.
A user unit runs with the user manager, which starts at login unless logind lingers the user, so install also enables linger when it is off.
It first calls `SetUserLinger` through `busctl` with `interactive=false` and no interactive authorization (bounded to 15s), because `loginctl enable-linger` always requests an interactive check that a desktop polkit agent can turn into a password dialog even with `--no-ask-password`.
Only where polkit requires authorization for `set-self-linger` (older systemd defaults) does it then run `loginctl enable-linger`, which may ask for your password; that comes after the start, so an interrupted prompt never leaves the owner stopped, and a refusal reports `"linger":"refused"` with a login-only unit.
`--no-linger` or `CADENCE_LINGER=0` skips linger; `restart` never changes it, so `loginctl disable-linger` is kept.
The unit bakes the installer's PATH (minus npm lifecycle entries), LANG/LC_ALL, an absolute XDG_STATE_HOME and a custom PI_CODING_AGENT_DIR, so shell management sees the same state home and Pi sources.
Proxy variables are not baked; add a validated pair with `systemctl --user edit cadence`.
It carries `ConditionPathExists=` for the CLI; explicit single-project definitions also require `auth.json` and both SDK packages, while the project-less workbench does not.

`restart` stops an active unit with its loaded definition, rewrites the file and starts it, without enabling a disabled unit; a masked unit is never enabled or started.
`ExecStop` is the authenticated orderly Stop, so admitted work is stopped, not detached; `stop` and `uninstall` (`disable --now`) use it too.
`KillMode=mixed` sends systemd's SIGTERM only to the management client.
An owner still settling after `TimeoutStopSec=90` is SIGKILLed, which is not orderly Stop; the next start recovers what it leaves.
`Restart=on-failure` is bounded to three starts in five minutes.
Deterministic foreground startup refusals exit78 (`EX_CONFIG`), and newly generated units use `RestartPreventExitStatus=78` so these refusals do not loop.
Ordinary unexpected process failures retain the bounded restart policy, and every restart first recovers what the failed owner left.
An owner that hits a runtime fault stops its own workers and exits 75; its unit restarts it after `RestartSec=10`.
Existing generated units get this setting on the next successful explicit install/restart, not by package installation or recovery.
Without linger, user units start at login and stop at logout through the same Stop.
Linger starts the user manager, with every enabled user unit, at boot and keeps it running after logout.
`status` reports `linger` as `on` or `off`, and Pi's startup card names `loginctl enable-linger` while an enabled unit is login-only; a disabled or masked unit (also `mask --runtime`) keeps it quiet.
To keep the service off while Cadence stays installed, use `systemctl --user disable --now cadence` or `mask`.

### macOS LaunchAgent

`install` writes `~/Library/LaunchAgents/com.abelxiaoxing.cadence.plist` with `RunAtLoad`, runs `launchctl enable` (undoing a `launchctl disable`) and loads it into `gui/<uid>` with `launchctl bootstrap`, which starts it; launchd loads it again at every login.
It bakes the same PATH, LANG/LC_ALL, XDG_STATE_HOME and PI_CODING_AGENT_DIR as the Linux unit and logs to `~/Library/Logs/cadence.log`.
For an explicit single-project service, missing `auth.json` leaves the agent written but not loaded; the project-less workbench can start without Pi login.
Because launchd stops a job only by signaling it, `restart`, `stop`, `uninstall` and a reinstall first stop the owner through the authenticated orderly Stop, and change nothing when that Stop is `unconfirmed`, `quarantined` or `unavailable`.
Only then does Cadence unload (`bootout`) or replace the agent; `stop` leaves it loaded, and the finished job starts again at the next login.
There is no `KeepAlive`: a failed owner stays down until the next login or `cadence server restart`, which recovers it; an owner that faults at runtime starts its own replacement.
`restart` keeps a `launchctl disable` and reports `disabled` without loading.
At logout or shutdown launchd signals the job, which is not orderly Stop: running work is interrupted, and the next start recovers the owner automatically.

### Windows login Run entry

`install` writes `%LOCALAPPDATA%\cadence-server\cadence.vbs`, beside rather than inside the private state home, and the per-user value `HKCU\Software\Microsoft\Windows\CurrentVersion\Run\Cadence`, which runs it with `wscript.exe` at login; no administrator rights are needed.
The launcher is UTF-16 with a byte-order mark, so non-ASCII paths work, and runs `workbench start` (or `start --workspace` for an explicit project) with window style 0, so no console window appears; the detached owner keeps running after the launcher exits.
It bakes only a custom PI_CODING_AGENT_DIR, because the login environment already carries PATH and `%LOCALAPPDATA%`.
`install` also removes Task Manager's `StartupApproved` switch for the entry (undoing a disable there); `restart` keeps both the entry and that switch, and `status` reports a disabled entry.
For an explicit single-project service, missing `auth.json` leaves the entry registered but starts nothing; the project-less workbench can start without Pi login.
Nothing supervises the detached owner, so a failed owner is not restarted, and every `restart`, `stop`, `uninstall` and reinstall first runs the authenticated orderly Stop and changes nothing unless it is confirmed; Cadence never kills a process.
A `Cadence` Run value without Cadence's launcher is `foreign` and left unchanged; registry values are only tested for presence, because `reg.exe` prints them in the console code page.
At sign-out or shutdown Windows ends the owner without orderly Stop: running work is interrupted, and the next start recovers the owner automatically.

## Status, departure and Stop

- `alive` requires an authenticated exact-epoch reply.
  It is not a PID probe.
  `actions:false` can describe the unscoped owner model slot; conversation model readiness is separate, not permission to execute a plan.
- Disconnect this page (the WebUI More menu), browser close and Pi quit detach clients; admitted work continues.
  Use the client's **Cancel** for a run or management **stop** for the service.
- Stop closes admission and waits original processes, credential writes, history writes, already-started publication continuations, owned networking and listener closure before releasing fences.
  Checks, native process outcomes and human review remain separate facts.
- `unconfirmed` means startup/Stop acknowledgment or settlement was not established within its reporting bound.
  It is not permission to restart, kill a guessed PID or remove a lock.
  `quarantined` means the owner could not settle some process/auth/storage work: it refuses new work, lets the original work settle, stops its own workers and restarts itself (recovered automatically).
- A host that refuses during `start` reports `{"status":"refused","error":<code>,"stage":<stage>}` and exits without registering an owner.
  Stages are `package` (installed web assets or Agent profiles), `pi-agent-directory`, `pi-auth`, `pi-models` and `service`; stderr names the selected Pi source to fix.
  `quarantined` with stage `pi-runtime` or `service` means the owner registered first; it exits, and the next start recovers its registration.
  Reports carry fixed codes and stages only, never paths, configuration bytes or OS error text.
- The selected Pi agent directory must be a real directory (not a symlink) owned by you; `auth.json` and `models.json` must be your own single-link regular files.
  Their mode bits are not checked: Pi creates its agent directory with your umask (0755, or 0775 under umask 002), and Cadence's own state stays 0700 regardless.
  `models.json` may use only fields the pinned SDK supports.
  `supportsMidConvoSystemMessages` from newer Pi is accepted because the pinned SDK has no system-role conversation message; other unknown fields refuse rather than being silently ignored.
  The conversation model picker offers every thinking level the pinned SDK supports for a model, `off` through `xhigh` and `max`; a model whose `thinkingLevelMap` maps only `max` (as some DeepSeek configurations do) is chosen at `max`, and a child runs only at exactly that level.
  A model whose provider or model ID contains a known credential value is withheld from the catalog, since showing the ID would reveal that value; the catalog says how many models are withheld, and the rest stay selectable.
  Credential values are `auth.json` keys, `models.json` `apiKey` and header values (and the variables they name) and service-environment variables whose names contain `KEY`, `TOKEN`, `SECRET`, `PASS`, `AUTH` or `COOKIE`.
  The common Ollama placeholder `"apiKey": "ollama"` under a provider named `ollama` is such a case: give the placeholder a value that matches no provider or model ID (for example `"local-no-auth"`), then restart the service.
  The owner loads `models.json` once at start: after it changes, the model catalog, the chosen model and the workspace view refuse as `stale` (credential changes in `auth.json` are read fresh).
  Restart the service to reload it (`cadence server restart`, or `stop` then `start`), then explicitly Send or choose the conversation model again; retained preferences are not erased.
- A foreground owner can be managed from another terminal.
  Ctrl-C, SIGTERM, machine crash or terminal destruction is **not** the authenticated orderly Stop route: running work is interrupted, and the next start recovers the owner automatically.
  A recovered `stopped` registration records that the old owner was proven gone, not that its interrupted work completed.

`diagnose` reports fixed pending/recording facts and `proxyAuth: direct|validated-proxy` when the owner is reachable; a busy global registry instead reports its fixed lock reason and names `cadence doctor`.
No normal status/diagnosis prints access secrets, provider credentials or outbound proxy URLs.
Keep the explicitly revealed token link private.
Browser entry redirects to a token-free URL and uses an HttpOnly cookie; that does not retract a link already copied into history, a clipboard or external logs.
File/generated token rotation invalidates old access and pending confirmations, not admitted work.
Environment-token replacement requires explicit Stop/restart with changed environment.
Inbound remote access still requires the selected same-host HTTPS reverse-proxy policy; outbound proxy routing below is independent.
Browser access, even with a valid token, cannot perform local management, `cadence doctor --fix` or arbitrary manual shell checks.
The native client's local capability uses an owned direct loopback dispatcher, never the Pi process's provider proxy.
Detach joins that client's original dispatcher closure without changing the parent's global dispatcher or canceling service work.

## Outbound owner authentication and workers

Set one credential-free HTTP(S) proxy URL as an equal HTTP/HTTPS pair in the **service launch** environment.
Matching uppercase/lowercase duplicates are supported; conflicting or partial pairs refuse.
No `ALL_PROXY`, `NO_PROXY`/bypass, URL userinfo, non-root path, query/fragment, percent-bearing authority, unsafe controls or `NODE_OPTIONS` (including empty entries) is accepted.
`NODE_USE_ENV_PROXY` can be absent or exactly `1` with the pair.
A standalone opt-in refuses.
The service launcher projects the validated uppercase pair and opt-in before SDK imports, and uses the actual runtime's public `--use-env-proxy` capability.
Missing capability refuses before launch, without finding/installing another Node.

After SDK imports the dedicated owner installs one owned public Undici dispatcher, verifies its identity and uses it for public SDK getAuth/OAuth refresh.
Workers retain their separately validated startup/post-import route.
Scoped provider route entries must exactly match the captured route; they cannot select a route or become process environment.
Changed route facts/dispatcher identity refuse, never silently fall back to direct traffic.
DIRECT adds no dispatcher work.
No TLS override, global fetch replacement or private SDK networking is used.
Proxy URLs are RAM-only known redaction literals, not persisted route hashes or public history fields.

Pi's selected existing `auth.json`/`models.json` remain the model sources; OAuth renewal writes back through public SDK coordination, preserving unrelated credentials.
Cancel stops launch admission but does not claim an already-started refresh/writeback was rolled back.
Checks inherit the owner's frozen ordinary launch environment (including the canonical configured proxy pair/opt-in), minus service/access/control material.
Resolved model auth is never merged into checks.
Local fabricated-provider plain/gzip routing tests are not paid-provider, all-adapter, Node22 or other-platform qualification.

## Automatic recovery

Cadence is built for unclean endings: power loss, shutdown or logout while it runs, a crash, `kill -9`, or a supervisor's forced stop.
The next start (`cadence start`, `cadence project open`, the workbench, the background service, or native `/cadence`) recovers by itself and prints one line, for example:

```text
Cadence recovered project /home/me/app automatically: the machine restarted while Cadence was running; released its locks, continued its history after the last complete entry.
```

The WebUI shows the same recovery once at the top of the page.
Nothing is asked of you unless Cadence cannot prove the old process is gone.

What a start does, in order:

- It proves the previous owner gone: new locks record the machine's boot identity and the process's start identity, so a restart of the machine, an absent process or a reused process ID is certain.
- It stops worker processes the dead owner left running on the same boot, by their exact recorded identity; after a machine restart none can remain.
- It releases the dead owner's registration and locks, and every other dead reservation in the account (including one whose project directory was removed).
- It continues the history after the last complete entry (a batch cut short by the crash stays where it is, untouched).
- Private files that a crash left half-written are moved to `<state-home>/cadence-assistant-v1/recovery-archive-v1/` and the affected part starts afresh; archived files are deleted after 30 days (or sooner beyond 2 GiB).

Work that was running is marked interrupted (its outcome stays unknown and is never guessed); nothing is replayed, and worktrees and partial edits are kept.
Start an interrupted run or message again when you want it.

An owner that hits a fault while running (for example a worker that will not exit, or a history write that failed) stops accepting work, lets its work settle, stops its own workers and restarts itself; the restart recovers as above.
It restarts itself at most three times in a row.

### cadence doctor

```sh
cadence doctor                      # readonly: what is left over and what recovered recently
cadence doctor --fix                # also handle the cases below, after one confirmation
cadence doctor --fix --yes          # the same without the prompt, for scripts
cadence doctor --workspace /absolute/project --state-home /absolute/state-home
```

`cadence recover inspect|resolve` remain as aliases of `doctor` and `doctor --fix`.
Use the same account and state home as the service.

Only these cases wait for you, each named with one sentence and the next step:

- **Running but not responding**: the owner process exists and is Cadence's own (its start identity matches) but does not answer.
  `--fix` stops it (TERM, then KILL) and recovers.
  An interactive `cadence start` asks the same question.
- **Another machine**: the state names a different host (a shared or copied state home).
  Cadence cannot observe that process; `--fix` takes the state over without touching any process.
  Confirm only if that machine no longer runs Cadence with this state.
- **Unidentifiable process**: a process with the recorded ID exists and Cadence cannot tell whether it is the old owner (older locks on some platforms).
  If no Cadence is running, `--fix` takes the state over.
- **Workers that did not stop**: leftover workers ignored TERM and KILL; stop them yourself (or restart the machine) and start again.

Doctor never deletes a project, its history or anything outside Cadence's private state; damaged private files go to the archive, and the doctor report names it.
