N-4: SendNewLeadsDigestJob выбирал сделки received_at>now()-30min без маркера
отправки → ручной/повторный прогон (R3b велит дёргать вручную) дублировал
письмо-дайджест. Окно «непересекается» только при ровно-30-мин прогонах.
Фикс без схемы: идемпотентность по сделке через Redis SETNX
(Cache::add 'digest_sent:<id>', TTL 1 сутки) — паттерн как rate-limit
ZeroBalancePausedMail. Уже отправленная сделка в дайджест повторно не входит.
TDD: тест «повторный прогон НЕ дублирует» (RED 2 письма → GREEN 1), сюит 5/5.
R3b DIGEST-ON + owner-decisions + NEW-статус обновлены (N-4 → закрыто).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Ревью раунда 1 (полнота): инвариант §20.2 не имел шага ни в одном плане.
Добавлен Шаг 9-4: вечерний BalancePreflightSweepJob @18:00 смотрит на ЗАВТРА
(тенант мёрзнет с «+» балансом), разморозка снимает только наши паузы
(paused_at >= frozen_at_was), ручные паузы клиента сохраняются. Код-факты
сверены по BalancePreflightSweepJob.php:62/70-85/97-108 + routes/console.php.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
R5 teardown/сверки (сверено по db/schema.sql):
- N-1 (CRITICAL): TD3 падал — supplier_lead_costs БЕЗ tenant_id (→ через deal_id,
до deals) + DELETE FROM reminders по дропнутой таблице (убрано). Прежде вся
транзакция откатывалась → тест-данные оставались на боевом.
- N-2: + DELETE pd_processing_log/tenant_operations_log (по tenant_id) + нота про
orphan-таблицы инъекции без tenant_id (чистить по манифесту R2).
- N-3: нота — НЕ удалять глобальные auth_log/saas_admin_audit_log (разрыв
hash-цепочки → F3 красный); per-tenant цепочки рвутся безопасно.
- N-8: F4 маржа — supplier_lead_costs.cost_kopecks→cost_rub + JOIN deals;
lead_charges.price_kopecks→price_per_lead_kopecks (ревьюер ошибся, сверено).
R2: N-5 — предупреждение про snapshot:rebuild (DELETE без фильтра тенанта/транзакции,
снесёт слепок дня вкл. lkomega); по умолчанию backfill с явной --date.
R1: N-7 — предусловие PR2: гейт реквизитов (422 requisites_required) + preflight
баланса (409) ДО создания проектов через портал.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Закрыты doc-находки двух ревью-сессий (точность планов перед прод-прогоном):
- GAP-1: Татарстан 16→19 (16=Мордовия, конституц. порядок ст.65, НЕ ГИБДД) —
свод, план №3 (C-2), PR2 (сноска: P5 [16]=Мордовия, не Татарстан). Сверено
по RussianRegions.php.
- M-2/GAP-3: капча = NullCaptchaVerifier БЕЗУСЛОВНО (не только local) — план №18.
- lpimp-status: lpimp_ → 401 на биллинг/api-keys (не 403), 403 только на admin,
/api/billing/charges читается — план №25.
- N-6: воронка статусов НЕ форсится (любой валидный slug, нет state-machine) — план №13.
- №23-tx: импорт пишет 1 нулевую historical_import строку; сверять баланс/lead_charges,
не число balance_transactions — план №23.
- append-only: lead_charges защищён GRANT'ом (prod/crm_app_user), не триггером →
на dev-superuser правка пройдёт (ложный GREEN); гонять на проде — план №12.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
G2-B (19.06) флипнул new_lead.email false→true — дайджест по умолчанию ВКЛ.
Докблок NotificationService ссылался на устаревший дефолт (email:false) и
старую строку schema.sql:699. Поправлено на канон-схему (email:true).
Только комментарий, без изменения логики.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Read-only разбор: код-факты денег/изоляции/распределения сверены построчно.
Блокеры до старта: Татарстан=16 неверно (19), корпус untracked, капча-Null.
Новые HIGH вне реестра: админ fail-open (=F-T2), капча отключена.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Мастер-документ точки входа для свежей сессии: тест на боевом liderra.ru,
порядок R1-R5, карта 26 планов, безопасность, эфир, вердикт GO/NO-GO.
Планы 24 помощь, 25 имперсонизация, 26 публичный API.
Поправка: F-DIGEST снята — была ошибкой чтения устаревшего комментария,
не отставанием локалки (она байт-в-байт с боевым 01a9029c). Канон-схема
db/schema.sql:796 new_lead.email:true — дайджест вкл по умолчанию. План 20
и реестр обновлены. Тесты — только на проде.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Реестр находок (сверка свода с кодом под планы):
F-CSV экспорт без защиты от CSV-инъекции (безопасность, под фикс),
F-DELPROJ удаление проекта со сделками запрещено (свод неточен),
F-DIGEST дефолт email-дайджеста по схеме выключен,
F-PDF штатность PDF-формата не подтверждена,
F-REMIND фронт-хвосты снятой фичи напоминаний,
F1-F5 прежние прогонные (F3 закрыта).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Пошаговые планы было→ожидали→стало с код-фактами file:line для пунктов:
PR2 проекты, PR3 балансы, 01 распределение CAP3, 03 каскад региона,
05 идемпотентность, 07 тройная запись, 08 тариф-ступень,
09 нехватка→откат, 10 два проекта одно списание,
13 сделки лента/карточка/статусы, 14 ручная/массовые/экспорт.
NB 14: защита экспорта от CSV-инъекции в коде не найдена — помечено как находка под проверку на прогоне.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Переработка register под новый бэкенд SP1 (код на почту), новый ConfirmEmailView, капча-шов, роут /confirm-email. Проверено Playwright: register→код→confirm→dashboard, негатив, fallback email. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Полный DAST active scan локальной копии 127.0.0.1:8000 через OWASP ZAP 2.17.0.
Сводка: High 1, Medium 4, Low 28, Info 7. Реальных high/critical — 0:
- High «Cloud Metadata Exposed» — false-positive: SPA отдаёт 200 на любой путь,
evidence пуст, nginx нет, SSRF закрыт WebhookUrlGuard.
- 4 Medium — отсутствие security-заголовков локально; на проде их шлёт nginx.
Вердикт ZAP active scan: GO. Скрипт-оркестратор воспроизводим.
Сырые json и html — локально в docs/security, не коммитятся.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Убраны дубли HTTP-заголовков. nginx уже шлёт enforcing CSP, X-Frame-Options,
X-Content-Type-Options, Referrer-Policy, HSTS, Permissions-Policy, COOP, CORP
через add_header always. App-уровневый middleware SecurityHeaders дублировал
четыре из них и слал лишний CSP Report-Only; на проде add_header always плюс
PHP-заголовок давали дубль в ответе.
- удалён middleware SecurityHeaders и его регистрация в bootstrap/app.php
- SecurityHeadersTest переписан: фиксирует, что приложение эти заголовки не ставит
Прод-дедуп вступит в силу после деплоя. Verify локально 4 из 4 green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Пошаговый деплой-пайплайн liderra.ru: clone из gitea, npm build на проде,
artisan down, rsync overlay с исключениями, composer и optimize от www-data,
миграции через postgres superuser, up и smoke. Грабли PowerShell-ssh кавычек,
heredoc с dollar-dollar, привилегии www-data и crm_app_user, rollback.
GitHub Actions deploy.yml мёртв, аккаунт CoralMinister suspended.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>