nx_kinddedupe.nx source
↩ module page · 235 lines · 11109 B
1// nx_kinddedupe.nx -- keep-first prune of AGREEING duplicate data rows in organ_kind.conf (2026-08-29).
2// license_tier: ORIGINAL No hw writes (Rule 26).
3//
4// nx_kinddedupe plan [conf] -> measure only: prints every would-drop row, the partition, verdict
5// nx_kinddedupe apply [conf] -> temp+rename rewrite dropping ONLY later exact same-name-same-kind
6// data rows; comments, blanks, order and first occurrences untouched
7// EXIT: 0 CLEAN-OR-PRUNED | 1 CONFLICT-REFUSED | 2 usage | 3 UNOBSERVABLE (unreadable conf, failed write)
8//
9// WHY A CONFLICT REFUSES INSTEAD OF RESOLVING: the reader is first-match-wins (nx_organkind ok_kind_of
10// returns on the first whole-field name match), so a name carrying two DIFFERENT kinds is decided by
11// LINE ORDER -- nx_organkind_gate neg-control-T20 proves the order IS the answer. A pruner that picked
12// a winner would silently adjudicate a question that needs evidence. It refuses and names both rows.
13//
14// WHY AN ORGAN AND NOT A CLIENT EDIT: the SSOT carries law in its UTF-8 comments; a client-side rewrite
15// republishes a transport RENDERING of those bytes (the transcription hazard, measured 2026-08-28 on the
16// CLAUDE.md recovery). This organ reads the real bytes and DELETES WHOLE LINES ONLY -- it never rewrites
17// a byte it keeps. Referee = nx_organkind_gate before/after: T22 agreeing_dups -> 0 with rows falling by
18// exactly the drop count, CONFLICTING_NAMES stays 0, T23 whole-file, T24 headroom grows by the pruned
19// bytes. The partition (bytes_kept + bytes_dropped == bytes_in) is asserted IN the organ, not beside it.
20import "nx_syscalls.nx"
21
22const KD_EXIT_OK: i64 = 0
23const KD_EXIT_CONFLICT: i64 = 1
24const KD_EXIT_USAGE: i64 = 2
25const KD_EXIT_UNOBS: i64 = 3
26const KD_CONF_DEFAULT: *u8 = "knowledge/status/organ_kind.conf"
27const KD_TMP_SUFFIX: *u8 = ".kdtmp"
28const KD_SLOT: i64 = 8
29const KD_ROW_FIELDS: i64 = 4 // name_off, name_len, kind_off, kind_len per seen row
30const KD_HASH_BYTE: i64 = 35 // the comment marker byte, constructed numerically (lexer law: never in a literal)
31const KD_NL: i64 = 10
32const KD_SP: i64 = 32
33const KD_TAB: i64 = 9
34
35func kd_w(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 }
36func kd_wb(b: *u8, n: i64) -> i64 { sys_write(1, b, n); return 0 }
37func kd_wn(v: i64) -> i64 {
38 var m: i64 = v
39 if m < 0 { kd_w("-" as *u8); m = 0 - m }
40 let t: *u8 = sys_mmap(32)
41 var k: i64 = 0
42 if m == 0 { t[0] = 48 as u8; k = 1 }
43 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 }
44 let o: *u8 = sys_mmap(32)
45 var i: i64 = 0
46 while i < k { o[i] = t[k - 1 - i]; i = i + 1 }
47 sys_write(1, o, k)
48 return 0
49}
50func kd_streq(a: *u8, b: *u8) -> i64 { var i: i64 = 0; while a[i] != (0 as u8) { if a[i] != b[i] { return 0 } i = i + 1 } if b[i] != (0 as u8) { return 0 } return 1 }
51func kd_memeq(a: *u8, ao: i64, b: *u8, bo: i64, n: i64) -> i64 {
52 var i: i64 = 0
53 while i < n { if a[ao + i] != b[bo + i] { return 0 } i = i + 1 }
54 return 1
55}
56func kd_cat(dst: *u8, at: i64, s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { dst[at + n] = s[n]; n = n + 1 } dst[at + n] = 0 as u8; return at + n }
57func kd_wseg(buf: *u8, off: i64, n: i64) -> i64 {
58 if n <= 0 { return 0 }
59 let t: *u8 = sys_mmap(n)
60 var i: i64 = 0
61 while i < n { t[i] = buf[off + i]; i = i + 1 }
62 sys_write(1, t, n)
63 return 0
64}
65
66// scan one line [i, len): returns end index e (exclusive of the newline). caller advances past the newline.
67func kd_line_end(buf: *u8, i: i64, len: i64) -> i64 {
68 var e: i64 = i
69 var go: i64 = 1
70 while go == 1 {
71 if e >= len { go = 0 } else { if buf[e] == (KD_NL as u8) { go = 0 } else { e = e + 1 } }
72 }
73 return e
74}
75func kd_is_ws(c: u8) -> i64 { if c == (KD_SP as u8) { return 1 } if c == (KD_TAB as u8) { return 1 } return 0 }
76
77func main(argc: i64, argv: *i64) -> i64 {
78 if argc < 2 {
79 kd_w("usage: nx_kinddedupe plan|apply [conf]\n" as *u8)
80 return KD_EXIT_USAGE
81 }
82 var apply: i64 = 0 - 1
83 if kd_streq(argv[1] as *u8, "plan" as *u8) == 1 { apply = 0 }
84 if kd_streq(argv[1] as *u8, "apply" as *u8) == 1 { apply = 1 }
85 if apply < 0 { kd_w("usage: nx_kinddedupe plan|apply [conf]\n" as *u8); return KD_EXIT_USAGE }
86 var conf: *u8 = KD_CONF_DEFAULT
87 if argc > 2 { conf = argv[2] as *u8 }
88
89 let lenp: *i64 = sys_mmap(KD_SLOT) as *i64
90 let buf: *u8 = sys_read_file(conf, lenp)
91 let len: i64 = lenp[0]
92 if len <= 0 {
93 kd_w("UNOBSERVABLE: could not read " as *u8); kd_w(conf); kd_w("\n" as *u8)
94 return KD_EXIT_UNOBS
95 }
96
97 // seen table sized FROM the file, never picked: a data row needs >= 4 bytes, so len/4+8 bounds rows.
98 let maxrows: i64 = len / 4 + 8
99 let seen: *i64 = sys_mmap(KD_SLOT * KD_ROW_FIELDS * maxrows) as *i64
100 let out: *u8 = sys_mmap(len + 16)
101
102 var nseen: i64 = 0
103 var i: i64 = 0
104 var kept_bytes: i64 = 0
105 var dropped: i64 = 0
106 var dropped_bytes: i64 = 0
107 var conflicts: i64 = 0
108 var data_rows: i64 = 0
109 var kept_rows: i64 = 0
110
111 while i < len {
112 let e: i64 = kd_line_end(buf, i, len)
113 var nl: i64 = 0
114 if e < len { nl = 1 }
115 let span: i64 = (e - i) + nl
116
117 // classify: leading-ws skip, then comment-or-blank => KEEP verbatim
118 var p: i64 = i
119 var skipping: i64 = 1
120 while skipping == 1 {
121 if p >= e { skipping = 0 } else { if kd_is_ws(buf[p]) == 1 { p = p + 1 } else { skipping = 0 } }
122 }
123 var keep: i64 = 1
124 if p < e {
125 if buf[p] != (KD_HASH_BYTE as u8) {
126 // DATA ROW: name token then kind token
127 data_rows = data_rows + 1
128 var q: i64 = p
129 var scan1: i64 = 1
130 while scan1 == 1 { if q >= e { scan1 = 0 } else { if kd_is_ws(buf[q]) == 1 { scan1 = 0 } else { q = q + 1 } } }
131 let name_off: i64 = p
132 let name_len: i64 = q - p
133 var k0: i64 = q
134 var scan2: i64 = 1
135 while scan2 == 1 { if k0 >= e { scan2 = 0 } else { if kd_is_ws(buf[k0]) == 1 { k0 = k0 + 1 } else { scan2 = 0 } } }
136 var k1: i64 = k0
137 var scan3: i64 = 1
138 while scan3 == 1 { if k1 >= e { scan3 = 0 } else { if kd_is_ws(buf[k1]) == 1 { scan3 = 0 } else { k1 = k1 + 1 } } }
139 let kind_off: i64 = k0
140 let kind_len: i64 = k1 - k0
141
142 // linear keep-first lookup
143 var hit: i64 = 0 - 1
144 var j: i64 = 0
145 while j < nseen {
146 let so: i64 = seen[j * KD_ROW_FIELDS]
147 let sl: i64 = seen[j * KD_ROW_FIELDS + 1]
148 if sl == name_len {
149 if kd_memeq(buf, so, buf, name_off, name_len) == 1 { hit = j; j = nseen }
150 }
151 if hit < 0 { j = j + 1 }
152 }
153 if hit < 0 {
154 seen[nseen * KD_ROW_FIELDS] = name_off
155 seen[nseen * KD_ROW_FIELDS + 1] = name_len
156 seen[nseen * KD_ROW_FIELDS + 2] = kind_off
157 seen[nseen * KD_ROW_FIELDS + 3] = kind_len
158 nseen = nseen + 1
159 } else {
160 let ko: i64 = seen[hit * KD_ROW_FIELDS + 2]
161 let kl: i64 = seen[hit * KD_ROW_FIELDS + 3]
162 var same: i64 = 0
163 if kl == kind_len { same = kd_memeq(buf, ko, buf, kind_off, kind_len) }
164 if same == 1 {
165 keep = 0
166 dropped = dropped + 1
167 dropped_bytes = dropped_bytes + span
168 kd_w(" DROP " as *u8); kd_wseg(buf, name_off, name_len)
169 kd_w(" " as *u8); kd_wseg(buf, kind_off, kind_len); kd_w("\n" as *u8)
170 } else {
171 conflicts = conflicts + 1
172 kd_w(" CONFLICT name=" as *u8); kd_wseg(buf, name_off, name_len)
173 kd_w(" first-kind=" as *u8); kd_wseg(buf, ko, kl)
174 kd_w(" second-kind=" as *u8); kd_wseg(buf, kind_off, kind_len)
175 kd_w(" -- REFUSING: first-match-wins means line order would decide\n" as *u8)
176 }
177 }
178 }
179 }
180 if keep == 1 {
181 var c: i64 = 0
182 while c < span { out[kept_bytes + c] = buf[i + c]; c = c + 1 }
183 kept_bytes = kept_bytes + span
184 if p < e { if buf[p] != (KD_HASH_BYTE as u8) { kept_rows = kept_rows + 1 } }
185 }
186 i = e + 1
187 }
188
189 kd_w("nx_kinddedupe " as *u8)
190 if apply == 1 { kd_w("apply " as *u8) } else { kd_w("plan " as *u8) }
191 kd_w(conf); kd_w("\n bytes_in=" as *u8); kd_wn(len)
192 kd_w(" data_rows=" as *u8); kd_wn(data_rows)
193 kd_w(" distinct=" as *u8); kd_wn(nseen)
194 kd_w(" dropped=" as *u8); kd_wn(dropped)
195 kd_w(" dropped_bytes=" as *u8); kd_wn(dropped_bytes)
196 kd_w(" conflicts=" as *u8); kd_wn(conflicts)
197 kd_w("\n partition bytes_kept=" as *u8); kd_wn(kept_bytes)
198 kd_w(" plus dropped_bytes=" as *u8); kd_wn(dropped_bytes)
199 kd_w(" equals bytes_in=" as *u8); kd_wn(kept_bytes + dropped_bytes)
200 if kept_bytes + dropped_bytes == len { kd_w(" SUMS\n" as *u8) } else {
201 kd_w(" DOES-NOT-SUM -- refusing every action\n" as *u8)
202 return KD_EXIT_UNOBS
203 }
204 if conflicts > 0 {
205 kd_w(" verdict=CONFLICT-REFUSED (a name declares two kinds; adjudicate on evidence, never on line order)\n" as *u8)
206 return KD_EXIT_CONFLICT
207 }
208 if apply == 0 {
209 kd_w(" verdict=PLAN-ONLY nothing written\n" as *u8)
210 return KD_EXIT_OK
211 }
212 if dropped == 0 {
213 kd_w(" verdict=CLEAN nothing to prune, nothing written\n" as *u8)
214 return KD_EXIT_OK
215 }
216
217 // temp+rename in the SAME directory, announced, size-asserted
218 let tmp: *u8 = sys_mmap(1024)
219 var tl: i64 = kd_cat(tmp, 0, conf)
220 tl = kd_cat(tmp, tl, KD_TMP_SUFFIX)
221 let fd: i64 = sys_openat_wr(tmp, MODE_0644)
222 if fd < 0 { kd_w("UNOBSERVABLE: cannot open tmp for write\n" as *u8); return KD_EXIT_UNOBS }
223 let wrote: i64 = sys_write(fd, out, kept_bytes)
224 sys_close(fd)
225 kd_w(" announce fd_wrote=" as *u8); kd_wn(wrote); kd_w(" of=" as *u8); kd_wn(kept_bytes); kd_w(" to_tmp\n" as *u8)
226 if wrote != kept_bytes { kd_w("UNOBSERVABLE: short write, target untouched\n" as *u8); return KD_EXIT_UNOBS }
227 let rr: i64 = sys_renameat(tmp, conf)
228 if rr < 0 { kd_w("UNOBSERVABLE: rename failed, target untouched\n" as *u8); return KD_EXIT_UNOBS }
229 // assert the RESULT, never trust the write
230 let lp2: *i64 = sys_mmap(KD_SLOT) as *i64
231 let b2: *u8 = sys_read_file(conf, lp2)
232 kd_w(" verdict=PRUNED bytes " as *u8); kd_wn(len); kd_w(" -> " as *u8); kd_wn(lp2[0])
233 if lp2[0] == kept_bytes { kd_w(" ASSERTED\n" as *u8) } else { kd_w(" MISMATCH -- investigate before trusting\n" as *u8); return KD_EXIT_UNOBS }
234 return KD_EXIT_OK
235}