# Provider Command Resolution And Durability

Oysterun treats provider installation, command availability, and provider
authentication as separate states. Command discovery never runs a provider
login, model, prompt, or authentication command.

At a real-TTY provider-install confirmation whose default is No, pressing
Enter cancels before any provider install, config write, AgentFolder creation,
temporary file, or background process. An implementation that performs an
early side effect must roll it back completely. Normal user cancellation exits
with code `0` and prints exactly:

`Installation cancelled; no changes were made.`

The message and zero exit code do not weaken the filesystem and process
no-side-effect contract.

## Canonical Resolver

`host-service/provider-command-detection.mjs` owns provider command discovery,
intent-aware config merging, and the provider directories added to the Host
launch `PATH`. Release Setup, stack config sync, provider availability, direct
Host startup, and macOS LaunchAgent generation consume this contract.

For `claude` and `codex`, resolution is deterministic:

1. an executable persisted configured command;
2. the current `PATH`, searched without a shell;
3. `/opt/homebrew/bin/<provider>`;
4. `/usr/local/bin/<provider>`;
5. `$HOME/.local/bin/<provider>`;
6. `$HOME/bin/<provider>`.

Executable symlinks are accepted. Broken symlinks and directories are not.
Auto-detected commands are persisted as normalized absolute paths. Unsupported
provider names fail closed before any filesystem lookup.

## Durable Intent

Host `config.json` stores commands in `claude_command` and `codex_command` and
stores their ownership in `provider_command_intent`:

| Intent | Meaning | Setup/restart behavior |
| --- | --- | --- |
| `auto` | Oysterun owns discovery | Populate a resolved path; leave unavailable state non-disabled when not found. |
| `manual` | User owns a non-empty command | Preserve it when another process environment cannot resolve it. A broken path remains an actionable unavailable state. |
| `disabled` | User explicitly disabled the provider | Keep the command null and never auto-enable it. |

Legacy absolute or otherwise non-bare commands migrate to `manual`; the legacy
bare values `claude` and `codex`, plus missing/null commands, migrate to `auto`
so stack sync can materialize their canonical absolute paths. Saving a
non-empty Host Preferences command records `manual`; saving the field blank
records `disabled`. Missing detection is not an explicit disable.

Release Setup uses the same preservation-first merge for local Direct,
Cloud Direct, Managed success, and Managed-pending fallback. It must not write
raw detector nulls over a valid command.

## Launch Environment

The generated macOS LaunchAgent persists the exact supported
`OYSTERUN_NODE_BIN`. Its `PATH` is generated by the canonical resolver and
contains, once each:

- the resolved Node directory;
- every resolved, non-disabled provider command directory; and
- the safe Homebrew, local, and system fallback directories.

The pid-file/direct Host startup invokes the same generator. Interactive shell
profiles are not sourced, and command strings, config JSON, credentials, and
raw environments are never embedded in the generated `PATH`.

## Safe Diagnosis And Recovery

Use Host Preferences or the redacted provider status endpoints to distinguish
command availability from provider authentication. Do not run provider login
as a discovery step. If a manual command is broken, correct it explicitly in
Host Preferences. If a provider was explicitly disabled, enter a valid command
to re-enable it. Repeated setup/start/restart must leave a semantically
unchanged config byte-stable.

Provider history and resume metadata are not deleted when a command is
temporarily unavailable. Authentication errors remain provider-runtime errors;
they do not remove an installed provider from Session Setup.
