record_experience() captures narrative, the skill compiler needs a claim — separate the lesson from what happened #35
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Follow-up to #33, which is closed on the diagnostic half. This is the schema half, left open deliberately because it is a design decision with a migration behind it rather than a bug.
The observation, restated
compile_skill(domain='python')found 29 candidates and produced zero rules. The gate was right; the candidates were the problem:find_all_duplicates()and_has_negation_pair()functions from…"These can never become rules however often something like them recurs, because they aren't the kind of sentence a rule is. The counter-example from #33 is the one that proves it — the valkey-py
dropindex()memory whose breakthrough generalises: "when a fake stands in for a component with known divergent behaviour, the fake must reproduce the divergence." That is rule-shaped, and it got that way because whoever wrote it happened to be in a generalising mood.Why the field produces narrative
record_experience()takesbreakthrough: str | Nonedocumented as "What finally worked." That prompt asks for the past tense of a specific incident, and gets it.extract_lessons()(memory/skills.py:495) then reads exactly that field and turns it into adorule.So the compiler is asking one field to do two jobs: be the record of what happened on this occasion, and be a claim that generalises beyond it. Those are different sentences, and when they conflict the narrative wins because that's what the docstring asked for.
Reinforcement can't rescue this. Clustering two narratives about different incidents finds no recurrence, and if it did, the rule it emitted would be a generalisation nobody made deliberately.
Proposed shape
Add a
lessonargument alongsidebreakthrough, stored as its own field:breakthrough— what happened, this time. Keeps its current meaning; still whatexplain_memoryand the experience views show.lesson— the generalisable claim, if there is one. Prompted for separately, and explicitly optional: plenty of real work teaches nothing that transfers, and a required field would just collect narrative wearing a different label.extract_lessons()preferslessonand falls back tobreakthroughwhen it's absent, which makes this additive with no migration: every existing memory keeps contributing exactly what it does today, and new ones can do better.gotchasalready tends to be written as a claim, so it likely needs nothing.What to decide
lessonoptional or prompted-with-a-nudge? Optional is honest and risks the field going unused. The agent instructions are the lever, not validation.lessonis a deliberate generalisation, so arguably it should count for more than abreakthroughfallback — but that's a scoring change and #30 is a fresh reminder to land a field and change what reads it in separate releases.bless()change? It currently promotes abreakthroughregardless of outcome. Iflessonexists, blessing arguably means "this claim generalises", which is closer to what blessing already meant.Not urgent, but not free either
Nothing is broken. Broad domains will keep compiling nothing and the diagnostics from 6.7.1 now explain why at the point of use. The cost of waiting is only that experience keeps accumulating in a shape the compiler can't use, which is the thing #33 flagged as worth getting ahead of.
Implemented on
v7.0.xin7a4c2ba, for 7.0.0.The four decisions, as settled:
record_experience(..., lesson=None). No validation; the tool docstring and the agent instructions (instructions.py,claude_config/CLAUDE.md) ask for a claim that holds beyond the incident, and to leave it out when nothing transfers.extract_lessons()useslessonfor the do-rule and falls back tobreakthrough. Same outcome gate, same reinforcement weight. A scoring change for lessons would ship on its own.bless()unchanged. It marks a memory eligible andextract_lessonsdecides the text, so blessing now promotes the lesson when one exists.breakthroughkeeps its meaning and is still what the experience views show.lessonis in the episodic return fields and the compiler's pool projection (tested, since a field missing from a projection reads asNone), and is returned byrecall(),recall_detail(),get_experience()andexplain_memory(), and shown on the web UI memory detail page.Tests:
tests/test_issue35_lesson.py. Full suite 1930 passed, 1 skipped,memory/andtools/at 100%.