nx_toolreg_reconcile.nx source
↩ module page · 89 lines · 5929 B
1// nx_toolreg_reconcile.nx -- CLI half of the discovery reconciler / drift auditor. Run from nishihost CWD:
2// nx_toolreg_reconcile [allowlist] [schemas] reconcile (register missing rows); default paths
3// nx_toolreg_reconcile check [allowlist] [schemas] AUDIT: register NOTHING, exit 1 if drift>0
4// Reconcile registers every GREEN allowlisted tool missing from discovery using the author's own schema
5// row; skips (reports) tools with no schema row. Idempotent. `check` is the read-only regression tooth:
6// it exits non-zero if ANY GREEN tool is unregistered or schema-less -- run it in the standing sweep so
7// the discovery-complete state (allowlist == tools/list) can never silently regrow.
8// exit: 0 clean | 1 io / failed rows / (check) drift>0 license_tier: ORIGINAL
9import "nx_toolreg_reconcile_lib.nx"
10
11const RC_ARG_ALLOW: i64 = 1 // argv: optional allowlist path (reconcile mode)
12const RC_ARG_SCH: i64 = 2 // argv: optional schemas path (reconcile mode)
13const RC_ARGC_A: i64 = 2 // argc with allowlist
14const RC_ARGC_S: i64 = 3 // argc with both
15const RC_CK_ARG_ALLOW: i64 = 2 // argv: optional allowlist path (check mode, after the verb)
16const RC_CK_ARG_SCH: i64 = 3 // argv: optional schemas path (check mode)
17const RC_CK_ARGC_A: i64 = 3 // argc: check + allowlist
18const RC_CK_ARGC_S: i64 = 4 // argc: check + allowlist + schemas
19const RC_ASCII_0: i64 = 48 // '0'
20const RC_DEC: i64 = 10 // decimal base
21
22func rc_puts(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 }
23func rc_putn(v: i64) -> i64 {
24 if v == 0 { sys_write(1, "0" as *u8, 1); return 0 }
25 var m: i64 = v; if m < 0 { sys_write(1, "-" as *u8, 1); m = 0 - m }
26 let d: *u8 = sys_mmap(32); var k: i64 = 0
27 while m > 0 { d[k] = (RC_ASCII_0 + (m % RC_DEC)) as u8; m = m / RC_DEC; k = k + 1 }
28 let o: *u8 = sys_mmap(32); var i: i64 = 0
29 while i < k { o[i] = d[k-1-i]; i = i + 1 } sys_write(1, o, k)
30 return 0
31}
32
33func rc_seq(a: *u8, b: *u8) -> i64 { var i: i64 = 0; while a[i] != (0 as u8) { if a[i] != b[i] { return 0 } i = i + 1 } if b[i] != (0 as u8) { return 0 } return 1 }
34
35func main(argc: i64, argv: *i64) -> i64 {
36 var allow: *u8 = "tool_allowlist.conf" as *u8
37 var sch: *u8 = "knowledge/tool_schemas.conf" as *u8
38 let counts: *i64 = sys_mmap(RR_COUNTS) as *i64
39 // AUDIT MODE: `check [allow] [schemas]` -- register nothing, exit 1 if drift>0 (the regression tooth)
40 if argc >= RC_ARGC_A { if rc_seq(argv[RC_ARG_ALLOW] as *u8, "check" as *u8) == 1 {
41 if argc >= RC_CK_ARGC_A { allow = argv[RC_CK_ARG_ALLOW] as *u8 }
42 if argc >= RC_CK_ARGC_S { sch = argv[RC_CK_ARG_SCH] as *u8 }
43 let drift: i64 = rr_drift(TOOL_PREFIX, allow, sch, counts)
44 if drift < 0 { rc_puts("NX-TOOLREG-AUDIT IO: cannot read allowlist/schemas\n" as *u8); return 1 }
45 rc_puts("NX-TOOLREG-AUDIT drift=" as *u8); rc_putn(drift)
46 rc_puts(" (would-register=" as *u8); rc_putn(counts[RR_C_REG])
47 rc_puts(" no-schema=" as *u8); rc_putn(counts[RR_C_NOSCHEMA])
48 rc_puts(" already-discoverable=" as *u8); rc_putn(counts[RR_C_ALREADY])
49 rc_puts(") verdict=" as *u8)
50 if drift == 0 { rc_puts("GREEN (allowlist == tools/list; every tool contracted)\n" as *u8); return 0 }
51 rc_puts("RED (run reconcile / add missing tool_schemas.conf rows)\n" as *u8)
52 return 1
53 } }
54 // HEAL MODE: `heal [allow] [schemas]` -- SELF-MANAGING. Auto-register all has-schema tools (the
55 // mechanical drift that needs no human), then report ONLY the genuine no-schema residue (named). Exit 0
56 // when the residue is empty (discovery self-healed), exit 1 when tools are allowlisted with NO contract
57 // (real AUTHOR debt a human must fix). This is what the standing sweep runs: it FIXES what it can and
58 // reds only on the un-authorable -- retiring the manual-reconcile treadmill.
59 if argc >= RC_ARGC_A { if rc_seq(argv[RC_ARG_ALLOW] as *u8, "heal" as *u8) == 1 {
60 if argc >= RC_CK_ARGC_A { allow = argv[RC_CK_ARG_ALLOW] as *u8 }
61 if argc >= RC_CK_ARGC_S { sch = argv[RC_CK_ARG_SCH] as *u8 }
62 if rr_reconcile(TOOL_PREFIX, allow, sch, counts) < 0 { rc_puts("NX-TOOLREG-HEAL IO: cannot read allowlist/schemas\n" as *u8); return 1 }
63 let names: *u8 = sys_mmap(RR_FILE_CAP)
64 let ns: i64 = rr_no_schema_names(TOOL_PREFIX, allow, sch, names, RR_FILE_CAP)
65 rc_puts("NX-TOOLREG-HEAL auto-registered=" as *u8); rc_putn(counts[RR_C_REG])
66 rc_puts(" already=" as *u8); rc_putn(counts[RR_C_ALREADY])
67 rc_puts(" no-schema-residue=" as *u8); rc_putn(ns)
68 rc_puts(" verdict=" as *u8)
69 if ns == 0 { rc_puts("GREEN (discovery self-healed; every tool contracted)\n" as *u8); return 0 }
70 rc_puts("RED needs-author-schema: " as *u8); rc_puts(names); rc_puts("\n" as *u8)
71 return 1
72 } }
73 // RECONCILE MODE (default): register missing rows
74 if argc >= RC_ARGC_A { allow = argv[RC_ARG_ALLOW] as *u8 }
75 if argc >= RC_ARGC_S { sch = argv[RC_ARG_SCH] as *u8 }
76 let rows: i64 = rr_reconcile(TOOL_PREFIX, allow, sch, counts)
77 if rows < 0 { rc_puts("NX-TOOLREG-RECONCILE IO: cannot read allowlist/schemas\n" as *u8); return 1 }
78 rc_puts("NX-TOOLREG-RECONCILE rows=" as *u8); rc_putn(rows)
79 rc_puts(" registered=" as *u8); rc_putn(counts[RR_C_REG])
80 rc_puts(" already=" as *u8); rc_putn(counts[RR_C_ALREADY])
81 // UPDATED is reported separately from REGISTERED so a description CORRECTION is visible as
82 // work done, not hidden inside 'already' (debt seq1526). 0 on a no-change run = idempotence.
83 rc_puts(" updated=" as *u8); rc_putn(counts[RR_C_UPDATED])
84 rc_puts(" skipped-no-schema=" as *u8); rc_putn(counts[RR_C_NOSCHEMA])
85 rc_puts(" failed=" as *u8); rc_putn(counts[RR_C_FAIL])
86 rc_puts("\n" as *u8)
87 if counts[RR_C_FAIL] > 0 { return 1 }
88 return 0
89}