Summary
handoff archives the capture buffer by moving DATA/buffer/session-<id>.md into DATA/buffer/archive/. When handoff runs twice in one session — which the skill explicitly supports ("HANDOFF.md updates are idempotent, so an early run is simply refined by the next") — the second run destroys the first run's archived buffer.
The mechanism is straightforward: the session id is stable across the whole session, so after the first archive, capture recreates buffer/session-<id>.md with the same filename. The second archive then moves that file onto the existing archived copy, overwriting it.
Observed
Two handoffs in one session, ~40 minutes apart:
handoff #1 -> archive/session-<id>.md 322 lines (06:44 -> 10:50)
handoff #2 -> archive/session-<id>.md 71 lines (10:50 -> 11:26) <- clobbered
After the second run, the archived file's first entry is timestamped 10:50:59. Everything captured before that is gone from the archive. The archive directory's file count did not change, so nothing about the outcome looks unusual.
Why it matters
This contradicts the skill's own stated guarantee, in the step that performs the move:
Archive only, never delete: an archived buffer is the recovery path if a distillation later turns out to have missed something.
After a same-session second handoff, that recovery path no longer exists for the earlier window. The distilled session log survives, so nothing is lost that was distilled — but the raw record exists precisely to recover what distillation missed, and that is exactly the part destroyed. It also fails silently: no error, no warning, and the archive count is unchanged.
The situation is not exotic. Any session that runs handoff at a natural checkpoint and again at the end hits it, and the skill encourages exactly that pattern.
Suggested fix
Make the archive move collision-safe. Either:
- Suffix on collision —
archive/session-<id>.md → archive/session-<id>.<n>.md (or a timestamp) when the target already exists. Preserves both windows as separate files.
- Append instead of move — concatenate the new buffer onto the existing archived file. Keeps one file per session, which matches the "one session is one file" model stated in Phase 1, and keeps the windows contiguous.
(2) is probably truer to the existing model, but either satisfies the guarantee. The key property is that mv must never be able to land on an existing path unconditionally.
Acceptance criteria
Summary
handoffarchives the capture buffer by movingDATA/buffer/session-<id>.mdintoDATA/buffer/archive/. When handoff runs twice in one session — which the skill explicitly supports ("HANDOFF.md updates are idempotent, so an early run is simply refined by the next") — the second run destroys the first run's archived buffer.The mechanism is straightforward: the session id is stable across the whole session, so after the first archive, capture recreates
buffer/session-<id>.mdwith the same filename. The second archive then moves that file onto the existing archived copy, overwriting it.Observed
Two handoffs in one session, ~40 minutes apart:
After the second run, the archived file's first entry is timestamped
10:50:59. Everything captured before that is gone from the archive. The archive directory's file count did not change, so nothing about the outcome looks unusual.Why it matters
This contradicts the skill's own stated guarantee, in the step that performs the move:
After a same-session second handoff, that recovery path no longer exists for the earlier window. The distilled session log survives, so nothing is lost that was distilled — but the raw record exists precisely to recover what distillation missed, and that is exactly the part destroyed. It also fails silently: no error, no warning, and the archive count is unchanged.
The situation is not exotic. Any session that runs handoff at a natural checkpoint and again at the end hits it, and the skill encourages exactly that pattern.
Suggested fix
Make the archive move collision-safe. Either:
archive/session-<id>.md→archive/session-<id>.<n>.md(or a timestamp) when the target already exists. Preserves both windows as separate files.(2) is probably truer to the existing model, but either satisfies the guarantee. The key property is that
mvmust never be able to land on an existing path unconditionally.Acceptance criteria