agentleFS
sunitghubsunitghub/canon-skills/skills/promote-learnings/SKILL.md

promote-learnings

Skill9 starsChanged 24 days agoMITView on GitHub

Reviews LEARNINGS.md's UNPROMOTED rows as a fresh, no-implementation-history reader and proposes where each durable one belongs. Report-only, except that in a consumer project it writes the proposals you confirm to PROMOTED.md. Run it by hand, in a fresh session, when the UNPROMOTED queue needs triage.

The file

We split this skill into the rules it gives an agent

11 rules. About 0 are common, 1 is distinctive, and 10 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈1

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 10

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Very specific:

    Read root LEARNINGS.md. For each row with Status: UNPROMOTED, read its linked .tickets/<id>/learnings.md in full (not just the one-line …

    line 47
  2. Very specific:

    Choosing between them: ask "at what moment would a future session make this mistake?" and pick the narrowest home that is read at that …

    line 77
  3. Very specific:

    Only bites at one workflow step or in one skill → that step's reference doc or SKILL.md.

    line 86
  4. Very specific:

    Before proposing, grep the destination for the same rule and extend it instead of adding a second copy.

    line 89
  5. Distinctive:

    Keep any always-loaded rule to one or two lines.

    line 92
  6. Very specific:

    Never edit LEARNINGS.md, any file under standards/, critique/canon-learnings.md, CLAUDE.md, or AGENTS.md.

    line 94
  7. Very specific:

    After presenting the report, ask the user which PROMOTED.md proposals to apply.

    line 100
  8. Very specific:

    Entry form: Every PROMOTED.md entry is one trigger and one action, one or two lines, never the ticket's story (the ticket id already points …

    line 103
  9. Very specific:

    Only after that write, flip the row's Status in LEARNINGS.md to ` promoted → PROMOTED.md .

    line 117
  10. Very specific:

    If LEARNINGS.md has zero UNPROMOTED rows, say so plainly in the report — an empty queue is a valid, unremarkable result, not an error.

    line 172
  11. Very specific:

    When recording or backfilling a promoted → <file> target, the lesson itself must appear in a real content line of that file.

    line 174

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

Promote Learnings

Proposes first. In canon itself the actual write into critique/canon-learnings.md, standards/, or (rarely) CLAUDE.md/AGENTS.md stays a separate, later, human-confirmed act. In a consumer project the one exception is the Apply step below: it writes to PROMOTED.md only what the user explicitly confirms, and only in an interactive session. This skill exists because learnings-sweep (aggregation) and tkt learn (candidate generation) explicitly refuse to make the promote/dismiss judgment call themselves — this is that judgment call, made fresh, then handed back as a report, not an edit.

Distinct from learnings-sweep: that skill keeps LEARNINGS.md's index current and never reads a candidate's full detail. This skill reads each UNPROMOTED row's source .tickets/<id>/learnings.md in full and judges whether it's durable enough to promote.

Why this must run fresh, every time

The non-self-promote rule (learnings-sweep's own SKILL.md) exists because the sprint that produced a learning has no perspective on whether it generalizes — everything looks load-bearing to the agent that just lived through it. This skill must run with no memory of the sprint(s) that produced the candidates it's reviewing. Start a fresh session for it — do not run it in the same conversation that just closed the sprint(s) in question.

Canon or consumer

The destinations depend on which project the session is in:

  • canon itself. critique/canon-learnings.md exists at the project root. Use the destinations in step 3 and leave every write to a human.
  • A consumer project (any other project that added canon's sprint skill). The destination is PROMOTED.md at the project root. skills.sh add sprint and refresh seed it, and the project's AGENTS.md loads it every session with @PROMOTED.md. Rarely, when a lesson must override the defaults of every session, the destination is the project's own AGENTS.md. Never propose or write anything under .claude/skills/, .agents/skills/, or outside the project root. Those paths resolve into canon's shared tree, so a write there would push one project's lesson into every project.

Process

  1. Read root LEARNINGS.md. For each row with Status: UNPROMOTED, read its linked .tickets/<id>/learnings.md in full (not just the one-line Finding cell — that's a terse distillation, the source file has the actual evaluator findings, reviewer findings and deviations).
  2. For each row, judge: is this durable and general enough that a different future sprint would benefit from knowing it, or is it a one-off specific to that ticket's exact circumstances? A learning tied to one ticket's specific bug is not durable; a learning about a class of mistake (a testing anti-pattern, a doc-drift shape, a protocol gap) usually is.
  3. For each durable row, propose exactly one destination. In a consumer project that destination is always PROMOTED.md (or, rarely, the project's own AGENTS.md). The options below apply to canon itself:

    • standards/<file>.md — when the learning generalizes into a reusable rule (a checklist item, a naming convention, a "never do X, always do Y"). Propose the target file and a starting-point example of the rule text (not a final draft — a human still writes it).
    • critique/canon-learnings.md — when the learning is better told as a narrative (a concrete incident, why the naive fix failed, what worked instead) — that file's own voice is essay-style, not a bullet list (see its existing entries for the register to match). Propose a suggested outline/key points to hit, not full prose — authoring that narrative needs a human's judgment on tone and framing.
    • CLAUDE.md/AGENTS.md — rarely, only when the lesson must apply on every session regardless of what the session is doing (these are always-loaded, so cost is paid on every turn). Say explicitly why a narrower home (standards/ or a scoped reference) doesn't fit.
    • The reference doc for the workflow step where the mistake happens (e.g. skills/sprint/reference/shared-gate-protocol.md for gate mechanics, the skill's own SKILL.md gotchas for a single-skill quirk) — when the lesson only matters at one step. Propose the file and the section.
    • A regression case in skills/<name>/evals/evals.json — when the defect was traced to a skill's own instructions (the instruction let an agent do the wrong thing, e.g. t-b0e5's partial flip), not to one ticket's code. Propose the skill, the case's prompt (the situation that produced the wrong behaviour) and the expectations that would have failed. The next sprint that edits that skill then runs it (complete.md's advisory skill-eval step).

    Choosing between them: ask "at what moment would a future session make this mistake?" and pick the narrowest home that is read at that moment (owner: standards/skill-setup-std.md ## Where a lesson goes).

    • Applies to every session whatever it is doing → CLAUDE.md/AGENTS.md.
    • Needed in nearly every session, whatever the work → standards/efficiency.md (always loaded in every project; keep it to one line).
    • A coding, testing, shell, security or platform rule that bites only in its domain → standards/efficiency-notes.md, under that domain's section (on demand; efficiency.md's index line names each section, so a new domain also gets added there).
    • Only bites at one workflow step or in one skill → that step's reference doc or SKILL.md.
    • Needs its incident to be understood (why the obvious fix failed) → critique/.
    • A skill's own instruction caused it, and a prompt can reproduce it → that skill's evals/evals.json. Before proposing, grep the destination for the same rule and extend it instead of adding a second copy. If the grep finds the rule already there, the row needs no write: propose it as `dismissed (covered: <file>)`, never as promoted. A row that spans two moments may be split into two proposals (as t-6328 was). Keep any always-loaded rule to one or two lines; its cost is paid every session.
  4. For each non-durable row, recommend dismissal with a one-line reason.
  5. Never edit LEARNINGS.md, any file under standards/, critique/canon-learnings.md, CLAUDE.md, or AGENTS.md. In canon this skill's report is its only output. In a consumer project, the Apply step below is the one exception.

Apply (consumer projects, interactive only)

After presenting the report, ask the user which PROMOTED.md proposals to apply. Skip this step entirely in a headless run (claude -p), where the report is the only output.

Entry form. Every PROMOTED.md entry is one trigger and one action, one or two lines, never the ticket's story (the ticket id already points to it), then the ticket id in brackets: - When <situation>, <do this>. (<ticket-id>). One trigger per entry: a row with two lessons becomes two entries. The report shows each proposed entry in this exact form, and Apply writes that text unchanged for every proposal the user confirms.

Carry out every decision the user gives, the ticked proposals and any typed as free text (a dismissal, a reword, a split). A decision you cannot apply is named in the summary, never dropped. For each:

  1. Grep PROMOTED.md for the same rule. If the rule is already there, write nothing: the row becomes `dismissed (covered: PROMOTED.md)` (step 3). Extend an existing entry only when the lesson adds a case to a related rule, and then the row is promoted → PROMOTED.md.
  2. Otherwise append the entry between <!-- canon:promoted:BEGIN --> and <!-- canon:promoted:END -->. If the file is missing, run skills.sh refresh, which seeds it.
  3. Only after that write, flip the row's Status in LEARNINGS.md to `promoted → PROMOTED.md`. A dismissal becomes dismissed; a row whose rule already exists becomes `dismissed (covered: <file>)` (no write happened, so it is never promoted).

Then re-read LEARNINGS.md and PROMOTED.md and print one summary: N promoted, M dismissed, K still waiting (their ticket ids, and why), and the managed block's line count against the 60-line cap.

PROMOTED.md is loaded on every session. When the managed block passes 60 lines, tell the user to consolidate or retire entries. The cap is advisory and never blocks a write.

Report format

# Promote Learnings Report

## Reviewed
<N> UNPROMOTED rows read in full from their source .tickets/<id>/learnings.md files.

## Proposals

### <ticket-id>: <one-line summary of the learning>
- **Durable:** yes/no — <why>
- **Proposed destination:** standards/<file>.md | critique/canon-learnings.md | CLAUDE.md/AGENTS.md | <workflow reference doc>#<section> | dismiss | dismissed (covered: <file>)
- **Starting point:** <example rule text, or outline/key points, or the dismissal reason>
- **Entry (PROMOTED.md only):** the exact text Apply will write, `- When <situation>, <do this>. (<ticket-id>)`; one entry per trigger

<repeat per row>

## PROMOTED.md size
<Consumer project: the managed block's current lines plus the proposed entries as written = <N> of 60.>

## Next Steps

> In canon, this skill never writes these changes itself. (In a consumer project, confirmed
> `PROMOTED.md` proposals go through the Apply step instead, so this section is omitted there; in canon the
> report's `PROMOTED.md` lines read "not applicable".) Apply them by hand: edit the destination
> file below, bump that file's frontmatter `version` and set `updated` to today (if it has
> them), then flip each promoted row's Status in LEARNINGS.md away from UNPROMOTED. Record the
> target in the cell — `` `promoted → standards/efficiency.md` `` (whole value in backticks so
> editors highlight it; or `dismissed`) — so where a lesson went is readable from the row without
> git archaeology.

<Include the frontmatter bump ("bump `version` 1.0.5 → 1.0.6, `updated` → <today>") in each
destination's action. Concrete, per-proposal actions a human can take — e.g. "Add the following to
standards/efficiency.md: ..." or "Consider writing a critique/canon-learnings.md section
covering: ...". Never performed by this skill itself.>

Gotchas

  • A row this skill proposes for promotion is not confirmed — a human (or a later, separate, non-builder pass) still decides. Do not mark a row promoted anywhere until the actual write has happened. In canon that write happens outside this skill. In a consumer project the only write this skill makes is the Apply step's confirmed PROMOTED.md entries.
  • If LEARNINGS.md has zero UNPROMOTED rows, say so plainly in the report — an empty queue is a valid, unremarkable result, not an error.
  • When recording or backfilling a promoted → <file> target, the lesson itself must appear in a real content line of that file. Read each grep hit: a ticket-ID match in a template or example row (e.g. the sample row in skills/learnings-sweep/SKILL.md) is not evidence. With no real hit, leave the row plain promoted rather than guessing.
  • A row that carries more than one lesson flips only when every part has a real destination line or the report explicitly dismissed it. Until then it stays UNPROMOTED with a note naming the pending part (t-b0e5: four rows were flipped on one half and the other halves dropped out of review).

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.