Skip to content

feat: add --command to terminal creation commands - #9614

Merged
austinywang merged 94 commits into
mainfrom
feat-2538-command-flag-pane-surface
Sep 9, 2026
Merged

austinywang merged 94 commits into
mainfrom
feat-2538-command-flag-pane-surface

Conversation

@austinywang

@austinywang austinywang commented Aug 5, 2026 •

Copy link
Copy Markdown
Contributor

Closes #2538

Summary

  • add --command <text> support to new-split, new-pane, and new-surface
  • deliver creation commands as Ghostty spawn-time initial_input, preserving command text literally and appending one Enter while keeping the normal interactive shell alive
  • carry raw initial input through workspace, split, pane, surface, and Dock creation; reject non-terminal --type combinations and unsupported remote-tmux mirror routing explicitly
  • retain the existing initial_command socket behavior for API clients that intentionally replace the shell with a one-shot process
  • localize the shared CLI help description in English and Japanese

Tests

  • keep the regression and fix in separate commits; CI run 31135031764 records the regression-only commit failing at the focused terminal-creation CLI step
  • cover all four CLI creation commands with a fake socket, including one-request delivery, literal escape preservation, omission, whitespace-only input, non-terminal rejection, and absence of initial_command
  • cover coordinator parsing, workspace/split/pane/surface creation, Dock creation, and remote-tmux fail-closed behavior
  • validate Swift and Python syntax, localization JSON and locale coverage, test-target wiring, package/workspace policies, and git diff --check without running a local build

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

Adds --command <text> to new-split, new-pane, new-surface, and new-workspace; for new-workspace, commands enter the shell at spawn instead of through the old post-create send, so the shell stays interactive. Closes Linear #2538.

Behavior

  • Preserves command text literally and appends one Enter.
  • Ignores blank input, requires a value, and rejects non-terminal --type combinations.
  • Layout-based new-workspace creation ignores the command and skips auto-welcome when input is injected.
  • Keeps initial_command behavior unchanged.
  • Rejects initial_input for remote tmux mirrors and keeps cloud-adjacent splits local.
  • Updates English and Japanese help, CLI/API docs, and workspace skills.

Validation

  • Adds coordinator, workspace, Dock, mirror-routing, and fake-socket CLI coverage.
  • Runs the CLI regression against the built binary in CI.
  • Includes inherited Swift warning/import fixes and current cloud and moved-pane test updates.
  • Replaces the mobile lane test's timing wait with a completion signal.
  • Hardens app-host test stability: bounds WebKit page-load waits, queues the first Codex prompt, waits for Codex thread/start before answering, gates file-preview saves on isSaving, tolerates closed-socket EINVAL, and samples the wedged app host in CI, matching it even when launched with arguments.

Written for commit 7708bd9. Summary will update on new commits.

Review in cubic

Summary by CodeRabbit

  • New Features

    • Added --command support for creating terminal splits, panes, surfaces, and workspaces.
    • Preserved complex command text and sends it to the new terminal with an Enter keystroke.
    • Added localized help text for the initial command option.
  • Bug Fixes

    • Ignored empty or whitespace-only commands.
    • Rejected commands for unsupported non-terminal types.
    • Applied commands during workspace creation without an extra follow-up request.
  • Tests

    • Added regression coverage for CLI and macOS terminal creation workflows.

Note

Medium Risk
Touches terminal spawn, workspace creation, and v2 control-socket creation paths used by CLI automation; behavior change for new-workspace --command (spawn vs post-create send) could affect scripts.

Overview
Adds --command to new-split, new-pane, new-surface, and layout-free new-workspace, sending literal text plus one Enter as initial_input at terminal spawn so the interactive shell stays alive. new-workspace no longer follows creation with surface.send_text; layout-based workspace creation still ignores --command. Socket RPCs pane.create, surface.split, and surface.create accept the same initial_input param, with invalid_params when it is paired with a non-terminal type; remote tmux mirror routing treats initial_input as unsupported like other local-only options.

CLI parsing moves into CMUXCLI+TerminalCreation.swift (dedicated --command parsing, blank-input omission, terminal-only validation). Help copy is localized (EN/JA).

CI adds a focused CLI regression (tests/test_cli_creation_initial_command.py), enables XCTest per-case timeouts on app-host batches, and samples wedged app hosts on batch deadline. Tests cover coordinator parsing, workspace/Dock creation, and harden flaky WebKit/Codex app-host cases with bounded waits and thread-start synchronization.

