Skip to content

bob ready

bob ready — the one command for starting or resuming a project

Section titled “bob ready — the one command for starting or resuming a project”

bob ready is the one command for starting a project or picking one up on any machine (#3158). Give it a new directory, an existing checkout or an owner/repo, and it ends with the project on the forge, on bob viz and warp-ready — or NOT READY with the exact fix for each blocker. It never asks a question, a second run changes nothing, and nothing another project is doing on the machine stops it: it never deploys BoB (#3160), and a live run elsewhere blocks none of its steps.

It began as a façade over five steps each remembered separately — bob init, bob provision refresh, label bootstrap, port band, dev.json / checks.json sanity (#1922). It now also creates the repo, registers the project with bob viz, and gives a project it creates its CI locus, automation level, remote decision bridge and mirror.

Terminal window
bob ready ~/projects/newthing # a missing or empty directory: created, git init, forge repo, … READY
bob ready acme/shop # clone into this machine's projects_root, then the same
bob ready # the project you are in
bob ready ./notes --new # a folder that already has files: --new creates its forge repo
bob ready ~/projects/shop --start # READY, then bob warp start, printing the attach command
bob ready ~/projects/shop --vision intent.txt # also write the first docs/vision.md from your text (#4033)
bob ready [project-dir | owner/repo] [--new] [--start] [--vision <file|->] [--dry-run] [--json] [--name <project-name>]
bob ready --all [--dry-run] [--json] # every declared project checked out here (#2068)
bob ready … # same engine, PATH wrapper
make ready ARGS=<project-dir> # same engine, from the BOB_SOURCE checkout

Exit 0 READY · 1 NOT READY · 2 usage error. Engine: scripts/ready/ready.js.

bob ready --all grades every project the registry declares — the derived projects.json, a projection of provisions/*.json _meta (#2066) — that is checked out on this machine, running the same steps below per project under the entry’s manifest name. A non-dry run writes each project’s .claude/.project-ready.json, so one command backfills the verdicts the fleet snapshot and bob dash status read.

Registry entry Reported as
active: false not listed — declared archival
path not checked out here ABSENT on one summary line — never graded, never a failure
the BoB entry ($HOME/.claude) SKIPPED — BOB_HOME is a deploy target graded by bob doctor; bob init / bob provision refresh never converge it
present READY / NOT READY with its first blocker (and how many more)
bob ready --all — 3 of 5 declared project(s) present on farm-01
PROJECT VERDICT FIRST BLOCKER
bigbrain READY —
shop NOT READY ports: port ledger has a collision (shop) (+1 more)
site READY —
absent here (1): mobile
skipped (1): BoB — BOB_HOME is graded by bob doctor
NOT READY — 1 of 3 present project(s): shop (bob ready <project-dir> for the full step list)

Exit 1 when any present project is NOT READY, 2 when projects.json is unreadable (or --all is given a project-dir / --name). --json emits project-ready-all/1: verdict, ok, summary (declared, present, ready, not_ready, absent, skipped) and projects[] (name, path, dir, present, verdict, first_blocker, blockers, reason for ABSENT/SKIPPED, and the per-project project-ready/1 document as report). The snapshot’s summary.project_ready also counts ungraded — present projects with no verdict yet — and audit.js view names the fix, bob ready --all.

You pass bob ready does
a path that does not exist, or an empty directory creates it, git init, puts the unborn HEAD on main, then links the forge repo <default_org>/<name> if it exists or creates it — private unless operator.json project_defaults.visibility is public
a directory with files but no forge repo the same local steps, but creates no forge repo unless you pass --new (a typo’d path never makes a GitHub repo); without it the repo step blocks with bob ready <dir> --new
an existing checkout with an origin nothing in the repo step (no-op)
owner/repo that is not a local path gh repo clone into this machine’s projects_root (machines.json, default ~/projects), then carries on

Anything only a human can do is a blocker naming the command: gh auth login when the forge refuses, bob operator init <dir> --link when no operator profile names the forge. When the repo step blocks, every later step is skipped, never run against a half-made repo.

What a project it creates also gets. These steps act only on a project this run created, cloned or first provisioned, so bob ready --all never raises an existing project’s automation level, moves its CI or adds an MCP server to it:

Step From Result
automation operator.json project_defaults.automation_level the level set with switch-level.sh <n> --yes
ci operator.json project_defaults.ci ({locus, runner_label?}) written into the manifest as _meta.ci, then CI_RUNNER_LABEL projected by ci-locus.js reconcile --apply (a public repo is never sent to farm)
rdb a machine that declares project_rdb: true in machines.json the remote-decision bridge copied from $BOB_HOME/.mcp.json (or the source checkout’s) into the project’s .mcp.json, with BRIDGE_PROJECT set, ignored by git and mode 600
mirror mirrors.json enabling a mirror for it bob mirror reconcile --apply creates it and wires the remote

Without a project_defaults entry each step says what to declare as an advisory and changes nothing. Every project also gets logs: a .logs/ directory, ignored through .git/info/exclude so the working tree stays clean.

The first manifest lands without waiting. bob provision --init --auto commits a project’s first manifest straight to the operator repo’s default branch at every automation level, and finishes any config/<project>-* branch it makes redundant — see the single-item fast path.

Step Runs ok / no-op means Blocker names
repo git, gh repo view/create/clone (new-project.js) a git checkout with an origin bob ready <dir> --new · gh auth login · bob operator init <dir> --link
host host-drift.js (bob doctor, #1921) this machine’s BOB_HOME is usable — measured against the commit it was deployed from, never the source working tree (#3160), and host-scoped sections only, so a stale link in the project is never reported as host drift (#1953). Host drift is an advisory: a link in BOB_HOME that does not resolve, the forge shim or the shell guard are named with their fix on an Advisory: line and the step stays ok. How far source has moved since the deploy is named in the detail as information only a hard finding blocks: a surface that was never deployed, node / jq / gh missing, host-drift.js erroring, or BOB_HOME differing from its recorded commit — scripts/deploy.sh, or the fix the failing section names
version reads dev.json bob_version and $BOB_HOME/.bob-version (the bob-version/1 marker deploy.sh writes, #2046) — no tool invoked (#2047) no pin declared (any deployed BoB is accepted); a channel pin (stable / nightly) matches the marker’s channel; a tag pin (vX.Y.Z / nightly-YYYY-MM-DD) equals the marker’s tag. A missing or off-schema marker is not measured (rendered n/a) — never a blocker; deploy.sh writes the marker bob update (stable) · bob update --channel nightly · bob update --to <tag> — the detail names both sides: BOB_HOME runs 1.273.0 (v1.273.0) [stable], project pins tag v1.270.0 — fix: bob update --to v1.270.0
links host-drift.js --section project_symlinks (#1953) every link under the project’s own .claude/ resolves on this machine per item: bob provision refresh (declared) · bob init -y (universal) · rm <link> (undeclared leftover)
init bob init -y <dir> .claude/ scaffolded, universal items linked, project registered bob init -y <dir>
provision bob provision refresh <dir>, or bob provision --init --auto <dir> when no provisions/<name>.json exists — a first manifest is committed to the operator repo’s default branch at every level (#3158) manifest projection converged (no [unlinked] items) the failing bob provision command
logs — .logs/ exists and git ignores it —
automation switch-level.sh (a project it creates) a level is set, or the project is not new the failing switch-level.sh
permissions reads .claude/settings.local.json and the deployed profile for its level ($HOME/.claude/profiles/<supervised|trusted|autonomous|full-auto>.json, the files switch-level.sh copies from); re-applies with switch-level.sh <level> --yes when they differ (#3702) the applied allow / deny lists equal the profile’s as sets, or no level is set. A profile changed since the level was set (new deny rules, #3618) reaches a project only when it is re-applied: --dry-run reports would-change, naming what differs and the command; a real run re-applies it, the level unchanged, and says what it changed. No readable deployed profile is not measured (rendered n/a) — never a blocker cd <dir> && bash …/switch-level.sh <level> --yes, when re-applying failed or left the lists different
labels bootstrap-labels.sh check, then ensure only if labels are missing. The check never waits for a quota reset: it reads gh label list, then the REST labels endpoint (a separate quota), then the forge cache’s last-known list (#3195) the canonical label set is on the repo’s forge; the detail names the source when it was REST or the cache. A rate limit with no source left is not measured (rendered n/a, naming the quota and its reset time) — never a blocker. A forge that cannot be reached is not measured too (#3160): the label set was not read, so nothing is known to be missing — the Advisory: line names gh auth login / a GitHub remote the failing bootstrap-labels.sh ensure — only when labels are known to be missing and could not be created
ports port-fleet-migrate.js --dry-run --json as a collision gate, then port-project-sync.js sync a band exists in provisions/ports.json and dev.json carries its _bob_ports projection. A collision in another project’s band is an advisory here (#3160) — it is that project’s to fix, named with make port-migrate make port-migrate on a collision that names this project
required for a project marked _workflow.unattended only: merge-on-green.sh --required-of <default branch> --json, the same read that judges an unattended PR (branch protection’s required checks, else .github/required-checks.json on that branch, #3625, #3696) the default branch names its required checks; the detail names them and where they come from. A project that is not unattended is no-op with no forge read. Advisory (never a blocker): an unattended project whose default branch names none, because merge-on-green would leave every one of its PRs open. The Advisory: line names the file and the body to add: .github/required-checks.json containing {"required": ["<the check that reports on every pull request>"]}. A read that fails (forge down, no remote) is not measured —
manifests dev.json against schemas/dev.schema.json; checks.json shape (name / command / kind, optional paths / tier / timeout / quarantine) manifests parse and validate (absent dev.json is fine — a CLI repo has no dev server). Advisory (never a blocker, #1979): a browser-driven suite (test:e2e* / *visual* scripts, a Playwright config) with no checks block is reported as checks: undeclared (wholesale fallback) with an Advisory: line — declare scoped checks so warp-drive runs only what the change touched the manifest to fix
ci writes _meta.ci (a project it creates), then ci-locus.js reconcile --apply _meta.ci declared and the repo variable in sync, or nothing declared. A reconcile that cannot reach the forge is an advisory —
viz projects-derive.js --write into the operator repo’s projects.json (#3432), which the fleet server reads, audit.js snapshot (queued-deploy drain off), bob viz --json (starts the fleet server, or restarts a stale one), then feed-has.js polls the live /fleet.json the project is listed under this machine on bob viz — a refresh of the page shows its tile. A snapshot that fails to publish is an advisory: this machine’s tile is read live bob viz <source> when the feed still does not list it after one refresh interval (20 s, BOB_READY_FEED_WAIT_S)
rdb copies the bridge entry (a project it creates, on a project_rdb machine) .mcp.json carries the bridge, or the machine does not declare it where to put the bridge entry
mirror mirror.js status, then reconcile --apply (a project it creates) the mirror is wired, disabled or undeclared. Declared but unwired on an existing project is an advisory —
vision only with --vision <file> (- reads stdin), vision.js (#4033) changed: docs/vision.md written from the text. skipped: one already exists — it is never overwritten, and the detail says so — (an empty or unreadable file is a usage error, exit 2, before any step runs)
work docs/vision.md, gh issue list --label approved (not in a dry run) informational, never a blocker: with neither a vision nor approved work the summary’s next command is /vision, then /capability —

Every step runs even after a blocker, so one pass reports every blocker — except after a blocked repo step, when there is no checkout to run them on.

READY ends with where to look and what to run. The summary prints the bob viz URL and the next command — bob warp start (or /warp-drive in a Claude session), or /vision then /capability when there is nothing to work on yet. --start runs bob warp start for you once the project is READY with approved work and prints the session and its attach command (bob warp attach <project>, #3163). A start that fails exits 1 with its fix; READY stays the verdict.

--vision writes the first vision (#4033). The operator’s text — from the shell, or from Control’s New project form, which runs the same command — becomes docs/vision.md when the project has none. The file follows the /vision template (templates/docs/vision.md): its title with the project name, > Last updated: today, every template heading in order, the text under ## Vision, and every other heading left empty to fill. It carries a vision-draft marker line; while the marker is there the next step is /vision — here and in bob status --scope project — and /vision removes it once it has worked the sections through. An existing docs/vision.md is never touched: the vision step is skipped and says so.

Advisories (#3160). An advisory is named with its fix and changes neither the verdict nor the exit code. project-ready/1 lists them as advisories[] (step, detail, fix); the step that raised one carries it as advisory. Host drift never blocks project work: before #3160 BOB_HOME was compared with bigbrain’s working tree, so a commit, an uncommitted edit or a feature-branch checkout there made every other project on the machine NOT READY. What still blocks is what would make the run wrong: an unusable BOB_HOME, and a declared bob_version pin that is not met.

Pinning a project to a BoB version (#2047). A public launch must not inherit a harness change it did not ask for. Declare the BoB the project expects in dev.json and the version step refuses a run on any machine whose deployed BoB does not satisfy it, naming the exact bob update that would:

{ "bob_version": "v1.273.0" } // exact tag — or "stable" / "nightly" for a channel

schemas/dev.schema.json pattern-checks the value (make check-schemas validates the committed fixture schemas/fixtures/dev.bob-version.json). bob preflight inherits the verdict as project:version through the same project-ready/1 blockers — one step, no new contract. See Pinning a project.

  • No prompts, ever. Every tool is invoked in its non-interactive form with stdin closed. Anything that would need a human is a blocker with the command to run, not a question.
  • Parallel-safe (#3158). The steps that write machine-wide files — provision (the operator repo), ports (the ledger) and the viz step’s registration and snapshot — hold one lock, ${XDG_STATE_HOME:-~/.local/state}/bob/ready.lock, so two bob ready runs on different projects never lose each other’s writes. A lock whose owner has died, or is 15 minutes old, is taken over; a live owner is waited for (BOB_READY_LOCK_WAIT_S, default 180 s), then the step blocks naming the project, pid and step holding it. projects.json and the port ledger are written through a temp file and a rename. A live run in another project blocks nothing; a live run in this one skips init (#3099).
  • Idempotent. A second run on a ready project changes nothing and reports each step ok / no-op. Change detection is a fingerprint of the project’s .claude/ tree (and, for ports, dev.json + the ledger) taken around each mutating step — changed is observed, not assumed.
  • --dry-run reports what each step would do (would lines) and runs nothing that writes; only read-only probes (host-drift.js, bob provision --status, bootstrap-labels.sh check, the collision gate) execute.
  • macOS and Linux alike. node + bash only; no iTerm2 or brew assumptions; paths resolve via $BOB_SOURCE / $BOB_HOME / $HOME.
  • No BOB_SOURCE export needed (#2273). The BoB source is resolved by scripts/lib/bob-source-resolve.js: $BOB_SOURCE, then the engine’s own checkout, then ~/projects/bigbrain. Only with no checkout at all does a deployed engine fall back to its own tree. The step tools are handed that source as BOB_SOURCE, so a first manifest write never lands on the deploy target.
  • --json emits a versioned project-ready/1 document: verdict, ok, bob_source (the source the run read), steps[] (name, status, detail, fix, and advisory on a step that passes with a recommendation; the version step adds pin, pin_kind, measured and, when measured, deployed: {version, channel, tag}), blockers[], advisories[], viz_url, next ({command, in_session}, or {command: "/vision", then: "/capability", why}) and, with --start, start ({started, session, attach} or {started: false, reason, fix}). A step whose evidence is absent reports status: skipped with a not measured detail — distinct from blocked.
  • The verdict persists. A non-dry run writes <project>/.claude/.project-ready.json (verdict, at, machine, blockers). The fleet snapshot reads it as project_ready per project and rolls it up per machine; audit.js view renders a “Project ready” table naming NOT READY projects with their blocking steps; bob dash status prints a ready: READY / ready: NOT READY (step…) line per graded project. Nothing re-runs the steps to show a verdict.

A new project:

bob ready — hello (/Users/you/projects/hello)
changed repo created the directory, git init, created acme/hello (private) and linked origin
ok host BOB_HOME matches the deployed commit c275c81 (4 sections)
ok version no bob_version pin in dev.json — any deployed BoB is accepted
ok links no .claude/ links to grade
changed init cdi scaffolded/relinked .claude/
changed provision first manifest generated by bob provision --init --auto and landed on the operator repo's default branch — no review PR at any level (#3158)
changed logs created .logs/; ignored through .git/info/exclude
changed automation automation level 2 (operator default)
ok labels standard label set present
changed ports band allocated/projected (base 5360)
ok manifests no dev.json (CLI/library project — dev-up not declared); no checks.json (warp-drive falls back to the detected test command)
changed ci declared _meta.ci locus farm; repo variable reconciled
changed viz registered in projects.json, snapshot published, listed by the live feed — http://farm-01.tail3bfead.ts.net:6952/bob
changed rdb added the remote-decision-bridge to .mcp.json (from /Users/you/.claude/.mcp.json; ignored by git)
changed mirror mirror farm@farm-01:~/git-mirrors/hello.git created and its remote wired
ok work no docs/vision.md; no approved issues — nothing for a run to pick up yet
READY — hello is warp-drive-ready on this machine.
bob viz: http://farm-01.tail3bfead.ts.net:6952/bob
next: /vision, then /capability — no docs/vision.md and no approved issues yet

An existing project with a blocker:

bob ready — shop (/Users/you/projects/shop)
no-op repo git repo with origin git@github.com:acme/shop.git
ok host BOB_HOME converged with source (4 sections)
ok version BOB_HOME runs 1.273.0 (v1.273.0) [stable] — satisfies the project's channel pin stable
ok links 12 project .claude/ link(s) resolve on this machine
no-op init .claude/ already initialised
changed provision cdprov refresh re-linked declared items
ok labels standard label set present
BLOCKED ports port ledger has a collision (shop)
Fix: make port-migrate (in BOB_SOURCE) — resolves the collision loudly
ok manifests dev.json valid; checks.json: 3 check(s) declared
NOT READY — 1 blocker(s): ports (fixes above)
  • A human, after cloning a project on any machine (bob init alone is no longer the whole story — see New machine setup).
  • Fleet repos sync as its post-clone BoB hook, and thefarm’s bob workload for farm checkouts (paulirv/fleet#15, #1920 R5).
  • The warp-drive preflight gate (#1923) consumes the same verdict: bob preflight (scripts/warp-drive/host-preflight.js) runs bob doctor and bob ready --dry-run as one warp-preflight/1 document — non-mutating by default, --converge applies bob ready first and then re-checks (kickoff-only: refused once a run has done work, and never a deploy.sh / settings rewrite, #1997) — and on failure files one todo per machine + cause (a host cause in bigbrain, a project cause in the project repo; a recurrence comments, a later pass closes it — #3161) naming its fix. It never proceeds past a failed check; exit 0 pass · 1 fail · 3 engine error (fails closed).

make test-ready drives the façade against stubbed step tools ($BOB_READY_TOOLS names a directory of same-named executables), so the contract above — order, classification, idempotence, dry-run, verdict, exit codes, persistence, the bob dash status line — is what is under test; each underlying tool keeps its own suite.

tests/test-ready-viz.js drives the real CLI and the real feed probe against a live HTTP feed that reads projects.json per request. tests/test-ready-new.js starts projects from nothing with real git and a scratch operator: a new directory, a second run, a clone, --new, a forge auth failure, an existing forge repo, no project_defaults, a dry run, --start, and --vision (the template’s headings in order, the text under ## Vision, an existing file never overwritten, an empty file refused). tests/test-ready-parallel.js runs two real bob ready processes at once against deliberately racy registry and ledger stubs, with a negative control that proves the race is real, beside a live run in a third project.

make test-ready-all drives --all over a throwaway $HOME whose registry mixes a READY project, a NOT READY one, an absent path, an active: false checkout and the BoB entry: the table, the exit code (1 mixed, 0 all READY, 2 usage / unreadable registry), per-project persistence, dry run, project-ready-all/1, and the audit.js view ungraded hint (#2068).