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}