Срез 0 (issue #1): раздел «Соглашения шаблона» (что оставлено от шаблона команды, что вырезается, что добавляется); change openspec/changes/slice-0-skeleton — proposal, design с таблицей «Decisions made without discussion», спеки и tasks; в openspec/config.yaml строки с двоеточием взяты в кавычки (YAML разбирал их как словари). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
7.0 KiB
7.0 KiB
Proposal: Срез 0 — скелет scan-gateway
Срез: 0 «Скелет» (../docs/context/09-delivery-plan.md). Репозиторий: scan-gateway. Issue: RSHB/arm-scan-gateway#1.
Родительский change: ../docs/openspec/changes/slice-0-skeleton (контракт v1.0, compose, CI-шаблон).
Соседние: ../arm-core/openspec/changes/slice-0-skeleton, ../tabular-parser/openspec/changes/slice-0-skeleton.
Why
Репозиторий — копия каркаса arm-core с фронтом и БД, тогда как по DECISION-4 это stateless-адаптер внешней ИС
Сканирование. Самая рискованная часть адаптера — не разбор JSON (Срез 3), а обмен через горячие папки с таймаутом и
без доступа к БД: положить файл, дождаться ответа, не потерять задание. Срез 0 строит этот конвейер и эмулятор ИС
Сканирование, чтобы он работал в compose без доступа к банковской системе.
What Changes
- Вырезать из шаблона (список —
CLAUDE.md, «Соглашения шаблона»):web/,setup/.npmrcи npm-шагиsetup/setup.sh; изapp.py— mount SPA, CORS, импортraisa_fastapi_protected_api;db/,models/,repositories/,alembic/,alembic.ini; настройкаOPENBAO.DATABASE; зависимостиasyncpg,SQLAlchemy,alembic,argon2-cffi,raisa_fastapi_protected_api; шаблонныйREADME.md. SMB остаётся (smbprotocol,OPENBAO.SMB, сессия в lifespan). - Инструменты:
api/pyproject.toml(ruff, mypy),requirements-dev.txt=-r requirements.txt+ инструменты,Dockerfile(строка убрана из.gitignore),Makefile,.env.example,.env.test;SettingsчитаетENV_FILE;SERVICE_NAME = "scan-gateway". - Контракт: вендоренные схемы
api/contracts/+ pydantic-моделиsrc/contract/; тест идентичности с../docs/contracts. - Горячие папки
src/gateway/: протоколHotFolder(put(name, data),poll(uuid) -> bytes | None) с двумя реализациями —LocalHotFolder(каталог; dev и тесты) иSmbHotFolder(smbclientшаблона; прод). Имена подкаталогов настраиваются (SCAN_INPUT_DIR = "Input",SCAN_OUTPUT_DIR = "Output"); файл кладётся как<job_id>_<original_name>, ответ ожидается в<output>/<job_id>/<job_id>.json. - Воркер
src/core/worker.py: durable pull-consumerscan-gatewayнаrecognition.image.request(max_deliver1 — OQ-15), доSCAN_MAX_INFLIGHT(4) заданий параллельно: S3uploads/…→ горячая папка → опрос каждыеSCAN_POLL_SECONDS(5) доmin(reply_deadline, now + SCAN_TIMEOUT_SECONDS=300)→ сырой ответ в S3raw/{job_id}.json→ нормализатор →results/{file_id}/{job_id}/result.json→ конвертrecognition.result(raw_s3_keyзаполнен) → ack. Таймаут →error.code = TIMEOUT. - Нормализатор как интерфейс
src/normalizer/: протоколNormalizer(raw_json) -> documents | error; в Срезе 0 —NullNormalizer: ошибки Content AI (error.codeиз ответа:FILE_READ_ERROR,INVALID_FORMAT,INVALID_DOCUMENT,RECOGNITION_FAILED,UNKNOWN_ERROR) пробрасываются как есть; успешный ответ →NOT_IMPLEMENTED(Срез 3 реализует §11). - Эмулятор ИС Сканирование
src/scan_emulator/(python -m src.scan_emulator): следит заInput/, черезSCAN_EMULATOR_DELAY(2 с) кладёт вOutput/<uuid>/<uuid>.jsonсодержимоеSCAN_FIXTURES_DIR/<имя без uuid и расширения>*.json(aep_5.pdf→aep_5.json,aep_8.pdf→aep_8_form4.json), для неизвестных имён — ответ Content AI сerror.code = INVALID_DOCUMENT. Запускается тем же образом в compose (scan-emulator). - Ошибки транспорта → результат: запрос не по схеме →
SCHEMA_MISMATCH; S3 не читается →FILE_READ_ERROR; горячая папка недоступна →RECOGNITION_FAILED; ответ ИС не JSON →INVALID_FORMAT; исключение →UNKNOWN_ERROR. - Healthcheck дополняется полями
natsиhotfolder(ok/down). - CI:
.gitea/workflows/ci.ymlиз шаблона docs.
Capabilities
New Capabilities
image-recognition-gateway: адаптер ИС Сканирование — приём задания из NATS, обмен файлами через горячие папки с таймаутом, сохранение сырого ответа, результат по контракту v1.0.scan-emulator: эмулятор ИС Сканирование для разработки и тестов, воспроизводящий протокол горячих папок на фикстурах../fixtures/ocr.
Modified Capabilities
Impact
- Таблицы DBML: сервис не пишет в БД. Косвенно:
recognition_jobядра получаетstatus,error_code(TIMEOUT,NOT_IMPLEMENTED, коды Content AI),service_name = scan-gateway,external_task_id— не передаётся (у ИС Сканирование идентификатор = нашjob_idв имени файла). Сырой ответraw/{job_id}.jsonв БД не регистрируется (v5.13 п. 5). - Правила: §11 — только как контракт нормализатора; §9.4 — статусы задания; OQ-13/14/15 — допущения в коде нормализатора (Срез 3).
- Зависимости: удаляются БД/фронт; остаётся
smbprotocol; добавляютсяruff,mypy,pytest*,jsonschema. - Внешние системы: NATS JetStream, MinIO/S3, SMB-шара ИС Сканирование (прод) / локальный каталог (dev).
Non-goals
- Нормализация ответа Content AI в
statementDocument(§11, OQ-13, OQ-14) — Срез 3. - PNG/JPG — транспорт не различает типы, но проверяется только PDF (пары в
fixtures/ocr). - Повторы OCR (
max_attempts= 1 по OQ-15), heartbeatprocessing. - Реальная SMB-шара в тестах —
SmbHotFolderпокрыт только unit-тестами с подменойsmbclient.