Skip to content

Huge Ade Mobile Pass#320

Merged
arul28 merged 2 commits into
mainfrom
ade/huge-ade-mobile-pass-831383b3
May 19, 2026
Merged

Huge Ade Mobile Pass#320
arul28 merged 2 commits into
mainfrom
ade/huge-ade-mobile-pass-831383b3

Conversation

@arul28
Copy link
Copy Markdown
Owner

@arul28 arul28 commented May 19, 2026

Summary

Describe the change.

What Changed

Key files and behaviors.

Validation

How you tested.

Risks

Anything to watch.

Summary by CodeRabbit

Release Notes

  • New Features

    • Added auto permission mode for Claude CLI sessions
    • Added reasoning effort support for CLI session creation
    • New PR queue/automation commands for remote work management
    • Terminal output search in Work sessions
    • Queued message dispatch with optimistic state tracking
    • Test push notification fallback via in-app messaging when APNs unavailable
  • Improvements

    • Enhanced session metadata handling and persistence
    • Improved chat transcript coalescing and transcript limit increases
    • Terminal buffer management and viewport revision deduplication
    • Merged assistant text events for cleaner message display
    • Better lane and project resolution for navigation
  • UI Enhancements

    • Replaced menu pickers with scrollable option button selections across iOS
    • Custom tab bar with work session badge counts
    • Improved file and lane selection interfaces
    • Phone-optimized terminal input composition
    • New attention drawer item dismissal capability
  • Bug Fixes

    • Fixed stale notification preference cleanup
    • Improved truncated file diff handling
    • Better chat session pending input tracking
    • Corrected archived session filtering

Review Change Stack

Greptile Summary

This PR delivers a large batch of iOS mobile improvements alongside backend changes: queued/optimistic message steering, terminal output search, PR queue automation commands, auto permission mode for Claude CLI sessions, and UX refinements across the Work, Lanes, and Settings tabs.

  • iOS Work view: optimistic pending-steer display, send-to-steer fallback on active turns, adaptive composer layout, archivedAt-aware session filtering, and a new work-session deep-link navigation request.
  • Backend / CLI: new auto permission mode, reasoningEffort propagation to CLI session start, CRSQL primary-key normalisation for incoming sync deltas, and new PR queue/automation remote commands.
  • Sync infrastructure: sendTestPush upgraded to async with in-app fallback notification delivery, notification-preferences pruning on save, and per-table-schema caching improvements for outgoing CRDTs.

Confidence Score: 4/5

Safe to merge with care — the main risk is terminal subscriptions accumulated silently during search, and the CRSQL batch abort on unsupported PK shapes (unchanged from a prior review).

The two issues found are non-blocking: terminal subscriptions from search are never cleaned up (ongoing data push after search clears) and pragma table_info is queried once per incoming change row rather than once per table per batch. Neither breaks correctness, but both add avoidable load. All other new paths — steer fallback, in-app notification delivery, optimistic archive state, and CRSQL PK normalisation — look logically correct.

apps/ios/ADE/Views/Work/WorkRootScreen+Actions.swift (terminal subscription cleanup) and apps/desktop/src/main/services/state/kvDb.ts (pragma table_info per-row cost)

Important Files Changed

Filename Overview
apps/desktop/src/main/services/state/kvDb.ts Adds CRSQL primary-key normalisation for incoming deltas and a packed-key encoder mirroring the iOS side; pragma table_info is queried per-row without caching. The throw-on-unsupported-shape behaviour (flagged in a prior review) is unchanged.
apps/ios/ADE/Services/SyncService.swift Large set of improvements: queued steer delivery state, refreshTerminalSnapshot refactor, in-app notification fallback via UNUserNotificationCenter, async sendTestPush, improved project selection ordering, chat-event deduplication before revision bump, and workspace-snapshot revision tracking.
apps/ios/ADE/Services/Database.swift Adds pending_input_item_id and archived_at columns to terminal_sessions, consolidated updateSessionMeta, multi-project-scope artifact queries, and outgoing CRSQL PK encoding. Previous empty-title guard-early-return bug was addressed in this PR.
apps/ios/ADE/Views/Work/WorkRootScreen+Actions.swift Adds hydrateSearchOutputBuffersIfNeeded for terminal output search, applyArchivedSessionOverride for optimistic archive state, and navigation helpers. Terminal subscriptions created for search are never cleaned up when search is cleared.
apps/ios/ADE/Views/Work/WorkChatSessionView.swift Adds optimistic pending-steer merging, awaiting-prompt-details notice, adaptive jump-pill padding, removes inline pending-input banner, drops latestVisibleAssistantMessageId from timeline presentation, and adds navigation/composer backdrop gradients.
apps/ios/ADE/Views/Work/WorkSessionDestinationView+Actions.swift Sends steer when session is active, falls back from send to steer on active-turn error, updates optimistic steer list on cancel/edit/dispatch, and consolidates permission-mode wire-field resolution.
apps/desktop/src/main/services/chat/agentChatService.ts Adds transcript coalescing for multi-part assistant text events, pendingInputItemId computation for session summaries, includeArchived filter for listSessions, and increases MAX_TRANSCRIPT_READ_CHARS to 120k.
apps/desktop/src/shared/cliLaunch.ts Adds auto permission mode, resolveCleanShellLaunchFields for platform-aware clean shell launch, deriveTrackedCliInitialInputSessionMeta for auto-titling from initial prompt, and resolveCodexCliModelForLaunch to strip the openai/ vendor prefix.
apps/ade-cli/src/services/sync/syncRemoteCommandService.ts Registers new PR queue/automation commands, migrates cto.getAgentCoreMemory from heartbeat to agent service, makes linearCredentialService optional, adds reasoningEffort and modelId to CLI session start, and changes chat.send to awaitDispatch: false.
apps/ios/ADE/Models/NotificationPreferences.swift Adds pruningInactivePerSessionOverrides to drop no-op per-session entries before saving, keeping stored preferences lean.

Sequence Diagram

sequenceDiagram
    participant iOS as iOS Client
    participant WS as WebSocket Sync
    participant Desktop as Desktop Host

    Note over iOS,Desktop: New: Send-to-Steer flow
    iOS->>WS: chat.send (awaitDispatch: false)
    WS-->>iOS: "{ok: true}"
    iOS->>iOS: updateLocalEcho(.sent)
    iOS->>WS: refreshChatState (forceRemote)

    alt Turn already active error
        iOS->>WS: chat.steer
        WS-->>iOS: "{ok: true, queued: true, steerId}"
        iOS->>iOS: upsertOptimisticPendingSteer(steerId)
    end

    Note over iOS,Desktop: New: In-app notification fallback
    Desktop->>WS: "in_app_notification {title, body, deepLink}"
    WS->>iOS: presentInAppNotification()
    iOS->>iOS: UNUserNotificationCenter.add(request)

    Note over iOS,Desktop: New: Work-session deep-link navigation
    Desktop->>WS: push WorkSessionNavigationRequest
    WS->>iOS: requestedWorkSessionNavigation published
    iOS->>iOS: handleRequestedWorkSessionNavigation()
    iOS->>iOS: NavigationPath.append(WorkSessionRoute)
Loading

Comments Outside Diff (1)

  1. apps/ios/ADE/Views/Work/WorkRootScreen.swift, line 93-114 (link)

    P1 Cross-device unarchive no longer clears stale local storage

    The old archivedSessionIds logic only included a locally-cached ID when the remote didn't know about the session (!remoteKnownIds.contains(id)). This ensured that once a remote chatSummaries entry appeared with archivedAt == nil, the local storage fallback was suppressed. The new resolvedWorkArchivedSessionIds unconditionally unions local storage with remote data, so any ID that remains in archivedSessionIdsStorage — even one the remote explicitly unarchived — continues to mark the session as archived on this device.

    Concrete failure: user archives session X on iPhone A → iPhone B or the desktop unarchives it via unarchiveChatSession → iPhone A reloads and receives summaries with archivedAt == nil, but its AppStorage still contains session.id. The new code returns that ID as archived; the old code filtered it out because the remote knew the session. iPhone A will continue hiding the session in the archived list until the user explicitly archives/unarchives it again on that device, clearing local storage.

    Prompt To Fix With AI
    This is a comment left during a code review.
    Path: apps/ios/ADE/Views/Work/WorkRootScreen.swift
    Line: 93-114
    
    Comment:
    **Cross-device unarchive no longer clears stale local storage**
    
    The old `archivedSessionIds` logic only included a locally-cached ID when the remote didn't know about the session (`!remoteKnownIds.contains(id)`). This ensured that once a remote `chatSummaries` entry appeared with `archivedAt == nil`, the local storage fallback was suppressed. The new `resolvedWorkArchivedSessionIds` unconditionally unions local storage with remote data, so any ID that remains in `archivedSessionIdsStorage` — even one the remote explicitly unarchived — continues to mark the session as archived on this device.
    
    Concrete failure: user archives session X on iPhone A → iPhone B or the desktop unarchives it via `unarchiveChatSession` → iPhone A reloads and receives summaries with `archivedAt == nil`, but its `AppStorage` still contains `session.id`. The new code returns that ID as archived; the old code filtered it out because the remote knew the session. iPhone A will continue hiding the session in the archived list until the user explicitly archives/unarchives it again on that device, clearing local storage.
    
    How can I resolve this? If you propose a fix, please make it concise.

    Fix in Claude Code

Fix All in Claude Code

Prompt To Fix All With AI
Fix the following 2 code review issues. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 2
apps/ios/ADE/Views/Work/WorkRootScreen+Actions.swift:46-70
**Search subscriptions never unsubscribed after search clears**

`hydrateSearchOutputBuffersIfNeeded` calls `subscribeTerminal` for up to 8 sessions whenever search text is non-empty. `subscribeTerminal` inserts each `sessionId` into `subscribedTerminalSessionIds` and establishes ongoing push subscriptions — the server will continue streaming `terminal_data` frames for all of those sessions for the rest of the connection. There is no counterpart call to `unsubscribeTerminal` when the search text is cleared or when the view disappears. Over a long session with repeated searches, this silently accumulates live subscriptions and unnecessary data transfer.

### Issue 2 of 2
apps/desktop/src/main/services/state/kvDb.ts:738-743
`pragma table_info` is issued once per incoming change row inside `normalizeIncomingCrsqlChange`. In a large sync batch all rows from the same table will trigger identical schema lookups. Memoising the per-table PK check with a local `Map` avoids redundant I/O across the batch.

```suggestion
function normalizeIncomingCrsqlChange(
  db: DatabaseSyncType,
  change: CrsqlChangeRow,
  pkColumnCountCache: Map<string, number> = new Map(),
): CrsqlChangeRow {
  let pkCount = pkColumnCountCache.get(change.table);
  if (pkCount === undefined) {
    const tableInfo = allRows<{ pk: number }>(
      db,
      `pragma table_info('${change.table.replace(/'/g, "''")}')`
    );
    pkCount = tableInfo.filter((column) => Number(column.pk) > 0).length;
    pkColumnCountCache.set(change.table, pkCount);
  }
  const primaryKeyColumns = { length: pkCount };
```

Reviews (3): Last reviewed commit: "ship: iteration 3 — address coderabbit +..." | Re-trigger Greptile

@vercel
Copy link
Copy Markdown

vercel Bot commented May 19, 2026

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

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
ade Ignored Ignored Preview May 19, 2026 9:46am

@arul28
Copy link
Copy Markdown
Owner Author

arul28 commented May 19, 2026

@copilot review but do not make fixes

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 19, 2026

📝 Walkthrough

Walkthrough

Adds Claude “auto” permission mode, derives initial-input session metadata, adjusts Codex initial input path, hardens sync/database ingestion, updates chat/terminal services, and overhauls multiple iOS/desktop UIs and helpers. Introduces notification fallbacks, clean shell spawning, pending-input tracking, and broader PR/queue/CTO command support.

Changes

Unified CLI/Sync/UX Update

Layer / File(s) Summary
CLI permission and session meta threading
apps/ade-cli/src/adeRpcServer.ts, apps/desktop/src/shared/cliLaunch.ts, apps/ade-cli/src/cli.ts, apps/ade-cli/src/cli.test.ts
Adds permissionMode: auto (Claude-only), derives initialInput-based title/goal, passes reasoningEffort, updates validation and tests.
Codex/PTY launch behavior
apps/desktop/src/main/services/pty/ptyService.ts, apps/desktop/src/renderer/components/terminals/cliLaunch.test.ts
Introduces “clean” shell launches, disables noisy MCP servers for Codex, updates resume args and tests.
Chat/session pending-input and summaries
apps/desktop/src/main/services/chat/agentChatService.ts, apps/desktop/src/shared/types/chat.ts
Computes pendingInputItemId, coalesces assistant drafts, supports includeArchived in listings.
Sync/notifications/db hardening
apps/desktop/src/main/services/state/kvDb.ts, apps/ade-cli/src/services/sync/syncHostService.ts, apps/desktop/src/main/services/notifications/notificationEventBus.ts
Normalizes CRSQL PKs, adds in-app notification fallback, structured results; adjusts test-push handling.
iOS UI/state updates
apps/ios/**
Multiple screens refactored to chip/option-button UIs, adds work session navigation requests, terminal input composer/backstop, snapshot revisions, and settings snapshots.

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related PRs

  • arul28/ADE#255 — Touches same start_cli_session permission/initialInput pathways in adeRpcServer.
  • arul28/ADE#240 — Related desktop chat pending-input flow changes intersecting agentChatService logic.

Suggested labels

desktop, ios

✨ 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 ade/huge-ade-mobile-pass-831383b3

@capy-ai
Copy link
Copy Markdown

capy-ai Bot commented May 19, 2026

Capy auto-review is paused for this organization because the monthly auto-review limit has been reached. Increase the limit or turn it off in billing settings to resume automatic reviews.

Copy link
Copy Markdown
Contributor

Copilot AI commented May 19, 2026

@copilot review but do not make fixes

Reviewed commit d741fe4 as requested. I did not make any fixes or code changes in this pass.

Copilot finished work on behalf of arul28 May 19, 2026 08:51
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 17

Caution

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

⚠️ Outside diff range comments (2)
apps/ios/ADE/Views/Work/WorkStatusAndFormattingHelpers.swift (1)

230-269: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

summary.status is effectively ignored whenever a session exists.

After Line 230, every recognized session.status / runtimeState path returns before Lines 255-266 can run. That means a fresher chat summary can no longer flip a stale session row from running to idle/ended, and downstream UI keeps treating it as active until the session projection catches up.

↕️ Minimal precedence fix
 private func rawWorkChatSessionStatus(session: TerminalSessionSummary?, summary: AgentChatSessionSummary?) -> String {
   if summary?.awaitingInput == true {
     return "awaiting-input"
   }
+  if let status = summary?.status.lowercased() {
+    switch status {
+    case "active", "running":
+      return "active"
+    case "idle", "paused":
+      return "idle"
+    case "ended", "completed", "failed", "interrupted":
+      return "ended"
+    default:
+      break
+    }
+  }
   if let session {
     let sessionStatus = session.status.lowercased()
     let runtimeState = session.runtimeState.lowercased()
     if sessionStatus == "awaiting-input" || sessionStatus == "awaiting_input" || runtimeState == "waiting-input" {
       return "awaiting-input"
@@
-  if let status = summary?.status.lowercased() {
-    switch status {
-    case "active", "running":
-      return "active"
-    case "idle", "paused":
-      return "idle"
-    case "ended", "completed", "failed", "interrupted":
-      return "ended"
-    default:
-      break
-    }
-  }
-
   guard let session else { return "ended" }
   return session.status.lowercased() == "running" ? "active" : "ended"
 }
🤖 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/ios/ADE/Views/Work/WorkStatusAndFormattingHelpers.swift` around lines
230 - 269, The logic currently returns based solely on
session.status/runtimeState when a session exists, ignoring summary?.status;
adjust the precedence so summary.status can override stale session state (e.g.,
if summary?.status indicates "idle" or "ended" when session appears "running",
treat it as idle/ended). Locate the block using session, session.status,
session.runtimeState and the later guard reading summary?.status and either (a)
evaluate summary?.status immediately after computing normalized
session/runtimeState and before returning, or (b) add conditional checks inside
the existing switch/default branches to consult summary?.status (checking
"active"/"running", "idle"/"paused", "ended"/"completed"/"failed"/"interrupted")
and return the summary-derived state where it reflects a more progressed state
than the session; ensure all comparisons use lowercased() like the current code.
apps/ade-cli/src/adeRpcServer.ts (1)

