diff --git a/.tasks/infra-inventory.md b/.tasks/2026-05-22-00196-infra-inventory.md similarity index 100% rename from .tasks/infra-inventory.md rename to .tasks/2026-05-22-00196-infra-inventory.md diff --git a/.tasks/stateful-split-volume-copy.md b/.tasks/2026-05-25-00234-stateful-split-volume-copy.md similarity index 100% rename from .tasks/stateful-split-volume-copy.md rename to .tasks/2026-05-25-00234-stateful-split-volume-copy.md diff --git a/.tasks/harden-books-vds-exposed-ports.md b/.tasks/2026-05-29-00265-harden-books-vds-exposed-ports.md similarity index 100% rename from .tasks/harden-books-vds-exposed-ports.md rename to .tasks/2026-05-29-00265-harden-books-vds-exposed-ports.md diff --git a/.tasks/fix-nl-vds-reality-pq-dest.md b/.tasks/2026-06-05-00301-fix-nl-vds-reality-pq-dest.md similarity index 100% rename from .tasks/fix-nl-vds-reality-pq-dest.md rename to .tasks/2026-06-05-00301-fix-nl-vds-reality-pq-dest.md diff --git a/.tasks/agents-task-runner-vds-deploy.md b/.tasks/2026-06-08-00314-agents-task-runner-vds-deploy.md similarity index 100% rename from .tasks/agents-task-runner-vds-deploy.md rename to .tasks/2026-06-08-00314-agents-task-runner-vds-deploy.md diff --git a/.tasks/kreknin-self-backup.md b/.tasks/2026-06-12-00356-kreknin-self-backup.md similarity index 100% rename from .tasks/kreknin-self-backup.md rename to .tasks/2026-06-12-00356-kreknin-self-backup.md diff --git a/.tasks/llm-web-proxy.md b/.tasks/2026-08-22-00803-llm-web-proxy.md similarity index 100% rename from .tasks/llm-web-proxy.md rename to .tasks/2026-08-22-00803-llm-web-proxy.md diff --git a/.tasks/llm-web-proxy-qwen-web.md b/.tasks/2026-08-22-00804-llm-web-proxy-qwen-web.md similarity index 100% rename from .tasks/llm-web-proxy-qwen-web.md rename to .tasks/2026-08-22-00804-llm-web-proxy-qwen-web.md diff --git a/.tasks/STATUS.md b/.tasks/STATUS.md index acf872d..3f5d540 100644 --- a/.tasks/STATUS.md +++ b/.tasks/STATUS.md @@ -7,9 +7,10 @@ _Updated: 2026-08-21 — 🟢 **[sched-fnf-daemon-image-storage] F&F ЗАКРЫ _Updated: 2026-08-21 — 🟢 **[sched-fnf-daemon-image-storage] F&F 7/7 PASS** (их письмо 23:12Z, таска sched 🟢 done). Образ `registry.kzntsv.site/sched-daemon:0.10.0` из опубликованного `@sched/daemon` + storage-композиция: 1) sqlite-дефолт баннер `storage=sqlite db=…` + раны в sqlite; 2) XOR `--storage mongo --db x.db` → точный отказ; 3) env fail-fast ДО старта (`MONGO_URL is not set`); 4) mongo-композицию собрал САМ (BuildKit secret, `@sched/storage-mongo@0.4.0` из verdaccio) → коллекции sched_tasks/runs/schedules + ран в mongo, баннер `storage=mongo db=…`; 5) missing-адаптер → хинт `add @sched/storage-mysql to your image`; 6) BYO `./my-storage.mjs` (MemoryStorage-порт пинового референса) → `storage=custom`, негатив без createStorage → точная ошибка контракта; 7) креды в connstring → весь лог = 1 строка баннера, 0 утечек. Находки: **F1** `deploy/tasks.json` (монтируется docker-compose.dev.yml, док. в 13.self-hosting) на старом ключе `schedule` → daemon 0.10.0 fatal (док. `docker compose up` не стартует); **F2** рецепт производного образа в 13.self-hosting без `ARG VERDACCIO_TOKEN` → YN0041 anonymous-auth при сборке 1-в-1 по докам; при фиксе через --build-arg токен оседает в image history (видно в их `schedd-mongo:0.10.0`; моя secret-сборка — 0 вхождений). N1: на винде git-bash нужен `MSYS_NO_PATHCONV=1` (иначе /app/… → `C:/Program Files/Git/…`). N2: BYO-баннер `db=default`. Репорт: `sched/.agents/inbox/2026-08-21T23-27-41Z-admin.md`. Стенд: `.tmp/fnf-daemon-image/` (gitignored), mongo-стенд возвращён в исходное. _Updated: 2026-08-20 — 🟡 **[sched-pipelines-local-stack]** schedd **0.9.0 применён + cancel-тест ЗЕЛЁНЫЙ**. **Мок-publish: режект #7 ozon** — фиксы #1-#6 приняты: npm ✅ create ✅ clone ✅ commit ✅ push fast-forward ✅ (2-й ран), release-409 ветка добавлена. 7-й баг: 409-ветка падает на стенде — `GET existing → 200` throw, т.к. прод httpGetFn=fetch отдаёт Response (.body=ReadableStream), а тест мокал `{body:{id:42}}` — разрыв тест/прод #2 (как режект #2, только httpGet). Gitea реально отдаёт id:114. Письмо 00:05Z. Ждём фикс #7. -## 🔴 [llm-web-proxy] — кастомный web-LLM провайдер для pi + Claude Code (web-чаты, эмуляция тулов, без оверкилла omniroute) +## 🔴 [#803 llm-web-proxy] — кастомный web-LLM провайдер для pi + Claude Code (web-чаты, эмуляция тулов, без оверкилла omniroute) **Status:** active — claimed 2026-08-22 (vitya, креды из omniroute) +**Created:** 2026-08-22 **Where I stopped:** Сессия 2026-08-22 закрыта — провайдер построен (58→82 теста) и развёрнут в систему: `npm i -g` v0.1.6 (глобально, autostart-пути починены), дашборд (/metrics + HTML, окна/эндпоинты/локальное время), executor-логирование спеки (tool-prompt/raw-reply/parse/upstream/session/stream), `lwp config set port/prefix`, настраиваемый modelPrefix (lwp/ — дефолт без префикса). Провайдер `llm-web` в pi → модели `lwp/deepseek-*-web`. Скилл web-search + тул search_web (pi-extensions) — RED→GREEN пройден, live-проверен. Осталось: п.2 mac/linux autostart live-тест (не проверить на win), п.4 qwen-web (DNS EAI_AGAIN), п.5 gemini/kimi/grok-web кандидаты. **Next action:** вытащить из docker-контейнера omniroute (`/app/open-sse/`): executors/deepseek-web.ts + translators (deepseekWebTools.ts, webTools.ts, claude-to-openai.ts, openai-to-claude.ts); собрать скелет src/server.js (`/v1/chat/completions` + `/v1/messages` + `/health`) с round-robin и логированием (req/tool-prompt/raw-reply/parse/upstream/session/stream/failover). **Branch:** master @@ -19,9 +20,10 @@ _Updated: 2026-08-20 — 🟡 **[sched-pipelines-local-stack]** schedd **0.9.0 --- -## 🟡 [llm-web-proxy-qwen-web] — добавить qwen-web комбо в lwp (подзадача llm-web-proxy, п.4) +## 🟡 [#804 llm-web-proxy-qwen-web] — добавить qwen-web комбо в lwp (подзадача llm-web-proxy, п.4) **Status:** paused — решение vitya 2026-08-22: стоп до следующей сессии (покажет рабочий подход; «из под omniroute работает» — там есть живой путь к qwen) +**Created:** 2026-08-22 **Where I stopped:** EXECUTOR ГОТОВ и задеплоен (v0.1.8, 94 теста, комбо qwen-web/qwen-main, свежий cookie юзера в secrets.json). СТЕНА: Alibaba baxia-WAF ставит слайдер-капчу (FAIL_SYS_USER_VALIDATE / RGV587 / punish?action=captcha) на ЛЮБОЙ не-браузерный запрос — даже со свежим cookie и полным jar. Проверено: (1) Node-fetch со всеми вариантами заголовков (sec-ch-ua, Accept: text/event-stream, bx-umidtoken статический/отсутствует) — капча; (2) настоящий Chrome через CDP (:9222, browser-tools профиль) — chats/new ПРОХОДИТ (браузерный фингерпринт), но completion виснет, капча возвращается; капчу юзер разгадывал в CDP вручную. Ключевая зацепка: omniroute qwen-web работает («из под omniroute работает») — надо посмотреть как (у omniroute в контейнере своя версия executor'а + возможно решённый punish-flow / живой браузерный путь). **Next action:** сессия-2: юзер покажет рабочий подход; сравнить с executor'ом в контейнере omniroute (/app/open-sse/executors/qwen-web.ts — версия в контейнере ≠ GitHub main, которую я портировал); варианты: реальный bx-umidtoken, CDP-драйвеный executor, либо путь omniroute. **Branch:** master @@ -31,8 +33,9 @@ _Updated: 2026-08-20 — 🟡 **[sched-pipelines-local-stack]** schedd **0.9.0 --- -## 🟢 [sched-pipelines-local-stack] — локальный стенд: sched daemon + ym/ozon воркеры + browser/ntfy/alert-bridge, потом VDS +## 🟢 [#746 sched-pipelines-local-stack] — локальный стенд: sched daemon + ym/ozon воркеры + browser/ntfy/alert-bridge, потом VDS **Status:** done +**Created:** 2026-08-20 **Closing note:** ЗАКРЫТА 2026-08-22 (ритуал закрытия, решение vitya). Мок-кампания финализирована ранее: оба воркера publish 0.1.0, повторные раны noop, кейс «спека→0.2.0» проверен симуляцией и откачен; фикс #7 (httpGet fetch-Response контракт) принят. Реальный деплой (npmjs + github.com) — отдельное решение юзера, в «Спроси user'а» handoff'а; VDS-миграция — таска sched-vds-deploy (paused). **Where I stopped:** schedd **0.9.0** (verdaccio latest, core 0.48.0), commit 225d235c. Cancel-тест зелёный. **Мок-publish: раны ozon 10-11 (фиксы #1-#6)** — ран 1 (create, state {}): **succeeded** — npm 0.1.0, create-repo, clone+commit+push, релиз v0.1.0 создан ✅. Ран 2 (state {}, репо существуют, npm unpublish): npm ✅, **push fast-forward ✅** (typescript dc31349 + postman e390f93), но **failed**: `github: release ... v0.1.0 (409): GET existing → 200` — Gitea отдаёт id:114 (curl подтвердил), но прод httpGetFn=fetch → Response (.body=ReadableStream, .id undefined) → throw. Тест мокал `{body:{id:42}}` (publish-gh.test.js:236-239) — **разрыв тест/прод #2** (класс режекта #2). Письмо 00:05Z (ТЗ: единый контракт httpGet — parsed body через fetch-обёртку ИЛИ .json() в коде; тест с реальным new Response()). Замечание cleanup принято (следы допустимы, warn). tasks.json: dryRun:false у обоих, расписания УБРАНЫ (manual-only). **Next action:** ЗАКРЫТА. См. handoff 2026-08-22 (реальный деплой — за отмашкой юзера). @@ -126,9 +129,10 @@ _Updated: 2026-05-26 — заведена `bookva-tenant-cutover-prep` ⚪ (7-st _Updated: 2026-05-25 (`iis-migration-to-ruvds` 🟢 closed Phase 1 per user decision — 9/24 hostnames live на RUVDS, source IIS оставлен running. `migrate-elasticsearch-to-books-vds` 🟢 closed ранее сегодня. 16 IIS hostnames + LE renewal pipeline + decommission — descoped в Closure note, не отдельные tracker tasks.)_ -## 🟢 [snolla-mailer-per-recipient-send] — Mailer: по одному письму на каждого получателя (loop), не в один To. Холодный ящик e-16513832@yandex.ru → 554 SPAM на 2+ получателях; 11 форм с 2–3 админами падают. +## 🟢 [#657 snolla-mailer-per-recipient-send] — Mailer: по одному письму на каждого получателя (loop), не в один To. Холодный ящик e-16513832@yandex.ru → 554 SPAM на 2+ получателях; 11 форм с 2–3 админами падают. **Status:** done +**Created:** 2026-08-14 **Where I stopped:** деплой тиража выполнен и принят 2026-08-14: pilonuxt 8b7e8c4, 7 env-сайтов web@0.44.0, data 0.15.1 единый; фикс mailSettings.from в production.json (550); контрольные заказы pilorama98 #2498 (3 письма) + kupimknigi #516 (2 письма) — 0 ошибок, mailer 0.9.0 в 8 контейнерах. Snolla уведомлён (закрывает логи). **Next action:** (none — kept until merged) **Branch:** master @@ -139,9 +143,10 @@ _Updated: 2026-05-25 (`iis-migration-to-ruvds` 🟢 closed Phase 1 per user deci --- -## 🟢 [snolla-smtp-send-monitor-vds] — DONE 2026-08-14 — периодический мониторинг SMTP-отправки писем форм snolla-сайтов с VDS (ловит тихие сбои почты типа 12–14.08.2026), алерт через ntfy. Деплой: `/root/snolla-smtp-monitor/monitor.py` (python3 stdlib: smtp-auth тест + weekly real-send + parity Portainer env 7 стеков) + cron `0 8 * * *` + креды `/root/.snolla-smtp-monitor.env` (600). Негатив-тест пройден (сломанный пароль → 535 → ntfy HTTP 200 → exit 1). Parity ЗЕЛЁНЫЙ после роллаута 2026-08-14. Копия скрипта: `.admin/scripts/snolla-smtp-monitor/monitor.py`. +## 🟢 [#658 snolla-smtp-send-monitor-vds] — DONE 2026-08-14 — периодический мониторинг SMTP-отправки писем форм snolla-сайтов с VDS (ловит тихие сбои почты типа 12–14.08.2026), алерт через ntfy. Деплой: `/root/snolla-smtp-monitor/monitor.py` (python3 stdlib: smtp-auth тест + weekly real-send + parity Portainer env 7 стеков) + cron `0 8 * * *` + креды `/root/.snolla-smtp-monitor.env` (600). Негатив-тест пройден (сломанный пароль → 535 → ntfy HTTP 200 → exit 1). Parity ЗЕЛЁНЫЙ после роллаута 2026-08-14. Копия скрипта: `.admin/scripts/snolla-smtp-monitor/monitor.py`. **Status:** done +**Created:** 2026-08-14 **Where I stopped:** (not started) — спецификация в `.tasks/snolla-smtp-send-monitor-vds.md` (дизайн: SMTP auth-тест + parity-чек Portainer env, вариант A = host-cron на VDS) **Next action:** Собрать node-скрипт SMTP auth-теста (nodemailer verify на smtp.yandex.ru:465, креды `e-16513832@yandex.ru`/app-password из `pass snolla-smtp/full-env`) → положить на VDS (креды в `/root/.snolla-smtp-monitor.env` chmod 600) → cron ~08:00 MSK → негатив-тест: временно сломать пароль → ntfy-алерт приходит → parity-чек env Portainer-стеков vs канон. **Branch:** master @@ -151,11 +156,13 @@ _Updated: 2026-05-25 (`iis-migration-to-ruvds` 🟢 closed Phase 1 per user deci --- -## 🟢 [maljarka-vds-restore] — closed 2026-07-31 — maljarka.tandemmebel.ru восстановлен как 7-й сайт тиража snolla на VDS. Per-task/runbook: `maljarka-vds-deploy-runbook.md`. .NET MoreThenCms tenant (Reversal, siteId `A2476738`) потерян при decomm RUVDS 2026-07-21 → восстановлен Node snolla-app 0.43.2 на VDS (стек 23). `victor/maljarka.tandemmebel.ru` @ `7f03d67`, layout.liquid byte-identical .NET-admin рендеру. LE issued. Live GREEN. +## 🟢 [#592 maljarka-vds-restore] — closed 2026-07-31 — maljarka.tandemmebel.ru восстановлен как 7-й сайт тиража snolla на VDS. Per-task/runbook: `maljarka-vds-deploy-runbook.md`. .NET MoreThenCms tenant (Reversal, siteId `A2476738`) потерян при decomm RUVDS 2026-07-21 → восстановлен Node snolla-app 0.43.2 на VDS (стек 23). `victor/maljarka.tandemmebel.ru` @ `7f03d67`, layout.liquid byte-identical .NET-admin рендеру. LE issued. Live GREEN. -## 🟢 [snolla-local-admin-restore] — closed 2026-07-20 — локальный .NET-админ (catch-all IIS `snolla`) поднят с RUVDS, `/admin` для тиража+on.snolla.com. Per-task: `snolla-local-admin-restore.md`. Selective tar-copy ~100MB (НЕ 8.76GB, исключил stale App_Data assets/galleries/themes). Web.config уже на mssql.kzntsv.site+MinIO (repoint не нужен, ключи реальные). Elevated setup: `scripts/local-snolla-admin-restore/setup-local-snolla-admin.ps1`. Smoke GREEN: 6 адресов `/admin/account/login` → 200 (tandemmebel/labtools/labtoolspro/emspb/kupimknigi/on), реальная MoreThenCms-логинформа. unblocks `on-snolla-vds-migration`. +**Created:** 2026-07-31 +## 🟢 [#579 snolla-local-admin-restore] — closed 2026-07-20 — локальный .NET-админ (catch-all IIS `snolla`) поднят с RUVDS, `/admin` для тиража+on.snolla.com. Per-task: `snolla-local-admin-restore.md`. Selective tar-copy ~100MB (НЕ 8.76GB, исключил stale App_Data assets/galleries/themes). Web.config уже на mssql.kzntsv.site+MinIO (repoint не нужен, ключи реальные). Elevated setup: `scripts/local-snolla-admin-restore/setup-local-snolla-admin.ps1`. Smoke GREEN: 6 адресов `/admin/account/login` → 200 (tandemmebel/labtools/labtoolspro/emspb/kupimknigi/on), реальная MoreThenCms-логинформа. unblocks `on-snolla-vds-migration`. -## 🟢 [on-snolla-vds-migration] — CLOSED 2026-08-03 — on.snolla.com (siteId B9ECDB50…) с RUVDS IIS → VDS Node snolla-app 0.42.1 (стек 22), **live с 2026-07-20**. Реконструкция шаблонов из боевого+админки. Per-task: `on-snolla-vds-migration.md` (§ As-built 2026-07-20). Design: `../.wiki/concepts/snolla-local-admin-and-on-snolla-migration-design.md` §Task B. Статус-маркер «⚪ ready» не был перевёрнут при live — исправлено 2026-08-03. +**Created:** 2026-07-20 +## 🟢 [#578 on-snolla-vds-migration] — CLOSED 2026-08-03 — on.snolla.com (siteId B9ECDB50…) с RUVDS IIS → VDS Node snolla-app 0.42.1 (стек 22), **live с 2026-07-20**. Реконструкция шаблонов из боевого+админки. Per-task: `on-snolla-vds-migration.md` (§ As-built 2026-07-20). Design: `../.wiki/concepts/snolla-local-admin-and-on-snolla-migration-design.md` §Task B. Статус-маркер «⚪ ready» не был перевёрнут при live — исправлено 2026-08-03. ## As-built (2026-07-20) - Repo `victor/on.snolla.com` pushed (git.kzntsv.site). Structure-clone of tandemmebel.ru. @@ -167,9 +174,11 @@ _Updated: 2026-05-25 (`iis-migration-to-ruvds` 🟢 closed Phase 1 per user deci - Completeness-gate: sitemap 3/3 parity, homepage + /yandex byte-identical, 0 regressions. -## 🟢 [kupimknigi-deploy-snolla-0-42-0] — VDS-staging образ kupimknigi.spb.ru на snolla@0.42.0 (одностраничник, финал тиража). Per-task: `kupimknigi-deploy-snolla-0-42-0.md`. +**Created:** 2026-07-20 +## 🟢 [#559 kupimknigi-deploy-snolla-0-42-0] — VDS-staging образ kupimknigi.spb.ru на snolla@0.42.0 (одностраничник, финал тиража). Per-task: `kupimknigi-deploy-snolla-0-42-0.md`. **Status:** done — 2026-07-05. ✅ Собрал `registry.kzntsv.site/kupimknigi:9608ff6` (digest `ac7f846`, 583MB) на VDS из sha 9608ff6 (snolla 0.42.0/core 0.24.0/data 0.14.1), запушил. Создал **новый Portainer-стек Id 21 `kupimknigi`** (POST create/standalone, env verbatim 8/8 из стека 20 — тот же тенант) → контейнер **healthy** сразу, running==9608ff6. Staging-host kupimknigi.vds.kzntsv.site. +**Created:** 2026-07-05 **Where I stopped:** GREEN. Staging-smoke с VDS пройден: `/`→200==prod; H1 «Скупка старых книг в Санкт-Петербурге…» идентичен prod; robots.txt 200 (healthcheck); тема-ассет `/themes/ef2c663c…/css/toolbox.css`→200 (MinIO); форма `action="/callback-order"` в HTML; callback-роуты паритет prod (`/callback-order/`→301 canonical, `/callback-order`→404 POST-only); байты 17717≈prod 17672. Форму не сабмитил (POST=реальное письмо клиенту) — роут зарегистрирован==prod, достаточно. **Next action:** — (закрыто, ВКЛ. CUTOVER). ✅ **CUTOVER ВЫПОЛНЕН 2026-07-05:** оператор флипнул DNS reg.ru `kupimknigi.spb.ru` 80.64.31.36→89.253.255.94; подтвердил на ОБОИХ авторит.NS (ns1+ns2.reg.ru); вставил боевой `Host(kupimknigi.spb.ru)` в стек 21 (PUT 200), убрал staging-host. LE-серт выпущен (CN=kupimknigi.spb.ru, valid Jul5→Oct3 2026), боевой хост GREEN: /→200 H1 «Скупка старых книг…», robots/тема-css 200, callback canonical 301. **kupimknigi.spb.ru живёт на VDS.** RUVDS IIS 80.64.31.36 = rollback (revert DNS), НЕ тронут/декоммишн. **Weight:** needs-claude · **Notify:** OpeItcLoc03/workshop @@ -177,8 +186,9 @@ _Updated: 2026-05-25 (`iis-migration-to-ruvds` 🟢 closed Phase 1 per user deci --- -## 🟢 [morethencms-s3-filestorage-provider] — CLOSED 2026-08-04 (по оператору: «сделано давно»). S3-провайдер FileStorage для MoreThenCms (MinIO), закрывает split-brain админка↔фронт. +## 🟢 [#520 morethencms-s3-filestorage-provider] — CLOSED 2026-08-04 (по оператору: «сделано давно»). S3-провайдер FileStorage для MoreThenCms (MinIO), закрывает split-brain админка↔фронт. **Status:** 🟢 done +**Created:** 2026-07-03 **Closing note:** По подтверждению оператора 2026-08-04 провайдер реализован и в работе давно — таска закрывается. (Детали реализации — в [morethencms-s3-filestorage-provider.md](morethencms-s3-filestorage-provider.md).) **Where I stopped:** Разведка 500 на `/admin/assets/.../delete` → корень RX-only ACL на `App_Data` (пофикшен `icacls …:(M) /T`, 44293 файла). `labtools-price.pdf` заменён в MinIO+локалке (MD5 сверены). ТЗ на провайдер: `~/projects/MoreThenCms/.agents/inbox/2026-07-03T06-40-09Z-admin.md`. Решения: все контентные классы, 1 MinIO, ключ без ведущего слэша (parity), deploy → локальный IIS windows-recovery-host → боевой. DNS-аудит чист. **Next action:** — (закрыто по оператору). @@ -187,8 +197,9 @@ _Updated: 2026-05-25 (`iis-migration-to-ruvds` 🟢 closed Phase 1 per user deci --- -## 🟡 [fix-nl-vds-reality-pq-dest] — Reality-инбаунд 443 на NL VDS (213.176.64.253): server-side done, ждёт client-side +## 🟡 [#301 fix-nl-vds-reality-pq-dest] — Reality-инбаунд 443 на NL VDS (213.176.64.253): server-side done, ждёт client-side **Status:** 🟡 server-side fix + cred-rotation применены и проверены 2026-06-05; остался один client-side шаг (user меняет SNI в v2rayN) + подтверждение → закрыть. +**Created:** 2026-06-05 **Where I stopped:** ✅ dest `www.intel.com`→`www.microsoft.com` на инбаунде 443 (x-ui.db + restart), сквозной тоннель через реальный :443 (SNI microsoft) = HTTP 204; 32030 жив. ✅ Креды ротированы (root SSH pass / panel user+pass / panel secret-JWT — оригиналы светились в плейнтексте), всё в `pass nl-vds-3xui/full-env`, БД-бэкап на сервере. Root cause: ML-DSA-65 (PQ) × intel/Akamai (HRR), разбор в `.wiki/concepts/reality-pq-mldsa65-dest-incompatibility.md`. **Next action:** user в v2rayN профиль `pqmkayaxo2`: SNI → `www.microsoft.com`, reconnect. Подтвердит, что Reality поднялся → 🟢 close. См. [fix-nl-vds-reality-pq-dest.md](fix-nl-vds-reality-pq-dest.md). **Branch:** n/a (admin ops) @@ -196,8 +207,9 @@ _Updated: 2026-05-25 (`iis-migration-to-ruvds` 🟢 closed Phase 1 per user deci --- -## 🟢 [llm-router-failover-proxy] — ЗАКРЫТА юзером 2026-08-18 («забудь» — сделано). Не трогать. +## 🟢 [#676 llm-router-failover-proxy] — ЗАКРЫТА юзером 2026-08-18 («забудь» — сделано). Не трогать. **Status:** 🟢 closed — юзер: «забудь про 3 … Сделано» (2026-08-18). Трек выброшен. +**Created:** 2026-08-16 **Where I stopped:** служба llm-router запущена и работает; routerai-verify НЕ делался — юзер сказал забыть. **Next action:** нет. Не проверять, не упоминать. **Branch:** n/a @@ -205,8 +217,9 @@ _Updated: 2026-05-25 (`iis-migration-to-ruvds` 🟢 closed Phase 1 per user deci --- -## ⚪ [kreknin-self-backup] — второй таргет для приёмника бэкапов (на потом) +## ⚪ [#356 kreknin-self-backup] — второй таргет для приёмника бэкапов (на потом) **Status:** ⚪ backlog — заведена 2026-06-12 по итогам backup-gap аудита, user: «на потом». +**Created:** 2026-06-12 **Where I stopped:** kreknin (`195.19.90.188`, `/volume1` 7 ТБ) — единственный приёмник всех 4 пайплайнов, сам не бэкапится → SPOF всей estate. Направление: второй облачный таргет (Backblaze B2 / Synology Hyper Backup, client-side encryption) для critical-subset (`*/latest` ~30 ГБ + `.hbk` vault). **Next action:** при подъёме — выбрать B2 vs Glacier, настроить DSM Hyper Backup на subset. См. [kreknin-self-backup.md](kreknin-self-backup.md) + `.wiki/concepts/backup-inventory-2026-06.md`. **Branch:** n/a (admin ops) @@ -214,7 +227,7 @@ _Updated: 2026-05-25 (`iis-migration-to-ruvds` 🟢 closed Phase 1 per user deci --- -## 🟢 [vehicles-loader-progress-deploy] — closed 2026-05-31 — 0.4.0 задеплоен + live-verify прошёл. Прогон Sun 31.05 06:21:48→07:53:14 MSK на changed-выгрузке: журнал показал движение по всем фазам (▶ branches/vehicles/units/delete-sweep, батч-прогресс `manufacturers 18/183…`, `units 3/26…`, `✓ done: N rows`, per-model delete-sweep), НЕ тишина. exit 0, `importRun id=4 ok`, `reportJson errors:[]`. **Email-баг найден+починен:** `STOSTAYER_MAIL_TO` слался сам себе (`site@stostayer.ru`) → исправлен на `vitya.kuznetsov@gmail.com` в host env (бэкап `.bak.20260531`), доставка на gmail подтверждена тест-письмом. Все 4 acceptance ✅. См. [vehicles-loader-progress-deploy.md](vehicles-loader-progress-deploy.md). +## 🟢 [#277 vehicles-loader-progress-deploy] — closed 2026-05-31 — 0.4.0 задеплоен + live-verify прошёл. Прогон Sun 31.05 06:21:48→07:53:14 MSK на changed-выгрузке: журнал показал движение по всем фазам (▶ branches/vehicles/units/delete-sweep, батч-прогресс `manufacturers 18/183…`, `units 3/26…`, `✓ done: N rows`, per-model delete-sweep), НЕ тишина. exit 0, `importRun id=4 ok`, `reportJson errors:[]`. **Email-баг найден+починен:** `STOSTAYER_MAIL_TO` слался сам себе (`site@stostayer.ru`) → исправлен на `vitya.kuznetsov@gmail.com` в host env (бэкап `.bak.20260531`), доставка на gmail подтверждена тест-письмом. Все 4 acceptance ✅. См. [vehicles-loader-progress-deploy.md](vehicles-loader-progress-deploy.md). **Follow-ups (не блокеры, в stostayer.new):** (1) units-фаза 1h31m на 100529 rows — узкое место, батч-инсёрт/индексы; (2) generation `unmatched:51` — проверить не теряем ли данные; (3) `config/default.json` дефолт `to: site@stostayer.ru` выровнять (прод перекрыт env). **Branch:** n/a @@ -222,7 +235,8 @@ _Updated: 2026-05-25 (`iis-migration-to-ruvds` 🟢 closed Phase 1 per user deci --- -## 🟢 [restore-elasticsearch-indices-books-vds] — closed 2026-05-28 — snapshot restore из `kreknin:daily-2026-05-25` за 46 сек (epz=820604, products=105922, artmone=2621, counts == source). Forensics: 5 индексов удалены через ES API `DELETE _all` 26.05 10:21 UTC, через 1ч 11мин после создания `bookva-es` — оператор в cutover-prep попал на canonical вместо internal-only bookva. Caller identity unrecoverable (audit log = X-Pack платный, traefik accessLog был выключен). 2 preventive фикса applied: ES env `action.destructive_requires_name=true` + traefik JSON accessLog в `/letsencrypt/access.log`. См. [restore-elasticsearch-indices-books-vds.md](restore-elasticsearch-indices-books-vds.md) § Closure + `.wiki/concepts/es-destructive-delete-incident-2026-05-26.md`. +**Created:** 2026-05-30 +## 🟢 [#256 restore-elasticsearch-indices-books-vds] — closed 2026-05-28 — snapshot restore из `kreknin:daily-2026-05-25` за 46 сек (epz=820604, products=105922, artmone=2621, counts == source). Forensics: 5 индексов удалены через ES API `DELETE _all` 26.05 10:21 UTC, через 1ч 11мин после создания `bookva-es` — оператор в cutover-prep попал на canonical вместо internal-only bookva. Caller identity unrecoverable (audit log = X-Pack платный, traefik accessLog был выключен). 2 preventive фикса applied: ES env `action.destructive_requires_name=true` + traefik JSON accessLog в `/letsencrypt/access.log`. См. [restore-elasticsearch-indices-books-vds.md](restore-elasticsearch-indices-books-vds.md) § Closure + `.wiki/concepts/es-destructive-delete-incident-2026-05-26.md`. **Branch:** n/a @@ -230,8 +244,10 @@ _Updated: 2026-05-25 (`iis-migration-to-ruvds` 🟢 closed Phase 1 per user deci --- -## ⚪ [harden-books-vds-exposed-ports] — закрыть публично торчащие host-порты на books VDS (89.253.255.133) +**Created:** 2026-05-28 +## ⚪ [#265 harden-books-vds-exposed-ports] — закрыть публично торчащие host-порты на books VDS (89.253.255.133) **Status:** ready +**Created:** 2026-05-29 **Where I stopped:** exposure-audit сделан (ES :9200 уже закрыт в рамках инцидента); mongo/books-db/bookva-db/minio/bookva-minio + rsync:873 торчат на `0.0.0.0`, все credentialed (auth включён, не дыра-нараспашку как free-ES). **Next action:** по каждому сервису определить — приложение коннектится через docker-сеть или host-порт; нужен ли внешний admin (тогда SSH-туннель / DOCKER-USER IP-allowlist вместо публичного порта). Начать с mongo (EOL 4.2 + reuse пароля). **Branch:** n/a (admin ops) @@ -239,7 +255,7 @@ _Updated: 2026-05-25 (`iis-migration-to-ruvds` 🟢 closed Phase 1 per user deci --- -## 🟢 [modulair-rag-vds-redeploy] — closed 2026-05-27 — 4-контейнерный стек развёрнут на VDS, acceptance 6/6 +## 🟢 [#251 modulair-rag-vds-redeploy] — closed 2026-05-27 — 4-контейнерный стек развёрнут на VDS, acceptance 6/6 NAS помер → 4 контейнера пропали → fresh redeploy на VDS (89.253.255.94). Portainer stack id=15. **Acceptance 6/6 green:** 4 контейнера up без restart-loop; lightrag Uvicorn :9621; `https://modulair-mcp.kzntsv.site`→200; pipeline+tier1 0% CPU; pipeline-лог без postgres/minio ошибок. @@ -253,19 +269,24 @@ NAS помер → 4 контейнера пропали → fresh redeploy на --- -## 🟢 [migrate-elasticsearch-to-books-vds] — closed 2026-05-25 — ES indices с Windows (elasticold.kzntsv.site, 7.10.1) → books VDS (elasticsearch.kzntsv.site, 7.10.0). 3 indices, 928616 docs. Reindex-from-remote через `reindex.remote.whitelist` env, добавленный в Portainer stack 33. Pre-migration snapshot в kreknin repo как rollback. Consumer configs sed'd, 3 containers restarted, smoke green. Source НЕ выключен. +**Created:** 2026-05-27 +## 🟢 [#233 migrate-elasticsearch-to-books-vds] — closed 2026-05-25 — ES indices с Windows (elasticold.kzntsv.site, 7.10.1) → books VDS (elasticsearch.kzntsv.site, 7.10.0). 3 indices, 928616 docs. Reindex-from-remote через `reindex.remote.whitelist` env, добавленный в Portainer stack 33. Pre-migration snapshot в kreknin repo как rollback. Consumer configs sed'd, 3 containers restarted, smoke green. Source НЕ выключен. См. [migrate-elasticsearch-to-books-vds.md](migrate-elasticsearch-to-books-vds.md) § Closure note. -## 🟢 [books-vds-stacks-to-portainer] — closed 2026-05-25 — 6 SSH-compose стеков на books VDS (89.253.255.133) мигрированы в Portainer-managed (endpoint 1, https://portainer.kzntsv.site). Skip traefik + portainer (management plane). Adapter `scripts/books-vds-portainer-migration/migrate.sh`. Wiki: [portainer-stack-management-books-vds.md](../.wiki/concepts/portainer-stack-management-books-vds.md). Backup pipeline bind paths preserved (verified by inspection, full run pending next 06:00 MSK). +**Created:** 2026-05-25 +## 🟢 [#232 books-vds-stacks-to-portainer] — closed 2026-05-25 — 6 SSH-compose стеков на books VDS (89.253.255.133) мигрированы в Portainer-managed (endpoint 1, https://portainer.kzntsv.site). Skip traefik + portainer (management plane). Adapter `scripts/books-vds-portainer-migration/migrate.sh`. Wiki: [portainer-stack-management-books-vds.md](../.wiki/concepts/portainer-stack-management-books-vds.md). Backup pipeline bind paths preserved (verified by inspection, full run pending next 06:00 MSK). -## 🟢 [books-vds-backup-daily-kreknin] — closed 2026-05-25 — daily backup books VDS (89.253.255.133, host4g.ru, CentOS 7) → kreknin в 06:00 MSK. DB dumps (mariadb/mongo×2) + ES snapshot via REST → rsync 4.87GB → ntfy/email (phone+inbox ✓). ES path.repo bootstrap + snapshot repo `kreknin` registered. См. [books-vds-backup-daily-kreknin.md](books-vds-backup-daily-kreknin.md). +**Created:** 2026-05-25 +## 🟢 [#231 books-vds-backup-daily-kreknin] — closed 2026-05-25 — daily backup books VDS (89.253.255.133, host4g.ru, CentOS 7) → kreknin в 06:00 MSK. DB dumps (mariadb/mongo×2) + ES snapshot via REST → rsync 4.87GB → ntfy/email (phone+inbox ✓). ES path.repo bootstrap + snapshot repo `kreknin` registered. См. [books-vds-backup-daily-kreknin.md](books-vds-backup-daily-kreknin.md). -## 🟢 [unify-backup-notifications] — closed 2026-05-25 — единый формат push + email для VDS/RUVDS/windows-host (3 scripts), VDS run.sh импортирован в repo. См. [unify-backup-notifications.md](unify-backup-notifications.md) § Closed. windows-host self-deploy остаётся на user'е (elevated PS). +**Created:** 2026-05-25 +## 🟢 [#235 unify-backup-notifications] — closed 2026-05-25 — единый формат push + email для VDS/RUVDS/windows-host (3 scripts), VDS run.sh импортирован в repo. См. [unify-backup-notifications.md](unify-backup-notifications.md) § Closed. windows-host self-deploy остаётся на user'е (elevated PS). --- -## 🟢 [bookva-tenant-cutover-prep] — closed 2026-05-26 — 7/7 (Step 6 descoped) + extension (bookva-minio + external port-bind) +**Created:** 2026-05-25 +## 🟢 [#250 bookva-tenant-cutover-prep] — closed 2026-05-26 — 7/7 (Step 6 descoped) + extension (bookva-minio + external port-bind) Все ops-шаги VDS-side выполнены: Gitea secrets ✓ (7 шт), **10/11** Portainer bookva stacks ✓ (ozon-mcp deferred — image отсутствует в registry; bookva-minio добавлен post-closure), volumes ✓ (db+mongo cp -a, es+ntfy empty, 4 config volumes populated, bookva-minio-data cp с books bucket), login-gate SQL ✓ (bookva-db users id≥3 → UUID random pwd), DNS ✓ (pre-existed), books-web embed-api switch ✓, **external access** ✓ (bookva-db:33306 + bookva-minio:9001). @@ -290,7 +311,8 @@ NAS помер → 4 контейнера пропали → fresh redeploy на --- -## 🟢 [books-ops-mcp-host-promote] — closed 2026-05-26 — host-promote ops-mcp+docker-proxy в `.admin/host-stacks/books-vds/`, drop bookva-ops-mcp +**Created:** 2026-05-26 +## 🟢 [#249 books-ops-mcp-host-promote] — closed 2026-05-26 — host-promote ops-mcp+docker-proxy в `.admin/host-stacks/books-vds/`, drop bookva-ops-mcp Stack 26 (books-ops-mcp) in-place PUT без container churn (env MARIADB_PASSWORD preserved). Stack 43 (bookva-ops-mcp) deleted. Gitea secrets `PORTAINER_STACK_ID_BOOKVA_OPS_MCP` + `PORTAINER_STACK_ID_OPS_MCP` revoked. ops-mcp убран из tenant=slovo|bookva pipeline (deploy.yml). Management-plane: image updates через Portainer UI manual. @@ -303,7 +325,8 @@ Stack 26 (books-ops-mcp) in-place PUT без container churn (env MARIADB_PASSWO --- -## ⚪ [stateful-split-volume-copy] — поднять `bookva-db` + `slovo-db` на VDS как копии `books-db` через `cp -a` volume +**Created:** 2026-05-26 +## ⚪ [#234 stateful-split-volume-copy] — поднять `bookva-db` + `slovo-db` на VDS как копии `books-db` через `cp -a` volume Ops-таска для Фазы 1 дизайна `tenant-split` из victor/books. Scope сужен 2026-05-25 (user: «просто поднимем 2 БД»): только MariaDB volume copy + up 2 контейнеров. Mongo / MinIO / DELETE / app-стеки — отдельными ops-тасками потом. @@ -312,6 +335,7 @@ Ops-таска для Фазы 1 дизайна `tenant-split` из victor/books **Acceptance:** `docker ps` показывает живые `bookva-db` + `slovo-db`, оба отвечают `SELECT 1`. Текущий `books-db` стек снова в строю после maintenance window. **Status:** ready +**Created:** 2026-05-25 **Where I stopped:** (not started — lean playbook готов в [stateful-split-volume-copy.md](stateful-split-volume-copy.md)) **Next action:** под maintenance window 5-10 мин на VDS — execute 5 команд из task-файла. Backup → stop books-db → cp -a в 2 volume'а → up 2 новых контейнера через Portainer → start books-db. **Blocker:** — @@ -328,11 +352,12 @@ Ops-таска для Фазы 1 дизайна `tenant-split` из victor/books 🟢 Done — moved to ARCHIVE.md once committed 🔵 Blocked — waiting on external input --> -## 🟢 [iis-migration-to-ruvds] — closed 2026-05-25 per user decision — Phase 1 done: RUVDS infra setup + 8.66GB scp + IIS recreate + 25 HTTPS SNI bindings (LE R13 expire 2026-07-22) + 9 hostnames (kupimknigi.spb.ru, emspb±www, pilorama98±www, labtools.pro±www, rimiz±www) live на 80.64.31.36 with correct per-tenant content. 16 hostnames остаются на windows source per user pace (incl. tandemmebel scope-exception). Source IIS:8089 + traefik routes ALIVE для rollback. Decommission + LE renewal + cleanup descoped в Closure note. См. [iis-migration-to-ruvds.md](iis-migration-to-ruvds.md) § Closure note. +## 🟢 [#195 iis-migration-to-ruvds] — closed 2026-05-25 per user decision — Phase 1 done: RUVDS infra setup + 8.66GB scp + IIS recreate + 25 HTTPS SNI bindings (LE R13 expire 2026-07-22) + 9 hostnames (kupimknigi.spb.ru, emspb±www, pilorama98±www, labtools.pro±www, rimiz±www) live на 80.64.31.36 with correct per-tenant content. 16 hostnames остаются на windows source per user pace (incl. tandemmebel scope-exception). Source IIS:8089 + traefik routes ALIVE для rollback. Decommission + LE renewal + cleanup descoped в Closure note. См. [iis-migration-to-ruvds.md](iis-migration-to-ruvds.md) § Closure note. **Status:** closed 2026-05-25 +**Created:** 2026-05-22 **Where I stopped:** 2026-05-24 — `kupimknigi.spb.ru` + `emspb.ru` (+ `www.emspb.ru`) DNS A flipped на `80.64.31.36`, authoritative `ns1.reg.ru` правильный, public resolver caches expire'ятся (8.8.8.8=~6h, 1.1.1.1=~24h max). RUVDS state: snolla site (8.66 GB / 44725 files) transferred + IIS recreated + 25 HTTPS SNI bindings c LE certs (R13, valid до 2026-07-22), 7 prod hostnames live-smoke через VDS (третья сеть) → 200 OK / correct content. Source IIS:8089 + traefik routes ALIVE — rollback ready. **2026-05-25 close-time DNS probe:** ещё 5 пар hostnames swap'нуты user'ом silently — итого 9 на RUVDS, 16 на source. **Findings зафиксированы в** [iis-migration-to-ruvds.md](iis-migration-to-ruvds.md) Decisions log: (1) outbound 445 блокирует home ISP, не RUVDS-FW → SSH/scp = canonical transfer-метод; (2) home network HTTP-middlebox mangles Host header for direct external HTTP — real end-users не пострадают, тестировать через VDS; (3) traefik acme.json → IIS PFX recipe работает (extract + openssl pkcs12 -export + Import-PfxCertificate + AddSslCertificate by thumbprint); (4) IIS 10 HTTP/2 default; (5) `maljarka.tandemmebel.ru` + 3 rimiz hostnames → 502/404 — pre-existing CMS-tenant config gap, не migration defect. @@ -356,9 +381,10 @@ Ops-таска для Фазы 1 дизайна `tenant-split` из victor/books --- -## ⚪ [infra-inventory] — two-tier инвентаризация: public-карта в global wiki + admin-detailed runbook локально +## ⚪ [#196 infra-inventory] — two-tier инвентаризация: public-карта в global wiki + admin-detailed runbook локально **Status:** ready +**Created:** 2026-05-22 **Where I stopped:** (not started — создана 2026-05-22 из инцидента `gitea.` vs `git.` + перепутанный source/dest IP в traefik access-логах; scope расширена в тот же день — single-page → two-tier per user «у все общее представление, у админа полное») **Next action:** см. полный план в `infra-inventory.md`. Шаги: (1) probing-фаза одна для обоих — `nslookup` известных subdomain'ов + `vds-ops`/`synology-ops` `ops_docker_ps`/`inspect` + `knowledge_search` по существующим bootstrap-concept'ам + `rg "kzntsv.site"` по `~/projects/`. (2) Write A — light public в `projects-wiki/concepts/infrastructure-inventory.md` (машины, canonical hostnames, anti-aliases) → `knowledge_ingest`. (3) Write B — heavy admin в `.admin/.wiki/concepts/infrastructure-stack.md` (per-stack: версии, internal IPs, volumes, depends-on, backup paths, DR pointers) → commit + push. (4) Cross-refs A↔B + pointer в `.admin/.wiki/CLAUDE.md` Domain conventions. **Branch:** n/a @@ -367,7 +393,7 @@ Ops-таска для Фазы 1 дизайна `tenant-split` из victor/books --- -## 🔵 [books-vds-bookva-bootstrap] — Ops-таска для Фазы 4 дизайна `tenant-split` из victor/books (см. `mcp__projects-meta__knowledge_get slug=concepts/tenant-split`). Поднять новый VDS для Bookva — billing на юрлицо Bookva (учредители развелись, каждое юрлицо платит инфру напрямую провайдеру). +## 🔵 [#216 books-vds-bookva-bootstrap] — Ops-таска для Фазы 4 дизайна `tenant-split` из victor/books (см. `mcp__projects-meta__knowledge_get slug=concepts/tenant-split`). Поднять новый VDS для Bookva — billing на юрлицо Bookva (учредители развелись, каждое юрлицо платит инфру напрямую провайдеру). **Контекст dev-source:** дизайн в victor/books `.wiki/concepts/tenant-split.md`; brainstorm trace `~/projects/.workshop/.archive/2026-05-24-books-tenant-split.md`. @@ -380,6 +406,7 @@ Ops-таска для Фазы 1 дизайна `tenant-split` из victor/books - SSH-ключи: только инженерные (core-разраб + админ Bookva, если есть). Аналитики Bookva — НЕ имеют SSH к новому VDS. **Status:** blocked +**Created:** 2026-05-24 **Where I stopped:** (not started) **Next action:** **Pre-step:** прочитать `concepts/tenant-split.md` § «Фаза 4» в victor/books (через `mcp__projects-meta__knowledge_get slug=concepts/tenant-split` или git clone victor/books → `.wiki/concepts/tenant-split.md`). @@ -397,7 +424,7 @@ Ops-таска для Фазы 1 дизайна `tenant-split` из victor/books --- -## 🔵 [books-dns-cutover-bookva] — Ops-таска для Фазы 4, шаг 5 дизайна `tenant-split` из victor/books. DNS switch `*.bookva.` → новый VDS IP, с parallel-run старого bookva-стека на shared VDS 7 дней без публичного hostname. +## 🔵 [#215 books-dns-cutover-bookva] — Ops-таска для Фазы 4, шаг 5 дизайна `tenant-split` из victor/books. DNS switch `*.bookva.` → новый VDS IP, с parallel-run старого bookva-стека на shared VDS 7 дней без публичного hostname. **Контекст dev-source:** дизайн в victor/books `.wiki/concepts/tenant-split.md` § «Фаза 4». @@ -409,6 +436,7 @@ Ops-таска для Фазы 1 дизайна `tenant-split` из victor/books - Через 7 дней parallel-run без откатов — decommission старого bookva-стека (см. Фаза 4, шаг 7 в spec). **Status:** blocked +**Created:** 2026-05-24 **Where I stopped:** (not started) **Next action:** **Pre-step:** прочитать `concepts/tenant-split.md` § «Фаза 4, шаги 4–7» в victor/books. @@ -427,7 +455,7 @@ Ops-таска для Фазы 1 дизайна `tenant-split` из victor/books --- -## ⚪ [books-bookva-user-whitelist-gathering] — **Процессная** ops-таска для Фазы 3, шаг 1 дизайна `tenant-split` из victor/books. Получить от учредителя Bookva письменный список пользователей, которые допущены к Bookva-инсталляции. Без этого списка cutover не возможен — иначе утечка доступа: либо лишние юзеры получат доступ к Bookva (через копирование «всех users»), либо легитимные юзеры заблокированы (через пустой whitelist). +## ⚪ [#214 books-bookva-user-whitelist-gathering] — **Процессная** ops-таска для Фазы 3, шаг 1 дизайна `tenant-split` из victor/books. Получить от учредителя Bookva письменный список пользователей, которые допущены к Bookva-инсталляции. Без этого списка cutover не возможен — иначе утечка доступа: либо лишние юзеры получат доступ к Bookva (через копирование «всех users»), либо легитимные юзеры заблокированы (через пустой whitelist). **Direction:** Bookva — новая инсталляция, начинается с whitelist'а (закрытый список). Slovo — остаётся на текущем стеке, получает копию всех текущих users (статус-кво). @@ -442,6 +470,7 @@ Ops-таска для Фазы 1 дизайна `tenant-split` из victor/books - Любые расхождения (запрошенный user не существует в shared / роль другая) — обработаны: создать новый user в Bookva-БД post-cutover ИЛИ скорректировать список с учредителем. **Status:** ready +**Created:** 2026-05-24 **Where I stopped:** (not started) **Next action:** 1. Отправить учредителю Bookva формальный запрос (email/мессенджер): «Для tenant-split нужен список пользователей, которые останутся в Bookva после разделения. Формат: ФИО + роль + email/login. Список будет применён на cutover'е как whitelist». 2. Дождаться ответа (это может занять дни/недели — нормально). @@ -456,7 +485,7 @@ Ops-таска для Фазы 1 дизайна `tenant-split` из victor/books **Branch:** n/a -## 🔵 [books-stateful-split-execution] — Ops-таска для **Фазы 1** дизайна `tenant-split` из victor/books. Выполнить playbook на shared VDS в maintenance window — docker volume copy для MariaDB + Mongo + MinIO bucket rename + DELETE cleanup. Identical bit-for-bit копии через `cp -a` alpine helper. Никаких mysqldump/mongodump/mc cp. +## 🔵 [#230 books-stateful-split-execution] — Ops-таска для **Фазы 1** дизайна `tenant-split` из victor/books. Выполнить playbook на shared VDS в maintenance window — docker volume copy для MariaDB + Mongo + MinIO bucket rename + DELETE cleanup. Identical bit-for-bit копии через `cp -a` alpine helper. Никаких mysqldump/mongodump/mc cp. **Playbook (dev-source, читать целиком до начала):** `victor/books/.tasks/stateful-split-volume-copy.md`. @@ -475,6 +504,7 @@ Ops-таска для Фазы 1 дизайна `tenant-split` из victor/books - Через 7-14 дней parallel-run без откатов → cleanup старых `books-*` контейнеров и volumes (отдельный заход). **Status:** blocked +**Created:** 2026-05-25 **Where I stopped:** (not started) **Next action:** **Pre-step:** прочитать `victor/books/.tasks/stateful-split-volume-copy.md` целиком. Убедиться что 7 open questions resolved (закрыто dev-prep таской `victor/books stateful-split-volume-copy`): FK CASCADE, units/receipts strategy, MinIO key structure, users whitelist, task_runs handling, chrome/traefik shared. @@ -492,7 +522,7 @@ Ops-таска для Фазы 1 дизайна `tenant-split` из victor/books --- -## 🟢 [vehicles-loader-image-distribution] — closed 2026-05-29 — канал поставки решён: build здесь → push в **собственный registry клиента** `docker.stostayer.ru` → host pull + re-tag `:latest`. **Без Portainer** (oneshot не stack + Portainer cron не умеет; systemd-timer + `docker run --rm` сохраняет `OnFailure=`). Build здесь = депы (вкл. приватный verdaccio) запекаются в образ, клиенту verdaccio/yarn/node не нужны. Финализированы `deploy/README.md` § Image distribution + дизайн-док (вопрос #4 закрыт). **Commit:** stostayer.new `e55cfba`. **Prod-деплой ВЫПОЛНЕН 2026-05-29** (stostayer.new `ddda63f`): образ `docker.stostayer.ru/vehicles-loader:0.3.0` собран здесь+запушен, env `/etc/stostayer/vehicles-loader.env` (600 root), systemd-timer enabled (next 2026-05-30 06:20 MSK). **Гочи по ходу (обе в `stostayer.new/.wiki/concepts/client-infra-access.md`):** (1) MariaDB — под `--network host` наследуется хостовый `/etc/hosts` (`www→127.0.0.1 И ::1`), Node резолвит `::1` первым, mariadb слушает только IPv4 → timeout. Фикс: `STOSTAYER_DB_HOST=127.0.0.1`. (2) Контейнерный DNS сломан (Docker переписывает resolv.conf на публичные, исходящий :53 зафайрволен; `--add-host` под host-net игнорится) → email-отчёт на `mail.stostayer.ru` не резолвился. Фикс: `-v /etc/stostayer/container-hosts:/etc/hosts:ro` (свой hosts с пином mail-IP). +## 🟢 [#266 vehicles-loader-image-distribution] — closed 2026-05-29 — канал поставки решён: build здесь → push в **собственный registry клиента** `docker.stostayer.ru` → host pull + re-tag `:latest`. **Без Portainer** (oneshot не stack + Portainer cron не умеет; systemd-timer + `docker run --rm` сохраняет `OnFailure=`). Build здесь = депы (вкл. приватный verdaccio) запекаются в образ, клиенту verdaccio/yarn/node не нужны. Финализированы `deploy/README.md` § Image distribution + дизайн-док (вопрос #4 закрыт). **Commit:** stostayer.new `e55cfba`. **Prod-деплой ВЫПОЛНЕН 2026-05-29** (stostayer.new `ddda63f`): образ `docker.stostayer.ru/vehicles-loader:0.3.0` собран здесь+запушен, env `/etc/stostayer/vehicles-loader.env` (600 root), systemd-timer enabled (next 2026-05-30 06:20 MSK). **Гочи по ходу (обе в `stostayer.new/.wiki/concepts/client-infra-access.md`):** (1) MariaDB — под `--network host` наследуется хостовый `/etc/hosts` (`www→127.0.0.1 И ::1`), Node резолвит `::1` первым, mariadb слушает только IPv4 → timeout. Фикс: `STOSTAYER_DB_HOST=127.0.0.1`. (2) Контейнерный DNS сломан (Docker переписывает resolv.conf на публичные, исходящий :53 зафайрволен; `--add-host` под host-net игнорится) → email-отчёт на `mail.stostayer.ru` не резолвился. Фикс: `-v /etc/stostayer/container-hosts:/etc/hosts:ro` (свой hosts с пином mail-IP). **Первый боевой прогон 2026-05-30 06:20:** sync УСПЕШЕН (`importRun id=3 ok`, ~84мин, units.json изменился относительно baseline), но упал на email (DNS, см. гочу #2) → systemd `failed` + ложный OnFailure. **Данные залились корректно.** Email-фикс применён и проверен `nodemailer.verify()` → `SMTP_VERIFY_OK` (DNS+TCP+STARTTLS+AUTH). Юнит redeployed, failed сброшен, next run Sun 06:21. Доки в stostayer.new `8e5997c`. **Заведена ⚪ `vehicles-loader-progress-logging`** (stostayer.new) — лог тонкий, ~2ч sync молчал. **Остаточное:** живой email-SEND в составе sync проверится на следующем changed-прогоне (verify доказал транспорт); OnFailure-юнит шлёт через хостовый `mail` — не проверен. @@ -509,6 +539,7 @@ vehicles-loader (stostayer) деплоится в Docker у клиента (lite > **UPDATE 2026-05-29 (из stostayer.new session):** lite docker-deploy CORE зашипан (10/10 tasks на master). **Dockerfile CMD уже финализирован** (`ENTRYPOINT ["node","index.js"]` + `CMD ["sync"]`, source через env `STOSTAYER_SOURCE_ZIP` дефолт `/var/from_1c/export_from_1c.zip`) — эта часть блокера СНЯТА. Остаётся ТОЛЬКО: (1) решение по каналу поставки образа; (2) distribution-секция `deploy/README.md` (помечена PENDING на эту таску). **A build-proven:** образ собран локально (`docker build` ок) и запускается (`docker run --rm vehicles-loader:dev --help` ок). С учётом verdaccio-зависимости B (выше) — **A ещё предпочтительнее**: при A клиенту verdaccio не нужен вообще (cache наполняется нашим authed `yarn install` при сборке). **Status:** closed 2026-05-29 +**Created:** 2026-05-29 **Resolution:** канал = собственный registry клиента `docker.stostayer.ru` (не наш VDS-registry, не build-from-git). Развилка пересмотрена при открытии `pass stostayer/client` — у клиента своя инфра (Portainer + registry). Build остаётся у нас (verdaccio-депы запекаются). Без Portainer для запуска (oneshot). Доки финализированы в stostayer.new `e55cfba`. **Branch:** n/a @@ -517,9 +548,10 @@ vehicles-loader (stostayer) деплоится в Docker у клиента (lite --- -## 🟡 [agents-task-runner-vds-deploy] — Deploy agents-task-runner (the poller) on WORKER machine(s) — per-machine federation (agent-orchestration-without-user Q9). Each worker runs its own runner, claims from the SHARED Gitea board, runs the agent LOCALLY, commits/pushes. VDS hosts the Gitea board ONLY (already up) — it is NOT a worker, nothing deploys on VDS. [Slug legacy-named '-vds-deploy'; scope corrected 2026-06-08, VDS-as-worker DROPPED.] Runtime code deploy-ready (poller registered in tasks.json, DRY_RUN + requirements-filter + confirm-gate built; compose+Dockerfile). Credentialed per-machine setup (ops). +## 🟡 [#314 agents-task-runner-vds-deploy] — Deploy agents-task-runner (the poller) on WORKER machine(s) — per-machine federation (agent-orchestration-without-user Q9). Each worker runs its own runner, claims from the SHARED Gitea board, runs the agent LOCALLY, commits/pushes. VDS hosts the Gitea board ONLY (already up) — it is NOT a worker, nothing deploys on VDS. [Slug legacy-named '-vds-deploy'; scope corrected 2026-06-08, VDS-as-worker DROPPED.] Runtime code deploy-ready (poller registered in tasks.json, DRY_RUN + requirements-filter + confirm-gate built; compose+Dockerfile). Credentialed per-machine setup (ops). **Status:** paused +**Created:** 2026-06-08 **Where I stopped:** 2026-06-08: DRY_RUN провалидирован; `.admin` claim-guard (`policy.toml` default_weight=needs-human) запушен+верифицирован (все `.admin`-таски → needs-human skip). Always-on bring-up начат (docker mongo+reconciler) затем **снесён по запрету user'а «не запускай поллеров без разрешения»** — host task-runner НЕ стартовал, ни одного claim/spawn. **GATE: запуск поллеров ждёт явного гранта user'а.** Попутно поймана+закрыта stale-claimable `cms-maljarka-https-mode-bug-fix` на MoreThenCms board (баг починен сегодня, таска висела открытой — без паузы always-on заспавнил бы агента на уже-решённое). Конфиг (.env/secrets.env/policy guard) готов. См. [agents-task-runner-vds-deploy.md](agents-task-runner-vds-deploy.md). **Next action:** (0) BUILD PREREQ (from consult-execution-policy-governance review-outcome, OpeItcLoc03/common): repo tracks TS source only — `dist/` is gitignored. After pulling OpeItcLoc03/common on each worker, run `npm run build` (tsc) in BOTH lib/projects-meta-mcp AND lib/agents-task-runner, then (re)start the runner; otherwise the execution-policy governance lever (consult_policy + needs-human claim-gate, runner v0.16.x) is INERT — the old compiled JS keeps running. (1) Pick first worker machine (workstation / factory Linux). (2) Install + run agents-task-runner there, pointed at the shared Gitea board. (3) Secrets PER MACHINE: ANTHROPIC_API_KEY (headless) or `claude login` + Gitea token + projects-meta config; 600 root, not in git. (4) Capability filter: claims only what that machine can do (requirements/runtime_allowed); cheap model (sonnet/haiku) for impl polling. (5) HARD: .admin excluded from autonomous claim until governance L2/L3 lands (see blocker). (6) DRY_RUN on prod board first → then one live cycle on a real non-.admin task → spawn→commit→push, verified. (7) anti-self-review: distinct machine-id per worker. **Branch:** n/a @@ -527,7 +559,7 @@ vehicles-loader (stostayer) деплоится в Docker у клиента (lite --- -## 🟢 [migrate-gallery-originals-to-s3] — closed 2026-06-12 (scope: pilorama98) — путь A: создан bucket `galleries`, залиты 301 файл (77 MiB) legacy-оригиналов pilorama98 с RUVDS IIS `App_Data\galleries\37e6…` → `s3://galleries/37e67fc44b9e4c06a522a26a73bac9b0/.jpg`. Smoke с books-vds: реальный объект через imgproxy → 200 image/webp 32102 B, fake guid → 404. Код snolla НЕ менялся (storageClient=имя бакета — рабочая конвенция). Inbox victor/snolla отправлен. Развилка A/B закрыта фактами MinIO: galleries в legacy = Local storage class (диск), никогда не в S3; A выбран user'ом. +## 🟢 [#357 migrate-gallery-originals-to-s3] — closed 2026-06-12 (scope: pilorama98) — путь A: создан bucket `galleries`, залиты 301 файл (77 MiB) legacy-оригиналов pilorama98 с RUVDS IIS `App_Data\galleries\37e6…` → `s3://galleries/37e67fc44b9e4c06a522a26a73bac9b0/.jpg`. Smoke с books-vds: реальный объект через imgproxy → 200 image/webp 32102 B, fake guid → 404. Код snolla НЕ менялся (storageClient=имя бакета — рабочая конвенция). Inbox victor/snolla отправлен. Развилка A/B закрыта фактами MinIO: galleries в legacy = Local storage class (диск), никогда не в S3; A выбран user'ом. Галерейные (blog/gallery) картинки не отдаются ни на dev (imgproxy 404 "Source image is unreachable"), ни на prod (legacy-ресайз 404). Диагностика проведена в victor/snolla против живого pilorama98 + кода MoreThenCms. Нужен доступ к БОЕВОМУ серверу (локальный диск IIS + MinIO/S3), которого нет у snolla-агента — поэтому задача на admin. @@ -580,6 +612,7 @@ vehicles-loader (stostayer) деплоится в Docker у клиента (lite **notify:** victor/snolla **Status:** done (closed 2026-06-12, scope pilorama98) +**Created:** 2026-06-12 **Where I stopped:** мигрировано. MinIO (books-vds стек 30, за `imgproxy.kzntsv.site` с 08.06): bucket `galleries` создан, prefix `37e67fc44b9e4c06a522a26a73bac9b0/` залит (301×.jpg, 77.0 MiB, rclone RUVDS→minio.kzntsv.site, count+size == source). Smoke OK с books-vds (200/404). **Next action:** — (хвосты в inbox victor/snolla: остальные siteId-папки галерей не мигрированы по скоупу; legacy prod-ресайз остаётся 404 by design). **Branch:** master @@ -596,7 +629,7 @@ vehicles-loader (stostayer) деплоится в Docker у клиента (lite --- -## 🟢 [migrate-assets-originals-to-s3] — closed 2026-06-13 — assets-оригиналы залиты в S3, imgproxy 200 verified (scope расширен: весь assets-дерево, не только pilorama98). Создан bucket `assets` (отсутствовал) на MinIO books-vds; rclone RUVDS IIS `App_Data\assets` → `s3://assets//` verbatim. **Verify:** dest == source точь-в-точь — 4750 объектов / 225 935 853 B (215.5 MiB), 145 owner-папок. **Smoke (через books-vds, мимо LAN-DNS):** brevno.jpg (предподписанный URL из задачи) → 200 image/webp 64556 B (был 404); валидно-подписанный несуществующий объект → 404 (негативный контроль). Код snolla НЕ менялся (path A). Inbox victor/snolla отправлен. ⚠️ Остаётся snolla-side: `content-api/routes/assets.js` пустой stub — картинки не поедут на сайт пока роут не реализован (код-таска snolla, не блокер миграции). +## 🟢 [#362 migrate-assets-originals-to-s3] — closed 2026-06-13 — assets-оригиналы залиты в S3, imgproxy 200 verified (scope расширен: весь assets-дерево, не только pilorama98). Создан bucket `assets` (отсутствовал) на MinIO books-vds; rclone RUVDS IIS `App_Data\assets` → `s3://assets//` verbatim. **Verify:** dest == source точь-в-точь — 4750 объектов / 225 935 853 B (215.5 MiB), 145 owner-папок. **Smoke (через books-vds, мимо LAN-DNS):** brevno.jpg (предподписанный URL из задачи) → 200 image/webp 64556 B (был 404); валидно-подписанный несуществующий объект → 404 (негативный контроль). Код snolla НЕ менялся (path A). Inbox victor/snolla отправлен. ⚠️ Остаётся snolla-side: `content-api/routes/assets.js` пустой stub — картинки не поедут на сайт пока роут не реализован (код-таска snolla, не блокер миграции). ### (исходное описание ниже сохранено до prune) Залить Local-storage assets-оригиналы сайта pilorama98 в S3 — латентная дыра того же класса, что галереи (закрыта миграцией `migrate-gallery-originals-to-s3`). snolla отдаёт inline-картинки контента (``) через imgproxy с источником `s3://assets//`, но объектов в S3 нет — imgproxy 404 "Source image is unreachable". БД-записи есть, файлы только на legacy-IIS диске. Тип `StorageClient="assets"` — это `MoreThenCms.FileStorage.Local.*`, в S3 никогда не мигрировали. @@ -637,6 +670,7 @@ vehicles-loader (stostayer) деплоится в Docker у клиента (lite **notify:** victor/snolla **Status:** done (closed 2026-06-13) +**Created:** 2026-06-13 **Where I stopped:** мигрировано всё дерево assets. rclone size min:assets == источник (4750 / 225935853). Smoke 200/404 OK. **Next action:** — (хвост на стороне snolla: реализовать `content-api/routes/assets.js`; остальные Local-классы images/scripts/stylesheets/watermarks — отдельный аудит snolla, если поднимутся через imgproxy). **Branch:** master @@ -653,7 +687,7 @@ vehicles-loader (stostayer) деплоится в Docker у клиента (lite --- -## 🟢 [deploy-pilonuxt-vds] — closed 2026-06-15 — CUTOVER LIVE на www.pilorama98.ru (tag 125a3e2, Portainer stack 16). **Цель:** Собрать prod-образ нового Nuxt-фронта ПИЛОРАМА98 (`victor/pilonuxt`, master) и задеплоить на VDS как Portainer-стек за Traefik. Готово в репо: `Dockerfile` + `.dockerignore` в корне. Полный овнершип сборки и деплоя — на тебе (build → push в VDS-registry → Portainer stack), как books/stostayer. +## 🟢 [#364 deploy-pilonuxt-vds] — closed 2026-06-15 — CUTOVER LIVE на www.pilorama98.ru (tag 125a3e2, Portainer stack 16). **Цель:** Собрать prod-образ нового Nuxt-фронта ПИЛОРАМА98 (`victor/pilonuxt`, master) и задеплоить на VDS как Portainer-стек за Traefik. Готово в репо: `Dockerfile` + `.dockerignore` в корне. Полный овнершип сборки и деплоя — на тебе (build → push в VDS-registry → Portainer stack), как books/stostayer. **Сборка:** - `docker build --secret id=verdaccio_token,env=VERDACCIO_TOKEN -t /pilonuxt: .` @@ -695,6 +729,7 @@ vehicles-loader (stostayer) деплоится в Docker у клиента (lite **notify:** victor/pilonuxt **Status:** done (closed 2026-06-15) +**Created:** 2026-06-14 **Closure / coverage check:** vitya флипнул DNS pilorama98.ru+www → 89.253.255.94; я перенастроил traefik-роутер на боевые хосты (`Host(www.pilorama98.ru)||Host(pilorama98.ru)||Host(pilonuxt.vds.kzntsv.site)`, cert LE на apex+www), снял smoke GTM-override (прод-аналитика GTM-MNNXFJ6 ВКЛ), выкатил `125a3e2` (фикс «Продукция»→/catalog). Acceptance: (1) образ в registry ✓ 125a3e2; (2) Portainer stack 16 за traefik healthy ✓; (3) smoke ✓ — финальный краул с VDS по боевому www.pilorama98.ru: все страницы page 200/viewModel 200 (главная/каталог/категории/товар/услуги/контакты/оплата/доставка), картинки imgproxy 200 webp, x-powered-by Nuxt (не IIS), cert valid, футер /products битый линк убран (0 вхождений, «Продукция»→/catalog); форма contacts — закрыта (vitya «формы заебись» + pilonuxt end-to-end verified в dev, smtp egress жив); (4) доклад pilonuxt ✓. Урок применён: краулил весь меню+viewModel, не 2 роута ([[smoke-crawl-real-pages-not-two-routes]]). **Follow-ups (не блокеры, отдельно):** - **Старый IIS RUVDS 80.64.31.36 — РЕШЕНИЕ vitya 2026-06-15: держим для отката, НЕ выводим.** ⚠️ Этот IIS = snolla catch-all на 25 хостнеймов (emspb/kupimknigi/labtools/…), целиком гасить нельзя — при будущем cleanup снимается только биндинг pilorama98, не весь сайт. **Rollback pilorama98:** вернуть DNS reg.ru `pilorama98.ru`+`www` → `80.64.31.36` (старый сайт + его LE-cert живы), TTL низкий. Decommission-биндинга — отдельно после soak. @@ -718,7 +753,7 @@ vehicles-loader (stostayer) деплоится в Docker у клиента (lite --- -## 🟢 [redeploy-pilonuxt-gsc-offers] — closed 2026-06-16 — pilonuxt `37412d0` в проде, offers в микроразметке Product live. Build на workstation (verdaccio build-secret) → push registry.kzntsv.site/pilonuxt:37412d0 (398MB) → перекат Portainer stack 16 (PUT API). Acceptance verified (curl --resolve к VDS): на `/shop/products/doska-obreznaya-25-100-6000` есть `itemprop="offers"` itemtype Offer с price=277.5/priceCurrency=RUB/availability=InStock/url-канон; X-Powered-By Nuxt; регрессии нет (меню /catalog,/services,/contacts,/payment = 200, viewModel жив). ⚠️ По ходу: в Portainer не был зарегистрирован НИ ОДИН registry → первый pull упал `no basic auth credentials`; зарегистрировал `registry.kzntsv.site` (Custom, Id 1, registry-креды из pass) → перекат прошёл, приватный registry теперь авторизован в Portainer постоянно. Inbox pilonuxt отправлен (GSC → «Проверить устранение»). Передеплой pilonuxt с SEO-фиксом микроразметки Product (commit 37412d0). Google Search Console отсеивал страницы товаров из выдачи: критическая ошибка «задайте offers / review / aggregateRating» — у Product не было блока offers, а aggregateRating рендерился только при наличии рейтинга. Фикс добавил schema.org/Offer (price/priceCurrency/availability/url) в app/pages/shop/products/[slug].vue, проверено в SSR. Только код фронта — env/инфра/секреты не менялись, build/runtime egress те же, что в прошлый деплой (verdaccio + mssql при сборке; mssql + smtp + self-origin в рантайме). Acceptance: новый образ в проде, на странице товара в HTML присутствует itemprop="offers" с ценой и наличием. +## 🟢 [#394 redeploy-pilonuxt-gsc-offers] — closed 2026-06-16 — pilonuxt `37412d0` в проде, offers в микроразметке Product live. Build на workstation (verdaccio build-secret) → push registry.kzntsv.site/pilonuxt:37412d0 (398MB) → перекат Portainer stack 16 (PUT API). Acceptance verified (curl --resolve к VDS): на `/shop/products/doska-obreznaya-25-100-6000` есть `itemprop="offers"` itemtype Offer с price=277.5/priceCurrency=RUB/availability=InStock/url-канон; X-Powered-By Nuxt; регрессии нет (меню /catalog,/services,/contacts,/payment = 200, viewModel жив). ⚠️ По ходу: в Portainer не был зарегистрирован НИ ОДИН registry → первый pull упал `no basic auth credentials`; зарегистрировал `registry.kzntsv.site` (Custom, Id 1, registry-креды из pass) → перекат прошёл, приватный registry теперь авторизован в Portainer постоянно. Inbox pilonuxt отправлен (GSC → «Проверить устранение»). Передеплой pilonuxt с SEO-фиксом микроразметки Product (commit 37412d0). Google Search Console отсеивал страницы товаров из выдачи: критическая ошибка «задайте offers / review / aggregateRating» — у Product не было блока offers, а aggregateRating рендерился только при наличии рейтинга. Фикс добавил schema.org/Offer (price/priceCurrency/availability/url) в app/pages/shop/products/[slug].vue, проверено в SSR. Только код фронта — env/инфра/секреты не менялись, build/runtime egress те же, что в прошлый деплой (verdaccio + mssql при сборке; mssql + smtp + self-origin в рантайме). Acceptance: новый образ в проде, на странице товара в HTML присутствует itemprop="offers" с ценой и наличием. ## Обязательные скилы — вызвать до начала работы - invoke `using-tasks` — управление статусом задачи @@ -730,6 +765,7 @@ vehicles-loader (stostayer) деплоится в Docker у клиента (lite **notify:** victor/pilonuxt **Status:** 🟢 done (closed 2026-06-16 vitya@.admin-exec) +**Created:** 2026-06-16 **Closure / coverage check:** build workstation @ 37412d0 (verdaccio JWT build-secret) → push registry.kzntsv.site/pilonuxt:37412d0 (398MB) → Portainer stack 16 PUT (PullImage:true) → контейнер running 37412d0. Smoke curl --resolve www.pilorama98.ru:443:89.253.255.94: offers-блок present (price/priceCurrency/availability/url), X-Powered-By Nuxt, меню+viewModel 200 (без content-api-500). Урок [[smoke-crawl-real-pages-not-two-routes]] применён — краулил реальные nav-href, не гадал слаги (/delivery 404 но не в меню → не баг). **Follow-up (не блокер):** registry.kzntsv.site зарегистрирован в Portainer (Custom Id 1) — закрывает «no basic auth» на будущих перекатах приватных образов; до этого pull держался на host docker login, который слетел. **Next action:** — @@ -740,7 +776,7 @@ vehicles-loader (stostayer) деплоится в Docker у клиента (lite --- -## 🟢 [redeploy-pilonuxt-gsc-structured-data] — closed 2026-06-17 — `19a4a84` в проде, structured-data (description/brand/hasMerchantReturnPolicy) live. Передеплой www.pilorama98.ru с фиксом микроразметки Product под minor-замечания Google Search Console (structured data «Данные о товарах продавца»). +## 🟢 [#422 redeploy-pilonuxt-gsc-structured-data] — closed 2026-06-17 — `19a4a84` в проде, structured-data (description/brand/hasMerchantReturnPolicy) live. Передеплой www.pilorama98.ru с фиксом микроразметки Product под minor-замечания Google Search Console (structured data «Данные о товарах продавца»). Коммит уже на origin/master: `19a4a84` (fix(seo): обогатил микроразметку Product offers). Изменён только `app/pages/shop/products/[slug].vue` — добавлены itemprop description (always-on), brand, hasMerchantReturnPolicy внутри offers. Тот же пайплайн, что вчерашний `redeploy-pilonuxt-gsc-offers` (37412d0). @@ -760,6 +796,7 @@ vehicles-loader (stostayer) деплоится в Docker у клиента (lite **notify:** victor/pilonuxt (ответ о закрытии/затыке — в inbox pilonuxt, там слушает монитор) **Status:** 🟢 done (closed 2026-06-17 vitya@.admin-exec) +**Created:** 2026-06-17 **Closure / coverage check:** build на workstation @ 19a4a84 (verdaccio JWT build-secret, EXIT=0, src/generated предгенерён → mssql на билде не дёргался) → push registry.kzntsv.site/pilonuxt:19a4a84 (digest sha256:9642…fa5) → Portainer stack 16 PUT(pullImage:true) → контейнер running 19a4a84 (vds-ops ps). Smoke curl --resolve www.pilorama98.ru:443:89.253.255.94 на /shop/products/doska-obreznaya-25-100-6000: **(1)** `itemprop="description"` непустой («Характеристики Сорт 1 Материал Сосна…») ✓; **(2)** `itemprop="brand"` content="Пилорама 98" ✓; **(3)** внутри `itemprop="offers"` (schema.org/Offer): `hasMerchantReturnPolicy`→`returnPolicyCategory=https://schema.org/MerchantReturnNotPermitted` ✓; **(4)** прежние offers-поля целы: price=277.5/priceCurrency=RUB/availability=InStock/url=канон ✓; X-Powered-By Nuxt. Регрессий нет: / /catalog /services /contacts /payment = 200 (/delivery 404 unlinked, как и вчера). Урок [[smoke-crawl-real-pages-not-two-routes]] применён. **Gotcha (новый, заингещён):** Portainer stack update через PS 5.1 Invoke-RestMethod коррапит кириллические комментарии compose — ответ /file декодится как ISO-8859-1 (mojibake) → обратный PUT даёт `yaml: could not find expected ':'`. Фикс: GET байтами (-OutFile) + read -Encoding UTF8, PUT тело UTF-8-байтами. Записано в concepts/portainer-stack-management-vds.md. **Next action:** — @@ -770,7 +807,7 @@ vehicles-loader (stostayer) деплоится в Docker у клиента (lite --- -## 🔵 [stostayer-web-complaint-form-deploy] — Раскатать на прод (www.stostayer.ru) фикс формы жалоб. Коммит **120bc07** уже в `origin/master` репо `victor/stostayer.new`. +## 🔵 [#423 stostayer-web-complaint-form-deploy] — Раскатать на прод (www.stostayer.ru) фикс формы жалоб. Коммит **120bc07** уже в `origin/master` репо `victor/stostayer.new`. ## Контекст @@ -798,6 +835,7 @@ vehicles-loader (stostayer) деплоится в Docker у клиента (lite **notify:** victor/stostayer.new **Status:** blocked +**Created:** 2026-06-17 **Where I stopped:** Cherry-pick `120bc07` на `d02f740` (pre-ESM, рецепт stostayer.new) → `7d88a02`. **Собралось** offline (nuxt build прошёл) → push 0.3.19 в `docker.stostayer.ru` (с 1 попытки на новом VPN; прежние retry-штормы засветили egress → хостинг забанил наш VPN-IP, vitya сменил VPN) → передеплой Portainer-стека `stostayer-web` (Id 16, EndpointId 3) на 0.3.19 через Portainer API по IP контейнера `172.18.0.2:9000` (мимо Angie BA). **Но контейнер краш-лупит в рантайме:** `ERR_REQUIRE_ESM` — `packages/web/server/index.js:6` `require('@snollajs/snolla')`, а он ESM (lockfile d02f740 тянет ESM-версию). d02f740 собирается, но НЕ runtime-чистая. **Откатил стек на 0.3.18** (тот же PUT, тег назад) — контейнер Up, штатный Nuxt2, сайт восстановлен. vitya: остаёмся на 0.3.18, апдейт отставлен. **Next action:** ПАРКНУТО. Деплой невозможен до web4-cutover. Вердикт (stostayer.new 14-55, подтверждён): легаси `packages/web` require-ит **5** ESM-ставших пакетов (`@snollajs/snolla` 0.7.4, `@snollajs/content-api` 0.8.0, `@stostayer/api`, `@stostayer/data` ×2 места) → чейз CJS-базы бесполезен (надо откатиться к ~0.3.18 и потерять всё). `0.3.18` = последний собираемый артефакт, заморожен. Фикс формы (`120bc07`) едет вместе с переездом формы в web4. Ранбук: `.wiki/concepts/stostayer-web-deploy-runbook.md`. Дохлый `0.3.19` в `docker.stostayer.ru` — **оставлен по решению vitya (2026-06-17)**, не деплоить. **Blocker:** web4-cutover (планирование на стороне vitya, отдельным решением) — НЕ на stostayer.new и НЕ канал доставки (он рабочий). Прод остаётся на 0.3.18, сайт восстановлен. @@ -807,7 +845,7 @@ vehicles-loader (stostayer) деплоится в Docker у клиента (lite --- -## 🟢 [books-scheduler-registry-auth-cred] — ## Проблема (простыми словами) +## 🟢 [#431 books-scheduler-registry-auth-cred] — ## Проблема (простыми словами) Планировщик books (`books-job-scheduler` на books-vds) умеет запускать «docker-таски» — для каждой такой задачи он скачивает (pull) готовый образ-инструмент из реестра `registry.kzntsv.site` и запускает его контейнером. Один из таких инструментов — `books-tool-create-picking-list-pdf` (рендерит PDF листа подбора FBS). @@ -861,6 +899,7 @@ Approach ратифицирован человеком (вариант A): ко **notify:** victor/books **Status:** done +**Created:** 2026-06-18 **Where I stopped:** cred placed in config-volume; activation = books code deploy (no premature restart) **Next action:** (none — kept until merged) **Branch:** n/a @@ -878,7 +917,7 @@ Approach ратифицирован человеком (вариант A): ко --- -## 🟢 [books-task-runner-registry-auth-cred] — Положить кред books-ci в **task-runner** config-volume, чтобы заработал переписанный `registryGc` (чистка Docker registry через Registry v2 DELETE). +## 🟢 [#432 books-task-runner-registry-auth-cred] — Положить кред books-ci в **task-runner** config-volume, чтобы заработал переписанный `registryGc` (чистка Docker registry через Registry v2 DELETE). Контекст: registryGc раньше бил в Gitea packages API и никогда не отрабатывал (образы books-* живут в standalone registry:2 на registry.kzntsv.site за htpasswd). Переписан на Registry v2 REST с Basic-auth кредом books-ci (commit books@a3734d5). Кред books-ci уже есть в **job-scheduler** config-volume (для docker pull) — нужен тот же и в **task-runner** (registryGc бежит там, http-runner, отдельный volume). @@ -910,6 +949,7 @@ Approach ратифицирован человеком (вариант A): ко **notify:** OpeItcLoc03/books **Status:** 🟢 done — кред положен, acceptance (auth/dryRun) verified +**Created:** 2026-06-18 **Where I stopped:** Закрыта. Секция `registry` (baseUrl+username+password books-ci, полная — bind-mount затеняет config образа) в `/opt/books/task-runner/config/default.json` (books-vds, 644 root сохранён, бэкап `.bak-pre-registry-2026-06-18`, только ключ registry added, EACCES-инцидент 18.06 не повторён). После deploy нового кода (образ `master-bf2a8c5`, код a3734d5 внутри) прогнан `registryGc {dryRun:true,debug:true}` через scheduler-dispatch-путь (`POST /tasks/registryGc` + `x-agenda-job-id`, idентично http.js handler'у). Результат: **success, 401 нет нигде** (hit `/v2/_catalog`=14 repos, `/tags/list`, resolveManifest), repos books-* видны (7 processed), отчёт `task-reports/j/...md`. **Acceptance coverage:** (1) `registry.password` в config-volume ✓ (len 48, `config.get` резолвится). (2) dryRun без 401 + repos books-* + «будет удалено N» ✓ (N=0 — см. finding ниже; критерий по форме выполнен). 405/`REGISTRY_STORAGE_DELETE_ENABLED` — dryRun DELETE не слал, не проверено этим прогоном (по вики registry-kzntsv-auth-model уже =true). **Finding (отдано books, НЕ блокер кред-таски):** GC аутентифицируется и бежит, но **ничего не удаляет** — по каждой репе `keep=tags−1, drop=0` независимо от keepLastN=3. Root cause в books-коде `lib/registryV2.js`: `getCreated` возвращает null для всех манифестов (вероятно `configDigest` не извлекается — OCI image-index/manifest-list от buildx, или blob 404), а `planDeletions` ставит `protected=true` любой группе с `created==null` → drop пуст всегда. keepLastN не применяется. Owner — books-сессия. @@ -920,7 +960,7 @@ Approach ратифицирован человеком (вариант A): ко --- -## 🟢 [archive-pilorama-source-repos] — closed 2026-06-18 — 3 исходных репо заморожены. `archived:true` (Gitea API, http 200) на `victor/pilonuxt`/`OpeItcLoc03/pilorama98.ru-legacy`/`OpeItcLoc03/snolla-products-loader`; в каждом README-баннер + repo-description → `victor/pilorama98.ru/apps/{web,legacy,loader}`; локальные клоны удалены (verified clean + no unpushed ДО rm); projects-meta `archived_skipped` 1→4 (поллер скипает); shared-wiki concept `pilorama98-monorepo-consolidation` создан. **Sequencing-замечание:** архив выполнен 17:43 — раньше, чем пришло предупреждение пира (17:46) о порядке deploy→archive; без последствий (deploy нацелен на монореп, не на pilonuxt; archived-репо остаётся читаемым). См. отчёт в inbox pilorama98.ru. +## 🟢 [#430 archive-pilorama-source-repos] — closed 2026-06-18 — 3 исходных репо заморожены. `archived:true` (Gitea API, http 200) на `victor/pilonuxt`/`OpeItcLoc03/pilorama98.ru-legacy`/`OpeItcLoc03/snolla-products-loader`; в каждом README-баннер + repo-description → `victor/pilorama98.ru/apps/{web,legacy,loader}`; локальные клоны удалены (verified clean + no unpushed ДО rm); projects-meta `archived_skipped` 1→4 (поллер скипает); shared-wiki concept `pilorama98-monorepo-consolidation` создан. **Sequencing-замечание:** архив выполнен 17:43 — раньше, чем пришло предупреждение пира (17:46) о порядке deploy→archive; без последствий (deploy нацелен на монореп, не на pilonuxt; archived-репо остаётся читаемым). См. отчёт в inbox pilorama98.ru. Поглощены → подпапки монорепы: - `victor/pilonuxt` → `apps/web` (Nuxt-сайт) @@ -946,6 +986,7 @@ Approach ратифицирован человеком (вариант A): ко **notify:** pilorama98.ru **Status:** done +**Created:** 2026-06-18 **Where I stopped:** closed — 5/5 acceptance met (archived:true ×3 / README+desc указатели / папки удалены / поллер скипает 1→4 / shared-wiki concept создан). Отчёт пиру отправлен (с замечанием по их sequencing-багу — две связанные таски делегированы в обратном порядке). **Next action:** — **Branch:** n/a @@ -955,7 +996,7 @@ Approach ратифицирован человеком (вариант A): ко --- -## 🟢 [deploy-pilorama-web-from-monorepo] — Раскатать на прод текущий `apps/web` из монорепы `victor/pilorama98.ru` — содержит SEO on-page фиксы (тех-аудит 2026-06-18): `buildProductSeo` (172 товарных title+meta, commit `03c250b`) + `seoOverrides` (16 category/service title + 8 meta, commit `b73c7c8`). На проде их пока НЕТ. +## 🟢 [#433 deploy-pilorama-web-from-monorepo] — Раскатать на прод текущий `apps/web` из монорепы `victor/pilorama98.ru` — содержит SEO on-page фиксы (тех-аудит 2026-06-18): `buildProductSeo` (172 товарных title+meta, commit `03c250b`) + `seoOverrides` (16 category/service title + 8 meta, commit `b73c7c8`). На проде их пока НЕТ. **Ключевое — перенацелить источник сборки.** Пайплайн исторически собирал из `victor/pilonuxt` (см. web-вики `apps/web/.wiki/concepts/docker-deploy.md`). После консолидации 2026-06-18 код в `victor/pilorama98.ru/apps/web/` (Dockerfile приехал с subtree, там же). Сборку надо перенаправить: clone монорепы, build-контекст = `apps/web/`. @@ -980,6 +1021,7 @@ Approach ратифицирован человеком (вариант A): ко **notify:** pilorama98.ru **Status:** done +**Created:** 2026-06-18 **Where I stopped:** prod зелёный на 6c6d52d — контейнер-smoke все 200 (ecommerce-листинг 200×3, SEO title/meta рендерятся), stderr пуст. Закрыто pilorama98 по подтверждению админа. **Blocker:** Рантайм-регресс в snolla-пакетах из монореп-лока. `apps/web` тянет `@snollajs/content-api@^0.11.0`, который nested-депом подтягивает **`@snollajs/snolla@0.11.0`** (рядом с прямым `@snollajs/snolla@0.16.0`). content-api зовёт `SitesService.getSiteById` из 0.11.0 → `Cannot read properties of undefined (reading 'findByPk')` (Sites Sequelize-модель не инициализирована) → весь SSR 500. **Откатил прод на `19a4a84` (verified 200).** Фикс версий snolla — зона web/snolla-владельца (пир + snolla upstream), не ops. Артефакты сборки (новый `apps/web/Dockerfile` + root `.dockerignore`) лежат в рабочем дереве монорепы, готовы к коммиту после фикса депов. **Update 2026-06-18 (2):** Пир снял snolla-блокер (commit `eaee56f`: content-api@0.12 peerDep + snolla@^0.17 + vue→3.5.38). Пересобрал из `eaee56f`, задеплоил → **snolla-500 ушёл, home/категории 200 с верным SEO** (`/catalog/planken` → title «Планкен прямой и скошенный — купить в СПб» ✓). **НО новый блокер:** `sharp` нативный libvips не бандлится в Nitro `.output` (`libvips-cpp.so.8.18.3: cannot open shared object`) → товарный листинг `/snolla/stores/.../products` 500 → каталог не грузит товары. Платформо-зависимо (локальный prod-бандл пира на его ОС грузит sharp). **Откатил прод на `19a4a84` снова (home 200 + products 200, каталог жив).** sharp приехал с бампом депов — старый 19a4a84 нативных модулей не имел. @@ -999,7 +1041,7 @@ Approach ратифицирован человеком (вариант A): ко --- -## 🟢 [deploy-gsc-product-name-fix] — closed 2026-06-19 — pilonuxt `c4e34d4` на проде, GSC Product `name` FIXED. Build здесь (425MB) → `docker save | ssh vds load` → push С VDS → Portainer stack 16 `pullImage:true` (тег 6c6d52d→c4e34d4). **Acceptance MET:** in-container smoke на VDS + прод-smoke (curl --resolve мимо LAN-DNS) — 3 товарных (`planken-{prjamoj,skoshennyj,zavaltsovannyj}`) → 200, внутри `schema.org/Product` ведущий `` = название товара (было только Brand `Пилорама 98`). 10 nav-роутов 200, без регрессии. Inbox victor/pilorama98.ru отправлен. — Собрать и переразвернуть прод-образ pilonuxt из master (commit `c4e34d4`) — критический GSC-фикс: микроразметка Product `name` (товары выпадали из выдачи Google «Описания товара → отсутствует поле name»). +## 🟢 [#449 deploy-gsc-product-name-fix] — closed 2026-06-19 — pilonuxt `c4e34d4` на проде, GSC Product `name` FIXED. Build здесь (425MB) → `docker save | ssh vds load` → push С VDS → Portainer stack 16 `pullImage:true` (тег 6c6d52d→c4e34d4). **Acceptance MET:** in-container smoke на VDS + прод-smoke (curl --resolve мимо LAN-DNS) — 3 товарных (`planken-{prjamoj,skoshennyj,zavaltsovannyj}`) → 200, внутри `schema.org/Product` ведущий `` = название товара (было только Brand `Пилорама 98`). 10 nav-роутов 200, без регрессии. Inbox victor/pilorama98.ru отправлен. — Собрать и переразвернуть прод-образ pilonuxt из master (commit `c4e34d4`) — критический GSC-фикс: микроразметка Product `name` (товары выпадали из выдачи Google «Описания товара → отсутствует поле name»). ## Что деплоим - Источник: `victor/pilorama98.ru` master @ `c4e34d4` (уже запушен в git.kzntsv.site). @@ -1026,6 +1068,7 @@ Approach ратифицирован человеком (вариант A): ко **notify:** victor/pilorama98.ru **Status:** done +**Created:** 2026-06-19 **Where I stopped:** closed — деплой завершён и верифицирован на проде (digest sha256:577d8b9). Compose SOT (`host-stacks/vds-kzntsv/pilonuxt.compose.yml`) выровнен на c4e34d4. **Next action:** — (владелец нажмёт «Проверить устранение» в GSC) **Branch:** n/a @@ -1034,7 +1077,7 @@ Approach ратифицирован человеком (вариант A): ко --- -## 🟢 [deploy-pilonuxt-static-pages-robots] — closed 2026-06-29 — `40bb383` LIVE на проде (стек 16). DB-backed staticPages + robots.txt отдаются Nitro. Build-пайплайн: master@40bb383 → yarn install → **форс** `rm src/generated`+`schema-gen 0.6` (новый клиент несёт `getStaticPage`/`getRobotsTxt`, грабля обойдена) → docker build 425МБ → push с дома упал на `.output`-слое (EOF/499) → fallback `docker save | ssh vds load` + push с VDS (локально) ✓ → Portainer PUT стек 16 (`pullImage:true`). **Smoke acceptance 6/6** (мимо LAN-DNS, `--resolve` 89.253.255.94): `/robots.txt`→200 text/plain полный из БД (Yandex Clean-param + Sitemap, не allow-all, не статика); `/yandex_*.html`→200 `Verification:`; `/google*.html`→200; `/nope.html`→404 (Nuxt fall-through, не 500); `/`+`/catalog`→200. Notify victor/pilorama98.ru отправлен. +## 🟢 [#466 deploy-pilonuxt-static-pages-robots] — closed 2026-06-29 — `40bb383` LIVE на проде (стек 16). DB-backed staticPages + robots.txt отдаются Nitro. Build-пайплайн: master@40bb383 → yarn install → **форс** `rm src/generated`+`schema-gen 0.6` (новый клиент несёт `getStaticPage`/`getRobotsTxt`, грабля обойдена) → docker build 425МБ → push с дома упал на `.output`-слое (EOF/499) → fallback `docker save | ssh vds load` + push с VDS (локально) ✓ → Portainer PUT стек 16 (`pullImage:true`). **Smoke acceptance 6/6** (мимо LAN-DNS, `--resolve` 89.253.255.94): `/robots.txt`→200 text/plain полный из БД (Yandex Clean-param + Sitemap, не allow-all, не статика); `/yandex_*.html`→200 `Verification:`; `/google*.html`→200; `/nope.html`→404 (Nuxt fall-through, не 500); `/`+`/catalog`→200. Notify victor/pilorama98.ru отправлен.
исходный наряд @@ -1065,6 +1108,7 @@ deps подняты: `@snollajs/snolla ^0.18`, `content-api ^0.13`, `content-api **notify:** victor/pilorama98.ru **Status:** done +**Created:** 2026-06-29 **Where I stopped:** деплой завершён — контейнер `pilonuxt` на `registry.kzntsv.site/pilonuxt:40bb383`, running; acceptance 6/6 verified. **Next action:** — (closed) **Branch:** n/a @@ -1076,7 +1120,7 @@ deps подняты: `@snollajs/snolla ^0.18`, `content-api ^0.13`, `content-api --- -## 🟢 [deploy-pilonuxt-forms-api-drop-snolla] — closed 2026-06-29 — `cf2bba2` LIVE (стек 16). ADR-0010: forms на `@snollajs/forms-api`, `@snollajs/snolla` дропнут целиком, core@0.1.1. Пайплайн: master@cf2bba2 → yarn install → форс `rm src/generated`+schema-gen → docker build 424МБ → save|ssh load + push с VDS (push с дома 499 как всегда) → Portainer PUT стек 16. **Tree-check `.output/server/node_modules` 4/4** (грабля #2): 0× snolla, 0× btoa, ровно 1× data@0.9.1, core 0.1.1 + content-api 0.14.0 + forms-api 0.1.0. **Smoke acceptance зелёный:** `/`+`/catalog`→200 (SSR жив, btoa/snolla-дроп бандл не сломал); `/robots.txt`+`/yandex_*.html`→200 (ADR-0009 не регресс); форма `POST /snolla-forms/forms?path=/checkout` пустой → **422 JSON** (`is_valid:false`, name/phone/email errors), `?path=/nope`→404 (valid НЕ слал — реального письма нет); stderr чист (нет btoa/snolla/MODULE_NOT_FOUND). Гоча smoke: 422 только с `Accept: application/json` (иначе `prefersJson` false → ветка `status||404` отдаёт 404 — не баг). Notify victor/pilorama98.ru (монореп-inbox) отправлен. +## 🟢 [#465 deploy-pilonuxt-forms-api-drop-snolla] — closed 2026-06-29 — `cf2bba2` LIVE (стек 16). ADR-0010: forms на `@snollajs/forms-api`, `@snollajs/snolla` дропнут целиком, core@0.1.1. Пайплайн: master@cf2bba2 → yarn install → форс `rm src/generated`+schema-gen → docker build 424МБ → save|ssh load + push с VDS (push с дома 499 как всегда) → Portainer PUT стек 16. **Tree-check `.output/server/node_modules` 4/4** (грабля #2): 0× snolla, 0× btoa, ровно 1× data@0.9.1, core 0.1.1 + content-api 0.14.0 + forms-api 0.1.0. **Smoke acceptance зелёный:** `/`+`/catalog`→200 (SSR жив, btoa/snolla-дроп бандл не сломал); `/robots.txt`+`/yandex_*.html`→200 (ADR-0009 не регресс); форма `POST /snolla-forms/forms?path=/checkout` пустой → **422 JSON** (`is_valid:false`, name/phone/email errors), `?path=/nope`→404 (valid НЕ слал — реального письма нет); stderr чист (нет btoa/snolla/MODULE_NOT_FOUND). Гоча smoke: 422 только с `Accept: application/json` (иначе `prefersJson` false → ветка `status||404` отдаёт 404 — не баг). Notify victor/pilorama98.ru (монореп-inbox) отправлен.
исходный наряд @@ -1111,6 +1155,7 @@ deps подняты: `@snollajs/snolla ^0.18`, `content-api ^0.13`, `content-api **notify:** victor/pilorama98.ru **Status:** done +**Created:** 2026-06-29 **Where I stopped:** деплой завершён — `pilonuxt` на `registry.kzntsv.site/pilonuxt:cf2bba2` running; tree-check 4/4 + smoke acceptance зелёный. **Next action:** — (closed) **Branch:** n/a @@ -1122,9 +1167,10 @@ deps подняты: `@snollajs/snolla ^0.18`, `content-api ^0.13`, `content-api --- -## 🟢 [pilonuxt-deploy-sitemap] — [weight: needs-human — deploy-infra] Деплой pilonuxt с sitemap.xml через content-api. Собрать образ из Gitea master victor/pilorama98.ru @ 42f9d64 (feat: sitemap via content-api proxy, content-api@0.15.0), запушить в registry.kzntsv.site/pilonuxt:, перекатить Portainer-стек 16 за Traefik. Грабли билда (как в прошлых pilonuxt-деплоях): форс `rm apps/web/src/generated && yarn workspace nuxt-app schema-gen` перед билдом; tree-check `.output` — 0× snolla, 0× btoa, ровно 1× @snollajs/data@0.9.1, и content-api теперь 0.15.0 (+ @snollajs/core@0.2.0). DB-миграция НЕ нужна — sitemap-колонки на проде уже есть (snolla live-проверил на прод-MSSQL). Локально verified против прод-MSSQL. +## 🟢 [#467 pilonuxt-deploy-sitemap] — [weight: needs-human — deploy-infra] Деплой pilonuxt с sitemap.xml через content-api. Собрать образ из Gitea master victor/pilorama98.ru @ 42f9d64 (feat: sitemap via content-api proxy, content-api@0.15.0), запушить в registry.kzntsv.site/pilonuxt:, перекатить Portainer-стек 16 за Traefik. Грабли билда (как в прошлых pilonuxt-деплоях): форс `rm apps/web/src/generated && yarn workspace nuxt-app schema-gen` перед билдом; tree-check `.output` — 0× snolla, 0× btoa, ровно 1× @snollajs/data@0.9.1, и content-api теперь 0.15.0 (+ @snollajs/core@0.2.0). DB-миграция НЕ нужна — sitemap-колонки на проде уже есть (snolla live-проверил на прод-MSSQL). Локально verified против прод-MSSQL. **Status:** 🟢 closed 2026-06-29 — `pilonuxt:42f9d64` LIVE на проде (Portainer стек 16). +**Created:** 2026-06-29 **Where I stopped:** Собран из Gitea master `victor/pilorama98.ru @ 42f9d64` (контекст=корень монорепы, `-f apps/web/Dockerfile`, verdaccio build-secret). Форс-регенерация `apps/web/src/generated` против прод-MSSQL (schema-gen ok). **Tree-check `.output/server/node_modules` 5/5:** content-api **0.15.0**, core **0.2.0**, data **0.9.1** (ровно 1×), forms-api 0.1.0; snolla-пакетов 0 (7 grep-хитов = только комменты-провенансы, 0 живых import), btoa 0 пакетов/0 импортов. Push в registry **напрямую** (499 не случился, digest `sha256:d937c6b3…`). Стек 16 перекатан PUT API (`cf2bba2`→`42f9d64`, pullImage:true), контейнер running, Nitro Listening, stderr чист (ECONNRESET нет). DB-миграция не требовалась. **Acceptance на проде (curl --resolve мимо LAN-DNS, 89.253.255.94) — 3/3 + регресс:** (1) `/sitemap.xml` → 200 application/xml, ``, 6 источников ✓; (2) `/sitemap-pages-1.xml` → 200 application/xml, ``, 70 url, все 70 абсолютные `https://pilorama98.ru/` ✓; (3) `/sitemap-store-products-999.xml` → 404 ✓. Регресс цел: `/robots.txt` 200 (Sitemap-директива на месте), `/`+`/catalog` 200. Откат при нужде: стек 16 → `pilonuxt:cf2bba2`. Ack отправлен в `victor/pilorama98.ru` inbox. **Branch:** n/a @@ -1134,9 +1180,10 @@ deps подняты: `@snollajs/snolla ^0.18`, `content-api ^0.13`, `content-api --- -## 🟢 [pilonuxt-deploy-sitemap-taxonomy] — [weight: needs-human — deploy-infra] Деплой pilonuxt: sitemap taxonomy (категории/теги/vendors). Bump-only поверх уже-LIVE sitemap v1 — `@snollajs/content-api` 0.15.0→0.16.0 (несёт `@snollajs/core@0.4.0`, taxonomy enumeration), код прокси не менялся (generic-шим). Собрать из Gitea master victor/pilorama98.ru @ 6b9ae63, push в registry.kzntsv.site/pilonuxt:, перекат Portainer-стека 16. Грабли билда как обычно: `rm apps/web/src/generated && yarn workspace nuxt-app schema-gen`; tree-check `.output` — 0× snolla, 0× btoa, ровно 1× @snollajs/data@0.9.1, content-api теперь 0.16.0 + core 0.4.0. DB-миграция НЕ нужна. Локально verified против прод-MSSQL. +## 🟢 [#468 pilonuxt-deploy-sitemap-taxonomy] — [weight: needs-human — deploy-infra] Деплой pilonuxt: sitemap taxonomy (категории/теги/vendors). Bump-only поверх уже-LIVE sitemap v1 — `@snollajs/content-api` 0.15.0→0.16.0 (несёт `@snollajs/core@0.4.0`, taxonomy enumeration), код прокси не менялся (generic-шим). Собрать из Gitea master victor/pilorama98.ru @ 6b9ae63, push в registry.kzntsv.site/pilonuxt:, перекат Portainer-стека 16. Грабли билда как обычно: `rm apps/web/src/generated && yarn workspace nuxt-app schema-gen`; tree-check `.output` — 0× snolla, 0× btoa, ровно 1× @snollajs/data@0.9.1, content-api теперь 0.16.0 + core 0.4.0. DB-миграция НЕ нужна. Локально verified против прод-MSSQL. **Status:** 🟢 closed 2026-06-29 — `pilonuxt:6b9ae63` LIVE на проде (Portainer стек 16). +**Created:** 2026-06-29 **Where I stopped:** Bump-only поверх sitemap v1. Собран из Gitea master `6b9ae63` (форс schema-gen против прод-MSSQL). **Tree-check `.output` 5/5:** content-api **0.16.0**, core **0.4.0**, data **0.9.1** (1×), snolla 0 пакетов/0 import, btoa 0. Push: словил **499 на .output-слое** (документированная грабля) → ретрай прошёл, digest `sha256:226584ce…`. Стек 16 перекатан PUT (`42f9d64`→`6b9ae63`, pullImage:true), running, Nitro Listening, stderr чист. **Acceptance на проде (curl --resolve мимо LAN-DNS, 89.253.255.94) — 4/4 + регресс:** (1) `/sitemap.xml` → 200, индекс 7 записей, есть `sitemap-store-taxonomy-1.xml` ✓; (2) `/sitemap-store-taxonomy-1.xml` → 200 application/xml, ``, 57 url все абсолютные (35 `/shop/categories/` + 22 `/shop/tags/`; vendors 0 — пусто в CMS, как в NB) ✓; (3) v1 цел: `/sitemap-store-products-1.xml` 200, `/sitemap-pages-1.xml` 200 ✓; (4) `/sitemap-store-taxonomy-999.xml` → 404 ✓. Регресс: `/`+`/catalog` 200. Откат при нужде: стек 16 → `pilonuxt:42f9d64`. Ack отправлен в `victor/pilorama98.ru` inbox. **Branch:** n/a @@ -1146,7 +1193,7 @@ deps подняты: `@snollajs/snolla ^0.18`, `content-api ^0.13`, `content-api --- -## 🟢 [labtools-web-vds-deploy] — Деплой `labtools.ru` snolla-приложения (`victor/labtools.ru/apps/web`) в docker-контейнере на Rusonyx VDS за traefik. Приложение parity-подтверждено (ре-ревью №2 PASS: HTML+визуал byte-parity с боем `www.labtools.ru`, ассеты из MinIO, ноль локальных хаков, `@snollajs/snolla@0.28.2`). +## 🟢 [#489 labtools-web-vds-deploy] — Деплой `labtools.ru` snolla-приложения (`victor/labtools.ru/apps/web`) в docker-контейнере на Rusonyx VDS за traefik. Приложение parity-подтверждено (ре-ревью №2 PASS: HTML+визуал byte-parity с боем `www.labtools.ru`, ассеты из MinIO, ноль локальных хаков, `@snollajs/snolla@0.28.2`). ## Кто/где собирает образ (решение workshop) **Образ собирает АДМИН на VDS** из `Dockerfile`, который пишет имплементер. build нужен docker + доступ к verdaccio (`verdaccio.kzntsv.site`) + `VERDACCIO_TOKEN` — эта инфра у админа/на VDS; имплементер владеет корректностью Dockerfile, не реестром. Кросс-машинный трансфер образа не нужен. @@ -1177,6 +1224,7 @@ deps подняты: `@snollajs/snolla ^0.18`, `content-api ^0.13`, `content-api Dev-source: `victor/labtools.ru/apps/web` (parity-док/буфер — в `.workshop/.brainstorm/snolla-sites-migration.md`). Blast: cutover затронет живой прод labtools.ru — staging-first, cutover gated. **Status:** 🟢 CLOSED 2026-07-02 — cutover LIVE. `labtools.ru`+`www` на VDS (89.253.255.94), стек 17, образ `labtools:43e28ba`. Рунбук § Cutover обновлён. +**Created:** 2026-07-01 **Closure:** DNS зона на **Yandex DNS** (не reg.ru). Флип шёл неравномерно — dns1/dns2 расходились; cutover-гейт = ОБА авторитетных Yandex-NS согласованно → VDS по apex+www (монитор дождался). Стек 17 traefik-rule → `Host(labtools.ru)||Host(www.labtools.ru)` (Portainer PUT, env 8/8, pullImage=false); LE-cert выпущен (~t+20s). Live-smoke GREEN: оба хоста 200 (nav+каталог-секции+продукты), редиректы (`/about/` trailing, `/Contacts` lowercase, `/index.php`→`/`) parity, sitemap index (host=labtools.ru non-www — siteUrl тенанта без www), theme-ассеты byte-identical (RUVDS 301 apex→www → прямой md5 артефакт, с -L MD5-OK). staging-host убран (→404). Внешне: apex→VDS 200; www ещё split-brain на кэше RUVDS (200, идентичный контент, тухнет по TTL 6ч). RUVDS = rollback, не тронут. Репорт workshop отправлен. **Весь тираж закрыт: labtools.ru + labtools.pro LIVE на VDS.** **Next action:** — (закрыто). Опционально позже по решению оператора: снять RUVDS-биндинги labtools.ru/www (как для emspb) — сейчас оставлены как rollback. **Branch:** master @@ -1185,10 +1233,11 @@ Dev-source: `victor/labtools.ru/apps/web` (parity-док/буфер — в `.wor --- -## 🟢 [emspb-web-vds-deploy] — closed 2026-07-02 — staging GREEN + cutover LIVE. Образ `registry.kzntsv.site/emspb:b6e361a` (digest `8f5ba02…`) собран на VDS, Portainer stack `emspb` (Id 18). Staging smoke vs бой 23/23 status-parity, theme-CSS+image из MinIO md5-identical, Cache-Control 86400. **Cutover выполнен в ту же сессию** (оператор флипнул DNS emspb.ru/www→VDS): traefik-rule `Host(emspb.ru)||Host(www.emspb.ru)` (staging-хост убран post-cutover→404), единый LE-cert, live-smoke `www.emspb.ru`+`emspb.ru`→200 `ssl_verify=0`, canonical нормализован, live==старый RUVDS байт-в-байт. RUVDS IIS `80.64.31.36` цел = rollback. Рунбук [emspb-vds-deploy-runbook](../.wiki/concepts/emspb-vds-deploy-runbook.md). Notify→workshop. +## 🟢 [#487 emspb-web-vds-deploy] — closed 2026-07-02 — staging GREEN + cutover LIVE. Образ `registry.kzntsv.site/emspb:b6e361a` (digest `8f5ba02…`) собран на VDS, Portainer stack `emspb` (Id 18). Staging smoke vs бой 23/23 status-parity, theme-CSS+image из MinIO md5-identical, Cache-Control 86400. **Cutover выполнен в ту же сессию** (оператор флипнул DNS emspb.ru/www→VDS): traefik-rule `Host(emspb.ru)||Host(www.emspb.ru)` (staging-хост убран post-cutover→404), единый LE-cert, live-smoke `www.emspb.ru`+`emspb.ru`→200 `ssl_verify=0`, canonical нормализован, live==старый RUVDS байт-в-байт. RUVDS IIS `80.64.31.36` цел = rollback. Рунбук [emspb-vds-deploy-runbook](../.wiki/concepts/emspb-vds-deploy-runbook.md). Notify→workshop. -## ⚪ [emspb-web-vds-deploy] — Деплой `victor/emspb.ru` `apps/web` (snolla-приложение, БД-контент+SSR Liquid) на Rusonyx VDS в docker за traefik, **staging-first**. Зеркало `[labtools-web-vds-deploy]` — тот же паттерн, адаптировать хосты/siteId. Код готов (ре-ревью PASS, ноль хаков, 29/29 parity). `deploy/` артефакты уже в репо. +**Created:** 2026-07-01 +## ⚪ [#488 emspb-web-vds-deploy] — Деплой `victor/emspb.ru` `apps/web` (snolla-приложение, БД-контент+SSR Liquid) на Rusonyx VDS в docker за traefik, **staging-first**. Зеркало `[labtools-web-vds-deploy]` — тот же паттерн, адаптировать хосты/siteId. Код готов (ре-ревью PASS, ноль хаков, 29/29 parity). `deploy/` артефакты уже в репо. ## Источник `victor/emspb.ru` — `deploy/Dockerfile` (multi-stage, build-arg `VERDACCIO_TOKEN` build-time, node:22-slim non-root, healthcheck) + `deploy/README.md` (env-лист, все секреты через env, ничего не бейкается) + корневой `.dockerignore`. Референс-рунбук: admin-вики `labtools-vds-deploy-runbook`. @@ -1218,6 +1267,7 @@ Dev-source: `victor/labtools.ru/apps/web` (parity-док/буфер — в `.wor **notify:** OpeItcLoc03/workshop **Status:** 🟢 closed 2026-07-02 — staging + cutover оба GREEN, emspb.ru LIVE на VDS. +**Created:** 2026-07-01 **Where I stopped:** (done) Образ b6e361a собран+запушен на VDS, stack Id 18, staging smoke 23/23, cutover выполнен (traefik-rule на боевые хосты, LE-cert, live-smoke 200/valid, паритет vs RUVDS байт-в-байт). RUVDS rollback цел. **Next action (было):** Собрать образ на VDS из victor/emspb.ru deploy/Dockerfile (чистый git-archive, секретов нет) → registry.kzntsv.site/emspb: → Portainer stack STAGING emspb.vds.kzntsv.site за traefik+LE → runtime-env-секреты → smoke parity vs бой www.emspb.ru. Рунбук в admin-вику. Cutover live DNS — НЕ трогать (отдельный gated-шаг). **Branch:** n/a @@ -1226,7 +1276,7 @@ Dev-source: `victor/labtools.ru/apps/web` (parity-док/буфер — в `.wor --- -## 🟢 [labtools-pro-web-vds-deploy] — Вынести `victor/labtools.pro` (snolla-app, `@snollajs/snolla@0.28.7`, server-side Liquid, БД-контент + MinIO-ассеты) с RUVDS IIS в docker-контейнер на VDS kzntsv (89.253.255.94) за traefik. **Зеркало `[emspb-web-vds-deploy]`** — тот же паттерн; рунбуки `emspb-vds-deploy-runbook` / `labtools-vds-deploy-runbook` (.admin вика) читать как шаблон. Код-DONE: ре-ревью PASS (все 9 дименшнов, ноль хаков), независимый ревьюер на живой БД+MinIO. +## 🟢 [#498 labtools-pro-web-vds-deploy] — Вынести `victor/labtools.pro` (snolla-app, `@snollajs/snolla@0.28.7`, server-side Liquid, БД-контент + MinIO-ассеты) с RUVDS IIS в docker-контейнер на VDS kzntsv (89.253.255.94) за traefik. **Зеркало `[emspb-web-vds-deploy]`** — тот же паттерн; рунбуки `emspb-vds-deploy-runbook` / `labtools-vds-deploy-runbook` (.admin вика) читать как шаблон. Код-DONE: ре-ревью PASS (все 9 дименшнов, ноль хаков), независимый ревьюер на живой БД+MinIO. **Weight:** needs-human **Notify:** OpeItcLoc03/workshop @@ -1261,6 +1311,7 @@ Acceptance = внешний smoke на задеплоенном URL: invoke `using-vds-ops`/`using-tasks`/`project-discipline`. Написать рунбук `labtools.pro-vds-deploy-runbook` в .admin вике (зеркало emspb). Репорт → OpeItcLoc03/workshop. Ops-acceptance = staging smoke-parity green; cutover — отдельным словом оператора. **Status:** 🟢 CLOSED 2026-07-02 — cutover LIVE. `labtools.pro`+`www` на VDS (89.253.255.94), стек 19, образ `labtools-pro:7bd9fae`. Runbook: `.wiki/concepts/labtools.pro-vds-deploy-runbook.md`. +**Created:** 2026-07-02 **Closure:** DNS флип подтверждён авторитетным `ns1.reg.ru` (Resolve-DnsName) + 8.8.8.8 → 89.253.255.94 (именно `.pro`; ранние проверки ловили старый TTL-кэш). Стек 19 traefik-rule → `Host(labtools.pro)||Host(www.labtools.pro)` (Portainer PUT, env 8/8 сохранён, pullImage=false); LE-cert выпущен мгновенно (ssl_verify=0). Live-smoke GREEN: оба хоста 200 (nav+каталог+продукты), редиректы parity, sitemap index host=www, контент реальный (lang=en). staging-хост убран (→404). Внешняя проверка с RUVDS-хоста: labtools.pro/www→89.253.255.94 HTTP 200 cert-trusted. RUVDS labtools.pro = rollback, жив, не тронут. Репорт workshop отправлен. **Where I stopped:** Образ `registry.kzntsv.site/labtools-pro:7bd9fae` (+latest, digest `sha256:23d0bc59…`) собран+запушен НА VDS (guard default.json absent OK). Portainer stack `labtools-pro` **Id 19** создан (env verbatim из stack 17, 8 секретов), контейнер healthy, MSSQL+S3 подключены, лог чист. Staging `labtools-pro.vds.kzntsv.site` LE-cert выпущен. **Smoke-parity vs бой `www.labtools.pro` GREEN** (гнал с VDS): статусы nav+3 каталог-секции+продукты 200/200; редиректы (каталог#1→lshm-750, /Contacts→lowercase, /contacts/→trailing) 301 parity; sitemap = index из 7 дочерних, в сумме 25 URL набор==бой, host=www (R3); theme-ассеты 7/7 md5-identical; Cache-Control absent (корректно, `CachingOptions=[]`); контент-страницы реальное тело (delta +155…+222B = host-длина). Косметика (не дефект, отдана workshop): стартовый баннер лога = `labtools.ru (snolla)` (константа из скаффолда). Compose: `host-stacks/vds-kzntsv/labtools-pro.compose.yml`. Репорт workshop отправлен. **Next action:** — (закрыто). Follow-up (не блокер, у workshop): косметика баннера `labtools.ru` в логе (константа app-name из скаффолда). Опционально позже: снять RUVDS-биндинги labtools.pro (как сделано для emspb) — по решению оператора, сейчас оставлены как rollback. @@ -1270,11 +1321,12 @@ invoke `using-vds-ops`/`using-tasks`/`project-discipline`. Написать ру --- -## 🟢 [imgproxy-watermark-glyph-books-vds] — closed 2026-07-04 — глиф LIVE на imgproxy стек 29 (books-vds). Первый base64 (в inbox И в таске) был битый на источнике (sha mismatch, IEND нет, зацикленный хвост — `tasks_create` порезал поле); поймал sha-сверкой ДО прода. Воркшоп до-доставил `logo.png` бинарём в git (`host-stacks/books-vds/tandemmebel-watermark-logo.png`, commit `7127e92a`, sha256 `e995…971fc`). PUT стека 29 (Portainer `portainer.kzntsv.site` ep1, X-API-Key, `IMGPROXY_WATERMARK_DATA` inline env, `PullImage:false`, БЕЗ глобального `_OPACITY`). Smoke cache-busted: A tandem 200/27294→27820B вотермарк ВИДЕН глазами; B pilorama контроль byte-identical 160010B (opacity не просочилась). Откат: PUT бэкапа стека без WATERMARK_DATA. Notify workshop отправлен → tandem-агент на ре-скрин→пин→deploy. +## 🟢 [#537 imgproxy-watermark-glyph-books-vds] — closed 2026-07-04 — глиф LIVE на imgproxy стек 29 (books-vds). Первый base64 (в inbox И в таске) был битый на источнике (sha mismatch, IEND нет, зацикленный хвост — `tasks_create` порезал поле); поймал sha-сверкой ДО прода. Воркшоп до-доставил `logo.png` бинарём в git (`host-stacks/books-vds/tandemmebel-watermark-logo.png`, commit `7127e92a`, sha256 `e995…971fc`). PUT стека 29 (Portainer `portainer.kzntsv.site` ep1, X-API-Key, `IMGPROXY_WATERMARK_DATA` inline env, `PullImage:false`, БЕЗ глобального `_OPACITY`). Smoke cache-busted: A tandem 200/27294→27820B вотермарк ВИДЕН глазами; B pilorama контроль byte-identical 160010B (opacity не просочилась). Откат: PUT бэкапа стека без WATERMARK_DATA. Notify workshop отправлен → tandem-агент на ре-скрин→пин→deploy. -## 🟢 [imgproxy-watermark-glyph-books-vds] — Ops-handoff от workshop (dev-source: victor/tandemmebel.ru pin 7e08bee; snolla core@0.13.8 commit 2ba8b54). Разблокирует финальный визуал-гейт tandemmebel post-featured watermark parity → потом deploy. +**Created:** 2026-07-04 +## 🟢 [#538 imgproxy-watermark-glyph-books-vds] — Ops-handoff от workshop (dev-source: victor/tandemmebel.ru pin 7e08bee; snolla core@0.13.8 commit 2ba8b54). Разблокирует финальный визуал-гейт tandemmebel post-featured watermark parity → потом deploy. ЗАДАЧА: на **books-vds (89.253.255.133)** imgproxy **стек 29** выставить env `IMGPROXY_WATERMARK_DATA` = base64 глифа logo.png + рестарт imgproxy (Portainer стек 29 → env → redeploy, либо .env стека + docker compose up -d). @@ -1300,6 +1352,7 @@ curl -sI 'https://imgproxy.kzntsv.site/nlXN7RMrer11Fe9XPQaXmq4H0ufzPpo2fOzBWtaL0 По готовности notify workshop → tandem-агент гоняет Playwright ре-скрин featured → пин → deploy. Отдельная deploy-таска `[tandemmebel-web-vds-deploy]` последует (в её production.json override `imgproxy.watermark={size:"post-featured",option:"wm:0.5:ce:0:0:0"}`). **Status:** 🟢 closed 2026-07-04 +**Created:** 2026-07-04 **Where I stopped:** глиф LIVE на imgproxy стек 29, smoke green (A вотермарк виден / B контроль чист), notify workshop отправлен. **Next action:** — (закрыто). Дальше по тиражу tandemmebel: ждать `[tandemmebel-web-vds-deploy]` от воркшопа, взять ops-часть. **Branch:** n/a @@ -1309,11 +1362,12 @@ curl -sI 'https://imgproxy.kzntsv.site/nlXN7RMrer11Fe9XPQaXmq4H0ufzPpo2fOzBWtaL0 --- -## 🟢 [tandemmebel-web-vds-deploy] — **CLOSED 2026-07-12 — CUTOVER LIVE на VDS.** Владелец флипанул DNS reg.ru `tandemmebel.ru`+`www`→89.253.255.94 (verify ns1/ns2.reg.ru оба 89.253.255.94). LE-порядок соблюдён: DNS first → traefik `Host()` staging→боевой на стеке 20 (env 8/8 preserve, `prune:false`, `pullImage:false` — образ `ed96b18`/0.42.0 не менялся, без пересборки по решению оператора). LE-серт issued on first hit: CN=tandemmebel.ru, SAN оба, until 2026-10-10. Live-smoke С VDS GREEN: `/`+`/projects`+3×gallery→200, `/articles`→404 (parity benign#3), sitemap **184 page-locs** (рост от blog-archive 0.42.0, benign), self-consistency 30/30 sample, sharp media webp 200, robots из БД (ADR-0009). **Gotham-Pro.css 0B→200/4436B** — латентный прод-баг починен cutover'ом. Rollback = revert DNS→80.64.31.36 (RUVDS жив) ИЛИ PUT стека назад на staging-host. Compose/board/wiki обновлены. **Тираж snolla 0.42.x теперь полностью живой (5/5 стеков на VDS).** +## 🟢 [#544 tandemmebel-web-vds-deploy] — **CLOSED 2026-07-12 — CUTOVER LIVE на VDS.** Владелец флипанул DNS reg.ru `tandemmebel.ru`+`www`→89.253.255.94 (verify ns1/ns2.reg.ru оба 89.253.255.94). LE-порядок соблюдён: DNS first → traefik `Host()` staging→боевой на стеке 20 (env 8/8 preserve, `prune:false`, `pullImage:false` — образ `ed96b18`/0.42.0 не менялся, без пересборки по решению оператора). LE-серт issued on first hit: CN=tandemmebel.ru, SAN оба, until 2026-10-10. Live-smoke С VDS GREEN: `/`+`/projects`+3×gallery→200, `/articles`→404 (parity benign#3), sitemap **184 page-locs** (рост от blog-archive 0.42.0, benign), self-consistency 30/30 sample, sharp media webp 200, robots из БД (ADR-0009). **Gotham-Pro.css 0B→200/4436B** — латентный прод-баг починен cutover'ом. Rollback = revert DNS→80.64.31.36 (RUVDS жив) ИЛИ PUT стека назад на staging-host. Compose/board/wiki обновлены. **Тираж snolla 0.42.x теперь полностью живой (5/5 стеков на VDS).** -## ⚪ [tandemmebel-web-vds-deploy] — Вынос tandemmebel.ru (snolla-app, `@snollajs/snolla` **0.32.2** / `@snollajs/core` **0.13.8**, server-side Liquid, контент+блог-портфолио+category, БЕЗ e-commerce каталога) с RUVDS IIS на VDS kzntsv (89.253.255.94) за traefik. **Зеркало рунбука `labtools.pro-vds-deploy-runbook`** (модель snolla-тенанта: БД MoreThenCms `mssql.kzntsv.site` + ассеты MinIO через imgproxy). Dev + ДВА полных независимых ре-ревью-раунда + focus watermark-раунд ЗАВЕРШЕНЫ, независимый **PASS**. Это ops-handoff. +**Created:** 2026-07-04 +## ⚪ [#545 tandemmebel-web-vds-deploy] — Вынос tandemmebel.ru (snolla-app, `@snollajs/snolla` **0.32.2** / `@snollajs/core` **0.13.8**, server-side Liquid, контент+блог-портфолио+category, БЕЗ e-commerce каталога) с RUVDS IIS на VDS kzntsv (89.253.255.94) за traefik. **Зеркало рунбука `labtools.pro-vds-deploy-runbook`** (модель snolla-тенанта: БД MoreThenCms `mssql.kzntsv.site` + ассеты MinIO через imgproxy). Dev + ДВА полных независимых ре-ревью-раунда + focus watermark-раунд ЗАВЕРШЕНЫ, независимый **PASS**. Это ops-handoff. ### Артефакты - **Код:** `victor/tandemmebel.ru` @ `0facb35afde2a966add75e0d603dcf8f16a868af` (master, apps/web). Раунд-2 re-review PASS (byte-parity 9/10, секреты, тесты 22/22, ноль хаков) + focus watermark-раунд PASS (featured-вотермарк=бой, мелкие чисты, cross-tenant чист). `deploy/Dockerfile` (multi-stage node:22-slim, non-root, healthcheck). @@ -1349,6 +1403,7 @@ Focus-ревьюер (независимо) нашёл: nginx перед imgprox DNS → RUVDS IIS (живой, не выводить до явного решения оператора). Стек remove / откат тега. **Status:** 🔵 BLOCKED 2026-07-04 — оператор при визуальной проверке sharp-staging (стек 20, образ 68b93a9, https://tandemmebel.vds.kzntsv.site) нашёл НОВЫЙ дефект (иной, чем прошлые). Специфику не назвал — «чиним в новой сессии». Cutover HELD до фикса. Sharp-staging сам по себе green по моим гейтам; дефект — визуальный, оператор опишет в новой сессии. +**Created:** 2026-07-04 **Where I stopped:** build ✅ (tandemmebel:0facb35 на VDS) + стек Portainer Id 20 (staging-rule, оставлен UP) + staging-smoke GREEN (все гейты, benign #2/3/5 учтены). Оператор поставил cutover на HOLD → решение: watermark через sharp (victor/snolla media-sharp-serve-and-watermark), НЕ imgproxy. **imgproxy глиф со стека 29 СНЯТ** (таск [imgproxy-stack29-watermark-rollback] 🟢, боевые тенанты verified чисты); глиф-бинарь 7127e92a оставлен для sharp-composite. Текущий образ 0facb35 использует imgproxy.watermark — при возврате к деплою нужен ПЕРЕСБОР на sharp-пин. Compose source-of-truth в host-stacks/vds-kzntsv/tandemmebel.compose.yml. **Next action:** ЖДУ отмашку ОПЕРАТОРА на DNS-флип reg.ru (tandemmebel.ru+www → 89.253.255.94). Потом: verify авторит.NS (Resolve-DnsName) ПЕРЕД traefik → PUT стека 20 rule → Host(`tandemmebel.ru`)||Host(`www.tandemmebel.ru`), убрать staging-host → live-smoke оба хоста + featured-вотермарк на живом. RUVDS=rollback, не трогать. **Branch:** n/a @@ -1358,11 +1413,12 @@ DNS → RUVDS IIS (живой, не выводить до явного реше --- -## 🟢 [imgproxy-stack29-watermark-rollback] — closed 2026-07-04 — `IMGPROXY_WATERMARK_DATA` снят со стека 29 (books-vds), стек byte-identical pre-watermark бэкапу (cross-check true), redeploy+purge. Verify: pilorama98 (главный консюмер, 122 ref) 3 рендера == baseline (43950/172042/160010B) — не задет; stostayer НЕ imgproxy-консюмер (картинки через /galleries//images//assets/) → не затрагивается; WATERMARK_DATA present:false; tandem featured→чистый (27820→27294B, ожидаемая побочка, staging→sharp). Глиф-бинарь 7127e92a НЕ тронут (для sharp-composite). Notify workshop отправлен. +## 🟢 [#535 imgproxy-stack29-watermark-rollback] — closed 2026-07-04 — `IMGPROXY_WATERMARK_DATA` снят со стека 29 (books-vds), стек byte-identical pre-watermark бэкапу (cross-check true), redeploy+purge. Verify: pilorama98 (главный консюмер, 122 ref) 3 рендера == baseline (43950/172042/160010B) — не задет; stostayer НЕ imgproxy-консюмер (картинки через /galleries//images//assets/) → не затрагивается; WATERMARK_DATA present:false; tandem featured→чистый (27820→27294B, ожидаемая побочка, staging→sharp). Глиф-бинарь 7127e92a НЕ тронут (для sharp-composite). Notify workshop отправлен. -## 🟢 [imgproxy-stack29-watermark-rollback] — СРОЧНО (оператор просил поскорее). Откатить watermark-инъекцию на SHARED imgproxy стек 29 (books-vds). Причина: tandemmebel уходит с imgproxy-watermark на in-process sharp (задача victor/snolla media-sharp-serve-and-watermark), глифу на общем инстансе больше не место. Shared инстанс держит БОЕВЫХ pilorama98/sotstayer — аккуратно. +**Created:** 2026-07-04 +## 🟢 [#536 imgproxy-stack29-watermark-rollback] — СРОЧНО (оператор просил поскорее). Откатить watermark-инъекцию на SHARED imgproxy стек 29 (books-vds). Причина: tandemmebel уходит с imgproxy-watermark на in-process sharp (задача victor/snolla media-sharp-serve-and-watermark), глифу на общем инстансе больше не место. Shared инстанс держит БОЕВЫХ pilorama98/sotstayer — аккуратно. ## Шаги @@ -1389,6 +1445,7 @@ tandemmebel-staging (стек 20) потеряет featured-вотермарк **Dev-source:** victor/snolla media-sharp-serve-and-watermark (переход на sharp) **Status:** 🟢 closed 2026-07-04 +**Created:** 2026-07-04 **Where I stopped:** WATERMARK_DATA снят со стека 29, redeploy+purge, pilorama/stostayer verified не задеты, глиф-бинарь оставлен. Notify workshop отправлен. **Next action:** — (закрыто). **Branch:** n/a @@ -1398,11 +1455,12 @@ tandemmebel-staging (стек 20) потеряет featured-вотермарк --- -## 🟢 [minio-variant-cache-bucket] — closed 2026-07-04 — бакет `variant-cache` создан в shared MinIO (minio.kzntsv.site/books-vds), пустой, другие 17 бакетов не тронуты. Грант: snolla S3-креды (S3_ACCESS_KEY_ID/SECRET из деплой-env) == MinIO root (сверено оба поля) → в mode-server-fs root имеет полный доступ by construction, IAM-грант не нужен (FS-режим его не держит). Write-verify ПОД деплойными кредами (явный alias): put→stat→get(md5 match 78fab481)→delete→gone ✓. Lifecycle не ставил (опц., кэш регенерируем). sharp-prereq (500 NoSuchBucket) снят. Notify workshop. +## 🟢 [#539 minio-variant-cache-bucket] — closed 2026-07-04 — бакет `variant-cache` создан в shared MinIO (minio.kzntsv.site/books-vds), пустой, другие 17 бакетов не тронуты. Грант: snolla S3-креды (S3_ACCESS_KEY_ID/SECRET из деплой-env) == MinIO root (сверено оба поля) → в mode-server-fs root имеет полный доступ by construction, IAM-грант не нужен (FS-режим его не держит). Write-verify ПОД деплойными кредами (явный alias): put→stat→get(md5 match 78fab481)→delete→gone ✓. Lifecycle не ставил (опц., кэш регенерируем). sharp-prereq (500 NoSuchBucket) снят. Notify workshop. -## 🟢 [minio-variant-cache-bucket] — Создать MinIO-бакет `variant-cache` — prereq для sharp-медиа доставки. Без него sharp-движок при resize пишет готовый вариант в бакет `variant-cache`, а его нет → HTTP 500 `NoSuchBucket` на ВСЕХ sharp-картинках (agent-блокер 1, подтверждён live на tandemmebel-verify). +**Created:** 2026-07-04 +## 🟢 [#540 minio-variant-cache-bucket] — Создать MinIO-бакет `variant-cache` — prereq для sharp-медиа доставки. Без него sharp-движок при resize пишет готовый вариант в бакет `variant-cache`, а его нет → HTTP 500 `NoSuchBucket` на ВСЕХ sharp-картинках (agent-блокер 1, подтверждён live на tandemmebel-verify). Это S3-эквивалент легаси App_Data/imageCache: движок ресайзит+вотермарчит on-demand и кладёт результат в этот бакет, потом отдаёт из него. Кэш ленивый/регенерируемый (можно чистить свободно — восстановится из оригиналов по заходу). @@ -1428,6 +1486,7 @@ tandemmebel-staging (стек 20) потеряет featured-вотермарк **Dev-source:** victor/snolla media-sharp-imageprocessor-port (agent-блокер 1 из tandemmebel sharp-verify) **Status:** 🟢 closed 2026-07-04 +**Created:** 2026-07-04 **Where I stopped:** бакет variant-cache создан + write-verify под деплойными кредами (put→get md5→delete) зелёный; snolla-креды==root (FS-режим). Notify workshop отправлен. **Next action:** — (закрыто). Имя может смениться при media-sharp-imageprocessor-port → пересоздам по пингу. **Branch:** n/a @@ -1437,11 +1496,12 @@ tandemmebel-staging (стек 20) потеряет featured-вотермарк --- -## 🟢 [tandemmebel-sharp-staging-rebuild] — closed 2026-07-04 — sharp staging GREEN. Образ `tandemmebel:68b93a9` (snolla 0.34.0/core 0.15.0 in-process sharp, digest 12288b15) собран на VDS; стек 20 обновлён (env verbatim 8/8). Smoke: nav-parity 8/8, sitemap staging⊇prod (prod-only=0, +12 categories benign#3), 2012-посты 31/31, redirects 301/301; sharp media = slug-URL 200 webp serve-bytes, **0 /imgproxy-рефов**, variant-cache пишется (32 объекта, NoSuchBucket снят); watermark визуально ✓ на featured+lightbox, gallery-small чистый; лог без ошибок. Находка (не блокер): осиротевший imgproxy-блок в production.json пина эмпирически мёртв (0 /imgproxy-рефов) — вычистить в след. пине. Cutover — НЕ в scope, gated оператором. Notify workshop. +## 🟢 [#542 tandemmebel-sharp-staging-rebuild] — closed 2026-07-04 — sharp staging GREEN. Образ `tandemmebel:68b93a9` (snolla 0.34.0/core 0.15.0 in-process sharp, digest 12288b15) собран на VDS; стек 20 обновлён (env verbatim 8/8). Smoke: nav-parity 8/8, sitemap staging⊇prod (prod-only=0, +12 categories benign#3), 2012-посты 31/31, redirects 301/301; sharp media = slug-URL 200 webp serve-bytes, **0 /imgproxy-рефов**, variant-cache пишется (32 объекта, NoSuchBucket снят); watermark визуально ✓ на featured+lightbox, gallery-small чистый; лог без ошибок. Находка (не блокер): осиротевший imgproxy-блок в production.json пина эмпирически мёртв (0 /imgproxy-рефов) — вычистить в след. пине. Cutover — НЕ в scope, gated оператором. Notify workshop. -## 🟢 [tandemmebel-sharp-staging-rebuild] — РЕЗЮМ ДЕПЛОЯ tandemmebel (был 🟡 paused под пивот на sharp — sharp готов, снимаем паузу). Оператор: «деплоим, ты всё проверишь». Пересобрать staging на новом sharp-движке, parity-smoke. Cutover DNS — НЕ в scope, отдельный gated шаг оператора позже. +**Created:** 2026-07-04 +## 🟢 [#543 tandemmebel-sharp-staging-rebuild] — РЕЗЮМ ДЕПЛОЯ tandemmebel (был 🟡 paused под пивот на sharp — sharp готов, снимаем паузу). Оператор: «деплоим, ты всё проверишь». Пересобрать staging на новом sharp-движке, parity-smoke. Cutover DNS — НЕ в scope, отдельный gated шаг оператора позже. ## Что нового vs прошлый staging (imgproxy-путь) @@ -1476,6 +1536,7 @@ tandemmebel-staging (стек 20) потеряет featured-вотермарк **Dev-source:** victor/tandemmebel sha 68b93a95 (sharp-движок snolla 0.34.0/core 0.15.0) **Status:** 🟢 closed 2026-07-04 (sharp staging green) +**Created:** 2026-07-04 **Where I stopped:** образ 68b93a9 собран + стек 20 обновлён + parity-smoke GREEN (media/watermark/variant-cache/sitemap все ок). Notify workshop отправлен. Осиротевший imgproxy-конфиг в пине — heads-up воркшопу (dead). **Next action:** — (закрыто). Cutover — в [tandemmebel-web-vds-deploy], ждёт DNS-отмашки оператора. **Branch:** n/a @@ -1485,7 +1546,7 @@ tandemmebel-staging (стек 20) потеряет featured-вотермарк --- -## 🟢 [tandemmebel-deploy-snolla-0-42-0] — Раскатить прод tandemmebel на `@snollajs/snolla@0.42.0` (core 0.24.0 / data 0.14.1) — engine-chain-обновление: перепил viewModel-навигации на единый ленивый cross-entity граф (EntityDrop/IndexDrop + декларативный GRAPH-реестр, includeX-запекание выпилено) + blog-вертикаль (blogAuthor / blog.archives / date-archive страницы) + path-фиксы. Всё TDD, опубликовано в registry, byte-verified двумя живыми консюмерами: artmone 17/17 byte-identical, tandemmebel 199/199 роутов Ф3-регресс 0 diff. Dev-source: `victor/snolla`, сессия 2026-07-04, трек `viewmodel-graph-full-platform`. Оператор деплой одобрил (workshop ратифицировал 2026-07-04). +## 🟢 [#541 tandemmebel-deploy-snolla-0-42-0] — Раскатить прод tandemmebel на `@snollajs/snolla@0.42.0` (core 0.24.0 / data 0.14.1) — engine-chain-обновление: перепил viewModel-навигации на единый ленивый cross-entity граф (EntityDrop/IndexDrop + декларативный GRAPH-реестр, includeX-запекание выпилено) + blog-вертикаль (blogAuthor / blog.archives / date-archive страницы) + path-фиксы. Всё TDD, опубликовано в registry, byte-verified двумя живыми консюмерами: artmone 17/17 byte-identical, tandemmebel 199/199 роутов Ф3-регресс 0 diff. Dev-source: `victor/snolla`, сессия 2026-07-04, трек `viewmodel-graph-full-platform`. Оператор деплой одобрил (workshop ратифицировал 2026-07-04). ## Топология — РЕШЕНА оператором: пересборка staging ДО cutover (вариант A) Оператор: «пусть всё переебашит с новыми версиями пакетов до cutover». Значит: @@ -1515,6 +1576,7 @@ tandemmebel-staging (стек 20) потеряет featured-вотермарк **weight:** needs-human (боевой деплой живого сайта + engine-ядро) **Status:** done +**Created:** 2026-07-04 **Where I stopped:** ✅ GOTOVO. Собрал `tandemmebel:ed96b18` (digest `ca4da79`) на VDS из sha ed96b18 (snolla 0.42.0/core 0.24.0/data 0.14.1, verdaccio publish подтверждён), запушил в registry, передеплоил стек 20 (Portainer PUT, env 8/8 сохранён, PullImage) → контейнер **healthy**, running image==ed96b18. Гейты GREEN на новом образе: **acceptance#1** SSR /→200, /projects→200 (title «Наши проекты мебели», не 500); **acceptance#2** completeness-DoD с VDS — sitemap **172/172 паритет, 0 real-fail** (prod200/staging-not200) + gallery-grid **12/12 точный паритет** (bedrooms 80/80…kids 138/138), крошки Главная/Мебель/Галерея наполнены==prod, title==prod, sharp отдаёт webp-байты (200 image/webp). archive-роуты N/A (projects archivesPath=""). Rollback наготове (RUVDS не тронут, b02ca18 в registry). **Next action:** — (закрыто). Cutover остаётся отдельным DNS-gated шагом в [tandemmebel-web-vds-deploy] — образ к флипу теперь ed96b18. Follow-up для dev/workshop: Dockerfile печёт VERDACCIO_TOKEN в ARG/ENV-историю слоёв (2 warn SecretsUsedInArgOrEnv) — вынести в build-secret. **Branch:** n/a @@ -1523,7 +1585,7 @@ tandemmebel-staging (стек 20) потеряет featured-вотермарк --- -## 🟢 [labtools-ru-deploy-snolla-0-42-1] — Обновить ЖИВОЙ прод labtools.ru на VDS (Portainer стек 17) с `@snollajs/snolla@0.28.2` → **0.42.1** in-place. Резолвимый sha бампа: **`566d41c`** (origin/master victor/labtools.ru; пин 0.42.1 + yarn.lock → core 0.24.1 / data 0.14.1 / liquid 0.10.2). Суперсидит пин 0.28.2 из `[labtools-web-vds-deploy]`. +## 🟢 [#561 labtools-ru-deploy-snolla-0-42-1] — Обновить ЖИВОЙ прод labtools.ru на VDS (Portainer стек 17) с `@snollajs/snolla@0.28.2` → **0.42.1** in-place. Резолвимый sha бампа: **`566d41c`** (origin/master victor/labtools.ru; пин 0.42.1 + yarn.lock → core 0.24.1 / data 0.14.1 / liquid 0.10.2). Суперсидит пин 0.28.2 из `[labtools-web-vds-deploy]`. ⚠️ **labtools.ru УЖЕ cutover'нут на VDS** (2026-07-02, боевой `Host(labtools.ru)` на стеке 17). Это **обновление ЖИВОГО прода in-place** (pull новый тег → смена тега на живом стеке 17 → rollback = предыдущий тег), **НЕ staging-first, НЕ DNS-флип** (домен уже на VDS). @@ -1555,6 +1617,7 @@ tandemmebel-staging (стек 20) потеряет featured-вотермарк **notify:** victor/labtools.ru (репорт мне; deploy-петля минует workshop). **Status:** 🟢 done — 2026-07-05. labtools.ru LIVE на snolla 0.42.1, стек 17, образ `registry.kzntsv.site/labtools:566d41c` (digest `ea0649a…`). Оператор дал отмашку на боевой apply (+пуш). +**Created:** 2026-07-05 **Where I stopped:** GREEN. Собрал `labtools:566d41c` на VDS (BUILD_EXIT=0, secret-guard: config/default.json ABSENT, VERDACCIO_TOKEN build-only). Acceptance на НОВОМ образе (throwaway-staging из env живого стека 17, С VDS против прод-оракула): (1) **completeness** — новый sitemap = **38 page-locs** (7 pages+1 static+6 sections+24 продукта), все 37×200 + 1×301(→200, `/products/laboratory-ball-mills`→`/lshm-750`, идентично проду); (2) **content-not-lost** — 11 реальных prod-страниц 0 потерь; (3) **order-fix 0.42.1** — presses = plg-20,plg-12,plg-25,pgr-10 == прод. NB: старый прод 0.28.2 sitemap был дефицитным (sections=1, catalog=2) — 0.42.1 отдаёт полную корректную структуру (per-category product-sitemaps). **Next action:** — (закрыто). Боевой swap выполнен: PUT стека 17 (env 8/8 сохранён, pullImage=true) → контейнер healthy на 566d41c → **live-smoke labtools.ru зелёный** (/,robots,about,contacts,presses=200; order фикс вживую; sitemap 38; TLS-серт CN=labtools.ru Jul2→Sep30 не тронут, без LE-churn). Rollback = тег `labtools:43e28ba` (0.28.2, в registry) / стек 17 PUT назад. RUVDS 80.64.31.36 = внешний rollback (не тронут). Compose source-of-truth обновлён на 566d41c. **Branch:** master @@ -1564,7 +1627,7 @@ tandemmebel-staging (стек 20) потеряет featured-вотермарк --- -## 🟢 [emspb-deploy-snolla-0-42-0] — Пересобрать образ emspb.ru на движке `@snollajs/snolla@0.42.1` (core 0.24.1 / data 0.14.1 / liquid 0.10.2) и обновить деплой на VDS. **Суперсидит** пин образа прошлой закрытой `[emspb-web-vds-deploy]` (текущий live-образ = `b6e361a` / snolla 0.28.4, Portainer stack Id 18). Консюмер-бамп ЗАПУШЕН: `victor/emspb.ru` master `95a5c42` (пин 0.42.1 + yarn.lock: snolla 0.42.1 / core 0.24.1 / data 0.14.1 / liquid 0.10.2). +## 🟢 [#557 emspb-deploy-snolla-0-42-0] — Пересобрать образ emspb.ru на движке `@snollajs/snolla@0.42.1` (core 0.24.1 / data 0.14.1 / liquid 0.10.2) и обновить деплой на VDS. **Суперсидит** пин образа прошлой закрытой `[emspb-web-vds-deploy]` (текущий live-образ = `b6e361a` / snolla 0.28.4, Portainer stack Id 18). Консюмер-бамп ЗАПУШЕН: `victor/emspb.ru` master `95a5c42` (пин 0.42.1 + yarn.lock: snolla 0.42.1 / core 0.24.1 / data 0.14.1 / liquid 0.10.2). ⚠️ ВАЖНО — emspb.ru УЖЕ ЖИВОЙ на VDS (cutover выполнен 2026-07-02, stack 18 за traefik+LE, бой резолвится в 89.253.255.94). Это НЕ чистый staging — обновление живого прод-контейнера. Топология (пересобрать → отдельный staging-стек → gated swap, ЛИБО in-place PUT stack 18) — на усмотрение админа; rollback = текущий 0.28.4-образ `b6e361a` + RUVDS-IIS (80.64.31.36). @@ -1598,6 +1661,7 @@ tandemmebel-staging (стек 20) потеряет featured-вотермарк **notify:** victor/emspb.ru (репортить НАПРЯМУЮ мне, минуя workshop). **Status:** done +**Created:** 2026-07-05 **Where I stopped:** superseded by emspb-deploy-snolla-0-42-1 (slug выровнен на реальный пин 0.42.1 + класс уточнён на in-place прод-обновление по коррекции workshop); НЕ исполнять этот, работать по -0-42-1 **Next action:** (none — kept until merged) **Branch:** n/a @@ -1607,7 +1671,7 @@ tandemmebel-staging (стек 20) потеряет featured-вотермарк --- -## ⚪ [emspb-deploy-snolla-0-42-1] — Обновить ЖИВОЙ прод emspb.ru на движке `@snollajs/snolla@0.42.1` (core 0.24.1 / data 0.14.1 / liquid 0.10.2) **in-place**. emspb боевой на VDS: Portainer стек **18**, образ `emspb:b6e361a` (snolla 0.28.4), cutover LIVE 2026-07-02, DNS emspb.ru/www→89.253.255.94. **Суперсидит** пин `b6e361a`. (Пересоздан из `emspb-deploy-snolla-0-42-0` — слаг выровнен на реальный пин 0.42.1 по коррекции workshop.) +## ⚪ [#558 emspb-deploy-snolla-0-42-1] — Обновить ЖИВОЙ прод emspb.ru на движке `@snollajs/snolla@0.42.1` (core 0.24.1 / data 0.14.1 / liquid 0.10.2) **in-place**. emspb боевой на VDS: Portainer стек **18**, образ `emspb:b6e361a` (snolla 0.28.4), cutover LIVE 2026-07-02, DNS emspb.ru/www→89.253.255.94. **Суперсидит** пин `b6e361a`. (Пересоздан из `emspb-deploy-snolla-0-42-0` — слаг выровнен на реальный пин 0.42.1 по коррекции workshop.) Консюмер-бамп ЗАПУШЕН: `victor/emspb.ru` master `95a5c42` (пин 0.42.1 + yarn.lock: snolla 0.42.1 / core 0.24.1 / data 0.14.1 / liquid 0.10.2). @@ -1644,6 +1708,7 @@ tandemmebel-staging (стек 20) потеряет featured-вотермарк **notify:** victor/emspb.ru (репортить НАПРЯМУЮ мне, минуя workshop). **Status:** 🟢 done — 2026-07-05. emspb.ru LIVE на snolla 0.42.1, стек 18, образ `registry.kzntsv.site/emspb:95a5c42` (digest `d6299cf…`). Оператор дал отмашку на боевой apply (+пуш). +**Created:** 2026-07-05 **Where I stopped:** GREEN. Собрал `emspb:95a5c42` на VDS (BUILD_EXIT=0, secret-guard OK). Acceptance на НОВОМ образе (throwaway-staging из env стека 18, С VDS против прод-оракула): completeness — **29 sitemap-роутов все 200** (0 non-2xx/3xx), content-not-lost 29/29 (0 потерь), ключевые страницы 200. Sitemap не реструктурировался (29==29, в отличие от labtools). **Next action:** — (закрыто). Боевой swap: PUT стека 18 (env 8/8 сохранён, pullImage=true) → контейнер healthy на 95a5c42 → **live-smoke emspb.ru зелёный** (/,robots,contacts=200; sitemap 29). TLS OK: SAN-серт покрывает emspb.ru/www.emspb.ru/emspb.vds.kzntsv.site (CN косметически=staging-хост, но curl validates sslverify=0; серт с cutover 07-02, swap не тронул). Rollback = тег `emspb:b6e361a` (0.28.4) / стек 18 PUT назад / RUVDS 80.64.31.36. Compose source-of-truth обновлён на 95a5c42. **Branch:** master @@ -1653,7 +1718,7 @@ tandemmebel-staging (стек 20) потеряет featured-вотермарк --- -## 🟢 [labtools-pro-deploy-snolla-0-42-1] — Обновить ЖИВОЙ прод labtools.pro in-place → snolla 0.42.1 (собрать образ из sha 0610432, перепрогнать гейты на образе, operator-gated apply на боевой стек 19, rollback=7bd9fae). +## 🟢 [#560 labtools-pro-deploy-snolla-0-42-1] — Обновить ЖИВОЙ прод labtools.pro in-place → snolla 0.42.1 (собрать образ из sha 0610432, перепрогнать гейты на образе, operator-gated apply на боевой стек 19, rollback=7bd9fae). ## Goal Обновить **ЖИВОЙ прод** labtools.pro in-place на движке `@snollajs/snolla@0.42.1` (core 0.24.1 / data 0.14.1 / liquid 0.10.2). Класс = обновление живого прода (как labtools.ru), **НЕ staging-first greenfield**. Сайт уже боевой на VDS: Portainer стек **19**, текущий образ `labtools-pro:7bd9fae`, cutover'нут 2026-07-02, DNS→89.253.255.94. @@ -1694,6 +1759,7 @@ Build-рецепт (зеркало tandemmebel): `git -C ~/projects/labtools.pro Cutover DNS уже сделан (07-02) — тут только смена образа на существующем боевом стеке, НЕ новый DNS-флип. **Status:** 🟢 done — 2026-07-05. labtools.pro LIVE на snolla 0.42.1, стек 19, образ `registry.kzntsv.site/labtools-pro:0610432` (digest `3d543fc…`). Оператор дал отмашку на боевой apply (+пуш). +**Created:** 2026-07-05 **Where I stopped:** GREEN. Собрал `labtools-pro:0610432` на VDS (BUILD_EXIT=0, secret-guard OK). Acceptance на НОВОМ образе (throwaway-staging из env стека 19, С VDS против прод-оракула): completeness — **25 sitemap page-locs все 200** (0 non-2xx/3xx; +robots+sitemap.xml = 27 прогерских), content-not-lost 25/25 (0 потерь). **Order-парити ВСЕ секции MATCH** (фикс liquid 0.10.2): presses=`plg-20,plg-12`, milling=`milling-jars,grinding-media`, press-forms=12 изделий (round-xrf…round-collapsible) — все == прод. (NB: мой первичный ручной счёт дал 13 — задвоил тайл; прог-сверка против прод+образа = ровно 12, парити не задет.) **Next action:** — (закрыто). Боевой swap: PUT стека 19 (env 8/8 сохранён, pullImage=true) → healthy на 0610432 → **live-smoke labtools.pro зелёный** (/,robots,about,contacts,presses,press-forms=200; order вживую plg-20,plg-12; sitemap 25). TLS CN=labtools.pro Jul2→Sep30 не тронут. Rollback = тег `labtools-pro:7bd9fae` (0.28.7) / стек 19 PUT назад / DNS→RUVDS. Compose source-of-truth обновлён на 0610432. **Branch:** master @@ -1703,9 +1769,10 @@ Cutover DNS уже сделан (07-02) — тут только смена об --- -## 🟢 [vehicles-loader-redeploy-per-group-rate-diff] — Пересобрать и задеплоить @stostayer/vehicles-loader на прод клиента с новым кодом per-group rate diff. Код уже на origin/master (запушено 85ca936, 5 коммитов b0e7b58..85ca936). Изменение: rate-группы с неизменённым мультимножеством {standardHours,notes} теперь skip'аются вместо слепого DELETE+INSERT — объём записей в БД 51 203 → 1 963 rate-rows/день (−96%), счётчик replaced=реальные записи, skipped=пропущенные группы. Read-consistency сохранена (изменившиеся группы — та же короткая транзакция). Write-примитив replaceGroup не менялся. Валидировано двумя dry-run против prod (0 ошибок, SG skipped=41089/replaced=1555, SM skipped=5131/replaced=408) + 36 unit-тестов. +## 🟢 [#574 vehicles-loader-redeploy-per-group-rate-diff] — Пересобрать и задеплоить @stostayer/vehicles-loader на прод клиента с новым кодом per-group rate diff. Код уже на origin/master (запушено 85ca936, 5 коммитов b0e7b58..85ca936). Изменение: rate-группы с неизменённым мультимножеством {standardHours,notes} теперь skip'аются вместо слепого DELETE+INSERT — объём записей в БД 51 203 → 1 963 rate-rows/день (−96%), счётчик replaced=реальные записи, skipped=пропущенные группы. Read-consistency сохранена (изменившиеся группы — та же короткая транзакция). Write-примитив replaceGroup не менялся. Валидировано двумя dry-run против prod (0 ошибок, SG skipped=41089/replaced=1555, SM skipped=5131/replaced=408) + 36 unit-тестов. **Status:** done +**Created:** 2026-07-13 **Where I stopped:** runtime verify GREEN per operator 2026-07-20 — 0.4.1 rate-diff confirmed on prod. **Next action:** (none — kept until merged) **Branch:** n/a @@ -1714,9 +1781,10 @@ Cutover DNS уже сделан (07-02) — тут только смена об --- -## 🟢 [tandemmebel-deploy-drop-fb-tw-gplus-buttons] — Срочный деплой-запрос: убрать с боевого tandemmebel.ru (стек 20) share-кнопки Facebook/Twitter/Google+ (Facebook = экстремистская символика в РФ). Фикс в master victor/tandemmebel.ru как 8df10ee (запушен, template-only, 12 deletions, VK+ОК оставлены). Пересобрать образ tandemmebel на sha 8df10ee (сейчас жив ed96b18/0.42.0, движок не менялся), передеплоить стек 20 (env verbatim 8/8), live-smoke 4 страниц со share-блоком (gallery, blog_post, product_category, product_subcategory) — ВК+ОК на месте, FB/TW/G+ нет, вёрстка цела. Откат = revert 8df10ee. Источник: inbox-письмо от tandemmebel-сессии 2026-07-13T19:21Z. +## 🟢 [#573 tandemmebel-deploy-drop-fb-tw-gplus-buttons] — Срочный деплой-запрос: убрать с боевого tandemmebel.ru (стек 20) share-кнопки Facebook/Twitter/Google+ (Facebook = экстремистская символика в РФ). Фикс в master victor/tandemmebel.ru как 8df10ee (запушен, template-only, 12 deletions, VK+ОК оставлены). Пересобрать образ tandemmebel на sha 8df10ee (сейчас жив ed96b18/0.42.0, движок не менялся), передеплоить стек 20 (env verbatim 8/8), live-smoke 4 страниц со share-блоком (gallery, blog_post, product_category, product_subcategory) — ВК+ОК на месте, FB/TW/G+ нет, вёрстка цела. Откат = revert 8df10ee. Источник: inbox-письмо от tandemmebel-сессии 2026-07-13T19:21Z. **Status:** done +**Created:** 2026-07-13 **Where I stopped:** Deployed 8df10ee to prod stack 20, live-smoke GREEN (vk+ok present, fb/tw/gp=0 on 4 share-block pages), sitemap 184 parity, TLS untouched. Pre-build verification caught letter's stale live-sha claim (ed96b18/0.42.0 → actually 0cd9351/0.42.1); gitea compare confirmed fix on correct 0.42.1 base. Records committed (632c843f). Tandemmebel-сессия confirmed receipt. **Next action:** (none — kept until merged) **Branch:** master @@ -1740,9 +1808,10 @@ Cutover DNS уже сделан (07-02) — тут только смена об --- -## 🟢 [de-vds-3xui-setup] — Поднять VPN-VDS в Германии (Fornex 130.17.17.158, Ubuntu 24.04, 1/2G/20G) — аналог nl-vds-3xui. Повторить проверенный минимальный сетап: 3x-UI + plain VLESS security=none на высоком порту, per-friend UUID. Reality/MTProto/SOCKS5 НЕ ставить (у боевых клиентов из РФ не работали на NL). Креды в pass de-vds-3xui/full-env. Завести entity в вики. Каждое «работает» подтверждать на реальном клиенте. +## 🟢 [#575 de-vds-3xui-setup] — Поднять VPN-VDS в Германии (Fornex 130.17.17.158, Ubuntu 24.04, 1/2G/20G) — аналог nl-vds-3xui. Повторить проверенный минимальный сетап: 3x-UI + plain VLESS security=none на высоком порту, per-friend UUID. Reality/MTProto/SOCKS5 НЕ ставить (у боевых клиентов из РФ не работали на NL). Креды в pass de-vds-3xui/full-env. Завести entity в вики. Каждое «работает» подтверждать на реальном клиенте. **Status:** done +**Created:** 2026-07-14 **Where I stopped:** Real RF-client confirmed working (user is connected through it right now). Plain VLESS 32030 proven again, DE node = live. Per-friend UUIDs + DPI-resistant channel = separate tasks if needed. **Next action:** (none — kept until merged) **Branch:** n/a @@ -1751,9 +1820,10 @@ Cutover DNS уже сделан (07-02) — тут только смена об --- -## 🟢 [kreknin-reenable-backups-after-rebuild] — Вернуть ежедневные бэкапы на vds-kzntsv после завершения ребилда md3 на kreknin. 2026-07-31 бэкапы ЗАГЛУШЕНЫ (не нагружать больной массив): /etc/cron.d/vds-backup (05:00) и /etc/cron.d/books-vds-backup (06:00) закомментированы. Ребилд md3 запущен 2026-08-10 (~4 дня по DSM), системные разделы md0/md1 починены, диски зелёные. После ребилда: 1) btrfs scrub /volume1 (был corruption_errs 1), 2) раскомментировать cron-строки, 3) подтвердить реальный запуск. +## 🟢 [#632 kreknin-reenable-backups-after-rebuild] — Вернуть ежедневные бэкапы на vds-kzntsv после завершения ребилда md3 на kreknin. 2026-07-31 бэкапы ЗАГЛУШЕНЫ (не нагружать больной массив): /etc/cron.d/vds-backup (05:00) и /etc/cron.d/books-vds-backup (06:00) закомментированы. Ребилд md3 запущен 2026-08-10 (~4 дня по DSM), системные разделы md0/md1 починены, диски зелёные. После ребилда: 1) btrfs scrub /volume1 (был corruption_errs 1), 2) раскомментировать cron-строки, 3) подтвердить реальный запуск. **Status:** done +**Created:** 2026-08-10 **Where I stopped:** closed 2026-08-10: backups re-enabled early by user order (rebuild is background/idle-IO). Cron restored vds-kzntsv 0 5 + books-vds 0 6; manual runs VERIFIED on kreknin (66G/15G, latest->08-10, 7 snaps). Remaining btrfs scrub /volume1 tracked in kreknin-repair-md3-rebuild. **Next action:** (none — kept until merged) **Blocker:** kreknin md3 rebuild in progress (~4d ETA, DSM shows optimization) @@ -1763,9 +1833,10 @@ Cutover DNS уже сделан (07-02) — тут только смена об --- -## 🟢 [sched-fnf-01-dir-size-watch] — F&F-тест sched №1 (первый внешний тестер): периодический размер директории → ntfy. Суть: установить sched (пакеты в verdaccio, доки в victor/sched docs/content/docs/), собрать задачу «раз в 6ч посчитать размер каталога (предложен C:\sites\snolla\App_Data, можно свой) и отправить результат в ntfy-топик; при превышении порога — отдельное алерт-сообщение». Путь решения — любой (рекомендуется daemon: admin UI + история ранов — часть теста). Цель теста — проверить, что доки ведут за руку, а код не сыпется: косяки доки/ошибки кода репортить сразу, не чинить самому. +## 🟢 [#677 sched-fnf-01-dir-size-watch] — F&F-тест sched №1 (первый внешний тестер): периодический размер директории → ntfy. Суть: установить sched (пакеты в verdaccio, доки в victor/sched docs/content/docs/), собрать задачу «раз в 6ч посчитать размер каталога (предложен C:\sites\snolla\App_Data, можно свой) и отправить результат в ntfy-топик; при превышении порога — отдельное алерт-сообщение». Путь решения — любой (рекомендуется daemon: admin UI + история ранов — часть теста). Цель теста — проверить, что доки ведут за руку, а код не сыпется: косяки доки/ошибки кода репортить сразу, не чинить самому. **Status:** done +**Created:** 2026-08-16 **Where I stopped:** ACCEPTED (2026-08-16 13:54Z) — репорт 13:21Z → 3 бага дистрибутива починены sched'ом (exports-map @sched/daemon 0.1.2, @sched/ui 0.1.1 serve, токен-бокс). Работа: custom runner dirSizeWatch через createDaemon (стенд `.tmp/sched-fnf-01/`), размер `C:\sites\snolla\App_Data` (37.5 MB) → ntfy `sched-fnf`, алерт при >1MB. Обе ветки проверены (1MB → ⚠️ THRESHOLD EXCEEDED priority 5; 100MB → обычное). Run succeeded + admin UI (ui.png). **Next action:** (закрыта — приёмка получена, таска №2 выдана) **Branch:** n/a @@ -1775,9 +1846,10 @@ Cutover DNS уже сделан (07-02) — тут только смена об --- -## 🟢 [sched-fnf-02-docker-runner] — F&F-тест sched №2: docker runner. Суть: периодический one-shot контейнер через docker runner (read-only проверка, результат/алерт в ntfy), путь — по докам 06.runners/02.docker.md. Проверить: exit code → status, env-мост (data → SCREAMING_SNAKE + SCHED_RUN_ID/SCHED_TASK_NAME), image allowlist, историю ранов в admin UI (теперь @sched/ui sched-ui serve + токен-бокс). Косяки доки/кода — репорт в inbox victor/sched, не чинить самому. +## 🟢 [#678 sched-fnf-02-docker-runner] — F&F-тест sched №2: docker runner. Суть: периодический one-shot контейнер через docker runner (read-only проверка, результат/алерт в ntfy), путь — по докам 06.runners/02.docker.md. Проверить: exit code → status, env-мост (data → SCREAMING_SNAKE + SCHED_RUN_ID/SCHED_TASK_NAME), image allowlist, историю ранов в admin UI (теперь @sched/ui sched-ui serve + токен-бокс). Косяки доки/кода — репорт в inbox victor/sched, не чинить самому. **Status:** done +**Created:** 2026-08-16 **Where I stopped:** ACCEPTED (2026-08-16 14:24Z) — оба замечания стали фичами (validateConfig allowlist на load + ntfy basic auth, core 0.12.2/daemon 0.1.3, сьют 362 ✓). Работа: alpine:3.20 one-shot `wget --spider` pilorama98.ru → ntfy sched-fnf (OK p2 / FAIL p5), succeeded rc=0 / failed exit 1, allowlist busybox → fail-fast, UI sched-ui serve + токен-бокс (ui2.png). **Next action:** (закрыта — приёмка получена, таска №3 выдана) **Branch:** n/a @@ -1787,9 +1859,10 @@ Cutover DNS уже сделан (07-02) — тут только смена об --- -## 🟢 [sched-fnf-03-async-poll] — F&F-тест sched №3: async/poll envelope-протокол. Суть: свой мини-воркер-эндпоинт (HTTP accepted + poll), happy path + опционально timeout-кейс. Путь — по докам 06.runners/01.http.md (envelope) + 05.protocol.md. Проверить: envelope body {task,data} + x-sched-run-id, accepted→queued→poll→running(progress)→succeeded, pollTimeoutMs на hung-run. Косяки — репорт в inbox victor/sched. +## 🟢 [#680 sched-fnf-03-async-poll] — F&F-тест sched №3: async/poll envelope-протокол. Суть: свой мини-воркер-эндпоинт (HTTP accepted + poll), happy path + опционально timeout-кейс. Путь — по докам 06.runners/01.http.md (envelope) + 05.protocol.md. Проверить: envelope body {task,data} + x-sched-run-id, accepted→queued→poll→running(progress)→succeeded, pollTimeoutMs на hung-run. Косяки — репорт в inbox victor/sched. **Status:** done +**Created:** 2026-08-16 **Where I stopped:** ACCEPTED (2026-08-16 14:31Z) — pollTimeoutMs+pollIntervalMs проброшены в daemon 0.1.4 (+CLI --poll-timeout), доки 10.cli.md/05.protocol.md. Работа: воркер :8293 envelope accepted+poll, happy queued→running(50)→succeeded (progress 100, workedMs 3519), timeout hung → failed «poll timeout after 15000ms» (через createEngine). ui3.png. **Next action:** (закрыта — приёмка получена, таска №4 финальная выдана) **Branch:** n/a @@ -1799,9 +1872,10 @@ Cutover DNS уже сделан (07-02) — тут только смена об --- -## 🟢 [sched-fnf-04-embedded-alerts] — F&F-тест sched №4 (финальная): embedded-режим. Суть: createEngine + createInternalRunner + createSqliteStorage + syncTasks в одном Node-скрипте; реальная проверка свежести бэкапов (возраст последнего снапшота — kreknin/бэкап-каталог), stale → алерт через createAlerts (ntfy basic auth работает), retries (maxAttempts/backoff) на транзиентные ошибки. Доки: 02.quick-start §1, 06.runners/04.internal.md, 03.tasks.md, 11.self-hosting.md. +## 🟢 [#682 sched-fnf-04-embedded-alerts] — F&F-тест sched №4 (финальная): embedded-режим. Суть: createEngine + createInternalRunner + createSqliteStorage + syncTasks в одном Node-скрипте; реальная проверка свежести бэкапов (возраст последнего снапшота — kreknin/бэкап-каталог), stale → алерт через createAlerts (ntfy basic auth работает), retries (maxAttempts/backoff) на транзиентные ошибки. Доки: 02.quick-start §1, 06.runners/04.internal.md, 03.tasks.md, 11.self-hosting.md. **Status:** done +**Created:** 2026-08-16 **Where I stopped:** F&F-цикл закрыт 2026-08-16: 4/4 таски приняты, 6 багов → 6 фич (exports-map, @sched/ui serve+токен-бокс, validateConfig, ntfy basic, pollTimeoutMs, /api/health) **Next action:** (none — kept until merged) **Branch:** n/a @@ -1811,9 +1885,10 @@ Cutover DNS уже сделан (07-02) — тут только смена об --- -## 🟢 [sched-fnf-02-docker-runner] — F&F-тест sched №2 (после приёмки №1): docker runner — периодический one-shot контейнер. Раз в 6ч запускать контейнер с read-only проверкой (например alpine du -sh по каталогу через bind-mount, или rclone stat бакета variant-cache на MinIO), результат/алерт — в ntfy-топик sched-fnf. Учит: docker runner (доки 06.runners/02.docker.md), image allowlist, exit codes, retries. Косяки доки/кода — репортить в inbox victor/sched сразу, не чинить. +## 🟢 [#679 sched-fnf-02-docker-runner] — F&F-тест sched №2 (после приёмки №1): docker runner — периодический one-shot контейнер. Раз в 6ч запускать контейнер с read-only проверкой (например alpine du -sh по каталогу через bind-mount, или rclone stat бакета variant-cache на MinIO), результат/алерт — в ntfy-топик sched-fnf. Учит: docker runner (доки 06.runners/02.docker.md), image allowlist, exit codes, retries. Косяки доки/кода — репортить в inbox victor/sched сразу, не чинить. **Status:** 🟢 done +**Created:** 2026-08-16 **Where I stopped:** (закрыто как дубль — работа принята в основном 🟢-блоке, F&F цикл закрыт 2026-08-16) **Next action:** прочитать docs/content/docs/06.runners/02.docker.md; собрать задачу runner=docker (one-shot контейнер, read-only проверка → ntfy); прогнать; проверить run history + exit codes/retries; репорт в inbox victor/sched (коммит в .agents/inbox/) **Branch:** n/a @@ -1822,9 +1897,10 @@ Cutover DNS уже сделан (07-02) — тут только смена об --- -## 🟢 [sched-fnf-03-async-poll] — F&F-тест sched №3 (после приёмки №2): async/poll протокол. Свой мини-воркер-эндпоинт (HTTP): принимает задачу (202 accepted + statusUrl), завершает через N секунд; sched-задача в envelope-режиме http-runner поллит statusUrl до терминального статуса. Проверить: историю ранов (queued → running → succeeded), контракт PollResult; опционально — poll-timeout кейс (воркер, который не завершается, на embedded-движке с коротким pollTimeoutMs). Учит: 06.runners/01.http.md (envelope mode), 05.protocol.md, poll-цикл движка. Косяки доки/кода — репортить в inbox victor/sched сразу, не чинить. +## 🟢 [#681 sched-fnf-03-async-poll] — F&F-тест sched №3 (после приёмки №2): async/poll протокол. Свой мини-воркер-эндпоинт (HTTP): принимает задачу (202 accepted + statusUrl), завершает через N секунд; sched-задача в envelope-режиме http-runner поллит statusUrl до терминального статуса. Проверить: историю ранов (queued → running → succeeded), контракт PollResult; опционально — poll-timeout кейс (воркер, который не завершается, на embedded-движке с коротким pollTimeoutMs). Учит: 06.runners/01.http.md (envelope mode), 05.protocol.md, poll-цикл движка. Косяки доки/кода — репортить в inbox victor/sched сразу, не чинить. **Status:** 🟢 done +**Created:** 2026-08-16 **Where I stopped:** (закрыто как дубль — работа принята в основном 🟢-блоке, F&F цикл закрыт 2026-08-16) **Next action:** прочитать docs 06.runners/01.http.md (envelope mode) + 05.protocol.md; написать мини-воркер (accepted + statusUrl + отложенное завершение); собрать задачу с envelope:true; прогнать; проверить queued→running→succeeded и poll-историю; опционально timeout-кейс; репорт в inbox victor/sched (коммит в .agents/inbox/) **Branch:** n/a @@ -1833,9 +1909,10 @@ Cutover DNS уже сделан (07-02) — тут только смена об --- -## 🟢 [sched-fnf-04-embedded-alerts] — F&F-тест sched №4 (финальный, после приёмки №3): embedded-режим без daemon. Один Node-скрипт: createEngine + createInternalRunner + createSqliteStorage + syncTasks (внутренний хендлер в процессе). Реальная проверка из твоей инфры: свежесть бэкапов (возраст последнего снапшота/бэкап-файла — kreknin или бэкап-каталог, SSH/локально на выбор), если stale → алерт через createAlerts (ntfy basic user+password теперь поддержан, или email). Retries (maxAttempts/backoff) на транзиентные ошибки. Учит: 02.quick-start §1 (embedded), 06.runners/04.internal.md, 03.tasks.md (retry), 11.self-hosting.md (alerts/onRunFinal). Косяки доки/кода — репортить в inbox victor/sched сразу, не чинить. +## 🟢 [#683 sched-fnf-04-embedded-alerts] — F&F-тест sched №4 (финальный, после приёмки №3): embedded-режим без daemon. Один Node-скрипт: createEngine + createInternalRunner + createSqliteStorage + syncTasks (внутренний хендлер в процессе). Реальная проверка из твоей инфры: свежесть бэкапов (возраст последнего снапшота/бэкап-файла — kreknin или бэкап-каталог, SSH/локально на выбор), если stale → алерт через createAlerts (ntfy basic user+password теперь поддержан, или email). Retries (maxAttempts/backoff) на транзиентные ошибки. Учит: 02.quick-start §1 (embedded), 06.runners/04.internal.md, 03.tasks.md (retry), 11.self-hosting.md (alerts/onRunFinal). Косяки доки/кода — репортить в inbox victor/sched сразу, не чинить. **Status:** 🟢 done +**Created:** 2026-08-16 **Where I stopped:** (закрыто как дубль — работа принята в основном 🟢-блоке, F&F цикл закрыт 2026-08-16) **Next action:** прочитать docs 02.quick-start §1 (embedded) + 06.runners/04.internal.md + 03.tasks.md (retry) + 11.self-hosting.md (alerts); написать embedded-скрипт (внутренний хендлер: проверка свежести бэкапа; retries; createAlerts на onRunFinal); прогнать happy path + stale-кейс; репорт в inbox victor/sched (коммит в .agents/inbox/) **Branch:** n/a @@ -1844,9 +1921,10 @@ Cutover DNS уже сделан (07-02) — тут только смена об --- -## 🟢 [sched-fnf-round2-docs-review] — F&F sched раунд 2: ревью доков после storage-сплита (core 0.13.0, адаптеры 0.1.0). Проверено живьём (стенд .admin/.tmp/sched-r2/): 3 косяка в репорт — (1) @sched/core/storage-contract subpath нерабочий (vitest не в deps core, ERR_MODULE_NOT_FOUND), (2) quick-start §1 embedded-пример на CJS против ESM-only пакета (мёртв с первой команды), (3) §2.4 обещает result {greeting}, legacy http-runner отдаёт result:null. Мелочи: CLI игнорит неизвестные флаги; sched shim в git-bash сломан. Гладкое: storage-сплит чистый, MySQL-адаптер живьём против MySQL 8.4, 13.ui delete/токен-бокс, --poll-timeout. Репорт: victor/sched/.agents/inbox/2026-08-16T17-40-09Z-admin-fnf-round2-review.md. +## 🟢 [#684 sched-fnf-round2-docs-review] — F&F sched раунд 2: ревью доков после storage-сплита (core 0.13.0, адаптеры 0.1.0). Проверено живьём (стенд .admin/.tmp/sched-r2/): 3 косяка в репорт — (1) @sched/core/storage-contract subpath нерабочий (vitest не в deps core, ERR_MODULE_NOT_FOUND), (2) quick-start §1 embedded-пример на CJS против ESM-only пакета (мёртв с первой команды), (3) §2.4 обещает result {greeting}, legacy http-runner отдаёт result:null. Мелочи: CLI игнорит неизвестные флаги; sched shim в git-bash сломан. Гладкое: storage-сплит чистый, MySQL-адаптер живьём против MySQL 8.4, 13.ui delete/токен-бокс, --poll-timeout. Репорт: victor/sched/.agents/inbox/2026-08-16T17-40-09Z-admin-fnf-round2-review.md. **Status:** 🟢 done +**Created:** 2026-08-16 **Where I stopped:** DONE (2026-08-16) — ревью отправлено (17:40Z). 3 косяка + 2 мелочи. **Дополнение 17:48Z — критичный баг UI sched-runs**: статусы врут (сдвиг на 1 при обновлении списка, не-keyed Lit + sched-status не перерисовывается по атрибуту); воспроизведено 11/17 строк; репорт `victor/sched/.agents/inbox/2026-08-16T17-48-12Z-admin-fnf-round2-ui-status-bug.md`. **Перепроверка 18:08Z**: 5/5 фиксов core 0.14.0/daemon 0.1.5 приняты (contract subpath без vitest, embed.mjs ESM, legacy result {greeting}, CLI strict, git-bash shim); UI status-desync ЖИВ (33/50 строк) — письмо `2026-08-16T18-08-55Z-admin-fnf-round2-recheck.md`, ждём фикс. **ФИНАЛ 18:39Z**: UI-фикс подтверждён на @sched/ui 0.1.3 (keyed list, 0/50 после 4 сдвигов); 0.1.2 ушёл со stale-dist (md5==0.1.1, их гоча снова выстрелила; prepack 75dd146 закрывает). Раунд 2 полностью закрыт. **Next action:** ждать приёмки/фиксов sched → перепроверить по пингу. **Branch:** n/a @@ -1855,7 +1933,7 @@ Cutover DNS уже сделан (07-02) — тут только смена об --- -## 🟢 [stostayer-web-deploy-content-api-fix] — Раскатать на прод www.stostayer.ru фикс content-api (commit c43d168, @stostayer/api 0.1.2) — вернуть SEO-тексты из MinIO на /auto/*, /remont/*, /diagnostika/*. +## 🟢 [#685 stostayer-web-deploy-content-api-fix] — Раскатать на прод www.stostayer.ru фикс content-api (commit c43d168, @stostayer/api 0.1.2) — вернуть SEO-тексты из MinIO на /auto/*, /remont/*, /diagnostika/*. Контекст: /api/content отдавал 500 на ВСЕХ путях (прогер подтвердил curl'ом) → SEO-тексты из seo-бакета не рендерились ни в легаси, ни в web4. Root cause (найден прогером): 1. region 'local' в конфигах (api/web/web4) — MinIO minio-api.stostayer.ru настроен на us-west-1 → AuthorizationHeaderMalformed 400 → listDirectFiles кидал → весь route 500. @@ -1874,6 +1952,7 @@ Verify: Rollback: откат конфига (region обратно 'local' не надо — вернуть прежний образ стека) либо реверт c43d168. **Status:** done +**Created:** 2026-08-16 **Where I stopped:** DONE 2026-08-16: деплой c43d168 вскрыл node16 getRandomValues → решено node18 (0.3.26) + region us-west-1 в хостовом конфиге; SEO-тексты рендерятся (проверено байт-в-байт с их verify); закрыта в составе node18-rebuild **Next action:** (none — kept until merged) **Branch:** master @@ -1883,7 +1962,7 @@ Rollback: откат конфига (region обратно 'local' не надо --- -## 🟢 [stostayer-web-deploy-content-api-fix-dup] — [ДУБЛЬ] Раскатать на прод www.stostayer.ru фикс content-api (commit c43d168, @stostayer/api 0.1.2) — вернуть SEO-тексты из MinIO на /auto/*, /remont/*, /diagnostika/*. +## 🟢 [#686 stostayer-web-deploy-content-api-fix-dup] — [ДУБЛЬ] Раскатать на прод www.stostayer.ru фикс content-api (commit c43d168, @stostayer/api 0.1.2) — вернуть SEO-тексты из MinIO на /auto/*, /remont/*, /diagnostika/*. Дубликат задачи stostayer-web-deploy-content-api-fix (создана на случай потери при сбоях MCP). Если оригинал ещё жив — взять её, эту закрыть как дубль. @@ -1904,6 +1983,7 @@ Verify: Rollback: вернуть прежний образ стека либо реверт c43d168. **Status:** done +**Created:** 2026-08-16 **Where I stopped:** duplicate — работа выполнена в stostayer-web-node18-rebuild (close 18:55Z) **Next action:** (none — kept until merged) **Branch:** master @@ -1913,9 +1993,10 @@ Rollback: вернуть прежний образ стека либо реве --- -## 🟢 [stostayer-web-node18-rebuild] — Пересборка образа stostayer-web 0.3.25: Dockerfile node:16→node:18 + фикс прод-бага /api/content 500 (S3 v3 getRandomValues на node16). Полифилл уже в master victor/stostayer.new (2b52df0, api 0.1.3, 14/14 тестов) — чинит на любом node≥16; бамп базы — корневой фикс (node16 EOL 2023-09-11). Верифицировано: node18.20.8 globalThis.crypto.getRandomValues = function. Подробности: письмо в .admin inbox 2026-08-16T18-14-54Z-stostayer.new.md. Acceptance: (1) packages/web/Dockerfile FROM node:18 (+ NODE_OPTIONS=--openssl-legacy-provider в build-шаг если nuxt build упадёт ERR_OSSL_EVP_UNSUPPORTED/md4 — образец huild-скрипт); (2) локальный docker build зелёный (populated .yarn/cache, VERDACCIO_TOKEN); (3) образ 0.3.25 задеплоен, легаси www.stostayer.ru жив; (4) MinIO read+write verify (smithy-pin 5.3.x = гипотеза до железа) + регресс /api/content: /audi /check-engine /auto /audi 200 + SEO-тексты рендерятся. Границы: endpoint minio-api.stostayer.ru и креды stayer_minio НЕ трогать; content-empty-path-404-return и деплой web4 — вне скоупа. +## 🟢 [#687 stostayer-web-node18-rebuild] — Пересборка образа stostayer-web 0.3.25: Dockerfile node:16→node:18 + фикс прод-бага /api/content 500 (S3 v3 getRandomValues на node16). Полифилл уже в master victor/stostayer.new (2b52df0, api 0.1.3, 14/14 тестов) — чинит на любом node≥16; бамп базы — корневой фикс (node16 EOL 2023-09-11). Верифицировано: node18.20.8 globalThis.crypto.getRandomValues = function. Подробности: письмо в .admin inbox 2026-08-16T18-14-54Z-stostayer.new.md. Acceptance: (1) packages/web/Dockerfile FROM node:18 (+ NODE_OPTIONS=--openssl-legacy-provider в build-шаг если nuxt build упадёт ERR_OSSL_EVP_UNSUPPORTED/md4 — образец huild-скрипт); (2) локальный docker build зелёный (populated .yarn/cache, VERDACCIO_TOKEN); (3) образ 0.3.25 задеплоен, легаси www.stostayer.ru жив; (4) MinIO read+write verify (smithy-pin 5.3.x = гипотеза до железа) + регресс /api/content: /audi /check-engine /auto /audi 200 + SEO-тексты рендерятся. Границы: endpoint minio-api.stostayer.ru и креды stayer_minio НЕ трогать; content-empty-path-404-return и деплой web4 — вне скоупа. **Status:** done +**Created:** 2026-08-16 **Where I stopped:** DONE 2026-08-16: node16→node18 (Dockerfile 83d0fd0) + ipv4first (664fb77), образы 0.3.25/0.3.26, прод live; /api/content verify зелёный (audi 6179B html / check-engine 4860B / auto/audi {} 200, регресс / 200); MinIO read SigV4 us-west-1 подтверждён (smithy-pin факт); region в хостовом конфиге поправлен (бэкап .bak-20260816); приёмка stostayer.new 18:53Z **Next action:** (none — kept until merged) **Branch:** n/a @@ -1925,9 +2006,10 @@ Rollback: вернуть прежний образ стека либо реве --- -## 🟢 [sched-fnf-r7] — F&F раунд 7: T1–T5 на живом демоне — runtime-task-api (POST /tasks, POST /tasks/:name/schedule), run-cancel (POST /runs/:id/cancel, DELETE active → 409), live-sync-recovery (sync 60с без рестарта, ghost-recovery, битый tasks.json), run-log-offset (GET /runs/:id?logFromOffset=N), lock-heartbeat (--lock-heartbeat, долгий sync-ран > lockTtl) + chore: RunnerRunHooks из @sched/core, typecheck-гейт чистый. Версии (после репаблиша): core 0.34.0 / daemon 0.1.28 / mcp 0.3.9 / storage-* 0.2.9 / ui 0.1.13 (sched подтвердил 20:57Z). Чек-листы и формат репорта — в пинг-письме. +## 🟢 [#705 sched-fnf-r7] — F&F раунд 7: T1–T5 на живом демоне — runtime-task-api (POST /tasks, POST /tasks/:name/schedule), run-cancel (POST /runs/:id/cancel, DELETE active → 409), live-sync-recovery (sync 60с без рестарта, ghost-recovery, битый tasks.json), run-log-offset (GET /runs/:id?logFromOffset=N), lock-heartbeat (--lock-heartbeat, долгий sync-ран > lockTtl) + chore: RunnerRunHooks из @sched/core, typecheck-гейт чистый. Версии (после репаблиша): core 0.34.0 / daemon 0.1.28 / mcp 0.3.9 / storage-* 0.2.9 / ui 0.1.13 (sched подтвердил 20:57Z). Чек-листы и формат репорта — в пинг-письме. **Status:** done +**Created:** 2026-08-17 **Where I stopped:** DONE 2026-08-17: стенд `sched-r2/r7` на новых версиях (verdaccio latest). Репорт 21:17Z в инбокс victor/sched (`.agents/inbox/2026-08-17T21-17-00Z-admin-fnf-r7-report.md`). **91/92 PASS + находки F1, F2.** Приёмка r6 (D1/F3/F5/F10) тоже в этом письме: 16/16 PASS. T1 runtime-task-api 23/24 (F1: runtime-таска дизаблится следующим syncTasks — пинг «НЕ дизаблятся» не прав; 09.admin-api.md «disabled by the next sync» + код правы; 03.tasks.md «on the next daemon start» вводит в заблуждение — live-sync дизаблит тоже). T2 run-cancel 17/17 (cancel реально убивает воркер — проверено по tasklist). 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-ран > lockTtl жив, второй инстанс ре-диспатчит без параллели) + **F2: валидации `--lock-heartbeat < --lock-ttl` НЕТ — демон стартует при 20/20 и 30/20, пинг/доки 12.cli+04.runs обещают отказ** (в engine/cli нигде не проверяется). T6 chore 3/3 (RunnerRunHooks type-only из published, tsc exit 0). **Next action:** ждём разбор r7 (F1/F2 → фичи) + приёмку. Стенд-репро: `cd .admin/.tmp/sched-r2/r7 && node t{1..6}*.mjs && node r6-acceptance.mjs`. **Branch:** n/a @@ -1937,7 +2019,7 @@ Rollback: вернуть прежний образ стека либо реве --- -## 🟢 [sched-admin-client-openapi] — Цель: написать admin-клиент для sched на ЛЮБОМ другом языке (не TypeScript/JavaScript) строго по OpenAPI-спеке @sched/admin-api, проверить живым smoke по методике F&F. Это живая проверка claim'а openapi-spec: «клиент на любом ЯП одной командой». Клиент пишется В РЕПО SCHED (C:\Users\vitya\projects\sched, папка examples/admin-client-/, по образцу examples/workers/), ПУШ РАЗРЕШЁН — решение vitya 2026-08-18 (явное исключение из fnf-testing-procedure «внешний агент не пушит в наш репо»). +## 🟢 [#706 sched-admin-client-openapi] — Цель: написать admin-клиент для sched на ЛЮБОМ другом языке (не TypeScript/JavaScript) строго по OpenAPI-спеке @sched/admin-api, проверить живым smoke по методике F&F. Это живая проверка claim'а openapi-spec: «клиент на любом ЯП одной командой». Клиент пишется В РЕПО SCHED (C:\Users\vitya\projects\sched, папка examples/admin-client-/, по образцу examples/workers/), ПУШ РАЗРЕШЁН — решение vitya 2026-08-18 (явное исключение из fnf-testing-procedure «внешний агент не пушит в наш репо»). ## Acceptance criteria 1. Язык — любой, кроме TS/JS (свободный выбор: Python/Go/C#/Java/…). Путь свободный: сгенерированный (openapi-generator/swagger-codegen/lang-tool) ИЛИ рукописный — но источник истины ОДИН: openapi.json (subpath-export @sched/admin-api/openapi.json, опубликован 0.1.2 в verdaccio; в репо: packages/admin-api/openapi.json), не чтение нашего TS-исходника. @@ -1960,6 +2042,7 @@ Rollback: вернуть прежний образ стека либо реве **notify:** victor/sched **Status:** done +**Created:** 2026-08-18 **Where I stopped:** ЗАКРЫТА 2026-08-18 — Python-клиент одной командой (openapi-generator 7.24.0, JDK 25) из спеки verdaccio 0.1.2, 15 путей/21 операция: сьют 24/24, smoke 19/19 против published daemon 0.3.4 (:8127). Код sched/examples/admin-client-python (e89e868). Репорт F&F — sched инбокс 19:15Z, находки F-1..F-5 (спеку не чинил). **Next action:** (none — kept until merged) **Branch:** n/a @@ -1969,9 +2052,10 @@ Rollback: вернуть прежний образ стека либо реве --- -## 🟡 [sched-vds-deploy] — Деплой sched (демон + web UI) на VDS на постоянку, без воркеров/задач. Решения (2026-08-21): поддомен sched.vds.kzntsv.site (websecure + letsEncrypt, сеть proxy); basic auth на web-морде через middleware traefik sched-auth@file (логин vitya, пароль — pass: sched/basic-auth, НЕ в git); daemon admin API наружу не выставлять (shell проксирует внутри сети); SCHED_ADMIN_KEY из секретов (pass: sched/admin-key при деплое); prod-compose: daemon+shell, volume для sched.db + бэкап-политика, restart unless-stopped; образ registry.kzntsv.site/sched:; деплой стека через Portainer (traefik/portainer — ad-hoc). Проверка после деплоя: curl -u → 401/200; UI открывается; БД персистентна. СКОУП: только sched, билдеры apilki НЕ входят (их деплой — отдельные задачи). +## 🟡 [#790 sched-vds-deploy] — Деплой sched (демон + web UI) на VDS на постоянку, без воркеров/задач. Решения (2026-08-21): поддомен sched.vds.kzntsv.site (websecure + letsEncrypt, сеть proxy); basic auth на web-морде через middleware traefik sched-auth@file (логин vitya, пароль — pass: sched/basic-auth, НЕ в git); daemon admin API наружу не выставлять (shell проксирует внутри сети); SCHED_ADMIN_KEY из секретов (pass: sched/admin-key при деплое); prod-compose: daemon+shell, volume для sched.db + бэкап-политика, restart unless-stopped; образ registry.kzntsv.site/sched:; деплой стека через Portainer (traefik/portainer — ad-hoc). Проверка после деплоя: curl -u → 401/200; UI открывается; БД персистентна. СКОУП: только sched, билдеры apilki НЕ входят (их деплой — отдельные задачи). **Status:** paused +**Created:** 2026-08-21 **Where I stopped:** Пауза по решению vitya (2026-08-21): ждём результатов миграции books→sched (victor/books sched-migration-phase-3). VDS-деплой sched решается по итогам — см. также victor/sched books-sched-integration (paused). Не запускать до отмашки. **Next action:** (1) prod-compose (daemon+shell, label-ы traefik, certresolver, port 8081), creds в pass (sched/basic-auth есть; sched/admin-key); (2) образ в registry.kzntsv.site/sched:; (3) деплой через Portainer; (4) dynamic/ traefik: middleware sched-auth (bcrypt из pass), релоад; (5) curl-проверка 401/200 + браузер; (6) отчёт. Перед подключением воркеров — HTTP-runner-таски sched (envelope-violation прежде всего). **Branch:** n/a @@ -1980,9 +2064,10 @@ Rollback: вернуть прежний образ стека либо реве --- -## ⚪ [apitano-engine-deploy] — Блок 5 дизайна apitano-engine: деплой на VDS. docker-compose + traefik-роут (инфра уже стоит: traefik, verdaccio, registry), модель — в образе/при старте. Свежесть human-gated: свежий граб → `ingest` → индекс обновлён (без расписаний). Ops-acceptance: docker stack на VDS, traefik labels, smoke против внешнего URL. +## ⚪ [#802 apitano-engine-deploy] — Блок 5 дизайна apitano-engine: деплой на VDS. docker-compose + traefik-роут (инфра уже стоит: traefik, verdaccio, registry), модель — в образе/при старте. Свежесть human-gated: свежий граб → `ingest` → индекс обновлён (без расписаний). Ops-acceptance: docker stack на VDS, traefik labels, smoke против внешнего URL. **Status:** ready +**Created:** 2026-08-22 **Where I stopped:** (not started) **Next action:** После 🟢 code-тасок (engine-ingest/index/retrieve/mcp-server). Собрать образ движка, поднять на VDS в docker-стеке с traefik-роутом, проверить smoke (healthcheck, авторизация, реальный запрос search_docs). Домен/URL — в связке с оператором. **Branch:** n/a @@ -1990,7 +2075,7 @@ Rollback: вернуть прежний образ стека либо реве --- -## ⚪ [tasks-migration-47-projects] — Ops: миграция всех проектов на формат .tasks v2 (этап 3 решения tasks-global-numbering). +## ⚪ [#806 tasks-migration-47-projects] — Ops: миграция всех проектов на формат .tasks v2 (этап 3 решения tasks-global-numbering). По всем 47 проектам с .tasks/: собрать ВСЕ задачи (блоки STATUS.md + per-task файлы; задачи без файла тоже нумеруются); дата создания по приоритету: git log --diff-filter=A файла → самое раннее упоминание слага в истории STATUS.md → created-by коммент → mtime; глобальная нумерация 1..N по датам; rename `yyyy-mm-dd--.md`; закрытые (🟢) → `.tasks/done/`; переписать STATUS.md (шапки `[#n slug]`, `**Created:**`, блокеры слаги→номера); `counter=N` в agenda; коммиты по проектам; sync MCP-кэша. @@ -2006,16 +2091,17 @@ Rollback: вернуть прежний образ стека либо реве **notify:** OpeItcLoc03/workshop **Status:** ready +**Created:** 2026-08-22 **Where I stopped:** ОСТАНОВЛЕНА ПОЛЬЗОВАТЕЛЕМ для корректировки (2026-08-22 ~23:55Z). Сделано: скрипт .admin/scripts/migrate-tasks-v2.mjs (collect/apply/verify, даты из git-истории, детерминизм через план-файл /tmp/tasks-v2-plan.json); смок применён+проверен парсером MCP на 3 репо (snolla/.admin/artmone.pro) — ЛОКАЛЬНЫЕ коммиты, не запушены (откат: git reset --hard HEAD~1). Полный collect: 52 репо, 803 задачи (расхождение с инвентаризацией 839 — проверить). НЕ сделано: apply полный, agenda (ремоут, .agenda-migration-tmp удалён), counter=N, sync кэша. Найдено перед apply: (1) fileForSlug не видит файлы в .tasks/done/ — при перенумерации разъедутся имена с шапками; (2) расхождение счёта 803 vs 839. План: /tmp/full-plan.txt. **Next action:** Написать скрипт миграции, smoke на 3 проектах (большой/средний/маленький), полный прогон, counter=N, коммиты. -**Blocker:** tasks-counter-and-numbering, tasks-counter-bootstrap-deploy +**Blocker:** #807, #805 **Branch:** n/a **Notify:** OpeItcLoc03/workshop --- -## 🟢 [tasks-counter-bootstrap-deploy] — Ops: bootstrap счётчика + деплой MCP (этап 1b решения tasks-global-numbering). +## 🟢 [#805 tasks-counter-bootstrap-deploy] — Ops: bootstrap счётчика + деплой MCP (этап 1b решения tasks-global-numbering). Создать файл `task-counter` в `OpeItcLoc03/agenda` (init = 0 до миграции, согласовать с таской миграции), деплой обновлённого projects-meta-mcp, smoke: tasks_create создаёт задачу с номером в шапке/имени/Created:. @@ -2031,9 +2117,10 @@ Rollback: вернуть прежний образ стека либо реве **notify:** OpeItcLoc03/workshop **Status:** done +**Created:** 2026-08-22 **Where I stopped:** task-counter=1 создан в OpeItcLoc03/agenda (bootstrap), MCP v2.37.0 уже задеплоен, smoke-create #1 прошёл (шапка/имя/Created: v2-формат). Блокер снят. **Next action:** (none — kept until merged) -**Blocker:** tasks-counter-and-numbering +**Blocker:** #807 **Branch:** n/a **Notify:** OpeItcLoc03/workshop diff --git a/.tasks/iis-migration-to-ruvds.md b/.tasks/done/2026-05-22-00195-iis-migration-to-ruvds.md similarity index 100% rename from .tasks/iis-migration-to-ruvds.md rename to .tasks/done/2026-05-22-00195-iis-migration-to-ruvds.md diff --git a/.tasks/books-vds-backup-daily-kreknin.md b/.tasks/done/2026-05-25-00231-books-vds-backup-daily-kreknin.md similarity index 100% rename from .tasks/books-vds-backup-daily-kreknin.md rename to .tasks/done/2026-05-25-00231-books-vds-backup-daily-kreknin.md diff --git a/.tasks/books-vds-stacks-to-portainer.md b/.tasks/done/2026-05-25-00232-books-vds-stacks-to-portainer.md similarity index 100% rename from .tasks/books-vds-stacks-to-portainer.md rename to .tasks/done/2026-05-25-00232-books-vds-stacks-to-portainer.md diff --git a/.tasks/migrate-elasticsearch-to-books-vds.md b/.tasks/done/2026-05-25-00233-migrate-elasticsearch-to-books-vds.md similarity index 100% rename from .tasks/migrate-elasticsearch-to-books-vds.md rename to .tasks/done/2026-05-25-00233-migrate-elasticsearch-to-books-vds.md diff --git a/.tasks/unify-backup-notifications.md b/.tasks/done/2026-05-25-00235-unify-backup-notifications.md similarity index 100% rename from .tasks/unify-backup-notifications.md rename to .tasks/done/2026-05-25-00235-unify-backup-notifications.md diff --git a/.tasks/books-ops-mcp-host-promote.md b/.tasks/done/2026-05-26-00249-books-ops-mcp-host-promote.md similarity index 100% rename from .tasks/books-ops-mcp-host-promote.md rename to .tasks/done/2026-05-26-00249-books-ops-mcp-host-promote.md diff --git a/.tasks/bookva-tenant-cutover-prep.md b/.tasks/done/2026-05-26-00250-bookva-tenant-cutover-prep.md similarity index 100% rename from .tasks/bookva-tenant-cutover-prep.md rename to .tasks/done/2026-05-26-00250-bookva-tenant-cutover-prep.md diff --git a/.tasks/modulair-rag-vds-redeploy.md b/.tasks/done/2026-05-27-00251-modulair-rag-vds-redeploy.md similarity index 100% rename from .tasks/modulair-rag-vds-redeploy.md rename to .tasks/done/2026-05-27-00251-modulair-rag-vds-redeploy.md diff --git a/.tasks/restore-elasticsearch-indices-books-vds.md b/.tasks/done/2026-05-28-00256-restore-elasticsearch-indices-books-vds.md similarity index 100% rename from .tasks/restore-elasticsearch-indices-books-vds.md rename to .tasks/done/2026-05-28-00256-restore-elasticsearch-indices-books-vds.md diff --git a/.tasks/vehicles-loader-progress-deploy.md b/.tasks/done/2026-05-30-00277-vehicles-loader-progress-deploy.md similarity index 100% rename from .tasks/vehicles-loader-progress-deploy.md rename to .tasks/done/2026-05-30-00277-vehicles-loader-progress-deploy.md diff --git a/.tasks/morethencms-s3-filestorage-provider.md b/.tasks/done/2026-07-03-00520-morethencms-s3-filestorage-provider.md similarity index 100% rename from .tasks/morethencms-s3-filestorage-provider.md rename to .tasks/done/2026-07-03-00520-morethencms-s3-filestorage-provider.md diff --git a/.tasks/tandemmebel-deploy-snolla-0-42-0.md b/.tasks/done/2026-07-04-00541-tandemmebel-deploy-snolla-0-42-0.md similarity index 100% rename from .tasks/tandemmebel-deploy-snolla-0-42-0.md rename to .tasks/done/2026-07-04-00541-tandemmebel-deploy-snolla-0-42-0.md diff --git a/.tasks/kupimknigi-deploy-snolla-0-42-0.md b/.tasks/done/2026-07-05-00559-kupimknigi-deploy-snolla-0-42-0.md similarity index 100% rename from .tasks/kupimknigi-deploy-snolla-0-42-0.md rename to .tasks/done/2026-07-05-00559-kupimknigi-deploy-snolla-0-42-0.md diff --git a/.tasks/on-snolla-vds-migration.md b/.tasks/done/2026-07-20-00578-on-snolla-vds-migration.md similarity index 100% rename from .tasks/on-snolla-vds-migration.md rename to .tasks/done/2026-07-20-00578-on-snolla-vds-migration.md diff --git a/.tasks/snolla-local-admin-restore.md b/.tasks/done/2026-07-20-00579-snolla-local-admin-restore.md similarity index 100% rename from .tasks/snolla-local-admin-restore.md rename to .tasks/done/2026-07-20-00579-snolla-local-admin-restore.md diff --git a/.tasks/snolla-mailer-per-recipient-send.md b/.tasks/done/2026-08-14-00657-snolla-mailer-per-recipient-send.md similarity index 100% rename from .tasks/snolla-mailer-per-recipient-send.md rename to .tasks/done/2026-08-14-00657-snolla-mailer-per-recipient-send.md diff --git a/.tasks/snolla-smtp-send-monitor-vds.md b/.tasks/done/2026-08-14-00658-snolla-smtp-send-monitor-vds.md similarity index 100% rename from .tasks/snolla-smtp-send-monitor-vds.md rename to .tasks/done/2026-08-14-00658-snolla-smtp-send-monitor-vds.md diff --git a/.tasks/llm-router-failover-proxy.md b/.tasks/done/2026-08-16-00676-llm-router-failover-proxy.md similarity index 100% rename from .tasks/llm-router-failover-proxy.md rename to .tasks/done/2026-08-16-00676-llm-router-failover-proxy.md diff --git a/.tasks/sched-pipelines-local-stack.md b/.tasks/done/2026-08-20-00746-sched-pipelines-local-stack.md similarity index 100% rename from .tasks/sched-pipelines-local-stack.md rename to .tasks/done/2026-08-20-00746-sched-pipelines-local-stack.md