code wiki / (root) / nx_recovery.nx

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}