10 Commits

Author SHA1 Message Date
Andrey Epifancev
64dab79290 docs: README и CLAUDE.md — адаптер OCR, arm_statements, контракт 2.0; интеграционные тесты (#46)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BJKMLHdxMwSUeYcdkwqX4W
2026-10-18 20:23:30 +04:00
Andrey Epifancev
192500e9f9 NATS: подписка, публикация и /healthcheck.subjects по настройкам; интеграционные тесты на своих subject'ах (#32)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 12:19:11 +04:00
Andrey Epifancev
7dc0317b99 feat(hotfolder): local тоже пишет прямо в Input; без SCAN_TMP_DIR; таймаут 1200 с (#26)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 16:34:39 +04:00
Andrey Epifancev
6c287ef4bc nats: subject'ы как в старом АРМ — files.*_uploaded, files.tabular_parsed (#22)
Решение владельца 08.10.2026: recognition.tabular.request → files.tabular_uploaded,
recognition.image.request → files.img_uploaded, recognition.result →
files.tabular_parsed (общий subject ack и результатов; files.img_parsed не
используется). Тела сообщений, Nats-Msg-Id, queue group'ы и schema_version не
меняются. Константы api/src/contract/subjects.py, докстринги, тесты, README,
CLAUDE.md; копии схем api/contracts/ из docs/contracts/ побайтно; change
nats-files-subjects с дельтой спеки. Сквозной change — RSHB/arm-ka-docs#74.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
2026-10-08 23:46:19 +04:00
Andrey Epifancev
c1288d08fb fix(normalizer): итоги финального ревью — Money, статус при ошибочном файле, пропущенные строки, длина дат, дубли yaml
- C-1 (№30): сумма с более чем двумя знаками после запятой не принимается (amount=null, string_value, is_recognized=false);
  confidence ячеек и шапки приводится к [0, 100], NaN и бесконечность дают 0
- F-A (№28): ошибочный файл набора при успешном понижает статус до needs_check
- F-B: «ИНН не распознан» только когда ИНН не извлечён, иначе «ИНН распознан неуверенно»
- №29: предупреждения «строк с нечисловым кодом пропущено: N» и «строк без кода с суммами пропущено: N»
- I-3 (№31): тексты дат длиннее 200 символов не разбираются и не проходят fix_lookalikes
- T1.3 (№33): загрузчик yaml отклоняет повторяющиеся ключи; тавтологичный тест дублей заменён настоящим
- гигиена: докстринг amounts.py описывает фактическое поведение (все точки удаляются), мёртвые noqa в ocr/__init__.py

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
2026-10-07 03:35:10 +04:00
Andrey Epifancev
bef8441ca8 docs(scan-gateway): README/CLAUDE.md под Срез 3a, roundtrip с документами, tasks.md
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
2026-10-07 02:42:49 +04:00
Andrey Epifancev
68389d5cc2 Срез 0 [7.4]: интеграционные тесты — наборы ответа ИС на реальных фикстурах (aep_3/aep_6/aep_5.PDF)
Сквозные сценарии с реальными NATS и MinIO: aep_3 (два файла, warnings про _ofr.json, склеенный raw),
aep_6 (NOT_IMPLEMENTED без metrics), aep_5.PDF (INVALID_DOCUMENT, повтор запроса), unknown (заглушка эмулятора).
settle 0.2 с, SCAN_TMP_DIR передаётся эмулятору. Исправлено устаревшее ожидание data_ocr в сценарии unknown (R-28).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-04 15:04:30 +04:00
Andrey Epifancev
973cf2717f Срез 0 [7.2]: склейка ответа ИС, сырой raw/ = склейка, NullNormalizer по файлам, metrics.warnings
- Исправлена критическая ошибка: NullNormalizer считал ответ ошибкой при `error is not None`,
  а у реального успеха ИС error = {"code": "", "message": ""} — aep_3.json и aep_6.json давали
  UNKNOWN_ERROR. Успех файла теперь — error.code == "" (отсутствующий/null/не-объектный error
  и error без кода ошибкой не считаются); регрессия — на реальных фикстурах (маркер fixtures).
- src/gateway/merge.py: merge_answers(job_id, files) -> MergedResponse
  {"job_id", "files": [{"name", "response", "parse_error"}]} по возрастанию имени; не JSON —
  parse_error файла; dump_merged — UTF-8 (с ASCII-экранированием при одиночном суррогате).
- Воркер: опрос отдаёт готовый набор → склейка → raw/{job_id}.json (application/json) до
  нормализации → Normalizer.normalize(склейка). Перечитывание «не JSON» убрано (R-24).
- NullNormalizer: все файлы ошибочны — код/сообщение первого по имени (parse_error и
  не-объект — INVALID_FORMAT, код вне контракта — UNKNOWN_ERROR с исходным кодом); есть
  успешный — NOT_IMPLEMENTED; ошибочные файлы — строки `<имя>: <код ИС>: <сообщение>`.
- NormalizeOutcome.warnings (R-25) → metrics.warnings конверта и файла результата; пустые
  опускаются; не больше 100 строк по 1000 символов, лишние — сводной строкой (R-30).
- Тесты: test_merge.py, test_normalizer.py переписан под склейку, test_worker.py — склейка в
  raw, ошибка одного файла, parse_error в warnings, все ошибочны, реальные aep_3+aep_3_ofr и
  aep_6, settle и таймаут ненастоявшегося набора; SUCCESS_ANSWER — в форме реального успеха.
  Интеграционный test_roundtrip.py минимально переведён на формат склейки (прогон — Task 5).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-04 14:27:07 +04:00
Andrey Epifancev
6552ebe345 Срез 0 [6.1]: сквозной интеграционный тест test_roundtrip.py, отметка задачи
Lifespan приложения и эмулятор ИС в процессе теста (R-7) против compose NATS/MinIO, горячая папка в tmp-каталоге.
Три сценария (R-10): aep_5.pdf (реальная фикстура) — INVALID_DOCUMENT от ИС, повтор во время ожидания и после
результата даёт ещё один ack без второго файла; ok_doc.pdf (синтетический успех) — NOT_IMPLEMENTED; unknown.pdf —
INVALID_DOCUMENT эмулятора. Проверяются схема конверта, service.name, Nats-Msg-Id, raw/<job_id>.json байт в байт,
входящий файл против объекта S3, /healthcheck, остановка без незавершённых задач и ошибок в логе.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-04 04:23:10 +04:00
Andrey Epifancev
5d82607715 Срез 0 [5.2]: слой NATS без JetStream (src/core/nats.py), интеграционные тесты, отметка задачи
NatsGateway: подключение с nkeys/JWT или без учётных данных, NATS_ENABLED=false — без подключения,
автопереподключение nats-py, subscribe_requests в queue group scan-gateway, publish с Nats-Msg-Id и
повторами (PUBLISH_RETRIES попыток всего) -> PublishFailed, publish_result для конвертов ack/result.
src.core.clients и src.core.nats — в strict-списке mypy. Интеграционные тесты против compose-NATS/MinIO:
queue group, заголовок Nats-Msg-Id, roundtrip S3, отсутствующий ключ.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-04 03:27:35 +04:00