# План v2 — F-P1: ретеншен ПДн удалённых лидов (анонимизация soft-deleted сделок)

## Цель

Реализовать по спеке `2026-06-17-fp1-deal-pii-retention-spec.md` плановую
команду-ретеншен, анонимизирующую ПДн soft-deleted сделок по истечении
настраиваемого срока, с частичным индексом и регистрацией в планировщике.
Полный TDD-цикл: красный тест → команда → зелёный → проводка → регрессия.

## Метод

Навыки `pdn-152fz-audit` и `test-driven-development` объявлены в `skills-json` и
реально вызываются до первого мутирующего шага (отдельными шагами не оформлены:
шаг `Skill` на исполнении не двигает указатель и запирает план — навыки
обеспечиваются дисциплиной навыков, шагами идут конкретные правки и прогоны).

Значения анонимизации (`phone`→`+7000XXXXXXX`, `contact_name`→`Удалено`,
`phones`→`NULL`) берутся ровно как в уже отревьюенном `PdErasureService`. Срок
ретеншена — из таблицы настроек, по умолчанию команда no-op (юр.срок не
зашивается в код). Индекс `deals (deleted_at) WHERE deleted_at IS NOT NULL` —
аддитивный, на работу команды и тестов не влияет (оптимизация выборки).

Замечание наставника по спеке (логирование при пустой выборке) учтено: при
отсутствии кандидатов команда не пишет в журнал — это поведение зафиксировано в
D4 (пустая выборка — корректный no-op без записи).

Шаги:

1. Красный тест `ScrubSoftDeletedDealsCommandTest` (критерии D7): анонимизация
   старого soft-deleted, не-трогание свежеудалённого/живого, no-op без настройки,
   запись в журнал, идемпотентность.
2. Прогон Pest по фильтру — красный (команды ещё нет).
3. Команда `pd:scrub-soft-deleted-deals` (контракт D1, источник срока D2, правило
   D3, edge-cases D4, журнал D5).
4. Прогон Pest по фильтру — зелёный.
5. Регистрация ежедневного запуска в планировщике (D6).
6. Laravel-миграция частичного индекса по `deleted_at` (D6).
7. Отражение индекса в `db/schema.sql` (D6).
8. Запись об индексе в `db/CHANGELOG_schema.md` (D6).
9. Повторный прогон Pest — зелёный после проводки (регрессия).

```skills-json
["pdn-152fz-audit","test-driven-development"]
```

```steps-json
[
  {"op":"Write","object":"app/tests/Feature/Pd/ScrubSoftDeletedDealsCommandTest.php","ref":"D7"},
  {"op":"Bash","object":"composer --working-dir=app test -- --filter=ScrubSoftDeletedDeals","ref":"D7"},
  {"op":"Write","object":"app/app/Console/Commands/Pd/ScrubSoftDeletedDealsCommand.php","ref":"D1"},
  {"op":"Bash","object":"composer --working-dir=app test -- --filter=ScrubSoftDeletedDeals","ref":"D7"},
  {"op":"Edit","object":"app/routes/console.php","ref":"D6"},
  {"op":"Write","object":"app/database/migrations/2026_06_17_120000_add_deals_deleted_at_index.php","ref":"D6"},
  {"op":"Edit","object":"db/schema.sql","ref":"D6"},
  {"op":"Edit","object":"db/CHANGELOG_schema.md","ref":"D6"},
  {"op":"Bash","object":"composer --working-dir=app test -- --filter=ScrubSoftDeletedDeals","ref":"D7"}
]
```

```verified-context-json
[
  {"id":"vc1","kind":"EXTRACTED","ref":"app/app/Services/Pd/PdErasureService.php","anchor":"public function eraseSubject("},
  {"id":"vc2","kind":"EXTRACTED","ref":"app/app/Console/Commands/PartitionsDropExpired.php","anchor":"partition_retention_months_"}
]
```

## Переговоры

### Круг 1

Почему девять шагов: полный TDD-цикл (красный→команда→зелёный), затем аддитивная
проводка (планировщик, миграция индекса, отражение в схеме и журнале схемы) и
финальный зелёный прогон как регрессия. Прогоны Pest — мутирующего класса
(composer-команда), указатель закрывается штатно; завершающий шаг 9 мутирующий.
Значения анонимизации идентичны уже отревьюенному `PdErasureService` — денежные и
связывающие сделку поля не трогаются. Срок ретеншена не зашит в код (читается из
настроек, по умолчанию no-op) — юридический срок выставляется на проде. Индекс
аддитивен и на прохождение тестов не влияет. Прошлое замечание наставника
(логирование no-op) закрыто: журнал при пустой выборке не пишется (D4).

### Круг 2 — запрос рекомендации наставника

Наставник, дай, пожалуйста, краткую forward-рекомендацию по этому плану: верна ли
последовательность TDD-шагов и аддитивной проводки, и нет ли риска в системном
акторе журнала (оба actor-поля NULL по `chk_pd_actor`). Прошлых незакрытых
замечаний нет — нужна твоя содержательная рекомендация вперёд, чтобы вердикт был
полным.
