code wiki / (root) / nx_writehub_factcheck.nx

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}