Teardown
Safely delete orchestrator runtime artifacts (runs directory + worktrees) after archiving them to ~/catalyst/archives/{orchId}/ and recording them in the SQLite index. The goal: once an orchestrator is finished, its artifacts survive even when worktrees and runtime directories are reaped.
When to run
- After a completed orchestrator, once
orchestratePhase 7 has run the archive sweep. The
sweep is automatic; teardown is explicit — the user invokes it when they no longer need the live worktree and runs directory.
- Manually:
claude /catalyst-dev:teardown <orchId>to clean up a finished orchestrator. - During disk cleanup: teardown refuses to delete unless the archive exists, so running it
against an un-archived orchestrator is safe (it will archive first, then delete).
Preconditions
An orchestrator is teardown-safe when ALL of these are true:
~/catalyst/runs/{orchId}/state.jsonexists and reportsstatusin{done, complete, failed}.- The archive sweep has succeeded:
~/catalyst/archives/{orchId}/metadata.jsonexists and the
SQLite row for {orchId} is present in orchestrators.
- No worker signal file has
status: "in_progress"or analivePID.
Use --force to bypass the preconditions (you own the consequences).
Procedure
- Arguments
`` /catalyst-dev:teardown <orchId> [--force] [--dry-run] ``
<orchId>— required. Must match/^[A-Za-z0-9._-]+$/.--force— skip safety checks.--dry-run— print what would be deleted without deleting.
- Archive sweep (prerequisite)
Before deleting anything, run the sweep to make sure artifacts are persisted:
``bash bun plugins/dev/scripts/orch-monitor/catalyst-archive.ts sweep "<orchId>" ``
If the sweep exits non-zero AND --force is not set, abort with a message that explains what's missing. The sweep is idempotent, so re-running it is safe.
- Enumerate deletion candidates
- Runtime directory:
~/catalyst/runs/<orchId>/ - Worktrees: anything listed in
git worktree list --porcelainwhose branch name
contains <orchId>.
Print the candidates.
- Safety gate
For each candidate, verify:
- The directory exists.
- For worktrees:
git -C <worktree> status --porcelainis clean (or--force). - For the runs dir: no
workers/*.jsonhasstatus: "in_progress"oralive: true
(or --force).
- Stop background jobs (CTL-567)
A phase-agents run spawns one claude --bg job per phase; a completed bg job keeps its process alive until the ~1h supervisor reaper. Before deleting the runtime directory, claude stop every remaining job of the run:
``bash plugins/dev/scripts/phase-agent-watch-bg reap \ --orch-dir ~/catalyst/runs/<orchId> --scope all ``
--scope all is correct here — the run is terminal, so the mid-run exemptions (failed-pending-revive, turn-cap-exhausted) no longer apply. Prefer claude stop over claude rm: phase agents for one ticket share a worktree, so claude rm could delete a live sibling's worktree. With --dry-run, pass --dry-run through. A missing run directory or phase-agent-watch-bg is non-fatal — skip and continue.
- Delete
git worktree remove <path>for each worktree (add--forceif step 4 flagged dirty
state AND user passed --force).
rm -rf ~/catalyst/runs/<orchId>/for the runs directory.
- Verify
- Re-run the archive listing to confirm the orchestrator is still discoverable:
``bash bun plugins/dev/scripts/orch-monitor/catalyst-archive.ts list --orch "<orchId>" --json ``
- The SQLite row must still exist and
archive_pathmust still point to a real directory.
Output
On success, print a summary:
Teardown complete for <orchId>
archived to: ~/catalyst/archives/<orchId>/
bg jobs stopped: <N>
deleted:
runs: ~/catalyst/runs/<orchId>/
worktrees: <paths...>
On refusal (preconditions failed and no --force), print what's blocking and exit non-zero.
Related
/catalyst-legacy:orchestratePhase 7 — runs the sweep automatically when orchestration ends.bun plugins/dev/scripts/orch-monitor/catalyst-archive.ts— the archive CLI with
sweep|sync|prune|list|show subcommands.
- See
docs/architecture.md§ "Artifact Persistence" for the end-to-end lifecycle.