generated from coulomb/repo-seed
125 lines
4.3 KiB
Python
125 lines
4.3 KiB
Python
import json
|
|
from datetime import datetime, timezone
|
|
from pathlib import Path
|
|
|
|
from phase_memory.contracts import graph_from_markitect
|
|
from phase_memory.lifecycle import (
|
|
LifecycleRuleConfig,
|
|
plan_compaction,
|
|
plan_lifecycle_from_profile,
|
|
plan_phase_transition,
|
|
plan_refresh,
|
|
plan_retention,
|
|
)
|
|
from phase_memory.models import LifecycleActionKind, LifecycleState, MemoryGraph, MemoryNode, MemoryPhase
|
|
|
|
|
|
FIXTURES = Path(__file__).parent / "fixtures"
|
|
|
|
|
|
def _load(name: str):
|
|
return json.loads((FIXTURES / name).read_text(encoding="utf-8"))
|
|
|
|
|
|
def test_phase_transition_to_stabilized_requires_review() -> None:
|
|
node = MemoryNode("event.restart", "episode", "Useful restart trace", phase=MemoryPhase.FLUID)
|
|
|
|
action = plan_phase_transition(node, MemoryPhase.STABILIZED)
|
|
|
|
assert action.action == LifecycleActionKind.TRANSITION_PHASE
|
|
assert action.requires_review
|
|
assert action.to_state == LifecycleState.REVIEW_NEEDED
|
|
assert action.metadata["to_phase"] == "stabilized"
|
|
|
|
|
|
def test_retention_plans_stale_and_delete_requested_without_physical_delete() -> None:
|
|
now = datetime(2026, 5, 18, tzinfo=timezone.utc)
|
|
stale = MemoryNode(
|
|
"stale",
|
|
"episode",
|
|
freshness={"updated_at": "2026-05-01T00:00:00+00:00"},
|
|
)
|
|
old = MemoryNode(
|
|
"old",
|
|
"episode",
|
|
freshness={"updated_at": "2026-04-01T00:00:00+00:00"},
|
|
)
|
|
|
|
actions = plan_retention([stale, old], stale_after_days=7, delete_after_days=30, now=now)
|
|
|
|
by_target = {action.target_id: action for action in actions}
|
|
assert by_target["stale"].action == LifecycleActionKind.MARK_STALE
|
|
assert by_target["old"].action == LifecycleActionKind.REQUEST_DELETE
|
|
assert by_target["old"].metadata["physical_delete"] is False
|
|
|
|
|
|
def test_compaction_and_refresh_are_reviewable_plans() -> None:
|
|
node = MemoryNode("artifact.profile", "artifact", "Profile text", freshness={"source_digest": "old"})
|
|
|
|
compact = plan_compaction([node])
|
|
refresh = plan_refresh([node], source_digest_by_node_id={"artifact.profile": "new"})[0]
|
|
|
|
assert compact.action == LifecycleActionKind.COMPACT
|
|
assert compact.requires_review
|
|
assert refresh.action == LifecycleActionKind.REFRESH
|
|
assert refresh.requires_review
|
|
assert refresh.metadata["proposed_digest"] == "new"
|
|
|
|
|
|
def test_profile_retention_rules_drive_lifecycle_plan_from_fixture() -> None:
|
|
now = datetime(2026, 5, 18, tzinfo=timezone.utc)
|
|
profile = _load("memory-profile.json")
|
|
graph = graph_from_markitect(_load("memory-graph.json")).value
|
|
config = LifecycleRuleConfig.from_profile(profile)
|
|
|
|
actions = plan_lifecycle_from_profile(
|
|
graph,
|
|
profile,
|
|
refresh_digests={"event.restart": "new"},
|
|
now=now,
|
|
)
|
|
|
|
by_target_and_action = {(action.target_id, action.action): action for action in actions}
|
|
assert config.retention_default.stale_after_days == 7
|
|
assert by_target_and_action[("event.restart", LifecycleActionKind.MARK_STALE)].to_state == LifecycleState.STALE
|
|
assert by_target_and_action[("event.restart", LifecycleActionKind.REFRESH)].requires_review
|
|
|
|
|
|
def test_profile_transition_rules_promote_matching_nodes() -> None:
|
|
now = datetime(2026, 5, 18, tzinfo=timezone.utc)
|
|
graph = MemoryGraph(
|
|
"graph.rules",
|
|
nodes=(
|
|
MemoryNode(
|
|
"episode.old",
|
|
"episode",
|
|
"Useful trace",
|
|
phase=MemoryPhase.FLUID,
|
|
freshness={"updated_at": "2026-05-01T00:00:00+00:00"},
|
|
),
|
|
),
|
|
)
|
|
profile = {
|
|
"schema_version": "markitect.memory.profile.v1",
|
|
"id": "profile.rules",
|
|
"metadata": {
|
|
"phase_transitions": [
|
|
{
|
|
"kind": "episode",
|
|
"from_phase": "fluid",
|
|
"to_phase": "stabilized",
|
|
"min_age_days": 7,
|
|
"reason": "episode old enough to stabilize",
|
|
}
|
|
]
|
|
},
|
|
}
|
|
|
|
actions = plan_lifecycle_from_profile(graph, profile, now=now)
|
|
|
|
assert len(actions) == 1
|
|
assert actions[0].action == LifecycleActionKind.TRANSITION_PHASE
|
|
assert actions[0].target_id == "episode.old"
|
|
assert actions[0].requires_review is True
|
|
assert actions[0].metadata["to_phase"] == "stabilized"
|