From a4e73c830f63638f6b53bdf4b67c65ff88bc4016 Mon Sep 17 00:00:00 2001 From: Lars Date: Fri, 12 Jun 2026 08:27:39 +0200 Subject: [PATCH] Implement Pruning of Filled Steps from Roadmap Unfilled - Introduced `_prune_filled_from_roadmap_unfilled` function to remove steps with filled exercises from the unfilled roadmap, preventing outdated references. - Updated `_run_roadmap_rematch_loop` to incorporate the new pruning logic, ensuring only relevant unfilled steps are retained during rematching. - Added tests for the pruning function to validate its behavior with various step scenarios. - Bumped version to 0.8.232 to reflect the new functionality and improvements. --- backend/planning_exercise_path_builder.py | 28 +++++++++++++++++++++ backend/tests/test_planning_path_rematch.py | 12 +++++++++ backend/version.py | 4 +-- 3 files changed, 42 insertions(+), 2 deletions(-) diff --git a/backend/planning_exercise_path_builder.py b/backend/planning_exercise_path_builder.py index 5c2c700..654f91d 100644 --- a/backend/planning_exercise_path_builder.py +++ b/backend/planning_exercise_path_builder.py @@ -1518,6 +1518,31 @@ def _merge_rematch_unfilled( return kept +def _prune_filled_from_roadmap_unfilled( + steps: Sequence[Mapping[str, Any]], + roadmap_unfilled: List[Tuple[int, StageSpecArtifact]], +) -> List[Tuple[int, StageSpecArtifact]]: + """Entfernt Stufen mit Bibliotheks-Treffer — verhindert veraltete roadmap_unfilled-Hinweise.""" + filled_majors: Set[int] = set() + for raw in steps: + if raw.get("exercise_id") is None: + continue + midx = raw.get("roadmap_major_step_index") + if midx is None: + continue + try: + filled_majors.add(int(midx)) + except (TypeError, ValueError): + continue + if not filled_majors: + return roadmap_unfilled + return [ + item + for item in roadmap_unfilled + if int(item[1].major_step_index) not in filled_majors + ] + + def _run_roadmap_rematch_loop( cur, *, @@ -1683,6 +1708,7 @@ def _run_roadmap_rematch_loop( current_stripped = prune_stripped_after_rematch(current_stripped, round_log) roadmap_unfilled = _merge_rematch_unfilled(roadmap_unfilled, rematch_new_unfilled) + roadmap_unfilled = _prune_filled_from_roadmap_unfilled(steps, roadmap_unfilled) use_initial_off_topic = False off_topic_steps = detect_off_topic_steps( @@ -1721,6 +1747,8 @@ def _run_roadmap_rematch_loop( goal_query=goal_query, ) + roadmap_unfilled = _prune_filled_from_roadmap_unfilled(steps, roadmap_unfilled) + return ( steps, rematch_log, diff --git a/backend/tests/test_planning_path_rematch.py b/backend/tests/test_planning_path_rematch.py index aaeddb5..9337c9e 100644 --- a/backend/tests/test_planning_path_rematch.py +++ b/backend/tests/test_planning_path_rematch.py @@ -223,3 +223,15 @@ def test_rematch_unfilled_leaves_placeholder_step(): assert slot1["roadmap_match_source"] == "unfilled" assert log[0]["action"] == "rematch_unfilled" assert len(unfilled) == 1 + + +def test_prune_filled_from_roadmap_unfilled(): + from planning_exercise_path_builder import _prune_filled_from_roadmap_unfilled + + spec = StageSpecArtifact(major_step_index=5, learning_goal="Zielgenauigkeit") + steps = [{"exercise_id": 99, "roadmap_major_step_index": 5}] + kept = _prune_filled_from_roadmap_unfilled(steps, [(4, spec)]) + assert kept == [] + unfilled_steps = [{"exercise_id": None, "roadmap_major_step_index": 5}] + kept2 = _prune_filled_from_roadmap_unfilled(unfilled_steps, [(4, spec)]) + assert len(kept2) == 1 diff --git a/backend/version.py b/backend/version.py index ce251cc..2e8c8e2 100644 --- a/backend/version.py +++ b/backend/version.py @@ -1,6 +1,6 @@ # Shinkan Jinkendo Version Information -APP_VERSION = "0.8.231" +APP_VERSION = "0.8.232" BUILD_DATE = "2026-05-22" DB_SCHEMA_VERSION = "20260607090" @@ -38,7 +38,7 @@ MODULE_VERSIONS = { "skill_profiles": "1.0.0", # Phase 3: gewichtetes Fähigkeiten-Profil + skill-discovery/suggestions "methods": "0.1.0", "exercises": "2.37.1", # KI-Endpoints: feature_usage nach ai_calls consume - "planning_exercise_suggest": "0.23.6", # Gap-Angebote nach Rematch; LLM-QA auf finalem Pfad; Post-Match-Gate + "planning_exercise_suggest": "0.23.7", # roadmap_unfilled nach Rematch-Treffer bereinigen "training_units": "0.4.0", # POST .../publish-to-framework: Ablauf aus geplanter Einheit → Rahmen-Slot-Blueprint "training_programs": "0.1.0", "planning": "0.15.0", # Vorlagen: Strukturvorschau, Bearbeiten inkl. Split-Sessions + Beschreibung