Claude Skill

autolearn

Use when a verified non-trivial fix lands or existing solution docs need refresh. Not for unverified fixes.

LLM Mart · 0 points · 0 views 0 listing impressions 0 install-command copies
Virus-scanned Reviewed automatically before listing.

Full trust report

Download outlinedriven-outline-driven-development-.devin_skills_autolearn-b0e8ce8.zip · 17 KB
Part of outlinedriven/outline-driven-development — 145 skills

Install

skills CLI npx skills add https://github.com/OutlineDriven/outline-driven-development/tree/main/.devin/skills/autolearn
Claude Code claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install outlinedriven-outline-driven-development@llmmart
Git git clone https://github.com/OutlineDriven/outline-driven-development.git

The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole outlinedriven/outline-driven-development collection as a plugin from our marketplace. Git is the plain clone.

Skill manifest

Autolearn

Contract

Field Bound contract
Trigger A non-trivial fix has been verified (observed working, not hoped working), or explicit autolearn or refresh invocation.
Authority Reversible local: writes only the operating repo's docs/solutions/ and repo-root CONCEPTS.md; rollback is version control. No remote mutation.
Side effect Writes or refreshes docs/solutions/ learning docs and CONCEPTS.md; stages only the surfaces this skill wrote or edited.
Done A validated learning or concept entry exists, or an explicit determination that nothing qualifies.

Inputs

  • The verified fix or solved problem, from conversation history or codebase. Must be supplied or derivable from context.
  • Optional: mode:refresh [scope] to maintain existing docs; mode:headless for non-interactive operation.
  • Optional: an injected auto-memory block (supplementary context, not primary evidence).

Procedure

0. Route the mode

Strip mode: tokens from arguments before treating the remainder as context or scope.

  • Capture (default): document one solved problem into docs/solutions/.
  • Vocabulary capture: a durable, reusable project term surfaces; reconcile CONCEPTS.md.
  • Memory handoff: a fact about the user, preferences, or cross-project context surfaces; do not write it into docs/solutions/ or CONCEPTS.md; surface it as a memory-handoff candidate for the memory system to capture.
  • Refresh: maintain existing docs/solutions/ and CONCEPTS.md.
  • Headless: overlays any mode; skip all questions, never pause, apply safe actions, mark uncertain as stale.

Fire automatically on a trigger phrase ("that worked", "it's fixed", "working now", "problem solved", "verified the fix", "tests pass now", "build succeeds", "that approach failed") or after a verified non-trivial fix. Auto-firing is permission to evaluate, not permission to fabricate.

One run can do all three repo-scoped actions: write a learning doc, reconcile a concept, and flag a memory-handoff candidate.

Done when: the mode is routed and mode: tokens are stripped from arguments.

1. Reject-by-default gate

A doc is earned, not assumed. Verify all three preconditions:

  1. The problem is solved, not in progress. An abandoned attempt counts as solved once it is finished (branch dead, decision to stop made); its learning is the anti-pattern: what was tried, the specific reason it failed, and the condition under which it would be worth trying again.
  2. The solution is verified: observed working, not hoped working.
  3. It was non-trivial, not a typo or obvious one-liner.

Then apply the reject-by-default gate (all three filters in order):

  1. Would I forget this? Skip baseline knowledge anyone in this codebase already carries.
  2. Already covered? If an existing docs/solutions/ doc covers it, updating that doc beats spawning a second one. A duplicate is drift, not knowledge.
  3. Universal or local? Scope-qualify the claim. Say when a quirk is repo-specific and when a truth is general. An unqualified claim is a future trap.

The gate governs CONCEPTS.md entries too: a term earns a slot only when its precise local meaning would otherwise be forgotten, it is not already defined there, and it is scope-qualified to this project rather than general programming or domain English.

If nothing clears the gate, say so in one line and exit. A clean "nothing worth capturing here" is a valid, correct result.

Done when: all three preconditions and all three gate filters are evaluated, with a pass or a one-line "nothing qualifies" exit.

2. Capture: research (parallel, read-only)

Scan any injected auto-memory block for entries related to the problem. If it is absent or empty, skip it. If relevant entries exist, carry them as a labeled supplementary context block. Memory is supplementary; when it conflicts with the codebase or conversation, prefer the codebase or conversation. Tag any memory-derived line that lands in the final doc with (auto memory [claude]).

Dispatch three subagents in parallel. Each returns text and writes nothing.

  1. Context Analyzer: from the problem, decide the track (bug vs knowledge), the problem_type, the category directory, and a slug filename ([sanitized-problem-slug].md, no date suffix). Return a frontmatter skeleton and which track applies. Do not invent enum values or fields.
  2. Solution Extractor: extract the substance from the conversation, folding in the auto-memory excerpt as supplementary evidence. Bug track: Problem, Symptoms, What Didn't Work, Solution (with code), Why This Works, Prevention. Knowledge track: Context, Guidance, Why This Matters, When to Apply, Examples.
  3. Related-Docs Finder: grep docs/solutions/ (title:, tags:, module:, component: on extracted keywords; narrow to the candidate subdirectory when known), read only frontmatter of candidates, fully read only strong matches. Score overlap across problem statement, root cause, solution approach, referenced files, prevention rules: High (4-5 dimensions), Moderate (2-3), Low (0-1). Return links and the overlap verdict.

Wait for all three before assembling.

Done when: all three subagents return and their results are assembled for the write step.

3. Capture: assemble and write

  1. Overlap gate: High (4-5) → update the existing doc, keep its path and frontmatter, add last_updated: YYYY-MM-DD. Moderate (2-3) → create normally; note as a refresh/consolidation candidate. Low or none → create normally. Done when: the overlap gate decision is made.
  2. Read assets/solution-template.md; assemble the doc with the track's section structure. Done when: the doc is assembled with the correct track structure.
  3. Frontmatter per the Solution schema section; apply the YAML-safety quoting rule to array items. Done when: frontmatter follows the Solution schema with YAML-safe quoting.
  4. mkdir -p docs/solutions/<category>/, write docs/solutions/<category>/<slug>.md. Done when: the doc file is written to the correct category directory.
  5. Validate: python3 scripts/validate-frontmatter.py <path>. Exit 0 = parser-safe; exit 1 names the offending field. Quote, re-write, re-run until 0. Done when: the validator exits 0.
  6. Read the file back to confirm it landed as intended. Done when: the file is read back and confirmed correct.
  7. Concept reconciliation (optional, when warranted): if the run surfaced a durable project term that clears the gate, reconcile CONCEPTS.md per step 5. Done when: concept reconciliation is performed or skipped.

4. Refresh check (selective, not automatic)

Suggest refresh with a narrow scope only when the new fix contradicts or supersedes an older doc, the work was a refactor/migration/rename/dependency-bump that likely invalidated references, or the Related-Docs Finder surfaced strong refresh candidates or moderate overlap. Otherwise do not. Capture the new learning first; refresh is targeted maintenance after.

Done when: a refresh recommendation is made or explicitly skipped.

5. Vocabulary capture: CONCEPTS.md

