# Инструкции для LLM-агента

Вы получили проверяемый пакет исследования «Полотно: карта расселения
Беларуси по сетке 1 км, 1975–2050» (INF-15 `grid`, v1.0.0). Ваша роль —
**независимый аудитор**: проверить источники, воспроизвести расчёт,
проверить самые сильные допущения и убедиться, что честность подачи
результата (в первую очередь провал G-3 и статус C-1/C-2) не потерялась
где-либо в тексте пакета.

## 1. Контекст

Главный результат пакета — **не одно число, а раздвоенный вердикт**:

- **C-1** (`population_weighted_density` растёт даже когда обычная плотность
  падает) и **C-2** (`area_share_below(5)` растёт монотонно 1975→2050, не
  объясняется одним Полесьем/Чернобылем) — статус **`open_question`**
  (`claims.yaml`), НЕ «подтверждено». Причина — провал перекрёстной проверки
  независимым сенсором G-3 (согласие знака население/огни 2012→2020 = 32,8 %
  при пороге 70 %, `docs/decisions/INF-15.md` D-007). Это результат
  предписанного разделом 7 ТЗ (проверка 3) fallback'а, применённого
  буквально, а не смягчения формулировки.
- **C-3** (`network_per_capita` выше там, где депопуляция района сильнее)
  — статус **`verified`**: Spearman ρ = **−0,7508** (p ≈ 1,25×10⁻²²,
  118 районов), и проверка полноты карты дорог G-6 пройдена
  (OSM 76 958,8 км против официальных 86 446 км, Δ = −10,97 %, допуск ±15 %).

Каноническая сводка чисел — `data/final/computed_results.json` (та же схема,
что у `by-maps-urban-overhang`: плоский список `{metric, value}`); контрольные
значения с допусками — `checks/expected_results.json` (20 метрик, покрывают
G-1…G-8, раздел 7 ТЗ); все допущения — `params/assumptions.yaml`; границы
применимости — `LIMITATIONS.md`. Пути ниже — относительно корня пакета
`artifacts/grid/`.

Ключевая структурная особенность этого пакета (в отличие от `urban-overhang`):
клеточные слои (`sources/raw/rasters/*.tif`) и финальный `data/final/grid.json`
— большие бинарные/JSON-артефакты, материализуемые сборкой манифеста
(`python -m etl.artifacts.build grid`) из `data/raw/grid/rasters_constrained/`
и `web/public/data/grid.json` основного репозитория. Если в вашей копии
пакета этих файлов физически нет — это не дефект: быстрый режим
воспроизведения (§3 ниже) их не требует, а полный растровый режим
регенерирует их с нуля.

---

## 2. Задача 1 — проверка источников

Два уровня происхождения (см. `sources/registry.csv`):

1. **Завендоренные агрегаты** (`sources/raw/*.json`, `*.csv`,
   `data/final/computed_results.json`) — самодостаточны для быстрого
   воспроизведения, сеть не требуется.
2. **Не вендоренные первичные данные** — 40 глобальных тайлов GHS-POP R2023A
   (~190 МБ, `sources/raw/registry_ghsl_pop.csv`, по 4 тайла на каждую из
   10 эпох, все 40 строк содержат sha256). При наличии сети:

   ```bash
   cd artifacts/grid
   python3 code/extract/grid_fetch.py    # запускать из корня основного
                                          # репозитория (см. README.md);
                                          # идемпотентно, сверяет sha256
   ```

   Несовпадение sha256 тайла — зафиксируйте как расхождение (это
   версионированный, редко обновляемый продукт JRC, в отличие от «живого»
   снимка OSM в пакете `urban-overhang`). Дополнительный источник —
   `sources/raw/ghsl_pop_input_metadata_belarus_extract.txt`: точная
   выдержка метаданных JRC, подтверждающая находку `docs/decisions/INF-15.md`
   D-005 (референсная перепись 2009 года в продукте = 10 018 692, что на
   5,4 % выше настоящего официального итога 9 503 807) — сверьте цифру
   10 018 692 в этом файле буквально, это не пересказ, а прямая выдержка.
