code wiki / _hdl_build / nx_lostset_gate.nx
nx_lostset_gate.nx source
↩ module page · 208 lines · 17368 B
1// nx_lostset_gate.nx -- THE REFEREE for nx_registrymine `lostset` (DM3, devmgmt rung).
2// END-TO-END: it forks the DEPLOYED subject elf, so what it grades is the artifact callers actually
3// run, not a re-compiled copy of the source. Pass the subject path as argv[1] to grade a candidate.
4//
5// WHY EACH TOOTH EXISTS -- every one of these is a way this miner could report a confident wrong
6// answer, and three of them are defects that were REAL in the input before the miner was written:
7// * the harness ships `deferred_tools_delta` attachments that LIST tool names as structural JSON,
8// so a bare-prefix scan counts listed-but-never-called tools as execution evidence;
9// * prose in a tool result can DISCUSS mcpMeta, so a naive needle counts a memory note as a fork;
10// * a resumed session replays earlier records, so a per-file count double-counts one call.
11// A tooth that only ever sees correct data has not been shown to fire, so the fixture carries the
12// bad case and the good case AT ONCE and the gate asserts they are told apart -- two runs that each
13// isolate one signal do not prove discrimination between them.
14//
15// FIXTURES ARE ASSEMBLED AT RUNTIME under /tmp/nx_lostset_gate/ and never in a production tree: a
16// gate that shares its scratch with a live path reports on the FIXTURE, not on the code (measured
17// estate-wide, the gate_fixture ratchet). Idempotent: every fixture file is opened O_TRUNC, so the
18// second run grades the same bytes as the first.
19// license_tier: ORIGINAL Read-only outside /tmp. No hw writes (Rule 26).
20import "nx_syscalls.nx"
21import "nx_sovjson_lib.nx"
22import "nx_gate_verdict.nx"
23import "nx_tool_run.nx"
24
25const LG_MODE_644: i64 = 420 // rw-r--r--
26const LG_MODE_755: i64 = 493 // rwxr-xr-x for the scratch dirs
27const LG_PATH: i64 = 512
28const LG_FIX: i64 = 65536 // one fixture transcript
29const LG_OUT: i64 = 1048576 // subject stdout capture
30const LG_EXIT_SKIP: i64 = 3 // "I could not look" -- never an acquittal
31const LG_RC_REFUSE: i64 = 3 // the subject's own refusal exit code
32
33func lg_wr(path: *u8, buf: *u8, n: i64) -> i64 {
34 let fd: i64 = sys_openat_wr(path, LG_MODE_644)
35 if fd < 0 { return 0 - 1 }
36 sys_write(fd, buf, n)
37 sys_close(fd)
38 return n
39}
40func lg_run(subj: *u8, a1: *u8, a2: *u8, a3: *u8, a4: *u8, out: *u8, cl: *i64) -> i64 {
41 let av: *i64 = sys_mmap(64) as *i64
42 av[0] = subj as i64
43 av[1] = a1 as i64
44 av[2] = a2 as i64
45 av[3] = a3 as i64
46 av[4] = a4 as i64
47 av[5] = 0
48 return tr_run_capture(subj, av, out, LG_OUT - 8, cl)
49}
50func lg_has(out: *u8, n: i64, lit: *u8) -> i64 { return tr_contains(out, n, lit) }
51
52func main(argc: i64, argv: *i64) -> i64 {
53 let ctr: *i64 = gv_ctr()
54 gv_head("nx_lostset_gate -- exec-evidence IS the population, the registry is only the test" as *u8)
55
56 // ---------- subject resolution: an unresolvable subject SKIPS, it never acquits ----------
57 let subj: *u8 = sys_mmap(LG_PATH)
58 var have: i64 = 0
59 if argc >= 2 { var z: i64 = sj_cat(subj, 0, argv[1] as *u8); subj[z] = 0 as u8; have = 1 }
60 if have == 0 { if sj_allow_path("nx_registrymine" as *u8, subj) == 1 { have = 1 } }
61 if have == 0 { var z2: i64 = sj_cat(subj, 0, "./nx_registrymine.elf" as *u8); subj[z2] = 0 as u8; have = 1 }
62
63 // ---------- SETUP: /tmp only, created here because a teardown does not run after a crash ----------
64 let root: *u8 = "/tmp/nx_lostset_gate" as *u8
65 let tdir: *u8 = "/tmp/nx_lostset_gate/t" as *u8
66 let sdir: *u8 = "/tmp/nx_lostset_gate/t/sub" as *u8
67 let fa: *u8 = "/tmp/nx_lostset_gate/t/a.jsonl" as *u8
68 let fb: *u8 = "/tmp/nx_lostset_gate/t/b.jsonl" as *u8
69 let fc: *u8 = "/tmp/nx_lostset_gate/t/sub/c.jsonl" as *u8
70 let reg: *u8 = "/tmp/nx_lostset_gate/reg.conf" as *u8
71 let noreg: *u8 = "/tmp/nx_lostset_gate/absent-registry.conf" as *u8
72 let noroot: *u8 = "/tmp/nx_lostset_gate/absent-root" as *u8
73 let since: *u8 = "2026-08-10T00:00:00Z" as *u8
74 sys_mkdir(root, LG_MODE_755)
75 sys_mkdir(tdir, LG_MODE_755)
76 sys_mkdir(sdir, LG_MODE_755)
77
78 let b: *u8 = sys_mmap(LG_FIX)
79 var o: i64 = 0
80 // -- a.jsonl: every discriminating case in ONE file, so the gate proves they are TOLD APART --
81 // t_lost exec-proven, no row -> LOST
82 // t_ok exec-proven, GREEN row -> OK-ROW (positive control)
83 // t_amber exec-proven, row present but not GREEN -> ROW-NOT-GREEN (its own state)
84 // t_den refusal only -> DENIED-ONLY (cap coverage, not loss)
85 // t_unk unrecognised result only -> UNKNOWN-ONLY (abstain, not acquit)
86 // t_prose result whose TEXT quotes the exec anchor -> UNKNOWN-ONLY (escaped != structural)
87 // t_listed named only in a deferred_tools_delta -> ABSENT entirely
88 // t_old exec-proven but BEFORE the window -> ABSENT entirely
89 // t_dup exec-proven, same id also in b.jsonl -> counted ONCE
90 // Read a non-nishi tool with exec meta -> ABSENT entirely
91 o = sj_cat(b, o, "{\"type\":\"assistant\",\"timestamp\":\"2026-08-15T10:00:00.000Z\",\"message\":{\"role\":\"assistant\",\"content\":[{\"type\":\"tool_use\",\"id\":\"toolu_LOST1\",\"name\":\"mcp__nishi__t_lost\",\"input\":{}},{\"type\":\"tool_use\",\"id\":\"toolu_OK1\",\"name\":\"mcp__nishi__t_ok\",\"input\":{}},{\"type\":\"tool_use\",\"id\":\"toolu_AMB1\",\"name\":\"mcp__nishi__t_amber\",\"input\":{}}]}}\n" as *u8)
92 o = sj_cat(b, o, "{\"type\":\"user\",\"message\":{\"role\":\"user\",\"content\":[{\"tool_use_id\":\"toolu_LOST1\",\"type\":\"tool_result\",\"content\":[{\"type\":\"text\",\"text\":\"ran\"}]}]},\"timestamp\":\"2026-08-15T10:00:01.000Z\",\"mcpMeta\":{\"_meta\":{\"exit_code\":0,\"bytes\":3}}}\n" as *u8)
93 o = sj_cat(b, o, "{\"type\":\"user\",\"message\":{\"role\":\"user\",\"content\":[{\"tool_use_id\":\"toolu_OK1\",\"type\":\"tool_result\",\"content\":[{\"type\":\"text\",\"text\":\"ran\"}]}]},\"timestamp\":\"2026-08-15T10:00:02.000Z\",\"mcpMeta\":{\"_meta\":{\"exit_code\":0,\"bytes\":3}}}\n" as *u8)
94 o = sj_cat(b, o, "{\"type\":\"user\",\"message\":{\"role\":\"user\",\"content\":[{\"tool_use_id\":\"toolu_AMB1\",\"type\":\"tool_result\",\"content\":[{\"type\":\"text\",\"text\":\"ran\"}]}]},\"timestamp\":\"2026-08-15T10:00:03.000Z\",\"mcpMeta\":{\"_meta\":{\"exit_code\":0,\"bytes\":3}}}\n" as *u8)
95 o = sj_cat(b, o, "{\"type\":\"assistant\",\"timestamp\":\"2026-08-15T10:01:00.000Z\",\"message\":{\"role\":\"assistant\",\"content\":[{\"type\":\"tool_use\",\"id\":\"toolu_DEN1\",\"name\":\"mcp__nishi__t_den\",\"input\":{}},{\"type\":\"tool_use\",\"id\":\"toolu_UNK1\",\"name\":\"mcp__nishi__t_unk\",\"input\":{}},{\"type\":\"tool_use\",\"id\":\"toolu_PRO1\",\"name\":\"mcp__nishi__t_prose\",\"input\":{}}]}}\n" as *u8)
96 o = sj_cat(b, o, "{\"type\":\"user\",\"message\":{\"role\":\"user\",\"content\":[{\"tool_use_id\":\"toolu_DEN1\",\"type\":\"tool_result\",\"content\":[{\"type\":\"text\",\"text\":\"capability denied for tool\"}]}]},\"timestamp\":\"2026-08-15T10:01:01.000Z\"}\n" as *u8)
97 o = sj_cat(b, o, "{\"type\":\"user\",\"message\":{\"role\":\"user\",\"content\":[{\"tool_use_id\":\"toolu_UNK1\",\"type\":\"tool_result\",\"content\":[{\"type\":\"text\",\"text\":\"the edge window closed early\"}]}]},\"timestamp\":\"2026-08-15T10:01:02.000Z\"}\n" as *u8)
98 // the escaped anchor lives INSIDE a JSON string value here, exactly as a memory note quoting it
99 // would. Structural quotes are unescaped; string quotes are not. That single fact is the whole
100 // defence, so it gets its own tooth rather than a comment.
101 o = sj_cat(b, o, "{\"type\":\"user\",\"message\":{\"role\":\"user\",\"content\":[{\"tool_use_id\":\"toolu_PRO1\",\"type\":\"tool_result\",\"content\":[{\"type\":\"text\",\"text\":\"note: exec proof is \\\"mcpMeta\\\":{\\\"_meta\\\":{\\\"exit_code\\\":0}} in the record\"}]}]},\"timestamp\":\"2026-08-15T10:01:03.000Z\"}\n" as *u8)
102 o = sj_cat(b, o, "{\"type\":\"user\",\"isSidechain\":false,\"attachment\":{\"type\":\"deferred_tools_delta\",\"addedNames\":[\"mcp__nishi__t_listed\",\"mcp__nishi__t_lost\"]},\"timestamp\":\"2026-08-15T10:02:00.000Z\"}\n" as *u8)
103 o = sj_cat(b, o, "{\"type\":\"assistant\",\"timestamp\":\"2026-08-01T09:00:00.000Z\",\"message\":{\"role\":\"assistant\",\"content\":[{\"type\":\"tool_use\",\"id\":\"toolu_OLD1\",\"name\":\"mcp__nishi__t_old\",\"input\":{}}]}}\n" as *u8)
104 o = sj_cat(b, o, "{\"type\":\"user\",\"message\":{\"role\":\"user\",\"content\":[{\"tool_use_id\":\"toolu_OLD1\",\"type\":\"tool_result\",\"content\":[{\"type\":\"text\",\"text\":\"ran\"}]}]},\"timestamp\":\"2026-08-01T09:00:01.000Z\",\"mcpMeta\":{\"_meta\":{\"exit_code\":0,\"bytes\":3}}}\n" as *u8)
105 o = sj_cat(b, o, "{\"type\":\"assistant\",\"timestamp\":\"2026-08-15T10:03:00.000Z\",\"message\":{\"role\":\"assistant\",\"content\":[{\"type\":\"tool_use\",\"id\":\"toolu_DUP1\",\"name\":\"mcp__nishi__t_dup\",\"input\":{}},{\"type\":\"tool_use\",\"id\":\"toolu_RD1\",\"name\":\"Read\",\"input\":{}}]}}\n" as *u8)
106 o = sj_cat(b, o, "{\"type\":\"user\",\"message\":{\"role\":\"user\",\"content\":[{\"tool_use_id\":\"toolu_DUP1\",\"type\":\"tool_result\",\"content\":[{\"type\":\"text\",\"text\":\"ran\"}]}]},\"timestamp\":\"2026-08-15T10:03:01.000Z\",\"mcpMeta\":{\"_meta\":{\"exit_code\":0,\"bytes\":3}}}\n" as *u8)
107 o = sj_cat(b, o, "{\"type\":\"user\",\"message\":{\"role\":\"user\",\"content\":[{\"tool_use_id\":\"toolu_RD1\",\"type\":\"tool_result\",\"content\":[{\"type\":\"text\",\"text\":\"ran\"}]}]},\"timestamp\":\"2026-08-15T10:03:02.000Z\",\"mcpMeta\":{\"_meta\":{\"exit_code\":0,\"bytes\":3}}}\n" as *u8)
108 let wa: i64 = lg_wr(fa, b, o)
109 // -- b.jsonl: the SAME tool_use id replayed, as a resumed session does --
110 o = 0
111 o = sj_cat(b, o, "{\"type\":\"assistant\",\"timestamp\":\"2026-08-15T10:03:00.000Z\",\"message\":{\"role\":\"assistant\",\"content\":[{\"type\":\"tool_use\",\"id\":\"toolu_DUP1\",\"name\":\"mcp__nishi__t_dup\",\"input\":{}}]}}\n" as *u8)
112 o = sj_cat(b, o, "{\"type\":\"user\",\"message\":{\"role\":\"user\",\"content\":[{\"tool_use_id\":\"toolu_DUP1\",\"type\":\"tool_result\",\"content\":[{\"type\":\"text\",\"text\":\"ran\"}]}]},\"timestamp\":\"2026-08-15T10:03:01.000Z\",\"mcpMeta\":{\"_meta\":{\"exit_code\":0,\"bytes\":3}}}\n" as *u8)
113 let wb: i64 = lg_wr(fb, b, o)
114 // -- sub/c.jsonl: reachable ONLY by recursion --
115 o = 0
116 o = sj_cat(b, o, "{\"type\":\"assistant\",\"timestamp\":\"2026-08-16T11:00:00.000Z\",\"message\":{\"role\":\"assistant\",\"content\":[{\"type\":\"tool_use\",\"id\":\"toolu_SUB1\",\"name\":\"mcp__nishi__t_sub\",\"input\":{}}]}}\n" as *u8)
117 o = sj_cat(b, o, "{\"type\":\"user\",\"message\":{\"role\":\"user\",\"content\":[{\"tool_use_id\":\"toolu_SUB1\",\"type\":\"tool_result\",\"content\":[{\"type\":\"text\",\"text\":\"ran\"}]}]},\"timestamp\":\"2026-08-16T11:00:01.000Z\",\"mcpMeta\":{\"_meta\":{\"exit_code\":0,\"bytes\":3}}}\n" as *u8)
118 let wc: i64 = lg_wr(fc, b, o)
119 // -- registry: GREEN row, a NOT-GREEN row, a row for a denied-only tool, a row nobody called --
120 o = 0
121 o = sj_cat(b, o, "t_ok\t/tmp/nx_lostset_gate/t_ok.elf\tGREEN\n" as *u8)
122 o = sj_cat(b, o, "t_amber\t/tmp/nx_lostset_gate/t_amber.elf\tAMBER\n" as *u8)
123 o = sj_cat(b, o, "t_den\t/tmp/nx_lostset_gate/t_den.elf\tGREEN\n" as *u8)
124 o = sj_cat(b, o, "t_listed\t/tmp/nx_lostset_gate/t_listed.elf\tGREEN\n" as *u8)
125 let wr: i64 = lg_wr(reg, b, o)
126 sys_unlinkat(noreg)
127
128 var fixok: i64 = 0
129 if wa > 0 { if wb > 0 { if wc > 0 { if wr > 0 { fixok = 1 } } } }
130 gv_check("fixture-written-all-four (assert the fixture reached the condition before the outcome)" as *u8, fixok, ctr)
131
132 let out: *u8 = sys_mmap(LG_OUT)
133 let cl: *i64 = sys_mmap(16) as *i64
134 let rc: i64 = lg_run(subj, "lostset" as *u8, tdir, reg, since, out, cl)
135 let n: i64 = cl[0]
136
137 // an unrunnable subject is UNOBSERVABLE, not a failing subject
138 if n <= 0 {
139 gv_puts("SKIP subject produced no output -- build/deploy nx_registrymine, then re-run: " as *u8)
140 gv_puts(subj)
141 gv_puts("\n" as *u8)
142 sys_exit(LG_EXIT_SKIP)
143 return LG_EXIT_SKIP
144 }
145 gv_check("subject-ran-and-exited-zero-on-a-valid-population" as *u8, 1 - rc, ctr)
146 gv_check("subject-examined-a-nonzero-population (a zero-subject run cannot acquit a registry)" as *u8, lg_has(out, n, "files_scanned=3" as *u8), ctr)
147 gv_check("recursive-walk-reached-the-subdirectory-transcript" as *u8, lg_has(out, n, "LOST t_sub exec=1" as *u8), ctr)
148
149 // ---- the anti-vacuity tooth first: the trivial wrong implementation cannot pass it ----
150 gv_check("anti-vacuity-exec-proven-tool-with-no-row-is-named-LOST" as *u8, lg_has(out, n, "LOST t_lost exec=1" as *u8), ctr)
151 gv_check("lost-row-carries-its-remedy-not-just-a-count" as *u8, lg_has(out, n, "REMEDY /api/tools/register" as *u8), ctr)
152 // ---- and the positive control beside it: a detector that fires on everything fails HERE ----
153 gv_check("pos-control-exec-proven-tool-WITH-a-green-row-is-OK-ROW" as *u8, lg_has(out, n, "OK-ROW t_ok exec=1" as *u8), ctr)
154 var notlost_ok: i64 = 1
155 if lg_has(out, n, "LOST t_ok " as *u8) == 1 { notlost_ok = 0 }
156 gv_check("pos-control-the-registered-tool-is-NOT-also-reported-lost" as *u8, notlost_ok, ctr)
157
158 // ---- states that must not collapse into LOST ----
159 gv_check("row-present-but-not-GREEN-is-its-own-state-ROW-NOT-GREEN" as *u8, lg_has(out, n, "ROW-NOT-GREEN t_amber exec=1" as *u8), ctr)
160 gv_check("denied-only-tool-is-its-own-bucket-cap-coverage-not-loss" as *u8, lg_has(out, n, "DENIED-ONLY t_den denied=1" as *u8), ctr)
161 gv_check("unknown-only-tool-abstains-rather-than-acquitting" as *u8, lg_has(out, n, "UNKNOWN-ONLY t_unk unknown=1" as *u8), ctr)
162
163 // ---- the three real input defects, each with a named negative control ----
164 var l_ok: i64 = 1
165 if lg_has(out, n, "t_listed" as *u8) == 1 { l_ok = 0 }
166 gv_check("neg-control-a-tool-merely-LISTED-in-a-deferred-tools-delta-is-not-evidence" as *u8, l_ok, ctr)
167 gv_check("neg-control-an-ESCAPED-exec-anchor-inside-a-string-is-not-a-fork" as *u8, lg_has(out, n, "UNKNOWN-ONLY t_prose unknown=1" as *u8), ctr)
168 var p_ok: i64 = 1
169 if lg_has(out, n, "OK-ROW t_prose" as *u8) == 1 { p_ok = 0 }
170 if lg_has(out, n, "LOST t_prose" as *u8) == 1 { p_ok = 0 }
171 gv_check("neg-control-the-prose-tool-never-enters-the-exec-partition" as *u8, p_ok, ctr)
172 gv_check("dedupe-the-same-tool-use-id-in-two-files-is-counted-once" as *u8, lg_has(out, n, "results_duplicate=1" as *u8), ctr)
173 gv_check("dedupe-leaves-the-replayed-tool-at-exec-1-not-2" as *u8, lg_has(out, n, "LOST t_dup exec=1" as *u8), ctr)
174
175 // ---- window, non-nishi, and the printed partition ----
176 gv_check("since-window-excludes-an-older-call-and-COUNTS-the-exclusion" as *u8, lg_has(out, n, "results_out_of_window=1" as *u8), ctr)
177 var o_ok: i64 = 1
178 if lg_has(out, n, "t_old" as *u8) == 1 { o_ok = 0 }
179 gv_check("neg-control-the-out-of-window-tool-appears-in-no-bucket" as *u8, o_ok, ctr)
180 var r_ok: i64 = 1
181 if lg_has(out, n, "OK-ROW Read" as *u8) == 1 { r_ok = 0 }
182 if lg_has(out, n, "LOST Read" as *u8) == 1 { r_ok = 0 }
183 gv_check("neg-control-a-non-nishi-tool-with-exec-meta-is-not-in-the-partition" as *u8, r_ok, ctr)
184 gv_check("partition-is-exactly-5-tools-1-green-1-not-green-3-lost" as *u8, lg_has(out, n, "exec_tools=5 row_green=1 row_notgreen=1 lost=3" as *u8), ctr)
185 gv_check("partition-prints-its-own-sum-and-it-reconciles" as *u8, lg_has(out, n, "sum=5 sum_ok=1" as *u8), ctr)
186 gv_check("per-call-classes-also-reconcile-against-results-counted" as *u8, lg_has(out, n, "calls_sum_ok=1" as *u8), ctr)
187 gv_check("caps-are-declared-in-every-report-so-a-floor-cannot-read-as-a-total" as *u8, lg_has(out, n, "any_cap_hit=0 floor_not_total=0" as *u8), ctr)
188
189 // ---- fail-closed: an instrument that cannot see must refuse, never return lost=0 ----
190 let rc2: i64 = lg_run(subj, "lostset" as *u8, tdir, noreg, since, out, cl)
191 var ref1: i64 = 0
192 if rc2 == LG_RC_REFUSE { if lg_has(out, cl[0], "REFUSED registry unreadable" as *u8) == 1 { ref1 = 1 } }
193 gv_check("neg-control-a-missing-registry-REFUSES-instead-of-acquitting" as *u8, ref1, ctr)
194 let rc3: i64 = lg_run(subj, "lostset" as *u8, noroot, reg, since, out, cl)
195 var ref2: i64 = 0
196 if rc3 == LG_RC_REFUSE { if lg_has(out, cl[0], "REFUSED transcripts root unreadable" as *u8) == 1 { ref2 = 1 } }
197 gv_check("neg-control-an-unwalkable-root-REFUSES-rather-than-reporting-zero-lost" as *u8, ref2, ctr)
198
199 // ---- idempotence: the second run of the WHOLE fixture must grade identically ----
200 let rc4: i64 = lg_run(subj, "lostset" as *u8, tdir, reg, since, out, cl)
201 var idem: i64 = 0
202 if rc4 == 0 { if lg_has(out, cl[0], "exec_tools=5 row_green=1 row_notgreen=1 lost=3" as *u8) == 1 { if lg_has(out, cl[0], "files_scanned=3" as *u8) == 1 { idem = 1 } } }
203 gv_check("idempotent-a-second-run-on-the-same-fixture-grades-identically" as *u8, idem, ctr)
204
205 let v: i64 = gv_verdict("LOSTSET-GATE" as *u8, ctr, "the miner joins execution evidence to registry rows, tells the three real look-alike defects apart, and refuses when it cannot see" as *u8)
206 sys_exit(v)
207 return v
208}