Skip to content

Add Inbox Cleanup to standards-based mail - #504

Merged
rajzik merged 3 commits into
mainfrom
codex/issue-351-standards-mail-cleanup
Aug 15, 2026
Merged

rajzik merged 3 commits into
mainfrom
codex/issue-351-standards-mail-cleanup

Conversation

@gipity-bot

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

Copy link
Copy Markdown
Contributor

Summary

  • enable the shared on-device Inbox Cleanup detector for authorized Standards-Based Mailbox Connections with safe Trash support
  • keep canonical Inbox, Spam, and Trash role checks and provider-scoped sender normalization
  • disclose mapped-Trash and automatic-Undo limits before confirmation
  • add focused Swift Testing coverage plus README documentation and a patch changeset

Closes #351

Validation

  • mise exec -- xcrun swift-format lint --strict on all changed Swift files — passed
  • swiftlint lint --strict --no-cache on all changed Swift files — passed
  • xcodebuild test ... -only-testing:unwired-mailTests/InboxCleanupTests on Mac Catalyst — passed, 11 tests
  • two focused IMAPMailboxConnectionAdapterTests for UIDPLUS uncertainty recovery and native-move stable identity — passed, 2 tests
  • iPhone 17 / iOS 26.5 Debug build-for-testing on D6D43A14-3B1A-41B6-B25A-0DFCA1BFB089 — passed
  • focused iOS execution on that same UDID — did not reach XCTest because the Simulator-reported testmanagerd Unix-domain socket did not exist
  • full scripts/check-apple-lint.zsh — recursive strict swift-format passed; SwiftLint reported only the existing untouched BlockedSenderSyncService.swift:516 51-line function-body violation, plus its sandbox cache-write warning
  • Xcode project plist lint and git diff --check — passed

Remaining human action

None beyond normal review and hosted CI. The local Simulator runner failure above is preserved for visibility; the implementation and deterministic focused coverage are complete.


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 15, 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: 44 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: ca528fae-41a0-4b89-9d3b-9f97065caba3

📥 Commits

Reviewing files that changed from the base of the PR and between 77df3d6 and 8390fc1.

📒 Files selected for processing (2)
  • README.md
  • apps/unwired-mail/unwired-mail/Services/Mailbox/IMAPMailboxConnectionAdapter.swift

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 8c72bc5c-67f6-4189-93a0-a0da196b2874

📥 Commits

Reviewing files that changed from the base of the PR and between 8efc873 and 77df3d6.

📒 Files selected for processing (5)
  • .changeset/calm-owls-clean.md
  • README.md
  • apps/unwired-mail/unwired-mail/Features/Account/InboxCleanupViews.swift
  • apps/unwired-mail/unwired-mail/Services/Mailbox/InboxCleanup.swift
  • apps/unwired-mail/unwired-mailTests/InboxCleanupTests.swift
📜 Recent review details
🧰 Additional context used
📓 Path-based instructions (6)
.changeset/**/*

📄 CodeRabbit inference engine (AGENTS.md)

Create a Changeset for runtime behavior changes or exported type/API changes; tests-only, internal refactors, documentation-only changes, and JSDoc-only maintenance may omit one at maintainer discretion.

Files:

  • .changeset/calm-owls-clean.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/Features/Account/InboxCleanupViews.swift
  • apps/unwired-mail/unwired-mail/Services/Mailbox/InboxCleanup.swift
  • apps/unwired-mail/unwired-mailTests/InboxCleanupTests.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/Features/Account/InboxCleanupViews.swift
  • apps/unwired-mail/unwired-mail/Services/Mailbox/InboxCleanup.swift
  • apps/unwired-mail/unwired-mailTests/InboxCleanupTests.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/Features/Account/InboxCleanupViews.swift
  • apps/unwired-mail/unwired-mail/Services/Mailbox/InboxCleanup.swift
  • apps/unwired-mail/unwired-mailTests/InboxCleanupTests.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
**/*.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:

  • README.md
**/*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/InboxCleanupTests.swift
🧠 Learnings (2)
📚 Learning: 2026-08-06T06:40:04.308Z
Learnt from: CR
Repo: unwired-dev/product PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-08-06T06:40:04.308Z
Learning: Applies to .changeset/**/* : Create a Changeset for runtime behavior changes or exported type/API changes; tests-only, internal refactors, documentation-only changes, and JSDoc-only maintenance may omit one at maintainer discretion.

