191 lines
9.3 KiB
Python
191 lines
9.3 KiB
Python
"""Tests for the 2026-07-03 scavenge sweep (batch 5/5: guides + role prompts + transcripts).
|
|
|
|
Lifted 11 new directives from guides, role prompts, and transcripts. Each one
|
|
encodes a process rule or architectural invariant. These tests pin the structural
|
|
contract:
|
|
|
|
- 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 (124) to 135
|
|
|
|
Additive to tests/test_aggregate_directives.py, tests/test_scavenge_directives_lift.py,
|
|
and tests/test_scavenge_batch_1.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_BATCH_5_DIRECTIVES: list[str] = [
|
|
"anti_entropy_state_audit_before_adding",
|
|
"audit_before_claiming_current_state",
|
|
"manual_compaction_only_no_auto_summarize",
|
|
"meta_tooling_app_boundary_check",
|
|
"spec_template_required_6_sections",
|
|
"system_reminder_redact_don_act",
|
|
"tier1_first_commit_6file_acknowledgment",
|
|
"tier2_post_track_ruff_mypy_audit",
|
|
"tier2_pre_commit_deletion_and_diff_check",
|
|
"tier2_pre_flight_audit_gates",
|
|
"worker_three_point_abort_check",
|
|
]
|
|
|
|
|
|
def _read(path: Path) -> str:
|
|
return path.read_text(encoding="utf-8")
|
|
|
|
|
|
def test_scavenge_batch_5_lift_count_matches_expected() -> None:
|
|
assert len(SCAVENGE_BATCH_5_DIRECTIVES) == 11, "scavenge batch 5 lifted 11 directives; list must stay in sync"
|
|
|
|
|
|
@pytest.mark.parametrize("directive_name", SCAVENGE_BATCH_5_DIRECTIVES)
|
|
def test_scavenge_batch_5_directive_has_v1_file(directive_name: str) -> None:
|
|
path = DIRECTIVES_DIR / directive_name / "v1.md"
|
|
assert path.is_file(), "missing v1.md for scavenge-batch-5 directive: " + directive_name
|
|
|
|
|
|
@pytest.mark.parametrize("directive_name", SCAVENGE_BATCH_5_DIRECTIVES)
|
|
def test_scavenge_batch_5_directive_has_meta_file(directive_name: str) -> None:
|
|
path = DIRECTIVES_DIR / directive_name / "meta.md"
|
|
assert path.is_file(), "missing meta.md for scavenge-batch-5 directive: " + directive_name
|
|
|
|
|
|
@pytest.mark.parametrize("directive_name", SCAVENGE_BATCH_5_DIRECTIVES)
|
|
def test_scavenge_batch_5_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_BATCH_5_DIRECTIVES)
|
|
def test_scavenge_batch_5_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"
|
|
|
|
|
|
def test_scavenge_batch_5_directives_listed_in_current_baseline_preset() -> None:
|
|
preset_body = _read(PRESET)
|
|
for name in SCAVENGE_BATCH_5_DIRECTIVES:
|
|
assert name in preset_body, (
|
|
"current_baseline.md does not reference scavenge-batch-5 directive: " + name
|
|
)
|
|
|
|
|
|
def test_total_directive_count_at_least_135_after_scavenge_batch_5() -> None:
|
|
v1_files = sorted(DIRECTIVES_DIR.glob("*/v1.md"))
|
|
assert len(v1_files) >= 135, (
|
|
"expected >= 135 directives after scavenge batch 5 (124 baseline + 11 batch-5); found "
|
|
+ str(len(v1_files))
|
|
)
|
|
|
|
|
|
def test_baseline_preset_size_grew_after_scavenge_batch_5() -> None:
|
|
preset_body = _read(PRESET)
|
|
assert preset_body.count("\n- ") >= 135, (
|
|
"current_baseline.md should have >= 135 directive lines after scavenge batch 5"
|
|
)
|
|
|
|
|
|
@pytest.mark.parametrize("directive_name", SCAVENGE_BATCH_5_DIRECTIVES)
|
|
def test_scavenge_batch_5_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_batch_5_directives_do_not_collide_with_existing() -> None:
|
|
"""Each batch-5 directive name must be unique vs the batch-1 (9 directives) and
|
|
batch-3 scavenge sets; the 11 new names do not overlap with the prior 124."""
|
|
prior_batches = {
|
|
"adapt_test_mocks_to_production_api_change", "acknowledgment_in_first_commit",
|
|
"ast_parse_insufficient", "ast_verify_class_methods_after_edit",
|
|
"atomic_per_task_commits", "ban_any_type", "ban_appdata_paths",
|
|
"ban_arbitrary_core_mocking", "ban_day_estimates", "ban_dict_any",
|
|
"ban_dict_get_on_known_fields", "ban_getattr_dispatch",
|
|
"ban_hasattr_dispatch", "ban_local_imports", "ban_optional_returns",
|
|
"ban_prefix_aliasing", "ban_repeated_from_from",
|
|
"batch_verification_not_isolation", "boundary_layer_exception",
|
|
"cache_stable_to_volatile", "cheap_fix_first_investigation_phases",
|
|
"chroma_cache_path", "chronology_must_regenerate_after_every_track_ship",
|
|
"classifier_must_emit_per_row_evidence", "comprehensive_logging",
|
|
"config_state_owner", "contract_change_audit",
|
|
"controller_property_delegation_no_dual_state",
|
|
"convention_enforcement_4_mechanisms", "core_value_read_first",
|
|
"decompose_or_isolate_never_offload", "decorator_orphan_pitfall",
|
|
"defer_heavy_sdk_imports_to_subprocess", "defer_not_catch_for_native_crashes",
|
|
"deduction_loop_limit", "deterministic_signal_endpoint_pattern",
|
|
"docs_philosophy_then_boundaries_then_logic_then_verify",
|
|
"dsl_uses_first_class_spans_for_errors", "edit_small_incremental",
|
|
"end_of_track_report_required", "enforce_no_real_toml_in_tests",
|
|
"failure_message_actionable_not_vague", "feature_flag_delete_to_turn_off",
|
|
"file_id_stable_across_rename", "file_naming_convention",
|
|
"float_only_math_for_visual_transforms", "fragile_test_in_batch_is_failing_test",
|
|
"generation_script_walks_filesystem_fresh_each_run", "git_hard_bans",
|
|
"graceful_optional_dependency_degradation",
|
|
"imgui_scope_entered_flag_for_no_op_return", "imgui_scope_verification",
|
|
"imscope_tuple_return_per_scope_override", "inherited_cruft_ask_first",
|
|
"intent_signal_postfix_not_xml", "interceptor_activates_only_on_matching_shape",
|
|
"knowledge_harvest_pattern", "large_files_are_fine", "master_branch_default",
|
|
"live_gui_poll_not_sleep", "live_gui_session_scoped_no_restart",
|
|
"log_pruner_backoff_for_locked_files", "mandatory_research_first",
|
|
"metadata_boundary_type", "missing_data_renders_as_em_dash_not_crash",
|
|
"modal_explicit_opened_list_for_lifecycle", "modular_controller_pattern",
|
|
"neutral_language_for_doc_drift", "nil_sentinel_pattern",
|
|
"no_comments_in_body", "no_conductor_yaml_for_artifacts",
|
|
"no_content_duplication_across_agent_docs", "no_diagnostic_noise",
|
|
"no_new_src_files_without_permission", "no_output_filtering",
|
|
"no_real_io_during_tests", "no_skip_markers_as_avoidance",
|
|
"one_space_indent", "opt_in_integration_test_via_env_var_marker",
|
|
"parse_failure_visible_to_conversation", "pathlib_read_write_no_newline_kwarg",
|
|
"per_aggregate_dataclass_promotion", "per_conversation_scratch_dir",
|
|
"per_dimension_pick_dim_not_tool", "per_phase_metric_regression_fix",
|
|
"pipeline_immediate_mode_no_object", "prefer_targeted_tier_runs",
|
|
"preserve_before_compact_archive", "preserve_line_endings",
|
|
"preserve_prior_versions_of_review_docs", "profile_first_optimize_second",
|
|
"quality_gate_catches_broken_classifier_before_ship",
|
|
"quarantine_flag_the_engine_not_shared_types", "rag_six_rules",
|
|
"report_instead_of_fix_ban", "reset_session_preserves_project_path",
|
|
"result_error_pattern", "run_full_tier_after_phase_refactor",
|
|
"runtime_config_flag_vs_test_env_var_gate", "scope_creep_track_doc_ban",
|
|
"sdm_dependency_tags", "search_all_call_sites_after_signature_change",
|
|
"state_visible_at_the_right_layer", "strict_state_management",
|
|
"stub_before_implement", "subagent_returns_artifact_not_transcript",
|
|
"submit_io_lazy_pool_recreation", "surface_dirty_state_in_test_runner",
|
|
"surface_gaps_at_discovery_not_checkpoint",
|
|
"surface_upstream_api_limits_honestly_in_spec",
|
|
"throwaway_scripts_isolated_subdir", "tdd_red_green_required",
|
|
"test_classification_via_import_presence", "test_instantiation_not_mock_away",
|
|
"test_narrow_not_kitchen_sink", "test_sandbox",
|
|
"three_tier_test_strategy_for_fragile_subsystems",
|
|
"tier1_orchestrator_no_implementation", "tier3_worker_amnesia",
|
|
"tier4_qa_compressed_fix", "timeline_is_immutable",
|
|
"token_firewall_prevents_bloat", "toml_loader_global_then_project_merge",
|
|
"type_hints_required", "typed_dataclass_fields", "ui_delegation_for_hot_reload",
|
|
"undo_redo_100_snapshot_capacity", "use_batched_test_runner",
|
|
"use_git_history_as_classification_source_of_truth",
|
|
"user_corrections_log_in_state_toml", "verbatim_lift_not_rewrite",
|
|
"view_composes_does_not_leak_into_theme_get_color",
|
|
"verify_before_editing", "verbose_commit_message_ban",
|
|
"warm_md_duplicates_not_in_place", "workspace_paths",
|
|
}
|
|
for name in SCAVENGE_BATCH_5_DIRECTIVES:
|
|
assert name not in prior_batches, (
|
|
"scavenge batch 5 directive name collides with an existing directive: " + name
|
|
)
|