feat(construction_interface): close PARTIAL gaps — dichotomy wave

Audit 25/27 → 27/27 FULL (main + _programme_continuation were the two
unmatched fns; the port carries the full envelope surface). Freeze rig
scripts/ci_freeze_seams/sitecustomize.py (PYTHONPATH auto-import, used by
BOTH generator and --verify): every RUNE_*_CORE gate pinned python for
the whole dependency cone, frozen clock on structural_store.now +
scaffold.now + capability_ledger._now + programme_continuation.time,
deterministic uuid mint (counter resets per rig helper), hermetic
per-case stores under CI_FREEZE_HOME (mini-doc paths replay
byte-identically), doc-carrying seam bindings for every stateful method.

Truth: 85-case frozen corpus + 5-case hash corpus (obligation ids carry
Python's process-randomized str hash — captured with PYTHONHASHSEED=0,
SHAPE-pinned downstream, the memory_consolidation law). Regeneration
byte-identical; freeze_truth --verify OK both corpora.

Rust kernels (src/rust/construction_interface): kernel.rs adds the
doc-in/doc-out halves of submit_need (need assembly + envelope over the
construction_decision make_decision kernel + contract generation twin),
verify (scope check + layout gate via the layout_contract kernel),
transition (guard canary over frozen command outcomes + legality map +
checkpoint/session assembly + normalize_sorted commit twin),
close (close_contract + observed-impact + close_quality bucket math +
contract_quality projection), _programme_continuation (load/save shape
twins + continuation canary), _auto_material_change (via the
capability_ledger op_material_change kernel), main (CLI dispatch + exit
law), ladder_walk (full ladder in ONE process); projection.rs the pure
helpers (to_stage_check, guard_specs, obligations projection with the
FNV-1a stand-in id, envelope with extra merge, _match).

Store-normalization law discovered: the oracle's commit path sorts
lists by canonical JSON (structural_store._sorted) — the kernel applies
the same normalization at its commit points or read-back projections
(patterns_considered) diverge.

Battery: 25 tests — the 85-case corpus replays VERBATIM through
kernel::dispatch, the 5 hash cases shape-pinned, gap-fns-are-real-ports
probe. clippy --all-targets -D warnings 0. Parity
scripts/check_construction_interface_parity.py: FROZEN layer 85/85 + 5
through the ci_replay ctypes FFI of the built cdylib; LIVE layer
fresh re-freeze byte-identical. Python 37/37 both pins (floors pinned
python + live cores).
This commit is contained in:
Nick committed 2026-10-13 19:03:37 +01:00
1 parent b433be3e33
commit 192c63b36e
12 files changed
+20943 -7

No files matched your search

