1148 lines
49 KiB
TypeScript
1148 lines
49 KiB
TypeScript
import { describe, expect, it, vi } from "vitest";
|
||
import { runAssistantMcpDiscoveryRuntimeBridge } from "../src/services/assistantMcpDiscoveryRuntimeBridge";
|
||
|
||
function buildDeps(rows: Array<Record<string, unknown>>, 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 buildBidirectionalDeps(
|
||
incomingRows: Array<Record<string, unknown>>,
|
||
outgoingRows: Array<Record<string, unknown>>
|
||
) {
|
||
return {
|
||
executeAddressMcpQuery: vi
|
||
.fn()
|
||
.mockResolvedValueOnce({
|
||
fetched_rows: incomingRows.length,
|
||
matched_rows: incomingRows.length,
|
||
raw_rows: incomingRows,
|
||
rows: incomingRows,
|
||
error: null
|
||
})
|
||
.mockResolvedValueOnce({
|
||
fetched_rows: outgoingRows.length,
|
||
matched_rows: outgoingRows.length,
|
||
raw_rows: outgoingRows,
|
||
rows: outgoingRows,
|
||
error: null
|
||
})
|
||
};
|
||
}
|
||
|
||
function buildSequentialDeps(results: Array<{ rows: Array<Record<string, unknown>>; 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 };
|
||
}
|
||
|
||
describe("assistant MCP discovery runtime bridge", () => {
|
||
it("composes planner, pilot executor, and answer draft without wiring the hot runtime", async () => {
|
||
const result = await runAssistantMcpDiscoveryRuntimeBridge({
|
||
turnMeaning: {
|
||
asked_domain_family: "counterparty_lifecycle",
|
||
asked_action_family: "activity_duration",
|
||
explicit_entity_candidates: ["SVK"]
|
||
},
|
||
deps: buildDeps([{ Период: "2020-01-15T00:00:00", Регистратор: "CP_CUSTOMER_ACTIVITY_FIRST" }])
|
||
});
|
||
|
||
expect(result.schema_version).toBe("assistant_mcp_discovery_runtime_bridge_v1");
|
||
expect(result.bridge_status).toBe("answer_draft_ready");
|
||
expect(result.hot_runtime_wired).toBe(false);
|
||
expect(result.pilot.mcp_execution_performed).toBe(true);
|
||
expect(result.answer_draft.answer_mode).toBe("confirmed_with_bounded_inference");
|
||
expect(result.business_fact_answer_allowed).toBe(true);
|
||
expect(result.user_facing_response_allowed).toBe(true);
|
||
expect(result.reason_codes).toContain("runtime_bridge_not_wired_to_hot_assistant_answer");
|
||
});
|
||
|
||
it("keeps missing scope as clarification and does not authorize a business fact answer", async () => {
|
||
const result = await runAssistantMcpDiscoveryRuntimeBridge({
|
||
turnMeaning: {
|
||
asked_domain_family: "counterparty_value",
|
||
asked_action_family: "turnover",
|
||
explicit_entity_candidates: ["SVK"]
|
||
},
|
||
deps: buildDeps([])
|
||
});
|
||
|
||
expect(result.bridge_status).toBe("needs_clarification");
|
||
expect(result.requires_user_clarification).toBe(true);
|
||
expect(result.pilot.mcp_execution_performed).toBe(false);
|
||
expect(result.business_fact_answer_allowed).toBe(false);
|
||
expect(result.answer_draft.next_step_line).toContain("Уточните контрагента");
|
||
});
|
||
|
||
it("keeps ranked value-flow in clarification without asking for a counterparty when only the period is known", async () => {
|
||
const result = await runAssistantMcpDiscoveryRuntimeBridge({
|
||
dataNeedGraph: {
|
||
schema_version: "assistant_data_need_graph_v1",
|
||
policy_owner: "assistantMcpDiscoveryDataNeedGraph",
|
||
subject_candidates: [],
|
||
business_fact_family: "value_flow",
|
||
action_family: "turnover",
|
||
aggregation_need: null,
|
||
time_scope_need: "explicit_period",
|
||
comparison_need: null,
|
||
ranking_need: "top_desc",
|
||
proof_expectation: "coverage_checked_fact",
|
||
clarification_gaps: [],
|
||
decomposition_candidates: ["collect_scoped_movements", "aggregate_ranked_axis_values", "probe_coverage"],
|
||
forbidden_overclaim_flags: ["no_raw_model_claims", "no_unchecked_fact_totals"],
|
||
reason_codes: ["data_need_graph_built", "data_need_graph_ranking_top_desc"]
|
||
},
|
||
turnMeaning: {
|
||
asked_domain_family: "counterparty_value",
|
||
asked_action_family: "turnover",
|
||
explicit_date_scope: "2020"
|
||
},
|
||
deps: buildDeps([])
|
||
});
|
||
|
||
expect(result.bridge_status).toBe("needs_clarification");
|
||
expect(result.requires_user_clarification).toBe(true);
|
||
expect(result.pilot.mcp_execution_performed).toBe(false);
|
||
expect(result.planner.selected_chain_id).toBe("value_flow_ranking");
|
||
expect(result.answer_draft.headline).toContain("рейтинг");
|
||
expect(result.answer_draft.next_step_line).toContain("организацию");
|
||
expect(result.answer_draft.next_step_line).not.toContain("Уточните контрагента");
|
||
});
|
||
|
||
it("emits a resumable loop state for clarification-driven ranking chains", async () => {
|
||
const result = await runAssistantMcpDiscoveryRuntimeBridge({
|
||
dataNeedGraph: {
|
||
schema_version: "assistant_data_need_graph_v1",
|
||
policy_owner: "assistantMcpDiscoveryDataNeedGraph",
|
||
subject_candidates: [],
|
||
business_fact_family: "value_flow",
|
||
action_family: "turnover",
|
||
aggregation_need: null,
|
||
time_scope_need: "explicit_period",
|
||
comparison_need: null,
|
||
ranking_need: "top_desc",
|
||
proof_expectation: "coverage_checked_fact",
|
||
clarification_gaps: [],
|
||
decomposition_candidates: ["collect_scoped_movements", "aggregate_ranked_axis_values", "probe_coverage"],
|
||
forbidden_overclaim_flags: ["no_raw_model_claims", "no_unchecked_fact_totals"],
|
||
reason_codes: ["data_need_graph_built", "data_need_graph_ranking_top_desc"]
|
||
},
|
||
turnMeaning: {
|
||
asked_domain_family: "counterparty_value",
|
||
asked_action_family: "turnover",
|
||
explicit_date_scope: "2020"
|
||
},
|
||
deps: buildDeps([])
|
||
});
|
||
|
||
expect(result.bridge_status).toBe("needs_clarification");
|
||
expect(result.loop_state).toMatchObject({
|
||
loop_status: "awaiting_clarification",
|
||
selected_chain_id: "value_flow_ranking",
|
||
asked_domain_family: "counterparty_value",
|
||
asked_action_family: "turnover",
|
||
ranking_need: "top_desc",
|
||
explicit_date_scope: "2020"
|
||
});
|
||
expect(result.loop_state.pending_axes).toContain("organization");
|
||
expect(result.loop_state.provided_axes).toContain("aggregate_axis");
|
||
expect(result.loop_state.catalog_chain_template_matches[0]).toBe("value_flow_ranking");
|
||
expect(result.loop_state.catalog_chain_template_alignment.alignment_status).toBe("selected_matches_top");
|
||
expect(result.loop_state.catalog_chain_template_alignment.selected_chain_matches_top).toBe(true);
|
||
expect(result.route_candidate).toMatchObject({
|
||
schema_version: "assistant_mcp_route_candidate_v1",
|
||
candidate_status: "needs_user_scope",
|
||
selected_chain_id: "value_flow_ranking",
|
||
nearest_catalog_chain_template: "value_flow_ranking",
|
||
catalog_alignment_status: "selected_matches_top",
|
||
business_fact_family: "value_flow",
|
||
action_family: "turnover",
|
||
executable_now: false
|
||
});
|
||
expect(result.route_candidate.missing_axes).toContain("organization");
|
||
expect(result.route_candidate.provided_axes).toContain("aggregate_axis");
|
||
expect(result.route_candidate.recommended_next_action).toBe(
|
||
"Ask the user for the missing scope axes before MCP execution."
|
||
);
|
||
expect(result.reason_codes).toContain("planner_selected_chain_matches_catalog_top");
|
||
expect(result.reason_codes).toContain("runtime_bridge_loop_state_awaiting_clarification");
|
||
expect(result.reason_codes).toContain("runtime_bridge_route_candidate_needs_user_scope");
|
||
});
|
||
|
||
it("produces a bounded ranked value-flow answer when period and organization are known", async () => {
|
||
const result = await runAssistantMcpDiscoveryRuntimeBridge({
|
||
dataNeedGraph: {
|
||
schema_version: "assistant_data_need_graph_v1",
|
||
policy_owner: "assistantMcpDiscoveryDataNeedGraph",
|
||
subject_candidates: [],
|
||
business_fact_family: "value_flow",
|
||
action_family: "turnover",
|
||
aggregation_need: null,
|
||
time_scope_need: "explicit_period",
|
||
comparison_need: null,
|
||
ranking_need: "top_desc",
|
||
proof_expectation: "coverage_checked_fact",
|
||
clarification_gaps: [],
|
||
decomposition_candidates: ["collect_scoped_movements", "aggregate_ranked_axis_values", "probe_coverage"],
|
||
forbidden_overclaim_flags: ["no_raw_model_claims", "no_unchecked_fact_totals"],
|
||
reason_codes: ["data_need_graph_built", "data_need_graph_ranking_top_desc"]
|
||
},
|
||
turnMeaning: {
|
||
asked_domain_family: "counterparty_value",
|
||
asked_action_family: "turnover",
|
||
explicit_date_scope: "2020",
|
||
explicit_organization_scope: "ООО Альтернатива Плюс"
|
||
},
|
||
deps: buildDeps([
|
||
{ Period: "2020-01-10T00:00:00", Amount: 1200, Counterparty: "СВК-А" },
|
||
{ Period: "2020-03-11T00:00:00", Amount: 800, Counterparty: "СВК-Б" },
|
||
{ Period: "2020-05-12T00:00:00", Amount: 900, Counterparty: "СВК-А" }
|
||
])
|
||
});
|
||
|
||
expect(result.bridge_status).toBe("answer_draft_ready");
|
||
expect(result.business_fact_answer_allowed).toBe(true);
|
||
expect(result.planner.selected_chain_id).toBe("value_flow_ranking");
|
||
expect(result.pilot.derived_ranked_value_flow?.ranked_values[0]).toMatchObject({
|
||
axis_value: "СВК-А",
|
||
total_amount: 2100
|
||
});
|
||
expect(result.route_candidate).toMatchObject({
|
||
candidate_status: "ready_for_reviewed_execution",
|
||
selected_chain_id: "value_flow_ranking",
|
||
executable_now: true,
|
||
enablement_reason: null
|
||
});
|
||
expect(result.answer_draft.confirmed_lines.join("\n")).toContain("СВК-А");
|
||
});
|
||
|
||
it("keeps open bidirectional comparison in clarification without asking for a counterparty when only the period is known", async () => {
|
||
const result = await runAssistantMcpDiscoveryRuntimeBridge({
|
||
dataNeedGraph: {
|
||
schema_version: "assistant_data_need_graph_v1",
|
||
policy_owner: "assistantMcpDiscoveryDataNeedGraph",
|
||
subject_candidates: [],
|
||
business_fact_family: "value_flow",
|
||
action_family: "net_value_flow",
|
||
aggregation_need: null,
|
||
time_scope_need: "explicit_period",
|
||
comparison_need: "incoming_vs_outgoing",
|
||
ranking_need: null,
|
||
proof_expectation: "coverage_checked_fact",
|
||
clarification_gaps: [],
|
||
decomposition_candidates: ["collect_incoming_movements", "collect_outgoing_movements", "probe_coverage"],
|
||
forbidden_overclaim_flags: ["no_raw_model_claims", "no_unchecked_fact_totals"],
|
||
reason_codes: ["data_need_graph_built", "data_need_graph_comparison_incoming_vs_outgoing"]
|
||
},
|
||
turnMeaning: {
|
||
asked_domain_family: "counterparty_value",
|
||
asked_action_family: "net_value_flow",
|
||
explicit_date_scope: "2020"
|
||
},
|
||
deps: buildDeps([])
|
||
});
|
||
|
||
expect(result.bridge_status).toBe("needs_clarification");
|
||
expect(result.requires_user_clarification).toBe(true);
|
||
expect(result.pilot.mcp_execution_performed).toBe(false);
|
||
expect(result.planner.selected_chain_id).toBe("value_flow_comparison");
|
||
expect(result.answer_draft.headline).toContain("входящий и исходящий");
|
||
expect(result.answer_draft.next_step_line).toContain("организацию");
|
||
expect(result.answer_draft.next_step_line).not.toContain("Уточните контрагента");
|
||
});
|
||
|
||
it("produces a bounded bidirectional comparison answer when period and organization are known", async () => {
|
||
const result = await runAssistantMcpDiscoveryRuntimeBridge({
|
||
dataNeedGraph: {
|
||
schema_version: "assistant_data_need_graph_v1",
|
||
policy_owner: "assistantMcpDiscoveryDataNeedGraph",
|
||
subject_candidates: [],
|
||
business_fact_family: "value_flow",
|
||
action_family: "net_value_flow",
|
||
aggregation_need: null,
|
||
time_scope_need: "explicit_period",
|
||
comparison_need: "incoming_vs_outgoing",
|
||
ranking_need: null,
|
||
proof_expectation: "coverage_checked_fact",
|
||
clarification_gaps: [],
|
||
decomposition_candidates: ["collect_incoming_movements", "collect_outgoing_movements", "probe_coverage"],
|
||
forbidden_overclaim_flags: ["no_raw_model_claims", "no_unchecked_fact_totals"],
|
||
reason_codes: ["data_need_graph_built", "data_need_graph_comparison_incoming_vs_outgoing"]
|
||
},
|
||
turnMeaning: {
|
||
asked_domain_family: "counterparty_value",
|
||
asked_action_family: "net_value_flow",
|
||
explicit_date_scope: "2020",
|
||
explicit_organization_scope: "ООО Альтернатива Плюс"
|
||
},
|
||
deps: buildBidirectionalDeps(
|
||
[
|
||
{ Period: "2020-01-10T00:00:00", Amount: 3200, Counterparty: "СВК-А" },
|
||
{ Period: "2020-04-11T00:00:00", Amount: 1800, Counterparty: "СВК-Б" }
|
||
],
|
||
[{ Period: "2020-02-12T00:00:00", Amount: 1400, Counterparty: "СВК-А" }]
|
||
)
|
||
});
|
||
|
||
expect(result.bridge_status).toBe("answer_draft_ready");
|
||
expect(result.business_fact_answer_allowed).toBe(true);
|
||
expect(result.planner.selected_chain_id).toBe("value_flow_comparison");
|
||
expect(result.pilot.derived_bidirectional_value_flow).toMatchObject({
|
||
period_scope: "2020",
|
||
incoming_customer_revenue: {
|
||
total_amount: 5000
|
||
},
|
||
outgoing_supplier_payout: {
|
||
total_amount: 1400
|
||
}
|
||
});
|
||
expect(result.answer_draft.confirmed_lines.join("\n")).toContain("получили");
|
||
expect(result.answer_draft.confirmed_lines.join("\n")).toContain("заплатили");
|
||
});
|
||
|
||
it("executes explicit-counterparty bidirectional comparison without period as bounded all-time scope", async () => {
|
||
const result = await runAssistantMcpDiscoveryRuntimeBridge({
|
||
dataNeedGraph: {
|
||
schema_version: "assistant_data_need_graph_v1",
|
||
policy_owner: "assistantMcpDiscoveryDataNeedGraph",
|
||
subject_candidates: ["SVK"],
|
||
business_fact_family: "value_flow",
|
||
action_family: "net_value_flow",
|
||
aggregation_need: null,
|
||
time_scope_need: "all_time_scope",
|
||
comparison_need: "incoming_vs_outgoing",
|
||
ranking_need: null,
|
||
proof_expectation: "coverage_checked_fact",
|
||
clarification_gaps: [],
|
||
decomposition_candidates: [
|
||
"resolve_entity_reference",
|
||
"collect_incoming_movements",
|
||
"collect_outgoing_movements",
|
||
"probe_coverage"
|
||
],
|
||
forbidden_overclaim_flags: ["no_raw_model_claims", "no_unchecked_fact_totals"],
|
||
reason_codes: [
|
||
"data_need_graph_built",
|
||
"data_need_graph_comparison_incoming_vs_outgoing",
|
||
"data_need_graph_subject_bidirectional_value_flow_defaults_to_all_time_scope"
|
||
]
|
||
},
|
||
turnMeaning: {
|
||
asked_domain_family: "counterparty_value",
|
||
asked_action_family: "net_value_flow",
|
||
explicit_entity_candidates: ["SVK"],
|
||
unsupported_but_understood_family: "counterparty_bidirectional_value_flow_or_netting"
|
||
},
|
||
deps: buildBidirectionalDeps(
|
||
[
|
||
{ Period: "2020-01-10T00:00:00", Amount: 3200, Counterparty: "SVK" },
|
||
{ Period: "2021-04-11T00:00:00", Amount: 1800, Counterparty: "SVK" }
|
||
],
|
||
[{ Period: "2022-02-12T00:00:00", Amount: 1400, Counterparty: "SVK" }]
|
||
)
|
||
});
|
||
|
||
expect(result.bridge_status).toBe("answer_draft_ready");
|
||
expect(result.requires_user_clarification).toBe(false);
|
||
expect(result.business_fact_answer_allowed).toBe(true);
|
||
expect(result.planner.selected_chain_id).toBe("value_flow_comparison");
|
||
expect(result.planner.required_axes).toContain("all_time_scope");
|
||
expect(result.pilot.mcp_execution_performed).toBe(true);
|
||
expect(result.pilot.derived_bidirectional_value_flow).toMatchObject({
|
||
period_scope: null,
|
||
incoming_customer_revenue: {
|
||
total_amount: 5000
|
||
},
|
||
outgoing_supplier_payout: {
|
||
total_amount: 1400
|
||
}
|
||
});
|
||
expect(result.answer_draft.confirmed_lines.join("\n")).toContain("SVK");
|
||
});
|
||
|
||
it("keeps document-ready plans bounded when the pilot finds no confirmed rows", async () => {
|
||
const result = await runAssistantMcpDiscoveryRuntimeBridge({
|
||
turnMeaning: {
|
||
asked_domain_family: "document",
|
||
asked_action_family: "documents",
|
||
explicit_entity_candidates: ["SVK"]
|
||
},
|
||
deps: buildDeps([])
|
||
});
|
||
|
||
expect(result.bridge_status).toBe("checked_sources_only");
|
||
expect(result.hot_runtime_wired).toBe(false);
|
||
expect(result.pilot.pilot_scope).toBe("counterparty_document_evidence_query_documents_v1");
|
||
expect(result.pilot.mcp_execution_performed).toBe(true);
|
||
expect(result.business_fact_answer_allowed).toBe(false);
|
||
expect(result.reason_codes).toContain("runtime_bridge_status_checked_sources_only");
|
||
});
|
||
|
||
it("bridges inventory stock catalog templates through exact runtime evidence", async () => {
|
||
const deps = buildDeps([
|
||
{ Period: "2021-09-30T00:00:00", Item: "Widget A", Quantity: 10, Warehouse: "Main" },
|
||
{ Period: "2021-09-30T00:00:00", Item: "Widget B", Quantity: 4, Warehouse: "Reserve" }
|
||
]);
|
||
const result = await runAssistantMcpDiscoveryRuntimeBridge({
|
||
dataNeedGraph: {
|
||
schema_version: "assistant_data_need_graph_v1",
|
||
policy_owner: "assistantMcpDiscoveryDataNeedGraph",
|
||
subject_candidates: [],
|
||
business_fact_family: "inventory_stock_snapshot",
|
||
action_family: "stock_snapshot",
|
||
aggregation_need: null,
|
||
time_scope_need: "explicit_period",
|
||
comparison_need: null,
|
||
ranking_need: null,
|
||
proof_expectation: "coverage_checked_fact",
|
||
clarification_gaps: [],
|
||
decomposition_candidates: [
|
||
"fetch_scoped_movements",
|
||
"aggregate_checked_amounts",
|
||
"probe_coverage",
|
||
"explain_evidence_basis"
|
||
],
|
||
forbidden_overclaim_flags: ["no_raw_model_claims", "no_unchecked_stock_snapshot"],
|
||
reason_codes: ["data_need_graph_built", "data_need_graph_family_inventory_stock_snapshot"]
|
||
},
|
||
turnMeaning: {
|
||
asked_domain_family: "inventory_stock",
|
||
asked_action_family: "stock_snapshot",
|
||
explicit_organization_scope: "OOO Alternative Plus",
|
||
explicit_date_scope: "2021-09-30"
|
||
},
|
||
deps
|
||
});
|
||
const userFacing = [
|
||
result.answer_draft.headline,
|
||
...result.answer_draft.confirmed_lines,
|
||
...result.answer_draft.inference_lines,
|
||
...result.answer_draft.unknown_lines,
|
||
...result.answer_draft.limitation_lines,
|
||
result.answer_draft.next_step_line ?? ""
|
||
].join("\n");
|
||
|
||
expect(result.bridge_status).toBe("answer_draft_ready");
|
||
expect(result.answer_draft.answer_mode).toBe("confirmed_with_bounded_inference");
|
||
expect(result.pilot.pilot_scope).toBe("inventory_route_template_v1");
|
||
expect(result.pilot.mcp_execution_performed).toBe(true);
|
||
expect(result.pilot.executed_primitives).toEqual(["query_movements"]);
|
||
expect(deps.executeAddressMcpQuery).toHaveBeenCalledWith(expect.objectContaining({ account_scope: ["41.01"] }));
|
||
expect(result.loop_state).toMatchObject({
|
||
loop_status: "ready_for_next_hop",
|
||
selected_chain_id: "inventory_stock_snapshot",
|
||
pilot_scope: "inventory_route_template_v1",
|
||
asked_domain_family: "inventory_stock",
|
||
asked_action_family: "stock_snapshot",
|
||
explicit_organization_scope: "OOO Alternative Plus",
|
||
explicit_date_scope: "2021-09-30"
|
||
});
|
||
expect(result.business_fact_answer_allowed).toBe(true);
|
||
expect(result.reason_codes).toContain("pilot_inventory_exact_recipe_selected");
|
||
expect(result.reason_codes).toContain("pilot_inventory_exact_mcp_executed");
|
||
expect(result.reason_codes).not.toContain("pilot_scope_unsupported_for_live_execution");
|
||
expect(result.answer_draft.must_not_claim).toEqual(
|
||
expect.arrayContaining([
|
||
"Do not expose inventory_route_template_v1 or MCP primitive names in the user answer.",
|
||
"Do not claim full inventory coverage outside the checked rows, date, organization, item, or supplier scope."
|
||
])
|
||
);
|
||
expect(result.answer_draft.must_not_claim).not.toContain(
|
||
"Do not present inventory route-template planning as executed stock, supplier, purchase, or sale evidence."
|
||
);
|
||
expect(userFacing).toContain("exact inventory runtime");
|
||
expect(userFacing).toContain("Widget A");
|
||
expect(userFacing).not.toContain("inventory_route_template_v1");
|
||
expect(userFacing).not.toContain("query_movements");
|
||
expect(userFacing).not.toContain("primitive");
|
||
expect(userFacing).not.toContain("MCP discovery pilot");
|
||
});
|
||
|
||
it("promotes inventory reserve boundary after reviewed quality-event route executes", async () => {
|
||
const deps = buildSequentialDeps([
|
||
{ rows: [{ Period: "2020-01-15T00:00:00", Amount: 120000, Counterparty: "Client A" }] },
|
||
{ rows: [{ Period: "2020-01-20T00:00:00", Amount: 50000, Counterparty: "Supplier A" }] },
|
||
{ rows: [] },
|
||
{ rows: [] },
|
||
{ rows: [] },
|
||
{ rows: [] },
|
||
{ rows: [] },
|
||
{
|
||
rows: [
|
||
{ Period: "2020-12-31T00:00:00", Amount: 250000, Quantity: 10, Item: "Widget A" },
|
||
{ Period: "2020-12-31T00:00:00", Amount: 50000, Quantity: 5, Item: "Widget B" }
|
||
]
|
||
},
|
||
{
|
||
rows: [
|
||
{ Period: "2020-01-10T00:00:00", Amount: 200000, Quantity: 8, Item: "Widget A" },
|
||
{ Period: "2020-11-01T00:00:00", Amount: 50000, Quantity: 2, Item: "Widget B" }
|
||
]
|
||
},
|
||
{
|
||
rows: [
|
||
{ Period: "2020-01-15T00:00:00", Registrator: "Purchase 1" },
|
||
{ Period: "2020-12-15T00:00:00", Registrator: "Purchase 2" }
|
||
]
|
||
},
|
||
{
|
||
rows: [
|
||
{ Period: "2020-03-01T00:00:00", Amount: 600000, Item: "Widget A", Counterparty: "Client A", AccountKt: "41.01" },
|
||
{ Period: "2020-02-01T00:00:00", Amount: 240000, Item: "Widget A", Counterparty: "Supplier A", AccountDt: "41.01" }
|
||
]
|
||
}
|
||
]);
|
||
const result = await runAssistantMcpDiscoveryRuntimeBridge({
|
||
dataNeedGraph: {
|
||
schema_version: "assistant_data_need_graph_v1",
|
||
policy_owner: "assistantMcpDiscoveryDataNeedGraph",
|
||
subject_candidates: [],
|
||
business_fact_family: "business_overview",
|
||
action_family: "inventory_reserve_boundary",
|
||
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",
|
||
"fetch_supporting_documents",
|
||
"probe_coverage",
|
||
"explain_evidence_basis"
|
||
],
|
||
forbidden_overclaim_flags: [
|
||
"no_raw_model_claims",
|
||
"no_unchecked_fact_totals",
|
||
"no_unchecked_business_health_claim"
|
||
],
|
||
reason_codes: ["data_need_graph_built", "data_need_graph_family_business_overview"]
|
||
},
|
||
turnMeaning: {
|
||
asked_domain_family: "business_overview",
|
||
asked_action_family: "inventory_reserve_boundary",
|
||
explicit_organization_scope: "OOO Alternative Plus",
|
||
explicit_date_scope: "2020"
|
||
},
|
||
deps
|
||
});
|
||
|
||
expect(result.bridge_status).toBe("answer_draft_ready");
|
||
expect(result.business_fact_answer_allowed).toBe(true);
|
||
expect(result.route_candidate).toMatchObject({
|
||
candidate_status: "ready_for_reviewed_execution",
|
||
selected_chain_id: "business_overview",
|
||
business_fact_family: "business_overview",
|
||
action_family: "inventory_reserve_boundary",
|
||
executable_now: true,
|
||
enablement_reason: null
|
||
});
|
||
expect(result.pilot.derived_business_overview?.inventory_quality_events?.evidence_status).toBe(
|
||
"reviewed_no_quality_events_found"
|
||
);
|
||
expect(result.pilot.derived_business_overview?.missing_proof_families.map((item) => item.family)).not.toContain(
|
||
"inventory_reserve_liquidation_quality"
|
||
);
|
||
expect(result.answer_draft.reason_codes).toContain("answer_contains_business_overview_inventory_quality_events");
|
||
expect(result.reason_codes).toContain("runtime_bridge_route_candidate_ready_for_reviewed_execution");
|
||
});
|
||
|
||
it("promotes profit-margin boundary when accounting 90/91/99 proof is available", async () => {
|
||
const deps = buildSequentialDeps([
|
||
{ rows: [{ Period: "2020-01-15T00:00:00", Amount: 12012833.72, Counterparty: "Client A" }] },
|
||
{ rows: [{ Period: "2020-01-20T00:00:00", Amount: 6430415.34, Counterparty: "Supplier A" }] },
|
||
{ rows: [] },
|
||
{
|
||
rows: [
|
||
{ Period: "2020-12-31T00:00:00", Registrator: "ACC90_REVENUE_KT", Amount: 12012833.72 },
|
||
{ Period: "2020-12-31T00:00:00", Registrator: "ACC90_COST_DT", Amount: 6430415.34 },
|
||
{ Period: "2020-12-31T00:00:00", Registrator: "ACC90_SELLING_DT", Amount: 1715450.75 },
|
||
{ Period: "2020-12-31T00:00:00", Registrator: "ACC90_RESULT_TO_99_PROFIT", Amount: 3053486.79 },
|
||
{ Period: "2020-12-31T00:00:00", Registrator: "ACC90_RESULT_FROM_99_LOSS", Amount: 1188658.09 },
|
||
{ Period: "2020-12-31T00:00:00", Registrator: "ACC91_RESULT_FROM_99_LOSS", Amount: 9001644.55 },
|
||
{ Period: "2020-12-31T00:00:00", Registrator: "ACC84_TO99_LOSS_TRANSFER", Amount: 7136815.85 }
|
||
]
|
||
},
|
||
{ rows: [] },
|
||
{ rows: [] },
|
||
{ rows: [] },
|
||
{ rows: [] },
|
||
{ rows: [] },
|
||
{ rows: [{ Period: "2020-01-15T00:00:00", Registrator: "Purchase 1" }] },
|
||
{ rows: [] },
|
||
{ rows: [] },
|
||
{ rows: [] },
|
||
{ rows: [] }
|
||
]);
|
||
const result = await runAssistantMcpDiscoveryRuntimeBridge({
|
||
dataNeedGraph: {
|
||
schema_version: "assistant_data_need_graph_v1",
|
||
policy_owner: "assistantMcpDiscoveryDataNeedGraph",
|
||
subject_candidates: [],
|
||
business_fact_family: "business_overview",
|
||
action_family: "profit_margin_boundary",
|
||
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",
|
||
"fetch_supporting_documents",
|
||
"probe_coverage",
|
||
"explain_evidence_basis"
|
||
],
|
||
forbidden_overclaim_flags: [
|
||
"no_raw_model_claims",
|
||
"no_unchecked_fact_totals",
|
||
"no_unchecked_business_health_claim"
|
||
],
|
||
reason_codes: ["data_need_graph_built", "data_need_graph_family_business_overview"]
|
||
},
|
||
turnMeaning: {
|
||
asked_domain_family: "business_overview",
|
||
asked_action_family: "profit_margin_boundary",
|
||
explicit_organization_scope: "OOO Alternative Plus",
|
||
explicit_date_scope: "2020"
|
||
},
|
||
deps
|
||
});
|
||
const overview = result.pilot.derived_business_overview;
|
||
const missingFamilies = overview?.missing_proof_families.map((item) => item.family) ?? [];
|
||
const userFacing = [
|
||
result.answer_draft.headline,
|
||
...result.answer_draft.confirmed_lines,
|
||
...result.answer_draft.inference_lines,
|
||
...result.answer_draft.unknown_lines,
|
||
...result.answer_draft.limitation_lines,
|
||
result.answer_draft.next_step_line ?? ""
|
||
].join("\n");
|
||
|
||
expect(result.bridge_status).toBe("answer_draft_ready");
|
||
expect(result.business_fact_answer_allowed).toBe(true);
|
||
expect(result.route_candidate).toMatchObject({
|
||
candidate_status: "ready_for_reviewed_execution",
|
||
selected_chain_id: "business_overview",
|
||
business_fact_family: "business_overview",
|
||
action_family: "profit_margin_boundary",
|
||
executable_now: true
|
||
});
|
||
expect(overview?.accounting_financial_result).toMatchObject({
|
||
period_scope: "2020",
|
||
final_result_amount: -7136815.85,
|
||
final_result_direction: "loss",
|
||
final_transfer_basis: "account_99_to_84_period_close",
|
||
period_close_rows_with_amount: 4
|
||
});
|
||
expect(overview?.accounting_financial_result?.net_margin_to_revenue_pct).toBe(-59.41);
|
||
expect(missingFamilies).not.toContain("accounting_profit_margin");
|
||
expect(userFacing).toContain("90/91/99");
|
||
expect(userFacing).toContain("7 136 815,85");
|
||
expect(userFacing).not.toContain("90.01");
|
||
expect(userFacing).not.toContain("operating-flow/trading-margin proxy");
|
||
expect(result.reason_codes).toContain("pilot_derived_business_overview_accounting_financial_result_from_confirmed_rows");
|
||
expect(result.reason_codes).toContain("answer_contains_business_overview_accounting_financial_result");
|
||
expect(result.reason_codes).toContain("runtime_bridge_route_candidate_ready_for_reviewed_execution");
|
||
});
|
||
|
||
it("promotes debt due-date boundary after reviewed payment-term route returns a checked negative", async () => {
|
||
const deps = buildSequentialDeps([
|
||
{ rows: [] },
|
||
{ rows: [] },
|
||
{ rows: [] },
|
||
{ rows: [] },
|
||
{ rows: [] },
|
||
{ rows: [] },
|
||
{
|
||
rows: [
|
||
{ Period: "2020-12-31T00:00:00", Amount: 100000, Counterparty: "Клиент А", Contract: "Договор А" }
|
||
]
|
||
},
|
||
{
|
||
rows: [
|
||
{
|
||
Period: "2020-12-31T00:00:00",
|
||
Amount: 100000,
|
||
Counterparty: "Клиент А",
|
||
Contract: "Договор А",
|
||
ДокументРасчетов: "Реализация товаров от 10.03.2020",
|
||
УстановленСрокОплаты: false,
|
||
СрокОплаты: 0
|
||
}
|
||
]
|
||
},
|
||
{ rows: [] },
|
||
{ rows: [] },
|
||
{ rows: [] },
|
||
{ rows: [] },
|
||
{ rows: [] },
|
||
{ rows: [] },
|
||
{ rows: [] }
|
||
]);
|
||
const result = await runAssistantMcpDiscoveryRuntimeBridge({
|
||
dataNeedGraph: {
|
||
schema_version: "assistant_data_need_graph_v1",
|
||
policy_owner: "assistantMcpDiscoveryDataNeedGraph",
|
||
subject_candidates: [],
|
||
business_fact_family: "business_overview",
|
||
action_family: "debt_due_date_boundary",
|
||
aggregation_need: null,
|
||
time_scope_need: "explicit_period",
|
||
comparison_need: null,
|
||
ranking_need: null,
|
||
proof_expectation: "due_date_aging",
|
||
clarification_gaps: [],
|
||
decomposition_candidates: [
|
||
"collect_scoped_movements",
|
||
"aggregate_checked_amounts",
|
||
"fetch_supporting_documents",
|
||
"probe_coverage",
|
||
"explain_evidence_basis"
|
||
],
|
||
forbidden_overclaim_flags: [
|
||
"no_raw_model_claims",
|
||
"no_unchecked_overdue_claim"
|
||
],
|
||
reason_codes: ["data_need_graph_built", "data_need_graph_family_business_overview"]
|
||
},
|
||
turnMeaning: {
|
||
asked_domain_family: "business_overview",
|
||
asked_action_family: "debt_due_date_boundary",
|
||
explicit_organization_scope: "ООО Альтернатива Плюс",
|
||
explicit_date_scope: "2020"
|
||
},
|
||
deps
|
||
});
|
||
const userFacing = [
|
||
result.answer_draft.headline,
|
||
...result.answer_draft.confirmed_lines,
|
||
...result.answer_draft.unknown_lines
|
||
].join("\n");
|
||
|
||
expect(result.bridge_status).toBe("answer_draft_ready");
|
||
expect(result.route_candidate).toMatchObject({
|
||
candidate_status: "ready_for_reviewed_execution",
|
||
selected_chain_id: "business_overview",
|
||
business_fact_family: "business_overview",
|
||
action_family: "debt_due_date_boundary",
|
||
executable_now: true
|
||
});
|
||
expect(result.pilot.derived_business_overview?.debt_due_date_aging).toMatchObject({
|
||
evidence_status: "no_payment_terms_configured",
|
||
rows_without_payment_terms: 1,
|
||
overdue_rows: 0
|
||
});
|
||
expect(userFacing).toContain("срок оплаты не установлен");
|
||
expect(userFacing).toContain("Подтвержденной просрочки");
|
||
expect(result.reason_codes).toContain("runtime_bridge_route_candidate_ready_for_reviewed_execution");
|
||
expect(result.reason_codes).toContain("answer_contains_business_overview_debt_due_date_aging_no_payment_terms_configured");
|
||
});
|
||
|
||
it("promotes vendor-risk boundary through reviewed procurement concentration evidence", async () => {
|
||
const deps = buildSequentialDeps([
|
||
{ rows: [] },
|
||
{
|
||
rows: [
|
||
{ Period: "2020-01-20T00:00:00", Amount: 700000, Counterparty: "СБЕРБАНК, ПАО" },
|
||
{ Period: "2020-02-20T00:00:00", Amount: 300000, Counterparty: "Поставщик А" }
|
||
]
|
||
},
|
||
{ rows: [] },
|
||
{ rows: [] },
|
||
{
|
||
rows: [
|
||
{ Period: "2020-01-01T00:00:00", Registrator: "CP_TOTAL", Amount: 12 },
|
||
{ Period: "2020-01-01T00:00:00", Registrator: "CP_SUPPLIER_ACTIVE", Amount: 5 },
|
||
{ Period: "2020-01-01T00:00:00", Registrator: "CP_MIXED_ACTIVE", Amount: 2 },
|
||
{ Period: "2020-01-01T00:00:00", Registrator: "CP_ACTIVE_UNION", Amount: 7 }
|
||
]
|
||
},
|
||
{
|
||
rows: [
|
||
{ Period: "2020-01-01T00:00:00", Registrator: "CT_TOTAL", Amount: 11 },
|
||
{ Period: "2020-01-01T00:00:00", Registrator: "CT_USED", Amount: 4 }
|
||
]
|
||
}
|
||
]);
|
||
const result = await runAssistantMcpDiscoveryRuntimeBridge({
|
||
dataNeedGraph: {
|
||
schema_version: "assistant_data_need_graph_v1",
|
||
policy_owner: "assistantMcpDiscoveryDataNeedGraph",
|
||
subject_candidates: [],
|
||
business_fact_family: "business_overview",
|
||
action_family: "vendor_risk_procurement_boundary",
|
||
aggregation_need: null,
|
||
time_scope_need: "all_time_scope",
|
||
comparison_need: null,
|
||
ranking_need: null,
|
||
proof_expectation: "vendor_risk_procurement_quality",
|
||
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_unchecked_vendor_reliability_claim"
|
||
],
|
||
reason_codes: ["data_need_graph_built", "data_need_graph_family_business_overview"]
|
||
},
|
||
turnMeaning: {
|
||
asked_domain_family: "business_overview",
|
||
asked_action_family: "vendor_risk_procurement_boundary",
|
||
explicit_organization_scope: "OOO Alternative Plus"
|
||
},
|
||
deps
|
||
});
|
||
const overview = result.pilot.derived_business_overview;
|
||
const missingFamilies = overview?.missing_proof_families.map((item) => item.family) ?? [];
|
||
const userFacing = [
|
||
result.answer_draft.headline,
|
||
...result.answer_draft.confirmed_lines,
|
||
...result.answer_draft.inference_lines,
|
||
...result.answer_draft.unknown_lines,
|
||
...result.answer_draft.limitation_lines,
|
||
result.answer_draft.next_step_line ?? ""
|
||
].join("\n");
|
||
|
||
expect(result.bridge_status).toBe("answer_draft_ready");
|
||
expect(result.business_fact_answer_allowed).toBe(true);
|
||
expect(result.route_candidate).toMatchObject({
|
||
candidate_status: "ready_for_reviewed_execution",
|
||
selected_chain_id: "business_overview",
|
||
business_fact_family: "business_overview",
|
||
action_family: "vendor_risk_procurement_boundary",
|
||
executable_now: true
|
||
});
|
||
expect(overview?.vendor_procurement_quality).toMatchObject({
|
||
total_outgoing_amount: 1000000,
|
||
top_outgoing_share_pct: 70,
|
||
top_non_financial_supplier_share_pct: 30,
|
||
financial_institution_leads_outgoing_cash: true,
|
||
supplier_only_count: 3,
|
||
mixed_role_count: 2,
|
||
used_contracts: 4,
|
||
total_contracts: 11,
|
||
evidence_status: "financial_institution_leads_outgoing_cash"
|
||
});
|
||
expect(missingFamilies).not.toContain("vendor_risk_procurement_quality");
|
||
expect(userFacing).not.toContain("procurement-concentration route");
|
||
expect(userFacing).toContain("банк/финансовая организация");
|
||
expect(userFacing).toContain("Поставщик А");
|
||
expect(userFacing).toContain("Надежность поставщиков");
|
||
expect(userFacing).not.toContain("outgoing cash concentration proxy");
|
||
expect(result.reason_codes).toContain("runtime_bridge_route_candidate_ready_for_reviewed_execution");
|
||
expect(result.reason_codes).toContain("answer_contains_business_overview_vendor_procurement_quality_financial_institution_leads_outgoing_cash");
|
||
});
|
||
|
||
it("bridges selected-item inventory provenance templates through exact document evidence", async () => {
|
||
const deps = buildDeps([
|
||
{
|
||
Period: "2021-08-10T00:00:00",
|
||
Item: "Widget A",
|
||
Counterparty: "Supplier One",
|
||
Registrator: "Purchase 0001"
|
||
}
|
||
]);
|
||
const result = await runAssistantMcpDiscoveryRuntimeBridge({
|
||
dataNeedGraph: {
|
||
schema_version: "assistant_data_need_graph_v1",
|
||
policy_owner: "assistantMcpDiscoveryDataNeedGraph",
|
||
subject_candidates: ["Widget A"],
|
||
business_fact_family: "inventory_purchase_provenance",
|
||
action_family: "purchase_provenance",
|
||
aggregation_need: null,
|
||
time_scope_need: null,
|
||
comparison_need: null,
|
||
ranking_need: null,
|
||
proof_expectation: "coverage_checked_fact",
|
||
clarification_gaps: [],
|
||
decomposition_candidates: [
|
||
"resolve_entity_reference",
|
||
"fetch_scoped_documents",
|
||
"drilldown_related_objects",
|
||
"probe_coverage",
|
||
"explain_evidence_basis"
|
||
],
|
||
forbidden_overclaim_flags: ["no_raw_model_claims", "no_unproven_supplier_attribution"],
|
||
reason_codes: ["data_need_graph_built", "data_need_graph_family_inventory_purchase_provenance"]
|
||
},
|
||
turnMeaning: {
|
||
asked_domain_family: "inventory_stock",
|
||
asked_action_family: "purchase_provenance",
|
||
explicit_entity_candidates: ["Widget A"]
|
||
},
|
||
deps
|
||
});
|
||
const userFacing = [
|
||
result.answer_draft.headline,
|
||
...result.answer_draft.confirmed_lines,
|
||
...result.answer_draft.inference_lines,
|
||
...result.answer_draft.unknown_lines,
|
||
...result.answer_draft.limitation_lines,
|
||
result.answer_draft.next_step_line ?? ""
|
||
].join("\n");
|
||
|
||
expect(result.bridge_status).toBe("answer_draft_ready");
|
||
expect(result.business_fact_answer_allowed).toBe(true);
|
||
expect(result.planner.selected_chain_id).toBe("inventory_purchase_provenance");
|
||
expect(result.pilot.executed_primitives).toEqual(["query_documents"]);
|
||
expect(deps.executeAddressMcpQuery).toHaveBeenCalledWith(expect.objectContaining({ account_scope: ["41.01"] }));
|
||
expect(userFacing).toContain("Widget A");
|
||
expect(userFacing).toContain("Supplier One");
|
||
expect(userFacing).not.toContain("inventory_route_template_v1");
|
||
expect(userFacing).not.toContain("query_documents");
|
||
expect(userFacing).not.toContain("primitive");
|
||
});
|
||
|
||
it("keeps selected-item inventory templates in clarification when the item anchor is missing", async () => {
|
||
const result = await runAssistantMcpDiscoveryRuntimeBridge({
|
||
dataNeedGraph: {
|
||
schema_version: "assistant_data_need_graph_v1",
|
||
policy_owner: "assistantMcpDiscoveryDataNeedGraph",
|
||
subject_candidates: [],
|
||
business_fact_family: "inventory_sale_trace",
|
||
action_family: "sale_trace",
|
||
aggregation_need: null,
|
||
time_scope_need: null,
|
||
comparison_need: null,
|
||
ranking_need: null,
|
||
proof_expectation: "clarification_required",
|
||
clarification_gaps: ["item"],
|
||
decomposition_candidates: [
|
||
"resolve_entity_reference",
|
||
"fetch_scoped_documents",
|
||
"drilldown_related_objects",
|
||
"probe_coverage",
|
||
"explain_evidence_basis"
|
||
],
|
||
forbidden_overclaim_flags: ["no_raw_model_claims", "no_unproven_buyer_or_sale_trace"],
|
||
reason_codes: ["data_need_graph_built", "data_need_graph_family_inventory_sale_trace"]
|
||
},
|
||
turnMeaning: {
|
||
asked_domain_family: "inventory_stock",
|
||
asked_action_family: "sale_trace"
|
||
},
|
||
deps: buildDeps([])
|
||
});
|
||
|
||
expect(result.bridge_status).toBe("needs_clarification");
|
||
expect(result.requires_user_clarification).toBe(true);
|
||
expect(result.pilot.pilot_scope).toBe("inventory_route_template_v1");
|
||
expect(result.pilot.mcp_execution_performed).toBe(false);
|
||
expect(result.reason_codes).toContain("runtime_bridge_loop_state_awaiting_clarification");
|
||
expect(result.loop_state.pending_axes).toContain("item");
|
||
});
|
||
|
||
it("keeps document evidence executable when the planner expands primitives from fact-axis search", async () => {
|
||
const result = await runAssistantMcpDiscoveryRuntimeBridge({
|
||
dataNeedGraph: {
|
||
schema_version: "assistant_data_need_graph_v1",
|
||
policy_owner: "assistantMcpDiscoveryDataNeedGraph",
|
||
subject_candidates: ["SVK"],
|
||
business_fact_family: "document_evidence",
|
||
action_family: "list_documents",
|
||
aggregation_need: null,
|
||
time_scope_need: "explicit_period",
|
||
comparison_need: null,
|
||
ranking_need: null,
|
||
proof_expectation: "coverage_checked_fact",
|
||
clarification_gaps: [],
|
||
decomposition_candidates: [],
|
||
forbidden_overclaim_flags: ["no_raw_model_claims", "no_unchecked_fact_totals"],
|
||
reason_codes: ["data_need_graph_built"]
|
||
},
|
||
turnMeaning: {
|
||
asked_domain_family: "documents",
|
||
asked_action_family: "list_documents",
|
||
explicit_entity_candidates: ["SVK"],
|
||
explicit_date_scope: "2020",
|
||
unsupported_but_understood_family: "document_evidence"
|
||
},
|
||
deps: buildDeps([{ Period: "2020-01-15T00:00:00", Registrator: "DOC-1", Counterparty: "SVK" }])
|
||
});
|
||
|
||
expect(result.bridge_status).toBe("answer_draft_ready");
|
||
expect(result.planner.selected_chain_id).toBe("document_evidence");
|
||
expect(result.planner.proposed_primitives).toEqual(["resolve_entity_reference", "query_documents", "probe_coverage"]);
|
||
expect(result.planner.reason_codes).toContain("planner_selected_catalog_primitives_from_fact_axis_search");
|
||
expect(result.business_fact_answer_allowed).toBe(true);
|
||
});
|
||
|
||
it("preserves the answer adapter boundary against internal mechanics leakage", async () => {
|
||
const result = await runAssistantMcpDiscoveryRuntimeBridge({
|
||
turnMeaning: {
|
||
asked_domain_family: "counterparty_lifecycle",
|
||
asked_action_family: "activity_duration",
|
||
explicit_entity_candidates: ["SVK"]
|
||
},
|
||
deps: buildDeps([{ Период: "2020-01-15T00:00:00", Регистратор: "CP_CUSTOMER_ACTIVITY_FIRST" }])
|
||
});
|
||
const userFacing = [
|
||
result.answer_draft.headline,
|
||
...result.answer_draft.confirmed_lines,
|
||
...result.answer_draft.inference_lines,
|
||
...result.answer_draft.unknown_lines,
|
||
...result.answer_draft.limitation_lines,
|
||
result.answer_draft.next_step_line ?? ""
|
||
].join("\n");
|
||
|
||
expect(userFacing).not.toContain("query_documents");
|
||
expect(userFacing).not.toContain("runtime_bridge");
|
||
expect(userFacing).not.toContain("primitive");
|
||
});
|
||
it("produces a bounded one-sided value-flow answer for an organization-scoped total without inventing a counterparty", async () => {
|
||
const result = await runAssistantMcpDiscoveryRuntimeBridge({
|
||
dataNeedGraph: {
|
||
schema_version: "assistant_data_need_graph_v1",
|
||
policy_owner: "assistantMcpDiscoveryDataNeedGraph",
|
||
subject_candidates: [],
|
||
business_fact_family: "value_flow",
|
||
action_family: "turnover",
|
||
aggregation_need: null,
|
||
time_scope_need: "explicit_period",
|
||
comparison_need: null,
|
||
ranking_need: null,
|
||
proof_expectation: "coverage_checked_fact",
|
||
clarification_gaps: [],
|
||
decomposition_candidates: ["collect_scoped_movements", "aggregate_checked_amounts", "probe_coverage"],
|
||
forbidden_overclaim_flags: ["no_raw_model_claims", "no_unchecked_fact_totals"],
|
||
reason_codes: ["data_need_graph_built", "data_need_graph_open_scope_total_without_subject"]
|
||
},
|
||
turnMeaning: {
|
||
asked_domain_family: "counterparty_value",
|
||
asked_action_family: "turnover",
|
||
explicit_date_scope: "2020",
|
||
explicit_organization_scope: "ООО Альтернатива Плюс"
|
||
},
|
||
deps: buildDeps([
|
||
{ Period: "2020-01-10T00:00:00", Amount: 3200, Counterparty: "Клиент-А" },
|
||
{ Period: "2020-05-22T00:00:00", Amount: 1800, Counterparty: "Клиент-Б" }
|
||
])
|
||
});
|
||
|
||
expect(result.bridge_status).toBe("answer_draft_ready");
|
||
expect(result.business_fact_answer_allowed).toBe(true);
|
||
expect(result.planner.selected_chain_id).toBe("value_flow");
|
||
expect(result.pilot.derived_value_flow).toMatchObject({
|
||
counterparty: null,
|
||
period_scope: "2020",
|
||
total_amount: 5000
|
||
});
|
||
expect(result.answer_draft.confirmed_lines.join("\n")).toContain("5 000");
|
||
expect(result.answer_draft.confirmed_lines.join("\n")).not.toContain("контрагенту");
|
||
});
|
||
it("keeps generic one-sided open totals in organization clarification without asking for a counterparty", async () => {
|
||
const result = await runAssistantMcpDiscoveryRuntimeBridge({
|
||
dataNeedGraph: {
|
||
schema_version: "assistant_data_need_graph_v1",
|
||
policy_owner: "assistantMcpDiscoveryDataNeedGraph",
|
||
subject_candidates: [],
|
||
business_fact_family: "value_flow",
|
||
action_family: "turnover",
|
||
aggregation_need: null,
|
||
time_scope_need: "explicit_period",
|
||
comparison_need: null,
|
||
ranking_need: null,
|
||
proof_expectation: "clarification_required",
|
||
clarification_gaps: ["organization"],
|
||
decomposition_candidates: ["collect_scoped_movements", "aggregate_checked_amounts", "probe_coverage"],
|
||
forbidden_overclaim_flags: ["no_raw_model_claims", "no_unchecked_fact_totals"],
|
||
reason_codes: [
|
||
"data_need_graph_built",
|
||
"data_need_graph_open_scope_total_without_subject",
|
||
"data_need_graph_open_scope_total_needs_organization"
|
||
]
|
||
},
|
||
turnMeaning: {
|
||
asked_domain_family: "counterparty_value",
|
||
asked_action_family: "turnover",
|
||
explicit_date_scope: "2020"
|
||
},
|
||
deps: buildDeps([])
|
||
});
|
||
|
||
expect(result.bridge_status).toBe("needs_clarification");
|
||
expect(result.requires_user_clarification).toBe(true);
|
||
expect(result.planner.selected_chain_id).toBe("value_flow");
|
||
expect(result.answer_draft.next_step_line).toContain("\u043e\u0440\u0433\u0430\u043d\u0438\u0437\u0430\u0446\u0438\u044e");
|
||
expect(result.answer_draft.next_step_line).not.toContain(
|
||
"\u043a\u043e\u043d\u0442\u0440\u0430\u0433\u0435\u043d\u0442\u0430"
|
||
);
|
||
});
|
||
it("persists metadata scope and subject-optional flags in the resumable loop state", async () => {
|
||
const result = await runAssistantMcpDiscoveryRuntimeBridge({
|
||
dataNeedGraph: {
|
||
schema_version: "assistant_data_need_graph_v1",
|
||
policy_owner: "assistantMcpDiscoveryDataNeedGraph",
|
||
subject_candidates: [],
|
||
metadata_scope_hint: "\u041d\u0414\u0421",
|
||
subject_resolution_optional: true,
|
||
business_fact_family: "movement_evidence",
|
||
action_family: "list_movements",
|
||
aggregation_need: null,
|
||
time_scope_need: "period_required",
|
||
comparison_need: null,
|
||
ranking_need: null,
|
||
proof_expectation: "clarification_required",
|
||
clarification_gaps: ["organization", "period"],
|
||
decomposition_candidates: ["fetch_scoped_movements", "probe_coverage"],
|
||
forbidden_overclaim_flags: ["no_raw_model_claims", "no_unchecked_fact_totals"],
|
||
reason_codes: ["data_need_graph_built", "data_need_graph_metadata_scoped_open_lane_without_subject"]
|
||
},
|
||
turnMeaning: {
|
||
asked_domain_family: "movements",
|
||
asked_action_family: "list_movements",
|
||
metadata_scope_hint: "\u041d\u0414\u0421",
|
||
subject_resolution_optional: true,
|
||
unsupported_but_understood_family: "movement_evidence"
|
||
},
|
||
deps: buildDeps([])
|
||
});
|
||
|
||
expect(result.bridge_status).toBe("needs_clarification");
|
||
expect(result.loop_state).toMatchObject({
|
||
loop_status: "awaiting_clarification",
|
||
selected_chain_id: "movement_evidence",
|
||
metadata_scope_hint: "\u041d\u0414\u0421",
|
||
subject_resolution_optional: true
|
||
});
|
||
expect(result.loop_state.pending_axes).toEqual(["organization", "period"]);
|
||
expect(result.loop_state.explicit_entity_candidates).toEqual([]);
|
||
expect(result.route_candidate).toMatchObject({
|
||
candidate_status: "needs_user_scope",
|
||
selected_chain_id: "movement_evidence",
|
||
business_fact_family: "movement_evidence",
|
||
action_family: "list_movements",
|
||
proof_expectation: "clarification_required",
|
||
executable_now: false
|
||
});
|
||
expect(result.route_candidate.missing_axes).toEqual(["organization", "period"]);
|
||
expect(result.route_candidate.forbidden_overclaim_flags).toContain("no_unchecked_fact_totals");
|
||
});
|
||
});
|