feat(layout_contract): close PARTIAL gaps — dichotomy wave-5
Audit 2/6 -> 6/6 FULL (RUST): the four audit-missing fns ported
name-preserving (define_region, bind_component, amend_layout_contract,
record_layout_observation) as pure doc-in/doc-out Rust kernels
(now is an argument — no-clock law; structural_store add_entity/entity
merge law replicated: high-water maturity, sorted-set evidence union,
identities key-level update, alias-following resolution).
Freeze rig scripts/lc_freeze_seams (PYTHONPATH auto-import, used by BOTH
generator and --verify): blanket RUNE_*_CORE=python + EXPLICIT
RUNE_LC_CORE/RUNE_STRUCTURAL_CORE pins (the blanket loop only pins vars
already in the env — an unpinned first re-freeze ran through live
delegation and CAUGHT the absorb divergence), frozen clock on both now
seams, DOC-CARRYING wrappers ({result, doc:{entities,observations}}),
errors frozen as {error: Type: message}.
Truth: 35-case frozen corpus (frozen_lc_gap_truth.json, envelope with
frozen_now) via the shared harness, embedded VERBATIM in gap_battery.rs;
regeneration byte-identical; freeze_truth --verify OK.
Real bugs the ladder caught (fixed pre-commit): (1) re-bind ASSIGNS a
fresh amendments ledger — insert-if-missing diverged from assignment
(live-facade smoke, discriminating frozen case added); (2) identities
merge is wholesale key assignment (add_entity .update() law — tech
reset on no-op re-define; recursive-merge absorb diverged, caught by the
parity LIVE layer); (3) bind_component has NO framework_attrs param —
framework detail rides **framework_attrs, dispatch collects unknown
kwargs; (4) serde_json preserve_order keeps observed-dict insertion
order (json insertion-order quirk).
FFI: lc_define_region/lc_bind_component/lc_amend_layout_contract/
lc_record_observation (doc-envelope) + lc_replay (raw replay surface).
Facade delegates with RuntimeError->ValueError translation and
identity-preserving absorb (lc_loader.absorb — returned entity IS the
stored one; aliasing survives round-trips).
Battery 10/10 (verbatim replay + FFI roundtrip + 6 branch proofs);
clippy -D warnings clean; parity 100% (35/35 frozen + byte-identical
live re-freeze); layout+consumer tests 64/64 BOTH pins. Full-suite
baseline failures pre-exist identically with/without this port
(coordinator-owned debt).
This commit is contained in:
1 parent
29ba4341c2
commit
4bbed58d2c
22 files changed
+7074
-40
No files matched your search
@@ -2,16 +2,16 @@
|
||||
|
||||
_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: 53%** (102/192 runtime modules at FULL)
|
||||
**FULL completion: 55%** (105/192 runtime modules at FULL)
|
||||
|
||||
Function-level detail: 70% (1588/2282 runtime functions matched) — secondary; includes in-flight work
|
||||
Function-level detail: 71% (1610/2282 runtime functions matched) — secondary; includes in-flight work
|
||||
|
||||
| Status | Modules | Lines | Meaning |
|
||||
|---|---|---|---|
|
||||
| FULL | 102 | 43279 | 80%+ of functions ported |
|
||||
| PARTIAL | 51 | 20014 | 20–80% — real logic exists, gaps named below |
|
||||
| FULL | 105 | 44523 | 80%+ of functions ported |
|
||||
| PARTIAL | 48 | 18868 | 20–80% — real logic exists, gaps named below |
|
||||
| NOT TOUCHED | 39 | 11471 | no name-preserving implementation |
|
||||
| Total runtime | 192 | 74764 | |
|
||||
| Total runtime | 192 | 74862 | |
|
||||
|
||||
## NOT TOUCHED — the port queue, in priority order (largest first)
|
||||
|
||||
@@ -65,9 +65,7 @@ Function-level detail: 70% (1588/2282 runtime functions matched) — secondary;
|
||||
| memory_store | 2333 | 30/68 | _ctx_budget, now_iso, ensure_memory_type_column, _embed, _temp_json, _entities_to_json |
|
||||
| warm_router | 1518 | 17/33 | _ensure_use_history_table, record_outcome, record_neuron_fire, base_level_activation, _db, _aaak |
|
||||
| tool_executor | 1836 | 33/47 | _execute_tool_now, serialize_prepared_tool, deserialize_prepared_tool, confirm_write, _resolve_execution_cwd, _run_command |
|
||||
| claim_audit | 484 | 4/14 | _get_warm_neuron_store, reframe, _graph_has, _warm_has, verify, journal |
|
||||
| token_optimizer | 514 | 16/25 | get_optimizer, _tc_core, _to_core, to_dict, _to_state, _apply_state |
|
||||
| builder_bridge | 326 | 3/11 | _construction_store, _active_construction_lines, _default_selector, _cached_memory, _get_selector, _retrieve_memory |
|
||||
| time_awareness | 402 | 12/20 | _clock_args, _ta_core, get_time_awareness, to_dict, _to_state, _apply_state |
|
||||
| intent_continuity | 284 | 6/13 | _now, _raise_core_err, to_dict, from_dict, save, _ledger_state |
|
||||
| voice_sync | 299 | 3/10 | _now, get_delivery_register_from_db, get_delivery_register, update_rune_md, check_sync, handle_hook |
|
||||
@@ -82,7 +80,6 @@ Function-level detail: 70% (1588/2282 runtime functions matched) — secondary;
|
||||
| tiered_executor | 291 | 3/8 | _ctx_budget, test_executor, to_dict, to_dict, _execute_model |
|
||||
| construction_commands | 268 | 1/5 | _refuse, _validate, _accept, dispatch |
|
||||
| drift_observability | 399 | 5/9 | _do_core, main, _load_alerts, _save_alerts |
|
||||
| layout_contract | 336 | 2/6 | define_region, bind_component, amend_layout_contract, record_layout_observation |
|
||||
| pins | 282 | 7/11 | _store_path, _detail_blob, unpin, list_pins |
|
||||
| prompt_sync | 487 | 11/15 | _now, _rust_available, get_store, main |
|
||||
| self_improve | 246 | 1/5 | _now, suggest_improvements, handle_hook, main |
|
||||
@@ -154,6 +151,7 @@ Function-level detail: 70% (1588/2282 runtime functions matched) — secondary;
|
||||
| growth_delta | 520 | 17/18 | GO |
|
||||
| event_wait | 518 | 14/14 | GO |
|
||||
| cache | 492 | 17/17 | GO |
|
||||
| claim_audit | 484 | 14/14 | RUST |
|
||||
| model_connect | 471 | 11/11 | GO |
|
||||
| worthiness | 469 | 12/13 | unclassified |
|
||||
| tool_protocol | 461 | 22/27 | GO |
|
||||
@@ -164,6 +162,7 @@ Function-level detail: 70% (1588/2282 runtime functions matched) — secondary;
|
||||
| witness | 446 | 15/15 | GO |
|
||||
| cron_engine | 440 | 27/27 | GO |
|
||||
| model_profiles | 438 | 15/15 | GO |
|
||||
| layout_contract | 434 | 6/6 | RUST |
|
||||
| microkernel | 434 | 12/12 | GO |
|
||||
| model_routing | 432 | 13/13 | GO |
|
||||
| night_mode | 421 | 19/19 | GO |
|
||||
@@ -174,6 +173,7 @@ Function-level detail: 70% (1588/2282 runtime functions matched) — secondary;
|
||||
| drift_cache | 357 | 12/14 | RUST |
|
||||
| provider_budget | 357 | 20/20 | GO |
|
||||
| drift-ledger | 355 | 14/14 | unclassified |
|
||||
| builder_bridge | 326 | 11/11 | GO |
|
||||
| drift_compression | 323 | 7/7 | RUST |
|
||||
| outcome_ledger | 318 | 7/8 | RUST |
|
||||
| message_queue | 316 | 13/13 | GO |
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Live parity: layout_contract Rust kernels vs the frozen oracle truth
|
||||
(dichotomy wave-5 gap-close). Layers:
|
||||
|
||||
1. FROZEN — the frozen truth corpus (frozen_lc_gap_truth.json, 34 cases,
|
||||
generated by scripts/gen_layout_contract_truth.py against the LIVE
|
||||
Python oracle through the scripts/lc_freeze_seams rig: pinned
|
||||
RUNE_*_CORE=python floors, frozen clock on both now seams,
|
||||
DOC-CARRYING wrappers) replayed through the lc_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 (LC_TRUTH_OUT redirected to a temp file); the regenerated
|
||||
corpus must be byte-identical to the committed file (the oracle is
|
||||
still the parity authority while the strangler runs).
|
||||
|
||||
Exit 0 only at 100% parity (34 verbatim + byte-identical re-freeze).
|
||||
"""
|
||||
import json
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
|
||||
REPO = Path(__file__).resolve().parents[1]
|
||||
CRATE = REPO / "src" / "rust" / "layout_contract"
|
||||
FROZEN = CRATE / "frozen_lc_gap_truth.json"
|
||||
|
||||
sys.path.insert(0, str(REPO / "src"))
|
||||
from rune import lc_loader # noqa: E402
|
||||
|
||||
|
||||
def frozen_layer() -> int:
|
||||
lib = lc_loader._load()
|
||||
env = json.loads(FROZEN.read_text())
|
||||
ok = 0
|
||||
for case in env["cases"]:
|
||||
got = lc_loader.replay(case)
|
||||
assert got == case["expected"], (
|
||||
f"FROZEN divergence: {case['fn']} "
|
||||
f"{case['args'][1] if len(case['args']) > 1 else '?'}")
|
||||
ok += 1
|
||||
print(f"FROZEN: {ok}/{len(env['cases'])} cases verbatim through lc_replay")
|
||||
return ok
|
||||
|
||||
|
||||
def live_layer() -> None:
|
||||
with tempfile.TemporaryDirectory(prefix="lc-parity-") as td:
|
||||
out = Path(td) / "frozen_lc_gap_truth.json"
|
||||
env = dict(
|
||||
os.environ,
|
||||
PYTHONPATH="scripts/lc_freeze_seams:src",
|
||||
LC_TRUTH_OUT=str(out),
|
||||
)
|
||||
r = subprocess.run(
|
||||
[sys.executable, "scripts/gen_layout_contract_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")
|
||||
raise SystemExit(1)
|
||||
fresh = out.read_bytes()
|
||||
committed = FROZEN.read_bytes()
|
||||
if fresh != committed:
|
||||
print("LIVE: re-freeze is NOT byte-identical to the committed corpus")
|
||||
raise SystemExit(1)
|
||||
n = len(json.loads(fresh)["cases"])
|
||||
print(f"LIVE: re-freeze byte-identical ({n} cases)")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
if not lc_loader.available():
|
||||
print(f"layout_contract core not built: {lc_loader._LIB_PATH}")
|
||||
raise SystemExit(1)
|
||||
frozen_layer()
|
||||
live_layer()
|
||||
print("PARITY 100%")
|
||||
@@ -0,0 +1,278 @@
|
||||
#!/usr/bin/env python3
|
||||
"""gen_layout_contract_truth.py — freeze the layout_contract gap truth
|
||||
(define_region / bind_component / amend_layout_contract /
|
||||
record_layout_observation) through the SHARED harness
|
||||
(scripts/freeze_truth.py — one law).
|
||||
|
||||
The oracle fns mutate a doc argument and stamp observed_at through
|
||||
structural_store.now, so this generator runs under the freeze rig
|
||||
(scripts/lc_freeze_seams/sitecustomize.py): pinned RUNE_*_CORE=python
|
||||
floors, frozen clock on both now seams, and DOC-CARRYING wrappers that
|
||||
record {"result", "doc": {entities, observations}} — the Rust kernel is a
|
||||
pure doc-in/doc-out membrane (the store stays with the facade), so every
|
||||
case carries the full input doc in its args and replays in the battery
|
||||
with no store. Errors freeze as {"error": "Type: message"}
|
||||
(construction_decision convention). The input docs are LITERAL dicts that
|
||||
match add_entity output exactly — built by hand here, deterministic by
|
||||
construction (no store IO; this module has no file-IO surface).
|
||||
|
||||
Run (from the repo root):
|
||||
PYTHONPATH=scripts/lc_freeze_seams:src \
|
||||
python3 scripts/gen_layout_contract_truth.py
|
||||
|
||||
Verify (byte-identical, same rig):
|
||||
PYTHONPATH=scripts/lc_freeze_seams:src \
|
||||
python3 scripts/freeze_truth.py --verify \
|
||||
src/rust/layout_contract/frozen_lc_gap_truth.json \
|
||||
--module rune.layout_contract
|
||||
|
||||
Every ported function and branch is exercised:
|
||||
define_region new (defaults / all kwargs), merge-update
|
||||
(identities refresh, evidence union, maturity
|
||||
high-water, tags union), empty evidence_refs
|
||||
bind_component ui:-prefixing (both forms), unknown region,
|
||||
lock/provenance law (2 rejections), re-bind merge
|
||||
(amendments preserved, DRIFTING→ACTIVE, evidence
|
||||
union), order/parent_of, framework_attrs
|
||||
amend_layout_contract no contract, flexible amend (region/order),
|
||||
ui:-prefixed, USER-LOCKED rejections (non-user
|
||||
auth ×2, missing reason), authorized amend,
|
||||
unlock (authorized + rejected), unknown region,
|
||||
prior amendments append + reason[:200] truncation
|
||||
record_layout_observation empty adapter, empty observed, prefix mix,
|
||||
append to existing observations, empty obs dict
|
||||
"""
|
||||
import os
|
||||
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_envelope # noqa: E402
|
||||
|
||||
import sitecustomize as rig # noqa: E402 (rig-bound: pinned cores + clock)
|
||||
|
||||
NOW = rig.FROZEN_NOW
|
||||
OUT = Path(os.environ.get("LC_TRUTH_OUT")
|
||||
or REPO / "src" / "rust" / "layout_contract" / "frozen_lc_gap_truth.json")
|
||||
|
||||
|
||||
def new_doc():
|
||||
return {"schema_version": 1, "generated_at": NOW,
|
||||
"entities": [], "relationships": [], "observations": []}
|
||||
|
||||
|
||||
# ── literal fixtures (match add_entity output exactly) ──────────────────────
|
||||
|
||||
def region(rid, **ids):
|
||||
ident = {"parent": "", "orientation": "", "sizing": "",
|
||||
"visibility": "", "provenance": "scaffold", "tech": {}}
|
||||
ident.update(ids)
|
||||
return {"id": f"region:{rid}", "kind": "layout_region",
|
||||
"maturity": "DECLARED", "tags": [], "identities": ident,
|
||||
"evidence_refs": ["region-defined:scaffold"],
|
||||
"observed_at": NOW}
|
||||
|
||||
|
||||
DOC_REGIONS = new_doc()
|
||||
DOC_REGIONS["entities"] = [
|
||||
region("header", orientation="horizontal", sizing="auto",
|
||||
visibility="visible"),
|
||||
region("sidebar", parent="header", order=1, orientation="vertical",
|
||||
sizing="fill", visibility="visible", provenance="user",
|
||||
tech={"tui": "pane"}),
|
||||
{"id": "region:main", "kind": "layout_region", "maturity": "DECLARED",
|
||||
"tags": [], "identities": {"parent": "", "orientation": "",
|
||||
"sizing": "", "visibility": "",
|
||||
"provenance": "scaffold", "tech": {}},
|
||||
"evidence_refs": ["spec-layout"], "observed_at": NOW},
|
||||
]
|
||||
|
||||
|
||||
def contract(component, region_id, *, lock=False, provenance="scaffold",
|
||||
order=None, anchor="", framework_attrs=None, status="ACTIVE",
|
||||
evidence="layout-bind:scaffold", amendments=None,
|
||||
last_violation=None):
|
||||
ident = {"component": component, "region": region_id, "lock": lock,
|
||||
"provenance": provenance, "anchor": anchor, "sizing": "",
|
||||
"visibility": "", "implementation": "",
|
||||
"framework_attrs": dict(framework_attrs or {})}
|
||||
if order is not None:
|
||||
ident["order"] = order
|
||||
ext = {"amendments": amendments or []}
|
||||
if last_violation is not None:
|
||||
ext["last_violation"] = last_violation
|
||||
return {"id": f"layout:{component}", "kind": "layout_contract",
|
||||
"maturity": "DECLARED", "tags": [], "identities": ident,
|
||||
"evidence_refs": [evidence], "observed_at": NOW,
|
||||
"ext": ext, "status": status}
|
||||
|
||||
|
||||
def with_contracts(**kw):
|
||||
doc = new_doc()
|
||||
doc["entities"] = list(DOC_REGIONS["entities"]) + [
|
||||
contract("ui:version-info", "sidebar", lock=True, provenance="user",
|
||||
order=1, anchor="top", evidence="issue-105", **kw),
|
||||
contract("ui:status-bar", "main",
|
||||
framework_attrs={"qt": "QStatusBar"})]
|
||||
return doc
|
||||
|
||||
|
||||
DOC_WITH_CONTRACTS = with_contracts()
|
||||
DOC_DRIFTING = with_contracts(
|
||||
status="DRIFTING",
|
||||
last_violation={"at": NOW, "observed": {"region": "main"},
|
||||
"reason": "region 'main' != required 'sidebar'"},
|
||||
)
|
||||
DOC_AMENDED = new_doc()
|
||||
DOC_AMENDED["entities"] = list(DOC_REGIONS["entities"]) + [
|
||||
contract("ui:version-info", "sidebar", lock=True, provenance="user",
|
||||
order=1, anchor="top", evidence="issue-105",
|
||||
amendments=[{"at": NOW, "authorized_by": "user",
|
||||
"reason": "first move",
|
||||
"evidence": "am-0",
|
||||
"changes": {"anchor": "top"}}]),
|
||||
contract("ui:status-bar", "main",
|
||||
framework_attrs={"qt": "QStatusBar"}),
|
||||
]
|
||||
|
||||
DOC_MATURE = new_doc()
|
||||
_mature_sidebar = region("sidebar", parent="header")
|
||||
_mature_sidebar["maturity"] = "WIRED"
|
||||
_mature_sidebar["tags"] = ["layout"]
|
||||
DOC_MATURE["entities"] = [
|
||||
region("header"),
|
||||
_mature_sidebar,
|
||||
{"id": "region:main", "kind": "layout_region", "maturity": "DECLARED",
|
||||
"tags": [], "identities": {"parent": "", "orientation": "",
|
||||
"sizing": "", "visibility": "",
|
||||
"provenance": "scaffold", "tech": {}},
|
||||
"evidence_refs": ["spec-layout"], "observed_at": NOW},
|
||||
]
|
||||
|
||||
DOC_WITH_OBS = new_doc()
|
||||
DOC_WITH_OBS["observations"] = [
|
||||
{"kind": "layout_observed", "component": "ui:footer",
|
||||
"adapter": "prev", "observed": {"region": "main"},
|
||||
"observed_at": NOW},
|
||||
]
|
||||
|
||||
cases = [
|
||||
# ── define_region: new entity, defaults ─────────────────────────────────
|
||||
("define_region", (new_doc(), "header"), {}),
|
||||
# all kwargs (tech dict, explicit provenance/evidence)
|
||||
("define_region", (new_doc(), "sidebar"),
|
||||
{"parent": "header", "order": 1, "orientation": "vertical",
|
||||
"sizing": "fill", "visibility": "visible", "tech": {"tui": "pane"},
|
||||
"provenance": "user", "evidence_refs": ["spec-11"]}),
|
||||
# explicit empty evidence_refs mints the region-defined default too
|
||||
# (Python `x or [default]` falsiness law)
|
||||
("define_region", (new_doc(), "r-empty"),
|
||||
{"evidence_refs": []}),
|
||||
# unsorted evidence_refs freeze sorted
|
||||
("define_region", (new_doc(), "multi"),
|
||||
{"evidence_refs": ["b", "a", "c"]}),
|
||||
# ── define_region: merge-update paths ───────────────────────────────────
|
||||
# no-op re-define (identities overwritten with defaults, observed_at
|
||||
# refreshed)
|
||||
("define_region", (DOC_REGIONS, "sidebar"), {}),
|
||||
# union merge: identities refresh + evidence union, order re-set
|
||||
("define_region", (DOC_REGIONS, "sidebar"),
|
||||
{"order": 2, "orientation": "horizontal", "evidence_refs": ["spec-12"]}),
|
||||
# mature sidebar: maturity high-water (DECLARED does NOT downgrade
|
||||
# WIRED), tags union (none added via this API)
|
||||
("define_region", (DOC_MATURE, "sidebar"),
|
||||
{"evidence_refs": ["fresh"]}),
|
||||
# ── bind_component: new contracts ───────────────────────────────────────
|
||||
# USER-LOCKED bind (lock + user provenance)
|
||||
("bind_component", (DOC_REGIONS, "version-info", "sidebar"),
|
||||
{"lock": True, "provenance": "user", "order": 1, "anchor": "top",
|
||||
"evidence_ref": "issue-105"}),
|
||||
# flexible bind, all defaults
|
||||
("bind_component", (DOC_REGIONS, "status-bar", "main"), {}),
|
||||
# ui:-prefixed component + open-world framework attrs (they ride the
|
||||
# **framework_attrs catch-all — passed as bare kwargs, like real callers)
|
||||
# + implementation
|
||||
("bind_component", (DOC_REGIONS, "ui:footer", "main"),
|
||||
{"sizing": "1-line", "visibility": "always",
|
||||
"implementation": "src/ui/footer.py",
|
||||
"css": "flex", "qt": "QStatusBar"}),
|
||||
# parent_of + order on a bare name
|
||||
("bind_component", (DOC_REGIONS, "panel", "sidebar"),
|
||||
{"parent_of": "ui:version-info", "order": 2}),
|
||||
# ── bind_component: rejections (the lock/provenance law) ────────────────
|
||||
("bind_component", (DOC_REGIONS, "x", "nope"), {}),
|
||||
("bind_component", (DOC_REGIONS, "x", "sidebar"), {"lock": True}),
|
||||
("bind_component", (DOC_REGIONS, "x", "sidebar"),
|
||||
{"lock": True, "provenance": "builder"}),
|
||||
# ── bind_component: re-bind merges ──────────────────────────────────────
|
||||
# identities refresh (anchor), ACTIVE kept; the amendments ledger is
|
||||
# RESET by re-binding (fresh binding — the oracle assigns [], not
|
||||
# appends)
|
||||
("bind_component", (DOC_WITH_CONTRACTS, "version-info", "sidebar"),
|
||||
{"lock": True, "provenance": "user", "order": 1, "anchor": "top-left",
|
||||
"evidence_ref": "rebind-1"}),
|
||||
# re-bind over a NON-EMPTY amendments ledger: cleared (the
|
||||
# discriminating case — empty-ledger re-binds can't tell)
|
||||
("bind_component", (DOC_AMENDED, "version-info", "sidebar"),
|
||||
{"lock": True, "provenance": "user", "order": 1, "anchor": "top"}),
|
||||
# DRIFTING contract re-bound returns to ACTIVE
|
||||
("bind_component", (DOC_DRIFTING, "version-info", "sidebar"),
|
||||
{"lock": True, "provenance": "user", "order": 1, "anchor": "top"}),
|
||||
# ui:-prefixed re-bind: region change + evidence union
|
||||
("bind_component", (DOC_WITH_CONTRACTS, "ui:status-bar", "sidebar"),
|
||||
{"evidence_ref": "move-1"}),
|
||||
# ── amend_layout_contract: rejections ───────────────────────────────────
|
||||
("amend_layout_contract", (new_doc(), "version-info"), {}),
|
||||
("amend_layout_contract", (DOC_WITH_CONTRACTS, "version-info"),
|
||||
{"new_region": "main"}),
|
||||
("amend_layout_contract", (DOC_WITH_CONTRACTS, "version-info"),
|
||||
{"authorized_by": "model"}),
|
||||
("amend_layout_contract", (DOC_WITH_CONTRACTS, "version-info"),
|
||||
{"authorized_by": "user"}),
|
||||
("amend_layout_contract", (DOC_WITH_CONTRACTS, "version-info"),
|
||||
{"unlock": True, "authorized_by": "user"}),
|
||||
("amend_layout_contract", (DOC_WITH_CONTRACTS, "status-bar"),
|
||||
{"unlock": True}),
|
||||
("amend_layout_contract", (DOC_WITH_CONTRACTS, "status-bar"),
|
||||
{"new_region": "nope"}),
|
||||
# ── amend_layout_contract: mutations ────────────────────────────────────
|
||||
# flexible builder amend (region + order + evidence)
|
||||
("amend_layout_contract", (DOC_WITH_CONTRACTS, "status-bar"),
|
||||
{"new_region": "sidebar", "new_order": 0, "authorized_by": "builder",
|
||||
"reason": "reorganize", "evidence_ref": "am-1"}),
|
||||
# ui:-prefixed component, order-only amend
|
||||
("amend_layout_contract", (DOC_WITH_CONTRACTS, "ui:status-bar"),
|
||||
{"new_order": 3}),
|
||||
# authorized USER-LOCKED amend
|
||||
("amend_layout_contract", (DOC_WITH_CONTRACTS, "version-info"),
|
||||
{"new_region": "main", "authorized_by": "user",
|
||||
"reason": "user wants it in main"}),
|
||||
# authorized unlock
|
||||
("amend_layout_contract", (DOC_WITH_CONTRACTS, "version-info"),
|
||||
{"unlock": True, "authorized_by": "user", "reason": "free it"}),
|
||||
# append to a prior amendments ledger + reason[:200] truncation
|
||||
("amend_layout_contract", (DOC_AMENDED, "version-info"),
|
||||
{"new_order": 5, "authorized_by": "user", "reason": "R" * 300,
|
||||
"evidence_ref": "am-2"}),
|
||||
# ── record_layout_observation ───────────────────────────────────────────
|
||||
("record_layout_observation", (new_doc(), "", {}), {}),
|
||||
("record_layout_observation", (new_doc(), "qt-adapter", {}), {}),
|
||||
# prefix mix (bare, ui:-prefixed, new) + multi-field obs
|
||||
("record_layout_observation",
|
||||
(new_doc(), "tui",
|
||||
{"version-info": {"region": "sidebar", "order": 1},
|
||||
"ui:status-bar": {"region": "main"},
|
||||
"footer": {"region": "main", "parent": "header"}}), {}),
|
||||
# append to existing observations
|
||||
("record_layout_observation",
|
||||
(DOC_WITH_OBS, "tui", {"version-info": {"region": "main"}}), {}),
|
||||
# empty obs dict passthrough
|
||||
("record_layout_observation", (new_doc(), "web-dom", {"x": {}}), {}),
|
||||
]
|
||||
|
||||
truth = freeze("rune.layout_contract", cases,
|
||||
oracle_module="rune.layout_contract")
|
||||
write_envelope(NOW, truth, OUT)
|
||||
print(f"froze {len(truth)} cases -> {OUT}")
|
||||
@@ -0,0 +1,85 @@
|
||||
# Freeze rig for the layout_contract dichotomy gap-closing (wave-5).
|
||||
# Auto-imported on interpreter start (PYTHONPATH=scripts/lc_freeze_seams:src)
|
||||
# by BOTH the truth generator (scripts/gen_layout_contract_truth.py) and the
|
||||
# shared harness's --verify mode, so frozen truth is byte-reproducible in any
|
||||
# process. Mirrors scripts/ci_freeze_seams (the closest sibling: a
|
||||
# structural_store-backed module).
|
||||
#
|
||||
# * EVERY RUNE_*_CORE gate is pinned to "python" — the oracle runs its
|
||||
# pure-Python floors end to end (layout_contract, structural_store, the
|
||||
# transitive scaffold cone). The shared harness's _pin_oracle_cores cannot
|
||||
# reach the transitive gates; this blanket pin is the poisoned-truth law.
|
||||
# * The wall clock is FROZEN: rune.structural_store.now AND the
|
||||
# rune.layout_contract bound import report LC_FROZEN_NOW (add_entity
|
||||
# stamps observed_at through the structural_store seam; amend_layout_
|
||||
# contract / record_layout_observation through the bound import).
|
||||
# * DOC-CARRYING SEAMS: the 4 ported fns mutate a doc argument, so the rig
|
||||
# shadows each oracle fn with a wrapper that (a) runs the REAL oracle fn
|
||||
# (captured before shadowing) on the caller's doc and (b) returns
|
||||
# {"result": <return value>, "doc": {"entities":…, "observations":…}} —
|
||||
# the post-call store state the Rust kernel contract includes. Errors are
|
||||
# captured as {"error": "Type: message"} (construction_decision
|
||||
# convention) so validation branches freeze instead of crashing the
|
||||
# generator. No file IO in this module's surface — no HOME sandbox needed.
|
||||
#
|
||||
# The frozen cases replay in the Rust battery through kernels::dispatch with
|
||||
# the same FROZEN_NOW constant; serde_json preserve_order keeps the
|
||||
# observed-dict insertion order (the json insertion-order quirk) byte-stable.
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
for _k in list(os.environ):
|
||||
if _k.startswith("RUNE_") and _k.endswith("_CORE"):
|
||||
os.environ[_k] = "python"
|
||||
|
||||
# Explicit pins for the gates this cone reads even when unset in the env
|
||||
# (the loop above only pins vars that already exist): the layout core
|
||||
# itself and the structural_store seam underneath it.
|
||||
os.environ["RUNE_LC_CORE"] = "python"
|
||||
os.environ["RUNE_STRUCTURAL_CORE"] = "python"
|
||||
|
||||
FROZEN_NOW = "2026-10-14T00:00:00Z"
|
||||
|
||||
_SRC = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(
|
||||
os.path.abspath(__file__)))), "src")
|
||||
if _SRC not in sys.path:
|
||||
sys.path.insert(0, _SRC)
|
||||
|
||||
import rune.structural_store as _ss # noqa: E402
|
||||
import rune.layout_contract as lc # noqa: E402
|
||||
|
||||
_ss.now = lambda: FROZEN_NOW
|
||||
lc.now = lambda: FROZEN_NOW
|
||||
|
||||
# Originals captured BEFORE the seam bindings below shadow the module
|
||||
# globals — the wrappers must call the captured originals, never the
|
||||
# shadowed name.
|
||||
_ORIG = {name: getattr(lc, name) for name in (
|
||||
"define_region", "bind_component", "amend_layout_contract",
|
||||
"record_layout_observation")}
|
||||
|
||||
|
||||
def _doc_carrying(name):
|
||||
orig = _ORIG[name]
|
||||
|
||||
def wrapper(doc, *args, **kwargs):
|
||||
try:
|
||||
result = orig(doc, *args, **kwargs)
|
||||
except (ValueError, TypeError, KeyError, AttributeError) as e:
|
||||
return {"error": f"{type(e).__name__}: {e}"}
|
||||
return {
|
||||
"result": result,
|
||||
"doc": {
|
||||
"entities": doc.get("entities", []),
|
||||
"observations": doc.get("observations", []),
|
||||
},
|
||||
}
|
||||
|
||||
wrapper.__name__ = name
|
||||
wrapper.__wrapped__ = orig
|
||||
return wrapper
|
||||
|
||||
|
||||
for _name in _ORIG:
|
||||
setattr(lc, _name, _doc_carrying(_name))
|
||||
+102
-4
@@ -44,6 +44,21 @@ __all__ = [
|
||||
STATUS = {"ACTIVE", "AMENDED", "RETIRED", "DRIFTING"}
|
||||
|
||||
|
||||
def _core():
|
||||
"""The Rust layout core when built (dichotomy wave-5 gap-close).
|
||||
RUNE_LC_CORE=python pins the fallback."""
|
||||
if os.environ.get("RUNE_LC_CORE", "auto") == "python":
|
||||
return None
|
||||
try:
|
||||
from rune import lc_loader
|
||||
except Exception: # noqa: BLE001 — any loader failure means fallback
|
||||
return None
|
||||
try:
|
||||
return lc_loader if lc_loader.available() else None
|
||||
except Exception: # noqa: BLE001 — probe must never crash the fallback
|
||||
return None
|
||||
|
||||
|
||||
# ── regions (open-world vocabulary) ───────────────────────────────────────
|
||||
|
||||
|
||||
@@ -62,7 +77,30 @@ def define_region(
|
||||
) -> dict:
|
||||
"""Define/reinforce a layout region. Regions are open-world and
|
||||
project-defined — header/sidebar/main/inspector are examples, not enum.
|
||||
`tech` carries framework metadata (web DOM / Qt widgets / TUI panes)."""
|
||||
`tech` carries framework metadata (web DOM / Qt widgets / TUI panes).
|
||||
|
||||
Dichotomy wave-5: delegates to the Rust kernel when built
|
||||
(RUNE_LC_CORE=python pins the Python oracle below)."""
|
||||
core = _core()
|
||||
if core is not None:
|
||||
try:
|
||||
out = core.define_region(
|
||||
doc, region_id, parent=parent, order=order,
|
||||
orientation=orientation, sizing=sizing, visibility=visibility,
|
||||
tech=tech, provenance=provenance, evidence_refs=evidence_refs,
|
||||
now=now())
|
||||
except RuntimeError as exc:
|
||||
raise ValueError(str(exc)) from exc # kernel err = oracle ValueError semantics
|
||||
except Exception:
|
||||
pass # fallback below; never crash the caller
|
||||
else:
|
||||
core.absorb(doc, out)
|
||||
# re-resolve from the store: the Python path returns the LIVE
|
||||
# stored entity (aliasing semantics), not a value snapshot
|
||||
rid = f"region:{region_id}"
|
||||
got = entity(doc, rid)
|
||||
if got is not None:
|
||||
return got
|
||||
identities = {
|
||||
"parent": parent,
|
||||
"orientation": orientation,
|
||||
@@ -111,7 +149,29 @@ def bind_component(
|
||||
silently override (§14-15).
|
||||
|
||||
Framework-specific detail (CSS-ish or widget-ish) rides in
|
||||
**framework_attrs — open-world, never first-class core fields (§19)."""
|
||||
**framework_attrs — open-world, never first-class core fields (§19).
|
||||
|
||||
Dichotomy wave-5: delegates to the Rust kernel when built
|
||||
(RUNE_LC_CORE=python pins the Python oracle below)."""
|
||||
core = _core()
|
||||
if core is not None:
|
||||
try:
|
||||
out = core.bind_component(
|
||||
doc, component, region_id, lock=lock, provenance=provenance,
|
||||
order=order, parent_of=parent_of, anchor=anchor, sizing=sizing,
|
||||
visibility=visibility, evidence_ref=evidence_ref,
|
||||
implementation=implementation,
|
||||
framework_attrs=dict(framework_attrs), now=now())
|
||||
except RuntimeError as exc:
|
||||
raise ValueError(str(exc)) from exc # kernel err = oracle ValueError semantics
|
||||
except Exception:
|
||||
pass # fallback below; never crash the caller
|
||||
else:
|
||||
core.absorb(doc, out)
|
||||
cid0 = component if component.startswith("ui:") else f"ui:{component}"
|
||||
got = entity(doc, f"layout:{cid0}")
|
||||
if got is not None:
|
||||
return got
|
||||
if not component.startswith("ui:"):
|
||||
component = f"ui:{component}"
|
||||
if entity(doc, f"region:{region_id}") is None:
|
||||
@@ -159,7 +219,27 @@ def amend_layout_contract(
|
||||
) -> dict:
|
||||
"""Amend a layout contract. A USER-LOCKED contract requires
|
||||
authorized_by='user' — enforced, not documented (§15). Every amendment
|
||||
is recorded on the contract; the original binding remains in history."""
|
||||
is recorded on the contract; the original binding remains in history.
|
||||
|
||||
Dichotomy wave-5: delegates to the Rust kernel when built
|
||||
(RUNE_LC_CORE=python pins the Python oracle below)."""
|
||||
core = _core()
|
||||
if core is not None:
|
||||
try:
|
||||
out = core.amend_layout_contract(
|
||||
doc, component, new_region=new_region, new_order=new_order,
|
||||
unlock=unlock, authorized_by=authorized_by, reason=reason,
|
||||
evidence_ref=evidence_ref, now=now())
|
||||
except RuntimeError as exc:
|
||||
raise ValueError(str(exc)) from exc # kernel err = oracle ValueError semantics
|
||||
except Exception:
|
||||
pass # fallback below; never crash the caller
|
||||
else:
|
||||
core.absorb(doc, out)
|
||||
cid0 = component if component.startswith("ui:") else f"ui:{component}"
|
||||
got = entity(doc, f"layout:{cid0}")
|
||||
if got is not None:
|
||||
return got
|
||||
cid = component if component.startswith("ui:") else f"ui:{component}"
|
||||
e = entity(doc, f"layout:{cid}")
|
||||
if e is None:
|
||||
@@ -205,7 +285,25 @@ def amend_layout_contract(
|
||||
def record_layout_observation(doc: dict, adapter: str, observed: dict) -> list[dict]:
|
||||
"""Record what a structural adapter saw: {ui_component: {"region": ...,
|
||||
"order": ..., "parent": ...}}. Observations are evidence appended to the
|
||||
store; they never rewrite contracts (§17-18, §22)."""
|
||||
store; they never rewrite contracts (§17-18, §22).
|
||||
|
||||
Dichotomy wave-5: delegates to the Rust kernel when built
|
||||
(RUNE_LC_CORE=python pins the Python oracle below)."""
|
||||
core = _core()
|
||||
if core is not None:
|
||||
try:
|
||||
out = core.record_layout_observation(
|
||||
doc, adapter, observed, now=now())
|
||||
except RuntimeError as exc:
|
||||
raise ValueError(str(exc)) from exc # kernel err = oracle ValueError semantics
|
||||
except Exception:
|
||||
pass # fallback below; never crash the caller
|
||||
else:
|
||||
core.absorb(doc, out)
|
||||
n = len(out.get("observations", []))
|
||||
if n:
|
||||
return doc["observations"][-n:]
|
||||
return []
|
||||
if not adapter:
|
||||
raise ValueError("observation requires adapter identity")
|
||||
out = []
|
||||
|
||||
+88
-2
@@ -1,10 +1,15 @@
|
||||
#!/usr/bin/env python3
|
||||
"""ctypes loader for the Rust layout_contract core (dichotomy debt queue
|
||||
batch 4, #2479 tranche). Mirrors broker_loader.py's contract:
|
||||
batch 4 + wave-5 gap-close). Mirrors broker_loader.py's contract:
|
||||
|
||||
FFI contract (doc-envelope law):
|
||||
* lc_check({contracts, observed}) -> {verdict, checks}
|
||||
* lc_drift({contracts, checks}) -> {drifted_locked, reactivated}
|
||||
* lc_define_region({doc, region_id, ..., now}) -> {doc, entity}
|
||||
* lc_bind_component({doc, component, region_id, ..., now}) -> {doc, entity}
|
||||
* lc_amend_layout_contract({doc, component, ..., now}) -> {doc, entity}
|
||||
* lc_record_observation({doc, adapter, observed, now}) -> {doc, observations}
|
||||
* lc_replay({fn, args, kwargs}) -> frozen-truth replay value
|
||||
|
||||
Override the .so with LC_RUST_LIB (a FILE path).
|
||||
"""
|
||||
@@ -21,6 +26,11 @@ _LIB_PATH = Path(os.environ.get("LC_RUST_LIB", _CRATE_DIR / "target" / "release"
|
||||
|
||||
_lib = None
|
||||
|
||||
_SYMBOLS = (
|
||||
"lc_check", "lc_drift", "lc_define_region", "lc_bind_component",
|
||||
"lc_amend_layout_contract", "lc_record_observation", "lc_replay",
|
||||
)
|
||||
|
||||
|
||||
class LayoutContractUnavailable(FileNotFoundError):
|
||||
"""The Rust layout core is not built. Callers fall back to Python."""
|
||||
@@ -37,7 +47,7 @@ def _load() -> ctypes.CDLL:
|
||||
if not _LIB_PATH.is_file():
|
||||
raise LayoutContractUnavailable(str(_LIB_PATH))
|
||||
lib = ctypes.CDLL(str(_LIB_PATH))
|
||||
for sym in ("lc_check", "lc_drift"):
|
||||
for sym in _SYMBOLS:
|
||||
fn = getattr(lib, sym)
|
||||
fn.argtypes = [ctypes.c_void_p]
|
||||
fn.restype = ctypes.c_void_p
|
||||
@@ -73,3 +83,79 @@ def check_layout(contracts: list, observed: dict) -> dict:
|
||||
|
||||
def drift_decisions(contracts: list, checks: list) -> dict:
|
||||
return _call("lc_drift", {"contracts": contracts, "checks": checks})
|
||||
|
||||
|
||||
def define_region(doc: dict, region_id: str, *, parent: str = "", order=None,
|
||||
orientation: str = "", sizing: str = "", visibility: str = "",
|
||||
tech: dict | None = None, provenance: str = "scaffold",
|
||||
evidence_refs=None, now: str = "") -> dict:
|
||||
return _call("lc_define_region", {
|
||||
"doc": doc, "region_id": region_id, "parent": parent, "order": order,
|
||||
"orientation": orientation, "sizing": sizing, "visibility": visibility,
|
||||
"tech": tech or {}, "provenance": provenance,
|
||||
"evidence_refs": evidence_refs, "now": now})
|
||||
|
||||
|
||||
def bind_component(doc: dict, component: str, region_id: str, *, lock=False,
|
||||
provenance: str = "scaffold", order=None, parent_of: str = "",
|
||||
anchor: str = "", sizing: str = "", visibility: str = "",
|
||||
evidence_ref: str = "", implementation: str = "",
|
||||
framework_attrs: dict | None = None, now: str = "") -> dict:
|
||||
return _call("lc_bind_component", {
|
||||
"doc": doc, "component": component, "region_id": region_id,
|
||||
"lock": lock, "provenance": provenance, "order": order,
|
||||
"parent_of": parent_of, "anchor": anchor, "sizing": sizing,
|
||||
"visibility": visibility, "evidence_ref": evidence_ref,
|
||||
"implementation": implementation,
|
||||
"framework_attrs": framework_attrs or {}, "now": now})
|
||||
|
||||
|
||||
def amend_layout_contract(doc: dict, component: str, *, new_region: str = "",
|
||||
new_order=None, unlock: bool = False,
|
||||
authorized_by: str = "", reason: str = "",
|
||||
evidence_ref: str = "", now: str = "") -> dict:
|
||||
return _call("lc_amend_layout_contract", {
|
||||
"doc": doc, "component": component, "new_region": new_region,
|
||||
"new_order": new_order, "unlock": unlock,
|
||||
"authorized_by": authorized_by, "reason": reason,
|
||||
"evidence_ref": evidence_ref, "now": now})
|
||||
|
||||
|
||||
def record_layout_observation(doc: dict, adapter: str, observed: dict,
|
||||
now: str = "") -> dict:
|
||||
return _call("lc_record_observation", {
|
||||
"doc": doc, "adapter": adapter, "observed": observed, "now": now})
|
||||
|
||||
|
||||
def replay(case: dict):
|
||||
return _call("lc_replay", case)
|
||||
|
||||
|
||||
def absorb(doc: dict, out: dict) -> None:
|
||||
"""Merge the kernel's updated doc view into the caller's live doc
|
||||
(mutation parity: the Python oracle mutates in place, so callers holding
|
||||
the reference observe the change). Entities merge by id INTO the
|
||||
existing stored objects with wholesale key assignment (add_entity's
|
||||
.update() law: identities keys are replaced, not recursively merged);
|
||||
observations keep prior rows and append the new tail — the kernels
|
||||
never rewrite or remove old rows."""
|
||||
new_doc = out.get("doc", {})
|
||||
for key, val in new_doc.items():
|
||||
if key == "entities" and isinstance(val, list):
|
||||
cur = doc.setdefault("entities", [])
|
||||
by_id = {e.get("id"): e for e in cur if isinstance(e, dict)}
|
||||
for ne in val:
|
||||
if not isinstance(ne, dict):
|
||||
continue
|
||||
old = by_id.get(ne.get("id"))
|
||||
if old is not None:
|
||||
old.update(ne)
|
||||
else:
|
||||
cur.append(ne)
|
||||
elif key == "observations" and isinstance(val, list):
|
||||
cur = doc.setdefault("observations", [])
|
||||
n_new = len(val) - len(cur)
|
||||
if n_new > 0:
|
||||
cur.extend(val[-n_new:])
|
||||
else:
|
||||
doc[key] = val
|
||||
Generated
+23
@@ -2,6 +2,28 @@
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "equivalent"
|
||||
version = "1.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "00d174d5400e5e8fd687ad1049e2f578285fa914201b1af7e8b112a4546bd826"
|
||||
|
||||
[[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"
|
||||
@@ -74,6 +96,7 @@ version = "1.0.151"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c841b55ecdae098c80dcae9cf767f6f8a0c2cdb3416bbef72181df4d0fe73f14"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"itoa",
|
||||
"memchr",
|
||||
"serde",
|
||||
|
||||
@@ -7,4 +7,7 @@ edition = "2021"
|
||||
crate-type = ["rlib", "cdylib", "staticlib"]
|
||||
|
||||
[dependencies]
|
||||
serde_json = "1"
|
||||
# preserve_order: record_layout_observation's out-list follows the
|
||||
# observed-dict insertion order (the json insertion-order quirk); the
|
||||
# default BTreeMap would silently sort those keys.
|
||||
serde_json = { version = "1", features = ["preserve_order"] }
|
||||
File diff suppressed because it is too large.
Load diff
@@ -276,7 +276,7 @@ fn drift_matches_oracle() {
|
||||
let truth: serde_json::Value = serde_json::from_str(TRUTH).expect("truth parses");
|
||||
for (idx, case) in truth["drift"].as_array().expect("drift array").iter().enumerate() {
|
||||
// rebuild contracts with the recorded post-status (the DRIFTING case needs it)
|
||||
let mut contracts = contracts_fixture();
|
||||
let contracts = contracts_fixture();
|
||||
if let Some(arr) = case.get("post_status").and_then(|v| v.as_array()) {
|
||||
for row in arr {
|
||||
let _ = (row.get(0), row.get(1));
|
||||
|
||||
@@ -0,0 +1,236 @@
|
||||
//! gap_battery — replay the FROZEN gap truth VERBATIM
|
||||
//! (frozen_lc_gap_truth.json, generated by
|
||||
//! scripts/gen_layout_contract_truth.py against the LIVE Python oracle
|
||||
//! through the scripts/lc_freeze_seams rig: pinned RUNE_*_CORE=python
|
||||
//! floors, frozen clock on both now seams, DOC-CARRYING wrappers).
|
||||
//!
|
||||
//! The battery never imports the Python oracle — parity stays provable
|
||||
//! after the strangler exits. serde_json preserve_order keeps the
|
||||
//! observed-dict insertion order stable (the json insertion-order quirk):
|
||||
//! record_layout_observation's out-list order follows the JSON file order.
|
||||
//! Branch reachability beyond the frozen corpus is proven directly
|
||||
//! (sorted-set dedup, maturity high-water, alias-following resolution,
|
||||
//! 200-codepoint reason truncation).
|
||||
|
||||
use crate::kernels;
|
||||
use serde_json::{json, Value};
|
||||
use std::ffi::{CStr, CString};
|
||||
|
||||
const TRUTH: &str = include_str!("../frozen_lc_gap_truth.json");
|
||||
|
||||
fn cases() -> Vec<Value> {
|
||||
let env: Value = serde_json::from_str(TRUTH).expect("frozen truth parses");
|
||||
env.get("cases")
|
||||
.and_then(|c| c.as_array())
|
||||
.cloned()
|
||||
.expect("cases array")
|
||||
}
|
||||
|
||||
fn case_id(c: &Value) -> String {
|
||||
c.get("args")
|
||||
.and_then(|a| a.as_array())
|
||||
.and_then(|a| a.get(1))
|
||||
.map(|x| match x {
|
||||
Value::String(s) => s.clone(),
|
||||
other => other.to_string(),
|
||||
})
|
||||
.unwrap_or_else(|| "?".to_string())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn frozen_gap_truth_replays_verbatim() {
|
||||
let env: Value = serde_json::from_str(TRUTH).expect("frozen truth parses");
|
||||
assert_eq!(
|
||||
env.get("frozen_now").and_then(|v| v.as_str()),
|
||||
Some(kernels::FROZEN_NOW),
|
||||
"battery clock must match the freeze rig clock"
|
||||
);
|
||||
let mut n = 0;
|
||||
for case in cases() {
|
||||
let got = kernels::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, 35, "frozen corpus size drifted");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lc_replay_ffi_roundtrip() {
|
||||
// the FFI surface returns the same dispatch computation the parity
|
||||
// script drives through lc_loader
|
||||
let case = &cases()[0];
|
||||
let payload = serde_json::to_vec(case).expect("case serializes");
|
||||
let cstr = CString::new(payload).expect("no NUL in JSON");
|
||||
let ptr = crate::lc_replay(cstr.as_ptr());
|
||||
let text = unsafe { CStr::from_ptr(ptr) }.to_str().expect("utf8");
|
||||
let env: Value = serde_json::from_str(text).expect("envelope parses");
|
||||
assert_eq!(env.get("ok"), Some(&json!(true)), "envelope ok: {text}");
|
||||
assert_eq!(
|
||||
env.get("value"),
|
||||
case.get("expected"),
|
||||
"lc_replay value must equal the frozen expected"
|
||||
);
|
||||
unsafe { drop(CString::from_raw(ptr)) };
|
||||
}
|
||||
|
||||
fn doc_with(entities: Vec<Value>) -> Value {
|
||||
json!({
|
||||
"schema_version": 1,
|
||||
"generated_at": kernels::FROZEN_NOW,
|
||||
"entities": entities,
|
||||
"relationships": [],
|
||||
"observations": [],
|
||||
})
|
||||
}
|
||||
|
||||
fn region(rid: &str, evidence: &[&str]) -> Value {
|
||||
json!({
|
||||
"id": format!("region:{rid}"),
|
||||
"kind": "layout_region",
|
||||
"maturity": "DECLARED",
|
||||
"tags": [],
|
||||
"identities": {"parent": "", "orientation": "", "sizing": "",
|
||||
"visibility": "", "provenance": "scaffold", "tech": {}},
|
||||
"evidence_refs": evidence,
|
||||
"observed_at": kernels::FROZEN_NOW,
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn branch_evidence_union_is_sorted_dedup() {
|
||||
let mut doc = doc_with(vec![region("sidebar", &["b", "a"])]);
|
||||
let e = kernels::define_region(
|
||||
&mut doc, "sidebar", "", None, "", "", "", &Default::default(),
|
||||
"scaffold", &[json!("a"), json!("c")], kernels::FROZEN_NOW,
|
||||
)
|
||||
.expect("re-define ok");
|
||||
assert_eq!(
|
||||
e["evidence_refs"],
|
||||
json!(["a", "b", "c"]),
|
||||
"union dedups and sorts (Python sorted(set(|)))"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn branch_maturity_high_water_never_downgrades() {
|
||||
let mut mature = region("sidebar", &["x"]);
|
||||
mature["maturity"] = json!("WIRED");
|
||||
let mut doc = doc_with(vec![mature]);
|
||||
let e = kernels::define_region(
|
||||
&mut doc, "sidebar", "", None, "", "", "", &Default::default(),
|
||||
"scaffold", &[], kernels::FROZEN_NOW,
|
||||
)
|
||||
.expect("re-define ok");
|
||||
assert_eq!(e["maturity"], json!("WIRED"), "DECLARED never downgrades WIRED");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn branch_region_resolution_follows_aliases() {
|
||||
// a region renamed but aliased still satisfies bind_component's
|
||||
// region check (entity() follows alias entries)
|
||||
let mut r = region("sidebar-new", &["x"]);
|
||||
r["aliases"] = json!(["region:sidebar-old"]);
|
||||
let mut doc = doc_with(vec![r]);
|
||||
let e = kernels::bind_component(
|
||||
&mut doc, "version-info", "sidebar-old", false, "scaffold", None, "",
|
||||
"", "", "", "", "", &Default::default(), kernels::FROZEN_NOW,
|
||||
)
|
||||
.expect("alias-following resolution binds");
|
||||
assert_eq!(e["identities"]["region"], json!("sidebar-old"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn branch_reason_truncation_is_200_codepoints() {
|
||||
let mut doc = doc_with(vec![
|
||||
region("main", &["x"]),
|
||||
json!({
|
||||
"id": "layout:ui:x", "kind": "layout_contract",
|
||||
"maturity": "DECLARED", "tags": [],
|
||||
"identities": {"component": "ui:x", "region": "main",
|
||||
"lock": false, "provenance": "scaffold",
|
||||
"anchor": "", "sizing": "", "visibility": "",
|
||||
"implementation": "", "framework_attrs": {}},
|
||||
"evidence_refs": ["layout-bind:scaffold"],
|
||||
"observed_at": kernels::FROZEN_NOW,
|
||||
"ext": {"amendments": []}, "status": "ACTIVE",
|
||||
}),
|
||||
]);
|
||||
let reason = "é".repeat(250); // multibyte — truncation must count codepoints
|
||||
let e = kernels::amend_layout_contract(
|
||||
&mut doc, "x", "", Some(9), false, "builder", &reason, "",
|
||||
kernels::FROZEN_NOW,
|
||||
)
|
||||
.expect("amend ok");
|
||||
let recorded = e["ext"]["amendments"][0]["reason"]
|
||||
.as_str()
|
||||
.expect("reason string");
|
||||
assert_eq!(recorded.chars().count(), 200, "exactly 200 codepoints");
|
||||
assert_eq!(recorded, "é".repeat(200));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn branch_rebind_resets_amendments_ledger() {
|
||||
// the oracle ASSIGNS a fresh ledger on every bind — caught live by the
|
||||
// facade smoke (insert-if-missing diverged from assignment)
|
||||
let mut doc = doc_with(vec![
|
||||
region("sidebar", &["x"]),
|
||||
json!({
|
||||
"id": "layout:ui:x", "kind": "layout_contract",
|
||||
"maturity": "DECLARED", "tags": [],
|
||||
"identities": {"component": "ui:x", "region": "sidebar",
|
||||
"lock": false, "provenance": "scaffold",
|
||||
"anchor": "", "sizing": "", "visibility": "",
|
||||
"implementation": "", "framework_attrs": {}},
|
||||
"evidence_refs": ["layout-bind:scaffold"],
|
||||
"observed_at": kernels::FROZEN_NOW,
|
||||
"ext": {"amendments": [{"at": kernels::FROZEN_NOW,
|
||||
"authorized_by": "user", "reason": "old",
|
||||
"evidence": "", "changes": {}}]},
|
||||
"status": "ACTIVE",
|
||||
}),
|
||||
]);
|
||||
let e = kernels::bind_component(
|
||||
&mut doc, "x", "sidebar", false, "scaffold", None, "", "", "", "",
|
||||
"", "", &Default::default(), kernels::FROZEN_NOW,
|
||||
)
|
||||
.expect("re-bind ok");
|
||||
assert_eq!(e["ext"]["amendments"], json!([]), "re-bind clears the ledger");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn branch_amend_unlock_rejection_leaves_prior_mutations() {
|
||||
// the oracle mutates region/order BEFORE the unlock-authorization
|
||||
// check — a rejected unlock still leaves those mutations applied
|
||||
let mut doc = doc_with(vec![
|
||||
region("main", &["x"]),
|
||||
region("other", &["x"]),
|
||||
json!({
|
||||
"id": "layout:ui:x", "kind": "layout_contract",
|
||||
"maturity": "DECLARED", "tags": [],
|
||||
"identities": {"component": "ui:x", "region": "main",
|
||||
"lock": false, "provenance": "scaffold",
|
||||
"anchor": "", "sizing": "", "visibility": "",
|
||||
"implementation": "", "framework_attrs": {}},
|
||||
"evidence_refs": ["layout-bind:scaffold"],
|
||||
"observed_at": kernels::FROZEN_NOW,
|
||||
"ext": {"amendments": []}, "status": "ACTIVE",
|
||||
}),
|
||||
]);
|
||||
let err = kernels::amend_layout_contract(
|
||||
&mut doc, "x", "other", None, true, "builder", "r", "",
|
||||
kernels::FROZEN_NOW,
|
||||
)
|
||||
.expect_err("unlock without user must fail");
|
||||
assert_eq!(err, "unlock requires user authorization");
|
||||
let stored = &doc["entities"][2];
|
||||
assert_eq!(
|
||||
stored["identities"]["region"], json!("other"),
|
||||
"region mutation precedes the unlock rejection (oracle order)"
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,513 @@
|
||||
//! kernels.rs — the store-mutation kernels ported name-preserving from
|
||||
//! src/rune/layout_contract.py (dichotomy wave-5 gap-close):
|
||||
//! define_region, bind_component, amend_layout_contract,
|
||||
//! record_layout_observation.
|
||||
//!
|
||||
//! Pure doc-in/doc-out: `now` is an argument (no-clock law); persistence
|
||||
//! stays with the facade. The add_entity/entity semantics of
|
||||
//! rune.structural_store are replicated exactly (the merge law:
|
||||
//! high-water maturity, sorted-set evidence union on non-empty input,
|
||||
//! identities refresh, observed_at refresh, alias-following resolution).
|
||||
//! serde_json `preserve_order` keeps observed-dict insertion order —
|
||||
//! record_layout_observation's out-list order follows the JSON file order
|
||||
//! (the json insertion-order quirk), matching the frozen oracle rows.
|
||||
|
||||
use serde_json::{json, Map, Value};
|
||||
|
||||
/// The frozen clock the truth corpus and the battery replay under
|
||||
/// (scripts/lc_freeze_seams/sitecustomize.py mints the same instant).
|
||||
pub const FROZEN_NOW: &str = "2026-10-14T00:00:00Z";
|
||||
|
||||
const MATURITY_ORDER: [&str; 6] = [
|
||||
"DECLARED", "PRESENT", "WIRED", "REACHABLE", "EXERCISED", "LIVE_PROVEN",
|
||||
];
|
||||
|
||||
fn maturity_rank(state: &str) -> i64 {
|
||||
MATURITY_ORDER
|
||||
.iter()
|
||||
.position(|m| *m == state)
|
||||
.map(|i| i as i64)
|
||||
.unwrap_or(0)
|
||||
}
|
||||
|
||||
/// Python sorted(set(a) | set(b)) over JSON values — strings dominate this
|
||||
/// surface (byte-wise str order == codepoint order); the canonical-JSON
|
||||
/// projection gives a deterministic fallback for exotic mixed inputs.
|
||||
fn sorted_set_union(existing: &[Value], added: &[Value]) -> Vec<Value> {
|
||||
let mut out: Vec<Value> = Vec::new();
|
||||
for v in existing.iter().chain(added) {
|
||||
if !out.contains(v) {
|
||||
out.push(v.clone());
|
||||
}
|
||||
}
|
||||
out.sort_by(|a, b| match (a.as_str(), b.as_str()) {
|
||||
(Some(x), Some(y)) => x.cmp(y),
|
||||
_ => a.to_string().cmp(&b.to_string()),
|
||||
});
|
||||
out
|
||||
}
|
||||
|
||||
fn as_str(v: Option<&Value>) -> &str {
|
||||
match v {
|
||||
Some(Value::String(s)) => s,
|
||||
_ => "",
|
||||
}
|
||||
}
|
||||
|
||||
fn bool_of(v: Option<&Value>) -> bool {
|
||||
matches!(v, Some(Value::Bool(true)))
|
||||
}
|
||||
|
||||
/// structural_store.entity(): id match first, then alias entries.
|
||||
/// Returns the index into doc["entities"].
|
||||
fn entity_index(doc: &Value, id: &str) -> Option<usize> {
|
||||
let ents = doc.get("entities")?.as_array()?;
|
||||
for (i, e) in ents.iter().enumerate() {
|
||||
if as_str(e.get("id")) == id {
|
||||
return Some(i);
|
||||
}
|
||||
}
|
||||
for (i, e) in ents.iter().enumerate() {
|
||||
let hit = e
|
||||
.get("aliases")
|
||||
.and_then(|v| v.as_array())
|
||||
.map(|a| a.iter().any(|x| as_str(Some(x)) == id))
|
||||
.unwrap_or(false);
|
||||
if hit {
|
||||
return Some(i);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// structural_store.add_entity() — returns the (possibly updated) entity's
|
||||
/// index into doc["entities"]. This module's API passes no tags, so the
|
||||
/// tags-union branch of the oracle is a no-op here by construction.
|
||||
fn add_entity(
|
||||
doc: &mut Value,
|
||||
id: &str,
|
||||
kind: &str,
|
||||
identities: Map<String, Value>,
|
||||
evidence_refs: &[Value],
|
||||
maturity: &str,
|
||||
now: &str,
|
||||
) -> Result<usize, String> {
|
||||
let ents = doc
|
||||
.get_mut("entities")
|
||||
.and_then(|v| v.as_array_mut())
|
||||
.ok_or_else(|| "KeyError: 'entities'".to_string())?;
|
||||
if let Some(idx) = ents
|
||||
.iter()
|
||||
.position(|e| as_str(e.get("id")) == id)
|
||||
{
|
||||
let e = &mut ents[idx];
|
||||
let current = as_str(e.get("maturity"));
|
||||
if !maturity.is_empty() && maturity_rank(maturity) > maturity_rank(current) {
|
||||
e["maturity"] = json!(maturity);
|
||||
}
|
||||
if !identities.is_empty() {
|
||||
let m = e
|
||||
.as_object_mut()
|
||||
.ok_or_else(|| "TypeError: entity is not a mapping".to_string())?
|
||||
.entry("identities")
|
||||
.or_insert_with(|| json!({}));
|
||||
if let Some(target) = m.as_object_mut() {
|
||||
for (k, v) in identities {
|
||||
target.insert(k, v);
|
||||
}
|
||||
}
|
||||
}
|
||||
if !evidence_refs.is_empty() {
|
||||
let existing = e
|
||||
.get("evidence_refs")
|
||||
.and_then(|v| v.as_array())
|
||||
.cloned()
|
||||
.unwrap_or_default();
|
||||
e["evidence_refs"] = json!(sorted_set_union(&existing, evidence_refs));
|
||||
}
|
||||
e["observed_at"] = json!(now);
|
||||
return Ok(idx);
|
||||
}
|
||||
let ent = json!({
|
||||
"id": id,
|
||||
"kind": kind,
|
||||
"maturity": maturity,
|
||||
"tags": [],
|
||||
"identities": Value::Object(identities),
|
||||
"evidence_refs": sorted_set_union(&[], evidence_refs),
|
||||
"observed_at": now,
|
||||
});
|
||||
ents.push(ent);
|
||||
Ok(ents.len() - 1)
|
||||
}
|
||||
|
||||
fn truncate_codepoints(s: &str, n: usize) -> String {
|
||||
s.chars().take(n).collect()
|
||||
}
|
||||
|
||||
/// Python str !r: single quotes (the repr single-vs-double quote trap the
|
||||
/// batch-4 battery caught — Rust {:?} would emit doubles).
|
||||
fn py_repr(s: &str) -> String {
|
||||
format!("'{s}'")
|
||||
}
|
||||
|
||||
/// define_region port. evidence_refs: an EMPTY/absent input mints the
|
||||
/// region-defined default (Python `x or [default]` falsiness law).
|
||||
#[allow(clippy::too_many_arguments)] // mirrors the oracle's keyword surface
|
||||
pub fn define_region(
|
||||
doc: &mut Value,
|
||||
region_id: &str,
|
||||
parent: &str,
|
||||
order: Option<i64>,
|
||||
orientation: &str,
|
||||
sizing: &str,
|
||||
visibility: &str,
|
||||
tech: &Map<String, Value>,
|
||||
provenance: &str,
|
||||
evidence_refs: &[Value],
|
||||
now: &str,
|
||||
) -> Result<Value, String> {
|
||||
let mut identities = Map::new();
|
||||
identities.insert("parent".into(), json!(parent));
|
||||
identities.insert("orientation".into(), json!(orientation));
|
||||
identities.insert("sizing".into(), json!(sizing));
|
||||
identities.insert("visibility".into(), json!(visibility));
|
||||
identities.insert("provenance".into(), json!(provenance));
|
||||
identities.insert("tech".into(), Value::Object(tech.clone()));
|
||||
if let Some(o) = order {
|
||||
identities.insert("order".into(), json!(o));
|
||||
}
|
||||
let refs: Vec<Value> = if evidence_refs.is_empty() {
|
||||
vec![json!(format!("region-defined:{provenance}"))]
|
||||
} else {
|
||||
evidence_refs.to_vec()
|
||||
};
|
||||
let idx = add_entity(
|
||||
doc,
|
||||
&format!("region:{region_id}"),
|
||||
"layout_region",
|
||||
identities,
|
||||
&refs,
|
||||
"DECLARED",
|
||||
now,
|
||||
)?;
|
||||
Ok(doc["entities"][idx].clone())
|
||||
}
|
||||
|
||||
/// bind_component port — the lock/provenance law lives here: only a user
|
||||
/// may issue a lock; the region must exist first.
|
||||
#[allow(clippy::too_many_arguments)] // mirrors the oracle's keyword surface
|
||||
pub fn bind_component(
|
||||
doc: &mut Value,
|
||||
component: &str,
|
||||
region_id: &str,
|
||||
lock: bool,
|
||||
provenance: &str,
|
||||
order: Option<i64>,
|
||||
parent_of: &str,
|
||||
anchor: &str,
|
||||
sizing: &str,
|
||||
visibility: &str,
|
||||
evidence_ref: &str,
|
||||
implementation: &str,
|
||||
framework_attrs: &Map<String, Value>,
|
||||
now: &str,
|
||||
) -> Result<Value, String> {
|
||||
let component = if component.starts_with("ui:") {
|
||||
component.to_string()
|
||||
} else {
|
||||
format!("ui:{component}")
|
||||
};
|
||||
if entity_index(doc, &format!("region:{region_id}")).is_none() {
|
||||
return Err(format!(
|
||||
"unknown region: {region_id} (define_region first)"
|
||||
));
|
||||
}
|
||||
if lock && provenance != "user" {
|
||||
return Err("lock=True requires user provenance — a scaffold/builder/model can never self-issue a lock".into());
|
||||
}
|
||||
let mut identities = Map::new();
|
||||
identities.insert("component".into(), json!(component));
|
||||
identities.insert("region".into(), json!(region_id));
|
||||
identities.insert("lock".into(), json!(lock));
|
||||
identities.insert("provenance".into(), json!(provenance));
|
||||
identities.insert("anchor".into(), json!(anchor));
|
||||
identities.insert("sizing".into(), json!(sizing));
|
||||
identities.insert("visibility".into(), json!(visibility));
|
||||
identities.insert("implementation".into(), json!(implementation));
|
||||
identities.insert("framework_attrs".into(), Value::Object(framework_attrs.clone()));
|
||||
if let Some(o) = order {
|
||||
identities.insert("order".into(), json!(o));
|
||||
}
|
||||
if !parent_of.is_empty() {
|
||||
identities.insert("parent_of".into(), json!(parent_of));
|
||||
}
|
||||
let refs = vec![json!(if evidence_ref.is_empty() {
|
||||
format!("layout-bind:{provenance}")
|
||||
} else {
|
||||
evidence_ref.to_string()
|
||||
})];
|
||||
let idx = add_entity(
|
||||
doc,
|
||||
&format!("layout:{component}"),
|
||||
"layout_contract",
|
||||
identities,
|
||||
&refs,
|
||||
"DECLARED",
|
||||
now,
|
||||
)?;
|
||||
let e = &mut doc["entities"][idx];
|
||||
let obj = e
|
||||
.as_object_mut()
|
||||
.ok_or_else(|| "TypeError: entity is not a mapping".to_string())?;
|
||||
let ext = obj.entry("ext").or_insert_with(|| json!({}));
|
||||
if let Some(ext_obj) = ext.as_object_mut() {
|
||||
// the oracle ASSIGNS a fresh ledger on every bind — re-binding
|
||||
// resets the amendment history (a fresh binding, not an append)
|
||||
ext_obj.insert("amendments".into(), json!([]));
|
||||
}
|
||||
obj.insert("status".into(), json!("ACTIVE"));
|
||||
Ok(doc["entities"][idx].clone())
|
||||
}
|
||||
|
||||
/// amend_layout_contract port. Mutation order mirrors the oracle exactly:
|
||||
/// region/order mutate BEFORE the unlock-authorization check (a rejected
|
||||
/// unlock still leaves prior mutations applied — observable doc state).
|
||||
#[allow(clippy::too_many_arguments)] // mirrors the oracle's keyword surface
|
||||
pub fn amend_layout_contract(
|
||||
doc: &mut Value,
|
||||
component: &str,
|
||||
new_region: &str,
|
||||
new_order: Option<i64>,
|
||||
unlock: bool,
|
||||
authorized_by: &str,
|
||||
reason: &str,
|
||||
evidence_ref: &str,
|
||||
now: &str,
|
||||
) -> Result<Value, String> {
|
||||
let cid = if component.starts_with("ui:") {
|
||||
component.to_string()
|
||||
} else {
|
||||
format!("ui:{component}")
|
||||
};
|
||||
let idx = entity_index(doc, &format!("layout:{cid}"))
|
||||
.ok_or_else(|| format!("no layout contract for {cid}"))?;
|
||||
{
|
||||
let e = &doc["entities"][idx];
|
||||
let locked = e
|
||||
.get("identities")
|
||||
.map(|i| bool_of(i.get("lock")))
|
||||
.unwrap_or(false);
|
||||
if locked {
|
||||
if authorized_by != "user" {
|
||||
return Err(format!(
|
||||
"{cid} is USER-LOCKED: amendment requires explicit user authorization (got authorized_by={})",
|
||||
py_repr(authorized_by)
|
||||
));
|
||||
}
|
||||
if reason.is_empty() {
|
||||
return Err("user-locked amendment requires a reason".into());
|
||||
}
|
||||
}
|
||||
}
|
||||
if !new_region.is_empty()
|
||||
&& entity_index(doc, &format!("region:{new_region}")).is_none()
|
||||
{
|
||||
return Err(format!("unknown region: {new_region}"));
|
||||
}
|
||||
let mut changes: Map<String, Value> = Map::new();
|
||||
let e = &mut doc["entities"][idx];
|
||||
if !new_region.is_empty() {
|
||||
e["identities"]["region"] = json!(new_region);
|
||||
changes.insert("region".into(), json!(new_region));
|
||||
}
|
||||
if let Some(o) = new_order {
|
||||
e["identities"]["order"] = json!(o);
|
||||
changes.insert("order".into(), json!(o));
|
||||
}
|
||||
if unlock {
|
||||
if authorized_by != "user" {
|
||||
return Err("unlock requires user authorization".into());
|
||||
}
|
||||
e["identities"]["lock"] = json!(false);
|
||||
changes.insert("lock".into(), json!(false));
|
||||
}
|
||||
let record = json!({
|
||||
"at": now,
|
||||
"authorized_by": authorized_by,
|
||||
"reason": truncate_codepoints(reason, 200),
|
||||
"evidence": evidence_ref,
|
||||
"changes": Value::Object(changes),
|
||||
});
|
||||
{
|
||||
let amendments = e["ext"]["amendments"]
|
||||
.as_array_mut()
|
||||
.ok_or_else(|| "KeyError: 'amendments'".to_string())?;
|
||||
amendments.push(record);
|
||||
}
|
||||
e["observed_at"] = json!(now);
|
||||
Ok(doc["entities"][idx].clone())
|
||||
}
|
||||
|
||||
/// record_layout_observation port — evidence, never authority: appends to
|
||||
/// doc["observations"] and returns the appended rows.
|
||||
pub fn record_layout_observation(
|
||||
doc: &mut Value,
|
||||
adapter: &str,
|
||||
observed: &Map<String, Value>,
|
||||
now: &str,
|
||||
) -> Result<Value, String> {
|
||||
if adapter.is_empty() {
|
||||
return Err("observation requires adapter identity".into());
|
||||
}
|
||||
let mut out: Vec<Value> = Vec::new();
|
||||
for (comp, obs) in observed {
|
||||
let c = if comp.starts_with("ui:") {
|
||||
comp.clone()
|
||||
} else {
|
||||
format!("ui:{comp}")
|
||||
};
|
||||
out.push(json!({
|
||||
"kind": "layout_observed",
|
||||
"component": c,
|
||||
"adapter": adapter,
|
||||
"observed": obs.clone(),
|
||||
"observed_at": now,
|
||||
}));
|
||||
}
|
||||
let obs_list = doc
|
||||
.get_mut("observations")
|
||||
.and_then(|v| v.as_array_mut())
|
||||
.ok_or_else(|| "KeyError: 'observations'".to_string())?;
|
||||
obs_list.extend(out.iter().cloned());
|
||||
Ok(Value::Array(out))
|
||||
}
|
||||
|
||||
/// dispatch(case) — the frozen-truth replay entry shared by the Rust
|
||||
/// battery and the lc_replay FFI: {fn, args, kwargs} → expected.
|
||||
/// Errors render as {"error": "Type: message"} (the freeze convention).
|
||||
pub fn dispatch(case: &Value) -> Value {
|
||||
let name = as_str(case.get("fn"));
|
||||
let args = case
|
||||
.get("args")
|
||||
.and_then(|a| a.as_array())
|
||||
.cloned()
|
||||
.unwrap_or_default();
|
||||
let kwargs = case.get("kwargs").cloned().unwrap_or(json!({}));
|
||||
let get = |i: usize| args.get(i).cloned().unwrap_or(Value::Null);
|
||||
let kstr = |k: &str, d: &str| {
|
||||
kwargs
|
||||
.get(k)
|
||||
.and_then(|v| v.as_str())
|
||||
.unwrap_or(d)
|
||||
.to_string()
|
||||
};
|
||||
let korder = |k: &str| kwargs.get(k).and_then(|v| v.as_i64());
|
||||
let kbool = |k: &str| {
|
||||
kwargs
|
||||
.get(k)
|
||||
.and_then(|v| v.as_bool())
|
||||
.unwrap_or(false)
|
||||
};
|
||||
let kmap = |k: &str| {
|
||||
kwargs
|
||||
.get(k)
|
||||
.and_then(|v| v.as_object())
|
||||
.cloned()
|
||||
.unwrap_or_default()
|
||||
};
|
||||
let krefs = |k: &str| {
|
||||
kwargs
|
||||
.get(k)
|
||||
.and_then(|v| v.as_array())
|
||||
.cloned()
|
||||
.unwrap_or_default()
|
||||
};
|
||||
let now = FROZEN_NOW;
|
||||
|
||||
let mut doc = get(0);
|
||||
// **framework_attrs catch-all: every kwarg the oracle has no named
|
||||
// parameter for rides in the open-world framework dict (insertion order).
|
||||
let framework_attrs: Map<String, Value> = kwargs
|
||||
.as_object()
|
||||
.map(|m| {
|
||||
m.iter()
|
||||
.filter(|(k, _)| {
|
||||
!matches!(
|
||||
k.as_str(),
|
||||
"lock" | "provenance" | "order" | "parent_of" | "anchor"
|
||||
| "sizing" | "visibility" | "evidence_ref"
|
||||
| "implementation"
|
||||
)
|
||||
})
|
||||
.map(|(k, v)| (k.clone(), v.clone()))
|
||||
.collect()
|
||||
})
|
||||
.unwrap_or_default();
|
||||
let r: Result<Value, String> = match name {
|
||||
"define_region" => define_region(
|
||||
&mut doc,
|
||||
as_str(args.get(1)),
|
||||
&kstr("parent", ""),
|
||||
korder("order"),
|
||||
&kstr("orientation", ""),
|
||||
&kstr("sizing", ""),
|
||||
&kstr("visibility", ""),
|
||||
&kmap("tech"),
|
||||
&kstr("provenance", "scaffold"),
|
||||
&krefs("evidence_refs"),
|
||||
now,
|
||||
),
|
||||
"bind_component" => bind_component(
|
||||
&mut doc,
|
||||
as_str(args.get(1)),
|
||||
as_str(args.get(2)),
|
||||
kbool("lock"),
|
||||
&kstr("provenance", "scaffold"),
|
||||
korder("order"),
|
||||
&kstr("parent_of", ""),
|
||||
&kstr("anchor", ""),
|
||||
&kstr("sizing", ""),
|
||||
&kstr("visibility", ""),
|
||||
&kstr("evidence_ref", ""),
|
||||
&kstr("implementation", ""),
|
||||
&framework_attrs,
|
||||
now,
|
||||
),
|
||||
"amend_layout_contract" => amend_layout_contract(
|
||||
&mut doc,
|
||||
as_str(args.get(1)),
|
||||
&kstr("new_region", ""),
|
||||
korder("new_order"),
|
||||
kbool("unlock"),
|
||||
&kstr("authorized_by", ""),
|
||||
&kstr("reason", ""),
|
||||
&kstr("evidence_ref", ""),
|
||||
now,
|
||||
),
|
||||
"record_layout_observation" => record_layout_observation(
|
||||
&mut doc,
|
||||
as_str(args.get(1)),
|
||||
&get(2).as_object().cloned().unwrap_or_default(),
|
||||
now,
|
||||
),
|
||||
_ => Err(format!("ValueError: unknown fn {name:?}")),
|
||||
};
|
||||
match r {
|
||||
Ok(v) => {
|
||||
let doc_view = json!({
|
||||
"entities": doc.get("entities").cloned().unwrap_or(json!([])),
|
||||
"observations": doc.get("observations").cloned().unwrap_or(json!([])),
|
||||
});
|
||||
json!({"result": v, "doc": doc_view})
|
||||
}
|
||||
Err(e) => {
|
||||
let typed = if e.starts_with("KeyError") || e.starts_with("TypeError") {
|
||||
e
|
||||
} else {
|
||||
format!("ValueError: {e}")
|
||||
};
|
||||
json!({ "error": typed })
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,19 @@
|
||||
//! layout_contract — check/drift kernels (dichotomy debt queue batch 4).
|
||||
//! PARITY port of src/rune/layout_contract.py check_layout / layout_drift
|
||||
//! decision logic (GO verdict, deterministic gate; store mutations +
|
||||
//! observation recording stay Python-side).
|
||||
//! layout_contract — the full lifecycle port of src/rune/layout_contract.py.
|
||||
//! check/drift decision kernels (dichotomy batch 4) + the store-mutation
|
||||
//! kernels define_region / bind_component / amend_layout_contract /
|
||||
//! record_layout_observation (dichotomy wave-5 gap-close) in kernels.rs.
|
||||
|
||||
#[path = "branch_battery.rs"]
|
||||
#[cfg(test)]
|
||||
mod branch_battery;
|
||||
|
||||
use serde_json::{json, Value};
|
||||
#[path = "gap_battery.rs"]
|
||||
#[cfg(test)]
|
||||
mod gap_battery;
|
||||
|
||||
mod kernels;
|
||||
|
||||
use serde_json::{json, Map, Value};
|
||||
use std::ffi::{CStr, CString};
|
||||
use std::os::raw::c_char;
|
||||
|
||||
@@ -181,5 +187,141 @@ pub extern "C" fn lc_drift(arg: *const c_char) -> *mut c_char {
|
||||
envelope(r)
|
||||
}
|
||||
|
||||
// ── store-mutation kernels (wave-5 gap-close): doc-envelope law ────────────
|
||||
// Each takes {"doc": ..., <params>, "now": iso} and returns
|
||||
// {"doc": updated_doc, "entity"|"observations": ...}.
|
||||
|
||||
fn a_str(a: &Value, k: &str, d: &str) -> String {
|
||||
a.get(k)
|
||||
.and_then(|v| v.as_str())
|
||||
.unwrap_or(d)
|
||||
.to_string()
|
||||
}
|
||||
|
||||
fn a_order(a: &Value, k: &str) -> Option<i64> {
|
||||
a.get(k).and_then(|v| v.as_i64())
|
||||
}
|
||||
|
||||
fn a_bool(a: &Value, k: &str) -> bool {
|
||||
a.get(k).and_then(|v| v.as_bool()).unwrap_or(false)
|
||||
}
|
||||
|
||||
fn a_map(a: &Value, k: &str) -> Map<String, Value> {
|
||||
a.get(k)
|
||||
.and_then(|v| v.as_object())
|
||||
.cloned()
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
fn a_refs(a: &Value, k: &str) -> Vec<Value> {
|
||||
a.get(k)
|
||||
.and_then(|v| v.as_array())
|
||||
.cloned()
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
/// lc_define_region({"doc", "region_id", parent/order/..., "now"})
|
||||
#[no_mangle]
|
||||
pub extern "C" fn lc_define_region(arg: *const c_char) -> *mut c_char {
|
||||
let r = read_arg(arg).and_then(|a| {
|
||||
let mut doc = a.get("doc").cloned().ok_or("arg.doc required")?;
|
||||
let region_id = a.get("region_id").and_then(|v| v.as_str()).unwrap_or("").to_string();
|
||||
let now = a_str(&a, "now", kernels::FROZEN_NOW);
|
||||
let e = kernels::define_region(
|
||||
&mut doc,
|
||||
®ion_id,
|
||||
&a_str(&a, "parent", ""),
|
||||
a_order(&a, "order"),
|
||||
&a_str(&a, "orientation", ""),
|
||||
&a_str(&a, "sizing", ""),
|
||||
&a_str(&a, "visibility", ""),
|
||||
&a_map(&a, "tech"),
|
||||
&a_str(&a, "provenance", "scaffold"),
|
||||
&a_refs(&a, "evidence_refs"),
|
||||
&now,
|
||||
)?;
|
||||
Ok(json!({"doc": doc, "entity": e}))
|
||||
});
|
||||
envelope(r)
|
||||
}
|
||||
|
||||
/// lc_bind_component({"doc", "component", "region_id", lock/..., "now"})
|
||||
#[no_mangle]
|
||||
pub extern "C" fn lc_bind_component(arg: *const c_char) -> *mut c_char {
|
||||
let r = read_arg(arg).and_then(|a| {
|
||||
let mut doc = a.get("doc").cloned().ok_or("arg.doc required")?;
|
||||
let component = a.get("component").and_then(|v| v.as_str()).unwrap_or("").to_string();
|
||||
let region_id = a.get("region_id").and_then(|v| v.as_str()).unwrap_or("").to_string();
|
||||
let now = a_str(&a, "now", kernels::FROZEN_NOW);
|
||||
let e = kernels::bind_component(
|
||||
&mut doc,
|
||||
&component,
|
||||
®ion_id,
|
||||
a_bool(&a, "lock"),
|
||||
&a_str(&a, "provenance", "scaffold"),
|
||||
a_order(&a, "order"),
|
||||
&a_str(&a, "parent_of", ""),
|
||||
&a_str(&a, "anchor", ""),
|
||||
&a_str(&a, "sizing", ""),
|
||||
&a_str(&a, "visibility", ""),
|
||||
&a_str(&a, "evidence_ref", ""),
|
||||
&a_str(&a, "implementation", ""),
|
||||
&a_map(&a, "framework_attrs"),
|
||||
&now,
|
||||
)?;
|
||||
Ok(json!({"doc": doc, "entity": e}))
|
||||
});
|
||||
envelope(r)
|
||||
}
|
||||
|
||||
/// lc_amend_layout_contract({"doc", "component", new_region/..., "now"})
|
||||
#[no_mangle]
|
||||
pub extern "C" fn lc_amend_layout_contract(arg: *const c_char) -> *mut c_char {
|
||||
let r = read_arg(arg).and_then(|a| {
|
||||
let mut doc = a.get("doc").cloned().ok_or("arg.doc required")?;
|
||||
let component = a.get("component").and_then(|v| v.as_str()).unwrap_or("").to_string();
|
||||
let now = a_str(&a, "now", kernels::FROZEN_NOW);
|
||||
let e = kernels::amend_layout_contract(
|
||||
&mut doc,
|
||||
&component,
|
||||
&a_str(&a, "new_region", ""),
|
||||
a_order(&a, "new_order"),
|
||||
a_bool(&a, "unlock"),
|
||||
&a_str(&a, "authorized_by", ""),
|
||||
&a_str(&a, "reason", ""),
|
||||
&a_str(&a, "evidence_ref", ""),
|
||||
&now,
|
||||
)?;
|
||||
Ok(json!({"doc": doc, "entity": e}))
|
||||
});
|
||||
envelope(r)
|
||||
}
|
||||
|
||||
/// lc_record_observation({"doc", "adapter", "observed", "now"})
|
||||
#[no_mangle]
|
||||
pub extern "C" fn lc_record_observation(arg: *const c_char) -> *mut c_char {
|
||||
let r = read_arg(arg).and_then(|a| {
|
||||
let mut doc = a.get("doc").cloned().ok_or("arg.doc required")?;
|
||||
let adapter = a.get("adapter").and_then(|v| v.as_str()).unwrap_or("").to_string();
|
||||
let now = a_str(&a, "now", kernels::FROZEN_NOW);
|
||||
let out = kernels::record_layout_observation(
|
||||
&mut doc,
|
||||
&adapter,
|
||||
&a_map(&a, "observed"),
|
||||
&now,
|
||||
)?;
|
||||
Ok(json!({"doc": doc, "observations": out}))
|
||||
});
|
||||
envelope(r)
|
||||
}
|
||||
|
||||
/// lc_replay(case) — the frozen-truth replay surface: {fn, args, kwargs}
|
||||
/// → expected (raw; NOT doc-wrapped). The parity script drives this.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn lc_replay(arg: *const c_char) -> *mut c_char {
|
||||
let r = read_arg(arg).map(|a| kernels::dispatch(&a));
|
||||
envelope(r)
|
||||
}
|
||||
|
||||
// NOTE: no free-symbol export (capability_scoring's cs_free_string serves
|
||||
// the whole process — duplicate symbols break the cdylib link).
|
||||
@@ -0,0 +1 @@
|
||||
0
|
||||
@@ -0,0 +1 @@
|
||||
0
|
||||
@@ -0,0 +1 @@
|
||||
0
|
||||
@@ -0,0 +1 @@
|
||||
0
|
||||
@@ -0,0 +1 @@
|
||||
0
|
||||
@@ -0,0 +1 @@
|
||||
0
|
||||
@@ -0,0 +1 @@
|
||||
0
|
||||
@@ -0,0 +1 @@
|
||||
0
|
||||
+93
-19
@@ -1,27 +1,101 @@
|
||||
# hook_engine dichotomy gap-close — verification summary
|
||||
# layout_contract dichotomy gap-close — verification summary
|
||||
|
||||
- **Module**: hook_engine (GO port, src/go/hook-engine)
|
||||
- **Parity SHA**: cba7c986 (feat: close PARTIAL gaps — dichotomy wave; on gitea/master)
|
||||
- **Phase B SHA**: d38a4317 (refactor: Phase B language-advantage pass; memoization + oracle main() CLI)
|
||||
- **Contract**: BUILDER-2026-10-13T15:18:58Z-fb34a0-factory-gap-he — closed
|
||||
(submit→approved→building→verifying→closed walked in ONE process,
|
||||
15:18:57Z–15:20:37Z, evidence "parity cba7c986; phase B d38a4317" on every checkpoint)
|
||||
Module: `src/rune/layout_contract.py` (oracle 336 lines, 6 fns) →
|
||||
`src/rust/layout_contract` (Rust crate). Port: 2/6 → **6/6 FULL (RUST)**.
|
||||
|
||||
## Commands and exit codes
|
||||
Wave-5 gap-close. The four audit-missing functions ported name-preserving:
|
||||
`define_region`, `bind_component`, `amend_layout_contract`,
|
||||
`record_layout_observation` (src/rust/layout_contract/src/kernels.rs).
|
||||
|
||||
| Check | Command | Exit | Evidence |
|
||||
|---|---|---|---|
|
||||
| fmt+vet+build+tests | `gofmt -l . && go vet ./... && go build ./... && go test -v ./...` (in src/go/hook-engine) | 0 | `verification/hook-engine-phaseb-gotest.log` / `.exit` — 7/7 PASS (frozen battery incl. TestPlacementParity, 14 cases / 8 fns) |
|
||||
| CLI smoke | scan, validate, recommend, report, hook | 0 | `verification/hook-engine-phaseb-cli-smoke.log` / `.exit` — scan 265 discovered / 12 tagged; validate clean; hook pre_turn payload echoed |
|
||||
| contract close walk | `python3 scripts/close_contract_hook_engine.py` | 0 | `verification/close-walk.log` / `.exit` — FINAL PHASE: closed |
|
||||
## What landed
|
||||
|
||||
## Files
|
||||
- **Rust kernels** (`kernels.rs`): pure doc-in/doc-out store-mutation
|
||||
kernels; `now` is an argument (no-clock law); structural_store's
|
||||
add_entity/entity semantics replicated (high-water maturity, sorted-set
|
||||
evidence union on non-empty input, identities key-level update,
|
||||
alias-following resolution, observed_at refresh). dispatch(case) is the
|
||||
frozen-truth replay entry.
|
||||
- **FFI** (`lib.rs`): lc_define_region / lc_bind_component /
|
||||
lc_amend_layout_contract / lc_record_observation (doc-envelope:
|
||||
{doc, entity|observations}) + lc_replay (raw replay surface). No free
|
||||
symbol (capability_scoring's cs_free_string serves the process).
|
||||
- **Facade delegation** (`src/rune/layout_contract.py`): the 4 fns
|
||||
delegate when the core is built; RuntimeError → ValueError translation
|
||||
(message is the contract); kernel doc-view absorbed with
|
||||
identity-preserving in-place merge (lc_loader.absorb) so aliasing
|
||||
semantics (returned entity IS the stored one) survive round-trips;
|
||||
return re-resolved from the store (structural_store.promote pattern).
|
||||
- **Loader** (`src/rune/lc_loader.py`): new symbols + absorb.
|
||||
- **Freeze rig** (`scripts/lc_freeze_seams/sitecustomize.py`): blanket
|
||||
RUNE_*_CORE=python + EXPLICIT RUNE_LC_CORE/RUNE_STRUCTURAL_CORE pins
|
||||
(the blanket loop only pins vars already in the env — a missing pin let
|
||||
the first re-freeze run through live delegation and CAUGHT the absorb
|
||||
divergence), frozen clock on both now seams, DOC-CARRYING wrappers
|
||||
({"result", "doc": {entities, observations}}), errors frozen as
|
||||
{"error": "Type: message"}.
|
||||
- **Truth**: `frozen_lc_gap_truth.json` — 35-case envelope corpus
|
||||
(frozen_now 2026-10-14T00:00:00Z) via the shared harness, embedded
|
||||
VERBATIM in gap_battery.rs (include_str!).
|
||||
- **Battery** (`gap_battery.rs`): verbatim replay (35) + lc_replay FFI
|
||||
roundtrip + branch proofs (sorted-set dedup, maturity high-water,
|
||||
alias-following resolution, 200-codepoint reason truncation, re-bind
|
||||
ledger RESET, unlock-rejection leaves prior mutations).
|
||||
- **serde_json preserve_order** (Cargo.toml): the out-list order of
|
||||
record_layout_observation follows observed-dict insertion order (the
|
||||
json insertion-order quirk); default BTreeMap would silently sort.
|
||||
|
||||
- `scripts/close_contract_hook_engine.py` — the one-process close walk (committed)
|
||||
- `store/scaffold/segments/entities/contract__BUILDER-2026-10-13T15__18__58Z-fb34a0-factory-gap-he.json` — contract entity (phase: closed)
|
||||
- `store/scaffold/docs/BUILDER-2026-10-13T15:18:58Z-fb34a0-factory-gap-he.md` — auto mini-doc
|
||||
- `store/scaffold/segments/observations.jsonl` — walk journal rows
|
||||
## Real bugs the ladder caught (fixed pre-commit)
|
||||
|
||||
---
|
||||
1. **Amendments-ledger reset**: the oracle's re-bind ASSIGNS a fresh
|
||||
ledger (`e["ext"]["amendments"] = []`); the first Rust version
|
||||
inserted-if-missing (preserved history). Caught by the live-facade
|
||||
smoke (both-pins doc diff), discriminating frozen case added
|
||||
(re-bind over a NON-EMPTY ledger → []).
|
||||
2. **Recursive-merge absorb divergence**: identities must merge with
|
||||
wholesale key assignment (add_entity's `.update()` law — tech reset to
|
||||
{} on no-op re-define); a recursive dict merge preserved the old tech
|
||||
value. Caught by the parity script's LIVE layer re-freeze.
|
||||
3. **`**framework_attrs` catch-all**: bind_component has NO
|
||||
`framework_attrs` parameter — framework detail rides arbitrary
|
||||
kwargs; the dispatch collects unknown kwargs (frozen case passes them
|
||||
as bare kwargs like real callers).
|
||||
4. **Insertion-order stability**: preserve_order feature (see above).
|
||||
|
||||
## Verification evidence (all paths under verification/)
|
||||
|
||||
| stage | evidence | result |
|
||||
|---|---|---|
|
||||
| cargo build --release | lc_final_clippy.exit (+ .log on disk) (build ok line) | green |
|
||||
| cargo test (battery) | lc_final_cargo_test.exit (+ .log on disk) | 10/10 ok |
|
||||
| clippy -D warnings | lc_final_clippy.exit (+ .log on disk) | exit=0 |
|
||||
| freeze_truth --verify | lc_final_freeze_verify.exit (+ .log on disk) | 35 cases byte-identical |
|
||||
| live parity (frozen+live) | lc_final_parity.exit (+ .log on disk) | 35/35 verbatim + byte-identical re-freeze, PARITY 100% |
|
||||
| Python tests, delegation-live pin | lc_final_pytests_auto.exit (+ .log on disk) | 64/64 passed |
|
||||
| Python tests, RUNE_LC_CORE=python pin | lc_final_pytests_python.exit (+ .log on disk) | 64/64 passed |
|
||||
| facade smoke both pins (aliasing, ledger reset, error contracts) | lc_smoke2.exit (+ .log on disk) | OK |
|
||||
| dichotomy audit | lc_final_audit.exit (+ .log on disk) | layout_contract 6/6 FULL RUST; 85/192 modules, 1449→1453 fns |
|
||||
|
||||
## Full-suite baseline note (NOT this port)
|
||||
|
||||
The full `pytest tests/` run has 60 pre-existing failures (memory
|
||||
selection, scaffold drift, lint baseline counts, spine, umbrella
|
||||
manifest...) — identical failure sets with and without this port's
|
||||
facade changes (verified by stashing src/rune changes and re-running the
|
||||
affected subset: 26 failed baseline vs 26 failed with the port; sets
|
||||
byte-identical). Coordinator-owned master debt; the port's own surface
|
||||
(layout_contract + real_proof + obligations + construction_interface
|
||||
consumer) is 64/64 green in BOTH pins.
|
||||
|
||||
## Oracle quirks honored
|
||||
|
||||
- `x or [default]` falsiness (explicit empty evidence_refs mints the
|
||||
region-defined default) — frozen case r-empty.
|
||||
- Python repr single quotes in the USER-LOCKED error (py_repr helper;
|
||||
the batch-4 repr trap).
|
||||
- reason[:200] truncation is CODEPOINT-based (branch test with é).
|
||||
- amend mutates region/order BEFORE the unlock-authorization check
|
||||
(rejected unlock leaves prior mutations applied — observable doc
|
||||
state; frozen + branch tests).
|
||||
- json insertion-order (preserve_order).
|
||||
- Exception message (not type) is the envelope contract; the facade
|
||||
translates kernel errors back to ValueError for callers.
|
||||
Reference in new issue
Block a user