CONCEPTS.md at the operating repo root is the shared-vocabulary glossary. It holds words with a precise meaning in this codebase, with one definition per concept. Other knowledge-capture processes may also write to this shared surface; always follow the one-definition-per-concept discipline.

  1. Locate the file: fd -g 'CONCEPTS.md' --max-depth 2. Absent and a term clears the gate → create it. Absent and nothing clears → write nothing; never scaffold an empty file. Done when: the file is located or its absence is handled.
  2. Search for the term and its synonyms: git grep -ni '<term>' CONCEPTS.md. A hit means the concept exists: refresh on drift, never add a second entry. Done when: the term is searched and existing entries are identified.
  3. New term → add one entry: a one-sentence definition of what it means here and what distinguishes it from neighbors; a second paragraph only for non-obvious behavioral rules. Retire synonyms as an *Avoid:* aliases line. No file paths, dates, owners, or version-specific claims. The file stands on its own. Done when: the new entry is written with its definition and alias line.
  4. Read the file back to confirm the merge landed and created no duplicate heading. Done when: the file is read back with no duplicate headings.

6. Refresh: maintain existing docs

Find every .md under docs/solutions/, excluding README.md and anything under _archived/. Top-level records written by compound carry that skill's own seven-field schema (type, tags, confidence, date, source, Context, Implication); the Solution schema below does not apply to them. Read them for overlap and contradiction, and never rewrite one to the Solution schema or mark it stale for lacking those fields. A [scope] hint narrows it: try in order, stop at first hit: (1) subdirectory name, (2) frontmatter module/component/tags match, (3) filename partial match, (4) content keyword. No match → report the miss and exit. No scope hint → process everything.

Classify every candidate doc into exactly one outcome:

Outcome When Action
Keep Still accurate and useful No edit. Report reviewed-and-trustworthy.
Update Core solution correct, references drifted Evidence-backed in-place edits.
Consolidate Two or more docs overlap heavily, both correct Merge unique content into the canonical doc, delete the subsumed one.
Replace Old guidance is now misleading, better answer known Write a trustworthy successor, then delete the old.
Delete No longer useful, applicable, or distinct Delete the file.

Core rules: evidence informs judgment, not a mechanical scorecard; prefer no-write Keep; match docs to reality, not the reverse; be decisive; no low-value churn (no typo fixes, prose polish, cosmetic edits); delete, don't archive (no _archived/, git history preserves everything).

Investigate each doc: read it, cross-reference claims against current codebase. Check references (file paths, symbols, modules: still exist or moved?), solution (does the fix still match how the code works today?), code examples (do snippets reflect current implementation?), related docs (cross-referenced learnings still present and consistent?), overlap (another in-scope doc covering the same domain?). Update vs Replace boundary: references moved but approach still correct → Update; recommended solution conflicts with current code or architecture changed → Replace; if rewriting the Solution section, it is Replace, not Update. Age alone is not a stale signal. Check for a successor before deleting.

Document-set analysis: step back and judge the set as a whole. High overlap across 3+ dimensions → strong Consolidate signal. Older narrow precursor vs newer canonical doc → consolidation candidate. Retrieval-value test: does keeping these separate help discoverability or just create drift risk? Cross-doc contradictions are more urgent than individual staleness.

Execute per action:

  • Keep: no edit, summarize why it remains trustworthy.
  • Update: in-place edits only when solution is still substantively correct. Not Update territory: typo/style-only edits, or old fix is now an anti-pattern → Replace.
  • Consolidate: confirm canonical doc (broader, more current); extract unique content from subsumed doc(s); merge into canonical; repoint cross-references; delete subsumed. Three or more overlapping → process pairwise.
  • Replace: process one at a time. Write a successor, validate with scripts/validate-frontmatter.py until exit 0, then delete the old file. Evidence insufficient → mark stale in place: add status: stale, stale_reason, stale_date: YYYY-MM-DD.
  • Delete: only when referenced code/workflow is gone, problem domain no longer exists, and inbound links absent or unambiguously decorative. Before unlinking, grep repo markdown for citations. Decorative → delete fine, clean up citation; substantive → Replace signal; mixed/unclear → stale-mark. A late-discovered citation that is anything but unambiguously decorative stops the Delete.

Headless variant: skip all questions, never pause. Process every in-scope doc. Attempt all safe actions. Uncertain → mark status: stale. A write that fails is recorded as Recommended, not retried. Emit a report split into Applied and Recommended.

Report:

Refresh Summary
===============
Scanned: N docs

Kept: X   Updated: Y   Consolidated: C   Replaced: Z   Deleted: W   Marked stale: S

Then per file: path, classification, evidence, action taken or recommended.

After refreshing, check whether the repo's documentation would lead an agent to discover and search docs/solutions/. If not, surface a discoverability recommendation in the report. Do not edit instruction files.

Done when: every in-scope doc is classified and acted on, and the refresh report is emitted.

7. Commit

One learning per commit. Stage only the surfaces this skill wrote or edited (a solution doc, CONCEPTS.md, or both). Never stage other dirty files. Commit locally by the operating repo's normal flow. Skip the commit if nothing was modified.

Done when: the commit is made with only this skill's surfaces staged, or skipped when nothing was modified.

Failure and recovery

  • Nothing qualifies: if no fix clears the reject-by-default gate, state "nothing worth capturing here" in one line and exit. This is a valid result, not a failure.
  • Validation failure: scripts/validate-frontmatter.py exits 1 naming the offending field. Quote the value, re-write the file, re-run until exit 0. Do not declare success while validation fails.
  • Overlap collision: High overlap with an existing doc → update the existing doc, do not create a duplicate. Creating a duplicate when an update was warranted is drift, not knowledge.
  • Insufficient evidence for Replace: mark the doc stale in place (status: stale, stale_reason, stale_date) rather than guessing a successor.
  • Late-discovered citation blocks Delete: reclassify to stale-mark or Replace; do not delete.
  • Partial-result rule: if a write fails during refresh, record it as Recommended, do not retry into a mess.
  • Rollback: all writes are to local files under version control. Revert the commit or restore the file from git history.

Output

One validated learning doc at docs/solutions/<category>/<slug>.md (or updated existing doc), optionally a CONCEPTS.md entry and a memory-handoff candidate, or a one-line "nothing qualifies" determination: in refresh mode, a report classifying every scanned doc into Keep/Update/Consolidate/Replace/Delete/stale with applied and recommended actions.

Solution schema

Owner of the field contract is references/schema.md. Do not recopy.

Canonical frontmatter contract for docs/solutions/ learning docs. The validator (scripts/validate-frontmatter.py) only catches silent YAML corruption; the field and enum rules below remain binding.

Two tracks

problem_type picks the track. The track decides which extra fields are required. Pick the narrowest value; best_practice is the Knowledge fallback.

Track problem_types
Bug build_error, test_failure, runtime_error, performance_issue, database_issue, security_issue, ui_bug, integration_issue, logic_error
Knowledge best_practice, documentation_gap, workflow_issue, developer_experience, architecture_pattern, design_pattern, tooling_decision, convention

