M13: Claude job queue — 'Voer uit'-knop + worker presence (ST-1111) (#18)

* feat(ST-1111.1): add ClaudeJob model and state-machine enum

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(ST-1111.2): add ClaudeJob status API mappers

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(ST-1111.3): add enqueue/cancel ClaudeJob server actions with idempotency + NOTIFY

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(ST-1111.4): forward ClaudeJob events on solo SSE stream + initial state

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(ST-1111.6): add 'Voer uit' + cancel buttons to task detail dialog

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(ST-1111.7): add job status pill with spinner on solo task cards

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* test(ST-1111.8): cover job-status mappers and enqueue/cancel actions

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* docs(ST-1111.9): document Claude job queue architecture and agent flow

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(ST-1111.10a): add ClaudeWorker presence model

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(ST-1111.10c): forward worker presence events on solo SSE + initial count

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(ST-1111.10d): show worker presence indicator and gate 'Voer uit' on connected workers

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Janpeter Visser 2026-04-29 19:51:48 +02:00 committed by GitHub
parent 1cb5772edd
commit 73087e9705
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
18 changed files with 921 additions and 27 deletions

32
lib/job-status.ts Normal file
View file

@ -0,0 +1,32 @@
import type { ClaudeJobStatus } from '@prisma/client'
const JOB_DB_TO_API = {
QUEUED: 'queued',
CLAIMED: 'claimed',
RUNNING: 'running',
DONE: 'done',
FAILED: 'failed',
CANCELLED: 'cancelled',
} as const satisfies Record<ClaudeJobStatus, string>
const JOB_API_TO_DB: Record<string, ClaudeJobStatus> = {
queued: 'QUEUED',
claimed: 'CLAIMED',
running: 'RUNNING',
done: 'DONE',
failed: 'FAILED',
cancelled: 'CANCELLED',
}
export type ClaudeJobStatusApi = typeof JOB_DB_TO_API[ClaudeJobStatus]
export function jobStatusToApi(s: ClaudeJobStatus): ClaudeJobStatusApi {
return JOB_DB_TO_API[s]
}
export function jobStatusFromApi(s: string): ClaudeJobStatus | null {
return JOB_API_TO_DB[s.toLowerCase()] ?? null
}
export const JOB_STATUS_API_VALUES = Object.values(JOB_DB_TO_API)
export const ACTIVE_JOB_STATUSES: ClaudeJobStatus[] = ['QUEUED', 'CLAIMED', 'RUNNING']