diff --git a/docs/orchestration/address_truth_harness_phase61_metadata_movement_pivot_year_switch.json b/docs/orchestration/address_truth_harness_phase61_metadata_movement_pivot_year_switch.json new file mode 100644 index 0000000..89fb3ce --- /dev/null +++ b/docs/orchestration/address_truth_harness_phase61_metadata_movement_pivot_year_switch.json @@ -0,0 +1,94 @@ +{ + "schema_version": "domain_truth_harness_spec_v1", + "scenario_id": "address_truth_harness_phase61_metadata_movement_pivot_year_switch", + "domain": "address_phase61_metadata_movement_pivot_year_switch", + "title": "Phase 61 metadata movement pivot year switch", + "description": "Targeted AGENT replay for Big Block F where a metadata-born document loop reaches bounded retrieval, pivots into movement evidence, and then survives a short year-switch follow-up without losing the selected lane, organization, or scoped proof path.", + "bindings": {}, + "steps": [ + { + "step_id": "step_01_metadata_ambiguity_surface", + "title": "Metadata ambiguity is surfaced honestly for VAT", + "question": "какие объекты 1С есть по НДС?", + "allowed_reply_types": ["partial_coverage", "factual_with_explanation"], + "required_answer_patterns_all": [ + "(?i)metadata|метадан", + "(?i)ндс", + "(?i)документ|регистр" + ], + "criticality": "critical", + "semantic_tags": ["metadata_surface", "mixed_ambiguity"] + }, + { + "step_id": "step_02_neutral_followup_requires_lane_choice", + "title": "Neutral follow-up still requires lane choice", + "question": "давай дальше", + "allowed_reply_types": ["clarification_required", "partial_coverage"], + "required_answer_patterns_all": [ + "(?i)документ", + "(?i)движени|регистр", + "(?i)уточн|выб(ери|рать)|какой контур" + ], + "criticality": "critical", + "semantic_tags": ["metadata_lane_choice_clarification", "neutral_followup"] + }, + { + "step_id": "step_03_document_lane_with_org_keeps_only_period_gap", + "title": "Document lane plus organization in one follow-up leaves only the period gap", + "question": "по документам по ООО Альтернатива Плюс", + "allowed_reply_types": ["clarification_required", "partial_coverage"], + "required_answer_patterns_all": [ + "(?i)документ|счет|накладн|акт", + "(?i)период" + ], + "criticality": "critical", + "semantic_tags": ["document_lane_after_clarification", "inline_organization_clarification"] + }, + { + "step_id": "step_04_period_clarification_executes_same_document_loop", + "title": "Period clarification executes the same bounded document loop", + "question": "за 2020 год", + "allowed_reply_types": ["factual", "factual_with_explanation", "partial_coverage"], + "required_answer_patterns_all": [ + "(?i)документ|счет|сч[её]т[- ]?фактур|накладн|акт|строк" + ], + "criticality": "critical", + "semantic_tags": ["document_lane_execution", "bounded_retrieval"] + }, + { + "step_id": "step_05_movement_pivot_keeps_same_scope", + "title": "Short movement pivot reuses the same organization and period", + "question": "а теперь по движениям?", + "allowed_reply_types": ["factual", "factual_with_explanation", "partial_coverage"], + "required_answer_patterns_all": [ + "(?i)ндс|движени|регистр|операц|платеж|поступлен|списан|строк" + ], + "forbidden_answer_patterns": [ + "(?i)уточните .*организац", + "(?i)уточните .*период", + "(?i)уточните .*контур", + "(?i)не найден контрагент" + ], + "criticality": "critical", + "semantic_tags": ["movement_pivot_after_document_retrieval", "scope_reuse", "same_proof_path_family_shift"] + }, + { + "step_id": "step_06_year_switch_keeps_movement_lane", + "title": "Short year-switch stays in the movement lane after the pivot", + "question": "а теперь за 2021?", + "allowed_reply_types": ["factual", "factual_with_explanation", "partial_coverage"], + "required_answer_patterns_all": [ + "(?i)2021", + "(?i)ндс|движени|регистр|операц|платеж|поступлен|списан|строк" + ], + "forbidden_answer_patterns": [ + "(?i)документ|счет|накладн|акт", + "(?i)уточните .*организац", + "(?i)уточните .*период", + "(?i)не найден контрагент" + ], + "criticality": "critical", + "semantic_tags": ["movement_lane_continuity", "year_switch_after_pivot", "same_scope_new_period"] + } + ] +} diff --git a/docs/orchestration/address_truth_harness_phase62_metadata_movement_pivot_all_time.json b/docs/orchestration/address_truth_harness_phase62_metadata_movement_pivot_all_time.json new file mode 100644 index 0000000..f750dc3 --- /dev/null +++ b/docs/orchestration/address_truth_harness_phase62_metadata_movement_pivot_all_time.json @@ -0,0 +1,95 @@ +{ + "schema_version": "domain_truth_harness_spec_v1", + "scenario_id": "address_truth_harness_phase62_metadata_movement_pivot_all_time", + "domain": "address_phase62_metadata_movement_pivot_all_time", + "title": "Phase 62 metadata movement pivot all-time follow-up", + "description": "Targeted AGENT replay for Big Block F where a metadata-born document loop reaches bounded retrieval, pivots into movement evidence, and then survives a short all-time follow-up without losing the selected lane, organization, or scoped proof path.", + "bindings": {}, + "steps": [ + { + "step_id": "step_01_metadata_ambiguity_surface", + "title": "Metadata ambiguity is surfaced honestly for VAT", + "question": "какие объекты 1С есть по НДС?", + "allowed_reply_types": ["partial_coverage", "factual_with_explanation"], + "required_answer_patterns_all": [ + "(?i)metadata|метадан", + "(?i)ндс", + "(?i)документ|регистр" + ], + "criticality": "critical", + "semantic_tags": ["metadata_surface", "mixed_ambiguity"] + }, + { + "step_id": "step_02_neutral_followup_requires_lane_choice", + "title": "Neutral follow-up still requires lane choice", + "question": "давай дальше", + "allowed_reply_types": ["clarification_required", "partial_coverage"], + "required_answer_patterns_all": [ + "(?i)документ", + "(?i)движени|регистр", + "(?i)уточн|выб(ери|рать)|какой контур" + ], + "criticality": "critical", + "semantic_tags": ["metadata_lane_choice_clarification", "neutral_followup"] + }, + { + "step_id": "step_03_document_lane_with_org_keeps_only_period_gap", + "title": "Document lane plus organization in one follow-up leaves only the period gap", + "question": "по документам по ООО Альтернатива Плюс", + "allowed_reply_types": ["clarification_required", "partial_coverage"], + "required_answer_patterns_all": [ + "(?i)документ|счет|накладн|акт", + "(?i)период" + ], + "criticality": "critical", + "semantic_tags": ["document_lane_after_clarification", "inline_organization_clarification"] + }, + { + "step_id": "step_04_period_clarification_executes_same_document_loop", + "title": "Period clarification executes the same bounded document loop", + "question": "за 2020 год", + "allowed_reply_types": ["factual", "factual_with_explanation", "partial_coverage"], + "required_answer_patterns_all": [ + "(?i)документ|счет|сч[её]т[- ]?фактур|накладн|акт|строк" + ], + "criticality": "critical", + "semantic_tags": ["document_lane_execution", "bounded_retrieval"] + }, + { + "step_id": "step_05_movement_pivot_keeps_same_scope", + "title": "Short movement pivot reuses the same organization and period", + "question": "а теперь по движениям?", + "allowed_reply_types": ["factual", "factual_with_explanation", "partial_coverage"], + "required_answer_patterns_all": [ + "(?i)ндс|движени|регистр|операц|платеж|поступлен|списан|строк" + ], + "forbidden_answer_patterns": [ + "(?i)уточните .*организац", + "(?i)уточните .*период", + "(?i)уточните .*контур", + "(?i)не найден контрагент" + ], + "criticality": "critical", + "semantic_tags": ["movement_pivot_after_document_retrieval", "scope_reuse", "same_proof_path_family_shift"] + }, + { + "step_id": "step_06_all_time_followup_keeps_movement_lane", + "title": "All-time follow-up clears the previous period but stays in the movement lane after the pivot", + "question": "а теперь за все время?", + "allowed_reply_types": ["factual", "factual_with_explanation", "partial_coverage"], + "required_answer_patterns_all": [ + "(?i)все время|доступное время|весь период", + "(?i)ндс|движени|регистр|операц|платеж|поступлен|списан|строк" + ], + "forbidden_answer_patterns": [ + "(?i)за 2020", + "(?i)документ|счет|накладн|акт", + "(?i)уточните .*организац", + "(?i)уточните .*контур", + "(?i)не найден контрагент" + ], + "criticality": "critical", + "semantic_tags": ["all_time_followup", "movement_lane_continuity", "period_cleared_after_pivot"] + } + ] +} diff --git a/docs/orchestration/address_truth_harness_phase63_metadata_document_pivot_all_time.json b/docs/orchestration/address_truth_harness_phase63_metadata_document_pivot_all_time.json new file mode 100644 index 0000000..1f30098 --- /dev/null +++ b/docs/orchestration/address_truth_harness_phase63_metadata_document_pivot_all_time.json @@ -0,0 +1,95 @@ +{ + "schema_version": "domain_truth_harness_spec_v1", + "scenario_id": "address_truth_harness_phase63_metadata_document_pivot_all_time", + "domain": "address_phase63_metadata_document_pivot_all_time", + "title": "Phase 63 metadata document pivot all-time follow-up", + "description": "Targeted AGENT replay for Big Block F where a metadata-born movement loop reaches bounded retrieval, pivots into document evidence, and then survives a short all-time follow-up without losing the selected lane, organization, or scoped proof path.", + "bindings": {}, + "steps": [ + { + "step_id": "step_01_metadata_ambiguity_surface", + "title": "Metadata ambiguity is surfaced honestly for VAT", + "question": "какие объекты 1С есть по НДС?", + "allowed_reply_types": ["partial_coverage", "factual_with_explanation"], + "required_answer_patterns_all": [ + "(?i)metadata|метадан", + "(?i)ндс", + "(?i)документ|регистр" + ], + "criticality": "critical", + "semantic_tags": ["metadata_surface", "mixed_ambiguity"] + }, + { + "step_id": "step_02_neutral_followup_requires_lane_choice", + "title": "Neutral follow-up still requires lane choice", + "question": "давай дальше", + "allowed_reply_types": ["clarification_required", "partial_coverage"], + "required_answer_patterns_all": [ + "(?i)документ", + "(?i)движени|регистр", + "(?i)уточн|выб(ери|рать)|какой контур" + ], + "criticality": "critical", + "semantic_tags": ["metadata_lane_choice_clarification", "neutral_followup"] + }, + { + "step_id": "step_03_movement_lane_with_org_keeps_only_period_gap", + "title": "Movement lane plus organization in one follow-up leaves only the period gap", + "question": "по движениям по ООО Альтернатива Плюс", + "allowed_reply_types": ["clarification_required", "partial_coverage"], + "required_answer_patterns_all": [ + "(?i)движени|регистр", + "(?i)период" + ], + "criticality": "critical", + "semantic_tags": ["movement_lane_after_clarification", "inline_organization_clarification"] + }, + { + "step_id": "step_04_period_clarification_executes_same_movement_loop", + "title": "Period clarification executes the same bounded movement loop", + "question": "за 2020 год", + "allowed_reply_types": ["factual", "factual_with_explanation", "partial_coverage"], + "required_answer_patterns_all": [ + "(?i)ндс|движени|регистр|операц|платеж|поступлен|списан|строк" + ], + "criticality": "critical", + "semantic_tags": ["movement_lane_execution", "bounded_retrieval"] + }, + { + "step_id": "step_05_document_pivot_keeps_same_scope", + "title": "Short document pivot reuses the same organization and period", + "question": "а теперь по документам?", + "allowed_reply_types": ["factual", "factual_with_explanation", "partial_coverage"], + "required_answer_patterns_all": [ + "(?i)ндс|документ|счет|сч[её]т[- ]?фактур|накладн|акт|строк" + ], + "forbidden_answer_patterns": [ + "(?i)уточните .*организац", + "(?i)уточните .*период", + "(?i)уточните .*контур", + "(?i)не найден контрагент" + ], + "criticality": "critical", + "semantic_tags": ["document_pivot_after_movement_retrieval", "scope_reuse", "same_proof_path_family_shift"] + }, + { + "step_id": "step_06_all_time_followup_keeps_document_lane", + "title": "All-time follow-up clears the previous period but stays in the document lane after the pivot", + "question": "а теперь за все время?", + "allowed_reply_types": ["factual", "factual_with_explanation", "partial_coverage"], + "required_answer_patterns_all": [ + "(?i)все время|доступное время|весь период", + "(?i)ндс|документ|счет|сч[её]т[- ]?фактур|накладн|акт|строк" + ], + "forbidden_answer_patterns": [ + "(?i)за 2020", + "(?i)движени|регистр", + "(?i)уточните .*организац", + "(?i)уточните .*контур", + "(?i)не найден контрагент" + ], + "criticality": "critical", + "semantic_tags": ["all_time_followup", "document_lane_continuity", "period_cleared_after_pivot"] + } + ] +} diff --git a/llm_normalizer/backend/dist/services/assistantMcpCatalogIndex.js b/llm_normalizer/backend/dist/services/assistantMcpCatalogIndex.js index cf86b39..6901132 100644 --- a/llm_normalizer/backend/dist/services/assistantMcpCatalogIndex.js +++ b/llm_normalizer/backend/dist/services/assistantMcpCatalogIndex.js @@ -85,7 +85,14 @@ const PRIMITIVE_CONTRACTS = [ supported_fact_families: ["document_evidence", "activity_lifecycle"], supported_action_families: ["list_documents", "activity_duration"], planning_tags: ["document"], - required_axes_any_of: [["document"], ["counterparty"], ["contract"], ["period", "organization"]], + required_axes_any_of: [ + ["document"], + ["counterparty"], + ["contract"], + ["period", "organization"], + ["all_time_scope", "counterparty"], + ["all_time_scope", "organization"] + ], optional_axes: ["account", "amount", "item", "warehouse"], output_fact_kinds: ["document_rows", "document_dates", "document_amounts"], evidence_floor: "rows_matched", diff --git a/llm_normalizer/backend/src/services/assistantMcpCatalogIndex.ts b/llm_normalizer/backend/src/services/assistantMcpCatalogIndex.ts index 7e89cfa..609160f 100644 --- a/llm_normalizer/backend/src/services/assistantMcpCatalogIndex.ts +++ b/llm_normalizer/backend/src/services/assistantMcpCatalogIndex.ts @@ -118,7 +118,14 @@ const PRIMITIVE_CONTRACTS: AssistantMcpCatalogPrimitiveContract[] = [ supported_fact_families: ["document_evidence", "activity_lifecycle"], supported_action_families: ["list_documents", "activity_duration"], planning_tags: ["document"], - required_axes_any_of: [["document"], ["counterparty"], ["contract"], ["period", "organization"]], + required_axes_any_of: [ + ["document"], + ["counterparty"], + ["contract"], + ["period", "organization"], + ["all_time_scope", "counterparty"], + ["all_time_scope", "organization"] + ], optional_axes: ["account", "amount", "item", "warehouse"], output_fact_kinds: ["document_rows", "document_dates", "document_amounts"], evidence_floor: "rows_matched", diff --git a/llm_normalizer/backend/tests/assistantMcpCatalogIndex.test.ts b/llm_normalizer/backend/tests/assistantMcpCatalogIndex.test.ts index 91e707b..cf685fa 100644 --- a/llm_normalizer/backend/tests/assistantMcpCatalogIndex.test.ts +++ b/llm_normalizer/backend/tests/assistantMcpCatalogIndex.test.ts @@ -157,7 +157,9 @@ describe("assistant MCP catalog index", () => { ["document"], ["counterparty"], ["contract"], - ["period", "organization"] + ["period", "organization"], + ["all_time_scope", "counterparty"], + ["all_time_scope", "organization"] ]); }); @@ -180,6 +182,27 @@ describe("assistant MCP catalog index", () => { expect(review.missing_axes_by_primitive).toEqual({}); }); + it("marks an all-time organization-scoped document plan as catalog-compatible without requiring an explicit period", () => { + const plan = buildAssistantMcpDiscoveryPlan({ + semanticDataNeed: "document evidence", + turnMeaning: { + asked_domain_family: "documents", + asked_action_family: "list_documents", + explicit_organization_scope: "ООО Альтернатива Плюс", + metadata_scope_hint: "НДС", + subject_resolution_optional: true + }, + proposedPrimitives: ["query_documents", "probe_coverage"], + requiredAxes: ["organization", "all_time_scope", "metadata_scope", "coverage_target"] + }); + + const review = reviewAssistantMcpDiscoveryPlanAgainstCatalog(plan); + + expect(review.review_status).toBe("catalog_compatible"); + expect(review.reason_codes).toContain("catalog_plan_compatible"); + expect(review.missing_axes_by_primitive).toEqual({}); + }); + it("preserves source-summary evidence floors for metadata and coverage primitives", () => { expect(getAssistantMcpCatalogPrimitive("inspect_1c_metadata").evidence_floor).toBe("source_summary"); expect(getAssistantMcpCatalogPrimitive("probe_coverage").evidence_floor).toBe("source_summary"); diff --git a/llm_normalizer/backend/tests/assistantMcpDiscoveryPlanner.test.ts b/llm_normalizer/backend/tests/assistantMcpDiscoveryPlanner.test.ts index aef750f..209f802 100644 --- a/llm_normalizer/backend/tests/assistantMcpDiscoveryPlanner.test.ts +++ b/llm_normalizer/backend/tests/assistantMcpDiscoveryPlanner.test.ts @@ -905,4 +905,46 @@ describe("assistant MCP discovery planner", () => { expect(result.catalog_review.review_status).toBe("catalog_compatible"); expect(result.reason_codes).toContain("planner_selected_metadata_scoped_movement_from_data_need_graph"); }); + + it("keeps metadata-scoped document evidence execution-ready with all-time scope once organization is known", () => { + const result = planAssistantMcpDiscovery({ + dataNeedGraph: { + schema_version: "assistant_data_need_graph_v1", + policy_owner: "assistantMcpDiscoveryDataNeedGraph", + subject_candidates: [], + metadata_scope_hint: "НДС", + subject_resolution_optional: true, + business_fact_family: "document_evidence", + action_family: "list_documents", + aggregation_need: null, + time_scope_need: "all_time_scope", + comparison_need: null, + ranking_need: null, + proof_expectation: "coverage_checked_fact", + clarification_gaps: [], + decomposition_candidates: ["fetch_scoped_documents", "probe_coverage"], + forbidden_overclaim_flags: ["no_raw_model_claims", "no_unchecked_fact_totals"], + reason_codes: [ + "data_need_graph_built", + "data_need_graph_metadata_scoped_open_lane_without_subject", + "data_need_graph_all_time_scope_hint" + ] + }, + turnMeaning: { + asked_domain_family: "documents", + asked_action_family: "list_documents", + metadata_scope_hint: "НДС", + subject_resolution_optional: true, + explicit_organization_scope: "ООО Альтернатива Плюс", + unsupported_but_understood_family: "document_evidence" + } + }); + + expect(result.planner_status).toBe("ready_for_execution"); + expect(result.selected_chain_id).toBe("document_evidence"); + expect(result.proposed_primitives).toEqual(["query_documents", "probe_coverage"]); + expect(result.required_axes).toEqual(["organization", "metadata_scope", "all_time_scope", "coverage_target"]); + expect(result.catalog_review.review_status).toBe("catalog_compatible"); + expect(result.reason_codes).toContain("planner_selected_metadata_scoped_document_from_data_need_graph"); + }); });