ГЛОБАЛЬНЫЙ РЕФАКТОРИНГ АРХИТЕКТУРЫ - Архитектурный фундамент: capability-guard, navigation state и трассируемый route-контракт

This commit is contained in:
2026-04-12 14:40:48 +03:00
parent fbd156e58e
commit c10260dedf
22 changed files with 1842 additions and 23 deletions
@@ -0,0 +1,37 @@
import { describe, expect, it } from "vitest";
import {
isCapabilityRouteBlocked,
resolveAddressCapabilityRouteDecision,
resolveShadowRouteIntent
} from "../src/services/addressCapabilityPolicy";
describe("address capability policy", () => {
it("maps confirmed payables intent to compute exact capability", () => {
const decision = resolveAddressCapabilityRouteDecision("payables_confirmed_as_of_date");
expect(decision.capability_id).toBe("confirmed_payables_as_of_date");
expect(decision.capability_layer).toBe("compute");
expect(decision.capability_route_mode).toBe("exact");
expect(decision.capability_route_enabled).toBe(true);
expect(isCapabilityRouteBlocked(decision)).toBe(false);
});
it("maps document drilldown intent to navigation capability", () => {
const decision = resolveAddressCapabilityRouteDecision("list_documents_by_contract");
expect(decision.capability_id).toBe("documents_drilldown");
expect(decision.capability_layer).toBe("navigation");
expect(decision.capability_route_mode).toBe("exact");
expect(decision.capability_route_enabled).toBe(true);
});
it("maps heuristic list intents to heuristic compute route mode", () => {
const decision = resolveAddressCapabilityRouteDecision("list_receivables_counterparties");
expect(decision.capability_id).toBe("receivables_candidates_list");
expect(decision.capability_layer).toBe("compute");
expect(decision.capability_route_mode).toBe("heuristic");
});
it("keeps shadow route disabled by default", () => {
expect(resolveShadowRouteIntent("payables_confirmed_as_of_date", "confirmed_balance")).toBeNull();
expect(resolveShadowRouteIntent("list_payables_counterparties", "confirmed_balance")).toBeNull();
});
});
@@ -0,0 +1,114 @@
import { describe, expect, it } from "vitest";
import {
createEmptyAddressNavigationState,
evolveAddressNavigationStateWithAssistantItem,
normalizeAddressNavigationState
} from "../src/services/addressNavigationState";
describe("address navigation state", () => {
it("creates default empty state", () => {
const state = createEmptyAddressNavigationState("asst-1", "2026-04-12T10:00:00.000Z");
expect(state.schema_version).toBe("address_navigation_state_v1");
expect(state.session_id).toBe("asst-1");
expect(state.result_sets).toEqual([]);
expect(state.navigation_history).toEqual([]);
expect(state.session_context.active_result_set_id).toBeNull();
});
it("captures result_set and focus from address assistant turn", () => {
const base = createEmptyAddressNavigationState("asst-2", "2026-04-12T10:00:00.000Z");
const assistantItem = {
message_id: "msg-a1",
session_id: "asst-2",
role: "assistant",
text: [
"Топ-6 заказчиков по сумме поступлений:",
"1. Группа | сумма: 12093465 | операций: 13",
"4. Гамма-мебель, ООО | сумма: 471000 | операций: 2"
].join("\n"),
reply_type: "factual",
created_at: "2026-04-12T10:00:10.000Z",
trace_id: "address-123",
debug: {
detected_mode: "address_query",
detected_intent: "customer_revenue_and_payments",
selected_recipe: "address_customer_revenue_and_payments_v1",
extracted_filters: {
period_from: "2020-01-01",
period_to: "2020-12-31"
},
anchor_type: "counterparty",
anchor_value_resolved: "Гамма-мебель, ООО",
dialog_continuation_contract_v2: {
decision: "new_topic"
}
}
} as any;
const evolved = evolveAddressNavigationStateWithAssistantItem(base, assistantItem, 2);
expect(evolved.result_sets.length).toBe(1);
expect(evolved.result_sets[0]?.result_set_id).toBe("rs-msg-a1");
expect(evolved.result_sets[0]?.intent).toBe("customer_revenue_and_payments");
expect(evolved.result_sets[0]?.entity_refs.length).toBeGreaterThan(0);
expect(evolved.session_context.active_result_set_id).toBe("rs-msg-a1");
expect(evolved.session_context.active_focus_object?.label).toBe("Гамма-мебель, ООО");
expect(evolved.navigation_history[0]?.action).toBe("open");
});
it("tracks drilldown event for follow-up continuation turn", () => {
const initial = normalizeAddressNavigationState(
{
schema_version: "address_navigation_state_v1",
session_id: "asst-3",
updated_at: "2026-04-12T10:00:00.000Z",
session_context: {
active_result_set_id: "rs-prev",
active_focus_object: null,
last_confirmed_route: "address_customer_revenue_and_payments_v1",
date_scope: {
as_of_date: null,
period_from: "2020-01-01",
period_to: "2020-12-31"
},
organization_scope: null
},
result_sets: [],
navigation_history: []
} as any,
"asst-3"
);
const assistantItem = {
message_id: "msg-a2",
session_id: "asst-3",
role: "assistant",
text: "Собран список договоров по контрагенту Гамма-мебель, ООО.",
reply_type: "factual",
created_at: "2026-04-12T10:02:00.000Z",
trace_id: "address-456",
debug: {
detected_mode: "address_query",
detected_intent: "list_contracts_by_counterparty",
selected_recipe: "address_contracts_by_counterparty_v1",
extracted_filters: {
period_from: "2020-01-01",
period_to: "2020-12-31",
counterparty: "Гамма-мебель, ООО"
},
anchor_type: "counterparty",
anchor_value_resolved: "Гамма-мебель, ООО",
dialog_continuation_contract_v2: {
decision: "continue_previous"
}
}
} as any;
const evolved = evolveAddressNavigationStateWithAssistantItem(initial, assistantItem, 4);
expect(evolved.navigation_history.length).toBe(1);
expect(evolved.navigation_history[0]?.action).toBe("drilldown");
expect(evolved.navigation_history[0]?.source_result_set_id).toBe("rs-prev");
expect(evolved.session_context.active_result_set_id).toBe("rs-msg-a2");
expect(evolved.session_context.active_focus_object?.object_type).toBe("counterparty");
expect(evolved.session_context.date_scope.period_from).toBe("2020-01-01");
expect(evolved.session_context.date_scope.period_to).toBe("2020-12-31");
});
});
@@ -19,6 +19,7 @@ describe("assistant session backward compatibility", () => {
expect(session?.session_id).toBe(sessionId);
expect(Array.isArray(session?.items)).toBe(true);
expect(session?.investigation_state?.schema_version).toBe("investigation_state_v1");
expect(session?.address_navigation_state?.schema_version).toBe("address_navigation_state_v1");
});
it("normalizes malformed legacy sessions with missing items array", () => {
@@ -35,6 +36,7 @@ describe("assistant session backward compatibility", () => {
expect(Array.isArray(ensured.items)).toBe(true);
expect(ensured.items.length).toBe(0);
expect(ensured.investigation_state?.schema_version).toBe("investigation_state_v1");
expect(ensured.address_navigation_state?.schema_version).toBe("address_navigation_state_v1");
});
it("preserves optional stage2 problem_unit_state in session clone flow", () => {
@@ -73,5 +75,6 @@ describe("assistant session backward compatibility", () => {
expect(session).toBeTruthy();
expect(session?.investigation_state?.problem_unit_state?.active_problem_units).toEqual(["pu-1", "pu-2"]);
expect(session?.investigation_state?.problem_unit_state?.focus_problem_types).toEqual(["broken_chain_segment"]);
expect(session?.address_navigation_state?.schema_version).toBe("address_navigation_state_v1");
});
});