fix: review hardening — full push-range diff, fail-closed diff fetch, crash-safe queue, 403 self-heal

- push reviews now fetch ONE compare diff (before...after) instead of
  slicing to the first 20 commits under a head-sha status; new-branch
  pushes (zero before-sha) fall back to the head commit diff
- truncated diffs can no longer yield a clean pass: verdict capped at
  warn and status description prefixed 'partial review (diff truncated)'
- any diff-fetch failure (throw / empty body on a non-empty push or PR)
  short-circuits to status 'warning' ('review unavailable — diff fetch
  failed (not blocking)') with a ledger row; a diffless prompt is never
  sent to the model
- accepted jobs persist to sqlite (queue_jobs) before the pending status
  posts; startup re-enqueues rows that never reached a final state, so
  restarts no longer strand shas at 'pending'
- 403 on status/comment posts self-heals: admin-scoped token (config
  admin_token) adds shre-reviewer as collaborator (write), retries once
- scripts/wire-repos.mjs: idempotent estate-wide webhook + collaborator
  wiring over GET /repos/search
- tests: 32 -> 51 (truncation cap, persisted-queue reconciliation,
  collab-retry stubs, fetchDiff routing)

Co-Authored-By: Claude Fable 5 <[email protected]>
This commit is contained in:
Nirav Patel
2026-08-19 09:12:10 -04:00
co-authored by Claude Fable 5
parent f35a75f787
commit e32c600215
14 changed files with 508 additions and 37 deletions
+43 -1
View File
@@ -26,7 +26,18 @@ const DDL = [
ts TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ','now'))
)`,
`CREATE INDEX IF NOT EXISTS idx_reviews_repo_ts ON reviews(repo, ts DESC)`,
`CREATE INDEX IF NOT EXISTS idx_reviews_sha ON reviews(sha)`
`CREATE INDEX IF NOT EXISTS idx_reviews_sha ON reviews(sha)`,
`CREATE TABLE IF NOT EXISTS queue_jobs (
id INTEGER PRIMARY KEY AUTOINCREMENT,
repo TEXT NOT NULL,
sha TEXT NOT NULL,
job TEXT NOT NULL,
state TEXT NOT NULL DEFAULT 'queued',
outcome TEXT,
created_ts TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ','now')),
finished_ts TEXT
)`,
`CREATE INDEX IF NOT EXISTS idx_queue_jobs_state ON queue_jobs(state)`
];
export function openDb(dbPath) {
@@ -55,6 +66,37 @@ export function insertReview(db, r) {
return res.lastInsertRowid;
}
// --- persistent work queue (crash-safe: rows written before the pending
// status posts; rows that never reach 'done' are re-enqueued on startup) ---
export function enqueueJob(db, job) {
const res = db.prepare(
`INSERT INTO queue_jobs (repo, sha, job) VALUES (?, ?, ?)`
).run(`${job.owner}/${job.repo}`, String(job.sha), JSON.stringify(job));
return res.lastInsertRowid;
}
export function finishJob(db, id, outcome = null) {
db.prepare(
`UPDATE queue_jobs SET state = 'done', outcome = ?,
finished_ts = strftime('%Y-%m-%dT%H:%M:%fZ','now') WHERE id = ?`
).run(outcome, id);
}
// Startup reconciliation: rows still 'queued' belonged to a previous process
// that died mid-review (their posted status is stuck at 'pending').
export function recoverPendingJobs(db) {
const rows = db.prepare(
`SELECT id, job FROM queue_jobs WHERE state != 'done' ORDER BY id ASC`
).all();
const out = [];
for (const row of rows) {
try { out.push({ id: row.id, job: JSON.parse(row.job) }); }
catch { finishJob(db, row.id, 'unparseable-job'); }
}
return out;
}
export function listReviews(db, repo, limit = 50) {
return db.prepare(
'SELECT * FROM reviews WHERE repo = ? ORDER BY id DESC LIMIT ?'