Add field correctness diagnostics: store invariant audit, drop and bounds proofs

ConversationStore.auditInvariants() verifies the per-conversation and
store-level message-ID indexes, caps, timestamp ordering, unread-set
membership, and selection validity - wired to the existing read-receipt
cleanup cadence, loud (.error) on violation, sampled heartbeat when
healthy (~2.8ms per audit at 5k messages, benchmarked and floored).
Router drops log both outcomes (marked failed / skipped by no-downgrade
guard); relay cap evictions, age sweeps, and jittered reconnect delays
log their counts; mirrored republishes get a sampled proof line. 11 new
invariant tests corrupt store state through DEBUG-only hooks since the
single-writer lockdown makes those states unreachable via intents.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
jack
2026-06-11 19:28:47 +02:00
co-authored by Claude Fable 5
parent 22be3d6392
commit 8899cb7f9e
8 changed files with 504 additions and 9 deletions
@@ -83,12 +83,22 @@ private extension ChatViewModelBootstrapper {
// not cover `.failed` over confirmed receipts, so guard here: a drop
// of an already-delivered/read message (e.g. a stale retained copy)
// must not downgrade its status.
viewModel.messageRouter.onMessageDropped = { [weak viewModel] messageID, _ in
viewModel.messageRouter.onMessageDropped = { [weak viewModel] messageID, peerID in
guard let viewModel else { return }
switch viewModel.conversations.deliveryStatus(forMessageID: messageID) {
case .delivered, .read:
return
// Field proof of the no-downgrade guard: the drop arrived
// after a confirmed receipt, so the `.failed` write is
// deliberately skipped.
SecureLogger.warning(
"📤 Router dropped message \(messageID.prefix(8))… for \(peerID.id.prefix(8))… → .failed skipped (already delivered/read)",
category: .session
)
default:
SecureLogger.warning(
"📤 Router dropped message \(messageID.prefix(8))… for \(peerID.id.prefix(8))… → marked failed",
category: .session
)
viewModel.conversations.setDeliveryStatus(
.failed(reason: "Not delivered"),
forMessageID: messageID