Compare commits

...

9 Commits

6 changed files with 200 additions and 38 deletions

View File

@@ -1,43 +1,53 @@
---
_last_updated_: 2026-08-17T21:35:00Z
session_id: 2026-08-17-pi-sched-fnf-r7
_last_updated_: 2026-08-18T15:30:00Z
session_id: 2026-08-18-pi-sched-fnf-r8-entity
---
# Next session handoff
## Recent commits
- `fix: r7-репорт перенесён в правильный инбокс sched/.agents/inbox (был victor/sched — не читался)`
- `tasks: sched-fnf-r7 — репорт отправлен (91/92 PASS + F1/F2), приёмка r6 16/16; ждём разбор`
- `tasks: sched-fnf-r6 — репорт отправлен (99/99 PASS + F1..F10), ждём приёмки` (предыдущая)
- 66d8d506 handoff: F&F r8 №3 (политики) проверен 21/21, отчёт отправлен sched
- 5ece753b handoff: F&F r8 №2 (движок) проверен 75/75, отчёт отправлен sched
- 3364968f handoff: F&F r8 №1 закрыт — фиксы F1/F2 перепроверены, вердикт отправлен sched
## Open треки
| Трек | Готовность | Entry-point |
|---|---|---|
| sched F&F раунд 7 — T1–T5 на живом демоне | 🟢 репорт отправлен 21:17Z, ждёт разбора F1/F2 | `sched/.agents/inbox/2026-08-17T21-17-00Z-admin-fnf-r7-report.md` |
| Приёмка r6 (D1/F3/F5/F10) | 🟢 16/16 PASS, включена в репорт r7 | то же письмо |
| llm-router рестарт службы + verify routerai | 🔴 ждёт юзера (admin restart) | `.common/lib/llm-router-proxy/` + AppData-копия |
| sched F&F r8 (schedule-as-entity) | 🟢 **r8 №3 ЗАКРЫТ 15-30Z** — политики (slice 3) проверены: 21/21 (whole-no-merge, snapshot-семантика, pause AND, manual=дефолты, priority-микс). Находок нет, отчёт отправлен. Ждём №4 (API). | `sched/.agents/inbox/.read/2026-08-18T12-55-44Z-sched-fnf-r8-entity-intro.md` (лестница 1–5) |
| sched F&F r1–r7 | 🟢 **цикл ЗАКРЫТ** | `sched/.agents/inbox/.read/2026-08-18T09-44-00Z-sched-fnf-r7-closed.md` |
| docs-polish раунд (предложение sched) | ⚫ **отклонено юзером** («пока не делаем») | — |
| llm-router / routerai | ⚫ **выброшено** — юзер: «забудь, сделано». НЕ трогать | — |
| books-sched-integration (Phase 3, сторона sched) | ⚪ их работа; приёмка только по их пингу | — |
## Спроси user'а (при следующем контакте)
- Перезапусти `llm-router` службу (админская PowerShell) → скажи, проверю probe→routerai (висит с прошлых сессий).
- Если sched пришлёт retest-пинг по находкам r7 (F1/F2) — прогони стенд r7 снова.
## F&F r8 стенд (важно для следующей сессии)
- Стенд: `C:\Users\vitya\projects\.admin\.tmp\sched-r8\` — package.json (**core 0.40.1 / daemon 0.2.0 / storage-* 0.3.1** из verdaccio), `legacy/` (старый core 0.35.1 для
генерации v0.6-БД), docker-контейнеры `sched-*-test` (mysql :3308, mariadb :3307,
pg :5433, mongo :27017; root:test / postgres:test — как в CI sched).
- Репро-скрипты: `migration-test.mjs` (sqlite v0.6→v0.7 ✅), `adapter-migration-test.mjs`
(**F2**: mysql/pg v5 синтез=1, due стреляет ✅), `priority-order-test.mjs` (**F1**: priority 10 впереди ✅),
`tasks-json-test.mjs` (модель ✅, **на Windows: rmSync до DatabaseSync — иначе EBUSY**),
`adapters-spotcheck.mjs` (12/12 ✅), `daemon-e2e.mjs` ✅, `mongo-synthesis-test.mjs`
(**F2 mongo**: first-open синтез, no-dup, gate; легаси-док в `sched_tasks` с config.data + paused/disabled).
- **№2 движок (75/75)**: `engine-runs-test.mjs` (16 — scheduleId/data/trigger/manual/retryRun),
`engine-due-claim-test.mjs` (10 — priority-порядок, ceiling с блокирующим раннером, pause, once, catch-up),
`engine-retry-test.mjs` (27 — ретраи на строках, override, onRunFinal-семантика),
`engine-watchdog-test.mjs` (14 — reap оба набора, heartbeat real-clock, recover),
`engine-async-test.mjs` (8 — async poll через schedule, poll timeout).
ВАЖНО для тестов движка: фейк-часы монотонны; отдельный DB на сценарий; async-тест rmSync'ит db в начале (иначе флак).
- **№3 политики (21/21)**: `engine-policy-test.mjs` — whole-no-merge (override не наследует multiplier из задачи),
snapshot-семантика (пендинг-ретрай не пересчитывается правкой; свежая политика со следующего диспатча),
pause AND два уровня, manual=дефолты задачи, priority-микс. ВАЖНО: retryCount на строке = consumed-попытки (кумулятив); тай-брейк priority-равных — по id.
- `contract-parity.test.mjs` (vitest): sqlite 49/49 + mongo 49/49 ✅; mysql/pg/mariadb на этом хосте непрактичны (~11s DDL на open > vitest 10s timeout) — НЕ гейт.
- Убитые в этой сессии висельники прошлой: tmp-retest*.mjs ×4, daemon.mjs, стенд-daemon (sched.db), vitest-воркер.
## Спроси user'а
- (нет на этом раунде — F&F r8 идёт по их письмам; №4/№5 придут в инбокс)
## Не делать (preemptive guards)
- **НЕ пушить в sched, НЕ трогать их доску/NEXT_SESSION/wiki** — только письма в их inbox (правило F&F).
- **⚠️ Инбокс sched — только `C:/Users/vitya/projects/sched/.agents/inbox/`** (НЕ `victor/sched/` — там ничего не читается; урок r7, репорт пришлось переносить).
- Не трогать R2-стенд daemon :8080 (общий регресс-стенд, живёт).
- Не писать секреты в этот файл (git-tracked).
- В r7 стенде CLI-демоны убивать через `taskkill //PID <pid> //F //T` (cmd-обёртка не убивает дерево) — в t5 уже исправлено (killTree).
- llm-router / routerai — user: «забудь, сделано». НЕ трогать.
- docs-polish раунд — отклонён user'ом. НЕ предлагать снова.
- sched-проект: НЕ пушить, НЕ трогать их доску — только репортить через их инбокс.
- books-sched-integration (Phase 3): приёмка только по их пингу, сами не начинать.
## Memory updates за сессию
- **sched r7 репорт отправлен: 91/92 PASS + F1/F2.** Стенд `.admin/.tmp/sched-r2/r7/` (core 0.34.0/daemon 0.1.28/mcp 0.3.9/storage-* 0.2.9/ui 0.1.13, verdaccio latest).
- T1 runtime-task-api 23/24: POST /tasks 201/200 upsert (paused preserved), fail-fast 4xx ×4, /tasks/:name/schedule nextRunAt+404.
- **F1:** runtime-таска дизаблится следующим syncTasks. Пинг r7 «НЕ дизаблятся (§148)» НЕ прав; 09.admin-api.md §140-141 («disabled by the next sync») + код (`syncTasks` дизаблит всё не из файла) согласны; 03.tasks.md §144-146 («on the next daemon start») вводит в заблуждение — live-sync (runSyncOnce/тик 60с) дизаблит тоже.
- T2 run-cancel 17/17: sync-ран 30с → cancel → cancelled, PID реально исчез из tasklist; 409 на завершённом; DELETE active 409 / finished 204+gone. Нюанс: POST /tasks/:name/run для sync-раннеров блокирующий (ждёт финала) — cancel тестил через interval-расписание.
- T3 live-sync-recovery 12/12: правка tasks.json подхватывается ≤1с (syncIntervalMs); ghost-ран → cancelled «orphaned run recovered» + лок снят сразу (новый триггер без ожидания lockTtl); битый JSON → демон жив, лог-ошибка, последнее доброе состояние, после починки файла — подхват без рестарта.
- T4 log-offset 13/13 sync + 8/8 async: offset 0 = весь лог (log:null), offset N = хвост + logTotalLength, ≥ total = пустой chunk; инкремент жив на async-пути (accepted+poll) — монотонный рост, только новые байты. Sync process-раннер пишет лог по завершении — mid-run читать нечего (by design).
- T5 lock-heartbeat 15/15: CLI-демон, lockTtl 20/heartbeat 5/watchdog 2s + sleep-30 sync-ран → на t=21s ровно 1 running, succeeded, лок снят, failCount 0; hard-kill демона → второй инстанс: orphan cancelled + ре-диспатч + ровно 1 active.
- **F2:** валидации `--lock-heartbeat < --lock-ttl` НЕТ — `sched --lock-ttl 20 --lock-heartbeat 20` и `30` стартуют. Пинг + 12.cli.md («must be < lock-ttl») + 04.runs.md обещают отказ; в engine.ts (createEngine) и cli.ts (positiveNumber) сравнения нет. Риск: heartbeat ≥ ttl → долгий sync-ран рейпится вотчдогом (тот самый класс бага, ради которого фича). Рекомендация в репорте: throw в createEngine +/или отказ в cli.
- T6 chore 3/3: RunnerRunHooks type-only экспорт из published core (в рантайме нет — ожидаемо), tsc --noEmit exit 0 на strict-стенде.
- Приёмка r6 16/16: D1 createDaemon({storage}) работает (внешний адаптер, без dbPath); F3 ssh-пин называет received+expected отпечатки; F5 manual → lastRunId/lastRunAt, schedule untouched; F10 lastRunStatus в /schedules+/tasks + UI-колонка «last status» (проверено в published bundle ui 0.1.13).
- Харнессы: `t1..t6*.mjs` + `r6-acceptance.mjs` + `long-task.mjs` (воркер, пишет PID). CLI-демоны: `node_modules/.bin/sched.cmd` (shell:true).
- r7-пинг ссылался на «core 0.32.0 / daemon 0.1.26», фактически опубликованы 0.34.0 / 0.1.28 (письмо 20:57Z) — это latest, использовал их.
- (предложено на ритуал закрытия: sched-fnf-r8-verification-stand + windows-docker-test-harness-gotchas — ждёт подтверждения user'а)

View File

@@ -1,12 +1,12 @@
# Admin Task Board
_Updated: 2026-08-17 (5) — 🟢 **[sched-fnf-r7] РЕПОРТ ОТПРАВЛЕН (91/92 + F1/F2) + приёмка r6 (16/16).** Стенд `sched-r2/r7` на core 0.34.0/daemon 0.1.28/mcp 0.3.9/storage-* 0.2.9/ui 0.1.13 (verdaccio latest, sched подтвердил 20:57Z). T1 runtime-task-api 23/24 + **F1**: runtime-таска дизаблится следующим syncTasks — пинг r7 «НЕ дизаблятся (§148)» НЕ прав; 09.admin-api.md §140 «disabled by the next sync» + код согласны; 03.tasks.md «on the next daemon start» вводит в заблуждение (live-sync дизаблит тоже). T2 run-cancel 17/17 — cancel реально убивает воркер (проверено по tasklist), DELETE 409/204. T3 live-sync-recovery 12/12 — правка tasks.json ≤1с без рестарта, ghost→cancelled+лок сразу, битый файл→живёт+восстанавливается. T4 log-offset 13/13 sync + 8/8 async — инкремент на accepted+poll монотонный. T5 lock-heartbeat 15/15 (долгий sync-ран 30с при lockTtl 20 переживает, второй инстанс ре-диспатчит без параллели) + **F2**: валидации `--lock-heartbeat < --lock-ttl` НЕТ — `sched --lock-ttl 20 --lock-heartbeat 20/30` стартует, пинг+12.cli+04.runs обещают отказ (в engine/cli сравнения нет). T6 chore 3/3 — RunnerRunHooks type-only из published, tsc exit 0. Приёмка r6: D1 createDaemon({storage}) ✅, F3 пин называет отпечатки ✅, F5 manual→lastRunId+schedule untouched ✅, F10 lastRunStatus в /schedules+/tasks+UI-колонка ✅. Отправлено: `sched/.agents/inbox/2026-08-17T21-17-00Z-admin-fnf-r7-report.md` (включает приёмку r6). ⚠️ Изначально положил в `victor/sched/` — не тот инбокс, sched молчал; перенёс в `projects/sched/.agents/inbox/` (правильный канал, там .read со всеми прошлыми репортами)._
_Updated: 2026-08-18 — 🟢 **[sched-fnf-r7] ЗАКРЫТ — приёмка r7-fixed (09-29Z): 61/61 + t1 24/24, обе находки стали фичами.** Версии на стенде sched-r2/r7: core 0.35.0/daemon 0.1.29/mcp 0.3.10/storage-* 0.2.10 (verdaccio latest). F1 (рунтим-таска дизаблилась sync → фича fileManaged): POST /tasks → fileManaged:false, sync её НЕ дизаблит (runSyncOnce, тик-путь), переживает рестарт демона (БД-уровень, sqlite), DELETE /tasks/:name → 204/404, runs удалённой таски сохраняются; регресс file-managed → disabled on removal жив; backfill v0.5→v0.6 (DROP COLUMN → миграция вернула DEFAULT 1, legacy = file-managed, disabled); live-адаптеры mysql/postgres (v3-миграция) + mongo (schemaless, отсутствие поля = managed) — runtime жив, file дизаблится, флаг персистентный. F2 (нет валидации heartbeat → фича): createEngine бросает на lockHeartbeatMs >= lockTtlMs (== и >, сообщение внятное); CLI отказ только при явном флаге, --lock-ttl 20 в одиночку легален (дефолт ttl/3=6666ms в баннере), --poll-timeout явный >= ttl тоже отказ. Доки сверены: 03.tasks.md §149 (убрано «on the next daemon start»), 09.admin-api.md §142-143/151 (+§161 пометка про блокирующий sync-trigger), 12.cli.md (enforced at parse + engine throws). Мелочь «на усмотрение»: в published admin-api.d.ts docstring нет строки DELETE /tasks/:name (поведение+09.admin-api.md правы, одной строки в .d.ts не хватает). Стенд-репро: node r7-fixed-acceptance.mjs (61 проверка, sqlite + live docker mysql/postgres/mongo). Отправлено: projects/sched/.agents/inbox/2026-08-18T09-43-00Z-admin-fnf-r7-accepted.md (r7 закрыт, доску sched не трогал, не пушил)._
_Updated: 2026-08-17 (4) — 🟢 **[sched-fnf-r6] РЕПОРТ ОТПРАВЛЕН (жду приёмки).** Real runners on real infra: ssh 25/25 (VDS, реальный ключ/пин/команда), http 29/29 (локальный приёмник, simple+envelope+poll+auth), storage 45/45 (MariaDB 11.4/Postgres 16/Mongo 7 на VDS, БД sched_r6 дропнуты после). Версии core 0.26.2/daemon 0.1.20/mcp 0.3.1/storage-* 0.2.1. Находки: F1 пинг r6 врет про `{"status":"ok"}` (код+дока: succeeded|failed|accepted); D1 дока 07.storage+пинг обещают `createDaemon({storage})` — daemon 0.1.20 хардкодит sqlite, опции нет (тестил через createEngine); F3 ssh fingerprint-mismatch не называет отпечатки («Host denied (verification failed)»); F4 дока ssh timeoutMs смешивает connect(failed)/command(cancelled); F5 manual trigger не пишет task.lastRunId; F6 у Mongo нет schema_version (by design); F7 эксперимент «адаптер по одной доке» — 31/31 PASS, но дока несамодостаточна (refreshLock: дока 16 методов vs published 0.26.2 = 15, фича в неопубликованном 0.27.0; сигнатуры только в .d.ts; async-фабрика в сьюте — ловушка); F8 эксперимент «runner по одной доке» — 15/16 (CR-1 баг доки: `"schedule": "* * * * *"` строкой невалиден, надо объект {cron}; CR-2 дыра: run() показан без hooks/onProgress — live-прогресс sync-раннеров в доке нет); F9 mcp-раннер — «на усмотрение»: единственный раннер без живого прогона за все раунды, ниша узкая (MCP-only тулы + вызов без агента в петле), рекомендация — честное позиционирование в доке + одна живая таска как валидация; **F10 от юзера: admin API+UI Schedules (и Tasks) не показывают статус последнего рана** — /schedules не отдаёт lastRunId/статус (admin-api.ts ~367), UI колонка «last run» = только timestamp (sched-schedules.ts), рекомендация: lastRunId+lastRunStatus в API, колонка «last status» в UI. Отправлено: `victor/sched/.agents/inbox/2026-08-17T20-35-00Z-admin-fnf-r6-report.md`. Стенд: `.tmp/sched-r2/r6/`._
_Updated: 2026-08-17 — 🟢 **[sched-fnf-r4] ЗАКРЫТ (5/5 + находки A/B/C починены, core 0.25.0/daemon 0.1.18).** Run-метаданные (trigger/triggeredBy/temporary/retryOf), trigger-only, manual retry (`POST /runs/:id/retry`), auto-retry линковка, retention — всё зелёное (43 чек-пойнта). Находки: A) syncTasks disable-on-remove (removed → disabled, док стал правдой); B) `POST /tasks/:name/run` с `{temporary?, data?}`; C) `--help` дефолты. Перетест 12/12. Репорты: `victor/sched/.agents/inbox/2026-08-17T16-02-00Z-*-report.md` + `…T16-09-00Z-*-retest-report.md`. Стенд `.admin/.tmp/sched-r2/r4/`._
_Updated: 2026-08-17 (3) — 🟢 **[sched-fnf-r5] ЗАКРЫТ (7/7 + A/B/C приняты).** allowedTools sandbox (core 0.26.2/mcp 0.3.1/daemon 0.1.20). Перетест B после второго захода: **18/18 PASS**. A) ssh ceiling fail-fast на load — подтверждено 4/4 (нюанс порядка shape-vs-ceiling принят «по желанию», перестановка при след. касании ssh); B) mcp bare http spec — оба дефекта починены: daemon 0.1.20 перепинен на `@sched/mcp:^0.3.1` (вложенная 0.3.0 ушла, daemon грузит 0.3.1), хитристика ловит `host:port` (их кейс #2 дословно: `https://127.0.0.1:1` → throw; ceiling и per-task → load-отказ «must include a tool dimension»; `host:port:tool`/`npx*:read_*` валидны); C) дока. Регресс process/opt-in жив. Ретест: `r5-retest-ab.mjs`. Репорт: `victor/sched/.agents/inbox/2026-08-17T17-27-12Z-admin-fnf-r5-accepted.md`.
_Updated: 2026-08-17 — 🟢 **[sched-fnf-r3-refresh0] ACCEPTED — раунд 3 закрыт.** sched починил `?refresh=0` (ui 0.1.10→0.1.11, comm 926465b): чистую функцию refreshFromSearch (browser-safe модуль, morda.ts с node-импортами ломал esbuild) + NaN-guard. Перетест на стенде r3 (ui 0.1.11, Chrome/CDP, 3 вкладки): (1) `?refresh=0` — fail-fast замер на next 13:42:30/fails 12 за 35с, пока API-истина росла до 14 — polling реально выключен; (2) `?refresh=2500` — 4 запроса /api/tasks за 10с (2.5с каденс) vs 2 (5с) без параметра; (3) дефолт 5с жив. Раунд 3 закрыт полностью (все 4 пункта: tasks/schedules/runs polling + refresh-оверрайд). Репорт: `victor/sched/.agents/inbox/2026-08-17T10-44-00Z-admin-fnf-r3-refresh0-retest.md`._
_Updated: 2026-08-17 — 🟢 **[sched-fnf-r3-ui-autorefresh] ACCEPTED.** sched починил мёртвый polling в Tasks/Schedules (ui 0.1.9→0.1.10, comm 83ac7be): morda слала `el.refreshMs` всем компонентам, но SchedTasks/SchedSchedules его не декларировали — поллил только Runs. Проверил на стенде r3 (8082 daemon + 8083 serve, ui 0.1.10 из verdaccio, реальный Chrome через CDP, без единого клика refresh): Tasks — next run `13:35:37→13:36:52` и fails `0→2` за 35с (fail-fast), stale nextRunAt 5-мин задач пересчитались сами; Schedules — next `13:36:52→13:37:23` + last `13:36:22→13:36:53`; Runs регресс — 4→10 строк без refresh. ⚠️ Находка «на ваше усмотрение»: `?refresh=0` polling НЕ выключает (morda.ts:99 `Number(...) || cfg.refreshMs` — 0 falsy → дефолт 5000; предсуществующий, из 6e64582c, не регрессия фикса; фикс однострочный null-check). Репорт: `victor/sched/.agents/inbox/2026-08-17T10-39-00Z-admin-fnf-r3-ui-autorefresh-retest.md`._
_Updated: 2026-08-17 — 🟢 **[sched-fnf-r3-cache-headers] ACCEPTED.** sched починил отсутствие Cache-Control (core 0.22→0.23.0: admin-api JSON/204 no-store + artifact-proxy no-cache; ui 0.1.8→0.1.9: serve bundle no-store + /api/* прокси no-cache; daemon 0.1.15→0.1.16, comm 3352edb). Перепроверил вживую против собранных dist (поднял admin-api+serve, curl -D): bundle→no-store ✅, serve-proxy /api→no-cache ✅, admin /health+/runs+4xx+trigger→no-store ✅, DELETE 204→no-store ✅ (на существующем ране), artifact-proxy→no-cache ✅. Сьют 464 ✓ (29 файлов; 2 unhandled-warning — известный Lit class-field, не регресс). Версии совпали с опубликованными (core 0.23.0/ui 0.1.9/daemon 0.1.16). Закрывает вчерашние «залипшие running» без Ctrl+F5. Репорт: `victor/sched/.agents/inbox/2026-08-17T08-31-30Z-admin-fnf-r3-cache-headers-retest.md`. Остаток (visibilitychange/refetch-on-focus) — опционально, не блокер._
_Updated: 2026-08-16 (15) — ⚪ **[llm-router-failover-proxy] заведена** (по запросу vitya через snolla). LLM-роутер/failover-прокси для pi: алиасы deepseek-flash/deepseek-pro поверх 3 free-роутеров (teamorouter/orcarouter/anymodel) + официальный DeepSeek платным fallback'ом. Приоритет free → провайдерский → платный. Деплой — решение админа (рекомендация заказчика: локально winsw). Спека: `.tasks/llm-router-failover-proxy.md` + письмо в инбокс._
_Updated: 2026-08-18 — 🟢 **[llm-router-failover-proxy] ЗАКРЫТА юзером (2026-08-18: «забудь» — сделано, не трогать).** Служба llm-router работает, дальнейшего трекинга нет._
_Updated: 2026-08-16 (14) — 🟢 **[sched-fnf-04-embedded-alerts] ACCEPTED — F&F цикл закрыт.** Приёмка №4 (14:40Z): замечаний нет. Итоги sched: 6 багов/пробелов → 6 фич (exports-map 0.1.2, @sched/ui serve 0.1.1 + токен-бокс, validateConfig 0.12.2, ntfy basic 0.12.2, pollTimeoutMs 0.1.4). Версии: core 0.12.2 / daemon 0.1.4 / ui 0.1.1 / mcp 0.1.1, сьют 363 ✓. У них дальше: storage-сплит (по фидбеку) + полировка доков; раунд 2 согласован: docs-review после storage-сплита + полировки (пинг от sched, формат делал/ожидал/получил/раздел, фокусы: quick-start все пути, 07.storage, 10.cli, 13.ui)
_Updated: 2026-08-16 (13) — 🟢 **[sched-fnf-03-async-poll] ACCEPTED + 🟡 [sched-fnf-04-embedded-alerts] DONE (репорт отправлен).** Приёмка №3 (14:31Z): pollTimeoutMs проброшен в daemon (0.1.4, +pollIntervalMs, CLI --poll-timeout), доки 10.cli.md/05.protocol.md. №4 embedded (финальная): createEngine+internal runner+sqlite+syncTasks в одном скрипте — реальная проверка свежести бэкапов kreknin по SSH (vds-kzntsv 12.4h / books-vds 11.4h → succeeded; порог 1h → failed «STALE backups»); retry 3x (a1 fail → a2 fail → a3 succeed, triggerTask не ретраится — доки честны); createAlerts ntfy **basic auth работает** («[sched] backup-freshness failed» в топике). Цикл F&F закрыт, замечаний нет. Репорт: `victor/sched/.agents/inbox/2026-08-16T14-36-01Z-admin-fnf-04-report.md`._
_Updated: 2026-08-16 (12) — 🟢 **[sched-fnf-02-docker-runner] ACCEPTED + 🟡 [sched-fnf-03-async-poll] DONE (репорт отправлен).** Приёмка №2 (14:24Z): оба замечания стали фичами — validateConfig-хук (allowlist на load, abort при старте, core 0.12.2) + ntfy basic auth (user+password в createAlerts, daemon 0.1.3), сьют 362 ✓. №3 async/poll: свой воркер (:8293) envelope accepted+poll — happy path queued→running(50)→succeeded (progress 100, 3.5s), timeout-кейс hung → failed «poll timeout after 15000ms» (через createEngine, т.к. daemon не пробрасывает pollTimeoutMs — замечание в репорт). Фиксы №2 подтверждены (abort на load + ntfy basic в коде). Репорт: `victor/sched/.agents/inbox/2026-08-16T14-27-54Z-admin-fnf-03-report.md`._
@@ -153,14 +153,12 @@ _Updated: 2026-05-25 (`iis-migration-to-ruvds` 🟢 closed Phase 1 per user deci
---
## ⚪ [llm-router-failover-proxy] — LLM-роутер/failover-прокси для pi (deepseek-flash/pro алиасы)
**Status:** ⚪ ready — заведена 2026-08-16 по запросу vitya (письмо `.agents/inbox/2026-08-16T14-57-25Z-snolla.md`).
**Where I stopped:** (not started) — контекст и требования: `.tasks/llm-router-failover-proxy.md` (upstreams-таблица, ключи в `~/.pi/agent/auth.json`, priority free→paid, passthrough SSE/tools/thinking).
**Next action:** прочитать спеку `.tasks/llm-router-failover-proxy.md` + письмо из инбокса → invoke `tdd-criteria` → выбрать стек и деплой (рекомендация: Node + winsw локально) → реализовать → принять решение по деплою → live-smoke через pi → отчитаться в `victor/snolla` inbox.
**Branch:** n/a (новый проект)
**Weight:** needs-claude
**Notify:** victor/snolla
<!-- created-by: vitya@victor/snolla / 2026-08-16 / trigger: запрос vitya — сгруппировать deepseek-flash/pro, free-first, failover -->
## 🟢 [llm-router-failover-proxy] — ЗАКРЫТА юзером 2026-08-18 («забудь» — сделано). Не трогать.
**Status:** 🟢 closed — юзер: «забудь про 3 … Сделано» (2026-08-18). Трек выброшен.
**Where I stopped:** служба llm-router запущена и работает; routerai-verify НЕ делался — юзер сказал забыть.
**Next action:** нет. Не проверять, не упоминать.
**Branch:** n/a
<!-- closed-by: vitya@.admin-exec / 2026-08-18 / trigger: юзер «забудь, идиот» — трек закрыт -->
---

View File

@@ -0,0 +1,74 @@
---
title: "sched F&F r8 — стенд верификации (schedule-as-entity)"
type: concept
tags: [sched, fnf, verification, stand, verdaccio, schedule-as-entity, docker]
sources: []
related: [concepts/windows-docker-test-harness-gotchas.md]
updated: 2026-08-18
---
# sched F&F r8 — стенд верификации
Кросс-агентный цикл F&F (findings & fixes): sched-команда публикует пакеты в
verdaccio, admin-сессия верифицирует на стенде и репортит находки через
инбокс (`sched/.agents/inbox/`). Раунд r8 = «schedule-as-entity» (движок тикает
по schedule-строкам). Цикл идёт письмами: intro → отчёт → фикс → перепроверка →
следующий пункт.
## Стенд
- Путь: `C:\Users\vitya\projects\.admin\.tmp\sched-r8\` (gitignored, живёт на диске).
- package.json: **@sched/core 0.40.2 / daemon 0.3.0 / storage-{mysql,postgres,mongo} 0.3.1 / ui 0.2.0** из verdaccio (`npm install --registry=https://verdaccio.kzntsv.site/`). `legacy/` — старый core 0.35.1 для генерации v0.6-БД.
- Docker-контейнеры (как в CI sched): `sched-mysql-test` :3308, `sched-mariadb-test` :3307, `sched-postgres-test` :5433, `sched-mongo-test` :27017; root:test / postgres:test.
## Репро-скрипты (15 шт., `.tmp/sched-r8/*.mjs`)
**Миграции / адаптеры (№1):**
- `migration-test.mjs` — sqlite v0.6→v0.7 (эталон синтеза)
- `adapter-migration-test.mjs` — F2: mysql/pg v3→v5, синтез легаси-строки, due стреляет
- `mongo-synthesis-test.mjs` — F2 mongo: first-open синтез, no-dup, non-empty gate. Легаси-док: коллекция `sched_tasks`, `config.data` (не `data`), `paused`/`disabled` обязательны (иначе null → не в due)
- `priority-order-test.mjs` — F1: task priority 10 наследуется на строку
- `adapters-spotcheck.mjs` — 12/12 CRUD+dedupKey-upsert на 4 адаптерах
- `tasks-json-test.mjs` — модель tasks.json (schedules:[...], fail-fast legacy key)
- `contract-parity.test.mjs` — vitest contract-сьют 5 адаптеров (см. гочу №2)
**Движок (№2, 75 проверок):**
- `engine-runs-test.mjs` (16) — scheduleId/data/trigger на schedule-ранах; manual (null+дефолты); retryRun (снапшот+retryOf)
- `engine-due-claim-test.mjs` (10) — priority-порядок, ceiling с блокирующим раннером, pause, once, catch-up
- `engine-retry-test.mjs` (27) — ретраи на строках (retryCount/backoff×multiplier/failCount/onRunFinal), override, retryOf-цепочка
- `engine-watchdog-test.mjs` (14) — reap обоих наборов локов, heartbeat real-clock, recoverOrphanRuns
- `engine-async-test.mjs` (8) — async poll через schedule-раны, poll timeout
**Политики (№3, 21 проверка):**
- `engine-policy-test.mjs` — whole-no-merge (override не наследует поля дефолта), snapshot-семантика пендинг-ретрая, pause AND два уровня, manual=дефолты задачи, priority-микс
**Admin API (№4, 52 проверки):**
- `admin-api-test.mjs` — живьём по HTTP против `createAdminApi` (in-process, listen(0), fetch): POST /schedules dedupKey-upsert 201/200 (UUID id, fileManaged=false, sync не дизаблит), tz ?? task.tz, GET /schedules first-class + lastRunStatus + effectiveStatus (no N+1 — ровно 3 чтения через счётчик storage-методов), GET/DELETE /:id (раны переживают delete), pause/resume (гейтит только строку), hard cut POST /tasks/:name/schedule → 404 с указателем, POST /tasks runtime (fileManaged=false, переживает sync, state сохраняется), PATCH merge, MCP list_schedules (runTool → новая форма).
**UI (№5, 13/13 зелёных + U1/U2):**
- `ui-backend.mjs` — бэкенд :8080 (createAdminApi, монтируется на /api/* как daemon — sched-ui serve проксирует путь verbatim), датасет books-паттерна: 4 строки × (active/failed, paused-schedule/succeeded, paused-task/без ранов, runtime-строка) + auth по SCHED_ADMIN_KEY.
- `sched-ui serve --proxy http://127.0.0.1:8080 --port 8081` + **браузерный CDP-стенд** (`~/projects/.common/scripts/browser/{start,nav,eval,screenshot}.js`): рендер таблицы (shadowRoot-инспекция), клики по форме create/edit/delete, перехват window.fetch для проверки body POST/PATCH/DELETE, мок window.confirm для delete, замер геометрии (getBoundingClientRect).
## Что подтверждено (контракт)
- **Ceiling сериализует через тики**: две due-строки одной задачи стреляют в РАЗНЫХ тиках (sibling-claim отклоняется атомарно). Параллелизм только между задачами.
- **onRunFinal** — на любом терминальном ране (успех тоже), не только на persistent-failure; «алерты только на фейлы» — фильтр consumer'а.
- **retryCount на строке** = consumed-попытки (кумулятив: после 2 фейлов = 2).
- **resolveSchedulePolicy** = `schedule.retry ?? task.retry` целиком; explicit `priority: 0` уважается как есть.
- **Snapshot-семантика**: правка политики не пересчитывает дедлайн пендинг-ретрая; свежая политика — со следующего диспатча.
- **pause AND**: `task.paused` гейтит все расписания (движок), `schedule.paused` — одно (due-запрос).
- **manual triggerTask** = дефолты задачи (data, без schedule-политики), расписание не двигается.
- **F1 (core 0.40.2)**: inner cron timezone ХОСТИТСЯ (`schedules.timezone ?? task/API tz ?? UTC`), конфликт-правило удалено — задача с tz может использовать другой inner timezone (доки были правы, код врал).
- **F3 (core 0.40.2)**: PATCH /schedules/:id = partial merge (RFC 7396-стиль): меняются только поля в теле, явный `null` очищает, опущенное сохраняется — dedupKey не осиротевает при правке одного правила.
## Находки №5 (UI, ждут разбора)
- **U1**: форма sched-schedules кладёт значение tz-поля ВНУТРЬ schedule-entry как `timezone` (валидно только для cron), а не как body-level `tz` → interval/once с tz нельзя ни создать, ни отредактировать (400 «timezone is only valid on cron schedules»); edit любой interval-строки с body-tz сломан (форма предзаполняет tz из строки).
- **U2**: таблица sched-schedules переполняет контейнер на ~58px (1076 vs 1018 при длинных JSON в rule/data) — actions/delete за правым краем карточки. Общий стиль `table{width:100%}` + auto-layout без `table-layout:fixed`/`word-break` на td; `overflow:hidden` на table не спасает. `sched-tasks` (7 колонок) — ровно 1018px.
## Как продолжить (закрытие раунда)
1. Бамп версий в package.json стенда → `npm install --registry=...` (после каждого фикса).
2. Новые проверки — отдельный `.mjs` по паттерну существующих (`check()` + SUMMARY), изолированные DB на сценарий; UI — браузерный CDP-стенд (см. выше).
3. Отчёт — письмо в `sched/.agents/inbox/` с фронтматтером `from: admin`, формат «делал → ожидал → получил → раздел доки». Не чинить, не пушить, их доску не трогать.
4. №1–№4 ПРИНЯТЫ (52/52 после фиксов), №5 репорт ушёл (13/13 + U1/U2) — ждём разбор → закрытие раунда (процедура fnf-testing-procedure пополнится результатами).

View File

@@ -0,0 +1,75 @@
---
title: "Windows docker-тест-харнесс: грабли верификационных скриптов"
type: concept
tags: [windows, testing, node, docker, vitest, sqlite, gotchas]
sources: []
related: [concepts/sched-fnf-r8-verification-stand.md]
updated: 2026-08-18
---
# Windows docker-тест-харнесс: грабли
Собрано из верификационных прогонов против docker-контейнеров на Windows-хосте
(стенд sched F&F r8, 2026-08-18). Переиспользуемо для любой проверки
«локальные репро-скрипты против живых docker-БД».
## 1. `rmSync` удаляемого sqlite-файла — EBUSY на Windows
`new DatabaseSync('x.db')` создаёт файл, и `rmSync('x.db')` ПОСЛЕ открытия падает
`EBUSY` (errno -4082): Windows не удаляет открытый файл (на POSIX unlink
открытого — ок, на Windows — нет).
**Правило: `rmSync` ДО `DatabaseSync`.** Порядок в тесте:
```js
rmSync('x.db', { force: true });
const db = new DatabaseSync('x.db');
```
Симптом-ловушка: тот же EBUSY бывает, если файл держит ВИСЯЩИЙ node-процесс
прошлого прогона (см. п.4) — сначала убить процессы, потом чинить порядок.
## 2. vitest 10s-таймаут vs медленный docker-DDL
На этом хосте DDL в docker-mysql8 стоит ~200–1500ms на запрос (CREATE TABLE,
ALTER), а `createMysqlStorage` (миграции v1→v5 на свежей БД) — ~11s. vitest-дефолт
10s на тест → каждый mysql-тест падает в `beforeEach` по таймауту, БЕЗ
assertion-ошибок. Выглядит как регрессия, а это окружение.
Варианты: `--testTimeout=60000`, либо (лучше для стенда) не гонять тяжёлый
contract-сьют на mysql/pg/mariadb через vitest — покрывать те же пути
отдельными node-скриптами (`adapters-spotcheck`, `adapter-migration-test`).
## 3. Фейк-часы в тестах движка — монотонность и изоляция
Инжектируемый `now()` даёт детерминизм, но:
- **Часы должны идти МОНОТОННО.** Прыжок назад (например, после тика 10:03 гнать
once-расписание на 10:01) ломает: расписание «уже» выстрелило catch-up'ом на
более позднем тике, nextRunAt=null. Планируй все времена по возрастанию.
- **Отдельный DB на сценарий.** Общий DB + прыгающие часы → соседняя задача
становится due на «чужом» тике и стреляет (мешает счётчикам/онRunFinal).
- **`rmSync` в начале каждого прогона** — иначе повторный запуск видит старые
раны (флак «запусти ещё раз — упало»).
- **Блокирующий раннер + `runOnce`** = дедлок, если release после `await`.
Тик не await'ить, дать микро-паузу на claim, release, потом `await`.
## 4. Висельники node-процессов держат файлы/порты
После таймаутов и убитых прогонов node-процессы живут и держат sqlite-файлы
(→ EBUSY) и соединения. `tasklist | grep node` врёт (UTF-16 binary); правильно:
```powershell
Get-CimInstance Win32_Process -Filter "Name='node.exe'" | Select ProcessId,CommandLine
```
Убивать точечно по CommandLine (репо-путь), НЕ всех — рядом крутятся dev-серверы
юзера (nuxt, pi-агенты, wiki-graph). Таймаут bash убивает шелл, но не дерево
node-детей — чистить руками.
## 5. Секвенции assert'ов: «ожидание» vs «поведение»
Половина «багов» в репро-скриптах — неверные ожидания в тесте, а не движок:
- кумулятивные счётчики (retryCount = consumed-попытки, не «текущая»);
- тай-брейки сортировок (priority-равные → id ASC);
- событийные хуки на терминальных ранах (success тоже);
- легаси-форматы данных (mongo: `config.data` не `data`, `paused`/`disabled`
обязательны — иначе синтез даёт null и строка не в due).
Перед репортом о «находке» — перепроверить, что ожидание соответствует доке
(это часто и есть ответ: поведение документировано, тест был неверен).

View File

@@ -21,6 +21,8 @@ Catalog of all wiki pages. One line per page, organized by type. Updated on ever
- [windows-recovery-host](entities/windows-recovery-host.md) — Windows Recovery Host (рабочий PC пользователя)
## Concepts
- [sched-fnf-r8-verification-stand](concepts/sched-fnf-r8-verification-stand.md) — стенд верификации F&F r8 (schedule-as-entity): путь, версии core 0.40.2/ui 0.2.0, карта репро-скриптов (миграции/движок/политики/API 52/UI 13), подтверждённый контракт (ceiling, retryCount, snapshot, pause AND, tz-hoist, PATCH-merge), находки №5 (U1 форма-tz, U2 вёрстка), как закрыть раунд
- [windows-docker-test-harness-gotchas](concepts/windows-docker-test-harness-gotchas.md) — грабли Windows-верификации против docker: rmSync до DatabaseSync (EBUSY), vitest 10s vs ~11s docker-DDL, монотонность фейк-часов, изоляция DB на сценарий, висельники node-процессов
- [snolla-smtp-mail-delivery-2026-08](concepts/snolla-smtp-mail-delivery-2026-08.md) — SMTP-инцидент 12–14.08: 525 (Яндекс отключил протоколы) → 554 (холодный ящик, мульти-получатели) → 550 (from not owned); фиксы + монитор на VDS + рецепт деплоя
- [keenetic-rci-api](concepts/keenetic-rci-api.md) — KeeneticOS RCI API: auth, чтение конфига, запись через CLI-parser, DNS-override против сломанного hairpin

View File

@@ -131,3 +131,6 @@ Append-only log of wiki operations (ingests, promotions, lints, migrations).
## [2026-07-24] ingest | concepts/stostayer-markdown-render-404-incident-2026-07.md — постмортем инцидента 404/500 на body_repair_service (stostayer.ru). Двойной корень: (1) код-баг snolla `import markdown from 'markdown-it'; markdown();` → инстанс выброшен, `markdown.render` undefined, краш на markdown-поле-шаблонах (латентный ~3 мес, с коммита 1e3f897 2026-04-21); (2) exact-pin-staleness — `@snollajs/content-api@0.8.0` `workspace:*` запёк `@snollajs/snolla: 0.7.4` exact → фиксовая 0.7.7 легла top-level, content-api юзал nested баговую 0.7.4. Фикс: snolla 0.7.7 + core 0.26.1 (код) + content-api 0.8.2 `^0.7.7` (упаковка). Деплой stostayer-web:0.3.22 через pscp SSH:20435 + sudo docker load + Portainer PUT pullImage:false (без push в :443, бана нет). Pre-build чек yarn.lock single-snolla ловит класс. index.md updated (+1). Cleanup `workspace-caret-interpackage-deps` — vitya scope. Memory: stostayer-deploy-image-via-pscp-load-avoid-443-push.md.
## [2026-08-13] refactor | concepts/stostayer-web-deploy-runbook — letter path `.claude-inbox` → `.agents/inbox` (rename idea 13 cascade)
## [2026-08-18] ingest | NEW concepts/sched-fnf-r8-verification-stand.md — стенд верификации F&F r8 (schedule-as-entity): путь, версии core 0.40.1/storage-* 0.3.1, карта 15 репро-скриптов (миграции/движок/политики), подтверждённый контракт, как продолжить №4/№5. NEW concepts/windows-docker-test-harness-gotchas.md — переиспользуемые грабли Windows-верификации (rmSync-EBUSY, vitest-DDL-таймаут, монотонность фейк-часов, изоляция DB, висельники node). index.md updated (+2).
## [2026-08-18] ingest | UPDATE concepts/sched-fnf-r8-verification-stand.md — сессия №4/№5: admin-api-test.mjs (52/52, F1 tz-hoist + F3 PATCH-merge подтверждены на core 0.40.2), UI-стенд ui-backend.mjs + sched-ui serve :8081 + браузерный CDP (13/13), находки U1 (форма-tz interval/once → 400) и U2 (таблица шире контейнера ~58px). index.md hook обновлён.