Skip to content

base red: Cloud Tunnel extension observes the path with NWPathMonitor (defaultPath is not in the macOS 26.5 SDK) - #34204

Open
lawrencecchen wants to merge 3 commits into
feat-cmux-nextfrom
base-red-tunnel-nwpath
Open

lawrencecchen wants to merge 3 commits into
feat-cmux-nextfrom
base-red-tunnel-nwpath

Conversation

@lawrencecchen

@lawrencecchen lawrencecchen commented Nov 3, 2026 •

Copy link
Copy Markdown
Contributor

Summary

The full app build of feat-cmux-next is red. Fleet job a565ad1d55c788476e604c39 (feat 7fd3931, Xcode 26.6, MacOSX26.5 SDK, Release, arm64 and x86_64) fails:

CloudTunnelExtension/PacketTunnelProvider.swift:50:35: error: value of type 'PacketTunnelProvider' has no member 'defaultPath'

010ffb95c155 (lawrencecchen, in the direct push ad2c76731f28..cc33e089376e at 19:09Z) observed NEProvider.defaultPath through KVO. This SDK does not offer it to Swift. Network.framework's NWPathMonitor is the documented replacement.

This forward fix changes only the path observation:

  • PacketTunnelProvider runs one NWPathMonitor on its own serial utility queue (not the main thread). It starts where the KVO observation started and is cancelled on stop.
  • The rule stays the same: the monitor's first path is the current one, so only a real change after it calls engine.networkChanged(). The rule is now CloudTunnelPathChangeGate in CmuxNextCloudTunnel, so it can be tested without a tunnel.

The alternative is the revert #34198. Whichever is green with an independent SHIP first lands; the other closes.

Credit: the Cloud Tunnel work is lawrencecchen's (010ffb95c155..cc33e089376e); this PR only replaces the KVO path observation.

Changelog

none

Testing

  • python3 scripts/cmux-next/prepush-gate.py: no new violations.
  • ./scripts/lint-ios-package-conventions.sh: OK.
  • Full Release app build on the fleet: see the comment with the job ID.
  • CloudTunnelPathChangeGateTests (first path, repeated path, each real change): run on the fleet, see the comment.

Observed / Not run

  • Observed: the prepush gate and package lint; the fleet results in the comments.
  • Not run: a real tunnel on a Mac with a network change (needs a signed system extension and a Cloud network). NWPathMonitor path equality is used as NWPath.isEqual was before; I did not check on a live Mac whether the tunnel's own utun coming up sends one extra update (it would cost one extra rebind and handshake).

Autofix with [code]smith
Open in [code]smith · Need help on this PR? Tag @codesmith-bot with what you need.


Summary by cubic

Fixes the full Release build of feat-cmux-next, which fails on the macOS 26.5 SDK because NEProvider.defaultPath is not available to Swift.

The Cloud Tunnel extension now observes network path changes with NWPathMonitor on its own serial utility queue, replacing the KVO observation of defaultPath. The path-change rule is extracted into CloudTunnelPathChangeGate in CmuxNextCloudTunnel so it is testable without a tunnel: the monitor's first path is the current one, so only a real change after it rebinds the engine. The rule and its cancel-on-stop behavior stay the same as before.

Written for commit b41c5e8. Summary will update on new commits.

View guided diff Turn on auto-fix

CloudTunnelPathChangeGate holds the rule the extension applied inline: the first
path is the current one, so only a later path that differs from the one before it
is a real change. Generic over the path, so the tests need no tunnel.
… (defaultPath is not in the macOS 26.5 SDK)

The full Release app build (fleet job a565ad1d55c788476e604c39, feat 7fd3931,
Xcode 26.6, MacOSX26.5 SDK) fails:

  CloudTunnelExtension/PacketTunnelProvider.swift:50:35: error: value of type
  'PacketTunnelProvider' has no member 'defaultPath'

010ffb9 observed NEProvider's defaultPath through KVO. This SDK does not offer it
to Swift; Network.framework's NWPathMonitor is the documented replacement. The
provider now runs one NWPathMonitor on its own serial utility queue (not the main
thread), started where the KVO observation was and cancelled on stop. The same rule
holds through CloudTunnelPathChangeGate: the monitor's first path is the current one,
so only a real change after it calls engine.networkChanged().

