BOLT-1268 P1.5 — soft-cancel backend for pending events (cancelWorkspaceProgramEvent)
blockedAdd real cancel semantics for pending events. Currently the dashboard exposes a Cancel Event row-action that calls deleteWorkspaceProgramEvent, which throws INVALID_STATUS for anything not draft (domains/program/src/services/workspaceProgramEvent/deleteWorkspaceProgramEvent.ts:38). So Cancel is silently broken in prod for pending events.
Scope (v1, soft-cancel only — no OEM recall, that is a separate Linear):
- Prisma: add WorkspaceProgramEvent.cancelledAt (DateTime?) column + migration.
- getWorkspaceProgramEventStatus.ts: extend status enum with `cancelled`; cancelledAt set => status=cancelled regardless of timing.
- New service cancelWorkspaceProgramEvent: only allowed from `pending`. Sets cancelledAt = now(). Calls deleteBatch on every batchId so BullMQ stops dispatching remaining commands. Already-issued OEM commands stay issued (out of v1 scope).
- Subgraph mutation cancelWorkspaceProgramEvent(id) + resolver.
- Wire up to dashboard list page so Cancel Event row-action calls the new mutation for pending events (still deletes for drafts).
- Tests covering: cancel-from-pending succeeds + sets cancelledAt + invokes deleteBatch; cancel-from-draft rejects; cancel-from-active/completed rejects; status enum returns `cancelled` for cancelledAt-set events.
Unblocks P2 (UI affordances on event details).
Event Timeline
created
status_change
queued → blocked