Skip to content

Handle OMX extended-keys tmux probes in cmux compat layer#2675

Open
kenryu42 wants to merge 6 commits intomanaflow-ai:mainfrom
kenryu42:fix/omx-extended-keys-compat
Open

Handle OMX extended-keys tmux probes in cmux compat layer#2675
kenryu42 wants to merge 6 commits intomanaflow-ai:mainfrom
kenryu42:fix/omx-extended-keys-compat

Conversation

@kenryu42
Copy link
Copy Markdown

@kenryu42 kenryu42 commented Apr 7, 2026

Summary

  • add regression coverage for OMX's show-options / set-option extended-keys startup flow
  • teach the tmux compatibility layer to serve show-options and persist set-option state
  • default extended-keys to off so OMX can acquire and release its tmux lease without unsupported-command errors

Fixes #2671

Why this PR

PR #2672 fixes the immediate reachability issue with a minimal show-options stub. This PR goes a step further and models the show-options + set-option round-trip that Oh My Codex now performs during startup.

Testing

  • python3 -m py_compile tests_v2/test_tmux_compat_matrix.py
  • swiftc -parse CLI/cmux.swift
  • ./scripts/reload.sh --tag fix-omx-extended-keys (blocked by unrelated Ghostty build panic: tagged releases must be in vX.Y.Z format matching build.zig)

Notes

  • follows the repo regression policy with a failing-test commit first, then the implementation fix
  • leaves unrelated local .gitignore changes out of this PR

Summary by CodeRabbit

  • New Features

    • Added tmux-compatible commands to view and modify options with persistent storage, case-insensitive names, default merging (e.g., extended-keys defaults to off), and updated help text. show-options prints specific or all options and respects quiet/verbose flags; set-option supports unset, conditional no-op, append, and exact-value handling.
  • Tests

    • Added tests for defaults, persistence, set/unset/append semantics, exact empty/whitespace values, and help/output formatting.

kenryu42 added 2 commits April 7, 2026 17:48
OMX now probes tmux extended-keys state during startup. Add a regression
check that expects show-options/set-option round-tripping so CI can prove
current tmux-compat behavior is insufficient before the implementation fix
lands.

Constraint: Regression-test policy requires a failing test commit before the fix commit
Rejected: Ship the implementation without a proving test commit | would violate repo regression policy
Confidence: high
Scope-risk: narrow
Reversibility: clean
Directive: Keep tmux compatibility coverage focused on observable CLI behavior, not implementation details
Tested: python3 -m py_compile tests_v2/test_tmux_compat_matrix.py
Not-tested: End-to-end execution against a running cmux instance; local test runs are deferred to CI/VM by repo policy
Teach the tmux compatibility shim to answer the show-options and set-option
traffic OMX now uses for its extended-keys lease flow. Persist option state in
the existing compat store so startup probes and restoration commands see a
consistent value instead of falling through to unsupported-command errors.

Constraint: OMX launches through cmux __tmux-compat, not a real tmux server
Rejected: Minimal empty-string show-options stub only | fixes the immediate crash but does not model the show/set round-trip OMX performs
Confidence: high
Scope-risk: narrow
Reversibility: clean
Directive: If future tmux consumers rely on more options, extend the compat store instead of adding one-off no-op cases
Tested: swiftc -parse CLI/cmux.swift
Tested: ./scripts/reload.sh --tag fix-omx-extended-keys (blocked by unrelated Ghostty build panic)
Not-tested: Full app build and runtime validation; Ghostty submodule setup currently panics on tagged-release version parsing
@vercel
Copy link
Copy Markdown

vercel bot commented Apr 7, 2026

@kenryu42 is attempting to deploy a commit to the Manaflow Team on Vercel.

A member of the Team first needs to authorize it.

@cubic-dev-ai
Copy link
Copy Markdown

cubic-dev-ai bot commented Apr 7, 2026

This review could not be run because your cubic account has exceeded the monthly review limit. If you need help restoring access, please contact contact@cubic.dev.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 7, 2026

