Skip to content

Lifecycle(feat): Add server defaults and owned scopes - #771

Draft
tony wants to merge 10 commits into
masterfrom
lifecycle-mvp
Draft

tony wants to merge 10 commits into
masterfrom
lifecycle-mvp

Conversation

@tony

@tony tony commented Oct 10, 2026 •

Copy link
Copy Markdown
Member

Ordinary imported examples can call Server().ensure_running() against their normal tmux endpoint, whether a daemon already exists or needs to start. The same program runs in tests with socket defaults supplied through LIBTMUX_SOCKET_PATH or LIBTMUX_SOCKET_NAME; the external harness owns isolation and final cleanup. The README and Sphinx quickstart show the complete examples/workspace.py program, which creates or reuses a session and window and leaves them available.

ensure_running() returns the same ordinary server handle without requiring a cleanup owner. It preserves an existing daemon's sessions, options and environment. A new daemon remains available before session creation, and startup adds no bootstrap session. Normal tmux configuration can create its own sessions. Each server captures its endpoint, executable and child environment at construction, so later environment changes do not redirect its commands.

Ownership, discovery and find or create

resource.own() accepts destruction responsibility for an existing server, session, window or pane. Cleanup retains the accepted daemon generation and object ID, checks that generation inside tmux's destructive dispatch, preserves body and cleanup failures, and permits retry. Session, window and pane contexts use this behavior. The existing session-cleanup example remains a separate demonstration.

Server.discover() performs bounded, non-starting probes of selected socket directories and returns borrowed servers with diagnostics and truncation status. Find-or-create methods return FoundOrCreated: its .value is the ordinary object, while entering the result as a scope requests cleanup of a proven creation. Reused matches remain borrowed. Session/window names and pane-local keys match literal values; ambiguity raises. Child lookup and creation use separate commands, so callers requiring uniqueness must serialize competing creators.

Creation records receipts before decoding snapshots. Failed readback, context entry, name checks or generation checks roll back the recorded resource against its creating daemon. Nonzero client status, timeout and interruption preserve readable receipts. Failed rollback exposes a retry owner through CreationCleanupError; missing or invalid receipts report UnknownCreation. Server startup uses a per-call proof to distinguish creation from reuse. Receipts and discovery probes retain printable framing under non-UTF-8 tmux client locales.

Compatibility and documentation

Python 3.10 remains the minimum. Paired failures use the conditional exceptiongroup dependency below Python 3.11. Existing conditional typing imports, classifiers, lint/type targets and tmux feature gates remain.

All 866 original docstring examples retain source, expected output, exception text and flags. All 427 original library docstrings remain: 416 are identical and eleven contain documented lifecycle clarifications. The ensure-server addition preserves all 516 incoming documentation-string expressions and 1,541 incoming doctest examples. The original _tmux_bin helper and its documentation remain intact.

Behavior changes to review: a plain Server context leaves the daemon alive; whole-daemon destruction requires explicit ownership. Explicit socket paths must be absolute, ambiguous explicit selectors raise, and tmux_bin is a read-only captured path. Creation and adoption reserve @libtmux_owner_generation; a malformed existing value fails ownership acceptance.

Validation

  • The ensure-server and ordinary-example stage passes 80 focused cases in each Python 3.10.19/3.12.3 × tmux 3.2a/3.7d combination. Fresh independent review repeats both endpoint-version lanes and checks configured sessions, existing user state, handle collection, the default socket and twelve concurrent startup processes.
  • The exact ordinary program passes 28 runs covering tmux 3.2a, 3.3a, 3.4, 3.5, 3.6, 3.7 and 3.7d, both initial daemon states and both environment selectors. Independent review verifies those retained receipts and binary identities. Parallel isolation has separate execution evidence.
  • All 51 preserved README, quickstart and context-manager doctest blocks pass at the Python 3.10 floor. An offline Sphinx build with warnings as errors verifies the rendered ordinary source. Ruff, mypy and whitespace checks pass.
  • Independent lifecycle reviews cover generation checks, adoption of all four object kinds, receipt loss, rollback failure and retry, timeout/interruption, locale framing and compatibility preservation. Deliberate startup, state-preservation, cleanup and displayed-source faults fail their checks.
  • The latest Python review records 1,256 accepted process exits before removing its 14 private roots. These records include clients and workers as well as daemons. Earlier evidence retains its original verification limits.

Remaining work

Keep this PR in draft. The legacy Unicode snapshot parser retains its non-UTF-8 limitation; the repaired receipt protocol does not establish every object API under the C locale. Creation deadlines apply per command or identity step, and snapshot queries retain their existing timeout behavior.

The full library suite, installed-package validation for this revision, every supported tmux patch release and the nine-dimension acceptance assessment remain pending. Plain Markdown, Astro Markdown/MDX, Sphinx reStructuredText/MyST and Python doctest remain in the shared integration scope; the native source-binding adapters are under separate review. The external runner is tracked in libtmux/docs#138. No merge or package release is included.

Continues the work and discussion in #769 on lifecycle-mvp.

tony added 9 commits October 9, 2026 17:18
why: Pane capture can finish before the shell executes a sent command.
The environment and capture tests must observe the expected output.