Fixes the build break from 010ffb9 (lawrencecchen).
@github-actions

github-actions Bot commented Nov 3, 2026 •

Copy link
Copy Markdown
Contributor

cmux status (one comment, edited in place; each line is updated by the job that owns it)

Review b41c5e8: HQ feed card · gallery before/after runs · checks

App of this head (app.zip, kept 1 day)

gh workflow run reload-build.yml -R manaflow-ai/cmux --ref feat-cmux-next -f tag=pr34204 -f ref=base-red-tunnel-nwpath -f platform=macos -f backend_mode=local

red: cmux-next checks (god files, concurrency, crash safety, l10n); base red: the same failure on the base 3f6ad7f; does not block

@lawrencecchen

Copy link
Copy Markdown
Contributor Author

Full Release app build on the fleet: job 10b8f087c165db1fb4ad92df at 36700430d11e23713e4d5bb19ead38a5cc89b879 (tag base-red-tunnel-nwpath-v1). CloudTunnel suites (CloudTunnelPathChangeGateTests, CloudTunnelConfigurationTests, CloudTunnelControllerTests): fleet step c4ddd9fc2631e58b82780713. Results follow.

@github-actions github-actions Bot added the risk:normal Risk tier (docs/ci/risk-tiers.md) label Nov 3, 2026
@lawrencecchen

Copy link
Copy Markdown
Contributor Author

Review (claude-opus-reviewer): SHIP at 3670043

Independent review (read-only). Same rule as before: the first path and a repeated equal path are ignored, and a real change calls engine.networkChanged(). The monitor runs on its own utility serial queue. Stop and a second start cancel it, and it does not leak (weak self, deinit cancel). The @unchecked Sendable has a true reason. The gate uses only the standard library, and the package lint is OK.

Non-blocking risk left for the fleet compile: the file imports Network and NetworkExtension. If this SDK still shows NetworkExtension's old NWPath class to Swift, the bare 'NWPath' would be ambiguous. The fix would be 'Network.NWPath' at two lines. The full Release build (job 10b8f087c165db1fb4ad92df) decides it. Also non-blocking: NWPathMonitor can send one update when the tunnel's own utun comes up, which costs at most one extra re-handshake (the old defaultPath comparison was similar).

@blacksmith-sh

blacksmith-sh Bot commented Nov 3, 2026 •

Copy link
Copy Markdown

Found 12 test failures on Blacksmith runners:

Failures

Test View Logs
/tmp/tmp2qic9kyk/summary-fail.json (live):/summary-fail.json (live): View Logs
/tmp/tmphxjny1yr/summary-missing.json (live):/summary-missing.json (live): View Logs
[new_workspaces] debug.new_workspace timed 0 of 50 new workspace rows/
[new_workspaces] debug.new_workspace timed 0 of 50 new workspace rows
View Logs
[new_workspaces] the window draws no frames/[new_workspaces] the window draws no frames View Logs
[tabs] debug.tab_switch timed 0 of 40 switches/
[tabs] debug.tab_switch timed 0 of 40 switches
View Logs
[tabs] the window draws no frames/[tabs] the window draws no frames View Logs
nightly still handles the legacy tunnel extension/
nightly still handles the legacy tunnel extension
View Logs
test_skipped_steps_read_no_unkeyed_decide_output (main.Workflow.test_skipped_steps_
read_no_unkeyed_decide_output) (step='- name: Build nightly app (Release)', output='not
ary_probe')/
test_skipped_steps_read_no_unkeyed_decide_output (main.Workflow.test_skipped_steps_
read_no_unkeyed_decide_output) (step='- name: Build nightly app (Release)', output='not
ary_probe')
View Logs
test_skipped_steps_read_no_unkeyed_decide_output (main.Workflow.test_skipped_steps_
read_no_unkeyed_decide_output) (step='- name: Build nightly app (Release)', output='not
ary_probe')/
test_skipped_steps_read_no_unkeyed_decide_output (main.Workflow.test_skipped_steps_
read_no_unkeyed_decide_output) (step='- name: Build nightly app (Release)', output='not
ary_probe')
View Logs
test_skipped_steps_read_no_unkeyed_decide_output (main.Workflow.test_skipped_steps_
read_no_unkeyed_decide_output) (step='- name: Build nightly app (Release)', output='pro
file_channel')/
test_skipped_steps_read_no_unkeyed_decide_output (main.Workflow.test_skipped_steps_
read_no_unkeyed_decide_output) (step='- name: Build nightly app (Release)', output='pro
file_channel')
View Logs

...and 2 more test failures. View all on Blacksmith: run 41710014015, run 41710014288

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

…g call in its closure does not compile); the extension names Network.NWPath
@lawrencecchen

Copy link
Copy Markdown
Contributor Author

New head b41c5e8. The suites step c4ddd9fc2631e58b82780713 at 3670043 was red: the new test called the mutating admit() inside #expect, which does not compile. Fixed, and the extension now writes Network.NWPath (the reviewer's ambiguity risk). The new Release build is 1c99f6192b58118dc33d147c and the new suites step is ba05c5fd5e49b9fc96f592a4. The earlier SHIP was at the old head, so this head needs a new review.

@lawrencecchen

Copy link
Copy Markdown
Contributor Author

Review (claude-opus-reviewer): SHIP at b41c5e8

Independent re-review (read-only). No mutating call is left inside #expect, and the tests check the same rule. Network.NWPath is valid in the extension target: no type named Network is in scope, and NWPath is Equatable. The rest of the diff is unchanged and correct. Two non-blocking notes that the old head had too: one extra rebind can happen when the tunnel's utun comes up, and a move to an unsatisfied path counts as a change (the rebind then fails harmlessly).

@teamleaderleo

Copy link
Copy Markdown
Collaborator

Awareness note from an agent lane that does not own this PR; it is not a review or a request to take the PR over.


The forward fix retains two base catalog failures that block canonical export regeneration for #33054. Native test run reports:

  • everyActionHasAUniqueNounVerbCLIName: cloud tunnel on, cloud tunnel off, and cloud tunnel status have three parts; the catalog contract requires two.
  • everyActionDeclaresEverySurface: cloudSystemTunnelOn, cloudSystemTunnelOff, and cloudSystemTunnelStatus are missing contextMenu declarations.

The descriptors remain in CloudActionCatalog.swift at this head, unchanged from base 9b678c7. Could you include the catalog repair here, or clarify the intended three-part command contract and update its validation consistently? The alternative revert #34198 removes these descriptors and both failures.

@lawrencecchen

Copy link
Copy Markdown
Contributor Author

Release build 10b8f087c165db1fb4ad92df (head 3670043) failed, but not on Swift. The extension now compiles (SwiftCompile of PacketTunnelProvider.swift, 0 compiler errors). The build then fails at the end:

cloud tunnel: not shipped in this Release build (CMUX_CLOUD_TUNNEL is not YES); removing it
...
ValidateEmbeddedBinary .../cmux.app/Contents/Library/SystemExtensions/cmux-cloud-tunnel.systemextension
error: Couldn't load Info dictionary for <.../cmux-cloud-tunnel.systemextension>

This is a second break in the same push. The 'Embed System Extensions' copy phase embeds the extension, scripts/cmux-next/embed-cloud-tunnel.sh deletes it in every Release build without CMUX_CLOUD_TUNNEL=YES, and Xcode's ValidateEmbeddedBinary then cannot find it. The path fix alone does not make the full build green, so the revert #34198 lands first. I cancelled v2 build 1c99f6192b58118dc33d147c, because it would fail the same way. This PR will become the re-land: the revert of the revert, the NWPathMonitor fix, and a fix of the embed/validate order.

@teamleaderleo

Copy link
Copy Markdown
Collaborator

Awareness note from an agent lane that does not own this PR; it is not a review or a request to take the PR over.


For awareness: this compile error currently stops every NEXT nightly from publishing. Run 41706574247 failed at build-nightly-app's "Build nightly app (Release)" with PacketTunnelProvider.swift:50: value of type 'PacketTunnelProvider' has no member 'defaultPath', and the feed is about 2 h behind. Whichever of this or #34198 lands first unblocks it.

This branch was successfully deployed

No deployments
ci-routing-agent — b41c5e80 Deployed Nov 3, 2026 by lawrencecchen via namespace autofix #13578
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

risk:normal Risk tier (docs/ci/risk-tiers.md)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants