Commit 7c7832147a2e
tarball1 file changed+69-44
@@ -48,8 +48,9 @@ The default one-event initial window cannot recover all earlier pings, including
4848
messages sent while the client was offline. Raising4949
`SyncServiceBuilder::with_room_list_timeline_limit` increases the initial5050
window but does not establish continuous history. The response hook processes51
-joined-room events and records limited timelines, initial room coverage,52
-checkpoint discontinuities, and recovery gaps.51
+joined-room events and records limited timelines of rooms it had already52
+observed, checkpoint discontinuities, and recovery gaps. A room seen for the53
+first time adds no gap, since every room starts that way.5354
5455
## Observation and durable commit5556
@@ -84,14 +85,27 @@ a handler and placing work on a queue without awaiting its commit does not
8485
close the crash window.8586
8687
macltok uses shared `pos` through its SDK service and commits each response87
-batch through one serialized mentions actor before the hook acknowledges it.88
-The actor records joined-room events, raw encrypted candidates, limited89
-timeline gaps, and the connection checkpoint in the same inbox revision.90
-Stable room and event keys make replay idempotent. Context capture, mirror91
-publication, and event-stream delivery continue after the commit. The classic88
+batch through one serialised mentions actor before the hook acknowledges it.89
+The actor records events from joined rooms and from rooms the same response90
+leaves, raw encrypted candidates, limited timeline gaps, and the connection91
+checkpoint in one store commit. Stable room and event keys make replay92
+idempotent. The mentions watch snapshot is replaced before the ack, while93
+mirror and event-stream delivery and context capture run after it. The classic9294
sync `next_batch` ordering differs and does not establish this guarantee.9395
Nor does this ordering make arbitrary concurrent sync loops safe.9496
97
+The checkpoint keeps the position the committed response started from as well98
+as the one it ended at. A crash after the commit but before the SDK saves `pos`99
+replays from that earlier position, and the replay is a superset of what was100
+committed, so it is not treated as a discontinuity. Any other starting position101
+records a `SyncDiscontinuity` gap and marks every observed room unresolved.102
+103
+The hook never fails sync. A failed write keeps the inbox in memory, records a104
+`StoreFailed` gap, acks, and retries the write a minute later, so a restart105
+before a write lands shows up as a discontinuity. The hook also stops waiting106
+after ten seconds and records `HookTimedOut`, so a stuck actor cannot hold the107
+position lock.108
+95109
Sources are `crates/matrix-sdk/src/event_handler/mod.rs`,96110
`crates/matrix-sdk/src/event_handler/static_events.rs`,97111
`crates/matrix-sdk/src/sliding_sync/client.rs`,@@ -114,13 +128,13 @@ acknowledgement returns a sync error without advancing the position.
114128
115129
The hook runs outside SDK store locks and supports local futures on wasm and116130
sendable futures natively. The default absent hook preserves existing SDK117
-behavior. Detached response processing observes a dropped store131
+behaviour. Detached response processing observes a dropped store118132
acknowledgement after its caller is cancelled. Checkpoint replay works after119133
SDK room state was saved, and stable event keys deduplicate repeated commits.120134
121135
The SDK audit and focused validation covered commit ordering, failed and122136
dropped acknowledgements, caller cancellation, replay after room-state123
-persistence, restoring a previous checkpoint, absent-hook behavior, builder137
+persistence, restoring a previous checkpoint, absent-hook behaviour, builder124138
forwarding, and connection isolation. The SDK UI library also received a125139
focused wasm compilation check with its `js` feature. The SDK changes are126140
published to the user's fork under `mentions-commit-hook`, and the manifest@@ -153,9 +167,11 @@ store-free client uses memory. Even with persistence, the asynchronous cache
153167
consumer is not awaited before Sliding Sync saves its position.154168
155169
The inbox retains unresolved encrypted candidates as encrypted raw events156
-within a configurable bound. It retries due candidates at startup and resets157
-their retry time when decryption reports arrive. Candidate eviction sets the158
-coverage truncation flag. Imported keys and backups cannot reconstruct events170
+within a configurable bound. It retries due candidates at startup. A report of171
+resolved events retries the encrypted candidates of that room, and lag or a172
+new backup retries all of them. Candidates still unresolved a week after they173
+were first seen expire behind a `CandidateExpired` gap. Over the bound the174
+oldest events go first, and eviction sets the coverage truncation flag. Imported keys and backups cannot reconstruct events159175
already discarded by every cache.160176
161177
Sources are `crates/matrix-sdk/src/event_cache/mod.rs`,@@ -168,19 +184,19 @@ Sources are `crates/matrix-sdk/src/event_cache/mod.rs`,
168184
Structured `m.mentions.user_ids` and `m.mentions.room` supply semantic169185
candidates. The classifier does not infer pings from rendered bodies or170186
unread counts. A reply relationship alone does not prove that a reply notifies171
-the user, so reply reasons require notification metadata and confirmation that172
-the referenced event belongs to the signed-in user.187
+the user, so reply reasons require confirmation that the referenced event188
+belongs to the signed-in user plus either an explicit user mention or a189
+notifying highlight.173190
174191
`Room::event_push_actions` evaluates push rules but does not independently175
-filter ignored senders. The classifier calls `Client::is_user_ignored` and176
-rechecks entries when the ignore list changes.192
+filter ignored senders. The service reads the ignore list once per response193
+and again when it changes, and rechecks entries then.177194
178195
A room mute can suppress evaluated notification actions while structured179196
mention metadata remains present. The classifier retains explicit user180
-mentions in muted rooms and marks notification delivery as suppressed. Reply181
-reasons require both a reply to the user and a notifying highlight. Room-wide197
+mentions in muted rooms and marks notification delivery as suppressed. Room-wide182198
reasons require the relevant push conditions and sender power level. Missing183
-authorization context remains unresolved for reevaluation. Keyword-only199
+authorisation context remains unresolved for reevaluation. Keyword-only184200
notifications are outside the inbox. The classifier excludes the signed-in185201
user's own events even when they contain explicit mention metadata.186202
@@ -197,7 +213,7 @@ Ruma exposes
197213
It accepts opaque `from`, optional `limit`, and optional `only` fields.198214
Responses contain notifications and an optional `next_token`. macltok requests199215
50 notifications per page. Entries include raw events, room IDs, timestamps,200
-actions, and server notification read state, which does not initialize or216
+actions, and server notification read state, which does not initialise or201217
replace local inbox read state.202218
203219
The service uses this endpoint as a bounded candidate source, validates@@ -207,7 +223,7 @@ including unresolved encrypted candidates. A failed commit leaves the prior
207223
cursor available for replay.208224
209225
An exhausted notification list does not mean complete room history. Muted-room210
-pings, encrypted mentions not recognized by the server, expired notifications,226
+pings, encrypted mentions not recognised by the server, expired notifications,211227
and server-specific retention can be absent. Recovery inspects at most 20 pages212228
of 50 notifications per run and resumes from committed cursors later. The213229
older-history cursor is separate from the cursor for scans from the newest@@ -224,8 +240,7 @@ remain visible when continuity cannot be established.
224240
Coverage exposes the earliest observed timestamp, history-endpoint support,225241
recovery activity, known room gaps, unresolved candidate count, and retention226242
truncation. The earliest timestamp is a summary, not proof that every room is227
-covered after that time. The inbox keeps its incomplete-history flag even228
-after notification pagination ends.243
+covered after that time. Each gap carries a typed `MentionGapReason`.229244
230245
Complete offline mention recovery across arbitrary muted and encrypted rooms231246
is not supplied by this endpoint or by the current sync configuration. It@@ -242,10 +257,12 @@ inbox commits and discovery checkpoints.
242257
243258
The passphrase wraps a random SDK `StoreCipher`. Logical keys are hashed, and244259
authenticated encrypted records include schema, account identity, and logical245
-key. Restore rejects mismatched accounts, unsupported schemas, corrupt records,246
-and inconsistent record counts or byte totals. Atomic transactions compare the247
-previous encrypted header before publishing a new revision, so stale writers248
-cannot overwrite a newer checkpoint.260
+key. Restore treats mismatched accounts, unsupported schemas, corrupt records,261
+and inconsistent record counts or byte totals as unreadable, empties the store262
+and records a `StoreReset` gap rather than failing `Account::build`. A store263
+that cannot be opened at all leaves the inbox in memory behind `StoreFailed`.264
+Atomic transactions compare the previous encrypted header before publishing a265
+new revision, so stale writers cannot overwrite a newer checkpoint.249266
250267
The default budget is 64 MiB of encrypted records, hashed keys, header, and251268
wrapped cipher, excluding database page overhead. The store record cap is@@ -255,22 +272,29 @@ limit requires eviction, the store drops bounded data and marks coverage
255272
truncated.256273
257274
The storage tests cover atomic restore of records and checkpoints, stale258
-writers, capacity failure, encrypted physical records, wrong credentials or259
-account, corruption, unsupported schemas, and injected SQLite write failure.260
-The inbox tests cover batch failure, notification recovery, live context261
-updates, and capture persistence across session resume.262
-263
-264
-The inbox is serialized into one `inbox` value and encrypted as a single265
-record on every revision. Each update reserializes and re-encrypts the retained266
-inbox, so write cost scales with retained data. Decrypted previews, saved267
-context, raw candidates, pending capture state, media source registrations,268
-read state, coverage, and sync checkpoints stay inside the authenticated269
-encrypted payload. SDK SQLite custom values remain plaintext and are not used275
+writers, capacity failure, encrypted physical records, resets on wrong276
+credentials or account, corruption and unsupported schemas, and injected277
+SQLite write failure. The inbox tests cover failed writes, checkpoint-only278
+writes, replay from the previous checkpoint, reset of an unreadable inbox,279
+notification recovery, live context updates, and capture persistence across280
+session resume.281
+282
+The store holds two records. `inbox` carries entries, saved events, context283
+windows, candidates, edits, redactions, coverage and recovery cursors. `sync`284
+carries the connection checkpoints, observed rooms, per-room repair coverage285
+and the inbox revision it belongs to. Every response writes `sync`, and286
+`inbox` only when the response changed it, both in one commit. A quiet287
+response therefore costs one small record, while a changed inbox is still288
+reserialised and re-encrypted whole. Background completions are applied in289
+memory and written together at most once a second. When the budget is short,290
+eviction runs against a size estimate and the inbox is written once.291
+Decrypted previews, saved context, raw candidates, pending capture state,292
+media source registrations, read state, coverage, and sync checkpoints stay293
+inside the authenticated encrypted payloads. SDK SQLite custom values remain plaintext and are not used270294
for decrypted mentions.271295
272296
Context capture in `src/engine/context.rs` sizes the before and after requests273
-independently and retains the media registrations produced by normalization.297
+independently and retains the media registrations produced by normalisation.274298
Each capture requests up to 10 events on each side. At most two capture jobs275299
run concurrently, with up to 128 pending or failed entries considered for276300
scheduling at a time. Capture uses independent `/context` and `/messages`@@ -278,7 +302,7 @@ requests, so it does not paginate the live cache. Media source registrations
278302
are encrypted with saved events and registered again when context is read.279303
Media bodies and key material do not enter the mentions public model.280304
281
-One account-scoped mentions actor serializes store commits for entries,305
+One account-scoped mentions actor serialises store commits for entries,282306
unresolved candidates, shared context events, read state, revisions, discovery283307
checkpoints, and retention decisions.284308
@@ -308,7 +332,7 @@ that boundary, including older messages resolved by late decryption.
308332
## Executable probes309333
310334
`tests/mentions_discovery.rs` exercises unopened-room update and cache access,311
-push-rule behavior for ignored senders and muted rooms, and typed335
+push-rule behaviour for ignored senders and muted rooms, and typed312336
notification-history pagination.313337
314338
`tests/mentions_decryption.rs` exchanges a real encrypted event and a delayed@@ -327,11 +351,12 @@ demonstrates that returning a handler error still advances the position.
327351
with revision boundaries, independent context limits, and persistence across328352
session resume. `tests/mentions_late_decryption.rs` verifies that a delayed room key329353
updates the production inbox once. `src/mentions/service_tests.rs` covers330
-checkpoint rollback, notification-history recovery, and context updates.354
+failed and checkpoint-only writes, checkpoint replay, store reset,355
+notification-history recovery, and context updates.331356
`src/mentions/store/tests.rs` exercises encrypted record restore, capacity,332357
stale writers, and corruption handling.333358
334
-These probes establish SDK behavior against mocked responses. They do not359
+These probes establish SDK behaviour against mocked responses. They do not335360
establish homeserver notification retention, room-list completeness, or336361
complete history on a deployed server.