feat(consistency): consume repo-manager conformance
Assistant: codex Assistant-Model: gpt-5.6-sol Assistant-Session: 01a023c0-a0a3-7c03-b395-5a0d2757214d
This commit is contained in:
parent
ec780c05a2
commit
b9d9ffed5f
2 changed files with 93 additions and 0 deletions
|
|
@ -38,6 +38,7 @@ Checks:
|
|||
C-32 work-record-not-indexed WARN Yes kind: intake/decision YAML block has no hub id — not indexed in DB (registration, CUST-WP-0061-T02)
|
||||
C-33 work-record-index-stale WARN Yes WORK-RECORDS.md missing or stale — generated per-repo index (CUST-WP-0061-T04)
|
||||
C-34 quality-dor-ready WARN No status=ready without quality_dor DoR-Ok (STATE-WP-0077 soft)
|
||||
C-35 repo-manager-conformance WARN No Repo Manager flavor/standards contract reports findings
|
||||
(finished¬DoD-Ok is listed by `statehub quality-debt`, not per-file C-warn — avoids historical flood)
|
||||
|
||||
Usage:
|
||||
|
|
@ -73,6 +74,7 @@ import argparse
|
|||
import os
|
||||
import json
|
||||
import re
|
||||
import shutil
|
||||
import socket
|
||||
import subprocess
|
||||
import sys
|
||||
|
|
@ -164,6 +166,56 @@ _WORK_REQUEST_RE = re.compile(
|
|||
)
|
||||
_OPEN_WORKPLAN_STATUSES = {"proposed", "ready", "active", "blocked", "backlog"}
|
||||
|
||||
|
||||
def _check_repo_manager_conformance(repo_dir: Path, report: "ConsistencyReport") -> None:
|
||||
"""C-35: consume Repo Manager's conformance contract without reimplementing it."""
|
||||
configured = os.environ.get("RMGR_BIN", "").strip()
|
||||
candidates = [configured] if configured else []
|
||||
candidates.extend(
|
||||
[
|
||||
str(Path.home() / "repo-manager" / ".venv" / "bin" / "rmgr"),
|
||||
shutil.which("rmgr") or "",
|
||||
]
|
||||
)
|
||||
executable = next((item for item in candidates if item and Path(item).is_file()), None)
|
||||
if executable is None:
|
||||
report.add(
|
||||
severity="INFO",
|
||||
check_id="C-35",
|
||||
message="Repo Manager conformance adapter unavailable; set RMGR_BIN or install rmgr",
|
||||
fixable=False,
|
||||
)
|
||||
return
|
||||
try:
|
||||
completed = subprocess.run(
|
||||
[executable, "conform", "--path", str(repo_dir)],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
check=False,
|
||||
timeout=30,
|
||||
)
|
||||
payload = json.loads(completed.stdout)
|
||||
except (OSError, subprocess.TimeoutExpired, json.JSONDecodeError) as exc:
|
||||
report.add(
|
||||
severity="INFO",
|
||||
check_id="C-35",
|
||||
message=f"Repo Manager conformance adapter could not run: {exc}",
|
||||
fixable=False,
|
||||
)
|
||||
return
|
||||
for finding in payload.get("findings") or []:
|
||||
severity = str(finding.get("severity") or "warning")
|
||||
report.add(
|
||||
severity="WARN" if severity in {"missing", "contradictory", "warning"} else "INFO",
|
||||
check_id="C-35",
|
||||
message=(
|
||||
f"Repo Manager {severity}: {finding.get('path') or '(repository)'}: "
|
||||
f"{finding.get('message') or finding.get('code') or 'conformance finding'}"
|
||||
),
|
||||
file_path=finding.get("path"),
|
||||
fixable=False,
|
||||
)
|
||||
|
||||
# Legacy file/API aliases translated before comparison and PATCHing.
|
||||
FILE_TO_DB_WORKSTREAM_STATUS: dict[str, str] = dict(LEGACY_WORKSTREAM_STATUS_ALIASES)
|
||||
|
||||
|
|
@ -1158,6 +1210,9 @@ def check_repo(api_base: str, repo_slug: str, repo_path_override: str | None = N
|
|||
fixable=False,
|
||||
)
|
||||
|
||||
# C-35: consume the repository-standard answer from Repo Manager.
|
||||
_check_repo_manager_conformance(repo_dir, report)
|
||||
|
||||
# C-31: work-record sidetrack detector (canon work-record-types registry)
|
||||
_check_unregistered_work_records(repo_dir, report)
|
||||
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ No network calls, no DB, no live API — these tests run fully offline.
|
|||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
|
|
@ -33,6 +34,7 @@ from consistency_check import (
|
|||
_BACKGROUND_CHECKS,
|
||||
_api_get,
|
||||
_check_scope_freshness,
|
||||
_check_repo_manager_conformance,
|
||||
_detect_behind_remote,
|
||||
_git_pull,
|
||||
_patch_frontmatter_field,
|
||||
|
|
@ -69,6 +71,42 @@ class TestResolveTopicDomainSlug:
|
|||
assert resolve_topic_domain_slug("custodian", repo_market_domain="infotech") == "infotech"
|
||||
|
||||
|
||||
class TestRepoManagerConformanceAdapter:
|
||||
def test_surfaces_repo_manager_findings_without_reimplementing_rules(self, tmp_path, monkeypatch):
|
||||
rmgr = tmp_path / "rmgr"
|
||||
rmgr.write_text("stub", encoding="utf-8")
|
||||
monkeypatch.setenv("RMGR_BIN", str(rmgr))
|
||||
payload = {
|
||||
"ok": False,
|
||||
"findings": [
|
||||
{
|
||||
"code": "intent-and-goal",
|
||||
"severity": "contradictory",
|
||||
"path": "INTENT.md",
|
||||
"message": "project flavor forbids both purpose files",
|
||||
}
|
||||
],
|
||||
}
|
||||
monkeypatch.setattr(
|
||||
"consistency_check.subprocess.run",
|
||||
lambda *args, **kwargs: subprocess.CompletedProcess(args[0], 1, json.dumps(payload), ""),
|
||||
)
|
||||
report = ConsistencyReport(repo_slug="demo", repo_path=str(tmp_path))
|
||||
_check_repo_manager_conformance(tmp_path, report)
|
||||
assert len(report.warnings) == 1
|
||||
assert report.warnings[0].check_id == "C-35"
|
||||
assert "project flavor forbids" in report.warnings[0].message
|
||||
|
||||
def test_missing_adapter_is_informational(self, tmp_path, monkeypatch):
|
||||
monkeypatch.setenv("RMGR_BIN", str(tmp_path / "missing"))
|
||||
monkeypatch.setattr("consistency_check.shutil.which", lambda _name: None)
|
||||
monkeypatch.setattr(Path, "home", classmethod(lambda cls: tmp_path))
|
||||
report = ConsistencyReport(repo_slug="demo", repo_path=str(tmp_path))
|
||||
_check_repo_manager_conformance(tmp_path, report)
|
||||
assert len(report.infos) == 1
|
||||
assert report.infos[0].check_id == "C-35"
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# parse_frontmatter
|
||||
# ---------------------------------------------------------------------------
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue