Commit Graph

13 Commits

Author SHA1 Message Date
Дмитрий 7f5ff874a8 phase2(notifications-stage2a): in_app_notifications + notifyInApp (schema v8.10)
P0 этап 2a — backend-фундамент bell-icon канала. UI bell + API endpoints
— этап 2b отдельным коммитом.

Schema v8.9 → v8.10:
- Новая таблица in_app_notifications (после reminders в schema):
  id/tenant_id/user_id/event/title/body/deal_id/payload/read_at/created_at.
- 2 индекса: unread (user_id, created_at DESC) WHERE read_at IS NULL
  + recent (user_id, created_at DESC).
- RLS tenant_isolation.
- Метрики: 55→56 таблиц, 93→95 индексов, 36→37 RLS.
- CHANGELOG_schema.md +§T.

Backend:
- App\Models\InAppNotification — Eloquent с UPDATED_AT=null.
- NotificationService::notifyInApp — INSERT через DB::transaction + SET
  LOCAL app.current_tenant_id для RLS. Throwable + Log::warning.
- notifyNewLead шлёт два канала параллельно: email + inapp.

Pest +11 (291/291 за 32.94 сек, 1060 assertions):
- inapp=true/false; schema-default (inapp=true в схеме);
- 2 user'а получают / inactive не получает / RLS изоляция;
- дубль Биз-19 / повторный vid / оба канала / payload deal_id;
- notifyInApp напрямую с reminder.

PHPStan baseline регенерирован. IDE-helper для InAppNotification.
cspell-words: +inapp.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 11:17:39 +03:00
Дмитрий f0dce283a8 phase2(soft-delete): schema v8.9 + DELETE /api/deals (этап 5/5 — авто-план закрыт)
Bulk soft-delete для UI applyBulkDelete. Hard-delete отбракован из-за
CASCADE-FK от webhook_dedup_keys: hard уничтожил бы dedup-ключи и
нарушил идемпотентность webhook §5.5.

Schema v8.8 → v8.9:
- deals.deleted_at TIMESTAMPTZ (NULL = живая).
- Partial index (tenant_id, status) WHERE deleted_at IS NULL —
  самый частый UI-фильтр.
- ALTER TABLE на партиционированной deals distributes во все 6
  партиций автоматически (PG 14+).
- CHANGELOG +§U с обоснованием soft vs hard.

Backend (DealController::destroy):
- DELETE /api/deals {tenant_id, ids: [1..1000 ints]}.
- Bulk-update deleted_at=NOW() через RLS + defense-in-depth where(tenant_id).
- ActivityLog event=deal.deleted (source='bulk') для каждой ИЗМЕНЁННОЙ.
- NO-OP (уже удалена) не пишет audit.
- Deal model: SoftDeletes trait + deleted_at в fillable/casts. Global
  scope автоматически добавляет whereNull('deleted_at') ко всем существующим
  query (index/show/transition/update/export).

Pest +8 (DealDestroyTest):
- 422/404 базовые / soft-delete + audit / defense-in-depth (свой
  удалён, чужой жив) / NO-OP idempotency / GET скрывает soft-deleted
  (list+show 404) / 422 пустой массив.
- Quirk: migrate:fresh --env=testing без .env.testing использует liderra
  вместо liderra_testing → решение DB_DATABASE=liderra_testing migrate:fresh.

Frontend:
- dealsApi.bulkDeleteDeals — DELETE-helper с config.data (axios особенность).
- DealsView::applyBulkDelete async: optimistic local-removal +
  bulkDeleteDeals если auth.user; success → toast «Удалено N из M.»;
  fail → warning toast + локальный update НЕ откатывается.

Vitest +3 (DealsListIntegration):
- bulkDeleteDeals с tenant_id + optimistic + toast.
- Без tenant_id — НЕ вызывается.
- Reject → warning toast + локальный update остаётся.

PHPStan baseline регенерирован.

АВТО-ПЛАН (5 этапов) ЗАКРЫТ ПОЛНОСТЬЮ.

Регресс:
- Lint+type-check+format passed.
- Vitest 308/308 за 20.12 сек (+3 от 305).
- Vite build 973 ms.
- Pint + PHPStan passed.
- Pest 256/256 за 27.75 сек (+8 от 248, 977 assertions).

