arm-scan-gateway/CLAUDE.md
Andrey Epifancev b127c02422 Срез 0 [7.5]: финальная волна, документы — T5.1, T5.3, T6.1, T6.3, T6.6, M-3
- README: верхняя граница задержки settle, шаг 5 «Полного круга»; критерии ошибочного файла (корень не объект)
- CLAUDE.md: по одному json на каждый распознанный документ; критерии ошибочного файла
- design.md: D-5 — оговорка про нестроковый error.code отдельным предложением; №79 — источник указания, SQL уборки в порядке FK
- open-questions-proposal.md п.20: замены ../docs/dev/README.md (185–187, 224–226, 252–253) и комментарий docker-compose.yml:184
- план: Task 6 — пометка про замену down -v (R-27)

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-04 16:01:13 +04:00

7.4 KiB
Raw Blame History

CLAUDE.md — scan-gateway

Общий контекст системы — ../CLAUDE.md и ../docs/. Этот файл — только про этот сервис.

Что это

Адаптер внешней ИС Сканирование (Content AI) для PDF/PNG/JPG: переименовать файл UUID_имя, положить в «Входящие» (через SCAN_TMP_DIR, по умолчанию Tmp: .part пишется туда, на той же ФС/шаре, и переименовывается), забрать все Исходящие/UUID/*.json (ИС кладёт по одному json на каждый распознанный документ), склеить и сохранить сырой ответ в S3 raw/{job_id}.json (в БД не регистрируется), нормализовать в ../docs/contracts/recognition-result.schema.json (statementDocument), опубликовать recognition.result. Таймаут → ошибка TIMEOUT по min(reply_deadline, старт + SCAN_TIMEOUT_SECONDS=300), старт — запись файла во «Входящие». Нет доступа к БД.

Правила

  • Нормализация по фактическим ответам ../fixtures/ocr/aep_*.json, а не по ТЗ (OQ-13, OQ-14): confidence → 0–100, поля организации/даты — по тому, что реально приходит.
  • Набор ответа готов по settle: есть *.json, а имена и размеры не менялись SCAN_SETTLE_SECONDS (по умолчанию 2; отсчёт от первого опроса, увидевшего окончательный набор). Не ждать «один файл <UUID>.json».
  • Склеенный raw/{job_id}.json: {"job_id", "files": [{"name", "response", "parse_error"}, ...]}, все *.json каталога по возрастанию имени; нормализатор получает ровно эту структуру.
  • Успех файла ответа — error.code == "" (error при этом не null!), ошибка — непустой error.code, parse_error или корень ответа не объект (INVALID_FORMAT). Проверка error is None для реального ответа неверна. Ошибочные файлы — в metrics.warnings (<имя>: <код>: <сообщение>); все файлы ошибочны → код первого, есть успешный → NOT_IMPLEMENTED (Срез 0).
  • В одном json может быть несколько таблиц разных форм (баланс, ОФР); тип документа задаёт label таблицы. Таблицы не интерпретируются в Срезе 0 — разбор в Срезе 3.
  • Dev-режим: эмулятор ИС Сканирование: по входящему <uuid>_<name>.<ext> кладёт в Output/<uuid>/ набор фикстур <name>.json + <name>_*.json как <uuid>.json / <uuid>_<суффикс>.json (атомарно, через Tmp): make emulator (то же, что python -m src.scan_emulator из api/); в контейнере — тот же образ с command: python -m src.scan_emulator.
  • Описание сервиса, протокол горячих папок, env, запуск и коды ошибок — README.md; требования и решения Среза 0 — openspec/changes/slice-0-skeleton/ (design.md).

Соглашения шаблона

Тот же шаблон команды, что у arm-core («Шаблон FastAPI», RSHB/DKB_ARMKA@ba172f8); общие соглашения — ../docs/context/03-architecture.md, «Соглашения шаблона команды». Репозиторий приведён к сервису без фронта и без БД, но с SMB. Команды make — из корня репозитория (рецепты сами заходят в api/), pytest — из api/.

Оставлено от шаблона: api/run_server.py (точка входа платформы, SERVER__HOST/PORT), api/src/app.py (lifespan: SMB-сессия, S3, NATS, воркер; GET /healthcheck; gzip/proxy middleware), core/config.py (pydantic-settings, OPENBAO__MINIO__*, OPENBAO__NATS__*, OPENBAO__SMB__*, DEBUG), core/exception_handlers.py (подключается в create_app), api/pytest.ini, api/requirements*.txt (включая smbprotocol), raisa_config.yml, setup/setup.sh + setup/pip.conf (без npm-шага). Заглушки шаблона без кода сервиса: src/api/ (пустой router, пустые dependencies/ и endpoints/), src/schemas/ и src/services/ (пустые пакеты), common/exceptions.py (NotFoundError, CouldntCreateError нигде не используются), core/logger.py (setup_logger нигде не вызывается).

Вырезано (Срез 0): web/, setup/.npmrc; из app.py — mount SPA, CORS, raisa_fastapi_protected_api; db/, models/, repositories/, alembic/; OPENBAO.DATABASE; из requirements.txt — asyncpg, SQLAlchemy, alembic, raisa_fastapi_protected_api; шаблонные core/nats_callback.py и README.md заменены своими.

Добавлено:

  • api/src/gateway/ — горячие папки: протокол HotFolder (hotfolder.py: LocalHotFolder, SettleTracker, inbox_name), smb.py (SmbHotFolder, импортируется лениво), фабрика create_hotfolder; merge.py — склейка файлов ответа в raw/; референс поллинга — ../reference-old-arm/api/src/core/PDFWorker.
  • api/src/core/ — worker.py (задание: проверка → ack → S3 → горячая папка → опрос → raw/ → нормализатор → результат; ошибки по таблице D-7), nats.py (core NATS без JetStream, queue group scan-gateway, публикация с повторами), clients.py (S3Storage), health.py (nats/hotfolder в /healthcheck).
  • api/src/normalizer/ — Normalizer, NullNormalizer (Срез 0: разбор по файлам склейки — все ошибочны → код первого, есть успешный → NOT_IMPLEMENTED, ошибочные файлы → metrics.warnings); настоящая нормализация — Срез 3.
  • api/src/contract/ + api/contracts/ — pydantic-модели и байт-в-байт копии схем ../docs/contracts; идентичность проверяет api/tests/unit/test_contract.py (входит в make check).
  • api/src/scan_emulator/ — эмулятор ИС Сканирование (SCAN_FIXTURES_DIR, SCAN_EMULATOR_DELAY, SCAN_EMULATOR_POLL_SECONDS); в compose контейнера пока нет — make emulator.
  • Makefile (venv, lint, test, test-integration, check, docker-build, check-full, up, emulator), api/pyproject.toml (ruff, mypy), Dockerfile + .dockerignore (один образ: шлюз и эмулятор), api/.env.example, api/.env.test.
  • Тесты: api/tests/unit/, api/tests/integration/ (маркеры integration, fixtures); перед PR — make check-full.