diff options
Diffstat (limited to 'docs/design')
| -rw-r--r-- | docs/design/2026-06-23-wrap-teardown-shutdown-proposal.org | 124 |
1 files changed, 124 insertions, 0 deletions
diff --git a/docs/design/2026-06-23-wrap-teardown-shutdown-proposal.org b/docs/design/2026-06-23-wrap-teardown-shutdown-proposal.org new file mode 100644 index 0000000..a47aa2d --- /dev/null +++ b/docs/design/2026-06-23-wrap-teardown-shutdown-proposal.org @@ -0,0 +1,124 @@ +#+TITLE: Proposal — wrap-it-up teardown + "wrap it up and shutdown" variant + +* Source + +Raised by Craig in a home-project session, 2026-06-23, after talking the +design through. Two related additions to =wrap-it-up.org=. Both touch the +Claude-session lifecycle (workflow + hook + the =ai-term= buffer/tmux pair), +so they're rulesets — with one companion piece that has to live in +=.emacs.d/modules/ai-term.el= (flagged below). Originally floated as an +archsetup task; archsetup owns the Hyprland/waybar layer, not the +Claude-session lifecycle, so it was re-routed here. + +* Architecture this depends on (so the design is grounded) + +- =ai-term.el= (=.emacs.d=) is the in-Emacs launcher: a vertical-split vterm + buffer running a tmux session named =aiv-<project-basename>= (prefix + =aiv-=). Layering: =claude= process → tmux session =aiv-<proj>= → Emacs + vterm buffer. +- Killing the tmux session takes the =claude= process with it, so "quit + Claude Code" is a *consequence* of killing =aiv-<proj>=, not a separate + step. +- Hooks already exist under =~/.claude/hooks/= (e.g. =session-clear-resume.sh=, + =precompact-priorities.sh=) — the teardown trigger fits that pattern. +- =sudo= is =NOPASSWD: ALL= on Craig's machines, so =sudo shutdown now= runs + unattended. + +* Item 1 — wrap-up also removes the buffer, quits Claude, removes the tmux session + +Recommend: yes, with one structural rule — the wrap-up runs *inside* the +things it tears down, so teardown is self-terminating and must be the last, +decoupled action, or the valediction may not flush before the session dies. + +Design: +1. *Teardown lives in =ai-term.el=* (companion, see below): one function + =cj/ai-term-quit= that kills the =aiv-<proj>= tmux session (takes =claude= + with it), kills the vterm buffer, and restores the saved window geometry — + =ai-term.el= already owns the buffer↔session pair and the geometry logic. +2. *Trigger from a Stop / SessionEnd hook, not inline.* Wrap-up does all its + git/archive work, delivers the valediction, then drops a sentinel (flag + file, e.g. =/tmp/ai-wrap-teardown-<session>=). The hook fires when Claude + finishes, sees the sentinel, and runs =cj/ai-term-quit= via =emacsclient=. + Decoupling guarantees the valediction lands before the session dies. +3. *Gate on commit+push verified* — never tear down before the session record + is pushed (wrap-up's existing Step 4 / validation checklist already + enforces push; teardown is strictly after it). +4. *Phrase split — teardown IS the default* (Craig's decision 2026-06-23). + Bare "wrap it up" does the full wrap AND removes the buffer/session/quits — + that's his typical case. The non-destructive variant gets the explicit + qualifier: "wrap it up with summary" summarizes + commits + pushes + + archives but keeps the buffer (no teardown), so the summary stays readable. + So: "wrap it up" → teardown; "wrap it up with summary" → no teardown; + "wrap it up and shutdown" → wrap + poweroff (supersedes teardown, Item 2). + +* Item 2 — "wrap it up and shutdown": 10-count then =sudo shutdown now= + +Recommend: yes, but the safety gate is load-bearing and the countdown has a +rendering gotcha. + +Design: +1. *"Only ai-term left" = hard blocking precondition*, evaluated BEFORE the + countdown. Count live sessions (=tmux ls | grep '^aiv-'= or + =pgrep -fc claude=). If more than this one is alive, ABORT the shutdown, + list what's running, and fall back to a normal wrap. Never power the box + off out from under another active Claude session. This is the most + important part of the item. +2. *The live countdown can't run through Claude's tool output.* The Bash tool + buffers stdout until the command returns, so a =for i in $(seq 10 -1 1); + sleep 1= prints all ten at once at the end, not one per second. It has to + run detached or in Emacs: + - tty writer: =for i in $(seq 10 -1 1); do printf '\rShutting down in %2d…' + "$i" > /dev/tty; sleep 1; done; sudo shutdown now= (backgrounded), or + - an Emacs =run-at-time= timer printing 10→1 in the echo area, then + =(shell-command "sudo shutdown now")=. +3. *Make it abort-able* (Ctrl-C / keypress cancels). A 10-second countdown's + whole purpose is a last-chance window; a non-cancellable one is just a + delay. +4. *Sequencing.* "...and shutdown" supersedes Item 1's teardown — if the box + is powering off, killing the buffer/session first is moot. Wrap (commit + + push + archive) → session-count gate → countdown → =shutdown=. + +Packaging: a small rulesets bin script (e.g. =ai-wrap-shutdown=) doing the +gate → abort-able countdown → shutdown, invoked by the workflow after the wrap +commit/push. Countdown either in that script (tty) or handed to Emacs. + +* Companion — required change in =.emacs.d/modules/ai-term.el= + +Item 1's teardown function =cj/ai-term-quit= must live in =ai-term.el= (it +owns =aiv-<proj>= session naming, the vterm buffer, and geometry restore). +rulesets owns the workflow + hook + bin script that *call* it; =.emacs.d= owns +the function itself. Spec for the =.emacs.d= side: + +- =cj/ai-term-quit (&optional project)= — resolve the =aiv-<basename>= session + for the current/!named project, =tmux kill-session= it, =kill-buffer= the + associated vterm buffer, restore saved geometry. Idempotent / no-op if the + session or buffer is already gone. Callable from =emacsclient -e= so the + Stop hook can invoke it headlessly. +- (Optional) a count helper =cj/ai-term-live-count= so the Item-2 gate can ask + Emacs how many ai-term sessions are live, as an alternative to =tmux ls= / + =pgrep=. + +When rulesets builds the workflow/hook side, route this companion to +=.emacs.d= (inbox-send) so the two land together. + +* Open decisions for Craig + +- Phrase set: DECIDED (2026-06-23) — "wrap it up" tears down (default); + "wrap it up with summary" wraps without teardown; "wrap it up and shutdown" + is the poweroff variant. Remaining nuance: confirm the exact non-destructive + qualifier wording is "with summary" (vs e.g. "and summarize"). +- Countdown home: tty-writer bin script vs Emacs timer. (Emacs timer reads + cleaner inside the vterm and is trivially abort-able.) +- Session-count mechanism for the gate: =tmux ls=, =pgrep claude=, or + =cj/ai-term-live-count=. + +* Verify + +- Item 1: bare "wrap it up" → valediction renders fully, THEN buffer + + =aiv-<proj>= session + claude all gone, geometry restored; "wrap it up with + summary" → wrap completes but the buffer stays intact (no teardown). +- Item 2 gate: with a second =aiv-*= session alive, "wrap it up and shutdown" + refuses, lists the other session, and does a normal wrap (no poweroff). +- Item 2 happy path: sole session → 10→1 renders one-per-second, is + cancellable, then =shutdown= fires. +- Teardown never runs before commit+push is verified. |
