# Sprint 12 — документация, runbook, нагрузочное тестирование Цель: переложить «то что знаю только я и комментарии в коде» в читаемые документы для следующего разработчика, замерить реальную производительность под нагрузкой, и закрыть автоматическую верификацию stage-стэйджа на каждый push. Старт: 2026-06-07. Исполнитель: Claude Opus 4.7. Это **последний автономно-безопасный спринт**. Дальше нужны входы от user'а: реальные ОФД-ApiKey, MoySklad webhook-token'ы, Windows-машина для POS WPF, прод-деплой план, казахские переводы, реальный SMTP-провайдер. ## Принципы - Документация — для человека, не «AI-портянка». Конкретные пути, имена типов, причины решений. Без воды и эмоций. - k6 — реальные числа. Если p95 высокий — пишем как есть. - НЕ трогать: `global.json`, прод-стек, POS WPF. ## Чек-лист - [x] **1. docs/ARCHITECTURE.md** — карта слоёв, модулей, потоков signup→bootstrap→операции. Реальные имена типов и путей, не маркетинг. - [x] **2. docs/MULTI-TENANCY.md** — `ITenantEntity` + reflection query-filter, stamping в SaveChanges, SuperAdmin override (read-only + edit-mode с reason), 8 подводных камней (IgnoreQueryFilters, фоновые jobs без HttpContext, raw SQL, и т.д.). - [x] **3. docs/RUNBOOK.md** — health-чеки, backup/restore (включая disaster-recovery), смена SDK, перенос на новый сервер, **6 описанных инцидентов** (включая docker-compose project name из ТЗ), troubleshooting БД (stock-агрегат расхождения, audit-log размер, EFMigrationsHistory). - [x] **4. docs/DEVELOPER-GUIDE.md** — локальный setup, запуск тестов, гочи integration-тестов (Ryuk, rate-limiter eager-config, один ApiFactory), полные паттерны: добавить controller с permission + добавить tenant-сущность с RowVersion + 5 шагов миграции, валидация (DataAnnotations / FluentValidation / бизнес), structured-логирование. - [x] **5. k6 нагрузочный тест** — `tests/load/` + 3 скрипта (signup-burst, retail-sales-parallel, sales-report-heavy) + `docs/performance-baseline.md` с **реальными цифрами** на stage'е. Главное найденное: race в `GenerateNumberAsync` при VU > 1 на одном tenant'е (unique-violation 23505 не ловится → 500). Прогон зарегистрирован как P0 для следующего рефакторинга. - [x] **6. CI workflow `.forgejo/workflows/stage-verify.yml`** — `on workflow_run` после `Docker API`/`Docker Web`, ждёт `/health/ready` и запускает `tests/stage-smoke.sh` (~7с, full-cycle smoke: signup → multi-tenant isolation → supply.post → retail-sale.post → stock check). Telegram-нотификация по успеху/падению. ## Журнал ### 2026-06-07 старт Sprint 11 закрыт (7/7 ✓). Поехали по docs-чек-листу. ### 2026-06-07 п.1–п.4 (документация) Прочитал реальный код: `Program.cs` composition root, `AppDbContext` reflection-фильтры, `HttpContextTenantContext` с AsyncLocal-override, `SuperAdminOverrideClaimsTransformer` + `ReadonlyOverrideMiddleware`, `RequiresPermissionAttribute` + policy-handler, `HangfireJobsConfigurator` recurring jobs, deploy/Dockerfile + docker-compose, backup-скрипт + systemd-timer. Написал 4 документа на основе этого: - `ARCHITECTURE.md` (372 строки) — слои + модули + composition root + поток signup→post с детальным трассировщиком ASP.NET pipeline. - `MULTI-TENANCY.md` (256 строк) — query-filter, stamping, SuperAdmin override, 8 подводных камней + чеклист «как добавить tenant-сущность». - `RUNBOOK.md` (337 строк) — health-чеки, backup/restore с примером, смена SDK, disaster-recovery, 6 инцидентов, БД-troubleshooting. - `DEVELOPER-GUIDE.md` (332 строки) — локальный setup, тесты, паттерны (controller + entity + валидация + логирование), "НЕ делать" список. ### 2026-06-07 п.5 (k6 baseline) k6 v0.55.0 standalone в `~/bin/k6`. 3 скрипта в `tests/load/`: - `signup-burst.js`: 50 RPM → p95 446ms, 0% errors. 100 RPM → 39% 429 (IP-лимит работает, by design). - `retail-sales-parallel.js`: VU=1 — 17 sales/sec, p95 71ms, 0% failures. VU=5 — **53% failure** из-за race в `GenerateNumberAsync` (unique violation на `RetailSale.Number`). Это **реальная находка**, P0 для следующего спринта. - `sales-report-heavy.js`: на tenant'е с 1500 чеков, VU=1 — p95 54ms, VU=4 — p95 81ms, VU=5 — p95 114ms (один аномальный прогон показал 3.8с — autovacuum suspect). Все цифры в `docs/performance-baseline.md` с воспроизведением. ### 2026-06-07 п.6 (CI workflow) `.forgejo/workflows/stage-verify.yml` — `on: workflow_run` после `Docker API` и `Docker Web`, не запускается на failed parent (нет смысла верифировать незадеплоенное). Шаги: wait-for-ready (60с retry loop) → запустить `tests/stage-smoke.sh` → Telegram пинг. `tests/stage-smoke.sh` — bash-скрипт без зависимостей кроме curl+jq+python3. 5 этапов: health, signup A, token A, multi-tenant isolation (A создаёт продукт, B получает 404 + список без продукта A), полный документ-цикл (supplier+supply.post → проверка stock=100 → sale.post → проверка stock=99). Локальный прогон против stage — **7 секунд**, всё зелёное. ### Итог Все 6 пунктов ✓. Документация: - 4 новых файла в `docs/` (~1300 строк суммарно). - `docs/performance-baseline.md` — реальные цифры + 1 находка P0. Тестирование: - 3 k6 скрипта в `tests/load/`. - `tests/stage-smoke.sh` — 7-секундный smoke против stage. CI: - `.forgejo/workflows/stage-verify.yml` — auto-verify на каждый successful deploy. Следующие шаги, требующие user'а (за пределами автономного режима): 1. Реальный ОФД ApiKey (Webkassa предпочтительно) — Sprint 11-fiscal ждёт это для активации. 2. Решение по прод-деплой (домен + cert + DNS). 3. MoySklad webhook-токены для inline-импорта. 4. Windows-машина (или CI runner) для POS WPF сборки. 5. Казахский переводчик для UI (i18n уже подготовлен). 6. Реальный SMTP-провайдер для платформы (Mailgun / Postmark / Yandex). Plus P0-задача из baseline'а: исправить race в `GenerateNumberAsync` для `RetailSalesController` и аналогичных контроллеров — это уже автономно делается, но требует дизайн-решения (per-tenant sequence vs counter table vs retry-loop).