arm-ka-fixtures/docs/superpowers/specs/2026-11-12-synthgen-design.md
Andrey Epifancev 03edcb4fdb Спека synthgen: решения 13–14 (рулинги контроллера)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-12 23:33:11 +04:00

36 KiB
Raw Blame History

Настраиваемый генератор синтетики ГСК — проект

Issue: RSHB/arm-ka-fixtures#38. Ветка feat/38-synthgen. Дата: 12.11.2026.

1. Зачем

Для ручного тестирования, демонстрации и проверки на объёме нужен набор первички группы компаний, который выглядит как выгрузки из 1С и внутренне согласован: ОСВ по счетам, ББ и ОФР каждой компании построены из одних и тех же проводок, поэтому сходятся между собой, а операции между компаниями группы дают пары ВГО и шахматку, на которые есть что посмотреть. Набор должен генерироваться скриптом под нужный размер, а не собираться руками.

Реальные фикстуры (osv/rb, osv/schastl, osv-dkb/) — две-три компании, неполные периоды, реальные ИНН. Контрольный набор #35 (synthetic-group/, 7 компаний, ручные сценарии, оракул) решает другую задачу — проверку алгоритмов на известных случаях — и для объёма не годится: состав фиксирован, контрагентов единицы, оформление не похоже на 1С.

2. Цели, не-цели, анти-цели

Цели

  • Один скрипт генерирует ОСВ, ББ, ОФР по всем компаниям и периодам и файл состава ГК для импорта ЕЦП.
  • Семь настроек (§3) из конфигурационного файла с переопределением из командной строки.
  • Файлы по виду — выгрузки 1С (§6).
  • Данные согласованы (§5.6) и детерминированы: одинаковые настройки и seed → побайтно одинаковый набор.

