Write the handover, then start clean
A long session gets worse before it gets full. Accuracy falls from roughly 300-400k tokens on a 1M
window and from about 50k on a 200k one, while Claude Code does not auto-compact until around 83% -
and compaction DISCARDS working detail rather than preserving it.
Both numbers are dated, and neither is measured here. The accuracy figures are Chroma's 2025
context-rot study, read second-hand from a summary rather than from the paper; the 83% is Claude
Code product behaviour that can move in any release. Treat them as the order of magnitude that
justifies handing over early, never as thresholds to tune against - and re-check both before
quoting either as current. Between those two points is where
a session should be handed over deliberately instead of truncated.
The one rule
Write only what the next session cannot re-derive.
It inherits the repository, the git history, the CLAUDE.md cascade and the memory store. Only one
thing dies with this session: what you were part-way through, and why you chose it.
Two files, because they have opposite lifetimes
handover.md describes ONE MOMENT and is replaced wholesale every time. OPEN-WORK.md is the
STANDING BACKLOG and is only ever edited line by line.
Keeping both in one file is what made the backlog rot. A handover is rewritten from what the
writing session has in mind, so every item is re-encoded from memory once per session, and it is
re-encoded in proportion to how recently it was TOUCHED. The item nobody worked loses one
attribute per rewrite: first its method, then its count, then its own heading, until it is a
clause in a sentence. Measured over one day in this repo, five tracked items - an 88-target audit
the user had asked for, a reviewer pass, 206 unframed bodies, 57 flagged candidates, 5 queued
contributions - went from named bullets with counts to absent, with no line saying any of them had
been closed.
Goes in handover.md |
Goes in OPEN-WORK.md |
| what is part-done RIGHT NOW, and its state |
anything not finished that outlives the session |
| what is uncommitted |
anything the user asked for and has not got |
| decisions this session took, with the reason |
work that is blocked, and on whom |
| the one next action |
the ranked list that next action is drawn from |
OPEN-WORK.md sits at the repo root, tracked, one item per line:
- [ ] (YYYY-MM-DD) [rank] ORIGIN: what it is | size: how much is left | open: why | next: the action
The date is when it was FIRST raised and never changes. ORIGIN is USER: (their own words where
they gave any) or FOUND:. size is how much is left; it is the field that disappears first when
a list is retyped, and it breaks ties within an origin, so it is not optional. It is NOT the first
ranking key - see below, where reading it as one is a named failure. Closing an item is - [x] plus
| closed: <reason>; the line stays. A SessionStart hook prints the top ranks with their age.
Rank in TENS - 10, 20, 30. An insertion is then a new number instead of a renumbering of
every line below it, which keeps a reorder out of the diff of an unrelated change.
How rank is decided, in this order:
- A
USER: item outranks every FOUND: item. Someone is waiting on the first kind and nobody is
waiting on the second, and no count changes that. Size is a tiebreak, never the first key: read
the other way round, a big internal sweep displaces the thing the user actually asked for, which
is the failure in a new costume.
- A
USER: item the USER has deferred sits below the live USER: items and still above every
FOUND: one. They asked for it and put it off; they did not stop wanting it. Record who
deferred it and when in the open: field, because a deferral with no owner reads as your
judgement a week later.
- Among items of the same origin, the bigger
size goes first. A big item is the one that never
fits into a spare moment, so it is the one that starves. An item whose size is unknown sorts
after the sized ones of its origin, and sizing it is then its own next action.
- Blocked keeps its rank. Being blocked on the user is a reason to go and ask, not a demotion.
Blocked is a value for
open:, never a third ORIGIN - there are two origins and inventing a
third puts the item in a category nothing sorts.
Never infer a date you do not have. When the source does not say when an item was first
raised, write TODAY's date with a question mark - (2026-08-31?) - or the bare word
(unknown), and say first seen here in the open: field. Both parse, and an undated item
sorts last within its rank because it makes no age claim. An estimated date is worse than an admitted unknown: age is the entire signal
that makes a long-carried item conspicuous, and a plausible guess silently resets it. The same
goes for size: size: unknown is a usable line, an invented count is not.
What belongs in it
- In flight - what is part-done, how far it got, and what state it is in right now.
- Committed, or not - uncommitted work can vanish; the reader cannot guess which they got.
- Decided, and why - choices a reader would otherwise reopen, with the reason that settled them.
- Decided against, and why - so it is not mistaken for an oversight and redone.
- Still open, untouched - ONE LINE each, pointing at
OPEN-WORK.md for the detail. Never a
summary of the backlog: a summary is the re-encoding, and re-encoding is what loses it. These two
were one heading once, and merging them is what let an undecided item sit under a heading that
reads as decided.
- Lessons for the next nap - ONE LINE each, trigger-first ("When , "),
under a heading of exactly that name. The nap captures them; this session does not.
- The exact next action - the command or edit to start with, not a direction of travel. It
must be the top-ranked open item in
OPEN-WORK.md, or the file must say plainly why a
lower-ranked one goes first. Recency is not a reason. "It is what I was working on", "it is
nearly done", "the user mentioned it most recently" and "it is small" are the four that get
written, and none of them outranks a bigger item the user is still waiting on.
- Files that matter - repo-relative PATHS, not bare filenames; inferring them is the work
this file exists to save.
- How to verify - the commands that prove the work still stands.
What does NOT belong
| Excuse for including it |
Why it is wrong |
| "The conventions are important" |
CLAUDE.md loads automatically. Restating it wastes the handover. |
| "Recent commits give context" |
git log re-derives that in one command. Say what is UNCOMMITTED. |
| "Explain how this file was produced" |
Provenance is narrative. The reader needs state, not a session story. |
| "Capture the lessons into memory first" |
A lesson is ONE LINE under ## Lessons for the next nap, and the nap captures it. An engine call at the end of a full session is where the tool gets fixed instead of the work: measured over three weeks of transcripts, that capture was the bridge from every handover into an engine fix and a plugin release shipped from whatever project the session was in. |
| "Summarise everything, to be safe" |
A handover nobody finishes reading is a handover that failed. |
Procedure
Record the lessons, do not capture them. Each durable thing this session learned goes into
the handover as ONE line under ## Lessons for the next nap, worded trigger-first, with no slug
and no engine call. Do NOT invoke bitranox:meta-self-improve here and do not run
memory_engine.py: the file is committed, so the line has reached the store's front door, and
bitranox:meta-dream-nap / bitranox:meta-dream-tree walk it in from there. A lesson about
the TOOLING (a hook, a skill, the engine) is the same one line, prefixed tooling:, and it is
NOT a reason to open the tool now.
RECONCILE the outgoing handover into OPEN-WORK.md BEFORE you overwrite it. Read the file
you are about to destroy and take every item in it that is not finished. Each one either
already has a line in OPEN-WORK.md, or you add it now with its first-raised date, its size
and its next action; an item you believe is finished gets - [x] and a reason, in the file,
not in your head. What you do not carry across goes SILENTLY, because a missing item looks
exactly like an item that was closed - and nobody diffs a handover against its predecessor
looking for absences. Being able to recover the old text from git is not the same as noticing
there was something to recover. Do this reconcile FIRST, while the outgoing file is still in
front of you: afterwards you are auditing from memory, which is the failure itself.
Add the same way anything the USER asked for in this session and did not get, in their own
words. A request that lives only in the sentence they typed is one re-ask away from being lost,
and they will not know to re-ask.
Write handover.md at the repo root, OVERWRITING whatever is there. A stale handover from an
earlier session is superseded the moment you write yours - replace it wholesale, never append to
it and never keep both. There is exactly one handover.md, and it describes one moment; two of
them, or one with two moments in it, leaves the reader deciding which half is true. Commit it
along with OPEN-WORK.md: tracking them costs one diff per session and buys a history for the
one file that is destroyed on purpose every time, so a dropped item can be recovered once
somebody notices. Check first that neither carries a secret, an internal hostname or a private
address, because a tracked file in a public repo publishes all three permanently.
End the file with its own expiry instruction:
Read this, then replace the first line with # STALE - read <date>, work continued. Do not
delete it - if this session ends badly it is the only record of where things stood.
Re-read it as the next session. Any line the repo could have told them is a line to cut.
STOP. The handover is the LAST thing you do in this session. Writing it ends the session's
work. Do NOT start a new task, resume the one you were part-way through, or "just finish" the
small thing first - not even the next action you have just written into the file. Every edit made
after the handover is work the handover does not describe, so the next session inherits a file
that is already wrong about the state it exists to report, which is worse than no handover at all.
If the user asks for something new, write the handover, stop, and let them re-ask after the clear.
Make the /clear nudge the last line, then stop. Say the handover is written, name the file,
and tell the user to type /clear. It is an instruction, not an invitation: "type /clear when
you're ready" hands back a decision nobody asked them to make. You cannot run it yourself -
built-in slash commands are not invocable by the model - and that is the half that goes missing
when the wording is rebuilt from scratch, so send it as written, changing one thing only - the
path:
Handover written to handover.md. Type /clear to start the next session - I cannot run it
for you.
handover.md there is the path slot, not a literal to copy. Send the path that reaches the file
from where the user is standing, and a bare basename only when that is unambiguous: a second
handover.md in another worktree, checkout or package makes the bare name point at somebody
else's file, and the reader cannot tell which one they opened.
Nothing follows it. Not a recap of what you just wrote, not an offer to keep going, not "let
me know if you want X first", not a question. A reply that ends by inviting more work invites it
into THIS session, which is the one thing the handover exists to prevent.
Anything you genuinely owe the user goes BEFORE that line, in one sentence. A question they asked
while you were writing is the case that matters: say you are not answering it in this session and
that they should re-ask after the clear, then send the nudge. Step 6 sends them back to re-ask,
which only works if they know the question was heard - dropping it in silence reads as ignored.
When you are the one READING a handover
Absorb it, then mark it STALE in place. Both alternatives fail: left untouched, the session
after next reads a passed moment as current; deleted, the record is gone the instant it is read, so
a crash mid-task leaves nothing saying where the work stood.
Never amend a stale handover to update it - write a NEW one and replace the file. An edited handover
holds two moments with no way to tell them apart.
Read OPEN-WORK.md too, and read it FIRST. The handover tells you where the last session
stopped; the backlog tells you what is actually worth doing, and the two answers are routinely
different. A handover's next action is one session's view of one moment, and the item that has
been waiting longest is the one least likely to be in it.
When it fires on its own
A Stop hook measures context from the transcript's last recorded usage - the real per-request
figure, not an estimate - and blocks when it crosses
min(context_handover_pct of the window, context_handover_cap).
The window is detected from the model this project has used, so nothing needs configuring. Declining
is not permanent: the next ask waits until context has grown another tenth of the window, because a
decline at 40% is "not yet" while 90% is a different question.
If it ever reports measuring MORE context than the window, the detection failed - set
context_window explicitly via bitranox:meta-memory-settings. That is reported rather than
ignored because a threshold nothing can reach looks exactly like a watcher that works.
Not the same as its neighbours
bitranox:meta-dream-nap - consolidates MEMORY after a compaction; it prunes task state as noise.
bitranox:meta-dream-tree - the periodic store consolidation, which also ends by nudging /clear.
bitranox:meta-self-improve - durable facts. The NAP runs it on the handover's lessons; this
session records them and stops.
This skill is the only one that preserves TASK state, and it runs BEFORE the wall, not after.
1---2name: meta-context-watcher3description: Use when a session's context is large enough that quality is degrading or compaction is close, when a Stop nudge reports the handover threshold was crossed, or on "write a handover", "hand this over", "context is getting full", or "let's start a fresh session". Also use when unfinished work or an unmet user request must outlive the session and belongs in the standing backlog OPEN-WORK.md, when open items keep sinking under whatever was worked on last, or on "add this to the backlog", "what is still open"4---56# Write the handover, then start clean78A long session gets worse before it gets full. Accuracy falls from roughly 300-400k tokens on a 1M9window and from about 50k on a 200k one, while Claude Code does not auto-compact until around 83% -10and compaction DISCARDS working detail rather than preserving it.1112Both numbers are dated, and neither is measured here. The accuracy figures are Chroma's 202513context-rot study, read second-hand from a summary rather than from the paper; the 83% is Claude14Code product behaviour that can move in any release. Treat them as the order of magnitude that15justifies handing over early, never as thresholds to tune against - and re-check both before16quoting either as current. Between those two points is where17a session should be handed over deliberately instead of truncated.1819## The one rule2021**Write only what the next session cannot re-derive.**2223It inherits the repository, the git history, the CLAUDE.md cascade and the memory store. Only one24thing dies with this session: what you were part-way through, and why you chose it.2526## Two files, because they have opposite lifetimes2728`handover.md` describes ONE MOMENT and is replaced wholesale every time. `OPEN-WORK.md` is the29STANDING BACKLOG and is only ever edited line by line.3031Keeping both in one file is what made the backlog rot. A handover is rewritten from what the32writing session has in mind, so every item is re-encoded from memory once per session, and it is33re-encoded in proportion to how recently it was TOUCHED. The item nobody worked loses one34attribute per rewrite: first its method, then its count, then its own heading, until it is a35clause in a sentence. Measured over one day in this repo, five tracked items - an 88-target audit36the user had asked for, a reviewer pass, 206 unframed bodies, 57 flagged candidates, 5 queued37contributions - went from named bullets with counts to absent, with no line saying any of them had38been closed.3940| Goes in `handover.md` | Goes in `OPEN-WORK.md` |41|----------------------------------------------|-------------------------------------------------|42| what is part-done RIGHT NOW, and its state | anything not finished that outlives the session |43| what is uncommitted | anything the user asked for and has not got |44| decisions this session took, with the reason | work that is blocked, and on whom |45| the one next action | the ranked list that next action is drawn from |4647`OPEN-WORK.md` sits at the repo root, tracked, one item per line:4849```50- [ ] (YYYY-MM-DD) [rank] ORIGIN: what it is | size: how much is left | open: why | next: the action51```5253The date is when it was FIRST raised and never changes. `ORIGIN` is `USER:` (their own words where54they gave any) or `FOUND:`. `size` is how much is left; it is the field that disappears first when55a list is retyped, and it breaks ties within an origin, so it is not optional. It is NOT the first56ranking key - see below, where reading it as one is a named failure. Closing an item is `- [x]` plus57`| closed: <reason>`; the line stays. A SessionStart hook prints the top ranks with their age.5859**Rank in TENS** - 10, 20, 30. An insertion is then a new number instead of a renumbering of60every line below it, which keeps a reorder out of the diff of an unrelated change.6162**How rank is decided, in this order:**63641. A `USER:` item outranks every `FOUND:` item. Someone is waiting on the first kind and nobody is65 waiting on the second, and no count changes that. Size is a tiebreak, never the first key: read66 the other way round, a big internal sweep displaces the thing the user actually asked for, which67 is the failure in a new costume.682. A `USER:` item the USER has deferred sits below the live `USER:` items and still above every69 `FOUND:` one. They asked for it and put it off; they did not stop wanting it. Record who70 deferred it and when in the `open:` field, because a deferral with no owner reads as your71 judgement a week later.723. Among items of the same origin, the bigger `size` goes first. A big item is the one that never73 fits into a spare moment, so it is the one that starves. An item whose size is unknown sorts74 after the sized ones of its origin, and sizing it is then its own next action.754. Blocked keeps its rank. Being blocked on the user is a reason to go and ask, not a demotion.76 Blocked is a value for `open:`, never a third `ORIGIN` - there are two origins and inventing a77 third puts the item in a category nothing sorts.7879**Never infer a date you do not have.** When the source does not say when an item was first80raised, write TODAY's date with a question mark - `(2026-08-31?)` - or the bare word81`(unknown)`, and say `first seen here` in the `open:` field. Both parse, and an undated item82sorts last within its rank because it makes no age claim. An estimated date is worse than an admitted unknown: age is the entire signal83that makes a long-carried item conspicuous, and a plausible guess silently resets it. The same84goes for `size`: `size: unknown` is a usable line, an invented count is not.8586## What belongs in it8788- **In flight** - what is part-done, how far it got, and what state it is in right now.89- **Committed, or not** - uncommitted work can vanish; the reader cannot guess which they got.90- **Decided, and why** - choices a reader would otherwise reopen, with the reason that settled them.91- **Decided against, and why** - so it is not mistaken for an oversight and redone.92- **Still open, untouched** - ONE LINE each, pointing at `OPEN-WORK.md` for the detail. Never a93 summary of the backlog: a summary is the re-encoding, and re-encoding is what loses it. These two94 were one heading once, and merging them is what let an undecided item sit under a heading that95 reads as decided.96- **Lessons for the next nap** - ONE LINE each, trigger-first ("When <situation>, <directive>"),97 under a heading of exactly that name. The nap captures them; this session does not.98- **The exact next action** - the command or edit to start with, not a direction of travel. **It99 must be the top-ranked open item in `OPEN-WORK.md`, or the file must say plainly why a100 lower-ranked one goes first.** Recency is not a reason. "It is what I was working on", "it is101 nearly done", "the user mentioned it most recently" and "it is small" are the four that get102 written, and none of them outranks a bigger item the user is still waiting on.103- **Files that matter** - repo-relative PATHS, not bare filenames; inferring them is the work104 this file exists to save.105- **How to verify** - the commands that prove the work still stands.106107## What does NOT belong108109| Excuse for including it | Why it is wrong |110|-----------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|111| "The conventions are important" | CLAUDE.md loads automatically. Restating it wastes the handover. |112| "Recent commits give context" | `git log` re-derives that in one command. Say what is UNCOMMITTED. |113| "Explain how this file was produced" | Provenance is narrative. The reader needs state, not a session story. |114| "Capture the lessons into memory first" | A lesson is ONE LINE under `## Lessons for the next nap`, and the nap captures it. An engine call at the end of a full session is where the tool gets fixed instead of the work: measured over three weeks of transcripts, that capture was the bridge from every handover into an engine fix and a plugin release shipped from whatever project the session was in. |115| "Summarise everything, to be safe" | A handover nobody finishes reading is a handover that failed. |116117## Procedure1181191. **Record the lessons, do not capture them.** Each durable thing this session learned goes into120 the handover as ONE line under `## Lessons for the next nap`, worded trigger-first, with no slug121 and no engine call. Do NOT invoke `bitranox:meta-self-improve` here and do not run122 `memory_engine.py`: the file is committed, so the line has reached the store's front door, and123 `bitranox:meta-dream-nap` / `bitranox:meta-dream-tree` walk it in from there. A lesson about124 the TOOLING (a hook, a skill, the engine) is the same one line, prefixed `tooling:`, and it is125 NOT a reason to open the tool now.1262. **RECONCILE the outgoing handover into `OPEN-WORK.md` BEFORE you overwrite it.** Read the file127 you are about to destroy and take every item in it that is not finished. Each one either128 already has a line in `OPEN-WORK.md`, or you add it now with its first-raised date, its size129 and its next action; an item you believe is finished gets `- [x]` and a reason, in the file,130 not in your head. What you do not carry across goes SILENTLY, because a missing item looks131 exactly like an item that was closed - and nobody diffs a handover against its predecessor132 looking for absences. Being able to recover the old text from git is not the same as noticing133 there was something to recover. Do this reconcile FIRST, while the outgoing file is still in134 front of you: afterwards you are auditing from memory, which is the failure itself.135136 Add the same way anything the USER asked for in this session and did not get, in their own137 words. A request that lives only in the sentence they typed is one re-ask away from being lost,138 and they will not know to re-ask.1391403. **Write `handover.md` at the repo root, OVERWRITING whatever is there.** A stale handover from an141 earlier session is superseded the moment you write yours - replace it wholesale, never append to142 it and never keep both. There is exactly one `handover.md`, and it describes one moment; two of143 them, or one with two moments in it, leaves the reader deciding which half is true. Commit it144 along with `OPEN-WORK.md`: tracking them costs one diff per session and buys a history for the145 one file that is destroyed on purpose every time, so a dropped item can be recovered once146 somebody notices. Check first that neither carries a secret, an internal hostname or a private147 address, because a tracked file in a public repo publishes all three permanently.1484. **End the file with its own expiry instruction:**149150 > Read this, then replace the first line with `# STALE - read <date>, work continued`. Do not151 > delete it - if this session ends badly it is the only record of where things stood.1521535. **Re-read it as the next session.** Any line the repo could have told them is a line to cut.1546. **STOP. The handover is the LAST thing you do in this session.** Writing it ends the session's155 work. Do NOT start a new task, resume the one you were part-way through, or "just finish" the156 small thing first - not even the next action you have just written into the file. Every edit made157 after the handover is work the handover does not describe, so the next session inherits a file158 that is already wrong about the state it exists to report, which is worse than no handover at all.159 If the user asks for something new, write the handover, stop, and let them re-ask after the clear.1607. **Make the `/clear` nudge the last line, then stop.** Say the handover is written, name the file,161 and tell the user to type `/clear`. It is an instruction, not an invitation: "type `/clear` when162 you're ready" hands back a decision nobody asked them to make. You cannot run it yourself -163 built-in slash commands are not invocable by the model - and that is the half that goes missing164 when the wording is rebuilt from scratch, so send it as written, changing one thing only - the165 path:166167 > Handover written to `handover.md`. Type `/clear` to start the next session - I cannot run it168 > for you.169170 `handover.md` there is the path slot, not a literal to copy. Send the path that reaches the file171 from where the user is standing, and a bare basename only when that is unambiguous: a second172 `handover.md` in another worktree, checkout or package makes the bare name point at somebody173 else's file, and the reader cannot tell which one they opened.174175 **Nothing follows it.** Not a recap of what you just wrote, not an offer to keep going, not "let176 me know if you want X first", not a question. A reply that ends by inviting more work invites it177 into THIS session, which is the one thing the handover exists to prevent.178179 Anything you genuinely owe the user goes BEFORE that line, in one sentence. A question they asked180 while you were writing is the case that matters: say you are not answering it in this session and181 that they should re-ask after the clear, then send the nudge. Step 6 sends them back to re-ask,182 which only works if they know the question was heard - dropping it in silence reads as ignored.183184## When you are the one READING a handover185186Absorb it, then **mark it STALE in place**. Both alternatives fail: left untouched, the session187after next reads a passed moment as current; deleted, the record is gone the instant it is read, so188a crash mid-task leaves nothing saying where the work stood.189190Never amend a stale handover to update it - write a NEW one and replace the file. An edited handover191holds two moments with no way to tell them apart.192193**Read `OPEN-WORK.md` too, and read it FIRST.** The handover tells you where the last session194stopped; the backlog tells you what is actually worth doing, and the two answers are routinely195different. A handover's next action is one session's view of one moment, and the item that has196been waiting longest is the one least likely to be in it.197198## When it fires on its own199200A `Stop` hook measures context from the transcript's last recorded usage - the real per-request201figure, not an estimate - and blocks when it crosses202`min(context_handover_pct` of the window, `context_handover_cap)`.203204The window is detected from the model this project has used, so nothing needs configuring. Declining205is not permanent: the next ask waits until context has grown another tenth of the window, because a206decline at 40% is "not yet" while 90% is a different question.207208If it ever reports measuring MORE context than the window, the detection failed - set209`context_window` explicitly via `bitranox:meta-memory-settings`. That is reported rather than210ignored because a threshold nothing can reach looks exactly like a watcher that works.211212## Not the same as its neighbours213214- `bitranox:meta-dream-nap` - consolidates MEMORY after a compaction; it prunes task state as noise.215- `bitranox:meta-dream-tree` - the periodic store consolidation, which also ends by nudging `/clear`.216- `bitranox:meta-self-improve` - durable facts. The NAP runs it on the handover's lessons; this217 session records them and stops.218219This skill is the only one that preserves TASK state, and it runs BEFORE the wall, not after.