Typed operations, engines, workspace, query & MCP (#689)#690
Conversation
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## master #690 +/- ##
===========================================
+ Coverage 52.44% 79.76% +27.32%
===========================================
Files 26 234 +208
Lines 3726 15902 +12176
Branches 747 1981 +1234
===========================================
+ Hits 1954 12685 +10731
- Misses 1468 2505 +1037
- Partials 304 712 +408 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
why: Record the experimental operations/engines layer for the upcoming release so the unreleased section tracks what landed. what: - Add a "What's new" deliverable under the unreleased 0.59.x section for the experimental operations and engines layer (#690) - Defer the release lead paragraph until the version is cut
Code reviewFound 2 issues:
libtmux/src/libtmux/experimental/ops/plan.py Lines 81 to 97 in e115eaf
libtmux/src/libtmux/experimental/ops/_ops/save_buffer.py Lines 38 to 41 in e115eaf 🤖 Generated with Claude Code - If this code review was useful, please react with 👍. Otherwise, react with 👎. |
Code reviewFound 1 issue:
libtmux/src/libtmux/experimental/ops/plan.py Lines 214 to 219 in 2e0b112 🤖 Generated with Claude Code - If this code review was useful, please react with 👍. Otherwise, react with 👎. |
Code reviewNo issues found. Checked for bugs and CLAUDE.md compliance. 🤖 Generated with Claude Code |
1 similar comment
Code reviewNo issues found. Checked for bugs and CLAUDE.md compliance. 🤖 Generated with Claude Code |
why: Record the experimental operations/engines layer for the upcoming release so the unreleased section tracks what landed. what: - Add a "What's new" deliverable under the unreleased 0.59.x section for the experimental operations and engines layer (#690) - Defer the release lead paragraph until the version is cut
why: Record the experimental operations/engines layer for the upcoming release so the unreleased section tracks what landed. what: - Add a "What's new" deliverable under the unreleased 0.59.x section for the experimental operations and engines layer (#690) - Defer the release lead paragraph until the version is cut
why: Record the experimental operations/engines layer for the upcoming release so the unreleased section tracks what landed. what: - Add a "What's new" deliverable under the unreleased 0.59.x section for the experimental operations and engines layer (#690) - Defer the release lead paragraph until the version is cut
why: Record the experimental operations/engines layer for the upcoming release so the unreleased section tracks what landed. what: - Add a "What's new" deliverable under the unreleased 0.59.x section for the experimental operations and engines layer (#690) - Defer the release lead paragraph until the version is cut
Code reviewNo issues found. Fresh review of the current HEAD ( 🤖 Generated with Claude Code |
why: Record the experimental operations/engines layer for the upcoming release so the unreleased section tracks what landed. what: - Add a "What's new" deliverable under the unreleased 0.59.x section for the experimental operations and engines layer (#690) - Defer the release lead paragraph until the version is cut
why: Record the experimental operations/engines layer for the upcoming release so the unreleased section tracks what landed. what: - Add a "What's new" deliverable under the unreleased 0.59.x section for the experimental operations and engines layer (#690) - Defer the release lead paragraph until the version is cut
why: Record the experimental operations/engines layer for the upcoming release so the unreleased section tracks what landed. what: - Add a "What's new" deliverable under the unreleased 0.59.x section for the experimental operations and engines layer (#690) - Defer the release lead paragraph until the version is cut
why: A create rendered with `-P -F` tells tmux exactly how many ids to print, but nothing checked that they arrived. A short or empty line left the missing ids None and logged nothing, so the loss only surfaced later and elsewhere -- an unresolvable plan reference, or an AttributeError wrapping a None id. The id-parse path had no logging at all. what: - Check the capture invariant in build_result: warn when a create did not complete, and error when a complete create captured fewer ids than its format requested (which is what catches the partial case). - Warn in status_for when tmux exits 0 but writes to stderr, since the result is downgraded to failed on stderr text alone. - Use the documented tmux_* extra keys; heavy stdout/stderr stay DEBUG.
why: The ops engines had almost no logging, so a command that returned nothing looked identical to one that worked. Control mode was worse: a dropped or misattributed reply block is silent, and correlation desync could only be inferred after the fact. what: - Log each subprocess dispatch at DEBUG (argv, exit code, stdout, stderr), matching what libtmux.common already provides. - Add BlockSequenceMonitor: tmux's guard echoes a strictly increasing command number, so a solicited block whose number does not advance is provable desync -- one integer compare per block. - Warn on the silent control-mode paths: an unparseable %begin guard, a surplus solicited block, a drained solicited reply, and an async block arriving with no pending command. - Heavy stdout/stderr stay DEBUG behind isEnabledFor guards.
why: tmux reports a lost server on the client's stderr, not in an
%error block, so _read_stderr logged that text and dropped it. A caller
saw only how libtmux noticed ("tmux -C closed stdout") and never what
tmux said ("server exited unexpectedly"), leaving control-mode failures
strictly less diagnosable than subprocess ones.
what:
- Keep a bounded tail of the tmux process's stderr as it is read.
- Add _died() to build connection-death errors with that tail appended,
and raise through it from the exit and closed-stdout sites.
- Leave the message untouched when stderr had nothing to add.
why: Each cell killed its session between builds, dropping the server to zero sessions. Under tmux's exit-empty default the server then began exiting, and the next build's new-session could reach the still-bound socket mid-shutdown and fail with "server exited unexpectedly" -- an intermittent create failure whose rate rose with machine load. Control mode never hit it: its tmux -C phantom already pinned the server, which is why only subprocess cells failed. what: - Give every bench server a keepalive session, so a cell's teardown never drops it to zero. Setting exit-empty alone cannot work: an empty server exits before the option can land. - Offload the async cells' kill-session to a thread; a blocking subprocess issued from inside a running event loop measurably raised the failure rate.
why: _cleanup only knows this process's socket dir, so a run killed before its atexit hook leaves its scratch dir -- and any tmux still bound to it -- behind for good. Those survivors keep consuming CPU and descriptors, and machine load is exactly what makes the server-teardown race fire, so an unreaped leak feeds the failure it came from. what: - On import, remove any ltbench-* scratch dir with no live tmux bound to it; report how many were reclaimed. - Leave dirs with a live server alone -- they may belong to a concurrent run, and stealing another run's servers is worse than leaking a hung client until its server exits.
why: Driving the MCP server across agent CLIs surfaced footguns the swapper couldn't see -- the repo registered under a name other than the derived default, un-reverted swaps and orphaned backups piling up, a state entry whose backup had vanished, and API-key env vars silently overriding subscription auth. Isolated testing also needed a per-entry env (LIBTMUX_SOCKET) that use-local couldn't write. what: - Add read-only `doctor`: cross-CLI entry inventory, server-name mismatch hint, outstanding-swap and orphaned-backup health, missing backup detection, and auth-env override warnings - Add repeatable `use-local --env KEY=VALUE`, layered over preserved env - Add a naming hint on use-local when the repo is registered under another server name - Keep the new logic server-agnostic (no hardcoded slug); the derived default here is `libtmux-engine`, from the console-script entry - Tests for env injection, the naming hint, and doctor; CHANGES under Development
why: Unit tests prove the MCP server's internals but not that a real agent CLI can discover a tool, clear its approval gate, call it, and survive cancelling it mid-flight. Capture the end-to-end harness so it is reproducible across Claude / Codex / Cursor / Gemini / Grok / agy. what: - Add SKILL.md and references/cli-matrix.md under .agents/skills - Concretize the generalized template for libtmux: server slug `libtmux`, backend-isolation lever an isolated tmux socket (LIBTMUX_SOCKET -> `tmux -L <scratch>`) - Document the three fidelity layers, approval/cancellation traps, and the mcp_swap doctor/use-local --env wiring
why: Operation pages need semantic links and styled facts backed by the registry. what: - Add operation and catalog directives with lifecycle handling - Render gp-sphinx cards, badges, source links, and catalogs - Test serial, parallel, incremental, invalid, and inventory builds
why: Rerun-free documentation tests exposed waits that matched echoed input or captured before output reached the pane. what: - Synchronize controlled commands through tmux wait channels - Match exact runtime-produced lines for bounded polling - Keep MCP guidance aligned with event-backed waiting
why: Published examples need reproducible shell, home, and tmux state with reruns disabled. what: - Add per-example home and short tmux socket isolation - Make pytest the canonical documentation example runner - Test block isolation and author-facing failure locations
why: Give every experimental operation a stable, navigable API page with an executable example and explicit failure semantics. what: - Split experimental guidance into operations, engines, and plans - Add registry-backed Server, Session, Window, Pane, and Client pages - Test page coverage, navigation, rendering, and doctest execution
why: Activate gp-sphinx card styling and responsive layout rules for custom operation entries. what: - Wrap operation entries in the shared API card shell - Assert the rendered shell contract in the Sphinx integration test
why: Let users choose a query API by freshness, filtering boundary, and failure behavior instead of navigating one mixed topic. what: - Add hierarchy, QueryList, tmux-format, Neo, and snapshot guides - Test independent happy and sad examples plus styled API sections - Preserve traversal and filtering URLs with redirects
why: Keep the verified documentation extension and tests in the repository canonical format. what: - Apply ruff line wrapping to the operation domain and query tests
why: Operation cards flattened Python signatures into literal text, bypassing Sphinx Python-domain semantics and gp-sphinx API styling. what: - Render operation classes and constructors as native Python descriptions - Register public class and constructor inventory targets - Test RST and MyST rendering against semantic gp-sphinx markup
why: Wall-clock corrections can move backward and invalidate timeout measurements in retry_until and its tests. what: - Measure retry deadlines with the monotonic clock - Measure elapsed-time assertions with the same clock
why: Operation pages need real tmux proofs and API references that match the normal gp-sphinx presentation. what: - Replace mock operation examples with live typed outcomes - Add engine, tutorial, and result API references - Align operation cards, source links, parameters, and badges - Test documentation inventory, rendering, and tmux behavior
why: CI checks the documentation extension under a different package root and against Sphinx's installed domain annotations. what: - Use package-relative imports for both Sphinx and mypy discovery - Follow inherited Sphinx domain attribute typing - Inspect public constructor signatures without unsafe __init__ access
why: tmux releases disagree on whether list-clients includes a suspended client, making the documented output version-dependent. what: - Verify the real AckResult and empty command output - Preserve the portable session-survival assertion - Document client-list visibility as version-dependent
why: Deleting a control-owned session can race tmux's deferred global notifications and terminate supported tmux servers. what: - Attach control clients to safe existing sessions without updating the environment - Bootstrap empty or unsafe servers through tracked subprocess execution - Serialize lifecycle transitions without blocking dependent fallback commands - Cover live hook, concurrency, cancellation, and cross-version paths
why: CI type-checks against Python 3.10, where Task.cancelling is not available in the asyncio stubs. what: - Replace the version-specific cancellation probe with a portable task wait - Make the shared startup future's nullable type explicit - Align lifecycle test overrides and terminal paths with their contracts
why: Whole-file backups could be overwritten or unwound out of order, and malformed state could abort configuration recovery. what: - Preserve first backups and enforce per-config LIFO ordering - Checkpoint state before config writes and each successful revert - Fail closed on missing backups, corrupt state, and malformed configs - Cover repeat swaps, partial failures, and recovery diagnostics
why: Experimental operations, engines, and agent tools could diverge from their documented constructor, result, and command-boundary contracts. what: - Make Attributes the checked source for generated constructor API prose - Expose only tmux-supported targets across Python, MCP, and payloads - Preserve create results and tmux 3.7 composed-operation invariants - Gate executable MCP payloads across every executable tool path - Preserve literal arguments across every experimental tmux transport - Add the direct YAML dependency and tested failure guidance
why: Show how typed Python chains become folded tmux command sequences while one asynchronous control-mode client carries the work. what: - Add a tabbed live tutorial for forward references and planner folding - Replace simulated plan examples with real tmux execution - Test visible compiled commands against actual control-mode dispatches - Add API destinations and navigation for plan concepts
why: First-party record fields must render with complete semantic prose, while gp-sphinx a36 now owns documented-member deduplication. what: - Expand the runtime contract across supported record declarations - Define inheritance, ordering, and exemption rules in AGENTS.md - Remove the obsolete no-undoc-members workaround and source test
why: CI must type-check the untyped doctest dependency boundary and the heterogeneous results produced by a compiled operation plan. what: - Describe the consumed doctest finder interface with a protocol - Narrow the terminal plan result before reading message text
why: Operation's ten class variables were documented as a hand-written definition list under Notes, because an Attributes entry for a class variable used to be dropped from the build. gp-sphinx renders one now, with the annotation and value alongside the prose, so the workaround costs a reader the type and default it cannot state. what: - Move kind, command, scope, result_cls, chainable, primitive, safety, effects, min_version, and flag_version_map into the Attributes section, dropping the Notes list Requires a gp-sphinx release carrying the class-variable rendering; under the pinned 0.0.1a36 these descriptions do not render.
why: Engine users need transport-specific examples that expose real output and boundaries without treating mock results as server evidence. what: - Add one executable first-success example per concrete engine - Map every engine to a focused live or offline tutorial - Test tutorial ownership and transport boundaries
why: gp-sphinx removes tabs.js after rendering, leaving pages with a missing asset reference even though inline tabs operate through CSS. what: - Filter only tabs.js from Sphinx page contexts at late priority - Verify final tab markup and assets with a one-page Sphinx build
Summary
Adds the typed experimental stack under
libtmux.experimental: operations,engines, eager/lazy/async objects, plans, declarative workspaces, live pane
queries, and an optional MCP server.
The work is additive and remains outside libtmux's stable-versioning policy. It
implements the architecture from #688 and #689 without changing the existing
public ORM API.
What ships
Typed operations and results
Pane, and Client scopes.
underlying tmux command.
incomplete composed-operation failures.
semicolon-folded, and
{marked}dispatch with identical result semantics.share the same constructor-field contract.
Engines and command boundaries
SubprocessEngineAsyncSubprocessEngineMockEngineAsyncMockEnginetmux -C)ControlModeEngineAsyncControlModeEngineImsgEngineEvery engine returns the same
CommandResultshape. Direct argv, control-modelines, and imsg frames preserve literal semicolons, newlines, carriage returns,
and leading-dash positional values. Typed separator provenance prevents user
data from becoming a second tmux command.
Objects, queries, and workspaces
operation spine.
panes()provides lazy filtering, ordering, limiting, mapping, and foldedper-pane commands against either a live engine or snapshots.
Workspacevalues compile into inspectable plans, support YAML,environment and option propagation, host-step fold boundaries, workspace
sets, and tmux 3.7 floating panes.
result omits the identifier required to construct the next object.
MCP server
The optional
libtmux[mcp]extra provides curated tools, one tool peroperation, plan tools, workspace tools, resources, prompts, and output
monitoring. Safety is evaluated from the concrete payload across direct,
per-operation, plan, workspace, and event paths. Executable shell,
configuration, environment, option, and active-format payloads require the
destructive tier; target-aware tools also protect the caller's own tmux
hierarchy.
Documentation and developer experience
Pane, and Client, with one generated API page per operation.
QueryList, tmux-format, Neo, and snapshotguides with API references, examples, happy paths, and failure paths.
MockEngineisreserved for explicitly offline engine/testing documentation.
Attributessections,and operation badges/cross-links use the normal gp-sphinx API presentation.
verifies the operation registry cannot drift from its pages.
Verification
uv run ruff check . --fix --show-fixesuv run ruff format .uv run ty checkuv run mypyuv run py.test --reruns 0 -vvv: 3,104 passed, 1 skippedjust build-docs: 141 pages built successfullycontrol-mode, async control-mode, and imsg transports.
Refs #688, #689.