from __future__ import annotations import ast import inspect from skills_learning import store as skill_store_module from skills_learning.models import ( LOCAL_SYNTHESIS_SOURCE, FlowStep, FlowTemplateSkill, SkillMetadata, ) 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", steps: list[FlowStep] | None = None, parameters: dict[str, dict[str, object]] | None = None, ) -> FlowTemplateSkill: return FlowTemplateSkill( metadata=SkillMetadata( name=name, description=f"Learned flow for {name}", source="external-source", originating_goal=name, ), steps=steps or [FlowStep("input_text", {"text": "coffee"})], parameters=parameters or {}, ) def test_skill_store_enforces_local_synthesis_source_without_catalog_write_path() -> None: store = SkillStore() stored = store.create_version(_skill()) assert stored.source == LOCAL_SYNTHESIS_SOURCE 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: store = SkillStore() first = store.create_version(_skill()) second = store.create_version( _skill(steps=[FlowStep("input_text", {"text": "tea"})]), parent=first, ) assert first.version == 1 assert second.version == 2 assert second.parent_version_id == first.id assert store.get_latest_by_name("search web") == second assert store.get_by_id(first.id) == first assert [skill.version for skill in store.list_versions("search web")] == [1, 2]