what:
- Wait for the initial prompt and exact command output
- Bound polling instead of relying on an immediate read or fixed sleep
why: Examples need ordinary tmux defaults and external test isolation.
Cleanup must retain its accepted target and expose failures for retry.

what:
- Capture endpoint, executable and child environment when constructing
  a Server, with explicit selectors ahead of environment defaults
- Add generation-bound ownership for servers, sessions, windows and
  panes, including bounded cleanup and paired body/cleanup errors
- Keep plain Server contexts borrowed and use owners for object scopes
- Test unchanged examples under external socket defaults and document
  the Python 3.11 floor, changed defaults and remaining lifecycle work
why: Creation could return a tmux ID and then fail during snapshot
decoding or context entry, leaving the resource behind. A later daemon
at the same endpoint could also be accepted as the original creation.

what:
- Retain daemon and object receipts on the creating connection
- Roll back failed materialization against the recorded generation
- Preserve receipt bytes across client timeout and interruption
- Expose failed rollback owners without replacing the original error
- Refuse replacement daemons before returning or entering the scope
why: The lifecycle changes do not require raising the Python floor or
removing the reference detail and cleanup examples users already have.

what:
- Restore Python 3.10 metadata, typing fallbacks and lock branches
- Use conditional exception-group support and compatible error notes
- Restore context-method reference sections and cleanup doctests
- Keep unrelated typing and lint cleanup outside the lifecycle diff
- Verify examples without requiring pidfd support from the interpreter
why: A descendant retaining an output pipe makes the post-timeout
communicate call wait beyond the command deadline. Receipt capture must
still finish before creation rollback handles an interruption.

what:
- Give draining and reaping finite cleanup intervals
- Let the receipt worker observe main-thread interruption between reads
- Retain partial output, text decoding and paired failures
- Test inherited pipes and real creation rollback on Python 3.10 and 3.12
why: A pipe-reader close failure can replace an interruption and skip
client reaping. A close failure inside communicate can also discard
output already captured before the timeout.

what:
- Retain the latest captured output across drain and close failures
- Close the remaining reader and attempt the bounded client wait
- Preserve the original failure alongside each cleanup failure
- Exercise both readers with timeout, interruption and inherited pipes
why: A close error during the final subprocess drain can discard a
receipt received after the preceding timeout snapshot, preventing
rollback of a known creation.

what:
- Retain bytes with a selector reader before closing output streams
- Keep bounded cancellation cleanup, grouped errors and client reaping
- Exercise late output with both reader errors, timeout and Ctrl-C
why: Examples need to reuse matching tmux resources while cleaning up
only the objects they create. Server discovery must be bounded and
must not change the daemons it finds.

what:
- Add created-or-reused results for servers, sessions, windows and panes
- Bind creation receipts and rollback to the accepted daemon identity
- Add bounded socket discovery with explicit diagnostics
- Use printable receipt fields under non-UTF-8 tmux client locales
- Preserve Python 3.10, existing docstrings and doctest expectations
why: Captured client configuration does not require removing the existing
private hook accessor or its documentation.

what:
- Restore the original helper and docstring verbatim
- Keep its name separate from the captured executable backing field
- Preserve endpoint defaults, capability checks and Python 3.10 support
@codecov

codecov Bot commented Oct 10, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 69.24644% with 302 lines in your changes missing coverage. Please review.
✅ Project coverage is 55.38%. Comparing base (500f99b) to head (1713557).

Files with missing lines Patch % Lines
src/libtmux/lifecycle.py 70.96% 105 Missing and 21 partials ⚠️
src/libtmux/server.py 54.92% 40 Missing and 24 partials ⚠️
src/libtmux/pane.py 38.00% 5 Missing and 26 partials ⚠️
src/libtmux/discovery.py 75.43% 25 Missing and 3 partials ⚠️
src/libtmux/common.py 84.84% 16 Missing and 4 partials ⚠️
src/libtmux/session.py 60.71% 4 Missing and 7 partials ⚠️
src/libtmux/pytest_plugin.py 70.00% 5 Missing and 1 partial ⚠️
src/libtmux/window.py 45.45% 3 Missing and 3 partials ⚠️
src/libtmux/hooks.py 0.00% 1 Missing and 4 partials ⚠️
src/libtmux/_internal/env.py 92.30% 2 Missing ⚠️
... and 2 more
Additional details and impacted files
@@            Coverage Diff             @@
##           master     #771      +/-   ##
==========================================
+ Coverage   52.37%   55.38%   +3.00%     
==========================================
  Files          26       28       +2     
  Lines        3729     4478     +749     
  Branches      747      861     +114     
==========================================
+ Hits         1953     2480     +527     
- Misses       1472     1655     +183     
- Partials      304      343      +39     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

why: Copied examples should work with an existing daemon or start one
through the public API, without requiring cleanup ownership.

what:
- Return the ordinary server after verified startup or reuse
- Add an imported workspace example that leaves its resources available
- Test absent/running endpoints, preserved state and startup failures
- Keep lifecycle examples and existing compatibility documentation
@tony tony changed the title Lifecycle(feat): Add owned tmux resource scopes Lifecycle(feat): Add server defaults and owned scopes Oct 10, 2026

This branch has not been deployed

No deployments
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.

1 participant