# Agent Fleet — Project Files agent-fleet keeps up to two small files in a project's `.ai/` directory. They have different readers and different lifetimes, so they are kept separate. | File | Read by | When | |------|---------|------| | `.ai/agent-fleet-overrides.md` | `spec-driven-development`, `planning-and-task-breakdown`, `browser-testing-with-devtools`, `git-workflow-and-versioning`, `compound-learning` (the `rules:`/`docs:` keys of `## agent-hub`), `agent-hub` pi harness | Every run of those skills / every session start of the harness | | `.ai/agent-fleet-setup.md` | `guided-workspace-setup` | Only when setup is run or re-run | | `.ai/stt.json` *(optional)* | `pi-voice-stt` extension | Every pi session start, when the extension is installed | The `.ai/stt.json` file is present only when the optional `pi-voice-stt` voice-dictation extension has been configured (by guided setup or by hand). Like the overrides file it holds **no secrets** — it names the env vars (`apiKeyEnv`, plus the Azure endpoint var) whose values live in a gitignored root `.env`. See [pi-voice-stt config](#ai-sttjson) below. Keep them split: the overrides file is loaded into context constantly, so it must stay minimal; the install record is consulted rarely, so it can be large. ## The overrides file — `.ai/agent-fleet-overrides.md` Some skills and pi harnesses need facts specific to each project — where specs and plans are saved, how to start a dev server, whether the agent may create branches, or which user-facing language a dispatcher should use. Each reader ships a sensible **default**; a project that needs something different declares it here, and the reader picks it up. - **Location:** `.ai/agent-fleet-overrides.md` at the project root. - **Format:** Markdown. One `## ` section per skill or harness reader, with terse `key: value` lines. Block values use the `key: |` multi-line form. No prose and no install detail — readers parse it by key and load it on every run/session start. - **Commit it.** Shared project configuration belongs in version control. Make sure no `.gitignore` rule (for example a broad `.env*` pattern) excludes it. - **No secrets.** For anything sensitive (test-account credentials), reference the **name** of an environment variable; the real value lives in a gitignored `.env`. - If the file is absent, or a reader has no section in it, that reader uses its built-in default. - **Validation.** Because an unknown section or key silently falls back to the default, typos are invisible at runtime. `agent-fleet doctor` (and the `/doctor-agent-fleet` command) validates the file — unknown sections, unknown keys in known sections, invalid values for the mechanically parsed `agent-hub` keys, missing `rules:` folders, and unset `## env` vars — as **advisory, warn-only findings**; it never edits the file. ### `spec-driven-development` | Key | Default | Meaning | |-----|---------|---------| | `spec-dir` | `docs/prds/{area}` | Directory specs are written to | | `naming` | `PRD{n}-{topic}` | File name pattern; `{n}` = next free PRD number, `{topic}` = kebab-case slug | Default output: `docs/prds/{area}/PRD{n}-{topic}.md`. ### `planning-and-task-breakdown` | Key | Default | Meaning | |-----|---------|---------| | `plan-dir` | `docs/plans/{area}` | Directory plans are written to | | `naming` | `PLAN-{prd-name}-{phase}` | File name pattern; `{phase}` suffix only when a plan spans multiple files | | `todo` | `embedded` | `embedded` keeps the task list inside the plan; `separate` writes a standalone `todo.md` | Default output: `docs/plans/{area}/PLAN-{prd-name}-{phase}.md`, task list embedded. ### `browser-testing-with-devtools` This skill has **no default** — the section is required for browser testing, because dev-server commands and login flows cannot be guessed. | Key | Meaning | |-----|---------| | `dev-server` | Command to start the local dev server | | `ready-check` | How to confirm the server is up | | `base-url` | Root URL for navigation | | `auth-flow` | Steps to log in (multi-line `|` block) | | `roles` | Test account per privilege level, referenced by env-var name | | `notes` | Anything else the agent should know (certs, seed data, ...) | ### `git-workflow-and-versioning` | Key | Default | Meaning | |-----|---------|---------| | `branching` | `never` | `never` = agent works in the current branch and never creates or switches branches; `allow` = agent may create feature branches | ### `agent-hub` Read by the `.pi/harnesses/agent-hub/` pi harness on every session start. The canonical section name is `## agent-hub`; the harness also accepts the legacy `## agent-team` name (from before the standalone `agent-team` harness was retired), so existing project override files keep working unchanged. When both sections are present their keys merge, with later lines winning. The `rules:` and `docs:` keys are also read outside the harness: the `compound-learning` skill (and the `/compound` claude-code/opencode commands built on it) resolves them as the targets an end-of-session compound pass writes lessons to. | Key | Default | Meaning | |-----|---------|---------| | `language` | `English` | User-facing language the dispatcher uses for every `ask_user` question, every `context` field, and every summary. Specialist task strings always stay in English regardless. | | `persona-gate` | `off` | When `on`, blocks input at session start until an orchestrator persona is picked. | | `model.` | persona frontmatter `model:` | Replaces the named persona's default model for this project (a full pi model spec). | | `models.` | persona frontmatter `models:` | Replaces the named persona's model-candidate list for `/agent-model` and `/models` profiles (comma-separated pi model specs). | | `thinking.` | persona frontmatter `thinking:` | Replaces the named persona's pi `--thinking` reasoning level for this project: one of `off`, `minimal`, `low`, `medium`, `high`, `xhigh`. Switchable at runtime with `/agent-model-thinking `. An invalid value is ignored with a session-start warning. | | `subagents..` | persona frontmatter `subagents:` | Replaces or adds one delegate sub-role for this project: `[, tools=]`. Other declared roles keep their frontmatter values. | | `delegate-depth.` | persona frontmatter `delegate_depth:` (default/max 1) | Replaces the persona's delegation depth budget: `0` makes its delegate tool refuse (delegation off for this project), `1` lets it spawn terminal children. Values above 1 are clamped to 1; children at remaining depth 0 do not receive delegate tooling. | | `rules` | none | Comma-separated repo-relative folders holding the project's own rule files (HOW — implementation patterns the work must comply with). Resolution is **index-first**: when a folder has a top-level `README.md`/`index.md`, personas read it first and follow its loading manifest (session bundles, conditional-load lists) instead of bulk-reading the tree; a folder without an index is searched **recursively** through all subfolders. The harness tells every dispatched specialist where the rules live and how to resolve them; the planner and code-reviewer personas read the relevant rules, validate their subject against them, and pass them on (cited in plan acceptance criteria / handed to delegate sub-reviewers). Missing folders produce a session-start warning. | | `docs` | none | Comma-separated repo-relative documentation **entry points** (WHAT/WHY — architecture, standards, decisions): canonical files (e.g. `Docs/AGENTS.md`) or doc folders (personas start from a folder's `README.md`/index). Unlike `rules`, docs orient rather than bind: every dispatched specialist and research helper is told to read the entry points relevant to its task and follow their links instead of bulk-reading doc trees; the code-reviewer flags changes that alter documented behavior without a doc update; the documenter treats the entry points and the trees they link as the docs it maintains. Missing paths produce a session-start warning. | | `research-keep` | `4` | How many **finished** manual/persona research helpers the hub retains (LRU by finish time) so they stay resumable via `/agents-cont rN`; older ones are pruned along with their session files. `all` disables the cap. Auto-research pipe helpers (spawned for `NEEDS_RESEARCH:` pauses) are always pruned as soon as they finish — their findings persist as files under `.pi/agent-sessions/findings/`. Running helpers are never pruned. An invalid value is ignored with a session-start warning. | | `recon-search-timeout-s` | `120` | Parent-side deadline, in seconds, for each `read`, `grep`, `find`, or `ls` call made by a native research helper or nested delegate child. Accepts an integer `1`–`3600` or `off`; invalid input warns and falls back to `120`. This is **not** an agent/turn deadline: non-tool work can run indefinitely. On timeout the hub returns `tool_timeout` with call metadata, sends SIGTERM to its owned process group, escalates to SIGKILL after a finite grace period, and settles even if the child never closes or a descendant holds a pipe. If that bounded cleanup cannot confirm child/process-group death, the metadata reports `terminationConfirmed: false`; this prevents a parent hang but may indicate an uninterruptible OS-level process needing operator attention. | | `mode` | `standard` | Execution mode for the hub's per-turn budgets: `fast` (single-specialist path, no nested delegation, assertion ledger optional), `standard` (batched execution with moderate budgets), or `strict` (full Verification Contract with wide budgets). Budgets are per **user turn** — exhausted budgets make `dispatch_agent`/`spawn_research` refuse with "summarize and ask the user"; the next user message opens a fresh window. Switchable live with `/hub-mode`. | | `max-dispatches-per-turn` | mode default (2/8/24) | `dispatch_agent` calls allowed per user turn. Positive integer or `off` (unlimited). | | `max-research-per-turn` | mode default (1/4/12) | `spawn_research` calls allowed per user turn (the auto-research pipe and the `/research` command are exempt). Positive integer or `off`. | | `turn-wall-time-s` | mode default (900/3600/14400) | Wall-clock budget, in seconds, per user turn; once exceeded, further dispatch/research calls refuse. Positive integer or `off`. | | `agent-turn-timeout-s` | mode default (600/1800/off) | Whole-run deadline, in seconds, for each spawned specialist, research helper, and nested delegate child (unlike `recon-search-timeout-s`, this bounds the entire run). On expiry the run terminates as `turn_timeout` (exit 124) with partial output preserved. Positive integer or `off`. | | `session-recycle-runs` | mode default (3/5/5) | Recycle a specialist's accumulated session (fresh spawn instead of `-c` resume) after this many resumed runs. Context is also always recycled at ≥60% measured context (input + cacheRead + cacheWrite). Positive integer or `off` (context threshold still applies). | | `watchdog` | `auto` | Drift watchdog default for dispatched specialists: `auto`/`on` arm the in-flight rules (out-of-scope writes, tool-call loops, repeated failures, tool-call cap) with LLM-judge escalation; `off` disarms. Overridable live per hub (`/watchdog on\|off\|auto`), per agent (`/watchdog on\|off\|clear`), and per dispatch (the `watchdog` param). A DRIFTING/STUCK verdict terminates the run as `drift_stop` (exit 125) with partial output preserved. | | `watchdog-judge-model` | researcher persona's model | pi model spec for the one-shot drift judge (e.g. `openai-codex/gpt-5.3-codex-spark`). Falls back to the researcher persona's resolved model, then the dispatcher's. | Example — switch the dispatcher to Bulgarian, pin the builder to sonnet, raise the code-reviewer's thinking level, move the code-reviewer's docs sub-reviewer to a different model, and point the team at the project's rule folders and doc entry points: ```markdown ## agent-hub language: Bulgarian model.builder: github-copilot/claude-sonnet-4.6 models.builder: github-copilot/claude-sonnet-4.6, github-copilot/claude-haiku-4.5 thinking.code-reviewer: xhigh subagents.code-reviewer.docs: github-copilot/claude-sonnet-4.6, tools=read,grep delegate-depth.code-reviewer: 1 recon-search-timeout-s: 120 # or off to disable the per-tool watchdog rules: docs/rules, .ai/rules docs: Docs/AGENTS.md, Docs/architecture/ARCHITECTURE_OVERVIEW.md ``` ### `env` (optional, read by the doctor) The overrides file references environment variables by **name** — test-account credentials for `browser-testing-with-devtools`, the `pi-voice-stt` API key — while the values live in a gitignored root `.env`. The optional `## env` section declares which names the project's readers expect, so a fresh clone can find out what's missing *before* a skill fails mid-run: | Key | Meaning | |-----|---------| | `required` | Comma-separated env-var **names** (never values) the project's sections reference | ```markdown ## env required: APP_TEST_ADMIN_USER, APP_TEST_ADMIN_PASS, AZURE_SPEECH_KEY ``` No skill or harness loads this section. Its only reader is `agent-fleet doctor` (and `/doctor-agent-fleet`), which warns when a declared name is neither set in the environment nor declared in the workspace root `.env`. ### Per-peer env files (`env_file:` in peers.yaml) Fleet peers spawned by `just team-up` / `just hub-team` can carry their own environment: an `env_file:` entry in `.pi/agents/peers.yaml` names a **repo-relative** KEY=VALUE file (same format as `.env`, no shell evaluation) that herdr injects into the peer's pane before the command runs — no `source`, no leaking into sibling panes. The file must exist at spawn (team-up refuses otherwise, never mid-run), and its **values never appear in `--dry-run` output** — only the path. Keep these files gitignored exactly like the root `.env` this section describes. ## The setup file — `.ai/agent-fleet-setup.md` The `guided-workspace-setup` skill writes this file to record what it installed into the project — which skills, commands, personas, and extensions, by what method, and when. It reads the file on a re-run to add, update, or remove artifacts without reinstalling everything. No other skill loads it. | Section | Meaning | |---------|---------| | `workspace-summary` | Workspace path, coding agent, **agent-fleet version**, project shape, checks discovered | | `install-status` | Installed artifacts, their targets, and the method (`copy` or `symlink`) | | `doctor-runs` | One line per doctor pass (preflight / postflight, repaired / deleted / skipped counts) | | `verification` | Checks confirming the install | ### The `version:` line `workspace-summary` carries a `version:` line set to the package version that performed the install (e.g. `version: 1.4.2`). It drives the version-aware update flow — on every re-run, `guided-workspace-setup` compares this against the current package version and: 1. Reads `CHANGELOG.md` between the two versions. 2. For each installed artifact, runs a three-way diff between *source@recorded*, the installed copy on disk, and *source@current*. 3. Surfaces the result in the install menu using these `Status` values: | Status | Meaning | |---|---| | `installed · upgrade available` | Source changed upstream; user copy still matches the old source → clean refresh | | `installed · conflicting upgrade` | Source changed upstream AND user modified the copy → menu shows the three-way diff before overwriting | | `installed · removed upstream` | Artifact gone in the new version → menu proposes deletion (subject to the removal-scope rule) | | `not installed · new in this version` | New artifact added in the new version → menu offers it, marked `★` if recommended | The diff sources at *source@recorded* are read from the package's `.versions//` snapshot tree, which the release pipeline writes for every published version. If the snapshot is missing (e.g. an unpublished local build), the skill falls back to "treat installed copy as canonical" and prompts for an explicit baseline. ### Pre-versioning workspaces A workspace whose `agent-fleet-setup.md` predates the `version:` line is treated as "pre-versioning". On first re-run, `guided-workspace-setup` prompts for a clean baseline: either accept the installed artifacts as matching the current source (stamp the current version), or re-run the install from scratch. Commit this file if the team should share install state — keep paths relative so it stays portable. A self-referencing checkout (agent-fleet itself) may instead `.gitignore` it, since its recorded paths are local to one machine. ## The `/orchestrate` command and its team config `/orchestrate` turns the main session (Claude Code) or primary agent (OpenCode) into an **orchestrator** that drives a config-defined team of installed subagents — subagents cannot nest, so all dispatching is the main session's job. The default team is `planner` + `builder` (no reviewer); the orchestrator routes the team as a **runtime roster, not a fixed `researcher → planner → builder` pipeline** — it skips planning when a plan already exists, re-runs `researcher` at any point, and loops back to `planner` when the build surfaces a wrong plan. It honours the existing subagent handoff markers: `PLAN_FILE: ` from the planner, and `NEEDS_RESEARCH: ` from planner/builder (which dispatches a read-only `researcher` and resumes the paused persona with the findings inlined). The named teams live in a per-agent config that mirrors pi's `.pi/agents/teams.yaml` (a map of team-name → ordered persona list, first key = default; `researcher`/`deep-researcher` deliberately unlisted but always available). The reader differs by runtime: pi's harness parses its YAML, while `/orchestrate` has the **command's instructions** read the YAML via the Read tool (no harness runtime in claude-code/opencode): | | Config file | Command | |---|---|---| | claude-code | `.claude/orchestrate-teams.yaml` | `/orchestrate` | | opencode | `.opencode/orchestrate-teams.yaml` | `/af-orchestrate` | | pi | `.pi/agents/teams.yaml` | via the `agent-hub` harness (no `/orchestrate`) | Switch teams at runtime with `/orchestrate ""` (parallel of pi's `/agents-team`); use `/orchestrate team= ` to disambiguate when the task text starts with a word that collides with a team key. **Guided-setup behaviour.** `guided-workspace-setup` offers `/orchestrate` **`★`-recommended** for claude-code + opencode (hidden for pi via the existing source-availability filter — there is no `.pi/prompts/orchestrate.md`), and installs the agent's `orchestrate-teams.yaml` as a **companion** of the command (a user-edited config is preserved on uninstall, never silently clobbered). Two artifacts stay **claude-only**: the lifecycle **hooks** (they register into `.claude/settings.json`, which opencode/pi have no install path for) and the **`AskUserQuestion` questionnaire menu mode** (the primary menu interaction on claude-code). The orchestrate command and its team config are *not* claude-only — they ship for opencode too. ## Templates ### `.ai/agent-fleet-overrides.md` Copy this in and delete the sections you don't need — anything absent falls back to that reader's default. ```markdown # Agent Fleet — Project Overrides # # Each section is applied ON TOP of the skill's built-in defaults. # Keys not listed keep the default. Absent file/section → pure defaults. ## spec-driven-development spec-dir: docs/prds/{area} naming: PRD{n}-{topic} ## planning-and-task-breakdown plan-dir: docs/plans/{area} naming: PLAN-{prd-name}-{phase} todo: embedded ## browser-testing-with-devtools dev-server: ready-check: base-url: auth-flow: | 1. Navigate to 2. Submit credentials for the role needed by the screen under test roles: admin: env APP_TEST_ADMIN_USER / APP_TEST_ADMIN_PASS player: env APP_TEST_PLAYER_USER / APP_TEST_PLAYER_PASS notes: | ## git-workflow-and-versioning branching: never # Optional for pi agent-hub; omit this section to keep default English. # (`## agent-team` is still accepted as a legacy alias for this section.) ## agent-hub language: rules: [, ] docs: [, ] # Optional; names (never values) of env vars the sections above reference. # Only `agent-fleet doctor` reads this — it warns when one is unset. ## env required: [, ] ``` ### `.ai/agent-fleet-setup.md` Written and maintained by `guided-workspace-setup`; shown here for reference. ```markdown # Agent Fleet — Workspace Setup # # Maintained by the guided-workspace-setup skill. ## workspace-summary agent: claude-code method: copy version: 1.4.2 shape: ## install-status skills: [spec-driven-development, test-driven-development, code-review-and-quality] commands: [spec, plan, build] personas: [code-reviewer] extensions: [] harnesses: [] harness-support: [] # justfile (managed region) + scripts/agents companions; tracked when any harness is installed updated: 2026-05-22 ## verification - Every recorded artifact exists at its target path. - No secrets are stored in this file. ``` ### `.ai/stt.json` Optional. Present only when the `pi-voice-stt` extension is installed and configured. Read by the extension on every pi session start, ahead of the global `~/.pi/agent/stt.json`. Holds the non-secret provider config; the API key (and Azure endpoint) live in a gitignored root `.env` as named env vars, which the `justfile`'s `dotenv-load` exposes to the session. ```json { "language": "bg-BG", "provider": { "type": "azure", "apiKeyEnv": "AZURE_SPEECH_KEY", "locales": ["bg-BG", "en-US"] } } ``` Corresponding gitignored `.env` at the repo root: ```sh AZURE_SPEECH_ENDPOINT=https://.cognitiveservices.azure.com AZURE_SPEECH_KEY= ``` For the OpenAI-compatible backend, `provider.type` is `openai` with `baseUrl` / `model` and an `OPENAI_API_KEY` env var instead. Full schema: [.pi/extensions/pi-voice-stt/README.md](../.pi/extensions/pi-voice-stt/README.md).