code wiki / wiki / nx_pipeline_graph_builder.nx

nx_pipeline_graph_builder.nx source

↩ module page · 185 lines · 8502 B

1// nx_pipeline_graph_builder.nx -- V2.0 P-8.5: populate NxDepGraph from 2// NxArtifactStore via doc_extractor_nx imports + suffix-match resolution. 3// 4// Closes the dep-graph data path: walker -> store -> graph -> Tarjan SCC -> 5// per-artifact cycle membership exposed at /wiki/pipeline + /wiki/<doc>. 6// 7// COMPOSES: 8// wiki/nx_artifact_store (artifact paths to resolve against) 9// hub/nx_doc_extractor_nx (nx_doc_extract_nx_imports + 10// nx_doc_imports_iter_next) 11// hub/nx_dep_graph (graph storage + Tarjan) 12// nx_syscalls.sys_read_file (re-read .nx bodies for imports) 13// nx_html_extract.nx_he_match_ci (path suffix match for resolution) 14// 15// COMPOSED BY: 16// wiki/nx_wiki_main (startup; called after artifact_store populated) 17// 18// V2.0 P-8.5 SCOPE: 19// - For each RUNTIME_NX / TEST_NX / SILICON_ARC artifact: re-read body; 20// extract import paths; suffix-match each against artifact paths; 21// add resolved edges to graph 22// - Bounded by NX_ARTIFACT_STORE artifact_cap + edges_cap 23// - Per Cardinal 14 graceful degradation: per-file read failures skip 24// the artifact; unresolved imports skipped silently 25// 26// V3+ SCOPE (TODO): 27// - Body cache in NxArtifactStore (avoid re-read) 28// - Hash-keyed import resolution (avoid O(N) suffix match) 29// - Per-import resolution-confidence score (exact vs ambiguous match) 30// 31// Status: V2.0 P-8.5. 2026-05-27. 32 33import "nx_syscalls.nx" 34import "nx_html_extract.nx" 35import "wiki/nx_artifact_store.nx" 36import "hub/nx_doc_extractor_nx.nx" 37import "hub/nx_dep_graph.nx" 38 39// ===== Sealed verdict surface (codes 3060-3064) ================================================= 40const NX_PGB_OK: i64 = 0 41const NX_PGB_BAD_INPUT: i64 = 3060 42const NX_PGB_READ_FAILED: i64 = 3061 43const NX_PGB_GRAPH_FULL: i64 = 3062 44const NX_PGB_LOOP_BUDGET: i64 = 3063 45 46// ===== Named constants (M7) ================================================= 47const NX_PGB_IMPORTS_BUF_CAP: i64 = 16384 // 16 KB per-artifact packed imports 48const NX_PGB_LOOP_BUDGET_CAP: i64 = 10000000 49 50// ===== Suffix-match resolution: find artifact rowid whose path ENDS WITH suffix ================================================= 51// 52// Substrate convention: `import "wiki/nx_artifact_store.nx"` should match 53// any artifact path ending in `/wiki/nx_artifact_store.nx` (or 54// `\wiki\nx_artifact_store.nx` on hostile filesystems). Linear scan 55// over artifact_count; V2.0 acceptable for ~500 artifacts; V3+ swaps 56// for hash-keyed lookup. 57// 58// Returns rowid (>= 0) on unique match; -1 on no match; -2 on ambiguous (>1 match). 59 60func nx_pgb_resolve_import(store: *NxArtifactStore, 61 import_path: *u8, import_n: i64) -> i64 { 62 if store.valid != 1 { return 0 - 1 } 63 if import_n < 1 { return 0 - 1 } 64 65 var found: i64 = 0 - 1 66 var n_matches: i64 = 0 67 var i: i64 = 0 68 while i < store.artifact_count { 69 let path_ptr: *u8 = ((store.paths_pool as i64) + store.paths_offs[i]) as *u8 70 let path_n: i64 = store.paths_lens[i] 71 if path_n >= import_n { 72 // Suffix match: compare path[path_n - import_n .. path_n] vs import 73 let suffix_off: i64 = path_n - import_n 74 // Must be preceded by '/' (or be the entire path) 75 var suffix_ok: i64 = 1 76 if suffix_off > 0 { 77 if path_ptr[suffix_off - 1] != (0x2F as u8) { suffix_ok = 0 } 78 } 79 if suffix_ok == 1 { 80 if nx_he_match_ci(path_ptr, suffix_off, path_n, 81 import_path, import_n) == 1 { 82 found = i 83 n_matches = n_matches + 1 84 } 85 } 86 } 87 i = i + 1 88 } 89 if n_matches > 1 { return 0 - 2 } 90 return found 91} 92 93// ===== Build dep graph: walk store; per .nx artifact re-read body + extract imports + add edges ================================================= 94 95func nx_pipeline_build_dep_graph(store: *NxArtifactStore, 96 graph: *NxDepGraph, 97 out_counters: *i64) -> i64 { 98 if store.valid != 1 { return 0 - NX_PGB_BAD_INPUT } 99 if graph.valid != 1 { return 0 - NX_PGB_BAD_INPUT } 100 if (out_counters as i64) == 0 { return 0 - NX_PGB_BAD_INPUT } 101 102 // counters[0] = edges added; [1] = imports unresolved; [2] = imports ambiguous; 103 // [3] = artifacts re-read; [4] = read failures 104 out_counters[0] = 0 105 out_counters[1] = 0 106 out_counters[2] = 0 107 out_counters[3] = 0 108 out_counters[4] = 0 109 110 let imports_buf: *u8 = sys_mmap(NX_PGB_IMPORTS_BUF_CAP) 111 let count_p: *i64 = (sys_mmap(8)) as *i64 112 let used_p: *i64 = (sys_mmap(8)) as *i64 113 let ptr_p: *i64 = (sys_mmap(8)) as *i64 114 let n_p: *i64 = (sys_mmap(8)) as *i64 115 let lenbox: *u8 = sys_mmap(8) 116 let lp: *i64 = lenbox as *i64 117 118 var rowid: i64 = 0 119 var iter: i64 = 0 120 while rowid < store.artifact_count { 121 if iter >= NX_PGB_LOOP_BUDGET_CAP { return 0 - NX_PGB_LOOP_BUDGET } 122 iter = iter + 1 123 124 let kind: i64 = nx_artifact_store_kind_at(store, rowid) 125 var is_nx: i64 = 0 126 if kind == NX_ART_KIND_RUNTIME_NX { is_nx = 1 } 127 if kind == NX_ART_KIND_TEST_NX { is_nx = 1 } 128 if kind == NX_ART_KIND_SILICON_ARC{ is_nx = 1 } 129 130 if is_nx == 1 { 131 // Re-read body (V3+ caches in store) 132 let path_ptr_out: *i64 = (sys_mmap(8)) as *i64 133 let path_n_out: *i64 = (sys_mmap(8)) as *i64 134 path_ptr_out[0] = 0; path_n_out[0] = 0 135 let rc_p: i64 = nx_artifact_store_path_at(store, rowid, path_ptr_out, path_n_out) 136 if rc_p == NX_ART_OK { 137 lp[0] = 0 138 let body: *u8 = sys_read_file(path_ptr_out[0] as *u8, lp) 139 if (body as i64) != 0 { 140 if lp[0] > 0 { 141 out_counters[3] = out_counters[3] + 1 142 // Extract import paths 143 count_p[0] = 0; used_p[0] = 0 144 let rc_x: i64 = nx_doc_extract_nx_imports(body, lp[0], 145 imports_buf, NX_PGB_IMPORTS_BUF_CAP, 146 count_p, used_p) 147 if rc_x == NX_DXNX_OK { 148 // Iterate packed imports; resolve + add edges 149 var off: i64 = 0 150 var imp_iter: i64 = 0 151 while off >= 0 { 152 if imp_iter >= 1000 { off = 0 - 1 } // per-artifact cap 153 imp_iter = imp_iter + 1 154 ptr_p[0] = 0; n_p[0] = 0 155 let next_off: i64 = nx_doc_imports_iter_next(imports_buf, used_p[0], 156 off, ptr_p, n_p) 157 if next_off < 0 { off = 0 - 1 } 158 if next_off >= 0 { 159 let resolved: i64 = nx_pgb_resolve_import(store, 160 ptr_p[0] as *u8, 161 n_p[0]) 162 if resolved >= 0 { 163 let rc_e: i64 = nx_dep_graph_add_edge(graph, rowid, resolved) 164 if rc_e == NX_DEPG_OK { out_counters[0] = out_counters[0] + 1 } 165 if rc_e == 0 - NX_DEPG_EDGE_FULL { return 0 - NX_PGB_GRAPH_FULL } 166 } 167 if resolved == 0 - 1 { out_counters[1] = out_counters[1] + 1 } 168 if resolved == 0 - 2 { out_counters[2] = out_counters[2] + 1 } 169 off = next_off 170 } 171 } 172 } 173 } 174 } 175 if (body as i64) == 0 { out_counters[4] = out_counters[4] + 1 } 176 } 177 } 178 rowid = rowid + 1 179 } 180 181 // Run Tarjan 182 let rc_tarjan: i64 = nx_dep_graph_tarjan_scc(graph) 183 if rc_tarjan != NX_DEPG_OK { return 0 - NX_PGB_BAD_INPUT } 184 return NX_PGB_OK 185}