Skip to content

Commit 887b2b4

Browse files
committed
Call spawned workers fleet agents, not leaves
1 parent 4a264d1 commit 887b2b4

36 files changed

Lines changed: 143 additions & 127 deletions

File tree

CHANGELOG.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,9 @@ parallel copies under `docs/` or `scripts/notes/`. At cut time: rename
5050
- Workers can ask their spawning director via `ask_director` when a brief is
5151
ambiguous. `wait_agents` returns `awaiting_director` with the question;
5252
soft `send_input` answers it. The operator is not in that loop unless the
53-
director escalates. Leaves still cannot call `ask_operator`.
53+
director escalates. Workers still cannot call `ask_operator`.
54+
- Spawned workers are called fleet agents, not leaves. Director packages still
55+
use `tier: "leaf"` as the runtime mount key.
5456
- Agents and contributor docs move Linear issues to In Review when a PR is ready
5557
for review.
5658

docs/ARCHITECTURE.md

Lines changed: 25 additions & 23 deletions
Large diffs are not rendered by default.

docs/IMPLEMENTATION.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ Sixteen packages under `src/agent/directors/<id>/` register in `DIRECTOR_REGISTR
160160
4. `directorProfiles()` is the spawn catalog (`default-agents.ts`) — closed set minus skywalker. Plugin and local `.agents/agents/` profiles still load, but closed `DIRECTOR_IDS` cannot be overridden or aliased.
161161
5. Primary chat role is Skywalker: `buildChatRole()``createSkywalkerSystemPrompt()`. Product mutation tools (`write_file` / `edit_file` / `delete_file`) live in CORE (and `SKYWALKER_TOOLS`) so they are advertised on the primary without a `tool_search` round-trip. DIY tiny/bounded edits on the parent; spawn builder/docs directors for substantial work — a prompt judgment call, not a toolset strip. `PRIMARY_DENIED_PRODUCT_TOOLS` is gone. Shell file-writes stay denied; MCP tools are not re-filtered by a product-write deny list. There is no static per-profile write-path lock (CL-6952).
162162

163-
**Codex tool proxies.** When the active provider is Codex (`isCodexProviderName`), `createAgentToolset` and `runSubAgent` mount `apply_patch`, `shell`, and `update_plan` stringTools from `createCodexToolProxies`, all forwarding through the same posix `ToolRunner` seam (`runTool`) so permission plugins still apply. `apply_patch` parses the Codex envelope and forwards each op (`write_file` / `delete_file` / `read_file`). `shell` — the native Codex name is `shell`, not `exec_command`, per the pinned base-instructions text quoted in `codex-responses-adapter.ts`'s bridge message — normalizes Codex's `command` (string or `["bash","-lc",script]`-style argv array), `workdir`, and `timeout_ms` onto `run_shell`'s `{command, cwd?, timeout?}` and is gated by `allowShellFromCapabilities` (mirrors `allowDeleteFromCapabilities` against `run_shell`). `update_plan` maps Codex's `plan: [{step, status}]` onto `manage_tasks(action: "create")`; `pending`/`in_progress`/`completed` map to `todo`/`doing`/`done` — `manage_tasks`'s `cancelled` status has no Codex equivalent and is never produced by this proxy. Primary strips `apply_patch` after mount (Corbits DIY stays on `write_file` / `edit_file` / `delete_file`); `shell` and `update_plan` stay on primary (same classification as `run_shell` / `manage_tasks`). Build and docs leaf allowlists (`BUILD_TOOLS` / `DOCS_TOOLS`) include `apply_patch` so Codex workers keep the proxy after the capability filter. `CORE_TOOL_NAMES` does not list it.
163+
**Codex tool proxies.** When the active provider is Codex (`isCodexProviderName`), `createAgentToolset` and `runSubAgent` mount `apply_patch`, `shell`, and `update_plan` stringTools from `createCodexToolProxies`, all forwarding through the same posix `ToolRunner` seam (`runTool`) so permission plugins still apply. `apply_patch` parses the Codex envelope and forwards each op (`write_file` / `delete_file` / `read_file`). `shell` — the native Codex name is `shell`, not `exec_command`, per the pinned base-instructions text quoted in `codex-responses-adapter.ts`'s bridge message — normalizes Codex's `command` (string or `["bash","-lc",script]`-style argv array), `workdir`, and `timeout_ms` onto `run_shell`'s `{command, cwd?, timeout?}` and is gated by `allowShellFromCapabilities` (mirrors `allowDeleteFromCapabilities` against `run_shell`). `update_plan` maps Codex's `plan: [{step, status}]` onto `manage_tasks(action: "create")`; `pending`/`in_progress`/`completed` map to `todo`/`doing`/`done` — `manage_tasks`'s `cancelled` status has no Codex equivalent and is never produced by this proxy. Primary strips `apply_patch` after mount (Corbits DIY stays on `write_file` / `edit_file` / `delete_file`); `shell` and `update_plan` stay on primary (same classification as `run_shell` / `manage_tasks`). Build and docs worker allowlists (`BUILD_TOOLS` / `DOCS_TOOLS`) include `apply_patch` so Codex workers keep the proxy after the capability filter. `CORE_TOOL_NAMES` does not list it.
164164

165165
6. There is no static write-path declaration on packages or profiles (CL-6952 removed it — no shipped director ever set one). Instead, `agent-fleet.ts` tracks each running dispatch by cwd; a new dispatch that lands on the same cwd as a still-running lane records a `concurrent-lane-overlap` entry in `intervention-log.ts` (class `conflict`). This is advisory only — it never blocks the spawn, since cwd overlap does not prove the two lanes touch the same files.
166166
7. Spawn effort: pin > package `modelRole` default (`defaultEffortForDirector`; intern=low; plan/review/orchestrator=high; implement/explore/docs/test=medium) > orchestrator/worker binary > parent inheritance. Optional skills are listed in the identity header for awareness; workers do not mount `use_skill` (guidance is baked into package system prompts). Primary mounts `use_skill` for its own skill list.

docs/PLUGINS.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ The kind-specific export is the implementation hook:
146146
| `command` | `commandPlugin` | slash-command registry | slash commands |
147147
| `workflow` | `workflowPlugin` + optional `commandPlugin` | workflow registry + slash-command registry | named workflow recipes behind an integration command prefix |
148148
| `tool` | `toolPlugin` (factory) | posix toolset | add new agent tools (highest trust) |
149-
| `agent` | `agentPlugin` | sub-agent profiles | contribute `spawn_agent`-dispatchable agent profiles |
149+
| `agent` | `agentPlugin` | fleet-agent profiles | contribute `spawn_agent`-dispatchable agent profiles |
150150

151151
A module with no valid manifest is ignored (not silently half-loaded).
152152

docs/PRODUCT.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ The evidence is in how the product fails today: the personas already produce exc
3535
## Key Value Propositions
3636

3737
1. **Deterministic progress** — Every turn must produce a tool call. No idle thinking; the director aborts a stalled run rather than spinning.
38-
2. **Task tracking** — The agent can maintain a `manage_tasks` checklist for multi-step work; non-interactive `submit_output` is blocked while checklist items remain open. (A "task" here is a work item, not a child agent — spawning uses the separate `spawn_agent` / `wait_agents` sub-agent surface.)
38+
2. **Task tracking** — The agent can maintain a `manage_tasks` checklist for multi-step work; non-interactive `submit_output` is blocked while checklist items remain open. (A "task" here is a work item, not a child agent — spawning uses the separate `spawn_agent` / `wait_agents` fleet-agent surface.)
3939
3. **Stall detection** — The director detects idle cycles and intervenes.
4040
4. **Safe by default** — Consequential actions (writes, edits, shell) pass a permission gate; secret files and catastrophic commands are denied outright, regardless of intent.
4141
5. **Resume capability** — Runs persist to a git-backed store and resume from the last point after interruption.
@@ -151,7 +151,7 @@ Capabilities beyond the core toolset are opt-in plugins, enabled per workspace t
151151

152152
- **Web search and fetch**`web_search`/`web_fetch` are always-on built-in core tools, no plugin or API key required. `web_fetch` runs in-process (Bun native `fetch()`) with SSRF guarding, a 5 MB response cap, and HTML-to-markdown conversion; `web_search` calls a keyless hosted MCP provider (Exa by default, Parallel optional). See `docs/ARCHITECTURE.md` for details.
153153

154-
## Multi-agent (sub-agents)
154+
## Multi-agent (fleet agents)
155155

156156
The primary session is always **orchestrator** (single-agent mode is gone). Its identity is **Skywalker** (product name remains Corbits Code; when asked its name, answer Skywalker): classify work, DIY tiny/single-file/one-route product edits, dispatch a **closed fleet of 16 directors** for substantial work, track the fleet, and synthesize. Product mutation tools (`write_file` / `edit_file` / `delete_file`) are mounted on the primary (CORE / `SKYWALKER_TOOLS`) — path tools are the DIY surface; spawn remains the default for substantial, multi-file, parallel, or specialist work. Shell file-writes stay denied. MCP tools are not re-filtered by a product-write deny list (that list is gone). There is no static per-package write-path declaration (CL-6952 removed it — no shipped director ever set one). A concurrent dispatch landing on the same working directory as another still-running lane is recorded as a `conflict` intervention, not blocked. Operator slash recipes (`/implement`, `/plan`, `/refactor`, `/review`, `/pull-request-review`, `/create-issue`, `/scribe`, `/interview`, `/ast-grep`) tell Skywalker which directors to spawn for substantial work; tiny/bounded edits may run on the primary.
157157

@@ -164,11 +164,11 @@ The primary session is always **orchestrator** (single-agent mode is gone). Its
164164

165165
There is **no catch-all worker**. `spawn_agent` requires `agent=…` or a non-general `intent` (implement/explore/plan/review→critic); bare dispatch and `intent=general` are refused. Named `spawn_agent(agent=…)` selects a director package without requiring a plugin profile, except `skywalker` which is the primary session identity and is refused as a spawned worker. Nested spawn is runtime-enforced: only skywalker (full fleet allowlist) and greybeard (intern/explorer/critic) may spawn; other workers have no fleet tools. Primary omits an allowlist so plugin profiles remain reachable from the main session.
166166

167-
Corbits Code fans work out to short-lived **sub-agents**child agents with their own loop, tools, and checklist — while the primary session stays focused.
167+
Corbits Code fans work out to short-lived **fleet agents**workers with their own loop, tools, and checklist — while the primary session stays focused.
168168

169169
- **Agents** are runtime entities (primary session or child).
170170
- **Tasks** are checklist items owned by one agent via `manage_tasks`.
171-
- **Sub-agents** are spawned with `spawn_agent` / `wait_agents`.
171+
- **Fleet agents** are spawned with `spawn_agent` / `wait_agents`. Workers ask the parent with `ask_director`. When `wait_agents` returns status `running` plus a question payload, the parent answers with `send_input`, then `wait_agents` again. Escalate to the human only with `ask_operator`.
172172

173173
Dispatch uses a structured brief (context / goal / optional goals seed) and returns a structured report. The TUI Agents strip and fleet board show who is running; live tool progress updates the status bar without dumping the child transcript into the parent chat. There is no turn budget. A tool-less final turn completes only with the four-heading report envelope; without it, one nudge is given and a second tool-less turn without the envelope salvages as `incomplete-report-stop`. A silent worker (no activity for `stallTimeoutMs`, opt-in) gets one continuation nudge, then salvages as `stalled` if a second consecutive check finds no activity. An opt-in `deadlineMs`, or an operator cancel, can also end a run early. Each of these returns a salvage report so a runaway or idle child cannot quietly burn a large token budget or look done after prose alone.
174174

docs/TELEMETRY.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ milliseconds and converts.
106106
(`tool_call_count`, `tool_error_count`, `subagent_call_count`). Per-call
107107
`$ai_span` events are **off by default**. Set `CORBITS_TELEMETRY_AI_SPANS` to
108108
a truthy value (`1`, `true`, …) to restore per-call spans for debugging.
109-
Leaf `runSubAgent` workers do not emit `$ai_*`; worker rollups travel on
109+
`runSubAgent` workers do not emit `$ai_*`; worker rollups travel on
110110
`subagent_end` instead. Both TUI and exec install the same turn observer, so a
111111
worker ending during an active parent turn carries that turn's `parent_trace_id`.
112112
Pre-progress operator aborts settle with `status=cancelled` and

docs/TUI.md

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ prose into a hard-capped inset paragraph (`LIVE_THINKING_MAX_LINES`, currently 1
7272
(assistant text, a tool call, or settle), the row collapses to its opening
7373
clause with the rest behind expand. Mid-turn thinking bursts fold onto that
7474
same one row per turn (`reasoning-fold`); `inference.text.delta` grows the
75-
open assistant streaming row in place. Sub-agent spawn_agent-row thinking is a
75+
open assistant streaming row in place. Worker spawn_agent-row thinking is a
7676
separate path and is unchanged by this preview.
7777

7878
The prompt box's border carries the metadata that would otherwise cost a
@@ -120,15 +120,15 @@ printed identically, so the only way to tell them apart was to wait.
120120
waiting on something outside itself — and are told apart by motion: `blocked`
121121
holds perfectly still, which is the signal that the session is waiting on _you_.
122122

123-
While sub-agents are running, the slot reports the _fleet_, not the parent.
123+
While fleet agents are running, the slot reports the _fleet_, not the parent.
124124
`resolveTurnLabel` and `resolveRampPhase` take a `FleetProgress` roll-up and
125125
rank it above the parent's own stall clock: with live lanes the parent is
126126
idle by design, so its silence says nothing about whether the session is
127127
progressing, and reporting it was how a session with every lane wedged still
128128
read as `working`. A fleet with no stalled lane reads `working`; one
129129
stalled lane makes the whole indicator read `stalled`, which is the state that
130130
should pull an operator's eye to the panel. A blocked gate and a stopping turn
131-
still outrank the fleet. With zero running sub-agents the roll-up is empty and
131+
still outrank the fleet. With zero running fleet agents the roll-up is empty and
132132
every path through both functions behaves exactly as it does for a plain
133133
single-agent turn.
134134

@@ -249,7 +249,7 @@ when non-zero, e.g. `N done, M failed, K cancelled · nothing running`).
249249
Per-lane `done — summary` walls and live `dispatched` re-announcements
250250
are never printed.
251251

252-
`src/subagent/fleet-report.ts` is pure: it reads the same sub-agent session
252+
`src/subagent/fleet-report.ts` is pure: it reads the same fleet-agent session
253253
store and the same `agentProgress()` stall definition. Store changes drive it;
254254
a `FLEET_REPORT_SETTLE_MS` (400ms) timer lets a parallel dispatch settle into
255255
one observation. Quiet detection uses `FLEET_STALL_POLL_MS` (5s). Past
@@ -553,13 +553,13 @@ at the interrupt itself (`doInterrupt` drains after `port.interrupt()`), not
553553
left waiting on an idle event the stop may never produce (`interrupt` in
554554
`session-queue.ts` no longer clears `items`).
555555

556-
**Sub-agent lanes on redirect.** Soft steer (Enter mid-run) and follow-up
556+
**Fleet agent lanes on redirect.** Soft steer (Enter mid-run) and follow-up
557557
(queued drain) leave running workers alone — they never call
558558
`runner.ts`'s `interrupt()`, so the parent's operation signal stays live and
559559
spawned workers keep running. Ctrl+C is the explicit fleet teardown:
560560
`doInterrupt``port.interrupt()``currentAgent.close()` aborts the shared
561561
operation signal and routes cancellation through the fleet/session-store
562-
teardown path, so in-flight sub-agent dispatch reports back as cancelled by
562+
teardown path, so in-flight fleet-agent dispatch reports back as cancelled by
563563
the operator rather than being left to finish silently detached. `/clear` and
564564
session exit still call `subAgentSessions.cancelAll` for an explicit
565565
session-wide cancel; that path is separate from interrupt and must stay off
@@ -616,7 +616,7 @@ Ctrl+C within a 2-second window (`CTRL_C_EXIT_WINDOW_MS`) quits — this
616616
replaced an Ink-era yes/no exit-confirm modal with the same intent (an
617617
explicit second confirmation) without adding a modal (`handleCtrlC`,
618618
`shell.ts`). See "Soft steer vs. follow-up" above for the two
619-
mid-run gestures and what interrupting does to sub-agent lanes. The interrupt
619+
mid-run gestures and what interrupting does to fleet-agent lanes. The interrupt
620620
keeps whatever is sitting in the queue rather than discarding it — the
621621
operator typed those messages meaning them delivered, not meaning "cancel
622622
this run and also throw away what I typed"; the transcript row says so

0 commit comments

Comments
 (0)