# Spec Delta: quality-metrics-model ## Purpose Модель данных и контракт фактов качества распознавания первички (AURORA-1759): какие факты пишутся, в каком формате, как они переживают удаление/перераспознавание документа и чем отличаются от рабочих таблиц. Алгоритм подсчёта и атрибуции — `context/05-domain-rules.md` §14 (не дублируется здесь). ## ADDED Requirements ### Requirement: Факты качества — append-only и без FK на рабочие таблицы `quality_intake_fact`, `quality_outcome_fact`, `quality_failure_fact` SHALL не иметь внешних ключей на `document`, `stored_file`, `recognition_job`, `legal_entity`, `company_group` — только простые `uuid` и денормализованные имена ЮЛ/ГК. Факты SHALL жить в отдельной схеме PostgreSQL (имя — настройка, по умолчанию `dkb_metrics`). #### Scenario: Документ удалён после записи факта - **WHEN** документ, по которому есть `quality_intake_fact`, физически удалён (`primary_files.delete`) - **THEN** строка факта остаётся читаемой, её `document_id` не разрешается в существующий `document`, и запрос к факту не падает на отсутствующем FK ### Requirement: `quality_intake_fact` — один факт на документ, кроме ОСВ без построчного снимка Таблица SHALL иметь ровно один факт на `document_id` (`unique`). Для `document_type = OSV` `payload` SHALL не содержать `lines`/`values` — только счётчики строк и предупреждения `OSV_*`; для `BALANCE`/`INCOME` `payload` SHALL содержать построчный снимок (`lines`, `values`). #### Scenario: Приём результата ОСВ - **WHEN** ядро мапит результат распознавания ОСВ в документ `OSV` - **THEN** `quality_intake_fact.payload` не содержит ключей `lines`/`values`, но содержит `osv_warnings` #### Scenario: Приём результата ББ - **WHEN** ядро мапит результат распознавания в документ `BALANCE` - **THEN** `quality_intake_fact.payload` содержит `lines` и `values` построчно ### Requirement: `quality_outcome_fact` — не unique на документ, метрики берут последний факт Таблица SHALL допускать несколько строк с одним `document_id` (повторное подтверждение ОСВ после правки, исход `*_AFTER_CONFIRM` поверх прежнего). Любой читатель метрик SHALL использовать факт с максимальным `outcome_at` для данного `document_id`. #### Scenario: Документ удалён после подтверждения - **WHEN** подтверждённый документ впоследствии удалён или вытеснен автозаменой - **THEN** пишется новый факт с `outcome ∈ {DELETED_AFTER_CONFIRM, SUPERSEDED_AFTER_CONFIRM}`, прежний факт (`CONFIRMED`/`AUTO_CONFIRMED`) не удаляется и не перезаписывается ### Requirement: Сырой ответ ИС Сканирование регистрируется и не экспирится с файлом `s3_object` SHALL поддерживать роль бакета `raw`; `recognition_job.raw_s3_object_id` SHALL ссылаться на зарегистрированный объект для image pipeline. Удаление `stored_file` SHALL НЕ устанавливать `expires_at` объекту с ролью `raw`. #### Scenario: Удаление файла с зарегистрированным сырым ответом - **WHEN** PDF-файл с заполненным `recognition_job.raw_s3_object_id` удаляется - **THEN** `s3_object.expires_at` объекта роли `raw` остаётся `NULL`, в отличие от объектов ролей `uploads`/`results` того же файла ### Requirement: Контракт payload не входит в `make check` этого репозитория `contracts/quality-facts.schema.json` SHALL существовать как валидная JSON Schema (Draft 2020-12), но SHALL NOT быть подключена к `scripts/validate_contracts.py` — payload не является сообщением NATS-контракта распознавания. #### Scenario: `make check` в arm-ka-docs - **WHEN** выполняется `make check` в этом репозитории - **THEN** `quality-facts.schema.json` не упоминается в выводе `validate-contracts`, и это не считается ошибкой