Files
rune/scripts/gen_drift_compression_truth.py
Nick d4640a7f12 feat(drift_compression): close PARTIAL gaps — dichotomy wave
Audit 3/7 PARTIAL -> 7/7 FULL (RUST). The five pure kernels already lived
in src/rust/memory_scoring (H3 slice 2, d58e88da) but the audit alias
mapped the module to cognition_core — the instrument now follows the live
delegation chain (drift_compression -> drift_core._ms_core -> ms_loader ->
libmemory_scoring.so). New kernel work: get_compression_stats — SQL stays
in the Python facade (boundary law), the row-reduction math (int()
truncation after the float multiply, banker's rounding at 1e2, SQL-null
coercion, empty-store int-shaped 0) is a Rust kernel + ms_get_compression_stats
FFI + loader wrapper + oracle delegation with a load-bearing _py fallback.

Fix-first (proven by the new frozen battery): warn_before_compress emitted
the COERCED rock_status; the oracle emits the raw value — a present None
rides the warning dict as null (entry.get default only fires when the key
is absent).

Verification: frozen truth 65 cases via the shared harness, byte-identical
across repeated gen + freeze_truth --verify runs; cargo battery 7/7
(include_str! embed); clippy -D warnings + fmt clean; live parity 1935/1935
(pinned facade / live facade / kernel direct / db-level / fuzz seed 2479);
Python both pins green (seam file 7/7 both; shims failures pre-exist on
base); ruff clean. Evidence: verification/dc-*.log.
2026-10-14 05:55:58 +01:00

