import { describe, expect, it } from "vitest"; import { planAssistantMcpDiscovery } from "../src/services/assistantMcpDiscoveryPlanner"; describe("assistant MCP discovery planner", () => { it("builds a catalog-compatible value-flow discovery plan from current turn meaning", () => { const result = planAssistantMcpDiscovery({ dataNeedGraph: { schema_version: "assistant_data_need_graph_v1", policy_owner: "assistantMcpDiscoveryDataNeedGraph", subject_candidates: ["SVK"], 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: [ "resolve_entity_reference", "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"] }, turnMeaning: { asked_domain_family: "counterparty_value", asked_action_family: "turnover", explicit_entity_candidates: ["SVK"], explicit_date_scope: "2020" } }); expect(result.planner_status).toBe("ready_for_execution"); expect(result.semantic_data_need).toBe("counterparty value-flow evidence"); expect(result.selected_chain_id).toBe("value_flow"); expect(result.selected_chain_summary).toContain("query scoped movements"); expect(result.proposed_primitives).toEqual([ "resolve_entity_reference", "query_movements", "aggregate_by_axis", "probe_coverage" ]); expect(result.required_axes).toEqual(["counterparty", "period", "aggregate_axis", "amount", "coverage_target"]); expect(result.catalog_review.review_status).toBe("catalog_compatible"); expect(result.discovery_plan.answer_may_use_raw_model_claims).toBe(false); expect(result.data_need_graph?.business_fact_family).toBe("value_flow"); expect(result.discovery_plan.execution_budget.max_probe_count).toBe(30); expect(result.reason_codes).toContain("planner_enabled_chunked_coverage_probe_budget"); expect(result.reason_codes).toContain("planner_consumed_data_need_graph_v1"); expect(result.reason_codes).toContain("planner_selected_catalog_primitives_from_decomposition_candidates"); }); it("keeps a value-flow plan in clarification state when period axis is missing", () => { const result = planAssistantMcpDiscovery({ turnMeaning: { asked_domain_family: "counterparty_value", asked_action_family: "turnover", explicit_entity_candidates: ["SVK"] } }); expect(result.planner_status).toBe("needs_clarification"); expect(result.catalog_review.review_status).toBe("needs_more_axes"); expect(result.catalog_review.missing_axes_by_primitive.query_movements).toContainEqual(["period", "counterparty"]); expect(result.reason_codes).toContain("planner_needs_more_user_or_scope_context"); }); it("keeps requested monthly aggregation as an explicit planning axis for value-flow discovery", () => { const result = 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" } }); expect(result.planner_status).toBe("ready_for_execution"); expect(result.proposed_primitives).toEqual([ "resolve_entity_reference", "query_movements", "aggregate_by_axis", "probe_coverage" ]); expect(result.required_axes).toEqual([ "counterparty", "period", "aggregate_axis", "amount", "coverage_target", "calendar_month" ]); expect(result.reason_codes).toContain("planner_selected_monthly_value_flow_recipe"); expect(result.discovery_plan.execution_budget.max_probe_count).toBe(30); }); it("builds a document discovery plan without falling back to movement primitives", () => { const result = planAssistantMcpDiscovery({ turnMeaning: { asked_domain_family: "counterparty_documents", asked_action_family: "list_documents", explicit_entity_candidates: ["SVK"] } }); expect(result.planner_status).toBe("ready_for_execution"); expect(result.proposed_primitives).toEqual(["resolve_entity_reference", "query_documents", "probe_coverage"]); expect(result.proposed_primitives).not.toContain("query_movements"); expect(result.required_axes).toEqual(["counterparty", "coverage_target"]); }); it("expands a document evidence chain from catalog fact-axis search when decomposition hints are absent", () => { const result = planAssistantMcpDiscovery({ 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" } }); expect(result.planner_status).toBe("ready_for_execution"); expect(result.selected_chain_id).toBe("document_evidence"); expect(result.proposed_primitives).toEqual(["resolve_entity_reference", "query_documents", "probe_coverage"]); expect(result.reason_codes).toContain("planner_selected_catalog_primitives_from_fact_axis_search"); expect(result.reason_codes).toContain("planner_instantiated_catalog_chain_template_document_evidence"); expect(result.reason_codes).not.toContain("planner_fell_back_to_recipe_primitives_after_empty_catalog_search"); }); it("filters conflicting document-vs-movement primitives when confirmed metadata surface recommends documents", () => { const result = planAssistantMcpDiscovery({ 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: ["fetch_scoped_documents", "fetch_scoped_movements", "probe_coverage"], forbidden_overclaim_flags: ["no_raw_model_claims", "no_unchecked_fact_totals"], reason_codes: ["data_need_graph_built"] }, metadataSurface: { selected_entity_set: "Документ", selected_surface_objects: ["Документ.СчетФактураВыданный"], downstream_route_family: "document_evidence", route_family_selection_basis: "selected_entity_set", recommended_next_primitive: "query_documents", ambiguity_detected: false, ambiguity_entity_sets: [] }, turnMeaning: { asked_domain_family: "documents", asked_action_family: "list_documents", explicit_entity_candidates: ["SVK"], explicit_date_scope: "2020", unsupported_but_understood_family: "document_evidence" } }); expect(result.planner_status).toBe("ready_for_execution"); expect(result.metadata_surface_ref?.recommended_next_primitive).toBe("query_documents"); expect(result.proposed_primitives).toEqual(["resolve_entity_reference", "query_documents", "probe_coverage"]); expect(result.proposed_primitives).not.toContain("query_movements"); expect(result.reason_codes).toContain("planner_consumed_metadata_surface_ref_v1"); expect(result.reason_codes).toContain("planner_filtered_catalog_primitives_by_confirmed_metadata_surface"); expect(result.reason_codes).toContain("planner_surface_aware_next_lane_from_confirmed_metadata_objects"); }); it("can select document evidence directly from a confirmed metadata surface when the follow-up itself is thin", () => { const result = planAssistantMcpDiscovery({ metadataSurface: { selected_entity_set: "Document", selected_surface_objects: ["Document.InvoiceIssued"], downstream_route_family: "document_evidence", route_family_selection_basis: "selected_entity_set", recommended_next_primitive: "query_documents", ambiguity_detected: false, ambiguity_entity_sets: [] }, turnMeaning: { explicit_entity_candidates: ["SVK"], explicit_date_scope: "2020" } }); expect(result.planner_status).toBe("ready_for_execution"); expect(result.selected_chain_id).toBe("document_evidence"); expect(result.proposed_primitives).toEqual(["resolve_entity_reference", "query_documents", "probe_coverage"]); expect(result.required_axes).toEqual(["counterparty", "period", "coverage_target"]); expect(result.reason_codes).toContain("planner_selected_document_from_confirmed_metadata_surface_ref"); expect(result.reason_codes).toContain("planner_selected_catalog_primitives_from_metadata_surface_search"); }); it("builds a movement discovery plan without aggregating value-flow totals", () => { const result = planAssistantMcpDiscovery({ dataNeedGraph: { schema_version: "assistant_data_need_graph_v1", policy_owner: "assistantMcpDiscoveryDataNeedGraph", subject_candidates: ["SVK"], business_fact_family: "movement_evidence", action_family: "list_movements", aggregation_need: null, time_scope_need: "explicit_period", comparison_need: null, ranking_need: null, proof_expectation: "coverage_checked_fact", clarification_gaps: [], decomposition_candidates: ["resolve_entity_reference", "fetch_scoped_movements", "probe_coverage"], forbidden_overclaim_flags: ["no_raw_model_claims", "no_unchecked_fact_totals"], reason_codes: ["data_need_graph_built"] }, turnMeaning: { asked_domain_family: "movements", asked_action_family: "list_movements", explicit_entity_candidates: ["SVK"], explicit_date_scope: "2020", unsupported_but_understood_family: "movement_evidence" } }); expect(result.planner_status).toBe("ready_for_execution"); expect(result.semantic_data_need).toBe("movement evidence"); expect(result.selected_chain_id).toBe("movement_evidence"); expect(result.selected_chain_summary).toContain("movement rows"); expect(result.proposed_primitives).toEqual(["resolve_entity_reference", "query_movements", "probe_coverage"]); expect(result.proposed_primitives).not.toContain("aggregate_by_axis"); expect(result.required_axes).toEqual(["counterparty", "period", "coverage_target"]); expect(result.reason_codes).toContain("planner_selected_movement_from_data_need_graph"); expect(result.reason_codes).toContain("planner_selected_catalog_primitives_from_decomposition_candidates"); expect(result.reason_codes).toContain("planner_instantiated_catalog_chain_template_movement_evidence"); }); it("filters conflicting document-vs-movement primitives when confirmed metadata surface recommends movements", () => { const result = planAssistantMcpDiscovery({ dataNeedGraph: { schema_version: "assistant_data_need_graph_v1", policy_owner: "assistantMcpDiscoveryDataNeedGraph", subject_candidates: ["SVK"], business_fact_family: "movement_evidence", action_family: "list_movements", 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_documents", "fetch_scoped_movements", "probe_coverage"], forbidden_overclaim_flags: ["no_raw_model_claims", "no_unchecked_fact_totals"], reason_codes: ["data_need_graph_built"] }, metadataSurface: { selected_entity_set: "РегистрНакопления", selected_surface_objects: ["РегистрНакопления.ДвиженияДенежныхСредств"], downstream_route_family: "movement_evidence", route_family_selection_basis: "dominant_surface_objects", recommended_next_primitive: "query_movements", ambiguity_detected: false, ambiguity_entity_sets: [] }, turnMeaning: { asked_domain_family: "movements", asked_action_family: "list_movements", explicit_entity_candidates: ["SVK"], explicit_date_scope: "2020", unsupported_but_understood_family: "movement_evidence" } }); expect(result.planner_status).toBe("ready_for_execution"); expect(result.metadata_surface_ref?.recommended_next_primitive).toBe("query_movements"); expect(result.proposed_primitives).toEqual(["resolve_entity_reference", "query_movements", "probe_coverage"]); expect(result.proposed_primitives).not.toContain("query_documents"); expect(result.reason_codes).toContain("planner_filtered_catalog_primitives_by_confirmed_metadata_surface"); }); it("can select movement evidence directly from a confirmed metadata surface when the follow-up itself is thin", () => { const result = planAssistantMcpDiscovery({ metadataSurface: { selected_entity_set: "AccumulationRegister", selected_surface_objects: ["Register.BankOperations"], downstream_route_family: "movement_evidence", route_family_selection_basis: "dominant_surface_objects", recommended_next_primitive: "query_movements", ambiguity_detected: false, ambiguity_entity_sets: [] }, turnMeaning: { explicit_entity_candidates: ["SVK"], explicit_date_scope: "2020" } }); expect(result.planner_status).toBe("ready_for_execution"); expect(result.selected_chain_id).toBe("movement_evidence"); expect(result.proposed_primitives).toEqual(["resolve_entity_reference", "query_movements", "probe_coverage"]); expect(result.required_axes).toEqual(["counterparty", "period", "coverage_target"]); expect(result.reason_codes).toContain("planner_selected_movement_from_confirmed_metadata_surface_ref"); expect(result.reason_codes).toContain("planner_selected_catalog_primitives_from_metadata_surface_search"); }); it("can select catalog drilldown directly from a confirmed catalog metadata surface when the follow-up itself is thin", () => { const result = 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" } }); expect(result.planner_status).toBe("ready_for_execution"); expect(result.selected_chain_id).toBe("catalog_drilldown"); expect(result.proposed_primitives).toEqual(["inspect_1c_metadata"]); expect(result.required_axes).toEqual(["metadata_scope"]); expect(result.reason_codes).toContain("planner_selected_catalog_drilldown_from_confirmed_metadata_surface_ref"); expect(result.reason_codes).toContain("planner_selected_catalog_primitives_from_metadata_surface_search"); }); it("does not force a lane from ambiguous metadata surface even when decomposition hints mention both documents and movements", () => { const result = planAssistantMcpDiscovery({ 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: ["fetch_scoped_documents", "fetch_scoped_movements", "probe_coverage"], forbidden_overclaim_flags: ["no_raw_model_claims", "no_unchecked_fact_totals"], reason_codes: ["data_need_graph_built"] }, metadataSurface: { selected_entity_set: null, selected_surface_objects: ["Документ.СчетФактураВыданный", "РегистрНакопления.ДвиженияДенежныхСредств"], downstream_route_family: null, route_family_selection_basis: null, recommended_next_primitive: null, ambiguity_detected: true, ambiguity_entity_sets: ["Документ", "РегистрНакопления"] }, turnMeaning: { asked_domain_family: "documents", asked_action_family: "list_documents", explicit_entity_candidates: ["SVK"], explicit_date_scope: "2020", unsupported_but_understood_family: "document_evidence" } }); expect(result.planner_status).toBe("ready_for_execution"); expect(result.reason_codes).toContain("planner_consumed_metadata_surface_ref_v1"); expect(result.reason_codes).not.toContain("planner_filtered_catalog_primitives_by_confirmed_metadata_surface"); expect(result.proposed_primitives).toContain("query_documents"); expect(result.proposed_primitives).toContain("query_movements"); }); it("does not force a thin follow-up into a lane when the carried metadata surface is still ambiguous", () => { const result = planAssistantMcpDiscovery({ metadataSurface: { selected_entity_set: null, selected_surface_objects: ["Document.InvoiceIssued", "Register.BankOperations"], downstream_route_family: null, route_family_selection_basis: null, recommended_next_primitive: null, ambiguity_detected: true, ambiguity_entity_sets: ["Document", "AccumulationRegister"] }, turnMeaning: { explicit_entity_candidates: ["SVK"], explicit_date_scope: "2020" } }); expect(result.planner_status).toBe("ready_for_execution"); expect(result.selected_chain_id).toBe("entity_resolution"); expect(result.reason_codes).toContain("planner_selected_entity_resolution_recipe"); expect(result.reason_codes).toContain("planner_instantiated_catalog_chain_template_entity_resolution"); expect(result.reason_codes).not.toContain("planner_selected_document_from_confirmed_metadata_surface_ref"); expect(result.reason_codes).not.toContain("planner_selected_movement_from_confirmed_metadata_surface_ref"); }); it("can select value-flow chain from data need graph even when turn meaning family is still under-specified", () => { const result = planAssistantMcpDiscovery({ 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: "by_month", time_scope_need: "explicit_period", 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", "aggregate_by_month", "probe_coverage" ], forbidden_overclaim_flags: ["no_raw_model_claims", "no_unchecked_fact_totals"], reason_codes: ["data_need_graph_built"] }, turnMeaning: { explicit_entity_candidates: ["SVK"], explicit_date_scope: "2020", asked_aggregation_axis: "month" } }); expect(result.planner_status).toBe("ready_for_execution"); expect(result.selected_chain_id).toBe("value_flow"); expect(result.proposed_primitives).toEqual([ "resolve_entity_reference", "query_movements", "aggregate_by_axis", "probe_coverage" ]); expect(result.required_axes).toEqual([ "counterparty", "period", "aggregate_axis", "amount", "coverage_target", "calendar_month" ]); expect(result.reason_codes).toContain("planner_selected_monthly_value_flow_from_data_need_graph"); expect(result.reason_codes).toContain("planner_instantiated_catalog_chain_template_value_flow"); }); it("does not collapse a ranking-shaped value graph into entity-resolution just because no subject is preselected", () => { const result = planAssistantMcpDiscovery({ 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" } }); expect(result.planner_status).toBe("needs_clarification"); expect(result.selected_chain_id).toBe("value_flow_ranking"); expect(result.proposed_primitives).toEqual(["query_movements", "aggregate_by_axis", "probe_coverage"]); expect(result.required_axes).toEqual(["period", "aggregate_axis", "amount", "coverage_target"]); expect(result.catalog_review.review_status).toBe("needs_more_axes"); expect(result.reason_codes).toContain("planner_selected_top_ranked_value_flow_from_data_need_graph"); expect(result.reason_codes).toContain("planner_instantiated_catalog_chain_template_value_flow_ranking"); expect(result.selected_chain_id).not.toBe("entity_resolution"); }); it("keeps ranked value-flow ready for execution once checked period and organization are known", () => { const result = planAssistantMcpDiscovery({ 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: "ООО Альтернатива Плюс" } }); expect(result.planner_status).toBe("ready_for_execution"); expect(result.selected_chain_id).toBe("value_flow_ranking"); expect(result.proposed_primitives).toEqual(["query_movements", "aggregate_by_axis", "probe_coverage"]); expect(result.required_axes).toEqual(["organization", "period", "aggregate_axis", "amount", "coverage_target"]); expect(result.catalog_review.review_status).toBe("catalog_compatible"); expect(result.reason_codes).toContain("planner_selected_top_ranked_value_flow_from_data_need_graph"); expect(result.reason_codes).toContain("planner_instantiated_catalog_chain_template_value_flow_ranking"); }); it("does not collapse incoming-vs-outgoing comparison into entity-resolution when no counterparty is preselected", () => { const result = planAssistantMcpDiscovery({ 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" } }); expect(result.planner_status).toBe("needs_clarification"); expect(result.selected_chain_id).toBe("value_flow_comparison"); expect(result.proposed_primitives).toEqual(["query_movements", "probe_coverage"]); expect(result.required_axes).toEqual(["period", "amount", "coverage_target"]); expect(result.reason_codes).toContain("planner_selected_bidirectional_value_flow_comparison_from_data_need_graph"); expect(result.reason_codes).toContain("planner_instantiated_catalog_chain_template_value_flow_comparison"); expect(result.selected_chain_id).not.toBe("entity_resolution"); }); it("keeps bidirectional comparison ready for execution once checked period and organization are known", () => { const result = planAssistantMcpDiscovery({ 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: "РћРћРћ Альтернатива Плюс" } }); expect(result.planner_status).toBe("ready_for_execution"); expect(result.selected_chain_id).toBe("value_flow_comparison"); expect(result.proposed_primitives).toEqual(["query_movements", "probe_coverage"]); expect(result.required_axes).toEqual(["organization", "period", "amount", "coverage_target"]); expect(result.catalog_review.review_status).toBe("catalog_compatible"); expect(result.reason_codes).toContain("planner_selected_bidirectional_value_flow_comparison_from_data_need_graph"); expect(result.reason_codes).toContain("planner_instantiated_catalog_chain_template_value_flow_comparison"); }); it("builds an inference-safe lifecycle plan with evidence explanation", () => { const result = planAssistantMcpDiscovery({ turnMeaning: { asked_domain_family: "counterparty_lifecycle", asked_action_family: "activity_duration", explicit_entity_candidates: ["SVK"] } }); expect(result.planner_status).toBe("ready_for_execution"); expect(result.proposed_primitives).toEqual([ "resolve_entity_reference", "query_documents", "probe_coverage", "explain_evidence_basis" ]); expect(result.required_axes).toEqual(["counterparty", "document_date", "coverage_target", "evidence_basis"]); }); it("uses metadata-only planning when the user asks about available schema surface", () => { const result = planAssistantMcpDiscovery({ turnMeaning: { asked_domain_family: "metadata", asked_action_family: "inspect_catalog" } }); expect(result.planner_status).toBe("ready_for_execution"); expect(result.proposed_primitives).toEqual(["inspect_1c_metadata"]); expect(result.required_axes).toEqual(["metadata_scope"]); expect(result.catalog_review.evidence_floors.inspect_1c_metadata).toBe("source_summary"); }); it("keeps broad metadata surface inspection on inspect_1c_metadata", () => { const result = planAssistantMcpDiscovery({ turnMeaning: { asked_domain_family: "metadata", asked_action_family: "inspect_surface", explicit_entity_candidates: ["НДС"] } }); expect(result.planner_status).toBe("ready_for_execution"); expect(result.semantic_data_need).toBe("1C metadata evidence"); expect(result.proposed_primitives).toEqual(["inspect_1c_metadata"]); expect(result.proposed_primitives).not.toContain("query_documents"); expect(result.proposed_primitives).not.toContain("query_movements"); expect(result.reason_codes).toContain("planner_selected_metadata_recipe"); }); it("keeps metadata document inspection on inspect_1c_metadata instead of query_documents", () => { const result = planAssistantMcpDiscovery({ turnMeaning: { asked_domain_family: "metadata", asked_action_family: "inspect_documents" } }); expect(result.planner_status).toBe("ready_for_execution"); expect(result.proposed_primitives).toEqual(["inspect_1c_metadata"]); expect(result.proposed_primitives).not.toContain("query_documents"); }); it("keeps metadata lane-choice clarification in needs_clarification without launching MCP primitives", () => { const result = planAssistantMcpDiscovery({ turnMeaning: { asked_domain_family: "metadata", asked_action_family: "resolve_next_lane", explicit_entity_candidates: ["SVK"], explicit_date_scope: "2020", unsupported_but_understood_family: "metadata_lane_choice_clarification" } }); expect(result.planner_status).toBe("needs_clarification"); expect(result.semantic_data_need).toBe("metadata lane clarification"); expect(result.selected_chain_id).toBe("metadata_lane_clarification"); expect(result.selected_chain_summary).toContain("choose the next data lane"); expect(result.proposed_primitives).toEqual([]); expect(result.required_axes).toEqual(["counterparty", "period", "lane_family_choice"]); expect(result.discovery_plan.plan_status).toBe("needs_clarification"); expect(result.reason_codes).toContain("planner_selected_metadata_lane_clarification_recipe"); expect(result.reason_codes).toContain("planner_needs_more_user_or_scope_context"); }); it("does not mark an unclassified turn as executable without turn meaning context", () => { const result = planAssistantMcpDiscovery({}); expect(result.planner_status).toBe("needs_clarification"); expect(result.discovery_plan.plan_status).toBe("needs_clarification"); expect(result.reason_codes).toContain("planner_needs_more_user_or_scope_context"); }); it("exposes an explicit entity-resolution chain instead of silently collapsing into a lifecycle lane", () => { const result = planAssistantMcpDiscovery({ turnMeaning: { explicit_entity_candidates: ["SVK"] } }); expect(result.planner_status).toBe("ready_for_execution"); expect(result.semantic_data_need).toBe("entity discovery evidence"); expect(result.selected_chain_id).toBe("entity_resolution"); expect(result.selected_chain_summary).toContain("resolve the most relevant 1C reference"); expect(result.proposed_primitives).toEqual(["search_business_entity", "resolve_entity_reference", "probe_coverage"]); }); it("keeps organization-scoped one-sided value-flow totals executable without forcing a counterparty", () => { const result = planAssistantMcpDiscovery({ 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: "ООО Альтернатива Плюс" } }); expect(result.planner_status).toBe("ready_for_execution"); expect(result.semantic_data_need).toBe("organization-scoped value-flow evidence"); expect(result.selected_chain_id).toBe("value_flow"); expect(result.proposed_primitives).toEqual(["query_movements", "aggregate_by_axis", "probe_coverage"]); expect(result.required_axes).toEqual(["organization", "period", "aggregate_axis", "amount", "coverage_target"]); expect(result.catalog_review.review_status).toBe("catalog_compatible"); expect(result.reason_codes).toContain("planner_selected_open_scope_value_flow_total_from_data_need_graph"); expect(result.reason_codes).toContain("planner_instantiated_catalog_chain_template_value_flow"); }); it("keeps generic one-sided open totals in organization clarification instead of forcing entity resolution", () => { const result = planAssistantMcpDiscovery({ 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" } }); expect(result.planner_status).toBe("needs_clarification"); expect(result.selected_chain_id).toBe("value_flow"); expect(result.proposed_primitives).toEqual(["query_movements", "aggregate_by_axis", "probe_coverage"]); expect(result.required_axes).toEqual(["period", "organization", "aggregate_axis", "amount", "coverage_target"]); expect(result.catalog_review.review_status).toBe("needs_more_axes"); expect(result.catalog_review.missing_axes_by_primitive.query_movements).toContainEqual(["organization"]); expect(result.reason_codes).toContain("planner_selected_open_scope_value_flow_total_from_data_need_graph"); expect(result.reason_codes).toContain("planner_instantiated_catalog_chain_template_value_flow"); expect(result.selected_chain_id).not.toBe("entity_resolution"); }); it("treats all-time organization-scoped open totals as execution-ready without reintroducing a fake period gap", () => { const result = planAssistantMcpDiscovery({ 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: "all_time_scope", 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", "data_need_graph_all_time_scope_hint" ] }, turnMeaning: { asked_domain_family: "counterparty_value", asked_action_family: "turnover", explicit_organization_scope: "ООО Альтернатива Плюс" } }); expect(result.planner_status).toBe("ready_for_execution"); expect(result.semantic_data_need).toBe("organization-scoped value-flow evidence"); expect(result.selected_chain_id).toBe("value_flow"); expect(result.proposed_primitives).toEqual(["query_movements", "aggregate_by_axis", "probe_coverage"]); expect(result.required_axes).toEqual([ "organization", "all_time_scope", "aggregate_axis", "amount", "coverage_target" ]); expect(result.catalog_review.review_status).toBe("catalog_compatible"); expect(result.reason_codes).toContain("planner_selected_open_scope_value_flow_total_from_data_need_graph"); expect(result.reason_codes).toContain("planner_instantiated_catalog_chain_template_value_flow"); }); it("keeps metadata-scoped movement evidence in clarification instead of forcing entity resolution", () => { const result = planAssistantMcpDiscovery({ dataNeedGraph: { schema_version: "assistant_data_need_graph_v1", policy_owner: "assistantMcpDiscoveryDataNeedGraph", subject_candidates: [], metadata_scope_hint: "НДС", 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: "НДС", subject_resolution_optional: true, unsupported_but_understood_family: "movement_evidence" } }); expect(result.planner_status).toBe("needs_clarification"); expect(result.selected_chain_id).toBe("movement_evidence"); expect(result.proposed_primitives).toEqual(["query_movements", "probe_coverage"]); expect(result.required_axes).toEqual(["metadata_scope", "organization", "coverage_target"]); expect(result.reason_codes).toContain("planner_selected_metadata_scoped_movement_from_data_need_graph"); expect(result.selected_chain_id).not.toBe("entity_resolution"); }); it("keeps metadata-scoped movement evidence execution-ready once organization and period are known", () => { const result = planAssistantMcpDiscovery({ dataNeedGraph: { schema_version: "assistant_data_need_graph_v1", policy_owner: "assistantMcpDiscoveryDataNeedGraph", subject_candidates: [], metadata_scope_hint: "НДС", subject_resolution_optional: true, business_fact_family: "movement_evidence", action_family: "list_movements", 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", "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: "НДС", subject_resolution_optional: true, explicit_organization_scope: "РћРћРћ Альтернатива Плюс", explicit_date_scope: "2020", unsupported_but_understood_family: "movement_evidence" } }); expect(result.planner_status).toBe("ready_for_execution"); expect(result.selected_chain_id).toBe("movement_evidence"); expect(result.proposed_primitives).toEqual(["query_movements", "probe_coverage"]); expect(result.required_axes).toEqual(["organization", "period", "metadata_scope", "coverage_target"]); expect(result.catalog_review.review_status).toBe("catalog_compatible"); expect(result.reason_codes).toContain("planner_selected_metadata_scoped_movement_from_data_need_graph"); }); it("keeps metadata-scoped document evidence execution-ready with all-time scope once organization is known", () => { const result = planAssistantMcpDiscovery({ dataNeedGraph: { schema_version: "assistant_data_need_graph_v1", policy_owner: "assistantMcpDiscoveryDataNeedGraph", subject_candidates: [], metadata_scope_hint: "НДС", subject_resolution_optional: true, business_fact_family: "document_evidence", action_family: "list_documents", aggregation_need: null, time_scope_need: "all_time_scope", comparison_need: null, ranking_need: null, proof_expectation: "coverage_checked_fact", clarification_gaps: [], decomposition_candidates: ["fetch_scoped_documents", "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", "data_need_graph_all_time_scope_hint" ] }, turnMeaning: { asked_domain_family: "documents", asked_action_family: "list_documents", metadata_scope_hint: "НДС", subject_resolution_optional: true, explicit_organization_scope: "ООО Альтернатива Плюс", unsupported_but_understood_family: "document_evidence" } }); expect(result.planner_status).toBe("ready_for_execution"); expect(result.selected_chain_id).toBe("document_evidence"); expect(result.proposed_primitives).toEqual(["query_documents", "probe_coverage"]); expect(result.required_axes).toEqual(["organization", "metadata_scope", "all_time_scope", "coverage_target"]); expect(result.catalog_review.review_status).toBe("catalog_compatible"); expect(result.reason_codes).toContain("planner_selected_metadata_scoped_document_from_data_need_graph"); }); });