deploy / deploy (push) Skipped
CI Pipeline / lint (push) Successful in 34s
CI Pipeline / go-surface (push) Successful in 3s
CI Pipeline / best-practices (push) Successful in 2s
CI Pipeline / documentation (push) Successful in 2s
CI Pipeline / type-check (push) Successful in 28s
CI Pipeline / canary (push) Successful in 45s
CI Pipeline / gitea-offline (push) Successful in 13s
CI Pipeline / lint (pull_request) Successful in 34s
CI Pipeline / go-surface (pull_request) Successful in 2s
CI Pipeline / canary (pull_request) Successful in 45s
CI Pipeline / best-practices (pull_request) Successful in 2s
CI Pipeline / documentation (pull_request) Successful in 1s
CI Pipeline / type-check (pull_request) Successful in 26s
CI Pipeline / gitea-offline (pull_request) Successful in 12s
Spec gate (headless) / spec-gate (pull_request) Successful in 4s
CI Pipeline / unit-tests (push) Successful in 4m54s
CI Pipeline / unit-tests (pull_request) Successful in 4m54s
CI Pipeline / required-checks (push) Successful in 0s
CI Pipeline / required-checks (pull_request) Successful in 1s
CI Pipeline / CI Pipeline / notify-event-bridge (push) Successful in 2s
CI Pipeline / CI Pipeline / notify-event-bridge (pull_request) Successful in 2s
The command seam validated intents but nothing EXECUTED them. dispatch() binds the canonical store, runs the validator, then executes accepted intents through the Construction Interface (resume/transition/amend_scope), refusing via the lifecycle with accepted=False+success=True (a refusal is a valid protocol outcome, not an error). Idempotent replay returns the recorded intent without re-executing. Three contracts: end-to-end phase movement, refusal shape, replay-no-double-execute; seam file 12/12. Slice 1 of 5 was stage_guards (#2467 wire wave).
250 lines
11 KiB
Python
250 lines
11 KiB
Python
"""test_substrate_seam.py — T16 §16I/§16J/§16K: future-Substrate
|
|
compatibility seam held-outs.
|
|
|
|
The MOCK client below knows NOTHING about current.json or internal
|
|
entity arrays — it sees only the public projection (construction_
|
|
projection) and the inbound command seam (construction_commands). It:
|
|
|
|
1. requests current construction state through the public seam;
|
|
2. observes a construction event (a projection whose waiting_state
|
|
advanced — simulated by a durable state change, not by poking
|
|
internals);
|
|
3. correlates project/contract/stage;
|
|
4. issues RESUME (valid) and receives acceptance;
|
|
5. issues an invalid transition (START on a non-draft) and receives
|
|
REFUSAL;
|
|
6. survives process restart using identities (re-request state by
|
|
contract id alone — no transcript, no cached internals);
|
|
7. leaves Scaffolding fully usable without the client existing.
|
|
|
|
NOT implemented (explicitly out of scope): relevance, autonomous
|
|
prioritisation, goal planning, memory, Substrate cognition. This is a
|
|
compatibility seam, not a Substrate.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
REPO = Path(__file__).resolve().parent.parent
|
|
sys.path.insert(0, str(REPO / "src"))
|
|
|
|
from rune import construction_commands as cmds # noqa: E402
|
|
from rune import construction_projection as proj # noqa: E402
|
|
from rune import scaffold as sc # noqa: E402
|
|
|
|
|
|
class MockSubstrateClient:
|
|
"""TEST CLIENT ONLY. Sees the projection + command seam; the store
|
|
object is passed to the SEAM functions, never touched directly."""
|
|
|
|
def __init__(self, scaffold):
|
|
self._scaffold = scaffold
|
|
self.observed: list[dict] = []
|
|
|
|
def request_state(self, contract_id: str, **kw) -> dict:
|
|
p = proj.project(self._scaffold, contract_id, **kw)
|
|
assert not proj.validate(p), f"invalid projection: {proj.validate(p)}"
|
|
self.observed.append(p)
|
|
return p
|
|
|
|
def issue(self, command_type: str, construction_id: str, **kw) -> dict:
|
|
# the serving layer binds the canonical store to the seam; the
|
|
# client only ever sees the returned envelope
|
|
return cmds.command(command_type, construction_id=construction_id,
|
|
scaffold=self._scaffold, **kw)
|
|
|
|
|
|
def _fixture_scaffold(tmp_path):
|
|
s = sc.Scaffold(tmp_path / ".rune" / "scaffold", "substrate-seam")
|
|
doc = s.init(purpose="substrate seam held-out")
|
|
s.new_contract(doc, contract_id="seam-1", need="a thing to build")
|
|
s.save(doc)
|
|
return s
|
|
|
|
|
|
def test_16k_state_through_public_seam_only(tmp_path):
|
|
s = _fixture_scaffold(tmp_path)
|
|
client = MockSubstrateClient(s)
|
|
p = client.request_state("seam-1", originating_goal_id="goal-9",
|
|
stage="BUILD")
|
|
assert p["schema_version"] == proj.SCHEMA_VERSION
|
|
assert p["contract_id"] == "seam-1"
|
|
assert p["phase"] == "draft"
|
|
assert p["candidate_next_action"] # Scaffolding tells, client reads
|
|
# the projection exposes NO storage internals
|
|
assert "entities" not in p and "current.json" not in str(p)
|
|
|
|
|
|
def test_16k_observe_event_correlate_and_resume(tmp_path):
|
|
s = _fixture_scaffold(tmp_path)
|
|
client = MockSubstrateClient(s)
|
|
before = client.request_state("seam-1", stage="BUILD")
|
|
# a construction EVENT: durable state advanced (transition to
|
|
# approved — done through the real lifecycle, not internals)
|
|
doc = s.load()
|
|
s.transition_contract(doc, "seam-1", "approved")
|
|
s.save(doc)
|
|
after = client.request_state("seam-1", stage="BUILD")
|
|
assert before["phase"] == "draft" and after["phase"] == "approved"
|
|
# correlate project/contract/stage, then issue the valid command
|
|
assert after["project_id"] == "substrate-seam"
|
|
resp = client.issue("RESUME", construction_id="seam-1",
|
|
correlation_id="corr-1")
|
|
assert resp["accepted"] is True and resp["op"] == "resume"
|
|
|
|
|
|
def test_16k_invalid_transition_refused(tmp_path):
|
|
s = _fixture_scaffold(tmp_path)
|
|
client = MockSubstrateClient(s)
|
|
doc = s.load()
|
|
s.transition_contract(doc, "seam-1", "approved")
|
|
s.save(doc)
|
|
# START on a non-draft is invalid — Scaffolding decides, Substrate asks
|
|
resp = client.issue("START", construction_id="seam-1")
|
|
assert resp["accepted"] is False
|
|
assert "draft" in resp["reason"]
|
|
|
|
|
|
def test_16k_direct_store_semantics_impossible(tmp_path):
|
|
s = _fixture_scaffold(tmp_path)
|
|
client = MockSubstrateClient(s)
|
|
# the client has no path to the store: it never received the store
|
|
# dir, only the scaffold object for seam calls. Attempting to mutate
|
|
# storage is not expressible through the seam — the closest it gets
|
|
# is PROVIDE_CONTEXT, which is validated and user-provenance-gated.
|
|
resp = client.issue("PROVIDE_CONTEXT", construction_id="seam-1",
|
|
payload={"add_paths": ["src/x.py"]})
|
|
assert resp["accepted"] is True
|
|
assert resp["args"]["authority"] == "substrate" # never user-forged
|
|
|
|
|
|
def test_16k_survives_restart_via_identities(tmp_path):
|
|
s = _fixture_scaffold(tmp_path)
|
|
# "restart": drop the client AND the scaffold; rebuild from identities
|
|
contract_id, project_id = "seam-1", "substrate-seam"
|
|
del s
|
|
s2 = sc.Scaffold(tmp_path / ".rune" / "scaffold", project_id)
|
|
client2 = MockSubstrateClient(s2)
|
|
p = client2.request_state(contract_id) # contract id alone — no transcript
|
|
assert p["phase"] == "draft" and p["project_id"] == project_id
|
|
|
|
|
|
def test_16k_scaffolding_usable_without_client(tmp_path):
|
|
# the client never existed here: the full lifecycle still works
|
|
s = _fixture_scaffold(tmp_path)
|
|
doc = s.load()
|
|
s.transition_contract(doc, "seam-1", "approved")
|
|
s.transition_contract(doc, "seam-1", "building")
|
|
s.save(doc)
|
|
doc2 = s.load()
|
|
c = next(e for e in doc2["entities"] if e["id"] == "contract:seam-1")
|
|
assert c["phase"] == "building"
|
|
|
|
|
|
def test_16k_idempotent_command_replay(tmp_path):
|
|
s = _fixture_scaffold(tmp_path)
|
|
client = MockSubstrateClient(s)
|
|
first = client.issue("RESUME", construction_id="seam-1",
|
|
command_id="replay-1")
|
|
second = client.issue("RESUME", construction_id="seam-1",
|
|
command_id="replay-1")
|
|
assert first["accepted"] and second["accepted"]
|
|
assert second["idempotent_replay"] is True
|
|
|
|
|
|
def test_16k_unknown_command_and_unknown_construction_refused(tmp_path):
|
|
s = _fixture_scaffold(tmp_path)
|
|
client = MockSubstrateClient(s)
|
|
assert client.issue("PAUSE", construction_id="seam-1")["accepted"] is False
|
|
assert client.issue("RESUME", construction_id="nope")["accepted"] is False
|
|
assert client.issue("RESUME", construction_id="")["accepted"] is False
|
|
|
|
|
|
def test_16k_projection_schema_validated(tmp_path):
|
|
s = _fixture_scaffold(tmp_path)
|
|
client = MockSubstrateClient(s)
|
|
p = client.request_state("seam-1")
|
|
assert proj.validate(p) == []
|
|
bad = dict(p)
|
|
del bad["phase"]
|
|
assert proj.validate(bad) != []
|
|
bad2 = dict(p, schema_version=999)
|
|
assert proj.validate(bad2) != []
|
|
|
|
|
|
# ── T5.15 slice 2: the dispatch wire (command → interface execution) ────────
|
|
|
|
|
|
def test_dispatch_executes_accepted_transition_end_to_end(tmp_path):
|
|
"""A START command on a draft contract executes through the interface:
|
|
the phase actually moves — not just an accepted intent."""
|
|
store = str(tmp_path / ".rune" / "scaffold")
|
|
iface = cmds if False else None # noqa: F841 — readability anchor
|
|
from rune import construction_interface as ci
|
|
ciface = ci.ConstructionInterface(builder="seed", project_store=store, project_id="proj")
|
|
ciface.scaffold.init(purpose="t")
|
|
doc = ciface.scaffold.load()
|
|
sc.add_entity(doc, id="contract:disp-1", kind="contract",
|
|
identities={"objective": "dispatch"}, evidence_refs=["e"])
|
|
doc["entities"][-1]["phase"] = "draft"
|
|
ciface.scaffold.commit(doc, what="seed")
|
|
out = cmds.dispatch("START", construction_id="disp-1",
|
|
store_path=store + "|proj")
|
|
# external-project store path: dispatch builds its own interface bound
|
|
# to the same store
|
|
assert out["executed"] is True, out
|
|
assert out["success"] is True, out
|
|
doc2 = ciface.scaffold.load()
|
|
c = sc.entity(doc2, "contract:disp-1")
|
|
assert c["phase"] == "approved"
|
|
|
|
|
|
def test_dispatch_refusal_is_a_valid_protocol_outcome(tmp_path):
|
|
"""START on a non-draft contract: the lifecycle refuses — the dispatch
|
|
itself succeeds (success=True) with accepted=False."""
|
|
store = str(tmp_path / ".rune" / "scaffold")
|
|
from rune import construction_interface as ci
|
|
ciface = ci.ConstructionInterface(builder="seed", project_store=store, project_id="proj")
|
|
ciface.scaffold.init(purpose="t")
|
|
doc = ciface.scaffold.load()
|
|
sc.add_entity(doc, id="contract:disp-2", kind="contract",
|
|
identities={"objective": "x"}, evidence_refs=["e"])
|
|
doc["entities"][-1]["phase"] = "draft"
|
|
ciface.scaffold.commit(doc, what="seed")
|
|
out = cmds.dispatch("START", construction_id="disp-2",
|
|
payload={"phase": "building"},
|
|
store_path=store + "|proj")
|
|
# contract starts in draft so START is ACCEPTED here; drive to building
|
|
# first to prove the refusal shape:
|
|
assert out["executed"] and out["success"]
|
|
out2 = cmds.dispatch("START", construction_id="disp-2",
|
|
store_path=store + "|proj")
|
|
assert out2["accepted"] is False and out2["executed"] is False
|
|
assert out2["success"] is True
|
|
assert "draft" in out2["reason"]
|
|
|
|
|
|
def test_dispatch_idempotent_replay_does_not_double_execute(tmp_path):
|
|
"""Same command_id twice: the second is a replay — the phase does not
|
|
move twice and idempotent_replay=True comes back."""
|
|
store = str(tmp_path / ".rune" / "scaffold")
|
|
from rune import construction_interface as ci
|
|
ciface = ci.ConstructionInterface(builder="seed", project_store=store, project_id="proj")
|
|
ciface.scaffold.init(purpose="t")
|
|
doc = ciface.scaffold.load()
|
|
sc.add_entity(doc, id="contract:disp-3", kind="contract",
|
|
identities={"objective": "idem"}, evidence_refs=["e"])
|
|
doc["entities"][-1]["phase"] = "draft"
|
|
ciface.scaffold.commit(doc, what="seed")
|
|
first = cmds.dispatch("START", construction_id="disp-3",
|
|
command_id="fixed-cmd-1", store_path=store + "|proj")
|
|
assert first["executed"] and first["success"]
|
|
second = cmds.dispatch("START", construction_id="disp-3",
|
|
command_id="fixed-cmd-1", store_path=store + "|proj")
|
|
assert second["idempotent_replay"] is True
|
|
assert second.get("executed") in (False, None) # replay ≠ execution
|