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
2 changes: 1 addition & 1 deletion src/autoskillit/recipes/diagrams/implementation-groups.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<!-- autoskillit-recipe-hash: sha256:0000000000000000000000000000000000000000000000000000000000000000 -->
<!-- autoskillit-recipe-hash: sha256:c8e501dca905a687bfceb05c903a86d7f4bdb9d0057fe39dc510a303d595e1bf -->
<!-- autoskillit-diagram-format: v7 -->
## implementation-groups
Group-based implementation with per-group plan/implement/test cycles and PR gates.
Expand Down
2 changes: 1 addition & 1 deletion src/autoskillit/recipes/diagrams/implementation.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<!-- autoskillit-recipe-hash: sha256:169e575e265babf6ca6e9eebb9b33d0614759e29b68b5f613d179d82461ec2ea -->
<!-- autoskillit-recipe-hash: sha256:6387790f3513145bccf0f7c64f4d1ac8e3b807f61bd3ff217da33b1bdefef946 -->
<!-- autoskillit-diagram-format: v7 -->
# implementation

Expand Down
2 changes: 1 addition & 1 deletion src/autoskillit/recipes/diagrams/merge-prs.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<!-- autoskillit-recipe-hash: sha256:0000000000000000000000000000000000000000000000000000000000000000 -->
<!-- autoskillit-recipe-hash: sha256:a5e0288f51345023d8161c7568652dc056eb49b396ab23f7803ff26d62e98ed4 -->
<!-- autoskillit-diagram-format: v7 -->
## merge-prs
Merge multiple PRs into an integration branch with conflict resolution and CI gates.
Expand Down
2 changes: 1 addition & 1 deletion src/autoskillit/recipes/diagrams/remediation.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<!-- autoskillit-recipe-hash: sha256:0000000000000000000000000000000000000000000000000000000000000000 -->
<!-- autoskillit-recipe-hash: sha256:3a11f66644f46f34ca24a05b186d0d2c7421a6c331a9440d764d0450f9a8ac27 -->
<!-- autoskillit-diagram-format: v7 -->
## remediation
Investigate, rectify, implement, and merge a bug fix with CI and PR gates.
Expand Down
1 change: 1 addition & 0 deletions tests/arch/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ AST enforcement, sub-package layer contracts, and architectural invariant tests.
| `test_pyright_suppression_allowlist.py` | REQ-PYRIGHT-001: pyright/type-ignore suppression allowlist + count budget |
| `test_python_no_hardcoded_temp.py` | Architectural invariant: no literal `.autoskillit/temp` outside the whitelist |
| `test_quota_capability_isolation.py` | AST guard: quota modules must not reference BackendCapabilities fields |
| `test_recipe_diagram_freshness.py` | Parametrized diagram freshness enforcement: bundled recipes must have non-stale diagrams; missing diagrams are xfail(strict=True) with shrink-enforcement meta-test |
| `test_recipe_rule_registration.py` | REQ-RECIPE-001: every recipe/rules_*.py file must be imported by recipe/__init__.py |
| `test_rule_severity_consistency.py` | AST guards: rule functions must use make_finding()/make_block_finding(); RuleFinding severity must match @semantic_rule decorator severity; _KNOWN_NON_CONFORMING_RULES entries must carry tracking comments |
| `test_regex_guards.py` | Arch guard: keyword regexes in cmd-scanning rules must use path-safe lookbehind guards |
Expand Down
80 changes: 80 additions & 0 deletions tests/arch/test_recipe_diagram_freshness.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
"""Parametrized diagram freshness enforcement: bundled recipes must have non-stale
diagrams; missing diagrams are xfail(strict=True) with shrink-enforcement meta-test.
"""

from __future__ import annotations

from pathlib import Path
from typing import Any

import pytest

from autoskillit.recipe.diagrams import check_diagram_staleness
from autoskillit.recipe.io import builtin_recipes_dir

pytestmark = [pytest.mark.layer("arch"), pytest.mark.small]

_RECIPES_DIR = builtin_recipes_dir()
_BUNDLED_RECIPE_YAML_PATHS = sorted(_RECIPES_DIR.glob("*.yaml"))

MINIMUM_RECIPE_COUNT = 10
CURRENT_XFAIL_CAP = 10

_RECIPES_WITH_DIAGRAMS = [
p for p in _BUNDLED_RECIPE_YAML_PATHS if (_RECIPES_DIR / "diagrams" / f"{p.stem}.md").exists()
]
_WITHOUT_SET = set(_BUNDLED_RECIPE_YAML_PATHS) - set(_RECIPES_WITH_DIAGRAMS)
_RECIPES_WITHOUT_DIAGRAMS = sorted(_WITHOUT_SET)


def _make_params() -> list[Any]:
params: list[Any] = []
for p in _BUNDLED_RECIPE_YAML_PATHS:
if p in _WITHOUT_SET:
params.append(
pytest.param(
p,
id=p.stem,
marks=pytest.mark.xfail(
strict=True,
reason=f"diagram not yet created for {p.stem}",
),
)
)
else:
params.append(pytest.param(p, id=p.stem))
return params


@pytest.mark.parametrize("recipe_path", _make_params())
def test_bundled_recipe_diagram_not_stale(recipe_path: Path) -> None:
is_stale = check_diagram_staleness(recipe_path.stem, _RECIPES_DIR, recipe_path)
assert not is_stale, (
f"Diagram for '{recipe_path.stem}' is stale or missing. "
f"Run '/render-recipe {recipe_path.stem}' to regenerate."
)


def test_minimum_recipe_count() -> None:
assert len(_BUNDLED_RECIPE_YAML_PATHS) >= MINIMUM_RECIPE_COUNT, (
f"Expected at least {MINIMUM_RECIPE_COUNT} bundled recipe YAMLs, "
f"found {len(_BUNDLED_RECIPE_YAML_PATHS)}. "
"Is builtin_recipes_dir() resolving correctly?"
)


def test_xfail_diagram_count_is_shrinking() -> None:
assert len(_RECIPES_WITHOUT_DIAGRAMS) <= CURRENT_XFAIL_CAP, (
f"Expected at most {CURRENT_XFAIL_CAP} recipes without diagrams, "
f"found {len(_RECIPES_WITHOUT_DIAGRAMS)}: "
f"{sorted(p.stem for p in _RECIPES_WITHOUT_DIAGRAMS)}. "
"A new recipe was added without a diagram — either create the diagram "
"or raise CURRENT_XFAIL_CAP (with justification)."
)


def test_check_diagram_staleness_missing_is_stale(tmp_path: Path) -> None:
(tmp_path / "diagrams").mkdir()
recipe_yaml = tmp_path / "fake_recipe.yaml"
recipe_yaml.write_text("steps: []\n")
assert check_diagram_staleness("fake_recipe", tmp_path, recipe_yaml)
Loading