@dataclass
class AuditQualityRule:
"""Run audit checks and return structured feedback.
Attributes:
categories: Audit categories to run (default: lint + type).
working_dir: Project root to audit.
guidance: Optional extra guidance appended on failure.
"""
categories: list[str] = field(default_factory=lambda: ["lint", "type"])
working_dir: str = "."
guidance: str | None = None
def validate(self, content: str, **kwargs: Any) -> WitnessResult:
"""Run audit categories and aggregate results.
Each category runs independently — failures in one do not
prevent execution of the others.
"""
working_dir = kwargs.get("working_dir") or self.working_dir
project_path = Path(working_dir).resolve()
if not project_path.is_dir():
return WitnessResult.failure(
feedback=ValidationFeedback(
what="Invalid working directory",
why=f"Not a directory: {project_path}",
how="Ensure the witness params.working_dir points to "
"a valid project root.",
),
)
# Filter to known categories, skip unknowns
categories = [c for c in self.categories if c in VALID_CATEGORIES]
if not categories:
return WitnessResult.success()
# Run each category independently
results: list[AuditResult] = []
for category in categories:
try:
result = audit_project(project_path, category=category)
results.append(result)
except Exception:
logger.exception("audit_project failed for category=%s", category)
if not results:
return WitnessResult.failure(
feedback=ValidationFeedback(
what="All audit categories failed to execute",
why="audit_project raised exceptions for every category.",
how="Check the project structure and audit configuration.",
),
)
# Aggregate: merge all check results into a single AuditResult
all_checks = []
for r in results:
all_checks.extend(r.checks)
merged = AuditResult(checks=all_checks)
agent_output = format_agent(merged)
total_failed = merged.failed
if total_failed == 0:
return WitnessResult.success(
metadata={"audit": agent_output},
)
# Build structured feedback
failed_items = agent_output.get("failed", [])
why_lines = json.dumps(failed_items, indent=2, ensure_ascii=False)
how = (
"Fix each violation listed above. Lint errors: fix the code "
"(do NOT add # noqa). Type errors: fix the types "
"(do NOT add # type: ignore without verifying)."
)
if self.guidance:
how = f"{how}\n\n{self.guidance}"
return WitnessResult.failure(
feedback=ValidationFeedback(
what=f"Quality gate failed: {total_failed} violation(s)",
why=why_lines,
how=how,
),
metadata={"audit": agent_output},
)