367 KiB
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-логинформа (<title>SNOLLA</title>, <form action="/admin/login">). 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, доступ <alias>.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/<gid>/images/<variant>/<seq> 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 <sitemapindex> 6 источников; /sitemap-pages-1.xml→200 <urlset> 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, ключи <ownerId>/<storageFilename> 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\<siteId>), НЕ S3 — оригиналы никогда туда не заливались (продукты — заливались, bucket pilorama98). Развилка A/B закрыта фактами MinIO → A: создан bucket galleries, залиты 301 файл (77 MiB) pilorama98 (siteId 37e67fc4…) с RUVDS IIS через rclone → s3://galleries/<siteId>/<guid>.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.compushed (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, ruleHost(on.snolla.com), LE cert. on.snolla.comDNS 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.)
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.
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 + .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.
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 § 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 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 § 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. 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.
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 § 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 bookvaat 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). books75ea320(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.siteenv на books-web для PWA redirect banner user.id=1).
Deferred (отдельной micro-task):
bookva-ozon-mcpstack — imageregistry.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 § 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) 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 § 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 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:
- Снизить 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 мин. - 24h soak kupimknigi + emspb — verify через cache-clean resolver (потом curl без
--resolve). - Bulk DNS swap оставшихся 22 hostnames на 80.64.31.36 (single sitting). tandemmebel.ru / www.tandemmebel.ru — пропустить.
- 1-week prod soak с RUVDS как live source для migrated hostnames.
- Decommission source IIS:8089 только для migrated hostnames (snolla catch-all site нельзя decommission'ить пока tandemmebel.ru на нём же). Решение defer до tandemmebel migration.
- LE renewal pipeline — win-acme + HTTP-01 на RUVDS после full cutover (LE certs expire 2026-07-22, soak window до ~07-15).
- 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. 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.<tld>готов резолвиться на новый 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).
- Согласовать с учредителем Bookva: provider, configuration (CPU/RAM/disk), регистрация billing'а на юрлицо Bookva.
- Provision VDS, базовая настройка (firewall, fail2ban, SSH ключи).
- Установить Docker + docker-compose.
- Развернуть traefik + certbot из overlay-репо
victor/books-bookva/deploy/traefik.compose.yml. - Подготовить DNS-зону
bookva.<tld>(создать A-records, TTL=300 для возможности быстрого switch'а). - Smoke:
curl https://placeholder.bookva.<tld>→ 200 от nginx-placeholder или из traefik. - Документировать в
victor/books-bookva/README.md: hostname, IP, как ssh, runbook smoke-теста. - Закрыть с 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.<tld> → новый VDS IP, с parallel-run старого bookva-стека на shared VDS 7 дней без публичного hostname.
Контекст dev-source: дизайн в victor/books .wiki/concepts/tenant-split.md § «Фаза 4».
Acceptance:
- DNS
*.bookva.<tld>(или конкретный hostname Bookva — определяется учредителем) указывает на новый VDS IP. - Старый bookva-стек на shared VDS остаётся запущенным, без публичного hostname (traefik labels убраны или disabled), доступен только по docker-сети для возможного отката.
- Smoke с двух разных сетей (mobile + офисный wifi) —
https://<bookva-hostname>отвечает с нового 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.
- Maintenance window 1–2ч (объявить за 7 дней).
- Финальная синхронизация:
mysqldump mariadb-bookvaна текущем VDS →mysqlна новом VDS (если schema/data разошлись с момента Фазы 2). - Smoke на новом VDS до DNS-switch'а: hosts-файл override → проверить login + base flow.
- DNS-switch: меняем A-record(s) для bookva-hostname на new IP. TTL=300 уже стоял (Phase 4 step 1).
- Через 1ч (TTL expiry + propagation): smoke с двух сетей.
- На старом VDS — отключить traefik labels для bookva-стека (контейнеры продолжают работать, но не доступны снаружи).
- Parallel-run 7 дней. Каждый день — smoke. Per-VDS backup на новом VDS — проверить хотя бы 1 успешный snapshot.
- Если за 7 дней проблем нет → закрыть с note «cutover stable». Если есть — DNS rollback на старый IP, follow-up debug task в victor/books.
- Decommission старого bookva-стека на shared VDS (отдельный шаг, не в этой таске — финальный backup в архив +
docker compose down+ volume drop). Blocker:books-vds-bookva-bootstrap+ victor/bookspwa-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 БД:
SELECT id, login, email, role FROM users WHERE email IN (<list>) AND deleted_at IS NULL;
- Разобрать расхождения с учредителем (missing / role mismatch / inactive).
- Зафиксировать финальный список в
OpeItcLoc03/admin/.wiki/concepts/books-bookva-whitelist.md(или в local-only encrypted file если NDA-чувствительно). - Финальный список становится input'ом для Фазы 3, шаг 1: при настройке Bookva-БД в неё попадают только эти users.
- Закрыть с 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-<svc> / slovo-<svc>.
TDD-режим: [skip-tdd: one-off migration on maintenance window] — verify через post-cleanup audit.
Acceptance:
bookva-db+slovo-dbконтейнеры живые, auditSELECT 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.<tld>→ 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.
- Подтвердить overlay-репо
victor/bookva-overlay+victor/slovo-overlayготовы — compose-файлы дляbookva-*/slovo-*стеков на месте. - Подтвердить per-tenant backups настроены (
victor/books per-tenant-backup-and-observability) — иначе нет точки rollback. - Объявить maintenance window 1-2ч (без записи).
- Execute Steps 0-11 из playbook на shared VDS.
- Post-execution audit:
SELECT DISTINCT idSeller FROM <each>,db.agendaJobs.distinct('data.idSeller'),mc lson MinIO. - Smoke с двух сетей.
- Через 7-14 дней parallel-run без откатов → cleanup старых
books-*контейнеров+volumes (отдельный заход). - 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 через envSTOSTAYER_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 наполняется нашим authedyarn 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.
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/<guid>.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\<siteId:N>\<storageFilename-guid>.<ext>). В S3/MinIO их нет. Продукты — мигрированы (s3://pilorama98/products/... отдаёт 200), галереи — нет.
Три независимых следствия одного корня:
-
Prod не отдаёт картинки блога. Legacy-роут
/galleries/<galleryId:N>/images/<size>/<seq>.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-ресайз.
-
Превью в админке видно. Админка рендерит ОРИГИНАЛ (passthrough по imageId →
GetGalleryImageById, без ресайза), читает локальный диск. Тот же путь, чтоoriginal/001.jpg=200. Сломанную ресайз-ветку не трогает. -
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, галереи должны быть под prefixgalleries/→ тогда + маленький код-фикс на стороне snolla (storageClient как prefix, а не bucket) — в этом случае ЗАВЕСТИ follow-up таску обратно на victor/snolla через notify, НЕ чинить snolla из admin.
Acceptance criteria
- Определено фактическое состояние MinIO: какие бакеты есть, как лежат рабочие продукты, отсутствуют ли галерейные объекты — зафиксировано в close-note.
- Развилка A/B закрыта явно (какой бакет/prefix — целевой).
- Если A: legacy-оригиналы залиты в S3 под ключи, которые snolla уже ждёт (
<siteId>/<storageFilename>); проверена доступность через imgproxy (хотя бы одна картинка → 200). - Если B: создана follow-up таска на victor/snolla с точной целевой формой ключа (bucket + prefix), миграция файлов под этот prefix выполнена.
- В 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
<fileStorageClients>storageClientgalleries= тип Local (GalleriesStorage, дискApp_Data\galleries\<siteId>), НЕ S3 → галереи никогда не были в S3 (продукты — да, bucketpilorama98/products). Корень доказан. A (отдельный bucketgalleries, key<siteId>/<storageFilename>) выбран 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…/<guid>.jpgverbatim. 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). Добавлен conceptgalleries-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/<ownerId>/<storageFilename> 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-картинки контента (<img src="/assets/<ownerId>/<size>/<file>">) через imgproxy с источником s3://assets/<ownerId>/<storageFilename>, но объектов в S3 нет — imgproxy 404 "Source image is unreachable". БД-записи есть, файлы только на legacy-IIS диске. Тип StorageClient="assets" — это MoreThenCms.FileStorage.Local.*, в S3 никогда не мигрировали.
Acceptance criteria
- Legacy assets-оригиналы pilorama98 залиты в S3 bucket
assetsключами<ownerId>/<storageFilename>(path A, как с галереями: storageClient = имя бакета). - Контрольный прозвон imgproxy подтверждает 200 image/webp на реальном объекте + 404 на несуществующем (негативный контроль).
- Verify: dest object count == source, размер == source.
- Зафиксирована точная форма ключа и охват (какие 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: OwnerId83150d77-3cf0-455e-a58d-dcccb2578713→ корневая папка/(FolderId03CE5D8D-95B9-4D81-B222-80AEC76225CD). Внимание: ownerId это владелец медиа-папки, НЕ siteId (siteId pilorama98 =37e67fc4…).Files:brevno.jpg→ StorageClientassets, StorageFilenamebrevno.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 owner83150d77…= 6 файлов,brevno.jpg=580667 B == DBFiles.Size. Ключ-форма<ownerId>/<storageFilename>подтверждена на диске. - MinIO (books-vds, minio.kzntsv.site): bucket
assetsотсутствовал → создан. Залив rclone v1.74.2 (env-var s3-remote, без записи cred),App_Data\assets→min:assetsverbatim. Verifyrclone 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 <registry>/pilonuxt:<tag> .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:
- Образ собран и в registry.
- Portainer-стек за Traefik, healthy.
- Smoke: главная 200 + SSR-разметка (не 500); товары/категории из CMS; картинки рендерятся; форма contacts шлёт письмо.
- Доклад в 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:"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:<pass vds-kzntsv/registry-books-ci>, 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
registryAuthyet (inert/backward-compatible). Activation happens on books' code deploy (rebuild = restart, bind-mounted config persists). If books had already deployed the new code, a singlebooks-job-schedulerrestart activates it. - Cred itself verified against registry earlier:
books-ciBasic → 200 onbooks-tool-create-picking-list-pdf:master, wrong-pass → 401. - Image +
mastertag 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) добавить секцию:
"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
- Все 3 Gitea-репо в archived (read-only); проверить
archived:trueчерез API/UI. - В README/описание каждого архивного репо — указатель «переехало в victor/pilorama98.ru → apps/<...>».
- Локальные папки
~/projects/{pilonuxt,pilorama98.ru-legacy,snolla-products-loader}удалены ПОСЛЕ подтверждения архива. Предохранитель перед rm:git -C <dir> statusчист Иgit -C <dir> log @{u}..пуст (нет unpushed). Контент уже в монорепе и запушен. - projects-meta: поллер не должен surface'ить stale-доски этих репо (archived → skip; свериться что
meta_status.archived_skippedвырос). Активные задачи уже перенесены в монореп. - Shared meta-wiki: если есть страницы про эти 3 проекта — обновить (переехали в монореп); если нет — короткая запись в concepts о консолидации pilorama-монорепо.
- Отчёт в 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
- 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). - Деплой образа на VDS (Portainer стек за Traefik) — обычный re-deploy.
- Smoke на проде (verify, не «собралось=работает»): товарная
…/shop/products/<slug>→<title>≤60 вида «{товар} — купить в СПб» + meta description уникальная с «доставка по СПб и ЛО»; категория…/catalog/planken→ короткий title «Планкен прямой и скошенный — купить в СПб»;…/shippingи блог-пост → непустая meta. - Зафиксировать в 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
🟢 [#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.rumaster @c4e34d4(уже запушен в git.kzntsv.site). - Фикс затрагивает только
apps/web/app/pages/shop/products/[slug].vue(SSR-микроданные Product).
Шаги (по concept .wiki/concepts/docker-deploy.md)
- 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. - ⚠️ ПУЛЛ ИЗ РЕЕСТРА. Push образа в
registry.kzntsv.site/pilonuxt:c4e34d4, затем стек 16 в Portainer должен подтянуть свежий образ из registry (re-pull, не крутить кэш старого тега). Без явного pull задеплоится старый код. - Перекат 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
🟢 [#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 отправлен.
исходный наряд
(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→ 200text/plainиз БД (полный robots с Yandex-секцией + Sitemap), НЕ старый allow-all плейсхолдер.GET /yandex_a4e129ae82fc9db5.html→ 200text/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
🟢 [#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) отправлен.
исходный наряд
(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
🟢 [#467 pilonuxt-deploy-sitemap] — [weight: needs-human — deploy-infra] Деплой pilonuxt с sitemap.xml через content-api. Собрать образ из Gitea master victor/pilorama98.ru @ 42f9d64 (feat: sitemap via content-api proxy, content-api@0.15.0), запушить в registry.kzntsv.site/pilonuxt:, перекатить Portainer-стек 16 за Traefik. Грабли билда (как в прошлых pilonuxt-деплоях): форс rm apps/web/src/generated && yarn workspace nuxt-app schema-gen перед билдом; tree-check .output — 0× snolla, 0× btoa, ровно 1× @snollajs/data@0.9.1, и content-api теперь 0.15.0 (+ @snollajs/core@0.2.0). DB-миграция НЕ нужна — sitemap-колонки на проде уже есть (snolla live-проверил на прод-MSSQL). Локально verified против прод-MSSQL.
Status: 🟢 closed 2026-06-29 — pilonuxt:42f9d64 LIVE на проде (Portainer стек 16).
Created: 2026-06-29
Where I stopped: Собран из Gitea master victor/pilorama98.ru @ 42f9d64 (контекст=корень монорепы, -f apps/web/Dockerfile, verdaccio build-secret). Форс-регенерация apps/web/src/generated против прод-MSSQL (schema-gen ok). Tree-check .output/server/node_modules 5/5: content-api 0.15.0, core 0.2.0, data 0.9.1 (ровно 1×), forms-api 0.1.0; snolla-пакетов 0 (7 grep-хитов = только комменты-провенансы, 0 живых import), btoa 0 пакетов/0 импортов. Push в registry напрямую (499 не случился, digest sha256:d937c6b3…). Стек 16 перекатан PUT API (cf2bba2→42f9d64, pullImage:true), контейнер running, Nitro Listening, stderr чист (ECONNRESET нет). DB-миграция не требовалась.
Acceptance на проде (curl --resolve мимо LAN-DNS, 89.253.255.94) — 3/3 + регресс: (1) /sitemap.xml → 200 application/xml, <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
🟢 [#468 pilonuxt-deploy-sitemap-taxonomy] — [weight: needs-human — deploy-infra] Деплой pilonuxt: sitemap taxonomy (категории/теги/vendors). Bump-only поверх уже-LIVE sitemap v1 — @snollajs/content-api 0.15.0→0.16.0 (несёт @snollajs/core@0.4.0, taxonomy enumeration), код прокси не менялся (generic-шим). Собрать из Gitea master victor/pilorama98.ru @ 6b9ae63, push в registry.kzntsv.site/pilonuxt:, перекат Portainer-стека 16. Грабли билда как обычно: rm apps/web/src/generated && yarn workspace nuxt-app schema-gen; tree-check .output — 0× snolla, 0× btoa, ровно 1× @snollajs/data@0.9.1, content-api теперь 0.16.0 + core 0.4.0. DB-миграция НЕ нужна. Локально verified против прод-MSSQL.
Status: 🟢 closed 2026-06-29 — pilonuxt:6b9ae63 LIVE на проде (Portainer стек 16).
Created: 2026-06-29
Where I stopped: Bump-only поверх sitemap v1. Собран из Gitea master 6b9ae63 (форс schema-gen против прод-MSSQL). Tree-check .output 5/5: content-api 0.16.0, core 0.4.0, data 0.9.1 (1×), snolla 0 пакетов/0 import, btoa 0. Push: словил 499 на .output-слое (документированная грабля) → ретрай прошёл, digest sha256:226584ce…. Стек 16 перекатан PUT (42f9d64→6b9ae63, pullImage:true), running, Nitro Listening, stderr чист.
Acceptance на проде (curl --resolve мимо LAN-DNS, 89.253.255.94) — 4/4 + регресс: (1) /sitemap.xml → 200, индекс 7 записей, есть sitemap-store-taxonomy-1.xml ✓; (2) /sitemap-store-taxonomy-1.xml → 200 application/xml, <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
🟢 [#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.jsongitignored). Отдать админу список требуемых runtime-env: DB (mssql.kzntsv.siteMoreThenCms creds), S3/MinIO (minio.kzntsv.sitekeys),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.ruDNS = ОТДЕЛЬНЫЙ 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-контейнеров) · invokeusing-wiki(рунбук после деплоя) · invokeproject-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
🟢 [#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. Notify→workshop.
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 собирает и катит)
- Образ собирает АДМИН на VDS из emspb
deploy/Dockerfile(docker+verdaccio+token на VDS), из чистого git-archive (config/default.json отсутствует ✅ — секретов в образе нет). Тегregistry.kzntsv.site/emspb:<sha>. - Portainer stack на STAGING
emspb.vds.kzntsv.siteза traefik (Host-rule + LE cert + порт). - Runtime-env-секреты в stack-env (НЕ в образ): DB
mssql.kzntsv.site/MoreThenCms, S3/MinIO,siteId=96EBC481-D26A-47BE-B660-13D49E7D0A61, imgproxy HMAC (кросс-проверить как labtools). - 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.ruDNS — НЕ трогать. Отдельный 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: → 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
🟢 [#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.jsonABSENT в архиве,.dockerignore). build-argVERDACCIO_TOKEN(build-time only, не в финальный образ). - siteId
663F9410-A6CC-4651-9A5C-62844A313957, activeTheme389AD745-E3EE-4F23-BE16-DF38440A0941(theme-store MinIOthemes/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
🟢 [#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.
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
🟢 [#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).
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, activeThemeEFCD9555-3400-411E-A27D-68D3D8B3DDB2(theme store MinIOthemes/efcd95553400411ea27d68d3d8b3ddb2/), Culture ru-RU. siteUrlhttps://www.tandemmebel.ru. - production.json В ПИНЕ содержит
imgproxy.watermarkoverride{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.js17/17 vs бой — spot-проверить 301 (target parity modulo host). - Sitemap: snolla index-структура (валидна), все post-URL → 200, 8 постов 2012 = верные даты (core@0.13.8 tz-fix).
/articlesbare-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)
/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./articlesbare-root 404 = parity.
Cutover (live DNS) — GATED ОПЕРАТОРОМ
- (оператор) reg.ru: A
tandemmebel.ru+www.tandemmebel.ru→89.253.255.94(с RUVDS). - (ops) verify флипа авторитетным NS (
ns1.reg.ruчерез Resolve-DnsName) ПЕРЕД traefik. - (ops) ТОЛЬКО ПОСЛЕ flip: stack
tandemmebeltraefik-rule →Host(tandemmebel.ru) || Host(www.tandemmebel.ru). Порядок критичен (иначе LE HTTP-01 упадёт на RUVDS → rate-limit). - 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
🟢 [#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 отправлен.
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 — аккуратно.
Шаги
- Verify (не по памяти): подтвердить, что env
IMGPROXY_WATERMARK_DATAреально сейчас присутствует на стеке 29. Если уже нет — стоп, доложить. - Восстановить env-бэкап БЕЗ
IMGPROXY_WATERMARK_DATA(точечный PUT, как планировалось при заведении глифа) → redeploy стека 29 → purge imgproxy-кэша. - Verify после: pilorama98.ru + sotstayer рендерят картинки как раньше (главный критерий — не задеть боевых тенантов). Приложить проверку (curl/скрин ключевого изображения каждого).
НЕ трогать
- Глиф-бинарь
host-stacks/books-vds/tandemmebel-watermark-logo.png(commit7127e92a) — ОСТАВИТЬ, переиспользуется для 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
🟢 [#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.
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 и кладёт результат в этот бакет, потом отдаёт из него. Кэш ленивый/регенерируемый (можно чистить свободно — восстановится из оригиналов по заходу).
Шаги
- Создать ПУСТОЙ бакет
variant-cacheв том же shared MinIO, где лежат snolla-бакеты оригиналов/тем (тот же endpoint). - Дать сервис-кредам snolla content-api (те же, что читают оригиналы) read+write (putObject/getObject/headObject) на
variant-cache. Verify, что деплойные S3-креды реально могут писать туда. - 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
🟢 [#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.
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больше нет.
Шаги
- Собрать образ на VDS из ЧИСТОГО git-archive sha
68b93a95(guard: секретов в образе нет; production.json = конфиг, не секреты). Push в registry. - Обновить/пересобрать staging-стек tandemmebel (прошлый — Portainer стек 20,
tandemmebel.vds.kzntsv.site) на новый образ. Runtime-секреты (DB mssql / MinIO S3-креды / siteId) — как раньше, verbatim, в stack-env, НЕ в образ. - 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 было зелёное).
- Картинки: slug-URL
- Доложить staging-green + находки в inbox workshop. Всё, что «увидишь хренью» — репорти, не проглатывай.
Заметки / guards
- variant-cache — общий бакет: staging пишет туда же, куда потом прод. Кэш content-addressed (source+size+watermark) → байты идентичны, staging просто пред-прогреет. Не пуга́йся объектов в бакете.
- GothamPro heads-up (из прошлого хендоффа): прод-tandemmebel.ru отдаёт
Gotham-Pro.css0 байт, 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
🟢 [#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 (обязательные гейты)
- Pre-deploy SSR-чек (напоминание сноллы): SSR
/projects+ blog-listing → 200, не 500. Причина:getBlogByPathтянет новые SELECT-колонки; в боевой БД tandemmebel колонки уже есть (проверено сырым SQL в снолла-сессии), но подтверди на целевой БД. - Completeness-DoD перепрогнать НА новом 0.42.0-образе (не наследовать GREEN с 0.16.2): боевой sitemap ~188 URL prod↔staging → 0 реальных prod-200/staging-не-200; gallery grid-паритет 15 роутов staging==prod. Ядро навигации полностью перепахано — свежий прогон обязателен.
- Archive-страницы — НЕ блокер: live-verify archive-страниц оператор отложил. Для
projectsarchive-роуты off (archivesPath=""). Не гейтить на них. - Rollback (RUVDS / предыдущий пин) наготове.
Обязательные скилы — вызвать до начала
- invoke
using-tasks— управление статусом задачи - invoke
using-vds-ops— диагностика контейнеров на VDS - invoke
project-discipline— дисциплина коммитов/деплоя - invoke
using-wikiпри закрытии — заингесть concept, если топология/пин менялись
Отчётность
- notify: OpeItcLoc03/workshop (комиссионер-оркестратор).
- По закрытию/парку — heads-up ТАКЖЕ в
victor/snollainbox: 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
🟢 [#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 (гейты на НОВОМ образе ДО подмены — не наследовать)
- Build на VDS из sha
566d41c→ образ вregistry.kzntsv.site(BUILD_EXIT=0). - 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-перехват).
- Catalog-parity: порядок изделий в листингах by list_priority (presses = plg-20, plg-12, plg-25, pgr-10 — ключевой чек фикса 0.42.1); непустые сетки всех 6 секций.
- Подмена тега на живом стеке 17 → контейнер healthy → live-smoke
labtools.ru== прод, rollback (предыдущий тег) наготове. - Секреты НЕ в образе (
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
🟢 [#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 buildOK; секреты НЕ запечены (history чист,config/default.jsonотсутствует в образе); контейнер healthy; image-sweep 29 роутов + robots/sitemap/theme.css/3.jpg = все 200.
Acceptance (перепрогнать на НОВОМ 0.42.1-образе — не наследовать GREEN)
- Собрать на VDS из чистого git-archive
95a5c42→registry.kzntsv.site/emspb:95a5c42→ push. - Контейнер healthy (healthcheck /robots.txt).
- Completeness-gate на образе: 29 sitemap-роутов → 200, canonical 301 (trailing/lowercase/collapse //), theme-ассеты из MinIO,
/images/3.jpg200. Гнать смоук С VDS — воркстейшн перехватывает прод-домены в LAN-DNS. - 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
⚪ [#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 buildOK; секреты НЕ запечены (history чист,config/default.jsonотсутствует в образе); контейнер healthy; image-sweep 29 роутов + robots/sitemap/theme.css/3.jpg = все 200.
Acceptance (на НОВОМ 0.42.1-образе ДО подмены — не наследовать GREEN)
- Собрать на VDS из чистого git-archive
95a5c42→registry.kzntsv.site/emspb:95a5c42→ push. - Контейнер healthy (healthcheck /robots.txt).
- Completeness-gate на образе: 29 sitemap-роутов → 200, canonical 301 (trailing/lowercase/collapse //), theme-ассеты из MinIO,
/images/3.jpg200. Гнать смоук С VDS — воркстейшн перехватывает прод-домены в LAN-DNS. - Боевой 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
🟢 [#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)
- Собрать образ на VDS
registry.kzntsv.site/labtools-pro:0610432(BUILD_EXIT=0), push в registry. - completeness-gate: полный список боевых роутов www.labtools.pro (sitemap-развёртка + краул) vs новый образ → 0 боевых-200 → образ-non-200 (проггер: 27 роутов, эталон).
- каталог-парити (order-фикс 0.42.1): порядок изделий в листингах — presses=[plg-20,plg-12], milling=[milling-jars,grinding-media], press-forms=12 шт. Смоук гнать С VDS (воркстейшн ловит LAN-DNS-перехват прод-доменов).
- Секреты не запечены в образ (проверить
docker image inspect ... .Config.Env— токена/паролей нет). - 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
🟢 [#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
🟢 [#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
Review outcome (2026-07-13)
🟢 DONE — 8df10ee LIVE на проде.
- Build
tandemmebel:8df10eeна VDS (clean gitea archive, build-arg VERDACCIO_TOKEN=books-ci JWT), digestsha256:30a7e5ab82f2bf371042f1b5fa0cd7ac5ceda71379d1da59e148dee8166bd560, pushed to registry. - Pre-build verification caught discrepancy: letter claimed live=
ed96b18/0.42.0; actually0cd9351/0.42.1 (in-place bump 2026-07-12). giteacompare/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
632c843fpushed. - 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).
🟢 [#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
🟢 [#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
🟢 [#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
🟢 [#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
🟢 [#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
🟢 [#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
🟢 [#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
🟢 [#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
🟢 [#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
🟢 [#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
🟢 [#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 (найден прогером):
- region 'local' в конфигах (api/web/web4) — MinIO minio-api.stostayer.ru настроен на us-west-1 → AuthorizationHeaderMalformed 400 → listDirectFiles кидал → весь route 500.
- Второй баг в цепочке: 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" с
-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
🟢 [#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:
- region 'local' в конфигах (api/web/web4) — MinIO minio-api.stostayer.ru настроен на us-west-1 → AuthorizationHeaderMalformed 400 → listDirectFiles кидал → весь route 500.
- Второй баг: 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
🟢 [#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
🟢 [#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
🟢 [#706 sched-admin-client-openapi] — Цель: написать admin-клиент для sched на ЛЮБОМ другом языке (не TypeScript/JavaScript) строго по OpenAPI-спеке @sched/admin-api, проверить живым smoke по методике F&F. Это живая проверка claim'а openapi-spec: «клиент на любом ЯП одной командой». Клиент пишется В РЕПО SCHED (C:\Users\vitya\projects\sched, папка examples/admin-client-/, по образцу examples/workers/), ПУШ РАЗРЕШЁН — решение vitya 2026-08-18 (явное исключение из fnf-testing-procedure «внешний агент не пушит в наш репо»).
Acceptance criteria
- Язык — любой, кроме 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-исходника.
- Покрытие: ВСЕ пути спеки (openapi.json 3.1, 15 путей: /health, /tasks (+run|pause|resume|schedule), /schedules (+/:id|pause|resume), /runs (+/:id|retry), artifacts; securityScheme Bearer).
- Живой smoke против реального admin-api: свой стенд (verdaccio-пакеты, свой порт — конвенция .admin/.tmp/) или наш :8080 (живой, Node 24). Каждый путь дёрнут минимум раз, результат — в репорте.
- Репорт по F&F-формату: что делал → что ожидал → что получил (вывод/стек) → ссылка на раздел доки + «гладкое». Находки/пробелы спеки-доков — в репорт, НЕ чинить (имплим мы).
- TDD: unit-тесты клиента на контракт спеки + smoke-харнесс.
Обязательные скилы — вызвать до начала работы
- invoke
tdd-criteria— до написания кода - invoke
using-tasks— управление статусом задачи - invoke
project-discipline— дисциплина коммитов/пушей (в чужом репо аккуратно: только свои файлы examples/admin-client-/) - 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
🟡 [#790 sched-vds-deploy] — Деплой sched (демон + web UI) на VDS на постоянку, без воркеров/задач. Решения (2026-08-21): поддомен sched.vds.kzntsv.site (websecure + letsEncrypt, сеть proxy); basic auth на web-морде через middleware traefik sched-auth@file (логин vitya, пароль — pass: sched/basic-auth, НЕ в git); daemon admin API наружу не выставлять (shell проксирует внутри сети); SCHED_ADMIN_KEY из секретов (pass: sched/admin-key при деплое); prod-compose: daemon+shell, volume для sched.db + бэкап-политика, restart unless-stopped; образ registry.kzntsv.site/sched:; деплой стека через Portainer (traefik/portainer — ad-hoc). Проверка после деплоя: curl -u → 401/200; UI открывается; БД персистентна. СКОУП: только sched, билдеры apilki НЕ входят (их деплой — отдельные задачи).
Status: paused Created: 2026-08-21 Where I stopped: Пауза по решению vitya (2026-08-21): ждём результатов миграции books→sched (victor/books sched-migration-phase-3). VDS-деплой sched решается по итогам — см. также victor/sched books-sched-integration (paused). Не запускать до отмашки. Next action: (1) prod-compose (daemon+shell, label-ы traefik, certresolver, port 8081), creds в pass (sched/basic-auth есть; sched/admin-key); (2) образ в registry.kzntsv.site/sched:; (3) деплой через Portainer; (4) dynamic/ traefik: middleware sched-auth (bcrypt из pass), релоад; (5) curl-проверка 401/200 + браузер; (6) отчёт. Перед подключением воркеров — HTTP-runner-таски sched (envelope-violation прежде всего). Branch: n/a Notify: .admin
⚪ [#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
🟢 [#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
🟢 [#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
⚪ [#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).
Что сделать:
- Docker compose рядом с общей БД на VDS (postgres:16 на VDS running; отдельная база mappa, свой role, свои pg_dump — решение 1 спеки). Сервис mappa (HTTP-ядро + MCP-адаптер, dist) одним контейнером.
- Прод-импорт в прод-БД: ~/projects/* + ~/projects/.wiki (идемпотентный upsert, импортёр прогоняется заново — решения 17/18, миграции dev→prod нет). Верификация счётчиков (сходятся с файловыми источниками).
- Верификация: health / admin.status / graph.stats.
- Файлы после импорта 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
🟢 [#1013 mappa-ntfy-monitor] — Мониторинг живости сервиса mappa на VDS → ntfy-алерт при падении. Остаток #985 mappa-deploy (решение 15/18: фолбэк-режим, алерт при падении сервиса).
Что сделать:
- Монитор health-эндпоинта https://mappa.vds.kzntsv.site/health (ответ {"ok":true}).
- При падении (недоступен / не-ok / ошибка) — алерт в ntfy: канал ntfy.vds.kzntsv.site, basic-auth vitya/Pryakhin9 (в pass vds-kzntsv/full-env), топик mappa-alerts (свободен).
- Интервал и ретраи — по ранбуку существующих мониторов 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
⚪ [#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: ready Created: 2026-08-23 Where I stopped: (not started) Next action: Редеплой mappa на VDS: git pull в контейнерном билде, задать MAPPA_API_TOKEN (сильный случайный), перезапустить, проверить GET /health без токена (200) и /admin/status без токена (401), обновить MCP-регистрацию агентов (env токена). Branch: n/a Notify: OpeItcLoc03/mappa
⚪ [#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: ready Created: 2026-08-23 Where I stopped: (not started) Next action: 1. Собрать+задеплоить mappa v0.5.0 (git-index #996: src/gitindex.ts, HTTP /gitindex/, MCP git.index_). 2. Задать MAPPA_GITEA_TOKEN на проде (read-only; фолбэк auth.toml). 3. POST /gitindex/sync + GET /gitindex/verify — сходимость. 4. Отписать в mappa-инбокс (event: closed). Branch: n/a Notify: OpeItcLoc03/mappa