code wiki / _hdl_build / _wiki_backlinks_gate.nx

_wiki_backlinks_gate.nx source

↩ module page · 307 lines · 13565 B

1// _wiki_backlinks_gate.nx -- WIKI R4 GATE: backlinks + auto-TOC + static graph. 2// 3// Re-proves, from a REAL run (no fabricated GREEN), the three R4 affordances on 4// GROUND TRUTH (substrings + ground-truth counts in the actually-produced 5// output), never a bare rc==const: 6// 7// SEED a doc store with three pages: 8// alpha -- body has NO wikilinks. 9// beta -- body is "[[alpha]]". 10// gamma -- body is "[[alpha]] [[beta]] [[ZzqMissing]]". 11// 12// backlinks_ok alpha's backlinks include BOTH beta AND gamma; 13// beta's backlinks include gamma; gamma's are EMPTY. 14// (the inversion of the forward [[wikilinks]] is correct) 15// toc_ok the TOC for a body "# Title / ## Section A / ## Section B" 16// contains BOTH "#section-a" and "#section-b" anchor links, 17// AND id-injection stamps id="section-a" onto the <h2>. 18// (TOC links jump to real injected ids) 19// graph_ok node_count==3 AND edge_count==3 (beta->alpha, gamma->alpha, 20// gamma->beta). The [[ZzqMissing]] reference is NOT a node and 21// NOT an edge -- it is rot. 22// rot_ok gamma's [[ZzqMissing]] is flagged broken (broken_count>=1). 23// 24// Verdict line (judged by this marker; stdout + knowledge/status): 25// WIKIBACKLINKS backlinks_ok=<0|1> toc_ok=<0|1> graph_ok=<0|1> rot_ok=<0|1> 26// verdict=GREEN|RED 27// 28// Pure NishiLang, NO SQL, NO .sh/.py/.js, no new .tsv/.conf. nx_sites_daemon 29// UNTOUCHED. Imports the three R4 organs under test; each shared base module is 30// imported via a single consistent path (the doc-store + syscalls come in 31// transitively through the R4 organs -- imported here only as needed) to avoid 32// the double-import nxasm rc6 trap. 33// license_tier: ORIGINAL 34import "nx_syscalls.nx" 35import "nx_wiki_index_builder.nx" 36import "nx_wiki_backlinks.nx" 37import "nx_wiki_toc.nx" 38import "nx_wiki_graph.nx" 39 40const WBG_LOG: *u8 = "knowledge/status/wiki_backlinks_gate.log" 41const WBG_OUTCAP: i64 = 262144 // per-render output buffer (256 KiB) 42 43// ===== io helpers (mirror _wiki_cite_gate.nx) ================================ 44func wbg_w(fd: i64, s: *u8) -> i64 { 45 var n: i64 = 0 46 while s[n] != (0 as u8) { n = n + 1 } 47 sys_write(fd, s, n) 48 return 0 49} 50func wbg_n(fd: i64, v: i64) -> i64 { 51 let bb: *u8 = sys_mmap(28) 52 var m: i64 = v 53 if m < 0 { m = 0 - m; sys_write(fd, "-" as *u8, 1) } 54 let t: *u8 = sys_mmap(28) 55 var k: i64 = 0 56 if m == 0 { t[0] = 48 as u8; k = 1 } 57 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 } 58 var i: i64 = 0 59 while i < k { bb[i] = t[k - 1 - i]; i = i + 1 } 60 sys_write(fd, bb, k) 61 return 0 62} 63// write to BOTH stdout (gate evidence) and the durable log 64func wbg_w2(lfd: i64, s: *u8) -> i64 { wbg_w(1, s); if lfd >= 0 { wbg_w(lfd, s) } return 0 } 65func wbg_n2(lfd: i64, v: i64) -> i64 { wbg_n(1, v); if lfd >= 0 { wbg_n(lfd, v) } return 0 } 66func wbg_p(s: *u8) -> i64 { wbg_w(1, s); return 0 } 67func wbg_pn(v: i64) -> i64 { wbg_n(1, v); return 0 } 68func wbg_slen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n } 69 70// ===== GROUND-TRUTH substring search ========================================= 71func wbg_find(hay: *u8, hn: i64, needle: *u8) -> i64 { 72 let nn: i64 = wbg_slen(needle) 73 if nn == 0 { return 0 - 1 } 74 if nn > hn { return 0 - 1 } 75 var i: i64 = 0 76 let last: i64 = hn - nn 77 while i <= last { 78 var j: i64 = 0 79 var eq: i64 = 1 80 while j < nn { 81 if eq == 1 { if hay[i + j] != needle[j] { eq = 0 } } 82 j = j + 1 83 } 84 if eq == 1 { return i } 85 i = i + 1 86 } 87 return 0 - 1 88} 89func wbg_has(hay: *u8, hn: i64, needle: *u8) -> i64 { 90 if wbg_find(hay, hn, needle) >= 0 { return 1 } 91 return 0 92} 93 94// is rowid R present in out_rowids[0..cnt]? (ground-truth membership) 95func wbg_rowid_in(arr: *i64, cnt: i64, r: i64) -> i64 { 96 var i: i64 = 0 97 while i < cnt { 98 if arr[i] == r { return 1 } 99 i = i + 1 100 } 101 return 0 102} 103 104func main() -> i64 { 105 wbg_p("WIKIBACKLINKS-GATE: start (backlinks + auto-TOC + static graph)\n" as *u8) 106 107 // ===== SEED a doc store with alpha / beta / gamma ======================== 108 let store: *NxWikiDocStore = sys_mmap(2048) as *NxWikiDocStore 109 let rc_init: i64 = nx_wiki_doc_store_init(store, 16, 4096, 4096, 65536) 110 wbg_p(" store_init rc=" as *u8); wbg_pn(rc_init); wbg_p("\n" as *u8) 111 112 // alpha: no links. (url = bare slug "alpha".) Lengths computed (not 113 // hardcoded) so the body bytes are never silently truncated mid-link. 114 let a_body: *u8 = "# Alpha\nAlpha has no outbound wikilinks.\n" as *u8 115 let a_rid: i64 = nx_wiki_doc_store_add(store, 116 "Alpha" as *u8, 5, "alpha" as *u8, 5, a_body, wbg_slen(a_body)) 117 // beta: [[alpha]] 118 let b_body: *u8 = "# Beta\nBeta links to [[alpha]] only.\n" as *u8 119 let b_rid: i64 = nx_wiki_doc_store_add(store, 120 "Beta" as *u8, 4, "beta" as *u8, 4, b_body, wbg_slen(b_body)) 121 // gamma: [[alpha]] [[beta]] [[ZzqMissing]] 122 let g_body: *u8 = "# Gamma\nGamma links to [[alpha]] and [[beta]] and [[ZzqMissing]].\n" as *u8 123 let g_rid: i64 = nx_wiki_doc_store_add(store, 124 "Gamma" as *u8, 5, "gamma" as *u8, 5, g_body, wbg_slen(g_body)) 125 wbg_p(" seeded rowids: alpha=" as *u8); wbg_pn(a_rid) 126 wbg_p(" beta=" as *u8); wbg_pn(b_rid) 127 wbg_p(" gamma=" as *u8); wbg_pn(g_rid) 128 wbg_p(" doc_count=" as *u8); wbg_pn(nx_wiki_doc_store_count(store)); wbg_p("\n" as *u8) 129 130 // ===== ASSERTION 1: backlinks inversion ================================== 131 let rids: *i64 = sys_mmap(64 * 8) as *i64 132 let cnt: *i64 = sys_mmap(8) as *i64 133 134 // alpha's backlinks -> must include beta AND gamma (count 2) 135 let rc_ba: i64 = nx_wiki_backlinks_of(store, "alpha" as *u8, 5, rids, 64, cnt) 136 var alpha_has_beta: i64 = 0 137 var alpha_has_gamma: i64 = 0 138 if rc_ba == NX_WBL_OK { 139 alpha_has_beta = wbg_rowid_in(rids, cnt[0], b_rid) 140 alpha_has_gamma = wbg_rowid_in(rids, cnt[0], g_rid) 141 } 142 wbg_p(" backlinks(alpha): rc=" as *u8); wbg_pn(rc_ba) 143 wbg_p(" count=" as *u8); wbg_pn(cnt[0]) 144 wbg_p(" has_beta=" as *u8); wbg_pn(alpha_has_beta) 145 wbg_p(" has_gamma=" as *u8); wbg_pn(alpha_has_gamma); wbg_p("\n" as *u8) 146 147 // beta's backlinks -> must include gamma (count 1), must NOT include alpha 148 let rc_bb: i64 = nx_wiki_backlinks_of(store, "beta" as *u8, 4, rids, 64, cnt) 149 var beta_has_gamma: i64 = 0 150 var beta_has_alpha: i64 = 0 151 if rc_bb == NX_WBL_OK { 152 beta_has_gamma = wbg_rowid_in(rids, cnt[0], g_rid) 153 beta_has_alpha = wbg_rowid_in(rids, cnt[0], a_rid) 154 } 155 wbg_p(" backlinks(beta): rc=" as *u8); wbg_pn(rc_bb) 156 wbg_p(" count=" as *u8); wbg_pn(cnt[0]) 157 wbg_p(" has_gamma=" as *u8); wbg_pn(beta_has_gamma) 158 wbg_p(" has_alpha(should be 0)=" as *u8); wbg_pn(beta_has_alpha); wbg_p("\n" as *u8) 159 160 // gamma's backlinks -> must be EMPTY (count 0) 161 let rc_bg: i64 = nx_wiki_backlinks_of(store, "gamma" as *u8, 5, rids, 64, cnt) 162 var gamma_empty: i64 = 0 163 if rc_bg == NX_WBL_OK { if cnt[0] == 0 { gamma_empty = 1 } } 164 wbg_p(" backlinks(gamma): rc=" as *u8); wbg_pn(rc_bg) 165 wbg_p(" count=" as *u8); wbg_pn(cnt[0]) 166 wbg_p(" empty=" as *u8); wbg_pn(gamma_empty); wbg_p("\n" as *u8) 167 168 var backlinks_ok: i64 = 0 169 if alpha_has_beta == 1 { 170 if alpha_has_gamma == 1 { 171 if beta_has_gamma == 1 { 172 if beta_has_alpha == 0 { 173 if gamma_empty == 1 { backlinks_ok = 1 } 174 } 175 } 176 } 177 } 178 wbg_p(" => backlinks_ok = " as *u8); wbg_pn(backlinks_ok); wbg_p("\n" as *u8) 179 180 // EVIDENCE: render alpha's "what links here" panel + echo it 181 let panel: *u8 = sys_mmap(WBG_OUTCAP) 182 let pused: *i64 = sys_mmap(8) as *i64 183 let rc_pan: i64 = nx_wiki_backlinks_panel(store, "alpha" as *u8, 5, panel, WBG_OUTCAP, pused) 184 if rc_pan == NX_WBL_OK { 185 wbg_p(" ---- what-links-here panel (alpha) ----\n " as *u8) 186 sys_write(1, panel, pused[0]) 187 wbg_p("\n ---- /panel ----\n" as *u8) 188 } 189 190 // ===== ASSERTION 2: auto-TOC + id injection ============================== 191 let toc_src: *u8 = "# Title\n\n## Section A\n\nbody a\n\n## Section B\n\nbody b\n" as *u8 192 let toc_src_n: i64 = wbg_slen(toc_src) 193 let toc: *u8 = sys_mmap(WBG_OUTCAP) 194 let tused: *i64 = sys_mmap(8) as *i64 195 let rc_toc: i64 = nx_wiki_toc_build(toc_src, toc_src_n, toc, WBG_OUTCAP, tused) 196 var toc_has_a: i64 = 0 197 var toc_has_b: i64 = 0 198 if rc_toc == NX_WTOC_OK { 199 toc_has_a = wbg_has(toc, tused[0], "href=\"#section-a\"" as *u8) 200 toc_has_b = wbg_has(toc, tused[0], "href=\"#section-b\"" as *u8) 201 } 202 wbg_p(" toc: rc=" as *u8); wbg_pn(rc_toc) 203 wbg_p(" len=" as *u8); wbg_pn(tused[0]) 204 wbg_p(" has_#section-a=" as *u8); wbg_pn(toc_has_a) 205 wbg_p(" has_#section-b=" as *u8); wbg_pn(toc_has_b); wbg_p("\n" as *u8) 206 if rc_toc == NX_WTOC_OK { 207 wbg_p(" ---- TOC ----\n " as *u8) 208 sys_write(1, toc, tused[0]) 209 wbg_p("\n ---- /TOC ----\n" as *u8) 210 } 211 212 // id injection: feed rendered-like HTML with <h2>Section A</h2> -> expect 213 // id="section-a" stamped onto it. 214 let html_in: *u8 = "<h1>Title</h1><h2>Section A</h2><p>x</p><h2>Section B</h2>" as *u8 215 let html_in_n: i64 = wbg_slen(html_in) 216 let injected: *u8 = sys_mmap(WBG_OUTCAP) 217 let iused: *i64 = sys_mmap(8) as *i64 218 let rc_inj: i64 = nx_wiki_toc_inject_ids(html_in, html_in_n, injected, WBG_OUTCAP, iused) 219 var inj_a: i64 = 0 220 var inj_b: i64 = 0 221 if rc_inj == NX_WTOC_OK { 222 inj_a = wbg_has(injected, iused[0], "<h2 id=\"section-a\">Section A</h2>" as *u8) 223 inj_b = wbg_has(injected, iused[0], "<h2 id=\"section-b\">Section B</h2>" as *u8) 224 } 225 wbg_p(" inject_ids: rc=" as *u8); wbg_pn(rc_inj) 226 wbg_p(" len=" as *u8); wbg_pn(iused[0]) 227 wbg_p(" id=section-a=" as *u8); wbg_pn(inj_a) 228 wbg_p(" id=section-b=" as *u8); wbg_pn(inj_b); wbg_p("\n" as *u8) 229 if rc_inj == NX_WTOC_OK { 230 wbg_p(" ---- id-injected HTML ----\n " as *u8) 231 sys_write(1, injected, iused[0]) 232 wbg_p("\n ---- /injected ----\n" as *u8) 233 } 234 235 var toc_ok: i64 = 0 236 if toc_has_a == 1 { if toc_has_b == 1 { if inj_a == 1 { if inj_b == 1 { toc_ok = 1 } } } } 237 wbg_p(" => toc_ok = " as *u8); wbg_pn(toc_ok); wbg_p("\n" as *u8) 238 239 // ===== ASSERTION 3 + 4: graph nodes/edges + rot ========================== 240 let graph: *NxWikiGraph = sys_mmap(256) as *NxWikiGraph 241 let rc_gb: i64 = nx_wiki_graph_build(store, graph) 242 let nodec: i64 = nx_wiki_graph_node_count(graph) 243 let edgec: i64 = nx_wiki_graph_edge_count(graph) 244 let brokenc: i64 = nx_wiki_graph_broken_count(graph) 245 wbg_p(" graph: rc=" as *u8); wbg_pn(rc_gb) 246 wbg_p(" node_count=" as *u8); wbg_pn(nodec) 247 wbg_p(" edge_count=" as *u8); wbg_pn(edgec) 248 wbg_p(" broken_count=" as *u8); wbg_pn(brokenc); wbg_p("\n" as *u8) 249 250 var graph_ok: i64 = 0 251 if rc_gb == NX_WGRAPH_OK { if nodec == 3 { if edgec == 3 { graph_ok = 1 } } } 252 wbg_p(" => graph_ok = " as *u8); wbg_pn(graph_ok); wbg_p("\n" as *u8) 253 254 var rot_ok: i64 = 0 255 if brokenc >= 1 { rot_ok = 1 } 256 wbg_p(" => rot_ok = " as *u8); wbg_pn(rot_ok); wbg_p("\n" as *u8) 257 258 // EVIDENCE: render the static SVG + confirm it is JS-free 259 let svg: *u8 = sys_mmap(WBG_OUTCAP) 260 let sused: *i64 = sys_mmap(8) as *i64 261 let rc_svg: i64 = nx_wiki_graph_render_svg(graph, store, svg, WBG_OUTCAP, sused) 262 var svg_has_svg: i64 = 0 263 var svg_has_line: i64 = 0 264 var svg_has_text: i64 = 0 265 var svg_has_script: i64 = 0 266 if rc_svg == NX_WGRAPH_OK { 267 svg_has_svg = wbg_has(svg, sused[0], "<svg" as *u8) 268 svg_has_line = wbg_has(svg, sused[0], "<line" as *u8) 269 svg_has_text = wbg_has(svg, sused[0], "<text" as *u8) 270 svg_has_script = wbg_has(svg, sused[0], "<script" as *u8) 271 } 272 wbg_p(" svg: rc=" as *u8); wbg_pn(rc_svg) 273 wbg_p(" len=" as *u8); wbg_pn(sused[0]) 274 wbg_p(" has<svg>=" as *u8); wbg_pn(svg_has_svg) 275 wbg_p(" has<line>=" as *u8); wbg_pn(svg_has_line) 276 wbg_p(" has<text>=" as *u8); wbg_pn(svg_has_text) 277 wbg_p(" has<script>(should be 0)=" as *u8); wbg_pn(svg_has_script); wbg_p("\n" as *u8) 278 if rc_svg == NX_WGRAPH_OK { 279 wbg_p(" ---- static SVG graph (JS-free) ----\n " as *u8) 280 sys_write(1, svg, sused[0]) 281 wbg_p("\n ---- /SVG ----\n" as *u8) 282 } 283 // fold the JS-free proof into graph_ok (a graph with a <script> is NOT ok) 284 if svg_has_script == 1 { graph_ok = 0 } 285 if svg_has_svg == 0 { graph_ok = 0 } 286 if svg_has_line == 0 { graph_ok = 0 } 287 if svg_has_text == 0 { graph_ok = 0 } 288 289 // ===== VERDICT =========================================================== 290 var green: i64 = 1 291 if backlinks_ok != 1 { green = 0 } 292 if toc_ok != 1 { green = 0 } 293 if graph_ok != 1 { green = 0 } 294 if rot_ok != 1 { green = 0 } 295 296 let lfd: i64 = sys_openat_append(WBG_LOG, 420) 297 wbg_w2(lfd, "WIKIBACKLINKS backlinks_ok=" as *u8); wbg_n2(lfd, backlinks_ok) 298 wbg_w2(lfd, " toc_ok=" as *u8); wbg_n2(lfd, toc_ok) 299 wbg_w2(lfd, " graph_ok=" as *u8); wbg_n2(lfd, graph_ok) 300 wbg_w2(lfd, " rot_ok=" as *u8); wbg_n2(lfd, rot_ok) 301 if green == 1 { wbg_w2(lfd, " verdict=GREEN\n" as *u8) } else { wbg_w2(lfd, " verdict=RED\n" as *u8) } 302 if lfd >= 0 { sys_close(lfd) } 303 304 if green == 1 { sys_exit(0); return 0 } 305 sys_exit(1) 306 return 1 307}