wrapup · git:20260814.b61c6f9 · 2026-08-14 · sha256 1e15803be59711de

wrapup git:20260814.b61c6f9A

Immutable. This exact content is served forever at /api/v1/blob/1e15803be59711de.

---
name: wrapup
description: Strategic session-end preservation. Captures decisions, discoveries, and state that the automatic hooks miss.
allowed-tools:
  - Bash
  - Read
  - Write
  - Edit
---

# Wrapup

When a runtime script is needed, resolve `ROOT` from `CARTOGRAPHER_ROOT`,
`CLAUDE_PLUGIN_ROOT`, or `PLUGIN_ROOT`. If none is set, derive the plugin root
from this skill's reported base directory (`../..` from `skills/wrapup`), with
the conventional checkout as a legacy fallback.

Deliberate end-of-session preservation. The hooks capture mechanical facts (files changed, commits made, session ended). This skill captures **strategic context** — the decisions, discoveries, and unfinished threads that make the next session productive.

Wrapup serves two audiences at once. The **log** gets a synthesis paragraph that `/remember` can recall months later. The **human** gets a digest panel they can verify at a glance — every line traces back to a logged event or to `git`, so a wrong claim is visible rather than merely plausible.

## Step 0: Render the digest — do this first

```bash
ROOT="${CARTOGRAPHER_ROOT:-${CLAUDE_PLUGIN_ROOT:-${PLUGIN_ROOT:-$HOME/Documents/dev/session-cartographer}}}"
node "$ROOT/scripts/session-digest.js"
```

The script resolves the session from `CARTOGRAPHER_SESSION_ID` / `CLAUDE_CODE_SESSION_ID`; pass `--session <id>` to override. It prints a panel covering span and tempo, commits with type and diff-shape mix, hottest files, research hosts, `/remember` served-vs-used, and the live dirty/unpushed state of every repo the session touched.

**Show the panel to the user verbatim.** Do not paraphrase it into prose or re-type its numbers into a bulleted list — the alignment is what makes it scannable, and re-typing is where the numbers drift.

Then read it before writing anything. This step exists so the synthesis is written *against the log* rather than from your own recollection of the conversation — recollection is exactly where confident, unfalsifiable summaries come from. If the digest disagrees with your memory of the session, the digest is right about what happened; your memory is only better on *why*.

If the digest exits non-zero (no events logged for this session), say so plainly and write the synthesis from the conversation, noting that it is unverified.

## What to capture

The digest already covers *what happened* mechanically — do not restate it. Your synthesis adds the layer no hook can infer:

1. **Key decisions or discoveries** — the non-obvious things that would be expensive to re-derive
2. **What's unfinished** — threads left open, next steps
3. **The hard problem** — what was actually difficult, not just what was done
4. **Why** — the reasoning behind the commits the digest lists

## Step 1: Write the milestone

Generate a one-paragraph synthesis of the session. Be specific — name the files, the commits, the discoveries. No filler.

Then log it:

