---
name: omniroute_3849_upgrade_traps
description: "OmniRoute 3.8.49 на mini: нативный better-sqlite3 под arm64 надо подкладывать руками после КАЖДОГО npm install (лечится tools/fix_native.sh), сторож bounce был мёртв 6 недель, голое имя модели auto висит 60 с — правильные auto/best-*."
metadata: 
  node_type: memory
  type: project
  originSessionId: abc7cc05-ae71-45e4-9d26-a815940af85c
  modified: 2026-08-21T20:44:02.907Z
---

**Обновление mini 3.8.48 → 3.8.49 (21.08.2026).** Прошло за ~26 секунд простоя, откат не
понадобился. Приёмка на 10 пунктов, подключения провайдеров целы (214, codex=1).
Полный разбор — Obsidian `Projects/omniroute/Changelog.md` 2026-08-21 и решения D12, D13.

**1. Нативный `better-sqlite3` под arm64 — руками после каждого обновления.**
Шина ищет модуль в `<пакет>/dist/node_modules/better-sqlite3/build/Release/better_sqlite3.node`.
Поставщик кладёт в эту dist-копию **только x64-prebuilds**, под arm64 нет вовсе, а
`binding.gyp`/`src`/`deps` там отсутствуют — поэтому `npm rebuild` и node-gyp бесполезны
**в принципе** (первый рефлекс именно такой, не тратьте на него время). Рабочее решение:
рядом лежит полная копия `<пакет>/node_modules/better-sqlite3` с `prebuilds/darwin-arm64.node`,
её бинарь копируется на место. Без этого шина уходит на JS-фолбэк, в логе
`[DB] Sync driver 'better-sqlite3' failed to open ... Cannot find module`.
**How to apply:** `~/.claude/skills/omniroute-update/tools/fix_native.sh [mini|local|all]` —
идемпотентный, сам ищет prebuild под текущую архитектуру и проверяет загрузку реальным
запросом. `update.sh` теперь зовёт его сам после install и до рестарта. Проверять
по-настоящему нужно через `lsof` (модуль открыт как `txt`), а не по наличию файла: процесс
мог подняться раньше, чем файл появился.

**2. `update.sh` верифицировал через мёртвый порт 20127** — давал гарантированный ложный
провал после успешной установки (поэтому это обновление шло вручную). Исправлено на 20128.

**3. Сторож `bounce` был мёртв 6 недель, теперь починен.**
`/usr/local/bin/omniroute_bounce.sh` (root-LaunchDaemon `local.omniroute-bounce`, каждые
300 с) искал цель через `pgrep -f 'next-server'`, а воркер шины зовётся `omniroute (vNN)` —
13812 пустых WARN с 04.07. Плюс его health-проба стучала в мёртвый 20127 (всегда 000).
Если бы починили только поиск цели — сторож начал бы убивать здоровый воркер каждые 5 минут.
**Главное про пороги:** у 3.8.49 RSS воркера пилит **1455–1735 МБ** вслед за GC, а heapUsed
всего **415–502 МБ** при `OMNIROUTE_MEMORY_MB=2048`. Единый порог по RSS ставить нельзя:
вариант 1700 в логе демона тут же дал замер 1669 МБ — 31 МБ до убийства здоровой шины.
Теперь превентивный триггер по heapUsed (1600, берётся с `/api/monitoring/health:20129`),
RSS — грубый предохранитель на 2600.

**4. Голое `auto` — НЕ валидное имя модели.** Замеры: `auto` → 000 за 60 с (и 5 попыток
подряд по 45 с), `auto/best-fast` → 200 за 6.3 с. В каталоге 1130 моделей, комбо всегда с
суффиксом (`auto/best-fast`, `auto/best-coding`, `auto/best-reasoning` — 38 штук).
**How to apply:** в тестовых вызовах и в коде всегда полное имя комбо.