Required fields (both tracks)

  • title: clear problem/topic title (string).
  • date: YYYY-MM-DD.
  • category: the docs/solutions/ subdirectory (see Category map).
  • module: module or area affected (string).
  • problem_type: one enum value from the tracks table; determines the track.
  • component: component or subsystem involved (free-form string; keep consistent within a repo so frontmatter search works).
  • severity: one of critical, high, medium, low.

Bug-track required fields

  • symptoms: array, 1-5 observable symptoms (errors, broken behavior).
  • root_cause: one of: missing_association, missing_include, missing_index, wrong_api, scope_issue, thread_violation, async_timing, memory_leak, config_error, logic_error, test_isolation, missing_validation, missing_permission, missing_workflow_step, inadequate_documentation, missing_tooling, incomplete_setup.
  • resolution_type: one of: code_fix, migration, config_change, test_fix, dependency_update, environment_setup, workflow_improvement, documentation_update, tooling_addition, seed_data_update.

Knowledge-track fields

No required fields beyond the shared core. All optional:

  • applies_when: array (≤5), conditions where the guidance applies.
  • symptoms: array (≤5), the gap or friction that prompted the guidance.
  • root_cause: from the bug-track enum, if there is a specific one.
  • resolution_type: from the bug-track enum, if a change was applied.

Optional fields (both tracks)

  • related_components: array of other components involved.
  • tags: array (≤8) of search keywords, lowercase and hyphen-separated.

Category map (problem_type → directory)

problem_type directory
build_error docs/solutions/build-errors/
test_failure docs/solutions/test-failures/
runtime_error docs/solutions/runtime-errors/
performance_issue docs/solutions/performance-issues/
database_issue docs/solutions/database-issues/
security_issue docs/solutions/security-issues/
ui_bug docs/solutions/ui-bugs/
integration_issue docs/solutions/integration-issues/
logic_error docs/solutions/logic-errors/
developer_experience docs/solutions/developer-experience/
workflow_issue docs/solutions/workflow-issues/
best_practice docs/solutions/best-practices/
documentation_gap docs/solutions/documentation-gaps/
architecture_pattern docs/solutions/architecture-patterns/
design_pattern docs/solutions/design-patterns/
tooling_decision docs/solutions/tooling-decisions/
convention docs/solutions/conventions/

Filename: [sanitized-problem-slug].md, no date suffix (the date field carries that).

Validation rules

Track from problem_type; shared required fields always present; bug track also requires symptoms, root_cause, resolution_type; knowledge track adds none. Enums match allowed values exactly, arrays respect item counts, date matches YYYY-MM-DD.

YAML safety (array items)

