Files
admin/.wiki/concepts/sched-fnf-r8-verification-stand.md

8.5 KiB
Raw Blame History

title, type, tags, sources, related, updated
title type tags sources related updated
sched F&F r8 — стенд верификации (schedule-as-entity) concept
sched
fnf
verification
stand
verdaccio
schedule-as-entity
docker
concepts/windows-docker-test-harness-gotchas.md
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 пополнится результатами).