153 lines
9.0 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
"""gen_drift_compression_truth.py — freeze drift_compression oracle truth
for the memory_scoring gap-close battery (dichotomy wave, #2479 tranche).
Freezes the PURE, replayable oracle surface (the six _py_* kernels:
_py_get_compression_ratio, _py_compress_text, _py_compress_entry,
_py_should_warn, _py_warn_before_compress, _py_get_compression_stats)
through the SHARED harness (scripts/freeze_truth.py) into
src/rust/memory_scoring/src/frozen_dcomp_truth.json — the freeze_truth
list format, so `freeze_truth.py --verify` re-freezes it byte-identically
against the live oracle (byte-identical law).
Poisoned-truth law: RUNE_MEMORY_CORE=python is pinned BEFORE any import —
drift_compression's delegation chain runs through drift_core._ms_core,
which reads RUNE_MEMORY_CORE (the harness's stem-derived pin cannot reach
a cross-module chain). Freezing the _py_* kernels directly makes the truth
delegation-proof by construction: no pin can poison a pure function.
Boundary law: the sqlite read in the public get_compression_stats(db)
stays in the Python facade — _py_get_compression_stats(rows) freezes the
math on the fetched triples [rock_status, wash_count, text_len], exactly
the payload the Rust kernel receives. The db-level seam (SQL, status
filtering, None coercion of SQL nulls) is covered live by
scripts/check_drift_compression_parity.py, not by frozen truth (a sqlite
connection is not JSON-freezable).
No clock, no HOME, no file seams in this module — no rig needed.
Run: python3 scripts/gen_drift_compression_truth.py
"""
from __future__ import annotations
import os
import sys
from pathlib import Path
REPO = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(REPO / "src"))
sys.path.insert(0, str(REPO / "scripts"))
os.environ["RUNE_MEMORY_CORE"] = "python" # poisoned-truth law
from freeze_truth import freeze, write as freeze_write # noqa: E402
TRUTH_PATH = REPO / "src" / "rust" / "memory_scoring" / "src" / "frozen_dcomp_truth.json"
import rune.drift_compression as dc # noqa: E402 (after the pin)
def long(n, filler="alpha bravo charlie delta echo foxtrot golf hotel "):
"""Deterministic n-char text (repeats a 40-char filler, truncated)."""
return (filler * (n // len(filler) + 1))[:n]
LONG_TEXT = long(300)
TRAILING_TEXT = long(120) + " " # cut boundary lands on trailing blanks
CASES = [
# ── _py_get_compression_ratio: the wash ladder × rock status ──────────
("_py_get_compression_ratio", (0, "washable"), {}),
("_py_get_compression_ratio", (9, "washable"), {}),
("_py_get_compression_ratio", (10, "washable"), {}), # partial threshold
("_py_get_compression_ratio", (19, "washable"), {}),
("_py_get_compression_ratio", (20, "washable"), {}), # full threshold
("_py_get_compression_ratio", (100, "washable"), {}),
("_py_get_compression_ratio", (5, "established"), {}), # status overrides wash
("_py_get_compression_ratio", (50, "established"), {}),
("_py_get_compression_ratio", (0, "rock"), {}),
("_py_get_compression_ratio", (25, "rock"), {}),
("_py_get_compression_ratio", (15, "muddy"), {}), # unknown status → washable math
("_py_get_compression_ratio", (10.5, "washable"), {}), # float wash
("_py_get_compression_ratio", (), {"wash_count": 20, "rock_status": "rock"}), # kwargs path
# ── _py_compress_text: ratio extremes + keep_chars clamp + unicode ────
("_py_compress_text", (long(100), 0.0), {}), # ratio 0 → full text
("_py_compress_text", (long(100), 1.0), {}), # ratio 1 → ""
("_py_compress_text", (long(100), 0.5), {}), # plain 50% cut + "..."
("_py_compress_text", (long(100),), {"ratio": 0.5}), # kwargs path
("_py_compress_text", (long(100), 0.3), {}),
("_py_compress_text", (long(100), 0.999), {}), # keep 0 → clamped to 20
("_py_compress_text", ("short text", 0.5), {}), # len < 20 → min(20, len) = full
("_py_compress_text", ("exactly-twenty-chars", 0.9), {}), # clamp keeps all 20
("_py_compress_text", ("", 0.5), {}), # empty text
("_py_compress_text", (TRAILING_TEXT, 0.5), {}), # rstrip before "..."
("_py_compress_text", ("métier naïve 中文文本 twenty", 0.5), {}), # codepoints, not bytes
("_py_compress_text", (long(100), -0.1), {}), # ratio < 0 → full text
("_py_compress_text", (long(100), 1.5), {}), # ratio > 1 → ""
# ── _py_compress_entry: coercion lattice on the entry dict ────────────
("_py_compress_entry", ({"text": long(100)},), {}), # defaults wash 0 washable
("_py_compress_entry", ({},), {}), # no text → ""
("_py_compress_entry", ({"text": long(100), "wash_count": 10},), {}),
("_py_compress_entry", ({"text": long(100), "wash_count": 25},), {}),
("_py_compress_entry", ({"text": long(100), "rock_status": "rock"},), {}),
("_py_compress_entry", ({"text": long(100), "rock_status": "established"},), {}),
("_py_compress_entry", ({"text": "tiny", "wash_count": None},), {}), # None wash → or 0
("_py_compress_entry", ({"text": "tiny", "rock_status": None},), {}), # None status → "washable"
("_py_compress_entry", ({"text": long(60), "wash_count": 0, "rock_status": "muddy"},), {}),
# ── _py_should_warn: every warn branch + both strict boundaries ───────
("_py_should_warn", ({"wash_count": 10, "text": "t"},), {}), # proven
("_py_should_warn", ({"wash_count": 9, "text": "t"},), {}), # below wash gate
("_py_should_warn", ({"rock_status": "established", "text": "t"},), {}),
("_py_should_warn", ({"rock_status": "rock", "text": "t"},), {}),
("_py_should_warn", ({"text": long(201)},), {}), # len > 200, ratio 0.5
("_py_should_warn", ({"text": long(200)},), {}), # len == 200 → no warn
("_py_should_warn", ({"text": long(300), "wash_count": 12},), {}), # ratio 0.25 ≤ 0.3 → no
("_py_should_warn", ({"text": long(300), "wash_count": 25},), {}), # ratio 0.1 → but wash ≥ 10 → yes
("_py_should_warn", ({},), {}), # all defaults
# ── _py_warn_before_compress: every recommendation branch + id shape ──
("_py_warn_before_compress", ({"id": 7, "text": long(100), "rock_status": "rock"},), {}),
("_py_warn_before_compress", ({"id": 8, "text": long(100), "wash_count": 25},), {}),
("_py_warn_before_compress", ({"id": 9, "text": long(100), "wash_count": 15},), {}),
("_py_warn_before_compress", ({"id": 10, "text": long(100)},), {}),
("_py_warn_before_compress", ({"text": long(100)},), {}), # no id → null
("_py_warn_before_compress", ({"id": "slug-1", "text": "tiny", "wash_count": None},), {}), # coerced wash rides verbatim
("_py_warn_before_compress", ({"id": None, "text": "", "rock_status": None},), {}),
# ── _py_get_compression_stats: the row-reduction lattice ──────────────
("_py_get_compression_stats", ([],), {}), # empty store → int 0 pct
("_py_get_compression_stats", ([["washable", 0, 100]],), {}), # integral 50.0 pct
("_py_get_compression_stats", ([["rock", 5, 50]],), {}), # ratio 0 → no savings
("_py_get_compression_stats", ([["established", 0, 50]],), {}), # 20% → 40.0 pct
("_py_get_compression_stats", ([["washable", 15, 40]],), {}), # ratio 0.25 → 25.0 pct
("_py_get_compression_stats", ([["washable", 25, 40]],), {}), # ratio 0.1 → 10.0 pct
("_py_get_compression_stats", ([["washable", 0, 33]],), {}), # int() truncation 16.5 → 16
("_py_get_compression_stats", ([["washable", 0, 1], ["washable", 0, 2]],), {}), # 33.33 pct rounding
("_py_get_compression_stats", ([["washable", 0, 2], ["washable", 0, 1]],), {}), # 66.67 pct rounding
("_py_get_compression_stats", ([["washable", 0, 2], ["rock", 0, 798]],), {}), # 0.125 → banker's 0.12
("_py_get_compression_stats", ([["washable", 0, 6], ["rock", 0, 794]],), {}), # 0.375 → banker's 0.38
("_py_get_compression_stats", ([["washable", 0, 100], ["established", 5, 60], ["rock", 9, 80]],), {}),
("_py_get_compression_stats", ([[None, None, None]],), {}), # SQL null row coerced
("_py_get_compression_stats", ([["washable", 0, 100], [None, None, None], ["rock", 1, 10]],), {}),
]
def main() -> int:
# sanity: every case fn exists on the oracle (import-time typos die here)
fns = {c[0] for c in CASES}
missing = [f for f in fns if not hasattr(dc, f)]
if missing:
print(f"ORACLE MISSING FNS: {missing}")
return 1
truth = freeze("rune.drift_compression", CASES, oracle_module="rune.drift_compression")
freeze_write(truth, TRUTH_PATH)
print(f"froze {len(truth)} cases -> {TRUTH_PATH.relative_to(REPO)}")
return 0
if __name__ == "__main__":
sys.exit(main())