Append recovery
Recovery preserves identity
Recovery is not a fresh append. It waits for the original mutation runner to stop, reuses the original append identity, and resolves whether the existing operation is committed. It must not allocate a new offset, Object key, or BookKeeper range while the first result is uncertain.
Bounded procedure
- Read the current stream head.
- If the head still ends at the original expected start, reuse the original commit intent and physical target and attempt the original publication.
- If the head has advanced, page through the reachable commit chain or a recovery checkpoint looking for the exact attempt identity.
- If found, reconstruct the same
AppendResultand continue any profile-specific completion. - If the evidence proves the attempt did not commit, return
KNOWN_NOT_COMMITTEDand release the lane. - If the evidence is incomplete, retain
MAY_HAVE_COMMITTEDand continue bounded retry or expose the recovery handle.
Pagination and timeout are part of correctness
The commit chain can be large. Recovery uses a maximum scan, page size, continuation cursor, and an overall timeout. Exhausting a page budget means “not resolved within this budget”, not “not committed”. The caller receives a retryable resolution failure rather than a false negative.
Recovery after head commit
If head CAS succeeded but generation-0 index or required Object completion failed, the outcome is KNOWN_COMMITTED. The offset is already visible in logical truth. Recovery reuses the same committed range and deterministic materialization task; it does not write another WAL record or roll back the head.
Stale owner fencing
Session epoch and token are checked again during recovery. A new owner may complete an old attempt only through the exact identity and current authority rules. An old owner cannot regain write authority by replaying a saved success callback.