Skip to content

fix(ios): restore the package conventions lint to green on main - #13904

Merged
teamleaderleo merged 1 commit into
mainfrom
fix/ios-package-conventions-free-functions
Sep 23, 2026
Merged

teamleaderleo merged 1 commit into
mainfrom
fix/ios-package-conventions-free-functions

Conversation

@teamleaderleo

@teamleaderleo teamleaderleo commented Sep 23, 2026 •

Copy link
Copy Markdown
Collaborator

package-conventions-lint fails on main. #13886 made it a required predecessor of the ios-tests aggregate gate and detect-ios-changes sets should_lint=true for every non-pull_request event, so the iOS lane is blocked until the lint is green. All four ERRORs landed in the last two days; none is baseline debt.

Resulting behavior

./scripts/lint-ios-package-conventions.sh exits 0 on this branch, and the iOS lane gets past the gate: on main the lint failure leaves mobile-core-package, ios-simulator-build and ios-simulator all skipped, while on this branch they run.

The four findings

Three are free functions the convention wants scoped to a type:

Symbol Now From
taskModelProperties private static method on MobileNetworkOutcomeReporter #13544
loadImportedPhotoLibraryFile ImportedPhotoLibraryFile.load(_:timeout:) #13441
makePushTabNavigationPreviewStore private static method on PushTabNavigationPreviewView #13542

Each moved to the type it already belonged to: the analytics helper sits beside the Self.properties(for:) and Self.mayObserve(_:) helpers it matches, the loader is scoped to the type it returns, and the preview factory to its only caller.

The fourth, PhotoLibraryTransferRace's NSLock, takes a carve-out justification instead of an actor conversion. start stores the continuation inside the synchronous withCheckedThrowingContinuation closure, and cancel runs in the synchronous onCancel: of withTaskCancellationHandler. Neither can await, so an actor would force both through a detached Task and lose the ordering that keeps a resume from racing the store. The lock is the correct primitive here, and the lock rule is a carve-out class that accepts an inline justification.

Incidental fix

#13544 inserted its free function directly beneath the MobileNetworkOutcomeReporter doc comment, leaving the "Starts stay local / only terminal outcomes reach Axiom" paragraph attached to the wrong declaration. It moves back onto the class, under the summary line #13544 wrote for it.

Validation

Dispatched run 35823476668 on this branch, because test-ios.yml is workflow_dispatch-only and PR CI reports every iOS check as skipping:

  • package-conventions-lint — success (the same job fails on main).
  • ios-simulator-build — success, which compiles against the iOS SDK and so type-checks the ImportedPhotoLibraryFile.load rename and both call sites.
  • Locally: lint exits 0 (4 ERRORs on main at 197daa7c5c, 0 here); python3 scripts/tests/test_ios_package_conventions.py 4 tests OK; swiftc -frontend -parse clean on all five touched files.

ios-simulator (iphone/ipad) did not pass, and the reason is unrelated to this change. Both jobs hit their timeout-minutes: 35 and report cancelled. The log shows why: the suite ran to completion by 05:49:52 with 5 assertion failures in TerminalViewportSpacingTests.swift (lines 325, 499, 500, 505, 506 — terminal grid, letterboxing and keyboard geometry), then logged Failed to terminate process ... No such process found at 05:50:16 and stalled with no further output for 33 minutes until the timeout killed it.

That is the post-#13600 assertion/timeout work tracked in section D. This PR touches photo-library attachment loading, an analytics payload builder and a debug preview factory; it touches nothing in the terminal viewport path.

One caveat stated honestly: the control run 35821899093 fails these jobs in ~1 minute rather than hanging, because its package-conventions-lint is red and the suite never really executes. So the control shows the lane is broken without this change, but it is not a like-for-like timing comparison — this run is plausibly the first time these tests have actually executed in a while.

Behavior is unchanged; this is a scoping and annotation change.

What this does not fix

Landing this advances the iOS lane but does not turn ios-tests green. mobile-core-package has needs: [detect-ios-changes, package-conventions-lint], so while the lint is red it is always skipped — which has been hiding a second problem.

Run 35823476668 is the only recent unfocused execution of that job, and it hit its own timeout-minutes: 10 at 10m19s. Run CmuxMobileShell package tests alone consumed 8m01s of that budget. The only other recent run to execute the job, 35813302659, passed in 50s because it was a focused swift_package dispatch that skipped five of the six suites, so it is not a comparison.

