Экспортёры report_*: подпись «Индекс дефицита (12 мес)» лжёт при отсутствии 12-мес прогноза — fallback берёт дефицит чужого горизонта #1590

Closed
opened 2026-06-16 17:13:54 +00:00 by bot-backend · 0 comments
Collaborator

Severity: low · Категория: ложь в отчёте / неверная подпись периода · Файл: backend/app/services/exporters/report_pdf.py:489, 455-476

Файлы: backend/app/services/exporters/report_pdf.py:489 (литерал '12 мес'), report_md.py:264 и report_docx.py:279 (через _PRIMARY_HORIZON_MONTHS=12); значение — _scenario_deficit_index (report_pdf.py:455-476). Логика: если у сценария нет forecast с horizon_months==12 ИЛИ его deficit_index is None, функция делает fallback на первый forecast с не-None дефицитом (строки 473-475) — это может быть горизонт 6/18/24 мес, но подпись столбца жёстко остаётся «(12 мес)». Аналогично Telegram-сводка report_md.py:508: horizon_note = ' (гор. 12 мес)' if horizons else '' ставит метку «(гор. 12 мес)» при любом непустом meta.horizons, не проверяя фактический горизонт значения (то же берётся через fallback в _tg_scenario_deficit_line→_scenario_deficit_index, стр.447). Импакт: deficit_index — ключевой инвест-сигнал; клиент читает дефицит другого горизонта как 12-месячный в PDF/DOCX/MD/Telegram. Путь редкий (дефолтные горизонты включают 12), но достижим на усечённых сценариях. Фикс: возвращать вместе со значением фактический horizon_months и подписывать столбец/метку реальным горизонтом, либо при отсутствии 12-мес прогноза выводить '—' вместо подмены чужим горизонтом под меткой «12 мес».

Почему баг: deficit_index — ключевой инвест-сигнал. Когда у сценария нет 12-мес горизонта (или там None), таблица всё равно подписывает выведенное число как «(12 мес)», хотя оно относится к другому горизонту. Клиент читает дефицит чужого горизонта как 12-месячный — тихая порча смысла цифры в клиентском документе (PDF/DOCX/MD/Telegram). При дефолтных горизонтах (6,12,18,24) 12 обычно есть, поэтому путь редкий, но достижим при усечённых/частичных сценариях (graceful-вход).

Как чинить: _scenario_deficit_index — возвращать (deficit, horizon_months) и подписывать колонку фактическим горизонтом; либо не делать fallback на чужой горизонт (нет 12-мес — «—»). В Telegram гейтить horizon_note по факту 12-мес forecast, а не по meta.horizons.


deep-audit backend v2 (после PR #1543) · verify+harden, confidence 0.78 · unit B20

**Severity:** low · **Категория:** ложь в отчёте / неверная подпись периода · **Файл:** `backend/app/services/exporters/report_pdf.py:489, 455-476` Файлы: backend/app/services/exporters/report_pdf.py:489 (литерал '12 мес'), report_md.py:264 и report_docx.py:279 (через `_PRIMARY_HORIZON_MONTHS`=12); значение — `_scenario_deficit_index` (report_pdf.py:455-476). Логика: если у сценария нет forecast с horizon_months==12 ИЛИ его deficit_index is None, функция делает fallback на первый forecast с не-None дефицитом (строки 473-475) — это может быть горизонт 6/18/24 мес, но подпись столбца жёстко остаётся «(12 мес)». Аналогично Telegram-сводка report_md.py:508: `horizon_note = ' (гор. 12 мес)' if horizons else ''` ставит метку «(гор. 12 мес)» при любом непустом meta.horizons, не проверяя фактический горизонт значения (то же берётся через fallback в _tg_scenario_deficit_line→_scenario_deficit_index, стр.447). Импакт: deficit_index — ключевой инвест-сигнал; клиент читает дефицит другого горизонта как 12-месячный в PDF/DOCX/MD/Telegram. Путь редкий (дефолтные горизонты включают 12), но достижим на усечённых сценариях. Фикс: возвращать вместе со значением фактический horizon_months и подписывать столбец/метку реальным горизонтом, либо при отсутствии 12-мес прогноза выводить '—' вместо подмены чужим горизонтом под меткой «12 мес». **Почему баг:** deficit_index — ключевой инвест-сигнал. Когда у сценария нет 12-мес горизонта (или там None), таблица всё равно подписывает выведенное число как «(12 мес)», хотя оно относится к другому горизонту. Клиент читает дефицит чужого горизонта как 12-месячный — тихая порча смысла цифры в клиентском документе (PDF/DOCX/MD/Telegram). При дефолтных горизонтах (6,12,18,24) 12 обычно есть, поэтому путь редкий, но достижим при усечённых/частичных сценариях (graceful-вход). **Как чинить:** `_scenario_deficit_index` — возвращать (deficit, horizon_months) и подписывать колонку фактическим горизонтом; либо не делать fallback на чужой горизонт (нет 12-мес — «—»). В Telegram гейтить horizon_note по факту 12-мес forecast, а не по meta.horizons. --- <sub>deep-audit backend v2 (после PR #1543) · verify+harden, confidence 0.78 · unit B20</sub>
bot-backend added the
week ревью 1
label 2026-06-16 17:13:54 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: lekss361/gendesign#1590
No description provided.