feat(email): split email-send from the full email provider - #17
Conversation
Auto-committed-on: dragonfly Co-authored-by: Medulla <medulla@tinyhumans.ai>
Auto-committed-on: dragonfly Co-authored-by: Medulla <medulla@tinyhumans.ai>
Auto-committed-on: dragonfly Co-authored-by: Medulla <medulla@tinyhumans.ai>
Auto-committed-on: dragonfly Co-authored-by: Medulla <medulla@tinyhumans.ai>
Auto-committed-on: dragonfly Co-authored-by: Medulla <medulla@tinyhumans.ai>
Auto-committed-on: dragonfly Co-authored-by: Medulla <medulla@tinyhumans.ai>
Auto-committed-on: dragonfly Co-authored-by: Medulla <medulla@tinyhumans.ai>
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (4)
Included review availability: Your plan provides up to 1 included review per hour; 0 remain after this review. Important Approval pendingCodeRabbit has no unresolved comments, but it has not reviewed the latest commit. Use the checkbox below to review the latest commit. CodeRabbit will approve the changes if it finds no blocking issues.
📝 WalkthroughWalkthroughThe email feature is split into SMTP-only and full IMAP receive plus SMTP send modes. Receive code and state compile only with ChangesEmail feature split
Estimated code review effort: 3 (Moderate) | ~20 minutes Merge Risk: ⚪ Minimal · up to The PR separates send-only email support from receive functionality and adds focused validation for the supported feature combinations; no actionable merge-blocking risk remains beyond normal checks and review. Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
Full details: Docstring CoverageExplanation Docstring coverage is 68.75% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 16 functions across 2 files. (2 skipped: 2 unsupported.) ✨ Finishing Touches 💡 2📝 Generate docstrings 💡
🛠️ Fix failing CI checks 💡
Comment |
There was a problem hiding this comment.
Requesting changes: 1 lane(s) blocking, worst finding is high.
Fix or reply to the findings below and push. The next review clears this automatically once they are gone — you should not need to dismiss anything by hand.
$0.0148 · 122,972 in / 4,352 out · 30,306 cached (25%) · openrouter/openai/text-embedding-3-small, deepseek/deepseek-v4-flash, z-ai/glm-5.2 · 342 embedded
critique: $0.0066 · 53,691 in / 2,917 out · 9,057 cached (17%) · deepseek/deepseek-v4-flash, z-ai/glm-5.2
security: $0.0055 · 46,505 in / 857 out · 15,331 cached (33%) · deepseek/deepseek-v4-flash, z-ai/glm-5.2
tests: $0.0012 · 15,053 in / 124 out · 0 cached (0%) · deepseek/deepseek-v4-flash
description: $0.0014 · 7,723 in / 454 out · 5,918 cached (77%) · z-ai/glm-5.2
How this change flows2 changed behaviours across 8 relationships. 6 surrounding behaviours are shown (60 graph nodes walked). 40 further behaviours left out to keep the diagram readable. flowchart LR
n0["ParsedEmail<br/>changed<br/>1 finding"]:::blocking
n1["yuanbao<br/>changed<br/>2 findings"]:::blocking
n2["Result"]:::impacted
n3["full_dm_dispatch"]:::impacted
n4["new"]:::impacted
n5["fetch_unseen"]:::impacted
n6["process_unseen"]:::impacted
n7["run_idle_session"]:::impacted
n3 -->|uses| n1
n5 -->|uses| n0
n5 -->|uses| n2
n5 -->|calls| n4
n6 -->|uses| n2
n6 -->|calls| n5
n7 -->|uses| n2
n7 -->|calls| n6
classDef changed fill:#0d4429,stroke:#238636,color:#e6edf3
classDef impacted fill:#161b22,stroke:#6e7681,color:#c9d1d9
classDef flagged fill:#5a1e02,stroke:#d93f0b,color:#ffffff
classDef blocking fill:#67060c,stroke:#f85149,color:#ffffff
Green: changed behaviour. Grey: surrounding behaviour. Arrows name the call, use, implementation, or test relationship. Orange: has findings. Red: has a finding that blocks the merge. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: fac79caaa0
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Split the existing `email` feature into a lighter `email-send` variant that only requires SMTP dependencies, allowing consumers that only need to send email to avoid pulling in the heavier IMAP and mail-parser crates. The `EmailChannel` type is now re-exported under the `email-send` feature gate, and the compile-time smoke test has been updated to verify the export works with the new feature name. Auto-committed-on: dragonfly Co-authored-by: Medulla <medulla@tinyhumans.ai>
Auto-committed-on: dragonfly Co-authored-by: Medulla <medulla@tinyhumans.ai>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: e820acac1f
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Splits the
emailfeature into a send-only half and the full provider, so a host that only ever delivers mail stops linking the IMAP receive stack.Why
This is a prerequisite for moving OpenHuman's channels onto the TinyChannels bus module, and without it that move sheds almost nothing.
OpenHuman's
voicefeature requirestinychannels/emailfor one reason:voice/audio_toolkit/ops.rsemails a generated podcast as an attachment. It uses exactlyEmailChannel::new(cfg)andsend_message(msg)— inherent methods, never theChanneltrait, never a mailbox. Butemailwas one feature, so a voice-enabled build linked the whole IMAP receive stack it could never call.Measured on OpenHuman's product profile (
dep-sim.py, cross-checked against an independent reachability walk overcargo tree --prefix depth— both agree):async-imap+mail-parser(receive)prost(lark)tinychannelsroot edge entirelySo ~17 of the 18 names attributable to this crate are the two provider features, not the crate itself. Without this split, a voice-enabled host keeps async-imap + mail-parser no matter where the channel providers run, and the bus move sheds only the 4 lark names.
At this crate's own level the split is 174 → 156 packages (−18) for
email→email-send: async-channel, async-imap, concurrent-queue, crossbeam-utils, equivalent, event-listener, event-listener-strategy, hashify, imap-proto, indexmap, mail-parser, minimal-lexical, parking, pin-project, pin-project-internal, pin-utils, self_cell, stop-token.What is gated
Everything lives in
src/providers/email_channel.rsand the two halves barely touch:email-send, lettre) —create_smtp_transport,send_message,build_plain_message,build_message_with_attachment.email, async-imap + mail-parser) —connect_imap,fetch_unseen,wait_for_changes,listen_with_idle,run_idle_session,process_unseen,extract_sender,extract_text,ParsedEmail,IdleWaitResult, and theseen_messagesdedupe field.impl Channel for EmailChannelis gated on the fullemailfeature. A send-only build cannotlisten, so advertising the trait would promise a half-working channel. Nothing is lost:voiceuses the inherent methods.The module crate is unaffected —
tinychannels-modulealready requestsfeatures = ["email", "lark", "whatsapp-web"]and still gets the complete provider.A hole in the existing CI matrix, found while doing this
The
--features email/--features larklanes run at the workspace root, wheretinychannels-moduledepends on this crate withfeatures = ["email", "lark", "whatsapp-web"]. Cargo unifies those in, so those lanes have really been all-features builds and could never observe a gate being off. My firstcargo check --features email-send"passed" for exactly that reason, andcargo treestill showed async-imap.The three lanes added here use
-p tinychannels --no-default-features, which is what makes them test the surface they name. I have deliberately not retrofitted the existing lanes in this PR — that is a separate change with its own risk of surfacing unrelated breakage, and worth doing on its own.Tests
send_only_testsinemail_channel.rs, gated#[cfg(all(test, feature = "email-send", not(feature = "email")))], pins the three items OpenHuman's voice path reaches for. This is the half a compile check cannot state on its own: the crate builds either way, so nothing would otherwise notice the send path growing a dependency on the receive half.Verified locally, exit codes checked individually rather than chained:
The scoped clippy run caught four real items a plain
cargo checkdid not — unusedanyhow!,async_trait,Duration, and aseen_messagesfield never read in a send-only build. All are now gated.Follow-up
Consumer side in OpenHuman:
voicemoves totinychannels/email-send, andchannelsdropstinychannels/emailonce the providers run in the module.Commits are the auto-commit hook's granular checkpoints, kept as-is by preference; the reasoning is here rather than squashed into one message.
Summary by CodeRabbit
New Features
Bug Fixes