+5 -5
View File
@@ -2,14 +2,14 @@
_Regenerate with `python3 scripts/dichotomy_audit.py --write`. A module is FULL only when 80%+ of its Python functions have name-preserving counterparts in the Go/Rust implementation — directories without the functions are facades and count as NOT TOUCHED._
**FULL completion: 42%** (81/192 runtime modules at FULL)
**FULL completion: 43%** (82/192 runtime modules at FULL)
Function-level detail: 62% (1419/2278 runtime functions matched) — secondary; includes in-flight work
Function-level detail: 63% (1440/2278 runtime functions matched) — secondary; includes in-flight work
| Status | Modules | Lines | Meaning |
|---|---|---|---|
| FULL | 81 | 33154 | 80%+ of functions ported |
| PARTIAL | 71 | 29497 | 20–80% — real logic exists, gaps named below |
| FULL | 82 | 34347 | 80%+ of functions ported |
| PARTIAL | 70 | 28304 | 20–80% — real logic exists, gaps named below |
| NOT TOUCHED | 40 | 11513 | no name-preserving implementation |
| Total runtime | 192 | 74164 | |
@@ -64,7 +64,6 @@ Function-level detail: 62% (1419/2278 runtime functions matched) — secondary;
|---|---|---|---|
| rune_operator | 2746 | 25/68 | _build_model_router, _sorted_tool_names, _filter_safety_noise, _wire_auto_filer, _reconcile_guarded, get_operator |
| memory_store | 2333 | 30/68 | _ctx_budget, now_iso, ensure_memory_type_column, _embed, _temp_json, _entities_to_json |
| construction_interface | 1193 | 6/27 | _to_stage_check, _guard_specs_from_obligations, t_layout, _obligations_projection, _obligation_scope_files, main |
| provider_registry | 679 | 11/28 | _ctx_budget, _format_tools_openai, _execute_tool_calls, _execute_tool_calls_anthropic, get_registry, connect_provider |
| warm_router | 1518 | 17/33 | _ensure_use_history_table, record_outcome, record_neuron_fire, base_level_activation, _db, _aaak |
| cron_engine | 440 | 13/27 | get_cron_engine, run_task, start_engine, stop_engine, to_dict, fire_on_task_complete |
@@ -140,6 +139,7 @@ Function-level detail: 62% (1419/2278 runtime functions matched) — secondary;
|---|---|---|---|
| curator | 1838 | 43/43 | GO |
| construction_decision | 1211 | 21/24 | RUST |
| construction_interface | 1193 | 27/27 | RUST |
| sleep_time | 1056 | 26/26 | GO |
| spine | 975 | 25/25 | RUST |
| structural_store | 921 | 34/41 | RUST |
@@ -0,0 +1,128 @@
#!/usr/bin/env python3
"""Live parity: construction_interface Rust kernels vs the frozen oracle
truth (dichotomy wave, contract factory-gap-ci). Layers:
1. FROZEN — the frozen truth corpora (frozen_ci_truth.json 85 cases +
frozen_ci_truth_hash.json 5 shape-pinned cases, generated by
scripts/gen_construction_interface_truth.py against the LIVE Python
oracle through the scripts/ci_freeze_seams rig) replayed through
the ci_replay ctypes FFI entry of the built cdylib, case by case.
2. LIVE — a fresh re-freeze of the oracle through the same rig in a
subprocess; the regenerated corpora must be byte-identical to the
committed files (the oracle is still the parity authority while the
strangler runs).
Exit 0 only at 100% parity (85 verbatim + 5 shape-pinned + byte-identical
re-freeze).
"""
import ctypes
import json
import os
import subprocess
import sys
import tempfile
from pathlib import Path
REPO = Path(__file__).resolve().parents[1]
CRATE = REPO / "src" / "rust" / "construction_interface"
LIB = CRATE / "target" / "release" / "libconstruction_interface.so"
FROZEN = CRATE / "frozen_ci_truth.json"
FROZEN_HASH = CRATE / "frozen_ci_truth_hash.json"
HASH_ID_PREFIXES = (
"contract:", "regression:", "verification:",
"composition:", "acquisition:",
)
def normalize_hash_ids(obj):
if isinstance(obj, str):
kind, _, digits = obj.partition(":")
if (f"{kind}:" in HASH_ID_PREFIXES and digits
and len(digits) <= 8 and digits.isdigit()):
return f"{kind}:#"
return obj
if isinstance(obj, list):
return [normalize_hash_ids(v) for v in obj]
if isinstance(obj, dict):
return {k: normalize_hash_ids(v) for k, v in obj.items()}
return obj
def replay(lib, case):
payload = json.dumps(case, separators=(",", ":")).encode()
ptr = lib.ci_replay(payload, len(payload))
if not ptr:
raise RuntimeError("ci_replay returned NULL")
text = ctypes.c_char_p(ptr).value.decode()
lib.ci_free_string(ptr)
return json.loads(text)
def frozen_layer():
lib = ctypes.CDLL(str(LIB))
lib.ci_replay.argtypes = [ctypes.c_char_p, ctypes.c_size_t]
lib.ci_replay.restype = ctypes.c_void_p
lib.ci_free_string.argtypes = [ctypes.c_void_p]
lib.ci_free_string.restype = None
ok = 0
cases = json.loads(FROZEN.read_text())
for case in cases:
got = replay(lib, case)
assert got == case["expected"], (
f"FROZEN divergence: {case['fn']} "
f"{case['args'][0] if case['args'] else '?'}")
ok += 1
hash_ok = 0
for case in json.loads(FROZEN_HASH.read_text()):
got = normalize_hash_ids(replay(lib, case))
want = normalize_hash_ids(case["expected"])
assert got == want, (
f"HASH divergence: {case['fn']} "
f"{case['args'][0] if case['args'] else '?'}")
hash_ok += 1
print(f"FROZEN: PARITY OK {ok}/{len(cases)} verbatim "
f"+ {hash_ok} hash-shape cases")
return ok, hash_ok
def main() -> int:
committed_main = FROZEN.read_bytes()
committed_hash = FROZEN_HASH.read_bytes()
n_main, n_hash = frozen_layer()
with tempfile.TemporaryDirectory(prefix="ci_gap_parity_") as _scratch:
# the re-freeze must run at the CANONICAL rig home — the mini-doc
# paths are embedded in the frozen truth, so a different
# CI_FREEZE_HOME changes the bytes without changing behavior
home = "/tmp/ci_gap_freeze_home"
subprocess.run(["rm", "-rf", home], check=True)
env = dict(
os.environ,
CI_FREEZE_HOME=home,
PYTHONPATH="scripts/ci_freeze_seams:src",
CS_RUST_LIB="/mnt/workdrive/rune/src/rust/capability_scoring/"
"target/release/libcapability_scoring.so",
)
r = subprocess.run(
[sys.executable, "scripts/gen_construction_interface_truth.py"],
cwd=str(REPO), env=env, capture_output=True, text=True, timeout=600)
if r.returncode != 0:
print(r.stdout[-2000:])
print(r.stderr[-2000:])
print("LIVE: re-freeze FAILED")
return 1
same_main = FROZEN.read_bytes() == committed_main
same_hash = FROZEN_HASH.read_bytes() == committed_hash
if not (same_main and same_hash):
print(f"LIVE: re-freeze NOT byte-identical "
f"(main={same_main} hash={same_hash})")
return 1
print(f"LIVE: fresh re-freeze byte-identical "
f"({n_main}+{n_hash} cases, oracle authority held)")
print("PARITY OK construction_interface")
return 0
if __name__ == "__main__":
sys.exit(main())
+562
View File
@@ -0,0 +1,562 @@
# Freeze rig for the construction_interface dichotomy gap-closing (dichotomy
# wave #2479 gap port). Auto-imported on interpreter start
# (PYTHONPATH=scripts/ci_freeze_seams:src) by BOTH the truth generator
# (scripts/gen_construction_interface_truth.py) and the shared harness's
# --verify mode, so frozen truth is byte-reproducible in any process:
#
# * EVERY RUNE_*_CORE gate is pinned to "python" — the oracle runs its
# pure-Python floors end to end (construction_interface, scaffold,
# structural_store, construction_decision, construction_mode, capability
# ledger, programme continuation, layout contract, ...). The shared
# harness's _pin_oracle_cores cannot reach the transitive gates; this
# blanket pin is the poisoned-truth law for the whole dependency cone.
# * The wall clocks are FROZEN: rune.structural_store.now and
# rune.scaffold.now (bound import) report CI_FROZEN_NOW. The oracle
# stamps need ids, observed_at, checkpoints and closure records through
# these two seams; the frozen truth carries the constant instant.
# * uuid.uuid4 is a deterministic counter (submit_need mints contract ids
# from uuid4().hex[:6]); the counter RESETS per rig helper call so a
# case's ids depend only on how many mints happen inside it.
# * CI_FREEZE_HOME (default /tmp/ci_gap_freeze_home) roots every per-case
# hermetic store: cases/<case>/proj/.rune/scaffold — deterministic
# per-case dirs, because envelopes that carry paths (mini_doc) must
# replay byte-identically.
# * DOC-CARRYING SEAMS: the Rust kernel is a pure doc-in/doc-out membrane
# (the store stays with the facade), so every stateful seam below takes
# the FULL input doc as an argument — the frozen args replay in the Rust
# battery without any store. The input docs are built by the deterministic
# builder helpers (build_doc/submit_doc/walk_doc) that the GENERATOR
# calls; the seam injects the doc into a fresh hermetic store, runs the
# ORACLE method, and returns the envelope (plus oracle-derived doc
# outputs where the kernel's contract includes them).
# * write_mini_doc is redirected into the case dir (the oracle's default
# routes through the SOURCE TREE — a repo-dependent absolute path).
#
# KNOWN-NONDETERMINISTIC SEAM (memory_consolidation precedent): the
# obligation ids in _obligations_projection carry Python's
# process-randomized str hash. Cases whose output carries those ids live
# in the SEPARATE frozen_ci_truth_hash.json battery (captured + verified
# with PYTHONHASHSEED=0) and are SHAPE-pinned in the Rust battery and the
# parity check — never value-frozen in the hash-free battery.
import itertools
import json
import os
import shutil
import uuid as _uuid
from pathlib import Path
for _k in list(os.environ):
if _k.startswith("RUNE_") and _k.endswith("_CORE"):
os.environ[_k] = "python"
CI_FREEZE_HOME = os.environ.get("CI_FREEZE_HOME") or "/tmp/ci_gap_freeze_home"
FROZEN_NOW = "2026-10-13T09:41:23Z"
import rune.structural_store as ss # noqa: E402
import rune.scaffold as _sc # noqa: E402
ss.now = lambda: FROZEN_NOW
_sc.now = lambda: FROZEN_NOW
# programme_continuation stamps saved_at/at through time.strftime directly
# (not ss.now) — pin the time module floor for it.
import rune.capability_ledger as _cl_mod # noqa: E402
import rune.programme_continuation as _pc_mod # noqa: E402
import time as _time_mod # noqa: E402
_cl_mod._now = lambda: FROZEN_NOW
_pc_mod.time = type("_T", (), {
"strftime": staticmethod(lambda f, _t: FROZEN_NOW),
"gmtime": staticmethod(lambda *_a: None),
"time": staticmethod(lambda: 0.0),
"monotonic": staticmethod(lambda: 0.0),
})()
_MINT = itertools.count(1)
class _DetUUID:
def __init__(self, n: int):
self.hex = f"{n:06x}" + "0" * 26
def _reset_mint():
global _MINT
_MINT = itertools.count(1)
_uuid.uuid4 = lambda: _DetUUID(next(_MINT)) # type: ignore[assignment]
import rune.construction_interface as ci # noqa: E402
# write_mini_doc redirect (source-tree default → per-case dir)
import rune.construction_projection as _cp_mod # noqa: E402
_orig_mini_doc = _cp_mod.write_mini_doc
def _hermetic_mini_doc(scaffold, contract_id, out_dir=None):
case = os.environ.get("CI_ACTIVE_CASE", "mini-doc")
out = Path(CI_FREEZE_HOME) / "cases" / case / "docs"
return _orig_mini_doc(scaffold, contract_id, out_dir=out)
_cp_mod.write_mini_doc = _hermetic_mini_doc
# Originals captured BEFORE the seam bindings below shadow the module
# globals the oracle itself calls internally (envelope assembly etc.) —
# the wrappers must call the captured originals, never the shadowed name.
_ORIG = {name: getattr(ci, name) for name in (
"_envelope", "_to_stage_check", "_guard_specs_from_obligations",
"t_layout", "_obligations_projection", "_obligation_scope_files",
"main")}
FIXTURE_ENTITIES = [
{"id": "capability:exporter", "kind": "capability", "identities": {},
"maturity": "WIRED", "evidence_refs": ["f"], "relationships": [],
"observations": []},
{"id": "contract:csv_export", "kind": "contract", "identities": {},
"maturity": "SEED", "evidence_refs": ["f"], "relationships": [],
"observations": []},
]
FIXTURE_RELATIONSHIPS = [
{"type": "implements", "from": "capability:exporter",
"to": "contract:csv_export", "evidence_refs": ["f"], "observations": []},
]
def _case_dir(case: str) -> Path:
os.environ["CI_ACTIVE_CASE"] = case
d = Path(CI_FREEZE_HOME) / "cases" / case
shutil.rmtree(d, ignore_errors=True)
(d / "proj" / ".rune").mkdir(parents=True, exist_ok=True)
return d
def _iface(case: str, project_id: str = "gap-ci-proj"):
_reset_mint()
d = _case_dir(case)
store = str(d / "proj" / ".rune" / "scaffold")
iface = ci.ConstructionInterface(
builder="factory-gap-ci", project_store=store, project_id=project_id)
iface.scaffold.init(purpose="fixture")
doc = iface.scaffold.load()
for e in FIXTURE_ENTITIES:
doc["entities"].append(json.loads(json.dumps(e)))
for r in FIXTURE_RELATIONSHIPS:
doc["relationships"].append(json.loads(json.dumps(r)))
iface.scaffold.save(doc)
return iface
def _iface_with_doc(case: str, doc: dict, project_id: str = "gap-ci-proj"):
"""Fresh hermetic store with `doc` injected — the seam entry point."""
_reset_mint()
d = _case_dir(case)
store = str(d / "proj" / ".rune" / "scaffold")
iface = ci.ConstructionInterface(
builder="factory-gap-ci", project_store=store, project_id=project_id)
iface.scaffold.init(purpose="fixture")
iface.scaffold.save(json.loads(json.dumps(doc)))
return iface
def build_doc() -> dict:
"""The fixture authority document (pure dict, no store IO)."""
doc = ss.new_doc()
for e in FIXTURE_ENTITIES:
doc["entities"].append(json.loads(json.dumps(e)))
for r in FIXTURE_RELATIONSHIPS:
doc["relationships"].append(json.loads(json.dumps(r)))
return doc
def submit_doc(case: str, doc: dict, objective: str = "persistent project export",
**kw) -> dict:
"""Builder helper (NOT a frozen fn): run oracle submit_need over a
hermetic store seeded with `doc`; return the updated doc."""
payload = dict(
constraints={"contracts": ["contract:csv_export"]},
affected_surface=[],
evidence_refs=["x"],
approval_context="covered by fixture authority",
)
payload.update(kw)
iface = _iface_with_doc(case, doc)
r = iface.submit_need(objective, **payload)
assert r["success"] or not payload.get("generate", True), r
return iface.scaffold.load()
def walk_doc(case: str, doc: dict, objective: str = "persistent project export",
phases=("approved", "building", "verifying"), **kw) -> dict:
"""Builder helper: submit + walk the legal ladder in ONE process."""
iface = _iface_with_doc(case, doc)
payload = dict(
constraints={"contracts": ["contract:csv_export"]},
affected_surface=[],
evidence_refs=["x"],
approval_context="covered by fixture authority",
)
payload.update(kw)
r = iface.submit_need(objective, **payload)
assert r["success"], r
cid = r["ids"]["contract_id"]
for ph in phases:
t = iface.transition(cid, ph)
assert t["success"], t
return iface.scaffold.load()
def contract_of(doc: dict, contract_id: str):
for e in doc.get("entities", []):
if e.get("kind") == "construction_contract" and \
e.get("id") == f"contract:{contract_id}":
return e
return None
def build_ledger_caps() -> dict:
"""A valid capability-ledger doc via the ledger's own define API
(builder helper; frozen clock keeps it deterministic)."""
from rune.capability_ledger import CapabilityLedger
ledger = CapabilityLedger(Path(CI_FREEZE_HOME) / "cases" / "b-ledger")
ledger.define(capability_id="capability:exporter", name="exporter",
purpose="export fixtures", owning_project="gap-ci-proj",
component="exporter", source_locations=["src/export.py"])
return json.loads(json.dumps(ledger.doc))
def _discover_doc(contract_id: str, observed_at: str, project: str) -> dict:
"""A minimal store doc carrying ONE construction contract (builder
helper for the discover_active_contract seam)."""
doc = ss.new_doc()
ss.add_entity(doc, id=f"contract:{contract_id}",
kind="construction_contract",
identities={"project": project, "scope": [],
"affected_entities": []},
evidence_refs=["f"])
doc["entities"][-1]["observed_at"] = observed_at
return doc
def _submitted_cid(doc: dict) -> str:
for e in doc.get("entities", []):
if e.get("kind") == "construction_contract":
return e["id"].split(":", 1)[1]
raise AssertionError("no contract in doc")
# ── pure module-level seams ─────────────────────────────────────────────────
def _envelope(operation, *, success, ids=None, state=None, evidence=None,
errors=None, warnings=None, extra=None):
return _ORIG["_envelope"](operation, success=success, ids=ids, state=state,
evidence=evidence, errors=errors,
warnings=warnings, **(extra or {}))
def _to_stage_check(spec):
from rune import stage_guards as _sg
c = _sg.StageCheck(
name=str(spec.get("name", "guard")),
kind=str(spec.get("kind", "assert")),
argv=spec.get("argv"),
output_pattern=str(spec.get("output_pattern", "")),
path=str(spec.get("path", "")),
check=spec.get("check"),
description=str(spec.get("description", "")),
)
return {"name": c.name, "kind": c.kind, "argv": c.argv,
"output_pattern": c.output_pattern, "path": c.path,
"check": c.check.__name__ if callable(c.check) else None,
"description": c.description}
def _guard_specs_from_obligations(c):
return _ORIG["_guard_specs_from_obligations"](c)
def t_layout(ext):
return _ORIG["t_layout"](ext)
def _obligations_projection(c):
return _ORIG["_obligations_projection"](c)
def _obligation_scope_files(obligations):
return _ORIG["_obligation_scope_files"](obligations)
def main(case, argv):
"""The JSON CLI transport over the hermetic rune-internal store."""
_reset_mint()
d = _case_dir(case)
(d / "store" / "scaffold").mkdir(parents=True, exist_ok=True)
prev = os.getcwd()
os.chdir(d)
try:
inner = ci.ConstructionInterface(builder="factory-gap-ci")
inner.scaffold.init(purpose="fixture")
return _ORIG["main"](list(argv))
finally:
os.chdir(prev)
# ── authority access ─────────────────────────────────────────────────────────
def _authorities(scaffold_doc, spine_doc=None, mode="external_project"):
"""The oracle's authority assembly given the loaded docs (doc-carrying:
the loads stay with the caller)."""
auth = {"project_scaffold": scaffold_doc}
if mode == "rune_internal" and spine_doc is not None:
auth["rune_spine"] = spine_doc
return auth
def _record_session(doc, operation, detail, builder="factory-gap-ci",
mode="external_project", now=None):
iface = _iface("rec-session")
d = json.loads(json.dumps(doc))
iface._record_session(d, operation, detail)
return d
# ── classify / context / capabilities ───────────────────────────────────────
def classify(decision, builder="factory-gap-ci"):
"""Envelope assembly over the canonical classifier's result (the
classifier itself is construction_mode's port surface)."""
iface = _iface("classify")
d = dict(decision)
env = _ORIG["_envelope"]("classify", success=d.get("mode") != "UNKNOWN",
state={**d, "builder": builder})
return env
def construction_context(case, doc, contract_id=None,
builder="factory-gap-ci", project="gap-ci-proj"):
iface = _iface_with_doc(case, doc, project_id=project)
return iface.construction_context(contract_id)
def capabilities(mode="external_project", project="gap-ci-proj"):
iface = _iface("caps")
iface.mode = mode
iface.scaffold.project_id = project
return iface.capabilities()
# ── submit_need ──────────────────────────────────────────────────────────────
def submit_need(case, doc, objective, origin="builder", constraints=None,
affected_surface=None, evidence_refs=None,
approval_context="", task_ref="", generate=True,
contract_id=None):
iface = _iface_with_doc(case, doc)
kw = dict(origin=origin, generate=generate)
if constraints is not None:
kw["constraints"] = constraints
if affected_surface is not None:
kw["affected_surface"] = affected_surface
if evidence_refs is not None:
kw["evidence_refs"] = evidence_refs
kw["approval_context"] = approval_context
kw["task_ref"] = task_ref
if contract_id is not None:
kw["contract_id"] = contract_id
return iface.submit_need(objective, **kw)
# ── read projections + evidence + verification ──────────────────────────────
def inspect_decision(case, doc, contract_id):
iface = _iface_with_doc(case, doc)
return iface.inspect_decision(contract_id)
def get_scope(case, doc, contract_id):
iface = _iface_with_doc(case, doc)
return iface.get_scope(contract_id)
def get_obligations(case, doc, contract_id):
iface = _iface_with_doc(case, doc)
return iface.get_obligations(contract_id)
def report_observation(case, doc, contract_id, observations):
iface = _iface_with_doc(case, doc)
return iface.report_observation(contract_id, observations)
def verify(case, doc, contract_id, changed_files=None,
layout_observations=None):
iface = _iface_with_doc(case, doc)
return iface.verify(contract_id, changed_files=changed_files,
layout_observations=layout_observations)
def transition(case, doc, contract_id, phase, evidence_ref="",
guard_checks=None):
iface = _iface_with_doc(case, doc)
return iface.transition(contract_id, phase, evidence_ref=evidence_ref,
guard_checks=guard_checks)
def close(case, doc, contract_id, observed_evidence, scope_files=None):
"""`doc` must already carry the contract at verifying (walk_doc)."""
iface = _iface_with_doc(case, doc)
return iface.close(contract_id, observed_evidence=observed_evidence,
scope_files=scope_files)
def _programme_continuation(case, doc, contract_id, programmes=None):
"""programmes: list of programme DOCS (generator-built via the pc API);
the seam persists them into the case's programme area, runs the oracle,
and returns the continuation plus the persisted-after docs."""
from rune import programme_continuation as pc
iface = _iface_with_doc(case, doc)
base = Path(iface.scaffold.current).parent / pc.DEFAULT_PROGRAMME_AREA
if programmes is not None:
base.mkdir(parents=True, exist_ok=True)
for pd in programmes:
prog = pc.Programme(
programme_id=pd["programme_id"], objective=pd["objective"],
stages=[pc.ProgrammeStage(**s) for s in pd["stages"]])
prog.save(base)
out = iface._programme_continuation(contract_id)
after = None
if base.is_dir():
after = []
for f in sorted(base.glob("*.json")):
after.append(json.loads(f.read_text()))
return {"continuation": out, "programmes_after": after}
def _auto_material_change(case, doc, scope_files, capabilities=None):
"""capabilities: the ledger DOC (kernel input); the seam persists it as
the project rune_model ledger, runs the oracle, and returns the oracle
result plus the ledger doc after the oracle's material_change/save."""
from rune.capability_ledger import CapabilityLedger
iface = _iface_with_doc(case, doc)
if capabilities is not None:
ledger_dir = Path(iface.scaffold.store_dir).parent / "rune_model"
ledger_dir.mkdir(parents=True, exist_ok=True)
(ledger_dir / "capability_ledger.json").write_text(
json.dumps(capabilities))
out = iface._auto_material_change(
list(scope_files), note="contract closed (material change)")
after = None
led = Path(iface.scaffold.store_dir).parent / "rune_model" / \
"capability_ledger.json"
if led.is_file():
after = json.loads(led.read_text())
return {"result": out, "ledger_after": after}
def discover_active_contract(case, candidates, root_name="store"):
"""candidates: [{project: str|null, doc: {...}}] — the seam persists
each doc at projects/<p>/current.json (or the root current.json) inside
the case dir and runs the oracle over that store root."""
d = _case_dir(case)
for cand in candidates:
proj, doc = cand["project"], cand["doc"]
if proj is None:
(d / "current.json").write_text(json.dumps(doc))
else:
p = d / "projects" / proj
p.mkdir(parents=True, exist_ok=True)
(p / "current.json").write_text(json.dumps(doc))
return ci.ConstructionInterface.discover_active_contract(str(d))
def resume(case, doc, contract_id):
iface = _iface_with_doc(case, doc)
return iface.resume(contract_id)
def amend_scope(case, doc, contract_id, add_paths, reason, authority="user"):
iface = _iface_with_doc(case, doc)
env = iface.amend_scope(contract_id, add_paths, reason=reason,
authority=authority)
return {"envelope": env,
"contract_after": contract_of(iface.scaffold.load(), contract_id)}
def ladder_walk(case, objective="persistent project export"):
"""The FULL ladder in ONE process (the shared-store clobber law):
submit_need → approved → building → verifying → close, with the read
projections riding the walk."""
iface = _iface(case)
out = []
r = iface.submit_need(
objective,
constraints={"contracts": ["contract:csv_export"]},
affected_surface=[], evidence_refs=["x"],
approval_context="covered by fixture authority")
out.append({"step": "submit_need", "envelope": r})
if not r["success"]:
return out
cid = r["ids"]["contract_id"]
out.append({"step": "inspect_decision", "envelope": iface.inspect_decision(cid)})
out.append({"step": "get_scope", "envelope": iface.get_scope(cid)})
out.append({"step": "verify", "envelope": iface.verify(cid)})
for ph in ("approved", "building", "verifying"):
t = iface.transition(cid, ph)
out.append({"step": f"transition:{ph}", "envelope": t})
if not t["success"]:
return out
c = iface.close(cid, observed_evidence=[
{"kind": "port_evidence", "detail": "gap closed with proof"}])
out.append({"step": "close", "envelope": c})
return out
# Bind the seams onto the oracle module so the shared harness's
# getattr(module, fn) resolves every frozen fn name.
ci.ladder_walk = ladder_walk
ci.submit_need = submit_need
ci.classify = classify
ci.construction_context = construction_context
ci.capabilities = capabilities
ci.inspect_decision = inspect_decision
ci.get_scope = get_scope
ci.get_obligations = get_obligations
ci.report_observation = report_observation
ci.verify = verify
ci.transition = transition
ci.close = close
ci._programme_continuation = _programme_continuation
ci._auto_material_change = _auto_material_change
ci.discover_active_contract = discover_active_contract
ci.resume = resume
ci.amend_scope = amend_scope
ci._authorities = _authorities
ci._record_session = _record_session
ci.t_layout = t_layout
ci._obligations_projection = _obligations_projection
ci._obligation_scope_files = _obligation_scope_files
ci._guard_specs_from_obligations = _guard_specs_from_obligations
ci.ci_to_stage_check = _to_stage_check
ci._envelope = _envelope
ci.main = main
# NOTE: ci._to_stage_check is deliberately NOT rebound — the oracle's
# transition() calls the module-global _to_stage_check internally and needs
# real StageCheck objects for the readiness canary. The rig's JSON-safe
# _to_stage_check is exposed as the ci_to_stage_check seam instead (same
# logic, JSON-safe projection).
# ci._obligations_projection IS rebound to the identity wrapper above
# (pure dict in → dict out through the captured original) — internal
# oracle calls with live contract dicts pass through unharmed.
+303
View File
@@ -0,0 +1,303 @@
#!/usr/bin/env python3
"""Freeze the dichotomy-wave gap truth for src/rune/construction_interface
through the SHARED harness (scripts/freeze_truth.py — one law).
The oracle is a stateful facade (ConstructionInterface over a Scaffold
store) with clock/uuid nondeterminism, so this generator runs under the
freeze rig (scripts/ci_freeze_seams/sitecustomize.py):
pinned RUNE_*_CORE=python floors, frozen clock, deterministic uuid mint,
hermetic per-case stores (deterministic per-case dirs — envelopes that
carry paths, like mini_doc, replay byte-identically), and DOC-CARRYING
seam bindings the shared harness can getattr: the Rust kernel is a pure
doc-in/doc-out membrane, so every stateful case carries the full input
doc in its args and replays in the battery with no store.
Run (from the repo root):
rm -rf /tmp/ci_gap_freeze_home
CI_FREEZE_HOME=/tmp/ci_gap_freeze_home \
PYTHONPATH=scripts/ci_freeze_seams:src \
python3 scripts/gen_construction_interface_truth.py
Verify (byte-identical, same rig):
CI_FREEZE_HOME=/tmp/ci_gap_freeze_home \
PYTHONPATH=scripts/ci_freeze_seams:src \
python3 scripts/freeze_truth.py --verify \
src/rust/construction_interface/frozen_ci_truth.json \
--module rune.construction_interface
Every public function and branch is exercised. The obligation-id seam
(_obligations_projection carries process-randomized str hash) lives in
the SEPARATE frozen_ci_truth_hash.json battery (captured + verified with
PYTHONHASHSEED=0) and is SHAPE-pinned downstream — never value-frozen
here.
"""
import os
import subprocess
import sys
from pathlib import Path
REPO = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(REPO / "scripts"))
from freeze_truth import freeze, write # noqa: E402
import sitecustomize as rig # noqa: E402
# ── deterministic input docs (builder helpers; NOT frozen fns) ──
DOC0 = rig.build_doc() # fixture authority, no contract
DOC_SUBMITTED = rig.submit_doc("b-submitted", rig.build_doc())
DOC_WALKED = rig.walk_doc("b-walked", rig.build_doc())
DOC_WITH_LEDGER_CAPS = rig.build_ledger_caps()
PROGRAMMES_BOUND = [{
"programme_id": "prog-gap-ci", "objective": "close the gap",
"stages": [
{"stage_id": rig._submitted_cid(DOC_SUBMITTED),
"objective": "the gap stage"},
{"stage_id": "next-stage", "objective": "the next stage"},
],
}]
GUARD_FAIL = [{"name": "always-fail", "kind": "command", "argv": ["false"]}]
GUARD_PASS = [{"name": "always-pass", "kind": "command", "argv": ["true"]}]
GUARD_ASSERT = [{"name": "truthy", "kind": "assert",
"description": "always true"}]
cases = [
# ── _envelope: success + failure shapes, extra keys ──
("_envelope", ("op",), {"success": True}),
("_envelope", ("op",), {"success": False,
"errors": [{"error": "X"}],
"warnings": [{"warning": "W"}],
"ids": {"a": "b"}, "state": {"k": 1},
"extra": {"extra_key": "extra"}}),
# ── _to_stage_check: full spec, defaults ──
("ci_to_stage_check", ({"name": "g", "kind": "command",
"argv": ["true"], "output_pattern": "ok",
"path": "/x", "description": "d"},), {}),
("ci_to_stage_check", ({},), {}),
# ── _guard_specs_from_obligations: guard rows, junk, bad JSON ──
("_guard_specs_from_obligations",
({"identities": {"verification_obligations": [
'guard:{"name": "g1", "kind": "command", "argv": ["true"]}',
"free-text obligation",
"guard:not-json{{{",
'["not", "a", "dict"]',
'guard:{"name": "g2"}',
]}},), {}),
("_guard_specs_from_obligations", ({},), {}),
# ── t_layout: present + absent ──
("t_layout", ({"decision_trace": {"layout_obligations": [
{"component": "c", "obligation": "o"}]}},), {}),
("t_layout", ({},), {}),
# ── _obligations_projection: synthetic hash-free fixtures (empty,
# pattern-only, layout-only — the contract/structural id branches
# carry process-randomized hash → hash battery) ──
("_obligations_projection", ({},), {}),
("_obligations_projection",
({"ext": {"decision_trace": {"obligations": {"pattern": [
{"id": "pat-1", "pattern": "seed_loop", "revision": 3,
"obligation": "keep the seed loop closed", "verdict": "SATISFIED",
"reason": "evidence row 1", "pattern_applicability": "always"}]}}}},),
{}),
("_obligations_projection",
({"ext": {"decision_trace": {"layout_obligations": [
{"component": "ui:header", "obligation": "stay in region top",
"verification": "adapter observation"}]}}},), {}),
# ── _obligation_scope_files: passthrough stub ──
("_obligation_scope_files", ([{"id": "x"}],), {}),
("_obligation_scope_files", ([],), {}),
# ── _authorities: external mode, rune-internal w/ + w/o spine ──
("_authorities", (rig.build_doc(),), {}),
("_authorities", (rig.build_doc(), {"schema_version": 1,
"entities": []}),
{"mode": "rune_internal"}),
("_authorities", (rig.build_doc(), None), {"mode": "rune_internal"}),
# ── _record_session: append + long-detail truncation ──
("_record_session", (rig.build_doc(), "submit_need", "short detail"), {}),
("_record_session", (rig.build_doc(), "close", "d" * 500), {}),
# ── classify: envelope assembly over classifier results ──
("classify", ({"mode": "UNKNOWN", "evidence": ["probe: which surface?"],
"probe_questions": ["q1"]},), {}),
("classify", ({"mode": "EXISTING_PROJECT_CHANGE", "evidence": ["e1"],
"probe_questions": []},), {}),
("classify", ({"mode": "LOCAL_CHANGE"},),
{"builder": "other-builder"}),
# ── construction_context: none active, explicit unknown id (the
# ACTIVE projection carries obligation ids → hash battery) ──
("construction_context", ("cc-none", rig.build_doc()), {}),
("construction_context", ("cc-unknown", DOC_SUBMITTED, "NOPE"), {}),
# ── capabilities: the full discovery envelope, both modes ──
("capabilities", (), {"mode": "external_project"}),
("capabilities", (), {"mode": "rune_internal",
"project": "issue161-harness"}),
# ── submit_need: happy, empty objective, MATERIAL_GAP, approval,
# no-generate, custom contract id, task_ref ──
("submit_need", ("sn-ok", rig.build_doc(), "persistent project export"), {}),
("submit_need", ("sn-empty", rig.build_doc(), ""), {}),
("submit_need", ("sn-gap", rig.build_doc(),
"change the exporter widget colors"),
{"affected_surface": ["capability:exporter"], "constraints": {},
"evidence_refs": ["x"]}),
("submit_need", ("sn-appr", rig.build_doc(),
"start a brand new repository for billing"),
{"constraints": {"new_repository": True}, "evidence_refs": ["x"]}),
("submit_need", ("sn-nogen", rig.build_doc(), "persistent project export"),
{"generate": False}),
("submit_need", ("sn-cid", rig.build_doc(), "persistent project export"),
{"contract_id": "MY-CID"}),
("submit_need", ("sn-task", rig.build_doc(), "persistent project export"),
{"task_ref": "task-1"}),
# ── inspect_decision: ok + unknown ──
("inspect_decision", ("in-ok", DOC_SUBMITTED,
rig._submitted_cid(DOC_SUBMITTED)), {}),
("inspect_decision", ("in-no", DOC_SUBMITTED, "NOPE"), {}),
# ── get_scope: ok + unknown ──
("get_scope", ("gs-ok", DOC_SUBMITTED,
rig._submitted_cid(DOC_SUBMITTED)), {}),
("get_scope", ("gs-no", DOC_SUBMITTED, "NOPE"), {}),
# ── get_obligations: unknown (ok carries hash ids → hash battery) ──
("get_obligations", ("go-no", DOC_SUBMITTED, "NOPE"), {}),
# ── report_observation: ok, bare-dict, missing entity, unknown ──
("report_observation", ("ro-ok", DOC_SUBMITTED,
rig._submitted_cid(DOC_SUBMITTED),
[{"entity": "contract:csv_export",
"detail": "observed while building"}]), {}),
("report_observation", ("ro-bare", DOC_SUBMITTED,
rig._submitted_cid(DOC_SUBMITTED),
{"entity": "x"}), {}),
("report_observation", ("ro-noent", DOC_SUBMITTED,
rig._submitted_cid(DOC_SUBMITTED),
[{"detail": "x"}]), {}),
("report_observation", ("ro-no", DOC_SUBMITTED, "NOPE",
[{"entity": "x"}]), {}),
# ── verify: unknown, scope unknown/green/red, layout PASS/FAIL/UNKNOWN,
# walked-contract contract-quality projection ──
("verify", ("vf-no", DOC_SUBMITTED, "NOPE"), {}),
("verify", ("vf-base", DOC_SUBMITTED,
rig._submitted_cid(DOC_SUBMITTED)), {}),
("verify", ("vf-green", DOC_SUBMITTED, rig._submitted_cid(DOC_SUBMITTED)),
{"changed_files": ["src/allowed.py"]}),
("verify", ("vf-red", DOC_SUBMITTED, rig._submitted_cid(DOC_SUBMITTED)),
{"changed_files": ["src/evil.py"]}),
("verify", ("vf-pass", DOC_SUBMITTED, rig._submitted_cid(DOC_SUBMITTED)),
{"layout_observations": {"components": []}}),
("verify", ("vf-fail", DOC_SUBMITTED, rig._submitted_cid(DOC_SUBMITTED)),
{"layout_observations": {"header": {"region": "bottom"}}}),
("verify", ("vf-unk", DOC_SUBMITTED, rig._submitted_cid(DOC_SUBMITTED)),
{"layout_observations": {"header": {"region": "top"}}}),
# ── transition: happy edge, illegal edge, unknown, guard block/pass ──
("transition", ("tr-appr", DOC_SUBMITTED,
rig._submitted_cid(DOC_SUBMITTED), "approved"), {}),
("transition", ("tr-ill", DOC_SUBMITTED,
rig._submitted_cid(DOC_SUBMITTED), "closed"), {}),
("transition", ("tr-no", DOC_SUBMITTED, "NOPE", "approved"), {}),
("transition", ("tr-block", DOC_SUBMITTED,
rig._submitted_cid(DOC_SUBMITTED), "approved"),
{"guard_checks": GUARD_FAIL}),
("transition", ("tr-pass", DOC_SUBMITTED,
rig._submitted_cid(DOC_SUBMITTED), "approved"),
{"guard_checks": GUARD_PASS}),
("transition", ("tr-assert", DOC_SUBMITTED,
rig._submitted_cid(DOC_SUBMITTED), "approved"),
{"guard_checks": GUARD_ASSERT}),
# ── close: full ladder doc, unknown, custom scope files ──
("close", ("cl-ok", DOC_WALKED, rig._submitted_cid(DOC_WALKED),
[{"kind": "port_evidence", "detail": "gap closed"}]), {}),
("close", ("cl-scope", DOC_WALKED, rig._submitted_cid(DOC_WALKED),
[{"kind": "port_evidence", "detail": "gap closed"}]),
{"scope_files": ["src/export.py"]}),
("close", ("cl-no", DOC_WALKED, "NOPE", [{"entity": "x"}]), {}),
# ── _programme_continuation: bound, unbound stage, no programme dir ──
("_programme_continuation",
("pc-hit", DOC_SUBMITTED, rig._submitted_cid(DOC_SUBMITTED)),
{"programmes": PROGRAMMES_BOUND}),
("_programme_continuation",
("pc-miss", DOC_SUBMITTED, "other-stage"),
{"programmes": PROGRAMMES_BOUND}),
("_programme_continuation",
("pc-none", DOC_SUBMITTED, rig._submitted_cid(DOC_SUBMITTED)), {}),
# ── _auto_material_change: hit, miss, no ledger ──
("_auto_material_change",
("mc-hit", DOC_SUBMITTED, ["src/export.py"]),
{"capabilities": DOC_WITH_LEDGER_CAPS}),
("_auto_material_change",
("mc-miss", DOC_SUBMITTED, ["src/zzz.py"]),
{"capabilities": DOC_WITH_LEDGER_CAPS}),
("_auto_material_change",
("mc-nol", DOC_SUBMITTED, ["src/export.py"]), {}),
# ── discover_active_contract: seeded store, empty store ──
("discover_active_contract", ("dc-seed",
[{"project": "alpha", "doc": rig._discover_doc("old", "2026-10-01T00:00:00Z", "alpha")},
{"project": None, "doc": rig._discover_doc("main-latest", "2026-10-03T00:00:00Z", "gamma")}]),
{}),
("discover_active_contract", ("dc-empty", []), {}),
# ── resume: unknown (ok carries hash ids → hash battery) ──
("resume", ("rs-no", DOC_SUBMITTED, "NOPE"), {}),
# ── amend_scope: ok, non-user, unknown ──
("amend_scope", ("am-ok", DOC_SUBMITTED,
rig._submitted_cid(DOC_SUBMITTED), ["src/a.py"]),
{"reason": "user asked"}),
("amend_scope", ("am-nouser", DOC_SUBMITTED,
rig._submitted_cid(DOC_SUBMITTED), ["src/a.py"]),
{"reason": "x", "authority": "builder"}),
("amend_scope", ("am-no", DOC_SUBMITTED, "NOPE", ["a"]),
{"reason": "x"}),
# ── main: every CLI command ──
("main", ("m-help",), {"argv": ["--help"]}),
("main", ("m-unk",), {"argv": ["frobnicate"]}),
("main", ("m-caps",), {"argv": ["capabilities"]}),
("main", ("m-cls",), {"argv": ["classify",
'{"objective": "fix typo"}']}),
("main", ("m-ctx",), {"argv": ["context"]}),
("main", ("m-sub",), {"argv": ["submit-need",
'{"objective": "x"}']}),
("main", ("m-dec",), {"argv": ["decision", "NOPE"]}),
("main", ("m-scope",), {"argv": ["scope", "NOPE"]}),
("main", ("m-obl",), {"argv": ["obligations", "NOPE"]}),
("main", ("m-obs",), {"argv": ["observe", "NOPE", "[]"]}),
("main", ("m-ver",), {"argv": ["verify", "NOPE"]}),
("main", ("m-tr",), {"argv": ["transition", "NOPE", "approved"]}),
("main", ("m-close",), {"argv": ["close", "NOPE", "[]"]}),
("main", ("m-res",), {"argv": ["resume", "NOPE"]}),
# ── the FULL ladder in ONE process (shared-store clobber law) ──
("ladder_walk", ("walk1",), {}),
("ladder_walk", ("walk2", "start a brand new repository for billing"), {}),
]
truth = freeze("rune.construction_interface", cases,
oracle_module="rune.construction_interface")
write(truth, REPO / "src" / "rust" / "construction_interface"
/ "frozen_ci_truth.json")
print(f"froze {len(truth)} cases")
# ── §2 hash battery: every envelope carrying obligation ids. Captured +
# verified with PYTHONHASHSEED=0 (set at INTERPRETER start — the seed
# cannot be changed mid-process, so the main run re-execs itself for
# this part); the Rust battery SHAPE-pins these ids (prefix before ":",
# stable description/kind/provenance/status) — never value-freezes the
# randomized suffix. ──
HASH_CASES = [
("_obligations_projection", (rig.contract_of(DOC_SUBMITTED,
rig._submitted_cid(DOC_SUBMITTED)),), {}),
("construction_context", ("hc-active", DOC_SUBMITTED), {}),
("construction_context", ("hc-explicit", DOC_SUBMITTED,
rig._submitted_cid(DOC_SUBMITTED)), {}),
("get_obligations", ("ho-ok", DOC_SUBMITTED,
rig._submitted_cid(DOC_SUBMITTED)), {}),
("resume", ("hr-ok", DOC_SUBMITTED,
rig._submitted_cid(DOC_SUBMITTED)), {}),
]
if os.environ.get("CI_GEN_HASH_ONLY") == "1":
assert os.environ.get("PYTHONHASHSEED") == "0", \
"hash battery requires PYTHONHASHSEED=0 at interpreter start"
hash_truth = freeze("rune.construction_interface", HASH_CASES,
oracle_module="rune.construction_interface")
write(hash_truth, REPO / "src" / "rust" / "construction_interface"
/ "frozen_ci_truth_hash.json")
print(f"froze {len(hash_truth)} hash cases (PYTHONHASHSEED=0)")
else:
env = dict(os.environ, PYTHONHASHSEED="0", CI_GEN_HASH_ONLY="1",
PYTHONPATH="scripts/ci_freeze_seams:src")
subprocess.run([sys.executable, str(Path(__file__).resolve())],
check=True, env=env, cwd=str(REPO))
+101 -1
View File
@@ -3,19 +3,107 @@
version = 4
[[package]]
name = "construction_interface"
name = "capability_fit"
version = "0.1.0"
dependencies = [
"serde",
"serde_json",
]
[[package]]
name = "capability_ledger"
version = "0.1.0"
dependencies = [
"serde_json",
]
[[package]]
name = "capability_scoring"
version = "0.1.0"
[[package]]
name = "construction_decision"
version = "0.1.0"
dependencies = [
"capability_fit",
"capability_scoring",
"construction_mode",
"construction_pattern",
"construction_patterns_builtin",
"serde",
"serde_json",
"toolchain",
]
[[package]]
name = "construction_interface"
version = "0.1.0"
dependencies = [
"capability_ledger",
"construction_decision",
"construction_mode",
"layout_contract",
"scaffold_core",
"serde",
"serde_json",
]
[[package]]
name = "construction_mode"
version = "0.1.0"
dependencies = [
"serde_json",
]
[[package]]
name = "construction_pattern"
version = "0.1.0"
dependencies = [
"serde_json",
]
[[package]]
name = "construction_patterns_builtin"
version = "0.1.0"
dependencies = [
"capability_scoring",
]
[[package]]
name = "equivalent"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
[[package]]
name = "hashbrown"
version = "0.17.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a"
[[package]]
name = "indexmap"
version = "2.14.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cc4e190f5d26ca7051642629da2c52fc03bde85a03197c99408dcd291734c855"
dependencies = [
"equivalent",
"hashbrown",
]
[[package]]
name = "itoa"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
[[package]]
name = "layout_contract"
version = "0.1.0"
dependencies = [
"serde_json",
]
[[package]]
name = "memchr"
version = "2.8.3"
@@ -40,6 +128,10 @@ dependencies = [
"proc-macro2",
]
[[package]]
name = "scaffold_core"
version = "0.1.0"
[[package]]
name = "serde"
version = "1.0.229"
@@ -76,6 +168,7 @@ version = "1.0.151"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c841b55ecdae098c80dcae9cf767f6f8a0c2cdb3416bbef72181df4d0fe73f14"
dependencies = [
"indexmap",
"itoa",
"memchr",
"serde",
@@ -94,6 +187,13 @@ dependencies = [
"unicode-ident",
]
[[package]]
name = "toolchain"
version = "0.1.0"
dependencies = [
"serde_json",
]
[[package]]
name = "unicode-ident"
version = "1.0.26"
+6 -1
View File
@@ -8,4 +8,9 @@ crate-type = ["rlib", "cdylib", "staticlib"]
[dependencies]
serde = { version = "1", features = ["derive"] }
serde_json = "1"
serde_json = { version = "1", features = ["preserve_order"] }
construction_decision = { path = "../construction_decision" }
construction_mode = { path = "../construction_mode" }
capability_ledger = { path = "../capability_ledger" }
layout_contract = { path = "../layout_contract" }
scaffold_core = { path = "../scaffold_core" }
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,125 @@
//! frozen_battery — replay the FROZEN oracle truth VERBATIM
//! (frozen_ci_truth.json + frozen_ci_truth_hash.json, generated by
//! scripts/gen_construction_interface_truth.py against the LIVE Python
//! oracle through the scripts/ci_freeze_seams rig: pinned
//! RUNE_*_CORE=python floors, frozen clock, deterministic uuid mint,
//! hermetic per-case stores).
//!
//! The battery never imports the Python oracle — parity stays provable
//! after the strangler exits. The main corpus (85 cases) replays
//! byte-exact; the hash corpus (5 cases) replays SHAPE-pinned: Python's
//! str-hash obligation ids are process-randomized, so both sides are
//! normalized (kind:<digits> → kind:#) before compare — the memory_
//! consolidation hash-id law.
use crate::kernel;
use serde_json::Value;
const TRUTH: &str = include_str!("../frozen_ci_truth.json");
const HASH_TRUTH: &str = include_str!("../frozen_ci_truth_hash.json");
fn cases(text: &str) -> Vec<Value> {
serde_json::from_str(text).expect("frozen truth parses")
}
fn case_id(c: &Value) -> String {
c.get("args")
.and_then(|a| a.as_array())
.and_then(|a| a.first())
.and_then(|x| x.as_str())
.unwrap_or("?")
.to_string()
}
#[test]
fn frozen_truth_replays_verbatim() {
let mut n = 0;
for case in cases(TRUTH) {
let got = kernel::dispatch(&case);
assert_eq!(
got,
case.get("expected").cloned().unwrap_or(Value::Null),
"case {} ({}) diverged",
case.get("fn").and_then(|f| f.as_str()).unwrap_or("?"),
case_id(&case),
);
n += 1;
}
assert_eq!(n, 85, "the frozen corpus carries 85 cases");
}
#[test]
fn hash_truth_replays_shape_pinned() {
let mut n = 0;
for case in cases(HASH_TRUTH) {
let got = kernel::dispatch(&case);
let expected = case.get("expected").cloned().unwrap_or(Value::Null);
assert_eq!(
normalize_hash_ids(got),
normalize_hash_ids(expected),
"hash case {} ({}) diverged beyond the id shape",
case.get("fn").and_then(|f| f.as_str()).unwrap_or("?"),
case_id(&case),
);
n += 1;
}
assert_eq!(n, 5, "the hash corpus carries 5 cases");
}
/// kind:<up-to-8 digits> → kind:# — the obligation-id seam (Python
/// str-hash ids are process-randomized; the shape is the contract).
fn normalize_hash_ids(v: Value) -> Value {
match v {
Value::String(s) => {
if let Some((kind, digits)) = s.split_once(':') {
if matches!(
kind,
"contract" | "regression" | "verification" | "composition" | "acquisition"
) && !digits.is_empty()
&& digits.len() <= 8
&& digits.chars().all(|c| c.is_ascii_digit())
{
return Value::String(format!("{}:#", kind));
}
}
Value::String(s)
}
Value::Array(a) => Value::Array(a.into_iter().map(normalize_hash_ids).collect()),
Value::Object(o) => Value::Object(
o.into_iter()
.map(|(k, v)| (k, normalize_hash_ids(v)))
.collect(),
),
other => other,
}
}
#[test]
fn audit_missing_fns_are_real_ports() {
// the two audit-named gap fns exist as name-preserving kernels with
// exercised behavior (not stubs): main (CLI transport) and
// _programme_continuation (programme ladder completion).
let argv: Vec<String> = vec!["--help".to_string()];
let d = kernel::main_dispatch(&argv);
assert_eq!(d["exit"], serde_json::json!(0));
let progs = vec![serde_json::json!({
"programme_id": "p1", "objective": "o",
"stages": [
{"stage_id": "S1", "objective": "a", "state": "PENDING",
"depends_on": [], "priority": 0},
{"stage_id": "S2", "objective": "b", "state": "PENDING",
"depends_on": [], "priority": 0},
],
"fallbacks": [],
})];
let (cont, after) = kernel::programme_continuation(&progs, "S1", "2026-10-13T09:41:23Z");
assert_eq!(cont["stage_marked_complete"], serde_json::json!("S1"));
assert_eq!(cont["canary"]["verdict"], serde_json::json!("PROGRAMME_OPEN"));
assert_eq!(cont["canary"]["next_action"], serde_json::json!(["start", "S2"]));
assert_eq!(cont["canary"]["reason"], serde_json::json!("1/2 complete; 1 eligible — continue immediately"));
let after = after.as_array().unwrap();
assert_eq!(after[0]["stages"][0]["state"], serde_json::json!("COMPLETE"));
// save drops the in-memory result — the persisted shape is 5-key stages
assert!(after[0]["stages"][0].get("result").is_none());
assert_eq!(after[0]["saved_at"], serde_json::json!("2026-10-13T09:41:23Z"));
}
File diff suppressed because it is too large. Load diff
@@ -7,6 +7,18 @@
//! IllegalPhaseTransition legality map. The 16-edge matrix truth battery
//! lives in tests (8 legal, 8 illegal — verified against the Python
//! scaffold authority 2026-10-07).
//!
//! kernel.rs + projection.rs carry the gap-closing doc-in/doc-out kernels
//! (dichotomy wave #2479): the envelope-assembly and doc-mutation halves of
//! the ConstructionInterface methods whose inputs are fully carried by the
//! contract doc + caller args. Stores/clocks/guard subprocesses stay behind
//! the facade's injectable seams.
pub mod kernel;
pub mod projection;
#[cfg(test)]
mod frozen_battery;
/// The legal phase transitions. This map IS the scaffold authority's
/// twin — changing one side without the other fails the parity battery.
@@ -285,6 +297,32 @@ fn to_cstring(s: String) -> *mut u8 {
ptr
}
/// ci_replay(case_json) -> result_json — the FFI replay surface over the
/// frozen truth rows ({fn, args, kwargs}); the parity script and the Rust
/// battery share kernel::dispatch, so both prove the same computation.
#[allow(clippy::not_unsafe_ptr_arg_deref)] // pinned: extern "C" ABI — the Survivor loader requires this signature
#[no_mangle]
pub extern "C" fn ci_replay(arg: *const u8, arg_len: usize) -> *mut u8 {
unsafe {
let slice = std::slice::from_raw_parts(arg, arg_len);
let text = std::str::from_utf8(slice).unwrap_or("{}");
let case: serde_json::Value =
serde_json::from_str(text).unwrap_or(serde_json::json!({}));
let result = kernel::dispatch(&case);
to_cstring(result.to_string())
}
}
/// ci_free_string(ptr) — release a string returned by ci_replay.
#[no_mangle]
pub extern "C" fn ci_free_string(ptr: *mut u8) {
if !ptr.is_null() {
unsafe {
drop(std::ffi::CString::from_raw(ptr as *mut i8));
}
}
}
#[cfg(test)]
mod ffi_battery {
use super::*;
@@ -0,0 +1,297 @@
//! projection.rs — the pure projection helpers of the construction
//! interface (dichotomy wave #2479 gap port). Name-preserving 1:1 ports
//! of construction_interface.py's module-level helpers; the contract doc
//! travels across the boundary (kernel is pure, the facade owns stores).
use serde_json::{json, Map, Value};
/// Python `str()` over a JSON value (the common frozen-case shapes).
pub fn py_str(v: &Value) -> String {
match v {
Value::String(s) => s.clone(),
Value::Null => "None".to_string(),
Value::Bool(b) => (if *b { "True" } else { "False" }).to_string(),
Value::Number(n) => n.to_string(),
other => other.to_string(),
}
}
/// Python slice `s[:n]` semantics for a string.
pub fn py_truncate(s: &str, n: usize) -> String {
if s.chars().count() <= n {
s.to_string()
} else {
s.chars().take(n).collect()
}
}
/// `_to_stage_check(spec)` — one guard spec → the StageCheck wire dict
/// (name/kind defaults "guard"/"assert", path/output_pattern/description
/// default ""). The callable `check` rides as JSON null across the FFI.
pub fn to_stage_check(spec: &Value) -> Value {
let get = |k: &str, d: &str| -> String {
match spec.get(k) {
Some(v) if !v.is_null() => py_str(v),
_ => d.to_string(),
}
};
json!({
"name": get("name", "guard"),
"kind": get("kind", "assert"),
"argv": spec.get("argv").cloned().unwrap_or(Value::Null),
"output_pattern": get("output_pattern", ""),
"path": get("path", ""),
"check": spec.get("check").cloned().unwrap_or(Value::Null),
"description": get("description", ""),
})
}
/// `_guard_specs_from_obligations(c)` — the contract's guard declarations:
/// identities.verification_obligations rows shaped `guard:<json-spec>`.
/// JSON-decode failures and non-dict payloads are skipped — no invented
/// facts.
pub fn guard_specs_from_obligations(c: &Value) -> Vec<Value> {
let mut specs = Vec::new();
let Some(rows) = c
.get("identities")
.filter(|i| !i.is_null())
.and_then(|i| i.get("verification_obligations"))
.and_then(|v| v.as_array())
.map(|a| a.as_slice())
else {
return specs;
};
for item in rows {
let Some(text) = item.as_str() else {
continue;
};
if !text.starts_with("guard:") {
continue;
}
if let Ok(Value::Object(spec)) = serde_json::from_str::<Value>(&text[6..]) {
specs.push(Value::Object(spec));
}
}
specs
}
/// `t_layout(ext)` — layout obligations from a contract's decision trace.
pub fn t_layout(ext: &Value) -> Value {
ext.get("decision_trace")
.and_then(|t| t.get("layout_obligations"))
.cloned()
.unwrap_or_else(|| json!([]))
}
/// The obligation-id seam. Python mints
/// `f"{kind}:{abs(hash(str(item))) % 10**8}"` — str hash is
/// process-randomized (PYTHONHASHSEED), so the value is NOT portable; the
/// battery and parity check pin the SHAPE (`kind:` + up-to-8 digits),
/// mirroring the memory_consolidation hash-id law. The kernel computes a
/// deterministic stand-in id of the identical shape.
pub fn obligation_id(kind: &str, item: &str) -> String {
// FNV-1a 64 — deterministic stand-in, same shape as the Python hash id
let mut h: u64 = 0xcbf2_9ce4_8422_2325;
for b in item.as_bytes() {
h ^= u64::from(*b);
h = h.wrapping_mul(0x0000_0100_0000_01b3);
}
format!("{}:{}", kind, (h % 100_000_000))
}
/// `_obligations_projection(c)` — the builder-facing obligations list:
/// CONTRACT rows (identities.verification/regression_obligations),
/// STRUCTURAL rows (trace obligations regression/verification/composition/
/// acquisition), PATTERN rows and LAYOUT rows.
pub fn obligations_projection(c: &Value) -> Vec<Value> {
let mut out: Vec<Value> = Vec::new();
let ident = c.get("identities").cloned().unwrap_or(json!({}));
let t = c
.get("ext")
.and_then(|e| e.get("decision_trace"))
.cloned()
.unwrap_or(json!({}));
let obl = t.get("obligations").cloned().unwrap_or(json!({}));
for (field_name, prov) in [
("verification_obligations", "CONTRACT"),
("regression_obligations", "CONTRACT"),
] {
if let Some(items) = ident.get(field_name).and_then(|v| v.as_array()) {
for item in items {
let text = py_str(item);
out.push(json!({
"id": obligation_id(&prov.to_lowercase(), &text),
"description": py_truncate(&text, 200),
"provenance": prov,
"kind": field_name,
"status": "REQUIRED",
}));
}
}
}
for kind in ["regression", "verification", "composition", "acquisition"] {
if let Some(items) = obl.get(kind).and_then(|v| v.as_array()) {
for item in items {
let text = py_str(item);
out.push(json!({
"id": obligation_id(kind, &text),
"description": py_truncate(&text, 200),
"provenance": "STRUCTURAL",
"kind": kind,
"status": "REQUIRED",
}));
}
}
}
if let Some(patterns) = obl.get("pattern").and_then(|v| v.as_array()) {
for p in patterns {
out.push(json!({
"id": p.get("id").cloned().unwrap_or(Value::Null),
"description": p.get("obligation").cloned().unwrap_or(Value::Null),
"provenance": "PATTERN",
"pattern": format!(
"{}@r{}",
p.get("pattern").and_then(|x| x.as_str()).unwrap_or(""),
p.get("revision").and_then(|x| x.as_i64()).unwrap_or(0)
),
"pattern_applicability": p
.get("pattern_applicability").and_then(|x| x.as_str()).unwrap_or(""),
"evidence": p.get("reason").and_then(|x| x.as_str()).unwrap_or(""),
"status": p.get("verdict").and_then(|x| x.as_str()).unwrap_or("REQUIRED"),
}));
}
}
if let Some(los) = t.get("layout_obligations").and_then(|v| v.as_array()) {
for lo in los {
out.push(json!({
"id": format!(
"layout:{}",
lo.get("component").and_then(|x| x.as_str()).unwrap_or("")
),
"description": lo.get("obligation").cloned().unwrap_or(Value::Null),
"provenance": "LAYOUT_CONTRACT",
"status": "REQUIRED",
"verification": lo.get("verification").cloned().unwrap_or(Value::Null),
}));
}
}
out
}
/// `_obligation_scope_files(obligations)` — obligation ids referenced by
/// closure scope checks (stub twin of the Python stub).
pub fn obligation_scope_files(_obligations: &Value) -> Vec<Value> {
Vec::new()
}
/// `_match(cap, f)` — capability source-location matching (the nested
/// helper of _auto_material_change, literal name preserved).
pub fn _match(cap: &Value, f: &str) -> bool {
let Some(locs) = cap.get("source_locations").and_then(|v| v.as_array()) else {
return false;
};
for loc in locs {
if let Some(loc) = loc.as_str() {
if f == loc || f.ends_with(&format!("/{}", loc)) || loc.ends_with(&format!("/{}", f)) {
return true;
}
}
}
false
}
/// `_envelope(operation, ...)` — the stable §12 output envelope with the
/// `extra` merge.
#[allow(clippy::too_many_arguments)]
pub fn envelope(
operation: &str,
success: bool,
ids: Value,
state: Value,
evidence: Value,
errors: Value,
warnings: Value,
extra: Value,
) -> Value {
let mut env = Map::new();
env.insert("schema".into(), json!(1));
env.insert("operation".into(), json!(operation));
env.insert("success".into(), json!(success));
env.insert("ids".into(), if ids.is_null() { json!({}) } else { ids });
env.insert("state".into(), if state.is_null() { json!({}) } else { state });
env.insert(
"evidence".into(),
if evidence.is_null() { json!([]) } else { evidence },
);
env.insert("errors".into(), if errors.is_null() { json!([]) } else { errors });
env.insert(
"warnings".into(),
if warnings.is_null() { json!([]) } else { warnings },
);
if let Value::Object(extra_map) = extra {
for (k, v) in extra_map {
env.insert(k, v);
}
}
Value::Object(env)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn to_stage_check_defaults() {
let c = to_stage_check(&json!({}));
assert_eq!(c["name"], json!("guard"));
assert_eq!(c["kind"], json!("assert"));
assert_eq!(c["argv"], json!(null));
assert_eq!(c["description"], json!(""));
}
#[test]
fn guard_specs_skip_junk() {
let c = json!({"identities": {"verification_obligations": [
"guard:{\"name\": \"g1\"}", "free text", "guard:not-json{{",
"[1]", "guard:{\"name\": \"g2\"}"]}});
let specs = guard_specs_from_obligations(&c);
assert_eq!(specs.len(), 2);
assert_eq!(specs[0]["name"], json!("g1"));
assert!(guard_specs_from_obligations(&json!({})).is_empty());
}
#[test]
fn projection_kinds() {
let c = json!({
"identities": {"verification_obligations": ["v-ob"]},
"ext": {"decision_trace": {"obligations": {
"pattern": [{"id": "p1", "pattern": "seed", "revision": 2,
"obligation": "ob", "verdict": "SATISFIED",
"reason": "r"}]},
"layout_obligations": [{"component": "ui:h",
"obligation": "o",
"verification": "adapter"}]}}});
let out = obligations_projection(&c);
assert_eq!(out.len(), 3);
assert_eq!(out[0]["provenance"], json!("CONTRACT"));
assert_eq!(out[1]["provenance"], json!("PATTERN"));
assert_eq!(out[1]["pattern"], json!("seed@r2"));
assert_eq!(out[2]["id"], json!("layout:ui:h"));
// shape law: the contract row id is kind:<up-to-8 digits>
let id = out[0]["id"].as_str().unwrap();
let num = id.split(':').nth(1).unwrap();
assert!(num.len() <= 8 && num.chars().all(|c| c.is_ascii_digit()));
}
#[test]
fn source_location_matching() {
let cap = json!({"source_locations": ["src/export.py"]});
assert!(_match(&cap, "src/export.py"));
assert!(_match(&cap, "repo/src/export.py"));
// oracle: "src/export.py/sub".endswith("/src/export.py") is False —
// only exact, suffix-dir, and dir-suffix forms match
assert!(!_match(&cap, "src/export.py/sub"));
assert!(!_match(&cap, "src/other.py"));
}
}