arm-ka-docs/context/07-open-questions.md
Andrey Epifancev 3209610214 Гайд фронта и OQ-33: crm_id ГСК/ГСЗ принимается API без сохранения (arm-core #111)
Решение владельца 14.10.2026: поле «ID ГСК/ГСЗ в CRM» форм 51-03/51-04/77-02
принимается POST/PATCH и отбрасывается; колонка — arm-core #112.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 12:33:56 +04:00

175 lines
144 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 07. Открытые вопросы (реестр)
Правило: агент **не закрывает** вопросы сам. Если решение нужно для текущего среза — делает явное допущение, помечает его
в коде `# ASSUMPTION(OQ-nn)` и в OpenSpec-change, и переносит вопрос в статус «допущение принято». Пользователь закрывает
вопросы с аналитиками/методологами и обновляет этот файл.
| ID | Вопрос | Откуда | Влияет на | Статус / допущение |
|---|---|---|---|---|
| OQ-01 | Хранить ли все строки ОСВ или только контрагентские; нужен ли `parent_entry_id` (замечание 2 ревью v5.13) | DBML, «Парсинг ОСВ» п. 11 | срез 2 (парсер, `osv_entry`) | **закрыт решением владельца 05.10.2026:** хранить **все** строки ОСВ с `level` и `parent_entry_id`; строку «Итого» не хранить, использовать только для сверки (парсер отдаёт её как `group_type = TOTAL`, маппер Среза 2c отбрасывает) |
| OQ-02 | ОСВ «за 2/3/4 квартал» — квартальная или нарастающая? Как определять автоматически | FNR01 маппинг периода | срез 2, §2 правил | **закрыт решением владельца 05.10.2026:** ОСВ «за N квартал» — **квартальная**; признак сверяется по данным: если есть ОСВ Q1 того же года и `opening` совпадает — нарастающая (парсер ставит `is_cumulative` по тексту, ядро переопределяет при маппинге/в консолидации); **допущение парсера (Срез 2a):** `period_quarter` и `is_cumulative` вычисляются только по тексту периода (`rules/periods.yaml`; Q2–Q4 «N квартал» → `false`), сверка по данным — в ядре, подсрез 2c (`ASSUMPTION(OQ-02)`) |
| OQ-03 | Приведение квартальных ОСВ к нарастающим: ключ соединения (контрагент vs договор), пропуск промежуточного квартала | DOC-259006 | срез 4 | **закрыт решением владельца 06.10.2026 (Срез 4):** ключ склейки — (счёт, контрагент по сырому имени и ИНН), договоры внутри контрагента (строка COUNTERPARTY уже содержит их сумму); пропуск квартала — **пробел** по паре (ЮЛ, счёт), а не блокирующая ошибка: строк пары в своде нет, пробел отдаётся в `gaps[]` сводки и в листе «Пробелы» экспорта; квартал без строки ключа = нули; дубль ключа внутри квартала суммируется. Правило — `05-domain-rules.md` §2; код — `domain/consolidation_periods.select_sources`, `domain/consolidated_osv.build` |
| OQ-04 | ОСВ по субсчёту (58.03 вместо 58) закрывает ли обязательный документ «ОСВ 58» | фикстуры | чек-лист | **закрыт решением владельца 05.10.2026:** **нет**, ОСВ по субсчёту (58.03) — только субсчёт, обязательный документ «ОСВ 58» закрывает только полная ОСВ по счёту 58; чек-лист считает документ выполненным при `osv_account_code = код правила` |
| OQ-05 | Нужен ли статус `document.RECOGNIZED` или все идут через `NEEDS_CHECK → CONFIRMED` | DBML | срез 2–3 | **закрыт решением владельца 05.10.2026:** статус `RECOGNIZED` **нужен**: документ после успешного распознавания — `RECOGNIZED`, при нарушениях — `NEEDS_CHECK`, после подтверждения аналитиком — `CONFIRMED`. Пересмотрено OQ-157 (08.10.2026): после маппинга чистый документ сразу `CONFIRMED`; `RECOGNIZED` остаётся у старых документов и форм после правки. |
| OQ-06 | Замена одного документа внутри многодокументного файла | DBML | срез 3 | решено: замена файлом целиком |
| OQ-07 | Матчинг ВГО: одна строка = одна позиция (DOC-223044) или агрегировать по (компания, контрагент, счёт) (FNR03)? | DOC-223044 vs FNR03 | срез 5 | допущение: агрегировать по (компания, контрагент, счёт, сторона) с детализацией по `osv_entry.id` |
| OQ-08 | Знак порога 5 % в правиле пары (≤ или <), и к чему считать % — к меньшей сумме (Методика сноска 14) | FNR03 (знак потерян) | срез 5 | допущение: `≤ 5 %` к меньшей |
| OQ-09 | Правила элиминации ОФР (2110/2120/2310/2320/2330): точный состав субсчетов, источник средневзвешенной ставки НДС, выбор субсчетов «работником ПА самостоятельно» | FNR03 | срез 6 | не реализовывать авто-элиминацию ОФР до решения; только ручные `correction` |
| OQ-10 | Гудвилл: входит ли стр. 1350 (добавочный капитал) в базу участия; УК из ББ (1310) или из ОСВ 80/83 | DBML, DOC-259025, FNR03 | срез 6 | допущение: 1310 из ББ; 1350 — настраиваемый флаг, по умолчанию выключен |
| OQ-11 | Строка «ППА» в КФО (правило № 1, когда у А нет ОСВ 03) — что это и куда | FNR03 | срез 6 | не реализовывать |
| OQ-12 | Формулы ОФР: знаки 2410/2420/2430 по ФСБУ 4/2023 vs 66н; состав 1100 (1120–1140) | DOC-221281 | срез 3 | допущение: формулы §7, строки без значения = 0; **расширение 07.10.2026:** формулы и справочники по редакциям форм (`66n` / `fsbu4`) — OQ-146 |
| OQ-13 | `confidence` от ИС Сканирование — доли 0..1 или проценты 0..100; имя поля организации (`full_name` vs `form_name`) | FNR02 vs `aep_*.json` | срез 3 (scan-gateway) | **закрыт для scan-gateway (04.10.2026, по `aep_3.json`, `aep_6.json`):** `confidence` — числа 0–100 (не доли); название организации — `data_ocr.organization.form_name` (шумный OCR-текст), ИНН — `inn.form_name`, единицы — `units.form_name`, дата — `date.form_name` (в `aep_6.json` мусор); `full_name`/`inn.inn`/`units.measurement` из ТЗ в реальных файлах нет. Нормализация — Срез 3 |
| OQ-14 | `reporting_date` в JSON ИС Сканирование — «ждём обновлённый JSON» | FNR01 | срез 3 | **уточнено (04.10.2026):** отдельного поля `reporting_date` в ответе нет; `date.form_name` ненадёжен (в `aep_6.json` — «Общество с ограниченной ответственностью», в `aep_3.json` — «2\|0\|2\|4\|»); даты колонок — в строке-шапке `rows[0]` таблицы («На 30 июня 2025 г» в `aep_6.json`), в `aep_3.json` шапки нет. Допущение прежнее: парсить из шапки таблицы, при её отсутствии — `NEEDS_CHECK`; решение — Срез 3 |
| OQ-15 | Таймаут OCR 5 мин / 0 повторов (ТЗ 3) vs outbox с повторами (DBML) | DOC-037736 | срез 3 | решено DBML: `max_attempts` OCR = 1, парсер = 3 |
| OQ-16 | Матрица прав (ТЗ сноска 29 «будет разработана»); роли макета (Аналитик ДКБ, Наблюдатель) ≠ ТЗ (ANL-034) | ТЗ 8.1, макет | срез 1 | допущение: seed по §10 правил |
| OQ-17 | `user_role` M:N или одна роль на пользователя | DBML | срез 1 | **закрыт реализацией Среза 1 (05.10.2026): M:N** — `user_role`, `PUT /admin/users/{id}/roles` заменяет набор ролей |
| OQ-18 | Статусы REJECTED / ARCHIVED из макета (ANL-035); `VERIFICATION_GO` (РКА ГО) — есть ли в маршруте | DBML, DOC-037734 | срез 7 | допущение: REJECTED не вводить (возврат = переход + комментарий); ARCHIVED — флаг фильтра; VERIFICATION_GO — есть |
| OQ-19 | `consolidation_lock` (взять в работу / перехват, ANL-036) — подтвердить с заказчиком | макет | срез 7 | **закрыт решением владельца 06.10.2026 (Срез 4):** блокировка «взять в работу» закрывает **все** правки версии (периметр, статус, ручное сопоставление; загрузка и подтверждение документов блокировкой не ограничиваются); перехват — любому с `CONSOLIDATION_EDIT`, с обязательной причиной (`takeover_reason`), событие `CONSOLIDATION_LOCK_TAKEOVER`; истечения нет (OQ-119). `05-domain-rules.md` §10 |
| OQ-20 | `company_group.crm_id` обязателен (модалка) или nullable (UC-02, DBML) | DOC-221280 | срез 1 | решено DBML: nullable + `code` |
| OQ-21 | «Дата перевода» ЮЛ в другую ГК — помечена «??» | DOC-221280 | срез 1 | **закрыт решением владельца 05.10.2026:** дата перевода — `effective_date` ≥ сегодня, пишется в `legal_entity_history`; перевод применяется сразу; граница суток — по МСК (OQ-96, follow-up arm-core#8) |
| OQ-22 | Ретеншн `results/` в S3 (ARCH-010) | DBML | срез 2 | допущение: как `uploads/` |
| OQ-23 | Расшифровки (`DISCLOSURE`): разбирать или только хранить (ANL-040) | DBML | — | только хранить; **Срез 3b: допущение реализовано** — `DISCLOSURE` — один документ на тип на файл (`locator` — через запятую), без данных, статус `RECOGNIZED` (§9.5 п. 2). |
| OQ-24 | Форма 276-ОД — состав и шаблон | FNR03 3.4 | — | вне scope |
| OQ-25 | Состав ГСК для тестовой группы «Русский бисквит»: входит ли ОАО «ЧКФ», ООО «ОФГ», «Лидер» | фикстуры | тесты консолидации | **частично закрыт по выгрузке ЕЦП (04.10.2026):** ГК «Русский Бисквит» (`1-AZFL6BQ`, Санкт-Петербургский РФ) — 21 запись; флаг «входит в ГСК» только у АО «Русский бисквит» и ООО «Пекарня Счастливочная»; ЧКФ, ОФГ, Малечкинская птицефабрика, ТК РБ, Регион Финанс и др. — в ГК, но **не в ГСК** (срез — `fixtures/directories/ecp-export-russkiy-biskvit.xlsx`). Периметр тестовой консолидации — две компании; OQ-25 остаётся про то, верен ли состав ГСК в CRM |
| OQ-26 | Единицы: ББ/ОФР в тыс. руб., ОСВ в руб. — правило округления при элиминации (до тыс., банковское?) | — | срез 6 | допущение: считать в рублях, округлять до тыс. руб. на выходе в `kfo_result_line` (ROUND_HALF_UP) |
| OQ-27 | Поля ТЗ 5.1 (сценарий, подход, гудвилл) не в модалке создания консолидации | макет [42] vs ТЗ 5.1 | срез 4, 6 | **решено (владелец макета, 03.10)**: `approach = BASIC` всегда в первой очереди, EXTENDED позже — определять по набору загруженных документов; `goodwill_enabled` — переключатель на вкладке «Гудвилл»; `scenario = CONFIRM` включается через меню «…» консолидации в списке |
| OQ-28 | ~~Нет экрана проверки распознавания~~ — есть, открывается кликом по документу в панели «Первичные документы» (снимок `41`) | макет [41] | срез 3 | **закрыт**: реализовать по макету; для ОСВ (xlsx) левая панель — уточнить |
| OQ-29 | Макет: нет экранов пользователей / ролей / матрицы прав (ТЗ 8.1, `role_permission`); в журнале макета роли «Аналитик ДКБ», «Кредитный аналитик», «Утверждающий РФ», «Верификатор ГО», «Система распознавания / Сервис» (ANL-034, снимок `43-02`) | макет vs ТЗ 8.1 | срез 1 | допущение: минимальный экран администратора по §10 правил; роли — коды ТЗ 8.1, названия макета не используются |
| OQ-30 | Макет: шахматка и КФО показаны в тыс. руб. с 2 знаками, ОСВ — в рублях; где и как округлять (связано с OQ-26) | макет [30] [31-01] | срез 5–6 | допущение: хранить и считать в рублях, переключатель единиц — только отображение |
| OQ-31 | КПП ЮЛ: в макете колонка и поле форм (`50`, `61`, `51-02`, `79`), в DBML v5.13 у `legal_entity` поля нет. Нужен ли КПП (и в импорте, и для ИП, у которых его нет)? | макет [50] [51-02] | срез 1, DBML v5.14; пересмотр — DBML v5.17 (docs#53, arm-core#45) | **закрыт решением владельца 07.10.2026: КПП нужен** (пересмотр; повод — макет и комментарий фронтенда «в ручке получения ЮЛ не приходит КПП»): DBML v5.17 — `legal_entity.kpp`, `legal_entity_history.kpp` (nullable). Формат — 9 символов: 4 цифры, 2 символа (цифра или латинская A–Z), 3 цифры, иначе `INVALID_KPP`; у ИП КПП запрещён (422 `INVALID_KPP`, при импорте строка пропускается с предупреждением); для ЮЛ необязателен (старые записи и импорт без колонки валидны); уникален только `inn` (КПП — атрибут, сопоставление контрагентов ОСВ по ИНН не меняется); изменение КПП — обычный PATCH со снимком в `legal_entity_history`. *История: 04.10.2026 — «КПП не нужен» (DBML v5.14 без поля; импорт его не читал; формы — без КПП); отменено 07.10.2026* |
| OQ-32 | ЮЛ в макете состоит одновременно в ГСК и ГСЗ («Входит в ГСК» и «Входит в ГСЗ» заполнены, [79]); ТЗ 6.1 / ANL-030 — только в одной. Что верно? | макет [79] [51-02] | срез 1 (partial unique `related_group_member.legal_entity_id`) | **закрыт решением владельца 04.10.2026: ЮЛ только в одной ГК, одновременно — в одной ГСК и одной ГСЗ.** DBML v5.14: `related_group_member.kind` + partial unique `(legal_entity_id, kind) WHERE is_current` (отменяет ANL-030); выгрузка ЕЦП это подтверждает (флаги «входит в ГСК» и «входит в ГСЗ» одновременно) |
| OQ-33 | «ID ГСК/ГСЗ в CRM» в формах (`51-03`, `51-04`, `77-02`; пример «3-9CGSK01» в формате ЕЦП CRM): у `related_group` в DBML только внутренний `code` (GSK-001). Есть ли у ГСК/ГСЗ идентификатор в CRM? | макет [77-02] | срез 1, DBML | **закрыт реализацией Среза 1:** `related_group.code` генерируется системой (`GSK-NNNNNN`/`GSZ-NNNNNN`), ID из CRM не вводится и не импортируется (в выгрузке ЕЦП его нет — OQ-91). **Решение владельца 14.10.2026:** `POST`/`PATCH` ГСК/ГСЗ принимают `crm_id` и пока отбрасывают его (arm-core #111), колонка `related_group.crm_id` — отдельной задачей arm-core #112 (DBML v5.19) |
| OQ-34 | «Удалить» ГСК в меню строки (`77-01`): физическое удаление или закрытие с историей? В DBML у `related_group` нет флага/истории удаления | макет [77-01] | срез 1 | **закрыт реализацией Среза 1 (ASSUMPTION подтверждено отсутствием возражений):** удаляется только пустая ГСК/ГСЗ (409 `GROUP_NOT_EMPTY`), состав сохраняется в `before_value` аудита; группа с FK из консолидаций — follow-up `GROUP_IN_USE` (arm-core#8, T8.1) |
| OQ-35 | База процента отклонения: в таблице корректировки % считается к сумме стороны ЮЛ-1 (−79,77 % для 1 000 ↔ 202,32, `31-02-02`), в «Подтверждении КФО клиента» — к версии банка (ст. 2, `31-06`); DBML и Методика (сноска 14) — к **меньшей** сумме. Что считать правильным для `correction.deviation_percent` и `client_kfo_line.deviation_percent`? | макет [31-02] [31-06] vs DBML, Методика | срезы 5–6 | допущение: к меньшей (Методика); макет считать неточным |
| OQ-36 | Экран проверки показывает строки формы **без кода** («в том числе: Основные средства, пригодные к использованию…», `41`); DBML хранит только строки с кодом (`financial_statement_line`), контракт требует `line_code`. Хранить ли расшифровочные строки без кода (и как их показывать)? | макет [41] vs DBML, контракт | срез 3, контракт v1.x | допущение: строки без кода не хранить и не показывать в первой очереди; **Срез 3b: допущение реализовано** — `financial_statement_line` пишется только для строк с кодом (строк без кода в результате нет), экран их не получает. |
| OQ-37 | Строка «Гудвилл 1105» (ФСБУ 4/2023) в распознанной форме (`41`) vs строка `kfo_result_line.line_code = 'GOODWILL'` в разделе I КФО: использовать код 1105 или служебный GOODWILL? Влияет на шаблон строк на фронте и на формулы §7 (1100 = … + 1105?) | макет [41], ФСБУ 4/2023 | срез 3, 6 | допущение: хранить код 1105 как пришёл; строка КФО «GOODWILL» — отдельная, уточнить у методологов |
| OQ-38 | ЕХ в макете показывает и **неверифицированные** консолидации («Консолидация 2025 К4», без даты верификации, `62` [83-01]); ЕХ ТЗ 6.2 — срез пишется только после «Верифицирован». Показывать ли в ЛК ЕХ незавершённые консолидации (как «в работе») или только `ex_snapshot`? | макет [62] [83-01] vs ЕХ ТЗ 6.2 | срез 7 | допущение: только срезы `ex_snapshot`; незавершённые — в разделе «Отчётность» |
| OQ-39 | Состав «сформированной отчётности» в ЕХ (`83-01`): КФО .xlsx, Шахматка .xlsx и «Консолидированная отчетность .pdf» с датой «Сформирован» и подписями Утвердил/Верифицировал. Нужен ли PDF-экспорт КФО и какие ещё артефакты (сводная ОСВ, корректировки, гудвилл — ЕХ ТЗ 5.2) показывать? | макет [83-01] vs ЕХ ТЗ 5.2 | срез 7 | допущение: xlsx из `payload` на лету для всех `item_type`; PDF — не делать до решения |
| OQ-40 | «Квартальная дата» в журнале для консолидации 2024 К1 показана как 01.01.2024 (`43-02`); по правилам банка 1 квартал 2024 = отчётная дата 01.04.2024 (`consolidation.reporting_date`). Что выводить? | макет [43-02] | срез 4, 7 | **допущение принято и реализовано в Срезе 4 (06.10.2026):** `consolidation.reporting_date` по правилу банка (Q1 → 01.04.Y, Q2 → 01.07.Y, Q3 → 01.10.Y, Q4 → 01.01.Y+1), `domain/consolidation_periods.reporting_date`; макет неточен |
| OQ-41 | Экран «ЮЛ вне ГК» (`61`, заголовок «Юридические лица») показывает все ЮЛ с заполненной колонкой ГК, а не только монозаёмщиков. Это сводный реестр ЮЛ или фильтр `company_group_id IS NULL`? | макет [61] | срез 1 | допущение: сводный реестр всех ЮЛ с фильтром «вне ГК», по умолчанию выключенным |
| OQ-42 | «Проверка распознавания» для ОСВ (xlsx) в макете отсутствует (строки `doc-row--readonly`, `39`). Нужен ли отдельный экран проверки ОСВ (дерево строк, подсветка контрагентов) или проверка ОСВ только через сводную ОСВ (`30`)? | макет [39] | срез 2 (UI) | **закрыт решением владельца 05.10.2026:** экран проверки ОСВ **нужен** (дерево строк, суммы, нераспознанные контрагенты); в макете его нет — проектируется в подсрезе 2d «UI первичных документов» |
| OQ-43 | Кнопка «К отчетности» неактивна у консолидаций в статусах «Инициация» и «Верификация РФ», активна у «Консолидация РФ» и «Верифицирован» (`34`). Каково правило доступности отчёта по статусу / роли? | макет [34] | срез 4, 7 (policy) | **решено владельцем 07.10.2026 (arm-core#42, П-3):** «К отчетности» доступна во всех статусах, начиная с `INITIATED`, для ролей с `CONSOLIDATION_VIEW`; правка свода (сопоставление контрагента) — только «в работе» со своей блокировкой. Было: допущение: отчёт доступен с `CONSOLIDATION_RF` и далее для всех ролей с `CONSOLIDATION_VIEW`; «Инициация» — только карточка |
| OQ-44 | «Добавить версию» создаёт версию сразу без модалки (`43-01`), копируя периметр; нужно ли подтверждение и выбор сценария/подхода новой версии? | макет [43-01] | срез 7 | допущение: без модалки, периметр и сценарий копируются из предыдущей версии |
| OQ-45 | Контракт `recognition-result.schema.json` допускает `data_type = NU` (строки налогового учёта в ОСВ «БУ, НУ»), а enum DBML `osv_data_type` — только BU, CURRENCY, QUANTITY, SUM. Добавлять NU в DBML (v5.14) или не хранить строки НУ? | контракт v1.0 vs DBML | срез 2 (маппинг `osv_entry`) | **закрыт решением владельца 05.10.2026:** строки налогового учёта `NU` **хранить в ядре**: `osv_data_type` расширяется значением `NU` в DBML v5.15 (подсрез 2c, маппинг); контракт уже допускает `NU` |
| OQ-46 | Обязательны ли все ОСВ расширенного подхода (02, 03, 08, 44, 26, 91, 51, 52, 59, 80, 83, 86, 97, 001, 008, 009, 011) при `approach = EXTENDED`, или часть — «при наличии»? | ТЗ 5.1; `required_document_rule.is_mandatory` | срез 0 (seed), срез 3 (чек-лист) | допущение принято и применено в Срезе 0: все 27 строк EXTENDED в seed `arm-core/api/seed/core/required_document_rules.yaml` (маркер `ASSUMPTION(OQ-46)`) — `is_mandatory = true`; BASIC — 10 строк, все обязательные; смена правила — правка одного yaml; вопрос к методологам остаётся открытым до Среза 3 (чек-лист); источник: arm-core, предложения, раздел A |
| OQ-47 | Источник полного плана счетов 1С для seed `chart_of_account`: в фикстурах файла нет, «план счетов из бандла макета» не передан | `06-test-data.md` §6, `fixtures/README.md` | срез 0 (seed) | допущение принято и применено в Срезе 0: seed `arm-core/api/seed/core/chart_of_accounts.csv` — 122 строки (62 синтетических счёта Плана счетов 94н, 11 забалансовых, 49 субсчетов и подсубсчетов 1С БП 3.0, упомянутых в §4/§6); названия нестандартных субсчетов (66/67 `.06/.22/.24/.26`, 76.07.x, 76.08/.28/.38) выведены по их роли в §4 правил, а не взяты из справочника 1С; названия-заглушки заменить по справочнику 1С клиента, остальное добавляет администратор (ARCH-004); источник: arm-core, предложения, раздел A |
| OQ-48 | Правило элиминации № 1 (§4), сторона Б «76.07.1 − 76.07.5 − 76.07.9»: 76.07.5 и 76.07.9 не потомки 76.07.1, поэтому «вычитание» чисто информативно. Так и задумано? | `05-domain-rules.md` §4 (правило 1); seed `elimination_rules_v1.yaml`; arm-core: предл. 1 (T14.1) | срез 5 (пары ВГО) | допущение принято: исключения 76.07.5/76.07.9 на стороне Б хранятся как `is_excluded` и не влияют на сумму (76.07.1 их не содержит); алгоритм на них не опирается; подтвердить с методологами |
| OQ-49 | Правило № 1, сторона А: резервный счёт 01 лежит в одном списке с 03 и 76.07.9 — приоритета нет. Как выбирать «03, а если у А нет ОСВ 03 — 01»? Что значит «результат в строку ППА» (см. OQ-11)? | `05-domain-rules.md` §4 (правило 1: «01 — резервный»); seed `elimination_rules_v1.yaml`; arm-core: предл. 2 (T14.2) | срез 5–6 | допущение принято: в seed `accounts: ["03", "76.07.9", "01"]` без порядка; fallback «берём 01, если нет ОСВ 03» — в `comment` правила; логику приоритета реализует срез 5, до решения она не реализуется |
| OQ-50 | Составные коды статей КФО `1450/1550` (правило 1) и `1310/1350` (правило 7, гудвилл) хранятся в `elimination_rule.article_2_line` одной строкой с «/». Как их потреблять: делить по «/» и подбирать строку, которая есть в ББ Б, или хранить две строки? | `05-domain-rules.md` §4 (правила 1, 7); seed `elimination_rules_v1.yaml`; arm-core: предл. 3 (T14.3) | срез 5–6; кандидат DBML v5.14 (note колонки) | допущение принято: хранится как в §4, потребитель делит по «/»; предложение — зафиксировать это в note `article_2_line` в DBML v5.14 (версию не создавали) |
| OQ-51 | `required_document_rule`: уникальный индекс `(approach, document_type, account_id)` при nullable `account_id` допускает дубли строк для не-ОСВ документов (NULL в PostgreSQL различны). Добавить `NULLS NOT DISTINCT`? | DBML v5.13 (`required_document_rule`, Indexes); arm-core: предл. 4 (T8.3) | срез 3 (чек-лист); кандидат DBML v5.14 | допущение принято: ORM и миграция 1:1 с DBML v5.13 (без `NULLS NOT DISTINCT`); предложение v5.14: `[unique, note: 'NULLS NOT DISTINCT']`, как у `document` |
| OQ-52 | Контракт v1.0, `recognition-result-message`: `schema_version` принимает только `1.x` (`^1\.\d+$`), а `result_schema_version` — любой MAJOR (`^\d+\.\d+$`). Намеренно (файл результата версионируется отдельно) или небрежность? | `contracts/recognition-result-message.schema.json`; arm-core: предл. 5 (T20.7) | контракт v1.x; срез 2 (inbox, маппинг) | допущение принято: ядро повторяет схему буквально (любой MAJOR проходит валидацию); совместимость MAJOR результата проверяет маппинг Среза 2; уточнить у владельца контракта |
| OQ-53 | Контракт v1.0: схемы нигде не задают `additionalProperties: false` (кроме `columns` ОСВ). Считать ли неизвестные поля допустимыми расширениями? | `contracts/*.schema.json`; arm-core: предл. 6 (T20) | контракт v1.x; все три сервиса | допущение принято: модели ядра `extra = "ignore"` (строже схемы быть нельзя — ядро отвергнет совместимого пира); изменение — через MINOR контракта |
| OQ-54 | Параметры JWT РАИСА неизвестны: JWKS URL / публичный ключ, `iss`, `aud`, имя claim с email. Что в продуктивном контуре? Делать ли проверку `aud`/`iss` обязательной в проде? | ТЗ 8.1, `03-architecture.md` (SSO РАИСА); `core/auth.py`; arm-core: предл. 7 (T16.2) | срез 1+ (прод-конфигурация) | допущение принято: настраиваются `AUTH_JWKS_URL`/`AUTH_PUBLIC_KEY` (взаимоисключающие), `AUTH_ISSUER`, `AUTH_AUDIENCE`, `AUTH_EMAIL_CLAIM` (по умолчанию `email`); при пустых `AUTH_AUDIENCE`/`AUTH_ISSUER` `aud`/`iss` не проверяются; предложение: в проде сделать `aud` обязательным и предупреждать при старте, если пусто; **контекст 07.10.2026:** контур временно работает на dev-заглушке аутентификации (`DEBUG=true`, `AUTH_DEV_ISSUER=true`, вход по email; `dev/contour-transfer-guide.md` §6.3); не выяснено, используется ли в контуре `raisa_fastapi_protected_api`; вероятно, Keycloak (у старого фронта `REACT_APP_KEYCLOAK_URL`); переход на SSO — после ответа и arm-core#43 |
| OQ-55 | В enum `audit_action` нет действия для смены статуса задания распознавания (`recognition_job`). Добавлять ли (DBML v5.14) и нужен ли аудит обмена вообще? | DBML v5.13 `audit_action`; arm-core: предл. 8 (R-7, design #20 arm-core) | срез 2 (обмен); кандидат DBML v5.14 | допущение принято: Срез 0 не пишет `audit_event` из обмена `recognition_job`; сервис аудита (`services/audit.py`) готов и покрыт тестами; если нужно — действие в v5.14 и вызовы в `recognition_exchange.py` |
| OQ-56 | Append-only аудит обеспечен только на уровне кода (единственная точка записи, статический тест на UPDATE/DELETE по `AuditEvent`). Нужна ли защита на уровне БД (триггер или `REVOKE UPDATE, DELETE` для роли приложения)? Вопрос поднят дважды (в том числе финальным ревью ветки). | инвариант «Append-only аудит» (`../CLAUDE.md`); `services/audit.py`; arm-core: предл. 9 и дополнение к нему (задача 21) | миграции, права БД; платформа | **решено человеком 04.10.2026: осознанный техдолг.** Append-only на уровне кода и тестов; защита на уровне БД (триггер или `REVOKE UPDATE, DELETE`) — позже, вместе с платформой, до прода |
| OQ-57 | Счётчик повторных публикаций запроса (`ACK_TIMEOUT_SECONDS` × `OUTBOX_MAX_REPUBLISH`) хранится в `recognition_job.metrics.republish_count` (jsonb), колонки в DBML нет; при каждой повторной публикации `published_at` перезаписывается временем последней, время первой не сохраняется. Нужны ли колонка счётчика и время первой публикации (v5.14)? | `services/recognition_exchange.py` (design #6 arm-core); arm-core: предл. 10 (T23, T23.1); docs: O-19 (задача 4 фазы 2) | срез replay; кандидат DBML v5.14 | допущение принято: счётчик в `metrics.republish_count`, `attempt` при повторе не растёт; `published_at` — время последней публикации (`dlq` через (`OUTBOX_MAX_REPUBLISH`+1)×`ACK_TIMEOUT_SECONDS` ≈ 120 с, описано в `dev/README.md`); при будущем replay счётчик и `reply_deadline` надо сбрасывать |
| OQ-58 | Семантика сроков и ошибок обмена в ядре: (а) `reply_deadline` по умолчанию = `now + 5 мин` для обоих pipeline (`tabular`, `image`), если у задания NULL; в DBML срок есть только для OCR («OCR — 5 мин»), для парсера значения нет; (б) при невозможности собрать запрос по контракту задание уходит в `error` / `INVALID_REQUEST`; (в) при `ACK_TIMEOUT × (MAX_REPUBLISH+1) ≥ 5 мин` статус `dlq` недостижим (раньше сработает `timeout`). Верно ли? Какой срок у `tabular-parser`? | DBML `recognition_job.reply_deadline` note; `recognition_exchange.py` (`DEFAULT_REPLY_TIMEOUT`); arm-core: предл. 11 и дополнение (T23.4) | срез 2–3; настройка таймаутов | допущение принято: как описано (дефолты 30 с × 3 укладываются в 5 мин); сроки по pipeline уточнить с командами парсера и шлюза **09.10.2026, решение владельца:** OCR (`image`) — 20 мин, парсер — 5 мин (arm-core #87; note DBML — OQ-159) |
| OQ-59 | Email пользователя сопоставляется с `app_user.email` по `lower(email)`; при двух записях, различающихся регистром, — 403 (fail-closed). Добавить `CHECK`/уникальный индекс по `lower(email)` в DBML? | `core/auth.py`, `api/dependencies/auth.py`; arm-core: предл. 12 (R-17, design #29 arm-core) | срез 1 (админ-экран); кандидат DBML v5.14 | **закрыт реализацией Среза 2c (06.10.2026, `arm-core/openspec/changes/slice-2c-osv-ingest`), иначе, чем предлагалось:** индекс по `lower(email)` не добавлен (сверка ORM↔DBML не умеет expression-индексы); в DBML v5.15 `app_user.email` хранится в нижнем регистре (миграция `0003` приводит существующие, seed и администрирование нормализуют при записи), поэтому обычный UNIQUE регистронезависим по построению |
| OQ-60 | Seed `--dev` не защищён флагом `DEBUG`: создаёт dev-пользователей (в том числе `ADMIN`) на любой БД, на которую указывает `.env`, и перезаписывает существующего `app_user` с совпадающим email. Нужна ли защита? | arm-core `scripts/seed.py`; arm-core: предл. 13 (T15.2, T15.4) | срез 0 (процесс); прод-развёртывание | **закрыт реализацией (arm-core PR #10, техдолг Среза 1, 05.10.2026):** `python -m scripts.seed --dev` при `DEBUG=false` отказывает с кодом 2 до чтения seed и подключения к БД; `run_seed(dev=True)` для тестов без защиты |
| OQ-61 | Неверный метод на существующий API-маршрут (например `POST /api/v1/me`) отдаёт 404 вместо 405: SPA-монтирование на `/` перехватывает частичное совпадение, а `/api/*` в нём принудительно 404. Приемлемо? | arm-core `app.py` (`SPAStaticFiles`); arm-core: предл. 14 (T18.2) | API-контракт (клиенты, фронт) | **закрыт реализацией (arm-core PR #10, техдолг Среза 1, 05.10.2026):** неверный метод на существующий маршрут `/api/v1/*` — 405 с заголовком `Allow` («метод не поддерживается этим маршрутом»); несуществующий путь и dev-маршрут при выключенных флагах — 404 |
| OQ-62 | В образе `arm-core` нет фронта (`Dockerfile` не собирает `web/`), `GET /` отдаёт 307 на `//` и затем 404; редирект на `//` похож на дефект монтирования SPA при `ROOT_PATH=/`. Собирать `web/` в образ в Срезе 0/1 и чинить редирект? | arm-core `Dockerfile`, `app.py`; docs: O-10 (задача 3 фазы 2), `dev/README.md` | срез 0 (compose), срез 1 (фронт) | **частично закрыт (arm-core PR #10):** редирект `/` → `//` устранён (причина — `root_path="/"`, теперь нормализуется), `GET /` отдаёт `index.html`. **Открыто:** фронт не собирается в образ `arm-core` — follow-up RSHB/arm-core#12 |
| OQ-63 | `web/package-lock.json`: шаблон lock не содержит, `raisa-*` из Nexus — lock вне банка отличался бы; в Срезе 0 lock в `.gitignore`. `npm install` сообщает 6 high (npm audit) в зависимостях шаблона web. Коммитить ли lock; кто обновляет зависимости? | arm-core `web/`; arm-core: предл. 15 (T25.1, R-23, design #35 arm-core) | платформа, фронт; до прода | допущение принято: lock не в репозитории, зависимости не менялись; решение — с платформенной командой (Nexus, политика audit) |
| OQ-64 | Аудит `LOGIN` / `LOGIN_FAILED` (действия есть в `audit_action`) для JWT-аутентификации: в каком срезе и что считать «входом» — первый запрос с токеном, каждый запрос, отказ проверки токена? | DBML `audit_action`; `core/auth.py`; arm-core: предл. 16 (финальное ревью) | срез 1 (вход, админ-экран); аудит | **закрыт реализацией (arm-core PR #10, техдолг Среза 1, 05.10.2026):** `LOGIN` — выдача dev-токена и первый успешный запрос пользователя с токеном после старта процесса; `LOGIN_FAILED` — 401 (токен не принят, без `user_id`, причина без токена) и 403 аутентификации (email в `description`); раздел `SESSION`, отдельная транзакция; запрос без `Authorization`/не Bearer и 403 по праву — не события входа; `LOGOUT` не пишется (сессий на сервере нет) |
| OQ-65 | Шаблонные CORS `allow_origins=["*"]` + `allow_credentials=True` и `ProxyHeadersMiddleware(trusted_hosts=["*"])`: в проде любой клиент может подставить `X-Forwarded-For`, и `audit_event.ip_address` недостоверен. Какие origins и доверенные прокси на платформе? | arm-core `app.py` (шаблон команды); arm-core: предл. 17 (финальное ревью) | прод-конфигурация; достоверность аудита | **частично закрыт (arm-core PR #10):** CORS при `DEBUG=true` — `*` без credentials, при `DEBUG=false` — `CORS_ORIGINS` (пусто — кросс-доменные запрещены); `X-Forwarded-For` только от `FORWARDED_ALLOW_IPS` (по умолчанию `127.0.0.1`). **Открыто (к платформе):** перечень доверенных прокси прода — follow-up RSHB/arm-core#12 |
| OQ-66 | `/docs` и `/openapi.json` FastAPI открыты без аутентификации, в том числе в проде. Закрывать их (выключать при `DEBUG=false`) или оставить? | arm-core `app.py` (шаблон); arm-core: предл. 18 (финальное ревью) | прод-конфигурация; безопасность | **закрыт реализацией (arm-core PR #10, техдолг Среза 1, 05.10.2026):** `/docs`, `/redoc`, `/openapi.json` существуют только при `DEBUG=true`; в проде — 404 |
| OQ-67 | Inbox не обновляет `stored_file.status` (`UPLOADED → PROCESSING → PROCESSED / ERROR`) и `stored_file.current_recognition_job_id` по результату задания: после результата файл остаётся `UPLOADED`, `current_recognition_job_id` пуст. Кто и когда это делает в Срезе 2 — inbox или маппер, в одной транзакции? | DBML `stored_file`, `file_status`; `recognition_exchange.py`; arm-core: предл. 19 (финальное ревью); docs: O-18 (задача 4 фазы 2) | срез 2 (загрузка и маппинг) | **закрыт реализацией Среза 2c (06.10.2026, `arm-core/openspec/changes/slice-2c-osv-ingest`):** статус файла меняет код, проверивший `current_recognition_job_id == job.id` (`services/file_status.py`): ack/результат — `PROCESSING`, `error`/`timeout`/`dlq` — `ERROR` (inbox, вахтёр, публикатор), успешный маппинг — `PROCESSED`, автозамена — `SUPERSEDED`; `current_recognition_job_id` ставит загрузка. См. `05-domain-rules.md` §9.2, §9.5 |
| OQ-68 | Конверт `error` у `scan-gateway` публикуется без `metrics`, поэтому `recognition_job.metrics` = NULL (у `tabular-parser` в `metrics` есть `duration_ms`). Требовать ли `metrics` в конверте ошибки и единые поля метрик у обоих сервисов? | scan-gateway `core/worker.py`; docs: O-18 (задача 4 фазы 2, сквозная проверка) | срез 2–3 (наблюдаемость, контракт v1.x) | допущение принято: `metrics` в контракте необязательны, ядро хранит NULL; требование — решение владельца контракта |
| OQ-69 | Обработчик `recognition.result` выполняется последовательно в callback подписки NATS; длинный маппер Среза 2 задержит следующие сообщения, ack сервиса не успеет за `ACK_TIMEOUT_SECONDS` (30 с) — ложный republish, затем `dlq`. Предложение: развести быстрый inbox (фиксация сообщения и статуса) и отдельный цикл маппинга результата. | arm-core `core/nats.py` (`subscribe_results`), `recognition_exchange.py` (`handle_message`, `ResultMapper`); arm-core: предл. 20 (финальное ревью) | срез 2 (маппинг); архитектура обмена (D-6) | **закрыт реализацией Среза 2c (06.10.2026, `arm-core/openspec/changes/slice-2c-osv-ingest`):** inbox быстрый (фиксация сообщения и статусов, файл результата не читает), маппинг — отдельный фоновый цикл `run_mapping_loop` (`MAPPING_POLL_SECONDS`, `MAPPING_MAX_ATTEMPTS`); ack за `ACK_TIMEOUT_SECONDS` не задерживается. См. `03-architecture.md`, «Цикл маппинга результата» |
| OQ-70 | Перед чтением файла результата маппер должен проверять, что ключ начинается с `results/{file_id}/{job_id}/`, а бакет — бакет роли `results` (иначе сервис может «подсунуть» чужой объект). Закрепить это в контракте и `03-architecture.md`? | контракт `recognition-result-message` (`result_s3`); arm-core `_register_result_object` (`recognition_exchange.py`); arm-core: предл. 21 (финальное ревью) | срез 2 (маппинг); контракт v1.x | **закрыт реализацией Среза 2c (06.10.2026, `arm-core/openspec/changes/slice-2c-osv-ingest`):** цикл маппинга отвергает результат, если бакет не бакет хранилища или ключ не начинается с `results/{file_id}/{job_id}/` (без `.`/`..`/пустых сегментов) — файл `ERROR`, повтора нет; правило закреплено в `03-architecture.md` («Схема ключей S3») |
| OQ-71 | Запрос без читаемых `job_id` / `file_id` / `attempt` остаётся без ответа (конверт собрать нельзя): сервис логирует ошибку и отбрасывает запрос, ядро закрывает задание `dlq` / `NO_ACK` примерно через 120 с. Расходятся и реализации: при нечитаемом `attempt` `tabular-parser` берёт его из заголовка `Nats-Msg-Id` запроса или ничего не отправляет, `scan-gateway` подставляет `attempt = 1` и публикует конверт. Оставить так, выровнять реализации или сделать `file_id` необязательным в конверте `error` (MINOR 1.1)? | `recognition-contract` spec, сценарий «Нечитаемый запрос»; tabular-parser: P-1 (задача 4.3); docs: O-24, design #35 | контракт v1.x; срез 2–3 | допущение принято: молчание сервиса (design #35); реализации выровнять в Срезе 2/3, влияние только на битые запросы |
| OQ-72 | Ack при несовместимой версии запроса: ранняя формулировка контракта и `03-architecture.md` требовали «`SCHEMA_MISMATCH` и ack», оба сервиса публикуют `error SCHEMA_MISMATCH` без ack (ядро принимает результат из `published`, ack перед ним ничего не добавляет). Спека и документ приведены к поведению сервисов — подтвердить. | tabular-parser: P-2; scan-gateway: п. 2 таблицы предложений (T12.13, R-16); docs: design #34 | контракт v1.x; срез 2 | допущение принято: без ack, результат `SCHEMA_MISMATCH` закрывает задание (design #34); если ядро начнёт требовать ack перед результатом — правка одной строки в воркерах и спеке |
| OQ-73 | Повторная попытка (`attempt + 1`) того же `job_id` после ошибки или результата: оба сервиса дедуплицируют по `job_id` (LRU `DONE_CACHE_SIZE`), поэтому replay с новым `attempt` получает только ack и не обрабатывается, на единственном инстансе задание повисает до `reply_deadline` → `timeout`. По `03-architecture.md` replay — «сброс в `queued` с `attempt + 1`». Сделать ключ дедупликации `(job_id, attempt)`; повтор во время обработки — только ack? | tabular-parser: P-3 (задача 4.3); scan-gateway: п. 17 таблицы предложений (I-3, R-17); `03-architecture.md` (replay) | срез 3+ (replay); контракт v1.x | вопрос: допущение принято на Срез 0 — дедуп по `job_id`, ядро повторяет только тем же `attempt`; при одобрении — одна строка в воркере (ключ LRU); **человеком 04.10.2026 не рассмотрен — вопрос остаётся открытым** |
| OQ-74 | `PUBLISH_RETRIES` = одна первая попытка + до 3 повторов (всего до 4) с тем же `Nats-Msg-Id`; у `scan-gateway` тип `PositiveInt` (повторы нельзя отключить настройкой), у `tabular-parser` `NonNegativeInt` (0 допустим); паузы 0,5 с и 2,0 с. Согласовать единую семантику (повторы или всего попыток) и тип? | scan-gateway: п. 1 таблицы предложений (R-13, T11.1); docs: O-25, design #36 | контракт v1.x; настройки сервисов | допущение принято: «1 + 3 повтора» (design #36), расхождение «3 против 4» обмен не ломает (дедуп по `Nats-Msg-Id`); выровнять тип — косметика конфигурации |
| OQ-75 | `reply_deadline` проверяет только `scan-gateway` (`error TIMEOUT` до отправки в ИС и потолок ожидания ответа); `tabular-parser` обрабатывает просроченный запрос. Пример `contracts/examples/request-image.json` имеет `reply_deadline` в прошлом (`2026-10-03T12:45:00Z`) и даёт `TIMEOUT` при ручном прогоне. Единая обязанность сервисов и свежая дата примера? | scan-gateway: п. 12 таблицы предложений (O-e); docs: O-26, design #37 | контракт v1.x; срез 2 | допущение принято: срок — дело сервиса, пример — иллюстрация формы (оговорка в spec и `03-architecture.md`, design #37); обновить дату можно одним PR с пересинхронизацией копий в трёх репозиториях |
| OQ-76 | Обе реальные фикстуры `fixtures/ocr` (`aep_5.json`, `aep_8_form4.json`) — ответы-ошибки ИС (`error.code = INVALID_DOCUMENT`, пустой `table_structure`). Успешный путь `image` (`done` / `needs_check`, файл в `results/…`, `s3_object` с `bucket_role = results`) сквозной проверкой не покрыт; в `scan-gateway` проверяется только синтетическим ответом. Нужны образцы успешных ответов Content AI. | scan-gateway: п. 3 таблицы предложений (O-1, R-10, design #32); docs: O-15 (задача 4 фазы 2), design #31 | срез 3 (OQ-13/14, нормализатор); fixtures | **частично закрыт человеком 04.10.2026:** добавлены реальные образцы — `aep_3.json` (успех: баланс 42 строки + ОФР 18 строк), `aep_3_ofr.json` (второй ответ ИС по тому же файлу, `INVALID_DOCUMENT`), `aep_6.json` (успех: баланс 122 строки + ОФР 49 строк); PR в `arm-ka-fixtures`. Успешный путь `image` на реальных ответах — Срез 3 (нормализатор); транспорт `scan-gateway` проверяется на них в Срезе 0 (склейка нескольких json, `NOT_IMPLEMENTED`) |
| OQ-77 | §11 `05-domain-rules.md` описывает `data_ocr[]` (список), а в фикстурах `data_ocr` — объект с полями `label/organization/inn/units/date/table_structure`. Какой формат фактический на стенде ИС (связано с OQ-13, OQ-14)? | `05-domain-rules.md` §11; scan-gateway: п. 4 таблицы предложений (O-1, T3.2) | срез 3 (нормализатор) | **закрыт человеком 04.10.2026 (по `aep_3.json`, `aep_6.json`):** `data_ocr` — **объект**, один на json-файл (`label`, `organization`, `inn`, `units`, `date` — `{form_name, confidence}`; `table_structure` — **список таблиц** `{label, rows[]}`, одна таблица = один документ БС/ОФР, в одном json их может быть несколько). Успех — `error = {code: "", message: ""}` (не `null`!), ошибка — непустой `code` и пустой `table_structure`. §11 `05-domain-rules.md` исправлен. **Уточнение человека 04.10.2026:** разбиение одной формы на несколько таблиц (сегменты) в образцах не встречалось; наблюдается только несколько таблиц **разных** форм в одном json (баланс + ОФР). Нештатный случай (повтор `label`) — `NEEDS_CHECK`, правило уточнить в Срезе 3 по реальным данным; **Срез 3b: допущение реализовано** — второй `BALANCE`/`INCOME` того же типа в файле не создаётся (предупреждение «вторая форма … пропущена (OQ-77)»), первый документ — `NEEDS_CHECK`; правило по реальным данным стенда по-прежнему уточнить (§9.5 п. 2). |
| OQ-78 | Шлюз читает ровно `<output>/<job_id>/<job_id>.json` (спека); старый код читал все файлы каталога `Output/<id>/`. Какие файлы и когда реальная ИС кладёт в `Output`, в каком порядке (момент готовности)? | scan-gateway: п. 5 таблицы предложений (T8.5) | срез 3 (стенд ИС) | **закрыт человеком 04.10.2026:** ИС кладёт **все распознанные документы одновременно** в каталог `<job_id>`, по одному json на документ (пример: `aep_3.json` + `aep_3_ofr.json`); шлюз читает **все** json каталога и склеивает в один ответ (готовность — по стабилизации набора файлов, `SCAN_SETTLE_SECONDS`); ошибка одного документа не отменяет остальные (`metrics.warnings`). Реализуется в Срезе 0 (доработка PR scan-gateway #3) |
| OQ-79 | Временный файл `.<имя>.part` лежит в `Input` (атомарная запись `.part` + rename): реальная ИС без маски может забрать недописанный файл. Уточнить у владельцев ИС маску и поведение или перенести временный файл в служебный подкаталог рядом с `Input`? | scan-gateway: п. 6 таблицы предложений (R-5, T7/T8) | срез 3 (стенд ИС) | **закрыт человеком 04.10.2026:** временный файл записи уходит из `Input` в служебный каталог рядом (`SCAN_TMP_DIR`, по умолчанию `Tmp`, на той же шаре — `rename` остаётся атомарным). Реализуется в Срезе 0 (доработка PR scan-gateway #3) **Пересмотрено владельцем 09.10.2026:** запись прямо в `Input` с перезаписью, как в старом АРМ (там работало в контуре); `Tmp` и `.part` у шлюза убраны (arm-scan-gateway #26). Допущение: ИС не берёт файл, пока шлюз держит его открытым на запись |
| OQ-80 | `make up` расходится с эталоном (scan-gateway и `dev/Makefile.common`): в `tabular-parser` нет `minio-init` — на свежем томе бакета `arm-ka` нет, `test_s3_integration` даёт `NoSuchBucket`, хотя README сервиса обещает бакет; в `arm-core` `up -d` без `--wait` и без `--project-name` (имя в compose фиксировано, безвредно). Также `check-full` в arm-core и tabular-parser — через пререквизиты (порядок при `make -j` не гарантирован), в scan-gateway — через `$(MAKE)`; `lint` arm-core включает `mypy scripts`. Подключить `include ../docs/dev/Makefile.common` в сервисах? | tabular-parser: `Makefile` (цель `up`); scan-gateway: п. 18 таблицы предложений (I-1); docs: O-1 и O-2 (задача 1 фазы 2), O-4 и O-5 (задача 2 фазы 2), `dev/checks.md`, `dev/Makefile.common` | срез 0 (процесс), все сервисы | допущение принято: эталон — scan-gateway, расхождения описаны в `dev/checks.md`, Makefile сервисов не правились (design #22, #24); выровнять — правкой Makefile сервисов (follow-up) |
| OQ-81 | Переменные `ARM_KA_DOCS` / `ARM_KA_FIXTURES` поддержаны неравномерно: `arm-core` — обе; `tabular-parser` — только `ARM_KA_DOCS`; `scan-gateway` — ни одной (жёстко `../docs`, `../../fixtures/ocr`). Текст причины skip («fixtures недоступен: положите репозиторий…») не содержит строки «ARM_KA_FIXTURES недоступен» из сценария «Нет фикстур» spec `quality-checks`; для docs-теста текст тоже без `ARM_KA_DOCS`. Нарушение spec — править сервисы? | scan-gateway и tabular-parser (тесты, Makefile); docs: O-3 (задача 1 фазы 2), `dev/checks.md`; spec `quality-checks` | срез 0 (процесс), все сервисы | допущение принято: расхождение описано в `dev/checks.md`; правка тестов и Makefile в scan-gateway и tabular-parser — follow-up человеку (сервисы не правились); **частично закрыт (Срез 2a, tabular-parser):** хелпер `tests/fixtures_helpers.py` поддерживает `ARM_KA_FIXTURES` (текст skip называет переменную и путь), `ARM_KA_DOCS` поддержан в тесте идентичности контракта; `scan-gateway` — по-прежнему ни одной переменной, остаётся follow-up человеку |
| OQ-82 | `.dockerignore` в `arm-core` не исключает `api/.env.test`: файл попадает в образ (у `tabular-parser` и `scan-gateway` исключён). Исправлять? | arm-core `.dockerignore`; docs: T3.2 (задача 3 фазы 2) | срез 0 (образы), прод-сборка | **закрыт реализацией (arm-core PR #10, техдолг Среза 1, 05.10.2026):** `.dockerignore` исключает `api/.env.*` (включая `.env.test` и `.env.example`); настройки контейнера — только из окружения |
| OQ-83 | Контейнеры профиля `services` подписаны на те же subject и queue group общего NATS, что и экземпляры в интеграционных тестах сервисов (`recognition.result` / `arm-core`, `*.request`): NATS может отдать сообщение теста контейнеру и наоборот; интеграционные тесты `scan-gateway` и `arm-core` одновременно тоже делят NATS. Сделать queue group и subject-префикс настраиваемыми для тестов (например `NATS_TEST_PREFIX`) или поднимать отдельный NATS на тесты? | scan-gateway: п. 11 таблицы предложений (T14.1, T11.8); docs: O-11 (задача 3 фазы 2; подтверждено ревью для tabular-parser и scan-gateway), `dev/README.md` | срез 0 (процесс), интеграционные тесты | допущение принято: в `dev/README.md` предупреждение и команда `stop` для контейнеров профиля; не гонять `make test-integration` двух сервисов одновременно. 13.10.2026 (nats-env-config): subject'ы — настройки; интеграционные тесты tabular-parser и scan-gateway идут на уникальных subject'ах прогона и не пересекаются со стендом. Для `make e2e` ядра вопрос остаётся: ему нужен контейнер tabular-parser стенда на умолчаниях, конфликт — общая queue group `arm-core` (design R-7). Статус: открыт (частично) |
| OQ-84 | Запуск сервисов из venv без Nexus (`make venv` + `python run_server.py` из `api/` с `.env` из `docs/dev/.env.example`, healthcheck по `localhost`) в Срезе 0 не проверялся ни в одном из трёх сервисов. Известно по чтению конфигов (design #44): `.env.example` в нынешнем виде отвергнут `scan-gateway` (`OPENBAO__DATABASE__*`) и `tabular-parser` (`OPENBAO__DATABASE__*`, `OPENBAO__SMB__*` — `extra_forbidden`), в нём нет `OPENBAO__MINIO__MINIO_SECURE=false` (по умолчанию `true` — https к http-MinIO). Прогнать сценарий, зафиксировать набор переменных и портов (`SERVER__PORT`, `SCAN_HOTFOLDER_PATH` для `scan-gateway`), решить, нужен ли отдельный `.env.example` на сервис, и снять пометку «черновик» с раздела `dev/README.md`? | spec `dev-environment`, сценарий «Запуск из venv против compose без Nexus»; docs: design #43, `dev/README.md` («Запуск сервисов из venv без Nexus»); финальное ревью фазы 2: I-1 | срез 0 (процесс, окружение разработки); кандидат в follow-up issue | вопрос: допущение Среза 0 — раздел README остаётся черновиком, сценарий не подтверждён (проверка требовала `.venv` внутри сервисных репозиториев, в фазе 2 доступных только для чтения) |
| OQ-85 | **Возможен ли JetStream на платформе** (серверный дедуп по `Nats-Msg-Id`, durable consumer'ы, redelivery вместо ack и повторной публикации на уровне приложения)? Сейчас NATS без JetStream (ответ платформы 03.10.2026); Срез 0 строит доставку на ack `processing`, повторной публикации ядром и дедупе по `recognition_job.result_msg_id`. Если JetStream подключить можно — меняются только publisher/subscriber (`core/nats.py` в трёх сервисах), outbox/inbox и контракт остаются; исчезают потеря результата при простое ядра и риск повторной обработки. | решение человека 04.10.2026: «узнать у платформы и проверить позже»; `design.md` slice-0-skeleton (D-3, Open Questions п. 4); OQ-72, OQ-73 | инфраструктура NATS (платформа); `core/nats.py`, воркеры сервисов | **вопрос к платформе (поставить напоминание, проверить после ревью Среза 0 / до Среза 3)**: допущение — без JetStream. 06.10.2026: `RSHB/armka_file_worker` (PDFWorker старого АРМ, проверен в контуре) тоже работает на core NATS (`subscribe`/`publish` + `flush`, без `jetstream()`), один JWT на все сервисы АРМ с правами по subject'ам; возможность включить JetStream по-прежнему не выяснена |
| OQ-86 | Фильтр видимости по ЗО (`business_segment`): сегмент есть только у `app_user`, у ГК/ЮЛ поля нет (DBML v5.14). Откуда брать сегмент ГК (из ЕЦП CRM? из РФ?), нужен ли вообще фильтр по ЗО в первой очереди? | §10.1 правил; DBML v5.14 | срез 1 (policy), DBML v5.15 | допущение: в Срезе 1 ЗО не фильтрует; видимость — только по филиалу (ГО видит всё) |
| OQ-87 | Выгрузка ЕЦП содержит типы `нерез.` (354), `ФЛ` (180), `нет инн` (2); enum `entity_type` — только ЮЛ/ИП. Нужны ли нерезиденты и физлица в справочнике (контрагенты ОСВ, поручители)? | `docs/source/biq/ecp-export-lagal-entity.xlsx`; §13.2 | срез 1 (импорт), DBML | допущение: строки пропускаются с кодом `UNSUPPORTED_TYPE`, enum не расширяется |
| OQ-88 | Проверять ли контрольные разряды ИНН при импорте и вводе (сейчас только длина 10/12); в выгрузке 279 нечисловых значений (`LV…`, `РНН…`, `нерез.`) | §13.2 | срез 1 | допущение: только длина; нечисловые → `INVALID_INN` |
| OQ-89 | «Базовый РФ ГК в CRM» в выгрузке содержит `ЦКБ` (1370 строк) и `Головная организация`: это головной офис (`regional_office.is_head_office`) или отдельные подразделения? Полный список — 53 значения, из них 51 «… РФ» | `ecp-export-lagal-entity.xlsx`; §13.2 | срез 1 (импорт, policy видимости) | допущение: создаются как обычные филиалы с именем из файла; маппинг на ГО — после ответа (миграция данных одной командой) |
| OQ-90 | Один ИНН в нескольких ГК выгрузки (215 случаев, напр. `7733288488` в `1-5AL0CC8` и `1-5A6WZN2`): какая ГК основная? `legal_entity.company_group_id` — одна | `ecp-export-lagal-entity.xlsx`; §13.2 | срез 1 (импорт) | допущение: первая встретившаяся ГК выигрывает, остальные строки — `LE_IN_OTHER_GROUP` в отчёте; перевод — вручную |
| OQ-91 | В выгрузке ЕЦП только флаги «входит в ГСК/ГСЗ» без идентификатора группы: у ГК предполагается одна ГСК и одна ГСЗ (в макете у «РМ Агро» две ГСК). Как ЕЦП кодирует несколько ГСК в одной ГК и даёт ли ГСК имя/ID (OQ-33)? | `ecp-export-lagal-entity.xlsx`; §13.2; макет [77] | срез 1 (импорт), срез 4 | допущение: импорт создаёт не более одной ГСК и одной ГСЗ на ГК с именем ГК; разделение — вручную Куратором/КА |
| OQ-92 | В выгрузке ЕЦП есть строки с непустым `iD ГК`, но пустыми «Организация»/«Группа компаний» (напр. строка 4034, ГК `1-KCKKHRK`). Импорт сейчас создаёт сущность с пустым именем. Пропускать такие строки или брать имя из других строк той же ГК? | `ecp-export-lagal-entity.xlsx`; §13.2; arm-core T9.6 | срез 1 (до боевого импорта) | допущение: импортируются как есть; исправление — повторный импорт с `update` |
| OQ-93 | ИНН в xlsx бывает числом — ведущий ноль теряется ещё в Excel (`0123456789` → `123456789`, 9 цифр → `INVALID_INN`). Считать ли такие ИНН восстанавливаемыми (дополнять нулём слева до 10/12)? | §13.1; arm-core T9.3 | срез 1 | допущение: не дополняем, строка → `INVALID_INN` |
| OQ-94 | Импорт с `on_existing = update` обновляет и архивные ГК (`is_archived`), не разархивируя их. Должен ли импорт разархивировать ГК, которая снова пришла из ЕЦП, или пропускать её? | §13.2; arm-core T10.7 | срез 1 | допущение: обновляется без разархивации |
| OQ-95 | Код `LE_IN_OTHER_GROUP` в отчёте импорта называет ЮЛ (ИНН, имя) из ГК, которую филиальный пользователь с `DIRECTORY_ADMIN` видеть не должен. Допустимо ли раскрытие в отчёте импорта (право по seed только у ADMIN ГО)? | §13.3; §10.1; arm-core T10.9 | срез 1 | допущение: отчёт импорта вне policy видимости, право даётся только ГО |
| OQ-96 | Граница «`effective_date` не раньше сегодня» (OQ-21) считается по UTC: для МСК правило сдвигается на 3 часа после полуночи. Какой часовой пояс — «банковский» МСК или UTC? | §13.4; arm-core R-9 | срез 1 | **закрыт решением владельца 05.10.2026: граница «сегодня» — по МСК.** Код arm-core пока считает по UTC; перевод на МСК — follow-up RSHB/arm-core#8 |
| OQ-97 | Право `DIRECTORY_DELETE` по матрице §10 только у ADMIN: удалять алиасы и пустые ГСК/ГСЗ могут только администраторы, а не КА. Расширить право на KA_RF/KA_GO/CURATOR_GO? | §10; arm-core O-6 | срез 1 (seed) | **закрыт решением владельца 05.10.2026: право `DIRECTORY_DELETE` на КА не расширяется**, удаляют алиасы и пустые ГСК/ГСЗ только ADMIN; матрица §10 без изменений |
| OQ-98 | Единый список кодов валют для строк `data_type = CURRENCY` («USD», «EUR» под контрагентом): достаточно ли ISO 4217-списка из `rules/data_types.yaml` (`currency_codes`: RUB, USD, EUR, CNY, KZT, BYN, GBP, CHF, JPY, TRY, AED)? Нужны ли нестандартные написания и коды, которые используют конфигурации 1С | tabular-parser: design D-4, R-6, R-22 | срез 2c (маппинг CURRENCY-строк), словарь `data_types.yaml` | допущение принято: ISO-перечень, дополняется строкой в YAML вместе с синтетикой; коды не входят в тест покрытия словарей (R-22) |
| OQ-99 | Несколько ОСВ **одного счёта** в одной книге (разные листы: два периода или две организации): парсер выдаёт по одному `document` на лист (`locator = sheet:<имя>`) и ничего не дедуплицирует. Как ядро выбирает/сопоставляет такие документы с периодом и организацией? | tabular-parser: spec `osv-parsing` («Листы ОСВ находятся по содержимому») | срез 2c (маппинг, `document.period_check`) | допущение принято: каждый лист — отдельный документ, выбор — за ядром и аналитиком при проверке |
| OQ-100 | ИНН внутри **текста** строки контрагента («Полиграф, 3528315710», 20 строк в ОСВ 76 Счастливочной) и склеенная ячейка «ИНН/КПП»: извлекать ли ИНН в парсере или в ядре при резолве контрагента? Сейчас `counterparty_inn` берётся только из колонки ИНН (§1.3), `counterparty_name` = весь текст строки | tabular-parser: R-15, R-17 (design №25, №27) | срез 2c (резолв `counterparty_id` по ИНН/имени) | **реализовано** (решение владельца 07.10.2026, RSHB/arm-tabular-parser#9, PR #15, слит с #13): ИНН из конца текста названия контрагента → `counterparty_inn`, `counterparty_name` без ИНН (`osv_headers.yaml`: `counterparty_inn_suffix`); тесты — `tests/unit/parser/test_classify.py` (`test_inn_is_extracted_from_counterparty_name`), синтетика `s17_inn_in_name`; описание — `05-domain-rules.md` §1.3 |
| OQ-101 | Числа с ведущим «+», «.5», «5.» парсер отвергает как нечисловой текст (`null` + предупреждение с номером строки). Встречаются ли такие записи в выгрузках других систем; принимать ли их | tabular-parser: T7.3 | срез 2a+, словарь/правила `numbers.py` | допущение принято: отвергаются; в реальных файлах не встречаются |
| OQ-102 | Нужен **реальный xlsb из Excel**: единственный `fixtures/osv/synthetic/s13_no_outline_indent.xlsb` собран тестовым писателем BIFF12 и не доказывает чтение файлов, сохранённых Excel (стили, другая раскладка записей). Получить xlsb у аналитиков или пересохранить `s02` в Excel и заменить `s13` по инструкции в README fixtures | tabular-parser: R-9, O-7 | срез 2a (достоверность xlsb) | допущение принято: читаем `pyxlsb` без outline level и форматирования; проверка — на первом настоящем xlsb |
| OQ-103 | Счета с буквой в **первом** сегменте кода (забалансовые «МЦ.04», «ГТД», «НЕ.01.1») не распознаются как ACCOUNT: регулярка требует цифр в первом сегменте, строка станет контрагентом/группировкой по позиции. Нужны ли такие ОСВ, как их кодировать в `chart_of_account` | tabular-parser: R-16 (design №26), `classify.py` | срез 2c (план счетов), парсер | допущение принято: не распознаются; в 66 реальных файлах не встречаются |
| OQ-104 | Группировка «Валюта» в строке детализации (шапка варианта 2 «Детализация по субсчетам: …, Валюта») совпадает с меткой валюты шапки и порождает ложное предупреждение «валюта «» не распознана» (`currency = null` в обоих случаях). Править парсер (различать область шапки и строку детализации)? | tabular-parser: O-8, T12.1 (синтетика s16) | срез 2a+ (`header.py`) | допущение принято: дефект малозначим, предупреждение зафиксировано в golden-снимке s16 |
| OQ-105 | Сверка «сумма детей = родитель» по нетто (R-21): у родителя сальдо свёрнуто (счёт — одна сторона), у детей — развёрнуто. Подтвердить аналитиками, что нетто-свёртка родителя — штатное поведение 1С для **всех** версий выгрузки, а не артефакт двух организаций; остаточные риски RR-1…RR-4 и слепые зоны (перестановка сторон и одинаковое увеличение обеих сторон сальдо ребёнка под свёрнутым прямым родителем не ловятся ничем) перечислены в §1.4 | tabular-parser: R-21 (design №31), `05-domain-rules.md` §1.4 | срез 2a (контрольные суммы), срез 2c | допущение принято: нетто допустимо, если у родителя не более одной ненулевой стороны сальдо; 32 из 66 реальных файлов иначе не сходились |
| OQ-106 | «Итого = корень» сверяется с **первой** корневой строкой ACCOUNT (уровень 0). Лист с несколькими корнями уровня 0 (несколько счетов в одной ОСВ; ОСВ по субконто без строк-счетов) получает `needs_check`, хотя «Итого» может быть равна Σ корней. Сверять «Итого» с суммой корней? | tabular-parser: финальное ревью Среза 2a, M-4 | срез 2a+ (контрольные суммы `checks.py`), чек-лист документов | допущение принято: сверка с первым корнем; многокорневой лист → `needs_check` (аналитик проверяет вручную); в фикстурах таких листов нет |
| OQ-107 | При `hierarchy=sums` (дерево восстановлено по равенству «сумма детей = родитель») сверка «сумма детей = родитель» тавтологична — она не может не сойтись. Считать ли такой разбор `needs_check` (или отдельное предупреждение достаточно)? | tabular-parser: финальное ревью Среза 2a, M-5 | срез 2a+ (статус результата), срез 2c (UI проверки) | допущение принято: статус — по контрольным суммам, стратегия видна в `metrics.warnings` (`hierarchy=sums`); на реальных файлах стратегия не выбирается (все — `outline`/`trivial`) |
| OQ-108 | Защита фазы чтения xlsx от XML-бомб (entity expansion) — `defusedxml` как новая зависимость сервиса — и иные лимиты памяти чтения. Остаточные риски после R-23/R-25: (1) бомба по объёму самих ячеек в пределах лимита распакованного zip — xlsx в 5 МБ с 2 млн обычных ячеек читается openpyxl 13 с и занимает 1,5 ГБ (замер ревьюера); (2) объём `sharedStrings` и стилей в тех же пределах; (3) entity expansion (от экспоненты «billion laughs» защищает только сам expat ≥ 2.4); (4) **известный обход предсканирования (R-26):** лист в части без `.xml` с ≥ 64 байтами ведущих пробелов и без XML-декларации не попадает в скан (файл 1,8 КБ → 27 с / 1,4 ГБ в openpyxl) — первый follow-up: убрать «sniff» и отдавать expat каждую некаталожную запись; (5) скан сам не ограничен размером чанка: «мусорная» XML-часть с атрибутом в 400 МБ → 1,3 ГБ / 1,2 с, 400 МБ тегов → 8,5 с CPU; (6) xls с очень многими крупными merged-диапазонами (> 10 000 ячеек каждый) — линейный просмотр индекса `Sheet` (≈ 4,4 мс на обращение), останавливает только `PARSE_TIMEOUT_SECONDS`: потолок на число таких диапазонов. Предскан — эшелон защиты, не граница безопасности; настоящая граница — лимит памяти контейнера / подпроцесс с RLIMIT. Рекомендация: жёсткий лимит памяти контейнера сервиса; и/или предпроверка объявленного `<dimension>` и потоковый счётчик ячеек `<c>`/строк `sharedStrings` (тем же expat-сканом, что объединения) с лимитом; и/или разбор книги в подпроцессе с `RLIMIT_AS`/таймаутом, который можно убить. Решение платформы | tabular-parser: финальное ревью Среза 2a, I-1 (хвост), R-23, R-25 | tabular-parser (readers), платформа (зависимости и лимиты образа/пода) | допущение принято: действуют лимиты R-23/R-25 — распакованный zip ≤ 10 × `PARSE_MAX_BYTES`, площадь объединённых ячеек книги ≤ 200 000 (потоковый expat-скан всех XML-частей до openpyxl); объём ячеек, `sharedStrings`/стили и entity expansion не ограничены (README «Известные ограничения») |
| OQ-109 | Воркер tabular-parser скачивает объект S3 целиком и только потом проверяет `PARSE_MAX_BYTES`; проверять размер по `stat` до скачивания | tabular-parser: финальное ревью Среза 2a, M-7 | tabular-parser (`core/worker.py`) — вне Среза 2a | **закрыт реализацией Среза 2c (06.10.2026, `arm-core/openspec/changes/slice-2c-osv-ingest`):** ядро ограничивает размер файла на загрузке (`UPLOAD_MAX_BYTES` = 50 МиБ, как `PARSE_MAX_BYTES` парсера), поэтому в воркер большие объекты из ядра не попадают; проверка по `stat` до скачивания в `tabular-parser` остаётся как есть (дефенсивная, не требуется) |
| OQ-110 | Удаление файла после консолидаций: у `document` появятся FK из `financial_statement_meta`, `consolidation_document`, `ex_snapshot_item`, а `DELETE /files/{id}` Среза 2c их не чистит (в 2c строк в этих таблицах нет). ANL-020 запрещает физическое удаление после INITIATED. Что делать: запретить удаление файла, документ которого входит в версию консолидации, и/или каскадно чистить служебные таблицы (`financial_statement_meta` — в Срезе 3) | arm-core Срез 2c: Task 10 (R-23, design №36, №48) | Срезы 3, 4 (расширить удаление или запрет) | **закрыт для Среза 4 (06.10.2026):** привязываются к версии только `CONFIRMED` документы незаменённых файлов, а файл с `CONFIRMED` документом удалить нельзя (`FILE_HAS_CONFIRMED_DOCUMENTS`) — удаление файла с документом в версии невозможно, расширять `DELETE /files/{id}` не нужно; автозамена снимает привязки документов заменённого файла (`FILE_REPLACE.unbound_version_ids`). Остаток: FK из `financial_statement_meta` и `ex_snapshot_item` — Срезы 3 и 7. Уточнено OQ-157: файл с автоподтверждёнными документами удаляется, пока их версии «в работе»; привязки снимаются. |
| OQ-111 | Ретеншн S3-объектов при удалении файла: сейчас `DELETE` лишь ставит `s3_object.expires_at = now()`, объекты `uploads`/`results` физически не стираются (дополняет OQ-22). Кто и когда их удаляет — lifecycle-правило бакета по метаданным, фоновая задача ядра, ручная очистка? Объект, записанный в S3 до сбоя БД, остаётся сиротой | arm-core Срез 2c: design D-10, №36 | платформа (lifecycle), Срез 2 → эксплуатация | допущение принято: бакет растёт; очистка — вне среза |
| OQ-112 | Остаточные объекты e2e-прогона: каждый запуск `make e2e` оставляет в MinIO dev-стенда ~2 объекта (`uploads/…`, `results/…` — следствие OQ-111) | arm-core Срез 2c: Task 11 (O-5) | dev-стенд | допущение принято: чистить бакет `arm-ka` вручную (раздел «Сброс стенда» `dev/qa-guide.md`) |
| OQ-113 | Полнота плана счетов seed: 19 нормализованных кодов строк-счетов golden ОСВ отсутствуют в `chart_of_account` (`01.01`, `66.01`, `76.01`, `60.АВ`, `76.ОТ` и др.), поэтому у их строк `account_id = NULL`. Достаточно ли «администратор добавит» или seed должен включать полный набор субсчетов (включая буквенные) до Среза ВГО (§4–§5) | arm-core Срез 2c: Task 8 (O-3) | Срезы 4–5 (пары счетов элиминации) | **закрыт: допущение принято — выгрузка плана счетов 1С заказчика (`fixtures/directories/chart-of-accounts-1c.xlsx`, 488 строк, все 122 кода seed в ней есть) полная (arm-core #25);** ранее: частично решён 06.10.2026, перенос в seed — arm-core#23 |
| OQ-114 | `EntryOut` (API строк ОСВ) не отдаёт `is_modified`/`original_*`: панели правки подсреза 2d нужен признак правки аналитика (инвариант «первичка не мутируется»: правка — рядом, с аудитом). Добавить поля в схему и контракт API при проектировании 2d | arm-core Срез 2c: Task 9 (T9.7) | подсрез 2d | **закрыт решением владельца 07.10.2026 (вариант А, возможен пересмотр):** ручной правки строк ОСВ нет — в макете строки ОСВ (xlsx) read-only, ошибка в ОСВ исправляется перезагрузкой файла (автозамена §9.5 п. 3); поля `is_modified`/`original_values`/`modified_*` DBML — резерв, `EntryOut` не расширяется. Пересмотреть, если качество парсинга или документов потребует правки на месте (RSHB/arm-ka-docs#62) |
| OQ-115 | Автозамена: если новый файл содержит лишь часть документов прежнего (прежний — ОСВ 60 и 51, новый — только 60), прежний уходит в `SUPERSEDED` **целиком**, документ 51 пропадает из списка файлов; в `FILE_REPLACE.description` о нём ничего нет. Подтвердить «замена файлом целиком» (OQ-06) для этого случая или заменять по документам | arm-core Срез 2c: Task 8 (T8.4) | Срез 2d (список файлов), Срез 4 (комплект документов) | допущение принято: замена файлом целиком, документы заменённого файла сохраняются в БД |
| OQ-116 | ОСВ Q2–Q4, признанная нарастающей по данным Q1 (сальдо на начало корня равно сальдо Q1, §1.5), получает предупреждение маппинга и поэтому остаётся `NEEDS_CHECK`, а не `RECOGNIZED` (буквально D-7/D-9 спеки: `RECOGNIZED` только без предупреждений). Верно ли, что аналитик подтверждает такие документы вручную, или нарастающая по данным — штатный случай без предупреждения (OQ-02, OQ-05) | arm-core Срез 2c: Task 7 (R-17, design №30) | Срез 2d (нагрузка на аналитика), §9.3 | допущение принято: `NEEDS_CHECK`; снять предупреждение — одна правка в `result_mapping.py` и правило статуса |
| OQ-117 | Индексы `osv_entry(parent_entry_id)`, `osv_entry(recognition_job_id)`, `stored_file(current_recognition_job_id)`: `DELETE /files/{id}` (и очистка файла при повторном маппинге) делает seq scan на проверке self-FK — на временной таблице 200 тыс. строк удаление документа в 1000 строк 8,6 с без индекса и 16 мс с индексом (замер в psql на временной таблице, final review); при ~3 млн строк DELETE ≈ 2 мин → HTTP-таймаут и долгое удержание блокировок. Требуется DBML v5.16 + миграция 0004. Решает владелец (источник истины — DBML) | arm-core Срез 2c: final review (I-1, R-24, design №51) | `DELETE /files/{id}`, повторный маппинг; подсрез 2d (UI удаления), прод | **закрыт в v5.15 (06.10.2026):** индексы добавлены в DBML v5.15 (п. 4 changelog) и миграцию `0003_dbml_v5_15` arm-core — `ix_osv_entry_parent_entry_id`, `ix_osv_entry_recognition_job_id`, `ix_stored_file_current_recognition_job_id`; v5.16 и миграция 0004 не понадобились (v5.15 ещё не слит) |
| OQ-118 | Запрет загрузки и подтверждения документов после перехода версии в `DATA_CORRECTION` (ТЗ 03_9 A1: «после перехода к исправлению данных загрузка и подтверждение заблокированы»). В Срезе 4 загрузка и подтверждение блокировкой не ограничиваются и в `DATA_CORRECTION` разрешены | решение владельца 06.10.2026; arm-core Срез 4 design D-6 | Срез 7 (маршрут) | допущение принято: в Срезе 4 запрета нет (только статус «в работе»); запрет вводится вместе с маршрутом |
| OQ-119 | Истечение блокировки «взять в работу»: DBML не задаёт срока, перехват — только вручную с причиной. Нужен ли авто-сброс брошенной блокировки (таймаут, «сессия умерла») | arm-core Срез 4 design D-7 | Срез 4, 7 (эксплуатация) | допущение принято: блокировка не истекает; освободить — `DELETE …/lock` владельцем или перехват |
| OQ-120 | `document.period_check` для документов, входящих в версии консолидации: подходящесть документа версии — функция (документ, версия), а один документ может входить в несколько версий (разные периметры и кварталы), поэтому записывать её в `document.period_check` нельзя. Показывать ли «подходит версиям: …» на карточке файла/документа | arm-core Срез 4 design D-12 | карточка файла (подсрез 2d), §9.3 | допущение принято: `period_check` остаётся `UNKNOWN`; подходящесть видна в чек-листе версии |
| OQ-121 | Порог размера сводной ОСВ, после которого нужен кэш процесса: свод строится целиком в памяти на каждый запрос (страница, сводка, экспорт); синтетический тест (5,4 тыс. исходных строк → 1,8 тыс. строк свода) — 0,27 с / 8 запросов; на фикстурах РБ — 1385 строк свода (22 документа). Замер на реальной группе не делался; при десятках тысяч строк может потребоваться кэш (DECISION-5 запрещает кэш в БД) | arm-core Срез 4 design D-1, Risks | эксплуатация, Срез 5 (шахматка поверх свода) | допущение принято: без кэша; замер на реальной группе — до Среза 5 |
| OQ-122 | Ячейка чек-листа `PROCESSING` по всем недостающим столбцам ЮЛ, пока файл не смаплен: до маппинга счёт файла неизвестен, поэтому «в обработке» показывается у всех незакрытых правил ЮЛ. Устраивает ли такая неточность или нужна эвристика по имени файла | arm-core Срез 4 design D-13, №6 | чек-лист, экраны `34`–`37` | допущение принято: временный шум в чек-листе |
| OQ-123 | Общие строки `osv_entry` у нескольких версий: документ может входить в несколько версий одной ГК, ручное сопоставление контрагента хранится на строке `osv_entry` (DECISION-5) и поэтому меняет её для всех версий. Допустимо ли, или сопоставление должно быть «по версии» | arm-core Срез 4 design Risks, D-10 | Срез 4, 7 (замороженные версии не меняются — они в `ex_snapshot`) | допущение принято: сопоставление общее; `before_value` аудита хранит прежнее значение |
| OQ-124 | Q1-эквивалентность ОСВ (R-14, design №81): парсер помечает ОСВ за Q1 как нарастающую (период с начала года = Q1), поэтому документ ОСВ за `Q1` в Срезе 4 равнозначен и нарастающему, и квартальному и подходит версиям любого квартала года (в версиях Q2+ он — квартальный источник Q1). ББ/ОФР за Q1 версиям Q2–Q4 **не** подходят (R-14a: баланс на конец Q1). Подтвердить; если ББ/ОФР за Q1 тоже должны подхватываться — снять условие в `consolidation_periods.effective_cumulative` | arm-core Срез 4: Task 13, R-14/R-14a, design №81 | `fits` (привязка), `select_sources` (свод), чек-лист | **закрыт 07.10.2026 (владелец):** ОСВ за Q1 используется и как нарастающая, и как квартальная (первое звено цепочки для версий Q2–Q4); ББ/ОФР за Q1 — только версии Q1 (R-14a) |
| OQ-125 | Версия за Q1 с квартальным ОСВ Q1: spec говорит «квартальный Q1 равнозначен нарастающему», но по design №50 квартальный Q1 идёт путём склейки (дубли ключа суммируются, `None` → 0, `is_cumulative = false`), а нарастающий Q1 отдаётся строка в строку (`None` сохраняется, дубли остаются раздельными). Различие видно только на дублях ключа и пустых ячейках. Приводить ли обе формы к одной | arm-core Срез 4: Task 9 (T9.b), design №50 | `consolidated_osv.build`, версии за Q1 | допущение принято: два пути; расхождение — только на дубле ключа внутри квартала и `None` в ячейках |
| OQ-126 | Область ручного сопоставления контрагента (R-11, design №62/№63): «строка версии» сужена до строк свода — `group_type = COUNTERPARTY`, `data_type` BU/SUM привязанных ОСВ-документов; строки `NU`/`ACCOUNT` дают 404 `NOT_FOUND` («строка ОСВ не найдена в версии») и в автоприменение не входят (§3 п. 2, R-3 Среза 2c: НУ не участвует). Цена: NU-строки того же контрагента остаются без `counterparty_id`. Буквальное чтение spec шире. Подтвердить | arm-core Срез 4: Task 11, R-11 | `counterparty_mapping`, автоприменение | допущение принято и реализовано |
| OQ-127 | Рекомендация кандидата (`recommended`) — только точное совпадение `normalize_alias` имени строки с нормализованным именем/алиасом ЮЛ. На реальных данных «Юг-Финанс / ПЕКАРНЯ СЧАСТЛИВОЧНАЯ» (так записан контрагент в ОСВ РБ) рекомендации не даёт: аналитик находит ЮЛ поиском по `q`. Нужна ли нечёткая рекомендация (токены, порядок слов, префикс «Юг-Финанс /») или достаточно ручного выбора и накопления алиасов | arm-core Срез 4: Task 13 (O-3), design D-11 | `GET …/counterparty-candidates` | допущение принято: рекомендация точная; после первого ручного сопоставления алиас закрепляет имя |
| OQ-128 | Неоднозначность дорезолва на лету при тёзке по имени: после ручного сопоставления создаётся алиас, но `CounterpartyIndex.resolve` на шаге «нормализованное имя/алиас» возвращает контрагента только при единственном кандидате — если ЮЛ-тёзка (по нормализованному `name`) есть рядом с владельцем нового алиаса, кандидатов двое, резолв неоднозначен и строки без сохранённого `counterparty_id` остаются несопоставленными. ТЗ DOC-221278: «алиас однозначен» — решает ли это, или алиас должен иметь приоритет над именем | arm-core Срез 4: Task 11 (T11.9) | дорезолв свода (D-3), `CounterpartyIndex` | **закрыт решением владельца 07.10.2026 (вариант Б):** совпадение по алиасу приоритетнее совпадения по нормализованному имени — алиас уникален (`409 ALIAS_TAKEN`), поэтому однозначен; несколько совпадений только по имени → `NULL`. Правило — `05-domain-rules.md` §3 п. 4; реализация — RSHB/arm-core#57 |
| OQ-129 | Формулировка сценария «сопоставление и автоприменение» (spec consolidated-osv): «`unmapped_total` падает на 1 + propagated» неверна на реальных данных. `propagated` считает записи `osv_entry` (включая пары-пробелы и строки, не дающие строк свода), а свод склеивает кварталы, поэтому `unmapped_total` падает на число **склеенных** строк свода (R-15, design №80). Уточнить текст spec при архивации | arm-core Срез 4: Task 13 (T13.M1, R-15) | spec `consolidated-osv` при архивации change | допущение принято: тест сверяет обе величины отдельно; текст spec уточнён в change, `openspec/specs/` — при архивации |
| OQ-130 | ОСВ произвольного формата: ИНН в ячейке **на строку выше** названия контрагента (строка только с ИНН, без сумм). Сейчас такая строка уходит в OTHER, ИНН для резолва теряется. Решение владельца 07.10.2026: склеивать со следующей строкой контрагента; синтетические фикстуры на этот случай, на ИНН в тексте и на «ИНН/КПП» в одной ячейке | владелец, 07.10.2026 | tabular-parser §1.0 п. 4, §1.3; резолв контрагента в ядре | **реализовано** (RSHB/arm-tabular-parser#9, PR #15): строка только с ИНН над названием склеивается со следующей строкой контрагента (`inn_only_row`, предупреждение «ИНН … над строкой … отнесён к ней»), «ИНН/КПП» в одной ячейке (`inn_kpp_cell`); тесты — `test_classify.py` (`test_inn_only_row_above_is_glued_to_next_counterparty`, `test_inn_with_kpp_in_one_column_cell_gives_inn`), синтетика `s18_inn_row_above`, `s19_inn_kpp_cell`; `05-domain-rules.md` §1.3. Правило строки ИНН заменено OQ-163 (13.10.2026). |
| OQ-131 | Скользящий период ОСВ «Октябрь 2024 г. – Сентябрь 2025 г.» (НПЗ 60/62/76, Агрокомплекс 76, `osv-dkb/`): парсер берёт первый месяц → `2024 Q4`, нарастающий. Нужен разбор диапазона дат и признак «не с начала года»; как ядру относиться к такому документу при привязке к версии (не нарастающий и не квартальный) | пакет ДКБ 06.10.2026, прогон парсера | tabular-parser `periods.yaml`/`header.py`; ядро `fits` (D-5 Среза 4) | **реализовано в парсере** (tabular-parser PR #13 + Срез 2b): диапазон месяцев разбирается (`periods.yaml`: `months`, `range`), границы `period_start`/`period_end`/`is_from_year_start` — в контракте v1.2 (docs change 2b), скользящий период → `period_quarter = null`, `is_cumulative = null` и предупреждение «не с начала года» (`05-domain-rules.md` §1.5). **Открыто для ядра:** привязка скользящего документа к версии (допущение — не привязывать, показывать в чек-листе как «период не соответствует») |
| OQ-132 | Тождество сальдо по строкам (§1.4) на счёте 91 (и, вероятно, 90, 99): строки аналитики доходов/расходов несут только обороты, сальдо закрывается на 91.09 → `needs_check` на каждой ОСВ 91 (Цимлянский, Приманыческий). Исключать ли счета без сальдо по аналитике из проверки тождества строк | пакет ДКБ 06.10.2026 | tabular-parser `checks.py`, статус документа | **закрыт решением владельца 07.10.2026 (вариант Б):** счета без сальдо по аналитике исключаются из тождества строк §1.4; список — данные `rules/*.yaml` tabular-parser, начальный состав 91 и 99 (90 не включать: внутри года у субсчетов 90 сальдо есть); «Итого» и сумма детей = родитель сверяются. Реализация — arm-tabular-parser#11 п. 2 |
| OQ-133 | ОСВ варианта 2 без строки корневого счёта: верхний уровень — субсчета (58.01, 58.03), «Итого» = их сумме, парсер сверяет «Итого» с первым корнем → `needs_check` на каждом листе (Новоросметалл, Агрокомплекс; уточняет OQ-106). Сверять «Итого» с суммой строк уровня 0 | пакет ДКБ 06.10.2026 | tabular-parser `checks.py` | **реализовано** (tabular-parser PR #13, arm-tabular-parser#11 п. 3): при нескольких корнях уровня 0 «Итого» сверяется с суммой строк уровня 0 (сальдо по колонкам либо по нетто, как у родителя, R-21); `checks.py`, `05-domain-rules.md` §1.4 |
| OQ-134 | Шапка ОСВ без строки организации и с кодом/названием счёта в двух колонках («Счет: Код / Наименование», строка «Бухгалтерский \| 9 месяцев 2025 г.»; Новоросметалл ОСВ 63): парсер не находит организацию, счёт, период, тип данных. Новый вариант заголовка — словарь или код | пакет ДКБ 06.10.2026 | tabular-parser `detect.py`/`header.py`, `osv_headers.yaml` | **реализовано** (tabular-parser PR #13, arm-tabular-parser#11 п. 4): шапка без метки организации (организация — хвост названия ведомости), период и тип данных отдельными ячейками (`periods.yaml: standalone`, `data_types.yaml: standalone_header`), подписи «Код»/«Наименование» не группировки (`osv_headers.yaml: column_captions`), код счёта — из первой строки-счёта (с предупреждением); `05-domain-rules.md` §1.1; тест `test_header_without_labels_and_code_name_columns_are_found` |
| OQ-135 | Отчёт 1С «Анализ счета» с отбором по контрагентам группы (`fixtures/analysis-reports/`, в именах файлов «ОСВ_…ПоКомпаниямГруппы»): не ОСВ, но содержит ВГО по корреспонденции счетов. Нужен ли отдельный тип документа для Среза 5, или ВГО считаются только из ОСВ | пакет ДКБ 06.10.2026 | `document_type`, Срез 5 | открыт: допущение — не разбирать; парсер отвечает `INVALID_DOCUMENT` |
| OQ-136 | ИНН организаций пакета ДКБ (Цимлянский, НКЗ, Новоросметалл, Приманыческий, Агрокомплекс) в шапках ОСВ отсутствуют; для теста консолидации по этим группам нужны ИНН и состав ГК/ГСК (выгрузка ЕЦП или список от ДКБ) | пакет ДКБ 06.10.2026 | seed/фикстуры справочников, тесты Среза 4–5 на новых группах | открыт |
| OQ-137 | Раскладка горячих папок ИС Сканирование: scan-gateway кладёт `Input/<job_id>_<original_name>` через временный `Tmp/.<имя>.part`, ничего не удаляет; PDFWorker старого АРМ (`RSHB/armka_file_worker`, проверен в контуре) кладёт `Input/<task_id>.<ext>` через временный `<назначение>.<uuid>.tmp` рядом (ИС игнорирует `*.tmp`), читает батч `Output/<task_id>/*.json` и удаляет `Output/<task_id>/` и входящий файл после чтения. Какая раскладка актуальна? | сравнение `armka_file_worker` (README «Файлы», `clients.py`) и `scan-gateway/gateway/hotfolder.py`, `smb.py`; OQ-78, OQ-79 | scan-gateway (спека `image-recognition-gateway`, эмулятор, тесты) | **закрыт владельцем 06.10.2026: «правильно как в FileWorker»** — имя `<job_id>.<ext>`, очистка `Output/<job_id>/` и входящего файла после чтения; `Output/<job_id>/*.json` батчем и стабилизация набора (OQ-78) остаются. Реализация — arm-scan-gateway #6. **Уточнение (Срез 3a, решение владельца 06.10.2026):** вместо временного `*.tmp` рядом с назначением — `.part` в `Tmp` (в `Input` временных файлов не класть: ИС может подхватить недописанный файл, гарантии игнора `*.tmp` у нас нет; OQ-79 остаётся в силе), публикация без замены снимает `.part` сама; очистка — под флагом `SCAN_CLEANUP`, по умолчанию выключена (OQ-144) **Уточнение 09.10.2026 (решение владельца):** `.part` в `Tmp` отменён — запись прямо в `Input` с перезаписью, как в старом АРМ (arm-scan-gateway #26) |
| OQ-138 | Единицы «тыс. руб.» в шапке против значений в рублях с копейками в выгрузках 1С (Унагранде Компани/Групп, НПЗ) и тысяч с дробями (Межениновская): кто и где приводит единицы. Примеры: книга НПЗ (`npz/2025-Q3`) — ББ `needs_check`, `1600 = 1700` расходится на 1.94 / 9.02 / 2.48 руб.; ОФР НПЗ и ОФР Унагранде (стр. 1) — `needs_check` по 2100/2200/2300 по той же причине; полные формы по факту сходятся только у Межениновской | `fixtures/statements-xlsx/README.md`, tabular-parser design 2b D-6, D-7, решения №22–24 (R-11, R-13, R-14) | Срез 3 (маппинг форм), §8 (КФО в тыс. руб.) | **закрыт решением владельца 06.10.2026:** единица «тыс. руб.» в шапке считается верной, значения отдаются как в файле; расхождения итогов в пределах округления (до 0,001 % наибольшего числа соотношения) статус не портят — мягкие нарушения «в пределах округления» (arm-tabular-parser#20). Книга НПЗ, ОФР стр. 1 Унагранде — `done`. Принимать ли расхождение и приводить ли единицы — решает КА в ядре (акцепт проверок, Срез 3) |
| OQ-139 | Форма без колонки кодов: сопоставление по именам строк из шаблонов 66н/ФСБУ (словарь `statement_lines.yaml`, `line_code_confidence < 100`)? Сейчас документ без строк и `needs_check` («колонка кодов не найдена») | решение владельца 06.10.2026 (вопрос 6), tabular-parser design 2b D-4 | §12 п. 2, Срез 3 | открыт: нужен реальный файл без колонки кодов; допущение — не сопоставлять по именам; **связано (Срез 3a):** то же для PDF-пути — справочник кодов по редакциям, имена из OCR при пустой редакции (OQ-146, OQ-150) |
| OQ-140 | Страница-продолжение в отдельном файле (1С печатает ББ/ОФР постранично, один файл = страница): склейка в ядре по ЮЛ + период + тип формы; признак `is_continuation` / `form_page` в контракте (MINOR, две необязательных поля) | решение владельца 06.10.2026 (вопрос 2), design 2b D-8 | §12 п. 9, Срез 3, контракт v1.x | допущение принято: страницы разных файлов не склеиваются; страница 2 без шапки — документ с пустой шапкой и `needs_check`, «загрузите форму одним файлом»; **Срез 3b: допущение реализовано** — страницы одной формы из разных файлов не склеиваются: каждый файл — отдельный документ со своим периодом. |
| OQ-141 | Строка без кода, но с суммами в ОФР ФСБУ 4/2023 (НПЗ «Реализация иного имущества…»): хранить ли (OQ-36 расширяется с расшифровочных строк на строки с суммами) | `fixtures/statements-xlsx/npz`, design 2b D-6 | Срез 3, DBML `financial_statement_line` (хранит только строки с кодом) | допущение принято: строки без кода не отдаются, при наличии сумм — предупреждение «строк без кода с суммами пропущено: N» |
| OQ-142 | Образец ФСБУ 4/2023 (`templates/ББ после 01.01.2025.xlsx`): «АКТИВ» и код 1110 в одной строке из-за объединённых ячеек; шаблоны (коды есть, сумм и шапки нет) не данные. Нужен ли их разбор вообще | `fixtures/statements-xlsx/templates`, design 2b D-8 | §12 п. 10, тесты | допущение принято: шаблоны дают `needs_check` (шапка пустая, значения `null`); разбор шаблонов как источника данных не планируется, пока владелец не скажет иначе |
| OQ-143 | Лист продолжения с **единственной** кодовой строкой (книга НПЗ: лист ОФР с одной строкой 2400): `min_codes` = 3 → лист не распознаётся как форма и пропускается, строка 2400 теряется при `needs_check` документа. Понизить порог / принимать лист по близости к открытому документу ОФР / маркеру продолжения | tabular-parser design 2b: решение №25, O-9 | §12 п. 2, п. 9; полнота ОФР НПЗ | **реализовано в 2b (финальная волна, I-1):** лист с 1–2 кодами форм после листа формы берётся как продолжение (`min_codes_continuation = 1` в словаре) и присоединяется при совпадении числа колонок сумм и типа формы; не присоединён — `needs_check` и предупреждение; первая страница с 1–2 кодами формой не считается. Строка 2400 НПЗ в результате есть |
| OQ-144 | Права на удаление на SMB-шаре контура (`Input` — входящие файлы и `.done`, `Output/<job_id>/`, `Tmp`): можно ли сервису `scan-gateway` удалять файлы, и не удаляет ли их сама ИС Сканирование после обработки. Пока `SCAN_CLEANUP=false` — сервис ничего не удаляет (кроме `.part`, ставшего целью публикации записью без замены), в `Input`, `Output` и `Tmp` накапливаются остатки | решение владельца 06.10.2026, change `slice-3a-ocr-normalizer` D-2, решения №26/№27 | scan-gateway (горячие папки), эксплуатация стенда | открыт: допущение — очистка выключена; dev-compose включает `SCAN_CLEANUP=true`. Спросить платформу/владельцев ИС; при положительном ответе — включить флаг на стенде |
| OQ-145 | Редакция формы (`66n` / `fsbu4`) в контракте: нужна ли ядру редакция документа в конверте (необязательное поле `statementDocument`, кандидат MINOR 1.3), или ядро выбирает редакцию по `report_date` само, по тому же правилу §11.1 п. 10. Сейчас scan-gateway выбирает редакцию внутри и наружу её не отдаёт | change `slice-3a-ocr-normalizer`, открытый вопрос 2 | контракт `docs/contracts/` (v1.3), Срез 3b (маппинг форм, справочник кодов в ядре) | **закрыт решением владельца 07.10.2026:** редакция формы в контракт не добавляется (контракт v1.2); ядро определяет её по дате периода документа (`66n` при дате ≤ 2024-12-31, иначе `fsbu4`) и отдаёт в `GET /documents/{id}/statement` (`edition`), как реализовано в Срезе 3b. Досрочное применение ФСБУ 4/2023 до 2025 года — новый OQ при первом реальном случае |
| OQ-146 | Словари §7 (проверки форм) и §12 (справочник кодов `statement_lines.yaml`, формулы ОФР) **по редакциям** 66н / ФСБУ 4/2023 для tabular-parser (2b) и ядра (3b): один набор правил на обе редакции или раздельные; как выбирать редакцию в ядре для Excel-пути | change `slice-3a-ocr-normalizer` (справочник с редакциями только в scan-gateway), OQ-12, OQ-139 | tabular-parser `rules/*.yaml`, ядро (Срез 3b), §7, §12 п. 2 | открыт, **решение владельца 07.10.2026: решается вместе с OQ-150** (состав редакции `fsbu4`); до тех пор допущение прежнее — scan-gateway ведёт свой `api/rules/statement_lines.yaml`, tabular-parser и ядро словарь по редакциям не используют, формулы §7 одни на обе редакции; расширяет OQ-12 и OQ-139 |
| OQ-147 | Закрывающая скобка, распознанная OCR как `}` или `]` (`(7744536}` в `aep_3.json`): дословный перенос `parse_amount` минус не ставит — сумма положительная, `is_recognized = false`. Расширить ли множество закрывающих скобок в §11.1 п. 7 после проверки на стенде | change `slice-3a-ocr-normalizer`, решение №12; владелец 06.10.2026: «пока устроит как есть» | scan-gateway `normalizer/ocr/amounts.py`, §11.1 п. 7 | открыт: допущение — перенос дословный; знак теряется, строка подсвечена `is_recognized = false`, исходный текст — в `raw/` |
| OQ-148 | Длинное/среднее тире «—»/«–» как прочерк: дословный `parse_amount` вырезает тире как «лишний» символ → сумма `null`, но `is_recognized = false` даже при `confidence = 100` (дефис «-» даёт `true`). Лишний `needs_check` на формах с длинным тире. Считать ли прочерк любого вида распознанным | change `slice-3a-ocr-normalizer`, решение №20 | scan-gateway `amounts.py`, §11.1 п. 7, доля `done` на реальных сканах | открыт: допущение — перенос дословный; правило уточняется после прогона на стенде |
| OQ-149 | Строки таблицы OCR, отброшенные классификацией (поведение источника, §11.1 п. 4), не восстанавливаются: с непустым нечисловым кодом (`1150 :`) и с пустым кодом, но с суммами (в `aep_3.json` — «БАЛАНС» и «Выручка»: код пуст, мусор `211(1 \|` стоит в колонке значения). Нормализатор выдаёт предупреждения `строк с нечисловым кодом пропущено: N` и `строк без кода с суммами пропущено: N` (как OQ-141 для Excel), документ — `needs_check`, содержимое строк остаётся в `raw/`. Восстанавливать ли такие строки по наименованию (словарь редакции) или по структуре формы | `fixtures/ocr/aep_3.json`, `aep_6.json`, change `slice-3a-ocr-normalizer`, решение №29, §11.1 п. 4 | scan-gateway `rows.py`/`document.py`, полнота форм, OQ-36, OQ-139, OQ-141 | открыт: допущение — строки пропускаются с предупреждением; аналитик видит расхождение итогов в проверках 3b |
| OQ-150 | Состав редакции `fsbu4` (ФСБУ 4/2023, отчётность с 2025 года) в `api/rules/statement_lines.yaml` scan-gateway: коды и наименования строк ББ и ОФР; источник — Приложения 3–4 к ФСБУ 4/2023 (черновой перечень кодов — шаблоны `fixtures/statements-xlsx/templates/`, OQ-142). Пока `lines: {}` | change `slice-3a-ocr-normalizer`, D-7 | scan-gateway `api/rules/`, эталоны `fixtures/ocr/expected_normalize/` для документов 2025 года, статус `done` | открыт: **данные даёт владелец**, код нормализатора не меняется; до заполнения документы 2025 года идут с именами из OCR, предупреждением «редакция fsbu4 не заполнена» и `needs_check`. Порядок заполнения — `scan-gateway/README.md` **09.10.2026, решение владельца:** состав `fsbu4` (и `66n`) для ядра принят из шаблонов банка — `arm-core/api/rules/statement_lines.yaml` (arm-core#80); словарь scan-gateway — follow-up, копии расходятся (66н scan-gateway — словарь старого ядра). |
| OQ-151 | Эмулятор ИС Сканирование: файл, совпавший по sha256 с PDF из `SCAN_FIXTURES_DIR`, у которого нет JSON-набора (`<имя>.json`), отвечает «нет пары» (`INVALID_DOCUMENT`) и **не** уходит на `SCAN_EMULATOR_DEFAULT`. Пробел спеки эмулятора: считать ли такой файл промахом и отдавать набор по умолчанию | change `slice-3a-ocr-normalizer`, спека `scan-emulator` | scan-gateway `scan_emulator`, dev-стенд (`dev/qa-guide.md` §6) | открыт: допущение — поведение как есть; PDF без JSON-пары в `fixtures/ocr` не класть |
| OQ-152 | Повтор кода строки в таблице OCR: остаётся **первая** строка (паритет с Excel-путём, §12 п. 5; решение №15), но в `aep_6.json` (ОФР) код 2410 повторён, оставленная первая строка пуста, а отброшенная содержит `(649)`. Выбрать правило «первая строка с суммой» вместо «первая» (для `aep_3` оно даёт иной результат, чем для `aep_6` — нужна проверка на стенде) | change `slice-3a-ocr-normalizer`, решение №34 (R-16), финальное ревью I-2 | scan-gateway `document.py`, §11.1 п. 4, Excel-путь §12 п. 5 | открыт: допущение — первая строка; предупреждение «код N повторяется» сигнализирует аналитику, сумма отброшенной строки остаётся в `raw/` |
| OQ-153 | Журналировать ли скачивание первички (`GET /files/{id}/content`) и CSV чек-листа (ТЗ 8.2: перечень событий журнала включает «выгрузку»)? Экспорт сводной ОСВ в xlsx пишет `audit_event(EXPORT)` (Срез 4), а скачивание исходного файла и CSV чек-листа в Срезе 3b — нет; комментарии к документу тоже не аудируются (`comment` хранит автора и время) | change `slice-3b-statements-api`, design D-10, D-11, D-13, решение №8; ТЗ 8.2 | `audit_event`, `GET /files/{id}/content`, `GET …/checklist?format=csv`, §9.5 п. 6 | **закрыт решением владельца 07.10.2026 (вариант А):** скачивание исходного файла (`GET /files/{id}/content`, это же и просмотр PDF) и CSV чек-листа в `audit_event` не пишутся; `EXPORT` пишет только экспорт сводной ОСВ в xlsx (Срез 4) |
| OQ-154 | Автозамена файла работает на уровне файла (ключ документа: тип, счёт/NULL, год, квартал, нарастающий): исправленный отдельный ОФР заменит весь прежний файл-книгу «ББ + ОФР» того же периода (`SUPERSEDED`), а подтверждённый ББ из неё отвяжется от версий «в работе». Допустима ли такая замена на уровне файла, или формы одного периода нужно заменять по документам | финальное ревью Среза 3b (заметка контроллера), change `slice-3b-statements-api` | Срез 3b / 5 | **закрыт решением владельца 07.10.2026 (вариант А):** замена остаётся на уровне файла (DBML `file_status.SUPERSEDED`, ТЗ 8.2); если у заменённого файла есть документы, ключа которых нет в новом, — предупреждение в `metrics.mapping_warnings` нового файла (какие документы выпали, был ли `CONFIRMED`, из каких версий сняты привязки). Правило — `05-domain-rules.md` §9.5 п. 3; реализация — RSHB/arm-core#58 |
| OQ-155 | Эталонная ручная консолидация (`fixtures/golden/Консолидация_Группа РБ.xlsx`, лист `01.10.2024`, «в т. ч. взаимные обязательства») элиминирует у каждой компании **полную сумму её строк на компании группы** (Счастливочная 1230 = 108 062 тыс. = 62.01 «БП Производство» 106 311 116,46 + 62.01 «БП Аренда» 1 401 330 + 76.06 350 000; РБ 1520 = 106 311 тыс.), а ТЗ (DOC-223044, §4–§5) — по **парам** с `min` и порогом 5 %. Какой алгоритм верен | разбор OQ перед Срезом 5 (08.10.2026), сверка эталона с golden ОСВ РБ/Счастливочной Q3 2024 | Срез 5 (матчинг §5, тест сверки с эталоном), Срез 6 (КФО) | **закрыт решением владельца 08.10.2026: алгоритм ТЗ** — пары, `min`, порог 5 % (§4–§5); эталон сверяется только по точным парам (106 311 116,46 по правилу № 6), асимметрия эталона (1 751 тыс. у Счастливочной) — ожидаемое расхождение, описывается в тесте. Связано: OQ-07 (строго 1:1 или гибрид — ждёт владельца), OQ-08 |
| OQ-156 | Ответ ИС Сканирование склеивает соседние строки формы в одну: код `1500\n1700`, наименование `Итого по разделу V\n\nБАЛАНС`, суммы `917 775\n2 371 333` (ББ РБ и Счастливочной за 1 кв. 2025, `fixtures/ocr/Бухгалтерская отчетность 1 кв.25.json`, `БО 1 кв 2025.json`). Нормализатор отбрасывал строку как нечисловой код — в документе не было 1500 и 1700 | прогон 1 кв. 2025 на реальных ответах ИС (arm-ka-fixtures#16) | scan-gateway `rows.py`, §11.1 п. 4 | **решено** (владелец 08.10.2026): разлеплять, если в каждой непустой ячейке значений столько же частей, сколько кодов; иначе — как раньше; предупреждение «строк с несколькими кодами разлеплено: N», `needs_check`; реализация — RSHB/arm-scan-gateway#16 |
| OQ-157 | Чистый документ (без замечаний) требовал ручного «Подтвердить» (`RECOGNIZED` → `CONFIRMED`), хотя строки ОСВ не правятся (OQ-114) и ТЗ 03_9 п. 4 / 10_10 даёт «Загружен» сразу при отсутствии замечаний; на комплекте 1 кв. 2025 — 16 пустых кликов | прогон владельца 08.10.2026 | arm-core `services/auto_confirm.py`, `result_mapping`, `primary_files.delete`; §9.3, §9.5 п. 4–5 | **решено** (владелец 08.10.2026): чистый документ при маппинге сразу `CONFIRMED` системой (`confirmed_by` NULL), переход версии — от загрузившего; удаление автоподтверждённого файла — пока версии «в работе»; RSHB/arm-core#64, change `auto-confirm-clean-documents` |
| OQ-158 | В «Осталось N» (строки свода без ЮЛ и без ИНН) попадают строки аналитики, которые не являются контрагентами-организациями: «Ведомость в счет з/платы», подотчётные лица, физлица и т. п. — на РБ 2025 К1 таких большинство из 1291 строки без ИНН, и довести баннер до нуля сопоставлением с ЮЛ нельзя. Нужно ли отличать такие строки (признак «не организация» вручную / по словарю / по счёту) и исключать их из «Осталось N» и предупреждения [31-00] | жалоба владельца на «Указать» в сводной ОСВ (08.10.2026), RSHB/arm-core#74 | arm-core `domain/consolidated_osv.needs_mapping`, §3, экраны [30], [32-osv-no-inn], [31-00] | открыт: до решения такие строки считаются несопоставленными (EXTERNAL), переход к следующему шагу ими не блокируется — блокировки в API нет |
| OQ-159 | Note DBML `recognition_job.reply_deadline` «таймаут ожидания (OCR — 5 мин)» расходится с решением владельца 09.10.2026: срок ответа OCR — 20 мин (`DEFAULT_REPLY_TIMEOUT` в arm-core, `SCAN_TIMEOUT_SECONDS=1200` в scan-gateway). Поправить note при следующей версии DBML | решение владельца 09.10.2026 (не выпускать версию DBML ради note); arm-core #87, arm-scan-gateway #26, arm-ka-docs #84 | DBML `recognition_job`, `05-domain-rules.md` §9.4 | **закрыт 09.10.2026:** note исправлен в DBML v5.18 (docs #90) |
| OQ-160 | Целевая модель версий консолидации (владелец, 09.10.2026): несколько рабочих версий одного периода с **разным периметром** делят первичку (рабочий стол консоли, `stored_file.consolidation_id`); после `VERIFIED` новая версия (`previous_version_id`) может менять документы при **том же** периметре. Ядро сейчас создаёт только версию 1 (`POST /consolidations`), «новая версия» и параллельные версии не реализованы. Нужны правила: создание второй рабочей версии (обязательно другой периметр?), создание версии после `VERIFIED` (копия периметра, подхват привязок), что видит панель [39] у верифицированной версии до Среза 7 (ЕХ) | brainstorming docs #90, `openspec/changes/console-files/design.md` R-6, R-7 | срез «версии консолидации» (после Среза 4), Срез 7 | открыт: модель файлов совместима; реализация — отдельный срез |
| OQ-161 | Документ, период которого не совпадает с периодом консолидации, молча не привязывается к версии: ручное подтверждение проходит (200), а `fits` (`period_year` = год версии, D-5 Среза 4) его отбрасывает, `period_check` остаётся `UNKNOWN` (OQ-120) — свод пуст без объяснения (владелец 12.10.2026: ОСВ за 1 кв. 2025 в консолидации 2026 Q1). Период ОСВ берётся из шапки и через API не правится. Нужно ли (1) предупреждение «документ не подходит ни одной версии» при загрузке или подтверждении, (2) правка периода документа аналитиком (рядом с первичкой, как `original_*`), (3) постоянный режим «период из консолидации» для файлов, загруженных в неё | разбор владельца 12.10.2026; RSHB/arm-core#103 | `fits`, свод, чек-лист (§2, §9.3), карточка документа | **временное допущение (решение владельца 12.10.2026):** настройка `CONSOLIDATION_PERIOD_FROM_VERSION` (по умолчанию `false`; в контуре — `OPENBAO__APP__CONSOLIDATION_PERIOD_FROM_VERSION`) — ОСВ, загруженная в консолидацию (`stored_file.consolidation_id`) и не подходящая ей по периоду шапки, считается нарастающей за год и квартал консолидации для привязки, свода, чек-листа и колонки «ОСВ»; документ не меняется; ББ/ОФР и файлы с рабочего стола — по шапке; склейки кварталов нет (один слот на счёт ЮЛ). Только для тестов; открыт выбор постоянного решения (1)–(3), после него режим снять |
| OQ-162 | ОСВ 1С, шапка варианта 2 «Детализация по субсчетам, субконто: Контрагенты, Контрагенты.ИНН» (ООО «Зерновая компания „Новопетровская“», ОСВ 60 за 9 мес. 2025, замечание банка 13.10.2026): ИНН выводится **отдельной строкой под** контрагентом (уровень глубже, те же суммы, ИНН числом). Парсер принимал «Контрагенты.ИНН» за второй уровень контрагентов: в «Контрагент / аналитика» — `2370012036.0`, наименования нет, ИНН у строк `null` → нет признака ГК/ГСК/Внешний, суммы контрагента в своде дважды | замечание банка 13.10.2026; RSHB/arm-tabular-parser#32 | tabular-parser §1.1, §1.3; свод и резолв контрагента в ядре | **решено владельцем 13.10.2026:** `<группировка>.ИНН` — реквизит, не уровень; строка ИНН склеивается с родительским контрагентом (ИНН — родителю, строка из `rows` убирается, дети — к контрагенту), только если она единственная у родителя и суммы равны; иначе строка `OTHER` и `needs_check`; пустая ячейка ИНН (физлицо) — склейка без ИНН (рулинг R-1); целое число из ячейки xls — без «.0». `05-domain-rules.md` §1.1, §1.3; **реализовано** RSHB/arm-tabular-parser#32 (713 из 716 контрагентов файла получили ИНН, 3 физлица без ИНН). Правило строки ИНН заменено OQ-163 (13.10.2026). |
| OQ-163 | ОСВ, где ИНН контрагента выведен **отдельной строкой** рядом с наименованием, в любой раскладке: над / под × в дереве / не в дереве × с суммами / без (требование владельца 13.10.2026). До решения парсер склеивал только «над, не в дереве, без сумм» (OQ-130) и «под, в дереве» при подсказке «Контрагенты.ИНН» (OQ-162); «под, не в дереве, без сумм» отдавал ИНН следующему контрагенту | владелец, 13.10.2026; RSHB/arm-tabular-parser#35, #34 | tabular-parser §1.1, §1.3 | **решено владельцем 13.10.2026:** без подсказки в шапке строка ИНН — только с верной контрольной суммой ИНН (во всех раскладках, включая OQ-130); с подсказкой — без проверки, порядок элементов задаёт направление; «над, в дереве» — только единственный ребёнок; соседи — по суммам, без сумм — по направлению листа (шапка → пары по суммам → однозначные блоки); неоднозначное — строка `OTHER`, `needs_check`; ведущий ноль числовой строки ИНН (#34). Уточнения владельца (H-1): строка из 10/12 цифр с неверной контрольной суммой не склеивается, остаётся обычной строкой, предупреждение «похожа на ИНН …», `needs_check` (синтетика `s28`); (H-2): если пара найдена, но раскладка противоречит направлению подсказки `<группировка>.ИНН`, строка склеивается, но выдаётся отдельное предупреждение «стоит под/над наименованием … а шапка указывает над/под — проверьте» и `needs_check`. `05-domain-rules.md` §1.3; **реализовано** RSHB/arm-tabular-parser#35 |
Служебные идентификаторы в DBML/аналитике: `ANL-nnn` — замечания аналитика к макету; `ARCH-nnn` — архитектурные решения;
`DECISION-n` — решения по модели; `FIX-nn` — исправления дефектов v4.