Andrey Epifancev 7a35b79d5e docs: change slice-0-skeleton под факты платформы — NATS без JetStream, Python 3.11, без CI и Nexus
Обмен с ядром на core NATS: подписка queue group, ack status=processing сразу по получении, process-local
LRU job_id против повторных запросов, публикация результата с повторами; удалены durable consumer / max_deliver /
ack_wait. Dockerfile python:3.11-slim; make check / make check-full вместо CI; raisa_* фильтруются при
dev-установке. Обновлены proposal, design (решения без обсуждения), specs, tasks, CLAUDE.md, openspec/config.yaml.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-03 16:21:45 +04:00

7.9 KiB
Raw Blame History

Spec Delta: tabular-recognition-service

Purpose

Stateless-сервис разбора табличных файлов (XLS/XLSX/XLSB): принимает задания из NATS без JetStream, подтверждает их, читает исходник из S3, отдаёт результат в S3 и конверт в NATS по контракту v1.0, не имея доступа к БД и фронта. В Срезе 0 разбор заменён заглушкой.

ADDED Requirements

Requirement: Сервис не зависит от БД и фронта

Репозиторий MUST NOT содержать каталоги web/, alembic/, src/db/, src/models/, src/repositories/; зависимости SQLAlchemy, asyncpg, alembic, smbprotocol, raisa_fastapi_protected_api MUST NOT присутствовать в requirements.txt; настройки OPENBAO.DATABASE и OPENBAO.SMB MUST NOT требоваться для старта.

Scenario: Старт с минимальным окружением

  • WHEN сервис запускается с .env, содержащим только OPENBAO__MINIO__*, OPENBAO__NATS__NATS_URL, DEBUG=true
  • THEN GET /healthcheck отвечает 200 с полями uptime, version, nats

Requirement: Подтверждение запроса до обработки

Сервис SHALL быть подписан на recognition.tabular.request в queue group tabular-parser. Получив запрос, проходящий recognition-request.schema.json, сервис SHALL до чтения файла из S3 опубликовать в recognition.result конверт status = processing с job_id, file_id, service {name: "tabular-parser", version} и заголовком Nats-Msg-Id = ack:<job_id>:<attempt>. Повторный запрос с job_id, который уже обрабатывается или обработан этим процессом, SHALL получить повторный ack и MUST NOT обрабатываться второй раз.

Scenario: Ack раньше результата

  • WHEN опубликован запрос из docs/contracts/examples/request-tabular.json
  • THEN первым сообщением сервиса в recognition.result является конверт processing, и только затем — результат

Scenario: Повторный запрос во время обработки

  • WHEN тот же запрос доставлен второй раз, пока первый обрабатывается
  • THEN публикуется второй ack processing, файл из S3 повторно не читается, результат публикуется один раз

Requirement: Результат по контракту

После подтверждения сервис SHALL прочитать объект s3.key из бакета s3.bucket, вызвать парсер, записать файл результата (по recognition-result.schema.json) в results/{file_id}/{job_id}/result.json и опубликовать конверт результата (по recognition-result-message.schema.json) с заголовком Nats-Msg-Id = result:<job_id>:<attempt>. При неудаче публикации сервис SHALL повторить её до трёх раз с паузой, затем залогировать ошибку.

Scenario: Запрос с заглушкой парсера (Срез 0)

  • WHEN запрос подтверждён и объект существует в uploads/
  • THEN в recognition.result появляется конверт status = error, error.code = NOT_IMPLEMENTED, job_id/file_id из запроса, metrics.duration_ms ≥ 0; файл результата может отсутствовать

Scenario: Повтор публикации

  • WHEN первая публикация конверта результата завершается ошибкой соединения, вторая — успешно
  • THEN конверт опубликован один раз с тем же Nats-Msg-Id, обработка не повторяется

Requirement: Ошибки транспорта превращаются в результат

Сервис SHALL отвечать конвертом status = error в случаях: тело не проходит схему или MAJOR версии ≠ 1 → SCHEMA_MISMATCH (если job_id читаем); объект S3 не читается → FILE_READ_ERROR; исключение парсера → UNKNOWN_ERROR (сообщение без трассировки). Сервис MUST NOT падать и MUST NOT молчать по этим причинам.

Scenario: Нет объекта в S3

  • WHEN s3.key запроса указывает на несуществующий объект
  • THEN публикуется конверт error.code = FILE_READ_ERROR, процесс продолжает работу

Scenario: Несовместимая версия

  • WHEN запрос содержит schema_version = "2.0"
  • THEN публикуется конверт error.code = SCHEMA_MISMATCH, ack не публикуется

Requirement: Парсер — заменяемый интерфейс

Разбор файла SHALL выполняться через единый интерфейс «байты + тип файла → документы/метрики/ошибка», не знающий о NATS и S3; воркер MUST NOT зависеть от конкретной реализации. В Срезе 0 реализация SHALL возвращать NOT_IMPLEMENTED для всех типов; каталог api/rules/ SHALL существовать с README о назначении словарей.

Scenario: Подмена реализации в тесте

  • WHEN в тесте воркеру передан парсер, возвращающий один osvDocument
  • THEN файл результата с этим документом записан в S3, конверт имеет status = done и result_s3 на этот файл

Requirement: Копия контракта идентична источнику

api/contracts/*.schema.json SHALL быть байт в байт равны файлам ../docs/contracts/; pydantic-модели SHALL валидировать все ../docs/contracts/examples/*.json и порождать JSON, проходящий схему. Проверка входит в make check.

Scenario: make check

  • WHEN выполняются тесты контракта при доступном ../docs (или ARM_KA_DOCS)
  • THEN идентичность подтверждена, примеры валидны; при недоступном docs тест идентичности пропущен с предупреждением

Requirement: Соединение с NATS по правилам шаблона, без JetStream

При пустых NATS_SEED/NATS_JWT сервис SHALL подключаться без учётных данных, иначе — с nkeys/JWT. Сервис MUST NOT требовать stream'ов или consumer'ов JetStream; при разрыве соединения SHALL переподключаться автоматически и восстанавливать подписку; /healthcheck SHALL отражать состояние (nats = ok | down).

Scenario: NATS перезапущен

  • WHEN контейнер NATS перезапущен во время работы сервиса
  • THEN в течение 20 с после старта NATS подписка восстановлена, nats = ok, новый запрос обрабатывается