Срез 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>
6.9 KiB
6.9 KiB
Tasks: Срез 0 — скелет scan-gateway
Репозиторий — scan-gateway; команды — из api/. Зависимости: 3.1 ждёт контракт v1.0 (docs 1.1–1.4); интеграционные
тесты 6.x — compose (docs 2.1, 5.3). Каждая задача ≤ половины дня.
1. Вырезать фронт и БД из шаблона (SMB остаётся)
- 1.1 Удалить
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 - 1.2
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,NATS_MAX_DELIVER=1,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,python run_server.pyс.env.example,/healthcheck200 - 1.3
README.md— описание сервиса (назначение, протокол горячих папок, env, запуск против compose и эмулятора),CLAUDE.md— актуализировать «Соглашения шаблона» по факту; проверка: упомянутые файлы существуют
2. Инструменты
- 2.1
api/pyproject.toml(ruff, mypy),requirements-dev.txt=-r requirements.txt+ ruff, mypy, pytest, pytest-asyncio, pytest-env, httpx, jsonschema; проверка:ruff check . && ruff format --check . && mypy srcзелёные - 2.2
Dockerfile(python:3.12-slim;CMD python run_server.py, эмулятор — черезcommandв compose),Makefile(up,test,test-integration,lint,emulator),.env.example,.env.test; проверка:docker buildуспешен
3. Контракт
- 3.1
api/contracts/— копии схем иexamples/из../docs/contracts;src/contract/— pydantic-модели;tests/unit/test_contract.py— идентичность (skip без docs), примеры, round-trip; проверка: тесты зелёные
4. Горячие папки и эмулятор
- 4.1
src/gateway/hotfolder.py: протоколHotFolder,LocalHotFolder(put/poll/healthy, созданиеOutput/<uuid>/не требуется — читаем, если есть);tests/unit/test_local_hotfolder.pyнаtmp_path; проверка: put пишет<uuid>_<name>, poll возвращаетNoneдо появления ответа и байты после - 4.2
src/gateway/smb.py:SmbHotFolderповерхsmbclient(UNC\\host\path\Input), те же методы; unit-тест с подменённымsmbclient(вызовыopen_file,listdir); проверка: тест зелёный, в режимеlocalмодуль не импортируется - 4.3
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 без фикстур); проверка: зелёный
5. Воркер и нормализатор-заглушка
- 5.1
src/normalizer/__init__.py: протоколNormalizer,NormalizeOutcome,NullNormalizer(ошибка ИС → код из ответа; успех →NOT_IMPLEMENTED; не JSON →INVALID_FORMAT); unit-тесты наaep_5.json,aep_8_form4.json, синтетическом ответе сerror; проверка: зелёный - 5.2
src/core/clients.py(get_bytes,put_json,put_bytes),src/core/nats.py(подключение с/без nkeys, подписка с повтором, publish сNats-Msg-Id,ack_wait = SCAN_TIMEOUT_SECONDS + 120,max_deliver = 1); проверка: unit с фейками, интеграционный тест подключения к compose-NATS - 5.3
src/core/worker.py: семафорSCAN_MAX_INFLIGHT,fetchна свободные слоты,handle(msg): валидация → S3 get →hotfolder.put→ опрос до дедлайна →raw/{job_id}.json→Normalizer→ результат/конверт → publish → ack; таблица ошибок D-7;/healthcheck.nats,.hotfolder; проверка: unit-тестыhandleс фейками иLocalHotFolder+process_onceэмулятора — сценарии spec (успех → NOT_IMPLEMENTED сraw_s3_key, INVALID_DOCUMENT, TIMEOUT с укороченными таймаутами, RECOGNITION_FAILED при отсутствии каталога, SCHEMA_MISMATCH, FILE_READ_ERROR) - 5.4 Тест параллелизма: пять заданий при
SCAN_MAX_INFLIGHT = 4— четыре файла во входящем каталоге сразу, пятый после освобождения слота (фейковые S3/NATS,LocalHotFolder); проверка: зелёный
6. Интеграция, CI, завершение
- 6.1
tests/integration/test_roundtrip.py(маркерintegration): compose сscan-emulator— положитьaep_5.pdfв MinIOuploads/…, опубликоватьrequest-image.json, дождаться конвертаNOT_IMPLEMENTEDсraw_s3_key, проверить объектraw/<job_id>.jsonв MinIO и ack запроса; второй запрос сunknown.pdf→INVALID_DOCUMENT; проверка: зелёный - 6.2
.gitea/workflows/ci.ymlиз../docs/dev/ci/python-service.yml(блокenv: scan-gateway, порт 8002); проверка: зелёный run на веткеfeat/1-slice-0-skeleton - 6.3 Прогон DoD:
make up→make test→make test-integration→ ручная проверка изdocs/dev/README.md;openspec validate slice-0-skeleton --strict; результаты — в описание PR