153 lines
6.7 KiB
Python
153 lines
6.7 KiB
Python
#!/usr/bin/env python3
|
|
"""gen_scaffold_truth.py — scaffold batch 1 truth battery
|
|
(contract BUILDER-2026-10-07T17:27:25Z-6bc737).
|
|
|
|
The LIVE Scaffold runs against a tmp store with now() frozen (module-patch
|
|
on rune.scaffold's imported binding). Cases: the transition machine (legal
|
|
+ illegal + unknown phase, exact error texts), new_contract/transition_
|
|
contract state evolution, check_scope (green/red/unknown), layout_
|
|
obligations (FLEXIBLE vs USER-LOCKED), json_frontier projection.
|
|
|
|
Usage: python3 scripts/gen_scaffold_truth.py (from repo root)
|
|
"""
|
|
|
|
import json
|
|
import sys
|
|
import tempfile
|
|
from pathlib import Path
|
|
|
|
REPO = Path(__file__).resolve().parent.parent
|
|
sys.path.insert(0, str(REPO / "src"))
|
|
|
|
from rune import scaffold as sc # noqa: E402
|
|
from rune.structural_store import new_doc, add_entity # noqa: E402
|
|
|
|
FROZEN = "2026-10-07T17:30:00"
|
|
|
|
|
|
def main():
|
|
# Freeze the clock at scaffold's namespace binding.
|
|
sc.now = lambda: FROZEN
|
|
|
|
tmp = Path(tempfile.mkdtemp(prefix="scaffold-truth-"))
|
|
s = sc.Scaffold(tmp, "truth-project")
|
|
s.init(purpose="truth battery")
|
|
|
|
cases = []
|
|
|
|
# ── transition machine ────────────────────────────────────────────────
|
|
def tv(name, current, to):
|
|
try:
|
|
sc.Scaffold.transition_contract # noqa: B018 — presence probe
|
|
# validate via a throwaway entity path
|
|
doc = new_doc()
|
|
e = s.new_contract(doc, contract_id="probe", need="probe")
|
|
if current != "draft":
|
|
e["phase"] = current
|
|
s.transition_contract(doc, "probe", to)
|
|
cases.append({"fn": "transition", "name": name,
|
|
"input": {"current": current, "to": to},
|
|
"expected": {"ok": True}})
|
|
except Exception as ex: # noqa: BLE001 — intentional: the case IS the error verdict; the oracle's error text becomes the truth column
|
|
cases.append({"fn": "transition", "name": name,
|
|
"input": {"current": current, "to": to},
|
|
"expected": {"ok": False, "error": str(ex)}})
|
|
|
|
tv("legal_approved", "draft", "approved")
|
|
tv("legal_building", "approved", "building")
|
|
tv("legal_verifying", "building", "verifying")
|
|
tv("legal_migrating", "verifying", "migrating")
|
|
tv("legal_superseding", "migrating", "superseding")
|
|
tv("legal_closed", "superseding", "closed")
|
|
tv("legal_failed_retry", "failed", "building")
|
|
tv("legal_abandon", "building", "abandoned")
|
|
tv("illegal_closed_from_draft", "draft", "closed")
|
|
tv("illegal_rewind", "verifying", "draft")
|
|
tv("illegal_skip", "draft", "verifying")
|
|
tv("unknown_phase", "draft", "nonexistent_phase")
|
|
|
|
# ── contract lifecycle evolution ──────────────────────────────────────
|
|
doc = new_doc()
|
|
s.new_contract(doc, contract_id="life", need="port the lifecycle",
|
|
origin="builder", scope=["src/go/scaffold/"],
|
|
acceptance=["truth battery passes"])
|
|
e = s.transition_contract(doc, "life", "approved", evidence_ref="e1")
|
|
e = s.transition_contract(doc, "life", "building", evidence_ref="e2", note="x" * 300)
|
|
cases.append({
|
|
"fn": "lifecycle", "name": "evolution",
|
|
"input": {},
|
|
"expected": {"entity": e},
|
|
})
|
|
|
|
# duplicate contract
|
|
try:
|
|
s.new_contract(doc, contract_id="life", need="again")
|
|
dup = {"ok": True}
|
|
except ValueError as ex:
|
|
dup = {"ok": False, "error": str(ex)}
|
|
cases.append({"fn": "dup", "name": "duplicate", "input": {},
|
|
"expected": dup})
|
|
|
|
# ── check_scope ───────────────────────────────────────────────────────
|
|
doc2 = new_doc()
|
|
s.new_contract(doc2, contract_id="scoped", need="n", scope=["src/go/a/", "src/go/b/main.go"])
|
|
e2 = s.contract(doc2, "scoped")
|
|
cases.append({
|
|
"fn": "check_scope", "name": "mixed",
|
|
"input": {"changed": ["src/go/a/x.go", "src/go/b/main.go", "src/other/f.py", "src/go/a/deep/y.go"], "scope": ["src/go/a/", "src/go/b/main.go"]},
|
|
"expected": s.check_scope(doc2, "scoped",
|
|
["src/go/a/x.go", "src/go/b/main.go", "src/other/f.py", "src/go/a/deep/y.go"]),
|
|
})
|
|
doc3 = new_doc()
|
|
s.new_contract(doc3, contract_id="unscoped", need="n")
|
|
cases.append({
|
|
"fn": "check_scope", "name": "unscoped",
|
|
"input": {"changed": ["anything.py"], "scope": []},
|
|
"expected": s.check_scope(doc3, "unscoped", ["anything.py"]),
|
|
})
|
|
|
|
# ── layout obligations ────────────────────────────────────────────────
|
|
doc4 = new_doc()
|
|
add_entity(doc4, id="layout:panel", kind="layout_contract",
|
|
identities={"component": "Panel", "region": "sidebar",
|
|
"implementation": "src/go/panel.go", "lock": False,
|
|
"provenance": "scaffold"}, maturity="DECLARED")
|
|
add_entity(doc4, id="layout:header", kind="layout_contract",
|
|
identities={"component": "Header", "region": "top",
|
|
"implementation": "src/go/header.go", "lock": True,
|
|
"provenance": "user"}, maturity="DECLARED")
|
|
cases.append({
|
|
"fn": "layout", "name": "mixed_locks",
|
|
"input": {"affected": ["Panel", "region:top"], "doc": doc4},
|
|
"expected": {"obligations": s.layout_obligations(doc4, ["Panel", "region:top"])},
|
|
})
|
|
cases.append({
|
|
"fn": "layout", "name": "no_hit",
|
|
"input": {"affected": ["Unrelated"], "doc": doc4},
|
|
"expected": {"obligations": []},
|
|
})
|
|
|
|
# ── json_frontier ─────────────────────────────────────────────────────
|
|
f = {"contracts": ["c1"], "level2_verification": ["v1"]}
|
|
cases.append({
|
|
"fn": "frontier", "name": "projection",
|
|
"input": {"frontier": f},
|
|
"expected": {"json": sc.json_frontier(f)},
|
|
})
|
|
|
|
out = {
|
|
"oracle": "src/rune/scaffold.py (live Scaffold, now frozen)",
|
|
"generated": "2026-10-07",
|
|
"frozen_now": FROZEN,
|
|
"cases": cases,
|
|
}
|
|
target = REPO / "src/go/scaffold" / "truth_scaffold.json"
|
|
target.write_text(json.dumps(out, indent=1, ensure_ascii=False) + "\n")
|
|
print(f"wrote {target} ({len(cases)} cases)")
|
|
import shutil
|
|
shutil.rmtree(tmp, ignore_errors=True)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|