Skip to content

Pluggable workspace builders and configurable pane readiness#1066

Merged
tony merged 20 commits into
masterfrom
pluggable-workspace-builder
Jun 28, 2026
Merged

Pluggable workspace builders and configurable pane readiness#1066
tony merged 20 commits into
masterfrom
pluggable-workspace-builder

Conversation

@tony

@tony tony commented Jun 28, 2026

Copy link
Copy Markdown
Member

Summary

  • Add pluggable workspace builders — a workspace file can select a builder other than the built-in one via workspace_builder (a registered entry-point name or a Python import path). workspace_builder_paths lists trusted directories temporarily added to sys.path to import out-of-environment builders, without the broader code-executing behavior of site-directory processing. The built-in builder stays the default, so existing workspaces are unaffected.
  • Add configurable pane readiness via a new workspace_builder_options catalog (pane_readiness: auto | always | never, plus truthy/falsy aliases). The wait guards against a zsh prompt-redraw artifact, so the new auto default waits only when the session shell is zsh — bash, sh, and others skip a wait they never needed.
  • Introduce a public builder API: WorkspaceBuilderProtocol (shaped to allow async builders later), the built-in ClassicWorkspaceBuilder (with WorkspaceBuilder kept as a backwards-compatible alias), a registry resolver, and a WorkspaceBuilderOptions catalog.
  • Improve tmuxp load error reporting for builder-resolution failures with styled, actionable messages.

Changes by area

  • src/tmuxp/workspace/builder/: the former builder.py is now a package — protocol.py (WorkspaceBuilderProtocol), classic.py (ClassicWorkspaceBuilder), registry.py (builder resolution + trusted-path handling), and __init__.py (re-exports and the WorkspaceBuilder alias).
  • src/tmuxp/workspace/options.py: WorkspaceBuilderOptions / PaneReadiness catalog parsing and shell detection.
  • src/tmuxp/exc.py: WorkspaceBuilderError hierarchy for resolution failures.
  • src/tmuxp/cli/load.py: routes load through the builder resolver and surfaces friendly errors.
  • docs/: a custom-workspace-builders guide, a workspace-builders config reference, and a builder API page grid.
  • tests/: registry, options, pane-readiness, and load coverage, plus builder fixtures.

Test plan

  • uv run py.test (incl. doctests)
  • uv run mypy (strict)
  • uv run ruff check . and ruff format --check .
  • docs build introduces no new warnings versus master
tony added 18 commits June 28, 2026 17:01
why: Resolving a workspace builder by dotted path, entry point, or
trusted import path needs actionable errors that do not leak
machine-specific paths.

what:
- Add WorkspaceBuilderError base plus WorkspaceBuilderNotFound,
  WorkspaceBuilderImportError, InvalidWorkspaceBuilder, and
  WorkspaceBuilderPathError under WorkspaceError
why: Builder behavior (starting with pane readiness) needs a config
home separate from tmux's options/global_options/environment catalogs,
and extensible for future builders.

what:
- Add workspace/options.py with the PaneReadiness policy
  (auto/always/never plus truthy/falsy aliases),
  WorkspaceBuilderOptions, and zsh-aware shell detection helpers
- Add unit tests for parsing and shell resolution
why: Separate builder selection from build behavior. Make the classic
builder explicit so a workspace can later choose a different builder,
and define a public contract third-party builders implement.

what:
- Convert workspace/builder.py into a package; rename WorkspaceBuilder
  to ClassicWorkspaceBuilder in builder/classic.py, keeping a
  backwards-compatible WorkspaceBuilder alias
- Add builder/protocol.py (WorkspaceBuilderProtocol, async-extensible)
- Gate pane readiness on WorkspaceBuilderOptions: auto waits only for
  zsh, always/never force the choice
- Point doctest tmux fixtures at builder.classic
why: A workspace selects a builder by name or import path, and a
builder may live outside tmuxp's environment. Resolution and trusted
imports belong in one place, separate from the builder itself.

what:
- Add builder/registry.py: resolve_builder_class (module:attr, dotted
  path, or tmuxp.workspace_builders entry point), resolve_builder_paths,
  available_builders, and a prepended_sys_path sandbox (no
  site.addsitedir)
