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>
- Parses the TOML document
- Reads
kind→ finds the plugin by name - Gates
versionagainst the plugin's declared schema version - Validates the payload against the plugin's
planSchema - 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 v1Absent 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 deferdefer 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 titlegates = "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 = trueunder 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 fixconvoy
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.