# Запуск финансового выпуска 0.5.1 на сайте — порядок действий

Переключение выполняется в два развёртывания: сначала загрузка нового среза рядом с работающим, затем переключение. Сначала рабочий API продолжает отдавать запечатанный 0.5.0, а 0.5.1 загружается в собственное пространство таблиц `rf04fde3f_` через временный маршрут `/api/staging/`. Только после полной сверки второе развёртывание делает 0.5.1 активным и убирает маршрут загрузки. Аудитория сайта не меняется: доступ остаётся закрытым, как раньше.

Развёртывание, секреты и запросы к рабочему сайту выполняет владелец. Ключи вводятся только в скрытый stdin загрузчика; в файлы, аргументы и журналы они не попадают.

## 0. Предусловия

- Рабочий сайт отдаёт 0.5.0: `GET /api/database/status` → `state: ready`, `data_version: e43569f3…83f1`.
- Есть checkout опубликованного коммита (UI 0.13.0, данные 0.5.0) и этот пакет 0.5.1.
- Python 3.12+, Node.js 22+ и для загрузчика `aiohttp` (`pip install aiohttp`).
- На D1 есть место ещё под ~1,95 млн строк. Пространства 0.4.3 и 0.5.0 не удаляются.

Локальная репетиция обеих фаз (рабочий 0.5.0 → загрузка 0.5.1 → переключение) уже выполнена:

```bash
python3 tests/prepare-staging-rehearsal.py
node tests/staging-rehearsal.mjs        # tests/staging-rehearsal-validation.json: 10/10 PASS
```

## 1. Фаза 1 — развёртывание с маршрутом загрузки

```bash
python3 launch/apply_phase.py phase1 /path/to/live-checkout
```

Инструмент сначала проверяет, что checkout — проверенный рабочий 0.5.0: SHA-256 `db/runtime-manifest.json`, `lib/sql-api.mjs`, `db/schema.ts` и журнала миграций. Затем копирует файлы из `launch/phase1-staging/`:

| Файл | Назначение |
|---|---|
| `drizzle/0003_financial_0_5_1.sql`, `drizzle/meta/*` | создают 42 таблицы, 5 представлений и 5 индексов `rf04fde3f_`; без DROP |
| `db/schema.ts`, `db/releases/base-schema-0-5-0.ts` | граф схемы сохраняет 0.4.3 и 0.5.0 |
| `lib/sql-api-core.mjs`, `lib/sql-api.mjs` | тот же API, отдельный экземпляр на каждый манифест; активный манифест остаётся 0.5.0 |
| `lib/sql-api-staging.mjs`, `app/api/staging/[...path]/route.ts` | временный маршрут `/api/staging/`: только загрузка и чтение 0.5.1, расчёты запрещены |
| `db/next-runtime-manifest.json` | манифест 0.5.1, `manifest_sha256 e3b149a1…3551` |

Сборка `npm run build` должна показать маршруты `/api/:path+` и `/api/staging/:path+`. После развёртывания проверить:

- `GET /api/database/status` → по-прежнему 0.5.0, `ready`;
- `GET /api/staging/database/status` → `data_version f04fde3f…`, `state: uninitialized`, `expected_chunks: 541`.

## 2. Загрузка 0.5.1

Нужны пакеты `work/sql-seed/00000.json…00540.json` и `work/sql-seed/manifest.json` из этого пакета; манифест совпадает с `db/runtime-manifest.json` побайтно.

```bash
python3 scripts/seed_site_database_async.py https://<адрес сайта> \
  --manifest db/runtime-manifest.json --seed-dir work/sql-seed \
  --api-prefix staging/ --workers 6 --report tests/staging-0.5.1-live-import.json
```

Загрузчик запрашивает ключ обхода и ключ импорта скрыто. Каждый пакет сверяется по SHA-256 и после записи читается обратно по всем полям. Повтор пакета допустим: ранее подтверждённый вернёт `already_verified`. `finish` проверяет число строк всех 42 таблиц, внешние ключи и метаданные, затем запечатывает выпуск (`ready`). После этого маршрут отвечает 403 на любую попытку загрузки.

Сверка загруженного среза до переключения. Скрипт сравнивает 290 историй со статическими выгрузками 0.5.1, а также каталог и денежную модель. Манифест и выгрузки он берёт из этого пакета, ключ обхода читает из скрытого stdin.

```bash
python3 tests/live-sql-api.py https://<адрес сайта> --api-prefix staging/ --report tests/staging-0.5.1-live-api-validation.json
```

## 3. Фаза 2 — переключение

Развернуть дерево этого пакета (данные 0.5.1, UI 0.13.0). Перед развёртыванием проверить:

```bash
python3 launch/apply_phase.py verify-phase2 .
```

Проверка требует, чтобы активный манифест был 0.5.1, временные файлы загрузки были удалены, миграция 0003 оставалась на месте, а `public/data/catalog.json` был выгрузкой 0.5.1.

После развёртывания:

- `GET /api/database/status` → `0.5.1`, `ready`, 541/541 пакетов, 1 946 772 строки;
- заголовок `x-portfoliolab-data-version: f04fde3f…` во всех ответах API;
- `GET /api/staging/...` → 404;
- старые страницы с данными 0.5.0 получают при расчёте 409 «Версия страницы или данных изменилась» и должны обновиться — это штатное поведение;
- `python3 tests/live-launch.py https://<адрес сайта> --quick` (секрет только через скрытый stdin).

Затем отозвать временный ключ импорта. Независимо от ключа запечатанные срезы закрыты для загрузки состоянием `ready`.

## 4. Откат

Откат — повторное развёртывание прежнего коммита 0.13.0 / 0.5.0. Таблицы 0.5.0 не изменялись (это проверено в репетиции по хешам манифеста), поэтому прежний код сразу работает со своим пространством. Строки готовых срезов не перезаписываются и не удаляются.

## 5. Чек-лист перед открытием

| Пункт | Статус |
|---|---|
| Финансовая база 0.5.1 собрана и независимо проверена (16/16) | готово |
| SQL-проекция и миграция 0.5.1, загрузка и чтение 541 пакета локально | готово |
| Репетиция двух фаз запуска | готово, 10/10 |
| Все контроли сайта на данных 0.5.1 | PASS, кроме не запускавшихся из-за отсутствия входов (см. RELEASE_0_5_1_RU.md) |
| Загрузка в рабочий D1 и HTTP-сверка | выполняет владелец по разделам 1–3 |
| Браузерная приёмка на телефоне и компьютере | не выполнена; статические и DOM-проверки её не заменяют |
| Нагрузочная проверка | не выполнена; лимит пилота — один активный расчёт на isolate |
| Права на использование данных (MOEX, ЦБ, агрегаторы, okama) для выбранной аудитории | не подтверждены |
| TCSG/AGRO RUB, неполные годовые реестры, правила 15 БПИФ, `available_at` | открыты, раскрыты в методике |
| MSTT: невыясненная стоимость выделения «Дорог и Мостов» (2020) | раскрыто |
| Систематическая сверка одиночных выплат с сообщениями НРД | следующий шаг |

Без отдельного решения владельца аудитория не меняется и публичный доступ не открывается.
