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:
1 parent
fa4f9a2cbb
commit
4649df1ccc
13 files changed
+2296
-134
No files matched your search
@@ -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 |
|
||||
|
||||
@@ -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)
|
||||
@@ -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")
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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)
|
||||
}
|
||||
Reference in new issue
Block a user