Skip to content

Preserve complete chronological notification history - #7875

Closed
azooz2003-bit wants to merge 95 commits into
mainfrom
task-notification-chronological-feed
Closed

azooz2003-bit wants to merge 95 commits into
mainfrom
task-notification-chronological-feed

Conversation

@azooz2003-bit

@azooz2003-bit azooz2003-bit commented Jul 11, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

  • retain every accepted notification as exactly one feed row keyed by its stable UUID
  • sort newest-first by initial acceptance time, then UUID text for simultaneous timestamps
  • preserve identity and chronology across late delivery, replay, reconnect, restore, and workspace replacement
  • keep external banner ownership separate from retained feed history

Product contract

An accepted notification appears exactly once. Its initial acceptance timestamp and UUID never change during policy evaluation, queueing, replay, reconnect, or restore. The feed sorts by acceptance timestamp descending and UUID text ascending, so late older arrivals insert at their chronological position and simultaneous timestamps remain deterministic. Duplicate UUIDs are idempotent replays. Corrupt restored arrays with repeated UUIDs preserve the first row deterministically. Read, open, unread navigation, and banner supersession do not reorder or delete feed history. Session replacement remaps known workspace and surface identities while retaining unmapped identities. Queue saturation rejects before acceptance with an explicit retry result; an accepted event is not dropped.

Acceptance coverage

  • distinct repeated payloads remain distinct rows
  • duplicate UUID replay remains one row, including effects-only replay
  • late arrivals insert without replacing newer rows
  • equal timestamps use a stable UUID tie-break
  • restored and live rows merge once in batch-independent order
  • pending delivery, reliable admission, unread state, banner ownership, and phone-dismiss buffers transfer across session replacement
  • source-confined relay notifications stay in their authorized workspace while live-owner notifications follow pane moves
  • read, open, dismiss, and unread navigation preserve chronology until explicit clear or row deletion
  • notification history has no feed cap

Verification

  • final head: 0b16a20eb413f1b21594ccec2ae8b633247c64a2
  • final focused notification matrix: 42/42 passed
  • chronology, scale, and restore ownership: 19/19 passed
  • control execution policy: 20/20 passed
  • socket notification actions: 7/7 passed in isolated fresh processes
  • retargeting integration: 3/3 passed, plus focused indicator and stale-source-clear cases 1/1 each
  • banner-owner transfer and rebind collisions: regression red on old code, then 7/7 banner and session-transfer cases passed
  • scale profile: 2,000 existing plus 500 inserted passed in 18.249 seconds
  • PBX normalization, test wiring, Swift file budget, and whitespace checks passed
  • artifacts: cmux-assets/task-notification-chronological-feed/nfeed5/tests/

The canonical upstream agent-notification host run passed 35/36 feature-relevant cases after isolated reruns. The remaining unchanged PID telemetry test fails before product resolution because its shell subprocess does not execute its SIGUSR1 trap or write the marker on this host; its isolated diagnostic is retained in final-upstream-pid-telemetry-isolated.xcresult.

Regression provenance remains split into test-only and repair commits where applicable. Runtime dogfood approval is required before merge.

Summary by CodeRabbit

  • New Features
    • Notification feed now retains each accepted notification by stable identity with deterministic newest-first ordering and correct replay/restore behavior.
    • Improved session restore/transfer to preserve notification identity, unread state, and external banner ownership (including duplicated IDs).
    • notification.list now uses a context-aware snapshot suitable for unread navigation.
  • Bug Fixes
    • Notification queue “saturated” outcomes are no longer treated as success; the UI shows “queue saturated—retry”.
    • Repeated queued notifications for the same workspace/surface are retained instead of coalesced.
  • Documentation
    • Updated guidance on feed ordering/identity and restore/replay semantics.

@vercel

vercel Bot commented Jul 11, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
cmux Ready Ready Preview, Comment Jul 16, 2026 1:40am
cmux-staging Building Building Preview, Comment Jul 16, 2026 1:40am

@coderabbitai

coderabbitai Bot commented Jul 11, 2026 •

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Notification handling now preserves stable UUIDs and acceptance timestamps, orders feed entries deterministically, reports explicit queue backpressure outcomes, merges restored session state, tracks external-banner ownership, and updates control-socket listing, UI projections, tests, documentation, localization, and project wiring.

Changes

Notification queue and delivery

