code wiki / _hdl_build / nx_patch_guard.nx
nx_patch_guard.nx source
↩ module page · 176 lines · 7956 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}
61func pg_cols(q: *u8, ls: i64, le: i64, sp: *i64) -> i64 {
62 var c: i64 = 0
63 var p: i64 = ls
64 while c < PG_NCOLMAX {
65 var e: i64 = p
66 var s: i64 = 1
67 while s == 1 { if e >= le { s = 0 } else { if q[e] == (PG_TAB as u8) { s = 0 } else { e = e + 1 } } }
68 sp[c*PG_PAIR] = p
69 sp[c*PG_PAIR+1] = e
70 c = c + 1
71 if e >= le { return c }
72 p = e + 1
73 }
74 return c
75}
76func pg_span_is(q: *u8, a: i64, b: i64, z: *u8) -> i64 {
77 let zn: i64 = pg_slen(z)
78 if b - a != zn { return 0 }
79 var i: i64 = 0
80 while i < zn { if q[a+i] != z[i] { return 0 } i = i + 1 }
81 return 1
82}
83func pg_atoi_span(q: *u8, a: i64, b: i64) -> i64 {
84 var v: i64 = 0
85 var got: i64 = 0
86 var i: i64 = a
87 while i < b {
88 let c: i64 = q[i] as i64
89 if c >= PG_ZERO { if c <= PG_NINE { v = v * 10 + (c - PG_ZERO); got = 1 } }
90 i = i + 1
91 }
92 if got == 0 { return 0 - 1 }
93 return v
94}
95func main(argc: i64, argv: *i64) -> i64 {
96 if argc < 3 {
97 pg_werr("usage: nx_patch_guard check <target> [plane-prefix]\n" as *u8)
98 sys_exit(PG_EXIT_USAGE)
99 return PG_EXIT_USAGE
100 }
101 let verb: *u8 = argv[1] as *u8
102 let target: *u8 = argv[2] as *u8
103 var prefix: *u8 = PG_PREFIX
104 if argc > 3 { prefix = argv[3] as *u8 }
105 if verb[0] != (99 as u8) {
106 pg_werr("usage: nx_patch_guard check <target> [plane-prefix]\n" as *u8)
107 sys_exit(PG_EXIT_USAGE)
108 return PG_EXIT_USAGE
109 }
110 let q: *u8 = sys_mmap(PG_PLANECAP)
111 let qn: i64 = sts_load(prefix, q, PG_PLANECAP - PG_SLACK)
112 let sp: *i64 = sys_mmap(PG_SPB) as *i64
113 var maxp: i64 = PG_DEFMAX
114 var consec: i64 = 0
115 var rows: i64 = 0
116 var seen: i64 = 0
117 var scanned: i64 = 0
118 var i: i64 = 0
119 while i < qn {
120 var le: i64 = i
121 var s: i64 = 1
122 while s == 1 { if le >= qn { s = 0 } else { if q[le] == (PG_NL as u8) { s = 0 } else { le = le + 1 } } }
123 if le > i {
124 if scanned < PG_MAXROWS {
125 let nc: i64 = pg_cols(q, i, le, sp)
126 if nc >= PG_NCOL {
127 rows = rows + 1
128 let ts: i64 = sp[PG_C_TARGET*PG_PAIR]
129 let te: i64 = sp[PG_C_TARGET*PG_PAIR+1]
130 if pg_span_is(q, ts, te, "__config__" as *u8) == 1 {
131 let cv: i64 = pg_atoi_span(q, sp[PG_C_NOTE*PG_PAIR], sp[PG_C_NOTE*PG_PAIR+1])
132 if cv > 0 { maxp = cv }
133 } else {
134 if pg_span_is(q, ts, te, target) == 1 {
135 seen = seen + 1
136 let ks: i64 = sp[PG_C_KIND*PG_PAIR]
137 let ke: i64 = sp[PG_C_KIND*PG_PAIR+1]
138 if pg_span_is(q, ks, ke, "patch" as *u8) == 1 { consec = consec + 1 }
139 if pg_span_is(q, ks, ke, "rewrite" as *u8) == 1 { consec = 0 }
140 if pg_span_is(q, ks, ke, "gate" as *u8) == 1 { consec = 0 }
141 }
142 }
143 }
144 scanned = scanned + 1
145 }
146 }
147 i = le + 1
148 }
149 let out: *u8 = sys_mmap(PG_OUTCAP)
150 var o: i64 = 0
151 o = pg_puts(out, o, "PATCH-GUARD target=" as *u8)
152 o = pg_puts(out, o, target)
153 o = pg_puts(out, o, " consecutive_patches=" as *u8)
154 o = pg_puti(out, o, consec)
155 o = pg_puts(out, o, " max=" as *u8)
156 o = pg_puti(out, o, maxp)
157 o = pg_puts(out, o, " target_rows=" as *u8)
158 o = pg_puti(out, o, seen)
159 o = pg_puts(out, o, " ledger_rows=" as *u8)
160 o = pg_puti(out, o, rows)
161 o = pg_puts(out, o, " (envelope: 4096 rows scanned, 256KB plane, 5-col; threshold is a CONFIG row not code)\n" as *u8)
162 if consec >= maxp {
163 o = pg_puts(out, o, "VERDICT=REFUSE rule-3: " as *u8)
164 o = pg_puti(out, o, consec)
165 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)
166 sys_write(PG_STDOUT, out, o)
167 sys_exit(PG_EXIT_REFUSE)
168 return PG_EXIT_REFUSE
169 }
170 o = pg_puts(out, o, "VERDICT=OK patch permitted, " as *u8)
171 o = pg_puti(out, o, maxp - consec)
172 o = pg_puts(out, o, " remaining before rule-3 requires a rewrite.\n" as *u8)
173 sys_write(PG_STDOUT, out, o)
174 sys_exit(0)
175 return 0
176}