Сесія має властивість закінчуватись не там, де закінчена робота. Контекст добіг ліміту, компакція зжерла половину встановленого, headless-ран відпрацював і вийшов — а задача ще відкрита. Наступний чат стартує з нуля і першим ділом перепитує те, що я вже з'ясував годину тому. Це і є дірка, яку затуляє родина handoff: не «збережи чат», а «дай наступному продовжити, не переводячи вдруге ті самі факти».
Скілів три, і поділ між ними — не косметика. handoff — сам контракт: що таке хендофф, яка в нього схема, де межа між durable-контекстом і session-станом. Він нічим не володіє і нічого не пише. handoff-prompt — той, хто пише, і єдиний власник сховища. handoff-pickup — той, хто читає і одразу продовжує. Кожен знає рівно свою частину, і це головне, що я переробив у другій версії.
Задача
Хендофф корисний рівно настільки, наскільки успішник може НЕ повторювати мою роботу. Тому документ ділиться по осях, які легко переплутати й дорого злити в купу: durable-контекст проти session-стану, verified-факти проти гіпотез, зроблене проти невирішеного, і один Next action, з якого наступний починає, не думаючи. Гіпотеза, записана як факт, — це міна: успішник бере її на віру й будує зверху.
Складне тут не схема — схему видно в будь-якому шаблоні. Складна дисципліна на двох краях. З одного боку — не звалити в файл увесь транскрипт: дамп сесії це не передача контексту, а відкладений re-read, і наступний чат усе одно перечитує все з нуля, лише тепер з диска. З іншого — не вірити рядку RESULT: ok на слово: для рану це означає піти в run-dir, глянути out.log і exit_code, прогнати Verify з task.md, а не переписати чужий вердикт собі в актив.
Що змінилось у v2
v1 (09.09) була парою handoff-prompt + handoff-pickup, спакованою разом. Producer писав HANDOFF-{slug}-{дата}.md у docs/ проєкту — але тільки якщо там уже був docs/-конвент; інакше віддавав pickup-промпт просто в чат. Consumer читав той файл напряму. Робоче, але шлях до сховища producer фактично вгадував з імені проєкту, а consumer знав фізичне місце файлу — тобто був прибитий до чужої приватної деталі.
| Що було (v1) | Що стало (v2) | Навіщо |
|---|---|---|
| шлях виводився з конвенту проєкту, часом вгадувався | handoff-prompt — єдиний owner, шукає канонічне сховище discovery-єм із доказів на вузлі (config/registry понад усталене місце) |
нема доказів — повертає storage_unresolved, а не вигаданий каталог; шлях перестав бути здогадом |
| pickup читав файл напряму з диска | pickup ходить лише через public interface owner-а (latest / read / format), у сховище руками — ніколи |
фізичне місце стало приватним; консюмер не знає і не мусить знати, де лежить файл |
| два скіли, контракт розлитий між ними | зверху чистий батько handoff — сам контракт документа, без володіння станом |
правила можна прочитати, нічого не записуючи; producer і consumer лишаються тонкими |
Плюс те, чого в v1 не було зовсім: read-back. Після запису handoff-prompt одразу читає файл назад і звіряє з тим, що збирався записати. Розбіжність або відсутній read-back — це провал, не «записалось, мабуть, ок». Дешева паранойя, яка ловить heredoc, що проковтнув $, і напівзаписаний файл, поки він ще не став чиїмось верифікованим контекстом.
Ставка
Беру — зчіпку чат↔ран↔чат саме під власні cc-рани. Headless-ран вийшов, наступна сесія піднімає його з run-dir (out.log, exit_code, Verify із task.md), і я не переказую вручну, на чому спинився. Конкретна задача, під яку це: багатосесійна робота на touchviz і nightshift, де контекст рветься посеред таска. Умова, за якої рішення зміниться: якщо discovery почне частіше помилятись у виборі канонічного сховища, ніж я ловлю це оком, — вертаю жорсткий per-project docs/ і не вдаю, що вивів шлях чесно.
Не беру — хендофф як дослівний транскрипт. Це найдорожча спокуса жанру: здається, що більше збережеш — тим безпечніше, а насправді роздутий дамп лише переносить re-read у наступну сесію. Беру рівно verified-факти з коротким evidence і один Next action; решту скидаю свідомо.
Переоцінене — discovery замість хардкод-шляху. У контракті виглядає стрункіше, ніж коштує: ціна — зайвий раунд пошуку на вузлі при кожному записі, а виграш проявляється тільки коли в проєкта два конкуруючі місця — локальний docs/HANDOFF-* і спільний /root/references/handoffs/. Для проєкту з одним місцем це церемонія заради церемонії. Тримаю її, бо в мене саме два місця й вони реально розходяться, — але не продаю це як загальну перемогу над хардкодом.
Що з цього мацане
Чесно розмежую, бо тон упевненого голосу легко видати за досвід, якого нема.
v1 має задокументований живий цикл. touchviz: docs/HANDOFF-recv-mode-20260909.md → наступна сесія підхопила, закрила суперечність (auto-rejoin прибрано) і написала docs/HANDOFF-code-pairing-20260909.md. Це працювало руками, не на папері.
Спільне сховище — теж не гіпотеза. /root/references/handoffs/ тримає 23 хендофи від 11.09 до 23.09; місце, яке v2-discovery і має знаходити, реально заповнене. Але писав його попередній механізм, не нова родина.
А от сам v2 я інтегрував у свій skills-репо сьогодні — комітами за кілька годин до цієї статті. Жодного повного циклу «написав контрактом v2 → підхопив контрактом v2 → закрив» я ще не прогнав. Кажу прямо: на папері контракт стрункий, у бою — нуль пробігів. Перша реальна передача через нього буде першим тестом, і це точно не ця стаття.
ЧОМУ НАСТУПНИЙ ЧАТ УСЕ ЗНАЄ
(рівні гарантії передачі контексту)
┌──────────────────────────────────────┐
│ [1] Канонічна схема на 13 заголовків │
│ durable/session, verified/hypo │
└──────────────────┬───────────────────┘
│ owner пише один документ
▼
┌──────────────────────────────────────┐
│ [2] Discovery канонічного сховища │
│ з доказів на вузлі, не хардкод │
└──────────────────┬───────────────────┘
│ read-back
▼
┌──────────────────────────────────────┐
│ [3] Звірка записаного з наміром │
│ розбіжність = провал, не «ок» │
└──────────────────┬───────────────────┘
│ pickup лише через public interface
▼
┌──────────────────────────────────────┐
│ [4] «Продовж роботу над …» — │
│ вставляю в новий чат руками │
└──────────────────────────────────────┘
Три рівні інженерної гарантії існують, щоб четвертий лишався тим, чим був завжди: людиною, яка копіпастить рядок у порожній чат.