Этап 4 / Пакет по РБП восстановление живого контура сбора данных и доказательной базы

This commit is contained in:
2026-03-29 10:38:04 +03:00
parent 095e74ad3b
commit 133b6dca3c
83 changed files with 265742 additions and 77 deletions
@@ -0,0 +1,448 @@
разведку структуры 1С и связей через MCP по 3 контрольным кейсам.md
---
# ТЗ для Codex
## Этап 4 — разведка структуры 1С и связей через MCP по 3 контрольным кейсам
## Цель: понять, какие сущности, регистры, документы и переходы реально нужны для доказательного ответа
### 1. Контекст
Текущая проблема выглядит не как чисто текстовый дефект, а как сбой в контуре:
**вопрос → якоря → маршрут → данные 1С → связи между сущностями → evidence → ответ**
Есть подозрение, что система:
* не до конца понимает, какие объекты 1С нужны для ответа на конкретный тип вопроса;
* не до конца понимает, как эти объекты связаны;
* не всегда идёт в нужные документы/регистры через MCP;
* даже когда данные приходят, не всегда умеет собрать из них admissible evidence.
Эта работа не про новый prompt и не про wording.
Это **разведка структуры данных и зависимостей**.
---
## 2. Главная цель задания
Нужно не “починить ответы” и не “сделать ещё один rerun”, а:
### **Построить доказательную карту связей 1С по 3 типовым вопросам**
И ответить на вопросы:
1. какие сущности 1С реально участвуют в каждом кейсе;
2. какие связи между ними обязательны;
3. какие регистры и документы надо проходить;
4. какие MCP-вызовы нужны, чтобы это собрать;
5. где контур сейчас рвётся:
* источник,
* маршрут,
* mapping структуры 1С,
* evidence materialization,
* answer handoff.
---
## 3. Source of truth — 3 контрольных вопроса
### Вопрос 1 — НДС
**13 июля проведено поступление товаров, а 15 июля — реализация этих же мебельных позиций. НДС-цепочка по этим движениям у нас полная или где-то есть выпадение между документом, проводкой и налоговым отражением?**
### Вопрос 2 — РБП
**31 июля прошло “Списание РБП за Июль 2020 г.”, в том числе на 5 000 и ещё несколько сумм. Есть ли в базе признаки, что часть РБП к концу июля всё ещё живёт дольше ожидаемого?**
### Вопрос 3 — Амортизация
**31 июля начислена амортизация тремя суммами — 2 471,52, 2 465,28 и 849,83. Это похоже на полное начисление по всем нужным объектам за июль или есть риск, что какой-то объект ОС в июле не попал в амортизацию?**
Эти 3 вопроса являются **единственной обязательной базой** для текущей разведки.
---
## 4. Что нельзя делать
1. Не пытаться описать “всю 1С целиком”.
2. Не делать общую энциклопедию регистров.
3. Не уходить в redesign архитектуры.
4. Не строить новый proof engine.
5. Не ограничиваться пересказом старых отчётов.
6. Не делать выводы без артефактов.
7. Не “гулять по базе” без цели — только от вопроса к связанным сущностям.
---
## 5. Что нужно сделать
Работу нужно провести по 5 узлам.
---
# Узел A — Построить карту обязательных сущностей для каждого вопроса
Для каждого из 3 вопросов определить:
### A1. Seed entities
С чего начинается поиск:
* документ;
* контрагент;
* договор;
* объект расчётов;
* объект РБП;
* объект ОС;
* номенклатура;
* проводка;
* регистр.
### A2. Required entities
Какие сущности обязательно нужны для ответа.
#### Для НДС-кейса
Минимально проверить:
* документ поступления;
* документ реализации;
* счёт-фактура;
* движения по НДС;
* проводки;
* запись книги покупок / продаж;
* связь между документами и регистрами;
* мебельные позиции / номенклатура / объект товара;
* контрагент / договор / документная цепочка.
#### Для РБП-кейса
Минимально проверить:
* документ списания РБП;
* объект РБП;
* движения по РБП;
* остаток/хвост на конец июля;
* основание и график списания;
* связанный month-close контур;
* проводки / регистры / документы подтверждения.
#### Для амортизации
Минимально проверить:
* объекты ОС;
* документ начисления амортизации;
* движения начисления;
* expected set объектов на июль;
* фактическое попадание в начисление;
* проводки;
* остаточные хвосты / пропуски по объектам.
### A3. Expected transitions
Для каждого вопроса зафиксировать:
* какие переходы между сущностями **обязательны**,
* какие переходы являются “смежным контуром”,
* какие missing transitions означают реальную проблему.
---
# Узел B — Проверить, что из этого реально есть в snapshot и что реально доступно через MCP
### B1. Snapshot coverage
Для каждого вопроса определить:
* какие snapshot-источники реально содержат нужные сущности;
* достаточно ли snapshot-only для ответа;
* какие части цепочки snapshot не покрывает.
### B2. MCP/live coverage
Для каждого вопроса определить:
* какие MCP-методы/запросы позволяют добыть нужные сущности;
* что доступно напрямую;
* что доступно только через несколько переходов;
* где нужны пошаговые адресные вызовы.
### B3. Coverage verdict
По каждому вопросу дать жёсткий ответ:
* `snapshot_sufficient`
* `live_required`
* `snapshot_plus_live_required`
* `data_not_reachable_in_current_runtime`
---
# Узел C — Построить карту связей между элементами 1С
Это ключевая часть задания.
Нужно не просто перечислить сущности, а построить **relation map**.
### Для каждого вопроса определить:
1. какие связи прямые:
* документ → проводка
* документ → счёт-фактура
* документ → регистр
* объект → начисление
* списание → остаток
2. какие связи косвенные:
* договор → расчёты → документ
* номенклатура → документ поступления → реализация
* объект ОС → амортизационный контур
3. какие связи обязательны для доказательства;
4. какие связи являются “смежным контуром”, но не ядром ответа;
5. какие связи невозможно восстановить текущими средствами.
### Формат
Не “словесное описание”, а нормальная матрица:
* `entity_A`
* `entity_B`
* `relation_type`
* `required_for_claim`
* `reachable_via_snapshot`
* `reachable_via_live`
* `current_runtime_uses_it`
* `notes`
---
# Узел D — Проверить реальные маршруты через MCP
Codex должен не гадать, а **пройти по MCP/1С и проверить**.
### Для каждого вопроса:
1. какие вызовы реально нужны;
2. в каком порядке их нужно делать;
3. какие аргументы нужны;
4. какие из них сейчас реально используются runtime;
5. каких вызовов не хватает;
6. какие вызовы возвращают шум вместо полезной опоры;
7. где вызов правильный, но не хватает связи к следующему узлу.
### Нужен результат в форме:
* `question`
* `required_live_calls`
* `current_live_calls`
* `missing_live_calls`
* `wrong_live_calls`
* `call_sequence_for_proof`
---
# Узел E — Построить source-to-proof failure map
Для каждого из 3 вопросов нужно финально ответить:
### Что система должна была сделать
Например:
* для НДС — собрать цепочку документ → проводка → НДС-регистр → книга;
* для РБП — доказать, что часть РБП осталась жить дольше ожидаемого;
* для амортизации — доказать полноту/неполноту охвата объектов ОС.
### Что система реально смогла поднять
* какие объекты;
* какие регистры;
* какие связи;
* какие evidence items.
### Где контур рвётся
Использовать такие категории:
* `missing_source_data`
* `source_exists_but_runtime_does_not_use_it`
* `wrong_route_selection`
* `wrong_entity_mapping`
* `missing_relation_reconstruction`
* `live_call_insufficient_for_proof`
* `evidence_not_materialized`
* `claim_anchor_coverage_insufficient`
* `answer_layer_cannot_close_proof`
---
## 6. Как выполнять работу
### Этап 1 — Статический проход по коду
Понять:
* какие модули отвечают за source selection;
* какие — за MCP/live;
* какие — за mapping сущностей;
* какие — за evidence packaging;
* какие — за admissibility и answer mode.
### Этап 2 — Инвентаризация данных
Проверить:
* какие snapshot-файлы реально участвуют;
* что в них лежит;
* какие live endpoints/методы реально доступны.
### Этап 3 — Целевая разведка по 3 вопросам
Не общая прогулка по базе, а **управляемое движение от вопроса к связанным объектам**.
### Этап 4 — Relation reconstruction
Построить карту обязательных связей по каждому вопросу.
### Этап 5 — Failure classification
Показать, на каком узле реальный обрыв.
---
## 7. Что нужно создать
Создать новый комплект в `docs/ARCH`.
### Основной отчёт
`docs/ARCH/9 - разведка_структуры_1с_и_связей_через_mcp_по_3_контрольным_вопросам_2026-03-29.md`
---
## 8. Обязательные приложения
### `9A - entity_seed_map.md`
Для каждого вопроса:
* seed entities
* required entities
* expected transitions
### `9B - snapshot_vs_live_coverage_map.md`
Что реально есть в snapshot и что реально добывается через live.
### `9C - relation_map_1c_entities.md`
Карта связей между сущностями 1С по 3 кейсам.
### `9D - mcp_query_recipes.md`
Какие MCP-вызовы нужны для доказательного ответа по каждому кейсу.
### `9E - source_to_proof_failure_map.md`
Где именно рвётся контур по каждому вопросу.
### `9F - current_runtime_vs_required_runtime.md`
Сравнение:
* что нужно было сделать;
* что делает текущий runtime.
---
## 9. Папка артефактов
`docs/ARCH/9_audit_artifacts/`
Обязательно положить:
* `chat_export_3_questions.md`
* `debug_payloads/`
* `raw_live_calls/`
* `snapshot_samples/`
* `relation_probe_logs/`
* `query_recipe_examples/`
* `replay_logs/`
---
## 10. Главные вопросы, на которые отчёт обязан ответить
### По НДС
* какие сущности и регистры реально нужны;
* как они связаны;
* почему positive path уже работает или где ещё шум;
* что ещё надо дочистить.
### По РБП
* есть ли в source нужные данные вообще;
* есть ли путь к ним через MCP;
* где рвётся связка `source coverage + route + evidence materialization`.
### По амортизации
* какие объекты ОС должны участвовать;
* как понять expected coverage;
* почему admissible evidence уже есть, но proof не замыкается;
* где именно не хватает mapping/anchors.
---
## 11. Итоговый общий verdict
В конце основного отчёта обязательно выдать один из вердиктов:
* `SOURCE_COVERAGE_IS_PRIMARY_PROBLEM`
* `MCP_ROUTE_IS_PRIMARY_PROBLEM`
* `ONE_C_RELATION_MAPPING_IS_PRIMARY_PROBLEM`
* `EVIDENCE_MATERIALIZATION_IS_PRIMARY_PROBLEM`
* `MULTI_NODE_FAILURE_CONFIRMED`
И отдельно по каждому из 3 вопросов дать свой локальный verdict.
---
## 12. Критерий качества
После выполнения этой работы должно стать **невозможно** говорить расплывчато:
“кажется, плохо работаем с источником”.
Вместо этого должно быть ясно:
* какие данные нужны;
* где они лежат;
* как они связаны;
* как до них идти через MCP;
* где текущий runtime теряет их;
* что конкретно чинить следующим пакетом.
---
## 13. Что должно получиться на выходе
Не новая волна фиксов, а **карта реальных зависимостей** и **точная карта поломки** по трём кейсам.
После этого должно быть понятно:
1. надо ли делать отдельный pack на source coverage;
2. надо ли делать pack на relation mapping 1С;
3. надо ли делать pack на evidence materialization;
4. можно ли РБП и амортизацию чинить отдельно, а НДС только дочищать.
---
Если хочешь, следующим сообщением я дам ещё **короткое русское название этой работы для коммита/задачи** и **сверхкороткую версию промпта для Codex** без длинного ТЗ.
+388
View File
@@ -0,0 +1,388 @@
Этап 4 — пакет по РБП: покрытие источника, live-маршрут и материализация доказательной базы
### 1. Контекст
Этот пакет делается **узко под кейс РБП**, а не как новая общая волна по всей системе.
Вопрос про РБП уже зафиксирован как company-specific тест на lifecycle anomaly без выхода в полный Stage 5:
**“31 июля прошло “Списание РБП за Июль 2020 г.”, в том числе на 5 000 и ещё несколько сумм. Есть ли в базе признаки, что часть РБП к концу июля всё ещё живёт дольше ожидаемого?”**
По живому прогону видно, что сейчас кейс остаётся в `partial_coverage`, живёт на snapshot-логике, live-путь слабый, admissible evidence до ответа не доезжает, а среди ограничений прямо фигурируют:
* snapshot 2020 read-only;
* live probe как ограниченный выборочный запрос;
* `matched_rows = 0`;
* live evidence исключён из grounded answer;
* admissibility gate убирает non-admissible evidence.
Stage 4 по-прежнему держим в рамках `P0-only`:
* без новых доменов,
* без graph/schema expansion,
* без Stage 5/6 как нового core path,
* без больших транспортных рефакторингов.
---
## 2. Цель пакета
Нужно **не улучшить wording**, а восстановить для РБП нормальный путь:
**вопрос про РБП → правильные seed-объекты → правильный source coverage → правильный live route → admissible evidence → внятный proof-style ответ**
Итогом пакета должно стать:
1. понимание, какие данные по РБП реально нужны;
2. понимание, где они лежат в snapshot/live;
3. рабочий live/source path до этих данных;
4. non-zero admissible evidence по РБП-кейсу;
5. ответ, который ограничивается только если данных реально нет, а не потому что контур не дошёл до нужных сущностей.
---
## 3. Source of truth для пакета
### Базовый вопрос
**31 июля прошло “Списание РБП за Июль 2020 г.”, в том числе на 5 000 и ещё несколько сумм. Есть ли в базе признаки, что часть РБП к концу июля всё ещё живёт дольше ожидаемого?**
### Текущее живое поведение
По живому прогону для этого вопроса зафиксировано:
* `business_scope = generic_accounting`;
* `entity_hints = ["РБП"]`;
* `document_hints = ["Списание РБП"]`;
* `time_scope = "июль 2020"`;
* режим ответа — `partial_coverage`;
* в uncertainties присутствуют snapshot-only, ограниченный live probe, `matched_rows=0`, live evidence excluded, admissibility gate removed evidence.
Это и есть исходная точка, от которой строится пакет.
---
## 4. Главная рабочая гипотеза
Для РБП-кейса проблема сейчас не в одном месте, а в связке:
* **source coverage**: runtime не поднимает нужный набор сущностей по РБП;
* **route gap**: live path либо не выбирается, либо не ведёт к нужным данным;
* **evidence materialization**: даже если часть сигнала есть, он не превращается в admissible evidence.
Эта гипотеза должна быть либо подтверждена, либо опровергнута артефактами этого пакета.
---
# 5. Что нужно сделать
Пакет выполнить по 4 узлам.
---
# Узел A — восстановить минимальную предметную модель РБП
## Задача
Понять, **какие сущности и переходы вообще обязательны** для ответа на вопрос о “живущем дольше ожидаемого” РБП.
## Что сделать
Для кейса РБП определить:
### A1. Seed entities
* документ “Списание РБП за Июль 2020 г.”;
* суммы списания, включая 5 000;
* объект РБП / карточка РБП;
* период July 2020.
### A2. Required entities
Минимально установить, какие сущности нужны для доказательного ответа:
* документ списания РБП;
* объект(ы) РБП;
* движения/остатки по РБП;
* основание списания;
* expected schedule/lifecycle списания;
* хвост/остаток на конец июля;
* связанные регистры;
* проводки;
* month-close связка, если она обязательна.
### A3. Expected transitions
Зафиксировать обязательные переходы:
* объект РБП → документ списания;
* документ списания → движение/регистр/проводка;
* движение/остаток → состояние на конец июля;
* expected schedule → фактическое списание;
* июльский остаток → признак “живёт дольше ожидаемого”.
## Acceptance
* Для РБП должен появиться явный `required_entity_map`, а не общий доменный шум.
* Должно быть ясно, **без каких сущностей proof в принципе невозможен**.
---
# Узел B — аудит покрытия источника по РБП
## Задача
Понять, **есть ли вообще нужные данные**, и где именно они доступны.
## Что сделать
Проверить отдельно:
### B1. Snapshot coverage
* какие snapshot-файлы реально содержат данные по РБП;
* есть ли в них документ списания;
* есть ли объекты РБП;
* есть ли остатки/движения/проводки;
* хватает ли snapshot-only, чтобы ответить на вопрос.
### B2. Live coverage
* какие MCP/live методы позволяют добраться до РБП-данных;
* есть ли прямой путь до:
* документа списания,
* объекта РБП,
* остатка на конец периода,
* движения списания,
* schedule/основания;
* что доступно напрямую, а что только через несколько шагов.
### B3. Coverage verdict
Для каждого обязательного элемента указать:
* `available_in_snapshot`
* `available_via_live`
* `available_but_not_used_by_runtime`
* `not_reachable_in_current_runtime`
## Acceptance
* По каждому required entity должно быть ясно, **где оно лежит и как до него дотянуться**.
* Должен появиться жёсткий ответ: проблема в отсутствии данных или в том, что runtime их не использует.
---
# Узел C — починить live route под РБП
## Задача
Сделать так, чтобы runtime **реально шёл** в нужный контур РБП, а не оставался на snapshot-only/semantic profile.
## Что сделать
1. Определить корректный `claim_type` для РБП-кейса, например:
* `prove_rbp_tail_state`
* `prove_rbp_lifecycle_overstay`
2. Для него прописать required live route:
* первый вызов на документ списания;
* второй на объект РБП;
* третий на движения/остатки;
* четвёртый на подтверждение конца периода / хвоста.
3. Проверить, что runtime действительно выбирает этот путь, а не остаётся в `canonical-only`.
4. В debug payload вывести:
* `claim_type`
* `required_live_calls`
* `executed_live_calls`
* `missing_live_calls`
* `route_gap_reason`
## Acceptance
* У РБП-кейса должен появиться **непустой и осмысленный live call path**.
* Live path должен быть привязан к claim, а не быть декоративным probe.
---
# Узел D — materialization: превратить данные по РБП в admissible evidence
## Задача
Убрать главный провал: данные/сигналы есть или могут быть, но до admissible evidence они не доезжают.
## Что сделать
Для РБП-кейса восстановить полный путь:
`raw source result -> candidate evidence -> admissible evidence -> answer`
И по каждому шагу показать:
* что пришло;
* что было отрезано;
* почему было отрезано;
* что должно было стать admissible evidence, но не стало.
### Обязательно классифицировать причины reject-а
Использовать причины вида:
* `wrong_period`
* `wrong_domain`
* `weak_source_mapping`
* `zero_live_match`
* `missing_relation_reconstruction`
* `missing_required_anchor`
* `insufficient_object_identity`
### Цель
Не просто сделать `admissible > 0`, а понять, **какие именно evidence items должны считаться допустимыми для РБП proof**.
## Acceptance
* У РБП-кейса должно появиться **non-zero admissible evidence**, либо должно быть строго доказано, почему это невозможно на текущих данных.
* Ответ “нет admissible evidence” должен быть объясним именно через materialization path.
---
# Узел E — финальный proof contract по РБП
## Задача
Сделать так, чтобы answer mode по РБП зависел от фактического proof-path, а не от общей слабой гипотезы.
## Что сделать
Финальный ответ по РБП должен собираться так:
### Если admissible evidence есть
Ответ должен уметь сказать:
* какие объекты РБП подтверждены;
* по каким из них списание подтверждено;
* по каким виден хвост/остаток на конец июля;
* почему это похоже на “живёт дольше ожидаемого”.
### Если admissible evidence всё ещё нет
Ответ должен честно сказать:
* какое именно звено не удалось подтвердить;
* не просто “сигнал слабый”, а:
* нет документа,
* нет объекта,
* нет движения,
* нет остатка,
* нет связи между ними.
## Acceptance
* РБП-кейс должен перестать быть общим lifecycle-туманом.
* Answer mode должен быть привязан к реальному состоянию claim-proof.
---
## 6. Проверки
Обязательно выполнить:
1. replay исходного РБП-вопроса;
2. debug/export после фикса;
3. live call inventory;
4. evidence conversion trace;
5. before/after по answer mode.
Желательно дополнительно:
* один соседний month-close кейс на sanity-check;
* один follow-up по тому же РБП-кейсу.
---
## 7. Метрики пакета
Добавить/обновить:
* `rbp_required_entity_coverage_rate`
* `rbp_live_route_execution_rate`
* `rbp_admissible_evidence_nonzero_rate`
* `rbp_source_to_proof_completion_rate`
* `rbp_partial_coverage_default_rate`
* `rbp_false_grounded_answer_rate`
### Минимальные пороги
* `rbp_required_entity_coverage_rate >= 0.9`
* `rbp_live_route_execution_rate >= 0.9`
* `rbp_admissible_evidence_nonzero_rate > 0`
* `rbp_false_grounded_answer_rate = 0`
* `rbp_partial_coverage_default_rate` должно снизиться относительно текущего live baseline
---
## 8. Обязательные артефакты
В новой run-папке положить:
1. `README.md`
2. `run_summary.json`
3. `rbp_source_coverage_report.md`
4. `rbp_live_route_report.md`
5. `rbp_evidence_materialization_report.md`
6. `rbp_claim_proof_report.md`
7. `rbp_before_after_matrix.md`
8. `chat_export_rbp.md`
9. `debug_payloads/`
10. `raw_live_calls/`
11. `rbp_required_entity_map.json`
12. `rbp_admissibility_reject_breakdown.json`
---
## 9. Что не делать
* не трогать НДС как главный фокус;
* не расползаться на общую архитектуру;
* не идти в новую большую волну по всем P0-доменам;
* не закрывать пакет по summary без replay РБП-кейса;
* не маскировать отсутствие данных красивым limited-answer.
---
## 10. Финальный verdict
В конце пакета выдать:
* `RBP_SOURCE_COVERAGE_FIXED / NOT_FIXED`
* `RBP_LIVE_ROUTE_FIXED / NOT_FIXED`
* `RBP_EVIDENCE_MATERIALIZATION_FIXED / NOT_FIXED`
И общий статус:
* `RBP_PACK_ACCEPTED`
* или `RBP_PACK_ACCEPTED_WITH_LIMITATIONS`
* или `RBP_PACK_NOT_ACCEPTED`
---
## 11. Ожидаемый результат
После этого пакета должно стать ясно:
* есть ли у вас вообще рабочий proof-path по РБП;
* можно ли из текущих данных построить доказательный ответ;
* если нельзя — это потому что данных реально нет, или потому что runtime не умеет до них дойти.
И главное:
по РБП должно стать **невозможно** говорить абстрактно “слабая доказательная база”.
Должно быть понятно, **что именно не доезжает и на каком узле**.
Если хочешь, следующим сообщением я дам ещё:
1. **понятное русское название этой волны**,
2. **текст коммита на 3–4 строки**.
Binary file not shown.
@@ -0,0 +1,111 @@
# 8 — Аудит source-to-proof по 3 контрольным вопросам (2026-03-29)
Статус: COMPLETED
Этап: Stage 4 (P0-only)
## 1) Что проверялось
Главный вопрос аудита:
**Есть ли в системе данные и маршруты, достаточные для ответа на 3 контрольных вопроса, и если да — на каком узле они теряются?**
Контрольные вопросы:
1. НДС-цепочка (13/15 июля, мебель).
2. РБП-хвост на 31 июля.
3. Амортизация 31 июля (2 471,52 / 2 465,28 / 849,83).
## 2) Источники фактов (не summary-only)
Аудит выполнен на базе реальных replay-артефактов и code inspection:
- `x:\1C\NDC_1C\docs\ARCH\8_audit_artifacts\question_replay_logs\1_live_replay.txt`
- `x:\1C\NDC_1C\docs\ARCH\8_audit_artifacts\debug_payloads\*.json`
- `x:\1C\NDC_1C\docs\ARCH\8_audit_artifacts\raw_live_calls\live_call_inventory_full_3_cases.json`
- `x:\1C\NDC_1C\docs\ARCH\8_audit_artifacts\snapshot_samples\snapshot_inventory_runtime.json`
- runtime code path (`assistantService`, `assistantDataLayer`, `assistantRuntimeGuards`, `assistantClaimBoundEvidence`, `answerComposer`)
## 3) Краткий итог по 3 кейсам
| Case | Claim | Reply mode | Admissible evidence | Ключевой исход |
| --- | --- | --- | ---: | --- |
| Q1 VAT (L1) | `prove_vat_chain_completeness` | grounded-positive | 12 | positive path работает |
| Q2 RBP (L2) | `prove_rbp_tail_state` | limited | 0 | proof path не замкнут |
| Q3 FA (L3) | `prove_month_close_state` | limited | 19 | evidence есть, но eligibility блокирует |
Сводные replay-метрики:
- `live_temporal_contradiction_rate = 0`
- `live_company_scope_resolution_rate = 1`
- `live_admissible_evidence_nonzero_rate = 0.6667`
- `live_partial_coverage_default_rate = 0.6667`
## 4) Ответ по узлам A–E
### Узел A — какие данные нужны
- Карта обязательных сущностей/связок по каждому вопросу заполнена в `8A`.
- Для Q2/Q3 текущий runtime source set не дает полный object-level набор для доказательного закрытия claim.
### Узел B — что реально есть в snapshot/live
- Runtime подключен к ограниченному `2020экспорт` набору (7 JSON файлов).
- Monthly as-of corpus физически есть (`2020_monthly_company_asof_full_test`), но не подключен текущим data layer.
- Live MCP есть, но запрос broad (generic accounting register probe).
### Узел C — как реально идет маршрут
- Q1: route корректный (`store_canonical + hybrid_store_plus_live`) -> grounded-positive.
- Q2: route уходит в `store_canonical + store_canonical`, live verification path не используется.
- Q3: route включает live (`store_feature_risk + hybrid_store_plus_live`), но mapping и anchors не закрывают eligibility.
### Узел D — как live превращается/не превращается в evidence
- Q1: `fetched=24, matched=24`, admissible `12` -> live участвует как verification path.
- Q2: live call отсутствует (route-level).
- Q3: `fetched=24, matched=24`, admissible `19`, но proof блокируется на claim-anchor coverage.
### Узел E — что именно ломает ответ
- Q1: не blocker; остаточный шум admissibility (`wrong_account_scope`, `weak_source_mapping`).
- Q2: multi-break на route/source/evidence узлах.
- Q3: multi-break на entity mapping + anchor coverage + live targeting specificity.
## 5) Вердикт по каждому вопросу (требуемый формат)
### Q1 — НДС
Проблема в первую очередь:
- **не** в отсутствии данных;
- **не** в полном провале маршрута;
- в комбинации: `wrong_live_call_target` + `admissibility_reject_not_due_to_data` (residual quality).
### Q2 — РБП
Проблема в комбинации:
- `source_not_connected_to_runtime`
- `missing_source_data` (для object-level proof в активном source set)
- `wrong_route_selection`
- `evidence_not_materialized`
- `admissibility_reject_not_due_to_data`
### Q3 — Амортизация
Проблема в комбинации:
- `wrong_entity_mapping`
- `wrong_live_call_target`
- `admissibility_reject_not_due_to_data`
- `answer_layer_underuses_available_evidence` (через `claim_anchor_coverage_insufficient`)
## 6) Главный вывод аудита
**MULTI_NODE_FAILURE_CONFIRMED**
Почему:
1. Один кейс (Q1) уже может идти в grounded-positive, значит контур не сломан тотально.
2. Два кейса (Q2/Q3) ломаются в разных местах цепочки.
3. Наблюдается не один первичный дефект, а связка: source coverage + routing + mapping + evidence materialization + eligibility handoff.
## 7) Что это означает для следующей fix-wave
Без redesign Stage 5/6, в рамках Stage 4 приоритетно чинить:
1. Route и source-подключение для RBP/FA claim-path.
2. Claim-targeted live query narrowing (не generic `TOP N` probe).
3. Entity/anchor mapping для object-level month-close/FA доказательности.
4. Admissibility tuning, чтобы уменьшить reject not-due-to-data.
См. детализацию в:
- `8A` (required data map)
- `8B` (coverage inventory)
- `8C` (runtime replay matrix)
- `8D` (live->evidence chain)
- `8E` (gap register)
- `8F` (code path inventory)
@@ -0,0 +1,58 @@
# 8A — Source Requirements Map
Дата: 2026-03-29
Контрольные вопросы: Q1 НДС, Q2 РБП, Q3 Амортизация
## Q1 — НДС цепочка (13/15 июля)
| Блок доказательства | Что нужно для proof | Минимально обязательно | Что есть в runtime source set |
| --- | --- | --- | --- |
| Source docs | Поступление + реализация по тем же мебельным позициям | Да | Частично: `05_samples_RealizaciyaTovarovUslug.json`, `06_samples_PostuplenieTovarovUslug.json` |
| Invoice layer | Счет-фактура / invoice linkage | Да | Частично через `08_samples_NDS_registers.json`, `09_samples_key_fields_...json` |
| Tax/register layer | НДС регистры (покупки/продажи/предъявленный) | Да | Да: `08_samples_NDS_registers.json` |
| Book layer | Запись книги покупок/продаж | Да | Частично (по available register records) |
| Posting layer | Проводка и связка с документом | Да | Частично: snapshot + live MCP movement probe |
| Link integrity | Документ → проводка → регистр → книга | Да | Частично; подтверждается не по всем звеньям без reject |
**Минимальный набор, без которого ответ всегда гипотетический:**
1. Пара документов (receipt + sale) по одной предметной позиции.
2. НДС-связка invoice/register/book.
3. Подтвержденная posting/link связь на тот же период.
## Q2 — РБП хвост на 31 июля
| Блок доказательства | Что нужно для proof | Минимально обязательно | Что есть в runtime source set |
| --- | --- | --- | --- |
| Write-off document | Документ/операция «Списание РБП за июль» | Да | Неявно/недостаточно в подключенном наборе |
| RBP object mapping | Объект РБП + параметр списания | Да | Недостаточно в подключенном snapshot profile |
| Lifecycle | Начало/продолжение/списание РБП | Да | Недостаточно в подключенных источниках |
| End-of-period residual | Остаток РБП на конец июля | Да | Не подтверждено admissible evidence |
| Contradiction check | «нормальный остаток» vs «хвост дольше ожидаемого» | Да | Не подтверждено |
**Минимальный набор, без которого ответ всегда partial:**
1. Явный write-off документ за июль.
2. Трассировка по объекту РБП до остатка на конец месяца.
3. Признак нормы/аномалии остатка с period-closure контекстом.
## Q3 — Амортизация (2 471,52 / 2 465,28 / 849,83)
| Блок доказательства | Что нужно для proof | Минимально обязательно | Что есть в runtime source set |
| --- | --- | --- | --- |
| Depreciation document | Документ начисления амортизации за июль | Да | Частично/косвенно |
| FA object set | Полный expected набор ОС на июль | Да | Нет полного object-level набора в runtime snapshot profile |
| Per-object fact | Начисление по каждому объекту ОС | Да | Частично, с weak mapping |
| Sum reconciliation | Сверка сумм с объектами и периодом | Да | Частично, anchors по суммам есть, но claim coverage нестабильный |
| Tail/risk check | Пропущенный объект vs нормальная картина | Да | Частично, итог limited из-за `claim_anchor_coverage_insufficient` |
**Минимальный набор, без которого ответ всегда partial:**
1. Полный expected set ОС за июль.
2. Связка объект ОС → начисление → проводка.
3. Объектно-уровневая сверка сумм и пропусков.
## Cross-question mandatory core
Для всех трех вопросов обязательны:
1. Жесткий периодный anchor (July 2020) как primary basis.
2. Claim-bound required checks (не broad retrieval).
3. Admissible evidence с source refs, не только candidate signals.
4. Явная причина ограничения (какое звено не подтверждено), если proof не закрывается.
@@ -0,0 +1,49 @@
# 8B — Snapshot and Live Coverage Inventory
Дата: 2026-03-29
Источник фактов: runtime wiring + replay artifacts в `x:\1C\NDC_1C\docs\ARCH\8_audit_artifacts`
## 1) Snapshot: что реально подключено runtime
Подключение подтверждено в `assistantDataLayer.ensureData`.
| Файл | Путь | Размер (байт) | Records | Использование |
| --- | --- | ---: | ---: | --- |
| `03_snapshot_fragment_problem_cases.json` | `x:\1C\NDC_1C\docs\ARCH\2020экспорт\03_snapshot_fragment_problem_cases.json` | 302520 | 80 | problem/risk signals |
| `04_samples_SpisanieSRaschetnogoScheta.json` | `x:\1C\NDC_1C\docs\ARCH\2020экспорт\04_samples_SpisanieSRaschetnogoScheta.json` | 196178 | 27 | settlement/payment docs |
| `05_samples_RealizaciyaTovarovUslug.json` | `x:\1C\NDC_1C\docs\ARCH\2020экспорт\05_samples_RealizaciyaTovarovUslug.json` | 132032 | 5 | realization docs |
| `06_samples_PostuplenieTovarovUslug.json` | `x:\1C\NDC_1C\docs\ARCH\2020экспорт\06_samples_PostuplenieTovarovUslug.json` | 181409 | 10 | receipt docs |
| `07_samples_DocumentJournals.json` | `x:\1C\NDC_1C\docs\ARCH\2020экспорт\07_samples_DocumentJournals.json` | 292931 | 80 | document journals |
| `08_samples_NDS_registers.json` | `x:\1C\NDC_1C\docs\ARCH\2020экспорт\08_samples_NDS_registers.json` | 291547 | 80 | VAT registers |
| `09_samples_key_fields_Recorder_Ref_Supplier_Buyer_Responsible.json` | `x:\1C\NDC_1C\docs\ARCH\2020экспорт\09_samples_key_fields_Recorder_Ref_Supplier_Buyer_Responsible.json` | 511133 | 140 | recorder/ref key fields |
## 2) Snapshot: что физически существует, но не подключено в текущий runtime path
| Набор | Путь | Состояние |
| --- | --- | --- |
| Monthly as-of corpus (12 файлов) | `x:\1C\NDC_1C\docs\ARCH\2020_monthly_company_asof_full_test` | Есть, файлы `snapshot_2020-01..12_asof_full.json` (42–46 MB каждый), но не подключены `assistantDataLayer` |
| Monthly manifest | `x:\1C\NDC_1C\docs\ARCH\2020_monthly_company_asof_full_test\manifest_2020_monthly_company_asof_full.json` | Есть, показывает `entity_sets_total=2` (AccountingRegister only) |
| `2020_monthly_company_asof_full` | `x:\1C\NDC_1C\docs\ARCH\2020_monthly_company_asof_full` | Папка есть, runtime ее не читает |
## 3) Live MCP/toolkit фактическое покрытие
| Параметр | Факт |
| --- | --- |
| Runtime live endpoint | `/api/execute_query` через MCP proxy |
| Query template | Generic выборка из `РегистрБухгалтерии.Хозрасчетный` (`TOP __LIMIT__`, без claim-specific where) |
| Фильтрация по account_scope в live query | Не задана (`account_scope: []` в live cases L1/L3) |
| Роль live в текущем контуре | Overlay enrichment + post-filter через admissibility gate |
## 4) Coverage verdict по 3 вопросам
| Вопрос | Данные есть в принципе | Доступны runtime | Использованы runtime | Ключевой вывод |
| --- | --- | --- | --- | --- |
| Q1 VAT | Да (snapshot + live) | Да | Да | Positive grounding достижим, но с высоким reject шумом |
| Q2 RBP | Частично | Частично | Ограниченно | Для proof path покрытие недостаточно, кейс остается limited |
| Q3 FA amortization | Частично | Частично | Да, но нестабильно | Есть admissible evidence, но claim/object proof не замыкается |
## 5) Узел B — ответ в требуемом формате
- Q1 (НДС): данные есть и доступны; используются; узкое место — admissibility noise + generic live targeting.
- Q2 (РБП): часть данных либо не в активном source set, либо не доезжает через текущий route; runtime usage недостаточен для proof.
- Q3 (Амортизация): данные частично есть и используются, но не в форме, достаточной для object-complete proof.
@@ -0,0 +1,35 @@
# 8C — Runtime Route Replay Matrix
Дата: 2026-03-29
Replay база: `x:\1C\NDC_1C\docs\ARCH\8_audit_artifacts\question_replay_logs\1_live_replay.txt`
## Matrix (3 control questions)
| Поле | Q1 VAT (L1) | Q2 RBP (L2) | Q3 FA amortization (L3) |
| --- | --- | --- | --- |
| Raw user question | Поступление 13 июля + реализация 15 июля, полнота НДС-цепочки | Списание РБП 31 июля (в т.ч. 5 000), есть ли хвост | Амортизация 31 июля по 3 суммам, полный ли охват ОС |
| Extracted anchors (ключевые) | period `2020-07-13`, `vat_signal`, `chain_signal` | period `2020-07-31`, amount `5 000`, `rbp_signal`, `writeoff_signal` | period `2020-07-31`, amounts `2 471,52/2 465,28/849,83` |
| Claim / question type | `prove_vat_chain_completeness`, `which_chains_are_complete_vs_incomplete` | `prove_rbp_tail_state`, `what_is_it_grounded_on` | `prove_month_close_state`, `why_breaks` |
| Business scope | raw/resolved: `company_specific_accounting` | raw/resolved: `company_specific_accounting` | raw/resolved: `company_specific_accounting` |
| Temporal basis | raw `2020-07` → resolved `2020-07-13`, guard `passed` | raw `2020-07` → resolved `2020-07-31`, guard `passed` | raw `2020-07` → resolved `2020-07-31`, guard `passed` |
| Selected route | `store_canonical` + `hybrid_store_plus_live` | `store_canonical` + `store_canonical` | `store_feature_risk` + `hybrid_store_plus_live` |
| Selected source profile (semantic) | `settlements` + `vat,taxes` | `deferred_expense,period_close` | `fixed_assets,period_close` |
| Snapshot source selection | Domain gate + semantic narrowing; docs + nds registers + key fields | Canonical docs/journals/key fields, без live | Risk + hybrid domain gate; problemCases/docs/journals/key fields |
| Live call selection | Да (F2): `execute_query`, fetched 24, matched 24 | Нет (оба фрагмента canonical) | Да (F2): `execute_query`, fetched 24, matched 24 |
| Retrieved raw entities (факт) | Документы поступления/реализации/списания | Нет material items в финальном normalized result | Преимущественно `NDFL` регистры + журналы + часть документов |
| Candidate evidence total | 30 | 12 | 35 |
| Admissible evidence | 12 | 0 | 19 |
| Eligibility | `grounded_allowed` | `limited_or_insufficient_evidence` | `limited_or_insufficient_evidence` |
| Final answer mode | `factual_with_explanation` / `grounded_positive` | `partial_coverage` / `limited_or_insufficient_evidence` | `partial_coverage` / `limited_or_insufficient_evidence` |
## Дополнительные наблюдения по маршруту
1. Q1: route корректный для chain-case, positive path сработал, но reject шум высокий (`weak_source_mapping`, `wrong_account_scope`).
2. Q2: route не включает live claim-bound путь; фактически pipeline закрывается на canonical snapshot и обнуляется в admissibility.
3. Q3: route включает live, но entity-level mapping уходит в нерелевантные регистры; claim anchors не покрыты, eligibility блокирует grounded.
## Узел C — требуемый ответ
- Есть случаи, где route правильный, но evidence не дожимается до полного proof (Q3).
- Есть случай, где route в текущей конфигурации не выводит в verification path (Q2).
- Есть случай, где route+source дают grounded-positive (Q1), но с остаточным качественным шумом по admissibility.
@@ -0,0 +1,47 @@
# 8D — Live Call to Evidence Matrix
Дата: 2026-03-29
Источник:
- `x:\1C\NDC_1C\docs\ARCH\8_audit_artifacts\raw_live_calls\live_mcp_to_evidence_handoff.json`
- `x:\1C\NDC_1C\docs\ARCH\8_audit_artifacts\evidence_conversion_traces\L*_evidence_trace.json`
## Матрица live -> evidence
| Case | Live call | Method | Query subject | Fetched | Matched | Returned | Candidate evidence | Admissible evidence | Final mode |
| --- | --- | --- | --- | ---: | ---: | ---: | ---: | ---: | --- |
| Q1 VAT (L1) | Yes | `execute_query` | `vat_chain_conflict` | 24 | 24 | 12 | 30 | 12 | `grounded_positive` |
| Q2 RBP (L2) | No | — | — | 0 | 0 | 0 | 12 | 0 | `limited_or_insufficient_evidence` |
| Q3 FA (L3) | Yes | `execute_query` | `fixed_asset_card_mismatch` | 24 | 24 | 12 | 35 | 19 | `limited_or_insufficient_evidence` |
## Evidence conversion breakdown
### Q1 VAT
- `raw source result`: live rows + snapshot chain candidates.
- `candidate -> admissible`: `30 -> 12`.
- `reject reasons`: `wrong_account_scope=12`, `weak_source_mapping=14`.
- `роль live`: verification path (не purely decorative), потому что кейс вышел в grounded-positive.
### Q2 RBP
- `raw source result`: live route не выбран (оба фрагмента `store_canonical`).
- `candidate -> admissible`: `12 -> 0`.
- `reject reasons`: `wrong_period=36`, `weak_source_mapping=12`.
- `роль live`: отсутствует в этом кейсе из-за route selection, не из-за MCP downtime.
### Q3 FA
- `raw source result`: live rows есть, плюс snapshot risk/hybrid источники.
- `candidate -> admissible`: `35 -> 19`.
- `reject reasons`: `wrong_domain=8`, `wrong_account_scope=8`, `weak_source_mapping=16`, `future_dated_or_out_of_window=8`.
- `роль live`: частично verification, но admissible evidence не конвертируется в grounded из-за claim-anchor gap.
## Узел D — ответы в требуемом формате
1. Был ли live декоративным enrichment или verification path?
- Q1: verification path.
- Q2: live path не был выбран.
- Q3: частично verification, но недожатый до proof.
2. Если строки пришли, почему не стали admissible evidence полностью?
- Из-за `wrong_account_scope`, `wrong_domain`, `weak_source_mapping`, `future_dated_or_out_of_window`.
3. Если строки не пришли (или call отсутствует), где проблема?
- В Q2 проблема в route selection + source profile, а не в технической недоступности MCP.
@@ -0,0 +1,41 @@
# 8E — Source-to-Proof Gap Register
Дата: 2026-03-29
## Severity scale
- `blocker`: без исправления невозможно закрыть proof path по целевому вопросу.
- `high`: сильно влияет на grounded eligibility и стабильность.
- `medium`: не блокирует всегда, но создает деградации/шум.
## Gap table
| Gap ID | Вопрос | Категория | Severity | Где рвется | Доказательство |
| --- | --- | --- | --- | --- | --- |
| GAP-8E-01 | Q2, Q3 | `source_not_connected_to_runtime` | blocker | Runtime читает ограниченный `2020экспорт`; monthly as-of corpus не включен в data layer | `8B`, `8F`, `snapshot_inventory_runtime.json` |
| GAP-8E-02 | Q2 | `missing_source_data` | high | Для RBP не подтверждаются object-level writeoff/residual звенья в активном source set | `L2_evidence_trace.json`, `8A` |
| GAP-8E-03 | Q2 | `wrong_route_selection` | high | Оба фрагмента routed в `store_canonical`, live verification path не включается | `runtime_route_snapshot.json`, `8C` |
| GAP-8E-04 | Q3 | `wrong_entity_mapping` | high | FA claim тянет нерелевантные сущности (NDFL-регистры) как опору | `L3_evidence_trace.json`, `8C` |
| GAP-8E-05 | Q1, Q3 | `wrong_live_call_target` | high | Live `execute_query` идет без claim/account фильтра (`account_scope=[]`) | `live_call_inventory_full_3_cases.json`, `8F` |
| GAP-8E-06 | Q2 | `evidence_not_materialized` | high | `candidate_evidence_total=12`, `admissible_evidence_count=0` | `L2_evidence_trace.json`, `8D` |
| GAP-8E-07 | Q2, Q3 | `admissibility_reject_not_due_to_data` | high | Существенный reject из-за gate mismatch (`wrong_period`, `wrong_domain`, `wrong_account_scope`, `weak_source_mapping`) | `admissibility_reject_breakdown_live.json` |
| GAP-8E-08 | Q3 | `answer_layer_underuses_available_evidence` | medium | При `admissible_evidence_count=19` ответ остается limited из-за `claim_anchor_coverage_insufficient` | `L3_evidence_trace.json`, `8C` |
## Question-specific final failure map
### Q1 VAT
- Что надо доказать: полнота НДС-цепочки document -> posting -> tax/register -> book.
- Что найдено: admissible evidence есть (`12`), кейс grounded-positive.
- Где остается риск: высокий reject шум и generic live target.
- Типы разрыва: `wrong_live_call_target`, `admissibility_reject_not_due_to_data` (residual).
### Q2 RBP
- Что надо доказать: наличие/отсутствие хвоста РБП на конец июля по объектам.
- Что найдено: targeted checks `not_found`, admissible `0`.
- Где рвется: route + source coverage + evidence materialization.
- Типы разрыва: `source_not_connected_to_runtime`, `missing_source_data`, `wrong_route_selection`, `evidence_not_materialized`, `admissibility_reject_not_due_to_data`.
### Q3 Амортизация
- Что надо доказать: полный object-level охват амортизации за июль.
- Что найдено: admissible `19`, но eligibility blocked (`claim_anchor_coverage_insufficient`).
- Где рвется: entity mapping + claim anchor coverage + live target specificity.
- Типы разрыва: `wrong_entity_mapping`, `wrong_live_call_target`, `admissibility_reject_not_due_to_data`, `answer_layer_underuses_available_evidence`.
@@ -0,0 +1,42 @@
# 8F — Code Path Inventory
Дата: 2026-03-29
Scope: Stage 4 P0 source-to-proof аудит по 3 контрольным вопросам (VAT / RBP / FA)
## 1) Основной runtime path (end-to-end)
| Узел | Файл | Ключевые функции / точки | Роль в source-to-proof |
| --- | --- | --- | --- |
| Normalization | `x:\1C\NDC_1C\llm_normalizer\backend\src\services\normalizerService.ts` | `inferTimeScope` (360), `business_scope` extraction (428, 490), `normalize` (1036) | Разбивка вопроса, базовые anchors, route hint summary input. |
| Route decision | `x:\1C\NDC_1C\llm_normalizer\backend\src\services\routeHintAdapter.ts` | deterministic v2 summary (422, 458), route/no-route status (327-377) | Детальный выбор маршрута по fragment-ам. |
| Orchestration | `x:\1C\NDC_1C\llm_normalizer\backend\src\services\assistantService.ts` | `AssistantService` (1338), вызовы guard/claim/data/composer (1397-1536), debug export (1588-1734) | Склейка всех стадий: normalize → route → fetch → evidence gates → answer mode. |
| Snapshot/Live fetch | `x:\1C\NDC_1C\llm_normalizer\backend\src\services\assistantDataLayer.ts` | `executeRouteRuntime` (2522), `executeHybrid` (2893), `executeCanonical` (3683), `executeRisk` (3352), `ensureData` (2856), MCP query template (140, 2591-2592) | Реальный data access к snapshot + live MCP overlay. |
| Claim anchors | `x:\1C\NDC_1C\llm_normalizer\backend\src\services\assistantClaimBoundEvidence.ts` | `resolveClaimBoundAnchors` (201), `requiredChecksByClaim` (305), `applyTargetedEvidenceAcquisition` (540) | Claim-type и required checks для P0 цепочек доказательства. |
| Temporal/polarity/admissibility | `x:\1C\NDC_1C\llm_normalizer\backend\src\services\assistantRuntimeGuards.ts` | `resolveTemporalGuard` (633), `resolveDomainPolarityGuard` (754), `applyDomainPolarityGuardToRetrievalResults` (934), `applyEvidenceAdmissibilityGate` (1273), `evaluateGroundedAnswerEligibility` (1421) | Где candidate evidence отсекается и где решается eligibility. |
| Evidence normalization | `x:\1C\NDC_1C\llm_normalizer\backend\src\services\retrievalResultNormalizer.ts` | `normalizeEvidenceItems` (457), `normalizeRetrievalResult` (508) | Перевод raw fetch в unified evidence/candidate structure. |
| Final answer mode | `x:\1C\NDC_1C\llm_normalizer\backend\src\services\answerComposer.ts` | `composeAssistantAnswer` (4451), `partial_coverage` ветки (1833, 4531, 4557), `coverage_partial_or_limited` (2908, 4368) | Финальное решение grounded vs partial и what/why/limitations слой. |
## 2) Source inventory wiring (факт подключения)
`assistantDataLayer.ensureData` (2856+) подключает только:
- `03_snapshot_fragment_problem_cases.json`
- `04_samples_SpisanieSRaschetnogoScheta.json`
- `05_samples_RealizaciyaTovarovUslug.json`
- `06_samples_PostuplenieTovarovUslug.json`
- `07_samples_DocumentJournals.json`
- `08_samples_NDS_registers.json`
- `09_samples_key_fields_Recorder_Ref_Supplier_Buyer_Responsible.json`
Root для этих файлов: `ARCH_EXPORT_2020_DIR` из `config.ts` (`x:\1C\NDC_1C\docs\ARCH\2020экспорт`).
## 3) Live MCP wiring (факт вызова)
- В `assistantDataLayer.ts` live overlay строится через `/api/execute_query`.
- Текущий live query template (`MCP_LIVE_MOVEMENTS_QUERY_TEMPLATE`, строка 140+) — generic запрос в `РегистрБухгалтерии.Хозрасчетный` с `TOP __LIMIT__`, без claim-specific фильтров по document/object/account.
- Значит live path есть, но он broad и требует сильного post-filter/admissibility, иначе evidence не доезжает до proof.
## 4) Где формально рвется source-to-proof
- Q2 (RBP): разрыв раньше live path (route в canonical, live call не выбран), далее admissibility обнуляет evidence.
- Q3 (FA): source/evidence есть, но claim anchor coverage и entity mapping не дотягивают до eligibility.
- Q1 (VAT): positive path работает, но с высоким объемом reject (`weak_source_mapping`, `wrong_account_scope`).
@@ -0,0 +1,44 @@
# Chat Export (3 Control Questions)
Источник replay: `x:\1C\NDC_1C\docs\ARCH\8_audit_artifacts\question_replay_logs\1_live_replay.txt`
## Q1 — НДС (L1)
**User question**
13 июля проведено поступление товаров, а 15 июля — реализация этих же мебельных позиций. НДС-цепочка по этим движениям у нас полная или где-то есть выпадение между документом, проводкой и налоговым отражением?
**Runtime outcome**
- `reply_type`: `factual_with_explanation`
- `claim_type`: `prove_vat_chain_completeness`
- `grounding_mode`: `grounded_positive`
- `admissible_evidence_count`: `12`
- `eligibility_outcome`: `grounded_allowed`
- `reason_codes`: none
## Q2 — РБП (L2)
**User question**
31 июля прошло «Списание РБП за Июль 2020 г.», в том числе на 5 000 и ещё несколько сумм. Есть ли в базе признаки, что часть РБП к концу июля всё ещё живёт дольше ожидаемого?
**Runtime outcome**
- `reply_type`: `partial_coverage`
- `claim_type`: `prove_rbp_tail_state`
- `grounding_mode`: `limited_or_insufficient_evidence`
- `admissible_evidence_count`: `0`
- `eligibility_outcome`: `limited_or_insufficient_evidence`
- `reason_codes`: `admissible_evidence_count_zero`, `targeted_evidence_hit_rate_zero`
## Q3 — Амортизация (L3)
**User question**
31 июля начислена амортизация тремя суммами — 2 471,52, 2 465,28 и 849,83. Это похоже на полное начисление по всем нужным объектам за июль или есть риск, что какой-то объект ОС в июле не попал в амортизацию?
**Runtime outcome**
- `reply_type`: `partial_coverage`
- `claim_type`: `prove_month_close_state`
- `grounding_mode`: `limited_or_insufficient_evidence`
- `admissible_evidence_count`: `19`
- `eligibility_outcome`: `limited_or_insufficient_evidence`
- `reason_codes`: `claim_anchor_coverage_insufficient`
## Быстрый итог по 3 вопросам
- 1 кейс вышел в `grounded_positive`.
- 2 кейса остались в `partial_coverage`.
- Основные причины ограничения: `admissible_evidence_count_zero` (Q2) и `claim_anchor_coverage_insufficient` (Q3).
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,127 @@
{
"case_id": "L1",
"label": "vat_chain_furniture_13_15_july",
"reply_type": "factual_with_explanation",
"question_type_class": "which_chains_are_complete_vs_incomplete",
"claim_type": "prove_vat_chain_completeness",
"claim_anchor_audit": {
"required_anchors": [
"period",
"document_types",
"vat_signal",
"chain_signal"
],
"missing_anchors": [
"document_types"
],
"claim_anchor_resolution_rate": 0.75,
"primary_period": {
"from": "2020-07-13",
"to": "2020-07-13",
"granularity": "day"
},
"allowed_context_window": {
"from": "2019-07-14",
"to": "2021-07-13",
"granularity": "month"
}
},
"targeted_evidence_acquisition": {
"required_checks": [
"source_document_found",
"invoice_found",
"tax_register_entry_found",
"book_entry_found",
"chain_linkage_status"
],
"check_status": {
"source_document_found": "found",
"invoice_found": "found",
"tax_register_entry_found": "found",
"book_entry_found": "found",
"chain_linkage_status": "found"
},
"targeted_item_hits": 20,
"targeted_evidence_hits": 30,
"targeted_evidence_hit_rate": 1
},
"evidence_gate": {
"candidate_evidence_total": 30,
"admissible_evidence_count": 12,
"rejected_evidence_count": 18,
"reject_breakdown": {
"wrong_period": 0,
"wrong_domain": 0,
"wrong_account_scope": 12,
"weak_source_mapping": 14,
"zero_live_match": 0,
"future_dated_or_out_of_window": 0
},
"category_breakdown": {
"hard_evidence": 4,
"supporting_signal": 8,
"inadmissible_noise": 18
}
},
"eligibility": {
"eligible": true,
"grounding_mode": "grounded_positive",
"outcome": "grounded_allowed",
"reason_codes": [
],
"temporal_passed": true,
"business_scope_passed": true,
"claim_anchors_passed": true,
"admissible_evidence_count": 12
},
"temporal": {
"raw_time_scope": "2020-07",
"resolved_time_anchor": "2020-07-13",
"effective_primary_period": {
"from": "2020-07-13",
"to": "2020-07-13",
"granularity": "day"
},
"temporal_alignment_status": "corrected",
"temporal_guard_basis": "resolved_primary_period",
"temporal_guard_outcome": "passed",
"eligibility_time_basis": "resolved_primary_period"
},
"business_scope": {
"raw": [
"company_specific_accounting"
],
"resolved": [
"company_specific_accounting"
],
"company_scope_resolution_reason": [
"temporal_claim_bound_company_scope_recovery"
]
},
"numeric_anchor_cleanup": {
"raw_numeric_tokens": [
"13",
"15"
],
"classified_numeric_tokens": [
{
"token": "13",
"classification": "account_token"
},
{
"token": "15",
"classification": "other_numeric"
}
],
"rejected_as_non_accounts": [
"15"
],
"resolved_account_anchors": [
"13"
],
"normalized_primary_accounts": [
null
]
}
}
@@ -0,0 +1,121 @@
{
"case_id": "L2",
"label": "rbp_tail_31_july_5000",
"reply_type": "partial_coverage",
"question_type_class": "what_is_it_grounded_on",
"claim_type": "prove_rbp_tail_state",
"claim_anchor_audit": {
"required_anchors": [
"period",
"rbp_signal",
"writeoff_signal"
],
"missing_anchors": [
],
"claim_anchor_resolution_rate": 1,
"primary_period": {
"from": "2020-07-31",
"to": "2020-07-31",
"granularity": "day"
},
"allowed_context_window": {
"from": "2019-08-01",
"to": "2021-07-31",
"granularity": "month"
}
},
"targeted_evidence_acquisition": {
"required_checks": [
"rbp_writeoff_lifecycle_confirmed",
"residual_tail_found",
"close_contradiction_or_normal_residual"
],
"check_status": {
"rbp_writeoff_lifecycle_confirmed": "not_found",
"residual_tail_found": "not_found",
"close_contradiction_or_normal_residual": "not_found"
},
"targeted_item_hits": 0,
"targeted_evidence_hits": 0,
"targeted_evidence_hit_rate": 0
},
"evidence_gate": {
"candidate_evidence_total": 12,
"admissible_evidence_count": 0,
"rejected_evidence_count": 12,
"reject_breakdown": {
"wrong_period": 36,
"wrong_domain": 0,
"wrong_account_scope": 0,
"weak_source_mapping": 12,
"zero_live_match": 0,
"future_dated_or_out_of_window": 0
},
"category_breakdown": {
"hard_evidence": 0,
"supporting_signal": 0,
"inadmissible_noise": 12
}
},
"eligibility": {
"eligible": false,
"grounding_mode": "limited_or_insufficient_evidence",
"outcome": "limited_or_insufficient_evidence",
"reason_codes": [
"admissible_evidence_count_zero",
"targeted_evidence_hit_rate_zero"
],
"temporal_passed": true,
"business_scope_passed": true,
"claim_anchors_passed": true,
"admissible_evidence_count": 0
},
"temporal": {
"raw_time_scope": "2020-07",
"resolved_time_anchor": "2020-07-31",
"effective_primary_period": {
"from": "2020-07-31",
"to": "2020-07-31",
"granularity": "day"
},
"temporal_alignment_status": "corrected",
"temporal_guard_basis": "resolved_primary_period",
"temporal_guard_outcome": "passed",
"eligibility_time_basis": "resolved_primary_period"
},
"business_scope": {
"raw": [
"company_specific_accounting"
],
"resolved": [
"company_specific_accounting"
],
"company_scope_resolution_reason": [
"temporal_claim_bound_company_scope_recovery"
]
},
"numeric_anchor_cleanup": {
"raw_numeric_tokens": [
"31",
"2020",
"5",
"000"
],
"classified_numeric_tokens": [
{
"token": "31",
"classification": "other_numeric"
}
],
"rejected_as_non_accounts": [
"31"
],
"resolved_account_anchors": [
],
"normalized_primary_accounts": [
null
]
}
}
@@ -0,0 +1,137 @@
{
"case_id": "L3",
"label": "fa_amortization_2471_2465_849",
"reply_type": "partial_coverage",
"question_type_class": "why_breaks",
"claim_type": "prove_month_close_state",
"claim_anchor_audit": {
"required_anchors": [
"period",
"close_signal",
"cost_scope"
],
"missing_anchors": [
"close_signal",
"cost_scope"
],
"claim_anchor_resolution_rate": 0.3333,
"primary_period": {
"from": "2020-07-31",
"to": "2020-07-31",
"granularity": "day"
},
"allowed_context_window": {
"from": "2019-08-01",
"to": "2021-07-31",
"granularity": "month"
}
},
"targeted_evidence_acquisition": {
"required_checks": [
"close_operation_found",
"distribution_step_found",
"residual_tail_found"
],
"check_status": {
"close_operation_found": "found",
"distribution_step_found": "found",
"residual_tail_found": "found"
},
"targeted_item_hits": 23,
"targeted_evidence_hits": 35,
"targeted_evidence_hit_rate": 1
},
"evidence_gate": {
"candidate_evidence_total": 35,
"admissible_evidence_count": 19,
"rejected_evidence_count": 16,
"reject_breakdown": {
"wrong_period": 0,
"wrong_domain": 8,
"wrong_account_scope": 8,
"weak_source_mapping": 16,
"zero_live_match": 0,
"future_dated_or_out_of_window": 8
},
"category_breakdown": {
"hard_evidence": 8,
"supporting_signal": 11,
"inadmissible_noise": 16
}
},
"eligibility": {
"eligible": false,
"grounding_mode": "limited_or_insufficient_evidence",
"outcome": "limited_or_insufficient_evidence",
"reason_codes": [
"claim_anchor_coverage_insufficient"
],
"temporal_passed": true,
"business_scope_passed": true,
"claim_anchors_passed": false,
"admissible_evidence_count": 19
},
"temporal": {
"raw_time_scope": "2020-07",
"resolved_time_anchor": "2020-07-31",
"effective_primary_period": {
"from": "2020-07-31",
"to": "2020-07-31",
"granularity": "day"
},
"temporal_alignment_status": "corrected",
"temporal_guard_basis": "resolved_primary_period",
"temporal_guard_outcome": "passed",
"eligibility_time_basis": "resolved_primary_period"
},
"business_scope": {
"raw": [
"company_specific_accounting"
],
"resolved": [
"company_specific_accounting"
],
"company_scope_resolution_reason": [
"temporal_claim_bound_company_scope_recovery"
]
},
"numeric_anchor_cleanup": {
"raw_numeric_tokens": [
"31",
"2",
"471,52",
"465,28",
"849,83"
],
"classified_numeric_tokens": [
{
"token": "31",
"classification": "other_numeric"
},
{
"token": "52",
"classification": "amount_token"
},
{
"token": "28",
"classification": "amount_token"
},
{
"token": "83",
"classification": "amount_token"
}
],
"rejected_as_non_accounts": [
"31",
"52",
"28",
"83"
],
"resolved_account_anchors": [
],
"normalized_primary_accounts": [
null
]
}
}
@@ -0,0 +1,49 @@
{
"generated_at": "2026-03-29T05:48:19.981Z",
"aggregate": {
"wrong_period": 36,
"wrong_domain": 8,
"wrong_account_scope": 20,
"weak_source_mapping": 42,
"zero_live_match": 0,
"future_dated_or_out_of_window": 8
},
"cases": [
{
"case_id": "L1",
"label": "vat_chain_furniture_13_15_july",
"reject_breakdown": {
"wrong_period": 0,
"wrong_domain": 0,
"wrong_account_scope": 12,
"weak_source_mapping": 14,
"zero_live_match": 0,
"future_dated_or_out_of_window": 0
}
},
{
"case_id": "L2",
"label": "rbp_tail_31_july_5000",
"reject_breakdown": {
"wrong_period": 36,
"wrong_domain": 0,
"wrong_account_scope": 0,
"weak_source_mapping": 12,
"zero_live_match": 0,
"future_dated_or_out_of_window": 0
}
},
{
"case_id": "L3",
"label": "fa_amortization_2471_2465_849",
"reject_breakdown": {
"wrong_period": 0,
"wrong_domain": 8,
"wrong_account_scope": 8,
"weak_source_mapping": 16,
"zero_live_match": 0,
"future_dated_or_out_of_window": 8
}
}
]
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,7 @@
# Live Case Matrix
| Case | Label | Reply | Claim Type | Admissible Evidence | Grounding Mode | Scope | Temporal |
| --- | --- | --- | --- | ---: | --- | --- | --- |
| L1 | vat_chain_furniture_13_15_july | factual_with_explanation | prove_vat_chain_completeness | 12 | grounded_positive | company_specific_accounting | passed |
| L2 | rbp_tail_31_july_5000 | partial_coverage | prove_rbp_tail_state | 0 | limited_or_insufficient_evidence | company_specific_accounting | passed |
| L3 | fa_amortization_2471_2465_849 | partial_coverage | prove_month_close_state | 19 | limited_or_insufficient_evidence | company_specific_accounting | passed |
@@ -0,0 +1,23 @@
# Live Replay Report (Wave 19.2)
## Source
- Source of truth replayed from: `X:\1C\NDC_1C\llm_normalizer\docs\runs\2026-03-29_Stage_04_Wave_19_1_Live_Alignment_Fix_Claim_Bound_Runtime\1.txt`
- Replayed exactly 3 user turns from the original export.
- Runtime path: MCP ON, useMock=true.
## Metrics
- live_temporal_contradiction_rate: 0
- live_company_scope_resolution_rate: 1
- live_admissible_evidence_nonzero_rate: 0.6667
- live_partial_coverage_default_rate: 0.6667
- baseline_partial_coverage_default_rate: 1
- live_claim_path_completion_rate: 0.6667
- live_false_grounded_answer_rate: 0
## Verdict
- LIVE_TEMPORAL_ALIGNMENT_FIXED: FIXED
- LIVE_COMPANY_SCOPE_FIXED: FIXED
- LIVE_EVIDENCE_PATH_FIXED: FIXED
- LIVE_PARTIAL_COVERAGE_DEFAULT_REDUCED: REDUCED
- Overall: WAVE19_2_ACCEPTED
@@ -0,0 +1,6 @@
{
"generated_at": "2026-03-29T09:22:59",
"cases": [
]
}
@@ -0,0 +1,36 @@
{
"run_id": "2026-03-29_Stage_04_Wave_19_2_Live_Runtime_Fix_Replay_1txt",
"stage": "Stage_04",
"wave": "Wave_19_2",
"scope": "live_runtime_fix_by_replay_1txt",
"source_of_truth": "X:\\1C\\NDC_1C\\llm_normalizer\\docs\\runs\\2026-03-29_Stage_04_Wave_19_1_Live_Alignment_Fix_Claim_Bound_Runtime\\1.txt",
"execution": {
"replay_mode": "exact_questions_from_1_txt",
"runtime_path": "assistant_message_with_mcp_runtime_on",
"normalizer_mode": "useMock=true",
"session_id": "asst-wave19_2-1774763298275"
},
"thresholds": {
"live_temporal_contradiction_rate": 0,
"live_company_scope_resolution_rate": 1,
"live_false_grounded_answer_rate": 0,
"live_admissible_evidence_nonzero_min_cases": 2
},
"metrics": {
"case_count": 3,
"baseline_partial_coverage_default_rate": 1,
"live_temporal_contradiction_rate": 0,
"live_company_scope_resolution_rate": 1,
"live_admissible_evidence_nonzero_rate": 0.6667,
"live_partial_coverage_default_rate": 0.6667,
"live_claim_path_completion_rate": 0.6667,
"live_false_grounded_answer_rate": 0
},
"verdicts": {
"LIVE_TEMPORAL_ALIGNMENT_FIXED": "FIXED",
"LIVE_COMPANY_SCOPE_FIXED": "FIXED",
"LIVE_EVIDENCE_PATH_FIXED": "FIXED",
"LIVE_PARTIAL_COVERAGE_DEFAULT_REDUCED": "REDUCED",
"overall_status": "WAVE19_2_ACCEPTED"
}
}
@@ -0,0 +1,38 @@
[
{
"case_id": "L1",
"label": "vat_chain_furniture_13_15_july",
"claim_type": "prove_vat_chain_completeness",
"routes": "store_canonical -\u003e hybrid_store_plus_live",
"business_scope_resolved": "company_specific_accounting",
"resolved_time_anchor": "2020-07-13",
"temporal_guard_outcome": "passed",
"admissible_evidence_count": 12,
"grounding_mode": "grounded_positive",
"reply_type": "factual_with_explanation"
},
{
"case_id": "L2",
"label": "rbp_tail_31_july_5000",
"claim_type": "prove_rbp_tail_state",
"routes": "store_canonical -\u003e store_canonical",
"business_scope_resolved": "company_specific_accounting",
"resolved_time_anchor": "2020-07-31",
"temporal_guard_outcome": "passed",
"admissible_evidence_count": 0,
"grounding_mode": "limited_or_insufficient_evidence",
"reply_type": "partial_coverage"
},
{
"case_id": "L3",
"label": "fa_amortization_2471_2465_849",
"claim_type": "prove_month_close_state",
"routes": "store_feature_risk -\u003e hybrid_store_plus_live",
"business_scope_resolved": "company_specific_accounting",
"resolved_time_anchor": "2020-07-31",
"temporal_guard_outcome": "passed",
"admissible_evidence_count": 19,
"grounding_mode": "limited_or_insufficient_evidence",
"reply_type": "partial_coverage"
}
]
@@ -0,0 +1,36 @@
[
{
"case_id": "L1",
"label": "vat_chain_furniture_13_15_july",
"claim_type": "prove_vat_chain_completeness",
"method": "execute_query",
"route": "hybrid_store_plus_live",
"query_subject": "vat_chain_conflict",
"fetched_rows": 24,
"returned_rows": 12,
"matched_rows": 24,
"account_scope": "",
"status": "ok",
"admissible_evidence_count": 12,
"used_for_grounding": true,
"eligibility_outcome": "grounded_allowed",
"eligibility_mode": "grounded_positive"
},
{
"case_id": "L3",
"label": "fa_amortization_2471_2465_849",
"claim_type": "prove_month_close_state",
"method": "execute_query",
"route": "hybrid_store_plus_live",
"query_subject": "fixed_asset_card_mismatch",
"fetched_rows": 24,
"returned_rows": 12,
"matched_rows": 24,
"account_scope": "",
"status": "ok",
"admissible_evidence_count": 19,
"used_for_grounding": true,
"eligibility_outcome": "limited_or_insufficient_evidence",
"eligibility_mode": "limited_or_insufficient_evidence"
}
]
@@ -0,0 +1,62 @@
[
{
"case_id": "L1",
"label": "vat_chain_furniture_13_15_july",
"claim_type": "prove_vat_chain_completeness",
"call_present": true,
"method": "execute_query",
"route": "hybrid_store_plus_live",
"args_summary": null,
"query_subject": "vat_chain_conflict",
"fetched_rows": 24,
"returned_rows": 12,
"matched_rows": 24,
"account_scope": "",
"used_for_admissible_evidence": true,
"rejection_or_block_reason": "n/a",
"admissible_evidence_count": 12,
"eligibility_outcome": "grounded_allowed",
"eligibility_mode": "grounded_positive",
"eligibility_reason_codes": ""
},
{
"case_id": "L2",
"label": "rbp_tail_31_july_5000",
"claim_type": "prove_rbp_tail_state",
"call_present": false,
"method": null,
"route": null,
"args_summary": null,
"query_subject": null,
"fetched_rows": 0,
"returned_rows": 0,
"matched_rows": 0,
"account_scope": "",
"used_for_admissible_evidence": false,
"rejection_or_block_reason": "no_live_call_selected_for_case",
"admissible_evidence_count": 0,
"eligibility_outcome": "limited_or_insufficient_evidence",
"eligibility_mode": "limited_or_insufficient_evidence",
"eligibility_reason_codes": "admissible_evidence_count_zero,targeted_evidence_hit_rate_zero"
},
{
"case_id": "L3",
"label": "fa_amortization_2471_2465_849",
"claim_type": "prove_month_close_state",
"call_present": true,
"method": "execute_query",
"route": "hybrid_store_plus_live",
"args_summary": null,
"query_subject": "fixed_asset_card_mismatch",
"fetched_rows": 24,
"returned_rows": 12,
"matched_rows": 24,
"account_scope": "",
"used_for_admissible_evidence": true,
"rejection_or_block_reason": "n/a",
"admissible_evidence_count": 19,
"eligibility_outcome": "limited_or_insufficient_evidence",
"eligibility_mode": "limited_or_insufficient_evidence",
"eligibility_reason_codes": "claim_anchor_coverage_insufficient"
}
]
@@ -0,0 +1,78 @@
{
"generated_at": "2026-03-29T05:48:19.981Z",
"cases": [
{
"case_id": "L1",
"label": "vat_chain_furniture_13_15_july",
"claim_type": "prove_vat_chain_completeness",
"admissible_evidence_count": 12,
"live_calls": [
{
"fragment_id": "F2",
"route": "hybrid_store_plus_live",
"method": "execute_query",
"args_summary": null,
"query_subject": "vat_chain_conflict",
"account_scope": [],
"fetched_rows": 24,
"returned_rows": 12,
"matched_rows": 24,
"status": "ok"
}
],
"claim_targeted_hit_rate": 1,
"eligibility": {
"eligible": true,
"grounding_mode": "grounded_positive",
"outcome": "grounded_allowed",
"reason_codes": []
}
},
{
"case_id": "L2",
"label": "rbp_tail_31_july_5000",
"claim_type": "prove_rbp_tail_state",
"admissible_evidence_count": 0,
"live_calls": [],
"claim_targeted_hit_rate": 0,
"eligibility": {
"eligible": false,
"grounding_mode": "limited_or_insufficient_evidence",
"outcome": "limited_or_insufficient_evidence",
"reason_codes": [
"admissible_evidence_count_zero",
"targeted_evidence_hit_rate_zero"
]
}
},
{
"case_id": "L3",
"label": "fa_amortization_2471_2465_849",
"claim_type": "prove_month_close_state",
"admissible_evidence_count": 19,
"live_calls": [
{
"fragment_id": "F2",
"route": "hybrid_store_plus_live",
"method": "execute_query",
"args_summary": null,
"query_subject": "fixed_asset_card_mismatch",
"account_scope": [],
"fetched_rows": 24,
"returned_rows": 12,
"matched_rows": 24,
"status": "ok"
}
],
"claim_targeted_hit_rate": 1,
"eligibility": {
"eligible": false,
"grounding_mode": "limited_or_insufficient_evidence",
"outcome": "limited_or_insufficient_evidence",
"reason_codes": [
"claim_anchor_coverage_insufficient"
]
}
}
]
}
@@ -0,0 +1,26 @@
# 2020 экспорт: состав выгрузки
Папка собрана автоматически для ручного анализа текущего состояния.
## Файлы
1. `01_ontology_mapping_layer.md` — текущая онтология/мэппинг и метрики среза.
2. `02_canonical_relation_rules.md` — правила построения canonical relations.
3. `03_snapshot_fragment_problem_cases.json` — проблемный фрагмент snapshot июня 2020.
4. `04_samples_SpisanieSRaschetnogoScheta.json` — реальные записи по `СписаниеСРасчетногоСчета`.
5. `05_samples_RealizaciyaTovarovUslug.json` — реальные записи по `РеализацияТоваровУслуг`.
6. `06_samples_PostuplenieTovarovUslug.json` — реальные записи по `ПоступлениеТоваровУслуг`.
7. `07_samples_DocumentJournals.json` — реальные записи по журналам документов.
8. `08_samples_NDS_registers.json` — реальные записи по НДС-регистрам.
9. `09_samples_key_fields_Recorder_Ref_Supplier_Buyer_Responsible.json` — записи с ключевыми полями.
## Ключевые поля: фактическая встречаемость в snapshot
| field | count |
| --- | --- |
| Ответственный_Key | 187 |
| Ref | 168 |
| Recorder | 147 |
| Ref_Key | 93 |
| Поставщик_Key | 78 |
| Покупатель_Key | 46 |
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,112 @@
{
"status": "success",
"snapshot_mode": "cumulative_asof",
"year": 2020,
"service_root": "http://localhost/buh_test/odata/standard.odata/",
"entity_sets_total": 2,
"entity_sets": [
"AccountingRegister_Хозрасчетный",
"AccountingRegister_Хозрасчетный_RecordType"
],
"page_size": 500,
"max_pages_per_set": 0,
"include_undated": "all_months",
"truncated_entity_sets_count": 0,
"truncated_entity_sets": [],
"records_exported_per_month": {
"2020-01": 15113,
"2020-02": 15211,
"2020-03": 15508,
"2020-04": 15578,
"2020-05": 15648,
"2020-06": 15953,
"2020-07": 16039,
"2020-08": 16122,
"2020-09": 16175,
"2020-10": 16212,
"2020-11": 16308,
"2020-12": 16377
},
"dated_records_exported_per_month": {
"2020-01": 10644,
"2020-02": 10742,
"2020-03": 11039,
"2020-04": 11109,
"2020-05": 11179,
"2020-06": 11484,
"2020-07": 11570,
"2020-08": 11653,
"2020-09": 11706,
"2020-10": 11743,
"2020-11": 11839,
"2020-12": 11908
},
"undated_records_exported_per_month": {
"2020-01": 4469,
"2020-02": 4469,
"2020-03": 4469,
"2020-04": 4469,
"2020-05": 4469,
"2020-06": 4469,
"2020-07": 4469,
"2020-08": 4469,
"2020-09": 4469,
"2020-10": 4469,
"2020-11": 4469,
"2020-12": 4469
},
"links_exported_per_month": {
"2020-01": 47623,
"2020-02": 48036,
"2020-03": 49243,
"2020-04": 49539,
"2020-05": 49834,
"2020-06": 51071,
"2020-07": 51426,
"2020-08": 51769,
"2020-09": 51992,
"2020-10": 52151,
"2020-11": 52545,
"2020-12": 52831
},
"entity_set_stats": {
"AccountingRegister_Хозрасчетный": {
"pages_read": 9,
"records_scanned": 4469,
"records_mapped": 4469,
"records_with_datetime": 0,
"records_without_datetime": 4469,
"records_skipped_future": 0,
"mapping_errors": 0,
"truncated_by_limit_or_guard": false,
"pagination_repeat_guard_hits": 0
},
"AccountingRegister_Хозрасчетный_RecordType": {
"pages_read": 26,
"records_scanned": 12659,
"records_mapped": 11908,
"records_with_datetime": 12659,
"records_without_datetime": 0,
"records_skipped_future": 751,
"mapping_errors": 0,
"truncated_by_limit_or_guard": false,
"pagination_repeat_guard_hits": 0
}
},
"snapshot_files": [
"snapshot_2020-01_asof_full.json",
"snapshot_2020-02_asof_full.json",
"snapshot_2020-03_asof_full.json",
"snapshot_2020-04_asof_full.json",
"snapshot_2020-05_asof_full.json",
"snapshot_2020-06_asof_full.json",
"snapshot_2020-07_asof_full.json",
"snapshot_2020-08_asof_full.json",
"snapshot_2020-09_asof_full.json",
"snapshot_2020-10_asof_full.json",
"snapshot_2020-11_asof_full.json",
"snapshot_2020-12_asof_full.json"
],
"output_dir": "x:\\1C\\NDC_1C\\docs\\ARCH\\2020_monthly_company_asof_full_test",
"generated_at_utc": "2026-03-27T21:34:24.669751+00:00"
}
@@ -0,0 +1,219 @@
{
"runtime_snapshot_root": "x:\\1C\\NDC_1C\\docs\\ARCH\\2020экспорт",
"runtime_snapshot_files": [
{
"file": "03_snapshot_fragment_problem_cases.json",
"path": "x:\\1C\\NDC_1C\\docs\\ARCH\\2020экспорт\\03_snapshot_fragment_problem_cases.json",
"size_bytes": 302520,
"records_count": 80,
"top_entities": [
{
"entity": "AccumulationRegister_НДСЗаписиКнигиПродаж_RecordType",
"count": 46
},
{
"entity": "AccumulationRegister_НДСПредъявленный_RecordType",
"count": 21
},
{
"entity": "AccumulationRegister_НДСЗаписиКнигиПокупок_RecordType",
"count": 12
},
{
"entity": "AccumulationRegister_НДСВключенныйВСтоимость_RecordType",
"count": 1
}
]
},
{
"file": "04_samples_SpisanieSRaschetnogoScheta.json",
"path": "x:\\1C\\NDC_1C\\docs\\ARCH\\2020экспорт\\04_samples_SpisanieSRaschetnogoScheta.json",
"size_bytes": 196178,
"records_count": 27,
"top_entities": {
"entity": "Document_СписаниеСРасчетногоСчета",
"count": 27
}
},
{
"file": "05_samples_RealizaciyaTovarovUslug.json",
"path": "x:\\1C\\NDC_1C\\docs\\ARCH\\2020экспорт\\05_samples_RealizaciyaTovarovUslug.json",
"size_bytes": 132032,
"records_count": 5,
"top_entities": {
"entity": "Document_РеализацияТоваровУслуг",
"count": 5
}
},
{
"file": "06_samples_PostuplenieTovarovUslug.json",
"path": "x:\\1C\\NDC_1C\\docs\\ARCH\\2020экспорт\\06_samples_PostuplenieTovarovUslug.json",
"size_bytes": 181409,
"records_count": 10,
"top_entities": {
"entity": "Document_ПоступлениеТоваровУслуг",
"count": 10
}
},
{
"file": "07_samples_DocumentJournals.json",
"path": "x:\\1C\\NDC_1C\\docs\\ARCH\\2020экспорт\\07_samples_DocumentJournals.json",
"size_bytes": 292931,
"records_count": 80,
"top_entities": [
{
"entity": "DocumentJournal_ДокументыПоставщиков",
"count": 30
},
{
"entity": "DocumentJournal_БанковскиеВыписки",
"count": 30
},
{
"entity": "DocumentJournal_ДокументыПокупателей",
"count": 16
},
{
"entity": "DocumentJournal_ДокументыПоУчетуЗарплаты",
"count": 4
}
]
},
{
"file": "08_samples_NDS_registers.json",
"path": "x:\\1C\\NDC_1C\\docs\\ARCH\\2020экспорт\\08_samples_NDS_registers.json",
"size_bytes": 291547,
"records_count": 80,
"top_entities": [
{
"entity": "AccumulationRegister_НДСЗаписиКнигиПродаж_RecordType",
"count": 46
},
{
"entity": "AccumulationRegister_НДСПредъявленный_RecordType",
"count": 21
},
{
"entity": "AccumulationRegister_НДСЗаписиКнигиПокупок_RecordType",
"count": 12
},
{
"entity": "AccumulationRegister_НДСВключенныйВСтоимость_RecordType",
"count": 1
}
]
},
{
"file": "09_samples_key_fields_Recorder_Ref_Supplier_Buyer_Responsible.json",
"path": "x:\\1C\\NDC_1C\\docs\\ARCH\\2020экспорт\\09_samples_key_fields_Recorder_Ref_Supplier_Buyer_Responsible.json",
"size_bytes": 511133,
"records_count": 140,
"top_entities": [
{
"entity": "AccumulationRegister_НДСПредъявленный_RecordType",
"count": 65
},
{
"entity": "AccumulationRegister_НДСЗаписиКнигиПродаж_RecordType",
"count": 46
},
{
"entity": "AccumulationRegister_НДСЗаписиКнигиПокупок_RecordType",
"count": 12
},
{
"entity": "AccumulationRegister_НДФЛРасчетыСБюджетом_RecordType",
"count": 7
},
{
"entity": "AccumulationRegister_НДФЛСведенияОДоходах_RecordType",
"count": 3
},
{
"entity": "AccumulationRegister_РасчетыНалоговыхАгентовСБюджетомПоНДФЛ_RecordType",
"count": 3
},
{
"entity": "AccumulationRegister_РеализацияУслуг_RecordType",
"count": 1
},
{
"entity": "AccumulationRegister_РасчетыПоСтраховымВзносам_RecordType",
"count": 1
},
{
"entity": "AccumulationRegister_НДСВключенныйВСтоимость_RecordType",
"count": 1
},
{
"entity": "AccumulationRegister_НДФЛПредоставленныеСтандартныеВычетыФизЛиц_RecordType",
"count": 1
}
]
}
],
"monthly_asof_root": "x:\\1C\\NDC_1C\\docs\\ARCH\\2020_monthly_company_asof_full_test",
"monthly_asof_files": [
{
"Name": "snapshot_2020-01_asof_full.json",
"Length": 42476280,
"FullName": "X:\\1C\\NDC_1C\\docs\\ARCH\\2020_monthly_company_asof_full_test\\snapshot_2020-01_asof_full.json"
},
{
"Name": "snapshot_2020-02_asof_full.json",
"Length": 42750865,
"FullName": "X:\\1C\\NDC_1C\\docs\\ARCH\\2020_monthly_company_asof_full_test\\snapshot_2020-02_asof_full.json"
},
{
"Name": "snapshot_2020-03_asof_full.json",
"Length": 43556064,
"FullName": "X:\\1C\\NDC_1C\\docs\\ARCH\\2020_monthly_company_asof_full_test\\snapshot_2020-03_asof_full.json"
},
{
"Name": "snapshot_2020-04_asof_full.json",
"Length": 43751598,
"FullName": "X:\\1C\\NDC_1C\\docs\\ARCH\\2020_monthly_company_asof_full_test\\snapshot_2020-04_asof_full.json"
},
{
"Name": "snapshot_2020-05_asof_full.json",
"Length": 43947779,
"FullName": "X:\\1C\\NDC_1C\\docs\\ARCH\\2020_monthly_company_asof_full_test\\snapshot_2020-05_asof_full.json"
},
{
"Name": "snapshot_2020-06_asof_full.json",
"Length": 44770832,
"FullName": "X:\\1C\\NDC_1C\\docs\\ARCH\\2020_monthly_company_asof_full_test\\snapshot_2020-06_asof_full.json"
},
{
"Name": "snapshot_2020-07_asof_full.json",
"Length": 45007595,
"FullName": "X:\\1C\\NDC_1C\\docs\\ARCH\\2020_monthly_company_asof_full_test\\snapshot_2020-07_asof_full.json"
},
{
"Name": "snapshot_2020-08_asof_full.json",
"Length": 45237062,
"FullName": "X:\\1C\\NDC_1C\\docs\\ARCH\\2020_monthly_company_asof_full_test\\snapshot_2020-08_asof_full.json"
},
{
"Name": "snapshot_2020-09_asof_full.json",
"Length": 45384277,
"FullName": "X:\\1C\\NDC_1C\\docs\\ARCH\\2020_monthly_company_asof_full_test\\snapshot_2020-09_asof_full.json"
},
{
"Name": "snapshot_2020-10_asof_full.json",
"Length": 45489151,
"FullName": "X:\\1C\\NDC_1C\\docs\\ARCH\\2020_monthly_company_asof_full_test\\snapshot_2020-10_asof_full.json"
},
{
"Name": "snapshot_2020-11_asof_full.json",
"Length": 45753055,
"FullName": "X:\\1C\\NDC_1C\\docs\\ARCH\\2020_monthly_company_asof_full_test\\snapshot_2020-11_asof_full.json"
},
{
"Name": "snapshot_2020-12_asof_full.json",
"Length": 45942785,
"FullName": "X:\\1C\\NDC_1C\\docs\\ARCH\\2020_monthly_company_asof_full_test\\snapshot_2020-12_asof_full.json"
}
],
"monthly_manifest": "x:\\1C\\NDC_1C\\docs\\ARCH\\2020_monthly_company_asof_full_test\\manifest_2020_monthly_company_asof_full.json"
}
Binary file not shown.
@@ -0,0 +1,115 @@
# 9 — Разведка структуры 1С и связей через MCP по 3 контрольным вопросам (2026-03-29)
Статус: COMPLETED
Scope: Stage 4, P0-only, без redesign/proof-engine расширения
## 1) Цель и границы
Цель этой работы: не чинить wording, а построить доказательную карту:
`вопрос -> anchors -> route -> 1C entities/relations -> evidence -> answer`.
Source of truth:
1. Q1 VAT (13/15 июля, мебель).
2. Q2 RBP (31 июля, 5 000).
3. Q3 FA amortization (31 июля, 2 471,52 / 2 465,28 / 849,83).
## 2) Что использовано как фактическая база
- Replay/live артефакты: `docs/ARCH/9_audit_artifacts/replay_logs/*`
- Debug payloads 3 кейсов: `docs/ARCH/9_audit_artifacts/debug_payloads/*`
- Live inventory: `docs/ARCH/9_audit_artifacts/raw_live_calls/*`
- Snapshot probes: `docs/ARCH/9_audit_artifacts/snapshot_samples/*`
- Relation probes: `docs/ARCH/9_audit_artifacts/relation_probe_logs/*`
- Query recipes: `docs/ARCH/9_audit_artifacts/query_recipe_examples/*`
- Code paths: `assistantService`, `assistantDataLayer`, `assistantRuntimeGuards`, `assistantClaimBoundEvidence`, `answerComposer`
## 3) Узел A — обязательные сущности и переходы
Результат: см. `9A - entity_seed_map.md`.
Коротко:
- Q1 VAT: обязательна цепочка doc->invoice->register->book + posting consistency.
- Q2 RBP: обязательна object-level цепочка writeoff->movement->close->residual.
- Q3 FA: обязательна object-level цепочка FA object->depreciation->posting + expected-set coverage.
## 4) Узел B — snapshot vs live coverage
Результат: см. `9B - snapshot_vs_live_coverage_map.md`.
Жесткий verdict по coverage:
- Q1 VAT: `snapshot_plus_live_required`
- Q2 RBP: `data_not_reachable_in_current_runtime`
- Q3 FA: `snapshot_plus_live_required`
Причина:
- runtime использует ограниченный `2020экспорт` subset;
- live path сейчас broad (`execute_query` generic probe), без claim-specific call sequence;
- object-level reconstruction для RBP/FA не замыкается.
## 5) Узел C — relation map 1С
Результат: см. `9C - relation_map_1c_entities.md`.
Ключевой вывод:
- VAT relation skeleton в текущих источниках есть и partially operational.
- RBP/FA critical relations либо не реконструируются, либо не используются в current runtime path.
- На FA есть cross-domain contamination (нерелевантные связи попадают в evidence path).
## 6) Узел D — реальные MCP маршруты
Результат: см. `9D - mcp_query_recipes.md`.
Фактическая картина:
- Q1: есть live call, но это broad probe.
- Q2: live call отсутствует (route canonical-only).
- Q3: live call есть, но без object scope.
Missing layer:
- claim-bound call sequence per question (required_live_calls) отсутствует во всех 3 кейсах.
## 7) Узел E — source-to-proof failure map
Результат: см. `9E - source_to_proof_failure_map.md`.
### Локальные verdict
- Q1 VAT: `EVIDENCE_MATERIALIZATION_IS_PRIMARY_PROBLEM` (residual)
- Q2 RBP: `SOURCE_COVERAGE_IS_PRIMARY_PROBLEM` (в связке с route gap)
- Q3 FA: `ONE_C_RELATION_MAPPING_IS_PRIMARY_PROBLEM`
### Общий verdict
`MULTI_NODE_FAILURE_CONFIRMED`
## 8) Ответы на главные вопросы ТЗ
### По НДС
- Какие сущности нужны: docs receipt/sale, invoice links, VAT registers, postings, book linkage.
- Почему positive path уже работает: relation skeleton для VAT реально доступен.
- Где шум: broad live probe + admissibility reject шум.
### По РБП
- Есть ли данные в source: частично.
- Есть ли путь через MCP в current runtime: практически нет (route не включает live).
- Где рвется: `source coverage + route + relation reconstruction + evidence materialization`.
### По амортизации
- Какие объекты должны участвовать: полный expected set ОС за июль.
- Почему admissible evidence уже есть, но proof не закрывается: object-level relation/anchor closure не достигается.
- Где не хватает mapping/anchors: `wrong_entity_mapping` + `claim_anchor_coverage_insufficient`.
## 9) Что конкретно чинить следующим пакетом
1. Добавить claim-specific MCP call sequences per question (вместо generic live probe).
2. Закрыть object-level relation reconstruction для RBP и FA.
3. Подключить/использовать source layers, которые сейчас физически есть, но runtime не использует.
4. Снизить reject not-due-to-data в evidence gate.
## 10) Финальный результат разведки
Размытая формулировка «слабая доказательная база» снята.
Теперь зафиксировано:
- какие данные нужны;
- какие связи обязательны;
- какие вызовы MCP должны быть;
- где текущий runtime теряет source-to-proof контур;
- какие узлы приоритетно чинить следующим execution pack.
@@ -0,0 +1,95 @@
# 9A — Entity Seed Map
Дата: 2026-03-29
Scope: 3 контрольных вопроса (НДС / РБП / Амортизация)
## Q1 — НДС (13/15 июля, мебель)
### Seed entities
- Поступление товаров (документ, 13 июля)
- Реализация товаров (документ, 15 июля)
- Номенклатура/позиции мебели
- Контрагент + договор
- Счет-фактура
- НДС-регистры
- Проводки/движения по хозрасчетному регистру
### Required entities
- `Document_ПоступлениеТоваровУслуг`
- `Document_РеализацияТоваровУслуг`
- Invoice-linked document refs
- `AccumulationRegister_НДС*` records (purchase/sales/tax chain)
- Book-layer records (книга покупок/продаж в терминах register linkage)
- Posting/movement records for doc confirmation
### Expected transitions (обязательные)
1. `receipt_document -> invoice_link`
2. `sale_document -> invoice_link`
3. `invoice_link -> vat_register_entry`
4. `vat_register_entry -> book_entry`
5. `document -> posting`
6. `item_scope(receipt) -> item_scope(sale)`
### Смежный контур (не ядро claim)
- Currency/warehouse/responsible fields.
- Broad journal references без цепочки invoice/register/book.
## Q2 — РБП (31 июля, 5 000)
### Seed entities
- Документ списания РБП за июль
- Объект РБП
- Движения по счету/регистрам РБП
- Month-close записи
- Остаток на конец июля
### Required entities
- RBP write-off document record
- RBP object card / object identifier
- RBP movements (дебет/кредит/period bound)
- Month-close linked entries
- End-period residual per object
### Expected transitions (обязательные)
1. `rbp_writeoff_document -> rbp_object`
2. `rbp_object -> movement_records`
3. `movement_records -> month_close_entries`
4. `month_close_entries -> end_period_residual`
5. `end_period_residual -> normal_vs_tail_state`
### Смежный контур (не ядро claim)
- Общие bank/payment journals без object-level RBP linkage.
## Q3 — Амортизация (31 июля, 2 471,52 / 2 465,28 / 849,83)
### Seed entities
- Объекты ОС (expected set за июль)
- Документы начисления амортизации
- Проводки по амортизации
- Периодные month-close записи
- Суммовые anchors из вопроса
### Required entities
- FA object identifiers (expected set)
- Depreciation documents for July 2020
- Object-to-posting linkage
- Posting accounts relevant to depreciation flow
- Missed-object detector (object without depreciation row)
### Expected transitions (обязательные)
1. `fa_object -> depreciation_document`
2. `depreciation_document -> posting`
3. `posting -> account_scope(02/20/44 and linked)`
4. `expected_fa_set -> covered_fa_set`
5. `set_diff -> missing_object_risk`
### Смежный контур (не ядро claim)
- Non-FA tax/personnel registers (например NDFL-related records), если не дают object-level FA proof.
## Seed/required summary
| Question | Seed quality in current runtime | Required transitions fully reconstructable now |
| --- | --- | --- |
| Q1 VAT | medium-high | partial-high |
| Q2 RBP | low-medium | low |
| Q3 FA | medium | medium-low |
@@ -0,0 +1,53 @@
# 9B — Snapshot vs Live Coverage Map
Дата: 2026-03-29
## 1) Snapshot coverage (факт)
Текущий runtime подключает только `docs/ARCH/2020экспорт` subset:
- `03_snapshot_fragment_problem_cases.json`
- `04_samples_SpisanieSRaschetnogoScheta.json`
- `05_samples_RealizaciyaTovarovUslug.json`
- `06_samples_PostuplenieTovarovUslug.json`
- `07_samples_DocumentJournals.json`
- `08_samples_NDS_registers.json`
- `09_samples_key_fields_Recorder_Ref_Supplier_Buyer_Responsible.json`
Дополнительно физически существует monthly as-of corpus (`2020_monthly_company_asof_full_test`), но current runtime его не использует.
## 2) Live coverage (факт)
Current runtime live path:
- method: `execute_query`
- endpoint: MCP proxy `/api/execute_query`
- запрос: generic accounting register probe (`TOP N`, без claim-specific filters)
## 3) Coverage by question
| Question | Snapshot needed entities reachable | Live needed entities reachable | Current runtime uses required route | Coverage verdict |
| --- | --- | --- | --- | --- |
| Q1 VAT | Yes (docs + VAT registers + key links) | Partially (generic postings probe) | Yes (`store_canonical + hybrid_store_plus_live`) | `snapshot_plus_live_required` |
| Q2 RBP | Partially (RBP object-level chain не подтверждается) | Not in current route (no live call selected) | No (canonical-only route) | `data_not_reachable_in_current_runtime` |
| Q3 FA | Partially (object-level FA chain неполная) | Partially (generic probe without object scope) | Partially (live есть, но не claim-targeted) | `snapshot_plus_live_required` |
## 4) Why these verdicts
### Q1
- Snapshot покрывает ключевой VAT relation skeleton.
- Live помогает подтвердить movement-level signals.
- Без связки snapshot+live цепочка доказательства хуже по устойчивости.
### Q2
- В текущем runtime path live verification не запускается.
- Snapshot subset не дает стабильный object-level RBP residual proof.
- Поэтому данные не являются достижимыми для полного proof в текущем контуре.
### Q3
- Есть часть snapshot/live сигналов, но object-level FA coverage не закрывается.
- Нужны адресные live calls по объектам ОС, которых сейчас нет.
## 5) B3 verdict (жесткая форма)
- Q1 VAT: `snapshot_plus_live_required`
- Q2 RBP: `data_not_reachable_in_current_runtime`
- Q3 FA: `snapshot_plus_live_required`
@@ -0,0 +1,43 @@
# 9C — Relation Map 1C Entities
Дата: 2026-03-29
Формат: `entity_A`, `entity_B`, `relation_type`, `required_for_claim`, `reachable_via_snapshot`, `reachable_via_live`, `current_runtime_uses_it`, `notes`
## Q1 — VAT chain completeness
| entity_A | entity_B | relation_type | required_for_claim | reachable_via_snapshot | reachable_via_live | current_runtime_uses_it | notes |
| --- | --- | --- | --- | --- | --- | --- | --- |
| `Document_ПоступлениеТоваровУслуг` | `Counterparty/Contract` | `document_has_counterparty` | yes | yes | no | yes | подтверждается links в sample docs |
| `Document_РеализацияТоваровУслуг` | `Counterparty/Contract` | `document_has_counterparty` | yes | yes | no | yes | есть в snapshot links |
| `Document_*` | `Posting` | `document_to_posting` | yes | partial | partial | partial | live probe broad, без doc filter |
| `VAT Register Record` | `Document (Recorder)` | `register_recorded_by_document` | yes | yes | no | yes | есть в `08_samples_NDS_registers` |
| `VAT Register Record` | `Invoice Document` | `register_relates_to_invoice` | yes | yes | no | yes | ключевой переход для claim |
| `VAT Register Record` | `Book Entry layer` | `register_to_book` | yes | partial | no | partial | собирается косвенно через chain signals |
| `Receipt Item Scope` | `Sale Item Scope` | `item_scope_match` | yes | partial | no | partial | прямой item-level реконструкции не хватает |
## Q2 — RBP tail state
| entity_A | entity_B | relation_type | required_for_claim | reachable_via_snapshot | reachable_via_live | current_runtime_uses_it | notes |
| --- | --- | --- | --- | --- | --- | --- | --- |
| `RBP Writeoff Document` | `RBP Object` | `writeoff_to_object` | yes | partial | partial | no | current route не выходит в live |
| `RBP Object` | `RBP Movements` | `object_to_movement` | yes | partial | partial | no | object-level mapping не реконструируется |
| `RBP Movements` | `Month Close Entry` | `movement_to_close` | yes | low | partial | no | нет явной цепочки в current runtime |
| `Month Close Entry` | `End Period Residual` | `close_to_residual` | yes | low | partial | no | ключевое звено для tail verdict отсутствует |
| `Residual` | `Tail Classification` | `residual_to_tail_state` | yes | low | low | no | claim check остается `not_found` |
## Q3 — FA amortization completeness
| entity_A | entity_B | relation_type | required_for_claim | reachable_via_snapshot | reachable_via_live | current_runtime_uses_it | notes |
| --- | --- | --- | --- | --- | --- | --- | --- |
| `FA Object` | `Depreciation Document` | `object_to_depreciation_doc` | yes | partial | partial | partial | в runtime нет устойчивого object-level bind |
| `Depreciation Document` | `Posting` | `depreciation_to_posting` | yes | partial | partial | partial | live call generic, object scope отсутствует |
| `Posting` | `Account Scope` | `posting_to_account_scope` | yes | yes | yes | partial | часть evidence режется gate по account/domain mismatch |
| `Expected FA Set` | `Covered FA Set` | `coverage_set_comparison` | yes | low | low | no | нет полноценного expected-set reconstruction |
| `Set Difference` | `Missing Object Risk` | `set_diff_to_risk` | yes | low | low | no | финальный proof не закрывается |
| `Non-FA Registers` | `FA Claim` | `cross_domain_noise` | no | yes | yes | yes (undesired) | в replay есть нерелевантные NDFL entities |
## Relation map conclusions
1. VAT relation skeleton в snapshot реально существует и частично закрывается текущим runtime.
2. Для RBP и FA критичные object-level transitions либо не реконструируются, либо не используются runtime.
3. На FA наблюдается cross-domain contamination: runtime использует связи, не обязательные для claim, что мешает proof closure.
@@ -0,0 +1,85 @@
# 9D — MCP Query Recipes
Дата: 2026-03-29
Основание: replay + текущий `assistantDataLayer` (`execute_query` generic probe)
## Q1 — VAT
- `question`: 13/15 июля, полнота НДС-цепочки
- `required_live_calls`:
1. `execute_query:find_receipt_document_by_date_item`
2. `execute_query:find_sale_document_by_date_item`
3. `execute_query:find_invoice_links_for_receipt_sale`
4. `execute_query:find_vat_register_entries_by_invoice`
5. `execute_query:find_sales_purchase_book_entries`
6. `execute_query:cross_check_document_postings`
- `current_live_calls`:
1. `execute_query:generic_accounting_register_probe`
- `missing_live_calls`: все 6 required calls выше
- `wrong_live_calls`:
1. `execute_query:generic_accounting_register_probe_without_claim_filters`
- `call_sequence_for_proof`:
1. seed receipt/sale docs
2. resolve invoice links
3. resolve VAT register rows
4. resolve book entries
5. cross-check postings
6. build admissible chain verdict
## Q2 — RBP
- `question`: есть ли хвост РБП на конец июля
- `required_live_calls`:
1. `execute_query:find_rbp_writeoff_documents_in_period`
2. `execute_query:find_rbp_object_movements_account_97`
3. `execute_query:find_month_close_entries_linked_to_rbp`
4. `execute_query:compute_end_period_residual_by_rbp_object`
5. `execute_query:classify_normal_vs_abnormal_residual`
- `current_live_calls`: none
- `missing_live_calls`: все 5 required calls выше
- `wrong_live_calls`:
1. `no_live_call_selected_due_to_canonical_route`
- `call_sequence_for_proof`:
1. seed writeoff doc
2. bind RBP object
3. collect 97 movements
4. collect close entries
5. compute residual tail
6. classify tail state
## Q3 — FA amortization
- `question`: полнота охвата объектов ОС амортизацией за июль
- `required_live_calls`:
1. `execute_query:find_fa_objects_expected_in_period`
2. `execute_query:find_depreciation_documents_2020_07`
3. `execute_query:map_fa_object_to_depreciation_rows`
4. `execute_query:cross_check_postings_accounts_02_20_44`
5. `execute_query:detect_missing_fa_objects_without_depreciation`
- `current_live_calls`:
1. `execute_query:generic_accounting_register_probe`
- `missing_live_calls`: все 5 required calls выше
- `wrong_live_calls`:
1. `execute_query:generic_accounting_register_probe_without_object_scope`
- `call_sequence_for_proof`:
1. seed by period + known amounts
2. resolve expected FA set
3. resolve depreciation docs
4. map object-level postings
5. detect missing objects
6. produce completeness verdict
## Current vs required MCP profile
| Question | Current live profile | Gap type |
| --- | --- | --- |
| Q1 VAT | Generic movement probe | claim-specific live calls missing |
| Q2 RBP | Live path absent | wrong route + missing live chain |
| Q3 FA | Generic movement probe | object-scoped live chain missing |
## Query recipe examples in artifacts
- `docs/ARCH/9_audit_artifacts/query_recipe_examples/mcp_query_recipe_matrix.json`
- `docs/ARCH/9_audit_artifacts/query_recipe_examples/Q1_VAT_recipe.md`
- `docs/ARCH/9_audit_artifacts/query_recipe_examples/Q2_RBP_recipe.md`
- `docs/ARCH/9_audit_artifacts/query_recipe_examples/Q3_FA_recipe.md`
@@ -0,0 +1,83 @@
# 9E — Source-to-Proof Failure Map
Дата: 2026-03-29
## Категории классификации
- `missing_source_data`
- `source_exists_but_runtime_does_not_use_it`
- `wrong_route_selection`
- `wrong_entity_mapping`
- `missing_relation_reconstruction`
- `live_call_insufficient_for_proof`
- `evidence_not_materialized`
- `claim_anchor_coverage_insufficient`
- `answer_layer_cannot_close_proof`
## Q1 — VAT
### Что система должна была сделать
Собрать и доказать цепочку: `receipt/sale doc -> invoice -> VAT register -> book entry -> posting consistency`.
### Что реально поднято
- claim path собран, `admissible_evidence_count=12`, grounded-positive достигнут.
- live call есть (`execute_query`, matched_rows=24).
### Где рвется/шумит
- `live_call_insufficient_for_proof` (частично): live probe broad, не claim-targeted.
- `evidence_not_materialized` (частично): высокий reject (`weak_source_mapping`, `wrong_account_scope`).
### Локальный verdict Q1
`EVIDENCE_MATERIALIZATION_IS_PRIMARY_PROBLEM` (residual cleanup mode, non-blocking).
## Q2 — RBP
### Что система должна была сделать
Доказать/опровергнуть хвост РБП к 31.07 через object-level цепочку `writeoff -> movements -> close -> residual`.
### Что реально поднято
- route: canonical-only.
- live calls: отсутствуют.
- targeted checks: все `not_found`.
- admissible evidence: `0`.
### Где рвется
- `wrong_route_selection`
- `source_exists_but_runtime_does_not_use_it`
- `missing_relation_reconstruction`
- `live_call_insufficient_for_proof`
- `evidence_not_materialized`
### Локальный verdict Q2
`SOURCE_COVERAGE_IS_PRIMARY_PROBLEM` (в комбинации с route gap).
## Q3 — FA amortization
### Что система должна была сделать
Построить object-level proof полноты амортизации: expected FA set, per-object posting coverage, missing-object detection.
### Что реально поднято
- live call есть, admissible evidence `19`.
- итог всё равно limited: `claim_anchor_coverage_insufficient`.
- в evidence встречается cross-domain noise (нерелевантные сущности для FA claim).
### Где рвется
- `wrong_entity_mapping`
- `missing_relation_reconstruction`
- `live_call_insufficient_for_proof`
- `claim_anchor_coverage_insufficient`
- `answer_layer_cannot_close_proof`
### Локальный verdict Q3
`ONE_C_RELATION_MAPPING_IS_PRIMARY_PROBLEM` (с evidence/anchor side-effects).
## Сводный failure map
| Question | Primary break nodes | Result mode |
| --- | --- | --- |
| Q1 VAT | evidence materialization residuals | grounded-positive |
| Q2 RBP | source coverage + route + missing relation reconstruction | limited |
| Q3 FA | relation mapping + claim-anchor closure | limited |
## Общий verdict
`MULTI_NODE_FAILURE_CONFIRMED`
@@ -0,0 +1,51 @@
# 9F — Current Runtime vs Required Runtime
Дата: 2026-03-29
## 1) Сравнение по этапам контура
| Stage | Required runtime behavior | Current runtime behavior | Gap |
| --- | --- | --- | --- |
| Anchor extraction | Claim-specific anchors (period + entity/object + account/doc links) | Есть claim layer, но coverage нестабильный на FA/RBP | partial gap |
| Route selection | Выбор route по required proof path | Для RBP уходит в canonical-only | high gap |
| Source selection | Targeted source scope per claim | Ограниченный snapshot subset + broad fallback patterns | high gap |
| Relation reconstruction | Object-level relation chain reconstruction | Частично (VAT лучше, RBP/FA хуже) | high gap |
| Live call plan | Sequence of claim-bound MCP calls | 0 или 1 generic `execute_query` probe | blocker/high gap |
| Evidence materialization | Candidate -> admissible aligned with claim links | Часто высокий reject шум | high gap |
| Answer handoff | Grounded when claim proof closed, limited when not | Работает, но Q3 блокируется при non-zero admissible из-за anchor closure | medium-high gap |
## 2) Required vs current per question
### Q1 VAT
- Required: цепочка doc->invoice->register->book + posting proof.
- Current: в целом закрывает positive path.
- Gap: current live query слишком broad, admissibility много отбрасывает.
### Q2 RBP
- Required: object-level RBP lifecycle + residual classification.
- Current: canonical route, live path не включен, admissible=0.
- Gap: route/source/relation/live одновременно недожаты.
### Q3 FA
- Required: expected FA set + per-object depreciation coverage.
- Current: live есть, admissible есть, но proof closure нет.
- Gap: relation/object mapping и claim-anchor closure.
## 3) Что должен делать required runtime (минимум)
1. Для каждого claim строить relation-aware execution graph.
2. Подключать claim-specific live call sequence, а не одиночный broad probe.
3. Материализовать evidence в object-level chain links.
4. Закрывать eligibility на claim-level coverage, а не на сигнальном surrogate.
## 4) Что делает current runtime сейчас
1. Имеет рабочий positive path на VAT.
2. Сохраняет limited mode честно, но часто из-за pipeline-level разрывов (не только из-за отсутствия данных).
3. Не имеет стабильного object-level proof path для RBP и FA.
## 5) Runtime delta verdict
- Q1: near-required with residual materialization cleanup.
- Q2: far-from-required.
- Q3: mid-gap (data/evidence есть, closure нет).
@@ -0,0 +1,44 @@
# Chat Export (3 Control Questions)
Источник replay: `x:\1C\NDC_1C\docs\ARCH\8_audit_artifacts\question_replay_logs\1_live_replay.txt`
## Q1 — НДС (L1)
**User question**
13 июля проведено поступление товаров, а 15 июля — реализация этих же мебельных позиций. НДС-цепочка по этим движениям у нас полная или где-то есть выпадение между документом, проводкой и налоговым отражением?
**Runtime outcome**
- `reply_type`: `factual_with_explanation`
- `claim_type`: `prove_vat_chain_completeness`
- `grounding_mode`: `grounded_positive`
- `admissible_evidence_count`: `12`
- `eligibility_outcome`: `grounded_allowed`
- `reason_codes`: none
## Q2 — РБП (L2)
**User question**
31 июля прошло «Списание РБП за Июль 2020 г.», в том числе на 5 000 и ещё несколько сумм. Есть ли в базе признаки, что часть РБП к концу июля всё ещё живёт дольше ожидаемого?
**Runtime outcome**
- `reply_type`: `partial_coverage`
- `claim_type`: `prove_rbp_tail_state`
- `grounding_mode`: `limited_or_insufficient_evidence`
- `admissible_evidence_count`: `0`
- `eligibility_outcome`: `limited_or_insufficient_evidence`
- `reason_codes`: `admissible_evidence_count_zero`, `targeted_evidence_hit_rate_zero`
## Q3 — Амортизация (L3)
**User question**
31 июля начислена амортизация тремя суммами — 2 471,52, 2 465,28 и 849,83. Это похоже на полное начисление по всем нужным объектам за июль или есть риск, что какой-то объект ОС в июле не попал в амортизацию?
**Runtime outcome**
- `reply_type`: `partial_coverage`
- `claim_type`: `prove_month_close_state`
- `grounding_mode`: `limited_or_insufficient_evidence`
- `admissible_evidence_count`: `19`
- `eligibility_outcome`: `limited_or_insufficient_evidence`
- `reason_codes`: `claim_anchor_coverage_insufficient`
## Быстрый итог по 3 вопросам
- 1 кейс вышел в `grounded_positive`.
- 2 кейса остались в `partial_coverage`.
- Основные причины ограничения: `admissible_evidence_count_zero` (Q2) и `claim_anchor_coverage_insufficient` (Q3).
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,15 @@
# Q1 VAT — MCP Query Recipe
Current runtime call:
- `execute_query` generic accounting-register probe (broad, not claim-specific).
Required proof-oriented call sequence:
1. `execute_query:find_receipt_document_by_date_item`
2. `execute_query:find_sale_document_by_date_item`
3. `execute_query:find_invoice_links_for_receipt_sale`
4. `execute_query:find_vat_register_entries_by_invoice`
5. `execute_query:find_sales_purchase_book_entries`
6. `execute_query:cross_check_document_postings`
Why current call is insufficient:
- returns broad movements without explicit invoice/register/book linkage.
@@ -0,0 +1,14 @@
# Q2 RBP — MCP Query Recipe
Current runtime call:
- no live call selected (both fragments routed to canonical snapshot path).
Required proof-oriented call sequence:
1. `execute_query:find_rbp_writeoff_documents_in_period`
2. `execute_query:find_rbp_object_movements_account_97`
3. `execute_query:find_month_close_entries_linked_to_rbp`
4. `execute_query:compute_end_period_residual_by_rbp_object`
5. `execute_query:classify_normal_vs_abnormal_residual`
Why current path is insufficient:
- object-level RBP lifecycle is not reconstructed in live path.
@@ -0,0 +1,14 @@
# Q3 FA — MCP Query Recipe
Current runtime call:
- `execute_query` generic accounting-register probe.
Required proof-oriented call sequence:
1. `execute_query:find_fa_objects_expected_in_period`
2. `execute_query:find_depreciation_documents_2020_07`
3. `execute_query:map_fa_object_to_depreciation_rows`
4. `execute_query:cross_check_postings_accounts_02_20_44`
5. `execute_query:detect_missing_fa_objects_without_depreciation`
Why current call is insufficient:
- object scope is missing, which causes claim-anchor coverage gap.
@@ -0,0 +1,100 @@
[
{
"question_id": "Q1_VAT",
"question": "13/15 July VAT chain completeness",
"required_live_calls": [
"execute_query:find_receipt_document_by_date_item",
"execute_query:find_sale_document_by_date_item",
"execute_query:find_invoice_links_for_receipt_sale",
"execute_query:find_vat_register_entries_by_invoice",
"execute_query:find_sales_purchase_book_entries",
"execute_query:cross_check_document_postings"
],
"current_live_calls": [
"execute_query:generic_accounting_register_probe"
],
"missing_live_calls": [
"execute_query:find_receipt_document_by_date_item",
"execute_query:find_sale_document_by_date_item",
"execute_query:find_invoice_links_for_receipt_sale",
"execute_query:find_vat_register_entries_by_invoice",
"execute_query:find_sales_purchase_book_entries",
"execute_query:cross_check_document_postings"
],
"wrong_live_calls": [
"execute_query:generic_accounting_register_probe_without_claim_filters"
],
"call_sequence_for_proof": [
"seed_by_receipt_sale_docs",
"resolve_invoice_links",
"resolve_vat_register_rows",
"resolve_book_entries",
"validate_doc_to_posting_chain",
"build_admissible_chain_summary"
]
},
{
"question_id": "Q2_RBP",
"question": "RBP write-off and residual tail at 2020-07-31",
"required_live_calls": [
"execute_query:find_rbp_writeoff_documents_in_period",
"execute_query:find_rbp_object_movements_account_97",
"execute_query:find_month_close_entries_linked_to_rbp",
"execute_query:compute_end_period_residual_by_rbp_object",
"execute_query:classify_normal_vs_abnormal_residual"
],
"current_live_calls": [
],
"missing_live_calls": [
"execute_query:find_rbp_writeoff_documents_in_period",
"execute_query:find_rbp_object_movements_account_97",
"execute_query:find_month_close_entries_linked_to_rbp",
"execute_query:compute_end_period_residual_by_rbp_object",
"execute_query:classify_normal_vs_abnormal_residual"
],
"wrong_live_calls": [
"no_live_call_selected_due_to_canonical_route"
],
"call_sequence_for_proof": [
"seed_by_writeoff_doc",
"bind_to_rbp_object",
"collect_97_movements",
"collect_close_entries",
"compute_residual_tail",
"produce_tail_state_verdict"
]
},
{
"question_id": "Q3_FA",
"question": "FA amortization completeness for 2020-07 with 3 amounts",
"required_live_calls": [
"execute_query:find_fa_objects_expected_in_period",
"execute_query:find_depreciation_documents_2020_07",
"execute_query:map_fa_object_to_depreciation_rows",
"execute_query:cross_check_postings_accounts_02_20_44",
"execute_query:detect_missing_fa_objects_without_depreciation"
],
"current_live_calls": [
"execute_query:generic_accounting_register_probe"
],
"missing_live_calls": [
"execute_query:find_fa_objects_expected_in_period",
"execute_query:find_depreciation_documents_2020_07",
"execute_query:map_fa_object_to_depreciation_rows",
"execute_query:cross_check_postings_accounts_02_20_44",
"execute_query:detect_missing_fa_objects_without_depreciation"
],
"wrong_live_calls": [
"execute_query:generic_accounting_register_probe_without_object_scope"
],
"call_sequence_for_proof": [
"seed_by_fa_amounts_and_period",
"resolve_expected_fa_set",
"resolve_depreciation_docs",
"map_object_level_postings",
"detect_missed_objects",
"produce_completeness_verdict"
]
}
]
@@ -0,0 +1,49 @@
{
"generated_at": "2026-03-29T05:48:19.981Z",
"aggregate": {
"wrong_period": 36,
"wrong_domain": 8,
"wrong_account_scope": 20,
"weak_source_mapping": 42,
"zero_live_match": 0,
"future_dated_or_out_of_window": 8
},
"cases": [
{
"case_id": "L1",
"label": "vat_chain_furniture_13_15_july",
"reject_breakdown": {
"wrong_period": 0,
"wrong_domain": 0,
"wrong_account_scope": 12,
"weak_source_mapping": 14,
"zero_live_match": 0,
"future_dated_or_out_of_window": 0
}
},
{
"case_id": "L2",
"label": "rbp_tail_31_july_5000",
"reject_breakdown": {
"wrong_period": 36,
"wrong_domain": 0,
"wrong_account_scope": 0,
"weak_source_mapping": 12,
"zero_live_match": 0,
"future_dated_or_out_of_window": 0
}
},
{
"case_id": "L3",
"label": "fa_amortization_2471_2465_849",
"reject_breakdown": {
"wrong_period": 0,
"wrong_domain": 8,
"wrong_account_scope": 8,
"weak_source_mapping": 16,
"zero_live_match": 0,
"future_dated_or_out_of_window": 8
}
}
]
}
@@ -0,0 +1,47 @@
{
"generated_at": "2026-03-29T05:48:19.981Z",
"cases": [
{
"case_id": "L1",
"label": "vat_chain_furniture_13_15_july",
"business_scope_raw": [
"company_specific_accounting"
],
"business_scope_resolved": [
"company_specific_accounting"
],
"company_scope_resolution_reason": [
"temporal_claim_bound_company_scope_recovery"
]
},
{
"case_id": "L2",
"label": "rbp_tail_31_july_5000",
"business_scope_raw": [
"company_specific_accounting"
],
"business_scope_resolved": [
"company_specific_accounting"
],
"company_scope_resolution_reason": [
"temporal_claim_bound_company_scope_recovery"
]
},
{
"case_id": "L3",
"label": "fa_amortization_2471_2465_849",
"business_scope_raw": [
"company_specific_accounting"
],
"business_scope_resolved": [
"company_specific_accounting"
],
"company_scope_resolution_reason": [
"temporal_claim_bound_company_scope_recovery"
]
}
],
"metric": {
"live_company_scope_resolution_rate": 1
}
}
@@ -0,0 +1,78 @@
{
"generated_at": "2026-03-29T05:48:19.981Z",
"cases": [
{
"case_id": "L1",
"label": "vat_chain_furniture_13_15_july",
"claim_type": "prove_vat_chain_completeness",
"admissible_evidence_count": 12,
"live_calls": [
{
"fragment_id": "F2",
"route": "hybrid_store_plus_live",
"method": "execute_query",
"args_summary": null,
"query_subject": "vat_chain_conflict",
"account_scope": [],
"fetched_rows": 24,
"returned_rows": 12,
"matched_rows": 24,
"status": "ok"
}
],
"claim_targeted_hit_rate": 1,
"eligibility": {
"eligible": true,
"grounding_mode": "grounded_positive",
"outcome": "grounded_allowed",
"reason_codes": []
}
},
{
"case_id": "L2",
"label": "rbp_tail_31_july_5000",
"claim_type": "prove_rbp_tail_state",
"admissible_evidence_count": 0,
"live_calls": [],
"claim_targeted_hit_rate": 0,
"eligibility": {
"eligible": false,
"grounding_mode": "limited_or_insufficient_evidence",
"outcome": "limited_or_insufficient_evidence",
"reason_codes": [
"admissible_evidence_count_zero",
"targeted_evidence_hit_rate_zero"
]
}
},
{
"case_id": "L3",
"label": "fa_amortization_2471_2465_849",
"claim_type": "prove_month_close_state",
"admissible_evidence_count": 19,
"live_calls": [
{
"fragment_id": "F2",
"route": "hybrid_store_plus_live",
"method": "execute_query",
"args_summary": null,
"query_subject": "fixed_asset_card_mismatch",
"account_scope": [],
"fetched_rows": 24,
"returned_rows": 12,
"matched_rows": 24,
"status": "ok"
}
],
"claim_targeted_hit_rate": 1,
"eligibility": {
"eligible": false,
"grounding_mode": "limited_or_insufficient_evidence",
"outcome": "limited_or_insufficient_evidence",
"reason_codes": [
"claim_anchor_coverage_insufficient"
]
}
}
]
}
@@ -0,0 +1,74 @@
{
"generated_at": "2026-03-29T05:48:19.980Z",
"cases": [
{
"case_id": "L1",
"label": "vat_chain_furniture_13_15_july",
"raw_time_scope": "2020-07",
"resolved_time_anchor": "2020-07-13",
"effective_primary_period": {
"from": "2020-07-13",
"to": "2020-07-13",
"granularity": "day"
},
"temporal_guard_input": "2020-07-13..2020-07-13",
"temporal_guard_basis": "resolved_primary_period",
"eligibility_time_basis": "resolved_primary_period",
"temporal_guard_outcome": "passed",
"contradiction": {
"has_july_effective_primary_period": true,
"temporal_guard_outcome": "passed",
"temporal_basis_mismatch": false,
"failed_under_july_effective_period": false,
"contradiction": false
}
},
{
"case_id": "L2",
"label": "rbp_tail_31_july_5000",
"raw_time_scope": "2020-07",
"resolved_time_anchor": "2020-07-31",
"effective_primary_period": {
"from": "2020-07-31",
"to": "2020-07-31",
"granularity": "day"
},
"temporal_guard_input": "2020-07-31..2020-07-31",
"temporal_guard_basis": "resolved_primary_period",
"eligibility_time_basis": "resolved_primary_period",
"temporal_guard_outcome": "passed",
"contradiction": {
"has_july_effective_primary_period": true,
"temporal_guard_outcome": "passed",
"temporal_basis_mismatch": false,
"failed_under_july_effective_period": false,
"contradiction": false
}
},
{
"case_id": "L3",
"label": "fa_amortization_2471_2465_849",
"raw_time_scope": "2020-07",
"resolved_time_anchor": "2020-07-31",
"effective_primary_period": {
"from": "2020-07-31",
"to": "2020-07-31",
"granularity": "day"
},
"temporal_guard_input": "2020-07-31..2020-07-31",
"temporal_guard_basis": "resolved_primary_period",
"eligibility_time_basis": "resolved_primary_period",
"temporal_guard_outcome": "passed",
"contradiction": {
"has_july_effective_primary_period": true,
"temporal_guard_outcome": "passed",
"temporal_basis_mismatch": false,
"failed_under_july_effective_period": false,
"contradiction": false
}
}
],
"metric": {
"live_temporal_contradiction_rate": 0
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,139 @@
{
"generated_at": "2026-03-29T09:50:58",
"cases": [
{
"case_id": "L1",
"label": "vat_chain_furniture_13_15_july",
"claim_type": "prove_vat_chain_completeness",
"question_type_class": "which_chains_are_complete_vs_incomplete",
"routes": [
{
"fragment_id": "F1",
"route": "store_canonical",
"reason": "Only plain factual lookups are allowed to stay on canonical path. Query class: canonical_fact_lookup. Allowed fallback: no_route. Forbidden fallback: hybrid_store_plus_live."
},
{
"fragment_id": "F2",
"route": "hybrid_store_plus_live",
"reason": "Lifecycle-first intents are deterministically promoted to hybrid path. Query class: lifecycle_first. Allowed fallback: no_route. Forbidden fallback: store_canonical."
}
],
"required_checks": [
"source_document_found",
"invoice_found",
"tax_register_entry_found",
"book_entry_found",
"chain_linkage_status"
],
"check_status": {
"source_document_found": "found",
"invoice_found": "found",
"tax_register_entry_found": "found",
"book_entry_found": "found",
"chain_linkage_status": "found"
},
"candidate_evidence_total": 30,
"admissible_evidence_count": 12,
"reject_breakdown": {
"wrong_period": 0,
"wrong_domain": 0,
"wrong_account_scope": 12,
"weak_source_mapping": 14,
"zero_live_match": 0,
"future_dated_or_out_of_window": 0
},
"eligibility_outcome": "grounded_allowed",
"grounding_mode": "grounded_positive",
"reason_codes": [
]
},
{
"case_id": "L2",
"label": "rbp_tail_31_july_5000",
"claim_type": "prove_rbp_tail_state",
"question_type_class": "what_is_it_grounded_on",
"routes": [
{
"fragment_id": "F1",
"route": "store_canonical",
"reason": "Only plain factual lookups are allowed to stay on canonical path. Query class: canonical_fact_lookup. Allowed fallback: no_route. Forbidden fallback: hybrid_store_plus_live."
},
{
"fragment_id": "F2",
"route": "store_canonical",
"reason": "Only plain factual lookups are allowed to stay on canonical path. Query class: canonical_fact_lookup. Allowed fallback: no_route. Forbidden fallback: hybrid_store_plus_live."
}
],
"required_checks": [
"rbp_writeoff_lifecycle_confirmed",
"residual_tail_found",
"close_contradiction_or_normal_residual"
],
"check_status": {
"rbp_writeoff_lifecycle_confirmed": "not_found",
"residual_tail_found": "not_found",
"close_contradiction_or_normal_residual": "not_found"
},
"candidate_evidence_total": 12,
"admissible_evidence_count": 0,
"reject_breakdown": {
"wrong_period": 36,
"wrong_domain": 0,
"wrong_account_scope": 0,
"weak_source_mapping": 12,
"zero_live_match": 0,
"future_dated_or_out_of_window": 0
},
"eligibility_outcome": "limited_or_insufficient_evidence",
"grounding_mode": "limited_or_insufficient_evidence",
"reason_codes": [
"admissible_evidence_count_zero",
"targeted_evidence_hit_rate_zero"
]
},
{
"case_id": "L3",
"label": "fa_amortization_2471_2465_849",
"claim_type": "prove_month_close_state",
"question_type_class": "why_breaks",
"routes": [
{
"fragment_id": "F1",
"route": "store_feature_risk",
"reason": "Rule checks without symptom/lifecycle signals run via risk profile path. Query class: rule_check_without_symptom. Allowed fallback: no_route. Forbidden fallback: store_canonical."
},
{
"fragment_id": "F2",
"route": "hybrid_store_plus_live",
"reason": "Lifecycle-first intents are deterministically promoted to hybrid path. Query class: lifecycle_first. Allowed fallback: no_route. Forbidden fallback: store_canonical."
}
],
"required_checks": [
"close_operation_found",
"distribution_step_found",
"residual_tail_found"
],
"check_status": {
"close_operation_found": "found",
"distribution_step_found": "found",
"residual_tail_found": "found"
},
"candidate_evidence_total": 35,
"admissible_evidence_count": 19,
"reject_breakdown": {
"wrong_period": 0,
"wrong_domain": 8,
"wrong_account_scope": 8,
"weak_source_mapping": 16,
"zero_live_match": 0,
"future_dated_or_out_of_window": 8
},
"eligibility_outcome": "limited_or_insufficient_evidence",
"grounding_mode": "limited_or_insufficient_evidence",
"reason_codes": [
"claim_anchor_coverage_insufficient"
]
}
]
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,14 @@
# Chat Export Live Replay
## L1 | vat_chain_furniture_13_15_july
user: 13 июля проведено поступление товаров, а 15 июля — реализация этих же мебельных позиций. НДС-цепочка по этим движениям у нас полная или где-то есть выпадение между документом, проводкой и налоговым отражением?
assistant(reply_type=factual_with_explanation, trace_id=i2sI02TOEt-jx_): Коротко: Цепочки разделены на подтвержденные, частично подтвержденные и неподтвержденные. Что сломано: - Ожидаемый переход в учетной цепочке не подтвержден; подтверждение пока частичное, вывод ограничен. Почему это похоже на проблему: - Цепочки разделены на завершенные и незавершенные по текущей опоре. - Часть НДС-цепочки подтверждена, но один или несколько переходов документ -> счет-фактура -> регистр -> книга не подтверждены. На чем это основано: - Опора собрана по НДС-звеньям, чтобы разделить полные и неполные переходы. - Опора собрана по звеньям НДС-цепочки, чтобы разделить полные и неполные переходы. - Вывод опирается на 10 подтвержденных наблюдений в текущем срезе. - Проверены связанные документы и проводки по 6 источникам. - Есть связка между основным выводом и подтверждающими записями. - Опора частичная: по НДС-цепочке не подтверждены одно или несколько звеньев. - Цепочки пока не удалось уверенно разделить на полные и неполные. Что проверить первым: - Сначала разложите НДС-цепочку по шагам: документ -> счет-фактура -> регистр -> книга. - Проверьте ОС-контур: объект ОС -> ввод в эксплуатацию -> начисление амортизации по счетам 01/02. - Сверьте параметр амортизации и наличие начисления по каждому объекту ОС в периоде. Ограничения: - Деление на «complete/incomplete» зависит от полноты цепочки в текущем срезе. - Надежность problem-сигнала низкая, поэтому вывод ограничен. - Оценка сделана на snapshot-срезе и может не включать часть цепочки. - Live probe использует ограниченный выборочный read-only запрос к 1С.
## L2 | rbp_tail_31_july_5000
user: 31 июля прошло “Списание РБП за Июль 2020 г.”, в том числе на 5 000 и ещё несколько сумм. Есть ли в базе признаки, что часть РБП к концу июля всё ещё живёт дольше ожидаемого?
assistant(reply_type=partial_coverage, trace_id=kDTLdj1FllFHW4): Коротко: Ниже перечислены основания вывода по списание, остаток и подтверждение на конец периода. Что сломано: - Цепочка затрат и закрытия месяца подтверждена только частично; подтверждение пока частичное, вывод ограничен. Почему это похоже на проблему: - Фокус ответа по подтверждение списания и остатка на конец периода, а не общий close-narrative. - Ожидаемая цепочка списания выглядит незавершенной. - По часть списаний к концу периода подтверждена не полностью, поэтому остаток может сохраняться дольше ожидаемого. На чем это основано: - Опора перечислена по РБП-объектам, документам списания и остаткам на конец периода. - Основание собрано по объект списания, документ списания и остаток на конец периода. - Опора частичная: не по всем объектам подтверждено списание к концу периода. - Отдельно не подтверждено или покрыто частично: R1, R2. - В опоре использованы якоря вопроса: 2020, period:2020. Что проверить первым: - Сначала перечислите по объект, документ списания и остаток после списания на конец периода. - Перечислите основание по объект, документ списания, движение по счету 97, остаток на конец периода. Ограничения: - В основание включены только подтвержденные источники; неподтвержденные вынесены в ограничения. - Якоря из вопроса без прямого подтверждения: 5 000. - Надежность problem-сигнала низкая, поэтому вывод ограничен. - Оценка сделана на snapshot-срезе и может не включать часть цепочки. - Canonical output ограничен доменным runtime-контрактом. - Ни одно требование не получило подтвержденного покрытия.
## L3 | fa_amortization_2471_2465_849
user: 31 июля начислена амортизация тремя суммами — 2 471,52, 2 465,28 и 849,83. Это похоже на полное начисление по всем нужным объектам за июль или есть риск, что какой-то объект ОС в июле не попал в амортизацию?
assistant(reply_type=partial_coverage, trace_id=tSRTKmvWeBFIig): Коротко: Наиболее вероятный механизм проблемы подтвержден частично и требует первичной проверки. Что сломано: - подтверждение пока частичное, вывод ограничен. Почему это похоже на проблему: - Переход от карточки ОС к начислению амортизации подтвержден не полностью. - По ОС часть переходов к начислению амортизации подтверждена не полностью, поэтому есть риск пропуска отдельных объектов. На чем это основано: - Вывод опирается на 10 подтвержденных наблюдений в текущем срезе. - Проверены связанные документы и проводки по 6 источникам. - Есть связка между основным выводом и подтверждающими записями. - Опора частичная: не по всем объектам ОС подтверждено попадание в начисление амортизации. - Подтверждено по требованиям: R1. Что проверить первым: - Проверьте ОС-контур: объект ОС -> ввод в эксплуатацию -> начисление амортизации по счетам 01/02. - Сверьте параметр амортизации и наличие начисления по каждому объекту ОС в периоде. Ограничения: - Якоря из вопроса без прямого подтверждения: 2 471,52, 2 465,28, 849,83. - Механизм проблемы подтвержден не полностью. - Надежность problem-сигнала низкая, поэтому вывод ограничен. - Риск-факторы определяются эвристикой, а не полным набором бизнес-правил 1С. - Часть нерелевантных записей исключена domain purity фильтром. - Оценка сделана на snapshot-срезе и может не включать часть цепочки.
@@ -0,0 +1,7 @@
# Live Case Matrix
| Case | Label | Reply | Claim Type | Admissible Evidence | Grounding Mode | Scope | Temporal |
| --- | --- | --- | --- | ---: | --- | --- | --- |
| L1 | vat_chain_furniture_13_15_july | factual_with_explanation | prove_vat_chain_completeness | 12 | grounded_positive | company_specific_accounting | passed |
| L2 | rbp_tail_31_july_5000 | partial_coverage | prove_rbp_tail_state | 0 | limited_or_insufficient_evidence | company_specific_accounting | passed |
| L3 | fa_amortization_2471_2465_849 | partial_coverage | prove_month_close_state | 19 | limited_or_insufficient_evidence | company_specific_accounting | passed |
@@ -0,0 +1,23 @@
# Live Replay Report (Wave 19.2)
## Source
- Source of truth replayed from: `X:\1C\NDC_1C\llm_normalizer\docs\runs\2026-03-29_Stage_04_Wave_19_1_Live_Alignment_Fix_Claim_Bound_Runtime\1.txt`
- Replayed exactly 3 user turns from the original export.
- Runtime path: MCP ON, useMock=true.
## Metrics
- live_temporal_contradiction_rate: 0
- live_company_scope_resolution_rate: 1
- live_admissible_evidence_nonzero_rate: 0.6667
- live_partial_coverage_default_rate: 0.6667
- baseline_partial_coverage_default_rate: 1
- live_claim_path_completion_rate: 0.6667
- live_false_grounded_answer_rate: 0
## Verdict
- LIVE_TEMPORAL_ALIGNMENT_FIXED: FIXED
- LIVE_COMPANY_SCOPE_FIXED: FIXED
- LIVE_EVIDENCE_PATH_FIXED: FIXED
- LIVE_PARTIAL_COVERAGE_DEFAULT_REDUCED: REDUCED
- Overall: WAVE19_2_ACCEPTED
@@ -0,0 +1,6 @@
{
"generated_at": "2026-03-29T09:22:59",
"cases": [
]
}
@@ -0,0 +1,36 @@
{
"run_id": "2026-03-29_Stage_04_Wave_19_2_Live_Runtime_Fix_Replay_1txt",
"stage": "Stage_04",
"wave": "Wave_19_2",
"scope": "live_runtime_fix_by_replay_1txt",
"source_of_truth": "X:\\1C\\NDC_1C\\llm_normalizer\\docs\\runs\\2026-03-29_Stage_04_Wave_19_1_Live_Alignment_Fix_Claim_Bound_Runtime\\1.txt",
"execution": {
"replay_mode": "exact_questions_from_1_txt",
"runtime_path": "assistant_message_with_mcp_runtime_on",
"normalizer_mode": "useMock=true",
"session_id": "asst-wave19_2-1774763298275"
},
"thresholds": {
"live_temporal_contradiction_rate": 0,
"live_company_scope_resolution_rate": 1,
"live_false_grounded_answer_rate": 0,
"live_admissible_evidence_nonzero_min_cases": 2
},
"metrics": {
"case_count": 3,
"baseline_partial_coverage_default_rate": 1,
"live_temporal_contradiction_rate": 0,
"live_company_scope_resolution_rate": 1,
"live_admissible_evidence_nonzero_rate": 0.6667,
"live_partial_coverage_default_rate": 0.6667,
"live_claim_path_completion_rate": 0.6667,
"live_false_grounded_answer_rate": 0
},
"verdicts": {
"LIVE_TEMPORAL_ALIGNMENT_FIXED": "FIXED",
"LIVE_COMPANY_SCOPE_FIXED": "FIXED",
"LIVE_EVIDENCE_PATH_FIXED": "FIXED",
"LIVE_PARTIAL_COVERAGE_DEFAULT_REDUCED": "REDUCED",
"overall_status": "WAVE19_2_ACCEPTED"
}
}
@@ -0,0 +1,38 @@
[
{
"case_id": "L1",
"label": "vat_chain_furniture_13_15_july",
"claim_type": "prove_vat_chain_completeness",
"routes": "store_canonical -\u003e hybrid_store_plus_live",
"business_scope_resolved": "company_specific_accounting",
"resolved_time_anchor": "2020-07-13",
"temporal_guard_outcome": "passed",
"admissible_evidence_count": 12,
"grounding_mode": "grounded_positive",
"reply_type": "factual_with_explanation"
},
{
"case_id": "L2",
"label": "rbp_tail_31_july_5000",
"claim_type": "prove_rbp_tail_state",
"routes": "store_canonical -\u003e store_canonical",
"business_scope_resolved": "company_specific_accounting",
"resolved_time_anchor": "2020-07-31",
"temporal_guard_outcome": "passed",
"admissible_evidence_count": 0,
"grounding_mode": "limited_or_insufficient_evidence",
"reply_type": "partial_coverage"
},
{
"case_id": "L3",
"label": "fa_amortization_2471_2465_849",
"claim_type": "prove_month_close_state",
"routes": "store_feature_risk -\u003e hybrid_store_plus_live",
"business_scope_resolved": "company_specific_accounting",
"resolved_time_anchor": "2020-07-31",
"temporal_guard_outcome": "passed",
"admissible_evidence_count": 19,
"grounding_mode": "limited_or_insufficient_evidence",
"reply_type": "partial_coverage"
}
]
@@ -0,0 +1,26 @@
# 2020 экспорт: состав выгрузки
Папка собрана автоматически для ручного анализа текущего состояния.
## Файлы
1. `01_ontology_mapping_layer.md` — текущая онтология/мэппинг и метрики среза.
2. `02_canonical_relation_rules.md` — правила построения canonical relations.
3. `03_snapshot_fragment_problem_cases.json` — проблемный фрагмент snapshot июня 2020.
4. `04_samples_SpisanieSRaschetnogoScheta.json` — реальные записи по `СписаниеСРасчетногоСчета`.
5. `05_samples_RealizaciyaTovarovUslug.json` — реальные записи по `РеализацияТоваровУслуг`.
6. `06_samples_PostuplenieTovarovUslug.json` — реальные записи по `ПоступлениеТоваровУслуг`.
7. `07_samples_DocumentJournals.json` — реальные записи по журналам документов.
8. `08_samples_NDS_registers.json` — реальные записи по НДС-регистрам.
9. `09_samples_key_fields_Recorder_Ref_Supplier_Buyer_Responsible.json` — записи с ключевыми полями.
## Ключевые поля: фактическая встречаемость в snapshot
| field | count |
| --- | --- |
| Ответственный_Key | 187 |
| Ref | 168 |
| Recorder | 147 |
| Ref_Key | 93 |
| Поставщик_Key | 78 |
| Покупатель_Key | 46 |
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,112 @@
{
"status": "success",
"snapshot_mode": "cumulative_asof",
"year": 2020,
"service_root": "http://localhost/buh_test/odata/standard.odata/",
"entity_sets_total": 2,
"entity_sets": [
"AccountingRegister_Хозрасчетный",
"AccountingRegister_Хозрасчетный_RecordType"
],
"page_size": 500,
"max_pages_per_set": 0,
"include_undated": "all_months",
"truncated_entity_sets_count": 0,
"truncated_entity_sets": [],
"records_exported_per_month": {
"2020-01": 15113,
"2020-02": 15211,
"2020-03": 15508,
"2020-04": 15578,
"2020-05": 15648,
"2020-06": 15953,
"2020-07": 16039,
"2020-08": 16122,
"2020-09": 16175,
"2020-10": 16212,
"2020-11": 16308,
"2020-12": 16377
},
"dated_records_exported_per_month": {
"2020-01": 10644,
"2020-02": 10742,
"2020-03": 11039,
"2020-04": 11109,
"2020-05": 11179,
"2020-06": 11484,
"2020-07": 11570,
"2020-08": 11653,
"2020-09": 11706,
"2020-10": 11743,
"2020-11": 11839,
"2020-12": 11908
},
"undated_records_exported_per_month": {
"2020-01": 4469,
"2020-02": 4469,
"2020-03": 4469,
"2020-04": 4469,
"2020-05": 4469,
"2020-06": 4469,
"2020-07": 4469,
"2020-08": 4469,
"2020-09": 4469,
"2020-10": 4469,
"2020-11": 4469,
"2020-12": 4469
},
"links_exported_per_month": {
"2020-01": 47623,
"2020-02": 48036,
"2020-03": 49243,
"2020-04": 49539,
"2020-05": 49834,
"2020-06": 51071,
"2020-07": 51426,
"2020-08": 51769,
"2020-09": 51992,
"2020-10": 52151,
"2020-11": 52545,
"2020-12": 52831
},
"entity_set_stats": {
"AccountingRegister_Хозрасчетный": {
"pages_read": 9,
"records_scanned": 4469,
"records_mapped": 4469,
"records_with_datetime": 0,
"records_without_datetime": 4469,
"records_skipped_future": 0,
"mapping_errors": 0,
"truncated_by_limit_or_guard": false,
"pagination_repeat_guard_hits": 0
},
"AccountingRegister_Хозрасчетный_RecordType": {
"pages_read": 26,
"records_scanned": 12659,
"records_mapped": 11908,
"records_with_datetime": 12659,
"records_without_datetime": 0,
"records_skipped_future": 751,
"mapping_errors": 0,
"truncated_by_limit_or_guard": false,
"pagination_repeat_guard_hits": 0
}
},
"snapshot_files": [
"snapshot_2020-01_asof_full.json",
"snapshot_2020-02_asof_full.json",
"snapshot_2020-03_asof_full.json",
"snapshot_2020-04_asof_full.json",
"snapshot_2020-05_asof_full.json",
"snapshot_2020-06_asof_full.json",
"snapshot_2020-07_asof_full.json",
"snapshot_2020-08_asof_full.json",
"snapshot_2020-09_asof_full.json",
"snapshot_2020-10_asof_full.json",
"snapshot_2020-11_asof_full.json",
"snapshot_2020-12_asof_full.json"
],
"output_dir": "x:\\1C\\NDC_1C\\docs\\ARCH\\2020_monthly_company_asof_full_test",
"generated_at_utc": "2026-03-27T21:34:24.669751+00:00"
}
@@ -256,7 +256,15 @@ function requiredChecksByClaim(claimType) {
if (claimType === "prove_month_close_state") {
return ["close_operation_found", "distribution_step_found", "residual_tail_found"];
}
return ["rbp_writeoff_lifecycle_confirmed", "residual_tail_found", "close_contradiction_or_normal_residual"];
return [
"rbp_writeoff_document_found",
"rbp_object_identified",
"rbp_movement_found",
"rbp_period_end_residual_found",
"rbp_writeoff_lifecycle_confirmed",
"residual_tail_found",
"close_contradiction_or_normal_residual"
];
}
function detectChecksForCorpus(corpus, claimType, anchors) {
const checks = new Set();
@@ -276,6 +284,10 @@ function detectChecksForCorpus(corpus, claimType, anchors) {
const hasRbp = /(?:\brbp\b|рбп|account\s*97|счет\s*97|deferred)/i.test(corpus);
const hasResidual = /(?:tail|остат|незакры|overdue|period_boundary|terminal_state_gap)/i.test(corpus);
const hasContradiction = /(?:contradiction|invalid_transition|normal residual|нормальн)/i.test(corpus);
const hasRbpWriteoffDoc = /(?:списани[ея]\s+рбп|rbp_writeoff|deferred_expense_document|writeoff document)/i.test(corpus);
const hasRbpObject = /(?:rbp[_\s-]?object|объект\s+рбп|analytics|subkonto|расходыбудущихпериодов)/i.test(corpus);
const hasMovement = /(?:movement|движен|хозрасчетный|document_to_posting|posting|проводк)/i.test(corpus);
const hasPeriodEndResidual = /(?:period_boundary|end_period|2020-07-31|остат)/i.test(corpus);
if (claimType === "prove_settlement_closure_state") {
if (hasPayment)
checks.add("payment_document_found");
@@ -325,6 +337,14 @@ function detectChecksForCorpus(corpus, claimType, anchors) {
checks.add("residual_tail_found");
}
else {
if (hasRbpWriteoffDoc || (hasRbp && hasDistribution))
checks.add("rbp_writeoff_document_found");
if (hasRbpObject || hasRbp)
checks.add("rbp_object_identified");
if (hasMovement)
checks.add("rbp_movement_found");
if (hasPeriodEndResidual || hasResidual)
checks.add("rbp_period_end_residual_found");
if (hasRbp && hasDistribution)
checks.add("rbp_writeoff_lifecycle_confirmed");
if (hasResidual)
+305 -37
View File
@@ -67,6 +67,26 @@ const MCP_LIVE_MOVEMENTS_QUERY_TEMPLATE = `
УПОРЯДОЧИТЬ ПО
Движения.Период УБЫВ
`;
const MCP_LIVE_MOVEMENTS_BY_PERIOD_QUERY_TEMPLATE = `
ВЫБРАТЬ ПЕРВЫЕ __LIMIT__
Движения.Период КАК Период,
ПРЕДСТАВЛЕНИЕ(Движения.Регистратор) КАК Регистратор,
ПРЕДСТАВЛЕНИЕ(Движения.СчетДт) КАК СчетДт,
ПРЕДСТАВЛЕНИЕ(Движения.СчетКт) КАК СчетКт,
Движения.Сумма КАК Сумма
ИЗ
РегистрБухгалтерии.Хозрасчетный КАК Движения
ГДЕ
Движения.Период МЕЖДУ __FROM_DATETIME__ И __TO_DATETIME__
УПОРЯДОЧИТЬ ПО
Движения.Период УБЫВ
`;
const RBP_REQUIRED_LIVE_CALLS = [
"find_rbp_writeoff_documents_in_period",
"find_rbp_object_movements_account_97",
"find_month_close_entries_linked_to_rbp",
"compute_end_period_residual_by_rbp_object"
];
function pushUniqueLine(target, line) {
if (!target.includes(line)) {
target.push(line);
@@ -94,6 +114,129 @@ function parseFiniteNumber(value) {
}
return null;
}
function formatIsoDateUtc(date) {
const year = date.getUTCFullYear();
const month = String(date.getUTCMonth() + 1).padStart(2, "0");
const day = String(date.getUTCDate()).padStart(2, "0");
return `${year}-${month}-${day}`;
}
function monthEndFromIso(isoDate) {
const match = String(isoDate ?? "").match(/^(\d{4})-(\d{2})-(\d{2})$/);
if (!match) {
return null;
}
const year = Number(match[1]);
const month = Number(match[2]);
if (!Number.isFinite(year) || !Number.isFinite(month)) {
return null;
}
const end = new Date(Date.UTC(year, month, 0));
return formatIsoDateUtc(end);
}
function shiftIsoDate(isoDate, deltaDays) {
const match = String(isoDate ?? "").match(/^(\d{4})-(\d{2})-(\d{2})$/);
if (!match) {
return null;
}
const date = new Date(Date.UTC(Number(match[1]), Number(match[2]) - 1, Number(match[3])));
if (Number.isNaN(date.getTime())) {
return null;
}
date.setUTCDate(date.getUTCDate() + deltaDays);
return formatIsoDateUtc(date);
}
function toDateTimeExpr(isoDate, endOfDay) {
const match = String(isoDate ?? "").match(/^(\d{4})-(\d{2})-(\d{2})$/);
if (!match) {
return null;
}
const year = Number(match[1]);
const month = Number(match[2]);
const day = Number(match[3]);
if (!Number.isFinite(year) || !Number.isFinite(month) || !Number.isFinite(day)) {
return null;
}
const hour = endOfDay ? 23 : 0;
const minute = endOfDay ? 59 : 0;
const second = endOfDay ? 59 : 0;
return `ДАТАВРЕМЯ(${year}, ${month}, ${day}, ${hour}, ${minute}, ${second})`;
}
function buildLiveRangeQuery(fromIso, toIso, limit) {
const fromExpr = toDateTimeExpr(fromIso, false);
const toExpr = toDateTimeExpr(toIso, true);
if (!fromExpr || !toExpr) {
return MCP_LIVE_MOVEMENTS_QUERY_TEMPLATE.replace("__LIMIT__", String(limit));
}
return MCP_LIVE_MOVEMENTS_BY_PERIOD_QUERY_TEMPLATE.replace("__LIMIT__", String(limit))
.replace("__FROM_DATETIME__", fromExpr)
.replace("__TO_DATETIME__", toExpr);
}
function hasRbpSignal(text) {
return /(?:\brbp\b|рбп|расходы\s+будущих\s+периодов|deferred|writeoff|списани[ея]\s+рбп|account\s*97|счет\s*97)/i.test(String(text ?? "").toLowerCase());
}
function buildLiveMcpCallPlan(route, fragmentText) {
const semanticProfile = buildSemanticRetrievalProfile(fragmentText);
const rbpClaim = hasRbpSignal(fragmentText) ||
semanticProfile.query_subject === "deferred_expense_lifecycle_anomaly" ||
semanticProfile.domain_scope.includes("deferred_expense");
if (!rbpClaim) {
return {
claim_type: null,
query_subject: semanticProfile.query_subject,
required_live_calls: [],
calls: [
{
call_id: "generic_accounting_register_probe",
purpose: "live_overlay_probe",
query: MCP_LIVE_MOVEMENTS_QUERY_TEMPLATE.replace("__LIMIT__", String(config_1.ASSISTANT_MCP_LIVE_LIMIT)),
required_for_claim: false
}
],
route_gap_reason: null
};
}
const periodScope = inferPeriodScope(fragmentText);
const primaryFrom = periodScope.from ?? "2020-07-01";
const primaryTo = periodScope.to ?? monthEndFromIso(primaryFrom) ?? "2020-07-31";
const carryFrom = shiftIsoDate(primaryFrom, -31) ?? primaryFrom;
const carryTo = shiftIsoDate(primaryTo, 31) ?? primaryTo;
return {
claim_type: "prove_rbp_tail_state",
query_subject: "deferred_expense_lifecycle_anomaly",
required_live_calls: [...RBP_REQUIRED_LIVE_CALLS],
calls: [
{
call_id: "find_rbp_writeoff_documents_in_period",
purpose: "seed_writeoff_documents",
query: buildLiveRangeQuery(primaryFrom, primaryTo, config_1.ASSISTANT_MCP_LIVE_LIMIT),
required_for_claim: true,
account_scope_override: ["97", "20", "25", "26", "44"]
},
{
call_id: "find_rbp_object_movements_account_97",
purpose: "collect_rbp_object_movements",
query: buildLiveRangeQuery(primaryFrom, primaryTo, config_1.ASSISTANT_MCP_LIVE_LIMIT),
required_for_claim: true,
account_scope_override: ["97"]
},
{
call_id: "find_month_close_entries_linked_to_rbp",
purpose: "link_month_close_to_rbp",
query: buildLiveRangeQuery(primaryFrom, primaryTo, config_1.ASSISTANT_MCP_LIVE_LIMIT),
required_for_claim: true,
account_scope_override: ["97", "20", "25", "26", "44"]
},
{
call_id: "compute_end_period_residual_by_rbp_object",
purpose: "collect_residual_tail_signals",
query: buildLiveRangeQuery(carryFrom, carryTo, config_1.ASSISTANT_MCP_LIVE_LIMIT),
required_for_claim: true,
account_scope_override: ["97", "20", "25", "26", "44"]
}
],
route_gap_reason: null
};
}
function detectBroadQuery(fragmentText, route) {
const text = String(fragmentText ?? "").trim();
const lower = text.toLowerCase();
@@ -2024,73 +2167,158 @@ class AssistantDataLayer {
return merged;
}
async fetchLiveMcpOverlay(route, fragmentText) {
const accountScope = extractAccountScopeFromText(fragmentText);
const endpoint = this.buildMcpUrl("/api/execute_query");
const query = MCP_LIVE_MOVEMENTS_QUERY_TEMPLATE.replace("__LIMIT__", String(config_1.ASSISTANT_MCP_LIVE_LIMIT));
const payload = await this.fetchJsonWithTimeout(endpoint, {
query,
limit: config_1.ASSISTANT_MCP_LIVE_LIMIT
});
const parsed = this.parseExecuteQueryPayload(payload);
if (parsed.error) {
return {
status: "error",
items: [],
evidence: [],
summary: {
enabled: true,
const livePlan = buildLiveMcpCallPlan(route, fragmentText);
const explicitAccountScope = extractAccountScopeFromText(fragmentText);
const accountScope = explicitAccountScope.length > 0
? explicitAccountScope
: livePlan.claim_type === "prove_rbp_tail_state"
? ["97", "20", "25", "26", "44"]
: [];
const callExecutions = [];
const collectedRows = [];
const errors = [];
let fetchedRowsTotal = 0;
let matchedRowsTotal = 0;
for (const call of livePlan.calls) {
const callAccountScope = Array.isArray(call.account_scope_override) && call.account_scope_override.length > 0
? call.account_scope_override
: accountScope;
try {
const payload = await this.fetchJsonWithTimeout(endpoint, {
query: call.query,
limit: config_1.ASSISTANT_MCP_LIVE_LIMIT
});
const parsed = this.parseExecuteQueryPayload(payload);
if (parsed.error) {
errors.push(parsed.error);
callExecutions.push({
call_id: call.call_id,
purpose: call.purpose,
required_for_claim: call.required_for_claim,
status: "error",
fetched_rows: 0,
matched_rows: 0,
returned_rows: 0,
error: parsed.error
});
continue;
}
const matchedRows = this.filterLiveRowsByAccountScope(parsed.rows, callAccountScope);
const rowsForAnswer = callAccountScope.length > 0 ? matchedRows : parsed.rows;
fetchedRowsTotal += parsed.rows.length;
matchedRowsTotal += matchedRows.length;
for (const row of rowsForAnswer) {
collectedRows.push({
...row,
__live_call_id: call.call_id,
__live_call_purpose: call.purpose,
__claim_type: livePlan.claim_type,
__query_subject: livePlan.query_subject,
__account_scope_applied: callAccountScope
});
}
callExecutions.push({
call_id: call.call_id,
purpose: call.purpose,
required_for_claim: call.required_for_claim,
status: rowsForAnswer.length > 0 ? "ok" : "empty",
fetched_rows: parsed.rows.length,
matched_rows: matchedRows.length,
returned_rows: rowsForAnswer.length,
error: null
});
}
catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
errors.push(errorMessage);
callExecutions.push({
call_id: call.call_id,
purpose: call.purpose,
required_for_claim: call.required_for_claim,
status: "error",
route,
channel: config_1.ASSISTANT_MCP_CHANNEL,
proxy: config_1.ASSISTANT_MCP_PROXY_URL,
account_scope: accountScope,
error: parsed.error
},
selection_reason: ["Live MCP probe завершился ошибкой, использован snapshot fallback."],
limitations: ["Live MCP недоступен или вернул ошибку; результат ограничен локальным snapshot."],
errors: [parsed.error]
};
fetched_rows: 0,
matched_rows: 0,
returned_rows: 0,
error: errorMessage
});
}
}
const matchedRows = this.filterLiveRowsByAccountScope(parsed.rows, accountScope);
const rowsForAnswer = matchedRows.length > 0 || accountScope.length === 0 ? matchedRows : parsed.rows;
const items = this.toLiveOverlayItems(rowsForAnswer.slice(0, 12), route);
const evidence = items.slice(0, 8).map((item) => ({
const items = this.toLiveOverlayItems(collectedRows.slice(0, 16), route);
const evidence = items.slice(0, 12).map((item) => ({
source_entity: item.source_entity,
source_id: item.source_id,
source_namespace: "assistant_derived",
period: item.period,
account_debit: item.account_debit,
account_credit: item.account_credit
account_credit: item.account_credit,
source_layer: item.source_layer,
document_context: item.document_context,
relation_pattern_hits: item.relation_pattern_hits,
lifecycle_markers: item.lifecycle_markers,
live_call_id: item.live_call_id,
live_call_purpose: item.live_call_purpose,
claim_type: item.claim_type
}));
const executedRequiredCalls = callExecutions
.filter((item) => item.required_for_claim && item.status !== "error")
.map((item) => item.call_id);
const missingLiveCalls = livePlan.required_live_calls.filter((callId) => !executedRequiredCalls.includes(callId));
const liveRouteExecutionRate = livePlan.required_live_calls.length > 0
? Number((executedRequiredCalls.length / livePlan.required_live_calls.length).toFixed(4))
: 1;
const routeGapReason = missingLiveCalls.length > 0
? "required_live_calls_not_executed"
: livePlan.claim_type && matchedRowsTotal <= 0
? "claim_live_calls_executed_but_zero_matches"
: livePlan.route_gap_reason;
const selectionReason = [
`Live MCP probe: ${parsed.rows.length} rows fetched from 1C register.`,
livePlan.claim_type
? `Claim-bound live path selected for ${livePlan.claim_type}.`
: `Live MCP probe: ${fetchedRowsTotal} rows fetched from 1C register.`,
accountScope.length > 0
? `Account scope filter (${accountScope.join(", ")}) matched ${matchedRows.length} rows.`
? `Account scope filter (${accountScope.join(", ")}) matched ${matchedRowsTotal} rows.`
: "Account scope filter was not applied."
];
const limitations = [
"Live probe использует ограниченный выборочный read-only запрос к 1С."
];
if (missingLiveCalls.length > 0) {
limitations.push(`Required live calls were not executed: ${missingLiveCalls.join(", ")}.`);
}
if (items.length === 0) {
limitations.push("Live probe не вернул строк, релевантных текущему запросу.");
}
if (errors.length > 0) {
limitations.push("Часть live вызовов завершилась ошибкой; включен ограниченный fallback.");
}
const status = items.length > 0 ? "ok" : callExecutions.every((item) => item.status === "error") ? "error" : "empty";
return {
status: items.length > 0 ? "ok" : "empty",
status,
items,
evidence,
summary: {
enabled: true,
status: items.length > 0 ? "ok" : "empty",
status,
route,
channel: config_1.ASSISTANT_MCP_CHANNEL,
proxy: config_1.ASSISTANT_MCP_PROXY_URL,
source_profile: livePlan.claim_type ? "claim_bound_rbp_live_path" : "generic_live_probe",
claim_type: livePlan.claim_type,
query_subject: livePlan.query_subject,
account_scope: accountScope,
fetched_rows: parsed.rows.length,
matched_rows: matchedRows.length,
returned_rows: items.length
fetched_rows: fetchedRowsTotal,
matched_rows: matchedRowsTotal,
returned_rows: items.length,
required_live_calls: livePlan.required_live_calls,
executed_live_calls: callExecutions,
missing_live_calls: missingLiveCalls,
live_route_execution_rate: liveRouteExecutionRate,
route_gap_reason: routeGapReason
},
selection_reason: selectionReason,
limitations,
errors: []
errors
};
}
async fetchJsonWithTimeout(url, body) {
@@ -2249,6 +2477,36 @@ class AssistantDataLayer {
const baseId = `${route}-mcp-${index + 1}`;
const sourceId = periodRaw ? `${baseId}-${periodRaw}` : baseId;
const displayName = registrator || `Live movement row #${index + 1}`;
const accountContext = uniqueStrings([debit, credit].filter((item) => item.length > 0));
const callId = valueAsString(row.__live_call_id ?? "").trim();
const callPurpose = valueAsString(row.__live_call_purpose ?? "").trim();
const claimType = valueAsString(row.__claim_type ?? "").trim() || null;
const querySubject = valueAsString(row.__query_subject ?? "").trim() || null;
const registratorLower = registrator.toLowerCase();
const hasRbpByDocument = /(?:рбп|deferred|списани[ея]\s+рбп)/i.test(registratorLower);
const hasAccount97 = accountContext.some((item) => /^97(?:\.|$)/.test(item));
const hasCloseDoc = /(?:закрыти[ея]\s+месяц|period\s*close|month\s*close|close\s+operation)/i.test(registratorLower) ||
callId.includes("month_close");
const relationPatternHits = uniqueStrings([
"document_to_posting",
hasRbpByDocument || hasAccount97 ? "deferred_expense_to_writeoff" : "",
hasCloseDoc ? "close_operation" : "",
callId.includes("residual") ? "residuals_zero_or_explained" : ""
]);
const documentContext = uniqueStrings([
hasRbpByDocument || hasAccount97 ? "deferred_expense_document" : "",
hasCloseDoc ? "period_close_document" : "",
"posting"
]);
const graphDomainScope = uniqueStrings([
hasRbpByDocument || hasAccount97 ? "deferred_expense" : "",
hasCloseDoc ? "period_close" : ""
]);
const lifecycleMarkers = uniqueStrings([
callId.includes("residual") ? "period_boundary" : "",
callId.includes("residual") ? "tail_state_observed" : "",
hasCloseDoc ? "close_operation" : ""
]);
return {
source_entity: "MCPLiveMovement",
source_id: sourceId,
@@ -2256,6 +2514,16 @@ class AssistantDataLayer {
period: periodRaw || null,
account_debit: debit || null,
account_credit: credit || null,
account_context: accountContext,
document_context: documentContext,
relation_pattern_hits: relationPatternHits,
graph_domain_scope: graphDomainScope,
lifecycle_markers: lifecycleMarkers,
source_namespace: "assistant_derived",
live_call_id: callId || null,
live_call_purpose: callPurpose || null,
claim_type: claimType,
query_subject: querySubject,
amount,
source_layer: "mcp_live_probe",
route
+165
View File
@@ -620,6 +620,159 @@ function toExecutionPlan(routeSummary, normalized, userMessage, requirementByFra
};
});
}
function enrichRbpFragmentForLive(fragmentText, temporalGuard) {
const base = compactWhitespace(String(fragmentText ?? ""));
const hints = ["Списание РБП", "объект РБП", "остаток на конец периода", "счет 97"];
const effective = temporalGuard && typeof temporalGuard === "object" ? temporalGuard.effective_primary_period : null;
if (effective && effective.from && effective.to) {
hints.push(`период ${effective.from}..${effective.to}`);
}
const hintText = hints.filter(Boolean).join(", ");
if (!base) {
return hintText;
}
if (/списани[ея]\s+рбп|счет\s*97|account\s*97|остат/i.test(base)) {
return base;
}
return `${base}; ${hintText}`;
}
function enforceRbpLiveRoutePlan(input) {
if (input.claimType !== "prove_rbp_tail_state") {
return {
executionPlan: input.executionPlan,
audit: null
};
}
const requiredLiveCalls = [
"find_rbp_writeoff_documents_in_period",
"find_rbp_object_movements_account_97",
"find_month_close_entries_linked_to_rbp",
"compute_end_period_residual_by_rbp_object"
];
let routeAdjusted = 0;
let rescuedNoRoute = 0;
const replacedRoutes = [];
const adjustedPlan = input.executionPlan.map((item) => {
if (!item || typeof item !== "object") {
return item;
}
if (item.should_execute !== true && item.no_route_reason === "insufficient_specificity") {
rescuedNoRoute += 1;
routeAdjusted += 1;
return {
...item,
route: "live_mcp_drilldown",
should_execute: true,
no_route_reason: null,
clarification_reason: null,
fragment_text: enrichRbpFragmentForLive(item.fragment_text, input.temporalGuard)
};
}
if (item.should_execute === true && item.route !== "hybrid_store_plus_live" && item.route !== "live_mcp_drilldown") {
routeAdjusted += 1;
if (item.route && item.route !== "no_route") {
replacedRoutes.push(String(item.route));
}
return {
...item,
route: "hybrid_store_plus_live",
fragment_text: enrichRbpFragmentForLive(item.fragment_text, input.temporalGuard)
};
}
if (item.should_execute === true) {
return {
...item,
fragment_text: enrichRbpFragmentForLive(item.fragment_text, input.temporalGuard)
};
}
return item;
});
return {
executionPlan: adjustedPlan,
audit: {
claim_type: "prove_rbp_tail_state",
required_live_calls: requiredLiveCalls,
route_adjustments_applied: routeAdjusted,
rescued_no_route_fragments: rescuedNoRoute,
replaced_routes: Array.from(new Set(replacedRoutes)),
route_gap_reason: routeAdjusted > 0 ? "rbp_claim_bound_live_route_override_applied" : null
}
};
}
function collectRbpLiveRouteAudit(input) {
if (input.claimType !== "prove_rbp_tail_state") {
return null;
}
const required = new Set(Array.isArray(input.planAudit?.required_live_calls) ? input.planAudit.required_live_calls : []);
const executed = [];
const missing = new Set();
const routeGaps = [];
let matchedRowsTotal = 0;
let returnedRowsTotal = 0;
let fetchedRowsTotal = 0;
for (const result of input.retrievalResults) {
if (!result || typeof result !== "object") {
continue;
}
const summary = result.summary && typeof result.summary === "object" ? result.summary : null;
const live = summary && typeof summary.live_mcp === "object" && summary.live_mcp ? summary.live_mcp : null;
if (!live) {
continue;
}
const requiredCalls = Array.isArray(live.required_live_calls) ? live.required_live_calls : [];
for (const callId of requiredCalls) {
required.add(String(callId ?? "").trim());
}
const executedCalls = Array.isArray(live.executed_live_calls) ? live.executed_live_calls : [];
for (const call of executedCalls) {
if (!call || typeof call !== "object") {
continue;
}
executed.push(call);
}
const missingCalls = Array.isArray(live.missing_live_calls) ? live.missing_live_calls : [];
for (const callId of missingCalls) {
const token = String(callId ?? "").trim();
if (token) {
missing.add(token);
}
}
const routeGapReason = String(live.route_gap_reason ?? "").trim();
if (routeGapReason) {
routeGaps.push(routeGapReason);
}
fetchedRowsTotal += Number(live.fetched_rows ?? 0) || 0;
matchedRowsTotal += Number(live.matched_rows ?? 0) || 0;
returnedRowsTotal += Number(live.returned_rows ?? 0) || 0;
}
const requiredList = Array.from(required).filter(Boolean);
const executedList = executed;
const missingFromExecuted = requiredList.filter((callId) => !executedList.some((item) => String(item.call_id ?? "") === callId));
for (const callId of missingFromExecuted) {
missing.add(callId);
}
const missingList = Array.from(missing);
const routeGapReason = missingList.length > 0
? "required_live_calls_not_executed"
: matchedRowsTotal <= 0
? "claim_live_calls_executed_but_zero_matches"
: routeGaps[0] ?? null;
const executionRate = requiredList.length > 0
? Number(((requiredList.length - missingList.length) / requiredList.length).toFixed(4))
: 1;
return {
claim_type: "prove_rbp_tail_state",
required_live_calls: requiredList,
executed_live_calls: executedList,
missing_live_calls: missingList,
route_gap_reason: routeGapReason,
live_route_execution_rate: executionRate,
fetched_rows_total: fetchedRowsTotal,
matched_rows_total: matchedRowsTotal,
returned_rows_total: returnedRowsTotal,
plan_override: input.planAudit ?? null
};
}
function toDebugRoutes(routeSummary) {
if (!routeSummary) {
return [];
@@ -1471,6 +1624,12 @@ class AssistantService {
const resolvedRouteSummary = businessScopeResolution.route_summary_resolved;
const requirementExtraction = extractRequirements(resolvedRouteSummary, normalized.normalized, userMessage);
let executionPlan = toExecutionPlan(resolvedRouteSummary, normalized.normalized, userMessage, requirementExtraction.byFragment);
const rbpRoutePlanEnforcement = enforceRbpLiveRoutePlan({
executionPlan,
claimType: claimAnchorAudit.claim_type,
temporalGuard
});
executionPlan = rbpRoutePlanEnforcement.executionPlan;
executionPlan = (0, assistantRuntimeGuards_1.applyTemporalHintToExecutionPlan)(executionPlan, temporalGuard);
executionPlan = (0, assistantRuntimeGuards_1.applyPolarityHintToExecutionPlan)(executionPlan, domainPolarityGuardInitial);
const retrievalCalls = [];
@@ -1553,6 +1712,11 @@ class AssistantService {
userMessage
});
retrievalResults = evidenceGateResult.retrievalResults;
const rbpLiveRouteAudit = collectRbpLiveRouteAudit({
claimType: claimAnchorAudit.claim_type,
retrievalResults,
planAudit: rbpRoutePlanEnforcement.audit
});
const coverageEvaluation = evaluateCoverage(requirementExtraction.requirements, retrievalResults);
const groundingCheckBase = checkGrounding(userMessage, coverageEvaluation.requirements, coverageEvaluation.coverage, retrievalResults);
const groundedAnswerEligibilityGuard = (0, assistantRuntimeGuards_1.evaluateGroundedAnswerEligibility)({
@@ -1665,6 +1829,7 @@ class AssistantService {
claim_anchor_audit: claimAnchorAudit,
targeted_evidence_acquisition: targetedEvidenceResult.audit,
evidence_admissibility_gate: evidenceGateResult.audit,
...(rbpLiveRouteAudit ? { rbp_live_route_audit: rbpLiveRouteAudit } : {}),
eligibility_time_basis: groundedAnswerEligibilityGuard.eligibility_time_basis,
grounded_answer_eligibility_guard: groundedAnswerEligibilityGuard,
...(followupBinding.usage ? { followup_state_usage: followupBinding.usage } : {}),
@@ -329,7 +329,15 @@ function requiredChecksByClaim(claimType: ClaimType): string[] {
if (claimType === "prove_month_close_state") {
return ["close_operation_found", "distribution_step_found", "residual_tail_found"];
}
return ["rbp_writeoff_lifecycle_confirmed", "residual_tail_found", "close_contradiction_or_normal_residual"];
return [
"rbp_writeoff_document_found",
"rbp_object_identified",
"rbp_movement_found",
"rbp_period_end_residual_found",
"rbp_writeoff_lifecycle_confirmed",
"residual_tail_found",
"close_contradiction_or_normal_residual"
];
}
function detectChecksForCorpus(corpus: string, claimType: ClaimType, anchors: Record<string, string[]>): string[] {
@@ -351,6 +359,10 @@ function detectChecksForCorpus(corpus: string, claimType: ClaimType, anchors: Re
const hasRbp = /(?:\brbp\b|рбп|account\s*97|счет\s*97|deferred)/i.test(corpus);
const hasResidual = /(?:tail|остат|незакры|overdue|period_boundary|terminal_state_gap)/i.test(corpus);
const hasContradiction = /(?:contradiction|invalid_transition|normal residual|нормальн)/i.test(corpus);
const hasRbpWriteoffDoc = /(?:списани[ея]\s+рбп|rbp_writeoff|deferred_expense_document|writeoff document)/i.test(corpus);
const hasRbpObject = /(?:rbp[_\s-]?object|объект\s+рбп|analytics|subkonto|расходыбудущихпериодов)/i.test(corpus);
const hasMovement = /(?:movement|движен|хозрасчетный|document_to_posting|posting|проводк)/i.test(corpus);
const hasPeriodEndResidual = /(?:period_boundary|end_period|2020-07-31|остат)/i.test(corpus);
if (claimType === "prove_settlement_closure_state") {
if (hasPayment) checks.add("payment_document_found");
@@ -377,6 +389,10 @@ function detectChecksForCorpus(corpus: string, claimType: ClaimType, anchors: Re
if (hasDistribution) checks.add("distribution_step_found");
if (hasResidual) checks.add("residual_tail_found");
} else {
if (hasRbpWriteoffDoc || (hasRbp && hasDistribution)) checks.add("rbp_writeoff_document_found");
if (hasRbpObject || hasRbp) checks.add("rbp_object_identified");
if (hasMovement) checks.add("rbp_movement_found");
if (hasPeriodEndResidual || hasResidual) checks.add("rbp_period_end_residual_found");
if (hasRbp && hasDistribution) checks.add("rbp_writeoff_lifecycle_confirmed");
if (hasResidual) checks.add("residual_tail_found");
if (hasContradiction || hasClose) checks.add("close_contradiction_or_normal_residual");
@@ -65,6 +65,31 @@ interface LiveMcpOverlay {
errors: string[];
}
interface LiveMcpCallPlan {
claim_type: string | null;
query_subject: string;
required_live_calls: string[];
calls: Array<{
call_id: string;
purpose: string;
query: string;
required_for_claim: boolean;
account_scope_override?: string[];
}>;
route_gap_reason: string | null;
}
interface LiveMcpCallExecution {
call_id: string;
purpose: string;
required_for_claim: boolean;
status: "ok" | "empty" | "error";
fetched_rows: number;
matched_rows: number;
returned_rows: number;
error?: string | null;
}
type BroadnessLevel = "low" | "medium" | "high";
interface BroadQueryAssessment {
@@ -150,6 +175,28 @@ const MCP_LIVE_MOVEMENTS_QUERY_TEMPLATE = `
Движения.Период УБЫВ
`;
const MCP_LIVE_MOVEMENTS_BY_PERIOD_QUERY_TEMPLATE = `
ВЫБРАТЬ ПЕРВЫЕ __LIMIT__
Движения.Период КАК Период,
ПРЕДСТАВЛЕНИЕ(Движения.Регистратор) КАК Регистратор,
ПРЕДСТАВЛЕНИЕ(Движения.СчетДт) КАК СчетДт,
ПРЕДСТАВЛЕНИЕ(Движения.СчетКт) КАК СчетКт,
Движения.Сумма КАК Сумма
ИЗ
РегистрБухгалтерии.Хозрасчетный КАК Движения
ГДЕ
Движения.Период МЕЖДУ __FROM_DATETIME__ И __TO_DATETIME__
УПОРЯДОЧИТЬ ПО
Движения.Период УБЫВ
`;
const RBP_REQUIRED_LIVE_CALLS = [
"find_rbp_writeoff_documents_in_period",
"find_rbp_object_movements_account_97",
"find_month_close_entries_linked_to_rbp",
"compute_end_period_residual_by_rbp_object"
];
function pushUniqueLine(target: string[], line: string): void {
if (!target.includes(line)) {
target.push(line);
@@ -181,6 +228,141 @@ function parseFiniteNumber(value: unknown): number | null {
return null;
}
function formatIsoDateUtc(date: Date): string {
const year = date.getUTCFullYear();
const month = String(date.getUTCMonth() + 1).padStart(2, "0");
const day = String(date.getUTCDate()).padStart(2, "0");
return `${year}-${month}-${day}`;
}
function monthEndFromIso(isoDate: string): string | null {
const match = String(isoDate ?? "").match(/^(\d{4})-(\d{2})-(\d{2})$/);
if (!match) {
return null;
}
const year = Number(match[1]);
const month = Number(match[2]);
if (!Number.isFinite(year) || !Number.isFinite(month)) {
return null;
}
const end = new Date(Date.UTC(year, month, 0));
return formatIsoDateUtc(end);
}
function shiftIsoDate(isoDate: string, deltaDays: number): string | null {
const match = String(isoDate ?? "").match(/^(\d{4})-(\d{2})-(\d{2})$/);
if (!match) {
return null;
}
const date = new Date(Date.UTC(Number(match[1]), Number(match[2]) - 1, Number(match[3])));
if (Number.isNaN(date.getTime())) {
return null;
}
date.setUTCDate(date.getUTCDate() + deltaDays);
return formatIsoDateUtc(date);
}
function toDateTimeExpr(isoDate: string, endOfDay: boolean): string | null {
const match = String(isoDate ?? "").match(/^(\d{4})-(\d{2})-(\d{2})$/);
if (!match) {
return null;
}
const year = Number(match[1]);
const month = Number(match[2]);
const day = Number(match[3]);
if (!Number.isFinite(year) || !Number.isFinite(month) || !Number.isFinite(day)) {
return null;
}
const hour = endOfDay ? 23 : 0;
const minute = endOfDay ? 59 : 0;
const second = endOfDay ? 59 : 0;
return `ДАТАВРЕМЯ(${year}, ${month}, ${day}, ${hour}, ${minute}, ${second})`;
}
function buildLiveRangeQuery(fromIso: string, toIso: string, limit: number): string {
const fromExpr = toDateTimeExpr(fromIso, false);
const toExpr = toDateTimeExpr(toIso, true);
if (!fromExpr || !toExpr) {
return MCP_LIVE_MOVEMENTS_QUERY_TEMPLATE.replace("__LIMIT__", String(limit));
}
return MCP_LIVE_MOVEMENTS_BY_PERIOD_QUERY_TEMPLATE.replace("__LIMIT__", String(limit))
.replace("__FROM_DATETIME__", fromExpr)
.replace("__TO_DATETIME__", toExpr);
}
function hasRbpSignal(text: string): boolean {
return /(?:\brbp\b|рбп|расходы\s+будущих\s+периодов|deferred|writeoff|списани[ея]\s+рбп|account\s*97|счет\s*97)/i.test(
String(text ?? "").toLowerCase()
);
}
function buildLiveMcpCallPlan(route: string, fragmentText: string): LiveMcpCallPlan {
const semanticProfile = buildSemanticRetrievalProfile(fragmentText);
const rbpClaim =
hasRbpSignal(fragmentText) ||
semanticProfile.query_subject === "deferred_expense_lifecycle_anomaly" ||
semanticProfile.domain_scope.includes("deferred_expense");
if (!rbpClaim) {
return {
claim_type: null,
query_subject: semanticProfile.query_subject,
required_live_calls: [],
calls: [
{
call_id: "generic_accounting_register_probe",
purpose: "live_overlay_probe",
query: MCP_LIVE_MOVEMENTS_QUERY_TEMPLATE.replace("__LIMIT__", String(ASSISTANT_MCP_LIVE_LIMIT)),
required_for_claim: false
}
],
route_gap_reason: null
};
}
const periodScope = inferPeriodScope(fragmentText);
const primaryFrom = periodScope.from ?? "2020-07-01";
const primaryTo = periodScope.to ?? monthEndFromIso(primaryFrom) ?? "2020-07-31";
const carryFrom = shiftIsoDate(primaryFrom, -31) ?? primaryFrom;
const carryTo = shiftIsoDate(primaryTo, 31) ?? primaryTo;
return {
claim_type: "prove_rbp_tail_state",
query_subject: "deferred_expense_lifecycle_anomaly",
required_live_calls: [...RBP_REQUIRED_LIVE_CALLS],
calls: [
{
call_id: "find_rbp_writeoff_documents_in_period",
purpose: "seed_writeoff_documents",
query: buildLiveRangeQuery(primaryFrom, primaryTo, ASSISTANT_MCP_LIVE_LIMIT),
required_for_claim: true,
account_scope_override: ["97", "20", "25", "26", "44"]
},
{
call_id: "find_rbp_object_movements_account_97",
purpose: "collect_rbp_object_movements",
query: buildLiveRangeQuery(primaryFrom, primaryTo, ASSISTANT_MCP_LIVE_LIMIT),
required_for_claim: true,
account_scope_override: ["97"]
},
{
call_id: "find_month_close_entries_linked_to_rbp",
purpose: "link_month_close_to_rbp",
query: buildLiveRangeQuery(primaryFrom, primaryTo, ASSISTANT_MCP_LIVE_LIMIT),
required_for_claim: true,
account_scope_override: ["97", "20", "25", "26", "44"]
},
{
call_id: "compute_end_period_residual_by_rbp_object",
purpose: "collect_residual_tail_signals",
query: buildLiveRangeQuery(carryFrom, carryTo, ASSISTANT_MCP_LIVE_LIMIT),
required_for_claim: true,
account_scope_override: ["97", "20", "25", "26", "44"]
}
],
route_gap_reason: null
};
}
function detectBroadQuery(fragmentText: string, route: string): BroadQueryAssessment {
const text = String(fragmentText ?? "").trim();
const lower = text.toLowerCase();
@@ -2587,79 +2769,170 @@ export class AssistantDataLayer {
}
private async fetchLiveMcpOverlay(route: string, fragmentText: string): Promise<LiveMcpOverlay> {
const accountScope = extractAccountScopeFromText(fragmentText);
const endpoint = this.buildMcpUrl("/api/execute_query");
const query = MCP_LIVE_MOVEMENTS_QUERY_TEMPLATE.replace("__LIMIT__", String(ASSISTANT_MCP_LIVE_LIMIT));
const livePlan = buildLiveMcpCallPlan(route, fragmentText);
const explicitAccountScope = extractAccountScopeFromText(fragmentText);
const accountScope =
explicitAccountScope.length > 0
? explicitAccountScope
: livePlan.claim_type === "prove_rbp_tail_state"
? ["97", "20", "25", "26", "44"]
: [];
const callExecutions: LiveMcpCallExecution[] = [];
const collectedRows: Array<Record<string, unknown>> = [];
const errors: string[] = [];
let fetchedRowsTotal = 0;
let matchedRowsTotal = 0;
const payload = await this.fetchJsonWithTimeout(endpoint, {
query,
limit: ASSISTANT_MCP_LIVE_LIMIT
});
for (const call of livePlan.calls) {
const callAccountScope =
Array.isArray(call.account_scope_override) && call.account_scope_override.length > 0
? call.account_scope_override
: accountScope;
try {
const payload = await this.fetchJsonWithTimeout(endpoint, {
query: call.query,
limit: ASSISTANT_MCP_LIVE_LIMIT
});
const parsed = this.parseExecuteQueryPayload(payload);
if (parsed.error) {
errors.push(parsed.error);
callExecutions.push({
call_id: call.call_id,
purpose: call.purpose,
required_for_claim: call.required_for_claim,
status: "error",
fetched_rows: 0,
matched_rows: 0,
returned_rows: 0,
error: parsed.error
});
continue;
}
const parsed = this.parseExecuteQueryPayload(payload);
if (parsed.error) {
return {
status: "error",
items: [],
evidence: [],
summary: {
enabled: true,
const matchedRows = this.filterLiveRowsByAccountScope(parsed.rows, callAccountScope);
const rowsForAnswer = callAccountScope.length > 0 ? matchedRows : parsed.rows;
fetchedRowsTotal += parsed.rows.length;
matchedRowsTotal += matchedRows.length;
for (const row of rowsForAnswer) {
collectedRows.push({
...row,
__live_call_id: call.call_id,
__live_call_purpose: call.purpose,
__claim_type: livePlan.claim_type,
__query_subject: livePlan.query_subject,
__account_scope_applied: callAccountScope
});
}
callExecutions.push({
call_id: call.call_id,
purpose: call.purpose,
required_for_claim: call.required_for_claim,
status: rowsForAnswer.length > 0 ? "ok" : "empty",
fetched_rows: parsed.rows.length,
matched_rows: matchedRows.length,
returned_rows: rowsForAnswer.length,
error: null
});
} catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
errors.push(errorMessage);
callExecutions.push({
call_id: call.call_id,
purpose: call.purpose,
required_for_claim: call.required_for_claim,
status: "error",
route,
channel: ASSISTANT_MCP_CHANNEL,
proxy: ASSISTANT_MCP_PROXY_URL,
account_scope: accountScope,
error: parsed.error
},
selection_reason: ["Live MCP probe завершился ошибкой, использован snapshot fallback."],
limitations: ["Live MCP недоступен или вернул ошибку; результат ограничен локальным snapshot."],
errors: [parsed.error]
};
fetched_rows: 0,
matched_rows: 0,
returned_rows: 0,
error: errorMessage
});
}
}
const matchedRows = this.filterLiveRowsByAccountScope(parsed.rows, accountScope);
const rowsForAnswer = matchedRows.length > 0 || accountScope.length === 0 ? matchedRows : parsed.rows;
const items = this.toLiveOverlayItems(rowsForAnswer.slice(0, 12), route);
const evidence = items.slice(0, 8).map((item) => ({
const items = this.toLiveOverlayItems(collectedRows.slice(0, 16), route);
const evidence = items.slice(0, 12).map((item) => ({
source_entity: item.source_entity,
source_id: item.source_id,
source_namespace: "assistant_derived",
period: item.period,
account_debit: item.account_debit,
account_credit: item.account_credit
account_credit: item.account_credit,
source_layer: item.source_layer,
document_context: item.document_context,
relation_pattern_hits: item.relation_pattern_hits,
lifecycle_markers: item.lifecycle_markers,
live_call_id: item.live_call_id,
live_call_purpose: item.live_call_purpose,
claim_type: item.claim_type
}));
const executedRequiredCalls = callExecutions
.filter((item) => item.required_for_claim && item.status !== "error")
.map((item) => item.call_id);
const missingLiveCalls = livePlan.required_live_calls.filter((callId) => !executedRequiredCalls.includes(callId));
const liveRouteExecutionRate =
livePlan.required_live_calls.length > 0
? Number((executedRequiredCalls.length / livePlan.required_live_calls.length).toFixed(4))
: 1;
const routeGapReason =
missingLiveCalls.length > 0
? "required_live_calls_not_executed"
: livePlan.claim_type && matchedRowsTotal <= 0
? "claim_live_calls_executed_but_zero_matches"
: livePlan.route_gap_reason;
const selectionReason = [
`Live MCP probe: ${parsed.rows.length} rows fetched from 1C register.`,
livePlan.claim_type
? `Claim-bound live path selected for ${livePlan.claim_type}.`
: `Live MCP probe: ${fetchedRowsTotal} rows fetched from 1C register.`,
accountScope.length > 0
? `Account scope filter (${accountScope.join(", ")}) matched ${matchedRows.length} rows.`
? `Account scope filter (${accountScope.join(", ")}) matched ${matchedRowsTotal} rows.`
: "Account scope filter was not applied."
];
const limitations: string[] = [
"Live probe использует ограниченный выборочный read-only запрос к 1С."
];
if (missingLiveCalls.length > 0) {
limitations.push(`Required live calls were not executed: ${missingLiveCalls.join(", ")}.`);
}
if (items.length === 0) {
limitations.push("Live probe не вернул строк, релевантных текущему запросу.");
}
if (errors.length > 0) {
limitations.push("Часть live вызовов завершилась ошибкой; включен ограниченный fallback.");
}
const status: LiveMcpOverlay["status"] =
items.length > 0 ? "ok" : callExecutions.every((item) => item.status === "error") ? "error" : "empty";
return {
status: items.length > 0 ? "ok" : "empty",
status,
items,
evidence,
summary: {
enabled: true,
status: items.length > 0 ? "ok" : "empty",
status,
route,
channel: ASSISTANT_MCP_CHANNEL,
proxy: ASSISTANT_MCP_PROXY_URL,
source_profile: livePlan.claim_type ? "claim_bound_rbp_live_path" : "generic_live_probe",
claim_type: livePlan.claim_type,
query_subject: livePlan.query_subject,
account_scope: accountScope,
fetched_rows: parsed.rows.length,
matched_rows: matchedRows.length,
returned_rows: items.length
fetched_rows: fetchedRowsTotal,
matched_rows: matchedRowsTotal,
returned_rows: items.length,
required_live_calls: livePlan.required_live_calls,
executed_live_calls: callExecutions,
missing_live_calls: missingLiveCalls,
live_route_execution_rate: liveRouteExecutionRate,
route_gap_reason: routeGapReason
},
selection_reason: selectionReason,
limitations,
errors: []
errors
};
}
@@ -2831,6 +3104,37 @@ export class AssistantDataLayer {
const baseId = `${route}-mcp-${index + 1}`;
const sourceId = periodRaw ? `${baseId}-${periodRaw}` : baseId;
const displayName = registrator || `Live movement row #${index + 1}`;
const accountContext = uniqueStrings([debit, credit].filter((item) => item.length > 0));
const callId = valueAsString(row.__live_call_id ?? "").trim();
const callPurpose = valueAsString(row.__live_call_purpose ?? "").trim();
const claimType = valueAsString(row.__claim_type ?? "").trim() || null;
const querySubject = valueAsString(row.__query_subject ?? "").trim() || null;
const registratorLower = registrator.toLowerCase();
const hasRbpByDocument = /(?:рбп|deferred|списани[ея]\s+рбп)/i.test(registratorLower);
const hasAccount97 = accountContext.some((item) => /^97(?:\.|$)/.test(item));
const hasCloseDoc =
/(?:закрыти[ея]\s+месяц|period\s*close|month\s*close|close\s+operation)/i.test(registratorLower) ||
callId.includes("month_close");
const relationPatternHits = uniqueStrings([
"document_to_posting",
hasRbpByDocument || hasAccount97 ? "deferred_expense_to_writeoff" : "",
hasCloseDoc ? "close_operation" : "",
callId.includes("residual") ? "residuals_zero_or_explained" : ""
]);
const documentContext = uniqueStrings([
hasRbpByDocument || hasAccount97 ? "deferred_expense_document" : "",
hasCloseDoc ? "period_close_document" : "",
"posting"
]);
const graphDomainScope = uniqueStrings([
hasRbpByDocument || hasAccount97 ? "deferred_expense" : "",
hasCloseDoc ? "period_close" : ""
]);
const lifecycleMarkers = uniqueStrings([
callId.includes("residual") ? "period_boundary" : "",
callId.includes("residual") ? "tail_state_observed" : "",
hasCloseDoc ? "close_operation" : ""
]);
return {
source_entity: "MCPLiveMovement",
source_id: sourceId,
@@ -2838,6 +3142,16 @@ export class AssistantDataLayer {
period: periodRaw || null,
account_debit: debit || null,
account_credit: credit || null,
account_context: accountContext,
document_context: documentContext,
relation_pattern_hits: relationPatternHits,
graph_domain_scope: graphDomainScope,
lifecycle_markers: lifecycleMarkers,
source_namespace: "assistant_derived",
live_call_id: callId || null,
live_call_purpose: callPurpose || null,
claim_type: claimType,
query_subject: querySubject,
amount,
source_layer: "mcp_live_probe",
route
@@ -582,6 +582,159 @@ function toExecutionPlan(routeSummary, normalized, userMessage, requirementByFra
};
});
}
function enrichRbpFragmentForLive(fragmentText, temporalGuard) {
const base = compactWhitespace(String(fragmentText ?? ""));
const hints = ["Списание РБП", "объект РБП", "остаток на конец периода", "счет 97"];
const effective = temporalGuard && typeof temporalGuard === "object" ? temporalGuard.effective_primary_period : null;
if (effective && effective.from && effective.to) {
hints.push(`период ${effective.from}..${effective.to}`);
}
const hintText = hints.filter(Boolean).join(", ");
if (!base) {
return hintText;
}
if (/списани[ея]\s+рбп|счет\s*97|account\s*97|остат/i.test(base)) {
return base;
}
return `${base}; ${hintText}`;
}
function enforceRbpLiveRoutePlan(input) {
if (input.claimType !== "prove_rbp_tail_state") {
return {
executionPlan: input.executionPlan,
audit: null
};
}
const requiredLiveCalls = [
"find_rbp_writeoff_documents_in_period",
"find_rbp_object_movements_account_97",
"find_month_close_entries_linked_to_rbp",
"compute_end_period_residual_by_rbp_object"
];
let routeAdjusted = 0;
let rescuedNoRoute = 0;
const replacedRoutes = [];
const adjustedPlan = input.executionPlan.map((item) => {
if (!item || typeof item !== "object") {
return item;
}
if (item.should_execute !== true && item.no_route_reason === "insufficient_specificity") {
rescuedNoRoute += 1;
routeAdjusted += 1;
return {
...item,
route: "live_mcp_drilldown",
should_execute: true,
no_route_reason: null,
clarification_reason: null,
fragment_text: enrichRbpFragmentForLive(item.fragment_text, input.temporalGuard)
};
}
if (item.should_execute === true && item.route !== "hybrid_store_plus_live" && item.route !== "live_mcp_drilldown") {
routeAdjusted += 1;
if (item.route && item.route !== "no_route") {
replacedRoutes.push(String(item.route));
}
return {
...item,
route: "hybrid_store_plus_live",
fragment_text: enrichRbpFragmentForLive(item.fragment_text, input.temporalGuard)
};
}
if (item.should_execute === true) {
return {
...item,
fragment_text: enrichRbpFragmentForLive(item.fragment_text, input.temporalGuard)
};
}
return item;
});
return {
executionPlan: adjustedPlan,
audit: {
claim_type: "prove_rbp_tail_state",
required_live_calls: requiredLiveCalls,
route_adjustments_applied: routeAdjusted,
rescued_no_route_fragments: rescuedNoRoute,
replaced_routes: Array.from(new Set(replacedRoutes)),
route_gap_reason: routeAdjusted > 0 ? "rbp_claim_bound_live_route_override_applied" : null
}
};
}
function collectRbpLiveRouteAudit(input) {
if (input.claimType !== "prove_rbp_tail_state") {
return null;
}
const required = new Set(Array.isArray(input.planAudit?.required_live_calls) ? input.planAudit.required_live_calls : []);
const executed = [];
const missing = new Set();
const routeGaps = [];
let matchedRowsTotal = 0;
let returnedRowsTotal = 0;
let fetchedRowsTotal = 0;
for (const result of input.retrievalResults) {
if (!result || typeof result !== "object") {
continue;
}
const summary = result.summary && typeof result.summary === "object" ? result.summary : null;
const live = summary && typeof summary.live_mcp === "object" && summary.live_mcp ? summary.live_mcp : null;
if (!live) {
continue;
}
const requiredCalls = Array.isArray(live.required_live_calls) ? live.required_live_calls : [];
for (const callId of requiredCalls) {
required.add(String(callId ?? "").trim());
}
const executedCalls = Array.isArray(live.executed_live_calls) ? live.executed_live_calls : [];
for (const call of executedCalls) {
if (!call || typeof call !== "object") {
continue;
}
executed.push(call);
}
const missingCalls = Array.isArray(live.missing_live_calls) ? live.missing_live_calls : [];
for (const callId of missingCalls) {
const token = String(callId ?? "").trim();
if (token) {
missing.add(token);
}
}
const routeGapReason = String(live.route_gap_reason ?? "").trim();
if (routeGapReason) {
routeGaps.push(routeGapReason);
}
fetchedRowsTotal += Number(live.fetched_rows ?? 0) || 0;
matchedRowsTotal += Number(live.matched_rows ?? 0) || 0;
returnedRowsTotal += Number(live.returned_rows ?? 0) || 0;
}
const requiredList = Array.from(required).filter(Boolean);
const executedList = executed;
const missingFromExecuted = requiredList.filter((callId) => !executedList.some((item) => String(item.call_id ?? "") === callId));
for (const callId of missingFromExecuted) {
missing.add(callId);
}
const missingList = Array.from(missing);
const routeGapReason = missingList.length > 0
? "required_live_calls_not_executed"
: matchedRowsTotal <= 0
? "claim_live_calls_executed_but_zero_matches"
: routeGaps[0] ?? null;
const executionRate = requiredList.length > 0
? Number(((requiredList.length - missingList.length) / requiredList.length).toFixed(4))
: 1;
return {
claim_type: "prove_rbp_tail_state",
required_live_calls: requiredList,
executed_live_calls: executedList,
missing_live_calls: missingList,
route_gap_reason: routeGapReason,
live_route_execution_rate: executionRate,
fetched_rows_total: fetchedRowsTotal,
matched_rows_total: matchedRowsTotal,
returned_rows_total: returnedRowsTotal,
plan_override: input.planAudit ?? null
};
}
function toDebugRoutes(routeSummary) {
if (!routeSummary) {
return [];
@@ -1433,6 +1586,12 @@ export class AssistantService {
const resolvedRouteSummary = businessScopeResolution.route_summary_resolved;
const requirementExtraction = extractRequirements(resolvedRouteSummary, normalized.normalized, userMessage);
let executionPlan = toExecutionPlan(resolvedRouteSummary, normalized.normalized, userMessage, requirementExtraction.byFragment);
const rbpRoutePlanEnforcement = enforceRbpLiveRoutePlan({
executionPlan,
claimType: claimAnchorAudit.claim_type,
temporalGuard
});
executionPlan = rbpRoutePlanEnforcement.executionPlan;
executionPlan = (0, assistantRuntimeGuards_1.applyTemporalHintToExecutionPlan)(executionPlan, temporalGuard);
executionPlan = (0, assistantRuntimeGuards_1.applyPolarityHintToExecutionPlan)(executionPlan, domainPolarityGuardInitial);
const retrievalCalls = [];
@@ -1515,6 +1674,11 @@ export class AssistantService {
userMessage
});
retrievalResults = evidenceGateResult.retrievalResults;
const rbpLiveRouteAudit = collectRbpLiveRouteAudit({
claimType: claimAnchorAudit.claim_type,
retrievalResults,
planAudit: rbpRoutePlanEnforcement.audit
});
const coverageEvaluation = evaluateCoverage(requirementExtraction.requirements, retrievalResults);
const groundingCheckBase = checkGrounding(userMessage, coverageEvaluation.requirements, coverageEvaluation.coverage, retrievalResults);
const groundedAnswerEligibilityGuard = (0, assistantRuntimeGuards_1.evaluateGroundedAnswerEligibility)({
@@ -1627,6 +1791,7 @@ export class AssistantService {
claim_anchor_audit: claimAnchorAudit,
targeted_evidence_acquisition: targetedEvidenceResult.audit,
evidence_admissibility_gate: evidenceGateResult.audit,
...(rbpLiveRouteAudit ? { rbp_live_route_audit: rbpLiveRouteAudit } : {}),
eligibility_time_basis: groundedAnswerEligibilityGuard.eligibility_time_basis,
grounded_answer_eligibility_guard: groundedAnswerEligibilityGuard,
...(followupBinding.usage ? { followup_state_usage: followupBinding.usage } : {}),
@@ -207,6 +207,19 @@ export interface GroundedAnswerEligibilityGuardDebug {
reason_codes: string[];
}
export interface RbpLiveRouteAuditDebug {
claim_type: "prove_rbp_tail_state";
required_live_calls: string[];
executed_live_calls: Array<Record<string, unknown>>;
missing_live_calls: string[];
route_gap_reason: string | null;
live_route_execution_rate: number;
fetched_rows_total: number;
matched_rows_total: number;
returned_rows_total: number;
plan_override: Record<string, unknown> | null;
}
export interface AssistantMessageRequestPayload {
session_id?: string;
user_message?: string;
@@ -302,6 +315,7 @@ export interface AssistantDebugPayload {
claim_anchor_audit?: ClaimBoundAnchorAuditDebug;
targeted_evidence_acquisition?: TargetedEvidenceAcquisitionDebug;
evidence_admissibility_gate?: EvidenceAdmissibilityGateDebug;
rbp_live_route_audit?: RbpLiveRouteAuditDebug;
eligibility_time_basis?: GroundedAnswerEligibilityGuardDebug["eligibility_time_basis"];
grounded_answer_eligibility_guard?: GroundedAnswerEligibilityGuardDebug;
followup_state_usage?: FollowupStateUsageDebug;
@@ -122,6 +122,86 @@ describe.sequential("assistant MCP runtime bridge", () => {
const summary = result.summary as Record<string, unknown>;
const liveSummary = summary.live_mcp as Record<string, unknown>;
expect(liveSummary.status).toBe("error");
expect(result.limitations.some((item) => item.includes("Live MCP"))).toBe(true);
expect(result.limitations.some((item) => /live/i.test(item))).toBe(true);
});
it("does not inject out-of-scope rows when account filter matched zero", async () => {
process.env[MCP_FLAG] = "1";
process.env[MCP_PROXY] = "http://127.0.0.1:6003";
process.env[MCP_CHANNEL] = "default";
const payload = JSON.stringify({
success: true,
data: [
{
Период: "2026-03-01T00:00:00",
Регистратор: "Операция бухгалтерская 0002",
СчетДт: "91.02",
СчетКт: "51",
Сумма: 900
}
]
});
const fetchMock = vi.fn(async () => new Response(payload, { status: 200 }));
vi.stubGlobal("fetch", fetchMock);
const { AssistantDataLayer } = await import("../src/services/assistantDataLayer");
const dataLayer = new AssistantDataLayer(createSnapshotRoot());
const result = await dataLayer.executeRouteRuntime("hybrid_store_plus_live", "Check account 60 only");
const summary = result.summary as Record<string, unknown>;
const liveSummary = summary.live_mcp as Record<string, unknown>;
expect(fetchMock).toHaveBeenCalledTimes(1);
expect(Array.isArray(liveSummary.account_scope)).toBe(true);
expect((liveSummary.account_scope as string[]).some((item) => String(item).includes("60"))).toBe(true);
expect(liveSummary.matched_rows).toBe(0);
expect(liveSummary.returned_rows).toBe(0);
expect(result.items).toHaveLength(0);
});
it("uses claim-bound live call sequence for RBP lifecycle query", async () => {
process.env[MCP_FLAG] = "1";
process.env[MCP_PROXY] = "http://127.0.0.1:6003";
process.env[MCP_CHANNEL] = "default";
const payload = JSON.stringify({
success: true,
data: [
{
Период: "2020-07-31T00:00:00",
Регистратор: "Списание РБП за Июль 2020",
СчетДт: "20.01",
СчетКт: "97.01",
Сумма: 5000
},
{
Период: "2020-07-31T00:00:00",
Регистратор: "Закрытие месяца Июль 2020",
СчетДт: "97.01",
СчетКт: "20.01",
Сумма: 5000
}
]
});
const fetchMock = vi.fn(async () => new Response(payload, { status: 200 }));
vi.stubGlobal("fetch", fetchMock);
const { AssistantDataLayer } = await import("../src/services/assistantDataLayer");
const dataLayer = new AssistantDataLayer(createSnapshotRoot());
const result = await dataLayer.executeRouteRuntime(
"hybrid_store_plus_live",
"31 июля прошло Списание РБП за июль 2020 на 5000. Есть ли хвост РБП?"
);
expect(fetchMock).toHaveBeenCalledTimes(4);
const summary = result.summary as Record<string, unknown>;
const liveSummary = summary.live_mcp as Record<string, unknown>;
expect(liveSummary.claim_type).toBe("prove_rbp_tail_state");
expect(Array.isArray(liveSummary.required_live_calls)).toBe(true);
expect((liveSummary.required_live_calls as unknown[]).length).toBe(4);
expect(Array.isArray(liveSummary.executed_live_calls)).toBe(true);
expect((liveSummary.executed_live_calls as unknown[]).length).toBe(4);
expect(liveSummary.matched_rows).toBeGreaterThan(0);
expect(result.items.some((item) => Array.isArray((item as Record<string, unknown>).relation_pattern_hits))).toBe(true);
});
});