Andrey Epifancev 561eaa5c71 docs: соглашения шаблона в CLAUDE.md и OpenSpec change slice-0-skeleton
Срез 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>
2026-10-03 02:43:41 +04:00

7.0 KiB
Raw Blame History

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-consumer scan-gateway на recognition.image.request (max_deliver 1 — OQ-15), до SCAN_MAX_INFLIGHT (4) заданий параллельно: S3 uploads/… → горячая папка → опрос каждые SCAN_POLL_SECONDS (5) до min(reply_deadline, now + SCAN_TIMEOUT_SECONDS=300) → сырой ответ в S3 raw/{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), heartbeat processing.
  • Реальная SMB-шара в тестах — SmbHotFolder покрыт только unit-тестами с подменой smbclient.