feat(construction_patterns_builtin): port to FULL Rust — dichotomy wave

RUST verdict (dichotomy law, #2479 tranche): construction_patterns_
builtin.py's registry surface is ported to src/rust/construction_
patterns_builtin — the four builtin pattern docs (capability-module-
adaptation, release-deployment-change, ui-tui-structural-change, store-
schema-evolution) served verbatim from frozen_cpb_patterns.json (deep-
copied from the live oracle's to_dict() output incl. __init__-sorted
provenance order), PATTERN_REGISTRY_REVISION, and get_pattern's full
branch set (hit per pattern_id, unknown/empty-id miss -> None, revision
match passthrough, revision mismatch with the oracle's exact ValueError
message).

Wiring: cpb_loader.py ctypes loader mirroring cs_loader.py/broker_
loader.py (doc-envelope FFI cpb_registry_revision/cpb_patterns/cpb_
get_pattern, free via capability_scoring cs_free_string — never
re-exported, CPB_RUST_LIB override), facade delegation in construction_
patterns_builtin.get_pattern with RUNE_CPB_CORE=python fallback pin and
strangler fallback to the Python registry when the .so is absent.

Truth: frozen_cpb_truth.json — 17 cases from the live oracle (deep-
copied inputs, RUNE_CPB_CORE=python pinned, every public fn and every
branch incl. all four patterns and the revision-mismatch raise),
replayed VERBATIM by the in-crate cfg(test) branch battery. Live parity
check (scripts/check_cpb_parity.py): 16/16 facade + kernel-surface
cases. clippy: 0 warnings. Consumer suites green (test_construction_
pattern 30, project_build_lifecycle, provider_ownership).

Audit fix (fix-first with proof): dichotomy_audit.py now aliases
curator -> curator-core (ported FULL Go in a95c6ce1 but mislabeled NOT
TOUCHED — the alias was missing from module_to_keys). Regenerated
audit: curator 40/43 FULL; function-level completion 34% -> 36%.
This commit is contained in:
Nick committed 2026-10-12 14:25:29 +01:00
1 parent fa4f9a2cbb
commit 4649df1ccc
13 files changed
+2296 -134

No files matched your search

+34 -22
View File
@@ -2,24 +2,22 @@
_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._
**Function-level completion: 32%** (838/2627 runtime functions matched)
**Function-level completion: 36%** (995/2780 runtime functions matched)
| Status | Modules | Lines | Meaning |
|---|---|---|---|
| FULL | 25 | 8218 | 80%+ of functions ported |
| PARTIAL | 89 | 44507 | 20–80% — real logic exists, gaps named below |
| NOT TOUCHED | 113 | 22686 | no name-preserving implementation |
| Total runtime | 227 | 75411 | |
| FULL | 35 | 12387 | 80%+ of functions ported |
| PARTIAL | 95 | 46242 | 20–80% — real logic exists, gaps named below |
| NOT TOUCHED | 109 | 19373 | no name-preserving implementation |
| Total runtime | 239 | 78002 | |
## NOT TOUCHED — the port queue, in priority order (largest first)
| Module | Lines | Fns | Side |
|---|---|---|---|
| rune_tui | 4170 | 159 | TUI-LAST |
| curator | 1838 | 43 | GO |
| canary_audit_ledger | 541 | 2 | TEST-INFRA |
| tui_validator | 514 | 13 | TUI-LAST |
| worthiness | 411 | 13 | unclassified |
| chat_bubble | 401 | 12 | TUI-LAST |
| drift-ledger | 355 | 14 | unclassified |
| pc_loader | 337 | 22 | unclassified |
@@ -27,31 +25,24 @@ _Regenerate with `python3 scripts/dichotomy_audit.py --write`. A module is FULL
| canary_p1_07 | 324 | 10 | TEST-INFRA |
| canary_p0_07 | 310 | 10 | TEST-INFRA |
| canary_p1_01 | 294 | 12 | TEST-INFRA |
| outcome_ledger | 286 | 7 | RUST |
| time_awareness | 286 | 14 | RUST |
| canary_p1_06 | 280 | 7 | TEST-INFRA |
| canary_p1_03 | 271 | 9 | TEST-INFRA |
| canary_gitea_automation | 263 | 3 | TEST-INFRA |
| canary_p1_02 | 253 | 10 | TEST-INFRA |
| construction_patterns_builtin | 249 | 1 | RUST |
| canary_full_system | 248 | 2 | TEST-INFRA |
| task_gate | 235 | 8 | RUST |
| sm_loader | 239 | 14 | unclassified |
| canary_native | 231 | 3 | TEST-INFRA |
| lexicon_evolution | 229 | 7 | unclassified |
| openai_compat_transport | 227 | 9 | GO |
| shard_manager | 223 | 9 | RUST |
| spine_language | 218 | 5 | RUST |
| canary_p0_04 | 207 | 8 | TEST-INFRA |
| wr_loader | 206 | 20 | unclassified |
| cc_loader | 205 | 28 | RUST |
| forge_client | 203 | 18 | GO |
| canary_context_broker | 199 | 10 | TEST-INFRA |
| canary_p0_05 | 199 | 13 | TEST-INFRA |
| cl_loader | 196 | 20 | unclassified |
| testing_mode | 196 | 12 | RUST |
| grading | 193 | 3 | unclassified |
| canary_spine_meta | 192 | 7 | TEST-INFRA |
| message_graph | 187 | 12 | unclassified |
| ffi_query_memory_smoke | 183 | 5 | RUST |
| tm_loader | 178 | 13 | unclassified |
| smod_loader | 175 | 10 | unclassified |
| behaviour_ledger | 174 | 12 | RUST |
| canary_tui_transport | 174 | 7 | TEST-INFRA |
| sr_loader | 173 | 18 | unclassified |
@@ -60,6 +51,7 @@ _Regenerate with `python3 scripts/dichotomy_audit.py --write`. A module is FULL
| ms_loader | 163 | 16 | RUST |
| canary_gitea_pipeline | 159 | 2 | TEST-INFRA |
| dc_loader | 158 | 13 | unclassified |
| mg_loader | 152 | 14 | unclassified |
| mse_loader | 152 | 12 | unclassified |
| bb_loader | 151 | 14 | unclassified |
| forge | 146 | 11 | GO |
@@ -72,29 +64,35 @@ _Regenerate with `python3 scripts/dichotomy_audit.py --write`. A module is FULL
| ps_loader | 136 | 13 | unclassified |
| verification_layer | 136 | 11 | RUST |
| tui_backlog | 125 | 5 | TUI-LAST |
| le_loader | 124 | 10 | unclassified |
| state_checkpoint | 123 | 8 | unclassified |
| cp_loader | 122 | 9 | unclassified |
| drift_resume | 118 | 3 | RUST |
| tl_loader | 118 | 9 | unclassified |
| bh_loader | 117 | 9 | unclassified |
| tg_loader | 117 | 7 | unclassified |
| canary_conversation_state | 115 | 2 | TEST-INFRA |
| contracts_loader | 112 | 8 | unclassified |
| gr_loader | 112 | 8 | unclassified |
| canary_query_memory | 111 | 2 | TEST-INFRA |
| cpb_loader | 111 | 6 | unclassified |
| tui_transport | 108 | 10 | TUI-LAST |
| canary_unbounded | 105 | 2 | TEST-INFRA |
| message_widgets | 104 | 6 | TUI-LAST |
| self_modify | 102 | 7 | unclassified |
| ct_loader | 103 | 8 | unclassified |
| sc_loader | 96 | 6 | RUST |
| inference_paths | 95 | 2 | unclassified |
| mutation_guards | 94 | 2 | unclassified |
| concept_topology | 90 | 7 | unclassified |
| cs_loader | 90 | 5 | RUST |
| ci_loader | 89 | 4 | unclassified |
| ee_loader | 89 | 4 | unclassified |
| tc_loader | 88 | 5 | RUST |
| broker_loader | 86 | 5 | unclassified |
| canary_schema_migration | 86 | 1 | TEST-INFRA |
| ss_loader | 86 | 7 | unclassified |
| ol_loader | 85 | 4 | unclassified |
| to_loader | 85 | 4 | unclassified |
| ta_loader | 84 | 4 | unclassified |
| mems_loader | 83 | 5 | RUST |
| cm_loader | 82 | 4 | unclassified |
| dl_loader | 77 | 5 | unclassified |
@@ -102,14 +100,12 @@ _Regenerate with `python3 scripts/dichotomy_audit.py --write`. A module is FULL
| cd_loader | 72 | 4 | unclassified |
| execution_trace | 71 | 4 | unclassified |
| gp_loader | 71 | 6 | unclassified |
| experience_encoding | 70 | 1 | unclassified |
| abstraction_ladder | 68 | 3 | unclassified |
| pane_authority | 67 | 2 | unclassified |
| ip_loader | 65 | 4 | unclassified |
| cb_loader | 64 | 3 | unclassified |
| skill_compiler | 64 | 4 | unclassified |
| sg_loader | 63 | 4 | unclassified |
| sm_loader | 63 | 4 | unclassified |
| meaningful_events | 61 | 3 | unclassified |
| outcome_utility | 59 | 4 | unclassified |
| memory_types | 54 | 2 | unclassified |
@@ -166,6 +162,7 @@ _Regenerate with `python3 scripts/dichotomy_audit.py --write`. A module is FULL
| claim_audit | 484 | 4/14 | _get_warm_neuron_store, reframe, _graph_has, _warm_has, verify, journal |
| drift_core | 471 | 4/14 | _ms_core, _now_iso, _py_check_promotion_eligibility, promote_entry, compute_fama, compute_drift_velocity |
| model_profiles | 438 | 5/15 | get_profile_path, load_profile, save_profile, build_profile_from_ollama, scan_all_models, get_or_create_profile |
| testing_mode | 362 | 11/21 | _now, _tm_core, _py_default_state, _py_next_item, _py_record, _py_note_blocker |
| tier_manager | 262 | 5/15 | _cc_probe, cc_loader_pin, _py_core_now, _py_get_tier, _py_promote, _py_demote |
| witness | 446 | 8/18 | _ctx_budget, _now, _cc_probe, cc_loader_pin, _py_extract_topic, _py_detect_signals |
| drift_compression | 307 | 3/12 | _py_get_compression_ratio, _py_compress_text, _py_compress_entry, _py_should_warn, _py_warn_before_compress, get_compression_ratio |
@@ -173,10 +170,12 @@ _Regenerate with `python3 scripts/dichotomy_audit.py --write`. A module is FULL
| scaffold | 868 | 22/31 | init, set_prd, add_maintenance_item, bind_repo, observe_repo, repo_state_from_git |
| behavior | 537 | 6/14 | _bh_core, connect, _correction_matches_python, _habit_matches_python, _reinforce_rust, _reinforce_python |
| builder_bridge | 326 | 3/11 | _construction_store, _active_construction_lines, _default_selector, _cached_memory, _get_selector, _retrieve_memory |
| time_awareness | 402 | 12/20 | _clock_args, _ta_core, get_time_awareness, to_dict, _to_state, _apply_state |
| construction_pattern | 547 | 7/14 | _core, _need_dict, _surface_text, _path_matches, _entity_matches, _obligation_when_hit |
| contracts | 743 | 4/11 | _ct_core, _resolve_and_project, _run_contract_python, capture_signatures, save_signatures, load_signatures |
| graph_memory | 474 | 2/9 | now_iso, connect, resolve_entity, add_edge, query, stats |
| intent_continuity | 168 | 2/9 | to_dict, from_dict, _load, save, create_detour, resolve_detour |
| lexicon_evolution | 304 | 7/14 | _le_core, _py_content_words, _py_distinctive_terms, _py_shared_distinctive, _py_coherence, _py_grounded |
| pins | 282 | 5/12 | _store_path, _save, _detail_blob, unpin, _py_pin_recall, list_pins |
| voice_hybrid | 427 | 3/10 | _connect, _load_growth, _save_growth, source_profiles, always, grow |
| voice_sync | 299 | 3/10 | _now, get_delivery_register_from_db, get_delivery_register, update_rune_md, check_sync, handle_hook |
@@ -197,6 +196,7 @@ _Regenerate with `python3 scripts/dichotomy_audit.py --write`. A module is FULL
| construction_commands | 268 | 1/5 | _refuse, _validate, _accept, dispatch |
| drift_cache | 357 | 12/16 | _dc_core, _py_compute_priority, _py_qualifies_for_cache, main |
| drift_observability | 399 | 5/9 | _do_core, main, _load_alerts, _save_alerts |
| grading | 253 | 3/7 | _gr_core, _py_affirm_rule, _py_grade_fact, _py_tier_of |
| layout_contract | 336 | 2/6 | define_region, bind_component, amend_layout_contract, record_layout_observation |
| model_connect | 471 | 7/11 | _ctx_budget, _now, _get_cloud_models, handle_hook |
| prompt_sync | 487 | 11/15 | _now, _rust_available, get_store, main |
@@ -204,6 +204,7 @@ _Regenerate with `python3 scripts/dichotomy_audit.py --write`. A module is FULL
| self_improve | 246 | 1/5 | _now, suggest_improvements, handle_hook, main |
| stage_guards | 303 | 7/11 | _py_liveness_canary, _py_reconcile, to_dict, chk |
| subconscious_pass | 187 | 1/5 | main, _extract_topic, save_reflection, get_recent_findings |
| task_gate | 301 | 5/9 | _tg_core, _now, list_gates, main |
| construction_activation | 237 | 1/4 | _scaffold_for, _auto_structural_awareness, dispatch_from_decision |
| construction_mode | 412 | 2/5 | _contains_any, to_dict, from_dict |
| construction_projection | 254 | 1/4 | _contract_entity, validate, write_mini_doc |
@@ -215,6 +216,7 @@ _Regenerate with `python3 scripts/dichotomy_audit.py --write`. A module is FULL
| warm_neuron_store | 122 | 3/6 | _db, neuron_slugs, handle_hook |
| cognitive_budgeter | 221 | 1/3 | test_budgeter, to_dict |
| event_bus | 244 | 5/7 | _store_path, handle_hook |
| experience_encoding | 113 | 1/3 | _ee_core, _py_evaluate |
| response_adapter | 222 | 5/7 | test_adapter, to_dict |
| rune_profiler | 165 | 1/3 | profile_model_call, main |
| standardized_providers | 281 | 3/5 | list_providers, connect_all_local |
@@ -227,11 +229,13 @@ _Regenerate with `python3 scripts/dichotomy_audit.py --write`. A module is FULL
| Module | Lines | Fns | Side |
|---|---|---|---|
| curator | 1838 | 40/43 | GO |
| abstraction | 656 | 17/17 | GO |
| system_registry | 603 | 14/16 | RUST |
| construction_intent | 580 | 13/16 | RUST |
| capability_ledger | 571 | 18/19 | RUST |
| event_wait | 518 | 14/14 | GO |
| worthiness | 469 | 12/14 | unclassified |
| tool_protocol | 461 | 22/27 | GO |
| auto_tasks | 460 | 14/14 | GO |
| programme_continuation | 458 | 11/12 | RUST |
@@ -240,12 +244,20 @@ _Regenerate with `python3 scripts/dichotomy_audit.py --write`. A module is FULL
| model_routing | 432 | 13/13 | GO |
| build_basis | 375 | 11/13 | RUST |
| attention_capsule | 365 | 6/7 | RUST |
| outcome_ledger | 318 | 7/8 | RUST |
| message_queue | 316 | 13/13 | GO |
| message_graph | 295 | 12/13 | unclassified |
| toolchain | 295 | 5/6 | RUST |
| shard_manager | 276 | 9/10 | RUST |
| auto_context | 273 | 8/8 | GO |
| construction_patterns_builtin | 266 | 1/1 | RUST |
| openai_compat_transport | 227 | 9/9 | GO |
| evidence_probe | 213 | 9/9 | GO |
| forge_client | 203 | 18/18 | GO |
| self_modify | 149 | 7/8 | unclassified |
| recall_uplift | 141 | 1/1 | RUST |
| rune_spinner | 130 | 4/5 | RUST |
| concept_topology | 128 | 7/8 | unclassified |
| wait_guard | 116 | 1/1 | RUST |
| failure_recovery | 108 | 1/1 | RUST |
| programme_gates | 85 | 4/5 | RUST |
+78
View File
@@ -0,0 +1,78 @@
#!/usr/bin/env python3
"""Live parity check: construction_patterns_builtin facade on the Rust core
vs the Python-pinned oracle (RUNE_CPB_CORE=python). Exercises every public
surface branch: BUILTIN_PATTERNS docs, registry revision, get_pattern hit
per pattern, unknown-id miss, revision match, revision mismatch (exact
exception type + message), revision=None. Deep-copies inputs per case.
"""
import copy
import json
import os
import sys
REPO = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.insert(0, os.path.join(REPO, "src"))
import rune.construction_patterns_builtin as cpb # noqa: E402
PIDS = [p.pattern_id for p in cpb.BUILTIN_PATTERNS]
cases = []
for pid in PIDS:
cases.append(("get_pattern", {"pattern_id": pid, "revision": None}))
rev = cpb.get_pattern(pid).revision
cases.append(("get_pattern", {"pattern_id": pid, "revision": rev}))
cases.append(("get_pattern", {"pattern_id": pid, "revision": rev + 1}))
cases += [
("get_pattern", {"pattern_id": "no-such-pattern", "revision": None}),
("get_pattern", {"pattern_id": "", "revision": None}),
("registry_revision", {}),
]
def run(pin):
os.environ["RUNE_CPB_CORE"] = "python" if pin else "auto"
out = []
for fn, args in cases:
try:
if fn == "get_pattern":
got = cpb.get_pattern(**copy.deepcopy(args))
out.append(("ok", None if got is None else got.to_dict()))
else:
out.append(("ok", cpb.PATTERN_REGISTRY_REVISION))
except ValueError as e:
out.append(("ValueError", str(e)))
return out
def run_rust_docs():
"""The Rust core's raw surface (registry docs + revision) directly."""
from rune import cpb_loader
return {
"revision": cpb_loader.registry_revision(),
"patterns": cpb_loader.patterns(),
"kernel": cpb_loader.available(),
}
want = run(pin=True)
got = run(pin=False)
match = sum(1 for w, g in zip(want, got) if w == g)
for i, (w, g) in enumerate(zip(want, got)):
if w != g:
print(f"DIVERGENCE case {i} {cases[i]}:\n python {w}\n rust {g}")
docs = run_rust_docs()
py_docs = [p.to_dict() for p in cpb.BUILTIN_PATTERNS]
if docs["patterns"] != py_docs or docs["revision"] != cpb.PATTERN_REGISTRY_REVISION:
print(f"DIVERGENCE kernel docs: rust rev={docs['revision']} patterns!=python" )
match = match # counted below as failure
docs_ok = False
else:
docs_ok = True
total = len(cases) + 1
matched = match + (1 if docs_ok else 0)
print(f"PARITY: {matched}/{total}")
sys.exit(0 if matched == total else 1)
+3
View File
@@ -112,6 +112,9 @@ def module_to_keys(module_stem: str) -> list[str]:
"rune_operator": ["operator", "operator-deep", "operator-support", "core-server"],
"rune_tui": ["tui"],
"rune_operator_cli": ["operator"],
# curator was ported to FULL Go (src/go/curator-core, a95c6ce1);
# without this alias the audit mislabels it NOT TOUCHED.
"curator": ["curator-core"],
"memory_store": ["memory-ops", "memory-store-deep"],
"warm_router": ["warm-router", "operator-support"],
"tool_executor": ["tool-executor", "local-tool-runtime", "tool-protocol", "tool-protocol-rw"],
@@ -0,0 +1,95 @@
#!/usr/bin/env python3
"""gen_construction_patterns_builtin_truth.py — dichotomy wave #2479
(construction_patterns_builtin FULL-Rust batch).
Freezes truth for the construction_patterns_builtin surface from the live
Python oracle: BUILTIN_PATTERNS (every pattern's to_dict doc), the registry
revision, and get_pattern across EVERY branch (hit for each pattern_id,
unknown-id miss, revision match, revision mismatch ValueError with the
exact message, revision=None). Inputs deep-copied; RUNE_CPB_CORE=python
pinned so the freeze is the pure-Python truth.
Output: src/rust/construction_patterns_builtin/src/frozen_cpb_patterns.json
(the canonical pattern data, embedded verbatim by the Rust kernel) and
src/rust/construction_patterns_builtin/src/frozen_cpb_truth.json (the case
battery, replayed verbatim by the in-crate cfg(test) branch battery).
Never hand-transcribe: every `want` comes from the live oracle run below.
"""
import copy
import json
import os
import sys
REPO = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.insert(0, os.path.join(REPO, "src"))
os.environ["RUNE_CPB_CORE"] = "python" # pin the Python oracle for the freeze
import rune.construction_patterns_builtin as cpb # noqa: E402 (the live oracle)
OUT_DIR = os.path.join(REPO, "src", "rust", "construction_patterns_builtin", "src")
# ---------------------------------------------------------- canonical data --
# The registry docs, deep-copied straight from the oracle (provenance order
# is the oracle's __init__-sorted order — never re-derive it).
pattern_docs = [copy.deepcopy(p.to_dict()) for p in cpb.BUILTIN_PATTERNS]
pattern_ids = [p.pattern_id for p in cpb.BUILTIN_PATTERNS]
with open(os.path.join(OUT_DIR, "frozen_cpb_patterns.json"), "w") as f:
json.dump({"revision": cpb.PATTERN_REGISTRY_REVISION, "patterns": pattern_docs}, f, indent=2)
f.write("\n")
print(f"frozen_cpb_patterns.json: {len(pattern_docs)} patterns, "
f"revision={cpb.PATTERN_REGISTRY_REVISION}")
# ------------------------------------------------------------------ cases ---
cases = []
def add(fn, name, input_, want):
cases.append({"fn": fn, "name": name, "input": input_, "want": want})
add("registry_revision", "current", {}, cpb.PATTERN_REGISTRY_REVISION)
# BUILTIN_PATTERNS: full doc list, in registry order.
add("patterns", "all docs in registry order", {}, pattern_docs)
# get_pattern — hit branch for EVERY pattern.
for pid in pattern_ids:
got = cpb.get_pattern(pid)
add("get_pattern", f"hit {pid}", {"pattern_id": pid, "revision": None},
None if got is None else copy.deepcopy(got.to_dict()))
# get_pattern — miss branch.
add("get_pattern", "unknown id -> None",
{"pattern_id": "no-such-pattern", "revision": None}, None)
add("get_pattern", "empty id -> None", {"pattern_id": "", "revision": None}, None)
# get_pattern — revision branches: match passes through, mismatch raises.
for pid in pattern_ids:
p = cpb.get_pattern(pid)
got = cpb.get_pattern(pid, revision=p.revision)
add("get_pattern", f"revision match {pid}",
{"pattern_id": pid, "revision": p.revision}, copy.deepcopy(got.to_dict()))
try:
cpb.get_pattern(pid, revision=p.revision + 1)
want = None
except ValueError as e:
want = {"raises": "ValueError", "message": str(e)}
add("get_pattern", f"revision mismatch {pid}",
{"pattern_id": pid, "revision": p.revision + 1}, want)
# get_pattern — revision=None explicit (the default path).
got = cpb.get_pattern(pattern_ids[0], revision=None)
add("get_pattern", "explicit revision=None",
{"pattern_id": pattern_ids[0], "revision": None},
copy.deepcopy(got.to_dict()))
out = os.path.join(OUT_DIR, "frozen_cpb_truth.json")
with open(out, "w") as f:
json.dump({"frozen_from": "live oracle construction_patterns_builtin.py "
"(RUNE_CPB_CORE=python)",
"cases": cases}, f, indent=2)
f.write("\n")
print(f"frozen_cpb_truth.json: {len(cases)} cases")
+18 -1
View File
@@ -9,6 +9,8 @@ CANDIDATE/UNIT-PROVEN — existence is not trust (§15).
from __future__ import annotations
import os
from rune.construction_pattern import ConstructionPattern
__all__ = ["BUILTIN_PATTERNS", "get_pattern", "PATTERN_REGISTRY_REVISION"]
@@ -236,7 +238,22 @@ _REGISTRY: dict[str, ConstructionPattern] = {p.pattern_id: p for p in BUILTIN_PA
def get_pattern(pattern_id: str, revision: int | None = None) -> ConstructionPattern | None:
"""Fetch a builtin pattern; a specific revision raises if the registry
no longer carries it (contracts reference the revision they consumed —
§17)."""
§17).
Dichotomy wave: the Rust registry core first (RUNE_CPB_CORE=python
pins the Python fallback — strangler law). Both serve the same frozen
registry docs, so the pin is only for oracle isolation."""
if os.environ.get("RUNE_CPB_CORE", "auto") != "python":
try:
from rune import cpb_loader
if cpb_loader.available():
doc = cpb_loader.get_pattern(pattern_id, revision)
return ConstructionPattern.from_dict(doc) if doc is not None else None
except ValueError:
raise # the kernel's revision-mismatch message is oracle-exact
except Exception:
pass # kernel unavailable — Python registry below
p = _REGISTRY.get(pattern_id)
if p is None:
return None
+111
View File
@@ -0,0 +1,111 @@
#!/usr/bin/env python3
"""ctypes loader for the Rust construction_patterns_builtin registry core
(dichotomy wave, #2479 tranche). Mirrors cs_loader.py's contract:
FFI contract (doc-envelope law — every call returns {"ok", "value"|"err"}):
* cpb_registry_revision({}) -> number
* cpb_patterns({}) -> [pattern doc, ...] registry order
* cpb_get_pattern({"pattern_id", "revision"}) -> pattern doc | null
(a stale revision is ok=false; get_pattern raises ValueError with the
oracle's exact message)
Free: capability_scoring's cs_free_string (shared process) — never
re-exported here. Override the .so with CPB_RUST_LIB (a FILE path).
"""
from __future__ import annotations
import ctypes
import json
import os
from pathlib import Path
_REPO = Path(__file__).resolve().parents[2]
_CRATE_DIR = _REPO / "src" / "rust" / "construction_patterns_builtin"
_LIB_PATH = Path(
os.environ.get(
"CPB_RUST_LIB",
_CRATE_DIR / "target" / "release" / "libconstruction_patterns_builtin.so",
)
)
_lib = None
class CpbUnavailable(FileNotFoundError):
"""The Rust registry core is not built. Callers fall back to the Python
registry — strangler law."""
def available() -> bool:
return _LIB_PATH.is_file()
def _load() -> ctypes.CDLL:
global _lib
if _lib is not None:
return _lib
if not _LIB_PATH.is_file():
raise CpbUnavailable(
f"Rust construction_patterns_builtin core not built: {_LIB_PATH} missing — "
f"run `cargo build --release` in {_CRATE_DIR}"
)
lib = ctypes.CDLL(str(_LIB_PATH))
for sym in ("cpb_registry_revision", "cpb_patterns", "cpb_get_pattern"):
fn = getattr(lib, sym)
fn.argtypes = [ctypes.c_char_p]
fn.restype = ctypes.c_void_p # char* — never default c_int
# free comes from capability_scoring (same process, shared allocator law)
scoring = ctypes.CDLL(
str(_REPO / "src" / "rust" / "capability_scoring" / "target" / "release" / "libcapability_scoring.so")
)
scoring.cs_free_string.argtypes = [ctypes.c_void_p]
lib._free = scoring.cs_free_string
_lib = lib
return lib
def _call(sym: str, payload: dict):
lib = _load()
fn = getattr(lib, sym)
raw = json.dumps(payload).encode("utf-8")
ptr = fn(raw)
if not ptr:
raise RuntimeError(f"{sym} returned a null pointer")
try:
text = ctypes.string_at(ptr).decode("utf-8")
finally:
lib._free(ptr)
env = json.loads(text)
if not env.get("ok"):
raise RuntimeError(f"{sym} core error: {env.get('err')}")
return env["value"]
def registry_revision() -> int:
"""The builtin pattern registry revision (PATTERN_REGISTRY_REVISION)."""
return int(_call("cpb_registry_revision", {}))
def patterns() -> list:
"""Every builtin pattern doc, in registry order."""
return _call("cpb_patterns", {})
def get_pattern(pattern_id: str, revision: int | None = None):
"""Pattern doc for `pattern_id`, or None. A revision that no longer
matches raises ValueError with the oracle's exact message."""
raw = json.dumps({"pattern_id": pattern_id, "revision": revision}).encode("utf-8")
lib = _load()
ptr = lib.cpb_get_pattern(raw)
if not ptr:
raise RuntimeError("cpb_get_pattern returned a null pointer")
try:
text = ctypes.string_at(ptr).decode("utf-8")
finally:
lib._free(ptr)
env = json.loads(text)
if not env.get("ok"):
# The only err the kernel produces is the oracle's revision-mismatch
# ValueError message — preserve its type for facade parity.
raise ValueError(env.get("err"))
return env["value"]
+5 -96
View File
@@ -2,104 +2,13 @@
# It is not intended for manual editing.
version = 4
[[package]]
name = "capability_scoring"
version = "0.1.0"
[[package]]
name = "construction_patterns_builtin"
version = "0.1.0"
dependencies = [
"serde_json",
"capability_scoring",
]
[[package]]
name = "itoa"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
[[package]]
name = "memchr"
version = "2.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98"
[[package]]
name = "proc-macro2"
version = "1.0.107"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.47"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001"
dependencies = [
"proc-macro2",
]
[[package]]
name = "serde"
version = "1.0.229"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba"
dependencies = [
"serde_core",
]
[[package]]
name = "serde_core"
version = "1.0.229"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.229"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "serde_json"
version = "1.0.151"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c841b55ecdae098c80dcae9cf767f6f8a0c2cdb3416bbef72181df4d0fe73f14"
dependencies = [
"itoa",
"memchr",
"serde",
"serde_core",
"zmij",
]
[[package]]
name = "syn"
version = "3.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8593e8e72159ed2257d083c7a454a85cbf854f37a0966d8d483aff8c8a3ebcee"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "unicode-ident"
version = "1.0.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d245f478577f809a851594d02313b640fb437e0bb33866753cff937863096954"
[[package]]
name = "zmij"
version = "1.0.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b"
@@ -2,9 +2,11 @@
name = "construction_patterns_builtin"
version = "0.1.0"
edition = "2021"
description = "Built-in construction pattern registry — dichotomy wave #2479 tranche; zero-dep (pyjson via capability_scoring)"
[lib]
name = "construction_patterns_builtin"
crate-type = ["rlib", "cdylib", "staticlib"]
[dependencies]
serde_json = "1"
capability_scoring = { path = "../capability_scoring" }
@@ -0,0 +1,365 @@
{
"revision": 1,
"patterns": [
{
"pattern_id": "capability-module-adaptation",
"purpose": "Adapting or extending an existing capability/module: prove the NEW behavior, preserve the OLD behavior (BUILD BUT DON'T BREAK).",
"revision": 1,
"maturity": "UNIT-PROVEN",
"signals": {
"entities_match": [
"module:",
"capability:"
],
"path_contains": [
"src/"
],
"objective_any": [
"adapt",
"extend",
"modify",
"preserve",
"existing",
"capability",
"repair"
]
},
"obligations": [
{
"id": "adapt-new-behavior-proof",
"obligation": "new behavior proven by tests before closure",
"when": {
"always": true
}
},
{
"id": "adapt-old-behavior-preserved",
"obligation": "existing capability behavior preserved; original contracts remain valid",
"when": {
"always": true
}
},
{
"id": "adapt-consumers-verified",
"obligation": "existing consumers of the adapted capability verified against their contracts",
"when": {
"entities_match": [
"module:",
"capability:"
]
}
},
{
"id": "adapt-migration-detected",
"obligation": "migration obligations detected and recorded if any consumer-visible interface changes",
"when": {
"path_contains": [
"src/"
]
}
}
],
"provenance": [
"LIVE-CONTRACT-QUALITY-001 (adaptation resolved + closed live)",
"SCAFFOLD-TRANCHE-001 (build-but-don't-break established)",
"SCAFFOLD-TRANCHE-003 (adaptation two-proof rule)",
"construction_decision.obligations_from_trace (auto-derived since T4)"
],
"exclusions": [],
"toolchain_notes": [],
"runtime_effects": []
},
{
"pattern_id": "release-deployment-change",
"purpose": "Changes to the release/deployment path: fail-closed qualification, exact candidate identity, staging != promotion, truthful promotion outcomes, deployed-identity verification, negative + forward proofs.",
"revision": 1,
"maturity": "UNIT-PROVEN",
"signals": {
"path_contains": [
"deploy",
"promotion",
"qualify",
"release.yml",
"ci.yml",
"deploy_qualify",
"deploy_stage",
"deploy_promotion"
],
"objective_any": [
"deploy",
"release",
"promot",
"qualif",
"candidate",
"current",
"monotonic"
],
"workflow_config_signals": true
},
"obligations": [
{
"id": "rel-fail-closed-qualification",
"obligation": "CI qualification remains fail-closed (exact SHA, two-leg verdict); red candidates never deploy",
"when": {
"path_contains": [
"deploy",
"release",
"qualify",
"ci.yml"
]
}
},
{
"id": "rel-exact-candidate-identity",
"obligation": "deployment addresses the EXACT pushed revision, never whatever HEAD is at deploy time",
"when": {
"path_contains": [
"deploy",
"release"
]
}
},
{
"id": "rel-staging-promotion-distinction",
"obligation": "staged candidate != promoted candidate; promotion outcome must be independently verifiable",
"when": {
"path_contains": [
"deploy",
"promotion",
"stage"
]
}
},
{
"id": "rel-promotion-truthfulness",
"obligation": "promotion outcomes are truthful (PROMOTED / NOOP_ALREADY_CURRENT / STALE_SUPERSEDED / REFUSED_*); stale candidates never claim success",
"when": {
"path_contains": [
"promotion",
"deploy_stage",
"deploy_promotion"
]
}
},
{
"id": "rel-monotonic-promotion",
"obligation": "promotion ordering: an older candidate finishing later cannot regress current; ancestry check + fenced critical section",
"when": {
"path_contains": [
"deploy_promotion",
"deploy_stage",
"promotion"
]
}
},
{
"id": "rel-negative-candidate-proof",
"obligation": "negative proof: red/unqualified candidate is refused; stale candidate cannot promote",
"when": {
"path_contains": [
"deploy",
"promotion",
"qualify"
]
}
},
{
"id": "rel-forward-candidate-proof",
"obligation": "forward proof: a green newer candidate still promotes normally",
"when": {
"path_contains": [
"deploy",
"promotion"
]
}
},
{
"id": "rel-deployed-identity-verification",
"obligation": "deployed identity verified from the deployment authority (current symlink + build identity), not from job artifacts alone",
"when": {
"always": true
}
},
{
"id": "rel-release-regression-verification",
"obligation": "existing release-safety guarantees re-proven (the guarantees that already exist must not silently weaken)",
"when": {
"always": true
}
}
],
"provenance": [
"DEPLOY-MONOTONIC-001 (monotonic promotion + truthful outcomes, live-proven)",
"maintenance:deploy-order-race (live incident: deploys 7355/7359)",
"release-safety repair 2026-09-13 (red-promotion incidents 0.66.28/29: required-checks aggregator lied)",
"scripts/deploy_promotion.py (ancestry + fenced critical section)",
"tests/test_deploy_stage_race.py (22-test matrix incl. two-process live race)"
],
"exclusions": [],
"toolchain_notes": [],
"runtime_effects": []
},
{
"pattern_id": "ui-tui-structural-change",
"purpose": "UI/TUI structural changes: component identity, Layout Contract inheritance, USER-LOCKED preservation, deterministic verification.",
"revision": 1,
"maturity": "UNIT-PROVEN",
"signals": {
"surface_kinds": [
"ui_component",
"layout_region"
],
"path_contains": [
"tui",
"ui",
"widget"
],
"entities_match": [
"ui:",
"region:",
"layout:"
]
},
"obligations": [
{
"id": "ui-layout-contract-inherited",
"obligation": "affected UI components inherit their Layout Contract obligations; layout_contract_check must PASS",
"when": {
"entities_match": [
"ui:",
"region:",
"layout:"
]
}
},
{
"id": "ui-user-locked-preserved",
"obligation": "USER-LOCKED placement is user authority: preserved exactly; changes require explicit user authorization",
"when": {
"entities_match": [
"ui:",
"region:"
]
}
},
{
"id": "ui-unknown-never-pass",
"obligation": "layout verification UNKNOWN (no adapter evidence) must never be reported as PASS",
"when": {
"entities_match": [
"ui:",
"region:",
"layout:"
]
}
},
{
"id": "ui-component-identity-stable",
"obligation": "ui:<component> identity survives implementation-path changes; authority never keyed by filename alone",
"when": {
"entities_match": [
"ui:",
"region:",
"layout:"
]
}
}
],
"provenance": [
"Tranche 3 real TUI proof (gate PASS -> violation FAIL -> restore)",
"issue #105 + plans/2026-09-03-tui-footer.md (version display)",
"maintenance:version-display-user-lock (USER-LOCK upgrade pending user confirmation \u2014 provenance rule honored, not fabricated)",
"tests/test_layout_contract.py + tests/test_layout_contract_real_proof.py"
],
"exclusions": [],
"toolchain_notes": [],
"runtime_effects": []
},
{
"pattern_id": "store-schema-evolution",
"purpose": "Durable store/schema changes: backward readability, authority preservation, deterministic roundtrip, migration only when necessary.",
"revision": 1,
"maturity": "CANDIDATE",
"signals": {
"path_contains": [
"store",
"schema",
"structural_store",
"scaffold",
"spine"
],
"entities_match": [
"module:structural_store",
"module:scaffold",
"module:spine"
],
"objective_any": [
"store",
"schema",
"record",
"persist",
"field",
"migration"
]
},
"obligations": [
{
"id": "store-backward-readability",
"obligation": "old records without new fields read as empty/unknown, never crash; legacy keys preserved",
"when": {
"path_contains": [
"store",
"structural_store",
"scaffold",
"spine",
"schema"
]
}
},
{
"id": "store-roundtrip",
"obligation": "deterministic save/load roundtrip incl. unknown entity/relation kinds and open-world fields",
"when": {
"path_contains": [
"store",
"structural_store",
"scaffold",
"schema"
]
}
},
{
"id": "store-authority-preservation",
"obligation": "structural facts only change through their owning projector/authority; canonical rebuild after registration (no hand-edited structural facts)",
"when": {
"path_contains": [
"spine",
"scaffold",
"store/"
]
}
},
{
"id": "store-migration-only-if-needed",
"obligation": "no mandatory migration unless semantics truly change; ext/open-world fields preferred over schema churn",
"when": {
"path_contains": [
"schema",
"store",
"structural_store"
]
}
}
],
"provenance": [
"Tranche 2 CI lesson: hand-edited spine store diverged from canonical rebuild (deploy-identity edges)",
"Tranche 2 structural-store extraction (behavior-neutrality proof)",
"Tranche 4 contract-quality repair (legacy keys preserved, old contracts readable \u2014 test_h)",
"Tranche 5 expected-runtime-effects extension (old contracts close without new fields)"
],
"exclusions": [],
"toolchain_notes": [],
"runtime_effects": []
}
]
}
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,126 @@
//! branch_battery — replays frozen_cpb_truth.json VERBATIM (17 cases
//! frozen from the live Python oracle construction_patterns_builtin.py,
//! RUNE_CPB_CORE=python pinned, inputs deep-copied before capture).
//! Order-insensitive object comparison; numbers compared exactly (both
//! sides IEEE-754 f64); list order significant (registry + provenance
//! order is oracle-derived).
use capability_scoring::pyjson::{parse, Json};
use crate::registry;
const TRUTH: &str = include_str!("../frozen_cpb_truth.json");
fn jeq(a: &Json, b: &Json) -> bool {
match (a, b) {
(Json::Num(x), Json::Num(y)) => x == y,
(Json::Str(x), Json::Str(y)) => x == y,
(Json::Bool(x), Json::Bool(y)) => x == y,
(Json::Null, Json::Null) => true,
(Json::Arr(x), Json::Arr(y)) => {
x.len() == y.len() && x.iter().zip(y).all(|(i, j)| jeq(i, j))
}
(Json::Obj(x), Json::Obj(y)) => {
x.len() == y.len()
&& x.iter()
.all(|(k, v)| y.iter().any(|(k2, v2)| k == k2 && jeq(v, v2)))
}
_ => false,
}
}
fn as_str(v: Option<&Json>) -> String {
match v {
Some(Json::Str(s)) => s.clone(),
_ => String::new(),
}
}
/// One case's oracle verdict, replayed against the Rust kernel.
fn run_case(fn_name: &str, args: &Json) -> Result<Json, String> {
match fn_name {
"registry_revision" => Ok(Json::Num(registry::registry_revision() as f64)),
"patterns" => Ok(Json::Arr(registry::patterns())),
"get_pattern" => {
let pattern_id = as_str(args.get("pattern_id"));
let revision = match args.get("revision") {
Some(Json::Num(n)) => Some(*n as i64),
_ => None,
};
registry::get_pattern(&pattern_id, revision)
}
other => Err(format!("unknown truth fn {other}")),
}
}
fn check_case(fn_name: &str, name: &str, args: &Json, want: &Json) -> Result<(), String> {
// The oracle's raising branch is recorded as {"raises","message"}.
if let Some(message) = want.get("message").filter(|_| want.get("raises").is_some()) {
match run_case(fn_name, args) {
Err(err) => {
let want_msg = as_str(Some(message));
if err != want_msg {
return Err(format!(
"{fn_name}/{name}: err mismatch\n want {want_msg}\n got {err}"
));
}
return Ok(());
}
Ok(got) => {
return Err(format!(
"{fn_name}/{name}: oracle raises, kernel returned {}",
capability_scoring::pyjson::dumps(&got)
))
}
}
}
match run_case(fn_name, args) {
Ok(got) => {
if jeq(&got, want) {
Ok(())
} else {
Err(format!(
"{fn_name}/{name}: value mismatch\n want {}\n got {}",
capability_scoring::pyjson::dumps(want),
capability_scoring::pyjson::dumps(&got)
))
}
}
Err(err) => Err(format!("{fn_name}/{name}: kernel error {err}")),
}
}
#[test]
fn frozen_truth_replays_verbatim() {
let truth = parse(TRUTH).expect("frozen_cpb_truth.json parses");
let cases = match truth.get("cases") {
Some(Json::Arr(c)) => c.clone(),
_ => panic!("truth has no cases"),
};
assert!(!cases.is_empty(), "frozen truth is empty");
let mut failures = Vec::new();
for case in &cases {
let fn_name = as_str(case.get("fn"));
let name = as_str(case.get("name"));
let args = case.get("input").cloned().unwrap_or(Json::Null);
let want = case.get("want").cloned().unwrap_or(Json::Null);
if let Err(err) = check_case(&fn_name, &name, &args, &want) {
failures.push(err);
}
}
assert!(
failures.is_empty(),
"branch battery diverged from frozen truth:\n{}",
failures.join("\n")
);
}
#[test]
fn truth_case_count_is_stable() {
let truth = parse(TRUTH).expect("frozen_cpb_truth.json parses");
let cases = match truth.get("cases") {
Some(Json::Arr(c)) => c.len(),
_ => 0,
};
assert_eq!(cases, 17, "frozen case count drifted — regenerate truth");
}
@@ -1,19 +1,96 @@
//! construction_patterns_builtin — built-in construction patterns (W6).
pub fn builtin_patterns() -> serde_json::Value {
serde_json::json!({
"patterns": [
{"pattern_id": "web-api", "description": "HTTP API with REST endpoints"},
{"pattern_id": "cli-tool", "description": "Command-line tool with argument parsing"},
]
})
}
//! construction_patterns_builtin — the built-in pattern registry kernel
//! (dichotomy wave #2479 tranche). Serves the oracle's pattern data
//! (frozen verbatim from the live Python registry — provenance order is
//! the oracle's __init__-sorted order) through a doc-envelope FFI. The
//! Python facade keeps a RUNE_CPB_CORE=python pin.
//!
//! FFI contract (doc-envelope law — every call returns {"ok","value"|"err"}):
//! * cpb_registry_revision({}) -> number
//! * cpb_patterns({}) -> [pattern doc, ...] registry order
//! * cpb_get_pattern({"pattern_id", "revision"}) -> pattern doc | null
//! (a revision that no longer matches is an err carrying the oracle's
//! exact ValueError message)
//!
//! Parity oracle: frozen_cpb_truth.json (17 cases frozen from the live
//! Python oracle) replayed verbatim by the in-crate branch battery.
pub mod registry;
pub use capability_scoring::pyjson;
#[path = "branch_battery.rs"]
#[cfg(test)]
mod branch_battery {
use super::*;
mod branch_battery;
#[test]
fn builtin_patterns_exist() {
assert!(true);
use std::ffi::{CStr, CString};
use std::os::raw::c_char;
use pyjson::Json;
fn read_arg(ptr: *const c_char) -> Result<String, String> {
if ptr.is_null() {
return Err("null argument pointer".to_string());
}
unsafe { CStr::from_ptr(ptr) }
.to_str()
.map(|s| s.to_string())
.map_err(|e| format!("argument is not valid UTF-8: {}", e))
}
fn envelope(result: Result<Json, String>) -> *mut c_char {
let body = match result {
Ok(value) => Json::Obj(vec![
("ok".to_string(), Json::Bool(true)),
("value".to_string(), value),
]),
Err(err) => Json::Obj(vec![
("ok".to_string(), Json::Bool(false)),
("err".to_string(), Json::Str(err)),
]),
};
let out = pyjson::dumps(&body);
CString::new(out)
.map(|c| c.into_raw())
.unwrap_or(std::ptr::null_mut())
}
fn arg_json(text: &str) -> Result<Json, String> {
pyjson::parse(text).map_err(|e| format!("argument JSON parse failed: {}", e))
}
/// cpb_registry_revision({}) -> number
#[no_mangle]
pub extern "C" fn cpb_registry_revision(arg: *const c_char) -> *mut c_char {
let result = read_arg(arg)
.and_then(|text| arg_json(&text))
.map(|_| Json::Num(registry::registry_revision() as f64));
envelope(result)
}
/// cpb_patterns({}) -> [pattern doc, ...] in registry order
#[no_mangle]
pub extern "C" fn cpb_patterns(arg: *const c_char) -> *mut c_char {
let result = read_arg(arg)
.and_then(|text| arg_json(&text))
.map(|_| Json::Arr(registry::patterns()));
envelope(result)
}
/// cpb_get_pattern({"pattern_id": "...", "revision": n|null}) -> doc | null
#[no_mangle]
pub extern "C" fn cpb_get_pattern(arg: *const c_char) -> *mut c_char {
let result = read_arg(arg)
.and_then(|text| arg_json(&text))
.and_then(|a| {
let pattern_id = match a.get("pattern_id") {
Some(Json::Str(s)) => s.clone(),
_ => return Err("arg.pattern_id required".to_string()),
};
let revision = match a.get("revision") {
Some(Json::Num(n)) => Some(*n as i64),
_ => None,
};
registry::get_pattern(&pattern_id, revision)
});
envelope(result)
}
@@ -0,0 +1,63 @@
//! registry — the built-in construction pattern registry. The pattern docs
//! are FROZEN from the live Python oracle (frozen_cpb_patterns.json,
//! generated by scripts/gen_construction_patterns_builtin_truth.py with
//! RUNE_CPB_CORE=python pinned — never hand-transcribed): each doc is the
//! oracle's ConstructionPattern.to_dict(), including its __init__-sorted
//! provenance/exclusions order.
use std::sync::OnceLock;
use capability_scoring::pyjson::{self, Json};
const PATTERNS_SRC: &str = include_str!("../frozen_cpb_patterns.json");
fn registry() -> &'static Result<Json, String> {
static REGISTRY: OnceLock<Result<Json, String>> = OnceLock::new();
REGISTRY.get_or_init(|| pyjson::parse(PATTERNS_SRC))
}
fn root() -> Result<&'static Json, String> {
registry().as_ref().map_err(Clone::clone)
}
/// PATTERN_REGISTRY_REVISION from the oracle.
pub fn registry_revision() -> i64 {
match root().ok().and_then(|r| r.get("revision")) {
Some(Json::Num(n)) => *n as i64,
_ => 0,
}
}
/// Every builtin pattern doc, in registry order (BUILTIN_PATTERNS order).
pub fn patterns() -> Vec<Json> {
match root().ok().and_then(|r| r.get("patterns")) {
Some(Json::Arr(items)) => items.clone(),
_ => vec![],
}
}
/// Oracle get_pattern semantics: None id -> Null; a revision that no longer
/// matches raises (err carries the oracle's exact ValueError message).
pub fn get_pattern(pattern_id: &str, revision: Option<i64>) -> Result<Json, String> {
let doc = patterns()
.into_iter()
.find(|p| matches!(p.get("pattern_id"), Some(Json::Str(s)) if s == pattern_id));
let doc = match doc {
Some(d) => d,
None => return Ok(Json::Null),
};
if let Some(rev) = revision {
let current = match doc.get("revision") {
Some(Json::Num(n)) => *n as i64,
_ => 0,
};
if current != rev {
return Err(format!(
"pattern {pattern_id} revision {rev} not in registry \
(current: {current}) \u{2014} the consuming contract records \
which revision it used"
));
}
}
Ok(doc)
}