76e4b577a0
Rename the eleven MCP tool names (bridge_ack/ask/list/poll/profiles/reply/ send/spawn/status/stop/whoami) to their fleet_* names across the Markdown documentation. fleet_* is written as the normal name; one deprecation note in README.md says bridge_* still works for one release. docs/MCP-Contract.md is renamed only inside section 6 (lines 210-293): sections 1-5 and 7-11 are stale pre-build design text (CB-609) and are deliberately left with old names so dead text does not look maintained. Also renames e2e/bridge_ask_transcript.md to e2e/fleet_ask_transcript.md to match its content. CLAUDE.md, wiki/, plugin/skills/setup/SKILL.md and .claude/skills/port-to-opencode/SKILL.md are owned by other units and are untouched.
138 lines
7.1 KiB
Markdown
138 lines
7.1 KiB
Markdown
# Worker startup: working directory & the folder-trust prompt
|
|
|
|
When `bridged` spawns a worker, the worker CLI may show an **interactive startup prompt** before it
|
|
is ready to accept a task — most importantly a *"Do you trust the files in this folder?"* dialog. An
|
|
unattended worker parked on that prompt never becomes injectable: the status-gated injector waits for
|
|
`idle`/`blocked`, the task is never delivered, and (worst case) a stray Enter answers the dialog
|
|
wrong. How this is handled depends on two things:
|
|
|
|
1. **The worker's working directory** — which folder the CLI is asked to trust.
|
|
2. **Which CLI launches the worker** — each has its own first-run/trust behaviour.
|
|
|
|
This doc pins the current assumption (**`ccs`** is the launcher), how its trust prompt works, the
|
|
rule that **a worker inherits the primary's directory** (never `$HOME`), and how other CLIs differ.
|
|
|
|
```mermaid
|
|
flowchart TD
|
|
A["fleet_spawn / POST /workers"] --> B{"explicit cwd?<br/>(profile cwd or spawn arg)"}
|
|
B -->|"yes — told otherwise"| C["use that cwd"]
|
|
B -->|"no"| D{"caller PID resolvable?<br/>(MCP peer PID)"}
|
|
D -->|"yes"| E["cwd = the primary's cwd<br/>lsof -a -p PID -d cwd"]
|
|
D -->|"no (REST / off-host)"| F["cwd = bridged daemon cwd<br/>(never $HOME by assumption)"]
|
|
C --> G["ensureWorkspace → tab.create → agent.start {cwd}"]
|
|
E --> G
|
|
F --> G
|
|
G --> H{"does the CLI trust this folder?"}
|
|
H -->|"yes"| I["worker reaches its prompt → injectable"]
|
|
H -->|"no"| J["worker BLOCKS on the trust dialog<br/>never injectable"]
|
|
classDef good fill:#2f855a,stroke:#22543d,color:#ffffff;
|
|
classDef bad fill:#9b2c2c,stroke:#63171b,color:#ffffff;
|
|
class I good
|
|
class J bad
|
|
```
|
|
|
|
*Figure 1 — spawn resolves a working directory, then the CLI's trust check gates readiness.*
|
|
|
|
## Working directory: inherit the primary's path
|
|
|
|
**Rule: a worker opens the same directory the primary (main) session is working in, unless told
|
|
otherwise. Never assume `$HOME`.** If the primary is in `/Users/you/LTMS/claude-bridge`, its workers
|
|
open there too — so delegated tasks share the same relative paths and the same (already-trusted)
|
|
project folder.
|
|
|
|
**The herdr seam.** An `agent.start` pane does **not** inherit its tab's or workspace's cwd — it
|
|
starts in `$HOME` unless told otherwise. herdr's `agent.start` honours an (undocumented) **`cwd`**
|
|
param, verified live: setting it roots the worker process at that directory. So the worker's cwd is
|
|
threaded onto `agent.start {…, cwd}`, not the placement step (`workspace.create`/`tab.create` cwd
|
|
only affect the seed shell, which the bridge closes).
|
|
|
|
**Resolution order** (first match wins):
|
|
|
|
| # | Source | When |
|
|
|---|--------|------|
|
|
| 1 | Explicit `cwd` — a per-profile `cwd:` in config, or a spawn argument | "told otherwise" — pin a fixed workdir |
|
|
| 2 | The **primary's cwd**, auto-detected from the `fleet_spawn` caller | normal MCP spawn from the primary |
|
|
| 3 | The `bridged` daemon's own cwd | REST spawn / off-host caller — **never `$HOME`** |
|
|
|
|
The primary's cwd (source 2) is discoverable with no new plumbing: `bridged` already resolves the MCP
|
|
caller's loopback **peer PID** for connection identity (`ConnectionIdentity` → `LsofPeerPidLookup`);
|
|
the same PID yields its cwd via `lsof -a -p <pid> -d cwd -Fn` (the `n…` line). The primary maps to no
|
|
worker pane (it is not a worker), but its PID and cwd are still readable.
|
|
|
|
```mermaid
|
|
sequenceDiagram
|
|
participant P as "Primary (main)"
|
|
participant B as "bridged"
|
|
participant O as "OS (lsof)"
|
|
participant H as "herdr"
|
|
P->>B: "fleet_spawn {profile} (no cwd)"
|
|
B->>O: "peer PID for this connection's port"
|
|
O-->>B: "pid"
|
|
B->>O: "cwd of pid (lsof -d cwd)"
|
|
O-->>B: "/Users/you/LTMS/claude-bridge"
|
|
B->>H: "tab.create (placement)"
|
|
B->>H: "agent.start {argv, env, tab_id, cwd}"
|
|
H-->>B: "worker in the primary's directory"
|
|
```
|
|
|
|
*Figure 2 — a no-cwd spawn inherits the primary's directory from the caller's PID.*
|
|
|
|
> **Status:** implemented (CB-112). `bridged` threads the resolved `cwd` onto **`agent.start {cwd}`**
|
|
> (verified: the worker process is rooted there), keeping the single shared worker space. On an MCP
|
|
> `fleet_spawn` the primary's cwd is auto-detected from the caller's PID; over REST (no MCP caller)
|
|
> it is the explicit `cwd` param else the daemon's cwd. Both placements (`tab` and legacy `pane`)
|
|
> carry it, since it rides `agent.start`.
|
|
|
|
## Assumed launcher: `ccs` (Claude Code)
|
|
|
|
For now the fleet assumes **`ccs`** (Claude Code under the hood) as the worker CLI — `argv: ["ccs",
|
|
"<profile>"]`. Its startup gate is the **folder-trust dialog**.
|
|
|
|
### How `ccs`/Claude Code decides whether to prompt
|
|
|
|
Trust is recorded **per-directory, per config dir**. Each `ccs` profile is an isolated instance with
|
|
its own config dir (`~/.ccs/instances/<profile>/`) and its own `.claude.json`:
|
|
|
|
```jsonc
|
|
// ~/.ccs/instances/<profile>/.claude.json
|
|
"projects": {
|
|
"/Users/you/LTMS/claude-bridge": { "hasTrustDialogAccepted": true }, // trusted → no prompt
|
|
"/Users/you": { "hasTrustDialogAccepted": false } // untrusted → prompts
|
|
}
|
|
```
|
|
|
|
The worker prompts **iff** its cwd is not marked `hasTrustDialogAccepted: true` for *that instance*.
|
|
This is why the directory rule above matters: land workers in the primary's project folder and you
|
|
grant trust **once per profile**, instead of scattering trust across `$HOME` and ad-hoc dirs.
|
|
|
|
### Clearing the prompt (ranked)
|
|
|
|
1. **Inherit the primary's project dir** (the rule above) and trust that folder once per profile.
|
|
2. **Pre-trust interactively:** run `ccs <profile>` in the target folder and accept — persists
|
|
`hasTrustDialogAccepted: true` for that path in the instance's `.claude.json`.
|
|
3. **Set the flag directly** (scriptable, no interaction): set
|
|
`projects["<cwd>"].hasTrustDialogAccepted = true` in `~/.ccs/instances/<profile>/.claude.json`.
|
|
4. **Do not** reach for `--dangerously-skip-permissions` — it disables *all* permission gating, not
|
|
just this dialog, which defeats running off-subscription workers autonomously.
|
|
|
|
## Other CLIs: different launchers, different prompts
|
|
|
|
`ccs` is the current assumption, not a hard dependency — a worker profile's `argv` can be any CLI.
|
|
Each launcher has its **own** first-run/trust gate, so the "clear the prompt" step is CLI-specific
|
|
and belongs with the profile, not hard-coded:
|
|
|
|
| Launcher (`argv`) | Startup gate | How to clear it |
|
|
|---|---|---|
|
|
| `ccs <profile>` (Claude Code) | Folder-trust dialog | `hasTrustDialogAccepted` per project in the instance's `.claude.json` (above) |
|
|
| Other Claude-compatible runtimes via `ccs` (codex, gemini, cursor, …) | Each has its own first-run / trust / login prompt | Per-runtime; document per launcher as it is adopted |
|
|
| A bare command (`bash -c …`, mechanics probe) | None | n/a — used for non-interactive smoke tests |
|
|
|
|
When adding a new launcher, capture its startup-prompt behaviour here (what blocks, and the
|
|
non-interactive way to satisfy it) so a spawned worker of that kind reaches an injectable prompt
|
|
unattended.
|
|
|
|
## See also
|
|
|
|
- `docs/MCP-Contract.md` — the tool surface (`fleet_spawn`, `fleet_profiles`, …).
|
|
- `wiki/2-Message-Server.md` — the herdr `agent.*` / `workspace.*` schema (`workspace.create {cwd}`).
|