fix(skill-versioning): consume divergence_tolerance, replace vacuous catalog-isolation guard

- divergence_tolerance was loaded from config but diff_flow_versions
  never consulted it, so structural_divergence was always plain
  sequence-equality regardless of the configured tolerance. Now
  actually applied per design.md D4.
- The skill-catalog-subscription isolation guard test asserted
  'skills.catalog' not in sys.modules, which is vacuously true since
  that module doesn't exist anywhere yet. Replaced with a real check
  against store.py's actual imports.

openspec: skill-versioning capability, archived change skill-learning-runtime
This commit is contained in:
2026-07-07 08:31:11 +08:00
parent ca5e300289
commit a279441aee
3 changed files with 161 additions and 29 deletions
+35 -2
View File
@@ -1,7 +1,9 @@
from __future__ import annotations
import sys
import ast
import inspect
from skills_learning import store as skill_store_module
from skills_learning.models import (
LOCAL_SYNTHESIS_SOURCE,
FlowStep,
@@ -11,6 +13,25 @@ from skills_learning.models import (
from skills_learning.store import SkillStore
def _imported_module_names(module) -> set[str]:
"""Statically collect every module name imported by ``module``'s source.
Covers both ``import x.y`` and ``from x.y import z`` forms so the check
actually fails if a catalog-subscription import is ever added, instead
of only checking whether some module happened to already be imported by
something else in the running interpreter.
"""
source = inspect.getsource(module)
tree = ast.parse(source)
names: set[str] = set()
for node in ast.walk(tree):
if isinstance(node, ast.Import):
names.update(alias.name for alias in node.names)
elif isinstance(node, ast.ImportFrom) and node.module:
names.add(node.module)
return names
def _skill(
*,
name: str = "search web",
@@ -35,7 +56,19 @@ def test_skill_store_enforces_local_synthesis_source_without_catalog_write_path(
stored = store.create_version(_skill())
assert stored.source == LOCAL_SYNTHESIS_SOURCE
assert "skills.catalog" not in sys.modules
imported_modules = _imported_module_names(skill_store_module)
catalog_coupled = {
name
for name in imported_modules
if "catalog" in name.lower() or "sync_client" in name.lower()
}
assert catalog_coupled == set(), (
"skills_learning/store.py must not import from the "
"skill-catalog-subscription store/sync-client (D6: locally-authored "
"skills live in their own store, composed only in prose); found: "
f"{catalog_coupled}"
)
def test_skill_store_keeps_version_chain_and_returns_latest_by_name() -> None: