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}