72 lines
9.4 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.

## Context
Решение принято в docs change `unified-statement-normalization` (RSHB/arm-ka-docs#116); подробный дизайн — его
`design.md` (`docs/openspec/changes/unified-statement-normalization/design.md`, до архивации; после — в архиве docs).
План реализации — `plans/2026-10-18-plan-5-scan-gateway.md` того же change'а.
## Decisions made without discussion
Дата всех решений — 18.10.2026. Маркеры: `P5-n` — решения автора плана, `R-n` — рулинги контроллера SDD. Формат пунктов P5-n:
решение, обоснование, цена ошибки — как в плане (без сокращений).
- **P5-1.** Сетка OCR — одна строка сетки на строку ИС по возрастанию `row_index`, пропуски `row_index` пустыми строками
не заполняются; ширина строк таблицы — `max(column_index) + 1`. «Плотная по `row_index`» прочитано как «по порядку
`row_index`», `ref` всё равно несёт номер ИС. Цена: шаг 2 не видит «дыры» между строками (в реальных json их нет).
- **P5-2.** Нарушенная структура таблицы ИС (повтор `row_index`, повтор `column_index` в строке, нецелые индексы, `rows`
не список) — исключение адаптера → `TABLE_PARSE_ERROR` всей таблицы. Сегодня такие ячейки молча пропускались или
перезаписывались; повтор `row_index` сломал бы уникальность `ref`. Цена: одна битая ячейка теряет таблицу (видно —
`error`).
- **P5-3.** `value` ячейки: `None` → `""`, не строка → `str(value)`, без `strip`; `confidence`: число (не `bool`) →
`[0, 100]`, иначе `0` (как `clamp_confidence`, §11.1 «NaN → 0»). Цена: числовой `value` ИС (не встречался) даст текст
`"1234.0"`.
- **P5-4.** Ячейка ИС с пустым текстом — `Cell("", …)`, а не `None`: ИС отдаёт ячейку на каждую позицию строки, `None` —
только там, где ячейки нет. Цена: нет.
- **P5-5.** Шапка: поле ИС отсутствует, не объект или `form_name` пустой → `None` (а не `Cell("", 0)`); `ref` полей шапки —
`data_ocr.<поле ИС>` (пакет `ref` шапки в выход не переносит — у `HeaderField` его нет, план 1, раздел API);
`data_ocr.label` (заголовок комплекта) не используется. Цена: `<поле>_text` пустого поля — `null`, а не `""`.
- **P5-6.** `evidence.title = Cell(label.form_name, …, ref="table:<i>.label")`; `header.form_name` контракта —
`document.header.form_name` пакета (= `evidence.title.text` как в источнике, пустой — `null`, план 1 P1-29); шлюз
`form_name` сам не ставит и пару «кандидат → документ» не хранит.
- **P5-7.** Успешный json без `data_ocr`, без `table_structure` или с пустым `table_structure` — `JSON_FAILED` (`error`):
«ничего не выбрасывать молча» (§11, принцип 2). Цена: если ИС вернёт успешный пустой json рядом с нормальными, конверт
будет `needs_check` (вопрос владельцу — Q-1).
- **P5-8.** Все json набора ошибочны — конверт `error` с кодом первого, а `metrics.warnings` всё равно перечисляют
`JSON_FAILED` каждого json (как строки Среза 0): иначе коды остальных json теряются. Цена: спека называет
`JSON_FAILED` «при успешном соседе»; на `error`-конверт ядро `RECOGNIZED` не считает, вреда нет (Q-2).
- **P5-9.** `TABLE_PARSE_ERROR.message` = `BACKEND_PARSING_ERROR: <тип>: <текст>` (текст до 300 символов) — код старого
§11.1 п. 9 сохранён в тексте для поиска по логам и golden. Цена: нет.
- **P5-10.** Предел `metrics.warnings` (100 структур, `message` до 1000 символов) сохранён; при переполнении остаются все
`error`, затем `info`; строки-сводки нет — новый код предупреждения вводится только словарём пакета. Список в конверте
и в файле результата одинаков (спека). Цена: при > 100 предупреждениях часть `info` не видна ни там, ни там (сырой
ответ — в `raw/`).
- **P5-11.** `statements_version` читается при создании `OcrNormalizer`; нет `VERSION` — сервис не стартует. Версия едет в
`NormalizeOutcome.statements_version` и в `service.statements_version` файла результата; в конверт не пишется (в
схеме конверта поля нет). Цена: dev-checkout без `VERSION` не стартует (копию всегда раскладывает скрипт).
- **P5-12.** `api/rules/` удалён целиком, `SCAN_RULES_DIR` убран из настроек; словари — `default_rules()` пакета. Цена:
переопределить словарь без релиза нельзя (и не нужно — словари меняются через `arm-statements`).
- **P5-13.** `CONTRACT_VERSION = "2.0"` — для файла результата и всех конвертов, включая ack; MAJOR запроса —
отдельная константа (`SUPPORTED_MAJOR = 1`).
- **P5-14.** Golden v2 — `{schema_version, status, error, metrics, documents}` файла результата без `job_id`, `file_id`,
`service`, `raw_s3_key` (`statements_version` меняется при каждой раскладке копии); успешный golden дополнительно
проходит схему v2.0. Цена: golden не ловит регресс поля `service`.
- **P5-15.** Перевод в контракт — `to_contract` / `warning_to_contract` пакета (план 1, P1-26): у предупреждений
документа нет `locator` и пустых необязательных ключей (схема не допускает `null` у `column_index` и `field`), у
нарушений §7 нет `column_key`, у предупреждений файла `locator` есть; шлюз только валидирует `dict` своими моделями.
Цена: правка правил перевода — в `arm-statements`, а не здесь.
- **P5-16.** Модули: `OcrNormalizer` остаётся в `src/normalizer/ocr/__init__.py` (меньше правок `app.py` и тестов),
адаптер — `ocr/adapter.py`, обход набора — `ocr/answer_set.py`, ошибки json — `normalizer/files.py` (снимает цикл
импортов `src.normalizer` ↔ `src.normalizer.ocr`); своего конвертера в контракт (`contract_map.py`) нет.
- **P5-17.** Review Focus 3 утверждает `done` для обоих документов «БО 1 кв 2025» и «Бухгалтерская отчетность 1 кв.25»
(proposal, Р-23). Падение при верном кандидате — находка для пакета, а не правка шлюза; ослабить утверждение может
только контроллер рулингом.
- **P5-18.** `metrics.rows` — число строк `lines` всех документов (как в v1.2), теперь включая строки без кода.
- **P5-19.** `NullNormalizer` сохраняется для тестов воркера и выдаёт `JSON_FAILED` через `make_warning` пакета.
- **P5-20.** Обход набора зовёт `normalize_candidate` на каждую таблицу, а не `normalize`: исключение одной таблицы —
`TABLE_PARSE_ERROR` с `locator` таблицы, остальные таблицы разбираются (план 1: `normalize` исключения кандидата не
перехватывает). `AnswerSetResult.documents` — документы пакета как есть (заголовок — в `header.form_name`).
- **R-1.** Коммиты ветки несут трейлеры `Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>` (бриф владельца) и
`Claude-Session: …` (план) — совпадает с обоими источниками. Цена ошибки: нет.
- **R-2 (Р-36).** Задача 4 копирует из `docs/contracts/examples` только существующие файлы верхнего уровня как есть, новых
примеров контракта верхнего уровня не добавляет (решение владельца Р-36). Цена ошибки: нет.