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:
co-authored by
Claude Fable 5
parent
f35a75f787
commit
e32c600215
+43
-1
@@ -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 ?'
|
||||
|
||||
Reference in New Issue
Block a user