# G3 — Полное удаление фичи «Напоминания» — Implementation Plan

> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.

**Goal:** Полностью удалить фичу «Напоминания по сделкам» из портала Лидерры — экран, серверный код, рассылку, крон и таблицу `reminders` в БД — ничего не сломав в смежных фичах.

**Architecture:** Удаление слоями сверху вниз (фронт → backend → БД → тесты/нормативка). На каждом слое — частые коммиты. Цикл для удаления инвертирован: сперва фиксируем зелёный baseline, затем удаляем, затем снова зелёно. Источник истины по БД — `db/schema.sql` + миграция-DROP.

**Tech Stack:** Laravel 13 / Pest 4 / Larastan; Vue 3 + Vuetify 3 / vue-tsc / eslint / Vite; PostgreSQL 16 (RLS). Спека: `docs/superpowers/specs/2026-06-19-g3-reminders-removal-design.md`.

**Окружение (факты):**
- Backend dev: `php artisan serve` на `127.0.0.1:8000`. Фронт — собранные ассеты (`npm --prefix app run build`, vite-dev не запущен).
- Pest: `composer --working-dir=app test -- <путь|--filter=X>`.
- Миграции тест-БД явно: `DB_DATABASE=liderra_testing php app/artisan migrate --force`. Dev-БД: `php app/artisan migrate --force`.
- Коммит: `composer --working-dir=app pint` → `LEFTHOOK=0 git commit`. Push: `git push gitea main`.
- Квирк Edit: нормализует пробел→NBSP (для literal ` ` использовать `perl -CSD -i -pe ...`). В этом плане NBSP не вводим.

---

## Файловая карта (что трогаем)

**Удаляем целиком (файлы):**
- Фронт: `resources/js/views/RemindersView.vue`, `resources/js/components/reminders/` (3 файла), `resources/js/stores/reminders.ts`, `resources/js/api/reminders.ts`.
- Backend: `app/Http/Controllers/Api/ReminderController.php`, `app/Models/Reminder.php`, `database/factories/ReminderFactory.php`, `app/Console/Commands/RemindersDispatchDue.php`, `app/Mail/ReminderDueNotification.php`, `resources/views/emails/reminder.blade.php`.
- Тесты: `tests/Feature/ReminderControllerTest.php`, `tests/Feature/RemindersDispatchDueTest.php`, `tests/Frontend/RemindersView.spec.ts`, `tests/Frontend/reminders-store.spec.ts`, `tests/Frontend/reminders-api.spec.ts`, `tests/Frontend/ReminderDialog.spec.ts`.

**Редактируем (вырезаем ветку reminders):**
- `resources/js/router/index.ts`, `resources/js/histoire.setup.ts`, `resources/js/layouts/AppLayout.vue`, `resources/js/components/layout/AppTopbar.vue`, `resources/js/components/deals/DealDetailBody.vue`, `resources/js/api/deals.ts`, `resources/js/composables/dealsApiMapper.ts`, `resources/js/composables/mockDeals.ts`, `resources/js/components/deals/DealsTable.vue`, `resources/js/views/settings/NotificationsTab.vue`, `resources/js/api/notifications.ts`.
- `routes/web.php`, `app/Http/Controllers/Api/DealController.php`, `app/Services/NotificationService.php`, `database/factories/UserFactory.php`, `database/seeders/DemoSeeder.php`.
- БД/нормативка: `db/schema.sql`, `db/CHANGELOG_schema.md`, `app/phpstan-baseline.neon`, `app/dev-indices.json`.
- Смежные тесты: `tests/Feature/DealIndexTest.php`, `tests/Feature/Notifications/InAppNotificationTest.php`, `tests/Feature/Auth/NotificationPreferencesTest.php`, `tests/Frontend/{dealsApiMapper,DealsView,DealDetailDrawerApi,DealsListIntegration,NotificationsTab}.spec.ts`.

**НЕ трогаем (швы):** `constants/polling.ts` (константа нужна балансу), общий каркас `NotificationService` (`notifyInApp`/`sendEmail`/`prefEnabled`/`InAppNotification`), `BalanceFrozenReminder*`, таблица `deals`.