Реестр v1.68→v1.69 / CLAUDE.md v1.59→v1.60.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 09:51:47 +03:00
Дмитрий 83bb9de2bb phase2(backend-completion): POST /api/deals + webhook_hmac_required + POST /api/deals/export
3 backend-completion после tightening v1.56.

(1) POST /api/deals — manual create endpoint:
- DealController::store. Project firstOrCreate (type='manual'). Deal с
  source_crm_id=NULL. RLS-обёрнутая транзакция.
- Manual НЕ списывает баланс / НЕ дедуп / НЕ SupplierLeadCost.
  ActivityLog с context.source=manual.
- NewDealDialog получил optional tenantId prop. С tenantId — POST → backend-id;
  на error fallback на local-id + warning + dialog open.
- DealsView/KanbanView передают auth.user?.tenant_id.
- Pest +8.

(2) webhook_hmac_required flag в system_settings:
- Seed-row в db/schema.sql (default false backward-compat).
- WebhookReceiveController::isHmacRequired private helper.
- При true: запрос без X-Webhook-Signature → 401.
- Pest +3.

(3) POST /api/deals/export — backend CSV:
- DealController::export. Валидация ids[1-10000]. RLS-обёрнутый whereIn.
- Excel-friendly CSV: BOM "\u{FEFF}" PHP-литерал, ; разделитель, \r\n.
- text/csv attachment headers.
- Frontend applyBulkExport: backend → fallback на client-side
  (buildLocalCsv вынесен).
- Pest +4.

Vitest +3 (всего 245/245).
PHPStan убрал лишнюю Deal->id===null проверку (Eloquent int).
DealsView/KanbanView spec'ы получили setActivePinia.

Регресс: lint+type-check+format ; vitest 245/245 за 17.07 сек (+3);
vite build 1.04 сек; Pint+PHPStan passed; Pest 156/156 за 20.27 сек
(+15 от 141, 675 assertions). Реестр v1.56→v1.57, CLAUDE.md v1.47→v1.48.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 06:43:21 +03:00
Дмитрий 73e64128dc phase2(2fa-setup): wizard init+confirm+disable+regenerate в SettingsView/SecurityTab
- TwoFactorSetupController (auth:sanctum): /api/2fa/{init,confirm,disable,regenerate-recovery-codes}
- init секрет в session (не в БД), QR-URL otpauth://; confirm активирует 2FA + 8 recovery codes
- disable/regenerate требуют password-confirmation
- User.casts: totp_secret => encrypted

Schema v8.7→v8.8: users.totp_secret VARCHAR(255) → TEXT (encrypted ~256 chars)
Migration fix: explicit ALTER TABLE webhook_dedup_keys ADD FK после DB::unprepared (PDO глотал FK на partitioned)
PartitionsCreateMonthsTest fix: DETACH PARTITION + DROP вместо DROP CASCADE

Frontend: SecurityTab реальная логика (setup wizard 3 шага, disable, regenerate dialogs)

- Pest +10 (101/101 за 13.37с, 364 assertions)
- Vitest 166/166
- CLAUDE.md v1.39→v1.40, реестр v1.48→v1.49, schema v8.7→v8.8

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 04:03:02 +03:00
Дмитрий 4803fa0200 phase1(webhook): Deal/WebhookDedupKey + ProcessWebhookJob (advisory lock) — CTO-17 addendum
Webhook PoC раскрыл архитектурный пробел в schema v8.6: §5.5-спецификация
делает INSERT в webhook_dedup_keys ДО INSERT в deals (атомарный захват
ключа), но FK был immediate. Решение в две стадии:

  1. schema.sql v8.6 → v8.7 — DEFERRABLE INITIALLY DEFERRED на FK
     (deal_id, deal_received_at) → deals. ON DELETE CASCADE остаётся
     immediate. В bare-транзакции production worker'а решает проблему.

  2. Pivot Job на pg_advisory_xact_lock — Pest-тесты с DatabaseTransactions
     trait всё равно падали: PG проверяет deferred FK на RELEASE SAVEPOINT,
     не на outer COMMIT. Воспроизведено standalone PHP-скриптом, это
     PG-семантика subtransactions. Advisory lock работает identically
     в любой вложенности транзакций. DEFERRABLE FK сохранён в schema
     как defense-in-depth для batch-импортов без savepoint.

