Port the last four audit-missing fns name-preserving into the crate: * _do_core — the facade loader-probe twin (_cs_core law; 4 pin rows). * _load_alerts — the METHOD's file law with the IO peeled off (text arg; None=missing → [], JSONDecodeError → [], valid doc VERBATIM — a non-array document rides through as-is); 5 rows incl. the non-array and corrupt branches. * _save_alerts — the persisted bytes law: json.dumps(alerts, indent=2) (ensure-ascii escapes, integral-float law); 2 rows incl. unicode. * main — the subparser CLI dispatcher (task_persist MainOutcome law): check/retrospective --days/alerts/acknowledge index/dashboard with the help-to-stdout + exit-1 no-cmd shape, exit-2 argparse classes (invalid choice, subparser usage with the per-sub prog prefix, missing/bad int index, bad --days), every print shape (Found N new alerts, the retrospective block, Unacknowledged (N) with [i] prefix, Alert N acknowledged., the full dashboard incl. .2% ratio and True/False bool str), and the in-memory alerts-file state (rendered bytes compared). 20 main rows frozen from the live oracle under a memory_store/drift_core seam rig. serde_json gains preserve_order (dict-order rendering); the rig's frozen stamp is deterministic and the truth keeps insertion order. FIX-FIRST WITH PROOF (pre-existing kernel bug): dob_run_retrospective returned the BARE local stamp as filename, but the facade joins it directly — kernel-live wrote retrospectives/20261011_130000 while the Python path wrote retrospective_20261011_130000.json. The kernel now returns the full name; the replay pass PROVED the divergence pre-fix (kernel 55/64 on the stale .so) and parity post-fix (facade 64/64, kernel 64/64, both core modes writing identical file names). Battery 9/9 green (was 5); drift-family pytest 20/20 BOTH pins; clippy -D warnings clean; gen x2 byte-identical. Fn coverage 5/9 -> 9/9.
729 lines
28 KiB
Python
729 lines
28 KiB
Python
#!/usr/bin/env python3
|
||
"""gen_drift_observability_truth.py — freeze/replay parity truth for
|
||
drift_observability (dichotomy wave, #2479 tranche).
|
||
|
||
Freeze mode (default): exercises EVERY public function of the LIVE Python
|
||
oracle (src/rune/drift_observability.py) across every branch and records
|
||
(kernel_input, expected_kernel_output) pairs into
|
||
src/rust/drift_observability/src/frozen_do_truth.json.
|
||
|
||
Replay mode (--replay): re-runs the SAME scenarios with the Rust core
|
||
delegation active and asserts both kernel-level (loader direct) and
|
||
facade-level (oracle methods delegate) parity against the frozen file.
|
||
|
||
Poisoned-truth law: freeze pins RUNE_DO_CORE=python BEFORE any import.
|
||
No-clock law: the wall clock is frozen by patching the datetime symbol in
|
||
BOTH drift_observability and drift_core, so every kernel payload carries
|
||
its own `now` and replay is deterministic regardless of run date.
|
||
"""
|
||
import copy
|
||
import json
|
||
import os
|
||
import sqlite3
|
||
import sys
|
||
import tempfile
|
||
from datetime import datetime, timedelta, timezone
|
||
|
||
REPO = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||
TRUTH_PATH = os.path.join(
|
||
REPO, "src", "rust", "drift_observability", "src", "frozen_do_truth.json"
|
||
)
|
||
REPLAY = "--replay" in sys.argv
|
||
|
||
if not REPLAY:
|
||
os.environ["RUNE_DO_CORE"] = "python" # poisoned-truth law
|
||
os.environ["RUNE_MEMORY_CORE"] = "python"
|
||
|
||
sys.path.insert(0, os.path.join(REPO, "src"))
|
||
sys.path.insert(0, os.path.join(REPO, "src", "rune"))
|
||
|
||
import drift_core # noqa: E402
|
||
import drift_observability as oracle_mod # noqa: E402
|
||
|
||
if REPLAY:
|
||
import do_loader # noqa: E402 (kernel-level replay side)
|
||
|
||
FROZEN_UTC = datetime(2026, 10, 11, 12, 0, 0, 123456, tzinfo=timezone.utc)
|
||
FROZEN_LOCAL = datetime(2026, 10, 11, 13, 0, 0, 123456) # +01:00 wall clock
|
||
FROZEN_NOW = FROZEN_UTC.isoformat()
|
||
FROZEN_LOCAL_ISO = FROZEN_LOCAL.isoformat()
|
||
|
||
|
||
class FrozenDatetime(datetime):
|
||
@classmethod
|
||
def now(cls, tz=None):
|
||
if tz is not None:
|
||
return FROZEN_UTC.astimezone(tz)
|
||
return FROZEN_LOCAL
|
||
|
||
|
||
oracle_mod.datetime = FrozenDatetime
|
||
drift_core.datetime = FrozenDatetime
|
||
|
||
|
||
def ago(**kw):
|
||
return (FROZEN_UTC - timedelta(**kw)).isoformat()
|
||
|
||
|
||
def row(rock="washable", created=None, washed=None, accessed=None, status="active"):
|
||
return (status, rock, created, washed, accessed)
|
||
|
||
|
||
def make_db(rows):
|
||
db = sqlite3.connect(":memory:")
|
||
db.execute(
|
||
"CREATE TABLE entries (status TEXT, rock_status TEXT, created_at TEXT,"
|
||
" last_washed TEXT, last_accessed TEXT)"
|
||
)
|
||
db.executemany("INSERT INTO entries VALUES (?,?,?,?,?)", rows)
|
||
return db
|
||
|
||
|
||
_MAX_LAST_WASHED_SQL = (
|
||
"SELECT MAX(last_washed) FROM entries "
|
||
"WHERE rock_status != 'washable' AND status = 'active'"
|
||
)
|
||
|
||
|
||
class Case:
|
||
def __init__(self, name, fn):
|
||
self.name = name
|
||
self.fn = fn
|
||
|
||
|
||
def fresh_obs(alerts_pre=None):
|
||
tmp = tempfile.mkdtemp(prefix="do_truth_")
|
||
oracle_mod.ALERTS_FILE = os.path.join(tmp, "drift_alerts.json")
|
||
oracle_mod.RETROSPECTIVES_DIR = os.path.join(tmp, "retrospectives")
|
||
obs = oracle_mod.DriftObservability()
|
||
if alerts_pre is not None:
|
||
obs.alerts = copy.deepcopy(alerts_pre)
|
||
return obs
|
||
|
||
|
||
def _stamped(alert_type, message, severity):
|
||
return {
|
||
"type": alert_type,
|
||
"message": message,
|
||
"severity": severity,
|
||
"timestamp": FROZEN_NOW,
|
||
"acknowledged": False,
|
||
}
|
||
|
||
|
||
# ── add_alert ────────────────────────────────────────────────────────────────
|
||
|
||
def c_add_default(rec):
|
||
obs = fresh_obs()
|
||
rec("add-default-severity", {"alerts": [], "alert_type": "high_velocity",
|
||
"message": "v up", "severity": "info", "now": FROZEN_NOW},
|
||
obs.add_alert("high_velocity", "v up"), {"alerts": obs.alerts})
|
||
|
||
|
||
def c_add_warning(rec):
|
||
obs = fresh_obs()
|
||
rec("add-warning-severity", {"alerts": [], "alert_type": "negative_drift",
|
||
"message": "m", "severity": "warning", "now": FROZEN_NOW},
|
||
obs.add_alert("negative_drift", "m", "warning"), {"alerts": obs.alerts})
|
||
|
||
|
||
def c_add_critical(rec):
|
||
obs = fresh_obs()
|
||
rec("add-critical-severity", {"alerts": [], "alert_type": "promotion_stall",
|
||
"message": "stalled", "severity": "critical", "now": FROZEN_NOW},
|
||
obs.add_alert("promotion_stall", "stalled", "critical"), {"alerts": obs.alerts})
|
||
|
||
|
||
def c_add_onto_existing(rec):
|
||
pre = [{"type": "old", "message": "m0", "severity": "info",
|
||
"timestamp": ago(days=1), "acknowledged": True}]
|
||
obs = fresh_obs(pre)
|
||
rec("add-onto-existing", {"alerts": pre, "alert_type": "velocity",
|
||
"message": "second", "severity": "info", "now": FROZEN_NOW},
|
||
obs.add_alert("velocity", "second"), {"alerts": obs.alerts})
|
||
|
||
|
||
# ── get_unacknowledged_alerts ────────────────────────────────────────────────
|
||
|
||
def _pre_mixed():
|
||
return [
|
||
{"type": "a", "message": "m1", "severity": "info",
|
||
"timestamp": ago(days=3), "acknowledged": True},
|
||
{"type": "b", "message": "m2", "severity": "warning",
|
||
"timestamp": ago(days=2), "acknowledged": False},
|
||
{"type": "c", "message": "m3", "severity": "critical",
|
||
"timestamp": ago(days=1)}, # missing key → unacknowledged
|
||
]
|
||
|
||
|
||
def c_unacked_empty(rec):
|
||
obs = fresh_obs([])
|
||
rec("unacked-empty", {"alerts": []},
|
||
obs.get_unacknowledged_alerts(), {"unacked": []})
|
||
|
||
|
||
def c_unacked_mixed(rec):
|
||
pre = _pre_mixed()
|
||
obs = fresh_obs(pre)
|
||
rec("unacked-mixed", {"alerts": pre},
|
||
obs.get_unacknowledged_alerts(), {"unacked": obs.get_unacknowledged_alerts()})
|
||
|
||
|
||
def c_unacked_all_open(rec):
|
||
pre = [dict(_stamped("t", "m", "info"), timestamp=ago(days=1)) for _ in range(3)]
|
||
obs = fresh_obs(pre)
|
||
rec("unacked-all-open", {"alerts": pre},
|
||
obs.get_unacknowledged_alerts(), {"unacked": obs.get_unacknowledged_alerts()})
|
||
|
||
|
||
# ── acknowledge_alert ────────────────────────────────────────────────────────
|
||
|
||
def _ack_case(rec, name, index):
|
||
pre = _pre_mixed()
|
||
obs = fresh_obs(pre)
|
||
obs.acknowledge_alert(index)
|
||
changed = 0 <= index < len(pre)
|
||
rec(name, {"alerts": pre, "index": index},
|
||
None, {"alerts": obs.alerts, "changed": changed})
|
||
|
||
|
||
def c_ack_first(rec):
|
||
_ack_case(rec, "ack-first", 0)
|
||
|
||
|
||
def c_ack_second(rec):
|
||
_ack_case(rec, "ack-second", 1)
|
||
|
||
|
||
def c_ack_negative(rec):
|
||
_ack_case(rec, "ack-negative", -1)
|
||
|
||
|
||
def c_ack_at_len(rec):
|
||
_ack_case(rec, "ack-at-len", 3)
|
||
|
||
|
||
def c_ack_beyond(rec):
|
||
_ack_case(rec, "ack-beyond", 8)
|
||
|
||
|
||
# ── check_alerts ─────────────────────────────────────────────────────────────
|
||
|
||
def _check_case(rec, name, rows):
|
||
db = make_db(rows)
|
||
velocity = drift_core.compute_drift_velocity(db)
|
||
direction = drift_core.compute_drift_direction(db)
|
||
last = db.execute(_MAX_LAST_WASHED_SQL).fetchone()[0]
|
||
obs = fresh_obs([])
|
||
new = obs.check_alerts(db)
|
||
rec(name,
|
||
{"alerts": [], "velocity": velocity, "direction": direction,
|
||
"last_washed": last, "now": FROZEN_NOW},
|
||
new, {"new_alerts": new, "alerts": obs.alerts})
|
||
|
||
|
||
def c_check_empty_db(rec):
|
||
# Empty db: stability_ratio 0.0 < 0.1 — the negative-drift alert FIRES.
|
||
_check_case(rec, "check-empty-db-negative-drift", [])
|
||
|
||
|
||
def c_check_all_clear(rec):
|
||
rows = [row(rock="established", created=ago(days=30), washed=ago(days=1),
|
||
accessed=ago(hours=1)) for _ in range(10)]
|
||
_check_case(rec, "check-all-clear", rows)
|
||
|
||
|
||
def c_check_velocity_only(rec):
|
||
rows = [row(created=ago(days=1), washed=ago(days=1)) for _ in range(71)]
|
||
rows += [row(rock="established", created=ago(days=30), washed=ago(days=1))
|
||
for _ in range(30)]
|
||
_check_case(rec, "check-velocity-only", rows)
|
||
|
||
|
||
def c_check_negative_drift_only(rec):
|
||
rows = [row(rock="established", created=ago(days=30), washed=ago(days=1))]
|
||
rows += [row(created=ago(days=1), washed=ago(days=1)) for _ in range(19)]
|
||
_check_case(rec, "check-negative-drift-only", rows)
|
||
|
||
|
||
def c_check_stall_only(rec):
|
||
rows = [row(rock="established", created=ago(days=40), washed=ago(days=20))
|
||
for _ in range(10)]
|
||
_check_case(rec, "check-stall-only", rows)
|
||
|
||
|
||
def c_check_stall_boundary(rec):
|
||
rows = [row(rock="established", created=ago(days=40), washed=ago(days=14))
|
||
for _ in range(10)]
|
||
_check_case(rec, "check-stall-boundary-14d-no-alert", rows)
|
||
|
||
|
||
def c_check_stall_15(rec):
|
||
rows = [row(rock="established", created=ago(days=40), washed=ago(days=15))
|
||
for _ in range(10)]
|
||
_check_case(rec, "check-stall-15d-fires", rows)
|
||
|
||
|
||
def c_check_malformed_last_washed(rec):
|
||
# MAX picks "garbage" ('g' > '2'); oracle fromisoformat → ValueError skip.
|
||
rows = [row(rock="established", created=ago(days=30), washed="garbage")]
|
||
rows += [row(rock="established", created=ago(days=30), washed=ago(days=30))
|
||
for _ in range(9)]
|
||
_check_case(rec, "check-malformed-last-washed-skipped", rows)
|
||
|
||
|
||
def c_check_naive_last_washed(rec):
|
||
# Naive (no offset) MAX(last_washed): aware − naive → TypeError skip.
|
||
naive = (FROZEN_UTC - timedelta(days=14)).replace(tzinfo=None).isoformat()
|
||
rows = [row(rock="established", created=ago(days=30), washed=naive)]
|
||
rows += [row(rock="established", created=ago(days=30), washed=ago(days=30))
|
||
for _ in range(9)]
|
||
_check_case(rec, "check-naive-last-washed-skipped", rows)
|
||
|
||
|
||
def c_check_null_last_washed(rec):
|
||
# All-NULL MAX → falsy → skip.
|
||
rows = [row(rock="established", created=ago(days=30)) for _ in range(10)]
|
||
_check_case(rec, "check-null-last-washed-skipped", rows)
|
||
|
||
|
||
def c_check_empty_string_last_washed(rec):
|
||
# MAX = "" → falsy → the oracle's `if last_promo and last_promo[0]` skips.
|
||
rows = [row(rock="established", created=ago(days=30), washed="")
|
||
for _ in range(10)]
|
||
_check_case(rec, "check-empty-string-last-washed-skipped", rows)
|
||
|
||
|
||
def c_check_all_three(rec):
|
||
rows = [row(created=ago(days=1), washed=ago(days=1)) for _ in range(100)]
|
||
rows += [row(rock="established", created=ago(days=30), washed=ago(days=20))
|
||
for _ in range(3)]
|
||
_check_case(rec, "check-all-three", rows)
|
||
|
||
|
||
# ── run_retrospective ────────────────────────────────────────────────────────
|
||
|
||
def _retro_case(rec, name, rows, days):
|
||
db = make_db(rows)
|
||
cutoff = (FROZEN_UTC - timedelta(days=days)).isoformat()
|
||
counts = {
|
||
"new_entries": db.execute(
|
||
"SELECT COUNT(*) FROM entries WHERE created_at > ? AND status = 'active'",
|
||
(cutoff,)).fetchone()[0],
|
||
"washed_entries": db.execute(
|
||
"SELECT COUNT(*) FROM entries WHERE last_washed > ? AND status = 'active'",
|
||
(cutoff,)).fetchone()[0],
|
||
"promoted_entries": db.execute(
|
||
"SELECT COUNT(*) FROM entries WHERE rock_status != 'washable'"
|
||
" AND last_washed > ? AND status = 'active'",
|
||
(cutoff,)).fetchone()[0],
|
||
"decayed_entries": db.execute(
|
||
"SELECT COUNT(*) FROM entries WHERE status = 'deleted'"
|
||
" AND last_accessed > ?", (cutoff,)).fetchone()[0],
|
||
}
|
||
velocity = drift_core.compute_drift_velocity(db, days)
|
||
direction = drift_core.compute_drift_direction(db)
|
||
obs = fresh_obs([])
|
||
report = obs.run_retrospective(db, days)
|
||
inp = dict(counts, velocity=velocity, direction=direction, days=days,
|
||
now=FROZEN_NOW, now_local=FROZEN_LOCAL_ISO)
|
||
rec(name, inp, report,
|
||
{"report": report,
|
||
"filename": f"retrospective_{FROZEN_LOCAL.strftime('%Y%m%d_%H%M%S')}.json"})
|
||
|
||
|
||
def c_retro_positive(rec):
|
||
rows = [row(rock="established", created=ago(days=30), washed=ago(days=1))
|
||
for _ in range(10)]
|
||
rows.append(row(created=ago(days=30), status="deleted", accessed=ago(hours=2)))
|
||
_retro_case(rec, "retro-positive", rows, 7)
|
||
|
||
|
||
def c_retro_neutral_high_velocity(rec):
|
||
rows = [row(created=ago(days=1), washed=ago(days=1)) for _ in range(71)]
|
||
rows += [row(rock="established", created=ago(days=30), washed=ago(days=1))
|
||
for _ in range(40)]
|
||
_retro_case(rec, "retro-neutral-high-velocity", rows, 7)
|
||
|
||
|
||
def c_retro_neutral_low_velocity(rec):
|
||
rows = [row(rock="established", created=ago(days=30), washed=ago(days=30))
|
||
for _ in range(2)]
|
||
rows += [row(created=ago(days=30), washed=ago(days=30)) for _ in range(8)]
|
||
rows.append(row(status="deleted", accessed=ago(days=1)))
|
||
_retro_case(rec, "retro-neutral-low-velocity", rows, 7)
|
||
|
||
|
||
def c_retro_concerning(rec):
|
||
rows = [row(rock="established", created=ago(days=30), washed=ago(days=30))]
|
||
rows += [row(created=ago(days=30), washed=ago(days=30)) for _ in range(19)]
|
||
_retro_case(rec, "retro-concerning", rows, 7)
|
||
|
||
|
||
def c_retro_concerning_boundary(rec):
|
||
rows = [row(rock="established", created=ago(days=30), washed=ago(days=30))]
|
||
rows += [row(created=ago(days=30), washed=ago(days=30)) for _ in range(9)]
|
||
_retro_case(rec, "retro-concerning-boundary-ratio-0.1", rows, 7)
|
||
|
||
|
||
def c_retro_velocity_boundary(rec):
|
||
rows = [row(created=ago(days=1), washed=ago(days=1)) for _ in range(70)]
|
||
rows += [row(rock="established", created=ago(days=30), washed=ago(days=30))
|
||
for _ in range(40)]
|
||
_retro_case(rec, "retro-positive-velocity-boundary-10", rows, 7)
|
||
|
||
|
||
def c_retro_custom_days(rec):
|
||
rows = [row(created=ago(days=1), washed=ago(days=1)) for _ in range(5)]
|
||
rows += [row(rock="established", created=ago(days=30), washed=ago(days=2))
|
||
for _ in range(2)]
|
||
rows.append(row(status="deleted", accessed=ago(days=2)))
|
||
_retro_case(rec, "retro-custom-days-3", rows, 3)
|
||
|
||
|
||
def c_retro_empty_db(rec):
|
||
_retro_case(rec, "retro-empty-db-concerning", [], 7)
|
||
|
||
|
||
def c_retro_default_days(rec):
|
||
rows = [row(created=ago(days=2), washed=ago(days=2)) for _ in range(3)]
|
||
_retro_case(rec, "retro-default-days-7", rows, 7)
|
||
|
||
|
||
# ── driver ───────────────────────────────────────────────────────────────────
|
||
|
||
# ── residue groups: _load_alerts / _save_alerts / _do_core / main ──────────
|
||
|
||
def c_load_alerts(rec):
|
||
"""The oracle's `_load_alerts` METHOD law via the ALERTS_FILE seam:
|
||
missing → [], JSONDecodeError → [], valid document → the list."""
|
||
import tempfile as _tf
|
||
|
||
def with_file(text):
|
||
tmp = _tf.mkdtemp(prefix="do_load_")
|
||
oracle_mod.ALERTS_FILE = os.path.join(tmp, "drift_alerts.json")
|
||
if text is not None:
|
||
with open(oracle_mod.ALERTS_FILE, "w") as f:
|
||
f.write(text)
|
||
return oracle_mod.DriftObservability()._load_alerts()
|
||
|
||
got = with_file(None)
|
||
rec("load-missing-file", {"text": None}, got, got)
|
||
got = with_file("{corrupt json")
|
||
rec("load-corrupt-json", {"text": "{corrupt json"}, got, got)
|
||
got = with_file("[]")
|
||
rec("load-empty-array", {"text": "[]"}, got, got)
|
||
doc = json.dumps([
|
||
_stamped("high_velocity", "v up", "warning"),
|
||
{"type": "old", "message": "m0", "severity": "info",
|
||
"timestamp": ago(days=1), "acknowledged": True},
|
||
], indent=2)
|
||
got = with_file(doc)
|
||
rec("load-two-alerts", {"text": doc}, got, got)
|
||
got = with_file("{}")
|
||
rec("load-non-array-doc", {"text": "{}"}, got, got)
|
||
|
||
|
||
def c_save_alerts(rec):
|
||
"""The oracle's `_save_alerts` bytes law: json.dumps(alerts, indent=2);
|
||
the FILE write stays facade-side — the rig reads the file back."""
|
||
import tempfile as _tf
|
||
|
||
def save_and_read(alerts):
|
||
tmp = _tf.mkdtemp(prefix="do_save_")
|
||
oracle_mod.ALERTS_FILE = os.path.join(tmp, "drift_alerts.json")
|
||
obs = oracle_mod.DriftObservability()
|
||
obs.alerts = copy.deepcopy(alerts)
|
||
obs._save_alerts()
|
||
with open(oracle_mod.ALERTS_FILE) as f:
|
||
return f.read()
|
||
|
||
alerts = [
|
||
_stamped("high_velocity", "café ✓ unicode", "warning"),
|
||
{"type": "old", "message": "m0", "severity": "info",
|
||
"timestamp": ago(days=1), "acknowledged": True},
|
||
]
|
||
got = save_and_read(alerts)
|
||
rec("save-two-alerts-indent2", {"alerts": alerts}, None, got)
|
||
got = save_and_read([])
|
||
rec("save-empty-list", {"alerts": []}, None, got)
|
||
|
||
|
||
def c_probe(rec):
|
||
for name, pin in [("pin-python", "python"), ("pin-auto", "auto"),
|
||
("pin-other-resolves", "rust"),
|
||
("pin-unset-defaults-auto", None)]:
|
||
old = os.environ.get("RUNE_DO_CORE")
|
||
if pin is None:
|
||
os.environ.pop("RUNE_DO_CORE", None)
|
||
else:
|
||
os.environ["RUNE_DO_CORE"] = pin
|
||
try:
|
||
resolved = oracle_mod._do_core() is not None
|
||
finally:
|
||
if old is None:
|
||
os.environ.pop("RUNE_DO_CORE", None)
|
||
else:
|
||
os.environ["RUNE_DO_CORE"] = old
|
||
rec(f"probe-{name}", {"pin": pin}, resolved, {"resolved": resolved})
|
||
|
||
|
||
# main-rig seam constants (deterministic; the Rust replay input mirrors
|
||
# them one-for-one)
|
||
M_VEL_HOT = {"changes_per_day": 12, "promotions_per_day": 3, "alert": True}
|
||
M_VEL_CALM = {"changes_per_day": 4.5, "promotions_per_day": 1.5, "alert": False}
|
||
M_DIR_STABLE = {"total_entries": 21, "established_count": 9, "rock_count": 5,
|
||
"stability_ratio": 2 / 3, "direction": "stabilizing"}
|
||
M_DIR_WOBBLY = {"total_entries": 20, "established_count": 1, "rock_count": 1,
|
||
"stability_ratio": 0.05, "direction": "destabilizing"}
|
||
M_COUNTS = {"new_entries": 4, "washed_entries": 2, "promoted_entries": 1,
|
||
"decayed_entries": 0}
|
||
M_LAST_WASHED = ago(days=20) # > PROMOTION_STALL_DAYS → stall alert fires
|
||
|
||
|
||
class _FakeCursor:
|
||
def __init__(self, row):
|
||
self._row = row
|
||
|
||
def fetchone(self):
|
||
return self._row
|
||
|
||
|
||
class _FakeDB:
|
||
def __init__(self, last_washed, counts):
|
||
self._lw = last_washed
|
||
self._c = counts
|
||
|
||
def execute(self, sql, params=()):
|
||
if sql.strip().upper().startswith("SELECT MAX("):
|
||
return _FakeCursor((self._lw,))
|
||
if "created_at >" in sql:
|
||
return _FakeCursor((self._c["new_entries"],))
|
||
if "status = 'deleted'" in sql or "status = \"deleted\"" in sql:
|
||
return _FakeCursor((self._c["decayed_entries"],))
|
||
if "rock_status != 'washable'" in sql:
|
||
return _FakeCursor((self._c["promoted_entries"],))
|
||
if "last_washed >" in sql:
|
||
return _FakeCursor((self._c["washed_entries"],))
|
||
return _FakeCursor((0,))
|
||
|
||
def close(self):
|
||
pass
|
||
|
||
|
||
def _run_oracle_main(argv, alerts_seed, velocity, direction, last_washed,
|
||
counts):
|
||
"""Patch the seams (memory_store/drift_core modules, ALERTS_FILE,
|
||
RETROSPECTIVES_DIR, clock already frozen), run the REAL oracle main(),
|
||
return (exit, stdout, stderr, state)."""
|
||
import contextlib
|
||
import io
|
||
import types
|
||
|
||
tmp = tempfile.mkdtemp(prefix="do_main_")
|
||
oracle_mod.ALERTS_FILE = os.path.join(tmp, "drift_alerts.json")
|
||
oracle_mod.RETROSPECTIVES_DIR = os.path.join(tmp, "retrospectives")
|
||
if alerts_seed is not None:
|
||
with open(oracle_mod.ALERTS_FILE, "w") as f:
|
||
f.write(alerts_seed)
|
||
|
||
fake_ms = types.ModuleType("memory_store")
|
||
fake_ms.connect = lambda: _FakeDB(last_washed, counts)
|
||
fake_dc = types.ModuleType("drift_core")
|
||
fake_dc.compute_drift_velocity = lambda db, days=None: copy.deepcopy(velocity)
|
||
fake_dc.compute_drift_direction = lambda db: copy.deepcopy(direction)
|
||
|
||
saved = {k: sys.modules.get(k) for k in ("memory_store", "drift_core")}
|
||
sys.modules["memory_store"] = fake_ms
|
||
sys.modules["drift_core"] = fake_dc
|
||
old_argv = sys.argv
|
||
sys.argv = ["drift_observability.py"] + list(argv)
|
||
buf_out, buf_err = io.StringIO(), io.StringIO()
|
||
code = 0
|
||
try:
|
||
with contextlib.redirect_stdout(buf_out), contextlib.redirect_stderr(buf_err):
|
||
try:
|
||
oracle_mod.main()
|
||
except SystemExit as e:
|
||
code = e.code if isinstance(e.code, int) else 0
|
||
finally:
|
||
sys.argv = old_argv
|
||
for k, v in saved.items():
|
||
if v is None:
|
||
sys.modules.pop(k, None)
|
||
else:
|
||
sys.modules[k] = v
|
||
|
||
alerts_after = None
|
||
if os.path.exists(oracle_mod.ALERTS_FILE):
|
||
with open(oracle_mod.ALERTS_FILE) as f:
|
||
alerts_after = f.read()
|
||
retro = None
|
||
if os.path.isdir(oracle_mod.RETROSPECTIVES_DIR):
|
||
names = sorted(os.listdir(oracle_mod.RETROSPECTIVES_DIR))
|
||
if names:
|
||
with open(os.path.join(oracle_mod.RETROSPECTIVES_DIR, names[0])) as f:
|
||
retro = {"filename": names[0], "report": json.load(f)}
|
||
return {"exit": code, "stdout": buf_out.getvalue(),
|
||
"stderr": buf_err.getvalue(), "alerts_file": alerts_after,
|
||
"retro": retro}
|
||
|
||
|
||
_SEED_ALERTS = json.dumps([
|
||
_stamped("high_velocity", "v up", "warning"),
|
||
{"type": "old", "message": "m0", "severity": "info",
|
||
"timestamp": ago(days=1), "acknowledged": True},
|
||
], indent=2)
|
||
|
||
|
||
def _main_case(rec, name, argv, seed=None, velocity=None, direction=None,
|
||
last_washed=None, counts=None):
|
||
velocity = M_VEL_CALM if velocity is None else velocity
|
||
direction = M_DIR_STABLE if direction is None else direction
|
||
counts = M_COUNTS if counts is None else counts
|
||
got = _run_oracle_main(argv, seed, velocity, direction, last_washed,
|
||
counts)
|
||
rec(f"main-{name}",
|
||
{"argv": list(argv), "alerts_text": seed, "velocity": velocity,
|
||
"direction": direction, "last_washed": last_washed,
|
||
"counts": counts, "now": FROZEN_NOW, "now_local": FROZEN_LOCAL_ISO},
|
||
None, got)
|
||
|
||
|
||
def c_main(rec):
|
||
_main_case(rec, "no-cmd-help-exit-1", [])
|
||
_main_case(rec, "help-flag-exit-0", ["--help"])
|
||
_main_case(rec, "invalid-choice-exit-2", ["bogus"])
|
||
_main_case(rec, "check-two-alerts-velocity-and-stall", ["check"],
|
||
velocity=M_VEL_HOT, last_washed=M_LAST_WASHED)
|
||
_main_case(rec, "check-no-alerts", ["check"])
|
||
_main_case(rec, "check-negative-drift", ["check"], direction=M_DIR_WOBBLY)
|
||
_main_case(rec, "check-onto-seeded-alerts", ["check"], seed=_SEED_ALERTS,
|
||
velocity=M_VEL_HOT, last_washed=M_LAST_WASHED)
|
||
_main_case(rec, "retrospective-default-days", ["retrospective"])
|
||
_main_case(rec, "retrospective-days-30", ["retrospective", "--days", "30"])
|
||
_main_case(rec, "retrospective-days-eq", ["retrospective", "--days=14"])
|
||
_main_case(rec, "retrospective-bad-days-exit-2",
|
||
["retrospective", "--days", "zz"])
|
||
_main_case(rec, "alerts-empty", ["alerts"])
|
||
_main_case(rec, "alerts-two-unacked", ["alerts"], seed=_SEED_ALERTS)
|
||
_main_case(rec, "alerts-all-acked", ["alerts"], seed=json.dumps([
|
||
{"type": "a", "message": "m", "severity": "info",
|
||
"timestamp": ago(days=1), "acknowledged": True}], indent=2))
|
||
_main_case(rec, "acknowledge-first", ["acknowledge", "0"],
|
||
seed=_SEED_ALERTS)
|
||
_main_case(rec, "acknowledge-beyond-len-noop", ["acknowledge", "9"],
|
||
seed=_SEED_ALERTS)
|
||
_main_case(rec, "acknowledge-missing-index-exit-2", ["acknowledge"])
|
||
_main_case(rec, "acknowledge-bad-int-exit-2", ["acknowledge", "z"])
|
||
_main_case(rec, "dashboard", ["dashboard"], velocity=M_VEL_HOT)
|
||
_main_case(rec, "dashboard-unicode-trim", ["dashboard"], seed=_SEED_ALERTS,
|
||
velocity=M_VEL_CALM, direction=M_DIR_WOBBLY)
|
||
|
||
|
||
ADD = [c_add_default, c_add_warning, c_add_critical, c_add_onto_existing]
|
||
UNACKED = [c_unacked_empty, c_unacked_mixed, c_unacked_all_open]
|
||
ACK = [c_ack_first, c_ack_second, c_ack_negative, c_ack_at_len, c_ack_beyond]
|
||
CHECK = [c_check_empty_db, c_check_all_clear, c_check_velocity_only,
|
||
c_check_negative_drift_only, c_check_stall_only, c_check_stall_boundary,
|
||
c_check_stall_15, c_check_malformed_last_washed, c_check_naive_last_washed,
|
||
c_check_null_last_washed, c_check_empty_string_last_washed,
|
||
c_check_all_three]
|
||
RETRO = [c_retro_positive, c_retro_neutral_high_velocity,
|
||
c_retro_neutral_low_velocity, c_retro_concerning,
|
||
c_retro_concerning_boundary, c_retro_velocity_boundary,
|
||
c_retro_custom_days, c_retro_empty_db, c_retro_default_days]
|
||
|
||
GROUPS = [("add_alert", ADD), ("get_unacknowledged_alerts", UNACKED),
|
||
("acknowledge_alert", ACK), ("check_alerts", CHECK),
|
||
("run_retrospective", RETRO),
|
||
("load_alerts", [c_load_alerts]), ("save_alerts", [c_save_alerts]),
|
||
("probe", [c_probe]), ("main", [c_main])]
|
||
|
||
# lib-level twins + the CLI dispatcher: no FFI symbol (the facade owns the
|
||
# file IO / argparse surface); battery-replayed from the frozen oracle rows
|
||
BATTERY_ONLY_GROUPS = {"load_alerts", "save_alerts", "probe", "main"}
|
||
|
||
|
||
def main():
|
||
frozen = None
|
||
if REPLAY:
|
||
with open(TRUTH_PATH) as f:
|
||
frozen = json.load(f)["cases"]
|
||
|
||
groups = {}
|
||
for group, cases in GROUPS:
|
||
recs = []
|
||
|
||
def rec(name, inp, oracle_ret, expected, _r=recs):
|
||
entry = {"name": name, "input": inp, "return": oracle_ret,
|
||
"expected": expected}
|
||
if REPLAY:
|
||
old = next(c for c in frozen[group] if c["name"] == name)
|
||
for key in ("input", "return", "expected"):
|
||
got, want = entry[key], old[key]
|
||
if json.dumps(got, sort_keys=True) != json.dumps(want, sort_keys=True):
|
||
print(f"FACADE DIVERGE [{group}] {name} {key}")
|
||
print(" got: ", json.dumps(got, sort_keys=True)[:600])
|
||
print(" want:", json.dumps(want, sort_keys=True)[:600])
|
||
recs.append({"name": name, "diverged": key})
|
||
return
|
||
_r.append(entry)
|
||
|
||
for c in cases:
|
||
c(rec)
|
||
groups[group] = recs
|
||
|
||
total = sum(len(v) for v in groups.values())
|
||
if REPLAY:
|
||
facade_failures = sum(1 for v in groups.values() for c in v if "diverged" in c)
|
||
|
||
# Kernel-level: the Rust core must reproduce the frozen expected
|
||
# from the frozen input even WITHOUT the facade in the loop. The
|
||
# loader unwraps the doc-envelope for the list-returning calls.
|
||
unwrap_want = {
|
||
"add_alert": lambda w: w["alerts"],
|
||
"get_unacknowledged_alerts": lambda w: w["unacked"],
|
||
"acknowledge_alert": lambda w: w,
|
||
"check_alerts": lambda w: w,
|
||
"run_retrospective": lambda w: w,
|
||
}
|
||
failures = 0
|
||
for group, cases in frozen.items():
|
||
if group in BATTERY_ONLY_GROUPS:
|
||
continue
|
||
for case in cases:
|
||
got = getattr(do_loader, group)(**copy.deepcopy(case["input"]))
|
||
want = unwrap_want[group](case["expected"])
|
||
if json.dumps(got, sort_keys=True) != json.dumps(want, sort_keys=True):
|
||
failures += 1
|
||
print(f"KERNEL DIVERGE [{group}] {case['name']}")
|
||
print(" got: ", json.dumps(got, sort_keys=True)[:600])
|
||
print(" want:", json.dumps(want, sort_keys=True)[:600])
|
||
print(f"replay: facade {total - facade_failures}/{total}, "
|
||
f"kernel {total - failures}/{total}")
|
||
return 1 if (failures or facade_failures) else 0
|
||
|
||
doc = {
|
||
# deterministic stamp (regen x2 must be byte-identical)
|
||
"frozen": FROZEN_NOW,
|
||
"oracle": "src/rune/drift_observability.py",
|
||
"pin": "RUNE_DO_CORE=python",
|
||
"note": "Frozen via scripts/gen_drift_observability_truth.py against "
|
||
"the live Python oracle; wall clock frozen at "
|
||
+ FROZEN_NOW + ". Battery embeds this VERBATIM.",
|
||
"cases": groups,
|
||
}
|
||
os.makedirs(os.path.dirname(TRUTH_PATH), exist_ok=True)
|
||
with open(TRUTH_PATH, "w") as f:
|
||
# insertion order preserved: alert/row dict key order IS the truth
|
||
# the _save_alerts renderer replays (sort_keys would lie about it)
|
||
json.dump(doc, f, indent=1)
|
||
f.write("\n")
|
||
print(f"frozen {total} cases → {os.path.relpath(TRUTH_PATH, REPO)}")
|
||
return 0
|
||
|
||
|
||
if __name__ == "__main__":
|
||
sys.exit(main())
|