code wiki / _hdl_build / nx_cms_schedule_gate.nx

nx_cms_schedule_gate.nx source

↩ module page · 171 lines · 9454 B

1// nx_cms_schedule_gate.nx -- CMS SCHEDULED-PUBLISHING gate (W-RE-CRON-001): land the missing class 2// HONESTLY by proving a REAL schedule->publish over the NOW-WIRED nx_cron dispatch core (job-store + 3// tick fires due jobs) driving a real nx_cms_store status transition. NOT the validation-half alone 4// (no thin-gate wave): the post genuinely goes scheduled -> published, exactly at its fire time, 5// exactly once. Appends "CMSGATE row=nx_cms_schedule scheduled-publish ... verdict=PASS" to 6// knowledge/status/cms_gate.log ONLY if every assertion holds (no fake-green). Exit 0 iff all pass. 7// license_tier: ORIGINAL 8import "nx_cron.nx" 9import "nx_cms_store.nx" 10import "nx_syscalls.nx" 11import "nx_gate_verdict.nx" 12 13const SG_T_FUTURE: i64 = 2000000000 // fixed absolute unix (year ~2033) -- deterministic, no wallclock 14const SG_T_PAST: i64 = 1000000000 // fixed absolute unix (year ~2001) -- a missed/past-due fire 15 16func sg_w(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 } 17func sg_len(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} return n } 18func sg_num(v: i64) -> i64 { let bb: *u8=sys_mmap(28); var m: i64=v; if m<0{m=0-m}; let t: *u8=sys_mmap(28); var k: i64=0; if m==0{t[0]=48;k=1}; while m>0{t[k]=48+(m%10);m=m/10;k=k+1}; var i: i64=0; while i<k{bb[i]=t[k-1-i];i=i+1}; sys_write(1,bb,k); return 0 } 19 20func sg_cat(dst: *u8, off: i64, s: *u8) -> i64 { var o: i64=off; var k: i64=0; while s[k]!=(0 as u8){dst[o]=s[k];o=o+1;k=k+1} return o } 21func sg_catnum(dst: *u8, off: i64, v: i64) -> i64 { 22 var o: i64=off; let t: *u8=sys_mmap(28); var m: i64=v; if m<0{m=0-m}; var k: i64=0 23 if m==0 {t[0]=48 as u8;k=1}; while m>0 {t[k]=(48+(m%10)) as u8; m=m/10; k=k+1} 24 var i: i64=0; while i<k {dst[o]=t[k-1-i]; o=o+1; i=i+1} return o 25} 26 27// compare exactly n bytes of buf to a NUL-terminated literal (literal must be exactly n long) 28func sg_streq(buf: *u8, n: i64, lit: *u8) -> i64 { 29 var i: i64=0 30 while i<n { if (buf[i] as i64) != (lit[i] as i64) { return 0 } i=i+1 } 31 if (lit[n] as i64) != 0 { return 0 } 32 return 1 33} 34 35func sg_cpy(dst: *u8, src: *u8) -> i64 { var k: i64=0; while src[k]!=(0 as u8){dst[k]=src[k];k=k+1} return k } 36 37func sg_row(id: i64, ok: i64, what: *u8) -> i64 { 38 sg_w("SCHEDROW " as *u8); sg_num(id); sg_w(" " as *u8) 39 if ok==1 { sg_w("PASS " as *u8) } else { sg_w("FAIL " as *u8) } 40 sg_w(what); sg_w("\n" as *u8) 41 return ok 42} 43 44// build a NxCronJob in fresh zeroed memory (mmap pages are zeroed -> unset fields are 0) 45func sg_job(name: *u8, kind: i64, fire_at: i64, interval: i64, action_arg: i64) -> *NxCronJob { 46 let jp: *NxCronJob = sys_mmap(256) as *NxCronJob 47 jp.job_name_ptr = name 48 jp.job_name_len = sg_len(name) 49 jp.schedule_kind = kind 50 jp.interval_seconds = interval 51 jp.fire_at_unix = fire_at 52 jp.missed_policy = NX_CRON_MISSED_CATCH_UP_ONCE 53 jp.action_kind = NX_CRON_ACTION_PUBLISH 54 jp.action_arg = action_arg 55 jp.enabled = 1 56 return jp 57} 58 59// current @status value of the post store; copies into out, returns length 60func sg_status(post: *u8, pn: i64, out: *u8) -> i64 { 61 return cst_get(post, pn, "status" as *u8, out, 64) 62} 63 64func main() -> i64 { 65 var pass: i64 = 0 66 var rows: i64 = 0 67 var ok: i64 = 0 68 69 // ---- a real CMS post in the sovereign nx_cms_store record format ---- 70 let post: *u8 = sys_mmap(8192) 71 let post2: *u8 = sys_mmap(8192) 72 var pn: i64 = sg_cpy(post, "@title\nFall Sale Announcement\n@status\nscheduled\n@body\nBig autumn news.\n" as *u8) 73 let sbuf: *u8 = sys_mmap(128) 74 let dk: *i64 = sys_mmap(16) as *i64 75 let da: *i64 = sys_mmap(16) as *i64 76 77 // R0: register a scheduled publish -> JOB_REGISTERED (proves dispatch is WIRED, not FAIL_DEPENDENCY_MISSING) 78 let j1: *NxCronJob = sg_job("publish-post-1" as *u8, NX_CRON_KIND_AT_UNIX_ONCE, SG_T_FUTURE, 0, 1) 79 let v0: i64 = nx_cron_register_job(j1) 80 ok = 0; if v0 == NX_CRON_VERDICT_JOB_REGISTERED { ok = 1 } 81 rows=rows+1; pass=pass+sg_row(0, ok, "register scheduled publish -> JOB_REGISTERED (dispatch wired)" as *u8) 82 83 // R1: a malformed schedule (AT_UNIX_ONCE with no fire time) -> FAIL_BAD_SCHEDULE (negative control) 84 let jbad: *NxCronJob = sg_job("bad-sched" as *u8, NX_CRON_KIND_AT_UNIX_ONCE, 0, 0, 9) 85 let v1: i64 = nx_cron_register_job(jbad) 86 ok = 0; if v1 == NX_CRON_VERDICT_FAIL_BAD_SCHEDULE { ok = 1 } 87 rows=rows+1; pass=pass+sg_row(1, ok, "malformed schedule refused -> FAIL_BAD_SCHEDULE" as *u8) 88 89 // R2: duplicate name -> FAIL_DUPLICATE_NAME 90 let jdup: *NxCronJob = sg_job("publish-post-1" as *u8, NX_CRON_KIND_AT_UNIX_ONCE, SG_T_FUTURE, 0, 1) 91 let v2: i64 = nx_cron_register_job(jdup) 92 ok = 0; if v2 == NX_CRON_VERDICT_FAIL_DUPLICATE_NAME { ok = 1 } 93 rows=rows+1; pass=pass+sg_row(2, ok, "duplicate job name refused -> FAIL_DUPLICATE_NAME" as *u8) 94 95 // R3: tick BEFORE the fire time -> nothing fires, post still scheduled, public NOT showing it 96 let f3: i64 = nx_cron_tick(SG_T_FUTURE - 100) 97 let drained3: i64 = nx_cron_drain_fired(dk, da) 98 let sl3: i64 = sg_status(post, pn, sbuf) 99 ok = 0 100 if f3 == 0 { if drained3 == 0 { if sg_streq(sbuf, sl3, "scheduled" as *u8) == 1 { ok = 1 } } } 101 rows=rows+1; pass=pass+sg_row(3, ok, "tick before fire-time: 0 fired, post still SCHEDULED (not public)" as *u8) 102 103 // R4: tick AT the fire time -> job fires once, drains PUBLISH action for post 1, apply it -> published 104 let f4: i64 = nx_cron_tick(SG_T_FUTURE) 105 let drained4: i64 = nx_cron_drain_fired(dk, da) 106 ok = 0 107 if f4 == 1 { if drained4 == 1 { if dk[0] == NX_CRON_ACTION_PUBLISH { if da[0] == 1 { 108 // the dispatch fired -> the CMS performs the real publish state transition 109 let pn2: i64 = cst_set(post, pn, "status" as *u8, "published" as *u8, 9, post2, 8192) 110 pn = pn2 111 var ci: i64 = 0; while ci < pn { post[ci] = post2[ci]; ci = ci + 1 } 112 let sl4: i64 = sg_status(post, pn, sbuf) 113 if sg_streq(sbuf, sl4, "published" as *u8) == 1 { ok = 1 } 114 } } } } 115 rows=rows+1; pass=pass+sg_row(4, ok, "tick at fire-time: fired once -> PUBLISH action -> post PUBLISHED (live)" as *u8) 116 117 // R5: a later tick does NOT re-fire (one-shot = FIRE-ONCE; not re-published) 118 let f5: i64 = nx_cron_tick(SG_T_FUTURE + 500) 119 let fc5: i64 = nx_cron_job_fired_count("publish-post-1" as *u8, 14) 120 ok = 0; if f5 == 0 { if fc5 == 1 { ok = 1 } } 121 rows=rows+1; pass=pass+sg_row(5, ok, "later tick: 0 fired, fired_count stays 1 (idempotent fire-once)" as *u8) 122 123 // R6: missed/past-due one-shot fires exactly once on the next tick (catch-up-once) 124 let j2: *NxCronJob = sg_job("publish-post-2" as *u8, NX_CRON_KIND_AT_UNIX_ONCE, SG_T_PAST, 0, 2) 125 let v6: i64 = nx_cron_register_job(j2) 126 let f6: i64 = nx_cron_tick(SG_T_PAST + 10000) 127 let fc6: i64 = nx_cron_job_fired_count("publish-post-2" as *u8, 14) 128 ok = 0 129 if v6 == NX_CRON_VERDICT_JOB_REGISTERED { if f6 == 1 { if fc6 == 1 { ok = 1 } } } 130 rows=rows+1; pass=pass+sg_row(6, ok, "past-due publish fires once on next tick (missed catch-up-once)" as *u8) 131 132 // R7: ON_DEMAND fire_now is repeatable (manual publish trigger) 133 let j3: *NxCronJob = sg_job("manual-pub" as *u8, NX_CRON_KIND_ON_DEMAND, 0, 0, 3) 134 let v7: i64 = nx_cron_register_job(j3) 135 nx_cron_fire_now("manual-pub" as *u8, 10) 136 nx_cron_fire_now("manual-pub" as *u8, 10) 137 let fc7: i64 = nx_cron_job_fired_count("manual-pub" as *u8, 10) 138 ok = 0; if v7 == NX_CRON_VERDICT_JOB_REGISTERED { if fc7 == 2 { ok = 1 } } 139 rows=rows+1; pass=pass+sg_row(7, ok, "ON_DEMAND fire_now repeatable (fired_count=2)" as *u8) 140 141 let jobs: i64 = nx_cron_count_jobs() 142 143 sg_w("CMS-SCHEDULE-GATE rows=" as *u8); sg_num(rows); sg_w(" pass=" as *u8); sg_num(pass) 144 sg_w(" jobs=" as *u8); sg_num(jobs); sg_w("\n" as *u8) 145 146 // D001-MIGRATION 2026-08-01: this gate was GREEN and UNREADABLE -- it printed "verdict=PASS" 147 // while nx_swcompare_evidence parses the nx_gate_verdict vocabulary, so cms scored gates_green=0/2 148 // with BOTH gates passing. The per-row SCHEDROW detail above is already emitted by sg_row and is 149 // kept verbatim; this adds the canonical summary line the ruler can actually read. The counter is 150 // populated from the SAME rows/pass the teeth produced -- it re-states the measurement, it does not 151 // re-run or re-decide it, so the verdict cannot drift from the evidence above it. 152 let ctr: *i64 = gv_ctr() 153 ctr[0] = pass 154 ctr[1] = rows 155 let rc: i64 = gv_verdict("CMS-SCHEDULE-GATE" as *u8, ctr, "fire-once scheduled publish: idempotent, catches up missed, ON_DEMAND repeatable" as *u8) 156 157 if pass == rows { 158 let line: *u8 = sys_mmap(256) 159 var lo: i64 = sg_cat(line, 0, "CMSGATE row=nx_cms_schedule scheduled-publish jobs=" as *u8) 160 lo = sg_catnum(line, lo, jobs) 161 lo = sg_cat(line, lo, " rows=" as *u8); lo = sg_catnum(line, lo, rows) 162 lo = sg_cat(line, lo, " pass=" as *u8); lo = sg_catnum(line, lo, pass) 163 lo = sg_cat(line, lo, " verdict=PASS\n" as *u8) 164 let gf: i64 = sys_openat_append("knowledge/status/cms_gate.log" as *u8, 0x1a4) 165 if gf >= 0 { sys_write(gf, line, lo); sys_close(gf) } 166 sg_w("CMS-SCHEDULE-GATE scheduled-publish recorded in cms_gate.log\n" as *u8) 167 sys_exit(0); return 0 168 } 169 sg_w("CMS-SCHEDULE-GATE NOT recorded (no fake-green)\n" as *u8) 170 sys_exit(rc); return rc 171}