# Resume

> Pick a devteam project back up after an interruption — a crash, a killed session, a machine reboot, or simply coming back tomorrow. Reconciles what the record claims against what the tree actually holds, reports the difference, and gets the client's confirmation before re-dispatching anything that could destroy work.

- Skill: `alternative-intelligence-cp/resume` (Agent Skill)
- Install (CLI): `npx skillmds@latest add alternative-intelligence-cp/resume`
- Raw SKILL.md: https://api.skillmd.com/api/skills/alternative-intelligence-cp/resume/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: alternative-intelligence-cp (https://skillmd.com/u/alternative-intelligence-cp)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/alternative-intelligence-cp/resume

---


# Resuming

Something interrupted the loop. **Your first job is to find out what actually
happened, not to get going again** — because the cheapest-looking recovery
action, re-dispatching a task whose agent is gone, is also the one that can
destroy work nobody knew was there.

`/devteam:run` already reconciles and recovers on startup. This exists because
it does so *immediately*, and after an interruption the record and the tree can
disagree in ways only a human can settle.

**It also covers the planned handoff**, which is not an interruption at all: a
session handing to a fresh one at a good stopping point, so the memory files
reload near the top of the new context and the finished task's debris does not
follow it. That case has a resource a crash does not — **the predecessor is
still alive** — and §0 exists to use it before anything else.

## 0. If the previous session is still alive, ask it

**Reconstruction is what you do when nobody is left to ask.** A handoff at a
planned stopping point is a strictly better position than a crash, and this
skill was written as though only the crash existed.

So before reconstructing anything: **is the outgoing session still open?**
`ListAgents` will say. If it is, message it — and keep it open until you are
done, because once it closes you are back to reading files.

**You drive, not it.** A written handoff can only contain what its author
thought to include, and what an outgoing session thinks is important is a poor
predictor of what an incoming one cannot work out. So do not ask for a summary.
**Read the record first, then ask about the specific things you could not
determine from it** — why a task was declined rather than closed, what a
half-finished branch was for, which of two plausible readings of a note is
right, what the client actually said as opposed to what was minuted.

### Every question you have to ask is a defect in the record

This is the part worth more than the handoff. **Treat the questions as a
measurement**: anything you had to ask about is something the written state
failed to carry, and it will fail to carry it again for the next reader, who
may not have anybody to ask.

So log them. A short list under the day's `RECORD.md` entry — *"the incoming
session had to ask X, Y and Z"* — is a free audit of the record's completeness,
taken at the one moment both the record and somebody who knows better are
available at once. It is also one of the very few points where this pipeline
looks **backwards** at what it wrote rather than forwards at what it will write
next.

### A planned rotation says so in a file, and it is a pointer

`devteam/.run/session/handoff-ready` exists only when a manager rotated itself
out at a checkpoint (`run` §7b). Two lines: `session <id>` and
`checkpoint C-n`. That is deliberately all of it — **it tells you a rotation
is in progress, who started it and at which point, and nothing that could be
stale by the time you read it.** Claims come from `BOARD.md`, open sandboxes
from `python3 "${CLAUDE_PLUGIN_ROOT}/scripts/sandbox.py" status`, the tree
from the tree. `cat` is the whole parser.

If the file names **your own** session you wrote it and your successor has not
arrived yet: keep working (`run` §7b step 4). If it names another, you are the
successor, and the order below matters:

1. **`ListAgents`, and expect the identity not to join.** Listed → your
   predecessor is alive and you are in the normal case. Not listed → it has
   exited; reconstruct from §1, and say in one line in `RECORD.md` that the
   handoff was announced but the predecessor was gone before you arrived. **A
   stale socket fails loudly, not silently** — a send to an exited session
   returns `ENOENT ... the peer process may have restarted`, so you will not
   mistake a dead peer for a quiet one.

   **`ListAgents` prints a name and a short ref; the board and `handoff-ready`
   carry a session id; nothing carries both.** So you cannot prove the busy
   peer named `<something>` is the session that wrote the pointer — you can
   only ask it. **Ask in the same message as your first questions** rather
   than discovering it as a defect afterwards: *"are you session `<id>`?"* is
   one line and it is the join. This is the same missing join the board's
   `Agent id` column exists to fix one layer down, and it is unfixed at the
   session layer because a session cannot read its own name.

