Срез 3a: горячие папки как у armka_file_worker, нормализатор OCR → statementDocument v1.2 #8
Loading…
x
Reference in New Issue
Block a user
No description provided.
Delete Branch "feat/6-slice-3a-ocr-normalizer"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Closes #6
OpenSpec change:
openspec/changes/slice-3a-ocr-normalizer/(proposal, design D-1…D-9 + таблица решений №1–35, спекиimage-recognition-gatewayиscan-emulator). План:docs/superpowers/plans/2026-10-06-slice-3a-ocr-normalizer.md.Связанные PR: fixtures RSHB/arm-ka-fixtures#13 (эталоны
ocr/expected_normalize/), docs RSHB/arm-ka-docs#47 (§11.1, OQ-144…152, compose, qa-guide). Порядок мержа: fixtures #13 и docs #47 — раньше или вместе с этим PR (без fixtures-PRtest_goldenв основном checkout красный). docs #27/#44, scan-gateway #5 (копия схемы v1.2) и arm-core #27 уже слиты.Что сделано
api/src/normalizer/ocr/*,rules.py,api/rules/*.yaml):OcrNormalizerстроитstatementDocumentv1.2 по таблицамdata_ocr(тип по label/кодам, раскладка колонок по шапке или по данным, значения сis_recognized, редакции форм66n/fsbu4, статусыdone/needs_check/error RECOGNITION_FAILED, предупреждения). Функции очистки старого ядра перенесены дословно (§11.1) с зафиксированными отличиями (№13, №15, №16, №22, №30, №31).Input/<job_id>.<ext>,.partвTmp, запись без замены (local:os.link; smb:rename),cleanupподSCAN_CLEANUP(по умолчанию выключен).raw/,RECOGNITION_FAILEDбез расширения,OcrNormalizer(load_rules(SCAN_RULES_DIR))в lifespan.SCAN_EMULATOR_DEFAULT, индекс не пересчитывается на каждом проходе.make golden-update, эталоны в fixtures), интеграционный roundtrip (aep_3, aep_6, ok_doc, cleanup) с валидацией по JSON-схеме, README/CLAUDE.md.make check-full(lint, 1019 unit, 11 integration, docker build) — зелёный;openspec validate slice-3a-ocr-normalizer --strict— valid.CONTRACT_VERSIONостаётся1.0(решение №14).Процесс
SDD: 17 задач, на каждую — исполнитель + ревью задачи (spec + quality) + при необходимости раунды исправлений; затем финальное ревью всей ветки (opus), единая правка и повторное ревью. Все решения без обсуждения — в
design.md, раздел «Decisions made without discussion» (№17–35).Rulings I made
(решение — маркер/номер в design.md; цена ошибки — в design.md)
os.link+unlink вместо rename для записи без замены, fallbackos.replace— №17.value_idxs == (2, 4)вместо ошибочного5 in value_idxs; алгоритм не менялся.line_code_confidenceкапится 99 для кода вне выбранной редакции (спека, не код плана).is_recognized=false(дословныйparse_amount).valuesпо позиции колонки; расхождение числа колонок после повторной шапки — одно предупреждение.header.form_nameне передаётся при пустом label (контракт запрещает null).needs_check..part, ставший целью публикации, снимается самой записью, независимо отSCAN_CLEANUP.SCAN_CLEANUP=falseсироты.partне логируются.done.amount=null,string_value,is_recognized=false; confidence в [0,100], NaN → 0..partсоздаётся заново (O_EXCL); при существующем назначенииputничего не пишет.Находки (маркер — суть — статус)
Критичные/важные:
1,234) валила всю таблицу; NaN/вне диапазона confidencec1288d0c1288d0(649)fix_lookalikesc1288d0ecdf9a4fix_lookalikes746d9938ebf57237b3604)c1288d0.part; D-2 vs снятие.part5c2d89b/ спекаMinor (fixed): T1.3 (
c1288d0), T2.4, T4.2, T4.4 (c1288d0), T5.3 (c1288d0), T7.1/T8.9 (спека), T8.3–T8.7 (8ebf572/37b3604), T8.8 (№24), T9.1 (c1288d0), T9.3 (→№28), T9.4 (c1288d0), T10.2, T11.4 (5c2d89b), T11.6, T12.1–T12.3, T12.5 (a65ff8c), T13.1 (a65ff8c), T13.3, T13.4 (c1288d0), T13.7 (№27), T14.3–T14.5, T15.1–T15.5, T15.8 (576ce2c/140ec5b), M-1 (b956d57,cb8c71f), M-2 (c1288d0), M-5 README (fixtures 5dcfaf8), RR-2 (1c39ca1).Rejected: T3.2 (косметика ruff-format), T4.5, T5.4, T6.4 (з��щитный код), T7.5, T10.8 (нет влияния) — шум отчётов/нет влияния на код.
Deferred (пойдут в follow-up issue при закрытии): I-2; T1.4–T1.8, T2.2, T2.3, T3.1, T3.3–T3.5, T4.1, T4.3, T5.1, T5.2, T5.5, T6.1–T6.3, T7.2–T7.4, T8.10, T8.11, T9.2, T9.5, T10.3–T10.7, T11.5, T12.4, T12.6, T12.7, T13.2, T13.5, T13.6, T14.6 (в т.ч. OQ-151: хеш-совпадение с PDF без JSON-набора не уходит на
SCAN_EMULATOR_DEFAULT), T15.6, T15.7 (источник «Приложения 3–4 ФСБУ 4/2023» не проверен), M-3, M-4, M-5 (namingaep_8_form4), RR-1 (yaml merge keys<<:ломают загрузку правил), RR-3 (symlink-.partвTmpобходит nlink-проверку; нужен посторонний актор), RR-4 (тесты caller-side лимита дат не различающие), RR-5 (режим.part0o644 вместо0o666&umask), docs M1–M4.Наблюдения (O-n): O-1, O-2, O-6, O-7, O-8 — неточные отчёты исполнителей-haiku (ревью ловило); O-3 — длинное тире (№20); O-4 — ошибочный файл-сосед (решено №28); O-5 — в части коммитов trailer
Co-Authored-By: Claude Sonnet 5.5(фактическая модель) вместоOpus 5.5из задания, история не переписывалась; O-8 — исполнитель закоммитил игнорируемый файл отчёта, коммит снят до push.Что осталось владельцу
fsbu4вapi/rules/statement_lines.yaml(Приложения ФСБУ 4/2023); процедура и список тестов, фиксирующих пустую редакцию, — в README.aep_4/7/9/30–34без JSON-набора, эмулятор отвечаетINVALID_DOCUMENT).SCAN_CLEANUP=false), OQ-149 (восстанавливать ли строки с пустым/мусорным кодом), OQ-152 (повтор кода — «первая с суммой»), OQ-148 (длинное тире).🤖 Generated with Claude Code
https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
Исправлена строка 26 в fix_lookalikes: текст.replace("\xa0", " ") для корректной замены неразрывного пробела (U+00A0) на обычный пробел (U+0020), как в источнике date_utils._normalize. Добавлен тест test_nbsp_replacement для проверки замены NBSP с таблицей двойников и без неё. Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2- Добавлена FixtureIndex с индексированием по sha256 содержимого файлов (не-JSON) - build_index() строит индекс один раз, refresh() обновляет только при изменении списка файлов - resolve_stem() находит основу имени по хешу, при промахе возвращает default или (None, "none") - process_once() принимает параметр index и default, работает с файлами <uuid>.<ext> - Пропускаются dot-файлы, *.part, *.tmp, *.done (Review Focus 5) - Сообщение об ошибке: "{sha256[:12]}: нет пары по содержимому в фикстурах" - EmulatorSettings.default = "" для SCAN_EMULATOR_DEFAULT - Логирование источника пары: hash / default / none - Все 909 тестов зелёные, включая 54 новых и переписанных тестов эмулятора Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2- T14.1: процесс_once() без refresh если индекс передан; run() refresh перед вызовом, не после - T14.2a: спай на build_index — не вызывается повторно при неизменных фикстурах - T14.2b: реальный тест файла, появляющегося на проходе 2: ровно 2 build_index всего - T14.2c: добавлен dot-файл `.{uuid}.pdf.part` в skip-тест - Мутационные тесты падают при удалении проверок (не коммичены) - Typo: «фиксутрах» → «фикстурах» - 915 тестов проходят, make check зелёный Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2