Files
Deal/docs/superpowers/reviews/2026-09-10-tz-compliance-audit.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

45 KiB
Raw Blame History

Аудит соответствия ТЗ «Дейл (Deal) — новая архитектура»

Исторический документ (аудит соответствия ТЗ, 2026-09-10). Актуальное состояние — docs/superpowers/STATUS.md и docs/technical/Техническая-документация-Дейл.md.

Дата: 2026-09-10 Проверяется: docs/spec/ТЗ-дейл-новая-архитектура.md (§1–§12) + расширенные требования этапов 8–12. Метод: только исходный код и артефакты репозитория (C:\telbase). Док-документам на слово не верим — каждое утверждение подкреплено файлом/символом. Единственное запущенное — линтер npm run lint:i18n (быстрый, read-only); остальное не запускалось. Проект не git; правок кода/доков не вносилось, создан только настоящий отчёт.

Сводка

Статус Кол-во
реализовано 131
⚠️ частично 12
отсутствует 1
Всего проверено пунктов 144

Топ-находок — в разделе «Найденные пропуски/расхождения». (Каждая строка таблицы = один проверяемый пункт ТЗ/расширенных требований.)


§1. О продукте

Требование Статус Доказательство
1.1 Приём сообщений из источников в реальном времени src/telegram-service/Deal.Telegram/Dialogs/RealtimeListener.cs (PushMessage + mark-read), Hosting/RealtimeMonitorService.cs
1.2 Отсев мусора (реклама/скам/служебное/дубли/устаревшее) PipelineRejectConstants.cs (stage labels stop/spam_ml/spam_ai/filter_ai/dup/stale), IncomingRules.cs, PipelineWorkerService.Checks.cs
1.3 Структурирование в карточки по профилю (сфера/стек/бюджет/локация) Pipeline/AiCardMapper.cs, Parse/LocalFieldsParser.cs, PipelineCardWriter.cs
1.4 Раскладка по колонкам-фильтрам Kanban/ColumnRules/ColumnRules.cs, CardsService (ContainerAccepts)
1.5 Самообучение на действиях (ML) Kanban/CardsService.Operations.cs (PushAsync на move/trash/restore), MlOutboxFlushScheduler
1.6 Discovery — поиск/подключение источников Deal.Modules.Discovery/*, DiscoveryWorkerService.Search/Evaluate/Join

§2. Термины

Требование Статус Доказательство
2.1 Тенант владеет схемой БД/настройками/ML Data/TenantContext.cs, модель на тенанта (TenantDb миграции), модель ML per-tenant (ml.proto, data/ml/<tenantId>.sqlite)
2.2 Аккаунт Telegram (1 на тенанта) Telegram/Sessions/TenantSession.cs («1 аккаунт на тенанта»)
2.3 Источник (канал/группа/чат) Deal.Modules.Telegram/Application/ITelegramStore.cs, DialogEntity
2.4 Сырое сообщение → очередь Pipeline/Application/Models/QueuedMessage.cs, PipelineIngestService.cs
2.5 Карточка — ядро + модули Deal.Modules.Cards/Application/Card.cs, интерфейсы IContentCard/IBudgetedCard/IContactCard/IFileCard/ITzCard/IRemindableCard/…
2.6 Типы источника (локально/ссылка/файл/Telegram/импорт/API/ИИ/составной) Deal.Modules.Cards/Application/ILocalSource.cs, IWebSource.cs, ITelegramSource.cs, IApiSource.cs, IFileSource.cs, IRowSource.cs, IAiSource.cs, ICompositeSource.cs
2.7 Контейнер + политика Kanban/Application/Models/ContainerPolicyDto.cs, ContainersService.cs
2.8 Отсев с причиной Pipeline/Application/Models/RejectedItemDto.cs, PipelineProcessingService.Rejected

§3. Роли и доступ

Требование Статус Доказательство
3.1 Оператор: тенанты/инвайты/лимиты/health/impersonation с аудитом Endpoints/Operator*, OperatorTenantsEndpoints.Impersonate, AuditEvents.ImpersonationStarted/Stopped
3.2 Тенант: вход по инвайту, пароль, TG-аккаунт, обработка, дашборд Tenants/Application/JoinService.cs, AuthService.cs, Endpoints/JoinEndpoint.cs
3.3 Регистрация только по инвайту IInviteStore, InviteCodeGenerator (16 симв., 72 ч), публичной регистрации нет
3.4 Логин email+пароль, email уникален в SaaS AuthService, users (public), уникальность email
3.5 tenantId — в сессии Models/SessionDto.cs, кука deal_session; JWT не используется (сессии) — допустимо формулировкой «сессии/JWT»
3.6 Вход оператора изолирован от тенантов Configuration/OperatorCookieOptions.cs (deal_operator_session), OperatorAuthEndpoints

§4. Подключение Telegram-аккаунта

Требование Статус Доказательство
4.1 Оператор глобально задаёт api_id/api_hash ⚠️ Ключи хранятся в настройке тенанта tgKeys (SettingsKeys.TgKeys, Deal.Api/Telegram/TelegramKeysService.cs) и задаются в UI тенанта (settings/TelegramTab.vue). Глобальной (операторской) настройки/ручки нет — расхождение с §4.1/§8
4.2 Подключение: QR или телефон+код TelegramTab.vue (qr/phone/code/password), TelegramEndpoints (start-qr/start-phone/submit-code/password), TenantSession.StartQrAsync
4.3 Сессия сохраняется, статус подключения показан Sessions/SessionStore.cs, SessionFileCipher.cs (AES-GCM), TgStatusService, GET /api/tg/status
4.4 1 аккаунт на тенанта (схема допускает расширение) TenantSession (один на тенанта), SessionFarm
4.5 Список диалогов подтягивается при подключении и обновляется на экране + в фоне Dialogs/RealtimeSweep.cs (SyncDialogs каждые 30 с), TelegramEndpoints /dialogs/refresh, ChannelsView.vue
4.6 Вкл/выкл мониторинга по источнику DialogsService.SetMonitorAsync, TelegramEndpoints /dialogs/{id}/monitor
4.7 «Новый чат → мониторинг автоматически» (вкл/выкл) SettingsKeys.AutoMonitorNew, DialogsService.SyncFromTelegramAsync, TelegramStore.SyncFromTelegramAsync
4.8 Удалённые/покинутые источники исчезают TelegramStore.SyncFromTelegramAsync (удаление отсутствующих)
4.9 «Перечитать»: догон ~10 сообщений включённых источников, анти-бан-паузы Dialogs/BackfillService.cs (MessagesLimit=10, паузы 1.53 с / 36 с), POST /api/tg/dialogs/backfill-all
4.10 Полученные сообщения сразу помечаются прочитанными RealtimeListener.OnMessageReceivedAsync (MarkReadAsync после Push), BackfillService (read-ack)
4.11 Discovery: задача поиска → ИИ ключевые слова DiscoveryEndpoints (generate-keywords), IAiTools.GenerateKeywordsAsync
4.12 Поиск каналов, где аккаунт не состоит DiscoveryWorkerService.Search.cs, IsDialogMonitoredAsync
4.13 Каскад: участники → язык → содержание (порог ≥40%) DiscoveryWorkerService.Evaluate.cs, SettingsDefaults.DiscEvalThreshold = 40
4.14 Кандидаты «на рассмотрение» с метаданными/fit/темами/метками (закрытая группа) DiscoveryWorkerService.Constants.cs (MarkClosedGroup…), FinishReviewAsync, Models/DiscoveryTopicDto
4.15 Действия: вручную «Вступить» / авто-вступление с квотами (50/сутки, 50–70 с) DiscoveryBanGuard.cs (DiscJoinLimit=50), DiscoveryPacer.cs (DiscJoinDelayMin/Max=50/70)
4.16 «Отклонить» → чёрный список; список исключает во всех задачах; снимается вручную DiscoveryBlacklistService.cs, DiscoveryBlacklistList.vue, RemoveBlacklistAsync

§5. Обработка входящих (пайплайн)

Требование Статус Доказательство
5.1 Путь: источник → очередь → стоп-лист → дедуп → ML → ИИ → карточка PipelineWorkerService.Pump.cs, SignificantPath, PipelineIngestService
5.2 Этап 1: минимальная длина текста IncomingRules.Evaluate (KindLength), SettingsDefaults.MinLen=24
5.3 Этап 1: стоп-фразы (настраиваемый список) SettingsKeys.StopPhrases, IncomingRules (KindStop), settings/StopTab.vue
5.4 Этап 1: отсев резюме соискателей (настройка) SettingsKeys.BlockResumes/ResumeMarkers, IncomingRules (KindResume, guard «резюме» при маркере найма)
5.5 Этап 1: тип заявки (только вакансии / только заказы) SettingsKeys.WantedType, IncomingRules (KindType), hireMarkers
5.6 Этап 1: дедуп (нормализованный хэш) Parse/DedupHasher.cs, DedupEntries (миграция TenantPipeline)
5.7 Этап 1: устаревшее сообщение → отсев PipelineWorkerService.Checks.cs IsStaleAsync (ArchiveAfterDays)
5.8 ML: уверена → решает сама (спам/колонка); не уверена → ИИ PipelineWorkerService.Pump.cs (run.MlEnabled && !force), ml.proto (take/label/margin)
5.9 Возврат из отсева (force) идёт мимо ML к ИИ Pump.cs (force пропускает ML), PipelineProcessingService.ReturnAsync (Force = true)
5.10 ИИ-фильтр: не про заявки → отсев; выключатель aiFilterEnabled Pump.cs, SettingsKeys.AiFilterEnabled, AiFilterResultDto.Skipped
5.11 Классификация: структурированный разбор (компания/формат/задача/требования/плюсы/условия/бюджет/стек/контакты/тип) Parse/ParsedCardContent.cs, AiCardMapper.cs, ai.proto ClassifyReply
5.12 Назначение колонки с проверкой правил ContainerAccepts, AiCardLearning.cs, ColumnRules.cs
5.13 Глобальный фильтр «без суммы» отдельно для вакансий и заказов SettingsKeys.BudgetRequiredHire/Order, PipelineWorkerService.Checks.cs SkipNoBudgetAsync
5.14 Глобальные исключения по ключевым словам/технологиям/бюджету/локации ⚠️ Глобальных настроек-исключений нет: в SettingsKeys только StopPhrases (стоп-фразы) и per-column exclude (ColumnExclusions.cs). Исключений «ключевые слова/технологии/бюджет/локация» отдельного глобального уровня не найдено
5.15 Карточка — одна строка одной таблицы Cards; ProjectCards упразднена Миграция TenantUnifiedCard.cs (DropTable("ProjectCards") + AddColumn StackJson/LinksJson/FilesJson/HistoryJson/TzText/Reminder…)
5.16 Комментарии — общая таблица LeadComments Миграция TenantKanban.cs (LeadComments), KanbanStore.Comments.cs
5.17 Единый реестр контейнеров; пространства не пересекаются; «взять в работу» = смена контейнера ContainerSpaces.cs, ContainersService.cs, CardsService.Selected.cs (TakeAsync)
5.18 Исходное сообщение хранится и доступно (открыть в Telegram / форматированно) CardDrawer.vue (sourceMsg, tgSourceUrl, renderSourceMessage), ProcessingView.vue

§6. Дашборд (канбан)

Требование Статус Доказательство
6.1 Колонки: «Неразобранное», пользовательские, «Архив», «Корзина» CardIds (inbox/archive/trash), ContainerKinds, KanbanColumns
6.2 Пользователь создаёт колонки; ИИ предлагает с обоснованием; принять/отклонить/переименовать AiSuggestEndpoints, SuggestHeuristics.cs, ContainerColumn.vue (acceptSuggestedBoard, suggested badge)
6.3 Колонка = сложный набор фильтров (ключевые слова/стек/грейд/уровень/цена/бюджет/локация/тип + отрицательные) ⚠️ ContainerRulesDto содержит только Mode/Direction/Keywords/Stack/Grade/Exclude/Budget. Отдельных групп «уровень/цена/локация/тип» нет (частично покрыты direction/keywords); отрицательные — exclude
6.4 При помещении указаны критерии попадания ColumnRules.ComputeHits, MatchHitBuilder, MatchHitDto
6.5 Свежие сверху; drag&drop между колонками с обучением ML KanbanStore.Cards.cs (OrderByDescending(ReceivedAt)), composables/dnd.js, PushAsync on move
6.6 Быстрые действия: комментарий, корзина, контакт, «открыть исходник» ⚠️ Комментарий/корзина/контакт — Card.vue (кнопки). «Открыть исходник» на самой карточке нет — только в CardDrawer.vue и ProcessingView.vue
6.7 Виджеты-счётчики свёрнутых колонок; двигать/менять размер Sidebar.vue, cards.js (cycleWidth, colExtra, reorder), COLUMN_WIDTHS
6.8 Архив: старше N дней (1–30), очистка через 90 дней StorageTickService.cs, SettingsDefaults.ArchiveAfterDays=14 (кламп 1..30), ArchiveClearDays=90
6.9 Корзина: очистка раз в 7 дней; возврат из архива/корзины SettingsDefaults.TrashClearDays=7, CardsService.Operations.cs (RestoreCardAsync)
6.10 «Выбранные»: стадии Запланировано→…→Готово/Отложено CardsDefaultContainers.cs (planned/reply/agree/work/review/ready/hold)
6.11 «Взять в работу» — переход в контейнер, не клон CardsService.Selected.cs TakeAsync
6.12 Модули работы: комментарии/сумма/стек/контакты, ссылки, ТЗ, файлы (S3/MinIO), значки количества CardsService.Files.cs, CardFileKind.cs, FileKindDetector.cs, CardDrawer.vue
6.13 Отложенные: напоминания (срок+время, календарь); выключатель; выключено → не срабатывают CardsService.Reminders.cs (RemindersDisabledDetail, snooze +24 ч), HoldReminderDialog.vue, SettingsDefaults.RemindersEnabled
6.14 История движения — под спойлером CardDrawer.vue (<details> «История движения», historyReversed)
6.15 Ручное создание карточки (пометка «создано локально») CardDetailsEndpoints POST /api/cards, Local флаг, Card.vue/CardDrawer.vue бейдж «Локальная»
6.16 Терминальные зоны «Отклонено»/«Выполнено»; в архив/корзину дашборда не попадают CardsDefaultContainers.finished/rejected (terminal), ContainerPolicyDto.IsTerminal, ClearRejected

§7. Вкладка «Обработка»

Требование Статус Доказательство
7.1 Очередь (этап 1 / ожидают ИИ) с автопрокруткой ProcessingView.vue (таймер-опрос ~2.6 с, статусы etap-1-bez-ii/ozhidaet-ii); «автопрокрутка» реализована как авто-обновление
7.2 Отсев с причиной и источником решения (правила/ML/ИИ/система) + конкретная фраза PipelineRejectConstants.cs (StageLabels/SourceLabels), RejectedItemDto (kw, reason)
7.3 Метаданные, «Открыть исходник», «Исходное сообщение (форматированно)» ProcessingView.vue (metaRows, sourceUrl, srcHtml)
7.4 Полнотекстовый поиск по отсеву PipelineEndpoints /rejected?q= (FTS LIKE), Store поиск
7.5 Возврат из отсева: причины игнорируются, ML/ИИ обучаются, причина возврата PipelineProcessingService.ReturnAsync (Force=true, PushAsync(spam,1.0), returnReason)
7.6 Автоочистка отсева раз в 3 дня; ручная очистка PipelineRejectConstants.RetentionDays=3, POST /pipeline/rejected/clear, DELETE /rejected/{id}
7.7 Счётчик обработки в боковой панели; отсев в панели не показывается Sidebar.vue (state.pQueueCounts.total), отсев — только внутри ProcessingView.vue

§8. Настройки тенанта

Требование Статус Доказательство
8.1 Telegram: ключи приложения (оператор), подключение, авто-мониторинг ⚠️ Подключение/авто-мониторинг (TelegramTab.vue, AutoMonitorNew). Ключи — настройка тенанта tgKeys, а не глобальная операторская (см. §4.1)
8.2 ИИ: провайдер (в т.ч. локальные), модель, ключ зашифрован AiProviders.cs, SettingsService.PatchSecrets.cs (enc:), ISecretCipher
8.3 Промпты: базовый + свой; библиотека по сферам + «мои промпты» PromptLibraryModal.vue (PROMPT_LIBRARY/PROMPT_CATEGORIES, поиск), SettingsService.PatchMyPrompts.cs, AiTab.vue
8.4 ИИ вкл/выкл; ИИ-фильтр вкл/выкл SettingsKeys.AiEnabled/AiFilterEnabled, Pump.cs
8.5 ML: вкл/выкл, обучение на действиях, проверка на сообщении/канале, сброс, самооценка ⚠️ mlEnabled, обучение (PushAsync), predict (сообщение) , сброс (/api/ml/reset), самооценка (MlEvalDto). Проверка на канале не реализована: POST /api/ml/candidates возвращает пустой список (заглушка), POST /api/ml/apply — всегда 404 (MlEndpoints.cs:130155)
8.6 Обработка: стоп-фразы, длина, резюме, тип, домен/ключи, маркеры найма/заказа SettingsKeys.StopPhrases/MinLen/BlockResumes/WantedType/DomainKeywords/HireMarkers, StopTab.vue/ScopeTab.vue
8.7 Колонки: набор, правила, отрицательные фильтры, исключения ContainersEndpoints, BoardRulesDialog.vue, ColumnExclusions.cs (см. замечание 6.3 по составу групп)
8.8 Валюта: целевая, источник (4 запроса/сутки), конвертация при приёме + пересчёт старых (кроме архива/корзины), USDT=USD RatesService.cs (RatesFetchInterval = 6 ч = 4/сутки; USDT→USD), ConversionRecomputer.cs (ConversionExcludedCols archive/trash)
8.9 Хранение: срок архивации (1–30), очистка архива/корзины StorageTickService.cs, SettingsDefaults.ArchiveAfterDays/ArchiveClearDays/TrashClearDays, StorageTab.vue
8.10 Уведомления и напоминания; отложенные — отдельно NotifyTab.vue, SettingsKeys.RemindersEnabled, CardsService.Reminders.cs
8.11 Звук NotifyTab.vue (soundOn, volume, testSound), utils.js (Web Audio) — клиентская настройка, без серверного ключа
8.12 Внешний вид В SettingsView.vue вкладок Telegram/AI/Storage/Stop/Scope/ML/Notify/Currency/Profile — раздела «Внешний вид» (тема/оформление) нет; style.css содержит единственную тёмную тему

§9. Лимиты (бюджет токенов)

Требование Статус Доказательство
9.1 Бюджет токенов на LLM, период настраивается TenantLimitDto (BudgetTokens, Period month/day), OperatorLimitUpdateRequest
9.2 ai-service оценивает вызов в токенах, списывает с бюджета TokenUsageRecorder.cs, BudgetedAiClassifier.cs, BudgetedAiTools.cs, ai.proto Usage
9.3 При исчерпании: fallback + уведомление; приём не блокируется BudgetedAiClassifier (Local-фолбэк), Warned80/NotifiedExhausted, условия pipeline не блокируются
9.4 Оператор видит расход и меняет бюджет OperatorLimitsEndpoints (/limits, /tenants/{id}/limit), AnalyticsService

§10. Админка оператора

Требование Статус Доказательство
10.1 Тенанты: создание, инвайты, статус, лимиты, приостановка OperatorTenantsEndpoints (create/suspend/unsuspend), OperatorInvitesEndpoints
10.2 Health всех сервисов и очередей ⚠️ Сервисы (OperatorHealthEndpoints, ml/ai/telegram по gRPC-пробам). «Очереди» в health нет — глубины очередей публикуются только в метриках (Observability/DealMetricsCollector.cs/metrics)
10.3 Аудит: входы/выходы, инвайты, impersonation, действия оператора и тенанта AuditEvents.cs (login/logout/invite/impersonation/card_/container_/settings/channels/telegram), AuditService
10.4 Аналитика: расход токенов (день/тенант/провайдер/модель) + лента действий с фильтрами AnalyticsService.TokensAsync (groupBy), OperatorAnalyticsEndpoints, AuditSection.vue/AnalyticsSection.vue
10.5 Подозрительная активность (по логам безопасности) ⚠️ Отдельного разбора/детектора подозрительной активности не найдено; есть счётчики неудачных входов в AnalyticsService.OverviewAsync (failedLogins) и общие Grafana-дашборды
10.6 Метрики сервисов (Prometheus/Grafana) DealMetricsHosting.cs (/metrics :9464), deploy/observability/prometheus.yml, prometheus-rules.yml, Grafana-дашборды
10.7 UI: #/operator и #/join router.js, views/operator/OperatorConsole.vue, views/JoinView.vue

§11. Нефункциональные требования

Требование Статус Доказательство
11.1 Безопасность: TLS, mTLS между сервисами scripts/mtls-certs.sh, MtlsCertificates.cs, compose.prod.yml (DEAL_MTLS_*), MtlsOptions.cs
11.2 Параметризованный SQL EF Core / Npgsql по всему Deal.Infrastructure; ручной SQL — параметризованный (ExecuteSqlRawAsync без конкатенации)
11.3 IDOR/XSS/SSRF/CSRF IDOR — session+tenant-scope middleware; XSS — renderSourceMessage (экранирование); SSRF — AiConnectionChecker.cs (IsPrivateEndpoint, allowlist AiProviders), CbrRateSource (fixed URL); CSRF — OriginGuardMiddleware.cs + SameSite
11.4 Argon2id DefaultPasswordHasher.cs (Isopoh Argon2, Variant Argon2id)
11.5 Rate limiting (прокси + приложение), счётчики распределённые в БД StoreBackedFixedWindowRateLimiter.cs, IRateLimitCounterStoreRateLimitCounterStore (public.rate_limit_counters), LoginAttemptGuard.cs, RateLimitPolicies.cs
11.6 Cloudflare ⚠️ В коде нет интеграции/конфигурации Cloudflare; edge — Caddy (deploy/caddy/Caddyfile, TLS internal). Требование внешнего периметра, вне репозитория
11.7 Ежедневные бэкапы (Postgres/файлы/сессии), outbox для событий scripts/backup.sh/restore.sh/deal-backup-lib.sh; outbox — MlOutboxQueue.cs, MlOutboxFlushScheduler.cs
11.8 Авто-очистки (retention аудита/лимитов/счётчиков), разлогин suspended DataRetentionScheduler.cs, DataRetentionOptions.cs; AuthService.ResolveSessionAsync (suspended → null)
11.9 Наблюдаемость: логи → Loki, метрики OTel→Prometheus→Grafana + алерты, token_usage_events Logging/DealLogging.cs, deploy/observability/{promtail,loki}.yml, prometheus-rules.yml; миграция AddTokenUsageEvents
11.10 Масштабируемость: модульный монолит + сервисы ml/ai/telegram; k8s позже Deal.Modules.*, отдельные проекты src/{ai,ml,telegram}-service, compose.*.yml; k8s отсутствует (заявлено позже)
11.11 Производительность: без потерь; анти-бан-паузы не блокируют обработку PipelineIngestService/DedupEntries, фоновые PipelineWorkerScheduler/BackfillService, progressive.js
11.12 i18n: строки вынесены, RU по умолчанию, новые языки, переключение на лету с сохранением, форматтеры дат/чисел/валют, фолбэк RU ⚠️ Ядро i18n есть (i18n/index.js, ru.js/ru.data.js, $t), линтер проходит зелёным (проверено: npm run lint:i18n → ✓). Но: нет UI-переключателя языка, нет второго языка и нет сохранения выбораindex.js прямо: «UI-переключателя на этом этапе нет»); даты/числа форматируются жёстко через toLocale*('ru-RU', …) (store/core.js, store/settings.js, fmtNum в store/operator.js), а не через locale-aware i18n-форматтеры

§12. Ограничения и допущения

Требование Статус Доказательство
12.1 Фронтенд Vue 3 + Vite + Tailwind; единый контракт /api/cards + /api/containers с этапа 9 package.json (vue/vite/tailwind), api.js, CardsEndpoints.cs, ContainersEndpoints.cs
12.2 Данные LeadRadar тестовые — не мигрируются Отдельные миграции Deal; данных-миграций из LeadRadar нет
12.3 Kafka, k8s, биллинг-провайдер, саморегистрация — вне рамок В коде отсутствуют
12.4 1 Telegram-аккаунт на тенанта; несколько — позже TenantSession (1 на тенанта)

Расширенные требования (этапы 8–12)

Требование Статус Доказательство
E1 Библиотека готовых промптов по специальностям ru.data.js PROMPT_LIBRARY (IT/дизайн/недвижимость/стройка/услуги/красота/обучение), PromptLibraryModal.vue
E2 Категории и поиск в библиотеке PROMPT_CATEGORIES, фильтр query/cat в PromptLibraryModal.vue
E3 Раздел «Мои промпты» + свой промпт SettingsService.PatchMyPrompts.cs, AiTab.vue (addMyPrompt/removeMyPrompt), лимит ≤100
E4 Двухэтапный стоп-лист: стоп-фразы без ИИ, затем ИИ-фильтр с возможностью отключить Этап 1 IncomingRules (без ИИ); ИИ-фильтр FilterSafelyAsync под AiFilterEnabled
E5 Исключения внутри колонки ColumnExclusions.cs (veto Exclude), BoardRulesDialog.vue
E6 Discovery: поиск/вступление в каналы и группы DiscoveryWorkerService.Search/Join, DiscoveryOps (telegram-service)
E7 Discovery: квоты/интервалы, закрытые группы, темы, список на рассмотрение DiscoveryBanGuard, DiscoveryPacer, MarkClosedGroup, DiscoveryTopicGroup, статус review
E8 «Перечитать каналы»/backfill, пометка прочитанными, мгновенный приём BackfillService.cs (10 сообщений, паузы), read-ack; RealtimeListener.cs
E9 ML отдельным контейнером src/ml-service/Deal.Ml/Dockerfile + compose.dev.yml/compose.prod.yml (ml-service, gRPC :5103)
E10 ML: обучение на действиях пользователя и ИИ Пользователь — CardsService.Operations.cs (PushAsync(…,1.0)); ИИ — AiCardLearning.cs, CardReclassifier.cs (AiPushWeight)
E11 Отдельная настройка проверки ML на сообщении/канале ⚠️ Проверка на сообщении (POST /api/ml/predict, MLPanel.vue); проверка на канале (/api/ml/candidates — пустая заглушка, /api/ml/apply — 404)
E12 Архив/корзина (сроки, возврат, ручная очистка) StorageTickService.cs, CardsService.Operations.cs, clear-col/DELETE, Restore
E13 Напоминания «Отложено» (календарь, отключение) HoldReminderDialog.vue, CardsService.Reminders.cs, RemindersEnabled
E14 История карточки под спойлером CardDrawer.vue <details> «История движения»
E15 Контакты квалифицированные (tg/phone/email/linkedin/site) Parse/ContactsQualifier.cs (типы tg/phone/email/linkedin/whatsapp/site, дедуп, отбой ботов/сервисных ссылок)
E16 «Открыть исходник» CardDrawer.vue (sourceUrl), ProcessingView.vue
E17 Источник не на карточке (только в деталях) Card.vue показывает лишь бейдж «Локальная»/контакты; канал и исходное сообщение — в CardDrawer.vue
E18 Бюджет: диапазон/вакансия/валюта + конвертация (4 раза в сутки) CardBudget.cs, BudgetNormalizer.cs, RatesService.cs (6 ч = 4/сутки), ConversionRecomputer.cs
E19 Обязательность суммы (опционально для вакансий) SettingsKeys.BudgetRequiredHire/BudgetRequiredOrder, SkipNoBudgetAsync
E20 Вкладка «Обработка» (очередь + отсев + причины + поиск) ProcessingView.vue, PipelineEndpoints
E21 Возврат из отсева с обучением PipelineProcessingService.ReturnAsync (PushAsync(spam,1.0), Force)
E22 Оператор-консоль views/operator/* (Tenants/Invites/Limits/Audit/Analytics/Health), router.js
E23 Аналитика токенов AnalyticsService.cs, OperatorAnalyticsEndpoints.cs, token_usage_events
E24 Аудит входов/выходов/действий (этап 10) AuditEvents.cs, AuditService.cs, AuditSection.vue
E25 i18n (вынос строк) ⚠️ Строки вынесены и линтер зелёный, но нет переключателя языка/второго языка/персистентности и locale-форматтеров (см. 11.12)
E26 Метрики Prometheus DealMetricsHosting.cs, SharedKernel/Observability/DealMetrics.cs, prometheus.yml (таргеты 5/5)
E27 Распределённый rate-limit RateLimitCounterStore.cs (Postgres), StoreBackedFixedWindowRateLimiter.cs, миграция RateLimitCounters
E28 reclassify (реальный, этап 12) CardsEndpoints /reclassify и /{id}/reclassify, CardReclassifier.cs (локальный фолбэк), ReclassifyGate.cs, audit card_reclassified

Найденные пропуски/расхождения

Отсутствует

  1. §8.12 «Внешний вид» (настройки оформления). В SettingsView.vue нет вкладки/раздела внешнего вида; тема одна (тёмная, style.css @theme). Отдельной настройки «внешний вид» не найдено.

⚠️ Частично

  1. §8.5 / E11 «проверка ML на канале». POST /api/ml/candidates (MlEndpoints.cs:131141) возвращает {items: []} с комментарием «До этапа 6 telegram-данных нет» — устаревшая заглушка; POST /api/ml/apply (MlEndpoints.cs:144155) всегда отвечает 404 «Исходное сообщение не найдено». Реального разбора сообщений канала/ручного применения решения ML нет, хотя telegram-данные в системе уже есть (проверка на сообщении — POST /api/ml/predict — работает).
  2. §5.14 «Глобальные исключения по ключевым словам/технологиям/бюджету/локации». Глобальных настроек такого исключения в SettingsKeys нет: есть только stopPhrases (стоп-фразы) и per-column exclude (ColumnExclusions.cs). Исключения уровня «технология/бюджет/локация» как общий фильтр не найдены.
  3. §4.1/§8.1 ключи Telegram. Хранятся как настройка тенанта tgKeys (TelegramKeysService.cs) и вводятся в UI тенанта (TelegramTab.vue). ТЗ требует, чтобы api_id/api_hash задавал оператор глобально — глобальной операторской настройки/ручки нет.
  4. §11.12 / E25 i18n. Строки вынесены в словари (i18n/locales/ru.js, ru.data.js), npm run lint:i18n проходит. Но отсутствуют: UI-переключатель языка, второй язык, сохранение выбора, «переключение на лету» (в i18n/index.js явно сказано «UI-переключателя на этом этапе нет»). Форматирование дат/чисел жёстко ru-RU (store/core.js:232276, store/settings.js:351406, store/operator.js:356), не через locale-aware i18n-форматтеры.
  5. §6.3 состав фильтров колонки. ContainerRulesDto = Mode/Direction/Keywords/Stack/Grade/Exclude/Budget. ТЗ перечисляет также «уровень/цена/локация/тип» отдельными опциями — явных групп нет (частично покрываются direction/keywords).
  6. §6.6 «открыть исходник» как быстрое действие карточки. На Card.vue есть комментарий/корзина/контакт, но ссылки «открыть исходник» нет — она доступна только в CardDrawer.vue и ProcessingView.vue.
  7. §10.2 health очередей. /api/operator/health проверяет БД и сервисы ml/ai/telegram, но глубины очередей (пайплайн, MlOutbox) в JSON health не отдаёт — они только в метриках (DealMetricsCollector.cs/metrics).
  8. §10.5 подозрительная активность. Специализированного детектора/ленты подозрительной активности по логам безопасности не найдено; есть лишь счётчик failedLogins в обзорной аналитике и общие Grafana-дашборды.
  9. §11.6 Cloudflare. В репозитории нет конфигурации/интеграции Cloudflare (edge — Caddy, deploy/caddy/Caddyfile). Требование периметра, вне кода приложения.
  10. §7.1 «автопрокрутка» очереди. Реализована как периодическое авто-обновление списка (~2.6 с, ProcessingView.vue), а не как буквальная авто-прокрутка. Семантически покрывает требование, но не дословно.

Дефекты/легаси, замеченные при проверке (не пункты ТЗ, но влияют на заявленные функции)

  1. NotifyTab.vue — сломан список активных напоминаний. const holdReminders = computed(() => state.projectCards.filter(...)) (settings/NotifyTab.vue:68), при этом state.projectCards больше нигде в src/ не определяется (grep даёт ровно одно совпадение — этот файл). После этапа 9 (projectCards/stage упразднены) обращение к state.projectCards.filter даёт undefined.filter → ошибка рендера вкладки «Уведомления».
  2. Легаси-артефакты LeadRadar. В корне остались docker-compose.yml (сервисы app/ml/minio старого стека), каталог backend/ (python app/) и mlservice/ (python). Текущая архитектура — deploy/compose.*.yml
    • src/{core,ai,ml,telegram}-service. Прямого нарушения ТЗ нет, но это риск путаницы (в STATUS.md «судьба legacy docker-compose.yml» помечена как открытый вопрос).

Чего проверка не покрывает

  • Живые внешние интеграции без кредов. Реальный Telegram-вход (api_id/api_hash/QR) и реальные LLM-вызовы не проверялись (нет кредов; см. STATUS.md, п.5 «нужны живые креды»). Проверяется только наличие кода/контрактов и локальных заглушек.
  • Живой контур Docker/k8s, mTLS-рукопожатие, Grafana/Loki/Prometheus. Проверены конфиги (compose.*.yml, deploy/observability/*) и код обвязки, но не факт поднятия/скрейпа в этой сессии (сервисы не поднимались).
  • Скрипты бэкапа/восстановления и нагрузочные тесты. Наличие и читаемость проверены (scripts/backup.sh, scripts/restore.sh, scripts/loadtest/), но не выполнялись.
  • Корректность чисел в тестах. Тест-счётчики (STATUS.md: core 1203 и т.п.) не пересчитывались — тесты не запускались (кроме быстрого lint:i18n).
  • UI-поведение в браузере. Выводы по фронту основаны на чтении .vue/.js; реальные клики, drag&drop и рендер не воспроизводились.
  • Внешний периметр (Cloudflare, TLS в проде, DNS, egress-контроль). Вне репозитория.
  • Соответствие формальным юридическим требованиям/биллингу — вне рамок ТЗ (заявлено как «позже»).

Обновление (2026-09-10, вечер) — статус после добивки

Часть найденных ⚠️/ закрыта в тот же день (детали — .superpowers/sdd/deal-stage12-observability-hardening/task-tz-*.md):

Пункт Было Стало
§8.12 «Внешний вид» раздел настроек + темы тёмная/светлая/системная (§15 техдока)
§8/E11 ML-проверка на канале ⚠️ заглушка MlReviewService (`/api/ml/candidates
§5.14 глобальные исключения ⚠️ excludeKeywords/Locations/Types/Budget* на стоп-этапе
§6.3 группы фильтров колонки ⚠️ levels/locations/types/prices + matchHits
§6.6 «открыть исходник» на карточке ⚠️ быстрое действие в Card.vue
§10.2 health очередей ⚠️ queues/sessions в /api/operator/health
§10.5 подозрительная активность ⚠️ SuspiciousActivityService + /api/operator/analytics/suspicious

Остаются требующими владельца/кредов (осознанно): глобальные Telegram-ключи оператора (§4.1/§8.1), переключатель языка (§11.12 — в бэклоге, по потребности), живые Telegram/LLM-вызовы, Cloudflare/прод-периметр. Итог после добивки: core-тесты 1245/1245; фронт build + lint:i18n зелёные.