Skip to content

docs: tmuxinator/teamocil parity analysis and feature comparison#1014

Draft
tony wants to merge 69 commits intomasterfrom
tmuxinator-parity
Draft

docs: tmuxinator/teamocil parity analysis and feature comparison#1014
tony wants to merge 69 commits intomasterfrom
tmuxinator-parity

Conversation

@tony
Copy link
Copy Markdown
Member

@tony tony commented Feb 8, 2026

Summary

  • Add docs/comparison.md — comprehensive feature comparison table (tmuxp vs tmuxinator vs teamocil) covering architecture, config keys, CLI commands, hooks, and config file discovery
  • Add notes/parity-tmuxinator.md — identifies 12 feature gaps, import bugs, and WorkspaceBuilder requirements for tmuxinator parity
  • Add notes/parity-teamocil.md — documents v0.x/v1.x format differences, 3 feature gaps, and import bugs
  • Add notes/import-tmuxinator.md — classifies each tmuxinator config key as translatable difference or tmuxp limitation, identifies pre/pre_window bug
  • Add notes/import-teamocil.md — documents v0.x-only importer targeting (v1.x unsupported), string pane TypeError bug, redundant filter loops
  • Update notes/plan.md — mark L1/L2/L3 as resolved (libtmux v0.55.0), fix 20+ stale line number references, reorganize phases to reflect unblocked items (T2, T9)

Key findings

Importer bugs discovered:

  • import_tmuxinator: when both pre and pre_window exist, writes to shell_command (not a valid tmuxp session key) — pre commands silently lost
  • import_teamocil: targets v0.x format only; v1.x string panes cause TypeError; redundant for _b in loops in filter handling

