Files
hive/core/tests/test_default_skills.py
T
2026-04-02 11:02:30 -07:00

315 lines
11 KiB
Python

"""Tests for default skills — parsing, token budget, and configuration."""
from pathlib import Path
import pytest
from framework.skills.config import DefaultSkillConfig, SkillsConfig
from framework.skills.defaults import (
DATA_BUFFER_KEYS,
SKILL_REGISTRY,
DefaultSkillManager,
is_batch_scenario,
)
from framework.skills.parser import parse_skill_md
_DEFAULT_SKILLS_DIR = (
Path(__file__).resolve().parent.parent / "framework" / "skills" / "_default_skills"
)
class TestDefaultSkillFiles:
"""Verify all 6 built-in SKILL.md files parse correctly."""
def test_all_six_skills_exist(self):
assert len(SKILL_REGISTRY) == 6
@pytest.mark.parametrize("skill_name,dir_name", list(SKILL_REGISTRY.items()))
def test_skill_parses(self, skill_name, dir_name):
path = _DEFAULT_SKILLS_DIR / dir_name / "SKILL.md"
assert path.is_file(), f"Missing SKILL.md at {path}"
parsed = parse_skill_md(path, source_scope="framework")
assert parsed is not None, f"Failed to parse {path}"
assert parsed.name == skill_name
assert parsed.description
assert parsed.body
assert parsed.source_scope == "framework"
def test_combined_token_budget(self):
"""All default skill bodies combined should be under 2000 tokens (~8000 chars)."""
total_chars = 0
for dir_name in SKILL_REGISTRY.values():
path = _DEFAULT_SKILLS_DIR / dir_name / "SKILL.md"
parsed = parse_skill_md(path, source_scope="framework")
assert parsed is not None
total_chars += len(parsed.body)
approx_tokens = total_chars // 4
assert approx_tokens < 2000, (
f"Combined default skill bodies are ~{approx_tokens} tokens "
f"({total_chars} chars), exceeding the 2000 token budget"
)
def test_data_buffer_keys_all_prefixed(self):
"""All data buffer keys must start with underscore."""
for key in DATA_BUFFER_KEYS:
assert key.startswith("_"), f"Data buffer key missing _ prefix: {key}"
class TestDefaultSkillManager:
def test_load_all_defaults(self):
manager = DefaultSkillManager()
manager.load()
assert len(manager.active_skill_names) == 6
for name in SKILL_REGISTRY:
assert name in manager.active_skill_names
def test_load_idempotent(self):
manager = DefaultSkillManager()
manager.load()
first_skills = dict(manager.active_skills)
manager.load()
assert manager.active_skills == first_skills
def test_build_protocols_prompt(self):
manager = DefaultSkillManager()
manager.load()
prompt = manager.build_protocols_prompt()
assert prompt.startswith("## Operational Protocols")
# Should contain content from each active skill
for name in SKILL_REGISTRY:
skill = manager.active_skills[name]
# At least some of the body should appear
assert skill.body[:20] in prompt
def test_protocols_prompt_empty_when_all_disabled(self):
config = SkillsConfig(all_defaults_disabled=True)
manager = DefaultSkillManager(config)
manager.load()
assert manager.build_protocols_prompt() == ""
assert manager.active_skill_names == []
def test_disable_single_skill(self):
config = SkillsConfig.from_agent_vars(
default_skills={"hive.quality-monitor": {"enabled": False}}
)
manager = DefaultSkillManager(config)
manager.load()
assert "hive.quality-monitor" not in manager.active_skill_names
assert len(manager.active_skill_names) == 5
def test_disable_all_via_convention(self):
config = SkillsConfig.from_agent_vars(default_skills={"_all": {"enabled": False}})
manager = DefaultSkillManager(config)
manager.load()
assert manager.active_skill_names == []
def test_log_active_skills(self, caplog):
import logging
with caplog.at_level(logging.INFO, logger="framework.skills.defaults"):
manager = DefaultSkillManager()
manager.load()
manager.log_active_skills()
assert "Default skills active:" in caplog.text
def test_log_all_disabled(self, caplog):
import logging
config = SkillsConfig(all_defaults_disabled=True)
with caplog.at_level(logging.INFO, logger="framework.skills.defaults"):
manager = DefaultSkillManager(config)
manager.load()
manager.log_active_skills()
assert "all disabled" in caplog.text
class TestSkillsConfig:
def test_default_is_enabled(self):
config = SkillsConfig()
assert config.is_default_enabled("hive.note-taking") is True
def test_explicit_disable(self):
config = SkillsConfig(
default_skills={"hive.note-taking": DefaultSkillConfig(enabled=False)}
)
assert config.is_default_enabled("hive.note-taking") is False
assert config.is_default_enabled("hive.batch-ledger") is True
def test_all_disabled_flag(self):
config = SkillsConfig(all_defaults_disabled=True)
assert config.is_default_enabled("hive.note-taking") is False
assert config.is_default_enabled("anything") is False
def test_from_agent_vars_basic(self):
config = SkillsConfig.from_agent_vars(
default_skills={
"hive.note-taking": {"enabled": True},
"hive.quality-monitor": {"enabled": False},
},
skills=["deep-research"],
)
assert config.is_default_enabled("hive.note-taking") is True
assert config.is_default_enabled("hive.quality-monitor") is False
assert config.skills == ["deep-research"]
def test_from_agent_vars_bool_shorthand(self):
config = SkillsConfig.from_agent_vars(default_skills={"hive.note-taking": False})
assert config.is_default_enabled("hive.note-taking") is False
def test_from_agent_vars_all_disabled(self):
config = SkillsConfig.from_agent_vars(default_skills={"_all": {"enabled": False}})
assert config.all_defaults_disabled is True
def test_get_default_overrides(self):
config = SkillsConfig.from_agent_vars(
default_skills={
"hive.batch-ledger": {"enabled": True, "checkpoint_every_n": 10},
}
)
overrides = config.get_default_overrides("hive.batch-ledger")
assert overrides == {"checkpoint_every_n": 10}
def test_get_default_overrides_empty(self):
config = SkillsConfig()
assert config.get_default_overrides("hive.note-taking") == {}
def test_from_agent_vars_none_inputs(self):
config = SkillsConfig.from_agent_vars(default_skills=None, skills=None)
assert config.skills == []
assert config.default_skills == {}
assert config.all_defaults_disabled is False
class TestConfigOverrideSubstitution:
"""Config overrides replace {{placeholder}} values in injected protocol text."""
def test_quality_monitor_default_interval(self):
manager = DefaultSkillManager()
manager.load()
prompt = manager.build_protocols_prompt()
assert "Every 5 iterations" in prompt
def test_quality_monitor_override_interval(self):
config = SkillsConfig.from_agent_vars(
default_skills={"hive.quality-monitor": {"assessment_interval": 10}}
)
manager = DefaultSkillManager(config)
manager.load()
prompt = manager.build_protocols_prompt()
assert "Every 10 iterations" in prompt
assert "Every 5 iterations" not in prompt
def test_error_recovery_default_retries(self):
manager = DefaultSkillManager()
manager.load()
prompt = manager.build_protocols_prompt()
assert "3+ times" in prompt
def test_error_recovery_override_retries(self):
config = SkillsConfig.from_agent_vars(
default_skills={"hive.error-recovery": {"max_retries_per_tool": 5}}
)
manager = DefaultSkillManager(config)
manager.load()
prompt = manager.build_protocols_prompt()
assert "5+ times" in prompt
assert "3+ times" not in prompt
def test_context_preservation_default_threshold(self):
manager = DefaultSkillManager()
manager.load()
prompt = manager.build_protocols_prompt()
assert "45%" in prompt
def test_context_preservation_override_threshold(self):
config = SkillsConfig.from_agent_vars(
default_skills={"hive.context-preservation": {"warn_at_usage_ratio": 0.4}}
)
manager = DefaultSkillManager(config)
manager.load()
prompt = manager.build_protocols_prompt()
assert "40%" in prompt
assert "45%" not in prompt
def test_no_unreplaced_placeholders_with_defaults(self):
"""All {{...}} placeholders should be replaced when using defaults."""
manager = DefaultSkillManager()
manager.load()
prompt = manager.build_protocols_prompt()
assert "{{" not in prompt
class TestBatchAutoDetection:
"""DS-12: is_batch_scenario() and batch_init_nudge property."""
def test_detects_list_of(self):
assert is_batch_scenario("process a list of 100 leads") is True
def test_detects_collection_of(self):
assert is_batch_scenario("a collection of invoices") is True
def test_detects_items(self):
assert is_batch_scenario("go through all items in the spreadsheet") is True
def test_detects_for_each(self):
assert is_batch_scenario("for each record, send an email") is True
def test_no_match_single_task(self):
assert is_batch_scenario("write a summary of the quarterly report") is False
def test_batch_nudge_active_by_default(self):
manager = DefaultSkillManager()
manager.load()
assert manager.batch_init_nudge is not None
assert "_batch_ledger" in manager.batch_init_nudge
def test_batch_nudge_none_when_skill_disabled(self):
config = SkillsConfig.from_agent_vars(
default_skills={"hive.batch-ledger": {"enabled": False}}
)
manager = DefaultSkillManager(config)
manager.load()
assert manager.batch_init_nudge is None
def test_batch_nudge_none_when_auto_detect_disabled(self):
config = SkillsConfig.from_agent_vars(
default_skills={"hive.batch-ledger": {"auto_detect_batch": False}}
)
manager = DefaultSkillManager(config)
manager.load()
assert manager.batch_init_nudge is None
class TestContextWarnRatio:
"""DS-13: context_warn_ratio property."""
def test_default_ratio(self):
manager = DefaultSkillManager()
manager.load()
assert manager.context_warn_ratio == pytest.approx(0.45)
def test_override_ratio(self):
config = SkillsConfig.from_agent_vars(
default_skills={"hive.context-preservation": {"warn_at_usage_ratio": 0.3}}
)
manager = DefaultSkillManager(config)
manager.load()
assert manager.context_warn_ratio == pytest.approx(0.3)
def test_ratio_none_when_skill_disabled(self):
config = SkillsConfig.from_agent_vars(
default_skills={"hive.context-preservation": {"enabled": False}}
)
manager = DefaultSkillManager(config)
manager.load()
assert manager.context_warn_ratio is None