Part of the high-latency work. Measured behind a proxy adding 125 ms each way (250 ms per round trip), by opening an unread message and following the mark-as-read echo.
A pushed mail change settles in two round trips instead of three.
Before: Mailbox/get + Email/changes → Email/get for what changed → the list, the open thread and a second Mailbox/get.
Now: Email/changes goes out with two Email/get calls, for updated and created messages, which back-reference /updated and /created. maxChanges is capped at maxObjectsInGet, so a page's back-referenced ids always fit in one get. The single Mailbox/get goes out at the same moment.
Then the list and the open thread follow, as before.
Updates merge only into messages already held. New mail is taken in, so the new-mail notice doesn't fetch it again.
cannotCalculateChanges is still caught: Email/changes is the first response, and chain throws on it.
Session refreshes.
A reply whose sessionState differs from the session's is announced once per new value, not once per reply.
useSession.refresh() shares a refresh already in flight.
In the mock, the session's state followed the data counter while replies always said "1". Every JMAP reply therefore triggered GET /api/auth/session?refresh=1. The session and its replies now share one constant, as they do on Stalwart. In the measurement, the per-request session calls are gone.
Tests.
Web: 1,363 pass, with 3 new cases:
one request for changes and their gets, with one Mailbox/get and nothing fetched twice;
Part of the high-latency work. Measured behind a proxy adding 125 ms each way (250 ms per round trip), by opening an unread message and following the mark-as-read echo.
**A pushed mail change settles in two round trips instead of three.**
- Before: `Mailbox/get` + `Email/changes` → `Email/get` for what changed → the list, the open thread and a second `Mailbox/get`.
- Now: `Email/changes` goes out with two `Email/get` calls, for updated and created messages, which back-reference `/updated` and `/created`. `maxChanges` is capped at `maxObjectsInGet`, so a page's back-referenced ids always fit in one get. The single `Mailbox/get` goes out at the same moment.
- Then the list and the open thread follow, as before.
- Updates merge only into messages already held. New mail is taken in, so the new-mail notice doesn't fetch it again.
- `cannotCalculateChanges` is still caught: `Email/changes` is the first response, and `chain` throws on it.
**Session refreshes.**
- A reply whose `sessionState` differs from the session's is announced once per new value, not once per reply.
- `useSession.refresh()` shares a refresh already in flight.
- In the mock, the session's `state` followed the data counter while replies always said `"1"`. Every JMAP reply therefore triggered `GET /api/auth/session?refresh=1`. The session and its replies now share one constant, as they do on Stalwart. In the measurement, the per-request session calls are gone.
**Tests.**
- Web: 1,363 pass, with 3 new cases:
- one request for changes and their gets, with one `Mailbox/get` and nothing fetched twice;
- `cannotCalculateChanges`;
- a session state announced once.
- Server: 251 pass.
No new UI strings.
**Merged** 2026-09-16 as coffey-labs/ihasmail@e158ebac5a92
<sub>Rebuilt from: git history, session transcript.</sub>
This repo is archived. You cannot comment on issues.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Part of the high-latency work. Measured behind a proxy adding 125 ms each way (250 ms per round trip), by opening an unread message and following the mark-as-read echo.
A pushed mail change settles in two round trips instead of three.
Mailbox/get+Email/changes→Email/getfor what changed → the list, the open thread and a secondMailbox/get.Email/changesgoes out with twoEmail/getcalls, for updated and created messages, which back-reference/updatedand/created.maxChangesis capped atmaxObjectsInGet, so a page's back-referenced ids always fit in one get. The singleMailbox/getgoes out at the same moment.cannotCalculateChangesis still caught:Email/changesis the first response, andchainthrows on it.Session refreshes.
sessionStatediffers from the session's is announced once per new value, not once per reply.useSession.refresh()shares a refresh already in flight.statefollowed the data counter while replies always said"1". Every JMAP reply therefore triggeredGET /api/auth/session?refresh=1. The session and its replies now share one constant, as they do on Stalwart. In the measurement, the per-request session calls are gone.Tests.
Mailbox/getand nothing fetched twice;cannotCalculateChanges;No new UI strings.
Merged 2026-09-16 as coffey-labs/ihasmail@e158ebac5a
Rebuilt from: git history, session transcript.