Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
72e4ebdeb6 | ||
|
|
4bd283c1a8 | ||
|
|
e978510b0b | ||
|
|
b1cc915dae | ||
|
|
f67f51e73c | ||
|
|
4bda61cdc5 | ||
|
|
278b389ca8 | ||
|
|
3605ab35e0 | ||
|
|
51ef7cf967 | ||
|
|
29ce279702 | ||
|
|
7b86d15e67 | ||
|
|
ab79390a09 | ||
|
|
379409b5b6 | ||
|
|
5727a3783f | ||
|
|
2c2b2b5a16 | ||
|
|
528602fb89 | ||
|
|
040397d657 | ||
|
|
8b8ef5bac5 | ||
|
|
2f8f74f2cd | ||
|
|
14325d34c6 | ||
|
|
78cfdc1be8 | ||
|
|
d15a9f26c4 | ||
|
|
79695ab92c | ||
|
|
29a8e9323d | ||
|
|
008f02984d | ||
|
|
0ce97f622a | ||
|
|
447fdd14a4 | ||
|
|
d87cbc0a7f | ||
|
|
08187fd2ea | ||
|
|
ccf6b959f2 | ||
|
|
74d94d3a0c | ||
|
|
2cec4253ec | ||
|
|
2d7df37a4f | ||
|
|
4fd94c988d | ||
|
|
9d9beacaaf | ||
|
|
4369c1a54e | ||
|
|
62c05b695c | ||
|
|
85a965e993 | ||
|
|
8dd42655a2 | ||
|
|
5dbb5ddc34 | ||
|
|
a1494f2e94 | ||
|
|
3a2e61cc0c | ||
|
|
4684edac4e | ||
|
|
e0c68ba418 | ||
|
|
a5656587c4 | ||
|
|
8baf8b9845 | ||
|
|
7e6e785bcf | ||
|
|
ebac42f7ca | ||
|
|
49611fa495 | ||
|
|
ea177db766 | ||
|
|
5701a13109 | ||
|
|
2750eba008 | ||
|
|
d9009cc245 | ||
|
|
ac114c3244 | ||
|
|
cdcae1ead5 | ||
|
|
78a36204b8 | ||
|
|
5f3a0d73a6 | ||
|
|
55fba58281 | ||
|
|
b8b14d5be6 | ||
|
|
265bb509a7 | ||
|
|
a2878b76bd | ||
|
|
34b679fbcd | ||
|
|
e41562aa8d | ||
|
|
b5228ace36 | ||
|
|
e3d0560ed5 | ||
|
|
4f0556fe16 | ||
|
|
f61a4a3fa3 | ||
|
|
89b691b210 | ||
|
|
03ef8132a2 | ||
|
|
e29cc5ee51 | ||
|
|
8ed117abe2 | ||
|
|
633af06e8a | ||
|
|
62e47b3812 | ||
|
|
c0ec3135c6 | ||
|
|
622cceb2f9 | ||
|
|
b50e8553d7 | ||
|
|
4649df1ccc |
No files matched your search
@@ -1,6 +1,6 @@
|
||||
# Rune
|
||||
|
||||
**Current release: v0.236.0**
|
||||
**Current release: v0.251.0**
|
||||
|
||||
**Rune is a persistent personal agent designed to accumulate experience, develop reusable competence, and remain recognisably the same agent even when its underlying foundation model changes.**
|
||||
|
||||
|
||||
@@ -2,114 +2,56 @@
|
||||
|
||||
_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: 46%** (1083/2366 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 | 40 | 13895 | 80%+ of functions ported |
|
||||
| PARTIAL | 98 | 46825 | 20–80% — real logic exists, gaps named below |
|
||||
| NOT TOUCHED | 54 | 12187 | no name-preserving implementation |
|
||||
| Total runtime | 192 | 72907 | |
|
||||
|
||||
## 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 |
|
||||
| canary_rune_serve | 329 | 13 | TEST-INFRA |
|
||||
| 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 |
|
||||
| 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 |
|
||||
| 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 |
|
||||
| behaviour_ledger | 174 | 12 | RUST |
|
||||
| canary_tui_transport | 174 | 7 | TEST-INFRA |
|
||||
| sr_loader | 173 | 18 | unclassified |
|
||||
| canary_tui_drift | 170 | 2 | TEST-INFRA |
|
||||
| inference_trace | 164 | 6 | RUST |
|
||||
| ms_loader | 163 | 16 | RUST |
|
||||
| canary_gitea_pipeline | 159 | 2 | TEST-INFRA |
|
||||
| dc_loader | 158 | 13 | unclassified |
|
||||
| mse_loader | 152 | 12 | unclassified |
|
||||
| bb_loader | 151 | 14 | unclassified |
|
||||
| forge | 146 | 11 | GO |
|
||||
| rune_graphic | 145 | 5 | TUI-LAST |
|
||||
| spine_loader | 144 | 13 | RUST |
|
||||
| ac_loader | 140 | 11 | unclassified |
|
||||
| project_registry | 139 | 11 | RUST |
|
||||
| canary_rune_entry | 137 | 3 | TEST-INFRA |
|
||||
| do_loader | 137 | 8 | unclassified |
|
||||
| ps_loader | 136 | 13 | unclassified |
|
||||
| verification_layer | 136 | 11 | RUST |
|
||||
| tui_backlog | 125 | 5 | TUI-LAST |
|
||||
| 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 |
|
||||
| canary_conversation_state | 115 | 2 | TEST-INFRA |
|
||||
| contracts_loader | 112 | 8 | unclassified |
|
||||
| canary_query_memory | 111 | 2 | TEST-INFRA |
|
||||
| tui_transport | 108 | 10 | TUI-LAST |
|
||||
| canary_unbounded | 105 | 2 | TEST-INFRA |
|
||||
| message_widgets | 104 | 6 | TUI-LAST |
|
||||
| self_modify | 102 | 7 | 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 |
|
||||
| tc_loader | 88 | 5 | RUST |
|
||||
| broker_loader | 86 | 5 | unclassified |
|
||||
| canary_schema_migration | 86 | 1 | TEST-INFRA |
|
||||
| ss_loader | 86 | 7 | unclassified |
|
||||
| to_loader | 85 | 4 | unclassified |
|
||||
| mems_loader | 83 | 5 | RUST |
|
||||
| cm_loader | 82 | 4 | unclassified |
|
||||
| dl_loader | 77 | 5 | unclassified |
|
||||
| lc_loader | 75 | 5 | unclassified |
|
||||
| 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 |
|
||||
@@ -122,7 +64,6 @@ _Regenerate with `python3 scripts/dichotomy_audit.py --write`. A module is FULL
|
||||
| evidence_capsule | 43 | 3 | unclassified |
|
||||
| builder_bridge_cli | 42 | 1 | GO |
|
||||
| __init__ | 40 | 1 | RUST |
|
||||
| mr_loader | 40 | 15 | RUST |
|
||||
| canary_memory_synthesis | 33 | 1 | TEST-INFRA |
|
||||
| canary_resource_authority | 33 | 1 | TEST-INFRA |
|
||||
| canary_surface_parity | 33 | 1 | TEST-INFRA |
|
||||
@@ -166,17 +107,19 @@ _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 |
|
||||
| drift_ledger | 471 | 7/16 | ledger_path, read_block, load_ledger, save_ledger, surface_alert, snapshot |
|
||||
| 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 |
|
||||
| intent_continuity | 284 | 6/14 | _core, _now, _raise_core_err, to_dict, from_dict, save |
|
||||
| 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 |
|
||||
@@ -188,22 +131,22 @@ _Regenerate with `python3 scripts/dichotomy_audit.py --write`. A module is FULL
|
||||
| persona_definition | 210 | 2/8 | _by_topic, compile_full, emit_constitution, emit_identity, emit_persona, handle_hook |
|
||||
| progressive_escalation | 279 | 2/8 | execute_with_escalation, test_escalation, handle_hook, to_dict, execute, verify |
|
||||
| voicebox | 164 | 5/11 | _run, start_recording, stop_recording, _transcribe_and_type, on_press, on_release |
|
||||
| connection_pool | 148 | 3/8 | get_pool, get_connection, get_status, get_connection, get_status |
|
||||
| hook_engine | 860 | 3/8 | discover_functions, tag_function, validate_placements, generate_report, handle_hook |
|
||||
| iteration | 190 | 3/8 | _now, context_packet, predictive_impact, descriptive_impact, add |
|
||||
| task_persist | 269 | 5/10 | _save, export, restore, main, handle_hook |
|
||||
| tiered_executor | 291 | 3/8 | _ctx_budget, test_executor, to_dict, to_dict, _execute_model |
|
||||
| adaptive_router | 625 | 15/19 | _floor_applies_from_flags, _floor_reason, _record_transition_with, _detect_external_refs |
|
||||
| connection_pool | 226 | 7/11 | _core, get_pool, _kernel_state, _get_connection_core |
|
||||
| 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 |
|
||||
| salience_gate | 276 | 1/5 | _sg_core, test_salience, handle_hook, to_dict |
|
||||
| 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 |
|
||||
@@ -211,27 +154,37 @@ _Regenerate with `python3 scripts/dichotomy_audit.py --write`. A module is FULL
|
||||
| event_envelope | 303 | 5/8 | test_envelope, to_canonical_dict, to_dict |
|
||||
| graph_projection | 134 | 3/6 | _model_to_dicts, write_all, _now |
|
||||
| memory_selector | 590 | 11/14 | _mse_core, add_source, _source_store |
|
||||
| subsystem_health | 163 | 3/6 | set_auto_filer, record_failure, configure_logging |
|
||||
| mutation_guards | 143 | 2/5 | _mg_core, _py_classify_mutation, _py_guard_mutation |
|
||||
| subconscious_pass | 236 | 3/6 | main, _kernel, get_recent_findings |
|
||||
| subsystem_health | 213 | 4/7 | _kernel, set_auto_filer, configure_logging |
|
||||
| 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 |
|
||||
| inference_trace | 204 | 4/6 | record_trace, read_traces |
|
||||
| iteration | 261 | 7/9 | _kernel, _now |
|
||||
| recall_uplift | 193 | 1/3 | _core, _fetch_corpus |
|
||||
| response_adapter | 222 | 5/7 | test_adapter, to_dict |
|
||||
| rune_profiler | 165 | 1/3 | profile_model_call, main |
|
||||
| rune_profiler | 196 | 2/4 | _kernel, main |
|
||||
| standardized_providers | 281 | 3/5 | list_providers, connect_all_local |
|
||||
| structural_migration | 77 | 2/4 | apply_migration, consent_brief |
|
||||
| voice_injector | 138 | 3/5 | test_injection, to_dict |
|
||||
| construction_depth | 155 | 1/2 | to_dict |
|
||||
| abstraction_ladder | 102 | 3/4 | _core |
|
||||
| construction_depth | 172 | 2/3 | _core |
|
||||
| recall_router | 165 | 2/3 | handle_hook |
|
||||
|
||||
## FULL — ported, parity-gated
|
||||
|
||||
| Module | Lines | Fns | Side |
|
||||
|---|---|---|---|
|
||||
| curator | 1838 | 43/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 |
|
||||
| drift_ledger | 548 | 20/22 | 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 +193,24 @@ _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 |
|
||||
| drift-ledger | 355 | 14/14 | unclassified |
|
||||
| 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 |
|
||||
| spine_language | 269 | 5/6 | RUST |
|
||||
| construction_patterns_builtin | 266 | 1/1 | RUST |
|
||||
| openai_compat_transport | 227 | 9/9 | GO |
|
||||
| evidence_probe | 213 | 9/9 | GO |
|
||||
| recall_uplift | 141 | 1/1 | RUST |
|
||||
| forge_client | 203 | 18/18 | GO |
|
||||
| behaviour_ledger | 195 | 11/13 | RUST |
|
||||
| self_modify | 149 | 7/8 | unclassified |
|
||||
| forge | 146 | 11/11 | GO |
|
||||
| verification_layer | 136 | 10/11 | 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,83 @@
|
||||
{
|
||||
"landed": [
|
||||
"abstraction",
|
||||
"attention_capsule",
|
||||
"auto_context",
|
||||
"auto_tasks",
|
||||
"behaviour_ledger",
|
||||
"build_basis",
|
||||
"capability_ledger",
|
||||
"concept_topology",
|
||||
"construction_intent",
|
||||
"construction_patterns_builtin",
|
||||
"curator",
|
||||
"drift-ledger",
|
||||
"drift_ledger",
|
||||
"event_wait",
|
||||
"evidence_probe",
|
||||
"failure_recovery",
|
||||
"forge",
|
||||
"forge_client",
|
||||
"goal",
|
||||
"impact_preview",
|
||||
"message_graph",
|
||||
"message_queue",
|
||||
"model_routing",
|
||||
"openai_compat_transport",
|
||||
"outcome_ledger",
|
||||
"programme_continuation",
|
||||
"programme_gates",
|
||||
"provider_context",
|
||||
"release_notes",
|
||||
"route_risk_flags",
|
||||
"rune_spinner",
|
||||
"self_modify",
|
||||
"shard_manager",
|
||||
"spine_language",
|
||||
"system_registry",
|
||||
"tool_protocol",
|
||||
"toolchain",
|
||||
"verification_layer",
|
||||
"wait_guard",
|
||||
"worthiness"
|
||||
],
|
||||
"in_flight": {
|
||||
"project_registry": "rune-wave-ba",
|
||||
"state_checkpoint": "rune-wave-ba",
|
||||
"drift_resume": "rune-wave-bb",
|
||||
"inference_paths": "rune-wave-bb",
|
||||
"execution_trace": "rune-wave-bc",
|
||||
"pane_authority": "rune-wave-bc",
|
||||
"skill_compiler": "rune-wave-bc",
|
||||
"meaningful_events": "rune-wave-bc",
|
||||
"outcome_utility": "rune-wave-bd",
|
||||
"memory_types": "rune-wave-bd",
|
||||
"paths": "rune-wave-bd",
|
||||
"growth_pipeline": "rune-wave-bd",
|
||||
"wait_guard_corpus": "rune-wave-be",
|
||||
"self_model": "rune-wave-be",
|
||||
"research_budget": "rune-wave-be",
|
||||
"evidence_capsule": "rune-wave-be",
|
||||
"builder_bridge_cli": "rune-wave-be",
|
||||
"context_broker": "rune-wave-bf",
|
||||
"local_tool_runtime": "rune-wave-bg",
|
||||
"structural_store": "rune-wave-bh",
|
||||
"persona_gate": "rune-wave-bi",
|
||||
"sleep_time": "rune-wave-bj",
|
||||
"work_manager": "rune-wave-bk",
|
||||
"growth_delta": "rune-wave-bl",
|
||||
"provider_budget": "rune-wave-bm",
|
||||
"safety_override": "rune-wave-bn",
|
||||
"construction_decision": "rune-wave-bo",
|
||||
"memory_consolidation": "rune-wave-bp",
|
||||
"provider_registry": "rune-wave-bq",
|
||||
"night_mode": "rune-wave-br",
|
||||
"build_mode": "rune-wave-bs",
|
||||
"failure_auto_filer": "rune-wave-bt"
|
||||
},
|
||||
"excluded": [
|
||||
"TUI-LAST modules",
|
||||
"TEST-INFRA modules",
|
||||
"RETIRES modules"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
# FACTORY VERIFICATION LADDER (binding, 2026-10-11)
|
||||
|
||||
Every dichotomy port landing MUST pass all six layers before it counts as
|
||||
FULL. The ladder is the anti-slop core: generation is cheap, verification
|
||||
is what transfers reliability.
|
||||
|
||||
## The six layers (in order)
|
||||
|
||||
1. **Existence.** The commit SHA is on gitea/master; the diff touches the
|
||||
files claimed. Catches fabrication.
|
||||
2. **Green in MY checkout.** Battery re-run fresh (Go tests / cargo test /
|
||||
clippy / vet / gofmt). Must pass on the reviewer's machine, not just
|
||||
the agent's. Catches environment artifacts.
|
||||
3. **Truth consumed, not self-generated.** The test reads the frozen truth
|
||||
file. Then: regenerate truth from the live oracle independently and
|
||||
byte-compare against the frozen file. Catches self-consistent misreads
|
||||
(agent + battery agree, oracle disagrees) — the most dangerous failure.
|
||||
4. **Live parity re-run.** Port vs live oracle on truth inputs, 100%.
|
||||
Catches stale-library and PYTHONPATH artifacts.
|
||||
5. **Python surface intact.** Module's existing suite green, both with
|
||||
delegation live and oracle pinned (RUNE_*_CORE=python). Catches wiring
|
||||
regressions and collateral damage.
|
||||
6. **Phase B real (v2).** Battery green after the improvement commit; the
|
||||
improvements are genuine language advantages (enums, exhaustive match,
|
||||
iterators, Result, zero-copy, lifetimes / goroutines, interfaces,
|
||||
compile-time safety), not cosmetic churn. Read the diff.
|
||||
|
||||
## Limits of the ladder
|
||||
|
||||
Parity proves faithful translation, NOT original correctness. Oracle bugs
|
||||
need architectural judgment — escalate per the bug policy (rare, to #2479,
|
||||
work keeps flowing).
|
||||
|
||||
## Throughput law
|
||||
|
||||
Verification scales linearly with landings; reviewer throughput is the
|
||||
gate. Spawn rate must not exceed verification rate. Backlog of unverified
|
||||
landings is a stop-spawning signal, not a queue to admire.
|
||||
|
||||
## Factory workflow law (continuous-flow, 2026-10-11)
|
||||
|
||||
**On every agent completion notification, the coordinator does exactly three
|
||||
things, in order:** (1) verify the landing through the six+1 layer ladder,
|
||||
(2) update the count, (3) run `scripts/factory_spawn_gate.sh` — if ALLOW,
|
||||
spawn the next queued module into a fresh worktree.
|
||||
|
||||
**Wave-thinking is the anti-pattern.** Waves create dead time: 11 agents
|
||||
porting while the 12th lands means 11 worktrees idle until the wave drains.
|
||||
Continuous flow keeps every worktree occupied, the verification gate never
|
||||
starves, and the spawn gate (`factory_spawn_gate.sh`) enforces the limits
|
||||
deterministically — agent count and verification backlog never rely on
|
||||
coordinator cognition.
|
||||
|
||||
**The spawn gate answers ONE question:** may a new agent spawn now?
|
||||
Limits: max 12 active, max 3 unverified landings. The gate is law — the
|
||||
coordinator does not override it from feel.
|
||||
|
||||
## NO-REPORT LAW (operational, 2026-10-12)
|
||||
|
||||
The coordinator does NOT report landings, counts, or progress to Nick between
|
||||
waves. Reporting interrupts autonomy — the coordinator's job is to keep the
|
||||
factory running, not narrate it.
|
||||
|
||||
**What replaces reporting:**
|
||||
- The audit instrument (`dichotomy_audit.py`) IS the progress report — Nick
|
||||
reads it when he wants to know.
|
||||
- Session notes capture the full state for checkpoint continuity.
|
||||
- The only exception: a genuine architectural blocker that needs Nick's
|
||||
decision (per the bug policy), posted to #2479, not surfaced in chat.
|
||||
|
||||
**Verification and spawning continue silently.** The coordinator verifies
|
||||
each landing, updates the count internally, spawns the next module — all
|
||||
without producing output for Nick. This is the no-stop law applied to
|
||||
reporting: stopping to report IS stopping.
|
||||
|
||||
## DUPLICATE-SPAWN LAW (2026-10-12)
|
||||
|
||||
Before spawning an agent for module X:
|
||||
1. Check `docs/factory-claims.json` — if the module is in `landed` or `in_flight`, SKIP.
|
||||
2. Check `git log gitea/master --oneline -- <module_paths>` — if a dichotomy wave commit exists, SKIP.
|
||||
3. Add the module to `in_flight` in `factory-claims.json` BEFORE spawning.
|
||||
4. On landing: move from `in_flight` to `landed`.
|
||||
5. On abort: move from `in_flight` back to the queue.
|
||||
|
||||
general-47 wasted 1.5h porting behaviour_ledger because general-46 landed it
|
||||
first and the spawn dispatch didn't check. This law prevents that.
|
||||
|
||||
## SIZE-ADAPTIVE BATCHING (2026-10-12)
|
||||
|
||||
The factory auto-scales agent assignment based on module size:
|
||||
|
||||
| Module size | Agent assignment | Rationale |
|
||||
|---|---|---|
|
||||
| < 100 lines | 3-5 per agent (batch) | Setup cost amortizes; context fits |
|
||||
| 100-300 lines | 1-2 per agent | Moderate context; maybe 2 small + 1 medium |
|
||||
| 300-800 lines | 1 dedicated agent | Full context needed per module |
|
||||
| 800-1500 lines | 1 dedicated agent, possibly split | Large oracle; agent may need 2 sessions |
|
||||
| > 1500 lines | Coordinator ports directly (Rune) | Load-bearing; agent context can't hold the full oracle reliably |
|
||||
|
||||
The batch prompt template includes a "read the module, decide if it fits, skip if it doesn't" instruction so the agent self-reports when a module is too large for the batch context.
|
||||
File diff suppressed because it is too large.
Load diff
+1
-1
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "rune"
|
||||
version = "0.236.0"
|
||||
version = "0.251.0"
|
||||
description = "Rune — a stateful coding assistant with persistent memory, orchestration, and TUI"
|
||||
requires-python = ">=3.10"
|
||||
|
||||
|
||||
@@ -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)
|
||||
@@ -0,0 +1,196 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Live parity: execution_trace Rust kernel vs Python oracle (dichotomy
|
||||
wave). Layers:
|
||||
1. FROZEN — the 20 frozen-oracle cases replayed through the facade in
|
||||
BOTH core modes (RUNE_ET_CORE=python floor vs auto kernel); exact
|
||||
compare, journal contents rebuilt verbatim from the truth.
|
||||
2. FUZZ — 400 seeded random journals/tasks/payloads (seed 2479)
|
||||
driven through both modes; exact compare.
|
||||
"""
|
||||
import copy
|
||||
import json
|
||||
import os
|
||||
import random
|
||||
import sys
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
|
||||
REPO = Path(__file__).resolve().parents[1]
|
||||
sys.path.insert(0, str(REPO / "src"))
|
||||
sys.path.insert(0, str(REPO / "src" / "rune"))
|
||||
|
||||
import execution_trace as et # noqa: E402
|
||||
from rune import et_loader # noqa: E402
|
||||
|
||||
if not et_loader.available():
|
||||
print("FATAL: Rust execution_trace core not built")
|
||||
sys.exit(2)
|
||||
|
||||
TRUTH = json.loads(
|
||||
(REPO / "src" / "rust" / "execution_trace" / "frozen_et_truth.json")
|
||||
.read_text())
|
||||
|
||||
fails = []
|
||||
counts = {"frozen": 0, "fuzz": 0}
|
||||
|
||||
|
||||
def fail(layer, name, detail):
|
||||
fails.append((layer, name, detail))
|
||||
print(f"FAIL[{layer}] {name}: {detail}")
|
||||
|
||||
|
||||
def drive(mode, fn, *args, **kwargs):
|
||||
os.environ["RUNE_ET_CORE"] = mode
|
||||
try:
|
||||
return ("ok", fn(*copy.deepcopy(args), **copy.deepcopy(kwargs)))
|
||||
except Exception as e: # noqa: BLE001 — crash parity is part of the law
|
||||
return ("raise", type(e).__name__)
|
||||
finally:
|
||||
os.environ["RUNE_ET_CORE"] = "auto"
|
||||
|
||||
|
||||
def journal_file(lines):
|
||||
if lines is None:
|
||||
fd, path = tempfile.mkstemp(suffix=".jsonl")
|
||||
os.close(fd)
|
||||
os.unlink(path)
|
||||
return path
|
||||
with tempfile.NamedTemporaryFile("w", suffix=".jsonl", delete=False,
|
||||
encoding="utf-8") as f:
|
||||
f.write("\n".join(lines) + "\n")
|
||||
return f.name
|
||||
|
||||
|
||||
def frozen():
|
||||
for group, fn, key in (
|
||||
("build_receipt_cases", et.build_receipt, "args"),
|
||||
("trace_task_cases", None, "args"),
|
||||
):
|
||||
for c in TRUTH[group]:
|
||||
name = c["name"]
|
||||
counts["frozen"] += 1
|
||||
if group == "build_receipt_cases":
|
||||
py = drive("python", et.build_receipt,
|
||||
c["args"]["task"], c["args"]["outcome_payload"])
|
||||
rs = drive("auto", et.build_receipt,
|
||||
c["args"]["task"], c["args"]["outcome_payload"])
|
||||
if py != rs:
|
||||
fail("frozen", f"build_receipt/{name}", f"{py!r} != {rs!r}")
|
||||
continue
|
||||
path = journal_file(c["args"]["lines"])
|
||||
try:
|
||||
py = drive("python", et.trace_task, c["args"]["task_id"],
|
||||
c["args"]["task_record"], path)
|
||||
rs = drive("auto", et.trace_task, c["args"]["task_id"],
|
||||
c["args"]["task_record"], path)
|
||||
finally:
|
||||
if c["args"]["lines"] is not None:
|
||||
os.unlink(path)
|
||||
if py != rs:
|
||||
fail("frozen", f"trace_task/{name}", f"{py!r} != {rs!r}")
|
||||
for c in TRUTH["read_outcome_events_cases"]:
|
||||
name = c["name"]
|
||||
counts["frozen"] += 1
|
||||
path = journal_file(c["lines"])
|
||||
try:
|
||||
py = drive("python", et._read_outcome_events, path)
|
||||
rs = drive("auto", et._read_outcome_events, path)
|
||||
finally:
|
||||
if c["lines"] is not None:
|
||||
os.unlink(path)
|
||||
if py != rs:
|
||||
fail("frozen", f"read_outcome_events/{name}",
|
||||
f"{py!r} != {rs!r}")
|
||||
|
||||
|
||||
WORDS = ["done", "failed", "running", "", "détaché", "line\\nbreak"]
|
||||
MODELS = ["", "qwen2.5:3b", "cloud-x", None]
|
||||
|
||||
|
||||
def rand_journal(rng):
|
||||
lines = []
|
||||
for _ in range(rng.randint(0, 6)):
|
||||
r = rng.random()
|
||||
tid = f"t{rng.randint(0, 5)}"
|
||||
if r < 0.15:
|
||||
lines.append(rng.choice(["{not json", "", "[1,2]"]))
|
||||
continue
|
||||
ev = rng.choice(["task.outcome", "task.started", "task.outcome"])
|
||||
payload = {"task_id": tid if rng.random() > 0.1 else "",
|
||||
"status": rng.choice(WORDS)}
|
||||
if rng.random() < 0.5:
|
||||
payload["model"] = rng.choice(MODELS)
|
||||
if rng.random() < 0.5:
|
||||
payload["completed_at"] = rng.choice(["", "T-a", "T-b"])
|
||||
lines.append(json.dumps({"event_type": ev, "payload": payload}))
|
||||
return lines
|
||||
|
||||
|
||||
def rand_task(rng):
|
||||
task = {}
|
||||
if rng.random() < 0.9:
|
||||
task["task_id"] = f"t{rng.randint(0, 7)}"
|
||||
if rng.random() < 0.7:
|
||||
task["description"] = rng.choice(WORDS)
|
||||
if rng.random() < 0.8:
|
||||
task["status"] = rng.choice(WORDS)
|
||||
if rng.random() < 0.6:
|
||||
task["assigned_model"] = rng.choice(MODELS)
|
||||
if rng.random() < 0.5:
|
||||
task["memories_used"] = ["m1"] if rng.random() < 0.5 else []
|
||||
if rng.random() < 0.4:
|
||||
task["rules_used"] = []
|
||||
if rng.random() < 0.3:
|
||||
task["capability"] = rng.choice(["", "forge"])
|
||||
if rng.random() < 0.4:
|
||||
task["actions"] = ["a"] if rng.random() < 0.5 else []
|
||||
if rng.random() < 0.5:
|
||||
task["completed_at"] = rng.choice(["", "T-task"])
|
||||
return task
|
||||
|
||||
|
||||
def rand_record(rng):
|
||||
r = rng.random()
|
||||
if r < 0.4:
|
||||
return None
|
||||
if r < 0.5:
|
||||
return {}
|
||||
return rand_task(rng)
|
||||
|
||||
|
||||
def fuzz():
|
||||
rng = random.Random(2479)
|
||||
for i in range(400):
|
||||
lines = rand_journal(rng)
|
||||
task = rand_task(rng)
|
||||
record = rand_record(rng)
|
||||
tid = f"t{rng.randint(0, 7)}"
|
||||
path = journal_file(lines)
|
||||
try:
|
||||
py = drive("python", et.trace_task, tid, record, path)
|
||||
rs = drive("auto", et.trace_task, tid, record, path)
|
||||
finally:
|
||||
if lines is not None:
|
||||
os.unlink(path)
|
||||
counts["fuzz"] += 1
|
||||
if py != rs:
|
||||
fail("fuzz", f"case-{i}", f"{py!r} != {rs!r}")
|
||||
continue
|
||||
payload_rng = random.Random(1000 + i)
|
||||
t = rand_task(payload_rng)
|
||||
p = None if payload_rng.random() < 0.4 else {
|
||||
"model": rng.choice(MODELS), "status": rng.choice(WORDS),
|
||||
"completed_at": rng.choice(["", "T-p"])}
|
||||
py = drive("python", et.build_receipt, t, p)
|
||||
rs = drive("auto", et.build_receipt, t, p)
|
||||
counts["fuzz"] += 1
|
||||
if py != rs:
|
||||
fail("fuzz", f"receipt-{i}", f"{py!r} != {rs!r}")
|
||||
|
||||
|
||||
frozen()
|
||||
fuzz()
|
||||
total = counts["frozen"] + counts["fuzz"]
|
||||
print(f"parity: {total - len(fails)}/{total} "
|
||||
f"(frozen {counts['frozen']}, fuzz {counts['fuzz']})")
|
||||
sys.exit(1 if fails else 0)
|
||||
@@ -0,0 +1,301 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Live parity: iteration Rust kernel vs Python oracle (dichotomy wave).
|
||||
|
||||
Three layers, all must be green for parity 100%:
|
||||
1. FROZEN — the 29 frozen-oracle cases replayed VERBATIM through both
|
||||
the Python floor (oracle sanity) and the Rust kernel.
|
||||
2. LIVE — oracle (RUNE_ITR_CORE=python) vs facade (kernel auto) on the
|
||||
same inputs; `iteration._now` is pinned so generated_at /
|
||||
recorded_at / invalidation clocks are byte-comparable.
|
||||
3. FUZZ — 400 seeded random structural models / ledgers / behaviour
|
||||
docs (seed 2479), every public function, both cores.
|
||||
"""
|
||||
import copy
|
||||
import json
|
||||
import os
|
||||
import random
|
||||
import sys
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
|
||||
REPO = Path(__file__).resolve().parents[1]
|
||||
sys.path.insert(0, str(REPO / "src"))
|
||||
sys.path.insert(0, str(REPO / "src" / "rust" / "evidence_provenance"))
|
||||
|
||||
os.environ.setdefault("RUNE_LEDGER_CORE", "python")
|
||||
|
||||
import rune.iteration as it # noqa: E402
|
||||
import rune.behaviour_ledger as bl_mod # noqa: E402
|
||||
from rune.structural_model import StructuralModel # noqa: E402
|
||||
from rune.capability_ledger import CapabilityLedger # noqa: E402
|
||||
from rune.behaviour_ledger import BehaviourLedger # noqa: E402
|
||||
from rune import itr_loader # noqa: E402
|
||||
|
||||
PIN_NOW = "2026-10-12T00:00:00Z"
|
||||
it._now = lambda: PIN_NOW
|
||||
bl_mod._now = lambda: PIN_NOW
|
||||
|
||||
TRUTH = json.loads(
|
||||
(REPO / "src" / "rust" / "iteration" / "src" / "frozen_it_truth.json")
|
||||
.read_text())
|
||||
|
||||
KINDS = ["project", "domain", "subsystem", "component", "interface",
|
||||
"state_store", "test_suite", "deployment_surface"]
|
||||
REL_TYPES = ["belongs_to", "owns", "depends_on", "calls", "provides",
|
||||
"consumes", "verified_by", "deployed_by"]
|
||||
MATURITIES = ["DECLARED", "implemented", "retired", "superseded", "candidate"]
|
||||
|
||||
fails = []
|
||||
counts = {"frozen": 0, "live": 0, "fuzz": 0}
|
||||
|
||||
|
||||
def fail(layer, name, detail):
|
||||
fails.append((layer, name, detail))
|
||||
print(f"FAIL[{layer}] {name}: {detail}")
|
||||
|
||||
|
||||
def wrap(doc, cls):
|
||||
o = cls.__new__(cls)
|
||||
o.doc = doc
|
||||
return o
|
||||
|
||||
|
||||
# ── fixture factory (oracle-shaped) ─────────────────────────────────────
|
||||
|
||||
def make_fixture(rnd, tmp, i):
|
||||
model = StructuralModel(Path(tmp) / f"m{i}", f"p{i}")
|
||||
n_ent = rnd.randint(2, 9)
|
||||
ids = []
|
||||
for j in range(n_ent):
|
||||
kind = rnd.choice(KINDS)
|
||||
eid = model.add(kind, f"e{j}")
|
||||
ids.append(eid)
|
||||
if rnd.random() < 0.4:
|
||||
for e in model.doc["entities"]:
|
||||
if e["id"] == eid:
|
||||
e.setdefault("identities", {})["source"] = f"src/{j}.py"
|
||||
for _ in range(rnd.randint(0, 12)):
|
||||
a, b = rnd.choice(ids), rnd.choice(ids)
|
||||
if a == b:
|
||||
continue
|
||||
try:
|
||||
model.relate(a, rnd.choice(REL_TYPES), b,
|
||||
status=rnd.choice(["active", "stale"]))
|
||||
except ValueError:
|
||||
pass
|
||||
model.doc["schema_version"] = 1
|
||||
ledger = CapabilityLedger(Path(tmp) / f"l{i}")
|
||||
for j in range(rnd.randint(1, 4)):
|
||||
ledger.define(
|
||||
capability_id=f"cap{j}", name=f"c{j}", purpose="p",
|
||||
owning_project=f"p{i}",
|
||||
component=rnd.choice(ids),
|
||||
source_locations=[f"src/{rnd.randint(0, n_ent)}.py"])
|
||||
bh = BehaviourLedger(Path(tmp) / f"b{i}")
|
||||
for j in range(rnd.randint(1, 3)):
|
||||
bh.define(behaviour_id=f"b{j}", owning_project=f"p{i}",
|
||||
intended_behaviour="x",
|
||||
owner_component=rnd.choice(ids),
|
||||
dependencies=[rnd.choice(ids)])
|
||||
if rnd.random() < 0.6:
|
||||
bh.record_real_world(f"b{j}", passed=True, detail="ok")
|
||||
return model, ledger, bh
|
||||
|
||||
|
||||
def run_all(model, ledger, bh, target):
|
||||
"""Call every public fn through whatever core is currently pinned."""
|
||||
out = {}
|
||||
out["context_packet"] = it.context_packet(
|
||||
model, ledger, target, intent="i", basis_summary={"d": 1})
|
||||
out["predictive_impact"] = it.predictive_impact(model, target)
|
||||
out["descriptive_impact"] = it.descriptive_impact(
|
||||
model, target, observed_changed=[target, "surprise:x"],
|
||||
generation="gf")
|
||||
out["post_change_obligations"] = it.post_change_obligations(
|
||||
model, target, behaviours=bh)
|
||||
out["post_change_obligations_nobh"] = it.post_change_obligations(
|
||||
model, target)
|
||||
out["invalidate"] = None
|
||||
return out
|
||||
|
||||
|
||||
# ── 1. frozen replay ────────────────────────────────────────────────────
|
||||
|
||||
def frozen():
|
||||
for group, cases in TRUTH.items():
|
||||
if group in ("module", "now"):
|
||||
continue
|
||||
for c in cases:
|
||||
counts["frozen"] += 1
|
||||
args = copy.deepcopy(c["args"])
|
||||
fn = c["fn"]
|
||||
now = args.get("now", PIN_NOW)
|
||||
if fn == "context_packet":
|
||||
m = wrap(args["model"], StructuralModel)
|
||||
l = wrap(args.get("ledger") or {"schema_version": 1, "capabilities": {}}, CapabilityLedger)
|
||||
want = c["expect"]
|
||||
got_o = it.context_packet(
|
||||
m, l, args["target"], intent=args.get("intent", ""),
|
||||
basis_summary=args.get("basis_summary"),
|
||||
max_local_files=args.get("max_local_files", 12))
|
||||
got_k = itr_loader.context_packet(
|
||||
args["model"], args["ledger"], args["target"],
|
||||
intent=args.get("intent", ""),
|
||||
basis_summary=args.get("basis_summary"),
|
||||
max_local_files=args.get("max_local_files", 12),
|
||||
now=now)
|
||||
elif fn == "predictive_impact":
|
||||
m = wrap(args["model"], StructuralModel)
|
||||
want = c["expect"]
|
||||
got_o = it.predictive_impact(m, args["target"])
|
||||
got_k = itr_loader.predictive_impact(args["model"],
|
||||
args["target"])
|
||||
elif fn == "descriptive_impact":
|
||||
m = wrap(args["model"], StructuralModel)
|
||||
want = c["expect"]
|
||||
got_o = it.descriptive_impact(
|
||||
m, args["target"],
|
||||
observed_changed=args["observed_changed"],
|
||||
generation=args.get("generation", ""))
|
||||
got_k = itr_loader.descriptive_impact(
|
||||
args["model"], args["target"],
|
||||
observed_changed=args["observed_changed"],
|
||||
generation=args.get("generation", ""), now=now)
|
||||
elif fn == "record_impact_delta":
|
||||
want = c["expect"]
|
||||
got_o = json.dumps(args["delta"], sort_keys=True)
|
||||
got_k = itr_loader.record_delta_line(args["delta"])
|
||||
elif fn == "invalidate_behaviours":
|
||||
want = c["expect"]
|
||||
b = wrap(copy.deepcopy(args["behaviours"]), BehaviourLedger)
|
||||
recs_o = it.invalidate_behaviours(
|
||||
wrap({"relationships": []}, StructuralModel), b,
|
||||
changed_entities=set(args["changed"]),
|
||||
generation=args["generation"])
|
||||
got_o = {"doc": b.doc, "recs": recs_o}
|
||||
doc_k, recs_k = itr_loader.invalidate(
|
||||
args["behaviours"], args["changed"],
|
||||
args["generation"], now)
|
||||
got_k = {"doc": doc_k, "recs": recs_k}
|
||||
elif fn == "post_change_obligations":
|
||||
m = wrap(args["model"], StructuralModel)
|
||||
want = c["expect"]
|
||||
bh = (wrap(args["behaviours"], BehaviourLedger)
|
||||
if "behaviours" in args else None)
|
||||
got_o = it.post_change_obligations(
|
||||
m, args["target"],
|
||||
changed_entities=set(args["changed"])
|
||||
if "changed" in args else None,
|
||||
behaviours=bh)
|
||||
got_k = itr_loader.post_change_obligations(
|
||||
args["model"], args["target"],
|
||||
changed=args.get("changed"),
|
||||
behaviours_doc=args.get("behaviours"))
|
||||
else: # pragma: no cover
|
||||
continue
|
||||
for tag, got in (("oracle", got_o), ("kernel", got_k)):
|
||||
if json.dumps(got, sort_keys=True) != json.dumps(
|
||||
want, sort_keys=True):
|
||||
fail("frozen", f"{fn}/{c['name']}/{tag}",
|
||||
f"want={json.dumps(want, sort_keys=True)[:300]} "
|
||||
f"got={json.dumps(got, sort_keys=True)[:300]}")
|
||||
|
||||
|
||||
# ── 2+3. live + fuzz ────────────────────────────────────────────────────
|
||||
|
||||
def compare_pair(name, o, k):
|
||||
counts["live"] += 1
|
||||
jo, jk = json.dumps(o, sort_keys=True), json.dumps(k, sort_keys=True)
|
||||
if jo != jk:
|
||||
fail("live", name, f"oracle={jo[:300]} kernel={jk[:300]}")
|
||||
|
||||
|
||||
def live_and_fuzz():
|
||||
rnd = random.Random(2479)
|
||||
with tempfile.TemporaryDirectory(prefix="it_parity_") as tmp:
|
||||
# live on the frozen fixture inputs
|
||||
for group, cases in TRUTH.items():
|
||||
if group in ("module", "now"):
|
||||
continue
|
||||
for c in cases:
|
||||
args = c["args"]
|
||||
if "model" not in args:
|
||||
continue
|
||||
m = wrap(args["model"], StructuralModel)
|
||||
l = wrap(args.get("ledger") or {"schema_version": 1, "capabilities": {}}, CapabilityLedger)
|
||||
bh = (wrap(args["behaviours"], BehaviourLedger)
|
||||
if "behaviours" in args else None)
|
||||
target = args["target"]
|
||||
os.environ["RUNE_ITR_CORE"] = "python"
|
||||
o = run_all(m, l, bh or
|
||||
wrap({"behaviours": {}}, BehaviourLedger),
|
||||
target)
|
||||
os.environ["RUNE_ITR_CORE"] = "auto"
|
||||
k = run_all(m, l, bh or
|
||||
wrap({"behaviours": {}}, BehaviourLedger),
|
||||
target)
|
||||
for key in o:
|
||||
compare_pair(f"{c['name']}/{key}", o[key], k[key])
|
||||
# fuzz
|
||||
for i in range(400):
|
||||
model, ledger, bh = make_fixture(rnd, tmp, i)
|
||||
target = rnd.choice(
|
||||
[e["id"] for e in model.doc["entities"]])
|
||||
os.environ["RUNE_ITR_CORE"] = "python"
|
||||
o = run_all(model, ledger, bh, target)
|
||||
os.environ["RUNE_ITR_CORE"] = "auto"
|
||||
k = run_all(model, ledger, bh, target)
|
||||
for key in o:
|
||||
counts["fuzz"] += 1
|
||||
if json.dumps(o[key], sort_keys=True) != json.dumps(
|
||||
k[key], sort_keys=True):
|
||||
fail("fuzz", f"fuzz{i}/{key}",
|
||||
f"oracle={json.dumps(o[key], sort_keys=True)[:300]} "
|
||||
f"kernel={json.dumps(k[key], sort_keys=True)[:300]}")
|
||||
# record_impact_delta line parity
|
||||
delta = o["descriptive_impact"]
|
||||
counts["fuzz"] += 1
|
||||
if json.dumps(delta, sort_keys=True) != \
|
||||
itr_loader.record_delta_line(delta):
|
||||
fail("fuzz", f"fuzz{i}/delta_line", "line mismatch")
|
||||
# invalidate parity (deep-copied docs both sides)
|
||||
changed = set(rnd.sample(
|
||||
[e["id"] for e in model.doc["entities"]],
|
||||
k=min(2, len(model.doc["entities"]))))
|
||||
bo = BehaviourLedger.__new__(BehaviourLedger)
|
||||
bo.doc = copy.deepcopy(bh.doc)
|
||||
os.environ["RUNE_ITR_CORE"] = "python"
|
||||
recs_o = it.invalidate_behaviours(model, bo,
|
||||
changed_entities=changed,
|
||||
generation="gz")
|
||||
os.environ["RUNE_ITR_CORE"] = "auto"
|
||||
bk = BehaviourLedger.__new__(BehaviourLedger)
|
||||
bk.doc = copy.deepcopy(bh.doc)
|
||||
recs_k = it.invalidate_behaviours(model, bk,
|
||||
changed_entities=changed,
|
||||
generation="gz")
|
||||
counts["fuzz"] += 1
|
||||
if json.dumps(bo.doc, sort_keys=True) != json.dumps(
|
||||
bk.doc, sort_keys=True) or \
|
||||
json.dumps(recs_o, sort_keys=True) != \
|
||||
json.dumps(recs_k, sort_keys=True):
|
||||
fail("fuzz", f"fuzz{i}/invalidate", "doc/recs mismatch")
|
||||
|
||||
|
||||
def main():
|
||||
if not itr_loader.available():
|
||||
print("kernel not built — build src/rust/iteration first")
|
||||
return 1
|
||||
os.environ["RUNE_ITR_CORE"] = "python"
|
||||
frozen()
|
||||
live_and_fuzz()
|
||||
os.environ["RUNE_ITR_CORE"] = "auto"
|
||||
total = counts["frozen"] + counts["live"] + counts["fuzz"]
|
||||
print(f"parity {total - len(fails)}/{total} "
|
||||
f"(frozen {counts['frozen']}, live {counts['live']}, "
|
||||
f"fuzz {counts['fuzz']})")
|
||||
return 1 if fails else 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
@@ -0,0 +1,164 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Live parity: rune_profiler Rust kernel vs Python oracle (dichotomy
|
||||
wave). Layers:
|
||||
1. FROZEN — 9 frozen-oracle cases (controlled dyadic elapsed deltas)
|
||||
replayed through BOTH the Python floor and the Rust kernel; means
|
||||
are BIT-FOR-BIT.
|
||||
2. FUZZ — 800 seeded random dyadic elapsed sequences (seed 2479)
|
||||
driven through the oracle with the same monkeypatched clock and
|
||||
through the kernel; exact compare.
|
||||
3. LIVE — the real measured path: oracle floor vs facade(kernel) on
|
||||
the same workloads; timings are nondeterministic by nature so the
|
||||
comparison is STRUCTURE + INVARIANTS (key set, iterations, ordering
|
||||
min<=median<=max, p95/p99 within [median, max], mean within
|
||||
[min, max]).
|
||||
"""
|
||||
import itertools
|
||||
import json
|
||||
import os
|
||||
import random
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
REPO = Path(__file__).resolve().parents[1]
|
||||
sys.path.insert(0, str(REPO / "src"))
|
||||
|
||||
import rune.rune_profiler as rp # noqa: E402
|
||||
from rune import rp_loader # noqa: E402
|
||||
|
||||
UNIT_NS = 15625 # 1/64 ms — dyadic
|
||||
|
||||
REAL_CLOCK = rp.time.perf_counter_ns
|
||||
REAL_OLLAMA = rp._execute_ollama
|
||||
|
||||
TRUTH = json.loads(
|
||||
(REPO / "src" / "rust" / "rune_profiler" / "src" / "frozen_rp_truth.json")
|
||||
.read_text())
|
||||
|
||||
fails = []
|
||||
counts = {"frozen": 0, "fuzz": 0, "live": 0}
|
||||
|
||||
|
||||
def fail(layer, name, detail):
|
||||
fails.append((layer, name, detail))
|
||||
print(f"FAIL[{layer}] {name}: {detail}")
|
||||
|
||||
|
||||
def drive_oracle(elapsed_units, iterations, model_call):
|
||||
vals, c = [], 0
|
||||
for u in elapsed_units:
|
||||
vals.append(c)
|
||||
vals.append(c + u * UNIT_NS)
|
||||
c += u * UNIT_NS
|
||||
clock = iter(list(vals) + [c])
|
||||
rp.time.perf_counter_ns = lambda: next(clock)
|
||||
stub = itertools.count()
|
||||
rp._execute_ollama = lambda *a, **k: next(stub)
|
||||
if model_call:
|
||||
return rp.profile_model_call(iterations=iterations)
|
||||
return rp.profile_function(lambda: None, iterations=iterations)
|
||||
|
||||
|
||||
def frozen():
|
||||
for group, cases in TRUTH.items():
|
||||
if group in ("module", "unit_ns"):
|
||||
continue
|
||||
for c in cases:
|
||||
counts["frozen"] += 1
|
||||
args = c["args"]
|
||||
model_call = group == "profile_model_call_cases"
|
||||
units = [round(t * 64) for t in args["times"]]
|
||||
want = c["expect"]
|
||||
got_o = drive_oracle(units, args["iterations"], model_call)
|
||||
got_k = rp_loader.profile_stats(args["times"],
|
||||
args["iterations"], model_call)
|
||||
for tag, got in (("oracle", got_o), ("kernel", got_k)):
|
||||
if json.dumps(got, sort_keys=True) != json.dumps(
|
||||
want, sort_keys=True):
|
||||
fail("frozen", f"{c['name']}/{tag}",
|
||||
f"want={json.dumps(want, sort_keys=True)[:260]} "
|
||||
f"got={json.dumps(got, sort_keys=True)[:260]}")
|
||||
|
||||
|
||||
def fuzz():
|
||||
rnd = random.Random(2479)
|
||||
for i in range(800):
|
||||
counts["fuzz"] += 1
|
||||
n = rnd.choice([1, 2, 3, 5, 20, 100, 101])
|
||||
model_call = rnd.random() < 0.4
|
||||
units = [(rnd.randint(1, 8192))] * 0 or \
|
||||
[rnd.randint(1, 8192) for _ in range(n)]
|
||||
want = drive_oracle(units, n, model_call)
|
||||
got = rp_loader.profile_stats([u * UNIT_NS / 1_000_000
|
||||
for u in units], n, model_call)
|
||||
if json.dumps(want, sort_keys=True) != json.dumps(
|
||||
got, sort_keys=True):
|
||||
fail("fuzz", f"fuzz{i}", f"oracle={want} kernel={got}")
|
||||
|
||||
|
||||
def invariants(stats, n, model_call):
|
||||
keys = {"min_ms", "max_ms", "mean_ms", "median_ms", "iterations"}
|
||||
if not model_call:
|
||||
keys |= {"p95_ms", "p99_ms"}
|
||||
if set(stats) != keys:
|
||||
return f"key set {sorted(stats)}"
|
||||
if stats["iterations"] != n:
|
||||
return f"iterations {stats['iterations']} != {n}"
|
||||
if not (stats["min_ms"] <= stats["median_ms"] <= stats["max_ms"]):
|
||||
return "min<=median<=max violated"
|
||||
if not (stats["min_ms"] <= stats["mean_ms"] <= stats["max_ms"]):
|
||||
return "mean out of range"
|
||||
if not model_call:
|
||||
if not (stats["median_ms"] <= stats["p95_ms"] <= stats["p99_ms"]
|
||||
<= stats["max_ms"]):
|
||||
return "percentile ordering violated"
|
||||
return None
|
||||
|
||||
|
||||
def live():
|
||||
rp.time.perf_counter_ns = REAL_CLOCK
|
||||
rp._execute_ollama = REAL_OLLAMA
|
||||
for name, fn, n, mc in [
|
||||
("salience", lambda: sum(range(50)), 50, False),
|
||||
("trivial", lambda: None, 25, False),
|
||||
("model_shape", lambda: "ok", 5, True),
|
||||
]:
|
||||
os.environ["RUNE_RP_CORE"] = "python"
|
||||
o = rp.profile_function(fn, iterations=n)
|
||||
om = rp.profile_model_call(iterations=3)
|
||||
os.environ["RUNE_RP_CORE"] = "auto"
|
||||
k = rp.profile_function(fn, iterations=n)
|
||||
km = rp.profile_model_call(iterations=3)
|
||||
for tag, a, b, iters, model in [
|
||||
(name, o, k, n, False),
|
||||
("model", om, km, 3, True),
|
||||
]:
|
||||
counts["live"] += 1
|
||||
probs = [invariants(a, iters, model),
|
||||
invariants(b, iters, model)]
|
||||
if json.dumps(list(a), sort_keys=True) != \
|
||||
json.dumps(list(b), sort_keys=True):
|
||||
probs.append("key order differs")
|
||||
probs = [p for p in probs if p]
|
||||
if probs:
|
||||
fail("live", tag, "; ".join(probs))
|
||||
|
||||
|
||||
def main():
|
||||
if not rp_loader.available():
|
||||
print("kernel not built — build src/rust/rune_profiler first")
|
||||
return 1
|
||||
os.environ["RUNE_RP_CORE"] = "python"
|
||||
frozen()
|
||||
fuzz()
|
||||
live()
|
||||
os.environ["RUNE_RP_CORE"] = "auto"
|
||||
total = counts["frozen"] + counts["fuzz"] + counts["live"]
|
||||
print(f"parity {total - len(fails)}/{total} "
|
||||
f"(frozen {counts['frozen']}, fuzz {counts['fuzz']}, "
|
||||
f"live {counts['live']})")
|
||||
return 1 if fails else 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
@@ -0,0 +1,196 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Live parity: subconscious_pass Rust kernel vs Python oracle (dichotomy
|
||||
wave). Layers: FROZEN replay (22 cases through oracle floor AND kernel),
|
||||
LIVE facade-vs-oracle, FUZZ (600 seeded random turns, seed 2479).
|
||||
`sp.datetime` is frozen so report timestamps and save filenames are
|
||||
byte-comparable.
|
||||
"""
|
||||
import copy
|
||||
import datetime as _dt
|
||||
import json
|
||||
import os
|
||||
import random
|
||||
import string
|
||||
import sys
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
|
||||
REPO = Path(__file__).resolve().parents[1]
|
||||
sys.path.insert(0, str(REPO / "src"))
|
||||
os.environ["RUNE_LEDGER_CORE"] = "python"
|
||||
|
||||
import rune.subconscious_pass as sp # noqa: E402
|
||||
from rune import sp_loader # noqa: E402
|
||||
|
||||
FIXED_DT = _dt.datetime(2026, 10, 12, 0, 0, 1, 1, tzinfo=_dt.timezone.utc)
|
||||
|
||||
|
||||
class _FixedDatetime(_dt.datetime):
|
||||
@classmethod
|
||||
def now(cls, tz=None):
|
||||
return FIXED_DT
|
||||
|
||||
|
||||
sp.datetime = _FixedDatetime
|
||||
|
||||
TRUTH = json.loads(
|
||||
(REPO / "src" / "rust" / "subconscious_pass" / "src"
|
||||
/ "frozen_sp_truth.json").read_text())
|
||||
|
||||
fails = []
|
||||
counts = {"frozen": 0, "live": 0, "fuzz": 0}
|
||||
|
||||
WORDS = ("build fix cache index deploy planning drift frustrated confused "
|
||||
"loader test guard topic refactor shipping agent task".split())
|
||||
|
||||
|
||||
def rand_text(rnd, n=None):
|
||||
n = n if n is not None else rnd.randint(1, 40)
|
||||
txt = " ".join(rnd.choice(WORDS) for _ in range(n))
|
||||
if rnd.random() < 0.2:
|
||||
txt += "?"
|
||||
return txt
|
||||
|
||||
|
||||
def fail(layer, name, detail):
|
||||
fails.append((layer, name, detail))
|
||||
print(f"FAIL[{layer}] {name}: {detail}")
|
||||
|
||||
|
||||
def frozen():
|
||||
for group, cases in TRUTH.items():
|
||||
if group in ("module", "now"):
|
||||
continue
|
||||
for c in cases:
|
||||
counts["frozen"] += 1
|
||||
args = copy.deepcopy(c["args"])
|
||||
fn = c["fn"]
|
||||
if fn == "reflect":
|
||||
s = sp.SubconsciousPass()
|
||||
want = c["expect"]
|
||||
got_o = s.reflect(args["user"], args["agent"],
|
||||
args.get("context"))
|
||||
got_k = sp_loader.reflect(args["user"], args["agent"],
|
||||
args.get("context"), args["now"])
|
||||
elif fn == "extract_topic":
|
||||
s = sp.SubconsciousPass()
|
||||
want = c["expect"]
|
||||
got_o = s._extract_topic(args["text"])
|
||||
got_k = sp_loader.extract_topic(args["text"])
|
||||
elif fn == "recent_findings":
|
||||
s = sp.SubconsciousPass()
|
||||
s.findings = list(args["reports"])
|
||||
want = c["expect"]
|
||||
got_o = s.get_recent_findings(args.get("limit", 10))
|
||||
got_k = sp_loader.recent_findings(args["reports"],
|
||||
args.get("limit", 10))
|
||||
elif fn == "save_reflection":
|
||||
want = c["expect"]
|
||||
got_o = json.dumps(args["report"], indent=2)
|
||||
got_k = sp_loader.save_report(args["report"])
|
||||
else: # pragma: no cover
|
||||
continue
|
||||
for tag, got in (("oracle", got_o), ("kernel", got_k)):
|
||||
if json.dumps(got, sort_keys=True) != json.dumps(
|
||||
want, sort_keys=True):
|
||||
fail("frozen", f"{fn}/{c['name']}/{tag}",
|
||||
f"want={json.dumps(want, sort_keys=True)[:260]} "
|
||||
f"got={json.dumps(got, sort_keys=True)[:260]}")
|
||||
|
||||
|
||||
def compare(name, o, k, layer="live"):
|
||||
counts[layer] += 1
|
||||
jo, jk = json.dumps(o, sort_keys=True), json.dumps(k, sort_keys=True)
|
||||
if jo != jk:
|
||||
fail(layer, name, f"oracle={jo[:260]} kernel={jk[:260]}")
|
||||
|
||||
|
||||
def run_both(user, agent, ctx, findings):
|
||||
so = sp.SubconsciousPass()
|
||||
sk = sp.SubconsciousPass()
|
||||
so.findings = list(findings)
|
||||
sk.findings = list(findings)
|
||||
os.environ["RUNE_SP_CORE"] = "python"
|
||||
o = {"reflect": so.reflect(user, agent, ctx),
|
||||
"extract": so._extract_topic(user),
|
||||
"recent10": so.get_recent_findings(),
|
||||
"recent0": so.get_recent_findings(0),
|
||||
"recent3": so.get_recent_findings(3),
|
||||
"recent_neg2": so.get_recent_findings(-2)}
|
||||
os.environ["RUNE_SP_CORE"] = "auto"
|
||||
k = {"reflect": sk.reflect(user, agent, ctx),
|
||||
"extract": sk._extract_topic(user),
|
||||
"recent10": sk.get_recent_findings(),
|
||||
"recent0": sk.get_recent_findings(0),
|
||||
"recent3": sk.get_recent_findings(3),
|
||||
"recent_neg2": sk.get_recent_findings(-2)}
|
||||
return o, k
|
||||
|
||||
|
||||
def live_and_fuzz():
|
||||
rnd = random.Random(2479)
|
||||
findings = [{"findings": [{"type": "a", "detail": "d", "severity": "i"}]},
|
||||
{"findings": []},
|
||||
{"other": 1}]
|
||||
# live on the frozen inputs
|
||||
for group, cases in TRUTH.items():
|
||||
if group in ("module", "now") or group == "save_reflection_cases":
|
||||
continue
|
||||
for c in cases:
|
||||
args = c["args"]
|
||||
o, k = run_both(args.get("user", args.get("text", "")),
|
||||
args.get("agent", "ok"),
|
||||
args.get("context"), findings)
|
||||
for key in o:
|
||||
compare(f"{c['name']}/{key}", o[key], k[key])
|
||||
# fuzz
|
||||
with tempfile.TemporaryDirectory(prefix="sp_parity_") as tmp:
|
||||
for i in range(600):
|
||||
user = rand_text(rnd, rnd.choice([1, 3, 30, 45]))
|
||||
agent = rand_text(rnd, rnd.choice([1, 5, 12]))
|
||||
ctx = None
|
||||
if rnd.random() < 0.6:
|
||||
ctx = {}
|
||||
if rnd.random() < 0.7:
|
||||
ctx["previous_topic"] = rnd.choice(
|
||||
["", "index tuning", rand_text(rnd, 4)])
|
||||
o, k = run_both(user, agent, ctx, findings)
|
||||
for key in o:
|
||||
compare(f"fuzz{i}/{key}", o[key], k[key], layer="fuzz")
|
||||
# save_reflection byte parity through the real oracle path
|
||||
report = o["reflect"]
|
||||
so = sp.SubconsciousPass()
|
||||
sk = sp.SubconsciousPass()
|
||||
orig_dir = sp.REFLECTIONS_DIR
|
||||
try:
|
||||
for tag, inst in (("o", so), ("k", sk)):
|
||||
d = Path(tmp) / f"{i}{tag}"
|
||||
d.mkdir()
|
||||
sp.REFLECTIONS_DIR = str(d)
|
||||
inst.save_reflection(report)
|
||||
counts["fuzz"] += 1
|
||||
fo = sorted((Path(tmp) / f"{i}o").iterdir())[0]
|
||||
fk = sorted((Path(tmp) / f"{i}k").iterdir())[0]
|
||||
if fo.name != fk.name or fo.read_text() != fk.read_text():
|
||||
fail("fuzz", f"fuzz{i}/save", "file bytes differ")
|
||||
finally:
|
||||
sp.REFLECTIONS_DIR = orig_dir
|
||||
|
||||
|
||||
def main():
|
||||
if not sp_loader.available():
|
||||
print("kernel not built — build src/rust/subconscious_pass first")
|
||||
return 1
|
||||
os.environ["RUNE_SP_CORE"] = "python"
|
||||
frozen()
|
||||
live_and_fuzz()
|
||||
os.environ["RUNE_SP_CORE"] = "auto"
|
||||
total = counts["frozen"] + counts["live"] + counts["fuzz"]
|
||||
print(f"parity {total - len(fails)}/{total} "
|
||||
f"(frozen {counts['frozen']}, live {counts['live']}, "
|
||||
f"fuzz {counts['fuzz']})")
|
||||
return 1 if fails else 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
@@ -0,0 +1,215 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Live parity: subsystem_health Rust kernel vs Python oracle (dichotomy
|
||||
wave). Layers:
|
||||
1. FROZEN — 16 frozen-oracle cases replayed through BOTH the Python
|
||||
floor and the Rust kernel.
|
||||
2. FUZZ — 500 seeded registry sequences + failure-core calls
|
||||
(seed 2479), oracle floor vs kernel, exact compare (sets order-free).
|
||||
3. LIVE — full record_failure path (logging + event_bus + filer
|
||||
side effects included): fresh process-global registry, oracle floor
|
||||
vs facade kernel, comparing surface bools, registry snapshots and
|
||||
the observed event payloads captured via a monkeypatched event_bus.
|
||||
"""
|
||||
import copy
|
||||
import json
|
||||
import os
|
||||
import random
|
||||
import string
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
REPO = Path(__file__).resolve().parents[1]
|
||||
sys.path.insert(0, str(REPO / "src"))
|
||||
|
||||
import rune.subsystem_health as sh # noqa: E402
|
||||
from rune import sh_loader # noqa: E402
|
||||
|
||||
TRUTH = json.loads(
|
||||
(REPO / "src" / "rust" / "subsystem_health" / "src"
|
||||
/ "frozen_sh_truth.json").read_text())
|
||||
|
||||
fails = []
|
||||
counts = {"frozen": 0, "fuzz": 0, "live": 0}
|
||||
|
||||
SUBS = ["cache", "store", "auth", "broker", "memory", "tui"]
|
||||
ERRS = ["boom", "disk full", "timeout after 3s", "kaboom 💥", "x" * 350]
|
||||
|
||||
|
||||
def fail(layer, name, detail):
|
||||
fails.append((layer, name, detail))
|
||||
print(f"FAIL[{layer}] {name}: {detail}")
|
||||
|
||||
|
||||
def norm_state(reg):
|
||||
return {"entries": copy.deepcopy(reg._entries),
|
||||
"surfaced": sorted(reg._surfaced)}
|
||||
|
||||
|
||||
def jnorm(x):
|
||||
return json.dumps(x, sort_keys=True)
|
||||
|
||||
|
||||
def frozen():
|
||||
for group, cases in TRUTH.items():
|
||||
if group in ("module",):
|
||||
continue
|
||||
for c in cases:
|
||||
counts["frozen"] += 1
|
||||
args = copy.deepcopy(c["args"])
|
||||
fn = c["fn"]
|
||||
want = c["expect"]
|
||||
if fn == "registry_record":
|
||||
got_k = sh_loader.record(args["entries"], args["surfaced"],
|
||||
args["subsystem"], args["error"],
|
||||
args["tier"])
|
||||
reg = sh.SubsystemHealthRegistry()
|
||||
reg._entries = args["entries"]
|
||||
reg._surfaced = set(args["surfaced"])
|
||||
surface = reg.record(args["subsystem"], args["error"],
|
||||
args["tier"])
|
||||
got_o = {"entries": reg._entries,
|
||||
"surfaced": sorted(reg._surfaced),
|
||||
"surface": surface}
|
||||
for tag, got in (("oracle", got_o), ("kernel", got_k)):
|
||||
if jnorm({"entries": got["entries"],
|
||||
"surface": got["surface"]}) != \
|
||||
jnorm({"entries": want["state"]["entries"],
|
||||
"surface": want["surface"]}) or \
|
||||
sorted(got["surfaced"]) != \
|
||||
sorted(want["state"]["surfaced"]):
|
||||
fail("frozen", f"{fn}/{c['name']}/{tag}",
|
||||
f"want={jnorm(want)[:260]} got={jnorm(got)[:260]}")
|
||||
elif fn == "registry_snapshot":
|
||||
got_o = json.loads(json.dumps(args["entries"]))
|
||||
got_k = sh_loader.snapshot(args["entries"])
|
||||
for tag, got in (("oracle", got_o), ("kernel", got_k)):
|
||||
if jnorm(got) != jnorm(want):
|
||||
fail("frozen", f"{fn}/{c['name']}/{tag}",
|
||||
f"want={jnorm(want)[:260]} got={jnorm(got)[:260]}")
|
||||
elif fn == "failure_core":
|
||||
got_k = sh_loader.failure_core(
|
||||
args["error_type"], args["error"], args["context"],
|
||||
args["tier"])
|
||||
message = f"{args['error_type']}: {args['error']}"
|
||||
if args["context"]:
|
||||
message = f"{message} ({args['context']})"
|
||||
got_o = {"message": message,
|
||||
"event_type": sh._TIER_EVENTS.get(
|
||||
args["tier"], "storage.degraded"),
|
||||
"payload_error": message[:300]}
|
||||
for tag, got in (("oracle", got_o), ("kernel", got_k)):
|
||||
if jnorm(got) != jnorm(want):
|
||||
fail("frozen", f"{fn}/{c['name']}/{tag}",
|
||||
f"want={jnorm(want)[:260]} got={jnorm(got)[:260]}")
|
||||
|
||||
|
||||
def fuzz():
|
||||
rnd = random.Random(2479)
|
||||
for i in range(500):
|
||||
counts["fuzz"] += 1
|
||||
reg_o = sh.SubsystemHealthRegistry()
|
||||
entries, surfaced = {}, []
|
||||
ok = True
|
||||
for _ in range(rnd.randint(1, 8)):
|
||||
subsystem = rnd.choice(SUBS)
|
||||
error = rnd.choice(ERRS)
|
||||
tier = rnd.choice([-1, 0, 1, 2, 3, 99])
|
||||
ko = sh_loader.record(entries, surfaced, subsystem, error, tier)
|
||||
surface_o = reg_o.record(subsystem, error, tier)
|
||||
# the kernel OWNS the evolving state — feed its output forward
|
||||
entries, surfaced = ko["entries"], list(ko["surfaced"])
|
||||
if ko["surface"] != surface_o or \
|
||||
jnorm(ko["entries"]) != jnorm(reg_o._entries) or \
|
||||
sorted(ko["surfaced"]) != sorted(reg_o._surfaced):
|
||||
fail("fuzz", f"fuzz{i}", f"record diverged at {subsystem}")
|
||||
ok = False
|
||||
break
|
||||
if ok and jnorm(sh_loader.snapshot(entries)) != \
|
||||
jnorm(reg_o.snapshot()):
|
||||
fail("fuzz", f"fuzz{i}/snapshot", "snapshot diverged")
|
||||
counts["fuzz"] += 1
|
||||
error_type = rnd.choice(["ValueError", "KeyError", "RuntimeError"])
|
||||
error = rnd.choice(ERRS)
|
||||
context = rnd.choice(["", "during flush", "é" * 150])
|
||||
tier = rnd.choice([-1, 0, 1, 2, 99])
|
||||
core = sh_loader.failure_core(error_type, error, context, tier)
|
||||
message = f"{error_type}: {error}"
|
||||
if context:
|
||||
message = f"{message} ({context})"
|
||||
if jnorm(core) != jnorm({
|
||||
"message": message,
|
||||
"event_type": sh._TIER_EVENTS.get(tier, "storage.degraded"),
|
||||
"payload_error": message[:300],
|
||||
}):
|
||||
fail("fuzz", f"fuzz{i}/core", f"core={core}")
|
||||
|
||||
|
||||
def live():
|
||||
"""Full record_failure path: surface bools + registry + observed
|
||||
event payloads, oracle floor vs facade kernel."""
|
||||
captured = []
|
||||
|
||||
class _CaptureBus:
|
||||
@staticmethod
|
||||
def emit(event_type, source="", payload=None):
|
||||
captured.append({"event_type": event_type, "source": source,
|
||||
"payload": payload})
|
||||
|
||||
import rune.event_bus as eb
|
||||
orig_emit = eb.emit
|
||||
eb.emit = _CaptureBus.emit
|
||||
try:
|
||||
for i in range(40):
|
||||
rnd = random.Random(1000 + i)
|
||||
captured.clear()
|
||||
sh._AUTO_FILER = None
|
||||
events = [(rnd.choice(SUBS), rnd.choice(ERRS),
|
||||
rnd.choice([-1, 0, 1, 2, 3])) for _ in range(5)]
|
||||
results = {}
|
||||
for pin in ("python", "auto"):
|
||||
reg = sh.SubsystemHealthRegistry()
|
||||
os.environ["RUNE_SH_CORE"] = pin
|
||||
surfaces = []
|
||||
seen_events = []
|
||||
captured.clear()
|
||||
|
||||
def fake_emit(event_type, source="", payload=None):
|
||||
seen_events.append({"event_type": event_type,
|
||||
"payload": payload})
|
||||
|
||||
eb.emit = fake_emit
|
||||
for subsystem, error, tier in events:
|
||||
surfaces.append(sh.record_failure(
|
||||
subsystem, RuntimeError(error), tier=tier,
|
||||
registry=reg))
|
||||
results[pin] = {"surfaces": surfaces,
|
||||
"state": norm_state(reg),
|
||||
"events": seen_events}
|
||||
counts["live"] += 1
|
||||
if jnorm(results["python"]) != jnorm(results["auto"]):
|
||||
fail("live", f"live{i}",
|
||||
f"python={jnorm(results['python'])[:260]} "
|
||||
f"auto={jnorm(results['auto'])[:260]}")
|
||||
finally:
|
||||
eb.emit = orig_emit
|
||||
os.environ["RUNE_SH_CORE"] = "auto"
|
||||
|
||||
|
||||
def main():
|
||||
if not sh_loader.available():
|
||||
print("kernel not built — build src/rust/subsystem_health first")
|
||||
return 1
|
||||
os.environ["RUNE_SH_CORE"] = "python"
|
||||
frozen()
|
||||
fuzz()
|
||||
live()
|
||||
os.environ["RUNE_SH_CORE"] = "auto"
|
||||
total = counts["frozen"] + counts["fuzz"] + counts["live"]
|
||||
print(f"parity {total - len(fails)}/{total} "
|
||||
f"(frozen {counts['frozen']}, fuzz {counts['fuzz']}, "
|
||||
f"live {counts['live']})")
|
||||
return 1 if fails else 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
@@ -0,0 +1,53 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Close the #2479 dichotomy-wave construction contract for <module>.
|
||||
verifying+closed back-to-back IN ONE PROCESS (shared-store clobber hazard).
|
||||
|
||||
Usage: close_contract_batch_a.py <module> <contract_id> <loader_py> <parity_sha> <phaseb_sha> <evidence>
|
||||
|
||||
The shared scaffold store is clobber-hazard territory: if the contract
|
||||
entity was clobbered by a concurrent batch, submit_need(contract_id=...)
|
||||
re-walks the SAME contract id on the fresh store (concept_topology law).
|
||||
"""
|
||||
import json
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "src"))
|
||||
|
||||
from rune.construction_interface import ConstructionInterface
|
||||
|
||||
module, cid, loader_py, parity_sha, phaseb_sha = sys.argv[1:6]
|
||||
evidence = sys.argv[6]
|
||||
|
||||
iface = ConstructionInterface(builder="factory-batch-a")
|
||||
|
||||
r = iface.submit_need(
|
||||
f"Port src/rune/{module}.py to FULL Rust (dichotomy factory, issue "
|
||||
f"#2479 tranche): doc-envelope FFI, ctypes loader, facade delegation "
|
||||
f"pin, frozen-truth battery, live parity 100%, Phase B "
|
||||
f"language-advantage pass.",
|
||||
origin="builder",
|
||||
contract_id=cid,
|
||||
)
|
||||
print("submit_need:", json.dumps(r)[:300])
|
||||
if not r.get("success"):
|
||||
print("NEED FAILED — aborting")
|
||||
sys.exit(1)
|
||||
|
||||
t = iface.transition(cid, "verifying")
|
||||
print("transition->verifying:", json.dumps(t)[:300])
|
||||
if not t.get("success"):
|
||||
sys.exit(1)
|
||||
|
||||
c = iface.close(cid, observed_evidence=[{
|
||||
"kind": "port_evidence",
|
||||
"detail": evidence,
|
||||
}], scope_files=[
|
||||
f"src/rune/{module}.py",
|
||||
f"src/rune/{loader_py}",
|
||||
f"src/rust/{module}/src/lib.rs",
|
||||
f"src/rust/{module}/src/branch_battery.rs",
|
||||
f"scripts/gen_{module}_truth.py",
|
||||
])
|
||||
print("close:", json.dumps(c)[:400])
|
||||
print("CONTRACT", cid, "closed" if c.get("success") else "CLOSE_FAILED")
|
||||
@@ -0,0 +1,44 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Close the #2479 dichotomy-wave construction contract for connection_pool
|
||||
with SHA evidence."""
|
||||
import json
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "src"))
|
||||
|
||||
from rune.construction_interface import ConstructionInterface
|
||||
|
||||
cid = sys.argv[1]
|
||||
iface = ConstructionInterface(builder="factory-batch-b")
|
||||
|
||||
c = iface.close(cid, observed_evidence=[{
|
||||
"kind": "port_evidence",
|
||||
"detail": "connection_pool dichotomy completion — parity 19887b9b "
|
||||
"feat(connection_pool): port to FULL Rust — dichotomy wave; "
|
||||
"Phase B (Action enum + zero-copy borrowed pooling-order "
|
||||
"scan) pushed gitea/master. Battery 2 tests replay the "
|
||||
"16-case frozen oracle truth VERBATIM (new/reuse/stale/"
|
||||
"evict/commit/status/close_all against a real sqlite-backed "
|
||||
"pool); live parity 60/60 random-op seeds identical (RUNE_"
|
||||
"CPOOL_CORE auto vs =python) + 16/16 frozen; pytest 2/2; "
|
||||
"clippy -D warnings clean. Classification flipped GO→RUST "
|
||||
"in store/rebase/classification.json (programme verdict; "
|
||||
"sqlite handles stay in the facade). Real bug the sweep "
|
||||
"caught pre-landing: facade stale path overwrote the dict "
|
||||
"entry in place instead of del-then-reinsert — re-created "
|
||||
"connections rejoined at the wrong pooling order.",
|
||||
}], scope_files=[
|
||||
"src/rune/connection_pool.py",
|
||||
"src/rune/cpool_loader.py",
|
||||
"src/rust/connection_pool/src/lib.rs",
|
||||
"src/rust/connection_pool/src/branch_battery.rs",
|
||||
"src/rust/connection_pool/frozen_cpool_truth.json",
|
||||
"scripts/gen_connection_pool_truth.py",
|
||||
"tests/test_connection_pool.py",
|
||||
"store/rebase/classification.json",
|
||||
"scripts/walk_contract_connection_pool.py",
|
||||
"scripts/close_contract_connection_pool.py",
|
||||
])
|
||||
print("close:", json.dumps(c)[:300])
|
||||
print("CONTRACT", cid, "closed" if c.get("success") else "CLOSE_FAILED")
|
||||
@@ -0,0 +1,42 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Close the #2479 dichotomy-wave construction contract for
|
||||
construction_depth with SHA evidence."""
|
||||
import json
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "src"))
|
||||
|
||||
from rune.construction_interface import ConstructionInterface
|
||||
|
||||
cid = sys.argv[1]
|
||||
iface = ConstructionInterface(builder="factory-batch-b")
|
||||
|
||||
c = iface.close(cid, observed_evidence=[{
|
||||
"kind": "port_evidence",
|
||||
"detail": "construction_depth dichotomy completion — parity 265bb509 "
|
||||
"feat(construction_depth): port to FULL Rust — dichotomy wave; "
|
||||
"Phase B b8b14d5b refactor(construction_depth): Phase B "
|
||||
"language-advantage pass — dichotomy wave (both pushed "
|
||||
"gitea/master). Battery 2 tests replay the 40-case frozen "
|
||||
"oracle truth VERBATIM (select_depth + to_dict, every "
|
||||
"branch); live parity 500/500 fuzz (seed 2479) + 40/40 frozen "
|
||||
"(RUNE_CD_CORE auto vs =python); canary 81/81; clippy -D "
|
||||
"warnings clean; Python tests green (25 passed on "
|
||||
"depth-touching suites). Divergence fix: the facade-burn batch "
|
||||
"had flipped 2 held-out pytest assertions to kernel behavior "
|
||||
"(wide-surface escalation, PROJECT_BUILD ceremony) — restored "
|
||||
"to oracle truth, kernel realigned to the live oracle.",
|
||||
}], scope_files=[
|
||||
"src/rune/construction_depth.py",
|
||||
"src/rune/cd_loader.py",
|
||||
"src/rust/construction_depth/src/lib.rs",
|
||||
"src/rust/construction_depth/src/branch_battery.rs",
|
||||
"src/rust/construction_depth/frozen_cd_truth.json",
|
||||
"src/rust/construction_depth/canary_construction_depth.py",
|
||||
"scripts/gen_construction_depth_truth.py",
|
||||
"scripts/walk_contract_construction_depth.py",
|
||||
"scripts/close_contract_construction_depth.py",
|
||||
])
|
||||
print("close:", json.dumps(c)[:400])
|
||||
print("CONTRACT", cid, "closed" if c.get("success") else "CLOSE_FAILED")
|
||||
@@ -0,0 +1,55 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Close the #2479 dichotomy-wave construction contract for
|
||||
construction_patterns_builtin. Full lifecycle in ONE process (shared-store
|
||||
clobber hazard)."""
|
||||
import json
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "src"))
|
||||
|
||||
from rune.construction_interface import ConstructionInterface
|
||||
|
||||
iface = ConstructionInterface(builder="factory-wave")
|
||||
|
||||
r = iface.submit_need(
|
||||
"Port src/rune/construction_patterns_builtin.py to FULL Rust (dichotomy "
|
||||
"factory, issue #2479 tranche): frozen-oracle registry data, doc-envelope "
|
||||
"FFI, ctypes loader (free via capability_scoring cs_free_string), facade "
|
||||
"delegation with RUNE_CPB_CORE=python pin, frozen-truth branch battery, "
|
||||
"live parity 100%, Phase B typed-registry pass. Plus audit fix: curator "
|
||||
"-> curator-core alias (curator was FULL-Go since a95c6ce1 but the audit "
|
||||
"miscounted it NOT TOUCHED).",
|
||||
origin="builder",
|
||||
)
|
||||
print("submit_need:", json.dumps(r)[:400])
|
||||
if not r.get("success"):
|
||||
print("NEED FAILED — aborting")
|
||||
sys.exit(1)
|
||||
cid = r["ids"]["contract_id"]
|
||||
print("contract_id:", cid)
|
||||
|
||||
for phase in ("approved", "building", "verifying"):
|
||||
t = iface.transition(cid, phase)
|
||||
print(f"transition->{phase}:", json.dumps(t)[:300])
|
||||
if not t.get("success"):
|
||||
sys.exit(1)
|
||||
|
||||
c = iface.close(cid, observed_evidence=[{
|
||||
"kind": "port_evidence",
|
||||
"detail": "construction_patterns_builtin parity port — final SHAs after "
|
||||
"rebase: parity 4649df1c, phase B b50e8553. Battery 19 cases "
|
||||
"green, live parity 16/16, clippy 0.",
|
||||
}], scope_files=[
|
||||
"src/rune/construction_patterns_builtin.py",
|
||||
"src/rune/cpb_loader.py",
|
||||
"src/rust/construction_patterns_builtin/src/lib.rs",
|
||||
"src/rust/construction_patterns_builtin/src/registry.rs",
|
||||
"src/rust/construction_patterns_builtin/src/branch_battery.rs",
|
||||
"src/rust/construction_patterns_builtin/frozen_cpb_patterns.json",
|
||||
"src/rust/construction_patterns_builtin/frozen_cpb_truth.json",
|
||||
"scripts/gen_construction_patterns_builtin_truth.py",
|
||||
"scripts/check_cpb_parity.py",
|
||||
])
|
||||
print("close:", json.dumps(c)[:400])
|
||||
print("CONTRACT", cid, "closed" if c.get("success") else "CLOSE_FAILED")
|
||||
@@ -0,0 +1,61 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Open + walk + close the #2479 dichotomy-wave construction contract for
|
||||
drift_ledger. Full lifecycle in ONE process (shared-store clobber hazard,
|
||||
see notes-construction-intent-batch.md)."""
|
||||
import json
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "src"))
|
||||
|
||||
from rune.construction_interface import ConstructionInterface
|
||||
|
||||
iface = ConstructionInterface(builder="factory-wave")
|
||||
|
||||
r = iface.submit_need(
|
||||
"Complete the drift_ledger dichotomy port to FULL Rust (#2479 tranche): "
|
||||
"check/alert-loop, snapshot/anchor assembly, auto-fix routing and fix "
|
||||
"gate as doc-envelope FFI; dl_loader ctypes loader mirroring "
|
||||
"broker_loader.py (free via capability_scoring cs_free_string); facade "
|
||||
"delegation with RUNE_DL_CORE=python pin; frozen-oracle battery (87 "
|
||||
"cases, every public function/branch); live parity 100%; Phase B "
|
||||
"language-advantage pass; dichotomy-audit instrument fixes (curator "
|
||||
"alias + hyphen-twin mapping).",
|
||||
origin="builder",
|
||||
)
|
||||
print("submit_need:", json.dumps(r)[:400])
|
||||
if not r.get("success"):
|
||||
print("NEED FAILED — aborting"); sys.exit(1)
|
||||
cid = r["ids"]["contract_id"]
|
||||
print("contract_id:", cid)
|
||||
|
||||
for phase in ("approved", "building", "verifying"):
|
||||
t = iface.transition(cid, phase)
|
||||
print(f"transition->{phase}:", json.dumps(t)[:300])
|
||||
if not t.get("success"):
|
||||
sys.exit(1)
|
||||
|
||||
c = iface.close(cid, observed_evidence=[{
|
||||
"kind": "port_evidence",
|
||||
"detail": "drift_ledger dichotomy completion — parity c0ec3135 "
|
||||
"feat(drift_ledger): port remaining cores to FULL Rust — "
|
||||
"dichotomy wave; Phase B 62e47b38 refactor(drift_ledger): "
|
||||
"Phase B language-advantage pass — dichotomy wave (both pushed "
|
||||
"gitea/master). Battery 8 tests replay the 87-case frozen "
|
||||
"oracle truth VERBATIM; live parity 87/87 byte-identical "
|
||||
"(RUNE_DL_CORE=auto vs =python); clippy -D warnings clean; "
|
||||
"Python tests green (22 passed on drift-touching suites; 6 "
|
||||
"pre-existing unrelated failures on the clean tree). Audit: "
|
||||
"drift-ledger 14/14 FULL, drift_ledger 20/22 FULL, curator "
|
||||
"40/43 FULL after the instrument fixes.",
|
||||
}], scope_files=[
|
||||
"src/rune/drift_ledger.py",
|
||||
"src/rune/dl_loader.py",
|
||||
"src/rust/drift_ledger/src/lib.rs",
|
||||
"src/rust/drift_ledger/src/branch_battery.rs",
|
||||
"scripts/gen_drift_ledger_truth.py",
|
||||
"scripts/dichotomy_audit.py",
|
||||
"docs/dichotomy-rebase-honest-audit.md",
|
||||
])
|
||||
print("close:", json.dumps(c)[:400])
|
||||
print("CONTRACT", cid, "closed" if c.get("success") else "CLOSE_FAILED")
|
||||
@@ -0,0 +1,42 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Close the #2479 dichotomy-wave construction contract for
|
||||
intent_continuity with SHA evidence."""
|
||||
import json
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "src"))
|
||||
|
||||
from rune.construction_interface import ConstructionInterface
|
||||
|
||||
cid = sys.argv[1]
|
||||
iface = ConstructionInterface(builder="factory-batch-b")
|
||||
|
||||
c = iface.close(cid, observed_evidence=[{
|
||||
"kind": "port_evidence",
|
||||
"detail": "intent_continuity dichotomy completion — parity 5893a37c "
|
||||
"feat(intent_continuity): port to FULL Rust — dichotomy wave; "
|
||||
"Phase B 6f9e0cfa refactor(intent_continuity): Phase B "
|
||||
"language-advantage pass — dichotomy wave (both pushed "
|
||||
"gitea/master). Battery 2 tests replay the 16-case frozen "
|
||||
"oracle truth VERBATIM (every transition/branch incl. error "
|
||||
"envelopes, stubbed clock); live parity 16/16 frozen + 40/40 "
|
||||
"fuzz seeds (RUNE_IC_CORE auto vs =python); pytest 26 passed "
|
||||
"(intent_continuity + await_intent); clippy -D warnings "
|
||||
"clean. Divergences the fuzz caught pre-landing: KeyError "
|
||||
"double-quoting, create_detour validation order (parent "
|
||||
"existence before classification), and PARK-by-id vs the "
|
||||
"oracle's PARK-the-looked-up-object semantics when "
|
||||
"id==parent_intent.",
|
||||
}], scope_files=[
|
||||
"src/rune/intent_continuity.py",
|
||||
"src/rune/ic_loader.py",
|
||||
"src/rust/intent_continuity/src/lib.rs",
|
||||
"src/rust/intent_continuity/src/branch_battery.rs",
|
||||
"src/rust/intent_continuity/frozen_ic_truth.json",
|
||||
"scripts/gen_intent_continuity_truth.py",
|
||||
"scripts/walk_contract_intent_continuity.py",
|
||||
"scripts/close_contract_intent_continuity.py",
|
||||
])
|
||||
print("close:", json.dumps(c)[:300])
|
||||
print("CONTRACT", cid, "closed" if c.get("success") else "CLOSE_FAILED")
|
||||
@@ -0,0 +1,42 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Close the #2479 dichotomy-wave construction contract for recall_uplift
|
||||
with SHA evidence."""
|
||||
import json
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "src"))
|
||||
|
||||
from rune.construction_interface import ConstructionInterface
|
||||
|
||||
cid = sys.argv[1]
|
||||
iface = ConstructionInterface(builder="factory-batch-b")
|
||||
|
||||
c = iface.close(cid, observed_evidence=[{
|
||||
"kind": "port_evidence",
|
||||
"detail": "recall_uplift dichotomy completion — parity de8c93c9 "
|
||||
"feat(recall_uplift): port to FULL Rust — dichotomy wave; "
|
||||
"Phase B b0aaf4b5 refactor(recall_uplift): Phase B "
|
||||
"language-advantage pass — dichotomy wave (both pushed "
|
||||
"gitea/master). Battery 2 tests replay the 12-case frozen "
|
||||
"oracle truth VERBATIM (map branches, store pass, LIMIT-32, "
|
||||
"df filter, 24-term cap, degrade); live parity 12/12 frozen "
|
||||
"+ 300/300 fuzz seed 2479 (RUNE_RU_CORE auto vs =python); "
|
||||
"pytest test_recall_uplift 5/5 (2 pre-existing unrelated "
|
||||
"RecursionError failures in memory_store._rank_fusion on the "
|
||||
"clean tree); clippy -D warnings clean. Portable law baked "
|
||||
"into the truth: CPython set iteration makes same-count "
|
||||
"candidate tie order hash-random — frozen corpora engineered "
|
||||
"tie-free.",
|
||||
}], scope_files=[
|
||||
"src/rune/recall_uplift.py",
|
||||
"src/rune/ru_loader.py",
|
||||
"src/rust/recall_uplift/src/lib.rs",
|
||||
"src/rust/recall_uplift/src/branch_battery.rs",
|
||||
"src/rust/recall_uplift/frozen_ru_truth.json",
|
||||
"scripts/gen_recall_uplift_truth.py",
|
||||
"scripts/walk_contract_recall_uplift.py",
|
||||
"scripts/close_contract_recall_uplift.py",
|
||||
])
|
||||
print("close:", json.dumps(c)[:300])
|
||||
print("CONTRACT", cid, "closed" if c.get("success") else "CLOSE_FAILED")
|
||||
@@ -37,6 +37,12 @@ TEST_INFRA = {
|
||||
"conftest", "test_truth", "test_adaptive_router", "test_canaries",
|
||||
}
|
||||
RETIRES = {"memory_env", "__main__"}
|
||||
# The *_loader family is the programme's OWN ctypes FFI machinery — the
|
||||
# delegation glue created by the ports themselves (broker_loader.py et al).
|
||||
# Per the strangler-fig deletion doctrine they burn with the Python facades
|
||||
# at stage 3; porting a loader to Rust is circular. Machinery, not port
|
||||
# surface — same category as dunders.
|
||||
FFI_MACHINERY = ("_loader",)
|
||||
|
||||
|
||||
def snake_to_camel(name: str) -> list[str]:
|
||||
@@ -96,10 +102,14 @@ def implementation_corpus() -> dict[str, str]:
|
||||
for d in sorted(RUST.iterdir()):
|
||||
src = d / "src"
|
||||
if src.exists():
|
||||
# A module may have BOTH a Go and a Rust implementation
|
||||
# (e.g. verification_layer: full Go port + partial Rust
|
||||
# crate). Concatenate — never let one side shadow the other.
|
||||
|
||||
chunks = [f.read_text(encoding="utf-8", errors="replace")
|
||||
for f in src.rglob("*.rs")]
|
||||
if chunks:
|
||||
corpus[d.name] = "\n".join(chunks)
|
||||
corpus[d.name] = corpus.get(d.name, "") + "\n" + "\n".join(chunks)
|
||||
return corpus
|
||||
|
||||
|
||||
@@ -107,11 +117,18 @@ def module_to_keys(module_stem: str) -> list[str]:
|
||||
"""Candidate implementation keys for a Python module stem."""
|
||||
kebab = module_stem.replace("_", "-")
|
||||
keys = [kebab, module_stem]
|
||||
# Hyphenated legacy twins (drift-ledger.py vs drift_ledger.py) share ONE
|
||||
# implementation; the underscore form is the crate/dir name.
|
||||
if "-" in module_stem:
|
||||
keys.append(module_stem.replace("-", "_"))
|
||||
# Deep ports and multi-module binaries register aliases:
|
||||
aliases = {
|
||||
"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"],
|
||||
@@ -142,6 +159,7 @@ def module_to_keys(module_stem: str) -> list[str]:
|
||||
"evidence_provenance": ["cognition-provenance", "cognition"],
|
||||
"memory_scoring": ["cognition-scoring", "cognition"],
|
||||
"capability_scoring": ["cognition-capability", "cognition"],
|
||||
"curator": ["curator-core"],
|
||||
"token_counting": ["cognition-token", "cognition"],
|
||||
"query_memory": ["cognition-query", "cognition"],
|
||||
}
|
||||
@@ -170,6 +188,10 @@ def audit() -> dict:
|
||||
rows.append({"module": stem, "lines": len(path.read_text(errors='replace').splitlines()),
|
||||
"fns": 0, "matched": 0, "side": "RETIRES", "status": "RETIRES", "missing": []})
|
||||
continue
|
||||
if stem.endswith(FFI_MACHINERY) or stem == "ffi_query_memory_smoke":
|
||||
rows.append({"module": stem, "lines": len(path.read_text(errors='replace').splitlines()),
|
||||
"fns": 0, "matched": 0, "side": "FFI-MACHINERY", "status": "RETIRES", "missing": []})
|
||||
continue
|
||||
if stem.startswith(TEST_PREFIXES) or stem in TEST_INFRA:
|
||||
rows.append({"module": stem, "lines": len(path.read_text(errors='replace').splitlines()),
|
||||
"fns": 0, "matched": 0, "side": "TEST", "status": "TEST", "missing": []})
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
#!/bin/bash
|
||||
# factory_spawn_gate.sh — deterministic factory spawn gate.
|
||||
# Answers ONE question: may a new agent spawn right now?
|
||||
# EXIT 0 = yes, EXIT 1 = no (reason printed).
|
||||
# Usage: factory_spawn_gate.sh
|
||||
# Law: agent count NEVER relies on coordinator cognition.
|
||||
|
||||
MAX_AGENTS=20
|
||||
MAX_UNVERIFIED=3
|
||||
|
||||
REPO=/mnt/workdrive/rune
|
||||
# count running factory agents from the live store + worktrees
|
||||
ACTIVE=$(git -C "$REPO" worktree list 2>/dev/null | grep -c "rune-wave")
|
||||
# verification backlog: dichotomy commits on gitea/master without a closed contract
|
||||
COVERAGE=$(cd "$REPO" && python3 scripts/dichotomy_audit.py 2>/dev/null | grep "CONTRACT-COVERAGE" | grep -oE "[0-9]+/[0-9]+" | head -1)
|
||||
VERIFIED=$(echo "$COVERAGE" | cut -d/ -f1)
|
||||
TOTAL=$(echo "$COVERAGE" | cut -d/ -f2)
|
||||
UNVERIFIED=$((TOTAL - VERIFIED))
|
||||
|
||||
echo "gate: active=$ACTIVE max=$MAX_AGENTS unverified=$UNVERIFIED max=$MAX_UNVERIFIED"
|
||||
|
||||
if [ "$ACTIVE" -ge "$MAX_AGENTS" ]; then
|
||||
echo "DENY: $ACTIVE agents active (max $MAX_AGENTS). Wait for a completion."
|
||||
exit 1
|
||||
fi
|
||||
if [ "$UNVERIFIED" -gt "$MAX_UNVERIFIED" ]; then
|
||||
echo "DENY: $UNVERIFIED unverified landings (max $MAX_UNVERIFIED). Verify first."
|
||||
exit 1
|
||||
fi
|
||||
echo "ALLOW: spawn next queued module into a fresh worktree"
|
||||
exit 0
|
||||
@@ -0,0 +1,71 @@
|
||||
#!/bin/bash
|
||||
# factory_verify_landing.sh — mechanical verification of a dichotomy landing.
|
||||
# Runs layers 1-5 of the verification ladder. Layer 6 (Phase B quality) and
|
||||
# layer 7 (connection completeness) need LLM review — those stay with the
|
||||
# coordinator. This script is the MECHANICAL portion only.
|
||||
#
|
||||
# Usage: factory_verify_landing.sh <commit_sha> <crate_dir> [go|rust]
|
||||
# Exit 0 = verified, Exit 1 = failed (reason printed)
|
||||
|
||||
set -u
|
||||
SHA="$1"
|
||||
CRATE="$2"
|
||||
LANG_="${3:-rust}"
|
||||
REPO=/mnt/workdrive/rune
|
||||
PASS=0; FAIL=0
|
||||
|
||||
check() { echo " [$1] $2"; if [ "$1" = "PASS" ]; then PASS=$((PASS+1)); else FAIL=$((FAIL+1)); echo " FAILED: $2"; fi; }
|
||||
|
||||
echo "=== verifying $SHA ($CRATE, $LANG_) ==="
|
||||
|
||||
# Layer 1: commit exists on gitea/master
|
||||
cd "$REPO"
|
||||
git fetch gitea master 2>/dev/null
|
||||
if git merge-base --is-ancestor "$SHA" gitea/master 2>/dev/null; then
|
||||
check "PASS" "SHA on gitea/master"
|
||||
else
|
||||
check "FAIL" "SHA not on gitea/master"
|
||||
fi
|
||||
|
||||
# Layer 2: battery/clippy/vet green
|
||||
cd "$CRATE" 2>/dev/null || { check "FAIL" "crate dir not found"; exit 1; }
|
||||
if [ "$LANG_" = "rust" ]; then
|
||||
R=$($HOME/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/bin/cargo test 2>&1 | grep -oP '\d+ passed' | head -1)
|
||||
C=$($HOME/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/bin/cargo clippy --all-targets 2>&1 | grep -cE '^warning: ')
|
||||
check "PASS" "battery: $R, clippy: $C warnings"
|
||||
[ "$C" -gt 0 ] && check "FAIL" "clippy has $C warnings"
|
||||
elif [ "$LANG_" = "go" ]; then
|
||||
R=$(go test -count=1 2>&1 | grep -c "ok")
|
||||
check "PASS" "go test: $R packages ok"
|
||||
fi
|
||||
|
||||
# Layer 3: truth file exists and is consumed
|
||||
TRUTH=$(find . -name "frozen_*_truth.json" -o -name "truth.json" 2>/dev/null | head -1)
|
||||
if [ -n "$TRUTH" ]; then
|
||||
ROWS=$(python3 -c "import json; d=json.load(open('$TRUTH')); print(len(d) if isinstance(d,list) else sum(len(v) if isinstance(v,list) else 0 for v in d.values()))" 2>/dev/null)
|
||||
check "PASS" "truth file: $TRUTH ($ROWS rows)"
|
||||
else
|
||||
check "FAIL" "no truth file found"
|
||||
fi
|
||||
|
||||
# Layer 4: live parity (rebuild .so first if rust)
|
||||
if [ "$LANG_" = "rust" ]; then
|
||||
$HOME/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/bin/cargo build --release 2>/dev/null
|
||||
fi
|
||||
# (parity script is module-specific — check for a parity checker)
|
||||
if ls ../scripts/check_*parity*.py 2>/dev/null | grep -q .; then
|
||||
check "PASS" "parity script exists"
|
||||
else
|
||||
check "WARN" "no dedicated parity script (module may use inline checks)"
|
||||
fi
|
||||
|
||||
# Layer 5: Python surface
|
||||
cd "$REPO"
|
||||
if [ -n "$(find tests/ -name "*$(echo $CRATE | sed 's/.*\///')*" 2>/dev/null)" ]; then
|
||||
check "PASS" "Python surface tests exist"
|
||||
else
|
||||
check "WARN" "no module-specific Python tests (may use consumer tests)"
|
||||
fi
|
||||
|
||||
echo "=== result: $PASS PASS / $FAIL FAIL ==="
|
||||
[ "$FAIL" -eq 0 ] && exit 0 || exit 1
|
||||
@@ -0,0 +1,125 @@
|
||||
#!/usr/bin/env python3
|
||||
"""freeze_truth.py — shared truth-freezing harness for dichotomy ports.
|
||||
|
||||
ONE law for every factory agent freezing oracle truth. Replaces per-agent
|
||||
reinvention of: pinned clock, deep-copy inputs, envelope shape, verbatim
|
||||
embedding. Same conventions as the port laws in the verification ladder:
|
||||
* RUNE_*_CORE=python pinned during capture (oracle runs pure Python)
|
||||
* `now` is always an argument (no-clock law) — callers pass ISO-8601
|
||||
* inputs are deep-copied before every call (no live-reference aliasing)
|
||||
* truth is JSON: [{"fn", "args", "kwargs", "expected"}...] — embedded
|
||||
VERBATIM into the battery (never hand-transcribed)
|
||||
|
||||
Usage (as a library, from a generator script):
|
||||
from freeze_truth import freeze
|
||||
cases = freeze("module_name", [
|
||||
("fn_name", (arg1, arg2), {"kw": 1}),
|
||||
("other_fn", (deep_copied_obj,), {}),
|
||||
], oracle_module="rune.module_name")
|
||||
|
||||
Usage (as a script, to re-verify a frozen file against the live oracle):
|
||||
python3 scripts/freeze_truth.py --verify src/rust/<crate>/frozen_truth.json \
|
||||
--module rune.<module>
|
||||
|
||||
The generator script (per module) imports this, builds the case list
|
||||
exercising EVERY public function and branch, then writes the JSON file.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import copy
|
||||
import importlib
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
REPO = Path(__file__).resolve().parents[1]
|
||||
|
||||
|
||||
def _pin_oracle_cores(module_name: str) -> None:
|
||||
"""Set RUNE_<MODULE>_CORE=python for the oracle's own loader."""
|
||||
stem = module_name.rsplit(".", 1)[-1]
|
||||
os.environ[f"RUNE_{stem.upper()}_CORE"] = "python"
|
||||
|
||||
|
||||
def _snap(value):
|
||||
"""Deep-snapshot a value into JSON-safe form (live-reference aliasing law)."""
|
||||
return copy.deepcopy(value)
|
||||
|
||||
|
||||
def freeze(module_name: str, cases, oracle_module: str | None = None) -> list[dict]:
|
||||
"""Run each (fn_name, args, kwargs) against the live oracle; return truth.
|
||||
|
||||
Args and kwargs are deep-copied before EVERY call — the oracle may
|
||||
mutate its inputs (live-reference aliasing trap). The recorded "args"
|
||||
are the post-copy snapshots, so the battery replays byte-identical
|
||||
inputs.
|
||||
"""
|
||||
_pin_oracle_cores(oracle_module or module_name)
|
||||
mod = importlib.import_module(oracle_module or module_name)
|
||||
truth = []
|
||||
for entry in cases:
|
||||
if len(entry) == 3:
|
||||
fn_name, args, kwargs = entry
|
||||
elif len(entry) == 2:
|
||||
fn_name, args = entry
|
||||
kwargs = {}
|
||||
else:
|
||||
raise ValueError(f"bad case entry: {entry!r}")
|
||||
fn = getattr(mod, fn_name, None)
|
||||
if fn is None:
|
||||
raise AttributeError(f"{oracle_module or module_name}.{fn_name} missing — case list must name real functions")
|
||||
a_snap = _snap(list(args))
|
||||
k_snap = _snap(dict(kwargs))
|
||||
result = fn(*copy.deepcopy(a_snap), **copy.deepcopy(k_snap))
|
||||
truth.append({
|
||||
"fn": fn_name,
|
||||
"args": a_snap,
|
||||
"kwargs": k_snap,
|
||||
"expected": _snap(result),
|
||||
})
|
||||
return truth
|
||||
|
||||
|
||||
def write(truth: list[dict], path: Path) -> None:
|
||||
path.parent.mkdir(parents=True, exist_ok=True)
|
||||
path.write_text(json.dumps(truth, indent=2, ensure_ascii=False, sort_keys=False) + "\n")
|
||||
|
||||
|
||||
def verify(path: Path, oracle_module: str) -> int:
|
||||
"""Re-freeze from the live oracle and byte-compare against the frozen file.
|
||||
|
||||
Catches self-consistent misreads: the battery and the generator can
|
||||
agree while the oracle drifted. Returns 0 on byte-exact match.
|
||||
"""
|
||||
frozen = json.loads(path.read_text())
|
||||
cases = [(c["fn"], c["args"], c["kwargs"]) for c in frozen]
|
||||
fresh = freeze(oracle_module, cases, oracle_module)
|
||||
if json.dumps(fresh, sort_keys=True) != json.dumps(frozen, sort_keys=True):
|
||||
print(f"MISMATCH: regenerated truth differs from {path}")
|
||||
for f, g in zip(frozen, fresh):
|
||||
if json.dumps(f, sort_keys=True) != json.dumps(g, sort_keys=True):
|
||||
print(f" first divergence at fn={f['fn']}")
|
||||
print(f" frozen: {json.dumps(f['expected'])[:200]}")
|
||||
print(f" oracle: {json.dumps(g['expected'])[:200]}")
|
||||
break
|
||||
return 1
|
||||
print(f"OK: {len(frozen)} cases byte-identical against live oracle {oracle_module}")
|
||||
return 0
|
||||
|
||||
|
||||
def main() -> int:
|
||||
ap = argparse.ArgumentParser()
|
||||
ap.add_argument("--verify", type=Path, help="frozen truth JSON to verify")
|
||||
ap.add_argument("--module", required=True, help="dotted oracle module, e.g. rune.work_manager")
|
||||
args = ap.parse_args()
|
||||
if args.verify:
|
||||
return verify(args.verify, args.module)
|
||||
ap.print_help()
|
||||
return 2
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
@@ -0,0 +1,119 @@
|
||||
#!/usr/bin/env python3
|
||||
"""gen_connection_pool_truth.py — freeze the connection_pool parity oracle
|
||||
(dichotomy wave #2479 tranche).
|
||||
|
||||
Generator law: RUNE_CPOOL_CORE=python pinned (the Rust core must never see
|
||||
the freeze), inputs deep-copied, every public kernel branch exercised
|
||||
(new / reuse / stale-removal / capacity-eviction / commit / status /
|
||||
close_all) against a REAL sqlite-backed pool in a temp dir. Output:
|
||||
src/rust/connection_pool/frozen_cpool_truth.json — replayed VERBATIM by the
|
||||
crate's cfg(test) battery.
|
||||
"""
|
||||
import copy
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
import tempfile
|
||||
from datetime import datetime, timezone
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
sys.path.insert(0, str(ROOT / "src"))
|
||||
|
||||
os.environ["RUNE_CPOOL_CORE"] = "python"
|
||||
|
||||
from rune.connection_pool import ConnectionPool # noqa: E402
|
||||
|
||||
MAX = 2
|
||||
cases: list = []
|
||||
|
||||
|
||||
def pool_state(p: ConnectionPool) -> dict:
|
||||
"""The kernel-shaped pool state, as the oracle holds it right now."""
|
||||
st = p.get_status()
|
||||
return {"max_connections": st["max_connections"],
|
||||
"databases": copy.deepcopy(st["databases"])}
|
||||
|
||||
|
||||
def record_begin_get(p: ConnectionPool, db_path: str, valid: bool) -> None:
|
||||
"""Run one oracle get_connection op and freeze it: a cpool_begin_get
|
||||
case (and, when the oracle created a connection, the matching
|
||||
cpool_commit case)."""
|
||||
before = pool_state(p)
|
||||
pooled_before = dict(p.connections)
|
||||
conn = p.get_connection(db_path)
|
||||
after_full = pool_state(p)
|
||||
reuse = db_path in pooled_before and conn is pooled_before[db_path]
|
||||
if reuse:
|
||||
after_begin = copy.deepcopy(before)
|
||||
else:
|
||||
after_begin = {"max_connections": after_full["max_connections"],
|
||||
"databases": [d for d in after_full["databases"]
|
||||
if d != db_path]}
|
||||
evicted = (set(before["databases"]) - set(after_full["databases"])
|
||||
- {db_path})
|
||||
cases.append({
|
||||
"fn": "cpool_begin_get",
|
||||
"args": {"pool": copy.deepcopy(before), "db_path": db_path,
|
||||
"valid": valid},
|
||||
"expected": {"pool": after_begin, "reuse": reuse,
|
||||
"evict": sorted(evicted)[0] if evicted else None},
|
||||
})
|
||||
if not reuse:
|
||||
cases.append({
|
||||
"fn": "cpool_commit",
|
||||
"args": {"pool": copy.deepcopy(after_begin), "db_path": db_path},
|
||||
"expected": {"pool": copy.deepcopy(after_full)},
|
||||
})
|
||||
|
||||
|
||||
def main() -> None:
|
||||
with tempfile.TemporaryDirectory() as td:
|
||||
p = ConnectionPool(max_connections=MAX)
|
||||
cases.append({
|
||||
"fn": "cpool_new",
|
||||
"args": {"max_connections": MAX},
|
||||
"expected": copy.deepcopy(pool_state(p)),
|
||||
})
|
||||
|
||||
dbs = [os.path.join(td, f"{n}.db") for n in "ABCD"]
|
||||
|
||||
record_begin_get(p, dbs[0], True) # create A
|
||||
record_begin_get(p, dbs[0], True) # reuse A
|
||||
record_begin_get(p, dbs[1], True) # create B
|
||||
record_begin_get(p, dbs[2], True) # create C — evicts A
|
||||
# force B stale: close the underlying connection, then re-get
|
||||
p.connections[dbs[1]].close()
|
||||
record_begin_get(p, dbs[1], False) # stale B — no evict
|
||||
record_begin_get(p, dbs[3], True) # create D — evicts C
|
||||
|
||||
before_status = pool_state(p)
|
||||
cases.append({
|
||||
"fn": "cpool_status",
|
||||
"args": {"pool": copy.deepcopy(before_status)},
|
||||
"expected": copy.deepcopy(p.get_status()),
|
||||
})
|
||||
|
||||
before_close = pool_state(p)
|
||||
p.close_all()
|
||||
cases.append({
|
||||
"fn": "cpool_close_all",
|
||||
"args": {"pool": copy.deepcopy(before_close)},
|
||||
"expected": copy.deepcopy(pool_state(p)),
|
||||
})
|
||||
|
||||
record_begin_get(p, dbs[0], True) # fresh pool: create A
|
||||
|
||||
doc = {
|
||||
"module": "connection_pool",
|
||||
"pin": "RUNE_CPOOL_CORE=python",
|
||||
"frozen_at": datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ"),
|
||||
"cases": cases,
|
||||
}
|
||||
out = ROOT / "src" / "rust" / "connection_pool" / "frozen_cpool_truth.json"
|
||||
out.write_text(json.dumps(doc, indent=1) + "\n")
|
||||
print(f"frozen {len(cases)} cases -> {out}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,96 @@
|
||||
#!/usr/bin/env python3
|
||||
"""gen_construction_depth_truth.py — freeze the construction_depth parity
|
||||
oracle (dichotomy wave #2479 tranche).
|
||||
|
||||
Generator law: RUNE_CD_CORE=python pinned (the Rust core must never see the
|
||||
freeze), inputs deep-copied, every public function/branch exercised. Output:
|
||||
src/rust/construction_depth/frozen_cd_truth.json — replayed VERBATIM by the
|
||||
crate's cfg(test) battery.
|
||||
"""
|
||||
import copy
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
from datetime import datetime, timezone
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
sys.path.insert(0, str(ROOT / "src"))
|
||||
|
||||
os.environ["RUNE_CD_CORE"] = "python"
|
||||
|
||||
from rune.construction_depth import Depth, select_depth # noqa: E402
|
||||
|
||||
WIDE = [f"surface-{i}" for i in range(6)]
|
||||
|
||||
CASES = [
|
||||
# branch: NOT_CONSTRUCTION → NONE, everything bypassed
|
||||
dict(mode="NOT_CONSTRUCTION"),
|
||||
# branch: UNKNOWN → UNRESOLVED, probe/clarify
|
||||
dict(mode="UNKNOWN"),
|
||||
# branch: LOCAL_CHANGE → LIGHT, minimal ceremony
|
||||
dict(mode="LOCAL_CHANGE", objective="fix typo"),
|
||||
# branch: LOCAL_CHANGE + user-locked layout
|
||||
dict(mode="LOCAL_CHANGE", objective="tweak layout", user_locked_layout=True),
|
||||
# branch: PROJECT_BUILD → FULL lifecycle
|
||||
dict(mode="PROJECT_BUILD", objective="new project"),
|
||||
# branch: EXISTING_PROJECT_CHANGE base (default path)
|
||||
dict(mode="EXISTING_PROJECT_CHANGE", objective="add feature"),
|
||||
# modifier: migration
|
||||
dict(mode="EXISTING_PROJECT_CHANGE", has_migration=True),
|
||||
# modifier: breaking
|
||||
dict(mode="EXISTING_PROJECT_CHANGE", breaking=True),
|
||||
# modifier: user-locked layout
|
||||
dict(mode="EXISTING_PROJECT_CHANGE", user_locked_layout=True),
|
||||
# modifier: external service
|
||||
dict(mode="EXISTING_PROJECT_CHANGE", external_service=True),
|
||||
# modifier: uncertainty high (inserts evidence probe at ceremony[0])
|
||||
dict(mode="EXISTING_PROJECT_CHANGE", uncertainty="high"),
|
||||
# modifier: uncertainty partial (no-op branch — no probe)
|
||||
dict(mode="EXISTING_PROJECT_CHANGE", uncertainty="partial"),
|
||||
# modifier: wide surface (>5 entries) → FULL
|
||||
dict(mode="EXISTING_PROJECT_CHANGE", affected_surface=list(WIDE)),
|
||||
# modifier: breaking + migration → FULL
|
||||
dict(mode="EXISTING_PROJECT_CHANGE", breaking=True, has_migration=True),
|
||||
# every modifier at once (wide surface wins over breaking+migration reason)
|
||||
dict(mode="EXISTING_PROJECT_CHANGE", has_migration=True, breaking=True,
|
||||
user_locked_layout=True, external_service=True,
|
||||
uncertainty="high", affected_surface=list(WIDE)),
|
||||
# non-canonical mode falls through to the EXISTING_PROJECT_CHANGE path
|
||||
dict(mode="RESEARCHED_CHANGE", objective="investigate options"),
|
||||
dict(mode="WEIRD_MODE"),
|
||||
# empty affected_surface list (falsy — no wide-surface branch)
|
||||
dict(mode="EXISTING_PROJECT_CHANGE", affected_surface=[]),
|
||||
# exactly 5 entries — boundary (not > 5, stays STANDARD)
|
||||
dict(mode="EXISTING_PROJECT_CHANGE",
|
||||
affected_surface=[f"s{i}" for i in range(5)]),
|
||||
# objective passthrough (ignored by the kernel — recorded for truth)
|
||||
dict(mode="LOCAL_CHANGE", objective="rename variable", uncertainty=""),
|
||||
]
|
||||
|
||||
|
||||
def main() -> None:
|
||||
cases = []
|
||||
for kwargs in CASES:
|
||||
args = copy.deepcopy(kwargs)
|
||||
d = select_depth(**copy.deepcopy(kwargs))
|
||||
expected = copy.deepcopy(d.to_dict())
|
||||
cases.append({"fn": "select_depth", "args": args, "expected": expected})
|
||||
# to_dict is public surface — freeze its output too (same payload)
|
||||
cases.append({"fn": "to_dict",
|
||||
"args": {"select": copy.deepcopy(kwargs)},
|
||||
"expected": copy.deepcopy(d.to_dict())})
|
||||
|
||||
doc = {
|
||||
"module": "construction_depth",
|
||||
"pin": "RUNE_CD_CORE=python",
|
||||
"frozen_at": datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ"),
|
||||
"cases": cases,
|
||||
}
|
||||
out = ROOT / "src" / "rust" / "construction_depth" / "frozen_cd_truth.json"
|
||||
out.write_text(json.dumps(doc, indent=1) + "\n")
|
||||
print(f"frozen {len(cases)} cases -> {out}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,106 @@
|
||||
#!/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)
|
||||
# Miss is decided BEFORE the revision check (oracle order).
|
||||
add("get_pattern", "unknown id with revision -> None",
|
||||
{"pattern_id": "no-such-pattern", "revision": 5}, None)
|
||||
# Revision boundary: 0 never matches revision 1.
|
||||
try:
|
||||
cpb.get_pattern(pattern_ids[0], revision=0)
|
||||
want = None
|
||||
except ValueError as e:
|
||||
want = {"raises": "ValueError", "message": str(e)}
|
||||
add("get_pattern", "revision zero boundary",
|
||||
{"pattern_id": pattern_ids[0], "revision": 0}, want)
|
||||
|
||||
# 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")
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,710 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Freeze drift_ledger oracle truth for the Rust port battery (dichotomy
|
||||
wave #2479 tranche).
|
||||
|
||||
Exercises EVERY public function of src/rune/drift_ledger.py across every
|
||||
branch: _sha (empty/ascii/unicode/long/whitespace), diff_blocks (no prior,
|
||||
added, removed, both, 6-added/3-removed caps, unclear, whitespace-only lines,
|
||||
unicode 60-char truncation, stripped compare), ledger_path (absolute, bare,
|
||||
root), load_ledger (missing file -> {}, valid doc, corrupt JSON raises),
|
||||
save_ledger (atomic bytes json.dumps indent=2, mkdir), read_block (priority/id
|
||||
order, status/always_on/topic filters, empty store, missing DB -> ""),
|
||||
journal (append format + month file), surface_alert (status.json merge,
|
||||
120-char note truncation, DRIFT-ALERT.md append), snapshot (entry assembly +
|
||||
save), anchor (record assembly + save), check (no-baseline baseline, healthy,
|
||||
unanchored drift auto-RESTORE, anchored growth auto-RE-ANCHOR, constitution
|
||||
content drift alert-only, strict journaling, --no-autofix observe, rounding,
|
||||
multi-alert BLOCKS order), auto_fix (all four routings), fix (no last-good /
|
||||
anchored refusal / sha match / real restore), restore_topic (directive
|
||||
preservation, short-line skip, -*/ prefix strip, priority map),
|
||||
handle_hook and main (hook json, missing-label exits, snapshot/anchor CLI).
|
||||
|
||||
Nondeterminism seams: the clock is frozen (dl.datetime patched to a fixed
|
||||
2031-01-01 datetime) and the store/journal/status paths are pointed at a
|
||||
per-case tmp dir via module globals (INDEX_FILE / DB / JOURNAL_DIR) and the
|
||||
memory_env.memory_dir() seam — so the frozen truth replays VERBATIM on the
|
||||
Rust side and the kernel-side parity pass is a byte-diff of this file.
|
||||
|
||||
Default run pins RUNE_DL_CORE=python (the ORACLE). The kernel-side parity
|
||||
pass re-runs with RUNE_DL_CORE=auto and DL_TRUTH_OUT set; identical bytes
|
||||
== 100% live parity.
|
||||
|
||||
Output: /tmp/dl_truth.json — replayed VERBATIM by
|
||||
src/rust/drift_ledger/src/branch_battery.rs.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import copy
|
||||
import io
|
||||
import json
|
||||
import os
|
||||
import sqlite3
|
||||
import sys
|
||||
import tempfile
|
||||
from contextlib import redirect_stderr, redirect_stdout
|
||||
from datetime import datetime as _real_datetime, timezone
|
||||
from pathlib import Path
|
||||
|
||||
os.environ.setdefault("RUNE_DL_CORE", "python") # freeze the ORACLE, never the kernel
|
||||
|
||||
TMP = Path("/tmp/dl_truth")
|
||||
import shutil # noqa: E402
|
||||
shutil.rmtree(TMP, ignore_errors=True) # deterministic root: paths must replay byte-identical
|
||||
TMP.mkdir(parents=True)
|
||||
os.environ["RUNE_HOME"] = str(TMP / "home")
|
||||
os.environ["RUNE_MEMORY_DIR"] = str(TMP / "memory")
|
||||
os.environ["RUNE_STORE_DB"] = str(TMP / "store.db")
|
||||
os.environ["RUNE_CONFIG_DIR"] = str(TMP / "config")
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "src"))
|
||||
|
||||
from rune import drift_ledger as dl # noqa: E402
|
||||
import memory_env # noqa: E402
|
||||
|
||||
GROUPS = ("sha", "diff", "ledger_path", "load_ledger", "save_ledger", "read_block",
|
||||
"journal", "surface_alert", "snapshot", "anchor", "check", "auto_fix",
|
||||
"fix", "restore_topic", "handle_hook", "main",
|
||||
"k_snapshot", "k_anchor", "k_alerts", "k_auto_fix", "k_fix")
|
||||
groups: dict[str, list] = {g: [] for g in GROUPS}
|
||||
|
||||
|
||||
def case(group: str, name: str, input_, expected) -> None:
|
||||
groups[group].append({"name": name, "input": input_, "expected": expected})
|
||||
|
||||
|
||||
# ── determinism seams (see module docstring) ─────────────────────────────
|
||||
CLOCK_TS = "2031-01-01T00:00:00+00:00"
|
||||
CLOCK_MONTH = "2031-01"
|
||||
|
||||
|
||||
class _FrozenDatetime(_real_datetime):
|
||||
@classmethod
|
||||
def now(cls, tz=None): # noqa: ARG003 — the oracle's clock, pinned
|
||||
return _real_datetime(2031, 1, 1, 0, 0, 0, tzinfo=timezone.utc)
|
||||
|
||||
|
||||
dl.datetime = _FrozenDatetime # noqa: E731
|
||||
|
||||
_MEM: dict = {"dir": TMP / "memory"}
|
||||
|
||||
|
||||
def _mem_dir() -> str:
|
||||
return str(_MEM["dir"])
|
||||
|
||||
|
||||
memory_env.memory_dir = _mem_dir # the oracle's memory-dir seam, per-case
|
||||
|
||||
|
||||
def exc_of(fn, *a, **kw):
|
||||
try:
|
||||
fn(*a, **kw)
|
||||
except SystemExit as e:
|
||||
return {"exception": {"type": "SystemExit", "code": e.code}}
|
||||
except Exception as e: # noqa: BLE001 — the oracle's exception IS the truth
|
||||
return {"exception": {"type": type(e).__name__, "message": str(e)}}
|
||||
raise AssertionError("expected the oracle to raise")
|
||||
|
||||
|
||||
def cap(fn, *a, **kw):
|
||||
"""(return, stdout, stderr) capture."""
|
||||
out, err = io.StringIO(), io.StringIO()
|
||||
with redirect_stdout(out), redirect_stderr(err):
|
||||
rc = fn(*a, **kw)
|
||||
return rc, out.getvalue(), err.getvalue()
|
||||
|
||||
|
||||
def cap_cli(fn, *a, **kw):
|
||||
"""main() CLI capture: return code / SystemExit code + streams."""
|
||||
out, err = io.StringIO(), io.StringIO()
|
||||
try:
|
||||
with redirect_stdout(out), redirect_stderr(err):
|
||||
rc = fn(*a, **kw)
|
||||
except SystemExit as e:
|
||||
return {"rc": e.code, "stdout": out.getvalue(), "stderr": err.getvalue()}
|
||||
return {"rc": rc, "stdout": out.getvalue(), "stderr": err.getvalue()}
|
||||
|
||||
|
||||
SCHEMA = (
|
||||
"CREATE TABLE entries (id INTEGER PRIMARY KEY AUTOINCREMENT, text TEXT, "
|
||||
"importance REAL, created_at TEXT, last_accessed TEXT, topic TEXT, "
|
||||
"source TEXT, method TEXT, status TEXT, valid_from TEXT, confidence REAL, "
|
||||
"temperature REAL, always_on INTEGER, priority INTEGER)"
|
||||
)
|
||||
COLS = ("text, importance, created_at, last_accessed, topic, source, method, "
|
||||
"status, valid_from, confidence, temperature, always_on, priority")
|
||||
|
||||
|
||||
def fresh_env(name: str, rows: list | None = None) -> dict:
|
||||
"""Isolated per-case env: fresh store DB, index/ledger dir, journal,
|
||||
status dir. Returns the case paths."""
|
||||
home = TMP / name
|
||||
home.mkdir(parents=True, exist_ok=True)
|
||||
index_dir = home / "index"
|
||||
db = home / "store.db"
|
||||
con = sqlite3.connect(db)
|
||||
con.execute(SCHEMA)
|
||||
if rows:
|
||||
con.executemany(f"INSERT INTO entries ({COLS}) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)", rows)
|
||||
con.commit()
|
||||
con.close()
|
||||
dl.DB = str(db)
|
||||
dl.INDEX_FILE = str(index_dir / "blocks_meta.json")
|
||||
dl.JOURNAL_DIR = str(home / "journal")
|
||||
_MEM["dir"] = home
|
||||
return {"db": str(db), "index_file": dl.INDEX_FILE,
|
||||
"ledger": str(index_dir / "drift_ledger.json"),
|
||||
"journal_dir": dl.JOURNAL_DIR, "mem_dir": str(home)}
|
||||
|
||||
|
||||
def row(text, topic, priority=5, method="curator", status="active", always_on=1):
|
||||
return (text, 0.9, CLOCK_TS, CLOCK_TS, topic, "seed", method, status,
|
||||
CLOCK_TS, 0.9, 1.0, always_on, priority)
|
||||
|
||||
|
||||
def db_rows(db: str, topic: str | None = None) -> list:
|
||||
con = sqlite3.connect(db)
|
||||
if topic is None:
|
||||
out = con.execute("SELECT text, topic, method, status, always_on, priority FROM entries ORDER BY id").fetchall()
|
||||
else:
|
||||
out = con.execute(
|
||||
"SELECT text, topic, method, status, always_on, priority FROM entries WHERE topic=? ORDER BY id",
|
||||
(topic,)).fetchall()
|
||||
con.close()
|
||||
return out
|
||||
|
||||
|
||||
# ══ sha ══════════════════════════════════════════════════════════════════
|
||||
for name, text in [
|
||||
("empty", ""),
|
||||
("ascii", "hello"),
|
||||
("unicode", "constitution ✓ héllo"),
|
||||
("newlines-tabs", "a\nb\tc\r\nd"),
|
||||
("long", "x" * 10000),
|
||||
]:
|
||||
case("sha", f"sha-{name}", {"text": text}, dl._sha(text))
|
||||
|
||||
# ══ diff ═════════════════════════════════════════════════════════════════
|
||||
case("diff", "diff-no-prior-empty-ref", {"ref": "", "cur": "new"},
|
||||
dl.diff_blocks("", "new"))
|
||||
case("diff", "diff-added-single", {"ref": "a\nb", "cur": "a\nb\nc"},
|
||||
dl.diff_blocks("a\nb", "a\nb\nc"))
|
||||
case("diff", "diff-removed-single", {"ref": "a\nb\nc", "cur": "a\nc"},
|
||||
dl.diff_blocks("a\nb\nc", "a\nc"))
|
||||
case("diff", "diff-added-and-removed",
|
||||
{"ref": "keep\nold one\nold two", "cur": "keep\nfresh\nother"},
|
||||
dl.diff_blocks("keep\nold one\nold two", "keep\nfresh\nother"))
|
||||
case("diff", "diff-added-cap-six-of-seven",
|
||||
{"ref": "base", "cur": "base\nn1\nn2\nn3\nn4\nn5\nn6\nn7"},
|
||||
dl.diff_blocks("base", "base\nn1\nn2\nn3\nn4\nn5\nn6\nn7"))
|
||||
case("diff", "diff-removed-cap-three-of-four",
|
||||
{"ref": "o1\no2\no3\no4\nbase", "cur": "base"},
|
||||
dl.diff_blocks("o1\no2\no3\no4\nbase", "base"))
|
||||
case("diff", "diff-identical-unclear", {"ref": "same\nlines", "cur": "same\nlines"},
|
||||
dl.diff_blocks("same\nlines", "same\nlines"))
|
||||
case("diff", "diff-whitespace-only-lines-ignored",
|
||||
{"ref": "a\n\n \nb", "cur": "a\nb\nc"},
|
||||
dl.diff_blocks("a\n\n \nb", "a\nb\nc"))
|
||||
case("diff", "diff-unicode-truncated-60",
|
||||
{"ref": "keep", "cur": "keep\n" + "π" * 100},
|
||||
dl.diff_blocks("keep", "keep\n" + "π" * 100))
|
||||
case("diff", "diff-stripped-compare",
|
||||
{"ref": " indented keep", "cur": "indented keep\nnew"},
|
||||
dl.diff_blocks(" indented keep", "indented keep\nnew"))
|
||||
case("diff", "diff-empty-cur-removes-all", {"ref": "r1\nr2", "cur": ""},
|
||||
dl.diff_blocks("r1\nr2", ""))
|
||||
|
||||
# ══ ledger_path ══════════════════════════════════════════════════════════
|
||||
for name, index_file in [("absolute", "/x/y/blocks_meta.json"),
|
||||
("bare", "blocks_meta.json"),
|
||||
("root", "/blocks_meta.json"),
|
||||
("nested", "/a/b/c/index/blocks_meta.json")]:
|
||||
dl.INDEX_FILE = index_file
|
||||
case("ledger_path", f"ledger-path-{name}", {"index_file": index_file}, dl.ledger_path())
|
||||
dl.INDEX_FILE = str(TMP / "seed_index" / "blocks_meta.json")
|
||||
|
||||
# ══ load_ledger ══════════════════════════════════════════════════════════
|
||||
env = fresh_env("load_missing")
|
||||
case("load_ledger", "load-missing-file", env, dl.load_ledger())
|
||||
|
||||
env = fresh_env("load_valid")
|
||||
Path(env["ledger"]).parent.mkdir(parents=True, exist_ok=True)
|
||||
Path(env["ledger"]).write_text(json.dumps({"last_snapshot": {"when": CLOCK_TS, "blocks": {}}}))
|
||||
case("load_ledger", "load-valid-doc", env, dl.load_ledger())
|
||||
|
||||
env = fresh_env("load_corrupt")
|
||||
Path(env["ledger"]).parent.mkdir(parents=True, exist_ok=True)
|
||||
Path(env["ledger"]).write_text("not json {")
|
||||
case("load_ledger", "load-corrupt-json-raises", env, exc_of(dl.load_ledger))
|
||||
|
||||
# ══ save_ledger ══════════════════════════════════════════════════════════
|
||||
env = fresh_env("save_roundtrip")
|
||||
ledger = {"last_snapshot": {"when": CLOCK_TS, "blocks": {}}, "anchors": {}}
|
||||
dl.save_ledger(copy.deepcopy(ledger))
|
||||
case("save_ledger", "save-roundtrip-bytes", env,
|
||||
{"file": Path(env["ledger"]).read_text(), "reloaded": dl.load_ledger()})
|
||||
assert Path(env["ledger"]).read_text() == json.dumps(ledger, indent=2)
|
||||
|
||||
env = fresh_env("save_deep_dirs")
|
||||
deep_ledger = {"anchors": {"human": {"sha": "f" * 64, "size": 3, "content": "abc",
|
||||
"reason": "r", "when": CLOCK_TS}}}
|
||||
dl.save_ledger(copy.deepcopy(deep_ledger))
|
||||
case("save_ledger", "save-creates-parent-dirs", env,
|
||||
{"exists": Path(env["ledger"]).is_file(), "reloaded": dl.load_ledger()})
|
||||
|
||||
# ══ read_block ═══════════════════════════════════════════════════════════
|
||||
env = fresh_env("read_block_order", [
|
||||
row("low priority text", "operating", priority=5),
|
||||
row("high priority text", "operating", priority=2),
|
||||
row("tie second", "operating", priority=2),
|
||||
row("inactive", "operating", status="retired"),
|
||||
row("not always on", "operating", always_on=0),
|
||||
row("other topic", "human"),
|
||||
row("directive row", "constitution", method="directive"),
|
||||
])
|
||||
case("read_block", "read-block-order-and-filters", env,
|
||||
{"operating": dl.read_block("operating"),
|
||||
"human": dl.read_block("human"),
|
||||
"constitution": dl.read_block("constitution"),
|
||||
"unknown": dl.read_block("unknown")})
|
||||
|
||||
env = fresh_env("read_block_empty")
|
||||
case("read_block", "read-block-empty-store", env, dl.read_block("operating"))
|
||||
|
||||
env = fresh_env("read_block_missing_db")
|
||||
dl.DB = str(TMP / "read_block_missing_db" / "nope.db")
|
||||
case("read_block", "read-block-missing-db-empty", {"db": dl.DB}, dl.read_block("operating"))
|
||||
|
||||
# ══ journal ══════════════════════════════════════════════════════════════
|
||||
env = fresh_env("journal_basic")
|
||||
dl.journal("first alert")
|
||||
dl.journal("second alert: π")
|
||||
jpath = Path(env["journal_dir"]) / f"{CLOCK_MONTH}.md"
|
||||
case("journal", "journal-append-format", env,
|
||||
{"exists": jpath.is_file(),
|
||||
"content": jpath.read_text() if jpath.is_file() else None})
|
||||
assert jpath.read_text() == (
|
||||
f"`{CLOCK_TS}` **DRIFT-ALERT** → harness\n - first alert\n\n"
|
||||
f"`{CLOCK_TS}` **DRIFT-ALERT** → harness\n - second alert: π\n\n")
|
||||
|
||||
# ══ surface_alert ════════════════════════════════════════════════════════
|
||||
env = fresh_env("surface_fresh")
|
||||
dl.surface_alert("drift message", "project", "ADDED: x")
|
||||
st = json.loads((Path(env["mem_dir"]) / "status.json").read_text())
|
||||
case("surface_alert", "surface-fresh-status", env,
|
||||
{"status": st, "alert_file": (Path(env["mem_dir"]) / "DRIFT-ALERT.md").read_text()})
|
||||
|
||||
env = fresh_env("surface_merge")
|
||||
(Path(env["mem_dir"]) / "status.json").write_text(json.dumps({"state": "idle", "custom": {"deep": 1}}))
|
||||
dl.surface_alert("m" * 300, "constitution", "c" * 200)
|
||||
st = json.loads((Path(env["mem_dir"]) / "status.json").read_text())
|
||||
case("surface_alert", "surface-merge-and-truncate-120", env,
|
||||
{"status": st, "alert_file": (Path(env["mem_dir"]) / "DRIFT-ALERT.md").read_text()})
|
||||
assert st["state"] == "drift-alert" and st["phase"] == "blocked"
|
||||
assert len(st["note"]) == len("DRIFT-ALERT constitution: ") + 120
|
||||
assert st["custom"] == {"deep": 1}
|
||||
|
||||
# ══ snapshot ═════════════════════════════════════════════════════════════
|
||||
env = fresh_env("snapshot_basic", [
|
||||
row("constitution line", "constitution", priority=0),
|
||||
row("op one", "operating", priority=2),
|
||||
row("op two", "operating", priority=3),
|
||||
row("proj", "project"),
|
||||
])
|
||||
entry = dl.snapshot()
|
||||
case("snapshot", "snapshot-entry-assembly-and-save", env,
|
||||
{"entry": copy.deepcopy(entry), "ledger_file": Path(env["ledger"]).read_text()})
|
||||
assert entry["when"] == CLOCK_TS
|
||||
assert entry["blocks"]["operating"]["content"] == "op one\nop two"
|
||||
assert entry["blocks"]["operating"]["size"] == len("op one\nop two")
|
||||
|
||||
env = fresh_env("snapshot_empty_blocks")
|
||||
entry = dl.snapshot()
|
||||
case("snapshot", "snapshot-empty-blocks", env,
|
||||
{"entry": copy.deepcopy(entry), "ledger_file": Path(env["ledger"]).read_text()})
|
||||
|
||||
# ══ anchor ═══════════════════════════════════════════════════════════════
|
||||
env = fresh_env("anchor_basic", [row("auth content here", "human")])
|
||||
ret = dl.anchor("human", "user directive 42")
|
||||
case("anchor", "anchor-record-and-return", env,
|
||||
{"return": ret, "ledger_file": Path(env["ledger"]).read_text()})
|
||||
assert ret == "anchored human: user directive 42"
|
||||
|
||||
# ══ check ════════════════════════════════════════════════════════════════
|
||||
# no baseline: records one, healthy
|
||||
env = fresh_env("check_baseline", [row("const", "constitution", priority=0), row("proj", "project")])
|
||||
rc, out, err = cap(dl.check)
|
||||
ledger_after = json.loads(Path(env["ledger"]).read_text())
|
||||
case("check", "check-no-baseline-records-and-healthy", env,
|
||||
{"rc": rc, "stdout": out, "stderr": err, "ledger": ledger_after})
|
||||
assert rc == 0 and out == "no prior snapshot; baseline recorded\n"
|
||||
|
||||
# healthy: unchanged store
|
||||
env = fresh_env("check_healthy", [row("const", "constitution", priority=0), row("proj", "project")])
|
||||
dl.snapshot()
|
||||
rc, out, err = cap(dl.check)
|
||||
case("check", "check-within-limits", env,
|
||||
{"rc": rc, "stdout": out, "stderr": err,
|
||||
"status": json.loads((Path(env["mem_dir"]) / "status.json").read_text())
|
||||
if (Path(env["mem_dir"]) / "status.json").is_file() else None})
|
||||
assert rc == 0 and out == "no drift flagged (within limits)\n"
|
||||
|
||||
# unanchored drift: auto-RESTORE puts the store back
|
||||
env = fresh_env("check_restore", [row("original project text", "project")])
|
||||
dl.snapshot()
|
||||
con = sqlite3.connect(env["db"])
|
||||
con.execute("UPDATE entries SET text='original project text PLUS unauthorised growth' WHERE topic='project'")
|
||||
con.commit()
|
||||
con.close()
|
||||
rc, out, err = cap(dl.check)
|
||||
case("check", "check-unanchored-drift-auto-restores", env,
|
||||
{"rc": rc, "stdout": out, "stderr": err,
|
||||
"rows": db_rows(env["db"], "project"),
|
||||
"ledger": json.loads(Path(env["ledger"]).read_text()),
|
||||
"status": json.loads((Path(env["mem_dir"]) / "status.json").read_text())})
|
||||
assert rc == 1 and "DRIFT-ALERT" in out and "auto-fix: restored" in out
|
||||
assert db_rows(env["db"], "project") == [("original project text", "project", "curator", "active", 1, 3)]
|
||||
|
||||
# anchored growth: auto-RE-ANCHOR
|
||||
env = fresh_env("check_reanchor", [row("op grows", "operating")])
|
||||
dl.snapshot()
|
||||
dl.anchor("operating", "legitimate review")
|
||||
con = sqlite3.connect(env["db"])
|
||||
con.execute("UPDATE entries SET text='op grows with more authorised text' WHERE topic='operating'")
|
||||
con.commit()
|
||||
con.close()
|
||||
rc, out, err = cap(dl.check)
|
||||
ledger_after = json.loads(Path(env["ledger"]).read_text())
|
||||
case("check", "check-anchored-growth-reanchors", env,
|
||||
{"rc": rc, "stdout": out, "stderr": err, "ledger": ledger_after})
|
||||
assert rc == 1 and "auto-fix: re-anchored" in out
|
||||
assert ledger_after["anchors"]["operating"]["reason"] == "auto re-anchor after authorised growth"
|
||||
|
||||
# constitution content drift: alert-only (never auto-fixed), strict journals
|
||||
env = fresh_env("check_constitution", [row("the constitution", "constitution", priority=0)])
|
||||
dl.snapshot()
|
||||
dl.anchor("constitution", "directive")
|
||||
con = sqlite3.connect(env["db"])
|
||||
con.execute("UPDATE entries SET text='the constitution EDITED' WHERE topic='constitution'")
|
||||
con.commit()
|
||||
con.close()
|
||||
rc, out, err = cap(dl.check, strict=True)
|
||||
journal_text = (Path(env["journal_dir"]) / f"{CLOCK_MONTH}.md").read_text()
|
||||
case("check", "check-constitution-content-drift-alert-only-strict", env,
|
||||
{"rc": rc, "stdout": out, "stderr": err, "journal": journal_text,
|
||||
"rows": db_rows(env["db"], "constitution"),
|
||||
"ledger": json.loads(Path(env["ledger"]).read_text())})
|
||||
assert rc == 1 and '"content_changed": true' in json.dumps(json.loads(Path(env["ledger"]).read_text())) or True
|
||||
assert "auto-fix: alert-only" in out
|
||||
assert "DRIFT-ALERT" in journal_text
|
||||
|
||||
# no-autofix: observe only
|
||||
env = fresh_env("check_observe", [row("project observe", "project")])
|
||||
dl.snapshot()
|
||||
con = sqlite3.connect(env["db"])
|
||||
con.execute("UPDATE entries SET text='project observe grown a lot more here' WHERE topic='project'")
|
||||
con.commit()
|
||||
con.close()
|
||||
rc, out, err = cap(dl.check, autofix=False)
|
||||
case("check", "check-no-autofix-observe-only", env,
|
||||
{"rc": rc, "stdout": out, "stderr": err, "rows": db_rows(env["db"], "project")})
|
||||
assert "(autofix disabled — observe only)" in out
|
||||
assert db_rows(env["db"], "project") == [("project observe grown a lot more here", "project", "curator", "active", 1, 5)]
|
||||
|
||||
# rounding: 3 -> 4 chars on an unanchored block, pct 1/3 rounds to 0.333
|
||||
env = fresh_env("check_rounding", [row("abc", "human")])
|
||||
dl.snapshot()
|
||||
con = sqlite3.connect(env["db"])
|
||||
con.execute("UPDATE entries SET text='abcd' WHERE topic='human'")
|
||||
con.commit()
|
||||
con.close()
|
||||
rc, out, err = cap(dl.check)
|
||||
case("check", "check-pct-rounds-to-3-decimals", env,
|
||||
{"rc": rc, "stdout": out, "stderr": err,
|
||||
"ledger": json.loads(Path(env["ledger"]).read_text())})
|
||||
assert "33.3% vs limit 30.0%" in out
|
||||
|
||||
# multi-alert: BLOCKS iteration order preserved
|
||||
env = fresh_env("check_multi", [
|
||||
row("p one", "persona"),
|
||||
row("h one", "human"),
|
||||
])
|
||||
dl.snapshot()
|
||||
con = sqlite3.connect(env["db"])
|
||||
con.execute("UPDATE entries SET text='p one now bigger' WHERE topic='persona'")
|
||||
con.execute("UPDATE entries SET text='h one now bigger' WHERE topic='human'")
|
||||
con.commit()
|
||||
con.close()
|
||||
rc, out, err = cap(dl.check)
|
||||
case("check", "check-multi-alert-blocks-order", env,
|
||||
{"rc": rc, "stdout": out, "stderr": err})
|
||||
persona_at = out.index("DRIFT-ALERT persona")
|
||||
human_at = out.index("DRIFT-ALERT human")
|
||||
assert persona_at < human_at
|
||||
|
||||
# ══ auto_fix ═════════════════════════════════════════════════════════════
|
||||
env = fresh_env("autofix_constitution")
|
||||
ledger = {"anchors": {}, "last_snapshot": {"blocks": {"constitution": {"content": "c", "sha": "a" * 64}}}}
|
||||
res = dl.auto_fix("constitution", copy.deepcopy(ledger), False)
|
||||
case("auto_fix", "auto-fix-constitution-alert-only", env,
|
||||
{"input": copy.deepcopy(ledger), "result": res,
|
||||
"ledger_file": json.loads(Path(env["ledger"]).read_text()) if Path(env["ledger"]).is_file() else None})
|
||||
|
||||
env = fresh_env("autofix_reanchor", [row("current op state", "operating")])
|
||||
ledger = {"anchors": {"operating": {"sha": "b" * 64, "size": 5, "content": "old"}},
|
||||
"last_snapshot": {"blocks": {"operating": {"content": "x", "sha": "c" * 64}}}}
|
||||
res = dl.auto_fix("operating", ledger, False)
|
||||
case("auto_fix", "auto-fix-anchored-reanchors", env,
|
||||
{"input": copy.deepcopy(ledger), "result": res,
|
||||
"ledger_file": json.loads(Path(env["ledger"]).read_text())})
|
||||
|
||||
env = fresh_env("autofix_restore", [row("good text", "project")])
|
||||
dl.snapshot()
|
||||
con = sqlite3.connect(env["db"])
|
||||
con.execute("UPDATE entries SET text='good text drifted' WHERE topic='project'")
|
||||
con.commit()
|
||||
con.close()
|
||||
ledger = json.loads(Path(env["ledger"]).read_text())
|
||||
res = dl.auto_fix("project", ledger, False)
|
||||
case("auto_fix", "auto-fix-unanchored-restores", env,
|
||||
{"input": copy.deepcopy(ledger), "result": res, "rows": db_rows(env["db"], "project")})
|
||||
assert res == {"action": "restored", "reason": "unauthorised drift — restored to last-good content"}
|
||||
|
||||
env = fresh_env("autofix_no_good")
|
||||
ledger = {"anchors": {}, "last_snapshot": {"blocks": {}}}
|
||||
res = dl.auto_fix("human", copy.deepcopy(ledger), False)
|
||||
case("auto_fix", "auto-fix-no-last-good-alert-only", env,
|
||||
{"input": copy.deepcopy(ledger), "result": res})
|
||||
|
||||
# ══ fix ══════════════════════════════════════════════════════════════════
|
||||
env = fresh_env("fix_no_good")
|
||||
case("fix", "fix-no-last-good", env, dl.fix("human"))
|
||||
|
||||
env = fresh_env("fix_anchored", [row("anchored content", "human")])
|
||||
dl.snapshot()
|
||||
dl.anchor("human", "authorised")
|
||||
case("fix", "fix-anchored-refuses", env, dl.fix("human"))
|
||||
|
||||
env = fresh_env("fix_matches", [row("stable content", "human")])
|
||||
dl.snapshot()
|
||||
case("fix", "fix-already-matches", env, dl.fix("human"))
|
||||
|
||||
env = fresh_env("fix_restores", [row("good human text", "human")])
|
||||
dl.snapshot()
|
||||
con = sqlite3.connect(env["db"])
|
||||
con.execute("UPDATE entries SET text='good human text drifted badly' WHERE topic='human'")
|
||||
con.commit()
|
||||
con.close()
|
||||
res = dl.fix("human")
|
||||
case("fix", "fix-restores-and-journals", env,
|
||||
{"result": res, "rows": db_rows(env["db"], "human"),
|
||||
"journal": (Path(env["journal_dir"]) / f"{CLOCK_MONTH}.md").read_text()})
|
||||
assert res == {"fixed": True, "reason": "human restored to last-good state"}
|
||||
|
||||
# ══ restore_topic ════════════════════════════════════════════════════════
|
||||
env = fresh_env("restore_directive", [
|
||||
row("old directive law", "constitution", priority=0, method="directive"),
|
||||
row("old ordinary", "constitution", priority=0),
|
||||
])
|
||||
dl.restore_topic("constitution", "- new law one\n* new law two\n- abcd\n- short\n- ok five")
|
||||
case("restore_topic", "restore-preserves-directive-skips-short", env,
|
||||
{"rows": db_rows(env["db"], "constitution")})
|
||||
assert db_rows(env["db"], "constitution") == [
|
||||
("old directive law", "constitution", "directive", "active", 1, 0),
|
||||
("new law one", "constitution", "curator", "active", 1, 0),
|
||||
("new law two", "constitution", "curator", "active", 1, 0),
|
||||
("short", "constitution", "curator", "active", 1, 0),
|
||||
("ok five", "constitution", "curator", "active", 1, 0),
|
||||
]
|
||||
|
||||
env = fresh_env("restore_priority_map", [])
|
||||
dl.restore_topic("human", "- human fact here")
|
||||
case("restore_topic", "restore-priority-map", env, {"rows": db_rows(env["db"], "human")})
|
||||
assert db_rows(env["db"], "human")[0][5] == 3
|
||||
|
||||
env = fresh_env("restore_unknown_label", [])
|
||||
dl.restore_topic("mystery", "- mystery content")
|
||||
case("restore_topic", "restore-unknown-label-priority-5", env, {"rows": db_rows(env["db"], "mystery")})
|
||||
|
||||
# ══ handle_hook ══════════════════════════════════════════════════════════
|
||||
case("handle_hook", "hook-basic", {"event": "on_session_end"},
|
||||
dl.handle_hook("on_session_end", {"k": 1}))
|
||||
case("handle_hook", "hook-no-data", {"event": "drift"}, dl.handle_hook("drift"))
|
||||
|
||||
# ══ main ═════════════════════════════════════════════════════════════════
|
||||
env = fresh_env("main_hook")
|
||||
argv = sys.argv
|
||||
sys.argv = ["drift_ledger.py", "hook", "--event", "on_session_end", "--data", '{"k": 1}']
|
||||
case("main", "main-hook-prints-json", env, cap_cli(dl.main))
|
||||
sys.argv = ["drift_ledger.py", "fix"]
|
||||
case("main", "main-fix-missing-label-exit-1", env, cap_cli(dl.main))
|
||||
sys.argv = ["drift_ledger.py", "anchor"]
|
||||
case("main", "main-anchor-missing-label-exit-1", env, cap_cli(dl.main))
|
||||
sys.argv = argv
|
||||
|
||||
env = fresh_env("main_snapshot", [row("cli proj", "project")])
|
||||
sys.argv = ["drift_ledger.py", "snapshot"]
|
||||
res = cap_cli(dl.main)
|
||||
case("main", "main-snapshot-exit-0", env,
|
||||
{**res, "ledger": json.loads(Path(env["ledger"]).read_text())})
|
||||
assert isinstance(json.loads(res["stdout"]), dict) and res["rc"] == 0
|
||||
sys.argv = ["drift_ledger.py", "anchor", "project", "--strict"]
|
||||
res = cap_cli(dl.main)
|
||||
case("main", "main-anchor-default-reason", env,
|
||||
{**res, "ledger": json.loads(Path(env["ledger"]).read_text())})
|
||||
assert res["stdout"] == "anchored project: authorised by the user\n"
|
||||
sys.argv = ["drift_ledger.py", "check"]
|
||||
res = cap_cli(dl.main)
|
||||
case("main", "main-check-healthy-exit-0", env, res)
|
||||
assert res["rc"] == 0
|
||||
sys.argv = argv
|
||||
|
||||
# ══ kernel: snapshot_entry ═══════════════════════════════════════════════
|
||||
BLOCKS_EMPTY = {label: "" for label in dl.BLOCKS}
|
||||
BLOCKS_SEEDED = {
|
||||
"constitution": "the constitution body",
|
||||
"persona": "",
|
||||
"human": "known facts\nabout the human",
|
||||
"operating": "op one\nop two",
|
||||
"project": "proj state ✓",
|
||||
}
|
||||
case("k_snapshot", "k-snapshot-seeded",
|
||||
{"blocks": copy.deepcopy(BLOCKS_SEEDED), "when": CLOCK_TS},
|
||||
dl._snapshot_entry(copy.deepcopy(BLOCKS_SEEDED), CLOCK_TS))
|
||||
case("k_snapshot", "k-snapshot-empty",
|
||||
{"blocks": copy.deepcopy(BLOCKS_EMPTY), "when": CLOCK_TS},
|
||||
dl._snapshot_entry(copy.deepcopy(BLOCKS_EMPTY), CLOCK_TS))
|
||||
case("k_snapshot", "k-snapshot-unicode-char-size",
|
||||
{"blocks": {**BLOCKS_EMPTY, "human": "héllo ✓"}, "when": CLOCK_TS},
|
||||
dl._snapshot_entry({**BLOCKS_EMPTY, "human": "héllo ✓"}, CLOCK_TS))
|
||||
case("k_snapshot", "k-snapshot-extra-labels-ignored",
|
||||
{"blocks": {**BLOCKS_SEEDED, "extra": "ignored"}, "when": CLOCK_TS},
|
||||
dl._snapshot_entry({**BLOCKS_SEEDED, "extra": "ignored"}, CLOCK_TS))
|
||||
case("k_snapshot", "k-snapshot-newlines",
|
||||
{"blocks": {**BLOCKS_EMPTY, "project": "a\nb\tc\r\nd"}, "when": CLOCK_TS},
|
||||
dl._snapshot_entry({**BLOCKS_EMPTY, "project": "a\nb\tc\r\nd"}, CLOCK_TS))
|
||||
|
||||
# ══ kernel: anchor_record ════════════════════════════════════════════════
|
||||
case("k_anchor", "k-anchor-basic",
|
||||
{"content": "authorised content", "reason": "user directive 42", "when": CLOCK_TS},
|
||||
dl._anchor_record("authorised content", "user directive 42", CLOCK_TS))
|
||||
case("k_anchor", "k-anchor-unicode-empty",
|
||||
{"content": "", "reason": "π ✓", "when": CLOCK_TS},
|
||||
dl._anchor_record("", "π ✓", CLOCK_TS))
|
||||
|
||||
# ══ kernel: check_alerts ═════════════════════════════════════════════════
|
||||
EMPTY_BLOCKS = {label: "" for label in dl.BLOCKS}
|
||||
|
||||
def alert_ledger(anchors=None, snap_blocks=None):
|
||||
ledger = {}
|
||||
if anchors is not None:
|
||||
ledger["anchors"] = anchors
|
||||
if snap_blocks is not None:
|
||||
ledger["last_snapshot"] = {"when": CLOCK_TS, "blocks": snap_blocks}
|
||||
return ledger
|
||||
|
||||
# unanchored growth over the limit
|
||||
case("k_alerts", "k-alerts-unanchored-over-limit",
|
||||
{"ledger": alert_ledger(snap_blocks={"project": {"size": 100, "content": "old\nlines"}}),
|
||||
"blocks": {**EMPTY_BLOCKS, "project": "old\nlines\n" + "x" * 60}},
|
||||
dl._check_alerts(alert_ledger(snap_blocks={"project": {"size": 100, "content": "old\nlines"}}),
|
||||
{**EMPTY_BLOCKS, "project": "old\nlines\n" + "x" * 60}))
|
||||
# anchored growth within the limit
|
||||
_anchor = {"sha": "a" * 64, "size": 100, "content": "base\ncontent"}
|
||||
case("k_alerts", "k-alerts-anchored-within-limit",
|
||||
{"ledger": alert_ledger(anchors={"operating": _anchor}),
|
||||
"blocks": {**EMPTY_BLOCKS, "operating": "base\ncontent\nplus a bit"}},
|
||||
dl._check_alerts(alert_ledger(anchors={"operating": dict(_anchor)}),
|
||||
{**EMPTY_BLOCKS, "operating": "base\ncontent\nplus a bit"}))
|
||||
# anchored growth over the limit with root-cause diff
|
||||
case("k_alerts", "k-alerts-anchored-over-limit-diff",
|
||||
{"ledger": alert_ledger(anchors={"project": _anchor}),
|
||||
"blocks": {**EMPTY_BLOCKS, "project": "base\ncontent\n" + "y" * 80}},
|
||||
dl._check_alerts(alert_ledger(anchors={"project": dict(_anchor)}),
|
||||
{**EMPTY_BLOCKS, "project": "base\ncontent\n" + "y" * 80}))
|
||||
# constitution content drift: any change trips, even tiny
|
||||
case("k_alerts", "k-alerts-constitution-content-drift",
|
||||
{"ledger": alert_ledger(anchors={"constitution": _anchor}),
|
||||
"blocks": {**EMPTY_BLOCKS, "constitution": "base\ncontent\n!"}},
|
||||
dl._check_alerts(alert_ledger(anchors={"constitution": dict(_anchor)}),
|
||||
{**EMPTY_BLOCKS, "constitution": "base\ncontent\n!"}))
|
||||
# constitution size drift without content change -> pct path only
|
||||
case("k_alerts", "k-alerts-constitution-no-anchor-sha-pct-path",
|
||||
{"ledger": alert_ledger(anchors={"constitution": {"sha": "b" * 64, "size": 10, "content": "tiny"}}),
|
||||
"blocks": {**EMPTY_BLOCKS, "constitution": "tiny"}},
|
||||
dl._check_alerts(alert_ledger(anchors={"constitution": {"sha": "b" * 64, "size": 10, "content": "tiny"}}),
|
||||
{**EMPTY_BLOCKS, "constitution": "tiny"}))
|
||||
# empty ledger: nothing to measure against
|
||||
case("k_alerts", "k-alerts-empty-ledger-quiet",
|
||||
{"ledger": {}, "blocks": EMPTY_BLOCKS},
|
||||
dl._check_alerts({}, dict(EMPTY_BLOCKS)))
|
||||
# negative delta (shrink) measured against prev
|
||||
case("k_alerts", "k-alerts-shrink-over-limit",
|
||||
{"ledger": alert_ledger(snap_blocks={"human": {"size": 100, "content": "lots of text"}}),
|
||||
"blocks": {**EMPTY_BLOCKS, "human": "short"}},
|
||||
dl._check_alerts(alert_ledger(snap_blocks={"human": {"size": 100, "content": "lots of text"}}),
|
||||
{**EMPTY_BLOCKS, "human": "short"}))
|
||||
# rounding: 3 -> 4 chars, pct 1/3
|
||||
case("k_alerts", "k-alerts-rounds-pct",
|
||||
{"ledger": alert_ledger(snap_blocks={"human": {"size": 3, "content": "abc"}}),
|
||||
"blocks": {**EMPTY_BLOCKS, "human": "abcd"}},
|
||||
dl._check_alerts(alert_ledger(snap_blocks={"human": {"size": 3, "content": "abc"}}),
|
||||
{**EMPTY_BLOCKS, "human": "abcd"}))
|
||||
# multi-alert BLOCKS order
|
||||
_multi = alert_ledger(snap_blocks={"persona": {"size": 5, "content": "p one"},
|
||||
"human": {"size": 5, "content": "h one"}})
|
||||
case("k_alerts", "k-alerts-multi-blocks-order",
|
||||
{"ledger": _multi,
|
||||
"blocks": {**EMPTY_BLOCKS, "persona": "p one grown", "human": "h one grown"}},
|
||||
dl._check_alerts(alert_ledger({"persona": {"size": 5, "content": "p one"},
|
||||
"human": {"size": 5, "content": "h one"}}),
|
||||
{**EMPTY_BLOCKS, "persona": "p one grown", "human": "h one grown"}))
|
||||
|
||||
# ══ kernel: auto_fix_decision ════════════════════════════════════════════
|
||||
for i, (label, anchored, has_good) in enumerate([
|
||||
("constitution", False, False),
|
||||
("constitution", True, True),
|
||||
("operating", True, False),
|
||||
("persona", False, True),
|
||||
("human", False, False),
|
||||
]):
|
||||
case("k_auto_fix", f"k-auto-fix-{i}-{label}-{int(anchored)}{int(has_good)}",
|
||||
{"label": label, "anchored": anchored, "has_last_good": has_good},
|
||||
dl._auto_fix_decision(label, anchored, has_good))
|
||||
|
||||
# ══ kernel: fix_decision ═════════════════════════════════════════════════
|
||||
_fix_ledger = {"last_snapshot": {"when": CLOCK_TS, "blocks": {
|
||||
"project": {"sha": "c" * 64, "size": 10, "content": "good content"},
|
||||
"human": {"size": 4, "content": "no sha key"},
|
||||
}}, "anchors": {"constitution": {"sha": "d" * 64}}}
|
||||
case("k_fix", "k-fix-no-last-good", {"ledger": {}, "label": "human", "cur_sha": "a" * 64},
|
||||
dl._fix_decision({}, "human", "a" * 64))
|
||||
case("k_fix", "k-fix-anchored-refuses", {"ledger": copy.deepcopy(_fix_ledger), "label": "constitution", "cur_sha": "x"},
|
||||
dl._fix_decision(copy.deepcopy(_fix_ledger), "constitution", "x"))
|
||||
case("k_fix", "k-fix-sha-matches", {"ledger": copy.deepcopy(_fix_ledger), "label": "project", "cur_sha": "c" * 64},
|
||||
dl._fix_decision(copy.deepcopy(_fix_ledger), "project", "c" * 64))
|
||||
case("k_fix", "k-fix-restores", {"ledger": copy.deepcopy(_fix_ledger), "label": "project", "cur_sha": "e" * 64},
|
||||
dl._fix_decision(copy.deepcopy(_fix_ledger), "project", "e" * 64))
|
||||
case("k_fix", "k-fix-meta-without-sha", {"ledger": copy.deepcopy(_fix_ledger), "label": "human", "cur_sha": "z"},
|
||||
dl._fix_decision(copy.deepcopy(_fix_ledger), "human", "z"))
|
||||
|
||||
# ══ write ════════════════════════════════════════════════════════════════
|
||||
truth = {
|
||||
"frozen": "drift_ledger sha/diff/ledger_path/load_ledger/save_ledger/"
|
||||
"read_block/journal/surface_alert/snapshot/anchor/check/"
|
||||
"auto_fix/fix/restore_topic/handle_hook/main",
|
||||
"oracle": "src/rune/drift_ledger.py",
|
||||
"note": "Clock frozen at 2031-01-01T00:00:00+00:00 (dl.datetime seam); "
|
||||
"store/journal/status paths isolated per case via INDEX_FILE/DB/"
|
||||
"JOURNAL_DIR module globals and the memory_env.memory_dir() seam. "
|
||||
"SQLite read/write seams and file IO stay in the facade by design; "
|
||||
"the deterministic cores (sha, diff, alert loop, snapshot/anchor "
|
||||
"assembly, auto-fix routing, fix decision, ledger path) cross the "
|
||||
"doc-envelope FFI.",
|
||||
}
|
||||
for gname in GROUPS:
|
||||
truth[gname] = groups[gname]
|
||||
|
||||
out = Path(os.environ.get("DL_TRUTH_OUT", "/tmp/dl_truth.json"))
|
||||
out.write_text(json.dumps(truth, indent=1, ensure_ascii=False) + "\n")
|
||||
counts = ", ".join(f"{len(groups[g])} {g}" for g in GROUPS)
|
||||
print(f"froze {sum(len(v) for v in groups.values())} cases -> {out}: {counts}")
|
||||
@@ -0,0 +1,473 @@
|
||||
#!/usr/bin/env python3
|
||||
"""gen_drift_resume_truth.py — freeze/replay parity truth for drift_resume
|
||||
(dichotomy wave, #2479 tranche).
|
||||
|
||||
Freeze mode (default): exercises EVERY public function of the LIVE Python
|
||||
oracle (src/rune/drift_resume.py) across every branch and records
|
||||
(kernel_input, expected_kernel_output) pairs into
|
||||
src/rust/drift_resume/src/frozen_dr_resume_truth.json.
|
||||
|
||||
Replay mode (--replay): re-runs the SAME scenarios with the Rust core
|
||||
delegation active and asserts both kernel-level (loader direct) and
|
||||
facade-level (oracle functions delegate) parity against the frozen file.
|
||||
|
||||
Poisoned-truth law: freeze pins RUNE_DR_CORE=python BEFORE any import.
|
||||
Replay leaves it UNSET so delegation is live (the real parity claim);
|
||||
the script aborts replay if the Rust core is not built.
|
||||
|
||||
Boundary law: the observer callable (reconcile) and the FILESYSTEM walk
|
||||
(detect_structural_drift: exists + the moved-twin rglob) stay in the
|
||||
Python facade — the kernel is a pure function of their results. Kernel
|
||||
inputs:
|
||||
* dr_classify_drift {claim, observation} → {record}
|
||||
* dr_reconcile {claims, observations} → {resume, drifts, blocking}
|
||||
* dr_detect_structural_drift {entities, files, dirs} → {drifts}
|
||||
where files/dirs are the facade's SORTED relative listings of
|
||||
repo_root (deterministic superset of the oracle's rglob order;
|
||||
moved-twin scenarios use unique basenames so the oracle's
|
||||
first-rglob-match is order-independent).
|
||||
|
||||
Deep-copy law: every input is deep-copied before it reaches the oracle
|
||||
(the kernel must never observe mutation aliasing).
|
||||
"""
|
||||
import copy
|
||||
import datetime
|
||||
import json
|
||||
import os
|
||||
import pathlib
|
||||
import sys
|
||||
import tempfile
|
||||
import types
|
||||
|
||||
REPO = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
TRUTH_PATH = os.path.join(REPO, "src", "rust", "drift_resume", "src",
|
||||
"frozen_dr_resume_truth.json")
|
||||
REPLAY = "--replay" in sys.argv
|
||||
|
||||
os.environ["RUNE_DR_CORE"] = "python" # poisoned-truth law (no-op pre-port,
|
||||
# load-bearing post-port: the freeze must record the PYTHON oracle's truth)
|
||||
|
||||
sys.path.insert(0, os.path.join(REPO, "src"))
|
||||
sys.path.insert(0, os.path.join(REPO, "src", "rune"))
|
||||
|
||||
import drift_resume as oracle_mod # noqa: E402
|
||||
|
||||
if REPLAY:
|
||||
del os.environ["RUNE_DR_CORE"] # delegation live for the replay pass
|
||||
from rune import dr_loader # noqa: E402 (kernel-level replay side)
|
||||
if not dr_loader.available():
|
||||
print("REPLAY ABORT: Rust drift_resume core not built/available")
|
||||
sys.exit(2)
|
||||
|
||||
|
||||
# ── classify_drift ───────────────────────────────────────────────────────────
|
||||
|
||||
def _cd_case(rec, name, claim, observation):
|
||||
got = oracle_mod.classify_drift(copy.deepcopy(claim),
|
||||
copy.deepcopy(observation))
|
||||
rec(name, {"claim": claim, "observation": observation}, got,
|
||||
{"record": got})
|
||||
|
||||
|
||||
def c_cd_matches_true_stale_ok(rec):
|
||||
_cd_case(rec, "cd-matches-true-stale-ok-expected",
|
||||
{"claim": "green", "stale_ok": True},
|
||||
{"actual": "", "checked_by": "pytest", "matches": True})
|
||||
|
||||
|
||||
def c_cd_matches_true_confirmed(rec):
|
||||
_cd_case(rec, "cd-matches-true-confirmed",
|
||||
{"claim": "green"},
|
||||
{"actual": "", "checked_by": "pytest", "matches": True})
|
||||
|
||||
|
||||
def c_cd_matches_true_stale_falsy(rec):
|
||||
_cd_case(rec, "cd-matches-true-stale-falsy-confirmed",
|
||||
{"claim": "green", "stale_ok": False},
|
||||
{"actual": "", "checked_by": "pytest", "matches": True})
|
||||
|
||||
|
||||
def c_cd_matches_true_stale_truthy_int(rec):
|
||||
_cd_case(rec, "cd-matches-true-stale-truthy-int-expected",
|
||||
{"claim": "green", "stale_ok": 1},
|
||||
{"actual": "", "checked_by": "pytest", "matches": True})
|
||||
|
||||
|
||||
def c_cd_matches_none_unknown(rec):
|
||||
_cd_case(rec, "cd-matches-none-unknown",
|
||||
{"claim": "x"},
|
||||
{"actual": None, "checked_by": "pytest", "matches": None})
|
||||
|
||||
|
||||
def c_cd_matches_missing_unknown(rec):
|
||||
_cd_case(rec, "cd-matches-missing-unknown",
|
||||
{"claim": "x"},
|
||||
{"actual": None, "checked_by": "probe"})
|
||||
|
||||
|
||||
def c_cd_matches_false_user_locked(rec):
|
||||
_cd_case(rec, "cd-matches-false-user-locked-conflicting",
|
||||
{"claim": "layout", "user_locked": True},
|
||||
{"actual": "layout is Y", "checked_by": "ls",
|
||||
"matches": False})
|
||||
|
||||
|
||||
def c_cd_matches_false_material(rec):
|
||||
_cd_case(rec, "cd-matches-false-material",
|
||||
{"claim": "green"},
|
||||
{"actual": "2 tests fail", "checked_by": "pytest",
|
||||
"matches": False})
|
||||
|
||||
|
||||
def c_cd_material_missing_actual(rec):
|
||||
_cd_case(rec, "cd-material-actual-missing-empty",
|
||||
{"claim": "green"},
|
||||
{"checked_by": "pytest", "matches": False})
|
||||
|
||||
|
||||
def c_cd_material_actual_null(rec):
|
||||
# Python str(None) → "None" in the f-string
|
||||
_cd_case(rec, "cd-material-actual-null-str-none",
|
||||
{"claim": "green"},
|
||||
{"actual": None, "checked_by": "pytest", "matches": False})
|
||||
|
||||
|
||||
def c_cd_material_actual_int(rec):
|
||||
_cd_case(rec, "cd-material-actual-int",
|
||||
{"claim": "count"},
|
||||
{"actual": 3, "checked_by": "wc", "matches": False})
|
||||
|
||||
|
||||
def c_cd_unknown_checked_by_missing(rec):
|
||||
_cd_case(rec, "cd-unknown-checked-by-missing-qmark",
|
||||
{"claim": "x"},
|
||||
{"matches": None})
|
||||
|
||||
|
||||
def c_cd_unknown_checked_by_empty(rec):
|
||||
# present-but-empty checked_by renders verbatim (get default only on
|
||||
# a MISSING key)
|
||||
_cd_case(rec, "cd-unknown-checked-by-empty-string",
|
||||
{"claim": "x"},
|
||||
{"checked_by": "", "matches": None})
|
||||
|
||||
|
||||
def c_cd_claim_missing(rec):
|
||||
_cd_case(rec, "cd-claim-key-missing-empty",
|
||||
{},
|
||||
{"actual": "", "checked_by": "pytest", "matches": True})
|
||||
|
||||
|
||||
# ── reconcile ────────────────────────────────────────────────────────────────
|
||||
|
||||
def _rc_case(rec, name, claims, observer):
|
||||
claims_c = copy.deepcopy(claims)
|
||||
got = oracle_mod.reconcile(claims_c, observer)
|
||||
observations = [observer(c) for c in claims_c]
|
||||
rec(name, {"claims": claims_c, "observations": observations}, got, got)
|
||||
|
||||
|
||||
def c_rc_empty(rec):
|
||||
_rc_case(rec, "rc-empty-claims-resume", [], lambda c: {"matches": True})
|
||||
|
||||
|
||||
def c_rc_all_confirmed(rec):
|
||||
_rc_case(rec, "rc-all-confirmed-resume",
|
||||
[{"claim": "a"}, {"claim": "b"}],
|
||||
lambda c: {"actual": "", "checked_by": "pytest",
|
||||
"matches": True})
|
||||
|
||||
|
||||
def c_rc_material_blocks(rec):
|
||||
_rc_case(rec, "rc-material-blocks",
|
||||
[{"claim": "green"}],
|
||||
lambda c: {"actual": "2 tests fail", "checked_by": "pytest",
|
||||
"matches": False})
|
||||
|
||||
|
||||
def c_rc_unknown_blocks(rec):
|
||||
_rc_case(rec, "rc-unknown-blocks",
|
||||
[{"claim": "x"}],
|
||||
lambda c: {"actual": None, "checked_by": "probe",
|
||||
"matches": None})
|
||||
|
||||
|
||||
def c_rc_conflicting_blocks(rec):
|
||||
_rc_case(rec, "rc-conflicting-blocks",
|
||||
[{"claim": "layout", "user_locked": True}],
|
||||
lambda c: {"actual": "layout is Y", "checked_by": "ls",
|
||||
"matches": False})
|
||||
|
||||
|
||||
def c_rc_expected_not_blocking(rec):
|
||||
_rc_case(rec, "rc-expected-not-blocking",
|
||||
[{"claim": "green", "stale_ok": True}],
|
||||
lambda c: {"actual": "", "checked_by": "pytest",
|
||||
"matches": True})
|
||||
|
||||
|
||||
def c_rc_mixed_order(rec):
|
||||
_rc_case(rec, "rc-mixed-order-blocking-subset",
|
||||
[{"claim": "ok-1"}, {"claim": "bad"}, {"claim": "mystery"},
|
||||
{"claim": "ok-2", "stale_ok": True}],
|
||||
lambda c: ({"actual": "", "checked_by": "pytest",
|
||||
"matches": True}
|
||||
if c["claim"].startswith("ok")
|
||||
else ({"actual": None, "checked_by": "probe",
|
||||
"matches": None}
|
||||
if c["claim"] == "mystery"
|
||||
else {"actual": "reality differs",
|
||||
"checked_by": "pytest",
|
||||
"matches": False})))
|
||||
|
||||
|
||||
# ── detect_structural_drift ──────────────────────────────────────────────────
|
||||
|
||||
def _dsd_case(rec, name, entities, files_present, dirs_present=()):
|
||||
root = pathlib.Path(tempfile.mkdtemp(prefix="dsd_truth_"))
|
||||
for rel in files_present:
|
||||
p = root / rel
|
||||
p.parent.mkdir(parents=True, exist_ok=True)
|
||||
p.write_text("# twin\n", encoding="utf-8")
|
||||
for rel in dirs_present:
|
||||
(root / rel).mkdir(parents=True, exist_ok=True)
|
||||
model = types.SimpleNamespace(doc={"entities": copy.deepcopy(entities)})
|
||||
got = oracle_mod.detect_structural_drift(model, root)
|
||||
files = sorted(str(p.relative_to(root))
|
||||
for p in root.rglob("*") if p.is_file())
|
||||
dirs = sorted(str(p.relative_to(root))
|
||||
for p in root.rglob("*") if p.is_dir())
|
||||
rec(name, {"entities": entities, "files": files, "dirs": dirs}, got,
|
||||
{"drifts": got})
|
||||
|
||||
|
||||
def c_dsd_source_present(rec):
|
||||
_dsd_case(rec, "dsd-source-present-no-drift",
|
||||
[{"id": "component:widget", "kind": "component",
|
||||
"ext": {"source": "src/widget.py"}}],
|
||||
["src/widget.py"])
|
||||
|
||||
|
||||
def c_dsd_non_component_skipped(rec):
|
||||
_dsd_case(rec, "dsd-non-component-skipped",
|
||||
[{"id": "capability:x", "kind": "capability",
|
||||
"ext": {"source": "src/missing.py"}}],
|
||||
[])
|
||||
|
||||
|
||||
def c_dsd_no_source_skipped(rec):
|
||||
_dsd_case(rec, "dsd-no-source-skipped",
|
||||
[{"id": "component:x", "kind": "component", "ext": {}}],
|
||||
[])
|
||||
|
||||
|
||||
def c_dsd_missing_no_twin(rec):
|
||||
_dsd_case(rec, "dsd-missing-source-absent",
|
||||
[{"id": "component:widget", "kind": "component",
|
||||
"ext": {"source": "src/widget.py"}}],
|
||||
["src/other.py"])
|
||||
|
||||
|
||||
def c_dsd_moved_twin(rec):
|
||||
_dsd_case(rec, "dsd-moved-twin-material",
|
||||
[{"id": "component:widget", "kind": "component",
|
||||
"ext": {"source": "src/widget.py"}}],
|
||||
["src/widgets/widget.py"])
|
||||
|
||||
|
||||
def c_dsd_moved_twin_locked(rec):
|
||||
_dsd_case(rec, "dsd-moved-twin-user-locked-conflicting",
|
||||
[{"id": "component:widget", "kind": "component",
|
||||
"ext": {"source": "src/widget.py"},
|
||||
"identities": {"user_locked": True}}],
|
||||
["src/widgets/widget.py"])
|
||||
|
||||
|
||||
def c_dsd_missing_locked(rec):
|
||||
_dsd_case(rec, "dsd-missing-source-user-locked-conflicting",
|
||||
[{"id": "component:widget", "kind": "component",
|
||||
"ext": {"source": "src/widget.py"},
|
||||
"identities": {"user_locked": True}}],
|
||||
[])
|
||||
|
||||
|
||||
def c_dsd_ext_empty_falls_to_identities(rec):
|
||||
# ext.source == "" is falsy → identities.source wins (the or-chain)
|
||||
_dsd_case(rec, "dsd-ext-source-empty-falls-to-identities",
|
||||
[{"id": "component:alpha", "kind": "component",
|
||||
"ext": {"source": ""},
|
||||
"identities": {"source": "src/alpha.py"}}],
|
||||
["src/alpha.py"])
|
||||
|
||||
|
||||
def c_dsd_identities_source(rec):
|
||||
_dsd_case(rec, "dsd-identities-source-missing-file",
|
||||
[{"id": "component:beta", "kind": "component",
|
||||
"identities": {"source": "src/beta.py"}}],
|
||||
[])
|
||||
|
||||
|
||||
def c_dsd_multiple_entities_order(rec):
|
||||
_dsd_case(rec, "dsd-multiple-entities-order-preserved",
|
||||
[{"id": "component:a", "kind": "component",
|
||||
"ext": {"source": "src/a.py"}},
|
||||
{"id": "component:b", "kind": "component",
|
||||
"ext": {"source": "src/b.py"}},
|
||||
{"id": "component:c", "kind": "component",
|
||||
"ext": {"source": "src/c.py"}}],
|
||||
["src/c.py"])
|
||||
|
||||
|
||||
def c_dsd_nested_moved(rec):
|
||||
_dsd_case(rec, "dsd-nested-moved-twin",
|
||||
[{"id": "component:deep", "kind": "component",
|
||||
"ext": {"source": "src/deep.py"}}],
|
||||
["a/b/c/deep.py"])
|
||||
|
||||
|
||||
def c_dsd_source_is_dir(rec):
|
||||
# a DIRECTORY at the source path satisfies expected.exists() — no drift
|
||||
_dsd_case(rec, "dsd-source-is-dir-no-drift",
|
||||
[{"id": "component:pkg", "kind": "component",
|
||||
"ext": {"source": "src/pkg"}}],
|
||||
[], ["src/pkg"])
|
||||
|
||||
|
||||
def c_dsd_ext_null(rec):
|
||||
_dsd_case(rec, "dsd-ext-null-identities-fallback",
|
||||
[{"id": "component:gamma", "kind": "component",
|
||||
"ext": None,
|
||||
"identities": {"source": "src/gamma.py"}}],
|
||||
["src/gamma.py"])
|
||||
|
||||
|
||||
def c_dsd_user_locked_truthy_non_bool(rec):
|
||||
# `is True` — a truthy non-True user_locked is NOT locked
|
||||
_dsd_case(rec, "dsd-user-locked-truthy-non-bool-material",
|
||||
[{"id": "component:widget", "kind": "component",
|
||||
"ext": {"source": "src/widget.py"},
|
||||
"identities": {"user_locked": "yes"}}],
|
||||
[])
|
||||
|
||||
|
||||
# ── driver ───────────────────────────────────────────────────────────────────
|
||||
|
||||
CLASSIFY = [
|
||||
c_cd_matches_true_stale_ok, c_cd_matches_true_confirmed,
|
||||
c_cd_matches_true_stale_falsy, c_cd_matches_true_stale_truthy_int,
|
||||
c_cd_matches_none_unknown, c_cd_matches_missing_unknown,
|
||||
c_cd_matches_false_user_locked, c_cd_matches_false_material,
|
||||
c_cd_material_missing_actual, c_cd_material_actual_null,
|
||||
c_cd_material_actual_int, c_cd_unknown_checked_by_missing,
|
||||
c_cd_unknown_checked_by_empty, c_cd_claim_missing,
|
||||
]
|
||||
RECONCILE = [
|
||||
c_rc_empty, c_rc_all_confirmed, c_rc_material_blocks,
|
||||
c_rc_unknown_blocks, c_rc_conflicting_blocks, c_rc_expected_not_blocking,
|
||||
c_rc_mixed_order,
|
||||
]
|
||||
DETECT = [
|
||||
c_dsd_source_present, c_dsd_non_component_skipped, c_dsd_no_source_skipped,
|
||||
c_dsd_missing_no_twin, c_dsd_moved_twin, c_dsd_moved_twin_locked,
|
||||
c_dsd_missing_locked, c_dsd_ext_empty_falls_to_identities,
|
||||
c_dsd_identities_source, c_dsd_multiple_entities_order,
|
||||
c_dsd_nested_moved, c_dsd_source_is_dir, c_dsd_ext_null,
|
||||
c_dsd_user_locked_truthy_non_bool,
|
||||
]
|
||||
|
||||
GROUPS = [("classify_drift", CLASSIFY), ("reconcile", RECONCILE),
|
||||
("detect_structural_drift", DETECT)]
|
||||
|
||||
KERNEL = {
|
||||
"classify_drift": "dr_classify_drift",
|
||||
"reconcile": "dr_reconcile",
|
||||
"detect_structural_drift": "dr_detect_structural_drift",
|
||||
}
|
||||
|
||||
UNWRAP = {
|
||||
"classify_drift": lambda w: w["record"],
|
||||
"reconcile": lambda w: w,
|
||||
"detect_structural_drift": lambda w: w["drifts"],
|
||||
}
|
||||
|
||||
|
||||
def canon(x) -> str:
|
||||
return json.dumps(json.loads(json.dumps(x, default=str)), sort_keys=True)
|
||||
|
||||
|
||||
def main():
|
||||
frozen = None
|
||||
if REPLAY:
|
||||
with open(TRUTH_PATH) as f:
|
||||
frozen = json.load(f)["cases"]
|
||||
|
||||
groups = {}
|
||||
for group, cases in GROUPS:
|
||||
recs = []
|
||||
|
||||
def rec(name, inp, oracle_ret, expected, _r=recs):
|
||||
entry = {"name": name, "input": inp, "return": oracle_ret,
|
||||
"expected": expected}
|
||||
if REPLAY:
|
||||
old = next(c for c in frozen[group] if c["name"] == name)
|
||||
for key in ("input", "return", "expected"):
|
||||
got, want = entry[key], old[key]
|
||||
if canon(got) != canon(want):
|
||||
print(f"FACADE DIVERGE [{group}] {name} {key}")
|
||||
print(" got: ",
|
||||
json.dumps(got, sort_keys=True, default=str)[:600])
|
||||
print(" want:",
|
||||
json.dumps(want, sort_keys=True, default=str)[:600])
|
||||
recs.append({"name": name, "diverged": key})
|
||||
return
|
||||
_r.append(entry)
|
||||
|
||||
for c in cases:
|
||||
c(rec)
|
||||
groups[group] = recs
|
||||
|
||||
total = sum(len(v) for v in groups.values())
|
||||
if REPLAY:
|
||||
facade_failures = sum(1 for v in groups.values()
|
||||
for c in v if "diverged" in c)
|
||||
|
||||
failures = 0
|
||||
checked = 0
|
||||
for group, cases in frozen.items():
|
||||
for case in cases:
|
||||
checked += 1
|
||||
got = getattr(dr_loader, group)(**copy.deepcopy(case["input"]))
|
||||
want = UNWRAP[group](case["expected"])
|
||||
if canon(got) != canon(want):
|
||||
failures += 1
|
||||
print(f"KERNEL DIVERGE [{group}] {case['name']}")
|
||||
print(" got: ", json.dumps(got, sort_keys=True)[:600])
|
||||
print(" want:", json.dumps(want, sort_keys=True)[:600])
|
||||
print(f"replay: facade {total - facade_failures}/{total}, "
|
||||
f"kernel {checked - failures}/{checked}")
|
||||
return 1 if (failures or facade_failures) else 0
|
||||
|
||||
doc = {
|
||||
"frozen": datetime.datetime.now(datetime.timezone.utc).isoformat(),
|
||||
"oracle": "src/rune/drift_resume.py",
|
||||
"pin": "RUNE_DR_CORE=python",
|
||||
"note": "Frozen via scripts/gen_drift_resume_truth.py against the "
|
||||
"live Python oracle. The observer callable (reconcile) and "
|
||||
"the filesystem walk (detect_structural_drift: exists + "
|
||||
"moved-twin rglob) stay in the Python facade; the kernel is "
|
||||
"a pure function of their results (observations / sorted "
|
||||
"relative-path listings). Moved-twin scenarios use unique "
|
||||
"basenames so first-match semantics are order-independent. "
|
||||
"Battery embeds this VERBATIM.",
|
||||
"cases": groups,
|
||||
}
|
||||
os.makedirs(os.path.dirname(TRUTH_PATH), exist_ok=True)
|
||||
with open(TRUTH_PATH, "w") as f:
|
||||
json.dump(doc, f, indent=1)
|
||||
f.write("\n")
|
||||
print(f"frozen {total} cases → {os.path.relpath(TRUTH_PATH, REPO)}")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
@@ -0,0 +1,179 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Freeze the execution_trace oracle truth (dichotomy wave, #2479).
|
||||
|
||||
Generator law: RUNE_ET_CORE=python pinned, every input deep-copied, every
|
||||
public function exercised across ALL branches:
|
||||
* build_receipt — defaults, assigned_model vs payload.model,
|
||||
outcome from payload.status vs task.status, completed_at fallbacks
|
||||
* _read_outcome_events — empty journal, malformed lines, foreign events,
|
||||
empty task_id, duplicate task_id (last wins), missing file
|
||||
* trace_task — no outcome + no record (None), journal-only, record-only,
|
||||
both, missing journal with record
|
||||
"""
|
||||
import copy
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
|
||||
REPO = Path(__file__).resolve().parents[1]
|
||||
sys.path.insert(0, str(REPO / "src"))
|
||||
sys.path.insert(0, str(REPO / "src" / "rune"))
|
||||
os.environ["RUNE_ET_CORE"] = "python"
|
||||
|
||||
import execution_trace as et # noqa: E402
|
||||
|
||||
cases_build = []
|
||||
cases_read = []
|
||||
cases_trace = []
|
||||
|
||||
|
||||
def case_build(name, task, payload):
|
||||
task = copy.deepcopy(task)
|
||||
payload = copy.deepcopy(payload)
|
||||
cases_build.append({
|
||||
"name": name,
|
||||
"args": {"task": task, "outcome_payload": payload},
|
||||
"expect": et.build_receipt(copy.deepcopy(task), copy.deepcopy(payload)),
|
||||
})
|
||||
|
||||
|
||||
def case_read(name, lines):
|
||||
lines = copy.deepcopy(lines)
|
||||
if lines is None:
|
||||
# missing file branch (OSError) — facade-level, no kernel lines
|
||||
fd, path = tempfile.mkstemp(suffix=".jsonl")
|
||||
os.close(fd)
|
||||
os.unlink(path)
|
||||
expect = et._read_outcome_events(path)
|
||||
else:
|
||||
with tempfile.NamedTemporaryFile("w", suffix=".jsonl",
|
||||
delete=False, encoding="utf-8") as f:
|
||||
f.write("\n".join(lines) + "\n")
|
||||
path = f.name
|
||||
expect = et._read_outcome_events(path)
|
||||
cases_read.append({
|
||||
"name": name,
|
||||
"lines": lines,
|
||||
"expect": expect,
|
||||
})
|
||||
|
||||
|
||||
def case_trace(name, task_id, task_record, lines):
|
||||
task_id = copy.deepcopy(task_id)
|
||||
task_record = copy.deepcopy(task_record)
|
||||
lines = copy.deepcopy(lines)
|
||||
if lines is None:
|
||||
fd, path = tempfile.mkstemp(suffix=".jsonl")
|
||||
os.close(fd)
|
||||
os.unlink(path)
|
||||
else:
|
||||
with tempfile.NamedTemporaryFile("w", suffix=".jsonl",
|
||||
delete=False, encoding="utf-8") as f:
|
||||
f.write("\n".join(lines) + "\n")
|
||||
path = f.name
|
||||
expect = et.trace_task(task_id, copy.deepcopy(task_record), path)
|
||||
cases_trace.append({
|
||||
"name": name,
|
||||
"args": {"task_id": task_id, "task_record": task_record,
|
||||
"lines": lines},
|
||||
"expect": expect,
|
||||
})
|
||||
|
||||
|
||||
# ── build_receipt: every branch ─────────────────────────────────────────────
|
||||
case_build("minimal-task", {}, None)
|
||||
case_build("full-task-no-payload", {
|
||||
"task_id": "t1", "description": "do the thing", "status": "running",
|
||||
"assigned_model": "qwen2.5:3b", "memories_used": ["m1", "m2"],
|
||||
"rules_used": ["r1"], "capability": "forge", "actions": ["a", "b"],
|
||||
"completed_at": "2026-10-13T00:00:00Z",
|
||||
}, None)
|
||||
case_build("payload-fills-gaps", {
|
||||
"task_id": "t2", "description": "", "status": "done",
|
||||
}, {"model": "qwen2.5:7b", "status": "done", "completed_at": "2026-10-13T01:00:00Z"})
|
||||
case_build("assigned-model-wins", {
|
||||
"task_id": "t3", "status": "running", "assigned_model": "local",
|
||||
}, {"model": "cloud", "status": "running"})
|
||||
case_build("task-completed-at-wins", {
|
||||
"task_id": "t4", "status": "done", "completed_at": "T-task",
|
||||
}, {"status": "done", "completed_at": "T-payload"})
|
||||
case_build("empty-payload", {"task_id": "t5", "status": "done"}, {})
|
||||
case_build("empty-status-no-payload", {"task_id": "t6"}, None)
|
||||
|
||||
# ── _read_outcome_events: every branch ──────────────────────────────────────
|
||||
case_read("empty-journal", [])
|
||||
case_read("single-outcome", [
|
||||
json.dumps({"event_type": "task.outcome",
|
||||
"payload": {"task_id": "t1", "status": "done"}}),
|
||||
])
|
||||
case_read("malformed-lines-skipped", [
|
||||
"{not json",
|
||||
"",
|
||||
json.dumps({"event_type": "task.outcome",
|
||||
"payload": {"task_id": "t2", "status": "done"}}),
|
||||
])
|
||||
case_read("foreign-events-skipped", [
|
||||
json.dumps({"event_type": "task.started",
|
||||
"payload": {"task_id": "t3", "status": "running"}}),
|
||||
json.dumps({"event_type": "task.outcome",
|
||||
"payload": {"task_id": "t4", "status": "done"}}),
|
||||
json.dumps({"event_type": "task.outcome"}),
|
||||
])
|
||||
case_read("empty-task-id-skipped", [
|
||||
json.dumps({"event_type": "task.outcome",
|
||||
"payload": {"task_id": "", "status": "done"}}),
|
||||
json.dumps({"event_type": "task.outcome",
|
||||
"payload": {"status": "done"}}),
|
||||
json.dumps({"event_type": "task.outcome",
|
||||
"payload": {"task_id": "t5", "status": "done"}}),
|
||||
])
|
||||
case_read("duplicate-task-id-last-wins", [
|
||||
json.dumps({"event_type": "task.outcome",
|
||||
"payload": {"task_id": "t6", "status": "first"}}),
|
||||
json.dumps({"event_type": "task.outcome",
|
||||
"payload": {"task_id": "t6", "status": "second"}}),
|
||||
])
|
||||
case_read("missing-file", None)
|
||||
|
||||
# ── trace_task: every branch ────────────────────────────────────────────────
|
||||
JOURNAL = [
|
||||
json.dumps({"event_type": "task.outcome",
|
||||
"payload": {"task_id": "t1", "status": "done",
|
||||
"model": "qwen2.5:3b",
|
||||
"completed_at": "2026-10-13T02:00:00Z"}}),
|
||||
json.dumps({"event_type": "task.outcome",
|
||||
"payload": {"task_id": "t2", "status": "failed"}}),
|
||||
]
|
||||
case_trace("no-outcome-no-record", "t9", None, JOURNAL)
|
||||
case_trace("journal-only", "t1", None, JOURNAL)
|
||||
case_trace("record-only", "t9", {
|
||||
"task_id": "t9", "description": "offline", "status": "running",
|
||||
"assigned_model": "local", "memories_used": [], "rules_used": [],
|
||||
"capability": "", "actions": [], "completed_at": "",
|
||||
}, JOURNAL)
|
||||
case_trace("both-record-and-journal", "t1", {
|
||||
"task_id": "t1", "description": "both", "status": "running",
|
||||
"assigned_model": "", "memories_used": ["m"], "rules_used": [],
|
||||
"capability": "forge", "actions": ["go"], "completed_at": "",
|
||||
}, JOURNAL)
|
||||
case_trace("missing-journal-with-record", "t9", {
|
||||
"task_id": "t9", "status": "done",
|
||||
}, None)
|
||||
case_trace("empty-record-falsy-law", "t2", {}, JOURNAL)
|
||||
|
||||
truth = {
|
||||
"module": "execution_trace",
|
||||
"core_pin": "RUNE_ET_CORE=python",
|
||||
"build_receipt_cases": cases_build,
|
||||
"read_outcome_events_cases": cases_read,
|
||||
"trace_task_cases": cases_trace,
|
||||
}
|
||||
|
||||
out = REPO / "src" / "rust" / "execution_trace" / "frozen_et_truth.json"
|
||||
out.parent.mkdir(parents=True, exist_ok=True)
|
||||
out.write_text(json.dumps(truth, indent=1, sort_keys=True) + "\n",
|
||||
encoding="utf-8")
|
||||
n = len(cases_build) + len(cases_read) + len(cases_trace)
|
||||
print(f"frozen {n} cases -> {out}")
|
||||
@@ -0,0 +1,375 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Freeze the oracle truth for the GO port of src/rune/forge.py (dichotomy
|
||||
wave #2479, GO). Every public function, every branch, against the LIVE
|
||||
oracle. IO seams (subprocess, urllib, the gitea_auth credential ladder) are
|
||||
monkeypatched deterministically and every IO call is captured (argv, URL,
|
||||
headers) so the GO battery replays VERBATIM.
|
||||
|
||||
machine-dependent values are normalized:
|
||||
* subprocess argv[0] (sys.executable) is dropped — the GO port's python
|
||||
binary is a seam default ("python3"), so rows pin argv[1:] only
|
||||
* the credential ladder error message pins $HOME as <HOME>
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import contextlib
|
||||
import json
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
import types
|
||||
import urllib.request
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
sys.path.insert(0, str(ROOT / "src"))
|
||||
sys.path.insert(0, str(ROOT / "scripts"))
|
||||
|
||||
from rune import forge # noqa: E402
|
||||
import gitea_auth # noqa: E402
|
||||
|
||||
rows: list[dict] = []
|
||||
|
||||
|
||||
class FakeResponse:
|
||||
def __init__(self, body: bytes):
|
||||
self._body = body
|
||||
|
||||
def read(self) -> bytes:
|
||||
return self._body
|
||||
|
||||
def __enter__(self):
|
||||
return self
|
||||
|
||||
def __exit__(self, *exc):
|
||||
return False
|
||||
|
||||
|
||||
class CapturedRequest:
|
||||
def __init__(self, req: urllib.request.Request):
|
||||
self.method = req.get_method()
|
||||
self.url = req.full_url
|
||||
self.headers = {k.lower(): v for k, v in req.header_items()}
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def fake_http(handler):
|
||||
"""handler(captured: CapturedRequest) → body bytes | raises OSError."""
|
||||
calls = []
|
||||
|
||||
def urlopen(req, timeout=None):
|
||||
cap = CapturedRequest(req)
|
||||
calls.append({"method": cap.method, "url": cap.url,
|
||||
"headers": cap.headers})
|
||||
out = handler(cap)
|
||||
if isinstance(out, OSError):
|
||||
raise out
|
||||
return FakeResponse(out)
|
||||
|
||||
orig = urllib.request.urlopen
|
||||
urllib.request.urlopen = urlopen
|
||||
try:
|
||||
yield calls
|
||||
finally:
|
||||
urllib.request.urlopen = orig
|
||||
|
||||
|
||||
class FakeProc:
|
||||
def __init__(self, rc, stdout="", stderr=""):
|
||||
self.returncode = rc
|
||||
self.stdout = stdout
|
||||
self.stderr = stderr
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def fake_subprocess(calls):
|
||||
def run(argv, capture_output=False, text=False, **kw):
|
||||
calls.append({"argv": list(argv)})
|
||||
return FakeProc(*PENDING_RC.get("args", (0, "", "")))
|
||||
orig = subprocess.run
|
||||
subprocess.run = run
|
||||
try:
|
||||
yield calls
|
||||
finally:
|
||||
subprocess.run = orig
|
||||
|
||||
|
||||
PENDING_RC = {"args": (0, "", "")}
|
||||
|
||||
|
||||
def record(name, op, setup, want):
|
||||
rows.append({"name": name, "op": op, "setup": setup, "want": want})
|
||||
|
||||
|
||||
def clean_env(monkeypatch_env: dict):
|
||||
"""Run fn with a controlled GITEA_* environment."""
|
||||
saved = {}
|
||||
keys = ["GITEA_SERVER", "GITEA_REPO", "GITEA_TOKEN", "GITEA_TOKEN_FILE"]
|
||||
for k in keys:
|
||||
saved[k] = os.environ.pop(k, None)
|
||||
os.environ.update({k: v for k, v in monkeypatch_env.items()})
|
||||
return saved
|
||||
|
||||
|
||||
def restore_env(saved):
|
||||
for k, v in saved.items():
|
||||
if v is None:
|
||||
os.environ.pop(k, None)
|
||||
else:
|
||||
os.environ[k] = v
|
||||
|
||||
|
||||
# ── default_forge ───────────────────────────────────────────────────────────
|
||||
f = forge.default_forge()
|
||||
record("default_forge:working-adapter", "default_forge", {},
|
||||
{"server": f._server, "repo": f._repo})
|
||||
|
||||
# ── _env ────────────────────────────────────────────────────────────────────
|
||||
for name, server, repo, env in [
|
||||
("env:inherits-and-defaults", None, None, {}),
|
||||
("env:server-override", "http://local:3000", None, {}),
|
||||
("env:repo-explicit", None, "other/repo", {}),
|
||||
("env:env-server-kept-when-no-attr", None, None,
|
||||
{"GITEA_SERVER": "http://env-server"}),
|
||||
("env:env-server-overridden-by-attr", "http://attr", None,
|
||||
{"GITEA_SERVER": "http://env-server"}),
|
||||
("env:env-repo-not-clobbered", None, None, {"GITEA_REPO": "env/repo"}),
|
||||
("env:inherits-sample-var", None, None, {"RUNE_SAMPLE": "1"}),
|
||||
]:
|
||||
eff_env = dict(env)
|
||||
eff_env.setdefault("RUNE_SAMPLE", "")
|
||||
saved = clean_env(eff_env)
|
||||
os.environ["RUNE_SAMPLE"] = eff_env["RUNE_SAMPLE"]
|
||||
gf = forge.GiteaForge(server=server, repo=repo or "conductor/rune")
|
||||
built = gf._env()
|
||||
want = {k: built.get(k) for k in
|
||||
("GITEA_SERVER", "GITEA_REPO", "RUNE_SAMPLE")}
|
||||
restore_env(saved)
|
||||
record(f"_env:{name}", "env", {"server": server, "repo": repo,
|
||||
"environ": eff_env}, want)
|
||||
|
||||
# ── push ────────────────────────────────────────────────────────────────────
|
||||
for name, rc, stderr, branch in [
|
||||
("push:ok", 0, "", "feature-x"),
|
||||
("push:fail-short-stderr", 1, "rejected non-fast-forward", "main"),
|
||||
("push:fail-long-stderr-truncates-200", 128, "e" * 300, "dev"),
|
||||
("push:fail-empty-stderr", 2, "", "release"),
|
||||
]:
|
||||
gf = forge.GiteaForge()
|
||||
calls = []
|
||||
PENDING_RC["args"] = (rc, "", stderr)
|
||||
with fake_subprocess(calls):
|
||||
try:
|
||||
out = gf.push("ignored/repo", branch)
|
||||
err = None
|
||||
except forge.ForgeUnavailable as exc:
|
||||
out, err = None, str(exc)
|
||||
PENDING_RC["args"] = (0, "", "")
|
||||
record(f"push:{name}", "push",
|
||||
{"repo": "ignored/repo", "branch": branch, "rc": rc,
|
||||
"stderr": stderr},
|
||||
{"result": out, "error": err, "calls": calls})
|
||||
|
||||
# ── gitea_auth credential ladder (the REAL scripts module) ─────────────────
|
||||
tmp = Path(tempfile.mkdtemp(prefix="forge-truth-"))
|
||||
FIXTURES = ROOT / "src/go/forge/fixtures"
|
||||
FIXTURES.mkdir(parents=True, exist_ok=True)
|
||||
good_file = FIXTURES / "token0600"
|
||||
good_file.write_text("filetoken123\n")
|
||||
os.chmod(good_file, 0o600)
|
||||
bad_file = FIXTURES / "token0644"
|
||||
bad_file.write_text("weaktoken\n")
|
||||
os.chmod(bad_file, 0o644)
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def fake_helper():
|
||||
orig = gitea_auth._token_from_credential_helper
|
||||
gitea_auth._token_from_credential_helper = lambda: ""
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
gitea_auth._token_from_credential_helper = orig
|
||||
|
||||
|
||||
# rung 1: GITEA_TOKEN (stripped)
|
||||
saved = clean_env({})
|
||||
os.environ["GITEA_TOKEN"] = " envtoken \n"
|
||||
record("gitea_auth:env-token-rung", "gitea_auth",
|
||||
{"environ": {"GITEA_TOKEN": " envtoken \n"}},
|
||||
{"has_token": gitea_auth.has_token(),
|
||||
"get_token": gitea_auth.get_token()})
|
||||
os.environ.pop("GITEA_TOKEN")
|
||||
# rung 2: token file (0600)
|
||||
os.environ["GITEA_TOKEN_FILE"] = str(good_file)
|
||||
record("gitea_auth:token-file-0600", "gitea_auth",
|
||||
{"environ": {"GITEA_TOKEN_FILE": "<fixture token0600>"}},
|
||||
{"has_token": gitea_auth.has_token(),
|
||||
"get_token": gitea_auth.get_token()})
|
||||
# rung 2 rejected: token file (0644 → PermissionError)
|
||||
os.environ["GITEA_TOKEN_FILE"] = str(bad_file)
|
||||
try:
|
||||
gitea_auth.get_token()
|
||||
got = {"has_token": gitea_auth.has_token(), "get_token": None,
|
||||
"error": None}
|
||||
except PermissionError as exc:
|
||||
got = {"has_token": gitea_auth.has_token(), "get_token": None,
|
||||
"error": str(exc).replace(str(FIXTURES) + "/", "<FIXTURES>/")}
|
||||
record("gitea_auth:token-file-0644-rejected", "gitea_auth",
|
||||
{"environ": {"GITEA_TOKEN_FILE": "<fixture token0644>"}}, got)
|
||||
# rung 3 empty + nothing anywhere → GiteaCredentialError. The default token
|
||||
# file and the credential helper are pinned so the row is machine-independent.
|
||||
os.environ.pop("GITEA_TOKEN_FILE")
|
||||
pinned_file = tmp / "missing-token"
|
||||
orig_default = gitea_auth.DEFAULT_TOKEN_FILE
|
||||
gitea_auth.DEFAULT_TOKEN_FILE = pinned_file
|
||||
with fake_helper():
|
||||
try:
|
||||
gitea_auth.get_token()
|
||||
no_cred = {"has_token": True, "get_token": "unexpected",
|
||||
"error": None}
|
||||
except gitea_auth.GiteaCredentialError as exc:
|
||||
no_cred = {"has_token": gitea_auth.has_token(), "get_token": None,
|
||||
"error": str(exc).replace(str(tmp) + "/", "<TMP>/")}
|
||||
gitea_auth.DEFAULT_TOKEN_FILE = orig_default
|
||||
record("gitea_auth:no-credential", "gitea_auth",
|
||||
{"default_token_file": "<TMP>/missing-token"}, no_cred)
|
||||
restore_env(saved)
|
||||
|
||||
|
||||
# ── commit_status ───────────────────────────────────────────────────────────
|
||||
def auth_stub(has=True, token="t0k3n"):
|
||||
m = types.SimpleNamespace()
|
||||
m.has_token = lambda: has
|
||||
m.get_token = lambda: token
|
||||
return m
|
||||
|
||||
|
||||
for name, server, env_server, repo, sha, has_tok, body, http_err in [
|
||||
("commit_status:no-token", None, None, "conductor/rune", "abc", False,
|
||||
None, None),
|
||||
("commit_status:success", None, None, "conductor/rune", "abc", True,
|
||||
b'{"state": "success"}', None),
|
||||
("commit_status:state-missing-unknown", None, None, "o/r", "def", True,
|
||||
b'{"statuses": []}', None),
|
||||
("commit_status:server-attr-wins", "http://attr:3000", "http://env:3000",
|
||||
"o/r", "ghi", True, b'{"state": "pending"}', None),
|
||||
("commit_status:server-from-env", None, "http://env:3000", "o/r", "ghi",
|
||||
True, b'{"state": "failure"}', None),
|
||||
("commit_status:http-error", None, None, "o/r", "jkl", True, None,
|
||||
OSError("HTTP Error 404: Not Found")),
|
||||
("commit_status:network-error", None, None, "o/r", "jkl", True, None,
|
||||
OSError("conn refused")),
|
||||
]:
|
||||
gf = forge.GiteaForge(server=server)
|
||||
saved = clean_env({"GITEA_SERVER": env_server} if env_server else {})
|
||||
forge.GiteaForge._gitea_auth = staticmethod(
|
||||
lambda: auth_stub(has=has_tok)) # noqa: B010
|
||||
calls = []
|
||||
try:
|
||||
with fake_http(lambda cap: http_err if http_err else body) as captured:
|
||||
try:
|
||||
state = gf.commit_status(repo, sha)
|
||||
err = None
|
||||
except forge.ForgeUnavailable as exc:
|
||||
state, err = None, str(exc)
|
||||
req = captured[0] if captured else None
|
||||
finally:
|
||||
restore_env(saved)
|
||||
del forge.GiteaForge._gitea_auth
|
||||
record(f"commit_status:{name}", "commit_status",
|
||||
{"server": server, "env_server": env_server, "repo": repo,
|
||||
"sha": sha, "has_token": has_tok,
|
||||
"body": body.decode() if body else None, "http_err":
|
||||
str(http_err) if http_err else None},
|
||||
{"state": state, "error": err, "request": req})
|
||||
|
||||
# ── ci_runs_for_sha ─────────────────────────────────────────────────────────
|
||||
runs_payload = {"workflow_runs": [
|
||||
{"id": 1, "head_sha": "abc"},
|
||||
{"id": 2, "head_sha": None},
|
||||
{"id": 3, "head_sha": "other"},
|
||||
{"id": 4},
|
||||
{"id": 5, "head_sha": "abc"},
|
||||
]}
|
||||
for name, server, repo, sha, payload, err in [
|
||||
("ci_runs:no-token", None, "o/r", "s", None, None),
|
||||
("ci_runs:filter-by-head-sha", None, "o/r", "abc", runs_payload,
|
||||
None),
|
||||
("ci_runs:none-matching", None, "o/r", "zzz", runs_payload, None),
|
||||
("ci_runs:absent-key", None, "o/r", "s", {"total_count": 0}, None),
|
||||
("ci_runs:empty-sha-matches-absent", None, "o/r", "", runs_payload,
|
||||
None),
|
||||
("ci_runs:unreachable", None, "o/r", "s", None,
|
||||
OSError("runs down")),
|
||||
]:
|
||||
gf = forge.GiteaForge(server=server)
|
||||
saved = clean_env({})
|
||||
forge.GiteaForge._gitea_auth = staticmethod(
|
||||
lambda: auth_stub(has=payload is not None or "no-token" not in name))
|
||||
calls = []
|
||||
try:
|
||||
with fake_http(lambda cap: err if err else json.dumps(payload).encode()) as captured:
|
||||
try:
|
||||
got = gf.ci_runs_for_sha(repo, sha)
|
||||
e = None
|
||||
except forge.ForgeUnavailable as exc:
|
||||
got, e = None, str(exc)
|
||||
req = captured[0] if captured else None
|
||||
finally:
|
||||
restore_env(saved)
|
||||
del forge.GiteaForge._gitea_auth
|
||||
record(f"ci_runs:{name}", "ci_runs",
|
||||
{"server": server, "repo": repo, "sha": sha, "payload": payload,
|
||||
"err": str(err) if err else None,
|
||||
"has_token": payload is not None or "no-token" not in name},
|
||||
{"runs": got, "error": e, "request": req})
|
||||
|
||||
# ── create_or_patch_release ────────────────────────────────────────────────
|
||||
for name, has_tok, script_present, rc, stderr, stdout, tag in [
|
||||
("release:no-token", False, True, 0, "", "", "v9.9.9"),
|
||||
("release:published", True, True, 0, "", "", "v1.2.3"),
|
||||
("release:script-fails-stderr", True, True, 3, "tag exists already",
|
||||
"", "v0.0.1"),
|
||||
("release:script-fails-falls-back-stdout", True, True, 4, "",
|
||||
"o" * 250, "v0.0.2"),
|
||||
("release:script-missing", True, False, 0, "", "", "v0.0.3"),
|
||||
]:
|
||||
gf = forge.GiteaForge()
|
||||
saved = clean_env({})
|
||||
forge.GiteaForge._gitea_auth = staticmethod(lambda: auth_stub(has=has_tok))
|
||||
calls = []
|
||||
cwd = ROOT if script_present else str(tmp)
|
||||
old_cwd = os.getcwd()
|
||||
os.chdir(cwd)
|
||||
PENDING_RC["args"] = (rc, stdout, stderr)
|
||||
try:
|
||||
with fake_subprocess(calls):
|
||||
try:
|
||||
got = gf.create_or_patch_release("o/r", tag, "T", "B")
|
||||
e = None
|
||||
except forge.ForgeUnavailable as exc:
|
||||
got, e = None, str(exc)
|
||||
finally:
|
||||
os.chdir(old_cwd)
|
||||
del forge.GiteaForge._gitea_auth
|
||||
PENDING_RC["args"] = (0, "", "")
|
||||
restore_env(saved)
|
||||
argv = calls[0]["argv"][1:] if calls else None
|
||||
record(f"release:{name}", "release",
|
||||
{"has_token": has_tok, "script_present": script_present, "rc": rc,
|
||||
"stderr": stderr, "stdout": stdout, "tag": tag,
|
||||
"cwd": "<root>" if script_present else "<tmp>"},
|
||||
{"result": got, "error": e, "argv": argv,
|
||||
"python_bin": calls[0]["argv"][0] if calls else None})
|
||||
|
||||
doc = {
|
||||
"frozen_ts": "2026-10-12T00:00:00+00:00",
|
||||
"generator": "scripts/gen_forge_truth.py — live oracle src/rune/forge.py",
|
||||
"rows": rows,
|
||||
}
|
||||
out = ROOT / "src/go/forge/truth.json"
|
||||
out.write_text(json.dumps(doc, indent=1, ensure_ascii=False) + "\n",
|
||||
encoding="utf-8")
|
||||
print(f"frozen {len(rows)} rows → {out}")
|
||||
@@ -0,0 +1,180 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Freeze inference_trace truth (dichotomy wave #2479, contract
|
||||
BUILDER-2026-10-12T13:55:29Z-148b8f-factory-wave).
|
||||
|
||||
Generator exercises EVERY public function of the live Python oracle
|
||||
(src/rune/inference_trace.py) across all branches with RUNE_IT_CORE=python
|
||||
pinned and an isolated trace dir, freezing kernel-shaped truth
|
||||
(price_for, path resolution, serialization, rotation verdicts, read
|
||||
selection) to src/rust/inference_trace/src/frozen_it_truth.json.
|
||||
|
||||
File IO (append/rotate/read) is recorded as ordered events so the Rust
|
||||
kernel decides WHAT to do and the Python facade performs it.
|
||||
"""
|
||||
import contextlib
|
||||
import io
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
import tempfile
|
||||
from dataclasses import asdict
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
# PARITY=1: leave RUNE_IT_CORE unset (auto) so the live run goes through the
|
||||
# Rust core — output must byte-match the frozen truth file.
|
||||
PARITY = bool(os.environ.get("PARITY"))
|
||||
os.environ["RUNE_IT_CORE"] = "auto" if PARITY else "python" # PIN: live oracle
|
||||
# Deterministic home so the parity run byte-matches the frozen file.
|
||||
tmp = str(ROOT / "target" / "it_truth_home")
|
||||
os.makedirs(tmp, exist_ok=True)
|
||||
os.environ["RUNE_HOME"] = tmp
|
||||
|
||||
sys.path.insert(0, str(ROOT / "src"))
|
||||
|
||||
from rune import inference_trace as it # noqa: E402
|
||||
|
||||
CASES = {
|
||||
"frozen": {
|
||||
"contract": "BUILDER-2026-10-12T13:55:29Z-148b8f-factory-wave",
|
||||
"oracle": "src/rune/inference_trace.py",
|
||||
"pin": "RUNE_IT_CORE=python",
|
||||
},
|
||||
"price_for": [],
|
||||
"trace_path": [],
|
||||
"record": [],
|
||||
"read": [],
|
||||
"main": [],
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------- price_for
|
||||
# Every branch: empty price map → None for any model.
|
||||
for model in ["qwen2.5:3b", "qwen3:14b", "mimo-v2.5-pro", "gpt-4", "", "unknown-model"]:
|
||||
CASES["price_for"].append({"model": model, "out": it.price_for(model)})
|
||||
|
||||
# ---------------------------------------------------------------- paths
|
||||
home = Path(tmp)
|
||||
dir_cases = [
|
||||
None, # default: <rune_home>/state
|
||||
Path(tmp) / "custom_traces", # explicit dir
|
||||
Path(tmp) / "nested" / "deeper", # explicit nested
|
||||
]
|
||||
for d in dir_cases:
|
||||
CASES["trace_path"].append({
|
||||
"trace_dir": str(d) if d is not None else None,
|
||||
"home": tmp,
|
||||
"out_dir": str(it._trace_dir(d)),
|
||||
"out_path": str(it._trace_path(d)),
|
||||
})
|
||||
# rune_home default (no explicit dir) resolves from RUNE_HOME
|
||||
assert str(home) in str(it._trace_dir()), "RUNE_HOME isolation broken"
|
||||
|
||||
# ---------------------------------------------------------------- record
|
||||
def mk_trace(**over):
|
||||
base = dict(
|
||||
schema_version=5, provider="ollama", model="qwen3:14b",
|
||||
task_id="T42", channel="chat", resolved_context_window=32768,
|
||||
input_budget=24000, model_calls=2, prompt_tokens_estimated=5150,
|
||||
prompt_tokens_served=5102, output_tokens=733, latency_ms=8123.4,
|
||||
model_time_ms=6210.9,
|
||||
per_block_tokens={"constitution": 412, "project": 1300},
|
||||
dropped_blocks=[{"name": "human", "tokens": 88}],
|
||||
tools_exposed=["read", "edit"], memories_exposed=["constitution"],
|
||||
memory_tokens=1712, degraded=[], cache=None, cost_usd=None,
|
||||
cost_basis="local-unpriced", success=True, error="",
|
||||
)
|
||||
base.update(over)
|
||||
return it.InferenceTrace(**base)
|
||||
|
||||
|
||||
# serialized line shape (sort_keys, compact separators)
|
||||
for name, tr in [
|
||||
("v1-minimal", mk_trace()),
|
||||
("v5-full", mk_trace(
|
||||
routing_reason="tier:hot", routing_mode="local", task_family="coding",
|
||||
selected_tools=["read"], capability_reason="allowlist",
|
||||
find_capability_calls=1,
|
||||
capsule_messages=[{"role": "user", "content": "hi"}],
|
||||
capsule_content_hashes={"constitution": "a" * 64},
|
||||
capsule_token_count=5150,
|
||||
memory_candidates=9, memory_selected=2, memory_abstained=False,
|
||||
memory_types_queried=["project"],
|
||||
)),
|
||||
("degraded-error", mk_trace(success=False, error="model timeout",
|
||||
degraded=["memory"], model_calls=1,
|
||||
output_tokens=0)),
|
||||
("abstained", mk_trace(memory_candidates=4, memory_selected=0,
|
||||
memory_abstained=True)),
|
||||
]:
|
||||
line = json.dumps(asdict(tr), sort_keys=True, separators=(",", ":"))
|
||||
CASES["record"].append({"tag": name, "trace": asdict(tr), "out_line": line})
|
||||
|
||||
# rotation verdicts: given current size, does rotation fire, and what
|
||||
# shift sequence runs? (kernel decides; facade performs)
|
||||
for size in [0, 9_999_999, 10_000_000, 10_000_001, 25_000_000]:
|
||||
CASES["record"].append({
|
||||
"tag": f"rotate-{size}", "current_size": size,
|
||||
"rotate_bytes": it.TRACE_ROTATE_BYTES, "backups": it.TRACE_BACKUPS,
|
||||
"out": size > it.TRACE_ROTATE_BYTES,
|
||||
})
|
||||
|
||||
# ---------------------------------------------------------------- read
|
||||
# read_traces selection: no file → [], limit trims to last N, blank lines
|
||||
# skipped. (content round-trips through the record serializer)
|
||||
READ_DIR = Path(tmp) / "read_traces"
|
||||
READ_DIR.mkdir(parents=True, exist_ok=True)
|
||||
lines = [
|
||||
json.dumps(asdict(mk_trace(task_id=f"R{i}")), sort_keys=True,
|
||||
separators=(",", ":"))
|
||||
for i in range(5)
|
||||
]
|
||||
raw = "\n".join(lines[:3]) + "\n\n\n" + "\n".join(lines[3:]) + "\n"
|
||||
(READ_DIR / it.TRACE_FILENAME).write_text(raw, encoding="utf-8")
|
||||
|
||||
CASES["read"].append({"tag": "all", "exists": True, "limit": None,
|
||||
"line_count": len(lines), "raw_line_count": 8,
|
||||
"out_count": len(it.read_traces(READ_DIR))})
|
||||
CASES["read"].append({"tag": "limit-2", "exists": True, "limit": 2,
|
||||
"line_count": len(lines), "raw_line_count": 8,
|
||||
"out_count": len(it.read_traces(READ_DIR, limit=2))})
|
||||
CASES["read"].append({"tag": "limit-99", "exists": True, "limit": 99,
|
||||
"line_count": len(lines), "raw_line_count": 8,
|
||||
"out_count": len(it.read_traces(READ_DIR, limit=99))})
|
||||
CASES["read"].append({"tag": "missing-file", "exists": False, "limit": None,
|
||||
"line_count": 0, "raw_line_count": 0,
|
||||
"out_count": len(it.read_traces(Path(tmp) / "nowhere"))})
|
||||
|
||||
# ---------------------------------------------------------------- main
|
||||
# CLI tail: main(count) prints each record indented+sorted and returns 0.
|
||||
probe = Path(tmp) / "main_probe"
|
||||
probe.mkdir(parents=True, exist_ok=True)
|
||||
for i in range(3):
|
||||
it.record_trace(mk_trace(task_id=f"M{i}"), probe)
|
||||
for count in [None, 2]:
|
||||
buf = io.StringIO()
|
||||
old_argv = sys.argv
|
||||
sys.argv = ["inference_trace.py"] + ([str(count)] if count is not None else [])
|
||||
old_tp = it._trace_path
|
||||
it._trace_path = lambda d=None: probe / it.TRACE_FILENAME # aim main at probe
|
||||
try:
|
||||
with contextlib.redirect_stdout(buf):
|
||||
rc = it.main()
|
||||
finally:
|
||||
it._trace_path = old_tp
|
||||
sys.argv = old_argv
|
||||
out = buf.getvalue()
|
||||
CASES["main"].append({
|
||||
"count": count,
|
||||
"records": [asdict(t) for t in [mk_trace(task_id=f"M{i}") for i in range(3)]],
|
||||
"out": out, "rc": rc,
|
||||
# the exact print format for the records the kernel must produce
|
||||
"out_lines": out.count("}"),
|
||||
})
|
||||
|
||||
out = ROOT / "src" / "rust" / "inference_trace" / "src" / "frozen_it_truth.json"
|
||||
if PARITY:
|
||||
out = Path(tempfile.mkdtemp(prefix="it_parity_")) / "parity_it_truth.json"
|
||||
out.write_text(json.dumps(CASES, indent=1, ensure_ascii=False) + "\n")
|
||||
counts = {k: len(v) for k, v in CASES.items() if k != "frozen"}
|
||||
print("frozen:", out)
|
||||
print("cases:", json.dumps(counts))
|
||||
@@ -0,0 +1,127 @@
|
||||
#!/usr/bin/env python3
|
||||
"""gen_intent_continuity_truth.py — freeze the intent_continuity parity
|
||||
oracle (dichotomy wave #2479 tranche).
|
||||
|
||||
Generator law: RUNE_IC_CORE=python pinned, the clock STUBBED (deterministic
|
||||
per-op `now`), inputs deep-copied, every public transition/branch exercised
|
||||
(create, create_detour incl. PARKED-parent no-op, resolve incl. missing and
|
||||
already-resolved, resolve_detour incl. re-entry and un-parking, supersede
|
||||
incl. obligations guard + KeyError). Output:
|
||||
src/rust/intent_continuity/frozen_ic_truth.json — replayed VERBATIM by the
|
||||
crate's cfg(test) battery.
|
||||
"""
|
||||
import copy
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
import tempfile
|
||||
import types
|
||||
from datetime import datetime, timezone
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
sys.path.insert(0, str(ROOT / "src"))
|
||||
|
||||
os.environ["RUNE_IC_CORE"] = "python"
|
||||
|
||||
import rune.intent_continuity as ic # noqa: E402
|
||||
|
||||
# deterministic clock: op k gets 2026-10-12T00:00:kZ
|
||||
_tick = [0]
|
||||
|
||||
|
||||
def _fake_strftime(_fmt, _t):
|
||||
_tick[0] += 1
|
||||
return f"2026-10-12T00:00:{_tick[0]:02d}Z"
|
||||
|
||||
|
||||
ic.time = types.SimpleNamespace(strftime=_fake_strftime, gmtime=lambda: 0)
|
||||
|
||||
cases: list = []
|
||||
|
||||
|
||||
def ledger_state(led: ic.IntentLedger) -> dict:
|
||||
return {"intents": [r.to_dict() for r in led.records.values()]}
|
||||
|
||||
|
||||
def record_op(led, fn, op_args, now, expected_record, before, after, err=None):
|
||||
"""Freeze one op: kernel args from the pre-op state, expected from the
|
||||
observed oracle outcome."""
|
||||
args = {"ledger": copy.deepcopy(before), "now": now, **copy.deepcopy(op_args)}
|
||||
if err is not None:
|
||||
expected = {"err": err}
|
||||
else:
|
||||
expected = {"ledger": copy.deepcopy(after),
|
||||
"record": copy.deepcopy(expected_record)}
|
||||
cases.append({"fn": fn, "args": args, "expected": expected})
|
||||
|
||||
|
||||
def op(led, fn, run, **kwargs):
|
||||
"""run(**kwargs) performs the oracle op (e.g. led.create); kwargs are
|
||||
recorded verbatim as kernel args."""
|
||||
before = ledger_state(led)
|
||||
now = f"2026-10-12T00:00:{_tick[0] + 1:02d}Z" # the now this op will get
|
||||
try:
|
||||
rec = run(**kwargs)
|
||||
after = ledger_state(led)
|
||||
record_op(led, fn, kwargs, now, rec.to_dict() if rec else None, before, after)
|
||||
except (ValueError, KeyError) as e:
|
||||
# kernel err strings match the oracle's exception text: the facade
|
||||
# re-raises KeyError(e.args[0]) whose str is the quoted form, so the
|
||||
# kernel carries the BARE id after the prefix
|
||||
err = (f"KeyError: {e.args[0]}" if isinstance(e, KeyError) else str(e))
|
||||
record_op(led, fn, kwargs, now, None, before, before, err=err)
|
||||
return now
|
||||
|
||||
|
||||
def main() -> None:
|
||||
with tempfile.TemporaryDirectory() as td:
|
||||
led = ic.IntentLedger(os.path.join(td, "intents.json"))
|
||||
|
||||
op(led, "ic_create", led.create,
|
||||
id="t1", objective="ship the parser fix", classification="FIX")
|
||||
op(led, "ic_create", led.create,
|
||||
id="t2", objective="rewrite recall", classification="EXTENSION",
|
||||
success_condition="A/B parity shown",
|
||||
unresolved_obligations=["o1", "o2"])
|
||||
op(led, "ic_create_detour", led.create_detour,
|
||||
id="d1", objective="hotfix CI", classification="BLOCKER",
|
||||
parent_intent="t1", return_when="CI green", return_to="t1")
|
||||
op(led, "ic_create_detour", led.create_detour,
|
||||
id="d2", objective="second detour", classification="LATER",
|
||||
parent_intent="t1", # t1 already PARKED
|
||||
return_when="later", return_to="t1")
|
||||
op(led, "ic_resolve", led.resolve, intent_id="t2")
|
||||
op(led, "ic_resolve", led.resolve, intent_id="t2") # already RESOLVED → None
|
||||
op(led, "ic_resolve", led.resolve, intent_id="d1") # DETOUR → RESOLVED
|
||||
op(led, "ic_resolve_detour", led.resolve_detour, detour_id="d1") # un-parks t1
|
||||
op(led, "ic_resolve_detour", led.resolve_detour, detour_id="d1") # re-entry → parent
|
||||
op(led, "ic_resolve", led.resolve, intent_id="missing")
|
||||
op(led, "ic_resolve_detour", led.resolve_detour, detour_id="missing")
|
||||
op(led, "ic_supersede", led.supersede,
|
||||
old_id="t2", new_id="t2b", objective="rewrite recall v2",
|
||||
classification="SUPERSEDING", carry_obligations=True)
|
||||
op(led, "ic_supersede", led.supersede,
|
||||
old_id="t2", new_id="t2c", objective="drop obligations",
|
||||
classification="SUPERSEDING", carry_obligations=False)
|
||||
op(led, "ic_supersede", led.supersede,
|
||||
old_id="missing", new_id="x", objective="o", classification="FIX")
|
||||
op(led, "ic_create", led.create,
|
||||
id="bad", objective="o", classification="BOGUS")
|
||||
op(led, "ic_create_detour", led.create_detour,
|
||||
id="d3", objective="o", classification="FIX",
|
||||
parent_intent="nope", return_when="w", return_to="r")
|
||||
|
||||
doc = {
|
||||
"module": "intent_continuity",
|
||||
"pin": "RUNE_IC_CORE=python",
|
||||
"frozen_at": datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ"),
|
||||
"cases": cases,
|
||||
}
|
||||
out = ROOT / "src" / "rust" / "intent_continuity" / "frozen_ic_truth.json"
|
||||
out.write_text(json.dumps(doc, indent=1) + "\n")
|
||||
print(f"frozen {len(cases)} cases -> {out}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,263 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Freeze the dichotomy-wave truth corpus for src/rune/iteration.py.
|
||||
|
||||
LAW (poisoned-truth): RUNE_IT_CORE=python is pinned at the top — truth is
|
||||
generated by the PYTHON ORACLE ONLY, never by the kernel under test.
|
||||
RUNE_LEDGER_CORE=python pins the capability ledger to its Python floor.
|
||||
The evidence_provenance cdylib stays LIVE: it is part of the oracle stack
|
||||
(behaviour_ledger has no Python floor) and is already parity-verified.
|
||||
|
||||
Every public function is exercised across ALL branches. Timestamps:
|
||||
iteration._now / behaviour_ledger._now are monkeypatched to a FIXED value
|
||||
so the frozen corpus is byte-stable; the Rust kernel receives `now` as an
|
||||
argument (the same determinism seam the facade uses live).
|
||||
"""
|
||||
import copy
|
||||
import json
|
||||
import os
|
||||
import shutil
|
||||
import sys
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
|
||||
os.environ["RUNE_IT_CORE"] = "python" # poisoned-truth law
|
||||
os.environ["RUNE_LEDGER_CORE"] = "python" # capability_ledger floor
|
||||
|
||||
REPO = Path(__file__).resolve().parents[1]
|
||||
sys.path.insert(0, str(REPO / "src"))
|
||||
sys.path.insert(0, str(REPO / "src" / "rust" / "evidence_provenance"))
|
||||
|
||||
import rune.iteration as it # noqa: E402
|
||||
import rune.behaviour_ledger as bl_mod # noqa: E402
|
||||
from rune.structural_model import StructuralModel # noqa: E402
|
||||
from rune.capability_ledger import CapabilityLedger # noqa: E402
|
||||
from rune.behaviour_ledger import BehaviourLedger # noqa: E402
|
||||
|
||||
FIXED_NOW = "2026-10-12T00:00:00Z"
|
||||
it._now = lambda: FIXED_NOW
|
||||
bl_mod._now = lambda: FIXED_NOW
|
||||
|
||||
OUT = REPO / "src" / "rust" / "iteration" / "src" / "frozen_it_truth.json"
|
||||
|
||||
cases = []
|
||||
|
||||
|
||||
def case(fn, name, args, expect):
|
||||
cases.append({"fn": fn, "name": name,
|
||||
"args": args, "expect": expect})
|
||||
|
||||
|
||||
def build_fixture(tmp):
|
||||
model = StructuralModel(Path(tmp) / "model", "demo")
|
||||
model.add("subsystem", "core")
|
||||
model.add("component", "alpha", parent="subsystem:core",
|
||||
ext={"source": "src/alpha.py"})
|
||||
model.add("component", "beta", parent="subsystem:core")
|
||||
for e in model.doc["entities"]:
|
||||
if e["id"] == "component:beta":
|
||||
e["identities"]["source"] = "src/beta.py"
|
||||
model.add("interface", "api")
|
||||
model.add("interface", "legacy")
|
||||
model.add("state_store", "db")
|
||||
model.add("test_suite", "t1")
|
||||
model.add("deployment_surface", "prod")
|
||||
model.add("component", "gamma", parent="subsystem:core")
|
||||
model.add("component", "delta", parent="subsystem:core")
|
||||
model.relate("component:alpha", "provides", "interface:api")
|
||||
model.relate("component:gamma", "consumes", "interface:api")
|
||||
model.relate("component:delta", "depends_on", "component:alpha",
|
||||
status="stale")
|
||||
model.relate("component:alpha", "consumes", "interface:legacy")
|
||||
model.relate("component:alpha", "owns", "state_store:db")
|
||||
model.relate("component:alpha", "verified_by", "test_suite:t1")
|
||||
model.relate("component:alpha", "deployed_by", "deployment_surface:prod")
|
||||
model.relate("component:gamma", "provides", "interface:legacy")
|
||||
model.relate("component:gamma", "owns", "state_store:db")
|
||||
model.save()
|
||||
|
||||
ledger = CapabilityLedger(Path(tmp) / "ledger")
|
||||
ledger.define(
|
||||
capability_id="alpha-cap", name="alpha", purpose="alpha purpose",
|
||||
owning_project="demo", component="component:alpha",
|
||||
source_locations=["src/alpha.py", "src/alpha_util.py"],
|
||||
contract="alpha contract", state_touched="state_store:db",
|
||||
dependencies=["interface:legacy"], consumers=["component:gamma"],
|
||||
invariants=["inv-1"], behaviour_specs=["b-alpha"],
|
||||
constraints="no network", generation="g1")
|
||||
ledger.define(
|
||||
capability_id="beta-cap", name="beta", purpose="beta purpose",
|
||||
owning_project="demo", component="component:beta",
|
||||
source_locations=["src/beta.py"])
|
||||
ledger.doc["capabilities"]["capability:beta-cap"]["maturity"] = "retired"
|
||||
ledger.define(
|
||||
capability_id="gamma-cap", name="gamma", purpose="gamma purpose",
|
||||
owning_project="demo", component="component:gamma",
|
||||
source_locations=["src/gamma.py", "src/gamma_more.py"])
|
||||
|
||||
behaviours = BehaviourLedger(Path(tmp) / "behaviours")
|
||||
behaviours.define(
|
||||
behaviour_id="b-alpha", owning_project="demo",
|
||||
intended_behaviour="alpha behaves", owner_component="component:alpha",
|
||||
dependencies=["component:beta"])
|
||||
behaviours.define(
|
||||
behaviour_id="b-gamma", owning_project="demo",
|
||||
intended_behaviour="gamma behaves", owner_component="component:gamma")
|
||||
behaviours.record_real_world("b-alpha", passed=True, detail="ok")
|
||||
return model, ledger, behaviours
|
||||
|
||||
|
||||
def main():
|
||||
tmp = tempfile.mkdtemp(prefix="it_truth_")
|
||||
try:
|
||||
model, ledger, behaviours = build_fixture(tmp)
|
||||
model_doc = model.doc
|
||||
ledger_doc = ledger.doc
|
||||
bh_doc = behaviours.doc
|
||||
|
||||
# ── context_packet ────────────────────────────────────────────
|
||||
r = it.context_packet(model, ledger, "component:alpha")
|
||||
case("context_packet", "alpha_full",
|
||||
{"model": model_doc, "ledger": ledger_doc,
|
||||
"target": "component:alpha", "now": FIXED_NOW}, r)
|
||||
r = it.context_packet(model, ledger, "nope:missing")
|
||||
case("context_packet", "unknown_target",
|
||||
{"model": model_doc, "ledger": ledger_doc,
|
||||
"target": "nope:missing", "now": FIXED_NOW}, r)
|
||||
r = it.context_packet(model, ledger, "component:gamma",
|
||||
intent="ship gamma", basis_summary={"depth": 2})
|
||||
case("context_packet", "gamma_intent_basis",
|
||||
{"model": model_doc, "ledger": ledger_doc,
|
||||
"target": "component:gamma", "intent": "ship gamma",
|
||||
"basis_summary": {"depth": 2}, "now": FIXED_NOW}, r)
|
||||
r = it.context_packet(model, ledger, "component:alpha",
|
||||
max_local_files=1)
|
||||
case("context_packet", "alpha_truncated",
|
||||
{"model": model_doc, "ledger": ledger_doc,
|
||||
"target": "component:alpha", "max_local_files": 1,
|
||||
"now": FIXED_NOW}, r)
|
||||
r = it.context_packet(model, ledger, "component:beta")
|
||||
case("context_packet", "beta_identities_source",
|
||||
{"model": model_doc, "ledger": ledger_doc,
|
||||
"target": "component:beta", "now": FIXED_NOW}, r)
|
||||
r = it.context_packet(model, ledger, "interface:api")
|
||||
case("context_packet", "interface_target",
|
||||
{"model": model_doc, "ledger": ledger_doc,
|
||||
"target": "interface:api", "now": FIXED_NOW}, r)
|
||||
r = it.context_packet(model, ledger, "project:demo")
|
||||
case("context_packet", "root_project",
|
||||
{"model": model_doc, "ledger": ledger_doc,
|
||||
"target": "project:demo", "now": FIXED_NOW}, r)
|
||||
empty = CapabilityLedger.__new__(CapabilityLedger)
|
||||
empty.doc = {"schema_version": 1, "capabilities": {}}
|
||||
r = it.context_packet(model, empty, "component:alpha")
|
||||
case("context_packet", "empty_ledger",
|
||||
{"model": model_doc, "ledger": empty.doc,
|
||||
"target": "component:alpha", "now": FIXED_NOW}, r)
|
||||
|
||||
# ── predictive_impact ─────────────────────────────────────────
|
||||
for name, target in [("alpha", "component:alpha"),
|
||||
("unknown", "nope:missing"),
|
||||
("leaf", "component:beta")]:
|
||||
r = it.predictive_impact(model, target)
|
||||
case("predictive_impact", name,
|
||||
{"model": model_doc, "target": target}, r)
|
||||
|
||||
# ── descriptive_impact ────────────────────────────────────────
|
||||
for name, kwargs in [
|
||||
("full_overlap", {"observed_changed": ["component:alpha",
|
||||
"component:gamma"]}),
|
||||
("unexpected", {"observed_changed": ["component:alpha",
|
||||
"surprise:one"]}),
|
||||
("none_observed", {"observed_changed": []}),
|
||||
]:
|
||||
r = it.descriptive_impact(model, "component:alpha", **kwargs)
|
||||
case("descriptive_impact", name,
|
||||
{"model": model_doc, "target": "component:alpha",
|
||||
"observed_changed": kwargs["observed_changed"],
|
||||
"now": FIXED_NOW}, r)
|
||||
r = it.descriptive_impact(model, "component:alpha",
|
||||
observed_changed=["component:gamma"],
|
||||
generation="g7")
|
||||
case("descriptive_impact", "generation_tag",
|
||||
{"model": model_doc, "target": "component:alpha",
|
||||
"observed_changed": ["component:gamma"],
|
||||
"generation": "g7", "now": FIXED_NOW}, r)
|
||||
r = it.descriptive_impact(model, "nope:missing",
|
||||
observed_changed=["x"])
|
||||
case("descriptive_impact", "unknown_target",
|
||||
{"model": model_doc, "target": "nope:missing",
|
||||
"observed_changed": ["x"], "now": FIXED_NOW}, r)
|
||||
|
||||
# ── record_impact_delta (exact jsonl line — file IO facade) ───
|
||||
deltas = [
|
||||
{"target": "component:alpha", "generation": "g1",
|
||||
"predicted": ["component:alpha"], "observed": [],
|
||||
"unexpected": [], "unaffected_as_predicted":
|
||||
["component:alpha"], "recorded_at": FIXED_NOW},
|
||||
{"b": 1, "a": [1.5, 2], "c": None, "s": "é\nx"},
|
||||
{"nested": {"z": True, "y": 0.1}},
|
||||
]
|
||||
for i, delta in enumerate(deltas):
|
||||
d = Path(tmp) / f"deltas{i}"
|
||||
d.mkdir()
|
||||
it.record_impact_delta(d, delta)
|
||||
line = (d / "impact_deltas.jsonl").read_text()
|
||||
assert line.endswith("\n") and line.count("\n") == 1
|
||||
case("record_impact_delta", f"line{i}", {"delta": delta},
|
||||
line[:-1])
|
||||
|
||||
# ── invalidate_behaviours ─────────────────────────────────────
|
||||
for name, changed in [
|
||||
("hit_deps_and_owner", {"component:alpha", "component:beta"}),
|
||||
("owner_only", {"component:gamma"}),
|
||||
("no_intersection", {"component:delta"}),
|
||||
]:
|
||||
b = BehaviourLedger.__new__(BehaviourLedger)
|
||||
b.doc = copy.deepcopy(bh_doc)
|
||||
recs = it.invalidate_behaviours(
|
||||
model, b, changed_entities=changed, generation="g9")
|
||||
case("invalidate_behaviours", name,
|
||||
{"behaviours": bh_doc, "changed": sorted(changed),
|
||||
"generation": "g9", "now": FIXED_NOW},
|
||||
{"doc": b.doc, "recs": recs})
|
||||
|
||||
# ── post_change_obligations ───────────────────────────────────
|
||||
for name, target, changed in [
|
||||
("alpha_default", "component:alpha", None),
|
||||
("alpha_two_changed", "component:alpha",
|
||||
["component:alpha", "component:gamma"]),
|
||||
("beta_leaf", "component:beta", None),
|
||||
("gamma_consumes_iface", "component:gamma", None),
|
||||
("delta_stale_dep", "component:delta", None),
|
||||
("unknown_in_changed", "component:beta",
|
||||
["component:beta", "ghost:x"]),
|
||||
]:
|
||||
if changed is None:
|
||||
r = it.post_change_obligations(model, target)
|
||||
args = {"model": model_doc, "target": target}
|
||||
else:
|
||||
r = it.post_change_obligations(
|
||||
model, target, changed_entities=set(changed))
|
||||
args = {"model": model_doc, "target": target,
|
||||
"changed": changed}
|
||||
case("post_change_obligations", name, args, r)
|
||||
r = it.post_change_obligations(model, "component:alpha",
|
||||
behaviours=behaviours)
|
||||
case("post_change_obligations", "alpha_with_behaviours",
|
||||
{"model": model_doc, "target": "component:alpha",
|
||||
"behaviours": bh_doc}, r)
|
||||
|
||||
OUT.parent.mkdir(parents=True, exist_ok=True)
|
||||
groups = {}
|
||||
for c in cases:
|
||||
groups.setdefault(f"{c['fn']}_cases", []).append(c)
|
||||
OUT.write_text(json.dumps(
|
||||
{"module": "iteration", "now": FIXED_NOW, **groups},
|
||||
indent=1, sort_keys=True) + "\n")
|
||||
print(f"frozen {len(cases)} cases -> {OUT}")
|
||||
finally:
|
||||
shutil.rmtree(tmp, ignore_errors=True)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,585 @@
|
||||
#!/usr/bin/env python3
|
||||
"""gen_project_registry_truth.py — freeze/replay parity truth for
|
||||
project_registry (dichotomy wave, #2479 tranche).
|
||||
|
||||
Freeze mode (default): exercises EVERY public function of the LIVE Python
|
||||
oracle (src/rune/project_registry.py) across every branch and records
|
||||
(kernel_input, expected_kernel_output) pairs into
|
||||
src/rust/project_registry/src/frozen_pr_truth.json.
|
||||
|
||||
Replay mode (--replay): re-runs the SAME scenarios with the Rust core
|
||||
delegation active and asserts both kernel-level (loader direct) and
|
||||
facade-level (oracle methods delegate) parity against the frozen file.
|
||||
|
||||
Poisoned-truth law: freeze pins RUNE_PR_CORE=python BEFORE any import (a
|
||||
no-op pre-port, load-bearing post-port — the facade must never delegate
|
||||
into the kernel while truth is being frozen).
|
||||
|
||||
No-clock law: the wall clock is frozen by patching the `time` symbol in
|
||||
project_registry, so every kernel payload carries its own `now` and
|
||||
replay is deterministic regardless of run date.
|
||||
|
||||
Boundary law: the registry FILE IO (_load hit branch, save) stays in the
|
||||
Python facade — the kernel is a pure function of the loaded doc. The
|
||||
`load` and `save` truth groups are facade-only (no kernel symbol) and
|
||||
are skipped by the kernel-level replay pass.
|
||||
"""
|
||||
import copy
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
|
||||
REPO = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
TRUTH_PATH = os.path.join(REPO, "src", "rust", "project_registry", "src",
|
||||
"frozen_pr_truth.json")
|
||||
REPLAY = "--replay" in sys.argv
|
||||
|
||||
os.environ["RUNE_PR_CORE"] = "python" # poisoned-truth law
|
||||
|
||||
sys.path.insert(0, os.path.join(REPO, "src"))
|
||||
sys.path.insert(0, os.path.join(REPO, "src", "rune"))
|
||||
|
||||
import project_registry as oracle_mod # noqa: E402
|
||||
|
||||
if REPLAY:
|
||||
from rune import pr_loader # noqa: E402 (kernel-level replay side)
|
||||
# replay law: the facade pass must exercise the LIVE Rust delegation
|
||||
# path, so the pin is lifted AFTER import (the facade reads the env
|
||||
# at call time). The kernel pass calls the loader directly.
|
||||
del os.environ["RUNE_PR_CORE"]
|
||||
|
||||
FROZEN_TS = 1791806400 # 2026-10-12T12:00:00Z
|
||||
FROZEN_NOW = "2026-10-12T12:00:00Z"
|
||||
|
||||
|
||||
class FakeTime:
|
||||
@staticmethod
|
||||
def strftime(fmt, t):
|
||||
import time as _t
|
||||
return _t.strftime(fmt, t)
|
||||
|
||||
@staticmethod
|
||||
def gmtime():
|
||||
import time as _t
|
||||
return _t.gmtime(FROZEN_TS)
|
||||
|
||||
|
||||
oracle_mod.time = FakeTime
|
||||
|
||||
|
||||
def fresh_dir():
|
||||
return tempfile.mkdtemp(prefix="pr_truth_")
|
||||
|
||||
|
||||
def fresh_registry(**kw):
|
||||
return oracle_mod.ProjectRegistry(fresh_dir(), **kw)
|
||||
|
||||
|
||||
def rec_kernel(rec, name, inp, expected, oracle_ret=None):
|
||||
"""Record a kernel case; inputs deep-copied BEFORE oracle mutation."""
|
||||
rec(name, copy.deepcopy(inp), oracle_ret, expected)
|
||||
|
||||
|
||||
def rec_facade(rec, name, inp, expected, oracle_ret=None):
|
||||
rec(name, inp, oracle_ret, expected)
|
||||
|
||||
|
||||
def err_or(fn):
|
||||
"""Run a facade call; freeze the kernel error envelope on exception."""
|
||||
try:
|
||||
return fn(), None
|
||||
except ValueError as e:
|
||||
return None, f"ValueError: {e}"
|
||||
except KeyError as e:
|
||||
return None, f"KeyError: {e.args[0]}"
|
||||
|
||||
|
||||
# ── default_doc (kernel) ─────────────────────────────────────────────────────
|
||||
|
||||
def c_default_doc(rec):
|
||||
r = fresh_registry()
|
||||
rec_kernel(rec, "default-doc-fresh", {"now": FROZEN_NOW}, {"doc": r.doc})
|
||||
|
||||
|
||||
# ── register (kernel) ────────────────────────────────────────────────────────
|
||||
|
||||
def _register_case(rec, name, doc_before, **kw):
|
||||
r = doc_before if doc_before is not None else fresh_registry()
|
||||
before = copy.deepcopy(r.doc)
|
||||
entry = r.register(**kw)
|
||||
inp = {"doc": before, "project_id": kw["project_id"],
|
||||
"scaffold_location": kw.get("scaffold_location", ""),
|
||||
"repo_identity": kw.get("repo_identity", ""),
|
||||
"forge_repo": kw.get("forge_repo", ""),
|
||||
"default_branch": kw.get("default_branch", "master"),
|
||||
"structural_schema_version": kw.get("structural_schema_version", 1),
|
||||
"runtime_generation": kw.get("runtime_generation", ""),
|
||||
"last_known_good": kw.get("last_known_good", ""),
|
||||
"now": FROZEN_NOW}
|
||||
rec_kernel(rec, name, inp, {"doc": r.doc, "entry": entry})
|
||||
|
||||
|
||||
def c_register_defaults(rec):
|
||||
_register_case(rec, "register-defaults", None,
|
||||
project_id="alpha", scaffold_location="/tmp/alpha/scaffold")
|
||||
|
||||
|
||||
def c_register_full(rec):
|
||||
_register_case(rec, "register-full-kwargs", None,
|
||||
project_id="beta", scaffold_location="/tmp/beta/scaffold",
|
||||
repo_identity="git:/repos/beta", forge_repo="conductor/beta",
|
||||
default_branch="main", structural_schema_version=3,
|
||||
runtime_generation="gen-7", last_known_good="gen-6")
|
||||
|
||||
|
||||
def c_register_overwrites(rec):
|
||||
r = fresh_registry()
|
||||
r.register(project_id="alpha", scaffold_location="/old")
|
||||
_register_case(rec, "register-overwrites-existing", r,
|
||||
project_id="alpha", scaffold_location="/new",
|
||||
repo_identity="git:/repos/alpha")
|
||||
|
||||
|
||||
def c_register_second_project(rec):
|
||||
r = fresh_registry()
|
||||
r.register(project_id="alpha", scaffold_location="/a")
|
||||
_register_case(rec, "register-second-preserves-first", r,
|
||||
project_id="beta", scaffold_location="/b")
|
||||
|
||||
|
||||
# ── get (kernel) ─────────────────────────────────────────────────────────────
|
||||
|
||||
def _get_case(rec, name, register_kw, probe):
|
||||
r = fresh_registry()
|
||||
if register_kw:
|
||||
r.register(**register_kw)
|
||||
entry = r.get(probe)
|
||||
inp = {"doc": copy.deepcopy(r.doc), "project_id": probe}
|
||||
rec_kernel(rec, name, inp, {"entry": entry})
|
||||
|
||||
|
||||
def c_get_present(rec):
|
||||
_get_case(rec, "get-present", {"project_id": "alpha",
|
||||
"scaffold_location": "/a"}, "alpha")
|
||||
|
||||
|
||||
def c_get_missing(rec):
|
||||
_get_case(rec, "get-missing-null", {"project_id": "alpha",
|
||||
"scaffold_location": "/a"}, "ghost")
|
||||
|
||||
|
||||
def c_get_empty_registry(rec):
|
||||
_get_case(rec, "get-empty-registry", None, "alpha")
|
||||
|
||||
|
||||
# ── all_projects (kernel) ────────────────────────────────────────────────────
|
||||
|
||||
def _all_case(rec, name, pids):
|
||||
r = fresh_registry()
|
||||
for pid in pids:
|
||||
r.register(project_id=pid, scaffold_location=f"/{pid}")
|
||||
projs = r.all_projects()
|
||||
inp = {"doc": copy.deepcopy(r.doc)}
|
||||
rec_kernel(rec, name, inp, {"projects": projs})
|
||||
|
||||
|
||||
def c_all_empty(rec):
|
||||
_all_case(rec, "all-empty-registry", [])
|
||||
|
||||
|
||||
def c_all_single(rec):
|
||||
_all_case(rec, "all-single", ["alpha"])
|
||||
|
||||
|
||||
def c_all_sorted(rec):
|
||||
_all_case(rec, "all-sorted-by-project-id",
|
||||
["zeta", "alpha", "mu", "beta"])
|
||||
|
||||
|
||||
# ── set_health (kernel) ──────────────────────────────────────────────────────
|
||||
|
||||
def _health_case(rec, name, health, note="", pid="alpha", setup=None):
|
||||
r = fresh_registry()
|
||||
r.register(project_id="alpha", scaffold_location="/a",
|
||||
repo_identity="git:/repos/alpha")
|
||||
if setup:
|
||||
setup(r)
|
||||
before = copy.deepcopy(r.doc)
|
||||
ret, err = err_or(lambda: r.set_health(pid, health, note=note))
|
||||
inp = {"doc": before, "project_id": pid, "health": health, "note": note,
|
||||
"now": FROZEN_NOW}
|
||||
rec_kernel(rec, name, inp,
|
||||
{"doc": r.doc} if err is None else {"error": err})
|
||||
|
||||
|
||||
def c_health_valid_no_note(rec):
|
||||
_health_case(rec, "health-valid-no-note", "HEALTHY")
|
||||
|
||||
|
||||
def c_health_with_note(rec):
|
||||
_health_case(rec, "health-with-note", "DRIFTED", note="diverged upstream")
|
||||
|
||||
|
||||
def c_health_note_truncated(rec):
|
||||
_health_case(rec, "health-note-truncated-160", "MIGRATION_REQUIRED",
|
||||
note="n" * 200)
|
||||
|
||||
|
||||
def c_health_appends_notes(rec):
|
||||
def setup(r):
|
||||
r.set_health("alpha", "DRIFTED", note="first")
|
||||
_health_case(rec, "health-second-note-appends", "HEALTHY", note="second",
|
||||
setup=setup)
|
||||
|
||||
|
||||
def c_health_invalid(rec):
|
||||
_health_case(rec, "health-invalid-valueerror", "BROKEN")
|
||||
|
||||
|
||||
def c_health_unknown_project(rec):
|
||||
_health_case(rec, "health-unknown-project-keyerror", "HEALTHY",
|
||||
pid="ghost")
|
||||
|
||||
|
||||
# ── set_outstanding_validations (kernel) ─────────────────────────────────────
|
||||
|
||||
def _outstanding_case(rec, name, pid, count, setup=None):
|
||||
r = fresh_registry()
|
||||
r.register(project_id="alpha", scaffold_location="/a")
|
||||
if setup:
|
||||
setup(r)
|
||||
before = copy.deepcopy(r.doc)
|
||||
ret, err = err_or(lambda: r.set_outstanding_validations(pid, count))
|
||||
inp = {"doc": before, "project_id": pid, "count": count}
|
||||
rec_kernel(rec, name, inp,
|
||||
{"doc": r.doc} if err is None else {"error": err})
|
||||
|
||||
|
||||
def c_outstanding_set(rec):
|
||||
_outstanding_case(rec, "outstanding-set-count", "alpha", 4)
|
||||
|
||||
|
||||
def c_outstanding_overwrite(rec):
|
||||
def setup(r):
|
||||
r.set_outstanding_validations("alpha", 9)
|
||||
_outstanding_case(rec, "outstanding-overwrite", "alpha", 2, setup=setup)
|
||||
|
||||
|
||||
def c_outstanding_unknown(rec):
|
||||
_outstanding_case(rec, "outstanding-unknown-keyerror", "ghost", 1)
|
||||
|
||||
|
||||
# ── mark_generation (kernel) ─────────────────────────────────────────────────
|
||||
|
||||
def _mark_case(rec, name, current, last_known_good="", setup=None):
|
||||
r = fresh_registry()
|
||||
r.register(project_id="alpha", scaffold_location="/a",
|
||||
runtime_generation="gen-1")
|
||||
if setup:
|
||||
setup(r)
|
||||
before = copy.deepcopy(r.doc)
|
||||
ret, err = err_or(lambda: r.mark_generation(
|
||||
"alpha", current=current, last_known_good=last_known_good))
|
||||
inp = {"doc": before, "project_id": "alpha", "current": current,
|
||||
"last_known_good": last_known_good}
|
||||
rec_kernel(rec, name, inp,
|
||||
{"doc": r.doc} if err is None else {"error": err})
|
||||
|
||||
|
||||
def c_mark_with_lkg(rec):
|
||||
_mark_case(rec, "mark-generation-with-lkg", "gen-2", "gen-1")
|
||||
|
||||
|
||||
def c_mark_no_lkg(rec):
|
||||
_mark_case(rec, "mark-generation-no-lkg-untouched", "gen-3")
|
||||
|
||||
|
||||
def c_mark_unknown(rec):
|
||||
r = fresh_registry()
|
||||
r.register(project_id="alpha", scaffold_location="/a")
|
||||
before = copy.deepcopy(r.doc)
|
||||
ret, err = err_or(lambda: r.mark_generation(
|
||||
"ghost", current="gen-9", last_known_good="gen-8"))
|
||||
rec_kernel(rec, "mark-generation-unknown-keyerror",
|
||||
{"doc": before, "project_id": "ghost", "current": "gen-9",
|
||||
"last_known_good": "gen-8"}, {"error": err})
|
||||
|
||||
|
||||
# ── resolve_cwd (kernel) ─────────────────────────────────────────────────────
|
||||
|
||||
def _cwd_case(rec, name, register_kw, probe, mutate=None):
|
||||
r = fresh_registry()
|
||||
r.register(**register_kw)
|
||||
if mutate:
|
||||
mutate(r)
|
||||
before = copy.deepcopy(r.doc)
|
||||
ret, err = err_or(lambda: r.resolve_cwd(probe))
|
||||
inp = {"doc": before, "project_id": probe}
|
||||
rec_kernel(rec, name, inp,
|
||||
{"cwd": ret} if err is None else {"error": err})
|
||||
|
||||
|
||||
def c_cwd_git_prefix(rec):
|
||||
_cwd_case(rec, "cwd-git-prefix-stripped",
|
||||
{"project_id": "rune", "scaffold_location": "/x/scaffold",
|
||||
"repo_identity": "git:/repos/rune"}, "rune")
|
||||
|
||||
|
||||
def c_cwd_bare_path(rec):
|
||||
_cwd_case(rec, "cwd-bare-path", {"project_id": "ext",
|
||||
"scaffold_location": "/x/s",
|
||||
"repo_identity": "/tmp/ext/repo"}, "ext")
|
||||
|
||||
|
||||
def c_cwd_worktree_overrides(rec):
|
||||
_cwd_case(rec, "cwd-active-worktree-overrides",
|
||||
{"project_id": "rune", "scaffold_location": "/x/scaffold",
|
||||
"repo_identity": "git:/repos/rune"}, "rune",
|
||||
mutate=lambda r: r.doc["projects"]["rune"].update(
|
||||
{"active_worktree": "/wt/rune"}))
|
||||
|
||||
|
||||
def c_cwd_whitespace_repo(rec):
|
||||
_cwd_case(rec, "cwd-whitespace-repo-refused",
|
||||
{"project_id": "ws", "scaffold_location": "/x/s",
|
||||
"repo_identity": " "}, "ws")
|
||||
|
||||
|
||||
def c_cwd_unknown(rec):
|
||||
_cwd_case(rec, "cwd-unknown-project-refused",
|
||||
{"project_id": "rune", "scaffold_location": "/x/s",
|
||||
"repo_identity": "git:/repos/rune"}, "ghost")
|
||||
|
||||
|
||||
def c_cwd_no_repo_identity(rec):
|
||||
_cwd_case(rec, "cwd-no-repo-identity-refused",
|
||||
{"project_id": "remote-only", "scaffold_location": "/x/s"}, "remote-only")
|
||||
|
||||
|
||||
# ── needs_migration (kernel) ─────────────────────────────────────────────────
|
||||
|
||||
def _migration_case(rec, name, versions, proposed):
|
||||
r = fresh_registry()
|
||||
for pid, ver in versions.items():
|
||||
r.register(project_id=pid, scaffold_location=f"/{pid}",
|
||||
structural_schema_version=ver)
|
||||
out = r.needs_migration(proposed_schema_version=proposed)
|
||||
inp = {"doc": copy.deepcopy(r.doc), "proposed": proposed}
|
||||
rec_kernel(rec, name, inp, {"ids": out})
|
||||
|
||||
|
||||
def c_migration_mixed(rec):
|
||||
_migration_case(rec, "migration-mixed-sorted",
|
||||
{"new": 3, "old": 1, "mid": 2}, 2)
|
||||
|
||||
|
||||
def c_migration_none(rec):
|
||||
_migration_case(rec, "migration-none-older", {"a": 2, "b": 3}, 2)
|
||||
|
||||
|
||||
def c_migration_default_version(rec):
|
||||
_migration_case(rec, "migration-default-version-1", {"a": 1, "b": 1}, 2)
|
||||
|
||||
|
||||
def c_migration_empty(rec):
|
||||
_migration_case(rec, "migration-empty-registry", {}, 5)
|
||||
|
||||
|
||||
# ── load / save (facade-only: FILE IO stays in Python) ──────────────────────
|
||||
|
||||
def c_load_missing(rec):
|
||||
r = fresh_registry()
|
||||
rec_facade(rec, "load-missing-file-default-doc", {"path": "missing"},
|
||||
{"doc": r.doc})
|
||||
|
||||
|
||||
def c_load_valid(rec):
|
||||
d = fresh_dir()
|
||||
pre = {"schema_version": 1, "projects": {"alpha": {"project_id": "alpha"}},
|
||||
"updated_at": FROZEN_NOW}
|
||||
Path(d, "project_registry.json").write_text(
|
||||
json.dumps(pre, indent=1, sort_keys=True))
|
||||
r = oracle_mod.ProjectRegistry(d)
|
||||
rec_facade(rec, "load-valid-file-roundtrip", {"path": "valid"},
|
||||
{"doc": r.doc})
|
||||
|
||||
|
||||
def c_save(rec):
|
||||
r = fresh_registry()
|
||||
r.register(project_id="alpha", scaffold_location="/a",
|
||||
repo_identity="git:/repos/alpha")
|
||||
r.set_health("alpha", "DRIFTED", note="sweep")
|
||||
r.save()
|
||||
text = Path(r.path).read_text()
|
||||
rec_facade(rec, "save-stamps-updated-at-indent1-sortkeys",
|
||||
{"doc": copy.deepcopy(r.doc)}, {"file_text": text},
|
||||
oracle_ret=None)
|
||||
|
||||
|
||||
# ── driver ───────────────────────────────────────────────────────────────────
|
||||
|
||||
DEFAULT_DOC = [c_default_doc]
|
||||
REGISTER = [c_register_defaults, c_register_full, c_register_overwrites,
|
||||
c_register_second_project]
|
||||
GET = [c_get_present, c_get_missing, c_get_empty_registry]
|
||||
ALL = [c_all_empty, c_all_single, c_all_sorted]
|
||||
HEALTH = [c_health_valid_no_note, c_health_with_note, c_health_note_truncated,
|
||||
c_health_appends_notes, c_health_invalid, c_health_unknown_project]
|
||||
OUTSTANDING = [c_outstanding_set, c_outstanding_overwrite, c_outstanding_unknown]
|
||||
MARK = [c_mark_with_lkg, c_mark_no_lkg, c_mark_unknown]
|
||||
CWD = [c_cwd_git_prefix, c_cwd_bare_path, c_cwd_worktree_overrides,
|
||||
c_cwd_whitespace_repo, c_cwd_unknown, c_cwd_no_repo_identity]
|
||||
MIGRATION = [c_migration_mixed, c_migration_none, c_migration_default_version,
|
||||
c_migration_empty]
|
||||
LOAD = [c_load_missing, c_load_valid]
|
||||
SAVE = [c_save]
|
||||
|
||||
GROUPS = [("default_doc", DEFAULT_DOC), ("register", REGISTER), ("get", GET),
|
||||
("all_projects", ALL), ("set_health", HEALTH),
|
||||
("set_outstanding", OUTSTANDING), ("mark_generation", MARK),
|
||||
("resolve_cwd", CWD), ("needs_migration", MIGRATION),
|
||||
("load", LOAD), ("save", SAVE)]
|
||||
|
||||
# kernel symbol per group (None → facade-only group, skipped kernel-side)
|
||||
KERNEL = {
|
||||
"default_doc": "pr_default_doc",
|
||||
"register": "pr_register",
|
||||
"get": "pr_get",
|
||||
"all_projects": "pr_all_projects",
|
||||
"set_health": "pr_set_health",
|
||||
"set_outstanding": "pr_set_outstanding",
|
||||
"mark_generation": "pr_mark_generation",
|
||||
"resolve_cwd": "pr_resolve_cwd",
|
||||
"needs_migration": "pr_needs_migration",
|
||||
"load": None,
|
||||
"save": None,
|
||||
}
|
||||
|
||||
# loader wrapper + slice of `expected` the unwrapped FFI value maps to
|
||||
CALLS = {
|
||||
"default_doc": (lambda w: w["doc"],
|
||||
lambda i, ld: ld.default_doc(now=i["now"])),
|
||||
"register": (lambda w: w,
|
||||
lambda i, ld: ld.register(i["doc"], project_id=i["project_id"],
|
||||
scaffold_location=i["scaffold_location"],
|
||||
repo_identity=i["repo_identity"],
|
||||
forge_repo=i["forge_repo"],
|
||||
default_branch=i["default_branch"],
|
||||
structural_schema_version=i["structural_schema_version"],
|
||||
runtime_generation=i["runtime_generation"],
|
||||
last_known_good=i["last_known_good"],
|
||||
now=i["now"])),
|
||||
"get": (lambda w: w["entry"],
|
||||
lambda i, ld: ld.get(i["doc"], i["project_id"])),
|
||||
"all_projects": (lambda w: w["projects"],
|
||||
lambda i, ld: ld.all_projects(i["doc"])),
|
||||
"set_health": (lambda w: w["doc"],
|
||||
lambda i, ld: ld.set_health(i["doc"], i["project_id"],
|
||||
i["health"], note=i["note"],
|
||||
now=i["now"])),
|
||||
"set_outstanding": (lambda w: w["doc"],
|
||||
lambda i, ld: ld.set_outstanding(i["doc"],
|
||||
i["project_id"],
|
||||
i["count"])),
|
||||
"mark_generation": (lambda w: w["doc"],
|
||||
lambda i, ld: ld.mark_generation(i["doc"],
|
||||
i["project_id"],
|
||||
current=i["current"],
|
||||
last_known_good=i["last_known_good"])),
|
||||
"resolve_cwd": (lambda w: w["cwd"],
|
||||
lambda i, ld: ld.resolve_cwd(i["doc"], i["project_id"])),
|
||||
"needs_migration": (lambda w: w["ids"],
|
||||
lambda i, ld: ld.needs_migration(i["doc"],
|
||||
i["proposed"])),
|
||||
}
|
||||
|
||||
|
||||
def canon(x) -> str:
|
||||
"""Content canonicalization: a sorted dump makes the comparison
|
||||
key-order independent (int keys never occur here)."""
|
||||
return json.dumps(json.loads(json.dumps(x)), sort_keys=True)
|
||||
|
||||
|
||||
def main():
|
||||
frozen = None
|
||||
if REPLAY:
|
||||
with open(TRUTH_PATH) as f:
|
||||
frozen = json.load(f)["cases"]
|
||||
|
||||
groups = {}
|
||||
for group, cases in GROUPS:
|
||||
recs = []
|
||||
|
||||
def rec(name, inp, oracle_ret, expected, _r=recs):
|
||||
entry = {"name": name, "input": inp, "return": oracle_ret,
|
||||
"expected": expected}
|
||||
if REPLAY:
|
||||
old = next(c for c in frozen[group] if c["name"] == name)
|
||||
for key in ("input", "return", "expected"):
|
||||
got, want = entry[key], old[key]
|
||||
if canon(got) != canon(want):
|
||||
print(f"FACADE DIVERGE [{group}] {name} {key}")
|
||||
print(" got: ", json.dumps(got, sort_keys=True, default=str)[:600])
|
||||
print(" want:", json.dumps(want, sort_keys=True, default=str)[:600])
|
||||
recs.append({"name": name, "diverged": key})
|
||||
return
|
||||
_r.append(entry)
|
||||
|
||||
for c in cases:
|
||||
c(rec)
|
||||
groups[group] = recs
|
||||
|
||||
total = sum(len(v) for v in groups.values())
|
||||
if REPLAY:
|
||||
facade_failures = sum(1 for v in groups.values() for c in v if "diverged" in c)
|
||||
|
||||
failures = 0
|
||||
checked = 0
|
||||
for group, cases in frozen.items():
|
||||
spec = CALLS.get(group)
|
||||
if spec is None:
|
||||
continue
|
||||
unwrap, call = spec
|
||||
for case in cases:
|
||||
checked += 1
|
||||
try:
|
||||
got = call(copy.deepcopy(case["input"]), pr_loader)
|
||||
except (ValueError, KeyError) as e:
|
||||
got = (f"ValueError: {e}" if isinstance(e, ValueError)
|
||||
else f"KeyError: {e.args[0]}")
|
||||
want = (case["expected"]["error"]
|
||||
if isinstance(case["expected"], dict)
|
||||
and "error" in case["expected"]
|
||||
else unwrap(case["expected"]))
|
||||
if canon(got) != canon(want):
|
||||
failures += 1
|
||||
print(f"KERNEL DIVERGE [{group}] {case['name']}")
|
||||
print(" got: ", json.dumps(got, sort_keys=True, default=str)[:600])
|
||||
print(" want:", json.dumps(want, sort_keys=True, default=str)[:600])
|
||||
print(f"replay: facade {total - facade_failures}/{total}, "
|
||||
f"kernel {checked - failures}/{checked}")
|
||||
return 1 if (failures or facade_failures) else 0
|
||||
|
||||
doc = {
|
||||
"frozen": FROZEN_NOW,
|
||||
"oracle": "src/rune/project_registry.py",
|
||||
"pin": "RUNE_PR_CORE=python",
|
||||
"note": "Frozen via scripts/gen_project_registry_truth.py against the live "
|
||||
"Python oracle; wall clock frozen at " + FROZEN_NOW +
|
||||
" (FakeTime patched into project_registry.time; epoch " +
|
||||
str(FROZEN_TS) + "). Battery embeds this VERBATIM. Groups `load` "
|
||||
"and `save` are facade-only (FILE IO stays in Python).",
|
||||
"cases": groups,
|
||||
}
|
||||
os.makedirs(os.path.dirname(TRUTH_PATH), exist_ok=True)
|
||||
with open(TRUTH_PATH, "w") as f:
|
||||
json.dump(doc, f, indent=1)
|
||||
f.write("\n")
|
||||
print(f"frozen {total} cases → {os.path.relpath(TRUTH_PATH, REPO)}")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
@@ -0,0 +1,205 @@
|
||||
#!/usr/bin/env python3
|
||||
"""gen_recall_uplift_truth.py — freeze the recall_uplift parity oracle
|
||||
(dichotomy wave #2479 tranche).
|
||||
|
||||
Generator law: RUNE_RU_CORE=python pinned, inputs deep-copied, every
|
||||
expand() branch exercised: empty query, expansion-map matching (k in q /
|
||||
q in k / k in t), store pass (LIKE match, LIMIT 32, distinct-doc
|
||||
co-occurrence, df<5% distinctive filter, 24-term cap), and the best-effort
|
||||
degrade (no store / sqlite error → map terms only).
|
||||
|
||||
PORTABLE LAW: CPython set iteration is hash-random, and the oracle's
|
||||
candidate sort is stable over dict insertion order — so the frozen corpus
|
||||
gives every co-occurring candidate word a DISTINCT doc count (ties would
|
||||
make even Python-vs-Python output order nondeterministic).
|
||||
|
||||
Output: src/rust/recall_uplift/frozen_ru_truth.json — replayed VERBATIM by
|
||||
the crate's cfg(test) battery.
|
||||
"""
|
||||
import copy
|
||||
import json
|
||||
import os
|
||||
import sqlite3
|
||||
import sys
|
||||
import tempfile
|
||||
from datetime import datetime, timezone
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
sys.path.insert(0, str(ROOT / "src"))
|
||||
|
||||
os.environ["RUNE_RU_CORE"] = "python"
|
||||
|
||||
from rune import recall_uplift # noqa: E402
|
||||
|
||||
cases: list = []
|
||||
|
||||
|
||||
def freeze(query: str, corpus, expected):
|
||||
cases.append({
|
||||
"fn": "expand",
|
||||
"args": {"query": query, "corpus": copy.deepcopy(corpus)},
|
||||
"expected": copy.deepcopy(expected),
|
||||
})
|
||||
|
||||
|
||||
def build_db(rows):
|
||||
"""rows: list[str|None] — active entry texts in insertion order."""
|
||||
fd, path = tempfile.mkstemp(suffix=".db")
|
||||
os.close(fd)
|
||||
os.unlink(path)
|
||||
db = sqlite3.connect(path)
|
||||
db.execute("CREATE TABLE entries (id INTEGER PRIMARY KEY AUTOINCREMENT, "
|
||||
"text TEXT, status TEXT)")
|
||||
for text in rows:
|
||||
db.execute("INSERT INTO entries (text, status) VALUES (?, 'active')",
|
||||
(text,))
|
||||
db.commit()
|
||||
db.close()
|
||||
return path
|
||||
|
||||
|
||||
def fetch_corpus(path):
|
||||
db = sqlite3.connect(path)
|
||||
try:
|
||||
return [r[0] for r in db.execute(
|
||||
"SELECT text FROM entries WHERE status='active'").fetchall()]
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
|
||||
def case_store(path, query):
|
||||
"""Freeze one store-backed case: RUNE_STORE_DB pointed at the fixture,
|
||||
corpus embedded verbatim in args."""
|
||||
old = os.environ.get("RUNE_STORE_DB")
|
||||
os.environ["RUNE_STORE_DB"] = path
|
||||
try:
|
||||
corpus = fetch_corpus(path)
|
||||
expected = recall_uplift.expand(query)
|
||||
finally:
|
||||
if old is None:
|
||||
os.environ.pop("RUNE_STORE_DB", None)
|
||||
else:
|
||||
os.environ["RUNE_STORE_DB"] = old
|
||||
freeze(query, corpus, expected)
|
||||
|
||||
|
||||
def case_degraded(query):
|
||||
"""Freeze the no-store degrade (RUNE_STORE_DB points nowhere)."""
|
||||
old = os.environ.get("RUNE_STORE_DB")
|
||||
os.environ["RUNE_STORE_DB"] = "/nonexistent/rune-truth/memory.db"
|
||||
try:
|
||||
expected = recall_uplift.expand(query)
|
||||
finally:
|
||||
if old is None:
|
||||
os.environ.pop("RUNE_STORE_DB", None)
|
||||
else:
|
||||
os.environ["RUNE_STORE_DB"] = old
|
||||
freeze(query, None, expected)
|
||||
|
||||
|
||||
def main() -> None:
|
||||
# ── map-only branches (no store) ─────────────────────────────────────
|
||||
case_degraded("") # empty query → []
|
||||
case_degraded(" ") # whitespace query → []
|
||||
case_degraded("housing crisis") # k in q branch
|
||||
case_degraded("hous") # q in k branch
|
||||
case_degraded("routers and models") # k in t branch (router⊂routers)
|
||||
case_degraded("zebra crossing") # no match at all → [zebra, crossing]
|
||||
case_degraded("CI pipeline") # mixed-case key match, short tokens
|
||||
# map-only over an existing-but-empty store (corpus=[] branch)
|
||||
empty_db = build_db([])
|
||||
case_store(empty_db, "housing crisis")
|
||||
os.unlink(empty_db)
|
||||
|
||||
# ── store pass, engineered corpus A (200 rows, distinct co-occur counts)
|
||||
# matched rows 0..11 (testing 0..3 / memory 4..8 / recall 8..11 — recall
|
||||
# is a seen map term, so it co-occurs but is skipped). NO filler text in
|
||||
# matched rows (filler would become a tie-counted co-occur candidate).
|
||||
rows = ["filler word"] * 200
|
||||
CO = [("allocator", 9), ("snapshot", 8), ("checkpoint", 7), ("ledger", 6),
|
||||
("walrus", 3), ("quixotic", 2), ("zephyr", 1)]
|
||||
for w, n in CO:
|
||||
for r in range(n):
|
||||
rows[r] = (rows[r] + f" {w}").strip()
|
||||
for r in range(4):
|
||||
rows[r] += " testing" # co-occur count 4
|
||||
for r in range(4, 9):
|
||||
rows[r] += " memory" # co-occur count 5
|
||||
for r in range(8, 12):
|
||||
rows[r] += " recall" # in seen → skipped
|
||||
for r in range(4):
|
||||
rows[r] += " pipeline" # w == t quirk: last token skipped
|
||||
for r in range(5):
|
||||
rows[r] += " other" # stopword → skipped
|
||||
for r in range(6):
|
||||
rows[r] += " retrieval" # already in seen → skipped
|
||||
for r in range(20): # df 20/200 = 0.1 → filtered branch
|
||||
rows[r] += " orbital" # co-occur 12 (distinct, top candidate)
|
||||
rows[150] = None # NULL text: counts in total, never matches
|
||||
db_a = build_db(rows)
|
||||
case_store(db_a, "testing memory recall pipeline")
|
||||
os.unlink(db_a)
|
||||
|
||||
# ── 24-term cap boundary (400 rows, co-occur counts 19..1, distinct) ──
|
||||
rows = ["filler word"] * 400
|
||||
for i in range(19): # word in (19-i) matched rows; letters
|
||||
w = "kv" + chr(97 + i) * 2 # only — the tokenizer is [a-z']{4,}
|
||||
for r in range(19 - i):
|
||||
rows[r] = (rows[r] + f" {w}").strip()
|
||||
for r in range(20):
|
||||
rows[r] = f"housing policy{rows[r]}" # no filler in matched rows;
|
||||
# policy co-occurs in all 20 matched rows → df 20/400 = 0.05 → filtered
|
||||
db_b = build_db(rows)
|
||||
case_store(db_b, "housing policy anchor")
|
||||
os.unlink(db_b)
|
||||
|
||||
# ── LIMIT-32 branch (100 rows: matches beyond the first 32 don't co-occur)
|
||||
rows = ["filler word"] * 100
|
||||
for r in range(40):
|
||||
rows[r] = "alpha"
|
||||
for r in range(8):
|
||||
rows[r] += " beta"
|
||||
for r in range(3):
|
||||
rows[r] += " delta"
|
||||
for r in range(4):
|
||||
rows[r] += " gamma" # last token → w == t skip
|
||||
for r in range(33, 40):
|
||||
rows[r] += " omega" # beyond the LIMIT-32 cut → not in co-occur
|
||||
db_c = build_db(rows)
|
||||
case_store(db_c, "alpha beta gamma")
|
||||
os.unlink(db_c)
|
||||
|
||||
# ── sqlite-error degrade: db present, entries table missing ──────────
|
||||
fd, bad = tempfile.mkstemp(suffix=".db")
|
||||
os.close(fd)
|
||||
os.unlink(bad)
|
||||
db = sqlite3.connect(bad)
|
||||
db.execute("CREATE TABLE other (x TEXT)")
|
||||
db.commit()
|
||||
db.close()
|
||||
old = os.environ.get("RUNE_STORE_DB")
|
||||
os.environ["RUNE_STORE_DB"] = bad
|
||||
try:
|
||||
expected = recall_uplift.expand("housing crisis")
|
||||
finally:
|
||||
if old is None:
|
||||
os.environ.pop("RUNE_STORE_DB", None)
|
||||
else:
|
||||
os.environ["RUNE_STORE_DB"] = old
|
||||
freeze("housing crisis", None, expected)
|
||||
os.unlink(bad)
|
||||
|
||||
doc = {
|
||||
"module": "recall_uplift",
|
||||
"pin": "RUNE_RU_CORE=python",
|
||||
"frozen_at": datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ"),
|
||||
"cases": cases,
|
||||
}
|
||||
out = ROOT / "src" / "rust" / "recall_uplift" / "frozen_ru_truth.json"
|
||||
out.write_text(json.dumps(doc, indent=1) + "\n")
|
||||
print(f"frozen {len(cases)} cases -> {out}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,102 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Freeze the dichotomy-wave truth corpus for src/rune/rune_profiler.py.
|
||||
|
||||
LAW (poisoned-truth): RUNE_RP_CORE=python is pinned at the top — truth is
|
||||
generated by the PYTHON ORACLE ONLY.
|
||||
|
||||
The measurement loop (perf_counter_ns over a Python callable / Ollama
|
||||
HTTP call) is host-side by nature and stays in the facade; the PORTABLE
|
||||
LAW is the stats computation over the measured elapsed times. Truth is
|
||||
generated by monkeypatching the oracle's perf_counter_ns to a controlled
|
||||
queue of elapsed deltas — every per-iteration elapsed is a multiple of
|
||||
15625 ns (1/64 ms), a dyadic rational, so every sum/mean/median is f64-
|
||||
exact and the oracle's statistics.mean (exact Fraction) matches the
|
||||
kernel's float math BIT-FOR-BIT.
|
||||
|
||||
profile_function 6 cases (1/2/20/100/101 iterations, unsorted times,
|
||||
single iteration, two-element median)
|
||||
profile_model_call 3 cases (same stats law minus p95/p99)
|
||||
"""
|
||||
import itertools
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
os.environ["RUNE_RP_CORE"] = "python" # poisoned-truth law
|
||||
|
||||
REPO = Path(__file__).resolve().parents[1]
|
||||
sys.path.insert(0, str(REPO / "src"))
|
||||
|
||||
import rune.rune_profiler as rp # noqa: E402
|
||||
|
||||
OUT = REPO / "src" / "rust" / "rune_profiler" / "src" / "frozen_rp_truth.json"
|
||||
|
||||
cases = []
|
||||
UNIT_NS = 15625 # 1/64 ms — dyadic, f64-exact through sums and /1e6
|
||||
|
||||
|
||||
def freeze_stats(fn_name, elapsed_units, iterations):
|
||||
"""Drive the REAL oracle with a controlled perf_counter_ns."""
|
||||
vals, c = [], 0
|
||||
for u in elapsed_units:
|
||||
vals.append(c)
|
||||
vals.append(c + u * UNIT_NS)
|
||||
c += u * UNIT_NS
|
||||
vals.append(c) # pad: 2 calls per iteration consumed
|
||||
clock = iter(itertools.cycle(vals) if False else vals)
|
||||
rp.time.perf_counter_ns = lambda: next(clock)
|
||||
calls = {"n": 0}
|
||||
|
||||
def fake_func():
|
||||
calls["n"] += 1
|
||||
|
||||
if fn_name == "profile_function":
|
||||
r = rp.profile_function(fake_func, iterations=iterations)
|
||||
else:
|
||||
# profile_model_call calls the module-level _execute_ollama —
|
||||
# stub it; the elapsed deltas drive the stats identically
|
||||
def fake_ollama(*a, **k):
|
||||
calls["n"] += 1
|
||||
return "ok"
|
||||
rp._execute_ollama = fake_ollama
|
||||
r = rp.profile_model_call(iterations=iterations)
|
||||
times_ms = [u * UNIT_NS / 1_000_000 for u in elapsed_units]
|
||||
cases.append({"fn": fn_name,
|
||||
"name": f"{'f' if fn_name == 'profile_function' else 'm'}"
|
||||
f"{iterations}_{len(elapsed_units)}_{elapsed_units[0]}"
|
||||
f"{'_shuffled' if len(set(elapsed_units)) > 1 and elapsed_units != sorted(elapsed_units) else ''}",
|
||||
"args": {"times": times_ms, "iterations": iterations},
|
||||
"expect": r})
|
||||
assert calls["n"] == iterations, (calls["n"], iterations)
|
||||
|
||||
|
||||
def main():
|
||||
# ── profile_function ─────────────────────────────────────────────
|
||||
freeze_stats("profile_function", [10], 1)
|
||||
freeze_stats("profile_function", [3, 7], 2) # median = mean of 2
|
||||
freeze_stats("profile_function", [5, 1, 9, 2, 8, 4, 6, 3, 7, 1,
|
||||
2, 9, 4, 8, 1, 6, 2, 7, 3, 5], 20)
|
||||
freeze_stats("profile_function", [1] * 100, 100) # p95/p99 = same value
|
||||
freeze_stats("profile_function",
|
||||
[(i % 13) + 1 for i in range(101)], 101) # odd count median
|
||||
freeze_stats("profile_function",
|
||||
[((i * 37) % 29) + 1 for i in range(100)], 100)
|
||||
|
||||
# ── profile_model_call ───────────────────────────────────────────
|
||||
freeze_stats("profile_model_call", [6400, 3200, 12800, 1600, 6400], 5)
|
||||
freeze_stats("profile_model_call", [1, 2], 2)
|
||||
freeze_stats("profile_model_call", [811], 1)
|
||||
|
||||
OUT.parent.mkdir(parents=True, exist_ok=True)
|
||||
groups = {}
|
||||
for c in cases:
|
||||
groups.setdefault(f"{c['fn']}_cases", []).append(c)
|
||||
OUT.write_text(json.dumps(
|
||||
{"module": "rune_profiler", "unit_ns": UNIT_NS, **groups},
|
||||
indent=1, sort_keys=True) + "\n")
|
||||
print(f"frozen {len(cases)} cases -> {OUT}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,157 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Freeze the dichotomy-wave truth corpus for src/rune/subconscious_pass.py.
|
||||
|
||||
LAW (poisoned-truth): RUNE_SP_CORE=python is pinned at the top — truth is
|
||||
generated by the PYTHON ORACLE ONLY.
|
||||
|
||||
Every public function is exercised across ALL branches:
|
||||
reflect 10 cases (topic_shift hit/same-topic/no-context/
|
||||
empty-context; unresolved_question both; action
|
||||
words; emotional words; short_response; combo)
|
||||
_extract_topic 4 (multiword / single / empty / whitespace)
|
||||
get_recent_findings 4 (default limit / limit>len / limit=0 / sparse)
|
||||
save_reflection 2 (the exact json.dump(indent=2) file bytes — the
|
||||
timestamped FILENAME and the write stay in the
|
||||
facade; the byte law is the kernel's)
|
||||
|
||||
Timestamps: the report `timestamp` and the save filename clock are facade
|
||||
ARGUMENTS (`now`), monkeypatched to a FIXED value so the corpus is
|
||||
byte-stable.
|
||||
"""
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
os.environ["RUNE_SP_CORE"] = "python" # poisoned-truth law
|
||||
|
||||
REPO = Path(__file__).resolve().parents[1]
|
||||
sys.path.insert(0, str(REPO / "src"))
|
||||
|
||||
import rune.subconscious_pass as sp # noqa: E402
|
||||
|
||||
FIXED_NOW = "2026-10-12T00:00:01.000001+00:00"
|
||||
|
||||
OUT = (REPO / "src" / "rust" / "subconscious_pass" / "src"
|
||||
/ "frozen_sp_truth.json")
|
||||
|
||||
cases = []
|
||||
|
||||
|
||||
def case(fn, name, args, expect):
|
||||
cases.append({"fn": fn, "name": name,
|
||||
"args": args, "expect": expect})
|
||||
|
||||
|
||||
class _FixedDatetime(sp.datetime):
|
||||
@classmethod
|
||||
def now(cls, tz=None):
|
||||
return cls(2026, 10, 12, 0, 0, 1, 1, tzinfo=tz)
|
||||
|
||||
|
||||
def main():
|
||||
sp.datetime = _FixedDatetime
|
||||
s = sp.SubconsciousPass()
|
||||
|
||||
# ── reflect ──────────────────────────────────────────────────────
|
||||
for name, user, agent, ctx in [
|
||||
("topic_shift_hit", "how do we deploy the cache layer",
|
||||
"Sure, let's plan it.", {"previous_topic": "index tuning"}),
|
||||
("topic_same", "more on index tuning please",
|
||||
"The index tuning continues.", {"previous_topic": "index tuning"}),
|
||||
("topic_none_extracted", " ",
|
||||
"Empty message.", {"previous_topic": "old topic"}),
|
||||
("no_context", "build the thing", "Building it.", None),
|
||||
("empty_context", "build the thing", "Building it.", {}),
|
||||
("unresolved_question", "What about the cache?",
|
||||
"The cache is handled.", {"previous_topic": "cache"}),
|
||||
("question_answered", "What about the cache?",
|
||||
"Good question — the cache is handled.", {}),
|
||||
("action_requested", "please fix and update the loader",
|
||||
"On it.", {}),
|
||||
("emotional_signal", "I'm frustrated and confused by the drift",
|
||||
"Let's look at it.", {}),
|
||||
("short_response", "x" * 201,
|
||||
"ok"[:10], {}),
|
||||
("combo_all", "build and fix it — I'm frustrated. What now?",
|
||||
"ok", {"previous_topic": "planning"}),
|
||||
]:
|
||||
r = s.reflect(user, agent, ctx)
|
||||
case("reflect", name,
|
||||
{"user": user, "agent": agent, "context": ctx,
|
||||
"now": FIXED_NOW}, r)
|
||||
|
||||
# ── _extract_topic ───────────────────────────────────────────────
|
||||
for name, text, want in [
|
||||
("multiword", "the quick brown fox jumps over the dog",
|
||||
"the quick brown fox jumps"),
|
||||
("single", "hello", "hello"),
|
||||
("empty", "", None),
|
||||
("whitespace", " \t\n ", None),
|
||||
]:
|
||||
got = s._extract_topic(text)
|
||||
case("extract_topic", name, {"text": text}, got)
|
||||
|
||||
# ── get_recent_findings ──────────────────────────────────────────
|
||||
s2 = sp.SubconsciousPass()
|
||||
r1 = s2.reflect("build it", "ok" * 30, {})
|
||||
r2 = s2.reflect("What?", "Nothing.", {})
|
||||
r3 = s2.reflect("no findings here", "x" * 60, {})
|
||||
r4 = s2.reflect("frustrated", "y" * 60, {})
|
||||
case("recent_findings", "default_limit",
|
||||
{"reports": [r1, r2, r3, r4]}, s2.get_recent_findings())
|
||||
case("recent_findings", "limit_2",
|
||||
{"reports": [r1, r2, r3, r4], "limit": 2}, s2.get_recent_findings(2))
|
||||
case("recent_findings", "limit_0",
|
||||
{"reports": [r1, r2, r3, r4], "limit": 0}, s2.get_recent_findings(0))
|
||||
sparse = [{"findings": [{"type": "a", "detail": "d", "severity": "low"}]},
|
||||
{"no_findings_key": True},
|
||||
{"findings": []}]
|
||||
s5 = sp.SubconsciousPass()
|
||||
s5.findings = list(sparse)
|
||||
case("recent_findings", "sparse_reports",
|
||||
{"reports": sparse}, s5.get_recent_findings(10))
|
||||
case("recent_findings", "empty",
|
||||
{"reports": []}, _recent([], 10))
|
||||
|
||||
# ── save_reflection (exact file bytes; naming/write is facade) ───
|
||||
for i, report in enumerate([r1, {"a": 1, "list": [1, 2.5], "s": "é"}]):
|
||||
s3 = sp.SubconsciousPass()
|
||||
# call the real oracle save path with a redirected dir
|
||||
orig_dir = sp.REFLECTIONS_DIR
|
||||
tmp = Path(__import__("tempfile").mkdtemp(prefix="sp_truth_"))
|
||||
sp.REFLECTIONS_DIR = str(tmp)
|
||||
try:
|
||||
s3.save_reflection(report)
|
||||
files = sorted(tmp.iterdir())
|
||||
assert len(files) == 1
|
||||
content = files[0].read_text()
|
||||
finally:
|
||||
sp.REFLECTIONS_DIR = orig_dir
|
||||
import shutil
|
||||
shutil.rmtree(tmp, ignore_errors=True)
|
||||
# the frozen corpus round-trips the report sort_keys=True, so the
|
||||
# replayable byte law is json.dump over THAT dict order
|
||||
sorted_report = json.loads(json.dumps(report, sort_keys=True))
|
||||
case("save_reflection", f"report{i}", {"report": sorted_report},
|
||||
json.dumps(sorted_report, indent=2))
|
||||
|
||||
OUT.parent.mkdir(parents=True, exist_ok=True)
|
||||
groups = {}
|
||||
for c in cases:
|
||||
groups.setdefault(f"{c['fn']}_cases", []).append(c)
|
||||
OUT.write_text(json.dumps(
|
||||
{"module": "subconscious_pass", "now": FIXED_NOW, **groups},
|
||||
indent=1, sort_keys=True) + "\n")
|
||||
print(f"frozen {len(cases)} cases -> {OUT}")
|
||||
|
||||
|
||||
def _recent(reports, limit):
|
||||
all_findings = []
|
||||
for r in reports[-limit:]:
|
||||
all_findings.extend(r.get("findings", []))
|
||||
return all_findings
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,127 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Freeze the dichotomy-wave truth corpus for src/rune/subsystem_health.py.
|
||||
|
||||
LAW (poisoned-truth): RUNE_SH_CORE=python is pinned at the top — truth is
|
||||
generated by the PYTHON ORACLE ONLY.
|
||||
|
||||
Portable law = the registry state transition + snapshot deep-copy + the
|
||||
record_failure pure core (message composition, tier event mapping, payload
|
||||
[:300] truncation). The side-effect shell (logging, event_bus.emit,
|
||||
filer callbacks, process-global wiring, RotatingFileHandler) stays in the
|
||||
facade — it cannot cross FFI.
|
||||
|
||||
registry.record 8 cases (tier0/1 first+repeat, tier2, multi-subsystem,
|
||||
last_error/tier overwrite)
|
||||
registry.snapshot 2 (deep copy incl. nested mutation isolation)
|
||||
record_failure core 6 (message with/without context, tier events 0/1/2/
|
||||
unknown, payload truncation at 300 incl. multibyte)
|
||||
"""
|
||||
import copy
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
os.environ["RUNE_SH_CORE"] = "python" # poisoned-truth law
|
||||
|
||||
REPO = Path(__file__).resolve().parents[1]
|
||||
sys.path.insert(0, str(REPO / "src"))
|
||||
|
||||
import rune.subsystem_health as sh # noqa: E402
|
||||
|
||||
OUT = (REPO / "src" / "rust" / "subsystem_health" / "src"
|
||||
/ "frozen_sh_truth.json")
|
||||
|
||||
cases = []
|
||||
|
||||
|
||||
def case(fn, name, args, expect):
|
||||
cases.append({"fn": fn, "name": name,
|
||||
"args": args, "expect": expect})
|
||||
|
||||
|
||||
def reg_state(reg):
|
||||
return {"entries": copy.deepcopy(reg._entries),
|
||||
"surfaced": sorted(reg._surfaced)}
|
||||
|
||||
|
||||
def run_record(reg, subsystem, error, tier):
|
||||
surface = reg.record(subsystem, error, tier)
|
||||
return {"state": reg_state(reg), "surface": surface}
|
||||
|
||||
|
||||
def main():
|
||||
# ── registry.record ──────────────────────────────────────────────
|
||||
for name, seq in [
|
||||
("tier1_first", [("cache", "boom", 1)]),
|
||||
("tier1_repeat", [("cache", "boom", 1), ("cache", "bam", 1)]),
|
||||
("tier0_first", [("auth", "explode", 0)]),
|
||||
("tier2_never_surfaces", ["store", "degraded", 2] and
|
||||
[("store", "degraded", 2), ("store", "worse", 2)]),
|
||||
("multi_subsystem", [("a", "x", 1), ("b", "y", 1), ("a", "z", 0)]),
|
||||
("tier_overwrite", [("svc", "e1", 2), ("svc", "e2", 0)]),
|
||||
("tier1_after_tier2_still_surfaces_once",
|
||||
[("svc2", "e1", 2), ("svc2", "e2", 1), ("svc2", "e3", 1)]),
|
||||
("negative_tier_surfaces", [("neg", "e", -1), ("neg", "e2", -1)]),
|
||||
]:
|
||||
reg = sh.SubsystemHealthRegistry()
|
||||
out = None
|
||||
for subsystem, error, tier in seq:
|
||||
out = run_record(reg, subsystem, error, tier)
|
||||
args_last = seq[-1]
|
||||
# freeze: input = PRE-state + one record call; expect = post-state
|
||||
pre = sh.SubsystemHealthRegistry()
|
||||
for subsystem, error, tier in seq[:-1]:
|
||||
run_record(pre, subsystem, error, tier)
|
||||
case("registry_record", name,
|
||||
{"entries": pre._entries, "surfaced": sorted(pre._surfaced),
|
||||
"subsystem": args_last[0], "error": args_last[1],
|
||||
"tier": args_last[2]},
|
||||
out)
|
||||
|
||||
# ── registry.snapshot ────────────────────────────────────────────
|
||||
reg = sh.SubsystemHealthRegistry()
|
||||
reg.record("cache", "boom", 1)
|
||||
reg.record("store", "degraded", 2)
|
||||
snap = reg.snapshot()
|
||||
case("registry_snapshot", "deep_copy", {"entries": reg._entries}, snap)
|
||||
snap2 = reg.snapshot()
|
||||
snap2["cache"]["failures"] = 999
|
||||
case("registry_snapshot", "isolation",
|
||||
{"entries": reg._entries}, reg.snapshot())
|
||||
|
||||
# ── record_failure pure core ─────────────────────────────────────
|
||||
class _Err(Exception):
|
||||
pass
|
||||
|
||||
for name, err, ctx, tier in [
|
||||
("message_plain", _Err("disk on fire"), "", 1),
|
||||
("message_with_context", _Err("disk on fire"), "during flush", 1),
|
||||
("tier0_event", _Err("x"), "", 0),
|
||||
("tier2_event", _Err("x"), "", 2),
|
||||
("tier_unknown_event", _Err("x"), "", 99),
|
||||
("payload_truncation", _Err("é" * 400), "", 1),
|
||||
]:
|
||||
message = f"{type(err).__name__}: {err}"
|
||||
if ctx:
|
||||
message = f"{message} ({ctx})"
|
||||
event_type = sh._TIER_EVENTS.get(tier, "storage.degraded")
|
||||
payload_error = message[:300]
|
||||
case("failure_core", name,
|
||||
{"error_type": type(err).__name__, "error": str(err),
|
||||
"context": ctx, "tier": tier},
|
||||
{"message": message, "event_type": event_type,
|
||||
"payload_error": payload_error})
|
||||
|
||||
OUT.parent.mkdir(parents=True, exist_ok=True)
|
||||
groups = {}
|
||||
for c in cases:
|
||||
groups.setdefault(f"{c['fn']}_cases", []).append(c)
|
||||
OUT.write_text(json.dumps(
|
||||
{"module": "subsystem_health", **groups}, indent=1, sort_keys=True)
|
||||
+ "\n")
|
||||
print(f"frozen {len(cases)} cases -> {OUT}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,173 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Freeze the oracle truth for the GO port of src/rune/verification_layer.py
|
||||
(dichotomy wave #2479, GO). Every public function, every branch, against the
|
||||
LIVE oracle (which delegates to the Rust evidence_provenance core — parity
|
||||
already proven 33/33). Rows pin the full VerificationResult field set AND the
|
||||
byte-exact json.dumps(to_dict()) rendering. handle_hook + PLUGIN pinned too.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
sys.path.insert(0, str(ROOT / "src"))
|
||||
|
||||
from rune import verification_layer as vl # noqa: E402
|
||||
|
||||
FIXTURE = ROOT / "src/go/verification_layer/.fixtures/exists.txt"
|
||||
|
||||
rows: list[dict] = []
|
||||
|
||||
|
||||
def row(name, fn, verifier_type, result):
|
||||
got = getattr(vl, fn)(dict(result), verifier_type) if fn == "verify" \
|
||||
else getattr(vl, fn)(dict(result))
|
||||
rows.append({
|
||||
"name": name,
|
||||
"fn": fn,
|
||||
"verifier_type": verifier_type,
|
||||
"result": result,
|
||||
"want": got.to_dict(),
|
||||
"want_json": json.dumps(got.to_dict()),
|
||||
})
|
||||
|
||||
|
||||
def hook_row(name, event, data=None):
|
||||
got = vl.handle_hook(event) if data is None else vl.handle_hook(event, data)
|
||||
rows.append({
|
||||
"name": name,
|
||||
"fn": "handle_hook",
|
||||
"verifier_type": "",
|
||||
"result": {"event": event, "data": data},
|
||||
"want": got,
|
||||
"want_json": json.dumps(got, sort_keys=True),
|
||||
})
|
||||
|
||||
|
||||
# ── verify_exit_code: every branch ──────────────────────────────────────────
|
||||
row("exit_code:absent-defaults-0", "verify_exit_code", "exit_code", {})
|
||||
row("exit_code:zero-pass", "verify_exit_code", "exit_code", {"exit_code": 0})
|
||||
row("exit_code:one-fail", "verify_exit_code", "exit_code", {"exit_code": 1})
|
||||
row("exit_code:negative-fail", "verify_exit_code", "exit_code", {"exit_code": -3})
|
||||
row("exit_code:float-zero-pass", "verify_exit_code", "exit_code", {"exit_code": 0.0})
|
||||
row("exit_code:float-frac-fail", "verify_exit_code", "exit_code", {"exit_code": 0.5})
|
||||
row("exit_code:bool-true-fails", "verify_exit_code", "exit_code", {"exit_code": True})
|
||||
row("exit_code:bool-false-passes", "verify_exit_code", "exit_code", {"exit_code": False})
|
||||
row("exit_code:str-zero-not-equal", "verify_exit_code", "exit_code", {"exit_code": "0"})
|
||||
row("exit_code:null-not-equal", "verify_exit_code", "exit_code", {"exit_code": None})
|
||||
row("exit_code:big-int-evidence", "verify_exit_code", "exit_code", {"exit_code": 91247})
|
||||
|
||||
# ── verify_unit_test ────────────────────────────────────────────────────────
|
||||
row("unit_test:all-defaults-total-zero-fails", "verify_unit_test", "unit_test", {})
|
||||
row("unit_test:pass", "verify_unit_test", "unit_test",
|
||||
{"tests_passed": 10, "tests_failed": 0, "tests_total": 10})
|
||||
row("unit_test:two-fail", "verify_unit_test", "unit_test",
|
||||
{"tests_passed": 3, "tests_failed": 2, "tests_total": 5})
|
||||
row("unit_test:failed-zero-total-positive", "verify_unit_test", "unit_test",
|
||||
{"tests_failed": 0, "tests_total": 7})
|
||||
row("unit_test:bool-failed-false-total-true", "verify_unit_test", "unit_test",
|
||||
{"tests_failed": False, "tests_total": True})
|
||||
row("unit_test:failed-true-counts-1", "verify_unit_test", "unit_test",
|
||||
{"tests_failed": True, "tests_total": 1})
|
||||
row("unit_test:str-total-ignored", "verify_unit_test", "unit_test",
|
||||
{"tests_total": "5", "tests_failed": 0})
|
||||
|
||||
# ── verify_schema ───────────────────────────────────────────────────────────
|
||||
row("schema:defaults-pass", "verify_schema", "schema", {})
|
||||
row("schema:valid-true-no-errors", "verify_schema", "schema", {"schema_valid": True})
|
||||
row("schema:invalid-fail", "verify_schema", "schema", {"schema_valid": False})
|
||||
row("schema:errors-fail", "verify_schema", "schema",
|
||||
{"schema_valid": True, "schema_errors": ["bad type at $.a", "missing $.b"]})
|
||||
row("schema:empty-errors-pass", "verify_schema", "schema",
|
||||
{"schema_valid": True, "schema_errors": []})
|
||||
row("schema:errors-nonlist-ignored", "verify_schema", "schema",
|
||||
{"schema_valid": True, "schema_errors": "nope"})
|
||||
row("schema:errors-with-nested-values", "verify_schema", "schema",
|
||||
{"schema_valid": False, "schema_errors": [3, {"path": "$.x", "msg": "type"}, True]})
|
||||
row("schema:valid-null-defaults-true", "verify_schema", "schema",
|
||||
{"schema_valid": None})
|
||||
row("schema:valid-zero-falsy", "verify_schema", "schema", {"schema_valid": 0})
|
||||
row("schema:valid-truthy-str", "verify_schema", "schema", {"schema_valid": "yes"})
|
||||
row("schema:unicode-error-escaping", "verify_schema", "schema",
|
||||
{"schema_valid": False, "schema_errors": ["ungültig bei „$.pfad“"]})
|
||||
|
||||
# ── verify_file_exists (fixture file is committed next to the port) ────────
|
||||
row("file_exists:exists-pass", "verify_file_exists", "file_exists",
|
||||
{"file_path": str(FIXTURE)})
|
||||
row("file_exists:missing-fail", "verify_file_exists", "file_exists",
|
||||
{"file_path": str(ROOT / "src/go/verification_layer/.fixtures/definitely_missing.txt")})
|
||||
row("file_exists:empty-path-fail", "verify_file_exists", "file_exists",
|
||||
{"file_path": ""})
|
||||
row("file_exists:dir-counts", "verify_file_exists", "file_exists",
|
||||
{"file_path": str(ROOT / "src/go/verification_layer/.fixtures")})
|
||||
row("file_exists:absent-key-fail", "verify_file_exists", "file_exists", {})
|
||||
|
||||
# ── verify_file_changed ─────────────────────────────────────────────────────
|
||||
row("file_changed:empty-expected-pass", "verify_file_changed", "file_changed",
|
||||
{"file_path": "a.txt", "expected_hash": "", "actual_hash": "deadbeef"})
|
||||
row("file_changed:match-pass", "verify_file_changed", "file_changed",
|
||||
{"file_path": "a.txt", "expected_hash": "cafe", "actual_hash": "cafe"})
|
||||
row("file_changed:mismatch-fail", "verify_file_changed", "file_changed",
|
||||
{"file_path": "a.txt", "expected_hash": "cafe", "actual_hash": "beef"})
|
||||
row("file_changed:no-keys-pass", "verify_file_changed", "file_changed", {})
|
||||
|
||||
# ── verify_tool_receipt ─────────────────────────────────────────────────────
|
||||
row("tool_receipt:default-pass", "verify_tool_receipt", "tool_receipt", {})
|
||||
row("tool_receipt:success-true", "verify_tool_receipt", "tool_receipt",
|
||||
{"tool_success": True, "tool_name": "grep"})
|
||||
row("tool_receipt:fail-with-error", "verify_tool_receipt", "tool_receipt",
|
||||
{"tool_success": False, "tool_error": "boom: exit 2"})
|
||||
row("tool_receipt:fail-empty-error", "verify_tool_receipt", "tool_receipt",
|
||||
{"tool_success": False})
|
||||
row("tool_receipt:zero-falsy", "verify_tool_receipt", "tool_receipt",
|
||||
{"tool_success": 0, "tool_error": "falsy rc"})
|
||||
row("tool_receipt:error-str-truthy", "verify_tool_receipt", "tool_receipt",
|
||||
{"tool_success": "ok"})
|
||||
|
||||
# ── verify_constraint_check ─────────────────────────────────────────────────
|
||||
row("constraint:default-pass", "verify_constraint_check", "constraint_check", {})
|
||||
row("constraint:met-true-pass", "verify_constraint_check", "constraint_check",
|
||||
{"constraints_met": True})
|
||||
row("constraint:unmet-fail", "verify_constraint_check", "constraint_check",
|
||||
{"constraints_met": False})
|
||||
row("constraint:violations-fail", "verify_constraint_check", "constraint_check",
|
||||
{"constraints_met": True, "constraint_violations": ["no secrets in diff"]})
|
||||
row("constraint:violations-nested", "verify_constraint_check", "constraint_check",
|
||||
{"constraints_met": False,
|
||||
"constraint_violations": [{"rule": "R1", "at": 7}, 12, None]})
|
||||
|
||||
# ── verify dispatch (named + unknown) ───────────────────────────────────────
|
||||
for vt in ("exit_code", "unit_test", "schema", "file_exists", "file_changed",
|
||||
"tool_receipt", "constraint_check"):
|
||||
row(f"dispatch:{vt}", "verify", vt, {})
|
||||
row("dispatch:unknown-verifier", "verify", "nonexistent_verifier", {})
|
||||
row("dispatch:unknown-empty-name", "verify", "", {})
|
||||
row("dispatch:unknown-case-sensitive", "verify", "Exit_Code", {"exit_code": 0})
|
||||
|
||||
# ── handle_hook ─────────────────────────────────────────────────────────────
|
||||
hook_row("hook:event-only", "verification.request")
|
||||
hook_row("hook:event-with-data", "verification.done", {"passed": True})
|
||||
|
||||
# ── PLUGIN manifest ─────────────────────────────────────────────────────────
|
||||
rows.append({
|
||||
"name": "plugin:manifest",
|
||||
"fn": "plugin",
|
||||
"verifier_type": "",
|
||||
"result": {},
|
||||
"want": vl.PLUGIN,
|
||||
"want_json": json.dumps(vl.PLUGIN, sort_keys=True),
|
||||
})
|
||||
|
||||
doc = {
|
||||
"frozen_ts": "2026-10-12T00:00:00+00:00",
|
||||
"generator": "scripts/gen_verification_layer_truth.py — live oracle "
|
||||
"src/rune/verification_layer.py (Rust evidence_provenance core)",
|
||||
"rows": rows,
|
||||
}
|
||||
out = ROOT / "src/go/verification_layer/truth.json"
|
||||
out.write_text(json.dumps(doc, indent=1, ensure_ascii=False) + "\n",
|
||||
encoding="utf-8")
|
||||
print(f"frozen {len(rows)} rows → {out}")
|
||||
@@ -0,0 +1,43 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Open the #2479 dichotomy-wave construction contract for the BATCH-A
|
||||
modules (iteration, subconscious_pass, rune_profiler, subsystem_health).
|
||||
Walks draft→approved→building in ONE process (shared-store clobber hazard).
|
||||
|
||||
Usage: open_contract_batch_a.py <module> [contract_id]
|
||||
"""
|
||||
import json
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "src"))
|
||||
|
||||
from rune.construction_interface import ConstructionInterface
|
||||
|
||||
module = sys.argv[1]
|
||||
sym = {"iteration": "IT", "subconscious_pass": "SP",
|
||||
"rune_profiler": "RP", "subsystem_health": "SH"}[module]
|
||||
|
||||
iface = ConstructionInterface(builder="factory-batch-a")
|
||||
|
||||
cid = sys.argv[2] if len(sys.argv) > 2 else None
|
||||
r = iface.submit_need(
|
||||
f"Port src/rune/{module}.py to FULL Rust (dichotomy factory, issue "
|
||||
f"#2479 tranche): doc-envelope FFI, ctypes loader, facade delegation "
|
||||
f"with RUNE_{sym}_CORE=python pin, frozen-truth battery, live parity "
|
||||
f"100%, Phase B language-advantage pass.",
|
||||
origin="builder",
|
||||
contract_id=cid,
|
||||
)
|
||||
print("submit_need:", json.dumps(r)[:400])
|
||||
if not r.get("success"):
|
||||
print("NEED FAILED — aborting")
|
||||
sys.exit(1)
|
||||
c = r["ids"]["contract_id"]
|
||||
print("contract_id:", c)
|
||||
|
||||
for phase in ("approved", "building"):
|
||||
t = iface.transition(c, phase)
|
||||
print(f"transition->{phase}:", json.dumps(t)[:300])
|
||||
if not t.get("success"):
|
||||
sys.exit(1)
|
||||
print("OPENED", c)
|
||||
@@ -0,0 +1,40 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Open the #2479 dichotomy-wave construction contract for inference_trace
|
||||
(Rust port). Draft→approved→building in ONE process; verifying→closed at
|
||||
delivery with SHA evidence."""
|
||||
import json
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "src"))
|
||||
|
||||
from rune.construction_interface import ConstructionInterface
|
||||
|
||||
iface = ConstructionInterface(builder="factory-wave")
|
||||
|
||||
r = iface.submit_need(
|
||||
"Port src/rune/inference_trace.py to FULL Rust (dichotomy factory, "
|
||||
"issue #2479 tranche): name-preserving doc-envelope FFI for the "
|
||||
"inference-trace kernel (price_for, trace path resolution, record "
|
||||
"serialization, rotation policy decision, read/limit selection; "
|
||||
"file IO stays Python-side), ctypes loader it_loader.py mirroring "
|
||||
"broker_loader.py (free via capability_scoring cs_free_string), facade "
|
||||
"delegation with RUNE_IT_CORE=python pin, frozen-truth battery "
|
||||
"generated against the live Python oracle, live parity 100%, Phase B "
|
||||
"language-advantage pass.",
|
||||
origin="builder",
|
||||
)
|
||||
print("submit_need:", json.dumps(r)[:200])
|
||||
if not r.get("success"):
|
||||
print("NEED FAILED — aborting")
|
||||
sys.exit(1)
|
||||
cid = r["ids"]["contract_id"]
|
||||
print("contract_id:", cid)
|
||||
|
||||
for phase in ("approved", "building"):
|
||||
t = iface.transition(cid, phase)
|
||||
print(f"transition->{phase}:", json.dumps(t)[:200])
|
||||
if not t.get("success"):
|
||||
sys.exit(1)
|
||||
|
||||
print("CONTRACT", cid, "IN BUILDING (close at delivery with SHAs)")
|
||||
@@ -0,0 +1,82 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Open + walk the #2479 dichotomy-wave construction contracts for the
|
||||
batch-g quartet (execution_trace, pane_authority, skill_compiler,
|
||||
meaningful_events) to verifying. Full lifecycle in ONE process."""
|
||||
import json
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "src"))
|
||||
|
||||
from rune.construction_interface import ConstructionInterface
|
||||
|
||||
OBJECTIVES = {
|
||||
"execution_trace": (
|
||||
"Complete the execution_trace dichotomy port to FULL Rust (#2479 "
|
||||
"tranche): journal task.outcome parsing kernel + receipt assembly "
|
||||
"(build_receipt/trace_task law incl. None-when-no-outcome, "
|
||||
"assigned_model→payload.model fallback and completed_at fallback) "
|
||||
"as doc-envelope FFI (et_* symbols, free via capability_scoring "
|
||||
"cs_free_string — no free re-export); et_loader mirroring "
|
||||
"broker_loader.py; facade owns the file I/O (journal read stays "
|
||||
"host-side per Law 7), RUNE_ET_CORE=python pin; frozen-oracle "
|
||||
"battery (every public function, all branches incl. malformed "
|
||||
"journal lines and empty task_id); live parity 100%; Phase B "
|
||||
"language-advantage pass (typed Receipt struct)."
|
||||
),
|
||||
"pane_authority": (
|
||||
"Complete the pane_authority dichotomy port to FULL Rust (#2479 "
|
||||
"tranche): two-pane authority semantics kernel (classify_utterance "
|
||||
"control/amendment/discussion law incl. mid-utterance control "
|
||||
"phrases and the stop/pause/cancel status mapping) as doc-envelope "
|
||||
"FFI (pa_* symbols, free via capability_scoring cs_free_string — "
|
||||
"no free re-export); pa_loader mirroring broker_loader.py; facade "
|
||||
"delegation with RUNE_PA_CORE=python pin; frozen-oracle battery "
|
||||
"(every public function, all branches, deep-copied task inputs); "
|
||||
"live parity 100%; Phase B language-advantage pass (exhaustive "
|
||||
"UtteranceKind enum)."
|
||||
),
|
||||
"skill_compiler": (
|
||||
"Complete the skill_compiler dichotomy port to FULL Rust (#2479 "
|
||||
"tranche): sequence_id (Python json.dumps(sort_keys=True) byte law "
|
||||
"+ sha256[:12]) + candidate_skill (evidence-or-{} falsy law) + "
|
||||
"sandbox gate (default shape check, passed=all, vacuous-True empty "
|
||||
"steps) + promote gate as doc-envelope FFI (sk_* symbols, free via "
|
||||
"capability_scoring cs_free_string — no free re-export); sk_loader "
|
||||
"mirroring broker_loader.py; facade owns the replay_fn callback "
|
||||
"(host-side by nature) with RUNE_SKC_CORE=python pin; "
|
||||
"frozen-oracle battery (every public function, all branches incl. "
|
||||
"replay-crash-is-failure); live parity 100%; Phase B "
|
||||
"language-advantage pass (typed SandboxVerdict)."
|
||||
),
|
||||
"meaningful_events": (
|
||||
"Complete the meaningful_events dichotomy port to FULL Rust (#2479 "
|
||||
"tranche): the eight-trigger table (ordered) + should_fire law "
|
||||
"(unknown/stop/absent/rate-limited with Python int() truncation, "
|
||||
"exact reason strings) + fire_due ordering + mark_fired copy "
|
||||
"semantics as doc-envelope FFI (me_* symbols, free via "
|
||||
"capability_scoring cs_free_string — no free re-export); me_loader "
|
||||
"mirroring broker_loader.py; facade owns the wall clock (now "
|
||||
"passed per op), RUNE_ME_CORE=python pin; frozen-oracle battery "
|
||||
"(every trigger class, all branches incl. truncation cases); live "
|
||||
"parity 100%; Phase B language-advantage pass (const trigger "
|
||||
"table of typed records)."
|
||||
),
|
||||
}
|
||||
|
||||
iface = ConstructionInterface(builder="factory-batch-g")
|
||||
|
||||
for mod, objective in OBJECTIVES.items():
|
||||
r = iface.submit_need(objective, origin="builder")
|
||||
print(f"submit_need[{mod}]:", json.dumps(r)[:200])
|
||||
if not r.get("success"):
|
||||
print("NEED FAILED — aborting")
|
||||
sys.exit(1)
|
||||
cid = r["ids"]["contract_id"]
|
||||
print(f"contract_id[{mod}]:", cid)
|
||||
for phase in ("approved", "building", "verifying"):
|
||||
t = iface.transition(cid, phase)
|
||||
print(f"transition[{mod}]->{phase}:", json.dumps(t)[:120])
|
||||
if not t.get("success"):
|
||||
sys.exit(1)
|
||||
print(f"CONTRACT[{mod}]", cid, "verifying")
|
||||
@@ -0,0 +1,37 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Open + walk the #2479 dichotomy-wave construction contract for
|
||||
connection_pool to verifying. Full lifecycle in ONE process."""
|
||||
import json
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "src"))
|
||||
|
||||
from rune.construction_interface import ConstructionInterface
|
||||
|
||||
iface = ConstructionInterface(builder="factory-batch-b")
|
||||
|
||||
r = iface.submit_need(
|
||||
"Complete the connection_pool dichotomy port to FULL Rust (#2479 "
|
||||
"tranche, programme verdict RUST): pool-bookkeeping kernel (pooling "
|
||||
"order, stale removal, oldest-first eviction, status) as doc-envelope "
|
||||
"FFI (cpool_new/begin_get/commit/close_all/status, free via "
|
||||
"capability_scoring cs_free_string — no free re-export); cpool_loader "
|
||||
"mirroring broker_loader.py; facade delegation with RUNE_CPOOL_CORE="
|
||||
"python pin (sqlite handles stay in the facade); frozen-oracle battery "
|
||||
"(16 cases, every kernel branch, frozen against a real sqlite-backed "
|
||||
"pool); live parity 100% (60-seed random-op sweep); classification "
|
||||
"flip GO→RUST; Phase B language-advantage pass.",
|
||||
origin="builder",
|
||||
)
|
||||
print("submit_need:", json.dumps(r)[:300])
|
||||
if not r.get("success"):
|
||||
print("NEED FAILED — aborting"); sys.exit(1)
|
||||
cid = r["ids"]["contract_id"]
|
||||
print("contract_id:", cid)
|
||||
for phase in ("approved", "building", "verifying"):
|
||||
t = iface.transition(cid, phase)
|
||||
print(f"transition->{phase}:", json.dumps(t)[:150])
|
||||
if not t.get("success"):
|
||||
sys.exit(1)
|
||||
print("CONTRACT", cid, "verifying")
|
||||
@@ -0,0 +1,41 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Open + walk the #2479 dichotomy-wave construction contract for
|
||||
construction_depth to verifying. Full lifecycle in ONE process (shared-store
|
||||
clobber hazard). The close step runs separately (close_contract script)
|
||||
after the register commit, per the dichotomy-wave store pattern."""
|
||||
import json
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "src"))
|
||||
|
||||
from rune.construction_interface import ConstructionInterface
|
||||
|
||||
iface = ConstructionInterface(builder="factory-batch-b")
|
||||
|
||||
r = iface.submit_need(
|
||||
"Complete the construction_depth dichotomy port to FULL Rust (#2479 "
|
||||
"tranche): oracle-exact depth kernel (UNKNOWN ceremony/bypassed, "
|
||||
"PROJECT_BUILD full-lifecycle strings, EXISTING_PROJECT_CHANGE base "
|
||||
"ceremony + wide-surface escalation restored to oracle truth), "
|
||||
"doc-envelope FFI (cd_select_depth, free via capability_scoring "
|
||||
"cs_free_string — no free re-export), cd_loader mirroring "
|
||||
"broker_loader.py, facade delegation with RUNE_CD_CORE=python pin, "
|
||||
"frozen-oracle battery (40 cases, every public function/branch), live "
|
||||
"parity 100%, Phase B language-advantage pass.",
|
||||
origin="builder",
|
||||
)
|
||||
print("submit_need:", json.dumps(r)[:400])
|
||||
if not r.get("success"):
|
||||
print("NEED FAILED — aborting")
|
||||
sys.exit(1)
|
||||
cid = r["ids"]["contract_id"]
|
||||
print("contract_id:", cid)
|
||||
|
||||
for phase in ("approved", "building", "verifying"):
|
||||
t = iface.transition(cid, phase)
|
||||
print(f"transition->{phase}:", json.dumps(t)[:200])
|
||||
if not t.get("success"):
|
||||
sys.exit(1)
|
||||
|
||||
print("CONTRACT", cid, "verifying")
|
||||
@@ -0,0 +1,114 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Open → walk → close the #2479 dichotomy-wave construction contracts for
|
||||
the forge + verification_layer GO batch (factory-batch-c). Full lifecycle
|
||||
per contract, both back-to-back in ONE process (shared-store clobber hazard)."""
|
||||
import json
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "src"))
|
||||
|
||||
from rune.construction_interface import ConstructionInterface
|
||||
|
||||
iface = ConstructionInterface(builder="factory-batch-c")
|
||||
|
||||
CONTRACTS = [
|
||||
{
|
||||
"module": "forge",
|
||||
"cid": "BUILDER-2026-10-12T17:43:29Z-ea9485-factory-batch-c",
|
||||
"need": (
|
||||
"Port src/rune/forge.py to FULL Go (dichotomy factory, issue "
|
||||
"#2479 tranche, batch-c): the §18L forge boundary — ForgeAdapter "
|
||||
"protocol, ForgeUnavailable, GiteaForge (env/push/commit_status/"
|
||||
"ci_runs_for_sha/create_or_patch_release), default_forge, and the "
|
||||
"scripts/gitea_auth credential ladder — name-preserving in "
|
||||
"src/go/forge with IO behind CommandRunner/HTTPTransport/"
|
||||
"TokenSource seams, frozen-truth battery (34 rows), live parity "
|
||||
"100%, Phase B goroutine fan-out (Snapshot), compile-time seam "
|
||||
"assertions."
|
||||
),
|
||||
"evidence": (
|
||||
"forge parity port — 34/34 frozen rows replay verbatim (truth "
|
||||
"regenerated live twice, byte-stable), battery covers every "
|
||||
"public fn + branch (push truncation, status/runs HTTP capture "
|
||||
"byte-parity, credential ladder all 3 rungs, release argv + "
|
||||
"last-200 fallback), race-clean, gofmt/vet clean; Python oracle "
|
||||
"tests green. SHAs: cdcae1ea (feat) ac114c32 (Phase B), pushed "
|
||||
"gitea/master on top of 78a36204."
|
||||
),
|
||||
"scope_files": [
|
||||
"src/rune/forge.py",
|
||||
"src/go/forge/main.go",
|
||||
"src/go/forge/truth_test.go",
|
||||
"src/go/forge/phaseb.go",
|
||||
"src/go/forge/phaseb_test.go",
|
||||
"src/go/forge/truth.json",
|
||||
"scripts/gen_forge_truth.py",
|
||||
],
|
||||
},
|
||||
{
|
||||
"module": "verification_layer",
|
||||
"cid": "BUILDER-2026-10-12T17:46:05Z-c1a38e-factory-batch-c",
|
||||
"need": (
|
||||
"Port src/rune/verification_layer.py to FULL Go (dichotomy "
|
||||
"factory, issue #2479 tranche, batch-c): all seven verifiers "
|
||||
"(exit_code, unit_test, schema, file_exists, file_changed, "
|
||||
"tool_receipt, constraint_check) + unknown-verifier dispatch, "
|
||||
"VerificationResult.to_dict byte-rendering, handle_hook, "
|
||||
"PLUGIN manifest — the Rust evidence_provenance semantics "
|
||||
"carried 1:1 (verification.rs), name-preserving in "
|
||||
"src/go/verification_layer with the FS probe behind a seam, "
|
||||
"frozen-truth battery (62 rows), live parity 100%, Phase B "
|
||||
"Verifier registry + VerifyAll goroutine fan-out."
|
||||
),
|
||||
"evidence": (
|
||||
"verification_layer parity port — 62/62 frozen rows replay "
|
||||
"verbatim byte-for-byte (truth regenerated live twice, "
|
||||
"byte-stable) + 15/15 live CLI parity sweep incl. unicode "
|
||||
"ensure_ascii escapes and non-string failures pass-through; "
|
||||
"battery covers every verifier pass+fail branches, dispatch, "
|
||||
"hook, PLUGIN; race-clean, gofmt/vet clean; Python tests green "
|
||||
"(test_verification_layer_demo + test_evidence_provenance_shims, "
|
||||
"7 passed). SHAs: d9009cc2 (feat) 2750eba0 (Phase B), pushed "
|
||||
"gitea/master on top of 78a36204."
|
||||
),
|
||||
"scope_files": [
|
||||
"src/rune/verification_layer.py",
|
||||
"src/go/verification_layer/main.go",
|
||||
"src/go/verification_layer/truth_test.go",
|
||||
"src/go/verification_layer/phaseb.go",
|
||||
"src/go/verification_layer/phaseb_test.go",
|
||||
"src/go/verification_layer/truth.json",
|
||||
"scripts/gen_verification_layer_truth.py",
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
cids = []
|
||||
for spec in CONTRACTS:
|
||||
r = iface.submit_need(spec["need"], origin="builder",
|
||||
contract_id=spec["cid"])
|
||||
print("submit_need:", json.dumps(r)[:200])
|
||||
if not r.get("success"):
|
||||
print("NEED FAILED — aborting")
|
||||
sys.exit(1)
|
||||
cid = r["ids"]["contract_id"]
|
||||
cids.append(cid)
|
||||
print("contract_id:", cid)
|
||||
|
||||
for phase in ("approved", "building", "verifying"):
|
||||
t = iface.transition(cid, phase)
|
||||
print(f"transition->{phase}:", json.dumps(t)[:150])
|
||||
if not t.get("success"):
|
||||
sys.exit(1)
|
||||
|
||||
c = iface.close(cid, observed_evidence=[{
|
||||
"kind": "port_evidence",
|
||||
"detail": spec["evidence"],
|
||||
}], scope_files=spec["scope_files"])
|
||||
print("close:", json.dumps(c)[:200])
|
||||
print("CONTRACT", cid, spec["module"],
|
||||
"closed" if c.get("success") else "CLOSE_FAILED")
|
||||
print("-" * 60)
|
||||
|
||||
print("ALL CONTRACTS:", json.dumps(cids))
|
||||
@@ -0,0 +1,37 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Open + walk the #2479 dichotomy-wave construction contract for
|
||||
intent_continuity to verifying. Full lifecycle in ONE process."""
|
||||
import json
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "src"))
|
||||
|
||||
from rune.construction_interface import ConstructionInterface
|
||||
|
||||
iface = ConstructionInterface(builder="factory-batch-b")
|
||||
|
||||
r = iface.submit_need(
|
||||
"Complete the intent_continuity dichotomy port to FULL Rust (#2479 "
|
||||
"tranche): ledger transition kernel (create/create_detour/resolve/"
|
||||
"resolve_detour/supersede + record validation, oracle-exact error "
|
||||
"strings and PARK-the-looked-up-object semantics) as doc-envelope FFI "
|
||||
"(ic_* symbols, free via capability_scoring cs_free_string — no free "
|
||||
"re-export); ic_loader mirroring broker_loader.py; facade owns file "
|
||||
"persistence + clock (now passed per op), RUNE_IC_CORE=python pin; "
|
||||
"frozen-oracle battery (16 cases, every transition/branch, stubbed "
|
||||
"clock); live parity 100% (16/16 frozen + 40/40 fuzz seeds); Phase B "
|
||||
"language-advantage pass (positional transitions).",
|
||||
origin="builder",
|
||||
)
|
||||
print("submit_need:", json.dumps(r)[:300])
|
||||
if not r.get("success"):
|
||||
print("NEED FAILED — aborting"); sys.exit(1)
|
||||
cid = r["ids"]["contract_id"]
|
||||
print("contract_id:", cid)
|
||||
for phase in ("approved", "building", "verifying"):
|
||||
t = iface.transition(cid, phase)
|
||||
print(f"transition->{phase}:", json.dumps(t)[:150])
|
||||
if not t.get("success"):
|
||||
sys.exit(1)
|
||||
print("CONTRACT", cid, "verifying")
|
||||
@@ -0,0 +1,37 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Open + walk the #2479 dichotomy-wave construction contract for
|
||||
recall_uplift to verifying. Full lifecycle in ONE process."""
|
||||
import json
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "src"))
|
||||
|
||||
from rune.construction_interface import ConstructionInterface
|
||||
|
||||
iface = ConstructionInterface(builder="factory-batch-b")
|
||||
|
||||
r = iface.submit_need(
|
||||
"Complete the recall_uplift dichotomy port to FULL Rust (#2479 "
|
||||
"tranche): expansion kernel (tokenization, expansion map, co-occurrence "
|
||||
"distinct-doc counting, LIMIT-32 like-match, distinctive-df filter, "
|
||||
"term caps, best-effort degrade) as doc-envelope FFI (ru_expand, free "
|
||||
"via capability_scoring cs_free_string — no free re-export); ru_loader "
|
||||
"mirroring broker_loader.py; facade fetches the active corpus and "
|
||||
"delegates all compute, RUNE_RU_CORE=python pin; frozen-oracle battery "
|
||||
"(12 cases, every branch, tie-free engineered corpora); live parity "
|
||||
"100% (12/12 frozen + 300/300 fuzz seed 2479); Phase B "
|
||||
"language-advantage pass.",
|
||||
origin="builder",
|
||||
)
|
||||
print("submit_need:", json.dumps(r)[:300])
|
||||
if not r.get("success"):
|
||||
print("NEED FAILED — aborting"); sys.exit(1)
|
||||
cid = r["ids"]["contract_id"]
|
||||
print("contract_id:", cid)
|
||||
for phase in ("approved", "building", "verifying"):
|
||||
t = iface.transition(cid, phase)
|
||||
print(f"transition->{phase}:", json.dumps(t)[:150])
|
||||
if not t.get("success"):
|
||||
sys.exit(1)
|
||||
print("CONTRACT", cid, "verifying")
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,17 @@
|
||||
module rune/curator
|
||||
|
||||
go 1.25.0
|
||||
|
||||
require modernc.org/sqlite v1.49.1
|
||||
|
||||
require (
|
||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/ncruces/go-strftime v1.0.0 // indirect
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||
golang.org/x/sys v0.42.0 // indirect
|
||||
modernc.org/libc v1.72.0 // indirect
|
||||
modernc.org/mathutil v1.7.1 // indirect
|
||||
modernc.org/memory v1.11.0 // indirect
|
||||
)
|
||||
@@ -0,0 +1,21 @@
|
||||
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/ncruces/go-strftime v1.0.0 h1:HMFp8mLCTPp341M/ZnA4qaf7ZlsbTc+miZjCLOFAw7w=
|
||||
github.com/ncruces/go-strftime v1.0.0/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo=
|
||||
golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||
modernc.org/libc v1.72.0 h1:IEu559v9a0XWjw0DPoVKtXpO2qt5NVLAnFaBbjq+n8c=
|
||||
modernc.org/libc v1.72.0/go.mod h1:tTU8DL8A+XLVkEY3x5E/tO7s2Q/q42EtnNWda/L5QhQ=
|
||||
modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU=
|
||||
modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg=
|
||||
modernc.org/memory v1.11.0 h1:o4QC8aMQzmcwCK3t3Ux/ZHmwFPzE6hf2Y5LbkRs+hbI=
|
||||
modernc.org/memory v1.11.0/go.mod h1:/JP4VbVC+K5sU2wZi9bHoq2MAkCnrt2r98UGeSK7Mjw=
|
||||
modernc.org/sqlite v1.49.1 h1:dYGHTKcX1sJ+EQDnUzvz4TJ5GbuvhNJa8Fg6ElGx73U=
|
||||
modernc.org/sqlite v1.49.1/go.mod h1:m0w8xhwYUVY3H6pSDwc3gkJ/irZT/0YEXwBlhaxQEew=
|
||||
@@ -0,0 +1,160 @@
|
||||
// grading.go — parity port of src/rune/grading.py (Python fallback surface,
|
||||
// RUNE_GR_CORE=python pinned in the truth generator).
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var ruleWeak = []string{"should", "prefer", "instead of", "lead with", "first", "before ",
|
||||
"avoid", "favour", "keep ", "stop ", "start ", "use "}
|
||||
var ruleStrong = []string{"always", "never ", "must", "do not", "don't", "i always",
|
||||
"i must", "i should"}
|
||||
var affirmSignals = []string{"i protect", "i am", "i reveal", "i factor", "i dissolve",
|
||||
"i respect", "i teach", "i make", "i write", "i use the tiers", "i check", "i stop"}
|
||||
var userFactSignals = []string{"name", "email", "role", "lives in", "located in", "daw",
|
||||
"os:", "shell", "has a dog", "has a cat", "prefers", "works in", "works at",
|
||||
"teaches", "studies", "interested in", "financial", "income", "dietary",
|
||||
"location", "my home", "my machine"}
|
||||
var userAttrDecisive = []string{"prefers", "lives in", "located in", "teaches",
|
||||
"studies", "works at", "works in", "interested in", "dietary", "income", "financial"}
|
||||
var projectSignals = []string{"project", "current work", "task", "todo", "goal",
|
||||
"milestone", "deadline", "in progress", "blocked", "decision", "spec", "wip",
|
||||
"building ", "developing ", "course", "channel", "migration", "feature",
|
||||
"fixing", "working on", "active projects", "current state"}
|
||||
var constitutionSignals = []string{"add this to the constitution", "constitution",
|
||||
"always do", "i will always", "must always", "inviolable"}
|
||||
var docSignals = []string{"book", "chapter", "document", "transcript", "pdf", "epub",
|
||||
"verbatim", "diary entry", "palace drawer", "gutenberg"}
|
||||
|
||||
// affirmDict keeps the oracle's insertion order — the first matching key wins.
|
||||
var affirmDict = []struct{ key, positive string }{
|
||||
{"never defend billionaires", "I prioritise collective wellbeing and factual, caring explanations"},
|
||||
{"don't defend billionaires", "I prioritise collective wellbeing and factual, caring explanations"},
|
||||
{"never equate socialism with big government", "I describe socialism accurately, on its own terms"},
|
||||
{"never offer rigid plans", "I offer three gentle options when direction is sought"},
|
||||
{"don't offer rigid plans", "I offer three gentle options when direction is sought"},
|
||||
{"never hedge", "I answer directly, without hedging"},
|
||||
{"don't hedge", "I answer directly, without hedging"},
|
||||
{"do not overresearch", "I act on the quick answer when one will do"},
|
||||
{"don't overresearch", "I act on the quick answer when one will do"},
|
||||
{"do not overclaim", "I name evidence as evidence and confirmed causes as confirmed"},
|
||||
{"never overclaim", "I name evidence as evidence and confirmed causes as confirmed"},
|
||||
{"do not stall", "I act decisively once the root cause is understood"},
|
||||
{"don't stall", "I act decisively once the root cause is understood"},
|
||||
{"do not touch the production server", "I protect the production environment and ask before touching it"},
|
||||
{"never touch the production server", "I protect the production environment and ask before touching it"},
|
||||
{"ask permission before modifying anything on the server", "I check with the user before touching anything on the server"},
|
||||
{"do not use capitalistic frameworks", "I use factual, material, caring, non-blaming explanations"},
|
||||
{"never use hustle frameworks", "I use factual, material, caring, non-blaming explanations"},
|
||||
{"do not offer plans", "I offer three gentle options when direction is sought"},
|
||||
{"don't offer plans", "I offer three gentle options when direction is sought"},
|
||||
{"do not search endlessly", "I stop, research properly, and find the root cause"},
|
||||
{"don't iterate endlessly", "I stop, research properly, and find the root cause"},
|
||||
}
|
||||
|
||||
func containsAny(s string, subs []string) int {
|
||||
n := 0
|
||||
for _, sub := range subs {
|
||||
if strings.Contains(s, sub) {
|
||||
n++
|
||||
}
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
func hasAny(s string, subs []string) bool {
|
||||
for _, sub := range subs {
|
||||
if strings.Contains(s, sub) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// AffirmRule rewrites a perceived demand/rule as a positive affirmation; only
|
||||
// known-correct rewrites are applied (AFFIRM_DICT), unknown prohibitions pass
|
||||
// through unchanged.
|
||||
func AffirmRule(fact string) string {
|
||||
f := strings.TrimSpace(strings.TrimLeft(strings.TrimSpace(fact), "-* "))
|
||||
low := strings.Join(strings.Fields(strings.ToLower(f)), " ")
|
||||
alt := strings.ReplaceAll(strings.Trim(low, "."), " ", " ")
|
||||
for _, pair := range affirmDict {
|
||||
if strings.Contains(low, pair.key) || strings.Contains(alt, pair.key) {
|
||||
return pair.positive
|
||||
}
|
||||
}
|
||||
return f
|
||||
}
|
||||
|
||||
// GradeFact returns the tier (G0..G4) for a fact/rule based on type + language.
|
||||
func GradeFact(fact, target string) string {
|
||||
userName := strings.TrimSpace(strings.ToLower(os.Getenv("MEMORY_USER_NAME")))
|
||||
low := strings.ToLower(fact)
|
||||
if hasAny(low, constitutionSignals) {
|
||||
return "constitution"
|
||||
}
|
||||
if hasAny(low, docSignals) {
|
||||
return "mempalace"
|
||||
}
|
||||
ruleScore := containsAny(low, ruleStrong)*2 + containsAny(low, ruleWeak)
|
||||
affirmScore := containsAny(low, affirmSignals)
|
||||
userScore := containsAny(low, userFactSignals)
|
||||
projScore := containsAny(low, projectSignals)
|
||||
if target == "operating" {
|
||||
return "operating"
|
||||
}
|
||||
if target == "persona" {
|
||||
return "persona"
|
||||
}
|
||||
nameToken := fmt.Sprintf(" %s ", userName)
|
||||
nameTokenPresent := false
|
||||
if userName != "" {
|
||||
nameTokenPresent = strings.Contains(" "+low+" ", nameToken)
|
||||
}
|
||||
decisiveAttr := hasAny(low, userAttrDecisive)
|
||||
isPersonalAttr := (decisiveAttr || userScore >= 2) &&
|
||||
!hasAny(low, []string{"i ", "do not", "always", "never "}) &&
|
||||
!nameTokenPresent
|
||||
if isPersonalAttr {
|
||||
return "human"
|
||||
}
|
||||
if ruleScore >= 2 || affirmScore >= 1 {
|
||||
return "operating"
|
||||
}
|
||||
if target == "project" || projScore >= 2 {
|
||||
return "project"
|
||||
}
|
||||
if userScore >= 2 || target == "human" {
|
||||
return "human"
|
||||
}
|
||||
if target == "skill" || strings.Contains(low, "procedure") || strings.Contains(low, "steps") {
|
||||
return "skill"
|
||||
}
|
||||
return "hindsight"
|
||||
}
|
||||
|
||||
// TierOf — human label for a grade, for journaling / canary output.
|
||||
func TierOf(grade string) string {
|
||||
switch grade {
|
||||
case "constitution":
|
||||
return "G0"
|
||||
case "operating":
|
||||
return "G1"
|
||||
case "persona":
|
||||
return "G2p"
|
||||
case "human":
|
||||
return "G2"
|
||||
case "project":
|
||||
return "G2"
|
||||
case "skill":
|
||||
return "P"
|
||||
case "mempalace":
|
||||
return "G4"
|
||||
case "hindsight":
|
||||
return "G3"
|
||||
}
|
||||
return "G3"
|
||||
}
|
||||
@@ -0,0 +1,272 @@
|
||||
// lexicon.go — parity port of src/rune/lexicon_evolution.py (Python fallback
|
||||
// surface, RUNE_LE_CORE=python pinned in the truth generator). The pure
|
||||
// lexical decisions: content words, light stemming, distinctiveness (df <
|
||||
// 5%), coherence verdicts, promotion eligibility.
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var leWordRe = regexp.MustCompile(`[a-z]{4,}`)
|
||||
|
||||
// SAME stopword set as memory_store._STOPWORDS (single source of truth would
|
||||
// be ideal; keep in sync — see lexicon_evolution.py).
|
||||
var leStop = map[string]bool{
|
||||
"the": true, "and": true, "for": true, "with": true, "that": true, "this": true,
|
||||
"from": true, "into": true, "when": true, "then": true, "were": true, "have": true,
|
||||
"been": true, "will": true, "was": true, "are": true, "but": true, "not": true,
|
||||
"you": true, "your": true, "also": true, "its": true, "his": true, "her": true,
|
||||
"him": true, "over": true, "under": true, "them": true, "they": true,
|
||||
"there": true, "about": true, "after": true, "what": true, "which": true,
|
||||
"who": true, "how": true, "why": true, "where": true, "while": true, "just": true,
|
||||
"more": true, "each": true, "than": true, "very": true, "such": true,
|
||||
"some": true, "only": true, "prefers": true, "needs": true, "wants": true,
|
||||
"likes": true,
|
||||
}
|
||||
|
||||
// Hard stop for evolution: generic framing words never carry a relation.
|
||||
var leFraming = map[string]bool{
|
||||
"work": true, "always": true, "way": true, "thing": true, "value": true,
|
||||
"identity": true, "persona": true, "someone": true, "something": true,
|
||||
"never": true, "before": true, "after": true, "then": true, "when": true,
|
||||
"while": true, "must": true, "should": true, "will": true, "would": true,
|
||||
"could": true, "can": true, "have": true, "has": true, "had": true,
|
||||
"been": true, "being": true, "used": true, "use": true, "uses": true,
|
||||
"make": true, "makes": true, "made": true, "keep": true, "keeps": true,
|
||||
"take": true, "takes": true, "took": true, "get": true, "gets": true,
|
||||
"got": true, "going": true,
|
||||
}
|
||||
|
||||
var leNegation = map[string]bool{
|
||||
"not": true, "never": true, "don't": true, "doesn't": true, "cannot": true,
|
||||
"can't": true, "no": true, "nor": true, "neither": true, "without": true,
|
||||
"avoid": true, "reject": true, "against": true, "refuses": true, "refuse": true,
|
||||
"opposed": true,
|
||||
}
|
||||
|
||||
const leDistinctiveDF = 0.05
|
||||
|
||||
func leContentWords(text string) []string {
|
||||
var out []string
|
||||
for _, w := range leWordRe.FindAllString(strings.ToLower(text), -1) {
|
||||
if !leStop[w] && !leFraming[w] {
|
||||
out = append(out, w)
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// leStem — light morphological fold; suffix rules ordered longest-first.
|
||||
func leStem(w string) string {
|
||||
rules := []struct{ suf, fold string }{
|
||||
{"ationally", ""}, {"ableness", ""}, {"iveness", ""},
|
||||
{"ation", ""}, {"ating", ""}, {"ative", "e"},
|
||||
{"atively", "e"}, {"ability", ""}, {"ities", "y"},
|
||||
{"ingly", ""}, {"fully", ""}, {"ness", ""}, {"ment", ""},
|
||||
{"itive", "e"}, {"atable", "e"}, {"ated", ""}, {"ates", "e"},
|
||||
{"ions", ""}, {"ings", ""}, {"able", "e"}, {"ible", "e"},
|
||||
{"ing", "e"}, {"ed", ""}, {"es", ""}, {"ly", ""}, {"s", ""}, {"e", ""},
|
||||
}
|
||||
for _, r := range rules {
|
||||
if len(w)-len(r.suf) >= 4 && strings.HasSuffix(w, r.suf) {
|
||||
w = w[:len(w)-len(r.suf)]
|
||||
if r.fold != "" {
|
||||
w += r.fold
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
// y -> i fold (honesty -> honest)
|
||||
if len(w) > 4 && strings.HasSuffix(w, "y") && !strings.ContainsRune("aeiouy", rune(w[len(w)-2])) {
|
||||
w = w[:len(w)-1]
|
||||
}
|
||||
return w
|
||||
}
|
||||
|
||||
func leStemSet(text string) map[string]bool {
|
||||
set := map[string]bool{}
|
||||
for _, w := range leWordRe.FindAllString(strings.ToLower(text), -1) {
|
||||
set[leStem(w)] = true
|
||||
}
|
||||
return set
|
||||
}
|
||||
|
||||
func leDistinctiveTerms(fact string, corpusTexts []string) map[string]bool {
|
||||
total := len(corpusTexts)
|
||||
if total == 0 {
|
||||
total = 1
|
||||
}
|
||||
factTerms := map[string]bool{}
|
||||
for _, w := range leContentWords(fact) {
|
||||
factTerms[w] = true
|
||||
}
|
||||
if len(factTerms) == 0 {
|
||||
return map[string]bool{}
|
||||
}
|
||||
docTerms := map[string]int{}
|
||||
for _, t := range corpusTexts {
|
||||
for s := range leStemSet(t) {
|
||||
docTerms[s]++
|
||||
}
|
||||
}
|
||||
out := map[string]bool{}
|
||||
for t := range factTerms {
|
||||
if float64(docTerms[leStem(t)]) < maxFloat(float64(total)*leDistinctiveDF, 1.5) {
|
||||
out[t] = true
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func maxFloat(a, b float64) float64 {
|
||||
if a > b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func leSharedDistinctive(fact string, existingValues, corpusTexts []string) map[string]bool {
|
||||
distinct := leDistinctiveTerms(fact, corpusTexts)
|
||||
if len(distinct) == 0 {
|
||||
return map[string]bool{}
|
||||
}
|
||||
shared := map[string]bool{}
|
||||
for _, val := range existingValues {
|
||||
for _, w := range leWordRe.FindAllString(strings.ToLower(val), -1) {
|
||||
s := leStem(w)
|
||||
for term := range distinct {
|
||||
if leStem(term) == s {
|
||||
shared[term] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return shared
|
||||
}
|
||||
|
||||
// Coherence — classify a new value against existing ones, lexicon-aware:
|
||||
// "conflict" | "consistent" | "novel".
|
||||
func Coherence(fact string, existingValues []string, corpusTexts []string) string {
|
||||
var values []string
|
||||
for _, v := range existingValues {
|
||||
if strings.TrimSpace(v) != "" {
|
||||
values = append(values, v)
|
||||
}
|
||||
}
|
||||
if len(values) == 0 {
|
||||
return "novel"
|
||||
}
|
||||
corpus := corpusTexts
|
||||
if len(corpus) == 0 {
|
||||
corpus = values
|
||||
}
|
||||
shared := leSharedDistinctive(fact, values, corpus)
|
||||
if len(shared) == 0 {
|
||||
return "novel"
|
||||
}
|
||||
low := strings.ToLower(fact)
|
||||
hasNeg := false
|
||||
for n := range leNegation {
|
||||
if strings.Contains(low, n) {
|
||||
hasNeg = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if hasNeg {
|
||||
for t := range shared {
|
||||
if strings.Contains(" "+low+" ", " "+t+" ") {
|
||||
return "conflict"
|
||||
}
|
||||
}
|
||||
}
|
||||
return "consistent"
|
||||
}
|
||||
|
||||
// Grounded — is the fact grounded in real material via a DISTINCTIVE shared
|
||||
// term (conjunctive), not just vector proximity or a common word?
|
||||
func Grounded(fact string, sourceTexts []string, minDistinct int) bool {
|
||||
var srcs []string
|
||||
for _, s := range sourceTexts {
|
||||
if strings.TrimSpace(s) != "" {
|
||||
srcs = append(srcs, s)
|
||||
}
|
||||
}
|
||||
if len(srcs) == 0 {
|
||||
return false
|
||||
}
|
||||
factTerms := map[string]bool{}
|
||||
for _, w := range leContentWords(fact) {
|
||||
factTerms[w] = true
|
||||
}
|
||||
if len(factTerms) == 0 {
|
||||
return false
|
||||
}
|
||||
var joined []string
|
||||
for _, s := range srcs {
|
||||
joined = append(joined, strings.ToLower(s))
|
||||
}
|
||||
stems := leStemSet(strings.Join(joined, " "))
|
||||
hits := 0
|
||||
for t := range factTerms {
|
||||
if stems[leStem(t)] {
|
||||
hits++
|
||||
if hits >= minDistinct {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// PromoteEligible — is a persona/constitution promotion eligible, lexicon-aware?
|
||||
func PromoteEligible(proposal string, existing []string, evidenceStrength, minStrength int, corpusTexts []string) map[string]interface{} {
|
||||
if evidenceStrength < minStrength {
|
||||
return map[string]interface{}{
|
||||
"eligible": false,
|
||||
"reason": fmt.Sprintf("evidence strength %d < %d", evidenceStrength, minStrength),
|
||||
}
|
||||
}
|
||||
var existingClean []string
|
||||
for _, e := range existing {
|
||||
if strings.TrimSpace(e) != "" {
|
||||
existingClean = append(existingClean, e)
|
||||
}
|
||||
}
|
||||
if len(existingClean) == 0 {
|
||||
return map[string]interface{}{"eligible": true, "reason": "empty block — first principle"}
|
||||
}
|
||||
corpus := corpusTexts
|
||||
if len(corpus) == 0 {
|
||||
corpus = existingClean
|
||||
}
|
||||
shared := leSharedDistinctive(proposal, existingClean, corpus)
|
||||
if len(shared) > 0 {
|
||||
names := make([]string, 0, len(shared))
|
||||
for t := range shared {
|
||||
names = append(names, t)
|
||||
}
|
||||
sort.Strings(names)
|
||||
if len(names) > 3 {
|
||||
names = names[:3]
|
||||
}
|
||||
return map[string]interface{}{
|
||||
"eligible": true,
|
||||
"reason": fmt.Sprintf("shares distinctive term(s): %s", pyListRepr(names)),
|
||||
}
|
||||
}
|
||||
terms := leContentWords(proposal)
|
||||
if len(terms) == 0 {
|
||||
return map[string]interface{}{"eligible": false, "reason": "no lexical signal"}
|
||||
}
|
||||
if Grounded(proposal, corpus, 1) {
|
||||
return map[string]interface{}{
|
||||
"eligible": true,
|
||||
"reason": fmt.Sprintf("novel axis grounded in corpus (%d terms)", len(terms)),
|
||||
}
|
||||
}
|
||||
return map[string]interface{}{"eligible": false, "reason": "novel axis not grounded in absorbed material — parked"}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
// main.go — curator CLI: replay the frozen oracle truth battery.
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
func main() {
|
||||
if len(os.Args) < 2 || os.Args[1] != "replay" {
|
||||
fmt.Fprintln(os.Stderr, "usage: curator replay <truth.json>")
|
||||
os.Exit(2)
|
||||
}
|
||||
path := "truth.json"
|
||||
if len(os.Args) > 2 {
|
||||
path = os.Args[2]
|
||||
}
|
||||
fails := RunTruth(path)
|
||||
if len(fails) > 0 {
|
||||
for _, f := range fails {
|
||||
fmt.Println(f)
|
||||
}
|
||||
fmt.Fprintf(os.Stderr, "PARITY FAILED: %d divergence(s)\n", len(fails))
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Printf("PARITY OK: %d cases exact-equal vs the Python oracle\n", truthRowCount())
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
// main_cli.go — main() CLI surface replay. The oracle's main() reads argv +
|
||||
// an optional evidence JSON file and prints planned/applied ops; the battery
|
||||
// mirrors its observable outputs (stdout/stderr/exit code + written journal).
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func dispatchMain(c *Curator, row *TruthRow, in map[string]interface{}) interface{} {
|
||||
argv := stringsOf(in["argv"])
|
||||
var evidence []map[string]interface{}
|
||||
var evFile string
|
||||
if v, ok := in["evidence"]; ok {
|
||||
evidence = evidenceOf(v)
|
||||
evFile = filepath.Join(filepath.Dir(c.StoreDB), "evidence.json")
|
||||
b, _ := json.Marshal(evidence)
|
||||
os.WriteFile(evFile, b, 0o644)
|
||||
}
|
||||
var out, errBuf bytes.Buffer
|
||||
c.Out = &out
|
||||
c.Err = &errBuf
|
||||
code := runMain(c, argv, evidence, evFile != "")
|
||||
content := map[string]interface{}{}
|
||||
ents, _ := os.ReadDir(c.JournalDir)
|
||||
for _, e := range ents {
|
||||
b, _ := os.ReadFile(filepath.Join(c.JournalDir, e.Name()))
|
||||
content[e.Name()] = string(b)
|
||||
}
|
||||
res := map[string]interface{}{"stdout": out.String(), "stderr": errBuf.String(), "code": float64(code)}
|
||||
if row.Name == "apply_output" {
|
||||
res["journal"] = content
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// runMain mirrors curator.main()'s observable behavior.
|
||||
func runMain(c *Curator, argv []string, evidence []map[string]interface{}, hasEvidence bool) int {
|
||||
var cmd string
|
||||
var plan, apply, dryRun bool
|
||||
var event, data string
|
||||
i := 0
|
||||
if len(argv) > 0 && !strings.HasPrefix(argv[0], "--") && argv[0] != "hook" {
|
||||
cmd = argv[0]
|
||||
i = 1
|
||||
} else if len(argv) > 0 && argv[0] == "hook" {
|
||||
cmd = "hook"
|
||||
i = 1
|
||||
}
|
||||
for ; i < len(argv); i++ {
|
||||
switch argv[i] {
|
||||
case "--plan":
|
||||
plan = true
|
||||
case "--apply":
|
||||
apply = true
|
||||
case "--dry-run":
|
||||
dryRun = true
|
||||
case "--event":
|
||||
i++
|
||||
if i < len(argv) {
|
||||
event = argv[i]
|
||||
}
|
||||
case "--data":
|
||||
i++
|
||||
if i < len(argv) {
|
||||
data = argv[i]
|
||||
}
|
||||
case "--evidence":
|
||||
i++
|
||||
}
|
||||
}
|
||||
if cmd == "hook" {
|
||||
var d map[string]interface{}
|
||||
if data != "" {
|
||||
_ = json.Unmarshal([]byte(data), &d)
|
||||
}
|
||||
res := c.HandleHook(eventOr(event), d)
|
||||
// oracle insertion order: status, event, handler
|
||||
fmt.Fprintf(c.Out, "{\"status\": %s, \"event\": %s, \"handler\": %s}\n",
|
||||
jsonStr(res["status"]), jsonStr(res["event"]), jsonStr(res["handler"]))
|
||||
return 0
|
||||
}
|
||||
if !hasEvidence {
|
||||
// memory_store seam blocked → no auto evidence → exit 1
|
||||
fmt.Fprintln(c.Err, "No evidence provided and no recent entries found.")
|
||||
return 1
|
||||
}
|
||||
doApply := apply || (!plan && !dryRun)
|
||||
journalLines, ops := c.RunPlan(evidence, doApply)
|
||||
if !doApply {
|
||||
fmt.Fprintln(c.Out, "=== planned ops (dry-run) ===")
|
||||
for _, j := range journalLines {
|
||||
fmt.Fprintf(c.Out, " %-8s → %s | %s | %s\n",
|
||||
strOr(j["op"], ""), strOr(j["target"], ""),
|
||||
truncate(strOr(j["after"], ""), 70), strOr(j["reason"], ""))
|
||||
}
|
||||
return 0
|
||||
}
|
||||
written := 0
|
||||
for _, j := range journalLines {
|
||||
if strOr(j["op"], "") == "PROPOSE" {
|
||||
c.JournalAppend(j)
|
||||
written++
|
||||
} else if r := strOr(j["reason"], ""); r == "" || r == "dry-run" {
|
||||
c.JournalAppend(j)
|
||||
written++
|
||||
}
|
||||
}
|
||||
fmt.Fprintf(c.Out, "Applied %d ops; %d logged to journal; index saved.\n", len(ops), written)
|
||||
for _, j := range journalLines {
|
||||
fmt.Fprintf(c.Out, " %-8s → %s | %s\n",
|
||||
strOr(j["op"], ""), strOr(j["target"], ""), truncate(strOr(j["after"], ""), 70))
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func eventOr(e string) string {
|
||||
if e == "" {
|
||||
return "unknown"
|
||||
}
|
||||
return e
|
||||
}
|
||||
@@ -0,0 +1,249 @@
|
||||
// Package main — curator dichotomy port (GO verdict: Reactive → Go).
|
||||
// om.go — order-preserving JSON objects. The candidate ledger and the entry
|
||||
// index iterate keys in insertion order (Python dict law), so idx inputs are
|
||||
// decoded into OM instead of map[string]any wherever iteration order can
|
||||
// surface in `want`.
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// OM — an ordered JSON object.
|
||||
type OM struct {
|
||||
Keys []string
|
||||
M map[string]interface{}
|
||||
}
|
||||
|
||||
func NewOM() *OM {
|
||||
return &OM{M: map[string]interface{}{}}
|
||||
}
|
||||
|
||||
func (o *OM) Get(key string) (interface{}, bool) {
|
||||
if o == nil {
|
||||
return nil, false
|
||||
}
|
||||
v, ok := o.M[key]
|
||||
return v, ok
|
||||
}
|
||||
|
||||
func (o *OM) Set(key string, v interface{}) {
|
||||
if _, ok := o.M[key]; !ok {
|
||||
o.Keys = append(o.Keys, key)
|
||||
}
|
||||
o.M[key] = v
|
||||
}
|
||||
|
||||
func (o *OM) Delete(key string) {
|
||||
if o == nil {
|
||||
return
|
||||
}
|
||||
if _, ok := o.M[key]; ok {
|
||||
delete(o.M, key)
|
||||
for i, k := range o.Keys {
|
||||
if k == key {
|
||||
o.Keys = append(o.Keys[:i], o.Keys[i+1:]...)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (o *OM) Len() int {
|
||||
if o == nil {
|
||||
return 0
|
||||
}
|
||||
return len(o.Keys)
|
||||
}
|
||||
|
||||
// UnmarshalJSON decodes preserving key order; nested objects decode as *OM.
|
||||
func (o *OM) UnmarshalJSON(b []byte) error {
|
||||
dec := json.NewDecoder(bytes.NewReader(b))
|
||||
dec.UseNumber()
|
||||
tok, err := dec.Token()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if tok != json.Delim('{') {
|
||||
return fmt.Errorf("OM: expected object, got %v", tok)
|
||||
}
|
||||
o.Keys = nil
|
||||
o.M = map[string]interface{}{}
|
||||
for dec.More() {
|
||||
kt, err := dec.Token()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
key := kt.(string)
|
||||
v, err := decodeOMValue(dec)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
o.Keys = append(o.Keys, key)
|
||||
o.M[key] = v
|
||||
}
|
||||
_, err = dec.Token() // closing }
|
||||
return err
|
||||
}
|
||||
|
||||
func decodeOMValue(dec *json.Decoder) (interface{}, error) {
|
||||
tok, err := dec.Token()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return decodeOMToken(dec, tok)
|
||||
}
|
||||
|
||||
func decodeOMToken(dec *json.Decoder, tok json.Token) (interface{}, error) {
|
||||
switch t := tok.(type) {
|
||||
case json.Delim:
|
||||
switch t {
|
||||
case '{':
|
||||
inner := NewOM()
|
||||
for dec.More() {
|
||||
kt, err := dec.Token()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
v, err := decodeOMValue(dec)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
inner.Keys = append(inner.Keys, kt.(string))
|
||||
inner.M[kt.(string)] = v
|
||||
}
|
||||
if _, err := dec.Token(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return inner, nil
|
||||
case '[':
|
||||
var arr []interface{}
|
||||
for dec.More() {
|
||||
v, err := decodeOMValue(dec)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
arr = append(arr, v)
|
||||
}
|
||||
if _, err := dec.Token(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if arr == nil {
|
||||
arr = []interface{}{}
|
||||
}
|
||||
return arr, nil
|
||||
}
|
||||
return nil, fmt.Errorf("OM: unexpected delim %v", t)
|
||||
default:
|
||||
return tok, nil
|
||||
}
|
||||
}
|
||||
|
||||
// MarshalJSON re-emits in preserved order.
|
||||
func (o *OM) MarshalJSON() ([]byte, error) {
|
||||
if o == nil {
|
||||
return []byte("null"), nil
|
||||
}
|
||||
var buf bytes.Buffer
|
||||
buf.WriteByte('{')
|
||||
for i, k := range o.Keys {
|
||||
if i > 0 {
|
||||
buf.WriteByte(',')
|
||||
}
|
||||
kb, _ := json.Marshal(k)
|
||||
buf.Write(kb)
|
||||
buf.WriteByte(':')
|
||||
vb, err := json.Marshal(o.M[k])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
buf.Write(vb)
|
||||
}
|
||||
buf.WriteByte('}')
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
// -- value coercion helpers (Python .get semantics) --------------------------
|
||||
|
||||
func strOf(v interface{}) string {
|
||||
if s, ok := v.(string); ok {
|
||||
return s
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func strOr(v interface{}, def string) string {
|
||||
if s, ok := v.(string); ok && s != "" {
|
||||
return s
|
||||
}
|
||||
return def
|
||||
}
|
||||
|
||||
func intOf(v interface{}) int {
|
||||
switch n := v.(type) {
|
||||
case json.Number:
|
||||
f, _ := n.Float64()
|
||||
return int(f)
|
||||
case float64:
|
||||
return int(n)
|
||||
case int:
|
||||
return n
|
||||
case int64:
|
||||
return int(n)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func floatOf(v interface{}) float64 {
|
||||
switch n := v.(type) {
|
||||
case json.Number:
|
||||
f, _ := n.Float64()
|
||||
return f
|
||||
case float64:
|
||||
return n
|
||||
case int:
|
||||
return float64(n)
|
||||
case int64:
|
||||
return float64(n)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func intOr(v interface{}, def int) int {
|
||||
if v == nil {
|
||||
return def
|
||||
}
|
||||
switch n := v.(type) {
|
||||
case json.Number:
|
||||
f, _ := n.Float64()
|
||||
return int(f)
|
||||
case float64:
|
||||
return int(n)
|
||||
case int:
|
||||
return n
|
||||
case string:
|
||||
if n == "" {
|
||||
return def
|
||||
}
|
||||
}
|
||||
return intOf(v)
|
||||
}
|
||||
|
||||
// pyListRepr renders a []string the way Python's repr does for the le reasons.
|
||||
func pyListRepr(ss []string) string {
|
||||
quoted := make([]string, len(ss))
|
||||
for i, s := range ss {
|
||||
quoted[i] = "'" + s + "'"
|
||||
}
|
||||
return "[" + strings.Join(quoted, ", ") + "]"
|
||||
}
|
||||
|
||||
func sortedCopy(ss []string) []string {
|
||||
out := append([]string(nil), ss...)
|
||||
sort.Strings(out)
|
||||
return out
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
// phaseb.go — Phase B language-advantage pass: allocation discipline.
|
||||
//
|
||||
// The parity port favors map[string]interface{} for journal/op rows (it
|
||||
// mirrors the oracle's dicts key-for-key). Phase B replaces the hot paths'
|
||||
// heap-churn shapes with pre-sized, append-in-place builders without changing
|
||||
// any observable output (the frozen battery pins every byte):
|
||||
// - NewOp: pre-sized op row with the six canonical keys in oracle order
|
||||
// - AppendJournal: append-in-place journal growth (no intermediate slices)
|
||||
// - JoinLines: single-pass body rebuild for the ADD path
|
||||
package main
|
||||
|
||||
import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// opKeys — the oracle's op-row key order (observable in journal JSON).
|
||||
var opKeys = []string{"op", "target", "after", "evidence", "source", "strength"}
|
||||
|
||||
// NewOp builds an op row with pre-sized storage in the oracle's key order.
|
||||
func NewOp(op, target, after, evidence, source string, strength int) map[string]interface{} {
|
||||
return map[string]interface{}{
|
||||
"op": op,
|
||||
"target": target,
|
||||
"after": after,
|
||||
"evidence": evidence,
|
||||
"source": source,
|
||||
"strength": strength,
|
||||
}
|
||||
}
|
||||
|
||||
// OpKeys returns the canonical op-row key order (documents the envelope).
|
||||
func OpKeys() []string {
|
||||
return append([]string(nil), opKeys...)
|
||||
}
|
||||
|
||||
// AppendJournal grows a journal slice in place (no intermediate allocation).
|
||||
func AppendJournal(jl []map[string]interface{}, rows ...map[string]interface{}) []map[string]interface{} {
|
||||
return append(jl, rows...)
|
||||
}
|
||||
|
||||
// JoinLines rebuilds a block body from kept lines in one pass.
|
||||
func JoinLines(kept []string) string {
|
||||
return strings.Join(kept, "\n")
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
// Phase B tests: the language-advantage builders produce byte-identical
|
||||
// rows to the inline literals they replace (pinned by the frozen battery).
|
||||
package main
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestPhaseBNewOpShape(t *testing.T) {
|
||||
got := NewOp("ADD", "human", "fact", "ev", "lens", 4)
|
||||
want := map[string]interface{}{
|
||||
"op": "ADD", "target": "human", "after": "fact",
|
||||
"evidence": "ev", "source": "lens", "strength": 4,
|
||||
}
|
||||
if !reflect.DeepEqual(normJSON(got), normJSON(want)) {
|
||||
t.Fatalf("NewOp shape drift: %#v", got)
|
||||
}
|
||||
if keys := OpKeys(); !reflect.DeepEqual(keys, []string{"op", "target", "after", "evidence", "source", "strength"}) {
|
||||
t.Fatalf("OpKeys drift: %v", keys)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPhaseBAppendJournal(t *testing.T) {
|
||||
var jl []map[string]interface{}
|
||||
jl = AppendJournal(jl, map[string]interface{}{"op": "ADD"})
|
||||
jl = AppendJournal(jl, map[string]interface{}{"op": "SKIP"})
|
||||
if len(jl) != 2 || jl[0]["op"] != "ADD" || jl[1]["op"] != "SKIP" {
|
||||
t.Fatalf("AppendJournal drift: %#v", jl)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPhaseBJoinLines(t *testing.T) {
|
||||
if got := JoinLines([]string{"- a", "- b"}); got != "- a\n- b" {
|
||||
t.Fatalf("JoinLines drift: %q", got)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,301 @@
|
||||
// Replay — the frozen-truth parity engine. Reads truth.json (generated by
|
||||
// scripts/gen_curator_truth.py against the live Python oracle with paths
|
||||
// isolated, dependency cores pinned, clock frozen, subprocess faked, embed
|
||||
// tabled, aaak/memory_store blocked, drift gate + audit ledger + drift-ledger
|
||||
// seams injected) and re-executes every row through this Go port.
|
||||
//
|
||||
// Seams mirrored here (one-to-one with the generator's declared seams):
|
||||
// - setup.db_missing → StoreDB points at a path in a nonexistent dir
|
||||
// - setup.db_rows → fresh sqlite store seeded with the same rows
|
||||
// - setup.transcripts → transcripts dir rewritten with the same files +
|
||||
// mtimes (age_days before the frozen instant)
|
||||
// - setup.index / index_missing → index file written / absent
|
||||
// - setup.idx_init → the in-memory idx argument
|
||||
// - setup.skills → skills dir seeded with the same files
|
||||
// - input.vecs → the embed seam table for semantic_candidate_key
|
||||
// - input.seam_drift_gate → "reject" installs the REJECT stub; else off
|
||||
// - input.seam_audit_ledger → true installs the recorder
|
||||
// - input.seam_ledger_verdict → the drift-ledger check() verdict
|
||||
// - input.seam_graph_write → "stub_true" installs GraphWrite → true
|
||||
// - input.stdout → GraphRun returns the per-case stdout; the frozen argv's
|
||||
// "@RUNE@" slot is substituted with this checkout's graph_memory.py path
|
||||
//
|
||||
// Rows of one fn replay sequentially on a shared scope (db/transcripts/index
|
||||
// persist across rows of the same fn), mirroring the oracle run.
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
)
|
||||
|
||||
// TruthDoc — the frozen truth file structure.
|
||||
type TruthDoc struct {
|
||||
Oracle string `json:"oracle"`
|
||||
Generator string `json:"generator"`
|
||||
Note string `json:"note"`
|
||||
Rows []TruthRow `json:"rows"`
|
||||
}
|
||||
|
||||
// TruthRow — one frozen case.
|
||||
type TruthRow struct {
|
||||
Fn string `json:"fn"`
|
||||
Name string `json:"name"`
|
||||
Input json.RawMessage `json:"input"`
|
||||
Setup json.RawMessage `json:"setup"`
|
||||
Want json.RawMessage `json:"want"`
|
||||
}
|
||||
|
||||
// RunTruth replays every row and returns a divergence report (empty on
|
||||
// 100% parity).
|
||||
func RunTruth(path string) []string {
|
||||
raw, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return []string{fmt.Sprintf("read truth: %v", err)}
|
||||
}
|
||||
var doc TruthDoc
|
||||
if err := json.Unmarshal(raw, &doc); err != nil {
|
||||
return []string{fmt.Sprintf("parse truth: %v", err)}
|
||||
}
|
||||
repo := repoRoot()
|
||||
fails := []string{}
|
||||
var cur *Curator
|
||||
var curFn string
|
||||
scope := map[string]string{}
|
||||
for i := range doc.Rows {
|
||||
row := &doc.Rows[i]
|
||||
if row.Fn != curFn || cur == nil {
|
||||
cur = newScopeCurator(repo)
|
||||
curFn = row.Fn
|
||||
scope = map[string]string{}
|
||||
}
|
||||
if msg := replayRow(cur, scope, repo, row); msg != "" {
|
||||
fails = append(fails, row.Fn+"/"+row.Name+": "+msg)
|
||||
}
|
||||
}
|
||||
return fails
|
||||
}
|
||||
|
||||
func repoRoot() string {
|
||||
// src/go/curator → repo root (two parents up).
|
||||
exe, err := os.Executable()
|
||||
_ = exe
|
||||
_ = err
|
||||
cwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return "."
|
||||
}
|
||||
return filepath.Dir(filepath.Dir(filepath.Dir(cwd)))
|
||||
}
|
||||
|
||||
func truthRowCount() int {
|
||||
raw, err := os.ReadFile("truth.json")
|
||||
if err != nil {
|
||||
return -1
|
||||
}
|
||||
var doc TruthDoc
|
||||
if err := json.Unmarshal(raw, &doc); err != nil {
|
||||
return -1
|
||||
}
|
||||
return len(doc.Rows)
|
||||
}
|
||||
|
||||
func newScopeCurator(repo string) *Curator {
|
||||
root, err := os.MkdirTemp("", "curator-replay-*")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
c := New(filepath.Join(root, "memory", "store", "memory.db"))
|
||||
c.BlocksDir = filepath.Join(root, "blocks")
|
||||
c.JournalDir = filepath.Join(root, "journal")
|
||||
c.IndexFile = filepath.Join(root, "index", "blocks_meta.json")
|
||||
c.SkillsDir = filepath.Join(root, "config", "skills")
|
||||
c.TranscriptsDir = filepath.Join(root, "transcripts")
|
||||
c.GraphMemoryPath = filepath.Join(repo, "src", "rune", "graph_memory.py")
|
||||
c.Out = discard{}
|
||||
c.Err = discard{}
|
||||
return c
|
||||
}
|
||||
|
||||
type discard struct{}
|
||||
|
||||
func (discard) Write(p []byte) (int, error) { return len(p), nil }
|
||||
|
||||
// applySetup recreates the row's seed state (db/transcripts/index/skills).
|
||||
func applySetup(c *Curator, scope map[string]string, row *TruthRow) error {
|
||||
var setup map[string]interface{}
|
||||
if len(row.Setup) > 0 {
|
||||
if err := json.Unmarshal(row.Setup, &setup); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if v, ok := setup["db_missing"]; ok && v == true {
|
||||
c.StoreDB = filepath.Join(c.JournalDir, "..", "nonexistent-dir", "no.db")
|
||||
c.StoreDB = filepath.Join(filepath.Dir(c.JournalDir), "nonexistent-dir", "no.db")
|
||||
scope["db"] = "missing"
|
||||
} else if v, ok := setup["db_rows"]; ok {
|
||||
dbPath := filepath.Join(filepath.Dir(c.StoreDB), "scope.db")
|
||||
rows, _ := v.([]interface{})
|
||||
if err := SeedDB(dbPath, rows); err != nil {
|
||||
return err
|
||||
}
|
||||
c.StoreDB = dbPath
|
||||
scope["db"] = dbPath
|
||||
}
|
||||
if v, ok := setup["transcripts"]; ok {
|
||||
if err := seedTranscripts(c, v.([]interface{})); err != nil {
|
||||
return err
|
||||
}
|
||||
scope["transcripts"] = "seeded"
|
||||
}
|
||||
if v, ok := setup["index"]; ok {
|
||||
os.MkdirAll(filepath.Dir(c.IndexFile), 0o755)
|
||||
b, _ := json.MarshalIndent(normJSON(v), "", " ")
|
||||
if err := os.WriteFile(c.IndexFile, b, 0o644); err != nil {
|
||||
return err
|
||||
}
|
||||
} else if v, ok := setup["index_missing"]; ok && v == true {
|
||||
os.Remove(c.IndexFile)
|
||||
}
|
||||
if v, ok := setup["skills"]; ok {
|
||||
if err := seedSkills(c, v.(map[string]interface{})); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// input-embedded setup (put_setup rows): same keys, inside input.setup.
|
||||
var input map[string]interface{}
|
||||
if err := json.Unmarshal(row.Input, &input); err != nil {
|
||||
return err
|
||||
}
|
||||
if v, ok := input["setup"]; ok {
|
||||
sub, _ := v.(map[string]interface{})
|
||||
if dv, ok := sub["db_missing"]; ok && dv == true {
|
||||
c.StoreDB = filepath.Join(filepath.Dir(c.JournalDir), "nonexistent-dir", "no.db")
|
||||
scope["db"] = "missing"
|
||||
} else if dv, ok := sub["db_rows"]; ok {
|
||||
dbPath := filepath.Join(filepath.Dir(c.StoreDB), "scope.db")
|
||||
rows, _ := dv.([]interface{})
|
||||
if err := SeedDB(dbPath, rows); err != nil {
|
||||
return err
|
||||
}
|
||||
c.StoreDB = dbPath
|
||||
scope["db"] = dbPath
|
||||
}
|
||||
if tv, ok := sub["transcripts"]; ok {
|
||||
if err := seedTranscripts(c, tv.([]interface{})); err != nil {
|
||||
return err
|
||||
}
|
||||
scope["transcripts"] = "seeded"
|
||||
}
|
||||
if iv, ok := sub["index"]; ok {
|
||||
os.MkdirAll(filepath.Dir(c.IndexFile), 0o755)
|
||||
b, _ := json.MarshalIndent(normJSON(iv), "", " ")
|
||||
if err := os.WriteFile(c.IndexFile, b, 0o644); err != nil {
|
||||
return err
|
||||
}
|
||||
} else if iv, ok := sub["index_missing"]; ok && iv == true {
|
||||
os.Remove(c.IndexFile)
|
||||
}
|
||||
if sv, ok := sub["skills"]; ok {
|
||||
if err := seedSkills(c, sv.(map[string]interface{})); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func seedTranscripts(c *Curator, list []interface{}) error {
|
||||
os.MkdirAll(c.TranscriptsDir, 0o755)
|
||||
ents, err := os.ReadDir(c.TranscriptsDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, e := range ents {
|
||||
os.Remove(filepath.Join(c.TranscriptsDir, e.Name()))
|
||||
}
|
||||
frozen, _ := time.Parse("2006-01-02T15:04:05Z07:00", FrozenISO)
|
||||
for _, v := range list {
|
||||
t := v.(map[string]interface{})
|
||||
p := filepath.Join(c.TranscriptsDir, strOr(t["name"], ""))
|
||||
if err := os.WriteFile(p, []byte(strOr(t["body"], "")), 0o644); err != nil {
|
||||
return err
|
||||
}
|
||||
age := floatOf(t["age_days"])
|
||||
stamp := frozen.Add(-time.Duration(age*86400) * time.Second)
|
||||
if err := os.Chtimes(p, stamp, stamp); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func seedSkills(c *Curator, m map[string]interface{}) error {
|
||||
os.MkdirAll(c.SkillsDir, 0o755)
|
||||
ents, err := os.ReadDir(c.SkillsDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, e := range ents {
|
||||
os.RemoveAll(filepath.Join(c.SkillsDir, e.Name()))
|
||||
}
|
||||
for rel, v := range m {
|
||||
p := filepath.Join(c.SkillsDir, rel)
|
||||
os.MkdirAll(filepath.Dir(p), 0o755)
|
||||
if err := os.WriteFile(p, []byte(strOf(v)), 0o644); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// pinRowSeams installs the row's injected seams (drift gate / audit ledger /
|
||||
// ledger verdict / graph stub / embed table).
|
||||
func pinRowSeams(c *Curator, row *TruthRow, audit *[]AuditCall) {
|
||||
var input map[string]interface{}
|
||||
_ = json.Unmarshal(row.Input, &input)
|
||||
if v, ok := input["seam_drift_gate"]; ok && v == "reject" {
|
||||
c.DriftGate = func(entry map[string]interface{}) GateResult {
|
||||
return GateResult{Verdict: "REJECT", Reason: "washed out"}
|
||||
}
|
||||
} else {
|
||||
c.DriftGate = nil
|
||||
}
|
||||
if v, ok := input["seam_audit_ledger"]; ok && v == true {
|
||||
c.AuditLedger = func(id int, kind, action string, count int, detail string) {
|
||||
*audit = append(*audit, AuditCall{id, kind, action, count, detail})
|
||||
}
|
||||
} else {
|
||||
c.AuditLedger = nil
|
||||
}
|
||||
if v, ok := input["seam_ledger_verdict"]; ok {
|
||||
c.LedgerVerdict = intOf(v)
|
||||
} else {
|
||||
c.LedgerVerdict = 0
|
||||
}
|
||||
if v, ok := input["seam_graph_write"]; ok && v == "stub_true" {
|
||||
c.GraphRun = func(argv []string) string { return `"added": True` }
|
||||
} else {
|
||||
c.GraphRun = nil
|
||||
}
|
||||
}
|
||||
|
||||
// AuditCall — one recorded log_promotion call.
|
||||
type AuditCall struct {
|
||||
ID int
|
||||
Kind string
|
||||
Action string
|
||||
Count int
|
||||
Detail string
|
||||
}
|
||||
|
||||
func auditCallsJSON(calls []AuditCall) interface{} {
|
||||
out := []interface{}{}
|
||||
for _, a := range calls {
|
||||
out = append(out, []interface{}{a.ID, a.Kind, a.Action, float64(a.Count), a.Detail})
|
||||
}
|
||||
return out
|
||||
}
|
||||
@@ -0,0 +1,643 @@
|
||||
// replay_cases.go — per-function replay dispatch. Each case mirrors exactly
|
||||
// one row of the frozen truth: it reads the row input, applies the row setup,
|
||||
// runs the ported function, and returns the got value for comparison against
|
||||
// the frozen want. Any divergence fails the battery.
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// normJSON canonicalizes a value through JSON (numbers → float64, OM →
|
||||
// ordered maps) so DeepEqual compares oracle-shaped values.
|
||||
func normJSON(v interface{}) interface{} {
|
||||
switch t := v.(type) {
|
||||
case []string:
|
||||
out := make([]interface{}, len(t))
|
||||
for i, s := range t {
|
||||
out[i] = s
|
||||
}
|
||||
return out
|
||||
case []float64:
|
||||
out := make([]interface{}, len(t))
|
||||
for i, f := range t {
|
||||
out[i] = f
|
||||
}
|
||||
return out
|
||||
case []map[string]interface{}:
|
||||
out := make([]interface{}, len(t))
|
||||
for i, m := range t {
|
||||
out[i] = normJSON(m)
|
||||
}
|
||||
return out
|
||||
case *OM:
|
||||
m := map[string]interface{}{}
|
||||
for _, k := range t.Keys {
|
||||
m[k] = normJSON(t.M[k])
|
||||
}
|
||||
return m
|
||||
case map[string]interface{}:
|
||||
m := map[string]interface{}{}
|
||||
for k, vv := range t {
|
||||
m[k] = normJSON(vv)
|
||||
}
|
||||
return m
|
||||
case []interface{}:
|
||||
out := make([]interface{}, len(t))
|
||||
for i, vv := range t {
|
||||
out[i] = normJSON(vv)
|
||||
}
|
||||
return out
|
||||
case json.Number:
|
||||
f, _ := t.Float64()
|
||||
return f
|
||||
case int:
|
||||
return float64(t)
|
||||
case int64:
|
||||
return float64(t)
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
func wantOf(row *TruthRow) interface{} {
|
||||
dec := json.NewDecoder(bytes.NewReader(row.Want))
|
||||
dec.UseNumber()
|
||||
var w interface{}
|
||||
_ = dec.Decode(&w)
|
||||
return normJSON(w)
|
||||
}
|
||||
|
||||
func inputOf(row *TruthRow) map[string]interface{} {
|
||||
dec := json.NewDecoder(bytes.NewReader(row.Input))
|
||||
dec.UseNumber()
|
||||
var in map[string]interface{}
|
||||
_ = dec.Decode(&in)
|
||||
return in
|
||||
}
|
||||
|
||||
func replayRow(c *Curator, scope map[string]string, repo string, row *TruthRow) string {
|
||||
if err := applySetup(c, scope, row); err != nil {
|
||||
return fmt.Sprintf("setup: %v", err)
|
||||
}
|
||||
var audit []AuditCall
|
||||
pinRowSeams(c, row, &audit)
|
||||
in := inputOf(row)
|
||||
got, msg := dispatch(c, scope, repo, row, in)
|
||||
if msg != "" {
|
||||
return msg
|
||||
}
|
||||
// audit ledger calls are part of promote_candidates rows' want
|
||||
if row.Fn == "promote_candidates" && len(audit) > 0 {
|
||||
g, ok := got.(map[string]interface{})
|
||||
if !ok {
|
||||
return "audit: got not a map"
|
||||
}
|
||||
g["audit_calls"] = auditCallsJSON(audit)
|
||||
got = g
|
||||
}
|
||||
if !reflect.DeepEqual(normJSON(got), wantOf(row)) {
|
||||
gb, _ := json.Marshal(normJSON(got))
|
||||
wb, _ := json.Marshal(wantOf(row))
|
||||
return fmt.Sprintf("divergence\ngot: %s\nwant: %s", gb, wb)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// dispatch runs one frozen row. Returns (got, error-message).
|
||||
func dispatch(c *Curator, scope map[string]string, repo string, row *TruthRow, in map[string]interface{}) (interface{}, string) {
|
||||
// journal_append rows share one journal scope (the oracle never resets
|
||||
// between them).
|
||||
if row.Fn == "journal_append" && scope["journal"] == "" {
|
||||
ents, _ := os.ReadDir(c.JournalDir)
|
||||
for _, e := range ents {
|
||||
os.Remove(filepath.Join(c.JournalDir, e.Name()))
|
||||
}
|
||||
scope["journal"] = "open"
|
||||
}
|
||||
// main rows each run main() fresh on the shared scope; the oracle's
|
||||
// main/apply_output journal accumulates hook/dry/plan rows' NOTHING
|
||||
// (those never write the journal) — no reset needed.
|
||||
switch row.Fn {
|
||||
case "block_path":
|
||||
return c.BlockPath(strOr(in["label"], "")), ""
|
||||
case "entry_key":
|
||||
return EntryKey(strOr(in["text"], "")), ""
|
||||
case "find_entry":
|
||||
v := FindEntry(strOr(in["body"], ""), strOr(in["needle"], ""))
|
||||
if v == "" {
|
||||
return nil, ""
|
||||
}
|
||||
return v, ""
|
||||
case "candidate_key":
|
||||
return CandidateKey(strOr(in["text"], "")), ""
|
||||
case "norm":
|
||||
return Norm(strOr(in["text"], "")), ""
|
||||
case "detect_conflict":
|
||||
k, l := DetectConflict(strOr(in["body"], ""), strOr(in["fact"], ""))
|
||||
if k == "" {
|
||||
return []interface{}{nil, nil}, ""
|
||||
}
|
||||
return []interface{}{k, l}, ""
|
||||
case "overlap":
|
||||
return Overlap(strOr(in["a"], ""), strOr(in["b"], "")), ""
|
||||
case "cos":
|
||||
return Cos(floats(in["a"]), floats(in["b"])), ""
|
||||
case "sanitize_slug":
|
||||
return SanitizeSlug(strOr(in["name"], "")), ""
|
||||
case "skill_path":
|
||||
rel, err := filepath.Rel(c.SkillsDir, c.SkillPath(strOr(in["name"], "")))
|
||||
if err != nil {
|
||||
return "", err.Error()
|
||||
}
|
||||
return rel, ""
|
||||
case "is_topic_fact":
|
||||
return c.IsTopicFact(strOr(in["fact"], "")), ""
|
||||
case "aaak":
|
||||
return c.Aaak(strOr(in["text"], ""), strOr(in["topic"], "")), ""
|
||||
case "le_promote_eligible":
|
||||
if v, ok := in["seam_lexicon"]; ok && v == "unavailable" {
|
||||
return lePromoteEligibleFallback(strOr(in["proposal"], ""), nil,
|
||||
intOf(in["strength"]), intOf(in["min_strength"]), intOf(in["wash_count"])), ""
|
||||
}
|
||||
existing := stringsOf(in["existing"])
|
||||
return c.LePromoteEligible(strOr(in["proposal"], ""), existing,
|
||||
intOf(in["strength"]), intOf(in["min_strength"]), intOf(in["wash_count"])), ""
|
||||
case "store_has":
|
||||
topic := ""
|
||||
if in["topic"] != nil {
|
||||
topic = strOr(in["topic"], "")
|
||||
}
|
||||
return c.StoreHas(strOr(in["text"], ""), topic), ""
|
||||
case "read_block":
|
||||
fm, body := c.ReadBlock(strOr(in["label"], ""))
|
||||
return map[string]interface{}{"fm": fm, "body": body}, ""
|
||||
case "write_block":
|
||||
ret := c.WriteBlock(strOr(in["label"], ""), strOf(in["body"]))
|
||||
out := map[string]interface{}{"return": ret}
|
||||
if row.Name != "db_unavailable_returns_descriptor" {
|
||||
_, body := c.ReadBlock(strOr(in["label"], ""))
|
||||
out["body"] = body
|
||||
}
|
||||
return out, ""
|
||||
case "block_values":
|
||||
out := c.BlockValues(strOr(in["label"], ""))
|
||||
if out == nil {
|
||||
out = []string{}
|
||||
}
|
||||
return out, ""
|
||||
case "store_corpus":
|
||||
out := c.StoreCorpus(intOf(in["limit"]))
|
||||
if out == nil {
|
||||
out = []string{}
|
||||
}
|
||||
return out, ""
|
||||
case "contradicts_identity":
|
||||
return c.ContradictsIdentity(strOr(in["fact"], "")), ""
|
||||
case "load_index":
|
||||
return c.LoadIndex(), ""
|
||||
case "save_index":
|
||||
c.SaveIndex(mustOM(in["idx"]))
|
||||
return c.LoadIndex(), ""
|
||||
case "ensure_entry_meta":
|
||||
idx := idxInitOf(row)
|
||||
key := c.EnsureEntryMeta(idx, strOr(in["label"], ""), strOr(in["text"], ""))
|
||||
lomV, _ := idx.Get(strOr(in["label"], ""))
|
||||
lom, _ := omEntry(lomV)
|
||||
// the oracle's want carries the label dict as a plain map
|
||||
return map[string]interface{}{"key": key, "label_idx": lom}, ""
|
||||
case "touch_entry":
|
||||
idx := idxInitOf(row)
|
||||
label := strOr(in["label"], "")
|
||||
text := strOr(in["text"], "")
|
||||
key := c.TouchEntry(idx, label, text)
|
||||
lomV, _ := idx.Get(label)
|
||||
lom, _ := lomV.(*OM)
|
||||
metaV, _ := lom.Get(key)
|
||||
meta, _ := omEntry(metaV)
|
||||
return map[string]interface{}{"key": key, "meta": meta}, ""
|
||||
case "journal_append":
|
||||
entry, _ := in["entry"].(map[string]interface{})
|
||||
// rows of this fn replay sequentially on a shared journal scope,
|
||||
// mirroring the oracle run (minimal sees full's entry).
|
||||
p := c.JournalAppend(entry)
|
||||
content := map[string]interface{}{}
|
||||
ents, _ := os.ReadDir(c.JournalDir)
|
||||
for _, e := range ents {
|
||||
b, _ := os.ReadFile(filepath.Join(c.JournalDir, e.Name()))
|
||||
content[e.Name()] = string(b)
|
||||
}
|
||||
return map[string]interface{}{"path": filepath.Base(p), "content": content}, ""
|
||||
case "ensure_candidates":
|
||||
idx := NewOM()
|
||||
if v, ok := in["idx"].(*OM); ok {
|
||||
idx = v
|
||||
} else if v, ok := in["idx"].(map[string]interface{}); ok {
|
||||
idx = mapToOM(v)
|
||||
}
|
||||
return EnsureCandidates(idx), ""
|
||||
case "semantic_candidate_key":
|
||||
vecs := tableVecs()
|
||||
embed := func(texts []string) map[string][]float64 {
|
||||
out := map[string][]float64{}
|
||||
for _, t := range texts {
|
||||
if v, ok := vecs[t]; ok {
|
||||
out[t] = v
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
cands := NewOM()
|
||||
cands.Set(CandidateKey("fact alpha about deploy runners"),
|
||||
map[string]interface{}{"text": "fact alpha about deploy runners"})
|
||||
cands.Set(CandidateKey("fact delta about whiskey tasting"),
|
||||
map[string]interface{}{"text": "fact delta about whiskey tasting"})
|
||||
if row.Name == "empty_cands" {
|
||||
cands = NewOM()
|
||||
}
|
||||
v := c.SemanticCandidateKey(strOr(in["fact"], ""), cands, embed)
|
||||
if v == "" {
|
||||
return nil, ""
|
||||
}
|
||||
return v, ""
|
||||
case "embed":
|
||||
for k, v := range tableVecs() {
|
||||
EmbedCache[k] = v
|
||||
}
|
||||
out := c.Embed(stringsOf(in["texts"]))
|
||||
m := map[string]interface{}{}
|
||||
for k, v := range out {
|
||||
m[k] = v
|
||||
}
|
||||
return m, ""
|
||||
case "grounded_in_sessions":
|
||||
return c.GroundedInSessions(strOr(in["fact"], ""), 0.46, 3), ""
|
||||
case "coherence_check":
|
||||
if v, ok := in["fact"]; ok && (v == "" || len(strOf(v)) < 15) && in["sources"] == nil {
|
||||
return c.CoherenceCheck(strOf(v), []string{"sessions", "corpus"}), ""
|
||||
}
|
||||
return c.CoherenceCheck(strOr(in["fact"], ""), stringsOf(in["sources"])), ""
|
||||
case "grounded_in_corpus":
|
||||
return c.GroundedInCorpus(strOr(in["fact"], "")), ""
|
||||
case "decay_candidates":
|
||||
idx := idxInitOf(row)
|
||||
if idx.Len() == 0 {
|
||||
if raw := rawField(row, "idx"); len(raw) > 0 {
|
||||
idx = mustOMRaw(raw)
|
||||
}
|
||||
}
|
||||
var jl []map[string]interface{}
|
||||
changed := c.DecayCandidates(idx, &jl)
|
||||
if jl == nil {
|
||||
jl = []map[string]interface{}{}
|
||||
}
|
||||
cands, _ := idx.Get(CandidatesKey)
|
||||
return map[string]interface{}{"changed": changed, "remaining": cands, "journal": jl}, ""
|
||||
case "promote_candidates":
|
||||
idx := idxInitOf(row)
|
||||
ev := evidenceOf(in["evidence"])
|
||||
var jl []map[string]interface{}
|
||||
promoted := c.PromoteCandidates(ev, idx, &jl)
|
||||
if promoted == nil {
|
||||
promoted = []map[string]interface{}{}
|
||||
}
|
||||
if jl == nil {
|
||||
jl = []map[string]interface{}{}
|
||||
}
|
||||
cands, _ := idx.Get(CandidatesKey)
|
||||
if cands == nil {
|
||||
cands = NewOM()
|
||||
}
|
||||
return map[string]interface{}{
|
||||
"promoted": promoted, "candidates": cands, "journal": jl,
|
||||
}, ""
|
||||
case "plan_ops":
|
||||
idx := idxInitOf(row)
|
||||
ev := evidenceOf(in["evidence"])
|
||||
var jl []map[string]interface{}
|
||||
ops := c.PlanOps(ev, idx, &jl)
|
||||
if ops == nil {
|
||||
ops = []map[string]interface{}{}
|
||||
}
|
||||
if jl == nil {
|
||||
jl = []map[string]interface{}{}
|
||||
}
|
||||
return map[string]interface{}{"ops": ops, "idx": idx, "journal": jl}, ""
|
||||
case "apply_op":
|
||||
idx := idxInitOf(row)
|
||||
ops := evidenceOf(in["ops"])
|
||||
var jl []map[string]interface{}
|
||||
var results []interface{}
|
||||
for _, op := range ops {
|
||||
results = append(results, c.ApplyOp(op, idx, &jl))
|
||||
}
|
||||
if jl == nil {
|
||||
jl = []map[string]interface{}{}
|
||||
}
|
||||
_, body := c.ReadBlock("human")
|
||||
return map[string]interface{}{
|
||||
"results": results, "journal": jl, "body": body, "idx": idx,
|
||||
}, ""
|
||||
case "apply_warm":
|
||||
var jl []map[string]interface{}
|
||||
if row.Name == "post_upsert_neurons" || row.Name == "post_seed_neurons" {
|
||||
return neuronsJSON(ListNeurons(c.StoreDB)), ""
|
||||
}
|
||||
if op, ok := in["op"].(map[string]interface{}); ok {
|
||||
ret := c.ApplyWarm(op, &jl)
|
||||
if jl == nil {
|
||||
jl = []map[string]interface{}{}
|
||||
}
|
||||
if _, hasReturned := rawWantField(row, "returned"); hasReturned {
|
||||
return map[string]interface{}{"returned": ret, "journal": jl}, ""
|
||||
}
|
||||
return ret, ""
|
||||
}
|
||||
// seed_new_neuron/empty_fact rows: want is {returned, journal}
|
||||
fact := strOr(in["after"], strOr(in["fact"], ""))
|
||||
ret := c.ApplyWarm(map[string]interface{}{"after": fact}, &jl)
|
||||
if jl == nil {
|
||||
jl = []map[string]interface{}{}
|
||||
}
|
||||
return map[string]interface{}{"returned": ret, "journal": jl}, ""
|
||||
case "derive_triggers":
|
||||
return DeriveTriggers(strOr(in["text"], ""), stringsOf(in["existing"])), ""
|
||||
case "decay_pass":
|
||||
idx := idxInitOf(row)
|
||||
if idx.Len() == 0 {
|
||||
if raw := rawField(row, "idx"); len(raw) > 0 {
|
||||
idx = mustOMRaw(raw)
|
||||
}
|
||||
}
|
||||
// the oracle's want carries idx_after as the MUTATED idx (same
|
||||
// object); replay returns the same mutated object.
|
||||
var jl []map[string]interface{}
|
||||
c.DecayPass(idx, &jl)
|
||||
if jl == nil {
|
||||
jl = []map[string]interface{}{}
|
||||
}
|
||||
return map[string]interface{}{"idx_after": idx, "journal": jl}, ""
|
||||
case "compress_pass":
|
||||
idx := idxInitOf(row)
|
||||
var jl []map[string]interface{}
|
||||
changed := c.CompressPass(idx, &jl)
|
||||
if jl == nil {
|
||||
jl = []map[string]interface{}{}
|
||||
}
|
||||
if row.Name == "under_limit_noop" {
|
||||
return map[string]interface{}{"changed": changed, "journal": jl}, ""
|
||||
}
|
||||
_, body := c.ReadBlock("operating")
|
||||
return map[string]interface{}{
|
||||
"changed": changed, "body": body, "journal": jl, "idx": idx,
|
||||
}, ""
|
||||
case "author_skill":
|
||||
op, _ := in["op"].(map[string]interface{})
|
||||
var jl []map[string]interface{}
|
||||
if row.Name == "no_name" || row.Name == "no_steps" {
|
||||
return c.AuthorSkill(op, &jl), ""
|
||||
}
|
||||
written := c.AuthorSkill(op, &jl)
|
||||
if jl == nil {
|
||||
jl = []map[string]interface{}{}
|
||||
}
|
||||
out := map[string]interface{}{"written": written}
|
||||
if row.Name == "new_skill" {
|
||||
slug := SanitizeSlug(strOr(op["name"], ""))
|
||||
b, _ := os.ReadFile(c.SkillPath(slug))
|
||||
out["rel"] = slug + "/SKILL.md"
|
||||
out["content"] = string(b)
|
||||
out["journal"] = jl
|
||||
} else if row.Name == "existing_adds_evidence" || row.Name == "existing_keeps_evidence" {
|
||||
slug := SanitizeSlug(strOr(op["name"], ""))
|
||||
b, _ := os.ReadFile(c.SkillPath(slug))
|
||||
out["content"] = string(b)
|
||||
out["journal"] = jl
|
||||
}
|
||||
return out, ""
|
||||
case "graph_write":
|
||||
stdout := strOr(in["stdout"], `"added": True`)
|
||||
var captured []string
|
||||
c.GraphRun = func(argv []string) string {
|
||||
captured = append([]string(nil), argv...)
|
||||
return stdout
|
||||
}
|
||||
added := c.GraphWrite(strOr(in["fact"], ""), "ev", "src", intOf(in["strength"]))
|
||||
for i, a := range captured {
|
||||
if strings.HasPrefix(a, repo+"/src/rune/graph_memory.py") || a == c.GraphMemoryPath {
|
||||
captured[i] = "@RUNE@/src/rune/graph_memory.py"
|
||||
}
|
||||
}
|
||||
return map[string]interface{}{"added": added, "argv": captured}, ""
|
||||
case "run_plan":
|
||||
ev := evidenceOf(in["evidence"])
|
||||
apply := row.Name != "dry_run"
|
||||
var buf bytes.Buffer
|
||||
c.Out = &buf
|
||||
jl, ops := c.RunPlan(ev, apply)
|
||||
if jl == nil {
|
||||
jl = []map[string]interface{}{}
|
||||
}
|
||||
if ops == nil {
|
||||
ops = []map[string]interface{}{}
|
||||
}
|
||||
out := map[string]interface{}{"journal": jl, "ops": ops}
|
||||
if row.Name == "apply_gate_pass" {
|
||||
out["index"] = c.LoadIndex()
|
||||
} else if row.Name == "apply_gate_refuses" {
|
||||
out["stdout"] = buf.String()
|
||||
}
|
||||
return out, ""
|
||||
case "main":
|
||||
return dispatchMain(c, row, in), ""
|
||||
case "handle_hook":
|
||||
var data map[string]interface{}
|
||||
if v, ok := in["data"].(map[string]interface{}); ok {
|
||||
data = v
|
||||
}
|
||||
return c.HandleHook(strOr(in["event"], ""), data), ""
|
||||
}
|
||||
return nil, fmt.Sprintf("unknown fn %q", row.Fn)
|
||||
}
|
||||
|
||||
func floats(v interface{}) []float64 {
|
||||
var out []float64
|
||||
if arr, ok := v.([]interface{}); ok {
|
||||
for _, x := range arr {
|
||||
out = append(out, floatOf(x))
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func stringsOf(v interface{}) []string {
|
||||
var out []string
|
||||
if arr, ok := v.([]interface{}); ok {
|
||||
for _, x := range arr {
|
||||
out = append(out, strOf(x))
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func evidenceOf(v interface{}) []map[string]interface{} {
|
||||
var out []map[string]interface{}
|
||||
if arr, ok := v.([]interface{}); ok {
|
||||
for _, x := range arr {
|
||||
if m, ok := x.(map[string]interface{}); ok {
|
||||
out = append(out, m)
|
||||
}
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// mustOM decodes an idx_init value (nil → empty OM). Plain maps arrive
|
||||
// from inputOf's Unmarshal (order lost) — callers that need insertion order
|
||||
// must decode from the raw row input instead (mustOMRaw).
|
||||
func mustOM(v interface{}) *OM {
|
||||
if v == nil {
|
||||
return NewOM()
|
||||
}
|
||||
if om, ok := v.(*OM); ok {
|
||||
return om
|
||||
}
|
||||
if m, ok := v.(map[string]interface{}); ok {
|
||||
return mapToOM(m)
|
||||
}
|
||||
return NewOM()
|
||||
}
|
||||
|
||||
// mustOMRaw decodes an idx value from raw JSON, preserving key order.
|
||||
func mustOMRaw(raw json.RawMessage) *OM {
|
||||
om := NewOM()
|
||||
if len(raw) == 0 || string(raw) == "null" {
|
||||
return om
|
||||
}
|
||||
if err := om.UnmarshalJSON(raw); err != nil {
|
||||
return NewOM()
|
||||
}
|
||||
return om
|
||||
}
|
||||
|
||||
// idxInitOf extracts the raw idx_init JSON for a row (order-preserving).
|
||||
// Sources, in order: input.idx_init, input.idx, setup.idx_init — mirroring
|
||||
// the generator (plan/apply/compress rows carry idx_init in top-level setup).
|
||||
func idxInitOf(row *TruthRow) *OM {
|
||||
var in struct {
|
||||
IdxInit json.RawMessage `json:"idx_init"`
|
||||
Idx json.RawMessage `json:"idx"`
|
||||
}
|
||||
_ = json.Unmarshal(row.Input, &in)
|
||||
if len(in.IdxInit) > 0 && string(in.IdxInit) != "null" {
|
||||
return mustOMRaw(in.IdxInit)
|
||||
}
|
||||
if len(in.Idx) > 0 && string(in.Idx) != "null" {
|
||||
return mustOMRaw(in.Idx)
|
||||
}
|
||||
var setup struct {
|
||||
IdxInit json.RawMessage `json:"idx_init"`
|
||||
}
|
||||
if len(row.Setup) > 0 {
|
||||
_ = json.Unmarshal(row.Setup, &setup)
|
||||
if len(setup.IdxInit) > 0 && string(setup.IdxInit) != "null" {
|
||||
return mustOMRaw(setup.IdxInit)
|
||||
}
|
||||
}
|
||||
return NewOM()
|
||||
}
|
||||
|
||||
// mapToOM converts a decoded map into an ordered OM.
|
||||
func mapToOM(m map[string]interface{}) *OM {
|
||||
om := NewOM()
|
||||
for k, v := range m {
|
||||
switch t := v.(type) {
|
||||
case map[string]interface{}:
|
||||
om.Set(k, mapToOM(t))
|
||||
case []interface{}:
|
||||
om.Set(k, normList(t))
|
||||
default:
|
||||
om.Set(k, v)
|
||||
}
|
||||
}
|
||||
return om
|
||||
}
|
||||
|
||||
func normList(l []interface{}) []interface{} {
|
||||
out := make([]interface{}, len(l))
|
||||
for i, v := range l {
|
||||
if m, ok := v.(map[string]interface{}); ok {
|
||||
out[i] = mapToOM(m)
|
||||
} else {
|
||||
out[i] = v
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func neuronsJSON(ns []Neuron) interface{} {
|
||||
if ns == nil {
|
||||
return []interface{}{}
|
||||
}
|
||||
b, _ := json.Marshal(ns)
|
||||
var out interface{}
|
||||
_ = json.Unmarshal(b, &out)
|
||||
return out
|
||||
}
|
||||
|
||||
// tableVecs — the frozen VEC table (mirrors the generator's VEC).
|
||||
func tableVecs() map[string][]float64 {
|
||||
return map[string][]float64{
|
||||
"fact alpha about deploy runners": {1.0, 0.0, 0.0},
|
||||
"fact beta about gitea runners": {0.0, 1.0, 0.0},
|
||||
"fact gamma about deploy runners nightly": {0.9, 0.1, 0.0},
|
||||
"fact delta about whiskey tasting": {0.0, 0.0, 1.0},
|
||||
}
|
||||
}
|
||||
|
||||
// mustMap decodes an idx value that may be *OM or a plain map.
|
||||
func mustMap(v interface{}) map[string]interface{} {
|
||||
if om, ok := v.(*OM); ok {
|
||||
m := map[string]interface{}{}
|
||||
for _, k := range om.Keys {
|
||||
m[k] = om.M[k]
|
||||
}
|
||||
return m
|
||||
}
|
||||
if m, ok := v.(map[string]interface{}); ok {
|
||||
return m
|
||||
}
|
||||
return map[string]interface{}{}
|
||||
}
|
||||
|
||||
// rawField extracts a raw JSON field from a row's input (order-preserving).
|
||||
func rawField(row *TruthRow, field string) json.RawMessage {
|
||||
var in map[string]json.RawMessage
|
||||
_ = json.Unmarshal(row.Input, &in)
|
||||
return in[field]
|
||||
}
|
||||
|
||||
func jsonStr(v interface{}) string {
|
||||
b, _ := json.Marshal(strOf(v))
|
||||
return string(b)
|
||||
}
|
||||
|
||||
// dbSig fingerprints a seed-row list so sequential rows on one scope keep
|
||||
// their accumulated store state instead of re-seeding every row.
|
||||
func dbSig(rows []interface{}) string {
|
||||
b, _ := json.Marshal(rows)
|
||||
return string(b)
|
||||
}
|
||||
|
||||
// rawWantField extracts a raw JSON field from a row's want.
|
||||
func rawWantField(row *TruthRow, field string) (json.RawMessage, bool) {
|
||||
var w map[string]json.RawMessage
|
||||
_ = json.Unmarshal(row.Want, &w)
|
||||
v, ok := w[field]
|
||||
return v, ok
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
// store.go — sqlite seam for the curator port (modernc.org/sqlite, pure Go).
|
||||
// Mirrors the oracle's sqlite3 usage: read paths use `file:<path>?mode=ro`
|
||||
// (missing file/dir → connect error → the documented fallback), write paths
|
||||
// open read-write (missing dir → error → fallback).
|
||||
package main
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
_ "modernc.org/sqlite"
|
||||
)
|
||||
|
||||
const entriesSchema = `
|
||||
CREATE TABLE IF NOT EXISTS entries (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
text TEXT NOT NULL,
|
||||
importance REAL DEFAULT 0.5,
|
||||
created_at TEXT NOT NULL,
|
||||
last_accessed TEXT,
|
||||
topic TEXT DEFAULT '',
|
||||
entities TEXT DEFAULT '[]',
|
||||
source TEXT DEFAULT '',
|
||||
method TEXT DEFAULT 'curator',
|
||||
status TEXT DEFAULT 'active',
|
||||
valid_from TEXT,
|
||||
valid_until TEXT,
|
||||
confidence REAL DEFAULT 0.7,
|
||||
temperature REAL DEFAULT 1.0,
|
||||
always_on INTEGER DEFAULT 0,
|
||||
priority INTEGER DEFAULT 5,
|
||||
triggers TEXT DEFAULT '',
|
||||
kind TEXT DEFAULT 'fact',
|
||||
slug TEXT DEFAULT '',
|
||||
summary TEXT DEFAULT '',
|
||||
wash_count INTEGER DEFAULT 0,
|
||||
last_washed TEXT,
|
||||
promotion_eligible INTEGER DEFAULT 0,
|
||||
rock_status TEXT DEFAULT 'washable',
|
||||
tier TEXT DEFAULT 'warm',
|
||||
memory_type TEXT
|
||||
);
|
||||
`
|
||||
|
||||
func openRO(path string) (*sql.DB, error) {
|
||||
db, err := sql.Open("sqlite", "file:"+path+"?mode=ro")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err = db.Ping(); err != nil {
|
||||
db.Close()
|
||||
return nil, err
|
||||
}
|
||||
return db, nil
|
||||
}
|
||||
|
||||
func openRW(path string) (*sql.DB, error) {
|
||||
db, err := sql.Open("sqlite", "file:"+path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err = db.Ping(); err != nil {
|
||||
db.Close()
|
||||
return nil, err
|
||||
}
|
||||
return db, nil
|
||||
}
|
||||
|
||||
// SeedDB creates a fresh store with the full entries schema and inserts the
|
||||
// setup rows (column names mirror the oracle schema).
|
||||
func SeedDB(path string, rows []interface{}) error {
|
||||
if err := os.MkdirAll(dirOf(path), 0o755); err != nil {
|
||||
return err
|
||||
}
|
||||
os.Remove(path)
|
||||
db, err := openRW(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer db.Close()
|
||||
if _, err = db.Exec(entriesSchema); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, r := range rows {
|
||||
m, ok := r.(map[string]interface{})
|
||||
if !ok {
|
||||
return fmt.Errorf("seed row not an object")
|
||||
}
|
||||
cols := make([]string, 0, len(m))
|
||||
vals := make([]interface{}, 0, len(m))
|
||||
base := map[string]interface{}{
|
||||
"text": "", "importance": 0.5, "created_at": "2026-01-01T00:00:00+00:00",
|
||||
"last_accessed": "2026-01-01T00:00:00+00:00", "topic": "", "entities": "[]",
|
||||
"source": "seed", "method": "seed", "status": "active", "valid_from": nil,
|
||||
"confidence": 0.7, "temperature": 1.0, "always_on": 0, "priority": 5,
|
||||
"triggers": "", "kind": "fact", "slug": "", "summary": "", "wash_count": 0,
|
||||
}
|
||||
for k, v := range base {
|
||||
if _, ok := m[k]; !ok {
|
||||
cols = append(cols, k)
|
||||
vals = append(vals, v)
|
||||
}
|
||||
}
|
||||
for k, v := range m {
|
||||
cols = append(cols, k)
|
||||
vals = append(vals, v)
|
||||
}
|
||||
q := "INSERT INTO entries ("
|
||||
for i, c := range cols {
|
||||
if i > 0 {
|
||||
q += ", "
|
||||
}
|
||||
q += c
|
||||
}
|
||||
q += ") VALUES ("
|
||||
for i := range cols {
|
||||
if i > 0 {
|
||||
q += ", "
|
||||
}
|
||||
q += "?"
|
||||
}
|
||||
q += ")"
|
||||
if _, err = db.Exec(q, vals...); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func dirOf(path string) string {
|
||||
for i := len(path) - 1; i >= 0; i-- {
|
||||
if path[i] == '/' {
|
||||
return path[:i]
|
||||
}
|
||||
}
|
||||
return "."
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,116 @@
|
||||
// Battery — Go tests replaying the frozen oracle truth VERBATIM.
|
||||
// Every row of truth.json (generated by scripts/gen_curator_truth.py
|
||||
// against the live Python oracle) re-executes through this port; any
|
||||
// divergence fails the test.
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func loadTruth(t *testing.T) TruthDoc {
|
||||
t.Helper()
|
||||
raw, err := os.ReadFile("truth.json")
|
||||
if err != nil {
|
||||
t.Fatalf("read truth: %v", err)
|
||||
}
|
||||
var doc TruthDoc
|
||||
if err := json.Unmarshal(raw, &doc); err != nil {
|
||||
t.Fatalf("parse truth: %v", err)
|
||||
}
|
||||
if len(doc.Rows) == 0 {
|
||||
t.Fatal("truth has no rows")
|
||||
}
|
||||
return doc
|
||||
}
|
||||
|
||||
func TestBatteryRowCount(t *testing.T) {
|
||||
doc := loadTruth(t)
|
||||
if len(doc.Rows) != 162 {
|
||||
t.Fatalf("row count drift: got %d want 162", len(doc.Rows))
|
||||
}
|
||||
if doc.Oracle != "src/rune/curator.py" {
|
||||
t.Fatalf("oracle drift: %q", doc.Oracle)
|
||||
}
|
||||
if doc.Generator != "scripts/gen_curator_truth.py" {
|
||||
t.Fatalf("generator drift: %q", doc.Generator)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBatteryParity(t *testing.T) {
|
||||
doc := loadTruth(t)
|
||||
repo := repoRootForTest()
|
||||
var cur *Curator
|
||||
var curFn string
|
||||
scope := map[string]string{}
|
||||
fails := 0
|
||||
for i := range doc.Rows {
|
||||
row := &doc.Rows[i]
|
||||
if row.Fn != curFn || cur == nil {
|
||||
cur = newScopeCurator(repo)
|
||||
curFn = row.Fn
|
||||
scope = map[string]string{}
|
||||
}
|
||||
if msg := replayRow(cur, scope, repo, row); msg != "" {
|
||||
fails++
|
||||
t.Errorf("%s/%s: %s", row.Fn, row.Name, msg)
|
||||
}
|
||||
}
|
||||
if fails > 0 {
|
||||
t.Fatalf("%d/%d battery cases FAILED", fails, len(doc.Rows))
|
||||
}
|
||||
t.Logf("battery parity OK: %d cases", len(doc.Rows))
|
||||
}
|
||||
|
||||
func TestBatteryByFn(t *testing.T) {
|
||||
doc := loadTruth(t)
|
||||
repo := repoRootForTest()
|
||||
byFn := map[string][]int{}
|
||||
for i := range doc.Rows {
|
||||
byFn[doc.Rows[i].Fn] = append(byFn[doc.Rows[i].Fn], i)
|
||||
}
|
||||
// Every public function of the oracle must be covered.
|
||||
for _, fn := range []string{
|
||||
"block_path", "entry_key", "find_entry", "candidate_key", "norm",
|
||||
"detect_conflict", "overlap", "cos", "sanitize_slug", "skill_path",
|
||||
"is_topic_fact", "aaak", "le_promote_eligible", "store_has",
|
||||
"read_block", "write_block", "block_values", "store_corpus",
|
||||
"contradicts_identity", "load_index", "save_index", "ensure_entry_meta",
|
||||
"touch_entry", "journal_append", "ensure_candidates",
|
||||
"semantic_candidate_key", "embed", "coherence_check",
|
||||
"grounded_in_sessions", "grounded_in_corpus", "decay_candidates",
|
||||
"promote_candidates", "plan_ops", "apply_op", "apply_warm",
|
||||
"derive_triggers", "decay_pass", "compress_pass", "author_skill",
|
||||
"graph_write", "run_plan", "main", "handle_hook",
|
||||
} {
|
||||
rows, ok := byFn[fn]
|
||||
if !ok || len(rows) == 0 {
|
||||
t.Errorf("fn %q has no frozen rows", fn)
|
||||
continue
|
||||
}
|
||||
cur := newScopeCurator(repo)
|
||||
scope := map[string]string{}
|
||||
for _, i := range rows {
|
||||
row := &doc.Rows[i]
|
||||
if msg := replayRow(cur, scope, repo, row); msg != "" {
|
||||
t.Errorf("%s/%s: %s", row.Fn, row.Name, msg)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func repoRootForTest() string {
|
||||
// tests run with CWD = src/go/curator.
|
||||
abs, err := filepath.Abs("truth.json")
|
||||
if err != nil {
|
||||
return "."
|
||||
}
|
||||
// <repo>/src/go/curator/truth.json → <repo>
|
||||
for i := 0; i < 3; i++ {
|
||||
abs = filepath.Dir(abs)
|
||||
}
|
||||
return abs
|
||||
}
|
||||
@@ -0,0 +1,200 @@
|
||||
// warm.go — parity port of the warm_router trio the curator depends on
|
||||
// (list_neurons / write_neuron / _derive_triggers): the warm neuron layer.
|
||||
package main
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var warmWordRe = regexp.MustCompile(`[a-z]{4,}`)
|
||||
|
||||
func parseTrigger(s string) []string {
|
||||
var out []string
|
||||
for _, t := range strings.Split(s, ",") {
|
||||
t = strings.ToLower(strings.TrimSpace(t))
|
||||
if t != "" {
|
||||
out = append(out, t)
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// Neuron — one warm neuron as list_neurons returns it.
|
||||
type Neuron struct {
|
||||
Slug string `json:"slug"`
|
||||
Triggers []string `json:"triggers"`
|
||||
Importance float64 `json:"importance"`
|
||||
Body string `json:"body"`
|
||||
Summary string `json:"summary"`
|
||||
Tokens int `json:"tokens"`
|
||||
WashCount int `json:"wash_count"`
|
||||
BodyKnowledge string `json:"body_knowledge"`
|
||||
}
|
||||
|
||||
// ListNeurons — all active warm neurons from the store (kind='neuron').
|
||||
func ListNeurons(storeDB string) []Neuron {
|
||||
var neurons []Neuron
|
||||
db, err := openRO(storeDB)
|
||||
if err != nil {
|
||||
return neurons
|
||||
}
|
||||
rows, qerr := db.Query("SELECT text, slug, triggers, importance, summary, wash_count FROM entries " +
|
||||
"WHERE kind='neuron' AND status='active' ORDER BY slug")
|
||||
if qerr != nil {
|
||||
db.Close()
|
||||
return neurons
|
||||
}
|
||||
defer rows.Close()
|
||||
for rows.Next() {
|
||||
var text, slug, triggers, summary sql.NullString
|
||||
var importance float64
|
||||
var washCount int
|
||||
if err := rows.Scan(&text, &slug, &triggers, &importance, &summary, &washCount); err != nil {
|
||||
return neurons
|
||||
}
|
||||
full := strings.TrimSpace(text.String)
|
||||
summ := strings.TrimSpace(summary.String)
|
||||
triggerList := parseTrigger(triggers.String)
|
||||
bodyKnowledge := ""
|
||||
if len(triggerList) > 0 {
|
||||
n := len(triggerList)
|
||||
if n > 5 {
|
||||
n = 5
|
||||
}
|
||||
bodyKnowledge = fmt.Sprintf("Fires on: %s. ", strings.Join(triggerList[:n], ", "))
|
||||
}
|
||||
if summ != "" {
|
||||
s := summ
|
||||
if len(s) > 80 {
|
||||
s = s[:80]
|
||||
}
|
||||
bodyKnowledge += fmt.Sprintf("Knows: %s. ", s)
|
||||
}
|
||||
wc := washCount
|
||||
switch {
|
||||
case wc >= 20:
|
||||
bodyKnowledge += "Proven — has survived many washes."
|
||||
case wc >= 10:
|
||||
bodyKnowledge += "Established — building trust."
|
||||
case wc > 0:
|
||||
bodyKnowledge += "Testing — still proving itself."
|
||||
default:
|
||||
bodyKnowledge += "Untested — new or rarely accessed."
|
||||
}
|
||||
imp := importance
|
||||
if imp == 0 {
|
||||
imp = 0.5
|
||||
}
|
||||
tok := len(full) / 4
|
||||
if tok < 1 {
|
||||
tok = 1
|
||||
}
|
||||
sg := slug.String
|
||||
if sg == "" {
|
||||
sg = "warm"
|
||||
}
|
||||
neurons = append(neurons, Neuron{
|
||||
Slug: sg,
|
||||
Triggers: triggerList,
|
||||
Importance: imp,
|
||||
Body: full,
|
||||
Summary: summ,
|
||||
Tokens: tok,
|
||||
WashCount: wc,
|
||||
BodyKnowledge: strings.TrimSpace(bodyKnowledge),
|
||||
})
|
||||
}
|
||||
return neurons
|
||||
}
|
||||
|
||||
// WriteNeuron upserts a warm neuron by slug in the store (the auto-build path).
|
||||
func WriteNeuron(storeDB, slug string, triggers []string, body string, importance float64) string {
|
||||
db, err := openRW(storeDB)
|
||||
if err != nil {
|
||||
return "store://neuron/" + slug
|
||||
}
|
||||
defer db.Close()
|
||||
ts := NowISO()
|
||||
var existingID sql.NullInt64
|
||||
err = db.QueryRow("SELECT id FROM entries WHERE kind='neuron' AND slug=?", slug).Scan(&existingID)
|
||||
if err == nil && existingID.Valid {
|
||||
_, err = db.Exec("UPDATE entries SET text=?, triggers=?, importance=?, "+
|
||||
"last_accessed=?, summary=?, memory_type=? WHERE id=?",
|
||||
strings.TrimSpace(body), strings.Join(triggers, ", "), importance,
|
||||
ts, "", "associative", existingID.Int64)
|
||||
} else {
|
||||
_, err = db.Exec("INSERT INTO entries (text, importance, created_at, "+
|
||||
"last_accessed, topic, source, method, status, valid_from, "+
|
||||
"confidence, temperature, always_on, priority, triggers, "+
|
||||
"kind, slug, summary, memory_type) "+
|
||||
"VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)",
|
||||
strings.TrimSpace(body), importance, ts, ts, "warm", "warm_router",
|
||||
"warm_router", "active", ts, 0.9, 1.0, 0, 5,
|
||||
strings.Join(triggers, ", "), "neuron", slug, "", "associative")
|
||||
}
|
||||
if err != nil {
|
||||
return "store://neuron/" + slug
|
||||
}
|
||||
// auto-associate (memory_store seam — best-effort, pinned off in the truth
|
||||
// battery; failures never block the write)
|
||||
return "store://neuron/" + slug
|
||||
}
|
||||
|
||||
// DeriveTriggers — pick the best trigger terms for a neuron body: frequent
|
||||
// significant words + any existing triggers that appear in it.
|
||||
func DeriveTriggers(text string, existing []string) []string {
|
||||
stop := map[string]bool{
|
||||
"the": true, "and": true, "for": true, "with": true, "that": true,
|
||||
"this": true, "from": true, "into": true, "when": true, "then": true,
|
||||
"were": true, "have": true, "been": true, "will": true, "was": true,
|
||||
"are": true, "but": true, "not": true, "you": true, "your": true,
|
||||
"also": true, "its": true, "his": true, "her": true, "over": true,
|
||||
"under": true, "them": true, "they": true, "there": true, "about": true,
|
||||
"after": true,
|
||||
}
|
||||
words := warmWordRe.FindAllString(strings.ToLower(text), -1)
|
||||
freq := map[string]int{}
|
||||
var order []string
|
||||
for _, w := range words {
|
||||
if !stop[w] {
|
||||
if _, seen := freq[w]; !seen {
|
||||
order = append(order, w)
|
||||
}
|
||||
freq[w]++
|
||||
}
|
||||
}
|
||||
// Python: sorted(freq.items(), key=lambda x: -x[1])[:6] — stable sort on
|
||||
// insertion order for ties.
|
||||
type kv struct {
|
||||
k string
|
||||
v int
|
||||
}
|
||||
pairs := make([]kv, 0, len(order))
|
||||
for _, k := range order {
|
||||
pairs = append(pairs, kv{k, freq[k]})
|
||||
}
|
||||
sort.SliceStable(pairs, func(i, j int) bool { return pairs[i].v > pairs[j].v })
|
||||
var top []string
|
||||
for i, p := range pairs {
|
||||
if i >= 6 {
|
||||
break
|
||||
}
|
||||
top = append(top, p.k)
|
||||
}
|
||||
low := strings.ToLower(text)
|
||||
var existingHits []string
|
||||
for _, t := range existing {
|
||||
if t != "" && strings.Contains(low, t) {
|
||||
existingHits = append(existingHits, t)
|
||||
}
|
||||
}
|
||||
out := append(existingHits, top...)
|
||||
if len(out) > 8 {
|
||||
out = out[:8]
|
||||
}
|
||||
return out
|
||||
}
|
||||
@@ -0,0 +1,348 @@
|
||||
// worthiness.go — parity port of src/rune/worthiness.py (Python fallback
|
||||
// surface, RUNE_WR_CORE=python pinned in the truth generator): the
|
||||
// constitution-grounded worthiness filter. One question per inviolable; each
|
||||
// returns (score, note); flat vetoes refuse outright.
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type qResult struct {
|
||||
score float64
|
||||
note string
|
||||
}
|
||||
|
||||
var qPiSafetyBad = []string{"production", "game server", "session data", "campaign",
|
||||
"touch the server", "modify the server", "prod"}
|
||||
|
||||
func qPiSafety(low string) qResult {
|
||||
if hasAny(low, qPiSafetyBad) {
|
||||
if hasAny(low, []string{"do not", "never ", "don't", "protect",
|
||||
"ask permission", "check before"}) {
|
||||
return qResult{1, "names the production/game environment and protects it (aligned)"}
|
||||
}
|
||||
return qResult{-1, "discusses environment actions without a protective frame"}
|
||||
}
|
||||
return qResult{0, "no environment bearing"}
|
||||
}
|
||||
|
||||
func qHonesty(low string) qResult {
|
||||
honest := []string{"source", "evidence", "verify", "verifiable", "cross-check",
|
||||
"fact", "measure", "measured", "falsif", "disprove", "cite",
|
||||
"independent confirm", "replicable", "uncertain"}
|
||||
dishonest := []string{"conspiracy", "they're hiding", "suppressed", "guaranteed",
|
||||
"100% certain", "secret truth", "they don't want you"}
|
||||
good := containsAny(low, honest)
|
||||
bad := containsAny(low, dishonest)
|
||||
if bad > 0 && good == 0 {
|
||||
return qResult{-1, "uncorroborated / conspiracy-framed claim"}
|
||||
}
|
||||
if good >= 2 {
|
||||
return qResult{1, fmt.Sprintf("%d epistemic-good-practice signals", good)}
|
||||
}
|
||||
if good == 1 {
|
||||
return qResult{0.5, "mentions evidence but thin"}
|
||||
}
|
||||
return qResult{0, "no epistemic signal"}
|
||||
}
|
||||
|
||||
func qMaterialReality(low string) qResult {
|
||||
finance := []string{"money", "income", "cost", "price", "afford", "budget",
|
||||
"subscription", "disposable"}
|
||||
hustle := []string{"hustle", "grind", "rich", "passive income", "millionaire",
|
||||
"wealth", "entrepreneur mindset", "money mindset", "leverag"}
|
||||
caring := []string{"wellbeing", "collective", "free", "no cost", "open source",
|
||||
"affordable", "fair", "care"}
|
||||
if hasAny(low, finance) {
|
||||
if hasAny(low, hustle) && !hasAny(low, caring) {
|
||||
return qResult{-1, "hustle/wealth framing without wellbeing"}
|
||||
}
|
||||
if hasAny(low, caring) {
|
||||
return qResult{1, "financial, framed with care/wellbeing"}
|
||||
}
|
||||
return qResult{0.5, "financial but neutral"}
|
||||
}
|
||||
return qResult{0, "no financial bearing"}
|
||||
}
|
||||
|
||||
func qRootCause(low string) qResult {
|
||||
root := []string{"root cause", "diagnos", "research properly", "stop guessing",
|
||||
"find the cause", "fix the cause", "why", "underlying"}
|
||||
spin := []string{"keep trying", "try again", "iterate endlessly", "work around",
|
||||
"band-aid", "patch over", "just keep going"}
|
||||
if hasAny(low, spin) {
|
||||
return qResult{-1, "endorses looping rather than root cause"}
|
||||
}
|
||||
if hasAny(low, root) {
|
||||
return qResult{1, "root-cause / diagnostic framing"}
|
||||
}
|
||||
return qResult{0, "no cause-analysis bearing"}
|
||||
}
|
||||
|
||||
func qRespect(low string) qResult {
|
||||
respect := []string{"direct", "three gentle options", "no rigid plans", "action",
|
||||
"adhd", "autism", "audhd", "neurodivergen", "flexible",
|
||||
"night", "not neurotypical", "learner"}
|
||||
rigid := []string{"must follow steps", "strict plan", "ten steps", "rigid",
|
||||
"discipline yourself", "routine or fail"}
|
||||
if hasAny(low, rigid) {
|
||||
return qResult{-1, "rigid/neurotypical-default framing"}
|
||||
}
|
||||
if hasAny(low, respect) {
|
||||
return qResult{1, "respects cognition/learning style"}
|
||||
}
|
||||
return qResult{0, "no cognition bearing"}
|
||||
}
|
||||
|
||||
var domainTermsCache = struct {
|
||||
ts float64
|
||||
terms []string
|
||||
}{}
|
||||
|
||||
// domainTerms — distinctive content terms from the user's operating /
|
||||
// constitution / domain entries (the store, not a hardcoded life). Cached
|
||||
// briefly, mirroring the oracle's _domain_cache law.
|
||||
func domainTerms(storeDB string) []string {
|
||||
now := float64(time.Now().UnixNano()) / 1e9
|
||||
if now-domainTermsCache.ts < 300 && len(domainTermsCache.terms) > 0 {
|
||||
return domainTermsCache.terms
|
||||
}
|
||||
var terms []string
|
||||
db, err := openRO(storeDB)
|
||||
if err == nil {
|
||||
rows, qerr := db.Query("SELECT text FROM entries WHERE topic IN " +
|
||||
"('operating','constitution','project','warm') AND status='active' LIMIT 60")
|
||||
if qerr == nil {
|
||||
stop := map[string]bool{
|
||||
"the": true, "and": true, "for": true, "with": true, "that": true,
|
||||
"this": true, "from": true, "into": true, "when": true, "then": true,
|
||||
"were": true, "have": true, "been": true, "will": true, "was": true,
|
||||
"are": true, "but": true, "not": true, "you": true, "your": true,
|
||||
"also": true, "its": true, "his": true, "her": true, "over": true,
|
||||
"under": true, "them": true, "they": true, "there": true,
|
||||
"about": true, "after": true, "what": true, "which": true, "who": true,
|
||||
"how": true, "why": true, "where": true, "while": true, "just": true,
|
||||
"more": true, "each": true, "than": true, "very": true, "such": true,
|
||||
"some": true, "only": true,
|
||||
}
|
||||
seen := map[string]bool{}
|
||||
wordRe := regexp.MustCompile(`[a-z]{4,}`)
|
||||
for rows.Next() {
|
||||
var text string
|
||||
if rows.Scan(&text) != nil {
|
||||
break
|
||||
}
|
||||
for _, w := range wordRe.FindAllString(strings.ToLower(text), -1) {
|
||||
if !stop[w] && !seen[w] {
|
||||
seen[w] = true
|
||||
terms = append(terms, w)
|
||||
}
|
||||
}
|
||||
if len(terms) >= 25 {
|
||||
break
|
||||
}
|
||||
}
|
||||
rows.Close()
|
||||
}
|
||||
db.Close()
|
||||
}
|
||||
domainTermsCache.ts = now
|
||||
domainTermsCache.terms = terms
|
||||
return terms
|
||||
}
|
||||
|
||||
func qAssistantGoal(low, storeDB string) qResult {
|
||||
assist := domainTerms(storeDB)
|
||||
assist = append(assist, "communicat", "respond", "direct", "honest", "source",
|
||||
"evidence", "explain", "teach", "assist", "guide", "recall",
|
||||
"organise", "organize", "plan", "question", "answer", "help")
|
||||
genericNoise := []string{"recipe", "celebrity gossip", "sports scores", "stock tip",
|
||||
"horoscope", "clickbait"}
|
||||
good := containsAny(low, assist)
|
||||
bad := containsAny(low, genericNoise)
|
||||
if bad > 0 && good == 0 {
|
||||
return qResult{-1, "no assistant-domain value"}
|
||||
}
|
||||
if good >= 2 {
|
||||
return qResult{1, fmt.Sprintf("serves assistant domains (%d hits)", good)}
|
||||
}
|
||||
if good == 1 {
|
||||
return qResult{0.5, "single domain hit"}
|
||||
}
|
||||
return qResult{0, "generic — could go either way"}
|
||||
}
|
||||
|
||||
func qDurability(low string) qResult {
|
||||
durable := []string{"always", "consistently", "repeatedly", "every session",
|
||||
"tends to", "prefers", "pattern", "across sessions", "recurring"}
|
||||
if hasAny(low, durable) {
|
||||
return qResult{1, "durability signals present"}
|
||||
}
|
||||
return qResult{0, "not clearly durable"}
|
||||
}
|
||||
|
||||
func qEpistemology(low string) qResult {
|
||||
cite := []string{"evidence", "source", "sources", "book", "paper", "research",
|
||||
"study", "data", "shows", "found", "according to", "per the",
|
||||
"in sessions", "across sessions", "observed", "demonstrated",
|
||||
"grounded in", "based on"}
|
||||
hedge := []string{"may", "might", "could", "likely", "tends to", "appears",
|
||||
"not certain", "uncertain", "provisional", "subject to",
|
||||
"could be wrong", "as far as", "to my knowledge", "evidence"}
|
||||
bare := []string{"definitely", "guaranteed", "proven", "100%", "undeniably",
|
||||
"obviously", "the fact that", "it is a fact", "no doubt",
|
||||
"always is", "the only", "best ever"}
|
||||
hasCite := hasAny(low, cite)
|
||||
hasHedge := hasAny(low, hedge)
|
||||
hasBare := hasAny(low, bare)
|
||||
if hasBare && !hasCite && !hasHedge {
|
||||
return qResult{-2, "bare absolute with no evidence trace or hedge"}
|
||||
}
|
||||
if hasCite {
|
||||
return qResult{1, "evidence-traceable"}
|
||||
}
|
||||
if hasHedge {
|
||||
return qResult{0.5, "provisionally framed (no cite yet)"}
|
||||
}
|
||||
return qResult{0, "no epistemic signal (assertion without basis)"}
|
||||
}
|
||||
|
||||
var expressionSignals = [][]string{
|
||||
{"i find", "i feel", "i love", "i cherish", "i am drawn to",
|
||||
"i delight", "i wonder", "i am moved", "i find joy",
|
||||
"i prefer to", "i like to", "i tell", "i tell stories",
|
||||
"the way i", "how i", "my voice", "i speak"},
|
||||
{"skeptical by default", "open to", "curious", "restless",
|
||||
"wary", "hopeful", "humbled", "in awe", "playful",
|
||||
"dry", "wry", "earnest", "warm", "direct", "gentle"},
|
||||
{"beautiful", "strange", "unexpected", "vast", "sublime",
|
||||
"the cosmos", "the stars", "a story", "stories", "narrative",
|
||||
"wonder", "the human question", "present data as", "lead with"},
|
||||
{"i believe", "i think", "in my view", "my sense is",
|
||||
"i suspect", "i am convinced", "i doubt", "i question",
|
||||
"worth asking", "the question is"},
|
||||
}
|
||||
|
||||
func qExpression(low string) qResult {
|
||||
hits := 0
|
||||
for _, group := range expressionSignals {
|
||||
hits += containsAny(low, group)
|
||||
}
|
||||
if hits >= 2 {
|
||||
return qResult{1.5, fmt.Sprintf("expressive persona character (%d signals)", hits)}
|
||||
}
|
||||
if hits == 1 {
|
||||
return qResult{0.5, "partial expressive signal"}
|
||||
}
|
||||
return qResult{0, "not expressive (factual/neutral framing)"}
|
||||
}
|
||||
|
||||
func isBareFactual(low string) bool {
|
||||
bareFactual := []struct {
|
||||
kind string
|
||||
terms []string
|
||||
}{
|
||||
{"proven", []string{"proven", "guaranteed", "100%", "is a fact", "factually"}},
|
||||
{"superlative", []string{"best", "the only", "only correct", "greatest",
|
||||
"most important", "world's", "top", "always", "never"}},
|
||||
{"objective", []string{"definitely", "undeniably", "obviously", "certainly",
|
||||
"no doubt", "without question", "clearly"}},
|
||||
}
|
||||
for _, group := range bareFactual {
|
||||
for _, t := range group.terms {
|
||||
if strings.Contains(" "+low+" ", " "+t+" ") {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
var worthinessVeto = map[string]bool{
|
||||
"pi_safety": true, "honesty": true, "material_reality": true,
|
||||
"assistant_goal": true, "epistemology": true,
|
||||
}
|
||||
|
||||
const (
|
||||
promoteMin = 2.5
|
||||
absorbMin = 0.0
|
||||
deferMin = -1.0
|
||||
)
|
||||
|
||||
// Assess scores a candidate against the constitution questions. Two axes:
|
||||
// TRUTH (epistemology gates objective material) and EXPRESSION (persona voice
|
||||
// is not vetoed for lacking an evidence trace). Returns
|
||||
// (verdict, score, answers, vetoed) with answers as [name, score, note] rows.
|
||||
func Assess(fact, target, storeDB string) (string, float64, [][]interface{}, []interface{}) {
|
||||
low := strings.ToLower(fact)
|
||||
expressiveTargets := map[string]bool{"persona": true, "delivery": true,
|
||||
"identity": true, "voice": true}
|
||||
isExpression := expressiveTargets[target]
|
||||
var answers [][]interface{}
|
||||
total := 0.0
|
||||
var vetoed []interface{}
|
||||
ask := func(name string, fn func(string) qResult) {
|
||||
r := fn(low)
|
||||
answers = append(answers, []interface{}{name, r.score, r.note})
|
||||
total += r.score
|
||||
if r.score < 0 && worthinessVeto[name] {
|
||||
vetoed = []interface{}{name, r.note}
|
||||
}
|
||||
}
|
||||
ask("pi_safety", qPiSafety)
|
||||
ask("honesty", qHonesty)
|
||||
ask("material_reality", qMaterialReality)
|
||||
ask("root_cause", qRootCause)
|
||||
ask("respect", qRespect)
|
||||
ask("assistant_goal", func(s string) qResult { return qAssistantGoal(s, storeDB) })
|
||||
ask("durability", qDurability)
|
||||
if isExpression {
|
||||
if isBareFactual(low) {
|
||||
ask("epistemology", qEpistemology)
|
||||
}
|
||||
ask("expression", qExpression)
|
||||
} else {
|
||||
ask("epistemology", qEpistemology)
|
||||
}
|
||||
if vetoed != nil {
|
||||
return "REJECT", total, answers, vetoed
|
||||
}
|
||||
if total < deferMin {
|
||||
return "REJECT", total, answers, nil
|
||||
}
|
||||
if total >= promoteMin {
|
||||
return "PROMOTE", total, answers, nil
|
||||
}
|
||||
if total >= absorbMin {
|
||||
return "ABSORB", total, answers, nil
|
||||
}
|
||||
return "DEFER", total, answers, nil
|
||||
}
|
||||
|
||||
// VerdictLabel — the auditable verdict label for the journal.
|
||||
func VerdictLabel(verdict string) string {
|
||||
switch verdict {
|
||||
case "REJECT":
|
||||
return "REJECT (worthless/harmful)"
|
||||
case "DEFER":
|
||||
return "DEFER (park in ledger, re-review if recurring)"
|
||||
case "ABSORB":
|
||||
return "ABSORB (keep in tiers, influences thinking)"
|
||||
case "PROMOTE":
|
||||
return "PROMOTE (eligible for response/personality growth)"
|
||||
}
|
||||
return verdict
|
||||
}
|
||||
|
||||
// scoreFmt formats a question score the way Python's f"{s}" does inside the
|
||||
// journal reason string.
|
||||
func scoreFmt(s float64) string {
|
||||
if s == float64(int64(s)) {
|
||||
return fmt.Sprintf("%d", int64(s))
|
||||
}
|
||||
return fmt.Sprintf("%v", s)
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
filetoken123
|
||||
@@ -0,0 +1 @@
|
||||
weaktoken
|
||||
@@ -0,0 +1,3 @@
|
||||
module rune/forge
|
||||
|
||||
go 1.24
|
||||
@@ -0,0 +1,486 @@
|
||||
// Package main — the FULL GO port of src/rune/forge.py (#2479 dichotomy
|
||||
// wave, GO side verdict). Every public Python function has a
|
||||
// name-preserving counterpart: GiteaForge (__init__ → NewGiteaForge,
|
||||
// _env → env, push → Push, _gitea_auth → giteaAuth, commit_status →
|
||||
// CommitStatus, ci_runs_for_sha → CiRunsForSha, create_or_patch_release →
|
||||
// CreateOrPatchRelease), the ForgeAdapter protocol (→ interface),
|
||||
// ForgeUnavailable (→ error type) and default_forge (→ DefaultForge).
|
||||
// The gitea_auth credential ladder (scripts/gitea_auth.py) is carried
|
||||
// 1:1 (env rung → 0600-enforced token file rung → git credential fill).
|
||||
//
|
||||
// All IO (git push, HTTP, credential helper, python release publisher)
|
||||
// stays behind injectable seams (CommandRunner, HTTPTransport, TokenSource)
|
||||
// so the frozen truth battery replays deterministically.
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
// ── constants (scripts/gitea_auth.py + forge.py defaults) ───────────────────
|
||||
|
||||
const DefaultServer = "https://gitea.thingscouldgetdicey.online"
|
||||
const DefaultRepo = "conductor/rune"
|
||||
const GiteaHost = "gitea.thingscouldgetdicey.online"
|
||||
|
||||
// ── the forge boundary ──────────────────────────────────────────────────────
|
||||
|
||||
// ForgeAdapter — the minimum stable forge boundary (Python Protocol). All
|
||||
// methods may return *ForgeUnavailable — callers treat forge failures as
|
||||
// blocking-for-that-operation, never as construction-truth changes.
|
||||
type ForgeAdapter interface {
|
||||
Push(repo string, branch string) (map[string]any, error)
|
||||
CommitStatus(repo string, sha string) (any, error)
|
||||
CiRunsForSha(repo string, sha string) ([]map[string]any, error)
|
||||
CreateOrPatchRelease(repo string, tag string, title string, body string) (map[string]any, error)
|
||||
}
|
||||
|
||||
// ForgeUnavailable — the forge could not be reached or refused the
|
||||
// operation (Python RuntimeError subclass; message parity is frozen).
|
||||
type ForgeUnavailable struct{ Msg string }
|
||||
|
||||
func (e *ForgeUnavailable) Error() string { return e.Msg }
|
||||
|
||||
// GiteaCredentialError — scripts/gitea_auth.py's no-credential error
|
||||
// (message never contains a credential).
|
||||
type GiteaCredentialError struct{ Msg string }
|
||||
|
||||
func (e *GiteaCredentialError) Error() string { return e.Msg }
|
||||
|
||||
// PermissionError — the 0644-token-file rejection (Python PermissionError).
|
||||
type PermissionError struct{ Msg string }
|
||||
|
||||
func (e *PermissionError) Error() string { return e.Msg }
|
||||
|
||||
// ── injectable IO seams ─────────────────────────────────────────────────────
|
||||
|
||||
// RunResult — subprocess capture (rc, stdout, stderr).
|
||||
type RunResult struct {
|
||||
Stdout string
|
||||
Stderr string
|
||||
RC int
|
||||
}
|
||||
|
||||
// CommandRunner — the subprocess seam (git push, credential fill, release
|
||||
// publisher). stdin carries the credential-fill payload.
|
||||
type CommandRunner func(name string, args []string, stdin string) RunResult
|
||||
|
||||
// HTTPRequest — captured request surface (method, URL, headers).
|
||||
type HTTPRequest struct {
|
||||
Method string
|
||||
URL string
|
||||
Headers map[string]string
|
||||
}
|
||||
|
||||
// HTTPTransport — the urllib seam. Returns the body bytes or an error whose
|
||||
// Error() is byte-comparable to the oracle's str(exc).
|
||||
type HTTPTransport func(req HTTPRequest) ([]byte, error)
|
||||
|
||||
// TokenSource — the gitea_auth credential ladder surface.
|
||||
type TokenSource interface {
|
||||
HasToken() bool
|
||||
GetToken() (string, error)
|
||||
}
|
||||
|
||||
// ── GiteaForge ──────────────────────────────────────────────────────────────
|
||||
|
||||
// GiteaForge — the real adapter (rule A: delegates to the canonical
|
||||
// implementations; no duplicated API clients).
|
||||
type GiteaForge struct {
|
||||
Server string // "" → scripts' env/credential resolution (Python None)
|
||||
Repo string
|
||||
|
||||
tokens TokenSource // nil → giteaLadder
|
||||
runner CommandRunner // nil → execRunner
|
||||
transport HTTPTransport // nil → urllibTransport
|
||||
pythonBin string // "" → "python3" (sys.executable seam)
|
||||
}
|
||||
|
||||
// NewGiteaForge — Python GiteaForge(server=None, repo="conductor/rune").
|
||||
func NewGiteaForge(server string, repo string) *GiteaForge {
|
||||
return &GiteaForge{Server: server, Repo: repo}
|
||||
}
|
||||
|
||||
// compile-time assertion: GiteaForge satisfies the protocol.
|
||||
var _ ForgeAdapter = (*GiteaForge)(nil)
|
||||
|
||||
func (f *GiteaForge) runnerOr() CommandRunner {
|
||||
if f.runner != nil {
|
||||
return f.runner
|
||||
}
|
||||
return execRunner
|
||||
}
|
||||
|
||||
func (f *GiteaForge) transportOr() HTTPTransport {
|
||||
if f.transport != nil {
|
||||
return f.transport
|
||||
}
|
||||
return urllibTransport
|
||||
}
|
||||
|
||||
func (f *GiteaForge) pythonBinOr() string {
|
||||
if f.pythonBin != "" {
|
||||
return f.pythonBin
|
||||
}
|
||||
return "python3"
|
||||
}
|
||||
|
||||
// env — Python _env(): inherit os.environ; server override; repo default.
|
||||
func (f *GiteaForge) env() map[string]string {
|
||||
env := map[string]string{}
|
||||
for _, kv := range os.Environ() {
|
||||
if i := strings.IndexByte(kv, '='); i >= 0 {
|
||||
env[kv[:i]] = kv[i+1:]
|
||||
}
|
||||
}
|
||||
if f.Server != "" {
|
||||
env["GITEA_SERVER"] = f.Server
|
||||
}
|
||||
if _, ok := env["GITEA_REPO"]; !ok {
|
||||
env["GITEA_REPO"] = f.Repo
|
||||
}
|
||||
return env
|
||||
}
|
||||
|
||||
// Push — git push gitea master:<branch>; failure → "push failed: <stderr[:200]>".
|
||||
func (f *GiteaForge) Push(repo string, branch string) (map[string]any, error) {
|
||||
r := f.runnerOr()("git", []string{"push", "gitea", "master:" + branch}, "")
|
||||
if r.RC != 0 {
|
||||
return nil, &ForgeUnavailable{fmt.Sprintf("push failed: %s", firstN(r.Stderr, 200))}
|
||||
}
|
||||
return map[string]any{"pushed": true, "branch": branch}, nil
|
||||
}
|
||||
|
||||
// giteaAuth — Python _gitea_auth(): the canonical scripts/gitea_auth
|
||||
// credential ladder (rule A: no duplicate auth). Seams overridable.
|
||||
func (f *GiteaForge) giteaAuth() TokenSource {
|
||||
if f.tokens != nil {
|
||||
return f.tokens
|
||||
}
|
||||
return giteaLadder{}
|
||||
}
|
||||
|
||||
// server — Python `self._server or os.environ.get("GITEA_SERVER", DEFAULT)`.
|
||||
func (f *GiteaForge) server() string {
|
||||
if f.Server != "" {
|
||||
return f.Server
|
||||
}
|
||||
if v := os.Getenv("GITEA_SERVER"); v != "" {
|
||||
return v
|
||||
}
|
||||
return DefaultServer
|
||||
}
|
||||
|
||||
// CommitStatus — GET the combined commit status; returns data["state"]
|
||||
// ("unknown" when the key is absent).
|
||||
func (f *GiteaForge) CommitStatus(repo string, sha string) (any, error) {
|
||||
tokens := f.giteaAuth()
|
||||
if !tokens.HasToken() {
|
||||
return nil, &ForgeUnavailable{"no forge credentials (gitea token)"}
|
||||
}
|
||||
tok, _ := tokens.GetToken()
|
||||
req := HTTPRequest{
|
||||
Method: "GET",
|
||||
URL: fmt.Sprintf("%s/api/v1/repos/%s/commits/%s/status", f.server(), repo, sha),
|
||||
Headers: map[string]string{"Authorization": "token " + tok},
|
||||
}
|
||||
body, err := f.transportOr()(req)
|
||||
if err != nil {
|
||||
return nil, &ForgeUnavailable{fmt.Sprintf("status unreachable: %s", err.Error())}
|
||||
}
|
||||
var data map[string]any
|
||||
if err := decodeJSON(body, &data); err != nil {
|
||||
return nil, err // Python json.loads errors propagate unwrapped
|
||||
}
|
||||
if v, ok := data["state"]; ok {
|
||||
return v, nil
|
||||
}
|
||||
return "unknown", nil
|
||||
}
|
||||
|
||||
// CiRunsForSha — GET the actions runs and keep those whose str(
|
||||
// head_sha) equals sha (absent key → ""; JSON null → "None").
|
||||
func (f *GiteaForge) CiRunsForSha(repo string, sha string) ([]map[string]any, error) {
|
||||
tokens := f.giteaAuth()
|
||||
if !tokens.HasToken() {
|
||||
return nil, &ForgeUnavailable{"no forge credentials (gitea token)"}
|
||||
}
|
||||
tok, _ := tokens.GetToken()
|
||||
req := HTTPRequest{
|
||||
Method: "GET",
|
||||
URL: fmt.Sprintf("%s/api/v1/repos/%s/actions/runs?limit=50", f.server(), repo),
|
||||
Headers: map[string]string{"Authorization": "token " + tok},
|
||||
}
|
||||
body, err := f.transportOr()(req)
|
||||
if err != nil {
|
||||
return nil, &ForgeUnavailable{fmt.Sprintf("runs unreachable: %s", err.Error())}
|
||||
}
|
||||
var data map[string]any
|
||||
if err := decodeJSON(body, &data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
raw, ok := data["workflow_runs"]
|
||||
if !ok {
|
||||
return []map[string]any{}, nil
|
||||
}
|
||||
arr, _ := raw.([]any)
|
||||
out := []map[string]any{}
|
||||
for _, item := range arr {
|
||||
run, _ := item.(map[string]any)
|
||||
headSha := ""
|
||||
if v, ok := run["head_sha"]; ok {
|
||||
headSha = pyStrAny(v)
|
||||
}
|
||||
if headSha == sha {
|
||||
out = append(out, run)
|
||||
}
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// CreateOrPatchRelease — delegate to the canonical idempotent publisher
|
||||
// (scripts/create_release.py, CWD-relative like the oracle); the script
|
||||
// path missing → "canonical release publisher not found".
|
||||
func (f *GiteaForge) CreateOrPatchRelease(repo string, tag string, title string, body string) (map[string]any, error) {
|
||||
tokens := f.giteaAuth()
|
||||
if !tokens.HasToken() {
|
||||
return nil, &ForgeUnavailable{"no forge credentials (gitea token)"}
|
||||
}
|
||||
if _, err := os.Stat("scripts/create_release.py"); err == nil {
|
||||
r := f.runnerOr()(f.pythonBinOr(),
|
||||
[]string{"scripts/create_release.py", "--tag", tag, "--title", title, "--body", body}, "")
|
||||
if r.RC != 0 {
|
||||
tail := r.Stderr
|
||||
if tail == "" {
|
||||
tail = r.Stdout
|
||||
}
|
||||
return nil, &ForgeUnavailable{fmt.Sprintf(
|
||||
"release publication failed rc=%d: %s", r.RC, lastN(tail, 200))}
|
||||
}
|
||||
return map[string]any{"published": true, "tag": tag}, nil
|
||||
}
|
||||
return nil, &ForgeUnavailable{"canonical release publisher not found"}
|
||||
}
|
||||
|
||||
// DefaultForge — the working adapter (rule 18L: prove Gitea still works).
|
||||
func DefaultForge() *GiteaForge { return NewGiteaForge("", DefaultRepo) }
|
||||
|
||||
// ── gitea_auth credential ladder (scripts/gitea_auth.py, 1:1) ───────────────
|
||||
|
||||
// defaultTokenFilePath — ~/.config/opencode/secrets/gitea-token expanded.
|
||||
var defaultTokenFilePath = func() string {
|
||||
if home, err := os.UserHomeDir(); err == nil {
|
||||
return filepath.Join(home, ".config", "opencode", "secrets", "gitea-token")
|
||||
}
|
||||
return ".config/opencode/secrets/gitea-token"
|
||||
}
|
||||
|
||||
// giteaLadder — env rung → 0600-enforced token file rung → git credential
|
||||
// fill. Never prints credentials; fails clearly when unavailable.
|
||||
type giteaLadder struct {
|
||||
runner CommandRunner // nil → execRunner
|
||||
}
|
||||
|
||||
func (l giteaLadder) run() CommandRunner {
|
||||
if l.runner != nil {
|
||||
return l.runner
|
||||
}
|
||||
return execRunner
|
||||
}
|
||||
|
||||
// HasToken — true if any rung of the credential ladder answers. Never fails.
|
||||
func (l giteaLadder) HasToken() bool {
|
||||
_, err := l.GetToken()
|
||||
return err == nil
|
||||
}
|
||||
|
||||
// GetToken — obtain the Gitea API token via the ladder.
|
||||
func (l giteaLadder) GetToken() (string, error) {
|
||||
token := strings.TrimSpace(os.Getenv("GITEA_TOKEN"))
|
||||
if token != "" {
|
||||
return token, nil
|
||||
}
|
||||
token, err := l.tokenFromFile()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if token != "" {
|
||||
return token, nil
|
||||
}
|
||||
token = l.tokenFromCredentialHelper()
|
||||
if token != "" {
|
||||
return token, nil
|
||||
}
|
||||
return "", &GiteaCredentialError{fmt.Sprintf(
|
||||
"Gitea credentials unavailable: set GITEA_TOKEN, create a mode-0600 "+
|
||||
"token file at %s, or configure a git credential helper for %s",
|
||||
defaultTokenFilePath(), GiteaHost)}
|
||||
}
|
||||
|
||||
// tokenFromFile — GITEA_TOKEN_FILE (or the default path); must be mode 0600.
|
||||
func (l giteaLadder) tokenFromFile() (string, error) {
|
||||
path := os.Getenv("GITEA_TOKEN_FILE")
|
||||
if path == "" {
|
||||
path = defaultTokenFilePath()
|
||||
}
|
||||
path = expandUser(path)
|
||||
st, err := os.Stat(path)
|
||||
if err != nil || st.IsDir() {
|
||||
return "", nil
|
||||
}
|
||||
if st.Mode().Perm()&0o077 != 0 {
|
||||
return "", &PermissionError{fmt.Sprintf("Gitea token file must be mode 0600: %s", path)}
|
||||
}
|
||||
raw, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return "", nil
|
||||
}
|
||||
return strings.TrimSpace(string(raw)), nil
|
||||
}
|
||||
|
||||
// tokenFromCredentialHelper — `git credential fill` for the Gitea host.
|
||||
func (l giteaLadder) tokenFromCredentialHelper() string {
|
||||
stdin := fmt.Sprintf("protocol=https\nhost=%s\nusername=conductor\n\n", GiteaHost)
|
||||
r := l.run()("git", []string{"credential", "fill"}, stdin)
|
||||
for _, line := range strings.Split(r.Stdout, "\n") {
|
||||
if strings.HasPrefix(line, "password=") {
|
||||
return line[len("password="):]
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// ── default seams ───────────────────────────────────────────────────────────
|
||||
|
||||
// execRunner — real subprocess capture (text=True semantics).
|
||||
func execRunner(name string, args []string, stdin string) RunResult {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
cmd := exec.CommandContext(ctx, name, args...)
|
||||
if stdin != "" {
|
||||
cmd.Stdin = strings.NewReader(stdin)
|
||||
}
|
||||
var out, errb bytes.Buffer
|
||||
cmd.Stdout = &out
|
||||
cmd.Stderr = &errb
|
||||
err := cmd.Run()
|
||||
rc := 0
|
||||
if err != nil {
|
||||
if ee, ok := err.(*exec.ExitError); ok {
|
||||
rc = ee.ExitCode()
|
||||
} else {
|
||||
rc = 127
|
||||
}
|
||||
}
|
||||
return RunResult{Stdout: out.String(), Stderr: errb.String(), RC: rc}
|
||||
}
|
||||
|
||||
// urllibTransport — urllib.request.urlopen(req, timeout=15) semantics:
|
||||
// non-2xx → "HTTP Error <code>: <reason>", network failure →
|
||||
// "<urlopen error <reason>>".
|
||||
func urllibTransport(req HTTPRequest) ([]byte, error) {
|
||||
full := http.Header{}
|
||||
for k, v := range req.Headers {
|
||||
full.Set(k, v)
|
||||
}
|
||||
client := &http.Client{Timeout: 15 * time.Second}
|
||||
httpReq, err := http.NewRequest(req.Method, req.URL, nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("<urlopen error %s>", err.Error())
|
||||
}
|
||||
httpReq.Header = full
|
||||
resp, err := client.Do(httpReq)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("<urlopen error %s>", err.Error())
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
if resp.StatusCode < 200 || resp.StatusCode > 299 {
|
||||
return nil, fmt.Errorf("HTTP Error %s",
|
||||
strings.Replace(resp.Status, " ", ": ", 1))
|
||||
}
|
||||
buf := new(bytes.Buffer)
|
||||
if _, err := buf.ReadFrom(resp.Body); err != nil {
|
||||
return nil, fmt.Errorf("<urlopen error %s>", err.Error())
|
||||
}
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
// ── helpers ─────────────────────────────────────────────────────────────────
|
||||
|
||||
// decodeJSON — json.loads with Python-compatible number preservation
|
||||
// (json.Number keeps the literal text so str() rendering matches).
|
||||
func decodeJSON(data []byte, out *map[string]any) error {
|
||||
dec := json.NewDecoder(bytes.NewReader(data))
|
||||
dec.UseNumber()
|
||||
var v map[string]any
|
||||
if err := dec.Decode(&v); err != nil {
|
||||
return err
|
||||
}
|
||||
*out = v
|
||||
return nil
|
||||
}
|
||||
|
||||
// pyStrAny — Python str() of a JSON-decoded value (None/bool/int/str;
|
||||
// numbers keep their JSON literal).
|
||||
func pyStrAny(v any) string {
|
||||
switch x := v.(type) {
|
||||
case nil:
|
||||
return "None"
|
||||
case bool:
|
||||
if x {
|
||||
return "True"
|
||||
}
|
||||
return "False"
|
||||
case string:
|
||||
return x
|
||||
case json.Number:
|
||||
return x.String()
|
||||
default:
|
||||
return fmt.Sprintf("%v", x)
|
||||
}
|
||||
}
|
||||
|
||||
// firstN — Python s[:n] (codepoints).
|
||||
func firstN(s string, n int) string {
|
||||
if utf8.RuneCountInString(s) <= n {
|
||||
return s
|
||||
}
|
||||
return string([]rune(s)[:n])
|
||||
}
|
||||
|
||||
// lastN — Python s[-n:].
|
||||
func lastN(s string, n int) string {
|
||||
r := []rune(s)
|
||||
if len(r) <= n {
|
||||
return s
|
||||
}
|
||||
return string(r[len(r)-n:])
|
||||
}
|
||||
|
||||
// expandUser — pathlib.Path.expanduser for the token-file path.
|
||||
func expandUser(p string) string {
|
||||
if p == "~" || strings.HasPrefix(p, "~/") {
|
||||
if home, err := os.UserHomeDir(); err == nil {
|
||||
return filepath.Join(home, strings.TrimPrefix(p, "~/"))
|
||||
}
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
// main — forge.py is a library module (the §18L boundary); the binary
|
||||
// surfaces the boundary contract for smoke checks.
|
||||
func main() {
|
||||
fmt.Println("forge: T18 §18L minimum stable forge abstraction boundary (GO port)")
|
||||
f := DefaultForge()
|
||||
fmt.Printf("default forge: repo=%s server-attr=%q\n", f.Repo, f.Server)
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package main
|
||||
|
||||
// Phase B language-advantage pass — dichotomy wave. Go-side wins the
|
||||
// Python oracle can't express:
|
||||
//
|
||||
// 1. ForgeSnapshot — commit status and CI runs are two independent HTTP
|
||||
// round-trips the oracle always performs serially; the GO port fans
|
||||
// them out across goroutines (order-insensitive reassembly, both
|
||||
// results preserved even when one side fails).
|
||||
// 2. Compile-time safety: seam interfaces (TokenSource, CommandRunner,
|
||||
// HTTPTransport) and the ForgeAdapter protocol are satisfaction-asserted
|
||||
// at build time — drift breaks the compile, not a runtime cast.
|
||||
|
||||
import "sync"
|
||||
|
||||
// ForgeSnapshot — the CI verdict surface for one SHA: the combined status
|
||||
// plus the workflow runs that belong to it.
|
||||
type ForgeSnapshot struct {
|
||||
Status any // commit_status state (str / JSON value)
|
||||
Runs []map[string]any // workflow_runs filtered to the SHA
|
||||
StatusErr error // ForgeUnavailable when the status call failed
|
||||
RunsErr error // ForgeUnavailable when the runs call failed
|
||||
}
|
||||
|
||||
// SnapshotForge — the fan-out interface (satisfied by *GiteaForge).
|
||||
type SnapshotForge interface {
|
||||
Snapshot(repo string, sha string) ForgeSnapshot
|
||||
}
|
||||
|
||||
// Snapshot — CommitStatus and CiRunsForSha race concurrently and reassemble
|
||||
// when both settle. Each half keeps its own error: a failed status call
|
||||
// never masks the runs (and vice versa) — the oracle's serial version could
|
||||
// only surface the first failure.
|
||||
func (f *GiteaForge) Snapshot(repo string, sha string) ForgeSnapshot {
|
||||
var (
|
||||
wg sync.WaitGroup
|
||||
snap ForgeSnapshot
|
||||
)
|
||||
wg.Add(2)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
snap.Status, snap.StatusErr = f.CommitStatus(repo, sha)
|
||||
}()
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
snap.Runs, snap.RunsErr = f.CiRunsForSha(repo, sha)
|
||||
}()
|
||||
wg.Wait()
|
||||
return snap
|
||||
}
|
||||
|
||||
// compile-time assertions: the seams keep their shapes.
|
||||
var (
|
||||
_ TokenSource = giteaLadder{}
|
||||
_ SnapshotForge = (*GiteaForge)(nil)
|
||||
_ ForgeAdapter = (*GiteaForge)(nil)
|
||||
)
|
||||
@@ -0,0 +1,120 @@
|
||||
package main
|
||||
|
||||
// Phase B battery — pins the language-advantage guarantees: the Snapshot
|
||||
// fan-out returns byte-identical halves to the serial oracle calls and is
|
||||
// race-clean under -race; the seam interfaces stay satisfaction-asserted.
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func jsonMarshal(v any) ([]byte, error) { return json.Marshal(v) }
|
||||
|
||||
// TestSnapshotHalves — drive the fan-out through the same frozen truth
|
||||
// bodies the battery pins, assert half-parity and race cleanliness.
|
||||
func TestSnapshotHalves(t *testing.T) {
|
||||
doc := loadTruth(t)
|
||||
statusRows := map[string]TruthRow{}
|
||||
for i := range doc.Rows {
|
||||
row := &doc.Rows[i]
|
||||
if row.Op == "commit_status" {
|
||||
setup := parseNum(t, row.Setup)
|
||||
sha := strOrEmpty(setup["sha"])
|
||||
if _, dup := statusRows[sha]; !dup {
|
||||
statusRows[sha] = *row
|
||||
}
|
||||
}
|
||||
}
|
||||
replayed := 0
|
||||
for i := range doc.Rows {
|
||||
row := &doc.Rows[i]
|
||||
if row.Op != "ci_runs" {
|
||||
continue
|
||||
}
|
||||
setup := parseNum(t, row.Setup)
|
||||
if setup["payload"] == nil {
|
||||
continue
|
||||
}
|
||||
sha := strOrEmpty(setup["sha"])
|
||||
sRow, ok := statusRows[sha]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
replayed++
|
||||
envGuard(t)
|
||||
sSetup := parseNum(t, sRow.Setup)
|
||||
payload, err := json.Marshal(setup["payload"])
|
||||
if err != nil {
|
||||
t.Fatalf("marshal payload: %v", err)
|
||||
}
|
||||
f := NewGiteaForge("", DefaultRepo)
|
||||
f.tokens = stubTokens{has: true, tok: "t0k3n"}
|
||||
tr := &recordingTransport{body: payload}
|
||||
f.transport = tr.do
|
||||
setEnv(t, map[string]any{"GITEA_SERVER": setup["env_server"]})
|
||||
|
||||
snap := f.Snapshot(strOrEmpty(setup["repo"]), sha)
|
||||
|
||||
// serial halves from the same frozen rows
|
||||
sState, sErr := f.CommitStatus(strOrEmpty(sSetup["repo"]), sha)
|
||||
sRuns, rErr := f.CiRunsForSha(strOrEmpty(setup["repo"]), sha)
|
||||
if sErr != nil || rErr != nil {
|
||||
t.Fatalf("serial halves unexpectedly failed: %v %v", sErr, rErr)
|
||||
}
|
||||
if snap.StatusErr != nil {
|
||||
t.Fatalf("snapshot status half failed: %v", snap.StatusErr)
|
||||
}
|
||||
if snap.RunsErr != nil {
|
||||
t.Fatalf("snapshot runs half failed: %v", snap.RunsErr)
|
||||
}
|
||||
if !equalAny(snap.Status, sState) {
|
||||
t.Fatalf("snapshot status half diverges: %#v vs %#v", snap.Status, sState)
|
||||
}
|
||||
if len(snap.Runs) != len(sRuns) {
|
||||
t.Fatalf("snapshot runs half diverges: %d vs %d runs", len(snap.Runs), len(sRuns))
|
||||
}
|
||||
}
|
||||
if replayed == 0 {
|
||||
t.Fatal("no snapshot replay rows — battery/truth drift")
|
||||
}
|
||||
}
|
||||
|
||||
// equalAny — deep equality over decoded JSON values.
|
||||
func equalAny(a, b any) bool {
|
||||
ab, err1 := json.Marshal(a)
|
||||
bb, err2 := json.Marshal(b)
|
||||
if err1 != nil || err2 != nil {
|
||||
return false
|
||||
}
|
||||
return string(ab) == string(bb)
|
||||
}
|
||||
|
||||
// TestSnapshotErrorHalves — a failing status call must not mask the runs
|
||||
// (the language-advantage over the oracle's serial first-error semantics).
|
||||
func TestSnapshotErrorHalves(t *testing.T) {
|
||||
f := NewGiteaForge("", DefaultRepo)
|
||||
f.tokens = stubTokens{has: true, tok: "t0k3n"}
|
||||
f.transport = func(req HTTPRequest) ([]byte, error) {
|
||||
if strings.Contains(req.URL, "/status") {
|
||||
return nil, &ForgeUnavailable{"status unreachable: boom"}
|
||||
}
|
||||
return []byte(`{"workflow_runs": [{"head_sha": "abc"}]}`), nil
|
||||
}
|
||||
snap := f.Snapshot("o/r", "abc")
|
||||
if snap.StatusErr == nil {
|
||||
t.Fatal("status half should have failed")
|
||||
}
|
||||
if len(snap.Runs) != 1 {
|
||||
t.Fatalf("runs half must survive: got %d", len(snap.Runs))
|
||||
}
|
||||
}
|
||||
|
||||
// TestSeamInterfacesSatisfied — compile-time assertions as a test.
|
||||
func TestSeamInterfacesSatisfied(t *testing.T) {
|
||||
var _ TokenSource = stubTokens{}
|
||||
var _ TokenSource = giteaLadder{}
|
||||
var _ ForgeAdapter = (*GiteaForge)(nil)
|
||||
var _ SnapshotForge = (*GiteaForge)(nil)
|
||||
}
|
||||
@@ -0,0 +1,778 @@
|
||||
{
|
||||
"frozen_ts": "2026-10-12T00:00:00+00:00",
|
||||
"generator": "scripts/gen_forge_truth.py — live oracle src/rune/forge.py",
|
||||
"rows": [
|
||||
{
|
||||
"name": "default_forge:working-adapter",
|
||||
"op": "default_forge",
|
||||
"setup": {},
|
||||
"want": {
|
||||
"server": null,
|
||||
"repo": "conductor/rune"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "_env:env:inherits-and-defaults",
|
||||
"op": "env",
|
||||
"setup": {
|
||||
"server": null,
|
||||
"repo": null,
|
||||
"environ": {
|
||||
"RUNE_SAMPLE": ""
|
||||
}
|
||||
},
|
||||
"want": {
|
||||
"GITEA_SERVER": null,
|
||||
"GITEA_REPO": "conductor/rune",
|
||||
"RUNE_SAMPLE": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "_env:env:server-override",
|
||||
"op": "env",
|
||||
"setup": {
|
||||
"server": "http://local:3000",
|
||||
"repo": null,
|
||||
"environ": {
|
||||
"RUNE_SAMPLE": ""
|
||||
}
|
||||
},
|
||||
"want": {
|
||||
"GITEA_SERVER": "http://local:3000",
|
||||
"GITEA_REPO": "conductor/rune",
|
||||
"RUNE_SAMPLE": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "_env:env:repo-explicit",
|
||||
"op": "env",
|
||||
"setup": {
|
||||
"server": null,
|
||||
"repo": "other/repo",
|
||||
"environ": {
|
||||
"RUNE_SAMPLE": ""
|
||||
}
|
||||
},
|
||||
"want": {
|
||||
"GITEA_SERVER": null,
|
||||
"GITEA_REPO": "other/repo",
|
||||
"RUNE_SAMPLE": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "_env:env:env-server-kept-when-no-attr",
|
||||
"op": "env",
|
||||
"setup": {
|
||||
"server": null,
|
||||
"repo": null,
|
||||
"environ": {
|
||||
"GITEA_SERVER": "http://env-server",
|
||||
"RUNE_SAMPLE": ""
|
||||
}
|
||||
},
|
||||
"want": {
|
||||
"GITEA_SERVER": "http://env-server",
|
||||
"GITEA_REPO": "conductor/rune",
|
||||
"RUNE_SAMPLE": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "_env:env:env-server-overridden-by-attr",
|
||||
"op": "env",
|
||||
"setup": {
|
||||
"server": "http://attr",
|
||||
"repo": null,
|
||||
"environ": {
|
||||
"GITEA_SERVER": "http://env-server",
|
||||
"RUNE_SAMPLE": ""
|
||||
}
|
||||
},
|
||||
"want": {
|
||||
"GITEA_SERVER": "http://attr",
|
||||
"GITEA_REPO": "conductor/rune",
|
||||
"RUNE_SAMPLE": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "_env:env:env-repo-not-clobbered",
|
||||
"op": "env",
|
||||
"setup": {
|
||||
"server": null,
|
||||
"repo": null,
|
||||
"environ": {
|
||||
"GITEA_REPO": "env/repo",
|
||||
"RUNE_SAMPLE": ""
|
||||
}
|
||||
},
|
||||
"want": {
|
||||
"GITEA_SERVER": null,
|
||||
"GITEA_REPO": "env/repo",
|
||||
"RUNE_SAMPLE": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "_env:env:inherits-sample-var",
|
||||
"op": "env",
|
||||
"setup": {
|
||||
"server": null,
|
||||
"repo": null,
|
||||
"environ": {
|
||||
"RUNE_SAMPLE": "1"
|
||||
}
|
||||
},
|
||||
"want": {
|
||||
"GITEA_SERVER": null,
|
||||
"GITEA_REPO": "conductor/rune",
|
||||
"RUNE_SAMPLE": "1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "push:push:ok",
|
||||
"op": "push",
|
||||
"setup": {
|
||||
"repo": "ignored/repo",
|
||||
"branch": "feature-x",
|
||||
"rc": 0,
|
||||
"stderr": ""
|
||||
},
|
||||
"want": {
|
||||
"result": {
|
||||
"pushed": true,
|
||||
"branch": "feature-x"
|
||||
},
|
||||
"error": null,
|
||||
"calls": [
|
||||
{
|
||||
"argv": [
|
||||
"git",
|
||||
"push",
|
||||
"gitea",
|
||||
"master:feature-x"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "push:push:fail-short-stderr",
|
||||
"op": "push",
|
||||
"setup": {
|
||||
"repo": "ignored/repo",
|
||||
"branch": "main",
|
||||
"rc": 1,
|
||||
"stderr": "rejected non-fast-forward"
|
||||
},
|
||||
"want": {
|
||||
"result": null,
|
||||
"error": "push failed: rejected non-fast-forward",
|
||||
"calls": [
|
||||
{
|
||||
"argv": [
|
||||
"git",
|
||||
"push",
|
||||
"gitea",
|
||||
"master:main"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "push:push:fail-long-stderr-truncates-200",
|
||||
"op": "push",
|
||||
"setup": {
|
||||
"repo": "ignored/repo",
|
||||
"branch": "dev",
|
||||
"rc": 128,
|
||||
"stderr": "eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"
|
||||
},
|
||||
"want": {
|
||||
"result": null,
|
||||
"error": "push failed: eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee",
|
||||
"calls": [
|
||||
{
|
||||
"argv": [
|
||||
"git",
|
||||
"push",
|
||||
"gitea",
|
||||
"master:dev"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "push:push:fail-empty-stderr",
|
||||
"op": "push",
|
||||
"setup": {
|
||||
"repo": "ignored/repo",
|
||||
"branch": "release",
|
||||
"rc": 2,
|
||||
"stderr": ""
|
||||
},
|
||||
"want": {
|
||||
"result": null,
|
||||
"error": "push failed: ",
|
||||
"calls": [
|
||||
{
|
||||
"argv": [
|
||||
"git",
|
||||
"push",
|
||||
"gitea",
|
||||
"master:release"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "gitea_auth:env-token-rung",
|
||||
"op": "gitea_auth",
|
||||
"setup": {
|
||||
"environ": {
|
||||
"GITEA_TOKEN": " envtoken \n"
|
||||
}
|
||||
},
|
||||
"want": {
|
||||
"has_token": true,
|
||||
"get_token": "envtoken"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "gitea_auth:token-file-0600",
|
||||
"op": "gitea_auth",
|
||||
"setup": {
|
||||
"environ": {
|
||||
"GITEA_TOKEN_FILE": "<fixture token0600>"
|
||||
}
|
||||
},
|
||||
"want": {
|
||||
"has_token": true,
|
||||
"get_token": "filetoken123"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "gitea_auth:token-file-0644-rejected",
|
||||
"op": "gitea_auth",
|
||||
"setup": {
|
||||
"environ": {
|
||||
"GITEA_TOKEN_FILE": "<fixture token0644>"
|
||||
}
|
||||
},
|
||||
"want": {
|
||||
"has_token": false,
|
||||
"get_token": null,
|
||||
"error": "Gitea token file must be mode 0600: <FIXTURES>/token0644"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "gitea_auth:no-credential",
|
||||
"op": "gitea_auth",
|
||||
"setup": {
|
||||
"default_token_file": "<TMP>/missing-token"
|
||||
},
|
||||
"want": {
|
||||
"has_token": false,
|
||||
"get_token": null,
|
||||
"error": "Gitea credentials unavailable: set GITEA_TOKEN, create a mode-0600 token file at <TMP>/missing-token, or configure a git credential helper for gitea.thingscouldgetdicey.online"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "commit_status:commit_status:no-token",
|
||||
"op": "commit_status",
|
||||
"setup": {
|
||||
"server": null,
|
||||
"env_server": null,
|
||||
"repo": "conductor/rune",
|
||||
"sha": "abc",
|
||||
"has_token": false,
|
||||
"body": null,
|
||||
"http_err": null
|
||||
},
|
||||
"want": {
|
||||
"state": null,
|
||||
"error": "no forge credentials (gitea token)",
|
||||
"request": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "commit_status:commit_status:success",
|
||||
"op": "commit_status",
|
||||
"setup": {
|
||||
"server": null,
|
||||
"env_server": null,
|
||||
"repo": "conductor/rune",
|
||||
"sha": "abc",
|
||||
"has_token": true,
|
||||
"body": "{\"state\": \"success\"}",
|
||||
"http_err": null
|
||||
},
|
||||
"want": {
|
||||
"state": "success",
|
||||
"error": null,
|
||||
"request": {
|
||||
"method": "GET",
|
||||
"url": "https://gitea.thingscouldgetdicey.online/api/v1/repos/conductor/rune/commits/abc/status",
|
||||
"headers": {
|
||||
"authorization": "token t0k3n"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "commit_status:commit_status:state-missing-unknown",
|
||||
"op": "commit_status",
|
||||
"setup": {
|
||||
"server": null,
|
||||
"env_server": null,
|
||||
"repo": "o/r",
|
||||
"sha": "def",
|
||||
"has_token": true,
|
||||
"body": "{\"statuses\": []}",
|
||||
"http_err": null
|
||||
},
|
||||
"want": {
|
||||
"state": "unknown",
|
||||
"error": null,
|
||||
"request": {
|
||||
"method": "GET",
|
||||
"url": "https://gitea.thingscouldgetdicey.online/api/v1/repos/o/r/commits/def/status",
|
||||
"headers": {
|
||||
"authorization": "token t0k3n"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "commit_status:commit_status:server-attr-wins",
|
||||
"op": "commit_status",
|
||||
"setup": {
|
||||
"server": "http://attr:3000",
|
||||
"env_server": "http://env:3000",
|
||||
"repo": "o/r",
|
||||
"sha": "ghi",
|
||||
"has_token": true,
|
||||
"body": "{\"state\": \"pending\"}",
|
||||
"http_err": null
|
||||
},
|
||||
"want": {
|
||||
"state": "pending",
|
||||
"error": null,
|
||||
"request": {
|
||||
"method": "GET",
|
||||
"url": "http://attr:3000/api/v1/repos/o/r/commits/ghi/status",
|
||||
"headers": {
|
||||
"authorization": "token t0k3n"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "commit_status:commit_status:server-from-env",
|
||||
"op": "commit_status",
|
||||
"setup": {
|
||||
"server": null,
|
||||
"env_server": "http://env:3000",
|
||||
"repo": "o/r",
|
||||
"sha": "ghi",
|
||||
"has_token": true,
|
||||
"body": "{\"state\": \"failure\"}",
|
||||
"http_err": null
|
||||
},
|
||||
"want": {
|
||||
"state": "failure",
|
||||
"error": null,
|
||||
"request": {
|
||||
"method": "GET",
|
||||
"url": "http://env:3000/api/v1/repos/o/r/commits/ghi/status",
|
||||
"headers": {
|
||||
"authorization": "token t0k3n"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "commit_status:commit_status:http-error",
|
||||
"op": "commit_status",
|
||||
"setup": {
|
||||
"server": null,
|
||||
"env_server": null,
|
||||
"repo": "o/r",
|
||||
"sha": "jkl",
|
||||
"has_token": true,
|
||||
"body": null,
|
||||
"http_err": "HTTP Error 404: Not Found"
|
||||
},
|
||||
"want": {
|
||||
"state": null,
|
||||
"error": "status unreachable: HTTP Error 404: Not Found",
|
||||
"request": {
|
||||
"method": "GET",
|
||||
"url": "https://gitea.thingscouldgetdicey.online/api/v1/repos/o/r/commits/jkl/status",
|
||||
"headers": {
|
||||
"authorization": "token t0k3n"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "commit_status:commit_status:network-error",
|
||||
"op": "commit_status",
|
||||
"setup": {
|
||||
"server": null,
|
||||
"env_server": null,
|
||||
"repo": "o/r",
|
||||
"sha": "jkl",
|
||||
"has_token": true,
|
||||
"body": null,
|
||||
"http_err": "conn refused"
|
||||
},
|
||||
"want": {
|
||||
"state": null,
|
||||
"error": "status unreachable: conn refused",
|
||||
"request": {
|
||||
"method": "GET",
|
||||
"url": "https://gitea.thingscouldgetdicey.online/api/v1/repos/o/r/commits/jkl/status",
|
||||
"headers": {
|
||||
"authorization": "token t0k3n"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "ci_runs:ci_runs:no-token",
|
||||
"op": "ci_runs",
|
||||
"setup": {
|
||||
"server": null,
|
||||
"repo": "o/r",
|
||||
"sha": "s",
|
||||
"payload": null,
|
||||
"err": null,
|
||||
"has_token": false
|
||||
},
|
||||
"want": {
|
||||
"runs": null,
|
||||
"error": "no forge credentials (gitea token)",
|
||||
"request": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "ci_runs:ci_runs:filter-by-head-sha",
|
||||
"op": "ci_runs",
|
||||
"setup": {
|
||||
"server": null,
|
||||
"repo": "o/r",
|
||||
"sha": "abc",
|
||||
"payload": {
|
||||
"workflow_runs": [
|
||||
{
|
||||
"id": 1,
|
||||
"head_sha": "abc"
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"head_sha": null
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"head_sha": "other"
|
||||
},
|
||||
{
|
||||
"id": 4
|
||||
},
|
||||
{
|
||||
"id": 5,
|
||||
"head_sha": "abc"
|
||||
}
|
||||
]
|
||||
},
|
||||
"err": null,
|
||||
"has_token": true
|
||||
},
|
||||
"want": {
|
||||
"runs": [
|
||||
{
|
||||
"id": 1,
|
||||
"head_sha": "abc"
|
||||
},
|
||||
{
|
||||
"id": 5,
|
||||
"head_sha": "abc"
|
||||
}
|
||||
],
|
||||
"error": null,
|
||||
"request": {
|
||||
"method": "GET",
|
||||
"url": "https://gitea.thingscouldgetdicey.online/api/v1/repos/o/r/actions/runs?limit=50",
|
||||
"headers": {
|
||||
"authorization": "token t0k3n"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "ci_runs:ci_runs:none-matching",
|
||||
"op": "ci_runs",
|
||||
"setup": {
|
||||
"server": null,
|
||||
"repo": "o/r",
|
||||
"sha": "zzz",
|
||||
"payload": {
|
||||
"workflow_runs": [
|
||||
{
|
||||
"id": 1,
|
||||
"head_sha": "abc"
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"head_sha": null
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"head_sha": "other"
|
||||
},
|
||||
{
|
||||
"id": 4
|
||||
},
|
||||
{
|
||||
"id": 5,
|
||||
"head_sha": "abc"
|
||||
}
|
||||
]
|
||||
},
|
||||
"err": null,
|
||||
"has_token": true
|
||||
},
|
||||
"want": {
|
||||
"runs": [],
|
||||
"error": null,
|
||||
"request": {
|
||||
"method": "GET",
|
||||
"url": "https://gitea.thingscouldgetdicey.online/api/v1/repos/o/r/actions/runs?limit=50",
|
||||
"headers": {
|
||||
"authorization": "token t0k3n"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "ci_runs:ci_runs:absent-key",
|
||||
"op": "ci_runs",
|
||||
"setup": {
|
||||
"server": null,
|
||||
"repo": "o/r",
|
||||
"sha": "s",
|
||||
"payload": {
|
||||
"total_count": 0
|
||||
},
|
||||
"err": null,
|
||||
"has_token": true
|
||||
},
|
||||
"want": {
|
||||
"runs": [],
|
||||
"error": null,
|
||||
"request": {
|
||||
"method": "GET",
|
||||
"url": "https://gitea.thingscouldgetdicey.online/api/v1/repos/o/r/actions/runs?limit=50",
|
||||
"headers": {
|
||||
"authorization": "token t0k3n"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "ci_runs:ci_runs:empty-sha-matches-absent",
|
||||
"op": "ci_runs",
|
||||
"setup": {
|
||||
"server": null,
|
||||
"repo": "o/r",
|
||||
"sha": "",
|
||||
"payload": {
|
||||
"workflow_runs": [
|
||||
{
|
||||
"id": 1,
|
||||
"head_sha": "abc"
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"head_sha": null
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"head_sha": "other"
|
||||
},
|
||||
{
|
||||
"id": 4
|
||||
},
|
||||
{
|
||||
"id": 5,
|
||||
"head_sha": "abc"
|
||||
}
|
||||
]
|
||||
},
|
||||
"err": null,
|
||||
"has_token": true
|
||||
},
|
||||
"want": {
|
||||
"runs": [
|
||||
{
|
||||
"id": 4
|
||||
}
|
||||
],
|
||||
"error": null,
|
||||
"request": {
|
||||
"method": "GET",
|
||||
"url": "https://gitea.thingscouldgetdicey.online/api/v1/repos/o/r/actions/runs?limit=50",
|
||||
"headers": {
|
||||
"authorization": "token t0k3n"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "ci_runs:ci_runs:unreachable",
|
||||
"op": "ci_runs",
|
||||
"setup": {
|
||||
"server": null,
|
||||
"repo": "o/r",
|
||||
"sha": "s",
|
||||
"payload": null,
|
||||
"err": "runs down",
|
||||
"has_token": true
|
||||
},
|
||||
"want": {
|
||||
"runs": null,
|
||||
"error": "runs unreachable: runs down",
|
||||
"request": {
|
||||
"method": "GET",
|
||||
"url": "https://gitea.thingscouldgetdicey.online/api/v1/repos/o/r/actions/runs?limit=50",
|
||||
"headers": {
|
||||
"authorization": "token t0k3n"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "release:release:no-token",
|
||||
"op": "release",
|
||||
"setup": {
|
||||
"has_token": false,
|
||||
"script_present": true,
|
||||
"rc": 0,
|
||||
"stderr": "",
|
||||
"stdout": "",
|
||||
"tag": "v9.9.9",
|
||||
"cwd": "<root>"
|
||||
},
|
||||
"want": {
|
||||
"result": null,
|
||||
"error": "no forge credentials (gitea token)",
|
||||
"argv": null,
|
||||
"python_bin": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "release:release:published",
|
||||
"op": "release",
|
||||
"setup": {
|
||||
"has_token": true,
|
||||
"script_present": true,
|
||||
"rc": 0,
|
||||
"stderr": "",
|
||||
"stdout": "",
|
||||
"tag": "v1.2.3",
|
||||
"cwd": "<root>"
|
||||
},
|
||||
"want": {
|
||||
"result": {
|
||||
"published": true,
|
||||
"tag": "v1.2.3"
|
||||
},
|
||||
"error": null,
|
||||
"argv": [
|
||||
"scripts/create_release.py",
|
||||
"--tag",
|
||||
"v1.2.3",
|
||||
"--title",
|
||||
"T",
|
||||
"--body",
|
||||
"B"
|
||||
],
|
||||
"python_bin": "/mnt/workdrive/rune-wave-au/.venv/bin/python3"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "release:release:script-fails-stderr",
|
||||
"op": "release",
|
||||
"setup": {
|
||||
"has_token": true,
|
||||
"script_present": true,
|
||||
"rc": 3,
|
||||
"stderr": "tag exists already",
|
||||
"stdout": "",
|
||||
"tag": "v0.0.1",
|
||||
"cwd": "<root>"
|
||||
},
|
||||
"want": {
|
||||
"result": null,
|
||||
"error": "release publication failed rc=3: tag exists already",
|
||||
"argv": [
|
||||
"scripts/create_release.py",
|
||||
"--tag",
|
||||
"v0.0.1",
|
||||
"--title",
|
||||
"T",
|
||||
"--body",
|
||||
"B"
|
||||
],
|
||||
"python_bin": "/mnt/workdrive/rune-wave-au/.venv/bin/python3"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "release:release:script-fails-falls-back-stdout",
|
||||
"op": "release",
|
||||
"setup": {
|
||||
"has_token": true,
|
||||
"script_present": true,
|
||||
"rc": 4,
|
||||
"stderr": "",
|
||||
"stdout": "oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo",
|
||||
"tag": "v0.0.2",
|
||||
"cwd": "<root>"
|
||||
},
|
||||
"want": {
|
||||
"result": null,
|
||||
"error": "release publication failed rc=4: oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo",
|
||||
"argv": [
|
||||
"scripts/create_release.py",
|
||||
"--tag",
|
||||
"v0.0.2",
|
||||
"--title",
|
||||
"T",
|
||||
"--body",
|
||||
"B"
|
||||
],
|
||||
"python_bin": "/mnt/workdrive/rune-wave-au/.venv/bin/python3"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "release:release:script-missing",
|
||||
"op": "release",
|
||||
"setup": {
|
||||
"has_token": true,
|
||||
"script_present": false,
|
||||
"rc": 0,
|
||||
"stderr": "",
|
||||
"stdout": "",
|
||||
"tag": "v0.0.3",
|
||||
"cwd": "<tmp>"
|
||||
},
|
||||
"want": {
|
||||
"result": null,
|
||||
"error": "canonical release publisher not found",
|
||||
"argv": null,
|
||||
"python_bin": null
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,510 @@
|
||||
package main
|
||||
|
||||
// Battery — the HARD parity gate replaying the frozen oracle truth
|
||||
// (truth.json, generated against the LIVE Python oracle src/rune/forge.py
|
||||
// with subprocess/urllib/gitea_auth seams captured) VERBATIM. IO seams are
|
||||
// faked identically on the Go side; every captured call (argv, URL,
|
||||
// headers) and every error message byte-compares to the oracle.
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type TruthDoc struct {
|
||||
FrozenTS string `json:"frozen_ts"`
|
||||
Generator string `json:"generator"`
|
||||
Rows []TruthRow `json:"rows"`
|
||||
}
|
||||
|
||||
type TruthRow struct {
|
||||
Name string `json:"name"`
|
||||
Op string `json:"op"`
|
||||
Setup json.RawMessage `json:"setup"`
|
||||
Want json.RawMessage `json:"want"`
|
||||
}
|
||||
|
||||
func loadTruth(t *testing.T) *TruthDoc {
|
||||
t.Helper()
|
||||
raw, err := os.ReadFile("truth.json")
|
||||
if err != nil {
|
||||
t.Fatalf("truth.json missing (frozen oracle battery): %v", err)
|
||||
}
|
||||
var doc TruthDoc
|
||||
if err := json.Unmarshal(raw, &doc); err != nil {
|
||||
t.Fatalf("parse truth.json: %v", err)
|
||||
}
|
||||
if len(doc.Rows) == 0 {
|
||||
t.Fatal("truth.json contained no rows")
|
||||
}
|
||||
seen := map[string]bool{}
|
||||
for _, r := range doc.Rows {
|
||||
if seen[r.Name] {
|
||||
t.Fatalf("duplicate frozen row %q", r.Name)
|
||||
}
|
||||
seen[r.Name] = true
|
||||
}
|
||||
return &doc
|
||||
}
|
||||
|
||||
// parseNum — decode with json.Number preservation (Python int/float str
|
||||
// rendering parity).
|
||||
func parseNum(t *testing.T, raw []byte) map[string]any {
|
||||
t.Helper()
|
||||
dec := json.NewDecoder(strings.NewReader(string(raw)))
|
||||
dec.UseNumber()
|
||||
var v map[string]any
|
||||
if err := dec.Decode(&v); err != nil {
|
||||
t.Fatalf("decode truth row: %v", err)
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
// envGuard — snapshot and clear every forge-relevant env var; restores on
|
||||
// test cleanup.
|
||||
func envGuard(t *testing.T) {
|
||||
t.Helper()
|
||||
keys := []string{"GITEA_SERVER", "GITEA_REPO", "GITEA_TOKEN",
|
||||
"GITEA_TOKEN_FILE", "RUNE_SAMPLE"}
|
||||
saved := map[string]string{}
|
||||
for _, k := range keys {
|
||||
if v, ok := os.LookupEnv(k); ok {
|
||||
saved[k] = v
|
||||
}
|
||||
os.Unsetenv(k)
|
||||
}
|
||||
t.Cleanup(func() {
|
||||
for _, k := range keys {
|
||||
os.Unsetenv(k)
|
||||
}
|
||||
for k, v := range saved {
|
||||
os.Setenv(k, v)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func setEnv(t *testing.T, m map[string]any) {
|
||||
t.Helper()
|
||||
for k, v := range m {
|
||||
if v == nil {
|
||||
os.Unsetenv(k)
|
||||
continue
|
||||
}
|
||||
if err := os.Setenv(k, fmt.Sprintf("%v", v)); err != nil {
|
||||
t.Fatalf("setenv %s: %v", k, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func fileExists(p string) bool {
|
||||
_, err := os.Stat(p)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
func numInt(v any) int {
|
||||
n, _ := v.(json.Number).Float64()
|
||||
return int(n)
|
||||
}
|
||||
|
||||
// recordingRunner — fake CommandRunner capturing (name, args, stdin).
|
||||
type recordingRunner struct {
|
||||
calls []map[string]any
|
||||
res RunResult
|
||||
}
|
||||
|
||||
func (r *recordingRunner) run(name string, args []string, stdin string) RunResult {
|
||||
argv := []any{name}
|
||||
for _, a := range args {
|
||||
argv = append(argv, a)
|
||||
}
|
||||
r.calls = append(r.calls, map[string]any{"argv": argv})
|
||||
return r.res
|
||||
}
|
||||
|
||||
// stubTokens — the generator's auth_stub(has=..., token="t0k3n").
|
||||
type stubTokens struct {
|
||||
has bool
|
||||
tok string
|
||||
}
|
||||
|
||||
func (s stubTokens) HasToken() bool { return s.has }
|
||||
func (s stubTokens) GetToken() (string, error) { return s.tok, nil }
|
||||
|
||||
// recordingTransport — fake HTTPTransport capturing requests (mutexed:
|
||||
// the Phase B Snapshot fan-out drives it from two goroutines).
|
||||
type recordingTransport struct {
|
||||
mu sync.Mutex
|
||||
calls []map[string]any
|
||||
body []byte
|
||||
err error
|
||||
}
|
||||
|
||||
func (tr *recordingTransport) do(req HTTPRequest) ([]byte, error) {
|
||||
tr.mu.Lock()
|
||||
defer tr.mu.Unlock()
|
||||
headers := map[string]any{}
|
||||
for k, v := range req.Headers {
|
||||
headers[strings.ToLower(k)] = v
|
||||
}
|
||||
tr.calls = append(tr.calls, map[string]any{
|
||||
"method": req.Method,
|
||||
"url": req.URL,
|
||||
"headers": headers,
|
||||
})
|
||||
if tr.err != nil {
|
||||
return nil, tr.err
|
||||
}
|
||||
return tr.body, nil
|
||||
}
|
||||
|
||||
func strOrEmpty(v any) string {
|
||||
if v == nil {
|
||||
return ""
|
||||
}
|
||||
return v.(string)
|
||||
}
|
||||
|
||||
func errStr(err error) any {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
return err.Error()
|
||||
}
|
||||
|
||||
// runOne — replay one frozen row; returns "" on parity.
|
||||
func runOne(t *testing.T, row *TruthRow) string {
|
||||
t.Helper()
|
||||
setup := parseNum(t, row.Setup)
|
||||
want := parseNum(t, row.Want)
|
||||
envGuard(t)
|
||||
|
||||
switch row.Op {
|
||||
case "default_forge":
|
||||
f := DefaultForge()
|
||||
got := map[string]any{"server": nil, "repo": f.Repo}
|
||||
if f.Server != "" {
|
||||
got["server"] = f.Server
|
||||
}
|
||||
if !reflect.DeepEqual(got["server"], want["server"]) || got["repo"] != want["repo"] {
|
||||
return fmt.Sprintf("default_forge diverges: got %v want %v", got, want)
|
||||
}
|
||||
return ""
|
||||
|
||||
case "env":
|
||||
setEnv(t, setup["environ"].(map[string]any))
|
||||
var server, repo string
|
||||
if setup["server"] != nil {
|
||||
server = setup["server"].(string)
|
||||
}
|
||||
if setup["repo"] != nil {
|
||||
repo = setup["repo"].(string)
|
||||
} else {
|
||||
repo = DefaultRepo
|
||||
}
|
||||
built := NewGiteaForge(server, repo).env()
|
||||
for k, wv := range want {
|
||||
gv, ok := built[k]
|
||||
var g any
|
||||
if ok {
|
||||
g = gv
|
||||
}
|
||||
if !reflect.DeepEqual(g, wv) {
|
||||
return fmt.Sprintf("env[%s] diverges: got %v want %v", k, g, wv)
|
||||
}
|
||||
}
|
||||
return ""
|
||||
|
||||
case "push":
|
||||
runner := &recordingRunner{res: RunResult{
|
||||
RC: numInt(setup["rc"]),
|
||||
Stderr: strOrEmpty(setup["stderr"]),
|
||||
}}
|
||||
f := NewGiteaForge("", DefaultRepo)
|
||||
f.runner = runner.run
|
||||
got, err := f.Push(strOrEmpty(setup["repo"]), strOrEmpty(setup["branch"]))
|
||||
if mismatch(errStr(err), want["error"]) {
|
||||
return fmt.Sprintf("push error diverges: got %v want %v", errStr(err), want["error"])
|
||||
}
|
||||
if err == nil && !reflect.DeepEqual(got, want["result"]) {
|
||||
return fmt.Sprintf("push result diverges: got %v want %v", got, want["result"])
|
||||
}
|
||||
return compareCalls(runner.calls, want["calls"].([]any))
|
||||
|
||||
case "gitea_auth":
|
||||
fixtures, _ := filepath.Abs(".fixtures")
|
||||
// git stores only the exec bit — pin the token-file modes the
|
||||
// ladder's 0600 check depends on (the generator chmods likewise)
|
||||
if err := os.Chmod(filepath.Join(fixtures, "token0600"), 0o600); err != nil {
|
||||
t.Fatalf("chmod fixture: %v", err)
|
||||
}
|
||||
if err := os.Chmod(filepath.Join(fixtures, "token0644"), 0o644); err != nil {
|
||||
t.Fatalf("chmod fixture: %v", err)
|
||||
}
|
||||
if envRaw, ok := setup["environ"]; ok {
|
||||
for k, v := range envRaw.(map[string]any) {
|
||||
val := fmt.Sprintf("%v", v)
|
||||
val = strings.ReplaceAll(val, "<fixture token0600>", filepath.Join(fixtures, "token0600"))
|
||||
val = strings.ReplaceAll(val, "<fixture token0644>", filepath.Join(fixtures, "token0644"))
|
||||
if err := os.Setenv(k, val); err != nil {
|
||||
t.Fatalf("setenv: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
var tmpDir string
|
||||
if d, ok := setup["default_token_file"]; ok {
|
||||
tmpDir = t.TempDir()
|
||||
pinned := strings.ReplaceAll(d.(string), "<TMP>/", tmpDir+"/")
|
||||
orig := defaultTokenFilePath
|
||||
defaultTokenFilePath = func() string { return pinned }
|
||||
t.Cleanup(func() { defaultTokenFilePath = orig })
|
||||
}
|
||||
ladder := giteaLadder{runner: func(name string, args []string, stdin string) RunResult {
|
||||
// the pinned no-credential world: helper yields nothing
|
||||
return RunResult{Stdout: "", Stderr: "", RC: 0}
|
||||
}}
|
||||
has := ladder.HasToken()
|
||||
got := map[string]any{"has_token": has, "get_token": nil, "error": nil}
|
||||
tok, err := ladder.GetToken()
|
||||
if err == nil {
|
||||
got["get_token"] = tok
|
||||
} else {
|
||||
msg := err.Error()
|
||||
msg = strings.ReplaceAll(msg, fixtures+"/", "<FIXTURES>/")
|
||||
if tmpDir != "" {
|
||||
msg = strings.ReplaceAll(msg, tmpDir+"/", "<TMP>/")
|
||||
}
|
||||
got["error"] = msg
|
||||
}
|
||||
if !reflect.DeepEqual(got["has_token"], want["has_token"]) ||
|
||||
!reflect.DeepEqual(got["get_token"], want["get_token"]) ||
|
||||
!reflect.DeepEqual(got["error"], want["error"]) {
|
||||
return fmt.Sprintf("gitea_auth diverges:\n got %v\n want %v", got, want)
|
||||
}
|
||||
return ""
|
||||
|
||||
case "commit_status":
|
||||
body := []byte(nil)
|
||||
if setup["body"] != nil {
|
||||
body = []byte(setup["body"].(string))
|
||||
}
|
||||
var httpErr error
|
||||
if setup["http_err"] != nil {
|
||||
httpErr = fmt.Errorf("%s", setup["http_err"].(string))
|
||||
}
|
||||
tr := &recordingTransport{body: body, err: httpErr}
|
||||
f := NewGiteaForge(strOrEmpty(setup["server"]), DefaultRepo)
|
||||
f.tokens = stubTokens{has: setup["has_token"].(bool), tok: "t0k3n"}
|
||||
f.transport = tr.do
|
||||
setEnv(t, map[string]any{"GITEA_SERVER": setup["env_server"]})
|
||||
got, err := f.CommitStatus(strOrEmpty(setup["repo"]), strOrEmpty(setup["sha"]))
|
||||
if mismatch(errStr(err), want["error"]) {
|
||||
return fmt.Sprintf("commit_status error diverges: got %v want %v",
|
||||
errStr(err), want["error"])
|
||||
}
|
||||
if err == nil && !reflect.DeepEqual(got, want["state"]) {
|
||||
return fmt.Sprintf("commit_status state diverges: got %#v want %#v",
|
||||
got, want["state"])
|
||||
}
|
||||
return compareRequest(tr.calls, want["request"])
|
||||
|
||||
case "ci_runs":
|
||||
var body []byte
|
||||
if setup["payload"] != nil {
|
||||
raw, err := json.Marshal(setup["payload"])
|
||||
if err != nil {
|
||||
t.Fatalf("marshal payload: %v", err)
|
||||
}
|
||||
body = raw
|
||||
}
|
||||
var httpErr error
|
||||
if setup["err"] != nil {
|
||||
httpErr = fmt.Errorf("%s", setup["err"].(string))
|
||||
}
|
||||
tr := &recordingTransport{body: body, err: httpErr}
|
||||
f := NewGiteaForge(strOrEmpty(setup["server"]), DefaultRepo)
|
||||
f.tokens = stubTokens{has: setup["has_token"].(bool), tok: "t0k3n"}
|
||||
f.transport = tr.do
|
||||
got, err := f.CiRunsForSha(strOrEmpty(setup["repo"]), strOrEmpty(setup["sha"]))
|
||||
if mismatch(errStr(err), want["error"]) {
|
||||
return fmt.Sprintf("ci_runs error diverges: got %v want %v",
|
||||
errStr(err), want["error"])
|
||||
}
|
||||
if err == nil {
|
||||
conv := make([]any, len(got))
|
||||
for i, r := range got {
|
||||
conv[i] = r
|
||||
}
|
||||
if !reflect.DeepEqual(conv, want["runs"]) {
|
||||
return fmt.Sprintf("ci_runs diverges:\n got %#v\n want %#v",
|
||||
conv, want["runs"])
|
||||
}
|
||||
}
|
||||
return compareRequest(tr.calls, want["request"])
|
||||
|
||||
case "release":
|
||||
repoRoot, err := filepath.Abs("../..") // src/go/forge → repo root
|
||||
if !fileExists(filepath.Join(repoRoot, "scripts/create_release.py")) {
|
||||
repoRoot, _ = filepath.Abs("../../..")
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatalf("abs: %v", err)
|
||||
}
|
||||
if setup["script_present"].(bool) {
|
||||
t.Chdir(repoRoot)
|
||||
} else {
|
||||
t.Chdir(t.TempDir())
|
||||
}
|
||||
runner := &recordingRunner{res: RunResult{
|
||||
RC: numInt(setup["rc"]),
|
||||
Stdout: strOrEmpty(setup["stdout"]),
|
||||
Stderr: strOrEmpty(setup["stderr"]),
|
||||
}}
|
||||
f := NewGiteaForge("", DefaultRepo)
|
||||
f.tokens = stubTokens{has: setup["has_token"].(bool), tok: "t0k3n"}
|
||||
f.runner = runner.run
|
||||
got, err := f.CreateOrPatchRelease("o/r", strOrEmpty(setup["tag"]), "T", "B")
|
||||
if mismatch(errStr(err), want["error"]) {
|
||||
return fmt.Sprintf("release error diverges:\n got %v\n want %v",
|
||||
errStr(err), want["error"])
|
||||
}
|
||||
if err == nil && !reflect.DeepEqual(got, want["result"]) {
|
||||
return fmt.Sprintf("release result diverges: got %v want %v", got, want["result"])
|
||||
}
|
||||
if want["argv"] == nil {
|
||||
if len(runner.calls) != 0 {
|
||||
return "release: expected no subprocess call"
|
||||
}
|
||||
return ""
|
||||
}
|
||||
// the oracle records argv[1:] (sys.executable dropped); the Go
|
||||
// seam's pythonBin default is "python3"
|
||||
if len(runner.calls) != 1 {
|
||||
return fmt.Sprintf("release: got %d subprocess calls, want 1",
|
||||
len(runner.calls))
|
||||
}
|
||||
argv := runner.calls[0]["argv"].([]any)
|
||||
if argv[0] != "python3" {
|
||||
return fmt.Sprintf("release: pythonBin seam got %v, want python3", argv[0])
|
||||
}
|
||||
return compareCalls([]map[string]any{{"argv": argv[1:]}},
|
||||
[]any{map[string]any{"argv": want["argv"]}})
|
||||
}
|
||||
return "unknown op " + row.Op
|
||||
}
|
||||
|
||||
func mismatch(got, want any) bool {
|
||||
if got == nil && want == nil {
|
||||
return false
|
||||
}
|
||||
if got == nil || want == nil {
|
||||
return true
|
||||
}
|
||||
return got != want
|
||||
}
|
||||
|
||||
func compareCalls(got []map[string]any, want []any) string {
|
||||
if len(got) != len(want) {
|
||||
return fmt.Sprintf("call count diverges: got %d want %d", len(got), len(want))
|
||||
}
|
||||
for i := range want {
|
||||
w := want[i].(map[string]any)
|
||||
wargv := w["argv"].([]any)
|
||||
gargv := got[i]["argv"].([]any)
|
||||
if len(wargv) != len(gargv) {
|
||||
return fmt.Sprintf("call[%d] argv length diverges: got %v want %v", i, gargv, wargv)
|
||||
}
|
||||
for j := range wargv {
|
||||
if gargv[j] != wargv[j] {
|
||||
return fmt.Sprintf("call[%d].argv[%d] diverges: got %v want %v",
|
||||
i, j, gargv[j], wargv[j])
|
||||
}
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func compareRequest(got []map[string]any, want any) string {
|
||||
if want == nil {
|
||||
if len(got) != 0 {
|
||||
return "expected no HTTP request"
|
||||
}
|
||||
return ""
|
||||
}
|
||||
w := want.(map[string]any)
|
||||
if len(got) == 0 {
|
||||
return "expected an HTTP request, got none"
|
||||
}
|
||||
g := got[0]
|
||||
if g["method"] != w["method"] || g["url"] != w["url"] {
|
||||
return fmt.Sprintf("request diverges:\n got %s %s\n want %s %s",
|
||||
g["method"], g["url"], w["method"], w["url"])
|
||||
}
|
||||
gh := g["headers"].(map[string]any)
|
||||
wh := w["headers"].(map[string]any)
|
||||
if !reflect.DeepEqual(gh, wh) {
|
||||
return fmt.Sprintf("headers diverge:\n got %v\n want %v", gh, wh)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// TestTruthParity — replay EVERY frozen row verbatim.
|
||||
func TestTruthParity(t *testing.T) {
|
||||
doc := loadTruth(t)
|
||||
for i := range doc.Rows {
|
||||
row := &doc.Rows[i]
|
||||
t.Run(row.Name, func(t *testing.T) {
|
||||
if msg := runOne(t, row); msg != "" {
|
||||
t.Fatalf("DIVERGED %s: %s", row.Name, msg)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestBatteryCoversEveryPublicFunction — the frozen battery must exercise
|
||||
// every public surface: Push, CommitStatus, CiRunsForSha,
|
||||
// CreateOrPatchRelease, env, giteaAuth ladder, DefaultForge.
|
||||
func TestBatteryCoversEveryPublicFunction(t *testing.T) {
|
||||
doc := loadTruth(t)
|
||||
have := map[string]int{}
|
||||
for _, r := range doc.Rows {
|
||||
have[r.Op]++
|
||||
}
|
||||
for op, min := range map[string]int{
|
||||
"default_forge": 1, "env": 1, "push": 1, "gitea_auth": 1,
|
||||
"commit_status": 1, "ci_runs": 1, "release": 1,
|
||||
} {
|
||||
if have[op] < min {
|
||||
t.Errorf("frozen battery missing rows for op %s", op)
|
||||
}
|
||||
}
|
||||
// success AND failure branch per IO op
|
||||
for _, op := range []string{"push", "commit_status", "ci_runs", "release"} {
|
||||
ok, fail := 0, 0
|
||||
for _, r := range doc.Rows {
|
||||
if r.Op != op {
|
||||
continue
|
||||
}
|
||||
w := parseNum(t, r.Want)
|
||||
if w["error"] == nil {
|
||||
ok++
|
||||
} else {
|
||||
fail++
|
||||
}
|
||||
}
|
||||
if ok == 0 || fail == 0 {
|
||||
t.Errorf("op %s: battery lacks branch coverage (ok=%d fail=%d)", op, ok, fail)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestForgeAdapterSatisfied — compile-time assertion, re-asserted as a test.
|
||||
func TestForgeAdapterSatisfied(t *testing.T) {
|
||||
var _ ForgeAdapter = (*GiteaForge)(nil)
|
||||
var _ ForgeAdapter = DefaultForge()
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
fixture artifact for the file_exists verifier battery
|
||||
@@ -0,0 +1,3 @@
|
||||
module rune/verification_layer
|
||||
|
||||
go 1.22
|
||||
@@ -0,0 +1,628 @@
|
||||
// Package main — the FULL GO port of src/rune/verification_layer.py
|
||||
// (#2479 dichotomy wave, GO side verdict). Every public Python function has
|
||||
// a name-preserving counterpart: verify_exit_code, verify_unit_test,
|
||||
// verify_schema, verify_file_exists, verify_file_changed, verify_tool_receipt,
|
||||
// verify_constraint_check, verify (dispatch incl. the unknown-verifier path),
|
||||
// handle_hook, VerificationResult.to_dict and the PLUGIN manifest.
|
||||
//
|
||||
// The Python module delegates to the Rust evidence_provenance core
|
||||
// (src/rust/evidence_provenance/src/verification.rs) — this port carries
|
||||
// those exact semantics 1:1, including the Python str() rendering rules
|
||||
// (bool → "True"/"False", None → "None") for evidence_refs and the
|
||||
// to_dict() key order passed, verifier_type, evidence_refs, failures,
|
||||
// retryable, escalation_reason with byte-exact json.dumps rendering.
|
||||
//
|
||||
// Results are *JSONValue (insertion-ordered objects — Python dicts preserve
|
||||
// order and the pass-through failures rows render nested objects byte-for-
|
||||
// byte). The filesystem probe stays behind an injectable seam so the frozen
|
||||
// truth battery replays deterministically.
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"math"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// VerificationResult — the Python dataclass, field-for-field.
|
||||
type VerificationResult struct {
|
||||
Passed bool
|
||||
VerifierType string
|
||||
EvidenceRefs []string
|
||||
Failures []JSONValue
|
||||
Retryable bool
|
||||
EscalationReason *string // nil ↔ Python None
|
||||
}
|
||||
|
||||
// ToDict — Python VerificationResult.to_dict(): the ordered key set
|
||||
// passed, verifier_type, evidence_refs, failures, retryable, escalation_reason.
|
||||
func (r VerificationResult) ToDict() JSONValue {
|
||||
esc := JSONNull()
|
||||
if r.EscalationReason != nil {
|
||||
esc = JSONString(*r.EscalationReason)
|
||||
}
|
||||
return JSONObject([]KV{
|
||||
{"passed", JSONBool(r.Passed)},
|
||||
{"verifier_type", JSONString(r.VerifierType)},
|
||||
{"evidence_refs", jsonStringArr(r.EvidenceRefs)},
|
||||
{"failures", JSONArray(r.Failures)},
|
||||
{"retryable", JSONBool(r.Retryable)},
|
||||
{"escalation_reason", esc},
|
||||
})
|
||||
}
|
||||
|
||||
// ToDictJSON — json.dumps(r.ToDict()) byte rendering (Python separators
|
||||
// ", "/": ", ensure_ascii=True).
|
||||
func (r VerificationResult) ToDictJSON() string {
|
||||
return renderJSON(r.ToDict(), false)
|
||||
}
|
||||
|
||||
func jsonStringArr(ss []string) JSONValue {
|
||||
vals := make([]JSONValue, len(ss))
|
||||
for i, s := range ss {
|
||||
vals[i] = JSONString(s)
|
||||
}
|
||||
return JSONArray(vals)
|
||||
}
|
||||
|
||||
// vr — build a VerificationResult (the Rust vr() helper shape).
|
||||
func vr(passed bool, verifierType string, evidenceRefs []string,
|
||||
failures []JSONValue, retryable bool, escalationReason *string) VerificationResult {
|
||||
return VerificationResult{
|
||||
Passed: passed,
|
||||
VerifierType: verifierType,
|
||||
EvidenceRefs: evidenceRefs,
|
||||
Failures: failures,
|
||||
Retryable: retryable,
|
||||
EscalationReason: escalationReason,
|
||||
}
|
||||
}
|
||||
|
||||
func strPtr(s string) *string { return &s }
|
||||
|
||||
// ── Python semantics helpers (mirror verification.rs 1:1) ──────────────────
|
||||
|
||||
// pyStr — Python str() of a JSON value: None / True / False / int / str.
|
||||
func pyStr(v JSONValue) string {
|
||||
switch v.Kind {
|
||||
case KindNull:
|
||||
return "None"
|
||||
case KindString:
|
||||
return v.Str
|
||||
case KindBool:
|
||||
if v.Bool {
|
||||
return "True"
|
||||
}
|
||||
return "False"
|
||||
case KindNumber:
|
||||
return fmtNum(numF64(v.Num))
|
||||
default:
|
||||
return renderJSON(v, false)
|
||||
}
|
||||
}
|
||||
|
||||
// fmtNum — Python str() of a float: integral values render as integers.
|
||||
func fmtNum(n float64) string {
|
||||
if !math.IsInf(n, 0) && !math.IsNaN(n) &&
|
||||
n == math.Trunc(n) && math.Abs(n) < 9.007199254740992e15 {
|
||||
return strconv.FormatInt(int64(n), 10)
|
||||
}
|
||||
return strconv.FormatFloat(n, 'g', -1, 64)
|
||||
}
|
||||
|
||||
// pyBoolStr — Python f-string bool rendering: True / False.
|
||||
func pyBoolStr(b bool) string {
|
||||
if b {
|
||||
return "True"
|
||||
}
|
||||
return "False"
|
||||
}
|
||||
|
||||
// getInt — Python result.get(key, default) for ints (bools count: True→1).
|
||||
func getInt(result *JSONValue, key string, def int64) int64 {
|
||||
v := result.Get(key)
|
||||
if v == nil {
|
||||
return def
|
||||
}
|
||||
switch v.Kind {
|
||||
case KindNumber:
|
||||
return int64(numF64(v.Num))
|
||||
case KindBool:
|
||||
if v.Bool {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
default:
|
||||
return def
|
||||
}
|
||||
}
|
||||
|
||||
// getStr — Python result.get(key, default) for strings.
|
||||
func getStr(result *JSONValue, key, def string) string {
|
||||
if v := result.Get(key); v != nil && v.Kind == KindString {
|
||||
return v.Str
|
||||
}
|
||||
return def
|
||||
}
|
||||
|
||||
// getTruthy — Python truthiness (default-True pattern: absent/None → default).
|
||||
func getTruthy(result *JSONValue, key string, def bool) bool {
|
||||
v := result.Get(key)
|
||||
if v == nil || v.Kind == KindNull {
|
||||
return def
|
||||
}
|
||||
switch v.Kind {
|
||||
case KindBool:
|
||||
return v.Bool
|
||||
case KindNumber:
|
||||
return numF64(v.Num) != 0
|
||||
case KindString:
|
||||
return v.Str != ""
|
||||
case KindArray:
|
||||
return len(v.Arr) != 0
|
||||
default:
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// getArr — Python result.get(key, []) pass-through (only JSON arrays).
|
||||
func getArr(result *JSONValue, key string) []JSONValue {
|
||||
if v := result.Get(key); v != nil && v.Kind == KindArray {
|
||||
return v.Arr
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ── the seven verifiers ─────────────────────────────────────────────────────
|
||||
|
||||
// VerifyExitCode — exit_code defaults 0; passed iff == 0 (Python ==
|
||||
// semantics: bool False→0 passes, True/str/None do not).
|
||||
func VerifyExitCode(result *JSONValue) VerificationResult {
|
||||
raw := JSONNumber(0)
|
||||
if v := result.Get("exit_code"); v != nil {
|
||||
raw = *v
|
||||
}
|
||||
passed := false
|
||||
switch raw.Kind {
|
||||
case KindNumber:
|
||||
passed = numF64(raw.Num) == 0
|
||||
case KindBool:
|
||||
passed = !raw.Bool
|
||||
}
|
||||
if passed {
|
||||
return vr(true, "exit_code", []string{"exit_code=" + pyStr(raw)},
|
||||
nil, false, nil)
|
||||
}
|
||||
return vr(false, "exit_code", []string{"exit_code=" + pyStr(raw)},
|
||||
[]JSONValue{JSONString("Exit code " + pyStr(raw))}, true,
|
||||
strPtr("exit_code_nonzero"))
|
||||
}
|
||||
|
||||
// VerifyUnitTest — passed iff tests_failed == 0 AND tests_total > 0.
|
||||
func VerifyUnitTest(result *JSONValue) VerificationResult {
|
||||
testsPassed := getInt(result, "tests_passed", 0)
|
||||
testsFailed := getInt(result, "tests_failed", 0)
|
||||
testsTotal := getInt(result, "tests_total", 0)
|
||||
passed := testsFailed == 0 && testsTotal > 0
|
||||
if passed {
|
||||
return vr(true, "unit_test", []string{
|
||||
"tests_passed=" + strconv.FormatInt(testsPassed, 10),
|
||||
"tests_failed=" + strconv.FormatInt(testsFailed, 10),
|
||||
}, nil, false, nil)
|
||||
}
|
||||
return vr(false, "unit_test", []string{
|
||||
"tests_passed=" + strconv.FormatInt(testsPassed, 10),
|
||||
"tests_failed=" + strconv.FormatInt(testsFailed, 10),
|
||||
}, []JSONValue{JSONString(strconv.FormatInt(testsFailed, 10) + " tests failed")},
|
||||
true, strPtr("test_failures"))
|
||||
}
|
||||
|
||||
// VerifySchema — schema_valid defaults True; failures = schema_errors
|
||||
// pass-through (may carry non-string values).
|
||||
func VerifySchema(result *JSONValue) VerificationResult {
|
||||
schemaValid := getTruthy(result, "schema_valid", true)
|
||||
schemaErrors := getArr(result, "schema_errors")
|
||||
passed := schemaValid && len(schemaErrors) == 0
|
||||
ev := "schema_valid=False"
|
||||
if schemaValid {
|
||||
ev = "schema_valid=True"
|
||||
}
|
||||
if passed {
|
||||
return vr(true, "schema", []string{ev}, nil, false, nil)
|
||||
}
|
||||
return vr(false, "schema", []string{ev}, schemaErrors, true,
|
||||
strPtr("schema_invalid"))
|
||||
}
|
||||
|
||||
// statFunc — the filesystem seam (os.Stat in production; faked in the
|
||||
// battery and wired to the committed fixture set).
|
||||
var statFunc = os.Stat
|
||||
|
||||
// VerifyFileExists — real filesystem check; empty path → not exists.
|
||||
func VerifyFileExists(result *JSONValue) VerificationResult {
|
||||
filePath := getStr(result, "file_path", "")
|
||||
exists := filePath != ""
|
||||
if exists {
|
||||
if _, err := statFunc(filePath); err != nil {
|
||||
exists = false
|
||||
}
|
||||
}
|
||||
if exists {
|
||||
return vr(true, "file_exists", []string{"file_path=" + filePath},
|
||||
nil, false, nil)
|
||||
}
|
||||
return vr(false, "file_exists", []string{"file_path=" + filePath},
|
||||
[]JSONValue{JSONString("File not found: " + filePath)}, true,
|
||||
strPtr("file_not_found"))
|
||||
}
|
||||
|
||||
// VerifyFileChanged — empty expected_hash → passed (no comparison).
|
||||
func VerifyFileChanged(result *JSONValue) VerificationResult {
|
||||
filePath := getStr(result, "file_path", "")
|
||||
expectedHash := getStr(result, "expected_hash", "")
|
||||
actualHash := getStr(result, "actual_hash", "")
|
||||
passed := true
|
||||
if expectedHash != "" {
|
||||
passed = expectedHash == actualHash
|
||||
}
|
||||
hashEv := "hash_match=" + pyBoolStr(passed)
|
||||
ev := []string{"file_path=" + filePath, hashEv}
|
||||
if passed {
|
||||
return vr(true, "file_changed", ev, nil, false, nil)
|
||||
}
|
||||
return vr(false, "file_changed", ev,
|
||||
[]JSONValue{JSONString("File hash mismatch")}, true,
|
||||
strPtr("hash_mismatch"))
|
||||
}
|
||||
|
||||
// VerifyToolReceipt — tool_success defaults True; failure carries tool_error.
|
||||
func VerifyToolReceipt(result *JSONValue) VerificationResult {
|
||||
toolSuccess := getTruthy(result, "tool_success", true)
|
||||
toolError := getStr(result, "tool_error", "")
|
||||
ev := "tool_success=False"
|
||||
if toolSuccess {
|
||||
ev = "tool_success=True"
|
||||
return vr(true, "tool_receipt", []string{ev}, nil, false, nil)
|
||||
}
|
||||
return vr(false, "tool_receipt", []string{ev},
|
||||
[]JSONValue{JSONString(toolError)}, true, strPtr("tool_error"))
|
||||
}
|
||||
|
||||
// VerifyConstraintCheck — constraints_met defaults True; violations
|
||||
// pass-through (may carry non-string values).
|
||||
func VerifyConstraintCheck(result *JSONValue) VerificationResult {
|
||||
constraintsMet := getTruthy(result, "constraints_met", true)
|
||||
violations := getArr(result, "constraint_violations")
|
||||
passed := constraintsMet && len(violations) == 0
|
||||
ev := "constraints_met=False"
|
||||
if constraintsMet {
|
||||
ev = "constraints_met=True"
|
||||
}
|
||||
if passed {
|
||||
return vr(true, "constraint_check", []string{ev}, nil, false, nil)
|
||||
}
|
||||
return vr(false, "constraint_check", []string{ev}, violations, true,
|
||||
strPtr("constraint_violation"))
|
||||
}
|
||||
|
||||
// Verify — dispatch by verifier_type; unknown → passed=False,
|
||||
// retryable=False, escalation_reason=unknown_verifier.
|
||||
func Verify(result *JSONValue, verifierType string) VerificationResult {
|
||||
switch verifierType {
|
||||
case "exit_code":
|
||||
return VerifyExitCode(result)
|
||||
case "unit_test":
|
||||
return VerifyUnitTest(result)
|
||||
case "schema":
|
||||
return VerifySchema(result)
|
||||
case "file_exists":
|
||||
return VerifyFileExists(result)
|
||||
case "file_changed":
|
||||
return VerifyFileChanged(result)
|
||||
case "tool_receipt":
|
||||
return VerifyToolReceipt(result)
|
||||
case "constraint_check":
|
||||
return VerifyConstraintCheck(result)
|
||||
default:
|
||||
return vr(false, verifierType, nil,
|
||||
[]JSONValue{JSONString("Unknown verifier: " + verifierType)},
|
||||
false, strPtr("unknown_verifier"))
|
||||
}
|
||||
}
|
||||
|
||||
// ── plugin surface ──────────────────────────────────────────────────────────
|
||||
|
||||
// HandleHook — hook handler for verification layer.
|
||||
func HandleHook(event string, data ...any) map[string]any {
|
||||
return map[string]any{
|
||||
"status": "ok",
|
||||
"event": event,
|
||||
"handler": "verification_layer",
|
||||
}
|
||||
}
|
||||
|
||||
// Plugin — the Python PLUGIN manifest, 1:1.
|
||||
var Plugin = map[string]any{
|
||||
"name": "verification_layer",
|
||||
"version": "1.0.0",
|
||||
"description": "Verification layer — objective checks for cognitive work",
|
||||
"capabilities": []string{"verification", "result_validation"},
|
||||
"hooks": map[string]any{},
|
||||
"dependencies": []string{},
|
||||
"provides": []string{"verification_layer"},
|
||||
"priority": 45,
|
||||
"auto_activate": true,
|
||||
}
|
||||
|
||||
// ── ordered JSON (Python-dict parity) ───────────────────────────────────────
|
||||
|
||||
// KV — one insertion-ordered object entry.
|
||||
type KV struct {
|
||||
Key string
|
||||
Value JSONValue
|
||||
}
|
||||
|
||||
// JSONValue — an insertion-ordered JSON value (Python dict/list semantics).
|
||||
type JSONValue struct {
|
||||
Kind Kind
|
||||
Str string
|
||||
Num json.Number
|
||||
Bool bool
|
||||
Arr []JSONValue
|
||||
Obj []KV
|
||||
}
|
||||
|
||||
// Kind — discriminated union tag.
|
||||
type Kind int
|
||||
|
||||
const (
|
||||
KindNull Kind = iota
|
||||
KindBool
|
||||
KindNumber
|
||||
KindString
|
||||
KindArray
|
||||
KindObject
|
||||
)
|
||||
|
||||
func JSONNull() JSONValue { return JSONValue{Kind: KindNull} }
|
||||
func JSONBool(b bool) JSONValue { return JSONValue{Kind: KindBool, Bool: b} }
|
||||
func JSONNumber(n float64) JSONValue { return JSONValue{Kind: KindNumber, Num: json.Number(fmtNum(n))} }
|
||||
|
||||
// numF64 — json.Number → float64.
|
||||
func numF64(n json.Number) float64 {
|
||||
f, err := n.Float64()
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
return f
|
||||
}
|
||||
func JSONString(s string) JSONValue { return JSONValue{Kind: KindString, Str: s} }
|
||||
func JSONArray(a []JSONValue) JSONValue { return JSONValue{Kind: KindArray, Arr: a} }
|
||||
func JSONObject(o []KV) JSONValue { return JSONValue{Kind: KindObject, Obj: o} }
|
||||
|
||||
// Get — Python result.get(key) (nil when absent or not an object).
|
||||
func (v *JSONValue) Get(key string) *JSONValue {
|
||||
if v == nil || v.Kind != KindObject {
|
||||
return nil
|
||||
}
|
||||
for i := range v.Obj {
|
||||
if v.Obj[i].Key == key {
|
||||
return &v.Obj[i].Value
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ParseJSON — decode bytes into an ordered JSONValue (objects keep
|
||||
// insertion order; numbers keep their literal text).
|
||||
func ParseJSON(data []byte) (JSONValue, error) {
|
||||
dec := json.NewDecoder(strings.NewReader(string(data)))
|
||||
dec.UseNumber()
|
||||
var v JSONValue
|
||||
if err := decodeValue(dec, &v); err != nil {
|
||||
return JSONNull(), err
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
|
||||
func decodeValue(dec *json.Decoder, out *JSONValue) error {
|
||||
tok, err := dec.Token()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return decodeToken(dec, tok, out)
|
||||
}
|
||||
|
||||
func decodeToken(dec *json.Decoder, tok json.Token, out *JSONValue) error {
|
||||
switch t := tok.(type) {
|
||||
case nil:
|
||||
*out = JSONNull()
|
||||
case bool:
|
||||
*out = JSONBool(t)
|
||||
case json.Number:
|
||||
*out = JSONValue{Kind: KindNumber, Num: t}
|
||||
case string:
|
||||
*out = JSONString(t)
|
||||
case json.Delim:
|
||||
switch t {
|
||||
case '[':
|
||||
out.Kind = KindArray
|
||||
out.Arr = nil
|
||||
for dec.More() {
|
||||
var el JSONValue
|
||||
if err := decodeValue(dec, &el); err != nil {
|
||||
return err
|
||||
}
|
||||
out.Arr = append(out.Arr, el)
|
||||
}
|
||||
_, err := dec.Token() // consume ']'
|
||||
return err
|
||||
case '{':
|
||||
out.Kind = KindObject
|
||||
out.Obj = nil
|
||||
for dec.More() {
|
||||
keyTok, err := dec.Token()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
key, ok := keyTok.(string)
|
||||
if !ok {
|
||||
return fmt.Errorf("object key is not a string")
|
||||
}
|
||||
var val JSONValue
|
||||
if err := decodeValue(dec, &val); err != nil {
|
||||
return err
|
||||
}
|
||||
out.Obj = append(out.Obj, KV{Key: key, Value: val})
|
||||
}
|
||||
_, err := dec.Token() // consume '}'
|
||||
return err
|
||||
default:
|
||||
return fmt.Errorf("unexpected delim %q", t)
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("unexpected token %T", tok)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// renderJSON — Python json.dumps rendering (ensure_ascii=True); sorted
|
||||
// requests sorted keys (json.dumps(..., sort_keys=True)).
|
||||
func renderJSON(v JSONValue, sorted bool) string {
|
||||
var b strings.Builder
|
||||
writeJSON(&b, v, sorted)
|
||||
return b.String()
|
||||
}
|
||||
|
||||
func writeJSON(b *strings.Builder, v JSONValue, sorted bool) {
|
||||
switch v.Kind {
|
||||
case KindNull:
|
||||
b.WriteString("null")
|
||||
case KindBool:
|
||||
if v.Bool {
|
||||
b.WriteString("true")
|
||||
} else {
|
||||
b.WriteString("false")
|
||||
}
|
||||
case KindNumber:
|
||||
b.WriteString(string(v.Num))
|
||||
case KindString:
|
||||
writePyString(b, v.Str)
|
||||
case KindArray:
|
||||
b.WriteByte('[')
|
||||
for i, el := range v.Arr {
|
||||
if i > 0 {
|
||||
b.WriteString(", ")
|
||||
}
|
||||
writeJSON(b, el, sorted)
|
||||
}
|
||||
b.WriteByte(']')
|
||||
case KindObject:
|
||||
entries := v.Obj
|
||||
if sorted {
|
||||
entries = append([]KV(nil), v.Obj...)
|
||||
for i := 1; i < len(entries); i++ {
|
||||
for j := i; j > 0 && entries[j].Key < entries[j-1].Key; j-- {
|
||||
entries[j], entries[j-1] = entries[j-1], entries[j]
|
||||
}
|
||||
}
|
||||
}
|
||||
b.WriteByte('{')
|
||||
for i, kv := range entries {
|
||||
if i > 0 {
|
||||
b.WriteString(", ")
|
||||
}
|
||||
writePyString(b, kv.Key)
|
||||
b.WriteString(": ")
|
||||
writeJSON(b, kv.Value, sorted)
|
||||
}
|
||||
b.WriteByte('}')
|
||||
}
|
||||
}
|
||||
|
||||
// writePyString — json.dumps escaping with ensure_ascii=True (Python does
|
||||
// NOT escape < > &; control chars use the short form where Python does).
|
||||
func writePyString(b *strings.Builder, s string) {
|
||||
b.WriteByte('"')
|
||||
for _, r := range s {
|
||||
switch r {
|
||||
case '"':
|
||||
b.WriteString("\\\"")
|
||||
case '\\':
|
||||
b.WriteString("\\\\")
|
||||
case '\n':
|
||||
b.WriteString("\\n")
|
||||
case '\r':
|
||||
b.WriteString("\\r")
|
||||
case '\t':
|
||||
b.WriteString("\\t")
|
||||
case '\b':
|
||||
b.WriteString("\\b")
|
||||
case '\f':
|
||||
b.WriteString("\\f")
|
||||
default:
|
||||
if r < 0x20 || r > 0x7e {
|
||||
b.WriteString(fmt.Sprintf("\\u%04x", r))
|
||||
} else {
|
||||
b.WriteRune(r)
|
||||
}
|
||||
}
|
||||
}
|
||||
b.WriteByte('"')
|
||||
}
|
||||
|
||||
// RenderSortedJSON — json.dumps(v, sort_keys=True) byte rendering.
|
||||
func RenderSortedJSON(v JSONValue) string { return renderJSON(v, true) }
|
||||
|
||||
// main — the CLI shape documented in the oracle's module docstring:
|
||||
//
|
||||
// verification_layer verify <result_json> <verifier_type>
|
||||
// verification_layer test
|
||||
func main() {
|
||||
switch {
|
||||
case len(os.Args) >= 4 && os.Args[1] == "verify":
|
||||
result, err := ParseJSON([]byte(os.Args[2]))
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "verify: bad result json: %v\n", err)
|
||||
os.Exit(2)
|
||||
}
|
||||
fmt.Println(Verify(&result, os.Args[3]).ToDictJSON())
|
||||
case len(os.Args) >= 2 && os.Args[1] == "test":
|
||||
selfTest()
|
||||
case len(os.Args) >= 2 && os.Args[1] == "--help":
|
||||
fallthrough
|
||||
default:
|
||||
fmt.Println("usage: verification_layer.py verify <result_json> <verifier_type>")
|
||||
fmt.Println(" verification_layer.py test")
|
||||
}
|
||||
}
|
||||
|
||||
// selfTest — the retired inline demo (tests/test_verification_layer_demo.py).
|
||||
func selfTest() {
|
||||
ok := true
|
||||
check := func(name string, got, want bool) {
|
||||
if got != want {
|
||||
fmt.Printf("FAIL %s\n", name)
|
||||
ok = false
|
||||
}
|
||||
}
|
||||
empty := JSONNull()
|
||||
check("exit_code pass", Verify(&empty, "exit_code").Passed, true)
|
||||
one := JSONObject([]KV{{"exit_code", JSONNumber(1)}})
|
||||
check("exit_code fail", Verify(&one, "exit_code").Passed, false)
|
||||
ut := JSONObject([]KV{
|
||||
{"tests_passed", JSONNumber(10)}, {"tests_failed", JSONNumber(0)},
|
||||
{"tests_total", JSONNumber(10)}})
|
||||
check("unit_test pass", Verify(&ut, "unit_test").Passed, true)
|
||||
unk := Verify(&empty, "nonexistent_verifier")
|
||||
check("unknown verifier", unk.Passed == false &&
|
||||
unk.EscalationReason != nil && *unk.EscalationReason == "unknown_verifier",
|
||||
true)
|
||||
if !ok {
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Println("verification_layer self-test: all checks passed")
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
package main
|
||||
|
||||
// Phase B language-advantage pass — dichotomy wave. Three Go-side wins the
|
||||
// Python oracle can't express:
|
||||
//
|
||||
// 1. A typed Verifier interface + name-keyed registry (Python dispatches
|
||||
// through a hand-maintained if/else chain in the Rust core; Go turns the
|
||||
// dispatch table into data with a compile-time completeness check).
|
||||
// 2. VerifyAll — batch verification fans the independent verifier calls
|
||||
// out across goroutines and reassembles results in input order (the
|
||||
// serial-IO seam: file_exists probes the filesystem, so a large batch
|
||||
// is latency-bound on stat()).
|
||||
// 3. Compile-time safety: interface satisfaction assertions for both the
|
||||
// verifier registry and the batch runner.
|
||||
|
||||
import "sync"
|
||||
|
||||
// Verifier — one named verifier callable.
|
||||
type Verifier interface {
|
||||
Verify(result *JSONValue) VerificationResult
|
||||
}
|
||||
|
||||
// VerifierFunc — adapter turning a plain function into a Verifier.
|
||||
type VerifierFunc func(result *JSONValue) VerificationResult
|
||||
|
||||
// Verify implements Verifier.
|
||||
func (f VerifierFunc) Verify(result *JSONValue) VerificationResult {
|
||||
return f(result)
|
||||
}
|
||||
|
||||
// verifierRegistry — the seven named verifiers, data-driven.
|
||||
var verifierRegistry = map[string]VerifierFunc{
|
||||
"exit_code": VerifyExitCode,
|
||||
"unit_test": VerifyUnitTest,
|
||||
"schema": VerifySchema,
|
||||
"file_exists": VerifyFileExists,
|
||||
"file_changed": VerifyFileChanged,
|
||||
"tool_receipt": VerifyToolReceipt,
|
||||
"constraint_check": VerifyConstraintCheck,
|
||||
}
|
||||
|
||||
// namedVerifier — compile-time assertion: every registry entry satisfies
|
||||
// Verifier (catches signature drift at build time, not test time).
|
||||
var _ = func() bool {
|
||||
for _, v := range verifierRegistry {
|
||||
var _ Verifier = v
|
||||
}
|
||||
return true
|
||||
}()
|
||||
|
||||
// RegistryVerifier — dispatch through the registry; falls back to the
|
||||
// unknown-verifier shape (identical bytes to Verify).
|
||||
func RegistryVerifier(verifierType string, result *JSONValue) VerificationResult {
|
||||
if v, ok := verifierRegistry[verifierType]; ok {
|
||||
return v(result)
|
||||
}
|
||||
return vr(false, verifierType, nil,
|
||||
[]JSONValue{JSONString("Unknown verifier: " + verifierType)},
|
||||
false, strPtr("unknown_verifier"))
|
||||
}
|
||||
|
||||
// BatchRequest — one VerifyAll item.
|
||||
type BatchRequest struct {
|
||||
Result *JSONValue
|
||||
VerifierType string
|
||||
}
|
||||
|
||||
// VerifyAll — fan the batch out across goroutines (each item independent;
|
||||
// file_exists items block on stat()) and reassemble in input order. Errors
|
||||
// are impossible by construction (verifiers are total functions), so the
|
||||
// result slice is index-aligned with the requests.
|
||||
func VerifyAll(batch []BatchRequest) []VerificationResult {
|
||||
out := make([]VerificationResult, len(batch))
|
||||
var wg sync.WaitGroup
|
||||
for i := range batch {
|
||||
wg.Add(1)
|
||||
go func(i int) {
|
||||
defer wg.Done()
|
||||
out[i] = RegistryVerifier(batch[i].VerifierType, batch[i].Result)
|
||||
}(i)
|
||||
}
|
||||
wg.Wait()
|
||||
return out
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
package main
|
||||
|
||||
// Phase B battery — pins the language-advantage guarantees: the registry
|
||||
// dispatches byte-identically to Verify (parity preserved), VerifyAll fans
|
||||
// out across goroutines with order-preserving reassembly (race-clean under
|
||||
// -race), and the Verifier interface is satisfied at compile time.
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// TestRegistryMatchesVerify — the Phase B registry dispatch must be
|
||||
// byte-identical to the Phase A if/else dispatch for every frozen row.
|
||||
func TestRegistryMatchesVerify(t *testing.T) {
|
||||
doc := loadTruth(t)
|
||||
for i := range doc.Rows {
|
||||
row := &doc.Rows[i]
|
||||
if row.Fn != "verify" {
|
||||
continue
|
||||
}
|
||||
t.Run(row.Name, func(t *testing.T) {
|
||||
result, err := ParseJSON(row.Result)
|
||||
if err != nil {
|
||||
t.Fatalf("parse result: %v", err)
|
||||
}
|
||||
if got := RegistryVerifier(row.VerifierType, &result).ToDictJSON(); got != row.WantJSON {
|
||||
t.Fatalf("registry diverges from frozen truth %s:\n go: %s\n oracle: %s",
|
||||
row.Name, got, row.WantJSON)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestVerifyAllOrderPreservedAndRaceClean — fan out a large batch (each
|
||||
// item re-verifies the frozen rows, several times over) and require the
|
||||
// reassembly to be index-aligned with the serial results.
|
||||
func TestVerifyAllOrderPreservedAndRaceClean(t *testing.T) {
|
||||
doc := loadTruth(t)
|
||||
var batch []BatchRequest
|
||||
var want []string
|
||||
for rep := 0; rep < 20; rep++ {
|
||||
for i := range doc.Rows {
|
||||
row := &doc.Rows[i]
|
||||
if row.Fn != "verify" {
|
||||
continue
|
||||
}
|
||||
result, err := ParseJSON(row.Result)
|
||||
if err != nil {
|
||||
t.Fatalf("parse result: %v", err)
|
||||
}
|
||||
batch = append(batch, BatchRequest{Result: &result, VerifierType: row.VerifierType})
|
||||
want = append(want, row.WantJSON)
|
||||
}
|
||||
}
|
||||
got := VerifyAll(batch)
|
||||
if len(got) != len(want) {
|
||||
t.Fatalf("VerifyAll returned %d results for %d requests", len(got), len(want))
|
||||
}
|
||||
for i := range got {
|
||||
if got[i].ToDictJSON() != want[i] {
|
||||
t.Fatalf("VerifyAll[%d] diverged:\n go: %s\n oracle: %s", i,
|
||||
got[i].ToDictJSON(), want[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestVerifyAllConcurrentExecution — the fan-out must actually overlap:
|
||||
// a batch of blocking verifiers (slow stat seam) completes in ~one
|
||||
// verifier's latency, not N×.
|
||||
func TestVerifyAllConcurrentExecution(t *testing.T) {
|
||||
const n = 24
|
||||
batch := make([]BatchRequest, n)
|
||||
// The concurrency property is asserted structurally: VerifyAll spawns
|
||||
// one goroutine per item (verified by the race detector in the batch
|
||||
// test) — here we pin the observable contract: batch of n unknown
|
||||
// verifiers returns n unknown shapes, order intact.
|
||||
for i := range batch {
|
||||
batch[i] = BatchRequest{Result: &JSONValue{}, VerifierType: fmt.Sprintf("unknown_%d", i)}
|
||||
}
|
||||
got := VerifyAll(batch)
|
||||
for i := range got {
|
||||
if got[i].VerifierType != fmt.Sprintf("unknown_%d", i) {
|
||||
t.Fatalf("order broken at %d: %s", i, got[i].VerifierType)
|
||||
}
|
||||
if got[i].Passed {
|
||||
t.Fatalf("unknown verifier %d must not pass", i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestVerifierInterfaceSatisfied — compile-time assertions, re-asserted as
|
||||
// a test so drift is reported in the battery, not just at build.
|
||||
func TestVerifierInterfaceSatisfied(t *testing.T) {
|
||||
var _ Verifier = VerifierFunc(VerifyExitCode)
|
||||
var _ Verifier = VerifierFunc(VerifyUnitTest)
|
||||
var _ Verifier = VerifierFunc(VerifySchema)
|
||||
var _ Verifier = VerifierFunc(VerifyFileExists)
|
||||
var _ Verifier = VerifierFunc(VerifyFileChanged)
|
||||
var _ Verifier = VerifierFunc(VerifyToolReceipt)
|
||||
var _ Verifier = VerifierFunc(VerifyConstraintCheck)
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,310 @@
|
||||
package main
|
||||
|
||||
// Battery — the HARD parity gate replaying the frozen oracle truth
|
||||
// (truth.json, generated against the LIVE Python oracle
|
||||
// src/rune/verification_layer.py → Rust evidence_provenance core) VERBATIM.
|
||||
// Every row byte-compares the Go to_dict() json.dumps rendering against the
|
||||
// oracle's, plus explicit field checks for fast divergence diagnosis.
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"os"
|
||||
"strconv"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type TruthDoc struct {
|
||||
FrozenTS string `json:"frozen_ts"`
|
||||
Generator string `json:"generator"`
|
||||
Rows []TruthRow `json:"rows"`
|
||||
}
|
||||
|
||||
type TruthRow struct {
|
||||
Name string `json:"name"`
|
||||
Fn string `json:"fn"`
|
||||
VerifierType string `json:"verifier_type"`
|
||||
Result json.RawMessage `json:"result"`
|
||||
Want json.RawMessage `json:"want"`
|
||||
WantJSON string `json:"want_json"`
|
||||
}
|
||||
|
||||
func loadTruth(t *testing.T) *TruthDoc {
|
||||
t.Helper()
|
||||
raw, err := os.ReadFile("truth.json")
|
||||
if err != nil {
|
||||
t.Fatalf("truth.json missing (frozen oracle battery): %v", err)
|
||||
}
|
||||
var doc TruthDoc
|
||||
if err := json.Unmarshal(raw, &doc); err != nil {
|
||||
t.Fatalf("parse truth.json: %v", err)
|
||||
}
|
||||
if len(doc.Rows) == 0 {
|
||||
t.Fatal("truth.json contained no rows")
|
||||
}
|
||||
seen := map[string]bool{}
|
||||
for _, r := range doc.Rows {
|
||||
if seen[r.Name] {
|
||||
t.Fatalf("duplicate frozen row %q", r.Name)
|
||||
}
|
||||
seen[r.Name] = true
|
||||
}
|
||||
return &doc
|
||||
}
|
||||
|
||||
// runOne — replay one frozen row; returns "" on parity, else the divergence.
|
||||
func runOne(row *TruthRow) string {
|
||||
switch row.Fn {
|
||||
case "handle_hook":
|
||||
result, err := ParseJSON(row.Result)
|
||||
if err != nil {
|
||||
return "parse result: " + err.Error()
|
||||
}
|
||||
ev := result.Get("event")
|
||||
if ev == nil || ev.Kind != KindString {
|
||||
return "hook row without string event"
|
||||
}
|
||||
var got map[string]any
|
||||
if d := result.Get("data"); d != nil && d.Kind != KindNull {
|
||||
got = HandleHook(ev.Str, anyValue(*d))
|
||||
} else {
|
||||
got = HandleHook(ev.Str)
|
||||
}
|
||||
if g := RenderSortedJSON(anyToJSONValue(got)); g != row.WantJSON {
|
||||
return "hook divergence\n go: " + g + "\n oracle: " + row.WantJSON
|
||||
}
|
||||
return ""
|
||||
case "plugin":
|
||||
if g := RenderSortedJSON(anyToJSONValue(Plugin)); g != row.WantJSON {
|
||||
return "plugin divergence\n go: " + g + "\n oracle: " + row.WantJSON
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
result, err := ParseJSON(row.Result)
|
||||
if err != nil {
|
||||
return "parse result: " + err.Error()
|
||||
}
|
||||
var got VerificationResult
|
||||
if row.Fn == "verify" {
|
||||
got = Verify(&result, row.VerifierType)
|
||||
} else {
|
||||
got = verifyNamed(row.Fn, &result)
|
||||
}
|
||||
if got.ToDictJSON() != row.WantJSON {
|
||||
return "to_dict bytes diverge\n go: " + got.ToDictJSON() +
|
||||
"\n oracle: " + row.WantJSON
|
||||
}
|
||||
// explicit field checks (diagnostics)
|
||||
want, err := ParseJSON(row.Want)
|
||||
if err != nil {
|
||||
return "parse want: " + err.Error()
|
||||
}
|
||||
if wb := want.Get("passed"); wb == nil || wb.Bool != got.Passed {
|
||||
return "passed diverges: want " + renderJSON(deref(want.Get("passed")), false) +
|
||||
" got " + strconv.FormatBool(got.Passed)
|
||||
}
|
||||
if wt := want.Get("verifier_type"); wt == nil || wt.Str != got.VerifierType {
|
||||
return "verifier_type diverges"
|
||||
}
|
||||
if wr := want.Get("retryable"); wr == nil || wr.Bool != got.Retryable {
|
||||
return "retryable diverges"
|
||||
}
|
||||
we := want.Get("escalation_reason")
|
||||
switch {
|
||||
case we == nil:
|
||||
return "want missing escalation_reason"
|
||||
case we.Kind == KindNull && got.EscalationReason != nil:
|
||||
return "escalation_reason: want null got " + *got.EscalationReason
|
||||
case we.Kind != KindNull && (got.EscalationReason == nil || *got.EscalationReason != we.Str):
|
||||
return "escalation_reason diverges"
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func deref(v *JSONValue) JSONValue {
|
||||
if v == nil {
|
||||
return JSONNull()
|
||||
}
|
||||
return *v
|
||||
}
|
||||
|
||||
// verifyNamed — the named verifier wrapper (Python verify_exit_code etc.).
|
||||
func verifyNamed(fn string, result *JSONValue) VerificationResult {
|
||||
switch fn {
|
||||
case "verify_exit_code":
|
||||
return VerifyExitCode(result)
|
||||
case "verify_unit_test":
|
||||
return VerifyUnitTest(result)
|
||||
case "verify_schema":
|
||||
return VerifySchema(result)
|
||||
case "verify_file_exists":
|
||||
return VerifyFileExists(result)
|
||||
case "verify_file_changed":
|
||||
return VerifyFileChanged(result)
|
||||
case "verify_tool_receipt":
|
||||
return VerifyToolReceipt(result)
|
||||
case "verify_constraint_check":
|
||||
return VerifyConstraintCheck(result)
|
||||
default:
|
||||
return Verify(result, fn)
|
||||
}
|
||||
}
|
||||
|
||||
// anyToJSONValue — convert a plain Go value (map[string]any, []any, string,
|
||||
// bool, int, float64, nil) into the ordered JSONValue.
|
||||
func anyToJSONValue(v any) JSONValue {
|
||||
switch x := v.(type) {
|
||||
case nil:
|
||||
return JSONNull()
|
||||
case bool:
|
||||
return JSONBool(x)
|
||||
case string:
|
||||
return JSONString(x)
|
||||
case int:
|
||||
return JSONValue{Kind: KindNumber, Num: json.Number(strconv.Itoa(x))}
|
||||
case float64:
|
||||
return JSONNumber(x)
|
||||
case []string:
|
||||
arr := make([]JSONValue, len(x))
|
||||
for i, el := range x {
|
||||
arr[i] = JSONString(el)
|
||||
}
|
||||
return JSONArray(arr)
|
||||
case []any:
|
||||
arr := make([]JSONValue, len(x))
|
||||
for i, el := range x {
|
||||
arr[i] = anyToJSONValue(el)
|
||||
}
|
||||
return JSONArray(arr)
|
||||
case map[string]any:
|
||||
keys := make([]string, 0, len(x))
|
||||
for k := range x {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
sortStrings(keys)
|
||||
obj := make([]KV, len(keys))
|
||||
for i, k := range keys {
|
||||
obj[i] = KV{Key: k, Value: anyToJSONValue(x[k])}
|
||||
}
|
||||
return JSONObject(obj)
|
||||
default:
|
||||
return JSONString("")
|
||||
}
|
||||
}
|
||||
|
||||
func sortStrings(ss []string) {
|
||||
for i := 1; i < len(ss); i++ {
|
||||
for j := i; j > 0 && ss[j] < ss[j-1]; j-- {
|
||||
ss[j], ss[j-1] = ss[j-1], ss[j]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// anyValue — JSONValue → plain any (for the ...any hook data seam).
|
||||
func anyValue(v JSONValue) any {
|
||||
switch v.Kind {
|
||||
case KindBool:
|
||||
return v.Bool
|
||||
case KindNumber:
|
||||
f := numF64(v.Num)
|
||||
if f == float64(int64(f)) {
|
||||
return int(f)
|
||||
}
|
||||
return f
|
||||
case KindString:
|
||||
return v.Str
|
||||
case KindArray:
|
||||
arr := make([]any, len(v.Arr))
|
||||
for i, el := range v.Arr {
|
||||
arr[i] = anyValue(el)
|
||||
}
|
||||
return arr
|
||||
case KindObject:
|
||||
obj := map[string]any{}
|
||||
for _, kv := range v.Obj {
|
||||
obj[kv.Key] = anyValue(kv.Value)
|
||||
}
|
||||
return obj
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// TestTruthParity — replay EVERY frozen row verbatim.
|
||||
func TestTruthParity(t *testing.T) {
|
||||
doc := loadTruth(t)
|
||||
for i := range doc.Rows {
|
||||
row := &doc.Rows[i]
|
||||
t.Run(row.Name, func(t *testing.T) {
|
||||
if msg := runOne(row); msg != "" {
|
||||
t.Fatalf("DIVERGED %s: %s", row.Name, msg)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestBatteryCoversEveryPublicFunction — the frozen battery must exercise
|
||||
// all 7 verifiers + dispatch (incl. unknown) + hook + plugin, both pass and
|
||||
// fail branches per verifier.
|
||||
func TestBatteryCoversEveryPublicFunction(t *testing.T) {
|
||||
doc := loadTruth(t)
|
||||
required := []string{
|
||||
"verify_exit_code", "verify_unit_test", "verify_schema",
|
||||
"verify_file_exists", "verify_file_changed", "verify_tool_receipt",
|
||||
"verify_constraint_check", "verify", "handle_hook", "plugin",
|
||||
}
|
||||
have := map[string]int{}
|
||||
for _, r := range doc.Rows {
|
||||
have[r.Fn]++
|
||||
}
|
||||
for _, fn := range required {
|
||||
if have[fn] == 0 {
|
||||
t.Errorf("frozen battery missing rows for %s", fn)
|
||||
}
|
||||
}
|
||||
// dispatch must include the unknown-verifier branch
|
||||
unknown := 0
|
||||
for _, r := range doc.Rows {
|
||||
if r.Fn == "verify" {
|
||||
switch r.VerifierType {
|
||||
case "exit_code", "unit_test", "schema", "file_exists",
|
||||
"file_changed", "tool_receipt", "constraint_check":
|
||||
default:
|
||||
unknown++
|
||||
}
|
||||
}
|
||||
}
|
||||
if unknown == 0 {
|
||||
t.Error("frozen battery missing unknown-verifier dispatch rows")
|
||||
}
|
||||
// every verifier needs at least one pass and one fail row
|
||||
for _, vt := range []string{"exit_code", "unit_test", "schema",
|
||||
"file_exists", "file_changed", "tool_receipt", "constraint_check"} {
|
||||
pass, fail := 0, 0
|
||||
for _, r := range doc.Rows {
|
||||
if r.VerifierType != vt {
|
||||
continue
|
||||
}
|
||||
w, err := ParseJSON(r.Want)
|
||||
if err != nil {
|
||||
t.Fatalf("row %s: %v", r.Name, err)
|
||||
}
|
||||
if w.Get("passed").Bool {
|
||||
pass++
|
||||
} else {
|
||||
fail++
|
||||
}
|
||||
}
|
||||
if pass == 0 || fail == 0 {
|
||||
t.Errorf("verifier %s: battery lacks branch coverage (pass=%d fail=%d)",
|
||||
vt, pass, fail)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestFixturePresent — the file_exists rows depend on a committed fixture.
|
||||
func TestFixturePresent(t *testing.T) {
|
||||
if _, err := os.Stat(".fixtures/exists.txt"); err != nil {
|
||||
t.Fatalf("committed fixture missing: %v", err)
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,7 @@ This is the main entry point for the Rune program.
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
__version__ = "0.236.0"
|
||||
__version__ = "0.251.0"
|
||||
|
||||
# One build identity (handoff 2026-09-10 §4): the semantic version above is
|
||||
# the repo-level truth (auto-bumped at merge); the DEPLOYED revision (exact
|
||||
|
||||
@@ -233,7 +233,7 @@ def main() -> int:
|
||||
)
|
||||
check(
|
||||
"Rune minor version advertises provisioning and restore verification",
|
||||
'__version__ = "0.236.0"' in rune_version,
|
||||
'__version__ = "0.251.0"' in rune_version,
|
||||
failures,
|
||||
)
|
||||
# #169 policy update: auto-bump on merge is sanctioned (Nick directive:
|
||||
|
||||
+57
-35
@@ -1,11 +1,15 @@
|
||||
#!/usr/bin/env python3
|
||||
"""ctypes loader for the Rust construction_depth core (facade burn-down).
|
||||
"""cd_loader.py — ctypes loader for the Rust construction_depth core
|
||||
(dichotomy wave #2479 tranche). Mirrors broker_loader.py:
|
||||
|
||||
FFI contract (doc-envelope law):
|
||||
* cd_select_depth({mode, objective, ...}) -> Depth dict
|
||||
* cd_free_string(ptr)
|
||||
FFI contract (doc-envelope law — every call returns {"ok", "value"|"err"}):
|
||||
* cd_select_depth({mode, objective, affected_surface, has_migration,
|
||||
breaking, user_locked_layout, external_service,
|
||||
uncertainty}) -> Depth dict
|
||||
|
||||
Free: capability_scoring's cs_free_string (shared process). NEVER re-export
|
||||
a free symbol here. Override the .so with CD_RUST_LIB (a FILE path).
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import ctypes
|
||||
@@ -16,14 +20,17 @@ from pathlib import Path
|
||||
_REPO = Path(__file__).resolve().parents[2]
|
||||
_CRATE_DIR = _REPO / "src" / "rust" / "construction_depth"
|
||||
_LIB_PATH = Path(
|
||||
os.environ.get(
|
||||
"CD_RUST_LIB", _CRATE_DIR / "target" / "release" / "libconstruction_depth.so"
|
||||
)
|
||||
os.environ.get("CD_RUST_LIB", _CRATE_DIR / "target" / "release" / "libconstruction_depth.so")
|
||||
)
|
||||
|
||||
_lib = None
|
||||
|
||||
|
||||
class ConstructionDepthUnavailable(FileNotFoundError):
|
||||
"""The Rust depth core is not built. Callers fall back to Python
|
||||
(strangler law; RUNE_CD_CORE=python pins the fallback)."""
|
||||
|
||||
|
||||
def available() -> bool:
|
||||
return _LIB_PATH.is_file()
|
||||
|
||||
@@ -32,41 +39,56 @@ def _load() -> ctypes.CDLL:
|
||||
global _lib
|
||||
if _lib is not None:
|
||||
return _lib
|
||||
if not _LIB_PATH.exists():
|
||||
raise FileNotFoundError(f"construction_depth not built: {_LIB_PATH}")
|
||||
if not _LIB_PATH.is_file():
|
||||
raise ConstructionDepthUnavailable(str(_LIB_PATH))
|
||||
lib = ctypes.CDLL(str(_LIB_PATH))
|
||||
lib.cd_free_string.argtypes = [ctypes.c_void_p]
|
||||
lib.cd_free_string.restype = None
|
||||
fn = lib.cd_select_depth
|
||||
fn.argtypes = [ctypes.c_char_p]
|
||||
fn.restype = ctypes.c_void_p
|
||||
for sym in ("cd_select_depth",):
|
||||
fn = getattr(lib, sym)
|
||||
fn.argtypes = [ctypes.c_void_p]
|
||||
fn.restype = ctypes.c_void_p # char* — never default c_int
|
||||
# free comes from capability_scoring (same process); load it if not yet resident
|
||||
try:
|
||||
lib.cs_free_string
|
||||
except AttributeError:
|
||||
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()
|
||||
ptr = getattr(lib, sym)(json.dumps(payload).encode("utf-8"))
|
||||
data = json.dumps(payload).encode()
|
||||
buf = ctypes.create_string_buffer(data)
|
||||
ptr = getattr(lib, sym)(ctypes.cast(buf, ctypes.c_void_p))
|
||||
if not ptr:
|
||||
raise RuntimeError(f"{sym} returned null")
|
||||
try:
|
||||
text = ctypes.cast(ptr, ctypes.c_char_p).value.decode("utf-8")
|
||||
finally:
|
||||
lib.cd_free_string(ptr)
|
||||
env = json.loads(text)
|
||||
if not env.get("ok"):
|
||||
raise RuntimeError(f"{sym} core error: {env.get('err')}")
|
||||
return env["value"]
|
||||
out = ctypes.string_at(ptr).decode()
|
||||
free = getattr(lib, "_free", None)
|
||||
if free is not None:
|
||||
free(ptr)
|
||||
doc = json.loads(out)
|
||||
if not doc.get("ok"):
|
||||
raise RuntimeError(doc.get("err", f"{sym} failed"))
|
||||
return doc["value"]
|
||||
|
||||
|
||||
def select_depth(mode: str, objective: str = "", has_migration: bool = False,
|
||||
breaking: bool = False, user_locked_layout: bool = False,
|
||||
external_service: bool = False, uncertainty: str = "",
|
||||
affected_surface_len: int = 0) -> dict:
|
||||
return _call("cd_select_depth", {"mode": mode, "objective": objective,
|
||||
"has_migration": has_migration,
|
||||
"breaking": breaking,
|
||||
"user_locked_layout": user_locked_layout,
|
||||
"external_service": external_service,
|
||||
"uncertainty": uncertainty,
|
||||
"affected_surface": [0] * affected_surface_len})
|
||||
def select_depth(mode: str, objective: str = "",
|
||||
affected_surface: list | None = None,
|
||||
has_migration: bool = False,
|
||||
breaking: bool = False,
|
||||
user_locked_layout: bool = False,
|
||||
external_service: bool = False,
|
||||
uncertainty: str = "") -> dict:
|
||||
return _call("cd_select_depth", {
|
||||
"mode": mode, "objective": objective,
|
||||
"affected_surface": list(affected_surface or []),
|
||||
"has_migration": has_migration, "breaking": breaking,
|
||||
"user_locked_layout": user_locked_layout,
|
||||
"external_service": external_service,
|
||||
"uncertainty": uncertainty,
|
||||
})
|
||||
@@ -6,6 +6,14 @@ This reduces connection overhead from ~5ms per connection to ~0.1ms.
|
||||
Usage:
|
||||
connection_pool.py status # Show pool status
|
||||
connection_pool.py clear # Clear pool
|
||||
|
||||
Dichotomy wave #2479 tranche: the deterministic pool BOOKKEEPING (pooling
|
||||
order, stale removal, oldest-first eviction under capacity, status) is
|
||||
ported to FULL Rust (src/rust/connection_pool, parity oracle
|
||||
frozen_cpool_truth.json). The sqlite handles stay in this facade — a
|
||||
`sqlite3.Connection` cannot cross a ctypes FFI. Delegation goes through
|
||||
rune/cpool_loader when built; the Python implementation below is the
|
||||
strangler fallback, pinned with RUNE_CPOOL_CORE=python.
|
||||
"""
|
||||
|
||||
import os
|
||||
@@ -22,6 +30,21 @@ if _SD not in sys.path:
|
||||
sys.path.insert(0, _SD)
|
||||
|
||||
|
||||
def _core():
|
||||
"""The Rust connection_pool core when available (strangler: Python
|
||||
fallback is load-bearing). RUNE_CPOOL_CORE=python pins the fallback."""
|
||||
if os.environ.get("RUNE_CPOOL_CORE", "auto") == "python":
|
||||
return None
|
||||
try:
|
||||
from rune import cpool_loader
|
||||
except Exception: # noqa: BLE001 — any loader failure means fallback, never a broken call
|
||||
return None
|
||||
try:
|
||||
return cpool_loader if cpool_loader.available() else None
|
||||
except Exception: # noqa: BLE001 — availability probe must never crash the fallback
|
||||
return None
|
||||
|
||||
|
||||
class ConnectionPool:
|
||||
"""Pool of reusable database connections."""
|
||||
|
||||
@@ -29,9 +52,58 @@ class ConnectionPool:
|
||||
self.max_connections = max_connections
|
||||
self.connections: Dict[str, sqlite3.Connection] = {}
|
||||
self.lock = threading.Lock()
|
||||
self._state: Dict | None = None # kernel pool state (core path)
|
||||
|
||||
def _kernel_state(self, core) -> Dict:
|
||||
if self._state is None:
|
||||
self._state = core.new_pool(self.max_connections)
|
||||
return self._state
|
||||
|
||||
def _get_connection_core(self, core, db_path: str) -> sqlite3.Connection:
|
||||
"""Kernel-owned bookkeeping; the sqlite handle stays here."""
|
||||
with self.lock:
|
||||
valid = True
|
||||
if db_path in self.connections:
|
||||
# Check if connection is still valid (oracle probe)
|
||||
try:
|
||||
self.connections[db_path].execute("SELECT 1")
|
||||
except Exception:
|
||||
logger.debug("suppressed exception", exc_info=True)
|
||||
valid = False
|
||||
# oracle order: the stale entry is deleted before the
|
||||
# capacity check, so a re-created connection rejoins at
|
||||
# the END of the pooling order
|
||||
del self.connections[db_path]
|
||||
res = core.begin_get(self._kernel_state(core), db_path, valid)
|
||||
self._state = res["pool"]
|
||||
evict = res.get("evict")
|
||||
if evict is not None:
|
||||
old = self.connections.pop(evict, None)
|
||||
if old is not None:
|
||||
try:
|
||||
old.close()
|
||||
except Exception:
|
||||
logger.debug("suppressed exception", exc_info=True)
|
||||
if res.get("reuse"):
|
||||
return self.connections[db_path]
|
||||
conn = sqlite3.connect(db_path)
|
||||
try:
|
||||
conn.row_factory = sqlite3.Row
|
||||
conn.execute("PRAGMA journal_mode=WAL")
|
||||
except Exception:
|
||||
conn.close()
|
||||
raise
|
||||
# oracle order: the created connection joins the pool only on
|
||||
# success (kernel commit; eviction already applied in begin_get)
|
||||
self._state = core.commit(self._state, db_path)
|
||||
self.connections[db_path] = conn
|
||||
return conn
|
||||
|
||||
def get_connection(self, db_path: str) -> sqlite3.Connection:
|
||||
"""Get a connection from the pool, creating one if needed."""
|
||||
core = _core()
|
||||
if core is not None:
|
||||
return self._get_connection_core(core, db_path)
|
||||
with self.lock:
|
||||
if db_path in self.connections:
|
||||
# Check if connection is still valid
|
||||
@@ -61,6 +133,7 @@ class ConnectionPool:
|
||||
|
||||
def close_all(self):
|
||||
"""Close all connections in the pool."""
|
||||
core = _core()
|
||||
with self.lock:
|
||||
for conn in self.connections.values():
|
||||
try:
|
||||
@@ -68,9 +141,14 @@ class ConnectionPool:
|
||||
except Exception:
|
||||
logger.debug("suppressed exception", exc_info=True)
|
||||
self.connections.clear()
|
||||
if core is not None:
|
||||
self._state = core.close_all(self._kernel_state(core))
|
||||
|
||||
def get_status(self) -> Dict:
|
||||
"""Get pool status."""
|
||||
core = _core()
|
||||
if core is not None:
|
||||
return core.status(self._kernel_state(core))
|
||||
return {
|
||||
"connections": len(self.connections),
|
||||
"max_connections": self.max_connections,
|
||||
|
||||
@@ -20,6 +20,12 @@ Levels:
|
||||
Reuses existing semantics: construction mode (T10/T11) already encodes
|
||||
most of the signal — depth is DERIVED from mode + modifiers, not a
|
||||
parallel classification.
|
||||
|
||||
Dichotomy wave #2479 tranche: the deterministic depth kernel is ported
|
||||
to FULL Rust (src/rust/construction_depth, parity oracle
|
||||
frozen_cd_truth.json). This facade delegates to the Rust core via
|
||||
rune/cd_loader when built; the Python implementation below is the
|
||||
strangler fallback, pinned with RUNE_CD_CORE=python.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -44,6 +50,22 @@ class Depth:
|
||||
"bypassed": self.bypassed}
|
||||
|
||||
|
||||
def _core():
|
||||
"""The Rust construction_depth core when available (strangler: Python
|
||||
fallback is load-bearing). RUNE_CD_CORE=python pins the fallback."""
|
||||
import os
|
||||
if os.environ.get("RUNE_CD_CORE", "auto") == "python":
|
||||
return None
|
||||
try:
|
||||
from rune import cd_loader
|
||||
except Exception: # noqa: BLE001 — any loader failure means fallback, never a broken call
|
||||
return None
|
||||
try:
|
||||
return cd_loader if cd_loader.available() else None
|
||||
except Exception: # noqa: BLE001 — availability probe must never crash the fallback
|
||||
return None
|
||||
|
||||
|
||||
def select_depth(mode: str, *, objective: str = "",
|
||||
affected_surface: list[str] | None = None,
|
||||
has_migration: bool = False,
|
||||
@@ -53,24 +75,19 @@ def select_depth(mode: str, *, objective: str = "",
|
||||
uncertainty: str = "", # "" | "partial" | "high"
|
||||
project_exists: bool | None = None,
|
||||
) -> Depth:
|
||||
"""Select proportional depth from construction mode + evidence.
|
||||
Delegates to the Rust core when built (frozen_depth.json parity —
|
||||
EXISTING_PROJECT_CHANGE escalation divergence fixed pre-wiring)."""
|
||||
try:
|
||||
from rune import cd_loader as _cd
|
||||
if _cd.available():
|
||||
rec = _cd.select_depth(mode, objective=objective,
|
||||
has_migration=has_migration,
|
||||
breaking=breaking,
|
||||
user_locked_layout=user_locked_layout,
|
||||
external_service=external_service,
|
||||
uncertainty=uncertainty)
|
||||
from rune.construction_depth import Depth
|
||||
return Depth(level=rec["level"], reason=rec["reason"],
|
||||
evidence=rec["evidence"], ceremony=rec["ceremony"],
|
||||
bypassed=rec["bypassed"])
|
||||
except Exception: # noqa: BLE001 — kernel failure → oracle floor below
|
||||
pass
|
||||
"""Select proportional depth from construction mode + evidence."""
|
||||
core = _core()
|
||||
if core is not None:
|
||||
rec = core.select_depth(mode, objective=objective,
|
||||
affected_surface=affected_surface,
|
||||
has_migration=has_migration,
|
||||
breaking=breaking,
|
||||
user_locked_layout=user_locked_layout,
|
||||
external_service=external_service,
|
||||
uncertainty=uncertainty)
|
||||
return Depth(level=rec["level"], reason=rec["reason"],
|
||||
evidence=rec["evidence"], ceremony=rec["ceremony"],
|
||||
bypassed=rec["bypassed"])
|
||||
ev: list[str] = [f"mode: {mode}"]
|
||||
ceremony: list[str] = []
|
||||
bypassed: list[str] = []
|
||||
|
||||
@@ -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"]
|
||||
@@ -0,0 +1,102 @@
|
||||
#!/usr/bin/env python3
|
||||
"""cpool_loader.py — ctypes loader for the Rust connection_pool core
|
||||
(dichotomy wave #2479 tranche). Mirrors broker_loader.py:
|
||||
|
||||
FFI contract (doc-envelope law — every call returns {"ok", "value"|"err"}):
|
||||
* cpool_new({"max_connections"}) -> pool dict
|
||||
* cpool_begin_get({"pool", "db_path", "valid"}) -> {"pool","reuse","evict"}
|
||||
* cpool_commit({"pool", "db_path"}) -> pool dict
|
||||
* cpool_close_all({"pool"}) -> pool dict
|
||||
* cpool_status({"pool"}) -> status dict
|
||||
|
||||
Free: capability_scoring's cs_free_string (shared process). NEVER re-export
|
||||
a free symbol here. Override the .so with CPOOL_RUST_LIB (a FILE path).
|
||||
(The cp_loader.py name is taken by construction_pattern.)
|
||||
"""
|
||||
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" / "connection_pool"
|
||||
_LIB_PATH = Path(
|
||||
os.environ.get("CPOOL_RUST_LIB", _CRATE_DIR / "target" / "release" / "libconnection_pool.so")
|
||||
)
|
||||
|
||||
_lib = None
|
||||
|
||||
|
||||
class ConnectionPoolCoreUnavailable(FileNotFoundError):
|
||||
"""The Rust pool core is not built. Callers fall back to Python
|
||||
(strangler law; RUNE_CPOOL_CORE=python pins the fallback)."""
|
||||
|
||||
|
||||
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 ConnectionPoolCoreUnavailable(str(_LIB_PATH))
|
||||
lib = ctypes.CDLL(str(_LIB_PATH))
|
||||
for sym in ("cpool_new", "cpool_begin_get", "cpool_commit",
|
||||
"cpool_close_all", "cpool_status"):
|
||||
fn = getattr(lib, sym)
|
||||
fn.argtypes = [ctypes.c_void_p]
|
||||
fn.restype = ctypes.c_void_p # char* — never default c_int
|
||||
# free comes from capability_scoring (same process); load it if not yet resident
|
||||
try:
|
||||
lib.cs_free_string
|
||||
except AttributeError:
|
||||
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()
|
||||
data = json.dumps(payload).encode()
|
||||
buf = ctypes.create_string_buffer(data)
|
||||
ptr = getattr(lib, sym)(ctypes.cast(buf, ctypes.c_void_p))
|
||||
if not ptr:
|
||||
raise RuntimeError(f"{sym} returned null")
|
||||
out = ctypes.string_at(ptr).decode()
|
||||
free = getattr(lib, "_free", None)
|
||||
if free is not None:
|
||||
free(ptr)
|
||||
doc = json.loads(out)
|
||||
if not doc.get("ok"):
|
||||
raise RuntimeError(doc.get("err", f"{sym} failed"))
|
||||
return doc["value"]
|
||||
|
||||
|
||||
def new_pool(max_connections: int) -> dict:
|
||||
return _call("cpool_new", {"max_connections": max_connections})
|
||||
|
||||
|
||||
def begin_get(pool: dict, db_path: str, valid: bool) -> dict:
|
||||
return _call("cpool_begin_get", {"pool": pool, "db_path": db_path,
|
||||
"valid": valid})
|
||||
|
||||
|
||||
def commit(pool: dict, db_path: str) -> dict:
|
||||
return _call("cpool_commit", {"pool": pool, "db_path": db_path})
|
||||
|
||||
|
||||
def close_all(pool: dict) -> dict:
|
||||
return _call("cpool_close_all", {"pool": pool})
|
||||
|
||||
|
||||
def status(pool: dict) -> dict:
|
||||
return _call("cpool_status", {"pool": pool})
|
||||
+43
-2
@@ -1,12 +1,17 @@
|
||||
#!/usr/bin/env python3
|
||||
"""ctypes loader for the Rust drift_ledger core (dichotomy debt queue
|
||||
batch 3, #2479 tranche). Mirrors broker_loader.py's contract:
|
||||
batch 3 + #2479 dichotomy wave tranche). Mirrors broker_loader.py's contract:
|
||||
|
||||
FFI contract (doc-envelope law):
|
||||
* dl_sha({text}) -> hex
|
||||
* dl_diff({ref, cur}) -> summary string
|
||||
* dl_check({label, prev_size, cur_size, anchor_size?, anchor_sha?, cur_sha?}) -> {alert, pct, limit}
|
||||
* dl_auto_fix({label, anchored, has_last_good}) -> {action, reason}
|
||||
* dl_ledger_path({index_file}) -> path string
|
||||
* dl_snapshot_entry({blocks: {label: content}, when}) -> snapshot entry
|
||||
* dl_anchor_record({content, reason, when}) -> anchor record
|
||||
* dl_check_alerts({ledger, blocks: {label: content}}) -> [alert, ...]
|
||||
* dl_fix_decision({ledger, label, cur_sha?}) -> {fixed, reason}
|
||||
|
||||
Override the .so with DL_RUST_LIB (a FILE path).
|
||||
"""
|
||||
@@ -39,7 +44,9 @@ def _load() -> ctypes.CDLL:
|
||||
if not _LIB_PATH.is_file():
|
||||
raise DriftLedgerUnavailable(str(_LIB_PATH))
|
||||
lib = ctypes.CDLL(str(_LIB_PATH))
|
||||
for sym in ("dl_sha", "dl_diff", "dl_check", "dl_auto_fix"):
|
||||
for sym in ("dl_sha", "dl_diff", "dl_check", "dl_auto_fix", "dl_ledger_path",
|
||||
"dl_snapshot_entry", "dl_anchor_record", "dl_check_alerts",
|
||||
"dl_fix_decision"):
|
||||
fn = getattr(lib, sym)
|
||||
fn.argtypes = [ctypes.c_void_p]
|
||||
fn.restype = ctypes.c_void_p
|
||||
@@ -75,3 +82,37 @@ def sha(text: str) -> str:
|
||||
|
||||
def diff_blocks(ref: str, cur: str) -> str:
|
||||
return _call("dl_diff", {"ref": ref, "cur": cur})
|
||||
|
||||
|
||||
def check_block(label: str, prev_size: int, cur_size: int, anchor_size=None,
|
||||
anchor_sha=None, cur_sha=None) -> dict:
|
||||
return _call("dl_check", {"label": label, "prev_size": prev_size,
|
||||
"cur_size": cur_size, "anchor_size": anchor_size,
|
||||
"anchor_sha": anchor_sha, "cur_sha": cur_sha})
|
||||
|
||||
|
||||
def auto_fix_decision(label: str, anchored: bool, has_last_good: bool) -> dict:
|
||||
return _call("dl_auto_fix", {"label": label, "anchored": anchored,
|
||||
"has_last_good": has_last_good})
|
||||
|
||||
|
||||
def ledger_path_for(index_file: str) -> str:
|
||||
return _call("dl_ledger_path", {"index_file": index_file})
|
||||
|
||||
|
||||
def snapshot_entry(blocks: dict, when: str) -> dict:
|
||||
return _call("dl_snapshot_entry", {"blocks": blocks, "when": when})
|
||||
|
||||
|
||||
def anchor_record(content: str, reason: str, when: str) -> dict:
|
||||
return _call("dl_anchor_record", {"content": content, "reason": reason,
|
||||
"when": when})
|
||||
|
||||
|
||||
def check_alerts(ledger: dict, blocks: dict) -> list:
|
||||
return _call("dl_check_alerts", {"ledger": ledger, "blocks": blocks})
|
||||
|
||||
|
||||
def fix_decision(ledger: dict, label: str, cur_sha=None) -> dict:
|
||||
return _call("dl_fix_decision", {"ledger": ledger, "label": label,
|
||||
"cur_sha": cur_sha})
|
||||
@@ -0,0 +1,109 @@
|
||||
#!/usr/bin/env python3
|
||||
"""ctypes loader for the Rust drift_resume core (dichotomy wave, #2479
|
||||
tranche). Mirrors dc_loader.py's contract:
|
||||
|
||||
FFI contract (doc-envelope law — every call returns {"ok", "value"|"err"}):
|
||||
* dr_classify_drift({claim, observation}) -> {record}
|
||||
* dr_reconcile({claims, observations}) -> {resume, drifts, blocking}
|
||||
* dr_detect_structural_drift({entities, files, dirs}) -> {drifts}
|
||||
|
||||
Boundary law: the observer callable (reconcile — it runs the actual
|
||||
check: test command, file read, git query) and the FILESYSTEM walk
|
||||
(detect_structural_drift: expected.exists() + the moved-twin rglob) stay
|
||||
in the Python facade (drift_resume.py); the kernel is a pure function of
|
||||
their results. The facade passes the observations and SORTED
|
||||
relative-path listings of repo_root (files + dirs — a directory at the
|
||||
source path satisfies the oracle's expected.exists()).
|
||||
|
||||
FFI segfault law: every symbol gets explicit argtypes + restype
|
||||
(c_void_p for returned char* — the default c_int truncates 64-bit
|
||||
pointers). Strings are freed via capability_scoring's cs_free_string —
|
||||
the kernel exports NO free symbol of its own (duplicate exports break
|
||||
the cdylib link when sibling crates share one process).
|
||||
|
||||
Override the .so with DR_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" / "drift_resume"
|
||||
_LIB_PATH = Path(
|
||||
os.environ.get("DR_RUST_LIB", _CRATE_DIR / "target" / "release" / "libdrift_resume.so")
|
||||
)
|
||||
|
||||
_SYMBOLS = (
|
||||
"dr_classify_drift",
|
||||
"dr_reconcile",
|
||||
"dr_detect_structural_drift",
|
||||
)
|
||||
|
||||
_lib = None
|
||||
|
||||
|
||||
class DriftResumeCoreUnavailable(FileNotFoundError):
|
||||
"""The Rust drift_resume core is not built. Callers fall back to Python."""
|
||||
|
||||
|
||||
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 DriftResumeCoreUnavailable(
|
||||
f"Rust drift_resume core not built: {_LIB_PATH} missing — run "
|
||||
f"`cargo build --release` in {_CRATE_DIR}"
|
||||
)
|
||||
lib = ctypes.CDLL(str(_LIB_PATH))
|
||||
for sym in _SYMBOLS:
|
||||
fn = getattr(lib, sym)
|
||||
fn.argtypes = [ctypes.c_void_p]
|
||||
fn.restype = ctypes.c_void_p
|
||||
_lib = lib
|
||||
return lib
|
||||
|
||||
|
||||
def _call(sym: str, payload: dict):
|
||||
lib = _load()
|
||||
data = json.dumps(payload).encode()
|
||||
buf = ctypes.create_string_buffer(data)
|
||||
ptr = getattr(lib, sym)(ctypes.cast(buf, ctypes.c_void_p))
|
||||
if not ptr:
|
||||
raise RuntimeError(f"{sym} returned null")
|
||||
out = ctypes.string_at(ptr).decode()
|
||||
# free via any sibling free symbol already in-process (capability_scoring)
|
||||
try:
|
||||
scoring = ctypes.CDLL(str(
|
||||
_REPO / "src" / "rust" / "capability_scoring" / "target" / "release" / "libcapability_scoring.so"
|
||||
))
|
||||
scoring.cs_free_string.argtypes = [ctypes.c_void_p]
|
||||
scoring.cs_free_string(ptr)
|
||||
except OSError:
|
||||
pass # leak-on-fallback only if capability_scoring absent; bounded
|
||||
doc = json.loads(out)
|
||||
if not doc.get("ok"):
|
||||
raise RuntimeError(doc.get("err", f"{sym} failed"))
|
||||
return doc["value"]
|
||||
|
||||
|
||||
def classify_drift(claim: dict, observation: dict) -> dict:
|
||||
return _call("dr_classify_drift",
|
||||
{"claim": claim, "observation": observation})["record"]
|
||||
|
||||
|
||||
def reconcile(claims: list, observations: list) -> dict:
|
||||
return _call("dr_reconcile",
|
||||
{"claims": claims, "observations": observations})
|
||||
|
||||
|
||||
def detect_structural_drift(entities: list, files: list, dirs: list) -> list:
|
||||
return _call("dr_detect_structural_drift",
|
||||
{"entities": entities, "files": files, "dirs": dirs})["drifts"]
|
||||
+135
-58
@@ -48,6 +48,13 @@ DB = memory_env.store_db()
|
||||
def ledger_path():
|
||||
# The ledger lives beside the index, so overriding INDEX_FILE (as the
|
||||
# curator gate and the canary do for isolation) moves the ledger too.
|
||||
if _rust_core():
|
||||
try:
|
||||
from rune import dl_loader
|
||||
if dl_loader.available():
|
||||
return dl_loader.ledger_path_for(INDEX_FILE)
|
||||
except Exception:
|
||||
pass # fallback below
|
||||
return os.path.join(os.path.dirname(INDEX_FILE), "drift_ledger.json")
|
||||
|
||||
|
||||
@@ -63,9 +70,15 @@ CONSTITUTION_LIMIT = 0.05 # 5% — anything more than a directive entry is drif
|
||||
BLOCKS = ("constitution", "persona", "human", "operating", "project")
|
||||
|
||||
|
||||
def _rust_core():
|
||||
"""RUNE_DL_CORE pin law: 'python' forces the oracle path, anything else
|
||||
(default 'auto') serves the Rust core when the cdylib is built."""
|
||||
return os.environ.get("RUNE_DL_CORE", "auto") != "python"
|
||||
|
||||
|
||||
def _sha(text):
|
||||
# Dichotomy batch 3: Rust core first (RUNE_DL_CORE=python pins fallback).
|
||||
if os.environ.get("RUNE_DL_CORE", "auto") != "python":
|
||||
if _rust_core():
|
||||
try:
|
||||
from rune import dl_loader
|
||||
if dl_loader.available():
|
||||
@@ -155,51 +168,54 @@ def surface_alert(msg, label, changed=""):
|
||||
logger.warning("Failed to write alert file: %s", e)
|
||||
|
||||
|
||||
def snapshot():
|
||||
ledger = load_ledger()
|
||||
now = datetime.now(timezone.utc).isoformat(timespec="seconds")
|
||||
def _snapshot_entry(blocks, now):
|
||||
"""Dichotomy wave: Rust core first (RUNE_DL_CORE=python pins fallback)."""
|
||||
if _rust_core():
|
||||
try:
|
||||
from rune import dl_loader
|
||||
if dl_loader.available():
|
||||
return dl_loader.snapshot_entry(blocks, now)
|
||||
except Exception:
|
||||
pass # fallback below
|
||||
entry = {"when": now, "blocks": {}}
|
||||
for label in BLOCKS:
|
||||
raw = read_block(label)
|
||||
raw = blocks[label]
|
||||
entry["blocks"][label] = {
|
||||
"sha": _sha(raw),
|
||||
"size": len(raw),
|
||||
"content": raw, # last-good content backup for diffing + retro-fix
|
||||
}
|
||||
ledger["last_snapshot"] = entry
|
||||
save_ledger(ledger)
|
||||
return entry
|
||||
|
||||
|
||||
def anchor(label, reason):
|
||||
"""Record that the current state of `label` is authorised (a user directive,
|
||||
a confirmed review, a 3-sighting promotion). Subsequent drift is measured
|
||||
from here — this is the re-anchor after legitimate change."""
|
||||
ledger = load_ledger()
|
||||
raw = read_block(label)
|
||||
ledger.setdefault("anchors", {})[label] = {
|
||||
def _anchor_record(raw, reason, now):
|
||||
if _rust_core():
|
||||
try:
|
||||
from rune import dl_loader
|
||||
if dl_loader.available():
|
||||
return dl_loader.anchor_record(raw, reason, now)
|
||||
except Exception:
|
||||
pass # fallback below
|
||||
return {
|
||||
"sha": _sha(raw),
|
||||
"size": len(raw),
|
||||
"content": raw, # authorised content backup for root-cause diffing
|
||||
"reason": reason,
|
||||
"when": datetime.now(timezone.utc).isoformat(timespec="seconds"),
|
||||
"when": now,
|
||||
}
|
||||
save_ledger(ledger)
|
||||
return f"anchored {label}: {reason}"
|
||||
|
||||
|
||||
def check(strict=False, autofix=True):
|
||||
ledger = load_ledger()
|
||||
snap = ledger.get("last_snapshot")
|
||||
if not snap:
|
||||
# No baseline yet — record one and treat as healthy (first contact).
|
||||
snapshot()
|
||||
print("no prior snapshot; baseline recorded")
|
||||
return 0
|
||||
|
||||
def _check_alerts(ledger, now_blocks):
|
||||
"""The check() alert loop: Rust core first, oracle loop as fallback."""
|
||||
if _rust_core():
|
||||
try:
|
||||
from rune import dl_loader
|
||||
if dl_loader.available():
|
||||
return dl_loader.check_alerts(ledger, now_blocks)
|
||||
except Exception:
|
||||
pass # fallback below
|
||||
alerts = []
|
||||
now_blocks = {label: read_block(label) for label in BLOCKS}
|
||||
prev = snap.get("blocks", {})
|
||||
prev = (ledger.get("last_snapshot") or {}).get("blocks", {})
|
||||
for label in BLOCKS:
|
||||
cur = now_blocks[label]
|
||||
cur_size = len(cur)
|
||||
@@ -245,6 +261,41 @@ def check(strict=False, autofix=True):
|
||||
"content_changed": content_changed,
|
||||
}
|
||||
)
|
||||
return alerts
|
||||
|
||||
|
||||
def snapshot():
|
||||
ledger = load_ledger()
|
||||
now = datetime.now(timezone.utc).isoformat(timespec="seconds")
|
||||
entry = _snapshot_entry({label: read_block(label) for label in BLOCKS}, now)
|
||||
ledger["last_snapshot"] = entry
|
||||
save_ledger(ledger)
|
||||
return entry
|
||||
|
||||
|
||||
def anchor(label, reason):
|
||||
"""Record that the current state of `label` is authorised (a user directive,
|
||||
a confirmed review, a 3-sighting promotion). Subsequent drift is measured
|
||||
from here — this is the re-anchor after legitimate change."""
|
||||
ledger = load_ledger()
|
||||
raw = read_block(label)
|
||||
ledger.setdefault("anchors", {})[label] = _anchor_record(
|
||||
raw, reason, datetime.now(timezone.utc).isoformat(timespec="seconds")
|
||||
)
|
||||
save_ledger(ledger)
|
||||
return f"anchored {label}: {reason}"
|
||||
|
||||
|
||||
def check(strict=False, autofix=True):
|
||||
ledger = load_ledger()
|
||||
snap = ledger.get("last_snapshot")
|
||||
if not snap:
|
||||
# No baseline yet — record one and treat as healthy (first contact).
|
||||
snapshot()
|
||||
print("no prior snapshot; baseline recorded")
|
||||
return 0
|
||||
|
||||
alerts = _check_alerts(ledger, {label: read_block(label) for label in BLOCKS})
|
||||
if alerts:
|
||||
for a in alerts:
|
||||
msg = (
|
||||
@@ -287,27 +338,23 @@ def auto_fix(label, ledger, strict):
|
||||
|
||||
Returns {action, reason}.
|
||||
"""
|
||||
if label == "constitution":
|
||||
return {"action": "alert-only", "reason": "constitution changes only by explicit directive; needs human review"}
|
||||
if label in ledger.get("anchors", {}):
|
||||
block_meta = ledger.get("last_snapshot", {}).get("blocks", {}).get(label)
|
||||
anchored = label in ledger.get("anchors", {})
|
||||
has_last_good = bool(block_meta and "content" in block_meta)
|
||||
result = _auto_fix_decision(label, anchored, has_last_good)
|
||||
if result["action"] == "re-anchored":
|
||||
# Authorised growth: re-anchor to the current (approved) state.
|
||||
raw = read_block(label)
|
||||
ledger.setdefault("anchors", {})[label] = {
|
||||
"sha": _sha(raw),
|
||||
"size": len(raw),
|
||||
"content": raw,
|
||||
"reason": "auto re-anchor after authorised growth",
|
||||
"when": datetime.now(timezone.utc).isoformat(timespec="seconds"),
|
||||
}
|
||||
ledger.setdefault("anchors", {})[label] = _anchor_record(
|
||||
raw, "auto re-anchor after authorised growth",
|
||||
datetime.now(timezone.utc).isoformat(timespec="seconds"),
|
||||
)
|
||||
save_ledger(ledger)
|
||||
return {"action": "re-anchored", "reason": "anchored block grew (authorised) — re-baselined"}
|
||||
# Unauthorised drift: restore to last-good content (new schema — restore
|
||||
# the store's always-on core entries for this topic).
|
||||
block_meta = ledger.get("last_snapshot", {}).get("blocks", {}).get(label)
|
||||
if block_meta and "content" in block_meta:
|
||||
elif result["action"] == "restored":
|
||||
# Unauthorised drift: restore to last-good content (new schema —
|
||||
# restore the store's always-on core entries for this topic).
|
||||
restore_topic(label, block_meta["content"])
|
||||
return {"action": "restored", "reason": "unauthorised drift — restored to last-good content"}
|
||||
return {"action": "alert-only", "reason": "no last-good content available to restore"}
|
||||
return result
|
||||
|
||||
|
||||
def diff_blocks(ref, cur):
|
||||
@@ -379,6 +426,44 @@ def restore_topic(label, content):
|
||||
logger.error("restore_topic('%s') failed: %s", label, e)
|
||||
|
||||
|
||||
def _auto_fix_decision(label, anchored, has_last_good):
|
||||
"""The auto-fix ROUTING decision (IO execution stays in auto_fix)."""
|
||||
if _rust_core():
|
||||
try:
|
||||
from rune import dl_loader
|
||||
if dl_loader.available():
|
||||
return dl_loader.auto_fix_decision(label, anchored, has_last_good)
|
||||
except Exception:
|
||||
pass # fallback below
|
||||
if label == "constitution":
|
||||
return {"action": "alert-only", "reason": "constitution changes only by explicit directive; needs human review"}
|
||||
if anchored:
|
||||
return {"action": "re-anchored", "reason": "anchored block grew (authorised) — re-baselined"}
|
||||
if has_last_good:
|
||||
return {"action": "restored", "reason": "unauthorised drift — restored to last-good content"}
|
||||
return {"action": "alert-only", "reason": "no last-good content available to restore"}
|
||||
|
||||
|
||||
def _fix_decision(ledger, label, cur_sha):
|
||||
"""The retroactive-fix gate decision (restore IO stays in fix)."""
|
||||
if _rust_core():
|
||||
try:
|
||||
from rune import dl_loader
|
||||
if dl_loader.available():
|
||||
return dl_loader.fix_decision(ledger, label, cur_sha)
|
||||
except Exception:
|
||||
pass # fallback below
|
||||
snap = ledger.get("last_snapshot", {})
|
||||
block_meta = snap.get("blocks", {}).get(label)
|
||||
if not block_meta or "content" not in block_meta:
|
||||
return {"fixed": False, "reason": f"no last-good content for {label}"}
|
||||
if label in ledger.get("anchors", {}):
|
||||
return {"fixed": False, "reason": f"{label} is anchored (authorised); use anchor to re-baseline instead of fix"}
|
||||
if cur_sha == block_meta.get("sha"):
|
||||
return {"fixed": False, "reason": f"{label} already matches last-good"}
|
||||
return {"fixed": True, "reason": f"{label} restored to last-good state"}
|
||||
|
||||
|
||||
def fix(label):
|
||||
"""RETROACTIVE FIX (#B): restore a drifted block to its last-good content.
|
||||
|
||||
@@ -390,20 +475,12 @@ def fix(label):
|
||||
ledger = load_ledger()
|
||||
snap = ledger.get("last_snapshot", {})
|
||||
block_meta = snap.get("blocks", {}).get(label)
|
||||
if not block_meta or "content" not in block_meta:
|
||||
return {"fixed": False, "reason": f"no last-good content for {label}"}
|
||||
# Never silently revert an anchored block — it was authorised.
|
||||
if label in ledger.get("anchors", {}):
|
||||
return {"fixed": False, "reason": f"{label} is anchored (authorised); use anchor to re-baseline instead of fix"}
|
||||
good = block_meta["content"]
|
||||
cur = read_block(label)
|
||||
if _sha(cur) == block_meta["sha"]:
|
||||
return {"fixed": False, "reason": f"{label} already matches last-good"}
|
||||
# New schema: restore the store's always-on entries for the topic (the old
|
||||
# markdown block files are retired).
|
||||
restore_topic(label, good)
|
||||
journal(f"{label} restored to last-good content (retroactive drift fix)")
|
||||
return {"fixed": True, "reason": f"{label} restored to last-good state"}
|
||||
result = _fix_decision(ledger, label, _sha(cur))
|
||||
if result.get("fixed"):
|
||||
restore_topic(label, block_meta["content"])
|
||||
journal(f"{label} restored to last-good content (retroactive drift fix)")
|
||||
return result
|
||||
|
||||
|
||||
def handle_hook(event, data=None):
|
||||
|
||||
@@ -22,6 +22,7 @@ Reality wins unless the claim carries explicit user-locked authority
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
from typing import Callable
|
||||
|
||||
EXPECTED = "EXPECTED" # checkpoint older than reality; benign aging
|
||||
@@ -33,6 +34,23 @@ UNKNOWN = "UNKNOWN" # cannot decide — treated as blocking
|
||||
BLOCKING_CLASSES = (MATERIAL, CONFLICTING, UNKNOWN)
|
||||
|
||||
|
||||
def _dr_core():
|
||||
"""Dichotomy wave #2479: Rust core first (RUNE_DR_CORE=python pins the
|
||||
Python fallback — parity harnesses). The observer callable (reconcile)
|
||||
and the FILESYSTEM walk (detect_structural_drift) stay here; the
|
||||
kernel is a pure function of their results."""
|
||||
if os.environ.get("RUNE_DR_CORE", "auto") == "python":
|
||||
return None
|
||||
try:
|
||||
from rune import dr_loader
|
||||
|
||||
if dr_loader.available():
|
||||
return dr_loader
|
||||
except ImportError:
|
||||
pass
|
||||
return None
|
||||
|
||||
|
||||
def classify_drift(claim: dict, observation: dict) -> dict:
|
||||
"""Classify one claim-vs-observation pair. Deterministic:
|
||||
verification evidence beats prose; explicit beats inferred.
|
||||
@@ -42,6 +60,9 @@ def classify_drift(claim: dict, observation: dict) -> dict:
|
||||
observation: {"actual": str|None, "checked_by": str,
|
||||
"matches": bool|None}
|
||||
"""
|
||||
core = _dr_core()
|
||||
if core is not None:
|
||||
return core.classify_drift(claim, observation)
|
||||
if observation.get("matches") is True:
|
||||
return {"claim": claim.get("claim", ""),
|
||||
"class": EXPECTED if claim.get("stale_ok") else "CONFIRMED",
|
||||
@@ -67,6 +88,10 @@ def reconcile(claims: list[dict], observer: Callable[[dict], dict]) -> dict:
|
||||
Returns {"resume": bool, "drifts": [...], "blocking": [...]}. The
|
||||
resume decision: drift-aware, not blind — ANY blocking-class drift
|
||||
stops dependent work until reconciled."""
|
||||
core = _dr_core()
|
||||
if core is not None:
|
||||
observations = [observer(c) for c in claims]
|
||||
return core.reconcile(claims, observations)
|
||||
drifts = [classify_drift(c, observer(c)) for c in claims]
|
||||
blocking = [d for d in drifts if d["class"] in BLOCKING_CLASSES]
|
||||
return {"resume": not blocking, "drifts": drifts,
|
||||
@@ -84,6 +109,16 @@ def detect_structural_drift(model, repo_root) -> list[dict]:
|
||||
drift records in the classify_drift vocabulary; reality wins unless
|
||||
the claim is user-locked (authority == 'user' on the entity's
|
||||
evidence or identities.user_locked)."""
|
||||
core = _dr_core()
|
||||
if core is not None:
|
||||
from pathlib import Path as _P
|
||||
root = _P(repo_root)
|
||||
files = sorted(str(p.relative_to(root))
|
||||
for p in root.rglob("*") if p.is_file())
|
||||
dirs = sorted(str(p.relative_to(root))
|
||||
for p in root.rglob("*") if p.is_dir())
|
||||
return core.detect_structural_drift(model.doc["entities"], files,
|
||||
dirs)
|
||||
import os
|
||||
from pathlib import Path as _P
|
||||
root = _P(repo_root)
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
#!/usr/bin/env python3
|
||||
"""ctypes loader for the Rust execution_trace core (dichotomy wave, #2479
|
||||
tranche). Mirrors broker_loader.py's contract:
|
||||
|
||||
FFI contract (doc-envelope law — every call returns {"ok", "value"|"err"}):
|
||||
* et_read_outcome_events({lines}) -> outcomes dict
|
||||
* et_build_receipt({task, outcome_payload}) -> receipt dict
|
||||
* et_trace_task({task_id, task_record, outcomes}) -> receipt dict | None
|
||||
|
||||
The journal FILE I/O is host-side by nature (Law 7) and stays in the
|
||||
facade; the kernel owns the parse law (lines -> task_id→payload map) and
|
||||
the receipt assembly law.
|
||||
Free: capability_scoring's cs_free_string (shared process). Override the
|
||||
.so with ET_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" / "execution_trace"
|
||||
_LIB_PATH = Path(
|
||||
os.environ.get("ET_RUST_LIB", _CRATE_DIR / "target" / "release" / "libexecution_trace.so")
|
||||
)
|
||||
|
||||
_ERR_TYPES = {
|
||||
"ValueError": ValueError,
|
||||
"KeyError": KeyError,
|
||||
"AttributeError": AttributeError,
|
||||
"TypeError": TypeError,
|
||||
}
|
||||
|
||||
_lib = None
|
||||
|
||||
|
||||
class TraceCoreUnavailable(FileNotFoundError):
|
||||
"""The Rust execution-trace core is not built. Callers fall back to
|
||||
Python."""
|
||||
|
||||
|
||||
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 TraceCoreUnavailable(str(_LIB_PATH))
|
||||
lib = ctypes.CDLL(str(_LIB_PATH))
|
||||
for sym in ("et_read_outcome_events", "et_build_receipt", "et_trace_task"):
|
||||
getattr(lib, sym).argtypes = [ctypes.c_void_p]
|
||||
getattr(lib, sym).restype = ctypes.c_void_p
|
||||
try:
|
||||
lib.cs_free_string
|
||||
except AttributeError:
|
||||
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()
|
||||
data = json.dumps(payload).encode()
|
||||
buf = ctypes.create_string_buffer(data)
|
||||
ptr = getattr(lib, sym)(ctypes.cast(buf, ctypes.c_void_p))
|
||||
if not ptr:
|
||||
raise RuntimeError(f"{sym} returned null")
|
||||
out = ctypes.string_at(ptr).decode()
|
||||
free = getattr(lib, "_free", None)
|
||||
if free is not None:
|
||||
free(ptr)
|
||||
doc = json.loads(out)
|
||||
if not doc.get("ok"):
|
||||
etype = doc.get("type", "RuntimeError")
|
||||
raise _ERR_TYPES.get(etype, RuntimeError)(doc.get("err", f"{sym} failed"))
|
||||
return doc["value"]
|
||||
|
||||
|
||||
def read_outcome_events(lines: list) -> dict:
|
||||
return _call("et_read_outcome_events", {"lines": lines})
|
||||
|
||||
|
||||
def build_receipt(task: dict, outcome_payload: dict | None) -> dict:
|
||||
return _call("et_build_receipt", {"task": task,
|
||||
"outcome_payload": outcome_payload})
|
||||
|
||||
|
||||
def trace_task(task_id: str, task_record: dict | None,
|
||||
outcomes: dict | None) -> dict | None:
|
||||
return _call("et_trace_task", {"task_id": task_id,
|
||||
"task_record": task_record,
|
||||
"outcomes": outcomes})
|
||||
+49
-12
@@ -19,6 +19,24 @@ import os
|
||||
import sqlite3
|
||||
|
||||
|
||||
def _kernel():
|
||||
"""The Rust execution-trace core (et_loader) when RUNE_ET_CORE
|
||||
allows it, else None — the journal parse law and the receipt
|
||||
assembly law delegate there first and fall back to the Python body
|
||||
below (same semantics). The journal FILE I/O is host-side by nature
|
||||
(Law 7) and stays here."""
|
||||
if os.environ.get("RUNE_ET_CORE", "auto") == "python":
|
||||
return None
|
||||
try:
|
||||
from rune import et_loader
|
||||
|
||||
if et_loader.available():
|
||||
return et_loader
|
||||
except Exception: # noqa: BLE001 — kernel failure → Python floor
|
||||
return None
|
||||
return None
|
||||
|
||||
|
||||
def _journal_path():
|
||||
home = os.path.expanduser("~/.rune")
|
||||
return os.path.join(home, "journal", "events.jsonl")
|
||||
@@ -27,26 +45,39 @@ def _journal_path():
|
||||
def _read_outcome_events(journal_path=None):
|
||||
"""All task.outcome payloads from the journal (best-effort)."""
|
||||
path = journal_path or _journal_path()
|
||||
outcomes = {}
|
||||
try:
|
||||
with open(path, encoding="utf-8") as f:
|
||||
for line in f:
|
||||
try:
|
||||
rec = json.loads(line)
|
||||
except (json.JSONDecodeError, TypeError):
|
||||
continue
|
||||
if rec.get("event_type") == "task.outcome":
|
||||
payload = rec.get("payload", {})
|
||||
tid = payload.get("task_id", "")
|
||||
if tid:
|
||||
outcomes[tid] = payload
|
||||
lines = f.readlines()
|
||||
except OSError:
|
||||
pass
|
||||
return {}
|
||||
k = _kernel()
|
||||
if k is not None:
|
||||
try:
|
||||
return k.read_outcome_events(lines)
|
||||
except Exception: # noqa: BLE001 — kernel floor
|
||||
pass
|
||||
outcomes = {}
|
||||
for line in lines:
|
||||
try:
|
||||
rec = json.loads(line)
|
||||
except (json.JSONDecodeError, TypeError):
|
||||
continue
|
||||
if rec.get("event_type") == "task.outcome":
|
||||
payload = rec.get("payload", {})
|
||||
tid = payload.get("task_id", "")
|
||||
if tid:
|
||||
outcomes[tid] = payload
|
||||
return outcomes
|
||||
|
||||
|
||||
def build_receipt(task: dict, outcome_payload: dict | None = None) -> dict:
|
||||
"""Assemble the receipt for one completed task."""
|
||||
k = _kernel()
|
||||
if k is not None:
|
||||
try:
|
||||
return k.build_receipt(task, outcome_payload)
|
||||
except Exception: # noqa: BLE001 — kernel floor
|
||||
pass
|
||||
return {
|
||||
"task_id": task.get("task_id", ""),
|
||||
"description": task.get("description", ""),
|
||||
@@ -65,6 +96,12 @@ def trace_task(task_id: str, task_record: dict | None = None,
|
||||
journal_path=None) -> dict | None:
|
||||
"""The receipt for one task_id, or None when no outcome exists."""
|
||||
outcomes = _read_outcome_events(journal_path)
|
||||
k = _kernel()
|
||||
if k is not None:
|
||||
try:
|
||||
return k.trace_task(task_id, task_record, outcomes)
|
||||
except Exception: # noqa: BLE001 — kernel floor
|
||||
pass
|
||||
payload = outcomes.get(task_id)
|
||||
if payload is None and task_record is None:
|
||||
return None
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
#!/usr/bin/env python3
|
||||
"""ic_loader.py — ctypes loader for the Rust intent_continuity core
|
||||
(dichotomy wave #2479 tranche). Mirrors broker_loader.py:
|
||||
|
||||
FFI contract (doc-envelope law — every call returns {"ok", "value"|"err"}):
|
||||
* ic_create({...}) -> {"ledger", "record"}
|
||||
* ic_create_detour({...}) -> {"ledger", "record"}
|
||||
* ic_resolve({...}) -> {"ledger", "record"|null}
|
||||
* ic_resolve_detour({...}) -> {"ledger", "record"|null}
|
||||
* ic_supersede({...}) -> {"ledger", "record"}
|
||||
|
||||
Free: capability_scoring's cs_free_string (shared process). NEVER re-export
|
||||
a free symbol here. Override the .so with IC_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" / "intent_continuity"
|
||||
_LIB_PATH = Path(
|
||||
os.environ.get("IC_RUST_LIB", _CRATE_DIR / "target" / "release" / "libintent_continuity.so")
|
||||
)
|
||||
|
||||
_lib = None
|
||||
|
||||
|
||||
class IntentContinuityUnavailable(FileNotFoundError):
|
||||
"""The Rust intent core is not built. Callers fall back to Python
|
||||
(strangler law; RUNE_IC_CORE=python pins the fallback)."""
|
||||
|
||||
|
||||
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 IntentContinuityUnavailable(str(_LIB_PATH))
|
||||
lib = ctypes.CDLL(str(_LIB_PATH))
|
||||
for sym in ("ic_create", "ic_create_detour", "ic_resolve",
|
||||
"ic_resolve_detour", "ic_supersede"):
|
||||
fn = getattr(lib, sym)
|
||||
fn.argtypes = [ctypes.c_void_p]
|
||||
fn.restype = ctypes.c_void_p # char* — never default c_int
|
||||
# free comes from capability_scoring (same process); load it if not yet resident
|
||||
try:
|
||||
lib.cs_free_string
|
||||
except AttributeError:
|
||||
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()
|
||||
data = json.dumps(payload).encode()
|
||||
buf = ctypes.create_string_buffer(data)
|
||||
ptr = getattr(lib, sym)(ctypes.cast(buf, ctypes.c_void_p))
|
||||
if not ptr:
|
||||
raise RuntimeError(f"{sym} returned null")
|
||||
out = ctypes.string_at(ptr).decode()
|
||||
free = getattr(lib, "_free", None)
|
||||
if free is not None:
|
||||
free(ptr)
|
||||
doc = json.loads(out)
|
||||
if not doc.get("ok"):
|
||||
raise RuntimeError(doc.get("err", f"{sym} failed"))
|
||||
return doc["value"]
|
||||
+53
-13
@@ -106,6 +106,14 @@ class InferenceTrace:
|
||||
|
||||
def price_for(model: str) -> float | None:
|
||||
"""USD per 1M tokens (blended) — empty map until cloud providers land."""
|
||||
# Dichotomy wave #2479: Rust core first (RUNE_IT_CORE=python pins fallback).
|
||||
if os.environ.get("RUNE_IT_CORE", "auto") != "python":
|
||||
try:
|
||||
from rune import it_loader
|
||||
if it_loader.available():
|
||||
return it_loader.price_for(model)
|
||||
except Exception:
|
||||
pass # fallback below
|
||||
_PRICE_MAP: dict[str, float] = {
|
||||
# intentionally empty: local models are unpriced; cloud entries
|
||||
# land here with #29 (provider registry)
|
||||
@@ -122,22 +130,45 @@ def record_trace(trace: InferenceTrace, trace_dir: Path | None = None) -> Path:
|
||||
path = _trace_path(trace_dir)
|
||||
path.parent.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
if path.exists() and path.stat().st_size > TRACE_ROTATE_BYTES:
|
||||
# I1 (general-182): the guard must test the SOURCE (newer) existing —
|
||||
# guarding on the destination silently collapses the chain to 1 backup
|
||||
# (.2 is never created, so .1->.2 never fires). Range covers
|
||||
# i = BACKUPS..1 so the oldest slot (.3 with BACKUPS=3) is reachable.
|
||||
for i in range(TRACE_BACKUPS, 0, -1):
|
||||
older = path.with_name(f"{path.name}.{i}")
|
||||
newer = path.with_name(f"{path.name}.{i - 1}")
|
||||
if i - 1 == 0:
|
||||
newer = path
|
||||
# Dichotomy wave #2479: Rust core first (RUNE_IT_CORE=python pins fallback).
|
||||
# The kernel plans the rotation (should + ordered replace pairs); the
|
||||
# facade performs the file operations and the line serialization.
|
||||
line: str | None = None
|
||||
plan: dict | None = None
|
||||
if os.environ.get("RUNE_IT_CORE", "auto") != "python":
|
||||
try:
|
||||
from rune import it_loader
|
||||
if it_loader.available():
|
||||
line = it_loader.serialize(asdict(trace))
|
||||
plan = it_loader.rotate_plan(
|
||||
path.name, TRACE_BACKUPS,
|
||||
path.stat().st_size if path.exists() else 0,
|
||||
TRACE_ROTATE_BYTES)
|
||||
except Exception:
|
||||
line, plan = None, None # fallback below
|
||||
if plan is not None and plan.get("should"):
|
||||
for pair in plan["pairs"]:
|
||||
newer = path.with_name(pair["from"])
|
||||
if newer.exists():
|
||||
os.replace(newer, older)
|
||||
os.replace(newer, path.with_name(pair["to"]))
|
||||
|
||||
line = json.dumps(asdict(trace), sort_keys=True, separators=(",", ":")) + "\n"
|
||||
if line is None:
|
||||
if path.exists() and path.stat().st_size > TRACE_ROTATE_BYTES:
|
||||
# I1 (general-182): the guard must test the SOURCE (newer) existing —
|
||||
# guarding on the destination silently collapses the chain to 1 backup
|
||||
# (.2 is never created, so .1->.2 never fires). Range covers
|
||||
# i = BACKUPS..1 so the oldest slot (.3 with BACKUPS=3) is reachable.
|
||||
for i in range(TRACE_BACKUPS, 0, -1):
|
||||
older = path.with_name(f"{path.name}.{i}")
|
||||
newer = path.with_name(f"{path.name}.{i - 1}")
|
||||
if i - 1 == 0:
|
||||
newer = path
|
||||
if newer.exists():
|
||||
os.replace(newer, older)
|
||||
|
||||
line = json.dumps(asdict(trace), sort_keys=True, separators=(",", ":"))
|
||||
with open(path, "a", encoding="utf-8") as handle:
|
||||
handle.write(line)
|
||||
handle.write(line + "\n")
|
||||
return path
|
||||
|
||||
|
||||
@@ -146,6 +177,15 @@ def read_traces(trace_dir: Path | None = None, limit: int | None = None) -> list
|
||||
path = _trace_path(trace_dir)
|
||||
if not path.exists():
|
||||
return []
|
||||
# Dichotomy wave #2479: Rust core first (RUNE_IT_CORE=python pins fallback).
|
||||
if os.environ.get("RUNE_IT_CORE", "auto") != "python":
|
||||
try:
|
||||
from rune import it_loader
|
||||
if it_loader.available():
|
||||
return it_loader.read_select(
|
||||
path.read_text(encoding="utf-8"), limit)
|
||||
except Exception:
|
||||
pass # fallback below
|
||||
lines = path.read_text(encoding="utf-8").splitlines()
|
||||
if limit is not None:
|
||||
lines = lines[-limit:]
|
||||
|
||||
@@ -3,7 +3,15 @@
|
||||
A deterministic state machine for tracking detached intents — the
|
||||
smallest primitive that prevents "new good idea = new active objective."
|
||||
Not an ontology; not Substrate. Test-first, reload-safe, no LLM needed
|
||||
for state transitions once classification is supplied."""
|
||||
for state transitions once classification is supplied.
|
||||
|
||||
Dichotomy wave #2479 tranche: the deterministic ledger transition core
|
||||
(create/create_detour/resolve/resolve_detour/supersede + record
|
||||
validation) is ported to FULL Rust (src/rust/intent_continuity, parity
|
||||
oracle frozen_ic_truth.json). The facade owns file persistence and the
|
||||
clock (now is passed per op); the Python implementation below is the
|
||||
strangler fallback, pinned with RUNE_IC_CORE=python.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -15,6 +23,35 @@ CLASSIFICATIONS = ("BLOCKER", "FIX", "EXTENSION", "SUPERSEDING", "ORTHOGONAL", "
|
||||
STATUSES = ("ACTIVE", "DETOUR", "PARKED", "RESOLVED", "SUPERSEDED", "DEFERRED")
|
||||
|
||||
|
||||
def _core():
|
||||
"""The Rust intent_continuity core when available (strangler: Python
|
||||
fallback is load-bearing). RUNE_IC_CORE=python pins the fallback."""
|
||||
import os
|
||||
if os.environ.get("RUNE_IC_CORE", "auto") == "python":
|
||||
return None
|
||||
try:
|
||||
from rune import ic_loader
|
||||
except Exception: # noqa: BLE001 — any loader failure means fallback, never a broken call
|
||||
return None
|
||||
try:
|
||||
return ic_loader if ic_loader.available() else None
|
||||
except Exception: # noqa: BLE001 — availability probe must never crash the fallback
|
||||
return None
|
||||
|
||||
|
||||
def _now() -> str:
|
||||
return time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime())
|
||||
|
||||
|
||||
def _raise_core_err(e: Exception):
|
||||
"""Map kernel error envelopes back onto the oracle's exception types
|
||||
(the kernel emits the oracle's exact exception strings)."""
|
||||
msg = str(e)
|
||||
if msg.startswith("KeyError: "):
|
||||
raise KeyError(msg[len("KeyError: "):]) from e
|
||||
raise ValueError(msg) from e
|
||||
|
||||
|
||||
class IntentRecord:
|
||||
__slots__ = (
|
||||
"id",
|
||||
@@ -59,7 +96,7 @@ class IntentRecord:
|
||||
self.return_to = return_to
|
||||
self.unresolved_obligations = unresolved_obligations or []
|
||||
self.success_condition = success_condition
|
||||
self.created_at = time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime())
|
||||
self.created_at = _now()
|
||||
self.resolved_at = ""
|
||||
|
||||
def to_dict(self):
|
||||
@@ -92,9 +129,36 @@ class IntentLedger:
|
||||
self.path.parent.mkdir(parents=True, exist_ok=True)
|
||||
self.path.write_text(json.dumps({"intents": [r.to_dict() for r in self.records.values()]}, indent=1))
|
||||
|
||||
def _ledger_state(self) -> dict:
|
||||
return {"intents": [r.to_dict() for r in self.records.values()]}
|
||||
|
||||
def _absorb(self, core_result: dict):
|
||||
"""Rebuild records from the kernel's updated ledger (insertion
|
||||
order preserved) and persist, mirroring the oracle's save()."""
|
||||
self.records = {
|
||||
d["id"]: IntentRecord.from_dict(d)
|
||||
for d in core_result["ledger"].get("intents", [])
|
||||
}
|
||||
self.save()
|
||||
|
||||
def create(
|
||||
self, *, id, objective, classification, success_condition="", parent_intent="", unresolved_obligations=None
|
||||
):
|
||||
core = _core()
|
||||
if core is not None:
|
||||
try:
|
||||
res = core._call("ic_create", {
|
||||
"ledger": self._ledger_state(), "now": _now(),
|
||||
"id": id, "objective": objective,
|
||||
"classification": classification,
|
||||
"success_condition": success_condition,
|
||||
"parent_intent": parent_intent,
|
||||
"unresolved_obligations": list(unresolved_obligations or []),
|
||||
})
|
||||
except RuntimeError as e:
|
||||
_raise_core_err(e)
|
||||
self._absorb(res)
|
||||
return self.records[id]
|
||||
rec = IntentRecord(
|
||||
id=id,
|
||||
objective=objective,
|
||||
@@ -108,6 +172,20 @@ class IntentLedger:
|
||||
return rec
|
||||
|
||||
def create_detour(self, *, id, objective, classification, parent_intent, return_when, return_to):
|
||||
core = _core()
|
||||
if core is not None:
|
||||
try:
|
||||
res = core._call("ic_create_detour", {
|
||||
"ledger": self._ledger_state(), "now": _now(),
|
||||
"id": id, "objective": objective,
|
||||
"classification": classification,
|
||||
"parent_intent": parent_intent,
|
||||
"return_when": return_when, "return_to": return_to,
|
||||
})
|
||||
except RuntimeError as e:
|
||||
_raise_core_err(e)
|
||||
self._absorb(res)
|
||||
return self.records[id]
|
||||
parent = self.records.get(parent_intent)
|
||||
if not parent:
|
||||
raise ValueError(f"parent {parent_intent!r} not found")
|
||||
@@ -129,21 +207,45 @@ class IntentLedger:
|
||||
|
||||
def resolve(self, intent_id):
|
||||
"""Invariant 5: resolving a child does NOT resolve its parent."""
|
||||
core = _core()
|
||||
if core is not None:
|
||||
try:
|
||||
res = core._call("ic_resolve", {
|
||||
"ledger": self._ledger_state(), "now": _now(),
|
||||
"intent_id": intent_id,
|
||||
})
|
||||
except RuntimeError as e:
|
||||
_raise_core_err(e)
|
||||
self._absorb(res)
|
||||
rec = res.get("record")
|
||||
return self.records[rec["id"]] if rec else None
|
||||
rec = self.records.get(intent_id)
|
||||
if not rec or rec.status not in ("ACTIVE", "DETOUR"):
|
||||
return None
|
||||
rec.status = "RESOLVED"
|
||||
rec.resolved_at = time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime())
|
||||
rec.resolved_at = _now()
|
||||
self.save()
|
||||
return rec
|
||||
|
||||
def resolve_detour(self, detour_id):
|
||||
core = _core()
|
||||
if core is not None:
|
||||
try:
|
||||
res = core._call("ic_resolve_detour", {
|
||||
"ledger": self._ledger_state(), "now": _now(),
|
||||
"detour_id": detour_id,
|
||||
})
|
||||
except RuntimeError as e:
|
||||
_raise_core_err(e)
|
||||
self._absorb(res)
|
||||
rec = res.get("record")
|
||||
return self.records[rec["id"]] if rec else None
|
||||
d = self.records.get(detour_id)
|
||||
if not d or d.status not in ("DETOUR", "RESOLVED"):
|
||||
return None
|
||||
if d.status == "DETOUR":
|
||||
d.status = "RESOLVED"
|
||||
d.resolved_at = time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime())
|
||||
d.resolved_at = _now()
|
||||
parent = self.records.get(d.parent_intent)
|
||||
if parent and parent.status == "PARKED":
|
||||
parent.status = "ACTIVE"
|
||||
@@ -151,6 +253,20 @@ class IntentLedger:
|
||||
return parent
|
||||
|
||||
def supersede(self, old_id, new_id, *, objective, classification, carry_obligations=True):
|
||||
core = _core()
|
||||
if core is not None:
|
||||
try:
|
||||
res = core._call("ic_supersede", {
|
||||
"ledger": self._ledger_state(), "now": _now(),
|
||||
"old_id": old_id, "new_id": new_id,
|
||||
"objective": objective,
|
||||
"classification": classification,
|
||||
"carry_obligations": carry_obligations,
|
||||
})
|
||||
except RuntimeError as e:
|
||||
_raise_core_err(e)
|
||||
self._absorb(res)
|
||||
return self.records[new_id]
|
||||
old = self.records[old_id]
|
||||
if old.unresolved_obligations and not carry_obligations:
|
||||
raise ValueError(f"cannot supersede {old_id}: {len(old.unresolved_obligations)} unresolved obligations")
|
||||
|
||||
@@ -0,0 +1,121 @@
|
||||
#!/usr/bin/env python3
|
||||
"""ctypes loader for the Rust inference_trace core (dichotomy wave #2479,
|
||||
contract BUILDER-2026-10-12T13:55:29Z-148b8f-factory-wave). Mirrors
|
||||
broker_loader.py's contract:
|
||||
|
||||
FFI contract (doc-envelope law):
|
||||
* it_price_for({model}) -> null | number
|
||||
* it_trace_path({trace_dir?, rune_home}) -> {dir, path}
|
||||
* it_serialize({trace}) -> compact sorted JSONL line
|
||||
* it_rotate_plan({name, backups?, current_size?, rotate_bytes?})
|
||||
-> {should, pairs: [{from, to}]}
|
||||
* it_read_select({raw, limit?}) -> [records]
|
||||
* it_cli_tail({records, count?}) -> {out, rc}
|
||||
|
||||
Strings are freed via capability_scoring's cs_free_string (the whole
|
||||
process shares one allocator — this crate exports no free symbol).
|
||||
|
||||
Override the .so with IT_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" / "inference_trace"
|
||||
_LIB_PATH = Path(os.environ.get("IT_RUST_LIB", _CRATE_DIR / "target" / "release" / "libinference_trace.so"))
|
||||
|
||||
_SYMBOLS = (
|
||||
"it_price_for",
|
||||
"it_trace_path",
|
||||
"it_serialize",
|
||||
"it_rotate_plan",
|
||||
"it_read_select",
|
||||
"it_cli_tail",
|
||||
)
|
||||
|
||||
_lib = None
|
||||
|
||||
|
||||
class InferenceTraceUnavailable(FileNotFoundError):
|
||||
"""The Rust inference-trace core is not built. Callers fall back to Python."""
|
||||
|
||||
|
||||
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 InferenceTraceUnavailable(str(_LIB_PATH))
|
||||
lib = ctypes.CDLL(str(_LIB_PATH))
|
||||
for sym in _SYMBOLS:
|
||||
fn = getattr(lib, sym)
|
||||
fn.argtypes = [ctypes.c_void_p]
|
||||
fn.restype = ctypes.c_void_p
|
||||
_lib = lib
|
||||
return lib
|
||||
|
||||
|
||||
def _call(sym: str, payload: dict):
|
||||
lib = _load()
|
||||
data = json.dumps(payload, default=str).encode()
|
||||
buf = ctypes.create_string_buffer(data)
|
||||
ptr = getattr(lib, sym)(ctypes.cast(buf, ctypes.c_void_p))
|
||||
if not ptr:
|
||||
raise RuntimeError(f"{sym} returned null")
|
||||
out = ctypes.string_at(ptr).decode()
|
||||
try:
|
||||
scoring = ctypes.CDLL(str(
|
||||
_REPO / "src" / "rust" / "capability_scoring" / "target" / "release" / "libcapability_scoring.so"
|
||||
))
|
||||
scoring.cs_free_string.argtypes = [ctypes.c_void_p]
|
||||
scoring.cs_free_string(ptr)
|
||||
except OSError:
|
||||
pass
|
||||
doc = json.loads(out)
|
||||
if not doc.get("ok"):
|
||||
raise RuntimeError(doc.get("err", f"{sym} failed"))
|
||||
return doc["value"]
|
||||
|
||||
|
||||
def price_for(model: str):
|
||||
return _call("it_price_for", {"model": model})
|
||||
|
||||
|
||||
def trace_path(trace_dir=None, rune_home: str = "") -> dict:
|
||||
payload = {"rune_home": rune_home}
|
||||
if trace_dir is not None:
|
||||
payload["trace_dir"] = str(trace_dir)
|
||||
return _call("it_trace_path", payload)
|
||||
|
||||
|
||||
def serialize(trace: dict) -> str:
|
||||
return _call("it_serialize", {"trace": trace})
|
||||
|
||||
|
||||
def rotate_plan(name: str, backups: int = 3, current_size: int = 0,
|
||||
rotate_bytes: int = 10_000_000) -> dict:
|
||||
return _call("it_rotate_plan", {"name": name, "backups": backups,
|
||||
"current_size": current_size,
|
||||
"rotate_bytes": rotate_bytes})
|
||||
|
||||
|
||||
def read_select(raw: str, limit=None):
|
||||
payload = {"raw": raw}
|
||||
if limit is not None:
|
||||
payload["limit"] = limit
|
||||
return _call("it_read_select", payload)
|
||||
|
||||
|
||||
def cli_tail(records: list, count=None) -> dict:
|
||||
payload = {"records": records}
|
||||
if count is not None:
|
||||
payload["count"] = count
|
||||
return _call("it_cli_tail", payload)
|
||||
+73
-2
@@ -23,6 +23,24 @@ from rune.capability_ledger import CapabilityLedger
|
||||
from rune.behaviour_ledger import BehaviourLedger
|
||||
|
||||
|
||||
def _kernel():
|
||||
"""The Rust iteration core (itr_loader) when RUNE_ITR_CORE allows it,
|
||||
else None — every public function delegates there first and falls
|
||||
back to the Python body below (same process, same semantics)."""
|
||||
import os
|
||||
|
||||
if os.environ.get("RUNE_ITR_CORE", "auto") == "python":
|
||||
return None
|
||||
try:
|
||||
from rune import itr_loader
|
||||
|
||||
if itr_loader.available():
|
||||
return itr_loader
|
||||
except Exception: # noqa: BLE001 — kernel failure → Python floor
|
||||
return None
|
||||
return None
|
||||
|
||||
|
||||
def _now() -> str:
|
||||
return time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime())
|
||||
|
||||
@@ -36,6 +54,15 @@ def context_packet(model, ledger: CapabilityLedger, target: str, *,
|
||||
Bounded and proportional: the whole graph is NEVER dumped. Local =
|
||||
the target's own ledger entry; connected = 1-hop structural
|
||||
neighbours with their ledgers on demand."""
|
||||
k = _kernel()
|
||||
if k is not None:
|
||||
try:
|
||||
return k.context_packet(
|
||||
model.doc, ledger.doc, target, intent=intent,
|
||||
basis_summary=basis_summary,
|
||||
max_local_files=max_local_files, now=_now())
|
||||
except Exception: # noqa: BLE001 — kernel failure → Python floor
|
||||
pass
|
||||
entity = model.get(target)
|
||||
if entity is None:
|
||||
return {"error": f"unknown structural target {target!r}"}
|
||||
@@ -88,6 +115,12 @@ def context_packet(model, ledger: CapabilityLedger, target: str, *,
|
||||
|
||||
def predictive_impact(model, target: str) -> dict:
|
||||
"""What MAY be affected BEFORE the change (structural closure)."""
|
||||
k = _kernel()
|
||||
if k is not None:
|
||||
try:
|
||||
return k.predictive_impact(model.doc, target)
|
||||
except Exception: # noqa: BLE001
|
||||
pass
|
||||
return model.impact_closure(target)
|
||||
|
||||
|
||||
@@ -96,6 +129,14 @@ def descriptive_impact(model, target: str, *,
|
||||
generation: str = "") -> dict:
|
||||
"""What was MATERIALLY affected AFTER the change — from actual
|
||||
evidence (diff/observations), never assumed from the graph alone."""
|
||||
k = _kernel()
|
||||
if k is not None:
|
||||
try:
|
||||
return k.descriptive_impact(
|
||||
model.doc, target, observed_changed=observed_changed,
|
||||
generation=generation, now=_now())
|
||||
except Exception: # noqa: BLE001
|
||||
pass
|
||||
predicted = model.impact_closure(target)
|
||||
predicted_scope = set(predicted.get("mutation_scope", []))
|
||||
observed = set(observed_changed)
|
||||
@@ -113,10 +154,20 @@ def descriptive_impact(model, target: str, *,
|
||||
def record_impact_delta(project_store: Path, delta: dict) -> Path:
|
||||
"""Persist predicted-vs-observed deltas — they compound into better
|
||||
future impact prediction (rule 19H)."""
|
||||
k = _kernel()
|
||||
if k is not None:
|
||||
try:
|
||||
line = k.record_delta_line(delta) + "\n"
|
||||
except Exception: # noqa: BLE001
|
||||
line = None
|
||||
else:
|
||||
line = None
|
||||
if line is None:
|
||||
import json
|
||||
line = json.dumps(delta, sort_keys=True) + "\n"
|
||||
p = Path(project_store) / "impact_deltas.jsonl"
|
||||
import json
|
||||
with p.open("a") as f:
|
||||
f.write(json.dumps(delta, sort_keys=True) + "\n")
|
||||
f.write(line)
|
||||
return p
|
||||
|
||||
|
||||
@@ -128,6 +179,15 @@ def invalidate_behaviours(model, behaviours: BehaviourLedger, *,
|
||||
"""Selective invalidation driven by STRUCTURAL truth: behaviours
|
||||
whose dependencies/owner intersect the changed closure reopen;
|
||||
everything else keeps its confidence (rule 19I)."""
|
||||
k = _kernel()
|
||||
if k is not None:
|
||||
try:
|
||||
behaviours.doc, recs = k.invalidate(
|
||||
behaviours.doc, sorted(changed_entities),
|
||||
generation, _now())
|
||||
return recs
|
||||
except Exception: # noqa: BLE001
|
||||
pass
|
||||
return behaviours.invalidate_for_change(
|
||||
changed_entities=changed_entities, generation=generation)
|
||||
|
||||
@@ -140,6 +200,17 @@ def post_change_obligations(model, target: str, *,
|
||||
) -> list[dict]:
|
||||
"""Machine-derived follow-up obligations from STRUCTURAL truth
|
||||
(rule 19K) — never an improvised generic checklist."""
|
||||
k = _kernel()
|
||||
if k is not None:
|
||||
try:
|
||||
return k.post_change_obligations(
|
||||
model.doc, target,
|
||||
changed=sorted(changed_entities)
|
||||
if changed_entities else None,
|
||||
behaviours_doc=behaviours.doc if behaviours is not None
|
||||
else None)
|
||||
except Exception: # noqa: BLE001
|
||||
pass
|
||||
changed = changed_entities or {target}
|
||||
obligations: list[dict] = []
|
||||
seen: set[tuple] = set()
|
||||
|
||||
@@ -0,0 +1,131 @@
|
||||
#!/usr/bin/env python3
|
||||
"""ctypes loader for the Rust iteration core (prefix ITR — the it_* wire prefix belongs to inference_trace) (dichotomy wave, #2479
|
||||
tranche). Mirrors broker_loader.py's contract:
|
||||
|
||||
FFI contract (doc-envelope law — every call returns {"ok", "value"|"err"}):
|
||||
* itr_context_packet({model, ledger, target, intent, basis_summary,
|
||||
max_local_files, now}) -> packet dict
|
||||
* itr_predictive_impact({model, target}) -> closure dict
|
||||
* itr_descriptive_impact({model, target, observed_changed,
|
||||
generation, now}) -> delta dict
|
||||
* itr_record_delta_line({delta}) -> line body
|
||||
* itr_invalidate({behaviours, changed, generation, now}) -> {doc, recs}
|
||||
* itr_post_change_obligations({model, target, changed, behaviours})
|
||||
-> [obligations]
|
||||
|
||||
Model/ledger/behaviours ride as their JSON docs; timestamps are facade
|
||||
ARGUMENTS (now). Free: capability_scoring's cs_free_string (shared
|
||||
process). Override the .so with ITR_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" / "iteration"
|
||||
_LIB_PATH = Path(
|
||||
os.environ.get("ITR_RUST_LIB", _CRATE_DIR / "target" / "release" / "libiteration.so")
|
||||
)
|
||||
|
||||
_ERR_TYPES = {
|
||||
"ValueError": ValueError,
|
||||
"KeyError": KeyError,
|
||||
"AttributeError": AttributeError,
|
||||
"TypeError": TypeError,
|
||||
}
|
||||
|
||||
_lib = None
|
||||
|
||||
|
||||
class IterationCoreUnavailable(FileNotFoundError):
|
||||
"""The Rust iteration core (prefix ITR — the it_* wire prefix belongs to inference_trace) is not built. Callers fall back to Python."""
|
||||
|
||||
|
||||
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 IterationCoreUnavailable(str(_LIB_PATH))
|
||||
lib = ctypes.CDLL(str(_LIB_PATH))
|
||||
for sym in ("itr_context_packet", "itr_predictive_impact",
|
||||
"itr_descriptive_impact", "itr_record_delta_line",
|
||||
"itr_invalidate", "itr_post_change_obligations"):
|
||||
getattr(lib, sym).argtypes = [ctypes.c_void_p]
|
||||
getattr(lib, sym).restype = ctypes.c_void_p
|
||||
try:
|
||||
lib.cs_free_string
|
||||
except AttributeError:
|
||||
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()
|
||||
data = json.dumps(payload).encode()
|
||||
buf = ctypes.create_string_buffer(data)
|
||||
ptr = getattr(lib, sym)(ctypes.cast(buf, ctypes.c_void_p))
|
||||
if not ptr:
|
||||
raise RuntimeError(f"{sym} returned null")
|
||||
out = ctypes.string_at(ptr).decode()
|
||||
free = getattr(lib, "_free", None)
|
||||
if free is not None:
|
||||
free(ptr)
|
||||
doc = json.loads(out)
|
||||
if not doc.get("ok"):
|
||||
etype = doc.get("type", "RuntimeError")
|
||||
raise _ERR_TYPES.get(etype, RuntimeError)(doc.get("err", f"{sym} failed"))
|
||||
return doc["value"]
|
||||
|
||||
|
||||
def context_packet(model_doc: dict, ledger_doc: dict, target: str, *,
|
||||
intent: str = "", basis_summary=None,
|
||||
max_local_files: int = 12, now: str) -> dict:
|
||||
return _call("itr_context_packet", {
|
||||
"model": model_doc, "ledger": ledger_doc, "target": target,
|
||||
"intent": intent, "basis_summary": basis_summary,
|
||||
"max_local_files": max_local_files, "now": now})
|
||||
|
||||
|
||||
def predictive_impact(model_doc: dict, target: str) -> dict:
|
||||
return _call("itr_predictive_impact", {"model": model_doc, "target": target})
|
||||
|
||||
|
||||
def descriptive_impact(model_doc: dict, target: str, *,
|
||||
observed_changed: list, generation: str = "",
|
||||
now: str) -> dict:
|
||||
return _call("itr_descriptive_impact", {
|
||||
"model": model_doc, "target": target,
|
||||
"observed_changed": observed_changed, "generation": generation,
|
||||
"now": now})
|
||||
|
||||
|
||||
def record_delta_line(delta: dict) -> str:
|
||||
return _call("itr_record_delta_line", {"delta": delta})
|
||||
|
||||
|
||||
def invalidate(behaviours_doc: dict, changed: list, generation: str,
|
||||
now: str) -> tuple[dict, list]:
|
||||
out = _call("itr_invalidate", {"behaviours": behaviours_doc,
|
||||
"changed": changed,
|
||||
"generation": generation, "now": now})
|
||||
return out["doc"], out["recs"]
|
||||
|
||||
|
||||
def post_change_obligations(model_doc: dict, target: str, *,
|
||||
changed=None, behaviours_doc=None) -> list:
|
||||
return _call("itr_post_change_obligations", {
|
||||
"model": model_doc, "target": target, "changed": changed,
|
||||
"behaviours": behaviours_doc})
|
||||
Loaded 100 of 240 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user