Skip to content

Adopt SwiftMail 1.10.0 experimentally - #426

Merged
rajzik merged 6 commits into
mainfrom
codex/issue-178-adopt-swiftmail
Aug 10, 2026
Merged

rajzik merged 6 commits into
mainfrom
codex/issue-178-adopt-swiftmail

Conversation

@gipity-bot

@gipity-bot gipity-bot Bot commented Aug 10, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • pin SwiftMail 1.10.0 at resolved commit c907f871bb23812895274f4c7ae17bf343171c1e
  • add an experimental MailEngine implementation for TLS/authentication, metadata paging, body-part fetches, IDLE, safe UID copy/move, Sent append, and SMTP outcome classification
  • keep externally distributed Release builds fail-closed until provider certification in Certify SwiftMail on iCloud Mail and Fastmail before release #280, with focused tests and qualification documentation

Validation

  • mise exec -- zsh scripts/check-apple-lint.zsh
  • focused ExperimentalSwiftMailEngineTests on iPhone 17 simulator D6D43A14-3B1A-41B6-B25A-0DFCA1BFB089
  • required Debug Apple test pass excluding the separately run mixed-connection and Release-only performance fixtures
  • isolated mixed-connection fixture
  • Release-only cached-presentation performance fixture with TESTING CI_PERFORMANCE_BUDGET
  • Mac Catalyst Debug build with code signing disabled
  • plutil -lint apps/unwired-mail/unwired-mail.xcodeproj/project.pbxproj
  • git diff --check

All commands passed. Xcode emitted existing Swift 6 concurrency and unused-result warnings outside this change.

Remaining human actions

No human action is required before this experimental change can merge or issue #178 can close. The protected iCloud Mail and Fastmail certification tracked in #280 must still be completed before SwiftMail can be enabled in an externally distributed Release build.

Closes #178


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.

@coderabbitai

coderabbitai Bot commented Aug 10, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

You’ve reached a temporary PR review limit under our Fair Usage Limits Policy.

Your recent review volume is higher than typical usage, so adaptive limits are currently applied.

Next review available in: 13 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 91e0671d-ee6b-464b-9d17-884fcad26c21

📥 Commits

Reviewing files that changed from the base of the PR and between efa05c1 and 86429ee.

📒 Files selected for processing (4)
  • apps/unwired-mail/unwired-mail.xcodeproj/project.pbxproj
  • apps/unwired-mail/unwired-mailTests/MailEngineQualificationTests.swift
  • docs/adr/0027-qualified-third-party-mail-protocol-engine.md
  • docs/qualification/swiftmail-experimental-engine.md
📝 Walkthrough

Summary

  • Added experimental SwiftMail 1.10.0 and swift-nio-imap 0.4.0 integration in the Apple app.
  • Added ExperimentalSwiftMailEngine for TLS, authentication, mailbox operations, paging, body-part reads, IDLE, safe UID mutations, Sent-folder append, cancellation, and SMTP outcome classification.
  • Release builds remain fail-closed until iCloud Mail and Fastmail certification is complete. Debug, test, and explicitly enabled internal builds may opt in.
  • Pinned SwiftMail to commit c907f871bb23812895274f4c7ae17bf343171c1e.
  • Documented privacy, content-free logging, TLS, mutation safety, dependency qualification, validation, and release gates.
  • Renamed MailEngineConnectionSnapshot.transportSecurity to minimumTLSVersions.
  • Added focused Apple tests and qualification documentation.
  • Added a mise exec -- workaround for Apple linting when mise is inactive.
  • No TypeScript areas changed.

Validation

  • Swift Testing covers dependency pinning, build policy, SMTP outcome classification, capability tokens, page bounds, UID validation, transport errors, mutation semantics, and TLS qualification.
  • Documented Apple build, test, and dependency-resolution commands passed.

Walkthrough

SwiftMail 1.10.0 and swift-nio-imap 0.4.0 are integrated behind an experimental MailEngine. The engine supports IMAP and SMTP operations, validation, error classification, qualification tests, and gated release documentation.

Changes

SwiftMail engine adoption

