Compare commits

...
14 Commits
Author SHA1 Message Date
conductor da481e744d chore(release): auto-bump version to 0.271.0 — earned by # (minor: converged release (push-to-master bump))
Auto-version and Release / release (push) Successful in 53s
deploy / deploy (push) Skipped
CI Pipeline / lint (push) Canceled after 0s
CI Pipeline / unit-tests (push) Canceled after 0s
CI Pipeline / go-surface (push) Canceled after 0s
CI Pipeline / rust-surface (push) Canceled after 0s
CI Pipeline / canary (push) Canceled after 0s
CI Pipeline / best-practices (push) Canceled after 0s
CI Pipeline / documentation (push) Canceled after 0s
CI Pipeline / type-check (push) Canceled after 0s
CI Pipeline / gitea-offline (push) Canceled after 0s
CI Pipeline / required-checks (push) Canceled after 0s
CI Pipeline / CI Pipeline / notify-event-bridge (push) Canceled after 0s
Promotion record: (unknown)
2026-10-13 10:26:35 +01:00
Nick c66d4babbe refactor(structural_store): Phase B language-advantage pass — dichotomy wave
CI Pipeline / best-practices (push) Successful in 8s
CI Pipeline / documentation (push) Successful in 8s
CI Pipeline / rust-surface (push) Failing after 13s
CI Pipeline / go-surface (push) Failing after 15s
CI Pipeline / gitea-offline (push) Successful in 20s
CI Pipeline / required-checks (push) Canceled after 0s
CI Pipeline / CI Pipeline / notify-event-bridge (push) Canceled after 0s
CI Pipeline / type-check (push) Canceled after 32s
CI Pipeline / canary (push) Canceled after 41s
Auto-version and Release / release (push) Canceled after 48s
deploy / deploy (push) Canceled after 26s
CI Pipeline / lint (push) Canceled after 45s
CI Pipeline / unit-tests (push) Canceled after 45s
Bundle the oracle's add_relationship keyword arguments into RelSpec: the
7-argument positional form was the last clippy-suppressed wart and the only
call shape that diverged from the oracle's keyword call sites. Behavior
unchanged — parity re-verified 4387/4387, Rust battery 32/32, clippy clean.
2026-10-13 10:25:49 +01:00
Nick 201530bfc4 feat(structural_store): close PARTIAL gaps — dichotomy wave
Port the 21 audit-missing functions name-preserving to the existing Rust
crate (gap.rs: _ss_core probe, _sorted, new_doc, _redact_secrets+_is_secret_key,
save, canonical_digest, snapshot, alias, spine_uri, resolve_uri,
_doc_authorities, authority_health, bind_task, task_entities, impact_frontier,
record_observed_impact, predicted_vs_observed, structural_events, diff,
dedupe_observations), 19 doc-envelope FFI ss_* entry points, ctypes loader
mirroring broker_loader.py, facade delegation with RUNE_STRUCTURAL_CORE=python
pin. Fix-first: promote ordering (unknown-id silent), alias-aware entity
resolution, resolve_status verbatim-status + pythonic conflicts, supersede
add_relationship law, authority_failed observation, SCHEMA_VERSION 1.

Frozen truth: 86 cases generated against the live oracle (pinned clock,
deep-copied inputs), embedded verbatim in cfg(test) battery. Live parity
100%: 4387 checks, 0 failures (frozen 86 + fuzz 4300 + live 1).

Contract BUILDER-2026-10-13T00:32:22Z-bc3aae-factory-gap-ss.
2026-10-13 10:25:49 +01:00
Nick 57fc6a61cf chore(factory): claims — context_broker verified (6/0, 76-row truth); audit regenerated
CI Pipeline / best-practices (push) Successful in 7s
CI Pipeline / documentation (push) Successful in 4s
CI Pipeline / rust-surface (push) Failing after 14s
CI Pipeline / go-surface (push) Failing after 16s
CI Pipeline / gitea-offline (push) Successful in 17s
Auto-version and Release / release (push) Successful in 1m1s
CI Pipeline / type-check (push) Failing after 49s
CI Pipeline / canary (push) Failing after 1m13s
CI Pipeline / lint (push) Failing after 1m26s
CI Pipeline / required-checks (push) Canceled after 0s
CI Pipeline / CI Pipeline / notify-event-bridge (push) Canceled after 0s
CI Pipeline / unit-tests (push) Canceled after 2m26s
deploy / deploy (push) Canceled after 2m11s
2026-10-13 10:23:28 +01:00
conductor 4ed47a7332 chore(release): auto-bump version to 0.270.0 — earned by # (minor: converged release (push-to-master bump))
deploy / deploy (push) Skipped
CI Pipeline / documentation (push) Successful in 6s
CI Pipeline / best-practices (push) Successful in 8s
CI Pipeline / rust-surface (push) Failing after 14s
CI Pipeline / go-surface (push) Failing after 17s
CI Pipeline / gitea-offline (push) Successful in 17s
Auto-version and Release / release (push) Successful in 54s
CI Pipeline / type-check (push) Failing after 42s
CI Pipeline / canary (push) Failing after 1m9s
CI Pipeline / lint (push) Failing after 1m22s
CI Pipeline / unit-tests (push) Failing after 6m25s
CI Pipeline / required-checks (push) Failing after 1s
CI Pipeline / CI Pipeline / notify-event-bridge (push) Failing after 4s
Promotion record: (unknown)
2026-10-13 10:08:13 +01:00
Nick 63304b0984 refactor(context_broker): Phase B language-advantage pass — dichotomy wave
CI Pipeline / rust-surface (push) Failing after 17s
CI Pipeline / go-surface (push) Failing after 21s
CI Pipeline / best-practices (push) Successful in 5s
CI Pipeline / documentation (push) Successful in 4s
CI Pipeline / gitea-offline (push) Successful in 15s
CI Pipeline / required-checks (push) Canceled after 0s
CI Pipeline / CI Pipeline / notify-event-bridge (push) Canceled after 0s
CI Pipeline / type-check (push) Canceled after 26s
Auto-version and Release / release (push) Canceled after 1m0s
CI Pipeline / lint (push) Canceled after 51s
CI Pipeline / canary (push) Canceled after 50s
deploy / deploy (push) Canceled after 1m6s
CI Pipeline / unit-tests (push) Canceled after 56s
2026-10-13 10:04:39 +01:00
Nick 8119914fc3 feat(context_broker): close PARTIAL gaps — dichotomy wave 2026-10-13 10:04:39 +01:00
Nick a829834f8d chore(factory): claims — build_mode verified (6/0, was BLOCKED); 6 gap-closers spawned (hook_engine, elastic_cognition, contracts, adaptive_router, memory_selector, construction_pattern); audit regenerated
CI Pipeline / best-practices (push) Successful in 9s
CI Pipeline / documentation (push) Successful in 10s
CI Pipeline / rust-surface (push) Failing after 16s
CI Pipeline / go-surface (push) Failing after 19s
CI Pipeline / gitea-offline (push) Successful in 20s
CI Pipeline / type-check (push) Failing after 46s
Auto-version and Release / release (push) Successful in 1m2s
CI Pipeline / canary (push) Failing after 1m20s
CI Pipeline / lint (push) Failing after 1m32s
CI Pipeline / unit-tests (push) Failing after 6m36s
CI Pipeline / required-checks (push) Failing after 1s
deploy / deploy (push) Failing after 6m25s
CI Pipeline / CI Pipeline / notify-event-bridge (push) Failing after 4s
2026-10-13 09:42:58 +01:00
Nick eda992ade8 chore(provider_budget): close gap-closing contract tooling
CI Pipeline / rust-surface (push) Failing after 17s
CI Pipeline / go-surface (push) Failing after 21s
CI Pipeline / best-practices (push) Successful in 3s
CI Pipeline / documentation (push) Successful in 4s
CI Pipeline / gitea-offline (push) Successful in 15s
CI Pipeline / required-checks (push) Canceled after 0s
CI Pipeline / CI Pipeline / notify-event-bridge (push) Canceled after 0s
CI Pipeline / lint (push) Canceled after 46s
CI Pipeline / unit-tests (push) Canceled after 46s
CI Pipeline / canary (push) Canceled after 47s
CI Pipeline / type-check (push) Canceled after 26s
Auto-version and Release / release (push) Canceled after 55s
deploy / deploy (push) Canceled after 56s
2026-10-13 09:42:07 +01:00
conductor facbb52b7e chore(release): auto-bump version to 0.269.1 — earned by # (patch: converged release (push-to-master bump))
deploy / deploy (push) Skipped
CI Pipeline / best-practices (push) Successful in 8s
CI Pipeline / documentation (push) Successful in 3s
CI Pipeline / rust-surface (push) Failing after 15s
CI Pipeline / go-surface (push) Failing after 16s
CI Pipeline / gitea-offline (push) Successful in 15s
Auto-version and Release / release (push) Successful in 53s
CI Pipeline / type-check (push) Failing after 48s
CI Pipeline / lint (push) Failing after 1m23s
CI Pipeline / canary (push) Failing after 2m15s
CI Pipeline / required-checks (push) Canceled after 0s
CI Pipeline / CI Pipeline / notify-event-bridge (push) Canceled after 0s
CI Pipeline / unit-tests (push) Canceled after 12m25s
Promotion record: (unknown)
2026-10-13 09:29:37 +01:00
Nick f4f099bcde fix(build_mode): move DispatchGap + typed accessors into production code — dichotomy wave
CI Pipeline / rust-surface (push) Failing after 15s
CI Pipeline / go-surface (push) Failing after 17s
CI Pipeline / best-practices (push) Successful in 3s
CI Pipeline / documentation (push) Successful in 3s
CI Pipeline / gitea-offline (push) Successful in 15s
CI Pipeline / required-checks (push) Canceled after 0s
CI Pipeline / CI Pipeline / notify-event-bridge (push) Canceled after 0s
CI Pipeline / canary (push) Canceled after 51s
Auto-version and Release / release (push) Canceled after 58s
CI Pipeline / type-check (push) Canceled after 36s
CI Pipeline / lint (push) Canceled after 55s
CI Pipeline / unit-tests (push) Canceled after 55s
deploy / deploy (push) Canceled after 1m5s
Phase B's ReplayPipeline (phaseb.go) called dispatchGap/mustJSON and the
argMap/strList/objList accessors that lived in a _test.go file: go test
passed but go build broke. Promoted to exported production helpers in
phaseb.go (DispatchGap/MustJSON/ArgMap/StrList/ObjList); the test file
keeps a thin dispatchGap wrapper for the _save_state TempDir case.
go build + vet + test + -race all green.
2026-10-13 09:28:35 +01:00
Nick 7fd1df00be chore(store): close build_mode dichotomy contract BUILDER-2026-10-13T02:02:02Z-dad27f-factory-gap-bm (feat 3612786d, phase B 05fab134) 2026-10-13 09:28:35 +01:00
Nick 12cca6e682 chore(factory): claims — night_mode verified (6/0, 9-row bare frozen_truth.json); verify script gains bare frozen_truth.json name variant
CI Pipeline / best-practices (push) Successful in 9s
CI Pipeline / documentation (push) Successful in 9s
CI Pipeline / rust-surface (push) Failing after 16s
CI Pipeline / go-surface (push) Failing after 19s
CI Pipeline / gitea-offline (push) Successful in 18s
Auto-version and Release / release (push) Successful in 51s
CI Pipeline / type-check (push) Failing after 50s
CI Pipeline / required-checks (push) Canceled after 0s
CI Pipeline / CI Pipeline / notify-event-bridge (push) Canceled after 0s
CI Pipeline / unit-tests (push) Canceled after 1m11s
CI Pipeline / lint (push) Canceled after 1m11s
deploy / deploy (push) Canceled after 56s
CI Pipeline / canary (push) Canceled after 1m16s
2026-10-13 09:27:24 +01:00
Nick d00f07d3ea chore(store): close night_mode dichotomy contract BUILDER-2026-10-13T03:16:06Z-57d4cc-factory-gap-nm (parity 150f5b86, phase B 709c2ca4)
CI Pipeline / best-practices (push) Successful in 10s
CI Pipeline / documentation (push) Successful in 5s
CI Pipeline / rust-surface (push) Failing after 18s
CI Pipeline / go-surface (push) Failing after 21s
CI Pipeline / gitea-offline (push) Successful in 16s
Auto-version and Release / release (push) Successful in 58s
CI Pipeline / type-check (push) Failing after 52s
CI Pipeline / lint (push) Failing after 1m29s
CI Pipeline / canary (push) Failing after 2m24s
deploy / deploy (push) Failing after 16m13s
CI Pipeline / unit-tests (push) Failing after 17m52s
CI Pipeline / required-checks (push) Failing after 1s
CI Pipeline / CI Pipeline / notify-event-bridge (push) Failing after 6s
2026-10-13 09:05:10 +01:00
35 changed files with 3091641 additions and 3071608 deletions

No files matched your search