```bash
DEV="${CARTOGRAPHER_DEV_DIR:-$HOME/Documents/dev}"
CLAUDE_SID="${CLAUDE_SESSION_ID:-${CLAUDE_CODE_SESSION_ID:-}}"
SESSION_ID="${CARTOGRAPHER_SESSION_ID:-${CLAUDE_SID:-${CODEX_SESSION_ID:-unknown}}}"
PROVIDER="${CARTOGRAPHER_PROVIDER:-unknown}"
[ "$PROVIDER" = "unknown" ] && [ -n "$CLAUDE_SID" ] && PROVIDER="claude"
# Fail loudly. A silent "unknown" is how 437 wrapups lost their transcripts.
[ "$SESSION_ID" = "unknown" ] && echo "warning: session id unresolved — this milestone will not link to a transcript" >&2
TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
EVENT_ID="evt-$(LC_ALL=C tr -dc 'a-z0-9' < /dev/urandom | head -c 12)"

# Detect project from cwd
GIT_REPO=$(git rev-parse --show-toplevel 2>/dev/null)
PROJECT=$(basename "${GIT_REPO:-$(pwd)}")
GIT_BRANCH=$(git branch --show-current 2>/dev/null || echo "none")

# Find transcript path
TRANSCRIPT=$(find ~/.claude/projects -name "${SESSION_ID}.jsonl" 2>/dev/null | head -1)
if [ -z "$TRANSCRIPT" ] && [ "$SESSION_ID" != "unknown" ]; then
  TRANSCRIPT=$(find ~/.codex/sessions -name "*${SESSION_ID}*.jsonl" 2>/dev/null | head -1)
  [ -n "$TRANSCRIPT" ] && PROVIDER="codex"
fi
ENCODED_PATH=$(echo "$TRANSCRIPT" | python3 -c "import sys, urllib.parse; print(urllib.parse.quote(sys.stdin.read().strip(), safe=''))" 2>/dev/null || echo "$TRANSCRIPT")
DEEPLINK=""
[ "$PROVIDER" = "claude" ] && DEEPLINK="claude-history://session/${ENCODED_PATH}"

# Attach the digest's scalars so the milestone stays checkable after the
# transcript hits Claude Code's ~30d TTL. Falls back to null if the digest
# could not run — never block the wrapup on it.
ROOT="${CARTOGRAPHER_ROOT:-${CLAUDE_PLUGIN_ROOT:-${PLUGIN_ROOT:-$HOME/Documents/dev/session-cartographer}}}"
DIGEST=$(node "$ROOT/scripts/session-digest.js" --json --no-git 2>/dev/null \
  | jq -c '{duration_minutes, event_count, projects, commit_types, commit_shapes, files_touched: (.files | length), recall}' 2>/dev/null)
[ -z "$DIGEST" ] && DIGEST=null

jq -n -c \
  --argjson digest "$DIGEST" \
  --arg eid "$EVENT_ID" \
  --arg ts "$TIMESTAMP" \
  --arg milestone "session_wrapup" \
  --arg description "SESSION_SYNTHESIS_HERE" \
  --arg session "$SESSION_ID" \
  --arg provider "$PROVIDER" \
  --arg transcript "$TRANSCRIPT" \
  --arg deeplink "$DEEPLINK" \
  --arg project "$PROJECT" \
  --arg cwd "$(pwd)" \
  --arg event "Wrapup" \
  --arg branch "$GIT_BRANCH" \
  '{event_id: $eid, timestamp: $ts, milestone: $milestone, provider: $provider, description: $description, session_id: $session, transcript_path: $transcript, deeplink: $deeplink, project: $project, cwd: $cwd, event: $event, git_branch: $branch, digest: $digest}' \
  >> "$DEV/session-milestones.jsonl"
```

Replace `SESSION_SYNTHESIS_HERE` with your synthesis paragraph. It must be a **single line** — the pipeline is TSV/line-based and a literal newline splits the row.

## Step 2: Index it

```bash
ROOT="${CARTOGRAPHER_ROOT:-${CLAUDE_PLUGIN_ROOT:-${PLUGIN_ROOT:-$HOME/Documents/dev/session-cartographer}}}"
tail -1 "$DEV/session-milestones.jsonl" | bash "$ROOT/scripts/index-event.sh"
```

## Step 3: Update memory if warranted

If the session produced a non-obvious discovery, preference, or decision that future sessions need — save it to memory. Most sessions don't warrant a new memory. Don't force it.

## What NOT to do

- Don't summarize every tool call or file read
- Don't write a changelog (the hooks handle that)
- Don't create memory entries for things derivable from git log
- Don't be verbose — one paragraph, specific, done
- Don't retype the digest's numbers into prose. It already showed them, aligned and correct; restating them is where drift enters
- Don't claim work the digest doesn't show. If you believe something happened that isn't logged, say it's unlogged rather than asserting it flatly

## Examples

What the digest looks like (Step 0 output — show it as-is):

```
━━ session digest · attentional-foraging ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

  session   dbc9ac21 · claude · 1242 events
  span      2026-05-01 02:36 → 2026-05-05 15:23 UTC · 108h47m
  tempo     ▂·▁▁▁▁▁▁··█▄▂▅▄·▁▅▄▁▃▄·▃▂▂▁▁▁·▁▆  (3h24m/mark)
  projects  attentional-foraging 1054 · approach-retreat 77 · movies-mindbendi…
  activity  580 edits · 608 bash · 4 searches · 6 compactions · subagents Plan

  commits   26 · 15 pushes
            15 feature · 8 docs · 2 fix · 1 refactor  ▸  17 construct · 6 surg…

            05-05 13:23  d6be69e  feat(ltr): typed-cascade migration + Pe…  +4724 −139
            05-03 17:55  7de5c98  docs(lit-notes): Dumais 2010 IIiX entry…      +12 −1
            … 20 more

  files     173 touched
            docs/drafts/cikm-2026/paper-v4.md                                   ×41

  recall    3 calls → 22 served · 2 used (9%)
            evt-7apd8osl9sud evt-wprsakq31ca5

  leaving   attentional-foraging@feat/dd-top-…  1 uncommitted
            approach-retreat@main               14 uncommitted

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
```

Good synthesis:
> "Trimmed root CLAUDE.md from 20KB to 4KB by moving project map, testing, and library details to per-project CLAUDE.md files. Created CLAUDE.md for scrutinizer2025, iblipper2025, interests2025. Pruned 4 stale memories. Key insight: /focus and /remember make the project map redundant in root context — saves ~4000 tokens per turn."

Bad synthesis:
> "Worked on various improvements to the codebase. Made things more efficient. Updated some files."