Укрепить автономные reviewed-маршруты и срезы задолженности

This commit is contained in:
2026-05-12 23:26:20 +03:00
parent 5dedfeee86
commit b99a3be083
46 changed files with 3234 additions and 142 deletions
@@ -96,6 +96,27 @@ describe("address follow-up temporal regressions", () => {
expect(result).not.toBeNull();
expect(result?.intent.intent).toBe("receivables_confirmed_as_of_date");
expect(result?.filters.extracted_filters.as_of_date).toBe("2026-04-16");
expect(result?.filters.extracted_filters.counterparty).toBeUndefined();
});
it("does not carry self-scope pronouns as counterparty anchors for short debt mirror follow-up", () => {
const result = runAddressDecomposeStage("а нам?", {
previous_intent: "payables_confirmed_as_of_date",
target_intent: "receivables_confirmed_as_of_date",
previous_filters: {
as_of_date: "2026-05-12",
organization: "ООО Альтернатива Плюс",
counterparty: "нас"
},
previous_anchor_type: "counterparty",
previous_anchor_value: "нас"
});
expect(result).not.toBeNull();
expect(result?.intent.intent).toBe("receivables_confirmed_as_of_date");
expect(result?.filters.extracted_filters.as_of_date).toBe("2026-05-12");
expect(result?.filters.extracted_filters.organization).toBe("ООО Альтернатива Плюс");
expect(result?.filters.extracted_filters.counterparty).toBeUndefined();
});
it("keeps same-date inventory pivot anchored to the previous VAT date", () => {
@@ -56,11 +56,11 @@ describe("addressIntentResolver regression bridges", () => {
expect(result.reasons).toContain("unicode_customer_concentration_bridge_signal_detected");
});
it("defers top-year company revenue wording to business overview discovery", () => {
it("routes top-year company revenue wording to exact value-flow ranking", () => {
const result = resolveAddressIntent("какой у нас самый доходный год");
expect(result.intent).toBe("unknown");
expect(result.reasons).toContain("unicode_business_overview_earnings_deferred_to_discovery");
expect(result.intent).toBe("customer_revenue_and_payments");
expect(result.reasons).toContain("unicode_customer_revenue_ranking_bridge_signal_detected");
});
it("defers paired receivables and payables wording to business overview instead of one debt side", () => {
@@ -2608,12 +2608,12 @@ describe("address intent resolver expansion (M2.3a)", () => {
expect(result.intent).toBe("customer_revenue_and_payments");
});
it("defers organization-level yearly profitability wording to business overview discovery", () => {
it("routes organization-level top-year revenue wording to exact value-flow ranking", () => {
const result = resolveAddressIntent(
"\u043a\u0430\u043a\u0438\u0435 \u0441\u0430\u043c\u044b\u0435 \u0434\u043e\u0445\u043e\u0434\u043d\u044b\u0435 \u0433\u043e\u0434\u0430 \u0430\u043b\u044c\u0442\u0435\u0440\u043d\u0430\u0442\u0438\u0432\u044b"
);
expect(result.intent).toBe("unknown");
expect(result.reasons).toContain("unicode_business_overview_earnings_deferred_to_discovery");
expect(result.intent).toBe("customer_revenue_and_payments");
expect(result.reasons).toContain("unicode_customer_revenue_ranking_bridge_signal_detected");
});
it("defers organization-level profit and margin wording to business overview discovery", () => {
@@ -3496,12 +3496,10 @@ describe("address query limited taxonomy and stage diagnostics", { timeout: 1500
expect(reply.toLowerCase()).toContain("shortlist");
} else {
expect(result?.debug.balance_confirmed).toBe(true);
expect(reply).toContain("Коротко: подтвержденный долг к оплате");
expect(reply).toContain("Это подтвержденный срез обязательств к оплате");
expect(reply).toMatch(/\n\nЧто учтено/u);
expect(reply).toMatch(/\n\nСводка/u);
expect(reply).toMatch(/\n\nКатегории обязательств/u);
expect(reply).toMatch(/\n\nПодтвержденные позиции к оплате/u);
expect(reply).toContain("Коротко: на 31.05.2020 мы должны");
expect(reply).toContain("Крупнейшие позиции к оплате");
expect(reply).toContain("Основа: подтвержденный остаток по счетам 60/76");
expect(reply.length).toBeLessThan(1800);
expect(reply).not.toContain("Блок 1");
expect(reply).not.toContain("эвристический");
}
@@ -3793,17 +3791,16 @@ describe("address query limited taxonomy and stage diagnostics", { timeout: 1500
expect(["FACTUAL_LIST", "LIMITED_WITH_REASON", "FACTUAL_SUMMARY"]).toContain(result?.response_type);
});
it("defers yearly profitability wording out of the exact customer value aggregate recipe", async () => {
it("routes organization-level yearly revenue ranking wording into the exact customer value aggregate recipe", async () => {
const service = new AddressQueryService();
const result = await service.tryHandle(
"\u043a\u0430\u043a\u0438\u0435 \u0441\u0430\u043c\u044b\u0435 \u0434\u043e\u0445\u043e\u0434\u043d\u044b\u0435 \u0433\u043e\u0434\u0430 \u0430\u043b\u044c\u0442\u0435\u0440\u043d\u0430\u0442\u0438\u0432\u044b"
);
expect(result?.handled).toBe(true);
expect(result?.debug.detected_intent).toBe("unknown");
expect(result?.debug.selected_recipe).toBeNull();
expect(result?.debug.limited_reason_category).toBe("unsupported");
expect(result?.debug.mcp_call_status).toBe("skipped");
expect(result?.response_type).toBe("LIMITED_WITH_REASON");
expect(result?.debug.detected_intent).toBe("customer_revenue_and_payments");
expect(result?.debug.selected_recipe).toBe("address_customer_revenue_and_payments_v1");
expect(result?.debug.mcp_call_status).not.toBe("skipped");
expect(["FACTUAL_LIST", "LIMITED_WITH_REASON", "FACTUAL_SUMMARY"]).toContain(result?.response_type);
});
it("routes typo highest-check wording into customer value aggregate recipe", async () => {
@@ -53,6 +53,7 @@ describe("receivables confirmed as-of route", () => {
const plan = buildAddressRecipePlan(selected.selected_recipe!, filters);
expect(plan.query).toContain("РегистрБухгалтерии.Хозрасчетный.Остатки");
expect(plan.query).toContain("СуммаРазвернутыйОстатокДт");
expect(plan.query).toContain("СуммаРазвернутыйОстатокКт");
expect(plan.query).toContain("Остатки.Счет");
expect(plan.query).toContain("62");
expect(plan.query).toContain("76");
@@ -1,6 +1,7 @@
import { describe, expect, it } from "vitest";
import { composeCounterpartyAnalyticsReply } from "../src/services/address_runtime/counterpartyAnalyticsReplyBuilders";
import { composeFactualReply } from "../src/services/address_runtime/composeStage";
import { composeInventoryReply } from "../src/services/address_runtime/inventoryReplyBuilders";
describe("address reply builders regressions", () => {
@@ -51,6 +52,88 @@ describe("address reply builders regressions", () => {
expect(result?.text.split("\n")[0]).toContain("Чапурнов");
});
it("keeps canonical single-best customer wording compact instead of returning the default top list", () => {
const result = composeCounterpartyAnalyticsReply(
"customer_revenue_and_payments",
[
{
counterparty: "Крупный клиент",
amount: 250000,
period: "2020-03-31",
registrator: "Поступление 1"
} as any,
{
counterparty: "Малый клиент",
amount: 100000,
period: "2020-04-30",
registrator: "Поступление 2"
} as any
],
{
userMessage: "определить самого доходного клиента за весь период"
},
{
formatPercent: () => null,
formatDateRu: (value: string) => value,
formatMoneyRub: (value: number) => `${value} руб.`,
extractYearFromIso: (value: string | null) => (value ? Number(value.slice(0, 4)) : null),
detectCounterpartyProfileFocus: () => "full_profile",
detectCounterpartyLifecycleFocus: () => "active_customers_all_time",
hasCounterpartyLifecycleLongevityQuestion: () => false,
hasCounterpartyActivityAgeQuestion: () => false,
detectRankingLimit: () => 20,
detectValueRankingFocus: () => "top_by_total",
detectContractValueFocus: () => "top_by_turnover",
detectMinOpsForAvgCheck: () => 1,
extractRequestedYearFromQuestion: () => null,
extractCounterpartyName: (row: any) => row.counterparty,
extractContractName: () => null,
counterpartyLookupMatches: () => false,
toUtcDayTimestamp: () => null,
formatAgeYearsMonthsDays: () => "0 дней",
normalizeQuestionText: (value: string | null | undefined) => String(value ?? "").toLowerCase()
}
);
expect(result?.text.split("\n")[0]).toContain("Крупный клиент");
expect(result?.text).not.toContain("2. Малый клиент");
});
it("keeps direct receivables snapshot answers compact", () => {
const result = composeFactualReply(
"receivables_confirmed_as_of_date",
[
{
period: "2020-05-10",
registrator: "Поступление 1",
account_dt: "62.01",
account_kt: "90.01",
amount: 100000,
analytics: ["Клиент А"],
counterparty: "Клиент А"
},
{
period: "2020-05-11",
registrator: "Поступление 2",
account_dt: "62.01",
account_kt: "90.01",
amount: 50000,
analytics: ["Клиент Б"],
counterparty: "Клиент Б"
}
],
{
userMessage: "кто нам должен денег на май 2020",
asOfDate: "2020-05-31"
}
);
expect(result.text.split("\n")[0]).toContain("Коротко:");
expect(result.text.split("\n")[0]).toContain("Клиент А");
expect(result.text.length).toBeLessThan(1200);
expect(result.text).toContain("Крупнейшие позиции к получению");
});
it("does not overclaim a comparative top customer ranking when only one candidate is present", () => {
const result = composeCounterpartyAnalyticsReply(
"customer_revenue_and_payments",
@@ -190,4 +273,174 @@ describe("address reply builders regressions", () => {
expect(firstLine).toContain("К самым старым закупкам");
expect(firstLine).not.toContain("2026-04-18");
});
it("excludes mirrored 76 settlements from clean payables", () => {
const result = composeFactualReply(
"payables_confirmed_as_of_date",
[
{
period: "2026-05-12",
registrator: "",
account_dt: "51",
account_kt: "76.09",
amount: 3677454.14,
analytics: ["Комитет государственных услуг г. Москвы", "ООО \\Альтернатива Плюс\\", "Финансовое обеспечение заявки"],
counterparty: "Комитет государственных услуг г. Москвы",
organization: "ООО \\Альтернатива Плюс\\"
},
{
period: "2026-05-12",
registrator: "Списание с расчетного счета 1",
account_dt: "76.09",
account_kt: "51",
amount: 1000000,
analytics: ["Комитет государственных услуг г. Москвы", "ООО \\Альтернатива Плюс\\", "Финансовое обеспечение заявки"],
counterparty: "Комитет государственных услуг г. Москвы",
organization: "ООО \\Альтернатива Плюс\\"
},
{
period: "2026-05-12",
registrator: "Списание с расчетного счета 2",
account_dt: "76.09",
account_kt: "51",
amount: 2677454.14,
analytics: ["Комитет государственных услуг г. Москвы", "ООО \\Альтернатива Плюс\\", "Финансовое обеспечение заявки"],
counterparty: "Комитет государственных услуг г. Москвы",
organization: "ООО \\Альтернатива Плюс\\"
},
{
period: "2026-05-12",
registrator: "Поступление товаров 1",
account_dt: "41.01",
account_kt: "60.01",
amount: 7271.2,
analytics: ["Авант мебель"],
counterparty: "Авант мебель",
organization: "ООО Альтернатива Плюс"
}
],
{
userMessage: "мы должны кому-то денег на сегодня?",
asOfDate: "2026-05-12"
}
);
const firstLine = result.text.split("\n")[0] ?? "";
expect(firstLine).toContain("7.271,20");
expect(firstLine).toContain("Авант мебель");
expect(firstLine).not.toContain("3.677.454,14");
expect(result.text).toContain("встречных остатков");
expect(result.text).toContain("Комитет государственных услуг г. Москвы");
expect(result.text).toContain("Финансовое обеспечение заявки");
expect(result.text).not.toContain("договор/аналитика: ООО \\Альтернатива Плюс\\");
});
it("excludes mirrored 76 settlements from clean receivables", () => {
const result = composeFactualReply(
"receivables_confirmed_as_of_date",
[
{
period: "2026-05-12",
registrator: "Реализация 1",
account_dt: "62.01",
account_kt: "90.01",
amount: 9612904.9,
analytics: ["Департамент капитального ремонта города Москвы."],
counterparty: "Департамент капитального ремонта города Москвы.",
organization: "ООО Альтернатива Плюс"
},
{
period: "2026-05-12",
registrator: "",
account_dt: "51",
account_kt: "76.09",
amount: 3677454.14,
analytics: ["Комитет государственных услуг г. Москвы", "Финансовое обеспечение заявки"],
counterparty: "Комитет государственных услуг г. Москвы",
organization: "ООО Альтернатива Плюс"
},
{
period: "2026-05-12",
registrator: "Списание с расчетного счета 1",
account_dt: "76.09",
account_kt: "51",
amount: 3677454.14,
analytics: ["Комитет государственных услуг г. Москвы", "Финансовое обеспечение заявки"],
counterparty: "Комитет государственных услуг г. Москвы",
organization: "ООО Альтернатива Плюс"
}
],
{
userMessage: "а нам кто должен на сегодня?",
asOfDate: "2026-05-12"
}
);
const firstLine = result.text.split("\n")[0] ?? "";
expect(firstLine).toContain("9.612.904,90");
expect(firstLine).toContain("Департамент капитального ремонта города Москвы.");
expect(firstLine).not.toContain("13.290.359,04");
expect(result.text).toContain("встречных остатков");
expect(result.text).toContain("Комитет государственных услуг г. Москвы");
});
it("keeps bank-named balance counterparties as counterparties, not settlement analytics", () => {
const result = composeFactualReply(
"receivables_confirmed_as_of_date",
[
{
period: "2017-05-31",
registrator: "Остатки на дату",
account_dt: "76.09",
account_kt: null,
amount: 39079.12,
analytics: ["Сбербанк-АСТ, ЗАО", "Финансовое обеспечение заявки"],
organization: "ООО \\Альтернатива Плюс\\"
}
],
{
userMessage: "кто нам должен денег на май 2017?",
asOfDate: "2017-05-31"
}
);
const firstLine = result.text.split("\n")[0] ?? "";
expect(firstLine).toContain("Сбербанк-АСТ, ЗАО");
expect(firstLine).not.toContain("Финансовое обеспечение заявки");
});
it("keeps different 76 contracts separated instead of netting by counterparty only", () => {
const result = composeFactualReply(
"payables_confirmed_as_of_date",
[
{
period: "2026-05-12",
registrator: "",
account_dt: "51",
account_kt: "76.09",
amount: 1000,
analytics: ["Комитет государственных услуг г. Москвы", "Договор А"],
counterparty: "Комитет государственных услуг г. Москвы",
organization: "ООО Альтернатива Плюс"
},
{
period: "2026-05-12",
registrator: "Списание с расчетного счета 1",
account_dt: "76.09",
account_kt: "51",
amount: 1000,
analytics: ["Комитет государственных услуг г. Москвы", "Договор Б"],
counterparty: "Комитет государственных услуг г. Москвы",
organization: "ООО Альтернатива Плюс"
}
],
{
userMessage: "мы должны кому-то денег на сегодня?",
asOfDate: "2026-05-12"
}
);
expect(result.text.split("\n")[0]).toContain("1.000,00");
expect(result.text).not.toContain("встречных остатков");
});
});
@@ -362,12 +362,14 @@ describe("assistant MCP discovery answer adapter", () => {
expect(draft.next_step_line).toContain("прибыль/маржу");
expect(draft.must_not_claim).toContain("Do not present business overview cash-flow spread as profit or margin.");
expect(draft.must_not_claim).toContain("Do not present business overview yearly operating-flow breakdown as profit, financial result, or a complete annual P&L.");
expect(draft.must_not_claim).toContain("Do not present business overview supplier concentration as vendor-risk audit, procurement quality, or full expense structure.");
expect(draft.must_not_claim).toContain("Do not present reviewed procurement concentration as supplier reliability, delivery quality, payment-purpose classification, or full expense structure.");
expect(draft.must_not_claim).toContain("Do not present business overview document/account-section activity profile as process quality, accounting correctness, or completeness of all 1C activity.");
expect(draft.must_not_claim).toContain("Do not present business overview counterparty or contract profile as CRM quality, counterparty due diligence, contract-risk audit, or legal completeness.");
expect(draft.must_not_claim).toContain("Do not present business overview missing proof families as checked, executed, or confirmed routes.");
expect(draft.reason_codes).toContain("answer_contains_business_overview");
expect(draft.reason_codes).toContain("answer_contains_business_overview_supplier_concentration");
expect(draft.reason_codes).toContain("answer_contains_business_overview_vendor_procurement_quality");
expect(draft.reason_codes).toContain("answer_contains_business_overview_vendor_procurement_quality_reviewed_procurement_concentration");
expect(draft.reason_codes).toContain("answer_contains_business_overview_yearly_operating_breakdown");
expect(draft.reason_codes).toContain("answer_contains_business_overview_document_activity_profile");
expect(draft.reason_codes).toContain("answer_contains_business_overview_counterparty_profile");
@@ -258,20 +258,36 @@ describe("assistant MCP discovery pilot executor", () => {
unused_contracts: 372,
used_contract_share_pct: 28.46
});
expect(result.derived_business_overview?.vendor_procurement_quality).toMatchObject({
rows_with_amount: 2,
total_outgoing_amount: 200000,
top_outgoing_share_pct: 75,
top_non_financial_supplier_share_pct: 75,
financial_institution_leads_outgoing_cash: false,
supplier_only_count: 71,
mixed_role_count: 23,
used_contracts: 148,
total_contracts: 520,
used_contract_share_pct: 28.46,
evidence_status: "reviewed_procurement_concentration",
inference_basis: "supplier_payout_concentration_counterparty_contract_profile_confirmed_1c_rows"
});
expect(result.derived_business_overview?.vendor_procurement_quality?.top_outgoing_counterparty).toMatchObject({
axis_value: "Поставщик А",
total_amount: 150000
});
expect(result.derived_business_overview?.missing_proof_families).toEqual(
expect.arrayContaining([
expect.objectContaining({
family: "accounting_profit_margin",
current_status: "reviewed_route_not_wired",
current_supported_evidence: null
}),
expect.objectContaining({
family: "vendor_risk_procurement_quality",
current_status: "proxy_only_currently",
current_supported_evidence: "supplier_concentration_proxy_from_confirmed_outgoing_payment_rows"
})
])
);
expect(result.derived_business_overview?.missing_proof_families.map((item) => item.family)).not.toContain(
"vendor_risk_procurement_quality"
);
expect(result.evidence.confirmed_facts.join("\n")).toContain("В 1С подтверждены входящие поступления");
expect(result.evidence.confirmed_facts.join("\n")).toContain("Самый крупный подтвержденный поставщик");
expect(result.evidence.confirmed_facts.join("\n")).toContain("Годовая раскладка операционного денежного потока");
@@ -289,6 +305,8 @@ describe("assistant MCP discovery pilot executor", () => {
expect(result.reason_codes).toContain("pilot_derived_business_overview_document_activity_profile_from_confirmed_rows");
expect(result.reason_codes).toContain("pilot_derived_business_overview_counterparty_profile_from_confirmed_rows");
expect(result.reason_codes).toContain("pilot_derived_business_overview_contract_usage_profile_from_confirmed_rows");
expect(result.reason_codes).toContain("pilot_derived_business_overview_vendor_procurement_quality_from_confirmed_rows");
expect(result.reason_codes).toContain("pilot_derived_business_overview_vendor_procurement_quality_reviewed_procurement_concentration");
expect(result.reason_codes).toContain("pilot_business_overview_missing_proof_families_recorded");
expect(deps.executeAddressMcpQuery).toHaveBeenCalledTimes(6);
});
@@ -904,6 +904,40 @@ describe("assistant MCP discovery planner", () => {
expect(result.selected_chain_id).not.toBe("entity_resolution");
});
it("keeps ranked value-flow when subject candidates only scope the ranking question", () => {
const result = planAssistantMcpDiscovery({
dataNeedGraph: {
schema_version: "assistant_data_need_graph_v1",
policy_owner: "assistantMcpDiscoveryDataNeedGraph",
subject_candidates: ["Альтернатива Плюс"],
business_fact_family: "value_flow",
action_family: "turnover",
aggregation_need: null,
time_scope_need: "explicit_period",
comparison_need: null,
ranking_need: "top_desc",
proof_expectation: "coverage_checked_fact",
clarification_gaps: [],
decomposition_candidates: ["collect_scoped_movements", "aggregate_ranked_axis_values", "probe_coverage"],
forbidden_overclaim_flags: ["no_raw_model_claims", "no_unchecked_fact_totals"],
reason_codes: ["data_need_graph_built", "data_need_graph_ranking_top_desc"]
},
turnMeaning: {
asked_domain_family: "counterparty_value",
asked_action_family: "turnover",
explicit_date_scope: "all_time",
explicit_organization_scope: "Альтернатива Плюс"
}
});
expect(result.planner_status).toBe("ready_for_execution");
expect(result.selected_chain_id).toBe("value_flow_ranking");
expect(result.catalog_chain_template_matches[0]).toBe("value_flow_ranking");
expect(result.catalog_chain_template_alignment.selected_chain_matches_top).toBe(true);
expect(result.reason_codes).toContain("planner_selected_top_ranked_value_flow_from_data_need_graph");
expect(result.reason_codes).toContain("planner_kept_ranking_over_subject_scope_candidates");
});
it("keeps ranked value-flow ready for execution once checked period and organization are known", () => {
const result = planAssistantMcpDiscovery({
dataNeedGraph: {
@@ -215,6 +215,95 @@ describe("assistant MCP discovery response candidate", () => {
expect(candidate.reply_text).not.toContain("операционное нетто");
});
it("uses reviewed procurement-concentration evidence for vendor-risk boundary answers", () => {
const candidate = buildAssistantMcpDiscoveryResponseCandidate(
entryPoint({
turn_input: {
adapter_status: "ready",
turn_meaning_ref: {
asked_domain_family: "business_overview",
asked_action_family: "vendor_risk_procurement_boundary",
unsupported_but_understood_family: "vendor_risk_procurement_boundary"
},
data_need_graph: {
business_fact_family: "business_overview",
ranking_need: null,
reason_codes: ["data_need_graph_family_business_overview"]
}
},
bridge: {
bridge_status: "answer_draft_ready",
user_facing_response_allowed: true,
business_fact_answer_allowed: true,
requires_user_clarification: false,
pilot: {
pilot_scope: "business_overview_route_template_v1",
derived_business_overview: {
period_scope: "2020",
outgoing_supplier_payout: {
total_amount_human_ru: "1 000 000 руб."
},
top_suppliers: [
{
axis_value: "СБЕРБАНК, ПАО",
total_amount_human_ru: "700 000 руб."
}
],
vendor_procurement_quality: {
period_scope: "2020",
rows_with_amount: 2,
total_outgoing_amount: 1000000,
total_outgoing_amount_human_ru: "1 000 000 руб.",
top_outgoing_counterparty: {
axis_value: "СБЕРБАНК, ПАО",
total_amount: 700000,
total_amount_human_ru: "700 000 руб.",
rows_with_amount: 1,
share_pct: 70,
is_likely_financial_institution: true
},
top_outgoing_share_pct: 70,
top_non_financial_supplier: {
axis_value: "Поставщик А",
total_amount: 300000,
total_amount_human_ru: "300 000 руб.",
rows_with_amount: 1,
share_pct: 30,
is_likely_financial_institution: false
},
top_non_financial_supplier_share_pct: 30,
financial_institution_leads_outgoing_cash: true,
supplier_only_count: null,
mixed_role_count: null,
used_contracts: null,
total_contracts: null,
used_contract_share_pct: null,
evidence_status: "financial_institution_leads_outgoing_cash",
inference_basis: "supplier_payout_concentration_counterparty_contract_profile_confirmed_1c_rows"
}
}
},
answer_draft: {
answer_mode: "confirmed_with_bounded_inference",
headline: "Vendor route.",
confirmed_lines: [],
inference_lines: [],
unknown_lines: [],
limitation_lines: [],
next_step_line: null
}
}
})
);
expect(candidate.reply_text).toContain("procurement-concentration route");
expect(candidate.reply_text).toContain("банк/финансовая организация");
expect(candidate.reply_text).toContain("Поставщик А");
expect(candidate.reply_text).toContain("надежность поставщика");
expect(candidate.reply_text).not.toContain("outgoing cash concentration proxy");
expect(candidate.reply_text).not.toContain("business_overview_route_template_v1");
});
it("uses a compact direct answer for business-overview top year questions", () => {
const candidate = buildAssistantMcpDiscoveryResponseCandidate(
entryPoint({
@@ -367,6 +367,83 @@ describe("assistant MCP discovery response policy", () => {
expect(result.reason_codes).not.toContain("mcp_discovery_response_policy_keep_exact_matched_factual_address_reply");
});
it("keeps exact receivables replies when metadata inspection is only a stale fallback candidate", () => {
const result = applyAssistantMcpDiscoveryResponsePolicy({
currentReply:
"Коротко: подтвержденная дебиторская задолженность на 31.05.2017 — 1 234 567,89 руб.",
currentReplySource: "address_query_runtime_v1",
currentReplyType: "factual",
addressRuntimeMeta: {
detected_intent: "receivables_confirmed_as_of_date",
selected_recipe: "address_receivables_confirmed_as_of_date_v1",
mcp_call_status: "matched_non_empty",
truth_mode: "confirmed",
capability_binding_status: "bound",
capability_binding_violations: [],
answer_shape_contract: {
reply_type: "factual",
capability_contract_id: "confirmed_receivables_as_of_date"
},
assistant_mcp_discovery_entry_point_v1: entryPoint({
turn_input: {
adapter_status: "ready",
should_run_discovery: true,
data_need_graph: {
business_fact_family: null,
action_family: "confirmed_snapshot",
clarification_gaps: ["subject"],
reason_codes: [
"data_need_graph_built",
"data_need_graph_family_unknown",
"data_need_graph_has_clarification_gaps"
]
},
turn_meaning_ref: {
asked_domain_family: "receivables",
asked_action_family: "confirmed_snapshot",
explicit_date_scope: "2017-05-31",
unsupported_but_understood_family: "profit_margin_boundary",
stale_replay_forbidden: true
},
reason_codes: [
"mcp_discovery_seeded_from_followup_context",
"mcp_discovery_resumed_from_saved_loop_state",
"mcp_discovery_data_need_graph_built"
]
},
bridge: {
bridge_status: "answer_draft_ready",
user_facing_response_allowed: true,
business_fact_answer_allowed: true,
requires_user_clarification: false,
pilot: {
pilot_scope: "metadata_inspection_v1"
},
answer_draft: {
answer_mode: "confirmed_with_bounded_inference",
headline: "Metadata surface confirmed.",
confirmed_lines: ["Available metadata object sets: Document, AccumulationRegister"],
inference_lines: [],
unknown_lines: ["Detailed metadata fields were not returned by this probe."],
limitation_lines: [],
next_step_line: null
}
}
})
}
});
expect(result.applied).toBe(false);
expect(result.decision).toBe("keep_current_reply");
expect(result.reply_text).toContain("дебиторская задолженность");
expect(result.reason_codes).toContain("mcp_discovery_response_policy_keep_exact_matched_factual_address_reply");
expect(result.reason_codes).toContain(
"mcp_discovery_response_policy_keep_exact_address_reply_over_stale_metadata_discovery"
);
expect(result.reason_codes).not.toContain("mcp_discovery_response_policy_metadata_candidate_priority");
expect(result.reason_codes).not.toContain("mcp_discovery_response_policy_candidate_applied");
});
it("keeps aligned factual address lane answers when the exact lane already matched the same semantic intent", () => {
const result = applyAssistantMcpDiscoveryResponsePolicy({
currentReply: "ИП Калинин Н.М. | сумма: 216600 | операций: 2",
@@ -582,6 +659,71 @@ describe("assistant MCP discovery response policy", () => {
expect(result.reason_codes).not.toContain("mcp_discovery_response_policy_keep_full_confirmed_factual_address_reply");
});
it("keeps exact top-year value-flow replies over business-overview direct-money clarification", () => {
const result = applyAssistantMcpDiscoveryResponsePolicy({
currentReply:
"Самый доходный год по подтвержденным поступлениям: 2020 (15 744 052,48 руб. по 20 операциям). Это денежный поток, а не чистая прибыль.",
currentReplySource: "address_query_runtime_v1",
currentReplyType: "factual",
addressRuntimeMeta: {
detected_intent: "customer_revenue_and_payments",
selected_recipe: "address_customer_revenue_and_payments_v1",
mcp_call_status: "matched_non_empty",
truth_mode: "confirmed",
capability_binding_status: "bound",
capability_binding_violations: [],
assistant_mcp_discovery_entry_point_v1: entryPoint({
turn_input: {
adapter_status: "ready",
should_run_discovery: true,
data_need_graph: {
business_fact_family: "business_overview",
action_family: "profit_margin_boundary",
ranking_need: "top_desc",
clarification_gaps: ["organization"],
reason_codes: [
"data_need_graph_built",
"data_need_graph_family_business_overview",
"data_need_graph_ranking_top_desc",
"data_need_graph_business_overview_direct_money_answer",
"data_need_graph_open_scope_total_needs_organization"
]
},
turn_meaning_ref: {
asked_domain_family: "business_overview",
asked_action_family: "profit_margin_boundary",
unsupported_but_understood_family: "profit_margin_boundary",
stale_replay_forbidden: true
}
},
bridge: {
bridge_status: "answer_draft_ready",
user_facing_response_allowed: true,
business_fact_answer_allowed: false,
requires_user_clarification: true,
answer_draft: {
answer_mode: "needs_clarification",
headline: "Нужно уточнить организацию.",
confirmed_lines: [],
inference_lines: [],
unknown_lines: ["Без организации бизнес-обзор не запускаю."],
limitation_lines: [],
next_step_line: "Уточните организацию."
}
}
})
}
});
expect(result.applied).toBe(false);
expect(result.decision).toBe("keep_current_reply");
expect(result.reply_text).toContain("Самый доходный год");
expect(result.reason_codes).toContain(
"mcp_discovery_response_policy_keep_exact_value_flow_reply_over_business_overview_direct_money_clarification"
);
expect(result.reason_codes).not.toContain("mcp_discovery_response_policy_candidate_applied");
});
it("overrides an exact ranking-shaped address reply when bounded open-scope ranking already has organization and period", () => {
const result = applyAssistantMcpDiscoveryResponsePolicy({
currentReply:
@@ -757,6 +757,107 @@ describe("assistant MCP discovery runtime bridge", () => {
expect(result.reason_codes).toContain("answer_contains_business_overview_debt_due_date_aging_no_payment_terms_configured");
});
it("promotes vendor-risk boundary through reviewed procurement concentration evidence", async () => {
const deps = buildSequentialDeps([
{ rows: [] },
{
rows: [
{ Period: "2020-01-20T00:00:00", Amount: 700000, Counterparty: "СБЕРБАНК, ПАО" },
{ Period: "2020-02-20T00:00:00", Amount: 300000, Counterparty: "Поставщик А" }
]
},
{ rows: [] },
{ rows: [] },
{
rows: [
{ Period: "2020-01-01T00:00:00", Registrator: "CP_TOTAL", Amount: 12 },
{ Period: "2020-01-01T00:00:00", Registrator: "CP_SUPPLIER_ACTIVE", Amount: 5 },
{ Period: "2020-01-01T00:00:00", Registrator: "CP_MIXED_ACTIVE", Amount: 2 },
{ Period: "2020-01-01T00:00:00", Registrator: "CP_ACTIVE_UNION", Amount: 7 }
]
},
{
rows: [
{ Period: "2020-01-01T00:00:00", Registrator: "CT_TOTAL", Amount: 11 },
{ Period: "2020-01-01T00:00:00", Registrator: "CT_USED", Amount: 4 }
]
}
]);
const result = await runAssistantMcpDiscoveryRuntimeBridge({
dataNeedGraph: {
schema_version: "assistant_data_need_graph_v1",
policy_owner: "assistantMcpDiscoveryDataNeedGraph",
subject_candidates: [],
business_fact_family: "business_overview",
action_family: "vendor_risk_procurement_boundary",
aggregation_need: null,
time_scope_need: "all_time_scope",
comparison_need: null,
ranking_need: null,
proof_expectation: "vendor_risk_procurement_quality",
clarification_gaps: [],
decomposition_candidates: [
"collect_scoped_movements",
"aggregate_checked_amounts",
"aggregate_ranked_axis_values",
"fetch_supporting_documents",
"probe_coverage",
"explain_evidence_basis"
],
forbidden_overclaim_flags: [
"no_raw_model_claims",
"no_unchecked_vendor_reliability_claim"
],
reason_codes: ["data_need_graph_built", "data_need_graph_family_business_overview"]
},
turnMeaning: {
asked_domain_family: "business_overview",
asked_action_family: "vendor_risk_procurement_boundary",
explicit_organization_scope: "OOO Alternative Plus"
},
deps
});
const overview = result.pilot.derived_business_overview;
const missingFamilies = overview?.missing_proof_families.map((item) => item.family) ?? [];
const userFacing = [
result.answer_draft.headline,
...result.answer_draft.confirmed_lines,
...result.answer_draft.inference_lines,
...result.answer_draft.unknown_lines,
...result.answer_draft.limitation_lines,
result.answer_draft.next_step_line ?? ""
].join("\n");
expect(result.bridge_status).toBe("answer_draft_ready");
expect(result.business_fact_answer_allowed).toBe(true);
expect(result.route_candidate).toMatchObject({
candidate_status: "ready_for_reviewed_execution",
selected_chain_id: "business_overview",
business_fact_family: "business_overview",
action_family: "vendor_risk_procurement_boundary",
executable_now: true
});
expect(overview?.vendor_procurement_quality).toMatchObject({
total_outgoing_amount: 1000000,
top_outgoing_share_pct: 70,
top_non_financial_supplier_share_pct: 30,
financial_institution_leads_outgoing_cash: true,
supplier_only_count: 3,
mixed_role_count: 2,
used_contracts: 4,
total_contracts: 11,
evidence_status: "financial_institution_leads_outgoing_cash"
});
expect(missingFamilies).not.toContain("vendor_risk_procurement_quality");
expect(userFacing).toContain("procurement-concentration route");
expect(userFacing).toContain("банк/финансовая организация");
expect(userFacing).toContain("Поставщик А");
expect(userFacing).toContain("Надежность поставщиков");
expect(userFacing).not.toContain("outgoing cash concentration proxy");
expect(result.reason_codes).toContain("runtime_bridge_route_candidate_ready_for_reviewed_execution");
expect(result.reason_codes).toContain("answer_contains_business_overview_vendor_procurement_quality_financial_institution_leads_outgoing_cash");
});
it("bridges selected-item inventory provenance templates through exact document evidence", async () => {
const deps = buildDeps([
{