/sweep — dropped-work sweep & recovery
Find work stranded between "exists on disk" and "merged + deployed"; recover it methodically after one batch approval. Born from a real multi-session crash recovery (2026-07-09) — every detection below corresponds to a failure mode that actually happened.
Invocation & scope
/sweep— current repo + everygit worktree listentry of it (default)./sweep all— every repo in the Config table below./sweep <path>— a named repo (+ its worktrees).
Config (edit this table for your repos — this is a template)
| Repo | Policy | Deploy provenance |
|---|---|---|
~/dev/my-app |
normal | BUILD_SHA via /version endpoint (see Provenance notes) |
~/dev/my-app-web |
normal | web bundle hash → commit mapping (see Provenance notes) |
~/dev/my-app-legacy |
report_only | deprecated; findings report-only / port-out |
Provenance notes (exact commands live here, NOT in the table — markdown tables mangle pipes):
# my-app: prod + staging stamp BUILD_SHA
curl -s https://<your-worker>/version # {"sha":...,"env":"production"}
curl -s https://<your-staging-host>/version # {"sha":...,"env":"staging"}
# arbiter when HTTP is ambiguous: npx wrangler deployments list (or your platform's equivalent)
# my-app-web: bundle hash, mapped to a commit READ-ONLY
curl -s https://<your-app>/ | grep -o 'index-[^"]*\.js'
# map via `git log --oneline -- web/` history + your deploy tool's release list.
# NEVER run a local build during discovery/triage (a local build writes build
# artifacts — violates the non-destructive rule). Unmappable read-only →
# report "DATA GAP: bundle-hash unmapped".
# a repo with no SHA endpoint: /health proves liveness only — there is NO deployed-SHA source.
curl -s https://<your-app>/health
# Deploy-surface comparison for this repo is BY DESIGN a stated limitation, not an
# error: record "deploy provenance: liveness-only (no SHA endpoint)" in the ledger's
# completeness section. Do not hunt for a SHA; do not mark it DATA GAP.
Coordination journals: your coordination/worktree journal (plus any handoffs/
directory it references). In-repo docs/worktree-journal.md may be a tombstone
pointing elsewhere — ALWAYS follow the pointer (the active journal is the one that
counts for staleness/heartbeats).
Policy semantics: in a report_only repo the only dispositions allowed are
report-only and port-out (recover content INTO a normal repo); Phase 4 refuses to
execute any mutation inside the repo — enforced at execution time, not just proposal
time. Discovery skips git fetch there (use existing refs; note their fetch age).
Ground rules (non-negotiable)
- Machinery state is not ground truth. Journal/handoff/memory lines saying
"SHIPPED" or "PENDING" are claims. Verify every claim against the real artifact:
git containment, live BUILD_SHA/bundle hash, an HTTP read,
gh pr list. - Discovery/triage are non-destructive. Exactly three writes are permitted in
Phases 1–3: (a)
git fetchinnormalrepos (remote-tracking refs only — never checkout/pull/working-tree); (b) the ledger file; (c) nothing else. Usegit fsck --unreachable --no-reflogs, NEVER--lost-found(it writes). - One batch gate; individual re-asks on top. Recovery runs only after the user
approves the ledger. Even inside an approved batch, ALWAYS re-ask individually:
prod/staging deploys, ANY push to a shared/protected branch (fast-forward
included), merges to main,
--force-with-lease, branch/worktree deletion, discarding any diff. Recovery stops at PR-opened; deploys are recommendations with the exact gated command, never executed. - Respect other sessions. Uncommitted WIP you didn't create belongs to someone
else until proven stale (file mtimes + ACTIVE journal heartbeats + live sessions —
the
orphaned-wip-adopterstaleness test if you have it — otherwise apply the staleness test inline: file mtimes + journal heartbeats + live sessions — but read the ACTIVE journal per Config, overriding that skill's owndocs/worktree-journal.mdreference). Active work isleave-active/ DO-NOT-TOUCH: never staged, stashed, or built on. - Zero findings in a category is stated explicitly (silence reads as "not checked"). A source that errors is a DATA GAP — report it, never invent.
Phase 1 — Discover (read-only)
Dispatch one subagent per repo (Agent tool, model: sonnet pinned), each prompted:
"Read ~/.claude/skills/sweep/references/discovery-protocol.md. Sweep
(policy: ). Deploy-provenance hints: <the repo's full Config-table row +
Provenance notes>. Coordination journal: .
Return the finding list only." The dispatcher COPIES the config context into the
prompt — the subagent cannot see this SKILL.md. Dispatch all repos in one message
for /sweep all.
Phase 2 — Triage
For each finding, run the checks in
~/.claude/skills/sweep/references/triage-rules.md (main session, or one sonnet
subagent per repo for large sweeps — a triage subagent's prompt must ALSO carry the
repo's Config row and resolved journal path, since it cannot see this SKILL.md;
supersession judgment calls may use model: opus).
Every surviving item carries: evidence command + output (REDACTED per triage-rules),
the claim it contradicts (if any), one-line proposed fix, proposed disposition.
Phase 3 — Ledger + the single gate
Write the ledger (template below) to docs/digests/YYYY-MM-DD-sweep.md in the repo
/sweep was invoked from — never into a report_only repo; if the invoking directory
is report_only or not a repo, write
~/.claude/coordination/sweep/YYYY-MM-DD-<repo>-sweep.md instead.
No-clobber: if the filename already exists (same-day re-sweep), NEVER overwrite
— the existing Recovery log is the checkpoint record of what already executed. Write
YYYY-MM-DD-sweep-2.md (-3, …), link the prior ledger at the top, and carry its
executed/blocked items forward so nothing re-runs. Present the
compact table in-chat and ask the user to approve/edit/veto the batch — ONE question,
listing each item's id, category, one-liner, and proposed disposition.
Ledger template
# /sweep — YYYY-MM-DD (<scope>)
**Status:** COMPLETE | DATA GAPS: <list>
**Baseline:** origin/main = <sha> · prod = <sha/provenance> · staging = <sha> (per repo)
## 1. LOSS-RISK (would vanish on disk death)
- [ ] SW-1 <worktree/branch> — <uncommitted diff / unpushed commits / stash> ·
evidence: `<cmd>` → `<output>` · fix: <one-liner> · proposed: <disposition>
## 2. NEEDS MERGE
## 3. NEEDS DEPLOY (commands given, never run)
## 4. NEEDS RECONCILE
## 5. SUSPECT ARTIFACTS
## 6. UNFINISHED INTENT
## 7. HYGIENE
(each category: items as above, or "None found — checked.")
## Recovery log <!-- Phase 4 appends here; checkpoint after EVERY item -->
- [ ] SW-<n>: <outcome line>
Phase 4 — Recover (only after approval)
Execute approved items in category order, one at a time, via the matching playbook in
~/.claude/skills/sweep/references/recovery-playbooks.md. After EVERY item update
the ledger's Recovery log (- [x] + outcome) — a second crash mid-sweep must itself
be sweepable. Re-ask individually per ground rule 3. report_only repos: refuse
in-repo mutation; port-out only.
Non-goals
Not a daily-driver digest skill (if you have one), not a live project board (if you have one, e.g. /pm), no issue-tracker sweep (if you have one), no memory/chat-history mining lane (/sitrep's historical lane covers that here, if present). Never merges to main, deploys, force-pushes, or prunes autonomously.