---

## Task 0: Baseline — зафиксировать зелёное ДО правок

**Files:** только чтение/прогон.

- [ ] **Step 1: Прогнать затронутые backend-тесты (должны быть зелёные)**

Run:
```bash
composer --working-dir=app test -- tests/Feature/ReminderControllerTest.php tests/Feature/RemindersDispatchDueTest.php tests/Feature/DealIndexTest.php tests/Feature/Notifications/InAppNotificationTest.php tests/Feature/Auth/NotificationPreferencesTest.php
```
Expected: всё PASS. Если что-то красное ДО правок — остановиться и сообщить владельцу (это «уже было», не наша поломка).

- [ ] **Step 2: Зафиксировать чистый Larastan baseline**

Run: `composer --working-dir=app stan`
Expected: 0 ошибок (baseline matched). Запомнить — после удаления файлов baseline-строки должны уйти.

- [ ] **Step 3: type-check фронта зелёный**

Run: `npm --prefix app run type-check`
Expected: 0 ошибок.

Коммита нет — это замер.

---

## Task 1: Фронт — удалить экран «Напоминания»

**Files:**
- Delete: `resources/js/views/RemindersView.vue`, `resources/js/components/reminders/RemindersList.vue`, `resources/js/components/reminders/RemindersFilters.vue`, `resources/js/components/reminders/ReminderDialog.vue`, `resources/js/stores/reminders.ts`, `resources/js/api/reminders.ts`
- Modify: `resources/js/router/index.ts`, `resources/js/histoire.setup.ts`

> ⚠ Порядок: сначала убрать ВСЕ импорты `api/reminders`/`stores/reminders`/`ReminderDialog` (Task 1–3), потом удалять файлы — иначе type-check между шагами красный. Здесь удаляем экран; `DealDetailBody` ещё ссылается на `api/reminders` и `ReminderDialog` — его чистим в Task 2 ПЕРЕД финальным type-check. Поэтому type-check гоняем в конце Task 3.

- [ ] **Step 1: Удалить роут `/reminders` из `router/index.ts`**

Удалить блок (строки ~176–188):
```ts
    {
        path: '/reminders',
        name: 'reminders',
        component: () => import('../views/RemindersView.vue'),
        meta: {
            layout: 'app',
            title: 'Напоминания',
            requiresAuth: true,
            transition: 'ld-route-fadeup',
            devIndex: 11,
            devLabel: 'Напоминания',
        },
    },
```

- [ ] **Step 2: Удалить стаб-роут в `histoire.setup.ts`**

Удалить строку:
```ts
            { path: '/reminders', component: { template: '<div />' } },
```

- [ ] **Step 3: Удалить файлы экрана**

Run:
```bash
git rm app/resources/js/views/RemindersView.vue \
  app/resources/js/components/reminders/RemindersList.vue \
  app/resources/js/components/reminders/RemindersFilters.vue \
  app/resources/js/components/reminders/ReminderDialog.vue \
  app/resources/js/stores/reminders.ts \
  app/resources/js/api/reminders.ts
```

Коммит — в конце Task 3 (после того как уйдут все ссылки и пройдёт type-check).

---

## Task 2: Фронт — вырезать раздел «Напоминания» из карточки сделки

**Files:**
- Modify: `resources/js/components/deals/DealDetailBody.vue`

- [ ] **Step 1: Удалить импорты reminders**

