Files
DuncanandWill Pfleger 24e87e85c7 fix(readState): v4 plan conformance — pending-intents consolidation, single-flight retry controller, biting witnesses
Closes all 7 IMPORTANT + 2 MINOR findings from Thufir pass-1.

Core changes:
- Consolidate pending-intents storage into override-state.v2 blob (Paul's
  design ruling). Single write point for registers, receipts, and intents
  eliminates the double-commit window; no migration needed since
  pending-intents.v1 never shipped.
- Implement retryLoad() single-flight controller: bounded exponential backoff
  (1s->2s->4s...60s cap), reconnect coalescing into pendingRetryOnComplete,
  destroy/identity cancellation, retryAttempt reset on complete verdict.
- sourceScope + readTarget captured in enqueue path so drain performs exact
  source cleanup at the correct frontier position.
- refused-unread rollback: snapshot prior forced-unread entry before ordered
  write; restore exact snapshot on refusal; delete only when no prior existed.
- Surfaced drain outcomes via onDrainOutcome hook; genuine refusals
  (uint32_overflow, budget_exhausted, storage_failed) trigger toast;
  already_inactive is silent success; load_incomplete never surfaces.
- Cleanup atomicity: intent + receipt deleted in a single persistLocalState()
  call (one v2 blob write) so they are never observable separately.
- createDrainContext typed factory replaces this-as-unknown-as-DrainContext cast.
- NIP-RS.md: added normative refusal-reason vocabulary list.

Biting witnesses added (readStateManager.test.mjs, +12 tests):
- Amendment A: gen2 enqueued mid-replay, gen1 register rolled back
- Both inversion directions (unread->read, read->unread in-flight)
- compare-delete preserves newer intent
- Amendment C via production fetchAndMerge load path (not direct register write)
- Atomic intent+receipt deletion witness
- Controller: automatic backoff, reconnect during loadInFlight, stale-gen ordering
- Controller: destroy-with-timer, exactly-one-drain-per-generation
- Store/UI: identity-swap-during-drain, deferred refusal surfaces outcome

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
2026-08-07 18:53:39 -04:00
..