--- name: session-handoff version: 0.3.2 description: "Sliding handoff between CC sessions via .tasks/NEXT_SESSION.md. Read on session start: orient agent, ask user before action. Write on session-end phrase or substantive commit. Session-end phrases: «завершаем сессию», «сворачиваемся», «закругляемся», «wrap up session», «end session», «we're done for now». Trigger-line in CLAUDE.md: `session handoff: read on start, write on end`. Skip task-zone phrases: «закрываем эту таску», «pause», «отбой», «разбегаемся»." --- # session-handoff Sliding handoff prompt между CC сессиями. На старте — читает `.tasks/NEXT_SESSION.md`, ориентирует агента и спрашивает user'а перед действиями. При substantive commit'е или session-end фразе — перезаписывает handoff для следующей сессии. Sliding overwrite: один файл, история — через `git log -p .tasks/NEXT_SESSION.md`. Forward-looking, не timeline: handoff = связка новых вещей конкретно для следующего разворота, не overview всего проекта. STATUS.md / MEMORY.md / `.wiki/log.md` остаются авторитетными для своего scope'а. Дизайн-источник: `~/projects/.workshop/.archive/2026-05-24-session-handoff-skill.md` (Round 1 design + Round 2 resolved Q1–Q10). ## When to use **Read mode (session start):** - CLAUDE.md проекта содержит trigger-строку `session handoff: read on start, write on end`. - Файл `.tasks/NEXT_SESSION.md` существует. **Write mode (session end / substantive commit):** - User'ская фраза из whitelist: - русский: «завершаем сессию», «сворачиваемся», «закругляемся» - английский: «wrap up session», «end session», «we're done for now» - ИЛИ — agent только что сделал substantive commit. Эвристика: - prefix НЕ в (`meta:`|`docs:`|`style:`|`chore:`|`fix typo`) - AND (body length > 200 символов OR files changed > 3) - Плюс: **первый** non-trivial commit сессии — всегда триггерит, даже если ниже порога (старт работы = context shift). - **Optional**: substantive-commit detection может быть автоматизирован harness-side через PostToolUse hook — см. `hooks/README.md` для opt-in инструкций. С enabled hook'ом первая часть becomes deterministic (parser-side, не behavioral memory). **Skip (false-positive guards):** - «закрываем эту таску» — task close, не session. Это zone `using-tasks`. - «pause», «приостанови» — task-pause, не session-end. - «отбой», «разбегаемся» — слишком broad, может относиться к другому контексту. - «сейчас завершу одну задачу и тогда поговорим» — частичное завершение. - Не-git папка, или `.tasks/` отсутствует — silent exit. - CLAUDE.md проекта НЕ содержит trigger-строку — silent exit. При неоднозначности — **ASK**, не угадывать: «закрываем сессию или таску?» ## Inputs **Read mode:** - `.tasks/NEXT_SESSION.md` — sliding handoff, должен существовать. - Текущая дата (для staleness check против `_last_updated_`). **Write mode:** - `git log --oneline -5` — последние commits сессии. - `.tasks/STATUS.md` — open треки + 🔴 active task'и (для mid-task capture). - Контекст сессии — pending user-decisions, waiting permissions, memory updates, preemptive guards. ## Steps ### Read mode 1. **Detect.** Проверить что `.tasks/NEXT_SESSION.md` существует. Нет — silent exit. 2. **Staleness check.** Прочитать frontmatter `_last_updated_`. Возраст > 7 дней → отметить user'у: ``` handoff от (N дней назад) — возможно устарел. Оверrайдить или продолжить? ``` Дождаться ответа перед продолжением. 3. **Summarize.** Прочитать тело handoff'а — 5 секций (recent commits / open треки / спроси user'а / не делать / memory updates). 4. **Orient.** Пересказать user'у одним блоком: «прошлая сессия предложила X (open треки + ask-items + don't-items + memory updates). Делаем?» 5. **Wait.** Не делать никаких действий до подтверждения user'ом. Default = orient + ask, **никакого auto-execute**. ### Write mode 1. **Scope check.** Это текущий проект (cwd с `.tasks/`). Никаких global мутаций, никаких других проектов. 2. **Mid-task capture.** Если в `.tasks/STATUS.md` есть 🔴 active task — захватить: ``` left mid-task: where_stopped: <текст из STATUS.md> ``` 3. **Compose content.** Собрать `.tasks/NEXT_SESSION.md`: ```markdown --- _last_updated_: session_id: --- # Next session handoff ## Recent commits - : (3–5 последних) ... ## Open треки | Трек | Готовность | Entry-point | |---|---|---| | ... | ... | ... | ## Спроси user'а - - ## Не делать (preemptive guards) - - ## Memory updates за сессию - <что нового сохранилось / обновилось> ``` Пустую секцию — оставить заголовок + пометка `(нет на этом раунде)`. Чтобы next агент видел: не забыто, а пусто. 4. **Sliding overwrite.** `Write` поверх `.tasks/NEXT_SESSION.md` (предыдущее содержимое НЕ архивируется в `.archive/handoff-*.md` — sliding contract). История восстанавливается через `git log -p .tasks/NEXT_SESSION.md`. 5. **Stage.** `git add .tasks/NEXT_SESSION.md` — попадает в следующий commit сессии (или в текущий, если запись была вызвана session-end фразой). ## Failure modes - **CLAUDE.md без trigger-строки** → silent exit, не вмешиваться. Скил project-opt-in. - **Не git-repo / `.tasks/` отсутствует** → silent exit. Скил требует обоих условий. - **`.tasks/NEXT_SESSION.md` отсутствует** в read mode → silent exit (первая сессия проекта, нечего читать). - **Неоднозначная фраза** («закругляемся» в контексте отдельной таски, а не сессии) → ASK user'а «закрываем сессию или таску?», не угадывать. - **Secret detected.** Содержимое handoff'а матчит паттерны секретов (`AKIA...`, `sk-...`, `ghp_...`, `ssh-rsa`, `BEGIN PRIVATE KEY`, JWT в обычном виде, `password=`/`token=` без obfuscation) → **abort write**, не записывать. Файл идёт в git — не место для credentials. Сообщить user'у с указанием подозрительной строки, дать дочистить контекст руками. - **Stale handoff (> 7 дней)** в read mode → не silent, **спросить** user'а оверrайдить или продолжить (Q9 resolved 2026-05-24). - **Mid-task без STATUS.md entry** в write mode → записать handoff без mid-task секции, не блокировать. ## Side effects - Записывает / перезаписывает `.tasks/NEXT_SESSION.md` (project-scope only). - Файл git-tracked, попадает в commit (либо вместе с session work, либо отдельным commit'ом). - Никаких других файлов: `.archive/` не плодим (sliding), `.wiki/log.md` не дёргаем (это не promoted event), `STATUS.md` не правим. - Никаких global мутаций, никаких других проектов, никаких user-level config writes. ## What NOT to do - **Не auto-execute** действия из read handoff'а. Default = orient + ask. Прошлая сессия могла ошибиться; user agency сохраняем. - **Не append-with-archive.** Sliding only. `.archive/handoff-.md` создавать не нужно — это создавало бы N artefact'ов, которые user не хочет. История — через git log. - **Не триггерить на task-zone phrases** («закрываем эту таску», «pause»), broad farewells («отбой», «разбегаемся»), partial completions («сейчас завершу одну задачу и тогда поговорим»). - **Не писать секреты** в handoff. Если контент матчит secret-patterns — abort, попросить user'а вычистить контекст. - **Не на каждом commit'е.** Только substantive (см. эвристика в When to use). Trivial `chore: bump dep` или `docs: typo` НЕ триггерят, иначе handoff'ы шумят. - **Не дублировать STATUS.md / MEMORY.md.** Handoff = **forward-looking связка** новых вещей для следующего разворота, не overview всего проекта. Open треки — да, но как мостик «вот где остановились», не как replica STATUS.md. - **Не cross-project.** Per-project scope. «Завершаем сессию» в `.workshop/` не трогает `.admin/` и наоборот. - **Не зависеть от harness `SessionEnd` hook** — такого hook'а в Claude Code нет (есть только `SessionStart`, `UserPromptSubmit`, `PreToolUse`, `PostToolUse`, `Stop`, `Notification`). Триггер — фраза или substantive-commit detection в самом agent flow. - **Не считать handoff авторитетным** на стороне читателя. Это рекомендация прошлой сессии, не директива. User может override любую её часть.