code wiki / (root) / nx_substrate_evolve.nx

nx_substrate_evolve.nx source

↩ module page · 506 lines · 20464 B

1// nx_substrate_evolve.nx -- generational substrate audit + watch-list. 2// 3// Walks the substrate as evolutionary generations: 4// 5// Gen 0 -- "God" : axioms (nx_axioms.nx, NX_AX_* constants) 6// Gen 1 -- "Adam" : foundational primitives that depend only 7// on syscalls + axioms (runtime.nx, nx_ascii.nx, 8// nx_dirent.nx, nx_fcntl.nx, ...) 9// Gen 2..N -- "Children": primitives whose only-import frontier sits 10// in earlier generations. 11// 12// For each generation we emit: 13// - members (the .nx files at this generation) 14// - genealogy completeness (% with genealogy_id + lineage_id + axioms) 15// - the WATCH LIST for the next generation: gaps / duplicates / 16// queued items that the NEXT gen must address. 17// 18// This makes the algorithm-evolving loop a generational lineage: 19// Gen N inherits axioms from God, builds on Gen N-1, and emits 20// concrete obligations for Gen N+1. 21// 22// Output JSONL to stdout: 23// {"gen":N,"members":[...],"complete":K,"total":M, 24// "watch_next":[{"kind":"...","what":"..."}, ...]} 25// {"summary":true,"n_generations":N,"total_files":M, ...} 26// 27// genealogy_id: dawkins_1976_meme + kuhn_1962_paradigm 28// + classical_topological_sort 29// lineage_id: generational_lineage_audit 30// axioms: NX_AX_ORD_LEAST_UPPER_BOUND (generation number is well- 31// defined: smallest n such that all deps are in Gen < n) 32// + NX_AX_REL_TRANSITIVITY (depends-on is transitive) 33 34// nx_safety_envelope: 35// intended_use: AUTO_APPLIED -- primitive-specific tuning queued 36// sil_target: SIL1 37// evidence: [bulk_applied_2026-05-16, see-file-comment-for-detail] 38// verdict: NOT_YET_EVALUATED 39 40import "syscalls.nx" 41import "runtime.nx" 42import "nx_axioms.nx" 43import "nx_dirent.nx" 44import "nx_fcntl.nx" 45import "nx_lex.nx" 46import "nx_ascii.nx" 47 48const NX_EV_MAX_FILES: i64 = 400 49const NX_EV_MAX_IMPORTS: i64 = 32 50const NX_EV_MAX_NAME: i64 = 96 51const NX_EV_RECORD_BYTES: i64 = 56 // 7 i64 fields 52const NX_EV_DIR_BUF: i64 = 16384 53const NX_EV_GEN_UNKNOWN: i64 = -1 54 55struct EvolveRecord { 56 name: *u8, // filename (no path, no .nx) 57 n_imports: i64, 58 import_idx: i64, // offset into imports_to[] array 59 has_genealogy: i64, 60 has_lineage: i64, 61 has_axioms: i64, 62 generation: i64, 63} 64 65// ===== filename pattern + module extraction ============================= 66 67func ev_name_matches(name: *u8, name_len: i64) -> i64 { 68 if name_len < 6 { return 0 } // nx_*.nx minimum 69 if name[0] != 110 { return 0 } // 'n' 70 if name[1] != 120 { return 0 } // 'x' 71 if name[2] != 95 { return 0 } // '_' 72 if name[name_len - 3] != 46 { return 0 } // '.' 73 if name[name_len - 2] != 110 { return 0 } // 'n' 74 if name[name_len - 1] != 120 { return 0 } // 'x' 75 // reject _test.nx 76 if name_len >= 8 { 77 if name[name_len - 8] == 95 { 78 if name[name_len - 7] == 116 { 79 if name[name_len - 6] == 101 { 80 if name[name_len - 5] == 115 { 81 if name[name_len - 4] == 116 { 82 return 0 83 } 84 } 85 } 86 } 87 } 88 } 89 return 1 90} 91 92// Convert filename "nx_geom.nx" to module key "nx_geom" (null-terminated). 93func ev_module_from_filename(name: *u8, name_len: i64, out: *u8) -> i64 { 94 let mod_len: i64 = name_len - 3 // strip ".nx" 95 var i: i64 = 0 96 while i < mod_len { 97 out[i] = name[i] 98 i = i + 1 99 } 100 out[mod_len] = 0 101 return mod_len 102} 103 104// ===== file header scan ================================================ 105// 106// Reads the file content and: 107// - extracts every `import "..."` line into module-name list 108// - sets has_genealogy / has_lineage / has_axioms based on presence 109// of those tags in header comments 110// 111// Returns 0 on success, -1 on read error. 112 113func ev_line_contains(buf: *u8, start: i64, end: i64, needle: *u8) -> i64 { 114 let n_len: i64 = strlen(needle) 115 if end - start < n_len { return 0 } 116 var i: i64 = start 117 while i <= end - n_len { 118 let buf_at_i: *u8 = ((buf as i64) + i) as *u8 119 if strneq(buf_at_i, needle, n_len) == 1 { return 1 } 120 i = i + 1 121 } 122 return 0 123} 124 125func ev_scan_header(file_path: *u8, rec: *EvolveRecord, 126 imports_to: *i64, all_modules: *u8, 127 n_modules: i64) -> i64 { 128 let out_len: *i64 = (sys_mmap(8)) as *i64 129 out_len[0] = 0 130 let buf: *u8 = sys_read_file(file_path, out_len) 131 if (buf as i64) == 0 { return -1 } 132 let n: i64 = out_len[0] 133 134 rec.n_imports = 0 135 rec.has_genealogy = 0 136 rec.has_lineage = 0 137 rec.has_axioms = 0 138 139 // Walk line by line. 140 var pos: i64 = 0 141 while pos < n { 142 // find end of line (proper done-flag pattern) 143 var eol: i64 = pos 144 var done_line: i64 = 0 145 while done_line == 0 { 146 if eol >= n { done_line = 1 } 147 if done_line == 0 { 148 if buf[eol] == 10 { done_line = 1 } 149 if done_line == 0 { eol = eol + 1 } 150 } 151 } 152 let line_len: i64 = eol - pos 153 154 // Check for `import "<name>"` 155 if line_len > 10 { 156 // match "import " 157 if buf[pos] == 105 { // 'i' 158 if buf[pos+1] == 109 { // 'm' 159 if buf[pos+2] == 112 { // 'p' 160 if buf[pos+3] == 111 { // 'o' 161 if buf[pos+4] == 114 { // 'r' 162 if buf[pos+5] == 116 { // 't' 163 if buf[pos+6] == 32 { // ' ' 164 // find opening " (done-flag) 165 var p: i64 = pos + 7 166 var done_q: i64 = 0 167 while done_q == 0 { 168 if p >= eol { done_q = 1 } 169 if done_q == 0 { 170 if buf[p] == 34 { done_q = 1 } 171 if done_q == 0 { p = p + 1 } 172 } 173 } 174 if p < eol { 175 if buf[p] == 34 { 176 p = p + 1 177 let mod_start: i64 = p 178 var done_m: i64 = 0 179 while done_m == 0 { 180 if p >= eol { done_m = 1 } 181 if done_m == 0 { 182 if buf[p] == 34 { done_m = 1 } 183 if done_m == 0 { 184 if buf[p] == 46 { done_m = 1 } 185 if done_m == 0 { p = p + 1 } 186 } 187 } 188 } 189 let mod_end: i64 = p 190 // record this import (with done-flag for inner loop) 191 if rec.n_imports < NX_EV_MAX_IMPORTS { 192 var found_idx: i64 = -1 193 var mi: i64 = 0 194 var done_search: i64 = 0 195 while done_search == 0 { 196 if mi >= n_modules { done_search = 1 } 197 if done_search == 0 { 198 let cand_addr: i64 = (all_modules as i64) + mi * NX_EV_MAX_NAME 199 let cand: *u8 = cand_addr as *u8 200 let cand_len: i64 = strlen(cand) 201 if cand_len == mod_end - mod_start { 202 let buf_at_mod: *u8 = ((buf as i64) + mod_start) as *u8 203 if strneq(cand, buf_at_mod, cand_len) == 1 { 204 found_idx = mi 205 done_search = 1 206 } 207 } 208 if done_search == 0 { mi = mi + 1 } 209 } 210 } 211 if found_idx >= 0 { 212 imports_to[rec.import_idx + rec.n_imports] = found_idx 213 rec.n_imports = rec.n_imports + 1 214 } 215 } 216 } 217 } 218 } 219 } 220 } 221 } 222 } 223 } 224 } 225 } 226 227 // Check tag lines `// genealogy_id:`, `// lineage_id:`, `// axioms:` 228 if line_len > 4 { 229 if buf[pos] == 47 { // '/' 230 if buf[pos+1] == 47 { // '//' 231 // look for tag keywords 232 if rec.has_genealogy == 0 { 233 if ev_line_contains(buf, pos, eol, "genealogy_id:" as *u8) == 1 { 234 rec.has_genealogy = 1 235 } 236 } 237 if rec.has_lineage == 0 { 238 if ev_line_contains(buf, pos, eol, "lineage_id:" as *u8) == 1 { 239 rec.has_lineage = 1 240 } 241 } 242 if rec.has_axioms == 0 { 243 if ev_line_contains(buf, pos, eol, "axioms:" as *u8) == 1 { 244 rec.has_axioms = 1 245 } 246 } 247 } 248 } 249 } 250 251 pos = eol + 1 252 } 253 return 0 254} 255 256// ===== directory walk ==================================================== 257 258func ev_collect_modules(dir_path: *u8, all_modules: *u8, n_out: *i64) -> i64 { 259 let fd: i64 = nx_openat(NX_AT_FDCWD, dir_path, 260 NX_O_RDONLY | NX_O_DIRECTORY, 0) 261 if fd < 0 { return -1 } 262 let buf: *u8 = sys_mmap(NX_EV_DIR_BUF) 263 let dr_raw: *u8 = sys_mmap(NX_DIRENT_BYTES) 264 let dr: *NxDirent = dr_raw as *NxDirent 265 let mod_buf: *u8 = sys_mmap(NX_EV_MAX_NAME) 266 n_out[0] = 0 267 268 var batch: i64 = nx_dirent_read(fd, buf, NX_EV_DIR_BUF) 269 while batch > 0 { 270 var off: i64 = 0 271 while off < batch { 272 let next_off: i64 = nx_dirent_iter(buf, off, batch, dr) 273 if next_off <= 0 { off = batch + 1 } 274 if off <= batch { 275 let nm_len: i64 = nx_dirent_name_len(dr) 276 if ev_name_matches(dr.name, nm_len) == 1 { 277 if n_out[0] < NX_EV_MAX_FILES { 278 let dst_addr: i64 = (all_modules as i64) + n_out[0] * NX_EV_MAX_NAME 279 let dst: *u8 = dst_addr as *u8 280 ev_module_from_filename(dr.name, nm_len, dst) 281 n_out[0] = n_out[0] + 1 282 } 283 } 284 off = next_off 285 } 286 } 287 batch = nx_dirent_read(fd, buf, NX_EV_DIR_BUF) 288 } 289 sys_close(fd) 290 return 0 291} 292 293// ===== topological generation assignment ================================= 294// 295// generation(M) = 0 if M imports nothing (or only axioms/syscalls) 296// = 1 + max(generation(D) for D in imports(M)) otherwise 297// 298// We compute generations via iterative refinement (no full toposort 299// needed; just keep assigning until stable). For acyclic substrate 300// this converges quickly. 301 302func ev_assign_generations(records: *EvolveRecord, n_records: i64, 303 imports_to: *i64) -> i64 { 304 var i: i64 = 0 305 while i < n_records { 306 let r: *EvolveRecord = ((records as i64) + i * NX_EV_RECORD_BYTES) as *EvolveRecord 307 r.generation = NX_EV_GEN_UNKNOWN 308 i = i + 1 309 } 310 // Records with no imports get generation 0. 311 var changed: i64 = 1 312 var iter: i64 = 0 313 while changed == 1 { 314 changed = 0 315 if iter > 50 { changed = 0 } // safety bound 316 if iter <= 50 { 317 var idx: i64 = 0 318 while idx < n_records { 319 let r: *EvolveRecord = ((records as i64) + idx * NX_EV_RECORD_BYTES) as *EvolveRecord 320 if r.generation == NX_EV_GEN_UNKNOWN { 321 if r.n_imports == 0 { 322 r.generation = 0 323 changed = 1 324 } 325 if r.n_imports > 0 { 326 var max_gen: i64 = -1 327 var all_known: i64 = 1 328 var j: i64 = 0 329 while j < r.n_imports { 330 let dep_idx: i64 = imports_to[r.import_idx + j] 331 let d_addr: i64 = (records as i64) + dep_idx * NX_EV_RECORD_BYTES 332 let d: *EvolveRecord = d_addr as *EvolveRecord 333 if d.generation == NX_EV_GEN_UNKNOWN { all_known = 0 } 334 if d.generation > max_gen { max_gen = d.generation } 335 j = j + 1 336 } 337 if all_known == 1 { 338 r.generation = max_gen + 1 339 changed = 1 340 } 341 } 342 } 343 idx = idx + 1 344 } 345 } 346 iter = iter + 1 347 } 348 return iter 349} 350 351// ===== emit per-generation report ======================================= 352 353func ev_emit_generation(records: *EvolveRecord, n_records: i64, gen: i64) -> i64 { 354 var n_members: i64 = 0 355 var n_with_geneal: i64 = 0 356 var n_with_lineage: i64 = 0 357 var n_with_axioms: i64 = 0 358 var i: i64 = 0 359 while i < n_records { 360 let r: *EvolveRecord = ((records as i64) + i * NX_EV_RECORD_BYTES) as *EvolveRecord 361 if r.generation == gen { 362 n_members = n_members + 1 363 n_with_geneal = n_with_geneal + r.has_genealogy 364 n_with_lineage = n_with_lineage + r.has_lineage 365 n_with_axioms = n_with_axioms + r.has_axioms 366 } 367 i = i + 1 368 } 369 if n_members == 0 { return 0 } 370 371 print("{\"gen\":" as *u8) 372 print_i64(gen) 373 print(",\"n_members\":" as *u8) 374 print_i64(n_members) 375 print(",\"with_genealogy\":" as *u8) 376 print_i64(n_with_geneal) 377 print(",\"with_lineage\":" as *u8) 378 print_i64(n_with_lineage) 379 print(",\"with_axioms\":" as *u8) 380 print_i64(n_with_axioms) 381 print(",\"members\":[" as *u8) 382 383 var n_emitted: i64 = 0 384 var j: i64 = 0 385 while j < n_records { 386 let r: *EvolveRecord = ((records as i64) + j * NX_EV_RECORD_BYTES) as *EvolveRecord 387 if r.generation == gen { 388 if n_emitted > 0 { print("," as *u8) } 389 print("\"" as *u8) 390 print(r.name) 391 print("\"" as *u8) 392 n_emitted = n_emitted + 1 393 } 394 j = j + 1 395 } 396 print("],\"watch_next\":[" as *u8) 397 398 // Watch list: every member of this generation missing genealogy 399 // becomes a watch item for the next generation. 400 var n_watch: i64 = 0 401 var k: i64 = 0 402 while k < n_records { 403 let r: *EvolveRecord = ((records as i64) + k * NX_EV_RECORD_BYTES) as *EvolveRecord 404 if r.generation == gen { 405 if r.has_genealogy == 0 { 406 if n_watch > 0 { print("," as *u8) } 407 print("{\"kind\":\"missing_genealogy\",\"module\":\"" as *u8) 408 print(r.name) 409 print("\"}" as *u8) 410 n_watch = n_watch + 1 411 } 412 } 413 k = k + 1 414 } 415 println("]}" as *u8) 416 return n_members 417} 418 419// ===== entry ============================================================ 420 421func main() -> i64 { 422 let all_modules: *u8 = sys_mmap(NX_EV_MAX_FILES * NX_EV_MAX_NAME) 423 let n_modules: *i64 = (sys_mmap(8)) as *i64 424 let imports_to: *i64 = (sys_mmap(NX_EV_MAX_FILES * NX_EV_MAX_IMPORTS * 8)) as *i64 425 426 if ev_collect_modules("nxc2/runtime" as *u8, all_modules, n_modules) != 0 { 427 println("error: cannot open runtime directory" as *u8) 428 return 1 429 } 430 431 let n: i64 = n_modules[0] 432 let records_raw: *u8 = sys_mmap(n * NX_EV_RECORD_BYTES) 433 let records: *EvolveRecord = records_raw as *EvolveRecord 434 435 // Initialize records: each module's name, import_idx into shared array. 436 var i: i64 = 0 437 while i < n { 438 let r: *EvolveRecord = ((records as i64) + i * NX_EV_RECORD_BYTES) as *EvolveRecord 439 r.name = ((all_modules as i64) + i * NX_EV_MAX_NAME) as *u8 440 r.import_idx = i * NX_EV_MAX_IMPORTS 441 r.generation = NX_EV_GEN_UNKNOWN 442 i = i + 1 443 } 444 445 // Scan each module for imports + tags. 446 let path_buf: *u8 = sys_mmap(512) 447 var j: i64 = 0 448 while j < n { 449 let r: *EvolveRecord = ((records as i64) + j * NX_EV_RECORD_BYTES) as *EvolveRecord 450 // Build path: "nxc2/runtime/" + module + ".nx" 451 let prefix: *u8 = "nxc2/runtime/" as *u8 452 let plen: i64 = strlen(prefix) 453 var k: i64 = 0 454 while k < plen { path_buf[k] = prefix[k]; k = k + 1 } 455 let mod_len: i64 = strlen(r.name) 456 var k2: i64 = 0 457 while k2 < mod_len { path_buf[plen + k2] = r.name[k2]; k2 = k2 + 1 } 458 path_buf[plen + mod_len] = 46 // '.' 459 path_buf[plen + mod_len + 1] = 110 // 'n' 460 path_buf[plen + mod_len + 2] = 120 // 'x' 461 path_buf[plen + mod_len + 3] = 0 462 ev_scan_header(path_buf, r, imports_to, all_modules, n) 463 j = j + 1 464 } 465 466 // Assign generations. 467 ev_assign_generations(records, n, imports_to) 468 469 // Find max generation. 470 var max_gen: i64 = 0 471 var idx: i64 = 0 472 while idx < n { 473 let r: *EvolveRecord = ((records as i64) + idx * NX_EV_RECORD_BYTES) as *EvolveRecord 474 if r.generation > max_gen { max_gen = r.generation } 475 idx = idx + 1 476 } 477 478 // Emit one JSONL line per generation, in order. 479 var total_emitted: i64 = 0 480 var g: i64 = 0 481 while g <= max_gen { 482 let emitted: i64 = ev_emit_generation(records, n, g) 483 total_emitted = total_emitted + emitted 484 g = g + 1 485 } 486 487 // Unresolved (cyclic or missing deps) get generation = -1. 488 var n_unresolved: i64 = 0 489 var u: i64 = 0 490 while u < n { 491 let r: *EvolveRecord = ((records as i64) + u * NX_EV_RECORD_BYTES) as *EvolveRecord 492 if r.generation == NX_EV_GEN_UNKNOWN { n_unresolved = n_unresolved + 1 } 493 u = u + 1 494 } 495 496 print("{\"summary\":true,\"n_generations\":" as *u8) 497 print_i64(max_gen + 1) 498 print(",\"total_files\":" as *u8) 499 print_i64(n) 500 print(",\"emitted\":" as *u8) 501 print_i64(total_emitted) 502 print(",\"unresolved\":" as *u8) 503 print_i64(n_unresolved) 504 println("}" as *u8) 505 return 0 506}