diff --git a/include/raps/safety/deterministic_safety_monitor.hpp b/include/raps/safety/deterministic_safety_monitor.hpp index ede7003..1a40eb3 100644 --- a/include/raps/safety/deterministic_safety_monitor.hpp +++ b/include/raps/safety/deterministic_safety_monitor.hpp @@ -31,6 +31,7 @@ constexpr double MIN_RESONANCE_AMPLITUDE_CUTOFF = 0.10; // WNN Constraints constexpr double WNN_MAX_CURVATURE_PROXY = 5.0e-11; constexpr double WNN_MIN_OSCILLATORY_PREFACTOR = 0.85; +constexpr double INVALID_TELEMETRY_SENTINEL = -1.0; } // namespace DSM_Config @@ -80,6 +81,8 @@ class DeterministicSafetyMonitor { bool safing_sequence_active_; bool hasInvalidInputs(const DsmSensorInputs& inputs) const; + bool hasInvalidWnnTelemetry(const WnnTelemetry& wnn_telem) const; + bool isWnnThresholdBreached(const WnnTelemetry& wnn_telem) const; bool checkResonanceStability(double A_t, double J_coupling) const; double estimateCurvatureScalar(double dilation) const; bool checkCurvatureViolation(double R_estimated) const; @@ -120,6 +123,22 @@ DeterministicSafetyMonitor::hasInvalidInputs( !std::isfinite(inputs.current_resonance_amplitude); } +inline bool +DeterministicSafetyMonitor::hasInvalidWnnTelemetry( + const WnnTelemetry& wnn_telem +) const { + return !std::isfinite(wnn_telem.curvature_proxy) || + !std::isfinite(wnn_telem.oscillatory_prefactor); +} + +inline bool +DeterministicSafetyMonitor::isWnnThresholdBreached( + const WnnTelemetry& wnn_telem +) const { + return wnn_telem.curvature_proxy > DSM_Config::WNN_MAX_CURVATURE_PROXY || + wnn_telem.oscillatory_prefactor < DSM_Config::WNN_MIN_OSCILLATORY_PREFACTOR; +} + inline bool DeterministicSafetyMonitor::checkResonanceStability( double A_t, @@ -193,11 +212,27 @@ DeterministicSafetyMonitor::pollWnnAndEnforce( uint32_t rollback_count, PhysicsState& active_state_pointer ) { - if (wnn_telem.curvature_proxy > DSM_Config::WNN_MAX_CURVATURE_PROXY || - wnn_telem.oscillatory_prefactor < DSM_Config::WNN_MIN_OSCILLATORY_PREFACTOR) { + const bool invalid_wnn_input = hasInvalidWnnTelemetry(wnn_telem); + const bool threshold_breach = isWnnThresholdBreached(wnn_telem); + + if (invalid_wnn_input || threshold_breach) { + // Keep safing active until the broader control loop restores margins. + safing_sequence_active_ = true; + if (invalid_wnn_input) { + std::cerr << "DSM ALERT: Non-finite WNN telemetry detected — ROLLBACK\n"; + } else { + std::cerr << "DSM ALERT: WNN thresholds exceeded — ROLLBACK\n"; + } + + const double logged_curvature = std::isfinite(wnn_telem.curvature_proxy) + ? wnn_telem.curvature_proxy + : DSM_Config::INVALID_TELEMETRY_SENTINEL; + const double logged_prefactor = std::isfinite(wnn_telem.oscillatory_prefactor) + ? wnn_telem.oscillatory_prefactor + : DSM_Config::INVALID_TELEMETRY_SENTINEL; // Breach detected! Log to ITL and execute immediate rollback - itl_manager.log_wnn_rollback_event(wnn_telem.curvature_proxy, wnn_telem.oscillatory_prefactor); + itl_manager.log_wnn_rollback_event(logged_curvature, logged_prefactor); return trigger_wnn_immediate_rollback( rollback_store, diff --git a/src/raps/rollback_execution.hpp b/src/raps/rollback_execution.hpp index 5ec9796..acdd0c4 100644 --- a/src/raps/rollback_execution.hpp +++ b/src/raps/rollback_execution.hpp @@ -53,7 +53,7 @@ inline bool trigger_wnn_immediate_rollback( uint32_t rollback_count, PhysicsState& active_state_pointer ) { - if (rollback_count == 0) { + if (rollback_count == 0 || rollback_store == nullptr) { return false; } diff --git a/tests/sil/test_rollback_execution.cpp b/tests/sil/test_rollback_execution.cpp index 759496b..87ec34e 100644 --- a/tests/sil/test_rollback_execution.cpp +++ b/tests/sil/test_rollback_execution.cpp @@ -76,6 +76,36 @@ void test_rollback_validation() { expect_true(tx_id.length() > 0, "tx_id is generated"); } +void test_wnn_rollback_hardening() { + std::cout << "--- Testing WNN Rollback Hardening ---\n"; + + PhysicsState active_state{}; + active_state.timestamp_ms = 10; + + // 1. Null rollback store with non-zero count must fail safely + bool null_store_result = trigger_wnn_immediate_rollback( + nullptr, + 1, + active_state + ); + expect_false( + null_store_result, + "trigger_wnn_immediate_rollback fails safely for null rollback store" + ); + + // 2. Empty rollback store must fail safely + RollbackPlan store[1]{}; + bool empty_store_result = trigger_wnn_immediate_rollback( + store, + 0, + active_state + ); + expect_false( + empty_store_result, + "trigger_wnn_immediate_rollback fails safely for empty rollback store" + ); +} + int main() { std::cout << "========================================================\n"; std::cout << " SIL TEST: Rollback Execution Logic\n"; @@ -85,6 +115,7 @@ int main() { PlatformHAL::seed_rng_for_stubs(12345); test_rollback_validation(); + test_wnn_rollback_hardening(); std::cout << "--------------------------------------------------------\n"; if (g_failures == 0) {