Reviewed by Cursor Bugbot for commit 7708bd9. Bugbot is set up for automated code reviews on this repo. Configure here.

@coderabbitai

coderabbitai Bot commented Aug 5, 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

The CLI accepts --command for terminal creation. Nonblank commands become initial_command and initial_input, with a trailing carriage return. Creation paths forward the input to terminal panels, while unsupported remote-mirror and non-terminal requests reject it. Tests and macOS CI validate the behavior.

Changes

Initial terminal input creation

Layer / File(s) Summary
CLI command contract
CLI/cmux.swift, Resources/Localizable.xcstrings
Creation commands parse and validate --command. Nonblank values become initial terminal input. Workspace creation no longer sends a separate surface.send_text request. Help and usage text describe the option.
RPC input contract
Packages/macOS/CmuxControlSocket/Sources/CmuxControlSocket/Coordinator/...
Pane, split, and surface creation inputs preserve nonblank raw initial_input values and omit blank values.
Terminal creation propagation
Sources/DockSplitStore.swift, Sources/TabManager.swift, Sources/TerminalController+*.swift, Sources/Workspace.swift
Workspace, Dock, split, and surface creation paths forward initialInput to terminal panels. Remote-mirror paths reject it as unsupported.
Creation regression validation
Packages/macOS/CmuxControlSocket/Tests/CmuxControlSocketTests/*, cmuxTests/*, tests/test_cli_creation_initial_command.py, .github/workflows/ci.yml
Tests verify input preservation, omission, command handling, non-terminal rejection, request counts, and macOS CLI execution.
Supporting test updates
cmuxTests/SessionPersistenceResumeBindingTests.swift, tests/test_omp_extension_install.py, web/tests/*
Existing persistence and extension expectations are updated. Hosted Subrouter tests use current process environment values per isolated test.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant CLI
  participant ControlCommandCoordinator
  participant TerminalController
  participant TerminalPanel
  CLI->>ControlCommandCoordinator: Send creation request with initial_command and initial_input
  ControlCommandCoordinator->>TerminalController: Pass validated creation inputs
  TerminalController->>TerminalPanel: Create terminal with initialInput
Loading

Possibly related PRs

Suggested reviewers: lawrencecchen, azooz2003-bit


Important

Pre-merge checks failed

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

❌ Failed checks (2 errors, 2 warnings)

Check name Status Explanation Resolution
Cmux User-Facing Error Privacy ❌ Error The new remote-tmux API error now exposes the internal socket field initial_input in its message and unsupported body. Map internal option names to safe product terms such as --command, or omit them from user-visible errors and retain raw names only in sanitized diagnostics.
Cmux Full Internationalization ❌ Error CLI adds localized help and error keys, but both catalog entries contain only en and ja while Resources/Localizable.xcstrings supports 20 locale codes. Add translated, non-placeholder values for the 18 missing catalog locales to both cli.terminalCreation keys: ar, bs, da, de, es, fr, it, km, ko, nb, pl, pt-BR, ru, th, tr, uk, zh-Hans, and zh-Hant.
Out of Scope Changes check ⚠️ Warning Several changes are unrelated to #2538, including web test mocks, session persistence expectations, and OMP hook persistence updates. Remove the unrelated web, session persistence, and OMP hook changes, or move them to separate pull requests.
Docstring Coverage ⚠️ Warning Docstring coverage is 24.56% 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 The implementation satisfies #2538 by adding --command support with spawn-time input, literal text preservation, and interactive-shell behavior.
Cmux Swift Actor Isolation ✅ Passed Production changes add only String? value plumbing; coordinator models are Sendable in a Swift 6 package without default MainActor isolation, and app stores/call paths are explicitly @MainActor.
Cmux Swift Blocking Runtime ✅ Passed The PR diff adds no Swift semaphores, waits, sleeps, delayed dispatch, polling, main-queue sync, or locks; production changes only propagate initialInput, and synchronization-related additions are...
Cmux Browser Automation Off-Main ✅ Passed The PR does not change browser.* automation or worker routing; policy files are unchanged, and new initialInput is passed only on terminal branches while browser branches remain separate.
Cmux Expensive Synchronous Load ✅ Passed The PR adds no expensive synchronous loads in production Swift; all 5 existing loader references are unchanged, and new code only forwards initialInput.
Cmux Cache Substitution Correctness ✅ Passed The production diff adds initial_input plumbing and CLI request handling; it replaces no fresh persistence/history/undo/snapshot read, and it contains no modified production TypeScript or JavaScript.
Cmux No Hacky Sleeps ✅ Passed The diff adds no prohibited delay to production non-Swift runtime code; non-Swift changes are CI YAML, localization, or test-only Python/TypeScript scaffolding.
Cmux Algorithmic Complexity ✅ Passed The production diff adds no nested scans, sorting, filtering, joins, or batch rescans; it adds one CLI-argument scan and linear string trimming, not scalable user-record processing.
Cmux Swift Concurrency ✅ Passed Swift additions only thread initialInput; no new Dispatch, Combine, completion-handler, or fire-and-forget Task patterns. The added @MainActor annotation is test-only.
Cmux Swift @Concurrent ✅ Passed The Swift diff adds no async/await or @concurrent functions; it adds only a synchronous pure nonisolated helper and an intentionally @MainActor test.
Cmux Swift Package Boundaries ✅ Passed The diff keeps parsing and public creation inputs in CmuxControlSocket, while app changes only thread initialInput through AppKit/Workspace/Ghostty lifecycle code; CLI logic is in the separate cmux...
Cmux Swiftpm Lockfiles ✅ Passed The PR adds only a CI regression step for dependency-related files; it changes no Package.swift, Package.resolved, .gitignore, or Xcode package references.
Cmux Swift Logging ✅ Passed The Swift diff adds no print, debugPrint, dump, NSLog, Logger, or ad hoc file/stream diagnostics; existing CLI output and unchanged logs are outside this check.
Cmux Swiftui State Layout ✅ Passed The Swift diff adds only initialInput plumbing and tests; it introduces no SwiftUI state, GeometryReader, lazy-row store references, or render-time state mutation. Existing legacy state is untouche...
Cmux Architecture Rethink ✅ Passed The Swift diff only threads immutable initialInput values through existing creation paths into the unchanged TerminalPanel/TerminalSurface owner; it adds no timing, blocking, observer, lock, pollin...
Cmux Swift Auxiliary Window Close Shortcuts ✅ Passed No changed Swift additions contain NSWindow, NSPanel, NSWindowController, Window, or WindowGroup code; the only NSWindow test fixture is allowed, and scripts/lint_auxiliary_window_close_shortcuts.p...
Cmux Source Artifacts ✅ Passed All 29 changed paths are hand-written source, tests, CI config, or localization; the only added file is a Python regression test, and no artifact-directory or binary paths appear in the diff.
Cmux No Test Or Debug Seam In Production Source ✅ Passed The production Swift diff adds only runtime initialInput plumbing and parsing; it adds no DEBUG/test guard, debug/test-named member, or test-only accessor, and production callers use the new values.
Cmux No Ambient Global State ✅ Passed Production Swift additions are private methods on CMUXCLI or an instance method on ControlCommandCoordinator; the diff adds no top-level mutable state, static namespace, or singleton.
Title check ✅ Passed The title clearly summarizes the primary change: adding --command support to terminal creation commands.
Description check ✅ Passed The description provides detailed change context and testing coverage, but omits the template's Demo Video, Review Trigger, and Checklist sections.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat-2538-command-flag-pane-surface

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: 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/macOS/CmuxControlSocket/Tests/CmuxControlSocketTests/ControlCommandCoordinatorSurfaceTests.swift`:
- Around line 16-50: Update capturedInitialCommand to return both a
dispatch/capture flag and the captured initialCommand value, distinguishing
omitted input from an unhandled request. In the tests covering omitted commands,
first assert that the appropriate fake input was received, then assert that its
initialCommand is nil; keep the existing method-specific coordinator dispatch
handling intact.
🪄 Autofix

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: d165398b-a3f2-41c5-823f-9cb93c2e6829

📥 Commits

Reviewing files that changed from the base of the PR and between 4cf7cc3 and ae12ff3.

📒 Files selected for processing (4)
  • .github/workflows/ci.yml
  • Packages/macOS/CmuxControlSocket/Tests/CmuxControlSocketTests/ControlCommandCoordinatorSurfaceTests.swift
  • Packages/macOS/CmuxControlSocket/Tests/CmuxControlSocketTests/FakeSurfaceControlCommandContext.swift
  • tests/test_cli_creation_initial_command.py

@cursor

cursor Bot commented Aug 5, 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 (1)
CLI/cmux.swift (1)

7857-7931: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Silently dropping --command when --layout is also passed.

applyInitialCommandOption(commandOpt, to: &params) runs only when layoutOpt == nil (line 7904). If a user passes both --layout and --command, the command text is silently discarded instead of producing an error or a warning. The command-line help documents that layout surfaces define their own commands, but a user who does not read the full help text gets no feedback that --command had no effect.

Add an explicit error when both flags are supplied together, so the CLI fails fast instead of silently ignoring user input.

Proposed fix
+        if layoutOpt != nil, let commandOpt, !commandOpt.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
+            throw CLIError(message: "\(commandName): --command is not supported with --layout; define commands per surface in the layout JSON instead")
+        }
         if layoutOpt == nil {
             applyInitialCommandOption(commandOpt, to: &params)
         }
🤖 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 `@CLI/cmux.swift` around lines 7857 - 7931, Add a validation in the
workspace-create option handling before the conditional
applyInitialCommandOption call: when both commandOpt and layoutOpt are supplied,
throw a localized CLIError stating that --command cannot be used with --layout.
Preserve applying --command for layout-free requests and continue processing
valid layout requests unchanged.
🤖 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 `@Resources/Localizable.xcstrings`:
- Around line 53602-53617: Add translated stringUnit entries for the new
cli.terminalCreation.help.initialCommandDescription key for every locale already
supported in Resources/Localizable.xcstrings, preserving the existing en and ja
translations and matching the catalog’s established locale structure.

---

Outside diff comments:
In `@CLI/cmux.swift`:
- Around line 7857-7931: Add a validation in the workspace-create option
handling before the conditional applyInitialCommandOption call: when both
commandOpt and layoutOpt are supplied, throw a localized CLIError stating that
--command cannot be used with --layout. Preserve applying --command for
layout-free requests and continue processing valid layout requests unchanged.
🪄 Autofix

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: eb267cdf-7087-4a27-8991-153b207d5824

📥 Commits

Reviewing files that changed from the base of the PR and between f2715d7 and d14aca9.

📒 Files selected for processing (3)
  • CLI/cmux.swift
  • Packages/macOS/CmuxControlSocket/Tests/CmuxControlSocketTests/ControlCommandCoordinatorSurfaceTests.swift
  • Resources/Localizable.xcstrings

Comment thread Resources/Localizable.xcstrings Outdated
@austinywang

Copy link
Copy Markdown
Contributor Author

CodeRabbit outside-diff note on new-workspace --layout --command: no change. Ignoring the top-level command for layout-backed workspaces is pre-existing, documented behavior, and this PR deliberately preserves existing new-workspace --command semantics while changing its non-layout delivery from surface.send_text to workspace.create.initial_command. Turning that combination into a new error would be an unrelated compatibility change.

@cursor

cursor Bot commented Aug 5, 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.

@cursor

cursor Bot commented Aug 5, 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.

@austinywang

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 7, 2026

Copy link
Copy Markdown

Rate Limit Exceeded

@austinywang have exceeded the limit for the number of chat messages per hour. Please wait 1 minutes and 59 seconds before sending another message.

@cursor

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

@austinywang

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 7, 2026 •

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

austinywang and others added 2 commits September 8, 2026 06:29
The auto-merged project file drifted from scripts/normalize-pbxproj.py
output, which fails the workflow-guard pbxproj check and gates every macOS
CI job behind it.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EXS7towgxy33eJ4ZTMeQHa
#11976 moved the pane-scoped notification clear for UserPromptSubmit and
PreToolUse out of the Claude hook and into the app's journal reconciler
(clearInvalidatedNotifications keys off the event's workspace and surface).
The two moved-pane tests still looked for the hook's old clear_notifications
send and turned the focused notification-routing step red on main.

They now assert the agent_journal_append event carries the re-homed
workspace and surface (and, for PreToolUse, the running phase), and keep
the guards against whole-workspace or stale-workspace clears.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EXS7towgxy33eJ4ZTMeQHa
austinywang and others added 7 commits September 8, 2026 08:36
On CI app hosts the WebContent process sometimes disappears mid-batch
("Could not signal service com.apple.WebKit.WebContent"), after which a
loadHTMLString navigation never reports didFinish. The screenshot evaluator
tests awaited that signal without a bound, so one lost process wedged the
whole app-host batch until the 30-minute timeout (shard 4 on runs
34232451577 and 34235694241 both stalled in
smoothScrollingPageCapturesRequestedRegionAndRestoresOffset).

Race every real page-load wait against a 30-second budget and fail the
test fast instead, so the rest of the batch still runs and reports.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EXS7towgxy33eJ4ZTMeQHa
testPTYBridgeDefersHalfCloseUntilAttachCompletes half-closes its bridge
socket, and the bridge answers and closes so quickly that the follow-up
SO_RCVTIMEO setsockopt sees a torn-down connection and fails with EINVAL.
That thrown error is counted as an unexpected failure and fails the whole
app-host batch (it reproduces on main's own shard 6 run 34221588627).

The timeout only bounds the read that follows, which returns EOF at once
on such a socket, so skip the option instead of throwing.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EXS7towgxy33eJ4ZTMeQHa
…atch

App-host unit-test batches on main and on PRs intermittently sit at the
30-minute batch timeout with only "started" lines for whichever tests were
in flight, which says nothing about what blocked the main actor. Sample
the app host process before terminating xcodebuild and print the leading
call graph in a log group, so the next hang names its stuck frames.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EXS7towgxy33eJ4ZTMeQHa
…ected

testCodexInputQueueBeforeThreadIsBounded spawned the first submit in a Task
and immediately submitted a second prompt on the same main actor. The
second call ran first, took the single pre-thread queue slot, and then
awaited a thread the test only starts afterwards, so the test hung until
the 30-minute app-host batch timeout (shard 2 on runs 34235694241 and
34245949340, and main's own shard 2).

Yield to the spawned task before the second submit so the first prompt
holds the slot and the second is rejected as intended.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EXS7towgxy33eJ4ZTMeQHa
…FIFO

testSaveTextContentIgnoresConcurrentSaveRequest replaced the previewed file
with a FIFO so the first write would stay in flight. Since the preview
panel re-opens its watched path for change monitoring, a FIFO with no
writer can block the app host's main thread, and this test was the
unfinished XCTest in two hung app-host shards (shard 1 on run 34232451577,
shard 5 on run 34245949340). saveTextContent() sets isSaving synchronously
and completes on a later main-actor hop, so the second request is always
observed while the first is still saving without any FIFO.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EXS7towgxy33eJ4ZTMeQHa
austinywang and others added 2 commits September 8, 2026 12:04
…r-test CI allowance

Every CodexAppServerSessionTests case fed the thread/start response after a
single Task.yield() following the initialize response. The session sends
`initialized` and then `thread/start` from a spawned main-actor task, so
when that task had not reached the request yet the response was dropped as
unknown, every later submit waited for a thread forever, and the reentrant
turn test spun on Task.yield() until the batch timeout (shard 2 on runs
34245949340 and 34256455336; main shows the same).

- CodexAppServerSession gains two read-only test seams
  (isAwaitingThreadStart, hasThread).
- The tests wait for the request before feeding its response, and the
  pending-write spin loop is bounded.
- CI passes XCTest's per-test execution allowance (300s by default) to the
  app-host batches so a single parked test fails with a spindump instead of
  taking the batch to its 30-minute timeout, and the hang sample prints
  more threads.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EXS7towgxy33eJ4ZTMeQHa
# Conflicts:
#	Sources/SessionIndexTableController.swift
#	cmuxTests/ClaudeHookLifecycleCleanupTests.swift
@austinywang
austinywang merged commit 7a0ba63 into main Sep 9, 2026
46 of 47 checks passed
rustybret pushed a commit to rustybret/bmux that referenced this pull request Sep 9, 2026
7a0ba63 feat: add --command to terminal creation commands (manaflow-ai#9614)

# Conflicts:
#	.github/workflows/ci.yml
aerickson pushed a commit to aerickson/cmux that referenced this pull request Sep 13, 2026
* test: cover creation initial command plumbing

* test: isolate creation command regression

* feat: add initial commands to terminal creation

* test: require creation request dispatch

* test(web): preserve hosted config overrides

* test(cli): expect OMP restore path

* test(session): expect codex wrapper shim

* docs(cli): sync restore help contract

* test(cli): preserve interactive shell for creation command

* fix(cli): inject creation commands into interactive shells

* fix(cli): preserve terminal creation command text

* test(cli): reject invalid creation command input

* fix(cli): validate terminal creation input boundaries

* fix(cli): resolve creation validation in app context

* test(cli): cover review-found creation boundaries

* test(cli): import welcome setting contract

* test(cli): wait for terminal readiness before welcome assertion

* test(cli): exercise focused workspace welcome path

* fix(cli): close terminal creation review gaps

* test: cover app-host temp path aliases

* ci: accept validated macOS temp aliases

* Fix Xcode 26 warning regressions

* test: keep install command fixture inert

* test: repair app-host CLI fixtures

* fix: remove duplicate dock initial input plumbing

* test: bound workspace readiness regression wait

* fix: order dock creation arguments

* fix: order dock input after startup options

* fix: preserve null terminal creation types

* fix: keep readiness wait actor-safe

* test: isolate creation command socket environment

* test: keep cloud splits local for initial input

* fix: keep initial input out of cloud split routing

* ci: route browser skill contract through linux runner

* test: isolate cloud routing fixture from local surfaces

* refactor: keep terminal creation checks within file budgets

* fix: wire terminal creation helpers into app target

* fix: repair current main compile errors

* docs: document --command on terminal creation commands

Cover the new --command flag on new-split, new-pane, and new-surface (and
the existing one on new-workspace) in the cmux and cmux-workspace skills,
the CLI contract (table rows, an Initial terminal command section, and
--help probes), and the web API docs in English and Japanese.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EXS7towgxy33eJ4ZTMeQHa

* fix: silence unmutated payload warning in browser key replay

CI's Swift warning budget fails on main because the delivered-key payload
in TerminalController.swift is declared var but never mutated. Use let so
the tests-build-and-lag job can pass on this branch.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EXS7towgxy33eJ4ZTMeQHa

* fix(web): keep devbox reachability script typechecking without bun-types

`import.meta.main` (added on main in manaflow-ai#12132) fails `tsgo --noEmit` because
the web tsconfig does not include bun-types, which turns the CI cheap layer
red and skips every macOS job. Cast the meta object so Bun's runtime flag
still gates main() while the typecheck passes.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EXS7towgxy33eJ4ZTMeQHa

* fix: repair package test import and Swift warnings inherited from main

- FakeTerminalEngine.swift (from manaflow-ai#10564) uses UUID without importing
  Foundation, which breaks `swift test` for CmuxTerminal in the
  swift-package-tests job.
- Parenthesize the two compactMap trailing closures in the pane memory
  guardrail guard and hop onto the main actor before reconcilePresentation
  in the session index table observer; both were new warnings over the
  cmux-owned Swift warning budget in tests-build-and-lag.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EXS7towgxy33eJ4ZTMeQHa

* fix: clear two more Swift warnings inherited from main

Drop the unreachable default branch in the cmux-tui snapshot parser's
exhaustive resource-kind switch and stop binding the unused rowID shadow in
SurfaceCatalogModel, so the cmux-owned warning budget passes.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EXS7towgxy33eJ4ZTMeQHa

* test: bring MachinesPanelModelTests in line with the current cloud tree and catalog

Four tests in the app-host shard were stale against main:
- a sleeping or broken machine now keeps a Ports group whose status row
  explains how to discover ports (manaflow-ai#12051), and an unregistered machine shows
  a Connecting placeholder instead of no children;
- display rows placed inside a remote workspace carry their tab id like
  every other placement;
- the catalog drops writes for a cloud machine with no registered provider,
  so the two workspace-group tests register the file's GroupFakeProvider
  before replacing resources.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EXS7towgxy33eJ4ZTMeQHa

* chore: normalize project.pbxproj after main merges

The auto-merged project file drifted from scripts/normalize-pbxproj.py
output, which fails the workflow-guard pbxproj check and gates every macOS
CI job behind it.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EXS7towgxy33eJ4ZTMeQHa

* test: assert pane re-homing through the journal event for started turns

manaflow-ai#11976 moved the pane-scoped notification clear for UserPromptSubmit and
PreToolUse out of the Claude hook and into the app's journal reconciler
(clearInvalidatedNotifications keys off the event's workspace and surface).
The two moved-pane tests still looked for the hook's old clear_notifications
send and turned the focused notification-routing step red on main.

They now assert the agent_journal_append event carries the re-homed
workspace and surface (and, for PreToolUse, the running phase), and keep
the guards against whole-workspace or stale-workspace clears.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EXS7towgxy33eJ4ZTMeQHa

* test: bound WebKit page-load waits in the design-mode screenshot suite

On CI app hosts the WebContent process sometimes disappears mid-batch
("Could not signal service com.apple.WebKit.WebContent"), after which a
loadHTMLString navigation never reports didFinish. The screenshot evaluator
tests awaited that signal without a bound, so one lost process wedged the
whole app-host batch until the 30-minute timeout (shard 4 on runs
34232451577 and 34235694241 both stalled in
smoothScrollingPageCapturesRequestedRegionAndRestoresOffset).

Race every real page-load wait against a 30-second budget and fail the
test fast instead, so the rest of the batch still runs and reports.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EXS7towgxy33eJ4ZTMeQHa

* test: tolerate an already-closed socket when bounding PTY bridge reads

testPTYBridgeDefersHalfCloseUntilAttachCompletes half-closes its bridge
socket, and the bridge answers and closes so quickly that the follow-up
SO_RCVTIMEO setsockopt sees a torn-down connection and fails with EINVAL.
That thrown error is counted as an unexpected failure and fails the whole
app-host batch (it reproduces on main's own shard 6 run 34221588627).

The timeout only bounds the read that follows, which returns EOF at once
on such a socket, so skip the option instead of throwing.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EXS7towgxy33eJ4ZTMeQHa

* ci: sample the wedged app host before killing a timed-out unit-test batch

App-host unit-test batches on main and on PRs intermittently sit at the
30-minute batch timeout with only "started" lines for whichever tests were
in flight, which says nothing about what blocked the main actor. Sample
the app host process before terminating xcodebuild and print the leading
call graph in a log group, so the next hang names its stuck frames.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EXS7towgxy33eJ4ZTMeQHa

* test: queue the first Codex prompt before asserting the second is rejected

testCodexInputQueueBeforeThreadIsBounded spawned the first submit in a Task
and immediately submitted a second prompt on the same main actor. The
second call ran first, took the single pre-thread queue slot, and then
awaited a thread the test only starts afterwards, so the test hung until
the 30-minute app-host batch timeout (shard 2 on runs 34235694241 and
34245949340, and main's own shard 2).

Yield to the spawned task before the second submit so the first prompt
holds the slot and the second is rejected as intended.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EXS7towgxy33eJ4ZTMeQHa

* test: gate the concurrent file-preview save on isSaving instead of a FIFO

testSaveTextContentIgnoresConcurrentSaveRequest replaced the previewed file
with a FIFO so the first write would stay in flight. Since the preview
panel re-opens its watched path for change monitoring, a FIFO with no
writer can block the app host's main thread, and this test was the
unfinished XCTest in two hung app-host shards (shard 1 on run 34232451577,
shard 5 on run 34245949340). saveTextContent() sets isSaving synchronously
and completes on a later main-actor hop, so the second request is always
observed while the first is still saving without any FIFO.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EXS7towgxy33eJ4ZTMeQHa

* ci: match the app host for hang sampling even when launched with arguments

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EXS7towgxy33eJ4ZTMeQHa

* test: wait for the Codex thread/start request before answering it; per-test CI allowance

Every CodexAppServerSessionTests case fed the thread/start response after a
single Task.yield() following the initialize response. The session sends
`initialized` and then `thread/start` from a spawned main-actor task, so
when that task had not reached the request yet the response was dropped as
unknown, every later submit waited for a thread forever, and the reentrant
turn test spun on Task.yield() until the batch timeout (shard 2 on runs
34245949340 and 34256455336; main shows the same).

- CodexAppServerSession gains two read-only test seams
  (isAwaitingThreadStart, hasThread).
- The tests wait for the request before feeding its response, and the
  pending-write spin loop is bounded.
- CI passes XCTest's per-test execution allowance (300s by default) to the
  app-host batches so a single parked test fails with a spindump instead of
  taking the batch to its 30-minute timeout, and the hang sample prints
  more threads.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EXS7towgxy33eJ4ZTMeQHa

* test: replace mobile lane timing wait with completion signal

---------

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>

This branch was successfully deployed

2 active and 1 inactive (outdated) deployments
Preview – cmux41 — 7708bd9c Deployed Sep 9, 2026 by vercel[bot]
Preview – cmux166 — 7708bd9c Deployed Sep 9, 2026 by vercel[bot]
cloud-vm-image-checks — 1bba3fc4 Deployed Sep 8, 2026 by austinywang via reachable #14
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.

feat: --command flag for new-split / new-surface / new-pane

1 participant