code wiki / _hdl_build / nx_wiki_walkability.nx

nx_wiki_walkability.nx source

↩ module page · 462 lines · 19004 B

1// nx_wiki_walkability.nx -- IMS arc: MEASURED walkability scorecard over the 2// REAL published wiki link graph (READ-ONLY baseline, no assertions). 3// 4// PURPOSE: produce the honest navigation baseline the operator asked for -- 5// NUMBERS from the link graph, not claims. Over the live corpus it computes: 6// 1. N pages, total internal page-links, avg/min/max OUT-degree + IN-degree. 7// 2. ORPHANS (in==0, excluding root) + DEAD-ENDS (out==0), with slugs. 8// 3. RECIPROCAL page pairs (A->B AND B->A) vs ONE-WAY pairs. 9// 4. Reachability from start: reachable count, max clicks-to-reach (BFS 10// depth), and STRANDED pages (unreachable from start). 11// 12// READ-ONLY BY CONSTRUCTION: reads ONE framed corpus snapshot via sys_read_file 13// (the exact A1 transport) and computes in memory. Writes NOTHING to the corpus 14// -- the only output is the scorecard to stdout + a status log. 15// 16// COMPOSES (DRY -- zero new corpus/link/slug substrate invented): 17// nx_ims_monitor nx_ims_collect_targets (the ONE unified outbound 18// [[wikilink]]+href extractor), nx_ims_slug_eq (the 19// ONE slug resolver), nx_ims_resolve_root, and the 20// NxWikiDocStore it composes -- so this scorecard's 21// notion of "a link/edge" is byte-identical to the 22// orphan/dead-link monitor and the graph builder. 23// nx_syscalls sys_read_file (read-only) + sys_mmap scratch. 24// 25// The adjacency is built ONCE into a dense N*N edge bitmatrix (self-edges 26// dropped -- a page linking itself is not navigation), then every metric is a 27// pure read of that matrix. N is small (corpus cap), so N*N is trivial. 28// 29// Hygiene: M1 out-params; M3 every while hard-capped; M5 bounded indexing; 30// M6 real semantics; M7 named constants; M8 verdicts. ("loop"/"match" reserved.) 31// license_tier: ORIGINAL 32import "nx_ims_monitor.nx" 33import "nx_syscalls.nx" 34const NX_MAGIC_8192: i64 = 8192 35const NX_MAGIC_16384: i64 = 16384 36const NX_MAGIC_1000000: i64 = 1000000 37 38// ===== Sealed verdict surface (codes 2760-2779) ============================== 39const NX_WALK_OK: i64 = 0 40const NX_WALK_BAD_INPUT: i64 = 2760 41const NX_WALK_OVERFLOW: i64 = 2761 42 43// ===== Named sizing constants (M7) =========================================== 44const WALK_MAX_PAGES: i64 = 256 // corpus cap for the dense matrix 45const WALK_MAX_BYTES: i64 = 4194304 // 4 MB snapshot cap 46const WALK_MARK_PAGE_N: i64 = 8 // len("###PAGE ") 47const WALK_MAX_LINKS_PP: i64 = 512 // outbound targets scanned per page 48 49// ===== tiny stdout+log writers (mirror the A1 live runner) =================== 50func wlp(fd: i64, s: *u8) -> i64 { 51 var n: i64 = 0 52 while s[n] != (0 as u8) { n = n + 1 } 53 sys_write(1, s, n) 54 if fd > 0 { sys_write(fd, s, n) } 55 return 0 56} 57func wln(fd: i64, v: i64) -> i64 { 58 var m: i64 = v 59 if m < 0 { wlp(fd, "-\x00" as *u8); m = 0 - m } 60 let t: *u8 = sys_mmap(28) 61 var k: i64 = 0 62 if m == 0 { t[0] = 48 as u8; k = 1 } 63 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 } 64 let bb: *u8 = sys_mmap(28) 65 var i: i64 = 0 66 while i < k { bb[i] = t[k - 1 - i]; i = i + 1 } 67 sys_write(1, bb, k) 68 if fd > 0 { sys_write(fd, bb, k) } 69 return 0 70} 71// print a value scaled by 100 as "X.YY" (integer fixed-point, no float) 72func wln2(fd: i64, scaled: i64) -> i64 { 73 wln(fd, scaled / 100) 74 wlp(fd, ".\x00" as *u8) 75 let frac: i64 = scaled - ((scaled / 100) * 100) 76 if frac < 10 { wlp(fd, "0\x00" as *u8) } 77 wln(fd, frac) 78 return 0 79} 80func wlb(fd: i64, p: *u8, n: i64) -> i64 { 81 var i: i64 = 0 82 while i < n { 83 if i >= NX_MAGIC_8192 { i = n } 84 if i < n { 85 sys_write(1, (p as i64 + i) as *u8, 1) 86 if fd > 0 { sys_write(fd, (p as i64 + i) as *u8, 1) } 87 } 88 i = i + 1 89 } 90 return 0 91} 92 93// ===== framed-snapshot parse helpers (mirror nx_ims_monitor_live) ============ 94func walk_parse_int(buf: *u8, off: i64, lim: i64, out_off: *i64) -> i64 { 95 var i: i64 = off 96 var v: i64 = 0 97 var done: i64 = 0 98 while done == 0 { 99 if i >= lim { done = 1 } 100 if done == 0 { 101 let c: i64 = buf[i] as i64 102 if c < 0x30 { done = 1 } 103 if done == 0 { if c > 0x39 { done = 1 } } 104 if done == 0 { v = v * 10 + (c - 0x30); i = i + 1 } 105 } 106 } 107 out_off[0] = i 108 return v 109} 110func walk_is_page_mark(buf: *u8, off: i64, lim: i64) -> i64 { 111 let mk: *u8 = "###PAGE \x00" as *u8 112 if off + WALK_MARK_PAGE_N > lim { return 0 } 113 var i: i64 = 0 114 var ok: i64 = 1 115 while i < WALK_MARK_PAGE_N { 116 if buf[off + i] != mk[i] { ok = 0 } 117 i = i + 1 118 } 119 return ok 120} 121 122// Ingest the framed corpus snapshot into the doc store (READ-ONLY). Returns the 123// page count (>=0) or -verdict. Identical framing/logic to the A1 live runner so 124// the SAME corpus is measured. 125func walk_ingest(store: *NxWikiDocStore, buf: *u8, total: i64) -> i64 { 126 var i: i64 = 0 127 var npages: i64 = 0 128 let oo: *i64 = sys_mmap(8) as *i64 129 while i < total { 130 if walk_is_page_mark(buf, i, total) == 1 { 131 let url_off: i64 = i + WALK_MARK_PAGE_N 132 var u: i64 = url_off 133 var done_u: i64 = 0 134 while done_u == 0 { 135 if u >= total { done_u = 1 } 136 if done_u == 0 { 137 if buf[u] == (0x20 as u8) { done_u = 1 } 138 if done_u == 0 { u = u + 1 } 139 } 140 } 141 let url_n: i64 = u - url_off 142 let blen: i64 = walk_parse_int(buf, u + 1, total, oo) 143 var hdr_end: i64 = oo[0] 144 var done_h: i64 = 0 145 while done_h == 0 { 146 if hdr_end >= total { done_h = 1 } 147 if done_h == 0 { 148 if buf[hdr_end] == (0x0A as u8) { done_h = 1 } 149 if done_h == 0 { hdr_end = hdr_end + 1 } 150 } 151 } 152 let body_off: i64 = hdr_end + 1 153 var body_n: i64 = blen 154 if body_off + body_n > total { body_n = total - body_off } 155 let url_p: *u8 = (buf as i64 + url_off) as *u8 156 let body_p: *u8 = (buf as i64 + body_off) as *u8 157 let rid: i64 = nx_wiki_doc_store_add(store, url_p, url_n, url_p, url_n, body_p, body_n) 158 if rid >= 0 { npages = npages + 1 } 159 i = body_off + body_n 160 } else { 161 i = i + 1 162 } 163 } 164 return npages 165} 166 167// ===== dense adjacency matrix ================================================ 168// 169// adj[s*N + d] = 1 iff page s has an outbound internal PAGE-link to page d, 170// s != d (self-links dropped: not navigation). Built ONCE from the shared 171// nx_ims_collect_targets + nx_ims_slug_eq. Returns NX_WALK_OK or -verdict. 172func walk_build_adj(store: *NxWikiDocStore, adj: *i64, n: i64) -> i64 { 173 var z: i64 = 0 174 while z < n * n { 175 if z >= WALK_MAX_PAGES * WALK_MAX_PAGES { return 0 - NX_WALK_OVERFLOW } 176 adj[z] = 0 177 z = z + 1 178 } 179 let offs: *i64 = sys_mmap(WALK_MAX_LINKS_PP * 8) as *i64 180 let lens: *i64 = sys_mmap(WALK_MAX_LINKS_PP * 8) as *i64 181 let lc: *i64 = sys_mmap(8) as *i64 182 let tp: *i64 = sys_mmap(8) as *i64 183 let tn: *i64 = sys_mmap(8) as *i64 184 let up: *i64 = sys_mmap(8) as *i64 185 let un: *i64 = sys_mmap(8) as *i64 186 let bp: *i64 = sys_mmap(8) as *i64 187 let bn: *i64 = sys_mmap(8) as *i64 188 let u2p: *i64 = sys_mmap(8) as *i64 189 let u2n: *i64 = sys_mmap(8) as *i64 190 let t2p: *i64 = sys_mmap(8) as *i64 191 let t2n: *i64 = sys_mmap(8) as *i64 192 let b2p: *i64 = sys_mmap(8) as *i64 193 let b2n: *i64 = sys_mmap(8) as *i64 194 195 var s: i64 = 0 196 while s < n { 197 if s >= WALK_MAX_PAGES { return 0 - NX_WALK_OVERFLOW } 198 let rc_s: i64 = nx_wiki_doc_store_lookup(store, s, tp, tn, up, un, bp, bn) 199 if rc_s == NX_WIB_OK { 200 let body: *u8 = bp[0] as *u8 201 let body_n: i64 = bn[0] 202 let rc_c: i64 = nx_ims_collect_targets(body, body_n, offs, lens, WALK_MAX_LINKS_PP, lc) 203 if rc_c == NX_IMS_OK { 204 var k: i64 = 0 205 while k < lc[0] { 206 if k >= WALK_MAX_LINKS_PP { k = lc[0] } 207 if k < lc[0] { 208 let lp: *u8 = (body as i64 + offs[k]) as *u8 209 let ln: i64 = lens[k] 210 var dst: i64 = 0 - 1 211 var t: i64 = 0 212 while t < n { 213 if dst < 0 { 214 let rc_t: i64 = nx_wiki_doc_store_lookup(store, t, t2p, t2n, u2p, u2n, b2p, b2n) 215 if rc_t == NX_WIB_OK { 216 if nx_ims_slug_eq(lp, ln, u2p[0] as *u8, u2n[0]) == 1 { dst = t } 217 } 218 } 219 t = t + 1 220 } 221 if dst >= 0 { 222 if dst != s { adj[s * n + dst] = 1 } // dedup + drop self 223 } 224 } 225 k = k + 1 226 } 227 } 228 } 229 s = s + 1 230 } 231 return NX_WALK_OK 232} 233 234func main() -> i64 { 235 let logfd: i64 = sys_openat_append("knowledge/status/wiki_walkability.log\x00" as *u8, 0x1a4) 236 wlp(logfd, "=== WIKI WALKABILITY SCORECARD (MEASURED, read-only) ===\n\x00" as *u8) 237 238 // ---- read the framed snapshot (READ-ONLY, the SAME A1 corpus) ---- 239 let lenbox: *i64 = sys_mmap(16) as *i64 240 lenbox[0] = 0 241 let buf: *u8 = sys_read_file("knowledge/status/ims_live_corpus.txt\x00" as *u8, lenbox) 242 if (buf as i64) == 0 { wlp(logfd, "FATAL: cannot read ims_live_corpus.txt\n\x00" as *u8); sys_exit(2) } 243 let total: i64 = lenbox[0] 244 wlp(logfd, "snapshot bytes=\x00" as *u8); wln(logfd, total); wlp(logfd, "\n\x00" as *u8) 245 if total < 1 { wlp(logfd, "FATAL: empty snapshot\n\x00" as *u8); sys_exit(2) } 246 if total > WALK_MAX_BYTES { wlp(logfd, "FATAL: snapshot too large\n\x00" as *u8); sys_exit(2) } 247 248 let store: *NxWikiDocStore = sys_mmap(512) as *NxWikiDocStore 249 nx_wiki_doc_store_init(store, 64, NX_MAGIC_16384, NX_MAGIC_16384, WALK_MAX_BYTES) 250 let n: i64 = walk_ingest(store, buf, total) 251 if n < 1 { wlp(logfd, "FATAL: 0 pages ingested\n\x00" as *u8); sys_exit(2) } 252 if n > WALK_MAX_PAGES { wlp(logfd, "FATAL: too many pages\n\x00" as *u8); sys_exit(2) } 253 254 // ---- resolve root = start ---- 255 let root: i64 = nx_ims_resolve_root(store, "start\x00" as *u8, 5) 256 257 // ---- build the dense adjacency ONCE ---- 258 let adj: *i64 = sys_mmap(WALK_MAX_PAGES * WALK_MAX_PAGES * 8) as *i64 259 let rc_a: i64 = walk_build_adj(store, adj, n) 260 if rc_a != NX_WALK_OK { wlp(logfd, "FATAL: adj build failed rc=\x00" as *u8); wln(logfd, rc_a); wlp(logfd, "\n\x00" as *u8); sys_exit(2) } 261 262 // ---- per-page out/in degree ---- 263 let outd: *i64 = sys_mmap(WALK_MAX_PAGES * 8) as *i64 264 let ind: *i64 = sys_mmap(WALK_MAX_PAGES * 8) as *i64 265 var total_edges: i64 = 0 266 var p: i64 = 0 267 while p < n { 268 var od: i64 = 0 269 var id: i64 = 0 270 var q: i64 = 0 271 while q < n { 272 od = od + adj[p * n + q] // p -> q 273 id = id + adj[q * n + p] // q -> p 274 q = q + 1 275 } 276 outd[p] = od 277 ind[p] = id 278 total_edges = total_edges + od 279 p = p + 1 280 } 281 282 // degree min/max/avg (avg scaled x100) 283 var omin: i64 = NX_MAGIC_1000000 284 var omax: i64 = 0 285 var imin: i64 = NX_MAGIC_1000000 286 var imax: i64 = 0 287 var pp: i64 = 0 288 while pp < n { 289 if outd[pp] < omin { omin = outd[pp] } 290 if outd[pp] > omax { omax = outd[pp] } 291 if ind[pp] < imin { imin = ind[pp] } 292 if ind[pp] > imax { imax = ind[pp] } 293 pp = pp + 1 294 } 295 let avg_scaled: i64 = (total_edges * 100) / n // == avg out == avg in (sum identical) 296 297 // lookup out-params for printing slugs 298 let tp: *i64 = sys_mmap(8) as *i64; let tn: *i64 = sys_mmap(8) as *i64 299 let up: *i64 = sys_mmap(8) as *i64; let un: *i64 = sys_mmap(8) as *i64 300 let bp: *i64 = sys_mmap(8) as *i64; let bn: *i64 = sys_mmap(8) as *i64 301 302 // ---- per-page table ---- 303 wlp(logfd, "pages N=\x00" as *u8); wln(logfd, n) 304 wlp(logfd, " root(start) rowid=\x00" as *u8); wln(logfd, root); wlp(logfd, "\n\x00" as *u8) 305 wlp(logfd, "--- PER-PAGE DEGREE (internal page-links; self-links dropped) ---\n\x00" as *u8) 306 var pr: i64 = 0 307 while pr < n { 308 nx_wiki_doc_store_lookup(store, pr, tp, tn, up, un, bp, bn) 309 wlp(logfd, " [\x00" as *u8); wln(logfd, pr); wlp(logfd, "] \x00" as *u8) 310 wlb(logfd, up[0] as *u8, un[0]) 311 wlp(logfd, " out=\x00" as *u8); wln(logfd, outd[pr]) 312 wlp(logfd, " in=\x00" as *u8); wln(logfd, ind[pr]); wlp(logfd, "\n\x00" as *u8) 313 pr = pr + 1 314 } 315 316 // ---- 1. aggregate metrics ---- 317 wlp(logfd, "--- 1. GRAPH METRICS ---\n\x00" as *u8) 318 wlp(logfd, " total internal page-links (edges)=\x00" as *u8); wln(logfd, total_edges); wlp(logfd, "\n\x00" as *u8) 319 wlp(logfd, " avg OUT-links/page=\x00" as *u8); wln2(logfd, avg_scaled) 320 wlp(logfd, " (min=\x00" as *u8); wln(logfd, omin); wlp(logfd, " max=\x00" as *u8); wln(logfd, omax); wlp(logfd, ")\n\x00" as *u8) 321 wlp(logfd, " avg IN-links/page=\x00" as *u8); wln2(logfd, avg_scaled) 322 wlp(logfd, " (min=\x00" as *u8); wln(logfd, imin); wlp(logfd, " max=\x00" as *u8); wln(logfd, imax); wlp(logfd, ")\n\x00" as *u8) 323 324 // ---- 2. orphans (in==0, excl root) + dead-ends (out==0) ---- 325 wlp(logfd, "--- 2. ORPHANS (in==0, excl root) + DEAD-ENDS (out==0) ---\n\x00" as *u8) 326 var n_orph: i64 = 0 327 var n_dead: i64 = 0 328 wlp(logfd, " ORPHANS:\x00" as *u8) 329 var oi: i64 = 0 330 while oi < n { 331 if ind[oi] == 0 { 332 if oi != root { 333 nx_wiki_doc_store_lookup(store, oi, tp, tn, up, un, bp, bn) 334 wlp(logfd, " \x00" as *u8); wlb(logfd, up[0] as *u8, un[0]) 335 n_orph = n_orph + 1 336 } 337 } 338 oi = oi + 1 339 } 340 wlp(logfd, " (count=\x00" as *u8); wln(logfd, n_orph); wlp(logfd, ")\n\x00" as *u8) 341 wlp(logfd, " DEAD-ENDS:\x00" as *u8) 342 var di: i64 = 0 343 while di < n { 344 if outd[di] == 0 { 345 nx_wiki_doc_store_lookup(store, di, tp, tn, up, un, bp, bn) 346 wlp(logfd, " \x00" as *u8); wlb(logfd, up[0] as *u8, un[0]) 347 n_dead = n_dead + 1 348 } 349 di = di + 1 350 } 351 wlp(logfd, " (count=\x00" as *u8); wln(logfd, n_dead); wlp(logfd, ")\n\x00" as *u8) 352 353 // ---- 3. reciprocal vs one-way pairs ---- 354 // For each unordered pair (a<b): both = adj[a,b] && adj[b,a]; oneway = exactly one. 355 wlp(logfd, "--- 3. PAIR DIRECTIONALITY (unordered page pairs) ---\n\x00" as *u8) 356 var recip: i64 = 0 357 var oneway: i64 = 0 358 var a: i64 = 0 359 while a < n { 360 var b: i64 = a + 1 361 while b < n { 362 let ab: i64 = adj[a * n + b] 363 let ba: i64 = adj[b * n + a] 364 if ab == 1 { 365 if ba == 1 { recip = recip + 1 } 366 } 367 if ab + ba == 1 { oneway = oneway + 1 } 368 b = b + 1 369 } 370 a = a + 1 371 } 372 wlp(logfd, " RECIPROCAL pairs (A<->B both ways)=\x00" as *u8); wln(logfd, recip); wlp(logfd, "\n\x00" as *u8) 373 wlp(logfd, " ONE-WAY pairs (A->B only)=\x00" as *u8); wln(logfd, oneway); wlp(logfd, "\n\x00" as *u8) 374 // enumerate the reciprocal pairs explicitly (small N) 375 wlp(logfd, " reciprocal detail:\x00" as *u8) 376 a = 0 377 while a < n { 378 var b2: i64 = a + 1 379 while b2 < n { 380 if adj[a * n + b2] == 1 { 381 if adj[b2 * n + a] == 1 { 382 nx_wiki_doc_store_lookup(store, a, tp, tn, up, un, bp, bn) 383 wlp(logfd, " \x00" as *u8); wlb(logfd, up[0] as *u8, un[0]) 384 wlp(logfd, "<->\x00" as *u8) 385 nx_wiki_doc_store_lookup(store, b2, tp, tn, up, un, bp, bn) 386 wlb(logfd, up[0] as *u8, un[0]) 387 } 388 } 389 b2 = b2 + 1 390 } 391 a = a + 1 392 } 393 wlp(logfd, "\n\x00" as *u8) 394 395 // ---- 4. reachability from start (BFS) ---- 396 // dist[] = clicks-to-reach from root; -1 = unvisited. Iterative relaxation 397 // over the dense matrix, capped at N rounds (longest simple path <= N-1). 398 wlp(logfd, "--- 4. REACHABILITY FROM start (BFS over the live graph) ---\n\x00" as *u8) 399 let dist: *i64 = sys_mmap(WALK_MAX_PAGES * 8) as *i64 400 var zz: i64 = 0 401 while zz < n { dist[zz] = 0 - 1; zz = zz + 1 } 402 var reachable: i64 = 0 403 var maxdepth: i64 = 0 404 if root >= 0 { 405 dist[root] = 0 406 reachable = 1 407 // relax up to N rounds: round r settles all nodes at distance r+1 408 var round: i64 = 0 409 while round < n { 410 var changed: i64 = 0 411 var s2: i64 = 0 412 while s2 < n { 413 if dist[s2] >= 0 { 414 var d2: i64 = 0 415 while d2 < n { 416 if adj[s2 * n + d2] == 1 { 417 if dist[d2] < 0 { 418 dist[d2] = dist[s2] + 1 419 if dist[d2] > maxdepth { maxdepth = dist[d2] } 420 reachable = reachable + 1 421 changed = 1 422 } 423 } 424 d2 = d2 + 1 425 } 426 } 427 s2 = s2 + 1 428 } 429 if changed == 0 { round = n } // fixpoint -> stop 430 round = round + 1 431 } 432 } 433 wlp(logfd, " reachable from start (incl start)=\x00" as *u8); wln(logfd, reachable) 434 wlp(logfd, " of \x00" as *u8); wln(logfd, n); wlp(logfd, "\n\x00" as *u8) 435 wlp(logfd, " max clicks-to-reach (graph depth)=\x00" as *u8); wln(logfd, maxdepth); wlp(logfd, "\n\x00" as *u8) 436 // per-page depth 437 wlp(logfd, " depth-from-start:\x00" as *u8) 438 var ri: i64 = 0 439 while ri < n { 440 nx_wiki_doc_store_lookup(store, ri, tp, tn, up, un, bp, bn) 441 wlp(logfd, " \x00" as *u8); wlb(logfd, up[0] as *u8, un[0]); wlp(logfd, "=\x00" as *u8); wln(logfd, dist[ri]) 442 ri = ri + 1 443 } 444 wlp(logfd, "\n\x00" as *u8) 445 // stranded = unreachable 446 var stranded: i64 = 0 447 wlp(logfd, " STRANDED (NOT reachable from start):\x00" as *u8) 448 var si: i64 = 0 449 while si < n { 450 if dist[si] < 0 { 451 nx_wiki_doc_store_lookup(store, si, tp, tn, up, un, bp, bn) 452 wlp(logfd, " \x00" as *u8); wlb(logfd, up[0] as *u8, un[0]) 453 stranded = stranded + 1 454 } 455 si = si + 1 456 } 457 wlp(logfd, " (count=\x00" as *u8); wln(logfd, stranded); wlp(logfd, ")\n\x00" as *u8) 458 459 wlp(logfd, "=== SCORECARD DONE (read-only; corpus untouched) ===\n\x00" as *u8) 460 sys_exit(0) 461 return 0 462}