All checks were successful
lakehouse/auditor all checks passed (4 findings, all info)
auditor/checks/kb_query.ts (task #7) — reads data/_kb/outcomes.jsonl, error_corrections.jsonl, data/_observer/ops.jsonl, data/_bot/cycles/*. Cheap/offline: no model calls, tail-reads only. Fail-rate >30% in recent scenario outcomes → warn; otherwise info. Live-proven: 1 finding emitted against current KB state (69 scenario runs, 27.7% fail rate — below warn threshold). auditor/audit.ts (task #8) — orchestrator. Runs static + dynamic + inference + kb_query in parallel, calls assembleVerdict, persists to data/_auditor/verdicts/, posts to Gitea (commit status + issue comment). AuditOptions supports skip_dynamic/skip_inference/dry_run for iteration. auditor/gitea.ts — added postIssueComment (author can comment on own PR, unlike postReview which self-review-blocks). static.ts — skip BLOCK_PATTERNS scan on auditor/checks/* and auditor/fixtures/* because those files legitimately contain the patterns as regex/string-literal data. WARN/INFO patterns (TODO comments, hardcoded placeholders) still run. Live-proven: dry-run audit of PR #1 after fix went from 13 block findings to 0 from static; 11 warn from inference still fire on real overreach claims. Dry-run audit against PR #1, skip_dynamic=true: verdict: block (BEFORE the static fix) verdict: request_changes (AFTER — inference correctly flagged "tasks 1-9 complete" as not backed; 0 false-positive blocks from static self-match) 42.5s total across checks (mostly cloud inference: 36s) 26 claims, 39KB diff Tasks 5 + 6 + 7 + 8 complete. Remaining: #9 (poller) + #10 (end-to-end proof) + #12 (upsert UPDATE merge fix).
160 lines
6.3 KiB
TypeScript
160 lines
6.3 KiB
TypeScript
// Static diff check — grep-style, no AST, no LLM. Looks for patterns
|
|
// that are high-signal evidence of placeholder code.
|
|
//
|
|
// Findings are severity-graded:
|
|
// block — explicit non-impl markers (unimplemented!, todo!,
|
|
// panic!("not implemented"), throw new Error("not implemented"))
|
|
// warn — TODO / FIXME / XXX / HACK comments on added lines,
|
|
// new struct fields with no read-site anywhere in the diff,
|
|
// suspiciously-empty function bodies ({ Ok(()) } / {} when
|
|
// the commit message claims the fn "implements" something)
|
|
// info — hardcoded "test" / "dummy" / "placeholder" strings in
|
|
// added lines (could be real, just flag for inspection)
|
|
//
|
|
// Consumes: raw unified diff text from Gitea.
|
|
|
|
import type { Finding } from "../types.ts";
|
|
|
|
// Rust + TypeScript patterns that almost always indicate "this is
|
|
// not actually implemented yet."
|
|
const BLOCK_PATTERNS: Array<{ re: RegExp; why: string }> = [
|
|
{ re: /\bunimplemented!\s*\(/, why: "unimplemented!() macro call" },
|
|
{ re: /\btodo!\s*\(/, why: "todo!() macro call" },
|
|
{ re: /panic!\s*\(\s*"(?:not implemented|TODO|not yet|unimpl)/i, why: "panic! with not-implemented message" },
|
|
{ re: /throw\s+new\s+Error\s*\(\s*['"](?:not implemented|TODO|unimpl)/i, why: "throw Error 'not implemented'" },
|
|
];
|
|
|
|
const WARN_COMMENT_PATTERNS: Array<{ re: RegExp; why: string }> = [
|
|
{ re: /^\+.*\/\/\s*(TODO|FIXME|XXX|HACK)\b/i, why: "TODO/FIXME/XXX/HACK comment added" },
|
|
{ re: /^\+.*#\s*(TODO|FIXME|XXX|HACK)\b/i, why: "TODO/FIXME/XXX/HACK comment added" },
|
|
];
|
|
|
|
const INFO_HARDCODED_PATTERNS: Array<{ re: RegExp; why: string }> = [
|
|
{ re: /"(?:placeholder|dummy|foobar|xxx|replaceme|changeme)"/i, why: "suspicious hardcoded string" },
|
|
];
|
|
|
|
export function runStaticCheck(diff: string): Finding[] {
|
|
const findings: Finding[] = [];
|
|
|
|
// Per-file walk: only look at ADDED lines (prefix '+' but not '+++'
|
|
// which is the diff header).
|
|
const perFile = splitDiffByFile(diff);
|
|
|
|
for (const [path, lines] of perFile) {
|
|
// Skip diff bookkeeping + pure-delete files
|
|
if (!lines.some(l => l.startsWith("+") && !l.startsWith("+++"))) continue;
|
|
|
|
// The auditor's own check files literally contain the BLOCK
|
|
// patterns as regex definitions (BLOCK_PATTERNS in this file,
|
|
// prompt examples in inference.ts). Skipping BLOCK scan on these
|
|
// specific paths prevents the checker from self-flagging its own
|
|
// string literals. WARN/INFO patterns still run — those genuinely
|
|
// could indicate problems in the checker's own code (TODO
|
|
// comments don't self-define).
|
|
const isAuditorCheckerFile = path.startsWith("auditor/checks/") ||
|
|
path.startsWith("auditor/fixtures/");
|
|
|
|
for (let idx = 0; idx < lines.length; idx++) {
|
|
const line = lines[idx];
|
|
if (!line.startsWith("+") || line.startsWith("+++")) continue;
|
|
const added = line.slice(1);
|
|
|
|
if (!isAuditorCheckerFile) {
|
|
for (const { re, why } of BLOCK_PATTERNS) {
|
|
if (re.test(added)) {
|
|
findings.push({
|
|
check: "static",
|
|
severity: "block",
|
|
summary: `${why} in ${path}`,
|
|
evidence: [`${path}:+${idx + 1}: ${added.trim().slice(0, 160)}`],
|
|
});
|
|
}
|
|
}
|
|
}
|
|
for (const { re, why } of WARN_COMMENT_PATTERNS) {
|
|
if (re.test(line)) {
|
|
findings.push({
|
|
check: "static",
|
|
severity: "warn",
|
|
summary: `${why} in ${path}`,
|
|
evidence: [`${path}:+${idx + 1}: ${added.trim().slice(0, 160)}`],
|
|
});
|
|
}
|
|
}
|
|
for (const { re, why } of INFO_HARDCODED_PATTERNS) {
|
|
if (re.test(added)) {
|
|
findings.push({
|
|
check: "static",
|
|
severity: "info",
|
|
summary: `${why} in ${path}`,
|
|
evidence: [`${path}:+${idx + 1}: ${added.trim().slice(0, 160)}`],
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
// "Field added but never read" heuristic — catches exactly the
|
|
// Phase 45 DocRef placeholder pattern. Limited to the diff itself:
|
|
// we're not doing a full-codebase grep here (too noisy; callers
|
|
// elsewhere might exist). The point is: if NEITHER this diff nor
|
|
// any other line in the diff reads the field, the PR is shipping
|
|
// state without a consumer.
|
|
const addedLines = lines.filter(l => l.startsWith("+") && !l.startsWith("+++"))
|
|
.map(l => l.slice(1));
|
|
const newFields = extractNewFields(addedLines);
|
|
for (const field of newFields) {
|
|
const readPattern = new RegExp(`[\\.:]\\s*${escape(field)}\\b|\\b${escape(field)}\\s*:`);
|
|
// The definition line itself matches readPattern — filter it out
|
|
// by requiring at least TWO lines in the diff mention the field
|
|
// (one defines, one reads).
|
|
const hits = addedLines.filter(l => readPattern.test(l));
|
|
if (hits.length < 2) {
|
|
findings.push({
|
|
check: "static",
|
|
severity: "warn",
|
|
summary: `field '${field}' added in ${path} but no read-site in the diff — could be placeholder state without a consumer`,
|
|
evidence: [`${path}: added '${field}' with no reader; rest of diff has ${hits.length - 1} mentions`],
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
return findings;
|
|
}
|
|
|
|
function splitDiffByFile(diff: string): Map<string, string[]> {
|
|
const out = new Map<string, string[]>();
|
|
let current: string | null = null;
|
|
let buf: string[] = [];
|
|
for (const line of diff.split(/\r?\n/)) {
|
|
const m = line.match(/^diff --git a\/(\S+) b\/(\S+)/);
|
|
if (m) {
|
|
if (current) out.set(current, buf);
|
|
current = m[2];
|
|
buf = [];
|
|
continue;
|
|
}
|
|
buf.push(line);
|
|
}
|
|
if (current) out.set(current, buf);
|
|
return out;
|
|
}
|
|
|
|
// Extract new `pub name: Type,` fields from added lines. Rust syntax.
|
|
// Narrowly-scoped: only matches at the start of a trimmed line,
|
|
// requires `pub ` prefix, ignores `pub fn` / `pub struct` / etc.
|
|
function extractNewFields(addedLines: string[]): string[] {
|
|
const fields = new Set<string>();
|
|
for (const line of addedLines) {
|
|
const t = line.trim();
|
|
// pub NAME: Type,
|
|
const m = t.match(/^pub\s+(?!fn\b|struct\b|enum\b|mod\b|use\b|trait\b|impl\b|const\b|static\b|type\b)(\w+)\s*:/);
|
|
if (m) fields.add(m[1]);
|
|
}
|
|
return Array.from(fields);
|
|
}
|
|
|
|
function escape(s: string): string {
|
|
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
}
|