proposal, design (D-1…D-13), спеки primary-files, osv-result-mapping, documents-api (новые), recognition-job-exchange, data-model (MODIFIED), audit-trail (ADDED), tasks (13 задач). Issue: RSHB/arm-core#14 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
4.8 KiB
Spec Delta: recognition-job-exchange
MODIFIED Requirements
Requirement: Ack сервиса переводит задание в processing
Получив в recognition.result конверт со status = processing для задания в published, ядро SHALL установить
status = processing, started_at, service_name, service_version и перевести файл задания в
stored_file.status = PROCESSING, если current_recognition_job_id файла равен этому заданию. Повторный ack для
задания в processing SHALL игнорироваться; ack для задания в другом статусе SHALL логироваться и не менять задание.
Scenario: Сервис подтвердил
- WHEN сервис опубликовал конверт
processingсjob_idопубликованного задания - THEN задание имеет
status = processing,started_atзаполнен,service_name = tabular-parser, файл —PROCESSING
Requirement: Inbox принимает результат идемпотентно
Для конверта со status ∈ {done, needs_check, error} ядро SHALL валидировать его по
recognition-result-message.schema.json, найти задание по job_id и: если result_msg_id задания уже равен
Nats-Msg-Id сообщения — ничего не менять; иначе зарегистрировать файл результата в s3_object (bucket_role = results,
created_by = <service.name>; только при наличии result_s3), обновить задание (status, service_name,
service_version, finished_at, metrics, error_code, error_message, result_schema_version, result_s3_object_id,
result_msg_id) и зафиксировать транзакцию. При status = error файл задания (если current_recognition_job_id равен
заданию) SHALL получить stored_file.status = ERROR; при done/needs_check файл остаётся PROCESSING и SHALL быть
обработан циклом маппинга (спека osv-result-mapping); inbox MUST NOT читать файл результата. Неизвестный job_id SHALL
быть залогирован. Результат для задания, уже находящегося в финальном статусе (done, needs_check, error,
timeout, dlq), SHALL логироваться и не применяться.
Scenario: Успешный результат
- WHEN сервис публикует конверт
status = doneсresult_s3 = {arm-ka, results/<file_id>/<job_id>/result.json}иNats-Msg-Id = result:<job_id>:1 - THEN задание получает
status = done,result_msg_id = result:<job_id>:1,result_s3_object_idуказывает на новую строкуs3_objectс этим ключом,finished_atзаполнен; файл остаётсяPROCESSINGдо маппинга
Scenario: Повторная доставка
- WHEN то же сообщение получено второй раз
- THEN строка
recognition_jobиs3_objectне меняются
Scenario: Ошибка сервиса
- WHEN конверт содержит
status = error,error = {UNSUPPORTED_FORMAT, …}безresult_s3 - THEN задание получает
status = error,error_code = UNSUPPORTED_FORMAT,error_messageиз конверта,result_s3_object_idпуст; файл —ERROR
Scenario: Результат пришёл после таймаута
- WHEN задание уже
timeout, и приходит конвертdone - THEN задание не меняется, событие залогировано
Requirement: Таймаут ожидания результата
Фоновый вахтёр SHALL с периодом 30 с переводить задания processing и published с reply_deadline < now() в
status = timeout, error_code = TIMEOUT, finished_at = now(), а их файлы (по current_recognition_job_id) — в
stored_file.status = ERROR. Публикатор при переводе задания в dlq SHALL так же переводить файл в ERROR. Replay
задания (новая попытка, attempt + 1) — вне этого среза.
Scenario: Сервис взял, но не ответил
- WHEN задание в
processingиreply_deadlineпрошёл без результата - THEN не позднее чем через 30 с задание имеет
status = timeout, файл —ERRORсerror_code = TIMEOUT