Skip to content

Measure production mail-shell release budgets - #269

Merged
rajzik merged 5 commits into
mainfrom
codex/issue-176-measure-production-mail-shell-budgets
Aug 4, 2026
Merged

rajzik merged 5 commits into
mainfrom
codex/issue-176-measure-production-mail-shell-budgets

Conversation

@gipity-bot

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

Copy link
Copy Markdown
Contributor

Summary

  • measure signed-in mail-shell launch through RootView, session bootstrap, connection loading, launch tasks, and visible shell rendering
  • measure cached mailbox and Mail View switching through the live sidebar selection binding and production mailbox-loading path
  • verify selected collections render visible rows from the expected dataset
  • exercise encrypted, file-backed warm message-body reads with representative SwiftData fixtures

Validation

  • mise exec -- zsh scripts/check-apple-lint.zsh — passed with 0 violations
  • focused xcodebuild attempt — final code compiled, but the test could not launch because Simulator installation failed with ENOSPC
  • full xcodebuild test — attempted, but interrupted when the host volume ran out of space while writing Simulator/test artifacts

The test commands need to be rerun after freeing local disk space. This PR is intentionally a draft.

Closes #176


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 3, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

Important

Review skipped

No new commits to review since the last review.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 7a2a7af3-0df6-410e-881e-c0e50325dae2

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Summary

  • Adds production-path release-budget tests for mail-shell launch, cached mailbox switching, Mail View switching, and warm message-body reads.
  • Adds launch and rendered-row instrumentation through RootView, AccountView, MailShellReleaseBudgetDriver, and MailShellThreadList.
  • Uses representative SwiftData fixtures, sent and inbox threads, and encrypted file-backed cache data in temporary directories.
  • Enforces one-second launch and 200 ms interaction budgets.

Affected areas

  • Apple: SwiftUI root and account views, mailbox and Mail View loading, SwiftData fixtures, and FileGmailMessageBodyCache.
  • TypeScript: No TypeScript areas changed.

Privacy and security

  • Tests read encrypted message-body cache fixtures from temporary directories.
  • No production privacy or security boundary changes.

Migrations and configuration

  • No migrations or configuration changes.

Validation

  • Apple lint passed with 0 violations.
  • Focused and full xcodebuild tests were attempted.
  • Simulator installation and test artifact generation failed because of insufficient disk space.
  • Tests must be rerun after freeing disk space.

Walkthrough

The mail shell now exposes launch and rendered-thread tracking hooks. RootView accepts injected signed-in content. Release measurements use production-style launch, mailbox switching, sent and inbox fixtures, cached metadata, and filesystem-backed message bodies.

Changes

Mail-shell release measurement

Layer / File(s) Summary
Account launch and render coordination
apps/unwired-mail/unwired-mail/Features/Account/AccountView.swift
AccountView wires launch completion, mailbox selection, generic mail setup, and rendered-thread callbacks through MailShellReleaseBudgetDriver.
Root signed-in content injection
apps/unwired-mail/unwired-mail/Features/Account/RootView.swift
RootView renders injected signed-in content and keeps AccountView as the default initializer path.
Production-path release measurements
apps/unwired-mail/unwired-mailTests/MailboxConnectionAdapterTests.swift
Release measurements use production-style RootView launch, inbox and sent mailbox switching, cached metadata, filesystem-backed body caching, release test doubles, and rendered-item polling.

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

Sequence Diagram(s)

sequenceDiagram
  participant ReleaseTest
  participant RootView
  participant AccountView
  participant MailShellThreadList
  participant MailShellReleaseBudgetDriver
  ReleaseTest->>RootView: launch signed-in mail shell
  RootView->>AccountView: build content from account snapshot
  AccountView->>MailShellReleaseBudgetDriver: attach mailbox selection handler
  AccountView->>MailShellThreadList: provide item render callback
  MailShellThreadList->>MailShellReleaseBudgetDriver: record rendered thread item
  ReleaseTest->>MailShellReleaseBudgetDriver: wait for expected mailbox items
Loading

Possibly related PRs

