code wiki / _hdl_build / nx_pub_recover_pulse_gate.nx
nx_pub_recover_pulse_gate.nx source
↩ module page · 100 lines · 6217 B
1// nx_pub_recover_pulse_gate.nx -- SOVEREIGN gate for the Publisher self-healing pulse (full chain end-to-end).
2// Proves metrics -> data-driven map -> DECIDE -> EXECUTE -> a REAL daemon dies. Spawns nx_pr_mock, maps a test
3// URL to it, and: T1 NEG-CONTROL healthy metrics -> 0 recoveries + mock ALIVE; T2 WEDGED metrics -> 1 recovery;
4// T3 mock KILLED; T4 ledgered. Per-run /tmp files. Build /tmp/nx_pr_mock.sov.elf first. license_tier: ORIGINAL
5import "nx_syscalls.nx"
6import "nx_pub_recover_pulse.nx" // pulse_pass + (transitively) proc_alive_by_name / pr_n / pr_s
7
8const GP_MET: *u8 = "/tmp/pp_test_metrics.tsv"
9const GP_Q: *u8 = "/tmp/pp_test_queue.tsv"
10const GP_LED: *u8 = "/tmp/pp_test_ledger.tsv"
11const GP_MAP: *u8 = "/tmp/pp_test_map.tsv"
12const GP_MOCK: *u8 = "/tmp/nx_pr_mock.sov.elf"
13const GP_NEEDLE: *u8 = "nx_pr_mock"
14
15func gp_uw(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 }
16func gp_wn(v: i64) -> i64 { let b: *u8=sys_mmap(24); let e: i64=pr_n(b,0,v); sys_write(1,b,e); return 0 }
17func gp_sleep_ms(ms: i64) -> i64 { let ts: *i64=sys_mmap(16) as *i64; ts[0]=ms/1000; ts[1]=(ms-(ms/1000)*1000)*1000000; __syscall(35, ts as i64, 0,0,0,0,0); return 0 }
18func gp_write(path: *u8, buf: *u8, len: i64) -> i64 { let fd: i64=sys_openat_wr(path, 0x1a4); if fd<0 {return 0-1} if len>0 { sys_write(fd, buf, len) } sys_close(fd); return 0 }
19// one apm_metrics row with field2=url and field9=max.
20func gp_row(buf: *u8, off: i64, url: *u8, maxv: i64) -> i64 {
21 var o: i64 = off
22 o = pr_n(buf,o,1782007999); buf[o]=9 as u8; o=o+1
23 o = pr_s(buf,o,"lbl" as *u8); buf[o]=9 as u8; o=o+1
24 o = pr_s(buf,o,url); buf[o]=9 as u8; o=o+1
25 o = pr_s(buf,o,"1" as *u8); buf[o]=9 as u8; o=o+1
26 o = pr_s(buf,o,"1" as *u8); buf[o]=9 as u8; o=o+1
27 o = pr_s(buf,o,"1" as *u8); buf[o]=9 as u8; o=o+1
28 o = pr_n(buf,o,maxv); buf[o]=9 as u8; o=o+1
29 o = pr_n(buf,o,maxv); buf[o]=9 as u8; o=o+1
30 o = pr_n(buf,o,maxv); buf[o]=9 as u8; o=o+1
31 o = pr_n(buf,o,maxv); buf[o]=9 as u8; o=o+1 // field9 = max
32 o = pr_s(buf,o,"0" as *u8); buf[o]=9 as u8; o=o+1
33 o = pr_s(buf,o,"0" as *u8); buf[o]=9 as u8; o=o+1
34 o = pr_s(buf,o,"0" as *u8); buf[o]=10 as u8; o=o+1
35 return o
36}
37
38func main(argc: i64, argv: *i64) -> i64 {
39 var pass: i64 = 0; var total: i64 = 0
40
41 // spawn mock
42 let pid: i64 = sys_fork()
43 if pid == 0 {
44 let av: *i64 = sys_mmap(16) as *i64; av[0]=GP_MOCK as i64; av[1]=0
45 let ev: *i64 = sys_mmap(8) as *i64; ev[0]=0
46 sys_execve(GP_MOCK, av, ev); sys_exit(127)
47 }
48 gp_sleep_ms(500)
49
50 // data-driven map: a test url-substring -> the mock daemon
51 let mp: *u8 = sys_mmap(128); var mo: i64 = 0
52 mo = pr_s(mp, mo, "/__t__" as *u8); mp[mo]=9 as u8; mo=mo+1; mo = pr_s(mp, mo, GP_NEEDLE); mp[mo]=10 as u8; mo=mo+1
53 gp_write(GP_MAP, mp, mo)
54
55 // T0: mock alive
56 let a0: i64 = proc_alive_by_name(GP_NEEDLE)
57 total = total + 1; if a0 == 1 { pass = pass + 1; gp_uw("[PASS] T0 mock ALIVE\n" as *u8) } else { gp_uw("[FAIL] T0 mock not alive\n" as *u8) }
58
59 // T1 NEG-CONTROL: HEALTHY metrics -> 0 recoveries, mock untouched
60 let m: *u8 = sys_mmap(1024); var o: i64 = 0
61 o = gp_row(m, o, "https://x/__t__" as *u8, 500)
62 gp_write(GP_MET, m, o); gp_write(GP_Q, "" as *u8, 0); gp_write(GP_LED, "" as *u8, 0)
63 let r1: i64 = pulse_pass(GP_MET, GP_Q, GP_LED, GP_MAP, 5000)
64 gp_sleep_ms(200)
65 let alive1: i64 = proc_alive_by_name(GP_NEEDLE)
66 total = total + 1; if r1 == 0 { if alive1 == 1 { pass = pass + 1; gp_uw("[PASS] T1 neg-control healthy -> 0 + mock ALIVE\n" as *u8) } else { gp_uw("[FAIL] T1 mock wrongly killed\n" as *u8) } } else { gp_uw("[FAIL] T1 expected 0 got " as *u8); gp_wn(r1); gp_uw("\n" as *u8) }
67
68 // T2 WEDGE: wedged metrics -> 1 recovery
69 var o2: i64 = 0
70 o2 = gp_row(m, o2, "https://x/__t__" as *u8, 20000)
71 gp_write(GP_MET, m, o2); gp_write(GP_Q, "" as *u8, 0)
72 let r2: i64 = pulse_pass(GP_MET, GP_Q, GP_LED, GP_MAP, 5000)
73 total = total + 1; if r2 == 1 { pass = pass + 1; gp_uw("[PASS] T2 wedge -> 1 recovery executed\n" as *u8) } else { gp_uw("[FAIL] T2 expected 1 got " as *u8); gp_wn(r2); gp_uw("\n" as *u8) }
74 gp_sleep_ms(400)
75
76 // T3: mock KILLED
77 let a1: i64 = proc_alive_by_name(GP_NEEDLE)
78 total = total + 1; if a1 == 0 { pass = pass + 1; gp_uw("[PASS] T3 mock KILLED by the pulse\n" as *u8) } else { gp_uw("[FAIL] T3 mock still alive\n" as *u8) }
79 let st: *i64 = sys_mmap(16) as *i64; sys_wait4(pid, st, 0)
80
81 // T4: ledgered
82 let lp: *i64 = sys_mmap(16) as *i64; lp[0]=0; let lb: *u8 = sys_read_file(GP_LED, lp)
83 var led: i64 = 0; if (lb as i64)!=0 { if lp[0]>0 { led=1 } }
84 total = total + 1; if led == 1 { pass = pass + 1; gp_uw("[PASS] T4 recovery LEDGERED\n" as *u8) } else { gp_uw("[FAIL] T4 no ledger\n" as *u8) }
85
86 // T5 COOLDOWN / KILL-STORM GUARD: ledger already shows >= PP_MAX_RECOVERIES recent recoveries ->
87 // a fresh wedge must BACK OFF (0), never re-kill (#26 never-brick). No live mock needed (it backs off before acting).
88 let cm: *u8 = sys_mmap(256); var co: i64 = 0
89 co = pr_n(cm,co,sys_now_realtime_sec()); cm[co]=9 as u8; co=co+1; co=pr_s(cm,co,GP_NEEDLE); cm[co]=9 as u8; co=co+1; co=pr_s(cm,co,"1" as *u8); cm[co]=9 as u8; co=co+1; co=pr_s(cm,co,"KILLED" as *u8); cm[co]=10 as u8; co=co+1
90 co = pr_n(cm,co,sys_now_realtime_sec()); cm[co]=9 as u8; co=co+1; co=pr_s(cm,co,GP_NEEDLE); cm[co]=9 as u8; co=co+1; co=pr_s(cm,co,"1" as *u8); cm[co]=9 as u8; co=co+1; co=pr_s(cm,co,"KILLED" as *u8); cm[co]=10 as u8; co=co+1
91 gp_write(GP_LED, cm, co)
92 var ow: i64 = 0; ow = gp_row(m, ow, "https://x/__t__" as *u8, 20000)
93 gp_write(GP_MET, m, ow); gp_write(GP_Q, "" as *u8, 0)
94 let r5: i64 = pulse_pass(GP_MET, GP_Q, GP_LED, GP_MAP, 5000)
95 total = total + 1; if r5 == 0 { pass = pass + 1; gp_uw("[PASS] T5 cooldown: MAX recoveries -> BACK OFF (0), no kill-storm\n" as *u8) } else { gp_uw("[FAIL] T5 expected 0 got " as *u8); gp_wn(r5); gp_uw("\n" as *u8) }
96
97 gp_uw("=== nx_pub_recover_pulse_gate " as *u8); gp_wn(pass); gp_uw("/" as *u8); gp_wn(total)
98 if pass == total { gp_uw(" GREEN\n" as *u8); return 0 }
99 gp_uw(" RED\n" as *u8); return 1
100}