Repository navigation
fix(imap): normalize subjects before creating reply drafts - #22
Draft
MakerFlyFly wants to merge 4 commits into
Draft
MakerFlyFly wants to merge 4 commits into
MakerFlyFly wants to merge 4 commits into
Conversation
Unfold and decode the shared Subject summary before reply-prefix handling. Replace line breaks produced by encoded words with spaces so draft headers remain single-line. Cover folded, encoded and empty subjects with offline read and draft round-trip regressions. Generated-by: Codex (GPT-6)
Decode RFC 2047 subjects without constructing a header object on the normal path. Normalize all line separators rejected by EmailMessage, retain raw 8-bit compatibility, and cover all four read operations. Keep subjects intact and leave address parsing and compat32 unchanged. Generated-by: Codex (GPT-6)
Skip Subject decoding above 8192 unfolded characters to protect folder scans while retaining the entire header text. Normalize raw separators before decoding and tolerate NUL-bearing charset names locally. Cover both decoder paths, the budget boundary, and all operations through the shared CLI executor in the presence of unrelated malformed mail. Generated-by: Codex (GPT-6)
Return the already normalized Subject when RFC 2047 decoding fails, removing a redundant separator substitution without changing behavior. Add four checks for oversized draft structure and malformed base64 fallback, preserving the agreed 8192-character decoding budget. Generated-by: Codex (GPT-6)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Folded root subjects prevented IMAP reply drafts, and RFC 2047 encoded reply prefixes could produce
Re: Re: ….Normalize the shared Subject before reply-prefix handling, retaining continuation spaces/tabs and the existing draft/threading rules.
Use lightweight standard-library decoding for ASCII headers, with a compatibility path for mixed raw 8-bit text. Normalize separators before and after decoding, tolerate invalid charset names locally, and return the already normalized text when decoding raises
HeaderParseError.Decode at most 8192 characters of unfolded Subject text: count after removing raw CR/LF, before decoding or separator normalization. Exactly 8192 still decodes; longer values bypass both decoders and retain the complete unfolded text with separator normalization. This is a Magpie resource tradeoff, not a protocol header-size limit. It introduces no truncation, suffix, or public configuration.
All four read operations (
list_recent_threads,read_thread,list_drafts,list_sent_since) return unfolded, decodedsubjectvalues within the budget; oversized values can retain RFC 2047 transport encoding. CLI arguments and JSON field names/types remain compatible. Oversized subjects can degrade decoded display, prefilter matching, and reply-prefix deduplication. The budget covers summary decoding, not the runtime of the existing draft serializer.Type of change
tools/mail-source/imap)Review disposition
str.splitlines(), including separators produced by charset decoding.(LookupError, ValueError)only around charset conversion and retain UTF-8 replacement fallback.UnicodeErroris already aValueError. Synthetic-byte reproduction is not a claim that a compliant production IMAP server returns NUL.HeaderParseErrorRe:; this is not a regression from the upstream baseline.Test plan
9de8b1f8; the complete final suite passes there as well. This revision simplifies one production line and strengthens coverage.In-Reply-To,References, andsent: false.prek run --all-files --skip workspace-pytest.prek run workspace-pytest --from-ref audit/h09-base --to-ref HEAD.pre-first-pr-check: all categories pass, zero blocking/advisory items.All mail tests use synthetic data and FakeMailbox. POSIX hooks and the hooked commit ran in an isolated Linux checkout; its exact Git tree was matched to the Windows staged tree before import.
Performance boundary and remaining serializer cost
The timed probes measure
parse_messageplussummarisefor summaries and CLI draft creation for drafts. At exactly 8192 characters, the Python 3.12 observations were 0.041s for unterminated ASCII words and 0.034s for mixed raw 8-bit text; at 8193, both returned complete text in about 0.003s. Timings vary with runtime and scheduling.A separate, same-host Python 3.12 comparison sends 20,000 unterminated encoded-word openers (140,000 characters) through the full draft path, with a 30-second timeout per child:
dcfe0ad59de8b1f872a05d08All three save a complete draft and return exit 0. This remains an existing serializer limitation for the targeted draft; the summary budget does not remove that cost. These single-run observations are not performance guarantees.
RFC-AI-0004 compliance
Linked issues
Related issue: #21.
Notes for reviewers
Fork audit only:
fix/imap-subject-normalizationtargetsaudit/h09-baseatdcfe0ad562b55d4df41bc69aee1f0f8289ee205b. Current head is72a05d0879503d512b6db2c50445da81ee15d84c.The four commits change only the Subject implementation and its tests. Published history, the audit baseline, and fork
mainare retained. No upstream issue or PR has been opened, and this PR remains a draft for human audit.CI on the revised head passed: tests, prek, rat, Actions analysis, and labeler. The actual
pytest (imap)job andRun pyteststep succeeded; prek's normal checks, whole-repository lychee, and Claude Code mods steps also succeeded. Final code-check snapshot: 2026-10-25 14:17 UTC. The committed tree passed all local Linux gates above; earlier cancelled checks are not used as validation.Gen-AI disclosure
AI-generated with Codex (GPT-6), following the AI contribution policy.
Published as a draft for human audit; no human review is claimed. All four commits carry
Generated-by: Codex (GPT-6).