Claude Code can rewind and summarize everything up to a message. Choosing that message is the hard part. It turns out to be a measurable quantity, and most obvious ways to measure it are wrong.
Press Esc twice in Claude Code. You get a list of your own
messages. Pick one and you are offered, among other things,
Summarize up to here.
That does something specific. Everything before the message you picked is replaced by a summary. Everything after it is kept word for word. The exact wording, the file paths, the line numbers, the decision you made three messages ago: all still there.
This is the right shape for coding work. What I am doing at this moment rarely needs the exact words from an hour ago, but it very much needs the exact words from ten minutes ago. A summary of the whole session loses both. A summary of the old half loses neither.
So the feature is good. The problem is the list.
A long session has a hundred and forty messages. The picker shows you five at a time. You scroll, you read half-remembered lines out of context, you pick something that looks like a beginning, and you hope.
I did this many times and I was never sure I had chosen well. Sometimes I summarized away a decision I needed ten minutes later. Sometimes I stopped so late that I saved almost nothing.
Find the latest message i such that everything after
i can stand on its own, given only a summary of everything
before it.
Latest, because a later stop summarizes more and saves more room. Stands on its own, because that is what makes the summary safe. Both halves of that sentence are measurable.
My first instinct, and probably yours, was semantic similarity. Embed the messages. A good place to split is where the two halves are least alike and each half is most alike within itself. Score every split, take the best.
I built it. I ran it on a session I had labelled by hand. Then I sorted the candidate stops by score and looked at the order.
It was exactly the tail-size order. Smallest tail first, every time, with no exception. The score was not measuring topic. It was measuring how many messages were left.
| Term | As the tail grows | Range |
|---|---|---|
| coherence, mean similarity to the tail centre | falls | 0.424 → 0.365 |
| overlap, head centre against tail centre | rises | 0.771 → 0.841 |
| compression | falls | 0.85 → 0.27 |
The compression term was supposed to oppose the coherence term. It does not. A bigger head is a smaller tail, so all three move together and nothing pushes back. Only a hard floor stopped a one-message tail from winning outright.
So I removed the size bias five different ways.
| Variant | Idea | Rank of the right answer, out of 25 |
|---|---|---|
| A | raw centroid coherence − overlap + compression | 14 |
| B | mean pairwise similarity, no size bias by construction | 14 |
| C | mean pairwise, compression term removed | 16 |
| D | topic dip alone, in the manner of TextTiling | 12 |
| E | coherence z-scored against random blocks of equal size | 19 |
| F | topic dip plus compression | 14 |
Variant B is the one that hurt. Mean pairwise similarity has no size bias at all, by construction, and it still ranked the right answer fourteenth. Variant E removes the size effect statistically and did worst of all.
Similarity cannot find the start of a piece of work. In that session the right stop was the beginning of a research task. Eight messages later came "create a directory, code and all". That is a stage change inside the same task, and it has a far bigger vocabulary shift than the task's own beginning. Every similarity variant preferred it.
A boundary in meaning and a boundary in dependency are different things. I was measuring the wrong one.
Stop asking whether the two halves look alike. Ask a narrower question with an unambiguous answer: how much of the vocabulary in the kept part was introduced before the stop?
Take every content word used after the stop. For each use, look up where in the session that word first appeared. If it first appeared after the stop, the kept part brought it in itself. If it first appeared before the stop, the kept part is borrowing from a part you are about to replace with a summary.
A stop with high self-containment is safe. What comes after it barely refers back. A stop with low self-containment is dangerous: the kept messages are full of words whose meaning was established in the part you are dropping.
Two properties make this work where similarity did not. It has no size bias to remove, because it is a ratio over the kept part alone. And it directly measures dependency rather than resemblance, which is the thing the question actually asked about.
The words borrowed most often in a long session are ordinary ones:
case, check, back, code,
test. That looks like a defect, so I tried to remove it four
ways: inverse document frequency, its square, and dropping any term that
appears in more than 10%, 5% or 3% of messages.
Nothing improved. The zone boundaries did not move by a single message under any setting. The most aggressive cap made it clearly worse, taking the strongest boundary in the test set from 128 times the noise down to 46.
The reason is arithmetic. A common word appears in the dropped part and the kept part alike, so it adds to the borrowed count and the total count at the same time. It cancels in the ratio. The measure was already doing the thing I was trying to add.
Two of my own sessions, measured. Move along the curve to see the message at each stop. The two sessions have different shapes, and the shape is the answer.
A rise of 0.03 means nothing on its own. The same rise is a clear break in a quiet curve and ordinary movement in a restless one. So every candidate zone is measured against the median wiggle of its own curve, and the answer is reported as a multiple of it.
Measured on real sessions: a genuine topic change scores 128 to 191 times the wiggle of its own curve. The sub-topics inside one long piece of work score 6 to 13 times. Both are real signals. Only the first is a break.
That distinction matters more than it sounds. My first version only looked for large steps, so on a long single-topic session it found nothing and said so. That is honest and useless. The session still has to be shortened. Now it reports the sub-topics with their grades and lets me choose, and when even those are absent it shows the trade instead: three stops, each with what it keeps and what it saves.
The point of all this is a position number and a quote, so I can find the message in the picker without reading anything.
Every row is one place I could stop. The oldest is at the top, so reading
down frees more room. frees is the share of the chat that
becomes a summary. shape says what kind of place it is: three
dots a clear topic change, two a sub-topic inside the same work, one
barely above the noise. A falling mark, ▔▔▁, is the other
shape: the curve holds there, then drops away, so the work up to that
stop is finished and everything after it leans much harder on the past.
The arrow marks the one to take.
keep session edd44f35 · 188 messages · 112 places to stop self-containment 0.66 ┤ ▃▇█▇▇▆▆▅▆▆▆▇▇ 0.63 ┤ ▅│ 0.60 ┤ │ 0.57 ┤ ▃│ 0.54 ┤▁▁▂▁▁▁▁ ▁▁ │ 0.51 ┤ ▇▆▅▃▂▁▁ ▄│ ▃▄▂▂▁▂│ 0.48 ┤ ▇▇▇▆▆▇▆▄▂▁▁▁▁ ▃▇│ └────────────────────────────────────────────────────── ········································▔ the pick ▔▔▔ older → newer ↑ 3 older scroll to frees shape the message there below~82 ▓▓▓▓▌ 49% ●●● "let me knwo whaen post is published s…" below~76 ▓▓▓▓█ 54% ●●● "ok, stay positive, lets me see what I…" below~70 ▓▓▓▓▓▎ 58% ▔▔▁ "ummm, why you think this is good, how…" below~56 ▓▓▓▓▓█ 65% ●●● "ok good we got or today content yeayy" → below~51 ▓▓▓▓▓▓▌ 71% ●●● "yes agree I continue as I feel dotn w…" Esc Esc → scroll to "↓ 51 more below" → "Summarize up to here" the note and every stop: file:///…/.claude/context-keep/edd44f35.html
The same numbers say which part of the dropped half the kept half still leans on. Split the dropped part into six blocks and attribute every borrowed word to the block that introduced it:
A block under 5% is a side trip. The later work never refers back to it, so the note tells the summary to drop it whole rather than compress it. That is usually the debugging detour that went nowhere.
A measure that only satisfies its author is worth nothing. So I built a blind judge: a small model that reads only the user messages of a session, splits them into pieces of work, and chooses where the newest piece began. It never sees the curve, the zones, or any number this tool produces.
| Over 19 real sessions, 2,614 user messages, 3 machines | Result |
|---|---|
| the judge's stop lands inside a zone | 13 to 15 of 19 |
| inside, or within five messages of one | 17 to 18 of 19 |
| cost of a full run | under 5 cents, about 20 seconds |
Those are ranges, not numbers, and the ranges are the finding. Three runs
of identical code at temperature=0 gave 13, 14 and 15. The
harness cannot resolve a difference smaller than about two sessions, so I
will not claim a score to one decimal place, and neither should anyone
else with a set this size.
The parameter check is firmer. I picked the right stop by hand in a labelled session, then swept 36 combinations of the tuning constants. My stop stayed inside the top zone in all 36. The measure is not balanced on a knife edge, and it agrees with the person whose session it is.
After the measure worked I went looking for why it works, and found that the psychology of memory had described the shape decades ago.
People do not remember experience as a stream. We cut it into events, and we cut it at moments where the situation changes. Zacks and Tversky called these event boundaries, and showed that they are perceived automatically and agreed on across observers.1 That is the same claim as a step in this curve: a place where what follows stops depending on what came before.
Radvansky and Copeland gave it a sharper form. Walk through a doorway and you are measurably worse at recalling what you were holding in mind in the previous room, even when the task is unchanged.2 Crossing a boundary flushes the working detail. Not everything, though: what survives is the point of it.
Brainerd and Reyna's fuzzy-trace theory names the two things separately. Verbatim traces hold the exact form and decay quickly. Gist traces hold the meaning and persist.3 A person leaving a room keeps the gist and drops the verbatim.
Find the doorway, keep the verbatim after it, keep only the gist before it.
I want to be careful about the direction of that claim. I did not derive this measure from the literature. I built it, it worked, and then I found that it matches something well described. The reading explains why the shape is right; it is not evidence that the implementation is.
One deliberate consequence of the metaphor: the promise is not that you forget. You carry the gist through the door. The detail is still in the session file on disk, and the next thing I want to build is the way back through: a local store holding the dropped messages, with the summary carrying an id, so the model can ask for a detail the summary dropped instead of losing it.
A Claude Code plugin. It reads your session file, prints the answer, and changes nothing. No network, no dependencies, Python 3 only.
/plugin marketplace add unclecode/context-keep /plugin install context-keep@context-keep
Then type /keep in any
session. Claude Code does the summarizing; this only says where.