auditor: kb_index aggregator + nine-consecutive empirical test
lakehouse/auditor 1 blocking issue: cloud: claim not backed — "the proven escalation ladder with learning context, collects"
lakehouse/auditor 1 blocking issue: cloud: claim not backed — "the proven escalation ladder with learning context, collects"
Phase 1 — definition-layer over append-only JSONL scratchpads.
auditor/kb_index.ts is the single shared aggregator:
aggregate<T>(jsonlPath, { keyFn, scopeFn, checkFn, tailLimit })
→ Map<signature, {count, distinct_scopes, confidence,
first_seen, last_seen, representative_summary, ...}>
ratingSeverity(agg) — confidence × count severity policy shared
across all KB readers. Kills the "same unfixed PR inflates its
own recurrence score" failure mode by design: confidence =
distinct_scopes/count, so same-scope noise stays below the 0.3
escalation threshold no matter how many times it repeats.
checkAuditLessons now routes through aggregate + ratingSeverity.
Net effect: the recurrence detector's bespoke Map/Set bookkeeping is
gone; same behavior, shared discipline, reusable by scrum/observer.
Also: symbolsExistInRepo now skips files >500KB so the audit can't
get stuck slurping a fixture.
Phase 2 — nine-consecutive audit runner.
tests/real-world/nine_consecutive_audits.ts pushes 9 empty commits,
waits for each verdict, captures the audit_lessons aggregate state
after each run, reports:
- sig_count trajectory (should stabilize, not grow linearly)
- max_count trajectory (same-signature repeat rate)
- max_confidence trajectory (must stay LOW on same-PR noise)
- verdict_stable across runs (must NOT oscillate)
This is the empirical proof that the KB compounds favorably:
noise doesn't escalate itself, and signal stays distinguishable.
Unit-tested both failure modes: same-PR × 9 repeats = conf=0.11
(info); cross-PR × 5 distinct = conf=1.00 (block). The rating
function correctly discriminates.
This commit is contained in:
+22
-43
@@ -18,6 +18,7 @@
|
||||
import { readFile, readdir, stat } from "node:fs/promises";
|
||||
import { join } from "node:path";
|
||||
import type { Claim, Finding } from "../types.ts";
|
||||
import { aggregate, ratingSeverity, formatAgg } from "../kb_index.ts";
|
||||
|
||||
const KB_DIR = "/home/profit/lakehouse/data/_kb";
|
||||
const OBSERVER_OPS = "/home/profit/lakehouse/data/_observer/ops.jsonl";
|
||||
@@ -26,11 +27,6 @@ const SCRUM_REVIEWS_JSONL = "/home/profit/lakehouse/data/_kb/scrum_reviews.jsonl
|
||||
const AUDIT_LESSONS_JSONL = "/home/profit/lakehouse/data/_kb/audit_lessons.jsonl";
|
||||
const TAIL_LINES = 500;
|
||||
const MAX_BOT_CYCLE_FILES = 30;
|
||||
// Recurrence threshold — at this count a warn becomes a block.
|
||||
// The rationale: three independent audits all flagging the SAME
|
||||
// pattern signature is strong evidence the pattern is a real
|
||||
// problem, not noise. One occurrence = info, two = warn, three+ = block.
|
||||
const RECURRENCE_BLOCK_THRESHOLD = 3;
|
||||
|
||||
export async function runKbCheck(claims: Claim[], prFiles: string[] = []): Promise<Finding[]> {
|
||||
const findings: Finding[] = [];
|
||||
@@ -212,52 +208,35 @@ function observerBySource(ops: any[]): string {
|
||||
}
|
||||
|
||||
// Audit-lessons — reads data/_kb/audit_lessons.jsonl (populated by
|
||||
// every audit's appendAuditLessons). Groups rows by `signature` (the
|
||||
// check-normalized dedup key) and emits a finding per signature that
|
||||
// has 2+ occurrences. Severity ramps with count: 2 = info, 3-4 = warn,
|
||||
// 5+ = block. This is how the auditor accumulates institutional
|
||||
// memory: without this check, a recurring flaw (placeholder code
|
||||
// class X, unbacked claim pattern Y) looks new every audit.
|
||||
// every audit's appendAuditLessons). Uses the shared kb_index
|
||||
// aggregator: groups by `signature`, distinct-scopes keyed by PR
|
||||
// number, severity from ratingSeverity(agg) which applies the
|
||||
// confidence × count rating (see kb_index.ts). This is the same
|
||||
// aggregation any other KB reader uses — shared discipline, not
|
||||
// per-check custom logic.
|
||||
async function checkAuditLessons(): Promise<Finding[]> {
|
||||
const rows = await tailJsonl<any>(AUDIT_LESSONS_JSONL, TAIL_LINES * 4);
|
||||
if (rows.length === 0) return [];
|
||||
|
||||
type Agg = { count: number; last_summary: string; last_pr: number; last_sha: string; checks: Set<string>; prs: Set<number> };
|
||||
const bySig = new Map<string, Agg>();
|
||||
for (const r of rows) {
|
||||
const sig = String(r.signature ?? "");
|
||||
if (!sig) continue;
|
||||
const a = bySig.get(sig) ?? {
|
||||
count: 0, last_summary: "", last_pr: 0, last_sha: "",
|
||||
checks: new Set<string>(), prs: new Set<number>(),
|
||||
};
|
||||
a.count += 1;
|
||||
a.last_summary = String(r.summary ?? a.last_summary);
|
||||
a.last_pr = Number(r.pr_number ?? a.last_pr);
|
||||
a.last_sha = String(r.head_sha ?? a.last_sha);
|
||||
if (r.check) a.checks.add(String(r.check));
|
||||
if (r.pr_number) a.prs.add(Number(r.pr_number));
|
||||
bySig.set(sig, a);
|
||||
}
|
||||
const bySig = await aggregate<any>(AUDIT_LESSONS_JSONL, {
|
||||
keyFn: (r) => r?.signature,
|
||||
scopeFn: (r) => (r?.pr_number !== undefined ? `pr-${r.pr_number}` : undefined),
|
||||
checkFn: (r) => r?.check,
|
||||
tailLimit: TAIL_LINES * 4,
|
||||
});
|
||||
if (bySig.size === 0) return [];
|
||||
|
||||
const findings: Finding[] = [];
|
||||
// Emit only signatures with 2+ prior PRs (not just 2+ rows — a
|
||||
// single unresolved PR being re-audited on every push would
|
||||
// otherwise self-inflate). Distinct-PRs count is the real signal.
|
||||
for (const [sig, a] of bySig) {
|
||||
if (a.prs.size < 2) continue;
|
||||
const sev: "block" | "warn" | "info" =
|
||||
a.prs.size >= RECURRENCE_BLOCK_THRESHOLD + 2 ? "block" :
|
||||
a.prs.size >= RECURRENCE_BLOCK_THRESHOLD ? "warn" : "info";
|
||||
for (const [sig, agg] of bySig) {
|
||||
// Silent on first-ever occurrence — not yet signal.
|
||||
if (agg.count < 2) continue;
|
||||
const sev = ratingSeverity(agg);
|
||||
findings.push({
|
||||
check: "kb_query",
|
||||
severity: sev,
|
||||
summary: `recurring audit pattern (${a.prs.size} distinct PRs, ${a.count} total flaggings): ${a.last_summary.slice(0, 180)}`,
|
||||
summary: `recurring audit pattern (${agg.distinct_scopes} distinct PRs, ${agg.count} flaggings, conf=${agg.confidence.toFixed(2)}): ${agg.representative_summary.slice(0, 160)}`,
|
||||
evidence: [
|
||||
`signature=${sig}`,
|
||||
`checks: ${Array.from(a.checks).join(",")}`,
|
||||
`PRs: ${Array.from(a.prs).sort((x,y)=>x-y).join(",")}`,
|
||||
`most recent: PR #${a.last_pr} @ ${a.last_sha.slice(0, 12)}`,
|
||||
`checks: ${agg.checks.join(",")}`,
|
||||
`scopes: ${agg.scopes.slice(-6).join(",")}`,
|
||||
formatAgg(agg),
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user