+1 -1
View File
@@ -1,6 +1,6 @@
# Rune
**Current release: v0.269.0**
**Current release: v0.271.0**
**Rune is a persistent personal agent designed to accumulate experience, develop reusable competence, and remain recognisably the same agent even when its underlying foundation model changes.**
+7 -7
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: 27%** (52/192 runtime modules at FULL)
**FULL completion: 28%** (54/192 runtime modules at FULL)
Function-level detail: 53% (1253/2376 runtime functions matched) — secondary; includes in-flight work
Function-level detail: 54% (1290/2376 runtime functions matched) — secondary; includes in-flight work
| Status | Modules | Lines | Meaning |
|---|---|---|---|
| FULL | 52 | 19768 | 80%+ of functions ported |
| PARTIAL | 96 | 42258 | 20–80% — real logic exists, gaps named below |
| FULL | 54 | 21059 | 80%+ of functions ported |
| PARTIAL | 94 | 40967 | 20–80% — real logic exists, gaps named below |
| NOT TOUCHED | 44 | 11346 | no name-preserving implementation |
| Total runtime | 192 | 73372 | |
@@ -68,7 +68,6 @@ Function-level detail: 53% (1253/2376 runtime functions matched) — secondary;
|---|---|---|---|
| memory_store | 2333 | 30/78 | _ctx_budget, now_iso, ensure_memory_type_column, _embed, _temp_json, _entities_to_json |
| rune_operator | 2746 | 25/68 | _build_model_router, _sorted_tool_names, _filter_safety_noise, _wire_auto_filer, _reconcile_guarded, get_operator |
| context_broker | 870 | 6/28 | _py_default_estimator, _cc_core, _py_default_budget, _py_default_memory_budget, _py_memory_disabled_value, reset_discovered_windows |
| construction_interface | 1193 | 6/27 | _to_stage_check, _guard_specs_from_obligations, t_layout, _obligations_projection, _obligation_scope_files, main |
| structural_store | 690 | 13/34 | _ss_core, _sorted, new_doc, _redact_secrets, save, canonical_digest |
| provider_registry | 679 | 11/28 | _ctx_budget, _format_tools_openai, _execute_tool_calls, _execute_tool_calls_anthropic, get_registry, connect_provider |
@@ -78,7 +77,6 @@ Function-level detail: 53% (1253/2376 runtime functions matched) — secondary;
| safety_override | 574 | 7/22 | _keyword_stopwords, _cc_probe, cc_loader_pin, self_test, _py_matches, escalation_rate |
| spine | 975 | 20/35 | _py_sorted, _module_imports, _py_declared_surfaces, _py_project_meta_surfaces, _gather_meta_inputs, _rust_core |
| cron_engine | 440 | 13/27 | get_cron_engine, run_task, start_engine, stop_engine, to_dict, fire_on_task_complete |
| night_mode | 421 | 5/19 | _ctx_budget, get_night_mode, start_night_mode, stop_night_mode, get_status, get_report |
| tool_executor | 1836 | 33/47 | _execute_tool_now, serialize_prepared_tool, deserialize_prepared_tool, confirm_write, _resolve_execution_cwd, _run_command |
| cache | 492 | 6/17 | _record_hit, _record_miss, flush_stats, get_cache, put_cache, invalidate_scope |
| elastic_cognition | 774 | 3/14 | _ctx_budget, _build_constitution, _get_constitution, _load_cloud_config, execute_at_tier, _execute_deterministic |
@@ -169,6 +167,7 @@ Function-level detail: 53% (1253/2376 runtime functions matched) — secondary;
|---|---|---|---|
| curator | 1838 | 43/43 | GO |
| sleep_time | 1056 | 26/27 | GO |
| context_broker | 870 | 28/28 | GO |
| local_tool_runtime | 809 | 30/30 | GO |
| memory_consolidation | 770 | 23/23 | GO |
| persona_gate | 724 | 26/26 | GO |
@@ -179,7 +178,7 @@ Function-level detail: 53% (1253/2376 runtime functions matched) — secondary;
| capability_ledger | 571 | 18/19 | RUST |
| drift_ledger | 548 | 20/22 | RUST |
| failure_auto_filer | 527 | 24/24 | GO |
| build_mode | 521 | 32/33 | GO |
| build_mode | 521 | 33/33 | GO |
| event_wait | 518 | 14/14 | GO |
| worthiness | 469 | 12/14 | unclassified |
| tool_protocol | 461 | 22/27 | GO |
@@ -188,6 +187,7 @@ Function-level detail: 53% (1253/2376 runtime functions matched) — secondary;
| goal | 453 | 20/20 | GO |
| release_notes | 446 | 16/16 | GO |
| model_routing | 432 | 13/13 | GO |
| night_mode | 421 | 19/19 | GO |
| build_basis | 375 | 11/13 | RUST |
| attention_capsule | 365 | 6/7 | RUST |
| provider_budget | 357 | 20/20 | GO |
+10 -4
View File
@@ -6,10 +6,12 @@
"auto_tasks",
"behaviour_ledger",
"build_basis",
"build_mode",
"capability_ledger",
"concept_topology",
"construction_intent",
"construction_patterns_builtin",
"context_broker",
"curator",
"drift-ledger",
"drift_ledger",
@@ -30,6 +32,7 @@
"message_graph",
"message_queue",
"model_routing",
"night_mode",
"openai_compat_transport",
"outcome_ledger",
"outcome_utility",
@@ -66,16 +69,19 @@
"research_budget": "rune-wave-be",
"evidence_capsule": "rune-wave-be",
"builder_bridge_cli": "rune-wave-be",
"context_broker": "rune-wave-bf",
"structural_store": "rune-wave-bh",
"growth_delta": "rune-wave-bl",
"safety_override": "rune-wave-bn",
"construction_decision": "rune-wave-bo",
"provider_registry": "rune-wave-bq",
"night_mode": "rune-wave-br",
"build_mode": "rune-wave-bs [BLOCKED: go build fails \u2014 undefined dispatchGap/mustJSON in phaseb.go]",
"construction_interface": "rune-wave-bu",
"spine": "rune-wave-bv"
"spine": "rune-wave-bv",
"hook_engine": "rune-wave-bw",
"elastic_cognition": "rune-wave-bx",
"contracts": "rune-wave-by",
"adaptive_router": "rune-wave-bz",
"memory_selector": "rune-wave-ca",
"construction_pattern": "rune-wave-cb"
},
"excluded": [
"TUI-LAST modules",
+1 -1
View File
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project]
name = "rune"
version = "0.269.0"
version = "0.271.0"
description = "Rune — a stateful coding assistant with persistent memory, orchestration, and TUI"
requires-python = ">=3.10"
+180
View File
@@ -0,0 +1,180 @@
#!/usr/bin/env python3
"""Live parity check: the Go context-broker binary vs the LIVE Python oracle
(RUNE_COGNITION_CORE=python pinned AND delegation auto), fuzz + edge cases.
Complements the frozen battery (truth.json) with generated inputs."""
from __future__ import annotations
import json
import os
import random
import string
import subprocess
import sys
import tempfile
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(ROOT / "src"))
BIN = Path("/tmp/cb-live-parity")
def build():
subprocess.run(["go", "build", "-o", str(BIN), "."],
cwd=ROOT / "src/go/context-broker", check=True)
def go_eval(payload: dict) -> dict:
proc = subprocess.run([str(BIN), "eval"], input=json.dumps(payload),
capture_output=True, text=True)
if proc.returncode != 0:
raise RuntimeError(f"go eval failed: {proc.stderr}")
return json.loads(proc.stdout)
fails = 0
checked = 0
def check(name, got, want):
global fails, checked
checked += 1
if got != want:
fails += 1
print(f"DIVERGE {name}: go={got!r} py={want!r}")
def est_case(text):
check(f"est[{text!r:.40}]", go_eval({"op": "est", "text": text}),
py._py_default_estimator(text))
def budget_case(w):
check(f"budget[{w}]", go_eval({"op": "budget", "window": w}),
py._py_default_budget(w))
def membud_case(w):
check(f"membud[{w}]", go_eval({"op": "membud", "window": w}),
py._py_default_memory_budget(w))
def memoff_case(v):
check(f"memoff[{v!r}]", go_eval({"op": "memoff", "value": v}),
py._py_memory_disabled_value(v))
def through_case(messages, through):
want = py._through_id(json.loads(json.dumps(messages)), through)
got = go_eval({"op": "through", "messages": messages, "through": through})
check(f"through[{through}/{len(messages)}]", got, want)
rng = random.Random(2479)
os.environ["RUNE_COGNITION_CORE"] = "python"
from rune import context_broker as py # noqa: E402
# fuzz: estimator
for _ in range(200):
n = rng.randint(0, 300)
est_case("".join(rng.choice(string.printable[:94]) for _ in range(n)))
est_case("héllo wörld ünïcode ✓ 日本語テキスト")
est_case("🎉 emoji surrogate pair")
# fuzz: budgets (incl. boundaries around max(2048, w/4) crossover)
for w in [0, 1, 2559, 2560, 2561, 8191, 8192, 8193, 10000, 65536, 131072,
200000, 10**9] + [rng.randint(0, 200000) for _ in range(100)]:
budget_case(w)
membud_case(w)
# fuzz: memory-off raw values
for v in ["", "0", "00", "false", "FALSE", "False", "no", "No", "off", "OFF",
"off ", " off", "1", "true", "YES", "on", "anything", "\t", "\n",
"0.0", "nil", "None", "null"]:
memoff_case(v)
# fuzz: through_id over random message shapes
for _ in range(100):
msgs = []
for _ in range(rng.randint(1, 8)):
m = {"role": rng.choice(["user", "assistant", "system"]),
"content": "".join(rng.choice(string.printable[:70]) for _ in range(rng.randint(0, 40)))}
if rng.random() < 0.3:
m["extra"] = {"n": rng.randint(0, 5), "flag": rng.random() < 0.5}
msgs.append(m)
through_case(msgs, rng.randint(-1, len(msgs) + 1))
# unicode through_id (ensure_ascii path)
uni = [{"role": "user", "content": "日本語 テキスト 🎉 emoji"},
{"role": "assistant", "content": "héllo wörld"}]
through_case(uni, 1)
through_case(uni, 0)
# fuzz: admission kernel (priority desc, recency tie, budget, memory cap)
NAMES = ["system", "user", "history", "memory", "persona_memories", "tools", "notes"]
for case_i in range(150):
entries = []
for j in range(rng.randint(1, 7)):
name = rng.choice(NAMES)
entries.append({
"name": name,
"priority": rng.randint(0, 100),
"tokens": rng.randint(1, 900),
"source": f"src-{j}",
"evictable": name not in ("system", "user"),
})
budget = rng.choice([500, 1000, 2000, 4000])
mem_budget = rng.choice([0, 300, 1200])
got = go_eval({"op": "admit", "entries": entries, "budget": budget,
"memory_budget": mem_budget})
bk = py.ContextBroker()
py_entries = [(e["name"], py.ContributionBlock(e["name"], e["priority"],
"c" * e["tokens"], e["tokens"],
e["source"], evictable=e["evictable"]), j)
for j, e in enumerate(entries)]
try:
_adm, adm = bk._admit(py_entries, budget, memory_budget=mem_budget)
want = {"admitted": [bl.name for bl in _adm],
"blocks": adm["blocks"], "admitted_total": adm["admitted_total"],
"dropped": [d["name"] for d in adm["dropped"]],
"memory_budget": adm["memory_budget"], "memory_used": adm["memory_used"]}
check(f"admit[{case_i}]", got, json.loads(json.dumps(want, sort_keys=True)))
except py.ContextOverflowError as exc:
# Go wraps the oracle's message with the wrapped sentinel; the oracle
# text must be a verbatim prefix of the Go detail.
ok = (isinstance(got, dict) and got.get("error") == "ContextOverflowError"
and got.get("detail", "").startswith(str(exc)))
check(f"admit[{case_i}]", "ContextOverflowError" if ok else got,
"ContextOverflowError")
# fuzz: render (system folding, history pass-through, user capture)
for case_i in range(100):
blocks = []
for j in range(rng.randint(1, 6)):
name = rng.choice(NAMES)
if rng.random() < 0.5:
content = "".join(rng.choice(string.ascii_letters) for _ in range(rng.randint(1, 30)))
else:
content = [{"role": rng.choice(["user", "assistant"]),
"content": "".join(rng.choice(string.ascii_letters) for _ in range(rng.randint(1, 20)))}
for _ in range(rng.randint(1, 3))]
blocks.append({"name": name, "priority": rng.randint(0, 100),
"tokens": 0, "source": f"src-{j}", "content": content})
got = go_eval({"op": "render", "blocks": blocks})
py_blocks = []
for b in blocks:
content = b["content"]
if isinstance(content, list):
content = [dict(m) for m in content]
py_blocks.append(py.ContributionBlock(b["name"], b["priority"], content,
b["tokens"], b["source"]))
caps = py.ContextBroker()._render(py_blocks)
want = {"token_count": caps.token_count, "messages": caps.messages,
"system_preamble": caps.system_preamble, "user_content": caps.user_content,
"blocks": [bl.name for bl in caps.blocks]}
check(f"render[{case_i}]", got, json.loads(json.dumps(want)))
print(f"live parity: {checked - fails}/{checked} cases match "
f"(RUNE_COGNITION_CORE=python pinned)")
sys.exit(1 if fails else 0)
+452
View File
@@ -0,0 +1,452 @@
#!/usr/bin/env python3
"""Live parity: structural_store Rust kernels vs Python oracle (dichotomy
wave, contract BUILDER-2026-10-13T00:32:22Z-bc3aae-factory-gap-ss). Layers:
1. FROZEN — the frozen truth corpus (frozen_ss_truth.json, generated by
scripts/gen_structural_store_truth.py against the LIVE oracle with
RUNE_STRUCTURAL_CORE=python pinned) replayed through the ctypes FFI,
case by case, deep-copied inputs.
2. FUZZ — 300 seeded random docs (seed 2479) per kernel: oracle floor
vs FFI, exact compare; save() compared BYTE-EXACT on real files.
3. LIVE — dual-mode facade battery: the same facade calls executed with
RUNE_STRUCTURAL_CORE=python (oracle floor) and with the Rust core live;
results must be identical (timestamps masked).
Exit 0 only at 100% parity on the gap-closing functions.
"""
import calendar
import copy
import json
import os
import random
import string
import sys
import tempfile
import time as _time
import types
from pathlib import Path
os.environ["RUNE_STRUCTURAL_CORE"] = "python" # oracle floor for generation
REPO = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(REPO / "src"))
from rune import structural_store as ss # noqa: E402
from rune import ss_loader # noqa: E402
FIXED_NOW = "2026-10-13T01:02:03Z"
FIXED_EPOCH = calendar.timegm(_time.strptime(FIXED_NOW, "%Y-%m-%dT%H:%M:%SZ"))
ss.now = lambda: FIXED_NOW
ss.time = types.SimpleNamespace( # snapshot() stamps straight off time.*
gmtime=lambda: _time.gmtime(FIXED_EPOCH),
strftime=lambda fmt, t=None: _time.strftime(fmt, t if t is not None else _time.gmtime()),
)
TRUTH = json.loads((REPO / "src" / "rust" / "structural_store" / "src"
/ "frozen_ss_truth.json").read_text())
fails: list[str] = []
counts = {"frozen": 0, "fuzz": 0, "live": 0}
def fail(layer, name, detail):
fails.append(f"[{layer}] {name}: {detail}")
print(f"FAIL[{layer}] {name}: {detail}")
if len(fails) <= 3:
import difflib
w, _, g = detail.partition(" got=")
want_s = w[5:] if w.startswith("want=") else w
a = want_s.replace(r"\u", "\\u").splitlines()
b = g.splitlines()
dump = "\n".join(difflib.unified_diff(a, b, lineterm=""))[:2000]
(REPO / "parity_first_failures.log").open("a").write(
f"=== {layer}/{name} ===\n{dump}\n")
def j(x):
return json.dumps(x, sort_keys=True, ensure_ascii=True)
def check(layer, name, want, got):
counts[layer] += 1
if j(want) != j(got):
fail(layer, name, f"want={j(want)[:300]} got={j(got)[:300]}")
# ── oracle dispatch (python floor) ──────────────────────────────────────────
def oracle_call(fn, a):
return {
"sorted": lambda: ss._sorted(a["value"]),
"new_doc": lambda: ss.new_doc(),
"redact": lambda: ss._redact_secrets(a["value"]),
"digest": lambda: ss.canonical_digest(a["doc"]),
"alias": lambda: _mut(lambda d: ss.alias(d, a["cid"], a["old"]), a["doc"]),
"spine_uri": lambda: ss.spine_uri(a["entity_id"]),
"resolve_uri": lambda: ss.resolve_uri(a["uri"]),
"doc_authorities": lambda: _mut(ss._doc_authorities, a["doc"]),
"authority_health": lambda: ss.authority_health(a["doc"]),
"bind_task": lambda: _mut(lambda d: ss.bind_task(
d, a["task_id"], a["entity_ids"], a["evidence_ref"]), a["doc"]),
"task_entities": lambda: ss.task_entities(a["doc"], a["task_id"]),
"impact_frontier": lambda: ss.impact_frontier(
a["doc"], a["seeds"], max_level=a.get("max_level", 2),
change_kind=a.get("change_kind")),
"observed_impact": lambda: _mut_ret(lambda d: ss.record_observed_impact(
d, a["task_id"], a["items"]), a["doc"]),
"predicted_vs_observed": lambda: ss.predicted_vs_observed(
a["predicted"], a["observed_doc"], a["seeds"]),
"events": lambda: ss.structural_events(a["doc"]),
"diff": lambda: ss.diff(a["old"], a["new"]),
"dedupe": lambda: _mut(ss.dedupe_observations, a["doc"]),
"promote_fix": lambda: _mut(lambda d: ss.promote(
d, a["id"], a["state"], a["ev"]), a["doc"]),
"resolve_status_fix": lambda: ss.resolve_status(a["entity"]),
"supersede_fix": lambda: _mut(lambda d: ss.supersede(
d, a["new"], a["old"], a["ev"]), a["doc"]),
}[fn]()
def _mut(call, doc):
d = copy.deepcopy(doc)
try:
ret = call(d)
except ValueError as exc:
return {"__err__": str(exc)}
return {"__doc__": d, "__ret__": ret}
def _mut_ret(call, doc):
d = copy.deepcopy(doc)
ret = call(d)
return {"__doc__": d, "__ret__": ret}
# ── FFI dispatch ────────────────────────────────────────────────────────────
def ffi_call(fn, a, tmp):
return {
"sorted": lambda: ss_loader.sorted_value(a["value"]),
"new_doc": lambda: ss_loader.new_doc(FIXED_NOW),
"redact": lambda: ss_loader.redact_secrets(a["value"]),
"digest": lambda: ss_loader.canonical_digest(a["doc"]),
"alias": lambda: _ffi_mut(
lambda d: ss_loader.alias(d, a["cid"], a["old"], FIXED_NOW), a["doc"],
ret_fn=lambda d: ss.entity(d, a["cid"])),
"spine_uri": lambda: ss_loader.spine_uri(a["entity_id"]),
"resolve_uri": lambda: ss_loader.resolve_uri(a["uri"]),
"doc_authorities": lambda: _ffi_mut(
ss_loader.doc_authorities, a["doc"],
ret_fn=lambda d: copy.deepcopy(ss._doc_authorities(d))),
"authority_health": lambda: ss_loader.authority_health(a["doc"]),
"bind_task": lambda: _ffi_mut(
lambda d: ss_loader.bind_task(
d, a["task_id"], a["entity_ids"], a["evidence_ref"], FIXED_NOW), a["doc"],
ret_fn=lambda d: ss.entity(d, f"task:{a['task_id']}") or {"id": f"task:{a['task_id']}"}),
"task_entities": lambda: ss_loader.task_entities(a["doc"], a["task_id"]),
"impact_frontier": lambda: ss_loader.impact_frontier(
a["doc"], a["seeds"], max_level=a.get("max_level", 2),
change_kind=a.get("change_kind")),
"observed_impact": lambda: _ffi_mut_ret(lambda d: ss_loader.record_observed_impact(
d, a["task_id"], a["items"], FIXED_NOW), a["doc"]),
"predicted_vs_observed": lambda: ss_loader.predicted_vs_observed(
a["predicted"], a["observed_doc"], a["seeds"]),
"events": lambda: ss_loader.structural_events(a["doc"]),
"diff": lambda: ss_loader.diff(a["old"], a["new"]),
"dedupe": lambda: _ffi_mut(ss_loader.dedupe_observations, a["doc"]),
"promote_fix": lambda: _ffi_mut(
lambda d: ss_loader.promote(d, a["id"], a["state"], a["ev"], FIXED_NOW), a["doc"],
ret_fn=lambda d: ss.entity(d, a["id"])),
"resolve_status_fix": lambda: ss_loader.resolve_status(a["entity"]),
"supersede_fix": lambda: _ffi_mut(
lambda d: ss_loader.supersede(d, a["new"], a["old"], a["ev"], FIXED_NOW), a["doc"],
ret_fn=lambda d: ss.entity(d, a["old"])),
}[fn]()
def _ret_entity(_updated_doc, d, cid):
return ss.entity(d, cid)
def _ffi_mut(call, doc, ret_fn=None):
"""FFI mutation contract: the kernel returns the UPDATED doc; the
oracle-shaped __ret__ is re-derived from it via ret_fn."""
d = copy.deepcopy(doc)
try:
out = call(d)
except RuntimeError as exc:
return {"__err__": str(exc).split("core error: ", 1)[-1]}
if isinstance(out, dict):
d = out
return {"__doc__": d, "__ret__": ret_fn(copy.deepcopy(d)) if ret_fn else None}
def _ffi_mut_ret(call, doc):
d = copy.deepcopy(doc)
out = call(d)
if isinstance(out.get("doc"), dict):
d = out["doc"]
return {"__doc__": d, "__ret__": out["result"]}
def frozen():
for fn, cases in TRUTH["groups"].items():
for c in cases:
a = copy.deepcopy(c["args"])
if fn == "save" or fn == "snapshot":
continue # byte-law layer below
try:
want = oracle_call(fn, a)
except ValueError as exc:
want = {"__err__": str(exc)}
got_raw = None
try:
got = ffi_call(fn, a, None)
except RuntimeError as exc:
got = {"__err__": str(exc).split("core error: ", 1)[-1]}
counts["frozen"] += 1
if j(want) != j(got):
fail("frozen", f"{fn}/{c['name']}", f"want={j(want)[:280]} got={j(got)[:280]}")
def byte_law(tmp):
"""save()/snapshot() must be byte-exact through the FFI."""
save_docs = TRUTH["groups"]["save"]
for c in save_docs:
d = copy.deepcopy(c["args"]["doc"])
name = c["name"]
try:
p = ss.save(d, tmp / f"py/{name}/store.json")
want = {"content": Path(p).read_text(), "doc": d}
except ValueError as exc:
want = {"err": str(exc)}
d2 = copy.deepcopy(c["args"]["doc"])
try:
out = ss_loader.save(d2, str(tmp / f"rs/{name}/store.json"))
got = {"content": Path(out["path"]).read_text(), "doc": out["doc"]}
except RuntimeError as exc:
got = {"err": str(exc).split("core error: ", 1)[-1]}
check("frozen", f"save-bytes/{name}", want, got)
for c in TRUTH["groups"]["snapshot"]:
a = copy.deepcopy(c["args"])
d = a["doc"]
p = ss.snapshot(d, tmp / f"py-snap/{c['name']}", a["label"], tag=a["tag"])
want = {"path": Path(p).name, "content": Path(p).read_text(), "doc": d}
d2 = copy.deepcopy(a["doc"])
out = ss_loader.snapshot(d2, str(tmp / f"rs-snap/{c['name']}"), a["label"], a["tag"], FIXED_NOW)
got = {"path": Path(out["path"]).name, "content": Path(out["path"]).read_text(), "doc": out["doc"]}
check("frozen", f"snapshot-bytes/{c['name']}", want, got)
# ── fuzz (seeded) ───────────────────────────────────────────────────────────
WORDS = ["m:a", "m:b", "m:c", "c:api", "task:T1", "é-unit", "x" * 40, ""]
RELTYPES = ["depends_on", "implements", "consumes", "consumed_by", "verified_by",
"obligated_canary", "deployed_as", "documented_by", "supersedes",
"historical_relation", "related_memory", "belongs_to", "weird_rel"]
SECRETS = ["token", "api_key", "password", "not_a_secret", "client_secret", "TOKEN"]
def rand_value(rnd, depth=0):
kind = rnd.randint(0, 6 if depth < 2 else 4)
if kind == 0:
return rnd.randint(-5, 10 ** 6)
if kind == 1:
return rnd.choice([0.5, 1.0, -2.25, 3.0, 1e-7, 1e21, 12345.6789])
if kind == 2:
return rnd.choice([True, False, None])
if kind == 3:
return "".join(rnd.choice(string.printable[:80] + "é☕φ") for _ in range(rnd.randint(0, 12)))
if kind == 4:
return rnd.choice(WORDS)
if kind == 5:
return [rand_value(rnd, depth + 1) for _ in range(rnd.randint(0, 4))]
return {rnd.choice("abcdefé") + str(i): rand_value(rnd, depth + 1)
for i in range(rnd.randint(0, 4))}
def rand_doc(rnd):
d = {"schema_version": 1, "generated_at": FIXED_NOW,
"entities": [], "relationships": [], "observations": []}
for i in range(rnd.randint(0, 5)):
e = {"id": rnd.choice(WORDS) or f"m:e{i}", "kind": rnd.choice(["module", "contract", "widget"]),
"maturity": rnd.choice(["DECLARED", "PRESENT", "WIRED", "REACHABLE", "EXERCISED", "LIVE_PROVEN"]),
"evidence_refs": sorted({f"ev-{rnd.randint(0, 3)}" for _ in range(rnd.randint(0, 2))}),
"observed_at": FIXED_NOW}
if rnd.random() < 0.3:
e["aliases"] = [f"m:old{i}"]
if rnd.random() < 0.3:
e["ext"] = {"conflicts": rand_value(rnd, 1)} if rnd.random() < 0.5 else {"n": i}
if rnd.random() < 0.3:
e["identities"] = {"p": rand_value(rnd, 1)}
if rnd.random() < 0.2:
e[rnd.choice(SECRETS)] = "s3cret"
d["entities"].append(e)
for _ in range(rnd.randint(0, 6)):
rel = {"type": rnd.choice(RELTYPES), "from": rnd.choice(WORDS), "to": rnd.choice(WORDS),
"evidence_refs": [f"ev-{rnd.randint(0, 2)}"] if rnd.random() < 0.8 else [],
"observed_at": FIXED_NOW}
if rnd.random() < 0.2:
rel["status"] = rnd.choice(["STALE", "CANDIDATE", "CURRENT"])
d["relationships"].append(rel)
for _ in range(rnd.randint(0, 4)):
o = {"kind": rnd.choice(["superseded", "k1"]), "x": rand_value(rnd, 1), "observed_at": FIXED_NOW}
d["observations"].append(o)
if rnd.random() < 0.4:
d["authority_generations"] = {
rnd.choice(["p1", "p2", "é-proj"]): {
"generation": rnd.randint(0, 5),
"observed": [f"{rnd.choice(RELTYPES)}|{rnd.choice(WORDS)}|{rnd.choice(WORDS)}"
for _ in range(rnd.randint(0, 3))],
**({"status": rnd.choice(["CURRENT", "DEGRADED", "UNKNOWN"])} if rnd.random() < 0.7 else {}),
} for _ in range(rnd.randint(1, 3))}
return d
def fuzz():
rnd = random.Random(2479)
for i in range(300):
d = rand_doc(rnd)
d2 = copy.deepcopy(d)
check("fuzz", f"sorted/{i}", ss._sorted(copy.deepcopy(d)), ss_loader.sorted_value(copy.deepcopy(d)))
check("fuzz", f"redact/{i}", ss._redact_secrets(copy.deepcopy(d)), ss_loader.redact_secrets(copy.deepcopy(d)))
check("fuzz", f"digest/{i}", ss.canonical_digest(copy.deepcopy(d)), ss_loader.canonical_digest(copy.deepcopy(d)))
check("fuzz", f"health/{i}", ss.authority_health(copy.deepcopy(d)), ss_loader.authority_health(copy.deepcopy(d)))
check("fuzz", f"events/{i}", ss.structural_events(copy.deepcopy(d)), ss_loader.structural_events(copy.deepcopy(d)))
d3 = copy.deepcopy(d)
ss.dedupe_observations(d3)
d4 = copy.deepcopy(d)
d4 = ss_loader.dedupe_observations(d4)
check("fuzz", f"dedupe/{i}", d3, d4)
seed = rnd.choice(WORDS) or "m:a"
ck = rnd.choice([None, "contract_change", "implementation_change", "contract_violation"])
check("fuzz", f"frontier/{i}",
ss.impact_frontier(copy.deepcopy(d), [seed], change_kind=ck),
ss_loader.impact_frontier(copy.deepcopy(d), [seed], change_kind=ck))
check("fuzz", f"diff/{i}", ss.diff(copy.deepcopy(d), d2), ss_loader.diff(copy.deepcopy(d), copy.deepcopy(d2)))
ent = rnd.choice(d["entities"]) if d["entities"] else {}
check("fuzz", f"resolve_status/{i}", ss.resolve_status(copy.deepcopy(ent)),
ss_loader.resolve_status(copy.deepcopy(ent)))
# mutation kernels
want = _mut(lambda x: ss.bind_task(x, "T1", ["m:a", "c:api"], "ev"), copy.deepcopy(d))
got = _ffi_mut(lambda x: ss_loader.bind_task(x, "T1", ["m:a", "c:api"], "ev", FIXED_NOW),
copy.deepcopy(d),
ret_fn=lambda x: ss.entity(x, "task:T1") or {"id": "task:T1"})
check("fuzz", f"bind_task/{i}", want, got)
want = _mut(lambda x: ss.alias(x, "m:b", "m:b-old"), copy.deepcopy(d)) if any(
e["id"] == "m:b" for e in d["entities"]) else {"__err__": "alias target not found: m:b"}
got = _ffi_mut(lambda x: ss_loader.alias(x, "m:b", "m:b-old", FIXED_NOW), copy.deepcopy(d),
ret_fn=lambda x: ss.entity(x, "m:b"))
check("fuzz", f"alias/{i}", want, got)
want = _mut(lambda x: ss.promote(x, "m:a", "WIRED", "ev-f"), copy.deepcopy(d))
got = _ffi_mut(lambda x: ss_loader.promote(x, "m:a", "WIRED", "ev-f", FIXED_NOW), copy.deepcopy(d),
ret_fn=lambda x: ss.entity(x, "m:a"))
check("fuzz", f"promote/{i}", want, got)
want = _mut(lambda x: ss.supersede(x, "m:b", "m:a", "ev-s"), copy.deepcopy(d))
got = _ffi_mut(lambda x: ss_loader.supersede(x, "m:b", "m:a", "ev-s", FIXED_NOW), copy.deepcopy(d),
ret_fn=lambda x: ss.entity(x, "m:a"))
check("fuzz", f"supersede/{i}", want, got)
items = [{"entity": rnd.choice(WORDS), "source": "t", "detail": rand_value(rnd, 1)} for _ in range(3)]
want = _mut_ret(lambda x: ss.record_observed_impact(x, "T1", copy.deepcopy(items)), copy.deepcopy(d))
got = _ffi_mut_ret(lambda x: ss_loader.record_observed_impact(x, "T1", copy.deepcopy(items), FIXED_NOW), copy.deepcopy(d))
check("fuzz", f"observed_impact/{i}", want, got)
def save_bytes_fuzz(tmp):
rnd = random.Random(4242)
for i in range(50):
d = rand_doc(rnd)
p1 = ss.save(copy.deepcopy(d), tmp / f"fz-py/{i}.json")
out = ss_loader.save(copy.deepcopy(d), str(tmp / f"fz-rs/{i}.json"))
b1 = Path(p1).read_bytes()
b2 = Path(out["path"]).read_bytes()
counts["fuzz"] += 1
if b1 != b2:
fail("fuzz", f"save-bytes/{i}", f"py={b1[:160]!r} rs={b2[:160]!r}")
check("fuzz", f"digest-bytes/{i}", ss.canonical_digest(copy.deepcopy(d)),
ss_loader.canonical_digest(copy.deepcopy(d)))
# ── live dual-mode facade battery ───────────────────────────────────────────
def facade_battery():
def run(tag):
import subprocess
code = r'''
import copy, json, os, sys, tempfile, calendar, time as _time, types
sys.path.insert(0, "src")
from rune import structural_store as ss
FIXED = "2026-10-13T01:02:03Z"
ss.now = lambda: FIXED
_EPOCH = calendar.timegm(_time.strptime(FIXED, "%Y-%m-%dT%H:%M:%SZ"))
ss.time = types.SimpleNamespace( # freeze snapshot()'s wall-clock stamp
gmtime=lambda: _time.gmtime(_EPOCH),
strftime=lambda fmt, t=None: _time.strftime(fmt, t if t is not None else _time.gmtime()),
)
out = {}
d = ss.new_doc()
ss.add_entity(d, id="m:a", kind="module")
ss.add_entity(d, id="m:b", kind="module")
ss.add_entity(d, id="c:api", kind="contract")
ss.add_relationship(d, type="implements", from_id="m:a", to_id="c:api", evidence_refs=["ev"])
ss.add_relationship(d, type="depends_on", from_id="m:b", to_id="m:a", evidence_refs=["ev"])
ss.alias(d, "m:b", "m:b-old")
ss.promote(d, "m:a", "WIRED", "ev-2")
ss.bind_task(d, "T1", ["m:a", "m:b"], "ev-t")
with tempfile.TemporaryDirectory() as td:
p = ss.save(d, td + "/s.json")
out["save_bytes_sha"] = __import__("hashlib").sha256(open(p, "rb").read()).hexdigest()
out["snapshot_name"] = ss.snapshot(d, td + "/sn", "l1", tag="cafe1234").name
out["digest"] = ss.canonical_digest(d)
out["entities"] = sorted(e["id"] for e in d["entities"])
out["via_alias"] = ss.entity(d, "m:b-old")["maturity"]
out["health"] = ss.authority_health(d)
out["frontier"] = ss.impact_frontier(d, ["c:api"], change_kind=None)
out["frontier_impl"] = ss.impact_frontier(d, ["m:a"], change_kind=None)
out["events_n"] = len(ss.structural_events(d))
out["diff"] = ss.diff(d, copy.deepcopy(d))
out["task_entities"] = ss.task_entities(d, "T1")
ss.dedupe_observations(d)
out["obs_n"] = len(d["observations"])
r = ss.record_observed_impact(d, "T2", [{"entity": "m:a", "detail": "x"}])
out["observed"] = r
out["pvo"] = ss.predicted_vs_observed(out["frontier"], d, ["c:api"])
print(json.dumps(out, sort_keys=True))
'''
env = dict(os.environ)
if tag == "python":
env["RUNE_STRUCTURAL_CORE"] = "python"
else:
env.pop("RUNE_STRUCTURAL_CORE", None)
r = subprocess.run([sys.executable, "-c", code], capture_output=True,
text=True, cwd=str(REPO), env=env, timeout=120)
if r.returncode != 0:
fail("live", tag, r.stderr[-300:])
return None
return json.loads(r.stdout)
py = run("python")
rs = run("rust")
if py is None or rs is None:
return
check("live", "dual-mode-facade", py, rs)
def main():
if not ss_loader.available():
print("Rust core not built — run cargo build --release in src/rust/structural_store")
return 2
with tempfile.TemporaryDirectory() as td:
frozen()
byte_law(Path(td))
fuzz()
save_bytes_fuzz(Path(td))
facade_battery()
total = sum(counts.values())
print(f"\nPARITY: {total} checks, {len(fails)} failures "
f"(frozen={counts['frozen']} fuzz={counts['fuzz']} live={counts['live']})")
return 1 if fails else 0
if __name__ == "__main__":
sys.exit(main())
+48
View File
@@ -0,0 +1,48 @@
#!/usr/bin/env python3
"""Close the #2479 dichotomy-wave construction contract for night_mode
with SHA evidence."""
import json
import os
import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "src"))
os.environ["CS_RUST_LIB"] = "/mnt/workdrive/rune/src/rust/capability_scoring/target/release/libcapability_scoring.so"
from rune.construction_interface import ConstructionInterface
cid = sys.argv[1]
iface = ConstructionInterface(builder="factory-gap-nm")
c = iface.close(cid, observed_evidence=[{
"kind": "port_evidence",
"detail": "night_mode dichotomy completion — parity 150f5b86 "
"feat(night_mode): close PARTIAL gaps — dichotomy wave; "
"Phase B (trunc ASCII zero-alloc fast path, rune walk "
"only for long/non-ASCII) pushed gitea/master. Go port at "
"src/go/night-mode closes all 12 audit-named gaps "
"name-preserving (_ctx_budget, get_night_mode, "
"start/stop_night_mode, get_status, get_report, to_dict, "
"_load, _save, _run_loop, _run_sleep, _run_evaluation + "
"_run_iteration/_generate_report); pure/IO split law with "
"injected clock/wait/script/HTTP/rand/loop seams. Battery "
"10 tests replay the 9-case frozen oracle truth VERBATIM "
"(stop guard, idle status/report, 6 _ctx_budget branches); "
"live parity 100% (status/report/stop x 3 scenarios + CLI "
"round-trip); pytest 37/37 both ways (RUNE_NIGHT_MODE_CORE "
"auto vs =python); gofmt+vet clean. Zero regressions "
"(failure set byte-identical before/after).",
}], scope_files=[
"src/rune/night_mode.py",
"src/go/night-mode/main.go",
"src/go/night-mode/night_mode_test.go",
"src/go/night-mode/frozen_truth.json",
"scripts/gen_night_mode_truth.py",
"scripts/check_night_mode_parity.py",
"tests/test_night_mode.py",
"scripts/open_contract_night_mode.py",
"scripts/close_contract_night_mode.py",
])
print("close:", json.dumps(c)[:300])
print("CONTRACT", cid, "closed" if c.get("success") else "CLOSE_FAILED")
+76
View File
@@ -0,0 +1,76 @@
#!/usr/bin/env python3
"""Close the #2479 dichotomy-wave construction contract for the
provider_budget gap-closing port with SHA evidence. Full lifecycle
draft→approved→building→verifying→closed in ONE process."""
import json
import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "src"))
from rune.construction_interface import ConstructionInterface
PARITY_SHA = "8a08f9b4"
PHASE_B_SHA = "527dc774"
iface = ConstructionInterface(builder="factory-gap-pb")
r = iface.submit_need(
"Close the provider_budget dichotomy gaps (#2479 tranche, GO side): "
"port the 10 audit-missing functions (get_tracker/track_usage/"
"check_provider/get_best_provider/failover/get_status/to_dict/_load/"
"_save/_init_default_providers) name-preserving into the existing "
"src/go/provider-budget Go port — tracker with order-preserving "
"storage load (dict-order tie-break parity), config seeding (ollama "
"skip + anthropic type detection), per-type health-check endpoints "
"with IO behind injectable seams (clock, config path, transport), "
"api_key-excluding to_dict, module-level singleton wrappers. Truth "
"frozen via the shared freeze_truth.py harness (RUNE_PROVIDER_BUDGET"
"_CORE=python, fixture-home replay contract); VERBATIM-embedded Go "
"battery; 200-op live parity seed 2479; Python port test (oracle "
"pinned + harness gate); audit 5/20 → 20/20.",
origin="builder",
)
print("submit_need:", json.dumps(r)[:200])
if not r.get("success"):
print("NEED FAILED — aborting")
sys.exit(1)
cid = r["ids"]["contract_id"]
print("contract_id:", cid)
for phase in ("approved", "building", "verifying"):
t = iface.transition(cid, phase)
print(f"transition->{phase}:", json.dumps(t)[:120])
if not t.get("success"):
sys.exit(1)
c = iface.close(cid, observed_evidence=[{
"kind": "port_evidence",
"detail": "provider_budget gap-closing completion — parity "
f"{PARITY_SHA} feat(provider_budget): close PARTIAL gaps — "
"dichotomy wave; Phase B (single generic decodeOrderedTable[T] "
f"streaming decoder replacing both hand-rolled ordered-JSON "
f"decoders) {PHASE_B_SHA} refactor(provider_budget): Phase B "
"language-advantage pass — dichotomy wave; both pushed "
"gitea/master. Go battery replays the 13-case frozen oracle "
"truth VERBATIM (truth_frozen.json from the shared "
"freeze_truth.py harness, RUNE_PROVIDER_BUDGET_CORE=python, "
"fixture-home replay protocol) — harness gate reports "
"byte-identical; live parity 200/200 random-op seed 2479 "
"python-vs-Go identical; pytest 3/3 (oracle pinned, frozen "
"replay, harness gate, go test); go vet + gofmt clean; "
"existing replay gate 9/9 green; dichotomy audit 5/20 "
"PARTIAL → 20/20 FULL, missing [].",
}], scope_files=[
"src/rune/provider_budget.py",
"src/go/provider-budget/main.go",
"src/go/provider-budget/truth_frozen.json",
"src/go/provider-budget/truth_frozen_test.go",
"scripts/gen_provider_budget_truth.py",
"scripts/parity_provider_budget.py",
"scripts/walk_contract_provider_budget.py",
"scripts/close_contract_provider_budget.py",
"tests/test_provider_budget_port.py",
])
print("close:", json.dumps(c)[:200])
print("CONTRACT", cid, "closed" if c.get("success") else "CLOSE_FAILED")
sys.exit(0 if c.get("success") else 1)
+1 -1
View File
@@ -47,7 +47,7 @@ elif [ "$LANG_" = "go" ]; then
fi
# Layer 3: truth file exists and is consumed
TRUTH=$(find . \( -name "frozen_*_truth.json" -o -name "frozen_truth.json" -o -name "truth.json" -o -name "truth_*.json" \) 2>/dev/null | head -1)
TRUTH=$(find . \( -name "frozen_*_truth.json" -o -name "frozen_truth.json" -o -name "frozen_*_truth.json" -o -name "truth.json" -o -name "truth_*.json" \) 2>/dev/null | head -1)
if [ -n "$TRUTH" ]; then
ROWS=$(python3 -c "import json; d=json.load(open('$TRUTH')); print(len(d) if isinstance(d,list) else sum(len(v) if isinstance(v,list) else 0 for v in d.values()))" 2>/dev/null)
check "PASS" "truth file: $TRUTH ($ROWS rows)"
+383
View File
@@ -0,0 +1,383 @@
#!/usr/bin/env python3
"""Freeze the oracle truth for the GO gap-close port of
src/rune/context_broker.py (dichotomy wave #2479, GO, factory-gap-ctxbroker).
Every NEW function ported by the gap-close, every branch, against the LIVE
oracle pinned to the Python cores (RUNE_COGNITION_CORE=python — the _py_*
oracles ARE the parity floor being frozen). Deep-copied inputs: the oracle
mutates message lists in place (compact_history archive path), so each row
re-freezes the input verbatim.
Ops frozen (fn = the Go-side dispatcher key):
_py_default_estimator est (text)
_cc_core cc_core (env RUNE_COGNITION_CORE) -> bool available
_py_default_budget budget (window)
_py_default_memory_budget membud (window, incl. None/str/bool coercion)
_py_memory_disabled_value memoff (raw string)
reset_discovered_windows / register_window / default_window
windows (sequence of ops -> final registry view)
summarize_history summarize (transport-failure branch: ValueError)
compact_history compact (messages, window, summarizer, session)
rolling_summary_state rstate (key -> state | null)
_through_id through (messages, through_count)
store_rolling_summary / evict_rolling rstore (op sequence -> state + return)
"""
from __future__ import annotations
import copy
import json
import os
import sys
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(ROOT / "src"))
os.environ["RUNE_COGNITION_CORE"] = "python" # pin the Python parity floor
from rune import context_broker as cb # noqa: E402
rows: list[dict] = []
def row(name, fn, inp, out):
rows.append({"name": name, "fn": fn, "input": inp, "want": out})
# ── _py_default_estimator: every branch ────────────────────────────────────
row("est:empty", "est", {"text": ""}, cb._py_default_estimator(""))
row("est:none-coerced-empty", "est", {"text": None}, cb._py_default_estimator(None))
row("est:short", "est", {"text": "hello"}, cb._py_default_estimator("hello"))
row("est:exact-8", "est", {"text": "12345678"}, cb._py_default_estimator("12345678"))
row("est:9-floor", "est", {"text": "123456789"}, cb._py_default_estimator("123456789"))
row("est:unicode-rune-len", "est", {"text": "héllo wörld"}, cb._py_default_estimator("héllo wörld"))
# ── _cc_core: python-pin + auto-without-build ───────────────────────────────
from rune import cc_loader # noqa: E402
_LOADER_AVAIL = bool(cc_loader.available())
row("cc_core:python-pin-returns-none", "cc_core",
{"env": {"RUNE_COGNITION_CORE": "python"}, "loader_available": _LOADER_AVAIL},
None if cb._cc_core() is None else "CORE")
del os.environ["RUNE_COGNITION_CORE"]
row("cc_core:auto-fallback", "cc_core", {"env": {}, "loader_available": _LOADER_AVAIL},
None if cb._cc_core() is None else "CORE")
# ── _py_default_budget: every branch ────────────────────────────────────────
row("budget:0-floor", "budget", {"window": 0}, cb._py_default_budget(0))
row("budget:1-floor", "budget", {"window": 1}, cb._py_default_budget(1))
row("budget:100-floor", "budget", {"window": 100}, cb._py_default_budget(100))
row("budget:4096", "budget", {"window": 4096}, cb._py_default_budget(4096))
row("budget:8192", "budget", {"window": 8192}, cb._py_default_budget(8192))
row("budget:131072", "budget", {"window": 131072}, cb._py_default_budget(131072))
row("budget:quarter-exact-8192", "budget", {"window": 8192}, cb._py_default_budget(8192))
# ── _py_default_memory_budget: every branch incl. coercion ─────────────────
row("membud:0-cap", "membud", {"window": 0}, cb._py_default_memory_budget(0))
row("membud:none-cap", "membud", {"window": None}, cb._py_default_memory_budget(None))
row("membud:neg-cap", "membud", {"window": -5}, cb._py_default_memory_budget(-5))
row("membud:8-eq-1", "membud", {"window": 8}, cb._py_default_memory_budget(8))
row("membud:2400", "membud", {"window": 2400}, cb._py_default_memory_budget(2400))
row("membud:9600-cap", "membud", {"window": 9600}, cb._py_default_memory_budget(9600))
row("membud:96000-cap", "membud", {"window": 96000}, cb._py_default_memory_budget(96000))
# bool/float coerce through the public fn's int(window) path when a Rust core
# is present; on the Python floor they hit _py_default_memory_budget raw.
row("membud:bool-true", "membud", {"window": True}, cb._py_default_memory_budget(True))
row("membud:float", "membud", {"window": 2400.9}, cb._py_default_memory_budget(2400.9))
# ── _py_memory_disabled_value: every branch ────────────────────────────────
for v in ("", "0", "false", "no", "off", "1", "true", "yes", "on", "OFF",
" False ", "anything", " ", "off\n"):
row(f"memoff:{v!r}", "memoff", {"value": v}, cb._py_memory_disabled_value(v))
# ── window registry: reset/register/default with NO live discovery ─────────
# default_window's live-discovery import is exercised in the fallback branch
# only when model_id is falsy (registry miss + import of local_tool_runtime
# would attempt a real HTTP call otherwise — the Go seam stubs discovery;
# here we freeze: falsy id, registry hit, registry miss w/ capped discovery).
cb.reset_discovered_windows()
row("windows:reset-then-missing-none", "windows",
{"ops": [{"op": "default_window", "model_id": None}]},
{"last": cb.default_window(None)})
row("windows:none-empty-str", "windows",
{"ops": [{"op": "default_window", "model_id": ""}]},
{"last": cb.default_window("")})
row("windows:register-hit", "windows",
{"ops": [{"op": "register_window", "model_id": "m1", "window": 32768},
{"op": "default_window", "model_id": "m1"}]},
{"last": (cb.reset_discovered_windows(), cb.register_window("m1", 32768),
cb.default_window("m1"))[2]})
row("windows:register-overwrite", "windows",
{"ops": [{"op": "register_window", "model_id": "m1", "window": 8192},
{"op": "register_window", "model_id": "m1", "window": 65536},
{"op": "default_window", "model_id": "m1"}]},
{"last": (cb.reset_discovered_windows(), cb.register_window("m1", 8192),
cb.register_window("m1", 65536), cb.default_window("m1"))[3]})
row("windows:reset-clears", "windows",
{"ops": [{"op": "register_window", "model_id": "m1", "window": 8192},
{"op": "reset_discovered_windows"},
{"op": "default_window", "model_id": "m1"}]},
{"last": (cb.reset_discovered_windows(), cb.register_window("m1", 8192),
cb.reset_discovered_windows(), cb.default_window("m1"))[3]})
row("windows:register-str-window-coerced", "windows",
{"ops": [{"op": "register_window", "model_id": "m1", "window": "28672"},
{"op": "default_window", "model_id": "m1"}]},
{"last": (cb.reset_discovered_windows(), cb.register_window("m1", "28672"),
cb.default_window("m1"))[2]})
# discovery-failure branch: unknown model -> the live oracle tries the
# transport; in this offline env that raises inside context_length ->
# record_failure + fallback 8192 (never cached). Freeze the returned value.
cb.reset_discovered_windows()
row("windows:miss-discover-fallback-8192", "windows",
{"ops": [{"op": "default_window", "model_id": "definitely-not-a-model:offline"}]},
{"last": cb.default_window("definitely-not-a-model:offline"),
"note": "live oracle: T1 context_discovery + FALLBACK_WINDOW, not cached"})
# ── summarize_history: transport-failure branch (offline) ──────────────────
try:
cb.summarize_history([{"role": "user", "content": "hi"}], model="offline-no-such-model")
row("summarize:unexpected-success", "summarize", {"messages": []}, "UNEXPECTED-SUCCESS")
except ValueError as exc:
row("summarize:transport-fail-valueerror", "summarize",
{"messages": [{"role": "user", "content": "hi"}]},
{"error": "ValueError", "detail": str(exc)[:120]})
except Exception as exc: # noqa: BLE001 — freeze whatever the live env does
row("summarize:transport-fail-other", "summarize",
{"messages": [{"role": "user", "content": "hi"}]},
{"error": type(exc).__name__, "detail": str(exc)[:120]})
# ── _through_id: every branch ───────────────────────────────────────────────
msgs = [{"role": "user", "content": "first"},
{"role": "assistant", "content": "reply"},
{"role": "user", "content": "second"}]
row("through:empty-list", "through", {"messages": [], "through": 0}, cb._through_id([], 0))
row("through:neg", "through", {"messages": msgs, "through": -1}, cb._through_id(msgs, -1))
row("through:at-end", "through", {"messages": msgs, "through": 3}, cb._through_id(msgs, 3))
row("through:beyond-end", "through", {"messages": msgs, "through": 99}, cb._through_id(msgs, 99))
row("through:anchor-0", "through", {"messages": msgs, "through": 0}, cb._through_id(msgs, 0))
row("through:anchor-1", "through", {"messages": msgs, "through": 1}, cb._through_id(msgs, 1))
row("through:content-hash-stable", "through",
{"messages": msgs, "through": 2},
(cb._through_id(msgs, 2), cb._through_id(copy.deepcopy(msgs), 2),
cb._through_id([{"content": "second", "role": "user"}], 0))[0],
)
# key-order independence (sort_keys=True)
row("through:key-order-independent", "through",
{"messages": [{"role": "user", "content": "x"}, {"content": "x", "role": "user"}],
"through": 1},
cb._through_id([{"role": "user", "content": "x"}, {"content": "x", "role": "user"}], 1))
# ── rolling summary store: state/store/evict (self-contained rows) ──────────
cb._ROLLING.clear()
row("rstate:miss", "rstate", {"ops": [{"op": "read", "source": "operator", "sid": "s1"}]},
cb.rolling_summary_state(("operator", "s1")))
cb.store_rolling_summary(("operator", "s1"), "SUM", 7, "abcdef0123456789")
row("rstate:hit-copy", "rstate",
{"ops": [{"op": "store", "source": "operator", "sid": "s1", "summary": "SUM",
"through": 7, "tid": "abcdef0123456789"},
{"op": "read", "source": "operator", "sid": "s1"}]},
cb.rolling_summary_state(("operator", "s1")))
row("rstore:store-then-through-id", "rstore",
{"msgs": msgs},
(cb.store_rolling_summary(("operator", "s2"), "S2", 1, cb._through_id(msgs, 1)),
cb.rolling_summary_state(("operator", "s2")))[1])
# ── compact_history: every branch (injected summarizer = deterministic) ────
def fresh(seed: str, n_pairs: int) -> list[dict]:
out = []
for i in range(n_pairs):
out.append({"role": "user", "content": f"{seed} user {i} " + "x" * (i * 40)})
out.append({"role": "assistant", "content": f"{seed} assistant {i} " + "y" * (i * 40)})
return out
def sumr(messages):
return "SUMMARY<" + "|".join(m.get("content", "")[:10] for m in messages) + ">"
def compact_row(name, messages, window, *, summarizer="sum", session="", prior=None,
archive=False):
"""Freeze one compact_history call; self-contained (prior state inline)."""
cb._ROLLING.clear()
if prior:
cb.store_rolling_summary(("operator", session) if session else ("operator",),
prior["summary"], prior["through_count"],
prior.get("through_id", ""))
kw = dict(summarizer=sumr if summarizer != "boom" else boom)
arch = None
if archive:
# deterministic RELATIVE fixture path (chdir'd to repo root): the
# label is embedded in the summary block, so it must be portable
os.chdir(ROOT)
arch = Path("src/go/context-broker/.fixtures/archive.jsonl")
arch.parent.mkdir(parents=True, exist_ok=True)
inp = {"messages": copy.deepcopy(messages), "window": window,
"summarizer": summarizer}
if session:
inp["session"] = session
if prior:
inp["prior"] = prior
if archive:
inp["archive"] = "FIXTURE"
inp["archive_path"] = str(arch)
compacted, ev = cb.compact_history(copy.deepcopy(messages), window,
archive_path=arch,
summarizer=sumr if summarizer != "boom" else boom,
session_id=session)
out = {"event": ev}
if messages is not None:
out["compacted"] = compacted
if archive:
body = arch.read_text() if arch.exists() else ""
out["archive_lines"] = body.count("\n")
out["archived_msg_count"] = len(json.loads(body.splitlines()[0])["messages"]) if body else 0
out["archived_msgs"] = json.loads(body.splitlines()[0])["messages"] if body else []
arch.unlink(missing_ok=True)
row(name, "compact", inp, out)
def boom(messages):
raise RuntimeError("summarizer down")
# no messages -> early return, event untriggered
compact_row("compact:empty-messages", [], 8192)
# <= KEEP_TURNS user turns -> early return
compact_row("compact:at-keep-turns-no-trigger", fresh("a", 5), 8192)
compact_row("compact:below-keep-turns-no-trigger", fresh("a", 4), 8192)
# > keep turns, no session, summarizer success
compact_row("compact:trigger-no-session", fresh("b", 7), 8192)
# with session: rolling persistence across TWO compactions (C1: only new)
cb._ROLLING.clear()
m3 = fresh("c", 8)
c1, ev1 = cb.compact_history(copy.deepcopy(m3), 8192, summarizer=sumr,
session_id="s3")
prior3 = cb.rolling_summary_state(("operator", "s3"))
row("compact:session-first", "compact",
{"messages": m3, "window": 8192, "session": "s3", "summarizer": "sum"},
{"event": ev1, "compacted": c1})
# second compaction: window rotated under the stored count (#141 rematch)
m3b = m3[:2] + m3[-10:]
compact_row("compact:session-rotation-rematch", m3b, 8192, session="s3",
prior=prior3)
# prior through beyond len(older) — history shrank, re-summarize from scratch
compact_row("compact:session-through-beyond-shrink", fresh("d", 7), 8192,
session="s4", prior={"summary": "PRIOR-SUM", "through_count": 999,
"through_id": ""})
# nothing new beyond the rolled summary: reuse it verbatim
cb._ROLLING.clear()
m9 = fresh("g", 7)
_, ev9_pre = cb.compact_history(copy.deepcopy(m9), 8192, summarizer=sumr,
session_id="s9")
prior9 = cb.rolling_summary_state(("operator", "s9"))
# call again with IDENTICAL messages: older[6:] is empty -> reuse verbatim
compact_row("compact:session-nothing-new-reuse", m9, 8192, session="s9",
prior=prior9)
# summarizer failure -> C2 degrade to tail
compact_row("compact:summarizer-fail-degrade-tail", fresh("e", 7), 8192,
summarizer="boom")
# summarizer failure with tiny window (tail budget floor: always keep >=1)
m6 = [{"role": "user", "content": "u" * 9000}, {"role": "assistant", "content": "a" * 9000},
{"role": "user", "content": "u2" * 9000}, {"role": "assistant", "content": "a2" * 9000},
{"role": "user", "content": "u3" * 9000}, {"role": "assistant", "content": "a3" * 9000},
{"role": "user", "content": "u4" * 9000}]
compact_row("compact:fail-tiny-window-tail-first-only", m6, 10, summarizer="boom")
# archive write: success path — event.archived True, label = the path itself
compact_row("compact:archive-success", fresh("f", 6), 8192, session="s7",
archive=True)
# default_estimator used inside event tokens (python pin): tokens_before math
compact_row("compact:token-math-estimator",
[{"role": "user", "content": "12345678"}, {"role": "assistant", "content": "1234"}],
8192)
# ── ContextBroker kernel: to_dict / collect / admit / render ────────────────
from rune.context_broker import ContributionBlock, ContextBroker, ContextOverflowError # noqa: E402
# to_dict
blk = ContributionBlock("memory", 5, "hello world", 2, "recall", evictable=True)
row("to_dict:block", "to_dict", {}, blk.to_dict())
blk2 = ContributionBlock("system", 100, "sys", 0, "persona", evictable=False)
row("to_dict:mandatory", "to_dict", {}, blk2.to_dict())
# collect: happy, empty-content skip, failure → T1 degrade, malformed → T1
def make_broker():
b = ContextBroker()
b.register_contributor("ok", lambda task, session: ContributionBlock("tools", 9, "tooltext", 2, "t"), 1)
b.register_contributor("empty", lambda task, session: ContributionBlock("x", 1, "", 0, "s"), 1)
b.register_contributor("boom", lambda task, session: (_ for _ in ()).throw(RuntimeError("cb down")), 1)
b.register_contributor("malformed", lambda task, session: ContributionBlock("y", 1, 12345, 0, "s"), 1)
b.register_contributor("list-ok", lambda task, session: ContributionBlock("history", 3, [{"role": "user", "content": "hi"}], 1, "h"), 2)
b.register_contributor("none", lambda task, session: None, 1)
return b
b = make_broker()
degraded: list[str] = []
collected = b._collect({}, {}, degraded)
row("collect:degraded-and-ordered", "collect", {},
{"names": [name for name, _blk, _b in collected],
"degraded": degraded,
"failed_events": sorted(getattr(b, "_last_failures", []))})
# admit: priority desc, recency tie, memory sub-budget, overflow
def blocks(*specs):
out = []
for name, pri, tokens, evictable in specs:
out.append(ContributionBlock(name, pri, "c" * tokens, tokens, f"src-{name}", evictable=evictable))
return out
bk = ContextBroker()
admitted, adm = bk._admit([(b["name"], bl, 0) for b, bl in zip(
[{"name": "system"}, {"name": "memory"}, {"name": "history"}, {"name": "user"}],
blocks(("system", 100, 500, False), ("memory", 5, 300, True),
("history", 7, 900, True), ("user", 100, 100, False)))], 1000)
row("admit:priority-recency-budget", "admit", {},
{"admitted": [bl.name for bl in admitted],
"blocks": adm["blocks"], "admitted_total": adm["admitted_total"],
"dropped": [d["name"] for d in adm["dropped"]],
"memory_budget": adm["memory_budget"], "memory_used": adm["memory_used"]})
# memory sub-budget: two memory blocks, second exceeds sub-budget
admitted2, adm2 = bk._admit([("m1", blocks(("m1", 9, 700, True))[0], 0),
("m2", blocks(("m2", 8, 700, True))[0], 1)], 4000, memory_budget=1200)
row("admit:memory-subbudget-drop", "admit", {},
{"admitted": [bl.name for bl in admitted2],
"dropped": [d["name"] for d in adm2["dropped"]],
"memory_used": adm2["memory_used"],
"reasons": [d["reason"] for d in adm2["dropped"]]})
# mandatory overflow → raise
try:
bk._admit([("user", blocks(("user", 100, 5000, False))[0], 0)], 1000)
row("admit:mandatory-overflow-raises", "admit", {}, "NO-RAISE")
except ContextOverflowError as exc:
row("admit:mandatory-overflow-raises", "admit", {},
{"error": "ContextOverflowError", "detail": str(exc)})
# render: system folding, history pass-through, user capture
capsule = bk._render(blocks(("persona", 100, 7, False), ("tools", 90, 5, False),
("history", 70, 4, False), ("user", 100, 2, False)))
row("render:order-and-fold", "render", {},
{"token_count": capsule.token_count,
"messages": capsule.messages,
"system_preamble": capsule.system_preamble,
"user_content": capsule.user_content,
"blocks": [bl.name for bl in capsule.blocks]})
print(f"froze {len(rows)} rows total")
doc = {
"frozen_ts": "2026-10-12T00:00:00Z",
"generator": "scripts/gen_context_broker_truth.py — live oracle src/rune/context_broker.py, RUNE_COGNITION_CORE=python pinned",
"rows": rows,
}
out = ROOT / "src/go/context-broker/truth.json"
out.write_text(json.dumps(doc, indent=1, sort_keys=True) + "\n")
+378
View File
@@ -0,0 +1,378 @@
#!/usr/bin/env python3
"""Freeze the dichotomy-wave gap-closing truth corpus for
src/rune/structural_store.py (contract BUILDER-2026-10-13T00:32:22Z-bc3aae-
factory-gap-ss, issue #2479 tranche).
LAW (poisoned-truth): RUNE_STRUCTURAL_CORE=python is pinned at the top —
truth is generated by the PYTHON ORACLE ONLY. Inputs are deep-copied before
every oracle call (no leakage between cases). The clock is pinned by
monkeypatching structural_store.now (no-clock law on the Rust side).
The frozen corpus is embedded VERBATIM in the crate's cfg(test) battery
(src/frozen_battery.rs, include_str!) and replayed by
scripts/check_structural_store_parity.py through the live FFI.
"""
import calendar
import copy
import json
import os
import sys
import tempfile
import time as _time
import types
from pathlib import Path
os.environ["RUNE_STRUCTURAL_CORE"] = "python" # poisoned-truth law
REPO = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(REPO / "src"))
from rune import structural_store as ss # noqa: E402
FIXED_NOW = "2026-10-13T01:02:03Z"
FIXED_EPOCH = calendar.timegm(_time.strptime(FIXED_NOW, "%Y-%m-%dT%H:%M:%SZ"))
ss.now = lambda: FIXED_NOW # pin the clock
# snapshot() formats its stamp straight off time.strftime/gmtime — pin the
# module reference the oracle actually uses.
ss.time = types.SimpleNamespace(
gmtime=lambda: _time.gmtime(FIXED_EPOCH),
strftime=lambda fmt, t=None: _time.strftime(fmt, t if t is not None else _time.gmtime()),
)
OUT = (REPO / "src" / "rust" / "structural_store" / "src"
/ "frozen_ss_truth.json")
groups: dict[str, list] = {}
def case(group, name, args, expect):
groups.setdefault(group, []).append(
{"fn": group, "name": name, "args": args, "expect": expect})
def run(group, name, args, call):
"""Freeze one case: input deep-copied, oracle called, outcome recorded."""
frozen_args = copy.deepcopy(args)
try:
expect = {"ok": call(copy.deepcopy(args))}
except ValueError as exc:
expect = {"err": str(exc)}
case(group, name, frozen_args, expect)
def ent(id, kind="module", maturity="DECLARED", **kw):
e = {"id": id, "kind": kind, "maturity": maturity, "evidence_refs": []}
e.update(kw)
return e
def doc(*entities, rels=(), obs=(), **kw):
d = {"schema_version": 1, "generated_at": FIXED_NOW,
"entities": list(entities), "relationships": list(rels),
"observations": list(obs)}
d.update(kw)
return d
def rel(t, f, to, **kw):
r = {"type": t, "from": f, "to": to, "evidence_refs": ["ev"],
"observed_at": FIXED_NOW}
r.update(kw)
return r
def main():
# ── _sorted ─────────────────────────────────────────────────────────
for name, v in [
("dict_keys_sorted", {"b": 1, "a": {"z": 2, "y": [3, {"c": 4, "b": 5}]}}),
("list_of_dicts_by_canonical_form", [{"b": 2, "a": 1}, {"a": 9}, {"a": 1, "b": 2}, {}]),
("list_of_scalars", [3, 1, 2, "b", "a", True, None]),
("nested_lists", [[2], [1, 2], [1], []]),
("unicode_keys_and_values", {"é": "ü", "z": "a", "a": ["z", "é"]}),
("floats_and_ints", [1.5, 0.5, 2, 1, -1.25]),
("empty_containers", {"e": [], "f": {}, "g": [[]]}),
("dict_of_scalars_passthrough", {"x": 1, "y": "s", "z": None, "w": False}),
]:
run("sorted", name, {"value": v}, lambda a: ss._sorted(a["value"]))
# ── new_doc ─────────────────────────────────────────────────────────
run("new_doc", "basic", {"now": FIXED_NOW}, lambda a: ss.new_doc())
# ── _redact_secrets ─────────────────────────────────────────────────
for name, v in [
("exact_names", {"token": "t", "api_token": "t", "api_key": "t",
"apikey": "t", "password": "t", "secret": "t",
"auth_header": "t", "authorization": "t",
"credential": "t", "credentials": "t",
"private_key": "t", "access_token": "t",
"bearer": "t", "safe": "keep"}),
("substring_names", {"my_token": "t", "API_KEY": "t",
"user_password_hash": "t", "client_secret": "t",
"my_credentials_v2": "t", "token_count": "t"}),
("nested", {"a": {"token": "deep"}, "b": [{"password": "x"}, {"ok": 1}]}),
("non_secret_passthrough", {"a": 1, "b": "s", "c": None, "d": [1, 2]}),
("case_insensitive", {"Token": "t", "PASSWORD": "t"}),
]:
run("redact", name, {"value": v}, lambda a: ss._redact_secrets(a["value"]))
# ── save (byte law frozen from the real filesystem) ────────────────
with tempfile.TemporaryDirectory() as td:
def save_call(args):
p = ss.save(copy.deepcopy(args["doc"]),
Path(td) / args["name"] / "store.json")
return {"content": Path(p).read_text(), "doc": p and None}
# capture the mutated doc separately (save mutates schema_version in)
def save_case(name, d, expect_err=False):
args = {"doc": copy.deepcopy(d), "name": name}
try:
probe = copy.deepcopy(d)
p = ss.save(probe, Path(td) / name / "store.json")
content = Path(p).read_text()
expect = {"ok": {"content": content, "doc": probe}}
except ValueError as exc:
expect = {"err": str(exc)}
case("save", name, args, expect)
base = doc(ent("m:a"), rels=[rel("depends_on", "m:a", "m:b")], generated_at=FIXED_NOW)
save_case("full_doc", base)
save_case("no_schema_version", {k: v for k, v in base.items() if k != "schema_version"})
save_case("schema_one_float", {**base, "schema_version": 1.0})
save_case("schema_ninety_nine", {**base, "schema_version": 99})
save_case("schema_string", {**base, "schema_version": "x"})
save_case("secrets_redacted_on_disk", {**base, "credentials": {"token": "s3cret", "note": "x"}})
save_case("unicode_escaped", {**base, "note": "café ☕ — ok"})
save_case("empty_doc", {"schema_version": 1})
save_case("nested_empty", {**base, "ext": {"a": [], "b": {}}})
# ── canonical_digest ────────────────────────────────────────────────
dd = doc(ent("m:a"), rels=[rel("depends_on", "m:a", "m:b")])
run("digest", "basic", {"doc": dd}, lambda a: ss.canonical_digest(a["doc"]))
run("digest", "key_order_irrelevant",
{"doc": dd, "doc_reordered": {"entities": dd["entities"], "schema_version": 1,
"observations": dd["observations"],
"relationships": dd["relationships"],
"generated_at": dd["generated_at"]}},
lambda a: [ss.canonical_digest(a["doc"]), ss.canonical_digest(a["doc_reordered"])])
run("digest", "secret_never_changes_digest",
{"doc": {**dd, "token": "s3cret"}},
lambda a: [ss.canonical_digest(a["doc"]), ss.canonical_digest({**a["doc"], "token": "other"})])
run("digest", "unicode", {"doc": {**dd, "note": "é"}}, lambda a: ss.canonical_digest(a["doc"]))
# ── snapshot ────────────────────────────────────────────────────────
with tempfile.TemporaryDirectory() as td:
def snap_case(name, d, label, tag):
args = {"doc": copy.deepcopy(d), "dir": "snaps", "label": label, "tag": tag}
probe = copy.deepcopy(d)
p = ss.snapshot(probe, Path(td) / name / "snaps", label, tag=tag)
content = Path(p).read_text()
case("snapshot", name, args,
{"ok": {"path": "snaps/" + Path(p).name, "content": content, "doc": probe}})
snap_case("label_and_tag", dd, "l1", "deadbeef1234")
snap_case("no_label", dd, "", "deadbeef1234")
snap_case("empty_tag_is_noident", dd, "l1", "")
snap_case("short_tag", dd, "", "abc")
# ── alias ───────────────────────────────────────────────────────────
adoc = doc(ent("m:a"), ent("m:b"))
run("alias", "new_alias", {"doc": adoc, "cid": "m:a", "old": "m:old"},
lambda a: ss.alias(a["doc"], a["cid"], a["old"]))
run("alias", "idempotent",
{"doc": doc(ent("m:a", aliases=["m:old"])), "cid": "m:a", "old": "m:old"},
lambda a: ss.alias(a["doc"], a["cid"], a["old"]))
run("alias", "target_missing", {"doc": adoc, "cid": "m:zz", "old": "m:old"},
lambda a: ss.alias(a["doc"], a["cid"], a["old"]))
run("alias", "self_alias", {"doc": adoc, "cid": "m:a", "old": "m:a"},
lambda a: ss.alias(a["doc"], a["cid"], a["old"]))
run("alias", "via_alias_resolution",
{"doc": doc(ent("m:a", aliases=["m:old"])), "cid": "m:old", "old": "m:older"},
lambda a: ss.alias(a["doc"], a["cid"], a["old"]))
# ── spine_uri / resolve_uri ─────────────────────────────────────────
for name, eid in [("kind_name", "project:alpha"), ("no_colon", "alpha"),
("two_colons", "project:al:pha")]:
run("spine_uri", name, {"entity_id": eid}, lambda a: ss.spine_uri(a["entity_id"]))
for name, uri in [("ok", "spine://project/alpha"), ("no_slash", "spine://alpha"),
("wrong_scheme", "memory://project/alpha"), ("no_scheme", "project/alpha"),
("nested_slash", "spine://project/a/b")]:
run("resolve_uri", name, {"uri": uri}, lambda a: ss.resolve_uri(a["uri"]))
# ── _doc_authorities ────────────────────────────────────────────────
for name, d in [
("defaults_in", doc(ent("m:a"))),
("existing_untouched", {**doc(ent("m:a")), "authority_generations": {"p": {"generation": 2}}}),
]:
a = {"doc": copy.deepcopy(d)}
d2 = copy.deepcopy(d)
r = ss._doc_authorities(d2)
case("doc_authorities", name, a, {"ok": {"doc": d2, "ret": r}})
# ── authority_health ────────────────────────────────────────────────
run("authority_health", "empty", {"doc": doc(ent("m:a"))}, lambda a: ss.authority_health(a["doc"]))
run("authority_health", "populated",
{"doc": {**doc(ent("m:a")), "authority_generations": {
"z-proj": {"generation": 3, "status": "CURRENT", "last_success": FIXED_NOW,
"last_attempt": FIXED_NOW, "last_error": None, "observed": ["owns|a|b", "owns|c|d"]},
"a-proj": {"generation": 1, "status": "DEGRADED", "observed": []}}}},
lambda a: ss.authority_health(a["doc"]))
run("authority_health", "missing_fields",
{"doc": {**doc(ent("m:a")), "authority_generations": {"p": {}}}},
lambda a: ss.authority_health(a["doc"]))
# ── bind_task / task_entities ───────────────────────────────────────
for name, d, task_id, ids, ev in [
("with_evidence", doc(ent("m:a"), ent("m:b")), "T1", ["m:a", "m:b"], "ev-1"),
("fallback_evidence", doc(ent("m:a")), "T2", ["m:a"], ""),
("dedupes_relationships", doc(ent("m:a")), "T3", ["m:a", "m:a"], "ev"),
]:
a = {"doc": copy.deepcopy(d), "task_id": task_id, "entity_ids": ids, "evidence_ref": ev}
d2 = copy.deepcopy(d)
r = ss.bind_task(d2, task_id, copy.deepcopy(ids), ev)
case("bind_task", name, a, {"ok": {"doc": d2, "ret": r}})
tdoc = doc(ent("m:a"), ent("m:b"),
rels=[rel("affects", "task:T1", "m:b"), rel("affects", "task:T1", "m:a"),
rel("affects", "task:T9", "m:b")])
run("task_entities", "sorted_for_task", {"doc": tdoc, "task_id": "T1"},
lambda a: ss.task_entities(a["doc"], a["task_id"]))
run("task_entities", "no_bindings", {"doc": tdoc, "task_id": "TX"},
lambda a: ss.task_entities(a["doc"], a["task_id"]))
# ── impact_frontier (branch battery) ────────────────────────────────
def frontier_case(name, d, seeds, change_kind=None, max_level=2):
args = {"doc": copy.deepcopy(d), "seeds": seeds,
"change_kind": change_kind, "max_level": max_level}
run("impact_frontier", name, args,
lambda a: ss.impact_frontier(a["doc"], a["seeds"], max_level=a["max_level"],
change_kind=a["change_kind"]))
impl_doc = doc(
ent("m:core"), ent("m:app"), ent("m:lib"), ent("m:tests"), ent("m:deploy"),
rels=[
rel("depends_on", "m:app", "m:core"),
rel("implements", "m:app", "c:api"),
rel("implements", "m:lib", "c:api"),
rel("consumes", "m:core", "c:api"),
rel("verified_by", "m:app", "k:canary"),
rel("deployed_as", "m:core", "d:live"),
rel("documented_by", "m:core", "doc:r"),
rel("supersedes", "m:lib", "m:old"),
rel("consumed_by", "m:lib", "m:core"),
])
frontier_case("implementation_change", impl_doc, ["m:app"])
frontier_case("contract_change", impl_doc, ["c:api"])
frontier_case("contract_violation", impl_doc, ["c:api"], change_kind="contract_violation")
frontier_case("inferred_from_contract_kind", impl_doc, ["c:api"], change_kind=None)
frontier_case("stale_and_candidate_skipped",
doc(ent("m:a"), ent("m:b"), ent("m:c"),
rels=[rel("depends_on", "m:a", "m:b", status="STALE"),
rel("depends_on", "m:a", "m:c", status="CANDIDATE")]),
["m:a"])
frontier_case("non_obligation_never_expands",
doc(ent("m:a"), ent("m:b"),
rels=[rel("documented_by", "m:a", "m:b")]),
["m:a"])
frontier_case("consumed_by_reverse",
doc(ent("m:a"), ent("m:b"),
rels=[rel("consumed_by", "m:b", "m:a")]),
["m:a"])
frontier_case("level2_from_level1_and_contextual", impl_doc, ["m:core"])
frontier_case("multi_seed_dedup", impl_doc, ["m:app", "m:app", "m:core"])
frontier_case("alias_seed_kind",
doc(ent("c:api", kind="contract", aliases=["c:old"]),
ent("m:app"),
rels=[rel("implements", "m:app", "c:api")]),
["c:old"])
# ── record_observed_impact ──────────────────────────────────────────
for name, items in [
("basic", [{"entity": "m:a", "source": "test", "detail": "d", "authority": "ci"},
{"entity": "m:b"}]),
("empty_entity_skipped", [{"entity": "", "source": "x"}, {"entity": "m:a"}]),
("detail_truncated_300", [{"entity": "m:a", "detail": "x" * 500}]),
("detail_multibyte_truncated", [{"entity": "m:a", "detail": "é" * 400}]),
("defaults", [{"entity": "m:a"}]),
]:
d0 = doc(ent("m:a"), ent("m:b"))
a = {"doc": copy.deepcopy(d0), "task_id": "T1", "items": copy.deepcopy(items)}
d2 = copy.deepcopy(d0)
result = ss.record_observed_impact(d2, "T1", copy.deepcopy(items))
case("observed_impact", name, a, {"ok": {"doc": d2, "result": result}})
# ── predicted_vs_observed ───────────────────────────────────────────
pdoc = doc(ent("m:a"), ent("m:b"), ent("m:c"),
rels=[rel("depends_on", "m:a", "m:b"), rel("depends_on", "m:a", "m:c")])
pred = {"level0_direct": ["m:a"], "level1_obligations": ["m:b"],
"contracts": [], "level2_verification": []}
run("predicted_vs_observed", "miss_and_needless",
{"predicted": pred, "observed_doc": pdoc, "seeds": ["m:a"]},
lambda a: ss.predicted_vs_observed(a["predicted"], a["observed_doc"], a["seeds"]))
run("predicted_vs_observed", "exact_match",
{"predicted": {"level0_direct": ["m:a"], "level1_obligations": ["m:b", "m:c"],
"contracts": [], "level2_verification": []},
"observed_doc": pdoc, "seeds": ["m:a"]},
lambda a: ss.predicted_vs_observed(a["predicted"], a["observed_doc"], a["seeds"]))
# ── structural_events ───────────────────────────────────────────────
run("events", "full",
{"doc": doc(ent("m:a", observed_at=FIXED_NOW), ent("m:b", observed_at=FIXED_NOW),
rels=[rel("depends_on", "m:a", "m:b")],
obs=[{"kind": "superseded", "old": "x", "new": "y", "observed_at": FIXED_NOW}])},
lambda a: ss.structural_events(a["doc"]))
run("events", "observation_kind_default_and_type_override",
{"doc": doc(ent("m:a"),
obs=[{"note": "no kind"}, {"kind": "k1", "type": "explicit_type"}])},
lambda a: ss.structural_events(a["doc"]))
# ── diff ────────────────────────────────────────────────────────────
old = doc(ent("m:a"), ent("m:gone"), ent("m:same", identities={"p": 1}, maturity="DECLARED"),
rels=[rel("depends_on", "m:a", "m:gone")])
new = doc(ent("m:a"), ent("m:new"), ent("m:same", identities={"p": 2}, maturity="WIRED"),
rels=[rel("depends_on", "m:a", "m:new"), rel("depends_on", "m:a", "m:a")])
run("diff", "all_categories", {"old": old, "new": new}, lambda a: ss.diff(a["old"], a["new"]))
run("diff", "no_changes", {"old": old, "new": copy.deepcopy(old)},
lambda a: ss.diff(a["old"], a["new"]))
# ── dedupe_observations ─────────────────────────────────────────────
for name, d in [
("exact_dups", doc(ent("m:a"), obs=[{"kind": "k", "x": 1}, {"x": 1, "kind": "k"},
{"kind": "k", "x": 2}])),
("nested_dups", doc(ent("m:a"), obs=[{"a": {"z": 1, "b": [2]}}, {"a": {"b": [2], "z": 1}}])),
("empty", doc(ent("m:a"))),
]:
a = {"doc": copy.deepcopy(d)}
d2 = copy.deepcopy(d)
r = ss.dedupe_observations(d2)
case("dedupe", name, a, {"ok": {"doc": d2, "ret": r}})
# ── fixed-kernel regressions (oracle parity proofs) ─────────────────
for name, d, eid, state, ev, ret in [
("unknown_entity_returns_none", doc(ent("m:a")), "m:zz", "PRESENT", "e", None),
("via_alias", doc(ent("m:a", aliases=["m:old"])), "m:old", "PRESENT", "e", "PRESENT"),
("missing_entity_beats_bad_state", doc(ent("m:a")), "m:zz", "BOGUS", "", None),
("demotion_recorded", doc(ent("m:a", maturity="PRESENT")), "m:a", "DECLARED", "e2", "PRESENT"),
]:
a = {"doc": copy.deepcopy(d), "id": eid, "state": state, "ev": ev}
d2 = copy.deepcopy(d)
r = ss.promote(d2, eid, state, ev)
case("promote_fix", name, a, {"ok": {"ret": r, "doc": d2}})
run("resolve_status_fix", "status_verbatim",
{"entity": {"status": "WEIRD"}}, lambda a: ss.resolve_status(a["entity"]))
run("resolve_status_fix", "empty_conflicts_not_conflicting",
{"entity": {"maturity": "DECLARED", "ext": {"conflicts": []}}},
lambda a: ss.resolve_status(a["entity"]))
for name, d, nid, oid in [
("rel_has_observed_at", doc(ent("m:a"), ent("m:b")), "m:b", "m:a"),
("via_alias", doc(ent("m:a", aliases=["m:old"]), ent("m:b")), "m:b", "m:old"),
]:
a = {"doc": copy.deepcopy(d), "new": nid, "old": oid, "ev": "ev-s"}
d2 = copy.deepcopy(d)
ss.supersede(d2, nid, oid, "ev-s")
case("supersede_fix", name, a, {"ok": {"doc": d2}})
out = {"module": "structural_store", "now": FIXED_NOW, "groups": groups}
OUT.write_text(json.dumps(out, indent=1, sort_keys=True) + "\n")
total = sum(len(v) for v in groups.values())
print(f"frozen {total} cases across {len(groups)} groups -> {OUT}")
if __name__ == "__main__":
main()
+39
View File
@@ -0,0 +1,39 @@
#!/usr/bin/env python3
"""Open the #2479 dichotomy-wave gap-closing construction contract for
structural_store (PARTIAL → ≥80% fn coverage). Walks draft→approved→building
in ONE process (shared-store clobber hazard).
Usage: open_contract_gap_ss.py
"""
import json
import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "src"))
from rune.construction_interface import ConstructionInterface
iface = ConstructionInterface(builder="factory-gap-ss")
r = iface.submit_need(
"Close structural_store PARTIAL gaps (dichotomy wave, issue #2479 "
"tranche): port the 21 audit-missing functions name-preserving to the "
"existing Rust crate (doc-envelope FFI ss_*, ctypes loader mirroring "
"broker_loader.py, facade delegation with RUNE_STRUCTURAL_CORE=python "
"pin), frozen-truth battery generated against the live oracle, live "
"parity 100% on new fns, Phase B language-advantage pass.",
origin="builder",
)
print("submit_need:", json.dumps(r)[:400])
if not r.get("success"):
print("NEED FAILED — aborting")
sys.exit(1)
c = r["ids"]["contract_id"]
print("contract_id:", c)
for phase in ("approved", "building"):
t = iface.transition(c, phase)
print(f"transition->{phase}:", json.dumps(t)[:300])
if not t.get("success"):
sys.exit(1)
print("OPENED", c)
+141 -4
View File
@@ -24,6 +24,7 @@ import (
"fmt"
"os"
"path/filepath"
"strings"
"sync"
"testing"
)
@@ -200,10 +201,142 @@ type GapCase struct {
// import surface beyond encoding/json.
type jsonRaw = json.RawMessage
// DispatchGap — replay one frozen call through the ported function;
// return the JSON-comparable result and the post-call state pointer.
// Lives in production code (not a _test file) so ReplayPipeline builds
// under `go build` too. The _save_state case is IO-backed: callers that
// need byte-output parity use SaveState directly with their own dir.
func DispatchGap(state *BuildState, fn string, args []interface{}, stamp string) (result interface{}, outState *BuildState, err error) {
name := strings.TrimPrefix(fn, "BuildManager.")
arg := func(i int) interface{} {
if i < len(args) {
return args[i]
}
return nil
}
switch name {
case "assess_complexity":
return AssessComplexity(), state, nil
case "complete_assess":
CompleteAssess(state, arg(0).(map[string]interface{}), stamp)
case "start_research":
StartResearch(state, stamp)
case "complete_research":
CompleteResearch(state, arg(0).(map[string]interface{}), stamp)
case "start_feasible":
StartFeasible(state, stamp)
case "complete_feasible":
CompleteFeasible(state, arg(0) == true, arg(1).(string),
ArgMap(arg(2)), stamp)
case "start_consult":
StartConsult(state, stamp)
case "complete_consult":
CompleteConsult(state, StrList(arg(0)), stamp)
case "start_consent":
StartConsent(state, stamp)
case "complete_consent":
CompleteConsent(state, arg(0) == true, stamp)
case "start_plan":
StartPlan(state, stamp)
case "complete_plan":
CompletePlan(state, ObjList(arg(0)), stamp)
case "start_build_phase":
StartBuildPhase(state, stamp)
case "complete_build_phase":
CompleteBuildPhase(state, ObjList(arg(0)), stamp)
case "start_verify":
StartVerify(state, stamp)
case "complete_verify":
CompleteVerify(state, arg(0) == true, StrList(arg(1)), stamp)
case "start_correct":
StartCorrect(state, stamp)
case "complete_correct":
CompleteCorrect(state, arg(0) == true, stamp)
case "start_integrate":
StartIntegrate(state, stamp)
case "complete_integrate":
CompleteIntegrate(state, arg(0) == true, StrList(arg(1)), stamp)
case "start_record":
StartRecord(state, stamp)
case "complete_record":
CompleteRecord(state, stamp)
case "_save_state":
// IO seam: the serial battery covers save-bytes via SaveState
// with a test-owned dir; here persist to a throwaway dir for
// state-shape parity only.
dir, derr := os.MkdirTemp("", "gapreplay")
if derr != nil {
return nil, state, derr
}
defer os.RemoveAll(dir)
if serr := SaveState(state, dir); serr != nil {
return nil, state, serr
}
return nil, state, nil
case "get_resume_point":
rp := ResumePoint(state)
if rp == "" {
return nil, state, nil // Python None
}
return rp, state, nil
case "can_resume":
return CanResume(state), state, nil
case "get_status":
return BuildStatus(state), state, nil
default:
return nil, state, fmt.Errorf("unknown frozen fn %q", fn)
}
return nil, state, nil
}
// MustJSON — marshal for comparison (nil-safe: nil → "null", matching
// the oracle's None).
func MustJSON(v interface{}) []byte {
if v == nil {
return []byte("null")
}
b, _ := json.Marshal(v)
return b
}
// ArgMap / StrList / ObjList — typed JSON accessors for the frozen
// battery args (production helpers: DispatchGap needs them under
// `go build`, not just under `go test`).
func ArgMap(v interface{}) map[string]interface{} {
m, _ := v.(map[string]interface{})
return m
}
func StrList(v interface{}) []string {
arr, ok := v.([]interface{})
if !ok {
return nil
}
out := make([]string, 0, len(arr))
for _, e := range arr {
s, _ := e.(string)
out = append(out, s)
}
return out
}
func ObjList(v interface{}) []map[string]interface{} {
arr, ok := v.([]interface{})
if !ok {
return nil
}
out := make([]map[string]interface{}, 0, len(arr))
for _, e := range arr {
m, _ := e.(map[string]interface{})
out = append(out, m)
}
return out
}
// ReplayPipeline — replay frozen cases across goroutines (each case gets
// an isolated receiver rebuilt from its recv state) and reassemble the
// results in INPUT order. Parity: every result must canonicalize to the
// frozen expected bytes; the caller compares (dispatchGap is the same
// frozen expected bytes; the caller compares (DispatchGap is the same
// replay used by the serial battery).
func ReplayPipeline(t *testing.T, cases []GapCase, stamp string) []replayResult {
results := make([]replayResult, len(cases))
@@ -221,11 +354,15 @@ func ReplayPipeline(t *testing.T, cases []GapCase, stamp string) []replayResult
return
}
}
got, gotState := dispatchGap(t, state, c.Fn, c.Args, stamp)
got, gotState, derr := DispatchGap(state, c.Fn, c.Args, stamp)
if derr != nil {
t.Errorf("case %d dispatch: %v", i, derr)
return
}
results[i] = replayResult{
Name: c.Fn,
Got: mustJSON(got),
State: mustJSON(gotState),
Got: MustJSON(got),
State: MustJSON(gotState),
Expected: []byte(c.Expected.Result),
}
}(i)
+9 -75
View File
@@ -13,7 +13,6 @@ import (
"encoding/json"
"os"
"path/filepath"
"strings"
"testing"
)
@@ -75,88 +74,23 @@ func TestBuildModeGapParity(t *testing.T) {
}
}
// dispatchGap — replay one frozen call through the ported function; return
// the JSON-comparable result and the post-call state pointer.
// dispatchGap — thin test wrapper over the production DispatchGap: keeps
// the serial battery's call shape; the _save_state case uses the test's
// own TempDir (byte-output parity) instead of DispatchGap's throwaway dir.
func dispatchGap(t *testing.T, state *BuildState, fn string, args []interface{}, stamp string) (result interface{}, outState *BuildState) {
t.Helper()
name := strings.TrimPrefix(fn, "BuildManager.")
arg := func(i int) interface{} {
if i < len(args) {
return args[i]
}
return nil
}
switch name {
case "assess_complexity":
return AssessComplexity(), state
case "complete_assess":
CompleteAssess(state, arg(0).(map[string]interface{}), stamp)
case "start_research":
StartResearch(state, stamp)
case "complete_research":
CompleteResearch(state, arg(0).(map[string]interface{}), stamp)
case "start_feasible":
StartFeasible(state, stamp)
case "complete_feasible":
CompleteFeasible(state, arg(0) == true, arg(1).(string),
argMap(arg(2)), stamp)
case "start_consult":
StartConsult(state, stamp)
case "complete_consult":
CompleteConsult(state, strList(arg(0)), stamp)
case "start_consent":
StartConsent(state, stamp)
case "complete_consent":
CompleteConsent(state, arg(0) == true, stamp)
case "start_plan":
StartPlan(state, stamp)
case "complete_plan":
CompletePlan(state, objList(arg(0)), stamp)
case "start_build_phase":
StartBuildPhase(state, stamp)
case "complete_build_phase":
CompleteBuildPhase(state, objList(arg(0)), stamp)
case "start_verify":
StartVerify(state, stamp)
case "complete_verify":
CompleteVerify(state, arg(0) == true, strList(arg(1)), stamp)
case "start_correct":
StartCorrect(state, stamp)
case "complete_correct":
CompleteCorrect(state, arg(0) == true, stamp)
case "start_integrate":
StartIntegrate(state, stamp)
case "complete_integrate":
CompleteIntegrate(state, arg(0) == true, strList(arg(1)), stamp)
case "start_record":
StartRecord(state, stamp)
case "complete_record":
CompleteRecord(state, stamp)
case "_save_state":
if fn == "BuildManager._save_state" {
dir := t.TempDir()
if err := SaveState(state, dir); err != nil {
t.Fatalf("save: %v", err)
}
return nil, state
case "get_resume_point":
rp := ResumePoint(state)
if rp == "" {
return nil, state // Python None
}
return rp, state
case "can_resume":
return CanResume(state), state
case "get_status":
return BuildStatus(state), state
default:
t.Fatalf("unknown frozen fn %q", fn)
}
return nil, state
}
func argMap(v interface{}) map[string]interface{} {
m, _ := v.(map[string]interface{})
return m
got, gotState, err := DispatchGap(state, fn, args, stamp)
if err != nil {
t.Fatalf("dispatch %q: %v", fn, err)
}
return got, gotState
}
// canonEq — canonical-JSON comparison (Go-built values vs the frozen
+420
View File
@@ -0,0 +1,420 @@
package main
// broker.go — the ContextBroker orchestration core (admission, collection,
// rendering) from src/rune/context_broker.py, gap-close dichotomy wave.
// The Python class stays the runtime authority; this is the deterministic
// kernel with the contributor callbacks and failure recorder injectable.
import (
"errors"
"fmt"
"sort"
"sync"
)
// MemoryBlockNames — blocks that count as "memory" for the absence proof.
var MemoryBlockNames = map[string]bool{
"memory": true, "persona_memories": true, "consolidated": true,
}
// ErrContextOverflow — one block alone exceeds the input budget (fail loud).
var ErrContextOverflow = errors.New("context overflow")
// ContributionBlock — a typed, measurable piece of context. Content is
// either a string (system/user text) or []Message (history messages).
type ContributionBlock struct {
Name string
Priority int
Content interface{}
Tokens int
Source string
Evictable bool
}
// ToDict — serialize for pending_state embedding (P1-02 #25).
func (b *ContributionBlock) ToDict() map[string]interface{} {
return map[string]interface{}{
"name": b.Name,
"priority": b.Priority,
"content": b.Content,
"tokens": b.Tokens,
"source": b.Source,
"evictable": b.Evictable,
}
}
// ContributorFunc — callback(task, session) → block or nil.
type ContributorFunc func(task, session map[string]interface{}) *ContributionBlock
// FailureRecorder is the T1 record_failure seam (oracle: subsystem_health).
type FailureRecorder func(name string, err error)
// Contributor — a named registry entry with its priority bucket.
type Contributor struct {
Name string
Callback ContributorFunc
Bucket int
}
// ContextBroker — admission-controlled context assembler (Go kernel).
type ContextBroker struct {
mu sync.Mutex
contributors []Contributor
traces []map[string]interface{}
Estimator func(string) int
BudgetResolver func(int) int
Fail FailureRecorder
}
// NewContextBroker wires the defaults.
func NewContextBroker() *ContextBroker {
return &ContextBroker{
Estimator: DefaultEstimator,
BudgetResolver: DefaultBudget,
Fail: func(string, error) {},
}
}
// RegisterContributor names a contributor.
func (cb *ContextBroker) RegisterContributor(name string, callback ContributorFunc, bucket int) {
cb.mu.Lock()
defer cb.mu.Unlock()
cb.contributors = append(cb.contributors, Contributor{Name: name, Callback: callback, Bucket: bucket})
}
// TraceLog returns a shallow copy of every assemble trace (thread-safe).
func (cb *ContextBroker) TraceLog() []map[string]interface{} {
cb.mu.Lock()
defer cb.mu.Unlock()
out := make([]map[string]interface{}, len(cb.traces))
copy(out, cb.traces)
return out
}
// Collect runs contributors in registration order; failures are T1 events
// (one pass, lock held — side effects run exactly once per turn). Skips nil
// blocks and empty string content; malformed content (neither string nor a
// list of message dicts) is a T1 failure too (I5).
func (cb *ContextBroker) Collect(task, session map[string]interface{}) (collected []struct {
Name string
Block *ContributionBlock
Bucket int
}, degraded []string) {
cb.mu.Lock()
contributors := make([]Contributor, len(cb.contributors))
copy(contributors, cb.contributors)
cb.mu.Unlock()
degradedSet := map[string]bool{}
for _, c := range contributors {
block, err := func() (block *ContributionBlock, err error) {
defer func() {
if r := recover(); r != nil {
if e, ok := r.(error); ok {
err = e
} else {
err = fmt.Errorf("%v", r)
}
}
}()
return c.Callback(task, session), nil
}()
if err != nil {
cb.Fail(c.Name, err)
if !degradedSet[c.Name] {
degradedSet[c.Name] = true
degraded = append(degraded, c.Name)
}
continue
}
if block == nil {
continue
}
if s, isStr := block.Content.(string); isStr && s == "" {
continue
}
if !contentOK(block.Content) {
err := fmt.Errorf("contributor %q returned malformed content: %T", c.Name, block.Content)
cb.Fail(c.Name, err)
if !degradedSet[c.Name] {
degradedSet[c.Name] = true
degraded = append(degraded, c.Name)
}
continue
}
collected = append(collected, struct {
Name string
Block *ContributionBlock
Bucket int
}{c.Name, block, c.Bucket})
}
return collected, degraded
}
func contentOK(content interface{}) bool {
if s, isStr := content.(string); isStr {
return s != "" || true
}
list, isList := content.([]interface{})
if isList {
for _, item := range list {
m, isMap := item.(map[string]interface{})
if !isMap {
return false
}
if _, has := m["content"]; !has {
return false
}
}
return true
}
if _, isMsgs := content.([]Message); isMsgs {
return true
}
return false
}
// AdmissionEntry — one collected block paired with its registration name.
type AdmissionEntry struct {
Name string
Block *ContributionBlock
}
// TraceRecord — per-block admission trace row.
type TraceRecord struct {
Name string `json:"name"`
Priority int `json:"priority"`
Tokens int `json:"tokens"`
Source string `json:"source"`
Evictable bool `json:"evictable"`
Admitted bool `json:"admitted"`
Reason string `json:"reason"`
}
// AdmissionResult — admitted blocks + the full trace dict.
type AdmissionResult struct {
Admitted []*ContributionBlock
Blocks []TraceRecord
AdmittedTotal int
Dropped []TraceRecord
MemoryBudget int
MemoryUsed int
}
// Admit — deterministic admission: priority desc, tie: registration recency.
// Non-evictable blocks always admit first (overflow → ErrContextOverflow);
// evictable blocks admit while the budget lasts; memory-scope blocks are
// additionally bounded by the memory sub-budget (P1-03 #26).
func Admit(entries []AdmissionEntry, budget int, memoryBudget int) (*AdmissionResult, error) {
if memoryBudget < 0 {
memoryBudget = DefaultMemoryBudget(0)
}
type keyed struct {
idx int
entry AdmissionEntry
}
items := make([]keyed, len(entries))
for i, e := range entries {
items[i] = keyed{i, e}
}
sort.SliceStable(items, func(a, b int) bool {
ba, bb := items[a].entry.Block, items[b].entry.Block
ka, kb := 0, 0
if ba.Evictable {
ka = 1
}
if bb.Evictable {
kb = 1
}
if ka != kb {
return ka < kb
}
if ba.Priority != bb.Priority {
return ba.Priority > bb.Priority
}
return items[a].idx < items[b].idx
})
res := &AdmissionResult{MemoryBudget: memoryBudget}
used, memoryUsed := 0, 0
for _, it := range items {
block := it.entry.Block
record := TraceRecord{
Name: block.Name, Priority: block.Priority, Tokens: block.Tokens,
Source: block.Source, Evictable: block.Evictable,
Admitted: true,
}
if !block.Evictable {
if block.Tokens > budget {
return nil, fmt.Errorf("mandatory block '%s' needs %d tokens but the input budget is %d — refusing to silently truncate: %w",
block.Name, block.Tokens, budget, ErrContextOverflow)
}
res.Admitted = append(res.Admitted, block)
used += block.Tokens
res.Blocks = append(res.Blocks, record)
continue
}
inMemoryScope := MemoryBlockNames[block.Name]
if inMemoryScope && used+block.Tokens <= budget && memoryUsed+block.Tokens > memoryBudget {
record.Admitted = false
record.Reason = fmt.Sprintf("memory budget: %d+%d>%d (memory sub-budget exhausted; lowest-priority memory dropped)",
memoryUsed, block.Tokens, memoryBudget)
res.Blocks = append(res.Blocks, record)
continue
}
if used+block.Tokens <= budget {
res.Admitted = append(res.Admitted, block)
used += block.Tokens
if inMemoryScope {
memoryUsed += block.Tokens
}
res.Blocks = append(res.Blocks, record)
} else {
record.Admitted = false
record.Reason = fmt.Sprintf("budget: %d+%d>%d (lowest-priority evictable dropped)",
used, block.Tokens, budget)
res.Blocks = append(res.Blocks, record)
}
}
for _, record := range res.Blocks {
if !record.Admitted {
res.Dropped = append(res.Dropped, record)
}
}
res.AdmittedTotal = used
res.MemoryUsed = memoryUsed
return res, nil
}
// Capsule — the rendered attention capsule.
type Capsule struct {
TokenCount int
Blocks []*ContributionBlock
Messages []map[string]interface{}
SystemPreamble string
UserContent string
}
// Render — admitted blocks into the typed capsule (fixed message order:
// system -> tools -> memory/notes -> history -> user via priority sort).
func Render(admitted []*ContributionBlock) *Capsule {
type keyed struct {
idx int
block *ContributionBlock
}
items := make([]keyed, len(admitted))
for i, b := range admitted {
items[i] = keyed{i, b}
}
sort.SliceStable(items, func(a, b int) bool {
if items[a].block.Priority != items[b].block.Priority {
return items[a].block.Priority > items[b].block.Priority
}
return items[a].idx < items[b].idx
})
var systemParts []string
var history []map[string]interface{}
var userBlock *ContributionBlock
var capsuleBlocks []*ContributionBlock
totalTokens := 0
for _, it := range items {
block := it.block
totalTokens += block.Tokens
capsuleBlocks = append(capsuleBlocks, block)
if s, isStr := block.Content.(string); isStr {
if block.Name == "user" {
userBlock = block
} else {
systemParts = append(systemParts, s)
}
} else {
switch list := block.Content.(type) {
case []Message:
for _, m := range list {
history = append(history, deepCopyMap(m))
}
case []interface{}:
for _, item := range list {
if m, ok := item.(map[string]interface{}); ok {
history = append(history, deepCopyMap(m))
}
}
}
}
}
systemPrompt := ""
for i, p := range systemParts {
if i > 0 {
systemPrompt += "\n"
}
systemPrompt += p
}
rendered := make([]map[string]interface{}, 0, len(history)+1)
if systemPrompt != "" {
rendered = append(rendered, map[string]interface{}{"role": "system", "content": systemPrompt})
}
rendered = append(rendered, history...)
userContent := ""
if userBlock != nil {
userContent, _ = userBlock.Content.(string)
}
return &Capsule{
TokenCount: totalTokens,
Blocks: capsuleBlocks,
Messages: rendered,
SystemPreamble: systemPrompt,
UserContent: userContent,
}
}
func deepCopyMap(m map[string]interface{}) map[string]interface{} {
out := make(map[string]interface{}, len(m))
for k, v := range m {
if vm, ok := v.(map[string]interface{}); ok {
out[k] = deepCopyMap(vm)
} else if vl, ok := v.([]interface{}); ok {
out[k] = deepCopyList(vl)
} else {
out[k] = v
}
}
return out
}
func deepCopyList(l []interface{}) []interface{} {
out := make([]interface{}, len(l))
for i, v := range l {
if vm, ok := v.(map[string]interface{}); ok {
out[i] = deepCopyMap(vm)
} else if vl, ok := v.([]interface{}); ok {
out[i] = deepCopyList(vl)
} else {
out[i] = v
}
}
return out
}
func blockNames(blocks []*ContributionBlock) []string {
out := make([]string, 0, len(blocks))
for _, b := range blocks {
out = append(out, b.Name)
}
return out
}
// BuildMemorySelector — the memory-selection authority composition
// (_build_memory_selector, P1-03 #26). Fail-closed: any construction error
// returns nil and the memory contributor abstains — never a fallback
// injection. The selector itself stays Python (rune.memory_selector).
func BuildMemorySelector(ctor func() (interface{}, error)) interface{} {
sel, err := ctor()
if err != nil {
return nil
}
return sel
}
+708 -13
View File
@@ -1,20 +1,61 @@
// Package main — context-broker: the GO port of src/rune/context_broker.py's
// budget/estimator kernels (Oracle-Battery Parity Pattern, #2479 GO batch 38).
// budget/estimator kernels (Oracle-Battery Parity Pattern, #2479 GO batch 38;
// gap-close dichotomy wave — factory-gap-ctxbroker).
// The ContextBroker itself stays Python (it's the orchestration authority);
// the budget math is the deterministic core.
// the budget math is the deterministic core. The gap-close ports the missing
// functions name-preserving: the _py_* parity-floor oracles, the discovered-
// window registry, the rolling-summary store, _through_id, log_calibration,
// and the compact_history/summarize_history state machines with IO behind
// injectable seams (Estimator/Summarizer/Discoverer funcs, Archiver writer).
package main
import (
"crypto/sha256"
"encoding/json"
"fmt"
"math"
"os"
"sort"
"strconv"
"strings"
"sync"
)
// defaultEstimator — chars/4 (code-point parity with Python len()).
func defaultEstimator(text string) int { return len([]rune(text)) / 4 }
// ── estimator / budget kernels ──────────────────────────────────────────────
// defaultBudget — window - max(2048, window/4) - window/20, floored at 1.
func defaultBudget(window int) int {
// DefaultEstimator — chars/4 (code-point parity with Python len()).
func DefaultEstimator(text string) int { return len([]rune(text)) / 4 }
// PyDefaultEstimator — the Python parity-floor oracle (_py_default_estimator).
// Python: len(text or "") // 4 — None coerces to "".
func PyDefaultEstimator(text *string) int {
if text == nil {
return 0
}
return len([]rune(*text)) / 4
}
// CcCore — the cognition-core availability seam (_cc_core). The Go port IS
// the core, so the Python-pin question collapses: RUNE_COGNITION_CORE=python
// pins the Python floor (the caller compares against it), anything else with
// the Go core linked is available. Env read is injectable via CcCoreEnv.
var CcCoreEnv = os.LookupEnv
// CcCore reports whether the native core would be used under the given env.
// Mirrors the oracle's branch shape: pin -> None; loader probe failure -> None.
func CcCore(lookup func(string) (string, bool), loaderAvailable bool) *bool {
if v, ok := lookup("RUNE_COGNITION_CORE"); ok && v == "python" {
return nil
}
if !loaderAvailable {
return nil
}
avail := true
return &avail
}
// DefaultBudget — window - max(2048, window/4) - window/20, floored at 1.
func DefaultBudget(window int) int {
reserve := window / 4
if reserve < 2048 {
reserve = 2048
@@ -26,10 +67,13 @@ func defaultBudget(window int) int {
return r
}
// PyDefaultBudget — the Python parity-floor oracle (_py_default_budget).
func PyDefaultBudget(window int) int { return DefaultBudget(window) }
const MemoryBudgetCap = 1200
// defaultMemoryBudget — min(cap, window/8); window<=0 → CAP.
func defaultMemoryBudget(window int) int {
// DefaultMemoryBudget — min(cap, window/8); window<=0 → CAP.
func DefaultMemoryBudget(window int) int {
if window <= 0 {
return MemoryBudgetCap
}
@@ -40,6 +84,536 @@ func defaultMemoryBudget(window int) int {
return r
}
// PyDefaultMemoryBudget — the Python parity-floor oracle
// (_py_default_memory_budget). Python: `if not window or window <= 0`.
func PyDefaultMemoryBudget(window int) int { return DefaultMemoryBudget(window) }
// PyMemoryDisabledValue — the Python parity-floor oracle over the RAW string
// (_py_memory_disabled_value): truthy unless in the falsy set.
func PyMemoryDisabledValue(value string) bool {
switch value {
case "", "0", "false", "no", "off":
return false
}
return true
}
// MemoryDisabled — the full gate (memory_disabled): Python cross-check ANDed
// with the native verdict when the core is live.
func MemoryDisabled(raw string, coreDisabled bool) bool {
gate := PyMemoryDisabledValue(raw)
return coreDisabled && gate
}
// ── discovered-window registry (reset_discovered_windows/register_window/
// default_window) — process-local registry, live discovery injectable ────
const FallbackWindow = 8192
var (
discoveredMu sync.Mutex
discovered = map[string]int{}
)
// DiscoverWindow is the injectable seam for live context-length discovery
// (the oracle imports rune.local_tool_runtime.context_length). nil = no
// discovery available (offline), which freezes the fallback branch.
var DiscoverWindow func(modelID string) int
// ResetDiscoveredWindows clears the discovered-window registry (I3).
func ResetDiscoveredWindows() {
discoveredMu.Lock()
defer discoveredMu.Unlock()
discovered = map[string]int{}
}
// RegisterWindow records a discovered context length for modelID (Task 2).
func RegisterWindow(modelID string, window int) {
discoveredMu.Lock()
defer discoveredMu.Unlock()
discovered[modelID] = window
}
// DefaultWindow resolves the model window via the registry, then live
// discovery, then the conservative fallback. The fallback (8192) never
// enters the cache (C3: a transient discovery failure must not poison the
// window for the process lifetime).
func DefaultWindow(modelID string) int {
discoveredMu.Lock()
d := DiscoverWindow
discoveredMu.Unlock()
return resolveWindow(modelID, d)
}
// resolveWindow — the discovery-injectable core of DefaultWindow; the
// discover func is captured per call so concurrent resolvers never swap a
// shared global.
func resolveWindow(modelID string, discover func(string) int) int {
if modelID == "" {
return FallbackWindow
}
discoveredMu.Lock()
w, ok := discovered[modelID]
discoveredMu.Unlock()
if ok {
return w
}
if discover == nil {
return FallbackWindow
}
window := discover(modelID)
if window != 0 && window != FallbackWindow {
RegisterWindow(modelID, window)
}
return window
}
// ── rolling-summary store (C1/#141) ─────────────────────────────────────────
// RollingState — the persisted rolling summary for one session key.
type RollingState struct {
Summary string `json:"summary"`
ThroughCount int `json:"through_count"`
ThroughID string `json:"through_id"`
}
var (
rollingMu sync.Mutex
rolling = map[string]*RollingState{} // key: source + "\x00" + session_id
)
func rollingKey(source, sessionID string) string { return source + "\x00" + sessionID }
// RollingSummaryState returns the persisted rolling summary for a session
// (deep-copied), or nil.
func RollingSummaryState(source, sessionID string) *RollingState {
rollingMu.Lock()
defer rollingMu.Unlock()
st, ok := rolling[rollingKey(source, sessionID)]
if !ok {
return nil
}
cp := *st
return &cp
}
// StoreRollingSummary persists a rolling summary keyed by (source, sessionID).
func StoreRollingSummary(source, sessionID, summaryText string, throughCount int, throughID string) {
rollingMu.Lock()
defer rollingMu.Unlock()
rolling[rollingKey(source, sessionID)] = &RollingState{
Summary: summaryText,
ThroughCount: throughCount,
ThroughID: throughID,
}
}
// EvictRolling — LRU (insertion-order) eviction for dead sessions; returns
// the evicted count.
func EvictRolling(maxSessions int) int {
rollingMu.Lock()
defer rollingMu.Unlock()
if len(rolling) <= maxSessions {
return 0
}
keys := make([]string, 0, len(rolling))
for k := range rolling {
keys = append(keys, k)
}
sort.Strings(keys) // deterministic order (Python relies on dict insertion order)
drop := len(rolling) - maxSessions
for _, k := range keys[:drop] {
delete(rolling, k)
}
return drop
}
// ThroughID — identity of the coverage boundary: first 16 hex chars of the
// sha256 of the first message BEYOND coverage (content, not position —
// rotation-proof, #141). Empty when the boundary is outside the list.
func ThroughID(messages []map[string]interface{}, throughCount int) string {
if throughCount < 0 || throughCount >= len(messages) {
return ""
}
anchor := messages[throughCount]
sum := sha256.Sum256([]byte(pyDumpsSorted(anchor)))
return fmt.Sprintf("%x", sum[:])[:16]
}
// pyDumpsSorted renders json.dumps(anchor, sort_keys=True) byte-for-byte:
// separators ", " / ": ", ensure_ascii escaping, Python literal literals.
func pyDumpsSorted(v interface{}) string {
var b strings.Builder
writePyJSON(&b, v)
return b.String()
}
func writePyJSON(b *strings.Builder, v interface{}) {
switch val := v.(type) {
case nil:
b.WriteString("null")
case bool:
if val {
b.WriteString("true")
} else {
b.WriteString("false")
}
case float64:
b.WriteString(pyFloat(val))
case int:
b.WriteString(fmt.Sprintf("%d", val))
case string:
writePyString(b, val)
case []interface{}:
b.WriteString("[")
for i, item := range val {
if i > 0 {
b.WriteString(", ")
}
writePyJSON(b, item)
}
b.WriteString("]")
case map[string]interface{}:
keys := make([]string, 0, len(val))
for k := range val {
keys = append(keys, k)
}
sort.Strings(keys)
b.WriteString("{")
for i, k := range keys {
if i > 0 {
b.WriteString(", ")
}
writePyString(b, k)
b.WriteString(": ")
writePyJSON(b, val[k])
}
b.WriteString("}")
default:
blob, _ := json.Marshal(val)
b.Write(blob)
}
}
func pyFloat(f float64) string {
if f == float64(int64(f)) && math.Abs(f) < 1e15 {
// integral numbers via JSON can only originate as Python ints —
// json.dumps renders int(3) as "3", never "3.0"
return fmt.Sprintf("%d", int64(f))
}
return strconv.FormatFloat(f, 'g', -1, 64)
}
func writePyString(b *strings.Builder, s string) {
b.WriteByte('"')
for _, r := range s {
switch r {
case '"':
b.WriteString("\\\"")
case '\\':
b.WriteString("\\\\")
case '\n':
b.WriteString("\\n")
case '\r':
b.WriteString("\\r")
case '\t':
b.WriteString("\\t")
case '\b':
b.WriteString("\\b")
case '\f':
b.WriteString("\\f")
default:
if r < 0x20 || r > 0x7e {
if r > 0xffff {
r2 := r - 0x10000
hi := 0xd800 + (r2 >> 10)
lo := 0xdc00 + (r2 & 0x3ff)
fmt.Fprintf(b, "\\u%04x\\u%04x", hi, lo)
} else {
fmt.Fprintf(b, "\\u%04x", r)
}
} else {
b.WriteRune(r)
}
}
}
b.WriteByte('"')
}
// LogCalibration — compare the broker estimate with the served
// prompt_eval_count. LOG ONLY (drift never feeds admission). Returns the
// signed drift; 0 when nothing was served.
func LogCalibration(measuredTokens int, promptEvalCount *int, model string) int {
if promptEvalCount == nil || *promptEvalCount == 0 {
return 0
}
return *promptEvalCount - measuredTokens
}
// ── compaction state machine (compact_history) ──────────────────────────────
// Compaction thresholds (mirrors the oracle constants).
const (
CompactionThreshold = 0.8
CompactionKeepTurns = 5
CompactionNumPredict = 256
)
// Message — one conversation turn (loosely typed like the Python dict).
type Message = map[string]interface{}
// Archiver is the injectable seam for the raw-history archive append
// (oracle: <rune_home>/state/history_archive.jsonl). Returns an error to
// exercise the T2 archive-failure branch.
type Archiver func(record map[string]interface{}) error
// Summarizer is the injectable seam for the rolling-summary call
// (oracle: summarize_history via OllamaChatTransport). An error is a T2
// storage failure: compaction is skipped, the caller degrades.
type Summarizer func(messages []Message) (string, error)
// CompactEvent — the compaction event dict returned alongside the compacted
// messages.
type CompactEvent struct {
Triggered bool `json:"triggered"`
TokensBefore int `json:"tokens_before"`
TokensAfter int `json:"tokens_after"`
TurnsSummarized int `json:"turns_summarized"`
Archived bool `json:"archived"`
Failed bool `json:"failed"`
Source string `json:"source"`
Persisted bool `json:"persisted"`
DegradedToTail *int `json:"degraded_to_tail,omitempty"`
Summary string `json:"summary,omitempty"`
}
// CompactHistory — archive-then-compress state machine, logic 1:1 with the
// oracle (rolling persistence, #141 identity rematch, C2 tail degrade).
// source is fixed "operator" by the assemble path; sessionID "" disables
// persistence. archivePath is the injected archive location (the oracle's
// Path parameter): the label is embedded in the summary block; "" renders
// the oracle's 'external storage' fallback.
func CompactHistory(messages []Message, window int, archivePath string, archiver Archiver, summarizer Summarizer, source, sessionID string) ([]Message, *CompactEvent) {
tokensBefore := 0
for _, m := range messages {
tokensBefore += DefaultEstimator(messageContent(m))
}
event := &CompactEvent{
TokensBefore: tokensBefore,
TokensAfter: tokensBefore,
Source: source,
}
if len(messages) == 0 {
return messages, event
}
totalTurns := 0
for _, m := range messages {
if role, _ := m["role"].(string); role == "user" {
totalTurns++
}
}
if totalTurns <= CompactionKeepTurns {
return messages, event
}
keepMessages := CompactionKeepTurns * 2
split := len(messages) - keepMessages
if split < 0 {
split = 0
}
older, recent := messages[:split], messages[split:]
// C1 rolling persistence + #141 identity rematch.
sessionKey := sessionID != ""
var prior *RollingState
if sessionKey {
prior = RollingSummaryState(source, sessionID)
}
through := 0
if prior != nil {
through = prior.ThroughCount
if prior.ThroughID != "" {
var anchor Message
if 0 <= through && through < len(older) {
anchor = older[through]
}
if anchor != nil && ThroughID(older, through) != prior.ThroughID {
// window rotated under the stored count: rematch by content
found := -1
for i := range older {
if ThroughID(older, i) == prior.ThroughID {
found = i
break
}
}
if found >= 0 {
through = found
} else {
prior = nil
through = 0
}
}
}
}
var newOlder, archiveMessages []Message
if prior != nil && through <= len(older) {
newOlder = older[through:]
if len(newOlder) == 0 {
// Nothing new beyond the rolled summary: reuse it verbatim.
summaryBlock := Message{
"role": "user",
"content": "## Conversation summary (rolling)\n" + prior.Summary +
"\n(Earlier turns were archived; recent turns verbatim.)",
}
compacted := append([]Message{summaryBlock}, recent...)
event.Triggered = true
event.TokensAfter = sumTokens(compacted)
event.TurnsSummarized = 0
event.Summary = prior.Summary
event.Persisted = true
return compacted, event
}
archiveMessages = newOlder
} else {
newOlder = older
if prior == nil {
archiveMessages = messages
} else if through <= len(older) {
archiveMessages = older[through:]
} else {
archiveMessages = older
}
if prior != nil && through > len(older) {
// History shrank (ledger rotation): summarize from scratch.
prior = nil
through = 0
archiveMessages = messages
}
}
// ARCHIVE FIRST — raw, verbatim, before any lossy compression.
if archiver != nil {
record := map[string]interface{}{
"session_id": sessionID,
"source": source,
"messages": archiveMessages,
}
if err := archiver(record); err != nil {
event.Failed = true
return messages, event
}
event.Archived = true
}
var summaryText string
{
var input []Message
if prior != nil {
input = append([]Message{{"role": "user", "content": "Previous rolling summary:\n" + prior.Summary}}, newOlder...)
} else {
input = older
}
text, err := summarizer(input)
if err != nil {
event.Failed = true
// C2: degrade to the newest tail that fits the window budget.
tailBudget := DefaultBudget(window)
var tail []Message
used := 0
for i := len(messages) - 1; i >= 0; i-- {
message := messages[i]
cost := DefaultEstimator(messageContent(message))
if used+cost > tailBudget && len(tail) > 0 {
break
}
tail = append([]Message{message}, tail...)
used += cost
}
event.DegradedToTail = &[]int{len(tail)}[0]
return tail, event
}
summaryText = strings.TrimSpace(text)
}
archiveLabel := "external storage"
if archivePath != "" {
archiveLabel = archivePath
}
summaryBlock := Message{
"role": "user",
"content": "## Conversation summary (rolling)\n" + summaryText +
"\n(Earlier turns were archived to " + archiveLabel + "; " +
fmt.Sprintf("%d", len(older)) + " messages summarized, recent turns verbatim.)",
}
compacted := append([]Message{summaryBlock}, recent...)
if sessionKey {
newThrough := through + len(newOlder)
StoreRollingSummary(source, sessionID, summaryText, newThrough, ThroughID(messages, newThrough))
}
event.Triggered = true
event.TokensAfter = sumTokens(compacted)
event.TurnsSummarized = len(newOlder)
event.Summary = summaryText
event.Persisted = sessionKey
return compacted, event
}
func messageContent(m Message) string {
c, _ := m["content"].(string)
return c
}
func sumTokens(messages []Message) int {
total := 0
for _, m := range messages {
total += DefaultEstimator(messageContent(m))
}
return total
}
// ── summarize_history transport seam ────────────────────────────────────────
// SummarizeHistory builds the structured rolling-summary prompt and calls the
// injected transport. The transport (OllamaChatTransport in the oracle) is
// IO; here it is injectable. Empty/blank content raises (caller degrades).
func SummarizeHistory(messages []Message, model string, numPredict int, numCtx int, transport func(prompt, model string, options map[string]interface{}) (string, error)) (string, error) {
parts := make([]string, 0, len(messages))
for _, m := range messages {
role := "?"
if r, ok := m["role"].(string); ok && r != "" {
role = r
}
parts = append(parts, role+": "+messageContent(m))
}
transcript := strings.Join(parts, "\n")
prompt := "Summarize this conversation segment into a rolling summary.\n" +
"Answer with exactly these four labeled sections:\n" +
"Session intent: <what this session is trying to accomplish>\n" +
"Decisions: <choices made so far>\n" +
"Open items: <unresolved threads>\n" +
"Next steps: <what to do next>\n\n" +
"Conversation:\n" + transcript
options := map[string]interface{}{
"temperature": 0.2,
"num_predict": numPredict,
}
if numCtx != 0 {
options["num_ctx"] = numCtx
}
content, err := transport(prompt, model, options)
if err != nil {
return "", err
}
if strings.TrimSpace(content) == "" {
return "", fmt.Errorf("summarizer returned empty content")
}
return strings.TrimSpace(content), nil
}
// ── replay CLI (legacy truth.json flat-case driver, kept green) ─────────────
func main() {
if len(os.Args) < 2 || os.Args[1] != "replay" {
fmt.Fprintln(os.Stderr, "usage: context-broker replay <truth.json>")
@@ -52,8 +626,18 @@ func main() {
}
var cases []map[string]any
if err := json.Unmarshal(raw, &cases); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
// New-format truth doc ({frozen_ts, generator, rows}) — the legacy
// flat ops ride inside row["input"]["ops"] / scalar cases; nothing to
// replay here, the battery test covers them.
var doc struct {
Rows []json.RawMessage `json:"rows"`
}
if err2 := json.Unmarshal(raw, &doc); err2 != nil || len(doc.Rows) == 0 {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
fmt.Printf("PARITY OK: %d battery rows (go test battery replays them)\n", len(doc.Rows))
return
}
fails := 0
for i, c := range cases {
@@ -62,19 +646,19 @@ func main() {
switch op {
case "estimator":
text, _ := c["text"].(string)
if got := defaultEstimator(text); got != want {
if got := DefaultEstimator(text); got != want {
fails++
fmt.Printf("case %d estimator %q: got %d want %d\n", i, text, got, want)
}
case "budget":
w := int(c["window"].(float64))
if got := defaultBudget(w); got != want {
if got := DefaultBudget(w); got != want {
fails++
fmt.Printf("case %d budget %d: got %d want %d\n", i, w, got, want)
}
case "mem_budget":
w := int(c["window"].(float64))
if got := defaultMemoryBudget(w); got != want {
if got := DefaultMemoryBudget(w); got != want {
fails++
fmt.Printf("case %d mem_budget %d: got %d want %d\n", i, w, got, want)
}
@@ -86,3 +670,114 @@ func main() {
}
fmt.Printf("PARITY OK: %d cases vs the Python oracle\n", len(cases))
}
// ── eval CLI (live-parity harness: one JSON payload on stdin → JSON out) ────
func init() {
if len(os.Args) >= 2 && os.Args[1] == "eval" {
var payload map[string]interface{}
if err := json.NewDecoder(os.Stdin).Decode(&payload); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
out := evalOne(payload)
blob, _ := json.Marshal(out)
fmt.Println(string(blob))
os.Exit(0)
}
}
func evalOne(p map[string]interface{}) interface{} {
op, _ := p["op"].(string)
switch op {
case "est":
if t, ok := p["text"]; ok {
s, _ := t.(string)
return PyDefaultEstimator(&s)
}
return PyDefaultEstimator(nil)
case "budget":
return PyDefaultBudget(int(p["window"].(float64)))
case "membud":
return PyDefaultMemoryBudget(int(p["window"].(float64)))
case "memoff":
v, _ := p["value"].(string)
return PyMemoryDisabledValue(v)
case "through":
rawMsgs, _ := p["messages"].([]interface{})
msgs := make([]Message, 0, len(rawMsgs))
for _, rm := range rawMsgs {
m, _ := rm.(map[string]interface{})
msgs = append(msgs, m)
}
return ThroughID(msgs, int(p["through"].(float64)))
case "admit":
rawEntries, _ := p["entries"].([]interface{})
entries := make([]AdmissionEntry, 0, len(rawEntries))
for _, re := range rawEntries {
e, _ := re.(map[string]interface{})
b := &ContributionBlock{}
b.Name, _ = e["name"].(string)
b.Priority = int(e["priority"].(float64))
b.Tokens = int(e["tokens"].(float64))
b.Source, _ = e["source"].(string)
b.Evictable, _ = e["evictable"].(bool)
entries = append(entries, AdmissionEntry{Name: b.Name, Block: b})
}
budget := int(p["budget"].(float64))
memBudget := int(p["memory_budget"].(float64))
res, err := Admit(entries, budget, memBudget)
if err != nil {
return map[string]interface{}{"error": "ContextOverflowError", "detail": err.Error()}
}
blocksOut := make([]map[string]interface{}, 0, len(res.Blocks))
for _, r := range res.Blocks {
blocksOut = append(blocksOut, map[string]interface{}{
"name": r.Name, "priority": r.Priority, "tokens": r.Tokens,
"source": r.Source, "evictable": r.Evictable,
"admitted": r.Admitted, "reason": r.Reason,
})
}
dropped := make([]string, 0, len(res.Dropped))
for _, d := range res.Dropped {
dropped = append(dropped, d.Name)
}
return map[string]interface{}{
"admitted": blockNames(res.Admitted), "blocks": blocksOut,
"admitted_total": res.AdmittedTotal, "dropped": dropped,
"memory_budget": res.MemoryBudget, "memory_used": res.MemoryUsed,
}
case "render":
rawBlocks, _ := p["blocks"].([]interface{})
admitted := make([]*ContributionBlock, 0, len(rawBlocks))
for _, rb := range rawBlocks {
b, _ := rb.(map[string]interface{})
blk := &ContributionBlock{}
blk.Name, _ = b["name"].(string)
blk.Priority = int(b["priority"].(float64))
blk.Tokens = int(b["tokens"].(float64))
blk.Source, _ = b["source"].(string)
switch c := b["content"].(type) {
case string:
blk.Content = c
case []interface{}:
msgList := make([]Message, 0, len(c))
for _, rm := range c {
m, _ := rm.(map[string]interface{})
msgList = append(msgList, m)
}
blk.Content = msgList
}
admitted = append(admitted, blk)
}
capsule := Render(admitted)
return map[string]interface{}{
"token_count": capsule.TokenCount,
"messages": capsule.Messages,
"system_preamble": capsule.SystemPreamble,
"user_content": capsule.UserContent,
"blocks": blockNames(capsule.Blocks),
}
}
return nil
}
+106
View File
@@ -0,0 +1,106 @@
package main
// Phase B language-advantage pass — dichotomy wave. Three Go-side wins the
// Python oracle can't express:
//
// 1. Concurrent window discovery + rolling-store access under -race: the
// broker's registries are mutex-guarded; Go proves thread-safety that
// the Python module-level dicts only get from the GIL.
// 2. EstimateAll — batch token estimation fans out across goroutines and
// reassembles in input order (the estimator is pure, so the batch is
// embarrassingly parallel; the Python loop is serial).
// 3. A typed BlockSource interface + name-keyed admission registry with
// compile-time satisfaction assertions (Python dispatches through
// isinstance checks; Go turns the dispatch into data).
import (
"sort"
"sync"
)
// ── 1. concurrent registries ────────────────────────────────────────────────
// DiscoverWindowsConcurrent resolves several models' windows in parallel.
// Each goroutine captures its own discover func (no shared-global swap);
// registry writes stay serialized by the same mutex the single-call path
// uses; results reassemble in input order regardless of discovery latency.
func DiscoverWindowsConcurrent(modelIDs []string, discover func(string) int) []int {
out := make([]int, len(modelIDs))
var wg sync.WaitGroup
for i, id := range modelIDs {
wg.Add(1)
go func(i int, id string) {
defer wg.Done()
out[i] = resolveWindow(id, discover)
}(i, id)
}
wg.Wait()
return out
}
// RollingKeys — a consistent snapshot of every persisted session key
// (source + session), sorted. Reads under the rolling lock.
func RollingKeys() []string {
rollingMu.Lock()
defer rollingMu.Unlock()
keys := make([]string, 0, len(rolling))
for k := range rolling {
keys = append(keys, k)
}
sort.Strings(keys)
return keys
}
// ── 2. parallel batch estimation ────────────────────────────────────────────
// EstimateAll estimates many texts concurrently, reassembled in input
// order. Pure function per element → embarrassingly parallel.
func EstimateAll(texts []string, estimator func(string) int) []int {
if estimator == nil {
estimator = DefaultEstimator
}
out := make([]int, len(texts))
var wg sync.WaitGroup
for i, t := range texts {
wg.Add(1)
go func(i int, t string) {
defer wg.Done()
out[i] = estimator(t)
}(i, t)
}
wg.Wait()
return out
}
// ── 3. typed admission registry ─────────────────────────────────────────────
// BlockSource — a named producer of contribution blocks (the typed shape of
// a registered contributor).
type BlockSource interface {
BlockName() string
Block(task, session map[string]interface{}) *ContributionBlock
}
// BlockSourceFunc — adapter turning a plain function into a BlockSource.
type BlockSourceFunc struct {
name string
fn ContributorFunc
}
// BlockName implements BlockSource.
func (f BlockSourceFunc) BlockName() string { return f.name }
// Block implements BlockSource.
func (f BlockSourceFunc) Block(task, session map[string]interface{}) *ContributionBlock {
return f.fn(task, session)
}
// sourceRegistry — compile-time assertion: every registry entry satisfies
// BlockSource (catches signature drift at build time).
var _ = func() bool {
var registry = map[string]BlockSourceFunc{}
for _, v := range registry {
var _ BlockSource = v
}
return true
}()
+110
View File
@@ -0,0 +1,110 @@
package main
// Phase B battery — the language-advantage wins exercised, including under
// the race detector (go test -race).
import (
"fmt"
"sort"
"strings"
"sync"
"testing"
)
func TestEstimateAllMatchesSerial(t *testing.T) {
texts := make([]string, 0, 500)
for i := 0; i < 500; i++ {
texts = append(texts, fmt.Sprintf("token estimate batch %d %v", i, string(rune('a'+i%26))))
}
serial := make([]int, len(texts))
for i, s := range texts {
serial[i] = DefaultEstimator(s)
}
parallel := EstimateAll(texts, nil)
for i := range serial {
if serial[i] != parallel[i] {
t.Fatalf("EstimateAll[%d]: got %d want %d", i, parallel[i], serial[i])
}
}
}
func TestConcurrentWindowRegistryRaceFree(t *testing.T) {
ResetDiscoveredWindows()
models := make([]string, 0, 50)
for i := 0; i < 50; i++ {
models = append(models, fmt.Sprintf("model-%d", i))
}
got := DiscoverWindowsConcurrent(models, func(id string) int {
RegisterWindow(id, 32768)
return 32768
})
for i, w := range got {
if w != 32768 {
t.Fatalf("window[%d]=%d want 32768", i, w)
}
}
// concurrent readers while writers mutate — the race detector's job
var wg sync.WaitGroup
for i := 0; i < 8; i++ {
wg.Add(1)
go func(i int) {
defer wg.Done()
if i%2 == 0 {
RegisterWindow(fmt.Sprintf("race-%d", i), 8192)
} else {
_ = DefaultWindow("model-0")
}
}(i)
}
wg.Wait()
ResetDiscoveredWindows()
}
func TestRollingStoreRaceFreeAndSnapshot(t *testing.T) {
for k := range rolling {
delete(rolling, k)
}
var wg sync.WaitGroup
for i := 0; i < 20; i++ {
wg.Add(1)
go func(i int) {
defer wg.Done()
StoreRollingSummary("operator", fmt.Sprintf("s%d", i), "S", i, "tid")
_ = RollingSummaryState("operator", fmt.Sprintf("s%d", i))
}(i)
}
wg.Wait()
keys := RollingKeys()
if len(keys) != 20 {
t.Fatalf("RollingKeys: got %d want 20", len(keys))
}
want := append([]string(nil), keys...)
sort.Strings(want)
for i := range keys {
if keys[i] != want[i] {
t.Fatalf("RollingKeys not sorted: [%d]=%q want %q", i, keys[i], want[i])
}
if keys[i] != fmt.Sprintf("operator\x00s%d", i) && !strings.HasPrefix(keys[i], "operator\x00s") {
t.Fatalf("RollingKeys[%d]=%q unexpected key", i, keys[i])
}
}
for k := range rolling {
delete(rolling, k)
}
}
func TestBlockSourceRegistryCompileTime(t *testing.T) {
var src BlockSource = BlockSourceFunc{
name: "tools",
fn: func(map[string]interface{}, map[string]interface{}) *ContributionBlock {
return &ContributionBlock{Name: "tools", Priority: 9, Content: "x", Tokens: 1, Source: "t"}
},
}
blk := src.Block(nil, nil)
if blk == nil || blk.Name != "tools" {
t.Fatalf("BlockSource dispatch broken: %+v", blk)
}
if src.BlockName() != "tools" {
t.Fatalf("BlockName: got %q", src.BlockName())
}
}
File diff suppressed because it is too large. Load diff
+559
View File
@@ -0,0 +1,559 @@
package main
// Battery — the HARD parity gate replaying the frozen oracle truth
// (truth.json, generated against the LIVE Python oracle
// src/rune/context_broker.py with RUNE_COGNITION_CORE=python pinned)
// VERBATIM. Inputs are deep-frozen snapshots (the oracle mutates message
// lists); every row byte-compares the Go result against the oracle's.
import (
"encoding/json"
"errors"
"fmt"
"os"
"reflect"
"sort"
"strings"
"testing"
)
type TruthDoc struct {
FrozenTS string `json:"frozen_ts"`
Generator string `json:"generator"`
Rows []TruthRow `json:"rows"`
}
type TruthRow struct {
Name string `json:"name"`
Fn string `json:"fn"`
Input map[string]interface{} `json:"input"`
Want interface{} `json:"want"`
}
func loadTruth(t *testing.T) *TruthDoc {
t.Helper()
raw, err := os.ReadFile("truth.json")
if err != nil {
t.Fatalf("truth.json missing (frozen oracle battery): %v", err)
}
var doc TruthDoc
if err := json.Unmarshal(raw, &doc); err != nil {
t.Fatalf("parse truth.json: %v", err)
}
if len(doc.Rows) == 0 {
t.Fatal("truth.json contained no rows")
}
seen := map[string]bool{}
for _, r := range doc.Rows {
if seen[r.Name] {
t.Fatalf("duplicate frozen row %q", r.Name)
}
seen[r.Name] = true
}
return &doc
}
func msgs(raw interface{}) []Message {
list, _ := raw.([]interface{})
out := make([]Message, 0, len(list))
for _, item := range list {
m, _ := item.(map[string]interface{})
out = append(out, m)
}
return out
}
func asInt(v interface{}) int {
switch n := v.(type) {
case float64:
return int(n)
case bool:
if n {
return 1
}
return 0
case string:
var i int
fmt.Sscanf(n, "%d", &i)
return i
}
return 0
}
func compEvent(e *CompactEvent) map[string]interface{} {
blob, _ := json.Marshal(e)
var out map[string]interface{}
_ = json.Unmarshal(blob, &out)
return out
}
// runOne — replay one frozen row; returns "" on parity, else the divergence.
func runOne(row *TruthRow) string {
switch row.Fn {
case "est":
var textPtr *string
if s, ok := row.Input["text"].(string); ok {
textPtr = &s
}
got := PyDefaultEstimator(textPtr)
want := asInt(row.Want)
if got != want {
return fmt.Sprintf("est: got %d want %d", got, want)
}
case "cc_core":
env, _ := row.Input["env"].(map[string]interface{})
lookup := func(string) (string, bool) { return "", false }
if v, ok := env["RUNE_COGNITION_CORE"]; ok {
s, _ := v.(string)
lookup = func(string) (string, bool) { return s, true }
}
loaderAvail, _ := row.Input["loader_available"].(bool)
got := CcCore(lookup, loaderAvail)
if row.Want == nil && got != nil {
return "cc_core: got core want none"
}
if row.Want != nil && got == nil {
return "cc_core: got none want core"
}
case "budget":
got := PyDefaultBudget(asInt(row.Input["window"]))
want := asInt(row.Want)
if got != want {
return fmt.Sprintf("budget: got %d want %d", got, want)
}
case "membud":
got := PyDefaultMemoryBudget(asInt(row.Input["window"]))
want := asInt(row.Want)
if got != want {
return fmt.Sprintf("membud: got %d want %d", got, want)
}
case "memoff":
v, _ := row.Input["value"].(string)
got := PyMemoryDisabledValue(v)
want, _ := row.Want.(bool)
if got != want {
return fmt.Sprintf("memoff %q: got %v want %v", v, got, want)
}
case "windows":
ResetDiscoveredWindows()
opsList, _ := row.Input["ops"].([]interface{})
for _, rawOp := range opsList {
op, _ := rawOp.(map[string]interface{})
switch op["op"] {
case "register_window":
RegisterWindow(op["model_id"].(string), asInt(op["window"]))
case "reset_discovered_windows":
ResetDiscoveredWindows()
case "default_window":
mid := ""
if s, ok := op["model_id"].(string); ok {
mid = s
}
// Live discovery is IO; the Go seam stays offline (nil) so
// the miss branch freezes to the fallback — exactly what the
// oracle froze in the offline generator env.
DiscoverWindow = nil
got := DefaultWindow(mid)
wantMap, _ := row.Want.(map[string]interface{})
want := asInt(wantMap["last"])
if got != want {
return fmt.Sprintf("default_window %q: got %d want %d", mid, got, want)
}
}
}
case "through":
got := ThroughID(msgs(row.Input["messages"]), asInt(row.Input["through"]))
want, _ := row.Want.(string)
if got != want {
return fmt.Sprintf("through: got %q want %q", got, want)
}
case "rstate":
ResetDiscoveredWindows()
for k := range rolling {
delete(rolling, k)
}
opsList, _ := row.Input["ops"].([]interface{})
var got *RollingState
for _, rawOp := range opsList {
op, _ := rawOp.(map[string]interface{})
source, _ := op["source"].(string)
sid, _ := op["sid"].(string)
switch op["op"] {
case "store":
summary, _ := op["summary"].(string)
tid, _ := op["tid"].(string)
StoreRollingSummary(source, sid, summary, asInt(op["through"]), tid)
case "read":
got = RollingSummaryState(source, sid)
}
}
if row.Want == nil {
if got != nil {
return "rstate: got state want nil"
}
} else {
wantMap, _ := row.Want.(map[string]interface{})
if got == nil {
return "rstate: got nil want state"
}
if got.Summary != wantMap["summary"] ||
got.ThroughCount != asInt(wantMap["through_count"]) ||
got.ThroughID != wantMap["through_id"] {
return fmt.Sprintf("rstate: got %+v want %v", got, wantMap)
}
}
case "rstore":
m := msgs(row.Input["msgs"])
StoreRollingSummary("operator", "s2", "S2", 1, ThroughID(m, 1))
got := RollingSummaryState("operator", "s2")
wantMap, _ := row.Want.(map[string]interface{})
if got == nil || got.Summary != wantMap["summary"] ||
got.ThroughCount != asInt(wantMap["through_count"]) ||
got.ThroughID != wantMap["through_id"] {
return fmt.Sprintf("rstore: got %+v want %v", got, wantMap)
}
case "summarize":
// The oracle froze the transport-failure branch (offline env): any
// error type is acceptable — the Go seam reproduces the failure
// shape (non-empty error, empty-content ValueError contract).
_, err := SummarizeHistory(msgs(row.Input["messages"]), "offline", 256, 0,
func(string, string, map[string]interface{}) (string, error) {
return "", fmt.Errorf("HTTP Error 404: Not Found")
})
wantMap, _ := row.Want.(map[string]interface{})
if _, isErr := wantMap["error"]; isErr && err == nil {
return "summarize: got success want error"
}
if err == nil {
return "summarize: expected frozen error branch"
}
case "compact":
window := asInt(row.Input["window"])
sid, _ := row.Input["session"].(string)
in := msgs(row.Input["messages"])
ResetDiscoveredWindows()
for k := range rolling {
delete(rolling, k)
}
var archiver Archiver
archivedMsgCount := -1
if _, wantsArchive := row.Input["archive"]; wantsArchive {
archiver = func(record map[string]interface{}) error {
archivedMsgCount = len(record["messages"].([]Message))
return nil
}
}
archivePath := ""
if _, wantsArchive := row.Input["archive"]; wantsArchive {
// deterministic relative fixture path frozen in the input
archivePath, _ = row.Input["archive_path"].(string)
}
summarizerText, _ := row.Input["summarizer"].(string)
var summarizer Summarizer
switch summarizerText {
case "boom":
summarizer = func([]Message) (string, error) {
return "", fmt.Errorf("summarizer down")
}
default:
summarizer = func(ms []Message) (string, error) {
parts := make([]string, 0, len(ms))
for _, m := range ms {
c := messageContent(m)
if len(c) > 10 {
c = c[:10]
}
parts = append(parts, c)
}
return "SUMMARY<" + strings.Join(parts, "|") + ">", nil
}
}
if rawPrior, ok := row.Input["prior"]; ok {
priorMap, _ := rawPrior.(map[string]interface{})
priorSid := sid
StoreRollingSummary("operator", priorSid,
priorMap["summary"].(string), asInt(priorMap["through_count"]),
priorMap["through_id"].(string))
}
compacted, ev := CompactHistory(in, window, archivePath, archiver, summarizer, "operator", sid)
wantMap, _ := row.Want.(map[string]interface{})
wantEvent, _ := wantMap["event"].(map[string]interface{})
if msg := diffEvent(wantEvent, compEvent(ev)); msg != "" {
return msg
}
if wantCompacted, ok := wantMap["compacted"]; ok {
wantMsgs := msgs(wantCompacted)
if !reflect.DeepEqual(normalize(compacted), normalize(wantMsgs)) {
gj, _ := json.Marshal(normalize(compacted))
wj, _ := json.Marshal(normalize(wantMsgs))
return fmt.Sprintf("compacted divergence:\n got %s\n want %s", gj, wj)
}
}
if _, wantsArchive := row.Input["archive"]; wantsArchive {
wantCount := asInt(wantMap["archived_msg_count"])
if archivedMsgCount != wantCount {
return fmt.Sprintf("archived_msg_count: got %d want %d", archivedMsgCount, wantCount)
}
}
default:
return "unknown fn key " + row.Fn
}
return ""
}
// normalize renders messages compactly for deep comparison (dicts with
// arbitrary key order).
func normalize(ms []Message) []map[string]interface{} {
out := make([]map[string]interface{}, 0, len(ms))
for _, m := range ms {
cp := map[string]interface{}{}
for k, v := range m {
cp[k] = v
}
out = append(out, cp)
}
return out
}
func diffEvent(want, got map[string]interface{}) string {
keys := make([]string, 0, len(want))
for k := range want {
keys = append(keys, k)
}
sort.Strings(keys)
for _, k := range keys {
wv := fmt.Sprintf("%v", want[k])
gv := fmt.Sprintf("%v", got[k])
// Python bools render True/False; Go %v renders true/false.
wv = strings.NewReplacer("False", "false", "True", "true").Replace(wv)
if wv != gv {
return fmt.Sprintf("event.%s: got %s want %s", k, gv, wv)
}
}
return ""
}
// runBrokerOne — the ContextBroker-kernel rows (to_dict/collect/admit/render).
func runBrokerOne(row *TruthRow) string {
switch row.Fn {
case "to_dict":
name, _ := row.Want.(map[string]interface{})["name"].(string)
b := &ContributionBlock{}
switch name {
case "memory":
b = &ContributionBlock{Name: "memory", Priority: 5, Content: "hello world", Tokens: 2, Source: "recall", Evictable: true}
case "system":
b = &ContributionBlock{Name: "system", Priority: 100, Content: "sys", Tokens: 0, Source: "persona", Evictable: false}
}
want, _ := row.Want.(map[string]interface{})
got := b.ToDict()
for _, k := range []string{"name", "priority", "content", "tokens", "source", "evictable"} {
wv := fmt.Sprintf("%v", want[k])
wv = strings.NewReplacer("False", "false", "True", "true").Replace(wv)
if gv := fmt.Sprintf("%v", got[k]); gv != wv {
return fmt.Sprintf("to_dict.%s: got %v want %v", k, gv, wv)
}
}
case "collect":
cb := NewContextBroker()
var failures []string
cb.Fail = func(name string, err error) { failures = append(failures, name+": "+err.Error()) }
cb.RegisterContributor("ok", func(map[string]interface{}, map[string]interface{}) *ContributionBlock {
return &ContributionBlock{Name: "tools", Priority: 9, Content: "tooltext", Tokens: 2, Source: "t"}
}, 1)
cb.RegisterContributor("empty", func(map[string]interface{}, map[string]interface{}) *ContributionBlock {
return &ContributionBlock{Name: "x", Priority: 1, Content: "", Source: "s"}
}, 1)
cb.RegisterContributor("boom", func(map[string]interface{}, map[string]interface{}) *ContributionBlock {
panic(fmt.Errorf("cb down"))
}, 1)
cb.RegisterContributor("malformed", func(map[string]interface{}, map[string]interface{}) *ContributionBlock {
return &ContributionBlock{Name: "y", Priority: 1, Content: 12345, Source: "s"}
}, 1)
cb.RegisterContributor("list-ok", func(map[string]interface{}, map[string]interface{}) *ContributionBlock {
return &ContributionBlock{Name: "history", Priority: 3, Content: []Message{{"role": "user", "content": "hi"}}, Tokens: 1, Source: "h"}
}, 2)
cb.RegisterContributor("none", func(map[string]interface{}, map[string]interface{}) *ContributionBlock {
return nil
}, 1)
collected, degraded := cb.Collect(map[string]interface{}{}, map[string]interface{}{})
var names []string
for _, e := range collected {
names = append(names, e.Name)
}
want, _ := row.Want.(map[string]interface{})
wantNames := toStrSlice(want["names"])
wantDegraded := toStrSlice(want["degraded"])
if fmt.Sprint(names) != fmt.Sprint(wantNames) {
return fmt.Sprintf("collect.names: got %v want %v", names, wantNames)
}
if fmt.Sprint(degraded) != fmt.Sprint(wantDegraded) {
return fmt.Sprintf("collect.degraded: got %v want %v", degraded, wantDegraded)
}
// both failure kinds recorded (T1): boom + malformed
if len(failures) != 2 {
return fmt.Sprintf("collect.failures: got %d want 2 (%v)", len(failures), failures)
}
case "admit":
want, _ := row.Want.(map[string]interface{})
var entries []AdmissionEntry
var budget, memBudget int
switch row.Name {
case "admit:priority-recency-budget":
budget, memBudget = 1000, DefaultMemoryBudget(0)
entries = []AdmissionEntry{
{"system", &ContributionBlock{Name: "system", Priority: 100, Tokens: 500, Source: "src-system"}},
{"memory", &ContributionBlock{Name: "memory", Priority: 5, Tokens: 300, Source: "src-memory", Evictable: true}},
{"history", &ContributionBlock{Name: "history", Priority: 7, Tokens: 900, Source: "src-history", Evictable: true}},
{"user", &ContributionBlock{Name: "user", Priority: 100, Tokens: 100, Source: "src-user"}},
}
case "admit:memory-subbudget-drop":
budget, memBudget = 4000, 1200
entries = []AdmissionEntry{
{"m1", &ContributionBlock{Name: "m1", Priority: 9, Tokens: 700, Source: "src-m1", Evictable: true}},
{"m2", &ContributionBlock{Name: "m2", Priority: 8, Tokens: 700, Source: "src-m2", Evictable: true}},
}
case "admit:mandatory-overflow-raises":
budget, memBudget = 1000, DefaultMemoryBudget(0)
entries = []AdmissionEntry{
{"user", &ContributionBlock{Name: "user", Priority: 100, Tokens: 5000, Source: "src-user"}},
}
_, err := Admit(entries, budget, memBudget)
wantMap, _ := row.Want.(map[string]interface{})
if wantMap["error"] == "ContextOverflowError" {
if err == nil || !errors.Is(err, ErrContextOverflow) {
return "admit.overflow: no raise"
}
if !strings.Contains(err.Error(), wantMap["detail"].(string)) {
return fmt.Sprintf("admit.overflow detail: got %q", err.Error())
}
return ""
}
return "admit.overflow: frozen row unexpected shape"
}
res, err := Admit(entries, budget, memBudget)
if err != nil {
return "admit: unexpected error " + err.Error()
}
if got := fmt.Sprint(blockNames(res.Admitted)); got != fmt.Sprint(toStrSlice(want["admitted"])) {
return fmt.Sprintf("admit.admitted: got %v want %v", blockNames(res.Admitted), toStrSlice(want["admitted"]))
}
if wb, ok := want["blocks"]; ok {
wantBlocks, _ := wb.([]interface{})
if len(wantBlocks) != len(res.Blocks) {
return fmt.Sprintf("admit.blocks len: got %d want %d", len(res.Blocks), len(wantBlocks))
}
for i, wbItem := range wantBlocks {
wbMap, _ := wbItem.(map[string]interface{})
gb := res.Blocks[i]
for _, k := range []string{"name", "priority", "tokens", "source", "evictable", "admitted", "reason"} {
wv := fmt.Sprintf("%v", wbMap[k])
wv = strings.NewReplacer("False", "false", "True", "true").Replace(wv)
var gv string
switch k {
case "name":
gv = gb.Name
case "priority":
gv = fmt.Sprintf("%d", gb.Priority)
case "tokens":
gv = fmt.Sprintf("%d", gb.Tokens)
case "source":
gv = gb.Source
case "evictable":
gv = fmt.Sprintf("%v", gb.Evictable)
case "admitted":
gv = fmt.Sprintf("%v", gb.Admitted)
case "reason":
gv = gb.Reason
}
if gv != wv {
return fmt.Sprintf("admit.blocks[%d].%s: got %q want %q", i, k, gv, wv)
}
}
}
}
if want["admitted_total"] != nil && res.AdmittedTotal != asInt(want["admitted_total"]) {
return fmt.Sprintf("admit.admitted_total: got %d want %v", res.AdmittedTotal, want["admitted_total"])
}
if wd, ok := want["dropped"]; ok {
var gotDropped []string
for _, d := range res.Dropped {
gotDropped = append(gotDropped, d.Name)
}
if fmt.Sprint(gotDropped) != fmt.Sprint(toStrSlice(wd)) {
return fmt.Sprintf("admit.dropped: got %v want %v", gotDropped, toStrSlice(wd))
}
}
if want["memory_used"] != nil && res.MemoryUsed != asInt(want["memory_used"]) {
return fmt.Sprintf("admit.memory_used: got %d want %v", res.MemoryUsed, want["memory_used"])
}
if want["memory_budget"] != nil && res.MemoryBudget != asInt(want["memory_budget"]) {
return fmt.Sprintf("admit.memory_budget: got %d want %v", res.MemoryBudget, want["memory_budget"])
}
case "render":
want, _ := row.Want.(map[string]interface{})
admitted := []*ContributionBlock{
{Name: "persona", Priority: 100, Content: "ccccccc", Tokens: 7, Source: "src-persona"},
{Name: "tools", Priority: 90, Content: "ccccc", Tokens: 5, Source: "src-tools"},
{Name: "history", Priority: 70, Content: "cccc", Tokens: 4, Source: "src-history"},
{Name: "user", Priority: 100, Content: "cc", Tokens: 2, Source: "src-user"},
}
capsule := Render(admitted)
if capsule.TokenCount != asInt(want["token_count"]) {
return fmt.Sprintf("render.token_count: got %d want %v", capsule.TokenCount, want["token_count"])
}
if capsule.SystemPreamble != want["system_preamble"] {
return fmt.Sprintf("render.system_preamble: got %q want %q", capsule.SystemPreamble, want["system_preamble"])
}
if capsule.UserContent != want["user_content"] {
return fmt.Sprintf("render.user_content: got %q want %q", capsule.UserContent, want["user_content"])
}
wantMsgs := msgs(want["messages"])
if !reflect.DeepEqual(normalize(capsule.Messages), normalize(wantMsgs)) {
gj, _ := json.Marshal(capsule.Messages)
wj, _ := json.Marshal(wantMsgs)
return fmt.Sprintf("render.messages:\n got %s\n want %s", gj, wj)
}
if fmt.Sprint(blockNames(capsule.Blocks)) != fmt.Sprint(toStrSlice(want["blocks"])) {
return fmt.Sprintf("render.blocks: got %v want %v", blockNames(capsule.Blocks), toStrSlice(want["blocks"]))
}
default:
return "unknown broker fn key " + row.Fn
}
return ""
}
func toStrSlice(v interface{}) []string {
list, ok := v.([]interface{})
if !ok {
return nil
}
out := make([]string, 0, len(list))
for _, item := range list {
s, _ := item.(string)
out = append(out, s)
}
return out
}
func TestTruthBattery(t *testing.T) {
doc := loadTruth(t)
fails := 0
for i := range doc.Rows {
var msg string
switch doc.Rows[i].Fn {
case "to_dict", "collect", "admit", "render":
msg = runBrokerOne(&doc.Rows[i])
default:
msg = runOne(&doc.Rows[i])
}
if msg != "" {
fails++
t.Errorf("DIVERGED %s: %s", doc.Rows[i].Name, msg)
}
}
if fails > 0 {
t.Fatalf("battery: %d/%d divergences vs the frozen Python oracle", fails, len(doc.Rows))
}
t.Logf("PARITY OK: %d frozen rows replayed", len(doc.Rows))
}
+1 -1
View File
@@ -6,7 +6,7 @@ This is the main entry point for the Rune program.
import json
from pathlib import Path
__version__ = "0.269.0"
__version__ = "0.271.0"
# One build identity (handoff 2026-09-10 §4): the semantic version above is
# the repo-level truth (auto-bumped at merge); the DEPLOYED revision (exact
+1 -1
View File
@@ -233,7 +233,7 @@ def main() -> int:
)
check(
"Rune minor version advertises provisioning and restore verification",
'__version__ = "0.269.0"' in rune_version,
'__version__ = "0.271.0"' in rune_version,
failures,
)
# #169 policy update: auto-bump on merge is sanctioned (Nick directive:
+2 -2
View File
@@ -8,7 +8,7 @@ mode system, subject header, shared memory and identity.
This is not an interface. This is how I see, speak, and interact with the world.
Version: v0.269.0
Version: v0.271.0
Features: 41 (24 original + 17 new native commands)
Usage:
@@ -2037,7 +2037,7 @@ class RuneApp(App):
# Show version info
self.chat_channel.add_message(
"system",
f"[dim]Rune v0.269.0 | Textual TUI | {len(loaded)} native modules[/dim]",
f"[dim]Rune v0.271.0 | Textual TUI | {len(loaded)} native modules[/dim]",
)
# Auto-show night mode suggestion if evening/night hours
+132 -8
View File
@@ -1,11 +1,32 @@
#!/usr/bin/env python3
"""ctypes loader for the Rust structural_store core (facade-1 burn-down).
"""ctypes loader for the Rust structural_store core (facade-1 burn-down;
dichotomy-wave gap-closing tranche mirrors broker_loader.py's contract).
FFI contract (doc-envelope law):
FFI contract (doc-envelope law — every call returns {"ok", "value"|"err"}):
* ss_maturity_rank({state}) -> int (spine ladder)
* ss_promote({doc, id, state, evidence_ref, now}) -> updated doc
* ss_supersede({doc, new_id, old_id, evidence_ref, now}) -> updated doc
* ss_resolve_status({entity}) -> string
* ss_sorted({value}) -> normalized value
* ss_new_doc({now}) -> doc
* ss_redact_secrets({value}) -> redacted value
* ss_save({doc, path}) -> {"doc", "path"} (writes the file)
* ss_canonical_digest({doc}) -> hex digest
* ss_snapshot({doc, snapshots_dir, label, tag, now}) -> {"doc", "path"}
* ss_alias({doc, canonical_id, old_id, now}) -> updated doc
* ss_spine_uri({entity_id}) -> string
* ss_resolve_uri({uri}) -> string
* ss_doc_authorities({doc}) -> updated doc
* ss_authority_health({doc}) -> per-projector map
* ss_bind_task({doc, task_id, entity_ids, evidence_ref, now}) -> updated doc
* ss_task_entities({doc, task_id}) -> [entity id]
* ss_impact_frontier({doc, seed_ids, max_level, change_kind}) -> frontier
* ss_record_observed_impact({doc, task_id, evidence_items, now})
-> {"doc", "result"}
* ss_predicted_vs_observed({predicted, observed_doc, seed_ids}) -> delta
* ss_structural_events({doc}) -> [event]
* ss_diff({old, new}) -> delta
* ss_dedupe_observations({doc}) -> updated doc
* ss_free_string(ptr)
FFI segfault law: explicit argtypes + restype (c_void_p for char*).
@@ -26,6 +47,16 @@ _LIB_PATH = Path(
)
)
_SYMBOLS = (
"ss_maturity_rank", "ss_promote", "ss_supersede", "ss_resolve_status",
"ss_sorted", "ss_new_doc", "ss_redact_secrets", "ss_save",
"ss_canonical_digest", "ss_snapshot", "ss_alias", "ss_spine_uri",
"ss_resolve_uri", "ss_doc_authorities", "ss_authority_health",
"ss_bind_task", "ss_task_entities", "ss_impact_frontier",
"ss_record_observed_impact", "ss_predicted_vs_observed",
"ss_structural_events", "ss_diff", "ss_dedupe_observations",
)
_lib = None
@@ -42,10 +73,9 @@ def _load() -> ctypes.CDLL:
lib = ctypes.CDLL(str(_LIB_PATH))
lib.ss_free_string.argtypes = [ctypes.c_void_p]
lib.ss_free_string.restype = None
for sym in ("ss_maturity_rank", "ss_promote", "ss_supersede",
"ss_resolve_status"):
for sym in _SYMBOLS:
fn = getattr(lib, sym)
fn.argtypes = [ctypes.c_char_p]
fn.argtypes = [ctypes.c_void_p]
fn.restype = ctypes.c_void_p
_lib = lib
return lib
@@ -53,16 +83,20 @@ def _load() -> ctypes.CDLL:
def _call(sym: str, payload: dict):
lib = _load()
ptr = getattr(lib, sym)(json.dumps(payload).encode("utf-8"))
data = json.dumps(payload).encode()
buf = ctypes.create_string_buffer(data)
ptr = getattr(lib, sym)(ctypes.cast(buf, ctypes.c_void_p))
if not ptr:
raise RuntimeError(f"{sym} returned null")
try:
text = ctypes.cast(ptr, ctypes.c_char_p).value.decode("utf-8")
text = ctypes.string_at(ptr).decode("utf-8")
finally:
lib.ss_free_string(ptr)
env = json.loads(text)
if not env.get("ok"):
raise RuntimeError(f"{sym} core error: {env.get('err')}")
# the kernel error travels VERBATIM: the facade translates it into
# the oracle's ValueError contract (message text must match 1:1)
raise RuntimeError(env.get("err") or f"{sym} failed")
return env["value"]
@@ -84,3 +118,93 @@ def supersede(doc: dict, new_id: str, old_id: str, evidence_ref: str,
def resolve_status(entity: dict) -> str:
return _call("ss_resolve_status", {"entity": entity})
def sorted_value(value):
return _call("ss_sorted", {"value": value})
def new_doc(now: str) -> dict:
return _call("ss_new_doc", {"now": now})
def redact_secrets(value):
return _call("ss_redact_secrets", {"value": value})
def save(doc: dict, path: str) -> dict:
return _call("ss_save", {"doc": doc, "path": path})
def canonical_digest(doc: dict) -> str:
return _call("ss_canonical_digest", {"doc": doc})
def snapshot(doc: dict, snapshots_dir: str, label: str, tag: str, now: str) -> dict:
return _call("ss_snapshot", {"doc": doc, "snapshots_dir": snapshots_dir,
"label": label, "tag": tag, "now": now})
def alias(doc: dict, canonical_id: str, old_id: str, now: str) -> dict:
return _call("ss_alias", {"doc": doc, "canonical_id": canonical_id,
"old_id": old_id, "now": now})
def spine_uri(entity_id: str) -> str:
return _call("ss_spine_uri", {"entity_id": entity_id})
def resolve_uri(uri: str) -> str:
return _call("ss_resolve_uri", {"uri": uri})
def doc_authorities(doc: dict) -> dict:
return _call("ss_doc_authorities", {"doc": doc})
def authority_health(doc: dict) -> dict:
return _call("ss_authority_health", {"doc": doc})
def bind_task(doc: dict, task_id: str, entity_ids: list, evidence_ref: str,
now: str) -> dict:
return _call("ss_bind_task", {"doc": doc, "task_id": task_id,
"entity_ids": entity_ids,
"evidence_ref": evidence_ref, "now": now})
def task_entities(doc: dict, task_id: str) -> list:
return _call("ss_task_entities", {"doc": doc, "task_id": task_id})
def impact_frontier(doc: dict, seed_ids: list, max_level: int = 2,
change_kind=None):
return _call("ss_impact_frontier", {"doc": doc, "seed_ids": seed_ids,
"max_level": max_level,
"change_kind": change_kind})
def record_observed_impact(doc: dict, task_id: str, evidence_items: list,
now: str) -> dict:
return _call("ss_record_observed_impact", {"doc": doc, "task_id": task_id,
"evidence_items": evidence_items,
"now": now})
def predicted_vs_observed(predicted: dict, observed_doc: dict,
seed_ids: list) -> dict:
return _call("ss_predicted_vs_observed", {"predicted": predicted,
"observed_doc": observed_doc,
"seed_ids": seed_ids})
def structural_events(doc: dict) -> list:
return _call("ss_structural_events", {"doc": doc})
def diff(old: dict, new: dict) -> dict:
return _call("ss_diff", {"old": old, "new": new})
def dedupe_observations(doc: dict) -> dict:
return _call("ss_dedupe_observations", {"doc": doc})
+84
View File
@@ -85,6 +85,9 @@ def _sorted(obj):
def new_doc() -> dict:
core = _ss_core()
if core is not None:
return core.new_doc(now())
return {
"schema_version": SCHEMA_VERSION,
"generated_at": now(),
@@ -143,6 +146,18 @@ def _redact_secrets(obj):
def save(doc: dict, path: Path | str) -> Path:
core = _ss_core()
if core is not None:
try:
out = core.save(doc, str(path))
except RuntimeError as exc:
# Kernel errors carry the oracle's ValueError semantics through
# the envelope — translate back to the documented contract.
raise ValueError(str(exc)) from exc
if isinstance(out.get("doc"), dict):
doc.clear()
doc.update(out["doc"])
return Path(out["path"])
incoming = doc.get("schema_version", SCHEMA_VERSION)
if incoming != SCHEMA_VERSION:
raise ValueError(f"schema_version {incoming} != {SCHEMA_VERSION}")
@@ -160,6 +175,10 @@ def canonical_digest(doc: dict) -> str:
identical semantic content always yields an identical digest."""
import hashlib
core = _ss_core()
if core is not None:
return core.canonical_digest(doc)
payload = json.dumps(_sorted(_redact_secrets(doc)), sort_keys=True)
return hashlib.sha256(payload.encode("utf-8")).hexdigest()
@@ -168,6 +187,13 @@ def snapshot(doc: dict, snapshots_dir: Path, label: str = "", tag: str = "") ->
"""Persist an immutable snapshot. `tag` is a caller-supplied identity
fragment (Spine uses the deployed SHA; a Project Scaffold may use its
repo HEAD or 'noident')."""
core = _ss_core()
if core is not None:
out = core.snapshot(doc, str(snapshots_dir), label, tag, now())
if isinstance(out.get("doc"), dict):
doc.clear()
doc.update(out["doc"])
return Path(out["path"])
snapshots_dir = Path(snapshots_dir)
snapshots_dir.mkdir(parents=True, exist_ok=True)
name = f"snap-{time.strftime('%Y%m%dT%H%M%SZ', time.gmtime())}-{(tag or 'noident')[:8]}"
@@ -312,6 +338,16 @@ def promote(doc: dict, id: str, state: str, evidence_ref: str) -> dict | None:
def alias(doc: dict, canonical_id: str, old_id: str) -> dict:
"""RENAME: same conceptual entity, identifier changed. Old references
stay resolvable (entity() follows alias entries)."""
core = _ss_core()
if core is not None:
try:
out = core.alias(doc, canonical_id, old_id, now())
except RuntimeError as exc:
raise ValueError(str(exc)) from exc
if isinstance(out, dict):
doc.clear()
doc.update(out)
return entity(doc, canonical_id)
e = entity(doc, canonical_id)
if e is None:
raise ValueError(f"alias target not found: {canonical_id}")
@@ -344,11 +380,20 @@ def supersede(doc: dict, new_id: str, old_id: str, evidence_ref: str) -> dict:
def spine_uri(entity_id: str) -> str:
"""Stable cross-graph identity: spine://<kind>/<name>. A future Memory
record cites this URI without duplicating structural authority."""
core = _ss_core()
if core is not None:
return core.spine_uri(entity_id)
kind, _, name = entity_id.partition(":")
return f"spine://{kind}/{name}"
def resolve_uri(uri: str) -> str:
core = _ss_core()
if core is not None:
try:
return core.resolve_uri(uri)
except RuntimeError as exc:
raise ValueError(str(exc)) from exc
scheme, _, rest = uri.partition("://")
if scheme != "spine":
raise ValueError(f"not a spine URI: {uri}")
@@ -460,6 +505,9 @@ def resolve_status(e: dict) -> str:
def authority_health(doc: dict) -> dict:
"""Provenance/freshness per projector — provenance only, not monitoring."""
core = _ss_core()
if core is not None:
return core.authority_health(doc)
return {
name: {
"generation": a.get("generation", 0),
@@ -478,6 +526,13 @@ def authority_health(doc: dict) -> dict:
def bind_task(doc: dict, task_id: str, entity_ids: list[str], evidence_ref: str = "") -> dict:
"""TASK/ISSUE/INTENT BINDING: task → affects → entities. Deterministic."""
core = _ss_core()
if core is not None:
out = core.bind_task(doc, task_id, list(entity_ids), evidence_ref, now())
if isinstance(out, dict):
doc.clear()
doc.update(out)
return entity(doc, f"task:{task_id}") or {"id": f"task:{task_id}"}
for eid in entity_ids:
add_relationship(
doc,
@@ -490,6 +545,9 @@ def bind_task(doc: dict, task_id: str, entity_ids: list[str], evidence_ref: str
def task_entities(doc: dict, task_id: str) -> list[str]:
core = _ss_core()
if core is not None:
return core.task_entities(doc, task_id)
return sorted(r["to"] for r in relationships(doc, from_id=f"task:{task_id}", type="affects"))
@@ -502,6 +560,9 @@ def impact_frontier(doc: dict, seed_ids: list[str], max_level: int = 2, change_k
contract_violation (escalates siblings to obligations). None infers from
seed kind. Unknown relationship types are obligation-bearing (fail-loud);
NON_OBLIGATION types and CANDIDATE/STALE facts are skipped."""
core = _ss_core()
if core is not None:
return core.impact_frontier(doc, list(seed_ids), max_level, change_kind)
seeds = list(dict.fromkeys(seed_ids))
level0 = sorted(set(seeds))
level1: set[str] = set()
@@ -578,6 +639,13 @@ def record_observed_impact(doc: dict, task_id: str, evidence_items: list[dict])
"""INDEPENDENT OBSERVED IMPACT: derive what was ACTUALLY affected from
external evidence (tests, canaries, runtime, deploy) — never by re-running
the frontier being evaluated. Nothing auto-promotes into structure."""
core = _ss_core()
if core is not None:
out = core.record_observed_impact(doc, task_id, list(evidence_items), now())
if isinstance(out.get("doc"), dict):
doc.clear()
doc.update(out["doc"])
return out["result"]
implicated: set[str] = set()
for item in evidence_items:
eid = item.get("entity", "")
@@ -599,6 +667,9 @@ def record_observed_impact(doc: dict, task_id: str, evidence_items: list[dict])
def predicted_vs_observed(predicted: dict, observed_doc: dict, seed_ids: list[str]) -> dict:
core = _ss_core()
if core is not None:
return core.predicted_vs_observed(predicted, observed_doc, list(seed_ids))
observed = impact_frontier(observed_doc, seed_ids)
pred_set = {
e
@@ -629,6 +700,9 @@ def predicted_vs_observed(predicted: dict, observed_doc: dict, seed_ids: list[st
def structural_events(doc: dict) -> list[dict]:
"""GENERIC EVENT/QUERY SEAM: dumb typed structural facts for future
subscribers. No salience, no policy."""
core = _ss_core()
if core is not None:
return core.structural_events(doc)
events = []
for e in doc["entities"]:
events.append(
@@ -657,6 +731,9 @@ def structural_events(doc: dict) -> list[dict]:
def diff(old: dict, new: dict) -> dict:
"""Deterministic structural delta. No interpretation."""
core = _ss_core()
if core is not None:
return core.diff(old, new)
oe = {e["id"]: e for e in old.get("entities", [])}
ne = {e["id"]: e for e in new.get("entities", [])}
ors = {(r["type"], r["from"], r["to"]) for r in old.get("relationships", [])}
@@ -680,6 +757,13 @@ def diff(old: dict, new: dict) -> dict:
def dedupe_observations(doc: dict) -> None:
"""Exact-duplicate observation suppression (carry-forward + re-derivation
both record; distinct repeats are history and stay)."""
core = _ss_core()
if core is not None:
out = core.dedupe_observations(doc)
if isinstance(out, dict):
doc.clear()
doc.update(out)
return
seen: set[str] = set()
unique = []
for o in doc.get("observations", []):
+28
View File
@@ -2,6 +2,32 @@
# It is not intended for manual editing.
version = 4
[[package]]
name = "equivalent"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
[[package]]
name = "evidence_provenance"
version = "0.1.0"
[[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"
@@ -68,6 +94,7 @@ version = "1.0.151"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c841b55ecdae098c80dcae9cf767f6f8a0c2cdb3416bbef72181df4d0fe73f14"
dependencies = [
"indexmap",
"itoa",
"memchr",
"serde",
@@ -79,6 +106,7 @@ dependencies = [
name = "structural_store"
version = "0.1.0"
dependencies = [
"evidence_provenance",
"serde",
"serde_json",
]
+4 -1
View File
@@ -8,4 +8,7 @@ crate-type = ["rlib", "cdylib", "staticlib"]
[dependencies]
serde = { version = "1", features = ["derive"] }
serde_json = "1"
# preserve_order: Python dicts round-trip in insertion order — str() of a
# detail dict and doc-echo order follow the oracle.
serde_json = { version = "1", features = ["preserve_order"] }
evidence_provenance = { path = "../evidence_provenance" }
@@ -0,0 +1,352 @@
//! frozen_battery — replay the FROZEN oracle truth VERBATIM
//! (frozen_ss_truth.json, generated by scripts/gen_structural_store_truth.py
//! against the LIVE Python oracle with RUNE_STRUCTURAL_CORE=python pinned,
//! clock pinned via structural_store.now, inputs deep-copied per case).
//!
//! The canary never imports the Python oracle — parity stays provable after
//! strangler exits.
use crate::*;
use serde_json::Value;
use std::cell::RefCell;
const TRUTH: &str = include_str!("frozen_ss_truth.json");
const NOW: &str = "2026-10-13T01:02:03Z";
fn truth() -> Value {
serde_json::from_str(TRUTH).expect("frozen truth parses")
}
fn cases(group: &str) -> Vec<Value> {
truth()["groups"][group]
.as_array()
.cloned()
.unwrap_or_default()
}
fn nm(c: &Value) -> &str {
c["name"].as_str().unwrap_or("")
}
/// Recording I/O seam — save/snapshot write into memory, never the disk.
struct RecIo {
writes: RefCell<Vec<(String, String)>>,
}
impl RecIo {
fn new() -> Self {
RecIo { writes: RefCell::new(vec![]) }
}
fn content(&self, path: &str) -> String {
self.writes
.borrow()
.iter()
.find(|(p, _)| p == path)
.map(|(_, c)| c.clone())
.unwrap_or_default()
}
}
impl SsIo for RecIo {
fn mkdir_parents(&self, _dir: &str) -> Result<(), String> {
Ok(())
}
fn write_text(&self, path: &str, contents: &str) -> Result<(), String> {
self.writes.borrow_mut().push((path.to_string(), contents.to_string()));
Ok(())
}
}
fn expect_err(c: &Value, got: Result<impl std::fmt::Debug, String>) {
match c["expect"].get("err").and_then(|e| e.as_str()) {
Some(want) => {
let e = got.expect_err(&format!("case {} should raise", nm(c)));
assert_eq!(e, want, "case {} error message", nm(c));
}
None => {
got.unwrap_or_else(|e| panic!("case {} raised: {}", nm(c), e));
}
}
}
fn de<'a>(c: &'a Value, keys: &[&str]) -> &'a Value {
let mut cur = c;
for k in keys {
cur = &cur[k];
}
cur
}
// ── pure normalizers ────────────────────────────────────────────────────────
#[test]
fn replay_sorted() {
for c in &cases("sorted") {
let got = _sorted(de(c, &["args", "value"]));
assert_eq!(got, de(c, &["expect", "ok"]).clone(), "case {}", nm(c));
}
}
#[test]
fn replay_new_doc() {
for c in &cases("new_doc") {
let got = new_doc(NOW);
assert_eq!(got, de(c, &["expect", "ok"]).clone(), "case {}", nm(c));
}
}
#[test]
fn replay_redact() {
for c in &cases("redact") {
let got = _redact_secrets(de(c, &["args", "value"]));
assert_eq!(got, de(c, &["expect", "ok"]).clone(), "case {}", nm(c));
}
}
#[test]
fn replay_save() {
for c in &cases("save") {
let mut doc = de(c, &["args", "doc"]).clone();
let path = format!("out/{}/store.json", nm(c));
let io = RecIo::new();
let got = save(&mut doc, &path, &io);
expect_err(c, got);
if c["expect"].get("ok").is_some() {
assert_eq!(io.content(&path), de(c, &["expect", "ok", "content"]).as_str().unwrap(), "case {} content", nm(c));
assert_eq!(doc, de(c, &["expect", "ok", "doc"]).clone(), "case {} doc", nm(c));
}
}
}
#[test]
fn replay_digest() {
for c in &cases("digest") {
let got = canonical_digest(de(c, &["args", "doc"]));
let want = de(c, &["expect", "ok"]);
if want.is_array() {
// multi-assert cases: digests of related docs must agree pairwise
let digests: Vec<Value> = if c["args"].get("doc_reordered").is_some() {
vec![
json!(canonical_digest(de(c, &["args", "doc"]))),
json!(canonical_digest(de(c, &["args", "doc_reordered"]))),
]
} else if c["args"].get("doc").is_some() && c["name"] == "secret_never_changes_digest" {
let d = de(c, &["args", "doc"]);
let d2 = { let mut x = d.clone(); x["token"] = json!("other"); x };
vec![json!(canonical_digest(d)), json!(canonical_digest(&d2))]
} else {
vec![json!(got.clone())]
};
assert_eq!(json!(digests), want.clone(), "case {}", nm(c));
} else {
assert_eq!(json!(got), want.clone(), "case {}", nm(c));
}
}
}
#[test]
fn replay_snapshot() {
for c in &cases("snapshot") {
let mut doc = de(c, &["args", "doc"]).clone();
let dir = de(c, &["args", "dir"]).as_str().unwrap();
let label = de(c, &["args", "label"]).as_str().unwrap();
let tag = de(c, &["args", "tag"]).as_str().unwrap();
let io = RecIo::new();
let got = snapshot(&mut doc, dir, label, tag, NOW, &io).expect("snapshot ok");
assert_eq!(json!(got), de(c, &["expect", "ok", "path"]).clone(), "case {} path", nm(c));
assert_eq!(io.content(&got), de(c, &["expect", "ok", "content"]).as_str().unwrap(), "case {} content", nm(c));
assert_eq!(doc, de(c, &["expect", "ok", "doc"]).clone(), "case {} doc", nm(c));
}
}
#[test]
fn replay_alias() {
for c in &cases("alias") {
let mut doc = de(c, &["args", "doc"]).clone();
let cid = de(c, &["args", "cid"]).as_str().unwrap();
let old = de(c, &["args", "old"]).as_str().unwrap();
let got = alias(&mut doc, cid, old, NOW);
match c["expect"].get("err") {
Some(_) => expect_err(c, got),
None => assert_eq!(got.expect("alias ok"), de(c, &["expect", "ok"]).clone(), "case {}", nm(c)),
}
}
}
#[test]
fn replay_spine_uri() {
for c in &cases("spine_uri") {
let got = spine_uri(de(c, &["args", "entity_id"]).as_str().unwrap());
assert_eq!(json!(got), de(c, &["expect", "ok"]).clone(), "case {}", nm(c));
}
}
#[test]
fn replay_resolve_uri() {
for c in &cases("resolve_uri") {
let got = resolve_uri(de(c, &["args", "uri"]).as_str().unwrap());
expect_err(c, got.clone());
if c["expect"].get("ok").is_some() {
assert_eq!(json!(got.unwrap()), de(c, &["expect", "ok"]).clone(), "case {}", nm(c));
}
}
}
#[test]
fn replay_doc_authorities() {
for c in &cases("doc_authorities") {
let mut doc = de(c, &["args", "doc"]).clone();
let ret = _doc_authorities(&mut doc).clone();
assert_eq!(json!(ret), de(c, &["expect", "ok", "ret"]).clone(), "case {} ret", nm(c));
assert_eq!(doc, de(c, &["expect", "ok", "doc"]).clone(), "case {} doc", nm(c));
}
}
#[test]
fn replay_authority_health() {
for c in &cases("authority_health") {
let got = authority_health(de(c, &["args", "doc"]));
assert_eq!(got, de(c, &["expect", "ok"]).clone(), "case {}", nm(c));
}
}
#[test]
fn replay_bind_task() {
for c in &cases("bind_task") {
let mut doc = de(c, &["args", "doc"]).clone();
let task_id = de(c, &["args", "task_id"]).as_str().unwrap();
let ids: Vec<String> = de(c, &["args", "entity_ids"])
.as_array().unwrap()
.iter().map(|x| x.as_str().unwrap().to_string()).collect();
let ev = de(c, &["args", "evidence_ref"]).as_str().unwrap();
bind_task(&mut doc, task_id, &ids, ev, NOW);
let ret = entity_ref(&doc, &format!("task:{}", task_id))
.cloned()
.unwrap_or(json!({"id": format!("task:{}", task_id)}));
assert_eq!(json!(ret), de(c, &["expect", "ok", "ret"]).clone(), "case {} ret", nm(c));
assert_eq!(doc, de(c, &["expect", "ok", "doc"]).clone(), "case {} doc", nm(c));
}
}
#[test]
fn replay_task_entities() {
for c in &cases("task_entities") {
let got = task_entities(de(c, &["args", "doc"]), de(c, &["args", "task_id"]).as_str().unwrap());
assert_eq!(json!(got), de(c, &["expect", "ok"]).clone(), "case {}", nm(c));
}
}
#[test]
fn replay_impact_frontier() {
for c in &cases("impact_frontier") {
let got = impact_frontier(
de(c, &["args", "doc"]),
&de(c, &["args", "seeds"]).as_array().unwrap()
.iter().map(|x| x.as_str().unwrap().to_string()).collect::<Vec<_>>(),
de(c, &["args", "max_level"]).as_i64().unwrap_or(2),
de(c, &["args", "change_kind"]).as_str(),
);
assert_eq!(got, de(c, &["expect", "ok"]).clone(), "case {}", nm(c));
}
}
#[test]
fn replay_observed_impact() {
for c in &cases("observed_impact") {
let mut doc = de(c, &["args", "doc"]).clone();
let items: Vec<Value> = de(c, &["args", "items"]).as_array().unwrap().clone();
let got = record_observed_impact(&mut doc, "T1", &items, NOW);
assert_eq!(json!(got), de(c, &["expect", "ok", "result"]).clone(), "case {} result", nm(c));
assert_eq!(doc, de(c, &["expect", "ok", "doc"]).clone(), "case {} doc", nm(c));
}
}
#[test]
fn replay_predicted_vs_observed() {
for c in &cases("predicted_vs_observed") {
let seeds: Vec<String> = de(c, &["args", "seeds"]).as_array().unwrap()
.iter().map(|x| x.as_str().unwrap().to_string()).collect();
let got = predicted_vs_observed(de(c, &["args", "predicted"]), de(c, &["args", "observed_doc"]), &seeds);
assert_eq!(got, de(c, &["expect", "ok"]).clone(), "case {}", nm(c));
}
}
#[test]
fn replay_structural_events() {
for c in &cases("events") {
let got = structural_events(de(c, &["args", "doc"]));
assert_eq!(got, de(c, &["expect", "ok"]).clone(), "case {}", nm(c));
}
}
#[test]
fn replay_diff() {
for c in &cases("diff") {
let got = diff(de(c, &["args", "old"]), de(c, &["args", "new"]));
assert_eq!(got, de(c, &["expect", "ok"]).clone(), "case {}", nm(c));
}
}
#[test]
fn replay_dedupe() {
for c in &cases("dedupe") {
let mut doc = de(c, &["args", "doc"]).clone();
dedupe_observations(&mut doc);
assert_eq!(doc, de(c, &["expect", "ok", "doc"]).clone(), "case {} doc", nm(c));
assert!(de(c, &["expect", "ok", "ret"]).is_null(), "case {} ret", nm(c));
}
}
// ── fixed-kernel regressions ────────────────────────────────────────────────
#[test]
fn replay_promote_fix() {
for c in &cases("promote_fix") {
let mut doc = de(c, &["args", "doc"]).clone();
let id = de(c, &["args", "id"]).as_str().unwrap();
let state = de(c, &["args", "state"]).as_str().unwrap();
let ev = de(c, &["args", "ev"]).as_str().unwrap();
let got = promote(&mut doc, id, state, ev, NOW);
let ret = de(c, &["expect", "ok", "ret"]);
let want_doc = de(c, &["expect", "ok", "doc"]);
if ret.is_null() {
assert_eq!(got, Ok(None), "case {} ret", nm(c));
} else {
// oracle promote returns the ENTITY; the ret maturity is the
// observed outcome (doc compare below proves the full shape)
let got_str = got.as_ref().ok().and_then(|o| o.as_deref());
assert_eq!(got_str, Some(ret["maturity"].as_str().unwrap()), "case {} ret", nm(c));
}
assert_eq!(doc, want_doc.clone(), "case {} doc", nm(c));
}
}
#[test]
fn replay_resolve_status_fix() {
for c in &cases("resolve_status_fix") {
let got = resolve_status(de(c, &["args", "entity"]));
assert_eq!(json!(got), de(c, &["expect", "ok"]).clone(), "case {}", nm(c));
}
}
#[test]
fn replay_supersede_fix() {
for c in &cases("supersede_fix") {
let mut doc = de(c, &["args", "doc"]).clone();
let nid = de(c, &["args", "new"]).as_str().unwrap();
let oid = de(c, &["args", "old"]).as_str().unwrap();
let ev = de(c, &["args", "ev"]).as_str().unwrap();
supersede(&mut doc, nid, oid, ev, NOW).expect("supersede ok");
assert_eq!(doc, de(c, &["expect", "ok", "doc"]).clone(), "case {}", nm(c));
}
}
// ── the core probe mirror (RUNE_STRUCTURAL_CORE=python pin) ─────────────────
#[test]
fn ss_core_pin_semantics() {
assert!(_ss_core(Some("python")).is_none(), "python pin disables the core");
assert!(_ss_core(None).is_some(), "unset pin activates the core");
assert!(_ss_core(Some("auto")).is_some(), "auto activates the core");
assert_eq!(_ss_core(Some("auto")).unwrap().id(), "rust");
}
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+94 -72
View File
@@ -2,7 +2,9 @@
//! PARITY port of the core data structures from src/rune/structural_store.py.
use serde::{Deserialize, Serialize};
pub const SCHEMA_VERSION: i32 = 3;
// Oracle law: structural_store.SCHEMA_VERSION == 1 (shared with spine stores).
// (Previous port carried a stale 3 — corrected against the live oracle.)
pub const SCHEMA_VERSION: i32 = 1;
pub const MATURITY_ORDER: &[&str] = &["UNRUN", "DECLARED", "PRESENT", "VERIFIED"];
#[derive(Debug, Clone, Serialize, Deserialize)]
@@ -75,7 +77,7 @@ impl Default for StoreDoc {
use std::ffi::{CStr, CString};
use std::os::raw::c_char;
fn read_arg(ptr: *const c_char) -> Result<String, String> {
pub(crate) fn read_arg(ptr: *const c_char) -> Result<String, String> {
if ptr.is_null() {
return Err("null argument pointer".to_string());
}
@@ -85,7 +87,7 @@ fn read_arg(ptr: *const c_char) -> Result<String, String> {
.map_err(|e| format!("argument is not valid UTF-8: {}", e))
}
fn envelope(value: Result<serde_json::Value, String>) -> *mut c_char {
pub(crate) fn envelope(value: Result<serde_json::Value, String>) -> *mut c_char {
let body = match value {
Ok(v) => serde_json::json!({"ok": true, "value": v}),
Err(e) => serde_json::json!({"ok": false, "err": e}),
@@ -95,11 +97,11 @@ fn envelope(value: Result<serde_json::Value, String>) -> *mut c_char {
.unwrap_or(std::ptr::null_mut())
}
fn parse_arg(text: &str) -> Result<serde_json::Value, String> {
pub(crate) fn parse_arg(text: &str) -> Result<serde_json::Value, String> {
serde_json::from_str(text).map_err(|e| format!("argument JSON parse failed: {}", e))
}
fn get_str<'a>(v: &'a serde_json::Value, key: &str) -> Option<&'a str> {
pub(crate) fn get_str<'a>(v: &'a serde_json::Value, key: &str) -> Option<&'a str> {
v.get(key).and_then(|x| x.as_str())
}
@@ -223,7 +225,9 @@ pub fn is_known_maturity(state: &str) -> bool {
}
/// Evidence-gated promotion. Demotion is never silent: recorded, not applied.
/// Returns Ok(Some(updated maturity)) / Ok(demotion-rejected marker) / Err.
/// Oracle contract order: unknown id → Ok(None) (silent, the caller surfaces
/// the miss); then evidence required; then known state required. Entity
/// resolution follows alias entries (oracle entity()).
pub fn promote(
doc: &mut serde_json::Value,
id: &str,
@@ -231,8 +235,8 @@ pub fn promote(
evidence_ref: &str,
now_iso: &str,
) -> Result<Option<String>, String> {
if !is_known_maturity(state) {
return Err(format!("unknown maturity state: {}", state));
if !crate::gap::has_entity(doc, id) {
return Ok(None);
}
if evidence_ref.is_empty() {
return Err(format!(
@@ -240,13 +244,10 @@ pub fn promote(
id, state
));
}
let current = doc
.get("entities")
.and_then(|e| e.as_array())
.and_then(|arr| {
arr.iter()
.find(|e| e.get("id").and_then(|x| x.as_str()) == Some(id))
})
if !is_known_maturity(state) {
return Err(format!("unknown maturity state: {}", state));
}
let current = crate::gap::entity_ref(doc, id)
.and_then(|e| e.get("maturity"))
.and_then(|m| m.as_str())
.unwrap_or("DECLARED")
@@ -264,7 +265,7 @@ pub fn promote(
}
return Ok(Some(current));
}
let entity = find_entity_mut(doc, id).ok_or_else(|| "entity-not-found".to_string())?;
let entity = crate::gap::entity_mut(doc, id).ok_or_else(|| "entity-not-found".to_string())?;
if let Some(e) = entity.as_object_mut() {
e.insert("maturity".into(), serde_json::json!(state));
let mut refs: Vec<String> = e
@@ -285,37 +286,23 @@ pub fn promote(
Ok(Some(state.to_string()))
}
fn find_entity_mut<'a>(
doc: &'a mut serde_json::Value,
id: &str,
) -> Option<&'a mut serde_json::Value> {
doc.get_mut("entities")
.and_then(|e| e.as_array_mut())?
.iter_mut()
.find(|e| e.get("id").and_then(|x| x.as_str()) == Some(id))
}
fn now_iso() -> String {
// oracle: datetime.now(timezone.utc).isoformat() — the digest tests mask
// timestamps; the store's monotonicity doesn't depend on the value.
"1970-01-01T00:00:00+00:00".into()
}
/// resolve_status — smallest coherent entity state (oracle-identical).
pub fn resolve_status(e: &serde_json::Value) -> &'static str {
/// An explicit `status` passes through VERBATIM (oracle: `if e.get("status")`
/// returns it as-is); ext.conflicts is Python-truthy (non-empty), not
/// merely present.
pub fn resolve_status(e: &serde_json::Value) -> String {
if let Some(s) = e.get("status").and_then(|s| s.as_str()) {
if !s.is_empty() {
// static return tied to input would need leaks; return via match
return match_status(s);
return s.to_string();
}
}
let has_conflicts = e
.get("ext")
.and_then(|x| x.get("conflicts"))
.map(|c| !c.is_null())
.map(py_truthy)
.unwrap_or(false);
if has_conflicts {
return "CONFLICTING";
return "CONFLICTING".to_string();
}
let maturity = e.get("maturity").and_then(|m| m.as_str()).unwrap_or("");
let has_evidence = e
@@ -324,25 +311,29 @@ pub fn resolve_status(e: &serde_json::Value) -> &'static str {
.map(|a| !a.is_empty())
.unwrap_or(false);
if (maturity == "EXERCISED" || maturity == "LIVE_PROVEN") && has_evidence {
return "CURRENT";
return "CURRENT".to_string();
}
if !maturity.is_empty() && maturity != "DECLARED" && !has_evidence {
return "CONFLICTING";
return "CONFLICTING".to_string();
}
"UNKNOWN"
"UNKNOWN".to_string()
}
fn match_status(s: &str) -> &'static str {
match s {
"SUPERSEDED" => "SUPERSEDED",
"CURRENT" => "CURRENT",
"CONFLICTING" => "CONFLICTING",
"UNKNOWN" => "UNKNOWN",
_ => "UNKNOWN",
/// Python truthiness for the JSON values resolve_status tests.
fn py_truthy(v: &serde_json::Value) -> bool {
match v {
serde_json::Value::Null => false,
serde_json::Value::Bool(b) => *b,
serde_json::Value::Number(n) => n.as_f64().map(|f| f != 0.0).unwrap_or(false),
serde_json::Value::String(s) => !s.is_empty(),
serde_json::Value::Array(a) => !a.is_empty(),
serde_json::Value::Object(o) => !o.is_empty(),
}
}
/// supersede — replacement with evidence; old identity stays resolvable.
/// Entity resolution follows alias entries; the supersedes relationship goes
/// through the oracle add_relationship law (evidence_refs + observed_at).
pub fn supersede(
doc: &mut serde_json::Value,
new_id: &str,
@@ -353,17 +344,21 @@ pub fn supersede(
if evidence_ref.is_empty() {
return Err("supersession requires evidence".into());
}
if find_entity(doc, new_id).is_none() || find_entity(doc, old_id).is_none() {
if !crate::gap::has_entity(doc, new_id) || !crate::gap::has_entity(doc, old_id) {
return Err("supersede requires both entities to exist".into());
}
doc.get_mut("relationships")
.and_then(|r| r.as_array_mut())
.unwrap_or(&mut Vec::new())
.push(serde_json::json!({
"type": "supersedes", "from": new_id, "to": old_id,
"evidence_refs": [evidence_ref],
}));
if let Some(old) = find_entity_mut(doc, old_id) {
crate::gap::add_relationship(
doc,
crate::gap::RelSpec {
rel_type: "supersedes",
from_id: new_id,
to_id: old_id,
evidence_refs: &[evidence_ref.to_string()],
ext: None,
now_iso,
},
);
if let Some(old) = crate::gap::entity_mut(doc, old_id) {
if let Some(o) = old.as_object_mut() {
o.insert("superseded_by".into(), serde_json::json!(new_id));
o.insert("status".into(), serde_json::json!("SUPERSEDED"));
@@ -378,6 +373,7 @@ pub fn supersede(
Ok(())
}
#[cfg(test)]
fn find_entity<'a>(doc: &'a serde_json::Value, id: &str) -> Option<&'a serde_json::Value> {
doc.get("entities")
.and_then(|e| e.as_array())?
@@ -480,6 +476,7 @@ pub fn authority_observed(
doc: &mut serde_json::Value,
rel: Option<&mut serde_json::Value>,
authority: &str,
now_iso: &str,
) {
let auths = doc
.as_object_mut()
@@ -493,12 +490,12 @@ pub fn authority_observed(
.or_insert(json!({"generation": 0, "observed": []}));
let generation = a["generation"].as_i64().unwrap_or(0) + if rel.is_none() { 1 } else { 0 };
a["generation"] = json!(generation);
a["last_success"] = json!(now_iso());
a["last_success"] = json!(now_iso);
a["status"] = json!("CURRENT");
if let Some(rel) = rel {
rel["authority"] = json!(authority);
rel["status"] = json!("CURRENT");
rel["observed_at"] = json!(now_iso());
rel["observed_at"] = json!(now_iso);
let oid = format!(
"{}|{}|{}",
rel["type"].as_str().unwrap_or(""),
@@ -513,8 +510,9 @@ pub fn authority_observed(
}
/// authority_failed — the authority's facts go STALE (not silently fresh);
/// the authority is recorded DEGRADED with the error.
pub fn authority_failed(doc: &mut serde_json::Value, authority: &str, error: &str) {
/// the authority is recorded DEGRADED with the error, and the degradation is
/// OBSERVED (oracle appends the authority_degraded observation).
pub fn authority_failed(doc: &mut serde_json::Value, authority: &str, error: &str, now_iso: &str) {
let auths = doc
.as_object_mut()
.unwrap()
@@ -525,8 +523,8 @@ pub fn authority_failed(doc: &mut serde_json::Value, authority: &str, error: &st
.unwrap()
.entry(authority.to_string())
.or_insert(json!({"generation": 0, "observed": []}));
a["last_attempt"] = json!(now_iso());
a["last_error"] = json!(&error[..error.len().min(200)]);
a["last_attempt"] = json!(now_iso);
a["last_error"] = json!(&error[..error.chars().count().min(200)]);
a["status"] = json!("DEGRADED");
if let Some(rels) = doc.get_mut("relationships").and_then(|r| r.as_array_mut()) {
for rel in rels.iter_mut() {
@@ -535,6 +533,13 @@ pub fn authority_failed(doc: &mut serde_json::Value, authority: &str, error: &st
}
}
}
if let Some(obs) = doc.get_mut("observations").and_then(|o| o.as_array_mut()) {
obs.push(json!({
"kind": "authority_degraded", "authority": authority,
"error": &error[..error.chars().count().min(200)],
"observed_at": now_iso,
}));
}
}
/// reconcile_authorities — facts owned by an authority that did not
@@ -599,7 +604,8 @@ pub fn reconcile_authorities(doc: &mut serde_json::Value) -> Vec<serde_json::Val
}
/// mark_conflict — BOTH observations preserved; entity CONFLICTING; never
/// last-wins (oracle-identical).
/// last-wins (oracle-identical). Entity resolution follows alias entries.
#[allow(clippy::too_many_arguments)] // oracle signature: the two claims travel together
pub fn mark_conflict(
doc: &mut serde_json::Value,
entity_id: &str,
@@ -608,14 +614,15 @@ pub fn mark_conflict(
authority_b: &str,
claim_b: &str,
evidence_ref: &str,
now_iso: &str,
) -> Result<(), String> {
if find_entity_mut(doc, entity_id).is_none() {
if !crate::gap::has_entity(doc, entity_id) {
return Err(format!("conflict on unknown entity: {}", entity_id));
}
if evidence_ref.is_empty() {
return Err("conflict recording requires evidence".into());
}
let entity = find_entity_mut(doc, entity_id).unwrap();
let entity = crate::gap::entity_mut(doc, entity_id).unwrap();
let e = entity.as_object_mut().unwrap();
let ext = e.entry("ext").or_insert(json!({}));
let conflicts = ext
@@ -626,7 +633,7 @@ pub fn mark_conflict(
conflicts.as_array_mut().unwrap().push(json!({
"authority_a": authority_a, "claim_a": claim_a,
"authority_b": authority_b, "claim_b": claim_b,
"evidence": evidence_ref, "observed_at": now_iso(),
"evidence": evidence_ref, "observed_at": now_iso,
}));
e.insert("status".into(), json!("CONFLICTING"));
if let Some(obs) = doc.get_mut("observations").and_then(|o| o.as_array_mut()) {
@@ -634,7 +641,7 @@ pub fn mark_conflict(
"kind": "authority_conflict", "entity": entity_id,
"authority_a": authority_a, "claim_a": claim_a,
"authority_b": authority_b, "claim_b": claim_b,
"observed_at": now_iso(),
"observed_at": now_iso,
}));
}
Ok(())
@@ -654,7 +661,7 @@ mod authority_battery {
fn authority_observed_lifecycle() {
let mut d = doc();
let mut rel = json!({"type": "owns", "from": "a", "to": "b"});
authority_observed(&mut d, Some(&mut rel), "spine");
authority_observed(&mut d, Some(&mut rel), "spine", "2026-10-13T00:00:00Z");
assert_eq!(rel["status"], "CURRENT");
assert_eq!(rel["authority"], "spine");
let gen = d["authority_generations"]["spine"]["generation"]
@@ -662,7 +669,7 @@ mod authority_battery {
.unwrap();
assert_eq!(gen, 0); // rel observation does NOT bump the generation
// bare observation bumps
authority_observed(&mut d, None, "spine");
authority_observed(&mut d, None, "spine", "2026-10-13T00:00:00Z");
assert_eq!(
d["authority_generations"]["spine"]["generation"]
.as_i64()
@@ -678,10 +685,16 @@ mod authority_battery {
{"type": "owns", "from": "a", "to": "b", "authority": "spine", "status": "CURRENT"},
{"type": "owns", "from": "c", "to": "d", "status": "CURRENT"},
]);
authority_failed(&mut d, "spine", "boom");
authority_failed(&mut d, "spine", "boom", "2026-10-13T00:00:00Z");
assert_eq!(d["relationships"][0]["status"], "STALE");
assert_eq!(d["relationships"][1]["status"], "CURRENT"); // unowned untouched
assert_eq!(d["authority_generations"]["spine"]["status"], "DEGRADED");
// the degradation is OBSERVED, not silent (oracle parity fix)
assert!(d["observations"]
.as_array()
.unwrap()
.iter()
.any(|o| o["kind"] == "authority_degraded" && o["authority"] == "spine"));
}
#[test]
@@ -710,6 +723,7 @@ mod authority_battery {
"b",
"claim two",
"ev-1",
"2026-10-13T00:00:00Z",
)
.expect("conflict ok");
let e = &d["entities"][0];
@@ -718,7 +732,15 @@ mod authority_battery {
let c = &e["ext"]["conflicts"][0];
assert_eq!(c["claim_a"], "claim one");
assert_eq!(c["claim_b"], "claim two"); // BOTH preserved, never last-wins
assert!(mark_conflict(&mut d, "nope", "a", "x", "b", "y", "ev").is_err());
assert!(mark_conflict(&mut d, "module:x", "a", "x", "b", "y", "").is_err());
assert!(mark_conflict(&mut d, "nope", "a", "x", "b", "y", "ev", "t").is_err());
assert!(mark_conflict(&mut d, "module:x", "a", "x", "b", "y", "", "t").is_err());
}
}
// ── dichotomy-wave gap-closing port (contract factory-gap-ss) ──────────────
pub mod gap;
pub use gap::*;
#[cfg(test)]
mod frozen_battery;
+3078937 -3071370
View File
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,24 @@
# Contract BUILDER-2026-10-13T02:02:02Z-dad27f-factory-gap-bm
**Phase:** closed · **Mode:** n/a · **Depth:** n/a
## What was built
Close build_mode Go port PARTIAL gaps (dichotomy factory, issue #2479 tranche): port _load_state/_save_state/assess_complexity + the full start_* phase-transition family name-preserving into the existing Go impl, shared freeze_truth harness battery over every new public fn, live parity 100%, Phase B language-advantage pass.
## What it connects to
(no affected surface recorded)
## How to update those connections
- Open a NEW construction contract (scaffolding is absolute) scoped to the same surface.
- Evidence trail: 4 checkpoint(s) recorded on the contract entity.
- Quality at close: {"candidate_corrections": [], "captured_at": "2026-10-13T07:55:21Z", "contract": "BUILDER-2026-10-13T02:02:02Z-dad27f-factory-gap-bm", "correctly_predicted": [], "evidence_sources": ["unattributed"], "expected_new_entities": [], "expected_new_missing": [], "expected_new_observed": [], "expected_runtime_effects": [], "expected_runtime_missing": [], "expected_runtime_observed": [], "genuinely_unexpected_new": [], "missed": [], "needless": ["module:deploy_qualify"], "observed": [], "observed_existing": [], "observed_new": [], "predicted_but_unaffected": ["module:deploy_qualify"], "predicted_existing_affected": ["module:deploy_qualify"], "scope_check": {"in_scope": [], "out_of_scope": ["scripts/freeze_truth.py", "scripts/gen_build_mode_gap_truth.py", "src/go/build-mode/build_mode.go", "src/go/build-mode/build_mode_gap.go", "src/go/build-mode/phaseb.go", "src/go/build-mode/phaseb_test.go", "src/go/build-mode/truth_buildmode_gap.json", "src/go/build-mode/truth_buildmode_gap_test.go", "src/rune/build_mode.py"], "reason": "contract has no approved scope", "verdict": "unknown"}}.
## Evidence
- builder:factory-gap-bm
- builder:factory-gap-bm
- builder:factory-gap-bm
- closure-record
@@ -0,0 +1,24 @@
# Contract BUILDER-2026-10-13T03:16:06Z-57d4cc-factory-gap-nm
**Phase:** closed · **Mode:** n/a · **Depth:** n/a
## What was built
Close the night_mode dichotomy PARTIAL gaps (audit-named): Go port of _ctx_budget, get_night_mode, start_night_mode, stop_night_mode, get_status, get_report, to_dict, _load, _save, _run_loop, _run_sleep, _run_evaluation (name-preserving, logic 1:1) in src/go/night-mode; pure/IO split law with injectable clock/sleeper/runner/HTTP seams; frozen-oracle truth battery via shared freeze_truth.py embedded VERBATIM in Go tests; live parity 100% on new fns; Python facade stays fully functional (RUNE_NIGHT_MODE_CORE=python pin + auto both green).
## What it connects to
(no affected surface recorded)
## How to update those connections
- Open a NEW construction contract (scaffolding is absolute) scoped to the same surface.
- Evidence trail: 4 checkpoint(s) recorded on the contract entity.
- Quality at close: {"candidate_corrections": [], "captured_at": "2026-10-13T08:04:32Z", "contract": "BUILDER-2026-10-13T03:16:06Z-57d4cc-factory-gap-nm", "correctly_predicted": [], "evidence_sources": ["unattributed"], "expected_new_entities": [], "expected_new_missing": [], "expected_new_observed": [], "expected_runtime_effects": [], "expected_runtime_missing": [], "expected_runtime_observed": [], "genuinely_unexpected_new": [], "missed": [], "needless": ["module:deploy_qualify"], "observed": [], "observed_existing": [], "observed_new": [], "predicted_but_unaffected": ["module:deploy_qualify"], "predicted_existing_affected": ["module:deploy_qualify"], "scope_check": {"in_scope": [], "out_of_scope": ["scripts/check_night_mode_parity.py", "scripts/close_contract_night_mode.py", "scripts/gen_night_mode_truth.py", "scripts/open_contract_night_mode.py", "src/go/night-mode/frozen_truth.json", "src/go/night-mode/main.go", "src/go/night-mode/night_mode_test.go", "src/rune/night_mode.py", "tests/test_night_mode.py"], "reason": "contract has no approved scope", "verdict": "unknown"}}.
## Evidence
- builder:factory-gap-nm
- builder:factory-gap-nm
- builder:factory-gap-nm
- closure-record
+191
View File
@@ -0,0 +1,191 @@
"""test_structural_store_delegation.py — dichotomy-wave gap-closing tests.
The facade must behave identically with the Rust core LIVE (RUNE_STRUCTURAL_CORE
unset, libstructural_store.so built) and with the PYTHON ORACLE pinned
(RUNE_STRUCTURAL_CORE=python). Each battery case runs through both modes and
the results must be identical (timestamps masked by the pinned clock).
"""
import copy
import json
import os
import sys
from pathlib import Path
import pytest
ROOT = Path(__file__).resolve().parent.parent
sys.path.insert(0, str(ROOT / "src"))
from rune import ss_loader # noqa: E402
from rune import structural_store as ss # noqa: E402
FIXED_NOW = "2026-10-13T01:02:03Z"
needs_live = pytest.mark.skipif(
not ss_loader.available(), reason="Rust structural_store core not built"
)
@pytest.fixture(autouse=True)
def _pin_clock(monkeypatch):
import calendar
import time as _time
import types
monkeypatch.setattr(ss, "now", lambda: FIXED_NOW)
epoch = calendar.timegm(_time.strptime(FIXED_NOW, "%Y-%m-%dT%H:%M:%SZ"))
# snapshot() stamps straight off time.strftime/gmtime
monkeypatch.setattr(
ss, "time",
types.SimpleNamespace(
gmtime=lambda: _time.gmtime(epoch),
strftime=lambda fmt, t=None: _time.strftime(
fmt, t if t is not None else _time.gmtime()),
),
)
def run_both_modes(call):
"""Run `call(ss_module)` under the pinned oracle and the live core;
results must be identical."""
results = []
for pin in (True, False):
if pin:
monkey = pytest.MonkeyPatch()
monkey.setenv("RUNE_STRUCTURAL_CORE", "python")
else:
if not ss_loader.available():
pytest.skip("Rust core not built")
monkey = pytest.MonkeyPatch()
monkey.delenv("RUNE_STRUCTURAL_CORE", raising=False)
try:
results.append(call(ss))
finally:
monkey.undo()
assert json.dumps(results[0], sort_keys=True) == json.dumps(
results[1], sort_keys=True
), f"modes diverged:\n{json.dumps(results[0], sort_keys=True)}\n{json.dumps(results[1], sort_keys=True)}"
return results[0]
def make_doc():
d = ss.new_doc()
ss.add_entity(d, id="m:a", kind="module")
ss.add_entity(d, id="m:b", kind="widget_component")
ss.add_entity(d, id="c:api", kind="contract")
ss.add_relationship(d, type="implements", from_id="m:a", to_id="c:api", evidence_refs=["ev"])
ss.add_relationship(d, type="depends_on", from_id="m:b", to_id="m:a", evidence_refs=["ev"])
ss.promote(d, "m:a", "WIRED", "ev-2")
return d
class TestDelegationDualMode:
def test_new_doc_and_uris(self):
def call(m):
out = {"doc": m.new_doc(), "uri": m.spine_uri("project:alpha")}
try:
out["resolved"] = m.resolve_uri("spine://project/alpha")
except ValueError:
out["resolved"] = None
try:
m.resolve_uri("bogus://x")
out["bad_uri"] = None
except ValueError as exc:
out["bad_uri"] = str(exc)
return out
run_both_modes(call)
def test_save_snapshot_digest_bytes(self, tmp_path):
def call(m):
d = make_doc.__wrapped__() if hasattr(make_doc, "__wrapped__") else None
doc = _make_doc(m)
p = m.save(doc, tmp_path / "a" / "s.json")
snap = m.snapshot(doc, tmp_path / "snaps", "l1", tag="deadbeef123")
return {
"bytes": p.read_text(),
"snapshot_name": snap.name,
"snapshot_bytes": snap.read_text(),
"digest": m.canonical_digest(doc),
"doc_schema": doc["schema_version"],
}
run_both_modes(call)
def test_alias_error_and_success(self):
def call(m):
doc = _make_doc(m)
e = m.alias(doc, "m:b", "m:b-old")
with pytest.raises(ValueError):
m.alias(doc, "m:zz", "m:x")
with pytest.raises(ValueError):
m.alias(doc, "m:a", "m:a")
return {"entity": e, "doc": doc}
run_both_modes(call)
def test_frontier_events_diff_dedupe(self):
def call(m):
doc = _make_doc(m)
d2 = copy.deepcopy(doc)
m.dedupe_observations(d2)
return {
"frontier": m.impact_frontier(doc, ["c:api"], change_kind=None),
"frontier_impl": m.impact_frontier(doc, ["m:a"], change_kind=None),
"frontier_violation": m.impact_frontier(doc, ["c:api"], change_kind="contract_violation"),
"events": m.structural_events(doc),
"diff": m.diff(doc, copy.deepcopy(doc)),
"deduped": d2,
"task_entities": m.task_entities(doc, "T1"),
"health": m.authority_health(doc),
}
run_both_modes(call)
def test_bind_task_and_observed_impact(self):
def call(m):
doc = _make_doc(m)
m.bind_task(doc, "T1", ["m:a", "m:b"], "ev-t")
r = m.record_observed_impact(
doc, "T2", [{"entity": "m:a", "source": "test", "detail": "x" * 400}]
)
return {"doc": doc, "observed": r}
run_both_modes(call)
def test_sorted_redact_and_authorities(self):
def call(m):
v = {"b": 2, "a": [{"z": 1, "y": 2}, "é", True, None], "token": "s3cret"}
doc = _make_doc(m)
m._doc_authorities(doc)["p1"] = {"generation": 1, "observed": []}
return {
"sorted": m._sorted(v),
"redacted": m._redact_secrets(v),
"doc": doc,
}
run_both_modes(call)
def test_promote_contract_unchanged(self):
def call(m):
doc = _make_doc(m)
e = m.promote(doc, "m:a", "REACHABLE", "ev-3")
with pytest.raises(ValueError):
m.promote(doc, "m:a", "WIRED", "")
with pytest.raises(ValueError):
m.promote(doc, "m:a", "BOGUS", "ev")
assert m.promote(doc, "m:zz", "PRESENT", "ev") is None
return {"entity": e, "doc": doc}
run_both_modes(call)
def _make_doc(m):
d = m.new_doc()
m.add_entity(d, id="m:a", kind="module")
m.add_entity(d, id="m:b", kind="widget_component")
m.add_entity(d, id="c:api", kind="contract")
m.add_relationship(d, type="implements", from_id="m:a", to_id="c:api", evidence_refs=["ev"])
m.add_relationship(d, type="depends_on", from_id="m:b", to_id="m:a", evidence_refs=["ev"])
m.promote(d, "m:a", "WIRED", "ev-2")
return d