nx_recovery.nx source
↩ module page · 142 lines · 5248 B
1// nx_recovery.nx -- recovery procedure invocation.
2//
3// When nx_health_check fires RED + nx_health_should_trigger_recovery
4// returns 1, the substrate fires nx_recovery_execute. Recovery
5// sequences a series of mitigations:
6// 1. Pause new cell admission
7// 2. nx_lysis on any confirmed-hostile xenocells
8// 3. nx_promote any cells in ABORTIVE state
9// 4. nx_aerobic mark stale resources for decomposition
10// 5. Write a RED-triggered book entry (severity=ALERT)
11// 6. Notify operator (via book unseen-alerts count)
12//
13// Per [[feedback-captain-moroni-doctrine]]: recovery is DEFENSIVE.
14// We never attack back. We restore substrate function for the user.
15//
16// Composes:
17// nx_health_check -- supplies the RED verdict that triggers
18// nx_lysis -- selective foreign-entity neutralization
19// nx_promote -- failover from compromised to backup cell
20// nx_aerobic -- decompose stale resources cleanly
21// nx_book -- RED transition logged as ALERT entry
22
23import "nx_syscalls.nx"
24import "nx_tier.nx"
25
26const NX_RC_OK: nx_int = 0
27const NX_RC_ERR_BAD_PHASE: nx_int = 1
28const NX_RC_ERR_NOT_TRIGGERED: nx_int = 2
29
30// ===== Sealed enum: NxRecoveryPhase ===============================
31
32const NX_RP_NOT_STARTED: nx_int = 0
33const NX_RP_PAUSE_ADMISSION: nx_int = 1
34const NX_RP_LYSE_HOSTILE: nx_int = 2
35const NX_RP_PROMOTE_BACKUPS: nx_int = 3
36const NX_RP_DECOMPOSE_STALE: nx_int = 4
37const NX_RP_NOTIFY_OPERATOR: nx_int = 5
38const NX_RP_COMPLETE: nx_int = 6
39const NX_RP_N_PHASES: nx_int = 7
40
41// ===== Struct: NxRecoverySession ==================================
42
43struct NxRecoverySession {
44 session_id: nx_int,
45 triggered_us: nx_size,
46 current_phase: nx_int,
47 cells_lysed: nx_int,
48 cells_promoted: nx_int,
49 resources_composted: nx_int,
50 completed_us: nx_size,
51}
52
53func nx_rp_is_valid(p: nx_int) -> nx_int {
54 if p < 0 { return 0 }
55 if p >= NX_RP_N_PHASES { return 0 }
56 return 1
57}
58
59func nx_recovery_new(session_id: nx_int, now_us: nx_size) -> *NxRecoverySession {
60 let r: *NxRecoverySession = (sys_mmap(56)) as *NxRecoverySession
61 r.session_id = session_id
62 r.triggered_us = now_us
63 r.current_phase = NX_RP_NOT_STARTED
64 r.cells_lysed = 0
65 r.cells_promoted = 0
66 r.resources_composted = 0
67 r.completed_us = 0
68 return r
69}
70
71// ===== nx_recovery_advance ========================================
72//
73// Move to next phase. Returns NX_RC_OK if transition allowed, else
74// BAD_PHASE. Phases progress strictly: NOT_STARTED → PAUSE_ADMISSION
75// → LYSE_HOSTILE → PROMOTE_BACKUPS → DECOMPOSE_STALE → NOTIFY_OPERATOR
76// → COMPLETE. No skipping.
77
78func nx_recovery_advance(r: *NxRecoverySession, now_us: nx_size) -> nx_int {
79 if r.current_phase == NX_RP_COMPLETE { return NX_RC_ERR_BAD_PHASE }
80 r.current_phase = r.current_phase + 1
81 if r.current_phase == NX_RP_COMPLETE {
82 r.completed_us = now_us
83 }
84 return NX_RC_OK
85}
86
87// ===== nx_recovery_record_lysed ===================================
88
89func nx_recovery_record_lysed(r: *NxRecoverySession, n: nx_int) -> nx_int {
90 if r.current_phase != NX_RP_LYSE_HOSTILE { return NX_RC_ERR_BAD_PHASE }
91 r.cells_lysed = r.cells_lysed + n
92 return NX_RC_OK
93}
94
95func nx_recovery_record_promoted(r: *NxRecoverySession, n: nx_int) -> nx_int {
96 if r.current_phase != NX_RP_PROMOTE_BACKUPS { return NX_RC_ERR_BAD_PHASE }
97 r.cells_promoted = r.cells_promoted + n
98 return NX_RC_OK
99}
100
101func nx_recovery_record_composted(r: *NxRecoverySession, n: nx_int) -> nx_int {
102 if r.current_phase != NX_RP_DECOMPOSE_STALE { return NX_RC_ERR_BAD_PHASE }
103 r.resources_composted = r.resources_composted + n
104 return NX_RC_OK
105}
106
107func nx_recovery_phase(r: *NxRecoverySession) -> nx_int {
108 return r.current_phase
109}
110
111func nx_recovery_is_complete(r: *NxRecoverySession) -> nx_int {
112 if r.current_phase == NX_RP_COMPLETE { return 1 }
113 return 0
114}
115
116func nx_recovery_duration_us(r: *NxRecoverySession) -> nx_size {
117 if r.completed_us == 0 { return 0 }
118 if r.completed_us < r.triggered_us { return 0 }
119 return r.completed_us - r.triggered_us
120}
121
122// ===== nx_recovery_run_full =======================================
123//
124// Convenience: advance through all phases with caller's per-phase
125// counts. Used in tests + non-streaming integrations.
126
127func nx_recovery_run_full(r: *NxRecoverySession,
128 n_to_lyse: nx_int,
129 n_to_promote: nx_int,
130 n_to_compost: nx_int,
131 now_us: nx_size) -> nx_int {
132 nx_recovery_advance(r, now_us) // -> PAUSE_ADMISSION
133 nx_recovery_advance(r, now_us) // -> LYSE_HOSTILE
134 nx_recovery_record_lysed(r, n_to_lyse)
135 nx_recovery_advance(r, now_us) // -> PROMOTE_BACKUPS
136 nx_recovery_record_promoted(r, n_to_promote)
137 nx_recovery_advance(r, now_us) // -> DECOMPOSE_STALE
138 nx_recovery_record_composted(r, n_to_compost)
139 nx_recovery_advance(r, now_us) // -> NOTIFY_OPERATOR
140 nx_recovery_advance(r, now_us) // -> COMPLETE
141 return NX_RC_OK
142}