Adversarial review of abaae9a7f confirmed 13 findings; fix the P1 and the
higher-value P2/P3 ones:
- P1 (resume slot ownership): reserveScheduledResume promoted the run to
'started' BEFORE the approval claim, so the reserve-winner and claim-winner
could diverge and a claim-loser would flip the claim-winner's active row back
to requires_action (breaking the single-active invariant mid-generation).
Split into a read-only checkScheduledResume (overlap/capacity) BEFORE the claim
(keeps the approval claimable) and promoteScheduledResume AFTER the claim is
won, so only the run's driver ever owns the slot. Removed the release race.
- P2 (topology wiring dead in production): api/server/index.js called
initializeScheduleEngine() with no clustered flag, so isJobStoreShared was
always true and a horizontally-replicated in-memory-stream deployment would
reap peers' live runs. index.js now passes clustered=USE_REDIS; experimental.js
passes clustered=(workers>1) instead of USE_REDIS.
- P2 (manual run-now fencing gap): manual fires skipped revalidateClaim, so a
delete/edit landing after acquireManualRunLease still dispatched. revalidateClaim
now takes requireEnabled; the manual path revalidates (delete/edit fenced),
only relaxing the enabled check.
- P3 (soft-deleted schedule leaks through reads/edits/claims): getScheduleById,
updateScheduleById, and claimDueSchedule now exclude deleting schedules.
- P3 (account-deletion job leak): quiesceUserSchedules aborts without
preserveForReconcile (its run rows are hard-deleted, so nothing reconciles),
while per-schedule delete keeps preserve=true.
Accepted residuals (documented): clustered-in-memory orphan reaping limits
(mitigated by the topology fix), narrow Redis TOCTOU on reconcile delete, and
the pre-revalidate skip-bookkeeping chip — all P3, self-healing or topology-gated.