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}