Skip to content

Fix per-keystroke render-grid replay loop on iOS, instrument sync latency - #9146

Merged
azooz2003-bit merged 11 commits into
mainfrom
feat-ios-sync-latency
Jul 30, 2026
Merged

azooz2003-bit merged 11 commits into
mainfrom
feat-ios-sync-latency

Conversation

@azooz2003-bit

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

Copy link
Copy Markdown
Collaborator

Measurement-first pass at iOS↔Mac sync latency. The instrumentation found a structural bug: on screen-anchored render-grid connections (every modern host), every typed keystroke's echo rendered through a full mobile.terminal.replay RPC plus the verified-replay freeze→fence→reveal pipeline instead of the live delta path.

Mechanism of the bug: the live delivery tail records terminalRenderGridHistoryContinuityBySurfaceID for screen-anchored frames, but the replay-response apply path never did. Cold attach always baselines via replay, so continuity started nil, every live delta failed the history chain check (base=0 delivered=nil), requested another replay, which again recorded no continuity — a permanent loop. The Mac side already rebases its emission chain onto served replays (MobileTerminalRenderObserver.adoptReplayBaseline) expecting exactly this contract. Introduced by #8860. Trace evidence: 40/40 probe keystrokes hit sync.render_grid_history_chain_break, zero live frames delivered.

Fixes (commit-by-commit, regression test red→green):

  1. fff620d0c3 failing tests: replay-applied baseline must let the next linked live delta deliver without a new replay; a fresh input ack must not re-subscribe while events are flowing.
  2. 359b8d7f81 fix: shared recordTerminalRenderGridHistoryContinuity helper called from both the live tail and the replay apply path; input-ack resubscribe (input_seq_wait) now skipped when the last terminal event is under 2 s old (terminalInputAckResubscribeSilenceThreshold), preserving lost-registration repair after silence. The per-ack mobile.events.subscribe matters on real-RTT links where acks outrun echo frames.

Instrumentation (all DEBUG-only, off unless CMUX_LATENCY_TRACE=1 or cmux.debug.latency-trace defaults): LAT <stage> t=<uptime_us> stamps across probe.send → host.in.recv → host.in.applied → host.grid → host.write → ev.grid → gate → ap.yield → ap.done → rd.present, plus state-sync emit/apply. CMUX_LATENCY_PROBE=<n>:<ms> types n synthetic keystrokes through the production input path (auto-navigating to the first workspace so headless sims work). scripts/mobile-latency-trace/analyze.py joins both logs (sim and Mac share a clock domain) into the tables below; see scripts/mobile-latency-trace/README.md.

Measured (iPhone 17 Pro Max sim ↔ tagged Mac over QUIC, same host, 40 keystrokes at 250 ms):

Metric Before After
Echo keystroke→visible p50 81 ms 15.2 ms
Echo keystroke→visible p95 133 ms 42.7 ms
Live frames delivered at gate 0 of 43 (all replay) 41 of 44 (3 = cold attach)
History chain breaks 40/40 keys 0
State sync Mac emit→iOS applied p50 — 0.6 ms

After-fix echo decomposition (p50): input wire+Mac apply 1.5 ms, echo capture→frame encode 2.6 ms, write+wire+decode+gate+apply 1.5 ms, display-link present 7.9 ms (120 Hz floor). On a physical iPhone the fix additionally removes one full wire RTT (the replay round trip) and ~31 ms of verified-replay freeze/reveal per keystroke.

Remaining known cost, intentionally not touched here: the stop-and-wait input batching RTT (probe.send→host.in.recv p95 29.7 ms when batches queue) is owned by #9036, which this PR avoids conflicting with (no CmuxMobileRPC edits).

Verification: CmuxMobileShell 813 tests (only the 3 known pre-existing failures), CmuxMobileShellModel 179 green, analyzer selftest green, live before/after traces above on tag slat.


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


Summary by cubic

Fixes an iOS render-grid continuity bug that forced a full replay on every keystroke; live deltas now link and apply normally. Adds opt-in iOS↔Mac latency tracing with a DEBUG typing probe and analyzer to measure end-to-end echo.

  • Bug Fixes

    • Preserve screen-anchored history continuity on both live-delivery and replay-apply paths; fail closed on missing/stale continuity and request replay. Retries catch-up replay when the pending-input gate requires it.
    • Defer input-ACK re-subscribe until 2s of terminal-event silence; cancel re-subscribe retries on replay success, fail-open recovery, or retry exhaustion. Thread terminal byte end-sequence through delivery and UI to tighten gating and continuity; tests cover replay-baseline→delta linking, stale-history rejection, ACK recency, and retry behavior.
    • Impact: echo p50 81ms → 15.2ms, p95 133ms → 42.7ms; live frames 41/44 (vs 0/43); history chain breaks 0/40.
  • New Features

    • DEBUG-only latency stamps across input→host→grid→enqueue→write→receive→gate→apply→present, with queue depth, host-side capture times, and FIFO terminal sequence to join logs by surface identity. Bounded writers on iOS and Mac batch to sinks and report visible drop counts; enable with CMUX_LATENCY_TRACE=1 or cmux.debug.latency-trace.
    • DEBUG typing probe CMUX_LATENCY_PROBE=<n>:<ms> with auto-navigation to the first workspace. Analyzer at scripts/mobile-latency-trace/analyze.py with README; joins iOS/Mac logs, supports same-clock echo decomposition on Simulator, and state-sync emit→apply timing.

Written for commit 24712a6. Summary will update on new commits.

Review in cubic

Summary by CodeRabbit

  • New Features
    • Added opt-in, DEBUG-only latency tracing across the Mac↔iOS workflow, including mobile trace writers and a log analyzer for LAT records.
    • Added DEBUG-only mobile latency probing with optional auto-navigation, plus additional latency stamps for grid, input, ACK, and state-sync paths.
  • Bug Fixes
    • Improved terminal byte sequencing by propagating an optional end-sequence “high-water mark” through live and verified-replay delivery/completion.
    • Tightened terminal input ACK resubscribe/retry retry/cancellation handling during replay and delivery transitions.
  • Documentation
    • Added setup/usage instructions for enabling tracing and running the analyzer.
  • Tests
    • Expanded render-grid replay staleness and ACK retry tests, plus new liveness and fixtures coverage.

