PR #393 extended AgentCaseExtracted and SkillClusterUpdated with case-body fields, all defaulted so pending pre-1.2.3 events in OME's SQLite run_record queue still deserialize on upgrade.
The pure back-compat path is safe: a 1.2.2 SkillClusterUpdated deserializes with quality_score = 0.0, and everalgo short-circuits at agent_memory/skill.py:189-199 (case.quality_score < skip_quality_threshold = 0.2) returning [] with no LLM call.
The leak is one event upstream. A pending 1.2.2 AgentCaseExtracted deserializes with approach = "" / key_insight = None but a real quality_score. trigger_skill_clustering then emits a fully 1.2.3-shaped SkillClusterUpdated carrying approach = "" alongside e.g. quality_score = 0.8, which clears the 0.2 floor. The LLM is asked to distil a skill from a task_intent with an empty approach; if it responds update, _persist_skill → write_main full-replaces the SKILL.md body — the corruption class #393 exists to prevent.
Window is narrow (upgrade with queued work) and approach = "" is not a reliable sentinel: AgentCase.approach is a required str with no normalization (memory/models.py:291), unlike key_insight which from_algo normalizes falsy → None (:321-322). So a legitimately-empty approach is indistinguishable from a back-compat default.
Options: a logger.warning + early return in extract_agent_skill when not event.approach; a schema-version field on the event; or drain the OME queue as an upgrade step. The first is cheap but carries a false-positive risk worth weighing.
Found by the whole-branch review of #393.
PR #393 extended
AgentCaseExtractedandSkillClusterUpdatedwith case-body fields, all defaulted so pending pre-1.2.3 events in OME's SQLiterun_recordqueue still deserialize on upgrade.The pure back-compat path is safe: a 1.2.2
SkillClusterUpdateddeserializes withquality_score = 0.0, and everalgo short-circuits atagent_memory/skill.py:189-199(case.quality_score < skip_quality_threshold = 0.2) returning[]with no LLM call.The leak is one event upstream. A pending 1.2.2
AgentCaseExtracteddeserializes withapproach = ""/key_insight = Nonebut a realquality_score.trigger_skill_clusteringthen emits a fully 1.2.3-shapedSkillClusterUpdatedcarryingapproach = ""alongside e.g.quality_score = 0.8, which clears the 0.2 floor. The LLM is asked to distil a skill from atask_intentwith an emptyapproach; if it respondsupdate,_persist_skill→write_mainfull-replaces theSKILL.mdbody — the corruption class #393 exists to prevent.Window is narrow (upgrade with queued work) and
approach = ""is not a reliable sentinel:AgentCase.approachis a requiredstrwith no normalization (memory/models.py:291), unlikekey_insightwhichfrom_algonormalizes falsy →None(:321-322). So a legitimately-empty approach is indistinguishable from a back-compat default.Options: a
logger.warning+ early return inextract_agent_skillwhennot event.approach; a schema-version field on the event; or drain the OME queue as an upgrade step. The first is cheap but carries a false-positive risk worth weighing.Found by the whole-branch review of #393.