Files
Deal/docs/superpowers/STATUS.md
T
Rustam Khalimov 27c7831910
ci / build-test (push) Canceled after 0s
Deal — единая кодовая база
SaaS-мониторинг Telegram: ядро (модули Cards/Kanban/Pipeline/Tenants/Settings/
Discovery, Api, Infrastructure), сервисы telegram/ai/ml/storage, фронт Vue,
контракты и grpc-hosting, деплой-конфиги (dev/prod/observability/CI-раннер),
Gitea Actions CI, документация (ТЗ, техдок, api-map, код-стайл, планы, бэклог).

Текущее состояние: все этапы роадмапа 0–12 закрыты, сборка 5 sln 0/0,
тесты 1340/130/52/38/9 зелёные.
2026-09-11 23:56:47 +03:00

242 lines
28 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Дейл (Deal) — Статус разработки и прогресс
> Обновляется в конце каждого захода. Проект в git (ветка `main`, коммиты на русском) — борд состояния + git-история.
> Дата последнего обновления: 2026-09-11.
>
> **2026-09-11 — единый контракт источника (generic source).** Ядро (домен Cards, Storage-сервис,
> персистентность, конвейер, wire, фронт) переведено с Telegram-полей карточки на generic-тип
> `SourceItem` (`SourceRef` + `SourceContent`, вложения — `DataRef` → общий Storage). Дашборды/канбан/конвейер
> больше не знают о Telegram; Telegram-специфика — только в тонком адаптере приёма. Tenant-миграции
> пересозданы с нуля (init). Добавлены extension-point `ISourceContentProvider`/`SourceContentResolver` и
> `GET /api/cards/{id}/source`. Входящий поток источников — generic (`sources.proto`/`PushSource`,
> `SourceIngressGrpcService`), `PushMessage` из telegram.proto удалён. Сухой прогон текста по конвейеру
> (стоп-правила → ML → ИИ) без записи: `POST /api/admin/check-message` + UI настроек. Remote-просмотр
> исходника: `TelegramService.ReadSource` + `TelegramSourceContentProvider` + UI «Обновить из источника».
> Метрика алертинга `deal.ai.budget.used.ratio{tenant}`; actor/tenant в access-логе; прогресс переклассификации
> через SSE. Hardening контейнеров (non-root/read-only/limits), шардированная пакетная миграция схем,
> единый CI (`scripts/ci.sh`). Ядро: build 5 sln 0/0, `Deal.Tests.Unit` **1326/1326 PASS**,
> telegram **130/130**, ai **52/52**, ml **38/38**, storage **9/9**, фронт `build` + `lint:i18n` зелёные.
> Детали — `docs/superpowers/specs/2026-09-11-source-contract-design.md`.
>
> **2026-09-11 (вечер) — закрыты остатки код-стайла (TD-COMMENTS-IFACE, TD-STYLE-ANALYZERS).** Дедупликация
> `<summary>`: дублей нет (сканы по тексту и по имени члена — 39 интерфейсов/229 членов). `var`: гейт
> `csharp_style_var_for_built_in_types = false:warning` (ломает сборку), остаток выправлен `dotnet format`
> по 5 sln (51 файл), «очевидный/прочий тип» — silent осознанно. Переводы строк: решено LF — `.gitattributes`
> (`* text=auto eol=lf`), `.editorconfig` → lf, нормализовано 1029 файлов; попутно починены 42 CRLF-.sh
> (первый прогон удалённого CI падал бы). Понижено 12 новых private XML-доков; добавлены 4 `<summary>`
> членам интерфейсов; переведены 3 англоязычных комментария; из индекса убраны 2 `__pycache__/*.pyc`;
> STATUS.md — удалён устаревший блок «Осталось (в backlog)» в шапке. Дочистка по контрольному скану
> краткости: удалены 73 очевидных `<param name="ct">` (пересказ сигнатуры) в 17 файлах, ужаты 3 summary
> (многосентенционные/длинные); контроль: `<remarks>` 0, inline-`<summary>` 0, многосентенционных 0, TODO 0. Явные реализации интерфейсов (§11) —
> остались точечным ревью владельца (43 интерфейса с реализациями, массовая правка не автоматизируется).
> Сборка 5 sln 0/0; тесты: core **1340/1340**, telegram **130/130**, ai **52/52**, ml **38/38**, storage **9/9** — зелёные.
> Детали — `docs/spec/Код-стайл-аудит-2026-09-11.md`, §2.
>
> **2026-09-11 (ночь) — Gitea-контур.** Репозиторий запушен (`gitea.khomegeneric.keenetic.pro/rust/Deal`),
> ~4000 спам-пользователей вычищено, регистрация закрыта владельцем. CI перенесён в
> `.gitea/workflows/ci.yml`; первый прогон поймал и закрыл кроссплатформенный баг
> (`ModelPool` валидировал tenant-id через `GetInvalidFileNameChars` — на Linux пропускал `\`);
> валидация заменена на явный белый список. **`scripts/` исключён из репозитория** (правило владельца:
> в репе только код) — скрипты сборки/тестов/бэкапов/кодмоды живут только локально; CI-workflow
> переписан inline. Раннер для CI — на сервере рядом с Gitea (пакет `deploy/gitea-runner/`).
>
> **2026-09-11 (поздний вечер) — явные реализации интерфейсов (вариант A, решение владельца).** Правило
> владельца: классы напрямую не вызываем (исключения — DTO, хелперы, экстеншены), тесты — через
> интерфейсы, моки — NSubstitute, маркерные классы не используем. Сделано: 161 член в 30 прод-файлах
> переведён на явные реализации codemod'ом `scripts/make_explicit.py`; потребители конкретных типов
> перетипизированы на интерфейсы (8 мест в проде, 17 тест-файлов; самовызовы — `((ISessionClient)this)`);
> 10 маркерных классов заменены маркерными интерфейсами (`IKanbanModule`…`ISharedKernel`); NSubstitute 6.1.0
> подключён к 5 тест-проектам, эталон миграции — `FakePasswordHasher` → `TestHashers.New()` (фейк удалён);
> правила зафиксированы в §11 код-стайла. Card/`I*Card` — implicit (DTO). Оставшиеся 30 фейков —
> поэтапная миграция (`backlog.md`, TD-TESTS-NSUBSTITUTE). Build 5 sln 0/0, тесты зелёные.
**Все этапы 0–12 выполнены (100%)** — см. roadmap
> `docs/superpowers/plans/2026-09-05-deal-roadmap.md`, план этапа 10
> `docs/superpowers/plans/2026-09-10-deal-stage10-operator-analytics.md` и ledgers в `.superpowers/sdd/`.
> Live-приёмки на Docker Desktop выполнены: dev-smoke 14/14, SaaS-контур 15/15, prod-контур+mTLS+
> observability PASS, backup/restore на копии PASS, runtime-приёмка этапа 10 (оператор-консоль,
> аналитика, аудит) PASS (см. чек-лист ниже). Осталось Manual: реальные Telegram/LLM-креды (п.5).
## Общий прогресс по этапам
| Этап | Статус | Задач | Тесты (unit, накопительно) | Приёмка |
|---|---|---|---|---|
| 0. Каркас | ✅ готов | 9/9 | 6 | health 200 |
| 1. Доступ и мультитенантность | ✅ готов | 6/6 | 25 | auth 1:1 |
| 2. Settings (настройки) | ✅ готов | 11/11 | 175 | 60/60 |
| 3. Kanban (дашборд) | ✅ готов | 15/15 | 410 | 94/94 |
| 4. Pipeline/«Обработка» | ✅ готов | 13/13 | 535 | 74/74 |
| 5. Projects («Выбранные») | ✅ готов | 13/13 | 620 | 75/75 |
| 6. Сервисы telegram/ml/ai + Discovery | ✅ готов | 20/20 | 830 | 20/20 + 37/37 |
| 7. SaaS-контур (оператор/инвайты/лимиты/аудит/безопасность/prod-деплой/бэкапы/доки) | ✅ готов | 16/16 | 1123 | ✅ live SaaS 15/15 (остальное — ⚠ Manual) |
| 8. Code-quality rework (ревью 5 зон) | ✅ готов | 5/5 фаз | 1139 | build 4 sln 0/0; фронт build OK |
| 9. Единая карточка (слияние Kanban/Projects, `/api/cards`+`/api/containers`) | ✅ готов | 11/11 | 1138 | ✅ live dev-smoke PASS=14 FAIL=0 |
| 10. Оператор-консоль, аналитика, аудит действий, Grafana/Loki-дашборды | ✅ готов | 7/7 | 1173 | ✅ live runtime (Docker dev) |
| 11. Локализация UI (вынос строк в ресурсы) | ✅ готов | 7/7 | 1173 | build + `lint:i18n` зелёные |
| 12. Наблюдаемость/устойчивость/перф + добивка ТЗ | ✅ готов | 4/4 пакетов + добивка | 1275 | build 4 sln 0/0; telegram 125/125 |
| **Итого** | **этапы 012 = 100%** | **137/137** | **1275 (core)** + 125/52/38 (сервисы) | — |
Финальный прогон этапа 12 (2026-09-10, автономный заход A–D): build `Deal.sln` 0/0; core **1275/1275 PASS**;
telegram **125/125**; фронт `npm run build` зелёный (main-чанк 309 kB, словарь в отдельном чанке i18n),
`npm run lint:i18n` зелёный; метрики: `/metrics` (OTel→Prometheus) во всех 4 процессах, Prometheus targets 5/5 UP;
пакеты B (rate-limit/LoginAttemptGuard на Postgres, разлогин suspended, purge) и D (reclassify + токены ML)
с зелёными тестами. Всё остановлено (правило «без хвостов»). LEDGER: `.superpowers/sdd/deal-stage12-observability-hardening/`.
Финальный прогон этапа 10 (2026-09-10): build `Deal.sln` 0/0; core **1173/1173 PASS**; фронт
`npm run build` зелёный; `GET /api/operator/analytics/{overview,tokens,activity}` и
`GET /api/operator/audit` живьём на dev-Postgres (`:5433`, миграция `AddTokenUsageEvents` применена),
`deal-core` healthy; страницы `#/operator` и `#/join` отдаются dev-сервером. Детали — ledger
`.superpowers/sdd/deal-stage10-operator-analytics/progress.md`.
Финальный прогон (Task 16, 2026-09-08, docker выключен): build 0 warnings / 0 errors всех четырёх sln
(core/telegram/ai/ml); core 1123/1123 PASS, telegram 114/114, ai 50/50, ml 36/36 PASS;
`docker compose -f deploy/compose.prod.yml config` rc=0 (+ профиль observability);
`sh -n` scripts/dev-smoke.sh/backup.sh/restore.sh/mtls-certs.sh rc=0.
> Накопительный счётчик core в таблице — по состоянию на конец этапа: этап 8 — 1139, этап 9 — 1138
> (часть тестов удалена вместе с доменом Projects), этап 10 — 1173.
## Что система умеет СЕЙЧАС (проверяемо)
- **Ядро (этапы 06)**: вход admin/admin (dev-seed, dev-only), сессии, схемы на тенанта (Postgres :5433);
дашборд (колонки/карточки/drag&drop/архив/корзина/FTS-поиск), «Обработка»
(очередь→стоп-лист→дедуп→ML/ИИ→карточка), «Выбранные» (стадии/напоминания SSE/файлы Local/MinIO),
Настройки (ключи AI enc:, промпты, валюты; Telegram-ключи — глобально у оператора), Каналы/Discovery; полный dev-стек этапа 6 —
`deploy/compose.dev.yml` (`Services__*__UseLocal=false`, gRPC-режим telegram/ai/ml + ингресс core).
- **SaaS-контур (этап 7)**: оператор (`public.operators/operator_sessions`, кука `deal_operator_session`,
bootstrap env `DEAL_OPERATOR_*`; dev-дефолт operator/operator) и ручки `/api/operator/*`
(auth/tenants/invites/limits/audit/health — с этапа 10 у них есть UI, см. ниже); инвайты (код 16 симв., 72 ч) и активация
`POST /api/join` (пользователь + провижининг тенанта); лимиты ИИ-бюджета (`tenant_limits`,
`TokenUsageRecorder`, гейт-декораторы → Local-фолбэк, SSE-тосты 80/100%); append-only аудит;
rate limiting (auth 10/мин·IP, api 600/мин·тенант, gRPC-ингресс 600/мин·тенант, `LoginAttemptGuard`
5/15 мин); Origin-проверка мутаций + security-заголовки + ForwardedHeaders за Caddy;
mTLS за флагом `DEAL_MTLS_*` (`scripts/mtls-certs.sh``deploy/certs/`); Serilog JSON во всех
4 процессах (+ access-логи HTTP/gRPC); prod-деплой `deploy/compose.prod.yml` (caddy 80/443,
postgres/minio без host-портов, профиль observability: promtail/loki/grafana, `.env.prod.example`);
бэкапы `scripts/backup.sh`/`restore.sh`/`deal-backup-lib.sh` (pg_dump -Fc + MinIO + tar; retention 14).
- **Оператор-консоль и аналитика (этап 10)**: hash-роутер фронта (`#/` приложение, `#/operator` консоль,
`#/join?code=…` активация инвайта); разделы консоли (вход, тенанты+suspend/resume/impersonate,
инвайты, лимиты, аудит с фильтрами/пагинацией, аналитика, health); impersonation ставит httpOnly-куку
`deal_session` тем же ответом (оператор сразу в тенанте). Сквозной аудит действий (`public.audit_log`):
выходы, `invite_joined`, действия карточек/комментариев, CRUD контейнеров, настройки, каналы, Telegram.
История расхода токенов `public.token_usage_events` + операторская аналитика
(`/api/operator/analytics/{overview,tokens,activity}`, `groupBy=day|tenant|provider|model`).
Grafana provisioning (datasource Loki + дашборды `Deal-Auth/Errors/Rps/Logs`) и promtail-лейблы.
- **Что увидеть глазами**: полный dev-стек — `docker compose -f deploy/compose.dev.yml up -d --build`
→ фронт `cd src/frontend && npm run dev` → логин `admin/admin` (dev-seed);
либо сквозной smoke одной командой: `sh scripts/dev-smoke.sh`. Prod-контур — §13.8 техдока
(оператор → тенант → инвайт → `/api/join`). Host-режим (Local-заглушки): postgres/minio +
`dotnet run --project src/core/Deal.Api --urls http://localhost:5080`.
- Полная карта «что/где» — техдок `docs/technical/Техническая-документация-Дейл.md` (§5, §7–§11,
§13.1–§13.10), api-map `docs/api/api-map.md` (раздел «Реализовано в Deal»), инструкция пользователя
`docs/user-guide/Инструкция-пользователя-Дейл.md`.
## Manual-чек-лист (остаток после live-приёмок)
Выполнено живьём на Docker (автономно от авто-прогонов Task 16, build/test/config/syntax — там же):
1.**SaaS-сквозная приёмка (live, 15/15 PASS)**`run-live-saas.sh` + `live-saas-check.sh` на поднятом
dev-Postgres (:5433, миграции SystemSaaS + SessionsImpersonationMark применены): оператор login →
создать тенанта → инвайт → `POST /api/join` → вход пользователя → settings/cards (на тот момент —
`boards`/демо-карточка) →
IDOR-негатив 401 (пользователь к операторским ручкам) → suspend (вход 403) → resume (вход 200) →
лимиты (tenant_limits) → аудит-лента. Core погашен, :5080 свободен.
2.**dev-smoke 14/14 PASS** — полный gRPC-стек (`scripts/dev-smoke.sh`): подъём, health, login
admin/admin, `/api/tg/status` idle, `POST /api/cards` (карточка `planned`) → trash → обучающий сигнал spam,
ML-флашер выгрузил outbox. Стек погашен скриптом (trap).
3.**Prod-контур + mTLS + observability (live)** — сертификаты перегенерированы (`scripts/mtls-certs.sh -f`,)
полный набор в deploy/certs; подъём `compose.prod.yml` + `--profile observability` с фиктивными
env-секретами (`DEAL_MTLS_ENABLED=1`, endpoint'ы https://): core/telegram/ai/ml **healthy** под mTLS;
исходящее mTLS подтверждено живьём — `/api/tg/status` (idle) и `/api/ml/status` (reachable:true) через
Caddy; фронт и `/api/health` через Caddy 200; promtail→loki (логи пишутся), Grafana 200. Исправлен
дефект `deploy/observability/loki.yml` (Loki 3.x: `delete_request_store`). `.env.prod` тестовый удалён.
4.**backup/restore (live, на копии)**`backup.sh`: pg (-Fc) + minio (docker-mc) + data (tar docker-томов)
+ retention; `restore.sh pg` в копию-БД — 43 таблицы/3 схемы идентичны, данные сошлись (users=2,
tenants=2, sessions=30); `restore.sh minio` с реальным объектом (залит→бэкап→удалён→восстановлен);
`restore.sh data`. Исправлены дефекты скриптов, проявившиеся живьём: двойная схема в MC_HOST_deal
(`deal-backup-lib.sh`), пустой бакет → mv (`backup.sh`), Windows/MSYS docker-пути (`host_docker_path`).
5. ❌ Реальный Telegram-вход (api_id/api_hash/QR) и LLM-вызовы — **нужны живые креды**.
6. ✅ Прогон `scripts/backup.sh` и restore-тест — см. п.4 (полный цикл на dev-хранилищах и копии-БД).
7.**Этап 10 — runtime-приёмка (Docker dev-стек)** — миграция `AddTokenUsageEvents` применена к
dev-Postgres (`:5433`), `deal-core` пересобран/healthy; операторский вход `operator`/`operator` → 200;
`GET /api/operator/tenants`, `/audit`, `/analytics/overview`, `/analytics/tokens?groupBy=day|provider`,
`/analytics/activity?limit=3` → 200 (реальные лента/агрегаты); фронт `npm run build` зелёный, dev-сервер
отдаёт `#/operator` и `#/join`; core-тесты 1173/1173. Детали — ledger этапа 10.
## Заделы (этап 13+; подробно — техдок §11 и roadmap)
> **Единый источник отложенного и техдолга — `docs/superpowers/backlog.md`.** Ниже — краткая выжимка.
- **Этап 11 — Локализация интерфейса (i18n)** — **выполнен** (2026-09-10, урезанный объём): все
пользовательские строки фронта в ресурсах (`src/frontend/src/i18n/`, 1039 ключей в 13 областях),
линтер `npm run lint:i18n`. Переключатель языка и второй язык — **в бэклоге**: делаем, когда появится
потребность (ядро i18n/`registerLocale` к этому готово).
- **Этап 12 — Наблюдаемость/устойчивость/перф** — **выполнен** (2026-09-10, автономно, пакеты A–D):
метрики Prometheus+Grafana (`/metrics` :9464 во всех процессах); распределённый rate-limit и
`LoginAttemptGuard` на Postgres; мгновенный разлогин suspended-сессий; авто-purge `audit_log`/`tenant_limits`;
разбиение бандла фронта + прогрессивный рендер колонок; LRU-кэши WTelegram; пакетная миграция схем тенантов;
реальный `reclassify` с Local-фолбэком. LEDGER: `.superpowers/sdd/deal-stage12-observability-hardening/`.
- **Остатки этапа 12 (закрыто 2026-09-10):** доки под этап 12 (real-reclassify, maintenance-migrate, без
демо), Prometheus alert rules (`deploy/observability/prometheus-rules.yml` + провижининг), устранена гонка
`FreeTcpPort()` в тест-харнессе (единый `TestPort`), SSE `cards_reclassified` (бэк+фронт), нагрузочные
скрипты `scripts/loadtest/`, скан уязвимостей — **чисто** (core: 0 уязвимых пакетов; frontend `npm audit`: 0).
- Прочее (требует владельца/кредов): биллинг/провайдер планов и саморегистрация; мультиаккаунтность Telegram;
k8s/Cloudflare; Kafka; экспорт/импорт ML; переключатель языка/второй язык (в бэклоге — по потребности);
реальный Telegram-вход и живые LLM-вызовы;
legacy-прототип `docker-compose.yml` перенесён в `archive/leadradar-legacy/` (2026-09-10).
- **Добивка по ТЗ (2026-09-10, автономно)** — закрыты найденные аудитом частично/незакрытые пункты:
ML-проверка на канале/сообщении (`/api/ml/candidates`+`/apply` — реальные, не заглушки); глобальные
исключения до ML/ИИ (§5.14); новые группы фильтров колонки `levels/locations/types/prices` (§6.3);
«открыть исходник» как быстрое действие на карточке (§6.6); глубины очередей/сессии в `/api/operator/health` (§10.2);
детектор подозрительной активности `/api/operator/analytics/suspicious` (§10.5); раздел настроек и
поддержка тем «Внешний вид» — тёмная (дефолт) / светлая / системная (§8.12). Отчёт аудита:
`docs/superpowers/reviews/2026-09-10-tz-compliance-audit.md`. Core-тесты **1275/1275**; фронт build+`lint:i18n` зелёные.
- **Telegram-ключи — вариант A (2026-09-10, по решению владельца):** `api_id`/`api_hash` задаёт
**оператор глобально** (таблица `public.global_settings`, ручки `GET/PUT /api/operator/settings/telegram-keys`,
hash шифруется; раздел «Telegram» в оператор-консоли). У тенанта ключи убраны — только подключение
аккаунта; без ключей подключение недоступно (`/api/tg/status → keysSet:false`). Миграция `GlobalSettings`
**применена**; `PUT` поддерживает частичное обновление (можно сменить одно поле). Core-тесты **1275/1275**.
## Code-quality rework (2026-09-08, после ревью 5 зон ~1100 файлов)
Проведено многоосевое ревью (безопасность/корректность/архитектура/перф) бэкенда и фронта; findings —
`docs/superpowers/reviews/2026-09-08-code-quality-review.md`. Все исправления закрыты и проверены:
**core 1135/1135, telegram 118/118, ai 52/52, ml 38/38**, фронт build OK, 4 sln 0/0. Главное:
- **Безопасность**: SSRF-гейт (baseUrl каталоговых провайдеров фиксирован + private-IP-блок в проверке),
fail-closed Production (rate limit/CORS/conn-string обязательны), код инвайта в аудите — SHA-256, пароль ≥8,
маска ключа не перезаписывает ключ и короткие секреты скрыты, TenantId = 32-hex, mTLS fail-closed,
gRPC-лимиты входных данных, join проверяет целевого тенанта, атомарный инкремент токенов (Npgsql).
- **Корректность**: атомарные append комментариев/ссылок/файлов (1 SQL), уникальный objectKey, атомарный
дедуп-pump, запрет move из trash/archive/taken, логирование «немых» catch, очистка сессий вне hot-path;
фронт: смена пароля с текущим паролем, boot не падает от одного 500, автосейв не затирает промпты,
гонки поиска закрыты seq-токенами.
- **Архитектура**: `Deal.Grpc.Hosting` (общая обвязка 3 сервисов), `TenantSettingsSnapshot` (9 копий чтения
настроек → одна), декомпозиция 7 крупных файлов на partial (<350 строк), фронт store.js → слайсы `store/`,
вынесены компоненты Settings/Discovery; мёртвый код удалён.
- **Реестры констант (C35, 2026-09-09)**: общие `Deal.Contracts.Integrations.MlLearningLabels`
(spam/t:hire/t:order) и `SourceDefaults` (DefaultHue) вместо дублей в 5 модулях; единый предикат «активные
правила» (Kanban `ColumnRules.HasActiveRules`); реестр id-стадий `ProjectStages` (с этапа 9 — `CardsDefaultContainers`); общий
`CardsService.JustNowLabel`; TTL-эвикция в DiscoverySearchErrorCounter (+4 теста, core 1139).
- **Заделы** (не рисковали без e2e/не успели): вынос оставшихся вкладок SettingsView, Optional-пункты
(пагинация колонок, виртуализация, LRU-кэши WTelegram и др.). Подробности — в отчёте ревью и
`.superpowers/sdd/deal-stage8-quality-rework/`.
## Процесс (обязательства, чтобы не жрать память/хосты)
- Acceptance-серверы — только через враппер с гарантированным kill (taskkill //T //F по PID-файлу) +
проверка освобождения порта; в конце каждой приёмки — шаг очистки.
- Контейнеры — по требованию (`docker compose ... up|down`); между заходами ничего не держать;
`scripts/cleanup-dev.sh` (kill висящих Deal.*/тест-хостов, `dotnet build-server shutdown`,
остановка deal-контейнеров) — в конце захода.
- **Хвостов не оставлять (правило владельца, 2026-09-10):** по завершении работы все сервисы и процессы
должны быть остановлены — включая Docker-контейнеры и dev-серверы (frontend/Vite, dotnet) — если
владелец явно не попросил оставить их запущенными. Проверка в конце: `docker ps` без `deal-*`,
свободные порты (5173/5080/5082/5101/5102/5103/5433/9000/9001/9464), `dotnet build-server shutdown`.
- **Разовые решения — одним списком в начале (правило владельца, 2026-09-10):** все вопросы, требующие
выбора владельца, собираются и задаются **сразу, до начала работы**, а не по ходу/в конце. **Не
спрашивать о том, что уже определено ТЗ/принятыми решениями** — это делать без вопросов; вопрос —
только при реальном противоречии в требованиях. При неоднозначности без противоречий — выбирать
безопасный обратимый дефолт и делать (напр. перенос, а не удаление).
- **Легаси-прототип LeadRadar** перенесён из корня в `archive/leadradar-legacy/` (2026-09-10; обратимо,
на сборку/запуск не влияет).