Files
multi-agent-mux/.agents/skills/multi-agent-mux-stop/SKILL.md
T
GodopuandClaude Opus 5.5 17eba0904e docs(issue-42): review - attach names the herdr server, --dry-run is not "write nothing", a deleted block stays deleted
Merge-workspace review of PR #46 (codex R1-R3 and C1, claude O1 and O4).

- R1: resume never attaches, and the server it exports does not reach the
  calling shell; the native herdr binary does not route on environment
  variables. The attach advice and the Verification block now name the
  server: `herdr --session <server> agent attach <name>`, with <server> the
  row's herdr_session.
- R2: --dry-run in resume and create skips the spawn and the registry
  update, but the checks before it still run and on macOS remove the
  quarantine attribute from the agent binary. The option rows say so.
- R3: test_no_deleted_body_came_back compares every current block with the
  deleted bodies by exact sha and by a whitespace-insensitive fingerprint
  (stored as base_fingerprint), independently of the inventory, so a
  deleted block cannot return by being listed again under class c.
  Replaying the review's probe (the deleted resume Workflow re-added and
  inventoried as c) fails the new test and passes the old one.
- C1 / O1: the base create call block at 263-266 (class c) folded into the
  Workflow call is recorded under "superseded"; the 37 base blocks are all
  accounted for.
- O4: create exit 42 also covers MAM_KILL_ON_READY_TIMEOUT=1; stop's no-op
  exit 0 does not apply with --purge-conversation.
- The inventory's positions and shas are regenerated for the edited blocks;
  every classification is unchanged.

No product code changed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VCgu9Fa9iT7TbJUSzdq4Ff
2026-10-04 08:06:32 +09:00

11 KiB

name, description, version, author, license, platforms, environments, metadata
name description version author license platforms environments metadata
multi-agent-mux-stop Stop an agent herdr session (claude, antigravity/agy) and update .mam/agent-sessions.yaml. Default stops gracefully and marks status=stopped with conversation preserved for resume. Does NOT delete on-disk conversation artifacts (jsonl/db) — those are preserved unless --purge-conversation is passed. Use when ending a work session, switching to a different one, or cleaning up before a fresh start. 4.8.2 godopu MIT
linux
macos
terminal
herdr
hermes
tags related_skills prereq_skills
agent
herdr
claude
antigravity
agy
multi-agent
stop
terminate
cleanup
multi-agent-mux-create
multi-agent-mux-resume
multi-agent-mux-monitor
multi-agent-mux-create
multi-agent-mux-resume

Multi-Agent Stop — Stop an Agent herdr Session

Companion skills: multi-agent-mux-create (start), multi-agent-mux-resume (re-attach), multi-agent-mux-monitor (live status). Herdr Isolation: stop 명령은 YAML의 herdr_session 필드를 자동으로 파싱하여 해당 격리 서버의 세션을 안전하게 종료(kill)하므로, HERDR_SESSION_NAME 환경변수를 수동으로 지정할 필요가 없습니다. (--herdr-workspace는 CLI 대칭성을 위해 파서에서 허용되지만 소켓 라우팅에는 영향을 주지 않습니다.) Single source of truth: ./.mam/agent-sessions.yaml.

What this skill does

Stop an agent's herdr session gracefully, resolve and store the conversation ID, and mark the YAML entry (status=stopped). Preserves:

  • The herdr session's recorded pane.pid / cmd / cwd / mcp_attachments for audit
  • The agent's on-disk conversation (claude *.jsonl, agy conversations/*.db) — so the user can multi-agent-mux-resume later
  • The start_command so a future multi-agent-mux-create --session <name> reproduces the same herdr spec

The stop command is always graceful by default:

  1. Sends exit keys to the agent TUI (/exit for Claude, /exit for Agy) and waits 3 seconds.
  2. If still alive, issues herdr kill-session (SIGTERM) and waits 5 seconds.
  3. If still alive, kills the pane PID via SIGKILL (kill -9) as a last resort.
  4. Auto-captures the conversation ID into the row (claude_session_id_own/agy_conversation_id_own) before killing, ensuring the next resume uses a race-free tier-1 lookup.

