fix: implement MSC3959/3960/3961 extension room scoping and required_state delta #1450

Open
0xnim wants to merge 10 commits from 0xnim/continuwuity:fix/v5-sync into main
Contributor

This pull request fixes several spec-compliance gaps in the Simplified Sliding Sync (MSC4186) extension handling.

Receipts (MSC3960), typing (MSC3961), and per-room account data (MSC3959) were all being sent for every room regardless of their enabled flag or lists/rooms filter fields. These are now correctly scoped using a shared extension_room_set helper.

Additionally, required_state on incremental syncs now only sends state events that have changed since they were last delivered, and skips the DB lookup entirely when no state is requested and nothing else has changed.

Pull request checklist:

  • This pull request targets the main branch, and the branch is named something other than
    main.
  • I have written an appropriate pull request title and my description is clear.
  • I understand I am responsible for the contents of this pull request.
  • I have followed the contributing guidelines:
<!-- In order to help reviewers know what your pull request does at a glance, you should ensure that 1. Your PR title is a short, single sentence describing what you changed 2. You have described in more detail what you have changed, why you have changed it, what the intended effect is, and why you think this will be beneficial to the project. If you have made any potentially strange/questionable design choices, but didn't feel they'd benefit from code comments, please don't mention them here - after opening your pull request, go to "files changed", and click on the "+" symbol in the line number gutter, and attach comments to the lines that you think would benefit from some clarification. --> This pull request fixes several spec-compliance gaps in the Simplified Sliding Sync (MSC4186) extension handling. Receipts (MSC3960), typing (MSC3961), and per-room account data (MSC3959) were all being sent for every room regardless of their enabled flag or lists/rooms filter fields. These are now correctly scoped using a shared extension_room_set helper. Additionally, required_state on incremental syncs now only sends state events that have changed since they were last delivered, and skips the DB lookup entirely when no state is requested and nothing else has changed. <!-- Example: This pull request allows us to warp through time and space ten times faster than before by double-inverting the warp drive with hyperheated jump fluid, both making the drive faster and more efficient. This resolves the common issue where we have to wait more than 10 milliseconds to engage, use, and disengage the warp drive when travelling between galaxies. --> <!-- Closes: #... --> <!-- Fixes: #... --> <!-- Uncomment the above line(s) if your pull request fixes an issue or closes another pull request by superseding it. Replace `#...` with the issue/pr number, such as `#123`. --> **Pull request checklist:** <!-- You need to complete these before your PR can be considered. If you aren't sure about some, feel free to ask for clarification in #dev:continuwuity.org. --> - [X] This pull request targets the `main` branch, and the branch is named something other than `main`. - [X] I have written an appropriate pull request title and my description is clear. - [X] I understand I am responsible for the contents of this pull request. - I have followed the [contributing guidelines][c1]: - [X] My contribution follows the [code style][c2], if applicable. - [X] I ran [pre-commit checks][c1pc] before opening/drafting this pull request. - [X] I have [tested my contribution][c1t] (or proof-read it for documentation-only changes) myself, if applicable. This includes ensuring code compiles. - [X] My commit messages follow the [commit message format][c1cm] and are descriptive. - [X] I have written a [news fragment][n1] for this PR, if applicable<!--(can be done after hitting open!)-->. <!-- Notes on these requirements: - While not required, we encourage you to sign your commits with GPG or SSH to attest the authenticity of your changes. - While we allow LLM-assisted contributions, we do not appreciate contributions that are low quality, which is typical of machine-generated contributions that have not had a lot of love and care from a human. Please do not open a PR if all you have done is asked ChatGPT to tidy up the codebase with a +-100,000 diff. - In the case of code style violations, reviewers may leave review comments/change requests indicating what the ideal change would look like. For example, a reviewer may suggest you lower a log level, or use `match` instead of `if/else` etc. - In the case of code style violations, pre-commit check failures, minor things like typos/spelling errors, and in some cases commit format violations, reviewers may modify your branch directly, typically by making changes and adding a commit. Particularly in the latter case, a reviewer may rebase your commits to squash "spammy" ones (like "fix", "fix", "actually fix"), and reword commit messages that don't satisfy the format. - Pull requests MUST pass the `Checks` CI workflows to be capable of being merged. This can only be bypassed in exceptional circumstances. If your CI flakes, let us know in matrix:r/dev:continuwuity.org. - Pull requests have to be based on the latest `main` commit before being merged. If the main branch changes while you're making your changes, you should make sure you rebase on main before opening a PR. Your branch will be rebased on main before it is merged if it has fallen behind. - We typically only do fast-forward merges, so your entire commit log will be included. Once in main, it's difficult to get out cleanly, so put on your best dress, smile for the cameras! --> [c1]: https://forgejo.ellis.link/continuwuation/continuwuity/src/branch/main/CONTRIBUTING.md [c2]: https://forgejo.ellis.link/continuwuation/continuwuity/src/branch/main/docs/development/code_style.mdx [c1pc]: https://forgejo.ellis.link/continuwuation/continuwuity/src/branch/main/CONTRIBUTING.md#pre-commit-checks [c1t]: https://forgejo.ellis.link/continuwuation/continuwuity/src/branch/main/CONTRIBUTING.md#running-tests-locally [c1cm]: https://forgejo.ellis.link/continuwuation/continuwuity/src/branch/main/CONTRIBUTING.md#commit-messages [n1]: https://towncrier.readthedocs.io/en/stable/tutorial.html#creating-news-fragments
handle_lists always returned BTreeMap::default() and the call site
never captured the return value.
The parameter was unused and suppressed with an underscore prefix.
Also replace explicit empty struct construction with Default::default().
The key is derived from sender_user, sender_device, and conn_id, all of
which are constant across list iterations. Compute it once before the loop.
Rooms with pending required state events were incorrectly skipped on
incremental syncs when the timeline and receipts were empty. Move
collect_required_state before the early-continue and add its emptiness
as a guard condition.
- Restore `_services: &Services` parameter on `collect_receipts`
- Add cheap pre-guard in `process_rooms` to skip `collect_required_state`
  DB call when no state was requested and there is nothing else to send
