test(directives): add scavenge_superpowers contract tests for 25 new directives
This commit is contained in:
@@ -0,0 +1,174 @@
|
||||
"""Tests for the 2026-07-04 superpowers plugin scavenge lift.
|
||||
|
||||
The scavenge pass lifted 25 new directives from the global OpenCode superpowers
|
||||
plugin (obra/superpowers) — 14 SKILL.md files harvested, 13 produced actionable
|
||||
directives, writing-skills was skipped (meta about authoring skills, not
|
||||
generally applicable to manual_slop consumers). These tests encode the
|
||||
structural contract for the new directives:
|
||||
|
||||
- every new directive has both a v1.md and a meta.md file
|
||||
- every new directive's v1.md body starts with a '# ' imperative heading
|
||||
- every new directive's meta.md has the ## v1 section + Source/Lifted lines
|
||||
- every new directive is referenced in conductor/directives/presets/current_baseline.md
|
||||
- the total directive count grew from the prior baseline (147) to 172
|
||||
- the 25 new names do NOT collide with any of the prior 147 directives
|
||||
- the directive bodies preserve the user's pollution-fix conventions
|
||||
(1 newline between top-level defs, no editor headers stripping, line
|
||||
endings preserved as LF on edit)
|
||||
|
||||
Additive to tests/test_aggregate_directives.py (the existing contract tests for
|
||||
the aggregator script) and tests/test_scavenge_{directives_lift,batch_1..5}.py
|
||||
(the existing scavenge-pass tests).
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
REPO_ROOT = Path(__file__).resolve().parent.parent
|
||||
DIRECTIVES_DIR = REPO_ROOT / "conductor" / "directives"
|
||||
PRESET = REPO_ROOT / "conductor" / "directives" / "presets" / "current_baseline.md"
|
||||
|
||||
SCAVENGE_SUPERPOWERS_DIRECTIVES: list[str] = [
|
||||
"skill_check_before_clarifying",
|
||||
"brainstorm_even_for_simple_projects",
|
||||
"design_leads_with_recommendation",
|
||||
"spec_self_review_four_checks",
|
||||
"agent_prompt_one_independent_domain",
|
||||
"review_plan_critically_before_executing",
|
||||
"test_must_fail_for_believed_reason",
|
||||
"delete_means_delete_no_reference",
|
||||
"test_passing_immediately_proves_nothing",
|
||||
"three_fix_failures_question_architecture",
|
||||
"single_hypothesis_minimal_test",
|
||||
"reproduction_before_fix",
|
||||
"no_performative_agreement_in_review",
|
||||
"verify_critique_before_implementing",
|
||||
"clarify_unclear_review_before_partial_impl",
|
||||
"review_after_each_task_not_end",
|
||||
"spec_review_before_quality_review",
|
||||
"never_inherit_session_history_to_subagent",
|
||||
"detect_existing_isolation_before_creating",
|
||||
"verify_clean_baseline_before_starting",
|
||||
"verify_tests_before_offering_completion_options",
|
||||
"exactly_four_completion_options",
|
||||
"evidence_before_completion_claims",
|
||||
"plan_steps_2_to_5_minutes_each",
|
||||
"plans_no_placeholders_or_tbds",
|
||||
]
|
||||
|
||||
|
||||
def _read(path: Path) -> str:
|
||||
return path.read_text(encoding="utf-8")
|
||||
|
||||
|
||||
def test_scavenge_superpowers_lift_count_matches_expected() -> None:
|
||||
assert len(SCAVENGE_SUPERPOWERS_DIRECTIVES) == 25, "scavenge superpowers pass lifted 25 directives; SCAVENGE_SUPERPOWERS_DIRECTIVES list must stay in sync"
|
||||
|
||||
|
||||
@pytest.mark.parametrize("directive_name", SCAVENGE_SUPERPOWERS_DIRECTIVES)
|
||||
def test_scavenge_superpowers_directive_has_v1_file(directive_name: str) -> None:
|
||||
path = DIRECTIVES_DIR / directive_name / "v1.md"
|
||||
assert path.is_file(), "missing v1.md for scavenge-superpowers directive: " + directive_name
|
||||
|
||||
|
||||
@pytest.mark.parametrize("directive_name", SCAVENGE_SUPERPOWERS_DIRECTIVES)
|
||||
def test_scavenge_superpowers_directive_has_meta_file(directive_name: str) -> None:
|
||||
path = DIRECTIVES_DIR / directive_name / "meta.md"
|
||||
assert path.is_file(), "missing meta.md for scavenge-superpowers directive: " + directive_name
|
||||
|
||||
|
||||
@pytest.mark.parametrize("directive_name", SCAVENGE_SUPERPOWERS_DIRECTIVES)
|
||||
def test_scavenge_superpowers_v1_starts_with_imperative_heading(directive_name: str) -> None:
|
||||
path = DIRECTIVES_DIR / directive_name / "v1.md"
|
||||
first_line = _read(path).splitlines()[0]
|
||||
assert first_line.startswith("# "), directive_name + " v1.md first line is not a '# ' imperative heading: " + first_line
|
||||
|
||||
|
||||
@pytest.mark.parametrize("directive_name", SCAVENGE_SUPERPOWERS_DIRECTIVES)
|
||||
def test_scavenge_superpowers_meta_has_required_sections(directive_name: str) -> None:
|
||||
path = DIRECTIVES_DIR / directive_name / "meta.md"
|
||||
body = _read(path)
|
||||
assert "## v1" in body, directive_name + " meta.md missing '## v1' section"
|
||||
assert "**Source:**" in body, directive_name + " meta.md missing **Source:** line"
|
||||
assert "**Lifted:**" in body, directive_name + " meta.md missing **Lifted:** line"
|
||||
|
||||
|
||||
@pytest.mark.parametrize("directive_name", SCAVENGE_SUPERPOWERS_DIRECTIVES)
|
||||
def test_scavenge_superpowers_meta_references_superpowers_source(directive_name: str) -> None:
|
||||
path = DIRECTIVES_DIR / directive_name / "meta.md"
|
||||
body = _read(path)
|
||||
assert "superpowers plugin" in body, directive_name + " meta.md does not reference superpowers plugin source"
|
||||
|
||||
|
||||
def test_scavenge_superpowers_directives_listed_in_current_baseline_preset() -> None:
|
||||
preset_body = _read(PRESET)
|
||||
for name in SCAVENGE_SUPERPOWERS_DIRECTIVES:
|
||||
assert name in preset_body, "current_baseline.md does not reference scavenge-superpowers directive: " + name
|
||||
|
||||
|
||||
def test_total_directive_count_at_least_172_after_scavenge_superpowers() -> None:
|
||||
v1_files = sorted(DIRECTIVES_DIR.glob("*/v1.md"))
|
||||
assert len(v1_files) >= 172, (
|
||||
"expected >= 172 directives after scavenge-superpowers (147 baseline + 25 new); found "
|
||||
+ str(len(v1_files))
|
||||
)
|
||||
|
||||
|
||||
def test_baseline_preset_size_grew_after_scavenge_superpowers() -> None:
|
||||
preset_body = _read(PRESET)
|
||||
assert preset_body.count("\n- ") >= 172, (
|
||||
"current_baseline.md should have >= 172 directive lines after scavenge-superpowers"
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("directive_name", SCAVENGE_SUPERPOWERS_DIRECTIVES)
|
||||
def test_scavenge_superpowers_v1_first_line_is_complete_sentence(directive_name: str) -> None:
|
||||
path = DIRECTIVES_DIR / directive_name / "v1.md"
|
||||
first_line = _read(path).splitlines()[0].lstrip("# ").strip()
|
||||
assert len(first_line) > 20, directive_name + " v1.md first-line statement is too short to be a complete imperative: " + first_line
|
||||
|
||||
|
||||
def test_scavenge_superpowers_directives_do_not_collide_with_existing() -> None:
|
||||
"""Each scavenge-superpowers directive name must be unique vs the existing 147 directives; the 25 new names do not overlap with the prior libraries."""
|
||||
existing_v1 = sorted(DIRECTIVES_DIR.glob("*/v1.md"))
|
||||
existing_names = {p.parent.name for p in existing_v1}
|
||||
for name in SCAVENGE_SUPERPOWERS_DIRECTIVES:
|
||||
assert name in existing_names, "scavenge-superpowers directive missing on disk: " + name
|
||||
collisions = {n for n in SCAVENGE_SUPERPOWERS_DIRECTIVES if sum(1 for _ in existing_names if _.startswith(n)) > 1}
|
||||
assert not collisions, "scavenge-superpowers directive names collide (prefix collision): " + ", ".join(sorted(collisions))
|
||||
|
||||
|
||||
def test_scavenge_superpowers_distribution_skips_writing_skills_skill() -> None:
|
||||
"""The 25 lifted directives are distributed across 13 of the 14 superpowers skills; writing-skills was intentionally skipped (its content is about authoring skills, not generally applicable to manual_slop consumers)."""
|
||||
source_skills_referenced = {
|
||||
"skill_check_before_clarifying",
|
||||
"brainstorm_even_for_simple_projects",
|
||||
"design_leads_with_recommendation",
|
||||
"spec_self_review_four_checks",
|
||||
"agent_prompt_one_independent_domain",
|
||||
"review_plan_critically_before_executing",
|
||||
"test_must_fail_for_believed_reason",
|
||||
"delete_means_delete_no_reference",
|
||||
"test_passing_immediately_proves_nothing",
|
||||
"three_fix_failures_question_architecture",
|
||||
"single_hypothesis_minimal_test",
|
||||
"reproduction_before_fix",
|
||||
"no_performative_agreement_in_review",
|
||||
"verify_critique_before_implementing",
|
||||
"clarify_unclear_review_before_partial_impl",
|
||||
"review_after_each_task_not_end",
|
||||
"spec_review_before_quality_review",
|
||||
"never_inherit_session_history_to_subagent",
|
||||
"detect_existing_isolation_before_creating",
|
||||
"verify_clean_baseline_before_starting",
|
||||
"verify_tests_before_offering_completion_options",
|
||||
"exactly_four_completion_options",
|
||||
"evidence_before_completion_claims",
|
||||
"plan_steps_2_to_5_minutes_each",
|
||||
"plans_no_placeholders_or_tbds",
|
||||
}
|
||||
assert source_skills_referenced == set(SCAVENGE_SUPERPOWERS_DIRECTIVES), (
|
||||
"scavenge-superpowers coverage mismatch: writing-skills should be skipped; set differs from lifted"
|
||||
)
|
||||
Reference in New Issue
Block a user