Да, теперь уже видно не просто то, что “онтология просажена”, а **почему именно она просажена и как именно её чинить**.

Главный вывод после догрузки: у вас ломается не “граф вообще”, а **семаническая нормализация ссылок**. Текущий mapping-слой построен слишком грубой эвристикой: все ссылки превращаются в один relation `reference`, а `target_entity` определяется почти только по подстроке в имени поля. Из-за этого система формально строит links, но содержательно не понимает, кто есть кто. Отсюда и 1016 `Unknown`-relations из 2618, при semantic coverage всего 61.19%, хотя link coverage 100%.

## Где именно источник поломки

Сейчас правило такое:

* если поле оканчивается на `_Key`, `ref`, или выглядит как GUID — оно становится link;
* relation всегда один: `reference`;
* target_entity угадывается по имени поля;
* если не угадали по нескольким словам вроде `контраг`, `договор`, `счет`, `организ`, `документ`, то ставится `Unknown`. 

Это и есть корневая ошибка архитектуры. Для бухгалтерского контура такой подход слишком бедный, потому что:

* `Recorder` — это не просто “reference”, а **источник движения / документ-регистратор**;
* `Ref` в журнале — это не просто “reference”, а **journal entry points to concrete document**;
* `Поставщик_Key` и `Покупатель_Key` — не generic refs, а **роли контрагентов**;
* `Ответственный_Key` — не generic ref, а **actor / responsible person**;
* `Валюта_Key` — это вообще не `Document`, а отдельная сущность валюты;
* `Склад_Key`, `ПодразделениеДт_Key`, `ФизЛицо_Key`, `СтатьяДвиженияДенежныхСредств_Key` требуют собственных классов, а не падения в `Unknown`.

## Что ломается на реальных примерах

### 1) `Recorder` в регистрах НДС

В НДС-регистрах `Recorder` приходит вместе с `Recorder_Type`, например:

* `Recorder_Type = StandardODATA.Document_ПоступлениеТоваровУслуг`
* либо `Recorder_Type = StandardODATA.Document_СчетФактураПолученный`
* либо `Recorder_Type = StandardODATA.Document_РеализацияТоваровУслуг`.
  Но в links это всё равно уходит как `target_entity = Unknown`.

Это критично, потому что для бухгалтерской аналитики регистр без корректного регистратора — почти полуслепая запись. Вы теряете причинную цепочку:
**движение регистра → документ-регистратор → контрагент/договор/товар/сумма**.

### 2) `СчетФактура` ошибочно уезжает в `Account`

В НДС-регистрах поле `СчетФактура` по смыслу ссылается на документ, а не на бухгалтерский счёт. Но из-за эвристики “если в имени есть `счет` → `Account`” оно типизируется как `Account`. Это уже не просто unknown, а **ложноположительное сопоставление**.

То есть у вас часть связей не просто потеряна, а неверно искажена.

### 3) `Ref` в журналах документов

В `DocumentJournal_БанковскиеВыписки` есть:

* `Ref`
* `Ref_Type = StandardODATA.Document_СписаниеСРасчетногоСчета`
  или `...ПоступлениеНаРасчетныйСчет`.
  Но link по `Ref` уходит в `Unknown`, хотя это должен быть прямой указатель на конкретный документ. Именно поэтому журналы дают много unknown и source_id=unknown.

### 4) Валюта маппится неверно

В `СписаниеСРасчетногоСчета` поле `ВалютаДокумента_Key` в одном из образцов уходит в `Document`, просто потому что в имени есть слово `документа`, хотя это ссылка на валюту. Аналогичная проблема в журналах по `Валюта_Key`, где поле уходит в `Unknown`.

### 5) Ролевые контрагенты не разведены

В регистрах есть `Поставщик_Key`, `Покупатель_Key`, `ДоговорКонтрагента_Key`.
Сейчас:

* `Поставщик_Key` и `Покупатель_Key` часто падают в `Unknown`,
* `ДоговорКонтрагента_Key` обычно распознаётся как `Contract`,
  но без явной роли в relation.

В результате граф знает, что “что-то связано с контрагентом/договором”, но не знает:

* это supplier или buyer,
* это основной контрагент документа или договор расчётов,
* это связь документа, журнала или регистра.

### 6) `Ответственный_Key`, `ФизЛицо_Key`, `Склад_Key`, `СтатьяДвиженияДенежныхСредств_Key`

