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

73 lines
6.4 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.

# 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`