Survive a crash, and a reboot after one

kill, a closed window, a reboot: no SessionEnd arrives and the state file
stays. Each case was tried rather than reasoned about, and one of the
three was broken.

A killed session was already handled -- the process is gone, so the file
and its lock are removed within the 20 s liveness tick. An interrupted
hook write left its temporary file behind forever; those are now swept
once they are five minutes old, which is late enough that a hook part-way
through writing one does not lose the update.

The reboot case was the broken one. State files outlive a reboot and pids
are handed out afresh, so "does /proc/<pid> exist" only answers "is some
process wearing that number". Verified by giving an unrelated live process
the pid of a dead session: the ghost sat in the panel as a session waiting
for input, and would have stayed there forever, asking for an answer
nobody could give. The pid is now pinned to the process start time from
/proc/<pid>/stat, recorded when the state is written and compared when it
is read.

Files written before that field existed compare only on existence, as
before, so a session open across the upgrade is not evicted.

An abandoned flock needed nothing: the kernel drops it when the holder
dies, so there is no deadlock to recover from.
This commit is contained in:
av
2026-08-09 19:43:31 +03:00
parent ca42d69704
commit 75bfe77950
5 changed files with 130 additions and 18 deletions
+25 -5
View File
@@ -197,13 +197,33 @@ dump-layout`: рабочий каталог сессии сопоставляе
Если zellij не используется, выключите в настройках: он стоит одного процесса
раз в пару минут.
## Аварийное завершение
`kill`, закрытое окно, перезагрузка — `SessionEnd` не приходит, и файл остаётся.
Разбор завалов проверен на каждом случае отдельно:
| Что осталось | Что с этим происходит |
|---|---|
| файл убитой сессии | процесса нет — файл и его `.lock` удаляются в пределах 20 с |
| файл из прошлой загрузки | pid сверяется по времени старта процесса, а не только по наличию |
| оборванная запись хука (`.tmp`) | удаляется, когда старше пяти минут |
| незакрытый `flock` | ядро снимает блокировку при смерти процесса — тупика не бывает |
Сверка по времени старта — не перестраховка. Файлы состояния переживают
перезагрузку, а pid после неё раздаются заново: проверка «есть ли `/proc/<pid>`»
отвечает лишь «какой-то процесс с таким номером есть». Без этой сверки сессия,
погибшая в аварии, висела бы в панели вечно, требуя ответа, которого некому дать.
Проверено подстановкой постороннего живого процесса на тот же pid.
Порог в пять минут для `.tmp` тоже осмысленный: хук может писать такой файл
прямо сейчас, и удаление свежего стоило бы потерянной записи.
Если хук вообще не смог опознать процесс claude, он пишет pid 0 — «неизвестно»,
что никогда не путается с «мёртв»; такие записи истекают по возрасту, через
36 часов.
## Известные шероховатости
- **Протухшие сессии.** Убитый терминал не присылает `SessionEnd`. Живость
перепроверяется каждые 20 с по `/proc/<pid>`, файл удаляется — убитая сессия
исчезает в пределах этого окна, а не висит вечно. Если хук вообще не смог
опознать процесс claude, он записывает pid 0 — «неизвестно», что никогда не
путается с «мёртв», — и такие записи истекают по возрасту, через 36 часов.
- **`Stop` не отличает «закончил» от «сдался».** Оба читаются как «ждёт ввода»,
и решение для вас в обоих случаях одно и то же.