Layer / File(s) Summary
Dependency and enablement policy
apps/unwired-mail/unwired-mail.xcodeproj/project.pbxproj, apps/unwired-mail/unwired-mail/Services/Mailbox/ExperimentalSwiftMailEngine.swift, apps/unwired-mail/unwired-mail/Services/Mailbox/MailEngine.swift, docs/adr/..., README.md
The project pins and links SwiftMail and NIOIMAP. The engine uses experimental build gates. Connection snapshots expose configured minimum TLS versions.
IMAP and SMTP connection setup
apps/unwired-mail/unwired-mail/Services/Mailbox/ExperimentalSwiftMailEngine.swift, apps/unwired-mail/unwired-mailTests/MailEngineQualificationTests.swift
The engine configures TLS, authenticates both services, discovers capabilities and mailboxes, creates sessions, and cleans up only established services.
Mailbox session operations
apps/unwired-mail/unwired-mail/Services/Mailbox/ExperimentalSwiftMailEngine.swift
The session supports mutations, metadata and body retrieval, IDLE events, UID validation, SMTP submission, reconnection, and protocol error classification.
Qualification tests and validation runbook
apps/unwired-mail/unwired-mailTests/ExperimentalSwiftMailEngineTests.swift, apps/unwired-mail/unwired-mailTests/MailEngineQualificationTests.swift, docs/qualification/swiftmail-experimental-engine.md
Tests cover build policy, SMTP outcomes, capability tokens, page and UID validation, transport semantics, and TLS snapshots. Documentation defines safety constraints, commands, and certification gates.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant ExperimentalSwiftMailEngine
  participant IMAP
  participant SMTP
  ExperimentalSwiftMailEngine->>IMAP: connect and authenticate
  ExperimentalSwiftMailEngine->>SMTP: connect and authenticate
  IMAP-->>ExperimentalSwiftMailEngine: capabilities and mailboxes
  SMTP-->>ExperimentalSwiftMailEngine: authenticated transport
  ExperimentalSwiftMailEngine-->>ExperimentalSwiftMailEngine: create snapshot and session
Loading

Possibly related issues

  • unwired-dev/product#428 — Concerns related SwiftMail integration, NIOIMAP linking, and Mail Engine qualification.
  • unwired-dev/product#280 — Defines the provider certification gate for external release enablement.
  • unwired-dev/product#179 — Covers mailbox setup and newest-mail retrieval using the SwiftMail engine.
  • unwired-dev/product#183 — Covers the IMAP, SMTP, and IDLE engine paths.
  • unwired-dev/product#66 — Covers the experimental Mail Engine operations.

Possibly related PRs

Suggested reviewers: rajzik

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning The PR covers most #178 requirements, but it modifies MailEngineQualificationTests.swift despite the issue requiring that suite to pass unchanged. Run the original MailEngineQualificationTests.swift unchanged, or document and obtain approval for any required test-contract changes.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the primary change: experimental adoption of SwiftMail 1.10.0.
Description check ✅ Passed The description directly explains the SwiftMail integration, release gate, tests, documentation, and validation results.
Out of Scope Changes check ✅ Passed The project updates, engine implementation, tests, documentation, and release-gate changes all support the linked #178 objective.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/issue-178-adopt-swiftmail

Comment @coderabbitai help to get the list of available commands.

@rajzik
rajzik marked this pull request as ready for review August 10, 2026 11:18
@github-actions

Copy link
Copy Markdown

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 10, 2026 •

Copy link
Copy Markdown
Contributor
⚠️ Action not completed

