# Ревью schema drafts

Пошаговая инструкция: как смотреть драфт, какие проверки делать, как
принимать решение.

## Где найти драфты на ревью

В навигации — раздел **«Согласования»** (`/reviews`). Вкладка **«Схемы»** —
ожидающие schema drafts (отсортированы по дате создания, старые сверху).

Также: на странице справочника, если у него **активный draft**, появляется
жёлтый банер «На согласовании: <автор>». Клик → откроется drawer с
содержимым драфта.

## Окно ревью (Schema Draft Drawer)

При открытии драфта видишь:

```
┌──────────────────────────────────────────────────────────────┐
│ Согласование draft                              [×]          │
│ автор: zimin.an · отправлен: 2026-05-12 14:00 · v1.0.0→2.0.0│
├──────────────────────────────────────────────────────────────┤
│ Комментарий автора:                                          │
│ «Добавляем поле frequency_band — будет FK на                 │
│  frequency_band.code, пока опциональное чтобы не ломать      │
│  существующие записи.»                                       │
├──────────────────────────────────────────────────────────────┤
│ Текущая (live) schema  │  Предложено                         │
│ {                      │  {                                   │
│   "code": ...          │    "code": ...,                     │
│   "name": ...          │    "name": ...,                     │
│ }                      │    "frequency_band": {              │
│                        │      "x-references":                │
│                        │        "frequency_band.code"        │
│                        │    }                                 │
│                        │  }                                   │
├──────────────────────────────────────────────────────────────┤
│ ◯ Breaking change keys: нет                                  │
│ ✓ Все существующие записи остаются валидными                 │
├──────────────────────────────────────────────────────────────┤
│ Комментарий ревьюера:                                        │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ ...                                                      │ │
│ └──────────────────────────────────────────────────────────┘ │
├──────────────────────────────────────────────────────────────┤
│       [REJECT]  [REQUEST CHANGES]  [APPROVE]  [PUBLISH]      │
└──────────────────────────────────────────────────────────────┘
```

## Что проверять — чек-лист

### 1. Понимаешь ли ты, ЧТО автор хочет сделать?

Прочти комментарий автора. Он должен объяснить:
- **Зачем** менять схему (новое требование? bug fix?)
- **Что** меняется (поле, тип, ограничение)
- **Что с существующими записями** (мигрируем? остаются как есть?)

Если непонятно — **REQUEST CHANGES** с вопросом. Не угадывай намерения.

### 2. Diff schema — что изменилось

В drawer'е — side-by-side сравнение. Смотри:
- **Новые поля** — выделены зелёным
- **Удалённые поля** — красным
- **Изменённые поля** — жёлтым (с подсветкой, что именно)
- **Breaking change keys** — система автоматически помечает: `type`,
  `format`, `required`, `x-references`, `*Of`, удаления

### 3. Совместимость с существующими записями

Откладывается всё на server: при попытке `publish` сервер прогоняет все
существующие записи через новую схему и проверяет.

Но ревьюер должен **проверить руками** в простых случаях:
- Поле добавляется как **обязательное** (`required`) без default? → существующие
  записи становятся невалидными
- Поле удаляется и оно **использовалось**? → потеряется данные
- Меняется `enum` (убирается значение)? → записи с этим значением станут
  невалидны

Если сомневаешься — **запроси у автора**: «прогони `GET /records?validate=new-schema`
и пришли результат». Эндпоинт покажет, сколько записей сломается.

### 4. Влияние на downstream

Schema change часто требует **уведомления потребителей**:
- Альтум — если меняем поле, которое они используют в карточках
- Геопортал — если меняем geo-поля
- BI — если меняем имена / типы полей, которые ушли в их витрины

Если не уверен — **REQUEST CHANGES** с просьбой согласовать с командами.
Лучше потратить 2 дня на согласование, чем 2 дня на rollback.

### 5. Версионирование

Схема имеет `schemaVersion` (`1.0.0`, `2.0.0`). Правила:
- **Major bump** — breaking change (удаление, изменение типа,
  обязательность). Все консьюмеры должны быть готовы.
- **Minor bump** — backward-compatible (добавление опциональных полей,
  расширение `enum`, новые `description`).
- **Patch** — косметика (документация, examples).

Если автор предлагает minor bump для breaking change — **REQUEST CHANGES**
с просьбой обновить версию.

### 6. Тесты / валидация

