bro

Plans

Structured TOML input — one envelope, per-command schemas.

Commands that take structured input share one envelope: a TOML file with a kind that routes to the owning plugin. Each kind's schema carries a version — version pins the contract the file was written against, and bro plan lists what the installed bro understands.

kind = "retrospect"          # routes to the retrospect plugin
version = 1                  # optional — the schema pin

[retro]
what = "the deploy broke"    # required — one line
why = "CI ran a different Node than prod"
scope = "project"

[[actions]]
title = "pin the Node version in CI"
sink = "workaround"          # backlog | memory | agentic-documents | …

bro run <plan.toml>

  1. Parses the TOML document
  2. Reads kind → finds the plugin by name
  3. Gates version against the plugin's declared schema version
  4. Validates the payload against the plugin's planSchema
  5. Executes via the plugin's runPlan

Unknown kinds are rejected with the list of known ones; a plugin without planSchema/runPlan says so instead of guessing. Validation is aggregate — every problem in the file is reported at once, not one error per run.

bro retrospect schema prints the commented template for its kind.

version — the schema pin

version is optional and must be a positive integer at or below the kind's current schema version — bro plan lists them:

$ bro plan
debt         v1
act          v1
convoy       v1
next         v1
drill        v1
retrospect   v1

Absent means "whatever the installed schema is" — every unversioned plan keeps working. A pin newer than this bro understands is rejected with the supported version named, never silently misparsed. The gate only looks upward: a pin at or below the installed version passes and the kind's planSchema decides what it accepts — whether an older pin survives a schema advance is the schema's contract, not the gate's.

bro plan validate <plan.toml>

Runs the full bro run pipeline — TOML parse, kind routing, the version gate, planSchema — and stops before runPlan. Valid files print ok — <kind> plan, schema v<N>; invalid ones exit 1 with the same aggregate error bro run would report. Validate before pushing a plan into a queue: the executor and the checker share one resolve step, so they can't disagree.

Kinds

retrospect

Retro plans — [retro] what/why/scope/wtf/evidence plus [[actions]] fanned out to prevention beads by sink.

act

Batch thread verdicts on the open PR — the fix/reject/defer triage as one plan instead of N act resolve/reply calls:

kind = "act"
pr = 66                      # optional — names the PR in defer beads

[[threads]]
thread_id = "PRRT_..."
action = "resolve"           # resolve | reply | defer
comment = "fixed in abc123"  # required for reply; optional elsewhere

[[threads]]
thread_id = "PRRT_..."
action = "defer"             # → debt bead + reply + resolve
title = "the bead's title"   # required for defer

defer creates a debt-labeled bead linked by --external-ref to the thread — if the bead can't be created, the thread is not resolved. A failed verdict doesn't abort the rest; failures are listed at the end.

next

The backlog-loop selection as a plan — bro run next.toml computes the queue under the plan's filters and ordering, claims up to limit beads, and emits one work order per pick:

kind = "next"
limit = 3                  # beads to claim — default 1
order = "priority"         # priority | oldest | newest
claim = false              # selection only — nothing is claimed
gates = "allow"            # forbid (default): gates surfaced, never
                           #   claimed; allow: HUMAN GATE beads join the queue
json = true                # machine-readable result

[filters]
types = ["task", "bug"]    # issue_type allowlist — epics and molecule
                           #   steps stay excluded regardless
max_priority = 2           # claim only P0..P2
match = "schema|plan"      # case-insensitive regex over the title

gates = "allow" is the pre-authorized verdict — the plan itself is the human's go-ahead, so gate beads become claimable work.

drill

A declared descent tree — the investigation shape materializes as open drill frames, each filled later via drill up --result:

kind = "drill"
title = "why does the cache miss"   # root frame — required

[[steps]]
title = "check the parser"          # child of root

[[steps]]
title = "narrow the repro"
under = 0                           # child of steps[0] — nested trees
ephemeral = true

under indexes an earlier step (no forward refs, no self-parenting). Steps also take description, priority, type, ephemeral.

debt

Batch triage verdicts for the review-debt ledger — replaces N interactive bro debt set calls:

kind = "debt"

[[verdicts]]
thread_id = "PRRT_..."
status = "wontfix"           # open|claimed|done|wontfix|duplicate
notes = "infra flake, not ours"

[[verdicts]]
thread_id = "PRRT_..."
status = "done"
fix_pr = 58                  # the PR that landed the fix

convoy

A pour plan — which molecules to bring into existence and under what gate policy. bro run materializes each entry; execution then proceeds through convoy next/done as usual.

kind = "convoy"
gates = "allow"                # plan-wide default: allow | forbid

[[molecules]]                  # pour a registered formula/proto
formula = "debt-pipeline"
[molecules.vars]
scope = "--last 20"

[[molecules]]                  # ad-hoc molecule — steps inline
title = "ship v0.1"
gates = "forbid"               # reject if any step is a human gate

[[molecules.steps]]
id = "tag"
title = "cut the tag"
type = "agent"                 # "human" declares a gate

[[molecules.steps]]
id = "publish"
title = "npm publish"
type = "agent"
needs = ["tag"]

gates = "forbid" is the unattended-run contract: inline steps are checked at parse time, formulas via bd formula show before anything is poured. Inline steps transpile to a generated formula so declared agent/human types survive the pour. needs must reference declared sibling ids — unknowns, self-refs, and cycles are rejected.

Why TOML

Diff-friendly, comment-friendly, and forgiving for agents writing it by hand — a plan is usually drafted in chat, pasted to a file, and run.

On this page