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}