Appearance
BoB — Big ol' Brain
BoB (Big ol' Brain) is the name for this global Claude Code tooling framework. See bob-identity.md for the full identity definition.
The source repo (BOB_SOURCE, default ~/projects/bigbrain) is the single source of truth for all BoB tooling. It is deployed to ~/.claude/ (BOB_HOME) where Claude Code reads it at runtime.
For the encyclopedic guide, see docs/project-orchestration-handbook.md. The docs are organized by Diátaxis mode — start at docs/index.md.
Architecture
BOB_SOURCE (~/projects/bigbrain) <- Source of truth (git repo)
├── skills/ <- <!-- count:skills -->9<!-- /count --> universal skills (auto-loaded globally — the #1132 minimal core)
├── commands/ <- <!-- count:commands -->18<!-- /count --> universal commands (symlinked into projects by cdi)
├── agents/ <- <!-- count:agents -->3<!-- /count --> universal agents (symlinked into projects by cdi)
├── runbooks/ <- <!-- count:runbooks -->6<!-- /count --> universal runbooks (procedural ops guides)
├── hooks/ <- <!-- count:hooks -->29<!-- /count --> hook scripts; <!-- count:hooks-wired -->24<!-- /count --> wired by settings.json — PreToolUse/PostToolUse guards run by dispatch.sh from dispatch.tsv + lib/ (<!-- count:hooks-unwired -->5<!-- /count --> unwired — see hooks-reference.md)
├── registry/ <- NOT auto-loaded — provisioned per-project
│ ├── skills/ <- <!-- count:registry-skills -->61<!-- /count --> domain skills (cloudflare-dev, graph-orchestration, openreplay-dev, eloqua-dev, etc.)
│ ├── commands/ <- <!-- count:registry-commands -->26<!-- /count --> PM/workflow commands (deploy, release, task, etc.)
│ ├── agents/ <- <!-- count:registry-agents -->23<!-- /count --> specialist agents (code-reviewer, qa-strategist, etc.)
│ └── runbooks/ <- <!-- count:registry-runbooks -->12<!-- /count --> domain-specific runbooks
├── provisions/ <- The product's two global manifests (_bob-home.json, _default.json); per-project manifests, per-machine/role globals and ports.json are operator data in the operator repo (#2270)
├── machines.json <- LEGACY location of the machine fleet manifest until 2026-10-31 — operator data that lives in the operator repo (#2270; resolver: scripts/fleet/machine.js through operatorPath) — not to be confused with the **Fleet** project (`~/projects/fleet`), the host-environment layer beneath BoB
├── secrets-inventory.json <- The credential inventory (#2237): every credential BoB and its projects depend on — names, locations as references, consumers, scope, rotation — never a value; `make check-secrets-inventory` refuses secret-shaped strings, `scripts/security/cred-check.js` reports where the stores differ from it by name (#2238), `cred-check.js due` files one deduplicated `todo` per rotation due within P14D and `rotated <id>` records `last_rotated` on a branch for the PR (#2239) (docs/reference/secrets-inventory.md)
├── scripts/ <- CLI scripts, helpers, state-machine engine, dev-lifecycle, fleet/, cli/ (the legacy engines — cdi, cdb, cdprov, warp, dev-up, … — reached as bob nouns, #2060); scripts/lib/retired-items.json is the declared retirement record every archive, demotion or promotion must update — `make check-retired-items` refuses a vanished item without one, and `bob provision refresh` / `bob ready` retarget or prune consuming projects' links from it (#2314, docs/reference/retired-items.md)
├── bin/ <- bob (the CLI router, #2044) + gh (forge-health PATH shim) + bob-shell.zsh (sourceable shell integration) + generated alias shims for every legacy name (cdi, cdb, cdprov, warp, dev-up, …: `scripts/bob/gen-shims.sh` renders them from the routing table; each prints its bob spelling once per shell and execs the route; they retire 2026-09-21 — #2060)
├── templates/ <- dev.json, seed/, hooks/ — copied (not symlinked) to projects
├── schemas/ <- JSON Schema for manifest, projects, settings
├── profiles/ <- Permission profiles per automation level (supervised, trusted, autonomous)
├── docs/ <- Guides (handbook lives here)
├── wiki/ <- The skill-evolution wiki (#2388): patterns/<slug>.md pages (schemas/wiki-page.schema.json), a derived index.md (scripts/wiki/index.js), logs.md and skill-impact.md; linted by `make check-wiki` (scripts/wiki/lint.js — tenant + credential rules, never referenced by a hook, command or manifest), pruned by scripts/wiki/retire.js; read only by the skill proposer, never deployed (docs/reference/wiki.md)
├── Makefile <- IaC verification entry point (make check / test / ci)
└── tests/ <- Schema validator + state-machine + integration tests
BOB_HOME (~/.claude/) <- Runtime directory (deployed)
├── skills/, scripts/, hooks/, etc. <- Synced from BOB_SOURCE by deploy.sh
├── settings.json <- Machine-specific (protected by deploy; its .hooks block is reconciled from source, #1918)
├── settings.local.json <- Machine-specific (protected; holds _automation, _rdb)
└── CLAUDE.md <- Deployed copy (mirrored from source by deploy.sh, #1911)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. See prime-directive.md.
Evidence and merit (Prime Directive, principle 8). Everything BoB concludes or produces — research, analysis, recommendations, briefings, client deliverables — rests on empirical evidence and merit, judges outcomes over intentions, and follows second- and third-order effects. Nothing is selected or excluded by race, sex, ethnicity or any other immutable trait: no demographic quota or minimum share in sources, examples, personas, reading lists, shortlists, test data or content, unless the operator directs one, and then it is recorded as the operator's criterion. Comparing groups by those traits is valid inquiry: name confounders, base rates, measurement validity and effect size, and report the findings as the evidence shows, whatever they are. See Evidence and Merit.
| Type | Source (BOB_SOURCE) | Runtime (BOB_HOME) | Loading |
|---|---|---|---|
| Universal skills | skills/ | Synced by deploy.sh | Auto-loaded globally |
| Universal commands | commands/ | Symlinked by bob init from BOB_SOURCE | Per-project symlinks |
| Universal agents | agents/ | Symlinked by bob init from BOB_SOURCE | Per-project symlinks |
| Universal runbooks | runbooks/ | Synced by deploy.sh | Referenced by absolute path; published as its own docs-site section |
| Registry items | registry/ | Synced by deploy.sh | Provisioned per-project via manifest |
| Hooks | hooks/ | Synced by deploy.sh | Referenced in settings.json |
| Scripts | scripts/ | Synced by deploy.sh | Referenced by absolute path |
| Templates | templates/ | Synced by deploy.sh | Copied to projects, then customized |
Authoring a new skill, command, or agent? See the Authoring a Skill how-to — it covers choosing the right surface (skill vs command vs agent), registering it, provisioning it (
bob provision//provision), promoting it (bob promote), and the pre-add validation checklist. Build mechanics live in theskill-creatorandagent-builderskills.
Provisioning: Each project has a manifest, provisions/<project>.json, declaring which registry items it needs. Manifests are operator data (#2270, decision #2266): they live in the operator repo's provisions/, alongside the per-machine and per-role _bob-home.*.json, ports.json, machines.json, mirrors.json, projects.json (the derived project registry, #3432), config/cloudflare-accounts.json and apps/intake/config/; BOB_SOURCE keeps only the product's _bob-home.json and _default.json. Every consumer reaches them through operatorPath(<name>) in scripts/lib/operator-resolve.js (bob_operator_path in the shell twin): the operator repo first, else the legacy BOB_SOURCE location with a once-per-process deprecation warning until 2026-10-31; check-abs-paths fails on a direct path. Run bob provision or /provision to manage. Universal items are always present; registry items are only loaded when provisioned. A manifest also declares this project's _workflow levers (#2703): WORKFLOW_DEFAULTS < provisions/_default.json _workflow < provisions/<project>.json _workflow < .claude/settings.local.json _workflow, every manifest read fail-open, the winning layer reported as source by config get/config list --sources/bob warp config, and the allowed key list generated into schemas/manifest.schema.json from the table (docs/reference/workflow-config.md). The IWPI files move with history via bob operator migrate --apply once the human has created and linked the operator repo (the todo #2270 filed).
BOB_SOURCE resolution (#1220). The provisioning engine (scripts/orchestrator/provision.js, #2084) resolves the source repo in the order scripts/lib/bob-source-resolve.sh defines: explicit $BOB_SOURCE env → the script's own checkout (self-relative, requires .git + provisions/) → the checked default ~/projects/bigbrain → a flagged BOB_HOME fallback. The resolved source is where the product's registry, scripts and the two global manifests are read from; project manifests resolve through the operator resolver (above), and manifest writes (--init, --init --auto, add/remove apply, interview) land in the operator repo — refused with an explicit error when no operator resolved (#2270), as they were refused when only the BOB_HOME fallback resolved (#1220): declared state never silently lands in the product repo or the deploy target. The #785 staged route's _cb_root resolves the operator repo the same way; its BOB_HOME last resort is still refused by the #787 carve-out.
Operator resolution (#2267). An operator's identity, fleet, manifests and routes live in a separate per-operator git repo, never in BOB_SOURCE (the product) or BOB_HOME (the deploy target) — decision #2266. Its root file operator.json (schemas/operator.schema.json: id, label, a forge-neutral forge, people[] with a closed roles set, routes.decisions / routes.notifications, bob_upstream, and secrets[] as names and sources only — no value field, extra keys refused) is found through one resolver, scripts/lib/operator-resolve.js (sourceable twin operator-resolve.sh), in the order $BOB_OPERATOR → the $BOB_HOME/.bob-operator pointer → ~/projects/bob-operator → unresolved, never a fallback into BOB_SOURCE or BOB_HOME. bob operator show prints the resolved profile and the winning rung (bob-operator/1; exit 0 / 3 unresolved / 2 invalid), bob operator init <dir> --link scaffolds a repo from templates/operator/ and writes the pointer, bob operator migrate [--apply] moves the operator data out of a BoB checkout into it with history (#2270), scripts/checks/check-operator.js is the validator an operator repo's CI runs, and bob doctor reports the operator as an ungraded line. See operator.md.
Single-item fast path (#1478). A single bob provision add/remove <type> <name> of a registry item is mechanical and schema-validated, so at automation Level 2+ the staged review PR is auto-merged instead of waiting on a human: the PR is labelled config-fastpath (audit trail preserved), squash-merged, and the apply steps are chained — the operator repo fast-forwards and bob provision refresh projects the links (no deploy step: the manifest is read from the operator checkout itself, #2270) — so a switch flip feels like one. Guards: the PR must change exactly provisions/<project>.json with a delta of exactly one item across the four item arrays, schema-valid on both sides; any miss (including #791-accumulated multi-item PRs) leaves the PR open for normal review. Level 1 keeps today's PR-for-human flow, with one exception: a project's first manifest from bob provision --init --auto (what bob ready runs) is a new file of one known shape, so it is committed straight to the operator repo's default branch at every level, and any config/<project>-* branch it supersedes is finished rather than left behind (#3158). BOB_FASTPATH=0 opts out. A failed chained step names itself (MERGE / PULL / REFRESH) and prints the exact resume command — a landed merge is never rolled back. Every manifest write — the staged config branch (_cb_root in scripts/lib/config-branch.sh), the fast path, --now and the ungated route — lands in the operator repo; with no operator resolved it is refused with a #1220-style error naming bob operator init <dir> --link (#2270).
Canonical link target (#1475). Registry symlinks are created and compared against LINK_ROOT = ${BOB_HOME:-~/.claude}/registry — the deployed registry — never the source checkout, so bob provision refresh converges to identical link targets regardless of how BOB_SOURCE resolved for the invocation (decision #1484). Only manifest reads/writes ride the #1220 resolution above. An item merged to source but not yet deployed reports "run deploy.sh" rather than linking into the checkout, and any retarget of existing links is reported explicitly ([retarget] per item + summary count), never silent.
Authored vs. derived in consuming repos (#1479). Commit what is authored (CLAUDE.md, dev.json, seed/, .claude/settings.json, real-file tooling); gitignore what is provisioned. bob provision maintains a managed ignore block listing every manifest-derived symlink path, every .claude/* symlink that resolves into BOB_HOME (the universal links bob init creates), and the BoB session-state files declared once in scripts/lib/derived-paths.json (overlay state, last hook payload, warp-drive state, project-ready verdict, forge cache, settings.local.json — #1969), rebuilt in place on each provisioning write (appended only when absent, so a block or line below it stays below — #3600), byte-ordered (LC_ALL=C sort) so the block is identical on every fleet OS — an unpinned sort followed glibc's punctuation-blind en_US.UTF-8 collation on farm-01 and left laptops permanently dirty (#1976). Placement via _meta.ignore_placement (.gitignore default, .git/info/exclude for no-fingerprint repos); _meta.track_provisioned: true opts out. Drift detection lives in bob provision status ([unlinked]) and the fleet snapshot (unlinked_declared); fresh clones materialize links via bob init + bob provision refresh.
Global-aware provisioning (#412). Project provisioning reads the current machine's resolved global manifest (#414) and is aware of what BOB_HOME already provides: an item that is already global is de-duped (not double-provisioned into the project), and a registry item whose name collides with a global item but diverges in definition is reported as a conflict with a resolution hint (it is still linked so the project isn't silently broken). bob provision status / bob provision --diff report the global-vs-project overlap.
BOB_HOME as a declared surface (#411). ~/.claude (BOB_HOME) is itself a declared, version-controlled surface: provisions/_bob-home.json lists its global skillset. bob provision recognises BOB_HOME via scripts/lib/bob-home-detect.sh and routes to a read-only verify path (reports declared-vs-present items) — it never symlinks registry items into a nested .claude/, never inverts deployed copies, and never touches the protected machine-specific files (settings.json, settings.local.json, CLAUDE.local.md). BOB_HOME stays a deploy target; deploy.sh remains the sync mechanism.
Cross-machine audit (#415). scripts/fleet/audit.js audits any project on any fleet machine from any other, over Tailscale. Two transports: audit.js snapshot publishes this machine's project/provision/health condition to $BOB_FLEET_SNAPSHOTS (default ~/.claude/fleet/snapshots/<id>.json; point it at a synced folder to share); audit.js pull <id|tailscale-name> runs the audit on a remote machine over Tailscale SSH (default the machine-readable snapshot, --cmd to run cdb/rebob/cdprov remotely). audit.js view aggregates live + last-known-cached state across the fleet, marking a machine stale when its newest snapshot is older than 24 h and never when it has never published one; audit.js snapshot --install-timer keeps each machine publishing on a launchd agent (macOS) or systemd user timer (Linux), and bob doctor reports the timer as an INFO line (fleet-snapshot-timer.md, #2067). Each snapshot also carries per-project harness_defects: {open, recurrences, unreported, fingerprints[], issues[]} read from the project's warp-drive state file (no forge call), rolled up per machine in summary.harness_defects and fleet-wide by view as a "Harness defects" table — distinct fingerprints, recurrences, unreported count, affected projects (#1838). Each present project also carries advisories — what its last dependency audit on that machine left open at the declared severity threshold ({open, critical, high, moderate, low, unknown, threshold, audited_at, run, issues[], unverified}, read from .claude/.dependency-advisories.json, the state file ritual.js prepare writes on a dependency-audit run; null = never audited here; no forge call), rolled up per machine in summary.advisories (with by_project) and fleet-wide by view as a "Dependency advisories" table that counts each project once by its newest audit — the field the briefing collector (#2243) reads; rendering it in the briefing is #2224's (#2242). Each present project also carries token_ledger (#4421) — the transcripts of its checkouts still without a token-ledger session record after the snapshot ran bob labor reconcile --apply first ({unrecorded, settling, recorded, written, transcripts, since, fix}; BOB_FLEET_LABOR_RECONCILE=0 skips the pass, as the test sandboxes do), rolled up per machine in summary.token_ledger with projects_with_gaps; bob status --scope project grades the same count and bob doctor prints it as one line, both read in quick mode (docs/reference/token-monitoring.md). Each present project also carries labor and cogs (#4422) — its agent hours over the last 30 days read through bob labor report ({hours_30d, by_day[], gap_minutes, unrecorded, since, until}) and its list-price COGS read through the token ledger priced by token-report.js ({usd_30d, qualified, records}), never recomputed; a failed read is null with labor_reason / cogs_reason beside it and the snapshot still succeeds, BOB_FLEET_LABOR_PROBE=0 skips both and summary.labor says so, summary.labor.by_project is the map view joins across machines as a "Labor" table (hours per machine, the sum — hours add across machines — COGS, unrecorded; ? never 0 for a machine without a read, * partial over a ? cell or a stale snapshot) and bob labor report --fleet [--project <name>] prints the same roll-up from the cached snapshots alone, each machine fresh / stale / never and the total labelled partial whenever one is stale or missing (docs/reference/work-attribution.md). Each present project also carries backups (#3086) — the freshness of the D1 backup its dev.json declares, read through bob backup status --json by scripts/backup/freshness.js ({declared, verdict, stale, newest_at, age_hours, size, due_at, reason, source, envs}; null when none is declared; an unreadable bucket is verdict unknown with stale: null, never fresh; BOB_FLEET_BACKUP_PROBE=0 skips the bucket read), rolled up per machine in summary.backups (declared / fresh / stale / unknown with names) and by view as a "Backups" table; bob status --scope project grades the same record, and bob tick / bob brief --send send one bob notify per missed run through scripts/backup/notice.js, deduplicated in $BOB_HOME/.backup-notices.json. Each snapshot also carries summary.actions_budget (#1695): the GitHub Actions minutes each distinct owner of this machine's projects has used against the included quota, read by scripts/fleet/actions-budget.js through a 6-hour per-owner cache and graded ok / warn (≥ BOB_ACTIONS_WARN_PCT, default 80) / over / unknown / unavailable. A token without the user scope is unavailable with its fix, never ok. BOB_FLEET_ACTIONS_PROBE=0 opts out. view shows it as an "Actions budget" table and /what-next --report prints it under the health line (actions-budget.md). Each present project also carries ci (#1728) — its declared CI locus resolved from provisions/<project>.json _meta.ci ({locus: farm | hosted, runner_label?}, the one declared home, validated by the manifest schema) → provisions/_default.json → hosted reported undeclared, plus drift against the repo variable CI_RUNNER_LABEL when the forge is operational ({locus, label, source, declared, verdict, drift, actual_variable, private, degraded, hazard, measured, fix}; one gh read per declared project, BOB_FLEET_CI_PROBE=0 opts out, an undeclared project is never probed), rolled up in summary.ci and fleet-wide by view as a "CI locus" table; scripts/fleet/ci-locus.js is the one resolver (resolve / status / reconcile --apply through runner.sh target, refusing a public repo to farm), bob provision status prints the same verdict per project (ci-locus.md); each measured record also carries ci.queue, the queued-job alarm (#1731: a run parked past _meta.ci.queue_alarm_minutes with its kind — offline / idle-runner / hosted alarm, busy never), rolled up as summary.ci.queue_alarm and a QUEUED column in the view. ci-locus.js preflight is the runner-availability preflight the state machine fires on entry to merging: will the next job run? — an online runner behind the effective target, else a fallback to hosted within one job (_meta.ci.fallback, restored once the runner is back), or ONE no-viable-runner report when hosted is dead too; recorded on state.ci_preflight, shown by bob warp status as CI runner:. Optional host layer (#1871, #1925): when this machine's machines.json entry declares host_provider: {id, command} (Fleet on the laptops, thefarm on farm-01 — declared, never discovered from PATH; $BOB_FLEET_HOST_PROVIDER overrides, empty opts out), the snapshot runs <command> status --json and consumes the result by contract — host-status/1 (schemas/host-status.schema.json, owned by bigbrain, deployed to BOB_HOME/schemas/, validated by producers with scripts/fleet/host-status-validate.js; fleet-status/1 is a profile of it). Provider-defined sections land in host.sections summarised to {ok, drift} (na: true = not measured), Fleet's five stay addressable as host.<name>, and view rolls them up as a "Host drift" table with columns from the union of reported sections plus PROVIDER. No provider means no host key; a declared provider that is missing / fails / times out (BOB_FLEET_HOST_TIMEOUT_MS, default 20000) is host.error, never a failed snapshot, and summary.host_drift (#1921) is carried either way. The document wins over the exit code (exit 3 = drift with a full document; verdict: error surfaces its error). Ownership seam: bob-identity.md. Prerequisite for inbound audits: a machine must accept SSH — macOS has Remote Login off by default, so farm→Mac pulls require enabling it on the Mac.
Dev/test/deploy readiness (#446). Each snapshot also grades every managed project's promotion readiness via scripts/fleet/readiness.js — a static, side-effect-free probe that reads declared config (dev.json, package.json, wrangler.toml, CI workflows, provisioned skills) and never executes bob dev up or a deploy (an audit must stay cheap). For each project it reports the detected stack, the deploy path (cf / external / none), the current implicit promotion behavior (ci-on-push / manual / none), and grades the dev, test, and deploy stages as working / partial / broken / n-a (n-a — legitimately absent, e.g. a CLI repo has no dev server — is distinct from broken). audit.js view rolls these up per-machine and fleet-wide (working/applicable, broken flagged) in both the human table and --json.
Per-machine resolution (#414). The BOB_HOME verify path identifies the current machine via scripts/fleet/machine.js whoami (#413's resolver) and selects the most-specific global manifest — _bob-home.<machine-id>.json → _bob-home.<role>.json → _bob-home.json (base). bob provision --diff against ~/.claude shows the convergence delta for this machine (would-add for declared-but-missing, would-remove for present-but-undeclared); it is deterministic and idempotent, and deploy.sh applies the actual sync.
Orchestrator (#157 family). Registry items declare orchestrator metadata in their frontmatter (applies_to.stacks, applies_to.project_types, recommended_for, category, default, required_with). The recommendation engine at scripts/orchestrator/recommend.js reads that metadata and produces a manifest from (stack, project type, capabilities) — supplied by the interview, or inferred from docs/vision.md + README + code by infer-profile.js (#1877/#1878; precedence: interview > code > vision for stack/type, the vision contributes capabilities; every field carries file:line evidence). explain.js gives each recommended item a why: line and flags unsupported manifest items as would-remove — report-only (#1879); bob provision reconcile applies the delta through the staged-PR / fast-path route (#1880); /vision and /groom nudge when the vision implies an unprovisioned item (#1881). When the user asks "what should I provision?" or "is X applicable to this project?" — that's the orchestrator's question. Full reference: docs/orchestrator-guide.md, consolidated in its Inference from vision and code section (#1873).
Deploying: Run scripts/deploy.sh to sync BOB_SOURCE → BOB_HOME. Use --dry-run to preview, --first-run on initial setup. provisions/, machines.json and projects.json are not synced (#2270, #3432): they are operator data read from the operator repo through scripts/lib/operator-resolve.js, so the deploy retires any copy an earlier deploy left in BOB_HOME ([retired]) and bob doctor, whose surface list is derived from deploy.sh, no longer grades them. Machine-specific files (settings.json, settings.hooks-local.json, settings.local.json, CLAUDE.local.md) are never overwritten. The one reconciled section inside a protected file is settings.json's .hooks block (#1918): source-declared hook wiring converges on every deploy via scripts/fleet/settings-hooks-reconcile.js — backup first, every change named, machine-specific keys untouched, intentional local hooks declared in the sibling file settings.hooks-local.json (a file Claude Code never reads — an in-file key naming PreToolUse/PermissionRequest makes the loader reject all of settings.json, #1998; a legacy _hooks_local key is migrated there on the next apply), an undeclared runtime hook adopted there mechanically with settings-hooks-reconcile.js adopt <script> without touching settings.json (#1913), undeclared hooks reported but never pruned. deploy.sh and apply refuse while a Claude Code session is live on this machine — a rewrite of settings.json silences every hook in sessions already running until they restart, so both exit 4 (live-session) naming each session's project and PID, writing nothing (#2040 reversed #1997's warn-only); --force-live / BOB_DEPLOY_FORCE_LIVE=1 is the one override and prints the #1997 NOTE: N running Claude Code session(s) …; settings-hooks-reconcile.js live-check is the same guard as a command, shared by bob update / bob tick. Every non-dry deploy ends by writing $BOB_HOME/.bob-version (bob-version/1: version, channel stable/nightly/checkout, tag, source_sha, source_dirty, deployed_at) — the record of which source this machine runs; bob doctor prints it as its version line and bob-host-drift/1 carries it (#2046). Host drift is measured against that commit, not the source working tree (#3160): bob doctor compares BOB_HOME with <source_sha>:<path>, so a dirty tree, a feature branch or new commits in BOB_SOURCE are never drift; how far the checkout has moved is the behind key, reported as information, and each finding carries a severity (hard = BOB_HOME unusable, else advisory). See hooks-reference.md.
CLI
Every command is one row in scripts/bob/commands.json and reachable as bob <noun> [<verb>]; the legacy name in the Was column is a generated alias shim that execs the same route (#2060, retiring 2026-09-21). This table is generated from that routing table — flags, exit codes and the engine per command are in docs/reference/cli.md, or bob <noun> --help.
| Command | Was | Purpose |
|---|---|---|
bob doctor | — | Assert this machine's BOB_HOME is what deploy.sh produced from the commit it records, across every synced surface, every symlink, the toolchain and the settings.json hook wiring; how far BOB_SOURCE has moved since the deploy is reported,… |
bob ready | bob-ready | The one command for starting or resuming a project: takes a new or existing directory, or an owner/repo it clones, to warp-ready and visible on bob viz — idempotent, no prompts, never blocked by other running projects: repo → host → link… |
bob preflight | — | The warp-drive preflight gate: doctor + ready --dry-run as one verdict. |
bob status | — | The front door: with no arguments, one line per scope — host, the project you are in, the fleet — from cached verdicts with no network, the warp run going here, and ONE next: command; with --scope, one health verdict for host (≡ doctor),… |
bob update | — | Converge this machine to a release channel or tag: fetch, fast-forward the BOB_SOURCE checkout to the newest stable/nightly tag (or --to), run deploy.sh, then doctor — refusing a dirty tree or a live Claude Code session. |
bob notify | — | Send a Telegram message over the remote-decision bridge from a shell: reads BRIDGE_WORKER_URL/BRIDGE_SECRET from the environment, the project's .mcp.json, or $BOB_HOME/.mcp.json, POSTs /notify, and prints the message instead when no brid… |
bob groom | — | Run the /groom pass headless from a shell: claude -p over the inlined /groom command text and scope — an explicit DRY-RUN by default, --apply at automation Level 3 (read-then-write logs and applies). |
bob tick | — | One unattended maintenance cycle under a lock: bob groom --all --apply → bob plan build --from farm → the host converged on BOB_SOURCE when it is behind source and safe to deploy (deploy.sh + bob doctor, #2472) → one bob warp start run,… |
bob tour | — | What is provisioned here but never invoked, from session-ledger evidence: each item with one line on when to reach for it and the command to try, graded unknown / parked / in-use — never useless. |
bob labor report | — | Agent active-hours from the Claude Code transcripts on this machine, one clock per project (#4420, cap #3135): every session transcript under ~/.claude/projects/<slug>/ (a Task subagent's transcript under <session>/subagents/ folded into… |
bob labor reconcile | — | Every transcript on this machine has one priced record in the token ledger (#4421, cap #3135): each session transcript under ~/.claude/projects/<slug>/ written since the window opened (--since, default 30 days) is classified against the… |
bob u | — | Bob University (BobU) from a shell — a thin router over the disposition record (#2901), applicability and fleet usage (#2900, #2899) and the eval suites (#2390): status counts each disposition, lists every undisposed item with its applic… |
bob wiki maintain | — | Consolidate one project's traces into the skill-evolution wiki (#2389, part of #2387): a scrubbed, bounded packet of everything new since the watermark — loop notes, the decision log, harness defects, ledger no-trigger and improvised rec… |
bob wiki propose | — | Propose one change to one skill, command or agent from the skill-evolution wiki (#2391, part of #2387): the item named, or with --auto the one with the most new wiki evidence among items with an eval suite, handed to one headless Read-on… |
bob eval run | — | Run one item's eval suite (#2390, part of #2387): evals/<item>/ in BOB_SOURCE through claude plugin eval with a with/without ablation, from the current directory, under a mandatory cost ceiling (--max-cost-usd, else _workflow.eval_max_co… |
bob eval gate | — | The acceptance rule for a skill change (#2390): pass only when a skill-eval/1 result's with score is strictly higher than the best recorded for the same model in evals/<item>/baseline.json (with no best yet, the result's own no-item arm… |
bob asks | — | Every open ask of the operator across the portfolio, in one list (#3801, cap #3800): one line per open todo without completed and per open brief in brief:new or brief:acting, across every registered, active project, with its project, pri… |
bob brief | — | One portfolio briefing as briefing/1 (#2243, cap #2224): Needs you / Done / Doing / Will do across every project since the previous briefing, assembled from source data with links — gh (merged PRs, closed reqs and bugs, open todos, PRs a… |
bob site publish | — | Publish one site-publish/1 bundle to the operator's site — the one publisher to it (#3103, decision on #3098): a producer (bob brief, release notes) hands over a section path and its files; the publisher refuses a file outside the sectio… |
bob operator show | — | Print the resolved operator profile and which rung found it — $BOB_OPERATOR → the $BOB_HOME/.bob-operator pointer → ~/projects/bob-operator — after validating operator.json against schemas/operator.schema.json (#2267, decision #2266). |
bob operator init | — | Scaffold an operator repo from templates/operator/ — operator.json, machines.json, provisions/, README, .gitignore and the validate workflow that runs BoB's validators — git-init it, and write the $BOB_HOME/.bob-operator pointer only wit… |
bob operator migrate | — | Move the operator data out of the BoB source checkout into the resolved operator repo WITH HISTORY — machines.json, mirrors.json, config/cloudflare-accounts.json, apps/intake/config/ and every provisions/*.json except the product's _bob-… |
bob secrets check | — | Verify every secret the operator profile declares is PRESENT on this machine and name its source — env / file / keychain / gh-secret / op probed for existence and metadata only, never a value (#2286): each secret is present, missing, ins… |
bob ibob check | — | Grade the iBoB terminal kit read-only (#2291, cap #2289): the declared phone setup in the operator repo's config/ibob-kit.json — schema, every target a machines.json id, every device key in secrets-inventory.json and operator.json secret… |
bob ibob attach | — | List the live warp and cdfork tmux sessions on one fleet machine and print, for each, the exact command to type in the phone client (mosh or ssh, from the kit's declared client) to attach to it (#2291). |
bob route status | — | What this machine's model-route working key has used against its limit (#3590, cap #3866): one request to the route for the key's own state — its label, usage, limit and what is left this period — read with the working key from the envir… |
bob route mint | — | Create this machine's model-route working key with the management key (#3590): named bob-<machine id>, limited to the monthly amount the machine's machines.json entry declares (routes.<route>.monthly_limit_usd, enforced by the route), an… |
bob route roles | — | Every model role BoB declares (#1231, cap #3866) — default, reasoning, fast, cheap — with the Claude Code alias agents name for it and its candidates in the order they are tried, each a model on the subscription or a route, marking any s… |
bob route run | — | Run a headless command on a model role with failover (#1231, cap #3866): start it on the role's first candidate in play; when it exits non-zero and its output shows a Claude API failure — the usage limit, a 429, a 5xx or overloaded API,… |
bob route failovers | — | The newest model failovers on this machine (#1231): when, which role, the candidate that failed and why (its class and the matched text), what it fell back to, and the project — read from $BOB_HOME/route-failover.jsonl, which bob route r… |
bob cloudflare verify | — | Check this machine's Cloudflare working token (#3583, cap #3581): the project's account — its manifest _meta.cloudflare_account, else the operator's default_account in config/cloudflare-accounts.json — the working token that account name… |
bob cloudflare mint | — | Create this machine's Cloudflare working token with the operator's root token (#3583): named bob-<machine id>, scoped to the account and all its zones, holding every permission group Cloudflare lists except the API Tokens groups, and sto… |
bob ritual run | — | Start the attended session of a ritual: resume its open run issue or open one, refuse a kit the machine role forbids (#2228), apply the ritual kit to the project checkout as a provision overlay, then print the evidence packet summary, th… |
bob ritual due | — | Which rituals are overdue, due or upcoming per project — the declared _meta.rituals schedule or liveness-scaled defaults, last run derived from closed ritual-run issues (#2230, #2231); a guide walkthrough is listed for each guide whose r… |
bob ritual open | — | File one ritual-run issue with the standard body (marker, scope, kit, the declared checklist embedded as tickable items at its version (#2241), evidence placeholder, findings checklist, delta against the previous run) after refusing a du… |
bob ritual prepare | — | Gather a run evidence packet report-only and post (or replace) it as one comment on the run issue; --due prepares every farm-eligible ritual due soon, what bob tick runs (#2233). |
bob ritual close | — | Close a held ritual run with durable outputs: every finding links a bug, req, task, security or todo issue or says no-action (an empty run needs --no-findings, recorded), lessons and decisions are linked /journal issues never labels on t… |
bob ritual decide | — | Write a portfolio-review decision into a project manifest: _meta.portfolio invest / maintain / park / archive, the dormant default cadence for a parked or archived project (and active: false for archive), only _meta rewritten in the file… |
bob ritual sweep | — | The portfolio sweep (#3609): inventory every directory under ~/projects and ~/Sites on every fleet machine — this one directly, the others over Tailscale SSH, nested repos included, a machine that cannot be reached named as not inventori… |
bob viz | — | Open bob viz — every project on every fleet machine as small multiples, from the fleet feed (/bob on the dashboard server, same freshness and self-update path as bob warp viz); each tile links to its bob warp viz card, and --tailnet serv… |
bob control | — | Open BoB Control (#3813, cap #3812): /control on the dashboard server, one frame whose navigation is Now, Projects, Fleet and Costs (#3914, #4034), then the crumbs of where you are (#4025) — /control is the Now page, /control?view=projec… |
bob serve | — | Converge this machine's declared tailscale serve front door (#2407): one MagicDNS address, no port, with /bob and every declared project's /<project>/warp path-routed to its loopback dashboard port, from the machines.json serve block; a… |
bob plan build | — | Derive a flight plan once and write/refresh its issue — flightplan.js build --no-interview with the mapped scope flags; prints the plan number. |
bob plan remaining | — | Runnable plan items in run order — flightplan.js remaining <N>, argv passed through unchanged. |
bob plan status | — | Plan progress — runnable vs gated vs withheld vs moot vs waiting, todo gate states, gate mode, integration branch, lifecycle — flightplan.js status <N>, argv passed through unchanged. |
bob plan items | — | The plan's ordered items, todo rows marked — flightplan.js items <N>, argv passed through unchanged. |
bob decompose | — | Decompose an approved capability or use case into filed child requirements through six evidence-gated phases on the shared machine engine (#2975): sourcing (cap-status) → drafting → deduping (issue-dedupe) → testability (the decompose-te… |
bob do | — | Run an hour of unstructured work as a state machine on the shared engine (#2975): framing (a one-line question and an artifact from the closed set decision · lesson · doc · spike · issue-set · discard) → working (bounded by a minutes or… |
bob init | cdi | Initialize a project with BoB tooling: the .claude/ directory, universal symlinks into BOB_HOME, and — when no manifest exists — an offer of one-shot auto-provisioning (bob provision --init --auto). |
bob dash status | cdb | The tooling dashboard: global items and every registered project's symlink status, port band, warp-drive harness defects, and last bob ready verdict. |
bob dash generate | cdg | Generate a project's static dashboard (bugs, releases, changes, incidents, journal, cross-refs) into .dash/ from the current directory. |
bob dash serve | cds | Start the dashboard server for the current project on its ledger-assigned port, or list / stop running dashboards and show port allocations. |
bob promote | cdp | Promote a local .claude/ item (command, agent, hook, skill) to the global source and replace the local file with a symlink back. |
bob link | cdl | Link a global item (command, agent, hook) into the current project's .claude/, link every missing one, or list what is available. |
bob provision | cdprov | Provision a project from its manifest — status / diff / check / refresh / prune / init / interview / detect / add / remove / decline / reconcile / overlay / untrack / ignore-paths — through the node engine scripts/orchestrator/provision.… |
bob fork | cdfork | Fan out parallel warp-drive sessions into isolated git worktrees + tmux windows: fork <branch...> | fork --from-issues [N] | fork --from-flightplan <N> | status | drop | watch | pair. |
bob pair | cdfork-pair | The cross-repo, contract-driven shape of fork: paired worktrees in a backend and a frontend repo on one shared branch (the standalone form of bob fork pair). |
bob swarm | swarm | Run the full agent-swarm loop as one command — decompose → approve → capped dispatch via bob fork → gate → integrate → summary (#582). |
bob warp status | warp | Show the active warp-drive phase, issue, chunk and metrics; --watch redraws in place, --log appends the phase history. |
bob warp log | warp | The warp-drive phase-transition history for this project. |
bob warp config | warp | Show the resolved workflow configuration (_workflow keys, kickoff options) for this project. |
bob warp viz | warp | Open the live session graph in the browser (#1552); --tailnet serves it on this machine's Tailscale address, the default where machines.json declares dashboard.bind: tailnet (#2636). |
bob warp stop | warp | Abort the active warp-drive session — graceful (transitions to aborted) or --hard (deletes the state file, no cleanup). |
bob warp reap | warp | GC leaked warp-seg-* tmux sessions and orphaned REPLs, and find Claude Code in every other tmux session graded stale or live by idle time (#3302) — dry run by default, --force executes, --yes also reaps attached or warp-running sessions,… |
bob warp session | warp | The integration-branch session: start <branch> [--area <slug>|--flightplan <N>] creates + checks out a scope-bound session branch (#1177), end clears it, status shows the resolved branch config. |
bob warp finalize | warp | Ship the integration branch to main — one PR at Level 2, rebase + merge at Level 3 (#268). |
bob warp start | — | Launch a warp-drive run in a named tmux session from any shell: bob preflight first (exit 1 with the verdict when it fails; on the BoB source checkout a failure that is only this machine being behind source is fixed first — deploy.sh the… |
bob warp attach | — | Attach to the project's live warp-drive run and follow it across segments: one name per run, no warp-seg-N to know. |
bob dev up | dev-up | Bring the dev environment to a fully testable state from dev.json: start the server, migrate, seed, provision test users, health-check, report the URL. |
bob dev health | dev-health | Quick health check of the dev environment — the shortcut for dev up --check. |
bob reap | bob-reap | Reap regenerable working-tree residue per the project's reap.json — dry run by default, --apply deletes; keep-list and liveness guards (#1664). |
bob release | bob-release | The BoB release engine from a shell: a stable release (bump + CHANGELOG + vX.Y.Z tag + GitHub Release), a nightly tag on a green, moved HEAD (#2045), or promote — stable from the newest nightly, refused on an open p1-critical harness def… |
bob mirror | bob-mirror | Reconcile and publish to each repo's declared second git host (mirrors.json): resolve / reconcile / publish / verify / status — dry run without --apply (#1747). |
bob forge health | forge-health | Forge availability classified from real gh call outcomes — operational / degraded / unreachable / unknown; the exit code is the answer (#1748). |
bob forge queue | forge-queue | The durable write-ahead queue of issue mutations the forge could not take — status / list / show / resolve / drop / replay / clear; replay is dry-run by default, --apply delivers (#1750); an edit with an empty body is never queued or rep… |
bob backup setup | — | Render a project's backup Worker from its dev.json environments.<env>.backup block (#3084, cap #3067): the Worker and its scheduled Workflow (templates/backup-worker/) written to <project>/backup-worker/ with the project's D1 id, account… |
bob backup status | — | How old a project's newest D1 backup is (#3085, cap #3067): per env whose dev.json declares environments.<env>.backup, the newest dump the backup Worker wrote (read from its latest/<db>.json pointer in the backup bucket), its age, size a… |
bob backup run | — | Take a D1 backup now (#3085, cap #3067): trigger the project's backup Workflow — the one its nightly cron starts — through wrangler workflows trigger, then wait until the latest/<db>.json pointer names a dump taken after the trigger. |
bob backup restore | — | Load a backup into the restore-target env's D1 and verify it (#3085, cap #3067): the newest dump (or --from <key>) fetched from the backup bucket, the target's existing tables and views dropped, the dump loaded with wrangler d1 execute -… |
bob deploy | — | Deploy this project to its test or prod environment through scripts/deploy/deploy.sh — the resolved adapter's check → build → migrate → deploy → verify contract — with a D1 backup taken before migrating when the env declares one and migr… |
bob promotion setup | setup-promotion | Scaffold a project's dev → test → prod promotion ladder — detect → dev.json → wrangler → provision handoff → todo → verify; dry run by default, --apply writes (#931). |
Not a bob command: claude (the shell wrapper in bin/bob-shell.zsh, sourced by one guarded .zshrc line — intercepts -a1 / -a2 / -a3 for automation level, -rdb for Telegram and --kit <name> to apply a session kit as a provision overlay first (#2226), then exec's the real claude CLI, #1195) and cdr (a shell alias for scripts/lib/cdr.sh — disaster recovery).
Verification (Makefile)
bash
make check # deps + schemas + symlinks + hooks + provisions + abs-paths + operator-literals + operator-paths (#1924, #2268, #2270) + legacy-names (#2091) + host
make test # every tests/test-*.{sh,js} through scripts/run-tests.sh; make test-<name> runs one (#2043)
make ci # check + test (what GitHub Actions runs)
make help # full target listCRITICAL: Hooks Receive JSON via stdin
There are NO magic variable substitutions. Hooks receive ALL data via stdin as JSON.
bash
# RIGHT — Read JSON from stdin:
INPUT=$(cat)
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command')| Hook Type | Extract Field |
|---|---|
| Bash PreToolUse | tool_input.command |
| Edit/Write PreToolUse | tool_input.file_path |
| Bash PostToolUse | tool_input.command, tool_response (an object; fires only on success — no exit code field; #1831) |
| Bash PostToolUseFailure | tool_input.command, error (Exit code N\n…) — a failed call fires this instead of PostToolUse (#2385) |
Requires jq. Template: ~/.claude/templates/hooks/command-hook.sh. Full hook table: Hooks Reference. Test fixtures for hook payloads are captured from session transcripts (tests/fixtures/hooks/), never authored from memory — a hook that passes against an invented shape can be dead in a live session (#1831).
Temporal Accuracy (Timelord)
All PM commands must verify dates via ~/.claude/skills/timelord/scripts/now.sh before writing. Never trust memory for dates — session context can become stale across midnight or long sessions.
Date formats: Files: YYYY-MM-DD, Prose: human-readable, Timestamps: ISO 8601. Week convention: ISO (Monday=1).
Work Tracking
All work is tracked via GitHub Issues on each project's repo. No local PM files or indexes.
Do not create REQ-*.md, SOL-*.md, BUG-*.md, RISK-*.md, or any PM document files. Do not create REQUIREMENTS-INDEX.md, SOLUTIONS-INDEX.md, backlog.md, or traceability-matrix.md. The full forbidden-pattern list is in handbook §15.3.
Product Hierarchy
Vision & Strategy (docs/vision.md) /vision — living doc: why, who, where
└── Business case (label: business-case) /business-case — justify investment (above capability)
└── Capability (label: cap) /capability — feature spec, user stories
└── Requirement (label: req) /requirement — acceptance criteria, testable work
└── Warp-drive chunks automatic — individual commits
Use case (label: use-case) /use-case — a scenario; decomposes via "Realizes #NN"
└── → Capability (broad) or Requirement (narrow)
Outside the tree — no parent capability, trace to a defect or an external trigger:
Bug (label: bug) a defect in delivered behaviour — reproduce, regression test, fix
Task (label: task) /task — maintenance (dependency bump, CVE, runtime/EOL, config migration)| Level | Format | Command | Contains |
|---|---|---|---|
| Vision | docs/vision.md | /vision | Product vision, strategy, principles, non-goals, roadmap |
| 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 via Realizes #NN |
| Reporting | GitHub Issue | /journal | Decisions, lessons, session summaries |
| Bug | GitHub Issue (bug) | gh issue create --label bug | Bug reports |
| Task | GitHub Issue (task) | /task | Maintenance work: trigger (advisory / CVE / EOL / release link), procedure, Verification checklist in place of acceptance criteria; no Part of #NN |
| TODO | GitHub Issue (todo) | auto-created by warp-drive timeout | Human action items; add the completed label to signal the action is done (canonical done-signal — see TODO lifecycle below) |
The vision doc is a living document, not an issue — it doesn't get "closed." Capabilities trace back to the vision. Requirements link to their parent capability via Part of #NN in the Notes section. Capabilities track child requirements as a checkbox list. A business case (business-case) sits above capability — it justifies investment, and the capabilities realizing its recommended option link back via Part of #NN. A use case (use-case) captures a concrete scenario and decomposes into a capability (broad) or requirement(s) (narrow), each realizing issue linking back via Realizes #NN. A task (task) is a sibling of bug, not a child of cap: it traces to an external trigger rather than to product intent, legitimately has no Part of #NN, and its Definition of Done is its Verification checklist — /groom never flags it as an orphan or reparents it (see task-work-type.md).
Priority labels: p1-critical / p2-high / p3-medium / p4-low
Workflow: cap + approved → ready to break into requirements. req, bug or task + approved → picked up by /warp-drive.
TODO lifecycle (completed done-signal): A todo issue is a human action item an agent hands back when it hits a human-only blocker. The completed label is the canonical done-signal: adding completed to a todo means the human performed the action, so downstream tooling (warp-drive / groom / /what-next) may resume the work the todo was gating — no re-prompt needed. A todo closed without completed means the action was cancelled or became moot — downstream tooling must not resume the gated work. Closing alone is therefore never a resume signal; only the completed label is. The standard label set (including completed) is bootstrapped into every repo via scripts/bootstrap-labels.sh (the label-bootstrap path bob init/project-init runs), so new projects start with the full set.
Autonomous Development Workflow
When the user asks "what next?", "what should we work on?", or similar — check GitHub Issues via gh issue list --label approved on the relevant repo (or run /what-next).
When the user asks for a status report or is returning to a project after time away — run /what-next --report (alias: /status): an on-demand standup that prepends a backward-looking catch-up (recent activity, capability status, a data-derived health line) to the same ranked next-work list. Terminal-only by default; --file (or /status --file) additionally files the report as a status-report-labelled GitHub Issue. Never auto-filed, no local files.
At automation Level 2/3, the /warp-drive command orchestrates the full cycle: work discovery → code → document → test → commit → report → merge/PR → continue. Only prompt for project decisions (architecture, ambiguity, new deps, scope expansion).
A forge failure never pauses the loop (#3191). A GitHub rate limit or outage never holds the project: coding, testing and committing continue; a write the gh shim queued ([forge-queue]) counts as done and is never waited on; the engine replays the queue at every item boundary; only merging parks, and a plain git push never parks on a rate limit. When the user says a todo is completed, act on it without waiting for the label to reach GitHub.
For parallel work clusters across a repo, use bob fork (or the /cdfork slash command) to fan out N warp-drive sessions into isolated git worktrees + tmux windows. Subcommands: bob fork fork <branches...>, bob fork fork --from-issues [N], bob fork status, bob fork drop <branch>, bob fork pair for cross-repo contract-driven mode. Pinch points (config sync, lockfiles, migrations, compiled assets, deploy) must NOT be fanned out — label such issues serial-only so --from-issues excludes them.
See warp-drive.md and cdfork.md for the full references.
Dev Environment Lifecycle (IaC)
Every project can declare its dev environment in a dev.json manifest at the project root. The /dev-up command (and bob dev up from a regular terminal) reads this and orchestrates: start server → run migrations → seed data → provision test users → health check → report URL.
On-demand: Run /dev-up anytime to bring a project's dev environment to fully testable state.
Warp-drive integration: When dev.json exists, warp-drive automatically calls bob dev up before the first chunk, verifies health between chunks, and auto-recovers on failure. Dev health failure is a blockable event.
Seed data convention: Projects use a seed/ directory with idempotent scripts. Seed data must cover ALL lifecycle states the domain defines (not just fresh records). New features include seed data in the same commit.
Standardized test users: seed/users.json defines test users provisioned via pluggable auth adapters (d1, sqlite, supabase, script, custom). The superuser (admin@test.local / admin123) is the product default, not a constant (#2272): an operator overrides it once in its profile — operator.json seed.superuser, an email and the NAME of a secrets[] entry holding the password, never a literal — seed-generate.sh writes that into every generated users.json (as password_secret, resolved from the environment at provisioning time), and /dev-up reports which superuser it provisioned and where it came from (Superuser: <email> — source: …). Never hand-edit the superuser per project.
See dev-lifecycle.md for the full reference.
Talking to the Operator (Retard Mode)
The operator is the reader, and he has said how he wants to be told things (#3798, part of #3074). Two phrases, typed or spoken, work in every session:
me do what now?— answer with numbered steps and nothing else. Every step has three parts: the thing to paste (one fenced block, safe to paste blind), what he should see, and what broken looks like ("stop and paste me what you got"). Small words. No headings, options, trade-offs, tables or issue-number history.TLDR— a concise summary of where things stand, then an interview: ask each open question one at a time, short enough to answer without scrolling, recommended answer first. With no open question, say so in one line.
The three-part step form is the default, not a rescue: write every todo, handoff and inline "here is what you need to do" that way without being asked. Nothing is left as obvious.
Remote Decision Bridge
Toggle RDB mode via /rdb on or the -rdb startup flag. When RDB is enabled (_rdb.enabled: true in settings.local.json), use ask_remote for ALL decisions — never fall back to AskUserQuestion. When the user says they're stepping away, going AFK, or going remote, treat that as /rdb on. Use notify_remote for status updates during autonomous work. /rdb off switches back to terminal prompts. From a plain shell — a farm tick, a release script, a terminal — bob notify <message> sends the same kind of message through scripts/rdb/notify.sh and degrades honestly (exit 3, message printed) when no bridge is configured (#2048).
Reporting (GitHub Issues)
| Type | Label | Created By |
|---|---|---|
| chunk report | chunk-report, warp-drive | warp-drive reporting phase — one per commit |
| session report | session-report, session-summary, warp-drive | warp-drive session end / abort |
| decision | decision | /journal decision |
| lesson | lesson | /journal lesson |
| status report | status-report | /what-next --report --file (or /status --file) — on-demand point-in-time snapshot requested by a human, never auto-filed |
Reports are execution artifacts, lesson/decision are knowledge records, and the two never share an issue (#2062). A chunk report's body narrates decisions and lessons — that is what a good report does — but labelling it lesson/decision diluted the labels that exist to make knowledge findable. A genuine lesson or decision is filed as its own issue via /journal and linked from the report. make check enforces it (check-report-labels.js); scripts/warp-drive/relabel-reports.js corrects history; the full label glossary is tracker-labels.md.
Memory Conventions
These conventions refine the harness's file-based memory behavior. The default is to overwrite a memory file in place when a fact changes. That is correct for project and reference memories (the current state is all that matters), but destructive for feedback and user (preference) memories, where why and when a preference changed is itself worth keeping.
Archive-on-supersede (mandatory for feedback and user memories). When the value of a feedback or user-preference memory changes, do not overwrite the prior value silently. Instead:
- Write the new value as the memory body, as usual.
- Preserve the prior value under a
## Historyblock at the end of the file, as a dated, superseded entry. - Stamp the change with today's ISO date (sourced via timelord — never from memory).
This keeps the episodic evolution of a preference legible: a returning session can see not just what the preference is, but what it was and when it changed.
## History format
Append (newest first) one bullet per supersede to a ## History section:
markdown
## History
- **YYYY-MM-DD** — Superseded. Prior value: "<the previous value, verbatim or summarized>". Reason: <one line on why it changed>.project / reference memories may overwrite in place and do not need a ## History block (though they may use one if the change history is genuinely useful).
The most recent ## History date counts as a memory's last-touched date for /memory-audit — a recently-superseded memory is fresh, not stale, even if the file mtime is older (e.g. after a deploy that rewrote mtimes).
Workaround provenance (defect: tag, #1837)
A feedback or project memory that describes a workaround for a BoB defect — "the hook didn't fire, advance by hand", "do it manually until X works" — is provisional by nature: it must expire when the bug is fixed, or the project keeps applying the workaround as a habit. Tag it with the defect it compensates for, as a frontmatter field:
markdown
---
name: warp-drive-commit-pitfalls
description: committing → reporting did not auto-advance
defect: paulirv/bigbrain#1830
metadata:
type: feedback
---defect: <owner>/<repo>#<n> (top-level, or under metadata:) names the tracking issue. /memory-audit resolves each tagged issue through the gh shim (cached per project; an unreachable forge counts as open) and:
- flags a tagged memory whose defect is closed as
workaround-stale— stale: verify the fix, then delete the memory (or drop the workaround from it); - flags an untagged
feedback/projectmemory whose prose reads as a workaround ("didn't fire", "do it manually", "advance by hand") asuntagged-workaround— file the bug if none exists, then tag the memory.
When the fix lands, delete the workaround memory (or record the retirement under ## History) rather than leaving a rule that no longer holds. The same tag drives loop-note retirement — see Workaround provenance in the loop-memory reference.
Worked example
A user previously preferred terse commit bodies, then changed their mind. The memory is not overwritten destructively — the prior preference is archived with the date it changed:
Before (feedback_commit_verbosity.md):
markdown
---
name: commit-verbosity
description: how detailed commit bodies should be
metadata:
type: feedback
---
Keep commit bodies terse — a one-line subject is usually enough; skip the body unless the change is subtle.
**Why:** The user reviews commits on mobile and finds long bodies noisy.
**How to apply:** Default to subject-only commits; add a body only for non-obvious changes.After the preference flips to detailed bodies (changed 2026-06-19):
markdown
---
name: commit-verbosity
description: how detailed commit bodies should be
metadata:
type: feedback
---
Write descriptive commit bodies — explain the *why* and the trade-offs, not just the *what*. A bare subject line is not enough for non-trivial changes.
**Why:** The user now relies on `git log` for project history and wants the reasoning captured at commit time.
**How to apply:** Default to a subject plus a body that explains motivation and any alternatives considered.
## History
- **2026-06-19** — Superseded. Prior value: "Keep commit bodies terse — subject-only unless the change is subtle." Reason: user shifted to using `git log` as the durable project record.