Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 83 additions & 0 deletions tests/test_skill_eval_optional_dependencies.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
"""Lightweight controllers must remain usable without the optional skills parser."""

import subprocess
import sys
import textwrap
import unittest
from pathlib import Path


class OptionalSkillsDependencyTests(unittest.TestCase):
def run_without_yaml(self, source):
root = Path(__file__).resolve().parents[1]
script = (
f"import sys; sys.path[:0] = {[str(root), str(root / 'src')]!r}\n"
"sys.modules['yaml'] = None\n"
+ textwrap.dedent(source)
)
result = subprocess.run(
[sys.executable, "-I", "-S", "-c", script],
capture_output=True, text=True, check=False,
)
self.assertEqual(result.returncode, 0, result.stderr)

def test_comparison_reporting_without_yaml(self):
self.run_without_yaml("""
from tools.skill_eval_compare import condition_summary, read_report

summary = condition_summary([])
assert set(summary) == {'baseline', 'candidate'}
assert all(item['input_tokens'] is None for item in summary.values())
assert callable(read_report)
assert 'inferrsi.skill_catalog' not in sys.modules
""")

def test_checkpoint_without_yaml(self):
self.run_without_yaml("""
from tools.goal_checkpoint import recommend

result = recommend({'problems': ['missing_evidence'], 'sources': {},
'input_sha256': '0' * 64})
assert result.action == 'repair_evidence'
assert result.reasons == ('missing_evidence',)
assert 'inferrsi.skill_catalog' not in sys.modules
""")

def test_daily_controller_without_yaml(self):
self.run_without_yaml("""
from tools.skill_eval_auto import daily_anchor
from tools.skill_eval_budget import BudgetError

try:
daily_anchor({'schema_version': 1})
except BudgetError as error:
assert 'Audited global daily genesis' in str(error)
else:
raise AssertionError('Daily controller accepted missing budget configuration')
assert 'inferrsi.skill_catalog' not in sys.modules
""")

def test_selected_snapshot_still_requires_yaml_before_side_effects(self):
self.run_without_yaml("""
import tempfile
from pathlib import Path
from unittest.mock import patch
from tools.skill_eval_snapshot import selected_snapshot

with tempfile.TemporaryDirectory() as temporary:
root = Path(temporary)
destination = root / 'output'
with patch('subprocess.check_output') as git:
try:
selected_snapshot(root, 'a' * 40, 'library', 'selected', destination)
except ValueError as error:
assert "'skills' extra (PyYAML)" in str(error), str(error)
else:
raise AssertionError('Selected snapshot silently skipped the skills parser')
git.assert_not_called()
assert not destination.exists()
""")


if __name__ == "__main__":
unittest.main()
5 changes: 4 additions & 1 deletion tools/skill_eval_snapshot.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@

from inferrsi.catalog import BACKENDS
from inferrsi.knowledge_operations import KnowledgeReader, safe_path
from inferrsi.skill_catalog import _skill_name, _unique_fields, skill_metadata

GIT_READ = ("git", "--no-replace-objects", "--no-lazy-fetch")

Expand All @@ -43,6 +42,10 @@ def selected_snapshot(repo: Path, revision: str, skill_root: str, skill: str,
All selected Git entries must be ordinary non-executable blobs, including
files the reader considers incidental. Failure publishes no destination.
"""
# Only materializing checkout skills requires the optional PyYAML parser;
# reporting and controller imports must remain usable in the base install.
from inferrsi.skill_catalog import _skill_name, _unique_fields, skill_metadata

if not re.fullmatch(r"[0-9a-f]{40}", revision):
raise ValueError("Selected snapshot requires a full immutable Git commit SHA")
_path(skill_root)
Expand Down
Loading