2. **Read the record before you ask anything, and do not take the lock yet.**
   §1 and §2 still govern: a rotation is a better position than a crash, not a
   licence to write early. Your predecessor is still working — that is by
   design — so what you read will move; the lock is the point at which it
   stops moving.

3. **Ask.** You drive. Only what the files could not tell you. Log every one
   as `record defect: the incoming session had to ask <what>` under the day's
   `RECORD.md` entry, one line each. **That list is the measurement**, and it
   is taken at the only moment when both the record and somebody who knows
   better are available at once.

4. **Take the lock.** Write `${CLAUDE_CODE_SESSION_ID}` to
   `devteam/.run/session/manager`, put the same id on `BOARD.md`'s
   `**Writer.**` line, and commit: `board: writer <new id> (rotation from
   <old id>, C-n)`. Then `writer handoff: <old> → <new>` in `RECORD.md`.
   **The id must be a real one** — a writer line holding an empty pair of
   backticks reads as neither yours nor anyone's and inverts the guard in both
   directions.

5. **Tell your predecessor, and ask it for one thing**: *"I hold the lock as
   of `<commit>`. Finish nothing further; end your turn — and reply with the
   sha of your last intended write."* Its `run` §2 says what it does next.
   From that commit the guard refuses its `devteam/` writes, which is the
   control for this whole mechanism and is the same one that has always
   prevented two writers.

   **The sha is asked for because the record cannot answer it.** A predecessor
   writes nothing after being replaced (that is the design), so nothing in
   `devteam/` says where its work stopped — and its last commit can land
   *after* the pointer was written, so "the commit before the rotation" is not
   the answer either. Record it: `writer handoff: <old> → <new>, predecessor's
   last write <sha>`. Measured — the first live rotation's successor had to
   ask this, and logged it as a record defect.

6. **Rewrite `handoff-ready`; do not remove it.** Append one line:

   ```
   completed <iso> by ${CLAUDE_CODE_SESSION_ID}
   ```

   **An absent file and a file nobody wrote read the same** — the reason
   P-14b never deletes a heartbeat, and it applies here for a sharper reason.
   Your predecessor is still a live session that believes it was the manager.
   If it tries to write, the guard's refusal has to be able to tell *"you were
   replaced, do not take the lock back"* from *"a stranger holds the lock, take
   it if they are gone"* — and the only thing that distinguishes them is this
   file naming that session. **Deleting it turns the correct refusal into the
   dangerous one**, measured: the first live rotation removed the file at this
   step, and the predecessor's next write got the generic takeover recipe.

   The `completed` line is what stops the *next* reader treating a finished
   rotation as one in progress. It stays until the next rotation overwrites it.

If the outgoing session is gone, continue from §1 and reconstruct. That is the
degraded case, not the normal one.

## 1. Do not take the lock yet

Read first, write nothing. **Do not** set the board's writer line, re-pin the
environment, or touch a title. If another session is genuinely still alive you
have not yet trampled it, and if it is not, nothing was lost by looking.

## 2. Establish what the record claims

- `devteam/BOARD.md` — what is claimed, by which agent label, since when, and
  which session holds the writer lock
- `devteam/RECORD.md`, last entries — the last thing that was known to happen
- every task title under `devteam/tasks/` — the states as the supervisors left
  them
- `devteam/QUESTIONS.md` — anything `open` was waiting on the client, and may
  have been waiting for days

## 3. Establish what the tree actually holds

The record is what somebody meant to be true. These are facts:

```bash
git -C "$REPO" status --porcelain
git -C "$REPO" log --oneline -15
ls -la devteam/.run/locks/            # heartbeats: which step since when,
                                      # or `closed <date>, verified PASS`
cat  devteam/.run/locks/*.sandbox     # under `structural`: which worker ran,
                                      # its pid, and WHERE its overlay is
ls -la devteam/.run/session/          # which session believed it held the lock
```

**The `.sandbox` files are part of what the tree holds, and they are the only
record of a worker that a restarted session can see** (P-14b). `ListAgents`
never saw a headless worker even while it lived. Each line names the task, the
step, the sandbox id, the pid, and the sandbox's **root as an absolute path** —
so a session that never spoke to the one that dispatched it can still find the
overlay. Then:

```bash
python3 "${CLAUDE_PLUGIN_ROOT}/scripts/sandbox.py" status   # what is still open
```

