diff --git a/tests/test_spine.py b/tests/test_spine.py index c3b4dc9d..ae4e8b10 100644 --- a/tests/test_spine.py +++ b/tests/test_spine.py @@ -926,14 +926,30 @@ class TestClosureNoCompetingAuthority: """dependency_tracker's graph describes ~/.config/opencode/scripts (the harness), NOT Rune modules. The name collisions with src/rune are coincidental. This test pins the separation so the harness map - can never silently become a second Rune structural authority.""" + can never silently become a second Rune structural authority. + + CI-safe: asserts the CLASSIFICATION (resolution root is the harness + dir; the map is not derived from Spine), never harness filesystem + state — the CI runner has no ~/.config/opencode.""" + import inspect from rune import dependency_tracker as dt assert "opencode" in dt.SCRIPTS_DIR, "tracker scope moved — re-classify" - # every graph basename resolves (or would resolve) in the harness dir - for fname in list(dt.DEPENDENCY_GRAPH)[:5]: - assert not (spine.SRC_DIR / fname).is_file() or \ - (Path(dt.SCRIPTS_DIR) / fname).is_file(), \ - f"{fname}: ambiguous scope — graph entry exists in src/rune " \ - "but not harness; re-classify against Spine" + # resolution root is the harness dir, by construction: propagate() + # checks os.path.exists against SCRIPTS_DIR, not src/rune + prop_src = inspect.getsource(dt.propagate) + assert "SCRIPTS_DIR" in prop_src and "os.path.join(SCRIPTS_DIR" in prop_src + # the map is a locally-owned table, NOT derived from the Spine store + # (check IMPORTS, not prose — the module docstring legitimately + # mentions Spine when documenting its scope classification) + import ast as _ast + tree = _ast.parse(inspect.getsource(dt)) + imported = set() + for node in _ast.walk(tree): + if isinstance(node, _ast.Import): + imported.update(a.name for a in node.names) + elif isinstance(node, _ast.ImportFrom) and node.module: + imported.add(node.module) + assert not any("spine" in m for m in imported), \ + f"tracker now imports Spine — re-classify as a consumer: {imported}" # and Spine remains the authority for Rune structure - assert spine.ROOT.name == "issue161-harness" or (spine.SRC_DIR / "spine.py").is_file() + assert (spine.SRC_DIR / "spine.py").is_file()