Backend стек:
  - app/app/Models/Deal.php — composite PK через override
    setKeysForSaveQuery (PG требует id+received_at для partition pruning)
  - app/app/Models/WebhookDedupKey.php — мини-модель для тестов и debug
  - app/database/factories/DealFactory.php — fake данные с received_at
    в текущей партиции
  - app/app/Jobs/ProcessWebhookJob.php — advisory-lock-based upsert
    по §5.5 v8.7. PoC scope: dedup + balance check + project findOrCreate.
    TODO для следующих ветвей: BalanceTransaction, SupplierLeadCost,
    ActivityLog, RejectedDealsLog, DuplicateDetector (Биз-19).
  - app/tests/Feature/DealModelTest.php — 6 тестов composite PK + связи
  - app/tests/Feature/ProcessWebhookJobTest.php — 6 тестов: новая сделка,
    дубль vid, balance=0, изоляция тенантов, findOrCreate проекта,
    ON DELETE CASCADE.

Pest 31/31 за 2.7 сек. Pint + Larastan чисто (phpstan-baseline регенерирован,
scanFiles _ide_helper_models.php добавлен в phpstan.neon).

Документы:
  - db/CHANGELOG_schema.md §W (две стадии решения)
  - narrative §2.4/§5.5/§6.5/§11 синхронизированы под advisory lock
  - Реестр Открытые_вопросы v1.20 → v1.21
  - CLAUDE.md v1.11 → v1.12

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 15:24:55 +03:00
Дмитрий f502b0058d phase1(backend): multi-tenant фундамент развёрнут — schema v8.5→v8.6 + migrate:fresh
Backend multi-tenant фундамент развёрнут на dev-БД liderra: 68 таблиц
(52 обычных + 16 партиций) + 36 RLS-policies + 5 функций + 13 триггеров.
`php artisan migrate:fresh` за 870 ms через одну raw-SQL миграцию
`load_initial_schema.php` (DB::unprepared с db/schema.sql).

Первый реальный запуск schema.sql на pristine PG 16 поймал 2
несовместимости v8.5, исправлены архитектурно (CTO-17):

1. CREATE UNIQUE INDEX на партиционированной deals (schema:1263)
   PG требует partition key (received_at) в UNIQUE; включить нельзя —
   ломает идемпотентность webhook'ов. Решение: новая таблица
   webhook_dedup_keys (не партиционированная, PK (tenant_id, source_crm_id)
   → deal_id, composite FK на deals(id, received_at) ON DELETE CASCADE,
   RLS tenant_isolation USING+WITH CHECK). UNIQUE INDEX в deals
   заменён на обычный. Webhook handler — двустадийная UPSERT.

2. GENERATED ALWAYS AS на pd_subject_requests.deadline_at (schema:1999)
   `+ INTERVAL '30 days'` не immutable. Решение: обычная TIMESTAMPTZ
   NOT NULL + триггер trg_pd_subject_requests_deadline + функция
   set_pd_subject_request_deadline().

Изменения:

- db/schema.sql: v8.5 → v8.6 (заголовок, 1 новая таблица, 1 RLS-policy,
  1 функция, 1 триггер, замена UNIQUE на обычный INDEX, замена GENERATED
  на TIMESTAMPTZ NOT NULL)
- db/CHANGELOG_schema.md: новая запись §X v8.5→v8.6
- db/00_create_roles.sql (NEW): deployment-скрипт 4 ролей PG для production
  (crm_app_user, crm_admin_user BYPASSRLS, crm_migrator BYPASSRLS+CREATEDB,
  crm_audit_writer). На dev — postgres superuser (schema §13 разрешает)
- db/02_grants.sql (NEW): GRANT/REVOKE из закомментированных секций §13
  schema. REVOKE на 6 saas-таблицах для crm_app_user (defense-in-depth
  поверх RLS, OPEN-И-14). REVOKE DELETE на 4 финансовых таблицах для
  crm_admin_user (только soft markers)
- app/database/migrations: удалены 3 default Laravel (users/cache/jobs
  дублировались с нашей schema), создан 0001_01_01_000000_load_initial_schema.php
- .squawk.toml: + excluded_paths для db/00_create_roles.sql (psql client-side
  variables :'name' не парсятся libpg_query)
