Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BJKMLHdxMwSUeYcdkwqX4W
9.4 KiB
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). Цена ошибки: нет.