From 55cb3479042210d52e315e828fa96e992db0c276 Mon Sep 17 00:00:00 2001 From: Sawyer Cutler Date: Sun, 13 Sep 2026 13:43:15 -0700 Subject: [PATCH] docs(directors): refresh stale skill-bake prose left by worker skill mounting Worker mounts from #982 made six prose sites stale: workers now mount skill_search + use_skill scoped to optionalSkills instead of baking bodies. Correct the count (20 packages), drop the bake parenthetical, and reattach the orphaned identity JSDoc. No behavior change. --- docs/ARCHITECTURE.md | 2 +- docs/IMPLEMENTATION.md | 4 ++-- src/agent/directors/draper/package.ts | 3 ++- src/agent/directors/identity.ts | 16 ++++++++-------- src/agent/directors/types.ts | 2 +- 5 files changed, 14 insertions(+), 13 deletions(-) diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index ede985e6d..f25f65a98 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -329,7 +329,7 @@ The primary session identity is **Skywalker** (`buildChatRole` → `createSkywal **Provider-conditional residuals.** Per-family additions layer on top of the shared block via the same `ModelFamilyPolicy` mechanism the directors use (`src/subagent/provider-family.ts`, `src/agent/model-family-policy.ts`) — additive lines, never prompt forks. **Grok** leaves get `buildGrokLeafAntiThrashNote` (gated by `shouldApplyGrokAntiThrash` / `applyGrokFinishBias`, withheld from orchestrators): a compact finish-bias reinforcement plus a one-line reminder to route file/web work through the dedicated tools rather than `run_shell`, motivated by observed tool-routing thrash on the same harness. **Kimi** intentionally has no residual yet — `detectModelFamily` already resolves the family so callers can branch on it, but the prompt seam is left unfilled pending eval characterization of Kimi's behavior, mirroring the provisional (permissive-default) policy in `model-family-policy.ts`. -`buildChatSystemPrompt` (TUI chat) assembles: base → core tool list → name-only skills listing → live `` block → appended extensions. `buildSubAgentSystemPrompt` assembles the worker prompt without a skills listing (workers bake optional skill bodies; they do not mount `use_skill` / `skill_search`). Built-in catalog tools (including `skill_search`) are advertised on the primary wire and callable directly; MCP integrations are discovered via `tool_search` rather than being enumerated. Skills follow the same lazy principle: each discovered skill contributes only its name to the primary prompt. The model calls `skill_search` for descriptions when choosing, then `use_skill` to load a body. The operator can also invoke the same skill as `/` (see Skills below). Skills are discovered (and deduped by name, first-wins) from enabled plugin dirs, then `.agents`/`.claude`/`.codex/skills`, in that precedence. Corbits Code ships a bundled catalog via the first-party `corbits-skills` plugin (origin `repo`); project-local skills of the same name are shadowed by an enabled plugin skill. +`buildChatSystemPrompt` (TUI chat) assembles: base → core tool list → name-only skills listing → live `` block → appended extensions. `buildSubAgentSystemPrompt` assembles the worker prompt without a skills listing. Built-in catalog tools (including `skill_search`) are advertised on the primary wire and callable directly; MCP integrations are discovered via `tool_search` rather than being enumerated. Skills follow the same lazy principle: each discovered skill contributes only its name to the primary prompt. The model calls `skill_search` for descriptions when choosing, then `use_skill` to load a body. The operator can also invoke the same skill as `/` (see Skills below). Skills are discovered (and deduped by name, first-wins) from enabled plugin dirs, then `.agents`/`.claude`/`.codex/skills`, in that precedence. Corbits Code ships a bundled catalog via the first-party `corbits-skills` plugin (origin `repo`); project-local skills of the same name are shadowed by an enabled plugin skill. **Overrides.** `loadSystemPromptOverrides` (`src/agent/context-extensions.ts`) resolves a project `SYSTEM.md` (repo root, then `.corbits/`) that **replaces** the static base block, and an `APPEND_SYSTEM.md` that is **appended** as an extension. These compose with `config.systemPromptExtensions` (profile config) and the auto-discovered `AGENTS.md`, all of which attach as appended sections after the base. diff --git a/docs/IMPLEMENTATION.md b/docs/IMPLEMENTATION.md index 8c00f53fe..7e7c7e3cf 100644 --- a/docs/IMPLEMENTATION.md +++ b/docs/IMPLEMENTATION.md @@ -157,7 +157,7 @@ docs/ ### Closed director fleet -Sixteen packages under `src/agent/directors//` register in `DIRECTOR_REGISTRY` (`registry.ts`). Wire path: +Twenty packages under `src/agent/directors//` register in `DIRECTOR_REGISTRY` (`registry.ts`). Wire path: 1. `spawn_agent(agent=…)` / `spawn_agent(intent=…)` → `resolveDirector` in `agent-fleet.ts` before tools and system prompt are built. Bare `spawn_agent` (neither field) and `intent=general` fail closed. After director resolution, `createSpawnAgentTool` fail-closes implement/review (and their default directors) without non-empty `success_criteria`. 2. `packageToProfile` maps envelope (`tools.allow`/`deny`) to `AgentProfile.capabilities` and `spawn.maySpawn` → `orchestrator`. System prompts are prefixed with a stable identity block (`formatDirectorSystemPrompt`: agent id, model role, optional skills). @@ -168,7 +168,7 @@ Sixteen packages under `src/agent/directors//` register in `DIRECTOR_REGISTR **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` — 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. 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 mutating dispatch that lands on the same cwd as a live mutating peer (`pending_init`/`running`, and not a declared read-only `modelRole` of `explore`/`plan`/`review`/`test`) records at most one `concurrent-lane-overlap` entry per cwd wave in `intervention-log.ts` (class `conflict`). The wave flag clears when no live mutating writer remains for that cwd. Terminal-but-unsettled lanes (for example cancelled with `finishedAt` set while the run promise has not reached `finally`) are pruned from the map and do not warn. This is advisory only — it never blocks the spawn, since cwd overlap does not prove the two lanes touch the same files. -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. +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 mount `skill_search` + `use_skill` scoped to the dispatch's `optionalSkills` and load bodies on demand (grok/kimi leaves omit `skill_search` and load brief-named skills straight through `use_skill`). Primary mounts `use_skill` for its own skill list. Intent defaults: `intent=implement` → director `builder`; `explore` → `explorer`; `plan` → `counsel`; `review` → `critic`; general → error. Spawn: skywalker full fleet; all other directors, including greybeard, mount no fleet tools. Live `` injects cwd, platform, arch, runtime, date, and git status on every chat and worker prompt. diff --git a/src/agent/directors/draper/package.ts b/src/agent/directors/draper/package.ts index 2e5410140..9bd5d00ff 100644 --- a/src/agent/directors/draper/package.ts +++ b/src/agent/directors/draper/package.ts @@ -12,7 +12,8 @@ import { REVIEW_TOOLS } from "../tool-sets.js"; * * Deviations from the original (deliberate Corbits translations): * 1. Brand references: the original loads the `brand-identity` skill and - * agents-repo `references/*.md` paths. Workers do not mount use_skill, + * agents-repo `references/*.md` paths. Skill bodies load on demand + * scoped to the dispatch's optionalSkills, and Draper declares none, * so Draper loads only in-repo references relevant to the active * lenses (DESIGN.md, design tokens, brand docs already in the tree) * plus the mounted read/search/web tools. diff --git a/src/agent/directors/identity.ts b/src/agent/directors/identity.ts index 23b29e91e..2fd4df008 100644 --- a/src/agent/directors/identity.ts +++ b/src/agent/directors/identity.ts @@ -3,23 +3,23 @@ import type { ModelRole } from "./types.js"; import type { ReasoningEffort } from "../../provider/reasoning-effort.js"; /** - * Prefix every director system prompt with a stable identity block so the model - * always sees agent id, model role, and optional skills — no ambiguity about which - * package it is or how the parent should re-spawn it. + * Worker skill-use rule: skills mount on every worker (scoped to the + * dispatch's optionalSkills), so the worker searches only when the brief + * names a skill or the task leaves its lane. * * Skill bodies are never baked here. Workers (non-orchestrator) list skill * names only and load bodies on demand with skill_search + use_skill, scoped * to the dispatch's optionalSkills. Primary orchestrator (skywalker): * use_skill is mounted — list skill names only. */ -/** - * Worker skill-use rule: skills mount on every worker (scoped to the - * dispatch's optionalSkills), so the worker searches only when the brief - * names a skill or the task leaves its lane. - */ export const WORKER_SKILL_SCOPING = "Skills are available; search only when the brief names a skill or the task is outside your lane. For a small, bounded edit, do not search skills."; +/** + * Prefix every director system prompt with a stable identity block so the model + * always sees agent id, model role, and optional skills — no ambiguity about which + * package it is or how the parent should re-spawn it. + */ export function formatDirectorSystemPrompt(pkg: DirectorPackage): string { const names = pkg.optionalSkills; const isPrimaryOrchestrator = pkg.tier === "orchestrator"; diff --git a/src/agent/directors/types.ts b/src/agent/directors/types.ts index f317fcef6..58445989c 100644 --- a/src/agent/directors/types.ts +++ b/src/agent/directors/types.ts @@ -102,7 +102,7 @@ export interface DirectorPackage { readonly description: string; /** Opinionated core prompt (prompt-first). */ readonly systemPrompt: string; - /** Optional skill names (ordered). Workers bake matching first-party bodies into the prompt; the primary orchestrator keeps them use_skill-loadable. */ + /** Optional skill names (ordered). Workers load matching bodies on demand with skill_search + use_skill, scoped to the dispatch's optionalSkills; the primary orchestrator keeps them use_skill-loadable. */ readonly optionalSkills?: readonly string[]; readonly tools?: ToolEnvelope; readonly spawn: SpawnRights;