Не-цели (в этой задаче)

  • Оракул, ожидаемые результаты (expected/), ручные сценарии вида R-/P-/V-/G-/S- из #35.
  • Сканы PDF и ответы ИС Сканирование.
  • ББ/ОФР по ФСБУ 4/2023 и упрощённые формы — только 66н.
  • Компании ГК вне ГСК, несколько ГК в одном прогоне, расшифровки, КФО клиента.
  • Загрузка на стенд (для этого есть load.py в #35; приспособить его — отдельная задача).

Анти-цели (провал, даже если «работает»)

  • Реальные ИНН или имена реальных организаций в наборе (в том числе остатки реквизитов из образцов 1С, §6.2).
  • ОСВ, не сходящаяся с ББ/ОФР той же компании; ББ, где 1600 ≠ 1700.
  • Набор, который парсер tabular-parser (ОСВ) или разбор ББ/ОФР в Excel не принимает из-за оформления генератора.
  • Пересечение компаний двух прогонов с разными префиксами на одном стенде (имя или ИНН).

3. Настройки

Файл YAML (--config), любой ключ переопределяется --set ключ=значение. Неизвестный ключ или значение вне диапазона — ошибка до начала генерации.

# Ключ Смысл По умолчанию Диапазон
1 group_size число компаний в ГК (все входят в ГСК) 5 2–40
2 prefix префикс ГК: добавляется к названию группы и её компаний (§4.2), задаёт диапазон ИНН компаний группы SYN 2–12 символов, буквы/цифры, без пробелов
3 periods число отчётных периодов 4 1–12
4 start первый период, ГГГГ-Qn 2025-Q1 2020-Q1 … 2030-Q4
5 osv_counterparties_mean среднее число внешних контрагентов в одной ОСВ (матожидание, §5.3) 60 0–150 (ограничено словарём, решение 9)
6 no_inn_companies сколько компаний группы выгружают ОСВ без ИНН контрагентов 1 0–group_size
7 vgo_mismatch_share доля пар ВГО с расхождением сторон 0.1 0–1
— seed зерно случайности 1 целое
— out каталог результата (или --out) ./out/<prefix> —

Пример: uv run python -m synthgen --config synthgen/examples/default.yaml --set group_size=12 --set prefix=DEMO2 --out /tmp/demo2.

4. Словарь компаний и состав группы

4.1 Словарь

Закоммиченный файл synthgen/dictionary/companies.csv: 400 вымышленных организаций — name;form;inn;kpp;kind (form ∈ ООО/АО/ПАО/ИП; kind — отрасль для правдоподобия: агро, торговля, логистика, производство, услуги, финансы, строительство). Около 10 % — ИП (ФИО + «ИП», ИНН 12 знаков, КПП пусто). Имена собираются один раз детерминированным скриптом synthgen/dictionary/build.py из списков корней/отраслевых слов и проверяются на совпадение с реальными: имена не должны совпадать с организациями из fixtures/directories/ и фикстур ОСВ. Из 400 записей 30 — банки (kind=банк, АО/ПАО): они — кредиторы по 66/67 и только там. ИНН — как в #35: регион 00 (не существует), верная контрольная сумма; номера словаря — диапазон 0100001–0109999 (не пересекается с ИНН #35, где номера 1–99, решение 10).

Внешние контрагенты всех ОСВ берутся из словаря. Компании группы — тоже из словаря (§4.2), и такая запись в этом прогоне внешним контрагентом не бывает.

4.2 Компании группы и префикс

  • Из словаря выбираются group_size записей с form ∈ ООО/АО (не ИП); головная — АО.
  • Имя компании группы: <форма> "<prefix> <имя из словаря>", например АО "DEMO2 Северная Нива". Название ГК: ГК <prefix>.
  • ИНН компании группы — не из словаря, а из диапазона префикса: номер = 1000000 + 100 × h(prefix) + i, где h — детерминированный хеш префикса (SHA-256 → целое) в 0…89999, i — порядковый номер компании (< 100). Так ИНН компаний группы не совпадают с ИНН словаря, #35 и групп с другими префиксами (коллизия хеша ~1/90000; README её оговаривает).
  • КПП — 9 цифр, как в #35: 00 + двузначный номер компании + 01001 (у ИП КПП нет).

5. Модель данных

5.1 Периоды

Периоды — periods кварталов подряд начиная с start. Каждый отчётный период — нарастающим итогом с начала года, как выгружает 1С: Q1 → «1 квартал», Q2 → «1 полугодие», Q3 → «9 месяцев», Q4 → «год» (ГГГГ-FY). Пример: start=2025-Q3, periods=4 → 2025-Q3, 2025-FY, 2026-Q1, 2026-Q2.

Книга проводок компании ведётся с 1 января года первого периода (входящие остатки датированы 31 декабря предыдущего года проводкой против вспомогательного счёта 000, как в #35) до конца последнего периода. ОСВ показывают 000 только как источник входящих остатков — в файлы ОСВ счёт 000 не выгружается. Переход года: сальдо на 31.12 становится входящим остатком следующего года, ОФР обнуляется закрытием 90/91 на 99 и 99 на 84 (реформация) в последний день года.

5.2 Экономика компании

Каждой компании назначается профиль из отрасли словаря и масштаб (выручка за год — логнормально, медиана ~1 млрд руб., разброс ×10 в обе стороны; головная — холдинг с небольшой выручкой и крупными вложениями 58.01). По профилю генерируются помесячные операции:

  • продажи (62/90), закупки (60/10/41/20), себестоимость (90.02), коммерческие и управленческие расходы (44/26);
  • ОС и амортизация (01/02/08), денежные средства (51), НДС (19/68), налог на прибыль (68.04/99), зарплата (70/69) — минимально, чтобы ББ был правдоподобным;
  • банковские кредиты (66/67 с внешним банком из словаря), прочие расчёты (76), прочие доходы/расходы (91).

Деньги — Decimal, ROUND_HALF_UP, копейки в ОСВ; float только при записи в ячейку.

5.3 Внешние контрагенты в ОСВ

ОСВ генерируются по счетам 01, 58, 60, 62, 66, 67, 76, 80, 90 с субсчетами (§6.1): чек-лист BASIC (01, 58, 60, 62, 66, 67, 76, 90 — docs/context/01-project-overview.md) плюс 80 (уставный капитал — нужен правилу гудвилла 58.01 ↔ 80, ДП §4) (решение 7). Счета 01 и 90 по контрагентам не разворачиваются (01 — по субсчетам, 90 — по субсчетам 90.01/90.02/…/90.09; у компании группы выручка от других компаний группы видна в ОСВ 62 продавца). 80 — по участникам (головная и миноритарий). Для каждой пары (компания, счёт с контрагентами) генератор выбирает число внешних контрагентов n и набор n записей словаря; этот набор постоянен по всем периодам компании (контрагент может иметь нулевое сальдо и обороты в отдельном периоде — тогда строки нет, как в 1С).

n — отрицательное биномиальное с матожиданием mean × w(счёт) (дисперсия ≈ 2 × матожидание), где веса w: 60 — 1.6, 62 — 1.6, 76 — 1.0, 66 — 0.1, 67 — 0.05, 58 — 0.1, 80 — 0 (только участники). Веса нормируются так, что среднее n по всем файлам ОСВ счетов 58, 60, 62, 66, 67, 76 = osv_counterparties_mean. Потолки: 60/62/76 — словарь без банков и компаний группы (≈ 370 − group_size), 66/67 — 30 банков. Если потолок срезал выборку, недобор перераспределяется на остальные файлы того же счёта; тест среднего (допуск ±15 % при числе файлов ≥ 20) применяется, когда потолок не сработал ни разу, иначе README прогона пишет «среднее ограничено словарём» (решение 9).

Обороты распределяются по контрагентам по Парето (несколько крупных, длинный хвост мелких), сумма = обороты счёта из экономики §5.2. У части контрагентов (~20 %) только обороты без сальдо, у ~5 % — сальдо без оборотов (старая задолженность). Знак сальдо по контрагенту — по смыслу счёта, у ~3 % в 60/62 — противоположный (аванс/переплата), как в реальных ОСВ.

5.4 Операции внутри группы

  • Головная компания владеет долями всех остальных: 58.01 у головной по каждой дочерней ↔ 80 у дочерней по головной (уставный капитал; доля головной = её часть в 80). Доли 51–100 %, у одной-двух дочерних — миноритарий из словаря (внешний участник, только в 80). Вклады — входящие остатки, движения в периодах нет.
  • Торговля: каждая компания с вероятностью 0.5 продаёт каждой другой (62 у продавца ↔ 60 у покупателя); объём — 5–20 % выручки продавца; расчёты гасятся частично, на конец периода остаётся сальдо.
  • Займы: головная выдаёт займы дочерним (58.03 ↔ 66.03/67.03), с процентами (76 ↔ 76, 91).
  • Дивиденды: дочерние объявляют головной раз в год во 2 квартале (Дт 84 Кт 76.03 у плательщика ↔ Дт 76.03 Кт 91 у получателя — стороны правила ОФР 2310, ДП §4) и выплачивают в том же месяце — на конец периода остатка нет, обороты видны в ОСВ 76 обеих сторон. Так дивиденды не дают пар ВГО по сальдо и не создают ложных односторонних пар; дивиденды в расхождения §5.5 не попадают (решение 11).
  • Каждая операция проводится в двух книгах одновременно симметрично (Дт у одной, Кт у другой — по смыслу счетов).

5.5 Расхождения ВГО

Пара ВГО — (компания A, компания B, вид расчёта: торговля / заём). Доля vgo_mismatch_share пар (округление вниз, но не меньше 1, если доля > 0 и пар ≥ 1) получает расхождение: в последнем месяце первого периода года, где у пары есть сальдо, одна сторона проводит накладную (или начисление процентов), которой нет у другой («в пути»). Величина — доля от меньшей стороны сальдо пары на конец этого периода, вперемешку (решение владельца 12.11.2026): половина пар с расхождением (чётные по порядку выбора) — 1–4 % (ниже порога 5 % ДП §4: ядро сводит по min автоматически), другая половина — 8–25 % (выше порога: пара подсвечивается и ждёт корректировки аналитика). При нечётном числе пар лишняя — крупная. README прогона пишет для каждой пары процент и сторону порога. Расхождение держится до конца года и снимается в январе следующего года проводкой второй стороны (накладная дошла) — в следующем году пара снова выбирается заново. Остальные пары сходятся точно.

5.6 Согласованность (инварианты, проверяются тестами)

  1. В каждой книге Σ Дт = Σ Кт по каждой проводке; сальдо 000 после входящих остатков — ноль.
  2. ОСВ компании за период = свёртка её книги (сальдо на начало, обороты, сальдо на конец) по счёту/субсчёту/контрагенту; строка родителя = сумма детей.
  3. ББ = сальдо книги на конец периода по правилам свёртки счетов в строки 66н (из forms.py #35, §8); ОФР = обороты 90/91/99 за период нарастающим итогом. В тыс. руб. с ROUND_HALF_UP построчно, итоги — суммы округлённых строк; 1600 = 1700 обеспечивается подгонкой ±1 тыс. в одной строке (как _plug в #35). ОСВ — в рублях.
  4. Пары ВГО без расхождения: сальдо у A по B = −(сальдо у B по A) по соответствующим счетам (62↔60, 58.03↔66.03/67.03, 76↔76); с расхождением — разница ровно на сумму накладной «в пути» §5.5 во всех периодах от её проводки до конца года и ноль после января.
  5. Колонка ББ «На 31 декабря» года, предшествующего первому периоду, = входящие остатки книги (= «Сальдо на начало» ОСВ первого года); колонки ранее этого (ББ «на 31 декабря» позапрошлого года, ОФР за прошлый год, если прошлого года нет в книге) — масштабированные значения профиля без требования согласованности (решение 5).
  6. Компания группы в ОСВ другой компании группы записана тем же именем, что в ЕЦП, и тем же ИНН, кроме §6.1 (без ИНН).

6. Вид файлов

6.1 ОСВ

Формат «ОСВ по счёту» 1С 8.3 (БП 3.0) по образцу fixtures/osv/rb/2024-Q1/сч 60_ 1 кв 2024 г.xlsx: лист Лист_1, строки 1–10 — шапка (организация, «Оборотно-сальдовая ведомость по счету NN за <период>», «Выводимые данные…», «Параметры: Валюта БУ, НУ: RUB», двухуровневый заголовок «Счет / Контрагенты», «Сальдо на начало периода / Обороты за период / Сальдо на конец периода», «Дебет / Кредит»), строка 12 — счёт, далее субсчета (уровень группировки 1, отступ 1) и контрагенты (уровень 2, отступ 2), внизу «Итого». Оформление — как в образце: Arial 8 (данные) / 9–10 (шапка) / 12 полужирный (заголовок), заливка шапки F4ECC5, строк счетов и субсчетов F8F2D8, тонкие рамки у всех ячеек таблицы, фиксированные высоты строк, сетка узких колонок A…W с объединением ячеек (колонка «Счет» — A:D, суммы — E:G, H:I, J:K, L:N, O:P, Q:… по образцу), формат #,##0.00, нулевые суммы — пустые ячейки, итог над детьми (summaryBelow=False). Сетку колонок, ширины и объединения генератор берёт из образца программно (читает образец при сборке шаблона), а не набирает вручную.

ИНН контрагента у компаний с ИНН — отдельная колонка после наименования (решение 3), у компаний из no_inn_companies — колонки нет. Парсер ищет колонку ИНН строго между колонкой текста и первой колонкой сумм (tabular-parser/api/src/parser/detect.py, _inn_col), поэтому в ОСВ с ИНН сетка образца сдвигается: наименование A:D, ИНН — новая колонка E (ширина ~11), суммы — со следующей колонки с той же внутренней сеткой объединений, что в образце. Шаблон хранит сетку как «блок текста + опциональная колонка ИНН + блок сумм», а не как фиксированные буквы. В ОСВ без ИНН компании группы записаны: большинство — тем же именем, что в ЕЦП (автоматическое сопоставление по имени), одна компания группы на каждую такую ОСВ-компанию — немного другим написанием (без кавычек и формы в конце, «Северная Нива DEMO2 ООО», как в 1С у реальных контрагентов) — её аналитик сопоставит вручную (решение 4).

Имя файла: сч NN_<период 1С>.xlsx, например сч 60_9 месяцев 2025 г.xlsx, сч 62_2025 г.xlsx.

6.2 ББ и ОФР

Шаблон — реальная выгрузка 1С на листе TDSheet (форма 66н): fixtures/statements-xlsx/mezheninovskaya/2025-Q3/ 1_Форма1_бух_09.xlsx (ББ) и 1_Форма2_бух_09.xlsx (ОФР). Генератор копирует книгу образца (стили, объединения, шрифты Times New Roman, высоты строк, рамки, формат нулей [=0]"-";General, блок «КОДЫ») и заменяет:

  • реквизиты: наименование, ИНН, ОКПО, ОКВЭД, ОКОПФ/ОКФС, вид деятельности, адрес — вымышленные; все прочие ячейки шапки с данными образца (аудитор, его ИНН/ОГРН, подписи, даты подписания) очищаются; тест проверяет, что ни одна строка из образца, кроме неизменяемых текстов формы, не попала в файл;
  • даты и заголовки колонок периодов («За 9 месяцев 2025 года», «На 31 декабря 2024 года», «На 31 декабря 2023 года» — для ББ; текущий и прошлый год — для ОФР);
  • значения — по кодам строк (колонка «Коды»), а не по номерам строк образца.

Образец правили в Excel, поэтому при сборке шаблона (synthgen/templates/build_templates.py, один раз, результат коммитится) генератор: заменяет все формулы значениями (итоги 1100/1200/1600/1300/1400/1500/1700, 2100/…/2500 и заголовки второй страницы — генератор пишет числа сам); удаляет рабочие формулы и значения вне формы (ячейки правее последней колонки формы); очищает ФИО в подписях и ставит вымышленные; оставляет в ОФР строки 2530, 2900, 2910 (2530 — из книги, если есть, иначе «-»; 2900/2910 — «-»). Значения в файле — в тыс. руб. целыми (решение 12), расходы ОФР — отрицательными числами, как в образце (в скобках их не пишет и 1С). Набор строк — 66н из forms.py #35 плюс 2530/2900/2910; edition_of из #35 не копируется (всегда 66н).

Колонки сравнительных периодов (прошлый год / позапрошлый год), если книга их не покрывает (период до начала книги), заполняются масштабированными значениями того же профиля (решение 5) — так у файла нет пустых колонок, но и согласованность для них не требуется.

Имена файлов: ББ_<период 1С>.xlsx, ОФР_<период 1С>.xlsx.

6.3 Состав ГК (импорт ЕЦП)

directories/ecp-<prefix>.xlsx в формате файла #35 synthetic-group/directories/ecp-synthetic-group.xlsx (он прошёл импорт в ядро; реальная выгрузка fixtures/directories/ecp-export-russkiy-biskvit.xlsx отличается — лист Выписки_01.04.26, без КПП, имена «ЭКО ВОЛОГДА, АО» — импорт ядра принимает оба вида): лист Выписки, колонки ИНН, КПП, iD ГК, Базовый РФ ГК в CRM, Группа компаний, Организация, ГСК, ГСЗ, Тип; все компании — «Входит в ГСК»; iD ГК = <prefix>-000001; ИНН/КПП текстом. Перед реализацией исполнитель сверяет формат с эндпоинтом импорта (arm-core, POST /api/v1/directories/import) и с ecp.py #35.

7. Результат прогона

<out>/
  README.md                 — настройки прогона, состав группы (имя, ИНН, доля головной), список пар ВГО с
                              расхождениями (стороны, вид, сумма), какие компании без ИНН и какой контрагент
                              записан «другим именем», число контрагентов по файлам — не оракул, а журнал генератора
  config.yaml               — фактические настройки (с seed)
  directories/ecp-<prefix>.xlsx
  osv/<КОД>/<период>/сч NN_….xlsx
  statements/<КОД>/<период>/ББ_….xlsx, ОФР_….xlsx

<КОД> — короткий латинский код компании (C01…Cnn, головная — C01); полное имя — в README и ЕЦП.

8. Код и переиспользование

  • Пакет synthgen/ в корне fixtures (рядом с synthetic-group/), запуск uv run --with openpyxl==3.1.5 --with pyyaml python -m synthgen …. Тесты — synthgen/tests/, pytest.
  • Ветка #35 ещё не слита, поэтому нужные модули копируются в synthgen/ (с пометкой источника в docstring), не импортируются из synthetic-group/: inn.py, ledger.py, xlsx.py (детерминированное сохранение), chart.py (субсчета из seed arm-core; добавить недостающие — 80.09, 84, 91.09, 99 — сверив с arm-core seed chart_of_accounts.csv), правила свёртки счетов в строки ББ/ОФР из forms.py, запись ЕЦП из ecp.py. Источник — worktree /Users/andrey.epifancev/projects/awp-ca/.wt/synthetic-group/fixtures/synthetic-group/gen/. Дедупликация — после слияния обоих (follow-up).
  • Сгенерированные наборы в git не коммитятся (out/ в .gitignore); коммитятся код, словарь, пример конфигурации и шаблоны, собранные из образцов (без реквизитов образца).

9. Приёмка

  • pytest зелёный: инварианты §5.6, распределение §5.3 (среднее в допуске), доля расхождений §5.5, число ОСВ без ИНН, ИНН из диапазона префикса и с верной контрольной суммой, отсутствие реквизитов образцов, детерминированность (два прогона с одним seed → побайтно равны; разный seed → различаются), ошибки конфигурации.
  • Прогон по умолчанию и прогон «крупный» (group_size=15, periods=6, osv_counterparties_mean=150) проходят парсер ОСВ tabular-parser (зеркальный worktree, как в #35) без ошибок; разбор ББ/ОФР в Excel находит все коды и сходит 1600 = 1700.
  • Время крупного прогона — до 5 минут на ноутбуке разработчика.

10. Что оставлено исполнителю

Выбор распределений внутри указанных рамок, алгоритм генерации имён словаря, внутренняя структура пакета, формат README прогона, точные субсчета внутри перечисленных счетов (из chart.py).

Decisions made without discussion

№ Дата Решение Обоснование Цена ошибки
1 12.11.2026 Новый пакет synthgen/ отдельно от synthetic-group/ (#35), модули #35 копируются #35 не слита; разные задачи (контрольный набор vs объём); импорт между каталогами связал бы их ветки дублирование кода до follow-up
2 12.11.2026 Все компании ГК входят в ГСК; group_size — одно число владелец назвал «размер ГСК/ГК» одной настройкой нет сценария «член ГК вне ГСК» — добавить настройку позже
3 12.11.2026 ИНН — отдельная колонка после наименования в образцах 1С ИНН нет; колонка уже принимается парсером (#35, приёмка tabular-parser); в имени ИНН сломал бы сопоставление по имени если в реальных выгрузках ИНН иначе — поменять запись ОСВ
4 12.11.2026 В ОСВ без ИНН одна компания группы — с другим написанием имени владелец: «аналитик сопоставит малую часть контрагентов» если нужно больше ручных сопоставлений — сделать настройкой
5 12.11.2026 ББ «на 31.12» года перед первым периодом — из книги (входящие остатки); более ранние сравнительные колонки — масштабированные значения профиля первая колонка должна совпадать с «Сальдо на начало» ОСВ того же набора; глубже книги нет, пустые колонки нетипичны для 1С аналитик может заметить несоответствие позапрошлогодних цифр
6 12.11.2026 Только форма 66н образец 1С с 66н есть (Межениновская, 9 мес. 2025); ФСБУ 4/2023 — не-цель годовая отчётность 2025+ по новой форме не покрыта
7 12.11.2026 Счета ОСВ: 01, 58, 60, 62, 66, 67, 76, 80, 90 чек-лист BASIC (01-project-overview) + 80 (правило гудвилла 58.01↔80); 02 не входит в BASIC версия EXTENDED покажет пробелы чек-листа — добавить счета позже
9 12.11.2026 osv_counterparties_mean ≤ 150, потолки по словарю, перераспределение недобора владелец задал словарь 200–400; при среднем 300 потолок 60/62 срезает выборку и среднее не выполняется для большего объёма нужен словарь крупнее — расширить build.py
10 12.11.2026 ИНН словаря — номера 0100001–0109999, компании группы — 1000000+ по хешу префикса не пересекаться с #35 (номера 1–99) и между префиксами на одном стенде коллизия хеша префиксов ~1/90000
11 12.11.2026 Дивиденды выплачиваются в месяце объявления, в пары ВГО по сальдо и расхождения не попадают дивиденды проводятся через 76.03 у обеих сторон (стороны правила ОФР 2310) и гасятся в месяце объявления; остаток на дату ББ дал бы пару по 76.03 вне доли расхождений (ДП §4, правила 5–8) — остаток дал бы ложную одностороннюю пару пары по дивидендам не видны в шахматке по сальдо (обороты видны в ОСВ 75/76)
12 12.11.2026 ББ/ОФР в тыс. руб. целыми, расходы ОФР отрицательными так выглядит образец 1С; дробные тысячи в образце — следствие ручной правки если ядро ждёт копейки в тысячах — поменять запись
8 12.11.2026 Значения по умолчанию §3 (5 компаний, SYN, 4 периода с 2025-Q1, 60 контрагентов, 1 без ИНН, доля 0.1) разумный стартовый объём: «есть что смотреть», прогон быстрый никакой — меняется настройкой
13 12.11.2026 Тесты жизненного цикла расхождений ВГО проверяют снятие 31 января, только если январь следующего года внутри периодов книги; иначе — только удержание до конца года (R-1) книга кончается последним периодом, января после него нет непроверенное снятие расхождения в хвосте набора
14 12.11.2026 Скрипт сборки шаблонов ББ/ОФР называется build_forms_templates.py (как в плане; §6.2 называл build_templates.py) (R-2) отличать от build_osv_style.py нет