* Reconnect hygiene: paced acks, persistent gift-wrap dedup, self-fragment sync fix
Remaining findings from the July 7 locked-phone test sessions, all rooted
in reconnect/relaunch behavior:
- All Nostr acks (READ and DELIVERED, direct and geohash) now flow through
the paced queue that previously only throttled direct READ receipts.
Reconnect redelivery produced 8 DELIVERED acks in under a second, which
damus rejects ("noting too much").
- Processed gift-wrap event IDs persist across launches
(NostrProcessedEventStore, wired through MessageDeduplicationService,
debounced writes, wiped on the existing clear/panic paths). NIP-59
randomizes gift-wrap timestamps, so the 24h-lookback DM subscriptions
redeliver the same events every launch; without a cross-launch record
each relaunch reprocessed old PMs and acks — the re-ack bursts and the
"delivered ack for unknown mid" warnings (now debug: a stale ack is
expected occasionally and not actionable).
- Own fragments handed back by sync replay (the deliberate RSR ttl=0
restore path) now re-enter the gossip sync store before the self-drop.
The fragment store is not archived, so after a relaunch our sync filter
did not cover our own fragments and peers re-offered them every 30s
round indefinitely; recording them stops the redelivery after one round
while keeping assembly skipped.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* Address Codex review on #1398: shared ack pacer, transient clears keep disk record
- Geohash acks are sent through short-lived NostrTransport instances
(makeGeohashNostrTransport creates one per ack), so the per-instance ack
queue never paced a burst. Acks now flow through a pacer shared across
instances: Dependencies.live wires the process-wide sharedAckPacer, and
the default Dependencies init builds an isolated pacer from the same
injected scheduleAfter so tests keep stepping the throttle manually.
- clearNostrCaches() runs on every geohash channel switch, so it no longer
wipes the persisted gift-wrap record (that stays on the clearAll/panic
path). Persistence is now append-merge instead of snapshot-overwrite —
serialized on the store's IO queue — so a transient in-memory clear
between debounced flushes can't shrink the on-disk record either.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
---------
Co-authored-by: jack <jackjackbits@users.noreply.github.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
BLEService's per-packet-type orchestration moves into owned, tested
components (BLEAnnounceHandler, BLEPublicMessageHandler,
BLENoisePacketHandler, BLEFileTransferHandler, BLEFragmentHandler),
each taking an environment struct of closures so every queue hop stays
in BLEService and the handlers are synchronously testable. Behavior is
preserved verbatim, including Noise session recovery on decrypt failure
and single-block UI event ordering. handleLeave/handleRequestSync stay
in place as already-thin delegations. BLEService drops to 3393 lines.
Four coordinators (delivery, private conversation, Nostr, public
conversation) drop their unowned/weak ChatViewModel back-references for
narrow @MainActor context protocols, with ChatViewModel conformances as
single shared witnesses for overlapping members. Their true coupling is
now an explicit, reviewable surface, and each gains a mock-context test
suite covering flows previously testable only through the full view
model. Delivery/read acks now also clear the router's retained-send
outbox via the delivery context.
New LargeTopologyTests exercise production-shaped meshes with the
in-memory harness: an 8-peer relay chain with per-hop TTL decay, a
14-peer cyclic mesh with exactly-once delivery, partition/heal, and
topology churn.
App-layer runtime/model files updated alongside.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>