Andrey Epifancev 0c04127aec docs: OpenSpec change slice-0-skeleton — сквозной скелет Среза 0
openspec init (config с контекстом docs-репозитория) и change slice-0-skeleton: proposal, design
(11 решений без обсуждения, открытые вопросы к человеку), спеки recognition-contract,
dev-environment, ci-template, tasks в порядке зависимостей между репозиториями. 06-test-data.md §6:
справочники продукта — seed arm-core, не фикстуры. 09-delivery-plan.md: Срез 0 по факту (change'и,
compose в docs/dev, контракт копией без общего пакета, seed).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-03 02:44:14 +04:00

6.3 KiB
Raw Blame History

Spec Delta: recognition-contract

Purpose

Контракт обмена между ядром arm-core и stateless-сервисами распознавания (tabular-parser, scan-gateway): тела сообщений NATS recognition.{tabular|image}.request и recognition.result, файл результата в S3 и правила версионирования и идемпотентности. Сервисы не знают БД; ядро — единственный писатель (DECISION-4).

ADDED Requirements

Requirement: Сообщение запроса распознавания

Тело сообщения на subject recognition.tabular.request или recognition.image.request SHALL соответствовать docs/contracts/recognition-request.schema.json v1.0: обязательные поля schema_version ("1.0"), job_id (uuid = recognition_job.id), file_id (uuid = stored_file.id), pipeline (tabular | image), file_type (XLS | XLSX | XLSB | PDF | PNG | JPG), original_name, s3 {bucket, key} исходника в роли uploads, reply_deadline (date-time, UTC), attempt (≥ 1). Subject SHALL соответствовать pipeline.

Scenario: Публикация запроса ядром

  • WHEN ядро переводит recognition_job из queued в published
  • THEN в subject, соответствующий recognition_job.pipeline, публикуется сообщение, проходящее схему запроса, с заголовком Nats-Msg-Id = <job_id>:<attempt>

Scenario: Сообщение не по схеме

  • WHEN сервис получает запрос, не проходящий схему (нет s3.key, неизвестный file_type)
  • THEN сервис публикует результат со status = error, error.code = SCHEMA_MISMATCH и не падает

Requirement: Сообщение результата распознавания

Тело сообщения на subject recognition.result SHALL соответствовать docs/contracts/recognition-result-message.schema.json v1.0: обязательные поля schema_version ("1.0"), job_id, file_id, status (done | needs_check | error), service {name, version}; при status ≠ error обязательны result_s3 {bucket, key} и result_schema_version; при status = error обязателен error {code, message}; опциональны raw_s3_key (только scan-gateway) и metrics. Поле group_schema в конверт NOT включается — источник recognition_job.group_schema — файл результата.

Scenario: Успешный результат

  • WHEN сервис завершил обработку файла
  • THEN файл результата записан в S3 по ключу results/{file_id}/{job_id}/result.json, а затем опубликован конверт со status = done | needs_check, result_s3 указывает на этот файл, заголовок Nats-Msg-Id = result:<job_id>:<attempt>

Scenario: Ошибка обработки

  • WHEN сервис не смог обработать файл (нечитаем, таймаут внешней системы, тип не поддерживается)
  • THEN публикуется конверт со status = error и error.code из перечня схемы (UNSUPPORTED_FORMAT, UNREADABLE, TIMEOUT, SCHEMA_MISMATCH, NOT_IMPLEMENTED, FILE_READ_ERROR, INVALID_FORMAT, INVALID_DOCUMENT, RECOGNITION_FAILED, UNKNOWN_ERROR); result_s3 может отсутствовать

Requirement: Файл результата

Файл результата SHALL соответствовать docs/contracts/recognition-result.schema.json v1.0: один файл (stored_file) → один job_id → массив documents из osvDocument и/или statementDocument; денежные значения — строки ^-?\d+(\.\d{1,2})?$; confidence — 0–100. Пустой массив documents допустим только при status = error.

Scenario: Результат валидируется примерами

  • WHEN CI сервиса запускает тесты контракта
  • THEN все файлы docs/contracts/examples/result-*.json проходят схему файла результата, а все request-*.json / result-message-*.json — схемы сообщений

Requirement: Версионирование контракта

Поле schema_version SHALL быть строкой MAJOR.MINOR. Добавление необязательных полей или значений enum — MINOR (1.1, обратно совместимо); удаление/переименование полей, смена типов или семантики — MAJOR (2.0). Сервис SHALL отвергать запрос с неизвестным MAJOR (error.code = SCHEMA_MISMATCH); ядро SHALL принимать результат с тем же MAJOR и любым известным или новым MINOR.

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

  • WHEN сервис, поддерживающий 1.x, получает запрос schema_version = "2.0"
  • THEN публикуется результат status = error, error.code = SCHEMA_MISMATCH, запрос подтверждается (ack), чтобы не зациклить доставку

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

Каждый сервис, читающий или пишущий контракт, SHALL хранить копию файлов схем в api/contracts/ и проверять в CI их байтовую идентичность файлам docs/contracts/ (checkout arm-ka-docs или путь ARM_KA_DOCS).

Scenario: Копия устарела

  • WHEN в docs/contracts/ изменена схема, а копия в сервисе — нет
  • THEN тест идентичности в CI сервиса падает с указанием имени файла