Удалить строки:
```ts
import * as remindersApi from '../../api/reminders';
import type { ApiReminder } from '../../api/reminders';
```
и
```ts
const ReminderDialog = defineAsyncComponent(() => import('../reminders/ReminderDialog.vue'));
```
(если `defineAsyncComponent` больше нигде не используется — убрать его и из импорта `vue`; проверить grep'ом по файлу).

- [ ] **Step 2: Удалить состояние и функции reminders**

Удалить: `const reminders = ref<ApiReminder[]>([])`, `const remindersLoading = ref(false)`, `const reminderDialogOpen = ref(false)`, функции `loadReminders`, `completeReminderInDrawer`, `onReminderSaved`, `formatReminderTime`, и вызов `void loadReminders();` внутри watch-блока (стр.~175).

- [ ] **Step 3: Удалить разметку секции и `<ReminderDialog>`**

Удалить `<section ... data-testid="reminders-section"> ... </section>` (стр.~264–312) и блок `<ReminderDialog v-if=... />` (стр.~314–319).

- [ ] **Step 4: Удалить CSS reminders**

Удалить правила `.reminders-list`, `.reminder-row`, `.reminder-body`, `.reminder-text`, `.reminder-meta` (стр.~357–385).

- [ ] **Step 5: Проверить, что файл не ссылается на reminders**

Run: `grep -in reminder app/resources/js/components/deals/DealDetailBody.vue`
Expected: пусто.

Коммит — в конце Task 3.

---

## Task 3: Фронт — убрать опрос напоминаний и иконку типа

**Files:**
- Modify: `resources/js/layouts/AppLayout.vue`, `resources/js/components/layout/AppTopbar.vue`

> ⚠ ШОВ: `POLLING_REMINDERS_INTERVAL_MS` оставить (на нём висит опрос баланса). Удаляем только опрос напоминаний.

- [ ] **Step 1: AppLayout — убрать стор и опрос напоминаний**

Удалить: `import { useRemindersStore } from '../stores/reminders';`, `const reminders = useRemindersStore();`, функцию `loadReminderCounts`, вызов `void loadReminderCounts();` в `onMounted`, и строку `usePolling(loadReminderCounts, { intervalMs: POLLING_REMINDERS_INTERVAL_MS, enabled: true });`.
**Оставить** строку опроса баланса `usePolling(loadBalanceStatus, { intervalMs: POLLING_REMINDERS_INTERVAL_MS, enabled: true });` и сам импорт `POLLING_REMINDERS_INTERVAL_MS`.

- [ ] **Step 2: AppTopbar — убрать ключ `reminder` из icon-map**

Удалить строку `reminder: 'mdi-clock-outline',` из объекта `map` (стр.~36).

- [ ] **Step 3: type-check всего фронта**

Run: `npm --prefix app run type-check`
Expected: 0 ошибок (все ссылки на reminders ушли — Task 1+2+3).

- [ ] **Step 4: eslint**

Run: `npm run lint:vue`
Expected: 0 ошибок.

- [ ] **Step 5: Commit (фронт-экран целиком)**

```bash
composer --working-dir=app pint
git add -A
LEFTHOOK=0 git commit -m "feat(G3): убрать экран «Напоминания» и раздел в карточке сделки" -m "Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>"
```

---

## Task 4: Фронт — убрать `next_reminder_at` из слоя сделок + смежные тесты

**Files:**
- Modify: `resources/js/api/deals.ts`, `resources/js/composables/dealsApiMapper.ts`, `resources/js/composables/mockDeals.ts`, `resources/js/components/deals/DealsTable.vue`
- Modify (тесты): `tests/Frontend/dealsApiMapper.spec.ts`, `tests/Frontend/DealsView.spec.ts`, `tests/Frontend/DealDetailDrawerApi.spec.ts`, `tests/Frontend/DealsListIntegration.spec.ts`

- [ ] **Step 1: deals.ts — убрать поле типа**

Удалить строку `next_reminder_at: string | null;` (стр.~174).

- [ ] **Step 2: dealsApiMapper.ts — убрать маппинг**

Удалить строку `nextReminderAt: api.next_reminder_at,` (стр.~81). Если тип `MockDeal` объявляет `nextReminderAt` — проверить grep'ом `nextReminderAt` по `mockDeals.ts`/`DealsTable.vue` и убрать поле и его использование (колонку/индикатор «следующее напоминание», если есть).

- [ ] **Step 3: mockDeals.ts / DealsTable.vue — убрать `nextReminderAt`/`next_reminder_at`**

Run: `grep -rin "next_reminder_at\|nextReminderAt" app/resources/js`
Удалить все оставшиеся вхождения (поле в моках, колонка/чип в таблице, если есть). Expected после правок: только тест-файлы.

- [ ] **Step 4: Поправить фронт-тесты — убрать `next_reminder_at` из фикстур/ассертов**

В `dealsApiMapper.spec.ts` (стр.21, 90), `DealsView.spec.ts` (стр.28), `DealDetailDrawerApi.spec.ts` (стр.66,122,176), `DealsListIntegration.spec.ts` (стр.40) удалить строки-фикстуры `next_reminder_at: …` и любые ассерты на `nextReminderAt`.

- [ ] **Step 5: type-check + eslint**

Run: `npm --prefix app run type-check && npm run lint:vue`
Expected: 0 ошибок. (vitest сломан G8 — фронт-юниты не гоняем, опираемся на type-check + Playwright в Task 12.)

- [ ] **Step 6: Commit**

```bash
git add -A
LEFTHOOK=0 git commit -m "feat(G3): убрать next_reminder_at из фронт-слоя сделок" -m "Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>"
```

---

## Task 5: Фронт — убрать преференцию уведомления «Напоминание»

**Files:**
- Modify: `resources/js/views/settings/NotificationsTab.vue`, `resources/js/api/notifications.ts`
- Modify (тест): `tests/Frontend/NotificationsTab.spec.ts`

- [ ] **Step 1: NotificationsTab.vue — убрать запись EVENTS и `reminder` из doc**

Удалить из массива `EVENTS` строку:
```ts
    { id: 'reminder', label: 'Напоминание', description: 'Срок касания клиента наступил.' },
```
и упоминание `reminder` в шапочном комментарии (`new_lead / reminder / low_balance …` → убрать `reminder /`).

- [ ] **Step 2: api/notifications.ts — убрать из union**

Удалить `| 'reminder'` из типа `NotificationEvent` (стр.~12).

- [ ] **Step 3: Поправить NotificationsTab.spec.ts**

Run: `grep -in reminder app/tests/Frontend/NotificationsTab.spec.ts`
Удалить ассерты/фикстуры, завязанные на событие `reminder` (ожидание строки «Напоминание» / ключа `reminder`).

- [ ] **Step 4: type-check + eslint**

Run: `npm --prefix app run type-check && npm run lint:vue`
Expected: 0 ошибок.

- [ ] **Step 5: Commit**

```bash
git add -A
LEFTHOOK=0 git commit -m "feat(G3): убрать преференцию уведомления «Напоминание» из настроек" -m "Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>"
```

---

## Task 6: Backend — убрать `next_reminder_at` из DealController + тест

**Files:**
- Modify: `app/Http/Controllers/Api/DealController.php`
- Modify: `tests/Feature/DealIndexTest.php`

- [ ] **Step 1: Удалить подзапрос addSelect**

Удалить блок (стр.~106–112):
```php
                ->addSelect(['next_reminder_at' => DB::table('reminders')
                    ->select('remind_at')
                    ->whereColumn('reminders.deal_id', 'deals.id')
                    ->whereNull('reminders.completed_at')
                    ->orderBy('remind_at')
                    ->limit(1),
                ])
```

- [ ] **Step 2: Удалить поле из ресурса**

Удалить (стр.~221–222):
```php
                'next_reminder_at' => $d->next_reminder_at
                    ? Carbon::parse($d->next_reminder_at)->toIso8601String()
                    : null,
```
Проверить: если `Carbon` после этого нигде не используется в файле — убрать неиспользуемый импорт (grep по файлу).

- [ ] **Step 3: Поправить DealIndexTest — убрать ассерт next_reminder_at**

В `tests/Feature/DealIndexTest.php` (стр.~338 тест-название, ~350 `toHaveKey('next_reminder_at')`): убрать ассерт `->toHaveKey('next_reminder_at')` и из названия теста токен `next_reminder_at`. Если тест целиком про `next_reminder_at` — удалить весь `test(...)`; иначе оставить про comment/city/project_signal_type.

- [ ] **Step 4: Прогнать тест**

Run: `composer --working-dir=app test -- tests/Feature/DealIndexTest.php`
Expected: PASS.

- [ ] **Step 5: Commit**

```bash
composer --working-dir=app pint
git add -A
LEFTHOOK=0 git commit -m "feat(G3): убрать next_reminder_at-подзапрос из DealController" -m "Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>"
```

---

## Task 7: Backend — убрать ветку reminder из NotificationService + смежные тесты

**Files:**
- Modify: `app/Services/NotificationService.php`
- Modify: `tests/Feature/Notifications/InAppNotificationTest.php`, `tests/Feature/Auth/NotificationPreferencesTest.php`

> ⚠ ШОВ: каркас (`notifyInApp`/`sendEmail`/`prefEnabled`) остаётся. Убираем только `EVENT_REMINDER`, `notifyReminder()` и импорты.

- [ ] **Step 1: Удалить из NotificationService**

Удалить: `use App\Mail\ReminderDueNotification;`, `use App\Models\Reminder;`, константу `public const EVENT_REMINDER = 'reminder';`, метод `notifyReminder(Reminder $reminder): void { … }` целиком (стр.~122–155), и строку `reminder  {inapp:true,…}` из doc-комментария дефолта (стр.~30).

- [ ] **Step 2: InAppNotificationTest — заменить пример события**

В `tests/Feature/Notifications/InAppNotificationTest.php` стр.~29/33 заменить `'reminder'` на `'new_lead'`:
```php
    $service->notifyInApp($user, 'new_lead', 'Срок касания', 'Перезвонить клиенту через 30 мин', ['deal_id' => 42]);
```
и ассерт `expect($notif->event)->toBe('new_lead');`. (Тест проверяет общий механизм, не фичу.)

- [ ] **Step 3: NotificationPreferencesTest — убрать reminder**

Run: `grep -in reminder app/tests/Feature/Auth/NotificationPreferencesTest.php`
Убрать ассерты/фикстуры на событие `reminder` в дефолте prefs (оставив проверки других событий).

- [ ] **Step 4: Прогнать тесты уведомлений**

Run: `composer --working-dir=app test -- tests/Feature/Notifications/InAppNotificationTest.php tests/Feature/Auth/NotificationPreferencesTest.php tests/Feature/Notifications/BalanceNotificationsTest.php`
Expected: PASS.

- [ ] **Step 5: Commit**

```bash
composer --working-dir=app pint
git add -A
LEFTHOOK=0 git commit -m "feat(G3): убрать ветку reminder из NotificationService" -m "Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>"
```

---

## Task 8: Backend — удалить контроллер/роуты/модель/фабрику/команду/письмо

**Files:**
- Delete: `app/Http/Controllers/Api/ReminderController.php`, `app/Models/Reminder.php`, `database/factories/ReminderFactory.php`, `app/Console/Commands/RemindersDispatchDue.php`, `app/Mail/ReminderDueNotification.php`, `resources/views/emails/reminder.blade.php`
- Delete: `tests/Feature/ReminderControllerTest.php`, `tests/Feature/RemindersDispatchDueTest.php`
- Modify: `routes/web.php`

- [ ] **Step 1: Удалить группу роутов и Route::view**

В `routes/web.php` удалить блок (стр.~65–72):
```php
// Reminders (P0 этап 4) — schema v8.10 §17.5. Auth обязательный.
Route::middleware(['auth:sanctum', 'tenant'])->prefix('/api/reminders')->group(function () {
    Route::get('/', 'App\Http\Controllers\Api\ReminderController@index');
    Route::post('/', 'App\Http\Controllers\Api\ReminderController@store');
    Route::patch('/{id}', 'App\Http\Controllers\Api\ReminderController@update')->where('id', '[0-9]+');
    Route::post('/{id}/complete', 'App\Http\Controllers\Api\ReminderController@complete')->where('id', '[0-9]+');
    Route::delete('/{id}', 'App\Http\Controllers\Api\ReminderController@destroy')->where('id', '[0-9]+');
});
```
и строку `Route::view('/reminders', 'welcome');` (стр.~339).

- [ ] **Step 2: Удалить файлы**

Run:
```bash
git rm app/app/Http/Controllers/Api/ReminderController.php \
  app/app/Models/Reminder.php \
  app/database/factories/ReminderFactory.php \
  app/app/Console/Commands/RemindersDispatchDue.php \
  app/app/Mail/ReminderDueNotification.php \
  app/resources/views/emails/reminder.blade.php \
  app/tests/Feature/ReminderControllerTest.php \
  app/tests/Feature/RemindersDispatchDueTest.php
```

- [ ] **Step 3: Проверить отсутствие живых ссылок (кроме schema/CHANGELOG-комментариев и BalanceFrozenReminder)**

Run: `grep -rin "App\\\\Models\\\\Reminder\|notifyReminder\|ReminderController\|reminders:dispatch-due\|api/reminders" app/app app/routes app/resources app/database`
Expected: пусто.

- [ ] **Step 4: Прогнать смок — роутинг и автозагрузка не падают**

Run: `php app/artisan route:list --json > /dev/null && echo OK`
Expected: `OK` (нет ссылок на удалённый контроллер).

- [ ] **Step 5: Commit**

```bash
git add -A
LEFTHOOK=0 git commit -m "feat(G3): удалить контроллер/роуты/модель/команду/письмо напоминаний" -m "Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>"
```

---

## Task 9: Backend — убрать `reminder` из дефолтов notification_preferences

**Files:**
- Modify: `database/factories/UserFactory.php`, `database/seeders/DemoSeeder.php`

> ⚠ ШОВ: не задеть соседние ключи (`new_lead` флипнут в G2-B, `low_balance`, `zero_balance`).

- [ ] **Step 1: UserFactory — убрать ключ reminder**

Run: `grep -in "reminder" app/database/factories/UserFactory.php`
Удалить строку `'reminder' => ['inapp' => true, 'push' => true, 'email' => true],` из дефолта `notification_preferences`.

- [ ] **Step 2: DemoSeeder — убрать ключ reminder**

Удалить строку (стр.~51) `'reminder' => ['inapp' => true, 'push' => true, 'email' => true],`.

- [ ] **Step 3: Прогон — фабрика/сидер не ломают тесты пользователей**

Run: `composer --working-dir=app test -- tests/Feature/Auth/NotificationPreferencesTest.php`
Expected: PASS.

- [ ] **Step 4: Commit**

```bash
composer --working-dir=app pint
git add -A
LEFTHOOK=0 git commit -m "feat(G3): убрать reminder из дефолтов notification_preferences (factory+seeder)" -m "Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>"
```

---

## Task 10: БД — миграция-DROP + правка schema.sql + CHANGELOG

**Files:**
- Create: `app/database/migrations/2026_06_19_130000_drop_reminders_table.php`
- Modify: `db/schema.sql`, `db/CHANGELOG_schema.md`

- [ ] **Step 1: Прочитать текущий DDL `reminders` из schema.sql**

Read: `db/schema.sql` стр.~1899–1958 (CREATE TABLE + индексы + RLS + комментарии) — нужен полный DDL для `down()` миграции.

- [ ] **Step 2: Написать миграцию**

Create `app/database/migrations/2026_06_19_130000_drop_reminders_table.php`:
```php
<?php

declare(strict_types=1);

use Illuminate\Database\Migrations\Migration;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    public function up(): void
    {
        // G3: фича «Напоминания» удалена из продукта. RLS-политика и индексы
        // (idx_reminders_*) уходят каскадом вместе с таблицей.
        Schema::dropIfExists('reminders');
    }

    public function down(): void
    {
        // Откат для dev: воссоздать по DDL из schema.sql (вставить полный
        // CREATE TABLE reminders + индексы + ENABLE RLS + CREATE POLICY
        // tenant_isolation из шага 1).
        DB::statement(<<<'SQL'
        -- <вставить полный DDL reminders из schema.sql стр.1899–1958>
        SQL);
    }
};
```
В `down()` вставить дословный DDL, полученный на шаге 1 (CREATE TABLE + 4 индекса + ALTER ENABLE RLS + CREATE POLICY tenant_isolation). Без плейсхолдера в финальном файле.

- [ ] **Step 3: Применить на dev и тест-БД**

Run:
```bash
php app/artisan migrate --force
DB_DATABASE=liderra_testing php app/artisan migrate --force
```
Expected: `Migrating: 2026_06_19_130000_drop_reminders_table` → `DONE`. Затем проверить отсутствие таблицы:
```bash
php app/artisan tinker --execute="echo \Schema::hasTable('reminders') ? 'EXISTS' : 'GONE';"
```
Expected: `GONE`.

- [ ] **Step 4: Удалить блоки `reminders` из schema.sql**

Удалить из `db/schema.sql`:
- `CREATE TABLE reminders (...)` (стр.~1899–1925) + хедер-комментарий «reminders — напоминания по сделкам».
- `CREATE INDEX idx_reminders_due` / `idx_reminders_deal` / `idx_reminders_tenant_user_active` / `idx_reminders_tenant_active` (стр.~1927–1944).
- `COMMENT ON TABLE reminders` + `COMMENT ON COLUMN reminders.*` (стр.~1946–1958).
- `ALTER TABLE reminders ENABLE ROW LEVEL SECURITY;` (стр.~2958).
- `CREATE POLICY tenant_isolation ON reminders …` (стр.~2999).
- Ключ `reminder` в дефолте `notification_preferences` (JSONB, ~стр.699).

Историко-комментарии про v8.3 (стр.163–167, 1683, 1748 — «reminder_text/reminder_at удалены») **оставить** — это летопись, не живой DDL.

- [ ] **Step 5: Обновить header-метрики + версию schema.sql**

В header `db/schema.sql` уменьшить счётчики (таблицы −1, индексы −4, RLS-политики −1) согласно факту и поднять версию схемы (минор, напр. v8.44 → v8.45). Точные числа — из текущего header (канон там, не в CLAUDE.md).

- [ ] **Step 6: Запись в CHANGELOG_schema.md**

Добавить запись: версия, дата 2026-06-19, «G3: удалена таблица `reminders` (фича Напоминания снята с продукта) — таблица, 4 индекса, RLS-политика tenant_isolation, комментарии; ключ `reminder` убран из дефолта notification_preferences. Миграция `2026_06_19_130000_drop_reminders_table`.»

- [ ] **Step 7: Self-review schema.sql (триггер CLAUDE.md §8)**

Run: `grep -in "reminders" db/schema.sql`
Expected: только историко-комментарии v8.3 (reminder_text/reminder_at), 0 живых `CREATE TABLE/INDEX/POLICY reminders`. Проверить 0 orphan-FK на reminders.

- [ ] **Step 8: Commit**

```bash
git add -A
LEFTHOOK=0 git commit -m "feat(G3): миграция drop reminders + schema.sql + CHANGELOG (vX.YZ)" -m "Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>"
```

---

## Task 11: Чистка baseline и dev-indices

**Files:**
- Modify: `app/phpstan-baseline.neon`, `app/dev-indices.json`

- [ ] **Step 1: Убрать ушедшие совпадения из phpstan-baseline.neon**

Удалить записи, ссылающиеся на удалённые файлы/свойства: `ReminderController.php` (стр.~85 path-блок), `ReminderFactory.php` (стр.~226–229), `ReminderControllerTest.php` (стр.~2077–2113), `RemindersDispatchDueTest.php` (стр.~2119), `Deal::$next_reminder_at` (стр.~52).

- [ ] **Step 2: Прогнать Larastan — baseline чист**

Run: `composer --working-dir=app stan`
Expected: 0 ошибок, **нет** «Ignored error … was not matched» (значит лишних baseline-строк не осталось).

- [ ] **Step 3: dev-indices.json — убрать devIndex 11**

Run: `grep -in "11\|Напомина\|remind" app/dev-indices.json`
Если есть запись devIndex 11 «Напоминания» — удалить. Если файл не содержит — пропустить.

- [ ] **Step 4: Commit**

```bash
git add -A
LEFTHOOK=0 git commit -m "chore(G3): почистить phpstan-baseline и dev-indices от напоминаний" -m "Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>"
```

---

## Task 12: Верификация (полный прогон + живой Playwright)

**Files:** только прогон.

- [ ] **Step 1: Полный прогон затронутых и смежных backend-тестов**

Run:
```bash
composer --working-dir=app test -- tests/Feature/DealIndexTest.php tests/Feature/Notifications tests/Feature/Auth/NotificationPreferencesTest.php tests/Feature/Billing/BalanceFrozenReminderJobTest.php
```
Expected: всё PASS (включая BalanceFrozenReminder — доказываем, что биллинг не задет).

- [ ] **Step 2: Larastan + type-check + eslint + build**

Run:
```bash
composer --working-dir=app stan
npm --prefix app run type-check
npm run lint:vue
npm --prefix app run build
```
Expected: все 0 ошибок / build OK.

- [ ] **Step 3: Поднять backend и пересобрать ассеты**

Run: `npm --prefix app run build` (уже в шаге 2) и `php artisan serve` (фон). Залогиниться тест-клиентом (создать свежего через самозапись или demo).

- [ ] **Step 4: Живой Playwright — 5 сценариев (спека §6)**

Через Playwright MCP на `127.0.0.1:8000`:
- (а) перейти на `/reminders` → ожидается 404-экран (`errorCode: '404'`), не белый экран, не JS-ошибка в консоли.
- (б) открыть карточку сделки → раздела «Напоминания» нет, консоль чистая, кнопка «+ Напоминание» отсутствует.
- (в) список сделок `/deals` грузится, строки рисуются (нет поломки из-за `next_reminder_at`).
- (г) Настройки → Уведомления — нет строки «Напоминание», остальные события на месте.
- (д) дашборд и шапка грузятся, опрос баланса работает (нет 500 на `/api/...balance`).

Зафиксировать консоль (`browser_console_messages`) — 0 ошибок на каждом экране.

- [ ] **Step 5: Финальный grep — ни одной живой ссылки**

Run: `grep -rin "reminder" app/app app/routes app/resources app/database | grep -vi "BalanceFrozenReminder\|frozen_reminder\|billing:frozen-reminder"`
Expected: пусто (только биллинговые доначисления остаются).

Коммита нет (только верификация). Если что-то красное — чинить в рамках соответствующей задачи.

---

## Task 13: Ревью схемы + push

- [ ] **Step 1: rls-reviewer по диффу схемы**

Dispatch агента `rls-reviewer` на дифф `db/schema.sql` + миграцию. Ожидается GREEN (удаление таблицы+политики консистентно, 0 orphan-policy).

- [ ] **Step 2: Push в gitea**

```bash
git push gitea main
```
Expected: pre-push lefthook (gitleaks + lychee) проходит; ветка уехала.

- [ ] **Step 3: Обновить память**

Записать в `project-golive-findings-status.md`: G3 DONE (дата, коммиты, что осталось — G7; G5 висяк). Требует `coverage: direct:memory-sync` или фразу `memory dump`.

- [ ] **Step 4: Операционная памятка владельцу**

Напомнить: на боевом liderra.ru после деплоя **выключить внешний крон** `reminders:dispatch-due` (планировщик Windows) и применить миграцию-DROP — отдельным разрешённым деплоем.

---

## Self-Review плана (выполнено автором)

- **Покрытие спеки:** §2.1 фронт → Task 1–5; §2.2 backend → Task 6–9; §2.3 БД → Task 10; §2.4 тесты/мусор → распределены по задачам + Task 11; §3 швы → явные ⚠-блоки в Task 3/6/7/9 + «НЕ трогаем» в карте; §4 решения → Task 13 Step 4 (крон/прод) + §7 спеки; §6 верификация → Task 12. Пробелов нет.
- **Плейсхолдеры:** единственный намеренный — DDL в `down()` миграции (Task 10 Step 2), который заполняется дословным DDL со Step 1; в финальном файле плейсхолдера быть не должно (явно оговорено).
- **Согласованность имён:** `next_reminder_at` (snake, API/PHP) ↔ `nextReminderAt` (camel, фронт-маппер) — обе формы учтены в Task 4. `POLLING_REMINDERS_INTERVAL_MS` сохраняется (Task 3). `EVENT_REMINDER`/`notifyReminder` удаляются согласованно (Task 7).
