import { describe, expect, it, vi } from "vitest"; import { planAssistantMcpDiscovery } from "../src/services/assistantMcpDiscoveryPlanner"; import { executeAssistantMcpDiscoveryPilot } from "../src/services/assistantMcpDiscoveryPilotExecutor"; function buildDeps(rows: Array>, error: string | null = null) { return { executeAddressMcpQuery: vi.fn(async () => ({ fetched_rows: rows.length, matched_rows: error ? 0 : rows.length, raw_rows: rows, rows: error ? [] : rows, error })) }; } function buildSequentialDeps(results: Array<{ rows: Array>; error?: string | null }>) { const executeAddressMcpQuery = vi.fn(async () => { const next = results.shift() ?? { rows: [] }; const rows = next.rows; const error = next.error ?? null; return { fetched_rows: rows.length, matched_rows: error ? 0 : rows.length, raw_rows: rows, rows: error ? [] : rows, error }; }); return { executeAddressMcpQuery }; } function buildMetadataDeps(rows: Array>, error: string | null = null) { return { executeAddressMcpMetadata: vi.fn(async () => ({ fetched_rows: error ? 0 : rows.length, raw_rows: error ? [] : rows, rows: error ? [] : rows, error })) }; } describe("assistant MCP discovery pilot executor", () => { it("executes only the lifecycle query_documents primitive through injected MCP deps", async () => { const planner = planAssistantMcpDiscovery({ turnMeaning: { asked_domain_family: "counterparty_lifecycle", asked_action_family: "activity_duration", explicit_entity_candidates: ["SVK"] } }); const deps = buildDeps([ { Период: "2020-01-15T00:00:00", Регистратор: "CP_CUSTOMER_ACTIVITY_FIRST", Контрагент: "SVK" }, { Период: "2023-12-20T00:00:00", Регистратор: "CP_CUSTOMER_ACTIVITY", Контрагент: "SVK" } ]); const result = await executeAssistantMcpDiscoveryPilot(planner, deps); expect(result.pilot_status).toBe("executed"); expect(result.mcp_execution_performed).toBe(true); expect(result.executed_primitives).toEqual(["query_documents"]); expect(result.skipped_primitives).toEqual(["resolve_entity_reference", "probe_coverage", "explain_evidence_basis"]); expect(result.evidence.evidence_status).toBe("confirmed"); expect(result.evidence.confirmed_facts[0]).toContain("SVK"); expect(result.evidence.inferred_facts[0]).toContain("may be inferred"); expect(result.evidence.unknown_facts).toContain("Legal registration date is not proven by this MCP discovery pilot"); expect(result.source_rows_summary).toBe("2 MCP document rows fetched, 2 matched lifecycle scope"); expect(result.derived_activity_period).toEqual({ first_activity_date: "2020-01-15", latest_activity_date: "2023-12-20", matched_rows: 2, duration_total_months: 47, duration_years: 3, duration_months_remainder: 11, duration_human_ru: "3 года 11 месяцев", inference_basis: "first_and_latest_confirmed_1c_activity_rows" }); expect(result.reason_codes).toContain("pilot_query_documents_mcp_executed"); expect(result.reason_codes).toContain("pilot_derived_activity_period_from_confirmed_rows"); expect(deps.executeAddressMcpQuery).toHaveBeenCalledTimes(1); const call = deps.executeAddressMcpQuery.mock.calls[0]?.[0]; expect(String(call?.query ?? "")).toContain("Документ.ПоступлениеНаРасчетныйСчет"); expect(call?.limit).toBeGreaterThan(0); }); it("does not execute MCP when dry-run still needs clarification", async () => { const planner = planAssistantMcpDiscovery({ turnMeaning: { asked_domain_family: "counterparty_value", asked_action_family: "turnover", explicit_entity_candidates: ["SVK"] } }); const deps = buildDeps([]); const result = await executeAssistantMcpDiscoveryPilot(planner, deps); expect(result.pilot_status).toBe("skipped_needs_clarification"); expect(result.mcp_execution_performed).toBe(false); expect(result.evidence.evidence_status).toBe("insufficient"); expect(deps.executeAddressMcpQuery).not.toHaveBeenCalled(); }); it("executes business overview as a bounded multi-probe bridge over money flow and activity evidence", async () => { const planner = planAssistantMcpDiscovery({ dataNeedGraph: { schema_version: "assistant_data_need_graph_v1", policy_owner: "assistantMcpDiscoveryDataNeedGraph", subject_candidates: [], business_fact_family: "business_overview", action_family: "broad_evaluation", aggregation_need: null, time_scope_need: "all_time_scope", comparison_need: null, ranking_need: null, proof_expectation: "bounded_inference", clarification_gaps: [], decomposition_candidates: [ "collect_scoped_movements", "aggregate_checked_amounts", "aggregate_ranked_axis_values", "fetch_supporting_documents", "probe_coverage", "explain_evidence_basis" ], forbidden_overclaim_flags: ["no_raw_model_claims", "no_profit_or_margin_claim_without_evidence"], reason_codes: ["data_need_graph_built", "data_need_graph_family_business_overview"] }, turnMeaning: { asked_domain_family: "business_overview", asked_action_family: "broad_evaluation", explicit_organization_scope: "ООО Альтернатива Плюс" } }); const deps = buildSequentialDeps([ { rows: [ { Period: "2020-01-15T00:00:00", Amount: 120000, Counterparty: "Клиент А" }, { Period: "2020-02-15T00:00:00", Amount: 80000, Counterparty: "Клиент Б" } ] }, { rows: [ { Period: "2020-01-20T00:00:00", Amount: 150000, Counterparty: "Поставщик А" } ] }, { rows: [ { Период: "2020-01-15T00:00:00", Регистратор: "Поступление 1" }, { Период: "2020-12-15T00:00:00", Регистратор: "Поступление 2" } ] }, { rows: [ { Period: "2020-03-01T00:00:00", Amount: 200000, Item: "Товар А", Counterparty: "Клиент А", СчетКт: "41.01" }, { Period: "2020-02-01T00:00:00", Amount: 120000, Item: "Товар А", Counterparty: "Поставщик А", СчетДт: "41.01" } ] } ]); const result = await executeAssistantMcpDiscoveryPilot(planner, deps); expect(planner.planner_status).toBe("ready_for_execution"); expect(result.pilot_status).toBe("executed"); expect(result.pilot_scope).toBe("business_overview_route_template_v1"); expect(result.mcp_execution_performed).toBe(true); expect(result.executed_primitives).toEqual(["query_movements", "query_documents"]); expect(result.skipped_primitives).toEqual([ "aggregate_by_axis", "probe_coverage", "explain_evidence_basis" ]); expect(result.derived_business_overview).toMatchObject({ organization_scope: "ООО Альтернатива Плюс", incoming_customer_revenue: { total_amount: 200000, rows_with_amount: 2 }, outgoing_supplier_payout: { total_amount: 150000, rows_with_amount: 1 }, net_amount: 50000, net_direction: "net_incoming" }); expect(result.derived_business_overview?.top_customers[0]).toMatchObject({ axis_value: "Клиент А", total_amount: 120000 }); expect(result.derived_business_overview?.top_suppliers[0]).toMatchObject({ axis_value: "Поставщик А", total_amount: 150000 }); expect(result.derived_business_overview?.activity_period?.duration_total_months).toBe(11); expect(result.evidence.confirmed_facts.join("\n")).toContain("В 1С подтверждены входящие поступления"); expect(result.evidence.confirmed_facts.join("\n")).toContain("Самый крупный подтвержденный поставщик"); expect(result.evidence.inferred_facts.join("\n")).toContain("procurement concentration proxy"); expect(result.evidence.unknown_facts).toContain( "Прибыль и маржа этим бизнес-обзором не подтверждены: нужны себестоимость, расходы и закрывающие документы." ); expect(result.reason_codes).toContain("pilot_derived_business_overview_from_confirmed_rows"); expect(result.reason_codes).toContain("pilot_derived_business_overview_top_suppliers_from_confirmed_rows"); expect(deps.executeAddressMcpQuery).toHaveBeenCalledTimes(3); }); it("adds a checked VAT/tax family to business overview only when an explicit period is available", async () => { const planner = planAssistantMcpDiscovery({ dataNeedGraph: { schema_version: "assistant_data_need_graph_v1", policy_owner: "assistantMcpDiscoveryDataNeedGraph", subject_candidates: [], business_fact_family: "business_overview", action_family: "broad_evaluation", aggregation_need: null, time_scope_need: "explicit_period", comparison_need: null, ranking_need: null, proof_expectation: "bounded_inference", clarification_gaps: [], decomposition_candidates: [ "collect_scoped_movements", "aggregate_checked_amounts", "aggregate_ranked_axis_values", "fetch_supporting_documents", "probe_coverage", "explain_evidence_basis" ], forbidden_overclaim_flags: ["no_raw_model_claims", "no_profit_or_margin_claim_without_evidence"], reason_codes: ["data_need_graph_built", "data_need_graph_family_business_overview"] }, turnMeaning: { asked_domain_family: "business_overview", asked_action_family: "broad_evaluation", explicit_organization_scope: "ООО Альтернатива Плюс", explicit_date_scope: "2020" } }); const deps = buildSequentialDeps([ { rows: [ { Period: "2020-01-15T00:00:00", Amount: 120000, Counterparty: "Клиент А" } ] }, { rows: [ { Period: "2020-01-20T00:00:00", Amount: 50000, Counterparty: "Поставщик А" } ] }, { rows: [ { Регистратор: "VAT_BOOK_SALES", СчетДт: "68.02", Сумма: 40000 }, { Регистратор: "VAT_BOOK_PURCHASES", СчетДт: "19", Сумма: 12000 } ] }, { rows: [] }, { rows: [] }, { rows: [] }, { rows: [] }, { rows: [] }, { rows: [ { Период: "2020-01-15T00:00:00", Регистратор: "Поступление 1" }, { Период: "2020-12-15T00:00:00", Регистратор: "Поступление 2" } ] }, { rows: [ { Period: "2020-03-01T00:00:00", Amount: 200000, Item: "Товар А", Counterparty: "Клиент А", AccountKt: "41.01" }, { Period: "2020-02-01T00:00:00", Amount: 120000, Item: "Товар А", Counterparty: "Поставщик А", AccountDt: "41.01" } ] } ]); const result = await executeAssistantMcpDiscoveryPilot(planner, deps); expect(result.pilot_status).toBe("executed"); expect(result.derived_business_overview?.tax_position).toMatchObject({ period_scope: "2020", rows_matched: 2, rows_with_amount: 2, sales_vat_amount: 40000, purchase_vat_amount: 12000, net_vat_amount: 28000, net_vat_direction: "vat_to_pay" }); expect(result.derived_business_overview?.trading_margin_proxy).toMatchObject({ period_scope: "2020", sales_rows_with_amount: 1, purchase_rows_with_amount: 1, sales_revenue: 200000, purchase_cost_proxy: 120000, gross_spread_proxy: 80000, margin_to_revenue_pct: 40 }); expect(result.derived_business_overview?.missing_signal_families).not.toContain("tax_position"); expect(result.derived_business_overview?.missing_signal_families).not.toContain("profit_margin"); expect(result.derived_business_overview?.missing_signal_families).toContain("accounting_profit_margin"); expect(result.evidence.confirmed_facts.join("\n")).toContain("НДС-позиция за 2020 подтверждена"); expect(result.evidence.confirmed_facts.join("\n")).toContain("Торговый margin proxy за 2020"); expect(result.evidence.unknown_facts.join("\n")).toContain("Чистая прибыль"); expect(result.evidence.unknown_facts.join("\n")).not.toContain("Налоговая/VAT-позиция этим бизнес-обзором не подтверждена"); expect(result.reason_codes).toContain("pilot_business_overview_tax_query_mcp_executed"); expect(result.reason_codes).toContain("pilot_derived_business_overview_tax_position_from_confirmed_rows"); expect(result.reason_codes).toContain("pilot_business_overview_trading_margin_query_mcp_executed"); expect(result.reason_codes).toContain("pilot_derived_business_overview_trading_margin_proxy_from_confirmed_rows"); expect(deps.executeAddressMcpQuery).toHaveBeenCalledTimes(10); const taxCall = deps.executeAddressMcpQuery.mock.calls[2]?.[0]; const tradingMarginCall = deps.executeAddressMcpQuery.mock.calls[9]?.[0]; expect(String(taxCall?.query ?? "")).toContain("НДСЗаписиКнигиПродаж"); expect(String(taxCall?.query ?? "")).toContain("НДСЗаписиКнигиПокупок"); expect(String(tradingMarginCall?.query ?? "")).toContain("Документ.РеализацияТоваровУслуг.Товары"); expect(String(tradingMarginCall?.query ?? "")).toContain("Документ.ПоступлениеТоваровУслуг.Товары"); }); it("keeps negative trading-margin proxy signed instead of reporting an absolute spread", async () => { const planner = planAssistantMcpDiscovery({ dataNeedGraph: { schema_version: "assistant_data_need_graph_v1", policy_owner: "assistantMcpDiscoveryDataNeedGraph", subject_candidates: [], business_fact_family: "business_overview", action_family: "broad_evaluation", aggregation_need: null, time_scope_need: "explicit_period", comparison_need: null, ranking_need: null, proof_expectation: "bounded_inference", clarification_gaps: [], decomposition_candidates: [ "collect_scoped_movements", "aggregate_checked_amounts", "aggregate_ranked_axis_values", "fetch_supporting_documents", "probe_coverage", "explain_evidence_basis" ], forbidden_overclaim_flags: ["no_raw_model_claims", "no_profit_or_margin_claim_without_evidence"], reason_codes: ["data_need_graph_built", "data_need_graph_family_business_overview"] }, turnMeaning: { asked_domain_family: "business_overview", asked_action_family: "broad_evaluation", explicit_organization_scope: "ООО Альтернатива Плюс", explicit_date_scope: "2020" } }); const deps = buildSequentialDeps([ { rows: [] }, { rows: [] }, { rows: [] }, { rows: [] }, { rows: [] }, { rows: [] }, { rows: [] }, { rows: [] }, { rows: [] }, { rows: [ { Period: "2020-03-01T00:00:00", Amount: 100000, Item: "Товар А", Counterparty: "Клиент А", AccountKt: "41.01" }, { Period: "2020-02-01T00:00:00", Amount: 150000, Item: "Товар А", Counterparty: "Поставщик А", AccountDt: "41.01" } ] } ]); const result = await executeAssistantMcpDiscoveryPilot(planner, deps); expect(result.derived_business_overview?.trading_margin_proxy).toMatchObject({ sales_revenue: 100000, purchase_cost_proxy: 150000, gross_spread_proxy: -50000, gross_spread_proxy_human_ru: "-50 000 руб.", margin_to_revenue_pct: -50 }); expect(result.evidence.confirmed_facts.join("\n")).toContain("валовый спред proxy -50 000 руб."); expect(result.evidence.confirmed_facts.join("\n")).toContain("не чистая прибыль"); }); it("adds a checked debt-position family to business overview only as an as-of-date snapshot", async () => { const planner = planAssistantMcpDiscovery({ dataNeedGraph: { schema_version: "assistant_data_need_graph_v1", policy_owner: "assistantMcpDiscoveryDataNeedGraph", subject_candidates: [], business_fact_family: "business_overview", action_family: "broad_evaluation", aggregation_need: null, time_scope_need: "explicit_period", comparison_need: null, ranking_need: null, proof_expectation: "bounded_inference", clarification_gaps: [], decomposition_candidates: [ "collect_scoped_movements", "aggregate_checked_amounts", "aggregate_ranked_axis_values", "fetch_supporting_documents", "probe_coverage", "explain_evidence_basis" ], forbidden_overclaim_flags: ["no_raw_model_claims", "no_profit_or_margin_claim_without_evidence"], reason_codes: ["data_need_graph_built", "data_need_graph_family_business_overview"] }, turnMeaning: { asked_domain_family: "business_overview", asked_action_family: "broad_evaluation", explicit_organization_scope: "ООО Альтернатива Плюс", explicit_date_scope: "2020" } }); const deps = buildSequentialDeps([ { rows: [{ Period: "2020-01-15T00:00:00", Amount: 120000, Counterparty: "Клиент А" }] }, { rows: [{ Period: "2020-01-20T00:00:00", Amount: 50000, Counterparty: "Поставщик А" }] }, { rows: [] }, { rows: [ { Period: "2020-12-31T00:00:00", Amount: 70000, Counterparty: "Клиент А" }, { Period: "2020-12-31T00:00:00", Amount: 30000, Counterparty: "Клиент Б" } ] }, { rows: [ { Period: "2020-12-31T00:00:00", Amount: 40000, Counterparty: "Поставщик А" } ] }, { rows: [ { Period: "2020-12-31T00:00:00", Amount: 90000, Counterparty: "Клиент А", Contract: "Договор А от 10.02.2019" }, { Period: "2020-12-31T00:00:00", Amount: 30000, Counterparty: "Клиент Б", Contract: "Договор Б от 15.03.2020" } ] }, { rows: [] }, { rows: [] }, { rows: [ { Period: "2020-01-15T00:00:00", Registrator: "Поступление 1" }, { Period: "2020-12-15T00:00:00", Registrator: "Поступление 2" } ] }, { rows: [] } ]); const result = await executeAssistantMcpDiscoveryPilot(planner, deps); expect(result.pilot_status).toBe("executed"); expect(result.derived_business_overview?.debt_position).toMatchObject({ as_of_date: "2020-12-31", receivables: { total_amount: 100000, rows_with_amount: 2 }, payables: { total_amount: 40000, rows_with_amount: 1 }, net_debt_position_amount: 60000, net_debt_position_direction: "net_receivable" }); expect(result.derived_business_overview?.debt_position?.receivables.top_counterparties[0]).toMatchObject({ axis_value: "Клиент А", total_amount: 70000 }); expect(result.derived_business_overview?.debt_open_settlement_quality).toMatchObject({ as_of_date: "2020-12-31", rows_with_amount: 2, gross_open_amount: 120000, unique_counterparties: 2, unique_contracts: 2, concentration_top_contract_pct: 75 }); expect(result.derived_business_overview?.debt_open_settlement_quality?.age_signal).toMatchObject({ contracts_with_start_date: 2, oldest_start_date: "2019-02-10", latest_start_date: "2020-03-15", max_age_days: 690 }); expect(result.derived_business_overview?.debt_staleness_risk_proxy).toMatchObject({ as_of_date: "2020-12-31", oldest_contract_start_date: "2019-02-10", max_contract_age_days: 690, top_contract_amount: 90000, top_contract_share_pct: 75, risk_band: "high" }); expect(result.derived_business_overview?.debt_open_settlement_quality?.age_signal?.top_aged_contracts[0]).toMatchObject({ contract: "Договор А от 10.02.2019", start_date: "2019-02-10", age_days: 690, total_amount: 90000 }); expect(result.derived_business_overview?.missing_signal_families).not.toContain("debt_position"); expect(result.derived_business_overview?.missing_signal_families).not.toContain("debt_open_settlement_quality"); expect(result.derived_business_overview?.missing_signal_families).toContain("debt_due_date_aging_quality"); expect(result.evidence.confirmed_facts.join("\n")).toContain("Долговая позиция на 2020-12-31"); expect(result.evidence.confirmed_facts.join("\n")).toContain("Качество открытых расчетов на 2020-12-31"); expect(result.evidence.confirmed_facts.join("\n")).toContain("Возрастной сигнал открытых расчетов"); expect(result.evidence.confirmed_facts.join("\n")).toContain("Staleness risk proxy открытых расчетов"); expect(result.evidence.confirmed_facts.join("\n")).toContain("высокая зона внимания"); expect(result.evidence.confirmed_facts.join("\n")).toContain("не due-date анализ"); expect(result.evidence.unknown_facts.join("\n")).toContain("due-date"); expect(result.reason_codes).toContain("pilot_business_overview_debt_query_mcp_executed"); expect(result.reason_codes).toContain("pilot_derived_business_overview_debt_position_from_confirmed_rows"); expect(result.reason_codes).toContain("pilot_business_overview_open_contracts_query_mcp_executed"); expect(result.reason_codes).toContain("pilot_derived_business_overview_open_settlement_quality_from_confirmed_rows"); expect(result.reason_codes).toContain("pilot_derived_business_overview_debt_age_signal_from_contract_dates"); expect(result.reason_codes).toContain("pilot_derived_business_overview_debt_staleness_risk_proxy_from_confirmed_rows"); expect(deps.executeAddressMcpQuery).toHaveBeenCalledTimes(10); const receivablesCall = deps.executeAddressMcpQuery.mock.calls[3]?.[0]; const payablesCall = deps.executeAddressMcpQuery.mock.calls[4]?.[0]; const openContractsCall = deps.executeAddressMcpQuery.mock.calls[5]?.[0]; expect(String(receivablesCall?.query ?? "")).toContain("62"); expect(String(payablesCall?.query ?? "")).toContain("60"); expect(String(openContractsCall?.query ?? "")).toContain("СуммаРазвернутыйОстатокКт"); }); it("adds a checked inventory-position family to business overview only as an as-of-date snapshot", async () => { const planner = planAssistantMcpDiscovery({ dataNeedGraph: { schema_version: "assistant_data_need_graph_v1", policy_owner: "assistantMcpDiscoveryDataNeedGraph", subject_candidates: [], business_fact_family: "business_overview", action_family: "broad_evaluation", aggregation_need: null, time_scope_need: "explicit_period", comparison_need: null, ranking_need: null, proof_expectation: "bounded_inference", clarification_gaps: [], decomposition_candidates: [ "collect_scoped_movements", "aggregate_checked_amounts", "aggregate_ranked_axis_values", "fetch_supporting_documents", "probe_coverage", "explain_evidence_basis" ], forbidden_overclaim_flags: ["no_raw_model_claims", "no_profit_or_margin_claim_without_evidence"], reason_codes: ["data_need_graph_built", "data_need_graph_family_business_overview"] }, turnMeaning: { asked_domain_family: "business_overview", asked_action_family: "broad_evaluation", explicit_organization_scope: "ООО Тест", explicit_date_scope: "2020" } }); const deps = buildSequentialDeps([ { rows: [{ Period: "2020-01-15T00:00:00", Amount: 120000, Counterparty: "Клиент А" }] }, { rows: [{ Period: "2020-01-20T00:00:00", Amount: 50000, Counterparty: "Поставщик А" }] }, { rows: [] }, { rows: [] }, { rows: [] }, { rows: [] }, { rows: [ { Period: "2020-12-31T00:00:00", Amount: 250000, Quantity: 10, Item: "Товар А" }, { Period: "2020-12-31T00:00:00", Amount: 50000, Quantity: 5, Item: "Товар Б" } ] }, { rows: [ { Period: "2020-01-10T00:00:00", Amount: 200000, Quantity: 8, Item: "Товар А" }, { Period: "2020-11-01T00:00:00", Amount: 50000, Quantity: 2, Item: "Товар Б" } ] }, { rows: [ { Period: "2020-01-15T00:00:00", Registrator: "Поступление 1" }, { Period: "2020-12-15T00:00:00", Registrator: "Поступление 2" } ] }, { rows: [ { Period: "2020-03-01T00:00:00", Amount: 600000, Item: "Товар А", Counterparty: "Клиент А", AccountKt: "41.01" }, { Period: "2020-02-01T00:00:00", Amount: 240000, Item: "Товар А", Counterparty: "Поставщик А", AccountDt: "41.01" } ] } ]); const result = await executeAssistantMcpDiscoveryPilot(planner, deps); expect(result.pilot_status).toBe("executed"); expect(result.derived_business_overview?.inventory_position).toMatchObject({ as_of_date: "2020-12-31", rows_matched: 2, rows_with_amount: 2, rows_with_quantity: 2, total_amount: 300000, total_quantity: 15, aging_signal: { rows_matched: 2, rows_with_purchase_date: 2, oldest_purchase_date: "2020-01-10", latest_purchase_date: "2020-11-01", max_age_days: 356 } }); expect(result.derived_business_overview?.inventory_position?.top_items[0]).toMatchObject({ item: "Товар А", total_amount: 250000, total_quantity: 10 }); expect(result.derived_business_overview?.inventory_turnover_proxy).toMatchObject({ period_scope: "2020", as_of_date: "2020-12-31", sales_revenue: 600000, inventory_amount: 300000, sales_to_stock_amount_ratio: 2, stock_to_sales_revenue_pct: 50 }); expect(result.derived_business_overview?.inventory_staleness_risk_proxy).toMatchObject({ period_scope: "2020", as_of_date: "2020-12-31", oldest_purchase_date: "2020-01-10", max_purchase_age_days: 356, sales_to_stock_amount_ratio: 2, risk_band: "watch" }); expect(result.derived_business_overview?.missing_signal_families).not.toContain("inventory_position"); expect(result.derived_business_overview?.missing_signal_families).not.toContain("inventory_turnover_quality"); expect(result.derived_business_overview?.missing_signal_families).not.toContain("inventory_liquidity_quality"); expect(result.derived_business_overview?.missing_signal_families).toContain("inventory_reserve_liquidation_quality"); expect(result.evidence.confirmed_facts.join("\n")).toContain("Складской срез на 2020-12-31"); expect(result.evidence.confirmed_facts.join("\n")).toContain("Оборотный proxy склада за 2020"); expect(result.evidence.confirmed_facts.join("\n")).toContain("sales-to-stock ratio 2x"); expect(result.evidence.confirmed_facts.join("\n")).toContain("Staleness risk proxy склада"); expect(result.evidence.confirmed_facts.join("\n")).toContain("зона наблюдения"); expect(result.evidence.unknown_facts.join("\n")).toContain("Резервы"); expect(result.reason_codes).toContain("pilot_business_overview_inventory_query_mcp_executed"); expect(result.reason_codes).toContain("pilot_derived_business_overview_inventory_position_from_confirmed_rows"); expect(result.reason_codes).toContain("pilot_derived_business_overview_inventory_turnover_proxy_from_confirmed_rows"); expect(result.reason_codes).toContain("pilot_derived_business_overview_inventory_staleness_risk_proxy_from_confirmed_rows"); expect(deps.executeAddressMcpQuery).toHaveBeenCalledTimes(10); const inventoryCall = deps.executeAddressMcpQuery.mock.calls[6]?.[0]; expect(inventoryCall?.account_scope).toContain("41.01"); }); it("uses the explicit selected chain id when choosing the movement pilot scope", async () => { const planner = planAssistantMcpDiscovery({ turnMeaning: { asked_domain_family: "movements", asked_action_family: "list_movements", explicit_entity_candidates: ["SVK"], explicit_date_scope: "2020", unsupported_but_understood_family: "movement_evidence" } }); const deps = buildDeps([{ Period: "2020-01-15T00:00:00", Amount: 1250, Counterparty: "SVK", Registrar: "Move1" }]); const result = await executeAssistantMcpDiscoveryPilot( { ...planner, reason_codes: planner.reason_codes.filter((code) => !code.startsWith("planner_selected_")) }, deps ); expect(result.pilot_status).toBe("executed"); expect(result.pilot_scope).toBe("counterparty_movement_evidence_query_movements_v1"); expect(result.executed_primitives).toEqual(["query_movements"]); }); it("executes generic document evidence through query_documents", async () => { const planner = planAssistantMcpDiscovery({ turnMeaning: { asked_domain_family: "documents", asked_action_family: "list_documents", explicit_entity_candidates: ["SVK"], explicit_date_scope: "2020", unsupported_but_understood_family: "document_evidence" } }); const deps = buildDeps([ { Period: "2020-01-15T00:00:00", Counterparty: "SVK", Registrar: "Doc1" }, { Period: "2020-03-20T00:00:00", Counterparty: "SVK", Registrar: "Doc2" } ]); const result = await executeAssistantMcpDiscoveryPilot(planner, deps); expect(result.pilot_status).toBe("executed"); expect(result.pilot_scope).toBe("counterparty_document_evidence_query_documents_v1"); expect(result.executed_primitives).toEqual(["query_documents"]); expect(result.evidence.confirmed_facts).toContain("В 1С найдены строки документов по контрагенту SVK за 2020."); expect(result.evidence.inferred_facts).toContain( "Срез документов по контрагенту SVK за 2020 ограничен только подтвержденными строками документов, найденными этим поиском." ); expect(result.evidence.unknown_facts).toContain( "Полный исторический срез документов по контрагенту SVK вне периода 2020 этим поиском не подтвержден." ); expect(result.source_rows_summary).toBe("2 MCP document rows fetched, 2 matched document scope"); }); it("executes generic movement evidence through query_movements without deriving turnover totals", async () => { const planner = planAssistantMcpDiscovery({ turnMeaning: { asked_domain_family: "movements", asked_action_family: "list_movements", explicit_entity_candidates: ["SVK"], explicit_date_scope: "2020", unsupported_but_understood_family: "movement_evidence" } }); const deps = buildDeps([ { Period: "2020-01-15T00:00:00", Amount: 1250, Counterparty: "SVK", Registrar: "Move1" }, { Period: "2020-03-20T00:00:00", Amount: "900,25", Counterparty: "SVK", Registrar: "Move2" } ]); const result = await executeAssistantMcpDiscoveryPilot(planner, deps); expect(result.pilot_status).toBe("executed"); expect(result.pilot_scope).toBe("counterparty_movement_evidence_query_movements_v1"); expect(result.executed_primitives).toEqual(["query_movements"]); expect(result.derived_value_flow).toBeNull(); expect(result.derived_bidirectional_value_flow).toBeNull(); expect(result.evidence.confirmed_facts).toContain("В 1С найдены строки движений по контрагенту SVK за 2020."); expect(result.evidence.inferred_facts).toContain( "Срез движений по контрагенту SVK за 2020 ограничен только подтвержденными строками движений, найденными этим поиском." ); expect(result.evidence.unknown_facts).toContain( "Полный исторический срез движений по контрагенту SVK вне периода 2020 этим поиском не подтвержден." ); expect(result.source_rows_summary).toBe("2 MCP movement rows fetched, 2 matched movement scope"); expect(deps.executeAddressMcpQuery).toHaveBeenCalledTimes(1); const call = deps.executeAddressMcpQuery.mock.calls[0]?.[0]; expect(String(call?.query ?? "")).toContain("Документ.СписаниеСРасчетногоСчета"); expect(String(call?.query ?? "")).toContain("Документ.ПоступлениеНаРасчетныйСчет"); expect(call?.limit).toBeGreaterThan(0); }); it("executes inspect_1c_metadata and derives a confirmed metadata surface", async () => { const planner = planAssistantMcpDiscovery({ turnMeaning: { asked_domain_family: "metadata", asked_action_family: "inspect_documents", explicit_entity_candidates: ["НДС"] } }); const deps = buildMetadataDeps([ { FullName: "Документ.СчетФактураВыданный", MetaType: "Документ", attributes: [{ Name: "Дата" }, { Name: "Организация" }] }, { FullName: "Документ.СчетФактураПолученный", MetaType: "Документ", attributes: [{ Name: "Контрагент" }] } ]); const result = await executeAssistantMcpDiscoveryPilot(planner, deps); expect(result.pilot_status).toBe("executed"); expect(result.pilot_scope).toBe("metadata_inspection_v1"); expect(result.mcp_execution_performed).toBe(true); expect(result.executed_primitives).toEqual(["inspect_1c_metadata"]); expect(result.evidence.evidence_status).toBe("confirmed"); expect(result.source_rows_summary).toBe("2 MCP metadata rows fetched"); expect(result.derived_metadata_surface).toMatchObject({ metadata_scope: "НДС", requested_meta_types: ["Документ"], matched_rows: 2, available_entity_sets: ["Документ"], matched_objects: ["Документ.СчетФактураВыданный", "Документ.СчетФактураПолученный"], selected_entity_set: "Документ", selected_surface_objects: ["Документ.СчетФактураВыданный", "Документ.СчетФактураПолученный"], downstream_route_family: "document_evidence", recommended_next_primitive: "query_documents", ambiguity_detected: false, ambiguity_entity_sets: [], available_fields: ["Дата", "Организация", "Контрагент"], inference_basis: "confirmed_1c_metadata_surface_rows" }); expect(result.reason_codes).toContain("pilot_inspect_1c_metadata_mcp_executed"); expect(result.reason_codes).toContain("pilot_derived_metadata_surface_from_confirmed_rows"); expect(deps.executeAddressMcpMetadata).toHaveBeenCalledTimes(1); expect(deps.executeAddressMcpMetadata.mock.calls[0]?.[0]).toMatchObject({ meta_type: ["Документ"], name_mask: "НДС" }); }); it("executes catalog drilldown through a narrowed metadata probe seeded from the confirmed surface object", async () => { const planner = planAssistantMcpDiscovery({ metadataSurface: { selected_entity_set: "Catalog", selected_surface_objects: ["Catalog.Counterparties"], downstream_route_family: "catalog_drilldown", route_family_selection_basis: "selected_entity_set", recommended_next_primitive: "drilldown_related_objects", ambiguity_detected: false, ambiguity_entity_sets: [] }, turnMeaning: { asked_domain_family: "metadata", asked_action_family: "inspect_catalog", unsupported_but_understood_family: "schema_surface" } }); const deps = buildMetadataDeps([ { FullName: "Catalog.Counterparties", MetaType: "Catalog", attributes: [{ Name: "Description" }] }, { FullName: "Catalog.Contracts", MetaType: "Catalog", attributes: [{ Name: "Owner" }] } ]); const result = await executeAssistantMcpDiscoveryPilot(planner, deps); expect(result.pilot_status).toBe("executed"); expect(result.pilot_scope).toBe("metadata_inspection_v1"); expect(result.executed_primitives).toEqual(["inspect_1c_metadata"]); expect(result.derived_metadata_surface).toMatchObject({ metadata_scope: "Counterparties", requested_meta_types: ["Catalog"], available_entity_sets: ["Catalog"], selected_entity_set: "Catalog", downstream_route_family: "catalog_drilldown", recommended_next_primitive: "drilldown_related_objects" }); expect(result.reason_codes).toContain("pilot_catalog_drilldown_metadata_scope_seeded_from_surface_ref"); expect(deps.executeAddressMcpMetadata).toHaveBeenCalledTimes(1); expect(deps.executeAddressMcpMetadata.mock.calls[0]?.[0]).toMatchObject({ meta_type: ["Catalog"], name_mask: "Counterparties" }); }); it("executes the full entity-resolution chain through the checked counterparty catalog slice", async () => { const planner = planAssistantMcpDiscovery({ turnMeaning: { asked_domain_family: "entity_resolution", asked_action_family: "search_business_entity", explicit_entity_candidates: ["Группа СВК"], unsupported_but_understood_family: "entity_resolution" } }); const deps = buildDeps([ { Counterparty: "Группа СВК", CounterpartyRef: "Ref-1" }, { Counterparty: "СВК Логистика", CounterpartyRef: "Ref-2" } ]); const result = await executeAssistantMcpDiscoveryPilot(planner, deps); expect(result.pilot_status).toBe("executed"); expect(result.pilot_scope).toBe("entity_resolution_search_v1"); expect(result.mcp_execution_performed).toBe(true); expect(result.executed_primitives).toEqual([ "search_business_entity", "resolve_entity_reference", "probe_coverage" ]); expect(result.skipped_primitives).toEqual([]); expect(result.derived_entity_resolution).toMatchObject({ requested_entity: "Группа СВК", resolution_status: "resolved", resolved_entity: "Группа СВК", resolved_reference: "Ref-1", confidence: "high", inference_basis: "catalog_counterparty_search_rows" }); expect(result.evidence.confirmed_facts).toContain( "В проверенном каталожном срезе 1С найден контрагент: Группа СВК" ); expect(result.evidence.inferred_facts).toContain( "Пока проверено только заземление сущности по каталогу 1С; документы, движения и денежные показатели еще не проверялись" ); expect(result.evidence.unknown_facts).toContain( "Документы, движения и денежные показатели по этому контрагенту еще не проверялись; пока был только каталожный поиск" ); expect(result.reason_codes).toContain("pilot_search_business_entity_mcp_executed"); expect(result.reason_codes).toContain("pilot_resolve_entity_reference_from_catalog_rows"); expect(result.reason_codes).toContain("pilot_probe_coverage_executed_for_entity_resolution"); expect(result.reason_codes).toContain("pilot_entity_resolution_grounding_stable_for_downstream_probe"); expect(result.reason_codes).toContain("pilot_derived_entity_resolution_from_catalog_rows"); expect(deps.executeAddressMcpQuery).toHaveBeenCalledTimes(1); }); it("keeps entity-resolution honest when several catalog candidates remain ambiguous", async () => { const planner = planAssistantMcpDiscovery({ turnMeaning: { asked_domain_family: "entity_resolution", asked_action_family: "search_business_entity", explicit_entity_candidates: ["СВК"], unsupported_but_understood_family: "entity_resolution" } }); const deps = buildDeps([ { Counterparty: "СВК-А", CounterpartyRef: "Ref-1" }, { Counterparty: "СВК-Б", CounterpartyRef: "Ref-2" } ]); const result = await executeAssistantMcpDiscoveryPilot(planner, deps); expect(result.pilot_status).toBe("executed"); expect(result.pilot_scope).toBe("entity_resolution_search_v1"); expect(result.executed_primitives).toEqual([ "search_business_entity", "resolve_entity_reference", "probe_coverage" ]); expect(result.skipped_primitives).toEqual([]); expect(result.derived_entity_resolution).toMatchObject({ requested_entity: "СВК", resolution_status: "ambiguous", resolved_entity: null, ambiguity_candidates: ["СВК-А", "СВК-Б"], confidence: "low" }); expect(result.evidence.confirmed_facts).toEqual([]); expect(result.evidence.unknown_facts).toContain( "Точное заземление контрагента в 1С остается неоднозначным между вариантами: СВК-А, СВК-Б" ); expect(result.reason_codes).toContain("pilot_resolve_entity_reference_requires_clarification"); expect(result.reason_codes).toContain("pilot_probe_coverage_executed_for_entity_resolution"); expect(result.reason_codes).toContain("pilot_entity_resolution_ambiguity_requires_clarification"); }); it("keeps metadata grounding ambiguous when several surface families compete", async () => { const planner = planAssistantMcpDiscovery({ turnMeaning: { asked_domain_family: "metadata", asked_action_family: "inspect_fields", explicit_entity_candidates: ["НДС"] } }); const deps = buildMetadataDeps([ { FullName: "Документ.СчетФактураВыданный", MetaType: "Документ", attributes: [{ Name: "Дата" }] }, { FullName: "РегистрНакопления.НДСПокупок", MetaType: "РегистрНакопления", resources: [{ Name: "СуммаНДС" }] } ]); const result = await executeAssistantMcpDiscoveryPilot(planner, deps); expect(result.pilot_status).toBe("executed"); expect(result.derived_metadata_surface).toMatchObject({ metadata_scope: "НДС", available_entity_sets: ["Документ", "РегистрНакопления"], selected_entity_set: null, downstream_route_family: null, recommended_next_primitive: null, ambiguity_detected: true, ambiguity_entity_sets: ["Документ", "РегистрНакопления"] }); expect(result.evidence.inferred_facts).toEqual([]); expect(result.evidence.unknown_facts).toContain( "Exact downstream metadata surface remains ambiguous across: Документ, РегистрНакопления" ); }); it("selects a downstream lane from dominant metadata surface objects when one family clearly prevails", async () => { const planner = planAssistantMcpDiscovery({ turnMeaning: { asked_domain_family: "metadata", asked_action_family: "inspect_surface", explicit_entity_candidates: ["НДС"] } }); const deps = buildMetadataDeps([ { FullName: "Документ.СчетФактураВыданный", MetaType: "Документ", attributes: [{ Name: "Дата" }] }, { FullName: "Документ.СчетФактураПолученный", MetaType: "Документ", attributes: [{ Name: "Контрагент" }] }, { FullName: "РегистрНакопления.НДСПокупок", MetaType: "РегистрНакопления", resources: [{ Name: "СуммаНДС" }] } ]); const result = await executeAssistantMcpDiscoveryPilot(planner, deps); expect(result.pilot_status).toBe("executed"); expect(result.derived_metadata_surface).toMatchObject({ metadata_scope: "НДС", available_entity_sets: ["Документ", "РегистрНакопления"], selected_entity_set: null, selected_surface_objects: [ "Документ.СчетФактураВыданный", "Документ.СчетФактураПолученный" ], surface_family_scores: { document_evidence: 2, movement_evidence: 1, catalog_drilldown: 0 }, downstream_route_family: "document_evidence", route_family_selection_basis: "dominant_surface_objects", recommended_next_primitive: "query_documents", ambiguity_detected: false, ambiguity_entity_sets: [] }); expect(result.reason_codes).toContain("pilot_selected_metadata_route_family_from_dominant_surface_objects"); expect(result.evidence.inferred_facts).toContain( "A likely next checked lane may be inferred as document_evidence from the confirmed metadata surface" ); expect(result.evidence.unknown_facts).not.toContain( "Exact downstream metadata surface remains ambiguous across: Документ, РегистрНакопления" ); }); it("can break a weak metadata family tie by ranking surface objects against the requested scope", async () => { const planner = planAssistantMcpDiscovery({ turnMeaning: { asked_domain_family: "metadata", asked_action_family: "inspect_surface", explicit_entity_candidates: ["НДС"] } }); const deps = buildMetadataDeps([ { FullName: "Document.НДССчетФактура", MetaType: "Document", attributes: [{ Name: "Дата" }] }, { FullName: "AccumulationRegister.BankOperations", MetaType: "AccumulationRegister", resources: [{ Name: "Amount" }] } ]); const result = await executeAssistantMcpDiscoveryPilot(planner, deps); expect(result.pilot_status).toBe("executed"); expect(result.derived_metadata_surface).toMatchObject({ metadata_scope: "НДС", available_entity_sets: ["Document", "AccumulationRegister"], selected_entity_set: null, selected_surface_objects: ["Document.НДССчетФактура"], surface_family_scores: { document_evidence: 1, movement_evidence: 1, catalog_drilldown: 0 }, downstream_route_family: "document_evidence", route_family_selection_basis: "dominant_surface_objects", recommended_next_primitive: "query_documents", ambiguity_detected: false, ambiguity_entity_sets: [], surface_object_ranking_applied: true }); expect(result.reason_codes).toContain("pilot_selected_metadata_route_family_from_surface_object_ranking"); }); it("keeps metadata ambiguity unresolved when surface-family scores are nearly tied", async () => { const planner = planAssistantMcpDiscovery({ turnMeaning: { asked_domain_family: "metadata", asked_action_family: "inspect_surface", explicit_entity_candidates: ["НДС"] } }); const deps = buildMetadataDeps([ { FullName: "Документ.A", MetaType: "Документ" }, { FullName: "Документ.B", MetaType: "Документ" }, { FullName: "Документ.C", MetaType: "Документ" }, { FullName: "Документ.D", MetaType: "Документ" }, { FullName: "РегистрНакопления.A", MetaType: "РегистрНакопления" }, { FullName: "РегистрНакопления.B", MetaType: "РегистрНакопления" }, { FullName: "РегистрНакопления.C", MetaType: "РегистрНакопления" } ]); const result = await executeAssistantMcpDiscoveryPilot(planner, deps); expect(result.pilot_status).toBe("executed"); expect(result.derived_metadata_surface).toMatchObject({ metadata_scope: "НДС", selected_entity_set: null, surface_family_scores: { document_evidence: 4, movement_evidence: 3, catalog_drilldown: 0 }, downstream_route_family: null, route_family_selection_basis: null, recommended_next_primitive: null, ambiguity_detected: true }); expect(result.derived_metadata_surface?.ambiguity_entity_sets).toContain("Документ"); expect(result.derived_metadata_surface?.ambiguity_entity_sets).toContain("РегистрНакопления"); expect(result.reason_codes).not.toContain("pilot_selected_metadata_route_family_from_dominant_surface_objects"); expect(result.evidence.unknown_facts).toContain( "Exact downstream metadata surface remains ambiguous across: Документ, РегистрНакопления" ); }); it("infers metadata entity-set families from object names when meta type columns are absent", async () => { const planner = planAssistantMcpDiscovery({ turnMeaning: { asked_domain_family: "metadata", asked_action_family: "inspect_surface", explicit_entity_candidates: ["НДС"] } }); const deps = buildMetadataDeps([ { FullName: "Документ.СчетФактураВыданный", attributes: [{ Name: "Дата" }] }, { FullName: "РегистрНакопления.НДСПокупок", resources: [{ Name: "СуммаНДС" }] }, { FullName: "Справочник.КодыОперацийНДС" } ]); const result = await executeAssistantMcpDiscoveryPilot(planner, deps); expect(result.pilot_status).toBe("executed"); expect(result.derived_metadata_surface).toMatchObject({ metadata_scope: "НДС", available_entity_sets: ["Документ", "РегистрНакопления", "Справочник"], selected_entity_set: null, downstream_route_family: null, recommended_next_primitive: null, ambiguity_detected: true, ambiguity_entity_sets: ["Документ", "РегистрНакопления", "Справочник"] }); expect(result.evidence.unknown_facts).toContain( "Exact downstream metadata surface remains ambiguous across: Документ, РегистрНакопления, Справочник" ); }); it("executes value-flow query_movements and derives a guarded turnover sum", async () => { const planner = planAssistantMcpDiscovery({ turnMeaning: { asked_domain_family: "counterparty_value", asked_action_family: "turnover", explicit_entity_candidates: ["SVK"], explicit_date_scope: "2020" } }); const deps = buildDeps([ { Period: "2020-01-15T00:00:00", Amount: 1250, Counterparty: "SVK" }, { Period: "2020-02-20T00:00:00", Amount: "2 500,50", Counterparty: "SVK" } ]); const result = await executeAssistantMcpDiscoveryPilot(planner, deps); expect(result.pilot_status).toBe("executed"); expect(result.pilot_scope).toBe("counterparty_value_flow_query_movements_v1"); expect(result.mcp_execution_performed).toBe(true); expect(result.executed_primitives).toEqual(["query_movements"]); expect(result.skipped_primitives).toEqual(["resolve_entity_reference", "aggregate_by_axis", "probe_coverage"]); expect(result.evidence.evidence_status).toBe("confirmed"); expect(result.evidence.confirmed_facts[0]).toContain("value-flow rows"); expect(result.source_rows_summary).toBe("2 MCP value-flow rows fetched, 2 matched value-flow scope"); expect(result.derived_value_flow).toMatchObject({ counterparty: "SVK", period_scope: "2020", rows_matched: 2, rows_with_amount: 2, total_amount: 3750.5, coverage_limited_by_probe_limit: false, first_movement_date: "2020-01-15", latest_movement_date: "2020-02-20", inference_basis: "sum_of_confirmed_1c_value_flow_rows" }); expect(result.reason_codes).toContain("pilot_query_movements_mcp_executed"); expect(result.reason_codes).toContain("pilot_derived_value_flow_from_confirmed_rows"); expect(deps.executeAddressMcpQuery).toHaveBeenCalledTimes(1); const call = deps.executeAddressMcpQuery.mock.calls[0]?.[0]; expect(String(call?.query ?? "")).toContain("ПоступлениеНаРасчетныйСчет"); expect(call?.limit).toBeGreaterThan(0); }); it("executes supplier payout query_movements and derives a guarded outgoing payment sum", async () => { const planner = planAssistantMcpDiscovery({ turnMeaning: { asked_domain_family: "counterparty_value", asked_action_family: "payout", explicit_entity_candidates: ["SVK"], explicit_date_scope: "2020", unsupported_but_understood_family: "counterparty_payouts_or_outflow" } }); const deps = buildDeps([ { Period: "2020-03-15T00:00:00", Amount: 4100, Counterparty: "SVK" }, { Period: "2020-04-20T00:00:00", Amount: "900,25", Counterparty: "SVK" } ]); const result = await executeAssistantMcpDiscoveryPilot(planner, deps); expect(result.pilot_status).toBe("executed"); expect(result.pilot_scope).toBe("counterparty_supplier_payout_query_movements_v1"); expect(result.derived_value_flow).toMatchObject({ value_flow_direction: "outgoing_supplier_payout", counterparty: "SVK", period_scope: "2020", rows_matched: 2, rows_with_amount: 2, total_amount: 5000.25, coverage_limited_by_probe_limit: false, first_movement_date: "2020-03-15", latest_movement_date: "2020-04-20" }); expect(result.evidence.confirmed_facts[0]).toContain("supplier-payout rows"); expect(result.evidence.inferred_facts[0]).toContain("supplier-payout total"); expect(result.evidence.unknown_facts).toContain("Full supplier-payout amount outside the checked period is not proven by this MCP discovery pilot"); expect(result.reason_codes).toContain("pilot_supplier_payout_recipe_selected"); const call = deps.executeAddressMcpQuery.mock.calls[0]?.[0]; expect(String(call?.query ?? "")).toContain("СписаниеСРасчетногоСчета"); }); it("marks value-flow coverage as limited when the probe row limit is reached", async () => { const planner = planAssistantMcpDiscovery({ turnMeaning: { asked_domain_family: "counterparty_value", asked_action_family: "payout", explicit_entity_candidates: ["SVK"], explicit_date_scope: "2020", unsupported_but_understood_family: "counterparty_payouts_or_outflow" } }); const rows = Array.from({ length: 100 }, (_, index) => ({ Period: `2020-01-${String((index % 28) + 1).padStart(2, "0")}T00:00:00`, Amount: 10, Counterparty: "SVK" })); const result = await executeAssistantMcpDiscoveryPilot(planner, buildDeps(rows)); expect(result.derived_value_flow?.coverage_limited_by_probe_limit).toBe(true); expect(result.evidence.unknown_facts).toContain( "Complete requested-period coverage is not proven by the available checked rows" ); }); it("recovers yearly value-flow coverage by splitting a limited broad probe into monthly subprobes", async () => { const planner = planAssistantMcpDiscovery({ turnMeaning: { asked_domain_family: "counterparty_value", asked_action_family: "payout", explicit_entity_candidates: ["SVK"], explicit_date_scope: "2020", unsupported_but_understood_family: "counterparty_payouts_or_outflow" } }); const broadRows = Array.from({ length: 100 }, (_, index) => ({ Period: `2020-01-${String((index % 28) + 1).padStart(2, "0")}T00:00:00`, Amount: 10, Counterparty: "SVK" })); const monthlyResults = Array.from({ length: 12 }, (_, index) => ({ rows: [ { Period: `2020-${String(index + 1).padStart(2, "0")}-05T00:00:00`, Amount: (index + 1) * 100, Counterparty: "SVK" } ] })); const result = await executeAssistantMcpDiscoveryPilot( planner, buildSequentialDeps([{ rows: broadRows }, ...monthlyResults]) ); expect(result.derived_value_flow).toMatchObject({ value_flow_direction: "outgoing_supplier_payout", coverage_limited_by_probe_limit: false, coverage_recovered_by_period_chunking: true, period_chunking_granularity: "month", rows_matched: 12, rows_with_amount: 12, total_amount: 7800, first_movement_date: "2020-01-05", latest_movement_date: "2020-12-05" }); expect(result.evidence.inferred_facts).toContain( "Requested period coverage was recovered through monthly 1C value-flow probes" ); expect(result.evidence.unknown_facts).not.toContain( "Complete requested-period coverage is not proven by the available checked rows" ); expect(result.reason_codes).toContain("pilot_monthly_period_chunking_recovered_coverage"); }); it("executes bidirectional value-flow queries and derives guarded net cash flow", async () => { const planner = planAssistantMcpDiscovery({ turnMeaning: { asked_domain_family: "counterparty_value", asked_action_family: "net_value_flow", explicit_entity_candidates: ["SVK"], explicit_date_scope: "2020", unsupported_but_understood_family: "counterparty_bidirectional_value_flow_or_netting" } }); const deps = buildSequentialDeps([ { rows: [ { Period: "2020-01-15T00:00:00", Amount: 10000, Counterparty: "SVK" }, { Period: "2020-02-20T00:00:00", Amount: "2 500,50", Counterparty: "SVK" } ] }, { rows: [{ Period: "2020-03-10T00:00:00", Amount: 4000, Counterparty: "SVK" }] } ]); const result = await executeAssistantMcpDiscoveryPilot(planner, deps); expect(result.pilot_status).toBe("executed"); expect(result.pilot_scope).toBe("counterparty_bidirectional_value_flow_query_movements_v1"); expect(result.mcp_execution_performed).toBe(true); expect(result.executed_primitives).toEqual(["query_movements"]); expect(result.derived_value_flow).toBeNull(); expect(result.derived_bidirectional_value_flow).toMatchObject({ counterparty: "SVK", period_scope: "2020", net_amount: 8500.5, net_amount_human_ru: "8 500,50 руб.", net_direction: "net_incoming", coverage_limited_by_probe_limit: false, incoming_customer_revenue: { rows_matched: 2, rows_with_amount: 2, total_amount: 12500.5, first_movement_date: "2020-01-15", latest_movement_date: "2020-02-20" }, outgoing_supplier_payout: { rows_matched: 1, rows_with_amount: 1, total_amount: 4000, first_movement_date: "2020-03-10", latest_movement_date: "2020-03-10" } }); expect(result.evidence.confirmed_facts[0]).toContain("bidirectional value-flow rows"); expect(result.evidence.inferred_facts[0]).toContain("net value-flow"); expect(result.evidence.unknown_facts).toContain( "Full bidirectional value-flow outside the checked period is not proven by this MCP discovery pilot" ); expect(result.reason_codes).toContain("pilot_bidirectional_value_flow_recipes_selected"); expect(result.reason_codes).toContain("pilot_derived_bidirectional_value_flow_from_confirmed_rows"); expect(deps.executeAddressMcpQuery).toHaveBeenCalledTimes(2); }); it("preserves explicit date ranges when building bidirectional value-flow probes", async () => { const planner = planAssistantMcpDiscovery({ turnMeaning: { asked_domain_family: "counterparty_value", asked_action_family: "net_value_flow", explicit_entity_candidates: ["SVK"], explicit_date_scope: "2020-01-01..2020-12-31", unsupported_but_understood_family: "counterparty_bidirectional_value_flow_or_netting" } }); const deps = buildSequentialDeps([ { rows: [{ Period: "2020-01-15T00:00:00", Amount: 10000, Counterparty: "SVK" }] }, { rows: [{ Period: "2020-03-10T00:00:00", Amount: 4000, Counterparty: "SVK" }] } ]); const result = await executeAssistantMcpDiscoveryPilot(planner, deps); expect(result.pilot_status).toBe("executed"); expect(result.derived_bidirectional_value_flow?.period_scope).toBe("2020-01-01..2020-12-31"); expect(deps.executeAddressMcpQuery).toHaveBeenCalledTimes(2); for (const call of deps.executeAddressMcpQuery.mock.calls) { const query = String(call[0]?.query ?? ""); expect(query).toContain("2020, 1, 1"); expect(query).toContain("2020, 12, 31"); } }); it("derives monthly bidirectional value-flow breakdown when the turn explicitly asks by month", async () => { const planner = planAssistantMcpDiscovery({ turnMeaning: { asked_domain_family: "counterparty_value", asked_action_family: "net_value_flow", asked_aggregation_axis: "month", explicit_entity_candidates: ["SVK"], explicit_date_scope: "2020", unsupported_but_understood_family: "counterparty_bidirectional_value_flow_or_netting" } }); const deps = buildSequentialDeps([ { rows: [ { Period: "2020-01-15T00:00:00", Amount: 10000, Counterparty: "SVK" }, { Period: "2020-02-20T00:00:00", Amount: "2 500,50", Counterparty: "SVK" } ] }, { rows: [ { Period: "2020-01-10T00:00:00", Amount: 4000, Counterparty: "SVK" }, { Period: "2020-02-11T00:00:00", Amount: 1000, Counterparty: "SVK" } ] } ]); const result = await executeAssistantMcpDiscoveryPilot(planner, deps); expect(result.derived_bidirectional_value_flow?.aggregation_axis).toBe("month"); expect(result.derived_bidirectional_value_flow?.monthly_breakdown).toMatchObject([ { month_bucket: "2020-01", incoming_total_amount: 10000, incoming_rows_with_amount: 1, outgoing_total_amount: 4000, outgoing_rows_with_amount: 1, net_amount: 6000, net_direction: "net_incoming" }, { month_bucket: "2020-02", incoming_total_amount: 2500.5, incoming_rows_with_amount: 1, outgoing_total_amount: 1000, outgoing_rows_with_amount: 1, net_amount: 1500.5, net_direction: "net_incoming" } ]); expect(result.derived_bidirectional_value_flow?.monthly_breakdown[0]?.incoming_total_amount_human_ru).toContain("10 000"); expect(result.derived_bidirectional_value_flow?.monthly_breakdown[0]?.net_amount_human_ru).toContain("6 000"); expect(result.derived_bidirectional_value_flow?.monthly_breakdown[1]?.incoming_total_amount_human_ru).toContain("2 500,50"); expect(result.derived_bidirectional_value_flow?.monthly_breakdown[1]?.net_amount_human_ru).toContain("1 500,50"); expect(result.evidence.inferred_facts).toContain( "Counterparty monthly net value-flow breakdown was grouped by month over confirmed incoming and outgoing 1C rows" ); expect(result.reason_codes).toContain("pilot_derived_bidirectional_monthly_breakdown_from_confirmed_rows"); }); it("recovers bidirectional yearly coverage when one side is rebuilt from monthly subprobes", async () => { const planner = planAssistantMcpDiscovery({ turnMeaning: { asked_domain_family: "counterparty_value", asked_action_family: "net_value_flow", explicit_entity_candidates: ["SVK"], explicit_date_scope: "2020", unsupported_but_understood_family: "counterparty_bidirectional_value_flow_or_netting" } }); const outgoingBroadRows = Array.from({ length: 100 }, (_, index) => ({ Period: `2020-01-${String((index % 28) + 1).padStart(2, "0")}T00:00:00`, Amount: 10, Counterparty: "SVK" })); const outgoingMonthlyResults = Array.from({ length: 12 }, (_, index) => ({ rows: [ { Period: `2020-${String(index + 1).padStart(2, "0")}-10T00:00:00`, Amount: (index + 1) * 50, Counterparty: "SVK" } ] })); const deps = buildSequentialDeps([ { rows: [ { Period: "2020-01-15T00:00:00", Amount: 10000, Counterparty: "SVK" }, { Period: "2020-02-20T00:00:00", Amount: 10000, Counterparty: "SVK" } ] }, { rows: outgoingBroadRows }, ...outgoingMonthlyResults ]); const result = await executeAssistantMcpDiscoveryPilot(planner, deps); expect(result.derived_bidirectional_value_flow).toMatchObject({ coverage_limited_by_probe_limit: false, coverage_recovered_by_period_chunking: true, period_chunking_granularity: "month", net_amount: 16100, incoming_customer_revenue: { total_amount: 20000, coverage_limited_by_probe_limit: false }, outgoing_supplier_payout: { total_amount: 3900, coverage_limited_by_probe_limit: false, coverage_recovered_by_period_chunking: true, period_chunking_granularity: "month" } }); expect(result.evidence.inferred_facts).toContain( "Requested period coverage for bidirectional value-flow was recovered through monthly 1C side probes" ); expect(result.evidence.unknown_facts).not.toContain( "Complete requested-period coverage for bidirectional value-flow is not proven by the available checked rows" ); expect(result.reason_codes).toContain("pilot_bidirectional_outgoing_monthly_period_chunking_recovered_coverage"); expect(deps.executeAddressMcpQuery).toHaveBeenCalledTimes(14); }); it("executes document-ready plans through the dedicated document pilot", async () => { const planner = planAssistantMcpDiscovery({ turnMeaning: { asked_domain_family: "counterparty_documents", asked_action_family: "list_documents", explicit_entity_candidates: ["SVK"] } }); const deps = buildDeps([]); const result = await executeAssistantMcpDiscoveryPilot(planner, deps); expect(result.pilot_status).toBe("executed"); expect(result.pilot_scope).toBe("counterparty_document_evidence_query_documents_v1"); expect(result.mcp_execution_performed).toBe(true); expect(result.executed_primitives).toEqual(["query_documents"]); expect(result.reason_codes).toContain("pilot_query_documents_mcp_executed"); expect(deps.executeAddressMcpQuery).toHaveBeenCalledTimes(1); }); it("keeps document and movement evidence scoped to the resolved entity and checked period", async () => { const documentPlanner = planAssistantMcpDiscovery({ turnMeaning: { asked_domain_family: "documents", asked_action_family: "list_documents", explicit_entity_candidates: ["Группа СВК"], explicit_date_scope: "2020", unsupported_but_understood_family: "document_evidence" } }); const documentResult = await executeAssistantMcpDiscoveryPilot( documentPlanner, buildDeps([{ Period: "2020-01-15T00:00:00", Counterparty: "Группа СВК", Registrar: "Doc1" }]) ); expect(documentResult.evidence.confirmed_facts).toContain( "В 1С найдены строки документов по контрагенту Группа СВК за 2020." ); expect(documentResult.evidence.inferred_facts).toContain( "Срез документов по контрагенту Группа СВК за 2020 ограничен только подтвержденными строками документов, найденными этим поиском." ); expect(documentResult.evidence.unknown_facts).toContain( "Полный исторический срез документов по контрагенту Группа СВК вне периода 2020 этим поиском не подтвержден." ); const movementPlanner = planAssistantMcpDiscovery({ turnMeaning: { asked_domain_family: "movements", asked_action_family: "list_movements", explicit_entity_candidates: ["Группа СВК"], explicit_date_scope: "2020", unsupported_but_understood_family: "movement_evidence" } }); const movementResult = await executeAssistantMcpDiscoveryPilot( movementPlanner, buildDeps([{ Period: "2020-01-15T00:00:00", Counterparty: "Группа СВК", Registrar: "Move1" }]) ); expect(movementResult.evidence.confirmed_facts).toContain( "В 1С найдены строки движений по контрагенту Группа СВК за 2020." ); expect(movementResult.evidence.inferred_facts).toContain( "Срез движений по контрагенту Группа СВК за 2020 ограничен только подтвержденными строками движений, найденными этим поиском." ); expect(movementResult.evidence.unknown_facts).toContain( "Полный исторический срез движений по контрагенту Группа СВК вне периода 2020 этим поиском не подтвержден." ); }); it("records MCP errors as limitations without converting them into facts", async () => { const planner = planAssistantMcpDiscovery({ turnMeaning: { asked_domain_family: "counterparty_lifecycle", asked_action_family: "activity_duration", explicit_entity_candidates: ["SVK"] } }); const deps = buildDeps([], "MCP fetch failed: timeout"); const result = await executeAssistantMcpDiscoveryPilot(planner, deps); expect(result.pilot_status).toBe("executed"); expect(result.mcp_execution_performed).toBe(true); expect(result.evidence.evidence_status).toBe("insufficient"); expect(result.evidence.confirmed_facts).toEqual([]); expect(result.derived_activity_period).toBeNull(); expect(result.query_limitations).toContain("MCP fetch failed: timeout"); expect(result.reason_codes).toContain("pilot_query_documents_mcp_error"); }); it("emits Russian confirmed and bounded facts for resolved entity grounding", async () => { const planner = planAssistantMcpDiscovery({ turnMeaning: { asked_domain_family: "entity_resolution", asked_action_family: "search_business_entity", explicit_entity_candidates: ["\u0413\u0440\u0443\u043f\u043f\u0430 \u0421\u0412\u041a"], unsupported_but_understood_family: "entity_resolution" } }); const deps = buildDeps([ { Counterparty: "\u0413\u0440\u0443\u043f\u043f\u0430 \u0421\u0412\u041a", CounterpartyRef: "Ref-1" }, { Counterparty: "\u0421\u0412\u041a \u041b\u043e\u0433\u0438\u0441\u0442\u0438\u043a\u0430", CounterpartyRef: "Ref-2" } ]); const result = await executeAssistantMcpDiscoveryPilot(planner, deps); expect(result.evidence.confirmed_facts.join("\n")).toContain("\u043d\u0430\u0439\u0434\u0435\u043d \u043a\u043e\u043d\u0442\u0440\u0430\u0433\u0435\u043d\u0442"); expect(result.evidence.confirmed_facts.join("\n")).toContain("\u0413\u0440\u0443\u043f\u043f\u0430 \u0421\u0412\u041a"); expect(result.evidence.inferred_facts.join("\n")).toContain( "\u041f\u043e\u043a\u0430 \u043f\u0440\u043e\u0432\u0435\u0440\u0435\u043d\u043e \u0442\u043e\u043b\u044c\u043a\u043e \u0437\u0430\u0437\u0435\u043c\u043b\u0435\u043d\u0438\u0435 \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u0438 \u043f\u043e \u043a\u0430\u0442\u0430\u043b\u043e\u0433\u0443 1\u0421" ); expect(result.evidence.unknown_facts.join("\n")).toContain( "\u0414\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u044b, \u0434\u0432\u0438\u0436\u0435\u043d\u0438\u044f \u0438 \u0434\u0435\u043d\u0435\u0436\u043d\u044b\u0435 \u043f\u043e\u043a\u0430\u0437\u0430\u0442\u0435\u043b\u0438" ); }); it("emits Russian ambiguity boundaries for entity grounding", async () => { const planner = planAssistantMcpDiscovery({ turnMeaning: { asked_domain_family: "entity_resolution", asked_action_family: "search_business_entity", explicit_entity_candidates: ["\u0421\u0412\u041a"], unsupported_but_understood_family: "entity_resolution" } }); const deps = buildDeps([ { Counterparty: "\u0421\u0412\u041a-\u0410", CounterpartyRef: "Ref-1" }, { Counterparty: "\u0421\u0412\u041a-\u0411", CounterpartyRef: "Ref-2" } ]); const result = await executeAssistantMcpDiscoveryPilot(planner, deps); expect(result.evidence.confirmed_facts).toEqual([]); expect(result.evidence.unknown_facts.join("\n")).toContain( "\u0422\u043e\u0447\u043d\u043e\u0435 \u0437\u0430\u0437\u0435\u043c\u043b\u0435\u043d\u0438\u0435 \u043a\u043e\u043d\u0442\u0440\u0430\u0433\u0435\u043d\u0442\u0430 \u0432 1\u0421 \u043e\u0441\u0442\u0430\u0435\u0442\u0441\u044f \u043d\u0435\u043e\u0434\u043d\u043e\u0437\u043d\u0430\u0447\u043d\u044b\u043c" ); expect(result.evidence.unknown_facts.join("\n")).toContain("\u0421\u0412\u041a-\u0410"); expect(result.evidence.unknown_facts.join("\n")).toContain("\u0421\u0412\u041a-\u0411"); }); });