📝 Walkthrough

Walkthrough

Adds tmux-compat support for show-option(s) and set-option/set/set-window-option/setw, implements persistent option storage in TmuxCompatStore with backward-compatible JSON decoding and case-insensitive resolution (defaulting extended-keys=off), and updates tests to validate show/set behavior and persistence.

Changes

Cohort / File(s) Summary
Tmux compatibility command support
CLI/cmux.swift
Dispatches show-option(s) and set-option/set/setw to tmux-compat handler; parses -q, -v, -u, -o, -a; implements printing/validation and error handling for unsupported/unset options.
Persistent options storage & resolution
CLI/cmux.swift (TmuxCompatStore additions)
Adds options: [String:String] to store, backward-compatible JSON decoding/init, case-insensitive name resolution, merges defaults (notably extended-keys=off) with stored values, and persists changes to disk.
Tests
tests_v2/test_tmux_compat_matrix.py
Adds tests exercising show-options and set-option flows: default value check, set/persist/reset, custom option lifecycle (set, -o, -a, show, unset), edge-case values, and help output assertions.

Sequence Diagram(s)

sequenceDiagram
    participant Client as Client (e.g., omx)
    participant CLI as CLI/cmux
    participant Store as TmuxCompatStore
    participant FS as FileSystem

    Client->>CLI: tmux show-options -sv extended-keys
    CLI->>CLI: parse flags (-s/-v/-q), normalize name
    CLI->>Store: load store & merge defaults (case-insensitive)
    Store->>FS: read JSON store file
    FS-->>Store: stored data
    Store-->>CLI: resolved option value or error
    CLI-->>Client: print value (honor -v/-q)

    Client->>CLI: tmux set-option -sq extended-keys always
    CLI->>CLI: validate args, apply flags (-u/-o/-a)
    CLI->>Store: update options map
    Store->>FS: write updated JSON store
    FS-->>Store: write ack
    Store-->>CLI: confirm update
    CLI-->>Client: optional output / exit
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Poem

🐰 I hopped through options, show and set with care,
Saved tiny flags in JSON hiding there.
extended-keys whispered off, then on anew,
A rabbit's tweak so omx can run true. 🥕

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 9.09% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: implementing OMX extended-keys tmux probes handling in the cmux compatibility layer, which is the primary focus of the PR.
Description check ✅ Passed The description covers all key sections: summary explains what changed and why, testing details provided (syntax checks and parse verification), and necessary context included despite build blockers.
Linked Issues check ✅ Passed Code changes fully address issue #2671 requirements: implements show-options command support, enables set-option persistence, and defaults extended-keys to off, allowing OMX startup without unsupported-command errors.
Out of Scope Changes check ✅ Passed All changes directly support the linked issue fix: tmux-compat command support, option storage implementation, and test coverage for the show-options/set-option round-trip are all in scope.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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 and usage tips.

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Apr 7, 2026

Greptile Summary

This PR implements show-options and set-option tmux compat commands to serve OMX's extended-keys lease-management probes, defaulting extended-keys to off. The core round-trip logic (default → probe → set → unset) is correct and well-tested. Two minor P2 gaps: a case normalization inconsistency in the named show-options output path, and a dead error branch that can never fire while defaults are registered.

Confidence Score: 5/5

Safe to merge; all remaining findings are P2 style/consistency issues that do not affect correctness for the OMX use case.

Both findings are P2. The prior P1 concern about show-options without a name argument (previous thread) is now addressed. No blocking issues remain.

CLI/cmux.swift — minor output case inconsistency and dead error-path

Important Files Changed

Filename Overview
CLI/cmux.swift Adds show-options/set-option handlers with option store, case normalization, and default extended-keys: off; minor case inconsistency in named show-options output and an unreachable empty-options error path
tests_v2/test_tmux_compat_matrix.py Adds OMX extended-keys startup flow regression tests covering default/persist/no-op/append/unset/list semantics

