Skip to content

Why BoB Holds Rituals

The work nobody files. A requirement waits in the queue until someone works on it. A retrospective, a dependency audit or a security review does not: it is due on a schedule, and if nobody holds it, nothing reminds you. BoB calls these recurring reviews rituals, and treats each run as ordinary tracked work: an issue, an evidence comment, a session and a close. This page explains why each step is shaped the way it is, following one retrospective on the hello-links product. The Holding a Ritual tutorial walks the same run, and takes every command below from this page. Every flag, exit code and output contract is in the Rituals reference.

The approved queue answers “what should the loop build next?”. It never answers “when did we last look back?”, because a look back is not a feature and has no acceptance criteria. Left to memory, it slips: a project gets busy, and the review that would have caught a slow build or a stale dependency never happens.

So a ritual has a cadence instead of a priority. P1M means “due one month after the last run”. before-stable-release means “due before the next stable release”. BoB grades each ritual against that cadence every time you ask, and the grade (overdue, due, upcoming) is what surfaces in /status, in bob ritual due and in the farm’s nightly pass.

A project already has one declaration of what it is: its provision manifest, provisions/<project>.json in the operator repo. The ritual schedule goes in the same file, under _meta.rituals, so the project’s whole declared shape is in one place and every fleet reader already knows where to look.

For hello-links, a monthly retrospective is enough. The product is small and changes fast, so a quarter is too long to wait:

Terminal window
bob operator show
{
"_meta": {
"project": "hello-links",
"rituals": [
{ "name": "retrospective", "cadence": "P1M", "why": "a small product that changes fast: look back every month" }
]
}
}
Terminal window
make -C ~/projects/bigbrain check-rituals

bob operator show names the operator repo, where provisions/hello-links.json lives. The rituals list goes inside the existing _meta object, next to project. check-rituals is the gate: it refuses an unknown ritual name, a cadence it cannot parse, or a kit that is not declared. It runs as part of make check, so a broken schedule never reaches the fleet.

Declaring a list, even an empty one, replaces the defaults. A project that declares nothing still gets rituals: the active or dormant defaults for its liveness, which BoB reads from the project’s last commit.

When was the last retrospective? BoB could keep a date in a file. But a file on one laptop is not visible from the farm, and a file anyone can edit is a date anyone can forget to update. Instead, every held ritual is a GitHub issue labelled ritual-run, and the last run is derived from the newest closed one. Holding the ritual and recording it are the same act.

Terminal window
bob ritual due

Run from inside the checkout, due reads the project’s schedule and its closed ritual-run issues, and prints one row per ritual. A retrospective that has never run is DUE. One whose last run plus its cadence has passed is OVERDUE, and the command then exits 1, so a script can gate on it. When the forge cannot be read, the row is unknown, never “never run”: an outage must not make the whole fleet look overdue.

Why a run is an issue before it is a session

Section titled “Why a run is an issue before it is a session”

Opening the run issue first gives the session somewhere to write. The issue carries a marker (bob-ritual-run/1, with the project, ritual and date), the ritual’s scope, the kit to load, an empty Findings checklist, a Journal section and the delta against the previous run.

Terminal window
bob ritual open retrospective --dry-run
bob ritual open retrospective

--dry-run prints the title, labels and body and files nothing. The real call files ritual: retrospective — hello-links (<today>), labelled ritual-run and ritual:retrospective. A second open of the same ritual is refused (exit 3) and names the run that is already open, so two people, or you and the farm, never hold the same review twice.

Why the evidence is gathered before you sit down

Section titled “Why the evidence is gathered before you sit down”

Most of a review’s first hour is counting: what closed, what is stuck, what it cost, what the memory audit found. That is mechanical, so BoB does it before the session. prepare runs the ritual’s collectors (for a retrospective: the status report, cost and churn, aged issues and the memory audit) and posts the result as one comment on the run issue.

Terminal window
bob ritual prepare 20 --dry-run
bob ritual prepare 20

prepare only reports. It writes nothing but that one comment, and a rerun replaces the comment instead of adding another. A collector that cannot run (a tool that is missing, no checkout on this machine) becomes a named gap in the packet, never a failure: the rest still runs and the comment is still posted.

