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

@tony tony commented Jun 6, 2026

Copy link
Copy Markdown
Member

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

codecov Bot commented Jun 6, 2026

Copy link
Copy Markdown

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).
⚠️ Report is 4 commits behind head on parity-runtime-config.

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

tony commented Jun 7, 2026

Copy link
Copy Markdown
Member Author

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

1 participant