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

68 lines
6.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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