## Context Сквозной дизайн — change docs `unified-statement-normalization` (`design.md`, `specs/arm-core/spec.md`, план `plans/2026-10-18-plan-3-arm-core.md`). Здесь — решения уровня ядра для части Б; решения части А — в `openspec/changes/unified-normalization-core-a/design.md` (D-1…, A1-1…A5-6, P3-1…P3-24). Схема данных — `../docs/model/data-model-v5.19.dbml`; код части А уже в `main`. ## Goals / Non-Goals Goals и Non-goals — см. `proposal.md`. ## Decisions - **D-1. Единственная точка входа в пакет.** `import arm_statements` — только `api/src/services/statements_engine.py` (протокол `StatementsEngine` и его реализация) и `api/src/domain/stored_form.py`; остальной код ядра работает с протоколом. - **D-2. §7 хранится только как `document_issue`.** `GET …/statement` и `POST …/confirm` читают нарушения из записей; временный расчёт на лету (P3-14 части А) удаляется в B6. - **D-3. Архивация вместе с частью А (R-7 части А).** `unified-normalization-core-b` архивируется только вместе с `unified-normalization-core-a` и только после выкладки обеих частей; сначала архивируется А, затем Б (дельты Б MODIFIED опираются на требования, которые А добавляет). - **D-4. Дельта «копия словаря §7 ядра» оформлена как MODIFIED.** Требования с таким именем в `openspec/specs/` нет: упоминание копии живёт в «Соотношения §7 по колонкам»; поэтому удаление выражено правкой этого требования (и пунктом «Словари ядра» в `documents-api`), а не `REMOVED`. ## Decisions made without discussion Решения, принятые без обсуждения с человеком на этапе плана и исполнения части Б (маркеры `P3-n` — из плана 3, `R-n` — рулинги контроллера, `Р-n` — решения владельца). Таблица дополняется задачами B1–B6. | Дата | Решение | Обоснование | Цена ошибки | |---|---|---|---| | 2026-10-18 | **R-1 (Р-40).** `CODE_NOT_IN_DICTIONARY` / `CODE_NOT_IN_EDITION` пишутся только для кодов из ровно четырёх цифр; пятизначные подстроки Excel не проверяются. **P3-7 плана в этой части заменён** (запись на код документа сохраняется, пятизначные коды — нет) | решение владельца 18.10.2026 (Р-40) перед исполнением Б; закрывает O-3 (поток `info` у пятизначных) | пропущенный `info` у нестандартного пятизначного кода; восстановление — снять условие длины кода в маппере 2.0 и пересчёте | | 2026-10-18 | **R-2 (Р-34).** Подтверждение кода строки — флаг `is_confirmed`; `line_code_confidence` и `line_code_modified` не меняются (P3-16 части А) | решение владельца 18.10.2026; в пересчёте Б правило `is_confirmed ⇒ needs_review строки = false` берётся из P3-16 | нет — поведение уже реализовано в части А | | 2026-10-18 | **R-3.** Удаление копии словаря §7 в дельте оформлено как MODIFIED «Соотношения §7 по колонкам», а не REMOVED (см. D-4); «Ответ `GET …/statement`» и порядок 409 оформлены новыми ADDED-требованиями, так как в `openspec/specs/` и дельтах части А одноимённых нет | `openspec archive` требует, чтобы имя REMOVED/MODIFIED существовало; иначе архив упадёт | при архивации — правка имён заголовков дельт | | 2026-10-18 | **P3-25 (B1).** Тип формы во входе пересчёта — `document.document_type` (`BALANCE` / `INCOME` / `DISCLOSURE`), а не `meta.statement_type` (только `balance` / `income`); план писал «тип формы» без источника | `DISCLOSURE` не представим в `statement_type`; тип документа — единственный источник для всех трёх типов | форма `DISCLOSURE` собралась бы с ошибкой `KeyError` / потерей типа; правка — одна строка в `build_stored_form` | | 2026-10-18 | **P3-26 (B1, O-3 часть А).** Копия `statement-code-orders.yaml` в `api/contracts/` не заводится: перечень кодов читает пакет из своей байтовой копии `api/arm_statements/rules/statement_code_orders.yaml` (`loader._ORDERS`), ядро файла не открывает; идентичность копии пакета с `docs/contracts/statement-code-orders.yaml` проверяет `sync-statements --check`; тест идентичности контракта сверяет только `*.json`. Сейчас копия пакета побайтно равна файлу docs | второй экземпляр файла в ядре никто бы не читал и он мог бы разойтись с пакетом | нет; если sync-statements когда-либо перестанет сверять YAML — добавить сверку в `contract_identity.py` | | 2026-10-18 | **P3-27 (B1).** `edition_codes` берёт строки `ok` результата `2.0`, сортирует по `row_index` и помечает код уверенным при `line_code_confidence == 100` (ровно), `None` — неуверенный | как в разделе «Что ядро вызывает» и в `recompute` пакета (код правленый/подтверждённый там — `code_fixed`) | редакция приёма разойдётся с редакцией пересчёта; сверку делает `test_edition_codes_and_engine_edition` |