- `handle_lists` now returns `BTreeMap<String, BTreeSet<OwnedRoomId>>`
  (rooms visible per list window)
- `fetch_subscriptions` now returns `BTreeSet<OwnedRoomId>` (subscription
  rooms); captured as `_rooms_by_list` / `_subscription_rooms` for use
  in the upcoming extension-scoping changes
Add extension_room_set helper that computes the qualifying room set for
an extension from its lists and rooms filter fields per MSC4186.

MSC3960 receipts: move collection out of process_rooms into
collect_receipts, gated on enabled + filters.

MSC3961 typing: replace the dead filter variables in collect_typing_events
with an extension_room_set call so only qualifying rooms get typing data.

MSC3959 account data: move per-room collection out of process_rooms into
collect_account_data using per-room roomsince values and filter.

Route handler: collect_receipts and collect_account_data now run after
todo_rooms is populated; e2ee and to_device still run in parallel join.
Add SnakeSyncCache.sent_state: a per-room map of (event_type, state_key)
→ event_id recording which state events were last sent to the client.

Two new Service methods:
  get_room_sent_state  – read the cache for one room
  update_room_sent_state – write the cache after each sync response

collect_required_state gains snake_key (None = connectionless/no cache)
and roomsince parameters:
  - roomsince == 0 (initial sync): return all matching events and
    populate the cache.
  - roomsince != 0 and snake_key is Some (incremental, has connection):
    only return events whose event_id differs from the cached value.
  - roomsince != 0 and snake_key is None (connectionless incremental):
    return all matching events (no cache to diff against).

Also export SnakeConnectionsKey from conduwuit-service::sync so the API
crate can reference it without duplicating the type definition.
style: cargo +nightly fmt
All checks were successful
Documentation / Build and Deploy Documentation (pull_request) Has been skipped
Checks / Prek / Pre-commit & Formatting (pull_request) Successful in 1m24s
Checks / Prek / Clippy and Cargo Tests (pull_request) Successful in 27m22s
5e33b29efc
Author
Contributor

note for reviewers, commits are well seperated, might be easier to review one by one, then wholly

note for reviewers, commits are well seperated, might be easier to review one by one, then wholly
Owner

Yay! This is something I wanted to do since I was implementing typing indicators. Let me have a preliminary read

Yay! This is something I wanted to do since I was implementing typing indicators. Let me have a preliminary read
nex 2026-02-25 01:13:48 +00:00
nex added this to the next milestone 2026-02-25 01:14:03 +00:00
Jade requested review from Jade 2026-02-25 01:22:28 +00:00
Owner

I'm seeing a lot of good stuff here, will finish reviewing after I've slept

I'm seeing a lot of good stuff here, will finish reviewing after I've slept
Jade approved these changes 2026-02-25 17:10:36 +00:00
Jade left a comment
Owner

Code level review, I think this is really good

Code level review, I think this is really good
@ -172,3 +165,2 @@
typing: sync_events::v5::response::Typing::default(),
};
let (e2ee, to_device) = try_join(e2ee, to_device).await?;
Owner

I'm not sure building the response early and then filling in the default fields is the best idea?
It might be better to actually build it near the bottom.

I'm not sure building the response early and then filling in the default fields is the best idea? It might be better to actually build it near the bottom.
@ -171,3 +165,1 @@
receipts,
typing: sync_events::v5::response::Typing::default(),
};
let (e2ee, to_device) = try_join(e2ee, to_device).await?;
Owner

I don't think this needs to be awaited before the room-dependant fields.

I would kind of try plot a dependency graph here - we can start this before gathering rooms, but we don't need to finish it before building the response.
I think the room dependant ones should probably be shoved into their own future so they can be awaited in parallel (and happen in parallel within that)? Alternatively non-deoendant ones can be spawned in a task so they're active before an await.
That might require a bit of a restructure though.

I don't think this needs to be awaited before the room-dependant fields. I would kind of try plot a dependency graph here - we can start this before gathering rooms, but we don't need to finish it before building the response. I think the room dependant ones should probably be shoved into their own future so they can be awaited in parallel (and happen in parallel within that)? Alternatively non-deoendant ones can be spawned in a task so they're active before an await. That might require a bit of a restructure though.
Owner

I haven't checked if this handles the per-extension lists and rooms filters, but that was the other thing I wanted to fix. I'm not sure if anything actually uses it, though

I haven't checked if this handles the per-extension lists and rooms filters, but that was the other thing I wanted to fix. I'm not sure if anything actually uses it, though
All checks were successful
Documentation / Build and Deploy Documentation (pull_request) Has been skipped
Checks / Prek / Pre-commit & Formatting (pull_request) Successful in 1m24s
Required
Details
Checks / Prek / Clippy and Cargo Tests (pull_request) Successful in 27m22s
Required
Details
This pull request is blocked because it's outdated.
This branch is out-of-date with the base branch
You are not authorized to merge this pull request.
View command line instructions

Checkout

From your project repository, check out a new branch and test the changes.
git fetch -u fix/v5-sync:0xnim-fix/v5-sync
git switch 0xnim-fix/v5-sync
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
continuwuation/continuwuity!1450
No description provided.