Спеки применены: 5 новых capability (arm-core, arm-web, scan-gateway, statement-normalization, tabular-recognition-service) с Purpose, изменения quality-checks и recognition-contract. Ссылки в 05-domain-rules, DBML v5.19, entities, dev/checks.md и dev/qa-statements-v2.md ведут в архив. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
35 KiB
recognition-contract Specification
Purpose
Контракт обмена между ядром arm-core и stateless-сервисами распознавания (tabular-parser, scan-gateway):
тела сообщений NATS на subject'ах запроса и ответа (настройки окружения OPENBAO__NATS__NATS_*_SUBJECT, по умолчанию
files.tabular_uploaded, files.img_uploaded и files.tabular_parsed), файл результата в S3 и правила версионирования и
идемпотентности. Сервисы не знают БД; ядро — единственный писатель (DECISION-4).
Requirements
Requirement: Сообщение запроса распознавания
Тело сообщения на subject запроса tabular или image (настройки окружения NATS_TABULAR_REQUEST_SUBJECT /
NATS_IMAGE_REQUEST_SUBJECT, по умолчанию files.tabular_uploaded / files.img_uploaded) 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), но из тела извлекаются корректныеjob_idиfile_id - THEN сервис публикует результат со
status = error,error.code = SCHEMA_MISMATCHбез предшествующего ackprocessingи не падает;attemptдляNats-Msg-Idрезультата берётся из тела запроса, при его отсутствии реализации расходятся (tabular-parserберётattemptиз заголовкаNats-Msg-Idзапроса или не отвечает,scan-gatewayподставляет 1) — см. OQ-71
Scenario: Нечитаемый запрос
- WHEN сервис получает сообщение, из которого нельзя извлечь
job_id(не JSON, не объект,job_idотсутствует или не uuid), а значит нельзя собрать валидный конверт - THEN сервис ответа не публикует: запрос записывается в лог и отбрасывается; ядро не получит ack, повторит
публикацию
OUTBOX_MAX_REPUBLISHраз и переведёт задание вdlq(error_code = NO_ACK)
Scenario: Невалидный повтор запроса, уже находящегося в работе
- WHEN сервис получает запрос, не проходящий схему, но с
job_id, который уже в работе или обработан этим процессом - THEN конверт
SCHEMA_MISMATCHне публикуется (егоNats-Msg-Idresult:<job_id>:<attempt>совпал бы с настоящим результатом, и ядро отбросило бы настоящий как дубликат); запрос записывается в лог и отбрасывается
Scenario: Примеры контракта — иллюстрации
- WHEN запрос берётся из
docs/contracts/examples/request-*.json(вrequest-image.jsonreply_deadlineлежит в прошлом) - THEN он проходит схему: схема проверяет для
reply_deadlineлишь форматdate-time, но не сравнивает срок с текущим временем; пример — иллюстрация формы сообщения, а не готовое к исполнению задание; проверка срока — дело сервиса (scan-gatewayотвечаетerror TIMEOUT, еслиreply_deadlineистёк до отправки файла в ИС Сканирование)
Requirement: Подтверждение взятия в работу (ack без JetStream)
Получив корректный запрос (прошедший проверку схемы и MAJOR), сервис SHALL немедленно, до чтения файла из S3, опубликовать в files.tabular_parsed конверт со
status = processing, job_id, file_id, service {name, version} и заголовком Nats-Msg-Id = ack:<job_id>:<attempt>.
Ядро SHALL перевести задание published → processing и записать started_at, service_name, service_version. Если
ack не получен за ACK_TIMEOUT_SECONDS (по умолчанию 30), ядро SHALL опубликовать запрос повторно с тем же
Nats-Msg-Id и тем же attempt, не более OUTBOX_MAX_REPUBLISH (по умолчанию 3) раз, после чего перевести задание в dlq.
Scenario: Сервис жив
- WHEN запрос опубликован и сервис подписан
- THEN в течение
ACK_TIMEOUT_SECONDSвfiles.tabular_parsedпоявляется конвертprocessing, задание получаетstatus = processingиstarted_at
Scenario: Сервис недоступен
- WHEN ни один инстанс сервиса не подписан на subject запроса
- THEN ядро публикует запрос повторно каждые
ACK_TIMEOUT_SECONDS; послеOUTBOX_MAX_REPUBLISHповторов задание получаетstatus = dlq,request_msg_idне меняется
Scenario: Повторный запрос пришёл в работающий сервис
- WHEN сервис получает запрос с
job_id, который уже обрабатывается или обработан этим процессом - THEN сервис публикует ack повторно и не начинает вторую обработку
Requirement: Сообщение результата распознавания
Тело сообщения на subject ответа (настройка окружения NATS_RESULT_SUBJECT, по умолчанию files.tabular_parsed)
SHALL соответствовать docs/contracts/recognition-result-message.schema.json
v2.0: обязательные поля schema_version ("2.0"), job_id, file_id, status (processing | done | needs_check |
error), service {name, version}; при status ∈ {done, needs_check} обязательны result_s3 {bucket, key} и
result_schema_version; при status = error обязателен error {code, message}; опциональны raw_s3_key
(только scan-gateway) и metrics. metrics.warnings[] — структуры warning (как в файле результата), не строки.
Поле group_schema в конверт NOT включается — источник recognition_job.group_schema — файл результата.
Производитель v2.0 SHALL писать в конверт тот же MAJOR, что и в файл результата.
Scenario: Успешный результат
- WHEN сервис завершил обработку файла
- THEN файл результата записан в S3 по ключу
results/{file_id}/{job_id}/result.json, а затем опубликован конверт соstatus = done | needs_check,result_s3указывает на этот файл,result_schema_version = "2.0", заголовок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может отсутствовать
Scenario: Предупреждения в конверте
- WHEN у файла есть предупреждение уровня файла (ошибочный json набора ИС)
- THEN
metrics.warningsконверта содержит объект{code: "JSON_FAILED", severity: "error", message, locator}
Requirement: Файл результата
Файл результата SHALL соответствовать docs/contracts/recognition-result.schema.json v2.0: один файл (stored_file)
→ один job_id → массив documents из osvDocument и/или statementDocument; денежные значения — строки
^-?\d+(\.\d{1,2})?$; confidence — 0–100. Пустой массив documents допустим только при status = error.
Предупреждение уровня файла пишется там, где документа нет (потерянный json, таблица ИС, исключение разбора таблицы,
лист «прочее»).
service.statements_version — коммит копии общего кода arm_statements, которым посчитан результат (обязателен при
наличии statementDocument). metrics.warnings[] — предупреждения уровня файла (структура warning с locator).
Каждый документ (и osvDocument, и statementDocument) SHALL иметь status (done | needs_check) и warnings[] —
структуры {code, severity, message, line_refs?, column_index?, field?} (line_refs — только ref строк из lines[]);
у osvDocument все предупреждения — info, status = done, когда контрольные суммы §1.4 листа сошлись, иначе
needs_check. Остальное в osvDocument — как в v1.2: у строки rows[] необязательное поле extra — объект «заголовок
колонки → строка или null» для колонок внутри таблицы, не сопоставленных с шестью суммами и ИНН (§1.2 «не терять»); у
шапки — period_start, period_end, is_from_year_start.
statementDocument SHALL иметь form_variant (full | simplified | null); header — поля organization_name,
inn, report_date, units, okud, okpo, okopf, okfs, okei, address и для каждого <поле>_text и
<поле>_confidence, плюс needs_review_fields[]; columns[] — column_index (номер столбца в сетке кандидата: у OCR
— column_index ИС, у Excel — номер столбца листа с 0), label, label_confidence,
period_date, period_kind (absolute | relative:current | relative:prev_year_end |
relative:prev_prev_year_end | relative:prev_period | none); lines[] — row_index, ref (у Excel <лист>!<A1>,
имя листа в кавычках по правилу Excel), line_status (ok |
unreadable | missing | detail), line_code (строка 4–5 цифр или null; null ⇔ статус не ok),
line_code_text, line_code_confidence, line_name (строка или null), line_name_confidence, parent_ref (у
detail), needs_review, values[] — column_index, amount, string_value, text, confidence,
is_recognized, needs_review. checks SHALL присутствовать у BALANCE / INCOME со строками и отсутствовать у
документа без строк; relations_failed и soft_relations_failed — {column_index, relation, codes[], expected, actual}; duplicate_line_codes удалён. document_type — BALANCE | INCOME | DISCLOSURE (OTHER в v2.0 не
порождается: лист «прочее» — предупреждение файла SHEET_OTHER, документа нет; значение остаётся только в схеме 1.x и в
document_type ядра для документов прежних результатов); form_variant обязателен (null у DISCLOSURE); у документа
без строк columns и lines — пустые массивы. ref уникален в документе.
Scenario: Результат валидируется примерами
- WHEN CI сервиса запускает тесты контракта
- THEN все файлы
docs/contracts/examples/result-*.jsonпроходят схему файла результата v2.0, а всеrequest-*.json/result-message-*.json— схемы сообщений
Scenario: Лишняя колонка сохранена
- WHEN в ОСВ между ИНН и суммами есть колонка «КПП»
- THEN у строк контрагентов
extra = {"КПП": "<значение>"}, файл проходит схему v2.0
Scenario: Форма из Excel с проверками
- WHEN tabular-parser разобрал книгу с балансом на двух листах, ОФР со строкой без кода под 2110 и пояснениями
(
examples/result-statement-excel.json) - THEN файл проходит схему v2.0:
BALANCEсlocatorиз двух листов,status,checksи мягким нарушением;INCOMEсо строкойdetail(line_code = null,parent_ref);DISCLOSUREс пустымиcolumns/linesи безchecks
Scenario: Форма из OCR с сомнительными значениями
- WHEN scan-gateway разобрал ББ, где одна сумма собрана из грязного текста и покрыта соотношением, а одна строка
unreadable(examples/result-statement-minimal.json) - THEN файл проходит схему v2.0; у покрытого значения
is_recognized = false,needs_review = false,text— сырой; у строкиunreadableline_code = null,line_code_text— сырой текст; документneeds_check
Scenario: Границы периода ОСВ
- WHEN в шапке ОСВ период «Октябрь 2024 г. – Сентябрь 2025 г.»
- THEN
period_start = "2024-10-01",period_end = "2025-09-30",is_from_year_start = false; документ ОСВ сstatusиwarnings, файл проходит схему v2.0
Scenario: Документ OTHER в v2.0
- WHEN в результате v2.0
statementDocumentсdocument_type = OTHERили безform_variant - THEN файл не проходит схему v2.0
Scenario: Строка без кода с кодом в поле
- WHEN у строки
line_status = unreadableпередан непустойline_code - THEN файл не проходит схему v2.0
Requirement: Версионирование контракта
Поле schema_version SHALL быть строкой MAJOR.MINOR. Добавление необязательных полей или значений enum —
MINOR (обратно совместимо); удаление/переименование полей, смена типов или семантики — MAJOR.
Сервис SHALL отвергать запрос с неизвестным MAJOR (error.code = SCHEMA_MISMATCH); ядро SHALL принимать результат
с известным MAJOR и любым известным или новым MINOR. Запрос recognition-request остаётся v1.0; файл результата и
конверт — v2.0 (MAJOR поднят: line_code стал nullable, metrics.warnings и document.warnings — структуры, удалён
checks.duplicate_line_codes, изменён смысл is_recognized у OCR). На окне перехода схемы прежнего MAJOR SHALL лежать
в docs/contracts/ рядом с текущими как recognition-result.v1.schema.json и recognition-result-message.v1.schema.json
(байтовые копии 1.2 и 1.0), их примеры — в docs/contracts/examples/v1/; scripts/validate_contracts.py проверяет оба
набора. Они удаляются тем же изменением, что удаляет маппер 1.x ядра.
Scenario: Несовместимая версия запроса
- WHEN сервис, поддерживающий запрос
1.x, получает запросschema_version = "2.0" - THEN публикуется результат
status = error,error.code = SCHEMA_MISMATCHбез ackprocessing: ответом на запрос служит сам финальный конверт, ядро закрывает им задание (published → error), и повторной доставки запроса не будет. Конверт публикуется только если из тела извлекаются корректныеjob_idиfile_id; иначе — как для нечитаемого запроса (ответа нет, ядро закроет задание поNO_ACK/dlq).attemptдляNats-Msg-Idберётся из тела запроса; при его отсутствии реализации расходятся (tabular-parserберётattemptиз заголовкаNats-Msg-Idзапроса или не отвечает,scan-gatewayподставляет 1) — см. OQ-71
Scenario: Ядро получает MAJOR, которого не знает
- WHEN ядро, знающее
1.xи2.x, получает конверт или файл результатаschema_version = "3.0" - THEN результат не проходит проверку схемы, файл получает
ERRORс причиной вmetrics.mapping_error, повтор не выполняется (как для любого результата не по схеме)
Requirement: Идемпотентность результатов без брокера
Публикующий SHALL ставить заголовок Nats-Msg-Id на каждое сообщение (запрос <job_id>:<attempt>, ack
ack:<job_id>:<attempt>, результат result:<job_id>:<attempt>). Поскольку серверного дедупа нет, ядро SHALL считать
результат дубликатом, если recognition_job.result_msg_id уже равен Nats-Msg-Id сообщения, и MUST NOT менять задание
повторно; потерянный результат SHALL обнаруживаться по reply_deadline (timeout).
Scenario: Результат доставлен дважды
- WHEN сервис опубликовал один и тот же результат два раза (повтор публикации после сетевой ошибки)
- THEN задание изменено один раз, второе сообщение проигнорировано
Requirement: Повторы публикации ответов сервисом
Сервис SHALL публиковать ack и результат с заголовком Nats-Msg-Id и при временной ошибке публикации (потеря соединения,
таймаут подтверждения сервера) повторять публикацию с тем же Nats-Msg-Id: одна первая попытка и до
PUBLISH_RETRIES повторов (значение по умолчанию 3, то есть не более 4 попыток), с паузой между попытками; пауза —
деталь реализации. Повторы исчерпаны — сервис пишет error-лог и не публикует это сообщение снова; задание закрывается
на стороне ядра (не пришёл результат — reply_deadline → timeout; не пришёл ack — повтор запроса и dlq, но результат,
дошедший без ack, ядро принимает и в статусе published). Дубликаты, появившиеся из-за повтора,
безопасны: ядро дедуплицирует их по Nats-Msg-Id.
Scenario: Временный сбой публикации
- WHEN публикация результата упала с временной ошибкой, а следующая попытка успешна
- THEN сообщение опубликовано с прежним
Nats-Msg-Id; ядро изменит задание один раз, даже если первая попытка тоже дошла
Scenario: Повторы исчерпаны
- WHEN все 1 +
PUBLISH_RETRIESпопыток публикации результата неуспешны - THEN сервис пишет error-лог и продолжает работу (процесс не падает), а ядро закрывает задание по
reply_deadline
Requirement: Копия контракта в сервисе идентична источнику
Каждый сервис, читающий или пишущий контракт, SHALL хранить копию файлов схем в api/contracts/ и проверять в make check
их байтовую идентичность файлам docs/contracts/ (../docs или путь ARM_KA_DOCS; при недоступности — пропуск с предупреждением).
Файл копии SHALL сверяться с файлом docs по правилу, одинаковому у ядра и сервисов и верному до, во время и после окна
перехода между MAJOR:
- схема
<имя>.schema.jsonкопии:M— номер из её$id(…/v<M>); пара —docs/contracts/<имя>.v<M>.schema.json, если такой файл есть, иначе основнойdocs/contracts/<имя>.schema.json(уrecognition-requestфайла.v1нет — его копия…/v1сверяется с основным файлом; до слияния v2.0 в docs файлов.v1нет вовсе — все копии сверяются с основными); - пример
examples/<имя>.jsonкопии: вид — по префиксу имени (request-— запрос,result-message-— конверт,result-— файл результата),M—MAJORкопии схемы этого вида; пара —docs/contracts/examples/v<M>/<имя>.json, если такой файл есть, иначеdocs/contracts/examples/<имя>.json; - прочие файлы копии (
<имя>.v<N>.schema.json,examples/v<N>/*.json— их держит ядро на окне) — с одноимённым файлом docs; нет его в docs — расхождение; - набор: каждой схеме и каждому примеру верхнего уровня docs (
contracts/*.schema.jsonбез.v<N>,contracts/examples/*.json) SHALL соответствовать файл копии; файлы docs*.v<N>.schema.jsonиexamples/v<N>/требуются в копии, только если копия держит этотMAJOR(ядро на окне); файлы другогоMAJORсервисом не сверяются.
Правило вводится в тест идентичности всех трёх потребителей (arm-core, tabular-parser, scan-gateway) до
слияния контракта v2.0 в docs (шаг 0 выкладки); иначе их make check упадёт на окне перехода. Ядро держит копии обоих
MAJOR с PR шага 3; при удалении MAJOR 1 (шаг 6) ядро сначала удаляет свои копии .v1, затем docs — файлы .v1.
Scenario: Копия устарела
- WHEN в
docs/contracts/изменена схема, а копия в сервисе — нет - THEN тест идентичности в
make checkсервиса падает с указанием имени файла
Scenario: Сервис ещё пишет 1.x после слияния v2.0 в docs
- WHEN в docs основной
recognition-result.schema.jsonуже v2.0, а копия вtabular-parser— v1.2 ($id…/v1) - THEN тест идентичности сверяет копию с
recognition-result.v1.schema.jsonи проходит; посторонний PR в сервис на окне перехода не краснеет
Scenario: Сервис перешёл на 2.0
- WHEN
scan-gatewayв PR шага 5 обновил копию до v2.0 ($id…/v2) и примеры до v2.0 - THEN тест сверяет схему с основным файлом docs (файла
.v2нет), примерыresult-*— сexamples/*.json;examples/v1/и*.v1.schema.jsondocs не сверяются
Scenario: Запрос v1.0 после слияния v2.0
- WHEN в docs уже есть
recognition-result.v1.schema.json, аrecognition-request.v1.schema.jsonнет; копия запроса в сервисе — v1.0 ($id…/v1), примерыrequest-image.json,request-tabular.json - THEN копия запроса сверяется с основным
recognition-request.schema.json, примеры запроса — сexamples/request-*.json(вexamples/v1/их нет); тест проходит
Scenario: Примеры прежнего MAJOR
- WHEN копия
tabular-parser— v1.2, в нейexamples/result-statement-excel.jsonверсии 1.2, а в docsexamples/result-statement-excel.jsonуже 2.0,examples/v1/result-statement-excel.json— 1.2 - THEN пример копии сверяется с
examples/v1/result-statement-excel.jsonи проходит
Scenario: Шаг 0 до слияния docs
- WHEN тест с новым правилом слит в
arm-core,tabular-parserиscan-gateway, а docs ещё на v1.2 без файлов.v1 - THEN все копии сверяются с основными файлами и примерами верхнего уровня, тест проходит, как прежде
Scenario: Ядро на окне перехода
- WHEN копия ядра после шага 3 держит основные схемы v2.0,
*.v1.schema.jsonиexamples/v1/ - THEN основные сверяются с основными docs,
.v1иexamples/v1/— с одноимёнными файлами docs; набор совпадает
Requirement: Subject'ы NATS — настройки окружения
Subject'ы обмена SHALL задаваться секретами группы OPENBAO__NATS__ с одинаковыми ключами во всех трёх сервисах:
NATS_TABULAR_REQUEST_SUBJECT (по умолчанию files.tabular_uploaded), NATS_IMAGE_REQUEST_SUBJECT (по умолчанию
files.img_uploaded), NATS_RESULT_SUBJECT (по умолчанию files.tabular_parsed). У ядра и сервисов одного окружения
значения MUST совпадать. Каждый сервис SHALL при старте проверять все три имени: непустое, без пробельных символов, без
шаблонов * и >, без пустых токенов, три имени попарно различны; при нарушении сервис MUST NOT стартовать.
Учётные данные NATS (OPENBAO__NATS__NATS_JWT, OPENBAO__NATS__NATS_SEED) SHALL задаваться по окружению теми же
секретами, и их значения MUST NOT попадать в логи, repr настроек и тексты ошибок валидации конфигурации. Каждый
сервис SHALL писать используемые subject'ы в лог при старте и отдавать их в /healthcheck объектом subjects
(ключи tabular_request, image_request, result — только те, что использует сервис) независимо от состояния NATS.
Queue group'ы и schema_version от настроек subject'ов не зависят.
Scenario: Умолчания
- WHEN сервис стартует без ключей
OPENBAO__NATS__NATS_*_SUBJECT - THEN обмен идёт по
files.tabular_uploaded,files.img_uploaded,files.tabular_parsed
Scenario: Набор тестового окружения
- WHEN всем трём сервисам заданы одинаковые
OPENBAO__NATS__NATS_TABULAR_REQUEST_SUBJECT,NATS_IMAGE_REQUEST_SUBJECT,NATS_RESULT_SUBJECT,NATS_JWT,NATS_SEEDтестового окружения - THEN ядро публикует запросы и получает ack и результаты только по этим subject'ам; subject'ы по умолчанию не
используются,
recognition_job.request_subjectсодержит subject тестового окружения
Scenario: Недопустимый набор
- WHEN
NATS_RESULT_SUBJECTсовпадает с одним из subject'ов запроса, или имя содержит*,>или пробел - THEN сервис не стартует с ошибкой конфигурации, называющей ключ
Scenario: Разъехавшиеся настройки видны
- WHEN у ядра и сервиса заданы разные subject'ы ответа
- THEN
/healthcheck.subjects.resultядра и сервиса различаются; JWT и seed в ответе/healthcheckотсутствуют
Scenario: Ошибка конфигурации не раскрывает JWT
- WHEN сервис не стартует из-за ошибки валидации настроек при заданных
NATS_JWTиNATS_SEED - THEN текст ошибки и лог не содержат значений JWT и seed
Requirement: Переход на контракт v2.0 — «сначала ядро»
Ядро SHALL до перехода любого сервиса принимать конверты (включая ack processing) и файлы результата и 1.x, и
2.0, с двумя мапперами файла результата. При приёме 1.x статус документа формы SHALL считаться по прежнему правилу
D-6, где «проверки §7 сошлись» — по расчёту ядра копией arm_statements (полный набор), записанному в document_issue,
а поля v2.0 заполняться по умолчанию: line_status = ok, row_index — по порядку строк в результате с 0, ref = null, line_code_original_value = line_code, text = string_value, needs_review значения = НЕ values_ok
(values_ok — is_recognized не false и confidence 100 или пуста: чистое значение — needs_review = false),
needs_review строки — false, period_kind — absolute при period_date, иначе none, form_variant — по
структуре кодов (точка входа пакета для пересчёта, спека arm-core), file_ordinal — индекс в documents[]; строки metrics.warnings — в recognition_job.metrics.warnings
структурами {code: "LEGACY_WARNING", severity: "info", message}; предупреждения маппера по документу —
document_issue MAPPING_WARNING (info); statements_version задания — null. Сервисы SHALL переходить на 2.0
только после выкладки ядра, принимающего 2.0. Маппер 1.x и схемы *.v1.schema.json удаляются после перехода обоих
сервисов отдельным изменением.
Scenario: Сервис ещё на v1.2
- WHEN после выкладки ядра tabular-parser присылает результат
1.2с повтором кода иchecks.duplicate_line_codes - THEN ядро принимает его маппером
1.x: из двух строк с одним кодом хранится первая (прежнее правило маппера1.x), вторая не хранится, у документаMAPPING_WARNINGо повторе; нарушения §7 — вdocument_issueот ядра, статус документа — по D-6, файл неERROR
Scenario: Чистое значение 1.x
- WHEN в результате
1.2значение сconfidence = 100иis_recognized = true - THEN
needs_review = false; значение сconfidence = 60—needs_review = true
Scenario: Ack нового сервиса
- WHEN scan-gateway после перехода публикует ack
processingсоschema_version = "2.0" - THEN ядро переводит задание в
processing, как для ack1.0
Scenario: Порядок нарушен
- WHEN сервис прислал результат
2.0сline_code = nullядру, знающему только1.x - THEN результат не проходит проверку схемы, файл получает
ERROR(metrics.mapping_error) — поэтому порядок «сначала ядро» обязателен