code wiki / (root) / nx_kinddedupe.nx

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}