nx_clockjob.nx source
↩ module page · 559 lines · 34211 B
1// nx_clockjob.nx -- THE MISSING PRODUCER for the clock's desired-state plane (2026-08-06).
2// WHY THIS EXISTS. A sibling seat made clk_merge_store RECONCILE knowledge/store/clockjobs- toward
3// desired state ("clockjobs- is now DESIRED STATE, not an append-only inbox") -- so re-periodding a
4// clock job is finally SUPPORTED. But NOTHING COULD WRITE THAT PLANE: nx_clockjobs.nx is a LIBRARY with
5// no main(), it is not in tool_allowlist.conf so it cannot even be cap-minted, and nx_store_put emits a
6// FIXED 9-FIELD row that projects garbage onto this 3-column grammar (a prior attempt left a row
7// NEUTRALIZED at interval 99999999). The live plane still carries the scar: row q:7 reads
8// "600<TAB>nx_segguard.sh" with NO NAME AT ALL -- a half-written row that the dispatcher's
9// MALFORMED-ROW guard now has to skip on every single window.
10// (STAR)A RECONCILER WITH NO PRODUCER IS A CONSUMER WITH NOTHING TO CONSUME -- shipping the desired-state
11// half without a writer leaves the registry exactly as unfixable as before, while LOOKING fixed.
12// (STAR)THE ABSENCE OF A SANCTIONED WRITER DOES NOT STOP WRITES, IT ONLY MAKES THEM MALFORMED.
13// nx_clockjob put <name> <interval-secs> <organ>
14// Grammar is EXACTLY the dispatcher's: name<TAB>interval<TAB>organ. Validated at the boundary so the
15// q:7 class cannot recur: no empty field, no embedded tab, interval must be a positive integer.
16// Append is sts_append_fast_locked (the O(1) locked path) -- last declaration wins at reconcile time.
17// license_tier: ORIGINAL No hw writes (Rule 26). expect_exit: 0
18import "nx_syscalls.nx"
19import "nx_store_seed_lib.nx"
20import "nx_clock_caps.nx"
21
22const CJ_PREFIX: *u8 = "knowledge/store/clockjobs-"
23const CJ_ROWCAP: i64 = CLK_CMD_CAP // THE SAME const the dispatcher's CLK_NAMEW slot is derived
24 // from (nx_clock_caps.nx): the writer can never admit a row
25 // the reader would truncate, and neither side can drift alone.
26const CJ_TAB: i64 = 9
27
28func cj_out(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 }
29// REFUSALS GO TO BOTH CHANNELS (2026-08-20). Every guard below reports through cj_err on fd 2, and the
30// MCP tool surface shows only fd 1 -- so a REFUSED put was delivered to its caller as COMPLETE SILENCE,
31// indistinguishable from a dropped response or a success with no receipt. That is exactly how this
32// organ's beat registration became unfalsifiable. ★AN ERROR CHANNEL THE CALLER CANNOT SEE IS AN ERROR
33// CHANNEL THAT DOES NOT EXIST; a refusal nobody receives reads as a no-op.
34func cj_err(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(2,s,n); sys_write(1,s,n); return 0 }
35func cj_cat(dst: *u8, o: i64, s: *u8) -> i64 { var x: i64=o; var i: i64=0; while s[i]!=(0 as u8){dst[x]=s[i];x=x+1;i=i+1} return x }
36func cj_num(dst: *u8, o: i64, v: i64) -> i64 { var x: i64=o; var mm: i64=v; if mm==0{dst[x]=48 as u8;return x+1} let t:*u8=sys_mmap(24); var k:i64=0; while mm>0{t[k]=(48+(mm%10)) as u8;mm=mm/10;k=k+1} var j:i64=0; while j<k{dst[x]=t[k-1-j];x=x+1;j=j+1} return x }
37func cj_len(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} return n }
38func cj_tabfree(s: *u8) -> i64 { var i: i64=0; while s[i]!=(0 as u8){ if s[i]==(CJ_TAB as u8){return 0} i=i+1 } return 1 }
39func cj_atoi(s: *u8) -> i64 {
40 var v: i64=0; var i: i64=0
41 if s[0]==(0 as u8) { return 0-1 }
42 while s[i]!=(0 as u8) {
43 if s[i]<(48 as u8) { return 0-1 }
44 if s[i]>(57 as u8) { return 0-1 }
45 v=v*10+((s[i]-(48 as u8)) as i64); i=i+1
46 }
47 return v
48}
49
50// RESOLVABILITY GUARD (2026-08-07). This organ already refused an empty name, an empty organ, a tab
51// inside either, and a non-positive interval -- but it accepted an organ that RESOLVES TO NOTHING.
52// Measured: a put of the bare name nx_raidwatch (no .elf) was accepted without complaint, and every
53// working row in the registry names either nx_foo.elf or a full path. A row whose organ does not
54// exist is a beat that is DEAD THE MOMENT IT IS WRITTEN, and it dies silently -- the clock simply
55// execs nothing and no one learns. That is the same shape as the five jobs this estate once lost to
56// a single wrong constant.
57// Resolution mirrors the clock: an organ beginning with / is absolute, anything else is relative to
58// the estate root. Only the FIRST token is checked, because a row may legitimately carry arguments
59// (nx_actlog.elf harden knowledge/status/fallback.jrnl is a real live row).
60// SCOPE, stated rather than implied: this proves the path EXISTS, not that the exec bit is set. It
61// closes the observed defect (a name pointing at nothing) and does not claim more than it tests.
62func cj_first_token(src: *u8, dst: *u8, cap: i64) -> i64 {
63 var i: i64 = 0
64 while i < cap - 1 {
65 let c: i64 = src[i] as i64
66 if c == 0 { dst[i] = 0 as u8; return i }
67 if c == 32 { dst[i] = 0 as u8; return i }
68 dst[i] = src[i]
69 i = i + 1
70 }
71 dst[i] = 0 as u8
72 return i
73}
74func cj_resolves(org: *u8) -> i64 {
75 let tok: *u8 = sys_mmap(CJ_ROWCAP)
76 let n: i64 = cj_first_token(org, tok, CJ_ROWCAP)
77 if n == 0 { return 0 }
78 let path: *u8 = sys_mmap(CJ_ROWCAP)
79 var o: i64 = 0
80 if tok[0] != (47 as u8) { o = cj_cat(path, 0, "/volume1/homes/elderwesto/nishihost/" as *u8) }
81 o = cj_cat(path, o, tok)
82 path[o] = 0 as u8
83 let sb: *u8 = sys_mmap(256)
84 let rc: i64 = sys_fstatat(path, sb)
85 if rc < 0 { return 0 }
86 return 1
87}
88const CJ_LIVE_PREFIX: *u8 = "knowledge/store/clocksched-"
89const CJ_NAMECAP: i64 = 256
90const CJ_NL: i64 = 10 // mirrors this file's own CJ_TAB convention (a local byte-name, not a second ruler)
91
92// THE READ SIDE (2026-08-20). This organ was WRITE-ONLY: `put` existed and NOTHING could read the plane
93// back, so "I declared a beat" and "a beat runs" were indistinguishable to every caller -- INCLUDING THE
94// WRITER. Measured the day this landed: a put returned no stdout at all (every refusal goes to fd 2 via
95// cj_err, and the MCP surface shows only fd 1), and the plane then read EMPTY through BOTH the MCP verb
96// and the CLI lane -- leaving the beat registration unfalsifiable in either direction.
97// ★A WRITE-ONLY CONTROL PLANE CANNOT BE AUDITED, AND EVERY ROW IN IT IS A CLAIM RATHER THAN A FACT.
98// The record already carried the cost twice: a first-write-wins registrar made every correction a silent
99// no-op, and 7 shell jobs kept running on the clock while their sovereign replacements sat unreached --
100// nothing could SEE either, because nothing could read this plane.
101// TWO PLANES, NEVER CONFLATED: `list` reads DESIRED (clockjobs- = what was declared) and `live` reads
102// LIVE (clocksched- = what the scheduler actually runs). clk_merge_store folds desired into live at the
103// scheduler's window-init (~28-30 min, NOT per tick), so a row can be legitimately DECLARED-BUT-NOT-YET
104// -LIVE. This reader NAMES which plane it is showing, so that state is READ instead of guessed.
105func cj_streq(a: *u8, b: *u8) -> i64 {
106 var i: i64 = 0
107 while a[i] != (0 as u8) { if a[i] != b[i] { return 0 } i = i + 1 }
108 if b[i] != (0 as u8) { return 0 }
109 return 1
110}
111// eol scan with an explicit FLAG, never a cursor-clobbering sentinel: a loop that breaks by overwriting
112// its own cursor cannot also report where it stopped (the estate has written that bug four times).
113func cj_eol(buf: *u8, n: i64, i: i64) -> i64 {
114 var e: i64 = i
115 var got: i64 = 0
116 while got == 0 {
117 if e >= n { got = 1 } else {
118 if buf[e] == (CJ_NL as u8) { got = 1 } else { e = e + 1 }
119 }
120 }
121 return e
122}
123func cj_rowname(buf: *u8, s: i64, e: i64, out: *u8, cap: i64) -> i64 {
124 var i: i64 = s
125 var o: i64 = 0
126 var stop: i64 = 0
127 while i < e {
128 if stop == 0 {
129 if buf[i] == (CJ_TAB as u8) { stop = 1 } else {
130 if o < cap - 1 { out[o] = buf[i]; o = o + 1 }
131 }
132 }
133 i = i + 1
134 }
135 out[o] = 0 as u8
136 return o
137}
138// THE TOMBSTONE (2026-08-20). This plane had NO WAY TO SAY "this job is retired": nx_store_compact
139// collapses SEGMENTS not rows, nx_retire_path retires FILES, nx_store_put's `close` sets a status column
140// the 3-col clock grammar does not have and the dispatcher ignores -- and nx_capsearch over 1,177
141// registered tools surfaces no tombstone verb for a seg-store row at all. So a declaration, once made,
142// could never be withdrawn: the only exit was a plane REWRITE, which is exactly what you must not do to
143// a production plane that nine lanes and a live scheduler are reading.
144// SHAPE, AND WHY THIS SHAPE. A tombstone is EXACTLY TWO TAB-SEPARATED FIELDS: __TOMB__ TAB <name>.
145// 1. TWO FIELDS. clk_merge_store accepts a desired row only when field 0 AND field 2 are both
146// non-empty (`if w > 0 { if g > 0 {`), so a two-field row is structurally skipped. That is not an
147// assertion: the desired plane has carried the malformed two-field row "600 TAB nx_segguard.sh"
148// for weeks through every merge window, and `get 600` reports it DECLARED=YES LIVE=NO. A production
149// row nobody planted is a free natural control, and it proves the property the tombstone rests on.
150// 2. THE TARGET NAME IS IN FIELD 1, NEVER FIELD 0. Even if some future reconciler grew lenient about
151// missing organs, it still could not create, revive or re-target the tombstoned job from this row --
152// the worst it could do is schedule a bogus job literally called __TOMB__ with nothing to exec.
153// (STAR)FAIL-SAFE BY CONSTRUCTION BEATS FAIL-SAFE BY TRUSTING A PARSER YOU DID NOT WRITE -- and I could
154// not trust this one: buildroot/runtime/_hdl_build/nx_clockjobs.nx's clk_merge_store is add-only and
155// FIRST-wins, while the LIVE plane demonstrably re-targets to the LAST declaration (rebuilddrain's
156// live row is `beat 16`, its third and last desired row, not its first). The source copy on disk
157// does not match the deployed behaviour, so the design was settled by measurement instead.
158// 3. APPEND, NEVER REWRITE. History is preserved exactly as the additive-only law requires; the
159// tombstone row IS the durable record of the removal, timestamped by its own position in the
160// append-only plane. A second journal file would be a duplicate ruler.
161// (STAR)(STAR)(STAR)(STAR)(STAR)WHAT A TOMBSTONE CANNOT DO, SAID OUT LOUD BECAUSE THE NAME `drop` PROMISES MORE THAN THE
162// MECHANISM DELIVERS: the reconciler ADDS and UPDATES and NEVER REMOVES, so a tombstone retracts a
163// DECLARATION and CANNOT STOP A JOB THAT IS ALREADY LIVE. Every verb below reports that state rather
164// than letting `drop` read as "the beat is stopped".
165const CJ_TOMB: *u8 = "__TOMB__"
166
167// second tab-delimited field of a row, i.e. the tombstone's TARGET NAME. Returns -1 when the row has no
168// second field at all, so "no field" is never silently read as the empty string.
169func cj_rowfield1(buf: *u8, s: i64, e: i64, out: *u8, cap: i64) -> i64 {
170 var i: i64 = s
171 var seen: i64 = 0
172 var done: i64 = 0
173 var o: i64 = 0
174 while i < e {
175 if done == 0 {
176 if seen == 0 {
177 if buf[i] == (CJ_TAB as u8) { seen = 1 }
178 } else {
179 if buf[i] == (CJ_TAB as u8) { done = 1 } else {
180 if o < cap - 1 { out[o] = buf[i]; o = o + 1 }
181 }
182 }
183 }
184 i = i + 1
185 }
186 out[o] = 0 as u8
187 if seen == 0 { return 0 - 1 }
188 return o
189}
190
191// THE ONE RULER every reader below shares, so a tombstone can never mean one thing to `get` and another
192// to `dupes`. ONE forward pass over the plane for ONE name:
193// returns EFFECTIVE row count = declarations that survive the last tombstone for this name
194// tp[0] = byte offset just after that last tombstone, or -1 when the name has never been tombstoned
195// so the three states are read from two numbers and never collapsed: eff>0 DECLARED, eff==0 with tp>=0
196// TOMBSTONED, eff==0 with tp<0 ABSENT. Scratch buffers are passed IN -- this is called once per row by
197// the listing verbs and allocating inside would put an mmap in the hot loop.
198func cj_scan(buf: *u8, n: i64, want: *u8, na: *u8, nb: *u8, tp: *i64) -> i64 {
199 var pos: i64 = 0 - 1
200 var eff: i64 = 0
201 var i: i64 = 0
202 while i < n {
203 let e: i64 = cj_eol(buf, n, i)
204 if e > i {
205 cj_rowname(buf, i, e, na, CJ_NAMECAP)
206 if cj_streq(na, CJ_TOMB) == 1 {
207 cj_rowfield1(buf, i, e, nb, CJ_NAMECAP)
208 if cj_streq(nb, want) == 1 { pos = e + 1; eff = 0 }
209 } else {
210 if cj_streq(na, want) == 1 { eff = eff + 1 }
211 }
212 }
213 i = e + 1
214 }
215 tp[0] = pos
216 return eff
217}
218
219// THREE OUTCOMES, never two: UNREADABLE (could not load) is NOT the same as EMPTY (loaded, nothing in it),
220// and conflating them is how "I could not look" gets published as "there is nothing there".
221func cj_dump2(prefix: *u8, tombsrc: *u8, label: *u8, show: i64) -> i64 {
222 let lenp: *i64 = sys_mmap(16) as *i64
223 lenp[0] = 0
224 let buf: *u8 = sts_load_fit(prefix, lenp)
225 let n: i64 = lenp[0]
226 cj_out(label)
227 if (buf as i64) == 0 {
228 cj_out(" UNREADABLE -- the plane could not be loaded. THIS IS NOT THE SAME AS EMPTY.\n" as *u8)
229 return 0 - 1
230 }
231 if n <= 0 {
232 cj_out(" EMPTY/unseeded -- no segments resolve under this prefix, so NOTHING WAS EVER COMMITTED HERE.\n" as *u8)
233 return 0
234 }
235 // WHERE THE TOMBSTONES LIVE. For DESIRED they are in this very buffer (tombsrc null). For LIVE they
236 // are in DESIRED, because a tombstone can never merge -- so `live` CROSS-READS, and that is the only
237 // way a reader can see "the declaration was withdrawn but the job is still scheduled".
238 var tbuf: *u8 = buf
239 var tn: i64 = n
240 var tok: i64 = 1
241 if (tombsrc as i64) != 0 {
242 let tlp: *i64 = sys_mmap(16) as *i64
243 tlp[0] = 0
244 let tb: *u8 = sts_load_fit(tombsrc, tlp)
245 if (tb as i64) == 0 { tok = 0 } else { tbuf = tb; tn = tlp[0] }
246 }
247 if tok == 0 {
248 cj_out(" TOMBSTONE-SOURCE UNREADABLE -- withdrawal marking is UNAVAILABLE for this listing, so the rows below are NOT certified as still-declared. An axis that cannot see must ABSTAIN, never acquit.\n" as *u8)
249 }
250 let na: *u8 = sys_mmap(CJ_NAMECAP)
251 let nb: *u8 = sys_mmap(CJ_NAMECAP)
252 let nc: *u8 = sys_mmap(CJ_NAMECAP)
253 let tp: *i64 = sys_mmap(16) as *i64
254 var rows: i64 = 0
255 var eff: i64 = 0
256 var tombrows: i64 = 0
257 var dead: i64 = 0
258 var i: i64 = 0
259 while i < n {
260 let e: i64 = cj_eol(buf, n, i)
261 if e > i {
262 rows = rows + 1
263 var mark: i64 = 0
264 cj_rowname(buf, i, e, nc, CJ_NAMECAP)
265 if cj_streq(nc, CJ_TOMB) == 1 { mark = 1; tombrows = tombrows + 1 } else {
266 if tok == 1 {
267 let ec: i64 = cj_scan(tbuf, tn, nc, na, nb, tp)
268 if (tombsrc as i64) == 0 {
269 if tp[0] > i { mark = 2 }
270 } else {
271 if tp[0] >= 0 { if ec == 0 { mark = 3 } }
272 }
273 }
274 }
275 if mark == 0 { eff = eff + 1 }
276 if mark == 2 { dead = dead + 1 }
277 if mark == 3 { dead = dead + 1 }
278 if show == 1 {
279 cj_out(" " as *u8)
280 sys_write(1, ((buf as i64) + i) as *u8, e - i)
281 if mark == 1 { cj_out(" [TOMBSTONE -- withdraws every earlier declaration of the name in its second field; two fields only, so the reconciler skips it]" as *u8) }
282 if mark == 2 { cj_out(" [WITHDRAWN by a later TOMBSTONE -- not desired state]" as *u8) }
283 if mark == 3 { cj_out(" [TOMBSTONED IN DESIRED, STILL SCHEDULED HERE -- the reconciler adds and updates but NEVER removes, so withdrawing a declaration cannot stop a live beat]" as *u8) }
284 cj_out("\n" as *u8)
285 }
286 }
287 i = e + 1
288 }
289 let m: *u8 = sys_mmap(CJ_ROWCAP)
290 var mo: i64 = 0
291 mo = cj_cat(m, mo, " rows=" as *u8)
292 mo = cj_num(m, mo, rows)
293 mo = cj_cat(m, mo, " effective=" as *u8)
294 mo = cj_num(m, mo, eff)
295 mo = cj_cat(m, mo, " tombstones=" as *u8)
296 mo = cj_num(m, mo, tombrows)
297 mo = cj_cat(m, mo, " withdrawn=" as *u8)
298 mo = cj_num(m, mo, dead)
299 var sok: i64 = 0
300 if (eff + tombrows + dead) == rows { sok = 1 }
301 mo = cj_cat(m, mo, " sums=" as *u8)
302 mo = cj_num(m, mo, sok)
303 mo = cj_cat(m, mo, " (a partition is a claim: effective + tombstones + withdrawn must equal rows, and sums=1 is that claim CHECKED rather than assumed)\n" as *u8)
304 sys_write(1, m, mo)
305 return rows
306}
307func cj_dump(prefix: *u8, label: *u8, show: i64) -> i64 { return cj_dump2(prefix, 0 as *u8, label, show) }
308// A DUPLICATE NAME IS NOT COSMETIC: the plane is DESIRED STATE and last declaration wins, so an earlier
309// row with the same name is dead weight the reconciler drops -- and nothing could see that until now.
310func cj_dupes(prefix: *u8) -> i64 {
311 let lenp: *i64 = sys_mmap(16) as *i64
312 lenp[0] = 0
313 let buf: *u8 = sts_load_fit(prefix, lenp)
314 let n: i64 = lenp[0]
315 if (buf as i64) == 0 { cj_out("DUPES UNREADABLE -- the plane could not be loaded (NOT the same as none)\n" as *u8); return 0 - 1 }
316 if n <= 0 { cj_out("DUPES none -- the plane is EMPTY/unseeded (NOT the same as no duplicates)\n" as *u8); return 0 }
317 let na: *u8 = sys_mmap(CJ_NAMECAP)
318 let nb: *u8 = sys_mmap(CJ_NAMECAP)
319 // COUNT WHAT THE NAME SAYS. The first cut of this counted PAIRINGS -- a name appearing k times
320 // contributed k(k-1)/2 -- and printed it as `DUPES total=`, so 20 duplicated names read as 82
321 // redundant rows. ★★★★★A COUNT WHOSE UNIT IS NOT ITS OBVIOUS UNIT SENDS THE NEXT READER ON THE
322 // WRONG SWEEP WITH YOUR NUMBERS APPARENTLY BACKING THEM. Now it scans BACKWARD and reports the two
323 // figures a reader actually acts on: how many NAMES are duplicated, and how many ROWS could be
324 // dropped without changing desired state.
325 let nc: *u8 = sys_mmap(CJ_NAMECAP)
326 let tp: *i64 = sys_mmap(16) as *i64
327 var surplus: i64 = 0
328 var dupnames: i64 = 0
329 var tombrows: i64 = 0
330 var withdrawn: i64 = 0
331 var i: i64 = 0
332 while i < n {
333 let e: i64 = cj_eol(buf, n, i)
334 if e > i {
335 cj_rowname(buf, i, e, na, CJ_NAMECAP)
336 // A TOMBSTONE ROW IS NOT A DECLARATION AND A WITHDRAWN ROW IS NOT A DUPLICATE OF A LIVE ONE.
337 // Counting either would inflate the worklist with rows whose remedy is already applied --
338 // the same wrong-unit defect that made this counter read 83 when the answer was 40.
339 if cj_streq(na, CJ_TOMB) == 1 { tombrows = tombrows + 1 } else {
340 cj_scan(buf, n, na, nb, nc, tp)
341 if tp[0] > i { withdrawn = withdrawn + 1 } else {
342 var fl: i64 = 0
343 if tp[0] > 0 { fl = tp[0] }
344 var earlier: i64 = 0
345 var j: i64 = fl
346 while j < i {
347 let e2: i64 = cj_eol(buf, n, j)
348 if e2 > j {
349 cj_rowname(buf, j, e2, nb, CJ_NAMECAP)
350 if cj_streq(na, nb) == 1 { earlier = earlier + 1 }
351 }
352 j = e2 + 1
353 }
354 if earlier > 0 {
355 surplus = surplus + 1
356 if earlier == 1 {
357 dupnames = dupnames + 1
358 cj_out(" DUPLICATE name=" as *u8); cj_out(na)
359 cj_out(" -- last declaration wins, so every earlier row of this name is dead weight the reconciler drops. COLLAPSE IT WITH: nx_clockjob drop <name> ; nx_clockjob put <name> <interval> <organ>\n" as *u8)
360 }
361 }
362 }
363 }
364 }
365 i = e + 1
366 }
367 let m: *u8 = sys_mmap(CJ_ROWCAP)
368 var mo: i64 = 0
369 mo = cj_cat(m, mo, "DUPES duplicated_names=" as *u8)
370 mo = cj_num(m, mo, dupnames)
371 mo = cj_cat(m, mo, " surplus_rows=" as *u8)
372 mo = cj_num(m, mo, surplus)
373 mo = cj_cat(m, mo, " tombstone_rows=" as *u8)
374 mo = cj_num(m, mo, tombrows)
375 mo = cj_cat(m, mo, " withdrawn_rows=" as *u8)
376 mo = cj_num(m, mo, withdrawn)
377 mo = cj_cat(m, mo, " (surplus_rows = rows droppable without changing desired state; NOT pairings. tombstone_rows and withdrawn_rows are EXCLUDED from both figures, because a retired declaration is not outstanding work)\n" as *u8)
378 sys_write(1, m, mo)
379 return surplus
380}
381
382// THE ONE-CALL ANSWER TO "IS MY BEAT DECLARED, AND IS IT ACTUALLY RUNNING?" -- the question whose
383// absence cost this lane a false claim. Reads BOTH planes for ONE name and labels each, so DECLARED and
384// LIVE are read in a single breath instead of inferred by eye from two separate dumps.
385// ★hits=0 prints ABSENT-from-this-plane, and that is kept distinct from UNREADABLE and from EMPTY:
386// "I could not look", "nothing is here" and "this name is not here" are three different answers.
387func cj_get(prefix: *u8, label: *u8, want: *u8) -> i64 {
388 let lenp: *i64 = sys_mmap(16) as *i64
389 lenp[0] = 0
390 let buf: *u8 = sts_load_fit(prefix, lenp)
391 let n: i64 = lenp[0]
392 cj_out(label)
393 if (buf as i64) == 0 { cj_out(" UNREADABLE -- the plane could not be loaded. THIS IS NOT THE SAME AS ABSENT.\n" as *u8); return 0 - 1 }
394 if n <= 0 { cj_out(" EMPTY/unseeded -- nothing was ever committed here, which is NOT the same as this name being absent.\n" as *u8); return 0 - 3 }
395 let nm: *u8 = sys_mmap(CJ_NAMECAP)
396 let ga: *u8 = sys_mmap(CJ_NAMECAP)
397 let gb: *u8 = sys_mmap(CJ_NAMECAP)
398 let tp: *i64 = sys_mmap(16) as *i64
399 // START AFTER THE LAST TOMBSTONE FOR THIS NAME -- rows before it are retired history, not state.
400 cj_scan(buf, n, want, ga, gb, tp)
401 var st: i64 = 0
402 if tp[0] > 0 { st = tp[0] }
403 var hits: i64 = 0
404 var i: i64 = st
405 while i < n {
406 let e: i64 = cj_eol(buf, n, i)
407 if e > i {
408 cj_rowname(buf, i, e, nm, CJ_NAMECAP)
409 if cj_streq(nm, want) == 1 {
410 hits = hits + 1
411 cj_out(" " as *u8)
412 sys_write(1, ((buf as i64) + i) as *u8, e - i)
413 cj_out("\n" as *u8)
414 }
415 }
416 i = e + 1
417 }
418 let m: *u8 = sys_mmap(CJ_ROWCAP)
419 var mo: i64 = 0
420 mo = cj_cat(m, mo, " hits=" as *u8)
421 mo = cj_num(m, mo, hits)
422 // FOUR ANSWERS, NEVER THREE. UNREADABLE (-1) I could not look; EMPTY (-3) nothing was ever committed
423 // here; TOMBSTONED (-2) it WAS declared and the declaration was WITHDRAWN; ABSENT (0) this name was
424 // never declared at all. Collapsing the last two would erase the whole point of `drop`: "retired" and
425 // "never existed" demand different decisions from whoever reads it.
426 if hits == 0 {
427 if tp[0] >= 0 { mo = cj_cat(m, mo, " TOMBSTONED -- declared once and then WITHDRAWN by a drop. NOT the same as ABSENT, and NOT the same as the plane being EMPTY." as *u8) } else { mo = cj_cat(m, mo, " ABSENT from this plane" as *u8) }
428 }
429 mo = cj_cat(m, mo, "\n" as *u8)
430 sys_write(1, m, mo)
431 if hits == 0 { if tp[0] >= 0 { return 0 - 2 } }
432 return hits
433}
434
435func main(argc: i64, argv: *i64) -> i64 {
436 if argc < 2 { cj_err("usage: nx_clockjob put <name> <interval-secs> <organ> | drop <name> | get <name> | list | live | dupes\n" as *u8); sys_exit(2); return 2 }
437 let verb: *u8 = argv[1] as *u8
438 // READ VERBS FIRST -- they need no further argv, and they are the reason this organ stopped being
439 // write-only. Each NAMES its plane so DECLARED and LIVE can never be read as the same thing.
440 if cj_streq(verb, "list" as *u8) == 1 {
441 cj_dump(CJ_PREFIX, "DESIRED plane knowledge/store/clockjobs- -- what was DECLARED. The scheduler folds this into the live plane at window-init (~28-30 min), so a row here is NOT yet proof that anything runs.\n" as *u8, 1)
442 sys_exit(0); return 0
443 }
444 if cj_streq(verb, "live" as *u8) == 1 {
445 // CROSS-READS DESIRED FOR TOMBSTONES. A withdrawal can never appear in this plane, so without the
446 // cross-read a job whose declaration was dropped would list here indistinguishable from one nobody
447 // ever touched -- and that is precisely the state an operator most needs to see.
448 cj_dump2(CJ_LIVE_PREFIX, CJ_PREFIX, "LIVE plane knowledge/store/clocksched- -- what the scheduler ACTUALLY RUNS. A row here runs; a row present only in DESIRED has not been merged yet.\n" as *u8, 1)
449 sys_exit(0); return 0
450 }
451 if cj_streq(verb, "get" as *u8) == 1 {
452 if argc < 3 { cj_err("usage: nx_clockjob get <name> -- shows the name in BOTH planes\n" as *u8); sys_exit(2); return 2 }
453 let want: *u8 = argv[2] as *u8
454 let d: i64 = cj_get(CJ_PREFIX, "DESIRED plane knowledge/store/clockjobs- -- what was DECLARED:\n" as *u8, want)
455 let l: i64 = cj_get(CJ_LIVE_PREFIX, "LIVE plane knowledge/store/clocksched- -- what the scheduler ACTUALLY RUNS:\n" as *u8, want)
456 if d > 0 {
457 if l > 0 { cj_out("VERDICT DECLARED=YES LIVE=YES -- it is running\n" as *u8) } else { cj_out("VERDICT DECLARED=YES LIVE=NO -- declared but NOT yet merged; the scheduler folds desired into live at window-init (~28-30 min), so this is legitimately pending, not a failure\n" as *u8) }
458 } else {
459 if d == 0 - 2 {
460 if l > 0 { cj_out("VERDICT DECLARED=TOMBSTONED LIVE=YES -- the declaration was WITHDRAWN and the job is STILL SCHEDULED. That is not a contradiction and not a defect: the reconciler ADDS and UPDATES but NEVER REMOVES, so a tombstone retracts a declaration and cannot stop a live beat.\n" as *u8) } else { cj_out("VERDICT DECLARED=TOMBSTONED LIVE=NO -- withdrawn from desired state, and not running\n" as *u8) }
461 } else {
462 if l > 0 { cj_out("VERDICT DECLARED=NO LIVE=YES -- running WITHOUT a desired-state row; a reconcile could drop it\n" as *u8) } else { cj_out("VERDICT DECLARED=NO LIVE=NO -- this name is in neither plane\n" as *u8) }
463 }
464 }
465 sys_exit(0); return 0
466 }
467 if cj_streq(verb, "dupes" as *u8) == 1 {
468 let d: i64 = cj_dupes(CJ_PREFIX)
469 if d > 0 { sys_exit(1); return 1 }
470 sys_exit(0); return 0
471 }
472 // DROP = APPEND A TOMBSTONE. It never rewrites, never compacts, and never touches a segment another
473 // lane may be appending to at this very instant -- which is the only reason it is safe to ship while
474 // nine lanes and a live scheduler are reading this plane.
475 if cj_streq(verb, "drop" as *u8) == 1 {
476 if argc < 3 { cj_err("usage: nx_clockjob drop <name> -- appends a TOMBSTONE withdrawing every declaration of <name> from the desired plane. It does NOT stop a job that is already live.\n" as *u8); sys_exit(2); return 2 }
477 let want: *u8 = argv[2] as *u8
478 if cj_len(want) == 0 { cj_err("nx_clockjob: REFUSED empty name -- a tombstone with no target withdraws nothing while reading as though it did\n" as *u8); sys_exit(1); return 1 }
479 if cj_tabfree(want) == 0 { cj_err("nx_clockjob: REFUSED tab inside name -- it would split into phantom columns and the tombstone would target something other than what you asked for\n" as *u8); sys_exit(1); return 1 }
480 if cj_streq(want, CJ_TOMB) == 1 { cj_err("nx_clockjob: REFUSED __TOMB__ is the reserved tombstone marker, not a job name -- tombstoning the marker would make every tombstone in the plane unreadable\n" as *u8); sys_exit(1); return 1 }
481 let dlp: *i64 = sys_mmap(16) as *i64
482 dlp[0] = 0
483 let dbuf: *u8 = sts_load_fit(CJ_PREFIX, dlp)
484 let dn: i64 = dlp[0]
485 if (dbuf as i64) == 0 { cj_err("nx_clockjob: REFUSED the desired plane is UNREADABLE -- I will not record a withdrawal against a plane I could not read. COULD NOT LOOK is not NOTHING THERE.\n" as *u8); sys_exit(1); return 1 }
486 let da: *u8 = sys_mmap(CJ_NAMECAP)
487 let db: *u8 = sys_mmap(CJ_NAMECAP)
488 let dtp: *i64 = sys_mmap(16) as *i64
489 let eff: i64 = cj_scan(dbuf, dn, want, da, db, dtp)
490 // THE NEGATIVE CONTROL LIVES IN THE ORGAN, NOT ONLY IN THE GATE. A tombstone for a name that was
491 // never declared is a recorded removal that never happened, and every reader downstream would
492 // then report that name as WITHDRAWN rather than ABSENT -- a manufactured fact with a receipt.
493 if eff == 0 {
494 if dtp[0] >= 0 {
495 cj_out("CLOCKJOB-DROP ALREADY-TOMBSTONED name=" as *u8); cj_out(want)
496 cj_out(" -- a tombstone for this name is already the last word in the plane; a second one would add a row and change nothing. NO WRITE PERFORMED (this verb is idempotent).\n" as *u8)
497 sys_exit(0); return 0
498 }
499 cj_err("nx_clockjob: REFUSED nothing to drop -- this name has NO surviving declaration in knowledge/store/clockjobs-. Writing a tombstone for a name that was never declared records a removal that never happened, and every reader would then report it as WITHDRAWN instead of ABSENT.\n" as *u8)
500 sys_exit(1); return 1
501 }
502 let trow: *u8 = sys_mmap(CJ_ROWCAP)
503 var to: i64 = 0
504 to = cj_cat(trow, to, CJ_TOMB); trow[to] = CJ_TAB as u8; to = to + 1
505 to = cj_cat(trow, to, want)
506 if to >= CJ_ROWCAP - 2 { cj_err("nx_clockjob: REFUSED tombstone row exceeds CJ_ROWCAP rather than truncating it into a row that would target the wrong name\n" as *u8); sys_exit(1); return 1 }
507 let trc: i64 = sts_append_fast_locked(CJ_PREFIX, trow, to)
508 if trc < 0 { cj_err("nx_clockjob: FAIL commit error appending the tombstone -- nothing was withdrawn\n" as *u8); sys_exit(1); return 1 }
509 let tm: *u8 = sys_mmap(CJ_ROWCAP)
510 var tmo: i64 = 0
511 tmo = cj_cat(tm, tmo, "CLOCKJOB-DROP ok name=" as *u8)
512 tmo = cj_cat(tm, tmo, want)
513 tmo = cj_cat(tm, tmo, " withdrew_declarations=" as *u8)
514 tmo = cj_num(tm, tmo, eff)
515 tmo = cj_cat(tm, tmo, " rows=" as *u8)
516 tmo = cj_num(tm, tmo, trc)
517 tmo = cj_cat(tm, tmo, " (TOMBSTONE APPENDED; nothing rewritten, history intact -- two fields only, which the reconciler skips)\n" as *u8)
518 sys_write(1, tm, tmo)
519 let lv: i64 = cj_get(CJ_LIVE_PREFIX, "LIVE plane knowledge/store/clocksched- -- is it still scheduled?\n" as *u8, want)
520 if lv > 0 { cj_out("STILL SCHEDULED -- the declaration is withdrawn but the beat KEEPS RUNNING, because the reconciler adds and updates and never removes. DO NOT READ THIS DROP AS A STOP.\n" as *u8) } else { cj_out("NOT SCHEDULED -- absent from the live plane as well.\n" as *u8) }
521 cj_out("verdict=DONE\n" as *u8)
522 sys_exit(0); return 0
523 }
524 if argc < 5 { cj_err("usage: nx_clockjob put <name> <interval-secs> <organ> | drop <name> | get <name> | list | live | dupes\n" as *u8); sys_exit(2); return 2 }
525 if verb[0] != (112 as u8) { cj_err("nx_clockjob: verbs are put | drop | get | list | live | dupes\n" as *u8); sys_exit(2); return 2 }
526 let name: *u8 = argv[2] as *u8
527 let ivs: *u8 = argv[3] as *u8
528 let org: *u8 = argv[4] as *u8
529 // BOUNDARY VALIDATION -- this is the whole point of the organ. The dispatcher can only SKIP a
530 // malformed row after the fact; refusing it here means it never reaches the plane.
531 if cj_len(name) == 0 { cj_err("nx_clockjob: REFUSED empty name (this is the live q:7 defect -- a nameless row is an unschedulable job)\n" as *u8); sys_exit(1); return 1 }
532 if cj_len(org) == 0 { cj_err("nx_clockjob: REFUSED empty organ (an organless row is an exec of nothing)\n" as *u8); sys_exit(1); return 1 }
533 if cj_tabfree(name) == 0 { cj_err("nx_clockjob: REFUSED tab inside name -- it would split into phantom columns\n" as *u8); sys_exit(1); return 1 }
534 if cj_tabfree(org) == 0 { cj_err("nx_clockjob: REFUSED tab inside organ -- it would split into phantom columns\n" as *u8); sys_exit(1); return 1 }
535 let iv: i64 = cj_atoi(ivs)
536 if cj_resolves(org) == 0 {
537 cj_err("nx_clockjob: REFUSED organ does not resolve to an existing file -- a row whose organ does not exist is a beat that is dead the moment it is written, and it dies SILENTLY. Give a full path, or a name that exists under the estate root (usually nx_NAME.elf -- the extension is NOT optional).\n" as *u8)
538 sys_exit(1)
539 return 1
540 }
541 if iv < 1 { cj_err("nx_clockjob: REFUSED interval must be a positive integer of seconds\n" as *u8); sys_exit(1); return 1 }
542 let row: *u8 = sys_mmap(CJ_ROWCAP)
543 var o: i64 = 0
544 o = cj_cat(row, o, name); row[o]=CJ_TAB as u8; o=o+1
545 o = cj_num(row, o, iv); row[o]=CJ_TAB as u8; o=o+1
546 o = cj_cat(row, o, org)
547 if o >= CJ_ROWCAP - 2 { cj_err("nx_clockjob: REFUSED row exceeds CJ_ROWCAP rather than truncating it into a malformed row\n" as *u8); sys_exit(1); return 1 }
548 let rc: i64 = sts_append_fast_locked(CJ_PREFIX, row, o)
549 if rc < 0 { cj_err("nx_clockjob: FAIL commit error\n" as *u8); sys_exit(1); return 1 }
550 let msg: *u8 = sys_mmap(CJ_ROWCAP)
551 var mo: i64 = 0
552 mo = cj_cat(msg, mo, "CLOCKJOB-PUT ok rows=" as *u8)
553 mo = cj_num(msg, mo, rc)
554 mo = cj_cat(msg, mo, " row=" as *u8)
555 mo = cj_cat(msg, mo, row)
556 mo = cj_cat(msg, mo, " (DESIRED STATE -- the clock reconciles at its next window; last declaration wins)\n" as *u8)
557 sys_write(1, msg, mo)
558 sys_exit(0)
559 return 0
560}