code wiki / (root) / nx_object_spec_match.nx

nx_object_spec_match.nx source

↩ module page · 335 lines · 11771 B

1// nx_object_spec_match.nx -- spec-compliance verification. 2// 3// CAPABILITY_COMPLETENESS: FULL 4// 5// Composes: 6// nx_object_spec (the catalog primitive) 7// caller-provided observed_parts list (upstream: nx_part_ 8// classifier, queued) 9// 10// Algorithm: 11// 1. For each REQUIRED part in spec: check if in observed list 12// 2. For each NEGATION in spec: check if in observed list 13// 3. Classify: 14// all required present + no negations -> MATCHES_SPEC 15// any negation present -> WRONG_CLASS 16// some required missing + no negations -> PART_MISSING 17// both required missing + negations -> PARTIAL_MATCH 18// 4. If WRONG_CLASS or PARTIAL_MATCH: scan all other specs for 19// the best-matching alternative (highest required-presence 20// score + no negations violated) 21// 22// genealogy_id: substrate_object_spec_match_2026_05_16 23// lineage_id: nx_object_spec_match_v1 24 25import "nx_syscalls.nx" 26import "nx_runtime.nx" 27import "nx_tier.nx" 28import "nx_object_spec.nx" 29const NX_MAGIC_1024: i64 = 1024 30 31// ===== sealed-verdict outcomes =================================== 32 33const NX_OSM_VERDICT_MATCHES_SPEC: nx_int = 0 34const NX_OSM_VERDICT_PART_MISSING: nx_int = 1 35const NX_OSM_VERDICT_WRONG_CLASS: nx_int = 2 36const NX_OSM_VERDICT_PARTIAL_MATCH: nx_int = 3 37const NX_OSM_VERDICT_NO_OBSERVATION: nx_int = 4 38 39const NX_OSM_NO_ALTERNATIVE: nx_int = -1 40 41// ===== match result struct ======================================= 42 43struct NxObjectMatchResult { 44 spec_id: nx_int, 45 verdict: nx_int, 46 n_required_matched: nx_int, 47 n_required_missing: nx_int, 48 n_negations_violated: nx_int, 49 missing_parts: *nx_int, 50 n_missing: nx_int, 51 violated_negations: *nx_int, 52 n_violated: nx_int, 53 suggested_alternative: nx_int, // spec_id of closest match if WRONG_CLASS 54 match_confidence_q10: nx_int, // n_matched / n_required (Q10) 55} 56 57const NX_OSM_RESULT_BYTES: nx_size = 96 58 59// ===== helpers ==================================================== 60 61func _osm_in_list(list: *nx_int, n: nx_int, val: nx_int) -> nx_int { 62 var i: nx_int = 0 63 while i < n { 64 if list[i] == val { return 1 } 65 i = i + 1 66 } 67 return 0 68} 69 70// ===== best-alternative finder =================================== 71// 72// Score for each candidate spec: 73// +1 per required-part observed 74// -2 per negation observed (heavy penalty if candidate spec 75// forbids one of the observed parts) 76// alternative is candidate with highest score above 0 + 77// zero negations violated 78// 79// Returns spec_id of best alternative, or NO_ALTERNATIVE if 80// no candidate scores positive. 81 82func _osm_find_alternative( 83 excluded_spec_id: nx_int, 84 observed_parts: *nx_int, n_observed: nx_int) -> nx_int { 85 86 var best_spec: nx_int = NX_OSM_NO_ALTERNATIVE 87 var best_score: nx_int = 0 88 var s: nx_int = 0 89 while s < NX_SPEC_N_SPECS { 90 if s != excluded_spec_id { 91 let candidate: *NxObjectSpec = nx_object_spec_get(s) 92 if candidate != (0 as *NxObjectSpec) { 93 // Count required matches. 94 var score: nx_int = 0 95 var has_violation: nx_int = 0 96 var i: nx_int = 0 97 while i < candidate.n_required { 98 let pid: nx_int = candidate.required_parts[i] 99 if _osm_in_list(observed_parts, n_observed, pid) == 1 { 100 score = score + 1 101 } 102 i = i + 1 103 } 104 var j: nx_int = 0 105 while j < candidate.n_negations { 106 let neg_pid: nx_int = candidate.negation_parts[j] 107 if _osm_in_list(observed_parts, n_observed, neg_pid) == 1 { 108 has_violation = 1 109 } 110 j = j + 1 111 } 112 if has_violation == 0 { 113 if score > best_score { 114 best_score = score 115 best_spec = s 116 } 117 } 118 } 119 } 120 s = s + 1 121 } 122 return best_spec 123} 124 125// ===== top-level match =========================================== 126 127func nx_object_spec_match( 128 spec_id: nx_int, 129 observed_parts: *nx_int, n_observed: nx_int) -> *NxObjectMatchResult { 130 131 let r_ptr: *u8 = sys_mmap(NX_OSM_RESULT_BYTES) 132 let r: *NxObjectMatchResult = r_ptr as *NxObjectMatchResult 133 r.spec_id = spec_id 134 r.n_required_matched = 0 135 r.n_required_missing = 0 136 r.n_negations_violated = 0 137 r.n_missing = 0 138 r.n_violated = 0 139 r.suggested_alternative = NX_OSM_NO_ALTERNATIVE 140 r.match_confidence_q10 = 0 141 142 let spec: *NxObjectSpec = nx_object_spec_get(spec_id) 143 if spec == (0 as *NxObjectSpec) { 144 r.verdict = NX_OSM_VERDICT_NO_OBSERVATION 145 return r 146 } 147 if n_observed <= 0 { 148 r.verdict = NX_OSM_VERDICT_NO_OBSERVATION 149 return r 150 } 151 152 // Allocate output lists. 153 let missing_bytes: nx_size = (spec.n_required as nx_size) * 8 154 let missing: *nx_int = (sys_mmap(missing_bytes)) as *nx_int 155 var miss_n: nx_int = 0 156 let violated_bytes: nx_size = (spec.n_negations as nx_size) * 8 157 let violated: *nx_int = (sys_mmap(violated_bytes)) as *nx_int 158 var viol_n: nx_int = 0 159 160 // 1. Required-part scan. 161 var n_matched: nx_int = 0 162 var i: nx_int = 0 163 while i < spec.n_required { 164 let pid: nx_int = spec.required_parts[i] 165 if _osm_in_list(observed_parts, n_observed, pid) == 1 { 166 n_matched = n_matched + 1 167 } else { 168 missing[miss_n] = pid 169 miss_n = miss_n + 1 170 } 171 i = i + 1 172 } 173 174 // 2. Negation scan. 175 var j: nx_int = 0 176 while j < spec.n_negations { 177 let neg_pid: nx_int = spec.negation_parts[j] 178 if _osm_in_list(observed_parts, n_observed, neg_pid) == 1 { 179 violated[viol_n] = neg_pid 180 viol_n = viol_n + 1 181 } 182 j = j + 1 183 } 184 185 r.n_required_matched = n_matched 186 r.n_required_missing = miss_n 187 r.n_negations_violated = viol_n 188 r.missing_parts = missing 189 r.n_missing = miss_n 190 r.violated_negations = violated 191 r.n_violated = viol_n 192 if spec.n_required > 0 { 193 r.match_confidence_q10 = (n_matched * NX_MAGIC_1024) / spec.n_required 194 } else { 195 r.match_confidence_q10 = NX_MAGIC_1024 196 } 197 198 // 3. Classify. 199 if viol_n == 0 { 200 if miss_n == 0 { 201 r.verdict = NX_OSM_VERDICT_MATCHES_SPEC 202 return r 203 } 204 r.verdict = NX_OSM_VERDICT_PART_MISSING 205 return r 206 } 207 if miss_n == 0 { 208 r.verdict = NX_OSM_VERDICT_WRONG_CLASS 209 } else { 210 r.verdict = NX_OSM_VERDICT_PARTIAL_MATCH 211 } 212 213 // 4. Find best alternative for WRONG_CLASS / PARTIAL_MATCH. 214 let alt: nx_int = _osm_find_alternative(spec_id, observed_parts, n_observed) 215 r.suggested_alternative = alt 216 return r 217} 218 219// ===== self-test ================================================= 220 221func main() -> nx_int { 222 // ---- CASE 1: D&D sword that IS a sword 223 // 224 // Observed: BLADE, EDGE, CROSSGUARD, GRIP, POMMEL, POINT 225 // Spec: SWORD_LONG (requires BLADE/EDGE/CROSSGUARD/GRIP/POMMEL) 226 // Expected: MATCHES_SPEC 227 let obs1: *nx_int = (sys_mmap(64)) as *nx_int 228 obs1[0] = NX_PT_BLADE 229 obs1[1] = NX_PT_EDGE 230 obs1[2] = NX_PT_CROSSGUARD 231 obs1[3] = NX_PT_GRIP 232 obs1[4] = NX_PT_POMMEL 233 obs1[5] = NX_PT_POINT 234 let r1: *NxObjectMatchResult = nx_object_spec_match( 235 NX_SPEC_SWORD_LONG, obs1, 6) 236 if r1 == (0 as *NxObjectMatchResult) { return 1 } 237 if r1.verdict != NX_OSM_VERDICT_MATCHES_SPEC { return 2 } 238 if r1.n_required_matched != 5 { return 3 } 239 if r1.n_negations_violated != 0 { return 4 } 240 if r1.match_confidence_q10 != NX_MAGIC_1024 { return 5 } 241 242 // ---- CASE 2: "DnD sword" that's actually a bubble wand 243 // 244 // Observed: HANDLE_STICK, LOOP, FILM 245 // Spec: SWORD_LONG 246 // Expected: WRONG_CLASS (LOOP + FILM are sword negations) + 247 // PARTIAL_MATCH actually since required parts are 248 // also missing. Both negations violated AND 249 // required missing -> PARTIAL_MATCH. 250 let obs2: *nx_int = (sys_mmap(64)) as *nx_int 251 obs2[0] = NX_PT_HANDLE_STICK 252 obs2[1] = NX_PT_LOOP 253 obs2[2] = NX_PT_FILM 254 let r2: *NxObjectMatchResult = nx_object_spec_match( 255 NX_SPEC_SWORD_LONG, obs2, 3) 256 if r2.verdict != NX_OSM_VERDICT_PARTIAL_MATCH { return 10 } 257 if r2.n_negations_violated < 2 { return 11 } 258 if r2.n_required_missing != 5 { return 12 } 259 // The suggested alternative should be BUBBLE_WAND (it has 260 // HANDLE_STICK + LOOP required and FILM optional, no violations). 261 if r2.suggested_alternative != NX_SPEC_BUBBLE_WAND { return 13 } 262 263 // ---- CASE 3: ORC that IS an orc 264 // 265 // Observed: HEAD, TUSK, FANG 266 // Spec: ORC_STANDARD 267 // Expected: MATCHES_SPEC 268 let obs3: *nx_int = (sys_mmap(64)) as *nx_int 269 obs3[0] = NX_PT_HEAD 270 obs3[1] = NX_PT_TUSK 271 obs3[2] = NX_PT_FANG 272 let r3: *NxObjectMatchResult = nx_object_spec_match( 273 NX_SPEC_ORC_STANDARD, obs3, 3) 274 if r3.verdict != NX_OSM_VERDICT_MATCHES_SPEC { return 20 } 275 276 // ---- CASE 4: ORC prompt got an ELF (pointed ear instead of tusks) 277 // 278 // Observed: HEAD, POINTED_EAR 279 // Spec: ORC_STANDARD (negates POINTED_EAR) 280 // Expected: WRONG_CLASS (negation hit + required TUSK missing) 281 // Wait: with TUSK missing AND POINTED_EAR negation 282 // hit, that's PARTIAL_MATCH (both miss + violate). 283 // Suggested alternative: ELF_HIGH 284 let obs4: *nx_int = (sys_mmap(64)) as *nx_int 285 obs4[0] = NX_PT_HEAD 286 obs4[1] = NX_PT_POINTED_EAR 287 let r4: *NxObjectMatchResult = nx_object_spec_match( 288 NX_SPEC_ORC_STANDARD, obs4, 2) 289 if r4.verdict != NX_OSM_VERDICT_PARTIAL_MATCH { return 30 } 290 if r4.suggested_alternative != NX_SPEC_ELF_HIGH { return 31 } 291 292 // ---- CASE 5: PART_MISSING case (required missing, no violation) 293 // 294 // Observed: BLADE, GRIP, POMMEL (no CROSSGUARD, no EDGE) 295 // Spec: SWORD_LONG 296 // Expected: PART_MISSING (no negations hit; some required absent) 297 let obs5: *nx_int = (sys_mmap(64)) as *nx_int 298 obs5[0] = NX_PT_BLADE 299 obs5[1] = NX_PT_GRIP 300 obs5[2] = NX_PT_POMMEL 301 let r5: *NxObjectMatchResult = nx_object_spec_match( 302 NX_SPEC_SWORD_LONG, obs5, 3) 303 if r5.verdict != NX_OSM_VERDICT_PART_MISSING { return 40 } 304 if r5.n_required_missing != 2 { return 41 } // EDGE + CROSSGUARD 305 306 // ---- CASE 6: WRONG_CLASS pure (all required present + negation hit) 307 // 308 // Observed: BLADE, EDGE, CROSSGUARD, GRIP, POMMEL, LOOP 309 // Spec: SWORD_LONG 310 // Expected: WRONG_CLASS (required all present BUT LOOP violates) 311 let obs6: *nx_int = (sys_mmap(64)) as *nx_int 312 obs6[0] = NX_PT_BLADE 313 obs6[1] = NX_PT_EDGE 314 obs6[2] = NX_PT_CROSSGUARD 315 obs6[3] = NX_PT_GRIP 316 obs6[4] = NX_PT_POMMEL 317 obs6[5] = NX_PT_LOOP 318 let r6: *NxObjectMatchResult = nx_object_spec_match( 319 NX_SPEC_SWORD_LONG, obs6, 6) 320 if r6.verdict != NX_OSM_VERDICT_WRONG_CLASS { return 50 } 321 if r6.n_negations_violated < 1 { return 51 } 322 323 // ---- CASE 7: NO_OBSERVATION (empty observed list) 324 let obs7: *nx_int = (sys_mmap(8)) as *nx_int 325 let r7: *NxObjectMatchResult = nx_object_spec_match( 326 NX_SPEC_SWORD_LONG, obs7, 0) 327 if r7.verdict != NX_OSM_VERDICT_NO_OBSERVATION { return 60 } 328 329 // ---- CASE 8: invalid spec_id 330 let r8: *NxObjectMatchResult = nx_object_spec_match( 331 NX_SPEC_N_SPECS, obs1, 5) 332 if r8.verdict != NX_OSM_VERDICT_NO_OBSERVATION { return 70 } 333 334 return 0 335}