Layer / File(s) Summary
Stable queued identity and backpressure
Sources/TerminalNotificationQueue*, Sources/AgentNotificationDelivery.swift, Sources/TerminalMutationBus+*, Sources/TerminalController.swift
Queued notifications retain identity and acceptance metadata, coalescing is removed, indexed FIFO draining is used, and enqueue outcomes distinguish gated, accepted, saturated, and cancelled states.
Notification storage and chronology
Sources/TerminalNotificationStore*, Sources/NotificationCooldownReservations.swift, Sources/AppDelegate+NotificationNavSeams.swift, Sources/App/MenuBarExtraController.swift, Sources/Update/UpdateTitlebarAccessory.swift
Stable IDs, accepted timestamps, deterministic indexes, replay deduplication, cooldown reservations, unread projections, native delivery, and feed ordering are updated.
Session restoration and banner ownership
Sources/SessionNotificationSnapshot.swift, Sources/ExternalNotificationBannerOwnership.swift, Sources/SupersededPhoneDismissBuffer.swift, Sources/TabManager*, Sources/Workspace.swift
Restored notifications are canonicalized and merged while queued mutations, workspace locations, banner ownership, and superseded-dismiss state are transferred.
Control-socket listing and validation
Packages/macOS/CmuxControlSocket/..., cmuxTests/*, cmux.xcodeproj/project.pbxproj, docs/notifications.md, Resources/Localizable.xcstrings
Notification listing uses one main-actor hop with typed timestamps and cached references; tests, project registration, documentation, localization, policy assertions, and diagnostics cover the updated behavior.

Estimated code review effort: 5 (Critical) | ~120 minutes

Sequence Diagram(s)

sequenceDiagram
  participant TerminalController
  participant AgentNotificationDelivery
  participant TerminalMutationBus
  participant TerminalNotificationStore
  participant Workspace
  TerminalController->>AgentNotificationDelivery: enqueue notification
  AgentNotificationDelivery->>TerminalMutationBus: return delivery outcome
  TerminalMutationBus->>TerminalNotificationStore: deliver stable id and acceptedAt
  Workspace->>TerminalNotificationStore: merge restored notifications
  TerminalNotificationStore->>TerminalNotificationStore: deduplicate and order chronologically
Loading

Possibly related PRs

Suggested reviewers: lawrencecchen, austinywang


Important

Pre-merge checks failed

Please resolve all errors before merging. Addressing warnings is optional.

❌ Failed checks (7 errors, 1 warning)

Check name Status Explanation Resolution
Cmux Swift Concurrency ❌ Error PR adds an internal serial DispatchQueue plus withCheckedContinuation for reliable notification admission in TerminalMutationBus; that's a new legacy async worker, not a boundary. Refactor the reliable-admission path to an actor/async semaphore-style design; avoid the dedicated GCD queue and continuation bridge in production code.
Cmux Swift @Concurrent ❌ Error New nonisolated async queue-wait helpers (enqueueNotificationReliably and its wrapper) lack @concurrent, so they can inherit caller isolation instead of leaving it. Add @concurrent to the off-actor async delivery helpers, or move the waiting/queue work into a detached worker/actor so UI callers don't inherit it.
Cmux Swift File And Package Boundaries ❌ Error Sources/TerminalNotificationStore.swift is already 2201 lines; the PR adds 365 lines there while keeping UI, persistence, banner ownership, cooldown, and policy logic together. Move the retained-history/session-restore/banner-ownership logic into a small CmuxNotificationCore SwiftPM target (or equivalent focused files) and keep TerminalNotificationStore as thin glue.
Cmux Swift Logging ❌ Error TerminalNotificationStore changed terminalNotificationLogger from nonisolated private let to nonisolated let inside an @MainActor type, violating the Swift logging rule. Restore the logger to nonisolated private let (or a private nested logger) and keep diagnostics in allowed unified/debug logging only.
Cmux Full Internationalization ❌ Error Resources/Localizable.xcstrings has 20 supported locales, but the new user-facing key notification.queue.error.saturated only has en/ja translations. Add translated values for every existing locale in Resources/Localizable.xcstrings (or remove the key until all locales are covered).
Cmux Architecture Rethink ❌ Error TerminalMutationBus adds NSCondition/lock.wait plus a serial admission queue to block on saturation, which is a new timing repair path. Move notification admission to one non-blocking owner/actor with explicit capacity state; reject or hand off without waits/locks.
Cmux No Ambient Global State ❌ Error Sources/TerminalNotificationQueueTypes.swift adds enum TerminalNotificationQueueErrorMessages { static var saturated ... }, a new namespace-only top-level type forbidden by the rule. Replace it with a fileprivate constant or move the message onto an owned type; avoid introducing new namespace-only file-scope API.
Docstring Coverage ⚠️ Warning Docstring coverage is 9.55% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (17 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and clearly summarizes the main change: preserving notification history chronologically.
Description check ✅ Passed The description covers the main change, product contract, acceptance coverage, and verification, though it doesn't follow every template heading exactly.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Cmux Swift Actor Isolation ✅ Passed No new actor-isolation violation: the main stores remain @MainActor, new shared bus state is lock-protected, and no new async protocol or unsafe Sendable reference type was introduced.
Cmux Swift Blocking Runtime ✅ Passed PASS: The only production diffs are a banner-owner guard and a conditional rebind path; no new semaphores, sleeps, main-queue sync, polling, or locks were introduced.
Cmux Browser Automation Off-Main ✅ Passed PR only changes notification queue/store paths; browser.* commands stay in socketWorkerMethods with policy tests, and no browser automation routing moved to mainActor.
Cmux Expensive Synchronous Load ✅ Passed PASS: The only RestorableAgentSessionIndex.load() uses are pre-existing cold-cache fallbacks; parent diff is identical, and the new notification/socket changes add no main-actor history loads.
Cmux Cache Substitution Correctness ✅ Passed PASS: the substitutions are event-driven and cold-safe; unread-navigation cache is invalidated on notification mutations, and the menu snapshot falls back to direct counting.
Cmux No Hacky Sleeps ✅ Passed The diff is Swift-only plus docs/project files; no TypeScript/JS/shell/build-runtime script changes introduced fixed waits or hacky sleeps.
Cmux Algorithmic Complexity ✅ Passed Hot notification UI paths now use cached indexes/projections; queue draining is amortized FIFO, and the remaining restore-time scans are linear and benchmarked by tests.
Cmux Swiftpm Lockfiles ✅ Passed Only cmux.xcodeproj file refs changed; no SwiftPM package refs, no .gitignore/Package.swift edits, and no Package.resolved diffs were needed.
Cmux User-Facing Error Privacy ✅ Passed PASS: The new user-facing notification error is a generic queue-saturation retry message; no vendor/provider names or sensitive details appear in production output.
Cmux Swiftui State Layout ✅ Passed Changed SwiftUI code only swaps to cached ID snapshots and immutable row data; no new ObservableObject/@published, GeometryReader, or render-time state writes were introduced.
Cmux Swift Auxiliary Window Close Shortcuts ✅ Passed The PR only changes notification-store logic and tests; no new standalone NSWindow/WindowGroup/NSPanel code or cmux.* close-shortcut wiring appears in the diff.
Cmux Source Artifacts ✅ Passed All changed paths are intentional source/docs/tests/config/localization files; none are artifact/cache/log/temp paths, so the source-control-artifacts rule is not violated.
Cmux No Test Or Debug Seam In Production Source ✅ Passed No new ForTesting/debug-only seam lines were added in changed Sources/ files; the diff only adjusts notification delivery/banner ownership logic.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch task-notification-chronological-feed

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@greptile-apps

greptile-apps Bot commented Jul 11, 2026 •

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR preserves complete notification history while separating feed order from external delivery state. The main changes are:

  • Stable UUID-based notification identity and deterministic chronological sorting.
  • Idempotent replay, restore merging, and session identity remapping.
  • Separate ownership tracking for native and phone banners.
  • Reliable queue admission with explicit saturation handling.
  • Updated unread navigation, socket listing, persistence, and focused tests.

Confidence Score: 5/5

This looks safe to merge.

  • The cooldown fixes keep committed timestamps monotonic without treating provisional reservations as accepted events.
  • External banner ownership is tracked separately from feed chronology across restore, transfer, rebind, and collisions.
  • Unmapped surface identities and legacy ownership snapshots retain their intended behavior.
  • No blocking issue was confirmed in the updated code.

Important Files Changed

Filename Overview
Sources/TerminalNotificationStore.swift Integrates chronological history, replay handling, cooldown admission, banner ownership, and session-state transfer.
Sources/ExternalNotificationBannerOwnership.swift Tracks external banner owners independently from feed rows and resolves transfer and rebind collisions.
Sources/NotificationCooldownReservations.swift Separates provisional cooldown reservations from monotonic committed timestamps.
Sources/SessionNotificationSnapshot.swift Preserves notification identity and location through restore and workspace replacement.
Sources/SupersededPhoneDismissBuffer.swift Transfers deferred phone dismissals while retaining unmapped surface identities.

Reviews (60): Last reviewed commit: "Fix agent resume liveness test initializ..." | Re-trigger Greptile

Comment thread Sources/TerminalNotificationStore.swift
Comment thread Sources/TerminalNotificationStore.swift Outdated

@coderabbitai coderabbitai 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.

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

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

Stale coalesces=0 in the enqueue debug log.

Coalescing was removed from enqueueNotification, so the hardcoded coalesces=0 in this DEBUG log is now misleading. Drop it to avoid confusion when tracing the enqueue path.

🧹 Proposed cleanup
         cmuxDebugLog(
-            "socket.notifyTargetAsync.enqueue workspace=\(tabId.uuidString.prefix(8)) surface=\(surfaceId.uuidString.prefix(8)) titleLen=\(title.count) subtitleLen=\(subtitle.count) bodyLen=\(body.count) coalesces=0"
+            "socket.notifyTargetAsync.enqueue workspace=\(tabId.uuidString.prefix(8)) surface=\(surfaceId.uuidString.prefix(8)) titleLen=\(title.count) subtitleLen=\(subtitle.count) bodyLen=\(body.count)"
         )
🤖 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 `@Sources/TerminalController.swift` around lines 12318 - 12328, Remove the
obsolete hardcoded “coalesces=0” field from the DEBUG log in the notification
enqueue path within the surrounding notification method, while preserving the
workspace, surface, and length details.
cmuxTests/WorkspaceManualUnreadTests.swift (1)

525-575: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Update the remaining test that still expects reordering.

markLatestNotificationAsOldestUnread now preserves feed order (Lines 1394-1398), but testMarkLatestNotificationAsOldestUnreadAppendsWhenNoOtherUnreadNotificationsRemain at Lines 1182-1219 still expects the old append behavior. The suite will fail.

-        XCTAssertEqual(store.notifications.map(\.id), [readNotificationId, currentNotificationId])
-        XCTAssertFalse(store.notifications.last?.isRead ?? true)
+        XCTAssertEqual(store.notifications.map(\.id), [currentNotificationId, readNotificationId])
+        XCTAssertFalse(store.notifications.first?.isRead ?? true)
🤖 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 `@cmuxTests/WorkspaceManualUnreadTests.swift` around lines 525 - 575, Update
testMarkLatestNotificationAsOldestUnreadAppendsWhenNoOtherUnreadNotificationsRemain
to expect the existing chronological feed order after
markLatestNotificationAsOldestUnread, rather than expecting the marked
notification to be appended. Adjust its notification ID/order assertions to
match the preserved order while retaining the unread-state verification.
Sources/TerminalNotificationStore.swift (1)

858-872: 🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

Make cooldown reservations ownership-aware across asynchronous completions.

Reservations are written before policy evaluation. If two same-key notifications are in flight, a later/replayed notification can record first, then an earlier completion reaches the duplicate guard and restores its stale previousDate, erasing the committed cooldown. The acceptedAt commits can also move a newer cooldown date backward when completion order differs.

Track a reservation owner/generation and only restore or commit while that operation still owns the slot; keep the stored cooldown date monotonic. Add a reversed-completion regression test.

Also applies to: 881-940, 1098-1104, 1119-1164

🤖 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 `@Sources/TerminalNotificationStore.swift` around lines 858 - 872, Make
cooldown reservations ownership-aware across asynchronous completion paths in
the notification store. Update makeCooldownReservation and the related
duplicate-guard, restoration, and acceptedAt commit logic (including the
additional referenced sections) to track a reservation owner/generation, only
restore or commit when the completing operation still owns the slot, and never
move a stored cooldown date backward. Add a regression test covering reversed
completion order for same-key notifications.
🤖 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 `@Sources/CmuxEventPublishing.swift`:
- Around line 299-301: Update the debug label in publishNotificationChanges to
reference the actual deduplication methods, recordNotification and
restoreSessionNotifications, instead of the nonexistent
TerminalNotificationStore.recordAndRestoreDeduplication identifier.

---

Outside diff comments:
In `@cmuxTests/WorkspaceManualUnreadTests.swift`:
- Around line 525-575: Update
testMarkLatestNotificationAsOldestUnreadAppendsWhenNoOtherUnreadNotificationsRemain
to expect the existing chronological feed order after
markLatestNotificationAsOldestUnread, rather than expecting the marked
notification to be appended. Adjust its notification ID/order assertions to
match the preserved order while retaining the unread-state verification.

In `@Sources/TerminalController.swift`:
- Around line 12318-12328: Remove the obsolete hardcoded “coalesces=0” field
from the DEBUG log in the notification enqueue path within the surrounding
notification method, while preserving the workspace, surface, and length
details.

In `@Sources/TerminalNotificationStore.swift`:
- Around line 858-872: Make cooldown reservations ownership-aware across
asynchronous completion paths in the notification store. Update
makeCooldownReservation and the related duplicate-guard, restoration, and
acceptedAt commit logic (including the additional referenced sections) to track
a reservation owner/generation, only restore or commit when the completing
operation still owns the slot, and never move a stored cooldown date backward.
Add a regression test covering reversed completion order for same-key
notifications.
🪄 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: ee8227dc-e4fa-4fcf-a898-9e8c8c0cf459

📥 Commits

Reviewing files that changed from the base of the PR and between fe87e60 and da2f0f7.

📒 Files selected for processing (10)
  • Sources/CmuxEventPublishing.swift
  • Sources/TerminalController.swift
  • Sources/TerminalNotificationQueue.swift
  • Sources/TerminalNotificationStore.swift
  • cmux.xcodeproj/project.pbxproj
  • cmuxTests/NotificationChronologyTests.swift
  • cmuxTests/SessionPersistenceTests.swift
  • cmuxTests/TerminalNotificationQueueTests.swift
  • cmuxTests/WorkspaceManualUnreadTests.swift
  • docs/notifications.md

Comment thread Sources/CmuxEventPublishing.swift Outdated
Comment thread Sources/TerminalNotificationStore.swift Outdated

@coderabbitai coderabbitai 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.

Actionable comments posted: 1

🤖 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 `@cmuxTests/NotificationChronologyTests.swift`:
- Around line 234-263: Update
reversedCooldownCompletionKeepsNewestReservationMonotonic to commit the later
reservation before the earlier one, asserting the stored date remains laterDate
after each completion; then restore the earlier reservation only if needed to
exercise its completion/rollback path and verify dates[key] still equals
laterDate.
🪄 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: 1043f4ce-3e13-4297-b6ab-856e85a8f4ac

📥 Commits

Reviewing files that changed from the base of the PR and between da2f0f7 and 832b5c9.

📒 Files selected for processing (9)
  • Sources/AppDelegate+NotificationNavSeams.swift
  • Sources/CmuxEventPublishing.swift
  • Sources/NotificationCooldownReservations.swift
  • Sources/TerminalController.swift
  • Sources/TerminalNotificationQueue.swift
  • Sources/TerminalNotificationStore.swift
  • cmux.xcodeproj/project.pbxproj
  • cmuxTests/NotificationChronologyTests.swift
  • cmuxTests/WorkspaceManualUnreadTests.swift

Comment thread cmuxTests/NotificationChronologyTests.swift
Comment thread Sources/TerminalNotificationStore.swift
@azooz2003-bit

Copy link
Copy Markdown
Collaborator Author

Addressed the CodeRabbit algorithmic-complexity finding in 9337ae7. notificationsForUnreadNavigation is now an O(1) cached projection rebuilt only when store state mutates, alongside the store’s existing O(n) index rebuild. NotificationChronologyTests pass 10/10.

@coderabbitai coderabbitai 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.

Actionable comments posted: 4

Caution

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

⚠️ Outside diff range comments (2)
cmuxTests/TerminalNotificationQueueTests.swift (1)

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

Avoid asserting feed order from back-to-back wall-clock timestamps.

TerminalNotificationQueue.enqueueNotification creates each notification with acceptedAt: Date(). If two enqueues receive the same timestamp, store ordering falls back to UUID text, so these ["Second", "First"] / ["Fresh unrelated", "Stale unrelated"] assertions can fail nondeterministically. Use an injected test clock, or compare the retained rows as a set when chronology is not the behavior under test.

As per coding guidelines, tests must avoid real wall-clock dependencies and use injected virtual clocks for time-driven behavior.

Also applies to: 355-359, 424-428

🤖 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 `@cmuxTests/TerminalNotificationQueueTests.swift` around lines 304 - 307,
Update the affected TerminalNotificationQueue tests to avoid relying on Date()
ordering: inject and advance a deterministic test clock before enqueueing
notifications when chronology is under test, or compare retained notification
rows without order when chronology is irrelevant. Apply this consistently to the
assertions around workspaceNotifications, the unrelated notification assertions,
and the additional occurrence near lines 424-428; preserve the existing
title/content expectations.

Source: Coding guidelines

Sources/TerminalNotificationStore.swift (1)

385-410: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Rebuild the unread-navigation cache when deferred IDs change.

The cache is rebuilt only from notifications.didSet, but operations such as markLatestNotificationAsOldestUnread update deferredUnreadNavigationIds afterward. The requested navigation order therefore remains stale until another notification mutation.

Proposed fix
-    private var deferredUnreadNavigationIds: [UUID] = []
+    private var deferredUnreadNavigationIds: [UUID] = [] {
+        didSet {
+            guard oldValue != deferredUnreadNavigationIds else { return }
+            rebuildUnreadNavigationNotifications()
+        }
+    }
🤖 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 `@Sources/TerminalNotificationStore.swift` around lines 385 - 410, Update every
mutation of deferredUnreadNavigationIds, especially
markLatestNotificationAsOldestUnread, to call
rebuildUnreadNavigationNotifications() after changing the IDs so
notificationsForUnreadNavigation immediately reflects the new navigation order.
Keep the existing notifications.didSet rebuild behavior unchanged.
🤖 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 `@cmuxTests/NotificationChronologyTests.swift`:
- Around line 63-107: Update lateOlderRecordDoesNotReplaceNewerExternalDelivery
to save the current AppFocusState.overrideIsFocused value, set a deterministic
unfocused override before adding notifications, and restore the original value
in the existing defer cleanup alongside the store handlers and notifications.

In `@Sources/NotificationCooldownReservations.swift`:
- Around line 3-13: Mark both TerminalNotificationStore.externalBannerTransition
and notificationSortPrecedes as nonisolated, preserving their pure comparison
behavior while allowing callers to invoke them without inheriting the `@MainActor`
isolation boundary.

In `@Sources/TerminalController.swift`:
- Around line 12328-12329: Replace the synchronous v2MainSync call in the
backpressure closure with an asynchronous capacity signal or continuation that
lets the socket worker yield without blocking on MainActor. Preserve the
existing TerminalMutationBus.shared.drainForBackpressure behavior while ensuring
queue saturation cannot require a synchronous main-actor round trip.

In `@Sources/TerminalController`+ControlNotificationContext.swift:
- Around line 4-25: Remove the
TerminalMutationBus.shared.discardPendingNotifications call from
deliverNotificationSynchronously so accepted pending notifications remain
preserved. Keep synchronous delivery routed through
TerminalNotificationStore.shared.addNotification, allowing acceptedAt ordering
to place the new notification correctly without target-level coalescing.

---

Outside diff comments:
In `@cmuxTests/TerminalNotificationQueueTests.swift`:
- Around line 304-307: Update the affected TerminalNotificationQueue tests to
avoid relying on Date() ordering: inject and advance a deterministic test clock
before enqueueing notifications when chronology is under test, or compare
retained notification rows without order when chronology is irrelevant. Apply
this consistently to the assertions around workspaceNotifications, the unrelated
notification assertions, and the additional occurrence near lines 424-428;
preserve the existing title/content expectations.

In `@Sources/TerminalNotificationStore.swift`:
- Around line 385-410: Update every mutation of deferredUnreadNavigationIds,
especially markLatestNotificationAsOldestUnread, to call
rebuildUnreadNavigationNotifications() after changing the IDs so
notificationsForUnreadNavigation immediately reflects the new navigation order.
Keep the existing notifications.didSet rebuild behavior unchanged.
🪄 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: 9c24d086-8324-47e7-884d-26ff3013341f

📥 Commits

Reviewing files that changed from the base of the PR and between 093280f and 305de95.

📒 Files selected for processing (9)
  • Sources/NotificationCooldownReservations.swift
  • Sources/TerminalController+ControlNotificationContext.swift
  • Sources/TerminalController+ControlSidebarContextSupport.swift
  • Sources/TerminalController.swift
  • Sources/TerminalNotificationQueue.swift
  • Sources/TerminalNotificationStore.swift
  • cmuxTests/NotificationChronologyTests.swift
  • cmuxTests/TerminalNotificationClearAllTests.swift
  • cmuxTests/TerminalNotificationQueueTests.swift

Comment thread cmuxTests/NotificationChronologyTests.swift
Comment thread Sources/NotificationCooldownReservations.swift
Comment thread Sources/TerminalController.swift Outdated
Comment thread Sources/TerminalController+ControlNotificationContext.swift Outdated
Comment thread Sources/SessionNotificationSnapshot.swift
cmux reload-cloud added 2 commits July 12, 2026 20:26
Comment thread Sources/SessionPersistence.swift
cmux reload-cloud added 4 commits July 15, 2026 12:41
…onological-feed

# Conflicts:
#	Resources/Localizable.xcstrings
#	Sources/TerminalNotificationPolicyInFlightStore.swift
#	Sources/TerminalNotificationStore.swift
#	cmux.xcodeproj/project.pbxproj
@cursor

cursor Bot commented Jul 15, 2026

Copy link
Copy Markdown

Bugbot is paused — on-demand spend limit reached

Bugbot uses usage-based billing for this team and has hit its on-demand spend limit.

A team admin can raise the spend limit in the Cursor dashboard, or wait for the next billing cycle to continue.

cmux reload-cloud and others added 4 commits July 15, 2026 14:26
* test(ios): cover reconnect overlap cleanup

* fix(ios): retire superseded reconnect sessions

* test(ios): isolate saved dev Mac instances

* fix(ios): enforce build compatibility boundaries

* test(ios): cover startup status auth race

* fix(ios): reuse connect token for identity check

* test(auth): preserve selected team during refresh outage

* fix(auth): keep selected team effective during startup

* test(auth): keep cached sessions restoring until ready

* fix(ios): wait for auth restore before reconnect

* test(ios): cover compatibility review regressions

* fix(ios): address compatibility review findings

* test(ios): use deterministic compatibility timestamps

---------

Co-authored-by: cmux reload-cloud <cmux-reload-cloud@users.noreply.github.com>
# Conflicts:
#	Packages/iOS/CmuxMobileShell/Sources/CmuxMobileShell/IOSBuildScopedPairedMacStore.swift
@cursor

cursor Bot commented Jul 17, 2026

Copy link
Copy Markdown

Bugbot is paused — on-demand spend limit reached

Bugbot uses usage-based billing for this team and has hit its on-demand spend limit.

A team admin can raise the spend limit in the Cursor dashboard, or wait for the next billing cycle to continue.

@cursor

cursor Bot commented Jul 17, 2026

Copy link
Copy Markdown

Bugbot is paused — on-demand spend limit reached

Bugbot uses usage-based billing for this team and has hit its on-demand spend limit.

A team admin can raise the spend limit in the Cursor dashboard, or wait for the next billing cycle to continue.

activeDatesByOwner: [:]
)
state.activeDatesByOwner[reservation.owner] = acceptedAt
stateByKey[key] = state

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.

P1 Separate provisional cooldowns

This writes an active reservation into the same timestamp used by the admission gate. If notification A reserves the key while its asynchronous policy check runs, notification B arriving within the interval is rejected immediately. When A later fails policy and restores its reservation, B has already been discarded even though no notification committed the cooldown. Keep provisional reservations separate from the committed timestamp, or retain arrivals blocked only by provisional state for reevaluation.

@cursor

cursor Bot commented Jul 22, 2026

Copy link
Copy Markdown

Bugbot is paused — on-demand spend limit reached

Bugbot uses usage-based billing for this team and has hit its on-demand spend limit.

A team admin can raise the spend limit in the Cursor dashboard, or wait for the next billing cycle to continue.

@lawrencecchen lawrencecchen added the stale-revisit Closed after 30+ days without activity; preserved for possible revisit or reopening. label Sep 23, 2026
@github-project-automation github-project-automation Bot moved this from Todo to Done in cmux backlog Sep 23, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

stale-revisit Closed after 30+ days without activity; preserved for possible revisit or reopening.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants