diff --git a/openspec/changes/unified-statement-normalization/design.md b/openspec/changes/unified-statement-normalization/design.md index e0b2270..b17732a 100644 --- a/openspec/changes/unified-statement-normalization/design.md +++ b/openspec/changes/unified-statement-normalization/design.md @@ -1,7 +1,7 @@ # Единый алгоритм нормализации ББ/ОФР (scan-gateway и tabular-parser) — дизайн -Issue: arm-ka-docs#116. Статус: черновик; текущая цепочка описана, целевая картина по шагам 1–9 — предложение, ждёт -решения владельца Р-7; Р-1…Р-6 решены (brainstorming 17.10.2026). +Issue: arm-ka-docs#116. Статус: целевая картина по шагам 1–9 согласована с владельцем (решения Р-1…Р-7, 17.10.2026); +следующий шаг — спека (дельты OpenSpec, контракт v1.3, §11/§12 в `05-domain-rules.md`, DBML v5.19). ## Цель @@ -316,9 +316,8 @@ sheet:,`. `DISCLOSURE` и форма без колонки кодов — ## Целевая картина Каждый шаг ниже: **вход = выход предыдущего шага**, единый выход для обоих сервисов, таблица «различие → к чему -приводим → что меняется в scan-gateway → что меняется в tabular-parser». Решения владельца помечены датой; остальное — -предложение (17.10.2026), решения, которые нужны от владельца, собраны в конце (Р-1…Р-7). Имена полей — рабочие, -уточняются при написании спеки. +приводим → что меняется в scan-gateway → что меняется в tabular-parser». Решения владельца помечены датой; сводка решений +Р-1…Р-7 — в конце (все приняты 17.10.2026). Имена полей — рабочие, уточняются при написании спеки. Принципы, из которых выведены решения по строкам: @@ -539,9 +538,9 @@ column_index?}`, а не строка. **Подтверждение проверкой (вопрос владельца 17.10.2026).** Проверка §7 — арбитр, уверенность — подсказка там, где проверки нет. По каждому значению сервис считает `needs_review`: -- `true`, если `confidence` ниже порога (Р-5: 100) **и** значение не покрыто сошедшимся жёстким соотношением §7 в своей - колонке (не входит ни в одно, соотношение не сошлось или оно мягкое); для `is_recognized = false` — то же правило, - если Р-7 = да, иначе всегда `true`; +- `true`, если `confidence` ниже порога (Р-5: 100) или `is_recognized = false` (число собрано из грязного текста, + Р-7) **и** значение не покрыто сошедшимся жёстким соотношением §7 в своей колонке (не входит ни в одно, соотношение + не сошлось или оно мягкое); - `true` у всех ячеек соотношения в колонке, где оно не сошлось, независимо от уверенности; - иначе `false`: значение подтверждено проверкой, не подсвечивается и статус не трогает; сырой текст и уверенность остаются в подсказке ячейки. @@ -550,7 +549,9 @@ column_index?}`, а не строка. кодом. Не покрыто проверками и остаётся на уверенности: поля шапки, справочные строки ОФР 25xx / 29xx, детализация без проверки родителя, `DISCLOSURE`, колонки, где есть только мягкие соотношения. Риск — две взаимно компенсирующие ошибки в одном соотношении; принимается (перекрёстные соотношения баланса его снижают). Закрывает AURORA-1524 без -ущерба для 1519. Распространение на `is_recognized = false` — Р-7. +ущерба для 1519. Сошедшееся жёсткое равенство подтверждает и догадку из грязного текста (Р-7, решение владельца +17.10.2026): «(7744536}» → −7 744 536 при 2100 = 2110 − 2120 верна; случайное совпадение требует второй, +компенсирующей ошибки в том же равенстве. **Код и наименование строки (Р-5, решение владельца 17.10.2026).** Уверенность кода подтверждается не §7 (перепутанные соседи 1220 и 1230 дают ту же сумму раздела), а структурой: код есть в перечне кодов формы и стоит по возрастанию @@ -589,7 +590,7 @@ column_index?}`, а не строка. делать при расхождении (владелец 17.10.2026: «наверное, будем пытаться сопоставить»). В сервисах уверенность и текст наименования на статус не влияют. -### Решения, которые нужны от владельца +### Решения владельца (Р-1…Р-7, 17.10.2026) - **Р-1** (шаг 1, п. 2) — **решено 17.10.2026: копия по скрипту.** Платформа не собирает Python-пакеты и не кладёт их в свой Nexus, поэтому общий код — dataclass-ы шага 1 (сериализуемые в json, golden-файлы в fixtures), шаги 2–8 и @@ -619,6 +620,30 @@ column_index?}`, а не строка. документе без даты с предупреждением `error` (`needs_check`, подтверждение руками); пропуск отвергнут как тихая потеря периода. Относительные подписи бланка 66н («На отчётную дату», «На 31 декабря предыдущего года», …) добавляются в словарь периодов и считаются от даты отчёта. -- **Р-7** (шаг 8, «Подтверждение проверкой») — снимать ли подсветку сошедшимся соотношением §7 и с `is_recognized = - false` (число собрано из грязного текста), а не только с низкой уверенности; предложение — да: сошедшаяся проверка - сильнее догадки о мусоре. +- **Р-7** (шаг 8, «Подтверждение проверкой») — **решено владельцем 17.10.2026: да.** Сошедшееся жёсткое соотношение §7 + снимает подсветку и с `is_recognized = false`; такой документ может получить `done` и автоподтвердиться, сырой + текст остаётся в подсказке ячейки. Варианты «всегда подсвечивать» и «снимать подсветку, но не автоподтверждать» + отвергнуты: первый возвращает ложные подсветки AURORA-1524, второй даёт статус «требует проверки» без подсветок. + +## Decisions made without discussion + +Решения агента 17.10.2026, принятые при оформлении целевой картины без отдельного обсуждения с владельцем; все +обратимы правкой спеки или словаря. + +1. **Уровни предупреждений** (шаг 8, п. 2): `error` — то, что меняет данные или их полноту (нет строки заголовка, + колонка без даты, тип по кодам противоречит заголовку, колонки сегмента не совпали); `info` — остальное (пропущена + подпись из словаря исключений, дробные при «тыс. руб.»). Цена ошибки: лишний или пропущенный `needs_check`, + правится таблицей уровней в спеке. +2. **`DISCLOSURE` всегда `done`** без условий о раскладке, строках и проверках — сохранено текущее поведение парсера. + Цена: пояснения не проверяются; так и сейчас. +3. **Порог столбца кодов** — «кодов ≥ 3 и доля среди непустых ≥ порога» взят у парсера как есть; `relaxed` для одиноких + продолжений снят, потому что продолжения склеиваются в шаге 1. Цена: таблица с одним-двумя кодами без столбца кодов + → документ без строк с `needs_check`. +4. **Начальный словарь исключений подписей** («Изменение», «Отклонение», «%», «Темп», «Доля», «Примечание») — по тестам + парсера; пополняется словарём, не кодом. +5. **Грамматика `locator`** — пары `ключ:значение` через `;`, ключи по источнику. Цена: минимальная, формат уже такой. +6. **`value`, который не число** (дата, логическое) в колонке сумм — разбор как текст. Цена: минимальная. +7. **`checks` отсутствует у документа без строк**, обязателен у `BALANCE` / `INCOME` со строками. +8. **Рабочие имена** `FormCandidate`, `TableLayout`, `RawLine`, `needs_review`, `line_status`, `line_code_text`, + `arm-statements`, `api/src/arm_statements/`, `sync-statements` — по образцу существующих (`sync-web.sh`); меняются + при написании спеки без последствий.