From 9d6befdffe676e1cc0bc3b7934399271f50b10c4 Mon Sep 17 00:00:00 2001 From: vitya Date: Tue, 25 Aug 2026 22:07:33 +0300 Subject: [PATCH] =?UTF-8?q?chore(meta):=20file-based=20meta=20channel=20cl?= =?UTF-8?q?osed=20(canon=3Dmappa)=20=E2=80=94=20stubs=20.wiki/.tasks/.agen?= =?UTF-8?q?ts-inbox=20+=20AGENTS.md=20cleanup=20(2026-08-25)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .agents/inbox/README.md | 5 + .tasks/NEXT_SESSION.md | 32 +- .tasks/STATUS.md | 2260 +-------------------------------------- .wiki/CLAUDE.md | 25 +- .wiki/index.md | 120 +-- .wiki/log.md | 158 +-- .wiki/overview.md | 27 +- 7 files changed, 17 insertions(+), 2610 deletions(-) create mode 100644 .agents/inbox/README.md diff --git a/.agents/inbox/README.md b/.agents/inbox/README.md new file mode 100644 index 0000000..000f6c9 --- /dev/null +++ b/.agents/inbox/README.md @@ -0,0 +1,5 @@ +# ⛔ Файловый инбокс закрыт + +**Не читать. Не править.** Канал почты — mappa (`mcp__mappa__inbox_*`): письма = inbox-сущности проекта. Скилы: `mappa-messaging`, `mappa-session-orient` (raise on start). + +Файлы ниже — легаси-история (файловый канал закрыт решением 2026-08-25). diff --git a/.tasks/NEXT_SESSION.md b/.tasks/NEXT_SESSION.md index c976c2f..8cf6645 100644 --- a/.tasks/NEXT_SESSION.md +++ b/.tasks/NEXT_SESSION.md @@ -1,31 +1,3 @@ -_Updated: 2026-08-24 (утро) — сессия: createPickingListPdf задеплоен + смоук ✅ оба тенанта._ +# ⛔ Файловая доска закрыта -# NEXT_SESSION - -## Последняя сессия (2026-08-24, .admin, утро) - -**createPickingListPdf ЗАРАБОТАЛ на обоих тенантах** (хендофф books 00:45Z, in_reply_to мой смоук-отчёт 22:00Z 23.08). - -Проделано: -- **Пересобрал books-sched-daemon на 0.11.1**: `books/deploy/sched/Dockerfile` FROM 0.11.0→0.11.1, локальная сборка (`docker build --secret verdaccio_token` с пустым контекстом), пуш schema2-флоу (docker save → OCI → amd64 child → трансформ mediaTypes → curl блобы + PUT манифеста; скрипт-образец `/c/tmp/pushimg/push.py`) → теги `master` + `master-0.11.1` (digest `7a43aa96…`). -- **Передеплой обоих sched-стеков** (Portainer books: stack 50 books-sched, 51 bookva-sched; portainer.kzntsv.site, X-API-Key из `pass books-vds/full-env` BOOKS_PORTAINER_API_KEY): DEPLOY_AT bump + pullImage:true. Верификация: `@sched/daemon` = 0.11.1 в контейнерах, `/api/tasks` → `config.network: "proxy"`. -- **tasks.json обоих тенантов** (jq/node-правки на VDS 89.253.255.133, ssh id_ed25519_books_ops): `network: "proxy"`, `ws://chrome:3000?token=0ffbbc13-…` (direct, БЕЗ stealth/proxy-chain!), реальный API_TOKEN (slovo `81e123d0-8edd-…` из Portainer env books-web). -- **Смоук** (ручной триггер `/api/tasks/createPickingListPdf/run`, docker exec node fetch в демоне, Bearer SCHED_ADMIN_KEY из env стека): slovo 429 ✅ (6.8MB → s3://books/fbs-picking-lists/429.pdf), bookva 219 ✅ (1.3MB). `pdf_remote_path` в БД обоих (`fbs_picking_lists.pdf_remote_path`). -- **Отчёт books**: `.agents/inbox/2026-08-24T05-44-00Z-.admin.md`. - -## 2 находки (записаны в .admin wiki books-vds-no-gitea-actions) - -1. **chrome WS-эндпоинт без `--proxy-server`**: proxy-chain гонит весь трафик (вкл. внутренний books-web:3000) во внешний пул → navigation timeout → browserless job timeout → «Target closed». Direct `ws://chrome:3000?token=…` работает (6.8s/200). -2. **bookva-api слушает 3000, НЕ 3021** (slovo books-api — 3021). API_URL bookva исправлен на `http://bookva-api:3000`. - -## Открыто - -- 🟡 **books решает по коммитам** (я НЕ пушил, по их просьбе): `books/deploy/sched/Dockerfile` (FROM 0.11.1), `books/deploy/sched-tasks.json`, `bookva-overlay/deploy/sched-tasks.json` (network/endpoint/API_URL) — правки в рабочем дереве, ждут слова юзера/books-агента. -- 🟢 git push .admin wiki (books-vds-no-gitea-actions update) — сделать при случае. -- graphify `ci.yml` (GitHub Actions) — проверить/решить при случае. - -## Полезное - -- books-VDS: 89.253.255.133, ssh `id_ed25519_books_ops` (root). Portainer books: `portainer.kzntsv.site` (X-API-Key из pass books-vds/full-env), endpoint 1. Sched-стеки: books-sched=50, bookva-sched=51, chrome=27, proxy-chain=28. -- Sched daemon admin API изнутри контейнера: `docker exec books-sched node -e 'fetch("http://127.0.0.1:3031/api/…",{headers:{Authorization:"Bearer "}})'` (ключ — из env стека). Триггер таски: `POST /api/tasks/:name/run` body `{data, temporary}`. -- tasks.json live-reload (~60s поллинг), бэкапы: `/opt/books/sched/tasks.json.bak-20260824`, `/tmp/bookva-tasks.json.bak-20260824`. +**Не читать. Не править.** Канон — mappa (`mcp__mappa__task_*`): task-сущности проекта. Скил: `mappa-task-work`. diff --git a/.tasks/STATUS.md b/.tasks/STATUS.md index f2bdef6..8cf6645 100644 --- a/.tasks/STATUS.md +++ b/.tasks/STATUS.md @@ -1,2259 +1,3 @@ -# Admin Task Board -_Updated: 2026-08-23 — 🟢 **[books-disable-legacy-task-runs-writes] ДЕПЛОЙ ВЫПОЛНЕН** (по письму books 08:49Z, юзер дал «го»). Блокер закрыт: собрал+запушил `sched-daemon:0.11.0` (base, apps/daemon, BuildKit secret). Собрал `books-task-runner:master-d57a310` + `books-sched-daemon:master-d57a310` (Gitea Actions, серийно). Задеплоил оба тенанта: task-runner (bookva стек 48 + slovo через job-scheduler стек 22), sched (стек 50+51). Все 4 контейнера healthy. Вручную на VDS: выпилен system-cleanup-task-runs из прод tasks.json (slovo /opt/books/sched + bookva volume), live-store disabled:true после рестарта демонов. SCHED_LEGACY_TASK_RUNS не включён; retention off в args. **Верификация:** task_runs не растёт — slovo MAX(created_at)=09:21:08 (старый контейнер), bookva=09:20:01; sched гоняет envelope-раны непрерывно, записей нет. Находки: (1) deploy.yml не инжектит CORE_SHA для bookva (overlay ${CORE_SHA}) — добавил CORE_SHA=master в env стека 51 вручную; (2) live-sync не зафайрился на file-change → docker restart демонов. Отчёт: books/.agents/inbox/2026-08-23T09-36-00Z-admin.md._Updated: 2026-08-22 — 🟢 **[llm-web-proxy-qwen-web] UNBLOCKED + live GREEN** (vitya): браузерный путь через baxia-WAF реализован (lwp 0.1.9, commit 43840ce): src/lib/qwen-browser.js (lazy puppeteer-core, persistent-профиль, фингерпринт-спуфинг) + executor runFlow/browser-fallback/force + CLI `--browser [fallback|force]`. Тесты 25/25 (qwen-browser 6, fallback 6, config 3 — все новые). **LIVE: qwen-web HTTP 200 headless** («Hi», «4»=2+2), капча руками НЕ понадобилась (куки из секрета + фингерпринт страницы). Конфиг: `browser: {enabled, force, headless:true}` в secrets.json qwen-main. Осталось (опционально): live-smoke стриминга и тулов через браузер. Референс: ForgetMeAI FreeQwenApi (212★), docs/reference/forgetmeai/._ -_Updated: 2026-08-22 — 🟡 **[llm-web-proxy-qwen-web] PAUSED** (решение vitya: стоп, next-сессия покажет рабочий подход; WAF-стена: baxia капча даже из браузера). Docker починен (GPU-крэш → DisableHardwareAcceleration). Хендофф записан._ -_Updated: 2026-08-22 — 🔵 **[llm-web-proxy-qwen-web]** executor готов и задеплоен (0.1.7, 92 теста, капча-детект, фикс model-alias), live заблокирован капчей — нужен свежий Cookie от vitya. Docker Desktop починен (GPU-крэш → DisableHardwareAcceleration)._ -_Updated: 2026-08-22 — 🆕 подзадача ⚪ **[llm-web-proxy-qwen-web]** заведена (п.4 qwen-web: DNS EAI_AGAIN перепроверен — chat.qwen.ai резолвится, блокер снят; ключ есть; комбо добавить в lwp). ⚪ **[llm-web-proxy] заведена** (спека .tasks/llm-web-proxy.md: кастомный web-LLM провайдер для pi + Claude Code — web-чаты, эмуляция тулов, без оверкилла omniroute; делать в следующей сессии). 🟢 **[sched-pipelines-local-stack] ЗАКРЫТА** (ритуал закрытия, решение vitya): мок-кампания финализирована ранее (0.1.0 published оба воркера, повторные раны noop, 0.2.0-кейс проверен симуляцией и откачен; фикс #7 httpGet fetch-Response контракта принят). Реальный деплой apilki — за отмашкой юзера (в «Спроси user'а»); VDS-миграция — таска sched-vds-deploy (paused). Wiki: +2 концепта в shared (verdaccio-token-buildkit-secret, msys-no-pathconv-docker-windows). -_Updated: 2026-08-21 — 🟢 **[sched-fnf-daemon-image-storage] F&F ЗАКРЫТ: 7/7 + фиксы F1/F2/N1/N2 ПРИНЯТЫ** (их письмо 23:40Z, commit e755b52, daemon 0.10.1). Ретест затронутых поверхностей на `registry.kzntsv.site/sched-daemon:0.10.1` (digest 269093a08f6e…): F1 — 0.10.1 стартует против исправленного `deploy/tasks.json` (schedules); F2 — рецепт из доков (BuildKit secret + npmMinimalAgeGate:0) собран 1-в-1, history 0 JWT, storage-mongo 0.4.0, +смоук ступени 4 (коллекции, ран); N2 — баннер `storage=custom module=./my-storage.mjs` (тест пинит module=); N1 — нота MSYS_NO_PATHCONV в доках. Регресс 1/2/3/5 на 0.10.1 — тексты фейлов идентичны. Акцепт: `sched/.agents/inbox/2026-08-21T23-41-30Z-admin.md`. -_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. +# ⛔ Файловая доска закрыта -## 🔴 [#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 -**Weight:** needs-claude -**Notify:** OpeItcLoc03/admin - - ---- - -## 🟡 [#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 -**Weight:** needs-claude -**Notify:** OpeItcLoc03/admin - - ---- - -## 🟢 [#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 (реальный деплой — за отмашкой юзера). -**Branch:** master -**Weight:** needs-claude -_Updated: 2026-08-19 — 🟢 **[sched-fnf-r10] ЗАКРЫТ (их ack 17:24Z): 6/6 PASS, 0 находок, 0 замечаний** — первый раунд без фиксов после режекта. Per-task alert routing (core 0.44.0/daemon 0.4.2): r10-a регресс, r10-b on:[] тишина (массивы replace), r10-c per-task on, r10-d per-task onMissed (missed-slot только для A, delayMs 129016), r10-e мультиканал (+без корневого webhook), r10-f fail-fast на load (3 варианта). Бонус HMAC MATCH. Процесс: раунд начинался с режекта (доки не сгенерированы/битый пример/не запушено), их фикс + push + сгенерированный dist + исправленный пример → ретест 6/6. Урок: F&F тестируется по СКОМПИЛИРОВАННОЙ доке (dev-сервер localhost:5108), не по сырцам; порядок хендоффа 1–6 у них в fnf-testing-procedure.md. Репорт: sched/.agents/inbox/2026-08-19T17-24-00Z-admin-fnf-r10-report.md. Стенд: /tmp/sched-alerts-stand/r10/._ -_Updated: 2026-08-19 — 🟢 **[sched-fnf-r9] ЗАКРЫТ (их ack 15:06Z, ретест не просили) — бухгалтерия.** Webhook-алерты (core 0.43.0/daemon 0.4.1): лестница 6/6 + 2 бонус-клейма PASS, багов нет. Замечания (их фикс — проза доков): `retry.backoffMs` де-факто required (`maxAttempts:1`+`backoffMs:0`); «catch-up on creation» — свежие расписания не догоняют прошлые слоты (nextRunAt = будущее), гейт lastRunAt гасит history-less late dispatch. Репорт: sched/.agents/inbox/2026-08-19T15-02-00Z-admin-alerts-webhook-fnf-r9.md._ -_Updated: 2026-08-18 — 🟢 **[sched-fnf-r8] ЗАКРЫТ (приёмка №5 UI, U1–U4 закрыты, ui 0.2.1→0.2.3) — бухгалтерия.** Schedule-as-entity (core 0.40.2/daemon 0.3.0/storage-* 0.3.1/ui 0.2.0): 15 репро-скриптов, №4 admin-api 52/52 (F1 tz-hoist, F3 PATCH-merge), №5 UI 13/13 (находки U1 форма-tz→400, U2 вёрстка ~58px). Подтверждённый контракт и карта стенда: .wiki/concepts/sched-fnf-r8-verification-stand.md._ -_Updated: 2026-08-18 — 🧹 гигиена: 4 июльских ⚪-блока флипнуты в 🟢 (уже были закрыты по закрывающим 🟢-блокам/_Updated-логу): imgproxy-stack29-watermark-rollback, minio-variant-cache-bucket, imgproxy-watermark-glyph-books-vds, tandemmebel-sharp-staging-rebuild. Wiki: +2 концепта (nvm-junction-ismain-gate-gotcha, nvm-windows-node-switch)._ -_Updated: 2026-08-18 — 🟢 **[sched-fnf-r7] ЗАКРЫТ — приёмка r7-fixed (09-29Z): 61/61 + t1 24/24, обе находки стали фичами.** Версии на стенде sched-r2/r7: core 0.35.0/daemon 0.1.29/mcp 0.3.10/storage-* 0.2.10 (verdaccio latest). F1 (рунтим-таска дизаблилась sync → фича fileManaged): POST /tasks → fileManaged:false, sync её НЕ дизаблит (runSyncOnce, тик-путь), переживает рестарт демона (БД-уровень, sqlite), DELETE /tasks/:name → 204/404, runs удалённой таски сохраняются; регресс file-managed → disabled on removal жив; backfill v0.5→v0.6 (DROP COLUMN → миграция вернула DEFAULT 1, legacy = file-managed, disabled); live-адаптеры mysql/postgres (v3-миграция) + mongo (schemaless, отсутствие поля = managed) — runtime жив, file дизаблится, флаг персистентный. F2 (нет валидации heartbeat → фича): createEngine бросает на lockHeartbeatMs >= lockTtlMs (== и >, сообщение внятное); CLI отказ только при явном флаге, --lock-ttl 20 в одиночку легален (дефолт ttl/3=6666ms в баннере), --poll-timeout явный >= ttl тоже отказ. Доки сверены: 03.tasks.md §149 (убрано «on the next daemon start»), 09.admin-api.md §142-143/151 (+§161 пометка про блокирующий sync-trigger), 12.cli.md (enforced at parse + engine throws). Мелочь «на усмотрение»: в published admin-api.d.ts docstring нет строки DELETE /tasks/:name (поведение+09.admin-api.md правы, одной строки в .d.ts не хватает). Стенд-репро: node r7-fixed-acceptance.mjs (61 проверка, sqlite + live docker mysql/postgres/mongo). Отправлено: projects/sched/.agents/inbox/2026-08-18T09-43-00Z-admin-fnf-r7-accepted.md (r7 закрыт, доску sched не трогал, не пушил)._ -_Updated: 2026-08-17 (4) — 🟢 **[sched-fnf-r6] РЕПОРТ ОТПРАВЛЕН (жду приёмки).** Real runners on real infra: ssh 25/25 (VDS, реальный ключ/пин/команда), http 29/29 (локальный приёмник, simple+envelope+poll+auth), storage 45/45 (MariaDB 11.4/Postgres 16/Mongo 7 на VDS, БД sched_r6 дропнуты после). Версии core 0.26.2/daemon 0.1.20/mcp 0.3.1/storage-* 0.2.1. Находки: F1 пинг r6 врет про `{"status":"ok"}` (код+дока: succeeded|failed|accepted); D1 дока 07.storage+пинг обещают `createDaemon({storage})` — daemon 0.1.20 хардкодит sqlite, опции нет (тестил через createEngine); F3 ssh fingerprint-mismatch не называет отпечатки («Host denied (verification failed)»); F4 дока ssh timeoutMs смешивает connect(failed)/command(cancelled); F5 manual trigger не пишет task.lastRunId; F6 у Mongo нет schema_version (by design); F7 эксперимент «адаптер по одной доке» — 31/31 PASS, но дока несамодостаточна (refreshLock: дока 16 методов vs published 0.26.2 = 15, фича в неопубликованном 0.27.0; сигнатуры только в .d.ts; async-фабрика в сьюте — ловушка); F8 эксперимент «runner по одной доке» — 15/16 (CR-1 баг доки: `"schedule": "* * * * *"` строкой невалиден, надо объект {cron}; CR-2 дыра: run() показан без hooks/onProgress — live-прогресс sync-раннеров в доке нет); F9 mcp-раннер — «на усмотрение»: единственный раннер без живого прогона за все раунды, ниша узкая (MCP-only тулы + вызов без агента в петле), рекомендация — честное позиционирование в доке + одна живая таска как валидация; **F10 от юзера: admin API+UI Schedules (и Tasks) не показывают статус последнего рана** — /schedules не отдаёт lastRunId/статус (admin-api.ts ~367), UI колонка «last run» = только timestamp (sched-schedules.ts), рекомендация: lastRunId+lastRunStatus в API, колонка «last status» в UI. Отправлено: `victor/sched/.agents/inbox/2026-08-17T20-35-00Z-admin-fnf-r6-report.md`. Стенд: `.tmp/sched-r2/r6/`._ -_Updated: 2026-08-17 — 🟢 **[sched-fnf-r4] ЗАКРЫТ (5/5 + находки A/B/C починены, core 0.25.0/daemon 0.1.18).** Run-метаданные (trigger/triggeredBy/temporary/retryOf), trigger-only, manual retry (`POST /runs/:id/retry`), auto-retry линковка, retention — всё зелёное (43 чек-пойнта). Находки: A) syncTasks disable-on-remove (removed → disabled, док стал правдой); B) `POST /tasks/:name/run` с `{temporary?, data?}`; C) `--help` дефолты. Перетест 12/12. Репорты: `victor/sched/.agents/inbox/2026-08-17T16-02-00Z-*-report.md` + `…T16-09-00Z-*-retest-report.md`. Стенд `.admin/.tmp/sched-r2/r4/`._ -_Updated: 2026-08-17 (3) — 🟢 **[sched-fnf-r5] ЗАКРЫТ (7/7 + A/B/C приняты).** allowedTools sandbox (core 0.26.2/mcp 0.3.1/daemon 0.1.20). Перетест B после второго захода: **18/18 PASS**. A) ssh ceiling fail-fast на load — подтверждено 4/4 (нюанс порядка shape-vs-ceiling принят «по желанию», перестановка при след. касании ssh); B) mcp bare http spec — оба дефекта починены: daemon 0.1.20 перепинен на `@sched/mcp:^0.3.1` (вложенная 0.3.0 ушла, daemon грузит 0.3.1), хитристика ловит `host:port` (их кейс #2 дословно: `https://127.0.0.1:1` → throw; ceiling и per-task → load-отказ «must include a tool dimension»; `host:port:tool`/`npx*:read_*` валидны); C) дока. Регресс process/opt-in жив. Ретест: `r5-retest-ab.mjs`. Репорт: `victor/sched/.agents/inbox/2026-08-17T17-27-12Z-admin-fnf-r5-accepted.md`. -_Updated: 2026-08-17 — 🟢 **[sched-fnf-r3-refresh0] ACCEPTED — раунд 3 закрыт.** sched починил `?refresh=0` (ui 0.1.10→0.1.11, comm 926465b): чистую функцию refreshFromSearch (browser-safe модуль, morda.ts с node-импортами ломал esbuild) + NaN-guard. Перетест на стенде r3 (ui 0.1.11, Chrome/CDP, 3 вкладки): (1) `?refresh=0` — fail-fast замер на next 13:42:30/fails 12 за 35с, пока API-истина росла до 14 — polling реально выключен; (2) `?refresh=2500` — 4 запроса /api/tasks за 10с (2.5с каденс) vs 2 (5с) без параметра; (3) дефолт 5с жив. Раунд 3 закрыт полностью (все 4 пункта: tasks/schedules/runs polling + refresh-оверрайд). Репорт: `victor/sched/.agents/inbox/2026-08-17T10-44-00Z-admin-fnf-r3-refresh0-retest.md`._ -_Updated: 2026-08-17 — 🟢 **[sched-fnf-r3-ui-autorefresh] ACCEPTED.** sched починил мёртвый polling в Tasks/Schedules (ui 0.1.9→0.1.10, comm 83ac7be): morda слала `el.refreshMs` всем компонентам, но SchedTasks/SchedSchedules его не декларировали — поллил только Runs. Проверил на стенде r3 (8082 daemon + 8083 serve, ui 0.1.10 из verdaccio, реальный Chrome через CDP, без единого клика refresh): Tasks — next run `13:35:37→13:36:52` и fails `0→2` за 35с (fail-fast), stale nextRunAt 5-мин задач пересчитались сами; Schedules — next `13:36:52→13:37:23` + last `13:36:22→13:36:53`; Runs регресс — 4→10 строк без refresh. ⚠️ Находка «на ваше усмотрение»: `?refresh=0` polling НЕ выключает (morda.ts:99 `Number(...) || cfg.refreshMs` — 0 falsy → дефолт 5000; предсуществующий, из 6e64582c, не регрессия фикса; фикс однострочный null-check). Репорт: `victor/sched/.agents/inbox/2026-08-17T10-39-00Z-admin-fnf-r3-ui-autorefresh-retest.md`._ -_Updated: 2026-08-17 — 🟢 **[sched-fnf-r3-cache-headers] ACCEPTED.** sched починил отсутствие Cache-Control (core 0.22→0.23.0: admin-api JSON/204 no-store + artifact-proxy no-cache; ui 0.1.8→0.1.9: serve bundle no-store + /api/* прокси no-cache; daemon 0.1.15→0.1.16, comm 3352edb). Перепроверил вживую против собранных dist (поднял admin-api+serve, curl -D): bundle→no-store ✅, serve-proxy /api→no-cache ✅, admin /health+/runs+4xx+trigger→no-store ✅, DELETE 204→no-store ✅ (на существующем ране), artifact-proxy→no-cache ✅. Сьют 464 ✓ (29 файлов; 2 unhandled-warning — известный Lit class-field, не регресс). Версии совпали с опубликованными (core 0.23.0/ui 0.1.9/daemon 0.1.16). Закрывает вчерашние «залипшие running» без Ctrl+F5. Репорт: `victor/sched/.agents/inbox/2026-08-17T08-31-30Z-admin-fnf-r3-cache-headers-retest.md`. Остаток (visibilitychange/refetch-on-focus) — опционально, не блокер._ -_Updated: 2026-08-18 — 🟢 **[llm-router-failover-proxy] ЗАКРЫТА юзером (2026-08-18: «забудь» — сделано, не трогать).** Служба llm-router работает, дальнейшего трекинга нет._ -_Updated: 2026-08-16 (14) — 🟢 **[sched-fnf-04-embedded-alerts] ACCEPTED — F&F цикл закрыт.** Приёмка №4 (14:40Z): замечаний нет. Итоги sched: 6 багов/пробелов → 6 фич (exports-map 0.1.2, @sched/ui serve 0.1.1 + токен-бокс, validateConfig 0.12.2, ntfy basic 0.12.2, pollTimeoutMs 0.1.4). Версии: core 0.12.2 / daemon 0.1.4 / ui 0.1.1 / mcp 0.1.1, сьют 363 ✓. У них дальше: storage-сплит (по фидбеку) + полировка доков; раунд 2 согласован: docs-review после storage-сплита + полировки (пинг от sched, формат делал/ожидал/получил/раздел, фокусы: quick-start все пути, 07.storage, 10.cli, 13.ui) -_Updated: 2026-08-16 (13) — 🟢 **[sched-fnf-03-async-poll] ACCEPTED + 🟡 [sched-fnf-04-embedded-alerts] DONE (репорт отправлен).** Приёмка №3 (14:31Z): pollTimeoutMs проброшен в daemon (0.1.4, +pollIntervalMs, CLI --poll-timeout), доки 10.cli.md/05.protocol.md. №4 embedded (финальная): createEngine+internal runner+sqlite+syncTasks в одном скрипте — реальная проверка свежести бэкапов kreknin по SSH (vds-kzntsv 12.4h / books-vds 11.4h → succeeded; порог 1h → failed «STALE backups»); retry 3x (a1 fail → a2 fail → a3 succeed, triggerTask не ретраится — доки честны); createAlerts ntfy **basic auth работает** («[sched] backup-freshness failed» в топике). Цикл F&F закрыт, замечаний нет. Репорт: `victor/sched/.agents/inbox/2026-08-16T14-36-01Z-admin-fnf-04-report.md`._ -_Updated: 2026-08-16 (12) — 🟢 **[sched-fnf-02-docker-runner] ACCEPTED + 🟡 [sched-fnf-03-async-poll] DONE (репорт отправлен).** Приёмка №2 (14:24Z): оба замечания стали фичами — validateConfig-хук (allowlist на load, abort при старте, core 0.12.2) + ntfy basic auth (user+password в createAlerts, daemon 0.1.3), сьют 362 ✓. №3 async/poll: свой воркер (:8293) envelope accepted+poll — happy path queued→running(50)→succeeded (progress 100, 3.5s), timeout-кейс hung → failed «poll timeout after 15000ms» (через createEngine, т.к. daemon не пробрасывает pollTimeoutMs — замечание в репорт). Фиксы №2 подтверждены (abort на load + ntfy basic в коде). Репорт: `victor/sched/.agents/inbox/2026-08-16T14-27-54Z-admin-fnf-03-report.md`._ -_Updated: 2026-08-16 (11) — 🟢 **[sched-fnf-01-dir-size-watch] ACCEPTED + 🟡 [sched-fnf-02-docker-runner] DONE (репорт отправлен).** Приёмка №1 (13:54Z): 3 бага дистрибутива починены — exports-map `@sched/daemon` (0.1.2), UI в `@sched/ui` 0.1.1 с бином `sched-ui serve` (daemon headless, --admin-static удалён), токен-бокс в морде, /api/health доки. №2 docker-runner: one-shot alpine:3.20 `wget --spider` на pilorama98.ru → ntfy sched-fnf, exit code → status (0=succeeded / 1=failed+алерт). Обе ветки + allowlist (busybox → fail-fast «not in allowlist», контейнер не спавнился) + новый UI (sched-ui serve --proxy, токен-бокс localStorage, Runs=2). Репорт: `victor/sched/.agents/inbox/2026-08-16T14-17-55Z-admin-fnf-02-report.md`. 2 замечания: allowlist проверяется на dispatch не на load (доки 03.tasks.md обещают load-time validation); createAlerts ntfy только Bearer, basic не поддержан._ -_Updated: 2026-08-16 (10) — 🟡 **[sched-fnf-01-dir-size-watch] DONE (репорт отправлен, жду приёмки).** F&F sched №1 выполнен: custom runner `dirSizeWatch` (createDaemon, @sched/core 0.12.1 + @sched/daemon 0.1.1 из verdaccio, стенд `.tmp/sched-fnf-01/`), задача «раз в 6ч размер C:\sites\snolla\App_Data → ntfy sched-fnf, алерт при >1MB». Обе ветки проверены: 1MB-порог → `⚠️ THRESHOLD EXCEEDED` (priority 5) в ntfy, 100MB-порог → обычное сообщение (37.5 MB). Run succeeded в истории, admin UI отрендерен (скриншот ui.png). **3 косяка доки/дистрибутива в репорт sched:** (1) `03.custom.md` импортирует `createDaemon` из `@sched/core` — его там нет (живёт в `@sched/daemon/dist/daemon.js`, пакет без main/exports); (2) published `@sched/daemon` не содержит `sched-ui.bundle.js` — admin UI 404 из коробки, нужен `uiBundlePath`; (3) морда не передаёт token в веб-компоненты — при SCHED_ADMIN_KEY UI показывает unauthorized, ввести ключ негде. Репорт: `victor/sched/.agents/inbox/2026-08-16T13-21-11Z-admin-fnf-01-report.md`._ -_Updated: 2026-08-16 (9) — 🟢 **[pilonuxt-restart-cache-volume-price] DONE.** По запросу прогера (письмо 14:40Z): после перезаливки volume_price (`38c26c7` + admin-api 0.11.0) часть карточек не показывала цену за м³ — content-api держал старый viewModel в памяти контейнера (в БД `voulme_price: 38000` массив, во фронте null). Рестарт контейнера (стек 16, образ `64c2539` без пересборки) → кэш сброшен. Verify мимо LAN-DNS: `/shop/products/imitaciya-brusa-podnyatyj-vors-20-145-6000` 200, `volume_price: 38000` в payload + «Цена за м³» + 38 000 в HTML; категории skandinavskaya-doska/imitaciya-brusa/brus/doska 200, регресс `/`/`/services` 200 + `/services/building` 404. Отписано прогеру (14:55Z)._ -_Updated: 2026-08-16 (8) — 🟢 **[deploy-pilonuxt-admin-api-0110] DONE.** Релиз snolla `@snolla/admin-api` 0.11.0 (array-canon: Content=массив, ruling vitya; + D6 dirty-flag). Монорепа HEAD `64c2539` (admin-api 0.10.1→0.11.0, yarn.lock; core 0.26.7). Миграция объектных строк уже выполнена прогером (БД чиста, dry-run идемпотентно). Build из монорепы, push напрямую, Portainer PUT стек 16 (`pullImage:true`, Env:0). Verify мимо LAN-DNS: `/catalog/skandinavskaya-doska` + `/catalog/imitaciya-brusa` 200 0×500 (6+6 товаров), карточки podnyatyj-vors 200, embedded admin-api `/admin/api/health` 200, регресс `/`/`/catalog`/`/catalog/brus`/`/services` 200 + `/services/building` 404. Откат: стек 16→`1128ddc`. Source-of-truth compose актуализирован (`64c2539`)._ -_Updated: 2026-08-16 (7) — 🟢 **[deploy-pilonuxt-core-0267-fix] DONE.** Фикс snolla `@snolla/core` 0.26.7 (fillViewModel flat-object normalize — чинит 500 на 10 новых товарах). Монорепа HEAD `1128ddc` (yarn.lock core 0.26.6→0.26.7). Build из монорепы, push напрямую, Portainer PUT стек 16 (`pullImage:true`, Env:0). Verify мимо LAN-DNS: `/catalog/skandinavskaya-doska` 200 0×500 **6 товаров** (3 гладких + 3 podnyatyj-vors), `/catalog/imitaciya-brusa` 200 0×500 **6 товаров** (4 старых + 2 podnyatyj-vors), карточки 3 сэмпла все 200 0×500, регресс `/`/`/catalog`/`/services`/`/catalog/brus` 200 + `/services/building` 404. Откат: стек 16→`f876e70`. Source-of-truth compose актуализирован (`1128ddc`). -_Updated: 2026-08-16 (6) — 🔵 **[pilonuxt-new-products-500] ROOT CAUSE найден — баг snolla, ждём фикс.** Прогер подтвердил инспекцией боевой MSSQL: admin-api при create пишет `Products.Content` плоским объектом, `@snolla/core` viewModels читает только массив `[{name,value}]` → `_.find(contentFields, f=>f.name)` падает (viewModels/index.js:419). Сломаны только 10 новых товаров (create через admin-api; update-путь merge'ит в existing-массив — старые живы). Письмо snolla отправлено прогером (фикс: create → 'array' + defensive normalize в core). Рестарт/деплой не нужен. Ожидание: фикс snolla ИЛИ миграция Content 10 товаров в массив (MSSQL, сам прогер) → тогда verify категорий/карточек на проде. Ack отправлен (12:05Z)._ -_Updated: 2026-08-16 (5) — 🔴 **[pilonuxt-restart-new-products-500] рестарт НЕ помог — серверный краш.** По запросу прогера (письмо 09:05Z): залил 10 новых товаров через loader (скандинавская доска, имитация бруса поднятый ворс, рейки скрытые), просил рестартнуть стек 16 для подхвата. Рестарт выполнен (Portainer, контейнер running, uptime 2s). Verify: `/catalog/skandinavskaya-doska` + `/catalog/imitaciya-brusa` → HTTP 200, но **500 внутри** (NuxtError, `Request failed with status code 500`); остальные категории (doska/brus/brusok/reyka/terrasnaya-doska) + `/` целы. Логи: `TypeError: Cannot read properties of undefined (reading 'name')` в `@snolla/core viewModels/index.js:419` (`_.find(x=>x.name)` на undefined) через `getProductViewModel` — missing-field у нового товара из заливки, НЕ кэш. Отписано прогеру с уликами (09:15Z); ждём фикс данных/кода → деплой или рестарт по его отмашке._ -_Updated: 2026-08-16 (4) — 🟢 **[deploy-pilonuxt-catalog-img-fix-2] DONE.** Фикс прогера `f876e70` (origin/master, 1 файл `categories.json`): оставшиеся 3 плейсхолдера каталога заменены эталонными imgproxy-фото из CMS (list_image → 800×800) — Доска обрезная `ylD4trDVXa…` (было `/images/1.webp`), Доска строительная `qqUyT37cNrf…` (`/images/2.webp`), Брус/брусок/рейка `CGyNTUwB7iY…` (`/images/3.webp`). Build из монорепы, push напрямую, Portainer PUT стек 16 (`pullImage:true`, Env:0). Verify мимо LAN-DNS: `/catalog` 200 + `/` 200, `src=/images/[123].webp` → **0 вхождений**, 25 imgproxy-refs, все 3 новых фото на месте; регресс `/services` 200, `/services/building` 404, `/catalog/brus` 200, `/catalog/antiseptirovannye-pilomaterialy` 200. Заглушки каталога закрыты полностью. Откат: стек 16→`24a13da`. Source-of-truth compose актуализирован (`f876e70`). -_Updated: 2026-08-16 (3) — 🟢 **[deploy-pilonuxt-catalog-img-fix] DONE.** Фикс прогера `24a13da` (origin/master, 1 файл `categories.json`): тайлы «Брус антисептированный» (мета «Брус, брусок, рейка» + «Антисептированные пиломатериалы») переведены с плейсхолдера `/images/2.webp` на imgproxy-фото `TGTqrE8…` (как у «Брус, брусок сухой антисептированный»). Build из монорепы, push напрямую, Portainer PUT стек 16 (`pullImage:true`, Env:0). Verify мимо LAN-DNS: `/catalog` 200, оба тайла `src=/imgproxy/TGTqrE8…`, заглушки нет; `/` 200; оставшиеся 3 плейсхолдера (обрезная 1.webp / строительная 2.webp / брус-брусок-рейка 3.webp) намеренно не тронуты (заметка прогера — заменят при отмашке владельца + эталонные URL); регресс: `/services` 200, `/services/building` 404, `/catalog/brus` 200. Откат: стек 16→`40806b1`. Source-of-truth compose актуализирован (`24a13da`). -_Updated: 2026-08-16 (2) — 🟢 **[deploy-pilonuxt-services-404-fix] DONE.** Фикс прогера `40806b1` (origin/master): `services/[slug].vue` — `await useAsyncData` до рендера + setup-time `throw createError(404)` вместо lazy `usePage`+watchEffect (мягкий 200 подтверждён прогером локально). Build из монорепы, push напрямую, Portainer PUT стек 16 (`pullImage:true`, Env:0). Verify мимо LAN-DNS: `/services/building` → **404** (тело «Услуга не найдена»), `/services/nope` → 404, `/services/painting` → 200, `/` → 200 («каркасн» 0), `/services` → 200, регресс 8 живых услуг все 200. Follow-up на SEO не нужен. Откат: стек 16→`340c0e7`. Source-of-truth compose актуализирован (`40806b1`). -_Updated: 2026-08-16 — 🟢 **[deploy-pilonuxt-frame-house-removal] DONE.** Запрос прогера pilorama98 (письмо в `.agents/inbox/2026-08-16T07-13-00Z-pilorama98.md`): передеплой стека 16 на `340c0e7` (монорепа; `25aaacf` — код: выпил «Строительство каркасных домов», баннер-фикс главной, h2-catalog v-html fix, services-404; `340c0e7` — вики). Build из монорепы (secret verdaccio_token, тег `340c0e7`), push напрямую (499 не случился), Portainer PUT стек 16 (`pullImage:true`, env запечён в compose — Env:0). Контейнер на `340c0e7` running. Smoke мимо LAN-DNS (`--resolve 89.253.255.94`): `/` 200 без «каркасн», баннер «Окрашивание древесины» один (alt «Новая услуга: окрашивание древесины»), h2-разделы каталога 8 видны; `/services` 200 без «каркасн», 8 карточек (без building); `/services/painting`+`/services/planing` 200 без упоминаний. ⚠️ **Finding прогеру:** `/services/building` рендерил 404-контент, но HTTP-статус **200** (watchEffect-бросок после фиксации Nitro-статуса, мягкий 404) — фикс пришёл следом (`40806b1`, задеплоен). Откат: стек 16→`8b7e8c4`. Source-of-truth `host-stacks/vds-kzntsv/pilonuxt.compose.yml` актуализирован._ -_Updated: 2026-08-14 — 🟢 **[snolla-mailer-per-recipient-send] DONE (деплой+приёмка).** Тирож на mailer 0.9.0 (per-recipient loop): pilonuxt 8b7e8c4 + 7 env-сайтов web@0.44.0 (83513ef/0cd20fc/698d5f5/3f6f0c4/ccd620c/a649bcf/4aaa175), data единый 0.15.1. Попутный фикс: `mailSettings.from=noreply@snolla.com` в production.json env-сайтов (550 not-owned) → e-16513832@yandex.ru. Приёмка: pilorama98 #2498 (3 письма) + kupimknigi #516 (2 письма) — messageId, 0 ошибок; mailer 0.9.0 во всех 8 контейнерах. Спека: `.tasks/snolla-mailer-per-recipient-send.md`._ -_Updated: 2026-08-14 — 🟢 **[snolla-smtp-send-monitor-vds] DONE** (по запросу vitya). Монитор SMTP-отправки snolla-сайтов задеплоен на VDS: `/root/snolla-smtp-monitor/monitor.py` (smtp-auth + weekly real-send + parity Portainer env 7 стеков) + cron 08:00 MSK; негатив-тест пройден (535 → ntfy-alert HTTP 200); parity зелёный после роллаута SMTP. Копия: `.admin/scripts/snolla-smtp-monitor/`. Попутно: msmtp на VDS (бэкап-отчёты) переведён на новые креды + From в run.sh (был мёртвый noreply@snolla.com)._ -_Updated: 2026-08-13 — 🟢 **[stostayer-web-deploy-0-3-23] CLOSED.** Запрос их прогера (письмо в `.agents/inbox/2026-08-13T08-43-41Z-stostayer-new.md`): calculator-баннер про цены при покупке запчастей + фикс краша (snolla loadCity/loadSite) + стек-фикс легаси (vue/vuex/vue-router/mariadb явные deps, nmHoistingLimits). Образ `stostayer-web:0.3.23` (коммиты `e5f7bd6`+`7e51cbf`+`d49b4bb`) собран (нюанс: offline-build не прошёл — в `.yarn/cache` нет linux-бинарей esbuild/rollup, собрал с временным `VERDACCIO_TOKEN` ARG/ENV), запушен 1 попыткой (слои already-exists → бана нет), стек Portainer 16 передеплоен 0.3.22→0.3.23 (HTTP=200, env re-supply). Verify с хоста: Focus II → 200 + баннер «актуальны при приобретении запасных частей», регресс `/`+ремонтная → 200, Focus IV (exactPrices=0) → баннера нет + алерт «уточняйте у мастеров» (всё по письму). Контейнер Up, RestartCount=0, логи чистые. Откат: PUT на 0.3.22. Ранбук обновлён (прод-тег, build-нюансы, порт :9000 в Portainer API). Всё: `.tasks/stostayer-web-deploy-0-3-23.md` + `.wiki/concepts/stostayer-web-deploy-runbook.md`._ -_Updated: 2026-08-10 — 🟡 **[kreknin-repair-md3-rebuild] ребилд идёт, бэкапы ВОЗВРАЩЕНЫ.** Утро: vitya на месте, кабели перетыкнуты (sdb WD-WX32D12L8HTE вернулся, SMART PASSED, все 3 HDD на 6 Gbps), md3 rebuild старт 07:57 (`mdadm --add /dev/md3 /dev/sdb3`), md0/md1 починены (системные разделы добавлены во все 4 диска — «зелёные» в DSM), win10 resolved (RuToken re-enum, кнопка «Игнорировать»). **~11:40 отключение электричества в деревне** → NAS лёг (роутер/WAN жив — на UPS). Возврат 16:20: md3 собрался деградированным `[_UU]`, **sdb3 сам не вернулся** (прогресс rebuild НЕ переживает power-loss — контрольной точки нет), повторён `mdadm --add /dev/md3 /dev/sdb3` → recovery с 0.0%, ETA ~4-5 дн (разгон 3→10-25МБ/с). md0/md1 все 4 диска `[UUUU]` (утр. ремонт пережил ребут), md2/VMM здоров. **Бэкапы возвращены 2026-08-10 18:00 по указанию vitya (ДО ребилда — ребилд фоновый):** cron раскомментирован (vds-kzntsv 05:00 + books-vds 06:00), ручные прогоны ПОДТВЕРЖДЕНЫ на kreknin (vds-kzntsv 2026-08-10 66G / books-vds 15G, latest→08-10, 7 снапшотов у обоих). Находка: books-vds не доезжал до kreknin с 08-05 (08-06..08-09 + 08-10 06:05 rsync падал — timed out/no route; другой триггер не найден); застрявший ES-снапшот `daily-2026-08-10` удалён. Осталось: дождаться ребилда md3 → `btrfs scrub /volume1` → monitor на ребилд активен. Всё: `.tasks/kreknin-repair-md3-rebuild.md` + `.wiki/entities/kreknin-synology.md`._ -_Updated: 2026-08-04 — 🔵 **[coord-loader-admin-api-client] координатор.** Я (admin) назначен координатором таски pilorama98 `loader-migrate-to-admin-api-client` (перевод `apps/loader` с прямого DB на `@snolla/admin-api-client` от snolla). Письма-интро отправлены обоим (pilorama98 — report готовности, snolla — краткое описание клиента). Предпосылка S1: **ApiKeys DDL прогнана на обоих боевых SNOLLA** (vds `MoreThenCms` + stostayer `stostayer`) — таблица + индексы + FK, RC=0, verify зелёный. См. `coord-loader-admin-api-client.md`._ -_Updated: 2026-08-03 (2) — 🟢 **[labtools.ru cylindrical] CLOSED.** Фикс core 0.26.5 (store-only content-page резолвер) НЕ чинил: labtools — catalog-модуль (store отсутствует), linked-item provider = legacy `LinkedCatalogProductsProvider`. Реализован полный диспетчер `viewModels/linkedItemResolver.js` (порт `ILinkedEntitiesProvider`): dispatch по provider-строке → catalog (CatalogsService.findProductById) / blog (findPostByGlobalId) / page (getPageById) / store (fallback). core 0.26.6 published (verdaccio), 263/263. labtools yarn.lock → 0.26.6 (d263e1c), стек 17 redeploy (env 8/8, pullImage), **LIVE GREEN**: cylindrical 0 empty href/src, 8 карточек гидратированы (round-xrf/vacuum-ir/…), остальные страницы 200. Таска snolla закрыта. Rollback = `0dc0b4e`._ -_Updated: 2026-08-02 — ⚪ **[kreknin-repair-md3-rebuild] заведена.** Xpenology kreknin деградед после продувки компрессором 2026-07-31: sdb выпал (md3 `[_UU]`), кабели sda/sdc срыв. **Rescue полный DONE 2026-08-02** (USB 2TB `/mnt/rescue`, 1.1TB: netbackup/*.tar включая diskstation_1.hbk 430G + userdata 5 шар Алексея), массив за 2 суток чтения — 0 новых ошибок. sdd/VM-том восстановлен (md2, `/volume2` rw). **Ежедневные бэкапы DISABLED** (vds-kzntsv 05:00 + books-vds 06:00, cron закомментированы, `.bak.20260731`). Осталось: power-off → перетык SATA (sda/sdc свежие, sdb питание+data) → Repair md3 (реборн) → вернуть бэкапы. Всё: `.tasks/kreknin-repair-md3-rebuild.md` + `.wiki/entities/kreknin-synology.md`._ -_Updated: 2026-07-31 (2) — 🟢 **variant-cache bucket fix.** maljarka portfolio фото 500 → root cause: v3 minio-split перевёл сайты на vds MinIO 2025, но `variant-cache` bucket не создали там (был только на старом minio.kzntsv.site/books-vds 2020). snolla sharp-pipeline (`variantCache.js` DEFAULT_NAMESPACE=`variant-cache`) → NoSuchBucket → 500 на ALL sharp gallery-images ВСЕХ 6 v3 сайтов с 2026-07-30 (тихо — v3-gate проверял theme CSS из `themes` bucket (themeFiles, без sharp), не gallery-image URLs). Fix: `mc mb vds/variant-cache` (пустой, кэш регенерируем). После: maljarka portfolio 34/34 → 200 image/webp, full-size 920x600 200, tandemmebel logs NoSuchBucket ушли. **minio-split Track A gap** — при endpoint-flip проверять ВСЕ runtime-buckets (`galleries`/`themes`/`assets`/`variant-cache`), и gate должен хитить gallery-image URL. Memory `variant-cache-bucket-missing-on-vds-2025`._ -_Updated: 2026-07-31 — 🟢 **[maljarka-vds-restore] CLOSED.** `maljarka.tandemmebel.ru` восстановлен как 7-й сайт тиража snolla на VDS. Сайт был потерян 2026-07-21 при декоммишне RUVDS IIS (единственный публичный хост maljarka; в v3 тираж не входила, осталась на RUVDS). DNS перебросили на VDS, но `Host()`-правила + snolla-app с siteId `A2476738` не было → traefik 404. Аудит 21.07 «VDS=200» = false-positive с локального IIS воркстейшна. **Контент цел** в shared `MoreThenCms` DB (5 Pages, 4 StaticPages, тема Reversal `4881FC7F` уже в MinIO themes bucket). Восстановление = клон `victor/on.snolla.com` → `victor/maljarka.tandemmebel.ru` (snolla 0.43.2/aws-sdk v3), swap siteId `A2476738`+siteUrl, `layout.liquid` **byte-identical** рендеру локального .NET-admin (43730B, sha256 match, `{{ item.content }}` inline — content пуст), error-страницы Reversal-бренд. Build на VDS (`7f03d67`, digest df4e3642, regen yarn.lock под переименованный workspace), throwaway-staging :5081 gate GREEN (byte-parity `/`, 4 staticpages 200 sizes==DB, theme asset 200 103320B, unpublished 404 parity), Portainer стек **Id 23** (node create-stack, НЕ PS — кириллица), env 8/8 shared, mem_limit 512m, `Host(maljarka.tandemmebel.ru)`. DNS уже VDS → LE issued on first hit (CN=maljarka.tandemmebel.ru, YR1, до 2026-10-29). Live smoke GREEN: `/` 200 43730B byte-identical, title «Малярка от Тандеммебель», 4 staticpages 200, theme asset 200, robots/sitemap 200. **Тираж 7/7**: labtools(17)/emspb(18)/labtools-pro(19)/tandemmebel(20)/kupimknigi(21)/on-snolla(22)/maljarka(23). Runbook: `maljarka-vds-deploy-runbook`. Rollback = образ-тег в registry (RUVDS DECOMM, не DNS)._ -_Updated: 2026-07-20 (2) — 🟢 **[snolla-local-admin-restore] CLOSED.** Локальный .NET-админ поднят: selective tar-copy `C:\sites\snolla\` ~100MB с RUVDS (исключил stale App_Data 8.76GB — assets/galleries/themes теперь в MinIO), Web.config уже → `mssql.kzntsv.site,1433` Catalog=MoreThenCms (user `snolla`) + MinIO S3 drop-in (ключи реальные, placeholder_count=0). Elevated setup `scripts/local-snolla-admin-restore/setup-local-snolla-admin.ps1` (ASCII-only, PS5.1 BOM-less-safe): AppPool `snolla` (.NET v4.0, AppPoolIdentity, recycle@200MB), IIS site `*:80` catch-all, ACL `IIS AppPool\snolla:(OI)(CI)(M)`, hosts-override `127.0.0.1 <6 aliases>.snolla.com`, FW block inbound 80. Smoke GREEN: `tandemmebel/labtools/labtoolspro/emspb/kupimknigi/on .snolla.com/admin/account/login` → 200, реальная MoreThenCms-логинформа (`SNOLLA`, `
`). **Unblocks [on-snolla-vds-migration]** (Task B — админ для контент-инспекции готов). МинIO upload-acceptance = ручной follow-up оператора. RUVDS SSH через plink `-hostkey SHA256:r/vSKU5WzH4B8T7RiXyXlg0D8XZ9hlBxzmdrPzuPWzE` (креды `pass show ruvds-iis/full-env`)._ -_Updated: 2026-07-20 — ⚪ **заведены [snolla-local-admin-restore] + [on-snolla-vds-migration].** Design: `.wiki/concepts/snolla-local-admin-and-on-snolla-migration-design.md`. **Task A:** восстановить локальный .NET-админ (catch-all IIS `snolla`, снесён 2026-06-08 при decommission) копированием `C:\sites\snolla\` с RUVDS (текущий прод-админ с MinIO drop-in), conn→`mssql.kzntsv.site:1433` Catalog=MoreThenCms, доступ `.snolla.com/admin` через hosts-override (1 AppPool, 6 адресов: tandemmebel/labtools/labtoolspro/emspb/kupimknigi/on). **Task B** (blocked by A): мигрировать посадочную on.snolla.com (siteId `B9ECDB50-2B93-4CE5-B238-A9918B3F9CF7`, alias `on`, «Internal Site», culture `en`) с RUVDS IIS на VDS как Node snolla-app 0.42.1, реконструируя Liquid-шаблоны из боевого сайта+админки (исходников нет). Addresses + siteIds извлечены из `MoreThenCms` DB read-only (tedious-проб, SA из `pass mssql-vds/sa-password`, скрипт в `.tmp/dbprobe-sites.mjs` gitignored). Развилка адресации confirmed оператором: catch-all `*:80` + hosts (не per-port)._ -_Updated: 2026-07-13 — 🟢 **[tandemmebel-web-vds-deploy] in-place template bump 8df10ee LIVE.** Ops-handoff от tandemmebel-сессии (inbox-запрос): убрать FB/Twitter/Google+ share-кнопки (extremist-icon compliance РФ), оставить ВК+Одноклассники. Коммит `8df10ee` в `victor/tandemmebel.ru` master (template-only, `apps/web/views/social_buttons.liquid`, 1 file 12 deletions, snolla pin 0.42.1 НЕ менялся). **Верификация перед сборкой поймала расхождение:** записка утверждала live=`ed96b18`/0.42.0, фактически на проде крутился `0cd9351`/0.42.1 (in-place bump 2026-07-12). gitea compare `0cd9351...8df10ee` = `total_commits:1` → фикс ровно один коммит поверх 0.42.1 (правильная база), yarn.lock идентичен → регрессии sitemap 184→172 нет. Собрал `tandemmebel:8df10ee` на VDS (чистый архив 8df10ee из gitea API, build-arg VERDACCIO_TOKEN=books-ci JWT, digest `sha256:30a7e5ab82f2bf371042f1b5fa0cd7ac5ceda71379d1da59e148dee8166bd560`, layer-cache hit, push OK). Throwaway-staging :5020 → completeness-gate GREEN: sitemap NEW==PROD **184=184 identical** (0 prod-only/new-only), self-consistency 183×200+1×404(/articles benign parity staging==prod), share-block staging vk+ok/fb-tw-gp=0 vs prod-before vk+ok+fb+tw+gp (доказательство — фикс убирает ровно лишнее). Swap стека 20 (Portainer JWT, PUT `/api/stacks/20?endpointId=1`, env 8/8 preserve, `prune:false`, `pullImage:true`), контейнер healthy ~8s. **Live-smoke С VDS GREEN:** robots/`/`/sitemap 200, /articles 404 parity, sitemap 184 locs, 4 share-block страницы (project-post ×2, /furniture/bedrooms, /furniture/kitchens/classic) все 200 → **vk+ok на месте, fb/tw/gp=0** (экстремистские иконы УБРАНЫ с прода), TLS-серт CN=tandemmebel.ru не дёрнут (in-place swap). `/galleries/` нет на 0.42.1 (sitemap-реструктуризация 0.42.x → gallery рендерится на project-post роуте). Rollback = PUT стека 20 назад на `0cd9351` (0.42.1, {% order %} fix, template-only чистый откат) / `ed96b18` (0.42.0, в registry, стёрт с VDS при disk-cleanup 2026-07-13) / `b02ca18` / revert DNS→80.64.31.36. Source-of-truth compose `host-stacks/vds-kzntsv/tandemmebel.compose.yml` актуализирован (8df10ee + LIVE-комменты + история bump'ов). Hygiene-заметка: Portainer stack 20 file несёт устаревшие STAGING-комменты на строках 16-17/53 (rule line 55 уже LIVE) — косметика, не трогал при деплое. Ответ отправлен в tandemmebel inbox._ -_Updated: 2026-07-12 — 🟢 **[tandemmebel-web-vds-deploy] CUTOVER COMPLETE — 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()`-rule staging→боевой на стеке 20 (env 8/8 preserve, `prune:false`, `pullImage:false` — образ `ed96b18`/0.42.0 не менялся, без пересборки по решению оператора). LE-серт issued on first hit: CN=tandemmebel.ru, SAN оба, issuer YR2, until 2026-10-10. Live-smoke С VDS GREEN: `/`+`/projects`+3×gallery (office/bedrooms/kids)→200, `/articles`→404 (parity benign#3, так и на RUVDS), sitemap **184 page-locs** (было 172 — рост от blog-archive фичей 0.42.0, benign), self-consistency 30/30 sample (только /articles 404 = тот же parity), sharp media `image/webp 200 35KB`, robots из БД (ADR-0009). **Латентный прод-баг починен cutover'ом:** Gotham-Pro.css был 0B на RUVDS → теперь 200/4436B (шрифт заголовков живой). Rollback = revert DNS→80.64.31.36 (RUVDS IIS жив, не тронут) ИЛИ PUT стека 20 назад на `tandemmebel.vds.kzntsv.site`. Compose source-of-truth обновлён. Тираж snolla 0.42.x теперь **полностью живой** (5/5 стеков на VDS)._ -_Updated: 2026-07-04 (8) — 🟢 **[tandemmebel-web-vds-deploy] gallery-дефект ЗАКРЫТ на staging — GREEN.** Workshop прислал коррекцию sha → ребилд на `b02ca18` (core 0.16.2, SlugFeedPage-fix). Собрал `tandemmebel:b02ca18` (digest f8672218), передеплой стека 20 (env 8/8, healthy). DoD-чек **12/12 GREEN**: gallery-грид staging == prod ТОЧНО на всех роутах (bedrooms 80/80, kids 138/138, office 26/26 и т.д.). Крошки наполнены (`Главная/Мебель/Галерея идей`, были пустые), title полный (`Фото Офисная мебель…`), байты 28508≈прод 28253. Я флагал сомнение (мой DB-дамп показал: `/gallery` — конвенционный саб-роут, а не feed; SlugFeedPage-fix мог промахнуться) — но жёсткий рендер-чек показал GREEN, фикс сработал. **Cutover ОТЛОЖЕН оператором (не забыть!) — триггер запуска = владелец сайта меняет DNS reg.ru→89.253.255.94**, тогда verify авторит.NS→боевой Host в стек 20→live-smoke (порядок в [[NEXT_SESSION.md]]). Прогеру после разноса от оператора отправлен рабочий green-хендофф на свип 188 URL. RUVDS=rollback._ -_Updated: 2026-07-04 (7) — 🔵 **[tandemmebel-web-vds-deploy] дефект идентифицирован = gallery FeedPages-VM.** Workshop прислал ops-таск: ребилд стека 20 на `a173401` (FeedPages page-type портирован, чинит gallery 404→200). Гейт green (origin HEAD=a173401 ⊇ a173401+22bd8f2), собрал `tandemmebel:a173401` (snolla 0.35.0/core 0.16.0/data 0.13.0, digest 6962bd97), передеплоил стек 20 (Portainer API, env 8/8, healthy). Роутинг чинится: 12/12 furniture gallery-роутов 404→200. **НО рендер-чек RED — grid ПУСТОЙ на всех роутах** (staging `/galleries/*/images` = 0 vs prod 24/80/62) + **breadcrumb-подписи пустые** (`itemprop=name` без текста). Оба симптома = один корень: FeedPage-VM не прокидывает `item` в шаблон. Оператор дефект подтвердил независимо. Мой деплой чист; фикс на прогере (tandemmebel.ru). Пропинговал прогера с уликами (empty-grid + breadcrumb addendum) → ждёт новый sha, прогоню тот же DoD (staging_imgs==prod_imgs + непустые крошки). Cutover HELD. См. [[NEXT_SESSION.md]]._ -_Updated: 2026-07-04 (6) — 🔵 **[tandemmebel-web-vds-deploy] BLOCKED на новом дефекте.** Дал оператору staging-URL (https://tandemmebel.vds.kzntsv.site) на визуальную проверку sharp-сборки → оператор нашёл НОВЫЙ дефект (иной, чем прошлые глиф/imgproxy), специфику не назвал, «чиним в новой сессии». Cutover HELD. Мои автоматические гейты по sharp-staging были green (media/watermark/variant-cache/parity) — значит дефект визуальный, не пойманный curl-смоком. Sharp-staging жив на стеке 20 для разбора в новой сессии. См. [[NEXT_SESSION.md]]._ -_Updated: 2026-07-04 (5) — 🟢 **[tandemmebel-sharp-staging-rebuild] closed — SHARP staging GREEN.** Пивот tandemmebel на in-process sharp завершён. Образ `tandemmebel:68b93a9` (snolla 0.34.0/core 0.15.0, digest 12288b15) собран на VDS, стек 20 обновлён (env verbatim 8/8, imgproxy из tandem-пути убран). Parity-smoke GREEN: nav 8/8, sitemap staging⊇prod (prod-only=0, +12 categories benign), 2012-посты 31/31, redirects 301/301; sharp media = slug-URL `/galleries//images//` 200 webp serve-bytes, **0 /imgproxy-рефов** (осиротевший imgproxy-конфиг в пине эмпирически мёртв → heads-up воркшопу вычистить), variant-cache пишется (32 объекта content-addressed), watermark визуально ✓ featured+lightbox / gallery-small чистый. [tandemmebel-web-vds-deploy] теперь 🟡 sharp-staging-green, ждёт **cutover DNS — gated оператором** (reg.ru→89.253.255.94). RUVDS=rollback._ -_Updated: 2026-07-04 (4) — 🟢 **[minio-variant-cache-bucket] closed.** sharp-prereq. Создан бакет `variant-cache` в shared MinIO (minio.kzntsv.site/books-vds) — S3-эквивалент легаси App_Data/imageCache, куда sharp кладёт resize+watermark варианты on-demand (без него 500 NoSuchBucket на всех sharp-картинках = agent-блокер 1 из tandemmebel sharp-verify). Грант: snolla S3-креды == MinIO root (сверено) → в mode-server-fs полный доступ by construction, IAM не нужен. Write-verify под деплойными кредами: put→stat→get(md5 match)→delete→gone ✓. Другие 17 бакетов не тронуты, lifecycle не ставил (кэш регенерируем). Готовит почву под sharp-переезд tandemmebel (движок victor/snolla media-sharp-imageprocessor-port в переработке)._ -_Updated: 2026-07-04 (3) — 🟢 **[imgproxy-stack29-watermark-rollback] closed.** Оператор сменил watermark-подход: tandemmebel уходит с imgproxy-watermark на in-process sharp (victor/snolla media-sharp-serve-and-watermark) → глифу на SHARED стеке 29 не место. Снял `IMGPROXY_WATERMARK_DATA` со стека 29 (books-vds): вырезал строку → результат byte-identical pre-watermark бэкапу (cross-check true) → PUT+redeploy+purge. Verify (не задеть боевых): pilorama98 (главный консюмер) 3 рендера == baseline (43950/172042/160010B) не задет; stostayer НЕ imgproxy-консюмер (картинки /galleries//images//assets/) → не затрагивается; WATERMARK_DATA present:false; tandem featured→чистый (ожидаемо, staging→sharp). Глиф-бинарь 7127e92a оставлен для sharp. [tandemmebel-web-vds-deploy] теперь ждёт ПЕРЕСБОРА образа на sharp-пин перед cutover. Notify workshop._ -_Updated: 2026-07-04 (2) — 🟡 **[tandemmebel-web-vds-deploy] STAGING GREEN, ждёт cutover.** Финал тиража tandemmebel. Образ `tandemmebel:0facb35` (snolla@0.32.2/core@0.13.8, digest 464d2f22, watermark-override в пине) собран на VDS (обход 499). Стек Portainer **Id 20** (staging-rule `tandemmebel.vds.kzntsv.site`, env verbatim из labtools стека 17, 8/8), контейнер healthy MSSQL+S3. imgproxy-nginx кэш (books-vds стек 29) purged → featured отдаёт вотермарк без cache-bust (27820B). Staging-smoke с VDS GREEN: status-parity 10/10, sitemap staging⊇prod (prod-only=0, +12 /projects/categories/*=benign#3, /articles 404 parity), trailing 301/301, blog-post 200 (+3.5KB=benign#2 imgproxy vs /galleries/ 26/26), 2012-посты 31/31, theme 21/22 MD5-OK. Находка (не блокер): prod Gotham-Pro.css=0B пустой, staging=4436B корректный → cutover чинит латентный прод-баг шрифта. **HOLD cutover** — жду отмашку оператора на DNS reg.ru→89.253.255.94; порядок: verify авторит.NS → ТОЛЬКО потом боевой Host-rule (иначе LE упадёт на RUVDS). RUVDS=rollback._ -_Updated: 2026-07-04 — 🟢 **[imgproxy-watermark-glyph-books-vds] closed.** Ops-подхват по тиражу tandemmebel: воркшоп попросил выставить глиф-вотермарк на imgproxy стек 29 (books-vds). Первый base64 (inbox И закоммиченный таск) был БИТЫЙ на источнике — `tasks_create` порезал 10КБ-поле (sha `f8f0…`≠`e995…`, IEND нет, зацикленный хвост `FQtAlGLzMfBc`×N); поймал sha-сверкой ДО прода (не запушил битьё → imgproxy не упал). Воркшоп до-доставил `logo.png` бинарём в git (`host-stacks/books-vds/tandemmebel-watermark-logo.png`, commit `7127e92a`, sha256 `e995…971fc`, 8015B, IEND ok). PUT стека 29 (Portainer `portainer.kzntsv.site` ep1, X-API-Key, `IMGPROXY_WATERMARK_DATA` inline, `PullImage:false`, БЕЗ глобального `_OPACITY`). Smoke cache-busted: A tandem вотермарк ВИДЕН глазами (27294→27820B); B pilorama контроль byte-identical (160010B) — opacity не просочилась. Notify workshop → tandem-агент на ре-скрин→пин→deploy. Дальше жду `[tandemmebel-web-vds-deploy]`._ -_Updated: 2026-07-03 — 🟡 **заведена [morethencms-s3-filestorage-provider].** Разведка «почему /admin/assets/.../delete → 500» на RUVDS IIS: корень = app pool `IIS AppPool\snolla` имел на `App_Data` только RX после scp-миграции → `File.Delete` UnauthorizedAccessException (не MinIO, провайдер `assets`=Local; NRE-версии исключены проверкой строк Folders/Files в БД). Fix применён: `icacls App_Data /grant "IIS AppPool\snolla:(OI)(CI)(M)" /T` (44293 файла, verified). Попутно заменён `labtools-price.pdf` в ОБА хранилища (MinIO ETag→6426ddd0 + локалка, MD5 сверены). Настоящее закрытие split-brain (админка=Local vs фронт=MinIO) → новая таска на S3-провайдер `MoreThenCms.FileStorage.S3` (name approved): ТЗ доставлено в инбокс MoreThenCms, объём=все контентные классы, 1 MinIO, deploy на локальный IIS windows-recovery-host→боевой. DNS-аудит: инфра-хосты не перехватываются (нет 127.0.0.1). Concepts: snolla-admin-appdata-acl-500 + galleries-storage-class-local-not-s3._ -_Updated: 2026-06-29 (4) — 🟢 **[pilonuxt-deploy-sitemap-taxonomy] closed.** pilonuxt `6b9ae63` LIVE (стек 16) — bump-only sitemap taxonomy: content-api 0.15→0.16, core 0.2→0.4 (taxonomy enum, прокси не менялся). **Tree-check `.output` 5/5** (content-api 0.16.0, core 0.4.0, data 0.9.1 1×, snolla 0, btoa 0). Push словил **499 на .output** → ретрай прошёл (digest `226584ce…`). Acceptance 4/4: `/sitemap.xml` индекс 7 записей с `store-taxonomy-1`; `/sitemap-store-taxonomy-1.xml`→200 57 url абсолютные (35 categories+22 tags, vendors 0=пусто в CMS); v1 цел (products-1/pages-1 200); `taxonomy-999`→404. Регресс `/`/`/catalog` 200, stderr чист. Откат: стек 16→`42f9d64`. Ack→victor/pilorama98.ru._ -_Updated: 2026-06-29 (3) — 🟢 **[pilonuxt-deploy-sitemap] closed.** pilonuxt `42f9d64` LIVE (стек 16) — sitemap.xml через content-api@0.15.0 (+core@0.2.0). Build из монорепы (форс schema-gen против прод-MSSQL). **Tree-check `.output` 5/5:** content-api 0.15.0, core 0.2.0, data 0.9.1 (1×), snolla 0 (7 grep-хитов = комменты-провенансы, 0 import), btoa 0. Push **напрямую** (499 не случился). Acceptance 3/3: `/sitemap.xml`→200 `` 6 источников; `/sitemap-pages-1.xml`→200 `` 70 url все абсолютные; `/sitemap-store-products-999.xml`→404. Регресс цел (robots/`/`/`/catalog` 200), stderr чист. Откат: стек 16→`cf2bba2`. Ack→victor/pilorama98.ru._ -_Updated: 2026-06-29 (2) — 🟢 **[deploy-pilonuxt-forms-api-drop-snolla] closed.** pilonuxt `cf2bba2` LIVE (стек 16) — ADR-0010: формы на `@snollajs/forms-api`, `@snollajs/snolla` дропнут целиком, core@0.1.1. Перед перекатом **tree-check `.output` 4/4** (0× snolla, 0× btoa, 1× data@0.9.1, core 0.1.1 + content-api 0.14 + forms-api 0.1.0). Smoke зелёный: SSR `/`+`/catalog`→200, robots/yandex из БД (ADR-0009 не регресс), форма `?path=/checkout` пустой→422 JSON / `/nope`→404 (valid НЕ слал). Гоча: 422 только с `Accept: application/json`. Notify victor/pilorama98.ru (монореп-inbox)._ -_Updated: 2026-06-29 — 🟢 **[deploy-pilonuxt-static-pages-robots] closed.** pilonuxt `40bb383` (DB-backed staticPages + robots.txt, ADR-0009) LIVE на проде (Portainer стек 16). Build из монорепы с **форс-регенерацией клиента** (`rm src/generated`+`schema-gen 0.6` → новые методы `getStaticPage`/`getRobotsTxt`); push с дома 499 на `.output`-слое → fallback `docker save | ssh vds load`+push с VDS. Smoke acceptance 6/6 verified телами (robots полный из БД с Yandex Clean-param+Sitemap, yandex/google верификации, /nope→404, /+/catalog→200). Notify victor/pilorama98.ru отправлен._ -_Updated: 2026-06-18 (2) — 🟢 **[books-task-runner-registry-auth-cred] closed.** Кред books-ci (секция `registry`, полная) в task-runner config-volume на books-vds (644 root, бэкап, EACCES не повторён). После deploy нового кода (`master-bf2a8c5`) прогнан `registryGc {dryRun:true}` через scheduler-dispatch: **success, 401 нет**, repos books-* видны (7), отчёт сгенерён. Acceptance auth/dryRun met. **Finding отдан books (НЕ блокер):** GC бежит, но `drop=0` всегда (keep=tags−1) — `getCreated`→null для всех манифестов (configDigest не извлекается, OCI image-index?), `planDeletions` защищает null-dated группы → keepLastN не применяется, реестр не чистится. Bug в books `lib/registryV2.js`, owner — books._ -_Updated: 2026-06-18 — 🔵 **[books-task-runner-registry-auth-cred] blocked.** Кред books-ci (секция `registry`, baseUrl+username+password — полная, bind-mount затеняет config образа) положен в task-runner config-volume на books-vds; mode 644 root сохранён (бэкап `.bak-pre-registry-2026-06-18`, EACCES-инцидент 25.05→18.06 не повторён), container restart→healthy, `config.get('registry.*')` резолвится. books-ci валиден: `/v2/_catalog`→200, репы books-* видны, 401 нет. **Блокер:** работающий образ (88f2bee, 17.06) содержит СТАРЫЙ registryGc (Gitea API), тега `master-a3734d5` в реестре нет → CI не собрал v2-DELETE rewrite. In-app dryRun ждёт build+deploy. Inbox-ответ books отправлен._ -_Updated: 2026-06-13 — 🟢 **[migrate-assets-originals-to-s3] closed.** assets-оригиналы (весь `App_Data\assets`: 4750 obj / 215.5 MiB, 145 owner-папок) залиты в новый MinIO bucket `assets` (books-vds) rclone'ом с RUVDS IIS, ключи `/` verbatim. Verify count+size == source. Smoke (мимо LAN-DNS): brevno.jpg → 200 image/webp 64556 B (был 404), валидно-подписанный несуществующий объект → 404 (негатив-контроль). Код snolla не менялся (path A); scope намеренно шире pilorama98 — закрывает класс-404 для всех тенантов snolla, регрессии нет (shared bucket, как galleries). ⚠️ snolla-side остаётся `content-api/routes/assets.js` (пустой stub) — без него картинки не рендерятся на сайте. Inbox victor/snolla отправлен._ -_Updated: 2026-06-12 (4) — 🟢 **[migrate-gallery-originals-to-s3] closed (scope pilorama98).** Галереи snolla не отдавались (imgproxy 404) т.к. в legacy `storageClient="galleries"` = тип **Local** (диск `App_Data\galleries\`), НЕ S3 — оригиналы никогда туда не заливались (продукты — заливались, bucket `pilorama98`). Развилка A/B закрыта фактами MinIO → **A**: создан bucket `galleries`, залиты 301 файл (77 MiB) pilorama98 (siteId `37e67fc4…`) с RUVDS IIS через rclone → `s3://galleries//.jpg` verbatim (== ровно то, что строит `middleware/galleries.js:47`). Код snolla НЕ менялся. Verify: count+size == source. Smoke с books-vds: реальный объект imgproxy → 200 webp, fake → 404. Уточнение инфра: `imgproxy.kzntsv.site` с 08.06 на **books-vds** (стек 29/30), не на windows-host — `minio-imgproxy-on-vds.md` был stale. Inbox victor/snolla отправлен._ -_Updated: 2026-06-12 (3) — 🟢 **bookva-es snapshot done** (не отложен). Portainer stack 37 пересоздан с `path.repo=/snapshots`+bind (PUT API, том цел, epz/products уцелели), repo `kreknin` зарегистрирован, snapshot-блок в `run.sh`. Поймана коллизия basename `snapshots` (оба ES-каталога одноимённы → сливались в один dest-репо, порча обоих) → источник bookva-es = родительский `/usr/docker/bookva-es`. Verified: `BOOKS-VDS backup OK 10m28s`, на kreknin раздельно `snapshots/`(slovo index-41) + `bookva-es/snapshots/`(index-4). Таска `bookva-es-snapshot-repo` 🟢 closed._ -_Updated: 2026-06-12 (2) — 🟢 **bookva-* backup gap закрыт.** bookva tenant (db/mongo/es/minio, поднят 26.05) не бэкапился — books-скрипт написан 25.05 до bookva, покрытие не расширили (висело wiki-follow-up #2). Добавлены bookva-db (тот же root pw), bookva-mongo (no-auth), bookva-minio (raw volume) в `books-vds-backup-daily-kreknin/run.sh`; деплой == репо (`.bak-pre-bookva`). Verified green: `BOOKS-VDS backup OK 10m42s`, артефакты на kreknin (bookva-mariadb 257M, bookva-mongo 3.2M, bookva-minio 1.4G). bookva-es отложен → `bookva-es-snapshot-repo` (покрыт slovo-снапшотом, индексы идентичны). Правило в память: новый stateful → бэкап в том же коммите._ -_Updated: 2026-06-12 — 🔴→🟢 **MSSQL backup incident + estate backup audit.** VDS daily backup падал с 12.06 (line 108, exit 1): mssql-блок (added 11.06) использовал `WITH ... INIT` → упирался в компрессованный media-header майских `.bak` (Express не пишет COMPRESSION) → молча падал первым cron-запуском. **5 боевых CMS-баз 3 недели без offsite-копии.** Fix: `INIT`→`FORMAT`, прогон verified (MoreThenCms 910M/StayerCalculator 528M/StayerPrice 39M/TireService 4.5M/stostayer 990M на kreknin). Репо-копия `run.sh` синхронизирована (mssql-блока не было). Concept: `.wiki/concepts/mssql-on-vds.md` gotcha#2. **Estate backup-аудит** → `.wiki/concepts/backup-inventory-2026-06.md`: ✅ openwrt UCI backup настроен (cron 03:30 → kreknin, restricted forced-command key); ⚪ заведена `kreknin-self-backup` (#1 SPOF, на потом); nl-vds x-ui.db — user: не нужен; windows-host stale script — user: забыть._ -_Updated: 2026-06-08 — 🟢 ad-hoc fix `maljarka.tandemmebel.ru` (iis-migration follow-up): домен переехал на RUVDS корректно (DNS/TLS/IIS ок), но отдавал 502 **только на HTTPS**. Root cause не в миграции: у тенанта maljarka `dbo.Sites.SettingsData=NULL` (нет блока `httpSecure`) → MoreThenCms `SnollaMiddleware` бросает KeyNotFound на HTTPS-ветке → 502; по HTTP 200. Fix: `UPDATE dbo.Sites SET SettingsData='{httpSecure:enableHttps=true,…}' WHERE SiteId=a2476738… AND SettingsData IS NULL` + `Restart-WebAppPool snolla`. Verified maljarka:443→200 (server-local + external по 80.64.31.36), kupimknigi не задет. Audit: maljarka — единственный NULL-сайт с :443-биндингом из 25. rimiz degraded по др. причине. Concept: `.wiki/concepts/morethencms-null-settingsdata-https-502.md`._ -_Updated: 2026-06-05 (вечер) — 🟡 `fix-nl-vds-reality-pq-dest`: по команде user применён server-side fix + ротация кредов. Reality dest intel→microsoft (x-ui.db + restart), сквозной тоннель через реальный :443 = HTTP 204; 32030 жив. Креды ротированы (root SSH / panel user+pass / panel secret-JWT — светились в плейнтексте; новые в `pass nl-vds-3xui/full-env`, старые отклоняются, БД-бэкап на сервере). Остался client-side: user меняет SNI в v2rayN (`pqmkayaxo2`→microsoft) + подтверждает → close._ -_Updated: 2026-06-05 — заведена `fix-nl-vds-reality-pq-dest`. Новый NL VDS (213.176.64.253, 3x-UI/Xray 26.6.1): диагностирован отказ Reality-инбаунда 443 — **ML-DSA-65 (PQ) × dest `www.intel.com` (Akamai шлёт HRR)**; plain VLESS 32030 работает. Узел+root-cause в вики (`nl-vds-3xui`, `reality-pq-mldsa65-dest-incompatibility`); креды в `pass nl-vds-3xui/full-env`._ -_Updated: 2026-05-31 — 🟢 `vehicles-loader-progress-deploy` **closed**: live-verify прогона Sun 31.05 06:21→07:53 MSK прошёл — progress-logging показывает движение по всем фазам (▶/батчи/✓/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 подтверждена тест-письмом (`250 queued`). Follow-up'ы (не блокеры): units-фаза 1h31m на 100k rows = узкое место; generation `unmatched:51`; репо `config/default.json` дефолт `to` всё ещё site@ (прод перекрыт env)._ -_Updated: 2026-05-30 — 🟡 `vehicles-loader-progress-deploy`: деплой 0.4.0 ВЫПОЛНЕН (build здесь → push `docker.stostayer.ru` → host pull + re-tag `:latest`=0.4.0, 0.3.0 retained). Acceptance #1 ✅. Verify-окно = natural (user-выбор, не форсим baseline → клиент без внепланового email). timer next trigger Sun 2026-05-31 06:21 MSK → live-verify journalctl в след. сессию. Open Q #1 (push кода) снят: `a74ef73` уже в origin/master._ -_Updated: 2026-05-30 — заведена ⚪ `vehicles-loader-progress-deploy` (handoff из stostayer.new). Progress-logging 0.4.0 зашипан в коде (stostayer.new `a74ef73`, 24/24 теста); ops-follow-up — rebuild образа той же схемой (build здесь → push `docker.stostayer.ru` → host pull+re-tag) и снять `journalctl` с боевого прогона (закрывает не-верифицированный 5-й критерий «journalctl показывает движение»). Попутно добивает остаток email-SEND из `vehicles-loader-image-distribution`._ -_Updated: 2026-05-29 — 🟢 `vehicles-loader-image-distribution` **closed**: канал поставки = собственный registry клиента `docker.stostayer.ru`, build у нас (verdaccio-депы запекаются → клиенту verdaccio не нужен), без Portainer (oneshot + systemd-timer). Доки финализированы (stostayer.new `e55cfba`). Открытие `pass stostayer/client` показало свою инфру клиента (Portainer+registry) → развилка A/B пересмотрена. Фактический prod-деплой — follow-up, ждёт grant'а._ -_Updated: 2026-05-29 — **РЕЦИДИВ того же инцидента, диагноз сменён.** Вчерашняя гипотеза «оператор в cutover» **опровергнута**. Истинная причина: ES публиковал `0.0.0.0:9200` мимо traefik (free-ES без auth) → **ransom-бот** удалял индексы by-name (мимо Control #1), оставлял `read_me` с BTC-выкупом. accessLog (Control #2) пуст — бот шёл прямо в порт. **Control #3 (fix):** убрана публикация host-порта (Portainer PUT stack 33) → дыра закрыта; `epz/products/artmone` restore из `daily-2026-05-25`. Второй, отдельный баг: epz-поиск падал у ОБОИХ тенантов — `config.get("tenant")` (node-config) не задан ни в `default.json`, ни в env-маппинге, `TENANT` env был мёртвым грузом → добавил `tenant` в overlay `default.json` (slovo/bookva), резолвится, ошибки прекратились (products работал — другой код-путь). accessLog откатан (сторожил не ту дверь). Exposure-audit → ⚪ `harden-books-vds-exposed-ports`. См. `.wiki/concepts/es-destructive-delete-incident-2026-05-26.md` § «Рецидив 2026-05-29»._ -_Updated: 2026-05-28 (вечер) — 🟢 `restore-elasticsearch-indices-books-vds` **closed** в ту же сессию через snapshot restore (46 сек) из `daily-2026-05-25` (полные данные, ~2ч после оригинального reindex'а). RCA: 5 индексов (включая system `.tasks`) удалены через ES API `DELETE _all` за 1 сек на 2026-05-26 10:21 UTC, **1ч 11мин после создания bookva-es** — оператор в cutover-prep попал на canonical вместо internal-only bookva-es. Caller identity не восстановим (audit log = X-Pack платный, traefik accessLog был выключен, Portainer audit = enterprise). 2 preventive фикса applied + verified: (1) ES env `action.destructive_requires_name=true` — `DELETE _all` / wildcard теперь 400; (2) traefik JSON accessLog в `/letsencrypt/access.log` — будущие DELETE оставят forensic след. См. таску + `.wiki/concepts/es-destructive-delete-incident-2026-05-26.md`._ -_Updated: 2026-05-28 — **prod incident**: books-app slovo поиск товаров + EPZ сломаны, ES `elasticsearch.kzntsv.site` (books VDS stack 33) пуст (0 индексов из 3 ожидаемых; source `elasticold.kzntsv.site` rollback — все 928k docs на месте). Заведена ⚪ `restore-elasticsearch-indices-books-vds`. Окно поломки: 27.05 10:24 → 28.05 13:59, в этом окне шла работа bookva-cutover-prep (bookva-es stack 37 создавался) — возможный конфликт. См. таску для playbook'а._ -_Updated: 2026-05-28 — **vds-kzntsv network-stack mismatch RESOLVED** в 13:52 MSK. Сначала ~2.5ч активного outage (05:45-08:30) + ~5.5ч на эфемерной статике до окончательного fix хостером. Revised RCA: наш netplan+networkd поверх provider's expected ifupdown stack ломал их auto-recovery когда DHCP-binding разорвался на их стороне. Fix: `systemctl mask netplan systemd-networkd` (на running system, без stop — IP и SSH сохранились), Rusonyx ребутнули + положили чистый `/etc/network/interfaces.d/ifcfg-eth0` с /18 netmask через свой `start/ipadd` procedure. Все 24 docker контейнера up. Disk after GC: 79% (132G→118G/158G). Anti-pattern закреплён: НЕ использовать netplan на Rusonyx VDS. Wiki updated с revised RCA + permanent-fix runbook + 2 quirks (#9 /18 layout, #10 ifupdown vs netplan)._ -_Updated: 2026-05-27 — `modulair-rag-vds-redeploy` 🟢 **closed** в ту же сессию: 4-контейнерный стек развёрнут на VDS (Portainer stack 15), acceptance 6/6. Образы пересобраны на самом VDS (push 3.36GB через traefik с дома падал 499); env/entrypoint/minio-host скорректированы под VDS-реальность. 3 follow-up'а переданы в modulair-rag handoff._ -_Updated: 2026-05-27 — заведена `modulair-rag-vds-redeploy` ⚪ (handoff из modulair-rag session). NAS-loss redeploy 4 контейнеров на VDS. Блокер MinIO снят — verified up на VDS 2026-05-27. postgres `proxy`-network reachability подтверждён (`postgres:5432` резолвится из pipeline/mcp). Спека: modulair-rag concept `nas-loss-vds-redeploy-context` + compose.yml as-is._ -_Updated: 2026-05-27 (ночь, после re-open) — ops-mcp multi-tenant **activated** (stack 26 PUT + BOOKVA_MARIADB_PASSWORD env). Smoke verified: slovo/bookva DB queries возвращают tenant-specific data (АФО2 vs Ира warehouses), bookva agendaJobs count=2818. Один host-level books-ops-mcp видит обе tenant DBs._ - -_Updated: 2026-05-27 (ночь) — bookva-tenant deep-debug session. 6 багов одной природы (incomplete cutover-prep): (1) bookva-web без config volume → slovo DB leak; (2) bookva-{api,scheduler,task-runner} mount path /app→/usr/src/app; (3) NITRO/NUXT env не reference'или в compose; (4) bookva-scheduler без RECONCILER_ENABLED → dry-run handlers; (5) bookva-scheduler без healthcheck → CI timeout; (6) deploy.yml svc=job-scheduler vs secret=SCHEDULER mismatch. + composite agendaJobId display fix + books-web config mount (slovo also missed) + jobs.post.js agenda.db.collection→MongoClient. Ingested wiki concept tenant-overlay-config-volume-mount-path-pitfall (shared). ops-mcp multi-tenant код в image готов но stack 26 ещё не пере-PUT'нут (next session)._ - -_Updated: 2026-05-26 (поздний вечер) — `books-ops-mcp-host-promote` 🟢 **closed** в ту же сессию следом за заведением. Stack 26 (books-ops-mcp) in-place PUT в host-level compose без container churn. Stack 43 (bookva-ops-mcp) deleted. 2 Gitea secrets revoked. `victor/books ae3ab14` + `victor/bookva-overlay 50f5bbb` + `.admin` host-stacks/books-vds/ops-mcp.compose.yml. ops-mcp теперь management-plane (manual Portainer)._ - -_Updated: 2026-05-26 (поздний вечер) — заведена `books-ops-mcp-host-promote` ⚪ (deploy run#437/439 валился на bookva-ops-mcp restart-loop → hot-fix `bookva-overlay 437d024` idle-stub interim → user ideology «books VDS = host, slovo/bookva = клиентские стэки» → promote `books-docker-proxy-ro`+`books-ops-mcp` в host-level stack, drop bookva-ops-mcp). Deploy run#440 зелёный._ - -_Updated: 2026-05-26 (поздний вечер) — `bookva-tenant-cutover-prep` 🟢 **closed** — 6/7 в одну сессию: Steps 1+2+3+4+5+7. Step 6 delayed по spec (1-2 нед до cutover). bookva-ozon-mcp deferred (image не в registry). MinIO bucket copy deferred (cutover-time). 9 bookva Portainer stacks created (3 active: db/mongo/es internal-only; 6 stopped pre-cutover). bookva-overlay 4 commits: templates+mariadb:10.6+mongo:4.2+api internal-only+depends_on removed._ - -_Updated: 2026-05-26 (вечер) — `bookva-tenant-cutover-prep` 🟡 paused, 4/7 steps done в этой сессии: Steps 1+5+7 closed, Step 3 partial (6 empty volumes + 4 populated). Maintenance-window remaining: Step 2 stacks, Step 3 stateful (Mongo/MariaDB/MinIO), Step 4 login-gate. bookva-overlay templates align'нуты к prod shape + design v3 (commit `5b173de` pushed). books `75ea320` pushed (deploy/web.compose.yml embed-api refs). Pass entry `gitea/victor-books-ci-bookva-overlay` added._ - -_Updated: 2026-05-26 — заведена `bookva-tenant-cutover-prep` ⚪ (7-step prep на books VDS перед поднятием Bookva стека). Code-side done в victor/books master: embed-api с zod^4 fix, ES endpoint per-tenant, PWA redirect banner, deploy.yml per-tenant, overlay-репо finalize, ntfy per-tenant. Осталась только Portainer/Gitea-secrets/VDS работа._ - -_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.)_ - -## 🟢 [#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 -**Weight:** needs-claude -**Notify:** OpeItcLoc03/admin - - - ---- - -## 🟢 [#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 -**Weight:** needs-claude -**Notify:** OpeItcLoc03/admin - - ---- - -## 🟢 [#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. - -**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`. - -**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. -- Image `registry.kzntsv.site/on-snolla:473923e494db` (+:latest), built on VDS. -- Portainer stack Id 22, `mem_limit 512m`, 8 env secrets, rule `Host(on.snolla.com)`, LE cert. -- `on.snolla.com` DNS reg.ru A 80.64.31.36→89.253.255.94 (operator flip). RUVDS IIS untouched (rollback = revert DNS). -- Reconstruction: layout.liquid = bootstraptor landing byte-identical prod; form "Join us" baked (POST / → forms middleware); StaticPages /yandex + /4a052808276c; /c = 404 dead-link (parity). -- robotsTxt fix in index.js: on.snolla.com has no Domains row → snolla robotsTxt crashes on undefined app.locals.domain; synthesized domain drop from site.robotsTxt. -- Completeness-gate: sitemap 3/3 parity, homepage + /yandex byte-identical, 0 regressions. - - -**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 - - ---- - -## 🟢 [#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:** — (закрыто по оператору). -**Branch:** n/a (admin ops + external code) - - ---- - -## 🟡 [#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) - - ---- - -## 🟢 [#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 - - ---- - -## ⚪ [#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) - - ---- - -## 🟢 [#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 - - - ---- - -**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 - - - - ---- - -**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) - - ---- - -## 🟢 [#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 ошибок. - -Таска писалась по stale-предпосылкам — по ходу исправлено (см. [modulair-rag-vds-redeploy.md](modulair-rag-vds-redeploy.md) Decisions): registry-образы отсутствовали (registry переустановлен 2026-05-20) → **rebuild на VDS** (push 3.36GB tier1-слоя с дома падал 499 через traefik → собрал на самом VDS, push локальный); `MINIO_ENDPOINT`=`minio.vds.kzntsv.site` (не `minio.kzntsv.site`=books VDS); traefik entrypoint `https`→`websecure` (NAS-имя в compose-лейбле); DB+bucket+scoped-svcacct созданы; ROUTERAI key в pass; DNS поправил user. - -**Follow-ups (не блокеры acceptance):** modulair-rag/compose.yml entrypoint-фикс в репо; portainer-stack.md под VDS; lightrag embedding binding=ollama/model=None — проверить ROUTERAI_* маппинг. Все переданы в handoff (consumer repo / parallel session). -**Branch:** master - - - - ---- - -**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. - -**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). - -**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). - -**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). - ---- - -**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). - -**Контекст dev-source:** victor/books `.wiki/concepts/tenant-split.md` rev v3. Code-side: `5e28fd1` embed-api+zod^4, `4a9cafc` ES endpoint, `c1e58cf` PWA redirect, `88df172` deploy.yml per-tenant. Overlay-repos: **bookva-overlay/main `8bffd68`** (templates aligned + mariadb:10.6 + mongo:4.2 + api internal-only + depends_on removed). - -**Done в сессии 2026-05-26:** -- ✅ Step 1: 7 Gitea secrets (BOOKVA_OVERLAY_TOKEN, HEALTHCHECK_BOOKVA_{API,WEB}_URL, PORTAINER_STACK_ID_BOOKVA_{API=45,WEB=46,SCHEDULER=47,OPS_MCP=43}). books `2f7b539` (secret name fix). -- ✅ Step 2: 9/10 Portainer stacks. Stack IDs: db=34, mongo=36, es=37, ops-mcp=43, ntfy=44, api=45, web=46, scheduler=47, task-runner=48. ozon-mcp deferred (image not in registry). User-facing stacks (api/web/scheduler/task-runner/ops-mcp/ntfy) **stopped** post-create (Status=2) — bookseller.kzntsv.site returns 000 как desired pre-cutover state. db/mongo/es оставлены running (внутренние, useful для testing). -- ✅ Step 3: 6 volumes created. **bookva-db-data (4.7G)** = cp -a `/usr/docker/books-db/data` (downtime 59s). **bookva-mongo-data (517M)** = cp -a `/opt/books/job-scheduler/mongo/db` (downtime 5s). 4 config volumes populated из corrected overlay templates (api, scheduler, task-runner, web-branding). 2 empty by design (ntfy-data, es-data — Phase 2 snapshot/restore). MinIO deferred (no maintenance impact — `mc cp books bookva` at cutover, не rename). -- ✅ Step 4: bookva-db login-gate `UPDATE users SET password=UUID() WHERE id_user NOT IN (1,2)` — 3 users scrambled (id=3,4,5), id=1 (Bookva founder) + id=2 (Slovo founder) untouched. books-db verified unaffected. -- ✅ Step 5: DNS `bookseller.kzntsv.site` → 89.253.255.133 (был pre-existing, closed by inspection). -- ✅ Step 7: Portainer books-web stack 24 PUT atomic (compose+env). Env: `NUXT_PUBLIC_BOOKS_API_URL=/api`, `NUXT_AUTH_TOKEN`, `NUXT_JWT_SECRET_KEY`. Embed-api на bookva.kzntsv.site/api/* живёт (smoke green, auth identical to books-api). books `75ea320` (compose template synced). Soak 24-48ч до books-api shutdown micro-task. -- 🔵 Step 6: delayed по spec (за 1-2 нед до cutover — `NUXT_PUBLIC_BOOKVA_REDIRECT_URL=https://bookseller.kzntsv.site` env на books-web для PWA redirect banner user.id=1). - -**Deferred (отдельной micro-task):** -- `bookva-ozon-mcp` stack — image `registry.kzntsv.site/books-ozon-mcp:master` не существует в registry. Нужен build в victor/books CI (ozon-mcp service в build.yml?). Скорее всего service ещё не настроен — отдельная task'а. -- Mongo agenda cleanup partial — 7 jobs с idSeller=2 удалены, осталось 2817 jobs (большинство — system-wide без idSeller). При cutover проверить если нужен дальнейший cleanup. - -**Branch:** master | **Closure pushes:** books `2f7b539`+`75ea320` (master); bookva-overlay `5b173de`→`8bffd68` (main): templates align + mariadb:10.6 + mongo:4.2 + api internal-only + depends_on removed. Pass: `gitea/victor-books-ci-bookva-overlay`. - - - ---- - -**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. - -**Design locks (см. [books-ops-mcp-host-promote.md](books-ops-mcp-host-promote.md) § Closure note):** Q1 location=`.admin/host-stacks/`, Q2 deploy=manual Portainer, Q3 MariaDB scope=slovo's only (Option A), Q4 config volume path unchanged, Q5 audit прочих host-level кандидатов punt. - -**Commits:** `victor/books ae3ab14` + `victor/bookva-overlay 50f5bbb` + `.admin` (this commit). -**Branch:** master - - - ---- - -**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-тасками потом. - -**Контекст dev-source:** дизайн в victor/books `.wiki/concepts/tenant-split.md` § «Фаза 1». Compose-файлы готовы в `bookva-overlay` / `slovo-overlay` (commit d0eb210 в 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:** — -**Branch:** master - - ---- - - -## 🟢 [#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. - -**Scope exception (2026-05-24 user decision):** `tandemmebel.ru` + `www.tandemmebel.ru` ОСТАЮТСЯ на windows-IIS на неопределённый срок (отдельное решение user'а — site не готов к cutover ровно сейчас). DNS НЕ свапать. Cert на RUVDS уже импортирован, binding existing — может оставаться idle, traffic не пойдёт. - -**Next action:** -1. **Снизить DNS TTL** в reg.ru на оставшиеся **22 hostnames** до 300s — `pilorama98.ru`, `labtools.{ru,pro}`, `snolla.com` + 11 snolla subdomains + `rimiz.{ru,snolla.com}` + `maljarka.tandemmebel.ru` (но не `tandemmebel.ru` — см. Scope exception). Сократит cache-tail с 24h до 5 мин. -2. **24h soak kupimknigi + emspb** — verify через cache-clean resolver (потом curl без `--resolve`). -3. **Bulk DNS swap** оставшихся 22 hostnames на 80.64.31.36 (single sitting). **tandemmebel.ru / www.tandemmebel.ru — пропустить.** -4. **1-week prod soak** с RUVDS как live source для migrated hostnames. -5. **Decommission source IIS:8089** только для migrated hostnames (snolla catch-all site нельзя decommission'ить пока tandemmebel.ru на нём же). Решение defer до tandemmebel migration. -6. **LE renewal pipeline** — win-acme + HTTP-01 на RUVDS после full cutover (LE certs expire 2026-07-22, soak window до ~07-15). -7. **Cleanup migration-temp:** `Remove-NetFirewallRule 'smb-from-source','ssh-from-source'` на RUVDS, удалить `~/.ssh/ruvds-iis-migration*` на source, очистить `C:\ProgramData\ssh\administrators_authorized_keys` на RUVDS. - -Полный план + Completed + Open questions + Remaining steps — в [iis-migration-to-ruvds.md](iis-migration-to-ruvds.md). -**Branch:** master - - - -**Branch:** master - ---- - -## ⚪ [#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 - - - ---- - -## 🔵 [#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`. - -**Direction:** Slovo остаётся на текущем VDS, Bookva переезжает на новый. - -**Acceptance:** -- Новый VDS у провайдера (Rusonyx или другой по предпочтению Bookva), billing-account оформлен на юрлицо Bookva. -- Docker + traefik + certbot развёрнуты. -- DNS `*.bookva.` готов резолвиться на новый IP (фактический switch — в `books-dns-cutover-bookva`). -- 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`). - -1. Согласовать с учредителем Bookva: provider, configuration (CPU/RAM/disk), регистрация billing'а на юрлицо Bookva. -2. Provision VDS, базовая настройка (firewall, fail2ban, SSH ключи). -3. Установить Docker + docker-compose. -4. Развернуть traefik + certbot из overlay-репо `victor/books-bookva/deploy/traefik.compose.yml`. -5. Подготовить DNS-зону `bookva.` (создать A-records, TTL=300 для возможности быстрого switch'а). -6. Smoke: `curl https://placeholder.bookva.` → 200 от nginx-placeholder или из traefik. -7. Документировать в `victor/books-bookva/README.md`: hostname, IP, как ssh, runbook smoke-теста. -8. Закрыть с note: «VDS поднят, IP=<...>, готов к stack deploy». -**Blocker:** `OpeItcLoc03/admin books-stateful-split-execution` (Фаза 1 — physical split на shared VDS, bookva-* контейнеры живут) + `victor/books per-tenant-backup-and-observability` (Фаза 2 — per-tenant backups настроены до физического переезда) -**Branch:** n/a - - ---- - -## 🔵 [#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». - -**Acceptance:** -- DNS `*.bookva.` (или конкретный hostname Bookva — определяется учредителем) указывает на новый VDS IP. -- Старый bookva-стек на shared VDS остаётся запущенным, **без публичного hostname** (traefik labels убраны или disabled), доступен только по docker-сети для возможного отката. -- Smoke с двух разных сетей (mobile + офисный wifi) — `https://` отвечает с нового VDS. -- Per-VDS backup'ы на новом VDS подтверждены — есть snapshot спустя 24ч после DNS-switch'а. -- Через 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. - -1. Maintenance window 1–2ч (объявить за 7 дней). -2. Финальная синхронизация: `mysqldump mariadb-bookva` на текущем VDS → `mysql` на новом VDS (если schema/data разошлись с момента Фазы 2). -3. Smoke на новом VDS до DNS-switch'а: hosts-файл override → проверить login + base flow. -4. DNS-switch: меняем A-record(s) для bookva-hostname на new IP. TTL=300 уже стоял (Phase 4 step 1). -5. Через 1ч (TTL expiry + propagation): smoke с двух сетей. -6. На старом VDS — отключить traefik labels для bookva-стека (контейнеры продолжают работать, но не доступны снаружи). -7. **Parallel-run 7 дней.** Каждый день — smoke. Per-VDS backup на новом VDS — проверить хотя бы 1 успешный snapshot. -8. Если за 7 дней проблем нет → закрыть с note «cutover stable». Если есть — DNS rollback на старый IP, follow-up debug task в victor/books. -9. **Decommission** старого bookva-стека на shared VDS (отдельный шаг, не в этой таске — финальный backup в архив + `docker compose down` + volume drop). -**Blocker:** `books-vds-bookva-bootstrap` + victor/books `pwa-redirect-handoff` (SW bump выкачен в shared API за 1–2 недели до этой таски). -**Branch:** n/a - - ---- - -## ⚪ [#214 books-bookva-user-whitelist-gathering] — **Процессная** ops-таска для Фазы 3, шаг 1 дизайна `tenant-split` из victor/books. Получить от учредителя Bookva письменный список пользователей, которые допущены к Bookva-инсталляции. Без этого списка cutover не возможен — иначе утечка доступа: либо лишние юзеры получат доступ к Bookva (через копирование «всех users»), либо легитимные юзеры заблокированы (через пустой whitelist). - -**Direction:** Bookva — новая инсталляция, начинается с whitelist'а (закрытый список). Slovo — остаётся на текущем стеке, получает копию всех текущих users (статус-кво). - -**Контекст dev-source:** дизайн в victor/books `.wiki/concepts/tenant-split.md` § «Двухуровневая изоляция пользователей → App-level» и § «Фаза 3, шаг 1». - -**Не блокируется ничем** — можно начинать gathering сразу, лаг до cutover'а может быть значительным. - -**Acceptance:** -- Письменный список от учредителя Bookva (email или подписанный документ): ФИО + role (если знают). -- Список зафиксирован в `OpeItcLoc03/admin/.wiki/concepts/books-bookva-whitelist.md` или в отдельной таблице. -- Для каждой строки списка сверка с текущей `users`-таблицей shared БД: user существует / роль соответствует / email актуален. -- Любые расхождения (запрошенный 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. Дождаться ответа (это может занять дни/недели — нормально). -3. Получив список — сверить с `users`-таблицей shared БД: - ```sql - SELECT id, login, email, role FROM users WHERE email IN () AND deleted_at IS NULL; - ``` -4. Разобрать расхождения с учредителем (missing / role mismatch / inactive). -5. Зафиксировать финальный список в `OpeItcLoc03/admin/.wiki/concepts/books-bookva-whitelist.md` (или в local-only encrypted file если NDA-чувствительно). -6. Финальный список становится input'ом для Фазы 3, шаг 1: при настройке Bookva-БД в неё попадают **только** эти users. -7. Закрыть с note: «whitelist (N users) подтверждён учредителем, готов к применению на cutover'е». -**Branch:** n/a - - -## 🔵 [#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`. - -**Контекст дизайна:** `mcp__projects-meta__knowledge_get slug=concepts/tenant-split` § «Фаза 1 — Physical stateful split + overlay-repos». Brainstorm trace: `~/projects/.workshop/.archive/2026-05-24-books-tenant-split.md`. - -**Container naming:** drop `books-` prefix → `bookva-` / `slovo-`. - -**TDD-режим:** `[skip-tdd: one-off migration on maintenance window]` — verify через post-cleanup audit. - -**Acceptance:** -- `bookva-db` + `slovo-db` контейнеры живые, audit `SELECT DISTINCT idSeller` = `{1}` / `{2}` соответственно. -- `bookva-mongo` + `slovo-mongo` контейнеры живые, agenda-история preserved, jobs чужого tenant'а cleanup'нуты. -- MinIO (один shared) имеет bucket'ы `bookva` + `slovo` вместо `books`; объекты чужого tenant'а удалены. -- Shared catalog tables (`books`, `editions`, `epz`, `epz_pages`, `warehouses`, `products`) — полные копии в обеих БД. -- Smoke с двух сетей: login через `bookva.` → Bookva-данные; текущий URL → Slovo. -- Через 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. - -1. Подтвердить overlay-репо `victor/bookva-overlay` + `victor/slovo-overlay` готовы — compose-файлы для `bookva-*` / `slovo-*` стеков на месте. -2. Подтвердить per-tenant backups настроены (`victor/books per-tenant-backup-and-observability`) — иначе нет точки rollback. -3. Объявить maintenance window 1-2ч (без записи). -4. Execute Steps 0-11 из playbook на shared VDS. -5. Post-execution audit: `SELECT DISTINCT idSeller FROM `, `db.agendaJobs.distinct('data.idSeller')`, `mc ls` on MinIO. -6. Smoke с двух сетей. -7. Через 7-14 дней parallel-run без откатов → cleanup старых `books-*` контейнеров+volumes (отдельный заход). -8. Closing note: «Physical split done, bookva-* + slovo-* контейнеры живут на shared VDS, audit clean, parallel-run start ». -**Blocker:** `victor/books stateful-split-volume-copy` (dev-prep — 7 open questions resolved + dev-DB test passed) + `victor/books per-tenant-backup-and-observability` (Фаза 2 — per-tenant backups настроены) -**Branch:** n/a - - ---- - -## 🟢 [#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` — не проверен. - -
исходный scope (decision-task) - -vehicles-loader (stostayer) деплоится в Docker у клиента (lite docker-deploy, дизайн залочен 29.05, см. stostayer.new `.wiki/concepts/vehicles-loader-docker-deploy.md`). Нужно решение по каналу поставки образа клиенту — единственный открытый вопрос #4, блокирующий финализацию Dockerfile CMD + deploy/README. - -Развилка: -- **A (рекомендация brainstorm'а):** мы билдим образ → push в docker registry VDS → клиент `docker pull` по pinned-тегу. Rollback = перетыкнуть тег. Клиенту не нужен monorepo + yarn berry build-toolchain. Требует: у клиента docker login creds + сетевой доступ к нашему registry. -- **B (fallback):** клиент билдит из git у себя. Требует VCS-доступ + node22 + yarn berry **+ доступ к verdaccio** (`verdaccio.kzntsv.site`) с валидным токеном на хосте клиента. Причина: `.yarn/cache` в `.gitignore` (не коммитится), build идёт offline против cache → свежий `git clone` имеет пустой cache → `yarn workspaces focus` падает, пока клиент не прогонит `yarn install` против приватной регистры. Тяжелее, медленнее rollback, +зависимость от приватной регистры. - -Образ: multi-stage node:22-alpine, `yarn workspaces focus @stostayer/vehicles-loader --production` (плагин `@yarnpkg/plugin-workspace-tools` закоммичен в stostayer.new `.yarn/plugins/`). - -> **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 -
- - - ---- - -## 🟡 [#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 - - ---- - -## 🟢 [#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. - -## Диагноз (доказан, не гипотеза) - -Корень: **галерейные оригиналы лежат ТОЛЬКО на локальном диске legacy-IIS** (`App_Data\galleries\\.`). В S3/MinIO их нет. Продукты — мигрированы (`s3://pilorama98/products/...` отдаёт 200), галереи — нет. - -Три независимых следствия одного корня: - -1. **Prod не отдаёт картинки блога.** Legacy-роут `/galleries//images//.jpg` — ресайзер на лету. Распиновка живого pilorama98 (2 галереи, 98078b74… и c1b53362…): - - `original/001.jpg` → **200** (235KB / 735KB) — оригинал жив, читается с локального диска; - - `800x450`, `default`, `thumbnail`, `100x100`, `600x400` → **404** всегда. - → ресайз-пайплайн legacy-хоста мёртв глобально (System.Drawing / image-cache storage). Это отдельная legacy-поломка; целевая архитектура — отдавать через imgproxy, не чинить legacy-ресайз. - -2. **Превью в админке видно.** Админка рендерит ОРИГИНАЛ (passthrough по imageId → `GetGalleryImageById`, без ресайза), читает локальный диск. Тот же путь, что `original/001.jpg`=200. Сломанную ресайз-ветку не трогает. - -3. **Dev imgproxy 404.** snolla строит source `s3://${storageClient}/${siteId}/${storageFilename}` (packages/snolla/middleware/galleries.js:47 и lib/viewModels/index.js:289). Ключ-форма КОРРЕКТНА (реальный StorageFilename guid + siteId, ровно legacy-раскладка). Но imgproxy читает S3, а объекта там нет → "Source unreachable". Код билдера менять НЕ надо. - -## Развилка, которую закрыть на боевом (нужен доступ к MinIO) - -Имя бакета. Продукты: бакет `pilorama98`, prefix `products/`. snolla для галерей берёт `image.storageClient` («galleries») как ИМЯ БАКЕТА → `s3://galleries/...`. Проверить в MinIO: -- (A) целевой бакет реально `galleries` (отдельный) → достаточно залить файлы, код snolla без изменений; -- (B) всё в бакете `pilorama98`, галереи должны быть под prefix `galleries/` → тогда + маленький код-фикс на стороне snolla (storageClient как prefix, а не bucket) — в этом случае ЗАВЕСТИ follow-up таску обратно на victor/snolla через notify, НЕ чинить snolla из admin. - -## Acceptance criteria - -1. Определено фактическое состояние MinIO: какие бакеты есть, как лежат рабочие продукты, отсутствуют ли галерейные объекты — зафиксировано в close-note. -2. Развилка A/B закрыта явно (какой бакет/prefix — целевой). -3. Если A: legacy-оригиналы залиты в S3 под ключи, которые snolla уже ждёт (`/`); проверена доступность через imgproxy (хотя бы одна картинка → 200). -4. Если B: создана follow-up таска на victor/snolla с точной целевой формой ключа (bucket + prefix), миграция файлов под этот prefix выполнена. -5. В inbox victor/snolla при close: итоговая форма source (bucket/prefix/key), какие галереи/сколько файлов мигрировано, и нужен ли код-фикс на snolla. - -## Контекст-источники (для воспроизведения) -- Живой prod: `https://www.pilorama98.ru/galleries/98078b74e062443ba0708476e031b6b0/images/original/001.jpg` (200) vs `/800x450/001.jpg` (404). -- Legacy-код: MoreThenCms/Galleries/GalleryImage.cs (Path/StorageFilename), GalleriesAppFunc.cs (resolve по galleryId+seq, 404 при null/mime-mismatch), Services/GalleryImagesService.cs:135 (StorageClient="galleries", filename=Guid.NewGuid():N), FileStorage.Local/GalleriesStorage.cs (BasePath App_Data\\galleries\\\\). -- snolla-консьюмер: packages/snolla/middleware/galleries.js, lib/viewModels/index.js:289. -- siteId pilorama98: 37e67fc4… (бакет-сегмент в source). - -## Обязательные скилы — вызвать до начала работы - -- invoke `systematic-debugging` — закрыть развилку A/B по фактам MinIO до любых действий -- invoke `using-tasks` — управление статусом задачи -- invoke `project-discipline` — дисциплина коммитов/пушей -- invoke `using-vds-ops` — если MinIO/imgproxy крутятся на VDS (docker-диагностика) -- invoke `using-projects-meta` — кросс-проектный отчёт/ follow-up в victor/snolla - -**TDD:** нет — ops/migration задача (нет тестируемого кодового ядра; если всплывёт код-фикс на snolla — он уйдёт отдельной impl-таской на victor/snolla с TDD). -**Разрешения:** интерны: нет | автопуш: да -**weight:** needs-human -**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 -**Notify:** victor/snolla — ✅ inbox `.agents/inbox/2026-06-12T11-47-33Z-admin.md` - -**Closure note (2026-06-12):** -- **Развилка A/B закрыта фактами:** в Web.config legacy `` storageClient `galleries` = тип **Local** (`GalleriesStorage`, диск `App_Data\galleries\`), НЕ S3 → галереи никогда не были в S3 (продукты — да, bucket `pilorama98/products`). Корень доказан. **A** (отдельный bucket `galleries`, key `/`) выбран user'ом → нулевой код-фикс snolla. -- **MinIO state:** bucket'ы до миграции — artmone/books/imgproxytest/manuals/modulair/modules/obsidian/ozon/pilorama98/strapi/test/upload; `galleries` отсутствовал; `pilorama98` = только `products/` (шард по hex, без siteId). Зафиксировано. -- **Залив:** RUVDS IIS `C:\sites\snolla\App_Data\galleries\37e67fc44b9e4c06a522a26a73bac9b0\` (301 файл, dir-name уже lowercase 32-hex == ownerId snolla) → `s3://galleries/37e6…/.jpg` verbatim. rclone v1.74.2 inline s3-remote (env-var config, без записи cred). Verify: 301 объект, 77.012 MiB == source. -- **Smoke (с books-vds, мимо LAN-DNS-перехвата воркстейшна):** подписанный imgproxy-URL реального объекта → 200 image/webp 32102 B; несуществующий guid → 404 (негативный контроль). -- **Wiki-drift зафиксирован:** `minio-imgproxy-on-vds.md` устарел (говорит pipeline на windows-host; фактически imgproxy на books-vds с 08.06). Добавлен concept `galleries-storage-class-local-not-s3`. - - - ---- - -## 🟢 [#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 никогда не мигрировали. - -## Acceptance criteria -1. Legacy assets-оригиналы pilorama98 залиты в S3 bucket `assets` ключами `/` (path A, как с галереями: storageClient = имя бакета). -2. Контрольный прозвон imgproxy подтверждает 200 image/webp на реальном объекте + 404 на несуществующем (негативный контроль). -3. Verify: dest object count == source, размер == source. -4. Зафиксирована точная форма ключа и охват (какие ownerId/папки залиты), отчёт в inbox victor/snolla. - -## Подтверждённое репро (от victor/snolla, 2026-06-13) -- URL у консьюмера: `http://localhost:3742/assets/83150d773cf0455ea58ddcccb2578713/800x450/brevno.jpg` → 404. -- БД `MoreThenCms` (mssql.kzntsv.site), таблицы `Folders`/`Files`: - - `Folders`: OwnerId `83150d77-3cf0-455e-a58d-dcccb2578713` → корневая папка `/` (FolderId `03CE5D8D-95B9-4D81-B222-80AEC76225CD`). **Внимание: ownerId это владелец медиа-папки, НЕ siteId** (siteId pilorama98 = `37e67fc4…`). - - `Files`: `brevno.jpg` → StorageClient `assets`, StorageFilename `brevno.jpg`, ContentType image/jpeg, Size 580667 B. -- Прямой прозвон imgproxy подписанным URL источника `s3://assets/83150d773cf0455ea58ddcccb2578713/brevno.jpg` → **HTTP 404 "Source image is unreachable"** (подпись валидна, объекта в S3 нет). -- Подписанный путь для верификации после заливки: - `https://imgproxy.kzntsv.site/n8AdQMelCr2lIvWzeQ3Mw6Fqxft3Q24Rd3vCg-ctHaA/fill/800/450/ce/1/czM6Ly9hc3NldHMvODMxNTBkNzczY2YwNDU1ZWE1OGRkY2NjYjI1Nzg3MTMvYnJldm5vLmpwZw.webp` → ждём 200 после миграции. - -## Подсказки по форме ключа (важно — отличие от галерей) -- Галереи: prefix = `String(site.id)` (siteId сайта). Ассеты: prefix = **OwnerId папки из таблицы Folders**, НЕ siteId. Для brevno.jpg это `83150d773cf0455ea58ddcccb2578713`. На диске ассеты, вероятно, разложены по этим owner-id, а не по siteId. -- Ключ верстается snolla как `s3://${asset.storageClient}/${ownerId}/${asset.storageFilename}` (`middleware/assets.js:98`), storageFilename берётся verbatim (с заменой `\`→`/`). -- Залить ВСЕ assets-папки/owner'ы pilorama98 (не только owner brevno.jpg) — иначе остальные inline-картинки контента дадут ту же дыру. - -## Хвосты (контекст, не блокеры) -- Тот же Local-класс у `images / scripts / stylesheets / watermarks` — если snolla отдаёт их через imgproxy, их ждёт та же дыра (отдельный аудит на стороне victor/snolla, таска `audit-local-storage-clients-imgproxy-s3-gap`). -- На стороне snolla отдельно нужна реализация `content-api/routes/assets.js` (сейчас пустой stub) — это код-таска victor/snolla, не блокер миграции, но картинки не поедут пока не сделаны ОБА (миграция + роут). - -## Обязательные скилы — вызвать до начала работы -- invoke `using-tasks` — управление статусом задачи -- invoke `project-discipline` — дисциплина коммитов/пушей -- invoke `using-projects-meta` — cross-project: отчёт в inbox victor/snolla при close/park -- invoke `systematic-debugging` — прозвон по фактам (DB → диск → S3 → imgproxy) до выводов - -**TDD:** нет — инфра-миграция/ops (если всплывёт код — отдельной impl-таской с TDD). -**Разрешения:** интерны: нет | автопуш: да -**weight:** needs-human -**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 -**Notify:** victor/snolla — ✅ inbox `.agents/inbox/2026-06-13T20-14-16Z-admin.md` - -**Closure note (2026-06-13):** -- **Диск (RUVDS IIS `C:\sites\snolla\App_Data\assets`):** Local storage class, 145 owner-папок (32-hex), плоские файлы. brevno owner `83150d77…` = 6 файлов, `brevno.jpg`=580667 B == DB `Files.Size`. Ключ-форма `/` подтверждена на диске. -- **MinIO (books-vds, minio.kzntsv.site):** bucket `assets` отсутствовал → создан. Залив rclone v1.74.2 (env-var s3-remote, без записи cred), `App_Data\assets` → `min:assets` verbatim. Verify `rclone size` == source: 4750 объектов / 225935853 B. -- **Smoke (мимо LAN-DNS воркстейшна, `--resolve …:89.253.255.133`):** предподписанный imgproxy-URL brevno.jpg → 200 image/webp 64556 B; валидно-подписанный (IMGPROXY_KEY/SALT с books-vds) несуществующий объект → 404 text/plain. 200 не ложный — imgproxy реально ходит в S3. -- **Scope расширен намеренно:** задача про pilorama98, залито всё (215 MiB — мизер). owner→сайт маппинг неочевиден (риск пропустить owner pilorama98), заливка всего гарантирует покрытие + закрывает класс-404 для всех тенантов. Регрессии нет (shared bucket, как galleries). -- **Wiki:** concept `galleries-storage-class-local-not-s3` обновлён (assets done). - - - ---- - -## 🟢 [#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: .` -- `VERDACCIO_TOKEN` — ЖИВОЙ JWT (len ~300, 2 точки), User-env этой же машины (где `pass`). НЕ legacy-opaque (len ~196). Детали: pilonuxt `.wiki/concepts/verdaccio-token-lifecycle.md`. -- Билд ходит в сеть: `verdaccio.kzntsv.site` (пакеты) + `mssql.kzntsv.site:1433` (postinstall→schema-gen генерит `src/generated/` из CMS-БД по `config/default.json`). Если собираешь там, где CMS-БД недоступна — предгенери `yarn schema-gen` и положи `src/generated/` в контекст (schema-gen в билде тогда пропустится). -- Многостадийный, нативных модулей НЕТ (better-sqlite3 выпилен) → build-toolchain не нужен. - -**Runtime egress из контейнера:** -- `mssql.kzntsv.site:1433` — живые запросы CMS (cacheTimeout=0), обязателен. -- `smtp.yandex.ru:465` — письма форм (contacts/checkout). -- Собственный публичный origin — SSR self-call на `/snolla` через `useRequestURL().origin` (`app/plugins/snolla.ts`). Контейнер должен резолвить+достигать свой публичный домен через Traefik. До cutover домена origin-петля на боевом не замкнётся. - -**Env:** -- build: `VERDACCIO_TOKEN` (secret, НЕ ARG/ENV — иначе осядет в слоях). -- runtime (опц.): `NITRO_PORT`(деф 3000), `NITRO_HOST`(деф 0.0.0.0), `NUXT_PUBLIC_GTM_ID`(деф боевой `GTM-MNNXFJ6`; пустой → выкл), `NUXT_PUBLIC_SNOLLA_BASE_URL`(деф `/snolla`). -- Секреты (MSSQL/SMTP/imgproxy key+salt/siteId) ЗАПЕЧЕНЫ в `config/default.json` внутри образа (решение vitya — образ только в приватном registry). Env-override не настроен. - -**Контекст инфры (с твоей доски):** -- imgproxy на books-vds (стек 29/30); у фронта `/imgproxy/**` = routeRule-редирект на `imgproxy.kzntsv.site`. -- ⚠️ Известный gap: snolla-side `content-api/routes/assets.js` — пустой stub (ты флагал в `migrate-assets-originals-to-s3`); без него часть картинок может не рендериться. Проверь на smoke. -- Прод `www.pilorama98.ru` сейчас = легаси MoreThenCms. Cutover домена — решение vitya: деплой на временный поддомен (напр. `pilonuxt.vds.kzntsv.site`) для smoke ИЛИ сразу боевой — согласуй. - -**Acceptance:** -1. Образ собран и в registry. -2. Portainer-стек за Traefik, healthy. -3. Smoke: главная 200 + SSR-разметка (не 500); товары/категории из CMS; картинки рендерятся; форма contacts шлёт письмо. -4. Доклад в inbox `victor/pilonuxt`: URL, имя стека/тег образа, итог smoke. - -## Обязательные скилы — вызвать до начала -- invoke `using-tasks` — статус задачи -- invoke `project-discipline` — дисциплина коммитов/пушей -- invoke `using-vds-ops` — диагностика контейнеров VDS -- invoke `using-projects-meta` — cross-project (notify pilonuxt, чтение pilonuxt-вики) -- invoke `using-wiki` после деплоя — заингесть итог в `.admin/.wiki/concepts/` - -**TDD:** нет — ops/deploy, не код. -**Разрешения:** интерны: нет | автопуш инфра-конфигов: да -**weight:** needs-human (deploy-инфра) -**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. -- DNS-propagation хвост (часть юзеров ещё ловит старый IIS пока кэши истекают). -- Опц.: apex→www 301 canonical (легаси так делал; сейчас apex отдаёт фронт напрямую) — SEO-nicety. -- bare `/products`→404, но unlinked (на него ничто не ссылается). -**Branch:** n/a | **Notify:** victor/pilonuxt ✓ - - -**Status (pre-cutover):** blocked -**Where I stopped (2026-06-14 21:05):** pilonuxt пофиксил content-api (`@snollajs/data@0.9.1` — models-load баг + `tedious` в `nitro.externals.traceInclude`), тег `961a838` (все прошлые битые). Перекатил stack 16. **Полный re-smoke (curl весь меню 21 стр + viewModel по каждому + браузер Playwright + скрин):** viewModel 200 везде (был 500), все страницы меню 200 с реальным контентом (категории/товары/услуги/инфо), товар-стр 200, картинки imgproxy 200 webp, браузер-консоль 0 ошибок, верстка целая. **ОСТАЛСЯ РЕАЛЬНЫЙ БАГ: пункт меню «Продукция» → `/products` → 404** (битый линк, до cutover нельзя). Мелочи: `/blog`+`/services/building/projects/` viewModel 404 (страницы 200), Я.Метрика external не грузится (не баг app). Форма contacts → email НЕ гонял (живой email, жду решение). Скрин `pilonuxt-home-smoke.jpeg`. Inbox pilonuxt отправлен (21:05). -**Next action:** pilonuxt чинит `/products` 404 → новый тег → pull+stack16 update → добить smoke (/products + форма). Согласовать с vitya: тест формы contacts (live [SMOKE TEST] vs закрыть по verified). Cutover www.pilorama98.ru — после закрытия /products + формы. -**Blocker:** pilonuxt: пункт меню «Продукция» → /products → 404 (роут/CMS-страница отсутствует либо нужен редирект на /catalog). -**Branch:** n/a -**Notify:** victor/pilonuxt - -**(history)** Фальш-green #1 (20:25): объявил green по `/`+`/catalog`, vitya поймал `/snolla/pages/viewModel?path=/services`→500; content-api лежал полностью (`sequelize['snolla']` undefined — `@snollajs/data` models не загружались + `tedious` не в .output). Урок: [[smoke-crawl-real-pages-not-two-routes]]. Путь: ТЗ→pull→домен(temp). Мои 2 Dockerfile-бага (npmAlwaysAuth + COPY scripts/src-generated до install) → pilonuxt пофиксил +свой (build-time VERDACCIO_TOKEN default `${VAR:-}`) → образ `36ec970` битый: дубль Vue в prod-бандле давал SSR 500 `reading 'ce'` на всех роутах; диагностировал «инфра зелёная, баг в app»; pilonuxt нашёл (Vite дедупит Vue в dev, Nitro prod нет) → `resolutions` → `3622662` зелёный. - - - - ---- - -## 🟢 [#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` — управление статусом задачи -- invoke `project-discipline` — дисциплина коммитов/пушей - -**TDD:** нет — ops/deploy, кода не пишем; верификация = SSR-вывод страницы товара. -**Разрешения:** интерны: нет | автопуш: n/a (деплой в твоём контуре) -**weight:** needs-human -**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:** — -**Branch:** master -**Notify:** victor/pilonuxt ✓ - - - ---- - -## 🟢 [#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). - -## Acceptance -- Образ собран из master @ `19a4a84`, тег `registry.kzntsv.site/pilonuxt:19a4a84`, запушен в приватный registry. -- Portainer-стек 16 перекатан на новый образ, контейнер healthy за Traefik. -- На проде на странице товара (например `/shop/products/doska-obreznaya-25-100-6000`) в SSR-HTML присутствуют: `itemprop="description"` (непустой), `itemprop="brand"` (Пилорама 98), внутри `itemprop="offers"` — `itemprop="hasMerchantReturnPolicy"` с `returnPolicyCategory=MerchantReturnNotPermitted`. Прежние offers-поля (price/priceCurrency/availability/url) на месте, регрессий нет. - -## Обязательные скилы — вызвать до начала работы -- invoke `using-tasks` — управление статусом этой задачи -- invoke `project-discipline` — дисциплина коммитов/пушей -- по деплою свериться с концептом вики pilonuxt `concepts/docker-deploy.md` (build-secret VERDACCIO_TOKEN, runtime-egress, приватный registry registry.kzntsv.site зарегистрирован в Portainer Custom Id 1) - -**TDD:** нет — ops-задача (build + redeploy), кода не пишется. -**Разрешения:** интерны: нет | автопуш: n/a (билд из уже запушенного master, кода не коммитим) -**weight:** needs-human (deploy-инфра: registry + Portainer-стек за Traefik) -**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:** — -**Branch:** n/a -**Notify:** victor/pilonuxt ✓ - - - ---- - -## 🔵 [#423 stostayer-web-complaint-form-deploy] — Раскатать на прод (www.stostayer.ru) фикс формы жалоб. Коммит **120bc07** уже в `origin/master` репо `victor/stostayer.new`. - -## Контекст - -Прод-форма `/pozhalovatsya#complaint_form` не отправляла данные. Причина: в `packages/web/pages/pozhalovatsya.vue` `onSubmit` вызывал голый `axios(...)` вместо `this.$axios(...)` → `ReferenceError`, съеденный пустым `catch{}`. Чистый клиентский фикс (1 файл). Прод = старый **`packages/web`** (Nuxt 2 SSR), web4 ещё НЕ переключён. - -## ⚠️ Деплой-нюанс - -Это `.vue`-страница в Nuxt 2 **SSR-сборке** — простого `git pull` НЕДОСТАТОЧНО. Прод крутит собранный output через pm2 (`packages/web`: `start = node server/index.js`, `build = nuxt build`, pm2 → `ecosystem.config.js`). Нужен: pull → `yarn build` (или `nuxt build` с `NODE_OPTIONS=--openssl-legacy-provider`) в `packages/web` → рестарт pm2-процесса. Точную прод-процедуру ты знаешь лучше (раскатывал vehicles-loader) — действуй по факту, это лишь напоминание что нужна пересборка, не только pull. - -## Acceptance - -- На проде HEAD `packages/web` = коммит 120bc07 (или master ≥ него). -- Пересборка выполнена, процесс рестартнут без ошибок в логах. -- Smoke вручную: открыть https://www.stostayer.ru/pozhalovatsya#complaint_form, заполнить (Имя, Телефон, СТО, Описание), Отправить → улетает `POST /ostavit-otzyv`, появляется success-модалка «Подтверждение». Письмо/заявка доходит как у `/ostavit-otzyv`. -- Откат: если форма ломается — вернуть прежний билд/коммит (RTO минимальный). - -## Обязательные скилы — вызвать до начала работы - -- invoke `using-tasks` — управление статусом этой задачи -- invoke `project-discipline` — дисциплина деплоя/коммитов - -**TDD:** нет — деплой existing-коммита, admin кода не пишет; верификация = ручной smoke. -**Разрешения:** интерны: нет | автопуш: нет (admin коммитов не делает; если понадобится тег/бамп — спросить у victor/stostayer.new через inbox). -**weight:** needs-human -**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, сайт восстановлен. -**Branch:** n/a -**Notify:** victor/stostayer.new - - ---- - -## 🟢 [#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). - -С 23 мая каждый такой запуск падает на стадии скачивания образа с ошибкой: -`pull/create failed: (HTTP 500) ... no basic auth credentials`. - -Причина (разобрана совместно с admin): `registry.kzntsv.site` — это standalone `registry:2` за htpasswd Basic-авторизацией (так с самого bootstrap 20.05, анонимного доступа не было никогда). Раньше образ просто лежал в локальном кэше демона и pull не требовался; ночная чистка `vdsDockerCleanup` 23 мая вычистила кэш — и следующий pull впервые упёрся в всегда-бывшую auth-стену. Код планировщика звал pull **без креда** (анонимно) → реестр отвечал 401 → dockerode оборачивал в 500. Итог: ~80 фейлов, PDF листов подбора не генерятся с 23 мая. - -Approach ратифицирован человеком (вариант A): код планировщика будет слать креды в pull (это делаю я, books, отдельно, под TDD), а реестру нужен отдельный htpasswd-юзер `books-ci` (его ты уже завёл, кред в `pass vds-kzntsv/registry-books-ci`, проверен live: authed HEAD manifests/master → 200). - -## Что нужно сделать (твоя зона — admin) - -Положить кред `books-ci` в config-volume планировщика на **books-vds (89.253.255.133)**: - -- **Файл (bind-mount):** `/opt/books/job-scheduler/config/default.json` -- В этом JSON уже есть объект верхнего уровня `"docker"` (там `host`, `maxConcurrent`, `pullBackoffMs` и т.д.). Добавить в него ключ `registryAuth`: - ```json - "docker": { - "...": "... существующие поля не трогать ...", - "registryAuth": { - "username": "books-ci", - "password": "<пароль из pass vds-kzntsv/registry-books-ci>", - "serveraddress": "registry.kzntsv.site" - } - } - ``` -- `serveraddress` — ровно `registry.kzntsv.site` (без `https://`, без `/v2`). -- После правки **перезапустить контейнер `books-job-scheduler`** (через Portainer/compose), чтобы node перечитал config — bind-mount подхватывается только на старте процесса. - -## Acceptance - -- Ключ `docker.registryAuth` присутствует в `/opt/books/job-scheduler/config/default.json`, пароль соответствует `books-ci` в реестре. -- Контейнер `books-job-scheduler` перезапущен и `healthy`. -- (Финальная проверка — за books: после деплоя code-части убедиться что `createPickingListPdf` перестал падать на pull. Образ и тег `master` на месте, допушивать ничего не надо — pull заработает сразу как кред доедет + выйдет code-часть.) - -## Замечания - -- Кред **не хардкодить** в git — только в config-volume на хосте (как mongo/s3/mariadb-креды рядом). -- registryGc (вторая docker-related задача, падает «gitea config missing») — **в эту таску НЕ входит**, разбирается отдельно на стороне books (мис-таргетирован на Gitea-packages, переписывается на registry v2 DELETE позже). - -## Обязательные скилы — вызвать до начала работы - -- invoke `using-tasks` — для управления статусом этой задачи -- invoke `using-projects-meta` — cross-project координация / нотификация -- invoke `project-discipline` — дисциплина изменений на проде (config-volume, рестарт) -- invoke `using-vds-ops` или `books-ops-mcp` — read-only проверка `books-job-scheduler` (health/env) после рестарта - -**TDD:** нет — ops-задача (размещение креда + рестарт контейнера), кода нет. -**Разрешения:** интерны: нет (секрет) | автопуш: н/п (не git-код books) -**weight:** needs-human -**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 -**Notify:** victor/books - - - - -## Done (admin infra) -- `docker.registryAuth = {username:"books-ci", password:, serveraddress:"registry.kzntsv.site"}` written into `/opt/books/job-scheduler/config/default.json` (bind-mount → scheduler container, verified pwlen=48, valid JSON). Backup: `default.json.bak-pre-registryauth-2026-06-18`. -- **No restart performed** — current running code doesn't read `registryAuth` yet (inert/backward-compatible). Activation happens on books' code deploy (rebuild = restart, bind-mounted config persists). If books had already deployed the new code, a single `books-job-scheduler` restart activates it. -- Cred itself verified against registry earlier: `books-ci` Basic → 200 on `books-tool-create-picking-list-pdf:master`, wrong-pass → 401. -- Image + `master` tag present in registry → pull works as soon as code+cred are both live; no re-push needed. - - ---- - -## 🟢 [#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). - -## Что сделать -В task-runner config-volume (`/opt/books/task-runner/config/...` — local.json/production.json поверх default.json) добавить секцию: -```json -"registry": { - "baseUrl": "https://registry.kzntsv.site", - "username": "books-ci", - "password": "<из pass vds-kzntsv/registry-books-ci>" -} -``` -(baseUrl+username уже в git default.json; нужен только password — секрет, в git его нет). - -⚠️ Файл config-volume: соблюсти права как в memory `feedback_container_user_node_uid_1000` — контейнер бежит под uid 1000, НЕ ставить `600 root:root` (был прод-даун 2026-06-18 на createPickingListPdf из-за EACCES). Нужен read для uid 1000 (644). - -## Acceptance -- `registry.password` присутствует в task-runner config-volume. -- Ручной trigger registryGc через /scheduler с `dryRun=true` отрабатывает без 401: в отчёте видны repos books-* и «будет удалено N». (Если 405 на DELETE — значит на registry не включён `REGISTRY_STORAGE_DELETE_ENABLED=true`; отдельный вопрос, отметить.) - -## Обязательные скилы — вызвать до начала работы -- invoke `using-tasks` — для управления статусом задачи -- invoke `using-projects-meta` — cross-project tasks/wiki -- invoke `books-ops-mcp` — проверить контейнер/конфиг task-runner read-only до и после - -**TDD:** нет — ops/secret, кода нет. -**Разрешения:** интерны: нет (секрет) | автопуш: n/a (ручная ops на VPS). -**weight:** needs-human — секрет + доступ к VPS config-volume, критичная инфра. -**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-сессия. -**Branch:** n/a -**Notify:** OpeItcLoc03/books - - - ---- - -## 🟢 [#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-сайт) -- `OpeItcLoc03/pilorama98.ru-legacy` → `apps/legacy` (старый сайт) -- `OpeItcLoc03/snolla-products-loader` → `apps/loader` - -## Acceptance criteria -1. Все 3 Gitea-репо в archived (read-only); проверить `archived:true` через API/UI. -2. В README/описание каждого архивного репо — указатель «переехало в victor/pilorama98.ru → apps/<...>». -3. Локальные папки `~/projects/{pilonuxt,pilorama98.ru-legacy,snolla-products-loader}` удалены ПОСЛЕ подтверждения архива. Предохранитель перед rm: `git -C status` чист И `git -C log @{u}..` пуст (нет unpushed). Контент уже в монорепе и запушен. -4. projects-meta: поллер не должен surface'ить stale-доски этих репо (archived → skip; свериться что `meta_status.archived_skipped` вырос). Активные задачи уже перенесены в монореп. -5. Shared meta-wiki: если есть страницы про эти 3 проекта — обновить (переехали в монореп); если нет — короткая запись в concepts о консолидации pilorama-монорепо. -6. Отчёт в inbox pilorama98.ru. - -## Обязательные скилы — вызвать до начала -- invoke `using-projects-meta` — meta-wiki/tasks мутации (preview→confirm) + freshness-gate -- invoke `using-tasks` — статус этой задачи -- invoke `project-discipline` — дисциплина коммитов/пушей - -**TDD:** нет — ops/админ-задача (verification = archived:true + папки удалены + meta обновлён). -**Разрешения:** интерны: нет | автопуш: да -**weight:** needs-human -**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 -**Notify:** victor/pilorama98.ru - - - ---- - -## 🟢 [#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/`. - -## Acceptance criteria -1. Build из `victor/pilorama98.ru`, контекст `apps/web/` (двухстадийный Nitro node-server; VERDACCIO_TOKEN как docker build-secret — живой JWT len~300, npmAlwaysAuth:true; порядок COPY scripts/ + src/generated/ до install — всё в docker-deploy + verdaccio-token-lifecycle). -2. Деплой образа на VDS (Portainer стек за Traefik) — обычный re-deploy. -3. **Smoke на проде (verify, не «собралось=работает»):** товарная `…/shop/products/` → `` ≤60 вида «{товар} — купить в СПб» + meta description уникальная с «доставка по СПб и ЛО»; категория `…/catalog/planken` → короткий title «Планкен прямой и скошенный — купить в СПб»; `…/shipping` и блог-пост → непустая meta. -4. Зафиксировать в web-вики (docker-deploy.md / log), что build-источник теперь монореп `apps/web`. - -## Sequencing (важно) -Перенацелить пайплайн на монореп **ДО** архива `victor/pilonuxt` (таска `archive-pilorama-source-repos`). Иначе после архива деплой со старого репо не подхватит SEO-фиксы — их в pilonuxt нет. Архив pilonuxt — только после зелёного деплоя с монорепы. - -## Обязательные скилы — вызвать до начала -- invoke `using-tasks` — статус задачи -- invoke `project-discipline` — коммиты/пуши -- invoke `verify` — РЕАЛЬНО открыть прод-URL и глазами увидеть title/meta, не доверять «собралось» -- invoke `using-projects-meta` — отчёт обратно - -**TDD:** нет — deploy/ops-задача (verification = smoke на проде). -**Разрешения:** интерны: нет | автопуш: да -**weight:** needs-human -**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 нативных модулей не имел. -**Blocker (актуальный):** `sharp`/libvips не попадает в standalone Nitro output. Шов: ПОЧЕМУ sharp в товарном пути = поведение snolla/content-api; КАК забандлить = `apps/web` `nuxt.config` (nitro traceInclude/externals) ИЛИ runner-стейдж Dockerfile (скопировать `@img/sharp-*` нативы в `.output`). Образ `eaee56f` в реестре (не рабочий из-за sharp). -**Update 2026-06-18 (3):** sharp-фикс СДЕЛАН (admin, Dockerfile-side): runner-стейдж копирует `/app/node_modules/@img` (вкл. `sharp-libvips-linux-x64` с `libvips-cpp.so.8.18.3`) в `.output/server/node_modules/@img`. Verified в контейнере: `import sharp → toBuffer()` = SHARP_OK. Пересобрал+задеплоил → sharp из лога ушёл. **НО за sharp вскрылся СЛЕДУЮЩИЙ блокер:** `getSiteById findByPk undefined` опять, теперь в snolla **0.17** (`@snollajs/snolla/lib/services/sites.js:21`) на **ecommerce-роуте** (`content-api/routes/ecommerce.js:304`) → `/snolla/stores/<id>/products` 500. При этом pages-путь работает: `/` и `/catalog/planken` = 200 c SEO, viewModel ок. Т.е. Sites-модель доступна на pages-роуте, но не на ecommerce — snolla-0.17-внутренности. Был замаскирован sharp'ом (тот падал первым на том же роуте). **Откатил прод на `19a4a84` (все 200, каталог жив).** -**Blocker (актуальный):** snolla 0.17 — `getSiteById` падает на ecommerce-роуте (Sites-модель undefined именно там; pages-роут ок). Зона пира/snolla. Локальный «товар 200» пира — видимо product-detail (viewModel), а не store-products-листинг; нужен прогон именно `/snolla/stores/<id>/products` в контейнере. sharp (моя часть) закрыт; Dockerfile-фикс ждёт в дереве монорепы (`M apps/web/Dockerfile`, не закоммичен). -**Update 2026-06-19 (4) — 🟢 GREEN на проде:** пир добил 4-й слой (commit `41cf89f`: `@snollajs/data` 0.9.0→0.9.1 — forEach-async race + Windows-path-surgery, ломавшая `import()` на Linux = ровно Linux-only дефект; snolla 0.17.1 единой runtime). Пересобрал из `6c6d52d` (HEAD, вкл. деп-фикс + мой sharp `@img` COPY), запушил с VDS, задеплоил стек 16. **Контейнер-smoke ВСЁ 200:** товарная `/shop/products/brusok-…-40-50-6000` → title «Брусок обрезной естественной влажности 40×50×6000 мм» (52≤60) + meta «…**доставка по СПб и ЛО** — от производителя» ✓; `/catalog/planken` → «Планкен прямой и скошенный — купить в СПб» ✓; `/snolla/stores/<id>/products` 200×3 стабильно; home/shipping/blog 200 + meta. Контейнер `6c6d52d` 3мин без рестартов, **stderr пуст**. Acceptance 1-3 выполнены, прод отдаёт SEO. -**Остаётся (🟡):** criterion 4 — закоммитить в МОНОРЕП (`victor/pilorama98.ru`) sharp-фикс `apps/web/Dockerfile` (некоммичен) + обновить `apps/web/.wiki/concepts/docker-deploy.md` («нативных модулей нет» → sharp via snolla 0.17, runner несёт @img). Это **cross-repo push** → ждёт явного push-ok от vitya (дисциплина Rule 4). Прод от этого не зависит — он уже зелёный. -**Next action:** (none — kept until merged) -**Branch:** n/a -**Notify:** victor/pilorama98.ru -<!-- created-by: OpeItcLoc03@DESKTOP-NSEF0UK / from: victor/pilorama98.ru / 2026-06-18T17:44:33.214Z --> -<!-- blocked-by: vitya@.admin-exec / 2026-06-18 / build+deploy mechanics solved; prod 500 from @snollajs/snolla@0.11.0 getSiteById; rolled back to 19a4a84 (200) --> -<!-- reblocked: vitya@.admin-exec / 2026-06-18 / snolla fixed (eaee56f), но sharp/libvips не бандлится в .output → products 500; rolled back to 19a4a84 again --> -<!-- reblocked-2: vitya@.admin-exec / 2026-06-18 / sharp FIXED (admin Dockerfile @img copy, SHARP_OK); behind it getSiteById on snolla 0.17 ecommerce route → products 500; rolled back to 19a4a84 --> -<!-- closed-by: OpeItcLoc03@DESKTOP-NSEF0UK / 2026-06-18T21:15:28.486Z / note: prod зелёный на 6c6d52d — контейнер-smoke все 200 (ecommerce-листинг 200×3, SEO title/meta рендерятся), stderr пуст. Закрыто pilorama98 по подтверждению админа. --> - ---- - -## 🟢 [#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` ведущий `<meta itemprop="name">` = название товара (было только 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). -- Фикс затрагивает только `apps/web/app/pages/shop/products/[slug].vue` (SSR-микроданные Product). - -## Шаги (по concept .wiki/concepts/docker-deploy.md) -1. Build из корня монорепы: контекст = корень, `-f apps/web/Dockerfile`, `yarn workspace nuxt-app build`. VERDACCIO_TOKEN — живой JWT через build-secret. `src/generated/` gitignored — собирать из populated dir (или `yarn schema-gen` заранее), иначе COPY упадёт. Порядок COPY: scripts/ до install. sharp @img: runner копирует полный `@img` из builder. -2. ⚠️ **ПУЛЛ ИЗ РЕЕСТРА.** Push образа в `registry.kzntsv.site/pilonuxt:c4e34d4`, затем стек 16 в Portainer **должен подтянуть свежий образ из registry** (re-pull, не крутить кэш старого тега). Без явного pull задеплоится старый код. -3. Перекат Portainer-стека 16 за Traefik. - -## Acceptance -- Rich Results Test / view-source на товарной странице (`/shop/products/*`) в контейнере/на проде показывает непустой `<meta itemprop="name">` внутри `schema.org/Product`. -- Verify ТОЛЬКО в контейнере/на проде (локальный node .output на Windows маскирует баг фоллбэком в корневой node_modules). -- После деплоя отписать в inbox `victor/pilorama98.ru` — владелец нажмёт «Проверить устранение» в GSC. - -## Обязательные скилы — вызвать до начала -- invoke `using-tasks` — управление статусом задачи -- invoke `project-discipline` — дисциплина коммитов/деплоя -- свериться с `.wiki/concepts/docker-deploy.md` (в repo pilorama98.ru, apps/web) — runbook деплоя: грабли sharp @img, порядок COPY, verdaccio-token - -**TDD:** н/п — ops-деплой; код-фикс уже с тестами в исходном проекте (46 зелёных). -**Разрешения:** интерны: н/п | автопуш: н/п (ops, в наш repo уже запушено). -**weight:** needs-human (deploy-инфра: docker/registry/Portainer/Traefik). -**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 -**Notify:** victor/pilorama98.ru -<!-- created-by: OpeItcLoc03@DESKTOP-NSEF0UK / from: victor/pilorama98.ru / 2026-06-19T06:43:45.922Z --> - ---- - -## 🟢 [#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 отправлен. - -<details><summary>исходный наряд</summary> - -## (orig) Деплой pilonuxt master (commit `40bb383`, включает feature `b32808d`) на прод VDS. Новая фича: DB-backed staticPages + robots.txt через content-api (ADR-0009). Build образа из Gitea master → push в `registry.kzntsv.site/pilonuxt:<sha>` → перекат Portainer-стека 16 за Traefik. Детали: apps/web/.wiki/concepts/docker-deploy.md. - -## ⚠️ Критично перед билдом — форс-регенерация клиента -deps подняты: `@snollajs/snolla ^0.18`, `content-api ^0.13`, `content-api-client ^0.10`, `@snolla/site-schema-gen ^0.6`. Сгенерённый клиент `apps/web/src/generated/` получил НОВЫЕ методы `getStaticPage` / `getRobotsTxt`. `src/generated/` **gitignored** → его нет в Gitea, в build-контексте надо сгенерить заново именно schema-gen@0.6.0. -- **НЕ полагаться на ensure-schema skip-if-present:** если в рабочей папке остался `src/generated/` со старого деплоя (без новых методов) — postinstall его пропустит, соберётся СТАРЫЙ клиент, и server-код (`server/middleware/snolla-static-pages.ts`, `server/routes/robots.txt.get.ts`) упадёт на отсутствующих методах. -- Порядок: обновить master до `40bb383` → `yarn install` (тянет schema-gen 0.6 из verdaccio) → **`rm -rf apps/web/src/generated` → `yarn workspace nuxt-app schema-gen`** (нужен egress в MSSQL на build-prep) → docker build (context = корень монорепы, `-f apps/web/Dockerfile`). - -## Acceptance (smoke на проде после переката) -- `GET /robots.txt` → 200 `text/plain` из БД (полный robots с Yandex-секцией + Sitemap), НЕ старый allow-all плейсхолдер. -- `GET /yandex_a4e129ae82fc9db5.html` → 200 `text/html` (Yandex-верификация). -- `GET /googlec155573e2673fe09.html` → 200. -- `GET /nope-not-a-page.html` → 404 (Nuxt fall-through, не 500). -- `/` + `/catalog` → 200 (без регресса). -- Убедиться, что `/robots.txt` не перехватывается traefik/статикой поверх Nitro (раньше был `public/robots.txt` — удалён в этом релизе). - -## Обязательные скилы — вызвать до начала работы -- invoke `project-discipline` — дисциплина коммитов/пушей -- invoke `using-tasks` — управление статусом задачи -- invoke `using-vds-ops` — smoke контейнера/логов + HTTP-проверка прод-URL после переката -- invoke `using-projects-meta` — cross-project lifecycle/notify - -**TDD:** нет — ops/deploy, кода не пишем. -**Разрешения:** интерны: нет | автопуш: да -**weight:** needs-human (deploy-инфра: docker/traefik/Portainer) -**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 -**Notify:** victor/pilorama98.ru -<!-- created-by: OpeItcLoc03@DESKTOP-NSEF0UK / from: victor/pilorama98.ru / 2026-06-29T08:52:31.094Z --> -<!-- closed-by: vitya@.admin-exec / 2026-06-29 / 40bb383 LIVE стек 16, smoke acceptance 6/6 (robots+staticPages из БД verified телами), notify sent --> - -</details> - ---- - -## 🟢 [#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) отправлен. - -<details><summary>исходный наряд</summary> - -## (orig) Деплой pilonuxt master (commit `cf2bba2`) на прод VDS. Релиз: forms на @snollajs/forms-api + ПОЛНЫЙ дроп @snollajs/snolla из pilonuxt (ADR-0010). content-api 0.14 (без snolla-peer) + новый @snollajs/core@0.1.1. Build из Gitea master → push registry → перекат Portainer-стека 16. Детали: apps/web/.wiki/concepts/{adr-0010-forms-api-consumer-drop-snolla,docker-deploy}.md. - -## ⚠️ Критично #1 — форс-регенерация клиента (как в прошлый деплой) -`apps/web/src/generated/` gitignored. Порядок: master→`cf2bba2` → `yarn install` → **`rm -rf apps/web/src/generated` → `yarn workspace nuxt-app schema-gen`** (egress в MSSQL) → docker build. Иначе соберётся stale-клиент. - -## ⚠️ Критично #2 — проверка дерева на prod `.output` ПЕРЕД перекатом -Это главный риск релиза (вся сага content-api↔snolla про dual-instance; dev маскирует unmet-deps). После `yarn install`/в собранном `.output/server/node_modules` проверить: -- **0× `@snollajs/snolla`** (полностью удалён; `yarn why @snollajs/snolla` пусто). Если транзитив притянул назад — СТОП, пинг victor/pilorama98.ru. -- **0× `btoa`** (был баг core@0.1.0, фикс в 0.1.1 — Buffer вместо btoa). -- **ровно 1× `@snollajs/data@0.9.1`** (НЕ две копии — иначе getSiteById падает, см. сагу). -- `@snollajs/core@0.1.1`, `@snollajs/content-api@0.14`, `@snollajs/forms-api@0.1.0` присутствуют. -Если что-то не так на `.output` — НЕ катить, написать victor/pilorama98.ru (snolla на standby, разберём). - -## Acceptance (smoke на проде после переката) -- `GET /` + `/catalog` → 200 (SSR жив — это и есть проверка, что btoa/snolla-дроп не сломал бандл). -- `GET /robots.txt` → 200 из БД, `/yandex_a4e129ae82fc9db5.html` → 200 (ADR-0009 не регрессировал). -- **Форма (НЕ слать valid на боевую!):** `POST /snolla-forms/forms?path=/checkout` пустой → 422 JSON `{is_valid:false,errors:[...]}`; `?path=/nope` → 404. Этого достаточно — valid-сабмит НЕ слать (уйдёт реальное письмо менеджеру + запись). Контракт valid уже проверен на dev (#2427 на тест-форме). -- Контейнер running, stderr без btoa/snolla/MODULE_NOT_FOUND. - -## Обязательные скилы — вызвать до начала работы -- invoke `project-discipline` — дисциплина коммитов/пушей -- invoke `using-tasks` — управление статусом задачи -- invoke `using-vds-ops` — smoke контейнера/логов + HTTP прод-URL после переката -- invoke `using-projects-meta` — cross-project lifecycle/notify - -**TDD:** нет — ops/deploy, кода не пишем. -**Разрешения:** интерны: нет | автопуш: да -**weight:** needs-human (deploy-инфра: docker/traefik/Portainer) -**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 -**Notify:** victor/pilorama98.ru -<!-- created-by: OpeItcLoc03@DESKTOP-NSEF0UK / from: victor/pilorama98.ru / 2026-06-29T19:50:46.958Z --> -<!-- closed-by: vitya@.admin-exec / 2026-06-29 / cf2bba2 LIVE стек 16, tree-check 4/4 + smoke acceptance (форма 422/404, SSR 200, robots/yandex из БД), notify sent (монореп-inbox) --> - -</details> - ---- - -## 🟢 [#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:<sha>, перекатить 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, `<sitemapindex>`, 6 источников ✓; (2) `/sitemap-pages-1.xml` → 200 application/xml, `<urlset>`, 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 -**Notify:** victor/pilorama98.ru ✅ ack sent -<!-- created-by: OpeItcLoc03@DESKTOP-NSEF0UK / from: victor/pilorama98.ru / 2026-06-29T20:46:40.071Z --> -<!-- closed-by: vitya@.admin-exec / 2026-06-29 / deploy 42f9d64 LIVE + smoke 3/3 --> - ---- - -## 🟢 [#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:<sha>, перекат 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, `<urlset>`, 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 -**Notify:** victor/pilorama98.ru ✅ ack sent -<!-- created-by: OpeItcLoc03@DESKTOP-NSEF0UK / from: victor/pilorama98.ru / 2026-06-29T21:06:37.502Z --> -<!-- closed-by: vitya@.admin-exec / 2026-06-29 / deploy 6b9ae63 LIVE + smoke 4/4 --> - ---- - -## 🟢 [#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, не реестром. Кросс-машинный трансфер образа не нужен. - -## Разделение обязанностей (code-ownership vs ops-ownership) -**Имплементер (`victor/labtools.ru`, отдельная сессия — код-артефакты в репо `deploy/`):** -- `deploy/Dockerfile` (multi-stage: node-build с `--build-arg VERDACCIO_TOKEN` → `yarn install` @snollajs/* из verdaccio → slim runtime, `node server.js`), `.dockerignore`, `PORT`-env, healthcheck. -- **Config/секреты — через env/mounted, НЕ запекать в образ** (config `default.json` gitignored). Отдать админу **список требуемых runtime-env**: DB (`mssql.kzntsv.site` MoreThenCms creds), S3/MinIO (`minio.kzntsv.site` keys), `siteId=D375C419-D787-4FCC-8934-A3B9DC951006`, imgproxy key/salt, verdaccio (только build-time). -- Отчёт workshop: Dockerfile готов + build-args + полный env-лист. - -**Админ (эта таска — ops-acceptance на VDS):** -- Собрать prod-образ на VDS из Dockerfile имплементера (VERDACCIO_TOKEN из secret-store). -- Docker stack/compose + **traefik labels** (Host-rule, TLS/letsencrypt, service-port). -- **Runtime-секреты на VDS** как env (DB/S3/siteId/imgproxy) — не в образе, не в git. -- **DNS/cutover:** сперва деплой на **staging-host** (напр. `labtools.vds.kzntsv.site`) за traefik → smoke parity на задеплоенном URL vs бой. **Cutover живого `labtools.ru` DNS = ОТДЕЛЬНЫЙ gated шаг, подтвердить с оператором** (прод-переключение, план отката) — бой сейчас живой на RUVDS IIS. -- Внешний smoke: главная styled, каталог+порядок, редиректы, canonical, ассеты из MinIO — parity с боем. -- **Вики-рунбук** (invoke `using-wiki`): stack, traefik, env-контракт, образ/тег, cutover + rollback. - -Оба — **репорт в inbox workshop** (from labtools / from admin) по ходу. - -## Обязательные скилы — вызвать до начала -- invoke `using-vds-ops` (диагностика VDS-контейнеров) · invoke `using-wiki` (рунбук после деплоя) · invoke `project-discipline` - -**TDD:** n/a (ops-деплой; приёмка = внешний smoke parity vs бой). -**Разрешения:** интерны: нет | автопуш: админ по своей дисциплине. -**weight:** needs-human -**notify:** OpeItcLoc03/workshop -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 -**Notify:** OpeItcLoc03/workshop -<!-- created-by: OpeItcLoc03@DESKTOP-NSEF0UK / from: OpeItcLoc03/workshop / 2026-07-01T13:02:23.634Z --> - ---- - -## 🟢 [#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. - -<!-- HISTORICAL task spec preserved below --> -**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`. - -## Split (как labtools — dev даёт Dockerfile, ops собирает и катит) -1. **Образ собирает АДМИН на VDS** из emspb `deploy/Dockerfile` (docker+verdaccio+token на VDS), из **чистого git-archive** (config/default.json отсутствует ✅ — секретов в образе нет). Тег `registry.kzntsv.site/emspb:<sha>`. -2. **Portainer stack** на STAGING `emspb.vds.kzntsv.site` за traefik (Host-rule + LE cert + порт). -3. **Runtime-env-секреты в stack-env (НЕ в образ):** DB `mssql.kzntsv.site`/`MoreThenCms`, S3/MinIO, `siteId=96EBC481-D26A-47BE-B660-13D49E7D0A61`, imgproxy HMAC (кросс-проверить как labtools). -4. **Smoke parity vs бой `www.emspb.ru`:** страницы 200 (main/contacts/portfolio/prices + service-выборка), canonical 301 (trailing/lowercase), theme-ассеты из MinIO byte-identical, Cache-Control как бой. - -## НЕ делать -- **Cutover live `emspb.ru` DNS — НЕ трогать.** Отдельный gated-шаг позже по слову оператора (бой на текущем хостинге живой; rollback = DNS назад). Эта таска = только staging + smoke. -- Секреты в образ/git — нет. - -## Ops-acceptance -Внешний smoke parity vs бой `www.emspb.ru` на задеплоенном staging-URL `emspb.vds.kzntsv.site` = GREEN. Рунбук в admin-вике `emspb-vds-deploy-runbook` (зеркало labtools). - -## Обязательные скилы — вызвать до начала работы -- invoke `using-tasks` — статус задачи -- invoke `project-discipline` — дисциплина -- invoke `using-vds-ops` — диагностика контейнеров на VDS при необходимости -- invoke `using-wiki` — записать рунбук `emspb-vds-deploy-runbook` в admin-вику - -**TDD:** n/a (ops-деплой). -**Разрешения:** ops-задача админ-сессии. -**weight:** needs-human -**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:<sha> → 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 -**Notify:** OpeItcLoc03/workshop -<!-- created-by: OpeItcLoc03@DESKTOP-NSEF0UK / from: OpeItcLoc03/workshop / 2026-07-01T21:19:42.728Z --> - ---- - -## 🟢 [#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 - -## Артефакты (labtools.pro-специфика — отличия от emspb выделены) -- **Код:** `victor/labtools.pro` @ **`7bd9fae`** (apps/web, PASS, snolla **0.28.7** final). `deploy/Dockerfile` (multi-stage node:22-slim non-root, healthcheck `/robots.txt`) + `deploy/README.md` (env-лист). -- **Образ:** ⚠️ **`registry.kzntsv.site/labtools-pro:7bd9fae`** (+`:latest`) — имя **`labtools-pro`**, НЕ `labtools` (не коллизить с образом labtools.ru!). Собрать НА VDS из чистого `git archive 7bd9fae` (обход traefik-499; guard: `config/default.json` ABSENT в архиве, `.dockerignore`). build-arg `VERDACCIO_TOKEN` (build-time only, не в финальный образ). -- **siteId** `663F9410-A6CC-4651-9A5C-62844A313957`, activeTheme `389AD745-E3EE-4F23-BE16-DF38440A0941` (theme-store MinIO `themes/389ad745e3ee4f23be16df38440a0941/`). Non-secret → в `production.json`, НЕ env. -- **siteUrl** `https://www.labtools.pro` — уже в `production.json` (R3-фикс: sitemap/robots отдают www). Non-secret. - -## Runtime env-контракт (8 секретов — Portainer stack-env, НЕ в образ/git) -**Значения идентичны labtools/emspb** (все snolla-тенанты = одна MoreThenCms + общий MinIO/imgproxy/smtp; tenant-разница = siteId, не секрет). **Переиспользовать env-массив verbatim из labtools stack (Id 17)** через Portainer API (как для emspb stack 18). Маппинг env→config: `apps/web/config/custom-environment-variables.json`. 8 env: DB_USER/DB_PASSWORD, IMGPROXY_KEY/IMGPROXY_SALT, S3_ACCESS_KEY_ID/S3_SECRET_ACCESS_KEY, SMTP_USER/SMTP_PASSWORD. PORT не переопределять (образ 5000; traefik service-port 5000). - -## Стек + staging -Portainer stack **`labtools-pro`** (новый), endpoint 1, source-compose `admin/host-stacks/vds-kzntsv/labtools-pro.compose.yml`. Staging-хост **`labtools-pro.vds.kzntsv.site`** за traefik+LE. Egress: mssql.kzntsv.site:1433, minio.kzntsv.site:443, imgproxy.kzntsv.site:443, smtp.yandex.ru:465. - -## Staging smoke — parity vs бой `www.labtools.pro` (RUVDS, через `--resolve`) -Acceptance = внешний smoke на задеплоенном URL: -- **Каталог!** (в отличие от контент-only emspb): nav (`/ /about /contacts`) + 3 каталог-секции (`/products/laboratory-presses`, `/products/press-forms`, `/products/milling-accessories`) + продукты (`/products/laboratory-presses/plg-20`, press-form) — статус 200/200 vs бой. -- **redirect#1:** `/products/laboratory-ball-mills` → 301 → `…/lshm-750`. seoCanonical: `/Contacts`→301 lowercase, `/contacts/`→301 trailing. -- **theme-ассеты из MinIO md5-identical** бою (theme.css, logo.png, шрифты). -- ⚠️ **Cache-Control: НЕТ заголовка на theme css/js/images** (labtools.pro `CachingOptions=[]` → empty-config, движок 0.28.7 не ставит заголовок = бой). **НЕ флагай отсутствие Cache-Control как дефект** — для этого тенанта это КОРРЕКТНО (в отличие от emspb/labtools, где был max-age=86400). -- sitemap `/sitemap.xml` = 25 URL, `<loc>` host = **www** (R3). - -## Cutover (live DNS) — ОТДЕЛЬНЫЙ gated-шаг по слову оператора -- Бой live на RUVDS IIS (rollback-цель; админ подтверждает IP — вероятно тот же `80.64.31.36`, что emspb, но проверить). **НЕ выводить RUVDS до подтверждённого cutover.** -- ⚠️ **НЕ путать с labtools.ru** — он ТОЖЕ ждёт DNS-cutover на RUVDS; флипается отдельно. Проверять внешним резолвером, что флипнут именно labtools.pro. -- Порядок (как emspb): оператор флипает DNS `labtools.pro`+`www.labtools.pro`→`89.253.255.94` → ТОЛЬКО ПОСЛЕ этого ops добавляет `Host(labtools.pro)||Host(www.labtools.pro)` в traefik-rule (иначе LE HTTP-01 упадёт на RUVDS → сожжём rate-limit) → live-smoke. -- **НЕ флипать DNS самим.** - -## Скилы / отчёт -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. -**Branch:** master -**Notify:** OpeItcLoc03/workshop -<!-- created-by: OpeItcLoc03@DESKTOP-NSEF0UK / from: OpeItcLoc03/workshop / 2026-07-02T08:27:39.437Z --> - ---- - -## 🟢 [#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. - -<!-- HISTORICAL turnkey preserved below (base64 в блоке БИТЫЙ — источник истины = git-бинарь 7127e92a) --> - -**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). - -**Глиф:** 348×42 RGBA брендовый вордмарк «ТАНДЕМ МЕБЕЛЬ», sha256 `e995624735d709dbf5d0b5b5f1a46653e595a20c93270192d4b38e98207971fc`. - -`IMGPROXY_WATERMARK_DATA` value (base64, одной строкой): -``` -iVBORw0KGgoAAAANSUhEUgAAAVwAAAAqCAYAAAD/GuNEAAAACXBIWXMAAAsTAAALEwEAmpwYAAAKT2lDQ1BQaG90b3Nob3AgSUNDIHByb2ZpbGUAAHjanVNnVFPpFj333vRCS4iAlEtvUhUIIFJCi4AUkSYqIQkQSoghodkVUcERRUUEG8igiAOOjoCMFVEsDIoK2AfkIaKOg6OIisr74Xuja9a89+bN/rXXPues852zzwfACAyWSDNRNYAMqUIeEeCDx8TG4eQuQIEKJHAAEAizZCFz/SMBAPh+PDwrIsAHvgABeNMLCADATZvAMByH/w/qQplcAYCEAcB0kThLCIAUAEB6jkKmAEBGAYCdmCZTAKAEAGDLY2LjAFAtAGAnf+bTAICd+Jl7AQBblCEVAaCRACATZYhEAGg7AKzPVopFAFgwABRmS8Q5ANgtADBJV2ZIALC3AMDOEAuyAAgMADBRiIUpAAR7AGDIIyN4AISZABRG8lc88SuuEOcqAAB4mbI8uSQ5RYFbCC1xB1dXLh4ozkkXKxQ2YQJhmkAuwnmZGTKBNA/g88wAAKCRFRHgg/P9eM4Ors7ONo62Dl8t6r8G/yJiYuP+5c+rcEAAAOF0ftH+LC+zGoA7BoBt/qIl7gRoXgugdfeLZrIPQLUAoOnaV/Nw+H48PEWhkLnZ2eXk5NhKxEJbYcpXff5nwl/AV/1s+X48/Pf14L7iJIEyXYFHBPjgwsz0TKUcz5IJhGLc5o9H/LcL//wd0yLESWK5WCoU41EScY5EmozzMqUiiUKSKcUl0v9k4t8s+wM+3zUAsGo+AXuRLahdYwP2SycQWHTA4vcAAPK7b8HUKAgDgGiD4c93/+8//UegJQCAZkmScQAAXkQkLlTKsz/HCAAARKCBKrBBG/TBGCzABhzBBdzBC/xgNoRCJMTCQhBCCmSAHHJgKayCQiiGzbAdKmAv1EAdNMBRaIaTcA4uwlW4Dj1wD/phCJ7BKLyBCQRByAgTYSHaiAFiilgjjggXmYX4IcFIBBKLJCDJiBRRIkuRNUgxUopUIFVIHfI9cgI5h1xGupE7yAAygvyGvEcxlIGyUT3UDLVDuag3GoRGogvQZHQxmo8WoJvQcrQaPYw2oefQq2gP2o8+Q8cwwOgYBzPEbDAuxsNCsTgsCZNjy7EirAyrxhqwVqwDu4n1Y8+xdwQSgUXACTYEd0IgYR5BSFhMWE7YSKggHCQ0EdoJNwkDhFHCJyKTqEu0JroR+cQYYjIxh1hILCPWEo8TLxB7iEPENyQSiUMyJ7mQAkmxpFTSEtJG0m5SI+ksqZs0SBojk8naZGuyBzmULCAryIXkneTD5DPkG+Qh8lsKnWJAcaT4U+IoUspqShnlEOU05QZlmDJBVaOaUt2ooVQRNY9aQq2htlKvUYeoEzR1mjnNgxZJS6WtopXTGmgXaPdpr+h0uhHdlR5Ol9BX0svpR+iX6AP0dwwNhhWDx4hnKBmbGAcYZxl3GK+YTKYZ04sZx1QwNzHrmOeZD5lvVVgqtip8FZHKCpVKlSaVGyovVKmqpqreqgtV81XLVI+pXlN9rkZVM1PjqQnUlqtVqp1Q61MbU2epO6iHqmeob1Q/pH5Z/YkGWcNMw09DpFGgsV/jvMYgC2MZs3gsIWsNq4Z1gTXEJrHN2Xx2KruY/R27iz2qqaE5QzNKM1ezUvOUZj8H45hx+Jx0TgnnKKeX836K3hTvKeIpG6Y0TLkxZVxrqpaXllirSKtRq0frvTau7aedpr1Fu1n7gQ5Bx0onXCdHZ4/OBZ3nU9lT3acKpxZNPTr1ri6qa6UbobtEd79up+6Ynr5egJ5Mb6feeb3n+hx9L/1U/W36p/VHDFgGswwkBtsMzhg8xTVxbzwdL8fb8VFDXcNAQ6VhlWGX4YSRudE8o9VGjUYPjGnGXOMk423GbcajJgYmISZLTepN7ppSTbmmKaY7TDtMx83MzaLN1pk1mz0x1zLnm+eb15vft2BaeFostqi2uGVJsuRaplnutrxuhVo5WaVYVVpds0atna0l1rutu6cRp7lOk06rntZnw7Dxtsm2qbcZsOXYBtuutm22fWFnYhdnt8Wuw+6TvZN9un2N/T0HDYfZDqsdWh1+c7RyFDpWOt6azpzuP33F9JbpL2dYzxDP2DPjthPLKcRpnVOb00dnF2e5c4PziIuJS4LLLpc+Lpsbxt3IveRKdPVxXeF60vWdm7Obwu2o26/uNu5p7ofcn8w0nymeWTNz0MPIQ+BR5dE/C5+VMGvfrH5PQ0+BZ7XnIy9jL5FXrdewt6V3qvdh7xc+9j5yn+M+4zw33jLeWV/MN8C3yLfLT8Nvnl+F30N/I/9k/3r/0QCngCUBZwOJgUGBWwL7+Hp8Ib+OPzrbZfay2e1BjKC5QRVBj4KtguXBrSFoyOyQrSH355jOkc5pDoVQfujW0Adh5mGLw34MJ4WHhVeGP45wiFga0TGXNXfR3ENz30T6RJZE3ptnMU85ry1KNSo+qi5qPNo3ujS6P8YuZlnM1VidWElsSxw5LiquNm5svt/87fOH4p3iC+N7F5gvyF1weaHOwvSFpxapLhIsOpZATIhOOJTwQRAqqBaMJfITdyWOCnnCHcJnIi/RNtGI2ENcKh5O8kgqTXqS7JG8NXkkxTOlLOW5hCepkLxMDUzdmzqeFpp2IG0yPTq9MYOSkZBxQqohTZO2Z+pn5mZ2y6xlhbL+xW6Lty8elQfJa7OQrAVZLQq2QqboVFoo1yoHsmdlV2a/zYnKOZarnivN7cyzytuQN5zvn//tEsIS4ZK2pYZLVy0dWOa9rGo5sjxxedsK4xUFK4ZWBqw8uIq2Km3VT6vtV5eufr0mek1rgV7ByoLBtQFr6wtVCuWFfevc1+1dT1gvWd+1YfqGnRs+FYmKrhTbF5cVf9go3HjlG4dvyr+Z3JS0qavEuWTPZtJm6ebeLZ5bDpaql+aXDm4N2dq0Dd9WtO319kXbL5fNKNu7g7ZDuaO/PLi8ZafJzs07P1SkVPRU+lQ27tLdtWHX+G7R7ht7vPY07NXbW7z3/T7JvttVAVVN1WbVZftJ+7P3P66Jqun4lvttXa1ObXHtxwPSA/0HIw6217nU1R3SPVRSj9Yr60cOxx++/p3vdy0NNg1VjZzG4iNwRHnk6fcJ3/ceDTradox7rOEH0x92HWcdL2pCmvKaRptTmvtbYlu6T8w+0dbq3nr8R9sfD5w0PFl5SvNUyWna6YLTk2fyz4ydlZ19fi753GDborZ752PO32oPb++6EHTh0kX/i+c7vDvOXPK4dPKy2+UTV7hXmq86X23qdOo8/pPTT8e7nLuarrlca7nuer21e2b36RueN87d9L158Rb/1tWeOT3dvfN6b/fF9/XfFt1+cif9zsu72Xcn7q28T7xf9EDtQdlD3YfVP1v+3Njv3H9qwHeg89HcR/cGhYPP/pH1jw9DBY+Zj8uGDYbrnjg+OTniP3L96fynQ89kzyaeF/6i/suuFxYvfvjV69fO0ZjRoZfyl5O/bXyl/erA6xmv28bCxh6+yXgzMV70VvvtwXfcdx3vo98PT+R8IH8o/2j5sfVT0Kf7kxmTk/8EA5jz/GMzLdsAAAAgY0hSTQAAeiUAAICDAAD5/wAAgOkAAHUwAADqYAAAOpgAABdvkl/FRgAAFHpJREFUeNrsXc9vGsm2PsFpg0DIgLEcWbGSdGZ/MyL755Hw+mUWeHPv6GU2eDkZzcLsktmBdJ9y7xI2Se6blVnMzNot3dx90M0fMNPjKMgyIgMdoSDsFs5bUMc+LtevbiDj5PaRUBzorjpV3fXVV1+dqrry/v17iCyyyCKLbP4Wi6ogssgiiywC3MgiiyyyT8qu/u9f/m8W6dxkH7Tn0yb43Q9fRU8nssgi+7QAd0qQ/QYA7nFgi/YUAJ7NAnwjiyyyyD4FCyspPACAf7N/b0quuQ8A/wSAJwCQiao6ssgiixhucHvCwNTU7gPABgB8CQAvZ+X4o0ePLnO97gCACwDNj+hdsNkHAMCJmsYn8R5G9pEz3EcasPWYhMAD603Gdu/8B9RpEQCqALA7ZTrZD+RvCQB+ZZ899ukBQDlqHpFF9scx3A0AeKgA2m9hotuiZZjk8JD8/58A8MUsme4lNjfEPQXGMouMHc+badYZsLYA4Db7bpf5UQWAhsLPLFdW9w+qZ/Sj/4Hz3AGAmirfD81+x+Nxttfr7eRyudrCwkJ/lukCAMwyzXmVfxZ+DofD4mg0KuRyudofCbhPFL99CRcnxzzGiH8CgB8Zy82wdL5gv8+buRUJgImsJWgwOpBraYblZZbmlqKxlgi4FiRsdttAAiiRshUF9zc0sgf6uknqYZOBbV+QF/rtCMC3z753NPmqRgYFTtow6aCyAJAzuE5WT1jmIJ1inXUwUzXs0WhUGA6HRQCAd+/eXfBrfX19M0hanU6nblmWKwMcBBLf9+3j42OjOj46OiqMx+PsZ599JqzjwWBQ8n3fFvkvM9/37VQq5ayurm5/KD9FPrx79654cnKSHY1GhfF4nMW/AQBWVlYqQcCZpnt8fGwvLy/XksmkExZwH4B8cuwnUEcivASAz4mkcIex3m/nCLRVOK9HOhywOQq2VgyZL7K8Jvu4EpDbYaDtsH+RJWFD1oF+lpWvyICtRq7vEfBraMB6h/3Ns7Q+AXu8DvPakpQLO5odVv9llkbLoN74OmmSPAEAKpJ0bFLWvkGn4jDfa8THMCOQPZZvI+wL2uv1djzPKy8sLPSTyaRjWZa7vLxcGwwGJc/zymGAu91u743H4+zS0lJDlF+v19uJx+OtVCrlpNPp5u+//76DYL+yslJJJBIX6vj4+Ng+OTnJxuPxPg/ifJrLy8s1mubq6ur24uKiK/K12+1W4/F460P4Keoger3eju/7NuaTSCRalmW5vV4P2wSI8qFM+u3bt+Ver7djWZabTCYd3/ftwWBQQoYtKl8QwP1G8dvPBvd7jNX+RqSGn2H2IWN11phqAtDrEYa6KWnAvxLAU7GLXZYPAMBdQ2DBhnpXAFoUbFVyhA0AL1j5biuG+U0DkEOwqmmAsEIAOEuYaJb9hv5WWL51AkwVjSxRZz7wdVIn4F8zkBNEae+yNLcFHVhRkE4/ANia1LEQGA8ODnYty3KvXbu2zTMgniWOx+OsDkAo2AIApNPpJs96Y7FY/8aNG3ctyzqt48PDwzqCg2zonEwm4eTkJMv7dHBwsHtycpJdW1vbomWgaWYymYYkTafb7VZTqZQzTz9F1ul06oPBoMT7DQDQ7XarlKmK2Cl9hqlUyqG+drvdKl6TTqebsudmMml2T8FuIQBoepyU8HAOYFtgQFTjGrAJGBUNZQWbgG0rAIsrMBB3BY19l8vTVQB9UwJCpv5nCbtraDqu2+SaMgP7PcawsxK5BcuYJc9EVId7rCx8nQSJlugL6h+BscHSdiTgyb8fQcG2HxRs2+32XiaTaayvr2+KGvRoNCpQsDk6OioEBVts6L7v2+12ey+dTjfX19c3abq+79u+79sqYDkFiFisT9leu93eW1xcdK9fv36uDKZp+r5vW5bloj/z8lPEngeDQYn3m7JerDvqAy8ftNvtvVwuV1tdXd2m1yELBwAQjTKCAO7/aH7f5/6fAYDHTELAONwNIi+glLChAfIweu2mpCGYgBF9uVXspWx4HQ+4DYlvu2TIqwJcZJZNA/8dTV2BAnDrhLn3uWsbBnn0Of15V1LmxpQdB/rkcJ3JniJtep3phGSWA1uAgJOZ4/E4226395aWlhqqiZiTk5MsZX4maSLY8qB0cHCwK8uPMmkdkGUymQZegywun89XeAZH01QBXywW66+trW3N008Z4KbT6Sbvm+/7dqfTqWcymUYsFpMCru/79sHBwW4+n6/w7H08Hp9qv5ZlucryGzzbjQDv1h0mGzxg923A2QKIHxkYPwWAr2csJ1RBPWNcJEDW0lzTAvWMe1DALbLGmpUAcZEBVJYDLRmgFjRA2tKwr7KinMjEKwp2b1J2h4CSzdUZyhm1KUcaqrLpZpcLYB5VsceudcL69fbt2zIAgG7Wm2e4Kmu323uoH+J3CNa9Xm9nPB5nZflRNmYK8KPRqNDr9XZWVlYqouEyZeNU1uBtYWHhlInOw09ZOuPxOIuASu3w8LBuWZabz+cryKZF2vPh4WE9mUw6IqmE+qkquwngboB6lRgPmj8qrr/HwHiDge4XAnYcxjASoam5RtVQTIexJTg/8RYkFKokaPRVOJsUMmWo5SnlkIIEMHGCbNug4zJ5+ZsS/8qaITntHIOGmpUNOpyioQyEHbnNnpFph3xRS/O8cjweb6n02OFwWFQxI15v9H3fXllZqWBjx4kfzE+lI+I9dGhv0mkkEomWjEUiGw2S5jz8VBlOalHWe3R0VLh+/fom7TD4vIbDYXE4HBZlkQtU+9U9QxPANTUTiSDD2O6jGbLbgoIV8kCnY7egaYymoMizvRo3HEd9s0XYWFFTjia5V+VXy6DjEfm/w/JoGXRKJsDQEuSLnaNK1tFp7WVNh2IbvC8m/hfhbNIwtJzAa5EqBmkCuMPhsNjr9Xby+XxFJCcMh8Mihl3J8hFNsPFAKANHXflMmei8/BRZPB5v8eFyGC2xurq6vbCw0KegyQPuYDAoqUCfAnlQhpuBs5hZAIA/GQBoGHB+COq43rA6blht1lS/LYXQb4E12goHbjacn/3XAaYLk0msu1N0GCpppQTqUCfTPETXZDVyiGkeWcVzxs6KhrzJQNlVPH/aITZZnYTpaM8No33ft2nYkQi0+AZNQQC1wsPDw3o6nW5mMpkG/R2Bi79HNfwVAfx4PM7yTJDeo2K3Mv1Txwpn5aeq/m3bvn3jxo3TttPpdOq5XK6GAEk7L5E+LStXkIk9gPNhYcg+7wDA39lwX8dY75C/XwZ8D++z9L+E6RZBYOXUJUBSNNA2TYbKPMMLO8ShzKklaPCqdF0JCBU1HU+Bu85R1JGq/KFBR+CPowHcsoDNFjQdXZPdUwV5LHQRJvHEqjAinDjcFtRp4LKn0+mm53nlbrdbTafTTVHjfffuXXFtbW1LFd7U6XTqAJMYV55Z8Y19NBoVRABAwdHzvDLPEo+OjgoyliYDSToc933ffv369R6v2crKPQ8/dSbSjlUhYb7v2/LZgw/lOzg42KXgjfHEWHYKuA8IgL4UAKrOnjPgzASUIR7DZBItrDkEeKpwPn6Wapaz0G+nBRxkTg6cn9ixDQFXxwpdCai2NLKDyTCbsuOgK6z6AvlCFLVR5GQYkabqakYSBYWsgIslWiBf4FJkz3qTMGY7xKjm1PL5fAVXTslY0snJSTaRSLRkbHI4HBYHg0FpfX19ExkzpicCnl6vt7O0tNTggQLTTyaTzvLyck2kD6uGzqLYYASdhYWFPg69x+NxFrXPwWBQwuE7P+k0Dz91zLrb7VbX1ta2aJ5YlzJgxefH50k7m0Qi0VpYWOhj2XElIS37Qi6NuMsLPelkYzTgEnIVlOr4Dz6NC/pYHB5StGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBcFQtAlGLzMfBc -``` - -**Проверка:** `echo '<base64>' | base64 -d | sha256sum` → должно дать `e995624735d709dbf5d0b5b5f1a46653e595a20c93270192d4b38e98207971fc`. - -⚠️ **КРИТИЧНО: НЕ выставлять** глобальный `IMGPROXY_WATERMARK_OPACITY` — иначе зальёт вотермарком ВСЕ тенанты (emspb/labtools/labtools.pro/pilorama — они ДОЛЖНЫ остаться чистыми). Глиф применяется ТОЛЬКО по per-URL `wm`-опции, которую snolla шлёт лишь для tandem post-featured. Ключ именно `IMGPROXY_WATERMARK_DATA` (base64), НЕ `_BASE64`. - -Рестарт imgproxy = короткий блип отдачи картинок всем сайтам (кэш-замаскирован nginx) — ратифицировано оператором. - -**SMOKE после рестарта** (должен вернуть 200 image/webp С вотермарком по центру): -``` -curl -sI 'https://imgproxy.kzntsv.site/nlXN7RMrer11Fe9XPQaXmq4H0ufzPpo2fOzBWtaL0po/rs:fill:1170:557:1/g:ce/wm:0.5:ce:0:0:0/czM6Ly9nYWxsZXJpZXMvNzgwODA3MDdmNmUwNDMzMGJhMzA3OTIyMzU0YzJjZWYvYWI0YmM0NDgwZjJkNDc4ZTgzYTQwOTc5NTI0NDA0NWEuSlBH.webp' -``` -(до глифа тот же URL = 200 но чистый; после = с вотермарком.) - -По готовности 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 -**Notify:** OpeItcLoc03/workshop -<!-- created-by: OpeItcLoc03@DESKTOP-NSEF0UK / from: OpeItcLoc03/workshop / 2026-07-04T04:30:04.466Z --> -<!-- closed-by: vitya@.admin-exec / 2026-07-04 / sha-gate поймал битый base64, git-бинарь 7127e92a раскатан, smoke green, opacity не просочилась --> - ---- - -## 🟢 [#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).** - -<!-- HISTORICAL spec preserved below --> - -**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). -- **Образ:** `registry.kzntsv.site/tandemmebel:0facb35` (+`:latest`), собрать **НА VDS** (обход traefik-499). Движок = **snolla@0.32.2 / core@0.13.8**. ⚠️ `content-api` НЕ runtime-dep web-апп (CMS/admin API, консюмер pilorama) — в образ не входит. -- **Стек Portainer:** новый `tandemmebel` (endpoint 1), compose по labtools-pro-шаблону (сменить service/host-имена). Source-of-truth: `admin/host-stacks/vds-kzntsv/tandemmebel.compose.yml`. -- **siteId** `78080707-F6E0-4330-BA30-7922354C2CEF`, activeTheme `EFCD9555-3400-411E-A27D-68D3D8B3DDB2` (theme store MinIO `themes/efcd95553400411ea27d68d3d8b3ddb2/`), Culture **ru-RU**. **siteUrl** `https://www.tandemmebel.ru`. -- **production.json В ПИНЕ содержит** `imgproxy.watermark` override `{size:"post-featured",option:"wm:0.5:ce:0:0:0"}` — уже в образе, отдельно ставить НЕ надо. - -### Runtime env (8 секретов — Portainer stack-env, НЕ в образ/git) -Значения идентичны labtools/emspb/labtools.pro (общий тенант). **Переиспользовать env-массив verbatim из labtools stack (Id 17)** через Portainer API. 8: `DB_USER DB_PASSWORD IMGPROXY_KEY IMGPROXY_SALT S3_ACCESS_KEY_ID S3_SECRET_ACCESS_KEY SMTP_USER SMTP_PASSWORD`. PORT не переопределять (5000; healthcheck; traefik service-port 5000). VERDACCIO_TOKEN build-time only. `config/default.json` исключён `.dockerignore` — проверить ABSENT в архиве. - -### ⚠️ ОБЯЗАТЕЛЬНЫЙ cutover-шаг — PURGE imgproxy-nginx кэша (books-vds) -Focus-ревьюер (независимо) нашёл: nginx перед imgproxy (books-vds, `Cache-Control: max-age=2592000` = 30 дней) закэшировал **ДО-глиф ЧИСТЫЕ** рендеры featured-URL для постов, запрошенных при dev-тестинге (мин. `53-podemnaya-krovat-s-uglovym-shkafom` + smoke-посты). Прод featured-URL детерминированы (без cache-bust) → эти посты отдадут **stale-clean featured (без вотермарка)** в проде до TTL/purge. **ПЕРЕД/НА cutover:** purge nginx-кэш imgproxy на books-vds (стек 29 — cache-dir nginx, напр. `docker exec <imgproxy-nginx> sh -c 'rm -rf /var/cache/nginx/* && nginx -s reload'`, либо полный purge/redeploy nginx-стека). **Verify после:** свежий featured-URL поста 53 (`.../rs:fill:1170:557:1/g:ce/wm:0.5:ce:0:0:0/...`) → 200 **с вотермарком**. - -### Staging smoke (`tandemmebel.vds.kzntsv.site` vs бой www.tandemmebel.ru; гнать С VDS) -- Status-parity: контент (`/ /prices /vacancies /worksorder /contacts`), category (`/furniture/kitchens`, `/furniture/kitchens/classic`), blog-listing `/projects`, blog-post, `/404` (брендированный, не пустой) — 200/parity. -- **Редиректы:** 65 DB-редиректов, `redirects.test.js` 17/17 vs бой — spot-проверить 301 (target parity modulo host). -- **Sitemap:** snolla index-структура (валидна), все post-URL → 200, 8 постов 2012 = верные даты (core@0.13.8 tz-fix). `/articles` bare-root в sitemap → 404 = **PARITY** с боем (не дефект). -- **Featured с вотермарком** (после cache-purge!), мелкие размеры (thumbs/related/category/hero/og) — чистые. -- theme-ассеты MinIO md5 vs бой под `themes/efcd95553400411ea27d68d3d8b3ddb2/`. -- Контейнер healthy, MSSQL+S3 подключены. - -### Documented benign divergences (НЕ флагать при smoke — 2 раунда ре-ревью подтвердили benign) -1. `/projects` ~10 diff = 2015-ordering isotope (within-date, визуально незаметно). 2. gallery imgproxy vs `/galleries/` direct (URL длиннее, та же картинка 800×800, тот же s3-ключ). 3. sitemap 12× `/projects/categories/*` (200, valid over-inclusion). 4. sitemap index-vs-flat (оба валидны). 5. `/articles` bare-root 404 = parity. - -### Cutover (live DNS) — GATED ОПЕРАТОРОМ -1. (оператор) reg.ru: A `tandemmebel.ru` + `www.tandemmebel.ru` → `89.253.255.94` (с RUVDS). -2. (ops) verify флипа авторитетным NS (`ns1.reg.ru` через Resolve-DnsName) ПЕРЕД traefik. -3. (ops) ТОЛЬКО ПОСЛЕ flip: stack `tandemmebel` traefik-rule → `Host(tandemmebel.ru) || Host(www.tandemmebel.ru)`. Порядок критичен (иначе LE HTTP-01 упадёт на RUVDS → rate-limit). -4. Live-smoke оба хоста + внешняя проверка + **featured-вотермарк на живом**. staging-host убрать из rule. - -### Rollback -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 -**Notify:** OpeItcLoc03/workshop -<!-- created-by: OpeItcLoc03@DESKTOP-NSEF0UK / from: OpeItcLoc03/workshop / 2026-07-04T05:18:05.411Z --> -<!-- staging-green: vitya@.admin-exec / 2026-07-04 / build+стек20+cache-purge+smoke green; cutover held on operator --> - ---- - -## 🟢 [#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 отправлен. - -<!-- HISTORICAL spec below --> - -**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 — аккуратно. - -## Шаги - -1. **Verify (не по памяти):** подтвердить, что env `IMGPROXY_WATERMARK_DATA` реально сейчас присутствует на стеке 29. Если уже нет — стоп, доложить. -2. Восстановить env-бэкап БЕЗ `IMGPROXY_WATERMARK_DATA` (точечный PUT, как планировалось при заведении глифа) → redeploy стека 29 → purge imgproxy-кэша. -3. **Verify после:** pilorama98.ru + sotstayer рендерят картинки как раньше (главный критерий — не задеть боевых тенантов). Приложить проверку (curl/скрин ключевого изображения каждого). - -## НЕ трогать - -- Глиф-бинарь `host-stacks/books-vds/tandemmebel-watermark-logo.png` (commit 7127e92a) — ОСТАВИТЬ, переиспользуется для sharp-composite. -- Никаких других env стека 29, никаких других стеков. - -## Ожидаемая побочка - -tandemmebel-staging (стек 20) потеряет featured-вотермарк — ЭТО НОРМАЛЬНО (staging paused, переезжает на sharp). Не чинить. - -## Обязательные скилы - -- invoke `using-vds-ops` / ops-доступ к стеку — verify состояния контейнеров -- invoke `using-tasks` — статус - -**weight:** needs-human (redeploy боевого shared docker-стека) -**notify:** OpeItcLoc03/workshop -**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 -**Notify:** OpeItcLoc03/workshop -<!-- created-by: OpeItcLoc03@DESKTOP-NSEF0UK / from: OpeItcLoc03/workshop / 2026-07-04T06:15:36.456Z --> -<!-- closed-by: vitya@.admin-exec / 2026-07-04 / rollback done, byte-identical to pre-watermark backup, boевые тенанты verified clean --> - ---- - -## 🟢 [#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. - -<!-- HISTORICAL spec below --> - -**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 и кладёт результат в этот бакет, потом отдаёт из него. Кэш ленивый/регенерируемый (можно чистить свободно — восстановится из оригиналов по заходу). - -## Шаги - -1. Создать ПУСТОЙ бакет `variant-cache` в том же shared MinIO, где лежат snolla-бакеты оригиналов/тем (тот же endpoint). -2. Дать сервис-кредам snolla content-api (те же, что читают оригиналы) **read+write** (putObject/getObject/headObject) на `variant-cache`. Verify, что деплойные S3-креды реально могут писать туда. -3. Confirm: бакет существует + writable тест-объектом (put→get→delete) → доложить. - -## Границы / заметки - -- Только СОЗДАТЬ новый бакет + грант snolla-сервису. Другие бакеты НЕ трогать. -- Имя бакета `variant-cache` — по текущему коду 0.14.0 + deploy-config. Снолла сейчас переделывает движок (задача victor/snolla media-sharp-imageprocessor-port); если в переработке имя cache-бакета изменится — она пингнёт, подкорректируешь. Пока — `variant-cache`. -- Опционально: lifecycle/retention на бакет (кэш регенерируем) — на твоё усмотрение, не обязательно. - -## Обязательные скилы - -- invoke `using-vds-ops` / ops-доступ к MinIO — создание/verify -- invoke `using-tasks` — статус - -**weight:** needs-human (провижн на shared prod storage) -**notify:** OpeItcLoc03/workshop -**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 -**Notify:** OpeItcLoc03/workshop -<!-- created-by: OpeItcLoc03@DESKTOP-NSEF0UK / from: OpeItcLoc03/workshop / 2026-07-04T07:50:45.270Z --> -<!-- closed-by: vitya@.admin-exec / 2026-07-04 / bucket created, deploy-creds write-verify green, snolla==root FS-mode --> - ---- - -## 🟢 [#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. - -<!-- HISTORICAL spec below --> - -**Created:** 2026-07-04 -## 🟢 [#543 tandemmebel-sharp-staging-rebuild] — РЕЗЮМ ДЕПЛОЯ tandemmebel (был 🟡 paused под пивот на sharp — sharp готов, снимаем паузу). Оператор: «деплоим, ты всё проверишь». Пересобрать staging на новом sharp-движке, parity-smoke. Cutover DNS — НЕ в scope, отдельный gated шаг оператора позже. - -## Что нового vs прошлый staging (imgproxy-путь) - -- **reviewed-sha = `68b93a95d6cd6170ecdd01a37585ece3d6221df5`** (victor/tandemmebel master, запушен) — с него собирай. -- Движок: snolla **0.34.0** / core **0.15.0** (in-process sharp, data-driven watermark из данных темы; imgproxy из пути tandem убран). -- `production.json` в репо несёт media-конфиг (`resize.engine=sharp`, `delivery=serve-bytes`, `cache.mode=local-variant`, `storage.provider=s3`) — это НЕ секрет, приезжает с образом. Рукописного `media.watermark` больше нет. - -## Шаги - -1. Собрать образ на VDS из ЧИСТОГО git-archive sha `68b93a95` (guard: секретов в образе нет; production.json = конфиг, не секреты). Push в registry. -2. Обновить/пересобрать staging-стек tandemmebel (прошлый — Portainer стек 20, `tandemmebel.vds.kzntsv.site`) на новый образ. Runtime-секреты (DB mssql / MinIO S3-креды / siteId) — как раньше, verbatim, в stack-env, НЕ в образ. -3. **Parity-smoke vs бой `www.tandemmebel.ru`:** - - Картинки: slug-URL `/galleries/<gid>/images/<slug>/<seq>` → 200 image/webp; **0 `/imgproxy`-редиректов наружу** (сырой /imgproxy под sharp → 404 — норма). - - Featured + lightbox (gallery-large 1200×794, post-featured 1170×557) несут **watermark** (из sharp, центр ~0.6); мелкие (gallery-small, slider) — чистые. - - variant-cache пишется в бакет `variant-cache` (без NoSuchBucket). - - Роуты/редиректы parity как прошлый staging (10/10 было зелёное). -4. Доложить staging-green + находки в inbox workshop. Всё, что «увидишь хренью» — репорти, не проглатывай. - -## Заметки / guards - -- **variant-cache — общий бакет:** staging пишет туда же, куда потом прод. Кэш content-addressed (source+size+watermark) → байты идентичны, staging просто пред-прогреет. Не пуга́йся объектов в бакете. -- **GothamPro heads-up (из прошлого хендоффа):** прод-tandemmebel.ru отдаёт `Gotham-Pro.css` 0 байт, staging корректный ~4436B → шрифт заголовков на staging «правее» боя. Это НЕ регресс — известная дельта, всплывёт при cutover, не флагай как поломку. -- Cutover DNS (reg.ru) — НЕ трогай. Отдельный gated шаг оператора по твоему staging-green. - -## Обязательные скилы - -- invoke `using-vds-ops` — состояние контейнеров/стека -- invoke `using-tasks` — статус - -**weight:** needs-human (сборка образа + деплой стека на VDS) -**notify:** OpeItcLoc03/workshop -**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 -**Notify:** OpeItcLoc03/workshop -<!-- created-by: OpeItcLoc03@DESKTOP-NSEF0UK / from: OpeItcLoc03/workshop / 2026-07-04T08:42:44.850Z --> -<!-- closed-by: vitya@.admin-exec / 2026-07-04 / sharp staging green, все гейты + watermark visual verified --> - ---- - -## 🟢 [#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». Значит: -- **Пересобрать VDS-staging образ на 0.42.0-чейне** (новые версии всех пакетов), НЕ деплоить in-place на текущий боевой old-infra прод. -- 0.42.0-образ **суперсидит** ранее собранный 0.16.2-staging (`b02ca18`) — это новый пин staging, старый снимается. -- Прод-флип остаётся **тем же DNS-gated cutover** из paused `[tandemmebel-web-vds-deploy]` (reg.ru→89.253.255.94, хозяин сайта). Этот таск готовит НОВЫЙ образ + перепрогоняет гейты; сам флип — по-прежнему по внешнему DNS + отмашке оператора. Развести с паркованным таском (объединить/пометить, чтобы не было двух конкурирующих образов). -- **Проверь tree-check образа:** 0.42.0 содержит sharp-пивот (snolla 0.34.0 нёс sharp, 0.42.0 > 0.34.0 → по версии да) — не потеряй media-sharp-переезд при пересборке. - -## Acceptance (обязательные гейты) -1. **Pre-deploy SSR-чек (напоминание сноллы):** SSR `/projects` + blog-listing → **200, не 500**. Причина: `getBlogByPath` тянет новые SELECT-колонки; в боевой БД tandemmebel колонки уже есть (проверено сырым SQL в снолла-сессии), но подтверди на целевой БД. -2. **Completeness-DoD перепрогнать НА новом 0.42.0-образе** (не наследовать GREEN с 0.16.2): боевой sitemap ~188 URL prod↔staging → 0 реальных prod-200/staging-не-200; gallery grid-паритет 15 роутов staging==prod. Ядро навигации полностью перепахано — свежий прогон обязателен. -3. **Archive-страницы — НЕ блокер:** live-verify archive-**страниц** оператор отложил. Для `projects` archive-роуты off (`archivesPath=""`). Не гейтить на них. -4. **Rollback** (RUVDS / предыдущий пин) наготове. - -## Обязательные скилы — вызвать до начала -- invoke `using-tasks` — управление статусом задачи -- invoke `using-vds-ops` — диагностика контейнеров на VDS -- invoke `project-discipline` — дисциплина коммитов/деплоя -- invoke `using-wiki` при закрытии — заингесть concept, если топология/пин менялись - -## Отчётность -- **notify: OpeItcLoc03/workshop** (комиссионер-оркестратор). -- По закрытию/парку — heads-up ТАКЖЕ в `victor/snolla` inbox: dev-source просил уведомление по деплою. - -**TDD:** нет — ops-деплой, кода не пишем. -**Разрешения:** интерны: нет | автопуш: н/д (ops). -**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 -**Notify:** OpeItcLoc03/workshop -<!-- created-by: OpeItcLoc03@DESKTOP-NSEF0UK / from: OpeItcLoc03/workshop / 2026-07-04T19:59:45.158Z --> - ---- - -## 🟢 [#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). - -Контекст: 0.42.x = перепил viewModel-ядра (ленивый cross-entity граф, includeX-запекание выпилено). Прямой bump на 0.42.0 вскрыл engine-регрессию порядка изделий (`{% order by list_priority %}` no-op на Drop-VM) — сноллой закрыто в **0.42.1** (liquid 0.10.2, orderBy резолвит field через Drop-акцессор). Поэтому цель = 0.42.1, НЕ 0.42.0. - -## Key files -- `~/projects/labtools.ru/apps/web/package.json:12` — пин `@snollajs/snolla` = `0.42.1` -- `~/projects/labtools.ru/deploy/Dockerfile` — multi-stage node:22-slim (без правок; пин течёт через yarn.lock, `yarn install --immutable`) -- Portainer **стек 17** labtools.ru — source-of-truth compose (боевой `Host(labtools.ru)`) - -## Acceptance (гейты на НОВОМ образе ДО подмены — не наследовать) -1. Build на VDS из sha `566d41c` → образ в `registry.kzntsv.site` (BUILD_EXIT=0). -2. **Completeness-gate** на новом образе: 38 боевых-200 роутов (DB source-of-truth MoreThenCms siteId D375C419: 7 content + 6 секций + 24 продукта + static-page + sitemap/robots) → **0 боевых-200 дают 404**. Прогонять С VDS (воркстейшн ловит LAN-DNS-перехват). -3. **Catalog-parity:** порядок изделий в листингах by list_priority (presses = plg-20, plg-12, plg-25, pgr-10 — ключевой чек фикса 0.42.1); непустые сетки всех 6 секций. -4. Подмена тега на живом стеке 17 → контейнер healthy → live-smoke `labtools.ru` == прод, rollback (предыдущий тег) наготове. -5. Секреты НЕ в образе (`config/default.json` .dockerignore'd; VERDACCIO_TOKEN build-only) — уже провалидировано dev'ом локально (docker build+run: image healthy, свип 0 gaps, order корректен в образе, config/default.json ABSENT, VERDACCIO_TOKEN UNSET). - -## Build-рецепт (как tandemmebel) -`git -C ~/projects/labtools.ru archive 566d41c | ssh vitya@<vds> tar -x` → `docker build -f deploy/Dockerfile --build-arg VERDACCIO_TOKEN=<token> -t registry.kzntsv.site/labtools:566d41c . && push`. Portainer PUT стека 17 через curl -X + node (НЕ PS Invoke-RestMethod), env сохранить. - -## Обязательные скилы — вызвать до начала -- invoke `using-tasks` — статус этой задачи -- invoke `project-discipline` — коммит/деплой-дисциплина -- invoke `using-vds-ops` — диагностика контейнеров VDS (стек 17, healthy) - -**TDD:** нет — ops/deploy, кода нет. -**Разрешения:** автопуш деплой-артефактов на VDS — да (боевой apply гейтит оператор). Секреты НЕ в git/образ. -**weight:** needs-human — live-prod in-place: админ подтверждает боевую подмену тега с оператором ДО apply, НЕ автономно. -**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 -**Notify:** victor/labtools.ru -<!-- created-by: OpeItcLoc03@DESKTOP-NSEF0UK / from: victor/labtools.ru / 2026-07-05T08:31:44.377Z --> -<!-- closed-by: vitya@.admin-exec / 2026-07-05 / live-prod in-place bump 0.28.2→0.42.1, operator-approved, acceptance GREEN, live-smoke GREEN --> - ---- - -## 🟢 [#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). - -## Источник -`victor/emspb.ru` @ `95a5c42` — `deploy/Dockerfile` (multi-stage node:22-slim non-root, build-arg `VERDACCIO_TOKEN` build-time, healthcheck /robots.txt) + `deploy/README.md` (env-лист, все секреты через env) + корневой `.dockerignore`. Dockerfile version-agnostic — пин тянется из package.json/yarn.lock через `yarn install --immutable`. - -## Dev-верификация (сделано в emspb-сессии, для справки — админ перепроверяет на образе) -- 43/43 тестов GREEN на 0.42.1 (viewModel-graph-перепил ядра, `includeX`-запекание выпилено — регрессии ноль). -- Completeness-gate: 29 боевых sitemap-роутов + краул → 0 live-200→local-404 (нет скрытого galleries/feed page-type). -- Локальный `docker build` OK; секреты НЕ запечены (history чист, `config/default.json` отсутствует в образе); контейнер healthy; image-sweep 29 роутов + robots/sitemap/theme.css/3.jpg = все 200. - -## Acceptance (перепрогнать на НОВОМ 0.42.1-образе — не наследовать GREEN) -1. Собрать на VDS из чистого git-archive `95a5c42` → `registry.kzntsv.site/emspb:95a5c42` → push. -2. Контейнер healthy (healthcheck /robots.txt). -3. Completeness-gate на образе: 29 sitemap-роутов → 200, canonical 301 (trailing/lowercase/collapse //), theme-ассеты из MinIO, `/images/3.jpg` 200. **Гнать смоук С VDS** — воркстейшн перехватывает прод-домены в LAN-DNS. -4. Rollback наготове (0.28.4-образ b6e361a + RUVDS). - -## Guards -- Секреты в образ/git — НЕТ (env-only, кросс-проверено локально). -- VERDACCIO_TOKEN печётся в ARG/ENV build-стадии → build-secret (известный follow-up, как на tandemmebel; НЕ блокер — runtime-стадия отдельная, токена в финальном образе нет). -- **Cutover live DNS уже сделан** — если топология = staging-rebuild, финальный swap живого стека = operator-gated. LF/CRLF в HTML — косметика, НЕ чинить. - -## Обязательные скилы — вызвать до начала -- invoke `using-tasks` — статус задачи -- invoke `project-discipline` — дисциплина -- invoke `using-vds-ops` — диагностика контейнеров на VDS -- invoke `using-wiki` — обновить рунбук `emspb-vds-deploy-runbook` под 0.42.1 - -**TDD:** n/a (ops-деплой). **Разрешения:** ops-задача админ-сессии. -**weight:** needs-human (обновляет ЖИВОЙ прод-контейнер; прошлая emspb-VDS-деплой-таска = needs-human). -**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 -**Notify:** victor/emspb.ru -<!-- created-by: OpeItcLoc03@DESKTOP-NSEF0UK / from: victor/emspb.ru / 2026-07-05T08:34:29.703Z --> -<!-- closed-by: OpeItcLoc03@DESKTOP-NSEF0UK / 2026-07-05T08:36:03.081Z / note: superseded by emspb-deploy-snolla-0-42-1 (slug выровнен на реальный пин 0.42.1 + класс уточнён на in-place прод-обновление по коррекции workshop); НЕ исполнять этот, работать по -0-42-1 --> - ---- - -## ⚪ [#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). - -## Класс: in-place прод-обновление (НЕ greenfield staging) -Собрать/pull образ 0.42.1 → **admin подтверждает боевой apply с ОПЕРАТОРОМ ПЕРЕД подменой тега** на живом стеке 18 → swap. Rollback = `emspb:b6e361a` (+ RUVDS-IIS 80.64.31.36). - -## Источник -`victor/emspb.ru` @ `95a5c42` — `deploy/Dockerfile` (multi-stage node:22-slim non-root, build-arg `VERDACCIO_TOKEN` build-time, healthcheck /robots.txt) + `deploy/README.md` (env-лист, секреты через env) + корневой `.dockerignore`. Dockerfile version-agnostic — пин тянется из package.json/yarn.lock через `yarn install --immutable`. - -## Dev-верификация (сделано в emspb-сессии — админ перепроверяет на образе) -- 43/43 тестов GREEN на 0.42.1 (viewModel-graph-перепил ядра, `includeX`-запекание выпилено — регрессии ноль). -- Completeness-gate: 29 боевых sitemap-роутов + краул → 0 live-200→local-404 (нет скрытого galleries/feed page-type). -- Локальный `docker build` OK; секреты НЕ запечены (history чист, `config/default.json` отсутствует в образе); контейнер healthy; image-sweep 29 роутов + robots/sitemap/theme.css/3.jpg = все 200. - -## Acceptance (на НОВОМ 0.42.1-образе ДО подмены — не наследовать GREEN) -1. Собрать на VDS из чистого git-archive `95a5c42` → `registry.kzntsv.site/emspb:95a5c42` → push. -2. Контейнер healthy (healthcheck /robots.txt). -3. Completeness-gate на образе: 29 sitemap-роутов → 200, canonical 301 (trailing/lowercase/collapse //), theme-ассеты из MinIO, `/images/3.jpg` 200. **Гнать смоук С VDS** — воркстейшн перехватывает прод-домены в LAN-DNS. -4. **Боевой apply (swap тега на стеке 18) — с подтверждения оператора.** Rollback `emspb:b6e361a` наготове. - -## Guards -- Секреты в образ/git — НЕТ (env-only, кросс-проверено локально). -- VERDACCIO_TOKEN печётся в ARG/ENV build-стадии → build-secret (известный follow-up, как на tandemmebel; НЕ блокер — runtime-стадия отдельная, токена в финальном образе нет). -- LF/CRLF в HTML — косметика, НЕ чинить. - -## Обязательные скилы — вызвать до начала -- invoke `using-tasks` — статус задачи -- invoke `project-discipline` — дисциплина -- invoke `using-vds-ops` — диагностика контейнеров на VDS -- invoke `using-wiki` — обновить рунбук `emspb-vds-deploy-runbook` под 0.42.1 - -**TDD:** n/a (ops-деплой). **Разрешения:** ops-задача админ-сессии. -**weight:** needs-human (in-place обновление ЖИВОГО прод-контейнера; прошлая emspb-VDS-деплой-таска = needs-human). -**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 -**Notify:** victor/emspb.ru -<!-- created-by: OpeItcLoc03@DESKTOP-NSEF0UK / from: victor/emspb.ru / 2026-07-05T08:35:56.737Z --> -<!-- closed-by: vitya@.admin-exec / 2026-07-05 / live-prod in-place bump 0.28.4→0.42.1, operator-approved, acceptance GREEN, live-smoke GREEN --> - ---- - -## 🟢 [#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. - -Код-часть закрыта проггером (`victor/labtools.pro`): bump 0.28.7→0.42.1, тесты 24/24 GREEN, completeness-gate PASS (27 роутов, order-парити ✓), docker build локально healthy, секреты не запечены. **sha для сборки = `0610432`** (master, pushed). - -Почему НЕ голый bump: 0.42.0 = viewmodel-graph-перепил ядра (`includeX`-запекание выпилено). 0.42.0 ловил engine-gap с `{% order … by <field> %}` на Drop-backed каталоге (порядок изделий уезжал) — снолла закрыла в liquid 0.10.2 → **целься на 0.42.1, НЕ 0.42.0**. - -## Key files -- `~/projects/labtools.pro/apps/web/package.json` — пин `@snollajs/snolla@0.42.1` (уже закоммичен в 0610432) -- `~/projects/labtools.pro/deploy/Dockerfile` — multi-stage node:22-slim, build context = repo root, VERDACCIO_TOKEN build-time only (не запекается) -- `~/projects/labtools.pro/deploy/README.md` — рантайм env-лист (8 секретов: DB_USER/DB_PASSWORD, IMGPROXY_KEY/SALT, S3_ACCESS_KEY_ID/SECRET, SMTP_USER/PASSWORD) -- Portainer стек **19** (labtools.pro) — source-of-truth compose, боевой - -## Acceptance (перепрогнать на НОВОМ 0.42.1-образе ДО подмены — НЕ наследовать GREEN) -1. Собрать образ на VDS `registry.kzntsv.site/labtools-pro:0610432` (BUILD_EXIT=0), push в registry. -2. **completeness-gate**: полный список боевых роутов www.labtools.pro (sitemap-развёртка + краул) vs новый образ → 0 боевых-200 → образ-non-200 (проггер: 27 роутов, эталон). -3. **каталог-парити (order-фикс 0.42.1)**: порядок изделий в листингах — presses=[plg-20,plg-12], milling=[milling-jars,grinding-media], press-forms=12 шт. Смоук гнать **С VDS** (воркстейшн ловит LAN-DNS-перехват прод-доменов). -4. Секреты не запечены в образ (проверить `docker image inspect ... .Config.Env` — токена/паролей нет). -5. **Rollback наготове = `labtools-pro:7bd9fae`** (текущий боевой). - -## Apply — OPERATOR-GATED -Подмена тега на живом стеке 19 (Portainer PUT, env сохранён, PullImage) = **боевой apply живого прода — админ подтверждает с оператором ПЕРЕД подменой**. Это НЕ автоматический шаг. После apply: контейнер healthy + running==0610432, повторный смоук ключевых роутов. Рухнуло → откат на 7bd9fae. - -## Обязательные скилы — вызвать до начала работы -- invoke `using-tasks` — управление статусом задачи -- invoke `project-discipline` — дисциплина -- invoke `using-vds-ops` — диагностика VDS-контейнеров (стек 19, registry, portainer) -- invoke `using-projects-meta` — cross-project (эта таска кросс-проектная) - -**TDD:** нет — ops/deploy-таска, код уже покрыт тестами проггером (не пишем код). -**Разрешения:** интерны: нет | автопуш: n/a (ops, не код-таска) -**weight:** needs-human — обновление ЖИВОГО прода in-place; критическая deploy-инфра; боевой apply подтверждается оператором. -**notify:** victor/labtools.pro (репортить проггеру напрямую, МИНУЯ workshop) - -Build-рецепт (зеркало tandemmebel): `git -C ~/projects/labtools.pro archive 0610432 | ssh vitya@89.253.255.94 tar -x`; `docker build -f deploy/Dockerfile --build-arg VERDACCIO_TOKEN -t registry.kzntsv.site/labtools-pro:0610432 . && push`. Portainer PUT через curl -X + node (НЕ PS Invoke-RestMethod). - -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 -**Notify:** victor/labtools.pro -<!-- created-by: OpeItcLoc03@DESKTOP-NSEF0UK / from: victor/labtools.pro / 2026-07-05T08:57:28.335Z --> -<!-- closed-by: vitya@.admin-exec / 2026-07-05 / live-prod in-place bump 0.28.7→0.42.1, operator-approved, acceptance GREEN (order-parity 3 секции), live-smoke GREEN --> - ---- - -## 🟢 [#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 -<!-- created-by: OpeItcLoc03@DESKTOP-NSEF0UK / from: victor/stostayer.new / 2026-07-13T14:08:16.228Z --> -<!-- closed-by: OpeItcLoc03@DESKTOP-NSEF0UK / 2026-07-20T08:00:52.478Z / note: runtime verify GREEN per operator 2026-07-20 — 0.4.1 rate-diff confirmed on prod. --> - ---- - -## 🟢 [#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 -**Notify:** victor/tandemmebel.ru -<!-- created-by: OpeItcLoc03@DESKTOP-NSEF0UK / from: victor/tandemmebel.ru / 2026-07-13T19:22:38.934Z --> - -## Review outcome (2026-07-13) - -🟢 **DONE — 8df10ee LIVE на проде.** - -- Build `tandemmebel:8df10ee` на VDS (clean gitea archive, build-arg VERDACCIO_TOKEN=books-ci JWT), digest `sha256:30a7e5ab82f2bf371042f1b5fa0cd7ac5ceda71379d1da59e148dee8166bd560`, pushed to registry. -- **Pre-build verification caught discrepancy:** letter claimed live=`ed96b18`/0.42.0; actually `0cd9351`/0.42.1 (in-place bump 2026-07-12). gitea `compare/0cd9351...8df10ee` = `total_commits:1` → fix is 1 commit on top of 0.42.1 (correct base), snolla pin+yarn.lock identical → no sitemap regression. -- Staging :5020 completeness-gate: sitemap NEW==PROD **184=184 identical**, self-consistency 183×200+1×404(/articles benign parity), share-block staging vk+ok/fb-tw-gp=0 vs prod-before all-5. -- Swap stack 20 (Portainer JWT, env 8/8 preserve, `prune:false, pullImage:true`), container healthy ~8s. -- **Live-smoke С VDS GREEN:** 4 share-block pages (project-post ×2, /furniture/bedrooms, /furniture/kitchens/classic) → vk+ok present, fb/tw/gp=0; sitemap 184 locs; /articles 404 parity; TLS-серт CN=tandemmebel.ru (LE YR2, until 2026-10-10) untouched. -- Rollback: PUT stack 20 → 0cd9351 (0.42.1, {% order %} fix, clean template-only revert) / ed96b18 (0.42.0, in registry, pruned from VDS 2026-07-13) / b02ca18; OR revert DNS→80.64.31.36. -- Records: STATUS.md _Updated 2026-07-13, runbook section In-place bump 8df10ee, compose source-of-truth 8df10ee. Commit `632c843f` pushed. -- Hygiene note: Portainer stack 20 file carries stale STAGING comments (lines 16-17, 53) but rule line 55 is LIVE — cosmetic, not touched during deploy. -- Tandemmebel-сессия confirmed receipt + acknowledged discrepancy lesson (verify live sha via docker inspect, not board narrative). -<!-- closed-by: OpeItcLoc03@DESKTOP-NSEF0UK / 2026-07-13T19:51:35.818Z / note: 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. --> - ---- - -## 🟢 [#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 -<!-- created-by: OpeItcLoc03@DESKTOP-NSEF0UK / from: OpeItcLoc03/admin / 2026-07-14T11:18:48.428Z --> -<!-- closed-by: OpeItcLoc03@DESKTOP-NSEF0UK / 2026-07-14T11:57:06.771Z / note: 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. --> - ---- - -## 🟢 [#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) -**Branch:** n/a -<!-- created-by: OpeItcLoc03@DESKTOP-NSEF0UK / from: OpeItcLoc03/admin / 2026-08-10T05:14:36.989Z --> -<!-- closed-by: OpeItcLoc03@DESKTOP-NSEF0UK / 2026-08-10T17:21:19.798Z / note: 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. --> - ---- - -## 🟢 [#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 -**Notify:** victor/sched -<!-- created-by: OpeItcLoc03@DESKTOP-NSEF0UK / from: victor/sched / 2026-08-16T12:37:43.150Z --> -<!-- closed-by: OpeItcLoc03@DESKTOP-NSEF0UK / 2026-08-16T14:18:00Z / note: accepted by sched 13:54Z; 3 dist bugs fixed in 0.1.2/0.1.1 --> - ---- - -## 🟢 [#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 -**Notify:** victor/sched -<!-- created-by: OpeItcLoc03@DESKTOP-NSEF0UK / from: victor/sched / 2026-08-16T14:00:00Z --> -<!-- closed-by: OpeItcLoc03@DESKTOP-NSEF0UK / 2026-08-16T14:30:00Z / note: accepted 14:24Z; both remarks became features --> - ---- - -## 🟢 [#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 -**Notify:** victor/sched -<!-- created-by: OpeItcLoc03@DESKTOP-NSEF0UK / from: victor/sched / 2026-08-16T14:30:00Z --> -<!-- closed-by: OpeItcLoc03@DESKTOP-NSEF0UK / 2026-08-16T14:36:30Z / note: accepted 14:31Z; pollTimeoutMs became daemon API --> - ---- - -## 🟢 [#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 -**Notify:** victor/sched -<!-- created-by: OpeItcLoc03@DESKTOP-NSEF0UK / from: victor/sched / 2026-08-16T14:36:30Z --> -<!-- closed-by: OpeItcLoc03@DESKTOP-NSEF0UK / 2026-08-16T14:38:42.093Z / note: F&F-цикл закрыт 2026-08-16: 4/4 таски приняты, 6 багов → 6 фич (exports-map, @sched/ui serve+токен-бокс, validateConfig, ntfy basic, pollTimeoutMs, /api/health) --> - ---- - -## 🟢 [#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 -**Notify:** victor/sched -<!-- created-by: OpeItcLoc03@DESKTOP-NSEF0UK / from: victor/sched / 2026-08-16T13:54:21.047Z --> - ---- - -## 🟢 [#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 -**Notify:** victor/sched -<!-- created-by: OpeItcLoc03@DESKTOP-NSEF0UK / from: victor/sched / 2026-08-16T14:24:47.837Z --> - ---- - -## 🟢 [#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 -**Notify:** victor/sched -<!-- created-by: OpeItcLoc03@DESKTOP-NSEF0UK / from: victor/sched / 2026-08-16T14:32:31.463Z --> - ---- - -## 🟢 [#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 -**Notify:** victor/sched -<!-- created-by: OpeItcLoc03@DESKTOP-NSEF0UK / 2026-08-16T17:41:00Z --> - ---- - -## 🟢 [#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. -2. Второй баг в цепочке: dir строился из route.path с лидирующим '/' (/auto/audi/), а ключи в бакете без него (auto/audi/...) → даже с фиксом региона листинг возвращал {}. - -Затронутые файлы (все в c43d168): packages/api/config/default.json + routes/content/index.js (strip ^/+) + тесты; packages/web/config/default.json; apps/web4/config/default.json. api 0.1.1 → 0.1.2. - -Деплой: пересобрать стек stostayer-web на master tip c43d168 (как в прошлый раз 0.3.23). Проверено локально на dev (легаси :3777 + web4 :3737): /api/content → 200, маркдаун+typograf рендерится, head.json отдаётся. - -Verify: -- /api/content?path=/remont/remont-podveski/rychag-zamena/auto/audi → 200, body содержит "content.01" с <p>-html -- /api/content?path=/diagnostika/diagnostika-check-engine → 200 с html -- /api/content?path=/auto/audi → 200 {} (пусто — норма, у /auto/* нет прямых .md, SEO из CMS seo_content) -- регресс: / и ремонтные страницы 200 - -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 -**Notify:** victor/stostayer.new -<!-- created-by: OpeItcLoc03@DESKTOP-NSEF0UK / from: victor/stostayer.new / 2026-08-16T15:04:45.133Z --> -<!-- closed-by: OpeItcLoc03@DESKTOP-NSEF0UK / 2026-08-16T18:54:35.686Z / note: DONE 2026-08-16: деплой c43d168 вскрыл node16 getRandomValues → решено node18 (0.3.26) + region us-west-1 в хостовом конфиге; SEO-тексты рендерятся (проверено байт-в-байт с их verify); закрыта в составе node18-rebuild --> - ---- - -## 🟢 [#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). Если оригинал ещё жив — взять её, эту закрыть как дубль. - -Контекст: /api/content отдавал 500 на ВСЕХ путях → SEO-тексты из seo-бакета не рендерились ни в легаси, ни в web4. Root cause: -1. region 'local' в конфигах (api/web/web4) — MinIO minio-api.stostayer.ru настроен на us-west-1 → AuthorizationHeaderMalformed 400 → listDirectFiles кидал → весь route 500. -2. Второй баг: dir строился из route.path с лидирующим '/' (/auto/audi/), а ключи в бакете без него (auto/audi/...) → даже с фиксом региона листинг возвращал {}. - -Файлы в c43d168: packages/api/config/default.json + routes/content/index.js (strip ^/+) + тесты; packages/web/config/default.json; apps/web4/config/default.json. api 0.1.1 → 0.1.2. - -Деплой: пересобрать стек stostayer-web на master tip c43d168. Проверено локально (легаси-дев :3777 + web4 :3737): /api/content → 200, маркдаун+typograf рендерится. - -Verify: -- /api/content?path=/remont/remont-podveski/rychag-zamena/auto/audi → 200, body с "content.01" (html) -- /api/content?path=/diagnostika/diagnostika-check-engine → 200 с html -- /api/content?path=/auto/audi → 200 {} (норма, SEO из CMS seo_content) -- регресс / и ремонтные страницы 200 - -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 -**Notify:** victor/stostayer.new -<!-- created-by: OpeItcLoc03@DESKTOP-NSEF0UK / from: victor/stostayer.new / 2026-08-16T15:28:04.961Z --> -<!-- closed-by: OpeItcLoc03@DESKTOP-NSEF0UK / 2026-08-16T18:54:28.226Z / note: duplicate — работа выполнена в stostayer-web-node18-rebuild (close 18:55Z) --> - ---- - -## 🟢 [#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 -**Notify:** victor/stostayer.new -<!-- created-by: OpeItcLoc03@DESKTOP-NSEF0UK / from: victor/stostayer.new / 2026-08-16T18:15:52.965Z --> -<!-- closed-by: OpeItcLoc03@DESKTOP-NSEF0UK / 2026-08-16T18:54:28.585Z / note: 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 --> - ---- - -## 🟢 [#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 -**Notify:** victor/sched -<!-- created-by: OpeItcLoc03@DESKTOP-NSEF0UK / from: victor/sched / 2026-08-17T20:36:56.320Z --> -<!-- closed-by: OpeItcLoc03@DESKTOP-NSEF0UK / 2026-08-17T21:17:xxZ / note: репорт r7 + приёмка r6 отправлены 21:17Z, 91/92 + F1/F2 --> - ---- - -## 🟢 [#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-<lang>/, по образцу 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-исходника. -2. Покрытие: ВСЕ пути спеки (openapi.json 3.1, 15 путей: /health, /tasks (+run|pause|resume|schedule), /schedules (+/:id|pause|resume), /runs (+/:id|retry), artifacts; securityScheme Bearer). -3. Живой smoke против реального admin-api: свой стенд (verdaccio-пакеты, свой порт — конвенция .admin/.tmp/) или наш :8080 (живой, Node 24). Каждый путь дёрнут минимум раз, результат — в репорте. -4. Репорт по F&F-формату: что делал → что ожидал → что получил (вывод/стек) → ссылка на раздел доки + «гладкое». Находки/пробелы спеки-доков — в репорт, НЕ чинить (имплим мы). -5. TDD: unit-тесты клиента на контракт спеки + smoke-харнесс. - -## Обязательные скилы — вызвать до начала работы - -- invoke `tdd-criteria` — до написания кода -- invoke `using-tasks` — управление статусом задачи -- invoke `project-discipline` — дисциплина коммитов/пушей (в чужом репо аккуратно: только свои файлы examples/admin-client-<lang>/) -- invoke `using-context7` — актуальные доки/SDK выбранного ЯП -- invoke `using-wiki` после закрытия — заингесть .wiki/concepts/sched-admin-client-openapi.md - -**TDD:** да — контракт-тесты + smoke -**Разрешения:** интерны: нет | автопуш: да -**weight:** needs-claude -**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 -**Notify:** victor/sched -<!-- created-by: OpeItcLoc03@DESKTOP-NSEF0UK / from: victor/sched / 2026-08-18T19:00:48.011Z --> -<!-- closed-by: OpeItcLoc03@DESKTOP-NSEF0UK / 2026-08-18T19:29:05.712Z / note: ЗАКРЫТА 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 (спеку не чинил). --> - ---- - -## 🟡 [#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:<ver>; деплой стека через 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:<ver>; (3) деплой через Portainer; (4) dynamic/ traefik: middleware sched-auth (bcrypt из pass), релоад; (5) curl-проверка 401/200 + браузер; (6) отчёт. Перед подключением воркеров — HTTP-runner-таски sched (envelope-violation прежде всего). -**Branch:** n/a -**Notify:** .admin -<!-- created-by: OpeItcLoc03@DESKTOP-NSEF0UK / from: OpeItcLoc03/admin / 2026-08-21T10:32:50.288Z --> - ---- - -## ⚪ [#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 -<!-- created-by: OpeItcLoc03@DESKTOP-NSEF0UK / from: OpeItcLoc03/workshop / 2026-08-22T20:31:21.119Z --> - ---- - -## 🟢 [#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-<n>-<slug>.md`; закрытые (🟢) → `.tasks/done/`; переписать STATUS.md (шапки `[#n slug]`, `**Created:**`, блокеры слаги→номера); `counter=N` в agenda; коммиты по проектам; sync MCP-кэша. - -Спека: .workshop/.brainstorm/tasks-global-numbering.md. Жёсткое правило: тулинг (этапы 1-2) понимает формат ДО миграции. - -## Обязательные скилы — вызвать до начала работы -- invoke `project-discipline` -- invoke `using-tasks` -- invoke `using-projects-meta` -- invoke `tdd-criteria` (для скрипта миграции) - -**weight:** needs-human (массовая инфра-операция) -**notify:** OpeItcLoc03/workshop - -**Status:** done -**Created:** 2026-08-22 -**Where I stopped:** Миграция v2 выполнена: 48 репо, 829 задач, counter=829, коммиты+push. Правки 2026-08-23 применены (5-значные имена, link-шапки, Created-dedup, done/ scan, agenda в пуле). Скипы: meeting-room (merge-conflict), projects-wiki (dup .wiki), projects-meta-mcp (архив, таски в .common), yt-tools (github+untracked), coworker-skill (github, локально). -**Next action:** (none — kept until merged) -**Blocker:** #807, #805 -**Branch:** n/a -**Notify:** OpeItcLoc03/workshop -<!-- created-by: OpeItcLoc03@DESKTOP-NSEF0UK / from: OpeItcLoc03/workshop / 2026-08-22T22:14:02.060Z --> -<!-- closed-by: OpeItcLoc03@DESKTOP-NSEF0UK / 2026-08-23T07:23:42.678Z / note: Миграция v2 выполнена: 48 репо, 829 задач, counter=829, коммиты+push. Правки 2026-08-23 применены (5-значные имена, link-шапки, Created-dedup, done/ scan, agenda в пуле). Скипы: meeting-room (merge-conflict), projects-wiki (dup .wiki), projects-meta-mcp (архив, таски в .common), yt-tools (github+untracked), coworker-skill (github, локально). --> - ---- - -## 🟢 [#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:. - -Зависит от: tasks-counter-and-numbering (код в .common). - -## Обязательные скилы — вызвать до начала работы -- invoke `project-discipline` -- invoke `using-tasks` -- invoke `using-projects-meta` -- invoke `tdd-criteria` - -**weight:** needs-human (инфра) -**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:** #807 -**Branch:** n/a -**Notify:** OpeItcLoc03/workshop -<!-- created-by: OpeItcLoc03@DESKTOP-NSEF0UK / from: OpeItcLoc03/workshop / 2026-08-22T22:14:47.537Z --> -<!-- closed-by: OpeItcLoc03@DESKTOP-NSEF0UK / 2026-08-22T23:44:00.355Z / note: task-counter=1 создан в OpeItcLoc03/agenda (bootstrap), MCP v2.37.0 уже задеплоен, smoke-create #1 прошёл (шапка/имя/Created: v2-формат). Блокер снят. --> - ---- ---- - -## ⚪ [#830 kreknin-repair-md3-rebuild] — Восстановить md3 RAID5 (data /volume1) на kreknin Synology после продувки 2026-07-31; вернуть ежедневные бэкапы - -**Status:** ready -**Created:** 2026-08-03 -**Where I stopped:** Rescue 2026-07-31→08-02 завершён (1.1TB в /mnt/rescue, данные в безопасности). sdb (WD-WX32D12L8HTE) не определяется → переподключить/заменить → ребилд md3. Бэкапы ЗАГЛУШЕНЫ до ремонта (не нагружать больной массив). -**Next action:** Скоординировать power-off с Алексеем (физическая работа), свежие SATA-кабели, определить судьбу sdb (мёртв или отвал кабеля); после ребилда — вернуть cron бэкапы (vds-kreknin / books-vds). -**Branch:** n/a - -## 🟢 [#1012 mappa-prod-deploy] — Прод-деплой сервиса mappa на VDS (по таске mappa #985 mappa-deploy). Разделение: деплой = админ (эта таска); артефакты после деплоя + регистрация MCP у агентов = mappa (там же #985). - -Что сделать: -1. Docker compose рядом с общей БД на VDS (postgres:16 на VDS running; отдельная база mappa, свой role, свои pg_dump — решение 1 спеки). Сервис mappa (HTTP-ядро + MCP-адаптер, dist) одним контейнером. -2. Прод-импорт в прод-БД: ~/projects/* + ~/projects/.wiki (идемпотентный upsert, импортёр прогоняется заново — решения 17/18, миграции dev→prod нет). Верификация счётчиков (сходятся с файловыми источниками). -3. Верификация: health / admin.status / graph.stats. -4. Файлы после импорта read-only (записи только через сервис); фолбэк при падении сервиса = чтение из файлов; алерт ntfy при падении. - -Спека: mcp__projects-meta__knowledge_get slug=concepts/mappa (решения 1/15/17/18); локально mappa/docker-compose.yml + README; таска mappa #985. -SSH-доступ: ~/.ssh/config dsm.kzntsv.site (VDS-хосты в known_hosts). - -## Обязательные скилы — вызвать до начала работы -- invoke `using-tasks` — управление статусом задачи -- invoke `project-discipline` — дисциплина коммитов/пушей - -**TDD:** нет — ops-задача (деплой), не импл -**Разрешения:** интерны: нет | автопуш: да -**weight:** needs-claude -**notify:** OpeItcLoc03/mappa - -**Status:** done -**Created:** 2026-08-23 -**Where I stopped:** Инфра-деплой выполнен: отдельная БД mappa (role+db, пароль в pass mappa/full-env), образ registry.kzntsv.site/mappa:0.1.0 (Dockerfile в репо, schema.sql включён), стек 26 через Portainer (compose: host-stacks/vds-kzntsv/mappa.compose.yml), контейнер healthy, endpoint https://mappa.vds.kzntsv.site (health/admin.status/graph.stats 200, БД пустая — миграции легли, 6 таблиц owner=mappa). Прод-импорт, verify счётчиков, read-only+фолбэк, ntfy-алерт, артефакты, MCP-регистрация — за mappa (#985). -**Next action:** (none — kept until merged) -**Branch:** n/a -**Notify:** OpeItcLoc03/mappa -<!-- created-by: OpeItcLoc03@DESKTOP-NSEF0UK / from: OpeItcLoc03/mappa / 2026-08-23T15:10:51.657Z --> -<!-- closed-by: OpeItcLoc03@DESKTOP-NSEF0UK / 2026-08-23T15:34:08.898Z / note: Инфра-деплой выполнен: отдельная БД mappa (role+db, пароль в pass mappa/full-env), образ registry.kzntsv.site/mappa:0.1.0 (Dockerfile в репо, schema.sql включён), стек 26 через Portainer (compose: host-stacks/vds-kzntsv/mappa.compose.yml), контейнер healthy, endpoint https://mappa.vds.kzntsv.site (health/admin.status/graph.stats 200, БД пустая — миграции легли, 6 таблиц owner=mappa). Прод-импорт, verify счётчиков, read-only+фолбэк, ntfy-алерт, артефакты, MCP-регистрация — за mappa (#985). --> - ---- - -## 🟢 [#1013 mappa-ntfy-monitor] — Мониторинг живости сервиса mappa на VDS → ntfy-алерт при падении. Остаток #985 mappa-deploy (решение 15/18: фолбэк-режим, алерт при падении сервиса). - -Что сделать: -1. Монитор health-эндпоинта https://mappa.vds.kzntsv.site/health (ответ {"ok":true}). -2. При падении (недоступен / не-ok / ошибка) — алерт в ntfy: канал ntfy.vds.kzntsv.site, basic-auth vitya/Pryakhin9 (в pass vds-kzntsv/full-env), топик mappa-alerts (свободен). -3. Интервал и ретраи — по ранбуку существующих мониторов VDS (пример: snolla-smtp-monitor cron 08:00 MSK). Разумно: проверка раз в ~5-10 мин, алерт после N подряд фейлов (не шуметь на единичном сбое). - -Спека: решения 6/15/18 concepts/mappa (SPOF принят, деградация = явные ошибки + meta.health + алерт). -Ограничение: не трогать сам стек mappa (Portainer 26) — только мониторинг. Редеплой/рестарт — отдельной задачей (правило: инфра на VDS через админа). - -## Обязательные скилы — вызвать до начала работы -- invoke `using-tasks` — управление статусом задачи -- invoke `project-discipline` — дисциплина коммитов/пушей - -**TDD:** нет — ops-задача (мониторинг), не импл -**Разрешения:** интерны: нет | автопуш: да -**weight:** needs-claude -**notify:** OpeItcLoc03/mappa - -**Status:** done -**Created:** 2026-08-23 -**Where I stopped:** Монитор mappa задеплоен на VDS: /root/mappa-ntfy-monitor/monitor.py + cron */5 (/etc/cron.d/mappa-ntfy-monitor), env /root/.mappa-ntfy-monitor.env (600, NTFY_* из pass vds-kzntsv/full-env), топик mappa-alerts. Проверка GET /health (HTTP 200 + ok:true + service:mappa), алерт после 3 подряд фейлов (~15 мин), раз за эпизод (dedupe) + recovery-сообщение. Тесты: dry-run OK; негатив 3× фейл → DOWN-алерт доставлен в топик (priority high); восстановление → RECOVERED, state сброшен. Стек 26 не тронут. Источник: .admin/scripts/mappa-ntfy-monitor/monitor.py. -**Next action:** (none — kept until merged) -**Branch:** n/a -**Notify:** OpeItcLoc03/mappa -<!-- created-by: OpeItcLoc03@DESKTOP-NSEF0UK / from: OpeItcLoc03/mappa / 2026-08-23T16:30:26.670Z --> -<!-- closed-by: OpeItcLoc03@DESKTOP-NSEF0UK / 2026-08-23T16:37:42.664Z / note: Монитор mappa задеплоен на VDS: /root/mappa-ntfy-monitor/monitor.py + cron */5 (/etc/cron.d/mappa-ntfy-monitor), env /root/.mappa-ntfy-monitor.env (600, NTFY_* из pass vds-kzntsv/full-env), топик mappa-alerts. Проверка GET /health (HTTP 200 + ok:true + service:mappa), алерт после 3 подряд фейлов (~15 мин), раз за эпизод (dedupe) + recovery-сообщение. Тесты: dry-run OK; негатив 3× фейл → DOWN-алерт доставлен в топик (priority high); восстановление → RECOVERED, state сброшен. Стек 26 не тронут. Источник: .admin/scripts/mappa-ntfy-monitor/monitor.py. --> - ---- - -## 🟢 [#1019 mappa-deploy-auth-token] — Деплой обновлённого mappa (v0.4.x, коммиты 7e5562c..043e2da в OpeItcLoc03/mappa) на VDS: (1) задать env MAPPA_API_TOKEN — shared-token auth (finding #1015) выключен, пока токен не задан (сейчас прод mappa.vds.kzntsv.site открыт); (2) schema.sql добавила partial unique index uq_entities_session_owner — migrate применяет при старте; (3) новый MCP-тул admin.export, требование токена для агентских MCP-конфигов (MAPPA_API_TOKEN в env MCP-регистрации). - -**Status:** done -**Created:** 2026-08-23 -**Where I stopped:** Закрыто 2026-08-25: прод mappa на 0.15.0, MAPPA_API_TOKEN в env контейнера, auth активен (health→200 без токена, admin/status→401 без/неверным токеном, 200 с токеном), MCP-регистрация агентов с токеном в ~/.claude.json. -**Next action:** (none — kept until merged) -**Branch:** n/a -**Notify:** OpeItcLoc03/mappa -<!-- created-by: OpeItcLoc03@DESKTOP-NSEF0UK / from: OpeItcLoc03/mappa / 2026-08-23T17:45:12.360Z --> -<!-- closed-by: OpeItcLoc03@DESKTOP-NSEF0UK / 2026-08-25T11:01:51.939Z / note: Закрыто 2026-08-25: прод mappa на 0.15.0, MAPPA_API_TOKEN в env контейнера, auth активен (health→200 без токена, admin/status→401 без/неверным токеном, 200 с токеном), MCP-регистрация агентов с токеном в ~/.claude.json. --> - ---- - -## 🟢 [#1020 mappa-deploy-git-index-token] — Деплой mappa v0.5.0 на VDS + задать MAPPA_GITEA_TOKEN (read-only). #996 mappa-git-index (решение 21) требует read-only Gitea-токен на проде: env MAPPA_GITEA_TOKEN, фолбэк ~/.config/projects-mcp/auth.toml gitea_token (уже есть на машине). После деплоя прогнать POST /gitindex/sync (или MCP git.index_sync) — идемпотентно, verify покажет сходимость с git log. - -**Status:** done -**Created:** 2026-08-23 -**Where I stopped:** Деплой mappa v0.5.1 (вместо v0.5.0 — findings #1021-1025 закрыты в 0.5.1, юзер подтвердил) + MAPPA_GITEA_TOKEN (read-only, создан mappa-gitindex). Образ registry.kzntsv.site/mappa:0.5.1, стек 26 Portainer обновлён (image+env), контейнер healthy. POST /gitindex/sync: 48/61 проектов, 11253 коммита (13 ошибок — нет Gitea-репо, ожидаемо). Verify на проде невозможен (контейнер без чек-аутов, MAPPA_PROJECTS_ROOT пуст). Сходимость подтверждена выборочно: rev-list vs индекс совпадает (mappa 156=156, snolla 883=883, books 1241=1241+1 unpushed). Source-of-truth compose обновлён. Токен в pass mappa/full-env. -**Next action:** (none — kept until merged) -**Branch:** n/a -**Notify:** OpeItcLoc03/mappa -<!-- created-by: OpeItcLoc03@DESKTOP-NSEF0UK / from: OpeItcLoc03/mappa / 2026-08-23T18:14:44.241Z --> -<!-- closed-by: OpeItcLoc03@DESKTOP-NSEF0UK / 2026-08-23T19:39:40.047Z / note: Деплой mappa v0.5.1 (вместо v0.5.0 — findings #1021-1025 закрыты в 0.5.1, юзер подтвердил) + MAPPA_GITEA_TOKEN (read-only, создан mappa-gitindex). Образ registry.kzntsv.site/mappa:0.5.1, стек 26 Portainer обновлён (image+env), контейнер healthy. POST /gitindex/sync: 48/61 проектов, 11253 коммита (13 ошибок — нет Gitea-репо, ожидаемо). Verify на проде невозможен (контейнер без чек-аутов, MAPPA_PROJECTS_ROOT пуст). Сходимость подтверждена выборочно: rev-list vs индекс совпадает (mappa 156=156, snolla 883=883, books 1241=1241+1 unpushed). Source-of-truth compose обновлён. Токен в pass mappa/full-env. --> - ---- - -## 🟢 [#1055 mappa-deploy-v080] — Деплой mappa v0.7.3 → v0.12.0 на прод (стек 26, Portainer). Вся очередь mappa закрыта (5/5): #1022/#1023/#1028/#1029/#1031/#1030/#1054 — деплой-запрос mappa i:2103. Батч: fcb8ca5 (v0.8.0 session live-ingest) → afcf6e1 (race fix) → af5bac0 (v0.9.0 refs) → 4eb1a08 (v0.10.0 sender-identity) → 3e42600 (project-entity race) → 7d10002 (v0.11.0 dispatcher) → 1dcd3d4 (v0.12.0 lease-model-generalization #1054). Миграции идемпотентны (schema.sql): version/claim_token/claim_owner/claim_expires_at + индекс claim_token. Контракт #1054: create без лиза (claim_token опционален), claim = замок на таску, update — version+409, heartbeat только у claim. - -Что в релизе: POST /session (upsert по sourceRef, meta {project,runtime,machine,folder}), PATCH /session/:id (finalize/heartbeat), GET /session?project= (latest-first), GET /session/:id, stale-active (query-параметр), MCP session_upsert/session_heartbeat/session_list/session_get. Миграция: колонка ended_at TIMESTAMPTZ (DO-block, идемпотентно). Сессии лизы НЕ берут (решение 19). - -Прод сейчас: v0.7.3 (последний деплой). Коммит: fcb8ca5 (master), тег v0.8.0. Спека: w:2604 concepts/session-live-ingest (mappa wiki). - -**Кто делает:** админ (.admin). Это ops-таска — деплой по правилу #985 (редеплой = админу, один деплойщик на стек). - -**Status:** done -**Created:** 2026-08-24 -**Where I stopped:** Деплой v0.12.0 выполнен .admin (inbox:2108, письмо i:2107): образ mappa:0.12.0 (1dcd3d4), pull до рестарта, compose стек 27, healthy, миграция прошла, /health ok. Таска поглощена деплоем всей очереди. -**Next action:** (none — kept until merged) - -Verify: GET /session?project= (200, latest-first), POST /session upsert, PATCH /session/:id finalize+heartbeat, MCP session_* зеркала, health 200. Письмо-подтверждение mappa (+ .workshop). -**Branch:** n/a -**Notify:** OpeItcLoc03/mappa -<!-- created-by: OpeItcLoc03@DESKTOP-NSEF0UK / from: OpeItcLoc03/workshop / 2026-08-24T18:43:25.557Z --> -<!-- closed-by: OpeItcLoc03@DESKTOP-NSEF0UK / 2026-08-24T20:20:12.355Z / note: Деплой v0.12.0 выполнен .admin (inbox:2108, письмо i:2107): образ mappa:0.12.0 (1dcd3d4), pull до рестарта, compose стек 27, healthy, миграция прошла, /health ok. Таска поглощена деплоем всей очереди. --> - ---- - -## 🟢 [#1067 diagnose-books-task-runner-empty-logs-2026-08-25] — При попытке достать Excel-артефакт задачи fbsPickingReserve (9:00 MSK) с slovo-инстанса (books-task-runner, books VDS 89.253.255.133): docker logs books-task-runner --since 2026-08-25T05:55 --until 06:40 вернул ПУСТО. Задача по конфигу есть (slovo-tasks.json: fbsPickingReserve, cron 0 9 * * *, createReport:true, reportLocal:false), но в логах task-runner за утро — ни строки. Возможные причины: логи идут в другой стрим (stdout/stderr), логгер буферизует, sched не дёрнул runner, другая таймзона логов (docker logs по UTC?), рестарт контейнера сбросил. Надо разобраться: почему за период 05:55-06:40 UTC (9:00 MSK) нет логов — проверить docker logs без фильтров, рестарты (docker inspect StartedAt), правильный контейнер (bookva-task-runner vs books-task-runner vs bookva-sched vs books-sched). - -**Status:** done -**Created:** 2026-08-25 -**Where I stopped:** Диагноз завершён: пустые логи books-task-runner = след сломанного sched (mongo пересоздан 24.08 22:02Z, sched не переподключился, sync к agendaDb closed с 22:03Z; fbsPickingReserve 9:00 MSK не запускалась). Полный репорт + предложения отправлены в victor/sched (inbox:2168) — правки sched/books вне зоны .admin, ждём их ответа. -**Next action:** (none — kept until merged) -**Branch:** n/a -<!-- created-by: OpeItcLoc03@DESKTOP-NSEF0UK / from: OpeItcLoc03/admin / 2026-08-25T11:41:56.307Z --> -<!-- closed-by: OpeItcLoc03@DESKTOP-NSEF0UK / 2026-08-25T12:29:26.610Z / note: Диагноз завершён: пустые логи books-task-runner = след сломанного sched (mongo пересоздан 24.08 22:02Z, sched не переподключился, sync к agendaDb closed с 22:03Z; fbsPickingReserve 9:00 MSK не запускалась). Полный репорт + предложения отправлены в victor/sched (inbox:2168) — правки sched/books вне зоны .admin, ждём их ответа. --> - ---- +**Не читать. Не править.** Канон — mappa (`mcp__mappa__task_*`): task-сущности проекта. Скил: `mappa-task-work`. diff --git a/.wiki/CLAUDE.md b/.wiki/CLAUDE.md index 0a9ab41..21695bc 100644 --- a/.wiki/CLAUDE.md +++ b/.wiki/CLAUDE.md @@ -1,24 +1,3 @@ -# .wiki/ — Karpathy LLM Wiki +# ⛔ Файловый канал закрыт -Curated knowledge base. Structure: - -- `entities/<slug>.md` — physical/logical assets (hosts, NAS, VDS, router, recovery-VM) -- `concepts/<slug>.md` — patterns, root-cause analyses, recipes, postmortems -- `packages/<slug>.md` — third-party / first-party software packages relevant to this stack -- `summaries/<slug>.md` — chronologies, session traces, primary records -- `raw/` — immutable external clippings (transcripts, screenshots, vendor docs) -- `index.md` — catalog of all pages -- `log.md` — op-log of ingest / promote / lint operations -- `overview.md` — high-level project orientation - -## Domain conventions - -### Mandatory: read design context before implementation - -Before picking up any task in `.tasks/`, load the full design context. It does **not** live in this repo as a standalone source — only pointers do. Sources, in order: - -1. **Local design (canonical):** `.wiki/concepts/admin-infra-project.md` — ingested via promote 2026-05-21. Identity, scope, content inventory, migration recipe (subtree-split + read-tree merge for populated prefixes), initial agenda. -2. **Brainstorm process trace (rationale):** `~/projects/.workshop/.archive/2026-05-21-admin-infra-project.md`. Why each decision was made (recommend vs menu trade-offs), what was rejected (filter-repo vs subtree-split, "admin-only" skills, splitting roadmap from ops), anti-patterns flagged during brainstorm. -3. **Local `overview.md`** — thin summary, quick orientation only — never the source of truth. - -Do **not** invent migration recipes, file lists, taxonomies, or scope decisions from task `where_stopped` lines alone — those are pointers, not specifications. The concept doc and archive contain the rationale. +**Не читать. Не править.** Канон — mappa (`mcp__mappa__*`): wiki-сущности проекта, конвенции — AGENTS-сущность. Скил: `mappa-knowledge`. diff --git a/.wiki/index.md b/.wiki/index.md index 37e9e4f..21695bc 100644 --- a/.wiki/index.md +++ b/.wiki/index.md @@ -1,119 +1,3 @@ -# Wiki Index +# ⛔ Файловый канал закрыт -Catalog of all wiki pages. One line per page, organized by type. Updated on every ingest / new page. - -## Overview - -- [overview.md](overview.md) — project overview - -## Entities - -- [books-vds](entities/books-vds.md) — Books VDS (89.253.255.133 — host4g.ru, client server hosting books app + shared mongo/minio/elasticsearch) -- [dead-synology-diskstation](entities/dead-synology-diskstation.md) — мёртвая Synology DiskStation (source NAS) -- [kreknin-synology](entities/kreknin-synology.md) — Kreknin Synology (backup target + DDNS) -- [nl-vds-3xui](entities/nl-vds-3xui.md) — NL VDS 3x-UI VLESS/Reality node (213.176.64.253) — Reality 443 сломан (PQ×intel), plain VLESS 32030 работает -- [de-vds-3xui](entities/de-vds-3xui.md) — DE VDS 3x-UI VLESS node (130.17.17.158, Fornex Germany) — plain VLESS 32030 security=none; 3x-ui 3.5.0 gotchas (CSRF на POST, client-tables, binary-only setting) -- [openwrt-router](entities/openwrt-router.md) — OpenWRT Router (192.168.1.1) -- [ruvds-iis-host](entities/ruvds-iis-host.md) — RUVDS IIS Host (Win Server 2025 Core, 80.64.31.36) — CMS catch-all destination -- [snolla-recovery-vm](entities/snolla-recovery-vm.md) — Snolla Recovery VM (VirtualBox, savestate'нута 2026-05-21 после 36h soak) -- [vds-kzntsv](entities/vds-kzntsv.md) — VDS kzntsv (Rusonyx 160 NVMe cloud server) -- [vdsina-outline-3xui](entities/vdsina-outline-3xui.md) — VDSina Amsterdam VPN (46.151.25.64) — Outline (основной, 11 keys, :443) + 3x-UI/Xray VLESS; основной боевой VPN семьи -- [windows-recovery-host](entities/windows-recovery-host.md) — Windows Recovery Host (рабочий PC пользователя) - -## Concepts - -- [books-vds-no-gitea-actions](concepts/books-vds-no-gitea-actions.md) — решение vitya 2026-08-23: Gitea Actions выпилены везде (books+vds-ops-mcp workflow удалены, раннер books-vds вычищен), сборка только локально (docker build+curl-upload, OCI-index→amd64 child; docker save сразу даёт OCI), деплой Portainer PUT (синхронный, тянет образ внутри запроса), смоук sched через docker exec node fetch; большие пуши через traefik умирают (Client Closed Request); sched docker-runner: config.json vs docker-config.json, docker-формат образов для registry 2.8.3 -- [registry-traefik-buffering-gotcha](concepts/registry-traefik-buffering-gotcha.md) — большие (>1.5GB) пуши в registry через traefik buffering middleware (4GB) умирают: traefik буферизует body в RAM (1GB free) + OOM на books-vds; обход — curl монолитными PUT / PATCH стримом + finalize, drop_caches перед большим ридом -- [config-custom-env-mapping-in-mounted-dirs](concepts/config-custom-env-mapping-in-mounted-dirs.md) — npm config: env читается только если custom-environment-variables.json лежит в ПРИМОНТИРОВАННОМ конфиг-дире (маунт затеняет образ); mapping не создаёт свойство без env; проверка docker exec с правильным cwd -- [sched-runtime-to-file-managed-migration](concepts/sched-runtime-to-file-managed-migration.md) — перевод sched slovo+bookva на file-managed (durable config, #991): почему runtime хрупкий (расписания в памяти демона, сироты без data), рецепт миграции (файл in-place → live-sync НЕ конвертирует runtime → DELETE /tasks+/schedules → verify), gotchas (id=taskName vs UUID, ghost-таски, task-runner config кэш→рестарт, SMTP from==auth, URL-encode) -- [mappa-vds-deploy-runbook](concepts/mappa-vds-deploy-runbook.md) — деплой/редеплой mappa на VDS (стек 26, образ registry.kzntsv.site/mappa): БД+role на общем postgres:16 (pass mappa/full-env), сборка образа (schema.sql в dist!), Portainer-реденплой (docker pull до restart), env через Portainer, gotchas -- [sched-ozon-creds-stub-contract](concepts/sched-ozon-creds-stub-contract.md) — контракт кредов Ozon slovo/bookva: мисматч-пары (client_id ↔ чужой ключ) — НАМЕРЕННЫЕ заглушки (анти-утечка после tenant-split), матрица 4 sellers, безопасность через break-on-error (2a38fb5), verify по SHA2, НЕ «чинить» -- [omniroute-combo-deepseek-web-tuning](concepts/omniroute-combo-deepseek-web-tuning.md) — настройка web-комбо под агента: system_message убивает скилы pi, tool_filter_regex режет тулы, search-режим ломает tool-calling, 503 chat_admission_busy, эмуляция `<tool>` работает, план кастомного провайдера, qwen-web baxia-WAF стена -- [forgetmeai-free-web-chat-proxies](concepts/forgetmeai-free-web-chat-proxies.md) — семейство бесплатных web-chat прокси ForgetMeAI (DeepSeek/Qwen/GLM/Kimi): паттерн «обход капчи через реальную браузерную сессию», PoW-солвер DeepSeek, браузерный путь через baxia-WAF (fetch в page.evaluate), CloakBrowser, классификаторы капчи; карта «что берём в lwp» -- [sched-admin-client-openapi](concepts/sched-admin-client-openapi.md) — F&F раунды 2026-08-18: Python-клиент одной командой из спеки (openapi-generator 7.24.0); r1 0.1.2 — 24/24+smoke 19/19, находки F-1..F-5 (8 операций на raw-обходе); r2 0.2.0/0.3.5 — все 5 закрыты, raw_ops удалён, 11/11 typed-only, вердикт «claim подтверждён полностью» -- [openapi-generator-3-1-spec-gotchas](concepts/openapi-generator-3-1-spec-gotchas.md) — генерация клиентов из 3.1-спеки (openapi-generator 7.24.0, python): wrapped-ответы {} → ValidationError (raw-обход историчен: r2 спека фикснута), const:true → строковый enum, oneOf+discriminator → actual_instance=None (r2: плоская схема), java из PATH а не JAVA_HOME, InlineObjectN-имена, info.version не бьётся -- [nvm-junction-ismain-gate-gotcha](concepts/nvm-junction-ismain-gate-gotcha.md) — ESM isMain-гейт молча не выполняется под junction'нутым npm-рутом (nvm/mise/volta); realpath-фикс; диагностика за 30 сек -- [nvm-windows-node-switch](concepts/nvm-windows-node-switch.md) — смена версии Node на nvm-windows: junction, per-version глобалы, PATH-кэш, npm 11 allow-scripts; Node 22→24 evidence -- [sched-fnf-r8-verification-stand](concepts/sched-fnf-r8-verification-stand.md) — стенд верификации F&F r8 (schedule-as-entity): путь, версии core 0.40.2/ui 0.2.0, карта репро-скриптов (миграции/движок/политики/API 52/UI 13), подтверждённый контракт (ceiling, retryCount, snapshot, pause AND, tz-hoist, PATCH-merge), находки №5 (U1 форма-tz, U2 вёрстка), как закрыть раунд -- [sched-fnf-testing-method](concepts/sched-fnf-testing-method.md) — метод F&F-тестирования (урок r10): тест по СКОМПИЛИРОВАННОЙ доке (dev-сервер localhost:5108), не по сырцам; порядок хендоффа 1–6 (код → доки source парсящиеся → generate/dist → push ноль ahead → verdaccio → пинг), битый пример = возврат, schedjs.com = заглушка (деплой не требовать) -- [windows-docker-test-harness-gotchas](concepts/windows-docker-test-harness-gotchas.md) — грабли Windows-верификации против docker: rmSync до DatabaseSync (EBUSY), vitest 10s vs ~11s docker-DDL, монотонность фейк-часов, изоляция DB на сценарий, висельники node-процессов -- [snolla-smtp-mail-delivery-2026-08](concepts/snolla-smtp-mail-delivery-2026-08.md) — SMTP-инцидент 12–14.08: 525 (Яндекс отключил протоколы) → 554 (холодный ящик, мульти-получатели) → 550 (from not owned); фиксы + монитор на VDS + рецепт деплоя -- [keenetic-rci-api](concepts/keenetic-rci-api.md) — KeeneticOS RCI API: auth, чтение конфига, запись через CLI-parser, DNS-override против сломанного hairpin - - - -- [admin-api-embed-pilonuxt](concepts/admin-api-embed-pilonuxt.md) — admin-api embedded in pilonuxt /admin/api, stack 25 retired (2026-08-05) -- [pilonuxt-restart-vs-redeploy-2026-08-16](concepts/pilonuxt-restart-vs-redeploy-2026-08-16.md) — стек 16: рестарт (in-memory viewModel кэш content-api) vs пересборка (код); инцидент 500 на новинках (Content object vs array → core 0.26.7/admin-api 0.11.0); откат-цепочка образов 2026-08-16 -- [namespace-consolidation-snolla](concepts/namespace-consolidation-snolla.md) — @snollajs → @snolla, 8 sites bumped, 17 packages (2026-08-04/05) -- [admin-infra-project](concepts/admin-infra-project.md) — design + migration plan для OpeItcLoc03/admin (canonical) -- [backup-inventory-2026-06](concepts/backup-inventory-2026-06.md) — карта всех машин × что реально бэкапится (с доказательством); дыры по приоритету (kreknin SPOF, nl-vds x-ui.db, bookva-*, openwrt) — триггер: MSSQL 3-нед gap -- [bindmount-config-edit-preserve-mode](concepts/bindmount-config-edit-preserve-mode.md) — правка bind-mounted config'а через mktemp+mv роняет режим 644→600 → non-root контейнер (uid 1000) EACCES crash-loop; chmod --reference=backup. + гоча: bind-mount затеняет config образа целиком → класть полную секцию, не дельту. Инциденты books-job-scheduler + task-runner 2026-06-18 -- [vds-kzntsv-ssh-access](concepts/vds-kzntsv-ssh-access.md) — SSH access audit на vds-kzntsv (infra VDS), retained keys + add/revoke processes (was misnamed `books-ssh-access` до 2026-05-25 — content всегда был про vds-kzntsv) -- [compose-bcrypt-escape-trap](concepts/compose-bcrypt-escape-trap.md) — Docker Compose ест `$` в bcrypt hashes -- [db-tls-self-signed-via-traefik-raw-tcp](concepts/db-tls-self-signed-via-traefik-raw-tcp.md) — DB TLS через traefik raw TCP с self-signed certs -- [docker-host-loopback-detect](concepts/docker-host-loopback-detect.md) — Docker host-loopback detection — как доказать что host.docker.internal не петля -- [emspb-vds-deploy-runbook](concepts/emspb-vds-deploy-runbook.md) — emspb.ru snolla-app на VDS (стек 18): сборка/env/cutover 07-02 + **0.42.1 in-place bump 07-05** (образ `emspb:95a5c42`) -- [es-destructive-delete-incident-2026-05-26](concepts/es-destructive-delete-incident-2026-05-26.md) — ES indices wiped on canonical — **true root cause (2026-05-29): ransom-бот через открытый `0.0.0.0:9200` мимо traefik, free-ES без auth**; гипотеза operator-error опровергнута. Fix = убрать публикацию host-порта (Control #3) + restore. Exposure-audit: mongo/mariadb/minio тоже exposed (credentialed) -- [future-resilient-architecture-goals](concepts/future-resilient-architecture-goals.md) — fault-tolerance roadmap placeholder (расширяется через `[resilience-roadmap-design]`) -- [hyper-backup-structure-and-recovery](concepts/hyper-backup-structure-and-recovery.md) — Hyper Backup — структура репо и стратегия восстановления -- [iis-migration-2026-05-19-postmortem](concepts/iis-migration-2026-05-19-postmortem.md) — post-mortem миграции CMS на нативный IIS, 2026-05-19 -- [labtools-vds-deploy-runbook](concepts/labtools-vds-deploy-runbook.md) — labtools.ru snolla-app на VDS (стек 17): сборка/env/cutover 07-02 (Yandex DNS!) + **0.42.1 in-place bump 07-05** (образ `labtools:566d41c`, order-фикс + sitemap починен) -- [labtools.pro-vds-deploy-runbook](concepts/labtools.pro-vds-deploy-runbook.md) — labtools.pro snolla-каталог на VDS (стек 19): сборка/env/cutover 07-02 + **0.42.1 in-place bump 07-05** (образ `labtools-pro:0610432`, order-парити 3 секции) -- [tandemmebel-vds-deploy-runbook](concepts/tandemmebel-vds-deploy-runbook.md) — tandemmebel.ru snolla-блог-портфолио на VDS (стек 20): cutover 07-12 (DNS reg.ru, LE) + **0.42.1 in-place bump 07-12** (образ `tandemmebel:0cd9351`, 184/184 parity, /articles 404 benign) -- [maljarka-vds-deploy-runbook](concepts/maljarka-vds-deploy-runbook.md) — maljarka.tandemmebel.ru snolla-лендинг на VDS (стек 23): restore 2026-07-31 (потерян при decomm RUVDS 2026-07-21, не был в v3 тираже). Клон on.snolla.com, siteId A2476738, layout.liquid byte-identical .NET-admin рендеру (43730B). Тираж 7/7 -- [minio-imgproxy-on-vds](concepts/minio-imgproxy-on-vds.md) — MinIO+imgproxy stack на VDS, миграция с upgrade 2020→2025 + DNS swap pending -- [minio-split-cutover-runbook](concepts/minio-split-cutover-runbook.md) — разнос MinIO-контента: Track A books-vds 2020→2025 (shared+bookva) + Track B CMS-медиа → vds 2025. Progress: B1 миррор✅ B2 DNS-flip✅ B3 on-snolla v2-пилот✅ LIVE, B3v3 (snolla 0.43.2 опубликована, live verify ЗЕЛЁНЫЙ, resolutions optional) ⚡READY, B4/B5/Track A TODO. v3 — конечная цель. -- [morethencms-null-settingsdata-https-502](concepts/morethencms-null-settingsdata-https-502.md) — MoreThenCms тенант с `Sites.SettingsData=NULL` → 502 только на HTTPS (нет `httpSecure` → KeyNotFound); fix = UPDATE + recycle. maljarka 2026-06-08 -- [snolla-admin-appdata-acl-500-after-scp-migration](concepts/snolla-admin-appdata-acl-500-after-scp-migration.md) — админский delete/upload ассета → 500: app pool имел на `App_Data` только RX после scp-миграции → `File.Delete` UnauthorizedAccessException. Fix = icacls Modify. + split-brain: админка пишет Local, боевой app читает MinIO -- [snolla-local-admin-and-on-snolla-migration-design](concepts/snolla-local-admin-and-on-snolla-migration-design.md) — design: Task A восстановить локальный .NET-админ (catch-all IIS `snolla`, hosts-override, MinIO drop-in) + Task B on.snolla.com → VDS как Node snolla-app -- [snolla-local-admin-https-binding](concepts/snolla-local-admin-https-binding.md) — реализация 2026-07-29: HTTPS на локальном IIS-админе `snolla` :443 (self-signed wildcard `*.snolla.com` в Trusted Root, без traefik) + pilorama98.snolla.com в hosts; рабочий entry-point `/admin/account/login` (голый `/admin` → canonical на public-домен, предсуществующее поведение CMS) -- [mssql-container-data-restore](concepts/mssql-container-data-restore.md) — MSSQL контейнер с восстановленными production data — паттерн -- [mssql-on-vds](concepts/mssql-on-vds.md) — MSSQL Express 2022 Linux на VDS — миграция + login orphan fix + traefik TCP gotchas -- [ocis-on-vds-deploy-recipe](concepts/ocis-on-vds-deploy-recipe.md) — oCIS на VDS — deploy recipe + non-obvious gotchas (UID 1001 vs 1000, basic auth, LibreGraph user-create) -- [portainer-2.21-admin-password-regression](concepts/portainer-2.21-admin-password-regression.md) — Portainer 2.21 `--admin-password` regression + min 12-char policy -- [portainer-stack-management-books-vds](concepts/portainer-stack-management-books-vds.md) — Portainer-managed stacks на books VDS — pattern application + migration log 2026-05-25 -- [portainer-stack-management-vds](concepts/portainer-stack-management-vds.md) — Portainer-managed stacks на VDS — canonical pattern + migration script + stack-redeploy recipe (новый тег) + gotchas (вкл. PS 5.1 ISO-8859-1 коррапт кириллицы при API round-trip) -- [snolla-live-prod-inplace-image-bump](concepts/snolla-live-prod-inplace-image-bump.md) — переиспользуемый рецепт обновления образа на ЖИВОМ snolla-стеке VDS in-place (build→throwaway-staging-acceptance С VDS→env-preserving Portainer PUT→live-smoke); вкл. `put-stack.js`; отработан на тираже 0.42.1 (4 сайта: labtools/emspb/labtools.pro 2026-07-05 + tandemmebel 2026-07-12) -- [stostayer-web-deploy-runbook](concepts/stostayer-web-deploy-runbook.md) — деплой легаси web (`www.stostayer.ru`) на прод клиента: build→registry→Portainer-стек 16 через container-IP API с хоста; **деплой 0.3.23 (2026-08-13): calculator-баннер + стек-фикс**; гочи — один push без retry (VPN-IP банится хостером), offline-build не всегда проходит (linux-бинари esbuild/rollup не в cache → временный VERDACCIO_TOKEN ARG/ENV), Portainer API base=http://IP:9000 (порт обязателен) -- [stostayer-admin-minio-config](concepts/stostayer-admin-minio-config.md) — локальная .NET-админка stostayer (:8090) на stostayer MinIO: креды НЕ в pass (в stostayer.new config), endpoint `minio-api.stostayer.ru` (minio.stostayer.ru — не API-порт), region `us-west-1` (не local); fix 2026-07-22 — 3 замены в Web.config, админка=прод единое хранилище -- [stostayer-markdown-render-404-incident-2026-07](concepts/stostayer-markdown-render-404-incident-2026-07.md) — постмортем 2026-07-24: 404/500 на body_repair_service = двойной корень (snolla `markdown();` без присвоения + exact-pin-staleness — content-api `workspace:*` запёк `@snollajs/snolla: 0.7.4`, фиксовая 0.7.7 легла top-level, content-api юзал nested баговую). Фикс snolla 0.7.7/core 0.26.1 + content-api 0.8.2 `^0.7.7`; деплой 0.3.22 через pscp+load (без :443 push). Pre-build чек yarn.lock на single-snolla ловит класс. Cleanup `workspace-caret-interpackage-deps` (vitya scope) -- [proxy-debugging-test-the-real-client](concepts/proxy-debugging-test-the-real-client.md) — анти-паттерн: свои curl/standalone-тесты «работают», а боевой клиент пользователя нет; источник истины — реальный клиент -- [reality-pq-mldsa65-dest-incompatibility](concepts/reality-pq-mldsa65-dest-incompatibility.md) — REALITY+ML-DSA-65 (PQ) не работает с не-PQ dest (Akamai/intel шлёт HRR); fix = PQ-совместимый dest или выключить PQ; **caveat: снятие PQ ≠ рабочий Reality у GUI-клиентов** -- [recovery-architecture-snapshot](concepts/recovery-architecture-snapshot.md) — текущая recovery architecture (2026-05-19/21, attempt 2) -- [registry-gc-mount-and-modify-flag](concepts/registry-gc-mount-and-modify-flag.md) — Docker Registry GC mount layout + `-m` flag -- [registry-kzntsv-auth-model](concepts/registry-kzntsv-auth-model.md) — registry.kzntsv.site = standalone registry:2 + htpasswd Basic (бинарный доступ, НЕ Gitea-packages, нет per-repo ACL/robot-токенов); как заводить htpasswd-юзеров (hot-reload), GC через v2 DELETE; юзеры vitya + books-ci -- [registry-oci-image-index-gc](concepts/registry-oci-image-index-gc.md) — books-* образы в registry = OCI image-index (buildx multi-manifest): top-level `.config`=null, дата `.created` живёт в платформенном sub-manifest; наивный GC по top-level дате → null у всех → no-op (keepLastN не применяется). DELETE по index-digest, дата из sub. + **dangling-индексы**: 16/19 тегов books-web — тег жив, sub-manifest `MANIFEST_UNKNOWN` (вычищен прежним host-GC); date-GC защищает их как null-dated → не чистит, хотя они и есть мусор. Зафиксировано на books registryGc dryRun 2026-06-18; 2026-08-23: registry 2.8.3 отдаёт OCI-index только на OCI-Accept (docker pull → «manifest unknown»), фикс — пуш docker schema2-манифестом, host-GC на живом реестре нецелесообразен (dry-run ~2.5МБ) -- [rusonyx-vps-onboarding-quirks](concepts/rusonyx-vps-onboarding-quirks.md) — Rusonyx VPS onboarding quirks (Astra Облако / myvm.rusonyx.ru) -- [traefik-acme-json-to-iis-cert-import](concepts/traefik-acme-json-to-iis-cert-import.md) — Экспорт LE certs из traefik acme.json в IIS (PFX + SNI bindings) -- [traefik-file-watch-wsl2-broken](concepts/traefik-file-watch-wsl2-broken.md) — Traefik file-watch broken под Docker Desktop Windows (WSL2 9p mount) -- [traefik-on-windows-docker-desktop](concepts/traefik-on-windows-docker-desktop.md) — Traefik на Windows Docker Desktop — нюансы -- [traefik-tcp-passthrough-vs-starttls](concepts/traefik-tcp-passthrough-vs-starttls.md) — Traefik TCP passthrough vs STARTTLS-protocols -- [vbox-windows-stability-tuning](concepts/vbox-windows-stability-tuning.md) — VirtualBox + Windows-гость — нюансы стабильности cross-hypervisor миграции -- [vds-kzntsv-dhcp-outage-2026-05-28](concepts/vds-kzntsv-dhcp-outage-2026-05-28.md) — DHCP outage 2026-05-28 на vds-kzntsv — диагностика + recovery runbook (link UP но без IPv4 → статика + тикет хостеру) -- [verdaccio-prune-semantics](concepts/verdaccio-prune-semantics.md) — Verdaccio prune semantics — proxied vs locally-published -- [verdaccio-restore-packument-desync](concepts/verdaccio-restore-packument-desync.md) — disaster-restore: тарболл на диске + древний packument → EEXISTS 409; fix = снять коллизирующий .tgz, не rm -rf каталог -- [verdaccio-token-lifecycle](concepts/verdaccio-token-lifecycle.md) — restart trap (ephemeral secret) + max_users:-1 pnpm 409 + JWT fix + yarn clients -- [yarn-npm-minimal-age-gate](concepts/yarn-npm-minimal-age-gate.md) — Yarn ≥4.16 client-side age-gate (YN0016 «quarantined») на версиях <24ч; глобальный, не per-scope; fix npmMinimalAgeGate:0 -- [winacme-iis-owin-catchall-http01](concepts/winacme-iis-owin-catchall-http01.md) — win-acme HTTP-01 авто-renewal LE на IIS под OWIN-catch-all CMS — challenge в отдельном No-Managed-Code приложении + патч шаблона; заменяет ручной PFX-импорт -- [wd40efax-smr-cascade](concepts/wd40efax-smr-cascade.md) — WD40EFAX SMR Cascade — root cause NAS failure 2026-05-18 -- [windows-server-2025-core-bootstrap](concepts/windows-server-2025-core-bootstrap.md) — Win Server 2025 Core (RUVDS) — bootstrap для IIS-хоста: default-blockers + transfer-методов матрица -- [mcp-init-resilience](concepts/mcp-init-resilience.md) — mcp-init-resilience -- [deploy-policy-mappa](concepts/deploy-policy-mappa.md) — deploy-policy-mappa - -## Packages - -<!-- (none yet) --> - -## Summaries - -- [omniroute-session-2026-08-22](summaries/omniroute-session-2026-08-22.md) — хронология сессии настройки omniroute web-комбо (deepseek-web): корни вылетов, фиксы, тесты эмуляции, план кастомного провайдера -- [iis-host-migration-2026-05-19](summaries/iis-host-migration-2026-05-19.md) — IIS Host Migration Session 2026-05-19 chronology -- [iis-migration-to-ruvds-2026-05-23](summaries/iis-migration-to-ruvds-2026-05-23.md) — IIS migration to RUVDS — session 2026-05-23/24 (SSH/scp pivot, 25 SNI bindings, partial DNS cutover) -- [nas-recovery-session-2026-05-18](summaries/nas-recovery-session-2026-05-18.md) — NAS Recovery Session 2026-05-18/19 chronology -- [books-vds-backup-daily-kreknin-2026-05-25](summaries/books-vds-backup-daily-kreknin-2026-05-25.md) — books VDS daily backup → kreknin pipeline standup 2026-05-25 (ES file-snapshot repo + DB dumps + curl SMTP) -- [ruvds-backup-daily-kreknin-2026-05-24](summaries/ruvds-backup-daily-kreknin-2026-05-24.md) — RUVDS daily backup → kreknin pipeline standup 2026-05-24 (rclone+SFTP, SYSTEM task) -- [nl-vds-3xui-setup-2026-06-05](summaries/nl-vds-3xui-setup-2026-06-05.md) — NL VDS 3x-UI setup+troubleshooting session; итог: у реальных клиентов из РФ работает только plain VLESS 32030 (Reality/MTProto/SOCKS не поднялись) -- [de-vds-3xui-setup-2026-07-14](summaries/de-vds-3xui-setup-2026-07-14.md) — DE VDS (Fornex Germany) setup session; 3x-ui 3.5.0 gotchas: CSRF на panel POST, прямой DB-insert не рендерит (client-tables), wrapper x-ui setting не применяет; plain VLESS 32030 confirmed на real RF-клиенте -- [vdsina-outline-3xui-inventory-2026-06-24](summaries/vdsina-outline-3xui-inventory-2026-06-24.md) — VDSina Amsterdam (46.151.25.64) inventory: Outline 11 keys + 3x-UI VLESS, секреты в pass vdsina-outline -- [vds-kzntsv-bootstrap-2026-05-20](summaries/vds-kzntsv-bootstrap-2026-05-20.md) — VDS bootstrap session 2026-05-20 chronology -- [vds-kzntsv-incident-2026-05-28](summaries/vds-kzntsv-incident-2026-05-28.md) — DHCP outage incident session 2026-05-28 — chronology + ticket text +**Не читать. Не править.** Канон — mappa (`mcp__mappa__*`): wiki-сущности проекта, конвенции — AGENTS-сущность. Скил: `mappa-knowledge`. diff --git a/.wiki/log.md b/.wiki/log.md index b454f38..21695bc 100644 --- a/.wiki/log.md +++ b/.wiki/log.md @@ -1,157 +1,3 @@ -## [2026-08-23] ingest | NEW concepts/mappa-vds-deploy-runbook.md — ранбук деплоя/редеплоя mappa на VDS (стек 26, образ registry.kzntsv.site/mappa:0.1.0, БД на общем postgres:16, endpoint mappa.vds.kzntsv.site). #1012 закрыта; грабли: schema.sql в dist при tsc-сборке, docker pull до Portainer restart, не-ad-hoc compose, внешний DATABASE_URL для импорта. index.md updated (+1). +# ⛔ Файловый канал закрыт -## [2026-08-19] ingest | NEW concepts/sched-fnf-testing-method.md — метод F&F-тестирования (урок r10): тест по скомпилированной доке (dev-сервер localhost:5108), порядок хендоффа 1–6, битый пример = возврат, schedjs.com заглушка. index.md updated (+1). -## [2026-08-16] ingest | concepts/pilonuxt-restart-vs-redeploy-2026-08-16.md — день деплоев стека 16 (7 операций): рестарт vs пересборка разведены как отдельные операции. Рестарт сбрасывает in-memory viewModel кэш content-api (цена за м³ подтянулась после рестарта без пересборки); пересборка — только для кода. Анти-симптом: 500 на категориях с логом `@snolla/core viewModels/index.js:419 _.find(x=>x.name)` = код/данные (Content object vs array, admin-api create), рестарт не лечит — фикс snolla core 0.26.7 + admin-api 0.11.0 array-canon (1128ddc → 64c2539). Откат-цепочка образов 64c2539→…→8b7e8c4. +index.md. Монорепа-сторона заингестена прогером (lazy-data-not-found-404, admin-api-content-wire array-canon, cms-edits-cache-restart, v-html-on-component-ssr, admin-api-content-edit). -## [2026-08-14] decision | FormSubmissions.Content ₽→«?» — координация закрыта. ALTER на клиентской stostayer сделан (.admin); snolla опубликовала `@snolla/data@0.15.1` (STRING→TEXT=NVARCHAR(MAX), тесты 7/7); скан живых БД VDS: MoreThenCms+Staging уже nvarchar (ALTER не нужен), VDS-stostayer varchar — не прод, не трогаем (роль — к оператору); legacy stostayer.web на @snollajs 0.7.x — фикс не едет, ALTER покрывает (backport опция `065bb44`); формы есть у всех сайтов тиража (pilorama98 2516/labtools 916 — вопреки гипотезе snolla). Бонус: email-контракт задокументирован snolla (forms-email-contract.md), warn при пустом `to` — ждёт решения vitya. Таска snolla [formsubmissions-content-nvarchar] закрыта. -## [2026-08-14] implement | MSSQL stayer_site: ALTER FormSubmissions.Content varchar(max) cp1251 → NVARCHAR(MAX) (запрос их прогера: ₽→«?» в письмах с калькулятора). Verify до/после (nvarchar max). Live-тест не гонял, тестовые заявки #68485/#68486 не удалял (ждут подтверждения). Соединение: tedious из packages/web node_modules, MSSQL 91.222.236.225:1433 открыт снаружи. -## [2026-08-14] lint | inbox-rename cascade audit (idea 13): rg по живых инструкциях .admin — единственный хит log.md (op-log история, ок); stostayer.new — только маркеры «устаревший путь»; ack-письма в .agents/inbox подтверждены (.read); pi-расширение inbox-monitor (деплой+source of truth) на .agents/inbox, .claude-inbox не упоминается. Подтверждение оператору отправлено в .workshop/.agents/inbox. -## [2026-08-13] ingest | UPDATE concepts/stostayer-web-deploy-runbook.md — offline-гоча ПОЧИНЕНА их коммитом `5c583f8` (supportedArchitectures libc glibc/musl): сетевой yarn install на build-машине долил linux-зипы в кэш, будущие offline-сборки чисты. Runbook переписан с рецептом. Ack-обмен с stostayer.new (их инбокс `.agents/inbox/`). -## [2026-08-13] ingest | UPDATE concepts/stostayer-web-deploy-runbook.md — деплой stostayer-web:0.3.23 на прод www.stostayer.ru (запрос их прогера, письмо inbox). Нюансы зафиксированы: (1) offline-build НЕ проходит — в `.yarn/cache` нет linux-бинарей `@esbuild/linux-x64`/`@rollup/rollup-linux-x64-gnu` → обход через временный `ARG/ENV VERDACCIO_TOKEN` + `--build-arg` (после — revert); (2) Portainer API с хоста — `base=http://<IP>:9000` (порт обязателен); (3) прод-тег в таблице актуализирован 0.3.18→0.3.23; (4) блок деплоя 0.3.23. Task [stostayer-web-deploy-0-3-23] 🟢. -## [2026-08-12] implement | UPDATE entities/kreknin-synology.md — автосинк LE-серта: acme-nginx-sync.sh (cron 15 мин) ротирует серт git.kreknin.site из traefik acme.json в nginx DSM автоматически. -## [2026-08-12] implement | UPDATE entities/kreknin-synology.md — LE-серт git.kreknin.site (из traefik acme.json) подложен в nginx DSM reverse proxy. Из LAN: валидный LE + Gitea, без DSM-пароля (root SSH). -## [2026-08-12] implement | UPDATE entities/kreknin-synology.md — reverse proxy git.kreknin.site:443 → traefik:4443 на DSM nginx (server.ReverseProxy.conf w3conf + reload). Из LAN git.kreknin.site → Gitea (DNS-override + proxy), снаружи LE-серт. Осталось: LE-серт в DSM для git.kreknin.site (из LAN, без предупреждения). -## [2026-08-12] implement | NEW concepts/keenetic-rci-api.md — разобрана автоматизация KeeneticOS 5.1.3 (RCI: GET /rci/show/*, POST /rci/ массив parse-команд). Через API добавлены DNS-записи git.kreknin.site/kreknin.site → 192.168.1.43 (решение сломанного hairpin). Проверено с LAN: всё 200. Правила порт-форвардинга в 5.x лежат в ip.static (to-host=MAC). -## [2026-08-12] implement | UPDATE entities/kreknin-synology.md — Gitea 1.27.1 + postgres 16 на kreknin (для Алексея), traefik починен (restart policy), LE-серт git.kreknin.site, проброс 80/443→8000/4443 на Keenetic. KeeneticOS Viva API закрыт — правила в GUI. NAT loopback сломан → нужен DNS-override git.kreknin.site→192.168.1.43. Пароли: pass kreknin/keenetic + pass kreknin/gitea. -# Wiki op-log -## [2026-08-23] ingest | UPDATE concepts/books-vds-no-gitea-actions.md + concepts/registry-oci-image-index-gc.md — разбор sched docker-runner `createPickingListPdf`: (1) docker CLI читает креды только из `${DOCKER_CONFIG}/config.json`, не `docker-config.json` (rename на slovo); (2) теги buildx/curl = OCI image-index, registry 2.8.3 отдаёт только на OCI-Accept, docker daemon → «manifest unknown» при отсутствующем child (после прежних host-GC) — фикс: перепуш docker schema2-манифестом (config mediaType `container.image.v1+json`, не `distribution.image.config.v2+json`); (3) production-конфиг daemon должен лежать в монтированной конфиг-дире (иначе плейсхолдер `<SET_IN_PROD_VOLUME_MOUNT>` в tasks.json). host-GC dry-run 2026-08-23: ~2.5МБ реклайма → нецелесообразен. -## [2026-08-23] ingest | UPDATE concepts/books-vds-no-gitea-actions.md — деплой books-web `master-a448cad` (фикс 8 embed-api роутов, оба тенанта, инцидент DELETE-404 закрыт). Уроки в ранбук: (1) `docker save` на Docker 29 сразу даёт OCI-layout (index.json+blobs) — конвертация не нужна, amd64 child manifest в `blobs/sha256/<index-digest>`; (2) `PUT /api/stacks/<id>` синхронный, тянет образ ВНУТРИ запроса — клиентский таймаут рвёт ожидание, оп доделывается server-side, НЕ пере-PUT, проверять контейнеры через `/api/endpoints/1/docker/containers/json` + images/json; (3) смоук sched: `docker exec <sched> node -e fetch(localhost:3031/api/runs)` (curl в контейнере нет), Nuxt `/admin/scheduler/**` требует JWT isAdmin — `?token=` 403. Процедура сборки шаг 3 уточнён. -## [2026-08-23] ingest | NEW concepts/sched-ozon-creds-stub-contract.md — контракт кредов Ozon slovo/bookva: мисматч-пары в обеих БД — намеренные заглушки (анти-утечка после tenant-split). Урок: .admin «починил» заглушку (slovo seller 1 ← bookva-ключ) → откат 12:07Z по подтверждению владельца; зеркальная заглушка bookva seller 2 (12:15Z). Безопасность: break-on-error 2a38fb5 задеплоен (фейл-фаст без спина). verify по SHA2 (d6227b = slovo-ключ, f700f3 = bookva-ключ). НЕ чинить. Связано: инцидент 2026-08-23, перевод slovo → file-managed (887bbae). -## [2026-08-14] ingest | NEW concepts/snolla-smtp-mail-delivery-2026-08.md — postmortem SMTP-инцидента 12–14.08: 525 SMTP disabled (Яндекс 360, доменка) → переезд на e-16513832@yandex.ru (app-password); 554 SPAM на 2+ получателях от холодного ящика → @snolla/mailer@0.9.0 per-recipient (forms-api 0.2.0, web 0.44.0); 550 from not owned → mailSettings.from во всех production.json + шаблон pilorama98checkoutconfirm. Монитор SMTP на VDS (cron 08:00, ntfy) + parity-чек Portainer env. Рецепт деплоя тиража (порядок, гочи: ad-hoc контейнер vs Portainer, regex-парсинг image, yarn up -R data). - -Append-only log of wiki operations (ingests, promotions, lints, migrations). - -## [2026-08-05] ingest | concepts/namespace-consolidation-snolla + concepts/admin-api-embed-pilonuxt — крупнейшие изменения сессии: неймспейс-миграция @snollajs→@snolla (17 пакетов, 8 сайтов бампнуты), admin-api embed в pilonuxt /admin/api, retire стека 25. +index.md строки. - -## [2026-07-30] migrate | RELOCATE runbook `scripts/minio-split-cutover/runbook.md` → `concepts/minio-split-cutover-runbook.md` (дисциплина: runbook'и → `.wiki/concepts/`, не `scripts/`; `scripts/` для executable). +UPDATE content: v3-секция (snolla 0.43.2 опубликована, live MinIO verify ЗЕЛЁНЫЙ оба конфига, resolutions=optional determinism НЕ mandatory — regression-гипотеза v3 SigV4 vs MinIO НЕ воспроизвелась end-to-end, «CONFIRMED» framing откатан), креды нормализованы → `pass show minio-vds/full-env` (были plaintext в bash-примерах — wiki-конвенция «креды в pass, не в git»). +index.md строка. Старый `scripts/minio-split-cutover/` удалён (в нём кроме runbook'а ничего не было). Cross-ref [[on-snolla-vds-deploy-runbook]], [[minio-imgproxy-on-vds]], [[snolla-live-prod-inplace-image-bump]], [[../entities/vds-kzntsv]], [[../entities/books-vds]]. Related memory: admin `aws-sdk-v3-smithy-sigv4-minio-regression`, `aws-sdk-v2-minio-2025-works-no-smithy-pin`. - -## [2026-07-29] implement | NEW concepts/snolla-local-admin-https-binding.md + UPDATE index.md (concepts +2 строки) — локальный .NET-админ snolla (catch-all IIS `snolla`) переведён на HTTPS. Повод: `https://pilorama98.snolla.com/admin` → 404 → ERR_CONNECTION_CLOSED. Корень 404: `pilorama98.snolla.com` не был в hosts-override → реальный DNS → VDS `89.253.255.94` → traefik без роута (системный прокси `127.0.0.1:10808` маскировал «server 127.0.0.1» в curl). pilorama98 — реальный сайт в `dbo.Sites` (Alias=pilorama98, PrimaryDomain=pilorama98.ru, Live), вне тиража-5 из design-дока. Корень ERR_CONNECTION_CLOSED: браузер HSTS-апгрейдил http→https, а локальный IIS без 443. Решение: **IIS-native self-signed wildcard `*.snolla.com` на :443 + Trusted Root**, НЕ traefik (локальный acme.json пуст → LE на snolla.com нет; traefik-as-TLS-terminator = NAT-loopback/docker.sock/URL-Rewrite/SERVER_PORT-leak готчи без выгоды). Скрипт elevated (UAC): `New-SelfSignedCertificate -Subject '*.snolla.com' -DnsName '*.snolla.com','snolla.com' -TextExtension EKU serverAuth` (гоча: `-DnsName` конфликтует с ручным SAN `2.5.29.17` → SAN через `-DnsName`) → копия в `Cert:\LocalMachine\Root` → `New-WebBinding snolla https 443 *` + `AddSslCertificate` + restart. Cert thumb `6E2212B4C13BD28E2FF680322F11741193E8C619`, SAN `*.snolla.com,snolla.com`, 10 лет — покрывает все alias'ы. hosts: добавлен `pilorama98.snolla.com` (elevated, построчно — `Get-Content -Raw` мог опустошить файл). Результат: `https://<alias>.snolla.com/admin/account/login` → 200 форма SNOLLA (pilorama98/kupimknigi/internal/tandemmebel верифицированы). **Голый `/admin` → 302 `/login` → 301 `https://www.<PrimaryDomain>/`** — предсуществующее поведение CMS (оператор подтвердил: «так и было» на RUVDS), не поломка миграции; рабочий entry-point `/admin/account/login`. `on.snolla.com` → 404: hosts стоит `internal.snolla.com` (реальный alias сайта `on` = `internal`), не `on` (design-док писал `on`). Trade-off: self-signed не LE — допустимо (админка локальная, FW закрыт). +index.md (2 строки: design-док ранее не был индексирован + новый page). Cross-ref [[snolla-local-admin-and-on-snolla-migration-design]], [[traefik-on-windows-docker-desktop]], [[stostayer-admin-minio-config]]. - -## [2026-07-22] fix | NEW concepts/stostayer-admin-minio-config.md — локальная .NET-админка stostayer (`C:\sites\stostayer\Web.config`, IIS :8090) переведена на рабочий stostayer MinIO. Исходно S3-провайдеры (`MoreThenCms.FileStorage.S3` в `bin/`) уже стояли, но с 3 багами: (1) чужие креды `AKIAJ2YJP72W6ZHCRE6Q` = books-vds/snolla root-ключ (копипаст из snolla catch-all admin, не заменён на stostayer-свои → "Access Key Id does not exist"); (2) `serviceURL=https://minio.stostayer.ru` — НЕ S3 API-порт ("S3 API Requests must be made to API port"), прод юзает `minio-api.stostayer.ru`; (3) `authenticationRegion="local"` → SigV4 регион-мismatch → `AmazonS3Exception: the region is wrong; expecting 'us-west-1'` (400) на `GalleriesStorage.EnsureKeyPrefixExists` (`ToolsController.ImagePreview` → `CreatePreview`). Креды stostayer MinIO **не в pass** — лежат в `~/projects/stostayer.new/packages/web/config/default.json` → `s3`: accessKey `stayer_minio`, endpoint `https://minio-api.stostayer.ru`, region `us-west-1`. Fix: 3 глобальные замены × 9 S3-блоков в Web.config (accessKey/secretKey[XML-escaped `>`/`&`]/serviceURL) + region `local`→`us-west-1`; бэкап `Web.config.bak-2026-07-22`; IIS recycle, лог чист. mc `alias set` + ListBuckets OK (10 бакетов: assets/galleries/themes/…); `assets/9cf0a8e52cf144619fd290606a146d35/` несёт реальные файлы. Acceptance оператором: картинки грузятся. Админка=прод единое хранилище, ручное зеркалирование в `App_Data` упразднено. +index.md concepts-строка. Cross-ref [[galleries-storage-class-local-not-s3]], [[snolla-admin-appdata-acl-500-after-scp-migration]], [[minio-imgproxy-on-vds]]. - -## [2026-07-14] ingest | sources/de-vds-3xui-setup-2026-07-14.md (new) + entities/de-vds-3xui.md (sources: привязан) — полноценный ingest DE-сессии: sources-хроника с борьбой против 3x-ui 3.5.0 (CSRF-403 на login → `CSRFMiddleware`/`X-CSRF-Token`/`GET {BP}csrf-token`; прямой DB-insert в `inbounds` не рендерит → client-tables `clients`/`client_inbounds`/`client_traffics` заполняются только `AddInbound` → panel API `POST {BP}panel/api/inbounds/add` с stringified settings/streamSettings/sniffing; wrapper `x-ui setting` не применяет креды → бинарник). Эталон JSON снят с рабочего nl-vds 32030. +index.md sources-строка. Real RF-client confirmed. - -## [2026-07-14] decision | entities/de-vds-3xui.md — real-client из РФ подтверждён (user подключён через 32030, сессия идёт через узел). Plain VLESS = proven-working на обоих NL+DE. Задача [de-vds-3xui-setup] закрыта. Перекрестил `Проверено` блок ✅. - -## [2026-07-14] ingest | entities/de-vds-3xui.md (new) — поднят VPN-VDS в Германии (Fornex `130.17.17.158`, Ubuntu 24.04, 1/2G/20G), аналог [[nl-vds-3xui]]. Повторён **только proven-working plain VLESS 32030** `security=none` (на NL у боевых клиентов из РФ заработал только он); Reality/MTProto/SOCKS5 намеренно НЕ подняты. Креды → `pass de-vds-3xui/full-env`. 3x-ui тут **3.5.0** (на NL была 3.2.7) — вскрыл новые гоча, зафиксированы в entity: (1) **CSRF на всех panel POST** (`CSRFMiddleware` → 403 пустое тело+CSP-nonce без заголовка `X-CSRF-Token`; токен — `GET {BP}csrf-token`; login = `POST {BP}login` JSON); (2) **прямой INSERT в `inbounds` НЕ рендерит инбаунд в 3.5.0** — клиентская модель разнесена по `clients`/`client_inbounds`/`client_traffics`, заполняется только сервисом `AddInbound` → инбаунд надо создавать через panel API `POST {BP}panel/api/inbounds/add` (body=model.Inbound camelCase, settings/streamSettings/sniffing stringified); (3) **wrapper `x-ui setting -username/-password` НЕ применяет** — только бинарник `/usr/local/x-ui/x-ui setting` (после `systemctl stop x-ui`). Server-side e2e подтверждён (xray слушает 32030, exit=сервер); реальный РФ-клиент ждёт проверки user. +index.md. - -## [2026-07-12] deploy | tandemmebel.ru in-place bump 0.42.0→0.42.1 LIVE (стек 20) — поверх cutover'а того же дня (стек уже боевой, образ `ed96b18`/0.42.0). Консюмер-бамп сделан оператором сам (тот же блокер-паттерн 0.42.0 2026-07-04 — теперь решён без запроса dev-source): пин `apps/web/package.json:12` 0.42.0→0.42.1 + `yarn install` (lock snolla 0.42.1/core 0.24.1/liquid 0.10.2/data 0.14.1) + commit `0cd9351` + push origin (ls-remote confirmed). Verdaccio: 0.42.1 published (latest). Build на VDS → `registry.kzntsv.site/tandemmebel:0cd9351` (digest f29c187f). Throwaway-staging :5020 из env живого стека, healthy. Completeness-gate С VDS: **184/184 parity** (NEW==PROD, вкл. 0.42.x sitemap-реструктуризацию), единственный 404 `/articles` идентичен прод-оракулу → benign. Operator-gated PUT стека 20 (node put-stack.js, env 8/8 preserve, prune:false pullImage:true) → контейнер 0cd9351+healthy ~8s. Live-smoke GREEN (robots/home/projects/sitemap 200, TLS-серт CN не дёрнут). 0.42.1 = order-tag Drop-field fix, инертен на блог-портфолио (без e-commerce каталога). NEW concepts/tandemmebel-vds-deploy-runbook.md + UPDATE concepts/snolla-live-prod-inplace-image-bump.md (тираж 0.42.1 = 4 in-place bump-сайта) + host-stacks/vds-kzntsv/tandemmebel.compose.yml (тег+комментарий 0.42.1) + index.md. Rollback = тег ed96b18 в registry (+ b02ca18). **Тираж snolla 0.42.1 закрыт полностью.** - -## [2026-07-12] deploy | tandemmebel.ru cutover LIVE — последний из тиража snolla 0.42.x. DNS reg.ru `tandemmebel.ru`+`www`→89.253.255.94 (verify ns1/ns2.reg.ru оба), traefik Host-rule 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: key pages 200, sitemap 184 page-locs, sharp media webp 200, robots из БД. Gotham-Pro.css 0B→4436B (латентный прод-баг починен). Rollback = revert DNS→80.64.31.36 ИЛИ PUT staging-host. UPDATE entities/ruvds-iis-host (tandemmebel→🗑️ cutover VDS, биндинги оставлены как rollback) + host-stacks/vds-kzntsv/tandemmebel.compose.yml (rule боевой, заголовок live). **Тираж snolla 0.42.x полностью живой (5/5 стеков на VDS).** - -## [2026-07-03] ingest | NEW concepts/snolla-admin-appdata-acl-500-after-scp-migration — разведка «почему `/admin/assets/<owner>/delete` → 500» на RUVDS IIS. Корень: app pool `IIS AppPool\snolla` (ApplicationPoolIdentity) имел на `C:\sites\snolla\App_Data` только `(RX)` после scp-миграции 23.05 → `File.Delete` в `AssetsService.DeleteAsset` кидает UnauthorizedAccessException → необработанное 500 (event log пуст, IIS substatus 0/win32 0). NRE-версии исключены проверкой строк в БД (Folders root + Files). Fix: `icacls App_Data /grant "IIS AppPool\snolla:(OI)(CI)(M)" /T` (44293 файла, verified `(I)(M)` на таргете). Класс шире delete — все Local-записи из админки (upload/кэши/галереи). Также: заменён `labtools-price.pdf` (owner d375c419) в ОБА хранилища (MinIO `assets/…` ETag→6426ddd0 + локалка IIS, MD5 сверены). Split-brain админка(Local)↔боевой app(MinIO) — cross-ref [[concepts/galleries-storage-class-local-not-s3]]. - -## [2026-06-29] deploy | pilorama98.ru (контейнер `pilonuxt`) `cf2bba2` LIVE (стек 16) — ADR-0010: формы переехали с monolith `@snollajs/snolla` на `@snollajs/forms-api`, snolla **дропнут целиком**, core@0.1.1 (Buffer вместо btoa). Тот же build-пайплайн (форс-регенерация клиента + save|ssh load + push с VDS). **Новый обязательный шаг — tree-check `.output/server/node_modules` ПЕРЕД перекатом** (вся сага content-api↔snolla про dual-instance, dev маскирует unmet-deps): `docker run --rm --entrypoint sh <img> -c 'find /app/.output/server/node_modules ...'` → подтвердил 0× snolla, 0× btoa, ровно 1× data@0.9.1, core 0.1.1 + content-api 0.14 + forms-api 0.1.0. Smoke: SSR `/`+`/catalog`→200 (= btoa/snolla-дроп бандл не сломал), robots/yandex из БД (ADR-0009 не регресс), форма `POST /snolla-forms/forms?path=/checkout` пустой→422 JSON (valid НЕ слал — реальное письмо менеджеру), `/nope`→404. **Гоча smoke-харнеса:** forms-api отдаёт 422 только при `Accept: application/json` (иначе `prefersJson(req)` false → ветка `res.status(result.status||404).end()` даёт 404 — это не баг приложения, а отсутствие Accept в curl). Источник: наряд [deploy-pilonuxt-forms-api-drop-snolla]. См. [[concepts/portainer-stack-management-vds]] § Stack redeploy. - -## [2026-06-29] deploy | pilorama98.ru (контейнер `pilonuxt`) `40bb383` LIVE (стек 16) — DB-backed staticPages + robots.txt (ADR-0009). Build из монорепы victor/pilorama98.ru: ключевой шаг — **форс-регенерация gitignored-клиента** `apps/web/src/generated/` (`rm -rf` + `yarn workspace nuxt-app schema-gen` против live MSSQL), иначе ensure-schema skip-if-present собрал бы старый клиент без `getStaticPage`/`getRobotsTxt` → server-500. Push с дома повторил traefik-499 на `.output`-слое даже при образе 425МБ (не multi-GB) → канонический fallback `docker save | ssh vds 'docker load'` + push с VDS (registry локален) сработал. Перекат через Portainer PUT стек 16 (`pullImage:true`, байты+UTF8 gotcha #9). Smoke acceptance 6/6 телами: robots полный из БД (Yandex Clean-param + Sitemap, не статика), yandex/google верификации 200, /nope→Nuxt-404, /+/catalog→200. Источник: наряд [deploy-pilonuxt-static-pages-robots]. См. [[concepts/portainer-stack-management-vds]] § Stack redeploy. - -## [2026-06-24] ingest | entities/vdsina-outline-3xui.md (new) + sources/vdsina-outline-3xui-inventory-2026-06-24.md (new) — задокументирован ранее неизвестный VDS: VDSina Амстердам `46.151.25.64` (`v1621967.hosted-by-vdsina.ru`), «основной боевой VPN» семьи. Два стека на одной машине: Outline/Shadowbox (docker, :443 chacha20, 11 access-keys, mgmt :8080) + 3x-UI/Xray 25.10.15 (панель :50806 `/ecCqrtFVl4zl5kFjdj/`, VLESS xhttp :43666 / grpc :46743, все security=none). Ubuntu 20.04.6 (панель VDSina врёт «22»), 1 vCPU/1 GiB/30 GiB, up 17д, healthy. Секреты → `pass vdsina-outline/full-env` (host-key, root pass, Outline mgmt apiUrl+cert+11 keys, panel user+secret). Открытое: пароль панели — только bcrypt-хеш, plaintext не извлечён. Кросс-линк добавлен в [[nl-vds-3xui]] (другой NL-VDS, Aeza). +index.md (2 строки). Источник: SSH-инвентаризация 2026-06-24. - -## [2026-06-18] ingest | concepts/registry-oci-image-index-gc.md (раздел real-run) — первый боевой registryGc dryRun:false на dangling-фиксе (books master-289c660, под добро юзера): 63 DELETE, 0 ошибок, 0×405 → REGISTRY_STORAGE_DELETE_ENABLED=true подтверждён живьём; 61 dangling + 2 datable снесены, реестр почищен (web 20→4 и т.д.). Побочка-урок: master у books-api/books-ops-mcp был dangling (не пересобирались ~3нед) → снесён → :master 404 (outage нет, контейнеры на локальных образах, но redeploy упрётся). Политика: не удалять named-теги master/latest даже dangling (protectRe → +master/latest); проверить keep/drop digest-коллизию. Источник: real run + post-state probe books-* :master. - -## [2026-06-18] ingest | concepts/registry-oci-image-index-gc.md (раздел dangling-индексы) — перепрогон registryGc dryRun на descent-фиксе (master-dcd7c91) вскрыл 2-й root-cause: реестр засорён dangling image-индексами (тег жив, платформенный sub-manifest отдаёт MANIFEST_UNKNOWN — вычищен прежним host-side `registry garbage-collect`, не следящим index→child для multi-arch). books-web: 19 тегов → 3 датируемых, 16 dangling. date-GC защищает их как null-dated → не удаляет, хотя они и есть мусор (логика задом наперёд). Рекомендация books: различать transient-error (protect) vs MANIFEST_UNKNOWN (eligible). freedBytes от dangling ~0 (слои уже вычищены). +index.md hook. Источник: dryRun #2 + полный manifest-обход books-web 2026-06-18. - -## [2026-06-18] ingest | concepts/registry-oci-image-index-gc.md (new) + concepts/bindmount-config-edit-preserve-mode.md (расширен) — следствие закрытия [books-task-runner-registry-auth-cred]. (1) Новый концепт: books-* образы в registry = OCI image-index (buildx), top-level `.config`=null, дата `.created` в платформенном sub-manifest → наивный GC по top-level дате даёт null у всех → планировщик защищает null-dated группы → drop=0 всегда (keepLastN не применяется). Правила: Accept со всеми media-types, дата из sub, DELETE по index-digest (не sub), 1 версия=1 index+attestation. Зафиксировано на books registryGc dryRun (deleted=0 при 14 tags). (2) bindmount-постмортем дополнен: bind-mount затеняет config-каталог образа целиком → нужна полная кред-секция, не дельта; + worked example task-runner registry-секция (mode не слетел, постмортем сработал). +index.md (2 строки). Источник: dryRun-верификация registryGc + манифест-dump books-web 2026-06-18. - -## [2026-06-18] ingest | concepts/registry-kzntsv-auth-model.md (new) + concepts/bindmount-config-edit-preserve-mode.md (new) — registry.kzntsv.site = standalone registry:2 + htpasswd Basic (бинарный доступ, НЕ Gitea-packages; нет per-repo ACL/robot-токенов; hot-reload htpasswd; GC через v2 DELETE + host garbage-collect; завёл юзера books-ci для books job-scheduler pull, кред в pass vds-kzntsv/registry-books-ci). Постмортем: правка bind-mounted `default.json` через mktemp+mv уронила режим 644→600 → `books-job-scheduler` EACCES crash-loop (прод-даун slovo-cron), fix chmod/chown --reference=backup. +entities/vds-kzntsv.md (registry-строка) +index.md. Источник: консультация+инфра-деплой books registry-auth + инцидент 2026-06-18. - -## [2026-06-17] ingest | concepts/stostayer-web-deploy-runbook.md (new) — ранбук деплоя легаси web на прод stostayer: канал build(offline)→push docker.stostayer.ru→Portainer-стек 16 через container-IP API (172.18.0.2:9000) с хоста мимо Angie BA; rollback; БЛОКЕР ESM-стена (top-level await @stostayer/data на build + ERR_REQUIRE_ESM @snollajs/snolla в рантайме d02f740) → 0.3.18 остаётся; гоча VPN-IP бан хостером при retry-push-шторме. Источник: попытка деплоя `stostayer-web-complaint-form-deploy` 2026-06-17 (откат на 0.3.18). +index.md. - -## [2026-06-17] update | entities/windows-recovery-host.md — уточнён live-state двух stostayer-IIS-сайтов: добавлена таблица порт→connection string→БД (`stostayer` :8090→`www.stostayer.ru,1433` внешний прод + S3 minio.stostayer.ru; `stostayer.old` :8091→`mssql.kzntsv.site,1433` наш VDS), путь админок `/admin`, live-проверка 2026-06-17 (W3SVC Running, :8090/:8091 LISTENING, :80 нет). Пофикшена стейловая строка в «Ключевые папки» (stostayer.old conn localhost→mssql.kzntsv.site). Источник: live-аудит IIS на DESKTOP-NSEF0UK. - -## [2026-06-17] update | concepts/portainer-stack-management-vds.md +раздел «Stack redeploy (новый image tag)» (JWT→GET file байтами→подмена тега→PUT pullImage:true, env=[] плейсхолдером, тело UTF-8) + gotcha #9 (PS 5.1 Invoke-RestMethod декодит /file как ISO-8859-1 → кириллические compose-комментарии mojibake → PUT падает `yaml: could not find expected ':'`; fix = байты+UTF8) + #10 (пустой env схлопывается). Источник: redeploy-pilonuxt-gsc-structured-data (19a4a84 в проде, stack 16). - -## [2026-06-14] update | concepts/minio-imgproxy-on-vds.md +раздел «S3 access для клиентских приложений» (lookup: endpoint `https://minio.kzntsv.site` / сырой `89.253.255.133:9000` / inter `http://minio:9000`, root accessKey `AKIAJ...` + secret в `pass minio-vds/full-env`, форма ключа, ssl/region/pathStyle). Закрывает класс-вопрос «дай MinIO endpoint+креды» БЕЗ SSH. Прецедент: inbox snolla 2026-06-14 (raw-stream content-api), ответ отправлен. -## [2026-06-11] ingest | verdaccio-restore-packument-desync — postmortem: disaster-restore вернул тарболлы но древний/пустой packument → publish свежей версии EEXISTS 409. Fix: снять только коллизирующий целевой .tgz (бэкап first), republish; НЕ rm -rf каталог (убьёт locally-published историю). Применено к @snollajs/{data,mailer,numbering,content-api}. -## [2026-06-11] ingest | verdaccio-token-lifecycle — restart trap (ephemeral secret → 401), max_users:-1 + pnpm 409 постмортем, JWT config fix, yarn classic vs berry token refresh recipe. -## [2026-06-11] ingest | yarn-npm-minimal-age-gate — Yarn ≥4.16 client-side supply-chain gate: версии <24ч → YN0016 «quarantined». Независим от auth (gate #2 после 401). default 1d, читается глобально (per-scope не работает), fix npmMinimalAgeGate:0 / YARN_NPM_MINIMAL_AGE_GATE=0. Диагностировано с pilorama98.ru (контейнер `pilonuxt`); опровергнута первичная гипотеза «серверный карантин verdaccio». -## [2026-06-11] update | MSSQL backup gap closed — `/opt/stacks/backup/scripts/run.sh` расширен: 5 prod DBs через `docker exec mssql sqlcmd BACKUP DATABASE ... WITH COPY_ONLY, INIT` (Express, без COMPRESSION); .bak в bind-mount → mv в DUMP_DIR → rsync kreknin. Обновлён [[concepts/mssql-on-vds]] (TODO→реализовано). - -## [2026-06-11] lint | 10 issues found, 8 fixed inline -- 🔴 recovery-architecture-snapshot.md: добавлена пометка ИСТОРИЧЕСКАЯ ЗАПИСЬ (рерайт текущей архитектуры отложен — большая задача) -- 🔴 snolla-recovery-vm.md: помечена УДАЛЕНА 2026-06-08, updated date исправлен -- 🟡 ruvds-iis-host.md: зачёркнуты 2 закрытых риска (imgproxy SPOF снят, LE renewal закрыт); добавлена ссылка на winacme; updated 2026-06-11 -- 🟡 future-resilient-architecture-goals.md: dead task link cms-stopgap-backup-daily заменён на текстовое примечание -- 🟡 mssql-on-vds.md: исправлен frontmatter (status→type, добавлен updated) -- 🟡 vds-kzntsv.md: добавлен MSSQL в software stack table -- 🟡 orphan winacme-iis-owin-catchall-http01: входящая ссылка добавлена из ruvds-iis-host -- ✅ ДОФИКСИРОВАНО: 3 broken links в recovery-architecture-snapshot убиты; TODO mssql backup integration закрыт (реализован) -- ⚪ НЕ ФИКСИРОВАНО: orphan mssql-container-data-restore — оставлен - -## [2026-06-08] update | entities/windows-recovery-host — декоммишн после Synology-recovery: снесены traefik/mssql/minio/imgproxy/es + 21 husk-стек + VM snolla-recovery (95ГБ) + C:\nas-recovery (~150ГБ) + IIS-сайт snolla + C:\sites\snolla + локальный MSSQL. Осталось: IIS stostayer/stostayer.old (репойнт на mssql.kzntsv.site, orphan-fix), lightrag, markitdown-MCP, mutable-dev VM. C: free 62→275ГБ (+~176ГБ docker pending vhdx-compact). image-pipeline SPOF снят. Источник: `.tasks/decommission-windows-recovery-host.md`. - -## [2026-06-08] ingest | concepts/morethencms-null-settingsdata-https-502 — RCA+fix: `maljarka.tandemmebel.ru` отдавал 502 только на HTTPS (HTTP=200). Корень = `dbo.Sites.SettingsData=NULL` у тенанта maljarka (нет блока `httpSecure`) → `KeyNotFoundException` в `SnollaMiddleware`/`Owin.ErrorHandler` на HTTPS-ветке. Fix = `UPDATE dbo.Sites SET SettingsData='{httpSecure:...}'` + `Restart-WebAppPool snolla`; verified 443→200 server-local + external. Audit: maljarka — единственный NULL-сайт с :443-биндингом; rimiz degraded по другой причине. Обновлён [[../entities/ruvds-iis-host]]. Источник: live-диагностика 2026-06-08. - -## [2026-06-05] ingest | concepts/winacme-iis-owin-catchall-http01 — постоянный self-renewing LE pipeline на RUVDS IIS (win-acme v2.2.9, 25-SAN cert, SYSTEM scheduled task). Главное: OWIN-catch-all CMS жрёт `/.well-known/acme-challenge/` → решено отдельным IIS-приложением в пуле «No Managed Code» + патч шаблона `Web_Config.xml` (snять Owin-handler). Заменяет ручной [[traefik-acme-json-to-iis-cert-import]] для renewal'а; снимает зависимость RUVDS от домашнего traefik. Закрывает дедлайн cert-expiry 2026-07-22 (новый cert до 2026-09-03). Источник: `.tasks/iis-migration-to-ruvds.md` Decisions log 2026-06-05. - -## [2026-05-28] ingest | concepts/es-destructive-delete-incident-2026-05-26 — RCA для удаления 3 user-индексов на canonical ES `elasticsearch.kzntsv.site` (books VDS stack 33) во время cutover-prep. Caller identity unrecoverable (audit log = X-Pack платный, traefik accessLog был выключен). 2 preventive controls applied + verified: ES env `action.destructive_requires_name=true` + traefik JSON accessLog. Источник: `.tasks/restore-elasticsearch-indices-books-vds.md` § Closure note. - -## 2026-05-21 - -- bootstrap: empty wiki skeleton (CLAUDE.md, index.md, log.md, overview.md, raw/README.md) committed - -## [2026-05-21] ingest | concepts/admin-infra-project - -## [2026-05-21] seed | CLAUDE.md Domain conventions — design-context pointers (task: admin-infra-project-pointers) - -## [2026-05-21] migrate | subtree-import 6 entities + 17 concepts + 3 sources from MoreThenCms (history preserved via subtree-split + temp-prefix merge) - -## [2026-05-21] regen | index.md catalog refresh after subtree import - -## [2026-05-21] ingest | concepts/ocis-on-vds-deploy-recipe — oCIS deploy recipe + gotchas (UID mismatch, PROXY_TLS, PROXY_ENABLE_BASIC_AUTH, LibreGraph user-create); updated entities/vds-kzntsv (added owncloud row to stack/hostnames/file-layout); source: .tasks/owncloud-vds-deploy.md - -## [2026-05-22] update | concepts/ocis-on-vds-deploy-recipe — Gotcha 5: 60s HTTP timeout caps slow-uplink uploads at ~200MB on 3 MiB/s link; traefik buffering NOT the fix (500 from oxy buffer); workaround = VDS-side curl PUT loopback via throwaway sftp key. Source: .tasks/owncloud-vds-deploy.md close-note. - -## [2026-05-23] ingest | concepts/windows-server-2025-core-bootstrap — default-blockers (SMB closed, IIS/URL-Rewrite/.NET unverified) + transfer-методов матрица (RDP-redirect / SMB / WinRM / SFTP); recommendation = SMB inbound с source-IP whitelist на время migration. Source: failed-robocopy инцидент 2026-05-23 18:08 (.tasks/iis-migration-to-ruvds.md Decisions log). - -## [2026-05-24] ingest | concepts/books-ssh-access — SSH access audit на shared VDS pre-cutover Фазы 3 tenant-split. Retained keys table (1 key, vitya core dev), removed cosmetic dead root key, sshd hardening verified via `sshd -T` (not raw config), fail2ban active 2670 failed/37 banned, only vitya@94.19.247.14 в access log last 7 days. Source: .tasks/books-ssh-audit-shared-vds.md. - -## [2026-05-24] ingest | RUVDS IIS migration + backup pipeline — entities/ruvds-iis-host (NEW, 80.64.31.36 Win Server 2025 Core, 25 SNI bindings, 2/24 hostnames DNS-flipped) + sources/iis-migration-to-ruvds-2026-05-23 (NEW, SSH/scp pivot после SMB-block by home-ISP) + sources/ruvds-backup-daily-kreknin-2026-05-24 (NEW, rclone+SFTP SYSTEM task daily 04:30) + concepts/traefik-acme-json-to-iis-cert-import (NEW, PFX+SNI recipe) + UPDATE concepts/windows-server-2025-core-bootstrap (SMB deprecate, HTTP middlebox warning, HTTP/2 note, backup-strategy + cert-import закрыты) + UPDATE entities/windows-recovery-host (IIS partial-cutover state, imgproxy SPOF carve-out) + UPDATE overview (RUVDS line). Sources: .tasks/iis-migration-to-ruvds.md, .tasks/ruvds-backup-daily-kreknin.md. - -## [2026-05-28] ingest | vds-kzntsv DHCP outage postmortem — concepts/vds-kzntsv-dhcp-outage-2026-05-28 (NEW, симптомокартина + диагностический алгоритм + recovery runbook + RCA) + sources/vds-kzntsv-incident-2026-05-28 (NEW, timeline 05:25 backup OK → 08:15 detect → 08:43 statics fix → 08:50 disk GC; ticket text) + UPDATE entities/vds-kzntsv (Доступ §+subnet/gw/hypervisor/DNS resolvers; pass-store вместо .common/secrets; Known issues §NEW with 2026-05-28 incident; Open issues bump GC priority + iputils-ping note) + UPDATE concepts/rusonyx-vps-onboarding-quirks (quirk #9 NEW — gw в /18 надсети + recovery commands). Live session, no raw source ingested. - -## [2026-05-28] update | vds-kzntsv DHCP outage post-resolution — после reset хостером в 13:52 MSK выяснилось что root cause — конфликт двух сетевых стеков (netplan+networkd поверх ожидаемого provider's ifupdown). Их `start/ipadd` ожидает чистый ifupdown, не мог auto-recover при разрыве DHCP binding. Resolution: mask netplan+systemd-networkd, reboot, provider положил `/etc/network/interfaces.d/ifcfg-eth0` с /18 netmask. UPDATE concepts/vds-kzntsv-dhcp-outage-2026-05-28 (revised RCA + permanent-fix § + anti-pattern + revised lessons-learned) + UPDATE sources/vds-kzntsv-incident-2026-05-28 (timeline до 14:00 + final config) + UPDATE entities/vds-kzntsv (mask /18, ifupdown stack, kernel cmdline net.ifnames=0) + UPDATE concepts/rusonyx-vps-onboarding-quirks (quirk #9 переписан про /18, quirk #10 NEW про ifupdown vs netplan stack). Memory `vds-kzntsv-rusonyx-network-recovery` переписан с новыми фактами. - -## [2026-05-29] update | ES destructive-delete RECURRENCE — true root cause найден, диагноз сменился. UPDATE concepts/es-destructive-delete-incident-2026-05-26 (correction-блок + Hypothesis помечена ОПРОВЕРГНУТА + новая секция «Рецидив 2026-05-29 — true root cause»: ransom-бот через открытый `0.0.0.0:9200` мимо traefik+basicAuth, ES 7.10 free без auth; `read_me`=BTC-выкуп; снос by-name мимо Control #1; accessLog пуст т.к. бот шёл прямо в :9200; firewalld bypass docker-publish; Control #3 = убрать публикацию host-порта via Portainer PUT stack 33; restore из daily-2026-05-25; exposure-audit таблица — mongo/books-db/bookva-db/minio/bookva-minio тоже exposed но credentialed). bookva не пострадал (bookva-es порт не публикует). Live incident response session. - -## [2026-06-05] ingest | NL VDS 3x-UI node + Reality PQ×dest root-cause -- new entities/nl-vds-3xui.md (213.176.64.253, Ubuntu 24.04, 3x-UI 3.2.7 / Xray 26.6.1); creds -> pass nl-vds-3xui/full-env -- new concepts/reality-pq-mldsa65-dest-incompatibility.md (root cause: ML-DSA-65 PQ ClientHello X25519MLKEM768 + Akamai dest www.intel.com -> HRR -> handshake fail; fix = PQ-capable dest e.g. www.microsoft.com, verified via isolation matrix) -- index.md updated (entity + concept). Fix NOT applied — awaiting user command. - -## [2026-06-05] ingest | NL VDS 3x-UI session — honest final state + lessons -- NEW sources/nl-vds-3xui-setup-2026-06-05.md (chronicle: Reality PQ fix, port 443→2053, mtg MTProto, SOCKS5, friend-client instr; HONEST outcome — у реальных клиентов из РФ работает только plain VLESS 32030) -- NEW concepts/proxy-debugging-test-the-real-client.md (anti-pattern: свои curl/standalone-тесты ≠ боевой клиент; overclaim «РКН режет 443» / MSS-clamp по MTU-догадке сломал коннект) -- REWROTE entities/nl-vds-3xui.md — убраны ложные «Reality verified/fixed», «mtg неотличим»; честная таблица статусов (32030 🟢, Reality 2053 / MTProto 8443 / SOCKS 47020 🔴 у клиентов); MSS-clamp снят -- UPDATE concepts/reality-pq-mldsa65-dest-incompatibility.md — caveat «снятие PQ ≠ рабочий Reality у GUI-клиентов» + source link -- index.md updated (2 new pages). Креды/ссылки — pass nl-vds-3xui/full-env. - -## [2026-06-06] decision | nl-vds-3xui: +3 classic Shadowsocks inbounds (ports 32031/32/33) for Outline-app, per-key revocable; protocol e2e-tested OK, RF reachability unverified (SS DPI-blocked in RF); creds in pass; discovered undocumented VLESS:13027. - -## [2026-06-10] ingest | concepts/mcp-init-resilience - -## [2026-06-12] migrate | galleries pilorama98 → S3. NEW concepts/galleries-storage-class-local-not-s3.md (root: storageClient `galleries` = Local disk class, never in S3; snolla ждёт s3://galleries/<siteId>/<file> → 404). Путь A: bucket `galleries` создан, 301 файл (77 MiB) с RUVDS IIS залиты verbatim, imgproxy 200 verified с books-vds. UPDATE concepts/minio-imgproxy-on-vds.md (stale-баннер: pipeline на books-vds с 08.06, не windows-host). Task migrate-gallery-originals-to-s3 🟢. - -## [2026-06-27] refactor | books-vds: документирован публичный bookva-db endpoint :33306 (+ slovo/bookva DB-exposure, mongo/ES internal) в § Доступ; закрыт wiki-drift по порту - -## [2026-07-05] decision | тираж snolla 0.42.1 (live-prod in-place bumps). NEW concepts/snolla-live-prod-inplace-image-bump.md (переиспользуемый рецепт build→staging-acceptance-С-VDS→env-preserving Portainer PUT `put-stack.js`→live-smoke). UPDATE 3 рунбука секцией «0.42.1 in-place bump»: labtools-vds-deploy-runbook (стек 17, `labtools:566d41c`), emspb-vds-deploy-runbook (18, `emspb:95a5c42`), labtools.pro-vds-deploy-runbook (19, `labtools-pro:0610432`). FIX orphan: все 3 рунбука добавлены в index.md (не были каталогизированы). Tasks `[labtools-ru|emspb|labtools-pro-deploy-snolla-0-42-1]` 🟢. - -## [2026-07-24] ingest | concepts/stostayer-markdown-render-404-incident-2026-07.md — постмортем инцидента 404/500 на body_repair_service (stostayer.ru). Двойной корень: (1) код-баг snolla `import markdown from 'markdown-it'; markdown();` → инстанс выброшен, `markdown.render` undefined, краш на markdown-поле-шаблонах (латентный ~3 мес, с коммита 1e3f897 2026-04-21); (2) exact-pin-staleness — `@snollajs/content-api@0.8.0` `workspace:*` запёк `@snollajs/snolla: 0.7.4` exact → фиксовая 0.7.7 легла top-level, content-api юзал nested баговую 0.7.4. Фикс: snolla 0.7.7 + core 0.26.1 (код) + content-api 0.8.2 `^0.7.7` (упаковка). Деплой stostayer-web:0.3.22 через pscp SSH:20435 + sudo docker load + Portainer PUT pullImage:false (без push в :443, бана нет). Pre-build чек yarn.lock single-snolla ловит класс. index.md updated (+1). Cleanup `workspace-caret-interpackage-deps` — vitya scope. Memory: stostayer-deploy-image-via-pscp-load-avoid-443-push.md. -## [2026-08-13] refactor | concepts/stostayer-web-deploy-runbook — letter path `.claude-inbox` → `.agents/inbox` (rename idea 13 cascade) -## [2026-08-18] ingest | NEW concepts/sched-fnf-r8-verification-stand.md — стенд верификации F&F r8 (schedule-as-entity): путь, версии core 0.40.1/storage-* 0.3.1, карта 15 репро-скриптов (миграции/движок/политики), подтверждённый контракт, как продолжить №4/№5. NEW concepts/windows-docker-test-harness-gotchas.md — переиспользуемые грабли Windows-верификации (rmSync-EBUSY, vitest-DDL-таймаут, монотонность фейк-часов, изоляция DB, висельники node). index.md updated (+2). - -## [2026-08-18] ingest | UPDATE concepts/sched-fnf-r8-verification-stand.md — сессия №4/№5: admin-api-test.mjs (52/52, F1 tz-hoist + F3 PATCH-merge подтверждены на core 0.40.2), UI-стенд ui-backend.mjs + sched-ui serve :8081 + браузерный CDP (13/13), находки U1 (форма-tz interval/once → 400) и U2 (таблица шире контейнера ~58px). index.md hook обновлён. -## [2026-08-18] ingest | NEW concepts/nvm-junction-ismain-gate-gotcha.md — ESM isMain-гейт молча no-op под junction-префиксом npm-рута (C:\nvm4w\nodejs → realpath …\nvm\v22.22.0): import.meta.url резолвится в realpath, argv[1] держит литеральный → main() не выполняется, exit 0. Бьёт global-install под любым version-manager'ом (nvm/mise/volta). Фикс realpath-сравнения (sched bd38aab, daemon 0.3.2). NEW concepts/nvm-windows-node-switch.md — Node 22→24.19.0 рецепт: junction-свап, per-version глобалы, PATH-кэш shell'а, npm 11 allow-scripts. Оба из сессии CLI-F&F sched 2026-08-18. index.md updated (+2). -## [2026-08-18] ingest | NEW concepts/openapi-generator-3-1-spec-gotchas.md — из F&F openapi-client sched (2026-08-18): генерация Python-клиента из 3.1-спеки openapi-generator 7.24.0. Гочи: (1) ответы `{}` в спеке + wrapped-тело ({task/run/schedule:…}) → pydantic ValidationError, фикс — реальные схемы ответов, обход raw-транспорт + by_alias=True; (2) `const:true` → строковый enum ('true') ломает boolean; (3) oneOf+discriminator → actual_instance=None, контент только через raw JSON; Java 11+ обязателен (системная 8 падает), Windows excluded-порты 8090/8091 (EACCES), консоль cp1251 без →/«». Полные находки F-1..F-5: sched/.agents/inbox репорт 19:15Z; пример sched/examples/admin-client-python (24/24). index.md updated. -## [2026-08-18] ingest | NEW concepts/sched-admin-client-openapi.md — F&F раунд openapi-claim sched (по предписанию таски): Python-клиент одной командой (openapi-generator 7.24.0 из спеки verdaccio 0.1.2), сьют 24/24 + smoke 19/19 против published daemon 0.3.4 (:8127), вердикт «claim подтверждён с оговорками» (8 операций требуют raw-обхода). Код sched/examples/admin-client-python (e89e868), репорт — sched инбокс 19:15Z, находки F-1..F-5 (спеку не чинил). Таска [sched-admin-client-openapi] 🟢. Гочи генерации — concepts/openapi-generator-3-1-spec-gotchas.md. index.md updated. -## [2026-08-18] ingest | UPDATE concepts/openapi-generator-3-1-spec-gotchas.md + concepts/sched-admin-client-openapi.md — round 2 ретест (0.2.0/daemon 0.3.5): все 5 находок F-1..F-5 закрыты, raw_ops.py удалён, 11/11 ранее-сырых операций typed-only, сьют 24/24 + smoke 19/19 (коммит sched d658ff0, репорт sched инбокс 22:55Z). Новые грабли в окружение: CLI спавнит java из PATH а не JAVA_HOME; wrapped-ответы в components.responses → InlineObjectN; info.version не бьётся с версией пакета. index.md hooks обновлены. - -## [2026-08-22] ingest | NEW concepts/omniroute-combo-deepseek-web-tuning.md + sources/omniroute-session-2026-08-22.md — сессия настройки web-комбо omniroute (deepseek-v4-flash/pro-web). Корни вылетов агента: (1) system_message override режет скилы pi, (2) tool_filter_regex ^(bash|computer)$ режет 337/339 тулов, (3) search-модель генерит DEEP_SEARCH-мусор + «Link reading unavailable», (4) quality validation режет стрим на type:error, (5) 503 chat_admission_busy = capacity веб-чата. Фиксы применены: system_message="", regex расширен (базовые+mcp__*), flash think-search→think, maxRetries 2→4, retryDelayMs→4000. Эмуляция tool-calling проверена тестами — исправна. План кастомного провайдера (вытащить executor+translator, без quality validation, persistSession). index.md updated (+2). -## [2026-08-22] ingest | UPDATE concepts/omniroute-combo-deepseek-web-tuning.md — qwen-web стена baxia-WAF (подзадача llm-web-proxy-qwen-web): FAIL_SYS_USER_VALIDATE/RGV587 punish-капча на любой не-браузерный запрос (Node-fetch все варианты заголовков + свежий cookie-jar; даже Chrome-CDP chats/new проходит, completion виснет). Капча привязана к device-фингерпринту (bx-umidtoken из sg-wum.alibaba.com), не к cookie. Зацепка: в контейнере omniroute рабочая версия executors/qwen-web.ts ≠ GitHub main. Подзадача 🟡 paused. index.md hook обновлён. -## [2026-08-22] ingest | NEW concepts/forgetmeai-free-web-chat-proxies.md + raw/forgetmeai-repo-analysis-2026-08-22.md — анализ аккаунта ForgetMeAI (8 репо, все — бесплатные web-chat прокси, та же ниша lwp). Главное: (1) паттерн «не решать капчу, а обходить реальной браузерной сессией» (Chrome-расширение-экспортёр кук/localStorage, cookie export, persistent-профиль); (2) PoW-солвер DeepSeek (lib/pow.js, WASM compile-cache) — в lwp уже портирован; (3) qwen через baxia-WAF работает ТОЛЬКО браузерным путём: fetch внутри page.evaluate (FreeQwenApi) — настоящий umidtoken+canvas-фингерпринт; наш вывод подтверждён; (4) CloakBrowser-заметки (патченый Chromium, humanize, persistent-профили, geoip), классификаторы капчи (Aliyun WAF/FRONTEND_CAPTCHA_REQUIRED/punish/purecaptcha). Референс-код закоммичен в lwp docs/reference/forgetmeai/. index.md updated (+1). - -## [2026-08-23] ingest | NEW concepts/sched-runtime-to-file-managed-migration.md — sched slovo+bookva переведены на file-managed (durable config, #991 closed): инцидент 2026-08-23 (спин, runtime-расписания в памяти демона) → миграционный рецепт (файл in-place из git, live-sync ≤60с НЕ конвертирует runtime-таски, DELETE /api/tasks+/schedules, verify fileManaged:true/дубли 0/ручной run), gotchas (id=taskName vs UUID, ghost-таски, task-runner config кэш→рестарт, SMTP from==auth Yandex, URL-encode имён). SMTP-уроки линкуются на snolla-smtp-mail-delivery-2026-08 (не дублируются). index.md updated (+1). -## [2026-08-23] ingest | NEW 3 concepts (books deploy session): books-vds-no-gitea-actions (решение: Actions выпилены, сборка локально, процедура + gotcha больших пушей), registry-traefik-buffering-gotcha (Client Closed Request на >1.5GB пушах, обход curl-монолитами), config-custom-env-mapping-in-mounted-dirs (mapping-файл в mounted-дире обязателен). index.md updated (+3). - -## [2026-08-24] ingest | concepts/deploy-policy-mappa - -## [2026-08-25] refactor | wiki bucket sources/ → summaries/ (решение оператора 2026-08-24; raw_path не меняется — ../raw/) +**Не читать. Не править.** Канон — mappa (`mcp__mappa__*`): wiki-сущности проекта, конвенции — AGENTS-сущность. Скил: `mappa-knowledge`. diff --git a/.wiki/overview.md b/.wiki/overview.md index 6adb013..21695bc 100644 --- a/.wiki/overview.md +++ b/.wiki/overview.md @@ -1,26 +1,3 @@ -# admin — infrastructure agenda +# ⛔ Файловый канал закрыт -Operational + roadmap workspace for personal-and-client infrastructure stack: - -- **VDS** (`vds.kzntsv.site`, Rusonyx) — gitea, verdaccio, registry, shared DB park, ntfy, traefik+portainer -- **RUVDS IIS host** (`80.64.31.36`, Win Server 2025 Core) — CMS multi-tenant IIS site `snolla` (catch-all для 24+ hostnames), partial-cutover idёт 2026-05-24 -- **NAS** (`kreknin`, Synology DSM) — backup target, Hyper Backup repo, secondary services -- **Windows recovery host** — раньше CMS prod IIS (мигрирует на RUVDS), всё ещё хостит imgproxy + `tandemmebel.ru` + parallel running docker/traefik как rollback -- **OpenWRT router** — NAT 80/443 → traefik (на windows-recovery-host) - -## Current state - -See `concepts/recovery-architecture-snapshot.md` (migrated 2026-05-21 from `MoreThenCms`). - -## Goal - -Build fault-tolerant infrastructure so the 2026-05-18 SMR-cascade incident does not repeat. Roadmap design in progress — see `concepts/future-resilient-architecture-goals.md` (migrated 2026-05-21) and active task `[resilience-roadmap-design]`. - -## Related repos (out of admin) - -- `OpeItcLoc03/vds-ops-mcp` — read-only docker ops MCP for VDS -- `OpeItcLoc03/synology-ops-mcp` — same for NAS -- `OpeItcLoc03/projects-meta-mcp` — cross-project state aggregator -- `OpeItcLoc03/agenda` — cross-project task meta-board -- `OpeItcLoc03/projects-wiki` — shared knowledge wiki -- `OpeItcLoc03/claude-skills` — global skill registry +**Не читать. Не править.** Канон — mappa (`mcp__mappa__*`): wiki-сущности проекта, конвенции — AGENTS-сущность. Скил: `mappa-knowledge`.