The farm does the same thing overnight. Each bob tick runs prepare --due: for every farm-eligible ritual that is overdue or due in the next two days, it opens the run if there is none and posts the packet. You can preview that pass for your project:

Terminal window
bob ritual prepare --due --dry-run

The session itself is never farm work. A retrospective needs someone to judge what the numbers mean.

A retrospective needs different tools from a feature: the incident and retrospective commands, a debugger, a lessons extractor, the project historian. Provisioning those into the project for good would crowd every other session. A kit is a named set of registry items applied for one session as a provision overlay and restored afterwards. The retrospective’s kit is incident.

Terminal window
bob ritual run retrospective --dry-run
bob ritual run retrospective
bob provision overlay status
claude

bob ritual run does four things in order. It checks the kit against this machine’s role, and refuses (exit 3) a kit the role forbids. A farm worker, for example, forbids security and vision-planning. It resumes the open run (issue #20 here) or opens one. It applies the kit as an overlay stamped with the run. Last, it prints the evidence summary and the previous run’s findings. Then claude starts the attended session with the kit loaded.

claude --kit incident applies the same kit without a run. Use it for an ad hoc session. For a ritual, use bob ritual run, because the overlay is then stamped with the run, and closing the run restores it.

Why a run closes only with durable outputs

Section titled “Why a run closes only with durable outputs”

A review that ends in conversation evaporates. Two weeks later, nobody can say what was decided or whether it was done. So bob ritual close refuses to close a run until everything it produced is somewhere durable:

  • each finding links its own issue labelled bug, req, task, security or todo, or carries a no-action reason;
  • each lesson or decision is its own /journal issue, linked from the run. The run itself is never labelled lesson or decision: it is a record of what was done, not a piece of knowledge.

In the hello-links retrospective, the packet shows that the link-expiry requirement took three coding cycles, because the seed data had no expired link to test against. That produces one finding and one lesson. The weekly email is still waiting on its todo, which needs no new issue:

/requirement Seed data includes an expired link and a link that expires tomorrow. Found in retrospective #20.
/journal lesson Seed data must cover every lifecycle state a requirement adds, before the requirement is built
Terminal window
bob ritual close 20 --finding 21 --no-action "the weekly email waits on its own todo" --journal 22 --dry-run
bob ritual close 20 --finding 21 --no-action "the weekly email waits on its own todo" --journal 22
bob provision overlay status
bob ritual due

--dry-run runs every check and prints the closed body. On a refusal (exit 3), it names each problem and leaves the run open. The real close rewrites the Findings, Journal and Delta sections, closes the issue and restores the kit overlay that bob ritual run applied. The next bob ritual due shows the retrospective with this run as its last run, and its next due date a month out.

The Delta section compares this run’s findings with the previous run’s: which were resolved, which recur and which are new. It is what makes a monthly ritual more than twelve separate conversations.

Why the portfolio review decides for every project at once

Section titled “Why the portfolio review decides for every project at once”

Every ritual above looks at one project. One question cannot be answered from inside a project: is this project still worth the attention it gets? The portfolio review answers it for all of them together, once a quarter. It is held on bigbrain, never declared by a project, and its run issue has one row per project. In the session, each row gets a status:

Status Means
invest Grow it: new capabilities are expected
maintain Keep it healthy: fixes and upkeep, no new direction
park Stop active work, but keep it safe
archive Retire it

A status only matters if something reads it, so it goes into the manifest, next to the ritual schedule it changes. A changed status also needs its reason on record, as a /journal decision issue linked from the run’s row:

/journal decision hello-links moves from maintain to invest: it is the product we are growing this quarter
Terminal window
bob ritual decide hello-links park
bob ritual decide hello-links invest --apply

Without --apply, decide only reports. For park, the report shows what parking would cost: _meta.rituals is replaced by the dormant schedule (a dependency audit every six months and a docs review every three), so the monthly retrospective, and the farm’s evidence for it, stop. A parked project stays safe but stops asking for your time. archive goes further: it also sets _meta.active: false, and an archived project has no rituals at all.

invest with --apply writes _meta.portfolio: "invest" into the manifest in the operator repo and leaves the declared retrospective alone. Commit that change in the operator repo. The portfolio run’s close checks that each manifest declares the status its row decided, so a decision that never reached the manifest cannot be closed as done.