code wiki / _hdl_build / nx_wiki_search_page.nx
nx_wiki_search_page.nx source
↩ module page · 102 lines · 8223 B
1// nx_wiki_search_page.nx -- GENERATE the live "/wiki/search.html" instant-answer page (IMS Thrust B).
2// A walkable Diataxis REFERENCE page built through the reusable typed generator nx_ims_page (full walk
3// furniture: head/nav/breadcrumb/tree/prev-next/related/backlinks/infobox/toc/footer, nav model read from
4// wiki_links.tsv by slug=search). The page's OWN content (body_html) is:
5// 1. a search box (GET form -> /wiki/search.html?q=...) -- the same form the site nav already exposes,
6// 2. an INSTANT-ANSWER CARD section: for 3 representative queries we call the FIXED answer organ
7// (nx_wiki_answer.answer) AT BUILD TIME and embed the returned snippet + the CORRECT source link,
8// 3. a short "how it works" note (registry-driven BM25F + densest-cluster snippet + NO_ANSWER teeth).
9//
10// This demonstrates the instant-answer card end-to-end with REAL output (not hand-written): the snippet is
11// produced by nx_search_snippet_extract (already html-escaped + <mark>-highlighted, safe to embed) and the
12// source link is base_url+slug.html exactly as the answer organ resolves it. STATIC page (the answer is
13// rendered at build time for the example queries); a live daemon /wiki/search route would call answer() per
14// request -- noted in the page itself. ADDITIVE: writes web_assets/search.html only.
15//
16// REUSE (nothing reinvented): nx_ims_page ims_page (the typed walkable generator) + nx_wiki_answer answer/
17// wa_new/WaAnswer (the fixed instant-answer engine). license_tier: ORIGINAL
18import "nx_ims_page.nx"
19import "nx_itoa_lib.nx" // shared MSB-first emitter (zero-alloc)
20import "nx_wiki_answer.nx"
21
22const WSP_BODY_CAP: i64 = 65536 // body_html scratch (>> the few KB this page needs)
23
24func wsp_w(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 }
25// MIGRATED to the shared emitter (debt 1785563586). The old body mmapped a scratch buffer
26// per call and never freed it. At PAGE granularity that is 4096B leaked PER CALL -- the
27// defect that took 28.5GB of a 36GB host in nx_ts_lumadiff (2MB input, ~3.66M calls).
28// nxi_* is MSB-first, allocates NOTHING, and emits identical bytes including the sign.
29func wsp_num(v: i64) -> i64 { nxi_out(v); return 0 }
30
31// append NUL-terminated s into dst at off; return new off.
32func wsp_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 }
33// append a counted (non-NUL-terminated) byte run.
34func wsp_catb(dst: *u8, off: i64, s: *u8, n: i64) -> i64 { var i: i64 = 0; while i < n { dst[off + i] = s[i]; i = i + 1 } return off + i }
35func wsp_slen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n }
36
37// render ONE instant-answer card for `query` into dst at off (calls the live answer organ). Returns new off.
38// On WA_OK: a card with the query, the (escaped+marked) snippet in a blockquote, and the CORRECT source link.
39// On WA_NO_ANSWER (e.g. the nonsense demo): a "refused" card -- proves the no-fabrication teeth, no fake link.
40func wsp_card(dst: *u8, off: i64, query: *u8) -> i64 {
41 let a: *WaAnswer = wa_new()
42 answer(query, a)
43 var o: i64 = off
44 o = wsp_cat(dst, o, "<div class=\"answer-card\"><p class=\"q\"><strong>Q:</strong> " as *u8)
45 o = wsp_cat(dst, o, query)
46 o = wsp_cat(dst, o, "</p>\n" as *u8)
47 if a.verdict == WA_OK {
48 // snippet is already html-escaped + <mark>-highlighted by nx_search_snippet_extract -> embed directly.
49 o = wsp_cat(dst, o, "<blockquote class=\"answer\">" as *u8)
50 o = wsp_catb(dst, o, a.snippet, wsp_slen(a.snippet))
51 o = wsp_cat(dst, o, "</blockquote>\n" as *u8)
52 // CORRECT source link: base_url + slug + .html (e.g. https://nishifamily.com/wiki/access_wall.html).
53 // Use the on-site relative form /wiki/<slug>.html so the link stays inside the corpus (guard-safe).
54 o = wsp_cat(dst, o, "<p class=\"src\">Source: <a href=\"/wiki/" as *u8)
55 o = wsp_catb(dst, o, a.slug, wsp_slen(a.slug))
56 o = wsp_cat(dst, o, ".html\"><code>" as *u8)
57 o = wsp_catb(dst, o, a.slug, wsp_slen(a.slug))
58 o = wsp_cat(dst, o, ".html</code></a></p>\n" as *u8)
59 wsp_w(" card OK q='"); wsp_w(query); wsp_w("' -> /wiki/"); wsp_w(a.slug); wsp_w(".html\n")
60 } else {
61 o = wsp_cat(dst, o, "<p class=\"noanswer\"><em>No confident answer — the engine refuses rather than fabricate (BM25F below the registered floor).</em></p>\n" as *u8)
62 wsp_w(" card REFUSED q='"); wsp_w(query); wsp_w("' (no fabrication)\n")
63 }
64 o = wsp_cat(dst, o, "</div>\n" as *u8)
65 return o
66}
67
68func main() -> i64 {
69 let b: *u8 = sys_mmap(WSP_BODY_CAP)
70 var o: i64 = 0
71
72 // ---- lede + the search box (GET -> /wiki/search.html, matching the site nav form) ----
73 o = wsp_cat(b, o, "<p class=\"lede\"><strong>Instant answers over the live wiki.</strong> Ask a question and get the passage that answers it — with its source page — in one step, no results list to click through. The ranker is sovereign BM25F over a clean per-page index; the answer is the densest cluster of your terms on the top page. If nothing scores above the confidence floor, the engine says so instead of inventing an answer.</p>\n" as *u8)
74 o = wsp_cat(b, o, "<form class=\"wiki-search\" action=\"/wiki/search.html\" method=\"get\"><input type=\"search\" name=\"q\" placeholder=\"Ask the wiki… (e.g. zero trust access)\" aria-label=\"Search the wiki\"><button type=\"submit\">Answer</button></form>\n" as *u8)
75
76 // ---- the instant-answer cards: REAL output from the fixed answer organ at build time ----
77 o = wsp_cat(b, o, "<h3>Example instant answers</h3>\n" as *u8)
78 o = wsp_cat(b, o, "<p>Each card below was rendered by calling the answer organ (<code>nx_wiki_answer</code>) at build time over the live index — the snippet and source link are real, not hand-written.</p>\n" as *u8)
79 o = wsp_card(b, o, "zero trust access" as *u8)
80 o = wsp_card(b, o, "capitalism lab game" as *u8)
81 o = wsp_card(b, o, "genesis genealogy lineage" as *u8)
82 // a teeth demo: nonsense -> refusal (no fabricated source).
83 o = wsp_card(b, o, "xyzzy qwffz" as *u8)
84
85 // ---- how it works ----
86 o = wsp_cat(b, o, "<h3 id=\"how\">How it works</h3>\n" as *u8)
87 o = wsp_cat(b, o, "<ol>\n" as *u8)
88 o = wsp_cat(b, o, "<li><strong>Clean index.</strong> Each of the 13 live pages is fetched, the nav/breadcrumb/footer chrome is stripped, and the visible prose is indexed — so common chrome words don't poison relevance.</li>\n" as *u8)
89 o = wsp_cat(b, o, "<li><strong>BM25F ranking.</strong> The fielded ranker boosts a term that appears in the page <em>title</em> (the page is about it), then picks the single best page.</li>\n" as *u8)
90 o = wsp_cat(b, o, "<li><strong>Densest-cluster snippet.</strong> The answer is the tightest window of your query terms on that page, highlighted.</li>\n" as *u8)
91 o = wsp_cat(b, o, "<li><strong>No-fabrication floor.</strong> If the top score is below the registered confidence threshold, the answer is withheld — the engine never makes one up.</li>\n" as *u8)
92 o = wsp_cat(b, o, "</ol>\n" as *u8)
93 o = wsp_cat(b, o, "<p class=\"why\">This page is <strong>static</strong>: the example cards are rendered at build time. A live <code>/wiki/search</code> daemon route would call the same <code>answer()</code> per request with your typed query — the engine and verdicts are identical; only the trigger differs.</p>\n" as *u8)
94 o = wsp_cat(b, o, "<p>Related: <a href=\"/wiki/board.html\">workstream board</a> · <a href=\"/wiki/devlog.html\">devlog</a> · <a href=\"/wiki/roadmap.html\">roadmap</a>.</p>\n" as *u8)
95 b[o] = 0 as u8
96
97 wsp_w("=== nx_wiki_search_page: rendering /wiki/search.html (reference, walkable) ===\n")
98 let bytes: i64 = ims_page("search" as *u8, "reference" as *u8, "Wiki Search — Instant Answers" as *u8, b)
99 if bytes < 0 { wsp_w("FATAL: ims_page failed rc="); wsp_num(bytes); wsp_w("\n"); sys_exit(2); return 2 }
100 wsp_w("=== wrote web_assets/search.html bytes="); wsp_num(bytes); wsp_w(" ===\n")
101 sys_exit(0); return 0
102}