nx_writehub_factcheck.nx source
↩ module page · 224 lines · 11478 B
1// nx_writehub_factcheck.nx -- WRITING HUB rung R4: sovereign GROUNDED FACT-CHECK for non-fiction. Closes the
2// field-wide gap: every AI book-writer ships a "fact-check agent" box that verifies NOTHING (undocumented). This
3// organ GROUNDS a claim in the sovereign-banked corpus: extract the claim's distinctive terms -> scan each banked
4// source for term co-occurrence -> corroborate with INDEPENDENCE (composes nx_research_corroborate: same
5// source-class = ECHO; CONFIRMED needs >=2 DISTINCT classes) -> verdict CONFIRMED/SINGLE/UNSUPPORTED with CITED
6// sources (provenance; no-delete). Data-driven corpus manifest (knowledge/registry/writehub_corpus.tsv). HONEST:
7// this is keyword-grounded corroboration (terms attested across independent sources), NOT full semantic
8// entailment -- but a REAL grounding rung that EXCEEDS "no grounding at all". Self-proves on a supported vs an
9// unsupported claim (the discriminator gate). license_tier: ORIGINAL module: nishi-core.writehub.factcheck
10import "nx_research_corroborate.nx"
11import "nx_syscalls.nx"
12const K_MAGIC_4096: i64 = 4096
13const K_MAGIC_65536: i64 = 65536
14const K_MAGIC_2097152: i64 = 2097152
15const K_MAGIC_8192: i64 = 8192
16const K_MAGIC_8191: i64 = 8191
17
18func fc_lower(c: i64) -> i64 { if c>=65 { if c<=90 { return c+32 } } return c }
19func fc_is_alpha(c: i64) -> i64 { let l: i64=fc_lower(c); if l>=97 { if l<=122 { return 1 } } return 0 }
20func fc_strlen(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} return n }
21
22func wp(fd: i64, s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); if fd>0 { sys_write(fd,s,n) } return 0 }
23func wn(fd: i64, v: i64) -> i64 {
24 var m: i64=v; if m<0 { wp(fd,"-" as *u8); m=0-m }
25 let t: *u8=sys_mmap(28); var k: i64=0; if m==0 { t[0]=48 as u8; k=1 }
26 while m>0 { t[k]=(48+(m%10)) as u8; m=m/10; k=k+1 }
27 let b: *u8=sys_mmap(28); var i: i64=0; while i<k { b[i]=t[k-1-i]; i=i+1 }
28 sys_write(1,b,k); if fd>0 { sys_write(fd,b,k) } return 0
29}
30
31func fc_wordeq(claim: *u8, a: i64, b: i64, lit: *u8) -> i64 {
32 var i: i64=a; var j: i64=0
33 while i<b { if lit[j]==(0 as u8) { return 0 } if fc_lower(claim[i] as i64)!=lit[j] as i64 { return 0 } i=i+1; j=j+1 }
34 if lit[j]!=(0 as u8) { return 0 }
35 return 1
36}
37func fc_is_stop(claim: *u8, a: i64, b: i64) -> i64 {
38 if fc_wordeq(claim,a,b,"that" as *u8)==1 { return 1 }
39 if fc_wordeq(claim,a,b,"this" as *u8)==1 { return 1 }
40 if fc_wordeq(claim,a,b,"with" as *u8)==1 { return 1 }
41 if fc_wordeq(claim,a,b,"have" as *u8)==1 { return 1 }
42 if fc_wordeq(claim,a,b,"from" as *u8)==1 { return 1 }
43 if fc_wordeq(claim,a,b,"they" as *u8)==1 { return 1 }
44 if fc_wordeq(claim,a,b,"will" as *u8)==1 { return 1 }
45 if fc_wordeq(claim,a,b,"into" as *u8)==1 { return 1 }
46 if fc_wordeq(claim,a,b,"than" as *u8)==1 { return 1 }
47 if fc_wordeq(claim,a,b,"then" as *u8)==1 { return 1 }
48 if fc_wordeq(claim,a,b,"them" as *u8)==1 { return 1 }
49 if fc_wordeq(claim,a,b,"what" as *u8)==1 { return 1 }
50 if fc_wordeq(claim,a,b,"when" as *u8)==1 { return 1 }
51 if fc_wordeq(claim,a,b,"which" as *u8)==1 { return 1 }
52 if fc_wordeq(claim,a,b,"their" as *u8)==1 { return 1 }
53 if fc_wordeq(claim,a,b,"there" as *u8)==1 { return 1 }
54 if fc_wordeq(claim,a,b,"been" as *u8)==1 { return 1 }
55 if fc_wordeq(claim,a,b,"were" as *u8)==1 { return 1 }
56 if fc_wordeq(claim,a,b,"using" as *u8)==1 { return 1 }
57 if fc_wordeq(claim,a,b,"used" as *u8)==1 { return 1 }
58 if fc_wordeq(claim,a,b,"such" as *u8)==1 { return 1 }
59 if fc_wordeq(claim,a,b,"also" as *u8)==1 { return 1 }
60 if fc_wordeq(claim,a,b,"more" as *u8)==1 { return 1 }
61 if fc_wordeq(claim,a,b,"only" as *u8)==1 { return 1 }
62 if fc_wordeq(claim,a,b,"other" as *u8)==1 { return 1 }
63 if fc_wordeq(claim,a,b,"these" as *u8)==1 { return 1 }
64 if fc_wordeq(claim,a,b,"those" as *u8)==1 { return 1 }
65 if fc_wordeq(claim,a,b,"about" as *u8)==1 { return 1 }
66 if fc_wordeq(claim,a,b,"would" as *u8)==1 { return 1 }
67 if fc_wordeq(claim,a,b,"could" as *u8)==1 { return 1 }
68 if fc_wordeq(claim,a,b,"should" as *u8)==1 { return 1 }
69 if fc_wordeq(claim,a,b,"through" as *u8)==1 { return 1 }
70 if fc_wordeq(claim,a,b,"involves" as *u8)==1 { return 1 }
71 if fc_wordeq(claim,a,b,"involve" as *u8)==1 { return 1 }
72 if fc_wordeq(claim,a,b,"requires" as *u8)==1 { return 1 }
73 if fc_wordeq(claim,a,b,"require" as *u8)==1 { return 1 }
74 if fc_wordeq(claim,a,b,"between" as *u8)==1 { return 1 }
75 if fc_wordeq(claim,a,b,"within" as *u8)==1 { return 1 }
76 return 0
77}
78
79// distinctive lowercased terms from claim[0..cn) -> termbuf (null-term each); termoff[t]=offset; return count
80func fc_extract(claim: *u8, cn: i64, termbuf: *u8, termoff: *i64, maxt: i64) -> i64 {
81 var nt: i64=0; var tb: i64=0; var i: i64=0; var ws: i64=0-1
82 while i<=cn {
83 var alpha: i64=0
84 if i<cn { if fc_is_alpha(claim[i] as i64)==1 { alpha=1 } }
85 if alpha==1 { if ws<0 { ws=i } }
86 else {
87 if ws>=0 {
88 let wl: i64=i-ws
89 if wl>=4 { if fc_is_stop(claim,ws,i)==0 {
90 if nt<maxt {
91 termoff[nt]=tb
92 var k: i64=0
93 while k<wl { termbuf[tb]=fc_lower(claim[ws+k] as i64) as u8; tb=tb+1; k=k+1 }
94 termbuf[tb]=0 as u8; tb=tb+1
95 nt=nt+1
96 }
97 } }
98 ws=0-1
99 }
100 }
101 i=i+1
102 }
103 return nt
104}
105
106// case-insensitive substring: hay[0..hn) contains the (already-lowercased) null-term needle?
107func fc_has(hay: *u8, hn: i64, needle: *u8) -> i64 {
108 var nl: i64=0; while needle[nl]!=(0 as u8){nl=nl+1}
109 if nl==0 { return 0 }
110 var i: i64=0
111 while i+nl<=hn {
112 var j: i64=0; var m: i64=1
113 while j<nl { if fc_lower(hay[i+j] as i64)!=needle[j] as i64 { m=0; j=nl } else { j=j+1 } }
114 if m==1 { return 1 }
115 i=i+1
116 }
117 return 0
118}
119
120// parse manifest (mutates mbuf -> null-terminates paths); fill srcpath[]/srcclass[]; return nsrc
121func fc_parse_manifest(mbuf: *u8, mn: i64, srcpath: *i64, srcclass: *i64, maxsrc: i64) -> i64 {
122 var nsrc: i64=0; var i: i64=0
123 while i<mn {
124 let c0: i64=mbuf[i] as i64
125 if c0==10 { i=i+1 }
126 else { if c0==35 {
127 var go: i64=1
128 while go==1 { if i>=mn { go=0 } else { if mbuf[i]==(10 as u8) { go=0 } i=i+1 } }
129 } else {
130 let lstart: i64=i
131 var g1: i64=1
132 while g1==1 { if i>=mn { g1=0 } else { if mbuf[i]==(9 as u8) { g1=0 } else { if mbuf[i]==(10 as u8) { g1=0 } else { i=i+1 } } } }
133 var hadtab: i64=0
134 if i<mn { if mbuf[i]==(9 as u8) { hadtab=1 } }
135 if hadtab==1 {
136 mbuf[i]=0 as u8; i=i+1
137 var cls: i64=0; var g2: i64=1
138 while g2==1 { if i>=mn { g2=0 } else { let d: i64=mbuf[i] as i64; if d>=48 { if d<=57 { cls=cls*10+(d-48); i=i+1 } else { g2=0 } } else { g2=0 } } }
139 if nsrc<maxsrc { srcpath[nsrc]=lstart+(mbuf as i64); srcclass[nsrc]=cls; nsrc=nsrc+1 }
140 var g3: i64=1
141 while g3==1 { if i>=mn { g3=0 } else { if mbuf[i]==(10 as u8) { g3=0 } i=i+1 } }
142 } else { if i<mn { i=i+1 } }
143 } }
144 }
145 return nsrc
146}
147
148func fc_check(label: *u8, claim: *u8, srcpath: *i64, srcclass: *i64, nsrc: i64, srcbuf: *u8, srccap: i64, fd: i64) -> i64 {
149 let cn: i64=fc_strlen(claim)
150 let termbuf: *u8=sys_mmap(K_MAGIC_4096)
151 let termoff: *i64=sys_mmap(8*64) as *i64
152 let nt: i64=fc_extract(claim, cn, termbuf, termoff, 24)
153 var T: i64=(nt+1)/2
154 if T<2 { T=2 }
155 if T>nt { T=nt }
156 if nt==0 { T=1 }
157 wp(fd," CLAIM [" as *u8); wp(fd,label); wp(fd,"]: " as *u8); wp(fd,claim); wp(fd,"\n" as *u8)
158 wp(fd," key-terms(" as *u8); wn(fd,nt); wp(fd,", need>=" as *u8); wn(fd,T); wp(fd,"): " as *u8)
159 var ti: i64=0
160 while ti<nt { wp(fd,(termbuf as i64 + termoff[ti]) as *u8); wp(fd," " as *u8); ti=ti+1 }
161 wp(fd,"\n" as *u8)
162 let cr: *NxCorrob=nx_corrob_new(128)
163 var s: i64=0
164 while s<nsrc {
165 let path: *u8=srcpath[s] as *u8
166 let sfd: i64=sys_openat_rd(path)
167 if sfd>=0 {
168 let sn: i64=sys_read(sfd, srcbuf, srccap)
169 sys_close(sfd)
170 if sn>0 {
171 var hits: i64=0; var k: i64=0
172 while k<nt { if fc_has(srcbuf, sn, (termbuf as i64 + termoff[k]) as *u8)==1 { hits=hits+1 } k=k+1 }
173 if hits>=T {
174 nx_corrob_assert(cr, 1, s, srcclass[s])
175 wp(fd," [attest] " as *u8); wp(fd,path); wp(fd," (class " as *u8); wn(fd,srcclass[s]); wp(fd,", " as *u8); wn(fd,hits); wp(fd,"/" as *u8); wn(fd,nt); wp(fd," terms)\n" as *u8)
176 }
177 }
178 }
179 s=s+1
180 }
181 let conf: i64=nx_corrob_confidence(cr, 1)
182 let ind: i64=nx_corrob_independent_count(cr, 1)
183 wp(fd," -> independent-classes=" as *u8); wn(fd,ind); wp(fd," VERDICT=" as *u8)
184 if conf==NX_CORR_CONFIRMED { wp(fd,"CONFIRMED (>=2 independent source-classes)" as *u8) }
185 else { if conf==NX_CORR_SINGLE { wp(fd,"SINGLE (1 class -- uncorroborated)" as *u8) } else { wp(fd,"UNSUPPORTED (no banked source attests)" as *u8) } }
186 wp(fd,"\n" as *u8)
187 return conf
188}
189
190func main() -> i64 {
191 let fd: i64=sys_openat_append("knowledge/status/writehub_factcheck.log" as *u8, 0x1a4)
192 wp(fd,"=== WRITEHUB GROUNDED FACT-CHECK (claim vs banked corpus, independence-aware; sovereign) ===\n" as *u8)
193 let mfd: i64=sys_openat_rd("knowledge/registry/writehub_corpus.tsv" as *u8)
194 if mfd<0 { wp(fd,"FACTCHECK: no corpus manifest (knowledge/registry/writehub_corpus.tsv)\n" as *u8); sys_exit(1); return 1 }
195 let mbuf: *u8=sys_mmap(K_MAGIC_65536)
196 let mn: i64=sys_read(mfd, mbuf, K_MAGIC_65536)
197 sys_close(mfd)
198 let srcpath: *i64=sys_mmap(8*64) as *i64
199 let srcclass: *i64=sys_mmap(8*64) as *i64
200 let nsrc: i64=fc_parse_manifest(mbuf, mn, srcpath, srcclass, 64)
201 wp(fd," corpus sources: " as *u8); wn(fd,nsrc); wp(fd,"\n" as *u8)
202 let srcbuf: *u8=sys_mmap(K_MAGIC_2097152)
203 let srccap: i64=K_MAGIC_2097152
204
205 let sup: *u8="narrative writing involves characters, scenes, and dialogue" as *u8
206 let unsup: *u8="narrative writing requires photosynthesis and volcanic tectonics" as *u8
207 let cs: i64=fc_check("supported " as *u8, sup, srcpath, srcclass, nsrc, srcbuf, srccap, fd)
208 let cu: i64=fc_check("unsupported" as *u8, unsup, srcpath, srcclass, nsrc, srcbuf, srccap, fd)
209
210 let lfd: i64=sys_openat_rd("knowledge/staging/writehub_claim.txt" as *u8)
211 if lfd>=0 {
212 let lb: *u8=sys_mmap(K_MAGIC_8192)
213 let ln: i64=sys_read(lfd, lb, K_MAGIC_8191)
214 sys_close(lfd)
215 if ln>0 { lb[ln]=0 as u8; fc_check("live " as *u8, lb, srcpath, srcclass, nsrc, srcbuf, srccap, fd) }
216 }
217
218 var ok: i64=1
219 if cs<NX_CORR_SINGLE { ok=0 }
220 if cu!=NX_CORR_NONE { ok=0 }
221 wp(fd," discriminator: supported=" as *u8); wn(fd,cs); wp(fd," (need>=1) unsupported=" as *u8); wn(fd,cu); wp(fd," (need=0)\n" as *u8)
222 if ok==1 { wp(fd,"WRITEHUB-FACTCHECK GREEN (grounds claims in independent banked sources; cites them; refuses the unsupported)\n" as *u8); if fd>0 { sys_close(fd) } sys_exit(0); return 0 }
223 wp(fd,"WRITEHUB-FACTCHECK RED (discriminator failed -- tune terms/threshold/corpus)\n" as *u8); if fd>0 { sys_close(fd) } sys_exit(1); return 1
224}