fix(token_optimizer): include content in CacheEntry.to_dict — closes #770 cache-loss defect
Pre-fix to_dict omitted the required field content, so _load's CacheEntry(**entry_data) raised TypeError on every reload and the blanket except silently discarded the whole cache on every restart. Frozen regression contract tests/test_token_optimizer_cache.py (3 tests: save_persists_content, roundtrip_restores_content, corrupt_storage_degrades_to_empty_cache). Full suite 2250/0.
This commit is contained in:
1 parent
6d40f94f18
commit
f1e63b703e
2 files changed
+67
No files matched your search
@@ -44,8 +44,13 @@ class CacheEntry:
|
||||
hit_count: int = 0
|
||||
|
||||
def to_dict(self):
|
||||
# `content` is required by CacheEntry; omitting it here made the
|
||||
# persisted JSON unloadable (CacheEntry(**entry_data) raised on
|
||||
# _load), which the blanket except in _load turned into silent
|
||||
# total cache loss on every restart. See issue #770.
|
||||
return {
|
||||
"key": self.key,
|
||||
"content": self.content,
|
||||
"tokens": self.tokens,
|
||||
"last_used": self.last_used,
|
||||
"use_count": self.use_count,
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
"""Cache roundtrip contract for TokenOptimizer.
|
||||
|
||||
Rebase w2 disposition evidence for issue #770: CacheEntry.to_dict omitted
|
||||
`content`, so the persisted JSON could never satisfy CacheEntry(**entry_data)
|
||||
on _load — the blanket except in _load silently discarded the whole cache on
|
||||
every process restart. These tests freeze the repaired contract.
|
||||
"""
|
||||
|
||||
import json
|
||||
|
||||
from rune.token_optimizer import CacheEntry, TokenOptimizer
|
||||
|
||||
|
||||
def _entry(key: str = "static", content: str = "constitution text") -> CacheEntry:
|
||||
return CacheEntry(
|
||||
key=key,
|
||||
content=content,
|
||||
tokens=4,
|
||||
last_used="2026-01-01T00:00:00+00:00",
|
||||
use_count=1,
|
||||
hit_count=0,
|
||||
)
|
||||
|
||||
|
||||
def _seed(opt: TokenOptimizer, key: str = "static", content: str = "constitution text"):
|
||||
"""Seed a cache entry via the real mutation contract (every mutator
|
||||
pairs its change with _mark_dirty — direct assignment alone never
|
||||
reaches the save path)."""
|
||||
opt.cache[key] = _entry(key, content)
|
||||
opt._mark_dirty()
|
||||
return opt
|
||||
|
||||
|
||||
def test_save_persists_content(tmp_path):
|
||||
path = tmp_path / "tok.json"
|
||||
opt = TokenOptimizer(storage_path=str(path))
|
||||
_seed(opt)
|
||||
opt.flush()
|
||||
|
||||
data = json.loads(path.read_text())
|
||||
assert data["cache"]["static"]["content"] == "constitution text"
|
||||
|
||||
|
||||
def test_roundtrip_restores_content(tmp_path):
|
||||
path = str(tmp_path / "tok.json")
|
||||
seeder = TokenOptimizer(storage_path=path)
|
||||
_seed(seeder)
|
||||
seeder.flush()
|
||||
|
||||
reloaded = TokenOptimizer(storage_path=path)
|
||||
assert "static" in reloaded.cache
|
||||
assert reloaded.cache["static"].content == "constitution text"
|
||||
assert reloaded.cache["static"].tokens == 4
|
||||
|
||||
|
||||
def test_corrupt_storage_degrades_to_empty_cache(tmp_path):
|
||||
"""The blanket except in _load is a deliberate corrupt-file recovery path:
|
||||
the optimizer must start empty, never crash."""
|
||||
path = tmp_path / "tok.json"
|
||||
path.write_text("{not json at all")
|
||||
opt = TokenOptimizer(storage_path=str(path))
|
||||
assert opt.cache == {}
|
||||
Reference in new issue
Block a user