Migrate 22 malformed contract-discovery IDs to construction-surface, retain evidence/edges, keep candidates DECLARED, unify cold/warm language projection, and refresh the pinned aiohttp lock. Red regression tests precede the fix; local full suite 2301 passed, spine canaries 19/19 and 21/21.
981 lines
53 KiB
Python
981 lines
53 KiB
Python
"""test_spine.py — Spine V1 contract tests.
|
|
|
|
Deterministic core, open-world extensibility, evidence-gated maturity,
|
|
snapshot/delta, drift, and the synthetic future-capability proof.
|
|
Machine-independent: deployment observations use fixture stores.
|
|
"""
|
|
import json
|
|
import sys
|
|
import tempfile
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
ROOT = Path(__file__).resolve().parent.parent
|
|
sys.path.insert(0, str(ROOT / "src"))
|
|
|
|
from rune import spine
|
|
|
|
|
|
def test_contract_discovery_preserves_unqualified_surface_without_inventing_module(monkeypatch):
|
|
monkeypatch.setattr(spine, "_contract_discoveries", lambda: [
|
|
("example", [("src/rune/audit_ledger.py", "construction-surface", "candidate"),
|
|
("rust-crate:cognition_core", "rust-crate", "observed")])])
|
|
doc = spine.new_doc()
|
|
spine._apply_contract_discoveries(doc)
|
|
found = {e["id"]: e for e in doc["entities"]}
|
|
assert found["construction-surface:src/rune/audit_ledger.py"]["kind"] == "construction-surface"
|
|
assert found["construction-surface:src/rune/audit_ledger.py"]["maturity"] == "DECLARED"
|
|
assert found["rust-crate:cognition_core"]["maturity"] == "PRESENT"
|
|
|
|
|
|
def test_legacy_unqualified_discoveries_are_normalized_without_losing_evidence():
|
|
doc = spine.new_doc()
|
|
spine.add_entity(doc, id="src/rune/audit_ledger.py", kind="module",
|
|
maturity="PRESENT", evidence_refs=["contract-discovery:example"])
|
|
spine.add_relationship(doc, type="depends_on", from_id="src/rune/audit_ledger.py",
|
|
to_id="module:other", evidence_refs=["test"])
|
|
spine._normalize_discovery_ids(doc)
|
|
assert doc["entities"][0]["id"] == "construction-surface:src/rune/audit_ledger.py"
|
|
assert doc["entities"][0]["maturity"] == "DECLARED"
|
|
assert doc["entities"][0]["evidence_refs"] == ["contract-discovery:example"]
|
|
assert doc["relationships"][0]["from"] == "construction-surface:src/rune/audit_ledger.py"
|
|
|
|
|
|
# ── helpers ───────────────────────────────────────────────────────────────
|
|
|
|
@pytest.fixture
|
|
def doc():
|
|
return spine.new_doc()
|
|
|
|
|
|
@pytest.fixture
|
|
def seeded(doc):
|
|
"""Minimal structural world: two subsystems, two modules."""
|
|
spine.add_entity(doc, id="subsystem:alpha", kind="subsystem",
|
|
identities={"description": "Alpha"}, maturity="DECLARED")
|
|
spine.add_entity(doc, id="subsystem:beta", kind="subsystem",
|
|
identities={"description": "Beta"}, maturity="DECLARED")
|
|
spine.add_entity(doc, id="module:m_a", kind="module", maturity="DECLARED",
|
|
identities={"source": "src/rune/m_a.py"})
|
|
spine.add_entity(doc, id="module:m_b", kind="module", maturity="DECLARED",
|
|
identities={"source": "src/rune/m_b.py"})
|
|
spine.add_relationship(doc, type="belongs_to", from_id="module:m_a",
|
|
to_id="subsystem:alpha", evidence_refs=["test"])
|
|
spine.add_relationship(doc, type="belongs_to", from_id="module:m_b",
|
|
to_id="subsystem:beta", evidence_refs=["test"])
|
|
return doc
|
|
|
|
|
|
# ── deterministic entity projection / lookups ─────────────────────────────
|
|
|
|
class TestLookups:
|
|
def test_entity_projection_roundtrip(self, seeded, tmp_path):
|
|
p = tmp_path / "s.json"
|
|
spine.save(seeded, p)
|
|
loaded = spine.load(p)
|
|
assert loaded == spine.load(p) # byte-stable across loads
|
|
assert spine.entity(loaded, "module:m_a")["kind"] == "module"
|
|
|
|
def test_ownership_lookup(self, seeded):
|
|
subs = [r["to"] for r in spine.relationships(
|
|
seeded, from_id="module:m_a", type="belongs_to")]
|
|
assert subs == ["subsystem:alpha"]
|
|
|
|
def test_dependency_lookup(self, seeded):
|
|
spine.add_relationship(seeded, type="depends_on", from_id="module:m_a",
|
|
to_id="module:m_b", evidence_refs=["ast"])
|
|
deps = [r["to"] for r in spine.relationships(
|
|
seeded, from_id="module:m_a", type="depends_on")]
|
|
assert deps == ["module:m_b"]
|
|
|
|
def test_consumer_lookup(self, seeded):
|
|
# "consumed_by" edges run provider → consumer: m_a is consumed by m_b
|
|
spine.add_relationship(seeded, type="consumed_by", from_id="module:m_a",
|
|
to_id="module:m_b", evidence_refs=["ast"])
|
|
consumers = [r["to"] for r in spine.relationships(
|
|
seeded, from_id="module:m_a", type="consumed_by")]
|
|
assert consumers == ["module:m_b"]
|
|
|
|
def test_missing_entity_is_not_found(self, seeded):
|
|
assert spine.entity(seeded, "module:ghost") is None
|
|
assert spine.relationships(seeded, from_id="module:ghost") == []
|
|
|
|
|
|
# ── open-world extensibility (the Prime Directive) ────────────────────────
|
|
|
|
class TestOpenWorld:
|
|
def test_unknown_kind_relationship_and_metadata_survive_roundtrip(self, seeded, tmp_path):
|
|
"""THE extensibility test: a synthetic future capability Spine core
|
|
was never written to understand enters as DATA — no core change."""
|
|
spine.add_entity(
|
|
seeded, id="capability:quantum_resonator_cache", kind="quantum_resonator_cache",
|
|
tags=["synthetic-future"], maturity="DECLARED",
|
|
identities={"resonance": "7.83Hz", "home_node": "orb-9"},
|
|
evidence_refs=["synthetic:proof"],
|
|
ext={"entanglement_pair": "module:m_a", "wibble_level": 11,
|
|
"future_field_undefined_in_v1": {"nested": True}})
|
|
spine.add_relationship(
|
|
seeded, type="harmonizes_with", from_id="capability:quantum_resonator_cache",
|
|
to_id="module:m_a", evidence_refs=["synthetic:resonance-proof"],
|
|
ext={"coupling": "0.99", "unknown_later_field": [1, 2, 3]})
|
|
|
|
p = tmp_path / "roundtrip.json"
|
|
spine.save(seeded, p)
|
|
loaded = spine.load(p)
|
|
|
|
e = spine.entity(loaded, "capability:quantum_resonator_cache")
|
|
assert e["kind"] == "quantum_resonator_cache" # unknown kind preserved
|
|
assert e["ext"]["wibble_level"] == 11 # unknown metadata preserved
|
|
assert e["ext"]["future_field_undefined_in_v1"]["nested"] is True
|
|
assert e["identities"]["resonance"] == "7.83Hz"
|
|
rels = spine.relationships(loaded, type="harmonizes_with")
|
|
assert len(rels) == 1 # unknown relationship type preserved
|
|
assert rels[0]["ext"]["coupling"] == "0.99"
|
|
assert rels[0]["to"] == "module:m_a" # linked to a real entity
|
|
|
|
# delta sees it
|
|
base = spine.new_doc()
|
|
d = spine.diff(base, loaded)
|
|
assert "capability:quantum_resonator_cache" in d["entities_added"]
|
|
assert "harmonizes_with|capability:quantum_resonator_cache|module:m_a" \
|
|
in d["relationships_added"]
|
|
|
|
# Scaffolding-style consumption: generic queries work on unknown kinds
|
|
assert spine.relationships(loaded, from_id="capability:quantum_resonator_cache")
|
|
|
|
def test_no_closed_enum_in_core(self):
|
|
"""Core must not hardcode today's ontology as a whitelist."""
|
|
source = (ROOT / "src" / "rune" / "spine.py").read_text()
|
|
# the maturity ladder is a scale, not an entity-kind ontology
|
|
assert "VALID_KINDS" not in source and "VALID_RELATIONSHIP_TYPES" not in source
|
|
|
|
|
|
# ── evidence / maturity semantics ─────────────────────────────────────────
|
|
|
|
class TestMaturity:
|
|
def test_no_evidence_no_promotion(self, seeded):
|
|
with pytest.raises(ValueError, match="requires evidence"):
|
|
spine.promote(seeded, "module:m_a", "PRESENT", "")
|
|
|
|
def test_no_evidence_means_no_live_proven(self, seeded):
|
|
e = spine.entity(seeded, "module:m_a")
|
|
assert e["maturity"] == "DECLARED" # nothing observed: stays DECLARED
|
|
|
|
def test_ladder_is_monotonic_with_evidence(self, seeded):
|
|
for state, ev in [("PRESENT", "src"), ("WIRED", "imports"), ("EXERCISED", "tests")]:
|
|
spine.promote(seeded, "module:m_a", state, ev)
|
|
assert spine.entity(seeded, "module:m_a")["maturity"] == "EXERCISED"
|
|
|
|
def test_demotion_rejected_and_recorded(self, seeded):
|
|
spine.promote(seeded, "module:m_a", "WIRED", "imports-x")
|
|
e = spine.promote(seeded, "module:m_a", "DECLARED", "downgrade-attempt")
|
|
assert e["maturity"] == "WIRED" # not silently demoted
|
|
assert any(o["kind"] == "maturity_demotion_rejected"
|
|
for o in seeded["observations"])
|
|
|
|
def test_unknown_maturity_state_rejected(self, seeded):
|
|
with pytest.raises(ValueError, match="unknown maturity"):
|
|
spine.promote(seeded, "module:m_a", "TRANSCENDENT", "vibes")
|
|
|
|
|
|
# ── deployed identity observation ─────────────────────────────────────────
|
|
|
|
class TestDeployedObservation:
|
|
def test_observes_fixture_identity(self, doc, tmp_path, monkeypatch):
|
|
dep = tmp_path / "deploy"
|
|
(dep / "src" / "rune").mkdir(parents=True)
|
|
(dep / "src" / "rune" / "_build_identity.json").write_text(json.dumps(
|
|
{"commit": "a" * 40, "version": "9.9.9", "deployed_at": "T"}))
|
|
monkeypatch.setattr(spine, "DEPLOYED_LINK", dep)
|
|
obs = spine._deployed_observation()
|
|
assert obs["deployed_sha"] == "a" * 12 and obs["deployed_version"] == "9.9.9"
|
|
|
|
def test_absent_deployment_is_none_not_invented(self, monkeypatch, tmp_path):
|
|
monkeypatch.setattr(spine, "DEPLOYED_LINK", tmp_path / "nonexistent")
|
|
assert spine._deployed_observation() is None
|
|
|
|
def test_deployed_identity_drift_detected(self, seeded, tmp_path, monkeypatch):
|
|
dep = tmp_path / "deploy"
|
|
(dep / "src" / "rune").mkdir(parents=True)
|
|
(dep / "src" / "rune" / "_build_identity.json").write_text(json.dumps(
|
|
{"commit": "b" * 40, "version": "0.0.1-test-mismatch", "deployed_at": "T"}))
|
|
monkeypatch.setattr(spine, "DEPLOYED_LINK", dep)
|
|
monkeypatch.setattr(spine, "ROOT", tmp_path) # no pyproject → no source version
|
|
# no version drift observable without source; identity observation works
|
|
obs = spine._deployed_observation()
|
|
assert obs["deployed_version"] == "0.0.1-test-mismatch"
|
|
|
|
|
|
# ── drift ─────────────────────────────────────────────────────────────────
|
|
|
|
class TestDrift:
|
|
def test_registered_but_absent(self, seeded, monkeypatch):
|
|
monkeypatch.setattr(spine, "_canary_registry", lambda: ["m_ghost"])
|
|
monkeypatch.setattr(spine, "SRC_DIR", tmp_src := ROOT / "src" / "rune") # real dir
|
|
d = spine.drift(seeded)
|
|
assert any(x["class"] == "registered_but_absent" and x["entity"] == "module:m_ghost"
|
|
for x in d)
|
|
|
|
def test_relationship_disappeared(self, seeded, monkeypatch):
|
|
spine.add_relationship(seeded, type="consumed_by", from_id="module:m_a",
|
|
to_id="module:m_b", evidence_refs=["stale"])
|
|
monkeypatch.setattr(spine, "_import_graph", lambda: {"m_b": set()})
|
|
monkeypatch.setattr(spine, "_canary_registry", lambda: [])
|
|
monkeypatch.setattr(spine, "SRC_DIR", ROOT / "src" / "rune")
|
|
d = spine.drift(seeded)
|
|
assert any(x["class"] == "relationship_disappeared" for x in d)
|
|
|
|
def test_stale_evidence_does_not_prove_live(self, seeded):
|
|
"""A stale evidence ref must never read as LIVE_PROVEN."""
|
|
e = spine.entity(seeded, "module:m_a")
|
|
e["evidence_refs"].append("deployed-tree:deadbeef-old-observation")
|
|
assert e["maturity"] == "DECLARED" # evidence ref alone promotes nothing
|
|
# LIVE_PROVEN only via explicit evidence-gated promotion
|
|
spine.promote(seeded, "module:m_a", "PRESENT", "fresh-source-check")
|
|
assert spine.entity(seeded, "module:m_a")["maturity"] == "PRESENT"
|
|
|
|
def test_declared_but_unwired_capability(self, seeded):
|
|
"""Registered with no wiring evidence → must not read WIRED."""
|
|
spine.add_entity(seeded, id="capability:orphan_feature", kind="capability",
|
|
maturity="DECLARED", evidence_refs=["registry:orphan"])
|
|
assert spine.entity(seeded, "capability:orphan_feature")["maturity"] == "DECLARED"
|
|
|
|
|
|
# ── snapshots / deltas ────────────────────────────────────────────────────
|
|
|
|
class TestSnapshots:
|
|
def test_snapshot_deterministic(self, seeded, tmp_path, monkeypatch):
|
|
monkeypatch.setattr(spine, "SNAPSHOTS", tmp_path / "snaps")
|
|
monkeypatch.setattr(spine, "_deployed_observation", lambda: None)
|
|
seeded["generated_at"] = "FIXED"
|
|
p1 = spine.snapshot(seeded, "t")
|
|
p2 = spine.snapshot(seeded, "t")
|
|
assert p1.read_bytes() == p2.read_bytes() # byte-identical
|
|
|
|
def test_diff_categories(self, seeded):
|
|
old = json.loads(json.dumps(seeded))
|
|
spine.add_entity(seeded, id="module:m_new", kind="module", maturity="DECLARED")
|
|
spine.promote(seeded, "module:m_a", "WIRED", "new-import-evidence")
|
|
spine.add_relationship(seeded, type="depends_on", from_id="module:m_a",
|
|
to_id="module:m_b", evidence_refs=["ast"])
|
|
d = spine.diff(old, seeded)
|
|
assert d["entities_added"] == ["module:m_new"]
|
|
assert d["relationships_added"] == ["depends_on|module:m_a|module:m_b"]
|
|
assert {"id": "module:m_a", "from": "DECLARED", "to": "WIRED"} \
|
|
in d["maturity_changed"]
|
|
assert d["entities_removed"] == []
|
|
|
|
def test_diff_is_deterministic(self, seeded):
|
|
old = json.loads(json.dumps(seeded))
|
|
d1 = spine.diff(old, seeded)
|
|
d2 = spine.diff(old, seeded)
|
|
assert d1 == d2
|
|
|
|
|
|
# ── schema versioning ─────────────────────────────────────────────────────
|
|
|
|
class TestSchemaVersion:
|
|
def test_save_stamps_version(self, seeded, tmp_path):
|
|
p = spine.save(seeded, tmp_path / "s.json")
|
|
assert json.loads(p.read_text())["schema_version"] == spine.SCHEMA_VERSION
|
|
|
|
def test_future_version_refused_for_rewrite(self, seeded, tmp_path):
|
|
seeded["schema_version"] = 99
|
|
with pytest.raises(ValueError, match="schema_version"):
|
|
spine.save(seeded, tmp_path / "future.json")
|
|
|
|
def test_unknown_fields_of_document_survive(self, seeded, tmp_path):
|
|
seeded["a_field_spine_v2_will_understand"] = {"x": 1}
|
|
p = spine.save(seeded, tmp_path / "s.json")
|
|
# loader tolerates unknown document fields (no whitelist strip)
|
|
loaded = json.loads(p.read_text())
|
|
assert loaded["a_field_spine_v2_will_understand"] == {"x": 1}
|
|
|
|
|
|
# ── ingest / compatibility (umbrella migration) ───────────────────────────
|
|
|
|
class TestUmbrellaMigration:
|
|
def test_ingest_refuses_generated_view(self, tmp_path):
|
|
generated = {"_generated_by_spine": True, "umbrellas": {}}
|
|
p = tmp_path / "view.json"
|
|
p.write_text(json.dumps(generated))
|
|
with pytest.raises(ValueError, match="refusing to ingest"):
|
|
spine.ingest_umbrellas(spine.new_doc(), p)
|
|
|
|
def test_ingest_preserves_structure(self, tmp_path):
|
|
manifest = {"umbrellas": {
|
|
"alpha": {"description": "A", "modules": ["m_a", "m_b"],
|
|
"depends_on": ["beta"], "mirrors": ["shared/x.py"]},
|
|
"beta": {"description": "B", "modules": [], "depends_on": [], "mirrors": []}}}
|
|
p = tmp_path / "umbrellas.json"
|
|
p.write_text(json.dumps(manifest))
|
|
doc = spine.new_doc()
|
|
n = spine.ingest_umbrellas(doc, p)
|
|
assert n == 2
|
|
# ownership preserved
|
|
assert [r["to"] for r in spine.relationships(
|
|
doc, from_id="module:m_a", type="belongs_to")] == ["subsystem:alpha"]
|
|
# subsystem deps preserved
|
|
assert [r["to"] for r in spine.relationships(
|
|
doc, from_id="subsystem:alpha", type="depends_on")] == ["subsystem:beta"]
|
|
# mirrors preserved
|
|
assert [r["to"] for r in spine.relationships(
|
|
doc, from_id="subsystem:alpha", type="mirrors")] == ["file:shared/x.py"]
|
|
|
|
def test_export_reproduces_equivalent_view(self, tmp_path):
|
|
manifest = {"umbrellas": {
|
|
"alpha": {"description": "A", "modules": ["m_a"],
|
|
"depends_on": ["beta"], "mirrors": ["shared/x.py"]},
|
|
"beta": {"description": "B", "modules": ["m_b"],
|
|
"depends_on": [], "mirrors": []}}}
|
|
p = tmp_path / "umbrellas.json"
|
|
p.write_text(json.dumps(manifest))
|
|
doc = spine.new_doc()
|
|
spine.ingest_umbrellas(doc, p)
|
|
view = spine.export_umbrellas(doc)
|
|
assert view["_generated_by_spine"] is True
|
|
assert view["umbrellas"]["alpha"]["modules"] == ["m_a"]
|
|
assert view["umbrellas"]["alpha"]["depends_on"] == ["beta"]
|
|
assert view["umbrellas"]["alpha"]["mirrors"] == ["shared/x.py"]
|
|
assert view["umbrellas"]["beta"]["modules"] == ["m_b"]
|
|
# round-trip fidelity: the generated view re-ingests to the same graph
|
|
view_path = tmp_path / "view.json"
|
|
view_path.write_text(json.dumps(view))
|
|
doc2 = spine.new_doc()
|
|
# (direct ingest of the view is refused; equivalence is proven by
|
|
# comparing the exported view against the original manual truth)
|
|
assert view["umbrellas"] == manifest["umbrellas"]
|
|
|
|
def test_export_never_overwrites_manual_manifest(self, seeded, monkeypatch, tmp_path):
|
|
monkeypatch.setattr(spine, "UMBRELLA_MANIFEST", tmp_path / "manual.json")
|
|
(tmp_path / "manual.json").write_text(json.dumps({"umbrellas": {}}))
|
|
# main() with no path → refuses (manual manifest protection)
|
|
rc = spine.main(["export-umbrellas"])
|
|
assert rc == 1
|
|
|
|
|
|
# ── Scaffolding consumes Spine ────────────────────────────────────────────
|
|
|
|
class TestScaffoldingConsumesSpine:
|
|
def test_scaffold_projection_reads_spine_store(self, seeded, tmp_path, monkeypatch):
|
|
"""Scaffolding's structural facts must come from the Spine store,
|
|
not from re-deriving authorities independently."""
|
|
p = tmp_path / "current.json"
|
|
spine.save(seeded, p)
|
|
monkeypatch.setattr(spine, "CURRENT", p)
|
|
monkeypatch.setattr(spine, "ROOT", tmp_path)
|
|
monkeypatch.setattr(spine, "SRC_DIR", tmp_path / "src" / "rune")
|
|
(tmp_path / "src" / "rune").mkdir(parents=True, exist_ok=True)
|
|
(tmp_path / "src" / "rune" / "m_a.py").write_text("x = 1\n")
|
|
(tmp_path / "tests").mkdir(exist_ok=True)
|
|
monkeypatch.setattr(spine, "TESTS_DIR", tmp_path / "tests")
|
|
monkeypatch.setattr(spine, "_deployed_observation", lambda: None)
|
|
# fresh store → Scaffolding builds it deterministically
|
|
doc = spine.build()
|
|
spine.save(doc)
|
|
# a component card must be answerable from the store alone
|
|
card_deps = [r["to"] for r in spine.relationships(
|
|
doc, from_id="module:m_a", type="depends_on")]
|
|
subs = [r["to"] for r in spine.relationships(
|
|
doc, from_id="module:m_a", type="belongs_to")]
|
|
assert subs == ["subsystem:alpha"]
|
|
assert isinstance(card_deps, list)
|
|
# maturity visible to Scaffolding consumers — the fixture created
|
|
# src/rune/m_a.py, so the evidence gate correctly promotes PRESENT
|
|
assert spine.entity(doc, "module:m_a")["maturity"] == "PRESENT"
|
|
|
|
def test_scaffold_impact_uses_spine_store_not_umbrellas(self, monkeypatch, tmp_path):
|
|
"""The real integration: scripts/scaffold_projection.py resolves
|
|
ownership from the Spine store. Proven by pointing the store at a
|
|
fixture world and checking the card reflects SPINE data (and that
|
|
a Spine-only entity — absent from any umbrella file — is served)."""
|
|
import importlib
|
|
doc = spine.new_doc()
|
|
spine.add_entity(doc, id="subsystem:spine_test_world", kind="subsystem",
|
|
identities={"description": "fixture"})
|
|
spine.add_entity(doc, id="module:widget_core", kind="module", maturity="EXERCISED",
|
|
identities={"source": "src/rune/widget_core.py"},
|
|
evidence_refs=["fixture"])
|
|
spine.add_relationship(doc, type="belongs_to", from_id="module:widget_core",
|
|
to_id="subsystem:spine_test_world", evidence_refs=["fixture"])
|
|
spine.add_relationship(doc, type="consumed_by", from_id="module:widget_core",
|
|
to_id="module:widget_user", evidence_refs=["fixture"])
|
|
store = tmp_path / "current.json"
|
|
spine.save(doc, store)
|
|
monkeypatch.setattr(spine, "CURRENT", store)
|
|
|
|
import scripts.scaffold_projection as sp # noqa: E402
|
|
importlib.reload(sp)
|
|
monkeypatch.setattr(sp, "ROOT", tmp_path)
|
|
card = sp.component_card("widget_core", spine.load(store))
|
|
assert card["subsystem"] == "spine_test_world" # from Spine, not umbrellas.json
|
|
assert card["consumers"] == ["widget_user"] # from Spine relationships
|
|
assert card["spine_maturity"] == "EXERCISED" # evidence state surfaced
|
|
|
|
|
|
# ── graph separation + cross-linking (addendum 1) ─────────────────────────
|
|
|
|
class TestGraphSeparation:
|
|
def test_spine_queries_need_no_memory_infrastructure(self, seeded):
|
|
"""Structural lookup works on the Spine store alone — no Memory
|
|
fixture, no retrieval, no inference."""
|
|
q = spine.query(seeded, "module:m_a")
|
|
assert q["owner"] == ["subsystem:alpha"]
|
|
assert q["state"]["maturity"] == "DECLARED"
|
|
|
|
def test_stable_ids_survive_full_cycle(self, seeded, tmp_path):
|
|
p = spine.save(seeded, tmp_path / "s.json")
|
|
loaded = spine.load(p)
|
|
snap = tmp_path / "snap.json"
|
|
spine.save(loaded, snap)
|
|
d = spine.diff(spine.load(p), spine.load(snap))
|
|
assert d["entities_removed"] == [] and d["entities_added"] == []
|
|
assert spine.entity(spine.load(snap), "module:m_a") is not None
|
|
|
|
def test_spine_uri_cross_reference_without_authority_duplication(self, seeded):
|
|
"""A future Memory record references a Spine entity by URI — the
|
|
structural edge stays in Spine, the reference carries no authority."""
|
|
uri = spine.spine_uri("module:m_a")
|
|
assert uri == "spine://module/m_a"
|
|
assert spine.resolve_uri(uri) == "module:m_a"
|
|
# round-trip: resolve → the same entity Spine owns
|
|
assert spine.entity(seeded, spine.resolve_uri(uri))["kind"] == "module"
|
|
with pytest.raises(ValueError, match="not a spine URI"):
|
|
spine.resolve_uri("memory://lesson/42")
|
|
|
|
def test_memory_association_does_not_expand_frontier(self, seeded):
|
|
"""A hypothetical Memory-style association edge must NOT expand the
|
|
structural impact frontier (noisy impact = broken resolution)."""
|
|
spine.add_relationship(seeded, type="related_memory", from_id="module:m_a",
|
|
to_id="memory:old-incident-42", evidence_refs=["hypothetical"])
|
|
f = spine.impact_frontier(seeded, ["module:m_a"])
|
|
assert "memory:old-incident-42" not in f["level1_obligations"]
|
|
assert f["traversal_scope"] == "spine-structural-only"
|
|
# while a REAL structural edge does expand
|
|
spine.add_relationship(seeded, type="consumed_by", from_id="module:m_a",
|
|
to_id="module:m_b", evidence_refs=["ast"])
|
|
f2 = spine.impact_frontier(seeded, ["module:m_a"])
|
|
assert "module:m_b" in f2["level1_obligations"]
|
|
|
|
def test_unknown_relationship_is_obligation_bearing_fail_loud(self, seeded):
|
|
"""A previously unseen relationship type defaults to frontier
|
|
visibility — new structural relations deserve inspection until
|
|
evidence reclassifies them (data-driven semantics, open world)."""
|
|
spine.add_relationship(seeded, type="contextualises", from_id="module:m_a",
|
|
to_id="subsystem:beta", evidence_refs=["unseen-type"])
|
|
f = spine.impact_frontier(seeded, ["module:m_a"])
|
|
assert "subsystem:beta" in f["level1_obligations"]
|
|
|
|
def test_structural_facts_survive_hypothetical_memory_archive(self, seeded, tmp_path):
|
|
"""Spine retains current structural truth regardless of any future
|
|
Memory salience/decay/archive state — Spine is not Memory-dependent."""
|
|
spine.promote(seeded, "module:m_a", "WIRED", "import-evidence")
|
|
p = spine.save(seeded, tmp_path / "s.json")
|
|
# (hypothetical: Memory archives the story of how this was learned)
|
|
assert spine.entity(spine.load(p), "module:m_a")["maturity"] == "WIRED"
|
|
|
|
|
|
# ── contracts, task binding, frontier, predicted-vs-observed (T22) ────────
|
|
|
|
class TestContractsAndFrontier:
|
|
def _contract_world(self, doc):
|
|
spine.add_entity(doc, id="contract:chat_transport", kind="contract",
|
|
evidence_refs=["protocol:ChatTransport"], maturity="DECLARED")
|
|
spine.add_entity(doc, id="module:transport_a", kind="module", maturity="DECLARED")
|
|
spine.add_entity(doc, id="module:transport_b", kind="module", maturity="DECLARED")
|
|
spine.add_entity(doc, id="module:operator", kind="module", maturity="DECLARED")
|
|
spine.add_relationship(doc, type="implements", from_id="module:transport_a",
|
|
to_id="contract:chat_transport", evidence_refs=["ast"])
|
|
spine.add_relationship(doc, type="implements", from_id="module:transport_b",
|
|
to_id="contract:chat_transport", evidence_refs=["ast"])
|
|
spine.add_relationship(doc, type="consumes", from_id="module:operator",
|
|
to_id="contract:chat_transport", evidence_refs=["ast"])
|
|
return doc
|
|
|
|
def test_contract_change_expands_to_all_parties(self, seeded):
|
|
"""A CONTRACT change deserves every implementer + consumer's inspection."""
|
|
doc = self._contract_world(seeded)
|
|
f = spine.impact_frontier(doc, ["contract:chat_transport"])
|
|
assert {"module:transport_a", "module:transport_b", "module:operator"} \
|
|
<= set(f["level1_obligations"])
|
|
|
|
def test_implementation_change_is_bounded(self, seeded):
|
|
"""An IMPLEMENTATION change (one transport) touches the contract but
|
|
the frontier stays bounded — it does not pull sibling implementers
|
|
in as direct obligations (they see the contract, not this change)."""
|
|
doc = self._contract_world(seeded)
|
|
f = spine.impact_frontier(doc, ["module:transport_a"])
|
|
# HARDENED SEMANTICS (T1A): implementation change, contract preserved
|
|
# → contract is a direct obligation; siblings are CONTEXTUAL, never
|
|
# direct. (The pre-hardening expectation that siblings land in
|
|
# level1 encoded the wrong semantics — see final report.)
|
|
assert "contract:chat_transport" in f["level1_obligations"]
|
|
assert "module:transport_b" in f["contextual_not_obligations"]
|
|
assert "module:transport_b" not in f["level1_obligations"]
|
|
assert f["change_kind"] == "implementation_change"
|
|
assert f["level0_direct"] == ["module:transport_a"]
|
|
|
|
def test_task_binding_and_frontier(self, seeded):
|
|
doc = self._contract_world(seeded)
|
|
spine.bind_task(doc, "TASK-123", ["module:transport_a"], evidence_ref="issue")
|
|
assert spine.task_entities(doc, "TASK-123") == ["module:transport_a"]
|
|
seeds = spine.task_entities(doc, "TASK-123")
|
|
f = spine.impact_frontier(doc, seeds)
|
|
assert "contract:chat_transport" in f["level1_obligations"]
|
|
|
|
def test_predicted_vs_observed_flags_misses_as_candidates(self, seeded):
|
|
old = json.loads(json.dumps(seeded))
|
|
predicted = spine.impact_frontier(old, ["module:m_a"])
|
|
# the change reveals a consequence the structure didn't predict:
|
|
# m_b (a consumer nobody had recorded) starts consuming m_a
|
|
spine.add_relationship(seeded, type="consumed_by", from_id="module:m_a",
|
|
to_id="module:m_b", evidence_refs=["observed:real-run"])
|
|
r = spine.predicted_vs_observed(predicted, seeded, ["module:m_a"])
|
|
assert "module:m_b" in r["missed"]
|
|
assert r["candidate_corrections"][0]["status"] == "CANDIDATE"
|
|
assert "evidence required" in r["candidate_corrections"][0]["note"]
|
|
|
|
def test_structural_event_emission_is_generic(self, seeded):
|
|
spine.add_relationship(seeded, type="consumed_by", from_id="module:m_a",
|
|
to_id="module:m_b", evidence_refs=["ast"])
|
|
events = spine.structural_events(seeded)
|
|
kinds = {e["type"] for e in events}
|
|
assert "entity_observed" in kinds and "relationship_observed" in kinds
|
|
# dumb typed facts — no salience, no policy fields
|
|
rel_event = next(e for e in events if e["type"] == "relationship_observed"
|
|
and e["relationship"] == "consumed_by")
|
|
assert rel_event["from"] == "module:m_a" and rel_event["to"] == "module:m_b"
|
|
|
|
def test_evidence_conflict_drift(self, seeded):
|
|
e = spine.entity(seeded, "module:m_a")
|
|
e["maturity"] = "LIVE_PROVEN"
|
|
e["evidence_refs"] = [] # claim without proof
|
|
d = spine.drift(seeded)
|
|
assert any(x["class"] == "evidence_conflict" and x["entity"] == "module:m_a"
|
|
for x in d)
|
|
|
|
def test_intrinsic_query_answers_question_set_deterministically(self, seeded):
|
|
"""THE acceptance property: the representative question set answered
|
|
from the store alone — no LLM, no Memory, no rediscovery."""
|
|
doc = self._contract_world(seeded)
|
|
q = spine.query(doc, "module:transport_a")
|
|
assert q["kind"] == "module"
|
|
assert q["contracts_implements"] == ["contract:chat_transport"]
|
|
assert q["state"]["maturity"] == "DECLARED"
|
|
assert q["evidence"] == []
|
|
assert q["impact_frontier"]["level0_direct"] == ["module:transport_a"]
|
|
assert q["uri"] == "spine://module/transport_a"
|
|
|
|
def test_synthetic_future_capability_flows_through_frontier(self, seeded):
|
|
"""Extensibility end-to-end: the unknown capability registers,
|
|
round-trips, binds to a task, expands a frontier, and survives
|
|
delta — with zero Spine core changes (kind/relationship unseen)."""
|
|
spine.add_entity(seeded, id="capability:quantum_resonator_cache",
|
|
kind="quantum_resonator_cache", maturity="DECLARED",
|
|
identities={"resonance": "7.83Hz"},
|
|
evidence_refs=["synthetic"], ext={"wibble": 11})
|
|
spine.add_relationship(seeded, type="harmonizes_with",
|
|
from_id="capability:quantum_resonator_cache",
|
|
to_id="module:m_a", evidence_refs=["synthetic"])
|
|
spine.bind_task(seeded, "TASK-FUTURE", ["capability:quantum_resonator_cache"])
|
|
q = spine.query(seeded, "capability:quantum_resonator_cache")
|
|
assert q["kind"] == "quantum_resonator_cache" # unknown kind queried generically
|
|
assert q["state"]["maturity"] == "DECLARED"
|
|
f = spine.impact_frontier(seeded, spine.task_entities(seeded, "TASK-FUTURE"))
|
|
assert "module:m_a" in f["level1_obligations"] # unseen rel = obligation-bearing
|
|
|
|
|
|
# ── HARDENING T1: contract-change vs implementation-change semantics ──────
|
|
|
|
class TestHardeningContractSemantics:
|
|
def _world(self, seeded):
|
|
spine.add_entity(seeded, id="contract:chat_transport", kind="contract",
|
|
evidence_refs=["protocol"], maturity="DECLARED")
|
|
for m in ("transport_a", "transport_b"):
|
|
spine.add_entity(seeded, id=f"module:{m}", kind="module", maturity="DECLARED")
|
|
spine.add_relationship(seeded, type="implements", from_id=f"module:{m}",
|
|
to_id="contract:chat_transport", evidence_refs=["ast"])
|
|
spine.add_entity(seeded, id="module:operator", kind="module", maturity="DECLARED")
|
|
spine.add_relationship(seeded, type="consumes", from_id="module:operator",
|
|
to_id="contract:chat_transport", evidence_refs=["ast"])
|
|
return seeded
|
|
|
|
def test_A_implementation_change_siblings_not_direct(self, seeded):
|
|
doc = self._world(seeded)
|
|
f = spine.impact_frontier(doc, ["module:transport_a"])
|
|
assert f["change_kind"] == "implementation_change"
|
|
assert f["level0_direct"] == ["module:transport_a"]
|
|
assert "contract:chat_transport" in f["level1_obligations"]
|
|
assert "module:transport_b" in f["contextual_not_obligations"]
|
|
assert "module:transport_b" not in f["level1_obligations"]
|
|
# X (consumer) is reached through the contract's contextual parties
|
|
# or obligations per evidence — must NOT be silently direct here
|
|
assert "module:operator" in f["contextual_not_obligations"] or \
|
|
"module:operator" in f["level1_obligations"]
|
|
|
|
def test_B_contract_change_all_parties_direct(self, seeded):
|
|
doc = self._world(seeded)
|
|
f = spine.impact_frontier(doc, ["contract:chat_transport"])
|
|
assert f["change_kind"] == "contract_change"
|
|
for party in ("module:transport_a", "module:transport_b", "module:operator"):
|
|
assert party in f["level1_obligations"], party
|
|
|
|
def test_C_contract_violation_escalates(self, seeded):
|
|
doc = self._world(seeded)
|
|
f = spine.impact_frontier(doc, ["module:transport_a"],
|
|
change_kind="contract_violation")
|
|
# verification proved the implementation breaks the contract:
|
|
# every party becomes a direct obligation
|
|
assert f["change_kind"] == "contract_violation"
|
|
for party in ("module:transport_b", "module:operator"):
|
|
assert party in f["level1_obligations"], party
|
|
|
|
def test_kind_inference_is_deterministic(self, seeded):
|
|
doc = self._world(seeded)
|
|
assert spine.impact_frontier(doc, ["contract:chat_transport"])["change_kind"] == "contract_change"
|
|
assert spine.impact_frontier(doc, ["module:transport_a"])["change_kind"] == "implementation_change"
|
|
|
|
|
|
# ── HARDENING T3/T4: projector ownership, reconciliation, state semantics ─
|
|
|
|
class TestHardeningReconciliation:
|
|
def _fact(self, doc, type="depends_on", frm="module:x", to="module:y", auth="proj-x"):
|
|
spine.add_entity(doc, id=frm, kind="module", maturity="DECLARED")
|
|
spine.add_entity(doc, id=to, kind="module", maturity="DECLARED")
|
|
r = spine.add_relationship(doc, type=type, from_id=frm, to_id=to,
|
|
evidence_refs=["fixture"])
|
|
r["authority"] = auth
|
|
r["status"] = "CURRENT"
|
|
return r
|
|
|
|
def test_foreign_projector_refresh_never_deletes_other_authority_facts(self, seeded):
|
|
"""Projector Y's refresh must not touch projector X's facts —
|
|
the reconciliation boundary is AUTHORITY OWNERSHIP."""
|
|
doc = seeded
|
|
self._fact(doc, auth="proj-x")
|
|
foreign = spine.add_relationship(doc, type="related_thing", from_id="module:x",
|
|
to_id="module:y", evidence_refs=["fixture"])
|
|
spine._authority_observed(doc, foreign, "proj-y") # proj-y ran and owns it
|
|
# proj-x refresh observes its own fact; reconcile must leave proj-y's
|
|
# fact alone — only proj-y's own pass can stale it
|
|
spine._doc_authorities(doc).setdefault("proj-x", {"generation": 1, "observed": ["depends_on|module:x|module:y"]})
|
|
staled = spine.reconcile_authorities(doc)
|
|
assert foreign["status"] == "CURRENT" # foreign fact survives
|
|
assert all(s["authority"] != "proj-y" for s in staled)
|
|
|
|
def test_owner_stopping_emission_stales_not_deletes(self, seeded):
|
|
"""Projector X later stops observing its fact → STALE (marked,
|
|
reported in drift, excluded from frontier) — not silently deleted,
|
|
not silently current."""
|
|
doc = seeded
|
|
self._fact(doc, auth="proj-x")
|
|
spine._doc_authorities(doc)["proj-x"] = {"generation": 2, "observed": []}
|
|
staled = spine.reconcile_authorities(doc)
|
|
assert len(staled) == 1
|
|
f = spine.impact_frontier(doc, ["module:x"])
|
|
assert "module:y" not in f["level1_obligations"] # stale ≠ current truth
|
|
d = spine.drift(doc)
|
|
assert any(x["class"] == "stale_fact" for x in d)
|
|
|
|
def test_projector_failure_does_not_masquerade_as_fresh(self, seeded):
|
|
doc = seeded
|
|
self._fact(doc, auth="proj-x")
|
|
spine._doc_authorities(doc)["proj-x"] = {"generation": 1, "observed": ["depends_on|module:x|module:y"]}
|
|
spine.authority_failed(doc, "proj-x", "source tree unreachable")
|
|
h = spine.authority_health(doc)
|
|
assert h["proj-x"]["status"] == "DEGRADED"
|
|
r = [r for r in doc["relationships"] if r.get("authority") == "proj-x"][0]
|
|
assert r["status"] == "STALE"
|
|
f = spine.impact_frontier(doc, ["module:x"])
|
|
assert "module:y" not in f["level1_obligations"]
|
|
assert any(o["kind"] == "authority_degraded" for o in doc["observations"])
|
|
|
|
def test_manual_fact_without_authority_survives(self, seeded):
|
|
doc = seeded
|
|
r = self._fact(doc, auth=None)
|
|
r.pop("authority", None); r.pop("status", None)
|
|
spine.reconcile_authorities(doc)
|
|
assert r["status"] != "STALE" if "status" in r else True
|
|
assert r in doc["relationships"]
|
|
|
|
def test_two_authorities_conflicting_preserve_both(self, seeded):
|
|
spine.mark_conflict(seeded, "module:m_a", "registry", "exists",
|
|
"runtime", "unavailable", "evidence:live-check")
|
|
e = spine.entity(seeded, "module:m_a")
|
|
assert e["status"] == "CONFLICTING"
|
|
assert len(e["ext"]["conflicts"]) == 1 # BOTH preserved, not last-wins
|
|
assert spine.resolve_status(e) == "CONFLICTING"
|
|
q = spine.query(seeded, "module:m_a")
|
|
assert q["state"]["resolved_status"] == "CONFLICTING"
|
|
|
|
def test_conflict_requires_evidence(self, seeded):
|
|
with pytest.raises(ValueError, match="requires evidence"):
|
|
spine.mark_conflict(seeded, "module:m_a", "a", "x", "b", "y", "")
|
|
|
|
def test_unknown_is_not_healthy(self, seeded):
|
|
"""A never-observed entity is UNKNOWN — not current, not false."""
|
|
spine.add_entity(seeded, id="module:never_checked", kind="module",
|
|
maturity="DECLARED")
|
|
assert spine.resolve_status(spine.entity(seeded, "module:never_checked")) == "UNKNOWN"
|
|
|
|
def test_live_proven_without_evidence_is_conflict_not_stale(self, seeded):
|
|
"""Case A vs B distinction: no evidence at all = CONFLICTING claim;
|
|
a projector that stopped refreshing = STALE. Different conditions."""
|
|
spine.add_entity(seeded, id="module:no_proof", kind="module",
|
|
maturity="LIVE_PROVEN") # zero evidence refs
|
|
assert spine.resolve_status(spine.entity(seeded, "module:no_proof")) == "CONFLICTING"
|
|
|
|
|
|
# ── HARDENING T5: durable identity — rename/alias/supersession ────────────
|
|
|
|
class TestHardeningDurableIdentity:
|
|
def test_rename_old_spine_reference_stays_meaningful(self, seeded, tmp_path):
|
|
"""Memory stores spine://module/memory_selector; the module is later
|
|
renamed memory_orchestrator. The old reference must still resolve."""
|
|
spine.alias(seeded, "module:m_a", "module:m_a_old_name")
|
|
p = spine.save(seeded, tmp_path / "s.json")
|
|
reloaded = spine.load(p)
|
|
# old URI resolves to the same conceptual entity
|
|
old_ref = spine.resolve_uri("spine://module/m_a_old_name")
|
|
e = spine.entity(reloaded, old_ref)
|
|
assert e is not None and e["id"] == "module:m_a"
|
|
# survives another build cycle (aliases are data, carried forward)
|
|
doc2 = spine.build.__wrapped__(reloaded) if hasattr(spine.build, "__wrapped__") else reloaded
|
|
assert spine.entity(doc2, "module:m_a_old_name") is not None
|
|
|
|
def test_supersession_requires_evidence_and_preserves_history(self, seeded):
|
|
spine.add_entity(seeded, id="module:new_thing", kind="module", maturity="DECLARED")
|
|
with pytest.raises(ValueError, match="requires evidence"):
|
|
spine.supersede(seeded, "module:new_thing", "module:m_a", "")
|
|
spine.supersede(seeded, "module:new_thing", "module:m_a", "evidence:decision-record")
|
|
old = spine.entity(seeded, "module:m_a")
|
|
assert old is not None # never deleted
|
|
assert old["superseded_by"] == "module:new_thing"
|
|
assert old["status"] == "SUPERSEDED"
|
|
# the supersedes relationship is data, round-trips, and is
|
|
# non-obligation for frontier purposes (context, not live wiring)
|
|
p = spine.save(seeded, Path(tempfile.mkdtemp()) / "s.json")
|
|
assert spine.entity(spine.load(p), "module:m_a")["superseded_by"] == "module:new_thing"
|
|
|
|
def test_superseded_entity_not_treated_as_current(self, seeded):
|
|
spine.add_entity(seeded, id="module:new_thing", kind="module", maturity="DECLARED")
|
|
spine.supersede(seeded, "module:new_thing", "module:m_a", "evidence:decision")
|
|
f = spine.impact_frontier(seeded, ["module:m_a"])
|
|
# a superseded seed is still answerable, but its status is explicit
|
|
assert spine.resolve_status(spine.entity(seeded, "module:m_a")) == "SUPERSEDED"
|
|
|
|
|
|
# ── HARDENING T2: independent observed impact ─────────────────────────────
|
|
|
|
class TestHardeningObservedImpact:
|
|
def test_observed_impact_comes_from_evidence_not_frontier_rerun(self, seeded):
|
|
"""The observed side must be built from EXTERNAL evidence items —
|
|
test failures, canary results, runtime errors — each naming its
|
|
authority. The frontier function is not invoked for the observed set."""
|
|
spine.add_entity(seeded, id="module:m_c", kind="module", maturity="DECLARED")
|
|
ev = [{"entity": "module:m_b", "source": "pytest-failure",
|
|
"authority": "test-suite", "detail": "test_consumer_lookup failed"},
|
|
{"entity": "module:m_c", "source": "canary", "authority": "canary-runner",
|
|
"detail": "canary_native red"}]
|
|
r = spine.record_observed_impact(seeded, "TASK-X", ev)
|
|
assert r["observed_entities"] == ["module:m_b", "module:m_c"]
|
|
kinds = [o["kind"] for o in seeded["observations"]]
|
|
assert kinds.count("observed_consequence") == 2
|
|
# nothing was auto-promoted into structural relationships
|
|
assert not spine.relationships(seeded, type="consumed_by",
|
|
from_id="module:m_a", to_id="module:m_c")
|
|
|
|
def test_predicted_vs_observed_with_independent_evidence(self, seeded):
|
|
old = json.loads(json.dumps(seeded))
|
|
predicted = spine.impact_frontier(old, ["module:m_a"])
|
|
# the change ran; independent evidence implicates an entity the
|
|
# structure never predicted
|
|
spine.record_observed_impact(seeded, "TASK-X", [
|
|
{"entity": "module:m_b", "source": "runtime-error",
|
|
"authority": "deployed-runtime", "detail": "import crash"}])
|
|
# compare: prediction's frontier vs the EVIDENCE-derived set
|
|
observed_set = {"module:m_b"}
|
|
pred_set = {e for k in ("level0_direct", "level1_obligations",
|
|
"contracts", "level2_verification")
|
|
for e in predicted.get(k, [])}
|
|
missed = observed_set - pred_set
|
|
assert missed == {"module:m_b"}
|
|
# and the comparison helper records it as CANDIDATE, not truth
|
|
result = spine.predicted_vs_observed(predicted, seeded, ["module:m_a"])
|
|
assert all(c["status"] == "CANDIDATE" for c in result["candidate_corrections"])
|
|
|
|
|
|
# ── HARDENING: candidate relationships + event seam (T8) ──────────────────
|
|
|
|
class TestHardeningCandidatesAndEvents:
|
|
def test_model_speculation_is_candidate_never_truth(self, seeded):
|
|
"""A model-hypothesised relationship (no evidence) enters as
|
|
CANDIDATE: representable, queryable, but NOT structural truth —
|
|
excluded from impact frontiers until evidence promotes it."""
|
|
spine.add_relationship(seeded, type="depends_on", from_id="module:m_a",
|
|
to_id="module:m_b", evidence_refs=[]) # speculation
|
|
rel = spine.relationships(seeded, from_id="module:m_a", type="depends_on")[0]
|
|
assert rel["status"] == "CANDIDATE"
|
|
f = spine.impact_frontier(seeded, ["module:m_a"])
|
|
assert "module:m_b" not in f["level1_obligations"]
|
|
# evidence arrives → promotion to CURRENT makes it real
|
|
rel["evidence_refs"] = ["ast-import:m_a"]
|
|
rel["status"] = "CURRENT"
|
|
f2 = spine.impact_frontier(seeded, ["module:m_a"])
|
|
assert "module:m_b" in f2["level1_obligations"]
|
|
|
|
def test_event_seam_carries_future_event_kinds_without_core_redesign(self, seeded):
|
|
"""The seam must transport stale/conflict/supersession/degradation
|
|
events as typed facts — future event kinds flow without policy."""
|
|
spine.add_entity(seeded, id="module:new_thing", kind="module", maturity="DECLARED")
|
|
spine.supersede(seeded, "module:new_thing", "module:m_a", "evidence:decision")
|
|
spine.mark_conflict(seeded, "module:new_thing", "registry", "exists",
|
|
"runtime", "unavailable", "evidence:check")
|
|
spine._authority_observed(seeded, None, "proj-x")
|
|
spine.authority_failed(seeded, "proj-x", "boom")
|
|
events = spine.structural_events(seeded)
|
|
kinds = {e["type"] for e in events}
|
|
assert {"superseded", "authority_conflict", "authority_degraded"} <= kinds
|
|
# all dumb typed facts: no salience/policy/wake fields anywhere
|
|
for e in events:
|
|
assert not {"salience", "attention", "wake", "priority"} & set(e.keys())
|
|
|
|
def test_intrinsic_query_on_real_store_is_fast_and_deterministic(self):
|
|
"""T6: the self-knowledge property on the REAL store — one query,
|
|
deterministic, no LLM/memory/rebuild. And fast (bounded lookup)."""
|
|
doc = spine.load()
|
|
if doc is None:
|
|
pytest.skip("no spine store in this checkout")
|
|
import time as _t
|
|
t0 = _t.monotonic()
|
|
q1 = spine.query(doc, "module:rune_operator")
|
|
q2 = spine.query(doc, "module:rune_operator")
|
|
dt = _t.monotonic() - t0
|
|
assert q1 == q2 # deterministic
|
|
assert q1["owner"] and q1["state"]["maturity"]
|
|
assert "impact_frontier" in q1
|
|
assert dt < 2.0, f"query took {dt:.2f}s — intrinsic property lost"
|
|
|
|
|
|
# ── CLOSURE 1: durable identity through the REAL build path ───────────────
|
|
|
|
class TestClosureRealBuildDurability:
|
|
"""The hardening pass tested alias/supersession around save/load with a
|
|
build-path fallback that could mock the real refresh. Memory will depend
|
|
on spine:// references — prove durability through the ACTUAL build."""
|
|
|
|
def _seeded_store(self, tmp_path):
|
|
doc = spine.new_doc()
|
|
spine.add_entity(doc, id="subsystem:memory", kind="subsystem",
|
|
identities={"description": "fixture"}, maturity="DECLARED")
|
|
for m in ("memory_selector",):
|
|
spine.add_entity(doc, id=f"module:{m}", kind="module", maturity="DECLARED",
|
|
identities={"source": f"src/rune/{m}.py"})
|
|
spine.add_relationship(doc, type="belongs_to", from_id=f"module:{m}",
|
|
to_id="subsystem:memory", evidence_refs=["fixture"])
|
|
spine.save(doc, spine.CURRENT)
|
|
return doc
|
|
|
|
def test_alias_survives_real_build_refresh(self, tmp_path, monkeypatch):
|
|
"""create A → alias A_old → save → reload → REAL build() → save →
|
|
reload → spine://.../A_old still resolves to A. No mocked path."""
|
|
monkeypatch.setattr(spine, "STORE_DIR", tmp_path / "store")
|
|
monkeypatch.setattr(spine, "CURRENT", tmp_path / "store" / "current.json")
|
|
monkeypatch.setattr(spine, "SNAPSHOTS", tmp_path / "store" / "snaps")
|
|
monkeypatch.setattr(spine, "_deployed_observation", lambda: None)
|
|
monkeypatch.setattr(spine, "SRC_DIR", tmp_path / "src" / "rune")
|
|
(tmp_path / "src" / "rune").mkdir(parents=True)
|
|
(tmp_path / "src" / "rune" / "memory_selector.py").write_text("x = 1\n")
|
|
monkeypatch.setattr(spine, "TESTS_DIR", tmp_path / "tests")
|
|
(tmp_path / "tests").mkdir(exist_ok=True)
|
|
monkeypatch.setattr(spine, "UMBRELLA_MANIFEST", tmp_path / "no-manifest.json")
|
|
|
|
self._seeded_store(tmp_path)
|
|
doc = spine.load()
|
|
spine.alias(doc, "module:memory_selector", "module:mem_sel_old")
|
|
spine.save(doc)
|
|
|
|
# THE REAL PATH: spine.build() (loads the persisted store, carries
|
|
# everything forward, re-derives module facts, reconciles, saves)
|
|
rebuilt = spine.build()
|
|
spine.save(rebuilt)
|
|
|
|
reloaded = spine.load()
|
|
old_ref = spine.resolve_uri("spine://module/mem_sel_old")
|
|
e = spine.entity(reloaded, old_ref)
|
|
assert e is not None, "alias lost by real build"
|
|
assert e["id"] == "module:memory_selector"
|
|
assert "module:mem_sel_old" in e.get("aliases", [])
|
|
|
|
def test_supersession_survives_real_build_refresh(self, tmp_path, monkeypatch):
|
|
"""B supersedes A → REAL build() → superseded_by intact, historical
|
|
entity not silently deleted, old reference resolves meaningfully."""
|
|
monkeypatch.setattr(spine, "STORE_DIR", tmp_path / "store")
|
|
monkeypatch.setattr(spine, "CURRENT", tmp_path / "store" / "current.json")
|
|
monkeypatch.setattr(spine, "SNAPSHOTS", tmp_path / "store" / "snaps")
|
|
monkeypatch.setattr(spine, "_deployed_observation", lambda: None)
|
|
monkeypatch.setattr(spine, "SRC_DIR", tmp_path / "src" / "rune")
|
|
(tmp_path / "src" / "rune").mkdir(parents=True)
|
|
(tmp_path / "src" / "rune" / "memory_selector.py").write_text("x = 1\n")
|
|
monkeypatch.setattr(spine, "TESTS_DIR", tmp_path / "tests")
|
|
(tmp_path / "tests").mkdir(exist_ok=True)
|
|
monkeypatch.setattr(spine, "UMBRELLA_MANIFEST", tmp_path / "no-manifest.json")
|
|
|
|
self._seeded_store(tmp_path)
|
|
doc = spine.load()
|
|
spine.add_entity(doc, id="module:memory_orchestrator", kind="module",
|
|
maturity="DECLARED")
|
|
spine.supersede(doc, "module:memory_orchestrator", "module:memory_selector",
|
|
"evidence:closure-decision")
|
|
spine.save(doc)
|
|
|
|
rebuilt = spine.build()
|
|
spine.save(rebuilt)
|
|
|
|
reloaded = spine.load()
|
|
old = spine.entity(reloaded, "module:memory_selector")
|
|
assert old is not None, "superseded entity deleted by real build"
|
|
assert old["superseded_by"] == "module:memory_orchestrator"
|
|
assert old["status"] == "SUPERSEDED"
|
|
# the supersedes edge survives as data (non-obligation context)
|
|
assert spine.relationships(reloaded, type="supersedes",
|
|
from_id="module:memory_orchestrator",
|
|
to_id="module:memory_selector")
|
|
|
|
|
|
# ── CLOSURE 2+3: no competing structural authority for RUNE ──────────────
|
|
|
|
class TestClosureNoCompetingAuthority:
|
|
def test_dependency_tracker_scope_is_harness_not_rune(self):
|
|
"""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.
|
|
|
|
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"
|
|
# 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.SRC_DIR / "spine.py").is_file()
|