code wiki / _hdl_build / nx_patch_guard.nx
nx_patch_guard.nx source
↩ module page · 179 lines · 8128 B
1// nx_patch_guard.nx -- F803 THE PATCH-CASCADE STRUCTURAL TOOTH (ecosystem-as-editor law, operator 2026-07-20).
2// Mechanizes CLAUDE rule-3: "if the second fix does not resolve it, STOP patching -- rewrite the function or
3// module from scratch with a clear understanding of the root cause." Patch-cascade was the editor coverage
4// board's LAST original GAP: a known failure mode of EVERY model (keep poking at a symptom, each patch
5// obscuring intent and adding regression traps) that was covered by process law ALONE -- no structural tooth.
6// This is the READ-ONLY verdict organ over the patch-ledger plane knowledge/store/patchledger- (seats append
7// rows via nx_store_put; SAME division as nx_debt_view over debt- and nx_route_diff over the live API: the
8// plane is data, the organ is the verdict). A seat calls `check <target>` BEFORE its next fix; N consecutive
9// patch-rows on that target since the last rewrite|gate row = REFUSE -> rewrite now.
10// nx_patch_guard check <target> [plane-prefix] -> VERDICT=OK (exit 0) | VERDICT=REFUSE (exit 3)
11// LEDGER ROW (appended via nx_store_put <prefix> put <actor> <id> <target> <kind> <session> <note>):
12// col0 id | col1 target | col2 kind (config|patch|rewrite|gate) | col3 session | col4 note
13// CONFIG row: id=0 target=__config__ kind=config note=<max-consecutive-patches integer> (rule-11:
14// the threshold is DATA, never a magic number in code; absent/unreadable config -> PG_DEFMAX, fail-safe).
15// A rewrite|gate row RESETS the counter -- that is the rule-3 escape hatch made mechanical: prove the rewrite
16// (or a GREEN gate) and the guard opens again. Enforcement-wiring into the nx_ws_cycle debt-gate = filed rung.
17// ENVELOPE (declared in output): 256KB plane read, 4096 rows scanned, 6 cols. Fixture gates prove correctness
18// NEVER scale -- the scale tooth lives in the gate plan (ark v1 lesson).
19// license_tier: ORIGINAL No hw writes (Rule 26). expect_exit: 0
20import "nx_store_seed_lib.nx"
21import "nx_syscalls.nx"
22
23const PG_PREFIX: *u8 = "knowledge/store/patchledger-" as *u8
24const PG_PLANECAP: i64 = 262144
25const PG_SLACK: i64 = 4096
26const PG_NL: i64 = 10
27const PG_TAB: i64 = 9
28const PG_NCOLMAX: i64 = 8
29const PG_PAIR: i64 = 2
30const PG_SPB: i64 = 128
31const PG_MAXROWS: i64 = 4096
32const PG_DEFMAX: i64 = 2
33const PG_OUTCAP: i64 = 4096
34const PG_STDOUT: i64 = 1
35const PG_STDERR: i64 = 2
36const PG_C_TARGET: i64 = 1
37const PG_C_KIND: i64 = 2
38const PG_C_NOTE: i64 = 4
39const PG_NCOL: i64 = 5
40const PG_EXIT_USAGE: i64 = 2
41const PG_EXIT_REFUSE: i64 = 3
42const PG_ZERO: i64 = 48
43const PG_NINE: i64 = 57
44
45func pg_slen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n }
46func pg_werr(s: *u8) -> i64 { sys_write(PG_STDERR, s, pg_slen(s)); return 0 }
47func pg_puts(b: *u8, off: i64, s: *u8) -> i64 { var o: i64 = off; var j: i64 = 0; while s[j] != (0 as u8) { b[o] = s[j]; o = o + 1; j = j + 1 } return o }
48func pg_putsl(b: *u8, off: i64, q: *u8, a: i64, e: i64) -> i64 { var o: i64 = off; var i: i64 = a; while i < e { b[o] = q[i]; o = o + 1; i = i + 1 } return o }
49func pg_puti(b: *u8, off: i64, v: i64) -> i64 {
50 var o: i64 = off
51 var m: i64 = v
52 if m < 0 { b[o] = 45 as u8; o = o + 1; m = 0 - m }
53 let t: *u8 = sys_mmap(28)
54 var k: i64 = 0
55 if m == 0 { t[0] = PG_ZERO as u8; k = 1 }
56 while m > 0 { t[k] = (PG_ZERO + (m % 10)) as u8; m = m / 10; k = k + 1 }
57 var i: i64 = 0
58 while i < k { b[o] = t[k - 1 - i]; o = o + 1; i = i + 1 }
59 return o
60}
61// TAB-split one line [ls,le) into sp pairs; returns column count
62func pg_cols(q: *u8, ls: i64, le: i64, sp: *i64) -> i64 {
63 var c: i64 = 0
64 var p: i64 = ls
65 while c < PG_NCOLMAX {
66 var e: i64 = p
67 var s: i64 = 1
68 while s == 1 { if e >= le { s = 0 } else { if q[e] == (PG_TAB as u8) { s = 0 } else { e = e + 1 } } }
69 sp[c*PG_PAIR] = p
70 sp[c*PG_PAIR+1] = e
71 c = c + 1
72 if e >= le { return c }
73 p = e + 1
74 }
75 return c
76}
77// does span [a,b) equal NUL-terminated z?
78func pg_span_is(q: *u8, a: i64, b: i64, z: *u8) -> i64 {
79 let zn: i64 = pg_slen(z)
80 if b - a != zn { return 0 }
81 var i: i64 = 0
82 while i < zn { if q[a+i] != z[i] { return 0 } i = i + 1 }
83 return 1
84}
85// parse span [a,b) as a non-negative integer; -1 if no digits
86func pg_atoi_span(q: *u8, a: i64, b: i64) -> i64 {
87 var v: i64 = 0
88 var got: i64 = 0
89 var i: i64 = a
90 while i < b {
91 let c: i64 = q[i] as i64
92 if c >= PG_ZERO { if c <= PG_NINE { v = v * 10 + (c - PG_ZERO); got = 1 } }
93 i = i + 1
94 }
95 if got == 0 { return 0 - 1 }
96 return v
97}
98func main(argc: i64, argv: *i64) -> i64 {
99 if argc < 3 {
100 pg_werr("usage: nx_patch_guard check <target> [plane-prefix]\n" as *u8)
101 sys_exit(PG_EXIT_USAGE)
102 return PG_EXIT_USAGE
103 }
104 let verb: *u8 = argv[1] as *u8
105 let target: *u8 = argv[2] as *u8
106 var prefix: *u8 = PG_PREFIX
107 if argc > 3 { prefix = argv[3] as *u8 }
108 if verb[0] != (99 as u8) {
109 pg_werr("usage: nx_patch_guard check <target> [plane-prefix]\n" as *u8)
110 sys_exit(PG_EXIT_USAGE)
111 return PG_EXIT_USAGE
112 }
113 let q: *u8 = sys_mmap(PG_PLANECAP)
114 let qn: i64 = sts_load(prefix, q, PG_PLANECAP - PG_SLACK)
115 let sp: *i64 = sys_mmap(PG_SPB) as *i64
116 var maxp: i64 = PG_DEFMAX
117 var consec: i64 = 0
118 var rows: i64 = 0
119 var seen: i64 = 0
120 var scanned: i64 = 0
121 var i: i64 = 0
122 while i < qn {
123 var le: i64 = i
124 var s: i64 = 1
125 while s == 1 { if le >= qn { s = 0 } else { if q[le] == (PG_NL as u8) { s = 0 } else { le = le + 1 } } }
126 if le > i {
127 if scanned < PG_MAXROWS {
128 let nc: i64 = pg_cols(q, i, le, sp)
129 if nc >= PG_NCOL {
130 rows = rows + 1
131 let ts: i64 = sp[PG_C_TARGET*PG_PAIR]
132 let te: i64 = sp[PG_C_TARGET*PG_PAIR+1]
133 if pg_span_is(q, ts, te, "__config__" as *u8) == 1 {
134 let cv: i64 = pg_atoi_span(q, sp[PG_C_NOTE*PG_PAIR], sp[PG_C_NOTE*PG_PAIR+1])
135 if cv > 0 { maxp = cv }
136 } else {
137 if pg_span_is(q, ts, te, target) == 1 {
138 seen = seen + 1
139 let ks: i64 = sp[PG_C_KIND*PG_PAIR]
140 let ke: i64 = sp[PG_C_KIND*PG_PAIR+1]
141 if pg_span_is(q, ks, ke, "patch" as *u8) == 1 { consec = consec + 1 }
142 if pg_span_is(q, ks, ke, "rewrite" as *u8) == 1 { consec = 0 }
143 if pg_span_is(q, ks, ke, "gate" as *u8) == 1 { consec = 0 }
144 }
145 }
146 }
147 scanned = scanned + 1
148 }
149 }
150 i = le + 1
151 }
152 let out: *u8 = sys_mmap(PG_OUTCAP)
153 var o: i64 = 0
154 o = pg_puts(out, o, "PATCH-GUARD target=" as *u8)
155 o = pg_puts(out, o, target)
156 o = pg_puts(out, o, " consecutive_patches=" as *u8)
157 o = pg_puti(out, o, consec)
158 o = pg_puts(out, o, " max=" as *u8)
159 o = pg_puti(out, o, maxp)
160 o = pg_puts(out, o, " target_rows=" as *u8)
161 o = pg_puti(out, o, seen)
162 o = pg_puts(out, o, " ledger_rows=" as *u8)
163 o = pg_puti(out, o, rows)
164 o = pg_puts(out, o, " (envelope: 4096 rows scanned, 256KB plane, 5-col; threshold is a CONFIG row not code)\n" as *u8)
165 if consec >= maxp {
166 o = pg_puts(out, o, "VERDICT=REFUSE rule-3: " as *u8)
167 o = pg_puti(out, o, consec)
168 o = pg_puts(out, o, " consecutive patches on this target with no rewrite or GREEN gate between them. STOP patching -- rewrite the function or module from a clear root-cause understanding, then append a rewrite row to reopen the guard.\n" as *u8)
169 sys_write(PG_STDOUT, out, o)
170 sys_exit(PG_EXIT_REFUSE)
171 return PG_EXIT_REFUSE
172 }
173 o = pg_puts(out, o, "VERDICT=OK patch permitted, " as *u8)
174 o = pg_puti(out, o, maxp - consec)
175 o = pg_puts(out, o, " remaining before rule-3 requires a rewrite.\n" as *u8)
176 sys_write(PG_STDOUT, out, o)
177 sys_exit(0)
178 return 0
179}