Repository navigation
Conversation
why: The released dependency range alone does not identify the new daemon version-check contract. what: - Select the reviewed immutable libtmux core source - Keep the existing package range and other locked versions
why: Direct subprocesses and completion helpers could bypass backend version checks, while a live probe alone permitted endpoint replacement. what: - Check the actual daemon or freshly checked cold executable - Fence owned commands and share the remaining process deadline - Carry synchronous and asynchronous callers with cancellation ownership - Retain baseline polling and standalone configuration caching - Exercise native old-server refusal and actual child cleanup
why: The supported matrix must retain the 3.3a floor and separately prove rejection below it. what: - Keep supported lanes from 3.3a and add stable 3.7a - Build native 3.2a and exact 3.3 for negative version checks - Isolate test sockets without suppressing the refusal cases
why: Operators need the selected-daemon rule and a concrete installation route for the unreleased coordinated backend. what: - Document the 3.3a core minimum and source-pinned development pair - State worker-thread cancellation and native alias boundaries - Keep optional requirements separate from baseline polling
why: The tmux support floor and owned command behavior change. what: - Record daemon version checks and bounded subprocess cleanup - Describe the coordinated backend requirement
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #139 +/- ##
==========================================
- Coverage 86.81% 86.52% -0.29%
==========================================
Files 46 46
Lines 3899 4053 +154
Branches 587 606 +19
==========================================
+ Hits 3385 3507 +122
- Misses 360 381 +21
- Partials 154 165 +11 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Move the coordinated libtmux source pin to the revision whose version probe uses run-shell -C instead of display-message, so no user hook fires before a refusal. uv.lock changes only the two rev references; the unrelated set reordering uv lock emits is not committed.
The owned-subprocess version probe ran display-message -p '#{version}' in
both the async and the synchronous lane, which fires a user's
after-display-message hook before any refusal. Probe with
`-N run-shell -C '<marker>#{version}'` as libtmux does: run-shell has no
after-hook, and with -C its failed parse reports the expanded text without a
command-error hook. The version is read from the `unknown command: <marker>`
line; any other failure stays a version-check error, and the absent-endpoint
detection and `-V` fallback are unchanged.
Tests: a native test sets after-display-message and, where set-hook accepts
it, command-error hooks, runs both lanes, then fires a control display-message
and a failing command. Hooks run in order, so each sentinel holding exactly
one letter shows the probes fired none. It runs on the 3.3a lane and the path
tmux. The stub tmux in the wait-tool test answers the new probe.
Verification: the new test and the other version check tests pass on 3.3a with
3.2a and unpatched 3.3 refusals, and on 3.7d. Putting a display-message
call back into the probe fails the test with b'xxx' where b'x' is expected,
on both binaries. Ruff and mypy are clean.
Note that owned subprocess version checks run no user hooks. Keep the release note separate from runtime changes.
The range `libtmux>=0.62.0,<1.0` was satisfied by the released 0.62.0, which has no version-check backend, so an installer that ignores uv's source override (pip, pipx, uvx) would pick a libtmux this package then refuses to use. libtmux 0.63.x is the series that carries libtmux._internal.support, so require 0.63.0 or newer. The `[tool.uv.sources]` git pin stays as the development path until libtmux 0.63.0 is released and moves to libtmux's hook-free, cached, version-check head (73f82c716). uv.lock changes only the two rev references; `uv lock --check` passes. Release order: libtmux 0.63.0 first, then drop the source override here.
Probe, -V and backend failures raised bare RuntimeError, which the tool error mapper reports as "Unexpected error" at ERROR level instead of an expected tool error. Raise LibTmuxException from them, and import the daemon-first check from the libtmux._internal.support module (require_version, check_fence, MINIMUM). A backend that lacks the module or its names is refused before any subprocess starts. Tests: the incompatible-backend test stubs libtmux._internal.support and expects LibTmuxException on both lanes. Verification: version check module passes on 3.3a with the 3.2a and unpatched 3.3 lanes; ruff and mypy are clean.
Startup probed only the default endpoint and stopped the whole server when that daemon was below 3.3a, even though every tool takes its own socket and checks it on each call. Log a warning for an unsupported, unreachable or slow default endpoint and keep serving; a missing backend or tmux binary still stops startup. Test: a stubbed VersionTooLow from the startup probe enters the lifespan and logs the warning. Replacing the handler with a re-raise fails it.
run_command types version-fenced tmux commands into the pane. If the daemon is replaced before the user's command finishes, tmux prints `unknown command: <marker>` in the pane and runs neither, so no status is stored and the channel is never signalled: the caller saw an ordinary timeout. Keep both fence markers and, on a timeout or an unreadable status, look for those exact lines in the pane and raise VersionTooLow instead. The tool boundary reports it as an expected tool error. Tests: a typed fence forced to fail its version condition surfaces "changed before run_command" instead of timed_out, and the marker helper ignores other lines and matches only this call's markers.
Name libtmux 0.63.0 as the first release with libtmux._internal.support, describe the source override as the development path only, and say to release libtmux first and drop the override before merging this change.
…vior Note the new libtmux floor, expected tool errors for version check failures, and that an unsupported default endpoint no longer stops startup. Keep the release note separate from runtime changes.
Cover the async and synchronous version check lanes with a probe that fails with permission denied: both raise LibTmuxException, which the tool boundary reports as an expected error. Making the helper raise RuntimeError again fails the new test.
tony
force-pushed
the
bump-min-tmux-000
branch
from
October 17, 2026 12:40
39cc1a7 to
918119a
Compare
Move the development source pin to the libtmux revision with the ranked version grammar, the per-endpoint cache, the control and name-guard fixes (f6e4630a), which publishes the version check as libtmux._internal.support. uv.lock changes only the two rev references.
Every owned call spawned a version probe before the command, and each polling iteration of wait_for_text paid it again. The fence in the dispatched command already catches a replaced daemon, so remember the daemon's release per live endpoint (binary, selectors, TMUX_TMPDIR, TMUX): a fence refusal forgets it, and a call that finds the cached daemon gone forgets it and checks again once. An absent endpoint is never cached. Tests: three sync calls and one async call probe once; a daemon killed from outside is checked again; a stale cached release is refused with no effect and forgotten. Never storing the release fails the probe-once test.
Run the matrix against the released 3.8 tag before master; only master stays continue-on-error.
Note that owned subprocesses remember the daemon's release per endpoint. Keep the release note separate from runtime changes.
tony
force-pushed
the
bump-min-tmux-000
branch
2 times, most recently
from
October 17, 2026 17:57
653f6d5 to
a2b4b3a
Compare
This branch has not been 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.
Require tmux 3.3a for ordinary MCP operations. The version check uses the selected daemon, or freshly checks the configured executable when that endpoint is absent. A newer installed client cannot authorize an older daemon.
The owned version probe is hook-free:
-N run-shell -C '<marker>#{version}'replacesdisplay-message, so a user'safter-display-messageandcommand-errorhooks do not run for the version check; the version is read from theunknown command: <marker>line. Owned asynchronous and synchronous subprocesses, buffer/input helpers and generated completion commands carry the same version check and native version guard. Metadata and effects share one remaining process deadline; cancellation kills and reaps the owned asynchronous child. Startup checks the backend and selected endpoint before tool traffic. Cached server handles retain configuration, while each operation checks the current daemon.The package requires
libtmux>=0.63.0, the first release with thelibtmux._internal.supportmodule this PR imports; the previous>=0.62.0,<1.0range was satisfied by the released 0.62.0, which has no backend, so pip, pipx and uvx would have installed a libtmux this package refuses. The development lock pins the implementation from Python core PR779 until the release exists; uv's source override is ignored by ordinary installers.Merge and release order: libtmux 0.63.0 is released first, from #779 and #780. This PR requires
libtmux>=0.63.0, which no installer can satisfy until then, so it merges only after that release, in the same change that drops the development[tool.uv.sources]pin. Nothing in this PR tags or releases.Validation:
after-display-messageand, where accepted,command-errorhooks and runs both lanes; it passes on 3.3a and 3.7d and fails withb'xxx'whereb'x'is expected when adisplay-messagecall is put back into the probe. The pin moves to libtmuxf6e4630a, which carries the same probe and publishes the version check aslibtmux._internal.support. Hosted CI passes ata2b4b3afon the unsupported-versions job and 3.3a, 3.4, 3.5, 3.6, 3.7a, 3.8 and master (run).-Vand backend failures raiseLibTmuxException, so the tool boundary reports them as expected errors; a probe that fails with permission denied raises it on both lanes and aRuntimeErrorfails the test. An unsupported default endpoint logs a warning and startup continues, because tools check each endpoint per call; re-raising fails the lifespan test. Arun_commandwhose typed fence is refused after a daemon replacement now reports a version error instead of timing out; dropping the check fails its test.Local native evidence covers Linux/WSL. The earlier full run on a mounted-drive virtual environment exceeded its bound; the fresh locked Linux environment passes with the same source. Parent libtmux operations delegated to worker threads retain their existing cancellation behavior. Library-owned metadata/guard commands assume native built-in semantics; configured alias bodies and user shell programs retain native semantics. Optional feature boundaries are the separate stacked PR, while baseline polling stays on 3.3a.
Closes #137.