Applied to files:

  • .changeset/calm-owls-clean.md
📚 Learning: 2026-08-06T06:40:04.308Z
Learnt from: CR
Repo: unwired-dev/product PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-08-06T06:40:04.308Z
Learning: Applies to apps/unwired-mail/**/*.swift : Apple validation must use SwiftLint strict mode and Swift Format strict linting; if Apple tooling is unavailable, report that clearly in the final handoff.

Applied to files:

  • apps/unwired-mail/unwired-mail/Features/Account/InboxCleanupViews.swift
🪛 markdownlint-cli2 (0.23.2)
.changeset/calm-owls-clean.md

[warning] 5-5: First line in a file should be a top-level heading

(MD041, first-line-heading, first-line-h1)

🔇 Additional comments (4)
apps/unwired-mail/unwired-mailTests/InboxCleanupTests.swift (2)

87-144: LGTM!

Also applies to: 421-435


160-169: 🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

Accept verified MOVE as an eligible recovery mechanism.

CONTEXT.md permits a mapped-Trash move when verified MOVE or UIDPLUS/COPYUID is available. The test constructs a mapped-Trash connection with .move and requires no proposal. This excludes a permitted safe action.

  • apps/unwired-mail/unwired-mailTests/InboxCleanupTests.swift#L160-L169: Require a proposal for the mapped-Trash MOVE connection.
  • apps/unwired-mail/unwired-mail/Services/Mailbox/InboxCleanup.swift#L233-L233: Ensure the .supports(.delete) contract accepts verified MOVE with mapped Trash.
  • README.md#L70-L70: Replace the UIDPLUS-only eligibility statement with MOVE or UIDPLUS/COPYUID.