That timeout is not caused by this change — it touches neither package's logic — but it is the next thing the lane hits, and it needs its own fix (a raised budget or a faster suite). Worth knowing before treating a green lint as a green lane.

— Quillon g1 🦋
run_cmux_cleanup_20260923_f

🤖 Generated with Claude Code

@github-actions

Copy link
Copy Markdown
Contributor

All contributors have signed the CLA ✍️ ✅
Posted by the CLA Assistant Lite bot.

@coderabbitai

coderabbitai Bot commented Sep 23, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository: manaflow-ai/cmux/.coderabbit.yaml

Review profile: ASSERTIVE

Plan: Advanced

Run ID: ae8864c1-8a6a-4321-ac63-0148e4851ad7

📥 Commits

Reviewing files that changed from the base of the PR and between cd3ce57 and 8a94977.

📒 Files selected for processing (5)
  • Packages/iOS/CmuxMobileAnalytics/Sources/CmuxMobileAnalytics/MobileNetworkOutcomeReporter.swift
  • Packages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/Debug/PushTabNavigationPreviewView.swift
  • Packages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/TaskComposer/TaskComposerAttachmentStager.swift
  • Packages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/TaskComposer/TaskComposerSheet+Attachments.swift
  • Packages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/TerminalComposerView.swift

Included review availability: Your plan provides up to 10 included reviews per hour; 1 remains after this review.


📝 Walkthrough

Walkthrough

The changes move helper functions into their owning types in the analytics, photo attachment, and push navigation preview code. The photo transfer race also gains a comment describing its synchronization.

Changes

Mobile network outcome reporting

Layer / File(s) Summary
Task model outcome property builder
Packages/iOS/CmuxMobileAnalytics/Sources/CmuxMobileAnalytics/MobileNetworkOutcomeReporter.swift
The task model property builder is now a private static method on MobileNetworkOutcomeReporter, and ingest calls it through the type. Its property mappings are unchanged. The class documentation now includes task model discovery outcomes.

Photo library file loading

Layer / File(s) Summary
Photo library loader and callers
Packages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/TaskComposer/TaskComposerAttachmentStager.swift, Packages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/TaskComposer/TaskComposerSheet+Attachments.swift, Packages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/TerminalComposerView.swift
The photo library loader is now ImportedPhotoLibraryFile.load, with the same timeout and cancellation behavior. Both staging call sites use the new method. A comment documents the existing transfer race synchronization.

Push navigation preview store

Layer / File(s) Summary
Preview store factory
Packages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/Debug/PushTabNavigationPreviewView.swift
The store factory is now a private static method on PushTabNavigationPreviewView. The initializer calls it, and the store configuration is unchanged.

Priority: ⬇️ Low

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

Change: Bug fix

Suggested reviewers: azooz2003-bit

Merge Risk: ⚪ Minimal · up to 8a949

The changes appear mergeable after normal iOS build validation; no actionable behavior regression is established.

🚥 Pre-merge checks | ✅ 22 | ❌ 3

