Repository navigation
Conversation
why: The module's tmux floor lived only in the shared .NET package,
which still accepts 3.2a and reads the client executable's `-V` instead
of asking the daemon that will run the command. Nothing refused an old
daemon before a cmdlet changed it.
what:
- Check the connected daemon before every cmdlet dispatches: tmux 3.3a
or newer, comparing patch letters, with `next-X.Y` and release
candidates ranked below the release they name and undated or unknown
reports refused.
- Probe with `-N run-shell -C <marker>#{version}`. run-shell has no
after-hook and `-C` fails while parsing its own output, so no
`after-*` or `command-error` hook fires; the version comes from the
`unknown command:` line on stderr. An absent endpoint falls back to
the executable's `-V`, accepting `tmux <ver>` and `<basename> <ver>`
because 3.3a prints its own file name.
- Resolve the server from the operation's target in
`TmuxCmdlet.RunOperation`; cmdlets whose target is not the endpoint
they use override `ResolveServer`.
- Report a refusal as a non-terminating error carrying
`TmuxVersionTooLowException`, so `Get-*` cmdlets cannot turn it into
an empty result.
- Fence `Invoke-TmuxCommand` and `Invoke-TmuxChain` batches with `if-
shell -F '#{==:#{version},<checked>}' '' <marker>` as the first
command, so a daemon replaced after the check fails on the unknown
marker before any later command runs. The fence is stripped from the
returned arguments.
- Refuse a daemon older than 3.2 by the answer it gives the probe: a
3.1c daemon prints the `run-shell` usage line, a 3.0a daemon makes the
client print `server version is too old for client`, and a client
older than 3.2 rejects `-N`, so the executable's `-V` decides. Each
refusal says to upgrade tmux and restart the server, or to keep
libtmux for PowerShell 0.1.0-alpha2.
why: The version check needs tests that fail when it stops refusing, stops fencing or starts firing tmux hooks. what: - Add tests/VersionCheck.Tests.ps1, which runs against the tmux first on PATH. - Below 3.3a, assert that reads, session creation, raw commands and chains are refused on a live daemon and on an absent endpoint, including through a client wrapper that claims `tmux 9.9`, and that the daemon's sessions and the absent socket are unchanged. - On 3.3a or newer, assert that reads and writes are checked and that neither the check nor the fence fires an after-display-message or command-error hook, with a native display-message and a failing command as positive controls. The command-error half is reported SKIPPED before tmux 3.5, where `set-hook` rejects it. - Assert that a replaced daemon fails the fence for both batch cmdlets and leaves a sentinel session uncreated, while an unchanged daemon behind the same wrapper is not refused. - Assert the version grammar: 3.2a, 3.3, next-3.3, 3.3-rc, master and malformed text are refused; 3.3a, 3.3b, 3.4, 3.4-rc1, next-3.4 and 3.7d are accepted. - Answer the probe with doubles that print the 3.1c usage line, the 3.0a protocol refusal and the unknown `-N` error, and require each refusal to name its release and the remedy. The 3.1c and 3.0a doubles keep stdout open past the client's exit, as those daemons do, and the read must fail within 10 s. With LIBTMUX_OLD_DAEMON set, the same bound applies to a real 3.0a or 3.1c daemon behind a 3.3a client.
why: Lanes below the floor must assert refusal without running suites that need a supported daemon. what: - Add `eng/Test.ps1 -Suite VersionCheck`, which installs the packed modules and runs tests/VersionCheck.Tests.ps1 alone. - Run the suite with the others in the Product suite.
why: tmux 3.2a sized detached windows from the command client unless `window-size` was manual, so the create and guide fixtures forced that option on that release only. The 3.3a floor removes the release. what: - Remove the forced option from the create and guide fixtures and the note from the create guide. - Keep reading the daemon version in the guide fixture for its deferral check and in Create.Tests.ps1 for tmux 3.4's dollar-sign escaping.
why: The workflow still exercised 3.2a, which the module no longer supports, and had no lane proving that older daemons are refused. what: - Pair PowerShell 7.4.20 and 7.6.6 with tmux 3.3a and 3.7c, and swap 3.2a for the same 3.3a source commit in the advisory macOS trial. The MCP discovery step follows the lower endpoint. - Add a `reject` job that builds tmux 3.2a and unpatched 3.3 from their pinned source commits, checks the tag, and runs only the VersionCheck suite against the packed modules. The 3.3 commit is the `3.3` tag of tmux/tmux; the shared build script does not know that release, so the job builds from source directly. - Add the job to the aggregate gate and the release artifact binding, so a release needs the rejection cells as well as the supported ones. The artifact checker's job set and its unit test follow. - Add a `reject-old-daemon` job that builds tmux 3.3a and a 3.0a or 3.1c daemon from their pinned tag commits and runs the VersionCheck suite with the 3.3a client against that daemon. The job joins the gate and the release artifact binding.
why: The install note, the compatibility page and the contributing guide disagreed about the minimum tmux. what: - State the 3.3a minimum, how the connected daemon is checked, and where it is tested in the README, the compatibility page and the contributing guide. - Explain on the compatibility page that reads fail with `TmuxVersionTooLowException` instead of returning nothing, what the batch fence covers and what it does not, and that the shared .NET package still lists 3.2a and reads only a `tmux <version>` banner. - Prepare a 3.3a plan in the Get-TmuxQueryPlan example; regenerate the help XML and follow in the help-example and source query tests. - Describe how daemons older than 3.2 are refused and which CI job proves it.
why: If the checked daemon disappeared before dispatch, an older client executable could start a replacement server and run the batch there. what: - Write `-N` as the first word of the native command list that starts a live endpoint's fence. The shared package cannot add the flag to a chain itself. - Add tests that kill the daemon between the probe and the dispatch of `new-session` and assert the dispatch fails with no server left, plus the development-build versions: next-3.9, 3.8-rc2 and 3.8 are accepted; next-3.3a, openbsd-7.4 and bare master are refused.
why: The package wraps a batch that carries a server generation in its own guard and then treats every command name as tmux text, so the leading `-N` failed the guarded input chain with `unknown command: -N`. what: - Add `-N` only to unguarded batches. A guarded batch is already bound to one daemon and fails on a replaced generation.
why: Several suites wrap tmux in a script that logs one line per invocation, then assert that invalid input, previews and offline paths dispatch nothing. The version check now runs one hook-free probe, or an executable `-V` for an absent endpoint, ahead of the library call that rejects the input. what: - Make those wrappers skip exactly the probe and `-V` forms and keep counting every other invocation, including the fenced batch.
why: tmux 3.3a's configure stops with "must give --enable-utf8proc or --disable-utf8proc" when it finds utf8proc, which Homebrew installs, so the shared build script's bare configure failed every 3.3a cell before any suite ran. what: - Configure the already cloned and verified source in the trial with `--enable-utf8proc`. - Write the same source-commit marker the later steps read, and keep the 3.7c cells on the same path.
why: With `--enable-utf8proc` the arm64 cells stopped at "utf8proc not found": Homebrew installs under /opt/homebrew, outside the compiler's default search paths, while the Intel cells find it under /usr/local. what: - Pass the include and library directories from `brew --prefix utf8proc` to configure.
why: `TmuxCmdlet` skipped the version check whenever `ResolveServer` returned null, so a cmdlet that forgot to resolve its server shipped unchecked without any failure. what: - Fail an operation that resolves no server with an error naming the cmdlet, unless the class carries the new `TmuxVersionCheckExempt` attribute, which states why no daemon needs a check. - Mark the three Select-Tmux* filters, the workspace import, resolve, discovery and conversion cmdlets, Disconnect-TmuxControl, Invoke- TmuxControlCommand and Watch-TmuxEvent exempt. The last two take a connection the caller acquired, and their own server is still checked.
why: A failed `Invoke-TmuxChain` rethrew the package's exception whose result still began with the fence and its marker. Separately, `Invoke- TmuxCommand kill-server` against an endpoint where no daemon answers dispatched a command that can only fail. what: - Carry the result the caller's commands produced in the error, without the fence. - Return an empty successful result for `kill-server` when no daemon answers.
why: The fail-closed rule, the version grammar and the error contents had no tests. what: - Check by reflection that the exempt cmdlets in both assemblies equal an explicit list, that every other cmdlet exposes a Server, Session, Window, Pane, Client or plan parameter or overrides `ResolveServer`, and that a test cmdlet that does neither is refused at run time. - Add one table of version vectors (accept 3.3a, 3.10, 4.0, bare and numbered release candidates, next-3.9; refuse 3.3, 3.3-rc, next-3.3, master, openbsd-7.6, empty, 3.3a-master, 3.3a-dev, next-3.3a, next-3.9-master) and the ordering next-X.Y < X.Y-rc < X.Y-rcN < X.Y < X.Ya. - Assert that a failed chain's error carries no fence or marker and that `kill-server` on an absent endpoint succeeds with no output.
why: Readers could not tell how a refusal behaves under `-ErrorAction`, whether the MCP server is covered, or why the benchmark receipts name 3.2a. what: - Say on the compatibility page that a refusal is a non-terminating error that follows `-ErrorAction`, so `SilentlyContinue` hides it as it does any operation failure. - Say that `kill-server` on an absent endpoint is a no-op, and that the separate MCP tool is outside this change and keeps the package's floor until the package checks the daemon itself. - Note in the benchmark README that its 3.2a receipts predate the floor.
why: `Invoke-TmuxControlCommand` sent commands over a connection the
caller opened elsewhere without checking the daemon behind it, and a
connection exposes no server to resolve.
what:
- Ask the connection for `list-commands -F '#{version}'` and refuse a
sub-floor or undated daemon before the command is sent. Remember the
answer per connection.
- Record the version `Connect-TmuxControl` acquired under. The package
refuses `run-shell` on a control connection, so the usual probe is
unavailable here.
why: The control-connection check needs a test that fails when the check is removed. what: - On tmux 3.7 or newer, use a connection opened with `EnterControlModeAsync` with `Invoke-TmuxControlCommand`; the same connection recorded as a 3.2a daemon's is refused with the version error while the option it would have set stays unset. - Report the check SKIPPED below 3.7, where control clients are not routine.
why: The Product suite stops at its first failing worker, and on macOS that is ResourceInstall.Tests.ps1, so the version check suite never ran there. what: - Run `-Suite VersionCheck` first in the advisory trial and report it beside Product and Documentation in the summary and warning. - Keep `continue-on-error` on every step: the trial stays advisory.
tony
force-pushed
the
bump-min-tmux-000
branch
from
October 17, 2026 12:16
d9994b9 to
8785b3e
Compare
why: A `list-commands` command sent over the connection carries an after-hook flag, but the version check must trigger no after-hook. what: - Rely on the version a connection from `Connect-TmuxControl` already carries from before the package attached. - Add `-Server` to `Invoke-TmuxControlCommand` for a connection opened elsewhere, which exposes no endpoint. The cmdlet runs the hook-free `run-shell -C` subprocess probe against that server and remembers the answer. Without `-Server` the command is refused. - The remaining race: the connection could belong to a different daemon if the endpoint was replaced between its attach and the call.
why: A regression to a `list-commands` probe must fail a test. what: - On tmux 3.7 or newer, arm after-list-commands (where the release allows it), after-display-message and command-error, and assert that connections from `Connect-TmuxControl` and a foreign connection named with `-Server` fire no more hooks than the package's own attach, and that the armed hooks do fire for ordinary commands. - Use a counting connection to prove that checking a foreign connection sends nothing over it and that an unknown connection without `-Server` is refused. after-list-commands is not settable on 3.5, 3.7d, 3.8 or master, so the counting connection is the check that catches a `list- commands` probe.
why: The compatibility page did not say how a control connection's daemon is checked. what: - Describe the version recorded by `Connect-TmuxControl`, the `-Server` probe for a foreign connection, and the refusal without it.
why: Marking the cmdlet exempt let it resolve no server, but its Session parameter still resolved to the session's server by default. Freezing a captured session then probed a daemon that may be gone, and after `kill- server` failed with "tmux did not report a daemon version". what: - Restore the `ResolveServer` override that returns null; the exemption states why.
why: The `-Server` parameter was missing from the cmdlet's reference page, so the documentation export failed its check that help and the native parameter inventory agree. what: - Describe `-Server` on the reference page and in the generated help XML.
why: Every cmdlet invocation ran its own probe, once per record in a pipeline. Separately, on native Windows an operation that resolved a server skipped the check and later dereferenced a null. what: - Cache the checked version of a live endpoint, keyed by the executable, socket, configuration and socket directory. An absent, refused or name-factory endpoint is never cached. - Forget the entry when a fenced batch finds the daemon changed, a batch fails, a transport fails or a generation goes stale, so the next operation probes again. Typed operations are unfenced, so a daemon replaced by an older one between two of them is still seen only when a fenced batch or a failure follows. - Fail with `PlatformNotSupportedException` on native Windows, since the cmdlets are Unix-only.
why: The cache and its invalidation need tests that fail without them. what: - Count probes through a wrapper: three typed reads and a raw command on one endpoint probe once. - Make a wrapper answer its first probe as 9.9, so the first fenced batch fails on the real daemon; the next batch must probe again (two probes in total) and succeed. - Plant an option and a buffer beside the session in the rejection half and assert all three are unchanged. - Make a rejection lane that sets `LIBTMUX_REQUIRE_REJECTED` fail unless the tmux on PATH is exactly that unsupported release.
why: The matrix covered only the floor and the newest release, so a release in between or an upcoming one could break unnoticed. what: - Return the Linux matrix to 3.3a, 3.4, 3.5, 3.6, 3.7a, 3.7b and 3.7c, each on PowerShell 7.4.20 and 7.6.6, and add a 3.8 lane (tag and commit pinned) and a master lane (`next-3.9`, built from the current head, logging the commit it built). The shared build script knows neither, so the job builds them from source. - Export `LIBTMUX_REQUIRE_REJECTED` in the rejection job so a lane whose tmux is not the unsupported release it names fails instead of passing quietly. - Follow with the release-artifact job set and its unit test.
why: The compatibility page and the contributing guide listed an outdated matrix. what: - List the releases the Linux workflow now exercises.
why: The offline probe cmdlet in Runtime.Tests.ps1 derives from `TmuxCmdlet` and resolves no server, so the fail-closed check refused it with "The runtime operation did not start" on every lane. what: - Mark it exempt. Its fenced case dispatches nothing, and its native case still names its server, which is checked.
why: On PowerShell 7.6 the 7.4-built module made the inline test cmdlet fail with CS1701. The other suites that compile inline C# reference the host's reference assemblies and suppress that warning. what: - Do the same in the VersionCheck suite.
tony
force-pushed
the
bump-min-tmux-000
branch
2 times, most recently
from
October 17, 2026 17:19
a0da4d8 to
bf018d3
Compare
tony
force-pushed
the
bump-min-tmux-000
branch
from
October 17, 2026 18:53
bf018d3 to
3a7f24e
Compare
why: Callers need to know that older tmux daemons are now refused and what the refusal looks like. what: - Add a breaking-change entry for the 3.3a floor, the version-too-low error on reads, the replaced-daemon refusal and the absent-endpoint kill-server no-op.
tony
force-pushed
the
bump-min-tmux-000
branch
from
October 17, 2026 19:04
3a7f24e to
831c283
Compare
tony
force-pushed
the
bump-min-tmux-000
branch
from
October 18, 2026 04:05
831c283 to
2c5c712
Compare
This branch was successfully deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Raise the minimum tmux for every cmdlet that talks to tmux to 3.3a, decided from the connected daemon before anything is sent.
The cmdlets are a thin layer over the published LibTmux .NET package, which still lists 3.2a as its own minimum and checks only the client executable's
-V. This change does not wait for that package:TmuxCmdletresolves the server behind each operation's target (server, session, window, pane or client) and checks its version first. Workspace plan, apply and conversion say which server they use. A refusal is aLibTmux.TmuxVersionTooLowExceptionerror on the error stream, soGet-*cmdlets cannot turn it into an empty result.The probe is
-N run-shell -C <marker>#{version}:run-shellhas no after-hook and-Cfails while parsing its own output, so neither anafter-*nor acommand-errorhook fires. Releases compare with patch letters.next-X.YandX.Y-rcNrank just belowX.Y, sonext-3.9and3.8-rc2are supported and3.3,next-3.3a,openbsd-*, baremasterand malformed reports are refused. An absent endpoint validates the executable's own-V(tmux <ver>or<basename> <ver>) instead.The checked version of a live endpoint is cached, keyed by the executable, socket, configuration and socket directory, so a pipeline of records probes once. An absent, refused or name-factory endpoint is never cached. A fenced batch that finds the daemon changed, a failed batch, a transport failure or a stale generation forgets the entry. On native Windows an operation that resolves a server now fails with
PlatformNotSupportedExceptioninstead of skipping the check and dereferencing a null.Invoke-TmuxCommandandInvoke-TmuxChainalso fence the batch: the first command isif-shell -F '#{==:#{version},<checked>}' '' <marker>, so a daemon replaced after the version check fails before any later command runs and raises the version error. A live endpoint's fence starts with-N, so a vanished daemon is not restarted by the dispatching client. A generation-guarded batch is left to the package's own wrapping and gets no-N. Typed operations are checked per cmdlet but not fenced, because the package dispatches them one command at a time, so a daemon replaced by an older one between two typed operations is seen only when a fenced batch or a failure follows.The check fails closed. A cmdlet that resolves no server fails its operation unless it carries
TmuxVersionCheckExempt; theSelect-Tmux*filters, the workspace import, resolve, discovery and conversion cmdlets,Disconnect-TmuxControl,Invoke-TmuxControlCommandandWatch-TmuxEventdo. A connection thatConnect-TmuxControlopened carries the version checked before the package attached. A connection opened elsewhere exposes no endpoint, soInvoke-TmuxControlCommandtakes-Serverand runs the hook-freerun-shell -Cprobe against it; without-Serverthe command is refused. Nothing is sent over the connection for the check. The narrow race is that the connection could belong to a different daemon if the endpoint was replaced between its attach and the call.Invoke-TmuxCommand kill-serveron an absent endpoint is an empty success, and a failed chain's error no longer carries the fence.A refusal is a non-terminating error like every other operation failure, so
-ErrorAction SilentlyContinuehides it; the compatibility page says to use-ErrorAction Stop. The separate MCP tool is outside this change and keeps the package's floor. Once the .NET package checks and fences by itself, typed calls can rely on it and this layer can shrink.Wait-TmuxPaneTextis untouched here. Its polling fallback below tmux 3.7 and the control-mode gating belong to the optional-features PR (#22).CI tests tmux 3.3a, 3.4, 3.5, 3.6, 3.7a, 3.7b, 3.7c, 3.8 and master on PowerShell 7.4.20 and 7.6.6, and adds a
rejectjob that builds tmux 3.2a and unpatched 3.3 from their pinned commits and runs only the VersionCheck suite against the packed modules. The 3.2a window-size workaround in two fixtures is removed. README, compatibility page, contributing guide, one help example and the release-artifact job set name the same floor.Each guard has a test that fails when the guard is removed: no check, floors 3.2a and 3.3, no fence, no
-N, adisplay-messageprobe, a missing exemption, the fail-closed check, a leaked fence, thekill-serverno-op, the control-connection check, alist-commandsprobe, no cache and no invalidation. The version grammar test is one table of vectors plus the orderingnext-X.Y < X.Y-rc < X.Y-rcN < X.Y < X.Ya. The command-error hook half is reported SKIPPED before tmux 3.5, and the renamed-3.3a-executable case is SKIPPED because the shared package reads only atmux <version>banner. A3.4a-rc1report would be refused where the Python reference accepts it.macOS: the trial is advisory and does not establish macOS support. The Product suite aborts at
ResourceInstall.Tests.ps1there, so the trial now runs the VersionCheck suite as its own step. The trial run on master is the baseline for that failure.Not yet qualified: the hosted matrix and the rejection lanes for the latest head. Results are posted on the PR when the runs finish.
Closes #17.