code wiki / _hdl_build / nx_ws_board.nx

nx_ws_board.nx source

↩ module page · 527 lines · 25893 B

1// nx_ws_board.nx -- WMS rung M2: the LIVE STATUS BOARD (derived view). 2// 3// module: nishi-core.wms.ws_board 4// capability: CORE_COMPUTE (a derived, self-validating view over WMS-R1 + WMS-R2) 5// 6// WHAT THIS CLOSES: crash recovery used to mean reading a stale THREADS worksheet 7// and a lossy MEMORY index by hand. This organ DERIVES the live state of every 8// empire/stream straight from the SSOT registry (WMS-R1, nx_workstream_store) and 9// validates freshness against the transition reflog (WMS-R2, nx_ws_ledger). 10// Crash-recovery becomes "read the board" -- and the board is NEVER silently wrong: 11// * if the registry is INCOMPLETE (a lost/never-committed segment) it REFUSES to 12// render (returns -1), it does not paper over the gap. (ws_audit_complete==0 13// is a precondition, in the DATA PATH, not just the test.) 14// * if the reflog overlay is TORN/OLD (replay flags any malformed line) it 15// REJECTS the reflog-derived view (returns -3) rather than rendering it wrong. 16// 17// IMPEDANCE NOTE (load-bearing, Rule 4 -- every displayed fact is a real field read): 18// R1 records store `state` as a STRING label (ACTIVE|DONE|BLOCKED|FLOATING|ABSENT). 19// R2's reflog stores `new=` as an INTEGER (0..4). The board's PRIMARY source of 20// truth for displayed state is the R1 string field (ws_field f=2). The R2 reflog 21// is used ONLY as an integrity/freshness OVERLAY (replay must yield flagged==0). 22// We do NOT re-map int->label here -- that int<->label bridge is a future rung; a 23// fabricated mapping would violate no-false-green. 24// 25// SCHEMA the board surfaces (Rule 4 -- closest VERIFIED facts, no invented columns): 26// field 1=empire 2=state 3=last_touched 5=code_link (owner/next-rung surrogate). 27// The registry has no dedicated owner/next_rung columns; code_link(5)+deps(6) are 28// the nearest measured facts. Inventing owner/next_rung would be a schema lie. 29// 30// REUSE / lineage: ws_manifest_p / ws_get_p / ws_field / ws_member / ws_audit_complete_p 31// (nx_workstream_store, WMS-R1); ledger_replay (nx_ws_ledger, WMS-R2); fa_appendz / 32// fa_cat / fa_catn (nx_framed_append, WMS-R0b -- the MANDATORY one-buffer-one-locked- 33// write discipline for the file channel). Additive: no organ modified. Sovereign: 34// imports only the WMS stack + nx_syscalls (no gcc/openssl/node). license_tier: ORIGINAL 35import "nx_workstream_store.nx" 36import "nx_itoa_lib.nx" // shared MSB-first emitter (zero-alloc) 37import "nx_ws_ledger.nx" 38import "nx_framed_append.nx" 39import "nx_heartbeat_str.nx" 40import "nx_syscalls.nx" 41 42const WB_LOG: *u8 = "knowledge/status/ws_board.log" 43const WB_RECCAP: i64 = 512 // bounded board-record size 44const WB_MAXEMP: i64 = 64 // max empires the board rolls up (12 today) 45const WB_MAXRW: i64 = 64 // reflog state[] capacity (ledger_replay maxws) 46// RETIRED: the old WB_MAXWS=512 stream cap. The board now COUNTS ws:ids and enumerates 47// EVERY registered stream from a single ss_open snapshot -- the registry SSOT sets the 48// size, never a magic number, and never a silent truncation (the bug this rung killed). 49 50// ---- liveness / freshness (Cardinal 11/17: data-driven, never baked) ------------------ 51// The board refuses to trust a stale ACTIVE label: liveness is recomputed EVERY render 52// from the HARDWARE clock (sys_now_realtime_sec) vs the stream's last activity = max of 53// registry last_touched and the live heartbeat channel, against a CONFIG window. So 54// "active" means beating NOW (fresh like energy), not a one-time stamp. 55const WB_HB_CHANNEL: *u8 = "knowledge/status/wms_heartbeat.log" // LIVE-1 beat channel 56const WB_CFG_FRESH_KEY: *u8 = "ws:cfg:fresh_window_sec" // operator override (DATA) 57const WB_FRESH_WINDOW_DEFAULT_SEC: i64 = 86400 // bootstrap FLOOR only (Cardinal 17 lowest tier) 58const WB_SW: i64 = 6 // i64 slots per named live-stream row 59const WB_ST_ACTIVE: i64 = 1 60const WB_ST_BLOCKED: i64 = 2 61 62// derived-view return codes (data-driven; no magic numbers). 63const WB_INCOMPLETE: i64 = 0 - 1 // registry audit failed -> board NOT rendered 64const WB_NOIDS: i64 = 0 - 2 // ws:ids absent -> nothing to render 65const WB_TORNREFLOG: i64 = 0 - 3 // reflog overlay torn/old -> view REJECTED 66 67// per-empire rollup row, packed into rows_out[] as 6 consecutive i64 slots: 68// [base+0]=empire-id ptr +1=total +2=done +3=active +4=blocked +5=other 69const WB_ROWW: i64 = 6 // i64 slots per empire row 70 71func wb_len(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n } 72 73// NUL-terminated byte-equality (state-label compare against the data-driven label set). 74func wb_streq(a: *u8, b: *u8) -> i64 { 75 var i: i64 = 0 76 while a[i] != (0 as u8) { 77 if a[i] != b[i] { return 0 } 78 i = i + 1 79 } 80 if b[i] != (0 as u8) { return 0 } 81 return 1 82} 83 84// decimal NUL-terminated string -> i64 (last_touched / config parse; ignores non-digits). 85func wb_atoi(s: *u8) -> i64 { 86 var v: i64 = 0 87 var i: i64 = 0 88 while s[i] != (0 as u8) { 89 let c: i64 = s[i] as i64 90 if c >= 48 { if c <= 57 { v = v * 10 + (c - 48) } } 91 i = i + 1 92 } 93 return v 94} 95 96// COUNT the registered ids straight from the ws:ids snapshot (the data-driven size that 97// replaces the retired 512 cap). Counts TAB-bounded non-empty tokens. ss_open snapshot. 98func wb_count_ids(prefix: *u8) -> i64 { 99 let h: *i64 = ss_open(prefix) 100 let pq: *i64 = sys_mmap(16) as *i64 101 let lq: *i64 = sys_mmap(16) as *i64 102 if ss_hget(h, "ws:ids" as *u8, pq, lq) != 1 { return 0 } 103 let ids: *u8 = pq[0] as *u8 104 let idn: i64 = lq[0] 105 var cnt: i64 = 0 106 var i: i64 = 0 107 while i < idn { 108 var atstart: i64 = 0 109 if i == 0 { atstart = 1 } 110 if i > 0 { if ids[i - 1] == (9 as u8) { atstart = 1 } } 111 if atstart == 1 { if ids[i] != (9 as u8) { cnt = cnt + 1 } } 112 i = i + 1 113 } 114 return cnt 115} 116 117// CONFIG read (Cardinal 17 hierarchy): the live freshness window is DATA in the store 118// (ws:cfg:fresh_window_sec). Returns the stored int, or `defval` (the bootstrap floor) 119// if the key is absent -- so the operative number lives in data, not buried in code. 120func wb_cfg_int(prefix: *u8, key: *u8, defval: i64) -> i64 { 121 let h: *i64 = ss_open(prefix) 122 let pq: *i64 = sys_mmap(16) as *i64 123 let lq: *i64 = sys_mmap(16) as *i64 124 if ss_hget(h, key, pq, lq) != 1 { return defval } 125 let b: *u8 = pq[0] as *u8 126 let n: i64 = lq[0] 127 if n <= 0 { return defval } 128 var v: i64 = 0 129 var i: i64 = 0 130 while i < n { 131 let c: i64 = b[i] as i64 132 if c >= 48 { if c <= 57 { v = v * 10 + (c - 48) } } 133 i = i + 1 134 } 135 return v 136} 137 138// THE DERIVED VIEW. Parameterized by prefix so the gate can drive a HERMETIC store. 139// `refpath` (or 0 to skip) is the WMS-R2 reflog used as a freshness/integrity overlay. 140// rows_out[] : caller-mmapped, >= WB_MAXEMP*WB_ROWW i64 slots (per-empire rollup). 141// empcount_out : set to the number of empires rolled up. 142// summary_out[]: [0]=total_streams [1]=total_done [2]=total_active [3]=total_blocked 143// [4]=reflog_flagged (0 when refpath==0 or clean). 144// Returns the empire count (>=0) on success, or a negative WB_* refusal code. 145func wb_board_p(prefix: *u8, refpath: *u8, 146 rows_out: *i64, cap: i64, 147 empcount_out: *i64, summary_out: *i64) -> i64 { 148 // ---- (1) TRUST GATE: registry must be COMPLETE before we trust any of it. ---- 149 let missing: *i64 = sys_mmap(8 * 128) as *i64 150 let mc: i64 = ws_audit_complete_p(prefix, missing, 128) 151 if mc < 0 { return WB_NOIDS } // ws:ids absent 152 if mc > 0 { return WB_INCOMPLETE } // a lost/never-committed segment -> refuse 153 154 // ---- (2) REFLOG INTEGRITY OVERLAY: a torn/old reflog REJECTS the view. ---- 155 var reflog_flagged: i64 = 0 156 if refpath != (0 as *u8) { 157 let rstate: *i64 = sys_mmap(8 * WB_MAXRW) as *i64 158 var ri: i64 = 0 159 while ri < WB_MAXRW { rstate[ri] = 0 - 1; ri = ri + 1 } 160 let routs: *i64 = sys_mmap(32) as *i64 161 ledger_replay(refpath, rstate, routs, WB_MAXRW) 162 reflog_flagged = routs[2] 163 if reflog_flagged > 0 { return WB_TORNREFLOG } 164 } 165 166 // ---- (3) snapshot the registry ONCE (ss_open) -> O(1) in-memory ss_hgets. 167 // SCALE FIX (the AUDIT-SCALE lesson + Cardinal 21): the old per-id ss_get re-read 168 // EVERY segment per lookup (~100KB mmap each) and starved the bump-mmap past ~1162 169 // ids. Mirror ws_audit_complete_p: open once, ss_hget thereafter. THIS is what makes 170 // retiring the 512 enumeration cap safe at full scale. ---- 171 let h: *i64 = ss_open(prefix) 172 let eq: *i64 = sys_mmap(16) as *i64 173 let el: *i64 = sys_mmap(16) as *i64 174 var emp: *u8 = "" as *u8 175 var empn: i64 = 0 176 if ss_hget(h, "ws:empires" as *u8, eq, el) == 1 { emp = eq[0] as *u8; empn = el[0] } 177 178 // build the empire-id list (NUL-term ptrs) by TAB-walking ws:empires, and zero its row. 179 var ecount: i64 = 0 180 var ei: i64 = 0 181 while ei < empn { 182 // start of a field iff ei==0 or previous byte is TAB 183 var atstart: i64 = 0 184 if ei == 0 { atstart = 1 } 185 if ei > 0 { if emp[ei - 1] == (9 as u8) { atstart = 1 } } 186 if atstart == 1 { 187 let nbuf: *u8 = sys_mmap(64) 188 var o: i64 = 0 189 var p: i64 = ei 190 var go: i64 = 1 191 while go == 1 { 192 if p >= empn { go = 0 } else { 193 if emp[p] == (9 as u8) { go = 0 } else { nbuf[o] = emp[p]; o = o + 1; p = p + 1 } 194 } 195 } 196 nbuf[o] = 0 as u8 197 if o > 0 { if ecount < WB_MAXEMP { 198 let base: i64 = ecount * WB_ROWW 199 rows_out[base + 0] = nbuf as i64 200 rows_out[base + 1] = 0 201 rows_out[base + 2] = 0 202 rows_out[base + 3] = 0 203 rows_out[base + 4] = 0 204 rows_out[base + 5] = 0 205 ecount = ecount + 1 206 } } 207 } 208 ei = ei + 1 209 } 210 211 // ---- (4) enumerate EVERY registered id with NO cap: walk the whole ws:ids string 212 // straight from the snapshot, ss_hget each record, tally per empire by real fields. ---- 213 let pq: *i64 = sys_mmap(16) as *i64 214 let lq: *i64 = sys_mmap(16) as *i64 215 var idstr: *u8 = "" as *u8 216 var idn: i64 = 0 217 if ss_hget(h, "ws:ids" as *u8, pq, lq) == 1 { idstr = pq[0] as *u8; idn = lq[0] } 218 let rq: *i64 = sys_mmap(16) as *i64 219 let rl: *i64 = sys_mmap(16) as *i64 220 let keybuf: *u8 = sys_mmap(160) 221 let empbuf: *u8 = sys_mmap(64) 222 let stbuf: *u8 = sys_mmap(64) 223 var t_total: i64 = 0 224 var t_done: i64 = 0 225 var t_active: i64 = 0 226 var t_blocked: i64 = 0 227 var ii: i64 = 0 228 while ii < idn { 229 let idbuf: *u8 = sys_mmap(160) 230 var io: i64 = 0 231 var igo: i64 = 1 232 while igo == 1 { 233 if ii >= idn { igo = 0 } else { 234 if idstr[ii] == (9 as u8) { ii = ii + 1; igo = 0 } else { idbuf[io] = idstr[ii]; io = io + 1; ii = ii + 1 } 235 } 236 } 237 idbuf[io] = 0 as u8 238 if io > 0 { 239 keybuf[0] = 119 as u8; keybuf[1] = 115 as u8; keybuf[2] = 58 as u8 240 var z: i64 = 0 241 while z < io { keybuf[3 + z] = idbuf[z]; z = z + 1 } 242 keybuf[3 + io] = 0 as u8 243 if ss_hget(h, keybuf, rq, rl) == 1 { 244 ws_field(rq[0] as *u8, rl[0], 1, empbuf) // empire 245 ws_field(rq[0] as *u8, rl[0], 2, stbuf) // state label 246 t_total = t_total + 1 247 var is_done: i64 = 0 248 var is_active: i64 = 0 249 var is_blocked: i64 = 0 250 if wb_streq(stbuf, "DONE" as *u8) == 1 { is_done = 1; t_done = t_done + 1 } 251 if wb_streq(stbuf, "ACTIVE" as *u8) == 1 { is_active = 1; t_active = t_active + 1 } 252 if wb_streq(stbuf, "BLOCKED" as *u8) == 1 { is_blocked = 1; t_blocked = t_blocked + 1 } 253 // find the empire's row and bump its tallies 254 var e: i64 = 0 255 while e < ecount { 256 let base: i64 = e * WB_ROWW 257 if wb_streq(rows_out[base + 0] as *u8, empbuf) == 1 { 258 rows_out[base + 1] = rows_out[base + 1] + 1 259 if is_done == 1 { rows_out[base + 2] = rows_out[base + 2] + 1 } 260 if is_active == 1 { rows_out[base + 3] = rows_out[base + 3] + 1 } 261 if is_blocked == 1 { rows_out[base + 4] = rows_out[base + 4] + 1 } 262 if is_done == 0 { if is_active == 0 { if is_blocked == 0 { 263 rows_out[base + 5] = rows_out[base + 5] + 1 264 } } } 265 e = ecount 266 } else { e = e + 1 } 267 } 268 } 269 } 270 } 271 272 summary_out[0] = t_total 273 summary_out[1] = t_done 274 summary_out[2] = t_active 275 summary_out[3] = t_blocked 276 summary_out[4] = reflog_flagged 277 *empcount_out = ecount 278 return ecount 279} 280 281// convenience: live ws- prefix. 282func wb_board(refpath: *u8, rows_out: *i64, cap: i64, 283 empcount_out: *i64, summary_out: *i64) -> i64 { 284 return wb_board_p(WS_PREFIX, refpath, rows_out, cap, empcount_out, summary_out) 285} 286 287// MANDATORY WRITE DISCIPLINE: assemble the WHOLE board line into ONE buffer, then a 288// SINGLE atomic locked fa_appendz to the file channel (cn_emit_red/green pattern). 289// One BOARD line per empire. Returns fa_appendz rc. 290func wb_emit_empire(path: *u8, empire: *u8, total: i64, done: i64, active: i64, blocked: i64) -> i64 { 291 let buf: *u8 = sys_mmap(WB_RECCAP + 16) 292 var o: i64 = 0 293 o = fa_cat(buf, o, "BOARD empire=\x00" as *u8) 294 o = fa_cat(buf, o, empire) 295 o = fa_cat(buf, o, " streams=\x00" as *u8) 296 o = fa_catn(buf, o, total) 297 o = fa_cat(buf, o, " done=\x00" as *u8) 298 o = fa_catn(buf, o, done) 299 o = fa_cat(buf, o, " active=\x00" as *u8) 300 o = fa_catn(buf, o, active) 301 o = fa_cat(buf, o, " blocked=\x00" as *u8) 302 o = fa_catn(buf, o, blocked) 303 o = fa_cat(buf, o, " END\x00" as *u8) 304 buf[o] = 0 as u8 305 return fa_appendz(path, buf, WB_RECCAP) 306} 307 308// final summary line, one buffer one locked write. 309func wb_emit_summary(path: *u8, total: i64, done: i64, active: i64, blocked: i64, flagged: i64) -> i64 { 310 let buf: *u8 = sys_mmap(WB_RECCAP + 16) 311 var o: i64 = 0 312 o = fa_cat(buf, o, "BOARD-SUMMARY total=\x00" as *u8) 313 o = fa_catn(buf, o, total) 314 o = fa_cat(buf, o, " done=\x00" as *u8) 315 o = fa_catn(buf, o, done) 316 o = fa_cat(buf, o, " active=\x00" as *u8) 317 o = fa_catn(buf, o, active) 318 o = fa_cat(buf, o, " blocked=\x00" as *u8) 319 o = fa_catn(buf, o, blocked) 320 o = fa_cat(buf, o, " reflog_flagged=\x00" as *u8) 321 o = fa_catn(buf, o, flagged) 322 o = fa_cat(buf, o, " verdict=RENDERED END\x00" as *u8) 323 buf[o] = 0 as u8 324 return fa_appendz(path, buf, WB_RECCAP) 325} 326 327// LIVE decomposition line: the active/blocked counts split by FRESH liveness, plus the 328// now+window the verdict was computed against (so the log records WHY a count is what it 329// is). One buffer, one locked atomic fa_appendz. 330func wb_emit_live(path: *u8, now: i64, window: i64, lsm: *i64) -> i64 { 331 let buf: *u8 = sys_mmap(WB_RECCAP + 16) 332 var o: i64 = 0 333 o = fa_cat(buf, o, "BOARD-LIVE now=\x00" as *u8); o = fa_catn(buf, o, now) 334 o = fa_cat(buf, o, " window=\x00" as *u8); o = fa_catn(buf, o, window) 335 o = fa_cat(buf, o, " active=\x00" as *u8); o = fa_catn(buf, o, lsm[0]) 336 o = fa_cat(buf, o, " a_alive=\x00" as *u8); o = fa_catn(buf, o, lsm[1]) 337 o = fa_cat(buf, o, " a_stalled=\x00" as *u8); o = fa_catn(buf, o, lsm[2]) 338 o = fa_cat(buf, o, " a_unknown=\x00" as *u8); o = fa_catn(buf, o, lsm[3]) 339 o = fa_cat(buf, o, " blocked=\x00" as *u8); o = fa_catn(buf, o, lsm[4]) 340 o = fa_cat(buf, o, " b_alive=\x00" as *u8); o = fa_catn(buf, o, lsm[5]) 341 o = fa_cat(buf, o, " b_stalled=\x00" as *u8); o = fa_catn(buf, o, lsm[6]) 342 o = fa_cat(buf, o, " b_unknown=\x00" as *u8); o = fa_catn(buf, o, lsm[7]) 343 o = fa_cat(buf, o, " END\x00" as *u8) 344 buf[o] = 0 as u8 345 return fa_appendz(path, buf, WB_RECCAP) 346} 347 348// stdout (fd 1) writers -- terminal, uncontended, plain writes are fine here. 349func wb_p(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 } 350// MIGRATED to the shared emitter (debt 1785563586). The old body mmapped a scratch buffer 351// per call and never freed it. At PAGE granularity that is 4096B leaked PER CALL -- the 352// defect that took 28.5GB of a 36GB host in nx_ts_lumadiff (2MB input, ~3.66M calls). 353// nxi_* is MSB-first, allocates NOTHING, and emits identical bytes including the sign. 354func wb_pn(v: i64) -> i64 { nxi_out(v); return 0 } 355 356// THE LIVE-STREAM NAMER: enumerate every registered stream (NO cap, ss_open snapshot) 357// and, for each one whose state is ACTIVE or BLOCKED, record its id + a FRESH liveness 358// verdict. Liveness = compare `now` (the caller injects the hardware clock; the gate 359// injects a controlled epoch -- no hidden wall-clock, mirroring hbm_scan's discipline) 360// against the stream's last activity = max(registry last_touched, last heartbeat beat), 361// using `fresh_window` (the caller sources it from config). So a stream LABELED active but 362// silent for longer than the window classifies STALLED, never silently counted alive. 363// streams_out[] packed WB_SW i64/row: +0 id ptr +1 statecode(1=ACTIVE 2=BLOCKED) 364// +2 liveness(HBS_ALIVE/STALLED/UNKNOWN) +3 last_activity +4 age +5 empire ptr 365// live_summary[]: [0]=active_total [1]=a_alive [2]=a_stalled [3]=a_unknown 366// [4]=blocked_total [5]=b_alive [6]=b_stalled [7]=b_unknown 367// Returns the number of named (active+blocked) streams. 368func wb_live_streams_p(prefix: *u8, hbpath: *u8, now: i64, fresh_window: i64, 369 streams_out: *i64, scap: i64, live_summary: *i64) -> i64 { 370 var z0: i64 = 0 371 while z0 < 8 { live_summary[z0] = 0; z0 = z0 + 1 } 372 let h: *i64 = ss_open(prefix) 373 let pq: *i64 = sys_mmap(16) as *i64 374 let lq: *i64 = sys_mmap(16) as *i64 375 var idstr: *u8 = "" as *u8 376 var idn: i64 = 0 377 if ss_hget(h, "ws:ids" as *u8, pq, lq) == 1 { idstr = pq[0] as *u8; idn = lq[0] } 378 let rq: *i64 = sys_mmap(16) as *i64 379 let rl: *i64 = sys_mmap(16) as *i64 380 let keybuf: *u8 = sys_mmap(160) 381 let stbuf: *u8 = sys_mmap(64) 382 let ltbuf: *u8 = sys_mmap(64) 383 var named: i64 = 0 384 var ii: i64 = 0 385 while ii < idn { 386 let idbuf: *u8 = sys_mmap(160) 387 var io: i64 = 0 388 var igo: i64 = 1 389 while igo == 1 { 390 if ii >= idn { igo = 0 } else { 391 if idstr[ii] == (9 as u8) { ii = ii + 1; igo = 0 } else { idbuf[io] = idstr[ii]; io = io + 1; ii = ii + 1 } 392 } 393 } 394 idbuf[io] = 0 as u8 395 if io > 0 { 396 keybuf[0] = 119 as u8; keybuf[1] = 115 as u8; keybuf[2] = 58 as u8 397 var z: i64 = 0 398 while z < io { keybuf[3 + z] = idbuf[z]; z = z + 1 } 399 keybuf[3 + io] = 0 as u8 400 if ss_hget(h, keybuf, rq, rl) == 1 { 401 ws_field(rq[0] as *u8, rl[0], 2, stbuf) // state label 402 var sc: i64 = 0 403 if wb_streq(stbuf, "ACTIVE" as *u8) == 1 { sc = WB_ST_ACTIVE } 404 if wb_streq(stbuf, "BLOCKED" as *u8) == 1 { sc = WB_ST_BLOCKED } 405 if sc != 0 { 406 ws_field(rq[0] as *u8, rl[0], 3, ltbuf) // last_touched 407 let lt: i64 = wb_atoi(ltbuf) 408 let lb: i64 = hb_last_beat_s(hbpath, idbuf) // live heartbeat (-1 = none) 409 var last_act: i64 = lt 410 if lb > last_act { last_act = lb } 411 var live: i64 = HBS_UNKNOWN 412 var age: i64 = 0 - 1 413 if last_act > 0 { 414 age = now - last_act 415 if age > fresh_window { live = HBS_STALLED } else { live = HBS_ALIVE } 416 } 417 let empbuf: *u8 = sys_mmap(64) 418 ws_field(rq[0] as *u8, rl[0], 1, empbuf) // empire 419 if named < scap { 420 let base: i64 = named * WB_SW 421 streams_out[base + 0] = idbuf as i64 422 streams_out[base + 1] = sc 423 streams_out[base + 2] = live 424 streams_out[base + 3] = last_act 425 streams_out[base + 4] = age 426 streams_out[base + 5] = empbuf as i64 427 } 428 named = named + 1 429 if sc == WB_ST_ACTIVE { 430 live_summary[0] = live_summary[0] + 1 431 if live == HBS_ALIVE { live_summary[1] = live_summary[1] + 1 } 432 if live == HBS_STALLED { live_summary[2] = live_summary[2] + 1 } 433 if live == HBS_UNKNOWN { live_summary[3] = live_summary[3] + 1 } 434 } 435 if sc == WB_ST_BLOCKED { 436 live_summary[4] = live_summary[4] + 1 437 if live == HBS_ALIVE { live_summary[5] = live_summary[5] + 1 } 438 if live == HBS_STALLED { live_summary[6] = live_summary[6] + 1 } 439 if live == HBS_UNKNOWN { live_summary[7] = live_summary[7] + 1 } 440 } 441 } 442 } 443 } 444 } 445 return named 446} 447 448// main(): render the LIVE board (ws- registry) to stdout + ws_board.log (file channel 449// via fa_appendz). The live reflog overlay is skipped (refpath=0) because the live 450// transition log path is not guaranteed present in every environment -- the overlay is 451// an OPTIONAL freshness check; the registry is the SSOT. Exit 0 on render, non-zero if 452// the registry guard refuses (the loud crash-recovery signal). 453func main() -> i64 { 454 wb_p("=== WMS-M2 LIVE STATUS BOARD (derived from WMS-R1 registry + WMS-R2 reflog) ===\n" as *u8) 455 let rows: *i64 = sys_mmap(8 * WB_MAXEMP * WB_ROWW) as *i64 456 let ec: *i64 = sys_mmap(16) as *i64 457 let sm: *i64 = sys_mmap(64) as *i64 458 let rc: i64 = wb_board(0 as *u8, rows, WB_MAXEMP, ec, sm) 459 if rc < 0 { 460 wb_p(" BOARD REFUSED rc=\x00" as *u8); wb_pn(rc) 461 if rc == WB_INCOMPLETE { wb_p(" reason=registry-incomplete (lost segment) -- NOT rendered\n" as *u8) } 462 if rc == WB_NOIDS { wb_p(" reason=no-ws:ids -- nothing registered\n" as *u8) } 463 if rc == WB_TORNREFLOG { wb_p(" reason=torn/old-reflog -- view REJECTED\n" as *u8) } 464 sys_exit(1); return 1 465 } 466 let necount: i64 = ec[0] 467 var e: i64 = 0 468 while e < necount { 469 let base: i64 = e * WB_ROWW 470 let empire: *u8 = rows[base + 0] as *u8 471 let total: i64 = rows[base + 1] 472 let done: i64 = rows[base + 2] 473 let active: i64 = rows[base + 3] 474 let blocked: i64 = rows[base + 4] 475 // stdout line 476 wb_p(" empire=\x00" as *u8); wb_p(empire) 477 wb_p(" streams=\x00" as *u8); wb_pn(total) 478 wb_p(" done=\x00" as *u8); wb_pn(done) 479 wb_p(" active=\x00" as *u8); wb_pn(active) 480 wb_p(" blocked=\x00" as *u8); wb_pn(blocked) 481 wb_p("\n\x00" as *u8) 482 // file channel: one locked atomic write per empire 483 wb_emit_empire(WB_LOG, empire, total, done, active, blocked) 484 e = e + 1 485 } 486 wb_p(" SUMMARY total=\x00" as *u8); wb_pn(sm[0]) 487 wb_p(" done=\x00" as *u8); wb_pn(sm[1]) 488 wb_p(" active=\x00" as *u8); wb_pn(sm[2]) 489 wb_p(" blocked=\x00" as *u8); wb_pn(sm[3]) 490 wb_p(" verdict=RENDERED\n\x00" as *u8) 491 wb_emit_summary(WB_LOG, sm[0], sm[1], sm[2], sm[3], sm[4]) 492 493 // ===== LIVE STREAMS: name every active/blocked stream + FRESH liveness ===== 494 // This is the crash-recovery payoff: not just counts, but WHICH streams, and 495 // whether each is actually beating NOW. Recomputed from the hardware clock each 496 // render (fresh like energy), with the freshness window sourced from config. 497 let now: i64 = sys_now_realtime_sec() 498 let fw: i64 = wb_cfg_int(WS_PREFIX, WB_CFG_FRESH_KEY, WB_FRESH_WINDOW_DEFAULT_SEC) 499 let nids: i64 = wb_count_ids(WS_PREFIX) 500 let streams: *i64 = sys_mmap(8 * WB_SW * (nids + 16)) as *i64 501 let lsm: *i64 = sys_mmap(8 * 16) as *i64 502 let nnamed: i64 = wb_live_streams_p(WS_PREFIX, WB_HB_CHANNEL, now, fw, streams, nids + 16, lsm) 503 wb_p("=== LIVE STREAMS (active/blocked; liveness FRESH @ now=\x00" as *u8); wb_pn(now) 504 wb_p(" window=\x00" as *u8); wb_pn(fw); wb_p("s) ===\n\x00" as *u8) 505 var si: i64 = 0 506 while si < nnamed { 507 let sb: i64 = si * WB_SW 508 wb_p(" id=\x00" as *u8); wb_p(streams[sb + 0] as *u8) 509 if streams[sb + 1] == WB_ST_ACTIVE { wb_p(" state=ACTIVE \x00" as *u8) } else { wb_p(" state=BLOCKED\x00" as *u8) } 510 let lv: i64 = streams[sb + 2] 511 if lv == HBS_ALIVE { wb_p(" live=ALIVE \x00" as *u8) } 512 if lv == HBS_STALLED { wb_p(" live=STALLED\x00" as *u8) } 513 if lv == HBS_UNKNOWN { wb_p(" live=UNKNOWN\x00" as *u8) } 514 wb_p(" age=\x00" as *u8); wb_pn(streams[sb + 4]) 515 wb_p("s empire=\x00" as *u8); wb_p(streams[sb + 5] as *u8) 516 wb_p("\n\x00" as *u8) 517 si = si + 1 518 } 519 wb_p(" LIVE-SUMMARY active=\x00" as *u8); wb_pn(lsm[0]) 520 wb_p(" (alive=\x00" as *u8); wb_pn(lsm[1]); wb_p(" stalled=\x00" as *u8); wb_pn(lsm[2]); wb_p(" unknown=\x00" as *u8); wb_pn(lsm[3]); wb_p(")\x00" as *u8) 521 wb_p(" blocked=\x00" as *u8); wb_pn(lsm[4]) 522 wb_p(" (alive=\x00" as *u8); wb_pn(lsm[5]); wb_p(" stalled=\x00" as *u8); wb_pn(lsm[6]); wb_p(" unknown=\x00" as *u8); wb_pn(lsm[7]); wb_p(")\n\x00" as *u8) 523 wb_emit_live(WB_LOG, now, fw, lsm) 524 525 sys_exit(0) 526 return 0 527}