Address all 7 findings from Codex review of e281b2b0c:
- Resume rollback race (P2): the capacity reserve-then-verify rollback could flip a
row a concurrent same-pause resume had taken over. Made capacity a READ-ONLY
pre-check before the promote and promote WITHOUT rollback — no rollback, no race.
Per-schedule overlap stays hard-enforced by the partial index; global cap at resume
is an explicit best-effort soft cap (documented; the fire path is the hard cap).
- Engine base-config gate (P2): runTick no longer returns early on the BASE
getLimits().enabled — schedules can be enabled per user/role/tenant even when base
disables them. The fire path re-resolves the OWNER's limits and skips a disabled
owner, so an owner-scoped disable is still honored; base only sets the claim budget.
- Retain attachments before committing edits (P2): the update handler now retains
the new files BEFORE updateScheduleById, so a retention failure leaves the entire
schedule unchanged instead of persisting prompt/cadence/agent/enabled edits.
- Move agent-fire access into TS (P2, CLAUDE.md): resolveAgentFireAccess logic moved
to packages/api/src/schedules/access.ts (createResolveAgentFireAccess with DI);
api/.../access.js is now thin wiring.
- Clustered-without-shared-streams (P2): warn at engine init that peer aborts
(deletion/account-deletion quiescing) and cross-worker orphan recovery need
USE_REDIS_STREAMS — an inherent limitation of that unsupported topology.
- Client: hide the schedules nav panel when disabled via the object form
({ use: false }), not just the boolean (P2); invalidate the schedules query on
run-now SETTLED (not just success) so 409 paths that disable the schedule refresh
the card (P3).