- docs/Открытые_вопросы_v8_3.md: v1.18 → v1.19, CTO-17 закрыт фиксом,
  70  / 5 🟦 / 4 ⏸. Техдолг: ТЗ §15-16 webhook handler нужно обновить
  под двустадийную dedup-логику
- CLAUDE.md: v1.9 → v1.10 (§0 ссылки на schema v8.6 + реестр v1.19;
  §2 метрики БД 54→55/91→92/35→36/12→13/4→5; §6 фундамент развёрнут)
- cspell-words.txt: +9 новых терминов

Smoke-test через Boost MCP database-query:
- 68 таблиц (включая webhook_dedup_keys + 16 партиций)
- 36 RLS-policies
- 35 RLS-enabled (relkind='r'; +2 partitioned 'p' = 37 total)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 13:48:24 +03:00
Дмитрий 887abf444e rebrand(v8.5→Лидерра): дизайн-handoff Платона v8 Forest + Лидпоток→Лидерра
Получен handoff-пакет liderra_v8_handoff/ от дизайнера Платона
(kpd9363@gmail.com) от 07.05.2026 — v8 Forest. Заказчик 08.05 решил
применить только в части дизайна, имени, логотипа. Функционал, состав
страниц и правила (CTO-11, click-wrap, SSO break-glass, 14 статусов
воронки) — без изменений (источник — ТЗ v8.5/schema v8.5).

Что сделано:

- Массовая замена Лидпоток→Лидерра (с учётом падежей: Лидерры/Лидерре)
  в 33 файлах (449 вхождений) — все .md/.sql/.json/.toml/.yml/.txt/.html,
  кроме исторических упоминаний внутри liderra_v8_handoff/
- Удалён docs/brandbook.md v1.1 — заменён на BRANDBOOK_v2.md из handoff
- Скопированы 13 концептов liderra_v8_handoff/concepts/v8_*.html в
  web/v8/. Удалены старые web/01-login.html, 02-dashboard.html,
  03-deals.html, index.html (палитра v1.1 deprecated)
- CLAUDE.md v1.0→v1.1: §0 (BRANDBOOK_v2 + DEVELOPER_HANDOFF в источниках),
  §2 (палитра Forest, Inter+JBM, Lucide), §5 п.6 (anti-pattern Inter
  снят — в Forest Inter наш основной шрифт), §6 (13 концептов в web/v8/)
- Реестр Открытые_вопросы_v8_3.md v1.12→v1.13: добавлена запись о
  ребрендинге + 4 точечных расхождений handoff vs ТЗ (статусы воронки,
  click-wrap чекбоксы, SSO fallback, axe violations)
- package.json/package-lock.json: name lidpotok→liderra

4 расхождения handoff vs ТЗ (НЕ применены, источник истины — ТЗ/schema):

1. 14 «обобщённых» статусов в BRANDBOOK_v2 §3.6 ≠ 14 slug'ов в
   schema.sql:2076 (совпадает 2 из 14: «Переговоры», «Оплачено»).
   Источник — schema/ТЗ §6.4 (реселлерская модель из аудита crm.bp-gr.ru,
   6 системных + 8 настраиваемых статусов).
2. 3-й click-wrap в v8_login.html («маркетинг-опционально») ≠ ТЗ §1.5/§4.1
   («согласие на ПДн», обязательное, OPEN-Ж-3).
3. SSO в v8_admin.html («локальный 2FA fallback») ≠ ТЗ OPEN-И-13
   (break-glass super_admin, локальный 2FA выключен).
