learn playbook
Compress retro findings into durable, actionable rules: rule records in the project's specs tier that reach the playbooks, steps, agents and skills they target, plus one-line project facts in the repo's own CLAUDE.md.
Learning is a playbook, not a skill — it is driven by /playbook:
/playbook learn
Why
The retro playbook records what happened on a set of shipped plans. Learn turns those observations into rules booping obeys on every future plan; without it, retros pile up unread and the same friction repeats sprint after sprint.
The unit of work is a standalone retrospective — retrospectives/{slug}.md at awaiting-learning, never a plan directory. It reads that file and each plan in its plans: list, proposes a small set of updates, and writes nothing until you confirm the whole set. The retrospective then walks awaiting-learning → done; the plans it covers are done before the run starts and stay done throughout.
What it does
Six steps, in dependency order:
| Step | What it does |
|---|---|
intake |
Resolve the retrospective and its working set; validate the entry status |
extract-candidates |
Decompose the retro into atomic rules, each routed to a single destination |
dedup-sweep |
Filtered read of the rules of all three specs tiers and the repo CLAUDE.md — update vs create vs covered vs conflict |
review-table |
Present the unified review table; you accept, reject rows, or add your own |
write |
Write the accepted items, one pass per target type |
transition |
Close the retrospective at done and commit |
The run workdir is the vault root and every booping state / booping transition call names retrospectives/{slug}.md with --playbook learn, so a stopped run is resumable.
What it writes
Two destinations, and every accepted finding lands in exactly one of them. A candidate that would span both is decomposed into one row per destination before writing.
- A rule record in the project specs tier at
rule/{NNNN}-{slug}.mdunder the tier root — each carrying atargets:list naming the playbooks, playbook steps, agents and skills the rule applies to, and abrief:of one or two lines that is what those targets receive. A rule that should only reach one worker agent or one skill is a rule targeting it, not a separate file. When the project has no specs tier yet, the run creates it first withbooping specs init --tier project;{NNNN}is the next free number frombooping specs next rule. After writing,booping specs check --tier projectmust report no error on the new records. - The repo's own
CLAUDE.md— a finding that is a project fact aiding fresh-agent project understanding (a layout path, a CLI command, a code-side convention) becomes a one-line bullet there. Those edits stay in the repo working tree for you to commit, and never touch the global~/.claude/CLAUDE.md.
Learn writes only the project tier. A finding the shipped (core) or your global tier already covers is shown as covered and not written; a finding that departs from one of their rules is written as a project rule that opens with a [clarify] sentence naming the departed rule, so you can record the departure in an ADR. See Specs → Tiers for where each tier lives.
What a learnt rule looks like
---
type: rule
title: "Name every artifact path absolutely"
summary: "Briefings and reports name vault and repo files by absolute path, never relative to a guessed cwd."
created_by: claude
created_at: 2026-10-14
updated_at: 2026-10-14
status: accepted
targets:
- groom/draft-plan
- agent:booping-developer
brief: "Name every artifact by absolute path; a relative path breaks once the worker's cwd differs."
sources: [vault/retrospectives/202610031129_flat-paths.md]
---
## Rule
Name every vault and repo file by its absolute path in briefings and reports.
**Example**: A briefing that said `plans/x/index.md` sent the worker to read a file under the repo, not the vault.
## Follows from
- [Retro: 202610031129_flat-paths](../../retrospectives/202610031129_flat-paths.md)
Every target named in targets: receives the brief: together with a link to the record, in a ## Rules section of its rendered body — see Playbooks → Rules. created_by: claude marks the record as learnt; learn sets a review level only with its evidence, and never accepted without your words quoted. sources: is present only when the vault lives inside the repo; ## Follows from links the value the rule follows from when one fits, else the retrospective.
Targets are fetched, never guessed
A rule with no valid targets: reaches nothing, so routing has to be exact. Before proposing, the run reads the target space: a table of contents of every discovered playbook, then a targets fetch for only the playbooks a candidate touches, listing their step names and addressable agents. Entries come from that fetch, and the exact targets: list each rule carries is assigned in the review table before the record is written.
Target forms are {playbook}, {playbook}/{step}, agent:{id} and skill:{name} — exact names only, no globs.
Update-vs-create sweep
Before drafting proposals, dedup-sweep reads the rules of the three specs tiers and the repo CLAUDE.md to check whether each candidate is genuinely new. A candidate that duplicates or refines a project rule becomes an update to that record, edited in place, rather than a near-duplicate. A candidate a core or global rule already holds is covered; one that contradicts a rule is flagged a conflict for you to resolve in the review table. That is what keeps the rule set from accumulating redundant or self-contradicting rules over many sprints.
The review table
Every candidate appears in a single review table before anything is written — one row per landing site, with columns #, Target, Type, Targets, Brief, Rule and Example:
| Column | Holds |
|---|---|
# |
A stable row number, used to reject rows. |
Target |
Where the row lands: rule/{NNNN}-{slug}.md for a new rule (the number is provisional until write), the existing rule/NNNN for an update, CLAUDE.md, or the covering rule's ref. |
Type |
rule, claude-md or covered — a covered row is shown, never written. |
Targets |
The exact targets: entries the rule is written with, drawn from the fetched target space; — for claude-md and covered. |
Brief |
The one or two lines injected into each target; — for claude-md and covered. |
Rule |
One imperative sentence, one concern. |
Example |
One concrete sentence — what went wrong or right. |
You accept all, reject rows by number (e.g. 2 5), or append your own row in the same shape; a row of yours carrying two rules is split into two before writing. Nothing is written until you respond; table acceptance is the consent for the whole write pass — there are no per-row prompts.
Best practices
Review every proposed update
The run proposes — you commit. Read each proposed rule — its brief above all, since that is what the targets receive — and each CLAUDE.md bullet before approving. Three things to check:
- Actionability. A rule should tell a future run what to do, not just describe the past. "Be careful with migrations" is a description; "Run
manage.py migrate --planbefore applying any migration generated this session" is a rule. - Right home. A rule that only matters for one step targets that step, not one targeting the whole playbook where every run pays the context cost.
- Direction. A rule should point forward. If the proposed update is really a complaint about the past plan, send it back — it is retro material, not rule material.
Shit in, shit out
Proposals come from whatever the retro says. Accepting them blindly produces conflicting and useless rules: redundant overlaps, contradictions with existing rules, vague platitudes that fire on every plan and steer none. Review every proposal as if you were writing it — because once you approve, you are.
The second half of the same warning as retro: a sloppy retro produces sloppy rule proposals, and a rubber-stamped write pass puts those proposals on disk where they steer every future sprint.