- Export the registry surface from the builder package
why: Let the classic builder be selected by the short name `classic`,
and seed the tmuxp.workspace_builders entry-point group that
third-party builders register under.

what:
- Register classic = ClassicWorkspaceBuilder under the
  tmuxp.workspace_builders entry points
why: Cover builder resolution and the pane-readiness policy, including
custom builders resolved by entry-point name, module:attr, and import
path.

what:
- Add tests/workspace/test_builder_registry.py and readiness cases in
  test_builder.py
- Add tests/fixtures/workspace_builders (valid and invalid custom
  builders)
why: tmuxp load must honor the workspace_builder selection and trusted
import paths instead of always constructing the classic builder.

what:
- Resolve the builder class via resolve_builder_class and construct it
  inside a prepended_sys_path sandbox built from resolve_builder_paths,
  keeping trusted paths active for both import and build
- Type the CLI build helpers against WorkspaceBuilderProtocol
why: The pluggable builder, readiness policy, and new modules need a
user guide and API reference entries.

what:
- Add topics/custom-workspace-builders.md covering selection, trusted
  paths, the builder contract, and readiness tuning
- Add options autodoc; document the builder package (classic,
  protocol, registry) and refresh workspace API toctrees
- Update the stale WorkspaceBuilder cross-reference
why: Record the user-facing 1.72.0 changes for the upcoming release
notes.

what:
- Document pluggable workspace builders: dotted-path / entry-point
  selection, trusted import paths, and the builder protocol
- Document the configurable pane_readiness policy and its zsh-only
  auto default
- Note the custom workspace builder guide
why: An invalid workspace_builder key raised a builder-resolution
exception that propagated as a raw traceback, leaking the local stack
and diverging from the styled handling load_plugins already gives an
unimportable plugin.

what:
- Catch exc.WorkspaceBuilderError in load_workspace and emit a styled
  [Builder Error] message + sys.exit(1), mirroring the plugin path
- Move resolve_builder_paths into the try so a bad
  workspace_builder_paths entry is handled too
- Add a parametrized test covering unknown name, import error, invalid
  object, and missing trusted-path cases
why: Structured log extra keys must carry the tmux_ prefix per the
project logging standards so downstream aggregators can filter them
consistently.

what:
- Rename the pane-readiness debug extra keys to tmux_pane_readiness
  and tmux_pane_readiness_wait
why: Two comments introduced on this branch were imprecise: the
readiness comment named only options.default-shell (global_options can
set it too), and the registry module docstring referenced the
backwards-compat alias rather than the canonical class.

what:
- Reword the readiness comment to "its options (including default-shell)"
- Point the registry docstring at ClassicWorkspaceBuilder
why: Auto pane-readiness mis-detected the shell when default-shell was
set globally (e.g. `set -g default-shell` in tmux.conf) rather than at
the session level: tmux returned None for the session-level lookup and
resolution fell back to $SHELL, so a global zsh default could be read
as a non-zsh shell and skip the prompt wait.

what:
- Pass include_inherited=True to show_option so an inherited
  default-shell is resolved before the $SHELL fallback
- Add a regression test with a session that exposes default-shell only
  via include_inherited
why: load_workspace always constructs the builder with plugins=..., so
a custom builder whose __init__ accepts only session_config and server
passed validation and then raised a raw TypeError at instantiation,
bypassing the styled [Builder Error] path.

what:
- Require plugins (alongside session_config and server, or **kwargs) in
  _validate_builder so an incompatible constructor is rejected as
  InvalidWorkspaceBuilder before instantiation
- Add a MissingPluginsBuilder fixture and a test
why: An invalid workspace_builder_options value (e.g. a typo'd
pane_readiness: sometimes, or a non-mapping catalog) raised a bare
ValueError/TypeError while constructing the default builder, which
escaped load_workspace's WorkspaceBuilderError handler and surfaced as
a raw traceback.

what:
- Add exc.InvalidWorkspaceBuilderOption (a WorkspaceBuilderError) and
  raise it from WorkspaceBuilderOptions.from_config for a non-mapping
  catalog or an invalid pane_readiness, so the error exits through the
  styled [Builder Error] path; PaneReadiness.from_config keeps its
  plain ValueError for the pure-parser unit/doctest
