nx_cite_lib.nx source
↩ module page · 159 lines · 8020 B
1// nx_cite_lib.nx -- F993 CITE-OR-ABSTAIN: deterministic citation verification, the legal flagship.
2//
3// THE 2026 WALL (measured, not rhetorical): independent benchmarks show ~24% of graded legal answers
4// cite or misapply law that does NOT support the claim, and every model tested fabricated at least one
5// citation. The sovereign answer is not a better-behaved chatbot -- it is a VERIFIER that structurally
6// cannot pass a citation whose quoted text is not BYTE-PRESENT in the cited source. No LLM sits in the
7// trust path: a citation is verified by exact substring containment against a stored, content-addressed
8// source, so a fabricated or subtly-altered quote is caught by construction.
9//
10// FAIL-CLOSED, three honest outcomes:
11// CITED (1) the quote is an exact span of a real source -> show the source
12// ABSTAIN_NO_SOURCE (-1) the cited source does not exist (the "made-up case" failure)
13// ABSTAIN_NO_SPAN (-2) the source exists but the quote is not in it (fabricated/altered/empty)
14// An empty quote is ABSTAIN, never CITED -- "the empty string is a substring of everything" is exactly
15// the hole a fabricator would drive through, so it is closed explicitly.
16//
17// BYTE-EXACT is the honest floor. Normalization (whitespace collapse, case-fold, smart-quote unify) is
18// a DECLARED follow-on, not silently applied -- a verifier that quietly normalizes can be tricked into
19// accepting a materially altered quote, so the strict floor ships first and any loosening is opt-in.
20//
21// SCALE ENVELOPE (declared): cite_verify is O(source-length) substring scan per call (Rabin-Karp/KMP is
22// the speed rung); source retrieval inherits the seg-store read cost (see the finance-spine scale note).
23// DRY: composes nx_matter_lib (reg_put/reg_get/mt_field/canon_encode). license_tier: ORIGINAL LIB.
24
25import "nx_matter_lib.nx"
26
27const CITE_CITED: i64 = 1
28const CITE_ABSTAIN_NO_SOURCE: i64 = 0 - 1
29const CITE_ABSTAIN_NO_SPAN: i64 = 0 - 2
30const CITE_SRC_CAP: i64 = 65536 // max stored source length (declared)
31
32func cite_len(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n }
33
34// exact byte-substring: 1 if needle[0..nl) occurs in hay[0..hl), else 0. No break keyword: a mismatch
35// jumps the inner cursor PAST nl so only a full run leaves j==nl.
36func cite_contains(hay: *u8, hl: i64, needle: *u8, nl: i64) -> i64 {
37 if nl == 0 { return 0 }
38 if nl > hl { return 0 }
39 var i: i64 = 0
40 while i + nl <= hl {
41 var j: i64 = 0
42 while j < nl {
43 if hay[i + j] == needle[j] { j = j + 1 } else { j = nl + 1 }
44 }
45 if j == nl { return 1 }
46 i = i + 1
47 }
48 return 0
49}
50
51// store a source document (statute, case, contract clause) on the content-addressed plane.
52func cite_source_put(prefix: *u8, source_id: *u8, text: *u8) -> i64 {
53 let k: *i64 = sys_mmap(8 * 1) as *i64
54 let v: *i64 = sys_mmap(8 * 1) as *i64
55 k[0] = ("text" as *u8) as i64
56 v[0] = text as i64
57 let rec: *u8 = sys_mmap(CITE_SRC_CAP)
58 let rl: i64 = canon_encode(k, v, 1, rec)
59 return reg_put(prefix, "cite:" as *u8, "cite:__idx__" as *u8, source_id, rec, rl)
60}
61
62// retrieve a source's text into out (<= CITE_SRC_CAP); returns length, or -1 if the source is absent.
63func cite_source_text(prefix: *u8, source_id: *u8, out: *u8) -> i64 {
64 let po: *i64 = sys_mmap(16) as *i64
65 let lo: *i64 = sys_mmap(16) as *i64
66 if reg_get(prefix, "cite:" as *u8, source_id, po, lo) != 1 { return 0 - 1 }
67 mt_field(po[0] as *u8, lo[0], "text" as *u8, 4, out)
68 return cite_len(out)
69}
70
71// ★THE VERIFIER. CITED only if `quoted` is an exact byte-span of source_id's stored text.
72// -1 = ABSTAIN, the source does not exist
73// -2 = ABSTAIN, the source exists but the quote is not in it (fabricated / altered / empty)
74func cite_verify(prefix: *u8, source_id: *u8, quoted: *u8) -> i64 {
75 let txt: *u8 = sys_mmap(CITE_SRC_CAP)
76 let tl: i64 = cite_source_text(prefix, source_id, txt)
77 if tl < 0 { return CITE_ABSTAIN_NO_SOURCE }
78 let ql: i64 = cite_len(quoted)
79 if ql == 0 { return CITE_ABSTAIN_NO_SPAN }
80 if cite_contains(txt, tl, quoted, ql) == 1 { return CITE_CITED }
81 return CITE_ABSTAIN_NO_SPAN
82}
83
84// 1 only if a citation is safe to surface. Any ABSTAIN code -> 0. This is the gate a drafting tool
85// calls before it is allowed to print a citation: no green light, no cite.
86func cite_ok(prefix: *u8, source_id: *u8, quoted: *u8) -> i64 {
87 if cite_verify(prefix, source_id, quoted) == CITE_CITED { return 1 }
88 return 0
89}
90
91// ★DOCUMENT REDLINE -- the product surface. A whole drafted paragraph carries inline citations in the
92// convention <<source_id|quoted span>>. This scans EVERY one, verifies it, and writes a human report:
93// [VERIFIED] or [STRUCK ...] per citation. Returns the number of STRUCK (unverifiable) citations; a
94// document is safe to file ONLY when the return is 0. Because the count is what gates the document, a
95// tool physically cannot emit a paragraph with an unverified citation passed off as verified.
96//
97// FAIL-CLOSED ON MALFORMED (the dodge a fabricator would try): an UNTERMINATED citation -- a << with no
98// matching >> -- is counted STRUCK, not silently dropped. Skipping verification by omitting the close
99// is exactly the hole that has to be closed, so an open citation at end-of-document is a strike.
100func cite_scan_doc(prefix: *u8, doc: *u8, report: *u8) -> i64 {
101 let dl: i64 = cite_len(doc)
102 let id: *u8 = sys_mmap(256)
103 let quote: *u8 = sys_mmap(4096)
104 var ro: i64 = 0
105 var struck: i64 = 0
106 var mode: i64 = 0 // 0 outside, 1 reading id, 2 reading quote
107 var ii: i64 = 0
108 var qi: i64 = 0
109 var i: i64 = 0
110 while i < dl {
111 if mode == 0 {
112 var op: i64 = 0
113 if doc[i] == (60 as u8) { if i + 1 < dl { if doc[i+1] == (60 as u8) { op = 1 } } }
114 if op == 1 { mode = 1; ii = 0; i = i + 2 } else { i = i + 1 }
115 } else {
116 if mode == 1 {
117 if doc[i] == (124 as u8) { mode = 2; qi = 0; i = i + 1 }
118 else { if ii < 255 { id[ii] = doc[i]; ii = ii + 1 } i = i + 1 }
119 } else {
120 var cl: i64 = 0
121 if doc[i] == (62 as u8) { if i + 1 < dl { if doc[i+1] == (62 as u8) { cl = 1 } } }
122 if cl == 1 {
123 id[ii] = 0 as u8
124 quote[qi] = 0 as u8
125 let v: i64 = cite_verify(prefix, id, quote)
126 if v == CITE_CITED {
127 ro = mt_catcopy(report, ro, "[VERIFIED] " as *u8)
128 ro = mt_catcopy(report, ro, id)
129 report[ro] = 10 as u8; ro = ro + 1
130 } else {
131 struck = struck + 1
132 ro = mt_catcopy(report, ro, "[STRUCK] " as *u8)
133 ro = mt_catcopy(report, ro, id)
134 if v == CITE_ABSTAIN_NO_SOURCE { ro = mt_catcopy(report, ro, " (source not found)" as *u8) }
135 if v == CITE_ABSTAIN_NO_SPAN { ro = mt_catcopy(report, ro, " (quote not in source)" as *u8) }
136 report[ro] = 10 as u8; ro = ro + 1
137 }
138 mode = 0; i = i + 2
139 } else { if qi < 4095 { quote[qi] = doc[i]; qi = qi + 1 } i = i + 1 }
140 }
141 }
142 }
143 // fail-closed: an unterminated citation at end-of-document is a STRIKE, never a silent skip
144 if mode != 0 {
145 struck = struck + 1
146 ro = mt_catcopy(report, ro, "[STRUCK] <unterminated citation> (malformed, no closing marker)" as *u8)
147 report[ro] = 10 as u8; ro = ro + 1
148 }
149 report[ro] = 0 as u8
150 return struck
151}
152
153// 1 only if EVERY citation in the document verified (and none was malformed). The document-level
154// green light: no clean scan, no filing.
155func cite_doc_clean(prefix: *u8, doc: *u8) -> i64 {
156 let rep: *u8 = sys_mmap(65536)
157 if cite_scan_doc(prefix, doc, rep) == 0 { return 1 }
158 return 0
159}