Evaluation of the changes required to wesm/msgvault#301 ("feat: expose HTML email content and inline MIME parts via API") once wesm/msgvault#304 (identities-collections-dedup) is merged into upstream main.
Zero textual conflicts. Every overlapping file edits a different function or different line range:
| File | PR301 hunks | #304 hunks | Conflict? |
|---|---|---|---|
internal/query/duckdb.go |
@@ -1448,6 @@ (new GetMessageRaw between GetAttachment and existing methods) |
@@ -297 -653 -857 -1118 -1469 -1558 -1667 -2309 -2466 @@ |
No — PR301's insertion sits between #304's 1118 and 1469 hunks |
internal/query/sqlite.go |
@@ -837,6 @@ (new GetMessageRaw after GetAttachment) |
@@ -189 -263 -885 -1003 -1141 -1290 -1431 @@ |
No — PR301's insertion is before #304's 885 hunk; clean rebase |
internal/query/engine.go |
@@ -27 @@ (interface addition) |
untouched on #304 | No |
internal/query/querytest/mock_engine.go |
@@ -36 -121 @@ |
untouched on #304 | No |
internal/query/shared.go |
@@ -183,6 @@ (new getMessageRawShared helper) |
untouched on #304 | No |
internal/query/sqlite_crud_test.go |
@@ -1 -1131 @@ |
untouched on #304 | No |
internal/remote/engine.go |
@@ -569,6 @@ (new stub method) |
@@ -589 @@ |
No — different functions, 20 lines apart |
internal/api/handlers.go, handlers_test.go, server.go |
new endpoints, new test cases | untouched on #304 | No |
After rebase, line numbers will adjust automatically; no <<<<<<< markers expected.
PR301's GetMessageRaw reads from message_raw via direct PK lookup (WHERE message_id = ? in getMessageRawShared). This is exactly the pattern #304's contributor guidelines mandate for body/raw access ("Only access message_bodies via direct PK lookup … when displaying a single message detail view"). PR301 is compliant by construction. No action needed.
PR301 adds GetMessageRaw(ctx context.Context, id int64) ([]byte, error) to the query.Engine interface and provides implementations in sqlite, duckdb (delegates to sqlite), remote/engine (returns ErrNotSupported), and querytest/mock_engine. #304 doesn't add or modify any methods on this interface — it only changes MessageFilter/AggregateOptions/StatsOptions struct shapes. The two changes are layered, not stacked. No action needed.
PR301 adds GET /messages/{id}/inline/{cid} which:
- Loads message detail (
engine.GetMessageDetailor store fallback). - Calls
engine.GetMessageRawfor the same ID. - Parses MIME, finds the part with matching CID, returns the decoded bytes.
#304 adds deleted_at IS NULL to Search, buildSearchQueryParts, buildSearchConditions, etc. — but these are search/list paths. GetMessageDetail and GetMessageRaw are single-message-by-PK lookups; #304 does not filter these by deleted_at. This means a client can still fetch a dedup-hidden message detail and its inline images by direct ID — which is the right behavior (the message hasn't been hard-deleted, just hidden from search). No action needed for PR301, but worth confirming the test suite doesn't pin "deleted message detail returns 404" behavior.
PR301's GET /messages/{id} and GET /messages/{id}/inline/{cid} are PK-keyed and ignore source/account scope. #304's appendSourceFilter only applies to list/search paths. No interaction.
When the engine path is available, GET /messages/{id} returns body and body_html from the engine's MessageDetail. The MessageDetail struct already carries BodyText and BodyHTML as separate fields on main, so PR301 only changes the API serialization, not the underlying read query. #304 doesn't redefine MessageDetail. No action needed.
PR301's content-type filter (rejects SVG/XHTML, applies Cache-Control: private, requires IsInline flag) is purely API-layer logic that doesn't intersect with #304's store/query/scope changes. No action needed.
git rebase origin/main— should apply cleanly with zero textual conflicts.- Run
make test. PR301's existing test coverage (CRUD round-trip onGetMessageRaw, inline endpoint with PNG/SVG/XHTML/CID-not-found/no-engine/message-not-found, engine-pathbody_html) should still pass. - (Optional) Add a coverage test: confirm
GET /messages/{id}andGET /messages/{id}/inline/{cid}both still work for dedup-soft-deleted messages (rows wheredeleted_at IS NOT NULL). This is a property of the PK-lookup design, not something PR301 needs to change, but worth pinning so it doesn't regress later. - (Optional, not required for merge) When #304 is final, check
MessageDetailfor any new fields (e.g.DeletedAt,DeletedFromSourceAt) and decide whether to surface them in the API JSON response. Default behavior — fields exist on the struct but aren't serialized — is acceptable.
Effectively orthogonal merge. PR301 operates on a different axis from #304: it adds a single-message HTML/inline read path, while #304 reshapes scope, identity, and dedup of the message corpus. They share three files (duckdb.go, sqlite.go, remote/engine.go) but in entirely different functions, and they share zero semantic surface.
Total fix surface after rebase: 0 lines required. Optional coverage additions are recommended but not blocking. This is the cleanest merge in the queue alongside PR284 (which has zero textual conflicts but does need ~15 lines of identity wiring).