- open-questions-proposal.md: compose-фрагмент scan-emulator/scan-gateway, таблица предложений по контракту и OQ, чеклист для человека (файлы в ../docs не менялись) - .env.example: SCAN_EMULATOR_POLL_SECONDS; уточнён комментарий PUBLISH_RETRIES (повторы после первой попытки) - отмечена задача 16 плана и пункт 6.3 tasks.md Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
10 KiB
10 KiB
Срез 0 — скелет scan-gateway — план исполнения (Срез 0)
For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (
- [ ]) syntax for tracking.
Branch: feat/1-slice-0-skeleton (worktree .wt/slice-0/scan-gateway). Issue: #1.
Spec: openspec/changes/slice-0-skeleton/ — proposal.md, design.md, specs/*/spec.md этого change'а. Спека — единственный источник истины при рулингах; этот план сгенерирован из её tasks.md 1:1 (номера в скобках — исходные номера задач tasks.md; по завершении задачи отметить её - [x] и в tasks.md).
Global Constraints
- Сервис без БД и фронта (DECISION-4), SMB остаётся. Раскладка шаблона, команды из
api/. - Платформа: Python 3.11, NATS без JetStream (queue group
scan-gateway, ackprocessingдо чтения S3, LRUjob_id, публикация с повторами), один бакет S3, Nexus недоступен, CI нет (make check/make check-full). - Горячие папки: протокол
HotFolder, режимыlocal/smb, каталогиSCAN_INPUT_DIR/SCAN_OUTPUT_DIR(по умолчаниюInput/Output), файл<job_id>_<original_name>, ответ<output>/<job_id>/<job_id>.json; таймаутmin(reply_deadline, старт + SCAN_TIMEOUT_SECONDS=300), опрос 5 с,SCAN_MAX_INFLIGHT=4; повторной отправки в ИС нет (OQ-15). - Сырой ответ → S3
raw/{job_id}.jsonдо нормализации,raw_s3_keyв конверте; в БД не регистрируется.NullNormalizer: ошибка ИС → её код, успех →NOT_IMPLEMENTED. - Эмулятор:
python -m src.scan_emulator, фикстуры../fixtures/ocr/<name>*.json, подстановкаfile_uid, неизвестное имя →INVALID_DOCUMENT,.doneпосле обработки. - Контракт: копии схем из
../docs/contracts/байт в байт;service.name = "scan-gateway";Nats-Msg-Idпо правилам контракта. - Линтеры: ruff (120, E,F,I,UP,B,PL) + mypy; pytest-asyncio auto; маркеры
integration/fixtures. - Язык комментариев/коммитов — русский, идентификаторы — английские; коммит заканчивается строкой
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>.
Задачи
Task 1: [1.1] Удалить web/, setup/.npmrc, npm-шаги setup/setup.sh, api/alembic/, `api/
- Удалить
web/,setup/.npmrc, npm-шагиsetup/setup.sh,api/alembic/,api/alembic.ini,api/src/db/,api/src/models/,api/src/repositories/; убратьDockerfileиз.gitignore; проверка:git status— только удаления и.gitignore
Task 2: [1.2] src/app.py
src/app.py: убрать mount SPA/os.makedirs("web"), CORS, импортraisa_fastapi_protected_api; оставить SMB-сессию (только приSCAN_HOTFOLDER_MODE=smb);src/core/config.py: убратьDBSettings; добавитьENV_FILE,SERVICE_NAME="scan-gateway",NATS_ENABLED=true,PUBLISH_RETRIES=3,DONE_CACHE_SIZE=1000,SCAN_HOTFOLDER_MODE="local",SCAN_HOTFOLDER_PATH,SCAN_INPUT_DIR="Input",SCAN_OUTPUT_DIR="Output",SCAN_POLL_SECONDS=5,SCAN_TIMEOUT_SECONDS=300,SCAN_MAX_INFLIGHT=4;requirements.txtбезasyncpg,SQLAlchemy,alembic,argon2-cffi,raisa_fastapi_protected_api; проверка: чистый venv (python3.11),python run_server.pyс.env.example,/healthcheck200
Task 3: [1.3] README.md — описание сервиса (назначение, протокол горячих папок, env, запуск
README.md— описание сервиса (назначение, протокол горячих папок, env, запуск против compose и эмулятора),CLAUDE.md— актуализировать «Соглашения шаблона» по факту; проверка: упомянутые файлы существуют
Task 4: [2.1] api/pyproject.toml (ruff, mypy), requirements-dev.txt = `-r requirements.txt
api/pyproject.toml(ruff, mypy),requirements-dev.txt=-r requirements.txt+ ruff, mypy, pytest, pytest-asyncio, pytest-env, httpx, jsonschema;Makefile:venv(python3.11, безraisa_*),lint,test,test-integration,check,check-full,up,emulator; проверка:make venv && make lintзелёные
Task 5: [2.2] Dockerfile (`python
Dockerfile(python:3.11-slim;CMD python run_server.py, эмулятор — черезcommandв compose),.env.example,.env.test; проверка:docker buildуспешен без Nexus
Task 6: [3.1] api/contracts/ — копии схем и examples/ из ../docs/contracts
api/contracts/— копии схем иexamples/из../docs/contracts;src/contract/— pydantic-модели (конверт со статусамиprocessing | done | needs_check | error);tests/unit/test_contract.py— идентичность (skip без docs), примеры, round-trip; проверка: тесты зелёные
Task 7: [4.1] src/gateway/hotfolder.py
src/gateway/hotfolder.py: протоколHotFolder,LocalHotFolder(put/poll/healthy);tests/unit/test_local_hotfolder.pyнаtmp_path; проверка: put пишет<uuid>_<name>, poll возвращаетNoneдо появления ответа и байты после
Task 8: [4.2] src/gateway/smb.py
src/gateway/smb.py:SmbHotFolderповерхsmbclient(UNC\\host\path\Input), те же методы; unit-тест с подменённымsmbclient(вызовыopen_file,listdir); проверка: тест зелёный, в режимеlocalмодуль не импортируется
Task 9: [4.3] src/scan_emulator/__init__.py + __main__.py
src/scan_emulator/__init__.py+__main__.py: функцияprocess_once(root, fixtures, delay)и цикл; поиск фикстуры по имени/префиксу, подстановкаfile_uid, ошибкаINVALID_DOCUMENT, переименование в.done;tests/unit/test_emulator.py— сценарии spec «Точное имя», «Префикс», «Нет пары» наtmp_pathс копиями../fixtures/ocr/*.json(маркерfixtures, skip без фикстур); проверка: зелёный
Task 10: [5.1] src/normalizer/__init__.py
src/normalizer/__init__.py: протоколNormalizer,NormalizeOutcome,NullNormalizer(ошибка ИС → код из ответа; успех →NOT_IMPLEMENTED; не JSON →INVALID_FORMAT); unit-тесты наaep_5.json,aep_8_form4.json, синтетическом ответе сerror; проверка: зелёный
Task 11: [5.2] src/core/clients.py (get_bytes, put_json, put_bytes), src/core/nats.py
src/core/clients.py(get_bytes,put_json,put_bytes),src/core/nats.py(подключение с/без nkeys, автопереподключение,subscribe_requests(queue="scan-gateway", cb),publishсNats-Msg-Idи повторами); проверка: unit с фейками, интеграционный тест подключения к compose-NATS без JetStream
Task 12: [5.3] src/core/worker.py
src/core/worker.py:on_request(msg): валидация → LRUjob_id→ ackprocessing→ очередь; семафорSCAN_MAX_INFLIGHT;handle(job): S3 get →hotfolder.put→ опрос до дедлайна →raw/{job_id}.json→Normalizer→ результат/конверт → publish; таблица ошибок D-7;/healthcheck.nats,.hotfolder; проверка: unit-тестыhandleс фейками иLocalHotFolder+process_onceэмулятора — сценарии spec (ack раньше файла, повторный запрос → один файл, успех → NOT_IMPLEMENTED сraw_s3_key, INVALID_DOCUMENT, TIMEOUT с укороченными таймаутами, RECOGNITION_FAILED при отсутствии каталога, SCHEMA_MISMATCH без ack, FILE_READ_ERROR)
Task 13: [5.4] Тест параллелизма
- Тест параллелизма: пять заданий при
SCAN_MAX_INFLIGHT = 4— пять ack сразу, четыре файла во входящем каталоге, пятый после освобождения слота (фейковые S3/NATS,LocalHotFolder); проверка: зелёный
Task 14: [6.1] tests/integration/test_roundtrip.py (маркер integration)
tests/integration/test_roundtrip.py(маркерintegration): compose сscan-emulator— положитьaep_5.pdfв MinIOuploads/…, опубликоватьrequest-image.json, собратьrecognition.result:processing, затемNOT_IMPLEMENTEDсraw_s3_key; проверить объектraw/<job_id>.jsonв MinIO; второй запрос сunknown.pdf→INVALID_DOCUMENT; проверка: зелёный
Task 15: [6.2] make check-full
make check-full:make check+pytest -m integration+docker build; проверка: зелёный против compose docs на веткеfeat/1-slice-0-skeleton
Task 16: [6.3] Прогон DoD
- Прогон DoD:
make up→make check-full→ ручная проверка изdocs/dev/README.md;openspec validate slice-0-skeleton --strict; выводmake check-full— в описание PR