Repository navigation
Swift calls cmux-focus-core's focus rules; the Swift copies go (A12 step B, carried from #31835; R-INPUT S2) - #34685
Conversation
…t rules (failing) cmux-focus-core gets the home of the focus rules the macOS app keeps in Swift beside it (rust-migration backlog A12), with their Swift tests ported as the Rust corpus (tests/focus_rules, filter `ported::`): - close successors of every kind (FocusAfterClose): the opener tests (FocusAfterCloseOpenerTests), the exhaustive model check of the deleted FocusAfterCloseModelCheckTests (panes both policies with its 7 mutants, tabs with its 2 mutants, workspaces), and the focus cases of closefocus.tla (the strip, strip-recent and list configurations with SuccessorRule, C1-C3, and its "history" mutant); - the Close Tab aim (PaneCloseAimTests); - the per-tab address bar memory (OmnibarFocusMemoryTests); - the input invariants (ChromiumPageFocusInvariantTests, plus one case per F, T and W rule and the app's InputObservation JSON). The pane rule is generic over the id type now (the model check runs it on integers); its behavior does not change and its cases pass. The tab, workspace and opener rules, the aim, the memory, the invariants, the Ghostty check, the typing tab and the closeFocus config parse are stubs here: their tests fail until the rules move in the next commit.
…cklog A12) The rules of the macOS app's Swift focus copies, moved as they are, with one rule set beside the existing pane rule: - close_focus: the tab successor (opener family first in Chrome's order, then next-else-previous shown tab), live opener relations (Chrome's FixOpeners) and the workspace successor (drawn row orders first), from CmuxNextDesignTokens FocusAfterClose; generic over the id type. - close_aim: PaneCloseAim, which pane a Close Tab press is aimed at after a close emptied a pane (presented frame and key repeat decide). - omnibar_memory: OmnibarFocusMemory, Chrome's per-tab address bar focus. One refinement: past the 64-tab bound the oldest tab goes; the Swift Set dropped an arbitrary one (hash order). - invariants: InputInvariants' model (F), transition (T) and world (W) checks over the app's FocusState read view and InputObservation JSON shapes, the expected responder table, and debug.focus's Ghostty check. Violation details are diagnostics; the invariant ids and windows are the Swift ones. - state: Resolved pane/tab/kind/isTextInput, FocusState typing_tab and terminal_tab (FocusState+TypingTab), CloseFocusPolicy::from_config_value. - wire: JSON requests for focus_input_world, focus_input_step and focus_debug_window. No behavior change; the Swift copies go in PR 2, after the app FFI re-pin.
…lose and per press) - cmux_close_focus_tab, cmux_close_focus_workspace and cmux_close_focus_live_openers: the close successors over the caller's u32 ids, one typed call per close, no handle and no JSON. - The window focus handle keeps the Close Tab aim (cmux_focus_pane_moved, cmux_focus_frame_presented, cmux_focus_pane_emptied, cmux_focus_aims_at_emptied_pane) and the address bar memory, which it notes on every reduction: the reply's former reserved bytes are omnibar_return and returns_to_omnibar (the layout is unchanged), and a typing_tab string follows the reply's last field. - cmux_layout_reducer_json answers focus_input_world, focus_input_step and focus_debug_window for the debug monitor, debug.focus and the journal replay. cmux-app-ffi links this crate already, so the next CCmuxAppFFI release carries the calls; the Swift copies call them in PR 2. The C ABI test (tests/close_focus_rules.rs) drives each call.
… check debug.focus's Ghostty check (FocusState.Resolved.ghosttyMismatch) moved into cmux-focus-core in the previous commits; its Swift test, with the same texts for each case, is now part of the ported corpus.
The transition arms fold their condition into the match guard (each falls to the no-op arm, as before), and the Ghostty check picks the key window's terminal first, then branches: the same rules, clippy -D warnings clean.
… copies go (backlog A12) The Swift half of the move. It needs CCmuxAppFFI re-pinned to a release built with this branch's Rust (the pre-merge FFI release, STATUS 5013): the calls below are not in the pinned release, so it does not compile against the pin yet. - FocusAfterClose (tab, liveOpeners, workspace) numbers the caller's ids and calls cmux_close_focus_*; the Swift rules, neighbor, resolve and openerFamily are gone. Callers are unchanged. - PaneCloseAim forwards to the window focus machine (cmux_focus_pane_moved and siblings); its state and rule are the machine's. PaneCloseAim.Press keeps its shape (TabCloseGuard builds it). - OmnibarFocusMemory is deleted: the machine notes every reduction, and FocusCoordinator reads the reply's omnibarReturn and returnsToOmnibar. - InputInvariants' model, transition and world checks and the expected responder table are deleted; reduction, world, expectedResponder and debugWindow call focus_input_step, focus_input_world and focus_debug_window. A refused call fails loud in debug builds (assertionFailure); world then reports every window unsettled. - FocusState.typingTab is the reply's typing_tab; the Ghostty check calls the machine's rule. Swift tests: PaneCloseAimTests and OmnibarFocusMemoryTests are deleted (their rules moved; the cases are cmux-focus-core's ported:: corpus). FocusAfterCloseOpenerTests, GhosttyFocusAgreementTests, ChromiumPageFocusInvariantTests, CloseTabKeepsPaneTests, PaneCloseControllerTests and BrowserTabOpenersCloseFocusTests stay and now run the Rust rules.
…rted:: filter One test-name filter (ported::) now selects the crate's ported rule tests and their C ABI tests, so one fleet step proves both.
contains_key instead of get().is_some(), and the mutant builder's closure type (the pane rule's own signature) is allowed as complex.
… tests used The app's settings layer parses layout.closeFocus (CloseFocusSetting, CloseFocusPolicy.init(configValue:)), like its other settings; the Rust copy had no caller. The settings parse moves with the settings (backlog D).
swift-logic-classes.tsv: the rows of the deleted rules (InputInvariants+World, OmnibarFocusMemory) go; FocusAfterClose, PaneCloseAim, InputInvariants and the Ghostty check are thin FFI glue now (PLATFORM), and the new IDNumbering glue has its row. FocusEvent.Responder.pane had no caller left and is deleted.
…s over #31835) The branch was 1721 feat commits behind (its last base merge: b6b550e). Conflicts, resolved this way: - The six Rust paths (cmux-focus-core lib.rs, wire.rs, tests/focus_rules/ported/input_invariants.rs; cmux-layout-reducer-ffi lib.rs, include/cmux_layout_reducer_ffi.h, tests/close_focus_rules.rs): feat's side. Step A (#31921, 80d716d) already landed every Rust change of this branch, and feat has moved on since then (agent cursor visibility, the scroll and hover reducers, the R-INPUT router), so cmux-tui now equals feat's tree. - FocusState+TypingTab.swift: the branch drops the computed `typingTab` (now `FocusState.typingTab`, the Rust machine's rule from its reply). Feat's `awaitableContent` (REOPEN-F4) and `awaitsOtherTab` (P0 INVERSE-KEY) stay. - InputInvariants+World.swift: deleted, as the branch intends; the world checks run in cmux-focus-core. INVERSE-KEY's rule in that file ("a window whose focus is on its way to another tab is unsettled", 5fe33a3) is not in the Rust world check yet: it moves into cmux-focus-core in #34641 (A12 step A of the FFI two-step rule), which lands and is re-pinned before this Swift half lands. Co-authored-by: Lawrence Chen <54008264+lawrencecchen@users.noreply.github.com>
…changed, effects, omnibar flags) A semantic conflict of the feat merge: the A12 move widened FocusMachine.send's result to Reduction (changed, effects, omnibarReturn, returnsToOmnibar), and P0 INVERSE-KEY (5fe33a3) added reapplyingOnWaitChange, which took and returned the old (changed, effects) pair. It now takes and returns a Reduction: on a wait change with no responder move it marks the reduction changed and appends the moveResponder, as before, and the omnibar flags pass through unchanged. Co-authored-by: Lawrence Chen <54008264+lawrencecchen@users.noreply.github.com>
|
cmux status (one comment, edited in place; each line is updated by the job that owns it) Review App of this head (app.zip, kept 1 day)
red: cmux-next checks (god files, concurrency, crash safety, l10n); NEW: Shipped in:
|
|
Found 6 test failures on Blacksmith runners: Failures
|
|
Review (independent subagent, correctness first): SHIP at 3d46ef9 Scope: the PR's own diff against its merge base 4a17e97 (18 files, +293/-709, no Rust file), the merge 3d46ef9, ff7d600 against 5fe33a3 and fc12029, the fleet evidence and the checks on this head. Read only. I did not build or run anything. Behavior equality (Swift copy against cmux-focus-core)I compared each deleted or thinned Swift rule with its Rust rule, branch by branch. I found no rule that the Swift copy had and Rust does not have.
ff7d600 against 5fe33a3 and fc12029
The merge 3d46ef9It is clean. Only plans/cmux-next/swift-logic-classes.tsv changed on both sides, and the hunks do not overlap. An automatic re-merge gives the same blob. There is no evil merge: the patch-ids of both sides match. The other 17 files equal ff7d600. I also merged this head into the current feat tip aeca627 with Evidence
Reds on this head
CreditThe 14 commits from #31835 keep lawrencecchen as author (151151d is a cmux-bot merge). The new authored commits 0d4e198 and ff7d600 carry Nits (none blocks)
|
Bugbot needs on-demand usage enabledBugbot uses usage-based billing for this team and requires on-demand usage to be enabled. A team admin can enable on-demand usage in the Cursor dashboard. |
|
REWRITE R-INPUT: the red parts of
Independent review: SHIP at 3d46ef9 (#34685 (comment)). The 11 focus suites (step 2fd742f616994eca487f554b) and the 4 typingTab suites (step 6e31c3cebe1932c775e178aa) passed on the fleet; the compile check (job ae88d00b33d75f1fc7ce369d) passed. |
![Fix with [code]smith](https://pr-comments-assets.blacksmith.sh/codesmith/fix-with-codesmith-light-3x.png)
#34641 and its re-pin (#34995) are on feat, so this can land. This is step B of the FFI two-step rule (STATUS 5388). It carries #31835 by @lawrencecchen, which REWRITE R-INPUT took over (lead decision, 2026-11-04; notice: #31835 (comment)). #31835 stays open for a person to close. The work is @lawrencecchen's: its commits are kept as they are, and each new commit credits him. It is the first PR of R-INPUT stage S2: one handle for focus and input.
Summary
The macOS app's focus rules leave Swift and are called from
cmux-focus-coreover the app FFI (rust-migration backlog A12;layer-ownership.md: Rust owns rules and input routing decisions). Step A (#31921) already landed the Rust half, and the app's pin (cmux-app-ffi-d3d3f420820f) already has every C function and reply field that this Swift half calls (checked against the pinned header: 18 functions, 30CmuxFocusReplyfields).CmuxNextDesignTokens/CloseFocus/FocusAfterClose.swifttab,openerFamily,liveOpeners,workspaceclose_focus::{tab, opener_family, live_openers, workspace}CmuxNextFocus/PaneCloseAim.swiftclose_aim::PaneCloseAimCmuxNextFocus/OmnibarFocusMemory.swiftomnibar_memory::OmnibarFocusMemoryCmuxNextFocus/InputInvariants.swift,InputInvariants+World.swiftinvariants::{model, transition, step, world, expected_responder}FocusState+TypingTab.swift(typingTab),FocusState+GhosttyAgreement.swiftFocusState::typing_tab(from the reply),Resolved::ghostty_mismatchBehavior: no change, with the one difference #31835 named. Past its 64-tab bound, the address bar memory drops the oldest tab; the Swift
Set.removeFirst()dropped one in hash order.The feat merge (1721 commits) and why this waits for #34641
scripts/merge-main.sh --base feat-cmux-next(git 2.47) merged feat 44365e0. 0d4e198 records how each conflict was resolved:cmux-tuiequals feat's tree, so this PR changes no FFI source.FocusState+TypingTab.swift: the computedtypingTabgoes (it is now the machine's rule, from its reply). Feat'sawaitableContent(REOPEN-F4) andawaitsOtherTab(P0 INVERSE-KEY) stay.InputInvariants+World.swiftis deleted, as Swift calls cmux-focus-core's focus rules; the Swift copies go (A12 step B, after #31921 and the FFI re-pin) #31835 intends, so the world check iscmux-focus-core's. P0 INVERSE-KEY (5fe33a3) put one rule in that Swift file only: a window whose focus is on its way to another tab is unsettled. focus-core: a window whose focus waits for another tab is unsettled in the Rust world check (P0 INVERSE-KEY rule; A12 step A, R-INPUT S2) #34641 adds that rule to the Rust world check, red first. This Swift half must not land before focus-core: a window whose focus waits for another tab is unsettled in the Rust world check (P0 INVERSE-KEY rule; A12 step A, R-INPUT S2) #34641 and its re-pin, or the live input monitor would file a W1 desync on every Ctrl-T or Ctrl-` from a Chromium page.FocusMachine.reapplyingOnWaitChangetook and returned the old(changed, effects)pair, while A12 widenedsend's result toReduction(with the omnibar flags). It now takes and returns aReduction, with the same behavior.Takes back #31265: the input invariants it moved into
CmuxNextFocus(InputInvariants+World.swift,InputInvariants.swift) now run incmux-focus-core. The Swift code becomes calls, which is the A12 move this PR carries.Takes back #32769: its rule in
InputInvariants+World.swift(a window waiting for another tab is unsettled) moves to the Rust world check in #34641, which lands first, and itsFocusMachineandFocusStatechanges stay (ff7d600).#31835's 12 red checks, compared with the base
All ran on its old head 151151d (2026-11-02):
cmux-next/compile,cmux-next fleet Release compile,cmux-next swift test,cmux-next fast tier,cmux-next generated files (shared build),cmux-next daemon tests (same-tree): its own. The Swift half calledcmux_focus_pane_movedandcmux_focus_frame_presented, which its pin at that time (8b62425, from before step A landed) did not have. The pin on feat has them now, so the merge removes this cause. The new head's compile is on this PR's CI and the fleet.cmux-next checks, part "Check the app FFI pin": its own (the branch carried the Rust half). Gone:cmux-tuiequals feat.risk evidence: its own: it took back base red: arch app_lines, move the input invariants out of CmuxNextApp into CmuxNextFocus (margin) #31265's change with no reason. TheTakes backlines above clear it.tooling-tests,nightly-guards / Guard status,nightly-guards / workflow-guard-tests / release-notary: base findings (test_download_prints_the_active_profile_only: "no ACTIVE profile named 'cmux NEXT Developer ID'").next-autoland / verdict: a wrapper over the others.Line accounting
python3 -I scripts/cmux-next/swift-logic-lines.py --repo . --rev REV --json:Tests
The Swift tests of these types became the Rust corpus in step A (
cmux-tui/crates/cmux-focus-core/tests/focus_rules, filterported::).OmnibarFocusMemoryTestsandPaneCloseAimTestsare deleted here, because their cases are in that corpus.Fleet compile check at ff7d600 (
cmux-ci compile-check --app --package CmuxNext, job 616c5c6bc8b330bcf99270d9): passed. CmuxNext, the app and cmux-tui areok: true, with 0 errors. It compiles against the current pin, because every C call is already pinned; only the INVERSE-KEY world rule's behavior waits for #34641.After #34641's re-pin (#34995,
cmux-app-ffi-2e0b6acc3593) merged, this branch merged feat with it (3d46ef9,scripts/merge-main.sh, no conflict):cmux-ci compile-check --app --package CmuxNext, job ae88d00b33d75f1fc7ce369d): CmuxNext passed (375 s), the app passed (91 s).FocusAfterCloseOpenerTests10,BrowserTabOpenersCloseFocusTests5,GhosttyFocusAgreementTests2,ChromiumPageFocusInvariantTests2,FocusWaitLeavesPageTests2,CloseTabKeepsPaneTests5,CmdWEmptiedSplitPaneLeavesInItsTurnTests4,ClosedPendingSplitFocusTests2,RapidCloseTabTests2,OmnibarFocusParityTests9,OmnibarFocusClickSelectionTests4. The step ran without a display.typingTab(the reviewer asked for them), step 6e31c3cebe1932c775e178aa on aws-m4pro-8: all passed, 23 tests:CreationInputHoldTests11,HomePendingTerminalFocusTests4,CmdNThenCreateKeysTests2,CloseThenTypeKeysTests6.FocusMachine.swiftis 477 lines (cap 400, warn ceiling 480), so the four close-aim calls move toFocusMachine+CloseAim.swiftin a follow-up; a wire-shape mismatch would make the debug world check report every window unsettled without a reason (the pin check prevents a mismatch).Observed / Not run
Changelog
none
No capture: no visible change; the focus rules move from Swift to Rust with the same behavior.
Co-authored-by: Lawrence Chen 54008264+lawrencecchen@users.noreply.github.com