All-Bun sub-agent that watches open PRs on Gitea, reads ship-claims, and hard-blocks merges when the code doesn't back the claim. First commit of N; this is the skeleton. Dynamic/static/inference/kb checks + poller land in follow-up commits on this same branch. - auditor/types.ts — Claim, Finding, Verdict, PrSnapshot shapes - auditor/gitea.ts — minimal API client (listOpenPrs, getPrDiff, postCommitStatus, postReview). Live-proven: returned 0 open PRs against our repo (which IS the current state — every commit today went to main directly, which is the problem this auditor is meant to prevent) - auditor/policy.ts — stub `assembleVerdict` + severity rules. Intentionally conservative defaults: strong claim + zero evidence = block, not warn. - auditor/README.md — how to run + the hard-block mechanism Workflow discipline change: starting with this branch, no more direct pushes to main. Every change lands as a PR. When this auditor is fully built and running, it'll review its own completion PR — the recursive self-test. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
63 lines
2.5 KiB
TypeScript
63 lines
2.5 KiB
TypeScript
// ═══════════════════════════════════════════════════════════════════
|
|
// YOU WRITE THIS FILE. Policy decides what blocks vs what's a comment.
|
|
// Defaults are opinionated on the "stop clicking past placeholder"
|
|
// side — easier to loosen than to tighten when you're watching the
|
|
// auditor behave in live PRs.
|
|
// ═══════════════════════════════════════════════════════════════════
|
|
|
|
import type { Finding, Verdict } from "./types.ts";
|
|
|
|
/// Translate the four-check output into a single verdict. This is the
|
|
/// single pane of glass the auditor operates on — tune thresholds here.
|
|
export function assembleVerdict(
|
|
findings: Finding[],
|
|
metrics: Record<string, number>,
|
|
pr_number: number,
|
|
head_sha: string,
|
|
): Verdict {
|
|
const blocking = findings.filter(f => f.severity === "block");
|
|
const warning = findings.filter(f => f.severity === "warn");
|
|
|
|
let overall: Verdict["overall"];
|
|
let one_liner: string;
|
|
|
|
if (blocking.length > 0) {
|
|
overall = "block";
|
|
one_liner = `${blocking.length} blocking issue${blocking.length > 1 ? "s" : ""}: ${blocking[0].summary}`;
|
|
} else if (warning.length >= 3) {
|
|
// Three or more warnings is a block — death by a thousand cuts.
|
|
overall = "request_changes";
|
|
one_liner = `${warning.length} warnings — see review`;
|
|
} else if (warning.length > 0) {
|
|
overall = "request_changes";
|
|
one_liner = warning[0].summary;
|
|
} else {
|
|
overall = "approve";
|
|
one_liner = `all checks passed (${findings.length} findings, all info)`;
|
|
}
|
|
|
|
return {
|
|
pr_number,
|
|
head_sha,
|
|
audited_at: new Date().toISOString(),
|
|
overall,
|
|
findings,
|
|
metrics,
|
|
one_liner,
|
|
};
|
|
}
|
|
|
|
/// Which strength-of-claim warrants which severity when evidence is
|
|
/// weak? A "Phase X shipped" claim with zero integration tests is a
|
|
/// blocker. A "should work" claim with no test is a warn.
|
|
export function severityFromClaimEvidence(
|
|
claim_strength: "weak" | "moderate" | "strong",
|
|
evidence_grade: "none" | "partial" | "full",
|
|
): "info" | "warn" | "block" {
|
|
if (evidence_grade === "full") return "info";
|
|
if (claim_strength === "strong" && evidence_grade === "none") return "block";
|
|
if (claim_strength === "strong" && evidence_grade === "partial") return "warn";
|
|
if (claim_strength === "moderate" && evidence_grade === "none") return "warn";
|
|
return "info";
|
|
}
|