fix(drafts): guard immutable draft replacement and preserve originals #45

Merged
jlxq0 merged 4 commits from fix/safe-draft-replacement-20261029 into main 2026-10-28 17:37:06 +00:00
Owner

update_draft previously patched immutable JMAP content and accepted Email IDs without draft ownership or concurrency guards. It now requires the saved account state and draft revision, creates and verifies a replacement, then conditionally moves the verified original to Trash and reads both back. It never permanently deletes or submits email. Partial/unknown outcomes carry recovery IDs and permanently consume the workflow generation; clients must serialize updates and reconcile manually before accepting a new generation.

save_draft returns verified ID/state/revision; read-only get_draft_version supports safe conflict checks. The server accepts any account-owned plain draft, so autonomous clients must separately track their own created IDs and unchanged generation revisions. Blank/foreign From fields, sent/moved mail and unsupported rich/MIME content are refused. RFC line endings, empty subject text and bracketed reply IDs are normalized before writing; the same plain part in both text/HTML display lists is supported. Existing update clients must migrate to required preconditions and the newly returned Email ID.

Rebased onto separately reviewed/green/merged dependency PR46. Exact current head 2e19f3e03b8489835b105c767bfd6fad6fa81add: pinned Rust1.93.0 fmt, clippy(all targets/features, locked, -Dwarnings),274tests pass. Nine deliberate guard-removal mutations each fail named handler tests; restored source hash matches current module. Cargo audit0vulnerabilities (existing allowed chacha20 yankedwarning), deny bans/licenses/sources and checksum-pinned OSV2.5.1 pass. Native read-only Stalwart metadata corrected fixture shape; it does not prove live writes.

Fresh independent Claude Code print review (claude-opus-5-5) APPROVE exact 2e19f3e03b8489835b105c767bfd6fad6fa81add; CI158 all required and Docker jobs success. The reviewer read source independently and did not run tests; exact-headCI supplies execution evidence. Source-only: no release/tag, credential/scope/profile changes, production mailbox writes or deployment. Release remains gated on Lucy caller/allowlist migration with a serialized durable created-ID/generation ledger and the disposable live Stalwart acceptance matrix, including normalization, race/unknown outcomes and Trash retention. Existing Lucy-own sends are outside this draft workflow and unchanged.

`update_draft` previously patched immutable JMAP content and accepted Email IDs without draft ownership or concurrency guards. It now requires the saved account state and draft revision, creates and verifies a replacement, then conditionally moves the verified original to Trash and reads both back. It never permanently deletes or submits email. Partial/unknown outcomes carry recovery IDs and permanently consume the workflow generation; clients must serialize updates and reconcile manually before accepting a new generation. `save_draft` returns verified ID/state/revision; read-only `get_draft_version` supports safe conflict checks. The server accepts any account-owned plain draft, so autonomous clients must separately track their own created IDs and unchanged generation revisions. Blank/foreign From fields, sent/moved mail and unsupported rich/MIME content are refused. RFC line endings, empty subject text and bracketed reply IDs are normalized before writing; the same plain part in both text/HTML display lists is supported. Existing update clients must migrate to required preconditions and the newly returned Email ID. Rebased onto separately reviewed/green/merged dependency PR46. Exact current head `2e19f3e03b8489835b105c767bfd6fad6fa81add`: pinned Rust1.93.0 fmt, clippy(all targets/features, locked, -Dwarnings),274tests pass. Nine deliberate guard-removal mutations each fail named handler tests; restored source hash matches current module. Cargo audit0vulnerabilities (existing allowed chacha20 yankedwarning), deny bans/licenses/sources and checksum-pinned OSV2.5.1 pass. Native read-only Stalwart metadata corrected fixture shape; it does not prove live writes. Fresh independent Claude Code print review (claude-opus-5-5) APPROVE exact `2e19f3e03b8489835b105c767bfd6fad6fa81add`; [CI158](https://forge.oddie.app/jlxq0/jmap-mcp/actions/runs/158) all required and Docker jobs success. The reviewer read source independently and did not run tests; exact-headCI supplies execution evidence. Source-only: no release/tag, credential/scope/profile changes, production mailbox writes or deployment. Release remains gated on Lucy caller/allowlist migration with a serialized durable created-ID/generation ledger and the disposable live Stalwart acceptance matrix, including normalization, race/unknown outcomes and Trash retention. Existing Lucy-own sends are outside this draft workflow and unchanged.
fix(drafts): replace immutable mail with guarded recoverable updates
Some checks failed
CI / tag-ancestry (pull_request) Successful in 2s
CI / cargo (pull_request) Failing after 2m13s
CI / docker (pull_request) Has been skipped
0986fbb4d3
fix: preserve draft recovery and validate native readback shapes
Some checks failed
CI / tag-ancestry (pull_request) Successful in 2s
CI / cargo (pull_request) Failing after 1m58s
CI / docker (pull_request) Has been skipped
1bc00e8c6d
test: lock draft serialization and replacement keyword contracts
Some checks failed
CI / cargo (pull_request) Failing after 51s
CI / tag-ancestry (pull_request) Successful in 2s
CI / docker (pull_request) Has been skipped
86e2a8c984
jlxq0 force-pushed fix/safe-draft-replacement-20261029 from 86e2a8c984
Some checks failed
CI / cargo (pull_request) Failing after 51s
CI / tag-ancestry (pull_request) Successful in 2s
CI / docker (pull_request) Has been skipped
to 2e19f3e03b
All checks were successful
CI / cargo (pull_request) Successful in 2m1s
CI / tag-ancestry (pull_request) Successful in 2s
CI / docker (pull_request) Successful in 58s
2026-10-28 17:27:19 +00:00
Compare
jlxq0 merged commit f98cfadf2d into main 2026-10-28 17:37:06 +00:00
Sign in to join this conversation.
No reviewers
No labels
waiting-on-julian
No milestone
No project
No assignees
1 participant
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
jlxq0/jmap-mcp!45
No description provided.