code wiki / _hdl_build / nx_cron_watch.nx
nx_cron_watch.nx source
↩ module page · 162 lines · 9411 B
1// nx_cron_watch.nx -- CRON/AUTOMATION FRESHNESS SUPERVISOR (operator: "prevent things like this...
2// supervised"; the next silent-failure class after API-contract drift + lib drift = a scheduled job that
3// SILENTLY STOPS FIRING, e.g. seq22 team_pulse died unnoticed). SOTA = the dead-man's-switch / heartbeat
4// model (Healthchecks.io "expected period + grace time", Cronitor, Dead Man's Snitch): a job pings on its
5// schedule; a check-in missed beyond a grace window -> STALE/alert. Sovereign twist: the heartbeat is each
6// job's own evidence-log `ts=<epoch>` (no external SaaS; evidence-layer native). Data-driven (rule 11):
7// knowledge/registry/cron_heartbeats.tsv rows name<TAB>heartbeat-file<TAB>ts-marker<TAB>max-age-secs<TAB>remediation.
8// max-age = expected-interval + grace. age = now - ts. age>max-age -> STALE. missing ts -> STALE (never fired).
9// nx_cron_watch check [manifest] (JSON: per watch fresh/stale + age + verdict GREEN/STALE-DETECTED)
10// license_tier: ORIGINAL No hw writes (Rule 26). expect_exit: 0
11import "nx_syscalls.nx"
12
13const CW_CAP: i64 = 262144
14const CW_TAB: i64 = 9
15const CW_NL: i64 = 10
16const CW_HASH: i64 = 35
17const CW_STDERR: i64 = 2
18const CW_SPAN: i64 = 16
19const CW_EXIT_USAGE: i64 = 2
20
21func cw_werr(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(CW_STDERR, s, n); return 0 }
22func cw_vlen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n }
23func cw_cat(d: *u8, o: i64, s: *u8) -> i64 { var i: i64 = 0; while s[i] != (0 as u8) { d[o] = s[i]; o = o + 1; i = i + 1 } return o }
24func cw_catn(d: *u8, o: i64, v: i64) -> i64 { let t: *u8 = sys_mmap(28); var m: i64 = v; if m < 0 { d[o] = 45 as u8; o = o + 1; m = 0 - m } var k: i64 = 0; 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 } var i: i64 = 0; while i < k { d[o] = t[k-1-i]; o = o + 1; i = i + 1 } return o }
25func cw_cat_esc(d: *u8, o: i64, q: *u8, s: i64, e: i64) -> i64 { var i: i64 = s; while i < e { var c: i64 = q[i] as i64; if c == 34 { c = 39 } if c == 92 { c = 47 } if c < 32 { c = 32 } d[o] = c as u8; o = o + 1; i = i + 1 } return o }
26// TAIL-READ (2026-08-02, debt 1785708737). This used to read from byte 0 up to `cap`, so once an
27// append-only stamp log grew past CW_CAP the watch only ever saw the HEAD of the file -- and computed
28// "latest" from the OLDEST bytes it could afford. MEASURED: surfsentinel-beat reported last_ts=1784445800
29// (~14.6 DAYS stale) while the log's real tail showed a COMPLETED run 23 minutes earlier. The guard aged
30// BACKWARDS as its subject got healthier, and screamed louder the longer the job ran correctly.
31// A FRESHNESS CHECK MUST READ THE TAIL. Seek to max(0, size-cap) so the newest evidence is always in the
32// window; a file smaller than cap is read whole exactly as before.
33func cw_read(path: *u8, buf: *u8, cap: i64) -> i64 {
34 let fd: i64 = sys_openat_rd(path)
35 if fd < 0 { return 0 - 1 }
36 let size: i64 = sys_lseek(fd, 0, 2)
37 if size > cap { sys_lseek(fd, size - cap, 0) } else { sys_lseek(fd, 0, 0) }
38 var n: i64 = 0
39 var go: i64 = 1
40 while go == 1 { let r: i64 = sys_read(fd, ((buf as i64) + n) as *u8, cap - n); if r <= 0 { go = 0 } else { n = n + r } if n >= cap { go = 0 } }
41 sys_close(fd)
42 return n
43}
44// find <marker> substring, then parse the decimal number immediately following it. -1 if not found.
45func cw_num_after(buf: *u8, n: i64, marker: *u8) -> i64 {
46 var ml: i64 = 0
47 while marker[ml] != (0 as u8) { ml = ml + 1 }
48 if ml == 0 { return 0 - 1 }
49 var last: i64 = 0 - 1
50 var found: i64 = 0
51 var i: i64 = 0
52 while i + ml <= n {
53 var j: i64 = 0
54 var ok: i64 = 1
55 while j < ml { if buf[i+j] != marker[j] { ok = 0; j = ml } else { j = j + 1 } }
56 if ok == 1 {
57 var p: i64 = i + ml
58 var v: i64 = 0
59 var got: i64 = 0
60 var go: i64 = 1
61 while go == 1 { if p >= n { go = 0 } else { let c: i64 = buf[p] as i64; if c >= 48 { if c <= 57 { v = v * 10 + (c - 48); got = 1; p = p + 1 } else { go = 0 } } else { go = 0 } } }
62 // LAST MATCH WINS (2026-08-02, debt 1785708737 -- the twin of the tail-read above). This used to
63 // `return v` on the FIRST marker found, which is correct only for a single-line stamp file
64 // (`date > log`). Every APPEND-only evidence log (`>>`) puts the FRESHEST stamp LAST, so the
65 // first match is the OLDEST run -- the watch then reports a healthy hourly job as weeks stale.
66 // That is why the single-line watches (planeguard/guardcheck) read correctly while the appending
67 // ones (surfsentinel) did not: first==last only when there is one line.
68 if got == 1 { last = v; found = 1 }
69 }
70 i = i + 1
71 }
72 if found == 1 { return last }
73 return 0 - 1
74}
75func cw_le(q: *u8, i: i64, n: i64) -> i64 { var e: i64 = i; var s: i64 = 1; while s == 1 { if e >= n { s = 0 } else { if q[e] == (CW_NL as u8) { s = 0 } else { e = e + 1 } } } return e }
76func cw_col(q: *u8, ls: i64, le: i64, c: i64, out: *i64) -> i64 {
77 var col: i64 = 0
78 var p: i64 = ls
79 while col < c {
80 var s: i64 = 1
81 while s == 1 { if p >= le { return 0 } if q[p] == (CW_TAB as u8) { s = 0 } else { p = p + 1 } }
82 p = p + 1
83 col = col + 1
84 }
85 var e: i64 = p
86 var s2: i64 = 1
87 while s2 == 1 { if e >= le { s2 = 0 } else { if q[e] == (CW_TAB as u8) { s2 = 0 } else { e = e + 1 } } }
88 out[0] = p
89 out[1] = e
90 return 1
91}
92func cw_cstr(q: *u8, s: i64, e: i64, dst: *u8) -> i64 { var i: i64 = 0; while s + i < e { dst[i] = q[s+i]; i = i + 1 } dst[i] = 0 as u8; return i }
93func cw_atoi_span(q: *u8, s: i64, e: i64) -> i64 { var v: i64 = 0; var i: i64 = s; while i < e { let c: i64 = q[i] as i64; if c >= 48 { if c <= 57 { v = v * 10 + (c - 48) } } i = i + 1 } return v }
94func main(argc: i64, argv: *i64) -> i64 {
95 // single verb (check); argv[1] optionally overrides the manifest path if it looks like a path
96 var mpath: *u8 = "knowledge/registry/cron_heartbeats.tsv" as *u8
97 if argc > 2 { mpath = argv[2] as *u8 }
98 let man: *u8 = sys_mmap(CW_CAP)
99 let mn: i64 = cw_read(mpath, man, CW_CAP)
100 if mn <= 0 { cw_werr("CW-FAIL manifest empty or unreadable\n" as *u8); sys_exit(1); return 1 }
101 let now: i64 = sys_now_realtime_sec()
102 let fbuf: *u8 = sys_mmap(CW_CAP)
103 let fpath: *u8 = sys_mmap(512)
104 let tmark: *u8 = sys_mmap(64)
105 let c0: *i64 = sys_mmap(CW_SPAN) as *i64
106 let c1: *i64 = sys_mmap(CW_SPAN) as *i64
107 let c2: *i64 = sys_mmap(CW_SPAN) as *i64
108 let c3: *i64 = sys_mmap(CW_SPAN) as *i64
109 let c4: *i64 = sys_mmap(CW_SPAN) as *i64
110 let out: *u8 = sys_mmap(CW_CAP)
111 var o: i64 = 0
112 o = cw_cat(out, o, "{\"verb\":\"check\",\"epoch\":" as *u8)
113 o = cw_catn(out, o, now)
114 o = cw_cat(out, o, ",\"watches\":[" as *u8)
115 var total: i64 = 0
116 var stale: i64 = 0
117 var emitted: i64 = 0
118 var i: i64 = 0
119 while i < mn {
120 let le: i64 = cw_le(man, i, mn)
121 if le > i { if (man[i] as i64) != CW_HASH {
122 if cw_col(man, i, le, 0, c0) == 1 { if cw_col(man, i, le, 1, c1) == 1 { if cw_col(man, i, le, 2, c2) == 1 { if cw_col(man, i, le, 3, c3) == 1 {
123 cw_cstr(man, c1[0], c1[1], fpath)
124 cw_cstr(man, c2[0], c2[1], tmark)
125 let maxage: i64 = cw_atoi_span(man, c3[0], c3[1])
126 total = total + 1
127 let fn: i64 = cw_read(fpath, fbuf, CW_CAP)
128 var ts: i64 = 0 - 1
129 if fn > 0 { ts = cw_num_after(fbuf, fn, tmark) }
130 var age: i64 = 0 - 1
131 var fresh: i64 = 0
132 if ts >= 0 { age = now - ts; if age < 0 { age = 0 } if age <= maxage { fresh = 1 } }
133 if fresh == 0 { stale = stale + 1 }
134 if emitted > 0 { o = cw_cat(out, o, "," as *u8) }
135 o = cw_cat(out, o, "{\"name\":\"" as *u8)
136 o = cw_cat_esc(out, o, man, c0[0], c0[1])
137 o = cw_cat(out, o, "\",\"last_ts\":" as *u8)
138 o = cw_catn(out, o, ts)
139 o = cw_cat(out, o, ",\"age_secs\":" as *u8)
140 o = cw_catn(out, o, age)
141 o = cw_cat(out, o, ",\"max_age\":" as *u8)
142 o = cw_catn(out, o, maxage)
143 o = cw_cat(out, o, ",\"fresh\":" as *u8)
144 o = cw_catn(out, o, fresh)
145 if fresh == 0 { if cw_col(man, i, le, 4, c4) == 1 { o = cw_cat(out, o, ",\"remediation\":\"" as *u8); o = cw_cat_esc(out, o, man, c4[0], c4[1]); o = cw_cat(out, o, "\"" as *u8) } }
146 o = cw_cat(out, o, "}" as *u8)
147 emitted = emitted + 1
148 } } } }
149 } }
150 i = le + 1
151 }
152 o = cw_cat(out, o, "],\"total\":" as *u8)
153 o = cw_catn(out, o, total)
154 o = cw_cat(out, o, ",\"stale\":" as *u8)
155 o = cw_catn(out, o, stale)
156 o = cw_cat(out, o, ",\"verdict\":\"" as *u8)
157 if stale == 0 { o = cw_cat(out, o, "GREEN" as *u8) } else { o = cw_cat(out, o, "STALE-DETECTED" as *u8) }
158 o = cw_cat(out, o, "\",\"envelope\":\"manifest + each heartbeat/stamp file read bounded per CW_CAP; a job whose evidence exceeds the cap reads TRUNCATED and could be misjudged stale/fresh -- declared per the scale-law (F846), never silent\"}\n" as *u8)
159 sys_write(1, out, o)
160 sys_exit(0)
161 return 0
162}