Зафиксировать семантическую целостность VAT, debt mirror и trace-ответов
This commit is contained in:
@@ -119,6 +119,51 @@ describe("address follow-up temporal regressions", () => {
|
||||
expect(result?.filters.extracted_filters.counterparty).toBeUndefined();
|
||||
});
|
||||
|
||||
it("suppresses stale counterparty filters from organization-scoped 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-17",
|
||||
organization: "ООО Альтернатива Плюс",
|
||||
counterparty: "нас самый доходный клиент"
|
||||
},
|
||||
previous_anchor_type: "organization",
|
||||
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-17");
|
||||
expect(result?.filters.extracted_filters.organization).toBe("ООО Альтернатива Плюс");
|
||||
expect(result?.filters.extracted_filters.counterparty).toBeUndefined();
|
||||
expect(result?.baseReasons).toContain("counterparty_carryover_suppressed_for_short_debt_mirror");
|
||||
expect(result?.baseReasons).not.toContain("counterparty_from_followup_context");
|
||||
});
|
||||
|
||||
it("keeps resolved counterparty focus 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-17",
|
||||
organization: "ООО Альтернатива Плюс",
|
||||
counterparty: "Группа СВК"
|
||||
},
|
||||
previous_anchor_type: "counterparty",
|
||||
previous_anchor_value: "Группа СВК",
|
||||
resolved_counterparty_from_display: true
|
||||
});
|
||||
|
||||
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-17");
|
||||
expect(result?.filters.extracted_filters.organization).toBe("ООО Альтернатива Плюс");
|
||||
expect(result?.filters.extracted_filters.counterparty).toBe("Группа СВК");
|
||||
expect(result?.baseReasons).toContain("counterparty_from_followup_context");
|
||||
expect(result?.baseReasons).not.toContain("counterparty_carryover_suppressed_for_short_debt_mirror");
|
||||
});
|
||||
|
||||
it("keeps same-date inventory pivot anchored to the previous VAT date", () => {
|
||||
const result = runAddressDecomposeStage("какие остатки по складу на эту же дату", {
|
||||
previous_intent: "vat_payable_confirmed_as_of_date",
|
||||
|
||||
@@ -703,6 +703,7 @@ describe("inventory selected-object follow-up", () => {
|
||||
expect(result?.debug.extracted_filters?.as_of_date).toBe("2020-05-31");
|
||||
expect(result?.debug.reasons).toContain("inventory_selected_object_sale_trace_signal_detected");
|
||||
expect(String(result?.reply_text ?? "").split("\n")[0]).toContain("ООО \\Ромашка\\");
|
||||
expect(String(result?.reply_text ?? "").split("\n")[0]).toContain("без партионного учета не доказывает");
|
||||
expect(String(result?.reply_text ?? "")).toContain("Документы выбытия");
|
||||
});
|
||||
|
||||
@@ -750,6 +751,8 @@ describe("inventory selected-object follow-up", () => {
|
||||
expect(result?.debug.extracted_filters?.item).toBe("Кромка с клеем 33 альмандин 137 м");
|
||||
expect(result?.debug.extracted_filters?.as_of_date).toBe("2020-03-31");
|
||||
expect(String(result?.reply_text ?? "")).toContain("ООО \\Покупатель\\");
|
||||
expect(String(result?.reply_text ?? "")).not.toContain("покупатель определен");
|
||||
expect(String(result?.reply_text ?? "")).toContain("продажный след по номенклатуре");
|
||||
});
|
||||
|
||||
it("detaches snapshot date from execution query during sale-trace history recovery", async () => {
|
||||
|
||||
@@ -479,6 +479,42 @@ describe("assistantContinuityPolicy organization authority", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("does not carry MCP discovery counterparty from a stale replay-forbidden turn", () => {
|
||||
const filters = resolveAddressDebugCarryoverFilters({
|
||||
detected_intent: "payables_confirmed_as_of_date",
|
||||
extracted_filters: {
|
||||
organization: "Org Alt",
|
||||
as_of_date: "2026-05-17"
|
||||
},
|
||||
mcp_discovery_response_applied: true,
|
||||
assistant_mcp_discovery_entry_point_v1: {
|
||||
entry_status: "bridge_executed",
|
||||
turn_input: {
|
||||
turn_meaning_ref: {
|
||||
explicit_entity_candidates: ["old ranking counterparty"],
|
||||
metadata_scope_hint: "old ranking counterparty",
|
||||
stale_replay_forbidden: true
|
||||
}
|
||||
},
|
||||
bridge: {
|
||||
bridge_status: "answer_draft_ready",
|
||||
business_fact_answer_allowed: true,
|
||||
answer_draft: {
|
||||
answer_mode: "confirmed_with_bounded_inference"
|
||||
},
|
||||
pilot: {
|
||||
pilot_scope: "counterparty_value_flow_query_movements_v1"
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
expect(filters).toEqual({
|
||||
organization: "Org Alt",
|
||||
as_of_date: "2026-05-17"
|
||||
});
|
||||
});
|
||||
|
||||
it("hydrates inventory root-frame state from navigation scope and preserves derived current frame kind", () => {
|
||||
const state = hydrateInventoryRootFrameState(
|
||||
{
|
||||
|
||||
@@ -445,9 +445,12 @@ describe("assistant MCP discovery response candidate", () => {
|
||||
expect(candidate.reply_text).toContain("нельзя автоматически читать как обычного клиента или поставщика");
|
||||
expect(candidate.reply_text).toContain("не полный бухгалтерский рейтинг доходности");
|
||||
expect(candidate.reply_text).toContain("не как чистую бухгалтерскую прибыль");
|
||||
expect(candidate.reply_text).toContain("Годовое операционное нетто в широком срезе не ранжирую");
|
||||
expect(candidate.reply_text).toContain("проверка достигла лимита строк");
|
||||
expect(candidate.reply_text).toContain("выбрать конкретный год или квартал для дозапроса");
|
||||
expect(candidate.reply_text).toContain("без выдачи непроверенного итога");
|
||||
expect(candidate.reply_text).not.toContain("По расчетному операционному нетто лучший год");
|
||||
expect(candidate.reply_text).not.toContain("По годам:");
|
||||
expect(candidate.reply_text).not.toContain("лимит выборки MCP");
|
||||
expect(candidate.reply_text).not.toContain("MCP-срез");
|
||||
expect(candidate.reply_text).not.toContain("Что подтверждено:");
|
||||
|
||||
@@ -657,8 +657,8 @@ describe("assistant MCP discovery runtime bridge", () => {
|
||||
expect(overview?.accounting_financial_result?.net_margin_to_revenue_pct).toBe(-59.41);
|
||||
expect(missingFamilies).not.toContain("accounting_profit_margin");
|
||||
expect(userFacing).toContain("90/91/99");
|
||||
expect(userFacing).toContain("90.01");
|
||||
expect(userFacing).toContain("7 136 815,85");
|
||||
expect(userFacing).not.toContain("90.01");
|
||||
expect(userFacing).not.toContain("operating-flow/trading-margin proxy");
|
||||
expect(result.reason_codes).toContain("pilot_derived_business_overview_accounting_financial_result_from_confirmed_rows");
|
||||
expect(result.reason_codes).toContain("answer_contains_business_overview_accounting_financial_result");
|
||||
@@ -850,7 +850,7 @@ describe("assistant MCP discovery runtime bridge", () => {
|
||||
evidence_status: "financial_institution_leads_outgoing_cash"
|
||||
});
|
||||
expect(missingFamilies).not.toContain("vendor_risk_procurement_quality");
|
||||
expect(userFacing).toContain("procurement-concentration route");
|
||||
expect(userFacing).not.toContain("procurement-concentration route");
|
||||
expect(userFacing).toContain("банк/финансовая организация");
|
||||
expect(userFacing).toContain("Поставщик А");
|
||||
expect(userFacing).toContain("Надежность поставщиков");
|
||||
|
||||
@@ -1861,6 +1861,62 @@ describe("assistantTransitionPolicy", () => {
|
||||
period_to: "2017-05-31"
|
||||
});
|
||||
});
|
||||
|
||||
it("retargets same-period VAT payable follow-up away from current-date payable snapshots", () => {
|
||||
const policy = buildPolicy({
|
||||
findLastAddressAssistantItem: () => ({
|
||||
text: "Receivables as of 2017-05-31 were collected for May.",
|
||||
debug: {
|
||||
detected_intent: "receivables_confirmed_as_of_date",
|
||||
extracted_filters: {
|
||||
organization: 'ООО "Альтернатива Плюс"',
|
||||
as_of_date: "2017-05-31",
|
||||
period_from: "2017-05-01",
|
||||
period_to: "2017-05-31"
|
||||
},
|
||||
anchor_type: "organization",
|
||||
anchor_value_resolved: 'ООО "Альтернатива Плюс"'
|
||||
}
|
||||
}),
|
||||
hasAddressFollowupContextSignal: () => true,
|
||||
hasReferentialPointer: () => true,
|
||||
resolveAddressIntent: () => ({ intent: "unknown" }),
|
||||
resolveAddressIntentFamily: (intent: unknown) => {
|
||||
if (String(intent ?? "").startsWith("receivables_")) return "receivables";
|
||||
if (String(intent ?? "").startsWith("vat_")) return "vat";
|
||||
return null;
|
||||
},
|
||||
resolveAssistantTurnMeaning: () => ({
|
||||
schema_version: "assistant_turn_meaning_v1",
|
||||
asked_domain_family: "vat",
|
||||
asked_action_family: "confirmed_snapshot",
|
||||
explicit_intent_candidate: "vat_payable_confirmed_as_of_date",
|
||||
explicit_entity_candidates: [],
|
||||
intent_override_strength: "explicit_current_turn_intent",
|
||||
stale_replay_forbidden: false
|
||||
})
|
||||
});
|
||||
|
||||
const carryover = policy.resolveAddressFollowupCarryoverContext(
|
||||
"а какой ндс мы должны примерно заплатить за этот период?",
|
||||
[],
|
||||
"Какой НДС мы должны заплатить за текущий период?",
|
||||
{
|
||||
predecomposeContract: {
|
||||
intent: "vat_payable_confirmed_as_of_date"
|
||||
}
|
||||
},
|
||||
null
|
||||
);
|
||||
|
||||
expect(carryover?.followupContext?.previous_intent).toBe("receivables_confirmed_as_of_date");
|
||||
expect(carryover?.followupContext?.target_intent).toBe("vat_liability_confirmed_for_tax_period");
|
||||
expect(carryover?.followupContext?.previous_filters).toMatchObject({
|
||||
organization: 'ООО "Альтернатива Плюс"',
|
||||
period_from: "2017-05-01",
|
||||
period_to: "2017-05-31"
|
||||
});
|
||||
});
|
||||
it("carries metadata-scoped subjectless loop state through follow-up context", () => {
|
||||
const policy = buildPolicy({
|
||||
findLastAddressAssistantItem: () => ({
|
||||
|
||||
Reference in New Issue
Block a user