⛔ Skipped due to learnings
Learnt from: rajzik
Repo: unwired-dev/product PR: 210
File: apps/unwired-mail/unwired-mailTests/GmailProviderConnectionServiceTests.swift:930-982
Timestamp: 2026-07-28T09:17:25.851Z
Learning: For Gmail mailbox cleanup, `packages/convex/convex/pushRelay.ts` implements `pushRelay:removeGmailConnection` as an idempotent mutation: an absent operational Gmail connection returns successfully with `removed: false` and independently computed `hasRemainingGmailConnections`. `apps/unwired-mail/unwired-mail/Services/Convex/ConvexClient.swift` intentionally exposes only `hasRemainingGmailConnections` through `ConvexClient.removeGmailConnection`; do not require tests for a duplicate-removal error path.
Learnt from: rajzik
Repo: unwired-dev/product PR: 201
File: apps/unwired-mail/unwired-mail/Services/Mailbox/OutboxDeliveryService.swift:0-0
Timestamp: 2026-07-27T07:26:00.112Z
Learning: For the Swift EWS mail provider, `apps/unwired-mail/unwired-mail/Services/Mailbox/OutboxDeliveryService.swift` and `apps/unwired-mail/unwired-mail/Services/Mailbox/PendingProviderActionService.swift` deliberately use different EWS failure-disposition policies. Outbox uncertain send handoffs classify HTTP 5xx, HTTP 408/409/425, `ErrorTimeoutExpired`, and invalid EWS responses as ambiguous and reconcile delivery before another send to prevent duplicates; provider actions treat their retry-safe EWS response cases as transient retries. Do not extract their classification logic into a shared helper merely because response-code checks overlap.
Learnt from: rajzik
Repo: unwired-dev/product PR: 200
File: apps/unwired-mail/unwired-mail/Services/Mailbox/MicrosoftGraphMailboxConnectionAdapter.swift:3247-3293
Timestamp: 2026-07-27T07:17:40.181Z
Learning: In `apps/unwired-mail/unwired-mail/Services/Mailbox/MicrosoftGraphMailboxConnectionAdapter.swift`, queued Microsoft Graph pending actions contain exactly one provider message. Microsoft Graph move requests use `Prefer: IdType="ImmutableId"`, so retrying the action does not have a multi-message replay window or moved-message-ID instability.
Learnt from: rajzik
Repo: unwired-dev/product PR: 200
File: apps/unwired-mail/unwired-mailTests/MailboxConnectionAdapterTests.swift:0-0
Timestamp: 2026-07-27T07:17:51.753Z
Learning: In `apps/unwired-mail/unwired-mailTests/MailboxConnectionAdapterTests.swift`, `testViewModelRefreshesItsConnectionSnapshotAfterMetadataSync` intentionally verifies an additive metadata refresh: existing mailbox connections may remain while a newly refreshed connection appears, so the test must not assert removal of the original connection.
Learnt from: rajzik
Repo: unwired-dev/product PR: 142
File: apps/unwired-mail/unwired-mail/Services/Mailbox/IMAPMailboxConnectionAdapter.swift:1943-1945
Timestamp: 2026-07-24T15:41:51.470Z
Learning: In `apps/unwired-mail/unwired-mail/Services/Mailbox/IMAPMailboxConnectionAdapter.swift`, `MailboxConnectionRouter.loadConnections(session:)` intentionally degrades gracefully when the IMAP adapter fails by treating its connection list as empty, so Gmail connections remain available. The intended composite-provider failure semantics are deferred to GitHub issue `#149`; do not flag this behavior as inconsistent error handling until that decision is made.
Learnt from: rajzik
Repo: unwired-dev/product PR: 75
File: apps/unwired-mail/unwired-mail/Services/ProductSync/NotificationRuleSyncService.swift:206-222
Timestamp: 2026-07-19T14:41:13.329Z
Learning: In `apps/unwired-mail/unwired-mail/Services/ProductSync/NotificationRuleSyncService.swift`, prefer explicit branches when they make materially different cache error-handling policies—fatal propagation versus best-effort suppression—clearer than a shared local helper.
Learnt from: rajzik
Repo: unwired-dev/product PR: 104
File: apps/unwired-mail/unwired-mail/Features/Account/AccountView.swift:1501-1505
Timestamp: 2026-07-23T04:43:56.733Z
Learning: In the Apple mail app, `apps/unwired-mail/unwired-mail/Features/Account/AccountView.swift` has no persisted pinned-message state or connection-level pinned-message source. `GmailInboxViewModel.startBodyPrefetch(connection:)` intentionally passes an empty `pinnedMessageIds` set to `MailboxMessageBodyPrefetching.prefetchMessageBodies`; the prefetch API supports pinned identities for a future pinning feature.
Learnt from: rajzik
Repo: unwired-dev/product PR: 137
File: apps/unwired-mail/unwired-mailTests/GmailMessageMetadataServiceTests.swift:1049-1055
Timestamp: 2026-07-24T03:42:51.509Z
Learning: In `apps/unwired-mail/unwired-mailTests/GmailMessageMetadataServiceTests.swift`, `testMailboxFreshnessKeepsCoalescedSyncRunningWhenFirstCallerIsCancelled` must release the deliberately suspended shared sync before awaiting the cancelled caller’s `first.value`. The coalesced sync is intentionally kept alive after one caller is cancelled; awaiting that caller before `RecordingMailboxFreshnessService.releaseSync()` deadlocks the test harness.
Learnt from: rajzik
Repo: unwired-dev/product PR: 249
File: docs/research/swiftmail-alternatives.md:18-31
Timestamp: 2026-07-31T14:59:35.634Z
Learning: For the SwiftMail qualification documentation, verify SwiftMail releases and public APIs against the exact upstream tag rather than search indexes. SwiftMail 1.10.0 was released on 2026-07-31 and declares public `CopyUID` for destination UIDVALIDITY and ordered source-to-destination UID mapping, plus public `SMTPSendError` for typed SMTP failure information.
Learnt from: rajzik
Repo: unwired-dev/product PR: 98
File: apps/unwired-mail/unwired-mail/Services/Gmail/MessageCategorizationService.swift:1240-1244
Timestamp: 2026-07-22T10:38:14.868Z
Learning: In `apps/unwired-mail/unwired-mail/Services/Gmail/MessageCategorizationService.swift`, `GmailMessageCategorizationService.classificationCategories(learningSignalSenderAddresses:session:)` must clear the combined background categorization cache when loading future learning signals fails because authentication is unavailable. The cache combines custom-category state (including explicit absence) with exact-sender learning signals; retaining it after a partial authenticated refresh could reuse stale category context. Background notification categorization must fail closed in this case.
Learnt from: rajzik
Repo: unwired-dev/product PR: 91
File: apps/unwired-mail/unwired-mail/Services/ProductSync/MailboxConnectionDefinition.swift:4-12
Timestamp: 2026-07-21T15:13:51.905Z
Learning: In `apps/unwired-mail/unwired-mail/Services/ProductSync/MailboxConnectionDefinition.swift`, `MailboxConnectionDefinition.stableProviderConnectionKey` is intentionally persisted for backward compatibility. It is a versioned derived hash of the provider and provider account identifier, not unused payload.

