Срез 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.4 KiB
Spec Delta: tabular-recognition-service
Purpose
Stateless-сервис разбора табличных файлов (XLS/XLSX/XLSB): принимает задания из NATS, читает исходник из 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 durable pull-consumer'ом tabular-parser; для каждого запроса,
проходящего recognition-request.schema.json, SHALL прочитать объект s3.key из бакета s3.bucket, вызвать парсер,
записать файл результата (по recognition-result.schema.json) в results/{file_id}/{job_id}/result.json, затем
опубликовать конверт в recognition.result (по recognition-result-message.schema.json) с заголовком
Nats-Msg-Id = result:<job_id>:<attempt> и service.name = "tabular-parser", и только после публикации подтвердить запрос.
Scenario: Запрос с заглушкой парсера (Срез 0)
- WHEN опубликован запрос из
docs/contracts/examples/request-tabular.jsonс существующим объектом вuploads/ - THEN в
recognition.resultпоявляется конвертstatus = error,error.code = NOT_IMPLEMENTED,job_id/file_idиз запроса,metrics.duration_ms ≥ 0; файл результата может отсутствовать; запрос подтверждён
Scenario: Ack только после результата
- WHEN публикация конверта завершается ошибкой
- THEN запрос не подтверждается (nak с задержкой), следующая доставка повторяет обработку; дубликат конверта
отбрасывается по
Nats-Msg-Id
Requirement: Ошибки транспорта превращаются в результат
Сервис SHALL отвечать конвертом status = error и подтверждать запрос в случаях: тело не проходит схему или MAJOR
версии ≠ 1 → SCHEMA_MISMATCH; объект 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, запрос подтверждён
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, проходящий схему.
Scenario: CI сервиса
- WHEN выполняются тесты контракта при доступном
../docs(илиARM_KA_DOCS) - THEN идентичность подтверждена, примеры валидны; при недоступном docs тест идентичности пропущен с предупреждением
Requirement: Соединение с NATS по правилам шаблона
При пустых NATS_SEED/NATS_JWT сервис SHALL подключаться без учётных данных, иначе — с nkeys/JWT. Отсутствие stream
RECOGNITION SHALL приводить к повторным попыткам подписки каждые 10 с и nats = down в /healthcheck, а не к падению.
Scenario: Stream появился позже
- WHEN сервис стартовал раньше ядра, создавшего stream
- THEN в течение 20 с после создания stream воркер подписан,
nats = ok