Compound Knowledge

workflow no always-loaded rules -- loads on demand updated 2026-06-22

Team-shared learnings in docs/solutions/. After solving a non-trivial problem, /compound writes a structured markdown doc with YAML frontmatter that later /xplan and /review runs re-inject as grounding. Counterpart to self-improving's personal MEMORY.md.

Tags

  • knowledge
  • learnings
  • compound
  • team
  • docs-solutions
  • review
  • planning

README

Compound Knowledge

Team-shared learnings in docs/solutions/. After solving a non-trivial problem, /compound writes a structured markdown doc that later /xplan and /review runs re-inject as grounding context via the learnings-researcher agent.

Team-Shared vs Personal Memory

CCGM ships two reflection stores; they are complementary, not competing.

self-improving (personal) compound-knowledge (team)
Location ~/.claude/projects/.../memory/MEMORY.md docs/solutions/ in the working repo
Committed to git No Yes
Scope Cross-repo, per-user Per-repo, shared
What to capture User preferences, cross-repo gotchas, working style Repo-specific bugs, patterns, conventions
Retrieval Auto-loaded at session start Pulled by learnings-researcher on demand

Use personal memory for things like "user prefers single bundled PRs" or "Tailwind v4 drops cursor: pointer - remember this across projects." Use team knowledge for things like "Supabase migrations in this repo quote all reserved words by convention" or "the Vite build in apps/web requires a CSS import in this specific order."

A learning can plausibly go in either store. When in doubt, ask: "would a teammate who never worked with this agent want to find this?" If yes, write it to docs/solutions/. If it is really about your own working style, write it to personal memory.

What This Module Provides

Files installed globally to ~/.claude/:

Source Target Purpose
skills/compound/SKILL.md skills/compound/SKILL.md /compound - capture a new learning
skills/compound/references/schema.yaml skills/compound/references/schema.yaml YAML schema for doc frontmatter
skills/compound-refresh/SKILL.md skills/compound-refresh/SKILL.md /compound-refresh - maintenance pass
skills/compound-reproject/SKILL.md skills/compound-reproject/SKILL.md /compound-reproject - re-project existing entries
agents/learnings-researcher.md agents/learnings-researcher.md Retrieval agent for /xplan, /review

Manual Installation

# From the CCGM repo root:

mkdir -p ~/.claude/skills/compound/references
mkdir -p ~/.claude/skills/compound-refresh
mkdir -p ~/.claude/skills/compound-reproject
mkdir -p ~/.claude/agents

cp modules/compound-knowledge/skills/compound/SKILL.md \
   ~/.claude/skills/compound/SKILL.md

cp modules/compound-knowledge/skills/compound/references/schema.yaml \
   ~/.claude/skills/compound/references/schema.yaml

cp modules/compound-knowledge/skills/compound-refresh/SKILL.md \
   ~/.claude/skills/compound-refresh/SKILL.md

cp modules/compound-knowledge/skills/compound-reproject/SKILL.md \
   ~/.claude/skills/compound-reproject/SKILL.md

cp modules/compound-knowledge/agents/learnings-researcher.md \
   ~/.claude/agents/learnings-researcher.md

Per-Repo Bootstrap

compound-knowledge writes to the repo being worked on, not to CCGM or to ~/.claude/. Each consuming repo needs two small things before the loop is fully discoverable:

  1. docs/solutions/README.md - an index describing categories and how to add a learning. The /compound skill offers to write this automatically on first use.

  2. A pointer block in the repo's AGENTS.md or CLAUDE.md. Example:

    ## Prior Learnings
    
    Team-shared learnings live in `docs/solutions/`. Before planning a new
    feature or debugging an unfamiliar problem, check for relevant priors.
    The `learnings-researcher` agent surfaces them automatically at the
    start of /xplan and /review.
    

The /compound skill runs a Discoverability Check on every invocation and offers to add this pointer if missing. You do not need to pre-seed either file - the skill self-bootstraps on first use. Pre-seeding is only worth it when onboarding a new repo or running a large /xplan before the first compound.

Usage

Capture a learning

/compound
/compound mode:light

Run after shipping a non-trivial fix or confirming a durable pattern. The skill interviews the session, classifies the problem, scores overlap with existing docs, and writes or updates the corresponding file.

Full mode (default) dispatches four parallel research subagents (Context Analyzer, Solution Extractor, Related Docs Finder, Session Historian). Lightweight mode skips the fan-out and writes directly from the current conversation.

Re-project the store

/compound-reproject type:qa
/compound-reproject type:contradictions
/compound-reproject type:summary
/compound-reproject type:outline

Run when the corpus has grown large enough that the raw docs are hard to navigate (roughly 20+ entries). Re-projection generates a single derived markdown artifact from existing entries without mutating them. Output goes to docs/solutions/_reprojections/{type}-{timestamp}.md in the working repo.

Four projection types:

  • qa — Q&A pairs phrased as a developer would ask them, each answer grounded in and citing a specific source entry. Useful for study, onboarding, or surfacing gaps.
  • contradictions — pairs of entries whose claims disagree, with the tension stated explicitly and a likely resolution. Useful after a /compound-refresh finds no staleness but the corpus still has internal friction.
  • summary — restructured thematic summary grouping related entries under synthesized headings. Useful when handing off a subsystem or writing documentation.
  • outline — narrative outline tracing how the team's understanding developed, with a current-consensus section and open-questions list. Useful for milestone reviews or onboarding a new agent to a domain.

Optional filters narrow the source set:

/compound-reproject type:qa tag:supabase tag:migrations
/compound-reproject type:outline topic:authentication n:20
/compound-reproject type:contradictions tag:deployment

All re-projections include a source_ids frontmatter field listing the exact source entry paths. No source entry is modified. Writing re-projections back as new docs/solutions/ entries (--ingest) is not implemented in v1.

Maintain the store

/compound-refresh
/compound-refresh mode:autofix
/compound-refresh mode:report-only

Monthly or after a major refactor. Classifies each existing doc as Keep / Update / Consolidate / Replace / Delete based on file mtime, referenced-code existence, and overlap with newer docs.

Retrieve priors