Source: Path instructions

apps/unwired-mail/unwired-mail/Features/Account/InboxCleanupViews.swift (1)

170-172: LGTM!

.changeset/calm-owls-clean.md (1)

1-5: LGTM!


📝 Walkthrough

Summary

  • Added Inbox Cleanup support for authorized Standards-Based Mailbox Connections.
  • Candidate selection uses category, read state, Pin, reply evidence, mapped mailbox roles, age, and on-device confidence.
  • Actions move revalidated messages to mapped provider Trash. Permanent deletion remains unavailable.
  • Connections without mapped Trash or required UIDPLUS support are excluded.
  • Preserved provider-scoped sender normalization, UIDVALIDITY, and UID identity checks.
  • Documented mapped-Trash and automatic-Undo limitations before confirmation.
  • Preserved connection-scoped recovery for partial failures, retries, stale proposals, and offline states.
  • Updated the Apple SwiftUI service and test areas in apps/unwired-mail.
  • Updated README documentation and added a patch changeset for the unwired-mail package.

Validation

  • Ran formatting, linting, focused tests, build-for-testing, project plist checks, and diff checks.
  • Focused simulator execution could not reach XCTest because the testmanagerd socket was unavailable.

Walkthrough

Inbox Cleanup now includes eligible Standards-Based Mail connections. Tests cover mapped mailbox roles, UIDPLUS support, sender identity, and unsupported connection types. The UI and documentation describe Trash-based recovery and automatic Undo limits.

Changes

Inbox Cleanup

Layer / File(s) Summary
Standards-Based eligibility and validation
apps/unwired-mail/unwired-mail/Services/Mailbox/InboxCleanup.swift, apps/unwired-mail/unwired-mailTests/InboxCleanupTests.swift
Inbox Cleanup accepts IMAP/SMTP connections. Tests cover POP3 rejection, mailbox role mappings, UIDPLUS requirements, and case-preserving sender normalization.
Recovery rules and release documentation
apps/unwired-mail/unwired-mail/Features/Account/InboxCleanupViews.swift, README.md, .changeset/calm-owls-clean.md
The cleanup disclaimer and README describe Trash mapping, automatic Undo limits, provider Trash as the recovery boundary, and permanent-erasure constraints. The changeset requests a patch release.

Estimated code review effort: 2 (Simple) | ~10 minutes

Merge Risk: 🟡 Moderate · up to 77df3

Verified MOVE support for mapped Trash is not currently treated as eligible, which may prevent Inbox Cleanup from working for supported mailboxes; merge should wait for the eligibility logic, test, and documentation to agree.

Possibly related issues

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The changes add Standards-Based Mail support to shared Inbox Cleanup, preserve mapped-role and recovery rules, and add focused tests for issue #351.
Out of Scope Changes check ✅ Passed All changed files support the linked Inbox Cleanup objective through implementation, tests, documentation, or release metadata.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Title check ✅ Passed The title clearly summarizes the main change: adding Inbox Cleanup support for standards-based mail.
Description check ✅ Passed The description directly explains the implementation, limitations, tests, documentation, and validation for the Inbox Cleanup changes.
✨ 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-351-standards-mail-cleanup

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

@rajzik
rajzik marked this pull request as ready for review August 15, 2026 13:49
@github-actions

Copy link
Copy Markdown

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 15, 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.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 77df3d6ad8

ℹ️ 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".

Comment thread apps/unwired-mail/unwired-mail/Services/Mailbox/InboxCleanup.swift
@rajzik
rajzik merged commit 6cfd2c6 into main Aug 15, 2026
5 of 9 checks passed
This was referenced Aug 15, 2026
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.

Add Inbox Cleanup to standards-based mail

1 participant