code wiki / _hdl_build / nx_reconcile.nx
nx_reconcile.nx source
↩ module page · 257 lines · 11757 B
1// nx_reconcile.nx -- the AUTONOMOUS STATUS-KEEPER (X-Q-005): DONE flips on
2// EVIDENCE, with no session. For every TODO/WIP queue row whose gate cell
3// carries the machine suffix
4// ...||MARK=<logpath>::<anchor>::<green-substring>
5// evaluate it exactly like the examiner/rung-grade family (the LAST <anchor>
6// line in <logpath> must contain <green-substring>); if GREEN, rewrite that
7// row's status to DONE in place. Rows without a MARK suffix are untouched
8// (X-Q-003b migrates them); NOVEL rows are NEVER auto-flipped (tutor work is
9// judged by its own gates after the visit). Deps untouched -- the sequencer
10// re-derives readiness next beat. Appends RECONCILE rows to
11// knowledge/status/reconcile.log; rewrite is whole-file (read -> patch in
12// memory -> write), safe at this row count.
13// argv[1] = queue override (gates use a scratch queue).
14// license_tier: ORIGINAL
15import "nx_registry_lock.nx" // lib-only (nx_syscalls transitive); rt_lock/rt_unlock for the shared queue lock
16import "nx_itoa_lib.nx" // shared MSB-first emitter (zero-alloc)
17const K_MAGIC_524288: i64 = 524288
18const K_MAGIC_1048576: i64 = 1048576
19
20func rc_w(fd: i64, s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(fd,s,n); return 0 }
21// MIGRATED to the shared emitter (debt 1785563586). The old body mmapped a scratch buffer
22// per call and never freed it. At PAGE granularity that is 4096B leaked PER CALL -- the
23// defect that took 28.5GB of a 36GB host in nx_ts_lumadiff (2MB input, ~3.66M calls).
24// nxi_* is MSB-first, allocates NOTHING, and emits identical bytes including the sign.
25func rc_wn(fd: i64, v: i64) -> i64 { nxi_fd(fd, v); return 0 }
26
27// ARK-GATE-001: when a reconcile pass flips >=1 rung TODO->DONE, a rung was
28// ACHIEVED this beat -> stamp a FRESH ark-gate marker (open-wr truncate, one
29// deterministic line). nx_ark_push reads epoch=<sec> + verdict=ACHIEVED and
30// pushes ONCE (its own ark_push.state one-shot guard). flips==0 leaves the
31// marker UNTOUCHED so it goes stale on its own -> un-gated beats HOLD. Same
32// epoch=<sec> shape ARKPUSH/RECONCILE already emit, so existing readers are happy.
33func rc_stamp_ark_marker(flips: i64) -> i64 {
34 let fd: i64 = sys_openat_wr("knowledge/status/ark_gate.marker" as *u8, 0x1a4)
35 if fd < 0 { return 0 }
36 rc_w(fd, "ARKGATE epoch=" as *u8); rc_wn(fd, sys_now_realtime_sec())
37 rc_w(fd, " verdict=ACHIEVED flipped=" as *u8); rc_wn(fd, flips)
38 rc_w(fd, " source=reconcile\n" as *u8)
39 sys_close(fd)
40 return 1
41}
42
43func rc_read(path: *u8, buf: *u8, cap: i64) -> i64 {
44 let fd: i64 = sys_openat_rd(path)
45 if fd < 0 { return 0 }
46 var n: i64 = 0
47 var r: i64 = sys_read(fd, buf, cap - 1)
48 while r > 0 { n = n + r; if n >= cap - 1 { r = 0 } else { r = sys_read(fd, buf + n, cap - 1 - n) } }
49 sys_close(fd)
50 return n
51}
52
53func rc_at(buf: *u8, n: i64, i: i64, pat: *u8, pl: i64) -> i64 {
54 if i + pl > n { return 0 }
55 var k: i64 = 0
56 while k < pl { if buf[i + k] != pat[k] { return 0 } k = k + 1 }
57 return 1
58}
59
60func rc_len(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n }
61
62// build "<qp>.lock" into out -- the shared queue lock (live queue => assignment_queue.tsv.lock).
63func rc_lockpath(qp: *u8, out: *u8) -> i64 {
64 var i: i64 = 0
65 while qp[i] != (0 as u8) { out[i] = qp[i]; i = i + 1 }
66 let s: *u8 = ".lock" as *u8
67 var k: i64 = 0
68 while s[k] != (0 as u8) { out[i] = s[k]; i = i + 1; k = k + 1 }
69 out[i] = 0 as u8
70 return 0
71}
72
73// last <anchor> line in logpath contains green? 1/0; -1 log-or-anchor absent
74func rc_eval(logpath: *u8, anchor: *u8, green: *u8) -> i64 {
75 let buf: *u8 = sys_mmap(K_MAGIC_524288)
76 let n: i64 = rc_read(logpath, buf, K_MAGIC_524288)
77 if n <= 0 { return 0 - 1 }
78 let al: i64 = rc_len(anchor)
79 let gl: i64 = rc_len(green)
80 var found: i64 = 0
81 var ls: i64 = 0
82 var fs2: i64 = 0
83 var fe2: i64 = 0
84 var i: i64 = 0
85 while i <= n {
86 var eol: i64 = 0
87 if i == n { eol = 1 } else { if buf[i] == (10 as u8) { eol = 1 } }
88 if eol == 1 {
89 var j: i64 = ls
90 while j < i {
91 if rc_at(buf, n, j, anchor, al) == 1 { found = 1; fs2 = ls; fe2 = i; j = i } else { j = j + 1 }
92 }
93 ls = i + 1
94 }
95 i = i + 1
96 }
97 if found == 0 { return 0 - 1 }
98 var q: i64 = fs2
99 while q < fe2 {
100 if rc_at(buf, n, q, green, gl) == 1 { return 1 }
101 q = q + 1
102 }
103 return 0
104}
105
106func main(argc: i64, argv: *i64) -> i64 {
107 var qp: *u8 = "knowledge/registry/assignment_queue.tsv" as *u8
108 // is_live: reconcile is running against the REAL queue (no argv override).
109 // Only a LIVE reconcile arms the ark-gate marker; a gate that passes a
110 // scratch queue (argv[1]) must NEVER arm a real push (ARK-GATE-001).
111 var is_live: i64 = 1
112 if argc >= 2 { qp = argv[1] as *u8; is_live = 0 }
113 // ACQUIRE the SHARED queue lock around the whole read-modify-write of qp (serializes against
114 // nx_novel_close + the other writers); degrade-gracefully on giveup (-1 -> proceed unlocked,
115 // never wedge; O_EXCL stale-steal bounds a crashed holder to 30s).
116 let lockpath: *u8 = sys_mmap(512)
117 rc_lockpath(qp, lockpath)
118 let qlk: i64 = rt_lock(lockpath)
119 let buf: *u8 = sys_mmap(K_MAGIC_1048576)
120 let n: i64 = rc_read(qp, buf, K_MAGIC_1048576)
121 if n <= 0 { if qlk >= 0 { rt_unlock(lockpath, qlk) } rc_w(1, "RECONCILE verdict=RED reason=queue-missing\n" as *u8); sys_exit(101) }
122 let lf: i64 = sys_openat_append("knowledge/status/reconcile.log" as *u8, 0x1a4)
123 let lpath: *u8 = sys_mmap(512)
124 let anch: *u8 = sys_mmap(256)
125 let grn: *u8 = sys_mmap(256)
126 var flips: i64 = 0
127 var marked: i64 = 0
128 var ls: i64 = 0
129 var i: i64 = 0
130 while i <= n {
131 var eol: i64 = 0
132 if i == n { eol = 1 } else { if buf[i] == (10 as u8) { eol = 1 } }
133 if eol == 1 {
134 // line [ls, i): find "\tTODO\t" or "\tWIP\t" position + "||MARK=".
135 // ||ASK=result rows are SUBJECTIVE deliverables (executive-sponsor
136 // doctrine 2026-06-11): objective evidence green -> flip to VIEW
137 // (sponsor viewpoint queue, same 4-byte in-place write), never
138 // straight to DONE -- only nx_sponsor_review's APPROVE does that.
139 var stp: i64 = 0 - 1
140 var iswip: i64 = 0
141 var mk: i64 = 0 - 1
142 var askr: i64 = 0
143 var j: i64 = ls
144 while j < i {
145 if rc_at(buf, n, j, "\tTODO\t" as *u8, 6) == 1 { if stp < 0 { stp = j } }
146 if rc_at(buf, n, j, "\tWIP\t" as *u8, 5) == 1 { if stp < 0 { stp = j; iswip = 1 } }
147 if rc_at(buf, n, j, "||MARK=" as *u8, 7) == 1 { mk = j + 7 }
148 if rc_at(buf, n, j, "||ASK=result" as *u8, 12) == 1 { askr = 1 }
149 j = j + 1
150 }
151 if stp >= 0 {
152 if mk >= 0 {
153 marked = marked + 1
154 // parse <log>::<anchor>::<green> ending at tab or eol
155 var p: i64 = mk
156 var k: i64 = 0
157 var go: i64 = 1
158 while go == 1 {
159 if p >= i { go = 0 } else {
160 if rc_at(buf, n, p, "::" as *u8, 2) == 1 { go = 0 } else {
161 if k < 511 { lpath[k] = buf[p]; k = k + 1 }
162 p = p + 1
163 }
164 }
165 }
166 lpath[k] = 0 as u8
167 if p < i { p = p + 2 }
168 k = 0
169 go = 1
170 while go == 1 {
171 if p >= i { go = 0 } else {
172 if rc_at(buf, n, p, "::" as *u8, 2) == 1 { go = 0 } else {
173 if k < 255 { anch[k] = buf[p]; k = k + 1 }
174 p = p + 1
175 }
176 }
177 }
178 anch[k] = 0 as u8
179 if p < i { p = p + 2 }
180 // green ends at TAB (field end) OR at "||" (the next suffix,
181 // e.g. ||EXEC=) -- without the || stop, an EXEC suffix is
182 // swallowed into green and never matches (caught live:
183 // X-EX-001 marked but flipped=0).
184 k = 0
185 go = 1
186 while go == 1 {
187 if p >= i { go = 0 } else {
188 if buf[p] == (9 as u8) { go = 0 } else {
189 if rc_at(buf, n, p, "||" as *u8, 2) == 1 { go = 0 } else {
190 if k < 255 { grn[k] = buf[p]; k = k + 1 }
191 p = p + 1
192 }
193 }
194 }
195 }
196 grn[k] = 0 as u8
197 if rc_eval(lpath, anch, grn) == 1 {
198 // flip in place: TODO->DONE or TODO->VIEW (same length);
199 // WIP->DONE shifts -- handle TODO only v1, WIP said
200 if iswip == 0 {
201 if askr == 0 {
202 buf[stp + 1] = 68 as u8 // D
203 buf[stp + 2] = 79 as u8 // O
204 buf[stp + 3] = 78 as u8 // N
205 buf[stp + 4] = 69 as u8 // E
206 } else {
207 buf[stp + 1] = 86 as u8 // V
208 buf[stp + 2] = 73 as u8 // I
209 buf[stp + 3] = 69 as u8 // E
210 buf[stp + 4] = 87 as u8 // W
211 }
212 flips = flips + 1
213 rc_w(1, "RECONCILE-FLIP line-at=" as *u8); rc_wn(1, ls)
214 if askr == 0 { rc_w(1, " marker-green\n" as *u8) } else { rc_w(1, " marker-green -> VIEW (sponsor viewpoint)\n" as *u8) }
215 if lf >= 0 {
216 rc_w(lf, "RECONCILE-FLIP offset=" as *u8); rc_wn(lf, ls)
217 if askr == 0 { rc_w(lf, " -> DONE\n" as *u8) } else { rc_w(lf, " -> VIEW\n" as *u8) }
218 }
219 } else {
220 rc_w(1, "RECONCILE-WIP-GREEN (flip manually or via v2; WIP len differs)\n" as *u8)
221 }
222 }
223 }
224 }
225 ls = i + 1
226 }
227 i = i + 1
228 }
229 if flips > 0 {
230 let wf: i64 = sys_openat_wr(qp, 0x1a4)
231 if wf >= 0 {
232 var off: i64 = 0
233 while off < n { let w: i64 = sys_write(wf, buf + off, n - off); if w <= 0 { off = n } else { off = off + w } }
234 sys_close(wf)
235 }
236 // a rung was ACHIEVED this beat -> let the ark push once (ARK-GATE-001).
237 // ONLY when reconciling the LIVE queue; a scratch-queue gate run never
238 // arms a real push.
239 if is_live == 1 { rc_stamp_ark_marker(flips) }
240 }
241 if qlk >= 0 { rt_unlock(lockpath, qlk) }
242 var p2: i64 = 0
243 while p2 < 2 {
244 var fd: i64 = 1
245 if p2 == 1 { fd = lf }
246 if fd >= 0 {
247 rc_w(fd, "RECONCILE marked=" as *u8); rc_wn(fd, marked)
248 rc_w(fd, " flipped=" as *u8); rc_wn(fd, flips)
249 rc_w(fd, " epoch=" as *u8); rc_wn(fd, sys_now_realtime_sec())
250 rc_w(fd, " verdict=GREEN\n" as *u8)
251 }
252 p2 = p2 + 1
253 }
254 if lf >= 0 { sys_close(lf) }
255 sys_exit(0)
256 return 0
257}