diff --git a/README.md b/README.md index 747c2d2..8b1eda8 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ **Coherence SRE** is a read-only site reliability analysis framework for high-variance, mission-critical environments. -Rather than relying on static thresholds or trained models, Coherence evaluates second-order changes in system telemetry — variance, rate-of-change, and amplification — to identify early indicators of instability that precede service degradation. +Rather than relying on trained models or opaque scoring, Coherence evaluates deterministic rolling-window signals — variance, rate-of-change, and amplification — against explicit thresholds to identify early indicators of instability that precede service degradation. The framework is designed for deterministic behavior, reproducible analysis, and safe deployment in air-gapped or regulated infrastructure. @@ -61,7 +61,7 @@ graph TD D -->|StdDev Calculation| E[Threshold Logic]; end - E -->|Signal > 2.0σ| F[VETO SIGNAL]; + E -->|Signal > configured limit| F[VETO SIGNAL]; E -->|Stream| G[Glass Cockpit]; class A,B,G logic; diff --git a/src/coherence/core/sentinel.py b/src/coherence/core/sentinel.py index 61e72f9..ab5854d 100644 --- a/src/coherence/core/sentinel.py +++ b/src/coherence/core/sentinel.py @@ -26,6 +26,15 @@ # --- Adapter Imports --- from ..ingestion.adapters import MetricAdapter, LiveAdapter, SimAdapter + +def calculate_amplification_ratio(sent_delta: int, recv_delta: int) -> float: + sent = max(0, sent_delta) + received = max(0, recv_delta) + if received == 0: + return float("inf") if sent > 0 else 1.0 + return sent / received + + class CoherenceSentinel: def __init__(self) -> None: self.history: deque[SystemMetrics] = deque(maxlen=CONFIG.window_size_seconds) @@ -106,7 +115,7 @@ def analyze(self) -> Dict[str, Any]: # Net s_delta = newest.net_sent_packets - oldest.net_sent_packets r_delta = newest.net_recv_packets - oldest.net_recv_packets - amp_ratio = (s_delta / r_delta) if r_delta > 0 else 1.0 + amp_ratio = calculate_amplification_ratio(s_delta, r_delta) metrics = { "cpu_mean": cpu_mean, @@ -125,7 +134,7 @@ def analyze(self) -> Dict[str, Any]: elif amp_ratio > CONFIG.amplification_ratio_limit: veto = {"type": "INTENT", "action": "CAP_RETRIES", "reason": f"Amp {amp_ratio:.2f}x > Limit"} - status = "INSTABLE" if veto else "STABLE" + status = "UNSTABLE" if veto else "STABLE" # 3. Advanced Analysis (The Brain) # Upgrades the analysis to generate a Narrative @@ -182,16 +191,16 @@ def make_dashboard(sentinel_data: Dict[str, Any]) -> Layout: err = sentinel_data.get("error") brain_status = sentinel_data.get("brain", "unknown") - color_map = {"STABLE": "green", "INSTABLE": "red", "WARMUP": "yellow", "ERROR": "magenta", "STALE": "grey50"} + color_map = {"STABLE": "green", "UNSTABLE": "red", "WARMUP": "yellow", "ERROR": "magenta", "STALE": "grey50"} # Priority: Error > Narrative > Veto > Status if narrative and hasattr(narrative, 'risk_score') and narrative.risk_score > 0.5: # Advanced Incident detected status_disp = "CRITICAL" header_col = "red" - elif status == "INSTABLE" and not narrative: + elif status == "UNSTABLE" and not narrative: # Basic Veto only - status_disp = "INSTABLE" + status_disp = "UNSTABLE" header_col = "red" else: status_disp = status diff --git a/tests/test_signal_hygiene.py b/tests/test_signal_hygiene.py index 1d5db0e..d7762bb 100644 --- a/tests/test_signal_hygiene.py +++ b/tests/test_signal_hygiene.py @@ -1,7 +1,12 @@ import time import pytest -from coherence.core.sentinel import CoherenceSentinel, SystemMetrics, CONFIG +from coherence.core.sentinel import ( + CoherenceSentinel, + SystemMetrics, + CONFIG, + calculate_amplification_ratio, +) def test_blindness_veto(): """ @@ -60,3 +65,35 @@ def test_fresh_signal(): assert analysis["status"] == "STABLE" assert analysis["metrics"]["signal_lag"] < 1.0 + + +def test_sent_only_network_delta_triggers_amplification_veto(): + """ + Egress growth with no matching ingress is retry/fan-out amplification, + not a nominal 1:1 ratio. + """ + sentinel = CoherenceSentinel() + now = time.time() + + for i in range(5): + sentinel.ingest( + SystemMetrics( + timestamp=now + i, + cpu_percent=10.0, + memory_used_mb=1024.0, + net_sent_packets=1000 + (i * 100), + net_recv_packets=1000, + ) + ) + + analysis = sentinel.analyze() + + assert analysis["status"] == "UNSTABLE" + assert analysis["veto"]["type"] == "INTENT" + assert analysis["metrics"]["amp_ratio"] == float("inf") + + +def test_amplification_ratio_handles_counter_resets(): + assert calculate_amplification_ratio(sent_delta=-10, recv_delta=100) == 0.0 + assert calculate_amplification_ratio(sent_delta=0, recv_delta=0) == 1.0 + assert calculate_amplification_ratio(sent_delta=100, recv_delta=0) == float("inf")