code wiki / _hdl_build / nx_apm_collect_gate.nx
nx_apm_collect_gate.nx source
↩ module page · 111 lines · 6756 B
1// nx_apm_collect_gate.nx -- SOVEREIGN gate for the internal-HTTP APM collector.
2//
3// Spawns real mock daemons (nx_apm_mock, self-terminating) on loopback and proves ac_burst's four verdicts
4// against ground truth -- each test prints its MEASURED numbers (no overclaim: the operator reads the raw
5// stats, not a curated "GREEN"):
6// T1 NEGATIVE CONTROL -- a healthy fast daemon must be GREEN, fully served, nothing flagged.
7// T2 DETECTION -- a daemon that SERIALIZES (300ms/req) must, under 8 concurrent probes, show a
8// stall and max >= ~2400ms = the "tabs don't load" wedge, caught.
9// T3 LIAR-KILL -- an ABSENT daemon (no listener) must be DOWN with oks=0, never GREEN.
10// T4 GRACEFUL DEGRADE -- a daemon slower than the probe timeout must time out (~1s) not hang forever.
11// T5 ARTIFACT-IMMUNITY -- a daemon that sends FAST then HOLDS the socket open (keep-alive/idle) must be
12// GREEN (ttfb fast) with slowclose>0 -- NOT a false wedge. This is THE fix: the
13// live :8791/vroom "20s" was exactly this (ttfb~0, total~20-25s), an artifact.
14//
15// Requires /tmp/nx_apm_mock.sov.elf built first. Per-test reap; no kill primitive needed. license_tier: ORIGINAL
16import "nx_syscalls.nx"
17import "nx_apm_collect.nx" // ac_burst + AC_R_* + ac_n
18import "nx_gate_verdict.nx"
19
20const GE_MOCK: *u8 = "_offc/nx_apm_mock.elf"
21
22func ge_uw(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 }
23func ge_wn(v: i64) -> i64 { let b: *u8 = sys_mmap(24); let e: i64 = ac_n(b, 0, v); sys_write(1, b, e); return 0 }
24func ge_itoa(v: i64) -> *u8 { let b: *u8 = sys_mmap(24); let e: i64 = ac_n(b, 0, v); b[e] = 0 as u8; return b }
25
26// fork + execve the mock with (port, delay_ms, hold_ms, max_reqs) as string args; returns pid.
27func ge_spawn_mock(port: i64, delay: i64, hold: i64, maxr: i64) -> i64 {
28 let pid: i64 = sys_fork()
29 if pid == 0 {
30 let av: *i64 = sys_mmap(8 * 6) as *i64
31 av[0] = GE_MOCK as i64
32 av[1] = ge_itoa(port) as i64
33 av[2] = ge_itoa(delay) as i64
34 av[3] = ge_itoa(hold) as i64
35 av[4] = ge_itoa(maxr) as i64
36 av[5] = 0
37 let ev: *i64 = sys_mmap(8) as *i64; ev[0] = 0
38 sys_execve(GE_MOCK, av, ev); sys_exit(127)
39 }
40 return pid
41}
42
43func main(argc: i64, argv: *i64) -> i64 {
44 var pass: i64 = 0; var total: i64 = 0
45 let out: *i64 = sys_mmap(8 * 12) as *i64
46 let rp: *i64 = sys_mmap(16) as *i64
47 let epoch: i64 = sys_now_realtime_sec()
48
49 // ---- T1: healthy fast daemon (NEGATIVE CONTROL) ----
50 let pA: i64 = ge_spawn_mock(18091, 0, 0, 8)
51 sys_sleep_ms(300)
52 let cA: i64 = ac_burst(18091, "/" as *u8, "mock-fast" as *u8, 8, 22, epoch, 0x7f000001, out)
53 total = total + 1
54 var okA: i64 = 0
55 if cA == AC_R_GREEN { if out[3] == 8 { if out[4] == 0 { okA = 1 } } }
56 if okA == 1 { pass = pass + 1; ge_uw("[PASS] " as *u8) } else { ge_uw("[FAIL] " as *u8) }
57 ge_uw("T1 healthy(neg-control): code=" as *u8); ge_wn(cA); ge_uw(" oks=" as *u8); ge_wn(out[3]); ge_uw(" p50=" as *u8); ge_wn(out[0]); ge_uw(" max=" as *u8); ge_wn(out[2]); ge_uw("ms stalls=" as *u8); ge_wn(out[4]); ge_uw(" (want GREEN, oks=8, stalls=0)\n" as *u8)
58 sys_wait4(pA, rp, 0)
59
60 // ---- T2: serializing daemon under 8 concurrent probes (DETECTION) ----
61 let pB: i64 = ge_spawn_mock(18092, 300, 0, 8)
62 sys_sleep_ms(300)
63 let cB: i64 = ac_burst(18092, "/library" as *u8, "mock-wedge" as *u8, 8, 22, epoch, 0x7f000001, out)
64 total = total + 1
65 var okB: i64 = 0
66 if cB >= AC_R_AMBER { if out[4] > 0 { if out[2] >= 2000 { okB = 1 } } }
67 if okB == 1 { pass = pass + 1; ge_uw("[PASS] " as *u8) } else { ge_uw("[FAIL] " as *u8) }
68 ge_uw("T2 serial-wedge(detect): code=" as *u8); ge_wn(cB); ge_uw(" p50=" as *u8); ge_wn(out[0]); ge_uw(" max=" as *u8); ge_wn(out[2]); ge_uw("ms stalls=" as *u8); ge_wn(out[4]); ge_uw(" (want >=AMBER, stalls>0, max>=2000ms)\n" as *u8)
69 sys_wait4(pB, rp, 0)
70
71 // ---- T3: absent daemon, no listener (LIAR-KILL) ----
72 let cC: i64 = ac_burst(18094, "/" as *u8, "mock-absent" as *u8, 4, 22, epoch, 0x7f000001, out)
73 total = total + 1
74 var okC: i64 = 0
75 if cC == AC_R_DOWN { if out[3] == 0 { okC = 1 } }
76 if okC == 1 { pass = pass + 1; ge_uw("[PASS] " as *u8) } else { ge_uw("[FAIL] " as *u8) }
77 ge_uw("T3 absent(liar-kill): code=" as *u8); ge_wn(cC); ge_uw(" oks=" as *u8); ge_wn(out[3]); ge_uw(" down=" as *u8); ge_wn(out[6]); ge_uw(" (want DOWN, oks=0)\n" as *u8)
78
79 // ---- T4: daemon slower than the probe timeout (GRACEFUL DEGRADATION) ----
80 let pD: i64 = ge_spawn_mock(18093, 3000, 0, 1)
81 sys_sleep_ms(300)
82 let cD: i64 = ac_burst(18093, "/" as *u8, "mock-hang" as *u8, 1, 1, epoch, 0x7f000001, out)
83 total = total + 1
84 var okD: i64 = 0
85 if cD == AC_R_RED { if out[5] >= 1 { if out[2] < 3000 { okD = 1 } } }
86 if okD == 1 { pass = pass + 1; ge_uw("[PASS] " as *u8) } else { ge_uw("[FAIL] " as *u8) }
87 ge_uw("T4 hang(timeout/grace): code=" as *u8); ge_wn(cD); ge_uw(" timeouts=" as *u8); ge_wn(out[5]); ge_uw(" max=" as *u8); ge_wn(out[2]); ge_uw("ms (want RED, timeouts>=1, max<3000ms i.e. it gave up at ~1s)\n" as *u8)
88 sys_wait4(pD, rp, 0)
89
90 // ---- T5: fast-then-HOLD daemon = keep-alive/idle-close ARTIFACT (the live :8791 + vroom :8446 case) ----
91 let pE: i64 = ge_spawn_mock(18095, 0, 4000, 1)
92 sys_sleep_ms(300)
93 let cE: i64 = ac_burst(18095, "/" as *u8, "mock-artifact" as *u8, 1, 22, epoch, 0x7f000001, out)
94 total = total + 1
95 var okE: i64 = 0
96 if cE == AC_R_GREEN { if out[9] >= 1 { if out[2] < 2000 { if out[8] >= 3000 { okE = 1 } } } }
97 if okE == 1 { pass = pass + 1; ge_uw("[PASS] " as *u8) } else { ge_uw("[FAIL] " as *u8) }
98 ge_uw("T5 fast-then-hold(artifact-immune): code=" as *u8); ge_wn(cE); ge_uw(" ttfb_max=" as *u8); ge_wn(out[2]); ge_uw("ms total_max=" as *u8); ge_wn(out[8]); ge_uw("ms slowclose=" as *u8); ge_wn(out[9]); ge_uw(" (want GREEN, ttfb<2000, total>=3000, slowclose>=1)\n" as *u8)
99 sys_wait4(pE, rp, 0)
100
101 ge_uw("=== nx_apm_collect_gate " as *u8); ge_wn(pass); ge_uw("/" as *u8); ge_wn(total)
102 // MIGRATED onto nx_gate_verdict by nx_gate_dry_apply (D001, minimal form): every check
103 // row above is untouched, so the PASS/FAIL vector cannot change; only the hand-rolled
104 // verdict emission is replaced by the ONE shared base class. Proven by nx_gate_migrate verify.
105 let ctr__dry: *i64 = gv_ctr()
106 ctr__dry[0] = pass
107 ctr__dry[1] = total
108 let rc__dry: i64 = gv_verdict("APM-COLLECT-GATE" as *u8, ctr__dry, "teeth unchanged; verdict emission migrated onto the shared base class" as *u8)
109 sys_exit(rc__dry)
110 return rc__dry
111}