Andrey Epifancev 1661ec2fca 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:38 +04:00

6.4 KiB
Raw Blame History

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