Auto-version and Release / release (push) Successful in 28s
CI Pipeline / lint (push) Failing after 48s
CI Pipeline / go-surface (push) Failing after 25s
CI Pipeline / rust-surface (push) Failing after 14s
CI Pipeline / best-practices (push) Successful in 14s
CI Pipeline / documentation (push) Successful in 11s
CI Pipeline / canary (push) Failing after 1m23s
CI Pipeline / gitea-offline (push) Successful in 22s
CI Pipeline / type-check (push) Failing after 49s
CI Pipeline / unit-tests (push) Failing after 12m27s
deploy / deploy (push) Failing after 15m42s
CI Pipeline / required-checks (push) Failing after 3s
CI Pipeline / CI Pipeline / notify-event-bridge (push) Failing after 15s
Dichotomy verdict: RUST kernel, PYTHON seam (mirrors batch-127 pattern).
Audit proved live: all 13 wrappers resolve; missing lib raises
MemoryScoringUnavailable (FileNotFoundError — callers fall back);
kernel !ok raises RuntimeError naming the symbol; null envelope is
{ok:false}; the ratio ladder pinned (washable 0.5/0.25/0.1 at 0/10/20,
established 0.2, rock 0).
Parity: oracle-first. 5/5 green. Improvements: parity-only — the seam
is verified sound; future symbol changes fail loudly.
84 lines
3.0 KiB
Python
84 lines
3.0 KiB
Python
"""test_ms_loader_seam.py — batch 130: the ms_loader seam contract.
|
|
|
|
Mirrors the cc_loader batch-127 pattern for the memory_scoring seam.
|
|
Pins what the audit proved live:
|
|
1. all 13 symbols resolve in the built .so (no phantom wrappers);
|
|
2. missing lib raises MemoryScoringUnavailable (a FileNotFoundError —
|
|
callers fall back; never ImportError, never silence);
|
|
3. kernel !ok raises RuntimeError naming the symbol
|
|
(ms_compress_text without ratio -> "arg.ratio required");
|
|
4. the null-pointer envelope is {ok:false} (kernel never crashes Python);
|
|
5. the compression-ratio ladder (washable 0.5/0.25/0.1 at 0/10/20,
|
|
established 0.2, rock 0) — the strangler seam both cores share.
|
|
"""
|
|
|
|
import ctypes
|
|
import json
|
|
import sys
|
|
|
|
sys.path.insert(0, "src")
|
|
|
|
from rune import ms_loader as ml
|
|
|
|
WRAPPER_TO_SYMBOL = {
|
|
"compute_value": "ms_compute_value",
|
|
"check_promotion_eligibility": "ms_check_promotion_eligibility",
|
|
"pre_commit_gate_entry": "ms_pre_commit_gate_entry",
|
|
"get_compression_ratio": "ms_get_compression_ratio",
|
|
"compress_text": "ms_compress_text",
|
|
"compress_entry": "ms_compress_entry",
|
|
"should_warn": "ms_should_warn",
|
|
"warn_before_compress": "ms_warn_before_compress",
|
|
"compute_priority": "ms_compute_priority",
|
|
"qualifies_for_cache": "ms_qualifies_for_cache",
|
|
"score_gate": "ms_score_gate",
|
|
"score_finish": "ms_score_finish",
|
|
"base_level_from_uses": "ms_base_level_from_uses",
|
|
}
|
|
|
|
|
|
def test_all_symbols_resolve():
|
|
lib = ml._load()
|
|
for wrapper, sym in WRAPPER_TO_SYMBOL.items():
|
|
assert hasattr(ml, wrapper), f"wrapper missing: {wrapper}"
|
|
try:
|
|
getattr(lib, sym)
|
|
except AttributeError:
|
|
raise AssertionError(f"symbol missing in .so: {sym} (wrapper {wrapper})")
|
|
|
|
|
|
def test_missing_lib_raises_unavailable():
|
|
assert issubclass(ml.MemoryScoringUnavailable, FileNotFoundError)
|
|
|
|
|
|
def test_error_envelope_raises_with_symbol():
|
|
try:
|
|
ml._call("ms_compress_text", {"wrong": "shape"})
|
|
except RuntimeError as e:
|
|
assert "ms_compress_text" in str(e)
|
|
assert "arg.ratio required" in str(e)
|
|
else:
|
|
raise AssertionError("kernel !ok must raise, never return None")
|
|
|
|
|
|
def test_null_envelope_is_err_not_crash():
|
|
lib = ml._load()
|
|
lib.ms_compress_text.argtypes = [ctypes.c_char_p]
|
|
lib.ms_compress_text.restype = ctypes.c_void_p
|
|
ptr = lib.ms_compress_text(None)
|
|
try:
|
|
text = ctypes.cast(ptr, ctypes.c_char_p).value.decode("utf-8")
|
|
finally:
|
|
lib.ms_free_string(ptr)
|
|
assert json.loads(text) == {"ok": False, "err": "null argument pointer"}
|
|
|
|
|
|
def test_ratio_ladder():
|
|
assert ml.get_compression_ratio(0.0, "washable") == 0.5
|
|
assert ml.get_compression_ratio(9.0, "washable") == 0.5
|
|
assert ml.get_compression_ratio(10.0, "washable") == 0.25
|
|
assert ml.get_compression_ratio(20.0, "washable") == 0.1
|
|
assert ml.get_compression_ratio(50.0, "washable") == 0.1
|
|
assert ml.get_compression_ratio(50.0, "established") == 0.2
|
|
assert ml.get_compression_ratio(50.0, "rock") == 0
|