Разрешены конфликты: isolated_runner (тщательный upstream-вариант killpg-wait), cli.py task-диспетчеризация upstream + guard ValueError, aum.py paths-изоляция + STORE_DB fallback.
Тесты после мерджа: 5 устарели к новой реальности — починены по интентам:
- test_aum 9c: hermetic-мок OPENROUTER_KEY (ключ уехал со старого хоста)
- test_router_env_fallback: патчит paths.env_path (runtime больше не читает _HARNESS_DIR)
- test_submit_title_guard: контракт task list = показ доски (exit 0), а не ERROR
- test_tg_commands: доска задач от тестовой ready-задачи (в прод-копии ready пусто)
- test_instance_isolation: SKIP RAG-контура без вендора /root/rag (хост мигрировал)
Итог: 19/19 зелёные
- voice.py: kind=video в TRANSCRIBABLE_KINDS; extract_audio_track() — ffmpeg
вытягивает дорожку (моно 16к opus) из видеоконтейнера; речь ≤60с
(voice/video_note/video) сразу идёт в faster-whisper-large-v3-turbo
(base на видео давал кашу). E2E: m-516173698 расшифрован.
- tg_inbox.py: поллер транскрибирует и msg.video, не только voice/audio/video_note.
- tools/tui_chat.py: TUI-чат (t-261004132716) лежал только у Volya (33301d3) —
в origin его не было, из-за этого терминал не грузился у 6 агентов.
Тестовое TUI-сообщение (t-261004132716, chat_id=tui:local) стало
последним в inbox → notify_chat/send_document целились в несуществующий
Telegram-чат → уведомления тихо SKIP'ались при живом чате. Фильтр
локальных каналов расширен: portal:* И tui:*.
Слепой TaskPromoted/TaskFailed по списку claimed возвращал в очередь
задачи, уже закрытые воркером ВНУТРИ тика до таймаута (кейс: полная
миграция Гермеса→Vybirai complete+delivery в 14:31, тик убит 14:48,
задача воскрешена — двойная работа и риск дублей).
_task_still_active(): живая проекция как истина; только running
ре-кьюится/фейлится. Ошибка чтения → прежнее поведение (True).
Воркер мог сам решить судьбу задачи: complete (done), requeue (ready),
close в архив (closed). Дайджест тика больше не перезаписывает его решения
TaskCompleted'ом — иначе requeue'd задачи нечестно закрывались бы дайджестом
(поверх проверенного результата «последний-победил»). found при e-261003094432
- es: TaskClosed → статус closed (терминальный, обратимый через requeue; closed_by manual|archive)
- commands: close_task (ручной, только failed, идемпотентен), archive_stale_failed(max_age_hours=24, now=None) — демон-архиватор
- scheduler._delivery_and_orphans_loop: каждый проход (15с) архивирует failed>24ч
- requeue принимает closed → ready (доработка из архива)
- cli: simbiot close <id> [-r reason]
- dashboard: порядок ready→running→failed→done→архив, счётчик «архив» в статусбаре, схема закрытых фейлов под одной строкой-счётчиком (тумблер показывает полностью), pill.closed
- тест tests/test_close_stale_failed.py: 15 проверок (закрытие, порог через симуляцию +25ч, done не трогается, идемпотентность, полный цикл closed→ready→failed→closed)
- portal_channel: команда cmd-settg-* (кнопка TG в UI портала) перехватывается
в pull() ДО инбокса (токен-секрет не попадает в стор/LLM), апплаер:
getMe-самотест -> атомарный .env (SIMBIOT_TG_TOKEN+ALLOWED, старый аллоулист
сохраняется) -> рестарт только simbiot-harness-tg; идемпотентно; отчёт в
чат портала (push, fallback outbox-файл); TgChannelConfigured в стор.
- isolated_runner: после TimeoutExpired kill процесс-группы воркера и ожидание
фактической смерти — без этого сабпроцессы продолжали писать BudgetSpent/
ToolCalled после SchedulerTimeout (burn 9router вхолостую, t-261002090242).
- levels: tick_timeout min 300->480 (занятый мин-уровень штатно поглощает такты;
эскалация e-261002085000). Контекст-схема не тронута (256k/512k/1M).
Тесты: test_budget_stub_guard / test_inbox_batch / test_delivery_idempotency —
зелёные; e2e на живом канале: cmd-settg consumed=1, стор чист, .env цел.
systemd-юниты не экспортируют .env; на Hetzner-агентах base_url тихо
уходил в дефолт 127.0.0.1:20128 (живой 9router только на эталонном
хосте). _env_or_file(): env > .env > default. E2E на агенте 82:
venv без openai (в cloud-init добавлен отдельно в coop).
- scheduler: guard «бюджет исчерпан» вынесен в _guard_budget_stub_requeue()
— ValueError requeue_task при ready (задача уже возвращена task_promoted
путём того же тика) гасится, тик дофинализируется; тесты
tests/test_guard_ready_race.py (FAILS 0), test_budget_stub_guard зелёный;
- es: TaskClaimed → task.claimed_at; TaskPromoted(requeue) → claimed_at=None;
- dashboard.py/.html: API отдаёт claimed_at; бейдж «🔧 в работе: hour · 12м»
(минуты/секунды/часы), подсветка running-строки, «взята в работу» в деталях;
- docs/PACKAGE-SIMBIOT-PRESALE.md: состав пакета подписки (EN-блок для
лендинга, состояние компонентов), цена единая $99.9 по директиве прайса
1) per-call timeout LLM-вызова (SIMBIOT_LLM_CALL_TIMEOUT, 90с default,
max_retries=1): роутер-лог показал — зависший тик = POST в ollama
принят, но байт стрима не пришёл (DONE без TTFT), соседние вызовы
ходили за 1-3с. Один висящий вызов больше не убивает весь тик по
уровневому таймауту (300/2880с) — режется сам вызов, event-loop
завершает тик штатно, spend-хвост и задачи не теряются
(e-260930161900-baaecb, e-260930162600-cbb7e2, e-260930153143-85246b).
2) обрезка TG-инбокса (e-260930154125-082a2a): приём 4000→10000 симв.,
показ в контексте тика 300→1200 — длинные сообщения Alex больше
не теряют хвост.
3) loss-of-claim в батче: TimeoutExpired от isolated_runner пролетал
мимо TaskFailed-блока run_level_tick — claimed-задачи висели в
running до сирот-чистки (час+). Теперь claim сразу возвращается
TaskPromoted в очередь (reason с причиной смерти воркера).
Тесты 8/8 (hermetic), живой вызов модели с timeout=90 — ok.
Причина: find_task возвращал ОДНУ ready-задачу за такт — при 5+ ready
очередь росла быстрее исполнения (1 задача/такт).
Теперь:
- select_task_batch: ВСЕ ready-задачи класса уровня, старейшая первой;
- кап пачки MAX_TASK_BATCH (min=3, hour=6, day=6; env SIMBIOT_BATCH_MAX_<LEVEL>);
- бюджет контекста: секция задач <= ctx_tokens*3*0.5 символов,
мандат/выжимки/эскалации/инбокс остаются в тике; старейшая берётся всегда;
- run_level_tick: claim всего батча ДО воркера; TaskCompleted/TaskFailed
ПО каждой отдельно (смерть воркера не теряет батч в running);
- build_context: секция 'ТЕКУЩАЯ РАБОТА — БАТЧ из N' с постановками,
инструкция закрывать каждую задачу cli.py complete <id> -a;
- CLI complete: закрытие running-задачи с результатом
(commands.complete_task_with_delivery — доставка в TG идемпотентна).
Тесты: tests/test_task_batch.py — 28 проверок (ALL PASS), набор 8/8 зелёный.
Инцидент: Alex получал презентацию трижды (m-516173239/241). Две причины:
1) гонка push (тик) vs pull (delivery-демон): оба проходили проверку
delivery_status != 'sent', пока TG-отправка (до 35с) была в полёте;
2) ad-hoc curl sendDocument из мин-тиков вообще без метки delivered.
Фикс — delivery_ledger (INSERT OR IGNORE: факт доставки раз и навсегда)
+ delivery_locks (атомарный claim ДО отправки, TTL 120с; одно SQLite-
выражение = нет гонок между потоками и процессами). deliver_result /
deliver_artifact: ledger → claim → отправка → запись; сбой → release,
демон досыплет. Requeue сбрасывает ledger (доработка доставляется заново).
Новое: cli.py tg-deliver-file (идемпотентная отправка файлов, --force),
tg-notify -k <ключ>, tg_inbox.send_document (multipart на чистом urllib),
deliver_pending pre-filter по ledger, мандаты уровней: прямые
curl/api.telegram отправки запрещены. DeliverySkipped логирует пропуски.
Тесты: tests/test_delivery_idempotency.py — 49 проверок (гонка потоков,
межпроцессный claim, сбои+ретраи, requeue, CLI). Регрессия: 6/6 тестов ОК.