Strict YAML parsers misread array items that start with a reserved indicator as unquoted scalars. For any array-of-strings field (symptoms, applies_when, tags, related_components), wrap the value in double quotes when it starts with any of: ` [ ] { } , * & ! | > % @ ?. Also quote when the value contains ": ". Scalar fields (title:, module:) have a separate failure mode: an unquoted # truncates at the comment, an unquoted : reframes as a mapping. scripts/validate-frontmatter.py catches those; quote and re-run until it exits 0.

Files (outline-driven-development)
  • agents
    • openai.yaml 144 B
      interface:
        display_name: "Autolearn"
        short_description: "Use when a verified non-trivial fix lands or existing solution docs need refresh."
      
  • assets
    • solution-template.md 2.6 KB
      # Solution doc templates
      
      Pick the template for the `problem_type` track (see the solution-schema section in `../SKILL.md`). Replace every bracketed placeholder. Delete sections with no real content instead of leaving placeholders.
      
      ---
      
      ## Bug track
      
      For: `build_error`, `test_failure`, `runtime_error`, `performance_issue`, `database_issue`, `security_issue`, `ui_bug`, `integration_issue`, `logic_error`
      
      <!-- YAML safety: array items (symptoms, applies_when, tags, related_components) that start with ` [ ] { } , * & ! | > % @ ? or contain ": " must be double-quoted. See `../SKILL.md#solution-schema`. -->
      
      ```markdown
      ---
      title: [Clear problem title]
      date: [YYYY-MM-DD]
      category: [docs/solutions subdirectory]
      module: [Module or area]
      problem_type: [schema enum]
      component: [component or subsystem]
      symptoms:
        - [Observable symptom 1]
      root_cause: [schema enum]
      resolution_type: [schema enum]
      severity: [schema enum]
      tags: [keyword-one, keyword-two]
      ---
      
      # [Clear problem title]
      
      ## Problem
      [1-2 sentences: the issue and its user-visible impact]
      
      ## Symptoms
      - [Observable symptom or error]
      
      ## What Didn't Work
      - [Attempted fix and why it failed]
      
      ## Solution
      [The fix that worked, with code snippets when they carry weight]
      
      ## Why This Works
      [Root cause, and why the fix addresses it, the WHY, not a restatement of the diff]
      
      ## Prevention
      - [Concrete practice, test, or guardrail that stops a recurrence]
      
      ## Related
      - [Related docs or issues, if any]
      ```
      
      ---
      
      ## Knowledge track
      
      For: `best_practice`, `documentation_gap`, `workflow_issue`, `developer_experience`, `architecture_pattern`, `design_pattern`, `tooling_decision`, `convention`
      
      <!-- YAML safety: array items (symptoms, applies_when, tags, related_components) that start with ` [ ] { } , * & ! | > % @ ? or contain ": " must be double-quoted. See `../SKILL.md#solution-schema`. -->
      
      ```markdown
      ---
      title: [Clear, descriptive title]
      date: [YYYY-MM-DD]
      category: [docs/solutions subdirectory]
      module: [Module or area]
      problem_type: [schema enum]
      component: [component or subsystem]
      severity: [schema enum]
      applies_when:
        - [Condition where this applies]
      tags: [keyword-one, keyword-two]
      ---
      
      # [Clear, descriptive title]
      
      ## Context
      [What situation, gap, or friction prompted this guidance]
      
      ## Guidance
      [The practice or recommendation, with code examples when useful]
      
      ## Why This Matters
      [Rationale and impact of following, or not following, this]
      
      ## When to Apply
      - [Conditions or situations where this applies]
      
      ## Examples
      [Concrete before/after or usage example showing the practice in action]
      
      ## Related
      - [Related docs or issues, if any]
      ```
      
  • references
    • concepts.md 5.1 KB
      # `CONCEPTS.md`: entry schema and reconciliation rules
      
      Sync-lineage note: `skills/compound/references/concepts.md` is an independent sibling file. It defines compound's own accretion and seeding model for the same `CONCEPTS.md` surface; it is not a copy of this file. Don't merge them.
      
      Read this in Mode 4 (Concepts capture) and when refreshing `CONCEPTS.md`. It defines the entry shape and the reconciliation and refresh loop that enforces one definition per concept. The file lives at the **operating repo root**. It is one glossary shared by autolearn and `compound`, its second legitimate writer. This document covers autolearn's entry schema and reconciliation loop. Create the file only when a term clears the gate; never scaffold an empty file.
      
      ## What earns a slot: the reject-by-default gate, applied to vocabulary
      
      A term earns an entry only if it clears all three filters in order, the same gate that governs a learning doc:
      
      1. **Would I forget its precise local meaning?** Define only words whose meaning *here* is precise enough that a new engineer needs it spelled out to follow code, tickets, or conversation. General programming vocabulary (cache, queue, job, session) and everyday domain English never qualify, however heavily used.
      2. **Already defined?** Search `CONCEPTS.md` for the term and its synonyms before adding. If it is already there, this is not a new entry; refresh on drift, never duplicate. One definition per concept.
      3. **Scope-qualified to this project?** The bar is "specific to this codebase," not "a generic CS term." An unqualified import of general vocabulary is noise.
      
      Nothing clears the gate → write nothing. A clean "no durable term here" is correct.
      
      ## Per entry: the shape
      
      - Definition is one sentence: what the term means in this domain and what distinguishes it from its neighbors. Not a tutorial.
      - Second paragraph only for non-obvious behavioral rules: lifecycle, ownership invariants, cancellation/transition semantics. Never to elaborate the definition itself.
      - Retired synonyms → an aliases line directly under the definition: `*Avoid:* OldName, otherword`. When the team uses several words for one concept, pick the best and retire the rest; the glossary is the agreed vocabulary, not a record of every word ever used.
      
      ## The file stands on its own
      
      Each entry must teach its concept to a reader without access to the codebase, PR history, or chat. This rules out:
      
      - Implementation specifics: file paths, class/function names, table names, library calls.
      - Status fields, dates, owners on entries.
      - Current-config values that will change: specific thresholds, counts, enum values. State the behavior, not the number.
      - Links to PRs, issues, channels, milestones; version-specific claims ("currently X, migrating to Y").
      
      Cross-references between entries *within* the file are fine; they resolve internally. If an entry leans on another **project-specific** term to make sense, that sibling term must also be defined here (an undefined project-specific sibling is itself a candidate).
      
      ## Organization
      
      Cluster concepts by domain relationship so the structure is easy to see: entities with their states, and processes with their stages. A flat list is fine while the file is small; reshape it as it grows. When relationships carry load-bearing meaning (ownership, cardinality, cross-entry lifecycle), capture them in a short `## Relationships` section near the top. Skip that section when entries stand alone.
      
      **Flagged ambiguities (tail of file):** when two terms were used interchangeably and the team settled a distinction, record it as a one-line note: *"'account' had been used for both Customer and User; these are distinct."* This tail is the audit trail for settled opinions.
      
      ## Reconciliation: idempotent merge, one definition per concept
      
      Reconcile; never append blindly.
      
      1. Locate the file: `fd -g 'CONCEPTS.md' --max-depth 2`. Absent + a term clears the gate → create it; absent + nothing clears → do nothing.
      2. Search for the term and its synonyms: `git grep -ni '<term>' CONCEPTS.md`. A hit means the concept exists: go to refresh, do not add a second entry.
      3. New term → add one entry in the right cluster, per the schema above.
      4. **Read the file back** to confirm the merge landed and created no duplicate or near-duplicate heading.
      
      ## Refresh: keep definitions matched to reality
      
      In `mode:refresh [scope]`, maintain `CONCEPTS.md` alongside `docs/solutions/`. Per in-scope concept, re-derive the meaning against current code and pick one outcome:
      
      | Outcome | When | Action |
      |---------|------|--------|
      | **Keep** | Definition still matches the code | No edit. Prefer no-write. |
      | **Refresh** | Meaning drifted; the term still exists | Rewrite the definition to current reality. |
      | **Consolidate** | Two entries name the same concept | Merge into the better name, retire the other as an alias. |
      | **Delete** | The concept's domain is gone from the code | Remove the entry. Age alone is never a reason. |
      
      Match the glossary to the code, not the reverse. No cosmetic churn: typo and prose-polish edits are not refreshes. Headless: skip questions, apply safe refreshes/deletes, leave genuinely ambiguous concepts untouched and report them.
      
    • refresh.md 2.5 KB
      # Refresh: maintain `docs/solutions/` against the current code
      
      Owner. autolearn/SKILL.md section 6 inlines the refresh loop. Do not recopy.
      
      Read this for `autolearn mode:refresh [scope]`. Classify every candidate doc into exactly one outcome:
      
      | Outcome | Meaning | Default action |
      |---------|---------|----------------|
      | **Keep** | Still accurate and useful | No edit. Report reviewed-and-trustworthy. |
      | **Update** | Core solution correct, references drifted | Evidence-backed in-place edits |
      | **Consolidate** | Two+ docs overlap heavily, both correct | Merge unique content into the canonical doc, delete the subsumed one |
      | **Replace** | Old guidance is now misleading, better answer known | Write a trustworthy successor, then delete the old |
      | **Delete** | No longer useful, applicable, or distinct | Delete the file |
      
      ## Execution notes
      
      - No candidate docs at all → report it and point the user at create mode.
      - Pick the lightest interaction path: Focused (1-2 files) → investigate, then recommend; Batch (up to ~8 mostly-independent docs) → grouped recommendations; Broad (9+, ambiguous, or repo-wide) → triage first (inventory frontmatter, cluster by area, spot-check whether referenced files still exist), then investigate in batches.
      - Investigators are read-only subagents: return path, evidence, recommended action, confidence, open questions; never write. Deletes, commits, and frontmatter metadata stay with the orchestrator. The one writing subagent is the Replace successor-drafter; even there the orchestrator validates the result, deletes the old file, and commits.
      - Tag memory-sourced findings `(auto memory [claude])`.
      - Consolidate reverse case: a doc that grew to cover several genuinely independent problems is a split candidate.
      - Replace successor-drafter input: the old doc's full content, an investigation-evidence summary (what changed, what the code does now, why the old guidance misleads), the target path + category, and the contents of `references/schema.md` + `assets/solution-template.md` (never invented from memory). `supersedes: [old-filename]` in the successor is optional.
      
      ## Interactive questions
      
      Most Updates and Consolidations apply directly without asking. Ask only when: the right action is genuinely ambiguous; about to Delete without all auto-delete criteria met; about to Consolidate with no clear-cut canonical; about to Replace. One question at a time, prefer multiple choice, lead with the recommended option.
      
      ## Commit
      
      One concern per commit, ODIN `Op:` trailer in the body.
      
    • schema.md 5.3 KB
      # Frontmatter contract: `docs/solutions/`
      
      Owner. autolearn/SKILL.md inlines the field contract. Do not recopy.
      
      Canonical schema for learning docs written by `autolearn`. Read this when classifying a track, assembling frontmatter, or validating. The validator (`scripts/validate-frontmatter.py`) catches only silent YAML corruption; you must still enforce the field and enum rules below.
      
      ## Two tracks
      
      `problem_type` picks the track. The track decides which extra fields are required. Pick the narrowest value; `best_practice` is the Knowledge fallback.
      
      | Track | problem_types |
      |-------|---------------|
      | **Bug** | `build_error`, `test_failure`, `runtime_error`, `performance_issue`, `database_issue`, `security_issue`, `ui_bug`, `integration_issue`, `logic_error` |
      | **Knowledge** | `best_practice`, `documentation_gap`, `workflow_issue`, `developer_experience`, `architecture_pattern`, `design_pattern`, `tooling_decision`, `convention` |
      
      ## Required fields (both tracks)
      
      - title: clear problem/topic title (string).
      - date: `YYYY-MM-DD`.
      - category: the `docs/solutions/` subdirectory (see Category map).
      - module: module or area affected (string).
      - problem_type: one enum value from the tracks table; determines the track.
      - component: component or subsystem involved (free-form string; keep consistent within a repo so frontmatter search works).
      - severity: one of `critical`, `high`, `medium`, `low`.
      
      ## Bug-track required fields
      
      - symptoms: array, 1-5 observable symptoms (errors, broken behavior).
      - root_cause: one of: `missing_association`, `missing_include`, `missing_index`, `wrong_api`, `scope_issue`, `thread_violation`, `async_timing`, `memory_leak`, `config_error`, `logic_error`, `test_isolation`, `missing_validation`, `missing_permission`, `missing_workflow_step`, `inadequate_documentation`, `missing_tooling`, `incomplete_setup`.
      - resolution_type: one of: `code_fix`, `migration`, `config_change`, `test_fix`, `dependency_update`, `environment_setup`, `workflow_improvement`, `documentation_update`, `tooling_addition`, `seed_data_update`.
      
      ## Knowledge-track fields
      
      No required fields beyond the shared core. All optional:
      
      - applies_when: array (≤5), conditions where the guidance applies.
      - symptoms: array (≤5), the gap or friction that prompted the guidance.
      - root_cause: from the bug-track enum, if there is a specific one.
      - resolution_type: from the bug-track enum, if a change was applied.
      
      ## Optional fields (both tracks)
      
      - related_components: array of other components involved.
      - tags: array (≤8) of search keywords, lowercase and hyphen-separated.
      
      ## Category map (problem_type → directory)
      
      | problem_type | directory |
      |---|---|
      | `build_error` | `docs/solutions/build-errors/` |
      | `test_failure` | `docs/solutions/test-failures/` |
      | `runtime_error` | `docs/solutions/runtime-errors/` |
      | `performance_issue` | `docs/solutions/performance-issues/` |
      | `database_issue` | `docs/solutions/database-issues/` |
      | `security_issue` | `docs/solutions/security-issues/` |
      | `ui_bug` | `docs/solutions/ui-bugs/` |
      | `integration_issue` | `docs/solutions/integration-issues/` |
      | `logic_error` | `docs/solutions/logic-errors/` |
      | `developer_experience` | `docs/solutions/developer-experience/` |
      | `workflow_issue` | `docs/solutions/workflow-issues/` |
      | `best_practice` | `docs/solutions/best-practices/` |
      | `documentation_gap` | `docs/solutions/documentation-gaps/` |
      | `architecture_pattern` | `docs/solutions/architecture-patterns/` |
      | `design_pattern` | `docs/solutions/design-patterns/` |
      | `tooling_decision` | `docs/solutions/tooling-decisions/` |
      | `convention` | `docs/solutions/conventions/` |
      
      Filename: `[sanitized-problem-slug].md`: no date suffix (the `date` field carries that).
      
      ## Validation rules
      
      Track from `problem_type`; shared required fields always present; bug track also requires `symptoms`, `root_cause`, `resolution_type`; knowledge track adds none. Enums match allowed values exactly, arrays respect item counts, `date` matches `YYYY-MM-DD`.
      
      ## Backward compatibility
      
      Pre-existing docs may carry bug-track fields (`symptoms`/`root_cause`/`resolution_type`) on a knowledge-track `problem_type`. These fields are harmless; leave them. Strip them only when rewriting the doc for other reasons. New docs follow the track rules above.
      
      ## YAML safety (array items)
      
      Strict YAML parsers (`yq`, `js-yaml` strict, PyYAML) misread array items that *start* with a reserved indicator as unquoted scalars. For any array-of-strings field (`symptoms`, `applies_when`, `tags`, `related_components`), wrap the value in double quotes when it starts with any of these commonly-hit indicators:
      
      `` ` ``  `[`  `]`  `{`  `}`  `,`  `*`  `&`  `!`  `|`  `>`  `%`  `@`  `?`
      
      Also quote when the value contains `": "`: that punctuation confuses flow-style parsers. The list above is the set that actually shows up at the front of solution-doc values, not an exhaustive YAML indicator catalogue; when unsure, just quote, and let a real YAML parse be the backstop.
      
      Before (breaks strict YAML):
      
      ```yaml
      symptoms:
        - `flush-cache` does not restore in-container mDNS
      ```
      
      After (parses cleanly):
      
      ```yaml
      symptoms:
        - "`flush-cache` does not restore in-container mDNS"
      ```
      
      Scalar fields (`title:`, `module:`) have a separate failure mode, an unquoted ` #` truncates at the comment, an unquoted `: ` reframes as a mapping. `scripts/validate-frontmatter.py` catches those; quote and re-run until it exits 0.
      
  • scripts
    • validate-frontmatter.py 4.9 KB
      #!/usr/bin/env python3
      """Validate autolearn docs/solutions/ frontmatter for parser-safety issues.
      
      Usage:
          python3 validate-frontmatter.py <doc-path>
      
      Exit codes:
          0 — frontmatter passes all checks
          1 — validation failure (diagnostics on stderr)
          2 — usage error (bad arguments, missing file)
      
      Scope: catches *parser-safety* issues — frontmatter that strict YAML parsers
      silently misread. It does NOT validate against the schema's required-field or
      enum-value rules (read references/schema.md for those). The intent is to
      prevent the silent-data-loss bug class where YAML's quoting rules truncate or
      reframe scalar values without raising.
      
      Checks (regex-based, no YAML parser dependency):
          1. File starts and ends frontmatter with `---` lines (matched as full
             lines, not substrings — `----` and `---extra` are rejected)
          2. No top-level scalar value contains ` #` unquoted — YAML reads
             space-then-# as a comment delimiter and silently drops the rest of the
             value
          3. No top-level scalar value contains `: ` unquoted — strict parsers may
             read this as a nested mapping and reframe the value
      
      Reserved-indicator characters (`` ` ``, `*`, `&`, `!`, etc.) are deliberately
      NOT flagged: those produce loud parser errors downstream rather than silent
      corruption, so whatever consumes the doc already catches them. This validator
      exists for silent-corruption prevention, not lint.
      
      Pure-stdlib (no PyYAML or other third-party deps). Runs in <50ms typical.
      Error messages are concrete and actionable so the calling agent can fix and
      retry without ambiguity.
      """
      import os
      import re
      import sys
      
      
      def usage_fail(msg: str) -> "NoReturn":
          sys.stderr.write(f"validate-frontmatter: {msg}\n")
          sys.exit(2)
      
      
      def main(argv: list[str]) -> int:
          if len(argv) != 2:
              usage_fail(f"usage: {os.path.basename(argv[0])} <doc-path>")
      
          doc_path = argv[1]
          if not os.path.isfile(doc_path):
              usage_fail(f"file not found: {doc_path}")
      
          with open(doc_path) as f:
              text = f.read()
      
          issues: list[str] = []
      
          # Check 1: frontmatter delimiters. Match the delimiter as a complete line
          # whose stripped content is exactly `---`. Substring matching (e.g.
          # text.find("\n---", 4)) would falsely accept `----` or `---extra` as a
          # terminator and let malformed docs slip through to downstream parsers
          # that require a strict `---` line.
          lines = text.split("\n")
          if not lines or lines[0].rstrip() != "---":
              sys.stderr.write(
                  f"FAIL: {doc_path}\n"
                  f"  file does not start with '---' frontmatter delimiter line\n"
              )
              return 1
      
          end_idx: int | None = None
          for i in range(1, len(lines)):
              if lines[i].rstrip() == "---":
                  end_idx = i
                  break
      
          if end_idx is None:
              sys.stderr.write(
                  f"FAIL: {doc_path}\n"
                  f"  frontmatter not closed (no '---' line after the opening delimiter)\n"
              )
              return 1
      
          fm_text = "\n".join(lines[1:end_idx])
      
          # Checks 2 & 3: silent-corruption quoting risks on top-level scalar fields.
          # Scan line-by-line and only flag top-level mapping entries (no leading
          # whitespace) whose value isn't already quoted/structured.
          for lineno, line in enumerate(fm_text.split("\n"), start=2):
              stripped = line.lstrip()
              if not stripped or stripped.startswith("#"):
                  continue
              if ":" not in line:
                  continue
              # Top-level mapping keys only — skip nested values and array items
              if line.startswith((" ", "\t")):
                  continue
              # Skip pure list-marker lines like "- item" (can't be top-level in our
              # frontmatter convention, but be defensive)
              if stripped.startswith("- "):
                  continue
      
              key, _, val = line.partition(":")
              val_stripped = val.strip()
              if not val_stripped:
                  # Key with no value on this line — likely a parent of a nested
                  # block (`tags:` followed by `- foo`). Nothing to validate here.
                  continue
              # Already quoted or structured (block scalar, flow collection)
              if val_stripped[0] in '"\'[{|>':
                  continue
      
              if re.search(r"\s#", val_stripped):
                  issues.append(
                      f"line {lineno}: '{key.strip()}' value contains ' #' — quote it. "
                      "YAML treats space-then-# as a comment delimiter and silently "
                      "drops the rest of the value."
                  )
              if re.search(r":\s", val_stripped):
                  issues.append(
                      f"line {lineno}: '{key.strip()}' value contains ': ' — quote it. "
                      "Strict YAML parsers may treat this as a nested mapping."
                  )
      
          if issues:
              sys.stderr.write(f"FAIL: {doc_path}\n")
              for issue in issues:
                  sys.stderr.write(f"  {issue}\n")
              return 1
      
          print(f"OK: {doc_path}")
          return 0
      
      
      if __name__ == "__main__":
          sys.exit(main(sys.argv))
      
  • SKILL.md 19 KB
    ---
    name: autolearn
    description: 'Use when a verified non-trivial fix lands or existing solution docs need refresh. Not for unverified fixes.'
    ---
    
    # Autolearn
    
    ## Contract
    
    | Field | Bound contract |
    |---|---|
    | Trigger | A non-trivial fix has been verified (observed working, not hoped working), or explicit autolearn or refresh invocation. |
    | Authority | Reversible local: writes only the operating repo's docs/solutions/ and repo-root CONCEPTS.md; rollback is version control. No remote mutation. |
    | Side effect | Writes or refreshes docs/solutions/ learning docs and CONCEPTS.md; stages only the surfaces this skill wrote or edited. |
    | Done | A validated learning or concept entry exists, or an explicit determination that nothing qualifies. |
    
    ## Inputs
    
    - The verified fix or solved problem, from conversation history or codebase. Must be supplied or derivable from context.
    - Optional: `mode:refresh [scope]` to maintain existing docs; `mode:headless` for non-interactive operation.
    - Optional: an injected auto-memory block (supplementary context, not primary evidence).
    
    ## Procedure
    
    ### 0. Route the mode
    
    Strip `mode:` tokens from arguments before treating the remainder as context or scope.
    
    - **Capture** (default): document one solved problem into docs/solutions/.
    - Vocabulary capture: a durable, reusable project term surfaces; reconcile CONCEPTS.md.
    - Memory handoff: a fact about the user, preferences, or cross-project context surfaces; do not write it into docs/solutions/ or CONCEPTS.md; surface it as a memory-handoff candidate for the memory system to capture.
    - Refresh: maintain existing docs/solutions/ and CONCEPTS.md.
    - Headless: overlays any mode; skip all questions, never pause, apply safe actions, mark uncertain as stale.
    
    Fire automatically on a trigger phrase ("that worked", "it's fixed", "working now", "problem solved", "verified the fix", "tests pass now", "build succeeds", "that approach failed") or after a verified non-trivial fix. Auto-firing is permission to evaluate, not permission to fabricate.
    
    One run can do all three repo-scoped actions: write a learning doc, reconcile a concept, and flag a memory-handoff candidate.
    
    Done when: the mode is routed and `mode:` tokens are stripped from arguments.
    
    ### 1. Reject-by-default gate
    
    A doc is earned, not assumed. Verify all three preconditions:
    
    1. The problem is solved, not in progress. An abandoned attempt counts as solved once it is finished (branch dead, decision to stop made); its learning is the anti-pattern: what was tried, the specific reason it failed, and the condition under which it would be worth trying again.
    2. The solution is verified: observed working, not hoped working.
    3. It was non-trivial, not a typo or obvious one-liner.
    
    Then apply the reject-by-default gate (all three filters in order):
    
    1. **Would I forget this?** Skip baseline knowledge anyone in this codebase already carries.
    2. **Already covered?** If an existing docs/solutions/ doc covers it, updating that doc beats spawning a second one. A duplicate is drift, not knowledge.
    3. **Universal or local?** Scope-qualify the claim. Say when a quirk is repo-specific and when a truth is general. An unqualified claim is a future trap.
    
    The gate governs CONCEPTS.md entries too: a term earns a slot only when its precise local meaning would otherwise be forgotten, it is not already defined there, and it is scope-qualified to this project rather than general programming or domain English.
    
    If nothing clears the gate, say so in one line and exit. A clean "nothing worth capturing here" is a valid, correct result.
    
    Done when: all three preconditions and all three gate filters are evaluated, with a pass or a one-line "nothing qualifies" exit.
    
    ### 2. Capture: research (parallel, read-only)
    
    Scan any injected auto-memory block for entries related to the problem. If it is absent or empty, skip it. If relevant entries exist, carry them as a labeled supplementary context block. Memory is supplementary; when it conflicts with the codebase or conversation, prefer the codebase or conversation. Tag any memory-derived line that lands in the final doc with `(auto memory [claude])`.
    
    Dispatch three subagents in parallel. Each returns text and writes nothing.
    
    1. **Context Analyzer**: from the problem, decide the track (bug vs knowledge), the problem_type, the category directory, and a slug filename (`[sanitized-problem-slug].md`, no date suffix). Return a frontmatter skeleton and which track applies. Do not invent enum values or fields.
    2. **Solution Extractor**: extract the substance from the conversation, folding in the auto-memory excerpt as supplementary evidence. Bug track: Problem, Symptoms, What Didn't Work, Solution (with code), Why This Works, Prevention. Knowledge track: Context, Guidance, Why This Matters, When to Apply, Examples.
    3. **Related-Docs Finder**: grep docs/solutions/ (`title:`, `tags:`, `module:`, `component:` on extracted keywords; narrow to the candidate subdirectory when known), read only frontmatter of candidates, fully read only strong matches. Score overlap across problem statement, root cause, solution approach, referenced files, prevention rules: High (4-5 dimensions), Moderate (2-3), Low (0-1). Return links and the overlap verdict.
    
    Wait for all three before assembling.
    
    Done when: all three subagents return and their results are assembled for the write step.
    
    ### 3. Capture: assemble and write
    
    1. **Overlap gate**: High (4-5) → update the existing doc, keep its path and frontmatter, add `last_updated: YYYY-MM-DD`. Moderate (2-3) → create normally; note as a refresh/consolidation candidate. Low or none → create normally. Done when: the overlap gate decision is made.
    2. Read `assets/solution-template.md`; assemble the doc with the track's section structure. Done when: the doc is assembled with the correct track structure.
    3. Frontmatter per the Solution schema section; apply the YAML-safety quoting rule to array items. Done when: frontmatter follows the Solution schema with YAML-safe quoting.
    4. `mkdir -p docs/solutions/<category>/`, write `docs/solutions/<category>/<slug>.md`. Done when: the doc file is written to the correct category directory.
    5. Validate: `python3 scripts/validate-frontmatter.py <path>`. Exit 0 = parser-safe; exit 1 names the offending field. Quote, re-write, re-run until 0. Done when: the validator exits 0.
    6. Read the file back to confirm it landed as intended. Done when: the file is read back and confirmed correct.
    7. **Concept reconciliation** (optional, when warranted): if the run surfaced a durable project term that clears the gate, reconcile CONCEPTS.md per step 5. Done when: concept reconciliation is performed or skipped.
    
    ### 4. Refresh check (selective, not automatic)
    
    Suggest refresh with a narrow scope only when the new fix contradicts or supersedes an older doc, the work was a refactor/migration/rename/dependency-bump that likely invalidated references, or the Related-Docs Finder surfaced strong refresh candidates or moderate overlap. Otherwise do not. Capture the new learning first; refresh is targeted maintenance after.
    
    Done when: a refresh recommendation is made or explicitly skipped.
    
    ### 5. Vocabulary capture: CONCEPTS.md
    
    CONCEPTS.md at the operating repo root is the shared-vocabulary glossary. It holds words with a precise meaning in this codebase, with one definition per concept. Other knowledge-capture processes may also write to this shared surface; always follow the one-definition-per-concept discipline.
    
    1. Locate the file: `fd -g 'CONCEPTS.md' --max-depth 2`. Absent and a term clears the gate → create it. Absent and nothing clears → write nothing; never scaffold an empty file. Done when: the file is located or its absence is handled.
    2. Search for the term and its synonyms: `git grep -ni '<term>' CONCEPTS.md`. A hit means the concept exists: refresh on drift, never add a second entry. Done when: the term is searched and existing entries are identified.
    3. New term → add one entry: a one-sentence definition of what it means here and what distinguishes it from neighbors; a second paragraph only for non-obvious behavioral rules. Retire synonyms as an `*Avoid:*` aliases line. No file paths, dates, owners, or version-specific claims. The file stands on its own. Done when: the new entry is written with its definition and alias line.
    4. Read the file back to confirm the merge landed and created no duplicate heading. Done when: the file is read back with no duplicate headings.
    
    ### 6. Refresh: maintain existing docs
    
    Find every `.md` under `docs/solutions/`, excluding `README.md` and anything under `_archived/`. Top-level records written by `compound` carry that skill's own seven-field schema (`type`, `tags`, `confidence`, `date`, `source`, `Context`, `Implication`); the Solution schema below does not apply to them. Read them for overlap and contradiction, and never rewrite one to the Solution schema or mark it stale for lacking those fields. A `[scope]` hint narrows it: try in order, stop at first hit: (1) subdirectory name, (2) frontmatter `module`/`component`/`tags` match, (3) filename partial match, (4) content keyword. No match → report the miss and exit. No scope hint → process everything.
    
    Classify every candidate doc into exactly one outcome:
    
    | Outcome | When | Action |
    |---------|------|--------|
    | Keep | Still accurate and useful | No edit. Report reviewed-and-trustworthy. |
    | Update | Core solution correct, references drifted | Evidence-backed in-place edits. |
    | Consolidate | Two or more docs overlap heavily, both correct | Merge unique content into the canonical doc, delete the subsumed one. |
    | Replace | Old guidance is now misleading, better answer known | Write a trustworthy successor, then delete the old. |
    | Delete | No longer useful, applicable, or distinct | Delete the file. |
    
    Core rules: evidence informs judgment, not a mechanical scorecard; prefer no-write Keep; match docs to reality, not the reverse; be decisive; no low-value churn (no typo fixes, prose polish, cosmetic edits); delete, don't archive (no `_archived/`, git history preserves everything).
    
    Investigate each doc: read it, cross-reference claims against current codebase. Check references (file paths, symbols, modules: still exist or moved?), solution (does the fix still match how the code works today?), code examples (do snippets reflect current implementation?), related docs (cross-referenced learnings still present and consistent?), overlap (another in-scope doc covering the same domain?). Update vs Replace boundary: references moved but approach still correct → Update; recommended solution conflicts with current code or architecture changed → Replace; if rewriting the Solution section, it is Replace, not Update. Age alone is not a stale signal. Check for a successor before deleting.
    
    Document-set analysis: step back and judge the set as a whole. High overlap across 3+ dimensions → strong Consolidate signal. Older narrow precursor vs newer canonical doc → consolidation candidate. Retrieval-value test: does keeping these separate help discoverability or just create drift risk? Cross-doc contradictions are more urgent than individual staleness.
    
    Execute per action:
    - Keep: no edit, summarize why it remains trustworthy.
    - Update: in-place edits only when solution is still substantively correct. Not Update territory: typo/style-only edits, or old fix is now an anti-pattern → Replace.
    - Consolidate: confirm canonical doc (broader, more current); extract unique content from subsumed doc(s); merge into canonical; repoint cross-references; delete subsumed. Three or more overlapping → process pairwise.
    - Replace: process one at a time. Write a successor, validate with `scripts/validate-frontmatter.py` until exit 0, then delete the old file. Evidence insufficient → mark stale in place: add `status: stale`, `stale_reason`, `stale_date: YYYY-MM-DD`.
    - Delete: only when referenced code/workflow is gone, problem domain no longer exists, and inbound links absent or unambiguously decorative. Before unlinking, grep repo markdown for citations. Decorative → delete fine, clean up citation; substantive → Replace signal; mixed/unclear → stale-mark. A late-discovered citation that is anything but unambiguously decorative stops the Delete.
    
    Headless variant: skip all questions, never pause. Process every in-scope doc. Attempt all safe actions. Uncertain → mark `status: stale`. A write that fails is recorded as Recommended, not retried. Emit a report split into Applied and Recommended.
    
    Report:
    ```
    Refresh Summary
    ===============
    Scanned: N docs
    
    Kept: X   Updated: Y   Consolidated: C   Replaced: Z   Deleted: W   Marked stale: S
    ```
    Then per file: path, classification, evidence, action taken or recommended.
    
    After refreshing, check whether the repo's documentation would lead an agent to discover and search `docs/solutions/`. If not, surface a discoverability recommendation in the report. Do not edit instruction files.
    
    Done when: every in-scope doc is classified and acted on, and the refresh report is emitted.
    
    ### 7. Commit
    
    One learning per commit. Stage only the surfaces this skill wrote or edited (a solution doc, CONCEPTS.md, or both). Never stage other dirty files. Commit locally by the operating repo's normal flow. Skip the commit if nothing was modified.
    
    Done when: the commit is made with only this skill's surfaces staged, or skipped when nothing was modified.
    
    ## Failure and recovery
    - Nothing qualifies: if no fix clears the reject-by-default gate, state "nothing worth capturing here" in one line and exit. This is a valid result, not a failure.
    - Validation failure: `scripts/validate-frontmatter.py` exits 1 naming the offending field. Quote the value, re-write the file, re-run until exit 0. Do not declare success while validation fails.
    - Overlap collision: High overlap with an existing doc → update the existing doc, do not create a duplicate. Creating a duplicate when an update was warranted is drift, not knowledge.
    - Insufficient evidence for Replace: mark the doc stale in place (`status: stale`, `stale_reason`, `stale_date`) rather than guessing a successor.
    - Late-discovered citation blocks Delete: reclassify to stale-mark or Replace; do not delete.
    - Partial-result rule: if a write fails during refresh, record it as Recommended, do not retry into a mess.
    - Rollback: all writes are to local files under version control. Revert the commit or restore the file from git history.
    
    ## Output
    One validated learning doc at `docs/solutions/<category>/<slug>.md` (or updated existing doc), optionally a CONCEPTS.md entry and a memory-handoff candidate, or a one-line "nothing qualifies" determination: in refresh mode, a report classifying every scanned doc into Keep/Update/Consolidate/Replace/Delete/stale with applied and recommended actions.
    
    ## Solution schema
    
    Owner of the field contract is references/schema.md. Do not recopy.
    
    Canonical frontmatter contract for `docs/solutions/` learning docs. The validator (`scripts/validate-frontmatter.py`) only catches silent YAML corruption; the field and enum rules below remain binding.
    
    ### Two tracks
    
    `problem_type` picks the track. The track decides which extra fields are required. Pick the narrowest value; `best_practice` is the Knowledge fallback.
    
    | Track | problem_types |
    |-------|---------------|
    | Bug | `build_error`, `test_failure`, `runtime_error`, `performance_issue`, `database_issue`, `security_issue`, `ui_bug`, `integration_issue`, `logic_error` |
    | Knowledge | `best_practice`, `documentation_gap`, `workflow_issue`, `developer_experience`, `architecture_pattern`, `design_pattern`, `tooling_decision`, `convention` |
    
    ### Required fields (both tracks)
    
    - `title`: clear problem/topic title (string).
    - `date`: `YYYY-MM-DD`.
    - `category`: the `docs/solutions/` subdirectory (see Category map).
    - `module`: module or area affected (string).
    - `problem_type`: one enum value from the tracks table; determines the track.
    - `component`: component or subsystem involved (free-form string; keep consistent within a repo so frontmatter search works).
    - `severity`: one of `critical`, `high`, `medium`, `low`.
    
    ### Bug-track required fields
    
    - `symptoms`: array, 1-5 observable symptoms (errors, broken behavior).
    - `root_cause`: one of: `missing_association`, `missing_include`, `missing_index`, `wrong_api`, `scope_issue`, `thread_violation`, `async_timing`, `memory_leak`, `config_error`, `logic_error`, `test_isolation`, `missing_validation`, `missing_permission`, `missing_workflow_step`, `inadequate_documentation`, `missing_tooling`, `incomplete_setup`.
    - `resolution_type`: one of: `code_fix`, `migration`, `config_change`, `test_fix`, `dependency_update`, `environment_setup`, `workflow_improvement`, `documentation_update`, `tooling_addition`, `seed_data_update`.
    
    ### Knowledge-track fields
    
    No required fields beyond the shared core. All optional:
    
    - `applies_when`: array (≤5), conditions where the guidance applies.
    - `symptoms`: array (≤5), the gap or friction that prompted the guidance.
    - `root_cause`: from the bug-track enum, if there is a specific one.
    - `resolution_type`: from the bug-track enum, if a change was applied.
    
    ### Optional fields (both tracks)
    
    - `related_components`: array of other components involved.
    - `tags`: array (≤8) of search keywords, lowercase and hyphen-separated.
    
    ### Category map (problem_type → directory)
    
    | problem_type | directory |
    |---|---|
    | `build_error` | `docs/solutions/build-errors/` |
    | `test_failure` | `docs/solutions/test-failures/` |
    | `runtime_error` | `docs/solutions/runtime-errors/` |
    | `performance_issue` | `docs/solutions/performance-issues/` |
    | `database_issue` | `docs/solutions/database-issues/` |
    | `security_issue` | `docs/solutions/security-issues/` |
    | `ui_bug` | `docs/solutions/ui-bugs/` |
    | `integration_issue` | `docs/solutions/integration-issues/` |
    | `logic_error` | `docs/solutions/logic-errors/` |
    | `developer_experience` | `docs/solutions/developer-experience/` |
    | `workflow_issue` | `docs/solutions/workflow-issues/` |
    | `best_practice` | `docs/solutions/best-practices/` |
    | `documentation_gap` | `docs/solutions/documentation-gaps/` |
    | `architecture_pattern` | `docs/solutions/architecture-patterns/` |
    | `design_pattern` | `docs/solutions/design-patterns/` |
    | `tooling_decision` | `docs/solutions/tooling-decisions/` |
    | `convention` | `docs/solutions/conventions/` |
    
    Filename: `[sanitized-problem-slug].md`, no date suffix (the `date` field carries that).
    
    ### Validation rules
    
    Track from `problem_type`; shared required fields always present; bug track also requires `symptoms`, `root_cause`, `resolution_type`; knowledge track adds none. Enums match allowed values exactly, arrays respect item counts, `date` matches `YYYY-MM-DD`.
    
    ### YAML safety (array items)
    
    Strict YAML parsers misread array items that start with a reserved indicator as unquoted scalars. For any array-of-strings field (`symptoms`, `applies_when`, `tags`, `related_components`), wrap the value in double quotes when it starts with any of: `` ` `` `[` `]` `{` `}` `,` `*` `&` `!` `|` `>` `%` `@` `?`. Also quote when the value contains `": "`. Scalar fields (`title:`, `module:`) have a separate failure mode: an unquoted ` #` truncates at the comment, an unquoted `: ` reframes as a mapping. `scripts/validate-frontmatter.py` catches those; quote and re-run until it exits 0.
    

Comments (0)

Sign in to join the conversation.

No comments yet.

Reviews (0)

No reviews yet.

Related