diff --git a/.agents/skills/firstmate-orca/SKILL.md b/.agents/skills/firstmate-orca/SKILL.md index f8261cacd..d2ffb3f9e 100644 --- a/.agents/skills/firstmate-orca/SKILL.md +++ b/.agents/skills/firstmate-orca/SKILL.md @@ -28,7 +28,7 @@ If `FM_HOME` is set, remember that operational state lives under `$FM_HOME` whil Before switching or spawning against Orca: - Confirm Orca is intentionally selected through `--backend orca`, `FM_BACKEND=orca`, or local `config/backend`. -- Confirm the Orca app is running and the backend readiness checks pass before expecting spawn to work. +- Confirm the Orca runtime is running (the desktop app on macOS, or `orca serve` on Linux) and the backend readiness checks pass before expecting spawn to work. - Inspect active `state/*.meta` records before changing backend selection. - Treat a backend switch as affecting future spawns only; existing tasks keep their recorded backend. - Reconcile watcher wakes before unrelated work, especially if Orca tasks are already in flight. diff --git a/bin/fm-bootstrap.sh b/bin/fm-bootstrap.sh index 1f5cc5d80..e0c4c3ad9 100755 --- a/bin/fm-bootstrap.sh +++ b/bin/fm-bootstrap.sh @@ -4,7 +4,7 @@ # Detect: prints one line per problem or capability fact and exits 0. # Silent = all good. # Lines: "MISSING: (install: )", -# "MISSING_MANUAL: (instructions: )", "NEEDS_GH_AUTH", +# "MISSING_MANUAL: (instructions: )", "NEEDS_GH_AUTH", # "BACKEND_INVALID: (known: )", # "CREW_HARNESS_OVERRIDE: ", # "CREW_DISPATCH: invalid config/crew-dispatch.json - ", @@ -313,7 +313,7 @@ secondmate_liveness_sweep() { install_cmd() { case "$1" in - tmux|node|git|gh|curl|jq|orca|zellij) echo "brew install $1 # or the platform's package manager" ;; + tmux|node|git|gh|curl|jq|zellij) echo "brew install $1 # or the platform's package manager" ;; cmux) echo "brew install --cask cmux # or see https://cmux.com" ;; treehouse) echo "curl -fsSL https://kunchenguid.github.io/treehouse/install.sh | sh" ;; no-mistakes) echo "curl -fsSL https://raw.githubusercontent.com/kunchenguid/no-mistakes/main/docs/install.sh | sh" ;; @@ -326,6 +326,11 @@ install_cmd() { manual_install_url() { case "$1" in herdr) echo "https://herdr.dev" ;; + # Orca ships its own runtime/CLI rather than a package-manager package, so + # there is no generic install command to eval - and `brew install orca` is + # Plotly's graphing library, not Orca (docs/orca-backend.md). Point at the + # authoritative setup doc instead of fabricating an unverified command. + orca) echo "docs/orca-backend.md" ;; *) return 1 ;; esac } diff --git a/docs/cmux-backend.md b/docs/cmux-backend.md index 5859e7f2d..a6982a9e3 100644 --- a/docs/cmux-backend.md +++ b/docs/cmux-backend.md @@ -7,12 +7,13 @@ cmux is [a Ghostty-based macOS terminal](https://cmux.com) built for AI coding a Verified against the real installed app: cmux 0.64.17 (build 97), macOS aarch64. The feasibility investigation that preceded this build (`data/cmux-backend-feasibility-c7/report.md`) verified the app's CLI surface from source only, flagging a live install-and-poke pass as the remaining gate; that pass is what this document and `tests/fm-backend-cmux-smoke.test.sh` record. All real-cmux verification here and in the smoke test creates only `fm-test-`-prefixed task workspaces, with one documented exception: the manual last-in-window verification also creates the unnamed default sibling cmux requires to close that task workspace. -It never enumerates-and-closes, touches no existing workspace, closes only its own `fm-test-` task workspaces, and never quits or relaunches the app - the same discipline `tests/herdr-test-safety.sh`/`tests/zellij-test-safety.sh` established for their backends, adapted in `tests/cmux-test-safety.sh` to cmux's shape (there is no isolated, throwaway session to spin up - cmux is one shared, GUI-first app instance, the same posture as Orca). +It never enumerates-and-closes, touches no existing workspace, closes only its own `fm-test-` task workspaces, and never quits or relaunches the app - the same discipline `tests/herdr-test-safety.sh`/`tests/zellij-test-safety.sh` established for their backends, adapted in `tests/cmux-test-safety.sh` to cmux's shape (there is no isolated, throwaway session to spin up - cmux is one shared app instance, the same single-instance posture as Orca's runtime). ## Setup Pick cmux if you already run it as your terminal and want firstmate crew tabs to live there instead of tmux. -cmux is **macOS-only** and **GUI-first** - selecting this backend means a real GUI window exists and is running, exactly like Orca's posture. +cmux is **macOS-only** and **GUI-first** - selecting this backend means a real GUI window exists and is running. +(This is stricter than Orca, which also runs headless on Linux via `orca serve`; cmux has no headless shape.) Prerequisites: @@ -365,8 +366,9 @@ All three tasks' cmux workspaces and worktrees were confirmed fully cleaned up a ## Known gaps left for a follow-up - **No event push at all**, not even herdr's semantic busy-state: cmux has agent-awareness elsewhere (Claude Code hooks, session-resume) but nothing exposed over the socket API for generic busy/idle classification, so `fm-watch.sh`'s existing pane-hash + `FM_BUSY_REGEX` poll loop is the ONLY event source for this backend, identical to the tmux/zellij/Orca path. -- **GUI-first, macOS-only, requires the app running** - identical posture to Orca. - Never a candidate for a headless/CI firstmate instance, because runtime auto-detection (cmux runtime signals; see "Runtime auto-detection" above) can only fire from inside a live cmux terminal in the first place. +- **GUI-first, macOS-only, requires the app running.** + cmux itself has no headless or Linux runtime, so it is never a candidate for a headless/CI/SSH-only firstmate instance (unlike Orca, which also runs headless on Linux via `orca serve`). + Runtime auto-detection (cmux runtime signals; see "Runtime auto-detection" above) can only fire from inside a live cmux terminal in the first place. The one-time socket-access setup remains an unavoidable manual step regardless of how the backend was selected. - **`--secondmate` spawns are refused** (mirrors Orca's refusal) - no per-home container design (a herdr-style workspace-per-home split, or similar) has been designed or verified for cmux yet. - **The one-time socket-access setup is a real, undocumented-by-upstream onboarding step.** A captain who selects `backend=cmux` without first switching `automation.socketControlMode` away from its `cmuxOnly` default to a viable mode (Automation mode recommended; see "Setup") will see every spawn fail with an actionable error naming the viable modes and pointing back to this document, but there is no way for firstmate to complete that GUI-only setup step on the captain's behalf. diff --git a/docs/orca-backend.md b/docs/orca-backend.md index f9ff8b323..34ca2db55 100644 --- a/docs/orca-backend.md +++ b/docs/orca-backend.md @@ -6,29 +6,49 @@ Firstmate agents operating this backend should load the agent-only [`firstmate-o ## Setup -Pick Orca if you already run the Orca macOS app as your terminal environment and want firstmate tasks to live in Orca-managed worktrees and terminals instead of a treehouse/tmux pair. -Orca is macOS-only, explicit-only (never auto-detected), and has no secondmate support. +Pick Orca if you run the Orca app as your terminal environment and want firstmate tasks to live in Orca-managed worktrees and terminals instead of a treehouse/tmux pair. +Orca runs in two shapes: the Orca desktop app on macOS, or a headless `orca serve` runtime on Linux. +It is explicit-only (never auto-detected) and has no secondmate support. Prerequisites: -- The Orca app installed at `/Applications/Orca.app`, and **running**. -- The `orca` CLI: `brew install orca`. +- The `orca` CLI on `PATH`, backed by a **running Orca runtime** reporting `reachable=true` and `state="ready"` - either the Orca desktop app on macOS or a headless `orca serve` process on Linux (see "Runtime shapes" below). + Note that `brew install orca` installs an unrelated package (Plotly's graphing library), not the Orca agentic CLI; install Orca from its own distribution. - `node`, used by firstmate's adapter to parse Orca's JSON output and to gate spawns on runtime readiness. - The universal firstmate prerequisites - a verified crew harness plus the required toolchain, owned by [`docs/configuration.md`](configuration.md) ("Harness support", "Toolchain") - with `orca` as the only backend-specific tool, since Orca replaces both the session multiplexer CLI and the `treehouse` worktree provider that the other backends require. +### Runtime shapes + +Orca's runtime owns the project repos, worktrees, and terminals it creates, and firstmate's adapter assumes all of those share one filesystem view with the firstmate process: the recorded `worktree=` and `project=` paths are read as local paths, the crewmate brief is read by a local `cat`, and the harness runs off local `PATH`. +Two runtime shapes satisfy that contract. + +**macOS desktop.** +The Orca app runs as a GUI at `/Applications/Orca.app`; firstmate, the repos, and the worktrees all live on the same Mac. +This is the shape the original adapter was built and smoke-verified against. + +**Linux headless (collocated).** +Firstmate, the project clones, Orca worktrees, terminals, and a headless `orca serve` runtime all run on one Linux host. +The Orca desktop app on a Mac, or a mobile client, may pair to that runtime as a viewer or interaction surface over the pairing address - it does not execute tasks, and firstmate never drives a remote runtime. +Verified 2026-07-15 on `oracle-vps` (Linux aarch64, kernel `6.8.0-1054-oracle`) against Orca `1.4.141`: the runtime is installed under `/opt/Orca` (a full Linux build of the app), the `orca` CLI resolves through a wrapper at `~/.local/bin/orca` to `/opt/Orca/resources/bin/orca-ide`, and `orca serve` starts a runtime server without a desktop window. +`orca serve --help` verbatim: *"Start an Orca runtime server without opening a desktop window"* and *"Use `--pairing-address` when clients should connect through a LAN, Tailscale, SSH-forward, or public tunnel address."* +`orca status --json` reported `result.runtime.reachable=true`, `result.runtime.state="ready"`, and `result.app.running=true`. +The runtime was supervised by a systemd user unit (`Restart=on-failure`, `RestartSec=5`, with `Linger=yes` so it survives SSH disconnects), launched as `orca-ide serve --port 6768 --pairing-address --json`. +Every lifecycle primitive firstmate's adapter calls - `status`, `repo add`, `worktree create`, `terminal create`, `terminal read`, `terminal send`, `terminal close`, `worktree rm` - returned the exact JSON shapes the adapter already parses, and the 50 fake-Orca tests in `tests/fm-backend-orca.test.sh` pass unchanged on Linux. +The Orca adapter itself contains no macOS-specific assumptions (no `uname`, no `/Applications`, no `Darwin` checks), so the code that works on macOS is the same code that works on Linux. + Select Orca by putting `orca` in a local `config/backend` file - the durable way to pick it - or by exporting `FM_BACKEND=orca` when you launch your harness for a one-off session; telling the first mate in chat to use Orca also works. It is never auto-detected. -First run: before spawn mutates any repo or worktree state, firstmate runs `orca status --json` and requires the app to report `reachable=true` and `state="ready"` - start the Orca app and wait for it to finish loading before spawning. +First run: before spawn mutates any repo or worktree state, firstmate runs `orca status --json` and requires the runtime to report `reachable=true` and `state="ready"` - start the Orca runtime (the desktop app on macOS, or `orca serve` on Linux) and wait for it to report ready before spawning. Spawn fails closed if the runtime is not ready. The first spawn against a given project also auto-registers that project's repo in Orca (`orca repo add --path`) if it is not already registered - no manual registration step is needed. -Watching and attaching: Orca owns both the worktree and the terminal for its tasks, so there is nothing to attach to outside the Orca app itself - open the app and find the terminal for the task (recorded as `terminal=` in the task's meta, with `window=fm-` as the shared firstmate alias). +Watching and attaching: Orca owns both the worktree and the terminal for its tasks, so there is nothing to attach to outside Orca itself - open the Orca desktop app (when running) and find the terminal for the task (recorded as `terminal=` in the task's meta, with `window=fm-` as the shared firstmate alias). You do not need to open the app for routine supervision: from an active firstmate session, `bin/fm-peek.sh ` reads a task's terminal without opening Orca, and `FM_HOME= bin/fm-send.sh ""` steers it unless `FM_HOME` is already set to the active firstmate home (the stable `fm-` alias also works; Enter and Ctrl-C are supported; Escape is not). -Verify it works by spawning a trivial task with `--backend orca` and confirming the task's meta records `backend=orca`, `terminal=`, `orca_worktree_id=`, and `worktree=`; the Orca app should show a new terminal for the task. +Verify it works by spawning a trivial task with `--backend orca` and confirming the task's meta records `backend=orca`, `terminal=`, `orca_worktree_id=`, and `worktree=`; Orca should show a new terminal for the task (visible in the desktop app when paired or running). -Limitations: `--secondmate` spawns refuse `backend=orca` (secondmate-home semantics need a separate design), Escape is unsupported, Orca is macOS-only and explicit-only, and it exposes no stable CLI version marker, so spawn gates on runtime reachability instead of a version floor - see "Limitations" below for the complete list. +Limitations: `--secondmate` spawns refuse `backend=orca` (secondmate-home semantics need a separate design), Escape is unsupported, Orca is explicit-only, and it exposes no stable CLI version marker, so spawn gates on runtime reachability instead of a version floor - see "Limitations" below for the complete list. ## Status @@ -98,13 +118,19 @@ Teardown: - Escape is unsupported because the current Orca terminal send primitive exposes Enter and interrupt-style input but no verified Escape operation. - Orca is explicit-only and is not selected by runtime auto-detection. - Orca currently exposes no stable CLI version or protocol marker. Unlike the herdr/zellij/cmux docs, this backend intentionally gates spawn support on runtime reachability from `orca status --json` rather than a version floor. +- `fm_backend_agent_alive` reports `unknown` for Orca (it has no native agent-liveness primitive), so the secondmate-liveness confident-respawn path does not apply to Orca tasks; this is pre-existing and not a regression of the Linux shape. ## Verification -Real-Orca smoke verification was run against `/usr/local/bin/orca` with `/Applications/Orca.app` reporting bundle version `1.4.116`; `orca status --json` reported `result.runtime.reachable=true` and `result.runtime.state="ready"`. +macOS desktop: real-Orca smoke verification was run against `/usr/local/bin/orca` with `/Applications/Orca.app` reporting bundle version `1.4.116`; `orca status --json` reported `result.runtime.reachable=true` and `result.runtime.state="ready"`. The verified terminal creation handle field is `result.terminal.handle` from `orca terminal create --json`; worktree creation returned `result.worktree.id` and `result.worktree.path` in the same smoke run. Firstmate intentionally ignores speculative terminal-handle shapes such as bare `result.id` and nested `result.worktree.terminal` until a real Orca smoke run proves them. +Linux headless: the full lifecycle was exercised 2026-07-15 against `orca` `1.4.141` on `oracle-vps` (Linux aarch64, kernel `6.8.0-1054-oracle`), with the runtime headless under a systemd user unit running `orca-ide serve`. +`orca status --json` reported `result.runtime.reachable=true`, `result.runtime.state="ready"`, and `result.app.running=true`. +Real CLI calls against a throwaway repo confirmed every primitive the adapter calls: `orca repo add --path` (returned `result.repo.id`), `orca worktree create` (returned `result.worktree.id` and `result.worktree.path`), `orca terminal create` (returned `result.terminal.handle`), `orca terminal send` plus `orca terminal read` round-trip, `orca terminal close`, and `orca worktree rm` - all returning the JSON shapes the adapter parses. +The 50 fake-Orca tests in `tests/fm-backend-orca.test.sh` pass unchanged on Linux, and the adapter has no macOS-specific code paths. + Fake-Orca tests cover: - helper parsing for repo registration, worktree creation, verified implicit-terminal reuse, terminal creation, terminal sends, and worktree removal; diff --git a/tests/fm-bootstrap.test.sh b/tests/fm-bootstrap.test.sh index 2b3399e58..4067a0c19 100755 --- a/tests/fm-bootstrap.test.sh +++ b/tests/fm-bootstrap.test.sh @@ -349,7 +349,7 @@ SH test_orca_backend_gates_orca_tool_only_when_selected() { local case_dir fakebin out missing_orca - missing_orca="MISSING: orca (install: brew install orca # or the platform's package manager)" + missing_orca="MISSING_MANUAL: orca (instructions: docs/orca-backend.md)" case_dir="$TMP_ROOT/orca-backend-selected" mkdir -p "$case_dir/home/config" @@ -370,6 +370,16 @@ test_orca_backend_gates_orca_tool_only_when_selected() { pass "bootstrap: backend=orca gates the Orca CLI without requiring it on the default backend" } +test_orca_install_requires_manual_action() { + local out status + out=$("$ROOT/bin/fm-bootstrap.sh" install orca 2>&1) + status=$? + [ "$status" -ne 0 ] || fail "install orca should fail instead of evaluating a fabricated package-manager command" + [ "$out" = "error: orca requires manual installation (instructions: docs/orca-backend.md)" ] \ + || fail "install orca should return actionable manual-install guidance, got: $out" + pass "bootstrap: Orca manual-install guidance is never executed as a shell command (no 'brew install orca')" +} + # Build a fake toolchain with tmux REMOVED and the named backend session CLI(s) # plus jq added, so a backend that must NOT require tmux can be proven silent # with tmux absent. Echoes the fakebin dir. The removed tmux is what makes these @@ -700,6 +710,7 @@ test_bootstrap_reporting test_no_mistakes_min_version test_git_is_required_with_supported_install_instruction test_orca_backend_gates_orca_tool_only_when_selected +test_orca_install_requires_manual_action test_session_provider_backends_do_not_require_tmux test_session_provider_backends_gate_own_cli_not_tmux test_herdr_install_requires_manual_action