Если справочник имеет sample data в bundle — после schema change нужно
проверить, что sample data всё ещё валидна. Это проверяет CI автоматически
после publish, но лучше catch'нуть раньше.

## Действия

### APPROVE

Изменение безопасное, можно публиковать. Кнопка переводит статус драфта в
`APPROVED`. **Публикация ещё не произошла** — нужен отдельный клик
«PUBLISH», который применяет схему к live справочнику.

Опциональный комментарий — почему approve. Появится в audit log.

### REJECT

Изменение неприемлемо в принципе, обсуждать нечего. Например:
- Удаляется поле, которое critical для бизнеса
- Меняется тип так, что данные потеряются необратимо

Драфт переходит в `REJECTED`. **Обязателен комментарий** — почему отказ.
Автор увидит, может создать новый драфт с учётом feedback.

### REQUEST CHANGES

Изменение в принципе ОК, но требует доработки. Например:
- Нужно изменить версию (major вместо minor)
- Нужно сделать поле опциональным с default
- Нужно добавить data migration script

Драфт переходит в `CHANGES_REQUESTED`. Автор увидит, отредактирует, отправит
заново (вернётся в `REVIEW_PENDING`). **Обязателен комментарий** — что
именно править.

### WITHDRAW

Только автор может withdraw свой драфт (если передумал). Ревьюер этого не
делает.

### PUBLISH

Только после `APPROVE`. Применяет новую схему к live справочнику:
- Обновляется `schema_definition` в БД
- Все будущие записи валидируются по новой схеме
- Существующие записи **не модифицируются** (их данные остаются как есть)
- Публикуется событие в Kafka — консьюмеры узнают о смене схемы

После publish откатить **нельзя в один клик** — нужен новый draft с
inverse change.

## Что произойдёт при APPROVE → PUBLISH

1. `dictionary_definitions.schema_json` обновляется до новой версии
2. `dictionary_definitions.schema_version` инкрементируется
3. В audit log пишется операция `SCHEMA_PUBLISHED`
4. В Kafka топик публикуется `DictionarySchemaUpdated` event с before/after
5. Redis projection (если включена) cascade-invalidates dependent projections
6. Frontend admin-ui получает push notification (через next polling) →
   баннер «новая схема» появляется у активных операторов

## Типичные паттерны

### Добавление опционального поля

```
- frequency_band: {x-references: "frequency_band.code"}  ← новое, опциональное
```

Обычно **APPROVE**. Существующие записи остаются валидными (поле просто
`undefined`).

### Добавление обязательного поля без default

```
- mass_kg: {type: "number"}  ← новое, в required
```

Обычно **REQUEST CHANGES** — либо `default`, либо `nullable`, либо
сначала миграция данных.

### Изменение `x-references`

```
- type: {x-references: "satellite_type.code"}
+ type: {x-references: "satellite_type_v2.name"}
```

**REQUEST CHANGES** в подавляющем большинстве случаев. Если справочник
переименован — нужно migration data, не просто schema swap.

### Расширение `enum`

```
- status: {enum: ["ACTIVE", "INACTIVE"]}
+ status: {enum: ["ACTIVE", "INACTIVE", "MAINTENANCE"]}
```

**APPROVE** — обратно совместимо.

### Сужение `enum`

```
- status: {enum: ["ACTIVE", "INACTIVE", "MAINTENANCE"]}
+ status: {enum: ["ACTIVE", "INACTIVE"]}
```

**REJECT** или **REQUEST CHANGES** — записи со значением `MAINTENANCE`
станут невалидны. Нужна data migration сначала.

### Удаление поля

```
- legacy_id: {type: "string", x-unique: true}
```

Опасно. Спроси:
- Кто-то использует это поле в downstream?
- Что мы делаем с данными в нём?
- Не лучше ли его deprecate (оставить, не использовать)?

Обычно **REQUEST CHANGES** с обсуждением.

## Если ошибся

Ревьюеры — люди. Если approve'нул и потом понял что зря:
1. **Если PUBLISH ещё не произошёл** — попроси автора `WITHDRAW`, потом
   `REJECT` или создать новый
2. **Если PUBLISH произошёл** — создай **новый schema draft** с inverse
   change (откати изменение), пройди ревью заново
3. **Документируй в audit** — комментарий «откатываю, потому что X» —
   важная информация для будущих ревьюеров

Это не катастрофа, но и не норма. Если ошибаешься > 1 раза в квартал —
стоит пересмотреть процедуру ревью (может, нужен mandatory peer-review).
