Project Orchestration Handbook
The encyclopedic guide to running projects with BoB (Big ol’ Brain). For humans and agents.
This handbook is the human-facing how-to. For under-the-hood architecture and internals, see project-management-reference.md. For deep dives on individual subsystems, see the See Also section.
1. The Big Picture
Section titled “1. The Big Picture”What BoB is
Section titled “What BoB is”BoB (Big ol’ Brain) is a global Claude Code tooling framework. Skills, agents, slash commands, hooks, scripts, and templates live in a single source-of-truth git repo and get distributed to every project via deploy + symlink.
When someone says Bob, BoB, big brain, or big ol’ brain, they mean this framework. See bob-identity.md for the full identity definition.
Source of truth → runtime
Section titled “Source of truth → runtime”BOB_SOURCE (~/projects/bigbrain) <- Source of truth (git repo) │ │ scripts/deploy.sh (sync) ▼BOB_HOME (~/.claude/) <- Runtime (Claude Code reads here) │ │ cdi (per-project init: symlinks) ▼.claude/ (in each project) <- Project-level entry points| Layer | Location | Purpose |
|---|---|---|
| BOB_SOURCE | ~/projects/bigbrain |
Edit here. One git repo. Single source of truth. |
| BOB_HOME | ~/.claude |
Where Claude Code reads at runtime. Synced from src. |
| Project | <repo>/.claude/ |
Per-project symlinks to BOB_HOME items + local items |
The Prime Directive
Section titled “The Prime Directive”Everything BoB manages must be declarative, version-controlled, reproducible, idempotent, and observable — with every piece of state owned by exactly one source of truth.
Full text and approved patterns: prime-directive.md.
Work tracking lives in GitHub Issues
Section titled “Work tracking lives in GitHub Issues”Every project’s vision lives in docs/vision.md. Everything below the vision lives in GitHub Issues — capabilities, requirements, bugs, decisions, lessons, session summaries, and TODOs. There are no REQ-*.md, BUG-*.md, SOL-*.md, or PM index files. If someone tries to create one, stop them.
Component types at a glance
Section titled “Component types at a glance”| Type | Source path | Loading model |
|---|---|---|
| Universal skill | BOB_SOURCE/skills/ |
Auto-loaded everywhere by Claude Code |
| Universal command | BOB_SOURCE/commands/ |
Symlinked into each project’s .claude/commands/ by cdi |
| Universal agent | BOB_SOURCE/agents/ |
Symlinked into each project’s .claude/agents/ by cdi |
| Universal runbook | BOB_SOURCE/runbooks/ |
Referenced by absolute path; not symlinked |
| Registry skill | BOB_SOURCE/registry/skills/ |
Provisioned per-project via manifest (provisions/<project>.json) |
| Registry command | BOB_SOURCE/registry/commands/ |
Same — opt-in per project |
| Registry agent | BOB_SOURCE/registry/agents/ |
Same — opt-in per project |
| Hook | BOB_SOURCE/hooks/ |
Wired up in ~/.claude/settings.json by event |
| Script | BOB_SOURCE/scripts/ |
Invoked by absolute path from skills, commands, hooks, or shell |
| Template | BOB_SOURCE/templates/ |
Copied (not symlinked) into projects, then customized |
| Provision manifest | BOB_SOURCE/provisions/ |
Declares which registry items a project gets |
2. Quick Start
Section titled “2. Quick Start”Moved to a tutorial — see Getting Started.
3. Setup & Lifecycle
Section titled “3. Setup & Lifecycle”3.1 First-time install on a machine
Section titled “3.1 First-time install on a machine”Moved — see Getting Started.
3.2 Deploying changes from BOB_SOURCE → BOB_HOME
Section titled “3.2 Deploying changes from BOB_SOURCE → BOB_HOME”deploy.sh is the only sanctioned way to update ~/.claude/. Direct edits to ~/.claude/ are anti-pattern — you’d lose them on the next deploy.
scripts/deploy.sh --dry-run # Preview what would changescripts/deploy.sh # Applyscripts/deploy.sh --first-run # First-deploy mode (cleans up legacy .git/ in BOB_HOME)scripts/deploy.sh --force # Skip confirmation promptsMachine-specific files are protected and never overwritten:
~/.claude/settings.json~/.claude/settings.local.json~/.claude/CLAUDE.local.md
3.3 Per-project initialization (cdi)
Section titled “3.3 Per-project initialization (cdi)”cd <project>cdi # Idempotent — safe to re-runCreates <project>/.claude/ with symlinks to:
commands/from~/.claude/commands/agents/from~/.claude/agents/hooks/referenced insettings.json- Templates (copied, not symlinked)
Skills are not symlinked — they’re auto-loaded globally from ~/.claude/skills/.
3.4 Provisioning registry items (cdprov + /provision)
Section titled “3.4 Provisioning registry items (cdprov + /provision)”Each project has a manifest at BOB_SOURCE/provisions/<project>.json declaring which registry skills, commands, and agents it uses (in addition to the universal set).
cdprov # Apply manifest — create symlinks to matchcdprov --status # What's in manifest vs. what's actually linkedcdprov --diff # Dry run — planned link changes + Recommendation (inferred): why: per item, would-removecdprov --refresh # Re-sync symlinks (drop stale, add missing)cdprov --init # Generate a manifest — profile inferred from vision + code by default (#1878)cdprov --init --auto # Same, one shot: write + refresh, no prompts; prints a why: line per item (#1879)cdprov --init --no-infer # Stack-only input (no capabilities) — honoured by --diff and reconcile toocdprov reconcile [--apply] [--allow-remove] # Re-infer and apply the manifest delta (#1880) — see belowThe inference inputs, evidence model, precedence and limits behind --init, --diff and
reconcile are explained once in the
orchestrator guide — Inference from vision and code,
with a flag table for the whole pipeline.
Inside Claude (/provision):
/provision status/provision add skill cloudflare-dev/provision add command deploy/provision add agent code-reviewer/provision remove skill seo-auditing/provision diff/provision init # Bootstrap a manifest for a new projectManifest shape (provisions/<project>.json):
{ "_meta": { "project": "myproj", "stack": ["cloudflare-workers", "d1"] }, "skills": ["cloudflare-dev", "d1-expert"], "commands": ["deploy", "research"], "agents": ["code-reviewer", "code-debugger"], "runbooks": []}Validate against schemas/manifest.schema.json.
Intent-mutating vs pass-through (#784). Every cd* invocation is classified by a single seam (scripts/lib/intent-classify.sh, sourced by provision.sh and cdi): an invocation is intent-mutating iff it edits provisions/<project>.json — the manifest is the source of truth and symlinks are a deterministic projection of it (#782). Only intent-mutating invocations are routed through the stage-on-branch review gate (#785, below); pass-through invocations (reads and pure projections) always run unchanged.
| Invocation | Classification | Why |
|---|---|---|
cdprov add / remove |
intent-mutating | Edits the manifest’s item lists |
cdprov init / interview |
intent-mutating | Creates / (re)writes the manifest |
cdprov status / diff / check |
pass-through | Reads only |
cdprov reconcile |
pass-through | Dry run: re-infers and prints the manifest delta (#1880) |
cdprov reconcile --apply |
intent-mutating | Applies the delta — the same staged PR / fast-path route as add/remove |
cdprov refresh |
pass-through | Pure projection manifest → symlinks (the post-merge apply, #789 — stays ungated) |
cdprov prune |
pass-through | Touches dangling symlinks, never the manifest |
cdi init — manifest step |
intent-mutating | Writes provisions/<project>.json (OQ-05) |
cdi init — structural step |
pass-through | .claude/ dir, universal symlinks, dev.json template — applies immediately (OQ-05) |
cdl |
pass-through | Symlink-only today: links a global item without touching the manifest (OQ-10). --manifest is reserved — a future manifest-aware cdl classifies intent-mutating |
cdb / cdg / cds |
pass-through | Dashboards / generators: pure reads |
| anything unrecognized | intent-mutating | Fail-safe: an unknown invocation gets reviewed, never silently applied |
The stage-on-branch gate (#785) — gated by default. An intent-mutating cdprov invocation does not write the manifest to disk or any symlink to the target project. Instead (scripts/lib/config-branch.sh, hung off the #784 seam’s route_intent):
- Any dirty BOB_SOURCE working tree is stashed (and restored when the flow finishes).
- A review branch
config/<project>-<action>-<YYYYMMDD>is cut from the default branch. Same-day collisions append an 8-hex hash of the manifest diff — deterministic, so re-running an identical change resolves to the same branch and reports “already staged” instead of duplicating. - The manifest edit is committed on that branch (
chore(config): <action> for <project>), pushed, and opened as a PR. - The command prints the PR URL and exits 0. The target project’s
.claude/is byte-identical to before the command — a rejected PR needs no on-disk revert.
What the PR contains: the manifest diff, the symlink projection that cdprov refresh will apply post-merge (each create/remove line, in the same naming the provisioner uses — skills as directories, commands/agents/runbooks as .md), and a link to each affected registry item’s definition. The PR is the canonical answer to “what did BoB just change?”.
Applying (post-merge projection, #789): merge the PR, then run cdprov refresh from the target project — refresh is the ungated post-merge projection. The PR body’s “Apply (post-merge)” section carries the exact copy-paste command for the staging machine (git -C <bob_source> pull --ff-only && cdprov refresh <target-dir>); on another fleet machine, substitute its own paths. Operator-run refresh is the default (OQ-03) — the merge applies nothing by itself, and no CI or fleet job triggers the projection; a CI/fleet auto-trigger is a documented follow-on that would need per-machine target paths (see #415/#446 fleet plumbing). Partial failures are recoverable by re-running refresh: a target path that is a real file is skipped as [local], a permission error on one link is reported as [failed] without aborting the rest of the run, and the summary says what to fix. Re-running re-attempts only what is missing (existing correct links are skipped) and the manifest is never rolled back — it stays the declared truth the next refresh converges toward. BOB_CONFIG_GATE=off remains the internal (test/CI) escape hatch; the user-facing bypass is --now (#788, below).
Config PRs are never auto-merged — with one guarded exception (#788, #1478). Staging is the default automatic step: config_branch_stage opens the review PR and stops. Warp-drive’s L3 direct-merge path merges feature branches (feature/issue-NN-*); it never touches config/* branches. A config change therefore waits for a human to merge its PR, then cdprov refresh projects it — except the single-item fast path below, which is the one code path that merges a config PR, and only under its guards. The immediate-apply alternative remains an explicit, per-invocation opt-in (--now), never the default.
Single-item fast path (#1478). A single cdprov add/remove of a registry item is mechanical, low-risk, and fully schema-validated — a review PR there adds latency without adding review value (the motivating case: a one-line flightplan add took a multi-step PR round-trip). So at automation Level 2+ (resolved through the same promotion decider --now uses; an unresolvable level fails safe to review), the just-staged PR is auto-merged and the apply steps are chained end-to-end: label config-fastpath → squash-merge → git pull --ff-only in BOB_SOURCE → deploy.sh --force → cdprov refresh. “Provision add” then means provisioned, not “merged, now run two more commands”. The guards (in config_branch_fastpath): the PR must change exactly provisions/<project>.json, the semantic delta must be exactly one item across the four item arrays (a textual line count would lie — jq’s array formatting rewrites the neighbouring comma line), and both manifest versions must pass the structural schema. Any guard miss degrades gracefully: rc 3, nothing mutated, the PR stays open for normal review — which is also how a #791-accumulated multi-item PR is handled. Level 1 keeps today’s behavior (PR opened, human merges), and BOB_FASTPATH=0 opts out at any level. The audit trail is preserved (Option A): the PR exists, labelled, with the merge commit reachable from the manifest history. A failure at any chained step names the step (MERGE / PULL / DEPLOY / REFRESH) and prints the exact resume command; a landed merge is never rolled back — resume forward, don’t restart.
The --now bypass (#788). cdprov <intent-mutating-action> --now skips the review PR and applies the change immediately: it commits the manifest edit straight to the default branch (logged [bypass: --now], via config_branch_apply_now) and projects the symlinks in the same run — today’s pre-gate behaviour, now behind a deliberate flag. It shares all of the stage path’s carve-outs and safety (BOB_HOME / protected-file refusal, no-delta no-op, dirty-tree stash/restore, staged-set invariant). --now is refused at automation Level 1 with promotion ceiling pr (exit 2, with a message pointing at /automation or /promotion): that stance mandates review, so an immediate apply is disallowed. At any other level×ceiling --now applies. The level+ceiling are resolved through the promotion decider (scripts/promotion/promotion.js); tests pin them via BOB_NOW_LEVEL / BOB_NOW_CEILING.
Warp-drive owns BOB_SOURCE — the concurrency guard (#790, resolves OQ-06). When a warp-drive session is active on BOB_SOURCE itself, an intent-mutating invocation refuses to cut a config branch on top of it (rc 5 from config_branch_stage, non-zero cdprov exit) — the session owns the repo’s branch state (checkouts, stashes, integration/feature branches), and staging underneath it would race those git operations. The signal is an explicit, documented sentinel, never an inference: the warp-drive state file at <BOB_SOURCE>/.claude/.warp-drive-state.json (warp_owns_bob_source in scripts/lib/config-branch.sh), which the state machine creates at session init and deletes at session_ended/reset. The refusal prompt surfaces the session’s phase/issue/branch and the ways forward: finish or merge that work first (warp status, /stop-warp-drive), clear stale state from a crashed session (state-machine.js reset), or override this one invocation with --now (config_branch_apply_now is deliberately unguarded — the operator explicitly accepts the risk, contained by the stash/restore + staged-set invariant). A warp-drive session on a target project never trips this — its state file lives in that project’s repo, not the config repo.
Batching policy — per-project accumulating config PR (#791, resolves OQ-01). Multiple intent-mutating commands in one session produce one config PR per project, not N. Every command still stages eagerly — there is no local buffer, so no flush trigger exists or is needed (each command pushes immediately; merging the PR is the flush). But when an open config PR already exists for the project (_cb_pending_branch: candidate config/<project>-* branches, gh-confirmed open — a merged/closed PR’s leftover branch is not pending), the command appends its manifest edit as a new commit on that PR’s branch instead of cutting a sibling PR. Edits compound: the append is computed against the pending manifest, and cdprov add/remove read their edit base from it too (config_branch_pending_manifest), so the second add’s PR diff contains both items rather than two conflicting single-item diffs. Re-adding an item already on the pending PR is a clean no-op (already staged on the pending config PR), as is a no-delta append (rc 4). The alternative — a session accumulator with a cdprov flush — was rejected: it would hold un-versioned pending state outside git (against the Prime Directive) and add a second thing to forget; here the open PR is the accumulator. If gh cannot answer the pending-PR probe, staging fails open to the eager per-command path. --now and the ungated apply route never read the pending base — they operate on the real manifest.
cdprov add / remove and the no-op guard (#786). cdprov add <type> <name> / cdprov remove <type> <name> are the item-level manifest mutations (types: skill, command, agent, runbook; the single-arg form cdprov add <name> infers the type — from the registry for add, from the project manifest for remove). Both flow through the gate above. Before any branch is cut, a no-op guard decides whether the invocation produces a manifest delta at all:
- item already in the project manifest →
already provisioned — no change - item already provided globally by BOB_HOME (#412 dedup, via the same
item_is_globalcheck the provisioner uses) →already provisioned (global dedup) — no change removeof an item the manifest doesn’t declare →not provisioned — no change
Each no-op exits 0 having created zero git refs — no branch, no PR, nothing to clean up (a fat-fingered cdprov add already-there costs nothing). add/remove require a per-project manifest (provisions/<project>.json); the _default.json fallback is a read-time convenience, never a write target. config_branch_stage keeps its own empty-diff backstop (rc 4) for the init/interview paths.
cdprov reconcile — re-derive the manifest after the vision or code moves (#1880). A manifest written once by cdprov --init drifts as a long-lived project changes: a new capability lands in docs/vision.md, a CI workflow appears, a stack is dropped. cdprov reconcile (alias cdprov --diff --infer) re-runs the #1878 inference and prints the delta the #1879 explain.js document computes against the current manifest — + <kind> <name> — would-add: <why> for recommended-but-undeclared items, - <kind> <name> — would-remove: <reason> for declared items nothing supports — and is a dry run by default (nothing written, no PR; --json emits the explain document). cdprov reconcile --apply builds the new manifest and hands it to exactly the machinery above: the #785 staged review PR, with the #1478 fast path on top — so a single-item delta at automation L2+ is auto-merged, deployed and refreshed end-to-end, a multi-item delta leaves the normal review PR open (the fast-path guard miss), and L1 always stages a PR; --now commits to the default branch, and the #791 pending-PR base applies (the delta is computed against the pending manifest and compounds onto it). It is idempotent: a reconcile immediately after an applied one computes an empty delta and exits 0 having created zero git refs. Removals are never applied by default — would-remove items are withheld (and re-reported on the next run) unless --apply --allow-remove is passed or an interactive run answers yes; _meta.manual items are never removal candidates at all. Note a would-remove whose reason is not a registry item with orchestrator metadata is unevidenced rather than unwanted — check before allowing it. Tests: make test-cdprov-reconcile.
BOB_HOME + protected-file carve-outs (#787). The gate never touches the BOB_HOME surface. provision.sh already routes a ~/.claude target to the verify-only path (#411) before the gate is reached; config_branch_stage enforces the same carve-outs itself as defense-in-depth, reusing bob-home-detect.sh (no duplicate detection): it refuses when the staging root resolves to BOB_HOME (covering the BOB_SOURCE-unset fallback, where it would otherwise cut branches inside ~/.claude — operator is pointed at deploy.sh), refuses the _bob-home(.machine/.role) global manifest as a staging target (edited via a normal BOB_SOURCE PR), and refuses a protected basename as a manifest path. A staged-set invariant before the commit guarantees a config branch carries exactly provisions/<project>.json — a touched settings.json, settings.local.json, or CLAUDE.local.md can never ride along (the stash/restore cycle hands it back to the working tree untouched).
3.4a Universal vs registry — and how to promote between tiers
Section titled “3.4a Universal vs registry — and how to promote between tiers”Every command, skill, and agent lives in exactly one of two tiers. This is the single most important distinction to internalise — most “why isn’t my command showing up?” confusion comes from not knowing which tier an item is in.
| Tier | Source folder | Who gets it | How it lands in a project |
|---|---|---|---|
| Universal | commands/, skills/, agents/, runbooks/ |
Every project, automatically | cdi symlinks commands/agents in; skills auto-load globally |
| Registry | registry/commands/, registry/skills/, … |
Only projects that opt in | Listed in provisions/<project>.json, then cdprov links it |
Registry is an à-la-carte menu: with 30+ registry skills and ~30 registry commands, you don’t want cloudflare-dev loaded into a non-Cloudflare project. So registry items are opt-in, declared per-project. Universal items carry a context cost in every project, so default new items to registry and promote only the ones that are genuinely needed everywhere.
The end-to-end flow — what links what:
edit in BOB_SOURCE → deploy.sh (sync to ~/.claude) → cdi (universal symlinks) → cdprov (registry symlinks, per manifest)deploy.sh copies files into the runtime dir but links nothing into projects. cdi links the universal set. cdprov links the registry items a project’s manifest names. A registry item that no manifest references is linked into no project — even after deploy.
Where the links point (#1475): registry symlinks target the deployed registry — LINK_ROOT = ${BOB_HOME:-~/.claude}/registry — not the source checkout (decision #1484). Manifest reads/writes resolve through BOB_SOURCE (#1220), but link creation and comparison are pinned to LINK_ROOT, so cdprov refresh produces byte-identical targets no matter which environment invoked it. Consequences: an item merged to source but not yet deployed is reported with a “run deploy.sh” hint instead of being linked into the checkout, and moving existing links to a new root is always called out explicitly ([retarget] lines plus a summary count) — a mass retarget can never happen silently. Regression coverage: make test-link-root. Extended to every link BoB writes (#1924): cdi’s universal command/hook/agent links and the git commit-msg hook, and cdprov’s universal retargets, all target $BOB_HOME too — a source-only item is reported [pending] … run deploy.sh, never linked into the checkout — and make check-abs-paths fails the build on any literal home path in a synced or ledgered file, or any absolute-target symlink in a synced tree (see check-abs-paths).
Authored vs. derived — what a consuming repo commits (#1479): commit what is authored in the project (CLAUDE.md, dev.json, seed/, .claude/settings.json, real-file commands/agents/skills); gitignore what is provisioned (every symlink cdprov materializes from the manifest, plus settings.local.json). Provisioned links are derived state — tracking them creates a second owner that fights the manifest, and they embed absolute user paths that dangle for any other clone. cdprov refresh maintains a managed block (# --- BoB-provisioned … ---) in the project’s ignore file, rebuilt from the manifest on every provisioning write so it can never drift from the provision list. Placement is per-project via the manifest’s _meta.ignore_placement: the committed .gitignore (default) or .git/info/exclude (no BoB fingerprints in client repos); _meta.track_provisioned: true is the explicit opt-out. Two consequences to know: drift detection moves to BoB tooling — a stale link no longer shows in git status; cdprov status flags [unlinked] items and the fleet snapshot carries unlinked_declared — and fresh clones materialize links with cdi + cdprov refresh (links do not arrive via checkout; a just-cloned project showing [unlinked] items is pre-refresh state, not breakage). Coverage: make test-ignore-block.
Promote registry → universal (the item should be in every project):
cd ~/projects/bigbraingit mv registry/commands/<name>.md commands/<name>.md # the tier IS the folderscripts/deploy.sh # sync to ~/.claude# then re-run `cdi` in each existing project to pick up the new symlink;# new projects get it automatically. Remove the item from any provisions/*.json# that listed it — it's now universal, so an explicit opt-in is redundant.Demote universal → registry (the item is only needed by some projects): reverse the move (git mv commands/<name>.md registry/<name>.md), deploy.sh, then add the item to the manifest of each project that still needs it and cdprov --refresh. Projects not naming it lose the symlink on their next cdi/refresh.
The tier of an item is defined entirely by which folder holds it — promotion and demotion are just a git mv plus a re-link. There is no separate registration step.
3.5 Updating BoB
Section titled “3.5 Updating BoB”cd ~/projects/bigbraingit pull # Get upstream changesscripts/deploy.sh --dry-run # Always preview firstscripts/deploy.sh # Applymake check # Verify nothing broke3.6 Local overrides (gitignored)
Section titled “3.6 Local overrides (gitignored)”<project>/.claude/CLAUDE.local.md— project notes that don’t get checked in.~/.claude/CLAUDE.local.md— global notes that don’t sync.
These are loaded into context but never deployed or committed.
3.7 Verification (Makefile)
Section titled “3.7 Verification (Makefile)”make is the IaC verification layer. Run any of these from BOB_SOURCE:
| Target | What it checks |
|---|---|
make check |
All of: deps + schemas + symlinks + hooks + provisions |
make check-deps |
node, jq, git, make versions |
make check-schemas |
All *.json configs against JSON Schema definitions |
make check-symlinks |
Every symlink across registered projects resolves |
make check-hooks |
Every hook script in settings.json exists and is executable |
make check-provisions |
Each project’s actual symlink state matches its manifest |
make test |
Schema validator unit tests + warp-drive state machine tests + integration |
make ci |
check + test (what GitHub Actions runs) |
make doctor |
Diagnostic dump for triaging weirdness |
make help |
Print all targets |
Current test counts (run make test for the live numbers):
- Schema validator: 34 unit tests
- Warp-drive state machine: 99 transition tests
- Integration tests for all check scripts: 13
make ci is what GitHub Actions runs on push and PR to master. CI skips check-symlinks and check-provisions because those scan local project directories that don’t exist on the runner.
Doc-link sweep: the doc-keeper auditor’s broken_relative_link rule (scripts/doc-keeper/audit.js, run via make docs-check) validates every relative .md/.txt link across README.md, CLAUDE.md, and docs/**/*.md (excluding docs/archive/**, which is frozen historical content). It supersedes the former standalone check-doc-links.sh, removed in #462 as dead tooling.
3.8 Cross-machine audit (#415)
Section titled “3.8 Cross-machine audit (#415)”BoB runs on a set of machines — the fleet — declared in machines.json (resolver: scripts/fleet/machine.js whoami). scripts/fleet/audit.js audits any managed project on any fleet machine from any other, over Tailscale: audit.js snapshot publishes this machine’s project/provision/health condition to $BOB_FLEET_SNAPSHOTS (default ~/.claude/fleet/snapshots/<id>.json), audit.js pull <id|tailscale-name> runs the audit on a remote machine over Tailscale SSH, and audit.js view aggregates live + last-known-cached state across the fleet, marking staleness and unreachable targets. Each snapshot also carries promotion readiness (#446), mirror condition (mirror-remote.md), and harness-defect counts (#1838). Optional host layer (#1871, #1925): when this machine’s machines.json entry declares a host_provider: {id, command} (Fleet on the laptops, thefarm on farm-01 — declared, never discovered from PATH; $BOB_FLEET_HOST_PROVIDER is an override only, and an empty value opts out), the snapshot runs <command> status --json and carries a host section consumed by contract: host-status/1 (schemas/host-status.schema.json, owned here, deployed to BOB_HOME/schemas/; fleet-status/1 is a profile of it). Every provider-defined section lands in host.sections (+ host.section_order) summarised to {ok, drift} — Fleet’s five are also still addressable as host.<name> — plus host.drift_count, and view rolls them up as a “Host drift” table whose columns are the union of sections reported across machines, with a PROVIDER column. No provider means no host key; a declared provider that is missing, fails or times out (BOB_FLEET_HOST_TIMEOUT_MS, default 20000) is recorded as host.error, never a failed snapshot, and summary.host_drift (#1921) is carried either way. The provider’s document wins over its exit code — a provider exits 3 on drift with the full document on stdout (na: true sections are not-measured rather than drift), and a verdict: error document surfaces its error as host.error (paulirv/fleet#7). A declared command may be $HOME/$BOB_HOME-relative (#1924) — expanded for the spawn, recorded as declared. Inbound audits require the target to accept SSH — macOS has Remote Login off by default.
“fleet” vs. “Fleet”. BoB’s fleet is the machine layer above —
machines.json,scripts/fleet/{audit,machine,readiness,mirror,runner}.js,BOB_FLEET_SNAPSHOTS. It is not the Fleet project (~/projects/fleet, provisioned viaprovisions/fleet.json): the host-environment layer beneath BoB for the operator’s Tailscale devices (Homebrew bundle, iTerm2 profiles, repo sync, SSH reachability,fleet status/fleet travel). Fleet composes BoB — it reusesmachines.json,scripts/fleet/*,deploy.sh, andmirror.jsand never re-declares_bob-home.*; see Fleet’s vision at~/projects/fleet/docs/vision.md. Both terms are defined in the glossary.
4. The Product Hierarchy
Section titled “4. The Product Hierarchy”Vision & Strategy (docs/vision.md) /vision└── Business case (GitHub Issue, business-case) /business-case — justify investment (above cap) └── Capability (GitHub Issue, label: cap) /capability └── Requirement (GitHub Issue, req) /requirement └── Warp-drive chunks automatic — individual commits
Use case (GitHub Issue, label: use-case) /use-case — a scenario; Realizes → cap/req└── → Capability (broad) or Requirement (narrow)| Level | Format | Command | Contains |
|---|---|---|---|
| Vision | docs/vision.md |
/vision |
Why, who, where, principles, non-goals, roadmap. Living doc. |
| Business case | GitHub Issue (business-case) |
/business-case |
Problem, options, cost/benefit, risk, recommendation, success metrics |
| Capability | GitHub Issue (cap) |
/capability |
User stories, success metrics, requirements checklist |
| Requirement | GitHub Issue (req) |
/requirement |
Description, acceptance criteria (checkboxes), priority, notes |
| Use case | GitHub Issue (use-case) |
/use-case |
Actor, goal, scenario, success criteria; decomposes to cap/req |
| Bug | GitHub Issue (bug) |
gh issue create --label bug |
Bug reports |
| TODO | GitHub Issue (todo) |
auto-created by warp-drive timeout | Human action items |
Justification and scenario nodes
Section titled “Justification and scenario nodes”Two issue types bracket the capability spine without being part of the linear cap → req decomposition:
- Business case (
business-case,/business-case) sits above capability — it justifies investment before capabilities are spawned. Its recommended option is realized by one or more capabilities, each carryingPart of #NN(whereNNis the business case) in its Notes, exactly as a requirement links to its capability. The vision-doc fold-in of an approved recommendation is deferred to grooming;/business-casenever editsdocs/vision.md. - Use case (
use-case,/use-case) captures a concrete scenario (actor, goal, main/alternate flows, success criteria) and decomposes into the work that realizes it — a capability (broad: multiple actors/steps) or requirement(s) (narrow: one actor/goal). Each realizing issue carriesRealizes #NN(whereNNis the use case) in its Notes, and inherits the use case’sarea:<slug>so the whole scenario tree is one warp-drivable workstream.
Query the traceability with gh issue list --label use-case, gh issue list --label business-case, and gh issue list --search "Realizes #NN".
Labels
Section titled “Labels”| Label | Meaning |
|---|---|
business-case |
Business case — investment justification above capability |
cap |
Capability |
req |
Requirement |
use-case |
Use case — a scenario that decomposes into cap/req |
bug |
Bug report |
todo |
Human action item |
approved |
Ready to work — picked up by /warp-drive |
in-progress |
Being worked on — on a cap, machine-derived from child-req state (see Capability status) |
blocked |
Waiting on something |
completed |
Done-signal: on a todo, the human performed the action; on a closed cap, every child req closed (derived — closed without it means abandoned) |
implemented |
Code done, awaiting verification |
serial-only |
Excluded from cdfork --from-issues (pinch points) |
area:<slug> |
Workstream tag — batches issues for one module/feature/capability (see Area labels) |
decision |
Architecture decision (created by /journal decision) |
lesson |
Lesson learned (created by /journal lesson) |
session-summary |
Session summary (created by /session-summary) |
p1-critical / p2-high / p3-medium / p4-low |
Priority |
The approval workflow
Section titled “The approval workflow”/capabilitycreates an issue (labelcap).- Break it into requirements with
/requirement(labelreq, withPart of #NNlinking back to the capability). - Add
approvedwhen a requirement is ready to work. /warp-drivepicks the highest-priorityreq+approvedissue not in progress.- When done, warp-drive flags it
implementedand moves on.
Capability status (derived, never hand-set)
Section titled “Capability status (derived, never hand-set)”Capability status is derived state (#989), machine-reconciled from child-requirement
issue state by scripts/warp-drive/cap-status.js — /groom runs it as the reconciler
of last resort, and /requirement invokes its reopen gate when linking a new child req.
Three states, no new labels:
| State | Encoding | Derivation |
|---|---|---|
| Not started | Open cap, no status label | No child req closed or in-progress (absence of a label is deliberate — one less invariant) |
| In progress | Open cap + in-progress |
≥1 child req closed or itself in-progress; removed again when none are |
| Complete | Closed cap + completed |
Every child req (checklist ∪ Part of #NN, issue state as truth) is closed |
A cap closed without completed means abandoned/cancelled — the reconciler never
produces that state and never touches closed caps, mirroring the todo lifecycle exactly.
Filter recipes:
gh issue list -l cap,completed -s closed # completed capabilitiesgh issue list -l cap,in-progress # in-progress capabilitiesgh issue list -l cap --search '-label:in-progress' # not-started capabilitiesArea labels (batching by workstream)
Section titled “Area labels (batching by workstream)”Within a single repo, issues for distinct workstreams — a new module, a feature, a capability rollout — are intermixed. An area:<slug> label tags every issue belonging to one workstream so the whole cluster can be listed and warp-driven as a unit without hand-picking issue numbers (#260).
-
Convention. Slugs are kebab-case workstream names:
area:billing,area:baz,area:seebod. Labels use a reserved colour (#5319E7) and anArea: <Name>description so the namespace is visually distinct and observable. Scope is within a single repo — each project tracks its own issues on its own repo. -
Hierarchy fit. A capability owns its
area:<slug>(derived from its title); its child requirements inherit the same label so the Capability → Requirement tree is warp-drivable as one unit. -
Idempotent provisioning (Prime Directive). Create/reconcile a label on demand with the helper — it never duplicates or clobbers:
Terminal window ~/.claude/scripts/area-labels.sh ensure billing # create/refresh area:billing (accepts a name too)~/.claude/scripts/area-labels.sh list # list existing area:* labels~/.claude/scripts/area-labels.sh slugify "Billing & Invoicing" # -> billing-invoicing -
Status overview per workstream (the
gh issue listrecipe):Terminal window ~/.claude/scripts/area-labels.sh issues billing # open issues in the area~/.claude/scripts/area-labels.sh issues billing --state all# equivalent raw form:gh issue list --label "area:billing" --state open -
Warp-drive batching.
/warp-drive --area <slug>narrows discovery toreq + approved + area:<slug>— see the warp-drive guide. -
Expectation & drift check (#527). Every open
capandreqshould carry anarea:<slug>label (other issue types — bugs, todos — take one when a workstream is meaningful, but it is not required)./capabilityand/requirementassign one at creation; to catch anything that slipped through, run the audit (also wired into/what-nextStep 5):Terminal window ~/.claude/scripts/area-labels.sh audit # lists open cap/req missing area:* (exit 3 on drift)
What NOT to create
Section titled “What NOT to create”The following filename patterns are forbidden. They predate the GitHub Issues migration. Do not regenerate them:
REQ-*.md,SOL-*.md,BUG-*.md,RISK-*.md,INC-*.md,CR-*.md,UXR-*.md,STATUS-*.mdREQUIREMENTS-INDEX.md,SOLUTIONS-INDEX.md,backlog.md,traceability-matrix.md.claude/project-management/,.claude/requirements/,.claude/journal/decisions/(orlessons/,sessions/)
If a tool emits any of these, treat it as a bug and fix the tool.
Pinch points (mark serial-only)
Section titled “Pinch points (mark serial-only)”Some work cannot be parallelized. Mark these requirements serial-only so cdfork --from-issues skips them:
- Config / settings sync
- Lockfile bumps
- DB migrations
- Compiled-asset rebuilds
- Deploy steps
5. Dev Environment Lifecycle
Section titled “5. Dev Environment Lifecycle”Every project can declare its dev environment in dev.json. The /dev-up command (and bin/dev-up from a terminal) reads it and brings the environment to a fully testable state — server, migrations, seed data, test users, health check.
Full reference: dev-lifecycle.md.
5.1 The dev.json manifest
Section titled “5.1 The dev.json manifest”Lives at the project root. Sample at templates/dev.json. Schema at schemas/dev.schema.json.
{ "server": { "command": "npm run dev", "port": 5173, "health": "/api/health" }, "migrations": { "command": "npm run migrate:dev", "auto_run": true }, "seed": { "directory": "seed/", "runner": "node", "order": "alphabetical" }, "auth": { "adapter": "d1", "users_file": "seed/users.json" }, "access": { "localhost": "http://localhost:5173" }}5.1a Port allocation (#197)
Section titled “5.1a Port allocation (#197)”cdi allocates every project a deterministic 10-port band so multiple dev
servers coexist without collisions. Two-tier, single source of truth:
- Ledger (authoritative):
BOB_SOURCE/provisions/ports.json, version-controlled, managed byscripts/port-ledger.js.base = 5200 + slot*10;slotfrom an FNV-1a hash of the project name. Sub-offsets:+0vite,+1wrangler,+2cds dashboard,+3..9reserved. - Projection (derived):
cdiwrites a marked, regeneratable block intodev.json(_bob_ports+server.port) and the vite/wrangler configs — never hand-edited; lossless and idempotent.
Name-hash is the default proposal; the ledger only records and arbitrates
collisions (deterministic next free band). Explicit non-default pins (e.g.
nanaawards 5180) are retrofit-backfilled and preserved. The legacy cds
~/.claude/dashboard-ports.json registry is folded into the ledger and retired.
Inspect with cdb --ports / cds --ports. Full reference:
dev-lifecycle.md → Port Allocation.
Fleet backfill (#198). Projects provisioned before #197 have no band yet.
make port-migrate (or node scripts/port-fleet-migrate.js) is a one-shot,
idempotent migration that walks every provisioned project (provisions/*.json),
allocates each its band via the same allocator as cdi, writes the ledger,
and regenerates the marked projection blocks. It backfills only un-allocated
projects, never reassigns an existing band, aborts loudly before any write on a
pin collision, and is a verified no-op on a second run. Always review
make port-migrate-dry first. deploy.sh runs it idempotently (honouring
--dry-run) and non-fatally on each deploy.
5.2 /dev-up and friends
Section titled “5.2 /dev-up and friends”| Command | Purpose |
|---|---|
/dev-up |
Full lifecycle (server + migrations + seed + users + health) |
/dev-up --check |
Health probe only |
/dev-up --skip-seed / --skip-users / --skip-server |
Partial run |
/dev-up --regen-seed |
Discard cached seed and regenerate |
bin/dev-up |
Same, from a regular terminal |
bin/dev-health |
Shortcut for dev-up --check |
5.3 Seed convention
Section titled “5.3 Seed convention”- Seed scripts live in
seed/— alphabetical execution order. - Seed data must cover all lifecycle states the domain defines (not just fresh records).
- New features include their seed updates in the same commit.
- Seed coverage gate:
scripts/dev-lifecycle/check-seed-coverage.sh.
5.4 Standardized test users
Section titled “5.4 Standardized test users”seed/users.json declares test users, provisioned via pluggable auth adapters.
| Adapter | Use when… |
|---|---|
d1 |
Cloudflare D1 (SQLite) |
sqlite |
Local SQLite |
supabase |
Supabase auth |
script |
Custom provisioning script |
custom |
Inline command in provision_command |
The superuser is consistent across every project: admin@test.local / admin123.
5.5 Integration with warp-drive
Section titled “5.5 Integration with warp-drive”When dev.json exists, warp-drive automatically:
- Calls
dev-upbefore the first chunk - Verifies health between chunks
- Auto-recovers on dev failure
- Treats dev health failure as a blockable event
6. Manual Workflow
Section titled “6. Manual Workflow”The day-to-day cycle when you’re at the keyboard.
/start-work → branch off… code …/commit → create a commit/journal → optional: capture a decision/lesson/pr → optional: open a PR/finish-work → merge + cleanup/session-summary → close the session as a GitHub Issue/start-work
Section titled “/start-work”Creates a new branch with a sensible name. Argument: feature | fix | docs | refactor | tooling | chore.
/commit
Section titled “/commit”Stages and commits with a generated message. The commit skill enforces:
- No commits to
master/main(use/start-workfirst) - Pre-commit hooks run (don’t bypass with
--no-verify) - Commit message reflects the why, not just the what
Pushes branch and opens a PR with requirement traceability — links back to the GitHub Issue(s) the branch addresses.
Subactions: /pr create, /pr template, /pr link.
/finish-work
Section titled “/finish-work”Merges current branch to master (Level 3) or opens a PR (Level 2), runs cleanup, deletes the local branch.
/journal
Section titled “/journal”Create a journal entry as a GitHub Issue. Three types:
| Subcommand | Label |
|---|---|
/journal decision <name> |
decision |
/journal lesson <name> |
lesson |
/journal session <name> |
session-summary |
/session-summary
Section titled “/session-summary”End-of-session writeup as a GitHub Issue. Captures what shipped, what’s next, decisions, lessons.
Built-in Claude Code commands you’ll use alongside
Section titled “Built-in Claude Code commands you’ll use alongside”| Command | Purpose |
|---|---|
/review |
Code review of pending changes |
/security-review |
Security review of pending changes |
/init |
Initialize a CLAUDE.md from existing codebase |
/clean_gone |
Clean up local branches that are gone on the remote |
/commit-push-pr |
Commit, push, and open PR in one go (built-in plugin) |
/loop |
Run a slash command on a recurring interval |
/schedule |
Cron-style scheduling for recurring agents |
7. Autonomous Workflow (Warp Drive)
Section titled “7. Autonomous Workflow (Warp Drive)”7.1 Automation levels
Section titled “7.1 Automation levels”| Level | Name | Start with | Default behavior |
|---|---|---|---|
| 1 | Supervised | claude / -a1 |
Confirm every tool that can change state |
| 2 | Trusted Dev | claude -a2 |
Auto-approve safe ops; PRs instead of direct merges |
| 3 | Autonomous | claude -a3 |
Minimize prompts; merge directly to master |
| 4 | Full-Auto | claude -a4 |
Superset of L3: reversible (two-way-door) decisions auto-made + recorded, irreversible (one-way-door) decisions notify-and-wait or stop; requires the full-auto profile and a cost ceiling to start |
Switch mid-session with /automation level <N>. See profiles/{supervised,trusted,autonomous,full-auto}.json for the exact permission profiles. Level 4 is a superset of Level 3 — it inherits the autonomous allow-list and always-blocked floor verbatim, then adds an irreversible-action floor; it only activates when active_level is explicitly 4 and the full-auto profile is present, and it refuses to launch without a spend ceiling. Full behavior table: automation-behavior.md.
7.2 /warp-drive — the autonomous development loop
Section titled “7.2 /warp-drive — the autonomous development loop”This replaces /auto-loop. /auto-loop is legacy and will be removed.
/warp-drive # Discover next approved requirement, work it/warp-drive 42 # Work on issue #42 specifically/stop-warp-drive # Graceful stopBefore the first cycle, prerequisites run two blocking health gates side by side: dev health (dev-up) and the provisioning preflight (#1267) — provision-preflight.js check grades the project’s manifest adequate / inadequate (missing orchestrator-recommended items, named) / absent / unknown (detect or engine error — failure-safe, never a silent proceed) / skipped (non-BoB or opt-out). A non-adequate verdict is a blockable event resolved per automation level: L1 blocks and asks, L2 requires confirm/deny of the proposed cdprov fix (timeout = block), L3 auto-provisions via cdprov and reports the applied diff at session start and in the session summary. The same gate runs at the other session-start seams — /start-work step 11 and /flightplan Step 0 — so no session begins coding against tooling that can’t support it. Declines and operator overrides are session-scoped (provision-preflight.js waive, cleared at session end).
The preflight’s mid-session complement is gap detection (#1134). When a session is about to hand-roll a procedure, gap-detect.js match --task "<what you're doing>" asks whether an unprovisioned registry item already covers it — matching frontmatter metadata with a deliberately conservative bar (a false “you need skill X” nudge is worse than a missed one) and honouring the #412 dedupe (provisioned and machine-global items are never nudged). A hit emits the exact cdprov add <kind>/<name> --now fix, resolved per automation level: L1 asks, L2 confirms, L3 applies and reports — the provision change is always a visible manifest edit. Its vision-side twin (#1881) is gap-detect.js divergence: instead of a free-text task it takes the project’s inferred profile (vision + README + code, #1878) and nudges — same dedupe, same fix: line, same exit codes — for every registry item the #1879 explain document would-add but the manifest does not declare, each reading vision now mentions <evidence> — registry item <name> covers it. /vision runs it after writing docs/vision.md and /groom once per reconciliation pass (step 2c); both print the nudges verbatim and never apply them — the manifest is edited only by cdprov add / cdprov reconcile --apply (#1880). Gaps that exist nowhere (no registry item at all) are the authoring-gap stream: sessions record them into the evidence ledger (gap-analysis.js observe), and the trace-mining pass batches recurring ones into issue-first authoring recommendations (§9.3) — never auto-authored.
What it does each cycle:
- Discover work (
gh issue list --label approved --label req) - Plan implementation
- Code (chunked: max 3 acceptance criteria per commit)
- Test
- Update issue (flip ACs, add labels)
- Commit + journal
- Merge (L3/L4) or open PR (L2)
- Ask “continue?” → loop or stop
At Level 4 the loop runs the same cycle, but the human-in-the-loop pauses are resolved by the reversibility decision engine instead of prompting: reversible calls are auto-made and recorded as decision issues, one-way doors notify-and-wait or stop. The continue step auto-continues (bounded), an in-session QA loop gates promotion, and a long run checkpoints and resumes in a fresh context so quality holds over hours — all under a required cost ceiling. See automation-behavior.md §Level 4.
Full deep-dive: warp-drive.md.
7.3 The warp CLI (terminal observation)
Section titled “7.3 The warp CLI (terminal observation)”Run from any shell — no Claude session needed:
warp status # Phase, current chunk, recent activitywarp stop # Abort the loop from outsidewarp config # Show _workflow settings (incl. session_merge)warp help # All commandsIntegration-branch streams (#268). Accumulate several requirements onto one branch and ship them once, instead of a merge per requirement:
warp session start integration/feature-x # create + check out the session branch# ...run /warp-drive over the cluster; each requirement merges into the session branch...warp session status # show resolved branch configwarp finalize --dry-run # preview the shipwarp finalize # L2: one consolidated PR / L3: rebase+merge to mainwarp session end # clear the session branch (git branch left intact)The merging phase switches to local-accumulate automatically when a session branch is set and _workflow.session_merge is local (the default); set it to pr to keep per-requirement PRs. See the warp-drive guide.
7.4 /what-next — discover work without committing
Section titled “7.4 /what-next — discover work without committing”Reads project state and proposes the next sensible work item. Use it when you’re not sure where to pick up.
/what-next7.5 /stop-warp-drive
Section titled “7.5 /stop-warp-drive”Inside Claude — graceful stop with proper cleanup. Don’t kill the session with Ctrl-C; let the skill flush state cleanly.
7.6 Remote Decision Bridge (/rdb)
Section titled “7.6 Remote Decision Bridge (/rdb)”When you’re away from the keyboard, RDB routes all decisions to Telegram via ask_remote/notify_remote.
/rdb on # Decisions go to Telegram/rdb off # Back to terminal prompts/rdb status # Current stateGoing AFK? Saying “I’m stepping away” is treated as /rdb on automatically.
7.7 Warp-drive launch checklist
Section titled “7.7 Warp-drive launch checklist”-
claude -a3 -rdb(or-a2 -rdb) -
/warp-drivestarted - Phone has Telegram open
- Laptop plugged in and won’t sleep
- At least one
req+approvedissue exists
7.8 Error handling
Section titled “7.8 Error handling”- Warp-drive does 3 strikes on failed fixes before escalating.
- Failures escalate to a Telegram prompt (RDB) or terminal (no RDB).
- “Type ‘skip’” or “do whatever you think is best” are valid responses.
- Repeated escalations create a
todo-labeled issue for human follow-up.
7.9 Declarative loop primitive (generic “do X until Y”)
Section titled “7.9 Declarative loop primitive (generic “do X until Y”)”Warp-drive is a loop hardcoded to the dev cycle. For any other
iterate-until-done loop — fix-until-tests-pass, refactor-until-lint-clean, or
“keep running this agent against this check until it passes” — use the
declarative loop primitive (#424): a manifest (goal + agent +
evaluator + stop_condition + guardrails) run by a single reusable runner.
node ~/.claude/scripts/loop/run.js ~/.claude/templates/loops/fix-until-tests-pass.jsonGuardrails are mandatory (max_iterations at minimum) and breaches halt as
blockable events, reusing warp-drive’s budget_exceeded semantics. Cost
telemetry reuses the warp-drive token/cost model. Provision it as the
skills/loop-primitive registry item. Full reference:
loop-primitive.md.
8. Parallel Work (cdfork)
Section titled “8. Parallel Work (cdfork)”cdfork fans out N parallel /warp-drive sessions, one per branch, in isolated git worktrees + tmux windows. Tmux + git are the state — no daemon, no DB. If the orchestrator dies, your worktrees and commits survive.
Full reference: cdfork.md.
8.1 Subcommands
Section titled “8.1 Subcommands”| Command | Purpose |
|---|---|
cdfork fork <branch>... |
Spawn one warp-drive per named branch |
cdfork fork --from-issues [N] |
Auto-pick N approved requirements; fan out |
cdfork status [--json] |
List worktrees + tmux + warp-drive state |
cdfork drop <branch> [--force] |
Tear down a worktree + tmux window |
cdfork drop --all [--force] |
Tear down every cdfork worktree for this repo |
cdfork pair --contract <p> --backend <r> --frontend <r> --branch <n> |
Cross-repo contract-driven mode |
cdfork-pair ... |
Standalone shape of cdfork pair |
cdfork help |
Show command summary |
8.2 Worktree layout
Section titled “8.2 Worktree layout”For a repo at /path/to/<repo>/:
/path/to/<repo>.worktrees/<branch>/Sibling, not nested — keeps Composer, Bundler, npm, etc. from getting confused.
8.3 When to use cdfork vs. a single warp-drive
Section titled “8.3 When to use cdfork vs. a single warp-drive”| Use cdfork when… | Use single /warp-drive when… |
|---|---|
Multiple independent issues are approved |
Single issue or tightly-coupled work |
| Work clusters don’t touch the same files | Issues touch shared config/migrations |
| You want to maximize throughput while AFK | Linear progression matters |
| You have CPU + RAM to spare | Single-threaded resource constraint |
8.4 What cdfork --from-issues actually does
Section titled “8.4 What cdfork --from-issues actually does”- Runs
gh issue list --label approved --label req(excludingserial-only) - Picks the top N (default 3) by priority + age
- Derives a branch name per issue (e.g.
req-42-add-auth) - Spawns one worktree + tmux window + warp-drive per branch
8.5 cdfork pair (cross-repo)
Section titled “8.5 cdfork pair (cross-repo)”Designed for headless splits where backend + frontend share a contract. Anchor use case: nanawall.com Drupal-headless migration.
cdfork pair \ --contract ~/contracts/api-spec.yaml \ --backend ~/Sites/nanawall-api \ --frontend ~/Sites/nanawall-web \ --branch feat/product-listingTwo worktrees, two tmux windows, both warp-drives anchored to the same contract path.
8.6 Prerequisites
Section titled “8.6 Prerequisites”| Tool | Why | Install |
|---|---|---|
git |
Worktrees | brew install git |
tmux |
Window management | brew install tmux (NOT installed by default) |
claude |
Spawned in each window | Claude Code CLI |
gh |
--from-issues |
brew install gh |
jq |
Status JSON, issue filtering | brew install jq |
Preflight runs before any worktree is touched and fails fast on missing deps.
9. Reporting & Continuous Improvement
Section titled “9. Reporting & Continuous Improvement”9.1 Per-event reporting (GitHub Issues)
Section titled “9.1 Per-event reporting (GitHub Issues)”| What | Command | Label |
|---|---|---|
| Architecture decision | /journal decision <name> |
decision |
| Lesson learned | /journal lesson <name> |
lesson |
| Session writeup | /session-summary or /journal session <name> |
session-summary |
| Bug report | gh issue create --label bug |
bug |
| Human action item | auto-created by warp-drive timeout | todo |
9.2 /rebob — full state reconciliation
Section titled “9.2 /rebob — full state reconciliation”Think git fsck for project management. Examines ground truth (git log, file system, codebase) and reconciles all PM artifacts against it.
/rebobUse it after long absences, before/after major refactors, or when state feels off.
9.3 /trace-mining — outer improvement loop
Section titled “9.3 /trace-mining — outer improvement loop”Analyzes past session journals to extract failure patterns, identify recurring harness gaps, and propose targeted improvements.
/trace-miningOutput feeds back into BoB — surfacing missing skills, broken hooks, or workflow friction. Pair with gh issue list --label lesson to mine accumulated learnings.
Trace-mining also hosts the authoring-gap analysis (#1134): gap-analysis.js analyze reads the session evidence ledger (#1131) for recurring improvised procedures and no-trigger tasks, clusters lesson issues by shared theme, and applies a configurable recurrence threshold (_workflow.gap_recurrence_count, default 3 occurrences across ≥2 sessions). Cross-project recurrence flags a registry candidate; single-project recurrence a project-docs candidate. gap-analysis.js recommend --apply files the qualifying batch as authoring-gap issues carrying the ledger evidence, routed toward the skill-creator path — zero skills are ever auto-authored, and a dismissed recommendation stays suppressed until new evidence accrues.
9.4 Registry-only continuous-improvement commands
Section titled “9.4 Registry-only continuous-improvement commands”Provision and use as needed (not universal — opt-in per project):
/research— Manage research projects (questions, sources, findings, decisions). Also available as a universal skill (research)./retrospective— Standard / start-stop-continue / 4Ls retros./standup— Daily standup format./status— Status reports at quick / weekly / monthly cadence.
9.5 Automatic self-healing (warp-drive session end)
Section titled “9.5 Automatic self-healing (warp-drive session end)”At session_ending, warp-drive runs scripts/warp-drive/trace-mine-session.sh
to close the improvement loop without manual effort. Since #295 it mines the
warp-drive state history, not the issues you filed:
- What counts as a finding — genuine execution friction recorded by the state
machine:
tests_failed/merge_failed/push_failedevents,retry_count, more than one coding↔testing cycle (coding_cycles),no_work, and phase stalls (longest inter-transition gap overmax_phase_minutes). Each finding is tagged with an action bucket: coding reliability (root-cause), merge/process (config), harness/capability (stall), or process. - No friction → no issue. A clean session is skipped silently
(
{"skipped":true,...}) — the common case. This is the key difference from the old behavior, which always emitted an issue that merely re-listed the chunk reports/decisions filed during the session. - Context, not findings. Issues filed during the session (
decision,risk,lesson,bug-deferred) and the session summary are listed under a Context section for cross-reference — they are no longer presented as findings. - When friction is found, the miner files a
warp-drive-labelled issue on the BoB repo titledself-healing: session friction from <project> #<req>.
This complements the on-demand /trace-mining (§9.3): the automatic miner flags
per-session friction; /trace-mining analyses patterns across many sessions.
9.6 Model tiering in multi-agent workflows
Section titled “9.6 Model tiering in multi-agent workflows”When you orchestrate work with the Workflow tool (agent() / parallel() / pipeline()), make asymmetric model tiering the default: spend a cheap, fast model on wide exploration and reserve a strong model for selective judgment. This is a convention, not a new capability — agent(prompt, {model, effort}) already accepts per-call overrides. Source: the “Self-Improving Loop” external-validation lesson (#264) — cheap breadth, expensive judgment.
The convention.
| Stage role | Examples | Default tier |
|---|---|---|
| Finder / explorer / sweep — wide, shallow, parallel, disposable | find bugs, grep logs, enumerate call-sites, map a subsystem | cheap: model: 'haiku' or low effort |
| Verify / judge / critic / synthesize — narrow, deep, decisive | adversarially refute a finding, score competing designs, write the final answer | strong: model: 'opus' (or omit to inherit) + high effort |
Economic rationale. Finders run in bulk and most of what they surface is noise — paying premium per-token to generate candidates is wasteful. Judgment runs on the small filtered set where correctness actually matters, so that is where the strong model earns its cost. A pool of cheap finders feeding a few expensive verifiers gets most of the quality of an all-strong fleet at a fraction of the spend.
Inheritance caveat. When you pass no model/effort, the agent inherits the session model (the resolved main-loop model) and session effort — which is almost always the right default. Only override when you’re confident a different tier fits the stage: drop finders to haiku/low, lift the hardest verify/judge stages to high effort. Don’t set model “just because”; an unnecessary override is how you accidentally run a 200-agent sweep on the expensive tier.
Canonical example — find → verify with explicit per-stage tiers:
// Cheap, parallel finders sweep each dimension; each finding is then verified// by an expensive, skeptical judge. The pipeline runs verify-as-soon-as-found.const results = await pipeline( DIMENSIONS, // FIND (cheap breadth): wide net, low cost, tolerant of false positives d => agent(d.prompt, { label: `find:${d.key}`, phase: 'Find', model: 'haiku', effort: 'low', // <-- cheap tier schema: FINDINGS_SCHEMA, }), // VERIFY (expensive judgment): adversarially refute each candidate review => parallel(review.findings.map(f => () => agent(`Adversarially verify — try to REFUTE: ${f.title}`, { label: `verify:${f.file}`, phase: 'Verify', model: 'opus', effort: 'high', // <-- strong tier schema: VERDICT_SCHEMA, }).then(v => ({ ...f, verdict: v })) )))const confirmed = results.flat().filter(Boolean).filter(f => f.verdict?.isReal)The same shape applies to judge panels (cheap candidate generation → strong scoring), loop-until-dry discovery (cheap finders → strong dedup/synthesis), and multi-modal sweeps (cheap per-modality search → strong completeness critic). Keep the breadth cheap; keep the gate strong.
10. Skill Glossary
Section titled “10. Skill Glossary”Moved to reference — see Glossary.
11. Agent Glossary
Section titled “11. Agent Glossary”Moved to reference — see Glossary.
12. Slash Command Glossary
Section titled “12. Slash Command Glossary”Moved to reference — see Glossary.
13. Shell Command Glossary
Section titled “13. Shell Command Glossary”Moved to reference — see Glossary.
14. Hooks Reference
Section titled “14. Hooks Reference”Moved to reference — see Hooks Reference.
15. Files & Layout
Section titled “15. Files & Layout”15.1 BOB_SOURCE structure
Section titled “15.1 BOB_SOURCE structure”BOB_SOURCE/├── skills/ # Universal skills (auto-loaded)├── commands/ # Universal slash commands├── agents/ # Universal subagents├── runbooks/ # Universal runbooks├── registry/│ ├── skills/ # Opt-in skills│ ├── commands/ # Opt-in commands│ ├── agents/ # Opt-in agents│ └── runbooks/ # Opt-in runbooks├── provisions/ # Per-project manifests (one JSON per project)├── machines.json # Machine fleet manifest (the BoB machine layer — not the Fleet project, ~/projects/fleet)├── hooks/ # All hooks; wired in settings.json├── scripts/ # CLI scripts, helpers, engines├── bin/ # PATH-friendly entrypoints (cdi, cdb, cdp, cdl, cdg, cds, cdprov, warp, …)├── templates/ # Copied (not symlinked) into projects├── schemas/ # JSON Schema for manifest, projects, settings├── profiles/ # Permission profiles per automation level├── docs/ # Guides (this file lives here)├── tests/ # `make test` targets├── claude-init.sh # DEPRECATED passthrough → bin/cdi (one release, #1193)├── claude-dashboard.sh # DEPRECATED passthrough → bin/cdb├── claude-promote.sh # DEPRECATED passthrough → bin/cdp├── claude-link.sh # DEPRECATED passthrough → bin/cdl├── settings.json # Default settings shipped to BOB_HOME├── CLAUDE.md # Default project instructions├── README.md├── Makefile└── bob-identity.md15.2 Project-level structure
Section titled “15.2 Project-level structure”A typical project has:
my-project/├── .claude/ # Created by cdi — symlinks into BOB_HOME│ ├── commands/ # Symlinks│ ├── agents/ # Symlinks│ ├── settings.json # Project-specific overrides (optional)│ └── settings.local.json # Machine-specific, gitignored├── CLAUDE.md # Project instructions (committed)├── CLAUDE.local.md # Local notes (gitignored)├── docs/│ └── vision.md # The living vision doc├── dev.json # Dev environment manifest├── seed/ # Seed scripts + users.json└── ...15.3 Forbidden patterns
Section titled “15.3 Forbidden patterns”Do not create these — they predate the GitHub Issues migration:
.claude/project-management/ ✗.claude/requirements/ ✗.claude/journal/decisions/ ✗ (use /journal decision).claude/journal/lessons/ ✗ (use /journal lesson).claude/journal/sessions/ ✗ (use /journal session)REQ-*.md SOL-*.md BUG-*.md ✗RISK-*.md INC-*.md CR-*.md ✗UXR-*.md STATUS-*.md ✗REQUIREMENTS-INDEX.md ✗SOLUTIONS-INDEX.md ✗backlog.md ✗traceability-matrix.md ✗Derived state is not a forbidden pattern. The rule above bans local PM
files — human-authored work records that compete with GitHub Issues as a source
of truth. It does not ban tooling-written caches. .claude/forge/ (the #1749
issue read cache) sits under .claude/ and persists across sessions, but it
fails every test that makes a PM file a problem: no human writes it, nothing
reads it as a source of truth, it is gitignored through the cdprov managed
ignore block, and deleting the whole directory costs a re-fetch and nothing
else. The distinction to apply is authored vs. derived, not where the file
lives or how long it survives.
15.4 BOB_HOME (~/.claude/) — runtime only
Section titled “15.4 BOB_HOME (~/.claude/) — runtime only”Synced from BOB_SOURCE by deploy.sh. Don’t edit directly. Files protected from overwrite:
~/.claude/settings.json~/.claude/settings.local.json~/.claude/CLAUDE.local.md
15.5 Documentation filename conventions (docs/)
Section titled “15.5 Documentation filename conventions (docs/)”Files under docs/ follow one mechanical naming rule so the directory greps and globs predictably, the docs site renders stable URLs, and contributors (and warp-drive) never have to guess casing.
| Rule | Decision |
|---|---|
| Case | lowercase only |
| Word separator | - (kebab-case) |
| Character set | [a-z0-9-] only — no underscores, spaces, accents, em-dashes |
| Extension | .md for prose, .json for data. .txt / .markdown are not allowed |
| Date stamps | ISO 8601 suffix: <topic>-YYYY-MM-DD.md (e.g. doc-audit-2026-05-07.md) |
| Acronyms | lowercased — pm-cheatsheet, iac-audit, bob-identity |
| Subdirectories | lowercase, plural where applicable: audits/, archive/, research/ |
| Suffix patterns | *-guide.md for “explain X”, *-reference.md for reference docs |
Frontmatter title |
independent of the filename — the docs-site engine uses title for display, the slug for the URL |
Reserved names (exempt): README.md, index.md, _index.md, CHANGELOG.md, CONTRIBUTING.md, LICENSE. These follow conventional uppercase and are skipped by the linter.
Canonical regex (basename minus extension): ^[a-z0-9]+(-[a-z0-9]+)*$ with extension in {md, json}.
Why:
- Slug collapse — under flat serving
PM-CHEATSHEET.mdandpm-cheatsheet.mdcan produce the same URL (and engines like Starlight lowercase slugs); lowercasing eliminates the collision and keeps links engine-portable. - Case-insensitive filesystems — macOS APFS treats
Foo.mdandfoo.mdas the same file. A single casing rule avoids silentgit mvno-ops and cross-machine drift. (When renaming,git mv -fthrough a temp name clears the no-op hurdle.) - Greppability — one rule means callers can link and search without second-guessing casing.
Not part of the convention: numbered ordering prefixes (01-foo.md) — use the docs-site engine’s nav-ordering config (e.g. Starlight’s sidebar.order frontmatter) instead; mixing the two is the worst outcome. There is no filename-length budget — long descriptive names like project-orchestration-handbook.md are fine.
Scope & enforcement: This convention covers docs/ (excluding docs/archive/**, which is frozen historical record). It is enforced two ways:
make check-doc-naming(script:scripts/checks/check-doc-naming.sh) — wired intomake check; fails CI on any non-reserveddocs/**file outside the rule.- The doc-keeper auditor emits a
filename-violationdrift category so violations also surface in/doc-auditreports.
The same lowercase-kebab rule is applied to the code-side item directories
(agents/, registry/{skills,agents,commands}/, runbooks/, provisions/) by
the sibling linter make check-filename-conventions
(scripts/checks/check-filename-conventions.sh, #238-241), with framework-reserved
carve-outs (SKILL.md, _default.json, …). Skill bundle assets (LICENSE.txt,
*.pdf, scripts inside a skill dir) are not policed — only item/dir names are.
15.6 Test & inspection artifacts (.bob-artifacts/)
Section titled “15.6 Test & inspection artifacts (.bob-artifacts/)”Ephemeral test/inspection output — Playwright screenshots, DOM snapshots, scratch
captures — goes in a single per-project directory: .bob-artifacts/. Never the
repo root or scattered /tmp paths (#253).
cdiidempotently adds.bob-artifacts/and.playwright-mcp/(the Playwright MCP default output dir) to the project’s root.gitignore, so these artifacts never get accidentally committed.- The
webapp-testingskill writes screenshots there;verify/runflows should follow the same convention. - Backfill an existing project (or clean stray root screenshots) with
scripts/clean-artifacts.sh <project>— idempotent: it relocates loose root-level screenshots into.bob-artifacts/and ensures the.gitignoreentries.
16. Troubleshooting
Section titled “16. Troubleshooting”16.1 Diagnostic commands
Section titled “16.1 Diagnostic commands”| Symptom | Run this |
|---|---|
| “Is BoB healthy?” | make check |
| “Are my project’s symlinks ok?” | cdb then cdb --check <project> |
| “What is provisioned in this project?” | cdprov --status |
| “Does the manifest match reality?” | cdprov --diff |
| “Why is warp-drive stuck?” | warp status |
| “Is the dev env healthy?” | bin/dev-health or /dev-up --check |
| “Hook script missing?” | make check-hooks |
| “JSON config invalid?” | make check-schemas |
| “Stale state across the system?” | /rebob |
| “Lessons accumulating? Patterns?” | /trace-mining |
16.2 State files
Section titled “16.2 State files”| File | Owner | Notes |
|---|---|---|
<project>/.warp-drive-state.json |
warp-drive engine | Current chunk, phase, history |
<project>/.autoloop-state.json |
(legacy) auto-loop | Will be removed |
~/.claude/settings.local.json |
machine | _rdb.enabled lives here |
16.3 Common problems
Section titled “16.3 Common problems”| Problem | Fix |
|---|---|
| Hooks aren’t firing | make check-hooks; verify ~/.claude/settings.json references existing scripts |
| Symlinks point to nowhere | cdprov --refresh (re-link) or cdprov --prune (drop dangling/cross-machine; dry-run first); fleet-wide: make repair-fleet then make check-fleet (#303) |
| Warp-drive won’t start | Check warp status; ensure at least one req + approved issue exists |
| Telegram silent (RDB on) | Check ~/.claude/settings.local.json for _rdb.enabled: true; type something in terminal |
/auto-loop did weird things |
Stop using /auto-loop — switch to /warp-drive |
Got prompt to create REQ-NNNN.md |
Stop. That’s a regression. File a bug — work tracking is GitHub Issues |
| Tests stuck in fix loop | Warp-drive 3-strikes-out automatically; let it escalate |
cdfork worktree won’t drop |
cdfork drop <branch> --force; if that fails, manually git worktree prune |
| Deploy refuses to overwrite something | That’s by design — settings.json, settings.local.json, CLAUDE.local.md are protected |
17. Automated Versioning
Section titled “17. Automated Versioning”Every BoB-managed project can be automatically versioned — conventional-commit-driven
semver bumps, git tags, CHANGELOG.md, and GitHub Releases — provisioned from one
source of truth rather than hand-copied CI (capability #275).
Tool: commit-and-tag-version
(maintained fork of standard-version). release-please was considered and deferred
(see the versioning guide).
Enable it — versioning is an opt-in manifest capability (like gh_project); it
copies template files rather than symlinking:
// provisions/<project>.json"versioning": { "enabled": true, "main_branch": "main" }cdprov refresh # copies auto-release.yml + .versionrc.json + commitlint config, # merges package.json (release script + devDependency)Local fallback (#1691): when Actions is unavailable, bob-release cuts the
identical release from a workstation, and the
local delivery runbook covers the full
no-Actions path (CI gate, release cut, docs deploy).
The orchestrator recommends versioning by default for every project and picks a
push trigger-path filter per project type (docroot/** for cms, the full
source surface for framework, src/**+package.json for apps/libraries,
no filter for research/mixed). An existing versioning block is always
preserved on re-run, so enabled:false opt-outs and customizations survive.
A merge to the main branch then bumps the version, rewrites the changelog, tags
vX.Y.Z, and publishes a GitHub Release — loop-guarded against the
chore(release) commit. Conventional-commit enforcement ships as config (#278).
Heads-up: the workflow pushes the release commit/tag directly to the main branch via
GITHUB_TOKEN; enabling branch protection without a bot exception breaks it. Full detail, bump rules, and the per-type path table: versioning.md.
18. See Also
Section titled “18. See Also”- bob-identity.md — What “BoB” means
- prime-directive.md — IaC principles, approved patterns, anti-patterns
- warp-drive.md — Full warp-drive deep-dive
- cdfork.md — Full cdfork deep-dive
- dev-lifecycle.md — Full dev env lifecycle reference (also covers the docs-site lifecycle, #153)
- orchestrator.md — Registry metadata contract, recommendation engine, interview question bank (#157 family)
- gh-projects.md — GitHub Projects v2 integration: the
cdprojCLI, profiles, canonical fields, manifest opt-in, warp-drive/command integration, backfill (#210 family) - versioning.md — Automated semantic versioning:
commit-and-tag-version, theversioningmanifest block, per-project-type trigger paths, provisioning, branch-protection caveat (#275 family) - vision.md — BoB’s own vision document
- project-management-reference.md — Under-the-hood architecture (note: predates GitHub Issues migration; due for its own audit)
- branch-config.md — Branch detection / configuration
- automation-behavior.md — How automation levels behave
- backup-recovery.md — Backup & recovery
- new-machine-setup.md — Setup on a fresh machine
- verification-system.md —
make checkdeep-dive - token-monitoring.md — Token usage observability
- pm-conventions.md — PM conventions (redirect stub; original archived 2026-05-07)
- doc-audit-2026-05-07.md — Documentation audit methodology and gap list (use as a template for the next reconciliation cycle)
- archive/ — Frozen historical PM docs (
2026-02-pm/,2026-05-pm/). Each archive subdir is dated and immutable; superseded docs leave a redirect stub at the original path. Excluded from the published docs site; browse on GitHub.
Last updated: 2026-05-07 — reconciled with doc audit (#144). Changelog: disambiguated cdr (Claude Disaster Recovery, not legacy reqs-index) in §13.1; surfaced live test counts and the doc-link sweep script in §3.7; added doc audit and archive subtree to §17 See Also.