Skip to content

Experimental: pure-chain ChainWorkspaceBuilder + --builder=chain#1054

Open
tony wants to merge 3 commits into
masterfrom
chainable-commands-experiment-00
Open

Experimental: pure-chain ChainWorkspaceBuilder + --builder=chain#1054
tony wants to merge 3 commits into
masterfrom
chainable-commands-experiment-00

Conversation

@tony

@tony tony commented Jun 20, 2026

Copy link
Copy Markdown
Member

Summary

  • Add a ChainWorkspaceBuilder that constructs a workspace's entire session β†’ window β†’ pane tree from a single libtmux._experimental.chain.ForwardPlan, resolved over the minimum number of tmux invocations instead of one subprocess per new-window / split-window.
  • Add an opt-in --builder=chain flag to tmuxp load (Env: TMUXP_BUILDER); the default builder is unchanged.
  • Experimental: depends on libtmux's _experimental.chain API, pinned to a sibling worktree via [tool.uv.sources]. Not for merge β€” companion to the libtmux experiment.

Changes by area

src/tmuxp/workspace/chain_builder.py (new)

ChainWorkspaceBuilder, a subclass of WorkspaceBuilder, overriding build(): the session is created, then every window and pane is described in one ForwardPlan seeded from the session and resolved via ServerPlanRunner. Reuses the parent's helpers (_wait_for_pane_ready, config_after_window, focus handling, plugin hooks, before_script, session options).

src/tmuxp/cli/load.py

Adds --builder={default,chain} (Env TMUXP_BUILDER), threaded through command_load β†’ load_workspace as builder_name, selecting the builder class. The rest of the load path is unchanged because the subclass mirrors the constructor, .session, and event hooks.

pyproject.toml

[tool.uv.sources] pins libtmux to the sibling ../libtmux worktree so the experimental chain API is importable.

Design decisions

  • Reuse the default window, don't orphan it. Seeding with ForwardPlan.from_session(session) and addressing window 1 through seed.initial_window / seed.initial_pane builds onto the session's default window instead of moving it aside and killing it (the move-to-99 + kill dance the classic builder performs).
  • send_keys_mode, default readiness. The chain batches split + send-keys without waiting for the new pane's shell prompt. readiness builds only the structure via the chain, then sends each pane's commands shell-ready (matching the classic builder, deterministic). batched folds send-keys into the plan for the fullest "all at once" β€” faster, but it can race an interactive prompt.
  • Sync, not async. tmuxp has no async today and a workspace build is causally serial (windows depend on the session; decorations depend on captured ids), so the chain's synchronous path (ServerPlanRunner / run_resolving) is the right fit.
  • Subclass, not fork. Extending WorkspaceBuilder keeps .session, find_current_attached_session, the on_* event hooks, and the plugin lifecycle identical, so load.py needs only to pick the class.

Test plan

  • Lint + format clean on the new builder, tests, and the CLI change
  • Type-check (mypy) clean
  • tests/workspace/test_chain_builder.py β€” two/three-pane structure, window + pane focus, readiness-mode commands landing, batched mode, and --builder flag parsing
  • Existing tests/workspace/test_builder.py and tests/cli/test_load.py still pass (the subclass leaves the default builder untouched)
  • Smoke: tmuxp load -d --builder=chain tests/fixtures/workspace/builder/two_pane.yaml builds editor (2 panes), logging, test, matching the default builder

Companion PR

Depends on the libtmux experimental chain API: tmux-python/libtmux#685.

tony added 3 commits June 20, 2026 11:19
why: Build the experimental ChainWorkspaceBuilder against the in-progress
libtmux._experimental.chain API on the sibling libtmux worktree.
what:
- Add [tool.uv.sources] libtmux = { path = "../libtmux", editable = true }
- Relock against the local editable checkout
why: Showcase libtmux's experimental chain API downstream: build a whole
workspace's window/pane tree in the minimum tmux invocations instead of
one subprocess per new-window / split-window.

what:
- Add ChainWorkspaceBuilder (subclass of WorkspaceBuilder): one
  ForwardPlan seeded from the session builds every window and pane,
  reusing the session's default window as window 1 (no orphan) via
  initial_window / initial_pane
- send_keys_mode: readiness (default, classic per-pane shell-ready
  delivery) or batched (folded into the plan)
- Reuse parent helpers (_wait_for_pane_ready, config_after_window, focus,
  plugins, before_script, options)
- Tests: two/three-pane structure, focus, readiness commands land, batched
why: Make the experimental ChainWorkspaceBuilder selectable from the CLI
without replacing the default builder.

what:
- Add --builder={default,chain} to `tmuxp load` (Env: TMUXP_BUILDER),
  threaded through command_load -> load_workspace as builder_name
- Select ChainWorkspaceBuilder when chosen; default builder otherwise
- Test the flag parsing (smoke-verified `tmuxp load --builder=chain`
  builds the expected window/pane tree)
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