19c7b5d7e4
Review findings on02503a4a(ConfigPublishCoordinator): 1 (HIGH) A recovered deployment whose every expected ack was already in NodeDeploymentState (a seal whose SaveChanges failed, a fused node's own row) was never completed: inside the deadline it waited for acks that had landed, past it HandleDeadline's "race" guard returned without sealing — stuck in AwaitingApplyAcks, refusing every later deploy. ApplyRecovery now completes it (Seal / PartiallyFailed), and HandleDeadline completes a full ack set instead of returning. 2 (MED-HIGH) The past-deadline recovery of a populated expected set ran HandleDeadline inline before the stash replay, discarding on-time acks queued during the outage. It is now self-sent, behind the replay. The zero-rows past-deadline branch stays inline on purpose: no ack can count there, and a self-sent deadline would time out a queued dispatch of that same row right after publishing it. 3 (MED) A failed PersistNodeAck threw, restarted the actor and dropped the ack (driver-only nodes never write their own row and do not resend). Acks are now persisted before being counted, and a failed persist is re-queued on a timer. The class remarks document that the Loading stash is node-local (lost on a hand-over; queued dispatches are recovered from their rows). Minor: MarkReady/Become(Ready) only after the recovery applied; a throw while applying is a failed load (backoff) rather than a tight restart loop; a queued dispatch matching the recovered Dispatching row logs Info, not the "lost dispatch" Warning. Tests (ConfigPublishCoordinatorDbOutageTests): fully-acked recovery seals / partially fails inside and past the deadline (4 cases); queued ack applied before a past-deadline recovery; failed ack persist retried. All 6 fail on02503a4a. Claude-Session: https://claude.ai/code/session_01GT67AD99zQQqpLCVm3p7xk