Skip to content

Workspace(feat[lifecycle]): Add project hooks and stop command#1048

Open
tony wants to merge 8 commits into
parity-runtime-configfrom
parity-lifecycle-hooks
Open

Workspace(feat[lifecycle]): Add project hooks and stop command#1048
tony wants to merge 8 commits into
parity-runtime-configfrom
parity-lifecycle-hooks

Conversation

@tony
Copy link
Copy Markdown
Member

@tony tony commented Jun 6, 2026

Summary

Stacked on #1047 to keep lifecycle behavior separate from runtime config primitives.

  • add on_project_start, on_project_restart, on_project_exit, and on_project_stop handling
  • add tmuxp stop so stored stop hooks run before killing a session
  • preserve existing session lifecycle metadata when appending/reusing sessions
  • document lifecycle hooks and stop command with focused CLI, builder, util, and config tests

Verification

Ran before commit:

$ rm -rf docs/_build; uv run ruff check . --fix --show-fixes; uv run ruff format .; uv run mypy; uv run py.test --reruns 0 -vvv; just build-docs;

Result: ruff clean, format unchanged, mypy clean, pytest 841 passed, 2 skipped, docs built successfully with the repo's existing warning class.

Stack: depends on #1047. Importer fallback follow-up is stacked on this branch.

@codecov
Copy link
Copy Markdown

codecov Bot commented Jun 6, 2026

Codecov Report

❌ Patch coverage is 81.30081% with 23 lines in your changes missing coverage. Please review.
✅ Project coverage is 82.47%. Comparing base (dab7070) to head (3e55df9).

Files with missing lines Patch % Lines
src/tmuxp/cli/load.py 55.00% 6 Missing and 3 partials ⚠️
src/tmuxp/util.py 79.31% 4 Missing and 2 partials ⚠️
src/tmuxp/cli/stop.py 91.66% 3 Missing and 1 partial ⚠️
src/tmuxp/workspace/builder.py 84.21% 1 Missing and 2 partials ⚠️
src/tmuxp/workspace/loader.py 85.71% 0 Missing and 1 partial ⚠️
Additional details and impacted files
@@                    Coverage Diff                    @@
##           parity-runtime-config    #1048      +/-   ##
=========================================================
+ Coverage                  82.30%   82.47%   +0.16%     
=========================================================
  Files                         28       29       +1     
  Lines                       2594     2704     +110     
  Branches                     502      527      +25     
=========================================================
+ Hits                        2135     2230      +95     
- Misses                       328      336       +8     
- Partials                     131      138       +7     

☔ 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 tony force-pushed the parity-lifecycle-hooks branch from b7442dc to 8047b2c Compare June 6, 2026 23:16
@tony tony force-pushed the parity-runtime-config branch from 1e5aeb0 to f031d96 Compare June 7, 2026 01:29
@tony tony force-pushed the parity-lifecycle-hooks branch from 8047b2c to eebcf20 Compare June 7, 2026 01:29
@tony tony force-pushed the parity-runtime-config branch from f031d96 to 99fbb1b Compare June 7, 2026 11:14
@tony tony force-pushed the parity-lifecycle-hooks branch from eebcf20 to 575d74a Compare June 7, 2026 11:15
@tony tony force-pushed the parity-runtime-config branch from 99fbb1b to 61a6df4 Compare June 7, 2026 11:46
@tony tony force-pushed the parity-lifecycle-hooks branch from 575d74a to 250222d Compare June 7, 2026 11:46
@tony
Copy link
Copy Markdown
Member Author

tony commented Jun 7, 2026

Code review

Found 1 issue:

  1. The lifecycle-hooks documentation states "Hooks run through the shell and block tmuxp until they finish" — true for on_project_start, on_project_restart, and on_project_stop (which run via run_hook_commands/subprocess.run), but false for on_project_exit, which is registered as a tmux client-detached hook via run-shell and fires after tmuxp has already returned. The per-hook table on the same page is accurate; the prose should scope the blocking claim to the three subprocess-driven hooks.

Hooks run through the shell and block tmuxp until they finish. Hook failures are
logged and do not stop the tmuxp command.

Implementation showing the non-blocking dispatch:

joined = f"cd {shlex.quote(start_dir)} && {joined}"
guarded = f"if [ #{{session_attached}} -eq 0 ]; then {joined}; fi"
self.session.set_hook(
"client-detached",
f"run-shell {shlex.quote(guarded)}",
)

🤖 Generated with Claude Code

- If this code review was useful, please react with 👍. Otherwise, react with 👎.

@tony tony force-pushed the parity-lifecycle-hooks branch 2 times, most recently from d22d8e1 to e104ea3 Compare June 7, 2026 14:48
tony added 8 commits June 7, 2026 10:14
why: Add lifecycle cleanup and restart hooks as a separate layer above
runtime config primitives.
what:
- Run start and restart hooks from load while preserving append behavior
- Store exit and stop hook metadata on newly created tmux sessions
- Add tmuxp stop with on_project_stop execution
- Register the new modules' doctest fixtures and the stop subcommand in
  the CLI-surface guards (conftest, help-example validation), which
  must move in lockstep with the CLI
why: Pin hook firing semantics per load path and the stop command's
behavior.
what:
- Load CLI tests for start/restart hook dispatch and append behavior
- Stop command tests including on_project_stop execution
- Builder and config tests for exit/stop hook metadata
- util tests for run_hook_commands
why: Give users working references for lifecycle hooks and the stop
command.
what:
- CLI page and API autodoc page for tmuxp stop
- Lifecycle hook sections in top-level configuration docs
- Example workspace file for lifecycle hooks
why: Lifecycle hook commands may contain expanded credentials, so
normal logs must not persist the command text.
what:
- Replace hook command log extras with a redacted placeholder
why: Pin that hook failure logging keeps exit codes without leaking
command text.
what:
- Assert redacted placeholder in hook log extras and absence of the
  raw command string
why: The prose claimed all hooks block tmuxp, but on_project_exit runs
via tmux's client-detached hook after tmuxp has returned — the claim
was false for one of the four hooks and contradicted the per-hook
table on the same page.
what:
- Name the three blocking hooks explicitly and describe
  on_project_exit's deferred, non-blocking dispatch
why: run_hook_commands logs records under tmux_hook_cmd, but the key
was absent from the structured-context table that downstream
consumers treat as the schema contract.
what:
- Add the tmux_hook_cmd row to the core key table
why: The command text is redacted because hooks may expand
credentials, but the failure path logged the hook's raw
stdout/stderr at debug — the same secrets leak through output.
what:
- Replace raw output debug logging with a single suppression record
  carrying tmux_stdout_len / tmux_stderr_len companion fields
- Add parametrized coverage asserting an echoed secret marker reaches
  no log record while the length fields remain attributable
@tony tony force-pushed the parity-lifecycle-hooks branch from e104ea3 to 3e55df9 Compare June 7, 2026 15:21
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