2026-06-14 11:24:36 -04:00
|
|
|
"""Regression test for task path conventions in prompts and templates."""
|
|
|
|
|
|
|
|
|
|
import re
|
|
|
|
|
from pathlib import Path
|
|
|
|
|
|
|
|
|
|
import pytest
|
|
|
|
|
|
|
|
|
|
ROOT = Path(__file__).resolve().parent.parent
|
|
|
|
|
PROMPTS_DIR = ROOT / "prompts"
|
|
|
|
|
TEMPLATES_DIR = ROOT / "templates"
|
|
|
|
|
|
2026-06-15 14:16:46 -04:00
|
|
|
# Legacy path pattern that should no longer appear (with placeholder).
|
2026-06-14 11:24:36 -04:00
|
|
|
LEGACY_PATH = re.compile(r"\{project\}/tasks/\{task-name\}/")
|
|
|
|
|
# Canonical path pattern that should be used instead.
|
|
|
|
|
CANONICAL_PATH = re.compile(r"\{project\}/\.automaton/tasks/\{task-name\}/")
|
2026-06-15 14:16:46 -04:00
|
|
|
# Concrete legacy path pattern: {project}/tasks/ followed by any name.
|
|
|
|
|
# Catches paths like {project}/tasks/onboarding/ that use concrete names.
|
|
|
|
|
CONCRETE_LEGACY_PATH = re.compile(r"\{project\}/tasks/[\w-]+/")
|
2026-06-14 11:24:36 -04:00
|
|
|
|
|
|
|
|
|
|
|
|
|
def _markdown_files(*directories: Path) -> list[Path]:
|
|
|
|
|
files = []
|
|
|
|
|
for directory in directories:
|
|
|
|
|
if directory.exists():
|
|
|
|
|
files.extend(directory.rglob("*.md"))
|
|
|
|
|
return files
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.parametrize("path", _markdown_files(PROMPTS_DIR, TEMPLATES_DIR))
|
|
|
|
|
def test_no_legacy_task_paths(path: Path) -> None:
|
|
|
|
|
"""Every prompt/template must use the canonical .automaton/tasks path."""
|
|
|
|
|
text = path.read_text(encoding="utf-8")
|
|
|
|
|
legacy_matches = LEGACY_PATH.findall(text)
|
|
|
|
|
assert not legacy_matches, (
|
|
|
|
|
f"Found legacy task path in {path.relative_to(ROOT)}: {legacy_matches}\n"
|
|
|
|
|
"Use {project}/.automaton/tasks/{task-name}/ instead."
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
2026-06-15 14:16:46 -04:00
|
|
|
@pytest.mark.parametrize("path", _markdown_files(PROMPTS_DIR, TEMPLATES_DIR))
|
|
|
|
|
def test_no_concrete_legacy_task_paths(path: Path) -> None:
|
|
|
|
|
"""Every prompt/template must not use {project}/tasks/ with concrete task names."""
|
|
|
|
|
text = path.read_text(encoding="utf-8")
|
|
|
|
|
# Exclude the migration detection reference, which legitimately mentions the deprecated path.
|
|
|
|
|
concrete_matches = CONCRETE_LEGACY_PATH.findall(text)
|
|
|
|
|
# Filter out the onboarding migration detection section that references the legacy path.
|
|
|
|
|
if concrete_matches and "Migration Check" in text:
|
|
|
|
|
lines = text.splitlines()
|
|
|
|
|
filtered = []
|
|
|
|
|
in_migration = False
|
|
|
|
|
for line in lines:
|
|
|
|
|
if "Migration Check" in line or "Migration Detection" in line:
|
|
|
|
|
in_migration = True
|
|
|
|
|
if in_migration and line.startswith("#") and "Migration" not in line:
|
|
|
|
|
in_migration = False
|
|
|
|
|
if not in_migration:
|
|
|
|
|
for m in CONCRETE_LEGACY_PATH.findall(line):
|
|
|
|
|
filtered.append(m)
|
|
|
|
|
concrete_matches = filtered
|
|
|
|
|
assert not concrete_matches, (
|
|
|
|
|
f"Found concrete legacy task path in {path.relative_to(ROOT)}: {concrete_matches}\n"
|
|
|
|
|
"Use {project}/.automaton/tasks/ instead of {project}/tasks/."
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
2026-06-14 11:24:36 -04:00
|
|
|
def test_canonical_path_present_in_prompts() -> None:
|
|
|
|
|
"""At least one prompt uses the canonical path (sanity check)."""
|
|
|
|
|
found = False
|
|
|
|
|
for path in _markdown_files(PROMPTS_DIR):
|
|
|
|
|
if CANONICAL_PATH.search(path.read_text(encoding="utf-8")):
|
|
|
|
|
found = True
|
|
|
|
|
break
|
|
|
|
|
assert found, "No prompt uses the canonical {project}/.automaton/tasks/{task-name}/ path"
|