Skip to content

A Week in the Life of a BoB Product

The layer above /requirement. A Day in the Life starts from a requirement someone has already written. This page starts a week earlier, with a product that has no requirements yet, only an idea. It explains why each layer BoB puts above the loop exists: the vision, the capability, the requirement, the groom, the flight plan, the integration branch and the release. The Your First Product with BoB tutorial walks the same week step by step, and takes every command below from this page.

The week follows one small product, hello-links: a link shortener on Cloudflare Workers. You post a long URL and get a short code back, and anyone who opens the short code is redirected. It is small enough to ship in a week, and it still raises the real product questions. Who is it for? What will it deliberately not do? What does “done” mean for each part?

The project starts the way any Workers project does, with Cloudflare’s scaffolder, and then gets wired to BoB in one command, bob ready --new — the steps BoB in One Day takes one at a time. Provisioning detects the Workers stack from wrangler.jsonc, so the Cloudflare skill arrives with the manifest instead of being added by hand.

Terminal window
bob doctor
cd ~/projects
npm create cloudflare@latest hello-links
cd hello-links
bob ready --new
git push -u origin HEAD

A requirement answers what to build next. It cannot answer why this product at all, and a loop that only ever sees requirements will build whatever it is handed. The vision (docs/vision.md) is a living document, not an issue. It records who the product serves, the problem it removes, where it is going, and, most usefully, its non-goals. When a later requirement drifts (“add user accounts”), the non-goal (“single-owner, no accounts”) is the written reason to refuse it.

/vision interviews you and writes the document. It is the only step in the week with no issue attached, because a vision is never finished and never closed.

/vision

Why capabilities sit between the vision and the work

Section titled “Why capabilities sit between the vision and the work”

A vision is too broad to build, and a requirement is too narrow to plan around. The capability is the level in between: one user-visible ability, with user stories and success metrics, that a handful of requirements will deliver. hello-links has two:

  • Short links: create a short code for a URL, and follow it.
  • Manage links: protect the API and see what each link has done.

/capability files each one as a GitHub issue labelled cap. It also gives the capability a workstream label (area:<slug>) that its requirements inherit, so the whole tree can be picked up as a unit.

/capability Short links
/capability Manage links

Approving a capability is a scope decision, not a build order. The approved label on a cap says “this ability belongs in the product, break it into requirements”. Nothing is coded because a capability is approved. The loop picks up only approved requirements.

Terminal window
gh issue edit 1 --add-label approved
gh issue edit 2 --add-label approved
gh issue list --label cap

Why requirements carry “Part of” and “Depends on”

Section titled “Why requirements carry “Part of” and “Depends on””

A requirement is the unit the loop builds. It has testable acceptance criteria as checkboxes, a priority, and two kinds of link that later steps read mechanically:

  • Part of #NN names the parent capability. The capability’s status is derived from its children: it is in progress while some are open, and closed as completed once all of them are. Nobody sets it by hand.
  • Depends on #NN names a requirement that must land first. The flight plan turns these lines into ordering edges, so independent requirements can run in parallel and dependent ones wait.

hello-links gets four requirements:

# Requirement Part of Depends on
3 Create a short link (POST /api/links) #1 —
4 Follow a short link (GET /:code, 302 or 404) #1 #3
5 Protect the API with a bearer token #2 —
6 Count visits and show them (GET /api/links/:code) #2 #4, #5
/requirement Create a short link (POST /api/links). Part of #1.
/requirement Follow a short link (GET /:code, 302 or 404). Part of #1. Depends on #3.
/requirement Protect the API with a bearer token. Part of #2.
/requirement Count visits and show them (GET /api/links/:code). Part of #2. Depends on #4 and #5.

Four requirements written on the same afternoon disagree with each other in small ways. One names a table links and another names it urls, one assumes JSON errors and another plain text, and a dependency that is obvious to the author is never written down. /groom reads a whole cluster together, reconciles the bodies, marks the dependency chain, checks each requirement is ready for the loop, and reports what it changed. It never builds anything.

/groom area:short-links
/groom area:manage-links

