code wiki / (root) / nx_clockjob.nx

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}