code wiki / _hdl_build / nx_plane_repair.nx
nx_plane_repair.nx source
↩ module page · 154 lines · 9064 B
1// nx_plane_repair.nx -- the MISSING RECOVERY VERB for a lossy seg-store plane.
2//
3// THE HOLE THIS FILLS. When a plane's declared `q:n` exceeds what the loader can reach, every writer
4// correctly refuses: *"lossy load ... committing would BAKE the loss into the next generation. NOTHING
5// COMMITTED."* nx_plane_migrate says **REPAIR-FIRST**. Nothing repairs. So the plane is write-blocked
6// forever — measured live 2026-07-30 on the debt- plane, the ecosystem's own ledger of what is broken:
7// it could not record that it was broken.
8// ★★A GUARD THAT IS FAIL-SAFE BUT HAS NO RECOVERY VERB IS A PERMANENT DENIAL OF SERVICE ON ITSELF.
9// The refusal is right. The missing half is a deliberate, loss-RECORDING reconciliation.
10//
11// ★IT DOES NOT HIDE THE DAMAGE — that is the whole difference between this and a migrator that "fixes"
12// a plane by making it self-consistent. A sibling shipped exactly that mistake and wrote the law:
13// *"A MIGRATOR MUST NEVER MAKE A DAMAGED PLANE LOOK HEALTHY. The rows were unreachable either way; what I
14// removed was the ALARM."* So this writes a durable RECEIPT (declared/loaded/lost/epoch/prefix) BEFORE it
15// rewrites anything. The alarm is not deleted; it is PROMOTED from "everything is blocked forever" into
16// "here is exactly what was lost, recorded permanently, and writes work again".
17//
18// ★THE LOST ROWS ARE NOT RECOVERABLE. Their keys are absent from every segment — sts_seed reassigns q:<i>
19// each generation, so the old rows are not merely unindexed, they are gone. This organ makes that fact
20// explicit and permanent rather than pretending a repair restored them.
21//
22// ---- REFUSALS (each one is a way this could destroy data, so each is checked BEFORE any write) ----
23// 1. NOT-LOSSY declared <= loaded ⇒ nothing to repair. Idempotent: safe to re-run.
24// 2. READ-TRUNCATED rows counted in the BUFFER != flags[1]. ★THE LETHAL ONE: sts_load_honest increments
25// flags[1] for every row it FINDS, but sts_emit_row is bounded by `cap` — so an undersized buffer
26// silently drops rows the counter still counted. Reseeding from that buffer would DESTROY the tail,
27// turning a recoverable index problem into real data loss. Refuse unless the two agree exactly.
28// 3. ROWS-BEYOND-QN flags[2] > 0 ⇒ rows exist past the declared count. That is a DIFFERENT defect
29// (an older seeding generation) and reconciling it needs a generation diff, not a reseed.
30// 4. NO-CONFIRM acknowledging permanent loss is an operator act, never a side effect.
31//
32// usage: nx_plane_repair <store-prefix> [confirm=yes] (CWD = the store's root)
33// exit 0 = repaired (or nothing to repair) · 2 = refused, nothing written · 3 = verify failed after write
34// license_tier: ORIGINAL
35import "nx_syscalls.nx"
36import "nx_store_seed_lib.nx"
37
38// Generous read buffer. This must exceed the largest plane by a wide margin: undersizing it is refusal
39// #2, not corruption, but a refusal you hit every time is a tool nobody can use.
40const PR_CAP: i64 = 33554432
41
42func w(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 }
43func wn(v: i64) -> i64 { let t: *u8=sys_mmap(28); var m: i64=v; var k: i64=0; if m==0{t[0]=48 as u8;k=1} while m>0{t[k]=(48+(m%10)) as u8;m=m/10;k=k+1} let b: *u8=sys_mmap(28); var i: i64=0; while i<k{b[i]=t[k-1-i];i=i+1} sys_write(1,b,k); return 0 }
44func wf(fd: i64, s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(fd,s,n); return 0 }
45func wnf(fd: i64, v: i64) -> i64 { let t: *u8=sys_mmap(28); var m: i64=v; var k: i64=0; if m==0{t[0]=48 as u8;k=1} while m>0{t[k]=(48+(m%10)) as u8;m=m/10;k=k+1} let b: *u8=sys_mmap(28); var i: i64=0; while i<k{b[i]=t[k-1-i];i=i+1} sys_write(fd,b,k); return 0 }
46func streq(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 }
47
48func pr_count_rows(buf: *u8, n: i64) -> i64 {
49 var r: i64 = 0
50 var i: i64 = 0
51 while i < n { if buf[i] == (10 as u8) { r = r + 1 } i = i + 1 }
52 return r
53}
54
55func main(argc: i64, argv: *i64) -> i64 {
56 if argc < 2 { w("usage: nx_plane_repair <store-prefix> [confirm=yes]\n"); w(" reconciles a lossy plane's q:n to what is actually reachable, recording the loss in a receipt.\n"); sys_exit(2); return 2 }
57 let prefix: *u8 = argv[1] as *u8
58 var confirmed: i64 = 0
59 if argc > 2 { if streq(argv[2] as *u8, "confirm=yes" as *u8) == 1 { confirmed = 1 } }
60
61 w("=== nx_plane_repair -- reconcile a lossy plane, RECORD the loss, restore writes ===\n")
62 w(" prefix="); w(prefix); w("\n")
63
64 let buf: *u8 = sys_mmap(PR_CAP)
65 let f: *i64 = sys_mmap(64) as *i64
66 let n: i64 = sts_load_honest(prefix, buf, PR_CAP, f)
67 let declared: i64 = f[0]
68 let found: i64 = f[1]
69 let beyond: i64 = f[2]
70 let inbuf: i64 = pr_count_rows(buf, n)
71
72 w(" declared_qn="); wn(declared)
73 w(" found="); wn(found)
74 w(" rows_in_buffer="); wn(inbuf)
75 w(" beyond_qn="); wn(beyond)
76 w(" bytes="); wn(n); w("\n")
77
78 // ---- REFUSAL 2 FIRST: a truncated read makes every other number a lie ----
79 if inbuf != found {
80 w(" REFUSED read-truncated: the loader FOUND "); wn(found)
81 w(" rows but only "); wn(inbuf)
82 w(" reached the buffer -- sts_emit_row is cap-bounded and drops silently while the counter keeps counting.\n")
83 w(" Reseeding from this buffer would DESTROY the difference. Raise PR_CAP and re-run. NOTHING WRITTEN.\n")
84 sys_exit(2); return 2
85 }
86 // ---- REFUSAL 3: rows past the declared count are a different defect ----
87 if beyond > 0 {
88 w(" REFUSED rows-beyond-qn: "); wn(beyond)
89 w(" rows are reachable PAST the declared count -- an older seeding generation. Reseeding would revive\n")
90 w(" eaten rows and duplicate live ones. That needs a generation diff, not a reconcile. NOTHING WRITTEN.\n")
91 sys_exit(2); return 2
92 }
93 // ---- REFUSAL 1: idempotent no-op ----
94 if declared <= found {
95 w(" NOTHING TO REPAIR: declared <= reachable, the plane is self-consistent.\n")
96 w("VERDICT: verdict=GREEN (no loss to reconcile)\n")
97 sys_exit(0); return 0
98 }
99
100 let lost: i64 = declared - found
101 w(" LOSSY: "); wn(lost); w(" row(s) declared but unreachable. These are NOT RECOVERABLE -- their keys are\n")
102 w(" absent from every segment, and sts_seed reassigns q:<i> per generation, so they are gone, not hidden.\n")
103
104 // ---- REFUSAL 4: acknowledging permanent loss is an operator act ----
105 if confirmed == 0 {
106 w(" REFUSED no-confirm: repairing writes a NEW generation whose q:n equals the reachable count, which\n")
107 w(" permanently accepts the loss above. Re-run with confirm=yes once you accept it. NOTHING WRITTEN.\n")
108 sys_exit(2); return 2
109 }
110
111 // ---- RECEIPT BEFORE REWRITE. The alarm becomes a record; it is never simply deleted. ----
112 let rp: *u8 = sys_mmap(512)
113 var o: i64 = 0
114 let pre: *u8 = "knowledge/status/plane_repair_receipt.log"
115 var q: i64 = 0
116 while pre[q] != (0 as u8) { rp[o] = pre[q]; o = o + 1; q = q + 1 }
117 rp[o] = 0 as u8
118 let rfd: i64 = sys_openat_append(rp, 0x1a4)
119 if rfd >= 0 {
120 wf(rfd, "PLANE-REPAIR prefix=" as *u8); wf(rfd, prefix)
121 wf(rfd, " declared_qn=" as *u8); wnf(rfd, declared)
122 wf(rfd, " reachable=" as *u8); wnf(rfd, found)
123 wf(rfd, " lost_unrecoverable=" as *u8); wnf(rfd, lost)
124 wf(rfd, " bytes=" as *u8); wnf(rfd, n)
125 wf(rfd, " epoch=" as *u8); wnf(rfd, sys_now_realtime_sec())
126 wf(rfd, " ts=" as *u8); wnf(rfd, sys_now_realtime_sec())
127 wf(rfd, "\n" as *u8)
128 sys_close(rfd)
129 w(" receipt appended: "); w(rp); w("\n")
130 } else { w(" WARNING: could not append the receipt -- proceeding would lose the record of the loss. NOTHING WRITTEN.\n"); sys_exit(2); return 2 }
131
132 // ---- REWRITE: sts_seed recomputes q:n from rows actually added, and now CHECKS every ss_add ----
133 let seeded: i64 = sts_seed(prefix, buf, n)
134 if seeded < 0 {
135 w(" REPAIR FAILED: sts_seed returned "); wn(0 - seeded)
136 w(" (negative sentinel) -- the plane is unchanged because commit is all-or-nothing.\n")
137 sys_exit(3); return 3
138 }
139 w(" reseeded rows="); wn(seeded); w("\n")
140
141 // ---- VERIFY BY RE-READING. A repair that reports success without re-measuring is the same class of
142 // claim this whole workstream exists to delete. ----
143 let vbuf: *u8 = sys_mmap(PR_CAP)
144 let vf: *i64 = sys_mmap(64) as *i64
145 let vn: i64 = sts_load_honest(prefix, vbuf, PR_CAP, vf)
146 w(" verify: declared_qn="); wn(vf[0]); w(" found="); wn(vf[1]); w(" beyond="); wn(vf[2]); w(" bytes="); wn(vn); w("\n")
147 if vf[0] != vf[1] {
148 w("VERDICT: verdict=RED (still lossy after repair -- do NOT retry blindly, investigate)\n")
149 sys_exit(3); return 3
150 }
151 w("VERDICT: verdict=GREEN (plane self-consistent, writes unblocked, "); wn(lost); w(" lost row(s) recorded in the receipt)\n")
152 sys_exit(0)
153 return 0
154}