Without a plan, the loop rediscovers “what next?” from the approved queue at every boundary. A flight plan derives the order once and writes it down as an issue labelled flight-plan. It lists every requirement in run order as a checkbox, with its depends_on edges and the integration branch the week’s work accumulates on. You can read it, edit it, and hold the loop to it.

Terminal window
bob plan build --from approved --branch week/hello-links --name "hello-links week one"

bob plan status is the week’s checkpoint. It says which items are runnable now, which are waiting on a dependency, which are gated by a human todo, and how many waves the rest will take, where a wave is a set of items that can run in parallel.

Terminal window
bob plan status 7

The loop can only prove a requirement works if it can run the product. dev.json at the project root declares how: the server command, the port, the migrations, the seed data and the test users. bob dev up brings all of it to a testable state, and it writes a dev.json for you when there is none, from what it detects in the project. During a run, warp-drive calls the same command before the first chunk and checks health between chunks. A dev server that will not come up blocks the loop, the same way a failing test does.

Terminal window
bob dev up --verbose
bob dev health

The flight plan’s first wave holds two requirements that share no dependency: creating a link and protecting the API. Running them one after the other wastes a day. bob fork gives each one its own git worktree, tmux window and warp-drive session, scoped to exactly that issue, so neither session can wander into the other’s work. --from-flightplan takes the wave from the plan, so an item that is still waiting on a dependency is never started.

Terminal window
bob fork fork --from-flightplan 7 --dry-run
bob fork fork --from-flightplan 7

When both sessions have landed their requirement, the next wave is unblocked. The worktrees are then residue, and dropping them never touches the commits they produced.

Terminal window
bob fork status
bob plan status 7
bob fork drop --all

Why the rest accumulates on an integration branch

Section titled “Why the rest accumulates on an integration branch”

The remaining two requirements build on each other: visit counts need redirects. Merging each one to main separately would leave main for a while with visits that are recorded but have nowhere to be shown. The flight plan therefore declares an integration branch. A run started with --flightplan opens that branch automatically, merges each finished requirement into it instead of into main, and keeps it fresh against main as it goes.

Terminal window
claude -a3
/warp-drive --flightplan 7
Terminal window
bob warp status --watch

bob warp finalize ships the accumulated branch to main in one step. At Level 3 it freshens the branch, merges it and pushes, and at Level 2 it opens one consolidated pull request instead. It refuses to run while a requirement is still in progress, and it leaves the branch intact if anything fails.

Terminal window
bob warp finalize --dry-run
bob warp finalize

Once every requirement under a capability is closed, the capability is complete. Nobody closes it by hand. /groom is the reconciler of last resort: it derives each capability’s status from its children, and closes a finished one with the completed label.

/groom
Terminal window
gh issue view 1
gh issue view 2

Merging to main is not a release. A release is a named, reproducible point that someone else can install or roll back to. For a BoB-managed project it is a semantic version computed from the Conventional Commit messages the loop wrote, a CHANGELOG.md section generated from the same commits, a vX.Y.Z tag, and a GitHub Release carrying that section. bob release does all four in one command, runs against whichever repository you are in, and refuses to run from anything but a clean, pushed main.

Terminal window
git checkout main && git pull
npm install --save-dev commit-and-tag-version
npm pkg set scripts.release=commit-and-tag-version
git commit -am "chore: add the release script" && git push
bob release --dry-run
bob release --channel stable

Why the week ends with a retrospective and a journal

Section titled “Why the week ends with a retrospective and a journal”

A week of loop runs produces a lot of reports, but a report records what happened, not what anyone learned from it. /retrospective turns the week into a summary issue (labelled session-summary) and its action items into retro-action issues that next week’s retrospective checks. /journal files the knowledge that should outlive the week: a decision explains a choice and the alternatives it beat, and a lesson explains what surprised you and how to avoid it. Both are their own issues, never labels on a report, so gh issue list --label lesson stays readable.

/retrospective
/journal decision bearer-token-not-accounts
/journal lesson plan-dependencies-before-forking
Terminal window
gh issue list --label decision
gh issue list --label lesson
gh issue list --label retro-action