generated from john/python-template
This commit is contained in:
@@ -259,7 +259,7 @@ template structure. Reports are dated, non-canonical artifacts: `docs/reviews/**
|
|||||||
---
|
---
|
||||||
|
|
||||||
## 8. Meta-Tooling & Instruction Update Recommendations
|
## 8. Meta-Tooling & Instruction Update Recommendations
|
||||||
- Required updates to docs/instructions/skills/tests to keep enforcement current.
|
- Required updates to docs, instructions, skills, or tests to keep enforcement current.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -11,11 +11,16 @@ PROJECT_ROOT = Path(__file__).resolve().parents[1]
|
|||||||
|
|
||||||
|
|
||||||
ACTIVE_CONTRACT_FILES = (
|
ACTIVE_CONTRACT_FILES = (
|
||||||
|
"AGENTS.md",
|
||||||
|
".github/agents/python-reviewer.agent.md",
|
||||||
|
".github/prompts/review-python-architecture.prompt.md",
|
||||||
|
".github/instructions/documentation-sync.instructions.md",
|
||||||
".github/instructions/services.instructions.md",
|
".github/instructions/services.instructions.md",
|
||||||
".github/instructions/ui.instructions.md",
|
".github/instructions/ui.instructions.md",
|
||||||
".github/instructions/error-handling.instructions.md",
|
".github/instructions/error-handling.instructions.md",
|
||||||
".github/skills/python-code-reviewer/skill.md",
|
".github/skills/python-code-reviewer/skill.md",
|
||||||
".github/skills/evidence-provenance-auditor/skill.md",
|
".github/skills/evidence-provenance-auditor/skill.md",
|
||||||
|
".github/skills/test-effectiveness-auditor/skill.md",
|
||||||
"docs/index.md",
|
"docs/index.md",
|
||||||
"docs/architecture.md",
|
"docs/architecture.md",
|
||||||
"docs/requirements.md",
|
"docs/requirements.md",
|
||||||
@@ -113,6 +118,22 @@ def test_active_contract_files_are_present():
|
|||||||
assert missing == []
|
assert missing == []
|
||||||
|
|
||||||
|
|
||||||
|
def test_every_github_contract_file_is_scanned():
|
||||||
|
"""A new instruction, skill, agent, or prompt must not escape these guards by default.
|
||||||
|
|
||||||
|
Files added under .github without being listed here would never be checked for legacy
|
||||||
|
authority references or broken repo paths, which is how stale guidance accumulates.
|
||||||
|
"""
|
||||||
|
github_root = PROJECT_ROOT / ".github"
|
||||||
|
discovered = {
|
||||||
|
path.relative_to(PROJECT_ROOT).as_posix()
|
||||||
|
for path in github_root.rglob("*.md")
|
||||||
|
if not path.is_relative_to(github_root / "ISSUE_TEMPLATE")
|
||||||
|
}
|
||||||
|
unscanned = sorted(discovered - set(ACTIVE_CONTRACT_FILES))
|
||||||
|
assert unscanned == [], f"Add these to ACTIVE_CONTRACT_FILES so they are guarded: {unscanned}"
|
||||||
|
|
||||||
|
|
||||||
def test_no_legacy_authority_references_in_active_contract_files():
|
def test_no_legacy_authority_references_in_active_contract_files():
|
||||||
"""Active contracts must not route authority through removed revision trees."""
|
"""Active contracts must not route authority through removed revision trees."""
|
||||||
violations: dict[str, list[str]] = {}
|
violations: dict[str, list[str]] = {}
|
||||||
@@ -124,9 +145,81 @@ def test_no_legacy_authority_references_in_active_contract_files():
|
|||||||
assert violations == {}
|
assert violations == {}
|
||||||
|
|
||||||
|
|
||||||
|
# Matches repo-relative references such as `docs/schema.md`, `./docs/reviews`, or
|
||||||
|
# `../../src/transcription/db/models.py` (the leading `../` is consumed by the lookbehind).
|
||||||
|
_REPO_PATH_REFERENCE = re.compile(r"(?<![\w.-])((?:\./)?(?:docs|src|tests)/[\w./-]+)")
|
||||||
|
_TEST_NODE_REFERENCE = re.compile(r"((?:\./)?tests/[\w./-]+\.py)::([\w:]+)")
|
||||||
|
|
||||||
|
|
||||||
|
def _normalize_reference(raw: str) -> str:
|
||||||
|
return raw.removeprefix("./").rstrip(".,;:)")
|
||||||
|
|
||||||
|
|
||||||
|
def test_contract_files_reference_only_existing_repo_paths():
|
||||||
|
"""Every docs/src/tests path named in a contract file must exist.
|
||||||
|
|
||||||
|
This generalizes the legacy-marker list: instead of enumerating retired trees one by one,
|
||||||
|
any reference that no longer resolves fails, whether it is a renamed doc, a moved module,
|
||||||
|
or a deleted test.
|
||||||
|
"""
|
||||||
|
violations: dict[str, list[str]] = {}
|
||||||
|
|
||||||
|
for relative_path in ACTIVE_CONTRACT_FILES:
|
||||||
|
text = _read(relative_path)
|
||||||
|
broken = set()
|
||||||
|
for match in _REPO_PATH_REFERENCE.finditer(text):
|
||||||
|
reference = _normalize_reference(match.group(1))
|
||||||
|
# Glob patterns describe a family of paths, not one target.
|
||||||
|
if "*" in reference or "<" in reference:
|
||||||
|
continue
|
||||||
|
candidate = reference.rstrip("/")
|
||||||
|
if not (PROJECT_ROOT / candidate).exists():
|
||||||
|
broken.add(reference)
|
||||||
|
if broken:
|
||||||
|
violations[relative_path] = sorted(broken)
|
||||||
|
|
||||||
|
assert violations == {}, f"Contract files reference paths that do not exist: {violations}"
|
||||||
|
|
||||||
|
|
||||||
|
def test_contract_files_reference_only_existing_test_names():
|
||||||
|
"""A cited test node id must still name a real test.
|
||||||
|
|
||||||
|
Contract files point at specific guards as proof a rule is enforced. A renamed test turns
|
||||||
|
that citation into a claim of enforcement that no longer holds.
|
||||||
|
"""
|
||||||
|
violations: dict[str, list[str]] = {}
|
||||||
|
|
||||||
|
for relative_path in ACTIVE_CONTRACT_FILES:
|
||||||
|
text = _read(relative_path)
|
||||||
|
broken = set()
|
||||||
|
for match in _TEST_NODE_REFERENCE.finditer(text):
|
||||||
|
test_file = _normalize_reference(match.group(1))
|
||||||
|
target = PROJECT_ROOT / test_file
|
||||||
|
if not target.exists():
|
||||||
|
continue # Reported by the path-existence guard.
|
||||||
|
source = target.read_text(encoding="utf-8")
|
||||||
|
missing_names = [name for name in match.group(2).split("::") if name not in source]
|
||||||
|
if missing_names:
|
||||||
|
broken.add(f"{test_file}::{match.group(2)}")
|
||||||
|
if broken:
|
||||||
|
violations[relative_path] = sorted(broken)
|
||||||
|
|
||||||
|
assert violations == {}, f"Contract files cite test names that no longer exist: {violations}"
|
||||||
|
|
||||||
|
|
||||||
def test_canonical_authority_references_are_present():
|
def test_canonical_authority_references_are_present():
|
||||||
"""Critical instruction and skill files must keep canonical references explicit."""
|
"""Critical instruction and skill files must keep canonical references explicit."""
|
||||||
required_fragments = {
|
required_fragments = {
|
||||||
|
"AGENTS.md": (
|
||||||
|
"docs/index.md",
|
||||||
|
"docs/invariant/*",
|
||||||
|
"uv run",
|
||||||
|
),
|
||||||
|
".github/instructions/documentation-sync.instructions.md": (
|
||||||
|
"docs/index.md",
|
||||||
|
"docs/schema.md",
|
||||||
|
"docs/error_handling.md",
|
||||||
|
),
|
||||||
".github/instructions/services.instructions.md": (
|
".github/instructions/services.instructions.md": (
|
||||||
"docs/",
|
"docs/",
|
||||||
"./error-handling.instructions.md",
|
"./error-handling.instructions.md",
|
||||||
@@ -154,6 +247,10 @@ def test_canonical_authority_references_are_present():
|
|||||||
"docs/requirements.md",
|
"docs/requirements.md",
|
||||||
"docs/error_handling.md",
|
"docs/error_handling.md",
|
||||||
),
|
),
|
||||||
|
".github/skills/test-effectiveness-auditor/skill.md": (
|
||||||
|
"docs/invariant/*",
|
||||||
|
"tests/test_meta_contract_guards.py",
|
||||||
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
missing: dict[str, list[str]] = {}
|
missing: dict[str, list[str]] = {}
|
||||||
|
|||||||
Reference in New Issue
Block a user