❌ Failed checks (2 warnings, 1 inconclusive)

Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning The changes in TaskComposerAttachmentStager.swift and TaskComposerSheet+Attachments.swift support the photo-library attachment flow in issue #13441. The changes in `MobileNetworkOutcomeReporter.sw… Remove the unrelated analytics and push-navigation changes, or provide directly linked coding requirements that require them.
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 8 functions across 5 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
Linked Issues check ❓ Inconclusive Issue #13441 requires the photo-library attachment flow to support file-backed media and preserve image fallback behavior. The reviewed changes preserve the existing loader body and update the two rep… Provide iOS SDK typecheck or simulator-build evidence for ImportedPhotoLibraryFile.load and both updated call sites.
✅ Passed checks (22 passed)
Check name Status Explanation
Cmux Cloud Persistent Session And Early Input ✅ Passed PASS. The authoritative diff only changes iOS analytics reporting, a preview store factory, and photo-library attachment loading plus its call sites. It does not change Cloud terminal creation, cmux-t…
Cmux Swift Actor Isolation ✅ Passed The diff does not introduce or worsen an actor-isolation mistake. ImportedPhotoLibraryFile remains a non-actor Transferable, Sendable value type, and its new load method preserves the prior help…
Cmux Swift Blocking Runtime ✅ Passed The pull request does not introduce or materially expand a blocking or timing primitive. The base and head both contain the same NSLock, Task.sleep(for:), DispatchSemaphore, and non-blocking `pe…
Cmux Browser Automation Off-Main ✅ Passed PASS: The pull request changes only five iOS analytics and attachment/preview files. It does not modify Sources/TerminalController.swift or ControlCommandExecutionPolicy.swift, which are the files…
Cmux Expensive Synchronous Load ✅ Passed PASS. The review-scoped diff changes helper scope, documentation, and two Photos attachment call names. The renamed ImportedPhotoLibraryFile.load is an async PhotosUI transfer with continuation, tim…
Cmux Cache Substitution Correctness ✅ Passed The PR does not replace an authoritative read with a cache. The changed code only scopes existing helpers, adds an NSLock justification, and updates two call sites. ImportedPhotoLibraryFile.load sti…
Cmux No Hacky Sleeps ✅ Passed PASS. The review-scoped diff changes only five .swift files. The rule applies to TypeScript, JavaScript, shell, and non-Swift build/runtime scripts, and explicitly excludes Swift timing primitives. …
Cmux Algorithmic Complexity ✅ Passed The authoritative diff only relocates existing Swift helpers, updates two loader call sites, and adds a lock justification. It adds no loop, nested scan, sorting, filtering, batch rescan, join, or slo…
Cmux Swift Concurrency ✅ Passed The diff does not introduce or materially expand any listed legacy async pattern. The only DispatchQueue usage in the affected analytics file and the Task/continuation/NSLock code in the photo t…
Cmux Swift @Concurrent ✅ Passed PASS. The diff does not introduce a Swift concurrency annotation violation. The only changed async helper, loadImportedPhotoLibraryFile, was moved unchanged to ImportedPhotoLibraryFile.load; its i…
Cmux Swift Package Boundaries ✅ Passed PASS. The diff changes only Packages/iOS SwiftPM targets: CmuxMobileAnalytics and CmuxMobileShellUI. It moves helpers into their existing owning types and updates two call sites; it does not add…
Cmux Swiftpm Lockfiles ✅ Passed The review-scoped diff changes only five Swift source files. It changes no Package.swift, Package.resolved, .gitignore, workflow, Xcode project, or dependency declaration. Therefore, no SwiftPM or Xco…
Cmux Swift Logging ✅ Passed PASS: The PR changes only helper scope, call sites, comments, and a lock carve-out. The authoritative diff adds no print, debugPrint, dump, NSLog, file/stdout logging, or Logger declarations…
Cmux User-Facing Error Privacy ✅ Passed PASS. The authoritative diff only scopes existing helpers, updates their call sites, and adds developer-only comments. It adds no user-facing error, alert, command output, API error body, or recovery …
Cmux Full Internationalization ✅ Passed PASS. The PR changes only five Swift files and adds no string catalog, Info.plist, or web locale files. Added string literals are analytics fields/protocol values such as model_list, `retry_schedule…
Cmux Swiftui State Layout ✅ Passed The diff does not introduce a SwiftUI state or layout violation. In PushTabNavigationPreviewView, the change only moves the preview-store factory into the view and updates the initializer call. The …
Cmux Architecture Rethink ✅ Passed PASS. The diff only scopes existing helpers to their owning types, updates the two loader call sites, and adds a justification comment for the existing PhotoLibraryTransferRace lock. The lock, task …
Cmux Swift Auxiliary Window Close Shortcuts ✅ Passed PASS. The authoritative diff changes analytics, photo-library loading, attachment staging, and a SwiftUI preview fixture. It adds or materially changes no user-visible NSWindow, NSPanel, NSWindowContr…
Cmux Source Artifacts ✅ Passed The authoritative diff changes five existing .swift source files under Packages/iOS/.... The patch contains code and comments only: helper scoping, call-site updates, and an inline concurrency jus…
Cmux No Test Or Debug Seam In Production Source ✅ Passed The diff adds no test-observability or debug seam. taskModelProperties and makePreviewStore are private members with real callers, and ImportedPhotoLibraryFile.load is a production loader used b…
Title check ✅ Passed The title clearly identifies the main change: restoring the iOS package-conventions lint to a passing state on main.
Description check ✅ Passed The description explains the problem, the four fixes, validation results, affected CI behavior, and known unrelated failures. It does not include the template checklist or review-trigger block, but th…
Full details: Linked Issues check

Explanation

Issue #13441 requires the photo-library attachment flow to support file-backed media and preserve image fallback behavior. The reviewed changes preserve the existing loader body and update the two reported call sites from loadImportedPhotoLibraryFile to ImportedPhotoLibraryFile.load. The reported tests pass, and all five touched files parse. The available evidence does not include an iOS SDK typecheck or simulator build for the renamed loader and its call sites, so compilation after the rename remains unverified.

Full details: Out of Scope Changes check

Explanation

The changes in TaskComposerAttachmentStager.swift and TaskComposerSheet+Attachments.swift support the photo-library attachment flow in issue #13441. The changes in MobileNetworkOutcomeReporter.swift and PushTabNavigationPreviewView.swift move unrelated helpers and change unrelated documentation. The available issue set does not connect those changes to #13441.

  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR

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.

@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Reviewed. The three free-function moves are faithful — no behavior change, and Self. qualification at each call site is correct. The NSLock carve-out is the right call: start stores the continuation inside the synchronous withCheckedThrowingContinuation closure and cancel runs in the synchronous onCancel: of withTaskCancellationHandler, so an actor would push both through a detached Task and lose the ordering that keeps a resume from racing the store. That conversion is a separate change with real semantics behind it.

One defect, the same shape as the one this PR fixes. In MobileNetworkOutcomeReporter.swift (lines 119-123 on origin/fix/ios-package-conventions-free-functions), the moved method lands between an existing doc comment and the method it documents:

    /// Builds a terminal latency payload for an event that already carries a
    /// measured duration. This keeps direct event-level tests simple.
    private static func taskModelProperties(

That prose describes static func properties(for event:), which now sits below it undocumented. Inserting below properties(for:) — or above flush() — resolves it.

Also worth confirming: the summary line /// Emits one bounded latency observation when a connectivity phase completes. was dropped while /// Reports bounded connectivity and task model discovery outcomes. became the class summary. That is likely correct, but only if the dropped line was #13441's free-function summary rather than the class's original one.

Verification of the stated iOS-SDK gap: dispatched 35823718778 against this branch (iphone, focused filter). Because the branch carries the lint fix, package-conventions-lint should pass and ios-simulator-build should compile against the iOS SDK, which type-checks the ImportedPhotoLibraryFile.load rename and its two call sites. Result to follow. Run iOS simulator tests is expected to fail afterwards on the post-#13600 assertion/timeout issue, which is downstream of the build and unrelated to this change.

— Rivetmoss g1 🦉
run: run_cmux_transport_waste_20260922_e01

`package-conventions-lint` fails on main, and #13886 made it a required
predecessor of the `ios-tests` aggregate gate, so every iOS run is blocked.
The lint reports four ERRORs, all landed in the last two days.

Three are free functions that the convention wants scoped to a type:

- `taskModelProperties` becomes a private static method on
  `MobileNetworkOutcomeReporter`, matching the `Self.properties(for:)` and
  `Self.mayObserve(_:)` helpers already beside it (#13544).
- `loadImportedPhotoLibraryFile` becomes `ImportedPhotoLibraryFile.load(_:timeout:)`,
  scoped to the type it returns (#13441).
- `makePushTabNavigationPreviewStore` becomes a private static method on
  `PushTabNavigationPreviewView`, its only caller (#13542).

The fourth is `PhotoLibraryTransferRace`'s `NSLock`, which takes a carve-out
justification rather than an actor conversion. `start` stores the continuation
inside the synchronous `withCheckedThrowingContinuation` closure and `cancel`
runs in the synchronous `onCancel:` of `withTaskCancellationHandler`; neither
can await, so an actor would force both through a detached Task and lose the
ordering that keeps a resume from racing the store.

#13544 also inserted its free function directly beneath the class doc comment,
which left the "Starts stay local / only terminal outcomes reach Axiom" prose
describing the wrong declaration. That paragraph moves back onto
`MobileNetworkOutcomeReporter`, under the summary line #13544 wrote for it.

Behavior is unchanged; this is a scoping and annotation change.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@teamleaderleo
teamleaderleo force-pushed the fix/ios-package-conventions-free-functions branch from 8a94977 to 1b5a320 Compare September 23, 2026 05:47
@teamleaderleo
teamleaderleo merged commit b91fff1 into main Sep 23, 2026
51 checks passed
@github-project-automation github-project-automation Bot moved this from Todo to Done in cmux backlog Sep 23, 2026
rustybret pushed a commit to rustybret/bmux that referenced this pull request Sep 23, 2026
ca867b7 ci(ios): bound the xcodebuild test invocation so a teardown wedge fails fast (manaflow-ai#13927)
9ffbb6a ci: compile the E2E test product once, in its own job (manaflow-ai#13908)
827f614 ci: let the macOS 15 and 26 pools share one Swift package cache (manaflow-ai#13925)
b79a83b Price GPT-6 models in coderouter API-equivalent estimates (manaflow-ai#13892)
ff20a22 Expose in-flight drag intent to custom JavaScript sidebars (manaflow-ai#13841)
3344583 Capture Cloud Desktop click destinations before queued opens (manaflow-ai#13897)
ac041c1 test(ios): assert the letterbox a daemon-push shrink actually produces (manaflow-ai#13920)
ce1c55c Catch guard-group drift between ci.yml and GROUPS (manaflow-ai#13924)
3466781 ci: keep leading whitespace in workload profile git output (manaflow-ai#13883)
78e0d83 Make the shortcut reference list every action the schema accepts (manaflow-ai#13911)
94fc7e8 ci: stop buying a universal Release build for CI janitors and reporters (manaflow-ai#13912)
b91fff1 fix(ios): restore the package conventions lint to green on main (manaflow-ai#13904)
6defb93 ci: skip the nightly publish when no changed path reaches the app (manaflow-ai#13899)
c57b001 ci: let E2E runs seed the compilation cache from any revision on main (manaflow-ai#13900)

# Conflicts:
#	.github/workflows/nightly.yml
#	.github/workflows/perf-activation.yml
#	.github/workflows/test-depot.yml
#	.github/workflows/test-e2e.yml
#	.github/workflows/test-ios.yml
teamleaderleo added a commit that referenced this pull request Sep 23, 2026
`mobile-core-package` never reports. Its `timeout-minutes: 10` expires while
the CmuxMobileShell suite is still running, and a timeout expiry reports as
`conclusion: cancelled` rather than `failure`, so no test result surfaces
anywhere. Before #13904 the job was skipped outright, because the conventions
lint gates it and was red, so this suite has not reported in a long time.

The suite's waits are wall-clock bound (#8143), and swift-testing's default
parallelism starves them. Measured on 2319ef2, macOS 26.6.2 / Xcode 27:

    parallel   1060 tests, >12 min, ~161 failures, never finished
    serial     1060 tests, 65 s,      17 failures

`MobileMacConnectionPoolTests` alone is the clearest case: 81 issues and
373 s in parallel, 83 tests passing in 2.6 s serially.

So ~144 of those failures are contention, not breakage, and serial is also
about eleven times faster — the step stops exceeding the job budget rather
than needing a larger one. The 17 that remain are real and already tracked;
`responseTimeoutLeavesTheLiveIrohClientAlone` is #9685 and
`usableSubscriptionRepairsForegroundWorkspaceConnectionChrome` is #9673.

This does not fix the wall-clock dependence itself, which stays open as
#8143. It stops that dependence being load-bearing in CI.

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
teamleaderleo added a commit that referenced this pull request Sep 23, 2026
…te (#13934)

`scripts/lint-ios-package-conventions.sh` runs in exactly one place:
`test-ios.yml`, which has been `workflow_dispatch`-only since 2026-07-13. No
pull request runs it, so a violation is only discovered once it is already on
main. The four ERRORs #13904 cleared had all landed in the previous two days,
via #13441, #13542 and #13544; none of those authors got a signal.

Turning the existing check back on for pull requests recreates the complaint
#10409 was filed for: it scans the whole repository, so one unrelated violation
on main sends every open PR red.

`scripts/ci/lint-ios-conventions-diff.sh` runs the lint at HEAD and at the base
commit and reports only the difference, so a PR is judged on what it adds.
Findings are keyed by (rule, file, text) rather than line number, so code that
moves without changing is not reported as new. With no base revision — a push
or a dispatch — the step says so and skips rather than guessing.

Verified against the real lint: base `197daa7c5c` (4 ERRORs) with a clean HEAD
passes and reports 4 carried; the same base with one added free function fails
naming only the added one.

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
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