fix: keep supervisor controls usable and prevent overlapping reviews

Allow only one active review across manual starts, automatic runs and resume. Add cancellation that holds admission through runtime cleanup, preserves saved batches and prevents cancelled runs from resuming.

Keep review controls usable during execution, show a dismissible progress notice and remove redundant filter labels. Include focused regressions and lifecycle documentation.

Release note: Supervision reviews can now be cancelled from Activity records. Starting another review shows the active review instead of duplicating work, while scope, time and refresh controls remain available.
This commit is contained in:
mesalogo
2026-09-26 15:55:50 +08:00
parent 0759d5dcff
commit 5116613615
30 changed files with 653 additions and 92 deletions
+11 -1
View File
@@ -121,10 +121,12 @@ export class SupervisionReviewStore {
}
resume(runId: string): void {
const status = this.db.prepare('SELECT status FROM supervision_runs WHERE id = ?').get(runId)?.status
if (status === 'cancelled') throw new Error('SUPERVISION_REVIEW_CANCELLED: 此回顾已取消,不能继续。请开始新的回顾。')
if (this.load(runId).restartRequired) throw new Error('Review source changed or was removed; start a new review')
const changed = this.db.prepare(`UPDATE supervision_runs SET status = 'running', error = NULL, completed_at = NULL
WHERE id = ? AND status IN ('paused', 'failed', 'running')`).run(runId)
if (!changed.changes) throw new Error('Review is already complete or unavailable')
if (!changed.changes) throw new Error('SUPERVISION_REVIEW_NOT_RESUMABLE: 此回顾已完成或不存在,请开始新的回顾。')
// A changed/deleted source cannot be silently counted as covered. Saved facts remain accessible.
let after = ''
for (;;) {
@@ -144,6 +146,14 @@ export class SupervisionReviewStore {
.run(reason, new Date().toISOString(), runId)
}
cancel(runId: string): void {
const changed = this.db.prepare(`UPDATE supervision_runs SET status = 'cancelled', error = NULL,
completed_at = CASE WHEN status = 'cancelled' THEN completed_at ELSE ? END
WHERE id = ? AND status IN ('running', 'paused', 'failed', 'cancelled')`)
.run(new Date().toISOString(), runId)
if (!changed.changes) throw new Error('SUPERVISION_REVIEW_NOT_CANCELLABLE: 此回顾已完成或不存在,无需取消。')
}
setPhase(runId: string, phase: NonNullable<SupervisionReviewProgress['phase']>): void {
this.db.prepare("UPDATE supervision_review_runs SET state_json = json_set(state_json, '$.phase', ?) WHERE run_id = ?").run(phase, runId)
}