**НО имя модели — не вся причина «то отвечает, то висит» (замеры 22.08).** Даже с правильным
`auto/best-fast`: последовательно 6 из 6 успешно (4–9 с), а **8 параллельных запросов → 7 из 8
успешно, один висел 60 с** (контрольный вызов сразу после — 200 за 5.7 с, то есть шина
восстанавливается мгновенно). Другой замер той же серии дал куда хуже: 2/5 и 1/5 под
непрерывной долбёжкой десятками вызовов, а после паузы 120 с — 12/12 и 8/8. Механика та же,
что уже записана по инциденту 21.07: `auto/*` роутит на разные модели, среди них попадаются
висящие насмерть (nvidia/*, dashscope/*, GLM-*), **а у шины нет серверного таймаута** — она
держит запрос до таймаута клиента вместо чистого отказа. **How to apply:** в боевых цепочках
не вешать голову на `auto/*` — брать конкретную проверенную модель и ставить свой таймаут;
пачками бить шину нельзя, доля зависаний растёт с нагрузкой. Боевые боты
голое `auto` не используют (проверено grep по `~/Services`). Отдельно: 404 вида «The model
does not exist or you do not have access to it» приходит **от самого провайдера**
(проверено на `groq/llama-3.3-70b-versatile`), шина тут не виновата — см. [[omniroute_cloud_bus]].

**5. РЕТЕНЦИЯ ИСТОРИИ — РАЗОБРАНО 22.08, диагноз ниже был неверным.** Она НЕ была сломана:
`LOG_RETENTION_DAYS=30` из `.env` историей вызовов **не управляет вовсе** (штатная автоочистка
по нему чистит только логи — в stdout `[COMPLIANCE] Expired log cleanup: appRetentionDays 7`).
Реальный порог — в БД, правится в дашборде: `key_value(databaseSettings,'retention.usageHistory')`,
стоял **365**. **Грабля с ключом:** рядом лежит легаси `key_value(databaseSettings,'usageHistory')`
со старым `365`, шина его НЕ использует — скрипт, читающий легаси, молча рапортует «удалять
нечего». **Что сделано:** порог → 30 через `POST /api/auth/login` + `PUT /api/settings/database`
(грабля API: GET отдаёт поля `location` и `stats`, которые PUT не принимает — `400 Unrecognized
keys`, выкидывать); удалено 488 327 записей (2.87 млн → 2.38 млн), дамп на Ext; файл сжат
3073 → 2657 МиБ. **Грабля VACUUM через API:** `POST /api/settings/database/vacuum` в WAL-режиме
переписывает базу через журнал — `-wal` вырос до 3094 МиБ, лечится `wal_checkpoint(TRUNCATE)`
сразу после; и сам VACUUM даёт эффект ТОЛЬКО после удаления записей. Чистка добавлена в
`ops_dashboard/scripts/omniroute_db_maintenance.sh` (порог читается из `retention.usageHistory`),
ветка удаления доказана на копии базы: порог 1 день → удалила 2 370 871 запись.

**Ключ `OpenCode-Mac` — ОБЩИЙ, не только Оддикс.** Его грузят 4 launchd-сервиса
(`source ~/.codex/omniroute.env`): CebersResearch, Делор, `oddiks-analyst`, `oddiks-main`.
Прежняя цифра «6357/11895 вызовов OpenCode-Mac» — суммарный трафик всех четырёх, не только
Оддикса; приписывать её одному сервису было ошибкой первого прохода.

**Диагностика Оддикса 22.08 (полный разбор — Obsidian `Projects/Оддикс/Changelog.md`
2026-08-22): 3 из 5 звеньев sport_llm мёртвы ОДНОВРЕМЕННО, не окнами.** Живой замер боевым
промптом: `siliconflow/deepseek-v3.2` (primary) — 503, 0 успехов из 2979 за 3ч, последний
успех **30.07** (23 дня мёртв). `cerebras/gpt-oss-120b` (fallback) — **на аккаунте cerebras
кончились деньги** (402 «Payment required», проверено шиной сегодня 05:54) — та же причина
валит и `deep_reviewer` (`cerebras/zai-glm-4.7`), общий провайдер. `gemini-2.5-flash`
(slot2) — 429/502, 0 из 1795 за 3ч, вдобавок ВИСИТ 45-60с на клиентском таймауте вместо
быстрого отказа. Живо и надёжно только **`mistral-large` через owner custom pool — 171/173
(99%) за 3ч, 6с** — но он специально задвинут на 4-е место решением от 30.07 (общая квота
с zemlya-bot). GigaChat напрямую (5-й рубеж, мимо OmniRoute) — живой, 27с, медленный.
tokenrouter.com (внешний, lesson/score_recovery) — мёртв, «no available channel».

**Предложил владельцу 2 обратимые техн. правки (поднять mistral / срезать таймаут gemini) —
обе осознанно отклонены**: приоритет квоты для zemlya-bot важнее скорости анализа, риск
оборвать редкий медленный успех не стоит выигрыша. **How to apply: не предлагать эти правки
повторно без новой причины** — решение уже принято дважды.

**Архитектурный долг, найден но НЕ починен (по решению владельца):** `llm_client.py`
`DynamicAdvisorLLM` читает модель из `settings.<field>` перед каждым вызовом с комментарием
про «`llm_health` auto-heal», но самого auto-heal (кто вызывает `setattr`) в коде **нет** —
цепочку вручную перетасовывает владелец при каждой смерти звена (комментарии D30-D49 в
`oddiks/config.py`). Реальный watchdog для этого предложен, не заказан.

**Итог 22.08: в код/конфиг Оддикса правок НЕ внесено** — только диагностика.

**Прежний неверный вывод (оставлен как след):** `LOG_RETENTION_DAYS=30` не применяется к `usage_history` —
2.87 млн записей, из них **488 327 старше 30 дней** (самая от 15.06), 819 МБ + ~1.6 ГБ
индексов; база вернулась к 3.1 ГБ через 40 минут после чистки, журнал `-wal` дорос до 1 ГБ.
Генератор трафика — клиент `OpenCode-Mac`, **6357 вызовов за час**. Раздутого FTS
(см. [[omniroute_db_fts_bloat]]) в этом росте нет, тревогу даст сторож диска на 2 ГБ журнала.
