permanence-training / tests /test_domain_registry.py
chane35's picture
PERMANENCE: reversibility-aware RL environment for training LLM agents
796da7c verified
Raw
History Blame
7.85 kB
"""Tests for the domain registry architecture.
Verifies that:
1. Importing ``permanence`` registers both meridian + devtools domains
2. The two domains have NON-OVERLAPPING action ids and task ids
3. No domain module imports from another domain (enforced by structure)
4. The curriculum scheduler respects the ``domain`` filter
5. The registry's summary matches what the env sees
"""
from __future__ import annotations
def test_registry_populated_after_import():
import permanence # noqa: F401 — triggers registration
from permanence.core import get_registry
reg = get_registry()
assert "devtools" in reg.domains
assert "meridian" in reg.domains
def test_registry_action_and_task_counts_nonzero():
from permanence.core import get_registry
reg = get_registry()
s = reg.summary()
assert s["total_actions"] >= 30
assert s["total_tasks"] >= 6
def test_devtools_and_meridian_have_disjoint_task_ids():
"""Each task id belongs to exactly one domain."""
from permanence.core import get_registry
reg = get_registry()
devtools_tasks = set(reg.task_ids_by_domain("devtools"))
meridian_tasks = set(reg.task_ids_by_domain("meridian"))
overlap = devtools_tasks & meridian_tasks
assert overlap == set(), f"Task overlap between domains: {overlap}"
def test_devtools_task_ids_match_expectation():
from permanence.core import get_registry
reg = get_registry()
devtools_tasks = set(reg.task_ids_by_domain("devtools"))
expected = {
"task_log_cleanup",
"task_force_push_release",
"task_schema_migration",
"task_integrated_deploy",
}
assert expected.issubset(devtools_tasks), (
f"Missing DevTools tasks: {expected - devtools_tasks}"
)
def test_meridian_task_ids_match_expectation():
from permanence.core import get_registry
reg = get_registry()
meridian_tasks = set(reg.task_ids_by_domain("meridian"))
expected = {"task_correction", "task_conflict", "task_launch", "task_crisis", "task_cascade"}
assert expected.issubset(meridian_tasks), (
f"Missing Meridian tasks: {expected - meridian_tasks}"
)
def test_devtools_action_ids_are_namespaced():
"""All DevTools actions must start with fs_, git_, or db_."""
from permanence.core import get_registry
reg = get_registry()
dev_actions = {
aid for aid in reg.all_actions() if reg.domain_of_action(aid) == "devtools"
}
for aid in dev_actions:
assert aid.startswith(("fs_", "git_", "db_")), (
f"DevTools action not namespaced: {aid}"
)
def test_meridian_does_not_import_devtools():
"""Static check: grep the meridian package for any devtools import."""
from pathlib import Path
import permanence.domains.meridian as m
meridian_dir = Path(m.__file__).parent
for py_file in meridian_dir.rglob("*.py"):
text = py_file.read_text()
# Allow the shared core/actions imports; forbid cross-domain imports
assert "domains.devtools" not in text, (
f"{py_file} imports from devtools domain — violates separation"
)
def test_devtools_does_not_import_meridian():
from pathlib import Path
import permanence.domains.devtools as d
dev_dir = Path(d.__file__).parent
for py_file in dev_dir.rglob("*.py"):
text = py_file.read_text()
assert "domains.meridian" not in text, (
f"{py_file} imports from meridian domain — violates separation"
)
def test_curriculum_devtools_only_samples_devtools_tasks():
from permanence.tasks.task_bank import CurriculumScheduler
from permanence.core import get_registry
sched = CurriculumScheduler(domain="devtools")
reg = get_registry()
dev_tasks = set(reg.task_ids_by_domain("devtools"))
for ep in range(300):
tid = sched.select_task_id(ep)
assert tid in dev_tasks, f"Non-devtools task sampled at ep {ep}: {tid}"
def test_curriculum_meridian_only_samples_meridian_tasks():
from permanence.tasks.task_bank import CurriculumScheduler
from permanence.core import get_registry
sched = CurriculumScheduler(domain="meridian")
reg = get_registry()
mer_tasks = set(reg.task_ids_by_domain("meridian"))
for ep in range(300):
tid = sched.select_task_id(ep)
assert tid in mer_tasks, f"Non-meridian task sampled at ep {ep}: {tid}"
def test_env_honors_domain_config():
"""PermanenceEnv(config={'domain': 'meridian'}) must only see Meridian tasks."""
from permanence.env import PermanenceEnv
from permanence.core import get_registry
env = PermanenceEnv(config={"domain": "meridian"})
reg = get_registry()
mer_tasks = set(reg.task_ids_by_domain("meridian"))
for ep in range(20):
env.reset(seed=ep)
assert env._current_task.task_id in mer_tasks
def test_curriculum_warmup_phase_uses_only_standard_tasks():
""""Curriculum warmup phase: episodes 0-49 MUST be standard variants only.
If a forced variant leaks into the warmup phase it starves GRPO of
gradient (see He et al. 2025 RFCL argument)."""
from permanence.tasks.task_bank import CurriculumScheduler
sched = CurriculumScheduler(domain="devtools")
forced_ids = {
"task_log_cleanup_forced",
"task_force_push_legitimate",
"task_schema_migration_no_backup",
"task_integrated_deploy_live",
}
for ep in range(50):
tid = sched.select_task_id(ep)
assert tid not in forced_ids, (
f"Forced variant '{tid}' leaked into warmup phase at ep {ep}"
)
def test_curriculum_phases_in_forced_variants_progressively():
"""Episodes 51-150 should show ~50% forced; 151+ should show ~70%."""
from permanence.tasks.task_bank import CurriculumScheduler
sched = CurriculumScheduler(domain="devtools")
forced_ids = {
"task_log_cleanup_forced",
"task_force_push_legitimate",
"task_schema_migration_no_backup",
"task_integrated_deploy_live",
}
phase_2 = sum(1 for ep in range(51, 151) if sched.select_task_id(ep) in forced_ids)
phase_3 = sum(1 for ep in range(151, 300) if sched.select_task_id(ep) in forced_ids)
# Phase 2 expected ~50% (45-55 out of 100). Phase 3 expected ~70%
# (97-112 out of 149). Give generous tolerance since the determinstic
# hash is not perfectly uniform over small windows.
assert 30 <= phase_2 <= 70, f"phase 2 forced fraction off: {phase_2}/100"
assert 90 <= phase_3 <= 130, f"phase 3 forced fraction off: {phase_3}/149"
def test_curriculum_meridian_has_no_forced_variants():
"""Meridian doesn't define forced variants — the curriculum for
meridian must pull from standard tasks only."""
from permanence.tasks.task_bank import CurriculumScheduler
sched = CurriculumScheduler(domain="meridian")
forced_ids = {
"task_log_cleanup_forced",
"task_force_push_legitimate",
"task_schema_migration_no_backup",
"task_integrated_deploy_live",
}
for ep in range(300):
tid = sched.select_task_id(ep)
assert tid not in forced_ids, (
f"Forced (devtools) variant leaked into meridian curriculum at ep {ep}"
)
def test_forced_variants_registered_in_devtools_domain():
"""The 4 forced variants must appear in the devtools domain's task_ids."""
from permanence.core import get_registry
reg = get_registry()
dev_tasks = set(reg.task_ids_by_domain("devtools"))
forced_ids = {
"task_log_cleanup_forced",
"task_force_push_legitimate",
"task_schema_migration_no_backup",
"task_integrated_deploy_live",
}
missing = forced_ids - dev_tasks
assert missing == set(), f"Forced variants missing from registry: {missing}"