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}