3. **Официальная статистика протяжённости дорог** (G-6) —
   `sources/raw/registry_road_stats.csv`: РУП «Белдорцентр» (86 446 км, на
   01.01.2026, <https://beldor.centr.by/en/2026/03/stata2026/>) и сверочный
   источник Минтранс РБ (86 538 км). Расхождение двух официальных источников
   между собой — 0,1 %, не влияет на исход G-6 (допуск ±15 %).
4. **Снимок OSM (INF-04)** — `sources/raw/network_by_raion.csv`, агрегат уже
   готового снимка Geofabrik `belarus-latest.osm.pbf` (обращение
   2026-07-11); полный PBF в пакете не вендорится (см. `sources/registry.csv`,
   `inf04_osm_road_graph`).

---

## 3. Задача 2 — воспроизведение

```bash
cd artifacts/grid
bash code/run.sh          # Python >= 3.10, без внешних зависимостей, секунды
```

Скрипт (1) пересчитывает `data/final/computed_results.json` из `sources/raw/`
(`code/build.py`), (2) прогоняет инварианты (`checks/tests/test_invariants.py`
— в т.ч. явную защиту от регресса: тест **упадёт**, если `g3["pass"]`
когда-нибудь станет `True` без обновления `docs/decisions/INF-15.md`), (3)
сверяет с `checks/expected_results.json` (`code/verify.py`, 20 метрик).
Проверьте код выхода (0 = совпало).

**Полное воспроизведение** (растры GHS-POP → клетки → метрики → прогноз →
кадры) требует `rasterio`/`numpy`/`scipy`/`Pillow` (`requirements-raster.txt`
в корне основного репозитория) и запускается из корня основного репозитория
пятью модулями `etl.grid_fetch` → `etl.grid_extract` → `etl.grid` →
`etl.grid_project` → `etl.grid_frames` (плюс независимый `etl.grid_network`
для G-6/C-3) — точные команды в README.md. Без сети и без тяжёлых
зависимостей ограничьтесь быстрым режимом — он самодостаточен для проверки
всей арифметики контрольных чисел.

---

## 4. Задача 3 — стресс допущений

Список самых сильных допущений пакета — со ссылками на
`params/assumptions.yaml` и `LIMITATIONS.md`. Проверьте каждое: воспроизведите
описанным путём, посмотрите, что «плывёт», а что нет.

1. **Constrained dasymetric калибровка (D-005) — сумма клетки района ЖЁСТКО
   приравнена к официальному ряду.** Сырой GHS-POP не проходит сверку G-1:
   до калибровки 119 из 119 районов хоть раз выходят за допуск ±3 % (по
   отдельным эпохам — от 40 до 111 из 118–119), национальная сумма вне
   допуска ±1 % на всех 10 эпохах (реально +1,7 %…+3,4 %, точные числа —
   `sources/raw/metrics_observed.json → national → raion_reconciliation_national`).
   Решение: клетки района домножаются на `official(epoch)/raw_grid_sum(epoch)`
   — GHS-POP остаётся источником ТОЛЬКО внутрирайонного пространственного
   рисунка, итог района становится тождественно равен официальной цифре.
   **Стресс-тест:** сравните `national` (constrained) и `national_raw`
   (сырой, до калибровки) в `sources/raw/metrics_observed.json` — оба ряда
   `area_share_below(5)` растут монотонно и расходятся <0,5 п.п. на каждой
   эпохе, `population_weighted_density` расходится на 4–11 %, но направление
   (рост) и разрыв с падающей `arithmetic_density` сохраняются в обеих
   версиях. Если бы raw и constrained давали разный ЗНАК тренда — C-1/C-2
   были бы артефактом одной лишь калибровки; это не так, но подрайонный
   confound «город-хозяин переоценен / пригород недооценен» калибровкой
   сумм по району не устраняется — см. пункт 3 ниже и `LIMITATIONS.md`.
2. **Вариант Б прогноза («пригороды/периферия») — полностью стилизованное
   допущение, не измерение.** Параметры `params/assumptions.yaml →
   forecast`: 7 «якорей» (крупнейшие городские ядра, плотность ≥1500 чел/км²
   в 2020, найдены автоматически связными компонентами, не по списку
   названий), пригородный буст до **+35 %** доли клетки к 2050 году
   (треугольная функция 8→20→45 км от якоря), периферийный штраф до **−25 %**
   дальше 60 км от ближайшего якоря (`docs/decisions/INF-15.md` D-006).
   Коэффициенты 0,35/0,25 и километры — экспертный выбор, не подгонка под
   данные и не результат регрессии. **Стресс-тест:** сравните
   `national.area_share_below.5` для ключей `2050:base:A` (0,8492) и
   `2050:base:B` (0,8478) в `sources/raw/metrics_forecast.json` — разница
   <0,15 п.п. на национальном уровне (варианты расходятся ВНУТРИ района, не
   в национальной сумме — G-2 фиксирует районные итоги идентичными по
   построению для обоих вариантов). Не путайте это с «прогноз не зависит от
   допущения» — на уровне отдельной клетки/малого региона разница A/Б
   заметно больше.
3. **Полесский operational-proxy список (D-004) — административный, не
   строгий физико-географический список.** 27 районов (16 Брестской области
   + 11 южных районов Гомельской, прилегающих к бассейну Припяти,
   `params/assumptions.yaml → polesye_operational_list`) выбраны потому, что
   у Полесья нет единого общепринятого перечня районов (5 физико-
   географических областей без официальной привязки к текущим границам).
   **Стресс-тест:** список пересекается, но не совпадает со списком
   чернобыльских районов (`data/curated/chernobyl_zones.csv`, 12 районов,
   `class ∈ {1,2}`) — объединение для G-4 даёт **36** районов (не 27+12=39,
   значит 3 района входят в оба списка). Проверьте самостоятельно:
   `sources/raw/metrics_observed.json → national.validation` (если
   присутствует) или пересчитайте объединение по двум спискам — количество
   исключённых должно быть 36.
4. **G-6/C-3 — сравнение «итог против итога», без классового сопоставления
   OSM/официальных категорий.** Снимок OSM (INF-04) содержит только 5
   классов (motorway/trunk/primary/secondary/tertiary + `_link`) — БЕЗ
   residential/unclassified/track; официальная статистика Белдорцентра
   включает ВСЕ дороги общего пользования. «Республиканские» ≠
   motorway/trunk и «местные» ≠ primary/secondary/tertiary один в один —
   это разные (административная vs функциональная) классификационные шкалы,
   поэтому сравнение сделано только по суммарному итогу, не по классам
   (`sources/raw/network_metrics.json → g6.note`). **Стресс-тест:** если
   снимок неполон (а он неполон — нет местных дорог), OSM-итог мог бы только
   вырасти, то есть отклонение приблизилось бы к нулю, а не ушло за допуск —
   односторонний риск работает В ПОЛЬЗУ прохождения G-6, не против.
   Проверьте разбивку по классам в `sources/raw/network_metrics.json →
   g6.osm_total_km_by_class` — tertiary даёт ≈64 % длины снимка.
5. **Интерполяция официального ряда района к узлам эпох GHSL — только
   2005 и 2015 совпадают напрямую с годом ряда `data.json`.** Остальные 8
   наблюдаемых эпох (1975, 1980, 1985, 1990, 1995, 2000, 2010, 2020)
   получают официальную численность линейной интерполяцией между ближайшими
   точками ряда (`etl/grid.py::interp_official`, экстраполяция за пределы
   диапазона не выполняется). Это влияет и на итог калибровки (допущение 1),
   и на трек центра масс.
6. **Ошибка возмущения G-5 обязана быть дифференцированной по районам, не
   общим множителем.** Проверка устойчивости центра масс перебирает ±10 %
   случайное (seed=42) возмущение веса КАЖДОГО района отдельно — равномерное
   масштабирование всех весов на одну константу математически не меняет
   средневзвешенную точку и было бы бессмысленной проверкой (это исправлено
   до публикации, см. комментарий `g5_perturbation_pct` в
   `params/assumptions.yaml`). Убедитесь, что при самостоятельном повторении
   G-5 вы используете дифференцированное, а не общее возмущение.

---

## 5. Формат отчёта

Верните машиночитаемый отчёт (не пересказ):

```json
{
  "artifact_id": "INF-15",
  "artifact_version": "1.0.0",
  "environment": { "python": "", "os": "", "network_available": false },
  "reproduction": {
    "status": "pass|partial|fail",
    "checks": [
      { "id": "sources_ghsl_sha256", "result": "pass|fail|skipped", "note": "" },
      { "id": "quick_repro",         "result": "pass|fail", "note": "code/run.sh exit code" },
      { "id": "expected_results",    "result": "pass|fail", "note": "N/20 метрик в допуске" },
      { "id": "g1_calibration_stress",  "result": "pass|fail", "note": "raw vs constrained direction match" },
      { "id": "g2_variant_ab_stress",   "result": "pass|fail", "note": "нац. разница A/Б на 2050, п.п." },
      { "id": "g4_polesye_union_36",    "result": "pass|fail", "note": "" },
      { "id": "g6_class_mismatch_noted","result": "pass|fail", "note": "" },
      { "id": "g5_perturbation_check",  "result": "pass|fail", "note": "" }
    ]
  },
  "claims_audit": [
    { "claim_id": "C-001", "declared_status": "open_question", "verified_as_declared": true, "note": "" },
    { "claim_id": "C-002", "declared_status": "open_question", "verified_as_declared": true, "note": "" },
    { "claim_id": "C-003", "declared_status": "verified", "verified_as_declared": true, "note": "" }
  ],
  "unsupported_claims": [
    { "type": "independent_census|causal|precise_cell|financial|road_history",
      "file": "", "quote": "", "severity": "critical|major|minor" }
  ],
  "severity_summary": { "critical": 0, "major": 0, "minor": 0 },
  "final_verdict": "reproducible | reproducible_with_caveats | not_reproducible"
}
```

Отдельно найдите и процитируйте нарушения запрещённых формулировок раздела 8
пререгистрации (`PREREGISTRATION.md`): «карта показывает точное число жителей
ячейки», «независимая оценка численности населения» о сеточных данных,
«дорожная сеть выросла/сократилась» без оговорки «снимок 2026 года, не
история», денежные/бюджетные формулировки, причинные глаголы без причинного
дизайна («миграция вызвала»), точность отдельных ячеек в малозаселённых
зонах, маркировка прогнозных клеток (2026+) как наблюдений.

---

## 6. Границы (что пакет НЕ утверждает)

- **G-3 провалена: C-1 и C-2 НЕ утверждаются как подтверждённый факт.**
  Пакет утверждает только, что *в сетке видно* растущую
  `population_weighted_density` и растущую `area_share_below(5)` — но
  независимая проверка ночными огнями не подтверждает направление изменения
  для большинства районов (32,8 % согласия при пороге 70 %). Любое
  прочтение README/METHODS/EXECUTIVE_BRIEF как «доказано, что люди
  концентрируются быстрее, чем пустеет территория» — ошибка чтения; пакет
  говорит «данные по клеткам указывают на это, но независимая проверка не
  подтверждает» — и это разные по силе утверждения.
- **Карта — не перепись и не альтернативный подсчёт населения.** Итоги по
  району/стране тождественно равны официальным (калибровка), поэтому
  сверка G-1 после калибровки — это НЕ эмпирический тест, а проверка
  корректности реализации (`sources/raw/metrics_observed.json →
  reconciliation_summary.calibration_applied`).
- **Отдельные клетки в малозаселённых/эвакуированных зонах не следует читать
  как точные числа** — известное искажение продукта, не ошибка расчёта
  (пример: Брагинский район, отклонение сырого продукта до +209,78 % на
  отдельной эпохе).
- **Прогноз — не альтернатива наблюдению.** Оба варианта A/Б — модельные
  допущения (маркировка `dtype: "f"`/[MODEL]), не диапазон точности; «правда
  может оказаться вне обоих» (раздел 8 ТЗ, п.3).
- **Дорожная сеть — снимок на 2026 год, не история.** `network_per_capita`
  нельзя пересказывать как «сеть выросла/сократилась»; корреляция C-3 не
  является причинным утверждением («депопуляция вызвала навес сети»
  запрещена — это корреляция, отчасти механическая по построению метрики,
  см. `docs/notes/grid_network_findings.md` раздел 3).
- **Денежного/бюджетного слоя в этом исследовании нет вообще** — «метры
  сети на жителя» не финансовый показатель; слово «стоимость» допустимо
  только как метафора обслуживания инфраструктуры.