- Add unit tests plus a load case covering the typo'd pane_readiness
why: The 1.72.0 entry mixed user-facing capability with the builder
API surface in What's new. Separate the two so the release lead reads
as an upgrade decision and builder authors get one linked API entry.

what:
- Trim the What's new entries to high-level user prose (selection,
  trusted paths, the pane_readiness policy and its zsh-only default)
- Add a Development entry linking the builder package, protocol,
  registry resolvers, options/PaneReadiness, and error hierarchy
- Fold the guide cross-reference into both sections
why: The workspace_builder, workspace_builder_paths, and
workspace_builder_options keys were documented only in the Topics
guide. A reader browsing the Configuration section could not discover
them, and top-level.md was a stub that dead-ended the "Top-level
Options" card.

what:
- Add configuration/workspace-builders.md: a config-section reference
  with a summary table, per-key sections, the pane_readiness values,
  and a minimal YAML/JSON example; volatile detail (full alias list,
  trust-boundary rationale) stays single-sourced in the topics guide
- Surface it via a "Workspace builders" card in both index grids and
  the configuration toctree
- Bridge the top-level.md stub and add a reverse seealso from the
  topics guide
why: The builder API page stacked the classic, protocol, and registry
modules on one long page. As a package, builder reads better as its own
section with a landing grid, matching the cli/ and workspace/ package
layout.

what:
- Split internals/api/workspace/builder.md into a builder/ package doc:
  an index.md landing with a card grid + toctree over classic, protocol,
  and registry sub-pages, one automodule each
- Point the workspace toctree at builder/index
- Repoint the legacy api/workspace/builder redirect at builder/index
@tony tony force-pushed the pluggable-workspace-builder branch from b084dac to 4d58e48 Compare June 28, 2026 23:13
@codecov

codecov Bot commented Jun 28, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 90.04329% with 23 lines in your changes missing coverage. Please review.
✅ Project coverage is 82.56%. Comparing base (4b28ce1) to head (389a178).

Files with missing lines Patch % Lines
src/tmuxp/workspace/builder/registry.py 84.00% 11 Missing and 5 partials ⚠️
src/tmuxp/workspace/builder/protocol.py 73.68% 5 Missing ⚠️
src/tmuxp/exc.py 93.54% 1 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #1066      +/-   ##
==========================================
+ Coverage   81.98%   82.56%   +0.57%     
==========================================
  Files          28       31       +3     
  Lines        2548     2770     +222     
  Branches      485      518      +33     
==========================================
+ Hits         2089     2287     +198     
- Misses        328      346      +18     
- Partials      131      137       +6     

☔ 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.
tony added 2 commits June 28, 2026 18:32
why: AGENTS.md reserves ### Development for dev tooling (helper scripts,
internal automation). The workspace builder API is a public extension
surface for builder authors, so it belongs under ### What's new.

what:
- Drop the ### Development heading so "Workspace builder API" sits as a
  third deliverable under ### What's new
why: CLAUDE.md requires working doctests on all functions and methods;
the new workspace-builder exception classes added in this branch had
none.

what:
- Add a concise print()-based doctest to each new builder exception
  (WorkspaceBuilderNotFound, WorkspaceBuilderImportError,
  InvalidWorkspaceBuilder, WorkspaceBuilderPathError,
  InvalidWorkspaceBuilderOption) covering message construction
@tony tony merged commit 51d7e02 into master Jun 28, 2026
14 checks passed
tony added a commit that referenced this pull request Jun 28, 2026
Minor release making the workspace build step pluggable and tunable.
A workspace can build through a third-party builder selected by
entry-point name or import path, and a new workspace_builder_options
catalog controls the pane-readiness wait per workspace (#1066).

Behavior change: the new pane_readiness=auto default skips the pane
prompt wait on non-zsh shells; set pane_readiness: always to restore
the previous wait-everywhere behavior.
@tony tony deleted the pluggable-workspace-builder branch June 29, 2026 00:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

1 participant