code wiki / _hdl_build / nx_ws_repair.nx

nx_ws_repair.nx source

↩ module page · 101 lines · 4641 B

1// nx_ws_repair.nx -- WMS-R9: ledger->registry CRASH REPAIR (git's defining reflog-recovery trick). 2// 3// module: nishi-core.wms.ws_repair 4// capability: CORE_COMPUTE (crash-recovery: rebuild the registry SSOT from the append-only reflog) 5// 6// WHAT THIS CLOSES: WMS-R4 (nx_workstream_audit) only DETECTS a lost/corrupt registry record; it 7// does not REPAIR it. Git's defining trick is that the refs (where branches point) can be rebuilt 8// from the reflog (the append-only history of every ref update) -- a corrupt/lost ref is not fatal 9// because the log is the source of truth. THIS is that trick for the WMS: the WMS-R2 transition 10// ledger (nx_ws_ledger) is the reflog; ledger_replay reconstructs the authoritative current state 11// of every workstream; ledger_rebuild WRITES those states back into the registry store, so a wiped 12// or stale registry is reconstructed from history rather than lost. 13// 14// HONEST SCOPE (rule 4, no fabrication): the reflog records the LEDGER's state vocabulary (integers; 15// 0=TODO 1=WIP 2=VIEW 3=DONE 4=NOVEL per nx_ws_ledger_gate) and ONLY the state -- not empire/links/ 16// deps. So a recovered record carries the reflog's NUMERIC state (not invented status labels) and is 17// marked empire="E-RECOVERED" with "-" links (intentionally none -> never false-orphans in R4); the 18// non-state metadata is re-enriched later from the census, exactly as git rebuilds where a ref points 19// (the state) but not the unreachable objects. last_touched=0 ("unknown" per R1 schema) keeps rebuild 20// IDEMPOTENT: re-running on the same reflog yields byte-identical records -> ws_put_p skips unchanged. 21// 22// REUSE (rule 15): ledger_replay (R2) for the authoritative state; ws_put_p (R1) for the writes 23// (recovery is single-writer at boot -- fail-fast-on-startup means the system isn't serving yet, so 24// the R8 inter-writer lock isn't needed here). Sovereign: nx_ws_ledger + nx_workstream_store + nx_syscalls. 25// license_tier: ORIGINAL 26import "nx_ws_ledger.nx" 27import "nx_workstream_store.nx" 28import "nx_syscalls.nx" 29 30// append NUL-term s into dst at off -> new off 31func wr_cat(dst: *u8, off: i64, s: *u8) -> i64 { 32 var i: i64 = 0 33 while s[i] != (0 as u8) { dst[off + i] = s[i]; i = i + 1 } 34 return off + i 35} 36// append decimal of v (>=0) into dst at off -> new off 37func wr_catn(dst: *u8, off: i64, v: i64) -> i64 { 38 var m: i64 = v 39 var o: i64 = off 40 if m == 0 { dst[o] = 48 as u8; return o + 1 } 41 let t: *u8 = sys_mmap(28) 42 var k: i64 = 0 43 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 } 44 var i: i64 = 0 45 while i < k { dst[o + i] = t[k - 1 - i]; i = i + 1 } 46 return o + k 47} 48 49// registry key "ws:<ws>" into out (NUL-term) 50func wr_key(out: *u8, ws: i64) -> i64 { 51 var o: i64 = 0 52 o = wr_cat(out, o, "ws:" as *u8) 53 o = wr_catn(out, o, ws) 54 out[o] = 0 as u8 55 return o 56} 57 58// recovered registry value into out (NUL-term), the R1 7-field schema: 59// "<ws>\tE-RECOVERED\t<state>\t0\t-\t-\t-" 60// state = the reflog's numeric state; last_touched=0 (unknown -> idempotent); links "-" (no orphan). 61func wr_val(out: *u8, ws: i64, st: i64) -> i64 { 62 var o: i64 = 0 63 o = wr_catn(out, o, ws) 64 o = wr_cat(out, o, "\tE-RECOVERED\t" as *u8) 65 o = wr_catn(out, o, st) 66 o = wr_cat(out, o, "\t0\t-\t-\t-" as *u8) 67 out[o] = 0 as u8 68 return o 69} 70 71// THE REPAIR CAPABILITY. Replay `ledger_path` -> authoritative state[ws]; for EVERY ws SEEN in the 72// reflog (state >= 0) write a recovered record into the registry `prefix` via ws_put_p. A ws never 73// in the reflog stays state[ws]==-1 and is NOT written (no phantom ref). Torn/corrupt reflog lines 74// are FLAGGED by ledger_replay (surfaced in outs[2]), never silently mis-applied. 75// outs[0]=lines outs[1]=applied outs[2]=flagged outs[3]=rebuilt 76// Returns the rebuilt count. Idempotent (stable recovered bytes -> ws_put_p skips unchanged). 77func ledger_rebuild(ledger_path: *u8, prefix: *u8, maxws: i64, outs: *i64) -> i64 { 78 let state: *i64 = sys_mmap(8 * maxws) as *i64 79 var i: i64 = 0 80 while i < maxws { state[i] = 0 - 1; i = i + 1 } // -1 = unseen 81 let ro: *i64 = sys_mmap(32) as *i64 82 ledger_replay(ledger_path, state, ro, maxws) 83 let key: *u8 = sys_mmap(128) 84 let val: *u8 = sys_mmap(256) 85 var rebuilt: i64 = 0 86 var ws: i64 = 0 87 while ws < maxws { 88 if state[ws] >= 0 { 89 wr_key(key, ws) 90 wr_val(val, ws, state[ws]) 91 ws_put_p(prefix, key, val) 92 rebuilt = rebuilt + 1 93 } 94 ws = ws + 1 95 } 96 outs[0] = ro[0] 97 outs[1] = ro[1] 98 outs[2] = ro[2] 99 outs[3] = rebuilt 100 return rebuilt 101}