code wiki / (root) / nx_paper_audit.nx

nx_paper_audit.nx source

↩ module page · 204 lines · 7519 B

1// nx_paper_audit.nx -- RESOLVE every citation in a manuscript against its ledger (AS-3). 2// 3// ★WHY THIS EXISTS -- A MEASURED BLIND SPOT IN OUR OWN RULER, NOT A SUSPECTED ONE. 4// nx_paperbench COUNTS provenance tokens; it never checks that a token RESOLVES, nor that 5// the number printed next to it MATCHES the ledger row it names. Proven with a fixture 6// (knowledge/papers/rt004_negcontrol_liar.md): a manuscript citing "999 [ev:E02]" when the 7// ledger says 254, and "[ev:E99]" which does not exist at all, scored composite 750 and 8// publishable=1. A well-formed token is indistinguishable from a true one to a counter. 9// 10// This organ closes that hole by a DIFFERENT METHOD: it resolves. For each "[ev:ID]" it 11// 1. looks ID up in the ledger -> absent = DANGLING 12// 2. reads the digit run immediately before -> differs = CONTRADICTED 13// 3. no adjacent number -> UNANCHORED (reported, not an error: 14// a citation may legitimately support a sentence rather than a figure) 15// 16// ★It audits the ARTIFACT, not the process. nx_paper_forge guarantees correctness at EMIT 17// time; this checks the file as it now stands, so a hand-edited, post-processed, or 18// forge-bugged manuscript is still caught. That independence is the whole point. 19// 20// DRY: ledger lookup is nx_paper_forge's pf_lookup and marker scanning is nx_paperbench's 21// pb_find -- reused, not reimplemented (rule 15). Imports are transitive through the forge. 22// 23// Integer, deterministic. No hw writes (Rule 26). license_tier: ORIGINAL 24// module: nishi-core.research.paper_audit 25// depends: nx_paper_forge.nx (-> nx_paperbench.nx -> nx_syscalls.nx) 26// genealogy_id: paperbench_token_count_blindspot + forge_ledger_lookup 27import "nx_paper_forge.nx" 28 29// The digit run immediately preceding position p, skipping intervening spaces. 30// out[0],out[1] = [start,end). Returns 1 if found, 0 if the token is UNANCHORED. 31func pa_prev_num(buf: *u8, p: i64, out: *i64) -> i64 { 32 var i: i64 = p - 1 33 var s: i64 = 1 34 while s == 1 { 35 if i < 0 { s = 0 } 36 else { if buf[i] == (32 as u8) { i = i - 1 } else { s = 0 } } 37 } 38 if i < 0 { return 0 } 39 let c: i64 = buf[i] as i64 40 if c < 48 { return 0 } 41 if c > 57 { return 0 } 42 let e: i64 = i + 1 43 var s2: i64 = 1 44 while s2 == 1 { 45 if i < 0 { s2 = 0 } 46 else { 47 let d: i64 = buf[i] as i64 48 if d >= 48 { 49 if d <= 57 { i = i - 1 } else { s2 = 0 } 50 } else { s2 = 0 } 51 } 52 } 53 out[0] = i + 1 54 out[1] = e 55 return 1 56} 57 58// byte-equality of a[a0,a1) and b[b0,b1) 59func pa_range_eq(a: *u8, ar: *i64, b: *u8, br: *i64) -> i64 { 60 let la: i64 = ar[1] - ar[0] 61 let lb: i64 = br[1] - br[0] 62 if la != lb { return 0 } 63 var k: i64 = 0 64 var same: i64 = 1 65 while k < la { 66 if a[ar[0]+k] != b[br[0]+k] { same = 0; k = la } else { k = k + 1 } 67 } 68 return same 69} 70 71// THE AUDIT. ledp[0]=ledger buffer as i64, ledp[1]=ledger length. 72// out[0]=tokens out[1]=ok out[2]=dangling out[3]=contradicted out[4]=unanchored 73func pa_audit(doc: *u8, dn: i64, ledp: *i64, out: *i64) -> i64 { 74 let led: *u8 = ledp[0] as *u8 75 let ln: i64 = ledp[1] 76 let idbuf: *u8 = sys_mmap(256) 77 let vr: *i64 = sys_mmap(2 * 8) as *i64 78 let nr: *i64 = sys_mmap(2 * 8) as *i64 79 var t: i64 = 0 80 var ok: i64 = 0 81 var dang: i64 = 0 82 var contra: i64 = 0 83 var unanch: i64 = 0 84 var i: i64 = 0 85 while i + 4 <= dn { 86 var hit: i64 = 0 87 if doc[i] == (91 as u8) { 88 if doc[i+1] == (101 as u8) { 89 if doc[i+2] == (118 as u8) { 90 if doc[i+3] == (58 as u8) { hit = 1 } 91 } 92 } 93 } 94 if hit == 1 { 95 t = t + 1 96 var j: i64 = i + 4 97 var idn: i64 = 0 98 var s: i64 = 1 99 while s == 1 { 100 if j >= dn { s = 0 } 101 else { 102 if doc[j] == (93 as u8) { s = 0 } 103 else { if idn < 250 { idbuf[idn] = doc[j]; idn = idn + 1 } j = j + 1 } 104 } 105 } 106 idbuf[idn] = 0 as u8 107 if pf_lookup(led, ln, idbuf, idn, vr) == 0 { 108 dang = dang + 1 109 } else { 110 if pa_prev_num(doc, i, nr) == 0 { 111 unanch = unanch + 1 112 } else { 113 if pa_range_eq(doc, nr, led, vr) == 1 { ok = ok + 1 } else { contra = contra + 1 } 114 } 115 } 116 i = j + 1 117 } else { i = i + 1 } 118 } 119 out[0] = t; out[1] = ok; out[2] = dang; out[3] = contra; out[4] = unanch 120 return 0 121} 122 123// ---------------------------------------------- ledger <-> SOURCE (the last unverified link) 124// 125// ★THE HOLE THIS CLOSES. The chain is manuscript -> ledger -> durable log. pa_audit proves 126// manuscript<->ledger. NOTHING proved ledger<->log: a mistyped or invented ledger value would 127// pass every gate in this lane, because every check downstream trusts the ledger as ground 128// truth. A provenance chain is only as strong as its weakest unchecked link, and this was it. 129// (Adjacent but distinct: nx_research_verify checks claims by FETCHING a remote URL; this 130// checks a measurement value against the local log the ledger itself names.) 131// 132// number of rows in the ledger 133func pa_rows(led: *u8, ln: i64) -> i64 { 134 var i: i64 = 0 135 var r: i64 = 0 136 while i < ln { 137 let le: i64 = pf_eol(led, ln, i) 138 if le > i { r = r + 1 } 139 i = le + 1 140 } 141 return r 142} 143 144// parse ledger row `idx` into out: [0,1]=id range, [2,3]=value range, [4,5]=source range. 145// Returns 1 on success, 0 if idx is past the end. 146func pa_row(led: *u8, ln: i64, idx: i64, out: *i64) -> i64 { 147 var i: i64 = 0 148 var r: i64 = 0 149 while i < ln { 150 let le: i64 = pf_eol(led, ln, i) 151 if le > i { 152 if r == idx { 153 var f: i64 = 0 154 var p: i64 = i 155 while f < 3 { 156 var q: i64 = p 157 var s: i64 = 1 158 while s == 1 { 159 if q >= le { s = 0 } else { if led[q] == (9 as u8) { s = 0 } else { q = q + 1 } } 160 } 161 out[f*2] = p 162 out[f*2+1] = q 163 p = q + 1 164 f = f + 1 165 } 166 return 1 167 } 168 r = r + 1 169 } 170 i = le + 1 171 } 172 return 0 173} 174 175// is the byte range ndl[nr[0]..nr[1]) present anywhere in hay[0..hn)? 176func pa_contains(hay: *u8, hn: i64, ndl: *u8, nr: *i64) -> i64 { 177 let l: i64 = nr[1] - nr[0] 178 if l <= 0 { return 0 } 179 var i: i64 = 0 180 while i + l <= hn { 181 var k: i64 = 0 182 var hit: i64 = 1 183 while k < l { if hay[i+k] != ndl[nr[0]+k] { hit = 0; k = l } else { k = k + 1 } } 184 if hit == 1 { return 1 } 185 i = i + 1 186 } 187 return 0 188} 189 190// integrity in permille = anchored citations that RESOLVE AND MATCH, over all citations. 191// A manuscript with no citations scores 0: unverifiable is not the same as verified, and 192// the ruler must never reward the absence of evidence. 193func pa_integrity(out: *i64) -> i64 { 194 if out[0] <= 0 { return 0 } 195 return (out[1] * 1000) / out[0] 196} 197 198// a manuscript is CLEAN only if nothing dangles and nothing contradicts the ledger. 199func pa_clean(out: *i64) -> i64 { 200 if out[2] > 0 { return 0 } 201 if out[3] > 0 { return 0 } 202 if out[0] <= 0 { return 0 } 203 return 1 204}