code wiki / (root) / nx_install_pipeline.nx

nx_install_pipeline.nx source

↩ module page · 190 lines · 7814 B

1// nx_install_pipeline.nx -- end-to-end SA arc orchestrator. 2// 3// Composes the full self-assembly chain into one call: 4// probe + calibration + policy + workload-DAG 5// -> nx_select_joint_chain (joint variant selection) 6// -> total_cost recompute (sum of selected variants' scores) 7// -> nx_install_plan_capture (snapshot probe + calib + policy + 8// selection) 9// -> nx_install_hash_compute (SHA-256 of canonical bytes) 10// -> returns (filled NxInstallPlan, 32-byte install_hash, verdict) 11// 12// V1 entry point: `nx_install_pipeline_run_with_inputs` takes 13// pre-built probe + calibration so the smoke runs fast + 14// deterministic. A future `nx_install_pipeline_run` would call 15// nx_probe_run + nx_calibrate_run for real measurements; deferred 16// to keep this primitive a pure composer rather than a wiring layer. 17// 18// Total cost of selection is re-computed here (the joint selector 19// returns indices only; cost = Σ_i score(picked_variant_at_node_i, 20// policy)). This keeps SA-5's commit untouched (no widening of the 21// joint-selector return signature). 22// 23// Caller-allocated outputs: out_plan (NxInstallPlan), out_hash 24// (32-byte buffer). Pipeline does NOT allocate result memory on 25// behalf of the caller -- explicit ownership. 26// 27// genealogy_id: cardinal_2026-05-19_self_assembly + composition_pattern 28// lineage_id: substrate_install_pipeline_v1 29// 30// nx_capability_manifest: 31// variant_class: install_pipeline 32// variant_id: install_pipeline_v1_compose_chain_hash 33// requires_isa: [rv64imac, x86_64] 34// requires_syscalls: [mmap, clock_gettime_mono] 35// requires_ram_min_b: 16384 // plan + sha256 ctx + scratch 36// tier_floor: NX_TIER_MOBILE 37// tier_ceiling: NX_TIER_HPC 38// cost_model: 39// flops_per_n: 1.0 // dominated by select + hash 40// bytes_per_n: 256.0 // plan + canonical bytes 41// syscalls_per_n: 1.0 // one clock_gettime in capture 42// adversary_class: THREAT_AI_ADVERSARY 43// 44// nx_safety_envelope: 45// intended_use: "End-to-end SA arc orchestrator -- pure 46// composition over nx_select_joint_chain + 47// nx_install_plan_capture + nx_install_hash_compute" 48// sil_target: SIL2 49// evidence: [pure_composition, no_internal_allocation_of_outputs, 50// verdict_propagated_from_inner_calls, 51// reproducible_same_inputs] 52// verdict: NOT_YET_EVALUATED 53 54import "nx_syscalls.nx" 55import "nx_probe.nx" 56import "nx_calibrate.nx" 57import "nx_select.nx" 58import "nx_select_joint.nx" 59import "nx_install_plan.nx" 60import "nx_install_hash.nx" 61const NX_MAGIC_1048576: i64 = 1048576 62 63// ===== Verdict sealed enum ======================================= 64const NX_PIPELINE_OK: i64 = 0 65const NX_PIPELINE_BAD_INPUT: i64 = 1 66const NX_PIPELINE_SELECT_FAILED: i64 = 2 67const NX_PIPELINE_CAPTURE_FAILED: i64 = 3 68const NX_PIPELINE_HASH_FAILED: i64 = 4 69const NX_PIPELINE_N: i64 = 5 70 71func nx_pipeline_verdict_is_valid(v: i64) -> i64 { 72 if v < 0 { return 0 } 73 if v >= NX_PIPELINE_N { return 0 } 74 return 1 75} 76 77// ===== Total-cost recompute ====================================== 78// Sum the per-node score of the selected variant. Called only on a 79// successful joint-chain selection (all picks >= 0). 80func _pipeline_total_cost( 81 nodes_ptrs: *i64, n_nodes: i64, 82 selected_indices: *i64, 83 policy: *NxPolicy 84) -> i64 { 85 var total: i64 = 0 86 var i: i64 = 0 87 while i < n_nodes { 88 let pick: i64 = selected_indices[i] 89 if pick < 0 { i = i + 1; continue } 90 let node_addr: i64 = nodes_ptrs[i] 91 if node_addr == 0 { i = i + 1; continue } 92 let node: *NxJointNode = node_addr as *NxJointNode 93 let m_addr: i64 = node.manifest_ptrs[pick] 94 if m_addr == 0 { i = i + 1; continue } 95 let m: *NxManifest = m_addr as *NxManifest 96 total = total + nx_select_score(m, policy) 97 i = i + 1 98 } 99 return total 100} 101 102// ===== Selection -> plan_verdict mapping ========================= 103// Maps nx_select_joint_chain's return code into the plan-verdict 104// sealed enum. This keeps plan_verdict source-of-truth in 105// nx_install_plan.nx without leaking joint-selector verdicts. 106func _map_select_verdict_to_plan(select_rc: i64) -> i64 { 107 if select_rc == NX_SEL_JOINT_OK { return NX_PLAN_VERDICT_OK } 108 if select_rc == NX_SEL_JOINT_NO_FEASIBLE_PAIR { return NX_PLAN_VERDICT_NO_FEASIBLE } 109 if select_rc == NX_SEL_JOINT_BAD_INPUT { return NX_PLAN_VERDICT_BAD_INPUT } 110 return NX_PLAN_VERDICT_UNKNOWN 111} 112 113// ===== End-to-end pipeline ======================================= 114// 115// Args: 116// probe -- on-device fingerprint (already populated) 117// calib -- empirical cost model (already populated) 118// policy -- selector policy weights 119// nodes_ptrs -- *i64 array of NxJointNode pointers 120// n_nodes -- node count 121// out_plan -- caller-allocated NxInstallPlan 122// out_install_hash -- caller-allocated 32-byte buffer 123// 124// Returns NX_PIPELINE_* verdict. On non-OK return, out_plan and 125// out_install_hash may be in a partial state -- the verdict 126// indicates where in the chain failure occurred. 127 128func nx_install_pipeline_run_with_inputs( 129 probe: *NxProbeRecord, 130 calib: *NxCalibrationRecord, 131 policy: *NxPolicy, 132 nodes_ptrs: *i64, n_nodes: i64, 133 out_plan: *NxInstallPlan, 134 out_install_hash: *u8 135) -> i64 { 136 if (probe as i64) == 0 { return NX_PIPELINE_BAD_INPUT } 137 if (calib as i64) == 0 { return NX_PIPELINE_BAD_INPUT } 138 if (policy as i64) == 0 { return NX_PIPELINE_BAD_INPUT } 139 if (nodes_ptrs as i64) == 0 { return NX_PIPELINE_BAD_INPUT } 140 if (out_plan as i64) == 0 { return NX_PIPELINE_BAD_INPUT } 141 if (out_install_hash as i64) == 0 { return NX_PIPELINE_BAD_INPUT } 142 if n_nodes <= 0 { return NX_PIPELINE_BAD_INPUT } 143 144 // ----- Step 1: joint-chain selection ----- 145 let selected: *i64 = (sys_mmap(n_nodes * 8)) as *i64 146 let rc_sel: i64 = nx_select_joint_chain( 147 nodes_ptrs, n_nodes, probe, 148 calib.inferred_tier, 149 NX_MAGIC_1048576, // RAM proxy (1 MiB available); SA-6 will 150 // wire to a real probe field 151 0, // ISA-ext mask; SA-1.5 will populate 152 policy, 153 selected 154 ) 155 let plan_verdict: i64 = _map_select_verdict_to_plan(rc_sel) 156 if rc_sel != NX_SEL_JOINT_OK { 157 // Capture an empty-selection plan recording the verdict so 158 // downstream can audit WHY selection failed. 159 let rc_cap_fail: i64 = nx_install_plan_capture( 160 out_plan, probe, calib, policy, 161 nodes_ptrs, n_nodes, selected, 162 0, plan_verdict) 163 if rc_cap_fail != 0 { return NX_PIPELINE_CAPTURE_FAILED } 164 // No install_hash for a failed selection -- zero the buffer 165 // so callers don't accidentally trust junk. 166 var z: i64 = 0 167 while z < 32 { 168 out_install_hash[z] = 0 as u8 169 z = z + 1 170 } 171 return NX_PIPELINE_SELECT_FAILED 172 } 173 174 // ----- Step 2: total cost ----- 175 let total_cost: i64 = _pipeline_total_cost(nodes_ptrs, n_nodes, 176 selected, policy) 177 178 // ----- Step 3: capture plan ----- 179 let rc_cap: i64 = nx_install_plan_capture( 180 out_plan, probe, calib, policy, 181 nodes_ptrs, n_nodes, selected, 182 total_cost, plan_verdict) 183 if rc_cap != 0 { return NX_PIPELINE_CAPTURE_FAILED } 184 185 // ----- Step 4: compute install_hash ----- 186 let rc_hash: i64 = nx_install_hash_compute(out_plan, out_install_hash) 187 if rc_hash != NX_HASH_OK { return NX_PIPELINE_HASH_FAILED } 188 189 return NX_PIPELINE_OK 190}