code wiki / _hdl_build / nx_discovery_selfheal_beat.nx
nx_discovery_selfheal_beat.nx source
↩ module page · 96 lines · 4965 B
1// nx_discovery_selfheal_beat.nx -- makes the discovery-selfheal check ACTUALLY self-heal.
2//
3// THE DEFECT THIS EXISTS TO KILL (measured 2026-07-30): the sweep row ran `nx_toolreg_reconcile heal`,
4// a verb named "heal" that only DIAGNOSES -- it prints `no-schema-residue=N needs-author-schema: ...`
5// and exits 1. Closing it needs a THREE-COMMAND MANUAL SEQUENCE (schema_backfill -> reconcile ->
6// heal), so every time any lane shipped a tool without a schema header the row went RED and STAYED
7// red until a human noticed. It sat red for 24+ consecutive cycles at 0 permille. Clearing the
8// backlog by hand does NOT fix it: within one hour of clearing 14, five MORE arrived
9// (nx_meshprofile, nx_routeguard, nx_wirecensus, nx_arousal_skin_gate, nx_arousal_mesh_gate) from
10// other lanes shipping normally. A treadmill is not a defect in the lanes -- it is a missing loop.
11//
12// So this beat RUNS the loop the check was only ever reporting on:
13// 1. ./nx_schema_backfill.elf harvest schemas from source headers (idempotent)
14// 2. ./nx_toolreg_reconcile.elf publish them into the registry
15// 3. ./nx_toolreg_reconcile.elf heal judge, and EXIT WITH ITS CODE
16// Step 3's rc is the beat's rc, so the rail still judges the real invariant -- we heal first, then
17// report honestly. If a residue SURVIVES the backfill (an organ whose source has no header at all)
18// the beat still goes RED and NAMES it, because that one genuinely needs a human to author it.
19//
20// Rule 10 idempotent: backfill is idempotent by contract; reconcile skips already-registered rows.
21// license_tier: ORIGINAL No hw writes (Rule 26). expect_exit: 0
22import "nx_syscalls.nx"
23import "nx_tool_run.nx"
24
25const DB_OUTCAP: i64 = 65536
26const DB_AVCAP: i64 = 40
27const DB_STDOUT: i64 = 1
28const DB_BACKFILL: *u8 = "./nx_schema_backfill.elf" as *u8
29const DB_RECONCILE: *u8 = "./nx_toolreg_reconcile.elf" as *u8
30const DB_HEAL: *u8 = "heal" as *u8
31const DB_STEP_MS: i64 = 120000
32
33func db_len(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n }
34func db_puts(s: *u8) -> i64 { sys_write(DB_STDOUT, s, db_len(s)); return 0 }
35func db_putn(v: i64) -> i64 {
36 let t: *u8 = sys_mmap(32)
37 var m: i64 = v
38 if m < 0 { m = 0 - m }
39 var o: i64 = 0
40 if m == 0 { t[0] = 48 as u8; o = 1 }
41 while m > 0 { t[o] = ((m % 10) + 48) as u8; o = o + 1; m = m / 10 }
42 if v < 0 { db_puts("-" as *u8) }
43 var i: i64 = o - 1
44 while i >= 0 { sys_write(DB_STDOUT, (t as i64 + i) as *u8, 1); i = i - 1 }
45 return 0
46}
47func db_tail(out: *u8, n: i64) -> i64 {
48 if n <= 0 { return 0 }
49 var e: i64 = n
50 if out[e - 1] == (10 as u8) { e = e - 1 }
51 var b: i64 = e
52 var go: i64 = 1
53 while go == 1 {
54 if b <= 0 { go = 0 } else {
55 if out[b - 1] == (10 as u8) { go = 0 } else { b = b - 1 }
56 }
57 }
58 sys_write(DB_STDOUT, (out as i64 + b) as *u8, e - b)
59 sys_write(DB_STDOUT, "\n" as *u8, 1)
60 return 0
61}
62
63func main(argc: i64, argv: *i64) -> i64 {
64 let out: *u8 = sys_mmap(DB_OUTCAP)
65 let ol: *i64 = sys_mmap(16) as *i64
66
67 let av1: *i64 = sys_mmap(DB_AVCAP) as *i64
68 av1[0] = DB_BACKFILL as i64
69 av1[1] = 0
70 let rc1: i64 = tr_run_capture_to(DB_BACKFILL, av1, out, DB_OUTCAP - 1, ol, DB_STEP_MS)
71 db_puts("SELFHEAL-BEAT backfill rc=" as *u8); db_putn(rc1); db_puts(" " as *u8); db_tail(out, ol[0])
72
73 let av2: *i64 = sys_mmap(DB_AVCAP) as *i64
74 av2[0] = DB_RECONCILE as i64
75 av2[1] = 0
76 let rc2: i64 = tr_run_capture_to(DB_RECONCILE, av2, out, DB_OUTCAP - 1, ol, DB_STEP_MS)
77 db_puts("SELFHEAL-BEAT publish rc=" as *u8); db_putn(rc2); db_puts(" " as *u8); db_tail(out, ol[0])
78
79 let av3: *i64 = sys_mmap(DB_AVCAP) as *i64
80 av3[0] = DB_RECONCILE as i64
81 av3[1] = DB_HEAL as i64
82 av3[2] = 0
83 let rc3: i64 = tr_run_capture_to(DB_RECONCILE, av3, out, DB_OUTCAP - 1, ol, DB_STEP_MS)
84 db_puts("SELFHEAL-BEAT verdict rc=" as *u8); db_putn(rc3); db_puts(" " as *u8); db_tail(out, ol[0])
85
86 if rc3 == 0 { sys_exit(0); return 0 }
87 // DISTINGUISH THE TWO RED CAUSES. My own negative control caught this: when the child organs could
88 // not even be exec'd (rc 127) the beat still blamed "residue", which is the wrong diagnosis and the
89 // exact misleading-error class this rail exists to kill. A step that COULD NOT RUN and a residue
90 // that SURVIVED are opposite failures with opposite fixes -- never report one as the other.
91 if rc1 != 0 { db_puts("SELFHEAL-BEAT RED -- INSTRUMENT BROKEN: backfill could not run (rc=" as *u8); db_putn(rc1); db_puts("); this is NOT a schema residue\n" as *u8); sys_exit(1); return 1 }
92 if rc2 != 0 { db_puts("SELFHEAL-BEAT RED -- INSTRUMENT BROKEN: publish could not run (rc=" as *u8); db_putn(rc2); db_puts("); this is NOT a schema residue\n" as *u8); sys_exit(1); return 1 }
93 db_puts("SELFHEAL-BEAT RED -- residue survived the backfill; those organs need an AUTHORED header\n" as *u8)
94 sys_exit(1)
95 return 1
96}