Комментарии 2
Снимал ту же механику с другой стороны - переносил одну беседу между claude и codex, и там всплыло то, чего у вас в разборе нет.
Для claude --resume <uuid> ключ проекта в пути ~/.claude/projects/<ключ>/ может быть любым, cwd на это не влияет вообще: на claude 2.1.233 файл подхватывался из папки с выдуманным ключом. То-есть слаг от cwd нужен вашему слою и списку /resume внутри проекта, а не самому восстановлению по uuid.
Обязательных полей в jsonl три: uuid у записей, сходящаяся цепочка parentUuid и ISO-timestamp на последней записи. Без timestamp получаете No conversation found при лежащем на месте файле - самая обидная ошибка, потому что формат вроде правильный.
И контекст модель собирает обратным обходом parentUuid, а не порядком строк в файле. Для ваших дублей это важно: если склеить две копии одной сессии по uuid, а цепочка не сходится, прочитается не то, что вы ожидаете, хотя записи все на месте.
Проверял кодовым словом: говоришь его в сессии на одной стороне, поднимаешь на другой и просишь назвать. Глазами перенос не проверяется никак - модель отвечает уверенно и с пустым контекстом.
Огромное спасибо за такой ценный комментарий! Сразу полез проверять и половина написанного оказалась про мою же дыру, которую я не видел.
Про обратный обход parentUuid подтверждаю на своих данных: в текущем транскрипте 3665 записей с uuid, а цепочка от последней записи назад проходит только 1764 из них. Меньше половины файла, остальное — боковые ветки. Я почему-то считал, что читается всё подряд, и нигде это не проверял.
А вот дальше начинается неприятное лично для меня. У меня в .gitattributes транскрипты помечены merge=union — ровно чтобы при расхождении двух машин ничего не потерялось. Смоделировал ваш сценарий: общий корень, две машины продолжили сессию независимо, git склеил. Файл валиден, все записи на месте, ни одна не пропала. Только восстановление идёт от последней записи назад, и ветка первой машины в контекст не попадает вообще. Я проверял это кодовым словом в двух ветках — читается только одно из двух.
Хуже того, моя же защита от дублей сверяет копии по множеству uuid: если все записи одной копии есть в другой, копия считается безопасной для удаления. Достижимость по цепочке при этом не проверяется. То есть формально я ничего не теряю, а фактически могу оставить файл, из которого читается не то, что человек ожидает. Пойду чинить: проверять не только совпадение uuid, но и то, что цепочка от последней записи сходится.
И спасибо отдельно за метод с кодовым словом. У меня в проверках 24 теста, и все они подтверждают, что файл доехал и лежит по нужному пути — но ни один не подтверждает, что модель на другой стороне действительно видит содержание разговора. Разница до вашего комментария была для меня неочевидной, а вы правы: пустой контекст выглядит ровно как уверенный ответ.
Подскажите, а вы у себя эту ситуацию как-то решили? Интересно, к чему пришли: чинить цепочку при склейке (перевешивать parentUuid у одной из веток), выбирать одну ветку и вторую откладывать в сторону, или вообще отказаться от автоматического слияния и разруливать руками? Может, вы нашли вариант поизящнее.

Одна сессия Claude Code на нескольких машинах: что ломается, если просто синхронизировать папку