docs(openspec): решения №30–34 по итогам финального ревью (Money, длина дат, .part, дубли yaml, дубли кодов), уточнение №29
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
This commit is contained in:
parent
02c6bc8762
commit
22025ecabe
@ -151,7 +151,7 @@ docs: bounded-PR (`03-architecture.md`, OQ-137, §11.1 п. 6 и 9, новые OQ
|
||||
| 9 | 2026-10-06 | Эмулятор пересчитывает индекс хешей при изменении списка файлов каталога, а не на каждый файл | дёшево и достаточно для dev | изменённый, но не переименованный PDF не переиндексируется |
|
||||
| 10 | 2026-10-06 | `label` колонки — сырой текст шапки с `\n` → пробел; `period_date` из `parse_date` | ядро показывает текст как есть | — |
|
||||
| 12 | 2026-10-06 | `parse_amount` переносится дословно: скобка, распознанная как `}` (`(7744536}`), минус не даёт — сумма положительная с `is_recognized = false` | проверенный код; расширение множества скобок — отдельное правило §11.1 после проверки на стенде | знак потерян, аналитик видит `raw/` и подсветку |
|
||||
| 13 | 2026-10-06 | В переносе `parse_amount` признак `verified` считается до постановки знака минус (в источнике — после, и любая сумма в скобках получала `verified = False`) | иначе каждая отрицательная сумма формы давала `is_recognized = false` и `needs_check` без причины; единственное отличие от источника, зафиксировано тестом `(6032409) → -6032409, verified` | расхождение с поведением старого ядра на стенде: там такие суммы подсвечивались, у нас — нет |
|
||||
| 13 | 2026-10-06 | В переносе `parse_amount` признак `verified` считается до постановки знака минус (в источнике — после, и любая сумма в скобках получала `verified = False`) | иначе каждая отрицательная сумма формы давала `is_recognized = false` и `needs_check` без причины; единственное отличие от источника, зафиксировано тестом `(6032409) → -6032409, verified` | расхождение с поведением старого ядра на стенде: там такие суммы подсвечивались, у нас — нет. Побочный эффект: скобки без содержимого (`()`, `( )`, `(.)`) дают `(None, verified = true)` (в источнике false) |
|
||||
| 14 | 2026-10-06 | `CONTRACT_VERSION` scan-gateway остаётся `1.0`, хотя копия схемы — v1.2 | документы 3a не используют полей 1.1–1.2 (`DISCLOSURE`, `checks`, границы периода ОСВ); потребитель принимает любой MINOR; меньше изменений в тестах конверта | если ядро начнёт различать MINOR производителя — одна константа и проверка в `worker_helpers` |
|
||||
| 15 | 2026-10-06 | Повтор кода строки в таблице OCR: остаётся первая строка, предупреждение, `needs_check` | DBML: уникальный индекс `financial_statement_line (meta_id, line_code)` — без этого маппинг 3b падает; Excel-путь 2b делает так же (§12 п. 5); старое ядро дубли не убирало | вторая строка с тем же кодом теряется в документе (остаётся в `raw/`) |
|
||||
| 16 | 2026-10-06 | `string_value` заполняется только при `amount = null` и непустом не-прочерке, текстом как распознан (`\n`/`\t` → пробел); у разобранных сумм — `null` | DBML `financial_statement_value.string_value`: «как распознано, если не число»; так же в 2b; первичная редакция спеки и старое ядро писали очищенный текст всегда | аналитик не видит сырой текст суммы на экране (он в `raw/`) |
|
||||
@ -168,4 +168,9 @@ docs: bounded-PR (`03-architecture.md`, OQ-137, §11.1 п. 6 и 9, новые OQ
|
||||
| 26 | 2026-10-07 | R-8. `.part`, ставший целью успешной публикации (`os.link` → назначение), снимается самой записью независимо от `SCAN_CLEANUP`; ошибка его снятия подавляется. Флаг управляет остатками: осиротевшие `.part`, `.part` при уже существующем назначении, ответы, входящий файл, `.done` | иначе при `SCAN_CLEANUP=false` на каждое задание оставалась бы жёсткая ссылка; ошибка удаления на шаре с неподтверждёнными правами не должна превращать доставленный файл в `RECOGNITION_FAILED` | на ФС без права удаления в `Tmp` у каждого задания останется `.part`-ссылка (место не занимает) |
|
||||
| 27 | 2026-10-07 | R-9. При `SCAN_CLEANUP=false` сервис ничего не удаляет и не логирует «осиротевший `.part`»; сценарий «Очистка выключена» приведён к «все файлы остаются на месте» | в слое горячих папок нет логгера, а поиск сирот потребовал бы обхода `Tmp` на каждое задание; ценность — только диагностическая | сироты в `Tmp` при выключенной очистке накапливаются без заметки в логе; чистятся вручную или включением флага |
|
||||
| 28 | 2026-10-07 | R-10. Ошибочный файл набора при хотя бы одном успешном понижает статус до `needs_check` (`done` невозможен) | спека «Ошибка одного из документов»: потерянный документ (например, ОФР) не должен остаться незамеченным под `done`; строка об ошибке уже в `metrics.warnings` | чаще `needs_check`; в D-6 («только документы») — уточнение |
|
||||
| 29 | 2026-10-07 | R-11. На таблицу — одно предупреждение `пропущено строк с нечисловым кодом: N` для строк с непустым нечисловым кодом, отброшенных `classify_row`; строки с пустым кодом — без предупреждения | иначе строки данных с мусорным кодом (aep_3: «Выручка» `211(1 |`, `БАЛАНС`) терялись молча; поведение источника (отбрасывание) не меняется | таблица с отброшенными строками всегда `needs_check`; восстановление таких строк — OQ-149 |
|
||||
| 29 | 2026-10-07 | R-11 (уточнён финальным ревью I-1). На таблицу — предупреждение `строк с нечисловым кодом пропущено: N` (непустой нечисловой код, строка отброшена `classify_row`) и предупреждение `строк без кода с суммами пропущено: N` (пустой код, в колонках значений есть хотя бы одна разобранная сумма — как в Excel-пути 2b, OQ-141); строки с пустым кодом без сумм — без предупреждения | в aep_3 «Выручка» и «БАЛАНС» имеют ПУСТОЙ код (мусор `211(1 \|` — в колонке значения), без предупреждения они терялись молча; поведение источника (отбрасывание) не меняется | таблица с отброшенными строками всегда `needs_check`; восстановление строк — OQ-149 |
|
||||
| 30 | 2026-10-07 | R-12 (финальное ревью C-1). Сумма с более чем двумя знаками после запятой (`1,234`, `0,125`) → `amount = null`, `string_value` — текст ячейки, `is_recognized = false`; `confidence` ячейки и полей шапки приводится к [0, 100], NaN → 0 | `Money` контракта — `^-?\d+(\.\d{1,2})?$`; иначе один ячейковый артефакт OCR (пробел тысяч как запятая) валил всю таблицу (`BACKEND_PARSING_ERROR`) и документ | значение остаётся в `string_value` и `raw/`, аналитик вводит сумму вручную |
|
||||
| 31 | 2026-10-07 | R-13 (финальное ревью I-3). Отклонение от дословного переноса: тексты дат длиннее 200 символов не разбираются (`None`), `fix_lookalikes` к ним не применяется | регулярки квартала/полугодия — кубическое backtracking (`"1" + " "*4000 + "b"` — 76 с, GIL держит весь процесс); дата — не более десятков символов | дата в аномально длинном тексте не найдена (не дата и так) |
|
||||
| 32 | 2026-10-07 | R-14 (финальное ревью). Локальная запись: `.part` создаётся заново (удаление старого + `O_EXCL`), fallback `os.replace` — только вокруг `os.link`, `EOPNOTSUPP` добавлен к списку | `.part`, оставшийся жёсткой ссылкой на уже опубликованный файл (подавленное удаление, OQ-144), перезаписывался бы на месте и портил файл, который читает ИС; EPERM на записи маскировался `FileNotFoundError` | — |
|
||||
| 33 | 2026-10-07 | R-15 (финальное ревью, T1.3). Загрузчик yaml отклоняет повторяющиеся ключи (`SafeLoader` с проверкой) | спека: «Коды справочника SHALL быть без дублей»; `safe_load` молча перезаписывает дубли, тест `len(set)` тавтологичен; владелец заполняет `fsbu4` вручную | битый справочник не стартует — это и нужно |
|
||||
| 34 | 2026-10-07 | R-16 (финальное ревью I-2). №15 (повтор кода → остаётся первая строка) НЕ меняется: в aep_6 ОФР код 2410 повторён, и оставленная первая строка пуста, а отброшенная (`(649)`) — со значением; выбор «первая с суммой» отложен владельцу как OQ | паритет с Excel-путём 2b (§12 п. 5); эвристика «первая с суммой» для aep_3 даёт иное, чем для aep_6 — нужна проверка на стенде | сумма отброшенной строки остаётся только в `raw/`; предупреждение «код N повторяется» сигнализирует аналитику |
|
||||
|
||||
@ -173,19 +173,21 @@
|
||||
- `values` — по одному на колонку документа (по позиции; если раскладка строки после повторной шапки даёт иное число колонок —
|
||||
недостающие значения как «ячейки нет», лишние отбрасываются, одно предупреждение на таблицу «повторная шапка изменила
|
||||
число колонок значений», решение №21; таблица без колонок значений — предупреждение «нет колонок значений», №25): `amount` — `parse_amount` (запятая → точка, скобки → минус, пробелы и прочие символы
|
||||
отбрасываются, несколько точек — разделители тысяч; прочерк/пусто → `null`); `string_value` — текст ячейки как
|
||||
отбрасываются, несколько точек — разделители тысяч; прочерк/пусто → `null`; сумма с более чем двумя знаками после запятой (`1,234`) не помещается в `Money` контракта — `amount = null`,
|
||||
`string_value` — текст ячейки, `is_recognized = false`, решение №30); `string_value` — текст ячейки как
|
||||
распознан (`\n`/`\t` → пробел, края обрезаны) **только если `amount = null`** и текст не прочерк (DBML: «как распознано,
|
||||
если не число»), иначе `null`; `confidence` — ячейки; `is_recognized = confidence == 100 и разбор не отбрасывал символов`;
|
||||
если не число»), иначе `null`; `confidence` — ячейки (значения вне 0–100 и NaN приводятся к диапазону, №30); `is_recognized = confidence == 100 и разбор не отбрасывал символов`;
|
||||
ячейки под колонкой нет → `amount = null`, `string_value = null`, `confidence = 0`, `is_recognized = false`.
|
||||
- `header` — `form_name` из `label` таблицы; `organization_name` и `organization_name_confidence` из `data_ocr.organization`
|
||||
(`\n` → пробел); `inn` — только цифры из `data_ocr.inn.form_name` (`null`, если цифр нет), `inn_confidence = confidence`,
|
||||
если сырой текст состоял только из цифр, иначе `min(confidence, 99)`; `units` — `clean_value(units.form_name)`;
|
||||
`report_date_text` — сырой текст `date.form_name`, `report_date` — `parse_date` после `fix_lookalikes`
|
||||
(`null`, если текст не дата); `okud`, `okpo`, `okopf`, `okfs`, `address` — `null` (ИС их не отдаёт).
|
||||
`report_date_text` — текст `date.form_name` с обрезанными краями, `report_date` — `parse_date` после `fix_lookalikes`
|
||||
(`null`, если текст не дата); `okud`, `okpo`, `okopf`, `okfs`, `address` — не передаются (в проводе отсутствуют, у потребителя `null`; ИС их не отдаёт).
|
||||
- `checks` MUST NOT присутствовать.
|
||||
Строка с непустым нечисловым кодом (`1150 :`, `211(1 |`) пропускается, на таблицу выдаётся одно предупреждение
|
||||
`пропущено строк с нечисловым кодом: <N>` (решение №29); строки с пустым кодом (заголовки групп, «в том числе») — без
|
||||
предупреждения.
|
||||
Строка с непустым нечисловым кодом (`1150 :`, `изо :`) пропускается, на таблицу выдаётся одно предупреждение
|
||||
`строк с нечисловым кодом пропущено: <N>`; строка с пустым кодом и хотя бы одной разобранной суммой в колонках значений
|
||||
пропускается с одним предупреждением на таблицу `строк без кода с суммами пропущено: <N>` (как в Excel-пути, OQ-141);
|
||||
строки с пустым кодом без сумм (заголовки групп, «в том числе») — без предупреждения (решение №29).
|
||||
Двойники букв и цифр (`fix_lookalikes`) SHALL применяться к тексту дат (дата отчёта и подписи колонок) только вплотную
|
||||
к цифрам; `|` SHALL считаться разделителем клеток, если первая строка значения состоит из цифр, пробелов и знаков
|
||||
пунктуации. Коды очищаются `clean_value`, суммы — `parse_amount`, ИНН — выделением цифр (как в проверенном на стенде
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user