Close a session — select the facts worth keeping into memory, update the current-state file, name what's left unfinished, then check and commit through the plugin's own verbs. Use when a session is ending, context is about to be cleared, or the user asks to wrap up.
Installs into .claude/skills of the current project.
Are you the author of Wrap?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/spscream-wrap)
---
name: wrap
description: Close a session — select the facts worth keeping into memory, update the current-state file, name what's left unfinished, then check and commit through the plugin's own verbs. Use when a session is ending, context is about to be cleared, or the user asks to wrap up.
---
# Wrap
The session is ending and context is about to go away. The job is making
sure the next session doesn't start from zero — and that means **selecting**
what to keep, not dumping the transcript into memory. A memory buried in
detail is worse than a short one.
See `agent-memory` for what a note, an index, and `evidence` mean; this skill
is about which facts earn a place in them and how the session closes.
The commands below call floppy's dispatcher, which ships with the plugin and
has no copy in this repository. The harness names this skill's own base
directory when it loads it — `Base directory for this skill:
<plugin>/skills/wrap` — and the dispatcher is `scripts/run` two directories
above that. Write that absolute path wherever a command says `<plugin>`.
If that line is not there — a harness that does not print it — do not guess a
cache path. `$CLAUDE_PLUGIN_ROOT` and `$CURSOR_PLUGIN_ROOT` are worth trying
first, and a checkout the human can name is worth asking for; a guess whose
failure mode is running the wrong copy of the plugin is worse than stopping.
## 0. Take the lock
```bash
bash <plugin>/scripts/run lock acquire "<the thread of work, one phrase>"
```
A parallel session writes into the same current-state file and the same
memory index, and the second write simply overwrites the first. If the lock
is held, write nothing: wait a minute, try once more, and if it's still held,
tell the human another session is closing and leave the decision to them.
Release it at the end of this skill; an abandoned lock ages out on its own
after about thirty minutes, and whoever takes it over then gets a warning
that the previous session may have left a half-written entry — that warning
is the point of the takeover, not a side effect: it tells you to go look
before trusting what's already there.
**What the lock covers, exactly.** It follows the memory rather than the
checkout, and `acquire` prints its reach as a `covers:` line. Where the
memory is a store shared by several checkouts, one lock serialises every
session on **this machine** that writes it, worktrees and separate clones
alike; where the memory lives inside the repository, the lock is per working
copy, which is the same thing said of a memory that is not shared.
**Two machines are covered by nothing** — not by this lock, which is never
committed, and not by anything else in the toolkit. There the failure is not
a silent overwrite: git refuses to merge two rewrites of the current-state
file and hands back a conflict, on every overlapping wrap. Loud, and yours to
resolve. Do not read the lock as protection against that case.
## 1. Select what is left
Most of this session's facts should already be notes — a fact is written when
it appears, not collected here; `agent-memory` holds the moments that produce
one. What is left for this step is whatever became clear only at the end: a
conclusion about the shape of the whole session, an approach whose rejection
made sense only in hindsight.
**Do not re-open what the session already wrote.** Those notes stand as
written. A refinement edits the file that already states the fact, and a note
that stopped being true is rewritten in place or deleted — both are
`agent-memory`'s rules, and neither is a review pass run at the single most
expensive moment of the session.
A candidate earns a note only if it passes all three:
- **it cost work** — a measurement, an investigation, a rejected hypothesis,
a tool trap. Not something readable out of the code in a minute.
- **it outlives the session** — useful in a week to someone who wasn't in
this conversation.
- **it isn't derivable from the repository** — not code structure, not git
history, not a retelling of what's already written in the docs.
Explicitly **not** saved: the reasoning trail, intermediate attempts ("tried
this, then that"), a retelling of the commits.
What's usually lost, and worth the most when it survives: **rejected
hypotheses with their reason** ("this doesn't work because…"), and **numbers
that turned out to mean something other than they first seemed**. Both of
those are more valuable than a list of what got done — the list is already
in the git log.
## 2. Evidence, chosen on purpose
Every note's `metadata.evidence` is one of four values, and they describe
where the claim came from, not how important it is:
| value | when it applies |
|---|---|
| `measured` | verified against reality — a run, a profile, an incident that happened |
| `read` | taken from code, a spec, or docs — true until something runs and says otherwise |
| `decided` | a choice or an agreement; evidence does not apply, a date and an author do |
| `sourced` | an outside claim checked against its primary source, quoted and dated |
**If unsure, write `read`**, and say in the note what a measurement would
need. Marking `measured` something that was actually only read is the
expensive mistake here — it hands the next session a guess dressed as fact.
A note that trips the consumer's own per-note cap
(`note_chars_max` in their `quota.lock` — in the project this convention
was ported from, that cap was measured at ten thousand characters, which is
a fact about that project's corpus, not a rule to inherit) is not a long
note. It's two notes written as one. Split it into one fact per file; do
not raise the cap to make it fit.
## Write for a stranger
Memory gets committed, and it will be read by someone who wasn't in this
conversation and may not even know who was. Write the **role** ("the
project owner"), not a name — and don't lean on conversation context:
"as discussed above" means nothing to a reader who wasn't there. If a fact
only makes sense with the discussion attached, the discussion belongs in
the note.
## 3. Update the current-state file, and journal only if there's something to journal
There are two current-state files, and which one a fact goes in is decided by
**who it is true for**, not by how important it is:
| fact | file |
|---|---|
| a metric, a freeze, a decision, a red or deferred item, something waiting on the human | `statuses_now` — the project's, committed and shared |
| what you were mid-way through, what is half-done, where to pick it up, which branch this thread lives on | `statuses_personal` — yours, on this machine |
Update the project file (`statuses_now` in `.floppy/config`) if this session
changed anything that belongs in it. A metric that got worse stays in, marked
worse: a disappearing line is how a regression hides in a file that's
rewritten rather than appended.
Update the personal file (`statuses_personal`, defaulting into the private
scope at `machines/<name>/NOW.md`) with the thread of work itself. Create it
if it isn't there; leave it alone if this session had nothing to hand over.
It is committed by the same `commit` call, into the private store rather than
this repository, because it is inside the memory.
**Why this is two files and not one.** The project file is the only artefact
every wrap rewrites whole, so two sessions overlapping in it conflict — while
a note collides with nothing and an index merges. Most of what forced a
rewrite was personal: one person's thread of work, changing every session and
shared with nobody. Moved out, it never needs merging at all, and what stays
behind changes rarely enough that two sessions rarely touch it. Putting a
private working note back into the shared file is not a small untidiness — it
re-creates the collision on the file a stranger has to read.
Only add a dated journal entry when the session produced numbers or an
analysis worth reconstructing a month from now. A small doc fix needs no
journal entry — that would be noise — but may still need the current-state
file updated on its own (a decision or a question taken off the table
carries no number). Re-read both files immediately before editing: another
session may have written to them since this one started.
## 4. Name what's unfinished — more important than what's done
State explicitly, in memory or the current-state file:
- background jobs and uncommitted work (`bash <plugin>/scripts/run status` gives
both in one call);
- what's waiting on the human versus what can proceed without them;
- what's broken or deferred — if it stays red, say so plainly.
## 5. Check, see the diff, commit — two calls, not ten
```bash
bash <plugin>/scripts/run check <files you wrote>
```
Both calls print the repository they resolved as their first line
(`repo: /path/to/it`) — `commit` also prints the branch and push target,
above its gates. Before running `commit`, the writing half that stages,
commits, and pushes, confirm that repository is the one the human actually
meant to close: several projects can sit open in the same harness at once
(Cursor especially), and a `wrap` aimed at "this session" can land in
whichever one the shell happened to be in rather than the one the human was
talking about. One line is enough — name it, don't turn it into a ceremony.
```bash
bash <plugin>/scripts/run commit -m "<what the facts are, not 'updated memory'>" <same files>
```
`commit` pulls `--rebase` and pushes after committing, by default — that
default is `commit_push=auto` in `.floppy/config`. A repository with no
remote configured at all sets `commit_push=never` there instead, so this
call stays entirely local rather than failing on the pull every time. For a
single call that should sync but not yet push, pass `--no-push`.
On a branch the remote has never seen there is no pull: nothing exists to
rebase against, so the first push is `-u origin HEAD` and sets the upstream
itself. This is the ordinary case wherever the default branch is protected,
because then every wrap runs on a branch made for it. If the push comes back
refused by a branch rule (`GH013`), nothing is broken and a retry will not
help: the commit is safe locally, and `commit` prints the branch-and-pull-request
recipe that moves it. Do that rather than reaching for a force push or a
config change.
The whole read-only half of closing a session is that first call; the whole
writing half is the second. Each one used to be several separate commands —
the memory linter, a guard, `git status`, `git diff --stat`, then staging,
committing, pulling, pushing, and releasing the lock. They were folded into two
because **the turn is the unit a session is billed for, not the tool call**, and
those steps were not independent: each result decided whether the next should
run, so each was its own turn. A skill that expands `check` and `commit` back
into their individual calls puts every one of those turns back.
The fold worked and the rite still doubled — 12 turns per run before it, 25
after, with the shim verbs costing 5.1 turns per run, 23% of the total, because
runs call `lint`, `guard` and `status` *on top of* `check`, which already runs
all three. Three habits are what the numbers point at:
- **call `check`, not the verbs it contains.** A separate `lint` or `guard` in
the same run is a turn spent re-reading what `check` just printed. Calling
`check` again after fixing what it found is not the same thing — that one is
the loop working.
- **issue independent calls in one block.** Taking the lock and reading
`status` are independent; so are several notes that are all ready to write.
Parallel calls in one block cost one turn, sequential ones cost one each.
- **rewrite the current-state file once, don't patch it.** Measured at 2.6 and
again at 2.7 edit turns per run — the rule has never held. A turn here costs a
median 32k base-equivalent tokens, almost all of it re-reading context, so two
patching turns cost 64k where one full rewrite costs 49k with the whole file in
its output; at the p90 of six turns the gap is 143k. Patching looks cheaper per
call and is not. Decide every change it needs, then write it.
Do not go looking for savings in the narration — there are none, and
`measurements.md` in this directory says why, along with what every number above
was measured on and which earlier explanation it replaced. Read it before
changing any rule here; the rite itself does not need it.
The diff prints before the commit on purpose: closing happens right when the
human has stopped watching closely, so the file list and the size of the
change need to be visible in the conversation, not only in git history
afterward.
What a red `check` means:
- **quota** — not a bug, the memory grew past the ceiling in `quota.lock`.
Raising the number is fine, but only in the same commit as the notes that
needed the room, with the reason in the commit message. Before raising it,
check whether something should be dropped instead — a note superseded by a
later measurement should go, not sit next to its replacement.
- **someone else's note** — the linter reads the whole memory on disk, so it
goes red on a neighboring session's unfinished write too. **Fix only what
this session wrote.** Someone else's half-finished note is not yours to
repair blind — name it in the report and leave it exactly as found.
- **this session's own file** — stop and fix it. The next session will not
find a note that never got written correctly.
`git add -A` is never used — the tree can hold nested repositories and other
people's working copies; stage by naming the files.
## What wrap commits, and what it never touches
This rite commits **memory, docs, the current-state file, and the session
procedure itself** — nothing else. `guard` enforces this against a watched
path list, but the enforcement being in a script is not a reason to leave it
unsaid here: a limit only a script knows about is a limit nobody can reason
about ahead of the call that hits it. If the session also touched product
code, that stays uncommitted — name it in the report to the human and leave
it for them, don't fold it into this commit because it happened to be open
in the same session.
## Workplace memory is a second repository
If any of what got written lives in a workplace-wide private store (see
`agent-memory` — facts private to this project but shared across a
workplace's machines), that store is a **separate git repository**, and this
project's own `git status` says nothing about it at all. `check` reports its
state; commit and push it on its own, the same way as this repository — an
unpushed note there blinds the other machine exactly as an unpushed commit
here would, silently, with nothing in this repository's status to reveal it.
A workplace-wide store like this should carry a `pre-commit` hook that
refuses a commit containing a secret — check whether this one does before
assuming it; not every workplace repository has set that up. Where it
exists, take a refusal as correct, not as an obstacle: remove the value and
leave a pointer to where it actually lives instead of the value itself.
Never answer it with `--no-verify` — the hook exists for exactly the commit
it just stopped. Where it doesn't exist, be the guard yourself: read what
you are about to commit for anything that looks like a secret before it
goes in.
## Report to the human
Four short parts:
1. **What got recorded** — one line per fact, with its file path. Both halves:
what was written during the session, and what this rite added. A report that
lists only the second understates the session's memory, and part 2 is
worthless when part 1 is incomplete.
2. **What was deliberately not recorded**, and why — so they can object.
3. **What's left unfinished** — background jobs, anything uncommitted,
anything waiting on a decision.
4. **Where the next session should start** — one phrase, in `start` terms.
If something looks like a scope decision — what can be shared, published, or
shown outside this project — **ask, don't decide it here**: that call
belongs to the project's owner. This isn't caution for its own sake: a rule
in this area that got decided out of caution, without asking, once had to be
rewritten together with the history it had already produced. Guessing at a
sharing boundary is not free to undo.