Sequence Diagram

sequenceDiagram
    participant OMX as Oh My Codex
    participant CLI as cmux CLI (compat layer)
    participant Store as TmuxCompatStore (JSON)

    OMX->>CLI: show-options -sv extended-keys
    CLI->>Store: loadTmuxCompatStore()
    Store-->>CLI: options: {}
    CLI->>CLI: merge defaults (extended-keys=off)
    CLI-->>OMX: off

    OMX->>CLI: set-option -sq extended-keys always
    CLI->>Store: store.options["extended-keys"] = "always"
    CLI->>Store: saveTmuxCompatStore()

    OMX->>CLI: show-options -sv extended-keys
    CLI->>Store: loadTmuxCompatStore()
    Store-->>CLI: options: {extended-keys: always}
    CLI-->>OMX: always

    OMX->>CLI: set-option -u extended-keys
    CLI->>Store: store.options.removeValue(extended-keys)
    CLI->>Store: saveTmuxCompatStore()
Loading

Greploops — Automatically fix all review issues by running /greploops in Claude Code. It iterates: fix, push, re-review, repeat until 5/5 confidence.
Use the Greptile plugin for Claude Code to query reviews, search comments, and manage custom context directly from your terminal.

Reviews (2): Last reviewed commit: "Close the remaining tmux option parity g..." | Re-trigger Greptile

_run_cli(cli, ["set-option", "-sq", "extended-keys", "always"])
show_extended_keys = _run_cli(cli, ["show-options", "-sv", "extended-keys"])
_must(show_extended_keys.stdout.strip() == "always", f"set-option should persist extended-keys, got {show_extended_keys.stdout!r}")
_run_cli(cli, ["set-option", "-sq", "extended-keys", "off"])
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Use -u to unset instead of re-setting to the default value

The teardown sets extended-keys back to "off" via set-option, which persists the entry in the store. Using set-option -u extended-keys would cleanly remove the key, restoring the store to its pre-test state and matching OMX's actual lease-release behavior (which calls -u to relinquish ownership).

Suggested change
_run_cli(cli, ["set-option", "-sq", "extended-keys", "off"])
_run_cli(cli, ["set-option", "-u", "extended-keys"])

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

current OMX restores extended-keys with set-option -sq ..., not -u

