checkpoint

A governed artifact in the stable bundle. Replacing this file on your machine makes your next attestation come back Drifted, naming this path.

DescriptionPreserve and restore session state across compact and resume boundaries. Use when the user says checkpoint, hand off / handoff, session recap, wrap up / wrap this up, pick this up later, pick up where we left off, where were we, save or preserve session state, I need to stop here, before we lose context, before the compact, running low on context, about to compact, this session is getting long, or asks to resume / pick up a prior session. For committing CODE use your commit skill instead — this skill is for SESSION state. Default mode writes a durable compact-format recap to shared memory and prints a human-readable checkpoint. Sub-command 'resume' reads the latest (or named) checkpoint and rebuilds local state in a fresh session. Sub-command 'list' shows recent archived checkpoints.
Arguments[resume|list|label] [key-or-issue-ref]
Tools it may use[Bash, Read, Grep, Glob, TodoWrite]
Installs atskills/checkpoint/SKILL.md
Mode0644 — never executable
Size17910 bytes
SHA-256923563ed9482bade968e90c2ccc5f29b6001a9b3c173fa20ead8d084798c7a5b
fetch the raw bytes

The complete file, verbatim

This is the entire SKILL.md, including its YAML frontmatter: byte-identical to what installs, not rendered and not reformatted. Hash exactly what is below and you get the digest above. The table is only a reading aid; this block is the artifact.

---
name: checkpoint
description: Preserve and restore session state across compact and resume boundaries. Use when the user says checkpoint, hand off / handoff, session recap, wrap up / wrap this up, pick this up later, pick up where we left off, where were we, save or preserve session state, I need to stop here, before we lose context, before the compact, running low on context, about to compact, this session is getting long, or asks to resume / pick up a prior session. For committing CODE use your commit skill instead — this skill is for SESSION state. Default mode writes a durable compact-format recap to shared memory and prints a human-readable checkpoint. Sub-command 'resume' reads the latest (or named) checkpoint and rebuilds local state in a fresh session. Sub-command 'list' shows recent archived checkpoints.
argument-hint: "[resume|list|label] [key-or-issue-ref]"
allowed-tools: [Bash, Read, Grep, Glob, TodoWrite]
# Add your memory service's MCP tools to allowed-tools under their real,
# namespaced names — e.g. mcp__<your-memory-server>__memory_write,
# mcp__<your-memory-server>__memory_read, mcp__<your-memory-server>__memory_list.
# The bare names used in the examples below are placeholders, not tool ids.
---

# checkpoint — bidirectional session checkpoint

This skill is bidirectional: write a checkpoint to survive a context compaction, **or** resume
from the most recent checkpoint in a fresh session.

> **Precondition.** This skill assumes a shared memory service exposed to the session as MCP
> tools, with keys addressable by name and content stored as free text. Any key-value store
> with write / read / list works. Throughout this document `memory_write`, `memory_read` and
> `memory_list` are **placeholders for your service's tools** — substitute the real,
> namespaced tool ids before running anything, and list them in `allowed-tools`. Likewise the
> argument names in the call examples (`name`, `description`, `memory_type`, `content`) are
> illustrative; match your service's signature.
>
> If you have no such service, the write step degrades to a file in the repo, but you lose the
> property that actually matters: reachability from a *different* client than the one that
> wrote it.

## Argument routing

Look at the first argument:

| First arg | Mode | Behavior |
|---|---|---|
| `resume` | Resume mode | Read latest (or named) checkpoint, rebuild local state. See § Resume mode. |
| `list` | List mode | Show recent archived checkpoints with one-line summaries. See § List mode. |
| anything else, or empty | Write mode (default) | Write a checkpoint. The arg becomes the label. See § Write mode. |

If invoked with no arguments **and** the conversation already contains a "this session is being
continued from a previous conversation" prose summary, prefer **resume mode** — the user almost
certainly wants to recover state, not write fresh.

---

# § Write mode (default)

Two outputs, both on every invocation:

1. **Durable memory write** — survives the compact + resume cycle. The auto-generated prose
   summary at the start of a resumed session is unreliable; the memory record is the source of
   truth.
2. **Human-readable checkpoint** — printed to chat for the person who has to decide what
   happens next.

The memory write is non-negotiable. Even a thirty-second compact-imminent ping must leave a
recoverable breadcrumb behind.

---

## Step 1 — Gather state

Run these in parallel (a single batch, since they're independent). They assume a git working
tree; the PR query additionally assumes an authenticated GitHub CLI. Every one of them is
written to fail quietly, so a missing tool costs you a section of the recap rather than the
whole checkpoint — but if a section comes back empty, say it was **not collected** rather than
recording it as "nothing to report".

```bash
# Recent commits (this session window)
git log --oneline --since="8 hours ago" --format="%h — %s — %ci" | head -20

# Uncommitted state
git status --short | head -30

# Current branch + tracking
git status -sb | head -1

# Untracked deltas (explicitly — these are the ones that get silently lost)
git status --short | grep "^??" | head -10

# Open PRs the user authored
gh pr list --author @me --state open \
  --json number,title,state,url,baseRefName --limit 10 2>/dev/null

# Plan / design docs, if your project keeps them in a known directory.
# Swap this path for wherever yours live; it is a no-op if the directory is absent.
ls docs/plans/*.md 2>/dev/null | head -5
```

**If your setup has a local task or issue store**, query it here for open items too — anything
not in `completed` or `cancelled`, newest first, and bounded — a checkpoint that
grows with the backlog stops being readable. Keep it optional and
failure-tolerant: a checkpoint must still succeed on a machine where that store doesn't exist.

Also pull from your own conversation, since none of the above captures it:

- the current todo list (in-progress + pending items)
- decisions made this session that are **not** in commit messages — architectural calls,
  trade-offs taken, work deliberately deferred
- pending external blockers — review approvals, gate results, actions owed by a human

## Step 2 — Compute memory keys

Deterministic keys, so a future session can find this without being told where to look:

```bash
TS=$(date -u +%Y%m%d_%H%M)
LABEL_RAW="${1:-$(git branch --show-current)}"
LABEL=$(printf '%s' "$LABEL_RAW" | tr '/' '-' | tr -cd 'a-zA-Z0-9-' | cut -c1-40)
[ -z "$LABEL" ] && LABEL="adhoc"
ARCHIVE_KEY="session_checkpoint_${TS}_${LABEL}"
LATEST_KEY="session_checkpoint_latest"
```

Write the recap to **both** keys:

- `session_checkpoint_latest` — always overwritten; gives one-call resume from any future
  session that knows nothing about this one
- `session_checkpoint_<TS>_<label>` — versioned archive; never overwritten, preserves history

## Step 3 — Build the recap

Use a compact, pipe-delimited, one-fact-per-line format. It survives truncation gracefully
(losing the tail costs you the last few facts, not the parse), it is trivially greppable, and
it resists the drift you get when a model is asked to "summarize" freely.

```
SESSION_RECAP/1
DATE|<ISO-8601 UTC>|session|<session-name-or-unknown>|label|<label>
BRANCH|<branch>|tracking|<remote-or-none>
TASK|<short>|<status>|<next_step>
TASK|<short>|<status>|<next_step>
COMMITS|<sha>|<subject>
COMMITS|<sha>|<subject>
UNCOMMITTED|<path>|<status>|<one-line-why>
PR|<number>|<state>|<url>|<one-line-why>
DECISION|<what>|<why>|<blast_radius>
DECISION|<what>|<why>|<blast_radius>
PENDING|<item>|<blocker_or_NONE>
PENDING|<item>|<blocker_or_NONE>
RESUME_NEXT|<self-contained one-line for the model that resumes>
RESUME_HUMAN|<one-line for the human: where we are, what's next>
```

**Rules:**

- One fact per line, pipe-delimited. No prose paragraphs.
- **Never include sensitive data** — no token fragments, no credential values, no SHAs of
  commits that touched secrets. This record is durable and cross-client readable; treat it as
  quotable.
- `TASK` status values: `done | in_progress | blocked | deferred`.
- `COMMITS`: at most the last eight commits from this session.
- `UNCOMMITTED`: only intentional files. Skip generated lockfiles and build noise unless they
  are load-bearing for the resume.
- `DECISION` captures the non-obvious choices a future session would otherwise re-litigate from
  scratch. This is the highest-value line type in the whole format and the one most often
  skipped.
- `RESUME_NEXT` must be **runnable**. `"check CI on the open PR, then request review, then
  squash-merge"` — not `"continue"`. If the next session has to guess, the checkpoint failed.

## Step 4 — Write to memory (BOTH keys)

Call `memory_write` once per key. **Never write the memory files directly on disk** — different
clients (CLI, desktop app, remote sessions) persist to different paths, and a direct file write
is reachable from exactly one of them. Routing through the shared service is the whole reason
cross-session memory works at all.

```
memory_write(
  name="session_checkpoint_latest",
  description="<one line: branch + headline of the work>",
  memory_type="project",
  content="<the recap above>"
)

memory_write(
  name="session_checkpoint_<TS>_<label>",
  description="<same>",
  memory_type="project",
  content="<same recap>"
)
```

Confirm **both** writes succeeded. If one fails, retry once; if it still fails, surface the
error to the user **before** printing the human readout. A silent memory failure is precisely
the failure mode this skill exists to prevent — and a pretty checkpoint printed over a failed
write is worse than no checkpoint, because it manufactures confidence.

## Step 5 — Print the human-readable checkpoint

```
## Session checkpoint — <feature/label> (<issue-ref-if-any>)

**Memory keys (resume from these):**
- `session_checkpoint_latest` — newest checkpoint (overwritten each call)
- `session_checkpoint_<TS>_<label>` — this checkpoint, archived

**Committed this session:**
- `<sha>` — `<subject>` — <files / inserts>, scope note

**Persisted outside git (safe across sessions):**
- Issue-tracker refs (ticket IDs + what changed)
- Agent role files in your harness's agent directory
- Plan file in your plan directory (if one exists)
- On-disk untracked deltas (explicit list + why each one matters)

**Phase status:**
- [done] <phase>
- [next] <phase> — **resume here**
- [todo] <remaining>

**[N] warnings to address before <gate>** (if any were deferred):
1. <severity>: <one-line fix>

**Resume prompt:** *"In a fresh session, read memory key `session_checkpoint_latest` — that
returns the recap with branch, PRs, decisions, and the next concrete action."*
```

### Format rules

- Status markers on phase bullets, so the resume point is findable without reading.
- Untracked deltas listed explicitly, never lumped into "plus some local changes".
- Skim-first: bullets beat paragraphs. Assume thirty seconds of attention.
- The **Memory keys** section MUST come first. It is the load-bearing artifact; everything
  below it is color.

## Step 6 — When you're checkpointing because a compact is imminent

If the user says "about to compact", "context filling up", "save state", or you observe context
utilization above ~85%, do everything above **and**:

- add a `COMPACT_IMMINENT|true` line to the recap
- tighten the human readout (drop Phase status entirely if there's no plan file)
- end with: *"Memory write succeeded. Safe to compact now."*

This is the exact failure mode the skill was built for. Any hesitation, any "let me first just
quickly…", defeats the purpose — you are racing a truncation.

---

## Why both outputs

- The **memory write** is for the next *model*. It survives compaction, survives resume, and
  survives the "this session is being continued" prose summaries that routinely drop detail.
- The **human readout** is for the *person*. Fast skim, decision-ready, and it doesn't require
  them to go query a memory store to find out where things stand.

Both must succeed. If the memory write fails, fix it before printing the readout — a readout
without the write is a false sense of safety.

---

# § Resume mode

Invoked as `checkpoint resume` (latest) or `checkpoint resume <key>` (specific).

The goal: take a fresh session that has no idea where prior work left off, and rehydrate it to
a runnable state in one round trip.

> **Pre-compact snapshot — check FIRST, if you have one.** If your harness supports a
> pre-compaction hook, have it write a mechanical snapshot (last task state + the recent user
> asks, captured verbatim **before** the summarizer runs) to a predictable per-session path.
> When resuming after a compaction or a crash, read that snapshot **alongside** the recap: the
> recap is the *intent*, the snapshot is the *uncompressed last-known state*. Where they
> disagree, resolve toward the recap — it was written deliberately; the snapshot is a raw
> capture with no editorial judgment applied.

## R1 — Read the checkpoint

```
memory_read(name="session_checkpoint_latest")
```

If a specific key was passed, use that instead. If the read returns `not found`, fall back to
listing (see § List mode) and present the options — do **not** silently fail into "starting
fresh".

## R2 — Parse the recap

The recap follows the format from § Write mode Step 3. Extract these fields (one entry per
matching line):

- `BRANCH|<branch>|tracking|<remote>` — **required**
- `TASK|<short>|<status>|<next_step>` — zero or more
- `COMMITS|<sha>|<subject>` — zero or more
- `UNCOMMITTED|<path>|<status>|<why>` — zero or more
- `PR|<number>|<state>|<url>|<why>` — zero or more
- `DECISION|<what>|<why>|<blast_radius>` — zero or more
- `PENDING|<item>|<blocker>` — zero or more
- `RESUME_NEXT|<one-liner>` — **required**, this is the first action
- `RESUME_HUMAN|<one-liner>` — **required**, this is what you tell the human

If a required field is missing or the format is malformed, surface that before doing anything
else. **A malformed recap is a bug — don't paper over it.** Inferring around a broken recap is
how a resume quietly rebuilds the wrong state.

## R3 — Reconcile branch state

```bash
CURRENT=$(git branch --show-current)
TARGET=<from the BRANCH line>
if [ "$CURRENT" != "$TARGET" ]; then
  echo "checkpoint expects branch $TARGET, currently on $CURRENT"
fi
```

**Never auto-checkout.** A branch switch can stash, lose, or stomp uncommitted work that the
new session doesn't know about. Always ask: *"Checkpoint was written on `<TARGET>`, you're on
`<CURRENT>`. Switch?"*

If the branch doesn't exist locally, surface that too — it may just need a fetch first.

## R4 — Rebuild the todo list

For each `TASK|<short>|<status>|<next_step>` line, build one todo entry:

| Recap status | Todo status |
|---|---|
| `done` | `completed` |
| `in_progress` | `in_progress` (cap at one — pick the most recent) |
| `blocked` / `deferred` | `pending` |

Concatenate `<short>` and `<next_step>` into the content field (e.g. `"Verify the test suite is
green — check CI on the open PR"`) so each item carries its own next action. Call the todo tool
**once** with the full reconstructed list.

## R5 — Print the resume summary

```
## Resumed from `<key>` — <RESUME_HUMAN>

**Branch:** `<branch>` (currently on `<current>` — <match|MISMATCH>)

**Open PRs:**
- #<n> — <state> — <why> — <url>

**Recent decisions** (so we don't re-litigate them):
- <DECISION what> — <why>

**Pending blockers:**
- <PENDING item> — blocker: <blocker>

**Next concrete step (RESUME_NEXT):**
> <one-liner>

**Todos rebuilt** — <N completed> / <N in-progress> / <N pending>.
```

End with a single explicit ask: *"Run RESUME_NEXT now? (y/n)"* — and wait for confirmation
before any destructive or networked action. Read-only verification (viewing a PR, `git status`)
is fine to do unprompted.

## R6 — Recover gracefully when the world has moved on

The world doesn't pause between sessions. Before declaring the resume successful:

- If a PR named in the recap is now **merged** or **closed**, note it in the resume summary and
  mark the related tasks `completed`.
- If the branch has **new commits** since the checkpoint (someone else pushed), surface the
  diff count.
- If `git status` shows **uncommitted work the recap never listed**, **stop.** That is drift —
  surface it before proceeding. Something happened between sessions that nobody recorded, and
  acting on a stale plan on top of unknown local changes is how work gets destroyed.

The resume is not done when the recap has been read. It is done when local state actually
matches the recap's expectations, or you have explicitly flagged every divergence.

---

# § List mode

Invoked as `checkpoint list`.

```
memory_list()   # assume NO server-side filter, and no guarantee this is the whole set
```

Filter client-side: keep only entries whose name starts with `session_checkpoint_`. Sort by the
timestamp embedded in the name (`YYYYMMDD_HHMM`), newest first. Mark `session_checkpoint_latest`
distinctly at the top.

```
## Recent checkpoints

-> session_checkpoint_latest — <description>
   session_checkpoint_<YYYYMMDD>_<HHMM>_add-retry-logic — <description>
   session_checkpoint_<YYYYMMDD>_<HHMM>_release-branch-pause — <description>
   ...

Resume any with: checkpoint resume <key>
```

Cap the display at ten archives.

**Account for what you elided.** Two different caps are in play and they must be reported
differently:

- *Your* display cap (ten archives) is a deliberate choice — say "showing 10 of N retrieved".
- The *listing call's own* cap is not. If the API returns a page rather than the whole set,
  either page it to exhaustion or state plainly that the listing is partial and where the full
  set lives.

A result that stops at N and says nothing is indistinguishable from a complete answer, and every
consumer downstream inherits the error. This is the ordinary failure mode of any paginated store
that defaults to a page size the caller never sees: the index looks whole, reasoning proceeds on
a clipped view, and nothing surfaces the gap. Check the response for a total or a
next-page cursor before you treat a listing as complete.

---

## Why both directions live in one skill

Write and resume are the same conceptual unit — one is the writer, the other is the reader —
and pairing them keeps the recap format spec authoritative in exactly one place. A separate
`resume` skill would eventually drift from the writer's format, and the failure would only show
up at the worst possible moment: in a fresh session, with no context, trying to recover.

The default-write / sub-command-resume shape also matches the other multi-mode skills in this
set, so the muscle memory transfers.

All skills · Back to the overview