Suggested reviewers: rajzik

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The changes align with [#176] by adding production-path launch, mailbox and Mail View switching, rendered-row, and file-backed warm-body measurements.
Out of Scope Changes check ✅ Passed All source and test changes support the release-budget instrumentation and validation described in [#176].
Title check ✅ Passed The title clearly summarizes the main change: measuring production mail-shell release budgets.
Description check ✅ Passed The description directly explains the release-budget measurements, validation results, and test limitations.
✨ Finishing Touches 💡 1
📝 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-176-measure-production-mail-shell-budgets

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

@gipity-bot

gipity-bot Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Chef's kiss.

Reviewed commit: 17df3b23fe

ℹ️ About Codex in GitHub

Codex has been enabled to automatically 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 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

@rajzik
rajzik marked this pull request as ready for review August 3, 2026 19:09
@github-actions

github-actions Bot commented Aug 3, 2026

Copy link
Copy Markdown

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 3, 2026 •

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

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.

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/MailboxConnectionAdapterTests.swift (1)

4463-4618: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Explicitly tear down each hosted launch view

releaseFixtureWindow only makes the window visible, and loop cleanup only sets isHidden. This does not guarantee that AccountView’s .onDisappear runs. Add explicit teardown so MailboxWorkCoordinator.shared does not retain each iteration’s registration and inboxViewModel closure.

🤖 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/MailboxConnectionAdapterTests.swift`
around lines 4463 - 4618, Explicitly tear down each hosted SwiftUI view before
iteration cleanup so AccountView’s onDisappear executes. Update the launch,
body, and draft hosting flow around launchHost, bodyHost, and draftHost to
remove or dismantle their hosting controllers/views and release their windows,
rather than only setting isHidden, ensuring MailboxWorkCoordinator.shared drops
the iteration’s registration and inboxViewModel closure.

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/MailboxConnectionAdapterTests.swift`:
- Around line 4463-4618: Explicitly tear down each hosted SwiftUI view before
iteration cleanup so AccountView’s onDisappear executes. Update the launch,
body, and draft hosting flow around launchHost, bodyHost, and draftHost to
remove or dismantle their hosting controllers/views and release their windows,
rather than only setting isHidden, ensuring MailboxWorkCoordinator.shared drops
the iteration’s registration and inboxViewModel closure.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 795f0145-2f4f-4732-ba96-3c8ae773cd98

📥 Commits

Reviewing files that changed from the base of the PR and between 65518d5 and 17df3b2.

📒 Files selected for processing (3)
  • apps/unwired-mail/unwired-mail/Features/Account/AccountView.swift
  • apps/unwired-mail/unwired-mail/Features/Account/RootView.swift
  • apps/unwired-mail/unwired-mailTests/MailboxConnectionAdapterTests.swift
📜 Review details
⏰ Context from checks skipped due to timeout. (1)
  • GitHub Check: Apple · Unwired Mail
🧰 Additional context used
📓 Path-based instructions (3)
apps/unwired-mail/**/*.{swift,mm,m,h}

📄 CodeRabbit inference engine (AGENTS.md)

The Apple app must provide formatter, linter, and test commands; use zsh scripts/check-apple-lint.zsh for formatting and linting and the specified xcodebuild test command for tests.

Files:

  • apps/unwired-mail/unwired-mail/Features/Account/RootView.swift
  • apps/unwired-mail/unwired-mailTests/MailboxConnectionAdapterTests.swift
  • apps/unwired-mail/unwired-mail/Features/Account/AccountView.swift
apps/unwired-mail/**/*.swift

📄 CodeRabbit inference engine (AGENTS.md)

SwiftLint runs in strict mode so warnings fail validation; use mise-managed SwiftLint and Xcode's xcrun swift-format where applicable.

Files:

  • apps/unwired-mail/unwired-mail/Features/Account/RootView.swift
  • apps/unwired-mail/unwired-mailTests/MailboxConnectionAdapterTests.swift
  • apps/unwired-mail/unwired-mail/Features/Account/AccountView.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/RootView.swift
  • apps/unwired-mail/unwired-mailTests/MailboxConnectionAdapterTests.swift
  • apps/unwired-mail/unwired-mail/Features/Account/AccountView.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/MailboxConnectionAdapterTests.swift
🔇 Additional comments (4)
apps/unwired-mail/unwired-mail/Features/Account/AccountView.swift (1)

1007-1021: LGTM!

Also applies to: 1027-1029, 1065-1077, 1095-1095, 1188-1195, 1400-1401, 1558-1558, 3119-3119, 3172-3172

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

3-13: LGTM!

Also applies to: 24-24, 33-40

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

4303-4423: LGTM!

Also applies to: 4435-4541


7431-7560: LGTM!

Also applies to: 8143-8143, 8177-8221

@github-actions

github-actions Bot commented Aug 3, 2026

Copy link
Copy Markdown

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 3, 2026 •

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

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: 1

Caution

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

⚠️ Outside diff range comments (3)
apps/unwired-mail/unwired-mail/Features/Account/AccountView.swift (3)

1032-1035: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Use constant-time membership tracking for rendered rows.

recordRenderedItem scans the full renderedItems array for every row appearance. This makes tracking O(n²) on the main actor. The release helper reads only id, but the driver stores each complete MailShellThreadListItem. Track identifiers in a Set and retain only the data required by the measurement.

🤖 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-mail/Features/Account/AccountView.swift` around
lines 1032 - 1035, Update recordRenderedItem to use a Set of item identifiers
for constant-time duplicate checks instead of scanning renderedItems. Retain
only the measurement-required item data while preserving the existing release
helper’s identifier-based behavior, and update related storage and accessors
consistently.

1417-1417: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Gate launch measurement on shell readiness and row rendering.

initialLaunchDidFinish() runs after refreshPinnedBodyPrefetch(...), but that method starts an unstructured Task and returns immediately. The release test also checks only for an expected thread ID in itemDidRender. Add an explicit NavigationSplitView shell-readiness signal, and wait for both that signal and a rendered row before recording launch time. Add focused success and timeout-path tests for shell readiness and prefetch ordering.

🤖 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-mail/Features/Account/AccountView.swift` at line
1417, Update the launch measurement flow around itemDidRender and
initialLaunchDidFinish() to require both an explicit NavigationSplitView
shell-readiness signal and a rendered expected row before recording launch time.
Ensure refreshPinnedBodyPrefetch(...)’s asynchronous work cannot allow
measurement to finish prematurely, and add focused tests covering successful
shell/prefetch ordering and the timeout path.

Source: Path instructions


1574-1574: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Guard the launch completion callback against cancellation.

The canceled .task can still reach initialLaunchDidFinish() because synchronization converts cancellation into a normal return. Add guard !Task.isCancelled else { return } before the callback. Add a cancellation/reappearance test and run the specified xcodebuild test command after cleanup.

🤖 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-mail/Features/Account/AccountView.swift` at line
1574, In the launch task flow immediately before initialLaunchDidFinish(), check
Task.isCancelled and return when cancellation is detected so a canceled task
cannot invoke the completion callback. Add a test covering cancellation followed
by reappearance, then clean up and run the specified xcodebuild test command.

Sources: Coding guidelines, 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-mail/Features/Account/AccountView.swift`:
- Around line 1009-1019: Update installSelectionHandler and the related
itemDidRender/releaseWaitForRenderedThreads flow to reset renderedItems whenever
a new shell owner is installed and associate render callbacks with the current
owner or generation. Ignore stale or late callbacks from prior owners so they
cannot release the wait before the current shell has rendered, and add a test
covering driver reuse across shell disappearance and reappearance.

---

Outside diff comments:
In `@apps/unwired-mail/unwired-mail/Features/Account/AccountView.swift`:
- Around line 1032-1035: Update recordRenderedItem to use a Set of item
identifiers for constant-time duplicate checks instead of scanning
renderedItems. Retain only the measurement-required item data while preserving
the existing release helper’s identifier-based behavior, and update related
storage and accessors consistently.
- Line 1417: Update the launch measurement flow around itemDidRender and
initialLaunchDidFinish() to require both an explicit NavigationSplitView
shell-readiness signal and a rendered expected row before recording launch time.
Ensure refreshPinnedBodyPrefetch(...)’s asynchronous work cannot allow
measurement to finish prematurely, and add focused tests covering successful
shell/prefetch ordering and the timeout path.
- Line 1574: In the launch task flow immediately before
initialLaunchDidFinish(), check Task.isCancelled and return when cancellation is
detected so a canceled task cannot invoke the completion callback. Add a test
covering cancellation followed by reappearance, then clean up and run the
specified xcodebuild test command.
🪄 Autofix (Beta)

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: 0e1f2cad-8ba8-4dcb-96b9-1f23c8e5773b

📥 Commits

Reviewing files that changed from the base of the PR and between 17df3b2 and fb0a4ce.

📒 Files selected for processing (1)
  • apps/unwired-mail/unwired-mail/Features/Account/AccountView.swift
📜 Review details
⏰ Context from checks skipped due to timeout. (1)
  • GitHub Check: Apple · Unwired Mail
🧰 Additional context used
📓 Path-based instructions (2)
apps/unwired-mail/**/*.{swift,mm,m,h}

📄 CodeRabbit inference engine (AGENTS.md)

The Apple app must provide formatter, linter, and test commands; use zsh scripts/check-apple-lint.zsh for formatting and linting and the specified xcodebuild test command for tests.

Files:

  • apps/unwired-mail/unwired-mail/Features/Account/AccountView.swift
apps/unwired-mail/**/*.swift

📄 CodeRabbit inference engine (AGENTS.md)

SwiftLint runs in strict mode so warnings fail validation; use mise-managed SwiftLint and Xcode's xcrun swift-format where applicable.

Files:

  • apps/unwired-mail/unwired-mail/Features/Account/AccountView.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/AccountView.swift
🔇 Additional comments (2)
apps/unwired-mail/unwired-mail/Features/Account/AccountView.swift (2)

1059-1059: LGTM!

Also applies to: 1081-1111


3135-3135: 🎯 Functional Correctness

No initializer update is required. itemDidRender has a default no-op closure, and the only instance initializer passes the callback.

			> Likely an incorrect or invalid review comment.

Comment thread apps/unwired-mail/unwired-mail/Features/Account/AccountView.swift

@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: fb0a4ce874

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

viewModel: inboxViewModel
)
let launchHost = UIHostingController(
rootView: RootView(session: productAccountSession) { launchSnapshot in

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Run the production budget with Release optimization

The new live RootView/AccountView timings are still executed by the scheme's TestAction, whose build configuration is Debug; the project sets that configuration to SWIFT_OPTIMIZATION_LEVEL = -Onone, while the repository's prescribed xcodebuild test command does not override it. As a result, these asserted launch and mailbox-switch budgets measure an unoptimized, DEBUG-conditional app rather than the production binary named by the test and commit, so the thresholds can produce misleading release conclusions. Use a Release-configured test plan or explicitly pass the Release configuration for this budget.

AGENTS.md reference: AGENTS.md:L126-L129

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Blocked in this automation run: the gipity-bot GitHub App can push code but GitHub rejected the required .github/workflows/ci.yml update because its token lacks workflow scope. I left this thread unresolved; a bot or maintainer with workflow-write permission must add the Release-configured budget invocation.

@github-actions

github-actions Bot commented Aug 3, 2026

Copy link
Copy Markdown

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 3, 2026 •

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

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.

@github-actions

github-actions Bot commented Aug 3, 2026

Copy link
Copy Markdown

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 3, 2026 •

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

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: 1

Caution

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

⚠️ Outside diff range comments (4)
apps/unwired-mail/unwired-mail/Features/Account/AccountView.swift (2)

1576-1583: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Guard the launch completion hook.

Line 1583 invokes initialLaunchDidFinish() after multiple awaits without checking cancellation or session freshness. SwiftUI can cancel this task when the view disappears or when the signed-in snapshot changes. Non-throwing operations can still return after cancellation. A stale shell can then satisfy the release measurement.

       inboxViewModel.refreshPinnedBodyPrefetch(connections: gmailViewModel.connections)
+      guard !Task.isCancelled, session.isCurrent(snapshot) else { return }
       initialLaunchDidFinish()

As per path instructions, check cancellation and lifecycle transitions in SwiftUI mail-shell code.

🤖 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-mail/Features/Account/AccountView.swift` around
lines 1576 - 1583, Guard the launch completion flow around
initialLaunchDidFinish() by checking task cancellation and current
session/lifecycle freshness after the awaited mailbox synchronization and reload
operations. Return or skip the completion hook when the SwiftUI task was
cancelled or the signed-in snapshot has changed, while preserving the existing
completion behavior for the active session.

Source: Path instructions


347-376: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Add an identity-token polling regression test.

Change only identityToken and assert that pollWhileActive continues polling.

🤖 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-mail/Features/Account/AccountView.swift` around
lines 347 - 376, Add a regression test for pollWhileActive that mutates only the
session’s identityToken while keeping all other session fields unchanged, then
assert polling continues rather than stopping. Reuse the existing polling test
setup and counters around pollWhileActive; do not alter production behavior or
introduce unrelated session changes.

Source: Path instructions

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

4532-4550: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Assert the cached body content before recording the sample.

The bodyLoaded expectation only observes onLoaded. It does not verify that the selected message returned "Cached body". A stale cache entry or incorrect message identity can therefore pass the performance gate.

Expose the loaded body through the test harness and assert its text and message identity before appending bodyOpenSamples.

As per path instructions, verify externally observable behavior and privacy invariants in tests.

🤖 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/MailboxConnectionAdapterTests.swift`
around lines 4532 - 4550, Update the body-loading test around
MailShellMessageBody and the bodyLoaded expectation to expose the rendered body
and associated message identity through the test harness. Before appending to
bodyOpenSamples, assert that the loaded content is exactly “Cached body” and
belongs to the selected latestMessage, preserving the existing timing
measurement only after these checks pass.

Source: Path instructions


7571-7580: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Require the rendered set to contain only current-mailbox IDs.

The helper returns true when any expected ID appears. It does not reject stale IDs or prove that the rendered dataset changed. A stale previous row plus one new row can satisfy this condition.

Require a non-empty rendered set that intersects the expected IDs and is a subset of the expected set. Add a stable-frame or completeness check appropriate for the lazy list.

Suggested predicate
   for _ in 0..<100 {
     await releaseRenderFrame(view)
-    if !driver.renderedItemIds.isDisjoint(with: expectedIdSet) {
-      return true
+    let renderedItemIds = driver.renderedItemIds
+    if !renderedItemIds.isDisjoint(with: expectedIdSet),
+      renderedItemIds.isSubset(of: expectedIdSet)
+    {
+      await releaseRenderFrame(view)
+      return driver.renderedItemIds.isSubset(of: expectedIdSet)
     }
   }

As per path instructions, verify externally observable behavior and deterministic isolation in tests.

🤖 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/MailboxConnectionAdapterTests.swift`
around lines 7571 - 7580, Update releaseWaitForRenderedThreads to require a
non-empty renderedItemIds set that intersects expectedIdSet and is entirely
contained within it, rejecting stale IDs from other mailboxes. Also add a
stable-frame or completeness check appropriate for the lazy list so the helper
only returns true after the current rendered dataset has settled, and verify the
observable behavior with deterministic test isolation.

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/MailboxConnectionAdapterTests.swift`:
- Around line 4706-4730: Update
testReleaseBudgetDriverIgnoresStaleRendersAfterShellReappears to compare
driver.renderedItemIds, a Set<MailboxThreadIdentity>, against Set([staleId]) and
Set([currentId]) in both XCTAssertEqual assertions.

---

Outside diff comments:
In `@apps/unwired-mail/unwired-mail/Features/Account/AccountView.swift`:
- Around line 1576-1583: Guard the launch completion flow around
initialLaunchDidFinish() by checking task cancellation and current
session/lifecycle freshness after the awaited mailbox synchronization and reload
operations. Return or skip the completion hook when the SwiftUI task was
cancelled or the signed-in snapshot has changed, while preserving the existing
completion behavior for the active session.
- Around line 347-376: Add a regression test for pollWhileActive that mutates
only the session’s identityToken while keeping all other session fields
unchanged, then assert polling continues rather than stopping. Reuse the
existing polling test setup and counters around pollWhileActive; do not alter
production behavior or introduce unrelated session changes.

In `@apps/unwired-mail/unwired-mailTests/MailboxConnectionAdapterTests.swift`:
- Around line 4532-4550: Update the body-loading test around
MailShellMessageBody and the bodyLoaded expectation to expose the rendered body
and associated message identity through the test harness. Before appending to
bodyOpenSamples, assert that the loaded content is exactly “Cached body” and
belongs to the selected latestMessage, preserving the existing timing
measurement only after these checks pass.
- Around line 7571-7580: Update releaseWaitForRenderedThreads to require a
non-empty renderedItemIds set that intersects expectedIdSet and is entirely
contained within it, rejecting stale IDs from other mailboxes. Also add a
stable-frame or completeness check appropriate for the lazy list so the helper
only returns true after the current rendered dataset has settled, and verify the
observable behavior with deterministic test isolation.
🪄 Autofix (Beta)

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: ea78af69-d3c3-4a7c-9264-fcb5760b27f2

📥 Commits

Reviewing files that changed from the base of the PR and between fb0a4ce and 34ac3bd.

📒 Files selected for processing (3)
  • apps/unwired-mail/unwired-mail/Features/Account/AccountView.swift
  • apps/unwired-mail/unwired-mail/Features/Account/RootView.swift
  • apps/unwired-mail/unwired-mailTests/MailboxConnectionAdapterTests.swift
📜 Review details
⏰ Context from checks skipped due to timeout. (1)
  • GitHub Check: Apple · Unwired Mail
🧰 Additional context used
📓 Path-based instructions (3)
apps/unwired-mail/**/*.{swift,mm,m,h}

📄 CodeRabbit inference engine (AGENTS.md)

The Apple app must provide formatter, linter, and test commands; use zsh scripts/check-apple-lint.zsh for formatting and linting and the specified xcodebuild test command for tests.

Files:

  • apps/unwired-mail/unwired-mail/Features/Account/RootView.swift
  • apps/unwired-mail/unwired-mail/Features/Account/AccountView.swift
  • apps/unwired-mail/unwired-mailTests/MailboxConnectionAdapterTests.swift
apps/unwired-mail/**/*.swift

📄 CodeRabbit inference engine (AGENTS.md)

SwiftLint runs in strict mode so warnings fail validation; use mise-managed SwiftLint and Xcode's xcrun swift-format where applicable.

Files:

  • apps/unwired-mail/unwired-mail/Features/Account/RootView.swift
  • apps/unwired-mail/unwired-mail/Features/Account/AccountView.swift
  • apps/unwired-mail/unwired-mailTests/MailboxConnectionAdapterTests.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/RootView.swift
  • apps/unwired-mail/unwired-mail/Features/Account/AccountView.swift
  • apps/unwired-mail/unwired-mailTests/MailboxConnectionAdapterTests.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/MailboxConnectionAdapterTests.swift
🔇 Additional comments (4)
apps/unwired-mail/unwired-mail/Features/Account/AccountView.swift (1)

783-797: LGTM!

Also applies to: 1017-1042, 1049-1051, 1066-1066, 1084-1100, 1118-1118, 1211-1218, 1424-1426, 1587-1595, 3149-3149, 3202-3202

apps/unwired-mail/unwired-mail/Features/Account/RootView.swift (2)

24-25: 🩺 Stability & Availability

Confirm that token-based identity does not reset the mail shell unexpectedly.

ProductAccountSession.revalidateProductAccountAfterForegrounding() can replace the snapshot token while the product account and trusted device remain the same. .id(snapshot.identityToken) then tears down and recreates AccountView, which can cancel mailbox work, reset selection, and rerun launch tasks.

Verify that this reset is required. If only account or device changes require recreation, use a stable session identity and update session-dependent state explicitly. Add a foreground revalidation test.

As per path instructions, check lifecycle transitions and cancellation behavior in the Apple app.

Source: Path instructions


3-13: 🎯 Functional Correctness

No call-site change is required.

The constrained initializer covers both RootView(session:) calls. The test supplies signedInContent, and no caller stores the old concrete type.

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

4303-4359: LGTM!

Also applies to: 4361-4385, 4415-4426, 4435-4484, 4590-4617, 8168-8246, 8286-8288

@rajzik
rajzik merged commit 39f8bef into main Aug 4, 2026
6 of 7 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.

Measure production mail-shell release budgets

1 participant