Brain · Lessons
Turn a repeated mistake into a check
A lesson is one recurring problem written down as a trigger, what went wrong, and the fix. Active lessons re-render a managed block in every tracked repository's CLAUDE.md and AGENTS.md, so the next agent reads them. A lesson can also carry a guard, and the guard is the only part that catches anything.
For: the engineer who keeps hitting the same failure, and whoever decides what is allowed to stop a build
What it does, and why it helps
The store is one JSON row per lesson in the repository brain, at lessons/lessons.jsonl. You write a row by hand with + Add lesson, or a producer writes one for you: accepting a rule on the Recurrence rules tab mints an inactive lesson, and Save as lesson on the Accountability ledger, Incidents and Agent traces tabs mints one from a chain that was already attributed. Every write to the file re-renders the managed block between the locked markers repoops:lessons:start and repoops:lessons:end in each tracked repository's CLAUDE.md and AGENTS.md. Content outside those markers is never touched, and re-running the same lesson set writes byte-identical output.
The block is a ranked window, not the store: at most 12 lessons and 1,500 bytes, with a line saying how many of the active set fit. So prose alone is a suggestion in a prompt. The part that can catch something is the guard: a deterministic match of one of four kinds, held at warn until a named person promotes it, and promotion is dormant unless an operator armed it. Three distinct catches with no false positive are the bar; three distinct false positives demote a blocking guard back to warn with nobody asked. Whether any of it held is read from later captured sessions, and when there are none the answer is that recurrence is not assessable rather than a number.
The pain. The same failure arrives for the third time. Somebody wrote it down after the first one, in a file nobody is reading at the moment it matters, and nothing in the loop checks for it.
The point of view. Writing a lesson down is not prevention. Prevention is a deterministic check that fired on the thing the lesson describes, and whether the fix held is read from later captured sessions rather than assumed from the writing.
What gets easier. Reading the store. Four panes keep what is live apart from what is queued, what someone rejected, and what stopped being true, so a machine-minted draft is never mistaken for a rule a person accepted.
When it helps. A repository whose brain has a lessons store and a CLAUDE.md at its root, on the desktop app. On the hosted side, once the team's desktop publishes a brain snapshot that carries the store.
Its limits. A lesson with no guard reaches an agent only as prose inside a capped block. A guard cannot block until an operator arms promotion and a person promotes it. The weekly rate reads measuring below three combined observations, and the whole series reads unmeasurable when the recurrence join matched no cluster. A retraction cannot be undone.
Understand it in 30 seconds
Read the narration
- 0:00 The same mistake comes back.
- 0:02 Someone already wrote that lesson down last time.
- 0:06 A note in a file stops nothing.
- 0:08 RepoOps gives a lesson a checkable guard.
- 0:13 Every guard starts advisory.
- 0:15 Three clean catches, no false positives, a clean replay.
- 0:19 Then a person may let one block a build.
- 0:23 Recurrence is read from later captured sessions, never assumed.
- 0:27 The rate can fall.
Synthetic example. Read the guide
Where to find it
Where to find it
- Desktop:
localhost:4000, then Memory in the sidebar, then Lessons under All tools, in the Evidence readers group. - Hosted:
repoops.ai/team/lessons, from Memory in the sidebar, then Lessons under All tools, in the Evidence readers group. - Keyboard: ⌘ K, then type “Lessons”.
When to use it
A lesson the machine wrote, waiting on a person
Situation. The nightly reflector distilled a lesson from the week's sessions. It was minted inactive, so it is in the queue, not in anyone's context.
What you do. Open the Lessons tab and read the queued for review pane, which is open by default and ranked by how often the friction was observed. Press Activate, or Deactivate to record that you read it and said no.
What you see. The pane says the machine wrote these and no one has ruled on them yet, and that they reach nothing: not a briefing, not the managed CLAUDE.md block, not any session's context. A lesson seen two or more times is marked as clearing the bar, which is a sort order and a prompt, never a rule.
What it establishes. Activating re-renders the managed block in every tracked repository at once. Whether the lesson then prevents anything is a separate question, answered by a guard and by later sessions.
Making a guard blocking
Situation. A lesson carries a diff-pattern guard at warn. It has fired on real diffs and nobody has marked a firing wrong.
What you do. Press Replay on the card first: it runs the guard's own matcher over the tree and the recorded incidents and records how broadly it matches. Then press Make blocking and enter your name.
What you see. The guard block on the card shows the severity, the counts, and the replay. When the bar is not met the button is disabled and carries the reason, for example that the guard has caught 2 of the 3 distinct times required before it may block.
What it establishes. The guard fails the check instead of reporting. Three distinct false positives demote it back to warn automatically, because demotion loosens and promotion tightens.
A lesson that stopped being true
Situation. A rule was pinned to a toolchain that has since been replaced. The lesson is not wrong about the past, but it should stop reaching sessions.
What you do. Press Retract on the card and give a reason, which is required. The date may be in the past, and it should be the day the rule stopped being true. To schedule a known end date instead, set an expiry.
What you see. The card moves into the retracted pane, which says these stopped being true on the date shown and are kept, not deleted. The row leaves the managed block, the briefing, and every surface that answers what is true now.
What it establishes. What was true in June and what RepoOps believed in June both stay answerable. Retraction is not reversible; deactivation is the reversible verb.
Before you start
- Supported versions
- RepoOps desktop v0.3.1, the release this guide was read against. The managed block needs a CLAUDE.md at the repository root; without one, the re-render after a write is skipped and the store is unaffected.
- Where it runs
- Local: Memory, then Lessons under All tools, in the Evidence readers group. Hosted: Memory, then Lessons under All tools, in the Evidence readers group (repoops.ai/team/lessons), renders the published lessons.jsonl from the team's brain snapshot, plus a Lessons that travel rollup and an Across repos feed when more than one repository has published.
- Permissions
- Local: no role gate. Anyone using the app can add, edit, activate, deactivate, retract or delete a lesson. Promoting a guard to block and retiring a guard each record who did it, and both refuse without that name. The hosted page is session-authed, scoped to the team from membership and to the repositories that member may see.
- Connections
- None for the local store; it is files on disk. The hosted page needs brain publishing turned on (the brainpublish.enabled toggle) and a bound device.
- Plan
- Free local. The pricing capability map lists the flat-markdown brain with lessons written back to CLAUDE.md at the solo tier, which is free forever. Reading the published store on the hosted dashboard follows the hosted dashboard tiers.
Configure it
- Write the first lesson, or accept one.
Press + Add lesson and fill in Trigger, What went wrong and Fix, then Create. All three are required and a blank one is refused. The other producers write the same three fields: accepting a rule on Recurrence rules answers Promoted as an INACTIVE lesson and names the id, and Save as lesson on the Accountability ledger, Incidents or Agent traces asks you for the fix rather than inventing one.
- Triage the queue before anything else.
A lesson the machine minted is inactive and unreviewed, so it reaches nothing. The queued for review pane ranks the queue by observed recurrences, most corroborated first, and marks anything seen twice or more. Activate what belongs in front of every session; deactivate the rest so the decision is recorded.
- Let the managed block re-render, or force it.
Every write to lessons.jsonl re-renders the block in that repository's CLAUDE.md and AGENTS.md. The re-render is best effort and never fails the write. Sync agent files on the toolbar runs it across every tracked repository and reports how many files it updated.
- Give a lesson a guard, and leave it at warn.
Four kinds only, all deterministic: error-signature (a production error matching a lesson already written, which is recurrence), diff-pattern (the same mistake being written again, which is prevention), content-pattern (text at the tool-call boundary), and regression-test. A new guard is warn unless a promotion says otherwise.
- Replay before you promote.
Replay runs the guard's own matcher over the tree and the recorded incidents and records how broadly it matches, including what it would have caught wrongly. It is a measurement and can be re-run; promotion is a decision and happens once. A replay older than 90 days reads stale.
- Only then, promote.
Make blocking needs three distinct catches, no recorded false positive, a name, and REPOOPS_LESSON_PROMOTION armed. Without the flag the whole promotion path is dormant, because blocking is the only thing in the loop that can stop a developer's work.
- Retire, deactivate or retract, and know which you mean.
Retire this guard removes the machine-checkable half and leaves the lesson readable. Deactivate takes the lesson out of the managed block and is reversible. Retract records that it became false on a date and is not. Delete removes the row from the file.
| Setting | Where | A sensible choice | Why it matters |
|---|---|---|---|
REPOOPS_LESSON_GUARDS | the data directory's .env, read by scripts/check-lesson-guards.mjs | unset, which leaves the gate on | The value off kills the whole gate, including the advisory warn firings, and the skip is logged rather than silent. |
REPOOPS_LESSON_PROMOTION | the data directory's .env | unset until one guard has earned it | 1, on or true arms promotion from warn to block. Unset, no guard in the repository can block anything. Not the same switch as REPOOPS_LESSON_GUARDS. |
REPOOPS_LESSON_DECAY | the data directory's .env | unset (off) | 1 arms idle decay. The nightly pass computes the disposition either way and writes none of it until this is set. Run npm run lesson-decay-report first and read the would-retire list. |
REPOOPS_LESSON_CONFIDENCE_RANK | the data directory's .env | unset (off) | 1 makes stored confidence a ranking key for the managed block. Run npm run lesson-confidence-report first: it prints which lessons would enter and leave the block in every tracked repository. |
REPOOPS_LESSON_BLOCK_SYNC_OFF | the environment, read by lib/lessons.mjs | unset | 1 stops the managed block re-rendering after a lesson write. The store still changes; the manuals stop following it. |
active | the card footer on the Lessons tab, Activate and Deactivate | active for a rule you want every session to read | Only an active lesson is ranked into the managed block, and deactivating keeps the history. |
guard.severity | the card's Guard block, Make blocking | warn until the bar is met | warn reports on a future diff and blocks nobody; block fails the check. |
valid_to | Retract on the card, or the expiry route | no end date unless the rule has one | Retraction closes the interval retroactively and cannot be reopened. A forward-dated expiry can be cleared by setting it to null, because a plan may change while a claim about the past may not. |
What you should see
The normal case
Configuration. A repository with a CLAUDE.md, no environment flags set, a few active lessons.
Expect. The tab lists the active pane twenty rows at a time behind Load more, with a queued for review pane above the deactivated drawer. Each card shows the trigger, What went wrong, the fix, and Created with its date.
Verify. Open the repository's CLAUDE.md and find the block between the locked markers. If the store holds more than fits, the block opens with a line naming how many of the active lessons it is showing and pointing at lessons/lessons.jsonl and this tab.
A guard that is not promotable yet
Configuration. A lesson carrying a diff-pattern guard at warn, with fewer than three distinct catches.
Expect. The guard block shows the severity, the counts, the replay if one has been run, and a disabled Make blocking carrying the shortfall.
Verify. The scorecard names which number fell short rather than saying it is not allowed. Marking a firing wrong three times demotes a guard that is already blocking; it does not need a promotion to have happened first.
The rate has nothing to measure
Configuration. A repository with few prevention events and few captured sessions in the window.
Expect. Weekly prevention rate reads n/a for the current figure, and a week under three combined observations renders as measuring rather than as a percentage. If the recurrence join matched no cluster at all, every week in the series reads unmeasurable with the reason attached.
Verify. Recurrence not assessable: no captured sessions in the window is the honest answer, and it is the one the page gives. A merged fix and an active lesson are not evidence that the defect cannot recur.
Data and cost
- What is captured
- One JSON row per lesson in the repository brain's lessons/lessons.jsonl: the trigger, what went wrong, the fix, the source defect pointer, times_recurred, prevented_count, active, the guard, provenance, a serial, the valid interval, and a stored confidence. The whole file is rewritten on each edit, because these files hold dozens of rows rather than thousands.
- Who can see it
- Local by default. With brain publishing on (the brainpublish.enabled toggle plus a bound device), lessons/lessons.jsonl is published to the team through the redaction pass and renders on the hosted Lessons page, scoped to the member's repositories. Share this repo's armed patterns sends a security pattern as its rule, the channel it arrived through, its weight, its tier and its counts, and one that arrives carrying a matched excerpt is refused rather than cleaned.
- How long it is kept
- Kept. No retention window, no purge pass, no knob. A retracted lesson is kept and not deleted, so what RepoOps believed at the time stays legible. A lesson retired by idle decay becomes inactive plus a reversible audit file under .claude/brain/retired/, and a lesson that has ever prevented a real recurrence is never retired for idleness however cold it goes.
- What leaves the machine
- Nothing leaves the machine unless brain publishing is on or the pattern share button is pressed. The tab makes no model call. The layer classifier is a keyword pass with a published rubric, and a guard proposal carries the detector's own pattern, escaped from the culprit line, as an editable suggestion that no model picks.
- What it costs
- No module meters a cost for writing, ranking or syncing a lesson. A lesson folded from a graph run carries that run's measured cost, and it is an honest null when the run was unpriced, never zero. Run the benchmark now measures on demand, promotes nothing and writes no ledger row.
When the result differs
| Symptom | Likely cause | Next action |
|---|---|---|
| The tab says to pick a repo from the dashboard. | The page was opened with no repository in the query string. | Open Lessons from the dashboard with a repository selected. |
| No lessons yet, on a repository that clearly has failures. | Nothing has written one. Lessons are distilled one per recurring problem, not one per pull request. | Press + Add lesson, save one from a chain on the Accountability ledger, Incidents or Agent traces, or accept a rule on the Recurrence rules tab. |
| A lesson is active in the store but missing from CLAUDE.md. | The block is bounded by both a 12-lesson cap and a 1,500-byte budget, and it holds the top-ranked window. | Read the Showing the top N of M line at the top of the block. Deactivate what no longer earns a slot, or shorten the fix, which is clipped at a sentence boundary. |
| The block is not re-rendering at all. | The repository has no CLAUDE.md at its root, or REPOOPS_LESSON_BLOCK_SYNC_OFF is set to 1. | Add the file or unset the variable, then press Sync agent files to re-render every tracked repository. |
| Make blocking is disabled. | Fewer than three distinct catches, a recorded false positive, or promotion not armed. | Read the reason on the button. Run Replay, wait for real catches, and set REPOOPS_LESSON_PROMOTION when a guard has earned it. |
| A guard keeps firing on the wrong thing. | The pattern is broader than the lesson it belongs to. | Press Mark a firing wrong. Three distinct false positives demote a blocking guard to warn. To stop one guard for good, press Retire this guard, which leaves the lesson readable. |
| Which layer owned the failure shows a large unclassified share. | The classifier is a keyword pass that needs two matching rules and a score of two, and a tie between two layers resolves to unclassified rather than an arbitrary winner. | Correct a lesson's stored layer with a PATCH to its row. Expect the unclassified share to stay large: most entries in a mature error log are ordinary defects rather than failures of a layer. |
| The hosted Lessons page is empty. | No published brain snapshot in the team carries a lesson file. | Turn on brain publishing in the desktop app and wait for the next snapshot. Ask the brain searches whatever lesson knowledge is already published. |
- Disable
- Deactivate the lesson to take it out of the managed block and keep its history. Set REPOOPS_LESSON_GUARDS to off to silence every guard including the advisory firings, leave REPOOPS_LESSON_PROMOTION unset so none can block, or set REPOOPS_LESSON_BLOCK_SYNC_OFF to 1 to stop the manuals following the store.
- Roll back
- Partly. Activate undoes a deactivation, and an expiry can be cleared by setting it to null. Retraction is not reversible by design: the invalidation keeps the earlier of two stops, so a claim about the past cannot be erased by a later, sloppier call. Demoting a promoted guard is the automatic path, through three marked false positives, or retire the guard by hand.
- Revoke access
- Turn the brainpublish.enabled toggle off on the desktop and disconnect the device, which forgets the binding and revokes its token. No route withdraws a lesson from a snapshot that has already been published; the next publish is what replaces the file.
- Delete
- Provided, locally. Delete on the card, or DELETE /api/lessons/<id>, removes the row and the file is rewritten without it. Nothing deletes the published copy on the hosted side, and the retired audit files under .claude/brain/retired/ are ordinary files you remove yourself.
Related tasks
Maintenance evidence
- Feature id
lessons(spine leaflessons)- Owner
- Accountability Loop, step A2 (docs/plans/accountability-a2-lessons.md), with the executable-guard (PAB.2), idle-decay (docs/plans/lesson-decay-and-semantic-dedup.md) and retraction (docs/features/lesson-retraction.md) extensions. Guide: LDG-0717.
- Supported product version
- RepoOps v0.3.1
- Last verified
- 2026-09-15, read against origin/main at 52366bb6d; labels read from the served tab source (public/lessons.html and public/lib/pagination.js), the caps and flags read from lib/agent-md-blocks.mjs, lib/causal/guard-verdicts.mjs, lib/causal/guard-quality.mjs, lib/brain-cleanup.mjs and .env.example, and the hosted page read from website/app/team/(home)/lessons/page.tsx.
- Example fixtures
- No fixture file for the store; the lesson shapes are inline in lib/lessons.test.mjs, lib/lessons-panes.test.mjs, lib/lessons.pending.test.mjs, lib/lessons.decay.test.mjs, lib/lessons.supersession.test.mjs, lib/routes/lessons.retract.test.mjs, lib/routes/lessons.layer-rollup.test.mjs, lib/causal/guard-verdicts.test.mjs, lib/causal/guard-quality.test.mjs, lib/causal/guard-replay.test.mjs, lib/agent-md-blocks.ranking.test.mjs and lib/brain/prevention-rate.test.mjs.
- Source references
lib/lessons.mjs,lib/lessons-panes.mjs,lib/agent-md-blocks.mjs,lib/lesson-guard-registry.mjs,lib/lesson-guards.mjs,lib/causal/guard-verdicts.mjs,lib/causal/guard-quality.mjs,lib/causal/guard-replay.mjs,lib/brain/prevention-rate.mjs,lib/brain/lesson-confidence.mjs,lib/brain-cleanup.mjs,lib/failure-layer.mjs,lib/routes/lessons.mjs,lib/brain-publisher.mjs,public/lessons.html- Documentation review
- Independent review requested on the slice pull request; not yet recorded.
- Video review
- Narrated story rendered and published 2026-09-26 (render ce03666177a3, LDG-1012) with the breadcrumb Memory, checked against main at b0bb02812. Six frames, the captions and the transcript were reviewed by the authoring agent, not an independent reviewer; the audio was not listened to by a person. Narration is the provisional Windows voice until LDG-0721.
Last updated