@coderabbitai

coderabbitai Bot commented Jul 29, 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

Adds opt-in DEBUG latency tracing across macOS and iOS terminal paths, propagates render-grid sequence metadata through delivery queues, refines replay and input synchronization behavior, and introduces a Python CLI for analyzing correlated trace logs.

Changes

Latency tracing and terminal delivery

Layer / File(s) Summary
Trace APIs and probe configuration
Packages/iOS/CmuxMobileDiagnostics/..., Sources/App/HostLatencyTrace.swift, cmux.xcodeproj/project.pbxproj, Packages/iOS/CmuxMobileShell/MobileLatencyProbe.swift
Adds DEBUG-gated timestamp, elapsed-time, capture, and probe configuration APIs for host and mobile tracing.
Mobile probe lifecycle and input tracing
Packages/iOS/CmuxMobileShell/MobileShellComposite+LatencyProbe.swift, Packages/iOS/CmuxMobileShell/MobileShellComposite.swift
Schedules and cancels latency probes, traces raw input and acknowledgements, and refreshes terminal subscriptions after event-stream silence.
Host sequence propagation and tracing
Sources/Mobile/*, Sources/TerminalController.swift
Carries render-grid state sequences through event fan-out and queue draining, while adding host-side input, sync, tee, rendering, and workspace trace stamps.
Sequence-aware terminal delivery and replay continuity
Packages/iOS/CmuxMobileShell/*, Packages/iOS/CmuxMobileShellModel/...
Propagates terminal end sequences through output chunks, records screen-anchored history continuity, and traces render-grid delivery and replay decisions.
Verified replay completion signaling
Packages/iOS/CmuxMobileShellUI/..., Packages/iOS/CmuxMobileTerminal/...
Makes verified replay application return success state and records applied sequences before presentation timing stamps.
Render-grid fixtures and regression coverage
Packages/iOS/CmuxMobileShell/Tests/CmuxMobileShellTests/*
Expands frame fixtures, scripts terminal input sequences, and tests silence-based resubscription plus linked screen-anchored replay deltas.
Trace analysis CLI and documentation
scripts/mobile-latency-trace/*
Documents trace capture and adds parsing, pairing, latency aggregation, same-clock analysis, Markdown/JSON output, and self-tests.

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

Sequence Diagram(s)

sequenceDiagram
  participant TerminalController
  participant MobileTerminalByteTee
  participant MobileTerminalRenderObserver
  participant MobileHostService
  participant MobileShellComposite
  participant GhosttySurfaceView
  TerminalController->>MobileTerminalByteTee: receive and apply terminal input
  MobileTerminalByteTee->>MobileTerminalRenderObserver: publish terminal sequence
  MobileTerminalRenderObserver->>MobileHostService: emit render-grid event with stateSeq
  MobileHostService-->>MobileShellComposite: deliver terminal event
  MobileShellComposite->>GhosttySurfaceView: apply output and mark sequence
  GhosttySurfaceView-->>MobileShellComposite: stamp rendered presentation
Loading

Possibly related PRs

Suggested reviewers: austinywang, lawrencecchen


Important

Pre-merge checks failed

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

❌ Failed checks (3 errors, 1 warning)

Check name Status Explanation Resolution
Cmux Swift Blocking Runtime ❌ Error Production MobileShellComposite.scheduleTerminalInputAckResubscribeRetry adds clock.sleep-based delayed resubscribe logic outside DEBUG/test code. Replace the retry sleep with an explicit terminal-event/subscription state transition or scheduler-owned callback; keep delay logic test-only if needed.
Cmux Swift Concurrency ❌ Error MobileLatencyTraceWriter starts an unmanaged Task.detached consumer without storing/cancelling it, matching the banned fire-and-forget pattern. Store the consumer task and cancel it on shutdown/deinit, or drive draining from the actor without a detached background task.
Cmux No Ambient Global State ❌ Error FAIL: MobileLatencyTrace.swift:5-11, HostLatencyTrace.swift:8-11, and MobileLatencyProbe.swift:6-29 add static-namespace ambient state (writer/isEnabled/claim flags). Move tracer/probe state into injectable instances owned by MobileShellComposite/app state (e.g. LatencyTraceWriter, LatencyProbeController) instead of static enums.
Docstring Coverage ⚠️ Warning Docstring coverage is 24.35% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (21 passed)
Check name Status Explanation
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 Added debug helpers are lock-protected Sendable utilities; new clock/task code stays on @MainActor UI types, with no new cross-actor store or protocol isolation regressions.
Cmux Browser Automation Off-Main ✅ Passed Diff only rewinds the ghostty submodule and touches font-binding files; no browser automation or ControlCommandExecutionPolicy changes are present.
Cmux Expensive Synchronous Load ✅ Passed Diff adds telemetry, continuity tracking, and probe tasks only; no new synchronous history loads or large JSON parsing were added to main/interactive Swift paths.
Cmux Cache Substitution Correctness ✅ Passed Continuity is recorded only from authoritative frames and cleared on cold/reset; stale or missing bases still trigger replay, and ACK retries check freshness with a nil fallback.
Cmux No Hacky Sleeps ✅ Passed PASS: The only changed non-Swift runtime script is scripts/mobile-latency-trace/analyze.py, and it only parses/stats timestamps; no sleep/timer/polling waits were added.
Cmux Algorithmic Complexity ✅ Passed No new hot-path nested scans; added collection work is O(n) or fixed-size, and the only repeated scans live in DEBUG/offline trace-analysis helpers.
Cmux Swift @Concurrent ✅ Passed Touched async helpers are UI-bound or explicitly hopped (Task.detached, Task { @mainactor }); no bad @concurrent usage or missing boundary was introduced.
Cmux Swift Package Boundaries ✅ Passed PASS: shared logic lives in SwiftPM packages; app-target edits are Mac-app lifecycle/glue, debug tracing, and singleton-backed host services, not reusable standalone features.
Cmux Swiftpm Lockfiles ✅ Passed Diff only touches source/docs files; no cmux-owned .gitignore, Package.swift, Package.resolved, or cmux.xcodeproj changes, and ghostty is vendored.
Cmux Swift Logging ✅ Passed PASS: the diff adds only DEBUG-gated latency trace stamps/writers to the existing cmux debug log; no print/debugPrint/dump/NSLog or unsanitized production logging was added.
Cmux User-Facing Error Privacy ✅ Passed No new user-facing error/alert text exposes vendor/internal names, env vars, or secrets; added strings are debug telemetry, docs, or generic CLI warnings.
Cmux Full Internationalization ✅ Passed Touched strings are DEBUG/internal logs or operational docs; no user-facing copy, xcstrings, or web locale files were added/changed.
Cmux Swiftui State Layout ✅ Passed No new ObservableObject/@Published/GeometryReader/list-row state patterns; the only SwiftUI file change is a UIViewRepresentable bridge with debug-only stamping.
Cmux Architecture Rethink ✅ Passed Replay continuity is centralized in one helper and reused on live/replay paths; ACK retry is bounded/cancellable and DEBUG probe/tracing stay isolated.
Cmux Swift Auxiliary Window Close Shortcuts ✅ Passed PASS: The PR only touches iOS latency tracing, shell/service logic, and tests; changed Swift files have no NSWindow/NSPanel/WindowGroup usage or cmuxAuxiliaryWindowIdentifiers edits.
Cmux Source Artifacts ✅ Passed Changed paths are hand-written source, tests, scripts, and docs; no logs, temp dirs, build outputs, or other source artifacts appear in the PR diff.
Cmux No Test Or Debug Seam In Production Source ✅ Passed The new DEBUG-only helpers are dedicated latency-trace facilities with production call sites, not test/debug seams; no new test-only accessor was added to expose private state.
Title check ✅ Passed The title clearly summarizes the main change: fixing the iOS replay loop and adding latency instrumentation.
Description check ✅ Passed The description covers summary and testing well, but it omits the template's demo video, review trigger block, and checklist.
✨ Finishing Touches 💡 2
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
⚔️ Resolve merge conflicts 💡
  • Resolve merge conflict in branch feat-ios-sync-latency
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat-ios-sync-latency

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.

@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

🤖 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
`@Packages/iOS/CmuxMobileDiagnostics/Sources/CmuxMobileDiagnostics/MobileLatencyTrace.swift`:
- Around line 50-58: Update the stamp(at:) overload and stampElapsed to check
isEnabled before writing trace output, matching the existing opt-in behavior of
the diagnostics API. Ensure write is called only when tracing is enabled while
preserving the current timestamp and field handling.

In
`@Packages/iOS/CmuxMobileShell/Sources/CmuxMobileShell/MobileShellComposite`+LatencyProbe.swift:
- Around line 15-34: Remove the Task.sleep-based delays from the latency probe
auto-navigation and initial probe flows. Update the lifecycle coordination
around latencyProbeAutoNavigationTask and the corresponding initial probe logic
(lines 43–69) to trigger only after explicit navigation completion and
terminal-sink/output readiness, ensuring a claimed probe configuration is sent
exactly when those readiness conditions are satisfied.

In
`@Packages/iOS/CmuxMobileShell/Sources/CmuxMobileShell/MobileShellComposite`+TerminalOutputDelivery.swift:
- Around line 33-41: Update recordTerminalRenderGridHistoryContinuity to remove
the surface’s entry whenever the frame is non-screen or lacks historyRows,
rather than returning while preserving stale history; continue storing
historyRows for valid screen frames. Also clear the corresponding continuity
entry in registerTerminalOutput when resetting the delivered-sequence baseline,
so subsequent deltas fail closed and request replay.

In `@Sources/TerminalController.swift`:
- Around line 14875-14877: Update the `HostLatencyTrace.stamp("host.in.applied",
...)` call in the input handling flow so it is emitted only after the actual
terminal write, not while queued input is merely accepted. If retaining the
current location, rename the stage to an acceptance label and update the
corresponding analyzer to use that label instead.
🪄 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 Plus

Run ID: 35d8a185-98b3-4620-817f-82ff1dcb125b

📥 Commits

Reviewing files that changed from the base of the PR and between cf817f7 and 359b8d7.

📒 Files selected for processing (28)
  • Packages/iOS/CmuxMobileDiagnostics/Sources/CmuxMobileDiagnostics/MobileLatencyTrace.swift
  • Packages/iOS/CmuxMobileShell/Sources/CmuxMobileShell/MobileLatencyProbe.swift
  • Packages/iOS/CmuxMobileShell/Sources/CmuxMobileShell/MobileShellComposite+LatencyProbe.swift
  • Packages/iOS/CmuxMobileShell/Sources/CmuxMobileShell/MobileShellComposite+StateSync.swift
  • Packages/iOS/CmuxMobileShell/Sources/CmuxMobileShell/MobileShellComposite+TerminalLane.swift
  • Packages/iOS/CmuxMobileShell/Sources/CmuxMobileShell/MobileShellComposite+TerminalOutputDelivery.swift
  • Packages/iOS/CmuxMobileShell/Sources/CmuxMobileShell/MobileShellComposite.swift
  • Packages/iOS/CmuxMobileShell/Sources/CmuxMobileShell/TerminalOutputDelivery.swift
  • Packages/iOS/CmuxMobileShell/Tests/CmuxMobileShellTests/MobileShellRenderGridEventFrameFixtures.swift
  • Packages/iOS/CmuxMobileShell/Tests/CmuxMobileShellTests/MobileShellRenderGridFrameTestSupport.swift
  • Packages/iOS/CmuxMobileShell/Tests/CmuxMobileShellTests/MobileShellRenderGridInputCatchUpTests.swift
  • Packages/iOS/CmuxMobileShell/Tests/CmuxMobileShellTests/MobileShellRenderGridLivenessTestSupport.swift
  • Packages/iOS/CmuxMobileShell/Tests/CmuxMobileShellTests/MobileShellRenderGridReplayStalenessTests.swift
  • Packages/iOS/CmuxMobileShellModel/Sources/CmuxMobileShellModel/MobileTerminalOutputSinking.swift
  • Packages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/GhosttySurfaceRepresentable.swift
  • Packages/iOS/CmuxMobileTerminal/Sources/CmuxMobileTerminal/GhosttySurfaceView+LatencyTrace.swift
  • Packages/iOS/CmuxMobileTerminal/Sources/CmuxMobileTerminal/GhosttySurfaceView.swift
  • Sources/App/HostLatencyTrace.swift
  • Sources/Mobile/MobileHostConnectionEventQueue.swift
  • Sources/Mobile/MobileHostService.swift
  • Sources/Mobile/MobileStateSync.swift
  • Sources/Mobile/MobileTerminalByteTee.swift
  • Sources/Mobile/MobileTerminalRenderObserver.swift
  • Sources/Mobile/MobileWorkspaceListObserver.swift
  • Sources/TerminalController.swift
  • cmux.xcodeproj/project.pbxproj
  • scripts/mobile-latency-trace/README.md
  • scripts/mobile-latency-trace/analyze.py

Comment on lines +15 to +34
latencyProbeAutoNavigationTask = Task { @MainActor [weak self] in
defer { self?.latencyProbeAutoNavigationTask = nil }
do {
try await Task.sleep(for: .seconds(1))
guard let self,
self.connectionState == .connected,
self.terminalOutputStreamTokensBySurfaceID.isEmpty,
self.deeplinkWorkspaceNavigationRequest == nil,
let workspaceID = self.workspaces.first(where: {
!$0.terminals.isEmpty
})?.id,
MobileLatencyProbe.claimAutoNavigation() else {
return
}
self.navigateToWorkspaceForDeeplink(workspaceID, origin: .external)
} catch {
return
}
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift

Replace readiness sleeps with lifecycle completion.

The 1-second navigation delay and initial 3-second probe delay guess when navigation/output is ready. On a slow transition the claimed probe configuration can be consumed without sending; on a fast path this pads the measurement. Trigger from explicit navigation and terminal-sink readiness instead.

As per coding guidelines, “do not introduce … Task.sleep … for … readiness waits, or delayed coordination.”

Also applies to: 43-69

🤖 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
`@Packages/iOS/CmuxMobileShell/Sources/CmuxMobileShell/MobileShellComposite`+LatencyProbe.swift
around lines 15 - 34, Remove the Task.sleep-based delays from the latency probe
auto-navigation and initial probe flows. Update the lifecycle coordination
around latencyProbeAutoNavigationTask and the corresponding initial probe logic
(lines 43–69) to trigger only after explicit navigation completion and
terminal-sink/output readiness, ensuring a claimed probe configuration is sent
exactly when those readiness conditions are satisfied.

Source: Coding guidelines

Comment thread Sources/TerminalController.swift

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

Caution

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

⚠️ Outside diff range comments (3)
Packages/iOS/CmuxMobileShell/Tests/CmuxMobileShellTests/MobileShellRenderGridReplayStalenessTests.swift (1)

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

Guarantee collector cleanup on failure paths.

The new test mounts a long-lived collector task but only calls unmount() at the function’s final line. Any later throwing await or #require can bypass that cleanup and leave the stream task active for subsequent tests. Install defer { collector.unmount() } immediately after mounting and remove the trailing manual cleanup.

Suggested fix
     collector.mount(store: store, surfaceID: "live-terminal")
+    defer { collector.unmount() }

     // test body

-    collector.unmount()
🤖 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
`@Packages/iOS/CmuxMobileShell/Tests/CmuxMobileShellTests/MobileShellRenderGridReplayStalenessTests.swift`
around lines 14 - 75, In screenAnchoredReplayBaselinesNextLiveDelta, install
defer { collector.unmount() } immediately after
collector.mount(store:surfaceID:) so cleanup runs on every exit path, including
thrown awaits and `#require` failures. Remove the trailing explicit
collector.unmount() call.

Source: Coding guidelines

Packages/iOS/CmuxMobileShell/Sources/CmuxMobileShell/MobileShellComposite.swift (1)

5429-5432: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Add surface/connection identifiers to latency trace stamps.

The analyzer groups by stage, not by ownership context, so stamps without surface or connection scope can join records from different terminals or connections across active sessions.

  • iOS input path: include surfaceID in in.send, in.settled, in.resp, and ev.grid.
  • MobileHostService.swift: include connection identity in host.enq and host.write alongside the sequence/depth data.
🤖 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
`@Packages/iOS/CmuxMobileShell/Sources/CmuxMobileShell/MobileShellComposite.swift`
around lines 5429 - 5432, Update the latency trace stamps in
MobileShellComposite.swift at lines 5429-5432, 5440, 7757-7759, and 8391-8395 to
include surfaceID for in.send, in.settled, in.resp, and ev.grid. Update
MobileHostService.swift at lines 540-546 and 2610-2617 so host.enq and
host.write include the connection identity alongside sequence/depth data.
Packages/iOS/CmuxMobileTerminal/Sources/CmuxMobileTerminal/GhosttySurfaceView.swift (1)

1203-1217: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Calculate the actual cursor bottom edge.

ghostty_surface_ime_point returns x/y/width/height as the cursor rectangle, so rejecting y == 0 hides valid first-row cursors and returning only y pins to the rectangle origin instead of the bottom. Drop the row-origin check and return the measured bottom after checking the height is positive.

Suggested fix
-        guard y > 0 else { return nil }
-        return CGFloat(y)
+        guard height > 0 else { return nil }
+        return CGFloat(y + height)
🤖 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
`@Packages/iOS/CmuxMobileTerminal/Sources/CmuxMobileTerminal/GhosttySurfaceView.swift`
around lines 1203 - 1217, Update cursorBottomInRenderPoints() to accept valid
cursors with y == 0, reject only rectangles whose measured height is not
positive, and return the cursor rectangle’s bottom edge by adding height to y
rather than returning y alone.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In
`@Packages/iOS/CmuxMobileShell/Sources/CmuxMobileShell/MobileShellComposite.swift`:
- Around line 5429-5432: Update the latency trace stamps in
MobileShellComposite.swift at lines 5429-5432, 5440, 7757-7759, and 8391-8395 to
include surfaceID for in.send, in.settled, in.resp, and ev.grid. Update
MobileHostService.swift at lines 540-546 and 2610-2617 so host.enq and
host.write include the connection identity alongside sequence/depth data.

In
`@Packages/iOS/CmuxMobileShell/Tests/CmuxMobileShellTests/MobileShellRenderGridReplayStalenessTests.swift`:
- Around line 14-75: In screenAnchoredReplayBaselinesNextLiveDelta, install
defer { collector.unmount() } immediately after
collector.mount(store:surfaceID:) so cleanup runs on every exit path, including
thrown awaits and `#require` failures. Remove the trailing explicit
collector.unmount() call.

In
`@Packages/iOS/CmuxMobileTerminal/Sources/CmuxMobileTerminal/GhosttySurfaceView.swift`:
- Around line 1203-1217: Update cursorBottomInRenderPoints() to accept valid
cursors with y == 0, reject only rectangles whose measured height is not
positive, and return the cursor rectangle’s bottom edge by adding height to y
rather than returning y alone.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 82b9d967-a296-4571-b6c5-1cb364557879

📥 Commits

Reviewing files that changed from the base of the PR and between 359b8d7 and c67612b.

📒 Files selected for processing (7)
  • Packages/iOS/CmuxMobileDiagnostics/Sources/CmuxMobileDiagnostics/MobileLatencyTrace.swift
  • Packages/iOS/CmuxMobileShell/Sources/CmuxMobileShell/MobileShellComposite+TerminalOutputDelivery.swift
  • Packages/iOS/CmuxMobileShell/Sources/CmuxMobileShell/MobileShellComposite.swift
  • Packages/iOS/CmuxMobileShell/Tests/CmuxMobileShellTests/MobileShellRenderGridReplayStalenessTests.swift
  • Packages/iOS/CmuxMobileTerminal/Sources/CmuxMobileTerminal/GhosttySurfaceView.swift
  • Sources/Mobile/MobileHostService.swift
  • Sources/TerminalController.swift

@cursor

cursor Bot commented Jul 29, 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.

@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 (4)
Packages/iOS/CmuxMobileShell/Sources/CmuxMobileShell/MobileShellComposite.swift (1)

5426-5445: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Add the surface token to input send/settlement stamps.

in.send/in.settled only carry n, while in.resp carries s. Pipelined requests can overlap across surfaces, so the analyzer’s global timestamp pairing can associate a response with the wrong batch. Emit s= on both stamps and partition pair_input_batches by that token.

Also applies to: 6822-6830

🤖 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
`@Packages/iOS/CmuxMobileShell/Sources/CmuxMobileShell/MobileShellComposite.swift`
around lines 5426 - 5445, Update the DEBUG input tracing around
sendRemoteTerminalInput and the corresponding settlement block to include the
same surface token used by in.resp as s= on both in.send and in.settled stamps.
Then update pair_input_batches to partition or match batches by that surface
token before pairing timestamps, preserving correct pairing for overlapping
requests across surfaces; apply the same change to the additional
send/settlement block around the other referenced occurrence.
scripts/mobile-latency-trace/analyze.py (3)

437-442: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Silent pass-through when zero joins are possible for cross-clock validation.

If joined_count == 0 (e.g. surfaces never line up between the two logs), cross_clock_enabled stays True with no warning even though the Mac/iOS clock-alignment check never actually validated anything — the resulting same-clock metrics would then be reported without any confirmed alignment.

♻️ Warn explicitly when no joins could be validated
-        if joined_count and violations / joined_count > 0.10:
+        if joined_count == 0:
+            cross_clock_enabled = False
+            warning = "WARNING: no host/iOS joins available to validate clock alignment; dropping cross-clock metrics."
+        elif violations / joined_count > 0.10:
             cross_clock_enabled = False
             warning = (
                 "WARNING: host.in.recv fell outside in.send→in.settled for "
                 f"{violations}/{joined_count} joins; dropping cross-clock metrics."
             )
🤖 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 `@scripts/mobile-latency-trace/analyze.py` around lines 437 - 442, Update the
cross-clock validation conditional near cross_clock_enabled so joined_count == 0
disables cross-clock metrics and sets a warning explaining that no joins were
available to validate clock alignment. Preserve the existing violation-rate
warning and threshold behavior for cases with one or more joins.

296-333: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Duplicated grid→write→enqueued resolution vs the inline echo-hop block.

mac_frame_chain() and the inline block at lines 488-514 both implement the same four-first_after-call chain (host.grid → host.write → conn → host.enq bounded by write time), differing only in whether the initial grid lookup uses >= sequence (here, tolerating coalescing from an "applied" stamp) or == echo_sequence (there, tracing an already-resolved specific frame). The duplicated shape risks drifting further apart on the next edit. Consider parameterizing mac_frame_chain (e.g. an exact: bool or an explicit sequence-matcher) so both call sites share one implementation.

🤖 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 `@scripts/mobile-latency-trace/analyze.py` around lines 296 - 333, Refactor
mac_frame_chain to accept a configurable sequence-matching mode or predicate for
its initial host.grid lookup, while retaining the existing coalescing >=
sequence behavior for applied stamps. Replace the inline echo-hop host.grid →
host.write → host.enq resolution with this shared helper using exact
echo_sequence matching, preserving the connection and write-time bounds.

467-470: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Fragile id()-based dict key.

input_identity keys a dict by id(pair) purely to recover the index of binding inside probe_bindings. This only stays correct because input_pairs remains alive and unmodified for the rest of analyze(); it's an unusual pattern that would silently misbehave if input_pairs were ever filtered/rebuilt between construction and use (CPython can and does reuse id() values for garbage-collected objects). Storing the index directly avoids the indirection entirely.

♻️ Store the index instead of reverse-looking it up via id()
-    probe_bindings: list[tuple[Stamp, tuple[Stamp, Stamp, Stamp | None]]] = []
+    probe_bindings: list[tuple[Stamp, tuple[Stamp, Stamp, Stamp | None], int]] = []
     input_index = 0
     for probe in ios.get("probe.send", []):
         while input_index < len(input_pairs) and input_pairs[input_index][0].time_us < probe.time_us:
             input_index += 1
         if input_index >= len(input_pairs):
             break
         binding = input_pairs[input_index]
+        bound_index = input_index
         input_index += 1
-        probe_bindings.append((probe, binding))
+        probe_bindings.append((probe, binding, bound_index))

And later:

-        input_identity = {id(pair): index for index, pair in enumerate(input_pairs)}
-        for probe, binding in probe_bindings:
-            index = input_identity.get(id(binding))
+        for probe, binding, index in probe_bindings:
             host_pair = host_by_input_index.get(index) if index is not None else None
🤖 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 `@scripts/mobile-latency-trace/analyze.py` around lines 467 - 470, Replace the
id()-based input_identity reverse lookup in analyze() with bindings that carry
each input pair’s index directly through probe_bindings. Update the loop over
probe_bindings to read that stored index and preserve the existing
host_by_input_index lookup behavior.
🤖 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 `@scripts/mobile-latency-trace/analyze.py`:
- Around line 351-365: Bind the current loop values as lambda default arguments
in the ev.grid loop’s first_after predicate, using surface and sequence, and
apply the same change to every flagged lambda in the probe_bindings loop around
probe_bindings, including surface, echo_sequence, and connection. Preserve the
existing predicate logic while ensuring each closure retains that iteration’s
values.

---

Outside diff comments:
In
`@Packages/iOS/CmuxMobileShell/Sources/CmuxMobileShell/MobileShellComposite.swift`:
- Around line 5426-5445: Update the DEBUG input tracing around
sendRemoteTerminalInput and the corresponding settlement block to include the
same surface token used by in.resp as s= on both in.send and in.settled stamps.
Then update pair_input_batches to partition or match batches by that surface
token before pairing timestamps, preserving correct pairing for overlapping
requests across surfaces; apply the same change to the additional
send/settlement block around the other referenced occurrence.

In `@scripts/mobile-latency-trace/analyze.py`:
- Around line 437-442: Update the cross-clock validation conditional near
cross_clock_enabled so joined_count == 0 disables cross-clock metrics and sets a
warning explaining that no joins were available to validate clock alignment.
Preserve the existing violation-rate warning and threshold behavior for cases
with one or more joins.
- Around line 296-333: Refactor mac_frame_chain to accept a configurable
sequence-matching mode or predicate for its initial host.grid lookup, while
retaining the existing coalescing >= sequence behavior for applied stamps.
Replace the inline echo-hop host.grid → host.write → host.enq resolution with
this shared helper using exact echo_sequence matching, preserving the connection
and write-time bounds.
- Around line 467-470: Replace the id()-based input_identity reverse lookup in
analyze() with bindings that carry each input pair’s index directly through
probe_bindings. Update the loop over probe_bindings to read that stored index
and preserve the existing host_by_input_index lookup behavior.
🪄 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 Plus

Run ID: 0bb175f9-6705-477d-aac4-59665069d207

📥 Commits

Reviewing files that changed from the base of the PR and between c67612b and 2cff20e.

📒 Files selected for processing (10)
  • Packages/iOS/CmuxMobileShell/Sources/CmuxMobileShell/MobileShellComposite+TerminalOutputDelivery.swift
  • Packages/iOS/CmuxMobileShell/Sources/CmuxMobileShell/MobileShellComposite.swift
  • Packages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/GhosttySurfaceRepresentable.swift
  • Packages/iOS/CmuxMobileTerminal/Sources/CmuxMobileTerminal/GhosttySurfaceView.swift
  • Sources/Mobile/MobileHostService.swift
  • Sources/Mobile/MobileTerminalByteTee.swift
  • Sources/Mobile/MobileTerminalRenderObserver.swift
  • Sources/TerminalController.swift
  • scripts/mobile-latency-trace/README.md
  • scripts/mobile-latency-trace/analyze.py

Comment on lines +351 to +365
surface = event.fields.get("s")
if sequence is None or surface is None:
continue
gate = first_after(
ios.get("gate", []),
event.time_us,
lambda stamp: stamp.fields.get("s") == surface
and stamp.integer("seq") == sequence,
)
if gate is None:
continue
add_duration(metrics, "iOS: ev.grid → gate", event, gate)
if gate.fields.get("out") != "delivered":
continue
chain = frame_chain(surface, sequence, gate.time_us, ios)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Loop-variable closures flagged by Ruff (B023) — safe today, fragile for later edits.

The lambdas passed to first_after() inside the ev.grid loop (e.g. surface, sequence at lines 357-358) and the probe_bindings loop (surface, echo_sequence, connection at 491-519) close over loop-scoped variables. This works correctly today only because each lambda is consumed synchronously by first_after() before the loop variable is reassigned — but it's the classic Python late-binding trap: if a future refactor stores one of these lambdas (e.g. for batching or deferred evaluation), all deferred lookups will silently use the final iteration's values.

♻️ Bind current values as default args to silence B023 and future-proof the closures
-        gate = first_after(
-            ios.get("gate", []),
-            event.time_us,
-            lambda stamp: stamp.fields.get("s") == surface
-            and stamp.integer("seq") == sequence,
-        )
+        gate = first_after(
+            ios.get("gate", []),
+            event.time_us,
+            lambda stamp, surface=surface, sequence=sequence: stamp.fields.get("s") == surface
+            and stamp.integer("seq") == sequence,
+        )

Apply the same pattern to the other flagged lambdas at 491-519.

Also applies to: 478-519

🧰 Tools
🪛 Ruff (0.16.0)

[warning] 357-357: Function definition does not bind loop variable surface

(B023)


[warning] 358-358: Function definition does not bind loop variable sequence

(B023)

🤖 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 `@scripts/mobile-latency-trace/analyze.py` around lines 351 - 365, Bind the
current loop values as lambda default arguments in the ev.grid loop’s
first_after predicate, using surface and sequence, and apply the same change to
every flagged lambda in the probe_bindings loop around probe_bindings, including
surface, echo_sequence, and connection. Preserve the existing predicate logic
while ensuring each closure retains that iteration’s values.

Source: Linters/SAST tools

@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 (2)
Packages/iOS/CmuxMobileShell/Sources/CmuxMobileShell/MobileShellComposite.swift (1)

7819-7824: 🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

Correlate input responses with the batch ID. A successful terminal-lane send emits in.settled ok=1 but no in.resp; the analyzer then assigns a later RPC response to that earlier batch and shifts subsequent probe measurements.

  • Packages/iOS/CmuxMobileShell/Sources/CmuxMobileShell/MobileShellComposite.swift#L7819-L7824: thread latencyBatchNumber into the response handler and emit n=<batch> on in.resp.
  • scripts/mobile-latency-trace/analyze.py#L188-L196: match responses by n, rather than consuming the next response by timestamp; add a self-test with a successful no-response lane settlement before an RPC response.
  • scripts/mobile-latency-trace/README.md#L65-L70: document the required keyed correlation schema.
🤖 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
`@Packages/iOS/CmuxMobileShell/Sources/CmuxMobileShell/MobileShellComposite.swift`
around lines 7819 - 7824, Correlate latency responses by batch ID across all
affected sites: in
Packages/iOS/CmuxMobileShell/Sources/CmuxMobileShell/MobileShellComposite.swift
lines 7819-7824, thread latencyBatchNumber into the response handler and include
n=<batch> in the in.resp trace; in scripts/mobile-latency-trace/analyze.py lines
188-196, match responses using n instead of consuming the next timestamp-ordered
response and add a self-test covering a successful no-response settlement before
an RPC response; in scripts/mobile-latency-trace/README.md lines 65-70, document
the required keyed correlation schema.
Packages/iOS/CmuxMobileShell/Sources/CmuxMobileShell/MobileShellComposite+TerminalOutputDelivery.swift (1)

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

Cancel the input-ACK resubscribe retry from the non-barrier reset path too.

terminalOutputDidReset(surfaceID:streamToken:) schedules a per-surface delay task in handleTerminalInputResponse, but the non-barrier branch only clears replay/delta state and requests a fresh replay. Add cancelTerminalInputAckResubscribeRetry(surfaceID: surfaceID) in that branch so a pending retry cannot run after the surface has been reset and a new requestTerminalReplay has been issued.

🤖 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
`@Packages/iOS/CmuxMobileShell/Sources/CmuxMobileShell/MobileShellComposite`+TerminalOutputDelivery.swift
around lines 590 - 631, Update the non-barrier reset branch in
terminalOutputDidReset(surfaceID:streamToken:) to call
cancelTerminalInputAckResubscribeRetry(surfaceID: surfaceID) before issuing the
fresh requestTerminalReplay. Leave the existing barrier-reset handling unchanged
and ensure any pending per-surface input-ACK retry is canceled before replay
restarts.
🤖 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
`@Packages/iOS/CmuxMobileShell/Sources/CmuxMobileShell/MobileShellComposite.swift`:
- Around line 7905-7906: Remove the clock-based delayed retry flow around
terminalInputAckResubscribeRetryTask, including the stored task and its Task
closure that calls clock.sleep. Trigger subscription refresh from the
authoritative liveness/state transition already used by MobileShellComposite
instead of scheduling recovery after an arbitrary delay.

---

Outside diff comments:
In
`@Packages/iOS/CmuxMobileShell/Sources/CmuxMobileShell/MobileShellComposite.swift`:
- Around line 7819-7824: Correlate latency responses by batch ID across all
affected sites: in
Packages/iOS/CmuxMobileShell/Sources/CmuxMobileShell/MobileShellComposite.swift
lines 7819-7824, thread latencyBatchNumber into the response handler and include
n=<batch> in the in.resp trace; in scripts/mobile-latency-trace/analyze.py lines
188-196, match responses using n instead of consuming the next timestamp-ordered
response and add a self-test covering a successful no-response settlement before
an RPC response; in scripts/mobile-latency-trace/README.md lines 65-70, document
the required keyed correlation schema.

In
`@Packages/iOS/CmuxMobileShell/Sources/CmuxMobileShell/MobileShellComposite`+TerminalOutputDelivery.swift:
- Around line 590-631: Update the non-barrier reset branch in
terminalOutputDidReset(surfaceID:streamToken:) to call
cancelTerminalInputAckResubscribeRetry(surfaceID: surfaceID) before issuing the
fresh requestTerminalReplay. Leave the existing barrier-reset handling unchanged
and ensure any pending per-surface input-ACK retry is canceled before replay
restarts.
🪄 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 Plus

Run ID: 4740c5b8-ede3-45dc-9492-acbe948900c9

📥 Commits

Reviewing files that changed from the base of the PR and between 2cff20e and dc0bf23.

📒 Files selected for processing (15)
  • Packages/iOS/CmuxMobileDiagnostics/Sources/CmuxMobileDiagnostics/MobileDebugLogSink.swift
  • Packages/iOS/CmuxMobileDiagnostics/Sources/CmuxMobileDiagnostics/MobileLatencyTrace.swift
  • Packages/iOS/CmuxMobileDiagnostics/Sources/CmuxMobileDiagnostics/MobileLatencyTraceWriter.swift
  • Packages/iOS/CmuxMobileShell/Sources/CmuxMobileShell/MobileShellComposite+TerminalOutputDelivery.swift
  • Packages/iOS/CmuxMobileShell/Sources/CmuxMobileShell/MobileShellComposite+TerminalReplayLifecycle.swift
  • Packages/iOS/CmuxMobileShell/Sources/CmuxMobileShell/MobileShellComposite+TerminalReplayRetry.swift
  • Packages/iOS/CmuxMobileShell/Sources/CmuxMobileShell/MobileShellComposite.swift
  • Packages/iOS/CmuxMobileShell/Sources/CmuxMobileShell/TerminalOutputDelivery.swift
  • Packages/iOS/CmuxMobileShell/Tests/CmuxMobileShellTests/InputAckRetryClock.swift
  • Packages/iOS/CmuxMobileShell/Tests/CmuxMobileShellTests/MobileShellRenderGridInputCatchUpTests.swift
  • Packages/iOS/CmuxMobileShell/Tests/CmuxMobileShellTests/MobileShellRenderGridLivenessTestSupport.swift
  • Packages/iOS/CmuxMobileShell/Tests/CmuxMobileShellTests/TerminalOutputDeliveryQueueTests.swift
  • Packages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/GhosttySurfaceRepresentable.swift
  • scripts/mobile-latency-trace/README.md
  • scripts/mobile-latency-trace/analyze.py

@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
`@Packages/iOS/CmuxMobileShell/Sources/CmuxMobileShell/MobileShellComposite`+TerminalOutputDelivery.swift:
- Around line 203-209: Update the DEBUG MobileLatencyTrace stamp in the advisory
branch of the terminal output delivery flow to emit out=advisory instead of
out=delivered, while preserving out=replay_req when requestReplay is true. Keep
out=delivered reserved for the path that actually calls
deliverTerminalRenderGrid.
🪄 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 Plus

Run ID: 2836ea41-ac97-4df5-adcd-189261c4920c

📥 Commits

Reviewing files that changed from the base of the PR and between dc0bf23 and 0ef6e45.

📒 Files selected for processing (12)
  • Packages/iOS/CmuxMobileDiagnostics/Sources/CmuxMobileDiagnostics/MobileLatencyTraceWriter.swift
  • Packages/iOS/CmuxMobileShell/Sources/CmuxMobileShell/MobileShellComposite+TerminalOutputDelivery.swift
  • Packages/iOS/CmuxMobileShell/Sources/CmuxMobileShell/MobileShellComposite.swift
  • Packages/iOS/CmuxMobileShell/Tests/CmuxMobileShellTests/MobileShellRenderGridInputCatchUpTests.swift
  • Packages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/GhosttySurfaceRepresentable.swift
  • Packages/iOS/CmuxMobileTerminal/Sources/CmuxMobileTerminal/GhosttySurfaceView.swift
  • Sources/App/HostLatencyTrace.swift
  • Sources/Mobile/MobileHostService.swift
  • Sources/Mobile/MobileTerminalByteTee.swift
  • Sources/Mobile/MobileTerminalRenderObserver.swift
  • Sources/TerminalController.swift
  • scripts/mobile-latency-trace/analyze.py

Comment on lines +203 to +209
#if DEBUG
MobileLatencyTrace.stamp(
"gate",
"s=\(renderGrid.surfaceID.prefix(8).lowercased()) " +
"seq=\(renderGrid.stateSeq) " +
"out=\(deliveryDecision.requestReplay ? "replay_req" : "delivered")"
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

Do not label advisory decisions as delivered.

This branch updates screen/policy state and may request replay, but it returns without calling deliverTerminalRenderGrid. Emitting out=delivered can make scripts/mobile-latency-trace/analyze.py treat the advisory stamp as an applied frame and pair it with a later same-sequence presentation. Use a distinct value such as out=advisory; reserve out=delivered for the actual delivery path.

Based on the supplied trace analyzer, gate.out=delivered is treated as a real delivery marker.

Suggested trace fix
-                    "out=\(deliveryDecision.requestReplay ? "replay_req" : "delivered")"
+                    "out=\(deliveryDecision.requestReplay ? "replay_req" : "advisory")"
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
#if DEBUG
MobileLatencyTrace.stamp(
"gate",
"s=\(renderGrid.surfaceID.prefix(8).lowercased()) " +
"seq=\(renderGrid.stateSeq) " +
"out=\(deliveryDecision.requestReplay ? "replay_req" : "delivered")"
)
`#if` DEBUG
MobileLatencyTrace.stamp(
"gate",
"s=\(renderGrid.surfaceID.prefix(8).lowercased()) " +
"seq=\(renderGrid.stateSeq) " +
"out=\(deliveryDecision.requestReplay ? "replay_req" : "advisory")"
)
🤖 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
`@Packages/iOS/CmuxMobileShell/Sources/CmuxMobileShell/MobileShellComposite`+TerminalOutputDelivery.swift
around lines 203 - 209, Update the DEBUG MobileLatencyTrace stamp in the
advisory branch of the terminal output delivery flow to emit out=advisory
instead of out=delivered, while preserving out=replay_req when requestReplay is
true. Keep out=delivered reserved for the path that actually calls
deliverTerminalRenderGrid.

# Conflicts:
#	Packages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/GhosttySurfaceRepresentable.swift
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.

1 participant