Fix main's CI after the CodeRouter handoff merge

Merged
AU
24d ago
+138-66

Summary

Main has been red since #18953 (CodeRouter handoff with cmux authentication). macos / swift-package-tests stalls and gets killed, and about 50 app-host and CLI regression tests fail. Every PR merged since picks up the same failures. This fixes the five causes #18953 introduced.

  1. CodeRouter reader tests deadlock the package run. The new ControlClientLineReaderTests passed each payload to a DispatchQueue.global writer, then blocked in a poll(2) with no timeout. Swift Testing runs them in parallel on the 6-thread cooperative pool, so all pool threads could end up polling while no writer ever ran. The CmuxControlSocket run then stalled until the 180s watchdog killed it (exit 124; a stack sample shows six threads in poll and no writer thread). The fixture now queues the whole payload and EOF up front, with 64 KiB socket buffers. The largest fixture payload is about 8 KB. The reader and its caps are unchanged.
  2. Test fakes didn't echo the v2 request id. #18953 made the CLI reject a v2 response whose id doesn't match the request. The shared UnixSocketResponder and RelaySocketResponder, and the capabilities handler in tests/test_cli_socket_operation_deadline.py, replied with canned JSON and no id, so their tests failed with Mismatched v2 response id. They now put the request's id into canned v2 responses, the way the app's ControlResponseEncoder does. v1 lines and responses that already set an id are left byte-for-byte. Real apps always echo the id, so this was a test-only break.
  3. CodeRouter children inherited a blocked signal mask and a reused stdin. The two posix_spawn sites #18953 added in CLI/cmux.swift passed on the Swift concurrency thread's signal mask, so a CodeRouter child couldn't receive SIGTERM or SIGWINCH. That failed CLIExecInheritedSignalStateTests and the alias termination-signal test. Both sites now set an empty mask and default dispositions, like the other CLI spawn sites. The alias exec also checked which standard descriptors were open at spawn time, so a file the CLI opened after launch could reuse a closed stdin and reach the child as open. The CLI now records the caller's standard descriptors at the top of main and passes on only those.
  4. A test expected coderouter add codex to arm the handoff. That contradicts the test's own name ("only exact top-level agent names use socket handoff") and the launch policy. CodeRouter's handoff parser accepts only a provider name after --, and add does its own login without a handoff lease. So the policy is right; the expectation now asserts that add codex does not arm the handoff.
  5. Browser eval fixtures sent "id":null. The two CLIBrowserEvalOutputTests responses set an explicit null id, which the CLI now rejects. They now leave the id out, and the shared responder fills in the request's id.

Not addressed here, and not caused by #18953: CloudWorkspaceCreationRevealTests, GhosttyMinimumContrastLayerBackgroundTests and MuseVaultRegistrationTests already fail on a04496c, the last green main commit. CloudTreeHeaderActionsTests is a sidebar layout test that doesn't use sockets.

Testing

  • These failures are red on main right now: run 38359537491 at 370c51e4017 has swift-package-tests killed after 180s without progress, about 150 Mismatched v2 response id failures across app-host shards, and CLI regressions (1/3).
  • python3 scripts/verify-local.py: 17/17 checks passed. Swift compile and tests run in this PR's CI; local Swift test runs aren't allowed here, and the build host is unreachable.
  • This PR's CI on 1f0423b8264 is green. macos / swift-package-tests passed (the CmuxControlSocket run no longer stalls), and the changed app-host suites ran 123 tests in 3 suites, all passing, including CLIAuthAliasTests. The closed-stdin test needed one more fix: with descriptor 0 closed, /bin/sh opens the script it runs on that descriptor, so the old (: <&0) probe always reported stdin open. I confirmed that locally with a standalone posix_spawn probe.

Changelog

none

🤖 Generated with Claude Code