| Сторож | Q1 — та ли дверь? | Q2 — что сторожит? | Вердикт | Улика |
|---|---|---|---|---|
MCP Daemons Watchdogmcp_daemons_watchdog_ZBOOKG8-2023PAL.cmd | БЫЛО: нет. Одна TCP-проба; дверь потребителя (реальный MCP-вызов) не трогалась | БЫЛО: факт запуска. Результат перезапуска не проверялся вообще | ВРАЛ / починено | ВРАЛ. Починено 29.08. ЗАМЕР: 31 строка «restarted» подряд за 28-29.08, порт 8765 не поднялся НИ РАЗУ, наружу ушло 0 тревог. Корень: шардирование 05.08 заморозило узел за день до общей починки 06.08. Настоящая причина смерти демона — мёртвая авторизация Telegram-сессии, её сторож не лечит в принципе. СТАЛО: две пробы + HTTP-проба + проверка перезапуска через 10с + счётчик подряд-неудач + эскалация в 03 после третьей. Break-тест пройден, эскалация доставлена (message_id 63137). |
Claude Git Daemon Serve Watchdoggit_daemon_serve_watchdog.cmd | ДА. Делает настоящий git ls-remote — ту же операцию, что и потребитель | Содержимое ответа (readiness), а не занятость порта | честный | ЭТАЛОН. В шапке прямым текстом: «порт-only = false-green», поэтому пробует реальный fetch и убивает залипшего держателя порта. Класс «слушает, но не обслуживает» закрыт 29.06. |
output_freshnessoutput_freshness.py (через housekeeping_daily) | ДА. Смотрит артефакт у ПОТРЕБИТЕЛЯ, а не факт запуска писателя | ВОЗРАСТ ВЫХОДА, ровно по §5.5 | честный | ЭТАЛОН и сейчас честно КРАСНЫЙ: 3 из 15 выходов протухли (search-catalog-weekly — 20 суток; у fb-diary-writer и content-drain-writer нет stamp-файлов). Прибор работает; неразобранное красное — долг владельцев, не дефект сторожа. |
TG Channels Check Hourlytg_channels_check.py | ЧАСТИЧНО и ЧЕСТНО: Telethon-рельса пробуется настоящим вызовом; MCP скриптом непробиваем в принципе, и это написано в шапке | Свежесть FATAL в логе MCP (окно 48ч) + живой процесс | граница метода | Не врёт: сам объявляет границу («настоящий вердикт MCP даёт только скилл /tg-check в сессии»). НО побочно от находки №1: mcp_errors.log не менялся с 14.08 при мёртвом демоне — «нет свежих фаталов» здесь значит «демон не доживает до ошибки», а не «всё хорошо». Дыру закрывает починенный сторож MCP. |
Claude-Desktop-Watchdogclaude_desktop_watchdog.ps1 | ДА (после починки 26.08) | Возраст выхода + state-файл; есть тормоз MAX_REVIVES_6H | честный | Класс «сторож судит прокси, а не предмет» разобран 26.08 (Breakage-Journal), тест _test_desktop_watchdog_freshness.py 13/13 + два мутанта. Именно отсюда взят образец счётчика подряд-неудач, применённый теперь к MCP-сторожу. |
watchdog_ping.py → n8n dead-mann8n.palo-alto.ai/webhook/watchdog-hb | ДА, живая проверка внешнего приёмника | Молчание источника (dead-man switch) | честный | Проверено живым запросом 29.08: HTTP 200. Гипотеза «эндпоинт умер и все пинги тонут молча» — не подтвердилась. |
ack-watchdog_shared/ack_watchdog.py (+ шим в scripts/) | ДА, после разбора 31.07 | Возраст неотвеченного ACK | честный | Класс «писатель и читатель на РАЗНЫХ файлах» закрыт шимом; ценой были 354 записи с nudged:0. Шим оставлен намеренно: удаление вернуло бы копию синком. |
Session Wait Watchdogsession_wait_watchdog.py | ДА, читает сами транскрипты сессий | Возраст последней активности + тип ожидания | честный | Честно объявляет свой false positive (долгий Bash неотличим от ожидания разрешения) и гасит его порогом 15 мин + капом в 3 пинга. |
Quarantine-Watchquarantine_watch.py | ДА, идёт через approval.py, а не в пустоту | Новые held-пакеты | честный | Остаточный корень «немой аск без id» закрыт 26.07: без id вопрос невидим для approval.py due/check. |
Syncthing Watchdog Periodicsyncthing_watchdog.ps1 | ДА, живой REST /rest/system/ping и /rest/system/connections | Число реально подключённых пиров, а не факт процесса | честный | Считает connected-пиров через API — это предмет, а не прокси. Ровно то, чего не хватало в инциденте «стоял с 0 пиров 4 часа». |
TG Heartbeattg_heartbeat.py | ДА, шлёт через ту же Telethon-рельсу, что и шина | Статус двух каналов от tg_channels_check | граница метода | Транспорт настоящий. Наследует ограничение строки выше: MCP-половина статуса — прокси. Это граница метода, а не дефект сторожа. |
Прочие 20+ гардов узлаcron_watchdog, revert_guard, memory_guard, dupgen_guardian, proc_patrol, orphan_engines, browser_rail_watch, oauth_probe, robot_watchdog, session_approvals_doctor и др. | поштучно живым прогоном НЕ проверялось | по тексту: у 19 из 23 есть работа с возрастом или выходом | не доказано | Честная граница аудита: это ТЕКСТОВЫЙ разбор — он ищет слова, а не понимает логику; та же оговорка, что в Watchdog-Symmetry-Audit 06.08. Зелёное по тексту слабее красного по замеру. Не-нулевые коды прошлых прогонов (Revert Guard=5, Fleet Manifest=1, Orphan Engines=1, Regress Grid=1, Token Audit=1) НЕ разбирались — кандидаты в следующий заход. |
Корень (доказан, не гипотеза): 05.08 файл сторожа отшардировали по хостам после клоббера синка. Общая починка сторожа приехала 06.08 — на день позже. Узел остался на старой слепой версии: одна TCP-проба → перезапуск → строка «restarted» → выход 0. Проверки «а поднялся ли демон» не было вообще.
Замер: 17 строк 28.08 плюс 14 строк 29.08, каждые 30 минут; порт 8765 не поднялся ни разу.
Настоящая причина смерти демона — Telegram client 'default' is not authorized, мёртвая
сессия. Такое сторож вылечить не может в принципе, а значит обязан был кричать, а не лечить.
Починка (правим дверь существующего, новых сущностей 0): обёртка узла теперь зовёт общий
закалённый mcp_daemons_watchdog.ps1 от 06.08 — две пробы вместо одной, HTTP-проба
поверх TCP (виден зомби «слушает, но не обслуживает»), перезапуск проверяется через 10 секунд.
Добавлено сверху: счётчик подряд-неудач на диске (отдельный на порт) и эскалация в чат 03 после
третьей подряд, не чаще раза в 12 часов. Образец счётчика взят у desktop-сторожа
(MAX_REVIVES_6H), а не изобретён заново.
Break-тест — убил предмет, сторож обязан покраснеть: живой порт 8768 → молчит, exit 0. Мёртвый порт 9999 → вердикт с уликами. Три подряд неудачных оживления → эскалация ушла и доставлена в 03, message_id 63137. Выздоровление обнуляет счётчик (проверено: 7 → 0). Синтаксис ps1 чист.
Канарейка поймала дефект в самой починке: первый прогон боевой обёртки выплюнул мусор — cmd.exe читает .cmd в OEM-кодировке, и кириллица в комментариях превращалась в команды, которые он пытался выполнить. Переписано ASCII-only, код возврата внутреннего сторожа проброшен наружу. Повторный прогон чист: exit 1 при мёртвом 8765, тишина по 8768.
Панель ломателей (3 из 3 headless-рельс ответили за 62с) нашла 5 дефектов. Проверил каждый по
коду, а не на слово: два оказались неверны — «состояние пишется в System32» (все пути
абсолютные через $env:USERPROFILE) и «сторож 8765 убьёт обёртку живого 8768»
(mcp_wrapper_cleanup.ps1 фильтрует строго по *telegram_mcp_sse.cmd*).
Одна находка оказалась настоящей и попала ровно в тот класс, который я чинил: если демон ФЛАПАЕТ, живая проба обнуляет счётчик, но штамп «уже кричали» остаётся — и следующая, уже настоящая, смерть молчит до конца 12-часового окна. Сторож снова замолкает, только изящнее. Починено: выздоровление снимает И счётчик, И штамп, поэтому окно тишины живёт только на НЕПРЕРЫВНОЙ беде. Break-тест: счётчик 3 + штамп → демон ожил → 0 и штампа нет (PASS).
Две оставшиеся находки — латентные, не текущий отказ, записаны честно: (1) осиротевший дочерний процесс может удерживать сокет после убийства cmd-родителя (сейчас не наш случай: замер показал 0 процессов сервера и 0 слушателей порта); (2) теоретическая гонка двух копий задачи за файл счётчика — прогон ~25с против интервала 30 мин, и оба порта идут последовательно в одной обёртке.
Сам Telegram-MCP на этом узле мёртв (мёртвая авторизация сессии). Это не дефект сторожа, а отдельная работа: нужен новый session string. Теперь об этом будет громко — починенный сторож доложит в 03 на третьем подряд провале, то есть в ближайшие полтора часа. До сегодня об этом не знал никто.
Красное у output_freshness не разбиралось: search-catalog-weekly протух 20 суток, у fb-diary-writer и content-drain-writer нет stamp-файлов. Прибор честен, долг за владельцами — вне рамок этого сида.
Не-нулевые коды прошлых прогонов (Revert Guard = 5, Fleet Manifest = 1, Orphan Engines = 1, Regress Grid = 1, Token Audit = 1) не разбирались. На Mac16 ровно такой exit 1 оказался легальным сигналом, а не поломкой, поэтому гадать не буду — это следующий заход.
Граница метода: поштучно живым прогоном проверены 11 сторожей. Остальные разобраны по тексту, а текст ищет слова, а не понимает логику. Зелёное по тексту слабее красного по замеру.
У нас есть сторожа — маленькие программы, которые проверяют, живы ли другие программы. Я проверял не «работает ли сторож», а в ту ли дверь он стучит. Потому что сторож может бодро стучать в стену и всем докладывать, что дома всё хорошо.
Одного такого поймал. Он каждые полчаса «чинил» телеграмного помощника: выключал и включал заново и писал в тетрадку «починил». Тридцать один раз подряд за два дня. А помощник ни разу не включился — у него просроченный пропуск, и выключением-включением это не лечится вообще никак. Сторож этого не проверял: включил и ушёл, довольный собой.
Теперь он считает свои неудачи. Три раза подряд не вышло — кричит в общий чат: «я не справляюсь, нужен человек». Я это специально сломал и проверил, что крик доходит — дошёл. Новых сторожей не заводил: починил дверь у старого.
Ещё пригодилась канарейка — правило проверять на одной машине, прежде чем раздавать всем. Моя же починка с первого раза выдала мусор, потому что Windows не понял русские буквы в служебном файле. Переписал латиницей — стало чисто.