nx_paperbench_cli.nx source
↩ module page · 207 lines · 10599 B
1// nx_paperbench_cli.nx -- callable MCP/API surface for the sovereign manuscript grader (AS-0).
2//
3// nx_paperbench score <slack> <bar> <axes_csv> [weights_csv] [mask_csv]
4// axes = 7 permille scores in canonical order:
5// claim_evidence,reproducibility,controls,limitations,baseline,data_provenance,stat_honesty
6// e.g. nx_paperbench score 250 600 1000,1000,200,1000,1000,1000,1000 -> capped 450
7//
8// nx_paperbench scan <path> <slack> <bar>
9// reads a REAL manuscript off disk and derives the axes mechanically:
10// claim_evidence = provenance tokens "[ev:" per numeric claim (digit run)
11// the other six = presence of the canonical section heading
12// HONEST SCOPE: section-presence is a REPORTING signal, not a quality signal.
13// REFORMS (arXiv:2308.07832) is itself a *reporting* standard, so this is the
14// right tier -- but a present-and-empty section scores the same as a good one.
15// Judging section CONTENT is the referee stage (AS-2), not the ruler.
16//
17// license_tier: ORIGINAL No hw writes (Rule 26). expect_exit: 0
18import "nx_paperbench.nx"
19import "nx_itoa_lib.nx" // shared MSB-first emitter (zero-alloc)
20import "nx_syscalls.nx"
21const K_MAGIC_1048576: i64 = 1048576
22const K_MAGIC_1048560: i64 = 1048560
23
24func pc_puts(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 }
25func pc_q() -> i64 { let b: *u8 = sys_mmap(1); b[0] = 34 as u8; sys_write(1, b, 1); return 0 }
26func pc_key(name: *u8) -> i64 { pc_q(); pc_puts(name); pc_q(); pc_puts(":" as *u8); return 0 }
27// MIGRATED to the shared emitter (debt 1785563586). The old body mmapped a scratch buffer
28// per call and never freed it. At PAGE granularity that is 4096B leaked PER CALL -- the
29// defect that took 28.5GB of a 36GB host in nx_ts_lumadiff (2MB input, ~3.66M calls).
30// nxi_* is MSB-first, allocates NOTHING, and emits identical bytes including the sign.
31func pc_num(v: i64) -> i64 { nxi_out(v); return 0 }
32func pc_strlen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n }
33func pc_atoi(a: *u8) -> i64 {
34 var v: i64 = 0; var i: i64 = 0
35 while a[i] != (0 as u8) {
36 let ch: i64 = a[i] as i64
37 if ch >= 48 { if ch <= 57 { v = v * 10 + (ch - 48) } }
38 i = i + 1
39 }
40 return v
41}
42func pc_parse_csv(s: *u8, out: *i64, maxn: i64) -> i64 {
43 var cnt: i64 = 0; var cur: i64 = 0; var have: i64 = 0; var i: i64 = 0
44 let n: i64 = pc_strlen(s)
45 while i < n {
46 let c: i64 = s[i] as i64
47 if c >= 48 { if c <= 57 { cur = cur * 10 + (c - 48); have = 1 } }
48 if c == 44 { if cnt < maxn { out[cnt] = cur; cnt = cnt + 1 } cur = 0; have = 0 }
49 i = i + 1
50 }
51 if have == 1 { if cnt < maxn { out[cnt] = cur; cnt = cnt + 1 } }
52 return cnt
53}
54func pc_axis_name(i: i64) -> i64 {
55 if i == 0 { pc_puts("claim_evidence" as *u8); return 0 }
56 if i == 1 { pc_puts("reproducibility" as *u8); return 0 }
57 if i == 2 { pc_puts("controls" as *u8); return 0 }
58 if i == 3 { pc_puts("limitations" as *u8); return 0 }
59 if i == 4 { pc_puts("baseline" as *u8); return 0 }
60 if i == 5 { pc_puts("data_provenance" as *u8); return 0 }
61 if i == 6 { pc_puts("stat_honesty" as *u8); return 0 }
62 pc_puts("axis" as *u8); pc_num(i)
63 return 0
64}
65func pc_tier_name(t: i64) -> i64 {
66 if t == 0 { pc_puts("REJECT" as *u8); return 0 }
67 if t == 1 { pc_puts("WEAK" as *u8); return 0 }
68 if t == 2 { pc_puts("BORDERLINE" as *u8); return 0 }
69 if t == 3 { pc_puts("ACCEPT" as *u8); return 0 }
70 pc_puts("STRONG" as *u8)
71 return 0
72}
73func pc_read(path: *u8, buf: *u8, cap: i64) -> i64 {
74 let fd: i64 = sys_openat_rd(path)
75 if fd < 0 { return 0 - 1 }
76 var n: i64 = 0
77 var go: i64 = 1
78 while go == 1 {
79 let base: i64 = buf as i64
80 let r: i64 = sys_read(fd, (base + n) as *u8, cap - n)
81 if r <= 0 { go = 0 } else { n = n + r }
82 if n >= cap { go = 0 }
83 }
84 sys_close(fd)
85 return n
86}
87// one section axis: present -> 1000, absent -> 0
88func pc_sec(buf: *u8, n: i64, pat: *u8) -> i64 {
89 if pb_find(buf, n, pat) > 0 { return 1000 }
90 return 0
91}
92func pc_emit(axes: *i64, w: *i64, mk: *i64, slack: i64, bar: i64) -> i64 {
93 let comp: i64 = pb_composite(axes, w, mk, 7, slack)
94 let bars: *i64 = sys_mmap(4 * 8) as *i64
95 bars[0] = 300; bars[1] = 600; bars[2] = 850; bars[3] = 950
96 let tier: i64 = pb_tier(comp, bars, 4)
97 let weak: i64 = pb_weakest(axes, 7)
98 pc_key("axes" as *u8); pc_puts("{" as *u8)
99 var i: i64 = 0
100 while i < 7 {
101 if i > 0 { pc_puts("," as *u8) }
102 pc_q(); pc_axis_name(i); pc_q(); pc_puts(":" as *u8); pc_num(axes[i])
103 i = i + 1
104 }
105 pc_puts("}," as *u8)
106 pc_key("weighted_mean" as *u8); pc_num(pb_weighted(axes, w, 7)); pc_puts("," as *u8)
107 pc_key("integrity_floor" as *u8); pc_num(pb_min_masked(axes, mk, 7)); pc_puts("," as *u8)
108 pc_key("slack" as *u8); pc_num(slack); pc_puts("," as *u8)
109 pc_key("composite" as *u8); pc_num(comp); pc_puts("," as *u8)
110 pc_key("tier" as *u8); pc_num(tier); pc_puts("," as *u8)
111 pc_key("tier_name" as *u8); pc_q(); pc_tier_name(tier); pc_q(); pc_puts("," as *u8)
112 pc_key("bars_source" as *u8); pc_q(); pc_puts("default-300-600-850-950" as *u8); pc_q(); pc_puts("," as *u8)
113 pc_key("weakest_axis" as *u8); pc_q(); pc_axis_name(weak); pc_q(); pc_puts("," as *u8)
114 pc_key("bar" as *u8); pc_num(bar); pc_puts("," as *u8)
115 pc_key("publishable" as *u8); pc_num(pb_publishable(axes, w, mk, 7, slack, bar)); pc_puts("," as *u8)
116 pc_key("voided" as *u8)
117 if comp == 0 { pc_num(1) } else { pc_num(0) }
118 return 0
119}
120func main(argc: i64, argv: *i64) -> i64 {
121 if argc < 3 {
122 pc_puts("{" as *u8); pc_key("error" as *u8); pc_q()
123 pc_puts("usage: nx_paperbench score <slack> <bar> <axes_csv> [weights_csv] [mask_csv] | scan <path> <slack> <bar>" as *u8)
124 pc_q(); pc_puts("}\n" as *u8); sys_exit(2); return 2
125 }
126 let verb: *u8 = argv[1] as *u8
127 // canonical weights + integrity mask (claim_evidence, controls, stat_honesty)
128 let w: *i64 = sys_mmap(7 * 8) as *i64
129 let mk: *i64 = sys_mmap(7 * 8) as *i64
130 var i: i64 = 0
131 while i < 7 { w[i] = 1; mk[i] = 0; i = i + 1 }
132 mk[0] = 1; mk[2] = 1; mk[6] = 1
133 let axes: *i64 = sys_mmap(7 * 8) as *i64
134 i = 0
135 while i < 7 { axes[i] = 0; i = i + 1 }
136
137 if pb_is_scan(verb) == 1 {
138 // ---- scan <path> <slack> <bar>
139 if argc < 5 {
140 pc_puts("{" as *u8); pc_key("error" as *u8); pc_q()
141 pc_puts("usage: nx_paperbench scan <path> <slack> <bar>" as *u8)
142 pc_q(); pc_puts("}\n" as *u8); sys_exit(2); return 2
143 }
144 let path: *u8 = argv[2] as *u8
145 let slack: i64 = pc_atoi(argv[3] as *u8)
146 let bar: i64 = pc_atoi(argv[4] as *u8)
147 let buf: *u8 = sys_mmap(K_MAGIC_1048576)
148 let n: i64 = pc_read(path, buf, K_MAGIC_1048560)
149 if n < 0 {
150 // FAIL LOUD: an unreadable manuscript is REPORTED, never scored as a zero paper
151 pc_puts("{" as *u8); pc_key("error" as *u8); pc_q()
152 pc_puts("manuscript unreadable -- refusing to score (an unreadable paper is not a bad paper)" as *u8)
153 pc_q(); pc_puts("," as *u8); pc_key("path" as *u8); pc_q(); pc_puts(path); pc_q()
154 pc_puts("}\n" as *u8); sys_exit(3); return 3
155 }
156 // claims are counted in the BODY only -- the bibliography is dense with
157 // digits that are citations, not assertions.
158 let body: i64 = pb_offset(buf, n, "## References" as *u8)
159 let claims: i64 = pb_claims(buf, body)
160 let cited: i64 = pb_find(buf, body, "[ev:" as *u8)
161 axes[0] = pb_axis(cited, claims)
162 axes[1] = pc_sec(buf, n, "## Reproducibility" as *u8)
163 axes[2] = pc_sec(buf, n, "## Negative Controls" as *u8)
164 axes[3] = pc_sec(buf, n, "## Limitations" as *u8)
165 axes[4] = pc_sec(buf, n, "## Baseline" as *u8)
166 axes[5] = pc_sec(buf, n, "## Data" as *u8)
167 axes[6] = pc_sec(buf, n, "n=" as *u8)
168 pc_puts("{" as *u8)
169 pc_key("tool" as *u8); pc_q(); pc_puts("nx_paperbench" as *u8); pc_q(); pc_puts("," as *u8)
170 pc_key("mode" as *u8); pc_q(); pc_puts("scan" as *u8); pc_q(); pc_puts("," as *u8)
171 pc_key("path" as *u8); pc_q(); pc_puts(path); pc_q(); pc_puts("," as *u8)
172 pc_key("bytes" as *u8); pc_num(n); pc_puts("," as *u8)
173 pc_key("body_bytes_scanned" as *u8); pc_num(body); pc_puts("," as *u8)
174 pc_key("numeric_claims" as *u8); pc_num(claims); pc_puts("," as *u8)
175 pc_key("provenance_tokens" as *u8); pc_num(cited); pc_puts("," as *u8)
176 pc_emit(axes, w, mk, slack, bar)
177 pc_puts("," as *u8); pc_key("scope" as *u8); pc_q()
178 pc_puts("section-presence is a REPORTING signal (REFORMS tier), not a content judgement; AS-2 referee judges content" as *u8)
179 pc_q(); pc_puts("}\n" as *u8)
180 sys_exit(0); return 0
181 }
182 // ---- score <slack> <bar> <axes_csv> [weights_csv] [mask_csv]
183 if argc < 5 {
184 pc_puts("{" as *u8); pc_key("error" as *u8); pc_q()
185 pc_puts("usage: nx_paperbench score <slack> <bar> <axes_csv> [weights_csv] [mask_csv]" as *u8)
186 pc_q(); pc_puts("}\n" as *u8); sys_exit(2); return 2
187 }
188 let slack: i64 = pc_atoi(argv[2] as *u8)
189 let bar: i64 = pc_atoi(argv[3] as *u8)
190 let got: i64 = pc_parse_csv(argv[4] as *u8, axes, 7)
191 if got < 7 {
192 pc_puts("{" as *u8); pc_key("error" as *u8); pc_q()
193 pc_puts("need 7 axis scores in permille (claim_evidence,reproducibility,controls,limitations,baseline,data_provenance,stat_honesty)" as *u8)
194 pc_q(); pc_puts("," as *u8); pc_key("got" as *u8); pc_num(got)
195 pc_puts("}\n" as *u8); sys_exit(1); return 1
196 }
197 if argc >= 6 { pc_parse_csv(argv[5] as *u8, w, 7) }
198 if argc >= 7 { pc_parse_csv(argv[6] as *u8, mk, 7) }
199 pc_puts("{" as *u8)
200 pc_key("tool" as *u8); pc_q(); pc_puts("nx_paperbench" as *u8); pc_q(); pc_puts("," as *u8)
201 pc_key("mode" as *u8); pc_q(); pc_puts("score" as *u8); pc_q(); pc_puts("," as *u8)
202 pc_emit(axes, w, mk, slack, bar)
203 pc_puts("," as *u8); pc_key("grounding" as *u8); pc_q()
204 pc_puts("REFORMS arXiv:2308.07832 + Pineau arXiv:2003.12206; composite VOIDs on a zero integrity axis and caps at floor+slack" as *u8)
205 pc_q(); pc_puts("}\n" as *u8)
206 sys_exit(0); return 0
207}