code wiki / _hdl_build / nx_ready_census.nx
nx_ready_census.nx source
↩ module page · 108 lines · 5976 B
1// nx_ready_census.nx -- LIVENESS-vs-READINESS CENSUS across the whole supervised fleet (seq1314 oracle).
2// WHY: on 2026-07-29 nx_tools_api_serve was PROCESS-ALIVE with its LISTEN SOCKET GONE; every liveness-only
3// guard read it as healthy and the agent-facing MCP surface stayed dark for an hour. seq1299 fixed the
4// detector and wired it into ONE guard. This organ answers the fleet-wide question that fix raised:
5// "which OTHER services are currently claiming UP while nothing is listening?"
6// METHOD: mgmt_snap.json is the supervisor's OWN per-poll claim (SVC <name> <port> <state> ...). We take
7// each claim and CONNECT to the port -- connect-only, never writing a byte, so this can never disturb a
8// working daemon (the retired /healthz serving-probe crash-looped redirect.elf by writing to it).
9// A DISAGREEMENT (claim=UP, socket=REFUSED) is the exact 07-29 wound, caught by measurement.
10// ★It is ALSO the empirical port oracle for seq1314: the supervisor's guard CALL-SITE COMMENTS are stale
11// (they say the docportal admin daemon is :8456; it is really :18456 -- wiring a guard from the comment
12// would kill a HEALTHY daemon every poll). mgmt_snap.json agrees with the live listen table; comments do not.
13// nx_ready_census [snapfile] exit 0 = all claims honest · 3 = disagreement · 2 = snapshot unreadable
14// license_tier: ORIGINAL No hw writes (Rule 26). expect_exit: 0
15import "nx_health_probe.nx"
16import "nx_itoa_lib.nx" // shared MSB-first emitter (zero-alloc)
17import "nx_syscalls.nx"
18
19// sized: mgmt_snap.json is one line per supervised service; 64KiB is ~100x the observed size
20const RC_CAP: i64 = 65536
21// sized: service basenames in the snapshot are well under this
22const RC_NAMECAP: i64 = 128
23
24func rc_w(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 }
25// MIGRATED to the shared emitter (debt 1785563586). The old body mmapped a scratch buffer
26// per call and never freed it. At PAGE granularity that is 4096B leaked PER CALL -- the
27// defect that took 28.5GB of a 36GB host in nx_ts_lumadiff (2MB input, ~3.66M calls).
28// nxi_* is MSB-first, allocates NOTHING, and emits identical bytes including the sign.
29func rc_n(v: i64) -> i64 { nxi_out(v); return 0 }
30
31func main(argc: i64, argv: *i64) -> i64 {
32 var path: *u8 = "mgmt_snap.json" as *u8
33 if argc > 1 { path = argv[1] as *u8 }
34 let fd: i64 = sys_openat_rd(path)
35 if fd < 0 { rc_w("{\"organ\":\"nx_ready_census\",\"refused\":\"snapshot unreadable\"}\n" as *u8); sys_exit(2); return 2 }
36 let buf: *u8 = sys_mmap(RC_CAP)
37 let n: i64 = sys_read(fd, buf, RC_CAP - 1)
38 sys_close(fd)
39 if n <= 0 { rc_w("{\"organ\":\"nx_ready_census\",\"refused\":\"snapshot empty\"}\n" as *u8); sys_exit(2); return 2 }
40 buf[n] = 0 as u8
41 var claimed: i64 = 0
42 var probed: i64 = 0
43 var listening: i64 = 0
44 var dead: i64 = 0
45 var unprobeable: i64 = 0
46 let nm: *u8 = sys_mmap(RC_NAMECAP)
47 rc_w("{\"organ\":\"nx_ready_census\",\"disagreements\":[" as *u8)
48 var first: i64 = 1
49 var i: i64 = 0
50 while i < n {
51 var le: i64 = i
52 var sc: i64 = 1
53 while sc == 1 { if le >= n { sc = 0 } else { if buf[le] == (10 as u8) { sc = 0 } else { le = le + 1 } } }
54 var issvc: i64 = 0
55 if le - i > 4 { if buf[i] == (83 as u8) { if buf[i+1] == (86 as u8) { if buf[i+2] == (67 as u8) { if buf[i+3] == (32 as u8) { issvc = 1 } } } } }
56 if issvc == 1 {
57 var p: i64 = i + 4
58 var nn: i64 = 0
59 while p < le { if buf[p] == (32 as u8) { p = le } else { if nn < RC_NAMECAP - 1 { nm[nn] = buf[p]; nn = nn + 1 } p = p + 1 } }
60 nm[nn] = 0 as u8
61 // re-scan for the port + state fields (field 2 and 3 after SVC)
62 var q: i64 = i + 4
63 var f: i64 = 0
64 var port: i64 = 0
65 var up: i64 = 0
66 while q < le {
67 if buf[q] == (32 as u8) {
68 f = f + 1
69 if f == 1 {
70 var r: i64 = q + 1
71 while r < le { let c: i64 = buf[r] as i64; if c < 48 { r = le } else { if c > 57 { r = le } else { port = port * 10 + (c - 48); r = r + 1 } } }
72 }
73 if f == 2 {
74 if q + 2 < le { if buf[q+1] == (85 as u8) { if buf[q+2] == (80 as u8) { up = 1 } } }
75 q = le
76 }
77 }
78 if q < le { q = q + 1 }
79 }
80 if up == 1 {
81 claimed = claimed + 1
82 if port <= 0 { unprobeable = unprobeable + 1 } else {
83 probed = probed + 1
84 if hp_listening(port) == 1 { listening = listening + 1 } else {
85 // CONFIRM before accusing -- same discipline as the supervisor's own check
86 if hp_socket_dead(port) == 1 {
87 dead = dead + 1
88 if first == 0 { rc_w("," as *u8) }
89 first = 0
90 rc_w("{\"service\":\"" as *u8); sys_write(1, nm, nn)
91 rc_w("\",\"port\":" as *u8); rc_n(port)
92 rc_w(",\"claim\":\"UP\",\"socket\":\"REFUSED\"}" as *u8)
93 } else { listening = listening + 1 }
94 }
95 }
96 }
97 }
98 i = le + 1
99 }
100 rc_w("],\"claimed_up\":" as *u8); rc_n(claimed)
101 rc_w(",\"probed\":" as *u8); rc_n(probed)
102 rc_w(",\"listening\":" as *u8); rc_n(listening)
103 rc_w(",\"alive_but_socket_dead\":" as *u8); rc_n(dead)
104 rc_w(",\"unprobeable_port0\":" as *u8); rc_n(unprobeable)
105 if dead > 0 { rc_w(",\"verdict\":\"DISAGREEMENT -- a service claims UP while nothing listens (the 2026-07-29 class)\"}\n" as *u8); sys_exit(3); return 3 }
106 rc_w(",\"verdict\":\"HONEST -- every UP claim has a live listener\"}\n" as *u8)
107 return 0
108}