ARCH: удерживать mixed metadata ambiguity как явное clarification state

This commit is contained in:
2026-04-22 10:02:01 +03:00
parent c0b3296953
commit 40cf71d118
7 changed files with 168 additions and 12 deletions
@@ -162,6 +162,27 @@ describe("assistant MCP discovery answer adapter", () => {
expect(draft.must_not_claim).toContain("Do not claim rows were checked when mcp_execution_performed=false.");
});
it("asks for an explicit lane choice when mixed metadata ambiguity cannot continue on a neutral follow-up", async () => {
const planner = planAssistantMcpDiscovery({
turnMeaning: {
asked_domain_family: "metadata",
asked_action_family: "resolve_next_lane",
explicit_entity_candidates: ["SVK"],
explicit_date_scope: "2020",
unsupported_but_understood_family: "metadata_lane_choice_clarification"
}
});
const pilot = await executeAssistantMcpDiscoveryPilot(planner, buildDeps([]));
const draft = buildAssistantMcpDiscoveryAnswerDraft(pilot);
expect(draft.answer_mode).toBe("needs_clarification");
expect(draft.headline).toContain("data-lane");
expect(draft.next_step_line).toContain("по документам");
expect(draft.next_step_line).toContain("по движениям/регистрам");
expect(draft.must_not_claim).toContain("Do not claim rows were checked when mcp_execution_performed=false.");
});
it("turns metadata surface evidence into a human-safe metadata answer draft", async () => {
const planner = planAssistantMcpDiscovery({
turnMeaning: {
@@ -152,6 +152,26 @@ describe("assistant MCP discovery planner", () => {
expect(result.proposed_primitives).not.toContain("query_documents");
});
it("keeps metadata lane-choice clarification in needs_clarification without launching MCP primitives", () => {
const result = planAssistantMcpDiscovery({
turnMeaning: {
asked_domain_family: "metadata",
asked_action_family: "resolve_next_lane",
explicit_entity_candidates: ["SVK"],
explicit_date_scope: "2020",
unsupported_but_understood_family: "metadata_lane_choice_clarification"
}
});
expect(result.planner_status).toBe("needs_clarification");
expect(result.semantic_data_need).toBe("metadata lane clarification");
expect(result.proposed_primitives).toEqual([]);
expect(result.required_axes).toEqual(["counterparty", "period", "lane_family_choice"]);
expect(result.discovery_plan.plan_status).toBe("needs_clarification");
expect(result.reason_codes).toContain("planner_selected_metadata_lane_clarification_recipe");
expect(result.reason_codes).toContain("planner_needs_more_user_or_scope_context");
});
it("does not mark an unclassified turn as executable without turn meaning context", () => {
const result = planAssistantMcpDiscovery({});
@@ -351,6 +351,34 @@ describe("assistant MCP discovery response candidate", () => {
expect(candidate.eligible_for_future_hot_runtime).toBe(true);
});
it("surfaces metadata lane-choice clarification as a user-facing clarification candidate", () => {
const candidate = buildAssistantMcpDiscoveryResponseCandidate(
entryPoint({
bridge: {
bridge_status: "needs_clarification",
user_facing_response_allowed: true,
business_fact_answer_allowed: false,
requires_user_clarification: true,
answer_draft: {
answer_mode: "needs_clarification",
headline: "По подтвержденной metadata-поверхности видно несколько конкурирующих data-lane, и без явного выбора дальше идти нельзя.",
confirmed_lines: [],
inference_lines: [],
unknown_lines: [],
limitation_lines: [],
next_step_line: "Уточните, в какой контур идти дальше: по документам или по движениям/регистрам."
}
}
})
);
expect(candidate.candidate_status).toBe("clarification_candidate");
expect(candidate.reply_type).toBe("clarification_required");
expect(candidate.reply_text).toContain("data-lane");
expect(candidate.reply_text).toContain("по документам");
expect(candidate.reply_text).toContain("по движениям/регистрам");
});
it("does not expose unsupported bridge output as a future hot candidate", () => {
const candidate = buildAssistantMcpDiscoveryResponseCandidate(
entryPoint({
@@ -570,6 +570,38 @@ describe("assistant MCP discovery turn input adapter", () => {
expect(result.reason_codes).toContain("mcp_discovery_metadata_ambiguity_collapsed_to_movement_lane");
});
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"],
explicit_date_scope: "2020",
unsupported_but_understood_family: "metadata_lane_choice_clarification",
stale_replay_forbidden: true
});
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?",