code wiki / (root) / nx_maint_ledger.nx

nx_maint_ledger.nx source

↩ module page · 137 lines · 4566 B

1// nx_maint_ledger.nx -- R4: the additive maintenance ledger (#13, history is 2// sacred). Append-only record of every asset's health results over time. Its 3// value is PREDICTIVE: a worsening severity trend warns BEFORE failure, and a 4// past SAFETY event is NEVER forgotten even after the asset recovers. 5// 6// Operator (2026-06-23): build "from the hardware rung up each rung" -- this is 7// R4 on top of R3's unified MaintResult. CLAUDE.md #13: additive-only, never 8// DELETE; history enables debugging, rollback, and degradation analysis. 9// 10// (Pure-buffer ledger LOGIC + trend; durable seg-store backing is a thin next 11// layer, same append-to-store pattern as nx_ciq_field_maintenance.) 12// 13// NEVER-BRICK (#26): append + query; no syscalls, no device writes, NO delete. 14// 15// genealogy_id: project-maintenance-platform-sclass-2026-06-23 (R4 ledger) 16// license_tier: ORIGINAL 17 18import "nx_maint_unify.nx" // NX_HEALTH_* severities (stored), MaintResult shape 19 20// ---- Severity trend over an asset's history ------------------------ 21const NX_TREND_NODATA: i64 = 0 22const NX_TREND_IMPROVING: i64 = 1 23const NX_TREND_STABLE: i64 = 2 24const NX_TREND_WORSENING: i64 = 3 25const NX_TREND_N: i64 = 4 26 27func nx_trend_is_valid(t: i64) -> i64 { 28 if t < 0 { return 0 } 29 if t >= NX_TREND_N { return 0 } 30 return 1 31} 32 33struct LedgerEntry { 34 asset_id: i64, 35 ts: i64, 36 severity: i64, // NX_HEALTH_* 37 kernel: i64, 38 native_verdict: i64, 39 headline: i64, 40} 41 42struct Ledger { 43 cap: i64, 44 count: i64, 45} 46 47func nx_ledger_init(led: *Ledger, cap: i64) -> i64 { 48 led.cap = cap 49 led.count = 0 50 return 0 51} 52 53// Append a result (ADDITIVE -- never overwrites a prior entry). Returns the 54// new entry index (>=0), or -1 if the ledger is full (still no overwrite). 55func nx_ledger_append(led: *Ledger, entries: *LedgerEntry, asset_id: i64, ts: i64, 56 severity: i64, kernel: i64, native_verdict: i64, headline: i64) -> i64 { 57 if led.count >= led.cap { 58 return 0 - 1 59 } 60 let i: i64 = led.count 61 entries[i].asset_id = asset_id 62 entries[i].ts = ts 63 entries[i].severity = severity 64 entries[i].kernel = kernel 65 entries[i].native_verdict = native_verdict 66 entries[i].headline = headline 67 led.count = led.count + 1 68 return i 69} 70 71func nx_ledger_count_for(led: *Ledger, entries: *LedgerEntry, asset_id: i64) -> i64 { 72 var c: i64 = 0 73 var i: i64 = 0 74 while i < led.count { 75 if entries[i].asset_id == asset_id { c = c + 1 } 76 i = i + 1 77 } 78 return c 79} 80 81// Worst (max) severity EVER recorded for an asset -- history sacred: a past 82// hazard is never forgotten even if the latest reading is OK. -1 if none. 83func nx_ledger_worst_for(led: *Ledger, entries: *LedgerEntry, asset_id: i64) -> i64 { 84 var w: i64 = 0 - 1 85 var i: i64 = 0 86 while i < led.count { 87 if entries[i].asset_id == asset_id { 88 if entries[i].severity > w { w = entries[i].severity } 89 } 90 i = i + 1 91 } 92 return w 93} 94 95// Most recent entry for an asset -> *out. Returns 1 if found, 0 if none. 96func nx_ledger_latest_for(led: *Ledger, entries: *LedgerEntry, asset_id: i64, out: *LedgerEntry) -> i64 { 97 var i: i64 = led.count - 1 98 while i >= 0 { 99 if entries[i].asset_id == asset_id { 100 out.asset_id = entries[i].asset_id 101 out.ts = entries[i].ts 102 out.severity = entries[i].severity 103 out.kernel = entries[i].kernel 104 out.native_verdict = entries[i].native_verdict 105 out.headline = entries[i].headline 106 return 1 107 } 108 i = i - 1 109 } 110 return 0 111} 112 113// Recent direction for an asset: latest vs the immediately-previous reading. 114// WORSENING (degradation -> act before failure), IMPROVING, STABLE, or NODATA. 115func nx_ledger_trend_for(led: *Ledger, entries: *LedgerEntry, asset_id: i64) -> i64 { 116 var latest_sev: i64 = 0 - 1 117 var prev_sev: i64 = 0 - 1 118 var found: i64 = 0 119 var i: i64 = led.count - 1 120 while i >= 0 { 121 if entries[i].asset_id == asset_id { 122 if found == 0 { 123 latest_sev = entries[i].severity 124 found = 1 125 } else { 126 prev_sev = entries[i].severity 127 found = 2 128 i = 0 - 1 129 } 130 } 131 i = i - 1 132 } 133 if found < 2 { return NX_TREND_NODATA } 134 if latest_sev > prev_sev { return NX_TREND_WORSENING } 135 if latest_sev < prev_sev { return NX_TREND_IMPROVING } 136 return NX_TREND_STABLE 137}