Skip to content

mailcore: let the engine fetch HTML bodies (#1901) - #1904

Merged
yeroo merged 4 commits into
mainfrom
issue-1901-mailcore-let-the-engine-fetch-ht
Nov 5, 2026
Merged

yeroo merged 4 commits into
mainfrom
issue-1901-mailcore-let-the-engine-fetch-ht

Conversation

@yeroo

@yeroo yeroo commented Nov 5, 2026 •

Copy link
Copy Markdown
Owner

What and why

SyncCommand::FetchBody always asked Graph for plain text (Prefer: outlook.body-content-type="text"), so a graphical client embedding the engine got every message as text. Now:

  • sync::engine::EngineOptions { body_format } (BodyFormat::{Text, Html}, default Text) is chosen at spawn: spawn runs the default (lookxy unchanged), spawn_with_options takes one. Html sends no Prefer, so each body comes back native (html, or text for a plain-text message).
  • bodies.fetched_as (additive migration) records the format FetchBody asked for, not the content type that came back, so a native-text answer to an Html ask is not refetched forever.
  • Store::get_body_for(id, format) is the embedder's gate: the stored body when it satisfies the format, else None (send one FetchBody). Old rows from the text-only engine read as "asked for text", so an existing store upgrades one message per open. A draft's body with no recorded format (local, createReply, or a legacy compose edit) satisfies both formats, so a refetch is never invited over unsynced edits (draft = is_draft, or filed under Drafts / the local Drafts sentinel, since delta does not select isDraft).
  • update_draft_fields now also writes content_type = 'html' and clears fetched_as on conflict (it used to keep a fetched-as-text draft's text type over html content).
  • Inline attachments' contentId / isInline were already stored and fetchable (FetchAttachments, FetchInlineImage); a test now pins it under an Html engine. Reply/forward drafts unchanged.

Closes #1901

Testing

  • cargo fmt --check, cargo clippy -p mailcore -p lookxy --all-targets -- -D warnings: clean
  • cargo test -p mailcore (317 passed), cargo test -p lookxy (333 passed). Only lookxy depends on mailcore.
  • New fake-server engine tests: the default engine sends the text Prefer and records text; an Html engine sends none and stores html; a legacy text body is fetched as html exactly once through the gate; a native-text answer is not fetched again; inline attachments store contentId; a plain-text reply draft is not a refetch candidate. Store tests in store::body_format_tests cover the rule, the migration and the draft cases. Mutation checks (by hand): recording the returned content type, or the old draft conflict arm, or dropping the draft rule each fail a test.

Review record

  • Plan agreed in one round (four test amendments from the implementer).
  • revmux r1: 3 Minor, 0 Major — fixed: legacy edited drafts could read as refetch candidates under Html (data loss), same for plain-text createReply drafts, and the store tests moved to their own module.
  • review stopped: round 1 had no Major; all findings fixed
  • Follow-up: mailcore: full-text search indexes HTML markup of HTML bodies #1905 (FTS indexes html bodies' markup under an Html store).

yeroo and others added 4 commits November 5, 2026 12:00
An Html engine gets each body native, so a plain-text message comes back
as text: content_type alone cannot tell "asked for html, got text" from
"fetched as text by an older engine". bodies.fetched_as records what was
asked (put_fetched_body), NULL for local writes and pre-column rows, and
get_body_for(id, format) answers whether the stored body satisfies a
format, so an embedder refetches each message once, not on every open.

update_draft_fields now also makes the body html with no fetch format:
compose writes html, and a draft fetched as text kept content_type text
over html content, which would have invited a refetch over the edits.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
EngineOptions { body_format } is chosen at spawn (spawn_with_options);
spawn keeps Text, so lookxy is unchanged. Html sends no Prefer header and
stores each body with the format it asked for. Tests pin the Prefer
header per format, the one-time upgrade of a text-stored body, no refetch
of a native-text answer, and inline attachments' contentId under Html.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A body with no recorded fetch format now satisfies both formats when it
belongs to a draft. Under Html, a plain-text createReply draft (stored as
text) and a draft an older update_draft_fields left as html content under
content_type 'text' read as "asked for text", inviting a FetchBody over
unsynced edits that a queued SaveDraft would then push back as Graph's
old body (m1, m3). A draft is is_draft or filed in the Drafts folder
(synced or the local sentinel): a complete delta item resets is_draft to
0, as isDraft is not in MESSAGE_SELECT, so the flag alone would not last
past a sync. A body FetchBody recorded keeps the strict rule.

The #1901 store tests move to their own body_format_tests module (m2).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@codecov-commenter

Copy link
Copy Markdown

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

❌ Patch coverage is 96.39640% with 20 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
mailcore/src/sync/engine.rs 93.21% 19 Missing ⚠️
mailcore/src/store/mod.rs 99.62% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

@yeroo
yeroo merged commit 7e4e9a9 into main Nov 5, 2026
27 of 28 checks passed
@yeroo
yeroo deleted the issue-1901-mailcore-let-the-engine-fetch-ht branch November 5, 2026 09:01
@yeroo

yeroo commented Nov 5, 2026

Copy link
Copy Markdown
Owner Author

Merged automatically (auto-merge is on for this checkout): review stopped: round 1 had no Major; all findings fixed; merge rounds: 1 CI rerun (a timing flake in the untouched set_automatic_replies_patches_and_emits_updated on windows-latest, green on rerun); whole suite green on 1853953 (fmt, clippy, cargo test -p mailcore -p lookxy: 650 tests); merge-check ok.

Follow-ups: #1905 (FTS indexes html bodies' markup under an Html store), its own issue. No leftovers checklist; nothing deferred or disputed from review.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

mailcore: let the engine fetch HTML bodies (FetchBody always asks for plain text)

2 participants