223-230: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Don't advertise auto on the default Codex path.

Line 230 now publishes "auto" as a valid spawn_agent.permissionMode, but Line 6895 rejects that combination whenever provider falls back to the schema default ("codex"). Tool callers that rely on the schema/defaults can now construct a guaranteed-invalid request. Either make the schema conditional on provider, or keep "auto" out of the shared enum and document it as Claude-only.

Also applies to: 6889-6900

🤖 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/ade-cli/src/adeRpcServer.ts` around lines 223 - 230, The schema for
spawn_agent currently lists permissionMode with enum
["default","auto","plan","edit","full-auto","config-toml"] which advertises
"auto" for all providers but the request validator rejects "auto" when provider
defaults to "codex"; fix by removing "auto" from the shared permissionMode enum
(the permissionMode property in the schema) or make the schema conditional so
that "auto" is only allowed when provider === "claude" (i.e., update the
provider/permissionMode validation logic used by the spawn_agent handler to
either restrict the enum or add provider-based schema branching).
🧹 Nitpick comments (7)
apps/ios/ADE/Views/Work/WorkChatSessionView+Actions.swift (1)

17-17: 💤 Low value

Clarify the rationale for increasing debounce delay to 220ms.

The delay was increased from 80ms to 220ms. While this reduces timeline rebuild frequency (improving performance), it may make the UI feel less responsive to rapid transcript updates.

Was this change driven by profiling data showing excessive rebuilds, or is 220ms an empirically determined sweet spot for this view? Documenting the reasoning would help future maintainers understand the tradeoff.

🤖 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/ios/ADE/Views/Work/WorkChatSessionView`+Actions.swift at line 17, The
debounce delay increase at the Task.sleep(for: .milliseconds(220)) call needs an
in-code rationale and a more maintainable implementation: replace the magic
number with a named constant (e.g., DEBOUNCE_DELAY_MS) and add a short comment
above the constant or the Task.sleep call that states whether 220ms came from
profiling data (reference the profiling run/metric), was empirically chosen, or
is a compromise to reduce rebuilds vs responsiveness; if profiling was used,
cite the metric (e.g., rebuilds/sec or CPU) and date, otherwise note it as an
empirical sweet spot and leave a TODO to re-evaluate with profiling.
apps/desktop/src/main/services/state/kvDb.ts (1)

3994-3996: ⚡ Quick win

Cache table PK metadata during batch apply to avoid per-row PRAGMA lookups.

The current loop does a schema query for every incoming change, which can become a sync bottleneck on large batches.

