@@ -79,7 +79,6 @@ class _FakeToolQualityRecord:
7979
8080from openspace .skill_engine .evolution .triggers import (
8181 _ANALYSIS_CONTEXT_MAX ,
82- _ANALYSIS_NOTE_MAX_CHARS ,
8382 _FALLBACK_THRESHOLD ,
8483 _HIGH_APPLIED_FOR_FIX ,
8584 _LOW_COMPLETION_THRESHOLD ,
@@ -208,7 +207,7 @@ def test_fix_loads_single_target(self):
208207 evolver = MagicMock ()
209208 evolver ._available_tools = []
210209 evolver ._store .load_record .return_value = _FakeSkillRecord (path = "/skills/s1/SKILL.md" )
211- evolver ._registry . load_skill_content .return_value = "# Content"
210+ evolver ._load_skill_content .return_value = "# Content"
212211
213212 analysis = _FakeAnalysis ()
214213 suggestion = _FakeSuggestion (
@@ -241,8 +240,8 @@ async def test_no_candidates_returns_empty(self):
241240 async def test_suggestions_dispatched (self ):
242241 evolver = MagicMock ()
243242 evolver ._available_tools = []
244- evolver . _store . load_record . return_value = _FakeSkillRecord ( path = "/s/SKILL.md" )
245- evolver ._registry . load_skill_content . return_value = "# Content"
243+ # After MRO fix: process_analysis calls evolver._build_context_from_analysis
244+ evolver ._build_context_from_analysis . return_value = MagicMock () # truthy context
246245 evolver ._execute_contexts = AsyncMock (return_value = [_FakeSkillRecord (name = "evolved" )])
247246
248247 analysis = _FakeAnalysis (
@@ -251,9 +250,7 @@ async def test_suggestions_dispatched(self):
251250 ],
252251 )
253252
254- with patch ("openspace.skill_engine.evolution.triggers.EvolutionType" , _FakeEvolutionType ), \
255- patch ("openspace.skill_engine.evolution.triggers.EvolutionTrigger" , _FakeTrigger ):
256- result = await process_analysis (evolver , analysis )
253+ result = await process_analysis (evolver , analysis )
257254
258255 assert len (result ) == 1
259256 evolver ._execute_contexts .assert_awaited_once ()
@@ -300,6 +297,28 @@ async def test_prunes_recovered_tools(self):
300297 assert "old_tool" not in evolver ._addressed_degradations
301298
302299
300+ @pytest .mark .asyncio
301+ async def test_confirmed_skill_triggers_execution (self ):
302+ """Happy path: LLM confirms → context built → _execute_contexts fires."""
303+ evolver = MagicMock ()
304+ evolver ._addressed_degradations = {}
305+ evolver ._store .find_skills_by_tool .return_value = ["skill-1" ]
306+ evolver ._store .load_record .return_value = _FakeSkillRecord (path = "/s/SKILL.md" )
307+ evolver ._load_skill_content .return_value = "# Content"
308+ evolver ._store .load_analyses .return_value = []
309+ evolver ._llm_confirm_evolution = AsyncMock (return_value = True )
310+ evolver ._execute_contexts = AsyncMock (return_value = [_FakeSkillRecord (name = "fixed" )])
311+ evolver ._available_tools = []
312+
313+ tool = _FakeToolQualityRecord (tool_key = "shell_exec" )
314+ result = await process_tool_degradation (evolver , [tool ])
315+
316+ assert len (result ) == 1
317+ assert result [0 ].name == "fixed"
318+ evolver ._llm_confirm_evolution .assert_awaited_once ()
319+ evolver ._execute_contexts .assert_awaited_once ()
320+
321+
303322# ---------------------------------------------------------------------------
304323# process_metric_check tests
305324# ---------------------------------------------------------------------------
@@ -327,12 +346,37 @@ async def test_healthy_skills_skipped(self):
327346 ),
328347 }
329348 evolver ._available_tools = []
349+ # After MRO fix: process_metric_check calls evolver._diagnose_skill_health
350+ evolver ._diagnose_skill_health .return_value = (None , "" )
330351
331- with patch ("openspace.skill_engine.evolution.triggers.EvolutionType" , _FakeEvolutionType ):
332- result = await process_metric_check (evolver , min_selections = 5 )
352+ result = await process_metric_check (evolver , min_selections = 5 )
333353
334354 assert result == []
335355
356+ @pytest .mark .asyncio
357+ async def test_confirmed_metric_triggers_execution (self ):
358+ """Happy path: diagnose → LLM confirm → _execute_contexts fires."""
359+ evolver = MagicMock ()
360+ evolver ._store .load_active .return_value = {
361+ "s1" : _FakeSkillRecord (total_selections = 10 ),
362+ }
363+ evolver ._available_tools = []
364+ evolver ._diagnose_skill_health .return_value = (_FakeEvolutionType .FIX , "needs fix" )
365+ evolver ._load_skill_content .return_value = "# Skill Content"
366+ evolver ._store .load_analyses .return_value = []
367+ evolver ._llm_confirm_evolution = AsyncMock (return_value = True )
368+ evolver ._execute_contexts = AsyncMock (return_value = [_FakeSkillRecord (name = "improved" )])
369+
370+ with patch ("openspace.skill_engine.evolution.triggers.EvolutionType" , _FakeEvolutionType ), \
371+ patch ("openspace.skill_engine.evolution.triggers.EvolutionTrigger" , _FakeTrigger ), \
372+ patch ("openspace.skill_engine.evolution.triggers.EvolutionSuggestion" , _FakeSuggestion ):
373+ result = await process_metric_check (evolver , min_selections = 5 )
374+
375+ assert len (result ) == 1
376+ assert result [0 ].name == "improved"
377+ evolver ._llm_confirm_evolution .assert_awaited_once ()
378+ evolver ._execute_contexts .assert_awaited_once ()
379+
336380
337381# ---------------------------------------------------------------------------
338382# Constants integrity
@@ -348,7 +392,6 @@ def test_thresholds_are_numbers(self):
348392
349393 def test_analysis_constants (self ):
350394 assert isinstance (_ANALYSIS_CONTEXT_MAX , int )
351- assert isinstance (_ANALYSIS_NOTE_MAX_CHARS , int )
352395
353396 def test_constants_no_longer_in_evolver (self ):
354397 """Constants should be imported, not defined, in evolver.py."""
0 commit comments