diff --git a/docs/audit-2026-09.md b/docs/audit-2026-09.md
index 22bde7fb..a089ae78 100644
--- a/docs/audit-2026-09.md
+++ b/docs/audit-2026-09.md
@@ -56,24 +56,28 @@ Effort: S (hours), M (a day or two), L (a week or more).
## Summary: fix these ten first
-1. [ ] **P0 · S:** read receipts carry attacker-controlled headers and are sent for messages the user
- never opened ([S1](#s1), [B7](#b7)).
-2. [ ] **P0 · S–M:** published release APKs are signed with the public React Native debug key
- ([S2](#s2)).
-3. [ ] **P0 · S–M:** viewer actions on a shared/group message hit the user's own account and can
- delete an unrelated message ([B3](#b3)).
-4. [ ] **P0 · S:** opening, starring or tagging replaces the whole keyword map; stars and tags are
- erased ([B4](#b4)).
-5. [ ] **P1 · S:** the viewer switches to another message when new mail arrives, and its buttons act
- on the message you are not looking at ([B5](#b5)).
-6. [ ] **P1 · S–M:** messages disappear from the list after a background refresh until you pull to
- refresh ([B6](#b6)).
-7. [ ] **P1 · S:** scheduled send, the undo-send delay and the Scheduled folder are dead on every
- Stalwart server (#57, [B8](#b8)).
-8. [ ] **P1 · S–M:** sharing a file into the app always fails, and `SENDTO`/`+`-address `mailto:` links
- open nothing ([B9](#b9), [B10](#b10), [B11](#b11)).
-9. [ ] **P1 · M:** opening a message takes 1.7–1.9 s and the layout jumps three times ([PF1](#pf1)).
-10. [ ] **P1 · M:** cold start takes 3.6–4.1 s to a settled inbox and is JS-bound ([PF2](#pf2)).
+1. [x] **P0 · S:** read receipts carry attacker-controlled headers and are sent for messages the user
+ never opened ([S1](#s1), [B7](#b7)). Fixed in `cb6ddd6`, `5f3f584`.
+2. [x] **P0 · S–M:** published release APKs are signed with the public React Native debug key
+ ([S2](#s2)). Fixed in `6f0abb4`, `746572c`; the release key and secrets still have to be set up
+ before the next release.
+3. [x] **P0 · S–M:** viewer actions on a shared/group message hit the user's own account and can
+ delete an unrelated message ([B3](#b3)). Fixed in `5c7301b`.
+4. [x] **P0 · S:** opening, starring or tagging replaces the whole keyword map; stars and tags are
+ erased ([B4](#b4)). Fixed in `5041897`.
+5. [x] **P1 · S:** the viewer switches to another message when new mail arrives, and its buttons act
+ on the message you are not looking at ([B5](#b5)). Fixed in `9c31de2`.
+6. [x] **P1 · S–M:** messages disappear from the list after a background refresh until you pull to
+ refresh ([B6](#b6)). Fixed in `0a8686a`.
+7. [x] **P1 · S:** scheduled send, the undo-send delay and the Scheduled folder are dead on every
+ Stalwart server (#57, [B8](#b8)). Fixed in `e28e8b4`.
+8. [x] **P1 · S–M:** sharing a file into the app always fails, and `SENDTO`/`+`-address `mailto:` links
+ open nothing ([B9](#b9), [B10](#b10), [B11](#b11)). Fixed in `cd4ce9d`, `06e8942`, `ee652d3` (not
+ yet checked on a device).
+9. [x] **P1 · M:** opening a message takes 1.7–1.9 s and the layout jumps three times ([PF1](#pf1)).
+ Fixed in `33e303e`, `ac8e3fb`, `1348e7d`, `01fc6fb`, `6ebadf7`, `54b169c` (not re-timed yet).
+10. [x] **P1 · M:** cold start takes 3.6–4.1 s to a settled inbox and is JS-bound ([PF2](#pf2)).
+ Fixed in `604eb39`, `063432b`, `5bcfe1f`, `82ca359`, `b9db376` (not re-timed yet).
**Why these ten:**
- **1–2:** security holes that can be exploited today without any user mistake.
@@ -89,8 +93,8 @@ suspected, or cosmetic.
### Crashes
-- [ ] **B1 · P1 · M: session restore can hang forever when a persisted cache fails
- to load.**
+- [x] **B1 · P1 · M: session restore can hang forever when a persisted cache fails
+ to load.** Fixed in `29169c3`, `e37621b`, `24849ba`.
- Where: `src/stores/auth-store.ts:23-31`, `src/stores/auth-store.ts:603-608`,
`src/stores/settings-store.ts:423`.
- Why:
@@ -110,8 +114,8 @@ suspected, or cosmetic.
### Data correctness
-- [ ] **B3 · P0 · S–M: viewer actions use the open folder's account instead of the
- message's.**
+- [x] **B3 · P0 · S–M: viewer actions use the open folder's account instead of the
+ message's.** Fixed in `5c7301b`, `d2066da`.
- Where: `src/screens/EmailThreadScreen.tsx:107-113` (owner falls back to the list folder),
`src/screens/EmailThreadScreen.tsx:355-358` (delete), `:388-414` (archive, move, spam),
`src/stores/email-store.ts:1194-1197`.
@@ -122,8 +126,8 @@ suspected, or cosmetic.
id, since Stalwart ids are only unique per account) to Trash.
- The team message stays where it was.
- **Verified.**
-- [ ] **B4 · P0 · S: mark-read, star, pin and tag replace the whole `keywords`
- map.**
+- [x] **B4 · P0 · S: mark-read, star, pin and tag replace the whole `keywords`
+ map.** Fixed in `5041897`, `ddff2f7`.
- Where: `src/stores/email-store.ts:963-983`, `src/api/email.ts:602-609`. Outbox replay does
the same.
- Web: `lib/jmap/client.ts` (`keywords/$seen` pointer patch, `batchMarkAsRead`). RN already has
@@ -135,8 +139,8 @@ suspected, or cosmetic.
- Repro 2 (message in the list): Star sent the full map `{$notjunk, $seen, $flagged}`. That
reverts any keyword another client changed since the list loaded.
- **Verified.**
-- [ ] **B5 · P1 · S: the viewer pager follows the live list, so new mail shifts the
- page under the user.**
+- [x] **B5 · P1 · S: the viewer pager follows the live list, so new mail shifts the
+ page under the user.** Fixed in `9c31de2`, `e0b7288`.
- Where: `src/screens/EmailThreadScreen.tsx:81-102`, `:639`.
- Repro:
- Open "Quarterly numbers" (row 2) and deliver 2 new messages.
@@ -145,7 +149,7 @@ suspected, or cosmetic.
Archive use the same `activeEmailId`.
- Fix: snapshot the id list when the viewer opens.
- **Verified.**
-- [ ] **B6 · P1 · S–M: messages vanish after a delta refresh.**
+- [x] **B6 · P1 · S–M: messages vanish after a delta refresh.** Fixed in `0a8686a`.
- Where: `src/stores/email-store.ts:1953-1990` and `:2058-2060`.
- Web: `stores/email-store.ts` (`applyEmailDeltaNow`, which runs `Email/changes` against the
list's own state).
@@ -161,8 +165,8 @@ suspected, or cosmetic.
- A pull-to-refresh (a full query) brings it back.
- Before the empty-folder visit the same test worked (`queryChanges` → `changes` → `get`).
- **Verified.**
-- [ ] **B7 · P1 · S: with "Always send", read receipts go out for messages that were
- never displayed, and a failed one leaves junk in Sent.**
+- [x] **B7 · P1 · S: with "Always send", read receipts go out for messages that were
+ never displayed, and a failed one leaves junk in Sent.** Fixed in `5f3f584`, `1fb60a5`.
- Where: `src/components/email/ReadReceiptBanner.tsx:92-101` (mounted in the neighbouring pages
the pager pre-renders, `src/screens/EmailThreadScreen.tsx:641` `windowSize={3}`),
`src/api/email.ts:1456-1486`.
@@ -173,8 +177,8 @@ suspected, or cosmetic.
confirm receipt" stays in Sent. `$mdnsent` is not set and a failed auto-send clears its guard
(`autoRef.current = null`), so it can run again and import another copy.
- **Verified.**
-- [ ] **B8 · P1 · S: scheduled send and the undo-send delay are off on Stalwart
- (#57).**
+- [x] **B8 · P1 · S: scheduled send and the undo-send delay are off on Stalwart
+ (#57).** Fixed in `e28e8b4`, `4e7ae2f`.
- Where: `src/api/jmap-client.ts:973-999` reads `session.capabilities[submission]`. Stalwart
only puts `maxDelayedSend` and FUTURERELEASE in `accountCapabilities`. `:919-925` (attachment
size cap) has the same problem.
@@ -183,7 +187,8 @@ suspected, or cosmetic.
→ "In 1 hour" → "Scheduling unavailable: This mail server does not support scheduled send".
- The Scheduled screen and the undo delay use the same check.
- **Verified.**
-- [ ] **B9 · P1 · S: files shared into the app never upload.**
+- [x] **B9 · P1 · S: files shared into the app never upload.** Fixed in `cd4ce9d`,
+ `3c7b96e`.
- Where: `src/api/blob.ts:99` passes the `content://` URI to `createUploadTask`, which
requires a file path. `src/lib/share-intent.ts:40` names the file after the URI's last
segment.
@@ -191,20 +196,23 @@ suspected, or cosmetic.
→ alert "Failed to upload 1000000027 … IOException: Directory for
'/external/images/media/1000000027' doesn't exist".
- **Verified.**
-- [ ] **B10 · P1 · S: `ACTION_SENDTO mailto:` opens the inbox, not the composer.**
+- [x] **B10 · P1 · S: `ACTION_SENDTO mailto:` opens the inbox, not the composer.**
+ Fixed in `06e8942`.
- Where: `android/app/src/main/java/com/anonymous/bulwarkmobile/ShareIntentStore.kt:25` only
handles SEND and SEND_MULTIPLE. RN's `Linking` forwards only VIEW. The manifest registers
SENDTO anyway.
- Repro: `am start -a android.intent.action.SENDTO -d mailto:bob@partner.example` → no
composer. The same URI with VIEW works.
- **Verified.**
-- [ ] **B11 · P2 · S: `mailto:` links with a `+` address do nothing.**
+- [x] **B11 · P2 · S: `mailto:` links with a `+` address do nothing.** Fixed in
+ `ee652d3`.
- Where: `src/lib/mailto.ts:21` turns `+` into a space, validation then fails, and the parser
returns null.
- Web: `lib/validation.ts`.
- Repro: `mailto:alice+news@partner.example` → no composer.
- **Verified.**
-- [ ] **B12 · P1 · S: tasks can't be completed (#958).**
+- [x] **B12 · P1 · S: tasks can't be completed (#958).** Fixed in `dcdda5e`,
+ `a7e4e84`.
- Where: `src/stores/calendar-store.ts:710-719` sends `progressUpdated`. The checkbox at
`src/components/calendar/TasksSheet.tsx:442` swallows the error.
- Web: `6b30e91c`, `stores/task-store.ts`.
@@ -212,35 +220,38 @@ suspected, or cosmetic.
stays `needs-action` and the UI shows no error.
- Tasks are off by default (`src/stores/settings-store.ts:381`), which limits the reach.
- **Verified.**
-- [ ] **B13 · P1 · M: the Files tab stops at 500 nodes (#1069).**
+- [x] **B13 · P1 · M: the Files tab stops at 500 nodes (#1069).** Fixed in
+ `4eb8c47`, `115e162`.
- Where: `src/api/files.ts:98-112` (`FileNode/get ids:null`, which Stalwart silently caps at
`maxObjectsInGet`). The same code runs at `:133-167`.
- Web: `lib/jmap/client.ts` (`fetchAllFileNodes`, `9893b0a8`).
- Repro: with 510 root files created before the folders, `file-0490`–`0499` are listed, while
`file-0500`–`0509` and the Documents and Photos folders (and their files) never appear.
- **Verified.**
-- [ ] **B14 · P2 · S: bodies over 512 KB render cut off with no notice (#884).**
+- [x] **B14 · P2 · S: bodies over 512 KB render cut off with no notice (#884).**
+ Fixed in `4767805`.
- Where: `src/api/email.ts:493-499` (`maxBodyValueBytes: 512000`, `isTruncated` never read).
- Web: `lib/jmap/client.ts` (`2cf7427e`, refetch of truncated bodies).
- Repro: for a 586 KB HTML report, the server returns `isTruncated: true` with 59 characters.
The viewer shows the heading, one line and "…"; the list preview shows the full text.
- **Verified.**
-- [ ] **B15 · P1 · S: a redirect that drops the Authorization header signs the user
- out on launch.**
+- [x] **B15 · P1 · S: a redirect that drops the Authorization header signs the user
+ out on launch.** Fixed in `859770b`.
- Where: `src/api/jmap-client.ts:737-763`. The retry keys on `response.redirected`, which RN's
fetch never sets. An empty session then throws `AuthenticationError`, and
`src/stores/auth-store.ts:677-683` evicts the account.
- Web: `40e38d16` (#892).
- Only affects servers behind a redirecting proxy; direct Stalwart is fine.
- **Suspected.**
-- [ ] **B16 · P1 · S: "Save draft" discards the text when identities didn't load
- (offline) or there is no Drafts folder.**
+- [x] **B16 · P1 · S: "Save draft" discards the text when identities didn't load
+ (offline) or there is no Drafts folder.** Fixed in `92b42e4`: the text is no longer dropped and
+ the iOS swipe is blocked; saving a draft offline is still impossible.
- Where: `src/screens/ComposeScreen.tsx:1208` returns early; `:1288-1290` closes anyway.
- On iOS, the `beforeRemove` listener at `:1340` doesn't stop a native swipe-down; that needs
`usePreventRemove`.
- **Suspected.**
-- [ ] **B17 · P1 · S: calendar reminders get cancelled when the calendar view
- moves.**
+- [x] **B17 · P1 · S: calendar reminders get cancelled when the calendar view
+ moves.** Fixed in `4b903ed`.
- Where: `src/lib/calendar-notifications.ts:116-134` rebuilds the reminder set from the
windowed `events` and cancels everything else.
- Viewing another month drops all pending reminders. Nothing reschedules them in a session
@@ -248,8 +259,8 @@ suspected, or cosmetic.
- There is no `setNotificationHandler`, so reminders don't show while the app is in the
foreground.
- **Suspected.**
-- [ ] **B18 · P1 · S: push can end up permanently off for users with ACL-shared
- mailboxes.**
+- [x] **B18 · P1 · S: push can end up permanently off for users with ACL-shared
+ mailboxes.** Fixed in `d8046a8`.
- Where: `src/lib/push-notifications.ts` (HEAD `:223-229` builds the `emailPush` map from
every account with mailboxes).
- Stalwart rejects the whole map with `forbidden` for accounts the user doesn't own and isn't
@@ -257,31 +268,34 @@ suspected, or cosmetic.
fails the same way.
- Web: `lib/web-push.ts:86-94` has the same code.
- **Suspected.**
-- [ ] **B19 · P2 · S: "Reschedule" drops Cc and Bcc.**
+- [x] **B19 · P2 · S: "Reschedule" drops Cc and Bcc.** Fixed in `e67947a`,
+ `7e598c5`.
- Where: `src/api/email.ts:1393` rebuilds recipients from `scheduled.to`;
`src/screens/ScheduledScreen.tsx:140`.
- Web: `lib/jmap/client.ts` (reuses the envelope).
- Unreachable on Stalwart until B8 is fixed.
- **Suspected.**
-- [ ] **B20 · P2 · S: "Load more" skips messages in Unread and Starred views.**
+- [x] **B20 · P2 · S: "Load more" skips messages in Unread and Starred views.**
+ Fixed in `35e0792`.
- Where: `src/stores/email-store.ts:776` uses `position: emails.length`, which counts rows that
were kept after they stopped matching the query.
- **Suspected.**
-- [ ] **B21 · P2 · S: the event editor wipes what the user typed when a calendar push
- or the alias lookup arrives.**
+- [x] **B21 · P2 · S: the event editor wipes what the user typed when a calendar push
+ or the alias lookup arrives.** Fixed in `15717f1`.
- Where: `src/components/calendar/EventModal.tsx:231-273` (the reset effect depends on
`calendars` and `currentUserEmails`).
- **Suspected.**
-- [ ] **B22 · P2 · S: an invitation's Accept sends no RSVP when the event is outside
- the loaded calendar window, yet says "Added to calendar".**
+- [x] **B22 · P2 · S: an invitation's Accept sends no RSVP when the event is outside
+ the loaded calendar window, yet says "Added to calendar".** Fixed in `c0582ab`.
- Where: `src/components/email/CalendarInvitationBanner.tsx:177-188`.
- **Suspected.**
-- [ ] **B23 · P2 · S: "Send failed" can be shown for mail that was sent, so a retry
- sends it twice.**
+- [x] **B23 · P2 · S: "Send failed" can be shown for mail that was sent, so a retry
+ sends it twice.** Fixed in `c51a848`.
- Where: `src/api/email.ts:1178-1181` throws on any `error` response. Stalwart appends one when
`onSuccessUpdateEmail` fails after a successful submission.
- **Suspected.**
-- [ ] **B24 · P3 · S: some writes report success when the server refused them.**
+- [x] **B24 · P3 · S: some writes report success when the server refused them.**
+ Fixed in `00ad2f4` (files), `a6ef0ab` (calendar), `ddfe85a` (push).
- Where: `src/api/files.ts:199` and `:225` (rename, move, delete read `[0][1]` without checking
for an `error` response); `src/api/calendar.ts:449-459` ignores `notDestroyed`;
`src/api/push.ts:14,82,105`.
@@ -289,7 +303,8 @@ suspected, or cosmetic.
### UI glitches
-- [ ] **U1 · P2 · S: the cold-start sequence shows a blank screen and jumps twice.**
+- [x] **U1 · P2 · S: the cold-start sequence shows a blank screen and jumps twice.**
+ Fixed in `b9db376`, `f2dd3a8`.
- Where: `App.tsx:613-620`; `AppTheme` has no `windowBackground`; `PushOnboardingPrompt`.
- Frames:
- The dark splash is followed by about 1 s of blank white.
@@ -297,36 +312,38 @@ suspected, or cosmetic.
- The header then pops in and pushes the list down.
- The notification card lands over the status bar and pushes the list down again.
- **Verified** (`screenrecord`).
-- [ ] **U2 · P2 · S: opening a message shows a skeleton, then a 120 px strip of body,
- then the full body.**
+- [x] **U2 · P2 · S: opening a message shows a skeleton, then a 120 px strip of body,
+ then the full body.** Fixed in `54b169c`, `ac8e3fb`.
- Where: `src/components/EmailBodyView.tsx:588-591` (the height resets to 120 on every
message).
- The header data is already in the list row but isn't used. The quick-reply box jumps as the
WebView grows. See PF1.
- **Verified.**
-- [ ] **U3 · P2 · S: sender image and table widths are overridden.**
+- [x] **U3 · P2 · S: sender image and table widths are overridden.** Fixed in
+ `7531118`.
- Where: `src/lib/email-html.ts:452-454`. `img { max-width: 100% !important }` beats inline
`max-width`.
- Web: `68341a0b`, `ab4bf91b` (#790).
- Repro: an image with `max-width:200px; max-height:134px; width:100%` renders full width and
squashed about 1.9×; a table with `max-width:300px` spans the pane.
- **Verified.**
-- [ ] **U4 · P2 · S: RTL mail renders left-to-right.**
+- [x] **U4 · P2 · S: RTL mail renders left-to-right.** Fixed in `910c503`.
- Where: `src/lib/email-html.ts:627` and `:686` (no `dir="auto"`).
- Web: `e2990da5`, `48048f16`.
- Repro: an Arabic plain-text body is left-aligned and its sentence-final period sits on the
wrong side.
- **Verified.**
-- [ ] **U5 · P3 · S: list previews keep the server's newlines.**
+- [x] **U5 · P3 · S: list previews keep the server's newlines.** Fixed in `d45c799`.
- Rows show a dangling "…" line and have uneven heights ("Q3 revenue is up 12 %.\n\n…").
- A subject that contains CR/LF also renders over several lines in the list and the viewer.
- Where: `src/screens/EmailListScreen.tsx` row.
- **Verified.**
-- [ ] **U6 · P3 · S: the schedule sheet offers presets, then refuses them.**
+- [x] **U6 · P3 · S: the schedule sheet offers presets, then refuses them.** Fixed in
+ `802b199`, `4e7ae2f`.
- Where: `src/screens/ComposeScreen.tsx:1846-1851`. The sheet should hide the presets.
- **Verified.**
-- [ ] **U7 · P3 · S: Back in Files selection mode switches to the Mail tab instead of
- clearing the selection.**
+- [x] **U7 · P3 · S: Back in Files selection mode switches to the Mail tab instead of
+ clearing the selection.** Fixed in `8d7fbfc`, `4fdf39a`.
- **Verified.**
- [ ] **U8 · P3 · M: icon buttons and switches have no accessibility label.**
- Unlabeled controls:
@@ -338,7 +355,8 @@ suspected, or cosmetic.
- Web: `4947c6c7`.
- Repro: `uiautomator dump` shows an empty `content-desc` on all of these.
- **Verified.**
-- [ ] **U9 · P3 · S: Android notifications use the stock envelope as their small icon.**
+- [x] **U9 · P3 · S: Android notifications use the stock envelope as their small
+ icon.** Fixed in `695aa23`.
- Where: `android/app/src/main/java/com/anonymous/bulwarkmobile/BulwarkFcmModule.kt:128` and
`:181` (`android.R.drawable.ic_dialog_email`).
- **Suspected.**
@@ -348,8 +366,9 @@ suspected, or cosmetic.
All numbers come from the x86_64 emulator with a release build and a local server. Use them to
compare against each other, not as phone numbers.
-- [ ] **PF1 · P1 · M: opening a message takes 1.65–1.87 s from tap to a settled
- screen.**
+- [x] **PF1 · P1 · M: opening a message takes 1.65–1.87 s from tap to a settled
+ screen.** Fixed in `33e303e`, `ac8e3fb`, `1348e7d`, `01fc6fb`, `6ebadf7`, `54b169c`, `7cdebc4`,
+ `b7a5953`; not re-timed on a device yet.
- Where: `src/screens/EmailThreadScreen.tsx:161-199` (a network-first detail fetch even for
cached ids, plus a thread fetch with full bodies), `:641` (the pager mounts 3 panes, each
with its own WebView), `src/components/EmailBodyView.tsx:588-591` (the body is sanitized
@@ -363,8 +382,11 @@ compare against each other, not as phone numbers.
- One open from the Unified Inbox made 12 requests: the body twice, the thread twice,
`Identity/get`, and a forbidden `x:Account/get`.
- **Verified** (numbers and requests); causes suspected.
-- [ ] **PF2 · P1 · M: cold start takes 3.6 / 3.8 / 4.1 s from launch to a settled
- inbox (3 runs).**
+- [x] **PF2 · P1 · M: cold start takes 3.6 / 3.8 / 4.1 s from launch to a settled
+ inbox (3 runs).** Fixed in `604eb39`, `063432b`, `5bcfe1f`, `82ca359`, `b9db376`, `9c2f819`:
+ modules run before the first frame 3,590 → 1,516, Hermes bundle 9.34 → 7.61 MB, a headless push
+ start 202 modules (`npx expo export`). The four hydrations were already awaited together
+ (`85f23df`). Not re-timed on a device yet.
- Measured:
- `am start` reports 451–564 ms to the first frame (the splash).
- Cached rows at about +1.2 s.
@@ -383,7 +405,9 @@ compare against each other, not as phone numbers.
- The headless push task imports the whole app (`index.ts:10`).
- **Verified** (device timings; bundle shares measured by rebuilding the bundle with hermesc and
removing module groups one at a time).
-- [ ] **PF3 · P2 · M: the mail list is heavier than the emulator floor.**
+- [ ] **PF3 · P2 · M: the mail list is heavier than the emulator floor.** Partly:
+ `cb9a741`, `aa25075`, `ebd8dac` fix the inline callbacks, the swipe bands and the per-row date
+ formatters; the swipe drag still runs on the JS thread, and nothing was re-measured.
- Measured over already-loaded rows (14 flings): p50 21 ms, p90 32 ms, p95 38 ms, p99 57 ms.
The same flings in the system Settings list: 17 / 18 / 20 / 36 ms.
- Causes:
@@ -394,20 +418,24 @@ compare against each other, not as phone numbers.
- Row dates build two `Intl` formatters per row (`src/lib/date-format.ts:75-97`).
- The PanResponder drag runs on the JS thread (`src/components/SwipeableRow.tsx:170-172`).
- **Verified** (numbers); causes suspected.
-- [ ] **PF4 · P2 · S: hidden screens keep rendering.**
+- [ ] **PF4 · P2 · S: hidden screens keep rendering.** Partly: `4a5d837` adds
+ `freezeOnBlur` and a count-only badge selector; the list directly under an open message stays live
+ on Fabric, and `fetchMailboxes` still sets a new array on every fetch.
- Where: there is no `enableFreeze` or `freezeOnBlur` (`App.tsx:634`, `index.ts`).
- The tab navigator subscribes to the whole `mailboxes` array to compute one badge
(`App.tsx:127`), and `src/stores/email-store.ts:1855` sets a new array on every mailbox
fetch.
- So every refresh also re-renders the mail list under an open message.
- **Suspected.**
-- [ ] **PF5 · P2 · S–M: every store write re-serializes the whole persisted cache.**
+- [x] **PF5 · P2 · S–M: every store write re-serializes the whole persisted cache.**
+ Fixed in `48825e9`, `e37621b`.
- Where: `src/stores/email-store.ts:1723-1731` (the email cache, with the current folder
stored twice), `src/stores/contacts-store.ts:824-829`,
`src/stores/calendar-store.ts:769-775`. This includes `loading` flips; there is no debounce.
- Opening one unread message causes about 5 full writes.
- **Suspected.** The stringify cost was measured off-device: 1.25 ms for 425 rows in V8.
-- [ ] **PF6 · P2 · S: requests that can be cut, all seen on the wire.**
+- [x] **PF6 · P2 · S: requests that can be cut, all seen on the wire.** Fixed in
+ `d097ae9`, `668e010`, `1c7c2a3`, `018accd`, `3bf2c30`, `1196ad5`, `9fa931c`.
- Every drawer open sends 14 `Email/query` calls for tag counts.
- The Contacts tab re-downloads all cards (58 KB) on every open, although they load at
startup.
@@ -422,7 +450,8 @@ compare against each other, not as phone numbers.
- There is a 30 s `Core/echo` on top of the SSE pings (`App.tsx:581-596`).
- Backgrounding is fine: no requests during 40 s in the background.
- **Verified.**
-- [ ] **PF7 · P2 · S: every list page and every search is 3 sequential requests.**
+- [x] **PF7 · P2 · S: every list page and every search is 3 sequential requests.**
+ Fixed in `d987930`, `4f3940f`, `9c2f819`.
- The chain is `Email/query` → `Email/get` → `Thread/get` (the last one fired after render).
First login adds two `Mailbox/get` and a 500 ms gap.
- Where: `src/stores/email-store.ts:2049-2060`, `src/screens/EmailListScreen.tsx:339-358`.
@@ -430,8 +459,8 @@ compare against each other, not as phone numbers.
`#ids /list/*/threadId` in one request). RN's `src/api/unified-inbox.ts:383-397` already
chains query and get.
- **Verified.**
-- [ ] **PF8 · P2 · M: each keystroke in the composer re-renders the 3,000-line
- screen twice.**
+- [x] **PF8 · P2 · M: each keystroke in the composer re-renders the 3,000-line
+ screen twice.** Fixed in `fc00781`.
- Where: `src/lib/editor-html.ts:160-168` posts the full `innerHTML` on every input and on every
selection change; `src/screens/ComposeScreen.tsx:2276-2277`.
- `htmlToPlainText` and a `JSON.stringify` snapshot also run on every render.
@@ -441,7 +470,9 @@ compare against each other, not as phone numbers.
- Contents: 25.85 MB of unminified dex, 24,621 classes; the ML Kit barcode scanner accounts for
5.83 MB. The UnifiedPush work in progress adds 3,009 classes (Tink, protobuf).
- **Verified** (APK inspection of the published 0.1.62).
-- [ ] **PF10 · P3 · S: slow paths that need large data to show.**
+- [ ] **PF10 · P3 · S: slow paths that need large data to show.** Partly: contacts
+ search (`e6be892`) and bulk read/star (`cb2f03b`) are fixed; upload progress and the offline-cache
+ index writes are unchanged.
- Contacts search re-sorts with `localeCompare` on every keystroke
(`src/stores/contacts-store.ts:923-925`).
- Bulk mark-read and star send N requests against a concurrency limit of 4
@@ -458,12 +489,14 @@ wrongly). Items already listed under Bugs are only cross-referenced here.
### Auth and accounts
-- [ ] **P2 · S · missing: adding a second OAuth account silently signs in the same user again.**
+- [x] **P2 · S · missing: adding a second OAuth account silently signs in the same user again.**
+ Fixed in `ab84578`.
- Where: `src/lib/oauth-native.ts:127-141` (no `prompt=select_account`, no ephemeral browser
session).
- Web: `5771a213`.
- **Suspected.**
-- [ ] **P3 · S · partial: a pasted `…/jmap/session` URL fails discovery with a 404.**
+- [x] **P3 · S · partial: a pasted `…/jmap/session` URL fails discovery with a 404.** Fixed in
+ `bd42206`.
- Where: `src/lib/server-discovery.ts:59-63`.
- Web: `b26c1746` (#971).
- **Suspected.**
@@ -471,37 +504,43 @@ wrongly). Items already listed under Bugs are only cross-referenced here.
### Mail list, folders and search
-- [ ] **P2 · L · diverges: "All folders" search only covers the open folder's account (#1082).**
+- [x] **P2 · L · diverges: "All folders" search only covers the open folder's account (#1082).**
+ Fixed in `ee50418`, `edab181`, `d2066da`.
- Where: `src/stores/email-store.ts:384-389`.
- Web: `lib/unified-mailbox.ts` (`searchAcrossAccounts`, `9ef7068b`).
- Repro: "zephyr" with All folders → `Email/query` on account `c` only → 3 hits. The 5 matches
in the team inbox are missing.
- **Verified.**
-- [ ] **P2 · M · diverges: the tag view and tag badges only cover one account (#1038).**
+- [x] **P2 · M · diverges: the tag view and tag badges only cover one account (#1038).** Fixed in
+ `8d5bced`, `d097ae9`.
- Where: `src/stores/email-store.ts:209` and `:387`, `src/components/SidebarDrawer.tsx:426-436`.
- Web: `stores/email-store.ts` (`fetchTagEmails`, `fedfc89d`).
- Repro: 2 own and 2 team messages tagged Red → the Red view shows 2.
- **Verified.**
-- [ ] **P3 · S · missing: folders created on the phone are not subscribed.**
+- [x] **P3 · S · missing: folders created on the phone are not subscribed.** Fixed in `30aea63`.
- Where: `src/api/email.ts:211-221` sends no `isSubscribed: true`, so IMAP clients hide them.
- Web: `2b186910` (#951).
- **Suspected.**
-- [ ] **P3 · S · diverges: archiving to a role-less "Archives" folder is offered but does nothing.**
+- [x] **P3 · S · diverges: archiving to a role-less "Archives" folder is offered but does nothing.**
+ Fixed in `9f75e70`, `ad1f053`.
- Where: the list matches by name (`src/screens/EmailListScreen.tsx:393-395`), but the store
needs the role or the exact name (`src/stores/email-store.ts:1202-1205`).
- Web: `2b32a011` (#578).
- **Suspected.**
-- [ ] **P3 · S · missing: no unread badge on the app icon.**
+- [ ] **P3 · S · missing: no unread badge on the app icon.** Partly: `25c0883` badges the icon on
+ iOS; Android is left to the launcher (see the fix-pass decisions).
- expo-notifications `setBadgeCountAsync` is already available.
- Web: `hooks/use-app-badge.ts` (`e7d37823`).
- **Suspected.**
-- [ ] **P3 · S · missing: list rows show no attachment chips.**
+- [x] **P3 · S · missing: list rows show no attachment chips.** Fixed in `b1fb2e9`, `edab181` (not
+ yet on Unified Inbox rows).
- Web: `0e1c34cd`.
- **Suspected.**
-- [ ] **P3 · S · missing: no "clear search when switching folders" setting.**
+- [x] **P3 · S · missing: no "clear search when switching folders" setting.** Fixed in `4e64261`.
- Web: `12f01409`.
- **Suspected.**
-- [ ] **P3 · S · diverges: the Unified section shows even when it can't be populated.**
+- [x] **P3 · S · diverges: the Unified section shows even when it can't be populated.** Fixed in
+ `af6290f`.
- Where: `src/components/SidebarDrawer.tsx:639`.
- Web: `f3f0127c` (#843).
- **Suspected.**
@@ -510,34 +549,36 @@ wrongly). Items already listed under Bugs are only cross-referenced here.
### Viewer
-- [ ] **P2 · M · missing: no per-message HTML/plain-text toggle, and reopened drafts are forced into
- the global format.**
+- [x] **P2 · M · missing: no per-message HTML/plain-text toggle, and reopened drafts are forced into
+ the global format.** Fixed in `472c4d8`.
- Where: `src/screens/ComposeScreen.tsx:531-536`. With plain text on, an HTML draft is
flattened on its first autosave.
- Web: `f6ad8a59` (#1022), `components/email/compose-format.ts`.
- **Suspected.**
-- [ ] **P2 · S · missing: sender avatars ignore address-book photos (#54).**
+- [x] **P2 · S · missing: sender avatars ignore address-book photos (#54).** Fixed in `eca3871`.
- Web: `components/ui/avatar.tsx` (looks up the sender's contact photo by email; the photo wins
over favicons and initials).
- Repro: Alice Nguyen's contact photo shows in Contacts; her mail shows "AN" initials.
- **Verified.**
-- [ ] **P2 · S · diverges: the viewer's Spam button is a plain move.**
+- [x] **P2 · S · diverges: the viewer's Spam button is a plain move.** Fixed in `5e42c67`.
- It sends no `$junk`/`$notjunk` flip and doesn't honour trash-and-read.
- Where: `src/screens/EmailThreadScreen.tsx:395-407`. The list and swipe paths are right
(`src/stores/email-store.ts:1058-1146`).
- Web: `150c25ca` (#695).
- **Suspected.**
-- [ ] **P2 · S · diverges: viewer Archive silently does nothing for messages that are not in the
- loaded list.**
+- [x] **P2 · S · diverges: viewer Archive silently does nothing for messages that are not in the
+ loaded list.** Fixed in `33d2674`.
- This covers the unified inbox, notification taps and deep links.
- Where: `src/stores/email-store.ts:1196-1197`.
- Web: `150c25ca`.
- **Suspected.**
-- [ ] **P3 · S · partial: failed actions and sends give no feedback.**
+- [x] **P3 · S · partial: failed actions and sends give no feedback.** Fixed in `9e95273`,
+ `cd911ad`.
- The store actions are called with `void`, and there is no "Email sent" toast.
- Web: `ec67ab99`, `b03a0c1d`, `lib/email-action-toast.ts`.
- **Verified** (no toast after send); failure path suspected.
-- [ ] **P3 · S · missing: body-embedded `cid:` parts typed octet-stream appear as nameless chips.**
+- [x] **P3 · S · missing: body-embedded `cid:` parts typed octet-stream appear as nameless chips.**
+ Fixed in `1d1d6a5`.
- Where: `src/lib/attachment-display.ts:91-104`.
- Web: `1c00812a`.
- **Suspected.**
@@ -545,26 +586,29 @@ wrongly). Items already listed under Bugs are only cross-referenced here.
### Composer
-- [ ] **P2 · S · diverges: replies go out from the default identity unless "Reply from received
- address" is on.**
+- [x] **P2 · S · diverges: replies go out from the default identity unless "Reply from received
+ address" is on.** Fixed in `a0c2bd7`.
- Forward also rewrites the From address, and replies to your own sent mail resolve from the
other party.
- Where: `src/screens/ComposeScreen.tsx:836-845`, `src/lib/reply-identity.ts:46-77,170`.
- Web: `4fc7b8b5`, `lib/reply-identity.ts`.
- **Suspected.**
-- [ ] **P3 · S · missing: a new message in a shared folder doesn't default to that mailbox's
- identity.**
+- [x] **P3 · S · missing: a new message in a shared folder doesn't default to that mailbox's
+ identity.** Fixed in `0ffdcac`.
- Where: `findComposeIdentityId` is ported but never called.
- Web: `129f545a`.
- **Suspected.**
-- [ ] **P3 · S · missing: no exact-address vs same-domain choice for "reply from received".**
+- [x] **P3 · S · missing: no exact-address vs same-domain choice for "reply from received".** Fixed
+ in `2d907ff`.
- Web: `565d5c10` (#1000).
- **Suspected.**
- [ ] **P3 · M · partial: no font-size picker or background colour; 8 text colours instead of 16.**
+ Partly: `548b647` adds the background colour and the 16 text colours; the font-size picker is not
+ ported.
- Where: `src/components/RichTextEditor.tsx:8-26`.
- Web: `7e0a86a6`, `3957b358`.
- **Suspected.**
-- [ ] **P3 · S · diverges: quick replies carry no signature.**
+- [x] **P3 · S · diverges: quick replies carry no signature.** Fixed in `3faac98`.
- Where: `src/components/email/QuickReplyBox.tsx:79-90`.
- **Suspected.**
- [ ] **P3 · S · partial: the undo-send snackbar exists only on the list screen.**
@@ -575,55 +619,58 @@ wrongly). Items already listed under Bugs are only cross-referenced here.
### Calendar
-- [ ] **P2 · S · missing: CalDAV tasks with `COMPLETED`, `NEEDS_ACTION` or `canceled` count as
- pending and toggle the wrong way.**
+- [x] **P2 · S · missing: CalDAV tasks with `COMPLETED`, `NEEDS_ACTION` or `canceled` count as
+ pending and toggle the wrong way.** Fixed in `1b64c14`.
- Web: `5f1d2795` (`normalizeCalendarTask`).
- **Suspected.**
-- [ ] **P3 · L · missing: no free-scrolling month, week or day views (#759) and no setting for them.**
+- [x] **P3 · L · missing: no free-scrolling month, week or day views (#759) and no setting for
+ them.** Fixed in `caf61ec`, `de3d697`.
- Where: `src/screens/CalendarScreen.tsx:89,169-171,360-374`.
- Web: `15a5497a`, `ae403b06`, `hooks/use-scroll-window.ts`.
- **Suspected.**
-- [ ] **P3 · M · missing: the agenda is a fixed 30-day page instead of infinite scroll.**
+- [x] **P3 · M · missing: the agenda is a fixed 30-day page instead of infinite scroll.** Fixed in
+ `71522b9`.
- Where: `src/components/calendar/AgendaView.tsx:46`.
- Web: `60e05c00`.
- **Suspected.**
-- [ ] **P3 · L · diverges: recurring events are always expanded client-side.**
+- [x] **P3 · L · diverges: recurring events are always expanded client-side.** Fixed in `8454889`,
+ `f397ee3`, `96c5a3f`.
- Where: `src/api/calendar.ts:267-304`, `src/lib/recurrence-expansion.ts`.
- This matches the webmail's pre-0.16.20 fallback, but there is no server-side expansion and no
per-occurrence edit through synthetic ids.
- Web: `lib/recurrence-instances.ts`.
- **Suspected.**
-- [ ] **P3 · S · partial: participants aren't deduplicated or matched to contact names, and the
- organizer's status dot is wrong.**
+- [x] **P3 · S · partial: participants aren't deduplicated or matched to contact names, and the
+ organizer's status dot is wrong.** Fixed in `1d755fc`.
- Where: `src/lib/calendar-participants.ts:130-167`.
- Web: `8cbc5bc5`, `6f99c017`.
- **Suspected.**
-- [ ] **P3 · S · diverges: a task's due time shows the raw Europe/Berlin wall time (17:00), while
- events are converted to the device zone.**
+- [x] **P3 · S · diverges: a task's due time shows the raw Europe/Berlin wall time (17:00), while
+ events are converted to the device zone.** Fixed in `6a975b8`, `1a651a9`, `a8e6f6e`.
- **Suspected.**
- Links in event descriptions are fixed on GitHub `main` (`c2584c2`) but not in this checkout.
- Also see B12, B17, B21, B22.
### Contacts
-- [ ] **P2 · S · partial: the vCard FN (`name.full`) is missing or stale on form, "add sender" and
- import writes (#430).**
+- [x] **P2 · S · partial: the vCard FN (`name.full`) is missing or stale on form, "add sender" and
+ import writes (#430).** Fixed in `a734d0a`.
- Where: `src/screens/ContactFormScreen.tsx:288-293`, `src/lib/vcard.ts:575-603`.
- Web: `a210b22e`.
- **Suspected.**
-- [ ] **P3 · S · missing: no sort by last name (#963).**
+- [x] **P3 · S · missing: no sort by last name (#963).** Fixed in `1827748`.
- Where: `src/stores/contacts-store.ts:923-925`.
- Web: `fb24ef5d`.
- **Suspected.**
-- [ ] **P3 · S · missing: a new contact made while viewing address book X lands in the default
- book.**
+- [x] **P3 · S · missing: a new contact made while viewing address book X lands in the default
+ book.** Fixed in `c888e95` (groups: `a3193eb`).
- Where: `src/screens/ContactsScreen.tsx:238,396`.
- Web: `494adb9b`.
- **Suspected.**
### Files
-- [ ] **P2 · S · diverges: sharing with other users is never offered.**
+- [x] **P2 · S · diverges: sharing with other users is never offered.** Fixed in `caea3d0`.
- Where: `src/api/files.ts:30-35` and `src/components/calendar/CalendarShareSheet.tsx:86` gate
on `principals:owner`, which Stalwart advertises nowhere. The session only has `principals`
and `principals:availability`.
@@ -631,44 +678,47 @@ wrongly). Items already listed under Bugs are only cross-referenced here.
- Repro: the per-file menu shows Preview/Share (OS), Download, Move, Duplicate, Rename,
Delete, and no "Share with…".
- **Verified.**
-- [ ] **P3 · M · missing: no in-app file preview.**
+- [x] **P3 · M · missing: no in-app file preview.** Fixed in `409cdb1` (images, text, and PDFs on
+ iOS; Android PDFs, audio and video still go to another app).
- Tapping a file downloads it and opens the OS "Open with" chooser.
- **Verified.**
- Also see B13 (500-node cap). WOPI editing (#425) is listed under open questions.
### Filters and vacation
-- [ ] **P2 · S · missing: "Keep" is still written as `keep;`.**
+- [x] **P2 · S · missing: "Keep" is still written as `keep;`.** Fixed in `f63d739`.
- Saving filters on the phone therefore undoes the webmail fix for allow-list rules.
- Where: `src/lib/sieve/generator.ts:131-132`.
- Web: `62465e1e` (#1027).
- **Suspected.**
-- [ ] **P3 · M · missing: no filters or vacation for shared/group accounts.**
+- [x] **P3 · M · missing: no filters or vacation for shared/group accounts.** Fixed in `a86cd1d`.
- Where: `src/api/sieve.ts:20-23`, `src/api/vacation.ts:26-27`.
- **Suspected.**
### Settings, themes and i18n
-- [ ] **P2 · M · partial: locale catalogs are stale.**
+- [x] **P2 · M · partial: locale catalogs are stale.** Fixed in `55f1ec4`, `008bff2`, `67935d2`.
- Last synced 2026-08-29 (`990cd84`): 2,959 keys against the webmail's 3,065. The German
update changed 59 values.
- `email_viewer.forward_as_attachment` is still English in about 25 locales.
- The 567 RN-only keys are English in every locale (`src/i18n/index.ts:98-100`).
- Fix: run `scripts/sync-locales.mjs` and translate the RN overlay.
- **Suspected.**
-- [ ] **P3 · S · partial: the Reading pane hard-codes English.**
+- [x] **P3 · S · partial: the Reading pane hard-codes English.** Fixed in `2511df1`.
- Labels such as "Message Spacing", "Read Receipts" and "Always send" don't go through `t()`
(`src/components/settings/ReadingSettings.tsx:305-327`).
- **Verified.**
-- [ ] **P3 · S · diverges: plural forms probably fall back to English rules.**
+- [x] **P3 · S · diverges: plural forms probably fall back to English rules.** Fixed in `e6549d3`.
- Where: `src/i18n/format.ts:18-47`. Hermes on Android has no `Intl.PluralRules`, so Russian
shows "2 писем".
- **Suspected.**
-- [ ] **P3 · M · missing: only 4 of the webmail's 7 built-in themes, and no "Flat fields".**
+- [ ] **P3 · M · missing: only 4 of the webmail's 7 built-in themes, and no "Flat fields".** Partly:
+ `7964e64` adds Roundcube Elastic and Aurora Glass; "Flat fields" is not ported (a product
+ decision).
- Where: `src/theme/builtin-themes.ts`.
- Web: `ab67d9b0`, `lib/theme-flat-fields.ts`.
- **Suspected.**
-- [ ] **P3 · M · missing: no settings search.**
+- [x] **P3 · M · missing: no settings search.** Fixed in `46e285b`.
- Web: `lib/settings-search.ts`.
- **Suspected.**
- [ ] **P3 · L · diverges: native still uses Lucide icons; the webmail moved to Tabler (`d7548a4b`).**
@@ -677,27 +727,28 @@ wrongly). Items already listed under Bugs are only cross-referenced here.
### Push
-- [ ] **P1 · S · diverges: tapping a group-mailbox notification opens the message against the wrong
- account.**
+- [x] **P1 · S · diverges: tapping a group-mailbox notification opens the message against the wrong
+ account.** Fixed in `8618abf`.
- The tap payload carries only the registry account, not the JMAP account.
- Where: `src/lib/push-background-task.ts:448-460`, `App.tsx:85-89`.
- Web: `b4f12b67` (#839).
- **Suspected.**
-- [ ] **P2 · S · diverges: `emailPush` is never read back, so the subscription is re-patched on
- every launch.**
+- [x] **P2 · S · diverges: `emailPush` is never read back, so the subscription is re-patched on
+ every launch.** Fixed in `ccaa24e`.
- `using` also lacks the emailpush capability.
- Where: `src/api/push.ts:11` (`PushSubscription/get` with default properties).
- Web: `lib/jmap/client.ts` (explicit properties).
- **Suspected.**
-- [ ] **P2 · S · diverges: revoking a device or disabling one account doesn't stick.**
+- [x] **P2 · S · diverges: revoking a device or disabling one account doesn't stick.** Fixed in
+ `a3d3af7`.
- Setup re-runs for the active account on every launch (HEAD `App.tsx:447-453`).
- **Suspected.**
- UnifiedPush (#44, #48) exists only as uncommitted work in this tree. Also see B18.
### Security
-- [ ] **S1 · P0 · S · missing: the GHSA-w38p fix (read-receipt header injection) was
- never ported.**
+- [x] **S1 · P0 · S · missing: the GHSA-w38p fix (read-receipt header injection) was
+ never ported.** Fixed in `cb6ddd6`.
- Where: `src/lib/mdn.ts:77-81` (`encodeHeaderWord` passes pure-ASCII values, CR/LF
included, through raw), `:111-113`, `:140-141`.
- Web: `lib/mdn.ts` (`stripHeaderControls`, `headerAtom`; `53a6670e`).
@@ -709,7 +760,9 @@ wrongly). Items already listed under Bugs are only cross-referenced here.
- With B7 this becomes zero-click: the malicious message only needs to sit next to one the
user opens.
- **Verified.**
-- [ ] **S2 · P0 · S–M: releases are signed with the public React Native debug key.**
+- [x] **S2 · P0 · S–M: releases are signed with the public React Native debug key.**
+ Fixed in `6f0abb4`, `746572c`, `5237848`; the release key, rotation lineage and secrets still have
+ to be created before the next release (`docs/android-release.md`).
- Where: `android/app/build.gradle:103-125`; `.github/workflows/release-android.yml:69-91` falls
back to it silently when `ANDROID_KEYSTORE_BASE64` is empty; `android/app/build.gradle:98`
(`versionCode 1`).
@@ -721,24 +774,27 @@ wrongly). Items already listed under Bugs are only cross-referenced here.
- Fix: a private key, a workflow that fails without it, v3 key rotation for existing
installs, and a `versionCode` derived from `VERSION`.
- **Verified.**
-- [ ] **P2 · S · diverges: "Sync trusted senders to address book" trusts every contact in every
- book.**
+- [x] **P2 · S · diverges: "Sync trusted senders to address book" trusts every contact in every
+ book.** Fixed in `d89e331`.
- Remote images and tracking pixels then load for any contact, including a spoofed From.
- The toggle switches itself on the first time the Content & Senders pane is opened on a
contacts-capable account (`src/components/settings/ContentSendersSettings.tsx:54-57`).
- Where: `src/components/EmailBodyView.tsx:533-544,562-566`.
- Web: `components/email/email-viewer.tsx` (only the Trusted Senders book counts).
- **Suspected.**
-- [ ] **P2 · S · missing: replying or forwarding loads the remote images the viewer blocked.**
+- [x] **P2 · S · missing: replying or forwarding loads the remote images the viewer blocked.** Fixed
+ in `235f4ea`.
- The editor page has no CSP (`src/components/RichTextEditor.tsx:223-253`,
`src/lib/editor-html.ts:29-35`).
- **Suspected.**
- [ ] **P2 · S · diverges: the webmail-to-app sign-in hand-off puts the password in the
- `bulwarkmobile://…#password=` fragment.**
+ `bulwarkmobile://…#password=` fragment.** Open: needs a one-time code exchange on the webmail
+ side; no app change in this pass.
- Where: `src/lib/oauth.ts:139-146`. The webmail side is `app/(main)/[locale]/login/page.tsx`.
- **Suspected.**
- [ ] **P3 · S · diverges: list and notification favicons are fetched from icons.duckduckgo.com on
- the device.**
+ the device.** Partly: `95c24a4` makes notifications honour the Sender Favicons switch; with it on
+ (still the default) the phone fetches from DuckDuckGo.
- This leaks sender domains and the device IP to a third party.
- Where: `src/lib/avatar-utils.ts:138-139`.
- Web: server-side `/api/favicon`.
@@ -753,34 +809,38 @@ spot-check covered 35 ticks plus the ones re-tested on the device.
### Ticked, but broken
-- [ ] `docs/parity/03-email-viewer.md:107`, read receipts: sent unsanitized, and sent for unseen
- neighbour panes (S1, B7). **Verified.**
-- [ ] `docs/parity/02-mail-list-folders.md:112` and `docs/parity/03-email-viewer.md:15`, pager and
+- [x] `docs/parity/03-email-viewer.md:107`, read receipts: sent unsanitized, and sent for unseen
+ neighbour panes (S1, B7). **Verified.** Fixed in `cb6ddd6`, `5f3f584`.
+- [x] `docs/parity/02-mail-list-folders.md:112` and `docs/parity/03-email-viewer.md:15`, pager and
wrong-message fixes: the body is right, but actions still hit the wrong account and message
- (B3, B5). **Verified.**
-- [ ] `docs/parity/05-calendar.md:268`, task edit/toggle: completing fails (B12). **Verified.**
-- [ ] `docs/parity/08-settings-push-i18n-ui.md:297`, deep links, mailto and share targets: SENDTO,
- shared files and `+` addresses all fail (B9–B11). **Verified.**
-- [ ] `docs/parity/07-filters-vacation-files.md:177` ("Verified at parity: `FileNode/get
+ (B3, B5). **Verified.** Fixed in `5c7301b`, `9c31de2`.
+- [x] `docs/parity/05-calendar.md:268`, task edit/toggle: completing fails (B12). **Verified.**
+ Fixed in `dcdda5e`.
+- [x] `docs/parity/08-settings-push-i18n-ui.md:297`, deep links, mailto and share targets: SENDTO,
+ shared files and `+` addresses all fail (B9–B11). **Verified.** Fixed in `cd4ce9d`, `06e8942`,
+ `ee652d3`.
+- [x] `docs/parity/07-filters-vacation-files.md:177` ("Verified at parity: `FileNode/get
ids:null`"): capped at 500 (B13). Its claim that `/query` drops folders has been outdated since
- Stalwart 0.16.6. **Verified.**
-- [ ] `docs/parity/02-mail-list-folders.md:244`, search all folders (#788): scope is one account
- (#1082 above). **Verified.**
-- [ ] `docs/parity/02-mail-list-folders.md:187`, `$junk`/`$notjunk` flip: the viewer's buttons
- still do a plain move. **Suspected.**
-- [ ] `docs/parity/01-auth-accounts.md:57` and `docs/parity/09-jmap-core-sync-security.md:99`,
- redirect with a dropped Authorization header (B15). **Suspected.**
-- [ ] `docs/parity/04-composer-send.md:12`, draft autosave: loses text offline, and iOS swipe-down
- bypasses the dialog (B16). **Suspected.**
-- [ ] `docs/parity/04-composer-send.md:224`, reschedule: drops Cc and Bcc (B19). **Suspected.**
-- [ ] `docs/parity/05-calendar.md:261`, reminders: cancelled when the view moves (B17).
- **Suspected.**
-- [ ] `docs/parity/06-contacts.md:259`, trusted-senders book: trusts every contact (Security).
- **Suspected.**
-- [ ] `docs/parity/08-settings-push-i18n-ui.md:139` and `:154`, push types and the `emailPush`
- filter: never read back, and breaks for ACL-shared accounts (B18). **Suspected.**
+ Stalwart 0.16.6. **Verified.** Fixed in `4eb8c47`.
+- [x] `docs/parity/02-mail-list-folders.md:244`, search all folders (#788): scope is one account
+ (#1082 above). **Verified.** Fixed in `ee50418`.
+- [x] `docs/parity/02-mail-list-folders.md:187`, `$junk`/`$notjunk` flip: the viewer's buttons
+ still do a plain move. **Suspected.** Fixed in `5e42c67`.
+- [x] `docs/parity/01-auth-accounts.md:57` and `docs/parity/09-jmap-core-sync-security.md:99`,
+ redirect with a dropped Authorization header (B15). **Suspected.** Fixed in `859770b`.
+- [x] `docs/parity/04-composer-send.md:12`, draft autosave: loses text offline, and iOS swipe-down
+ bypasses the dialog (B16). **Suspected.** Fixed in `92b42e4`.
+- [x] `docs/parity/04-composer-send.md:224`, reschedule: drops Cc and Bcc (B19). **Suspected.**
+ Fixed in `e67947a`, `7e598c5`.
+- [x] `docs/parity/05-calendar.md:261`, reminders: cancelled when the view moves (B17).
+ **Suspected.** Fixed in `4b903ed`.
+- [x] `docs/parity/06-contacts.md:259`, trusted-senders book: trusts every contact (Security).
+ **Suspected.** Fixed in `d89e331`.
+- [x] `docs/parity/08-settings-push-i18n-ui.md:139` and `:154`, push types and the `emailPush`
+ filter: never read back, and breaks for ACL-shared accounts (B18). **Suspected.** Fixed in
+ `ccaa24e`, `d8046a8`.
- [ ] `docs/parity/02-mail-list-folders.md:217`, date-locale: the tick claims a `dateLocale` setting
- that doesn't exist. **Suspected.**
+ that doesn't exist. **Suspected.** Open: there is still no `dateLocale` setting.
### Unticked, but done or obsolete
@@ -789,16 +849,16 @@ spot-check covered 35 ticks plus the ones re-tested on the device.
| Item | Where it landed |
|---|---|
| `01:109` remove a non-active account | `6ae21d5`, `490355c` |
- | `01:114` switcher error state | |
- | `01:129` account cap | now 10, `src/lib/account-utils.ts:4` |
- | `01:139` unified shared-account filter | |
- | `02:163` permanent-delete confirm | `src/screens/EmailThreadScreen.tsx:361-386` |
- | `02:168` "never" mark-as-read | |
- | `04:256` mailto/share intents | Android; broken per B9–B11 |
- | `06:200` add sender to contacts | |
- | `06:281` auto-trust reply recipients | |
- | `07:164` quota | shown in Account settings |
- | `09:131` contact state changes for shared accounts | |
+ | `01:114` switcher error state | `6ae21d5` |
+ | `01:129` account cap | now 10, `src/lib/account-utils.ts:4` (`6ae21d5`) |
+ | `01:139` unified shared-account filter | `6ae21d5` |
+ | `02:163` permanent-delete confirm | `src/screens/EmailThreadScreen.tsx:361-386` (`d2ed27f`) |
+ | `02:168` "never" mark-as-read | `d2ed27f` |
+ | `04:256` mailto/share intents | Android; broken per B9–B11 (`5802cae`; fixed in `cd4ce9d`, `06e8942`, `ee652d3`) |
+ | `06:200` add sender to contacts | `d2ed27f` |
+ | `06:281` auto-trust reply recipients | `5b72c4d` |
+ | `07:164` quota | shown in Account settings (`490355c`) |
+ | `09:131` contact state changes for shared accounts | `26a34d5` |
- **Obsolete (drop them):**
- `05:91` natural-language event input: the webmail has no parser.
- `06:294` print contact: not applicable on mobile.
@@ -815,12 +875,12 @@ spot-check covered 35 ticks plus the ones re-tested on the device.
| Issue | Status after this audit |
|---|---|
-| #57 scheduled sending unsupported | Root cause found and reproduced (B8). |
-| #54 contact photos not used as avatars | Reproduced (Viewer, "sender avatars"). |
+| #57 scheduled sending unsupported | Root cause found and reproduced (B8). Fixed in `e28e8b4`. |
+| #54 contact photos not used as avatars | Reproduced (Viewer, "sender avatars"). Fixed in `eca3871`. |
| #50 crash opening any mail | Not reproduced on 0.1.62; likely fixed by `f228257`. Ask the reporter, then close. |
| #47 account security not accessible | Fixed: Security → Account Security works on 0.16.23. Close. |
| #46 email preview shows CSS | Not reproduced: HTML-only mail with `