# Spec Delta: tabular-recognition-service ## MODIFIED Requirements ### 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::`. При неудаче публикации сервис SHALL повторить её до трёх раз с паузой, затем залогировать ошибку. #### Scenario: Запрос с заглушкой парсера (Срез 0) - **WHEN** воркеру явно передана заглушка `NullParser` (только тесты воркера; реализация по умолчанию — реальный парсер), запрос подтверждён и объект существует в `uploads/` - **THEN** в `recognition.result` появляется конверт `status = error`, `error.code = NOT_IMPLEMENTED`, `job_id`/`file_id` из запроса, `metrics.duration_ms ≥ 0`; файл результата может отсутствовать #### Scenario: Запрос с реальным парсером - **WHEN** запрос подтверждён и объект в `uploads/` существует, но не является книгой Excel (сигнатура не XLS/XLSX/XLSB) - **THEN** в `recognition.result` появляется конверт `status = error`, `error.code = UNSUPPORTED_FORMAT` (повреждённая книга — `UNREADABLE`), `job_id`/`file_id` из запроса, `metrics.duration_ms ≥ 0`; файл результата может отсутствовать; корректная ОСВ в том же потоке даёт `done` (см. «Парсер — заменяемый интерфейс») #### Scenario: Повтор публикации - **WHEN** первая публикация конверта результата завершается ошибкой соединения, вторая — успешно - **THEN** конверт опубликован один раз с тем же `Nats-Msg-Id`, обработка не повторяется ### Requirement: Парсер — заменяемый интерфейс Разбор файла SHALL выполняться через единый интерфейс «байты + тип файла → документы/метрики/ошибка», не знающий о NATS и S3; воркер MUST NOT зависеть от конкретной реализации. Реализация по умолчанию (`get_parser()`) SHALL разбирать ОСВ по спеке `osv-parsing` и писать файл результата с `schema_version = "1.1"`; листы с признаками ББ/ОФР SHALL пропускаться с предупреждением до подсреза 2b. Заглушка `NullParser` (`error NOT_IMPLEMENTED`) SHALL использоваться только в тестах воркера и MUST NOT быть реализацией по умолчанию. Реализация по умолчанию SHALL создаваться один раз при старте приложения (словари читаются и проверяются там же). Каталог `api/rules/` SHALL содержать словари ОСВ (`osv_headers`, `osv_groups`, `periods`, `data_types`) и README. #### Scenario: Подмена реализации в тесте - **WHEN** в тесте воркеру передан парсер, возвращающий один `osvDocument` - **THEN** файл результата с этим документом записан в S3, конверт имеет `status = done` и `result_s3` на этот файл #### Scenario: Реальная ОСВ через воркер - **WHEN** в интеграционном тесте в S3 лежит `rb/2024-FY/ОСВ_66 за 2024.xls` и опубликован запрос `file_type = XLS` - **THEN** после ack публикуется конверт `done` с `result_schema_version = "1.1"`, файл результата проходит схему v1.1 и содержит один `osvDocument` с `account_code_raw = "66"`