code wiki / _hdl_build / nx_js_hydrate_index_gate.nx

nx_js_hydrate_index_gate.nx source

↩ module page · 100 lines · 6030 B

1// nx_js_hydrate_index_gate.nx -- CLOSES THE LOOP: a page whose INLINE JS builds content -> hydrate 2// (js_render_page, async-drained) -> extract text -> INDEX into a seg_store shard -> the JS-built token 3// is SEARCHABLE (ss_term finds it), whereas indexing the RAW static HTML does NOT. Proves hydration makes 4// JS content indexable/searchable -- the payoff of the whole JS-execution arc, end-to-end, HERMETIC. 5// 6// D001 MIGRATION 2026-08-22 -- WHY THIS FILE CHANGED. It hand-rolled its own pass/total counters and 7// printed "=== GREEN (4/4) ===" with NO canonical verdict= anchor, so /api/promote REFUSED it by name and 8// nx_gate_green could not judge it: its outcome has been unreadable FROM OUTSIDE for its whole life. 9// It now inherits nx_gate_verdict, so declared == executed BY CONSTRUCTION (a hand-rolled counter can 10// print 5/4 when a tooth silently stops running) and the EXIT CODE CARRIES the verdict. 11// AND THE EMPTY-SET HOLE IS CLOSED: T2 and T4 are ABSENCE assertions -- "the JS token is NOT in the 12// static shard", "'loading' is GONE from the hydrated one" -- and BOTH PASS TRIVIALLY IF NOTHING WAS 13// INDEXED AT ALL. gv_subjects binds them to their denominators, so a hydration that silently produced 14// zero bytes now SKIPs (evidence=none) instead of reporting a clean 4/4. 15// license_tier: ORIGINAL 16import "nx_syscalls.nx" 17import "nx_gate_verdict.nx" // gv_ctr/gv_head/gv_check/gv_subjects/gv_verdict -- the ONE verdict lib 18import "nx_js_eval.nx" // js_render_page 19import "nx_html_to_text.nx" // hydrated/raw HTML -> text 20import "nx_seg_store.nx" // ss_begin/add/commit/open/term/manifest_cap 21 22func r_clen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n } 23 24func next_segid(prefix: *u8) -> i64 { 25 let segs: *i64 = sys_mmap(4096 * 8) as *i64 26 let nseg: i64 = ss_manifest_cap(prefix, segs, 4096) 27 var segid: i64 = 1; var si: i64 = 0 28 // ROOT FIX seq1730 (id 1785450987): segs[] holds POINTERS to seg-<id> name strings 29 // (nx_seg_store.nx:1234 stores segs[cnt] = name as i64), NOT ids -- so segs[si] + 1 produced 30 // MMAP_ADDRESS+1 (~1.4e14), the pointer-shaped poison that PINS a plane forever: every later 31 // epoch id sorts BELOW it in supersede order and its rows are silently shadowed while rc=0. 32 // Parse the DIGITS, as nx_web_shard_compact.nx:55-58 already does on this SAME array. 33 while si < nseg { 34 let sg_nm: *u8 = segs[si] as *u8 35 var sg_v: i64 = 0 36 var sg_ci: i64 = 0 37 while sg_nm[sg_ci] != (0 as u8) { let sg_c: i64 = sg_nm[sg_ci] as i64; if sg_c >= 48 { if sg_c <= 57 { sg_v = sg_v * 10 + (sg_c - 48) } } sg_ci = sg_ci + 1 } 38 if sg_v >= segid { segid = sg_v + 1 } 39 si = si + 1 40 } 41 return segid 42} 43// index `text` as one doc into the shard at `prefix` (fresh segment; re-run-safe). 44func index_doc(prefix: *u8, key: *u8, text: *u8, tlen: i64) -> i64 { 45 let segid: i64 = next_segid(prefix) 46 let w: *i64 = ss_begin() 47 ss_add(w, 1, key, text, tlen) 48 ss_commit(prefix, w, segid) 49 return 0 50} 51// how many docs in the shard contain `term` (0 = not indexed / not searchable). 52func term_count(prefix: *u8, term: *u8) -> i64 { 53 let h: *i64 = ss_open(prefix) 54 if (h as i64) == 0 { return 0 } 55 let kp: *i64 = sys_mmap(64 * 8) as *i64 56 let kl: *i64 = sys_mmap(64 * 8) as *i64 57 return ss_term(h, term, kp, kl, 64) 58} 59 60func main() -> i64 { 61 let ctr: *i64 = gv_ctr() 62 gv_head("=== nx_js_hydrate_index_gate: JS-built content -> hydrate -> index -> SEARCHABLE ===" as *u8) 63 64 // A page whose INLINE SCRIPT replaces "LOADING" with a distinctive token only JS produces. 65 let html: *u8 = "<html><body><div id=\"app\">LOADING</div><script>document.getElementById('app').innerHTML='<p>jshydratedtoken99 kyokahydrated</p>';</script></body></html>\x00" as *u8 66 let hlen: i64 = r_clen(html) 67 68 // STATIC path = what a non-JS crawler indexes (raw HTML -> text). Contains "loading", NOT the token. 69 let stat: *u8 = sys_mmap(65536) 70 let statlen: i64 = nx_html_to_text(html, hlen, stat, 65536) 71 // HYDRATED path = run the JS, serialize, then text. Contains the token, NOT "loading". 72 let hydr: *u8 = sys_mmap(262144) 73 let hyl: i64 = js_render_page(html, hlen, hydr, 262144) 74 let hydtext: *u8 = sys_mmap(65536) 75 let hydtlen: i64 = nx_html_to_text(hydr, hyl, hydtext, 65536) 76 77 index_doc("/tmp/hydidx-static-\x00" as *u8, "doc:s\x00" as *u8, stat, statlen) 78 index_doc("/tmp/hydidx-hydr-\x00" as *u8, "doc:h\x00" as *u8, hydtext, hydtlen) 79 80 let ch: i64 = term_count("/tmp/hydidx-hydr-\x00" as *u8, "jshydratedtoken99\x00" as *u8) 81 let cs: i64 = term_count("/tmp/hydidx-static-\x00" as *u8, "jshydratedtoken99\x00" as *u8) 82 let sl: i64 = term_count("/tmp/hydidx-static-\x00" as *u8, "loading\x00" as *u8) 83 let hl2: i64 = term_count("/tmp/hydidx-hydr-\x00" as *u8, "loading\x00" as *u8) 84 85 // THE DENOMINATORS. T2 and T4 assert ABSENCE and would BOTH pass on an empty index, so bind them to 86 // the populations they claim to be absent FROM before asserting anything about them. 87 gv_subjects("static text bytes indexed" as *u8, statlen, ctr) 88 gv_subjects("hydrated text bytes indexed" as *u8, hydtlen, ctr) 89 90 // T1 THE PAYOFF: the JS-built token is searchable after hydration. 91 gv_check("T1 hydrated: JS token SEARCHABLE" as *u8, (ch >= 1) as i64, ctr) 92 // T2 a static crawler MISSES it (the whole reason this arc exists). 93 gv_check("T2 static: JS token ABSENT" as *u8, (cs == 0) as i64, ctr) 94 // T3 without this control T2 also passes whenever the static shard is simply broken. 95 gv_check("T3 neg-control: static shard works (finds 'loading')" as *u8, (sl >= 1) as i64, ctr) 96 // T4 hydration REPLACED (not appended): 'loading' is gone from the hydrated doc. 97 gv_check("T4 hydration replaced 'loading'" as *u8, (hl2 == 0) as i64, ctr) 98 99 return gv_verdict("JS-HYDRATE-INDEX-GATE" as *u8, ctr, "JS-built token is indexed and searchable; a static crawler misses it" as *u8) 100}