- 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>
7.4 KiB
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 groupscan-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.