generated from coulomb/repo-seed
feat(custodian): add ADR-001 compliance validator
Scripts, Makefile target, and MCP tool for checking a repository against ADR-001 (workplans as repo artefacts, state-hub as cache). Checks performed: File-side: workplans/ dir exists, valid YAML frontmatter (required fields, type, status, id format), filename matches id, embedded task blocks have id/status/priority. State-hub cross-reference: state_hub_workstream_id references resolve to real DB records; orphan detection flags active DB workstreams with no backing workplan file. Usage: make validate-adr REPO=<path> [DOMAIN=<slug>] validate_repo_adr(repo_path, domain_slug?) # MCP tool Running against the-custodian itself correctly surfaces the 4 pre-ADR-001 workstreams that still need workplan files written. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -10,6 +10,7 @@ import os
|
||||
import re
|
||||
import sys
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
from uuid import UUID
|
||||
|
||||
@@ -629,6 +630,71 @@ def update_td_status(td_uuid: str, status: str) -> str:
|
||||
return json.dumps(td, indent=2)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# ADR-001 compliance validation
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
@mcp.tool()
|
||||
def validate_repo_adr(repo_path: str, domain_slug: str | None = None) -> str:
|
||||
"""Check whether a repository is consistent with ADR-001.
|
||||
|
||||
Validates that workplan files exist in workplans/ with correct frontmatter,
|
||||
that state_hub_workstream_id references resolve to real DB records, and that
|
||||
no active state-hub workstreams for the domain lack a backing file (orphan
|
||||
detection — DB-only records are an ADR-001 violation).
|
||||
|
||||
Args:
|
||||
repo_path: Absolute path to the repository root.
|
||||
domain_slug: Domain slug for orphan detection (e.g. 'custodian').
|
||||
If omitted, inferred from workplan frontmatter.
|
||||
"""
|
||||
import subprocess
|
||||
script = Path(__file__).parent.parent / "scripts" / "validate_repo_adr.py"
|
||||
cmd = [sys.executable, str(script), repo_path, "--json",
|
||||
"--api-base", API_BASE]
|
||||
if domain_slug:
|
||||
cmd += ["--domain", domain_slug]
|
||||
|
||||
result = subprocess.run(cmd, capture_output=True, text=True)
|
||||
try:
|
||||
data = json.loads(result.stdout)
|
||||
except json.JSONDecodeError:
|
||||
return f"Validator script error:\n{result.stderr or result.stdout or '(no output)'}"
|
||||
|
||||
findings = data.get("findings", [])
|
||||
summary = data.get("summary", {})
|
||||
overall = data.get("result", "unknown")
|
||||
|
||||
failures = [f for f in findings if f["level"] == "FAIL"]
|
||||
warnings = [f for f in findings if f["level"] == "WARN"]
|
||||
|
||||
lines = [f"ADR-001 Compliance: {repo_path}", ""]
|
||||
|
||||
if failures:
|
||||
lines.append(f"FAILURES ({len(failures)}):")
|
||||
for f in failures:
|
||||
loc = f" [{f['file']}]" if f.get("file") else ""
|
||||
lines.append(f" FAIL {f['check']}{loc}")
|
||||
lines.append(f" {f['detail']}")
|
||||
lines.append("")
|
||||
|
||||
if warnings:
|
||||
lines.append(f"WARNINGS ({len(warnings)}):")
|
||||
for f in warnings:
|
||||
loc = f" [{f['file']}]" if f.get("file") else ""
|
||||
lines.append(f" WARN {f['check']}{loc}")
|
||||
lines.append(f" {f['detail']}")
|
||||
lines.append("")
|
||||
|
||||
lines.append(
|
||||
f"Summary: {summary.get('pass', 0)} pass | "
|
||||
f"{summary.get('warn', 0)} warn | "
|
||||
f"{summary.get('fail', 0)} fail"
|
||||
)
|
||||
lines.append(f"Result: {'FAIL' if overall == 'fail' else 'PASS (with warnings)' if overall == 'warn' else 'PASS'}")
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Entry point
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user