tmuxinator feature gaps (12):
Lifecycle hooks (5 hooks vs tmuxp's 1), pane synchronization, pane titles, ERB templating, wemux support, stop/kill command, debug/dry-run, config management CLIs, --no-pre-window flag

teamocil feature gaps (3):
--here flag (reuse current window), --debug dry-run mode, shell_command_after

tmuxp advantages (unique features):
Per-pane environment/shell/sleep/history control, plugin system, JSON config, session freeze, workspace search, upward config traversal

Test plan

  • All documents created and committed individually
  • Verify importer bug descriptions match importers.py line numbers
  • Review accuracy of feature mapping tables against source code

@codecov
Copy link
Copy Markdown

codecov Bot commented Feb 8, 2026

Codecov Report

❌ Patch coverage is 87.24832% with 19 lines in your changes missing coverage. Please review.
✅ Project coverage is 82.34%. Comparing base (281eb9a) to head (be5ce33).

Files with missing lines Patch % Lines
src/tmuxp/workspace/importers.py 87.24% 14 Missing and 5 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #1014      +/-   ##
==========================================
+ Coverage   81.96%   82.34%   +0.37%     
==========================================
  Files          28       28              
  Lines        2545     2662     +117     
  Branches      485      520      +35     
==========================================
+ Hits         2086     2192     +106     
- Misses        328      339      +11     
  Partials      131      131              

☔ View full report in Codecov by Sentry.
📢 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 tony force-pushed the tmuxinator-parity branch from 304ff40 to 8e9425c Compare February 8, 2026 13:37
@tony tony force-pushed the tmuxinator-parity branch 5 times, most recently from 19b04a3 to 1f937ad Compare March 9, 2026 01:33
@tony tony force-pushed the tmuxinator-parity branch 5 times, most recently from bb72be2 to ca74e75 Compare March 21, 2026 10:53
@tony tony force-pushed the tmuxinator-parity branch from ca74e75 to b446e6b Compare April 26, 2026 10:25
@tony tony force-pushed the tmuxinator-parity branch from b446e6b to 2d03c24 Compare May 9, 2026 10:56
tony added 16 commits May 9, 2026 07:10
…amocil

Comprehensive side-by-side comparison covering architecture, config keys,
CLI commands, hooks, and config file discovery across all three tools.
Documents 12 feature gaps (hooks, stop command, pane sync, pane titles,
ERB templating, wemux, debug/dry-run, config management CLIs), import
behavior with bug inventory, and WorkspaceBuilder requirements.
Documents v0.x vs v1.x format differences, 3 feature gaps (--here flag,
debug mode, shell_command_after), import bugs (v1.x incompatibility,
redundant filter loops), and WorkspaceBuilder requirements.
Classifies each config key as difference (translatable) or limitation
(needs tmuxp feature). Identifies pre/pre_window bug, missing rvm/pre_tab
mappings, and 5 features requiring new tmuxp capabilities.
Documents v0.x-only targeting (v1.x unsupported), string pane TypeError
bug, redundant filter loop bug, and 6 missing v1.x key mappings
(commands, focus, options, string shorthand).
- Remove duplicate 'Attach on create' row in comparison table, keep
  corrected version with '(default: true)' near socket_path
- Annotate pre_tab as (deprecated) in comparison table
- Annotate startup_window as accepting name or index
- Fix pre_tab description: deprecated predecessor, not alias (it was
  renamed in tmuxinator, not aliased)
- Clarify startup_window renders as "#{name}:#{value}"
- tmuxinator min tmux is 1.8 (recommended), not 1.5; tmux 2.5 is
  explicitly unsupported
- teamocil has no documented min tmux version
- tmuxinator detach is via `attach: false` config or `--no-attach`
  CLI flag, not `-d` (which doesn't exist in tmuxinator)
- Add socket_path as item 16 (tmuxinator config key not handled)
- socket_path takes precedence over socket_name in tmuxinator
- tmuxp only accepts socket path via CLI -S flag
- Add to summary table as missing Difference
- with_env_var is an import-only fix (tmuxp already has environment
  key), not a Limitation — moved to new "Import-Only Fixes" section
- cmd_separator is irrelevant (tmuxp sends commands individually),
  clarified it needs no import
- Fix "1.5+" to "1.8+" in architecture description (was already
  fixed in overview table but missed in prose)
- Clarify YAML anchors: tmuxinator enables via YAML.safe_load
  aliases param, not a config key
- Clarify tmuxinator edit is alias of new command
…s equivalent

tmuxp doesn't have startup_window/startup_pane keys but achieves the
same result via focus: true on individual windows/panes. Add cross-reference
annotation so users aren't misled by (none).
- before_script maps to on_project_first_start (runs only when session
  doesn't exist), not on_project_start (runs every invocation)
- Add teamocil --here implementation details: sends cd via send-keys,
  decrements window count for index calculation
- import-teamocil.md: Code block comment said "Lines 144-149" but the
  `if "filters"` guard is on line 143, so range is 143-149
- parity-teamocil.md: Referenced "Line 142" for `clear` handling but
  actual code is lines 140-141 (line 142 is blank)
…, expand CLI table

- Fix min tmux: 1.5+ (not "1.8 recommended; not 2.5"), per tmux_version.rb
- Note teamocil renames session (rename-session) rather than creating new
- Add teamocil auto-generated session name detail
- Expand pre_window to show full deprecation chain (rbenv/rvm/pre_tab)
- Add synchronize values (true/before/after)
- Add --suppress-tmux-version-warning to CLI table
- Split deprecated pre/post into separate rows with hook mappings
- Fix tmuxp --append flag syntax
- Fix pane focus to note startup_pane equivalent
… chain, remove --here

- Fix startup_window: accepts name OR index (not just name)
- Document pre_window fallback chain: rbenv → rvm → pre_tab → pre_window
- Remove section 12 (--here) — this is a teamocil feature, not tmuxinator
- Renumber section 13 → 12
- Clarify freeze vs tmuxinator new comparison
- Add rvm source reference (project.rb:181)
- Add tmuxinator version range to header
tony added 11 commits May 9, 2026 07:14
…ore, multi-file load

why: Source code analysis revealed details not captured in comparison table.
what:
- Note tmuxinator synchronize true/before deprecated in favor of after
- Add pane-level shell_command_before row (tmuxp-unique feature)
- Add multi-file loading CLI row (tmuxp load f1 f2)
…ormat default

why: tmuxinator source confirms synchronize true/before is deprecated.
what:
- Note synchronize true/before deprecated in project.rb:21-29
- Add context that import should still honor original semantics per value
- Document pane_title_format default and pane_title_position default
why: teamocil agent confirmed v1.0 explicitly dropped v0.x features.
what:
- Add paragraph explaining v1.0 rewrite dropped hooks, env vars, DSL
- Provides context for why v0.x import has more keys than v1.x
why: Deprecation context helps prioritize import implementation.
what:
- Add deprecation note to synchronize row in summary table
why: Cross-referenced against teamocil source, no corrections needed.
what:
- Update last-updated date to 2026-03-07
why: Deprecation affects import behavior and priority decisions.
what:
- Note tmuxinator deprecates true/before in favor of after
- Clarify that import should still honor original semantics per value
why: Cross-referencing issue #1016 against source code found 2 inaccuracies.
what:
- I1 Bug B: Add double-wrapping consequence when pre/pre_window types mismatch
- T4: Clarify teamocil always renames session regardless of --here flag
why: Second parity pass found inaccurate fallback chain order and missing CLI commands/flags.
what:
- Fix pre_window fallback chain: pre_window → pre_tab → rbenv → rvm (was reversed)
- Add doctor command and completions to commands table
- Add start --append, --no-pre-window, --project-config flags
- Add list --active, --newline flags
why: Second parity pass found undocumented behavioral differences.
what:
- Add section 8: teamocil applies layout after each pane split (vs tmuxp once at end)
- Add section 10: root path expansion comparison (no gap, both tools expand)
- Renumber existing sections 8-9 to 9-10
why: Third parity pass found importer test fixtures cover only ~40% of real-world patterns.
what:
- Add Tier 1 crash risks: v1.x teamocil string panes, commands key, rvm
- Add Tier 2 missing coverage: YAML aliases, emoji names, pane titles, etc.
- List required new fixtures for Phase 1 implementation
…ings

why: Third parity pass found untested edge cases from tmuxinator spec fixtures.
what:
- Add section 18: YAML aliases/anchors (transparent, but untested)
- Add section 19: numeric/emoji window names (YAML type coercion risk)
- Update summary table with new entries
@tony tony force-pushed the tmuxinator-parity branch from 2d03c24 to ffe3b41 Compare May 9, 2026 12:18
…organize phases

why: libtmux#635 closed — all three API additions shipped in v0.55.0.
Line references drifted as code grew. Phase structure misrepresented
what's blocked vs unblocked.
what:
- Mark L1, L2, L3 as RESOLVED with correct libtmux source locations
- Fix 20+ stale line number references across importers.py, builder.py, libtmux
- Move T2 from Phase 3 to Phase 2, mark Phase 3 COMPLETE
- Move T9 to Phase 5 as unblocked, remove L2/L3 from Phase 5
- Fix EnvironmentMixin location (common.py, not session.py)
@tony tony force-pushed the tmuxinator-parity branch from ffe3b41 to a108693 Compare May 9, 2026 12:31
tony added 15 commits May 9, 2026 07:54
…ignment

why: Eliminate dead loop overhead in importers.py:160-166.

what:
- Replace `for _b in w["filters"]["before"]:` (and after) with direct
  assignment. The loop iterated N times but only ever set the same
  value, producing identical output but obscuring intent.
- Output unchanged; tests confirm equivalence.

ref: notes/plan.md I3
…tructs

why: tmuxinator's `pre` is a session-level hook that runs once before any
windows are created. The importer was mapping it to `shell_command_before`
(per-pane) when alone, and to the invalid `shell_command` key (silently
dropped) when combined with `pre_window`. Both produced wrong semantics
and execution counts.

Additionally, the combo branch's `isinstance(workspace_dict["pre"], str)`
checked the wrong variable to decide how to wrap `pre_window`.

what:
- Map `pre` -> `before_script` (session-level), matching tmuxinator
  semantics in template.erb:18-19.
- Map `pre_window` -> `shell_command_before` independently; isinstance
  check now correctly inspects `pre_window`'s type.
- Warn via `logger.warning` with extra={"tmux_key": "pre", ...} when
  `pre` contains shell metacharacters (`|`, `&&`, `>`, `$(`, etc.) since
  `before_script` runs via `subprocess.Popen` without `shell=True`.
- New module-private `_has_shell_metachars()` helper.
- Fix existing fixtures (test2/test3) that encoded the old buggy
  behavior; expected output now matches corrected mapping.

ref: notes/plan.md I1
why: Lock in the corrected pre/pre_window mapping behavior.

what:
- New fixtures: test_pre_alone (solo pre -> before_script),
  test_pre_combo (pre + pre_window map independently), and
  test_pre_shell (pre with shell metacharacters).
- Wire fixtures into the parametrized test_config_to_dict suite.
- Add test_import_tmuxinator_warns_on_shell_metachars_in_pre
  asserting WARNING record with extra.tmux_key="pre" and
  extra.tmux_session set. Build workspace inline since the
  parametrized fixture dict gets mutated by the importer's
  .pop() calls in earlier tests.
- Add test_import_tmuxinator_no_warning_when_pre_is_plain as
  the negative case.

ref: notes/plan.md I1
why: The previous `str.replace("-f", "").strip()` global replacement
was flag-unaware. It would corrupt paths containing `-f` (e.g.
`~/-f-test.conf`) and silently dropped `-L`/`-S` socket flags entirely.

what:
- New module-private `_parse_tmuxinator_tmux_args()` splits the
  argument string with `shlex.split()` and walks tokens, extracting
  `-f` -> `config`, `-L` -> `socket_name`, `-S` -> `socket_path`.
- Unknown flags log `WARNING` with extra={"tmux_key": <flag>}.
- Existing test2 (`cli_args: "-f ~/.tmux.mac.conf"`) and test3
  (`tmux_options: "-f ~/.tmux.mac.conf"`) still pass — same output
  for the simple case.
- Behavior change: configs that previously passed `-L`/`-S` flags
  in `cli_args`/`tmux_options` now have those mapped to their tmuxp
  equivalents instead of being dropped.

ref: notes/plan.md I2
why: Verify I2 fix for fragile -f parsing.

what:
- New fixture test_cli_args_multi: `-f ~/.tmux.conf -L mysock -S /tmp/...`
  -> config + socket_name + socket_path (was: only -f extracted, -L/-S
  silently dropped).
- New fixture test_cli_args_dash_path: `-f /home/me/-f-config.conf`
  -> config preserved (was: corrupted by str.replace("-f", "")).
- Both wired into parametrized test_config_to_dict.
- New test_import_tmuxinator_warns_on_unknown_cli_args_flag asserts
  WARNING fires with extra.tmux_key=<flag>.

ref: notes/plan.md I2
…ttach

why: These tmuxinator config keys were silently dropped by the importer.
Add explicit translation matching tmuxinator's actual semantics.

what:
- `rvm` -> wrapped as `rvm use {value}` in shell_command_before, mirroring
  tmuxinator project.rb:181.
- `pre_tab` -> shell_command_before (deprecated predecessor to pre_window).
- `pre_window` source resolution now follows tmuxinator's OR-fallback
  chain (rbenv -> rvm -> pre_tab -> pre_window) — first non-nil wins,
  matching project.rb:175-188.
- `on_project_first_start` -> before_script when `pre` not set.
- `socket_path` -> socket_path (top-level pass-through).
- `attach: false` -> WARNING directing user to `tmuxp load -d`.
- `startup_window` / `startup_pane`: scan windows by name OR integer
  index, set `focus: true` on matched window/pane. Unresolved values
  emit WARNING. Pane resolution converts positional pane strings to
  dict form when adding `focus`.
- New helpers: `_apply_tmuxinator_startup_focus()`,
  `_resolve_startup_index()`.

ref: notes/plan.md I5
why: Lock in the I5 behavior for the new tmuxinator key translations.

what:
- New fixtures: test_rvm, test_startup_window_by_{name,index},
  test_socket_path. All wired into parametrized test_config_to_dict.
- New unit tests:
  - test_import_tmuxinator_warns_on_attach_false (I5: attach false ->
    WARNING about CLI -d).
  - test_import_tmuxinator_on_project_first_start_falls_back_to_pre
    (I5: hook key mapped to before_script when `pre` absent).
  - test_import_tmuxinator_pre_window_chain_first_match_wins (I5:
    rbenv wins over rvm/pre_tab/pre_window per project.rb:175-188).
  - test_import_tmuxinator_warns_on_unresolved_startup_window (I5:
    name not found -> WARNING with tmux_key=startup_window).

ref: notes/plan.md I5
…/clear

why: Triage the v0.x-only TODOs that were in the importer's docstring.

what:
- with_env_var (default true in v0.x per teamocil 0.4-stable): when the
  `session:` wrapper is present (v0.x marker) and `with_env_var` is not
  explicitly false, emit `environment: {TEAMOCIL: "1"}` at the session
  level so each pane inherits the env var.
- cmd_separator: emit a WARNING that it has no effect in tmuxp, since
  tmuxp sends commands individually via separate send-keys calls.
- clear: still preserved on the window dict, but emit a WARNING that
  the builder does not yet act on it (deferred to a future T-item).
- Refactor the v0.x detection: capture `is_v0x = "session" in
  workspace_dict` once for the with_env_var gate.
- Update docstring TODOs: remove the now-handled keys, document the
  per-key behavior in a Notes section.
- Existing fixture `three_windows_within_a_session` (the only v0.x
  multisession layout) updated to expect the new TEAMOCIL env var.

ref: notes/plan.md I7
why: Lock in I7 behavior for the v0.x TODO key triage.

what:
- New fixtures: test_with_env_var_default (default true -> TEAMOCIL=1)
  and test_with_env_var_false (explicit false -> no env var). Wired
  into parametrized test_config_to_dict.
- New unit tests:
  - test_import_teamocil_warns_on_cmd_separator (I7: WARNING with
    extra.tmux_key=cmd_separator).
  - test_import_teamocil_warns_on_clear (I7: WARNING with
    extra.tmux_key=clear).
  - test_import_teamocil_v1x_skips_env_var (I7 regression: a v1.x
    config (no session: wrapper) gets no TEAMOCIL env var).

ref: notes/plan.md I7
…itted

why: Real-world v0.x configs sometimes omit the `session:` wrapper but
still use v0.x pane keys (`splits`, `cmd`) and `filters`. Routing them
to the v1.x path silently dropped commands.

what:
- New `_has_v0x_window_markers()` helper inspects the inner workspace
  for v0.x signals: window has `splits` or `filters`, or any pane has
  `cmd`. Routes to `_import_teamocil_v0x` if found.
- All existing v0.x test fixtures (test1-test4 and the layouts.py
  multisession scenarios) updated to expect the new
  `environment: {TEAMOCIL: "1"}` from I7's with_env_var default.
- The v1.x skip test (`test_import_teamocil_v1x_skips_env_var`) now
  uses the real v1.x `commands` key (was using v0.x `cmd` and
  incorrectly being treated as v1.x by the old detection).

ref: notes/plan.md I4
why: Lock in I4 behavior — string pane shorthand, `commands` key,
per-window/pane `focus`, window `options`, and the v1.x format
dispatch.

what:
- New fixture test_v1x_string_pane: bare string panes
  (`panes: [vim, top]`) -> `[{shell_command: [vim]}, {shell_command: [top]}]`.
- New fixture test_v1x_full: covers `commands`, window `focus`,
  pane `focus`, window `options`, mixed string + dict panes,
  session-level `root`, window-level `root`.
- Both wired into parametrized test_config_to_dict.

ref: notes/plan.md I4
why: `width` was already popped silently. `height` and `target` were
left on the pane dict (silently passing through to the builder, which
ignored them). Make all three pop with an audible WARNING so users
know the importer dropped per-pane geometry.

what:
- Loop over ("width", "height", "target") inside the v0.x pane loop;
  pop each and emit `logger.warning` with extra={"tmux_key": <key>}.
- Update existing layouts fixture expected output to drop the
  previously-preserved `target` key.

ref: notes/plan.md I6
why: Lock in I6 behavior — width/height/target each warn when popped.

what: New test_import_teamocil_warns_on_v0x_pane_geometry asserts
all three keys produce WARNING records with extra.tmux_key set.

ref: notes/plan.md I6
why: Phase 1 (Import Fixes) is complete. Update plan.md status so
the table reflects shipped behavior.

what:
- I1: pre → before_script with shell-metachar warning.
- I2: shlex parsing for cli_args/tmux_options.
- I3: direct assignment replaces redundant filter loops.
- I4: v0.x/v1.x dispatch, detection by session: wrapper OR
  splits/filters/cmd markers; v1.x handles string panes, commands,
  focus, options.
- I5: pre_window OR-fallback chain (rbenv→rvm→pre_tab→pre_window);
  startup_window/pane resolved to focus: true; on_project_first_start
  fallback; socket_path pass-through; attach: false warns.
- I6: v0.x height/target now popped with WARNING.
- I7: with_env_var → environment.TEAMOCIL=1 for v0.x; cmd_separator
  and clear warn (clear builder support deferred).

Total Phase 1 commits: 14 (7 source + 7 tests).
ref: notes/plan.md Phase 1
…Phase 1

why: Phase 1 of the tmuxinator-parity branch (PR #1014) is behavior-
changing for users importing tmuxinator/teamocil configs. Document
what changed so upgraders know what to expect.

what: Adds a "Behavior changes — tmuxinator/teamocil import" section to
the upcoming-release placeholder, summarizing all 7 import-side fixes
and the v1.x teamocil format support.

Also drops a stray blank line in tests/workspace/test_import_teamocil.py
left by an autosquash conflict resolution (cosmetic, ruff-format).
@tony tony force-pushed the tmuxinator-parity branch from 0d5918c to be5ce33 Compare May 9, 2026 14:32
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