code wiki / (root) / nx_paper_audit_cli.nx

nx_paper_audit_cli.nx source

↩ module page · 172 lines · 8002 B

1// nx_paper_audit_cli.nx -- callable MCP/API surface for citation resolution (AS-3). 2// 3// nx_paper_audit <manuscript.md> <evidence.tsv> 4// resolves every [ev:ID] against the ledger and checks the adjacent number matches. 5// dangling = the row named does not exist 6// contradicted= the printed number differs from the row it names 7// unanchored = the citation supports prose, not a figure (reported, not an error) 8// 9// license_tier: ORIGINAL No hw writes (Rule 26). expect_exit: 0 (1 if NOT clean) 10import "nx_paper_audit.nx" 11import "nx_syscalls.nx" 12const K_MAGIC_1048576: i64 = 1048576 13const K_MAGIC_1048560: i64 = 1048560 14const K_MAGIC_262144: i64 = 262144 15const K_MAGIC_262128: i64 = 262128 16const K_MAGIC_4096: i64 = 4096 17const K_MAGIC_1024: i64 = 1024 18const K_MAGIC_4000: i64 = 4000 19 20func pac_puts(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 } 21func pac_q() -> i64 { let b: *u8 = sys_mmap(1); b[0] = 34 as u8; sys_write(1, b, 1); return 0 } 22func pac_key(name: *u8) -> i64 { pac_q(); pac_puts(name); pac_q(); pac_puts(":" as *u8); return 0 } 23func pac_num(v: i64) -> i64 { 24 let bb: *u8 = sys_mmap(28); var m: i64 = v 25 if m < 0 { m = 0 - m } 26 let t: *u8 = sys_mmap(28); var k: i64 = 0 27 if m == 0 { t[0] = 48 as u8; k = 1 } 28 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 } 29 var i: i64 = 0 30 while i < k { bb[i] = t[k-1-i]; i = i + 1 } 31 sys_write(1, bb, k); return 0 32} 33func pac_read(path: *u8, buf: *u8, cap: i64) -> i64 { 34 let fd: i64 = sys_openat_rd(path) 35 if fd < 0 { return 0 - 1 } 36 var n: i64 = 0 37 var go: i64 = 1 38 while go == 1 { 39 let base: i64 = buf as i64 40 let r: i64 = sys_read(fd, (base + n) as *u8, cap - n) 41 if r <= 0 { go = 0 } else { n = n + r } 42 if n >= cap { go = 0 } 43 } 44 sys_close(fd) 45 return n 46} 47// does s start with pfx? offset past pfx, or -1. Named optional args stay position-free 48// so adding them cannot disturb existing callers (rule 19). 49func pac_pfx(s: *u8, pfx: *u8) -> i64 { 50 var i: i64 = 0 51 while pfx[i] != (0 as u8) { 52 if s[i] != pfx[i] { return 0 - 1 } 53 i = i + 1 54 } 55 return i 56} 57func main(argc: i64, argv: *i64) -> i64 { 58 if argc < 3 { 59 pac_puts("{" as *u8); pac_key("error" as *u8); pac_q() 60 pac_puts("usage: nx_paper_audit <manuscript.md> <evidence.tsv>" as *u8) 61 pac_q(); pac_puts("}\n" as *u8); sys_exit(2); return 2 62 } 63 let doc: *u8 = sys_mmap(K_MAGIC_1048576) 64 let dn: i64 = pac_read(argv[1] as *u8, doc, K_MAGIC_1048560) 65 if dn < 0 { 66 pac_puts("{" as *u8); pac_key("error" as *u8); pac_q() 67 pac_puts("manuscript unreadable -- refusing to audit (an unreadable paper is not a dishonest one)" as *u8) 68 pac_q(); pac_puts("}\n" as *u8); sys_exit(3); return 3 69 } 70 let led: *u8 = sys_mmap(K_MAGIC_262144) 71 let ln: i64 = pac_read(argv[2] as *u8, led, K_MAGIC_262128) 72 if ln <= 0 { 73 pac_puts("{" as *u8); pac_key("error" as *u8); pac_q() 74 pac_puts("ledger unreadable -- refusing to audit (without a ledger every citation would look dangling)" as *u8) 75 pac_q(); pac_puts("}\n" as *u8); sys_exit(3); return 3 76 } 77 let ledp: *i64 = sys_mmap(2 * 8) as *i64 78 ledp[0] = led as i64 79 ledp[1] = ln 80 let out: *i64 = sys_mmap(8 * 8) as *i64 81 pa_audit(doc, dn, ledp, out) 82 let clean: i64 = pa_clean(out) 83 84 // ---- optional: verify each LEDGER value against the durable log it names --------------- 85 // This is the last link in manuscript -> ledger -> log. Off by default because it reads 86 // arbitrary source paths relative to CWD; on, it is the only thing standing between a 87 // typo'd ledger row and a published number nobody can trace. 88 var dosrc: i64 = 0 89 var ai: i64 = 3 90 while ai < argc { 91 if pac_pfx(argv[ai] as *u8, "sources=1" as *u8) >= 0 { dosrc = 1 } 92 ai = ai + 1 93 } 94 var s_rows: i64 = 0 95 var s_ok: i64 = 0 96 var s_absent: i64 = 0 97 var s_unread: i64 = 0 98 // NAME THE OFFENDERS: "3 absent" is a number; "E23,E24,E25" is a work order. 99 let absids: *u8 = sys_mmap(K_MAGIC_4096) 100 var abo: i64 = 0 101 if dosrc == 1 { 102 let rw: *i64 = sys_mmap(8 * 8) as *i64 103 let sbuf: *u8 = sys_mmap(K_MAGIC_1048576) 104 let spath: *u8 = sys_mmap(K_MAGIC_1024) 105 s_rows = pa_rows(led, ln) 106 var r: i64 = 0 107 while r < s_rows { 108 if pa_row(led, ln, r, rw) == 1 { 109 var k: i64 = 0 110 let sl: i64 = rw[5] - rw[4] 111 while k < sl { spath[k] = led[rw[4]+k]; k = k + 1 } 112 spath[sl] = 0 as u8 113 let sn: i64 = pac_read(spath, sbuf, K_MAGIC_1048560) 114 if sn <= 0 { s_unread = s_unread + 1 } 115 else { 116 let vr: *i64 = sys_mmap(2 * 8) as *i64 117 vr[0] = rw[2]; vr[1] = rw[3] 118 if pa_contains(sbuf, sn, led, vr) == 1 { s_ok = s_ok + 1 } 119 else { 120 s_absent = s_absent + 1 121 if abo > 0 { if abo < K_MAGIC_4000 { absids[abo] = 44 as u8; abo = abo + 1 } } 122 var m: i64 = rw[0] 123 while m < rw[1] { if abo < K_MAGIC_4000 { absids[abo] = led[m]; abo = abo + 1 } m = m + 1 } 124 } 125 } 126 } 127 r = r + 1 128 } 129 } 130 131 // ⚠THE HEADLINE MUST NOT HIDE A RED SUB-CHECK. First run of sources=1 reported 132 // "verdict":"CLEAN" while source_verdict said a ledger row was invented -- the manuscript 133 // matched the ledger perfectly, and the ledger was wrong. That is the same defect class as 134 // a composite masking a zero axis, and as a 200 masking the wrong page. A verdict that 135 // summarises must be at least as pessimistic as anything it summarises. 136 var overall: i64 = clean 137 if dosrc == 1 { 138 if s_absent > 0 { overall = 0 } 139 } 140 pac_puts("{" as *u8) 141 pac_key("tool" as *u8); pac_q(); pac_puts("nx_paper_audit" as *u8); pac_q(); pac_puts("," as *u8) 142 pac_key("manuscript" as *u8); pac_q(); pac_puts(argv[1] as *u8); pac_q(); pac_puts("," as *u8) 143 pac_key("bytes" as *u8); pac_num(dn); pac_puts("," as *u8) 144 pac_key("citations" as *u8); pac_num(out[0]); pac_puts("," as *u8) 145 pac_key("resolved_and_matching" as *u8); pac_num(out[1]); pac_puts("," as *u8) 146 pac_key("dangling" as *u8); pac_num(out[2]); pac_puts("," as *u8) 147 pac_key("contradicted" as *u8); pac_num(out[3]); pac_puts("," as *u8) 148 pac_key("unanchored" as *u8); pac_num(out[4]); pac_puts("," as *u8) 149 pac_key("integrity_permil" as *u8); pac_num(pa_integrity(out)); pac_puts("," as *u8) 150 pac_key("clean" as *u8); pac_num(clean); pac_puts("," as *u8) 151 if dosrc == 1 { 152 pac_key("ledger_rows" as *u8); pac_num(s_rows); pac_puts("," as *u8) 153 pac_key("source_confirmed" as *u8); pac_num(s_ok); pac_puts("," as *u8) 154 pac_key("source_absent" as *u8); pac_num(s_absent); pac_puts("," as *u8) 155 pac_key("source_unreadable" as *u8); pac_num(s_unread); pac_puts("," as *u8) 156 absids[abo] = 0 as u8 157 pac_key("source_absent_ids" as *u8); pac_q(); pac_puts(absids); pac_q(); pac_puts("," as *u8) 158 pac_key("source_verdict" as *u8); pac_q() 159 if s_absent > 0 { pac_puts("LEDGER-VALUE-NOT-IN-SOURCE" as *u8) } 160 else { if s_unread > 0 { pac_puts("SOURCE-UNREADABLE" as *u8) } else { pac_puts("TRACED" as *u8) } } 161 pac_q(); pac_puts("," as *u8) 162 } 163 pac_key("verdict" as *u8); pac_q() 164 if overall == 1 { pac_puts("CLEAN" as *u8) } else { pac_puts("DEFECTIVE" as *u8) } 165 pac_q(); pac_puts("," as *u8) 166 pac_key("method" as *u8); pac_q() 167 pac_puts("resolves each [ev:ID] against the ledger and compares the adjacent number; a counter cannot see a dangling or contradicted citation" as *u8) 168 pac_q(); pac_puts("}\n" as *u8) 169 if overall == 1 { sys_exit(0); return 0 } 170 sys_exit(1) 171 return 1 172}