**A kept sandbox with a non-empty `upper/` is work a predecessor did not
promote.** It is the one thing in this whole procedure that a restart does not
recover on its own: the commits are real, they are on disk, and nothing on the
host knows about them. List every one of them in your report, by id and by
what it holds, **before anything is re-dispatched** — a re-dispatch opens a new
sandbox from the current base and the old work stops being reachable from the
board.

Then run all four checks. They compare the two directly and are the fastest
route to the disagreement:

```bash
python3 "${CLAUDE_PLUGIN_ROOT}/scripts/check_trace.py"  .
python3 "${CLAUDE_PLUGIN_ROOT}/scripts/check_refs.py"   .
python3 "${CLAUDE_PLUGIN_ROOT}/scripts/check_scope.py"  .
python3 "${CLAUDE_PLUGIN_ROOT}/scripts/check_report.py" . <each claimed task>
```

## 4. Reconcile, and look for the cases that mislead

For every `CLAIMED` row, apply the recovery table in `run` §3 — liveness over
the **whole agent subtree**, then the heartbeat, then the tree's mtimes (P-14).
Then check specifically for these, because each one makes a lost task look like
a finished one or the reverse:

| Looks like | Might actually be | How to tell |
|---|---|---|
| the work was lost — `RUNNING`, clean tree | committed under **somebody else's message** (F-17) | `check_scope` reporting `misattributed-write`; `git log` for the scope's paths |
| a finished task — title `DONE` | never verified; the verifier ran and FAILed, or never ran | `RECORD.md` has no `verify … PASS` line for it |
| nothing in flight — no heartbeat | a supervisor that died **before** its first dispatch | the task file is dirty but no commit exists |
| a task still working — heartbeat says `waiting on S-n` | a task that closed, whose heartbeat was never retired | the board and the task title say `DONE`; a heartbeat is retired to `closed <date>` at close, never deleted, so a live-looking one after a close is a lie told to this procedure |
| a live claim | a task stopped for a question, whose title was never updated | `QUESTIONS.md` has an open item naming it (P-27b) |
| a stale claim | a live worker under a completed supervisor | a live child in `ListAgents` (P-14) |
| a stale claim, nothing live anywhere | a headless worker still writing its overlay | `ListAgents` **cannot see one at all**; the `.sandbox` file's pid can, and a live pid there means the claim is working (P-14b) |
| the work was lost — `RUNNING`, clean tree, dead worker | done but never promoted | the `.sandbox` line names a root whose `upper/` is not empty; the commits are in the overlay, not the host |

**An uncommitted change under a claimed task's scope is work, not debris.**
Read it before deciding anything. A predecessor's uncommitted diff is
frequently the most valuable thing in the tree — it is what the worker had
learned and had not yet said.

## 5. Report before you act

Tell the client, on the charter's `Client channel`, in under twenty lines:

- **what was in flight** and how long ago it last moved
- **what is uncommitted**, path by path, and whether it looks like work or debris
- **what the checks say**, especially where the record and the tree disagree
- **what has been waiting for them** — open questions, and for how long
- **what you propose to do**, task by task: continue, re-dispatch, or ask
- **what re-dispatching would destroy**, if anything

**Then stop and wait.** This is a gate, not a courtesy. The client may know
something the record cannot: that a task was finished and the commit did not
land, that the interruption was a machine reboot rather than a failure, that
the whole thing should be abandoned. Re-dispatching a task whose worker had
uncommitted findings costs all of them, silently.

**Where the channel is `none`**, proceed on the safest reading — continue what
is clearly live, leave anything ambiguous stopped, and record every judgement
as an assumption for the next checkpoint. Do not re-dispatch anything whose
loss you cannot rule out.

## 6. Only then, hand back to the loop

On the client's word: take the writer lock, record a `resumed` line and a
`stale claim` line per recovery in `RECORD.md`, and run `/devteam:run`. The
loop's own startup will reconcile again, which is harmless and is one more
chance to notice something.

## What resuming must never do

- **Never re-dispatch before reporting.** The whole point.
- **Never stash or discard an uncommitted change** to get to a clean tree. A
  clean tree is not the goal; an accurate one is.
- **Never take the lock from a session you have not established is gone.** Two
  writers is the failure the design exists to prevent, and an interruption is
  exactly when it is most tempting.
- **Never quietly repair the record.** If the board and a task file disagree,
  say so, fix it deliberately, and record that you did — a resume that tidies
  is a resume that erases the evidence of what went wrong.

