2526 lines
123 KiB
TypeScript
2526 lines
123 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
||
import { buildAssistantMcpDiscoveryTurnInput } from "../src/services/assistantMcpDiscoveryTurnInputAdapter";
|
||
|
||
describe("assistant MCP discovery turn input adapter", () => {
|
||
it("maps unsupported assistant turn meaning into a discovery-ready value-flow input", () => {
|
||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||
assistantTurnMeaning: {
|
||
schema_version: "assistant_turn_meaning_v1",
|
||
asked_domain_family: "counterparty",
|
||
asked_action_family: "counterparty_value_or_turnover",
|
||
unsupported_but_understood_family: "counterparty_value_or_turnover",
|
||
stale_replay_forbidden: true,
|
||
explicit_entity_candidates: [{ type: "counterparty", value: "SVK", source: "current_turn_loose_entity_tail" }]
|
||
},
|
||
predecomposeContract: {
|
||
entities: { counterparty: "Группа СВК", organization: "Альтернатива" },
|
||
period: { period_from: "2020-01-01", period_to: "2020-12-31" }
|
||
}
|
||
});
|
||
|
||
expect(result.adapter_status).toBe("ready");
|
||
expect(result.should_run_discovery).toBe(true);
|
||
expect(result.semantic_data_need).toBe("counterparty value-flow evidence");
|
||
expect(result.data_need_graph?.business_fact_family).toBe("value_flow");
|
||
expect(result.data_need_graph?.time_scope_need).toBe("explicit_period");
|
||
expect(result.turn_meaning_ref?.explicit_entity_candidates).toEqual(["SVK", "Группа СВК"]);
|
||
expect(result.turn_meaning_ref?.explicit_organization_scope).toBe("Альтернатива");
|
||
expect(result.turn_meaning_ref?.explicit_date_scope).toBe("2020");
|
||
expect(result.turn_meaning_ref?.stale_replay_forbidden).toBe(true);
|
||
});
|
||
|
||
it("bootstraps lifecycle discovery from raw user wording and predecompose scope", () => {
|
||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||
userMessage: "Сколько лет мы работаем с Группа СВК?",
|
||
predecomposeContract: {
|
||
entities: { counterparty: "Группа СВК" },
|
||
period: { period_from: null, period_to: null, as_of_date: null }
|
||
}
|
||
});
|
||
|
||
expect(result.adapter_status).toBe("ready");
|
||
expect(result.source_signal).toBe("predecompose_contract");
|
||
expect(result.semantic_data_need).toBe("counterparty lifecycle evidence");
|
||
expect(result.turn_meaning_ref).toMatchObject({
|
||
asked_domain_family: "counterparty_lifecycle",
|
||
asked_action_family: "activity_duration",
|
||
explicit_entity_candidates: ["Группа СВК"],
|
||
unsupported_but_understood_family: "counterparty_lifecycle",
|
||
stale_replay_forbidden: true
|
||
});
|
||
expect(result.reason_codes).toContain("mcp_discovery_lifecycle_signal_detected");
|
||
});
|
||
|
||
it("bootstraps value-flow discovery from raw turnover wording when no exact route owns it", () => {
|
||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||
userMessage: "какой денежный поток был у Группа СВК за 2020 год?",
|
||
predecomposeContract: {
|
||
entities: { counterparty: "Группа СВК" },
|
||
period: { period_from: "2020-01-01", period_to: "2020-12-31" }
|
||
}
|
||
});
|
||
|
||
expect(result.adapter_status).toBe("ready");
|
||
expect(result.should_run_discovery).toBe(true);
|
||
expect(result.semantic_data_need).toBe("counterparty value-flow evidence");
|
||
expect(result.turn_meaning_ref).toMatchObject({
|
||
asked_domain_family: "counterparty_value",
|
||
asked_action_family: "turnover",
|
||
explicit_entity_candidates: ["Группа СВК"],
|
||
explicit_date_scope: "2020",
|
||
unsupported_but_understood_family: "counterparty_value_or_turnover",
|
||
stale_replay_forbidden: true
|
||
});
|
||
expect(result.reason_codes).toContain("mcp_discovery_value_flow_signal_detected");
|
||
});
|
||
|
||
it("treats value-flow organization-shaped target as entity candidate when counterparty is absent", () => {
|
||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||
userMessage: "какой денежный поток был у Группа СВК за 2020 год?",
|
||
predecomposeContract: {
|
||
entities: { organization: "Группа СВК" },
|
||
period: { period_from: "2020-01-01", period_to: "2020-12-31" }
|
||
}
|
||
});
|
||
|
||
expect(result.adapter_status).toBe("ready");
|
||
expect(result.turn_meaning_ref?.explicit_entity_candidates).toEqual(["Группа СВК"]);
|
||
expect(result.turn_meaning_ref?.explicit_organization_scope).toBeUndefined();
|
||
});
|
||
|
||
it("keeps payout wording as outgoing supplier-payout discovery", () => {
|
||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||
userMessage: "сколько мы заплатили Группа СВК за 2020 год?",
|
||
predecomposeContract: {
|
||
entities: { counterparty: "Группа СВК" },
|
||
period: { period_from: "2020-01-01", period_to: "2020-12-31" }
|
||
}
|
||
});
|
||
|
||
expect(result.adapter_status).toBe("ready");
|
||
expect(result.semantic_data_need).toBe("counterparty value-flow evidence");
|
||
expect(result.turn_meaning_ref).toMatchObject({
|
||
asked_domain_family: "counterparty_value",
|
||
asked_action_family: "payout",
|
||
explicit_entity_candidates: ["Группа СВК"],
|
||
explicit_date_scope: "2020",
|
||
unsupported_but_understood_family: "counterparty_payouts_or_outflow",
|
||
stale_replay_forbidden: true
|
||
});
|
||
expect(result.reason_codes).toContain("mcp_discovery_payout_signal_detected");
|
||
});
|
||
|
||
it("keeps net cash wording as bidirectional value-flow discovery", () => {
|
||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||
userMessage: "какое нетто по деньгам с Группа СВК за 2020: сколько получили и сколько заплатили?",
|
||
predecomposeContract: {
|
||
entities: { counterparty: "Группа СВК" },
|
||
period: { period_from: "2020-01-01", period_to: "2020-12-31" }
|
||
}
|
||
});
|
||
|
||
expect(result.adapter_status).toBe("ready");
|
||
expect(result.semantic_data_need).toBe("counterparty value-flow evidence");
|
||
expect(result.turn_meaning_ref).toMatchObject({
|
||
asked_domain_family: "counterparty_value",
|
||
asked_action_family: "net_value_flow",
|
||
explicit_entity_candidates: ["Группа СВК"],
|
||
explicit_date_scope: "2020",
|
||
unsupported_but_understood_family: "counterparty_bidirectional_value_flow_or_netting",
|
||
stale_replay_forbidden: true
|
||
});
|
||
expect(result.reason_codes).toContain("mcp_discovery_bidirectional_value_flow_signal_detected");
|
||
expect(result.reason_codes).not.toContain("mcp_discovery_payout_signal_detected");
|
||
});
|
||
|
||
it("overrides a supported exact current-turn payout route when the question asks for a payment amount", () => {
|
||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||
userMessage:
|
||
"\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043c\u044b \u0437\u0430\u043f\u043b\u0430\u0442\u0438\u043b\u0438 \u0413\u0440\u0443\u043f\u043f\u0430 \u0421\u0412\u041a \u0437\u0430 2020 \u0433\u043e\u0434?",
|
||
effectiveMessage:
|
||
"\u0421\u043a\u043e\u043b\u044c\u043a\u043e \u0431\u044b\u043b\u043e \u043e\u043f\u043b\u0430\u0447\u0435\u043d\u043e \u043a\u043e\u043d\u0442\u0440\u0430\u0433\u0435\u043d\u0442\u0443 '\u0413\u0440\u0443\u043f\u043f\u0430 \u0421\u0412\u041a' \u0432 2020 \u0433\u043e\u0434\u0443?",
|
||
assistantTurnMeaning: {
|
||
asked_domain_family: "counterparty",
|
||
asked_action_family: "counterparty_population_and_roles",
|
||
explicit_intent_candidate: "counterparty_population_and_roles",
|
||
explicit_entity_candidates: [{ value: "\u0413\u0440\u0443\u043f\u043f\u0430 \u0421\u0412\u041a" }]
|
||
},
|
||
predecomposeContract: {
|
||
entities: { counterparty: "\u0413\u0440\u0443\u043f\u043f\u0430 \u0421\u0412\u041a" },
|
||
period: { period_from: "2020-01-01", period_to: "2020-12-31" }
|
||
}
|
||
});
|
||
|
||
expect(result.adapter_status).toBe("ready");
|
||
expect(result.should_run_discovery).toBe(true);
|
||
expect(result.turn_meaning_ref).toMatchObject({
|
||
asked_domain_family: "counterparty_value",
|
||
asked_action_family: "payout",
|
||
explicit_entity_candidates: ["\u0413\u0440\u0443\u043f\u043f\u0430 \u0421\u0412\u041a"],
|
||
explicit_date_scope: "2020",
|
||
unsupported_but_understood_family: "counterparty_payouts_or_outflow",
|
||
stale_replay_forbidden: true
|
||
});
|
||
expect(result.reason_codes).toContain("mcp_discovery_current_turn_value_flow_overrides_supported_exact");
|
||
expect(result.reason_codes).not.toContain("mcp_discovery_not_applicable_for_supported_exact_turn");
|
||
});
|
||
|
||
it("overrides a supported exact current-turn bank list when the question asks for net cash flow", () => {
|
||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||
userMessage:
|
||
"\u043a\u0430\u043a\u043e\u0435 \u043d\u0435\u0442\u0442\u043e \u043f\u043e \u0434\u0435\u043d\u044c\u0433\u0430\u043c \u0441 \u0413\u0440\u0443\u043f\u043f\u0430 \u0421\u0412\u041a \u0437\u0430 2020 \u0433\u043e\u0434: \u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043f\u043e\u043b\u0443\u0447\u0438\u043b\u0438 \u0438 \u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0437\u0430\u043f\u043b\u0430\u0442\u0438\u043b\u0438?",
|
||
assistantTurnMeaning: {
|
||
asked_domain_family: "counterparty",
|
||
asked_action_family: "bank_operations_by_counterparty",
|
||
explicit_intent_candidate: "bank_operations_by_counterparty",
|
||
explicit_entity_candidates: [{ value: "\u0413\u0440\u0443\u043f\u043f\u0430 \u0421\u0412\u041a" }]
|
||
},
|
||
predecomposeContract: {
|
||
entities: { counterparty: "\u0413\u0440\u0443\u043f\u043f\u0430 \u0421\u0412\u041a" },
|
||
period: { period_from: "2020-01-01", period_to: "2020-12-31" }
|
||
}
|
||
});
|
||
|
||
expect(result.adapter_status).toBe("ready");
|
||
expect(result.should_run_discovery).toBe(true);
|
||
expect(result.turn_meaning_ref).toMatchObject({
|
||
asked_domain_family: "counterparty_value",
|
||
asked_action_family: "net_value_flow",
|
||
explicit_entity_candidates: ["\u0413\u0440\u0443\u043f\u043f\u0430 \u0421\u0412\u041a"],
|
||
explicit_date_scope: "2020",
|
||
unsupported_but_understood_family: "counterparty_bidirectional_value_flow_or_netting",
|
||
stale_replay_forbidden: true
|
||
});
|
||
expect(result.reason_codes).toContain("mcp_discovery_current_turn_value_flow_overrides_supported_exact");
|
||
expect(result.reason_codes).not.toContain("mcp_discovery_not_applicable_for_supported_exact_turn");
|
||
});
|
||
|
||
it("prefers the explicit current-turn counterparty over stale organization-scoped carryover in net follow-up discovery", () => {
|
||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||
userMessage: "какое нетто по деньгам с Группа СВК за 2020 год: сколько получили и сколько заплатили?",
|
||
assistantTurnMeaning: {
|
||
asked_domain_family: "counterparty_value",
|
||
asked_action_family: "net_value_flow",
|
||
explicit_entity_candidates: ["Альтернатива Плюс"],
|
||
explicit_organization_scope: "ООО Альтернатива Плюс",
|
||
explicit_date_scope: "2020",
|
||
unsupported_but_understood_family: "counterparty_bidirectional_value_flow_or_netting",
|
||
stale_replay_forbidden: true
|
||
},
|
||
predecomposeContract: {
|
||
entities: { counterparty: "Группа СВК" },
|
||
period: { period_from: "2020-01-01", period_to: "2020-12-31" }
|
||
},
|
||
followupContext: {
|
||
previous_intent: "counterparty_activity_lifecycle",
|
||
previous_filters: {
|
||
organization: "ООО Альтернатива Плюс",
|
||
counterparty: "Альтернатива Плюс",
|
||
period_from: "2020-01-01",
|
||
period_to: "2020-12-31"
|
||
},
|
||
previous_anchor_type: "organization",
|
||
previous_anchor_value: "ООО Альтернатива Плюс"
|
||
}
|
||
});
|
||
|
||
expect(result.adapter_status).toBe("ready");
|
||
expect(result.should_run_discovery).toBe(true);
|
||
expect(result.turn_meaning_ref).toMatchObject({
|
||
asked_domain_family: "counterparty_value",
|
||
asked_action_family: "net_value_flow",
|
||
explicit_entity_candidates: ["Группа СВК"],
|
||
explicit_organization_scope: "ООО Альтернатива Плюс",
|
||
explicit_date_scope: "2020",
|
||
unsupported_but_understood_family: "counterparty_bidirectional_value_flow_or_netting",
|
||
stale_replay_forbidden: true
|
||
});
|
||
expect(result.turn_meaning_ref?.metadata_scope_hint).toBeUndefined();
|
||
expect(result.data_need_graph?.subject_candidates).toEqual(["Группа СВК"]);
|
||
});
|
||
|
||
it("captures monthly aggregation as part of bidirectional value-flow meaning", () => {
|
||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||
userMessage: "какое нетто по деньгам с Группа СВК за 2020 год по месяцам: сколько получили и сколько заплатили помесячно?",
|
||
predecomposeContract: {
|
||
entities: { counterparty: "Группа СВК" },
|
||
period: { period_from: "2020-01-01", period_to: "2020-12-31" }
|
||
}
|
||
});
|
||
|
||
expect(result.adapter_status).toBe("ready");
|
||
expect(result.turn_meaning_ref).toMatchObject({
|
||
asked_domain_family: "counterparty_value",
|
||
asked_action_family: "net_value_flow",
|
||
asked_aggregation_axis: "month",
|
||
explicit_entity_candidates: ["Группа СВК"],
|
||
explicit_date_scope: "2020"
|
||
});
|
||
expect(result.reason_codes).toContain("mcp_discovery_monthly_aggregation_signal_detected");
|
||
});
|
||
|
||
it("bootstraps metadata discovery from raw schema wording", () => {
|
||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||
userMessage: "какие регистры и поля есть в 1С по НДС?"
|
||
});
|
||
|
||
expect(result.adapter_status).toBe("ready");
|
||
expect(result.should_run_discovery).toBe(true);
|
||
expect(result.source_signal).toBe("raw_text");
|
||
expect(result.semantic_data_need).toBe("1C metadata evidence");
|
||
expect(result.data_need_graph?.business_fact_family).toBe("schema_surface");
|
||
expect(result.data_need_graph?.decomposition_candidates).toEqual(["inspect_metadata_surface"]);
|
||
expect(result.turn_meaning_ref).toMatchObject({
|
||
asked_domain_family: "metadata",
|
||
asked_action_family: "inspect_fields",
|
||
unsupported_but_understood_family: "1c_metadata_surface",
|
||
stale_replay_forbidden: true
|
||
});
|
||
expect(result.reason_codes).toContain("mcp_discovery_metadata_signal_detected");
|
||
});
|
||
|
||
it("treats broad 1C object wording as metadata surface discovery instead of narrowing to catalog-only", () => {
|
||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||
userMessage: "какие объекты 1С есть по НДС?"
|
||
});
|
||
|
||
expect(result.adapter_status).toBe("ready");
|
||
expect(result.should_run_discovery).toBe(true);
|
||
expect(result.source_signal).toBe("raw_text");
|
||
expect(result.semantic_data_need).toBe("1C metadata evidence");
|
||
expect(result.turn_meaning_ref).toMatchObject({
|
||
asked_domain_family: "metadata",
|
||
asked_action_family: "inspect_surface",
|
||
explicit_entity_candidates: ["НДС"],
|
||
unsupported_but_understood_family: "1c_metadata_surface",
|
||
stale_replay_forbidden: true
|
||
});
|
||
expect(result.reason_codes).toContain("mcp_discovery_metadata_signal_detected");
|
||
expect(result.reason_codes).toContain("mcp_discovery_metadata_scope_hint_from_raw_text");
|
||
});
|
||
|
||
it("bootstraps entity-resolution discovery from raw counterparty search wording", () => {
|
||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||
userMessage: "найди в 1С контрагента Группа СВК"
|
||
});
|
||
|
||
expect(result.adapter_status).toBe("ready");
|
||
expect(result.should_run_discovery).toBe(true);
|
||
expect(result.source_signal).toBe("raw_text");
|
||
expect(result.semantic_data_need).toBe("entity discovery evidence");
|
||
expect(result.data_need_graph?.business_fact_family).toBe("entity_grounding");
|
||
expect(result.data_need_graph?.subject_candidates).toEqual(["Группа СВК"]);
|
||
expect(result.turn_meaning_ref).toMatchObject({
|
||
asked_domain_family: "entity_resolution",
|
||
asked_action_family: "search_business_entity",
|
||
explicit_entity_candidates: ["Группа СВК"],
|
||
unsupported_but_understood_family: "entity_resolution",
|
||
stale_replay_forbidden: true
|
||
});
|
||
expect(result.reason_codes).toContain("mcp_discovery_entity_resolution_signal_detected");
|
||
expect(result.reason_codes).toContain("mcp_discovery_entity_scope_from_raw_entity_search");
|
||
});
|
||
|
||
it("seeds document evidence follow-up from prior entity-resolution grounding", () => {
|
||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||
userMessage: "по нему документы за 2020 год",
|
||
followupContext: {
|
||
previous_discovery_pilot_scope: "entity_resolution_search_v1",
|
||
previous_anchor_type: "counterparty",
|
||
previous_anchor_value: "Группа СВК",
|
||
previous_discovery_entity_candidates: ["Группа СВК", "СВК"]
|
||
}
|
||
});
|
||
|
||
expect(result.adapter_status).toBe("ready");
|
||
expect(result.should_run_discovery).toBe(true);
|
||
expect(result.source_signal).toBe("followup_context");
|
||
expect(result.semantic_data_need).toBe("document evidence");
|
||
expect(result.turn_meaning_ref).toMatchObject({
|
||
asked_domain_family: "documents",
|
||
asked_action_family: "list_documents",
|
||
explicit_entity_candidates: ["Группа СВК"],
|
||
explicit_date_scope: "2020",
|
||
unsupported_but_understood_family: "document_evidence",
|
||
stale_replay_forbidden: true
|
||
});
|
||
expect(result.reason_codes).toContain("mcp_discovery_entity_resolution_grounded_document_followup");
|
||
expect(result.reason_codes).toContain("mcp_discovery_counterparty_from_followup_context");
|
||
});
|
||
|
||
it("seeds movement evidence follow-up from prior entity-resolution grounding", () => {
|
||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||
userMessage: "а теперь по нему движения за 2020 год",
|
||
followupContext: {
|
||
previous_discovery_pilot_scope: "entity_resolution_search_v1",
|
||
previous_discovery_entity_candidates: ["Группа СВК", "СВК"]
|
||
}
|
||
});
|
||
|
||
expect(result.adapter_status).toBe("ready");
|
||
expect(result.should_run_discovery).toBe(true);
|
||
expect(result.source_signal).toBe("followup_context");
|
||
expect(result.semantic_data_need).toBe("movement evidence");
|
||
expect(result.turn_meaning_ref).toMatchObject({
|
||
asked_domain_family: "movements",
|
||
asked_action_family: "list_movements",
|
||
explicit_entity_candidates: ["Группа СВК"],
|
||
explicit_date_scope: "2020",
|
||
unsupported_but_understood_family: "movement_evidence",
|
||
stale_replay_forbidden: true
|
||
});
|
||
expect(result.reason_codes).toContain("mcp_discovery_entity_resolution_grounded_movement_followup");
|
||
expect(result.reason_codes).toContain("mcp_discovery_counterparty_from_followup_context");
|
||
});
|
||
|
||
it("restarts entity-resolution when the user clarifies one ambiguous candidate", () => {
|
||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||
userMessage: "СВК-А",
|
||
followupContext: {
|
||
previous_discovery_pilot_scope: "entity_resolution_search_v1",
|
||
previous_discovery_entity_resolution_status: "ambiguous",
|
||
previous_discovery_entity_candidates: ["СВК", "СВК-А", "СВК-Б"],
|
||
previous_discovery_entity_ambiguity_candidates: ["СВК-А", "СВК-Б"]
|
||
}
|
||
});
|
||
|
||
expect(result.adapter_status).toBe("ready");
|
||
expect(result.should_run_discovery).toBe(true);
|
||
expect(result.source_signal).toBe("followup_context");
|
||
expect(result.semantic_data_need).toBe("entity discovery evidence");
|
||
expect(result.turn_meaning_ref).toMatchObject({
|
||
asked_domain_family: "entity_resolution",
|
||
asked_action_family: "search_business_entity",
|
||
explicit_entity_candidates: ["СВК-А"],
|
||
unsupported_but_understood_family: "entity_resolution",
|
||
stale_replay_forbidden: true
|
||
});
|
||
expect(result.reason_codes).toContain("mcp_discovery_entity_resolution_signal_detected");
|
||
expect(result.reason_codes).toContain("mcp_discovery_entity_resolution_clarification_candidate_selected");
|
||
});
|
||
|
||
it("restarts entity-resolution when the user clarifies an ambiguous candidate by ordinal choice", () => {
|
||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||
userMessage: "второй вариант",
|
||
followupContext: {
|
||
previous_discovery_pilot_scope: "entity_resolution_search_v1",
|
||
previous_discovery_entity_resolution_status: "ambiguous",
|
||
previous_discovery_entity_candidates: ["СВК", "СВК-А", "СВК-Б"],
|
||
previous_discovery_entity_ambiguity_candidates: ["СВК-А", "СВК-Б"]
|
||
}
|
||
});
|
||
|
||
expect(result.adapter_status).toBe("ready");
|
||
expect(result.should_run_discovery).toBe(true);
|
||
expect(result.source_signal).toBe("followup_context");
|
||
expect(result.semantic_data_need).toBe("entity discovery evidence");
|
||
expect(result.turn_meaning_ref).toMatchObject({
|
||
asked_domain_family: "entity_resolution",
|
||
asked_action_family: "search_business_entity",
|
||
explicit_entity_candidates: ["СВК-Б"],
|
||
unsupported_but_understood_family: "entity_resolution",
|
||
stale_replay_forbidden: true
|
||
});
|
||
expect(result.reason_codes).toContain("mcp_discovery_entity_resolution_clarification_candidate_selected");
|
||
});
|
||
|
||
it("chains an ordinal ambiguity clarification directly into value-flow follow-up intent", () => {
|
||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||
userMessage: "второй вариант, сколько получили за 2020 год",
|
||
followupContext: {
|
||
previous_discovery_pilot_scope: "entity_resolution_search_v1",
|
||
previous_discovery_entity_resolution_status: "ambiguous",
|
||
previous_discovery_entity_candidates: ["СВК", "СВК-А", "СВК-Б"],
|
||
previous_discovery_entity_ambiguity_candidates: ["СВК-А", "СВК-Б"]
|
||
}
|
||
});
|
||
|
||
expect(result.adapter_status).toBe("ready");
|
||
expect(result.should_run_discovery).toBe(true);
|
||
expect(result.source_signal).toBe("followup_context");
|
||
expect(result.semantic_data_need).toBe("counterparty value-flow evidence");
|
||
expect(result.turn_meaning_ref).toMatchObject({
|
||
asked_domain_family: "counterparty_value",
|
||
asked_action_family: "turnover",
|
||
explicit_entity_candidates: ["СВК-Б"],
|
||
explicit_date_scope: "2020",
|
||
unsupported_but_understood_family: "counterparty_value_or_turnover",
|
||
stale_replay_forbidden: true
|
||
});
|
||
expect(result.reason_codes).toContain("mcp_discovery_entity_resolution_clarification_candidate_selected");
|
||
expect(result.reason_codes).toContain("mcp_discovery_value_flow_signal_detected");
|
||
});
|
||
|
||
it("chains an ordinal ambiguity clarification directly into document evidence follow-up intent", () => {
|
||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||
userMessage: "второй вариант, покажи документы за 2020 год",
|
||
followupContext: {
|
||
previous_discovery_pilot_scope: "entity_resolution_search_v1",
|
||
previous_discovery_entity_resolution_status: "ambiguous",
|
||
previous_discovery_entity_candidates: ["СВК", "СВК-А", "СВК-Б"],
|
||
previous_discovery_entity_ambiguity_candidates: ["СВК-А", "СВК-Б"]
|
||
}
|
||
});
|
||
|
||
expect(result.adapter_status).toBe("ready");
|
||
expect(result.should_run_discovery).toBe(true);
|
||
expect(result.source_signal).toBe("followup_context");
|
||
expect(result.semantic_data_need).toBe("document evidence");
|
||
expect(result.turn_meaning_ref).toMatchObject({
|
||
asked_domain_family: "documents",
|
||
asked_action_family: "list_documents",
|
||
explicit_entity_candidates: ["СВК-Б"],
|
||
explicit_date_scope: "2020",
|
||
unsupported_but_understood_family: "document_evidence",
|
||
stale_replay_forbidden: true
|
||
});
|
||
expect(result.reason_codes).toContain("mcp_discovery_entity_resolution_clarification_candidate_selected");
|
||
expect(result.reason_codes).toContain("mcp_discovery_entity_resolution_clarified_document_followup");
|
||
});
|
||
|
||
it("chains an ordinal ambiguity clarification directly into movement evidence follow-up intent", () => {
|
||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||
userMessage: "первый вариант, покажи движения за 2020 год",
|
||
followupContext: {
|
||
previous_discovery_pilot_scope: "entity_resolution_search_v1",
|
||
previous_discovery_entity_resolution_status: "ambiguous",
|
||
previous_discovery_entity_candidates: ["СВК", "СВК-А", "СВК-Б"],
|
||
previous_discovery_entity_ambiguity_candidates: ["СВК-А", "СВК-Б"]
|
||
}
|
||
});
|
||
|
||
expect(result.adapter_status).toBe("ready");
|
||
expect(result.should_run_discovery).toBe(true);
|
||
expect(result.source_signal).toBe("followup_context");
|
||
expect(result.semantic_data_need).toBe("movement evidence");
|
||
expect(result.turn_meaning_ref).toMatchObject({
|
||
asked_domain_family: "movements",
|
||
asked_action_family: "list_movements",
|
||
explicit_entity_candidates: ["СВК-А"],
|
||
explicit_date_scope: "2020",
|
||
unsupported_but_understood_family: "movement_evidence",
|
||
stale_replay_forbidden: true
|
||
});
|
||
expect(result.reason_codes).toContain("mcp_discovery_entity_resolution_clarification_candidate_selected");
|
||
expect(result.reason_codes).toContain("mcp_discovery_entity_resolution_clarified_movement_followup");
|
||
});
|
||
|
||
it("does not silently ground value-flow follow-ups from an ambiguous entity-resolution answer", () => {
|
||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||
userMessage: "сколько получили по нему за 2020 год",
|
||
followupContext: {
|
||
previous_discovery_pilot_scope: "entity_resolution_search_v1",
|
||
previous_discovery_entity_resolution_status: "ambiguous",
|
||
previous_discovery_entity_candidates: ["СВК", "СВК-А", "СВК-Б"],
|
||
previous_discovery_entity_ambiguity_candidates: ["СВК-А", "СВК-Б"]
|
||
}
|
||
});
|
||
|
||
expect(result.adapter_status).toBe("ready");
|
||
expect(result.should_run_discovery).toBe(true);
|
||
expect(result.semantic_data_need).toBe("counterparty value-flow evidence");
|
||
expect(result.turn_meaning_ref).toMatchObject({
|
||
asked_domain_family: "counterparty_value",
|
||
asked_action_family: "turnover",
|
||
explicit_date_scope: "2020",
|
||
unsupported_but_understood_family: "counterparty_value_or_turnover",
|
||
stale_replay_forbidden: true
|
||
});
|
||
expect(result.turn_meaning_ref?.explicit_entity_candidates).toBeUndefined();
|
||
expect(result.reason_codes).not.toContain("mcp_discovery_grounded_value_flow_followup");
|
||
});
|
||
|
||
it("overrides a wrong exact document intent when a grounded document follow-up asks to switch into movements", () => {
|
||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||
userMessage: "а теперь по нему движения за 2020 год",
|
||
assistantTurnMeaning: {
|
||
asked_domain_family: "inventory",
|
||
asked_action_family: "purchase_documents",
|
||
explicit_intent_candidate: "inventory_purchase_documents_for_item",
|
||
explicit_entity_candidates: [{ value: "нему" }]
|
||
},
|
||
predecomposeContract: {
|
||
entities: {
|
||
counterparty: "нему"
|
||
},
|
||
period: {
|
||
period_from: "2020-01-01",
|
||
period_to: "2020-12-31"
|
||
}
|
||
},
|
||
followupContext: {
|
||
previous_discovery_pilot_scope: "counterparty_document_evidence_query_documents_v1",
|
||
previous_filters: {
|
||
counterparty: "Группа СВК",
|
||
period_from: "2020-01-01",
|
||
period_to: "2020-12-31"
|
||
},
|
||
previous_discovery_entity_candidates: ["Группа СВК", "СВК"]
|
||
}
|
||
});
|
||
|
||
expect(result.adapter_status).toBe("ready");
|
||
expect(result.should_run_discovery).toBe(true);
|
||
expect(result.source_signal).toBe("assistant_turn_meaning");
|
||
expect(result.semantic_data_need).toBe("movement evidence");
|
||
expect(result.turn_meaning_ref).toMatchObject({
|
||
asked_domain_family: "movements",
|
||
asked_action_family: "list_movements",
|
||
explicit_entity_candidates: ["Группа СВК"],
|
||
explicit_date_scope: "2020",
|
||
unsupported_but_understood_family: "movement_evidence",
|
||
stale_replay_forbidden: true
|
||
});
|
||
expect(result.reason_codes).toContain("mcp_discovery_document_evidence_grounded_movement_followup");
|
||
expect(result.reason_codes).not.toContain("mcp_discovery_not_applicable_for_supported_exact_turn");
|
||
});
|
||
|
||
it("overrides a supported exact turnover intent when a grounded entity follow-up asks about incoming money", () => {
|
||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||
userMessage: "сколько получили по нему за 2020 год",
|
||
assistantTurnMeaning: {
|
||
asked_domain_family: "counterparty",
|
||
asked_action_family: "turnover",
|
||
explicit_intent_candidate: "customer_revenue_and_payments",
|
||
explicit_entity_candidates: [{ value: "нему" }]
|
||
},
|
||
predecomposeContract: {
|
||
entities: {
|
||
counterparty: "нему"
|
||
},
|
||
period: {
|
||
period_from: "2020-01-01",
|
||
period_to: "2020-12-31"
|
||
}
|
||
},
|
||
followupContext: {
|
||
previous_discovery_pilot_scope: "entity_resolution_search_v1",
|
||
previous_filters: {
|
||
counterparty: "Группа СВК"
|
||
},
|
||
previous_discovery_entity_candidates: ["Группа СВК", "СВК"]
|
||
}
|
||
});
|
||
|
||
expect(result.adapter_status).toBe("ready");
|
||
expect(result.should_run_discovery).toBe(true);
|
||
expect(result.semantic_data_need).toBe("counterparty value-flow evidence");
|
||
expect(result.turn_meaning_ref).toMatchObject({
|
||
asked_domain_family: "counterparty_value",
|
||
asked_action_family: "turnover",
|
||
explicit_entity_candidates: ["Группа СВК"],
|
||
explicit_date_scope: "2020",
|
||
unsupported_but_understood_family: "counterparty_value_or_turnover",
|
||
stale_replay_forbidden: true
|
||
});
|
||
expect(result.reason_codes).toContain("mcp_discovery_grounded_value_flow_followup");
|
||
expect(result.reason_codes).not.toContain("mcp_discovery_not_applicable_for_supported_exact_turn");
|
||
});
|
||
|
||
it("overrides a supported exact payout intent when a grounded value-flow follow-up switches from incoming to outgoing", () => {
|
||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||
userMessage: "а теперь сколько заплатили?",
|
||
assistantTurnMeaning: {
|
||
asked_domain_family: "counterparty",
|
||
asked_action_family: "turnover",
|
||
explicit_intent_candidate: "customer_revenue_and_payments"
|
||
},
|
||
followupContext: {
|
||
previous_discovery_pilot_scope: "counterparty_value_flow_query_movements_v1",
|
||
previous_filters: {
|
||
counterparty: "Группа СВК",
|
||
organization: "ООО Альтернатива Плюс",
|
||
period_from: "2020-01-01",
|
||
period_to: "2020-12-31"
|
||
},
|
||
previous_anchor_type: "counterparty",
|
||
previous_anchor_value: "Группа СВК"
|
||
}
|
||
});
|
||
|
||
expect(result.adapter_status).toBe("ready");
|
||
expect(result.should_run_discovery).toBe(true);
|
||
expect(result.semantic_data_need).toBe("counterparty value-flow evidence");
|
||
expect(result.turn_meaning_ref).toMatchObject({
|
||
asked_domain_family: "counterparty_value",
|
||
asked_action_family: "payout",
|
||
explicit_entity_candidates: ["Группа СВК"],
|
||
explicit_organization_scope: "ООО Альтернатива Плюс",
|
||
explicit_date_scope: "2020",
|
||
unsupported_but_understood_family: "counterparty_payouts_or_outflow",
|
||
stale_replay_forbidden: true
|
||
});
|
||
expect(result.reason_codes).toContain("mcp_discovery_grounded_value_flow_followup");
|
||
expect(result.reason_codes).toContain("mcp_discovery_payout_signal_detected");
|
||
expect(result.reason_codes).not.toContain("mcp_discovery_not_applicable_for_supported_exact_turn");
|
||
});
|
||
|
||
it("overrides a supported exact net intent when a grounded payout follow-up switches into net flow", () => {
|
||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||
userMessage: "а какое нетто?",
|
||
assistantTurnMeaning: {
|
||
asked_domain_family: "counterparty",
|
||
asked_action_family: "turnover",
|
||
explicit_intent_candidate: "customer_revenue_and_payments"
|
||
},
|
||
followupContext: {
|
||
previous_discovery_pilot_scope: "counterparty_supplier_payout_query_movements_v1",
|
||
previous_filters: {
|
||
counterparty: "Группа СВК",
|
||
period_from: "2021-01-01",
|
||
period_to: "2021-12-31"
|
||
},
|
||
previous_anchor_type: "counterparty",
|
||
previous_anchor_value: "Группа СВК"
|
||
}
|
||
});
|
||
|
||
expect(result.adapter_status).toBe("ready");
|
||
expect(result.should_run_discovery).toBe(true);
|
||
expect(result.semantic_data_need).toBe("counterparty value-flow evidence");
|
||
expect(result.turn_meaning_ref).toMatchObject({
|
||
asked_domain_family: "counterparty_value",
|
||
asked_action_family: "net_value_flow",
|
||
explicit_entity_candidates: ["Группа СВК"],
|
||
explicit_date_scope: "2021",
|
||
unsupported_but_understood_family: "counterparty_bidirectional_value_flow_or_netting",
|
||
stale_replay_forbidden: true
|
||
});
|
||
expect(result.reason_codes).toContain("mcp_discovery_grounded_value_flow_followup");
|
||
expect(result.reason_codes).toContain("mcp_discovery_bidirectional_value_flow_signal_detected");
|
||
expect(result.reason_codes).not.toContain("mcp_discovery_not_applicable_for_supported_exact_turn");
|
||
});
|
||
|
||
it("prefers grounded discovery metadata scope over stale conflicting counterparty in a short net follow-up", () => {
|
||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||
userMessage: "а какое нетто?",
|
||
assistantTurnMeaning: {
|
||
asked_domain_family: "counterparty",
|
||
asked_action_family: "turnover",
|
||
explicit_intent_candidate: "customer_revenue_and_payments"
|
||
},
|
||
followupContext: {
|
||
previous_discovery_pilot_scope: "counterparty_supplier_payout_query_movements_v1",
|
||
previous_filters: {
|
||
counterparty: "РќРћР РўРћРќ",
|
||
period_from: "2020-01-01",
|
||
period_to: "2020-12-31"
|
||
},
|
||
previous_anchor_type: "counterparty",
|
||
previous_anchor_value: "СервисКонсалт, ООО",
|
||
previous_discovery_loop_metadata_scope_hint: "Группа СВК"
|
||
}
|
||
});
|
||
|
||
expect(result.adapter_status).toBe("ready");
|
||
expect(result.should_run_discovery).toBe(true);
|
||
expect(result.turn_meaning_ref).toMatchObject({
|
||
asked_domain_family: "counterparty_value",
|
||
asked_action_family: "net_value_flow",
|
||
explicit_entity_candidates: ["Группа СВК"],
|
||
explicit_date_scope: "2020",
|
||
unsupported_but_understood_family: "counterparty_bidirectional_value_flow_or_netting",
|
||
stale_replay_forbidden: true
|
||
});
|
||
expect(result.turn_meaning_ref?.explicit_entity_candidates).not.toContain("РќРћР РўРћРќ");
|
||
expect(result.reason_codes).toContain("mcp_discovery_grounded_value_flow_followup");
|
||
});
|
||
|
||
it.skip("switches from a grounded exact value-flow answer into document evidence without restating the counterparty", () => {
|
||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||
userMessage: "а по документам?",
|
||
assistantTurnMeaning: {
|
||
asked_domain_family: "counterparty",
|
||
asked_action_family: "turnover",
|
||
explicit_intent_candidate: "customer_revenue_and_payments"
|
||
},
|
||
followupContext: {
|
||
previous_intent: "customer_revenue_and_payments",
|
||
previous_filters: {
|
||
counterparty: "Группа СВК",
|
||
period_from: "2021-01-01",
|
||
period_to: "2021-12-31"
|
||
},
|
||
previous_anchor_type: "counterparty",
|
||
previous_anchor_value: "Группа СВК"
|
||
}
|
||
});
|
||
|
||
expect(result.adapter_status).toBe("ready");
|
||
expect(result.should_run_discovery).toBe(true);
|
||
expect(result.source_signal).toBe("assistant_turn_meaning");
|
||
expect(result.semantic_data_need).toBe("document evidence");
|
||
expect(result.turn_meaning_ref).toMatchObject({
|
||
asked_domain_family: "documents",
|
||
asked_action_family: "list_documents",
|
||
explicit_entity_candidates: ["Группа СВК"],
|
||
explicit_date_scope: "2021",
|
||
unsupported_but_understood_family: "document_evidence",
|
||
stale_replay_forbidden: true
|
||
});
|
||
expect(result.reason_codes).toContain("mcp_discovery_value_flow_grounded_document_followup");
|
||
expect(result.reason_codes).not.toContain("mcp_discovery_not_applicable_for_supported_exact_turn");
|
||
});
|
||
|
||
it.skip("switches from a grounded exact value-flow answer into movement evidence without restating the counterparty", () => {
|
||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||
userMessage: "а по движениям?",
|
||
assistantTurnMeaning: {
|
||
asked_domain_family: "counterparty",
|
||
asked_action_family: "turnover",
|
||
explicit_intent_candidate: "customer_revenue_and_payments"
|
||
},
|
||
followupContext: {
|
||
previous_intent: "customer_revenue_and_payments",
|
||
previous_filters: {
|
||
counterparty: "Группа СВК",
|
||
organization: "ООО Альтернатива Плюс",
|
||
period_from: "2021-01-01",
|
||
period_to: "2021-12-31"
|
||
},
|
||
previous_anchor_type: "counterparty",
|
||
previous_anchor_value: "Группа СВК"
|
||
}
|
||
});
|
||
|
||
expect(result.adapter_status).toBe("ready");
|
||
expect(result.should_run_discovery).toBe(true);
|
||
expect(result.source_signal).toBe("assistant_turn_meaning");
|
||
expect(result.semantic_data_need).toBe("movement evidence");
|
||
expect(result.turn_meaning_ref).toMatchObject({
|
||
asked_domain_family: "movements",
|
||
asked_action_family: "list_movements",
|
||
explicit_entity_candidates: ["Группа СВК"],
|
||
explicit_organization_scope: "ООО Альтернатива Плюс",
|
||
explicit_date_scope: "2021",
|
||
unsupported_but_understood_family: "movement_evidence",
|
||
stale_replay_forbidden: true
|
||
});
|
||
expect(result.reason_codes).toContain("mcp_discovery_value_flow_grounded_movement_followup");
|
||
expect(result.reason_codes).not.toContain("mcp_discovery_not_applicable_for_supported_exact_turn");
|
||
});
|
||
|
||
it("switches from a grounded exact value-flow answer into document evidence with a clean UTF-8 follow-up", () => {
|
||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||
userMessage: "а по документам?",
|
||
assistantTurnMeaning: {
|
||
asked_domain_family: "counterparty",
|
||
asked_action_family: "turnover",
|
||
explicit_intent_candidate: "customer_revenue_and_payments"
|
||
},
|
||
followupContext: {
|
||
previous_intent: "customer_revenue_and_payments",
|
||
previous_filters: {
|
||
counterparty: "Группа СВК",
|
||
period_from: "2021-01-01",
|
||
period_to: "2021-12-31"
|
||
},
|
||
previous_anchor_type: "counterparty",
|
||
previous_anchor_value: "Группа СВК"
|
||
}
|
||
});
|
||
|
||
expect(result.adapter_status).toBe("ready");
|
||
expect(result.should_run_discovery).toBe(true);
|
||
expect(result.source_signal).toBe("assistant_turn_meaning");
|
||
expect(result.semantic_data_need).toBe("counterparty value-flow evidence");
|
||
expect(result.turn_meaning_ref).toMatchObject({
|
||
asked_domain_family: "documents",
|
||
asked_action_family: "list_documents",
|
||
explicit_entity_candidates: ["Группа СВК"],
|
||
explicit_date_scope: "2021",
|
||
unsupported_but_understood_family: "document_evidence",
|
||
stale_replay_forbidden: true
|
||
});
|
||
expect(result.reason_codes).toContain("mcp_discovery_value_flow_grounded_document_followup");
|
||
expect(result.reason_codes).not.toContain("mcp_discovery_not_applicable_for_supported_exact_turn");
|
||
});
|
||
|
||
it("switches from a grounded exact value-flow answer into movement evidence with a clean UTF-8 follow-up", () => {
|
||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||
userMessage: "а по движениям?",
|
||
assistantTurnMeaning: {
|
||
asked_domain_family: "counterparty",
|
||
asked_action_family: "turnover",
|
||
explicit_intent_candidate: "customer_revenue_and_payments"
|
||
},
|
||
followupContext: {
|
||
previous_intent: "customer_revenue_and_payments",
|
||
previous_filters: {
|
||
counterparty: "Группа СВК",
|
||
organization: "ООО Альтернатива Плюс",
|
||
period_from: "2021-01-01",
|
||
period_to: "2021-12-31"
|
||
},
|
||
previous_anchor_type: "counterparty",
|
||
previous_anchor_value: "Группа СВК"
|
||
}
|
||
});
|
||
|
||
expect(result.adapter_status).toBe("ready");
|
||
expect(result.should_run_discovery).toBe(true);
|
||
expect(result.source_signal).toBe("assistant_turn_meaning");
|
||
expect(result.semantic_data_need).toBe("counterparty value-flow evidence");
|
||
expect(result.turn_meaning_ref).toMatchObject({
|
||
asked_domain_family: "movements",
|
||
asked_action_family: "list_movements",
|
||
explicit_entity_candidates: ["Группа СВК"],
|
||
explicit_organization_scope: "ООО Альтернатива Плюс",
|
||
explicit_date_scope: "2021",
|
||
unsupported_but_understood_family: "movement_evidence",
|
||
stale_replay_forbidden: true
|
||
});
|
||
expect(result.reason_codes).toContain("mcp_discovery_value_flow_grounded_movement_followup");
|
||
expect(result.reason_codes).not.toContain("mcp_discovery_not_applicable_for_supported_exact_turn");
|
||
});
|
||
|
||
it("seeds short monthly follow-up from prior bidirectional discovery context", () => {
|
||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||
userMessage: "а по месяцам?",
|
||
followupContext: {
|
||
previous_discovery_pilot_scope: "counterparty_bidirectional_value_flow_query_movements_v1",
|
||
previous_filters: {
|
||
counterparty: "Группа СВК",
|
||
organization: "ООО Альтернатива Плюс",
|
||
period_from: "2020-01-01",
|
||
period_to: "2020-12-31"
|
||
},
|
||
previous_anchor_type: "counterparty",
|
||
previous_anchor_value: "Группа СВК"
|
||
}
|
||
});
|
||
|
||
expect(result.adapter_status).toBe("ready");
|
||
expect(result.should_run_discovery).toBe(true);
|
||
expect(result.source_signal).toBe("followup_context");
|
||
expect(result.turn_meaning_ref).toMatchObject({
|
||
asked_domain_family: "counterparty_value",
|
||
asked_action_family: "net_value_flow",
|
||
asked_aggregation_axis: "month",
|
||
explicit_entity_candidates: ["Группа СВК"],
|
||
explicit_organization_scope: "ООО Альтернатива Плюс",
|
||
explicit_date_scope: "2020",
|
||
unsupported_but_understood_family: "counterparty_bidirectional_value_flow_or_netting",
|
||
stale_replay_forbidden: true
|
||
});
|
||
expect(result.reason_codes).toContain("mcp_discovery_seeded_from_followup_context");
|
||
expect(result.reason_codes).toContain("mcp_discovery_counterparty_from_followup_context");
|
||
expect(result.reason_codes).toContain("mcp_discovery_date_scope_from_followup_context");
|
||
});
|
||
|
||
it("seeds short metadata follow-up from prior metadata discovery context", () => {
|
||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||
userMessage: "а по регистрам?",
|
||
followupContext: {
|
||
previous_discovery_pilot_scope: "metadata_inspection_v1",
|
||
previous_filters: {
|
||
counterparty: "НДС"
|
||
},
|
||
previous_anchor_type: "counterparty",
|
||
previous_anchor_value: "НДС"
|
||
}
|
||
});
|
||
|
||
expect(result.adapter_status).toBe("ready");
|
||
expect(result.should_run_discovery).toBe(true);
|
||
expect(result.source_signal).toBe("followup_context");
|
||
expect(result.semantic_data_need).toBe("1C metadata evidence");
|
||
expect(result.turn_meaning_ref).toMatchObject({
|
||
asked_domain_family: "metadata",
|
||
asked_action_family: "inspect_registers",
|
||
explicit_entity_candidates: ["НДС"],
|
||
unsupported_but_understood_family: "1c_metadata_surface",
|
||
stale_replay_forbidden: true
|
||
});
|
||
expect(result.reason_codes).toContain("mcp_discovery_metadata_seeded_from_followup_context");
|
||
expect(result.reason_codes).toContain("mcp_discovery_counterparty_from_followup_context");
|
||
});
|
||
|
||
it("pivots grounded metadata follow-up into document evidence when the next lane is explicit", () => {
|
||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||
userMessage: "then documents",
|
||
followupContext: {
|
||
previous_discovery_pilot_scope: "metadata_inspection_v1",
|
||
previous_discovery_metadata_route_family: "document_evidence",
|
||
previous_discovery_metadata_route_family_selection_basis: "selected_entity_set",
|
||
previous_discovery_metadata_selected_entity_set: "Документ",
|
||
previous_discovery_metadata_selected_surface_objects: ["Документ.СчетФактураВыданный"],
|
||
previous_discovery_metadata_recommended_next_primitive: "query_documents",
|
||
previous_filters: {
|
||
counterparty: "SVK",
|
||
period_from: "2020-01-01",
|
||
period_to: "2020-12-31"
|
||
},
|
||
previous_anchor_type: "counterparty",
|
||
previous_anchor_value: "SVK"
|
||
}
|
||
});
|
||
|
||
expect(result.adapter_status).toBe("ready");
|
||
expect(result.should_run_discovery).toBe(true);
|
||
expect(result.source_signal).toBe("followup_context");
|
||
expect(result.semantic_data_need).toBe("document evidence");
|
||
expect(result.turn_meaning_ref).toMatchObject({
|
||
asked_domain_family: "documents",
|
||
asked_action_family: "list_documents",
|
||
explicit_entity_candidates: ["SVK"],
|
||
explicit_date_scope: "2020",
|
||
unsupported_but_understood_family: "document_evidence",
|
||
stale_replay_forbidden: true
|
||
});
|
||
expect(result.metadata_surface_ref).toMatchObject({
|
||
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: []
|
||
});
|
||
expect(result.reason_codes).toContain("mcp_discovery_metadata_grounded_document_followup");
|
||
expect(result.reason_codes).toContain("mcp_discovery_metadata_surface_ref_from_followup_context");
|
||
expect(result.reason_codes).toContain("mcp_discovery_metadata_next_primitive_from_followup_context");
|
||
expect(result.reason_codes).toContain("mcp_discovery_counterparty_from_followup_context");
|
||
});
|
||
|
||
it("pivots grounded metadata follow-up into movement evidence when the next lane is explicit", () => {
|
||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||
userMessage: "then movements",
|
||
followupContext: {
|
||
previous_discovery_pilot_scope: "metadata_inspection_v1",
|
||
previous_discovery_metadata_route_family: "movement_evidence",
|
||
previous_discovery_metadata_selected_entity_set: "РегистрНакопления",
|
||
previous_filters: {
|
||
counterparty: "SVK",
|
||
period_from: "2020-01-01",
|
||
period_to: "2020-12-31"
|
||
},
|
||
previous_anchor_type: "counterparty",
|
||
previous_anchor_value: "SVK"
|
||
}
|
||
});
|
||
|
||
expect(result.adapter_status).toBe("ready");
|
||
expect(result.should_run_discovery).toBe(true);
|
||
expect(result.source_signal).toBe("followup_context");
|
||
expect(result.semantic_data_need).toBe("movement evidence");
|
||
expect(result.turn_meaning_ref).toMatchObject({
|
||
asked_domain_family: "movements",
|
||
asked_action_family: "list_movements",
|
||
explicit_entity_candidates: ["SVK"],
|
||
explicit_date_scope: "2020",
|
||
unsupported_but_understood_family: "movement_evidence",
|
||
stale_replay_forbidden: true
|
||
});
|
||
expect(result.reason_codes).toContain("mcp_discovery_metadata_grounded_movement_followup");
|
||
expect(result.reason_codes).toContain("mcp_discovery_counterparty_from_followup_context");
|
||
});
|
||
|
||
it("continues from grounded metadata into document evidence on a generic downstream follow-up", () => {
|
||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||
userMessage: "давай дальше",
|
||
followupContext: {
|
||
previous_discovery_pilot_scope: "metadata_inspection_v1",
|
||
previous_discovery_metadata_route_family: "document_evidence",
|
||
previous_discovery_metadata_selected_entity_set: "Документ",
|
||
previous_filters: {
|
||
counterparty: "SVK",
|
||
period_from: "2020-01-01",
|
||
period_to: "2020-12-31"
|
||
},
|
||
previous_anchor_type: "counterparty",
|
||
previous_anchor_value: "SVK"
|
||
}
|
||
});
|
||
|
||
expect(result.adapter_status).toBe("ready");
|
||
expect(result.should_run_discovery).toBe(true);
|
||
expect(result.source_signal).toBe("followup_context");
|
||
expect(result.semantic_data_need).toBe("document evidence");
|
||
expect(result.turn_meaning_ref).toMatchObject({
|
||
asked_domain_family: "documents",
|
||
asked_action_family: "list_documents",
|
||
explicit_entity_candidates: ["SVK"],
|
||
explicit_date_scope: "2020",
|
||
unsupported_but_understood_family: "document_evidence",
|
||
stale_replay_forbidden: true
|
||
});
|
||
expect(result.reason_codes).toContain("mcp_discovery_metadata_grounded_lane_continuation");
|
||
});
|
||
|
||
it("continues from grounded metadata into movement evidence on a generic downstream follow-up", () => {
|
||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||
userMessage: "continue with data",
|
||
followupContext: {
|
||
previous_discovery_pilot_scope: "metadata_inspection_v1",
|
||
previous_discovery_metadata_route_family: "movement_evidence",
|
||
previous_discovery_metadata_selected_entity_set: "РегистрНакопления",
|
||
previous_filters: {
|
||
counterparty: "SVK",
|
||
period_from: "2020-01-01",
|
||
period_to: "2020-12-31"
|
||
},
|
||
previous_anchor_type: "counterparty",
|
||
previous_anchor_value: "SVK"
|
||
}
|
||
});
|
||
|
||
expect(result.adapter_status).toBe("ready");
|
||
expect(result.should_run_discovery).toBe(true);
|
||
expect(result.source_signal).toBe("followup_context");
|
||
expect(result.semantic_data_need).toBe("movement evidence");
|
||
expect(result.turn_meaning_ref).toMatchObject({
|
||
asked_domain_family: "movements",
|
||
asked_action_family: "list_movements",
|
||
explicit_entity_candidates: ["SVK"],
|
||
explicit_date_scope: "2020",
|
||
unsupported_but_understood_family: "movement_evidence",
|
||
stale_replay_forbidden: true
|
||
});
|
||
expect(result.reason_codes).toContain("mcp_discovery_metadata_grounded_lane_continuation");
|
||
});
|
||
|
||
it("resolves ambiguous metadata surface into document lane when the follow-up explicitly asks for documents", () => {
|
||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||
userMessage: "по документам",
|
||
followupContext: {
|
||
previous_discovery_pilot_scope: "metadata_inspection_v1",
|
||
previous_discovery_metadata_ambiguity_detected: true,
|
||
previous_discovery_metadata_ambiguity_entity_sets: ["Документ", "РегистрНакопления"],
|
||
previous_filters: {
|
||
counterparty: "SVK",
|
||
period_from: "2020-01-01",
|
||
period_to: "2020-12-31"
|
||
},
|
||
previous_anchor_type: "counterparty",
|
||
previous_anchor_value: "SVK"
|
||
}
|
||
});
|
||
|
||
expect(result.adapter_status).toBe("ready");
|
||
expect(result.should_run_discovery).toBe(true);
|
||
expect(result.source_signal).toBe("followup_context");
|
||
expect(result.semantic_data_need).toBe("document evidence");
|
||
expect(result.turn_meaning_ref).toMatchObject({
|
||
asked_domain_family: "documents",
|
||
asked_action_family: "list_documents",
|
||
explicit_entity_candidates: ["SVK"],
|
||
explicit_date_scope: "2020",
|
||
unsupported_but_understood_family: "document_evidence",
|
||
stale_replay_forbidden: true
|
||
});
|
||
expect(result.reason_codes).toContain("mcp_discovery_metadata_ambiguity_resolved_to_document_lane");
|
||
});
|
||
|
||
it("resolves ambiguous metadata surface into movement lane when the follow-up explicitly asks for movements", () => {
|
||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||
userMessage: "по движениям",
|
||
followupContext: {
|
||
previous_discovery_pilot_scope: "metadata_inspection_v1",
|
||
previous_discovery_metadata_ambiguity_detected: true,
|
||
previous_discovery_metadata_ambiguity_entity_sets: ["Документ", "РегистрНакопления"],
|
||
previous_filters: {
|
||
counterparty: "SVK",
|
||
period_from: "2020-01-01",
|
||
period_to: "2020-12-31"
|
||
},
|
||
previous_anchor_type: "counterparty",
|
||
previous_anchor_value: "SVK"
|
||
}
|
||
});
|
||
|
||
expect(result.adapter_status).toBe("ready");
|
||
expect(result.should_run_discovery).toBe(true);
|
||
expect(result.source_signal).toBe("followup_context");
|
||
expect(result.semantic_data_need).toBe("movement evidence");
|
||
expect(result.turn_meaning_ref).toMatchObject({
|
||
asked_domain_family: "movements",
|
||
asked_action_family: "list_movements",
|
||
explicit_entity_candidates: ["SVK"],
|
||
explicit_date_scope: "2020",
|
||
unsupported_but_understood_family: "movement_evidence",
|
||
stale_replay_forbidden: true
|
||
});
|
||
expect(result.reason_codes).toContain("mcp_discovery_metadata_ambiguity_resolved_to_movement_lane");
|
||
});
|
||
|
||
it("resolves ambiguous metadata surface into document lane from semantic document hints", () => {
|
||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||
userMessage: "покажи счет-фактуры",
|
||
followupContext: {
|
||
previous_discovery_pilot_scope: "metadata_inspection_v1",
|
||
previous_discovery_metadata_ambiguity_detected: true,
|
||
previous_discovery_metadata_ambiguity_entity_sets: ["Документ", "РегистрНакопления"],
|
||
previous_filters: {
|
||
counterparty: "SVK",
|
||
period_from: "2020-01-01",
|
||
period_to: "2020-12-31"
|
||
},
|
||
previous_anchor_type: "counterparty",
|
||
previous_anchor_value: "SVK"
|
||
}
|
||
});
|
||
|
||
expect(result.adapter_status).toBe("ready");
|
||
expect(result.should_run_discovery).toBe(true);
|
||
expect(result.semantic_data_need).toBe("document evidence");
|
||
expect(result.turn_meaning_ref).toMatchObject({
|
||
asked_domain_family: "documents",
|
||
asked_action_family: "list_documents",
|
||
explicit_entity_candidates: ["SVK"],
|
||
explicit_date_scope: "2020",
|
||
unsupported_but_understood_family: "document_evidence",
|
||
stale_replay_forbidden: true
|
||
});
|
||
expect(result.reason_codes).toContain("mcp_discovery_metadata_ambiguity_resolved_to_document_lane");
|
||
});
|
||
|
||
it("resolves ambiguous metadata surface into movement lane from semantic movement hints without turning it into value-flow", () => {
|
||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||
userMessage: "покажи платежи",
|
||
followupContext: {
|
||
previous_discovery_pilot_scope: "metadata_inspection_v1",
|
||
previous_discovery_metadata_ambiguity_detected: true,
|
||
previous_discovery_metadata_ambiguity_entity_sets: ["Документ", "РегистрНакопления"],
|
||
previous_filters: {
|
||
counterparty: "SVK",
|
||
period_from: "2020-01-01",
|
||
period_to: "2020-12-31"
|
||
},
|
||
previous_anchor_type: "counterparty",
|
||
previous_anchor_value: "SVK"
|
||
}
|
||
});
|
||
|
||
expect(result.adapter_status).toBe("ready");
|
||
expect(result.should_run_discovery).toBe(true);
|
||
expect(result.semantic_data_need).toBe("movement evidence");
|
||
expect(result.turn_meaning_ref).toMatchObject({
|
||
asked_domain_family: "movements",
|
||
asked_action_family: "list_movements",
|
||
explicit_entity_candidates: ["SVK"],
|
||
explicit_date_scope: "2020",
|
||
unsupported_but_understood_family: "movement_evidence",
|
||
stale_replay_forbidden: true
|
||
});
|
||
expect(result.reason_codes).toContain("mcp_discovery_metadata_ambiguity_resolved_to_movement_lane");
|
||
expect(result.reason_codes).not.toContain("mcp_discovery_value_flow_signal_detected");
|
||
});
|
||
|
||
it("does not resolve metadata ambiguity into movement lane when confirmed ambiguity sets contain documents only", () => {
|
||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||
userMessage: "по движениям",
|
||
followupContext: {
|
||
previous_discovery_pilot_scope: "metadata_inspection_v1",
|
||
previous_discovery_metadata_ambiguity_detected: true,
|
||
previous_discovery_metadata_ambiguity_entity_sets: ["Документ"],
|
||
previous_filters: {
|
||
counterparty: "SVK",
|
||
period_from: "2020-01-01",
|
||
period_to: "2020-12-31"
|
||
},
|
||
previous_anchor_type: "counterparty",
|
||
previous_anchor_value: "SVK"
|
||
}
|
||
});
|
||
|
||
expect(result.reason_codes).not.toContain("mcp_discovery_metadata_ambiguity_resolved_to_movement_lane");
|
||
});
|
||
|
||
it("continues from ambiguous metadata into document lane when ambiguity choice set collapses to documents on a generic downstream follow-up", () => {
|
||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||
userMessage: "continue with data",
|
||
followupContext: {
|
||
previous_discovery_pilot_scope: "metadata_inspection_v1",
|
||
previous_discovery_metadata_ambiguity_detected: true,
|
||
previous_discovery_metadata_ambiguity_entity_sets: ["Документ", "invoice"],
|
||
previous_filters: {
|
||
counterparty: "SVK",
|
||
period_from: "2020-01-01",
|
||
period_to: "2020-12-31"
|
||
},
|
||
previous_anchor_type: "counterparty",
|
||
previous_anchor_value: "SVK"
|
||
}
|
||
});
|
||
|
||
expect(result.adapter_status).toBe("ready");
|
||
expect(result.should_run_discovery).toBe(true);
|
||
expect(result.semantic_data_need).toBe("document evidence");
|
||
expect(result.turn_meaning_ref).toMatchObject({
|
||
asked_domain_family: "documents",
|
||
asked_action_family: "list_documents",
|
||
explicit_entity_candidates: ["SVK"],
|
||
explicit_date_scope: "2020",
|
||
unsupported_but_understood_family: "document_evidence",
|
||
stale_replay_forbidden: true
|
||
});
|
||
expect(result.reason_codes).toContain("mcp_discovery_metadata_ambiguity_collapsed_to_document_lane");
|
||
});
|
||
|
||
it("continues from ambiguous metadata into movement lane when ambiguity choice set collapses to movements on a generic downstream follow-up", () => {
|
||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||
userMessage: "continue with data",
|
||
followupContext: {
|
||
previous_discovery_pilot_scope: "metadata_inspection_v1",
|
||
previous_discovery_metadata_ambiguity_detected: true,
|
||
previous_discovery_metadata_ambiguity_entity_sets: ["РегистрНакопления", "movement"],
|
||
previous_filters: {
|
||
counterparty: "SVK",
|
||
period_from: "2020-01-01",
|
||
period_to: "2020-12-31"
|
||
},
|
||
previous_anchor_type: "counterparty",
|
||
previous_anchor_value: "SVK"
|
||
}
|
||
});
|
||
|
||
expect(result.adapter_status).toBe("ready");
|
||
expect(result.should_run_discovery).toBe(true);
|
||
expect(result.semantic_data_need).toBe("movement evidence");
|
||
expect(result.turn_meaning_ref).toMatchObject({
|
||
asked_domain_family: "movements",
|
||
asked_action_family: "list_movements",
|
||
explicit_entity_candidates: ["SVK"],
|
||
explicit_date_scope: "2020",
|
||
unsupported_but_understood_family: "movement_evidence",
|
||
stale_replay_forbidden: true
|
||
});
|
||
expect(result.reason_codes).toContain("mcp_discovery_metadata_ambiguity_collapsed_to_movement_lane");
|
||
});
|
||
|
||
it("continues from a confirmed catalog metadata surface into inspect_catalog on a generic downstream follow-up", () => {
|
||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||
userMessage: "continue with data",
|
||
followupContext: {
|
||
previous_discovery_pilot_scope: "metadata_inspection_v1",
|
||
previous_discovery_metadata_route_family: "catalog_drilldown",
|
||
previous_discovery_metadata_selected_surface_objects: ["Catalog.Counterparties"],
|
||
previous_discovery_metadata_recommended_next_primitive: "drilldown_related_objects",
|
||
previous_discovery_metadata_ambiguity_detected: false
|
||
}
|
||
});
|
||
|
||
expect(result.adapter_status).toBe("ready");
|
||
expect(result.should_run_discovery).toBe(true);
|
||
expect(result.semantic_data_need).toBe("1C metadata evidence");
|
||
expect(result.turn_meaning_ref).toMatchObject({
|
||
asked_domain_family: "metadata",
|
||
asked_action_family: "inspect_catalog"
|
||
});
|
||
expect(result.metadata_surface_ref).toMatchObject({
|
||
selected_entity_set: null,
|
||
selected_surface_objects: ["Catalog.Counterparties"],
|
||
downstream_route_family: "catalog_drilldown",
|
||
route_family_selection_basis: null,
|
||
recommended_next_primitive: "drilldown_related_objects",
|
||
ambiguity_detected: false,
|
||
ambiguity_entity_sets: []
|
||
});
|
||
expect(result.reason_codes).toContain("mcp_discovery_metadata_grounded_catalog_continuation");
|
||
expect(result.reason_codes).toContain("mcp_discovery_metadata_surface_ref_from_followup_context");
|
||
});
|
||
|
||
it("requires an explicit lane choice on a generic downstream follow-up when metadata ambiguity stays mixed", () => {
|
||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||
userMessage: "давай дальше",
|
||
followupContext: {
|
||
previous_discovery_pilot_scope: "metadata_inspection_v1",
|
||
previous_discovery_metadata_ambiguity_detected: true,
|
||
previous_discovery_metadata_ambiguity_entity_sets: ["Документ", "РегистрНакопления"],
|
||
previous_filters: {
|
||
counterparty: "SVK",
|
||
period_from: "2020-01-01",
|
||
period_to: "2020-12-31"
|
||
},
|
||
previous_anchor_type: "counterparty",
|
||
previous_anchor_value: "SVK"
|
||
}
|
||
});
|
||
|
||
expect(result.adapter_status).toBe("ready");
|
||
expect(result.should_run_discovery).toBe(true);
|
||
expect(result.source_signal).toBe("followup_context");
|
||
expect(result.semantic_data_need).toBe("metadata lane clarification");
|
||
expect(result.turn_meaning_ref).toMatchObject({
|
||
asked_domain_family: "metadata",
|
||
asked_action_family: "resolve_next_lane",
|
||
explicit_entity_candidates: ["SVK"],
|
||
metadata_ambiguity_entity_sets: ["Документ", "РегистрНакопления"],
|
||
explicit_date_scope: "2020",
|
||
unsupported_but_understood_family: "metadata_lane_choice_clarification",
|
||
stale_replay_forbidden: true
|
||
});
|
||
expect(result.metadata_surface_ref).toMatchObject({
|
||
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: ["Документ", "РегистрНакопления"]
|
||
});
|
||
expect(result.reason_codes).toContain("mcp_discovery_metadata_ambiguity_requires_lane_choice");
|
||
});
|
||
|
||
it("switches the checked year on a short payout follow-up while keeping prior discovery counterparty", () => {
|
||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||
userMessage: "а теперь за 2021?",
|
||
followupContext: {
|
||
previous_discovery_pilot_scope: "counterparty_supplier_payout_query_movements_v1",
|
||
previous_filters: {
|
||
counterparty: "Группа СВК",
|
||
organization: "ООО Альтернатива Плюс",
|
||
period_from: "2020-01-01",
|
||
period_to: "2020-12-31"
|
||
},
|
||
previous_anchor_type: "counterparty",
|
||
previous_anchor_value: "Группа СВК"
|
||
}
|
||
});
|
||
|
||
expect(result.adapter_status).toBe("ready");
|
||
expect(result.should_run_discovery).toBe(true);
|
||
expect(result.turn_meaning_ref).toMatchObject({
|
||
asked_domain_family: "counterparty_value",
|
||
asked_action_family: "payout",
|
||
explicit_entity_candidates: ["Группа СВК"],
|
||
explicit_organization_scope: "ООО Альтернатива Плюс",
|
||
explicit_date_scope: "2021",
|
||
unsupported_but_understood_family: "counterparty_payouts_or_outflow",
|
||
stale_replay_forbidden: true
|
||
});
|
||
});
|
||
|
||
it("does not activate discovery for supported exact current-turn intent", () => {
|
||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||
assistantTurnMeaning: {
|
||
asked_domain_family: "counterparty",
|
||
asked_action_family: "list_documents",
|
||
explicit_intent_candidate: "list_documents_by_counterparty",
|
||
explicit_entity_candidates: [{ value: "SVK" }],
|
||
stale_replay_forbidden: false
|
||
}
|
||
});
|
||
|
||
expect(result.adapter_status).toBe("not_applicable");
|
||
expect(result.should_run_discovery).toBe(false);
|
||
expect(result.turn_meaning_ref).toBeNull();
|
||
expect(result.reason_codes).toContain("mcp_discovery_not_applicable_for_supported_exact_turn");
|
||
});
|
||
|
||
it("routes broad business evaluation into business overview discovery without metadata drift", () => {
|
||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||
userMessage:
|
||
"\u041a\u0430\u043a \u0442\u044b \u043e\u0446\u0435\u043d\u0438\u0448\u044c \u0434\u0435\u044f\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u044c \u043a\u043e\u043c\u043f\u0430\u043d\u0438\u0438?",
|
||
assistantTurnMeaning: {
|
||
asked_domain_family: "business_summary",
|
||
asked_action_family: "broad_evaluation",
|
||
explicit_date_scope: "2026-05-01",
|
||
unsupported_but_understood_family: "broad_business_evaluation",
|
||
stale_replay_forbidden: true
|
||
}
|
||
});
|
||
|
||
expect(result.adapter_status).toBe("ready");
|
||
expect(result.should_run_discovery).toBe(true);
|
||
expect(result.semantic_data_need).toBe("business overview evidence with bounded analyst interpretation");
|
||
expect(result.data_need_graph?.business_fact_family).toBe("business_overview");
|
||
expect(result.data_need_graph?.clarification_gaps).toContain("organization");
|
||
expect(result.turn_meaning_ref).toMatchObject({
|
||
asked_domain_family: "business_overview",
|
||
asked_action_family: "broad_evaluation",
|
||
explicit_date_scope: "2026-05-01",
|
||
unsupported_but_understood_family: "broad_business_evaluation",
|
||
stale_replay_forbidden: true
|
||
});
|
||
expect(result.reason_codes).toContain("mcp_discovery_broad_business_evaluation_route_candidate");
|
||
expect(result.reason_codes).toContain("mcp_discovery_data_need_graph_built");
|
||
expect(result.reason_codes).not.toContain("mcp_discovery_metadata_signal_detected");
|
||
expect(result.reason_codes).not.toContain("mcp_discovery_not_applicable_for_supported_exact_turn");
|
||
});
|
||
|
||
it("lets raw business-overview wording override stale exact turnover meaning", () => {
|
||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||
userMessage:
|
||
"\u0414\u0430\u0439 \u0431\u0438\u0437\u043d\u0435\u0441-\u043e\u0431\u0437\u043e\u0440 \u041e\u041e\u041e \u0410\u043b\u044c\u0442\u0435\u0440\u043d\u0430\u0442\u0438\u0432\u0430 \u041f\u043b\u044e\u0441 \u043f\u043e \u0434\u0430\u043d\u043d\u044b\u043c 1\u0421: \u043e\u0431\u043e\u0440\u043e\u0442\u044b, \u043d\u0435\u0442\u0442\u043e, \u0430\u043a\u0442\u0438\u0432\u043d\u043e\u0441\u0442\u044c.",
|
||
assistantTurnMeaning: {
|
||
asked_domain_family: "counterparty",
|
||
asked_action_family: "counterparty_value_or_turnover",
|
||
explicit_intent_candidate: "customer_revenue_and_payments",
|
||
explicit_entity_candidates: [{ value: "\u043d\u0435\u0438\u0437\u0432\u0435\u0441\u0442\u043d\u043e." }],
|
||
stale_replay_forbidden: false
|
||
},
|
||
predecomposeContract: {
|
||
entities: { organization: "\u041e\u041e\u041e \u0410\u043b\u044c\u0442\u0435\u0440\u043d\u0430\u0442\u0438\u0432\u0430 \u041f\u043b\u044e\u0441" },
|
||
period: { period_to: "2026-05-03" }
|
||
}
|
||
});
|
||
|
||
expect(result.adapter_status).toBe("ready");
|
||
expect(result.should_run_discovery).toBe(true);
|
||
expect(result.semantic_data_need).toBe("business overview evidence with bounded analyst interpretation");
|
||
expect(result.data_need_graph?.business_fact_family).toBe("business_overview");
|
||
expect(result.data_need_graph?.time_scope_need).toBe("all_time_scope");
|
||
expect(result.data_need_graph?.clarification_gaps).toEqual([]);
|
||
expect(result.turn_meaning_ref).toMatchObject({
|
||
asked_domain_family: "business_overview",
|
||
asked_action_family: "broad_evaluation",
|
||
explicit_organization_scope: "\u041e\u041e\u041e \u0410\u043b\u044c\u0442\u0435\u0440\u043d\u0430\u0442\u0438\u0432\u0430 \u041f\u043b\u044e\u0441",
|
||
unsupported_but_understood_family: "broad_business_evaluation",
|
||
stale_replay_forbidden: true
|
||
});
|
||
expect(result.turn_meaning_ref?.explicit_entity_candidates).toBeUndefined();
|
||
expect(result.reason_codes).toContain("mcp_discovery_broad_business_evaluation_route_candidate");
|
||
expect(result.reason_codes).not.toContain("mcp_discovery_value_flow_signal_detected");
|
||
});
|
||
|
||
it("keeps explicit year out of the organization scope for raw business overview wording", () => {
|
||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||
userMessage:
|
||
"Дай бизнес-обзор ООО Альтернатива Плюс за 2020 год по данным 1С: деньги, нетто, активность, дебиторка и кредиторка на дату.",
|
||
assistantTurnMeaning: {
|
||
asked_domain_family: "business_summary",
|
||
asked_action_family: "broad_evaluation",
|
||
unsupported_but_understood_family: "broad_business_evaluation",
|
||
stale_replay_forbidden: true
|
||
},
|
||
predecomposeContract: {
|
||
entities: { organization: "ООО Альтернатива Плюс" },
|
||
period: { period_from: "2020-01-01", period_to: "2020-12-31" }
|
||
}
|
||
});
|
||
|
||
expect(result.adapter_status).toBe("ready");
|
||
expect(result.should_run_discovery).toBe(true);
|
||
expect(result.data_need_graph?.business_fact_family).toBe("business_overview");
|
||
expect(result.data_need_graph?.time_scope_need).toBe("explicit_period");
|
||
expect(result.turn_meaning_ref).toMatchObject({
|
||
explicit_organization_scope: "ООО Альтернатива Плюс",
|
||
explicit_date_scope: "2020"
|
||
});
|
||
});
|
||
|
||
it("keeps all-time business overview from reusing a negated VAT period as active scope", () => {
|
||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||
userMessage:
|
||
"\u0422\u0435\u043f\u0435\u0440\u044c \u043f\u043e \u041e\u041e\u041e \u0410\u043b\u044c\u0442\u0435\u0440\u043d\u0430\u0442\u0438\u0432\u0430 \u041f\u043b\u044e\u0441 \u0437\u0430 \u0432\u0441\u0435 \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u043e\u0435 \u0432\u0440\u0435\u043c\u044f \u0434\u0430\u0439 \u0431\u0438\u0437\u043d\u0435\u0441-\u043e\u0431\u0437\u043e\u0440 \u0432 \u0446\u0435\u043b\u043e\u043c, \u043d\u043e \u043d\u0435 \u0442\u0430\u0449\u0438 \u041d\u0414\u0421 \u0437\u0430 2020 \u043a\u0430\u043a \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u043d\u0443\u044e \u043e\u0431\u0449\u0443\u044e \u043d\u0430\u043b\u043e\u0433\u043e\u0432\u0443\u044e \u043f\u043e\u0437\u0438\u0446\u0438\u044e.",
|
||
assistantTurnMeaning: {
|
||
asked_domain_family: "business_summary",
|
||
asked_action_family: "broad_evaluation",
|
||
explicit_date_scope: "2020-12-31",
|
||
unsupported_but_understood_family: "broad_business_evaluation",
|
||
stale_replay_forbidden: true
|
||
},
|
||
predecomposeContract: {
|
||
entities: { organization: "\u041e\u041e\u041e \u0410\u043b\u044c\u0442\u0435\u0440\u043d\u0430\u0442\u0438\u0432\u0430 \u041f\u043b\u044e\u0441" },
|
||
period: { period_to: "2020-12-31" }
|
||
}
|
||
});
|
||
|
||
expect(result.adapter_status).toBe("ready");
|
||
expect(result.should_run_discovery).toBe(true);
|
||
expect(result.data_need_graph?.business_fact_family).toBe("business_overview");
|
||
expect(result.data_need_graph?.time_scope_need).toBe("all_time_scope");
|
||
expect(result.turn_meaning_ref).toMatchObject({
|
||
asked_domain_family: "business_overview",
|
||
asked_action_family: "broad_evaluation",
|
||
explicit_organization_scope: "\u041e\u041e\u041e \u0410\u043b\u044c\u0442\u0435\u0440\u043d\u0430\u0442\u0438\u0432\u0430 \u041f\u043b\u044e\u0441",
|
||
unsupported_but_understood_family: "broad_business_evaluation",
|
||
stale_replay_forbidden: true
|
||
});
|
||
expect(result.turn_meaning_ref?.explicit_date_scope).toBeUndefined();
|
||
expect(result.turn_meaning_ref?.explicit_entity_candidates).toBeUndefined();
|
||
expect(result.reason_codes).toContain("mcp_discovery_all_time_scope_signal_detected");
|
||
expect(result.reason_codes).toContain("mcp_discovery_negated_tax_period_scope_suppressed");
|
||
});
|
||
|
||
it("does not bootstrap metadata discovery from a referential document exclusion follow-up over exact document context", () => {
|
||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||
userMessage: "кроме этого документа есть еще что-то?",
|
||
effectiveMessage: "документы по контрагенту документа",
|
||
assistantTurnMeaning: {
|
||
asked_domain_family: "counterparty",
|
||
asked_action_family: "list_documents",
|
||
explicit_intent_candidate: "list_documents_by_counterparty",
|
||
explicit_entity_candidates: [{ value: "ТСЖ \"Жуковка 51\"" }]
|
||
},
|
||
followupContext: {
|
||
previous_intent: "list_documents_by_counterparty",
|
||
target_intent: "list_documents_by_counterparty",
|
||
previous_anchor_type: "counterparty",
|
||
previous_anchor_value: "ТСЖ \"Жуковка 51\"",
|
||
previous_filters: {
|
||
counterparty: "жуковке 51"
|
||
}
|
||
}
|
||
});
|
||
|
||
expect(result.adapter_status).toBe("not_applicable");
|
||
expect(result.should_run_discovery).toBe(false);
|
||
expect(result.turn_meaning_ref).toBeNull();
|
||
expect(result.reason_codes).toContain("mcp_discovery_not_applicable_for_supported_exact_turn");
|
||
expect(result.reason_codes).not.toContain("mcp_discovery_metadata_signal_detected");
|
||
expect(result.reason_codes).not.toContain("mcp_discovery_metadata_scope_hint_from_raw_text");
|
||
});
|
||
|
||
it("never serializes object candidates as [object Object]", () => {
|
||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||
assistantTurnMeaning: {
|
||
asked_domain_family: "counterparty",
|
||
asked_action_family: "counterparty_value_or_turnover",
|
||
unsupported_but_understood_family: "counterparty_value_or_turnover",
|
||
explicit_entity_candidates: [{ type: "counterparty", value: "SVK" }]
|
||
}
|
||
});
|
||
|
||
expect(result.turn_meaning_ref?.explicit_entity_candidates).toEqual(["SVK"]);
|
||
expect(result.turn_meaning_ref?.explicit_entity_candidates).not.toContain("[object Object]");
|
||
});
|
||
|
||
it("prefers the raw cleaned entity anchor over canonicalized turn-meaning pollution", () => {
|
||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||
userMessage: "\u043d\u0430\u0439\u0434\u0438 \u0432 1\u0421 \u043a\u043e\u043d\u0442\u0440\u0430\u0433\u0435\u043d\u0442\u0430 \u0413\u0440\u0443\u043f\u043f\u0430 \u0421\u0412\u041a",
|
||
assistantTurnMeaning: {
|
||
asked_domain_family: "counterparty",
|
||
asked_action_family: "search_business_entity",
|
||
explicit_entity_candidates: [{ value: "\u0441 \u043d\u0430\u0438\u043c\u0435\u043d\u043e\u0432\u0430\u043d\u0438\u0435\u043c '\u0413\u0440\u0443\u043f\u043f\u0430 \u0421\u0412\u041a' \u0432 \u0441\u0438\u0441\u0442\u0435\u043c\u0435 1\u0421" }]
|
||
},
|
||
predecomposeContract: {
|
||
entities: {
|
||
counterparty: "\u0441 \u043d\u0430\u0438\u043c\u0435\u043d\u043e\u0432\u0430\u043d\u0438\u0435\u043c '\u0413\u0440\u0443\u043f\u043f\u0430 \u0421\u0412\u041a' \u0432 \u0441\u0438\u0441\u0442\u0435\u043c\u0435 1\u0421"
|
||
}
|
||
}
|
||
});
|
||
|
||
expect(result.adapter_status).toBe("ready");
|
||
expect(result.turn_meaning_ref?.explicit_entity_candidates?.[0]).toBe("\u0413\u0440\u0443\u043f\u043f\u0430 \u0421\u0412\u041a");
|
||
expect(result.turn_meaning_ref?.explicit_entity_candidates).not.toContain(
|
||
"\u0441 \u043d\u0430\u0438\u043c\u0435\u043d\u043e\u0432\u0430\u043d\u0438\u0435\u043c '\u0413\u0440\u0443\u043f\u043f\u0430 \u0421\u0412\u041a' \u0432 \u0441\u0438\u0441\u0442\u0435\u043c\u0435 1\u0421"
|
||
);
|
||
});
|
||
|
||
it("does not concatenate effectiveMessage into the raw entity anchor", () => {
|
||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||
userMessage: "\u043d\u0430\u0439\u0434\u0438 \u0432 1\u0421 \u043a\u043e\u043d\u0442\u0440\u0430\u0433\u0435\u043d\u0442\u0430 \u0413\u0440\u0443\u043f\u043f\u0430 \u0421\u0412\u041a",
|
||
effectiveMessage:
|
||
"\u043d\u0430\u0439\u0442\u0438 \u043a\u043e\u043d\u0442\u0440\u0430\u0433\u0435\u043d\u0442\u0430 \u0441 \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435\u043c '\u0413\u0440\u0443\u043f\u043f\u0430 \u0421\u0412\u041a' \u0432 \u0441\u0438\u0441\u0442\u0435\u043c\u0435 1\u0421"
|
||
});
|
||
|
||
expect(result.adapter_status).toBe("ready");
|
||
expect(result.turn_meaning_ref?.explicit_entity_candidates?.[0]).toBe("\u0413\u0440\u0443\u043f\u043f\u0430 \u0421\u0412\u041a");
|
||
expect(result.turn_meaning_ref?.explicit_entity_candidates).not.toContain(
|
||
"\u0413\u0440\u0443\u043f\u043f\u0430 \u0421\u0412\u041a \u043d\u0430\u0439\u0442\u0438 \u043a\u043e\u043d\u0442\u0440\u0430\u0433\u0435\u043d\u0442\u0430 \u0441 \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435\u043c '\u0413\u0440\u0443\u043f\u043f\u0430 \u0421\u0412\u041a'"
|
||
);
|
||
});
|
||
|
||
it("lets an explicit current entity search override stale ranking follow-up scope", () => {
|
||
const staleEntity = "\u043f\u0440\u0438\u043d\u0451\u0441 \u043d\u0430\u0438\u0431\u043e\u043b\u044c\u0448\u0443\u044e \u0432\u044b\u0440\u0443\u0447\u043a\u0443 \u043e\u0440\u0433\u0430\u043d\u0438\u0437\u0430\u0446\u0438\u0438 \u0432";
|
||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||
userMessage:
|
||
"\u0422\u0435\u043f\u0435\u0440\u044c \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u0430\u044f \u0442\u0435\u043c\u0430 \u043f\u043e \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u043e\u043c\u0443 \u043a\u043e\u043d\u0442\u0440\u0430\u0433\u0435\u043d\u0442\u0443. \u041d\u0430\u0439\u0434\u0438 \u0432 1\u0421 \u0413\u0440\u0443\u043f\u043f\u0443 \u0421\u0412\u041a.",
|
||
assistantTurnMeaning: {
|
||
asked_domain_family: "entity_resolution",
|
||
asked_action_family: "search_business_entity",
|
||
explicit_entity_candidates: [staleEntity],
|
||
explicit_organization_scope: "\u041e\u041e\u041e \u0410\u043b\u044c\u0442\u0435\u0440\u043d\u0430\u0442\u0438\u0432\u0430 \u041f\u043b\u044e\u0441",
|
||
explicit_date_scope: "2021",
|
||
unsupported_but_understood_family: "entity_resolution",
|
||
stale_replay_forbidden: true
|
||
},
|
||
followupContext: {
|
||
previous_discovery_pilot_scope: "counterparty_value_flow_query_movements_v1",
|
||
previous_discovery_ranking_need: "top_desc",
|
||
previous_filters: {
|
||
organization: "\u041e\u041e\u041e \u0410\u043b\u044c\u0442\u0435\u0440\u043d\u0430\u0442\u0438\u0432\u0430 \u041f\u043b\u044e\u0441",
|
||
counterparty: staleEntity,
|
||
period_from: "2021-01-01",
|
||
period_to: "2021-12-31"
|
||
}
|
||
}
|
||
});
|
||
|
||
expect(result.adapter_status).toBe("ready");
|
||
expect(result.should_run_discovery).toBe(true);
|
||
expect(result.source_signal).toBe("raw_text");
|
||
expect(result.turn_meaning_ref?.explicit_entity_candidates).toEqual(["\u0413\u0440\u0443\u043f\u043f\u0430 \u0421\u0412\u041a"]);
|
||
expect(result.turn_meaning_ref?.metadata_scope_hint).toBeUndefined();
|
||
expect(result.turn_meaning_ref?.explicit_organization_scope).toBeUndefined();
|
||
expect(result.turn_meaning_ref?.explicit_date_scope).toBeUndefined();
|
||
expect(result.turn_meaning_ref?.seeded_ranking_need).toBeUndefined();
|
||
expect(result.data_need_graph?.subject_candidates).toEqual(["\u0413\u0440\u0443\u043f\u043f\u0430 \u0421\u0412\u041a"]);
|
||
expect(result.reason_codes).toContain("mcp_discovery_entity_scope_from_raw_entity_search");
|
||
expect(result.reason_codes).not.toContain("mcp_discovery_counterparty_from_followup_context");
|
||
});
|
||
|
||
it("marks top-value wording as a ranking data need without inventing a missing subject gap", () => {
|
||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||
userMessage: "кто больше всего принес денег в 2020"
|
||
});
|
||
|
||
expect(result.adapter_status).toBe("ready");
|
||
expect(result.should_run_discovery).toBe(true);
|
||
expect(result.semantic_data_need).toBe("counterparty value-flow evidence");
|
||
expect(result.data_need_graph?.business_fact_family).toBe("value_flow");
|
||
expect(result.data_need_graph?.ranking_need).toBe("top_desc");
|
||
expect(result.data_need_graph?.clarification_gaps).toEqual(["organization"]);
|
||
expect(result.data_need_graph?.decomposition_candidates).toEqual([
|
||
"collect_scoped_movements",
|
||
"aggregate_ranked_axis_values",
|
||
"probe_coverage"
|
||
]);
|
||
});
|
||
|
||
it("keeps open organization ranking subjectless when assistant meaning invents a predicate-shaped entity", () => {
|
||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||
userMessage:
|
||
"\u0418 \u043a\u0442\u043e \u0431\u043e\u043b\u044c\u0448\u0435 \u0432\u0441\u0435\u0433\u043e \u043f\u0440\u0438\u043d\u0435\u0441 \u0434\u0435\u043d\u0435\u0433 \u044d\u0442\u043e\u0439 \u043e\u0440\u0433\u0430\u043d\u0438\u0437\u0430\u0446\u0438\u0438 \u0432 2020 \u0433\u043e\u0434\u0443?",
|
||
assistantTurnMeaning: {
|
||
asked_domain_family: "counterparty_value",
|
||
asked_action_family: "turnover",
|
||
explicit_entity_candidates: [
|
||
"\u043f\u0440\u0438\u043d\u0451\u0441 \u043d\u0430\u0438\u0431\u043e\u043b\u044c\u0448\u0443\u044e \u0432\u044b\u0440\u0443\u0447\u043a\u0443 \u043e\u0440\u0433\u0430\u043d\u0438\u0437\u0430\u0446\u0438\u0438 \u0432"
|
||
],
|
||
explicit_organization_scope: "\u044d\u0442\u0430 \u043e\u0440\u0433\u0430\u043d\u0438\u0437\u0430\u0446\u0438\u044f",
|
||
explicit_date_scope: "2020",
|
||
unsupported_but_understood_family: "counterparty_value_or_turnover",
|
||
stale_replay_forbidden: true
|
||
},
|
||
predecomposeContract: {
|
||
entities: {
|
||
organization: "\u044d\u0442\u0430 \u043e\u0440\u0433\u0430\u043d\u0438\u0437\u0430\u0446\u0438\u044f"
|
||
}
|
||
},
|
||
followupContext: {
|
||
previous_discovery_pilot_scope: "counterparty_value_flow_query_movements_v1",
|
||
previous_filters: {
|
||
organization: "\u041e\u041e\u041e \u0410\u043b\u044c\u0442\u0435\u0440\u043d\u0430\u0442\u0438\u0432\u0430 \u041f\u043b\u044e\u0441"
|
||
}
|
||
}
|
||
});
|
||
|
||
expect(result.adapter_status).toBe("ready");
|
||
expect(result.should_run_discovery).toBe(true);
|
||
expect(result.turn_meaning_ref?.explicit_entity_candidates).toBeUndefined();
|
||
expect(result.turn_meaning_ref?.explicit_organization_scope).toBe(
|
||
"\u041e\u041e\u041e \u0410\u043b\u044c\u0442\u0435\u0440\u043d\u0430\u0442\u0438\u0432\u0430 \u041f\u043b\u044e\u0441"
|
||
);
|
||
expect(result.turn_meaning_ref?.explicit_date_scope).toBe("2020");
|
||
expect(result.data_need_graph?.subject_candidates).toEqual([]);
|
||
expect(result.data_need_graph?.ranking_need).toBe("top_desc");
|
||
expect(result.data_need_graph?.decomposition_candidates).toContain("aggregate_ranked_axis_values");
|
||
});
|
||
|
||
it("keeps organization as scope for open bidirectional comparison wording instead of inventing a subject candidate", () => {
|
||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||
userMessage: "что больше: входящие или исходящие деньги за 2020 год по ООО Альтернатива Плюс?",
|
||
predecomposeContract: {
|
||
entities: { organization: "ООО Альтернатива Плюс" },
|
||
period: { period_from: "2020-01-01", period_to: "2020-12-31" }
|
||
}
|
||
});
|
||
|
||
expect(result.adapter_status).toBe("ready");
|
||
expect(result.should_run_discovery).toBe(true);
|
||
expect(result.turn_meaning_ref).toMatchObject({
|
||
asked_domain_family: "counterparty_value",
|
||
asked_action_family: "net_value_flow",
|
||
explicit_organization_scope: "ООО Альтернатива Плюс",
|
||
explicit_date_scope: "2020"
|
||
});
|
||
expect(result.turn_meaning_ref?.explicit_entity_candidates).toBeUndefined();
|
||
expect(result.data_need_graph?.comparison_need).toBe("incoming_vs_outgoing");
|
||
expect(result.data_need_graph?.clarification_gaps).toEqual([]);
|
||
});
|
||
|
||
it("drops organization-shaped assistant-turn entity pollution when open comparison already has explicit organization scope", () => {
|
||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||
userMessage: "что больше: входящие или исходящие деньги за 2020 год по ООО Альтернатива Плюс?",
|
||
assistantTurnMeaning: {
|
||
asked_domain_family: "counterparty_value",
|
||
asked_action_family: "net_value_flow",
|
||
explicit_entity_candidates: [{ value: "ООО Альтернатива Плюс" }],
|
||
explicit_organization_scope: "ООО Альтернатива Плюс",
|
||
explicit_date_scope: "2020",
|
||
unsupported_but_understood_family: "counterparty_bidirectional_value_flow_or_netting"
|
||
},
|
||
predecomposeContract: {
|
||
entities: { counterparty: "ООО Альтернатива Плюс", organization: "ООО Альтернатива Плюс" },
|
||
period: { period_from: "2020-01-01", period_to: "2020-12-31" }
|
||
}
|
||
});
|
||
|
||
expect(result.turn_meaning_ref).toMatchObject({
|
||
asked_domain_family: "counterparty_value",
|
||
asked_action_family: "net_value_flow",
|
||
explicit_organization_scope: "ООО Альтернатива Плюс",
|
||
explicit_date_scope: "2020"
|
||
});
|
||
expect(result.turn_meaning_ref?.explicit_entity_candidates).toBeUndefined();
|
||
expect(result.data_need_graph?.comparison_need).toBe("incoming_vs_outgoing");
|
||
});
|
||
|
||
it.skip("treats mirrored predecompose organization and counterparty as organization scope for open comparison", () => {
|
||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||
userMessage: "что больше: входящие или исходящие деньги за 2020 год по ООО Альтернатива Плюс?",
|
||
predecomposeContract: {
|
||
entities: {
|
||
counterparty: "ООО Альтернатива Плюс",
|
||
organization: "ООО Альтернатива Плюс"
|
||
},
|
||
period: { period_from: "2020-01-01", period_to: "2020-12-31" }
|
||
}
|
||
});
|
||
|
||
expect(result.turn_meaning_ref).toMatchObject({
|
||
asked_domain_family: "counterparty_value",
|
||
asked_action_family: "net_value_flow",
|
||
explicit_organization_scope: "ООО Альтернатива Плюс",
|
||
explicit_date_scope: "2020"
|
||
});
|
||
expect(result.turn_meaning_ref?.explicit_entity_candidates).toBeUndefined();
|
||
expect(result.data_need_graph?.subject_candidates).toEqual([]);
|
||
expect(result.data_need_graph?.comparison_need).toBe("incoming_vs_outgoing");
|
||
expect(result.reason_codes).not.toContain("mcp_discovery_counterparty_from_predecompose");
|
||
});
|
||
|
||
it.skip("treats mirrored predecompose organization and counterparty as organization scope for open comparison (utf8-safe)", () => {
|
||
const orgName = "\u041e\u041e\u041e \u0410\u043b\u044c\u0442\u0435\u0440\u043d\u0430\u0442\u0438\u0432\u0430 \u041f\u043b\u044e\u0441";
|
||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||
userMessage: "что больше: входящие или исходящие деньги за 2020 год по ООО Альтернатива Плюс?",
|
||
predecomposeContract: {
|
||
entities: {
|
||
counterparty: orgName,
|
||
organization: orgName
|
||
},
|
||
period: { period_from: "2020-01-01", period_to: "2020-12-31" }
|
||
}
|
||
});
|
||
|
||
expect(result.turn_meaning_ref).toMatchObject({
|
||
asked_domain_family: "counterparty_value",
|
||
asked_action_family: "net_value_flow",
|
||
explicit_organization_scope: orgName,
|
||
explicit_date_scope: "2020"
|
||
});
|
||
expect(result.turn_meaning_ref?.explicit_entity_candidates).toBeUndefined();
|
||
expect(result.data_need_graph?.subject_candidates).toEqual([]);
|
||
expect(result.data_need_graph?.comparison_need).toBe("incoming_vs_outgoing");
|
||
expect(result.reason_codes).not.toContain("mcp_discovery_counterparty_from_predecompose");
|
||
});
|
||
it("keeps organization-scoped incoming totals in an open value-flow lane without inventing a counterparty", () => {
|
||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||
userMessage: "сколько входящих денег за 2020 год по ООО Альтернатива Плюс?",
|
||
predecomposeContract: {
|
||
entities: { organization: "ООО Альтернатива Плюс" },
|
||
period: { period_from: "2020-01-01", period_to: "2020-12-31" }
|
||
}
|
||
});
|
||
|
||
expect(result.adapter_status).toBe("ready");
|
||
expect(result.should_run_discovery).toBe(true);
|
||
expect(result.semantic_data_need).toBe("counterparty value-flow evidence");
|
||
expect(result.turn_meaning_ref).toMatchObject({
|
||
asked_domain_family: "counterparty_value",
|
||
asked_action_family: "turnover",
|
||
explicit_organization_scope: "ООО Альтернатива Плюс",
|
||
explicit_date_scope: "2020",
|
||
unsupported_but_understood_family: "counterparty_value_or_turnover",
|
||
stale_replay_forbidden: true
|
||
});
|
||
expect(result.turn_meaning_ref?.explicit_entity_candidates).toBeUndefined();
|
||
expect(result.data_need_graph?.subject_candidates).toEqual([]);
|
||
expect(result.data_need_graph?.reason_codes).toContain("data_need_graph_open_scope_total_without_subject");
|
||
});
|
||
|
||
it("does not treat mirrored organization/counterparty predecompose as a real subject for organization-scoped payouts", () => {
|
||
const orgName = "ООО Альтернатива Плюс";
|
||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||
userMessage: "сколько исходящих денег за 2020 год по ООО Альтернатива Плюс?",
|
||
predecomposeContract: {
|
||
entities: { counterparty: orgName, organization: orgName },
|
||
period: { period_from: "2020-01-01", period_to: "2020-12-31" }
|
||
}
|
||
});
|
||
|
||
expect(result.adapter_status).toBe("ready");
|
||
expect(result.turn_meaning_ref).toMatchObject({
|
||
asked_domain_family: "counterparty_value",
|
||
asked_action_family: "payout",
|
||
explicit_organization_scope: orgName,
|
||
explicit_date_scope: "2020"
|
||
});
|
||
expect(result.turn_meaning_ref?.explicit_entity_candidates).toBeUndefined();
|
||
expect(result.data_need_graph?.subject_candidates).toEqual([]);
|
||
});
|
||
it("treats a generic incoming total as an open-scope value ask that needs organization rather than a counterparty", () => {
|
||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||
userMessage: "сколько входящих денег за 2020 год?"
|
||
});
|
||
|
||
expect(result.adapter_status).toBe("ready");
|
||
expect(result.should_run_discovery).toBe(true);
|
||
expect(result.semantic_data_need).toBe("counterparty value-flow evidence");
|
||
expect(result.turn_meaning_ref).toMatchObject({
|
||
asked_domain_family: "counterparty_value",
|
||
asked_action_family: "turnover",
|
||
explicit_date_scope: "2020",
|
||
unsupported_but_understood_family: "counterparty_value_or_turnover",
|
||
stale_replay_forbidden: true
|
||
});
|
||
expect(result.turn_meaning_ref?.explicit_entity_candidates).toBeUndefined();
|
||
expect(result.data_need_graph?.subject_candidates).toEqual([]);
|
||
expect(result.data_need_graph?.clarification_gaps).toEqual(["organization"]);
|
||
expect(result.data_need_graph?.reason_codes).toContain("data_need_graph_open_scope_total_without_subject");
|
||
expect(result.data_need_graph?.reason_codes).toContain("data_need_graph_open_scope_total_needs_organization");
|
||
});
|
||
|
||
it("treats colloquial open incoming total wording with filler words as an open-scope ask that needs organization and period", () => {
|
||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||
userMessage: "сколько вообще входящих денег было?"
|
||
});
|
||
|
||
expect(result.adapter_status).toBe("ready");
|
||
expect(result.should_run_discovery).toBe(true);
|
||
expect(result.semantic_data_need).toBe("counterparty value-flow evidence");
|
||
expect(result.turn_meaning_ref).toMatchObject({
|
||
asked_domain_family: "counterparty_value",
|
||
asked_action_family: "turnover",
|
||
unsupported_but_understood_family: "counterparty_value_or_turnover",
|
||
stale_replay_forbidden: true
|
||
});
|
||
expect(result.turn_meaning_ref?.explicit_entity_candidates).toBeUndefined();
|
||
expect(result.data_need_graph?.subject_candidates).toEqual([]);
|
||
expect(result.data_need_graph?.clarification_gaps).toEqual(["organization", "period"]);
|
||
expect(result.data_need_graph?.reason_codes).toContain("data_need_graph_open_scope_total_without_subject");
|
||
expect(result.data_need_graph?.reason_codes).toContain("data_need_graph_open_scope_total_needs_organization");
|
||
});
|
||
|
||
it("resumes an open-scope incoming total from follow-up context when the user clarifies only the organization", () => {
|
||
const orgName = "ООО Альтернатива Плюс";
|
||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||
userMessage: "по ООО Альтернатива Плюс",
|
||
predecomposeContract: {
|
||
entities: { organization: orgName }
|
||
},
|
||
followupContext: {
|
||
previous_discovery_pilot_scope: "counterparty_value_flow_query_movements_v1",
|
||
previous_filters: {
|
||
period_from: "2020-01-01",
|
||
period_to: "2020-12-31"
|
||
}
|
||
}
|
||
});
|
||
|
||
expect(result.adapter_status).toBe("ready");
|
||
expect(result.should_run_discovery).toBe(true);
|
||
expect(result.source_signal).toBe("followup_context");
|
||
expect(result.semantic_data_need).toBe("counterparty value-flow evidence");
|
||
expect(result.turn_meaning_ref).toMatchObject({
|
||
asked_domain_family: "counterparty_value",
|
||
asked_action_family: "turnover",
|
||
explicit_organization_scope: orgName,
|
||
explicit_date_scope: "2020",
|
||
unsupported_but_understood_family: "counterparty_value_or_turnover",
|
||
stale_replay_forbidden: true
|
||
});
|
||
expect(result.turn_meaning_ref?.explicit_entity_candidates).toBeUndefined();
|
||
expect(result.reason_codes).toContain("mcp_discovery_seeded_from_followup_context");
|
||
expect(result.data_need_graph?.clarification_gaps).toEqual([]);
|
||
});
|
||
|
||
it("does not keep a stale follow-up date when the user switches an open-scope total to all-time wording", () => {
|
||
const orgName = "ООО Альтернатива Плюс";
|
||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||
userMessage: "сколько вообще денег мы заработали за все время?",
|
||
followupContext: {
|
||
previous_discovery_pilot_scope: "counterparty_value_flow_query_movements_v1",
|
||
previous_filters: {
|
||
organization: orgName,
|
||
as_of_date: "2026-04-23"
|
||
}
|
||
}
|
||
});
|
||
|
||
expect(result.adapter_status).toBe("ready");
|
||
expect(result.should_run_discovery).toBe(true);
|
||
expect(result.semantic_data_need).toBe("counterparty value-flow evidence");
|
||
expect(result.turn_meaning_ref).toMatchObject({
|
||
asked_domain_family: "counterparty_value",
|
||
asked_action_family: "turnover",
|
||
explicit_organization_scope: orgName,
|
||
unsupported_but_understood_family: "counterparty_value_or_turnover",
|
||
stale_replay_forbidden: true
|
||
});
|
||
expect(result.turn_meaning_ref?.explicit_date_scope).toBeUndefined();
|
||
expect(result.reason_codes).toContain("mcp_discovery_all_time_scope_signal_detected");
|
||
expect(result.reason_codes).not.toContain("mcp_discovery_date_scope_from_followup_context");
|
||
expect(result.data_need_graph?.clarification_gaps).toEqual([]);
|
||
expect(result.data_need_graph?.time_scope_need).toBe("all_time_scope");
|
||
expect(result.data_need_graph?.reason_codes).toContain("data_need_graph_all_time_scope_hint");
|
||
});
|
||
|
||
it("resumes an open-scope total clarification loop from saved state when the user resolves the pending period with all-time wording", () => {
|
||
const orgName = "ООО Альтернатива Плюс";
|
||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||
userMessage: "за все время",
|
||
assistantTurnMeaning: {
|
||
explicit_intent_candidate: "customer_revenue_and_payments"
|
||
},
|
||
followupContext: {
|
||
previous_discovery_loop_status: "awaiting_clarification",
|
||
previous_discovery_loop_selected_chain_id: "value_flow",
|
||
previous_discovery_loop_pending_axes: ["period"],
|
||
previous_discovery_loop_provided_axes: ["organization", "aggregate_axis", "amount", "coverage_target"],
|
||
previous_discovery_loop_asked_domain_family: "counterparty_value",
|
||
previous_discovery_loop_asked_action_family: "turnover",
|
||
previous_discovery_loop_unsupported_family: "counterparty_value_or_turnover",
|
||
previous_filters: {
|
||
organization: orgName
|
||
}
|
||
}
|
||
});
|
||
|
||
expect(result.adapter_status).toBe("ready");
|
||
expect(result.should_run_discovery).toBe(true);
|
||
expect(result.reason_codes).toContain("mcp_discovery_all_time_scope_signal_detected");
|
||
expect(result.reason_codes).toContain("mcp_discovery_period_clarification_followup_from_followup_context");
|
||
expect(result.reason_codes).not.toContain("mcp_discovery_not_applicable_for_supported_exact_turn");
|
||
expect(result.turn_meaning_ref).toMatchObject({
|
||
asked_domain_family: "counterparty_value",
|
||
asked_action_family: "turnover",
|
||
explicit_organization_scope: orgName,
|
||
unsupported_but_understood_family: "counterparty_value_or_turnover",
|
||
stale_replay_forbidden: true
|
||
});
|
||
expect(result.turn_meaning_ref?.explicit_date_scope).toBeUndefined();
|
||
expect(result.data_need_graph?.clarification_gaps).toEqual([]);
|
||
expect(result.data_need_graph?.time_scope_need).toBe("all_time_scope");
|
||
expect(result.data_need_graph?.reason_codes).toContain("data_need_graph_open_scope_total_without_subject");
|
||
expect(result.data_need_graph?.reason_codes).toContain("data_need_graph_all_time_scope_hint");
|
||
});
|
||
|
||
it("resumes an open-scope ranking from follow-up context when the user clarifies only the organization", () => {
|
||
const orgName = "ООО Альтернатива Плюс";
|
||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||
userMessage: "по ООО Альтернатива Плюс",
|
||
predecomposeContract: {
|
||
entities: { organization: orgName }
|
||
},
|
||
followupContext: {
|
||
previous_discovery_pilot_scope: "counterparty_value_flow_query_movements_v1",
|
||
previous_discovery_ranking_need: "top_desc",
|
||
previous_filters: {
|
||
period_from: "2020-01-01",
|
||
period_to: "2020-12-31"
|
||
}
|
||
}
|
||
});
|
||
|
||
expect(result.adapter_status).toBe("ready");
|
||
expect(result.should_run_discovery).toBe(true);
|
||
expect(result.source_signal).toBe("followup_context");
|
||
expect(result.semantic_data_need).toBe("counterparty value-flow evidence");
|
||
expect(result.turn_meaning_ref).toMatchObject({
|
||
asked_domain_family: "counterparty_value",
|
||
asked_action_family: "turnover",
|
||
seeded_ranking_need: "top_desc",
|
||
explicit_organization_scope: orgName,
|
||
explicit_date_scope: "2020",
|
||
unsupported_but_understood_family: "counterparty_value_or_turnover",
|
||
stale_replay_forbidden: true
|
||
});
|
||
expect(result.turn_meaning_ref?.explicit_entity_candidates).toBeUndefined();
|
||
expect(result.data_need_graph?.ranking_need).toBe("top_desc");
|
||
expect(result.data_need_graph?.clarification_gaps).toEqual([]);
|
||
});
|
||
|
||
it("resumes an open-scope ranking from saved loop state even without a previous pilot scope", () => {
|
||
const orgName = "ООО Альтернатива Плюс";
|
||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||
userMessage: "по ООО Альтернатива Плюс",
|
||
predecomposeContract: {
|
||
entities: { organization: orgName }
|
||
},
|
||
followupContext: {
|
||
previous_discovery_loop_status: "awaiting_clarification",
|
||
previous_discovery_loop_selected_chain_id: "value_flow_ranking",
|
||
previous_discovery_loop_pending_axes: ["organization"],
|
||
previous_discovery_loop_provided_axes: ["aggregate_axis", "amount", "coverage_target"],
|
||
previous_discovery_loop_asked_domain_family: "counterparty_value",
|
||
previous_discovery_loop_asked_action_family: "turnover",
|
||
previous_discovery_loop_unsupported_family: "counterparty_value_or_turnover",
|
||
previous_discovery_ranking_need: "top_desc",
|
||
previous_filters: {
|
||
period_from: "2020-01-01",
|
||
period_to: "2020-12-31"
|
||
}
|
||
}
|
||
});
|
||
|
||
expect(result.adapter_status).toBe("ready");
|
||
expect(result.should_run_discovery).toBe(true);
|
||
expect(result.source_signal).toBe("followup_context");
|
||
expect(result.semantic_data_need).toBe("counterparty value-flow evidence");
|
||
expect(result.turn_meaning_ref).toMatchObject({
|
||
asked_domain_family: "counterparty_value",
|
||
asked_action_family: "turnover",
|
||
seeded_ranking_need: "top_desc",
|
||
explicit_organization_scope: orgName,
|
||
explicit_date_scope: "2020",
|
||
unsupported_but_understood_family: "counterparty_value_or_turnover",
|
||
stale_replay_forbidden: true
|
||
});
|
||
expect(result.data_need_graph?.ranking_need).toBe("top_desc");
|
||
expect(result.reason_codes).toContain("mcp_discovery_resumed_from_saved_loop_state");
|
||
});
|
||
|
||
it("does not keep an implicit today date while a ranking clarification loop still needs period", () => {
|
||
const todayIso = new Date().toISOString().slice(0, 10);
|
||
const orgName = "ООО Альтернатива Плюс";
|
||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||
userMessage: "по ООО Альтернатива Плюс",
|
||
assistantTurnMeaning: {
|
||
asked_domain_family: "counterparty_value",
|
||
asked_action_family: "turnover",
|
||
explicit_date_scope: todayIso,
|
||
explicit_intent_candidate: "customer_revenue_and_payments"
|
||
},
|
||
predecomposeContract: {
|
||
entities: { organization: orgName }
|
||
},
|
||
followupContext: {
|
||
previous_discovery_loop_status: "awaiting_clarification",
|
||
previous_discovery_loop_selected_chain_id: "value_flow_ranking",
|
||
previous_discovery_loop_pending_axes: ["organization", "period"],
|
||
previous_discovery_loop_provided_axes: ["aggregate_axis", "amount", "coverage_target"],
|
||
previous_discovery_loop_asked_domain_family: "counterparty_value",
|
||
previous_discovery_loop_asked_action_family: "turnover",
|
||
previous_discovery_loop_unsupported_family: "counterparty_value_or_turnover",
|
||
previous_discovery_ranking_need: "top_desc",
|
||
previous_filters: {
|
||
period_to: todayIso
|
||
}
|
||
}
|
||
});
|
||
|
||
expect(result.turn_meaning_ref).toMatchObject({
|
||
asked_domain_family: "counterparty_value",
|
||
asked_action_family: "turnover",
|
||
seeded_ranking_need: "top_desc",
|
||
explicit_organization_scope: orgName
|
||
});
|
||
expect(result.turn_meaning_ref?.explicit_date_scope).toBeUndefined();
|
||
expect(result.data_need_graph?.clarification_gaps).toEqual(["period"]);
|
||
});
|
||
|
||
it("does not keep an implicit today date while an open total clarification loop still needs period", () => {
|
||
const todayIso = new Date().toISOString().slice(0, 10);
|
||
const orgName = "ООО Альтернатива Плюс";
|
||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||
userMessage: "по ООО Альтернатива Плюс",
|
||
assistantTurnMeaning: {
|
||
asked_domain_family: "counterparty_value",
|
||
asked_action_family: "turnover",
|
||
explicit_date_scope: todayIso,
|
||
explicit_intent_candidate: "customer_revenue_and_payments"
|
||
},
|
||
predecomposeContract: {
|
||
entities: { organization: orgName }
|
||
},
|
||
followupContext: {
|
||
previous_discovery_loop_status: "awaiting_clarification",
|
||
previous_discovery_loop_selected_chain_id: "value_flow",
|
||
previous_discovery_loop_pending_axes: ["organization", "period"],
|
||
previous_discovery_loop_provided_axes: ["amount", "coverage_target"],
|
||
previous_discovery_loop_asked_domain_family: "counterparty_value",
|
||
previous_discovery_loop_asked_action_family: "turnover",
|
||
previous_discovery_loop_unsupported_family: "counterparty_value_or_turnover",
|
||
previous_filters: {
|
||
period_to: todayIso
|
||
}
|
||
}
|
||
});
|
||
|
||
expect(result.turn_meaning_ref).toMatchObject({
|
||
asked_domain_family: "counterparty_value",
|
||
asked_action_family: "turnover",
|
||
explicit_organization_scope: orgName
|
||
});
|
||
expect(result.turn_meaning_ref?.explicit_date_scope).toBeUndefined();
|
||
expect(result.data_need_graph?.clarification_gaps).toEqual(["period"]);
|
||
});
|
||
|
||
it("resolves metadata lane choice from saved loop state even without a previous pilot scope", () => {
|
||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||
userMessage: "по движениям",
|
||
followupContext: {
|
||
previous_discovery_loop_status: "awaiting_clarification",
|
||
previous_discovery_loop_selected_chain_id: "metadata_lane_clarification",
|
||
previous_discovery_loop_pending_axes: ["lane_family_choice"],
|
||
previous_discovery_loop_asked_domain_family: "metadata",
|
||
previous_discovery_loop_asked_action_family: "resolve_next_lane",
|
||
previous_discovery_loop_unsupported_family: "metadata_lane_choice_clarification",
|
||
previous_filters: {
|
||
counterparty: "Группа СВК",
|
||
period_from: "2020-01-01",
|
||
period_to: "2020-12-31"
|
||
},
|
||
previous_discovery_metadata_ambiguity_detected: true,
|
||
previous_discovery_metadata_ambiguity_entity_sets: ["Документ", "РегистрНакопления"]
|
||
}
|
||
});
|
||
|
||
expect(result.adapter_status).toBe("ready");
|
||
expect(result.should_run_discovery).toBe(true);
|
||
expect(result.source_signal).toBe("followup_context");
|
||
expect(result.semantic_data_need).toBe("movement evidence");
|
||
expect(result.turn_meaning_ref).toMatchObject({
|
||
asked_domain_family: "movements",
|
||
asked_action_family: "list_movements",
|
||
explicit_entity_candidates: ["Группа СВК"],
|
||
explicit_date_scope: "2020",
|
||
unsupported_but_understood_family: "movement_evidence",
|
||
stale_replay_forbidden: true
|
||
});
|
||
expect(result.reason_codes).toContain("mcp_discovery_resumed_from_saved_loop_state");
|
||
});
|
||
|
||
it("keeps seeded ranking through a year-switch follow-up after organization clarification", () => {
|
||
const orgName = "ООО Альтернатива Плюс";
|
||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||
userMessage: "Р° Р·Р° 2021?",
|
||
followupContext: {
|
||
previous_discovery_pilot_scope: "counterparty_value_flow_query_movements_v1",
|
||
previous_discovery_ranking_need: "top_desc",
|
||
previous_filters: {
|
||
organization: orgName,
|
||
counterparty: "\u041f\u0440\u043e\u0432\u0435\u0441\u0442\u0438",
|
||
period_from: "2020-01-01",
|
||
period_to: "2020-12-31"
|
||
}
|
||
}
|
||
});
|
||
|
||
expect(result.adapter_status).toBe("ready");
|
||
expect(result.should_run_discovery).toBe(true);
|
||
expect(result.source_signal).toBe("followup_context");
|
||
expect(result.semantic_data_need).toBe("counterparty value-flow evidence");
|
||
expect(result.turn_meaning_ref).toMatchObject({
|
||
asked_domain_family: "counterparty_value",
|
||
asked_action_family: "turnover",
|
||
seeded_ranking_need: "top_desc",
|
||
explicit_organization_scope: orgName,
|
||
explicit_date_scope: "2021",
|
||
unsupported_but_understood_family: "counterparty_value_or_turnover",
|
||
stale_replay_forbidden: true
|
||
});
|
||
expect(result.data_need_graph?.ranking_need).toBe("top_desc");
|
||
expect(result.data_need_graph?.clarification_gaps).toEqual([]);
|
||
});
|
||
it("forces discovery over a supported exact intent when organization-only follow-up resolves an open-scope total", () => {
|
||
const orgName = "\u041e\u041e\u041e \u0410\u043b\u044c\u0442\u0435\u0440\u043d\u0430\u0442\u0438\u0432\u0430 \u041f\u043b\u044e\u0441";
|
||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||
userMessage: "\u043f\u043e \u041e\u041e\u041e \u0410\u043b\u044c\u0442\u0435\u0440\u043d\u0430\u0442\u0438\u0432\u0430 \u041f\u043b\u044e\u0441",
|
||
assistantTurnMeaning: {
|
||
asked_domain_family: "counterparty",
|
||
asked_action_family: "turnover",
|
||
explicit_intent_candidate: "customer_revenue_and_payments"
|
||
},
|
||
followupContext: {
|
||
previous_discovery_pilot_scope: "counterparty_value_flow_query_movements_v1",
|
||
previous_filters: {
|
||
period_from: "2020-01-01",
|
||
period_to: "2020-12-31"
|
||
}
|
||
}
|
||
});
|
||
|
||
expect(result.adapter_status).toBe("ready");
|
||
expect(result.should_run_discovery).toBe(true);
|
||
expect(result.semantic_data_need).toBe("counterparty value-flow evidence");
|
||
expect(result.turn_meaning_ref).toMatchObject({
|
||
asked_domain_family: "counterparty_value",
|
||
asked_action_family: "turnover",
|
||
explicit_organization_scope: orgName,
|
||
explicit_date_scope: "2020",
|
||
unsupported_but_understood_family: "counterparty_value_or_turnover",
|
||
stale_replay_forbidden: true
|
||
});
|
||
expect(result.turn_meaning_ref?.explicit_entity_candidates).toBeUndefined();
|
||
expect(result.reason_codes).toContain(
|
||
"mcp_discovery_organization_clarification_followup_from_followup_context"
|
||
);
|
||
expect(result.reason_codes).not.toContain("mcp_discovery_not_applicable_for_supported_exact_turn");
|
||
});
|
||
|
||
it.skip("keeps metadata-born movement lane subjectless and asks for organization plus period", () => {
|
||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||
userMessage: "по движениям",
|
||
followupContext: {
|
||
previous_discovery_loop_status: "awaiting_clarification",
|
||
previous_discovery_loop_selected_chain_id: "metadata_lane_clarification",
|
||
previous_discovery_loop_pending_axes: ["lane_family_choice"],
|
||
previous_discovery_loop_asked_domain_family: "metadata",
|
||
previous_discovery_loop_asked_action_family: "resolve_next_lane",
|
||
previous_discovery_loop_unsupported_family: "metadata_lane_choice_clarification",
|
||
previous_discovery_entity_candidates: ["НДС"],
|
||
previous_discovery_metadata_ambiguity_detected: true,
|
||
previous_discovery_metadata_ambiguity_entity_sets: ["Документ", "РегистрНакопления"]
|
||
}
|
||
});
|
||
|
||
expect(result.adapter_status).toBe("ready");
|
||
expect(result.should_run_discovery).toBe(true);
|
||
expect(result.semantic_data_need).toBe("movement evidence");
|
||
expect(result.turn_meaning_ref).toMatchObject({
|
||
asked_domain_family: "movements",
|
||
asked_action_family: "list_movements",
|
||
metadata_scope_hint: "НДС",
|
||
subject_resolution_optional: true,
|
||
unsupported_but_understood_family: "movement_evidence",
|
||
stale_replay_forbidden: true
|
||
});
|
||
expect(result.turn_meaning_ref?.explicit_entity_candidates).toBeUndefined();
|
||
expect(result.data_need_graph?.clarification_gaps).toEqual(["organization", "period"]);
|
||
expect(result.reason_codes).toContain("mcp_discovery_metadata_ambiguity_resolved_to_movement_lane");
|
||
expect(result.reason_codes).toContain("mcp_discovery_metadata_scoped_lane_without_subject");
|
||
});
|
||
|
||
it.skip("keeps metadata scope through organization-only clarification and leaves only period pending", () => {
|
||
const orgName = "ООО Альтернатива Плюс";
|
||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||
userMessage: "по ООО Альтернатива Плюс",
|
||
predecomposeContract: {
|
||
entities: { organization: orgName }
|
||
},
|
||
followupContext: {
|
||
previous_discovery_loop_status: "awaiting_clarification",
|
||
previous_discovery_loop_selected_chain_id: "movement_evidence",
|
||
previous_discovery_loop_pending_axes: ["organization", "period"],
|
||
previous_discovery_loop_asked_domain_family: "movements",
|
||
previous_discovery_loop_asked_action_family: "list_movements",
|
||
previous_discovery_loop_unsupported_family: "movement_evidence",
|
||
previous_discovery_entity_candidates: ["НДС"]
|
||
}
|
||
});
|
||
|
||
expect(result.adapter_status).toBe("ready");
|
||
expect(result.should_run_discovery).toBe(true);
|
||
expect(result.semantic_data_need).toBe("movement evidence");
|
||
expect(result.turn_meaning_ref).toMatchObject({
|
||
asked_domain_family: "movements",
|
||
asked_action_family: "list_movements",
|
||
metadata_scope_hint: "НДС",
|
||
subject_resolution_optional: true,
|
||
explicit_organization_scope: orgName,
|
||
unsupported_but_understood_family: "movement_evidence",
|
||
stale_replay_forbidden: true
|
||
});
|
||
expect(result.turn_meaning_ref?.explicit_entity_candidates).toBeUndefined();
|
||
expect(result.data_need_graph?.clarification_gaps).toEqual(["period"]);
|
||
});
|
||
it("keeps metadata-born movement lane subjectless and asks for organization plus period (utf8-safe)", () => {
|
||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||
userMessage: "\u043f\u043e \u0434\u0432\u0438\u0436\u0435\u043d\u0438\u044f\u043c",
|
||
followupContext: {
|
||
previous_discovery_loop_status: "awaiting_clarification",
|
||
previous_discovery_loop_selected_chain_id: "metadata_lane_clarification",
|
||
previous_discovery_loop_pending_axes: ["lane_family_choice"],
|
||
previous_discovery_loop_asked_domain_family: "metadata",
|
||
previous_discovery_loop_asked_action_family: "resolve_next_lane",
|
||
previous_discovery_loop_unsupported_family: "metadata_lane_choice_clarification",
|
||
previous_discovery_entity_candidates: ["\u041d\u0414\u0421"],
|
||
previous_discovery_metadata_ambiguity_detected: true,
|
||
previous_discovery_metadata_ambiguity_entity_sets: [
|
||
"\u0414\u043e\u043a\u0443\u043c\u0435\u043d\u0442",
|
||
"\u0420\u0435\u0433\u0438\u0441\u0442\u0440\u041d\u0430\u043a\u043e\u043f\u043b\u0435\u043d\u0438\u044f"
|
||
]
|
||
}
|
||
});
|
||
|
||
expect(result.adapter_status).toBe("ready");
|
||
expect(result.should_run_discovery).toBe(true);
|
||
expect(result.semantic_data_need).toBe("movement evidence");
|
||
expect(result.turn_meaning_ref).toMatchObject({
|
||
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",
|
||
stale_replay_forbidden: true
|
||
});
|
||
expect(result.turn_meaning_ref?.explicit_entity_candidates).toBeUndefined();
|
||
expect(result.data_need_graph?.clarification_gaps).toEqual(["organization", "period"]);
|
||
expect(result.reason_codes).toContain("mcp_discovery_metadata_ambiguity_resolved_to_movement_lane");
|
||
expect(result.reason_codes).toContain("mcp_discovery_metadata_scoped_lane_without_subject");
|
||
});
|
||
|
||
it("keeps metadata scope through organization-only clarification and leaves only period pending (utf8-safe)", () => {
|
||
const orgName =
|
||
"\u041e\u041e\u041e \u0410\u043b\u044c\u0442\u0435\u0440\u043d\u0430\u0442\u0438\u0432\u0430 \u041f\u043b\u044e\u0441";
|
||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||
userMessage:
|
||
"\u043f\u043e \u041e\u041e\u041e \u0410\u043b\u044c\u0442\u0435\u0440\u043d\u0430\u0442\u0438\u0432\u0430 \u041f\u043b\u044e\u0441",
|
||
predecomposeContract: {
|
||
entities: { organization: orgName }
|
||
},
|
||
followupContext: {
|
||
previous_discovery_loop_status: "awaiting_clarification",
|
||
previous_discovery_loop_selected_chain_id: "movement_evidence",
|
||
previous_discovery_loop_pending_axes: ["organization", "period"],
|
||
previous_discovery_loop_asked_domain_family: "movements",
|
||
previous_discovery_loop_asked_action_family: "list_movements",
|
||
previous_discovery_loop_unsupported_family: "movement_evidence",
|
||
previous_discovery_loop_metadata_scope_hint: "\u041d\u0414\u0421",
|
||
previous_discovery_loop_subject_resolution_optional: true,
|
||
previous_discovery_entity_candidates: ["\u041d\u0414\u0421"]
|
||
}
|
||
});
|
||
|
||
expect(result.adapter_status).toBe("ready");
|
||
expect(result.should_run_discovery).toBe(true);
|
||
expect(result.semantic_data_need).toBe("movement evidence");
|
||
expect(result.turn_meaning_ref).toMatchObject({
|
||
asked_domain_family: "movements",
|
||
asked_action_family: "list_movements",
|
||
metadata_scope_hint: "\u041d\u0414\u0421",
|
||
subject_resolution_optional: true,
|
||
explicit_organization_scope: orgName,
|
||
unsupported_but_understood_family: "movement_evidence",
|
||
stale_replay_forbidden: true
|
||
});
|
||
expect(result.turn_meaning_ref?.explicit_entity_candidates).toBeUndefined();
|
||
expect(result.data_need_graph?.clarification_gaps).toEqual(["period"]);
|
||
});
|
||
|
||
it("pivots a metadata-scoped subjectless movement retrieval into documents without inventing a counterparty", () => {
|
||
const orgName =
|
||
"\u041e\u041e\u041e \u0410\u043b\u044c\u0442\u0435\u0440\u043d\u0430\u0442\u0438\u0432\u0430 \u041f\u043b\u044e\u0441";
|
||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||
userMessage: "\u0430 \u0442\u0435\u043f\u0435\u0440\u044c \u043f\u043e \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u043c?",
|
||
assistantTurnMeaning: {
|
||
asked_domain_family: "counterparty",
|
||
asked_action_family: "list_documents",
|
||
explicit_intent_candidate: "list_documents_by_counterparty"
|
||
},
|
||
followupContext: {
|
||
previous_discovery_pilot_scope: "counterparty_movement_evidence_query_movements_v1",
|
||
previous_discovery_loop_status: "ready_for_next_hop",
|
||
previous_discovery_loop_selected_chain_id: "movement_evidence",
|
||
previous_discovery_loop_pending_axes: [],
|
||
previous_discovery_loop_asked_domain_family: "movements",
|
||
previous_discovery_loop_asked_action_family: "list_movements",
|
||
previous_discovery_loop_unsupported_family: "movement_evidence",
|
||
previous_discovery_loop_metadata_scope_hint: "\u041d\u0414\u0421",
|
||
previous_discovery_loop_subject_resolution_optional: true,
|
||
previous_discovery_entity_candidates: ["\u041d\u0414\u0421"],
|
||
previous_filters: {
|
||
organization: orgName,
|
||
period_from: "2020-01-01",
|
||
period_to: "2020-12-31"
|
||
}
|
||
}
|
||
});
|
||
|
||
expect(result.adapter_status).toBe("ready");
|
||
expect(result.should_run_discovery).toBe(true);
|
||
expect(result.semantic_data_need).toBe("document evidence");
|
||
expect(result.turn_meaning_ref).toMatchObject({
|
||
asked_domain_family: "documents",
|
||
asked_action_family: "list_documents",
|
||
metadata_scope_hint: "\u041d\u0414\u0421",
|
||
subject_resolution_optional: true,
|
||
explicit_organization_scope: orgName,
|
||
explicit_date_scope: "2020",
|
||
unsupported_but_understood_family: "document_evidence",
|
||
stale_replay_forbidden: true
|
||
});
|
||
expect(result.turn_meaning_ref?.explicit_entity_candidates).toBeUndefined();
|
||
expect(result.reason_codes).toContain("mcp_discovery_metadata_scoped_movement_to_document_followup");
|
||
});
|
||
|
||
it("keeps the metadata-scoped document lane after a year-switch follow-up", () => {
|
||
const orgName =
|
||
"\u041e\u041e\u041e \u0410\u043b\u044c\u0442\u0435\u0440\u043d\u0430\u0442\u0438\u0432\u0430 \u041f\u043b\u044e\u0441";
|
||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||
userMessage: "\u0430 \u0442\u0435\u043f\u0435\u0440\u044c \u0437\u0430 2021?",
|
||
followupContext: {
|
||
previous_discovery_pilot_scope: "counterparty_document_evidence_query_documents_v1",
|
||
previous_discovery_loop_status: "ready_for_next_hop",
|
||
previous_discovery_loop_selected_chain_id: "document_evidence",
|
||
previous_discovery_loop_pending_axes: [],
|
||
previous_discovery_loop_asked_domain_family: "documents",
|
||
previous_discovery_loop_asked_action_family: "list_documents",
|
||
previous_discovery_loop_unsupported_family: "document_evidence",
|
||
previous_discovery_loop_metadata_scope_hint: "\u041d\u0414\u0421",
|
||
previous_discovery_loop_subject_resolution_optional: true,
|
||
previous_discovery_entity_candidates: ["\u041d\u0414\u0421"],
|
||
previous_filters: {
|
||
organization: orgName,
|
||
period_from: "2020-01-01",
|
||
period_to: "2020-12-31"
|
||
}
|
||
}
|
||
});
|
||
|
||
expect(result.adapter_status).toBe("ready");
|
||
expect(result.should_run_discovery).toBe(true);
|
||
expect(result.semantic_data_need).toBe("document evidence");
|
||
expect(result.turn_meaning_ref).toMatchObject({
|
||
asked_domain_family: "documents",
|
||
asked_action_family: "list_documents",
|
||
metadata_scope_hint: "\u041d\u0414\u0421",
|
||
subject_resolution_optional: true,
|
||
explicit_organization_scope: orgName,
|
||
explicit_date_scope: "2021",
|
||
unsupported_but_understood_family: "document_evidence",
|
||
stale_replay_forbidden: true
|
||
});
|
||
expect(result.turn_meaning_ref?.explicit_entity_candidates).toBeUndefined();
|
||
});
|
||
|
||
it("does not promote organization scope into counterparty subject after a metadata-scoped movement year switch", () => {
|
||
const orgName =
|
||
"\u041e\u041e\u041e \u0410\u043b\u044c\u0442\u0435\u0440\u043d\u0430\u0442\u0438\u0432\u0430 \u041f\u043b\u044e\u0441";
|
||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||
userMessage: "\u0417\u0430 2020 \u0433\u043e\u0434.",
|
||
followupContext: {
|
||
previous_discovery_pilot_scope: "counterparty_movement_evidence_query_movements_v1",
|
||
previous_discovery_loop_status: "ready_for_next_hop",
|
||
previous_discovery_loop_selected_chain_id: "movement_evidence",
|
||
previous_discovery_loop_pending_axes: [],
|
||
previous_discovery_loop_asked_domain_family: "movements",
|
||
previous_discovery_loop_asked_action_family: "list_movements",
|
||
previous_discovery_loop_unsupported_family: "movement_evidence",
|
||
previous_discovery_loop_metadata_scope_hint: "\u041d\u0414\u0421",
|
||
previous_discovery_loop_subject_resolution_optional: true,
|
||
previous_discovery_entity_candidates: ["\u041d\u0414\u0421"],
|
||
previous_filters: {
|
||
organization: orgName,
|
||
counterparty: orgName,
|
||
period_to: "2026-04-24"
|
||
}
|
||
}
|
||
});
|
||
|
||
expect(result.adapter_status).toBe("ready");
|
||
expect(result.should_run_discovery).toBe(true);
|
||
expect(result.semantic_data_need).toBe("movement evidence");
|
||
expect(result.turn_meaning_ref).toMatchObject({
|
||
asked_domain_family: "movements",
|
||
asked_action_family: "list_movements",
|
||
metadata_scope_hint: "\u041d\u0414\u0421",
|
||
subject_resolution_optional: true,
|
||
explicit_organization_scope: orgName,
|
||
explicit_date_scope: "2020",
|
||
unsupported_but_understood_family: "movement_evidence",
|
||
stale_replay_forbidden: true
|
||
});
|
||
expect(result.turn_meaning_ref?.explicit_entity_candidates).toBeUndefined();
|
||
expect(result.data_need_graph?.subject_candidates).toEqual([]);
|
||
expect(result.reason_codes).toContain("mcp_discovery_metadata_scoped_lane_without_subject");
|
||
expect(result.reason_codes).not.toContain("mcp_discovery_counterparty_from_followup_context");
|
||
});
|
||
});
|