Skip to content

Build master @ bc50cd5bunreleased — 31 commits past v1.282.0Last reviewed Last changed Grade fresh (aging >90d, stale >180d)Source / History

bob status — one engine, one contract over the health readers ​

Six engines answered "is it healthy" with four contracts: bob doctor (bob-host-drift/1), bob ready --dry-run (project-ready/1), the readiness probe, the fleet snapshot (bob-fleet-snapshot/1), audit.js view and bob dash status, plus rebob --report. bob status composes them behind one bob-status/1 document and one renderer (#2080). It grades nothing itself: each section embeds the composed engine's document unchanged, so a consumer of a v1 contract reads the same bytes inside the section.

bash
bob status                 [--json] [--live]
bob status --scope host    [--json] [--project <dir>] [--section <a,b>] [--in-flight-base <ref>]
bob status --scope project [--json] [project-dir] [--dry-run] [--name <n>] [--reconcile-preview]
bob status --scope fleet   [--json] [--no-live] [--projects-only | --global-only | --matrix | --ports | --check <project>]

Exit 0 ok · 1 drift (host drift, NOT READY, a never-published or stale fleet machine) · 2 error or usage error. Engine: scripts/status/status.js.

bob status never mutates. The project scope always grades as a dry run; --dry-run is accepted so the spelling matches bob ready --dry-run. To converge a project, run bob ready; to converge the machine, run scripts/deploy.sh.

The front door (no arguments, #2641) ​

bob status with no --scope answers "is everything OK, and if not, what do I run?" on one screen:

text
bob status — farm-01 (cached; --live to re-grade)
  host OK
  project bigbrain NOT READY (2) — graded 6h ago
  fleet 1 stale (of 3)

running here:
  warp-drive run  bigbrain: coding on #2641
  dashboard       bigbrain  http://farm-01.example.ts.net:6952/warp
  fleet view      http://farm-01.example.ts.net:6952/bob

next: bob ready
LineWhere it comes from
hostthe live bob doctor probe (host-drift.js probe()), which reads only local files, git and ps
projectthe project the working directory is in (its git root, with a .claude/): its last recorded bob ready verdict in .claude/.project-ready.json, with its age. --live runs bob ready --dry-run instead. Outside a project the line reads project — not in a project
fleetthe cached fleet snapshots, as --scope fleet --no-live reads them. --live pulls each machine
running here:what runs on this machine now: the warp-drive run (phase, issue) in this project and in any project a dashboard serves; each dashboard server with its viz URL; and the fleet view. The URLs come from the one resolver in dashboard-servers.js (list() + preferredUrl(), #2639): a live serve route, else MagicDNS, else the tailnet IP, else localhost. The block is left out when nothing runs

Without --live it makes no network call: no gh, no ssh, no pull. The only calls leave nothing on the machine: tailscale status and the host probe's loopback freshness check of this machine's own dashboards. It finishes in a few seconds. The exit code is the worst of the graded lines under the contract above. A running warp run and a project with no recorded verdict are listed but never move it.

It ends with exactly one next: line. The command comes from one declared table, NEXT in status.js; the first finding present, in this order, wins:

Findingnext:
the host probe could not runbob doctor
host driftthe host engine's own Fix: command, e.g. scripts/deploy.sh
the project is NOT READYbob ready — the one convergence command, never its individual steps
a warp run is going herebob warp status
a fleet machine is stale or never reportedbob status --scope fleet
the project has no recorded verdict herebob ready --dry-run --record
none of thesenothing — all green

--json emits bob-status/1 with scope: "all" and the host, project and fleet sections, each embedding its engine's document as the scoped forms do (the project section embeds the recorded verdict). Each section also carries its line and finding, and the document carries running (warp[], dashboards[] with each url and its full urls set, fleet_url) and next: {finding, command, why}.

Scopes ​

ScopeSectionEngineEmbedded documentGraded
hosthostscripts/checks/host-drift.js probe()bob-host-drift/1yes
projectprojectscripts/ready/ready.js ready({dryRun: true})project-ready/1yes
projectreadinessscripts/fleet/readiness.js projectReadiness()the readiness record the fleet snapshot carriesno
projectritualsscripts/rituals/ritual.js due() for the manifest whose _meta.path holds the projectbob-rituals-due/1no
projectbackupsscripts/backup/freshness.js over bob backup status --jsonthe backup freshness record (null when none is declared)yes, when a backup is declared
projectreconcile_previewscripts/rebob/index.js --report --json (only with --reconcile-preview)rebob's {summary, findings} reportno
fleetsnapshotscripts/fleet/audit.js buildSnapshot() (skipped with --no-live)bob-fleet-snapshot/1 for this machineno
fleetfleetscripts/fleet/audit.js aggregate()the audit.js view --json rowsyes
fleetritualsscripts/fleet/audit.js rollupFleetRituals() over those rows (absent when no machine reports rituals)the fleet ritual roll-upno

A graded section decides the scope's verdict: the worst of them, error > drift > ok. An informational section carries its own verdict — drift when a readiness stage is broken or the rebob preview has findings — but never moves the scope's, so the exit code stays the v1 command's.

Rituals (#2232). Both the project and the fleet scope carry the overdue ritual count at the top of the document as rituals_overdue: the project's summary.overdue from ritual.js due, or the fleet roll-up's overdue. It is null when nothing measured it (the host scope, an unmanaged project, a fleet whose snapshots predate #2232). The rituals section is drift when anything is overdue, but it is informational: an overdue ritual is something to schedule, not a health failure, so it never changes the verdict or the exit code. An unmanaged project is a rituals section with verdict: "error" and the engine's message. The fleet roll-up counts a project checked out on several machines once, taking the machine that could read most of its run history: {projects, overdue, due, upcoming, unknown, overdue_projects: [{name, machine, keys}]}.

Backups (#3086). A project whose dev.json declares environments.<env>.backup gets a graded backups section: ok when the newest backup is fresh against its schedule, drift when it is stale (a missed run, or no backup yet), error when the bucket could not be read — an unknown backup is never ok. A project that declares no backup gets an ungraded backups line (backups: none declared). The record and its fields are the one the fleet snapshot carries, described in Dev lifecycle. It reads the bucket through wrangler. The other network reads of the project scope are the forge reads of the project's next step.

The fleet scope builds this machine's snapshot once and hands it to aggregate(), so the snapshot section and this machine's fleet row describe the same document. A cdb view flag (--projects-only, --global-only, --matrix, --ports, --check <project>) renders that bob dash status view unchanged; it is human output only, and --json with one is a usage error.

The project's next step (#3921) ​

The project scope ends with one next step for the project, as next in the document and a next: line in the human rendering. The rules are applied worst first, and the first one that applies wins:

FindingWhennext.command
warp-runA warp-drive run is going in the checkout (its state file's phase is not completed).bob warp status
blocking-askAn open ask (a todo without completed, or an open brief) holds up work: what it unblocks is an item of a running plan, a ready plan or an approved issue (#3915). The most-blocking one is named.gh issue view <N> --repo <owner/repo>
project-not-readyThe project section's ready dry run says NOT READY.bob ready
plan-runnableAn open flight plan has a runnable item (flightplan.js remaining <N> --json) and no run is going. The newest such plan is named, of the first five open plans.bob warp start --flightplan <N>
approved-unplannedApproved issues exist and no flight plan is open.bob plan build
vision-draftdocs/vision.md still carries the vision-draft marker bob ready --vision wrote (#4033) — the project's vision is the operator's few sentences, and /vision removes the marker once it has worked the sections through. Last, so it never outranks real work./vision

When nothing applies, next is null and the line reads next: nothing to do here right now. An open plan with nothing runnable is nothing to do: bob plan build is suggested only when no plan is open.

next is {finding, command, why}. A blocking-ask also carries ask: {ref, repo, number, title, url}, from which BoB Control draws the ask's Do it. The step is informational: it never moves the verdict or the exit code.

What it reads. A run going wins before anything else is read. Otherwise the project's open asks (todo and brief), its open flight plans and its approved issues are read with three gh issue list calls in the checkout, and the flight-plan engine is asked what is runnable. A read that fails is named in next_unread ([{part, reason}], parts asks, plans, approved or plan #<N>) and its rules are skipped. A forge that could not be read is never a suggestion, and it is never reported as nothing to do without saying so.

The rules live in scripts/status/project-next.js. $BOB_STATUS_FLIGHTPLAN names the flight-plan engine (default scripts/warp-drive/flightplan.js). gh is the one on PATH.

The document ​

json
{
  "schema": "bob-status/1",
  "scope": "host",
  "machine": "farm-01",
  "generated_at": "2026-09-12T21:00:00.000Z",
  "verdict": "drift",
  "ok": false,
  "rituals_overdue": null,
  "sections": [
    {
      "name": "host",
      "engine": "scripts/checks/host-drift.js",
      "schema": "bob-host-drift/1",
      "graded": true,
      "verdict": "drift",
      "document": { "schema": "bob-host-drift/1", "…": "the engine's own document" }
    }
  ]
}

schema on a section names the embedded contract, or null when the engine's document is unversioned (readiness, the audit view rows, rebob). A section whose engine could not answer has verdict: "error", document: null and an error string.

The human rendering is one table — section, verdict (* marks informational), schema, engine — followed by each engine's own human rendering under its section name, and the scope verdict last.

Relation to the v1 commands ​

v1 commandbob status spellingWhat is equal
bob doctor [--project D] [--section S]bob status --scope host [--project D] [--section S]the host section's document and the exit code
bob ready D --dry-runbob status --scope project D --dry-runthe project section's document and the exit code
bob dash status --ports (and the other view flags)bob status --scope fleet --portsthe rendered view and the exit code
rebob D --report --jsonbob status --scope project D --reconcile-previewthe reconcile_preview section's document

The v1 commands keep emitting their v1 documents for existing consumers — make check-host, make ready, the warp-drive preflight (host-preflight.js) and the fleet snapshot read them directly and are unchanged. A /2 bump of any embedded contract is out of scope.

Tests ​

tests/test-bob-status.js proves each row of the table above under a throwaway BOB_HOME / BOB_SOURCE: the section document equals the v1 command's output (minus generated_at) and the exit codes match, through the bin/bob router. It also proves the fleet scope against audit.js view --no-live --json and asserts bin/cdb stays a router. $BOB_STATUS_REBOB names the rebob entrypoint (default scripts/rebob/index.js) so the test can stub the preview, as $BOB_READY_TOOLS does for bob ready. Section 5b drives the project's next step through every rule with gh and the flight-plan engine stubbed, and through a forge that is down.

Related: bob ready · bob CLI reference · Verification System.