Эти поля в топе проблемных, потому что для них вообще нет соответствующих сущностей в базовой canonical-модели. В текущем ядре у вас classes: `Organization`, `Counterparty`, `Contract`, `Account`, `Subconto`, `Document`, `Posting`, `RegisterMovement`, `Period`, плюс `CanonicalEntity`. Но нет нормальных классов для people/employee, warehouse, currency, cashflow article, department, item/product. Поэтому всё это закономерно валится в `Unknown`.

## Значит ли это, что надо “подкручивать только relation rules”?

Нет. Тут нужно чинить **сразу три слоя**:

### A. Расширять canonical classes

Минимально надо добавить:

* `EmployeeOrUser` / `ResponsiblePerson`
* `Currency`
* `Warehouse`
* `CashflowArticle`
* `Department`
* `Individual`
* `Item` / `Nomenclature`
* `BankAccount`
* `TaxRegisterRecord` или более общий `RegisterRecord`
* `InvoiceDocument` / `FacturaDocument` как подтип документа, если хотите потом делать объяснения по НДС аккуратнее.

Без этого вы можете переписать relation rules хоть десять раз, но часть полей всё равно некуда будет положить.

### B. Уходить от одного relation `reference`

Нужен не один `reference`, а словарь осмысленных relations. Минимальный стартовый набор я бы делал такой:

**Документы / журналы**

* `journal_refers_to_document`
* `document_belongs_to_organization`
* `document_has_counterparty`
* `document_has_contract`
* `document_has_currency`
* `document_has_warehouse`
* `document_has_responsible`
* `document_has_cashflow_article`
* `document_has_bank_account`

**Регистры**

* `register_recorded_by_document`
* `register_relates_to_supplier`
* `register_relates_to_buyer`
* `register_relates_to_invoice`
* `register_relates_to_vat_account`
* `register_relates_to_contract`
* `register_relates_to_organization`

**Финансовые документы**

* `payment_relates_to_counterparty`
* `payment_relates_to_bank_account`
* `payment_relates_to_cashflow_article`
* `payment_relates_to_individual`
* `payment_relates_to_department`

**Строки табличных частей**

* `document_line_has_item`
* `document_line_has_account`
* `document_line_has_vat_account`
* `document_line_has_expense_account`
* `document_line_has_income_account`

### C. Менять сам принцип типизации

Не по имени поля alone, а по комбинации:

1. `source_entity`
2. `source_field`
3. `*_Type` рядом
4. наличие `navigationLinkUrl`
5. контекст набора полей вокруг записи.

Именно это даст переносимость между разными 1С-контурами, а не ручную подгонку под июнь 2020, чего у вас как раз требует ТЗ.

## Как я бы правил правила маппинга

### Правило 1. `*_Type` имеет приоритет над эвристикой имени

Если есть:

* `Recorder_Type`
* `Ref_Type`
* `СчетФактура_Type`
* `ДокументОплаты_Type`
* и т.п.,
  то target_entity нужно определять не по имени поля, а по значению type.
  Например:
* `StandardODATA.Document_*` → `Document`
* `StandardODATA.Catalog_Контрагенты` → `Counterparty`
* `StandardODATA.Catalog_Склады` → `Warehouse`
* `StandardODATA.Catalog_Валюты` → `Currency`
* `StandardODATA.Catalog_ФизическиеЛица` → `Individual`
* `StandardODATA.Catalog_СтатьиДвиженияДенежныхСредств` → `CashflowArticle`.
  Это автоматически лечит большую часть `Recorder`, `Ref` и typed-полей.

### Правило 2. Для конкретных полей — словарь приоритетных semantic mappings

Нужен явный field dictionary, например:

* `Recorder` → relation `register_recorded_by_document`, target `Document`
* `Ref` в `DocumentJournal_*` → relation `journal_refers_to_document`, target `Document`
* `Поставщик_Key` → relation `*_relates_to_supplier`, target `Counterparty`
* `Покупатель_Key` → relation `*_relates_to_buyer`, target `Counterparty`
* `Ответственный_Key` → relation `*_has_responsible`, target `ResponsiblePerson`
* `Валюта_Key` / `ВалютаДокумента_Key` → relation `*_has_currency`, target `Currency`
* `Склад_Key` → relation `*_has_warehouse`, target `Warehouse`
* `СтатьяДвиженияДенежныхСредств_Key` → relation `*_has_cashflow_article`, target `CashflowArticle`
* `ФизЛицо_Key` → relation `*_relates_to_individual`, target `Individual`
* `БанковскийСчет_Key` / `СчетОрганизации_Key` → target `BankAccount`.

### Правило 3. `СчетФактура` — специальный case

Поле `СчетФактура` нельзя по общему правилу отправлять в `Account`.
Если рядом есть `СчетФактура_Type = StandardODATA.Document_*`, то это relation к документу:

* `register_relates_to_invoice`
* target `Document`.

### Правило 4. Нулевые GUID не создавать как обычные бизнес-связи

`00000000-0000-0000-0000-000000000000` сейчас плодит мусорные links. Их лучше:

* либо не писать в canonical links вообще,
* либо писать как `null_reference` / `empty_reference` технического типа, отдельно от семанических relations.

Это сразу уменьшит шум в графе и не будет создавать фальшивых “связей с нулевым контрагентом”.

### Правило 5. `source_id` для register records нельзя оставлять `unknown`

Для регистров, где нет `Ref_Key`, нужно собирать составной ключ, например:

* `source_entity + Recorder + Recorder_Type + LineNumber + Period`
  или аналог.
  Иначе у вас 358 записей с `source_id=unknown`, и это разрушает стабильное переиспользование сущности при повторных загрузках.

## Приоритеты ремонта по очереди

### Приоритет 1 — `Recorder`, `Ref`, `СчетФактура`, `Поставщик_Key`, `Покупатель_Key`

Это даст самый большой выигрыш по semantic coverage, потому что именно эти поля сейчас массово ломают регистры, журналы и ключевые документы.

### Приоритет 2 — `Ответственный_Key`, `Валюта_Key`, `ВалютаДокумента_Key`

Это быстро снижает долю `Unknown` в журналах и платежных документах, плюс улучшает explainability по “кто оформлял / в какой валюте”.

### Приоритет 3 — `СтатьяДвиженияДенежныхСредств_Key`, `ФизЛицо_Key`, `Склад_Key`, `ПодразделениеДт_Key`

Это уже следующий слой качества, особенно для операционных и зарплатных/кассовых сценариев.

## Что это даст benchmark’у

Если вы сделаете только этот ontology repair, без переписывания всего остального, я ожидаю:

* существенное падение `unknown_relations`;
* рост semantic coverage заметно выше текущих 61%;
* лучшую связность сценариев `document ↔ register ↔ journal`;
* меньше ошибочных ответов по cross-entity вопросам;
* меньше ложной уверенности в store-only ответах, потому что router будет видеть более богатую и корректную семантику.

Особенно выиграют вопросы вида:

* связать документ и проводки,
* связать контрагента, договор и документ,
* объяснить происхождение записи НДС,
* пройти от банковской выписки к первичному документу.

## Практический план, что делать прямо сейчас

Я бы формализовал следующий этап так:

**Шаг 1. Переписать mapper architecture**

* ввести `entity_type_resolver`
* ввести `relation_resolver`
* ввести `null_guid_filter`
* ввести `composite_source_id_builder` для register records.

**Шаг 2. Сделать словарь field semantics**
Отдельная таблица:

* `source_entity_pattern`
* `field_name`
* `optional_type_field`
* `target_class`
* `relation_name`
* `allow_zero_guid`
* `priority`.
  Это даст переносимость и управляемость.

**Шаг 3. Расширить canonical classes**
Хотя бы теми классами, которые сейчас массово падают в `Unknown`.

**Шаг 4. Переингестить июнь 2020**
После изменения rules — заново посадить slice и пересчитать:

* unknown_relations,
* semantic_coverage_pct,
* relation_types_total,
* top problematic fields.

**Шаг 5. Только потом — дочищать router**
Потому что router сейчас частично страдает вторично: он опирается на недотипизированный граф. Сначала надо дать ему нормальную семаническую подложку.

## Мой жёсткий вывод

Сейчас у вас не “плохая онтология”, а **слишком примитивный canonical mapper для домена уровня 1С-бухгалтерии**. Он годится как стартовый универсальный мост, но не как слой, который должен объяснять бухгалтерскую причинность. Проблема не в snapshot’е июня 2020 — он как раз полезен и достаточно показателен. Проблема в том, что mapping-слой ещё не различает:

* тип ссылки,
* бизнес-роль ссылки,
* typed reference,
* пустую ссылку,
* и специальные 1С-паттерны вроде `Recorder/Ref/СчетФактура`.

Следующим сообщением я могу собрать тебе уже **прямо готовое ТЗ на ремонт ontology/mapping-слоя**: с таблицей новых canonical classes, таблицей relation types и таблицей правил для `Recorder`, `Ref`, `Поставщик`, `Покупатель`, `Ответственный`, `Валюта`, `Склад`, `СтатьяДДС`.