4. Заявление «axe-core 4.10.2 — 0 violations» в README handoff — локально
   Pa11y 9.1.1 + axe нашёл 81 violation на 10/13 HTML (преимущественно
   color-contrast на декоративных separator'ах с --ink-disabled).
   Чисто: settings/errors/palette_options.

Что НЕ включено в коммит:
- лендинг/TZ_landing_v1_0.md — untracked, не моя работа в этой сессии
- .tmp/ — gitignored

Что осталось (для следующих сессий):
- Возможное переименование GitHub-репо CoralMinister/lidpotok → liderra
  (отдельное решение заказчика)
- Опционально: обратная связь Платону по 4 расхождениям handoff vs ТЗ

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 07:11:58 +03:00
Дмитрий 038a884daf schema(v8.5): реализация 27 решений аудита C — DDL + CHANGELOG + метрики
Закрывает все 27 решений из Открытые_вопросы v1.12 §13.10 на уровне
schema.sql. Narrative-обновление (§10/§12.5.5/§14/§17/§19.10/§22/§23.10/
Прил.И) — отдельным коммитом.

Метрики v8.4 → v8.5:
- 53 → 54 таблицы (+1: project_user_assignments)
- 86 → 91 индекс (+5)
- 34 → 35 RLS-политик (+1) + WITH CHECK на 2 существующих
- 34 → 35 ENABLE RLS (+1)
- 3 → 4 роли (+crm_audit_writer)
- 0 → 12 триггеров (5×2 audit append-only + 1 report_jobs export
  + 1 deals lead_score)
- 0 → 4 функции (audit_chain_hash, audit_block_mutation,
  report_jobs_log_export, calc_lead_score)
- +26 колонок: suppliers.quality_score; saas_admin_users (sso_provider,
  is_break_glass); impersonation_tokens (second_approver_id,
  second_approval_at); tenants (api_key_limit, telegram_bot_token);
  projects (assignment_strategy, ttfr_target_minutes); users
  (telegram_user_id); deals (assigned_at, escalated_count, duplicate_of_id,
  utm_source/medium/campaign/content, region_code, city,
  time_in_form_seconds, lead_score); +log_hash на 5 audit-таблицах
- ALTER api_keys.expires_at SET NOT NULL DEFAULT NOW()+365d
- REVOKE ALL на 6 saas-таблицах от crm_app_user

P0 (8) разблокировали триггер фазы 1 (composer create-project):
- Биз-17 (manual routing), Биз-18 (TTFR 15м), Биз-19 (24ч-дедуп без списания)
- CTO-13 (e2e SET LOCAL+PgBouncer тест в спринте 1; план в narrative)
- OPEN-И-13 (OIDC+JIT+break-glass), OPEN-И-14 (WITH CHECK + REVOKE)
- OPEN-И-15 (append-only audit + hash chain + crm_audit_writer)
- OPEN-И-16 (Sentry whitelist+regex; конфигурация в Laravel, не DDL)

Self-review: 0 orphan-FK, 0 дубликатов CREATE TABLE, метрики совпадают
с grep'ами; markdownlint+cspell чистые.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 17:46:46 +03:00
Дмитрий 574154bcf9 fix(schema+narrative): B-5 — schema P1 + синхронизация метрик
schema.sql v8.4 (hotfix Z.5.3-Z.5.6):
- outbound_webhook_subscriptions.events: убран DEFAULT '[]' (конфликт
  с CHECK jsonb_array_length>0). NOT NULL остался — приложение должно
  явно передать список событий ≥1.
- deal_tag_pivot: добавлены ENABLE RLS + CREATE POLICY tenant_isolation
  через JOIN на deal_tags(tenant_id) — паттерн как у saas_invoice_items.
- Шапка schema.sql:107-108: «33 политики / 34 защищённых» → «34/34, 1:1»
  (после правки выше). CHANGELOG_schema.md: расширена запись Z.5
  (Z.5.3-Z.5.6) с финальными метриками.

narrative v8.4:
- §1.4, §3.2, §7.1, §22.6, §27 «33 политики на 34» (5 мест) → «34/34,
  1:1». Шапка «Что нового в v8.4»: +3 RLS вместо +2 (с учётом hotfix).

Прил. Б+В:
- Шапка ссылалась на «schema.sql v8.3, 51 таблица» → актуально:
  «schema.sql v8.4, 53/86/34/34, при расхождении приоритет за schema.sql».
  Добавлены изменения v8.4 в перечень того, что не отражено в ER.

cspell-words.txt: добавлено «партиционированной» (склонение, нужно для
CHANGELOG Z.5.4).

Метрики schema.sql v8.4: 65 CREATE TABLE (53+12), 86 индексов,
34 RLS-политики, 34 ENABLE RLS, 3 роли БД.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 00:19:20 +07:00
Дмитрий 7619ac0345 fix(schema+package): D-блок P0 — forward FK + битые partial-индексы + npm links
- schema.sql: forward FK saas_admin_sessions.impersonating_tenant_id и
  impersonation_tokens.tenant_id → tenants(id) вынесены в ALTER TABLE
  после CREATE TABLE tenants (миграция с нуля больше не падает на
  forward-reference).
- schema.sql: partial-индексы idx_saas_admin_sessions_expires и
  idx_sessions_expires переведены на полное поле expires_at — PostgreSQL
  не разрешает NOW() в предикате частичного индекса (STABLE, не IMMUTABLE).
- package.json:10 (links): lychee → bin\lychee.exe — Windows-cmd корректно
  резолвит относительный путь только через backslash.
- CHANGELOG_schema.md: запись Z.5 (hotfix v8.4) с метриками 53/86/33/34.

Метрики schema.sql v8.4 не меняются. Self-review §8 CLAUDE.md пройден.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-06 23:15:45 +07:00
Дмитрий c8db9a26c4 docs(narrative): v8.4 финал (§23.10 + 13/13 + rename _v8_3 → _v8_4)
- §23.10 Админка SaaS: расширен с 5 до 10 подсекций — Биз-16
  (колонка «Желаемое × факт сегодня» с цветовым кодированием),
  Ю-2 (поставщики, дашборд маржи, сверка счетов), OPEN-Д-5/И-1
  (incidents_log: 8 типов × 4 severity, 24ч SLA уведомления РКН для
  data_breach по 152-ФЗ ст.18.1 ч.3.1), Прил. Д (workflow обращений
  субъектов ПДн с 30-дневным SLA), таблица преимуществ vs оригинал.
- Шапка narrative: убрано «in progress», блок «Что нового в v8.4»
  дополнен §23.10. Подвал: имя файла v8.4.
- Переименование: CRM_bp-gr_Инструкция_v8_3.md → _v8_4.md.
- Кросс-ссылки обновлены: CLAUDE.md (§0/§2/§6/§8 — версии, метрики
  схемы 53/86/33, счётчик прототипов 3/8), README.md (версии, статусы
  прототипов, репо CoralMinister), db/schema.sql, db/CHANGELOG_schema.md,
  web/index.html.
- .lychee.toml: exclude приватного github.com/CoralMinister/lidpotok
  (404 анонимно — норма).
- Plan_narrative_v8_4.md удалён (план v8.4 выполнен полностью, 13/13).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-06 19:14:53 +07:00
Дмитрий a05b09a025 schema(v8.4)+docs(narrative): §7 + outbound_webhook_* DDL
schema.sql v8.3 → v8.4 (закрывает тех-долг §19.10):
- +outbound_webhook_subscriptions (15 полей: target_url, secret_hash,
  secret_prefix, events JSONB, custom_headers, is_active, paused_at,
  consecutive_failures, …) + 2 индекса
- +outbound_webhook_deliveries (16 полей с retry-флоу: attempt_number 1-7,
  status pending/success/failed/permanently_failed, scheduled_at,
  next_retry_at, response_body, …) + 3 индекса
- +RLS-политики tenant_isolation для обеих таблиц
- Шапка обновлена: 51→53 таблицы, 81→86 индексов, 31→33 RLS,
  32→34 ENABLE RLS

db/CHANGELOG_schema.md: новая запись §Z (v8.3 → v8.4) с обоснованием,
сводной таблицей, описанием новых таблиц/индексов, разъяснением что
crm_connections/crm_field_mappings отложены до спринта 14-15 (amoCRM).

narrative §7: синхронизация с фактическим schema.sql v8.4.
- §7.1: метрики обновлены (53/12/86/33/34), карта таблиц расширена
  до полного списка (включая ранее отсутствовавшие
  tenant_custom_domains, user_recovery_codes, user_sessions,
  project_suppliers, project_limit_adjustments, incidents_log,
  report_jobs, document_sequences, outbound_webhook_*)
- §7.3: DDL deals — удалены reminder_text/reminder_at/idx_deals_reminder
  (синхронизация с v8.3, партия 12.2.5). Добавлены DDL reminders v8.3,
  outbound_webhook_subscriptions, outbound_webhook_deliveries

Шапка narrative: блок «Что нового в v8.4» обновлён (+§7).
План v8.4: 8/13  (было 7/13). cspell-words.txt: +воркер, +деплой формы.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-06 17:35:18 +07:00
Дмитрий b5cda8886d Restructure: split files into docs/web/db folders 2026-05-06 01:39:59 +07:00