CLI/cmux.swift Outdated
Comment on lines +12238 to +12241
guard let optionName = parsed.positional.first?
.trimmingCharacters(in: .whitespacesAndNewlines),
!optionName.isEmpty else {
throw CLIError(message: "\(command) requires an option name")
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 show-options without a name argument errors instead of listing all options

In real tmux, show-options (or show-options -g) with no positional argument lists every option's current value. The guard here throws a CLIError when no name is supplied, so any caller that enumerates all options will get an error rather than the options map. Consider returning the full store.options merged with built-in defaults when no positional name is provided.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
tests_v2/test_tmux_compat_matrix.py (1)

160-166: Ensure extended-keys cleanup runs on failure paths.

On Line 162–165, if the "always" assertion fails, the reset to "off" is skipped. Wrap the mutate/verify block in try/finally to avoid leaking option state across later checks.

Proposed refactor
         show_extended_keys = _run_cli(cli, ["show-options", "-sv", "extended-keys"])
         _must(show_extended_keys.stdout.strip() == "off", f"show-options should default extended-keys to off, got {show_extended_keys.stdout!r}")
-        _run_cli(cli, ["set-option", "-sq", "extended-keys", "always"])
-        show_extended_keys = _run_cli(cli, ["show-options", "-sv", "extended-keys"])
-        _must(show_extended_keys.stdout.strip() == "always", f"set-option should persist extended-keys, got {show_extended_keys.stdout!r}")
-        _run_cli(cli, ["set-option", "-sq", "extended-keys", "off"])
+        try:
+            _run_cli(cli, ["set-option", "-sq", "extended-keys", "always"])
+            show_extended_keys = _run_cli(cli, ["show-options", "-sv", "extended-keys"])
+            _must(show_extended_keys.stdout.strip() == "always", f"set-option should persist extended-keys, got {show_extended_keys.stdout!r}")
+        finally:
+            _run_cli(cli, ["set-option", "-sq", "extended-keys", "off"])
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests_v2/test_tmux_compat_matrix.py` around lines 160 - 166, The test mutates
tmux option extended-keys via _run_cli(["set-option", "-sq", "extended-keys",
"always"]) but if the assertion on show_extended_keys fails the cleanup
_run_cli(... "off") is skipped; wrap the mutate + verification (the call to
_run_cli that sets "always" and the subsequent show-options/_must assertion that
expects "always") in a try/finally and move the reset call (_run_cli(cli,
["set-option", "-sq", "extended-keys", "off"])) into the finally block so
extended-keys is always reset even on assertion failures; reference the
show_extended_keys variable, the _run_cli calls for "set-option" and
"show-options", and the _must call when making this change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@CLI/cmux.swift`:
- Around line 12257-12285: The current code always overwrites
store.options[normalizedName] but parseTmuxArguments included flags for "-o"
(only-set) and "-a" (append); update the set-option handling to honor these
flags: after computing normalizedName and value, if parsed.hasFlag("-o") then
only set store.options[normalizedName] if it does not already exist; if
parsed.hasFlag("-a") then read existing = store.options[normalizedName] and set
store.options[normalizedName] = existing.isEmpty ? value :
"\(existing),\(value)" (or similar list-append logic), otherwise perform the
current overwrite; finally call try saveTmuxCompatStore(store) as before.

---

Nitpick comments:
In `@tests_v2/test_tmux_compat_matrix.py`:
- Around line 160-166: The test mutates tmux option extended-keys via
_run_cli(["set-option", "-sq", "extended-keys", "always"]) but if the assertion
on show_extended_keys fails the cleanup _run_cli(... "off") is skipped; wrap the
mutate + verification (the call to _run_cli that sets "always" and the
subsequent show-options/_must assertion that expects "always") in a try/finally
and move the reset call (_run_cli(cli, ["set-option", "-sq", "extended-keys",
"off"])) into the finally block so extended-keys is always reset even on
assertion failures; reference the show_extended_keys variable, the _run_cli
calls for "set-option" and "show-options", and the _must call when making this
change.
🪄 Autofix (Beta)

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: defaults

Review profile: CHILL

Plan: Pro

Run ID: 47e749ff-8c6d-4e9a-b2ba-4d265c244d9c

📥 Commits

Reviewing files that changed from the base of the PR and between f550206 and 758c722.

📒 Files selected for processing (2)
  • CLI/cmux.swift
  • tests_v2/test_tmux_compat_matrix.py

Comment on lines +12257 to +12285
case "set-option", "set", "set-window-option", "setw":
let parsed = try parseTmuxArguments(
commandArgs,
valueFlags: [],
boolFlags: ["-F", "-a", "-g", "-o", "-p", "-q", "-s", "-u", "-w"]
)
guard let optionName = parsed.positional.first?
.trimmingCharacters(in: .whitespacesAndNewlines),
!optionName.isEmpty else {
throw CLIError(message: "\(command) requires an option name")
}

var store = loadTmuxCompatStore()
let normalizedName = optionName.lowercased()
if parsed.hasFlag("-u") {
store.options.removeValue(forKey: normalizedName)
try saveTmuxCompatStore(store)
return
}

let value = parsed.positional.dropFirst().joined(separator: " ")
.trimmingCharacters(in: .whitespacesAndNewlines)
guard !value.isEmpty else {
throw CLIError(message: "\(command) requires an option value")
}

store.options[normalizedName] = value
try saveTmuxCompatStore(store)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Implement parsed -o / -a behavior in set-option.

Line 12261 parses -o and -a, but Lines 12283-12284 always overwrite the value. That diverges from tmux semantics and can break scripts expecting conditional set or append behavior.

🔧 Suggested patch
         case "set-option", "set", "set-window-option", "setw":
             let parsed = try parseTmuxArguments(
                 commandArgs,
                 valueFlags: [],
                 boolFlags: ["-F", "-a", "-g", "-o", "-p", "-q", "-s", "-u", "-w"]
             )
@@
             if parsed.hasFlag("-u") {
                 store.options.removeValue(forKey: normalizedName)
                 try saveTmuxCompatStore(store)
                 return
             }
@@
-            store.options[normalizedName] = value
+            if parsed.hasFlag("-o"), store.options[normalizedName] != nil {
+                return
+            }
+
+            if parsed.hasFlag("-a"), let existing = store.options[normalizedName] {
+                store.options[normalizedName] = existing + value
+            } else {
+                store.options[normalizedName] = value
+            }
             try saveTmuxCompatStore(store)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@CLI/cmux.swift` around lines 12257 - 12285, The current code always
overwrites store.options[normalizedName] but parseTmuxArguments included flags
for "-o" (only-set) and "-a" (append); update the set-option handling to honor
these flags: after computing normalizedName and value, if parsed.hasFlag("-o")
then only set store.options[normalizedName] if it does not already exist; if
parsed.hasFlag("-a") then read existing = store.options[normalizedName] and set
store.options[normalizedName] = existing.isEmpty ? value :
"\(existing),\(value)" (or similar list-append logic), otherwise perform the
current overwrite; finally call try saveTmuxCompatStore(store) as before.

kenryu42 added 2 commits April 7, 2026 18:03
Add regression coverage for two follow-up tmux-compat behaviors: listing
options with bare show-options and honoring set-option -o/-a semantics for
stored values. These expectations should fail until the CLI implementation is
extended to match them.

Constraint: Regression-test policy requires a failing test commit before the fix commit
Rejected: Fold the new expectations directly into the implementation commit | would weaken the regression proof required by repo policy
Confidence: high
Scope-risk: narrow
Reversibility: clean
Directive: Keep tmux option parity checks behavior-focused so future compat rewrites can still reuse them
Tested: python3 -m py_compile tests_v2/test_tmux_compat_matrix.py
Not-tested: End-to-end execution against a running cmux instance; local test runs are deferred to CI/VM by repo policy
Extend the new tmux option compatibility layer so bare show-options lists
known values and set-option now honors the parsed -o and -a flags. This keeps
the new behavior internally consistent and resolves the follow-up review notes
without changing the OMX-specific happy path.

Constraint: Follow-up review fixes must preserve the OMX startup behavior already covered by this branch
Rejected: Leave the parsed -o/-a flags as no-ops | would keep newly added compat behavior internally inconsistent
Confidence: high
Scope-risk: narrow
Reversibility: clean
Directive: Keep tmux option semantics minimal-but-coherent; add broader tmux parity in focused follow-ups if more flags become necessary
Tested: swiftc -parse CLI/cmux.swift
Tested: python3 -m py_compile tests_v2/test_tmux_compat_matrix.py
Not-tested: Full app build and runtime validation; Ghostty submodule setup currently panics on tagged-release version parsing
@kenryu42
Copy link
Copy Markdown
Author

kenryu42 commented Apr 7, 2026

@greptileai pls review

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

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)

11721-11736: ⚠️ Potential issue | 🟠 Major

Persist tmux option state per socket and under a lock.

extended-keys is a server-scoped option, but this stores it in one flat store.options map shared by every tmux-compat invocation. Tagged/debug sockets will now see and overwrite each other’s value, and the unlocked load/mutate/save path can also drop concurrent updates from another compat command. This needs a socket-scoped bucket plus a locked write path (the ClaudeHookSessionStore.withLockedState pattern earlier in this file would fit).

Also applies to: 12289-12313

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@CLI/cmux.swift` around lines 11721 - 11736, The store currently puts
server-scoped tmux options (like the extended-keys entry) into a single flat
options map (`options` / `store.options`) which causes tagged/debug sockets to
clobber each other and allows racey loss on concurrent mutates; change the
persistence to use a socket-scoped bucket (keyed by the socket identifier used
by your tmux-compat invocations) instead of a single map and wrap all
load/mutate/save accesses in the existing locked pattern
(`ClaudeHookSessionStore.withLockedState`) so reads/writes acquire the same
lock; update the code paths that read/write `options` (and related per-socket
state such as `mainVerticalLayouts`/`lastSplitSurface` if applicable) to use the
socket-keyed submap and the locked write path to avoid cross-socket pollution
and race conditions.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@CLI/cmux.swift`:
- Around line 12297-12301: The code currently builds `value` from
`parsed.positional.dropFirst().joined(separator: " ")` then trims and rejects
empty strings, which strips significant leading/trailing whitespace and forbids
explicit empty option values; change it to use the joined string as-is (do not
call `trimmingCharacters(in:)`) and stop throwing `CLIError` when `value` is
empty—only throw if there are no positional tokens at all (e.g.
`parsed.positional.count <= 1`); update the logic around `value` and the guard
so `set-option foo ""` and whitespace-significant values are preserved.
- Around line 2697-2703: dispatchSubcommandHelp() only shows help for commands
present in subcommandUsage(_:), so add entries for the new tmux-compat commands
to that function: include "show-option", "show-options", "set", "set-option",
"set-window-option", "set-buffer", and "setw" in subcommandUsage(_:) so that
cmux <command> --help routes to the proper help text; update the same additions
where subcommandUsage(_:) is duplicated (also applies to the other occurrence
referenced in the comment).

---

Outside diff comments:
In `@CLI/cmux.swift`:
- Around line 11721-11736: The store currently puts server-scoped tmux options
(like the extended-keys entry) into a single flat options map (`options` /
`store.options`) which causes tagged/debug sockets to clobber each other and
allows racey loss on concurrent mutates; change the persistence to use a
socket-scoped bucket (keyed by the socket identifier used by your tmux-compat
invocations) instead of a single map and wrap all load/mutate/save accesses in
the existing locked pattern (`ClaudeHookSessionStore.withLockedState`) so
reads/writes acquire the same lock; update the code paths that read/write
`options` (and related per-socket state such as
`mainVerticalLayouts`/`lastSplitSurface` if applicable) to use the socket-keyed
submap and the locked write path to avoid cross-socket pollution and race
conditions.
🪄 Autofix (Beta)

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: defaults

Review profile: CHILL

Plan: Pro

Run ID: f83350fc-bd47-4e78-bc5c-3a1af77e113f

📥 Commits

Reviewing files that changed from the base of the PR and between 758c722 and e0a1a27.

📒 Files selected for processing (2)
  • CLI/cmux.swift
  • tests_v2/test_tmux_compat_matrix.py
✅ Files skipped from review due to trivial changes (1)
  • tests_v2/test_tmux_compat_matrix.py

kenryu42 added 2 commits April 7, 2026 18:40
Add behavioral coverage for two remaining tmux-compat gaps on this branch:
new show/set subcommands should surface help via --help, and set-option
should preserve explicit empty strings plus leading or trailing whitespace in
stored values.

Constraint: Regression-test policy requires a failing test commit before the fix commit
Rejected: Bundle these assertions into the implementation commit | would weaken the regression proof required by repo policy
Confidence: high
Scope-risk: narrow
Reversibility: clean
Directive: Keep tmux-compat regression cases focused on user-visible CLI behavior rather than implementation details
Tested: python3 -m py_compile tests_v2/test_tmux_compat_matrix.py
Not-tested: End-to-end execution against a running cmux instance; local test runs are deferred to CI/VM by repo policy
Close the remaining review feedback on this branch by wiring show/set tmux-compat
commands into subcommand help and by storing option values exactly as passed.
That keeps quoted empty strings and whitespace-significant values round-trippable
while making Unknown command 'show-options'. Run 'cmux help' to see available commands. and friends behave like other CLI
subcommands.

Constraint: The fix must preserve the OMX startup behavior already covered by this branch
Rejected: Trim and reject empty option values | would keep the new tmux-compat layer unable to round-trip exact values
Confidence: high
Scope-risk: narrow
Reversibility: clean
Directive: Treat tmux-compat option values as exact user input unless tmux semantics require transformation
Tested: swiftc -parse CLI/cmux.swift
Tested: python3 -m py_compile tests_v2/test_tmux_compat_matrix.py
Tested: ./scripts/reload.sh --tag fix-tmux-option-help (blocked by unrelated Ghostty build panic)
Not-tested: Full app build and runtime validation; Ghostty submodule setup currently panics on tagged-release version parsing
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
tests_v2/test_tmux_compat_matrix.py (1)

162-197: Make option-state cleanup failure-safe to avoid cross-run test pollution.

This test mutates persisted compat options. If execution fails/interruption occurs before teardown lines, later runs can inherit stale values (notably extended-keys), causing cascading failures. Prefer try/finally cleanup.

♻️ Suggested refactor (failure-safe cleanup)
-        show_extended_keys = _run_cli(cli, ["show-options", "-sv", "extended-keys"])
-        _must(show_extended_keys.stdout.strip() == "off", f"show-options should default extended-keys to off, got {show_extended_keys.stdout!r}")
-        _run_cli(cli, ["set-option", "-sq", "extended-keys", "always"])
-        show_extended_keys = _run_cli(cli, ["show-options", "-sv", "extended-keys"])
-        _must(show_extended_keys.stdout.strip() == "always", f"set-option should persist extended-keys, got {show_extended_keys.stdout!r}")
-        _run_cli(cli, ["set-option", "-sq", "extended-keys", "off"])
-        show_all_options = _run_cli(cli, ["show-options"])
-        _must("extended-keys off" in show_all_options.stdout, f"show-options should list default options, got {show_all_options.stdout!r}")
-
-        custom_option = f"@compat_probe_{stamp}"
-        _run_cli(cli, ["set-option", custom_option, "alpha"])
-        _run_cli(cli, ["set-option", "-o", custom_option, "beta"])
-        custom_value = _run_cli(cli, ["show-options", "-v", custom_option])
-        _must(custom_value.stdout.strip() == "alpha", f"set-option -o should keep the existing value, got {custom_value.stdout!r}")
-        _run_cli(cli, ["set-option", "-a", custom_option, "gamma"])
-        custom_value = _run_cli(cli, ["show-options", "-v", custom_option])
-        _must(custom_value.stdout.strip() == "alphagamma", f"set-option -a should append to the existing value, got {custom_value.stdout!r}")
-        show_all_options = _run_cli(cli, ["show-options"])
-        _must(f"{custom_option} alphagamma" in show_all_options.stdout, f"show-options should list custom options, got {show_all_options.stdout!r}")
-        _run_cli(cli, ["set-option", "-u", custom_option])
+        custom_option = None
+        empty_option = None
+        spaced_option = None
+        try:
+            show_extended_keys = _run_cli(cli, ["show-options", "-sv", "extended-keys"])
+            _must(show_extended_keys.stdout.strip() == "off", f"show-options should default extended-keys to off, got {show_extended_keys.stdout!r}")
+            _run_cli(cli, ["set-option", "-sq", "extended-keys", "always"])
+            show_extended_keys = _run_cli(cli, ["show-options", "-sv", "extended-keys"])
+            _must(show_extended_keys.stdout.strip() == "always", f"set-option should persist extended-keys, got {show_extended_keys.stdout!r}")
+            _run_cli(cli, ["set-option", "-sq", "extended-keys", "off"])
+            show_all_options = _run_cli(cli, ["show-options"])
+            _must("extended-keys off" in show_all_options.stdout, f"show-options should list default options, got {show_all_options.stdout!r}")
+
+            custom_option = f"@compat_probe_{stamp}"
+            _run_cli(cli, ["set-option", custom_option, "alpha"])
+            _run_cli(cli, ["set-option", "-o", custom_option, "beta"])
+            custom_value = _run_cli(cli, ["show-options", "-v", custom_option])
+            _must(custom_value.stdout.strip() == "alpha", f"set-option -o should keep the existing value, got {custom_value.stdout!r}")
+            _run_cli(cli, ["set-option", "-a", custom_option, "gamma"])
+            custom_value = _run_cli(cli, ["show-options", "-v", custom_option])
+            _must(custom_value.stdout.strip() == "alphagamma", f"set-option -a should append to the existing value, got {custom_value.stdout!r}")
+            show_all_options = _run_cli(cli, ["show-options"])
+            _must(f"{custom_option} alphagamma" in show_all_options.stdout, f"show-options should list custom options, got {show_all_options.stdout!r}")
+
+            empty_option = f"@empty_probe_{stamp}"
+            _run_cli(cli, ["set-option", empty_option, ""])
+            empty_value = _run_cli(cli, ["show-options", "-v", empty_option])
+            _must(empty_value.stdout == "\n", f"set-option should preserve explicit empty values, got {empty_value.stdout!r}")
+
+            spaced_option = f"@space_probe_{stamp}"
+            spaced_value = "  padded value  "
+            _run_cli(cli, ["set-option", spaced_option, spaced_value])
+            shown_spaced_value = _run_cli(cli, ["show-options", "-v", spaced_option])
+            _must(shown_spaced_value.stdout == spaced_value + "\n", f"set-option should preserve leading/trailing whitespace, got {shown_spaced_value.stdout!r}")
+        finally:
+            _run_cli(cli, ["set-option", "-sq", "extended-keys", "off"], expect_ok=False)
+            for opt in (custom_option, empty_option, spaced_option):
+                if opt:
+                    _run_cli(cli, ["set-option", "-u", opt], expect_ok=False)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests_v2/test_tmux_compat_matrix.py` around lines 162 - 197, The test mutates
persisted options (calls to _run_cli with "set-option" for "extended-keys",
custom_option, empty_option, spaced_option) without guaranteeing cleanup on
failure; wrap the section that changes options in a try/finally so all mutated
state is reverted in the finally block (restore original extended-keys value
using show-options -sv before changing it, and ensure you call _run_cli(...,
["set-option", "-u", <custom_option>]) / ["set-option", "-u", <empty_option>] /
["set-option", "-u", <spaced_option>] even if an assertion via _must or an
exception occurs). Use the existing helpers (_run_cli, _must) and reference the
variables extended-keys, custom_option, empty_option, spaced_option to locate
the changes to revert.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@tests_v2/test_tmux_compat_matrix.py`:
- Around line 162-197: The test mutates persisted options (calls to _run_cli
with "set-option" for "extended-keys", custom_option, empty_option,
spaced_option) without guaranteeing cleanup on failure; wrap the section that
changes options in a try/finally so all mutated state is reverted in the finally
block (restore original extended-keys value using show-options -sv before
changing it, and ensure you call _run_cli(..., ["set-option", "-u",
<custom_option>]) / ["set-option", "-u", <empty_option>] / ["set-option", "-u",
<spaced_option>] even if an assertion via _must or an exception occurs). Use the
existing helpers (_run_cli, _must) and reference the variables extended-keys,
custom_option, empty_option, spaced_option to locate the changes to revert.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 4ce75d70-e553-43c1-9d4e-d2b3f0936fc1

📥 Commits

Reviewing files that changed from the base of the PR and between e0a1a27 and 912beac.

📒 Files selected for processing (2)
  • CLI/cmux.swift
  • tests_v2/test_tmux_compat_matrix.py
✅ Files skipped from review due to trivial changes (1)
  • CLI/cmux.swift

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.

cmux omx fails with "Unsupported tmux compatibility command: show-options"

1 participant