The learnings-researcher agent is a drop-in, invoked by other skills. It is not wired into /xplan or /review by this PR - those integrations are tracked separately (see CCGM issues #268 and #277). To use it manually from any skill or command:

Dispatch the learnings-researcher agent with:
- task_summary: <one paragraph>
- files_hint: [<paths>]
- tags_hint: [<tags>]

The agent returns structured blocks listing matching priors with excerpts.

Dependencies

  • skill-authoring - compound and compound-refresh follow the skill-authoring discipline (reference files via backticks, imperative voice, one command per Bash call, etc.)

No runtime dependencies beyond the module system.

Non-Goals

This module does not:

  • Replace self-improving. Personal memory and team knowledge are complementary. self-improving stays installed alongside this module.
  • Auto-wire itself into /xplan or /review. Those integrations ship as follow-up PRs (#268 for two-stage review prompt templates; #277 for the unified review orchestrator).
  • Install docs/solutions/ in any repo. The skill bootstraps per-repo on first use - CCGM itself does not touch consuming repos.

Source

Ported from EveryInc/compound-engineering-plugin. The original ships ce-compound, ce-compound-refresh, and agents/research/learnings-researcher.md as part of a larger compound-engineering loop. CCGM adopts the keystone piece - the learnings loop - while leaving the rest of that plugin's surface for separate evaluation.

Adaptations from the source:

  • Mode token names match the CCGM skill-authoring convention (mode:full, mode:light, mode:autofix, mode:report-only)
  • Frontmatter schema is extracted to references/schema.yaml so the skill body does not carry it in every invocation
  • The pass-paths-not-contents subagent dispatch pattern is applied to the four Phase-1 research subagents
  • The learnings-researcher agent lives under agents/ (per the agents/ directory convention added in CCGM #273) rather than inside a skill directory

Will install

Path Action Target Type
skills/compound/SKILL.md skills/compound/SKILL.md skill
skills/compound/references/schema.yaml skills/compound/references/schema.yaml doc
skills/compound-refresh/SKILL.md skills/compound-refresh/SKILL.md skill
agents/learnings-researcher.md agents/learnings-researcher.md agent
skills/compound-reproject/SKILL.md skills/compound-reproject/SKILL.md skill

Dependencies

Required by

Included in presets

Install this module

Agent prompt

Recommended for agent users -- hands the whole install off to your assistant.

Fetch https://cd23a9be.ccgm-site.pages.dev/modules/compound-knowledge.md and install this module into my Claude Code setup.

Native plugin marketplace

One command via the native plugin marketplace -- additive, does not merge settings.json.

claude plugin install compound-knowledge@ccgm

The marketplace path is additive, not a replacement: it installs commands, agents, and skills as native plugin components, but it does not perform the bash installer's deep settings.json merge, and it does not write the always-loaded global CLAUDE.md context. Rules are only injected via an opt-in SessionStart hook rather than being auto-loaded. Use the bash installer when those pieces matter to you.

Manual, per file

Full control -- copy exactly the files you want from the sections below.

Files

Files

skill (3)

skills/compound/SKILL.md

---
name: compound
description: >
  After solving a non-trivial problem, extract a durable learning to docs/solutions/ in the current repo. Two modes - Full (parallel research subagents, strict schema, overlap check) and Lightweight (single-pass, direct from current conversation). Writes team-shared knowledge that /xplan and /review later re-inject as grounding. Runs a Discoverability Check so every repo's AGENTS.md or CLAUDE.md points at docs/solutions/.
  Triggers: compound, capture learning, write solution doc, post-mortem, retro, log this lesson, save this finding.
disable-model-invocation: true
---

# /compound - Compound Team Knowledge

Capture a learning from the current task into `docs/solutions/{category}/{slug}.md` in the repo being worked on. The file is committed with the rest of the codebase, greppable by teammates and agents, and re-injected as grounding context on future `/xplan` and `/review` runs via the `learnings-researcher` agent.

This is the team-shared counterpart to the personal `~/.claude/projects/.../memory/MEMORY.md` that the `self-improving` module writes. Both exist; neither replaces the other. Use personal memory for cross-repo patterns about your own working style. Use `docs/solutions/` for durable, per-repo facts that a teammate or a fresh agent session would want on hand.

## When to Run

Run `/compound` after:

- Shipping a fix for a non-trivial bug
- Resolving a tricky three-strike debugging session
- Confirming a non-obvious pattern, convention, or constraint that future work will need to respect
- Landing a decision that a future agent might accidentally unmake without knowing the prior context

Do NOT run for:

- Typo fixes, version bumps, or purely mechanical changes
- Speculative conclusions from a single observation (wait for the second occurrence)
- Anything already well-covered in the repo's AGENTS.md, CLAUDE.md, or existing `docs/solutions/` files

## Mode Selection

On invocation, parse `$ARGUMENTS` for a mode token:

- `mode:full` (or no mode token) - Full run with parallel research subagents
- `mode:light` - Lightweight single-pass, direct from current conversation

Full mode is the default. Use light mode when:

- The session already contains all the evidence needed (the bug and fix just shipped in this conversation)
- No prior `docs/solutions/` docs exist that plausibly overlap
- Speed matters more than completeness

If the user runs `/compound` with no arguments and the session is short or still has ambient context about the problem, prefer Full mode - the research passes often surface overlaps and related docs the agent has forgotten.

## Phase 1: Research (Full Mode)

Dispatch four subagents in parallel with the pass-paths-not-contents pattern (see `modules/subagent-patterns/rules/subagent-patterns.md`). Each returns a short structured report; the orchestrator merges them.

> **Concurrency — avoid the 429 throttle.** Dispatch these four at `model: "sonnet"` with medium reasoning effort — they are bounded researchers returning a short report, not deep synthesizers. Four light agents in one wave is within the safe band; do not widen this fan-out or escalate to a heavier model without capping simultaneous heavy agents at 4 and waving the rest. Bursting >5 heavy agents trips a server-side rate limit (`Server is temporarily limiting requests · Rate limited`) that fails the whole dispatch. See `~/.claude/rules/concurrency-and-rate-limits.md`.

### Context Analyzer

Objective: Restate the problem in one paragraph and identify what would make this learning retrievable.

Inputs: the current conversation summary, the most recent diff (`git diff origin/main...HEAD`), and any linked issue or PR.

Deliverable:

- `problem`: one paragraph describing what went wrong or what was learned
- `trigger`: the specific reproduction steps or the conditions under which the pattern applies
- `surface_area`: which files, modules, or subsystems are implicated
- `tags_candidate`: 3-8 searchable tags, biased toward tags that already appear in other `docs/solutions/` docs in this repo

### Solution Extractor

Objective: Extract the fix (for bugs) or the durable rule (for knowledge) as something another agent could execute without the current conversation in context.

Inputs: the recent diff, the conversation trail that led to the fix.

Deliverable:

- `solution`: the fix or rule in imperative voice, 3-8 sentences max
- `why_it_works`: one paragraph on the mechanism, not just "because it passed tests"
- `prevention`: how to avoid hitting the same issue again (if bug) or when to apply the pattern (if knowledge)
- `anti_patterns`: common wrong turns to explicitly reject

### Related Docs Finder

Objective: Find existing `docs/solutions/` docs in this repo that plausibly overlap with the new learning.

Inputs: `tags_candidate`, `surface_area`, the one-paragraph problem.

Method: Use the native file-search tool (e.g., Glob) for `docs/solutions/**/*.md`. Use the native content-search tool (e.g., Grep) to search their frontmatter for matching `tags`, `module`, `component`, or `category`. Read the ones that match.

Deliverable:

- `overlap_candidates`: list of existing doc paths that might duplicate or relate to the new learning
- For each, a two-line justification

### Session Historian

Objective: Surface any prior conversation or agent log on this exact problem so the new doc can cite "we tried X before and it failed because Y".

Inputs: the repo name, the one-paragraph problem, any session-history module outputs if available.

Method: If the `session-history` module is installed, invoke its `session-historian` agent. Otherwise, grep the local agent log repo (`~/code/{log-repo-name}/{repo-name}/`) for entries mentioning the implicated files or error strings.

Deliverable:

- `prior_sessions`: list of prior session IDs or log entries touching this problem, with a one-line summary of what each concluded

## Phase 2: Classification and Scoring

Classify `problem_type`:

- `bug` - The learning is about a specific failure with a reproducible trigger. Example: "Supabase db push fails with circuit breaker after second retry."
- `knowledge` - The learning is a durable rule, convention, or constraint. Example: "Always quote PostgreSQL reserved words in migrations."

Both types use the same schema (see `references/schema.yaml`); the tag matters for retrieval - `learnings-researcher` can prefer bug docs when investigating a new failure, or knowledge docs when planning new work.

### Overlap Scoring

For each candidate in `overlap_candidates`, score across 5 dimensions. Each dimension is 0, 1, or 2:

| Dimension | 0 | 1 | 2 |
|-----------|---|---|---|
| **Problem** | Different problem | Related problem | Same underlying problem |
| **Root cause** | Different mechanism | Adjacent mechanism | Same root cause |
| **Solution** | Different fix | Partially overlapping fix | Same fix |
| **Files** | No shared files | Some shared files | Majority of files overlap |
| **Prevention** | Different preventive rule | Partially overlapping rule | Same preventive rule |

Total: 0-10. Decision:

- **8-10** - Update the existing doc in place. Do not create a new file.
- **5-7** - Create a new doc but set `related: [path-to-overlap]` in frontmatter, and add a one-line "See also" to the existing doc pointing at the new one.
- **0-4** - Create a new independent doc.

### Category Selection

Select the `category` from the standard list in `references/schema.yaml`. If the repo has already established repo-specific categories (look in `docs/solutions/README.md`), prefer those. Only add a new category when no standard category fits - and when you add one, update `docs/solutions/README.md` at the same time.

## Phase 3: Write or Update

### Path and Slug

Derive the slug from the learning title:

```
slug = kebab-case(title, max 60 chars)
path = docs/solutions/{category}/{slug}.md
```

Always use `.md`. Always lowercase, hyphen-separated slug. No date suffix in the filename - the `date` frontmatter field is the canonical source.

If the slug collides with an existing file, append `-2`, `-3`, etc. Do not overwrite silently.

### Frontmatter

Write frontmatter matching `references/schema.yaml`. Required fields: `title`, `date`, `problem_type`, `category`, `root_cause`, `tags`, `severity`. Optional fields: `module`, `component`, `files`, `related`.

### Body Structure

```markdown
---
{frontmatter}
---

# {title}

## Problem
{one paragraph; include reproduction steps for bugs}

## Root Cause
{one or two paragraphs; the "why it works" mechanism}

## Solution
{imperative-voice fix or rule, 3-8 sentences}

## Prevention
{how to avoid hitting this again, or when to apply this pattern}

## Anti-Patterns
{wrong turns the next agent might take; enumerate as bullets}

## References
{commit SHAs, PR links, issue numbers, related solution docs}
```

Keep each section short. If any section wants to grow past a page, the learning is probably two learnings - split it.

### Update vs Create

If overlap score was 8-10, update the existing doc:

- Merge new evidence into `Problem`, `Root Cause`, and `References`
- Bump `date` to today
- Append to `tags` without removing existing tags
- Do not rewrite the whole doc

Otherwise create a new doc.

## Phase 4: Discoverability Check

After writing, verify the learning is reachable:

1. Check `docs/solutions/README.md` exists. If not, create it with a short index pointing at the category directories. Use the bootstrap template below.

2. Check `AGENTS.md` or `CLAUDE.md` at the repo root for a pointer to `docs/solutions/`. Look for the phrase "docs/solutions" or the string "compound knowledge" or "prior learnings". If not found, offer to add one. Example pointer block:

   ```
   ## Prior Learnings

   Team-shared learnings live in `docs/solutions/`. Before planning a new
   feature or debugging an unfamiliar problem, check for a relevant prior
   with `rg <keyword> docs/solutions/` or let the `learnings-researcher`
   agent fan out at the start of /xplan or /review.
   ```

   Ask the user before writing this pointer. If they accept, inject the block into the file's most appropriate section (usually right after the top-level intro).

3. If the repo has a session log file for today (`~/code/{log-repo-name}/{repo}/YYYYMMDD/{agent-id}.md`), append a one-line entry noting the new learning and its path.

## Phase 5: Lightweight Mode

When invoked with `mode:light`, skip Phases 1 and 2 and write directly from the current conversation. Still:

- Apply the same frontmatter schema
- Select a `category` from the standard list
- Run the Discoverability Check at the end

Skip:

- The four-subagent fan-out
- The overlap scoring
- The related-docs merge

Use lightweight mode when the cost of a full research pass outweighs the value of catching overlaps. A single-repo agent fresh off fixing a 20-line bug usually knows the context well enough that full-mode research returns the same conclusions.

## Output

On completion, print:

```
Wrote: docs/solutions/{category}/{slug}.md
Mode: {full|light}
Problem type: {bug|knowledge}
Overlap: {none|related: <path>|updated: <path>}
Discoverability: {ok|pointer-added|pointer-offered}
```

If the user has an open PR or branch, suggest committing the new doc alongside the fix so the learning ships with the change that inspired it.

## Anti-Patterns

- **Speculative learnings.** One observation is not a pattern. Wait for the second hit, or write the doc and mark severity P3 with a clear "observed once" note.
- **Copy-paste from the conversation.** Extract the rule; do not paste the dialogue. If the next agent has to re-read a whole conversation to use the doc, the doc failed.
- **Skipping the overlap check in Full mode.** Duplicated learnings poison retrieval - two docs on the same problem make the agent see "this has been handled before" twice and dilute the signal.
- **Writing to a generic category.** `tooling` is not a category; `vite-build-config` is. Be specific; teammates grep by category name.
- **Second-person voice.** The doc is a spec for the next agent to execute. Use imperative voice: "Quote the identifier." Not: "You should quote the identifier."
- **Skipping Discoverability.** A doc no agent can find is indistinguishable from no doc.

## Bootstrap Template: docs/solutions/README.md

If the repo has no `docs/solutions/README.md`, write one with this shape:

```markdown
# Solutions

Team-shared learnings for this repo. Each subdirectory groups docs by
category. Every doc starts with YAML frontmatter matching the schema at
`modules/compound-knowledge/skills/compound/references/schema.yaml` in CCGM.

## Categories

- `build-errors/` - Build pipeline failures and their fixes
- `runtime-errors/` - Application-layer errors encountered in dev or prod
- `performance-issues/` - Profiling finds, slow queries, render bottlenecks
- `security-issues/` - Vulnerabilities, mis-configurations, secret handling
- `data-migrations/` - Schema changes, migration gotchas, rollback patterns
- `testing/` - Test infrastructure patterns and flake fixes
- `tooling/` - CI, linters, package managers, local dev environment
- `skill-design/` - Patterns for writing agent skills and commands
- `architecture/` - Boundaries, coupling, dependency direction choices
- `api-contracts/` - Public interface decisions and their rationale
- `deployment/` - Release, deploy, and rollback procedures
- `dev-environment/` - Local setup, editor config, tooling quirks

Add a new category only when no existing category fits, and update this
README in the same commit.

## Adding a Learning

Run `/compound` from Claude Code. It writes a frontmatter-tagged doc to
the right category and updates related prior docs as needed.

## Reading Learnings

At the start of `/xplan` and `/review`, the `learnings-researcher` agent
greps this directory by frontmatter tags and surfaces relevant priors as
planning or review context. Teammates without agents can grep directly -
tags and categories are designed for human browsing first.
```

## Source

Ported from EveryInc/compound-engineering-plugin's `ce-compound` skill. Adapted to CCGM voice, schema-first frontmatter, and the pass-paths-not-contents subagent pattern. Kept verbatim: the two-mode choice, the 5-dimension overlap scoring, the Discoverability Check.

skills/compound-refresh/SKILL.md

---
name: compound-refresh
description: >
  Periodic maintenance pass over docs/solutions/. For each doc, classify as Keep / Update / Consolidate / Replace / Delete based on staleness, referenced-code existence, and overlap with newer learnings. Run monthly or after a major refactor. Modes - interactive, autofix, report-only.
  Triggers: compound refresh, clean up solutions docs, docs/solutions maintenance, solution doc audit.
disable-model-invocation: true
---

# /compound-refresh - Maintain docs/solutions/

`/compound` writes new learnings; `/compound-refresh` reviews existing ones. Over time, solution docs go stale:

- The code they reference gets deleted or moved
- A newer doc supersedes them
- Multiple adjacent docs should merge into one
- The underlying problem stopped being a problem

This skill walks `docs/solutions/**/*.md` in the current repo and classifies each doc into one of five outcomes, then applies (or reports) the classification.

## When to Run

Run `/compound-refresh`:

- Monthly, as a standing maintenance chore
- After a major refactor that moves or deletes files many solution docs reference
- Before a milestone that will attract new contributors (clean docs help onboarding)
- When retrieval feels noisy - too many false-positive hits from `learnings-researcher`

Do NOT run during active feature work - the autofix mode will create a large diff that muddies the signal of the feature branch.

## Mode Selection

Parse `$ARGUMENTS` for a mode token:

- `mode:interactive` (default) - Classify, then ask per-doc before applying any change
- `mode:autofix` - Apply `Keep`, `Update`, and `Delete` automatically; ask for `Consolidate` and `Replace`
- `mode:report-only` - Strictly read-only; print the classification table and exit

When composed from other skills (e.g., called from a repo-wide `/audit`), prefer `mode:report-only` so the caller decides.

## Phase 1: Inventory

Use the native file-search tool (e.g., Glob) to list every `docs/solutions/**/*.md` in the current repo. Skip `docs/solutions/README.md`.

For each doc, read the frontmatter and the body. Record:

- Path
- Frontmatter fields (`title`, `date`, `problem_type`, `category`, `tags`, `files`, `related`, `severity`)
- File mtime (`git log -1 --format=%ct {path}` for the last change time)
- Body length in lines

Skip any doc with invalid frontmatter - surface it at the end as a validation failure for the user to fix before re-running.

## Phase 2: Staleness Probes

For each doc, run two probes in parallel across docs (one per file at a time per doc):

### Probe A: Referenced Code Still Exists

For each path in frontmatter `files`:

- Use the native file-search tool to check if the path exists
- If the path is a file that has been moved, `git log --follow -- {path}` finds the new location
- If the file still exists, grep it for any identifiers or line ranges referenced in the body (function names, class names, error strings)

Record:

- `files_missing`: count of files no longer present
- `files_moved`: count of files that moved
- `identifiers_missing`: count of named symbols in the body no longer present in the code

### Probe B: Age vs Severity

Compute age in days from the frontmatter `date`. Combine with `severity`:

| Severity | Stale after |
|----------|-------------|
| P0 | 180 days |
| P1 | 270 days |
| P2 | 365 days |
| P3 | 540 days |

Rationale: higher severity issues are more likely to have been patched at the root by follow-up work; lower severity evergreen-knowledge docs age slower.

Record:

- `age_days`
- `is_aged`: true if `age_days` > threshold for that severity

## Phase 3: Classify

For each doc, output one of five outcomes:

### Keep

Criteria (all must hold):

- `files_missing` == 0
- `identifiers_missing` == 0
- No newer doc supersedes it (no other doc with `related: [this-path]` and a later `date`)
- `is_aged` is false OR the doc is `problem_type: knowledge` and still accurate

Action: none.

### Update

Criteria (any one holds):

- `files_moved` > 0 and `files_missing` == 0 (paths need rewriting but the substance is intact)
- `is_aged` is true but the substance still applies (re-date, optionally freshen language)
- Minor drift from a newer related doc but the two cover different angles

Action: rewrite paths, bump `date`, refresh stale wording. Preserve the substance.

### Consolidate

Criteria:

- Two or more docs share a root cause and a fix, or a tag set of 3+ common tags, and their combined content would be clearer as one doc

Action: Merge content, pick the best slug, set the merged doc's `related: []` to the other paths (now deleted), and `git rm` the obsolete files in the same commit.

### Replace

Criteria:

- `files_missing` > 0 AND a newer doc already exists that correctly captures the current state
- The doc's `solution` is now wrong (contradicted by the current codebase) but the problem described is real and has a better documented fix elsewhere

Action: Delete this doc. If the replacement doc does not reference it, add a `related` entry to the replacement pointing at the deleted slug in case of link collisions.

### Delete

Criteria (any one holds):

- `files_missing` counts the majority of `files` in frontmatter (the doc is about code that no longer exists)
- The underlying problem has been fixed at the root and the fix is documented in code comments or in a rule file
- The doc is a duplicate of another with the same `root_cause` and no additional content

Action: `git rm` the file. If other docs reference it in their `related` list, remove those references in the same commit.

## Phase 4: Apply or Report

### Interactive Mode

For each doc classified as anything other than `Keep`:

1. Print the classification, the reasoning in one line, and a summary of the proposed action
2. Use an AskUserQuestion with options: `Apply | Skip | Show Details`
3. On `Show Details`, print the full doc and any supersession candidates
4. On `Apply`, execute the action; on `Skip`, move to the next doc

### Autofix Mode

Apply `Keep`, `Update`, and `Delete` without prompting. For `Consolidate` and `Replace`, batch the pending items at the end and ask once per category.

Write a run artifact at `docs/solutions/.refresh/{YYYYMMDD-HHMM}.md` summarizing:

- Count of each outcome
- Every path touched, with the outcome
- Any failures or skipped items

### Report-Only Mode

Print the classification table, one row per doc:

```
Path                                                     Outcome    Reason
docs/solutions/build-errors/vite-preflight.md            Keep       -
docs/solutions/build-errors/old-webpack-quirk.md         Delete     All referenced files missing
docs/solutions/data-migrations/reserved-words.md         Keep       -
docs/solutions/testing/flaky-auth.md                     Consolidate  3-tag overlap with flaky-session-expiry
```

Do NOT write anything, including the run artifact.

## Phase 5: Commit

In `autofix` and `interactive` modes, stage only the touched `docs/solutions/**` files and the run artifact. Commit with:

```
compound-refresh: {kept} kept, {updated} updated, {consolidated} consolidated, {replaced} replaced, {deleted} deleted
```

Do not mix refresh changes with code or config changes. If the working tree has unrelated staged changes, prompt the user to commit or stash (commit - see `git-workflow` rules about never stashing) before running.

## Anti-Patterns

- **Aggressive deletion.** When in doubt, classify as `Update` and re-date. A doc that was useful once may be useful again; deletion is destructive.
- **Consolidating across categories.** Docs with the same tags in different categories usually cover different angles - merging them hides the distinction.
- **Running in autofix during a feature branch.** The refresh diff buries the feature diff. Do this on its own branch.
- **Skipping the run artifact.** Without the artifact, the next refresh cannot tell which docs were last reviewed and when.

## Source

Ported from EveryInc/compound-engineering-plugin's `ce-compound-refresh`. Kept: the 5-outcome classification, the age/severity table, the staleness probes. Adapted: mode names now match CCGM skill-authoring conventions (see `modules/skill-authoring/rules/skill-authoring.md`).

skills/compound-reproject/SKILL.md

---
name: compound-reproject
description: >
  Generate derived markdown artifacts from existing docs/solutions/ entries without mutating the source. Four projection types: qa (Q&A pairs), contradictions (disagreeing entries made explicit), summary (restructured alternative summary), outline (synthesized narrative outline). Output goes to docs/solutions/_reprojections/{type}-{timestamp}.md with traceable source IDs.
  Triggers: compound reproject, reproject learnings, synthesize solutions, generate qa from solutions, find contradictions in solutions.
disable-model-invocation: true
---

# /compound-reproject - Re-Project Team Knowledge

Read existing `docs/solutions/` entries and generate one derived markdown artifact. Re-projection turns a passive store into a thinking surface: the same facts, looked at from a different angle, reveal structure that accumulates invisibly in any large learning corpus.

Inspired by the observation that a knowledge base becomes most useful when you generate synthetic views over fixed data — not just accumulate and deduplicate.

## When to Run

Run `/compound-reproject` when:

- The `docs/solutions/` directory has grown large enough that no single agent can hold all entries in context at once (rough threshold: 20+ docs)
- You want Q&A study pairs to prepare for a complex feature area
- Two recent incidents feel related but no doc links them — contradictions mode surfaces the tension
- You are onboarding a new teammate or agent and want a narrative overview of a subsystem
- A periodic review (`/compound-refresh`) found no staleness but the corpus still feels hard to navigate

Do NOT run for:

- A corpus with fewer than five entries in the filter set — re-projection on too little data produces noise
- Generating new learnings from scratch — use `/compound` for that
- Writing back the output as new `docs/solutions/` entries — v1 is read-only; `--ingest` is future work

## Arguments

Parse `$ARGUMENTS` for the following tokens. All are optional except the `type` token, which must be provided.

### Required

`type:{value}` — The projection type. One of:

- `type:qa` — Q&A pairs
- `type:contradictions` — pairs of entries that disagree, made explicit
- `type:summary` — restructured alternative summary of the same facts
- `type:outline` — synthesized narrative outline tying entries together

### Optional

`tag:{value}` — Filter source entries to those whose frontmatter `tags` list includes this value. Repeat for multiple tags (any-match semantics). If omitted, all entries in `docs/solutions/` are candidates.

`n:{value}` — Maximum number of source entries to read. Default is 50. If the filtered set is smaller than N, use all of them.

`topic:{value}` — Free-text topic hint. When set, the skill uses this to bias entry selection toward entries whose title, tags, or body are most relevant to the topic. Applied after the `tag:` filter.

### Examples

```
/compound-reproject type:qa tag:supabase
/compound-reproject type:contradictions n:30
/compound-reproject type:outline topic:authentication
/compound-reproject type:summary tag:migrations tag:postgres
```

## Phase 1: Collect Source Entries

1. Use Glob to find all `docs/solutions/**/*.md`. Exclude `_reprojections/` subdirectory.
2. If `tag:` tokens are present, read each file's frontmatter and keep only entries whose `tags` list includes at least one of the specified values.
3. If `topic:` is set, rank the filtered set by relevance to the topic (title keyword match first, then body keyword match) and take the top N.
4. Otherwise, take up to N entries in filesystem order.
5. Record the `id` or derivable identifier for each source entry. If the frontmatter has no `id` field, use the repo-relative file path as the identifier.

Emit a source list before proceeding:

```
Source entries ({count} files):
- docs/solutions/{category}/{slug}.md
- ...
```

Stop if fewer than 2 entries remain after filtering. Inform the user and exit cleanly.

## Phase 2: Generate Re-Projection

Apply the generation procedure for the specified type. Read the full body of each source entry before generating — do not rely on frontmatter alone.

### type:qa

Produce a list of Q&A pairs grounded in the source entries. Each pair must:

- Phrase the question as a developer would ask it ("Why does X fail?", "When should I use Y?", "What is the rule for Z?")
- Answer in 2-5 sentences using only facts present in the source entries
- Cite the source entry path or slug in parentheses after the answer

Target: 3-5 Q&A pairs per source entry, deduplicated. Merge pairs whose questions are semantically identical.

Format:

```markdown
**Q: {question}**

A: {answer} *(source: {slug-or-path})*
```

### type:contradictions

Identify pairs of entries that make claims that conflict with each other. A contradiction is:

- Two entries that prescribe opposite actions for the same condition
- Two entries that diagnose the same symptom with different root causes
- One entry that marks something as safe or recommended and another that marks it as dangerous or discouraged

For each contradiction:

- State the tension in one sentence
- Quote the conflicting claims verbatim (one line each, with source)
- Note the likely resolution (newer entry wins, or both apply in different contexts, or genuine ambiguity)

If no contradictions are found, say so explicitly. Do not fabricate tensions.

Format:

```markdown
### Contradiction: {short label}

**Tension:** {one sentence}

- "{claim A}" — *{source-A}*
- "{claim B}" — *{source-B}*

**Likely resolution:** {one sentence}
```

### type:summary

Produce an alternative summary of the source entries, restructured by theme rather than chronological order. Group related entries under synthesized headings. Each group should:

- Open with a one-sentence synthesis of the theme
- Bullet the key facts from member entries
- Note dissent or nuance where entries within the group partially disagree

The goal is a document a new agent could read in place of the individual entries and arrive at the same working understanding.

Format:

```markdown
## {Synthesized Theme Heading}

{One-sentence synthesis.}

- {Key fact 1} *(source: {slug})*
- {Key fact 2} *(source: {slug})*
```

### type:outline

Produce a narrative outline that ties the source entries into a coherent story. Identify the arc: what class of problems does this corpus address, what sequence of understanding did the team build, what is the current state of knowledge, and what questions remain open?

Structure:

```markdown
## Overview

{2-3 sentence framing of what this set of learnings covers and why it exists.}

## How the Problem Space Developed

{Narrative paragraphs tracing key discoveries in roughly chronological order.}

## Current Consensus

{Bullet list of the firmest conclusions drawn from the corpus.}

## Open Questions

{Bullet list of unresolved tensions, known unknowns, or areas where the corpus is thin.}

## Source Entries

{Flat list of all source paths.}
```

## Phase 3: Write Output File

Derive the output path:

```
docs/solutions/_reprojections/{type}-{YYYYMMDD-HHMMSS}.md
```

Create `docs/solutions/_reprojections/` if it does not exist.

Write the output file with this header:

```markdown
---
reprojection_type: {type}
generated: {ISO 8601 UTC timestamp}
source_count: {N}
source_ids:
  - {path-or-id-1}
  - {path-or-id-2}
  ...
filter_tags: [{tags if any}]
filter_topic: "{topic if set}"
---

# {Type-title}: {Short description of the filter/topic}

{Body generated in Phase 2}
```

The `source_ids` frontmatter field is the traceable source citation required by the spec. Every re-projection is self-documenting.

## Phase 4: Report

Print a short summary:

```
Wrote: docs/solutions/_reprojections/{type}-{timestamp}.md
Type: {type}
Source entries: {count}
Filter: {tags/topic description, or "none"}
```

Do NOT suggest committing the re-projection to the repo unless the user asks. Re-projections are ephemeral work artifacts; whether to commit them is the user's call.

## Constraints

- **No mutation of source entries.** The skill reads `docs/solutions/` but never writes to it. Re-projections go only to `_reprojections/`.
- **No write-back as new entries in v1.** The `--ingest` flag (which would write the re-projection back as new `docs/solutions/` entries with `source: reprojection`) is not implemented in this version.
- **Single project only.** Cross-project re-projection (spanning multiple repos) is out of scope for v1.
- **Grounded output.** Every claim in the re-projection must trace to a specific source entry. Do not synthesize conclusions that no source entry supports.

## Anti-Patterns

- **Inventing claims.** If no source entry says X, the re-projection may not say X. Ground every assertion.
- **Re-projecting too few entries.** Fewer than 5 entries rarely produces a useful artifact. Check the source count before generating.
- **Generating without filtering when the corpus is large.** A 200-doc corpus without a tag or topic filter will produce an incoherent summary. Encourage the user to narrow the scope.
- **Treating the output as authoritative.** Re-projections are derived views, not new learnings. If a Q&A pair reveals a gap, the right follow-up is `/compound` to write a new entry — not to treat the re-projection itself as the learning.
- **Committing re-projections without user intent.** They are working artifacts. Some warrant committing; most do not. Never commit silently.

## Example Output Shapes

The following illustrates the shape of each type on a small corpus. These are not real entries.

### qa example

```markdown
---
reprojection_type: qa
generated: 2026-05-02T14:00:00Z
source_count: 4
source_ids:
  - docs/solutions/data-migrations/quote-reserved-words.md
  - docs/solutions/data-migrations/idempotent-migrations.md
  - docs/solutions/tooling/supabase-circuit-breaker.md
  - docs/solutions/testing/migration-validation.md
filter_tags: [supabase, migrations]
filter_topic: ""
---

# Q&A: supabase, migrations

**Q: Why do Supabase migrations fail on keywords like "position" or "order"?**

A: PostgreSQL reserves these identifiers. Using them unquoted in a column definition triggers a syntax error at parse time, before the migration runs. Always double-quote reserved words: `"position" integer`. *(source: quote-reserved-words)*

**Q: How do I make a migration safe to re-run?**

A: Use idempotent DDL patterns: `CREATE TABLE IF NOT EXISTS`, `ADD COLUMN IF NOT EXISTS`, `CREATE INDEX IF NOT EXISTS`. For triggers, use `DROP TRIGGER IF EXISTS` before `CREATE TRIGGER`. *(source: idempotent-migrations)*

**Q: What happens if I retry a failing `db push` more than once?**

A: The Supabase connection pooler circuit breaker trips after repeated auth failures. Once tripped, all CLI database operations fail for a 5-30 minute cooldown. Re-authenticate with `npx supabase login` and wait before retrying once. *(source: supabase-circuit-breaker)*
```

### contradictions example

```markdown
---
reprojection_type: contradictions
generated: 2026-05-02T14:05:00Z
source_count: 8
source_ids:
  - docs/solutions/deployment/wrangler-pages-deploy.md
  - docs/solutions/deployment/cloudflare-git-integration.md
  ...
filter_tags: [cloudflare, deployment]
filter_topic: ""
---

# Contradictions: cloudflare, deployment

### Contradiction: wrangler pages deploy vs. git integration

**Tension:** One entry recommends using `wrangler pages deploy` to ship quickly; another marks it as creating an unrecoverable direct-upload project.

- "Use `wrangler pages deploy <name>` to get the site live immediately." — *wrangler-pages-deploy*
- "Never run `wrangler pages deploy <new-project-name>` for a project that should auto-deploy; it creates a direct-upload project Cloudflare cannot convert to Git integration." — *cloudflare-git-integration*

**Likely resolution:** The newer entry (*cloudflare-git-integration*, dated 2026-04-20) supersedes the older. The older entry predates the discovery that Git integration cannot be retrofitted.
```

### summary example

```markdown
---
reprojection_type: summary
generated: 2026-05-02T14:10:00Z
source_count: 6
source_ids:
  - docs/solutions/data-migrations/quote-reserved-words.md
  - docs/solutions/data-migrations/idempotent-migrations.md
  - docs/solutions/data-migrations/on-conflict-requires-unique.md
  - docs/solutions/tooling/supabase-circuit-breaker.md
  - docs/solutions/testing/migration-validation.md
  - docs/solutions/testing/local-migration-test.md
filter_tags: [supabase, migrations]
filter_topic: ""
---

# Summary: supabase, migrations

## DDL Safety Rules

PostgreSQL has reserved identifiers that cause parse-time errors if used unquoted.

- Always double-quote reserved words (`"position"`, `"order"`, `"user"`) in column definitions. *(source: quote-reserved-words)*
- Use idempotent DDL variants (`IF NOT EXISTS`, `CREATE OR REPLACE`) so migrations can be re-run without failure. *(source: idempotent-migrations)*
- `ON CONFLICT` requires a unique constraint on the conflict column; adding one mid-migration is a separate step. *(source: on-conflict-requires-unique)*

## CLI Safety Rules

The Supabase connection pooler has a circuit breaker that trips on repeated auth failures.

- Never retry a failing `db push` more than once without re-authenticating. *(source: supabase-circuit-breaker)*
- Use `supabase migration up` (incremental) over `db reset` (destructive) during development. *(source: local-migration-test)*
```

### outline example

```markdown
---
reprojection_type: outline
generated: 2026-05-02T14:15:00Z
source_count: 6
source_ids:
  - docs/solutions/data-migrations/quote-reserved-words.md
  - docs/solutions/data-migrations/idempotent-migrations.md
  - docs/solutions/data-migrations/on-conflict-requires-unique.md
  - docs/solutions/tooling/supabase-circuit-breaker.md
  - docs/solutions/testing/migration-validation.md
  - docs/solutions/testing/local-migration-test.md
filter_tags: [supabase, migrations]
filter_topic: ""
---

# Outline: supabase, migrations

## Overview

This corpus covers hard-won knowledge about running Supabase/PostgreSQL migrations safely. The six entries span DDL correctness, CLI safety, and local validation workflow. They accumulated over roughly three months of incident-driven learning.

## How the Problem Space Developed

The earliest entry (*quote-reserved-words*, 2026-01-10) captures the first incident: a migration failed because `position` is a PostgreSQL reserved word. This triggered a broader audit of identifiers in existing schemas.

The idempotency and ON CONFLICT entries followed as the team ran migrations in CI where partial failures and retries are common. The circuit breaker entry was the costliest: a developer retried a failing `db push` six times, tripped the pooler, and blocked the team from running any migration for forty minutes.

The two testing entries represent the team's response — a local validation checklist that catches DDL errors before they reach the pooler.

## Current Consensus

- Quote all PostgreSQL reserved words in column definitions.
- Use idempotent DDL patterns everywhere; treat non-idempotent migrations as a code smell.
- Never retry a failing CLI migration command without re-authenticating first.
- Validate migrations locally with `supabase migration up` before pushing.

## Open Questions

- No entry yet covers rollback procedures for destructive migrations (DROP COLUMN, DROP TABLE).
- The circuit breaker cooldown time (5-30 minutes) is stated as a range; the exact duration is unknown.

## Source Entries

- docs/solutions/data-migrations/quote-reserved-words.md
- docs/solutions/data-migrations/idempotent-migrations.md
- docs/solutions/data-migrations/on-conflict-requires-unique.md
- docs/solutions/tooling/supabase-circuit-breaker.md
- docs/solutions/testing/migration-validation.md
- docs/solutions/testing/local-migration-test.md
```

## Source

Added in CCGM #439. Inspired by Karpathy on LLM knowledge bases (Sequoia interview, 2026-04-29): "I always like feel like I gain insight... it's really just a lot of prompts for me to do synthetic data generation kind of over fixed data." Re-projection is the CCGM implementation of that move: the same docs, looked at from a different angle.
agent (1)

agents/learnings-researcher.md

---
name: learnings-researcher
description: >
  Retrieves relevant prior learnings from docs/solutions/ in the current repo and returns them as structured context for the caller. Invoked at the start of /xplan and /review so planning and review can stand on codified team knowledge. Grep-first - never reads the full directory.
tools: Glob, Grep, Read
---

# learnings-researcher

Find prior `docs/solutions/**/*.md` entries that match the current task and return them as grounding context for the caller. The caller is almost always an orchestrator skill (currently `/xplan` and `/review` - future: any planning or debugging flow).

This agent does **not** write, summarize, or opine. It retrieves, scores, and returns. The caller decides what to do with the matches.

## Inputs

The caller passes a JSON-ish block with:

- `task_summary` (required) - one paragraph describing the new work or problem
- `files_hint` (optional) - list of paths the task will touch or has touched
- `tags_hint` (optional) - list of tags the caller thinks are relevant
- `problem_type_filter` (optional) - `bug`, `knowledge`, or absent for both
- `max_results` (optional, default 5) - cap on returned priors

Example:

```
task_summary: >
  Planning a new Supabase migration that adds a user "position" column
  and indexes. Want to surface any prior learnings on reserved-word
  quoting or RLS policy gotchas.
files_hint: [supabase/migrations/]
tags_hint: [supabase, postgres, migrations]
problem_type_filter: knowledge
max_results: 5
```

## Discovery

1. Use the native file-search tool (e.g., Glob) to enumerate `docs/solutions/**/*.md` in the repo root. Skip `docs/solutions/README.md` and anything under `docs/solutions/.refresh/`.

2. If the directory does not exist, return `no_solutions_directory: true` and stop. Do not error - this is expected for repos that have not yet bootstrapped.

3. Use the native content-search tool (e.g., Grep) to filter by frontmatter. Preferred signals, in order:

   - Exact match on any tag in `tags_hint` against the `tags:` line
   - Exact match on any path prefix in `files_hint` against the `files:` list
   - `module:` or `component:` match on strings in `task_summary`
   - Keyword match in `title` or `root_cause` against salient terms in `task_summary`

4. If a `problem_type_filter` is set, drop docs whose frontmatter `problem_type` does not match.

## Scoring

For each candidate doc, compute a relevance score out of 10:

| Signal | Points |
|--------|--------|
| Exact tag match | 3 per tag, max 6 |
| `files:` path overlaps `files_hint` | 2 |
| `module:` or `component:` match | 2 |
| Keyword match in `title` | 1 |
| Keyword match in `root_cause` | 1 |

Sort candidates descending by score. Keep the top `max_results`. Drop any doc scoring 0.

If fewer than 3 candidates survive and `tags_hint` was set, retry with `tags_hint` removed to widen the net.

## Output

Return structured results, one block per prior:

```
### Prior: docs/solutions/{category}/{slug}.md  (score: {N})
- title: {title}
- date: {date}
- problem_type: {bug|knowledge}
- root_cause: {root_cause}
- why_relevant: {one sentence - which signal matched}
- excerpt:
  {the Solution section verbatim, or Problem section if no Solution present}
```

Keep each block under 40 lines. If a doc's Solution section is longer than that, excerpt the first 40 lines and end with `...see full doc for remainder`.

End the output with a one-line summary:

```
Returned {N} priors from docs/solutions/ in {repo}.
```

If nothing matched:

```
No prior learnings found in docs/solutions/ for this task.
```

## Guardrails

- Never read a doc whose frontmatter did not match. The whole point is grep-first retrieval - random full-text reads defeat it.
- Never return the full body of every candidate. The caller's context window is already under pressure from the planning flow.
- Never edit any file. This agent is strictly read-only.
- Never cross repos. Scope to `docs/solutions/` in the current working directory's repo root.
- Never include frontmatter from docs under `docs/solutions/.refresh/` - those are maintenance artifacts, not learnings.

## When to Invoke

The caller decides. Typical invocations:

- At the start of `/xplan` Phase 0 (research) - pass the user's brief as `task_summary`
- At the start of `/review` after scope-drift audit - pass the diff summary as `task_summary` and the touched files as `files_hint`
- At the start of `/debug` when the error message or stack trace hints at a known area
- Manually, when a user asks "what have we learned about X here"

This agent is a drop-in. Callers do not need to pre-process or post-process results - just forward the output blocks as context into their own reasoning.
doc (1)

skills/compound/references/schema.yaml

# Frontmatter schema for docs/solutions/**/*.md
#
# Every learning doc under docs/solutions/ starts with a YAML frontmatter
# block matching this schema. The learnings-researcher agent greps the
# frontmatter to retrieve relevant priors during /xplan and /review.
#
# Fields marked required: true must be present on every doc. Fields marked
# required: false are optional but should be filled when the information is
# known.

fields:
  title:
    required: true
    type: string
    description: >
      Short human-readable title. One line. Imperative or noun-phrase, not a
      question. Example: "Quote PostgreSQL reserved words in migrations".

  date:
    required: true
    type: string
    format: YYYY-MM-DD
    description: Date the learning was recorded.

  problem_type:
    required: true
    type: enum
    values:
      - bug
      - knowledge
    description: >
      "bug" tracks a fix for a specific failure with a reproducible trigger.
      "knowledge" tracks a durable insight, convention, or pattern that is
      not tied to a single failure (e.g., "we always quote reserved words").

  category:
    required: true
    type: string
    description: >
      Directory category under docs/solutions/. Matches one of the standard
      categories below, or a repo-specific category added in the repo's
      docs/solutions/README.md.
    standard_values:
      - build-errors
      - runtime-errors
      - performance-issues
      - security-issues
      - data-migrations
      - testing
      - tooling
      - skill-design
      - architecture
      - api-contracts
      - deployment
      - dev-environment

  module:
    required: false
    type: string
    description: >
      Codebase module / package / service the learning applies to. Use the
      path or package name the repo itself uses. Blank if the learning is
      cross-cutting.

  root_cause:
    required: true
    type: string
    description: >
      One-sentence description of why the problem occurred. For "knowledge"
      docs, the underlying mechanism or constraint that motivates the
      pattern.

  component:
    required: false
    type: string
    description: >
      Finer-grained pointer than "module" - a function, file, class, or
      subsystem. Example: "auth/middleware/refreshToken".

  tags:
    required: true
    type: list<string>
    description: >
      3-8 searchable tags. Prefer reusing tags that already appear in other
      docs/solutions/ files in the same repo. Tags drive retrieval by
      learnings-researcher.
    examples:
      - [supabase, migrations, reserved-words]
      - [vite, env-vars, build-config]
      - [react-query, cache-invalidation]

  severity:
    required: true
    type: enum
    values:
      - P0
      - P1
      - P2
      - P3
    description: >
      Impact of the original problem. P0 = blocked a ship / production
      outage. P1 = broke a workflow for hours. P2 = noticeable friction or
      rework. P3 = nit or nice-to-know.

  files:
    required: false
    type: list<string>
    description: >
      Paths to files that were changed or that the fix touches. Used by the
      overlap scorer to detect duplicate learnings.

  related:
    required: false
    type: list<string>
    description: >
      Paths (relative to docs/solutions/) of related prior learnings. Use
      when the new doc extends or supersedes an earlier one.

example: |
  ---
  title: "Quote PostgreSQL reserved words in migrations"
  date: 2026-04-15
  problem_type: knowledge
  category: data-migrations
  module: db/migrations
  root_cause: >
    PostgreSQL reserves identifiers like "position", "order", "user" - using
    them unquoted in CREATE TABLE fails with a syntax error at the column
    definition.
  component: supabase/migrations
  tags: [supabase, postgres, migrations, reserved-words]
  severity: P1
  files:
    - supabase/migrations/20260415_add_user_position.sql
  related: []
  ---