code wiki / _hdl_build / nx_oeuvre_resolve.nx

nx_oeuvre_resolve.nx source

↩ module page · 307 lines · 13167 B

1// nx_oeuvre_resolve.nx -- SREACH-OEUVRE O2: the ANY-NODE RESOLVER. Turns a free-text 2// producer/work/franchise/medium term into a CANONICAL sovereign entity anchor (its Wikidata 3// Q-id), LIVE, over our own TLS stack -- the front door of "search Tinto Brass / Zatoichi / 4// ukiyo-e and get the real entity, not a guess". No privileged root: person, org, franchise, 5// character and medium all resolve through the SAME path (the Q-id is type-agnostic). 6// 7// HONEST SCOPE (rule 5, no-overclaim): the HARD gate is RESOLUTION -- each term fetches its 8// English-Wikipedia page (guarded, sovereign) and yields a DISTINCT real Wikidata Q-id 9// (wgWikibaseItemId, deterministic). COARSE type (person/org/franchise/character/medium) is a 10// best-effort lead-marker heuristic, REPORTED not gated -- authoritative typing = the Wikidata 11// P31 "instance of" statement, which O3 reads when it fetches Wikidata to enumerate works. 12// Resolved anchors persist as resolve:<bk> records in the oeuvre store for O3 to consume. 13// Walls stay named-not-faked (a fetch failure is a finding, not a fabricated id). 14// Evidence -> knowledge/status/oeuvre_resolve.log. license_tier: ORIGINAL 15// 16// module: nishi-core.search.oeuvre.resolve 17// depends: nx_guarded_run.nx, nx_str.nx, nx_seg_store.nx, /tmp/_ed_fetch_html.sov.elf 18// capability: RESOLVER + GATE + EVIDENCE 19import "nx_str.nx" 20import "nx_itoa_lib.nx" // shared MSB-first emitter (zero-alloc) 21import "nx_syscalls.nx" 22import "nx_guarded_run.nx" 23import "nx_seg_store.nx" 24const OR_MAGIC_786432: i64 = 786432 25 26const OR_PREFIX: *u8 = "knowledge/store/oeuvre-" 27const OR_LOG: *u8 = "knowledge/status/oeuvre_resolve.log" 28const OR_NTERMS: i64 = 5 29const OR_DEADLINE: i64 = 18000 30 31func or_p(s: *u8) -> i64 { sys_write(1, s, nx_str_len(s)); return 0 } 32// MIGRATED to the shared emitter (debt 1785563586). The old body mmapped a scratch buffer 33// per call and never freed it. At PAGE granularity that is 4096B leaked PER CALL -- the 34// defect that took 28.5GB of a 36GB host in nx_ts_lumadiff (2MB input, ~3.66M calls). 35// nxi_* is MSB-first, allocates NOTHING, and emits identical bytes including the sign. 36func or_n(v: i64) -> i64 { nxi_out(v); return 0 } 37func or_f(fd: i64, s: *u8) -> i64 { sys_write(fd, s, nx_str_len(s)); return 0 } 38// MIGRATED to the shared emitter (debt 1785563586). The old body mmapped a scratch buffer 39// per call and never freed it. At PAGE granularity that is 4096B leaked PER CALL -- the 40// defect that took 28.5GB of a 36GB host in nx_ts_lumadiff (2MB input, ~3.66M calls). 41// nxi_* is MSB-first, allocates NOTHING, and emits identical bytes including the sign. 42func or_fn(fd: i64, v: i64) -> i64 { nxi_fd(fd, v); return 0 } 43 44func or_cat(dst: *u8, off: i64, s: *u8) -> i64 { var i: i64 = 0; while s[i] != (0 as u8) { dst[off+i] = s[i]; i = i + 1 } return off + i } 45 46func or_streq(a: *u8, b: *u8) -> i64 { 47 var i: i64 = 0 48 while a[i] != (0 as u8) { if a[i] != b[i] { return 0 } i = i + 1 } 49 if b[i] != (0 as u8) { return 0 } 50 return 1 51} 52 53func or_lc(c: i64) -> i64 { if c >= 0x41 { if c <= 0x5A { return c + 0x20 } } return c } 54func or_ci_has(hay: *u8, hn: i64, needle: *u8) -> i64 { 55 let nn: i64 = nx_str_len(needle) 56 if nn > hn { return 0 } 57 var i: i64 = 0 58 while i <= hn - nn { 59 var j: i64 = 0 60 var ok: i64 = 1 61 while j < nn { if or_lc(hay[i+j] as i64) != or_lc(needle[j] as i64) { ok = 0; j = nn } else { j = j + 1 } } 62 if ok == 1 { return 1 } 63 i = i + 1 64 } 65 return 0 66} 67 68// case-sensitive byte find: first index of needle in hay[0..hn), or -1. 69func or_find(hay: *u8, hn: i64, needle: *u8) -> i64 { 70 let nn: i64 = nx_str_len(needle) 71 if nn > hn { return 0 - 1 } 72 var i: i64 = 0 73 while i <= hn - nn { 74 var j: i64 = 0 75 var ok: i64 = 1 76 while j < nn { if hay[i+j] != needle[j] { ok = 0; j = nn } else { j = j + 1 } } 77 if ok == 1 { return i } 78 i = i + 1 79 } 80 return 0 - 1 81} 82 83// extract the Wikidata Q-id into out (NUL-term); returns id length or 0 if none. 84// pattern in the served HTML: wgWikibaseItemId":"Q148378" -- locate the JS var name, then 85// take the first Q<digits> run within the next 40 bytes (no quote-escaping needed). 86func or_qid(html: *u8, hn: i64, out: *u8) -> i64 { 87 let idx: i64 = or_find(html, hn, "wgWikibaseItemId") 88 if idx < 0 { out[0] = 0 as u8; return 0 } 89 var j: i64 = idx + 16 90 let lim: i64 = idx + 56 91 var qpos: i64 = 0 - 1 92 while j < lim { if j < hn { if html[j] == (0x51 as u8) { qpos = j; j = lim } else { j = j + 1 } } else { j = lim } } 93 if qpos < 0 { out[0] = 0 as u8; return 0 } 94 // require Q immediately followed by a digit 95 if qpos + 1 >= hn { out[0] = 0 as u8; return 0 } 96 let d0: i64 = html[qpos + 1] as i64 97 if d0 < 48 { out[0] = 0 as u8; return 0 } 98 if d0 > 57 { out[0] = 0 as u8; return 0 } 99 var k: i64 = 0 100 out[k] = 0x51 as u8; k = k + 1 101 var p: i64 = qpos + 1 102 var go: i64 = 1 103 while go == 1 { 104 if p >= hn { go = 0 } else { 105 let c: i64 = html[p] as i64 106 if c >= 48 { if c <= 57 { out[k] = c as u8; k = k + 1; p = p + 1 } else { go = 0 } } else { go = 0 } 107 } 108 } 109 out[k] = 0 as u8 110 return k 111} 112 113// best-effort coarse type from lead markers (REPORTED, not gated). 0 person 1 org 2 franchise 114// 3 character 4 medium -1 unknown. Priority surfaces the search-useful anchor (a franchise over 115// its character, so "Zatoichi" yields the films). 116func or_type(html: *u8, hn: i64) -> i64 { 117 if or_ci_has(html, hn, "woodblock") == 1 { return 4 } 118 if or_ci_has(html, hn, "ukiyo") == 1 { return 4 } 119 if or_ci_has(html, hn, "art movement") == 1 { return 4 } 120 if or_ci_has(html, hn, "film series") == 1 { return 2 } 121 if or_ci_has(html, hn, "media franchise") == 1 { return 2 } 122 if or_ci_has(html, hn, "fictional character") == 1 { return 3 } 123 if or_ci_has(html, hn, "manufacturer") == 1 { return 1 } 124 if or_ci_has(html, hn, "film studio") == 1 { return 1 } 125 if or_ci_has(html, hn, "corporation") == 1 { return 1 } 126 if or_ci_has(html, hn, "actress") == 1 { return 0 } 127 if or_ci_has(html, hn, "actor") == 1 { return 0 } 128 if or_ci_has(html, hn, "director") == 1 { return 0 } 129 if or_ci_has(html, hn, "photographer") == 1 { return 0 } 130 if or_ci_has(html, hn, "artist") == 1 { return 0 } 131 if or_ci_has(html, hn, "company") == 1 { return 1 } 132 return 0 - 1 133} 134func or_tname(t: i64) -> *u8 { 135 if t == 0 { return "person" } 136 if t == 1 { return "org" } 137 if t == 2 { return "franchise" } 138 if t == 3 { return "character" } 139 if t == 4 { return "medium" } 140 return "unknown" 141} 142 143func or_term(i: i64) -> *u8 { 144 if i == 0 { return "Diora Baird" } 145 if i == 1 { return "Daiei Film" } 146 if i == 2 { return "Klein Tools" } 147 if i == 3 { return "Zatoichi" } 148 return "Japanese woodblock prints (ukiyo-e)" 149} 150func or_bk(i: i64) -> *u8 { 151 if i == 0 { return "diora_baird" } 152 if i == 1 { return "daiei_film" } 153 if i == 2 { return "klein_tools" } 154 if i == 3 { return "zatoichi" } 155 return "japanese_woodblock_prints" 156} 157func or_wpath(i: i64) -> *u8 { 158 if i == 0 { return "Diora_Baird" } 159 if i == 1 { return "Daiei_Film" } 160 if i == 2 { return "Klein_Tools" } 161 if i == 3 { return "Zatoichi" } 162 return "Woodblock_printing_in_Japan" 163} 164func or_exptype(i: i64) -> i64 { 165 if i == 0 { return 0 } 166 if i == 1 { return 1 } 167 if i == 2 { return 1 } 168 if i == 3 { return 2 } 169 return 4 170} 171func or_out(i: i64) -> *u8 { 172 if i == 0 { return "/tmp/ores_0.html\x00" } 173 if i == 1 { return "/tmp/ores_1.html\x00" } 174 if i == 2 { return "/tmp/ores_2.html\x00" } 175 if i == 3 { return "/tmp/ores_3.html\x00" } 176 return "/tmp/ores_4.html\x00" 177} 178 179func or_read_all(path: *u8, buf: *u8, cap: i64) -> i64 { 180 let fd: i64 = sys_openat_rd(path) 181 if fd < 0 { return 0 } 182 var total: i64 = 0 183 var go: i64 = 1 184 while go == 1 { 185 let n: i64 = sys_read(fd, (((buf as i64) + total) as *u8), cap - total) 186 if n <= 0 { go = 0 } 187 if n > 0 { total = total + n; if total >= cap { go = 0 } } 188 } 189 sys_close(fd) 190 return total 191} 192 193func or_seg_next() -> i64 { 194 let segs: *i64 = sys_mmap(8 * 260) as *i64 195 let nseg: i64 = ss_manifest(OR_PREFIX, segs) 196 if nseg < 0 { return 1 } 197 return 1 + nseg 198} 199 200func main() -> i64 { 201 or_p("=== SREACH-OEUVRE O2: any-node resolver (term -> canonical Wikidata Q-id, live sovereign) ===\n") 202 let child: *u8 = "/tmp/_ed_fetch_html.sov.elf\x00" 203 let pr: i64 = sys_openat_rd(child) 204 if pr < 0 { or_p(" prereq missing: /tmp/_ed_fetch_html.sov.elf (build it first)\n"); sys_exit(3); return 3 } 205 sys_close(pr) 206 let envp: *i64 = sys_mmap(16) as *i64 207 envp[0] = "PATH=/usr/bin:/bin" as *u8 as i64; envp[1] = 0 208 let devnull: i64 = sys_openat_wr("/dev/null" as *u8, 0x1a4) 209 let lfd: i64 = sys_openat_append(OR_LOG, 0x1a4) 210 if lfd >= 0 { or_f(lfd, "ORES-RUN epoch=" as *u8); or_fn(lfd, sys_now_realtime_sec()); or_f(lfd, "\n" as *u8) } 211 212 let qids: **u8 = sys_mmap(OR_NTERMS * 8) as **u8 213 let w: *i64 = ss_begin() 214 var resolved: i64 = 0 215 var type_ok: i64 = 0 216 var towrite: i64 = 0 217 218 var i: i64 = 0 219 while i < OR_NTERMS { 220 let url: *u8 = sys_mmap(512) 221 var uo: i64 = or_cat(url, 0, "https://en.wikipedia.org/wiki/" as *u8) 222 uo = or_cat(url, uo, or_wpath(i)); url[uo] = 0 as u8 223 let av: *i64 = sys_mmap(8 * 4) as *i64 224 av[0] = child as i64; av[1] = url as i64; av[2] = or_out(i) as i64; av[3] = 0 225 let rc: i64 = nx_guarded_run(child, av, envp, OR_DEADLINE, devnull, devnull) 226 or_p(" "); or_p(or_term(i)); or_p(" -> ") 227 let qbuf: *u8 = sys_mmap(64) 228 qbuf[0] = 0 as u8 229 var ty: i64 = 0 - 1 230 if rc != 0 { 231 if rc == 124 { or_p("TIMEOUT-REAPED\n") } else { or_p("walled/fail rc="); or_n(rc); or_p(" (named)\n") } 232 } else { 233 let html: *u8 = sys_mmap(OR_MAGIC_786432) 234 let hn: i64 = or_read_all(or_out(i), html, OR_MAGIC_786432) 235 let ql: i64 = or_qid(html, hn, qbuf) 236 ty = or_type(html, hn) 237 if ql > 0 { 238 resolved = resolved + 1 239 or_p(qbuf); or_p(" type="); or_p(or_tname(ty)); or_p(" ("); or_n(hn); or_p(" html bytes)\n") 240 if ty == or_exptype(i) { type_ok = type_ok + 1 } 241 // persist resolve:<bk> -> term=..|qid=..|type=..|src=.. 242 let val: *u8 = sys_mmap(640) 243 var vo: i64 = or_cat(val, 0, "term=" as *u8) 244 vo = or_cat(val, vo, or_term(i)) 245 vo = or_cat(val, vo, "|qid=" as *u8); vo = or_cat(val, vo, qbuf) 246 vo = or_cat(val, vo, "|type=" as *u8); vo = or_cat(val, vo, or_tname(ty)) 247 vo = or_cat(val, vo, "|src=" as *u8); vo = or_cat(val, vo, url); val[vo] = 0 as u8 248 let key: *u8 = sys_mmap(128) 249 var ko: i64 = or_cat(key, 0, "resolve:" as *u8); ko = or_cat(key, ko, or_bk(i)); key[ko] = 0 as u8 250 ss_add(w, 1, key, val, vo); towrite = towrite + 1 251 } else { 252 or_p("NO-QID (html="); or_n(hn); or_p(" bytes)\n") 253 } 254 } 255 qids[i] = qbuf 256 if lfd >= 0 { 257 or_f(lfd, "ORES term=" as *u8); or_f(lfd, or_bk(i)) 258 or_f(lfd, " rc=" as *u8); or_fn(lfd, rc) 259 or_f(lfd, " qid=" as *u8); if qbuf[0] != (0 as u8) { or_f(lfd, qbuf) } else { or_f(lfd, "-" as *u8) } 260 or_f(lfd, " type=" as *u8); or_f(lfd, or_tname(ty)); or_f(lfd, "\n" as *u8) 261 } 262 i = i + 1 263 } 264 265 var committed: i64 = 0 266 if towrite > 0 { 267 let rc2: i64 = ss_commit(OR_PREFIX, w, or_seg_next()) 268 if rc2 == 0 { committed = 1 } 269 } 270 271 // distinctness of the resolved Q-ids (a resolver that maps two terms to one id is broken). 272 var distinct: i64 = 1 273 var a: i64 = 0 274 while a < OR_NTERMS { 275 if qids[a][0] != (0 as u8) { 276 var b: i64 = a + 1 277 while b < OR_NTERMS { 278 if qids[b][0] != (0 as u8) { if or_streq(qids[a], qids[b]) == 1 { distinct = 0 } } 279 b = b + 1 280 } 281 } 282 a = a + 1 283 } 284 285 var ok: i64 = 1 286 if resolved != OR_NTERMS { ok = 0 } 287 if distinct != 1 { ok = 0 } 288 if towrite > 0 { if committed == 0 { ok = 0 } } 289 290 or_p(" resolved="); or_n(resolved); or_p("/"); or_n(OR_NTERMS) 291 or_p(" distinct="); or_n(distinct) 292 or_p(" type_match="); or_n(type_ok); or_p("/"); or_n(OR_NTERMS); or_p(" (reported)\n") 293 if lfd >= 0 { 294 or_f(lfd, "ORES-GATE resolved=" as *u8); or_fn(lfd, resolved) 295 or_f(lfd, "/" as *u8); or_fn(lfd, OR_NTERMS) 296 or_f(lfd, " distinct=" as *u8); or_fn(lfd, distinct) 297 or_f(lfd, " type_match=" as *u8); or_fn(lfd, type_ok) 298 or_f(lfd, " written=" as *u8); or_fn(lfd, towrite) 299 if ok == 1 { or_f(lfd, " verdict=GREEN\n" as *u8) } else { or_f(lfd, " verdict=RED\n" as *u8) } 300 sys_close(lfd) 301 } 302 303 if ok == 1 { or_p(" OEUVRE-RESOLVE O2: GREEN (every term -> a distinct live Wikidata anchor)\n"); sys_exit(0); return 0 } 304 or_p(" OEUVRE-RESOLVE O2: RED\n") 305 sys_exit(1) 306 return 1 307}