# Инструкция для ИИ-агента: аудит пакета INF-08 v2.1 «Беларусь из космоса, 1992–2075»

Ты работаешь с проверяемым пакетом исследования. Всё, что заявлено на
лендинге, обязано воспроизводиться отсюда одной командой.

## Быстрый старт

```bash
bash code/run.sh   # Python >= 3.10, стандартная библиотека, ~10 c
```

Ожидаемый финал: строка `Все 21 контрольных метрик воспроизведены в
допусках.` и затем `ГОТОВО: INF-08 v2 воспроизведён и совпадает с
заявленным.`

**Сразу главная ловушка**: во всех зональных CSV
(`zonal_dmsp/vnl/simviirs.csv`) кроме 119 зон есть строка
`zone_id=BY` — сумма по всему клипу страны (она шире объединения зон
на приграничные пиксели). При любых собственных пересчётах её нужно
исключать, иначе мост даст n=1307 вместо 1296, а стык уедет на
десятки процентов.

## Три задания (обязательная программа аудита)

### Задание 1. Источники и лицензии

`sources/registry.csv` — 83 записи: для каждого года глобальный файл
(URL, дата обращения, лицензия, sha256) и вырезка по Беларуси (sha256).
Проверь: (а) sha256 всех 35 вендоренных вырезок
`data/raw/nightlights/rasters/*.tif` совпадают с реестром (это делает и
`checks/tests/test_invariants.py`); (б) лицензии совместимы с CC BY 4.0
(Li et al. — CC BY 4.0; зеркало OpenGeoHub — CC BY 4.0; EOG VNL —
свободное использование с цитированием); (в) при доступе в сеть — скачай
1–2 глобальных файла по URL из реестра и сверь их sha256 (полные файлы
30–80 МБ, анонимно).

### Задание 2. Зональные суммы и стык в допусках

Воспроизведи зональные суммы и стык независимо от нашего кода:
(а) прочитай пару вырезок растров своим кодом (rasterio/GDAL, при
наличии) и пересчитай зональную сумму 2–3 районов — должна совпасть с
`zonal_dmsp.csv`/`zonal_vnl.csv` (растеризация по центру пикселя, Минск
поверх, порог VNL ≥ 1 нВт); (б) на stdlib пересчитай мост: регрессия
ln(VNL) на ln(simVIIRS) c FE года по 2014–2024 — b ≈ 0,915, R² ≈ 0,92
(гейт ≥ 0,9); (в) пересчитай стык: коррекция F18 (средние DN 2006–2009
к 2010–2013 ≈ ×0,486), отображение ретро-уровней, сравнение средних
4-летних окон по обе стороны склейки — разрыв ≈ −3,0 % (гейт ±5 %;
конвенция знака: (ретро-сторона − VNL-сторона)/VNL-сторона, т. е.
ретро чуть тусклее; при расчёте «в другую сторону» получится
зеркальное +3,1 %).
Числа и чувствительность к окнам — `docs/notes/nightlights_v2_validation.md`
(регенерируется шагом 1/6 run.sh).

### Задание 3. Стресс-тест модели будущего

Модель: light(t) = bright₂₀₂₄·(pop(t)/pop₂₀₂₄)^β + floor (см.
`params/assumptions.json`). Измени параметры и покажи чувствительность
картины 2075: (а) β по классам ± SE (industrial 1,60±0,18, rural
1,66±0,15) и крайний вариант β=1 для всех; (б) порог floor 5 нВт → 3 и
10 нВт (пересчитай floor/bright из `vnl_2024.tif` или интерполируй);
(в) перенеси 3–5 районов между классами (например, облцентры →
industrial). Ожидаемая устойчивость: качественная картина (агломерация
держится, периферия тускнеет, разрыв сценариев ~20–25 п.п. нац. света к
2075) сохраняется; количественно нац. свет 2075 в базовом сценарии
меняется в пределах ~±10 %. Если найдёшь конфигурацию, ломающую
качественную картину, — это находка, о ней нужно сообщить.

## Что ещё проверять (v2.1: визуальный и событийный слои)

0. **Слои разделены**: числа/события — только из аналитических рядов;
   визуальная реконструкция 1992–2011 (шаблон VNL-2012 × зональные
   факторы) не годится для точных локальных выводов — проверь, что
   события в nightlights_events.json подтверждаются zonal-рядами, а не
   пикселями PNG. Кадры не вендорены — их sha256 зафиксированы в
   nightlights_manifest.json, регенерация: python -m
   etl.nightlights_visual && python -m etl.nightlights_delta
   (rasterio+numpy+Pillow). События: python -m etl.nightlights_events
   (stdlib, смежность — data/raw/nightlights/adjacency.json) — прогон
   в run.sh, детерминирован.


1. **Доли, не уровни**: главная метрика — доля зоны в национальном
   свете; уровни между эрами продуктов не сравнивать (VNL 2021+ даёт
   версионный скачок, ретро — радианс-эквивалент по калибровке).
2. **Индекс v1 своим кодом**: для 2–3 крупных районов пересчитай
   лог-линейный тренд долей 2015–2019 и отношение к 2022–2023 по
   `zonal_vnl.csv`; Минск ≈ 0, Смолевичский/Солигорский < −0,4.
3. **Детерминизм**: два прогона run.sh — байт-в-байт одинаковый
   `nightlights_v2.json`.
4. **Маркировка модели (T-13)**: `web/public/data/nightlights_v2.json`
   отдаёт будущее только под ключом `model`; кадры будущего (в репо
   сайта) несут впечатанный бейдж «МОДЕЛЬ» + штриховую рамку.

## Ловушки

- Растровые шаги (`etl/nightlights_fetch.py`, `etl/nightlights_zonal.py`,
  `etl/nightlights_frames.py`, `tools/render_reel_space.py`) требуют
  rasterio/numpy/Pillow и в run.sh НЕ вызываются; stdlib-конвейер
  стартует с вендоренных зональных CSV.
- **Свет ≠ население**: индекс — маркер, не оценка численности;
  расхождение равно объясняется деиндустриализацией, LED, экономией.
- **Модель — не прогноз света**: это иллюстрация прогноза населения при
  прочих равных; санкции/энергетика/технологии не моделируются.
- **Ретро грубее**: кросс-сенсорная точность зонального уровня на
  перекрытии (R² ln-долей ~0,78) — потолок метода (собственный стык
  продукта Li — те же 0,777); районная детализация надёжна с 2012 года.
- Прямое перекрытие 2012–2013 для стыковки непригодно (худшие годы обоих
  сенсоров) — поэтому мост через simVIIRS; не «улучшай» стык подгонкой
  константы на 2012–2013.
- Факелы двух НПЗ обнулены дисками 2,5 км в ОБОИХ сегментах
  (`assumptions.json: deflare`) — не электрический свет; в вырезках их
  уже нет, глобальные файлы их содержат.
- Минск (BY-HM) — отдельная зона из adm1; Минск и Минский район — два
  ярчайших источника, меняются местами — это не дефект.
- Островецкий район (id зоны `r-astraviecki`): самый быстрый рост
  света 1992→2024 (БелАЭС) — реальный объект, не артефакт.