💡 Proposed direction
- for (const rawChange of changes) {
-   const change = normalizeIncomingCrsqlChange(db, rawChange);
+ const pkCountByTable = new Map<string, number>();
+ for (const rawChange of changes) {
+   const change = normalizeIncomingCrsqlChange(db, rawChange, pkCountByTable);
// helper signature idea
function normalizeIncomingCrsqlChange(
  db: DatabaseSyncType,
  change: CrsqlChangeRow,
  pkCountByTable: Map<string, number>
): CrsqlChangeRow { /* ... */ }
🤖 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/desktop/src/main/services/state/kvDb.ts` around lines 3994 - 3996, The
per-row PRAGMA/schema lookup happens inside normalizeIncomingCrsqlChange causing
a query for every change; modify the batch apply to first compute and cache
primary-key column counts by table (e.g. build a Map<string, number>
pkCountByTable), then change normalizeIncomingCrsqlChange signature to accept
this pkCountByTable and use it instead of performing per-row schema queries, and
update the loop where runStatement is called to pass pkCountByTable into
normalizeIncomingCrsqlChange so each change uses the cached metadata.
apps/ios/ADE/Views/Files/FilesDetailScreen.swift (1)

26-27: ⚡ Quick win

Scope new @State properties as private.

lastHandledFilesDetailRevision and lastFilesDetailReload should be private to keep view-local state encapsulated.

As per coding guidelines, apps/ios/**/*.swift: iOS Swift app — check for memory management, Swift conventions, and proper SwiftUI patterns.

🤖 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/ios/ADE/Views/Files/FilesDetailScreen.swift` around lines 26 - 27, Make
the two `@State` properties view-local by marking lastHandledFilesDetailRevision
and lastFilesDetailReload as private; update their declarations (the `@State` vars
in FilesDetailScreen.swift) to use private so the state is encapsulated within
the view and follows SwiftUI/Swift conventions for memory/scope management.
apps/ios/ADE/Views/Work/WorkNewChatScreen.swift (1)

773-810: 💤 Low value

Duplicate compactChoiceChip implementation.

This function is identical to the one defined in WorkNewChatScreen at lines 186-223. Extract it to a shared location or make the outer one accessible to the inner struct to eliminate duplication.

🤖 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/ios/ADE/Views/Work/WorkNewChatScreen.swift` around lines 773 - 810,
compactChoiceChip is duplicated inside WorkNewChatScreen; remove the inner
duplicate and refactor to a single shared implementation by either (1) moving
compactChoiceChip(title:systemImage:tint:isSelected:accessibilityPrefix:action:)
into the outer WorkNewChatScreen scope so the inner struct can call the outer
function, or (2) extracting it to a shared helper (e.g., a private fileprivate
function or a small View type) that both locations reference; update all call
sites to use that single symbol and delete the redundant definition.
apps/ios/ADE/Views/Lanes/LaneDetailContentSections.swift (1)

267-283: ⚡ Quick win

Avoid nesting a horizontal ScrollView inside the existing horizontal chip row.

statusRow already scrolls horizontally, so wrapping the PR chips in another horizontal ScrollView invites drag-gesture contention and awkward VoiceOver focus. Returning the HStack directly keeps the layout without same-axis nesting.

♻️ Simplify the PR chip container
-      ScrollView(.horizontal, showsIndicators: false) {
-        HStack(spacing: 6) {
-          ForEach(Array(linkedPullRequests.enumerated()), id: \.offset) { _, pr in
-            Button {
-              onOpenLinkedPullRequest(pr)
-            } label: {
-              LaneTypeBadge(
-                text: "#\(pr.githubPrNumber)",
-                tint: lanePullRequestTint(pr.state)
-              )
-            }
-            .buttonStyle(.plain)
-            .accessibilityLabel(pr.title.isEmpty ? "Open PR \(pr.githubPrNumber)" : "Open \(pr.title)")
-          }
-        }
-      }
+      HStack(spacing: 6) {
+        ForEach(Array(linkedPullRequests.enumerated()), id: \.offset) { _, pr in
+          Button {
+            onOpenLinkedPullRequest(pr)
+          } label: {
+            LaneTypeBadge(
+              text: "#\(pr.githubPrNumber)",
+              tint: lanePullRequestTint(pr.state)
+            )
+          }
+          .buttonStyle(.plain)
+          .accessibilityLabel(pr.title.isEmpty ? "Open PR \(pr.githubPrNumber)" : "Open \(pr.title)")
+        }
+      }
       .accessibilityLabel("\(linkedPullRequests.count) linked pull requests")
As per coding guidelines, `apps/ios/**/*.swift`: iOS Swift app — check for memory management, Swift conventions, and proper SwiftUI patterns.
🤖 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/ios/ADE/Views/Lanes/LaneDetailContentSections.swift` around lines 267 -
283, Remove the horizontal ScrollView wrapper around the PR chips to avoid
same-axis scroll nesting: replace the ScrollView(.horizontal, ...) block with
the existing HStack(spacing: 6) {
ForEach(Array(linkedPullRequests.enumerated()), id: \.offset) { _, pr in ... } }
and move the accessibilityLabel("\(linkedPullRequests.count) linked pull
requests") onto that HStack (or remove it if already covered by parent), keeping
the Button, onOpenLinkedPullRequest(pr) call, LaneTypeBadge and
lanePullRequestTint(pr.state) intact.
apps/ios/ADE/Views/PRs/PrFiltersCard.swift (1)

79-98: 💤 Low value

Remove unused sortMenuLabel view.

This view builder was the label for the previous Menu-based dropdown which has been replaced with a horizontal chip selector. It's no longer referenced anywhere in the file.

🧹 Proposed cleanup
-  `@ViewBuilder`
-  private var sortMenuLabel: some View {
-    HStack(spacing: 5) {
-      Image(systemName: "arrow.up.arrow.down")
-        .font(.system(size: 9, weight: .bold))
-      Text(sortOption.title)
-        .font(.system(size: 11, weight: .semibold))
-    }
-    .foregroundStyle(PrsGlass.textSecondary)
-    .padding(.horizontal, 10)
-    .padding(.vertical, 6)
-    .background(
-      Capsule(style: .continuous)
-        .fill(Color.white.opacity(0.05))
-    )
-    .overlay(
-      Capsule(style: .continuous)
-        .stroke(Color.white.opacity(0.10), lineWidth: 0.6)
-    )
-  }
🤖 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/ios/ADE/Views/PRs/PrFiltersCard.swift` around lines 79 - 98, The private
ViewBuilder property `sortMenuLabel` is dead code (no longer referenced after
replacing the Menu with the chip selector); remove the entire `@ViewBuilder
private var sortMenuLabel: some View { ... }` declaration to clean up, ensure
there are no remaining references to `sortMenuLabel` (and delete any now-unused
supporting code tied only to it), then build the app to confirm no compiler
warnings/errors remain.
apps/ios/ADETests/ADETests.swift (1)

3691-3697: ⚡ Quick win

Scope database-change observers to avoid cross-test notification noise.

Using NotificationCenter.default with object: nil can count unrelated .adeDatabaseDidChange events and make these assertions flaky.

Suggested fix
-    let token = NotificationCenter.default.addObserver(
+    let token = NotificationCenter.default.addObserver(
       forName: .adeDatabaseDidChange,
-      object: nil,
+      object: database,
       queue: nil
     ) { _ in
       notificationCount += 1
     }

Also applies to: 3847-3853

🤖 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/ios/ADETests/ADETests.swift` around lines 3691 - 3697, The observer for
.adeDatabaseDidChange is currently registered with object: nil which captures
unrelated notifications across tests; update the
NotificationCenter.default.addObserver call to scope it to the specific database
instance under test (pass that instance as the object instead of nil, e.g., the
local db variable or the subject-under-test) so only changes from that instance
increment notificationCount, and make the same change for the other occurrence
noted (lines ~3847-3853). Keep the observer token name (token) and ensure you
still remove the observer after the test
(NotificationCenter.default.removeObserver(token)) to avoid leaks.
🤖 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/ade-cli/src/adeRpcServer.ts`:
- Around line 5254-5263: The metadata update is currently gated on
initialInputMeta.goal, causing promptTitle-derived titles to be treated as
manually named only when a goal exists; move the title-related update out of the
initialInputMeta.goal conditional so runtime.sessionService.updateMeta is
invoked for the title independently of goal. Specifically, keep the goal update
conditional (use session?.goal check and only include { goal:
initialInputMeta.goal } when appropriate) but always evaluate and include the
title/manuallyNamed piece when initialInputMeta.promptTitle exists and title ===
initialInputMeta.promptTitle, using the same keys ({ title:
initialInputMeta.promptTitle, manuallyNamed: false }) and still call
runtime.sessionService.updateMeta with the merged object for
created.sessionId/session variable.

In `@apps/ade-cli/src/services/sync/syncRemoteCommandService.ts`:
- Around line 1289-1305: The parser currently accepts any finite number for
confidenceThreshold; after const confidenceThreshold =
asOptionalNumber(value.confidenceThreshold) add a strict guard that rejects or
omits values outside the allowed range (e.g. require
Number.isFinite(confidenceThreshold) && confidenceThreshold >= 0 &&
confidenceThreshold <= 1); if the check fails throw a clear validation error or
do not include confidenceThreshold in the returned object so invalid numbers
like -1 or 1.5 cannot reach StartQueueAutomationArgs["confidenceThreshold"];
update any tests that exercise confidenceThreshold to expect this validation.

In `@apps/desktop/src/main/services/state/kvDb.ts`:
- Around line 739-753: normalizeIncomingCrsqlChange currently returns early for
byte-typed PKs via isSyncScalarBytes(packed) before verifying the table's
primary key arity, allowing malformed inputs to bypass validation; change the
logic to first query the table schema (using allRows and primaryKeyColumns from
the pragma) and validate primaryKeyColumns.length, then handle change.pk: if
primaryKeyColumns.length === 1 allow/convert scalar bytes via
packedCrsqlPrimaryKey(change.pk) (return change or {...change, pk: packedPk} as
appropriate), otherwise reject/non-scalar cases with an Error referencing
change.table and change.cid; preserve use of isSyncScalarBytes and
packedCrsqlPrimaryKey but ensure the table arity check happens before any early
return for byte-typed PKs.

In `@apps/ios/ADE/Services/SyncService.swift`:
- Around line 3752-3757: cachedChatModelCatalog() currently returns the newest
catalog from chatModelCatalogCache regardless of host/project scope; change it
to only consider entries whose cache key matches the current host/project scope
(use the same scoping logic as chatModelsCacheKey(provider:)), i.e. compute the
current cache key for the active provider/project and filter
chatModelCatalogCache to entries with that key before sorting by fetchedAt and
applying chatModelsCacheTTL, keeping the method name cachedChatModelCatalog(),
the cache variable chatModelCatalogCache, and the TTL constant
chatModelsCacheTTL intact.
- Around line 1655-1663: The current block in
refreshProjectCatalog(preferRemoteSelection:) can set activeProjectId to an ID
from deduplicatedRemoteProjectCatalog() that isn't present in the in-memory
projects list, causing activeProject to become nil; change the logic so that
before calling setActiveProjectId(remoteProject.id, rootPath: ... ) you verify
the chosen remoteProject.id exists in projects (e.g. projects.contains(where: {
$0.id == remoteProject.id })) or otherwise find the matching project from
projects by normalizedProjectRoot and use that project's id; only perform the
setActiveProjectId and return when the target ID is confirmed to be in projects
to avoid remapping to a deduped-away ID.
- Around line 7385-7414: In sendTestPush() in SyncService, short-circuit when
the socket is offline by checking canSendLiveRequests() before calling
awaitResponse; if canSendLiveRequests() is false, return a
SyncSendTestPushResult(ok: false, message: "The socket is offline" or similar)
immediately so you don't start awaitResponse/sendEnvelope and hang until
timeout; update the function to perform this pre-check (referencing
sendTestPush(), canSendLiveRequests(), awaitResponse(...), and
sendEnvelope(...)) and keep the existing result shapes for consistency.

In `@apps/ios/ADE/Views/Cto/CtoSettingsScreen.swift`:
- Around line 349-354: The current calls to syncService.fetchCtoBudget() and
syncService.fetchLinearConnectionStatus() only assign self.budget and
self.linearStatus on success, leaving stale values on failure; change the calls
to explicitly handle the failure case (e.g. use a do/catch or if let/else) and
reset self.budget and self.linearStatus to nil or a safe default in the
else/catch branch so stale UI state is cleared when fetches fail; locate the
assignments around syncService.fetchCtoBudget() and
syncService.fetchLinearConnectionStatus() and add the failure/reset handling for
self.budget and self.linearStatus.

In `@apps/ios/ADE/Views/Cto/CtoWorkerDetailScreen.swift`:
- Around line 529-535: When handling memoryResult in the switch, the failure
branch currently sets memoryLoadError but leaves coreMemory stale; update the
.failure(let err) branch to clear the previous snapshot by setting coreMemory =
nil (or an empty/default snapshot) in addition to setting memoryLoadError =
err.localizedDescription so stale memory is not shown when refresh fails; modify
the switch handling for memoryResult in CtoWorkerDetailScreen (the
.success/.failure branches) accordingly.

In `@apps/ios/ADE/Views/Lanes/LaneDetailContentSections.swift`:
- Around line 67-74: The current logic uses detail?.syncStatus fallback values
(syncStatus, remoteAhead, remoteBehind, hasUpstream, diverged) which causes the
Push button to be disabled while detail is loading; change the fallback to use
snapshot.lane.status as the temporary source of truth: when detail?.syncStatus
is nil, read ahead/behind/hasUpstream/diverged from snapshot.lane.status instead
of hardcoded defaults so shouldPush/shouldPull reflect the snapshot state during
initial render; apply the same replacement for the equivalent block around the
variables referenced on the other occurrence (the 98-104 region) so both
first-paint states use snapshot.lane.status until detail is available.

In `@apps/ios/ADE/Views/PRs/PrDetailChecksTab.swift`:
- Around line 10-21: The summary currently ignores .neutral in
prChecksSummaryStats (function prChecksSummaryStats, type PrCheck, helper
prCheckConclusionKind) but still counts neutrals in total, causing total >
pass+fail+pending; fix by treating .neutral as pass: increment pass in the
.neutral case (or alternatively add a neutral variable and include it in the
returned PrChecksSummaryStats if you prefer an explicit bucket) so that
pass+fail+pending (+neutral if added) always equals total; update the switch in
prChecksSummaryStats and the initializer/return (PrChecksSummaryStats)
accordingly to keep internal consistency.

In `@apps/ios/ADE/Views/PRs/PrDetailFilesTab.swift`:
- Around line 353-362: The inline action Buttons (prFileInlineAction) are
currently inside PrFileRowLabel which is used as the label for the outer
PrFileDiffCard Button, causing nested Button/tap conflicts; fix by removing the
HStack of prFileInlineAction from inside PrFileRowLabel and placing it outside
the outer Button (e.g., render the HStack as a sibling view alongside
PrFileDiffCard or in the trailing area of the row) so the outer Button only
wraps the row content and the action Buttons (which call onOpenFile and
onCopyPath) are independent Buttons not nested inside the label; update
PrFileRowLabel and PrFileDiffCard usages accordingly to ensure only the row
toggle is the outer Button and action Buttons are separate.

In `@apps/ios/ADE/Views/PRs/PrDetailOverviewTab.swift`:
- Around line 1674-1695: The inline action buttons (prInlineAction) render the
tappable area at 26×26 which is below the iOS 44pt accessibility touch target
(same issue exists in PrPathReviewCommentRow and PrIssueInventoryRow); update
the view so the button's hit area is at least 44×44 while keeping the icon
visually small: wrap the Image in the existing Button but set the Button (or its
container) to frame(width: 44, height: 44) and center the icon inside (or add
.contentShape(Circle()) / .padding to expand touch area) and keep the visual
circle/background sized for the icon, ensuring accessibilityLabel remains and
.buttonStyle(.plain) is preserved.

In `@apps/ios/ADE/Views/PRs/PrsRootScreen.swift`:
- Around line 1731-1737: The filter currently uses caseInsensitiveCompare, which
can match branches that only differ by case; change the comparison in the lanes
filter to a case-sensitive equality check by replacing
normalizedPrBranchName(lane.branchRef).caseInsensitiveCompare(expectedBranch) ==
.orderedSame with a direct case-sensitive comparison (e.g.
normalizedPrBranchName(lane.branchRef) == expectedBranch). Keep the existing
guards (lane.archivedAt, lane.laneType, and !expectedBranch.isEmpty) and update
only the equality check involving normalizedPrBranchName, lane.branchRef, and
expectedBranch.
- Around line 1855-1858: The current .onAppear uses item.linkedLaneId (and
fallback ids) without verifying they exist in availableLanes, which can leave
selectedLaneId set to an id not in the visible options; change the logic in the
.onAppear handler for selectedLaneId so it only assigns item.linkedLaneId or
exactBranchMatchedLane?.id if that id is contained in availableLanes.map { $0.id
} (or use availableLanes.contains(where: { $0.id == ... })), otherwise fall back
to availableLanes.first?.id or leave an empty string; update the selection
assignment near selectedLaneId/item.linkedLaneId/exactBranchMatchedLane to
perform these membership checks before setting selectedLaneId.

In `@apps/ios/ADE/Views/Work/WorkArtifactTerminalViews.swift`:
- Around line 291-304: The current submitReturn in WorkArtifactTerminalViews
sends buffered text first and then schedules a delayed "\r", which can reorder
or drop the return; change it to send the entire command and return in a single
terminal write via syncService.sendTerminalInput(sessionId: session.id, data:
input + "\r") (when input is non-empty) and, for the empty-input case, continue
to send just "\r"; also perform a connectionState check on syncService before
sending to avoid sending when disconnected and remove the Task sleep/delayed
send logic in submitReturn.

In `@apps/ios/ADE/Views/Work/WorkSessionDestinationView.swift`:
- Around line 659-680: The early return on fallbackEntries prevents upgrading a
truncated fallback to the canonical transcript; change the guard so we only skip
reconciliation when there is a non-truncated fallback. Replace the current check
(guard fallbackEntries.isEmpty else { return }) with logic that allows entry
when fallbackEntries is present but truncated (e.g., add a Bool property
fallbackIsTruncated or compute combined char count vs. the 32_000 cap), and
update loadTranscript(to: forceRemote:preferLightweight:) to set
fallbackIsTruncated (or ensure the truncation check is available) when it fills
fallbackEntries so reconcileIdleCanonicalTranscriptIfNeeded() can proceed only
when fallbackEntries is non-empty AND not truncated (guard
!(fallbackEntries.nonEmpty && !fallbackIsTruncated) else { return }).

In `@apps/ios/ADETests/ADETests.swift`:
- Around line 66-76: The test constructs a local `service` but never binds it to
the global used by the router; set `SyncService.shared = service` (after saving
`previousShared` and before calling `DeepLinkRouter.shared.handle(...)`) so
`DeepLinkRouter` actions target the intended `SyncService` instance; the
existing `defer { SyncService.shared = previousShared }` will restore the
previous global after the test.

---

Outside diff comments:
In `@apps/ade-cli/src/adeRpcServer.ts`:
- Around line 223-230: The schema for spawn_agent currently lists permissionMode
with enum ["default","auto","plan","edit","full-auto","config-toml"] which
advertises "auto" for all providers but the request validator rejects "auto"
when provider defaults to "codex"; fix by removing "auto" from the shared
permissionMode enum (the permissionMode property in the schema) or make the
schema conditional so that "auto" is only allowed when provider === "claude"
(i.e., update the provider/permissionMode validation logic used by the
spawn_agent handler to either restrict the enum or add provider-based schema
branching).

In `@apps/ios/ADE/Views/Work/WorkStatusAndFormattingHelpers.swift`:
- Around line 230-269: The logic currently returns based solely on
session.status/runtimeState when a session exists, ignoring summary?.status;
adjust the precedence so summary.status can override stale session state (e.g.,
if summary?.status indicates "idle" or "ended" when session appears "running",
treat it as idle/ended). Locate the block using session, session.status,
session.runtimeState and the later guard reading summary?.status and either (a)
evaluate summary?.status immediately after computing normalized
session/runtimeState and before returning, or (b) add conditional checks inside
the existing switch/default branches to consult summary?.status (checking
"active"/"running", "idle"/"paused", "ended"/"completed"/"failed"/"interrupted")
and return the summary-derived state where it reflects a more progressed state
than the session; ensure all comparisons use lowercased() like the current code.

---

Nitpick comments:
In `@apps/desktop/src/main/services/state/kvDb.ts`:
- Around line 3994-3996: The per-row PRAGMA/schema lookup happens inside
normalizeIncomingCrsqlChange causing a query for every change; modify the batch
apply to first compute and cache primary-key column counts by table (e.g. build
a Map<string, number> pkCountByTable), then change normalizeIncomingCrsqlChange
signature to accept this pkCountByTable and use it instead of performing per-row
schema queries, and update the loop where runStatement is called to pass
pkCountByTable into normalizeIncomingCrsqlChange so each change uses the cached
metadata.

In `@apps/ios/ADE/Views/Files/FilesDetailScreen.swift`:
- Around line 26-27: Make the two `@State` properties view-local by marking
lastHandledFilesDetailRevision and lastFilesDetailReload as private; update
their declarations (the `@State` vars in FilesDetailScreen.swift) to use private
so the state is encapsulated within the view and follows SwiftUI/Swift
conventions for memory/scope management.

In `@apps/ios/ADE/Views/Lanes/LaneDetailContentSections.swift`:
- Around line 267-283: Remove the horizontal ScrollView wrapper around the PR
chips to avoid same-axis scroll nesting: replace the ScrollView(.horizontal,
...) block with the existing HStack(spacing: 6) {
ForEach(Array(linkedPullRequests.enumerated()), id: \.offset) { _, pr in ... } }
and move the accessibilityLabel("\(linkedPullRequests.count) linked pull
requests") onto that HStack (or remove it if already covered by parent), keeping
the Button, onOpenLinkedPullRequest(pr) call, LaneTypeBadge and
lanePullRequestTint(pr.state) intact.

In `@apps/ios/ADE/Views/PRs/PrFiltersCard.swift`:
- Around line 79-98: The private ViewBuilder property `sortMenuLabel` is dead
code (no longer referenced after replacing the Menu with the chip selector);
remove the entire `@ViewBuilder private var sortMenuLabel: some View { ... }`
declaration to clean up, ensure there are no remaining references to
`sortMenuLabel` (and delete any now-unused supporting code tied only to it),
then build the app to confirm no compiler warnings/errors remain.

In `@apps/ios/ADE/Views/Work/WorkChatSessionView`+Actions.swift:
- Line 17: The debounce delay increase at the Task.sleep(for:
.milliseconds(220)) call needs an in-code rationale and a more maintainable
implementation: replace the magic number with a named constant (e.g.,
DEBOUNCE_DELAY_MS) and add a short comment above the constant or the Task.sleep
call that states whether 220ms came from profiling data (reference the profiling
run/metric), was empirically chosen, or is a compromise to reduce rebuilds vs
responsiveness; if profiling was used, cite the metric (e.g., rebuilds/sec or
CPU) and date, otherwise note it as an empirical sweet spot and leave a TODO to
re-evaluate with profiling.

In `@apps/ios/ADE/Views/Work/WorkNewChatScreen.swift`:
- Around line 773-810: compactChoiceChip is duplicated inside WorkNewChatScreen;
remove the inner duplicate and refactor to a single shared implementation by
either (1) moving
compactChoiceChip(title:systemImage:tint:isSelected:accessibilityPrefix:action:)
into the outer WorkNewChatScreen scope so the inner struct can call the outer
function, or (2) extracting it to a shared helper (e.g., a private fileprivate
function or a small View type) that both locations reference; update all call
sites to use that single symbol and delete the redundant definition.

In `@apps/ios/ADETests/ADETests.swift`:
- Around line 3691-3697: The observer for .adeDatabaseDidChange is currently
registered with object: nil which captures unrelated notifications across tests;
update the NotificationCenter.default.addObserver call to scope it to the
specific database instance under test (pass that instance as the object instead
of nil, e.g., the local db variable or the subject-under-test) so only changes
from that instance increment notificationCount, and make the same change for the
other occurrence noted (lines ~3847-3853). Keep the observer token name (token)
and ensure you still remove the observer after the test
(NotificationCenter.default.removeObserver(token)) to avoid leaks.
🪄 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: CHILL

Plan: Pro

Run ID: 46bdfb2c-b9f4-4427-b978-b50bec32ecc8

📥 Commits

Reviewing files that changed from the base of the PR and between 049a79f and d741fe4.

⛔ Files ignored due to path filters (8)
  • apps/ios/ADE.xcodeproj/project.pbxproj is excluded by !**/*.xcodeproj/project.pbxproj
  • docs/features/chat/README.md is excluded by !docs/**
  • docs/features/sync-and-multi-device/README.md is excluded by !docs/**
  • docs/features/sync-and-multi-device/ios-companion.md is excluded by !docs/**
  • docs/features/sync-and-multi-device/remote-commands.md is excluded by !docs/**
  • docs/features/terminals-and-sessions/README.md is excluded by !docs/**
  • docs/features/terminals-and-sessions/pty-and-processes.md is excluded by !docs/**
  • docs/perf/ios-mobile-action-inventory.md is excluded by !docs/**
📒 Files selected for processing (115)
  • apps/ade-cli/src/adeRpcServer.test.ts
  • apps/ade-cli/src/adeRpcServer.ts
  • apps/ade-cli/src/bootstrap.ts
  • apps/ade-cli/src/cli.test.ts
  • apps/ade-cli/src/cli.ts
  • apps/ade-cli/src/services/sync/syncHostService.ts
  • apps/ade-cli/src/services/sync/syncRemoteCommandService.ts
  • apps/desktop/src/main/services/chat/agentChatService.test.ts
  • apps/desktop/src/main/services/chat/agentChatService.ts
  • apps/desktop/src/main/services/git/gitOperationsService.test.ts
  • apps/desktop/src/main/services/git/gitOperationsService.ts
  • apps/desktop/src/main/services/notifications/notificationEventBus.test.ts
  • apps/desktop/src/main/services/notifications/notificationEventBus.ts
  • apps/desktop/src/main/services/pty/ptyService.test.ts
  • apps/desktop/src/main/services/pty/ptyService.ts
  • apps/desktop/src/main/services/state/kvDb.sync.test.ts
  • apps/desktop/src/main/services/state/kvDb.ts
  • apps/desktop/src/main/services/sync/deviceRegistryService.test.ts
  • apps/desktop/src/main/services/sync/syncHostService.test.ts
  • apps/desktop/src/main/services/sync/syncRemoteCommandService.test.ts
  • apps/desktop/src/renderer/components/terminals/cliLaunch.test.ts
  • apps/desktop/src/shared/cliLaunch.ts
  • apps/desktop/src/shared/types/chat.ts
  • apps/desktop/src/shared/types/sessions.ts
  • apps/desktop/src/shared/types/sync.ts
  • apps/ios/ADE/App/ContentView.swift
  • apps/ios/ADE/App/DeepLinkRouter.swift
  • apps/ios/ADE/Models/NotificationPreferences.swift
  • apps/ios/ADE/Models/RemoteModels.swift
  • apps/ios/ADE/Services/Database.swift
  • apps/ios/ADE/Services/SyncService.swift
  • apps/ios/ADE/Shared/ADESharedContainer.swift
  • apps/ios/ADE/Shared/ADESharedModels.swift
  • apps/ios/ADE/Views/AttentionDrawer/AttentionDrawerModel.swift
  • apps/ios/ADE/Views/AttentionDrawer/AttentionDrawerSheet.swift
  • apps/ios/ADE/Views/Components/ADEDesignSystem.swift
  • apps/ios/ADE/Views/Components/ADEMobilePrimitives.swift
  • apps/ios/ADE/Views/Components/ADEStreamingShimmer.swift
  • apps/ios/ADE/Views/Cto/CtoBriefEditor.swift
  • apps/ios/ADE/Views/Cto/CtoIdentityEditor.swift
  • apps/ios/ADE/Views/Cto/CtoReloadHelpers.swift
  • apps/ios/ADE/Views/Cto/CtoRootScreen.swift
  • apps/ios/ADE/Views/Cto/CtoSessionDestinationView.swift
  • apps/ios/ADE/Views/Cto/CtoSettingsScreen.swift
  • apps/ios/ADE/Views/Cto/CtoTeamScreen.swift
  • apps/ios/ADE/Views/Cto/CtoWorkerDetailScreen.swift
  • apps/ios/ADE/Views/Cto/CtoWorkflowsScreen.swift
  • apps/ios/ADE/Views/Files/FilesDetailComponents.swift
  • apps/ios/ADE/Views/Files/FilesDetailScreen+Actions.swift
  • apps/ios/ADE/Views/Files/FilesDetailScreen.swift
  • apps/ios/ADE/Views/Files/FilesDirectoryContentsView+Actions.swift
  • apps/ios/ADE/Views/Files/FilesDirectoryContentsView.swift
  • apps/ios/ADE/Views/Files/FilesModels.swift
  • apps/ios/ADE/Views/Files/FilesRootComponents.swift
  • apps/ios/ADE/Views/Files/FilesRootScreen.swift
  • apps/ios/ADE/Views/Lanes/LaneBatchManageSheet.swift
  • apps/ios/ADE/Views/Lanes/LaneBranchPickerSheet.swift
  • apps/ios/ADE/Views/Lanes/LaneChatLaunchSheet.swift
  • apps/ios/ADE/Views/Lanes/LaneCommitHistoryScreen.swift
  • apps/ios/ADE/Views/Lanes/LaneCommitSheet.swift
  • apps/ios/ADE/Views/Lanes/LaneComponents.swift
  • apps/ios/ADE/Views/Lanes/LaneCreateSheet.swift
  • apps/ios/ADE/Views/Lanes/LaneDetailContentSections.swift
  • apps/ios/ADE/Views/Lanes/LaneDetailScreen.swift
  • apps/ios/ADE/Views/Lanes/LaneDiffScreen.swift
  • apps/ios/ADE/Views/Lanes/LaneFileTreeComponents.swift
  • apps/ios/ADE/Views/Lanes/LaneHelpers.swift
  • apps/ios/ADE/Views/Lanes/LaneListViewParts.swift
  • apps/ios/ADE/Views/Lanes/LaneManageSheet.swift
  • apps/ios/ADE/Views/Lanes/LaneTypes.swift
  • apps/ios/ADE/Views/LanesTabView.swift
  • apps/ios/ADE/Views/PRs/CreatePrWizardView.swift
  • apps/ios/ADE/Views/PRs/PrAiResolverCtaCard.swift
  • apps/ios/ADE/Views/PRs/PrDetailActivityTab.swift
  • apps/ios/ADE/Views/PRs/PrDetailChecksTab.swift
  • apps/ios/ADE/Views/PRs/PrDetailFilesTab.swift
  • apps/ios/ADE/Views/PRs/PrDetailOverviewTab.swift
  • apps/ios/ADE/Views/PRs/PrDetailScreen.swift
  • apps/ios/ADE/Views/PRs/PrFiltersCard.swift
  • apps/ios/ADE/Views/PRs/PrHelpers.swift
  • apps/ios/ADE/Views/PRs/PrMergeGateCard.swift
  • apps/ios/ADE/Views/PRs/PrWorkflowCards.swift
  • apps/ios/ADE/Views/PRs/PrsRootScreen.swift
  • apps/ios/ADE/Views/Settings/ConnectionSettingsView.swift
  • apps/ios/ADE/Views/Settings/NotificationsCenterView.swift
  • apps/ios/ADE/Views/Settings/PerSessionOverrideView.swift
  • apps/ios/ADE/Views/Settings/SettingsConnectionHeader.swift
  • apps/ios/ADE/Views/Settings/SettingsDiagnosticsSection.swift
  • apps/ios/ADE/Views/Settings/SettingsNotificationsSection.swift
  • apps/ios/ADE/Views/Settings/SettingsPairingSection.swift
  • apps/ios/ADE/Views/Work/WorkArtifactTerminalViews.swift
  • apps/ios/ADE/Views/Work/WorkBrowserHelpers.swift
  • apps/ios/ADE/Views/Work/WorkChatComposerAndInputViews.swift
  • apps/ios/ADE/Views/Work/WorkChatHeaderAndMessageViews.swift
  • apps/ios/ADE/Views/Work/WorkChatSessionView+Actions.swift
  • apps/ios/ADE/Views/Work/WorkChatSessionView+MessageLiveness.swift
  • apps/ios/ADE/Views/Work/WorkChatSessionView+Timeline.swift
  • apps/ios/ADE/Views/Work/WorkChatSessionView.swift
  • apps/ios/ADE/Views/Work/WorkErrorAndMessageHelpers.swift
  • apps/ios/ADE/Views/Work/WorkNewChatScreen.swift
  • apps/ios/ADE/Views/Work/WorkNewChatSheet.swift
  • apps/ios/ADE/Views/Work/WorkPreviews.swift
  • apps/ios/ADE/Views/Work/WorkRootComponents.swift
  • apps/ios/ADE/Views/Work/WorkRootScreen+Actions.swift
  • apps/ios/ADE/Views/Work/WorkRootScreen+Selection.swift
  • apps/ios/ADE/Views/Work/WorkRootScreen.swift
  • apps/ios/ADE/Views/Work/WorkSessionDestinationView+Actions.swift
  • apps/ios/ADE/Views/Work/WorkSessionDestinationView.swift
  • apps/ios/ADE/Views/Work/WorkSessionGrouping.swift
  • apps/ios/ADE/Views/Work/WorkSessionSettingsSheet.swift
  • apps/ios/ADE/Views/Work/WorkStatusAndFormattingHelpers.swift
  • apps/ios/ADE/Views/Work/WorkTerminalEmulatorView.swift
  • apps/ios/ADE/Views/Work/WorkTimelineHelpers.swift
  • apps/ios/ADETests/ADETests.swift
  • apps/ios/ADETests/AttentionDrawerModelTests.swift
💤 Files with no reviewable changes (2)
  • apps/ios/ADE/Views/Components/ADEStreamingShimmer.swift
  • apps/ios/ADE/Views/Work/WorkChatSessionView+MessageLiveness.swift

Comment thread apps/ade-cli/src/adeRpcServer.ts Outdated
Comment thread apps/ade-cli/src/services/sync/syncRemoteCommandService.ts
Comment thread apps/desktop/src/main/services/state/kvDb.ts Outdated
Comment thread apps/ios/ADE/Services/SyncService.swift
Comment thread apps/ios/ADE/Services/SyncService.swift
Comment thread apps/ios/ADE/Views/PRs/PrsRootScreen.swift
Comment thread apps/ios/ADE/Views/PRs/PrsRootScreen.swift
Comment thread apps/ios/ADE/Views/Work/WorkArtifactTerminalViews.swift Outdated
Comment thread apps/ios/ADE/Views/Work/WorkSessionDestinationView.swift
Comment thread apps/ios/ADETests/ADETests.swift
Comment thread apps/ios/ADE/Services/Database.swift
Comment thread apps/ios/ADE/Services/SyncService.swift
Comment thread apps/ios/ADE/Services/SyncService.swift
@arul28
Copy link
Copy Markdown
Owner Author

arul28 commented May 19, 2026

@codex review

@chatgpt-codex-connector
Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

Comment thread apps/desktop/src/main/services/state/kvDb.ts
arul28 and others added 2 commits May 19, 2026 05:45
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Addresses 20 actionable comments across TS (3) and iOS Swift (17):
- ade-cli/adeRpcServer: decouple auto-title meta from goal
- ade-cli/syncRemoteCommandService: validate confidenceThreshold 0..1
- desktop/kvDb: PK arity check runs before byte-typed PK accept
- iOS Database: empty title skips title only, not whole update
- iOS SyncService: contain activeProjectId remap; scope cached model catalog; fast-fail sendTestPush offline; indentation normalize
- iOS Views Cto: clear stale budget/linearStatus/coreMemory on refresh failure
- iOS Views Lanes: don't disable Push during sync-status loading
- iOS Views PRs: neutral/skipped checks counted; 44pt touch targets; case-sensitive branch match; validate linkedLaneId against availableLanes
- iOS Views Work: one-write terminal submit; don't skip idle transcript reconcile
- iOS Tests: explicit SyncService.shared binding in deep-link test

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@arul28 arul28 force-pushed the ade/huge-ade-mobile-pass-831383b3 branch from 9ff9c64 to 4aea07a Compare May 19, 2026 09:46
@arul28
Copy link
Copy Markdown
Owner Author

arul28 commented May 19, 2026

@codex review

@chatgpt-codex-connector
Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

@arul28 arul28 merged commit 0710313 into main May 19, 2026
28 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants