nx_mfrow.nx source
↩ module page · 239 lines · 13023 B
1// nx_mfrow.nx -- UPDATE ONE ROW OF A TREECANON MANIFEST, AND NOTHING ELSE.
2//
3// WHY THIS EXISTS, measured 2026-08-16. The treecanon family has a generator (nx_treediff, nx_treehash),
4// two comparers (nx_treediverge, nx_hashdiverge) and a gate that refuses (nx_treecanon_gate). It has NO
5// surgical write. So when ONE file is converged across trees, the only sanctioned way to record it is to
6// REGENERATE THE WHOLE MANIFEST -- and the record is explicit that a whole-tree regeneration while a
7// sibling is mid-edit is the one action that blesses unreviewed bytes at scale. Measured the same day:
8// nx_treediverge reports 2,154 divergent paths, so that precondition is not merely risky, it is UNMET.
9// The result was a converged file that could not be recorded, and therefore an organ that could not be
10// rebuilt: nx_sov_build_run, laptop and NAS byte-identical, still refused by the canon gate.
11// ★A FAMILY WITH NO SURGICAL WRITE TURNS EVERY CORRECTION INTO A WHOLE-TREE REGENERATION, SO THE ONLY
12// SAFE-SIZED FIX IS THE ONE NOBODY CAN MAKE.
13//
14// SCOPE, deliberately narrow: one row, named by its relpath, in a manifest whose other bytes are
15// untouched. It does not scan a tree, does not compute a hash, and cannot invent a row. Recording a
16// convergence someone else proved is a DIFFERENT act from deciding one, and only the first belongs here.
17// license_tier: ORIGINAL Writes ONE named file, atomically. No hw writes (Rule 26). expect_exit: 0
18
19import "nx_gate_verdict.nx"
20import "nx_tool_run.nx" // tr_contains: the estate's substring primitive, not a fifth private copy
21
22const MFR_SP: i64 = 32
23const MFR_NL: i64 = 10
24const MFR_HEADROOM: i64 = 256 // output = input + this; a rewritten row can only grow by its own field widths
25
26func mfr_w(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 }
27func mfr_slen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n }
28func mfr_n(v: i64) -> i64 {
29 let b: *u8 = sys_mmap(32)
30 let t: *u8 = sys_mmap(32)
31 if v == 0 { b[0] = 48 as u8; sys_write(1, b, 1); return 0 }
32 var m: i64 = v
33 if m < 0 { sys_write(1, "-" as *u8, 1); m = 0 - m }
34 var k: i64 = 0
35 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 }
36 var o: i64 = 0
37 while k > 0 { k = k - 1; b[o] = t[k]; o = o + 1 }
38 sys_write(1, b, o)
39 return 0
40}
41func mfr_eq(a: i64, b: i64) -> i64 { if a == b { return 1 } return 0 }
42
43// Does buf[s,e) end with exactly this relpath as its LAST space-separated field?
44// The relpath is matched WHOLE: a suffix test would match nx_sov_build_run.nx against
45// _register_nx_sov_build_run.nx, and a manifest has thousands of near-identical names.
46func mfr_row_is(buf: *u8, s: i64, e: i64, rel: *u8) -> i64 {
47 var last: i64 = s
48 var i: i64 = s
49 while i < e { if buf[i] == (MFR_SP as u8) { last = i + 1 } i = i + 1 }
50 let rl: i64 = mfr_slen(rel)
51 if e - last != rl { return 0 }
52 var k: i64 = 0
53 while k < rl { if buf[last + k] != rel[k] { return 0 } k = k + 1 }
54 return 1
55}
56
57func mfr_spaces(buf: *u8, s: i64, e: i64) -> i64 {
58 var c: i64 = 0
59 var i: i64 = s
60 while i < e { if buf[i] == (MFR_SP as u8) { c = c + 1 } i = i + 1 }
61 return c
62}
63
64func mfr_put(dst: *u8, o: i64, s: *u8) -> i64 {
65 var i: i64 = 0
66 var oo: i64 = o
67 while s[i] != (0 as u8) { dst[oo] = s[i]; i = i + 1; oo = oo + 1 }
68 return oo
69}
70
71// ---- THE UPDATE -------------------------------------------------------------------------------------
72// Returns rows matched. REFUSES (via the caller) on 0 or >1 -- see the laws at each site.
73func mfr_apply(path: *u8, rel: *u8, newsize: *u8, newhash: *u8) -> i64 {
74 let ln: *i64 = sys_mmap(16) as *i64
75 let buf: *u8 = sys_read_file(path, ln)
76 if (buf as i64) == 0 { mfr_w("NX-MFROW REFUSED: manifest unreadable -- " as *u8); mfr_w(path); mfr_w("\n" as *u8); return 0 - 1 }
77 let n: i64 = ln[0]
78 if n <= 0 { mfr_w("NX-MFROW REFUSED: manifest is EMPTY -- a rewriter that read nothing must not write anything\n" as *u8); return 0 - 1 }
79
80 // SIZED FROM THE INPUT, NOT GUESSED. sys_read_file gives the exact length, and a rewritten row can
81 // only grow by the width of the two fields being replaced, so input+HEADROOM is a derived bound and
82 // not a ceiling anyone has to tune.
83 let out: *u8 = sys_mmap(n + MFR_HEADROOM)
84 var o: i64 = 0
85 var rows: i64 = 0
86 var matched: i64 = 0
87 var badfmt: i64 = 0
88 var ls: i64 = 0
89 var i: i64 = 0
90 while i <= n {
91 var atend: i64 = 0
92 if i == n { atend = 1 } else { if buf[i] == (MFR_NL as u8) { atend = 1 } }
93 if atend == 1 {
94 if i > ls {
95 rows = rows + 1
96 if mfr_row_is(buf, ls, i, rel) == 1 {
97 matched = matched + 1
98 let nsp: i64 = mfr_spaces(buf, ls, i)
99 // FORMAT IS READ FROM THE ROW, NEVER ASSUMED. Two families share this directory:
100 // `<bytes> <relpath>` (nx_treediff) and `<sha> <bytes> <relpath>` (nx_treehash).
101 // Writing the wrong shape is how a comparer later parses ZERO rows and reports a
102 // confident CONVERGED -- the exact defect fixed in nx_treediverge the same day.
103 if nsp == 1 {
104 o = mfr_put(out, o, newsize)
105 out[o] = MFR_SP as u8; o = o + 1
106 o = mfr_put(out, o, rel)
107 } else {
108 if nsp == 2 {
109 if (newhash as i64) == 0 { badfmt = 1 }
110 else {
111 o = mfr_put(out, o, newhash)
112 out[o] = MFR_SP as u8; o = o + 1
113 o = mfr_put(out, o, newsize)
114 out[o] = MFR_SP as u8; o = o + 1
115 o = mfr_put(out, o, rel)
116 }
117 } else { badfmt = 1 }
118 }
119 } else {
120 var k: i64 = ls
121 while k < i { out[o] = buf[k]; o = o + 1; k = k + 1 }
122 }
123 }
124 if i < n { out[o] = MFR_NL as u8; o = o + 1 }
125 ls = i + 1
126 }
127 i = i + 1
128 }
129
130 mfr_w("NX-MFROW rows=" as *u8); mfr_n(rows)
131 mfr_w(" matched=" as *u8); mfr_n(matched)
132 mfr_w(" bytes_in=" as *u8); mfr_n(n)
133 mfr_w(" bytes_out=" as *u8); mfr_n(o)
134 mfr_w("\n" as *u8)
135
136 // ★A PATCH THAT CANNOT FAIL CANNOT BE TRUSTED TO HAVE APPLIED. A rewriter that finds nothing and
137 // writes the file back unchanged reports success for a no-op, which is how a correction silently
138 // does not happen. Zero matches is a REFUSAL, never a quiet pass.
139 if matched == 0 { mfr_w("NX-MFROW REFUSED: relpath not present in this manifest -- nothing was written\n" as *u8); return 0 }
140 // Ambiguity is a DIFFERENT defect and must not be silently resolved: a manifest holding two rows for
141 // one path has no single answer, and picking one would bless whichever came first.
142 if matched > 1 { mfr_w("NX-MFROW REFUSED: relpath appears more than once -- a registry with two rows for one path has no single answer; de-duplicate first. Nothing was written\n" as *u8); return 0 - 2 }
143 if badfmt == 1 { mfr_w("NX-MFROW REFUSED: the matched row is neither `<bytes> <relpath>` nor `<sha> <bytes> <relpath>`, or a hash row was given no hash. Nothing was written\n" as *u8); return 0 - 3 }
144
145 // ATOMIC: write a sibling temp then rename. A half-written manifest is worse than a stale one --
146 // the canon gate would read a truncated registry and refuse every organ in the tree.
147 let tmp: *u8 = sys_mmap(512)
148 var t: i64 = mfr_put(tmp, 0, path)
149 t = mfr_put(tmp, t, ".mfrowtmp" as *u8)
150 tmp[t] = 0 as u8
151 let fd: i64 = sys_openat_wr(tmp, MODE_0644)
152 if fd < 0 { mfr_w("NX-MFROW REFUSED: cannot open temp for write; the manifest is untouched\n" as *u8); return 0 - 4 }
153 let wr: i64 = sys_write(fd, out, o)
154 sys_close(fd)
155 if wr != o { mfr_w("NX-MFROW REFUSED: short write to temp; the manifest is untouched\n" as *u8); return 0 - 5 }
156 sys_renameat(tmp, path)
157 mfr_w("NX-MFROW UPDATED " as *u8); mfr_w(path); mfr_w(" row=" as *u8); mfr_w(rel); mfr_w("\n" as *u8)
158 return 1
159}
160
161func mfr_selftest() -> i64 {
162 let ctr: *i64 = gv_ctr()
163 gv_head("nx_mfrow -- one row changes, every other byte survives" as *u8)
164 sys_mkdir("/tmp/nx_mfrow" as *u8, MODE_0755)
165
166 // 3-field (hash) fixture. The neighbour rows exist so "untouched" is a claim with witnesses.
167 let p3: *u8 = "/tmp/nx_mfrow/h.mf" as *u8
168 let f3: *u8 = "aaaa 111 a/one.nx\nbbbb 222 b/two.nx\ncccc 333 c/three.nx\n" as *u8
169 let fd3: i64 = sys_openat_wr(p3, MODE_0644)
170 sys_write(fd3, f3, mfr_slen(f3)); sys_close(fd3)
171 let r3: i64 = mfr_apply(p3, "b/two.nx" as *u8, "999" as *u8, "zzzz" as *u8)
172 gv_check("a 3-field hash row is rewritten and reports exactly one match", mfr_eq(r3, 1), ctr)
173 let l3: *i64 = sys_mmap(16) as *i64
174 let g3: *u8 = sys_read_file(p3, l3)
175 gv_check("the new hash and size are present", tr_contains(g3, l3[0], "zzzz 999 b/two.nx" as *u8), ctr)
176 gv_check("the row BEFORE it is untouched", tr_contains(g3, l3[0], "aaaa 111 a/one.nx" as *u8), ctr)
177 gv_check("the row AFTER it is untouched", tr_contains(g3, l3[0], "cccc 333 c/three.nx" as *u8), ctr)
178 gv_check("the OLD row is gone -- a rewriter that appends instead of replacing leaves two answers", mfr_eq(tr_contains(g3, l3[0], "bbbb 222 b/two.nx" as *u8), 0), ctr)
179
180 // 2-field (size) fixture -- the OTHER family, read from the row rather than assumed.
181 let p2: *u8 = "/tmp/nx_mfrow/s.mf" as *u8
182 let f2: *u8 = "111 a/one.nx\n222 b/two.nx\n" as *u8
183 let fd2: i64 = sys_openat_wr(p2, MODE_0644)
184 sys_write(fd2, f2, mfr_slen(f2)); sys_close(fd2)
185 let r2: i64 = mfr_apply(p2, "b/two.nx" as *u8, "888" as *u8, 0 as *u8)
186 gv_check("a 2-field size row is rewritten with no hash supplied", mfr_eq(r2, 1), ctr)
187 let l2: *i64 = sys_mmap(16) as *i64
188 let g2: *u8 = sys_read_file(p2, l2)
189 gv_check("the 2-field row keeps its shape: no phantom hash column appears", tr_contains(g2, l2[0], "888 b/two.nx" as *u8), ctr)
190
191 // NEG-CONTROLS. A rewriter that quietly does nothing is the defect this organ exists to avoid.
192 let rmiss: i64 = mfr_apply(p2, "nope/absent.nx" as *u8, "1" as *u8, 0 as *u8)
193 gv_check("neg-control-absent-relpath-REFUSES-rather-than-silently-rewriting-nothing", mfr_eq(rmiss, 0), ctr)
194
195 let pd: *u8 = "/tmp/nx_mfrow/d.mf" as *u8
196 let fdup: *u8 = "111 dup/x.nx\n222 dup/x.nx\n" as *u8
197 let fdd: i64 = sys_openat_wr(pd, MODE_0644)
198 sys_write(fdd, fdup, mfr_slen(fdup)); sys_close(fdd)
199 let rdup: i64 = mfr_apply(pd, "dup/x.nx" as *u8, "777" as *u8, 0 as *u8)
200 gv_check("neg-control-duplicate-relpath-REFUSES-rather-than-picking-the-first", mfr_eq(rdup, 0 - 2), ctr)
201 let ld: *i64 = sys_mmap(16) as *i64
202 let gd: *u8 = sys_read_file(pd, ld)
203 gv_check("and the ambiguous manifest is left EXACTLY as it was", tr_contains(gd, ld[0], "111 dup/x.nx" as *u8), ctr)
204
205 // A SUFFIX MATCH WOULD BE WRONG: thousands of manifest paths share tails.
206 let ps: *u8 = "/tmp/nx_mfrow/x.mf" as *u8
207 let fs2: *u8 = "111 a/build_run.nx\n222 a/nx_build_run.nx\n" as *u8
208 let fds: i64 = sys_openat_wr(ps, MODE_0644)
209 sys_write(fds, fs2, mfr_slen(fs2)); sys_close(fds)
210 let rs: i64 = mfr_apply(ps, "a/build_run.nx" as *u8, "555" as *u8, 0 as *u8)
211 let ls2: *i64 = sys_mmap(16) as *i64
212 let gs: *u8 = sys_read_file(ps, ls2)
213 gv_check("neg-control-a-LONGER-path-ending-in-the-same-text-is-NOT-matched", tr_contains(gs, ls2[0], "222 a/nx_build_run.nx" as *u8), ctr)
214 gv_check("and the intended row DID change, so the test is not passing by matching nothing", tr_contains(gs, ls2[0], "555 a/build_run.nx" as *u8), ctr)
215
216 return gv_verdict("nx_mfrow" as *u8, ctr, "one row changes, neighbours survive, and every ambiguity refuses" as *u8)
217}
218
219func main(argc: i64, argv: *i64) -> i64 {
220 if argc >= 2 {
221 let v: *u8 = argv[1] as *u8
222 if v[0] == (115 as u8) { let rc: i64 = mfr_selftest(); sys_exit(rc); return rc }
223 }
224 if argc < 4 {
225 mfr_w("usage: nx_mfrow <manifest> <relpath> <newsize> [newhash]\n" as *u8)
226 mfr_w(" nx_mfrow selftest\n" as *u8)
227 mfr_w(" Updates ONE row, named by its WHOLE relpath. Refuses on 0 matches, on >1, and on a row\n" as *u8)
228 mfr_w(" whose shape it does not recognise. Every other byte of the manifest is preserved.\n" as *u8)
229 mfr_w(" It does NOT hash or scan anything: supply the values something else already proved.\n" as *u8)
230 sys_exit(2)
231 return 2
232 }
233 var nh: *u8 = 0 as *u8
234 if argc >= 5 { nh = argv[4] as *u8 }
235 let r: i64 = mfr_apply(argv[1] as *u8, argv[2] as *u8, argv[3] as *u8, nh)
236 if r == 1 { sys_exit(0); return 0 }
237 sys_exit(1)
238 return 1
239}