Already reviewed.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 9

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/unwired-mail/unwired-mail.xcodeproj/project.pbxproj`:
- Around line 119-121: Update the unwired-mailTests target configuration to
explicitly include the SwiftMail package product in its package dependencies and
Frameworks build phase. Use the existing SwiftMail product reference and ensure
ExperimentalSwiftMailEngineTests.swift resolves the module through the test
target, without relying on the host application’s dependency.

In
`@apps/unwired-mail/unwired-mail/Services/Mailbox/ExperimentalSwiftMailEngine.swift`:
- Around line 56-63: The MailEngineConnectionSnapshot construction currently
reports configuration.minimumTLSVersion as negotiated transport security. Update
the surrounding Swift mail connection flow to capture the actual negotiated TLS
versions for IMAP and SMTP using a supported mechanism, or rename/redefine
transportSecurity to represent minimum TLS versions; do not populate it with the
configured floor while retaining negotiated semantics.
- Around line 119-133: Update capabilities to compare exact capability names via
NIOIMAPCore.Capability.name rather than substring matching, preserving the
existing capability mappings and special-use mailbox check. Ensure XMOVE and
NOMOVE do not enable .move, and add regression coverage for both tokens.
- Around line 234-243: Update appendToSent to avoid converting rawMessage to
UTF-8 before the IMAP append; use the supported SwiftMail API or adapter that
accepts the original Data bytes, preserving existing mailbox and error behavior.
Add a focused test covering a non-UTF-8 MIME message and verifying it is
appended to Sent successfully.
- Around line 462-464: Update the UIDPLUS capability check in the surrounding
mail move flow to match capability tokens by exact equality rather than
substring containment, so values such as X-UIDPLUS are not accepted. Add a
near-match capability test covering X-UIDPLUS and preserve the existing UIDPLUS
behavior only for the exact token.

In `@apps/unwired-mail/unwired-mailTests/ExperimentalSwiftMailEngineTests.swift`:
- Around line 6-79: Extend ExperimentalSwiftMailEngineTests with the ADR-0027
MailEngineQualificationCandidateFactory and invoke the unchanged reusable
MailEngineQualificationTests contract. Add focused tests for
SwiftMailEngineSession.validatePage boundary failures, mapping rejection of nil
CopyUID, metadata rejection of missing or zero UIDs,
ExperimentalSwiftMailEngine.connectionError and
SwiftMailEngineSession.mutationError transport-to-operationOutcomeUnknown
mappings, and ExperimentalSwiftMailEngine.capabilities parsing.

In `@docs/adr/0027-qualified-third-party-mail-protocol-engine.md`:
- Line 11: Rename the enclosing ADR heading currently labeled “Candidate
decision” to “Decision” or “Engine decision” so it reflects the accepted
SwiftMail selection. Keep the existing rejected-alternative text and decision
paragraph unchanged.

In `@docs/qualification/swiftmail-experimental-engine.md`:
- Around line 16-18: The documentation must align with ADR-0027’s move
acceptance gate: update the paragraph describing COPYUID, UIDPLUS, and MOVE to
state that advertised MOVE remains supported without UIDPLUS, or explicitly
document the amended ADR decision and rationale if the engine behavior remains
unchanged. Verify the wording against ExperimentalSwiftMailEngine.move and
ADR-0027 so the documented product behavior is consistent.
- Around line 45-53: Update the Apple lint command documentation in this guide
and README.md to use consistent conditional wording, clearly stating when the
mise-managed `mise exec -- zsh scripts/check-apple-lint.zsh` form is required or
accepted. Preserve the existing test command and reference the linked runbook
for the prerequisite details.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 44f8362c-7e7d-4217-8902-010df3fd5cea

📥 Commits

Reviewing files that changed from the base of the PR and between 3cd5f7c and 7c2fce7.

⛔ Files ignored due to path filters (1)
  • apps/unwired-mail/unwired-mail.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved is excluded by !**/Package.resolved
📒 Files selected for processing (6)
  • README.md
  • apps/unwired-mail/unwired-mail.xcodeproj/project.pbxproj
  • apps/unwired-mail/unwired-mail/Services/Mailbox/ExperimentalSwiftMailEngine.swift
  • apps/unwired-mail/unwired-mailTests/ExperimentalSwiftMailEngineTests.swift
  • docs/adr/0027-qualified-third-party-mail-protocol-engine.md
  • docs/qualification/swiftmail-experimental-engine.md
📜 Review details
🧰 Additional context used
📓 Path-based instructions (6)
apps/unwired-mail/**/*.{swift,m,mm}

📄 CodeRabbit inference engine (AGENTS.md)

The Apple app must provide formatter, linter, and test commands. Run zsh scripts/check-apple-lint.zsh for format and lint, and use the documented xcodebuild test command for tests; SwiftLint runs in strict mode.

Files:

  • apps/unwired-mail/unwired-mailTests/ExperimentalSwiftMailEngineTests.swift
  • apps/unwired-mail/unwired-mail/Services/Mailbox/ExperimentalSwiftMailEngine.swift
apps/unwired-mail/**/*.swift

📄 CodeRabbit inference engine (AGENTS.md)

Apple validation must use SwiftLint strict mode and Swift Format strict linting; if Apple tooling is unavailable, report that clearly in the final handoff.

Files:

  • apps/unwired-mail/unwired-mailTests/ExperimentalSwiftMailEngineTests.swift
  • apps/unwired-mail/unwired-mail/Services/Mailbox/ExperimentalSwiftMailEngine.swift

⚙️ CodeRabbit configuration file

apps/unwired-mail/**/*.swift: Prioritize functional correctness, data protection, and Apple platform
behavior over style already enforced by swift-format and SwiftLint.
Check actor isolation, cancellation, lifecycle transitions, persistence,
retry and offline behavior, and Keychain accessibility. Flag credentials,
tokens, mail content, or decrypted Product Sync data that can be logged,
persisted outside approved device-local storage, or cross a documented
privacy boundary. Require focused tests for changed behavior and failure
paths.

Files:

  • apps/unwired-mail/unwired-mailTests/ExperimentalSwiftMailEngineTests.swift
  • apps/unwired-mail/unwired-mail/Services/Mailbox/ExperimentalSwiftMailEngine.swift
**/*Tests/**/*.swift

⚙️ CodeRabbit configuration file

**/*Tests/**/*.swift: Verify externally observable behavior, privacy invariants, error paths,
cancellation or restart behavior where relevant, and deterministic
isolation. Flag fragile sleeps, ordering dependencies, and tests that
duplicate the implementation instead of exercising it.

Files:

  • apps/unwired-mail/unwired-mailTests/ExperimentalSwiftMailEngineTests.swift
docs/adr/**/*

📄 CodeRabbit inference engine (AGENTS.md)

Record durable architecture decisions and privacy-boundary decisions in docs/adr/.

Files:

  • docs/adr/0027-qualified-third-party-mail-protocol-engine.md
**/*.md

⚙️ CodeRabbit configuration file

**/*.md: Treat docs as product truth, not marketing. Prefer precise domain language
from CONTEXT.md; flag undefined terms, synonym drift, and conflicts with
existing ADRs under docs/adr/. For ADR changes, require a clear decision,
consequences, and privacy or security boundary impact; flag silent
reversals of prior ADRs without an explicit supersession note. For
docs/agents/ and bootstrap docs, verify agent workflow, validation, and
triage guidance stay aligned with AGENTS.md. Flag broken cross-links,
speculative future claims presented as shipped behavior, and docs that
omit the Apple vs TypeScript ownership of a boundary they describe.

Files:

  • docs/adr/0027-qualified-third-party-mail-protocol-engine.md
  • README.md
  • docs/qualification/swiftmail-experimental-engine.md
README.md

📄 CodeRabbit inference engine (AGENTS.md)

Update README.md when developer setup, common commands, or high-level project navigation changes.

Files:

  • README.md
🔇 Additional comments (11)
apps/unwired-mail/unwired-mail/Services/Mailbox/ExperimentalSwiftMailEngine.swift (8)

6-21: LGTM!


154-195: LGTM!


262-292: LGTM!


294-315: LGTM!


512-568: LGTM!

Also applies to: 570-659


380-391: 🚀 Performance & Scalability



492-494: 🗄️ Data Integrity & Integration

Keep the cancellation classification

SwiftMail wraps every failure after MAIL FROM dispatch in SMTPSendError. A bare CancellationError can only occur before the SMTP dialogue starts, so .notSubmitted(.transportUnavailable) is safe here.

			> Likely an incorrect or invalid review comment.

335-352: 🩺 Stability & Availability

Define the empty refresh signal. MailEngineIdleEvent does not document whether .changedUIDs([]) means “mailbox changed; re-scan.” Add an explicit contract and focused tests for .exists and .expunge with unchanged UIDVALIDITY, or use a distinct event.

README.md (1)

171-171: LGTM!

apps/unwired-mail/unwired-mail.xcodeproj/project.pbxproj (1)

784-791: 📐 Maintainability & Code Quality

Keep the checked-in SwiftPM lockfile.

Package.resolved is tracked and records SwiftMail 1.10.0 at revision c907f871bb23812895274f4c7ae17bf343171c1e, matching SwiftMailExperimentalBuildPolicy.dependencyRevision.

			> Likely an incorrect or invalid review comment.
apps/unwired-mail/unwired-mailTests/ExperimentalSwiftMailEngineTests.swift (1)

22-31: 📐 Maintainability & Code Quality

Do not raise this concern. SwiftMail 1.10.0 declares both initializers as public at the pinned commit.

			> Likely an incorrect or invalid review comment.

Comment thread apps/unwired-mail/unwired-mail.xcodeproj/project.pbxproj
Comment thread apps/unwired-mail/unwired-mail/Services/Mailbox/ExperimentalSwiftMailEngine.swift Outdated
Comment thread docs/adr/0027-qualified-third-party-mail-protocol-engine.md Outdated
Comment thread docs/qualification/swiftmail-experimental-engine.md Outdated
Comment thread docs/qualification/swiftmail-experimental-engine.md Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
docs/qualification/swiftmail-experimental-engine.md (2)

1-1: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Use the product term “Experimental Mail Engine”.

The heading uses “Experimental SwiftMail engine”. Use “Experimental Mail Engine” for the implementation and reserve “SwiftMail” for the dependency. Use “Standards-Based Mailbox Connection” when the document refers to the account boundary.

As per path instructions: “Prefer precise domain language from CONTEXT.md; flag undefined terms, synonym drift, and conflicts with existing ADRs under docs/adr/.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/qualification/swiftmail-experimental-engine.md` at line 1, Update the
document heading and terminology to use “Experimental Mail Engine” for the
implementation, reserve “SwiftMail” for the dependency, and use “Standards-Based
Mailbox Connection” for the account boundary. Check the document for synonym
drift and align terms with CONTEXT.md and relevant ADRs.

Source: Path instructions


39-45: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Add an exact SwiftMail pin check.

xcodebuild -resolvePackageDependencies resolves dependencies but does not assert SwiftMail version 1.10.0 and revision c907f871bb23812895274f4c7ae17bf343171c1e. Add a check that reads Package.resolved and fails when either value differs.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/qualification/swiftmail-experimental-engine.md` around lines 39 - 45,
Extend the dependency verification instructions after the xcodebuild command to
read Package.resolved and assert SwiftMail is exactly version 1.10.0 at revision
c907f871bb23812895274f4c7ae17bf343171c1e. Make the check fail when either value
differs, while retaining the existing package-resolution step.

Source: Path instructions

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/unwired-mail/unwired-mailTests/MailEngineQualificationTests.swift`:
- Around line 2873-2874: Update the second minimumTLSVersions expectation in the
qualification test to match the .tls12 default copied by
snapshot(configuration:) for both connections, unless intentionally changing the
snapshot contract; if changing it, update the fixture and
ExperimentalSwiftMailEngine consistently to represent negotiated TLS.

In `@docs/adr/0027-qualified-third-party-mail-protocol-engine.md`:
- Around line 9-11: Update the Decision section to state that SwiftMail 1.10.0
is the adopted and integrated Experimental Mail Engine, rather than a preferred
candidate. Replace the production-default wording with the precise boundary that
Standards-Based Mailbox Connections are not enabled in externally distributed
Release builds until the required vertical spikes and certification are
complete, and explicitly record the resulting privacy/security boundary.

---

Outside diff comments:
In `@docs/qualification/swiftmail-experimental-engine.md`:
- Line 1: Update the document heading and terminology to use “Experimental Mail
Engine” for the implementation, reserve “SwiftMail” for the dependency, and use
“Standards-Based Mailbox Connection” for the account boundary. Check the
document for synonym drift and align terms with CONTEXT.md and relevant ADRs.
- Around line 39-45: Extend the dependency verification instructions after the
xcodebuild command to read Package.resolved and assert SwiftMail is exactly
version 1.10.0 at revision c907f871bb23812895274f4c7ae17bf343171c1e. Make the
check fail when either value differs, while retaining the existing
package-resolution step.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 884e5090-0462-4e67-ac6f-db12638f26a5

📥 Commits

Reviewing files that changed from the base of the PR and between 98e766c and 127ce4d.

📒 Files selected for processing (8)
  • README.md
  • apps/unwired-mail/unwired-mail.xcodeproj/project.pbxproj
  • apps/unwired-mail/unwired-mail/Services/Mailbox/ExperimentalSwiftMailEngine.swift
  • apps/unwired-mail/unwired-mail/Services/Mailbox/MailEngine.swift
  • apps/unwired-mail/unwired-mailTests/ExperimentalSwiftMailEngineTests.swift
  • apps/unwired-mail/unwired-mailTests/MailEngineQualificationTests.swift
  • docs/adr/0027-qualified-third-party-mail-protocol-engine.md
  • docs/qualification/swiftmail-experimental-engine.md
📜 Review details
⏰ Context from checks skipped due to timeout. (2)
  • GitHub Check: Apple Debug · Unwired Mail
  • GitHub Check: Apple performance · Unwired Mail
🧰 Additional context used
📓 Path-based instructions (6)
**/*.md

⚙️ CodeRabbit configuration file

**/*.md: Treat docs as product truth, not marketing. Prefer precise domain language
from CONTEXT.md; flag undefined terms, synonym drift, and conflicts with
existing ADRs under docs/adr/. For ADR changes, require a clear decision,
consequences, and privacy or security boundary impact; flag silent
reversals of prior ADRs without an explicit supersession note. For
docs/agents/ and bootstrap docs, verify agent workflow, validation, and
triage guidance stay aligned with AGENTS.md. Flag broken cross-links,
speculative future claims presented as shipped behavior, and docs that
omit the Apple vs TypeScript ownership of a boundary they describe.

Files:

  • docs/qualification/swiftmail-experimental-engine.md
  • README.md
  • docs/adr/0027-qualified-third-party-mail-protocol-engine.md
apps/unwired-mail/**/*.{swift,m,mm}

📄 CodeRabbit inference engine (AGENTS.md)

The Apple app must provide formatter, linter, and test commands. Run zsh scripts/check-apple-lint.zsh for format and lint, and use the documented xcodebuild test command for tests; SwiftLint runs in strict mode.

Files:

  • apps/unwired-mail/unwired-mail/Services/Mailbox/MailEngine.swift
  • apps/unwired-mail/unwired-mailTests/ExperimentalSwiftMailEngineTests.swift
  • apps/unwired-mail/unwired-mailTests/MailEngineQualificationTests.swift
  • apps/unwired-mail/unwired-mail/Services/Mailbox/ExperimentalSwiftMailEngine.swift
apps/unwired-mail/**/*.swift

📄 CodeRabbit inference engine (AGENTS.md)

Apple validation must use SwiftLint strict mode and Swift Format strict linting; if Apple tooling is unavailable, report that clearly in the final handoff.

Files:

  • apps/unwired-mail/unwired-mail/Services/Mailbox/MailEngine.swift
  • apps/unwired-mail/unwired-mailTests/ExperimentalSwiftMailEngineTests.swift
  • apps/unwired-mail/unwired-mailTests/MailEngineQualificationTests.swift
  • apps/unwired-mail/unwired-mail/Services/Mailbox/ExperimentalSwiftMailEngine.swift

⚙️ CodeRabbit configuration file

apps/unwired-mail/**/*.swift: Prioritize functional correctness, data protection, and Apple platform
behavior over style already enforced by swift-format and SwiftLint.
Check actor isolation, cancellation, lifecycle transitions, persistence,
retry and offline behavior, and Keychain accessibility. Flag credentials,
tokens, mail content, or decrypted Product Sync data that can be logged,
persisted outside approved device-local storage, or cross a documented
privacy boundary. Require focused tests for changed behavior and failure
paths.

Files:

  • apps/unwired-mail/unwired-mail/Services/Mailbox/MailEngine.swift
  • apps/unwired-mail/unwired-mailTests/ExperimentalSwiftMailEngineTests.swift
  • apps/unwired-mail/unwired-mailTests/MailEngineQualificationTests.swift
  • apps/unwired-mail/unwired-mail/Services/Mailbox/ExperimentalSwiftMailEngine.swift
**/*Tests/**/*.swift

⚙️ CodeRabbit configuration file

**/*Tests/**/*.swift: Verify externally observable behavior, privacy invariants, error paths,
cancellation or restart behavior where relevant, and deterministic
isolation. Flag fragile sleeps, ordering dependencies, and tests that
duplicate the implementation instead of exercising it.

Files:

  • apps/unwired-mail/unwired-mailTests/ExperimentalSwiftMailEngineTests.swift
  • apps/unwired-mail/unwired-mailTests/MailEngineQualificationTests.swift
README.md

📄 CodeRabbit inference engine (AGENTS.md)

Update README.md when developer setup, common commands, or high-level project navigation changes.

Files:

  • README.md
docs/adr/**/*

📄 CodeRabbit inference engine (AGENTS.md)

Record durable architecture decisions and privacy-boundary decisions in docs/adr/.

Files:

  • docs/adr/0027-qualified-third-party-mail-protocol-engine.md
🔇 Additional comments (12)
apps/unwired-mail/unwired-mail/Services/Mailbox/ExperimentalSwiftMailEngine.swift (3)

119-131: Use Capability.name before exact matching.

String(describing:) does not use the protocol token API. Capability.name is the public parser for the capability name. The current conversion can produce a reflected struct description, so IDLE, MOVE, and UIDPLUS are not detected even when the server advertises them. (raw.githubusercontent.com)

Keep the exact comparisons. Change the input conversion to $0.name.uppercased(). Add an integration-level regression test that passes real Capability values.

#!/bin/bash
set -euo pipefail

rg -n -C 6 'fetchCapabilities\(\)|String\(describing: \$0\)|Capability\.name|capabilities\(' \
  apps/unwired-mail/unwired-mail/Services/Mailbox/ExperimentalSwiftMailEngine.swift \
  apps/unwired-mail/unwired-mailTests --glob '*.swift'

228-260: Append the original message bytes to Sent.

appendToSent rejects non-UTF-8 bytes before IMAP APPEND. SMTP can already accept those exact bytes. The Sent-copy recovery path then fails without a valid reason.

Use a byte-preserving APPEND API or adapter. Add a non-UTF-8 MIME regression test.


23-117: LGTM!

Also applies to: 172-196, 294-698

apps/unwired-mail/unwired-mail.xcodeproj/project.pbxproj (2)

119-122: LGTM!

Also applies to: 270-271, 294-303, 369-369, 438-438, 503-525, 675-675, 729-729, 805-814


789-795: 📐 Maintainability & Code Quality

No change required. Package.resolved is tracked and records SwiftMail 1.10.0 at revision c907f871bb23812895274f4c7ae17bf343171c1e.

apps/unwired-mail/unwired-mail/Services/Mailbox/MailEngine.swift (1)

98-104: LGTM!

README.md (1)

166-173: LGTM!

apps/unwired-mail/unwired-mailTests/ExperimentalSwiftMailEngineTests.swift (2)

6-18: Wire this suite to the reusable qualification contract.

This file still declares no MailEngineQualificationCandidateFactory and never invokes MailEngineQualificationContract from MailEngineQualificationTests.swift. The added tests cover helper functions, but they do not run the reusable TLS, cancellation, UID-mapping, SMTP, and privacy checks for this candidate. Add the factory and contract invocation, or identify the target file that owns this wiring.

As per path instructions: “Require focused tests for changed behavior and failure paths.”

Source: Path instructions


20-46: LGTM!

Also applies to: 48-78, 80-94, 96-107, 109-136, 138-148

docs/qualification/swiftmail-experimental-engine.md (2)

16-20: Keep the ADR-0027 gate in the release condition.

Lines 16-20 state that the candidate does not satisfy ADR-0027’s MOVE-without-UIDPLUS production gate. Lines 26-35 name only issue #280 certification before production enablement. Add the ADR gate to the release condition, or implement the missing path or explicitly supersede the ADR. Provider certification alone must not enable a candidate that this document records as outside the production acceptance gate.

As per path instructions: “Flag conflicts with existing ADRs under docs/adr/.”

Also applies to: 26-35

Source: Path instructions


3-10: LGTM!

Also applies to: 12-15, 21-24, 47-59

apps/unwired-mail/unwired-mailTests/MailEngineQualificationTests.swift (1)

360-375: LGTM!

Also applies to: 438-439, 519-519, 6379-6379, 6525-6526, 6566-6569

Comment thread apps/unwired-mail/unwired-mailTests/MailEngineQualificationTests.swift Outdated
Comment thread docs/adr/0027-qualified-third-party-mail-protocol-engine.md Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/unwired-mail/unwired-mailTests/MailEngineQualificationTests.swift (1)

438-440: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

Assert the configured TLS values exactly.

MailEngineConnectionSnapshot now exposes configured minimum TLS versions. The helper only checks that both service keys exist and that each value is at least .tls12. A candidate can report .tls12 for a .tls13 configuration and still pass. Compare the map with the expected configuration and add a successful .tls13 case when the fixture supports it.

As per path instructions, tests must verify externally observable behavior and relevant failure paths.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/unwired-mail/unwired-mailTests/MailEngineQualificationTests.swift`
around lines 438 - 440, Update the snapshot assertions in the relevant
MailEngine qualification test to compare minimumTLSVersions against the exact
expected IMAP and SMTP TLS configuration, rather than only checking keys and a
lower bound. Add a successful TLS 1.3 configuration case when supported by the
fixture, while preserving coverage of the existing TLS 1.2 behavior and relevant
failure paths.

Source: Path instructions

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@apps/unwired-mail/unwired-mailTests/MailEngineQualificationTests.swift`:
- Around line 438-440: Update the snapshot assertions in the relevant MailEngine
qualification test to compare minimumTLSVersions against the exact expected IMAP
and SMTP TLS configuration, rather than only checking keys and a lower bound.
Add a successful TLS 1.3 configuration case when supported by the fixture, while
preserving coverage of the existing TLS 1.2 behavior and relevant failure paths.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 53bc8cd3-1c4f-4e08-b56d-0a3abcf0e1c5

📥 Commits

Reviewing files that changed from the base of the PR and between 127ce4d and efa05c1.

📒 Files selected for processing (3)
  • apps/unwired-mail/unwired-mail.xcodeproj/project.pbxproj
  • apps/unwired-mail/unwired-mailTests/MailEngineQualificationTests.swift
  • docs/adr/0027-qualified-third-party-mail-protocol-engine.md
📜 Review details
⏰ Context from checks skipped due to timeout. (1)
  • GitHub Check: Apple Debug · Unwired Mail
🧰 Additional context used
📓 Path-based instructions (5)
docs/adr/**/*

📄 CodeRabbit inference engine (AGENTS.md)

Record durable architecture decisions and privacy-boundary decisions in docs/adr/.

Files:

  • docs/adr/0027-qualified-third-party-mail-protocol-engine.md
**/*.md

⚙️ CodeRabbit configuration file

**/*.md: Treat docs as product truth, not marketing. Prefer precise domain language
from CONTEXT.md; flag undefined terms, synonym drift, and conflicts with
existing ADRs under docs/adr/. For ADR changes, require a clear decision,
consequences, and privacy or security boundary impact; flag silent
reversals of prior ADRs without an explicit supersession note. For
docs/agents/ and bootstrap docs, verify agent workflow, validation, and
triage guidance stay aligned with AGENTS.md. Flag broken cross-links,
speculative future claims presented as shipped behavior, and docs that
omit the Apple vs TypeScript ownership of a boundary they describe.

Files:

  • docs/adr/0027-qualified-third-party-mail-protocol-engine.md
apps/unwired-mail/**/*.{swift,m,mm}

📄 CodeRabbit inference engine (AGENTS.md)

The Apple app must provide formatter, linter, and test commands. Run zsh scripts/check-apple-lint.zsh for format and lint, and use the documented xcodebuild test command for tests; SwiftLint runs in strict mode.

Files:

  • apps/unwired-mail/unwired-mailTests/MailEngineQualificationTests.swift
apps/unwired-mail/**/*.swift

📄 CodeRabbit inference engine (AGENTS.md)

Apple validation must use SwiftLint strict mode and Swift Format strict linting; if Apple tooling is unavailable, report that clearly in the final handoff.

Files:

  • apps/unwired-mail/unwired-mailTests/MailEngineQualificationTests.swift

⚙️ CodeRabbit configuration file

apps/unwired-mail/**/*.swift: Prioritize functional correctness, data protection, and Apple platform
behavior over style already enforced by swift-format and SwiftLint.
Check actor isolation, cancellation, lifecycle transitions, persistence,
retry and offline behavior, and Keychain accessibility. Flag credentials,
tokens, mail content, or decrypted Product Sync data that can be logged,
persisted outside approved device-local storage, or cross a documented
privacy boundary. Require focused tests for changed behavior and failure
paths.

Files:

  • apps/unwired-mail/unwired-mailTests/MailEngineQualificationTests.swift
**/*Tests/**/*.swift

⚙️ CodeRabbit configuration file

**/*Tests/**/*.swift: Verify externally observable behavior, privacy invariants, error paths,
cancellation or restart behavior where relevant, and deterministic
isolation. Flag fragile sleeps, ordering dependencies, and tests that
duplicate the implementation instead of exercising it.

Files:

  • apps/unwired-mail/unwired-mailTests/MailEngineQualificationTests.swift
🔇 Additional comments (4)
docs/adr/0027-qualified-third-party-mail-protocol-engine.md (2)

11-11: Align the remaining ADR language with the adopted state.

Line 11 correctly adopts SwiftMail 1.10.0 for experimental and internal use and gates externally distributed Release builds. Line 17 still says “by default in a production release”, and Line 44 still says the façade exists “before any candidate dependency is adopted”. Replace both phrases with the adopted Experimental Mail Engine and externally distributed Release terminology.

As per path instructions, docs are product truth and must use precise domain language without synonym drift.

Source: Path instructions


9-9: LGTM!

Also applies to: 45-45

apps/unwired-mail/unwired-mail.xcodeproj/project.pbxproj (1)

119-124: LGTM!

Also applies to: 272-273, 296-297, 306-307, 373-373, 442-442, 508-508, 530-531, 589-589, 682-682, 736-736, 796-811, 820-839

apps/unwired-mail/unwired-mailTests/MailEngineQualificationTests.swift (1)

360-375: LGTM!

Also applies to: 519-519, 2873-2874, 6356-6366, 6379-6379, 6525-6526, 6566-6569

@rajzik
rajzik merged commit 601e781 into main Aug 10, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Adopt SwiftMail 1.10.0 experimentally behind MailEngine

1 participant