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

6.9 KiB
Raw Blame History

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, /healthcheck 200
  • 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 в MinIO uploads/…, опубликовать 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