Skip to content

feat(skill-tree): learned procedural memory the maestro grows and retrieves (#458)#462

Merged
hadamrd merged 2 commits into
trunkfrom
feat/skill-tree
Jun 27, 2026
Merged

feat(skill-tree): learned procedural memory the maestro grows and retrieves (#458)#462
hadamrd merged 2 commits into
trunkfrom
feat/skill-tree

Conversation

@hadamrd

@hadamrd hadamrd commented Jun 27, 2026

Copy link
Copy Markdown
Owner

Implements the skill tree epic (#458): a living, evolving memory of how to do X in this repo that the loop writes from its own critic-clean merges and reads back into the next worker's brief — so workers stop re-deriving repo conventions cold.

It wakes forge-loop's dormant procedural-memory substrate (the SqliteMemoryStore + record_procedural_skill) rather than adding a parallel system.

The tree

Each card carries an area:<path> tag — its address in the tree. area_ancestors walks most-specific-leaf → ancestor node.

  • Leaves = concrete procedure cards (trigger + recipe + pitfalls, stamped with the merged commit SHA).
  • Internal nodes (area-node) = generalised patterns distilled once enough sibling leaves accrue.

Lifecycle

  1. Harvest — a one-shot librarian (skill_librarian.py, LLM behind an injected seam) distils diff + acceptance → SkillCard; harvest_skills_from_merge records it with SHA provenance. Wired best-effort into the post-merge hook (runner/tick.py), gated by misc.skill_tree_harvest (FORGE_SKILL_TREE_HARVEST, on by default).
  2. Retrieve + inject — area-weighted deterministic ranking (memory/skills.py) feeds render_skill_section into make_brief + make_repair_brief; emits skill_injected for measurement. Retrieval is cheap + local so it's always on.
  3. Curateexpire_stale_skills retires cards whose proof SHA aged out of history; promote_internal_nodes generalises sibling leaves. Events: skill_harvested / injected / promoted / expired.

Design traps engineered around

  • Stale > empty — every card carries provenance SHA + confidence; expiry drops stale cards from retrieval (adversarial test: an expired card matching the query is NOT returned).
  • Retrieval is the hard part — honest deterministic area/keyword ranking now, with a clean seam for semantic (Lumen) retrieval as a documented fast-follow.
  • Contradiction sprawl — one librarian writes; supersession preserves lineage; no free-for-all.

Tests (TDD, adversarial)

New: test_skill_area_tags, test_skill_librarian, test_skill_harvest, test_skill_events, test_skill_retrieval, test_skill_curation, test_worker_brief_skills — each behavior has a test that goes red if the harvest/inject/expiry is removed. Full suite green except pre-existing test_runner_retry.py cases that require a GitHub token (they fail identically on clean trunk in a token-less shell). Lint + format clean.

Out of scope (fast-follow)

Semantic/Lumen retrieval (seam only); a UI surface; cross-repo skill sharing.

Spec: docs/design/81-skill-tree.md.

🤖 Generated with Claude Code

hadamrd and others added 2 commits June 27, 2026 13:12
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…rieves (#458)

Wakes forge-loop's dormant procedural-memory substrate into a living "skill
tree": the loop distils a reusable "how to do X in this repo" card from each
critic-clean merge, retrieves the relevant ones into the next worker's brief,
and curates them over time — so workers stop re-deriving repo conventions cold.

Tree structure
- area-path tags (`area:<path>`) give each card its address; `area_ancestors`
  walks most-specific-leaf -> ancestor nodes (memory/models.py).
- leaves = concrete procedure cards; internal nodes = generalised patterns
  (`area-node` tag) distilled once enough sibling leaves accrue.

Lifecycle
- Harvest: a one-shot "librarian" (skill_librarian.py, LLM behind an injected
  seam) distils diff+acceptance -> SkillCard; harvest_skills_from_merge records
  it with the merged commit SHA as provenance. Wired best-effort into the
  post-merge hook (runner/tick.py), gated by `misc.skill_tree_harvest`.
- Retrieve+inject: area-weighted deterministic ranking (memory/skills.py) feeds
  render_skill_section into make_brief + make_repair_brief; emits skill_injected
  for measurement. (Semantic/Lumen retrieval is a documented fast-follow behind
  the same seam.)
- Curate: expire_stale_skills retires cards whose proof SHA aged out of history;
  promote_internal_nodes generalises sibling leaves. Events: skill_harvested /
  injected / promoted / expired.

Extends the existing SqliteMemoryStore + record_procedural_skill (now carries
area tags + evidence refs + confidence) — no parallel store. Fully TDD'd with
adversarial tests (remove harvest/inject/expiry and a test goes red); retrieval
excludes non-matches and expired cards.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@hadamrd hadamrd merged commit 060eb6a into trunk Jun 27, 2026
2 checks passed
@hadamrd hadamrd deleted the feat/skill-tree branch June 27, 2026 19:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant