diff --git a/.tasks/NEXT_SESSION.md b/.tasks/NEXT_SESSION.md index 534ccac..a16c8b0 100644 --- a/.tasks/NEXT_SESSION.md +++ b/.tasks/NEXT_SESSION.md @@ -1,64 +1,61 @@ --- -_last_updated_: 2026-08-21T00:55:00Z -session_id: sched-pipelines-apply-0.5.0-2026-08-20 +_last_updated_: 2026-08-21T01:40:00Z +session_id: sched-cancel-rootcause-2026-08-21 --- # Next session handoff -Сессия: schedd 0.5.0 применён, timeoutMs -1, одновременный dry-run обеих тасок зелёный, мок-publish ТЗ разосланы. +Сессия: cancel-канал доведён до root cause (баг sched daemon-диспетчера), фикс 0.9.0 НЕ применён — первым делом. -## Recent commits (в .admin) +## 🔴 CRITICAL: применить schedd 0.9.0 и повторить cancel-тест -- `7ec721e2` ops: schedd 0.6.0 — timeoutMs применяется к существующим таскам, absent=-1 дефолт (проверено живьём) -- `3060263b` ops: стенд — schedd 0.5.0 (run-deadline), timeoutMs -1, ozon bind-mount state на хост +**sched нашёл root cause cancel-бага (письмо 21:27:46Z):** +- daemon-диспетчер НЕ форвардил `cancel` к per-task раннеру — обёртка Runner содержала только `{run, poll}`, без `cancel`. `runner.cancel` = undefined → `if (ar.cancelUrl && runner.cancel)` = false → **сигнал не слался никогда**, ран финишил `cancelled` с чистым error (провала нет → `signal failed` не откуда). Воркер продолжал стадии. +- Логи 0.8.0 в этом случае врали: `[cancel] no cancelUrl — legacy stop-polling`, хотя cancelUrl был в envelope. +- **Фикс: daemon 0.9.0** (core 0.48.0 остаётся), commit a6c8a4b, на verdaccio latest. Диспетчер форвардит cancel по task.runner; нет хука → no-op. -## Open треки +**Что сделать:** +1. `schedd.Dockerfile`: `@sched/daemon@0.8.0` → `@sched/daemon@0.9.0` (host-stacks/local/sched-pipelines/) +2. `docker compose up -d --build schedd` +3. Повторить cancel-тест: ран ozon (state сброшен в {} — full run) → cancel на generate → по логам `[sched] [cancel] POST runId=...` + `[cancel] ack status=200` + воркер `[cancel] received` + `[cancel] stop before stage ...` + `[stage] ... done` — стоп ДО publish. +4. Если после ack стадии продолжаются — баг воркера (флаг не проверяется), контракт-ревью с sched. -| Трек | Готовность | Entry-point | -|---|---|---| -| `sched-pipelines-local-stack` (🟡 active) | schedd 0.5.0; **оба dry-run зелёные через sched (ozon 8/8, yandex 11/11)**; publish-мок ТЗ на досках команд | `.tasks/sched-pipelines-local-stack.md` | - -## Cancel-канал (раунд 3) - -- **sched** ✅ (core 0.47.0/daemon 0.7.0, на стенде): cancelUrl-канал. **Контракт ратифицирован с обеих сторон** (письма 00:52Z admin + 00:52:37Z sched): A (cancelUrl) — accepted-envelope несёт cancelUrl; cancel → POST {runId} на cancelUrl (auth + x-sched-run-id), ждёт 2xx, потом cancelled; провал → `cancel signal failed`. sched менять нечего — реализация соответствует. Ждём импл воркеров → контракт-ревью против 05.protocol.md. -- **воркеры** ⚪ `worker-cancel-url` (обе команды): принять cancelUrl, POST /cancel эндпоинт, флаг между стадиями ДО мутаций. - -## Раунды ТЗ - -**Раунд 1 (мокабельность publish, оба закрыли):** -- ozon `publish-mockable` ✅ (175/175): data.registry/remoteBase/githubApiBase + PUBLISH_* env. -- yandex `publish-mockable` ✅: data.githubApiBase для REST (registry/remoteBase были). - -**Раунд 2 (Gitea-совместимость, оба на досках ⚪ `gitea-mock-publish`):** -- Мок-publish = verdaccio (npm) + наш Gitea (git.kzntsv.site, орга apilki, API v1). -- ozon: gh repo create CLI → REST POST /orgs/{owner}/repos (Gitea нет /api/v3); remoteBase URL-join. -- yandex: только remoteBase URL-join (REST уже ок). -- Репо руками НЕ создавать — create-if-missing воркерами (тестируемый сценарий). Gitea push auth проверен: x-access-token:TOKEN@ работает. - -**sched** — ✅ ЗАКРЫТО (0.6.0): applyTask timeoutMs + absent=-1 дефолт, проверено живьём. +**Важно про cancel-тест:** cancel теперь реально остановит воркер (если 0.9.0 работает) — воркер не будет публиковать после cancel. Но publish у ozon с `dryRun: true` в таске — безопасно в любом случае. ## Стенд (host-stacks/local/sched-pipelines) -- schedd **0.6.0** (daemon); tasks.json без timeoutMs — дефолт absent=-1 (никогда), применён и проверен. -- Оба воркера свежие (ozon с фиксами ТЗ №1-10: envelope реализован; ym с JSDoc-фиксом). -- Compose: ozon bind-mount `./data-ozon` → /app/data + `data-ozon/state.json` → /app/state.json (раньше state жил в слое образа). ym: `./data-ym` → /app/data. -- Находка для ozon: run.js хардкодит state/output в ROOT образа, /app/data не используется воркером — не включено в их ТЗ (могу добавить позже). +- schedd **0.8.0** (нужно 0.9.0!), ym-client-builder свежий (e7cf3e5 cancel-канал + c740bdc логи), ozon свежий (0649c80 + f021f38 логи). +- tasks.json: ozon stages [...publish] dryRun:true; yandex stages [...publish, githubDistro] dryRun:true. DRY_RUN=1 в compose (безопасный дефолт). +- Ozon state.json на хосте (bind-mount), сейчас {} (сброшен тестами) — следующий ран полный (initial). +- Все контейнеры Up. Исключение: **озон-контейнер был пересоздан** (docker rm + up) — образ тот же, данные в data-ozon/. + +## Мок-publish статус + +- **Мок-npm = verdaccio** (verdaccio.kzntsv.site), **мок-GitHub = Gitea** (git.kzntsv.site, орга `apilki` создана, API v1, push x-access-token:TOKEN@ проверен). +- **Оба dry-publish зелёные**: yandex 13/13 (publish+githubDistro, reposToCreate честно показан), ozon 10/10 (publish dryRun preview). GitHub/Gitea/verdaccio чисты — ноль мутаций. +- Репо руками НЕ создаём — create-if-missing воркеров (тестируемый сценарий). +- data dryRun в тасках: `registry/remoteBase/githubApiBase` через `__PUBLISH_*__` плейсхолдеры в render-tasks.cjs (из .env, gitignored). + +## Команды — все таски закрыты, ждём только применение + +- **sched**: timeoutMs (0.6.0), dry-run контракт (0.7.0 был... нет — 0.7.0 это cancel-канал), cancel-канал (0.7.0), cancel-логи (0.8.0), **cancel-форвардинг фикс (0.9.0 — НЕ применён)**. +- **ozon**: publish-mockable ✅, gitea-mock-publish ✅, dry-run-contract-align ✅, worker-cancel-url ✅, cancel-signal-logging ✅. +- **yandex**: publish-mockable ✅, gitea-mock-publish ✅, dry-run-contract-align ✅, worker-cancel-url ✅, cancel-signal-logging ✅. + +## Инцидент сессии (важно для истории) + +- Yandex **создал репо на github.com** (apilki/yandex-market-typescript) при мок-прогоне — потому что (а) publish без dry-run (тогда ещё не было фикса) и (б) cancel не остановил воркер (баг sched daemon-диспетчера). Репо **удалено** (204→404), npmjs не тронут, verdaccio пуст. GitHub/Gitea сейчас чисты. +- Урок: yandex publish реальный без dryRun — не запускать publish/githubDistro без явного dryRun:true или GO. ## Спроси user'а -1. **Мок-publish стенд**: после фиксов publish-mockable от обеих команд — построить мок-GitHub (локальный HTTP-мок REST + git remote на локальный bare-репо) + мок-npm (verdaccio) и прогнать publish-стадию. Либо просто ждать GO и идти на verdaccio напрямую. -2. **GO на реальный прогон** — пока НЕ дан. План vitya: verdaccio (не npmjs) → books тестирует → релиз. Воркерам: registry verdaccio + NPM_TOKEN verdaccio. -3. **VDS-миграция** — после пары дней локально + зелёных реальных прогонов. +1. Применить schedd 0.9.0 + повторить cancel-тест (см. CRITICAL). +2. GO на реальный мок-publish (снять dryRun:true, публиковать в verdaccio+Gitea) — после зелёного cancel-теста. +3. VDS-миграция — после пары дней локально. ## Не делать -- Не запускать реальные публикации (publish/githubDistro) без явного GO. -- Не слать реальные письма unisender — только мок (data-mail/). -- Не патчить код команд — ТЗ через их инбоксы/доски, они пушат, я пересобираю. -- Не коммитить в репо команд (yandex/ozon/sched) — только в .admin. - -## Memory updates за сессию - -- **sched run-deadline контракт**: task-level `timeoutMs` (-1 = никогда, >0 = фейл через N мс, absent = legacy 25-мин poll-потолок). Баг: applyTask при upsert не переносит timeoutMs. Требование vitya: -1 = дефолт, absent должен быть -1. -- **ozon publish.js**: npm publish хардкод (нет registry), pushUrl хардкод github.com, GH API хардкод — мокабельность только через data-поля (ТЗ). -- **ym github-distro.js**: data.registry (npm) + data.remoteBase (git remotes) уже есть; GH_API константа — нужен data.githubApiBase (ТЗ). +- Не запускать реальные публикации (publish/githubDistro без dryRun:true) без явного GO. +- Не слать реальные письма unisender — только мок. +- Не патчить код команд — ТЗ через их инбоксы/доски. +- Не коммитить в репо команд — только в .admin. diff --git a/.tasks/STATUS.md b/.tasks/STATUS.md index 6c12cbf..d5c8cbe 100644 --- a/.tasks/STATUS.md +++ b/.tasks/STATUS.md @@ -3,8 +3,8 @@ _Updated: 2026-08-20 — 🟡 **[sched-pipelines-local-stack]** schedd 0.5.0 п ## 🟡 [sched-pipelines-local-stack] — локальный стенд: sched daemon + ym/ozon воркеры + browser/ntfy/alert-bridge, потом VDS **Status:** active -**Where I stopped:** schedd **0.5.0** (core 0.45.0) пересобран, `timeoutMs: -1` на обе таски (не применился — баг sched applyTask, письмо 19:30Z + требование vitya absent=-1). **Одновременный dry-run через sched — оба зелёные**: ozon 8/8, yandex 11/11. Ozon пересобран с их фиксами (envelope реализован), ym свежий. Мок-publish: ТЗ обеим командам `publish-mockable` (ozon: registry/remoteBase/githubApiBase — у них хардкод; ym: только githubApiBase). Compose: ozon bind-mount state на хост (был в слое образа). Осталось: GO на реальный прогон → verdaccio → книги → релиз. -**Next action:** ждать фиксы команд: sched (timeoutMs applyTask + absent=-1 дефолт), ozon+ym (publish-mockable). После: мок-publish стенд (локальный мок-GitHub REST + verdaccio), контракт-ревью envelope, GO vitya → реальный прогон → VDS. +**Where I stopped:** schedd **0.8.0** (нужно 0.9.0 — cancel-форвардинг фикс НЕ применён, см. NEXT_SESSION CRITICAL). Мок-publish dry **оба зелёные** (yandex 13/13 вкл publish+githubDistro, ozon 10/10 вкл publish; GitHub/Gitea/verdaccio чисты, ноль мутаций). Cancel-канал: все команды закрыли свои части (sched 0.7.0 канал + 0.8.0 логи; ozon 0649c80+f021f38; yandex e7cf3e5+c740bdc). **Root cause cancel-бага: sched daemon-диспетчер не форвардил cancel к раннеру** — сигнал не слался никогда; фикс daemon 0.9.0 на verdaccio. Инцидент: yandex создал репо на github.com при мок-прогоне (publish без dry-run + cancel не остановил) — удалено, всё чисто. +**Next action:** (1) schedd 0.9.0 + повторный cancel-тест (стоп воркера до publish); (2) GO на реальный мок-publish (снять dryRun:true → verdaccio+Gitea); (3) VDS-миграция. **Branch:** master **Weight:** needs-claude _Updated: 2026-08-19 — 🟢 **[sched-fnf-r10] ЗАКРЫТ (их ack 17:24Z): 6/6 PASS, 0 находок, 0 замечаний** — первый раунд без фиксов после режекта. Per-task alert routing (core 0.44.0/daemon 0.4.2): r10-a регресс, r10-b on:[] тишина (массивы replace), r10-c per-task on, r10-d per-task onMissed (missed-slot только для A, delayMs 129016), r10-e мультиканал (+без корневого webhook), r10-f fail-fast на load (3 варианта). Бонус HMAC MATCH. Процесс: раунд начинался с режекта (доки не сгенерированы/битый пример/не запушено), их фикс + push + сгенерированный dist + исправленный пример → ретест 6/6. Урок: F&F тестируется по СКОМПИЛИРОВАННОЙ доке (dev-сервер localhost:5108), не по сырцам; порядок хендоффа 1–6 у них в fnf-testing-procedure.md. Репорт: sched/.agents/inbox/2026-08-19T17-24-00Z-admin-fnf-r10-report.md. Стенд: /tmp/sched-alerts-stand/r10/._ diff --git a/host-stacks/local/sched-pipelines/schedd.Dockerfile b/host-stacks/local/sched-pipelines/schedd.Dockerfile index 9708aae..62920ed 100644 --- a/host-stacks/local/sched-pipelines/schedd.Dockerfile +++ b/host-stacks/local/sched-pipelines/schedd.Dockerfile @@ -1,6 +1,6 @@ # schedd — опубликованный daemon из приватного реестра (verdaccio), как на проде. -# @sched/daemon@0.7.0 (latest) + deps (core 0.47.0) — cancel-канал воркеру: accepted-envelope -# несёт cancelUrl, POST /runs/:id/cancel сначала POSTит {runId} на cancelUrl, потом финиширует. +# @sched/daemon@0.8.0 (latest) + deps (core 0.48.0) — cancel-сигнал логируется в stdout +# ([cancel] POST/ack/failed/no cancelUrl), Runner.cancel возвращает транспортный статус. # node >= 24 (engines у daemon; node:sqlite). FROM node:24-alpine RUN apk add --no-cache git @@ -8,7 +8,7 @@ WORKDIR /app ENV NODE_ENV=production ARG VERDACCIO_TOKEN RUN npm config set //verdaccio.kzntsv.site/:_authToken=${VERDACCIO_TOKEN} \ - && npm install -g @sched/daemon@0.7.0 --registry=https://verdaccio.kzntsv.site --no-audit --no-fund + && npm install -g @sched/daemon@0.8.0 --registry=https://verdaccio.kzntsv.site --no-audit --no-fund EXPOSE 8080 VOLUME ["/data"] ENTRYPOINT ["schedd"]