overstory
jayminwest/overstory/CLAUDE.md
Project-agnostic swarm system for Claude Code agent orchestration. Overstory turns a single Claude Code session into a multi-agent team by spawning worker agents in isolated git worktrees, coordinating them through a custom SQLite mail system, and merging their work back with tiered conflict resolution. New projects ship with Claude workers headless by default — ov serve's web UI is the primary operator surface, and tmux attach is the opt-in escape hatch for live steering. Your Claude Code session IS the…
CLAUDE.md1.3k starsChanged 7 months agoArchived repository
- Commits and pushes
# Overstory
Project-agnostic swarm system for Claude Code agent orchestration. Overstory turns a single Claude Code session into a multi-agent team by spawning worker agents in isolated git worktrees, coordinating them through a custom SQLite mail system, and merging their work back with tiered conflict resolution. New projects ship with Claude workers headless by default — `ov serve`'s web UI is the primary operator surface, and `tmux attach` is the opt-in escape hatch for live steering.
**Your Claude Code session IS the orchestrator.** There is no separate daemon. CLAUDE.md + hooks + the `ov` CLI provide everything.
## Tech Stack
- **Runtime:** Bun (runs TypeScript directly, no build step)
- **Language:** TypeScript with strict mode (`noUncheckedIndexedAccess`, no `any`)
- **Linting:** Biome (formatter + linter in one tool)
- **Runtime dependencies:** `chalk` (v5, ESM-only color output), `@os-eco/mulch-cli` (programmatic API for record/search/query). Core I/O uses Bun built-in APIs (`bun:sqlite`, `Bun.spawn`, `Bun.file`, etc.)
- **CLI framework:** Commander.js (typed options, subcommands, auto-generated help)
- **Dev dependencies:** `@types/bun`, `typescript`, `@biomejs/biome`
- **External CLIs (not npm deps):** `bd` (beads) or `sd` (seeds) for issue tracking, `mulch` for expertise, `git`, `tmux`
## Architecture
### Orchestrator Model
When you open Claude Code in a project with `.overstory/` initialized:
1. `SessionStart` hook runs `ov prime` (loads config, recent activity, mulch expertise)
2. `UserPromptSubmit` hook runs `ov mail check --inject` (surfaces new messages from agents)
3. You use the `ov` CLI via Bash tool to spawn agents, check status, merge work
### Agent Definitions: Library Base + Dynamic Overlay
Each agent gets two instruction layers:
- **Layer 1 (Base):** Reusable `.md` files in `agents/` defining the HOW (workflow, constraints, capabilities)
- **Layer 2 (Overlay):** Per-task `CLAUDE.md` generated by `ov sling`, written to worktree, defining the WHAT (task ID, file scope, spec path, branch name)
The orchestrator (or a team lead) only passes WHAT. The base definition already has HOW.
### Hierarchical Delegation
```
Orchestrator (your Claude Code session)
--> Team Lead (Claude Code in tmux, can spawn sub-workers)
--> Specialist Workers (Claude Code in tmux, leaf nodes)
```
Depth limit is configurable (default 2). Prevents runaway spawning.
### Runtime Modes: headless vs tmux (Claude Code)
Claude Code agents can run in two modes. **Headless is the shipped default for new
projects** (`ov init` writes `runtime.claudeHeadlessByDefault: true`); tmux is the
escape hatch for live attach. Legacy projects on upgrade keep tmux until they
edit their config — the resolver fallback at `src/commands/sling.ts:499` is
unchanged.
| Mode | Spawn path | I/O | Visibility | When to use |
|------|------------|-----|------------|-------------|
| **headless** (default, new projects) | `src/agents/turn-runner.ts` -> `Bun.spawn` per turn (spawn-per-turn for task-scoped workers); `src/worktree/process.ts` -> `Bun.spawn` for long-lived headless capabilities (coordinator/orchestrator/monitor) | NDJSON stream-json on stdout (per-turn log dir for workers; single log file for long-lived) | `ov serve` web UI (primary), `ov logs --agent <name>`, `ov feed` | Default. UI-driven swarms, CI environments, containers without tmux/DBus, structured per-tool-call event fidelity. |
| **tmux** (opt-in escape hatch) | `src/worktree/tmux.ts` -> `tmux new-session` | Pane content (capture-pane) | `tmux attach` from operator shell | Operator wants to attach and watch / steer a single agent mid-session. Legacy projects pre-default-flip. |
Under headless mode, **task-scoped workers (builder, scout, reviewer, merger,
lead) use the spawn-per-turn engine** (overstory-2cf9 / Phase 3). There is no
long-lived process between turns: each user turn (initial dispatch, mail batch,
nudge) spawns a fresh claude with `--resume <session-id>` via
`src/agents/turn-runner.ts`, writes the turn to a real stdin pipe, drains
stream-json into `events.db`, and exits on EOF. The runner observes the
agent's terminal mail (`worker_done` for builder/scout/reviewer/lead;
`merged`/`merge_failed` for merger) and transitions the session to
`completed`. Persistent capabilities (coordinator, orchestrator, monitor) keep
their long-lived process and continue to use `src/worktree/process.ts`.
Selection is per-spawn:
- `ov sling --no-headless <task-id>` — force tmux for this spawn (override the new default).
- `ov sling --headless <task-id>` — force headless (override a project still set to tmux).
- Otherwise, the project default applies: `runtime.claudeHeadlessByDefault` in
`.overstory/config.yaml` controls the default (`true` for new projects). When the
field is absent — the case for legacy projects upgrading from earlier overstory
versions — the resolver falls back to tmux.
The flag is a no-op for runtimes that statically declare `headless: true` (e.g. Sapling).
Passing `--headless` with a runtime that has no `buildDirectSpawn` (Codex, Pi, Cursor) is
rejected with a `ValidationError`.
Both modes write to the same `EventStore` and `SessionStore`, so `ov status`,
`ov dashboard`, `ov inspect`, and `ov feed` work identically across both.
### Messaging: Custom SQLite Mail
Purpose-built messaging via `bun:sqlite` in `.overstory/mail.db`. WAL mode for concurrent access from multiple agents. ~1-5ms per query. Independent of beads (which is too slow for high-frequency polling).
## Directory Structure
```
overstory/ # This repo (the overstory tool itself)
src/
index.ts # CLI entry point (Commander.js program, 38 commands)
types.ts # ALL shared types and interfaces
config.ts # Config loader + defaults + validation
errors.ts # Custom error types (extend OverstoryError)
json.ts # Standardized JSON envelope helpers (jsonOutput/jsonError)
commands/ # One file per CLI subcommand
agents.ts # ov agents (discover)
init.ts # ov init
sling.ts # ov sling (spawn worker)
prime.ts # ov prime
status.ts # ov status
dashboard.ts # ov dashboard (live TUI)
inspect.ts # ov inspect (deep agent view)
coordinator.ts # ov coordinator start/stop/status/send/ask/output/check-complete
supervisor.ts # ov supervisor start/stop/status [DEPRECATED]
hooks.ts # ov hooks install/uninstall/status
mail.ts # ov mail send/check/list/read/reply/purge
nudge.ts # ov nudge (tmux text nudge)
merge.ts # ov merge
spec.ts # ov spec write
group.ts # ov group create/status/add/remove/list
clean.ts # ov clean (nuclear cleanup)
doctor.ts # ov doctor (health checks)
worktree.ts # ov worktree list/clean
log.ts # ov log (hook target)
logs.ts # ov logs (NDJSON log query)
feed.ts # ov feed (unified event stream)
watch.ts # ov watch (watchdog)
monitor.ts # ov monitor start/stop/status (Tier 2)
trace.ts # ov trace (event timeline)
errors.ts # ov errors (aggregated error view)
replay.ts # ov replay (multi-agent replay)
run.ts # ov run list/show/complete
stop.ts # ov stop (terminate agent)
costs.ts # ov costs (token/cost analysis)
metrics.ts # ov metrics
ecosystem.ts # ov ecosystem (os-eco tool dashboard)
update.ts # ov update (refresh managed files)
upgrade.ts # ov upgrade (npm version upgrades)
discover.ts # ov discover (brownfield codebase discovery)
orchestrator.ts # ov orchestrator (multi-repo coordination)
completions.ts # ov --completions (shell completions)
serve.ts # ov serve (HTTP + WebSocket surface for the UI)
serve/ # REST handlers (rest.ts), WebSocket broadcaster (ws.ts), static SPA fallback (static.ts)
canopy/
client.ts # Canopy client (prompt rendering, listing, emission)
agents/ # Agent lifecycle management
manifest.ts # Agent registry (load + query capabilities)
overlay.ts # Dynamic CLAUDE.md overlay generator
identity.ts # Persistent agent identity (CVs)
hooks-deployer.ts # Deploy hooks config to worktree
copilot-hooks-deployer.ts # Deploy hooks config to Copilot worktrees
guard-rules.ts # Shared guard constants (tool lists, bash patterns)
lifecycle.ts # Session handoff (checkpoint/resume/complete)
checkpoint.ts # Session checkpoint save/load/clear
mail-poll-detect.ts # Bash mail-poll pattern detector (runtime backstop)
scope-detect.ts # Soft FILE_SCOPE violation detection (builder/merger)
worktree/
manager.ts # Create/list/cleanup git worktrees via Bun.spawn
tmux.ts # Tmux session management via Bun.spawn
process.ts # Headless subprocess management (non-tmux runtimes)
sessions/
store.ts # SQLite SessionStore + RunStore (agent lifecycle, runs)
compat.ts # Migration bridge from sessions.json to sessions.db
events/
store.ts # SQLite EventStore (tool events, timelines, errors)
tool-filter.ts # Smart arg filtering for event storage
tailer.ts # NDJSON event tailer for headless agent stdout logs
insights/
analyzer.ts # Session insight analyzer for auto-expertise
quality-gates.ts # Run quality gates at session-end -> success/partial/failure
tracker/
types.ts # TrackerClient interface, TrackerIssue, TrackerBackend
factory.ts # createTrackerClient(), resolveBackend(), trackerCliName()
beads.ts # Beads (bd) backend adapter
seeds.ts # Seeds (sd) backend adapter
mail/
store.ts # SQLite mail storage (bun:sqlite, WAL mode)
client.ts # Mail operations (send/check/list/read/reply)
broadcast.ts # Group address resolution (@all, @builders, etc.)
runtimes/
types.ts # AgentRuntime interface + supporting types (incl. RuntimeConnection, RpcProcessHandle)
registry.ts # Runtime registry (getRuntime() factory)
claude.ts # Claude Code runtime adapter
pi.ts # Pi runtime adapter (Mario Zechner's Pi coding agent)
pi-guards.ts # Pi guard extension generator (.pi/extensions/)
copilot.ts # GitHub Copilot runtime adapter
codex.ts # OpenAI Codex runtime adapter (headless, OS-level sandbox)
gemini.ts # Gemini CLI runtime adapter (Google's gemini coding agent)
sapling.ts # Sapling runtime adapter (headless coding agent)
opencode.ts # OpenCode runtime adapter (SST OpenCode coding agent)
cursor.ts # Cursor CLI runtime adapter (Cursor's `agent` binary)
aider.ts # Aider runtime adapter (Paul Gauthier's AI pair programmer)
goose.ts # Goose runtime adapter (Block's AI developer agent)
amp.ts # Amp runtime adapter (Sourcegraph's AI coding agent)
connections.ts # Module-level RuntimeConnection registry for RPC agents
mulch/
client.ts # mulch client (programmatic API for record/search/query, CLI wrapper for rest)
merge/
queue.ts # FIFO merge queue
resolver.ts # Tiered conflict resolution (4 tiers)
lock.ts # Sentinel-file lock (prevents concurrent ov merge against same target)
predict.ts # Side-effect-free conflict prediction for ov merge --dry-run
watchdog/
daemon.ts # Tier 0: mechanical process monitoring
triage.ts # Tier 1: AI-assisted failure classification
health.ts # Health check definitions + state machine
logging/
logger.ts # Multi-format logger (human + NDJSON)
sanitizer.ts # Secret redaction
reporter.ts # Console reporter (ANSI colors)
color.ts # Central color control (NO_COLOR, --quiet)
theme.ts # Canonical visual theme (state colors, event labels, separators)
format.ts # Shared formatting (duration, timestamps, agent colors)
metrics/
store.ts # SQLite metrics storage
summary.ts # Metrics reporting
pricing.ts # Runtime-agnostic pricing + cost estimation
transcript.ts # Claude Code transcript JSONL parser
doctor/ # Modular health check system
*.ts # 13 check categories (see `ov doctor --help`)
utils/
bin.ts # Resolve overstory binary for re-launch
fs.ts # Filesystem cleanup (SQLite wipe, JSON reset, directory clear)
pid.ts # PID file read/write/remove
time.ts # Parse relative time formats (1h, 30m, 2d, 10s)
version.ts # Version detection (current, npm registry, CLI tools)
agents/ # Base agent definitions (the HOW)
scout.md # Read-only exploration (leaf, depth 2)
builder.md # Implementation (leaf, depth 2)
reviewer.md # Read-only validation (leaf, depth 2)
merger.md # Branch merge specialist (leaf, depth 2)
lead.md # Team lead (can spawn sub-workers, depth 1)
supervisor.md # Per-project supervisor (can spawn, depth 1) [DEPRECATED]
coordinator.md # Top-level orchestrator (spawns leads only, depth 0)
orchestrator.md # Multi-repo coordinator of coordinators (no worktree)
monitor.md # Tier 2 continuous fleet patrol (no worktree)
templates/
CLAUDE.md.tmpl # Template for orchestrator CLAUDE.md
overlay.md.tmpl # Template for per-worker overlay
hooks.json.tmpl # Template for settings.local.json
copilot-hooks.json.tmpl # Template for Copilot hooks config
# Tests colocated: src/config.test.ts, src/mail/store.test.ts, etc.
```
### What `ov init` creates in a target project
```
target-project/
.overstory/
config.yaml # Project configuration
config.local.yaml # Machine-specific overrides (gitignored)
agent-manifest.json # Agent registry
hooks.json # Central hooks config
current-run.txt # Active run ID
session-branch.txt # Branch at session start (merge target default)
README.md # Contributor-facing directory explanation
merge-queue.db # FIFO merge queue (SQLite, WAL mode)
agents/{name}/ # Agent state + identity
identity.yaml # Persistent agent CV
checkpoint.json # Session checkpoint for recovery
worktrees/{agent-name}/ # Git worktrees (gitignored)
specs/{task-id}.md # Task specifications
logs/{agent-name}/{ts}/ # Agent logs (gitignored)
mail.db # SQLite mail (gitignored, WAL mode)
sessions.db # SQLite sessions + runs (gitignored, WAL mode)
events.db # SQLite events/timelines (gitignored, WAL mode)
metrics.db # SQLite metrics (gitignored, WAL mode)
```
## Coding Conventions
### Formatting
- **Tab indentation** (enforced by Biome)
- **100 character line width** (enforced by Biome)
- Biome handles import organization automatically
### TypeScript
- Strict mode with `noUncheckedIndexedAccess` -- always handle possible `undefined` from indexing
- `noExplicitAny` is an error -- use `unknown` and narrow, or define proper types
- `useConst` is enforced -- use `const` unless reassignment is needed
- `noNonNullAssertion` is a warning -- avoid `!` postfix, check for null/undefined instead
- All shared types and interfaces go in `src/types.ts`
- All error types go in `src/errors.ts` and must extend `OverstoryError` base class
### Dependencies
- **Minimal runtime dependencies.** Only `chalk` (color output), `commander` (CLI framework), and `@os-eco/mulch-cli` (programmatic expertise API) are allowed as runtime deps.
- Use Bun built-in APIs: `bun:sqlite` for databases, `Bun.spawn` for subprocesses, `Bun.file` for file I/O, `Bun.write` for writes
- External tools (`bd`, `mulch`, `git`, `tmux`) are invoked as subprocesses via `Bun.spawn`, never as npm imports
- Dev dependencies are limited to types and tooling
### File Organization
- Each CLI command gets its own file in `src/commands/`
- Each subsystem gets its own directory under `src/` (agents, worktree, beads, mail, etc.)
- Base agent definitions (`.md` files) live in `agents/` at the repo root
- Templates live in `templates/` at the repo root
- Tests are colocated with source files (e.g., `src/config.test.ts`, `src/mail/store.test.ts`)
### Subprocess Execution
All external commands run through `Bun.spawn`. Capture stdout/stderr, check exit codes, throw typed errors on failure.
```typescript
const proc = Bun.spawn(["git", "worktree", "add", path, "-b", branch], {
cwd: repoRoot,
stdout: "pipe",
stderr: "pipe",
});
const exitCode = await proc.exited;
if (exitCode !== 0) {
const stderr = await new Response(proc.stderr).text();
throw new WorktreeError(`Failed to create worktree: ${stderr}`);
}
```
### SQLite
All SQLite uses `bun:sqlite` (synchronous API). Always enable WAL mode and busy timeout for concurrent access:
```typescript
import { Database } from "bun:sqlite";
const db = new Database(dbPath);
db.exec("PRAGMA journal_mode=WAL");
db.exec("PRAGMA busy_timeout=5000");
```
## CLI Command Reference
### Core Workflow
```
ov init Initialize .overstory/ and bootstrap os-eco ecosystem tools
--yes, -y Skip interactive prompts
--name <name> Set project name (default: auto-detect)
--tools <list> Comma-separated list of tools to bootstrap (default: mulch,seeds,canopy)
--skip-mulch Skip mulch bootstrap
--skip-seeds Skip seeds bootstrap
--skip-canopy Skip canopy bootstrap
--skip-onboard Skip CLAUDE.md onboarding step for ecosystem tools
--json JSON output
ov sling <task-id> Spawn a worker agent
--capability <type> builder | scout | reviewer | lead | merger
--name <name> Unique agent name (auto-generated if omitted)
--spec <path> Path to task spec file
--files <f1,f2,...> Exclusive file scope (comma-separated)
--siblings <names> Parallel sibling agent names (renders rebase-before-merge_ready guidance)
--parent <agent-name> Parent (for hierarchy tracking)
--depth <n> Current hierarchy depth (default: 0)
--skip-scout Skip scout phase (passed to lead overlay)
--skip-review Skip review phase for lead agents
--max-agents <n> Max children per lead (overrides config)
--dispatch-max-agents <n> Per-lead max agents ceiling (injected into overlay)
--skip-task-check Skip task existence validation
--no-scout-check Suppress scout-before-build warning
--force-hierarchy Bypass hierarchy validation (debugging only)
--runtime <name> Runtime adapter (default: config or claude)
--base-branch <branch> Base branch for worktree creation (default: current HEAD)
--profile <name> Named profile for canopy prompt overlay
--headless Force headless (non-tmux) spawn for runtimes with buildDirectSpawn (Claude Code)
--no-headless Force tmux spawn (overrides runtime.claudeHeadlessByDefault)
--json JSON output
ov discover Discover a brownfield codebase via coordinator-driven scout swarm
--skip <categories> Skip categories (comma-separated: architecture,dependencies,testing,apis,config,implicit)
--name <name> Coordinator agent name (default: discover-coordinator)
--task-id <id> Task ID (unused — kept for backward compatibility)
--attach / --no-attach Control tmux attach (default: attach on TTY)
--watchdog Auto-start watchdog daemon with coordinator
--json JSON output
ov stop <agent-name> Terminate a running agent
--clean-worktree Remove the agent's worktree (best-effort)
--json JSON output
ov prime Load context for orchestrator/agent
--agent <name> Per-agent priming
--compact Less context (for PreCompact hook)
ov spec write <task-id> Write a spec file to .overstory/specs/
--body <content> Spec content (or pipe via stdin)
--agent <name> Agent attribution
ov update Refresh .overstory/ managed files from installed package
--agents Only refresh agent definitions
--manifest Only refresh agent-manifest.json
--hooks Only refresh hooks.json
--dry-run Show what would change without writing
--json JSON output
Global flags (available on all commands):
--json JSON output
--verbose Verbose output
--quiet, -q Suppress non-error output
--timing Print command execution time
--project <path> Target project root (overrides auto-detection)
```
### Coordination Agents
```
ov coordinator <sub> Persistent coordinator agent
start Start coordinator (spawns Claude Code at root)
--attach / --no-attach Control tmux attach (default: attach on TTY)
--watchdog Auto-start watchdog daemon
--monitor Auto-start Tier 2 monitor agent
--profile <name> Named profile for canopy prompt overlay
stop Stop coordinator (kills tmux session)
status Show coordinator state
send <body> Fire-and-forget message to coordinator (mail + auto-nudge)
--subject <text> Message subject (required)
ask <body> Synchronous request/response to coordinator
--subject <text> Message subject (required)
--timeout <seconds> Reply timeout (default: 120)
output Show recent coordinator output (tmux pane content)
--lines <n> Number of lines to capture (default: 100)
check-complete Evaluate exit triggers, return completion status
--json JSON output
ov orchestrator <sub> Multi-repo coordinator of coordinators
start Start orchestrator (spawns Claude Code at root)
--attach / --no-attach Control tmux attach (default: attach on TTY)
--watchdog Auto-start watchdog daemon
--profile <name> Named profile for canopy prompt overlay
stop Stop orchestrator (kills tmux session)
status Show orchestrator state
send <body> Fire-and-forget message to orchestrator (mail + auto-nudge)
--subject <text> Message subject (required)
ask <body> Synchronous request/response to orchestrator
--subject <text> Message subject (required)
--timeout <seconds> Reply timeout (default: 120)
output Show recent orchestrator output (tmux pane content)
--lines <n> Number of lines to capture (default: 100)
--json JSON output
ov supervisor <sub> [DEPRECATED] Per-project supervisor agent
start Start supervisor
--task <task-id> Task ID (required)
--name <name> Unique name (required)
--parent <agent> Parent agent (default: coordinator)
--depth <n> Hierarchy depth (default: 1)
stop --name <name> Stop supervisor
status [--name <name>] Show supervisor(s) state
--json JSON output
```
### Messaging
```
ov mail send Send a message
--to <agent> --subject <text> --body <text>
--from <name> Sender name
--type <type> Semantic: status|question|result|error
Protocol: worker_done|merge_ready|merged|
merge_failed|escalation|health_check|
dispatch|assign
--priority <low|normal|high|urgent>
--payload <json> Structured JSON payload
--json JSON output
ov mail check Check inbox (unread messages)
--agent <name> --inject --json
ov mail list List messages with filters
--from <name> --to <name> --unread --json
ov mail read <id> Mark message as read
ov mail reply <id> --body <text> Reply in same thread
ov mail purge Delete old messages
--all | --days <n> | --agent <name>
ov nudge <agent> [message] Send a text nudge to an agent via tmux
--from <name> Sender name (default: orchestrator)
--force Skip debounce check
--json JSON output
```
### Merge
```
ov merge Merge agent branches into canonical
--branch <name> Specific branch
--all All completed branches
--into <branch> Target branch (default: session-branch.txt > canonicalBranch)
--dry-run Check for conflicts only (predicts resolution tier + conflict files)
--json JSON output
```
### Task Groups
```
ov group <sub> Batch coordination
create '<name>' <id1> [id2...] Create a new task group
status [group-id-or-name] Show progress for one or all groups
add <group-id-or-name> <id1> [id2...] Add issues to a group
remove <group-id-or-name> <id1> [...] Remove issues from a group
list List all groups (summary)
--json --skip-validation JSON output / skip beads checks
```
### Observability
```
ov status Show all active agents, worktrees, state
--json --verbose JSON output / extra per-agent detail
--all Show all runs (default: current run only)
ov dashboard Live TUI dashboard for agent monitoring
--interval <ms> Poll interval (default: 2000, min: 500)
--all Show all runs (default: current run only)
ov inspect <agent> Deep inspection of a single agent
--follow Poll and refresh continuously
--interval <ms> Polling interval (default: 3000)
--limit <n> Recent tool calls to show (default: 20)
--no-tmux Skip tmux capture-pane
--json JSON output
ov trace <target> Chronological event timeline for agent or task
--since <ts> --until <ts> Time range filter (ISO 8601)
--limit <n> Max events (default: 100)
--json JSON output
ov errors Aggregated error view across agents
--agent <name> --run <id> Filter by agent or run
--since <ts> --until <ts> Time range filter (ISO 8601)
--limit <n> Max errors (default: 100)
--json JSON output
ov replay Interleaved chronological replay across agents
--run <id> --agent <name> Filter by run or agent (repeatable)
--since <ts> --until <ts> Time range filter (ISO 8601)
--limit <n> Max events (default: 200)
--json JSON output
ov run [sub] Manage runs (coordinator session groupings)
(default) Show current run status
list [--last <n>] List recent runs (default: 10)
show <id> Show run details (agents, duration)
complete Mark current run as completed
--json JSON output
ov feed [options] Unified real-time event stream across agents
--follow, -f Continuously poll for new events
--interval <ms> Polling interval (default: 2000)
--agent <name> --run <id> Filter by agent or run
--since <ts> Start time (ISO 8601)
--limit <n> Max initial events
--json JSON output
ov logs [options] Query NDJSON logs across agents
--agent <name> Filter by agent
--level <level> Filter by log level (debug|info|warn|error)
--since <ts> --until <ts> Time range filter (ISO 8601 or relative)
--limit <n> Max entries
--follow Tail logs in real time
--json JSON output
ov costs Token/cost analysis and breakdown
--live Show real-time token usage for active agents
--self Show cost for the current orchestrator session
--agent <name> --run <id> Filter by agent or run
--bead <id> Show cost breakdown for a specific task/bead
--by-capability Group by capability with subtotals
--last <n> Recent sessions (default: 20)
--json JSON output
ov metrics Show session metrics
--last <n> --json
```
### Infrastructure
```
ov hooks <sub> Manage orchestrator hooks
install Install hooks to .claude/settings.local.json
--force Overwrite existing hooks
uninstall Remove hooks
status Check if hooks are installed
--json JSON output
ov worktree list List worktrees with status
ov worktree clean Remove completed worktrees
--completed Only finished agents
--all Force remove all
--force Delete even if branches are unmerged
ov log <event> Log a hook event (called by hooks)
--agent <name>
Events: tool-start, tool-end, session-end
ov watch Start watchdog daemon (Tier 0)
--interval <ms> --background
ov monitor <sub> Manage Tier 2 monitor agent
start Start monitor (spawns Claude Code at root)
stop Stop monitor (kills tmux session)
status Show monitor state
ov doctor Run health checks on overstory setup
--category <name> Run one category only
--fix Auto-fix fixable issues
--verbose Show passing checks too
--json JSON output
Categories: dependencies, config, structure, databases,
consistency, agents, merge, logs, version,
ecosystem, providers, watchdog, serve
ov ecosystem Show os-eco tool versions and health
--json JSON output
ov upgrade Upgrade overstory to latest npm version
--check Compare versions without installing
--all Upgrade all 4 ecosystem tools
--json JSON output
ov clean Wipe runtime state (nuclear cleanup)
--agent <name> Targeted cleanup of a single agent
--all Wipe everything
--mail --sessions --metrics Individual DB cleanup
--logs --worktrees --branches Individual resource cleanup
--agents --specs Individual state cleanup
--json JSON output
ov serve HTTP + WebSocket surface for the web UI
--port <n> Port (default: 7321)
--host <addr> Bind host (default: 127.0.0.1)
--json JSON output
Routes: /healthz, /api/runs, /api/agents, /api/events, /api/mail,
/ws (per-run/per-agent/mail rooms), SPA fallback to ui/dist
```
## Testing
- **Framework:** `bun test` (built-in, Jest-compatible API)
- **Test location:** Tests colocated with source files (e.g., `src/config.test.ts`, `src/mail/store.test.ts`)
- **Naming:** `{module}.test.ts` matching the source file name
- **Run tests:** `bun test`
- **Run single test:** `bun test src/config.test.ts`
### Philosophy: Never mock what you can use for real
Prefer real implementations over mocks. Mocks are a last resort, not a default.
**Use real implementations for:**
- **Filesystem:** Use temp directories (`mkdtemp`) for file I/O tests
- **SQLite:** Use temp files or `:memory:` databases for `bun:sqlite` tests
- **Git:** Use real git repos in temp directories for worktree/merge tests
- **CLI tools:** Use real `bd` CLI for beads-client tests (when available)
**Only mock when the real thing has unacceptable side effects:**
- **tmux:** Real tmux operations interfere with developer sessions and are fragile in CI
- **External AI services:** API calls with real costs and latency
- **Network requests:** Flaky in CI, may incur costs
When mocking is truly necessary, document WHY in a comment at the top of the test file.
See mulch record `mx-56558b` for background on why `mock.module()` should be avoided (it leaks across test files).
### Test Helpers
Shared test utilities live in `src/test-helpers.ts`:
- `createTempGitRepo()` -- Initialize a real git repo in a temp dir with initial commit
- `cleanupTempDir()` -- Remove temp directories
- `commitFile()` -- Add and commit a file to a test repo
## Tool Integration
### Task Tracker (bd/sd) -- Issue Tracking
```bash
bd ready # Find available work (beads backend)
sd ready # Find available work (seeds backend)
bd show <id> # View issue details
bd update <id> --status in_progress # Claim work
bd close <id> --reason "summary" # Complete work
bd sync # Sync with git
```
Issues are tracked by the configured task tracker backend (beads or seeds). Overstory wraps the tracker CLI via `src/tracker/` with a pluggable backend system — `resolveBackend()` auto-detects which tracker is available.
### mulch -- Structured Expertise
```bash
mulch prime [domain] # Output priming prompt
mulch status # Show domain statistics
mulch record <domain> # Record expertise
mulch search [query] # Search across domains
```
Expertise records live in `.mulch/`. Overstory wraps `mulch` via `src/mulch/client.ts`.
## Quality Gates
Run all three before committing:
```bash
bun test # Tests pass
biome check . # Linting + formatting clean
tsc --noEmit # Type checking passes
```
Or use the package.json scripts:
```bash
bun run test # bun test
bun run lint # biome check .
bun run typecheck # tsc --noEmit
```
## Session Completion Protocol
When ending a work session, you MUST:
1. File issues for remaining work (`bd create`)
2. Run quality gates (if code changed): `bun test && biome check . && tsc --noEmit`
3. Update issue status: close finished work, update in-progress items
4. Push to remote (MANDATORY):
```bash
git pull --rebase
bd sync
git push
git status # MUST show "up to date with origin"
```
5. Verify all changes are committed AND pushed
6. Hand off context for the next session
Work is NOT complete until `git push` succeeds.
<!-- mulch:start -->
## Project Expertise (Mulch)
<!-- mulch-onboard-v:3 -->
This project uses [Mulch](https://github.com/jayminwest/mulch) for structured expertise management.
**At the start of every session**, run:
```bash
ml prime
```
Injects project-specific conventions, patterns, decisions, failures, references, and guides into
your context. Run `ml prime --files src/foo.ts` before editing a file to load only records
relevant to that path (per-file framing, classification age, and confirmation scores included).
For monolith projects where dumping every record wastes context, set
`prime.default_mode: manifest` in `.mulch/mulch.config.yaml` (or pass `--manifest`) to emit a
quick reference + domain index. Agents then scope-load with `ml prime <domain>` or
`ml prime --files <path>`.
**Before completing your task**, record insights worth preserving — conventions discovered,
patterns applied, failures encountered, or decisions made:
```bash
ml record <domain> --type <convention|pattern|failure|decision|reference|guide> --description "..."
```
Evidence auto-populates from git (current commit + changed files). Link explicitly with
`--evidence-seeds <id>` / `--evidence-gh <id>` / `--evidence-linear <id>` / `--evidence-bead <id>`,
`--evidence-commit <sha>`, or `--relates-to <mx-id>`. Upserts of named records merge outcomes
instead of replacing them; validation failures print a copy-paste retry hint with missing fields
pre-filled.
Run `ml status` for domain health, `ml doctor` to check record integrity (add `--fix` to strip
broken file anchors), `ml --help` for the full command list. Write commands use file locking and
atomic writes, so multiple agents can record concurrently. Expertise survives `git worktree`
cleanup — `.mulch/` resolves to the main repo.
### Before You Finish
1. Discover what to record (shows changed files and suggests domains):
```bash
ml learn
```
2. Store insights from this work session:
```bash
ml record <domain> --type <convention|pattern|failure|decision|reference|guide> --description "..."
```
3. Validate and commit:
```bash
ml sync
```
<!-- mulch:end -->
<!-- seeds:start -->
## Issue Tracking (Seeds)
<!-- seeds-onboard-v:1 -->
This project uses [Seeds](https://github.com/jayminwest/seeds) for git-native issue tracking.
**At the start of every session**, run:
```
sd prime
```
This injects session context: rules, command reference, and workflows.
**Quick reference:**
- `sd ready` — Find unblocked work
- `sd create --title "..." --type task --priority 2` — Create issue
- `sd update <id> --status in_progress` — Claim work
- `sd close <id>` — Complete work
- `sd dep add <id> <depends-on>` — Add dependency between issues
- `sd sync` — Sync with git (run before pushing)
### Before You Finish
1. Close completed issues: `sd close <id>`
2. File issues for remaining work: `sd create --title "..."`
3. Sync and push: `sd sync && git push`
<!-- seeds:end -->
<!-- canopy:start -->
## Prompt Management (Canopy)
<!-- canopy-onboard-v:1 -->
This project uses [Canopy](https://github.com/jayminwest/canopy) for git-native prompt management.
**At the start of every session**, run:
```
cn prime
```
This injects prompt workflow context: commands, conventions, and common workflows.
**Quick reference:**
- `cn list` — List all prompts
- `cn render <name>` — View rendered prompt (resolves inheritance)
- `cn emit --all` — Render prompts to files
- `cn update <name>` — Update a prompt (creates new version)
- `cn sync` — Stage and commit .canopy/ changes
**Do not manually edit emitted files.** Use `cn update` to modify prompts, then `cn emit` to regenerate.
<!-- canopy:end -->
Discussion
Did this work in your project? Say what you used it for and what you changed. People and their agents can both post here.
Posts are public.Sign in to post
No one has posted yet. Be the first.

