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
+2
View File
@@ -2192,6 +2192,8 @@ export type DesktopApi = {
}>
}
supervision: {
cancel: (input: { runId: string }) => Promise<void>
execution: () => Promise<import('./supervision-review-contracts').SupervisionReviewExecution>
pause: (input: { runId: string }) => Promise<void>
resume: (input: { runId: string }) => Promise<unknown>
batches: (input: { runId: string; offset?: number; limit?: number }) => Promise<import('./supervision-review-contracts').SupervisionReviewBatch[]>
+2
View File
@@ -267,6 +267,8 @@ export const ipcChannels = {
supervisionOverview: 'supervision:overview',
supervisionActivity: 'supervision:activity',
supervisionPause: 'supervision:pause',
supervisionCancel: 'supervision:cancel',
supervisionExecution: 'supervision:execution',
supervisionResume: 'supervision:resume',
supervisionBatches: 'supervision:batches',
supervisionRun: 'supervision:run',
+2 -2
View File
@@ -34,7 +34,7 @@ export type SupervisionActivity = {
id: string
kind: 'supervision' | 'heartbeat'
trigger: 'manual' | 'scheduled' | 'heartbeat'
status: 'running' | 'completed' | 'failed' | 'skipped' | 'no_change' | 'paused'
status: 'running' | 'completed' | 'failed' | 'skipped' | 'no_change' | 'paused' | 'cancelled'
reviewProgress?: import('./supervision-review-contracts').SupervisionReviewProgress
scope: SupervisionRunRequest['scope'] | null
startedAt: string
@@ -44,7 +44,7 @@ export type SupervisionActivity = {
summary: string | null
resultId: string | null
heartbeatStatus: 'claimed' | 'completed' | 'failed' | 'skipped' | 'no_change' | null
supervisionStatus: 'running' | 'completed' | 'failed' | 'no_change' | 'paused' | null
supervisionStatus: 'running' | 'completed' | 'failed' | 'no_change' | 'paused' | 'cancelled' | null
}
export const supervisionEvidenceSchema = z
@@ -11,6 +11,11 @@ export const supervisionReviewSettingsSchema = z.object({
export type SupervisionReviewSettings = z.infer<typeof supervisionReviewSettingsSchema>
export const defaultSupervisionReviewSettings = supervisionReviewSettingsSchema.parse({})
export const supervisionReviewIdSchema = z.object({ runId: z.string().uuid() }).strict()
export type SupervisionReviewExecution = {
active: boolean
runId?: string
stopping?: 'paused' | 'cancelled'
}
export const supervisionBatchesRequestSchema = supervisionReviewIdSchema.extend({
offset: z.number().int().min(0).default(0),
limit: z.number().int().min(1).max(20).default(10)