Pre-flight

stop_session.sh checks this itself: the YAML exists and the session is registered (otherwise exit 1), and a session already stopped is a no-op (exit 0).

Workflow

# 1. Stop gracefully (default — captures ID, shuts down safely, status=stopped)
bash .agents/skills/multi-agent-mux-stop/scripts/stop_session.sh \
  --session "$SESSION_NAME" --agent "$AGENT"

# 2. Stop gracefully + record a custom stop reason
bash .agents/skills/multi-agent-mux-stop/scripts/stop_session.sh \
  --session "$SESSION_NAME" --agent "$AGENT" --reason api_error

# 3. Stop gracefully + clean up on-disk conversation (DANGEROUS)
#    — this prevents any future resume (status=terminated, resumable=false).
bash .agents/skills/multi-agent-mux-stop/scripts/stop_session.sh \
  --session "$SESSION_NAME" --agent "$AGENT" --purge-conversation --yes

--purge-conversation cannot be undone. Pass --yes only after the user has confirmed it.

Exit Meaning
0 Stopped; or already stopped (no-op, except with --purge-conversation, which still purges)
1 From the checks before the stop, nothing was changed: YAML not found, or the session is not registered. The registry update after the stop also exits 1 (the row disappeared from the YAML during the run)
2 Bad arguments (including the retired --mode / --capture-id / --graceful), or --agent omitted and not resolvable
3 --purge-conversation without --yes
4 Purge aborted: the herdr session survived the kill chain
other The registry update after the stop (YAML / DB write) failed and its code is passed through

Exits 2 and 3, and exit 1 from the pre-stop checks, come before anything is stopped, so nothing was changed. On exit 1 from the registry update, or an other exit, the session may already be down while its row still says running: check with multi-agent-mux-status, then run the stop again (it is idempotent).

Idempotency: if the row is already status: stopped, the script prints already stopped (...) and exits 0 — re-running is a safe no-op.

--agent is the standard. Pass it on every invocation. If omitted, the script resolves the agent from the registry record — the row's agent field, then the session-name suffix, then pane.cmd — and exits 2 if none of the three resolve. The fallback exists for recovery, not as the normal calling convention: a session whose name carries no agent suffix (e.g. agy-creator-01) is only resolvable while its registry row survives.

State machine

running ──(stop default / --reason)────────► stopped      (resumable:true, conv preserved)
running ──(stop --purge-conversation --yes)► terminated   (resumable:false, conv deleted)
stopped ──(stop default … again)───────────► stopped      (idempotent no-op)

Fields written in STOP mode: status: stopped, stopped_at, stopped_at_epoch, stop_reason, termination_mode: graceful, claude_session_id_own/agy_conversation_id_own and resumable: true.

If --purge-conversation is used: status: terminated, terminated_at, terminated_at_epoch, termination_mode: purge and resumable: false.

The script:

  1. Verifies the session is in agent-sessions.yaml
  2. If delegate_job_id is set, automatically publishes a progress --detail "terminating" event to the multi-agent-mux-delegate-job registry
  3. Captures the last_visible_status from herdr capture-pane (so we have a final TUI snapshot for audit)
  4. Attempts graceful exit keys → SIGTERM kill-session → SIGKILL fallback
  5. For purge-conversation: deletes ~/.claude/projects/.../jsonl (claude) or, for agy, ~/.gemini/antigravity-cli/conversations/<uuid>.db with its -wal/-shm/-journal sidecars, brain/<uuid>/, annotations/<uuid>.pbtxt and presence/<uuid>.lock (the lock only once no live process references the conversation — Issue #10)
  6. Updates the YAML entry and SQLite database atomically
  7. If delegate_job_id is set, publishes a completed event to the multi-agent-mux-delegate-job registry

Removing an issue worktree: worktree_remove.sh (Issue #21 item 4)

bash .agents/skills/multi-agent-mux-stop/scripts/worktree_remove.sh --path <WT> [--stop] [--dry-run]

Every check runs first and changes nothing; a refusal exits 3:

  1. a linked worktree of the control root, not locked (git worktree unlock first - git refuses even --force on a locked one);
  2. running sessions under it only with --stop (stopped normally, conversations kept);
  3. no open job of those sessions - never cancelled automatically, they may belong to a running loop (the delegate job of a session --stop stops is cancelled by stop itself);
  4. a clean tree, submodules included (status --porcelain --untracked-files=all --ignore-submodules=none - a status.showUntrackedFiles=no setting cannot hide a file from a check that decides a forced removal);
  5. inside submodules: no modification, no commit reachable from HEAD or any local ref that is on no remote (a submodule sits on a detached HEAD; a commit kept only by a tag or a note is protected too) and no stash (refs/stash is reachable from no branch and leaves the status clean) - checked in every repository under the worktree's modules/, including deinitialised submodules (their repository stays there) and nested ones; a repository that cannot be checked refuses.

Then it stops the sessions, re-checks the jobs, runs checks 4 and 5 again (a session that is being stopped can still write, commit or stash - any change refuses with exit 3, sessions already stopped) and removes the worktree (the branch is kept): plain, or - when the worktree holds submodule state, which git refuses to remove otherwise - a single --force justified by checks 4 and 5. Do not run git worktree remove --force by hand on such a worktree: it deletes the worktree's own submodule repositories, unpushed commits included.

Exit codes: 0 removed, 1 stop/git failure, 2 not a worktree of the control root, 3 refused (nothing changed). Sessions created in a removed worktree cannot be resumed until a worktree exists at that path again.

Pitfalls

  • Don't delete on-disk artifacts by default — the agent's *.jsonl / conversations/*.db is the data that multi-agent-mux-resume needs. --purge-conversation is for when the user is genuinely done with the conversation and wants zero recovery chance.
  • YAML is append-only until you write a stop — if a previous run left the entry as running but herdr is actually dead (crash, host reboot), the YAML is stale. Running multi-agent-mux-stop will detect "herdr already dead, just update YAML" and proceed.
  • Don't delete the claude_session_id_own: null placeholder — when the user creates a fresh session with multi-agent-mux-create and never sent a message, the entry has claude_session_id_own: null. Stopping must preserve that field.
  • Monitor skill may still be tracking — if multi-agent-mux-monitor is running a heartbeat loop, stopping a session while it watches will trigger its herdr ls != yaml reconciliation. That's expected — let the monitor run, it will mark the entry as terminated on its own.

Verification

# 1. herdr gone (real native command — `herdr has-session` is a lib.sh
#    tmux-compat pseudo-command and needs `source .agents/skills/lib.sh` first)
herdr agent get "$SESSION_NAME" >/dev/null 2>&1 && echo "STILL ALIVE" || echo "OK: herdr gone"

# 2. YAML has stopped entry
python3 -c "
import yaml
d = yaml.safe_load(open('$AGENT_SESSIONS_YAML'))
s = [x for x in d['herdr_sessions'] if x['name']=='$SESSION_NAME'][0]
assert s['status'] == 'stopped', f'expected stopped, got {s[\"status\"]}'
assert s.get('stopped_at'), 'missing stopped_at'
print(f'OK: stopped at {s[\"stopped_at\"]}')
print(f'  preserved: pane.pid={s[\"pane\"][\"pid\"]}, cmd={s[\"pane\"][\"cmd\"]}, cwd={s[\"pane\"][\"cwd\"]}')
"

# 3. (if --purge-conversation) disk artifacts gone
[ -f "${CLAUDE_PROJECT_DIR:-$HOME/.claude/projects}/<projkey>/<uuid>.jsonl" ] && echo "WARN: jsonl still exists" || echo "OK: jsonl purged"

When NOT to use this skill

  • Just detaching → there's no herdr detach CLI command; press the herdr detach keybinding inside the pane, or just close the terminal. The herdr session keeps running.
  • Stopping the agent inside but keeping herdr → send Ctrl-C or /exit (claude) / Ctrl-D (agy) via herdr agent send <target> <text> (real native command; herdr send-keys is a lib.sh tmux-compat pseudo-command that needs source .agents/skills/lib.sh first). The herdr session stays but the agent process is gone.
  • Replacing an existing session with a new one → multi-agent-mux-stop first, then multi-agent-mux-create.