From 399e29f4a49165cf60ae4c53470ec4ff005ffcaa Mon Sep 17 00:00:00 2001 From: dctouch Date: Mon, 20 Apr 2026 07:45:57 +0300 Subject: [PATCH] =?UTF-8?q?ARCH:=20=D1=81=D0=B2=D1=8F=D0=B7=D0=B0=D1=82?= =?UTF-8?q?=D1=8C=20phase18=20capabilities=20=D1=81=20runtime=20contracts?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...alog_authority_recovery_plan_2026-04-19.md | 15 ++++++++ docs/TECH/address_route_baseline_v1.json | 12 +++++++ .../dist/services/addressCapabilityPolicy.js | 10 ++++++ .../assistantRuntimeContractRegistry.js | 33 +++++++++++++++++ .../src/services/addressCapabilityPolicy.ts | 10 ++++++ .../assistantRuntimeContractRegistry.ts | 33 +++++++++++++++++ .../tests/addressCapabilityPolicy.test.ts | 14 ++++++++ ...antCapabilityRuntimeBindingAdapter.test.ts | 29 +++++++++++++++ .../assistantRuntimeContractRegistry.test.ts | 35 +++++++++++++++++++ 9 files changed, 191 insertions(+) diff --git a/docs/ARCH/11 - architecture_turnaround/14 - semantic_dialog_authority_recovery_plan_2026-04-19.md b/docs/ARCH/11 - architecture_turnaround/14 - semantic_dialog_authority_recovery_plan_2026-04-19.md index e47d136..2558fb7 100644 --- a/docs/ARCH/11 - architecture_turnaround/14 - semantic_dialog_authority_recovery_plan_2026-04-19.md +++ b/docs/ARCH/11 - architecture_turnaround/14 - semantic_dialog_authority_recovery_plan_2026-04-19.md @@ -474,6 +474,21 @@ Important semantic conclusion: - the turn `какой оборот был свк` now routes to `customer_revenue_and_payments` and opens with direct turnover for `Группа СВК`, not with stale `Чепурнов` documents or a generic top-client ranking; - off-domain living-chat turns after a business answer are accepted when they stay live and do not replay stale business context. +## Progress Update - 2026-04-20 Contract Binding Cleanup + +The same phase18 replay was rerun after adding runtime contracts for the counterparty document and value-flow capabilities: + +- live replay artifact: `artifacts/domain_runs/address_truth_harness_phase18_contract_binding_rerun` +- `final_status`: `accepted` +- `steps_passed`: 7/7 + +Contract-layer result: + +- `documents_drilldown`: `transition_contract_id=T1`, `capability_binding_status=bound`, no binding violations; +- `address_customer_revenue_and_payments`: `transition_contract_id=T1`, `capability_binding_status=bound`, no binding violations. + +This closes the misleading `capability_contract_missing` / `transition_contract_not_resolved` debug gap for the core phase18 path without changing the user-facing answer semantics. + ## Execution Rule Do not implement this plan as: diff --git a/docs/TECH/address_route_baseline_v1.json b/docs/TECH/address_route_baseline_v1.json index 8fa5321..d2e082e 100644 --- a/docs/TECH/address_route_baseline_v1.json +++ b/docs/TECH/address_route_baseline_v1.json @@ -50,6 +50,18 @@ "capability_layer": "compute", "capability_route_mode": "exact" }, + { + "intent": "customer_revenue_and_payments", + "capability_id": "address_customer_revenue_and_payments", + "capability_layer": "compute", + "capability_route_mode": "exact" + }, + { + "intent": "supplier_payouts_profile", + "capability_id": "address_supplier_payouts_profile", + "capability_layer": "compute", + "capability_route_mode": "exact" + }, { "intent": "list_documents_by_counterparty", "capability_id": "documents_drilldown", diff --git a/llm_normalizer/backend/dist/services/addressCapabilityPolicy.js b/llm_normalizer/backend/dist/services/addressCapabilityPolicy.js index 7554cb9..c868c06 100644 --- a/llm_normalizer/backend/dist/services/addressCapabilityPolicy.js +++ b/llm_normalizer/backend/dist/services/addressCapabilityPolicy.js @@ -14,6 +14,8 @@ const COMPUTE_EXACT_INTENTS = new Set([ "inventory_sale_trace_for_item", "inventory_purchase_to_sale_chain", "inventory_aging_by_purchase_date", + "customer_revenue_and_payments", + "supplier_payouts_profile", "open_contracts_confirmed_as_of_date", "payables_confirmed_as_of_date", "receivables_confirmed_as_of_date", @@ -128,6 +130,14 @@ function resolveCapabilityEnabled(intent) { : "vat_liability_confirmed_tax_period_route_disabled_by_flag" }; } + if (intent === "customer_revenue_and_payments" || intent === "supplier_payouts_profile") { + return { + enabled: config_1.FEATURE_ASSISTANT_ROUTE_ADDRESS_GENERIC_V1, + reason: config_1.FEATURE_ASSISTANT_ROUTE_ADDRESS_GENERIC_V1 + ? "counterparty_value_flow_route_enabled" + : "counterparty_value_flow_route_disabled_by_flag" + }; + } if (intent === "inventory_on_hand_as_of_date") { return { enabled: config_1.FEATURE_ASSISTANT_ROUTE_BALANCE_EXACT_V1, diff --git a/llm_normalizer/backend/dist/services/assistantRuntimeContractRegistry.js b/llm_normalizer/backend/dist/services/assistantRuntimeContractRegistry.js index 59dda4b..7014751 100644 --- a/llm_normalizer/backend/dist/services/assistantRuntimeContractRegistry.js +++ b/llm_normalizer/backend/dist/services/assistantRuntimeContractRegistry.js @@ -324,6 +324,39 @@ exports.INVENTORY_CAPABILITY_CONTRACTS = [ }) ]; exports.ROOT_EXACT_CAPABILITY_CONTRACTS = [ + rootExactCapability({ + capability_id: "documents_drilldown", + domainId: "counterparty_documents", + intent_ids: ["list_documents_by_counterparty", "list_documents_by_contract"], + transitions: ["T1", "T2", "T6", "T7"], + requiredAnchors: [], + optionalAnchors: ["organization", "date_scope", "counterparty", "contract", "document_type"], + resultShape: "counterparty_or_contract_document_list", + answerObjectShape: "documents_drilldown_list", + scenarioFamilies: ["canonical", "colloquial", "short_counterparty_retarget", "followup_date_carryover"] + }), + rootExactCapability({ + capability_id: "address_customer_revenue_and_payments", + domainId: "counterparty_revenue", + intent_ids: ["customer_revenue_and_payments"], + transitions: ["T1", "T2", "T6", "T7"], + requiredAnchors: [], + optionalAnchors: ["organization", "date_scope", "counterparty", "contract"], + resultShape: "counterparty_revenue_payment_flow", + answerObjectShape: "counterparty_revenue_summary", + scenarioFamilies: ["canonical", "colloquial", "short_counterparty_retarget", "answer_top_block_matches_current_user_intent"] + }), + rootExactCapability({ + capability_id: "address_supplier_payouts_profile", + domainId: "counterparty_payouts", + intent_ids: ["supplier_payouts_profile"], + transitions: ["T1", "T2", "T6", "T7"], + requiredAnchors: [], + optionalAnchors: ["organization", "date_scope", "counterparty", "contract"], + resultShape: "supplier_payout_payment_flow", + answerObjectShape: "supplier_payout_summary", + scenarioFamilies: ["canonical", "colloquial", "short_counterparty_retarget"] + }), rootExactCapability({ capability_id: "confirmed_payables_as_of_date", domainId: "counterparty_debt", diff --git a/llm_normalizer/backend/src/services/addressCapabilityPolicy.ts b/llm_normalizer/backend/src/services/addressCapabilityPolicy.ts index a286848..16cdc13 100644 --- a/llm_normalizer/backend/src/services/addressCapabilityPolicy.ts +++ b/llm_normalizer/backend/src/services/addressCapabilityPolicy.ts @@ -33,6 +33,8 @@ const COMPUTE_EXACT_INTENTS = new Set([ "inventory_sale_trace_for_item", "inventory_purchase_to_sale_chain", "inventory_aging_by_purchase_date", + "customer_revenue_and_payments", + "supplier_payouts_profile", "open_contracts_confirmed_as_of_date", "payables_confirmed_as_of_date", "receivables_confirmed_as_of_date", @@ -154,6 +156,14 @@ function resolveCapabilityEnabled(intent: AddressIntent): { enabled: boolean; re : "vat_liability_confirmed_tax_period_route_disabled_by_flag" }; } + if (intent === "customer_revenue_and_payments" || intent === "supplier_payouts_profile") { + return { + enabled: FEATURE_ASSISTANT_ROUTE_ADDRESS_GENERIC_V1, + reason: FEATURE_ASSISTANT_ROUTE_ADDRESS_GENERIC_V1 + ? "counterparty_value_flow_route_enabled" + : "counterparty_value_flow_route_disabled_by_flag" + }; + } if (intent === "inventory_on_hand_as_of_date") { return { enabled: FEATURE_ASSISTANT_ROUTE_BALANCE_EXACT_V1, diff --git a/llm_normalizer/backend/src/services/assistantRuntimeContractRegistry.ts b/llm_normalizer/backend/src/services/assistantRuntimeContractRegistry.ts index f8f2e3f..1cb6cc4 100644 --- a/llm_normalizer/backend/src/services/assistantRuntimeContractRegistry.ts +++ b/llm_normalizer/backend/src/services/assistantRuntimeContractRegistry.ts @@ -352,6 +352,39 @@ export const INVENTORY_CAPABILITY_CONTRACTS: readonly AssistantCapabilityContrac ] as const; export const ROOT_EXACT_CAPABILITY_CONTRACTS: readonly AssistantCapabilityContract[] = [ + rootExactCapability({ + capability_id: "documents_drilldown", + domainId: "counterparty_documents", + intent_ids: ["list_documents_by_counterparty", "list_documents_by_contract"], + transitions: ["T1", "T2", "T6", "T7"], + requiredAnchors: [], + optionalAnchors: ["organization", "date_scope", "counterparty", "contract", "document_type"], + resultShape: "counterparty_or_contract_document_list", + answerObjectShape: "documents_drilldown_list", + scenarioFamilies: ["canonical", "colloquial", "short_counterparty_retarget", "followup_date_carryover"] + }), + rootExactCapability({ + capability_id: "address_customer_revenue_and_payments", + domainId: "counterparty_revenue", + intent_ids: ["customer_revenue_and_payments"], + transitions: ["T1", "T2", "T6", "T7"], + requiredAnchors: [], + optionalAnchors: ["organization", "date_scope", "counterparty", "contract"], + resultShape: "counterparty_revenue_payment_flow", + answerObjectShape: "counterparty_revenue_summary", + scenarioFamilies: ["canonical", "colloquial", "short_counterparty_retarget", "answer_top_block_matches_current_user_intent"] + }), + rootExactCapability({ + capability_id: "address_supplier_payouts_profile", + domainId: "counterparty_payouts", + intent_ids: ["supplier_payouts_profile"], + transitions: ["T1", "T2", "T6", "T7"], + requiredAnchors: [], + optionalAnchors: ["organization", "date_scope", "counterparty", "contract"], + resultShape: "supplier_payout_payment_flow", + answerObjectShape: "supplier_payout_summary", + scenarioFamilies: ["canonical", "colloquial", "short_counterparty_retarget"] + }), rootExactCapability({ capability_id: "confirmed_payables_as_of_date", domainId: "counterparty_debt", diff --git a/llm_normalizer/backend/tests/addressCapabilityPolicy.test.ts b/llm_normalizer/backend/tests/addressCapabilityPolicy.test.ts index a054e33..e4a5c0e 100644 --- a/llm_normalizer/backend/tests/addressCapabilityPolicy.test.ts +++ b/llm_normalizer/backend/tests/addressCapabilityPolicy.test.ts @@ -119,6 +119,20 @@ describe("address capability policy", () => { expect(decision.capability_route_enabled).toBe(true); }); + it("maps counterparty value-flow intents to exact compute capabilities", () => { + const revenue = resolveAddressCapabilityRouteDecision("customer_revenue_and_payments"); + expect(revenue.capability_id).toBe("address_customer_revenue_and_payments"); + expect(revenue.capability_layer).toBe("compute"); + expect(revenue.capability_route_mode).toBe("exact"); + expect(revenue.capability_route_enabled).toBe(true); + expect(revenue.capability_route_reason).toBe("counterparty_value_flow_route_enabled"); + + const payouts = resolveAddressCapabilityRouteDecision("supplier_payouts_profile"); + expect(payouts.capability_id).toBe("address_supplier_payouts_profile"); + expect(payouts.capability_layer).toBe("compute"); + expect(payouts.capability_route_mode).toBe("exact"); + }); + it("maps heuristic list intents to heuristic compute route mode", () => { const decision = resolveAddressCapabilityRouteDecision("list_receivables_counterparties"); expect(decision.capability_id).toBe("receivables_candidates_list"); diff --git a/llm_normalizer/backend/tests/assistantCapabilityRuntimeBindingAdapter.test.ts b/llm_normalizer/backend/tests/assistantCapabilityRuntimeBindingAdapter.test.ts index 2f4da70..068c141 100644 --- a/llm_normalizer/backend/tests/assistantCapabilityRuntimeBindingAdapter.test.ts +++ b/llm_normalizer/backend/tests/assistantCapabilityRuntimeBindingAdapter.test.ts @@ -76,6 +76,35 @@ describe("assistant capability runtime binding adapter", () => { expect(binding.violations).toEqual([]); }); + it("binds counterparty revenue answers to the phase18 exact contract", () => { + const binding = resolveAssistantCapabilityRuntimeBinding({ + addressDebug: { + capability_id: "address_customer_revenue_and_payments", + detected_intent: "customer_revenue_and_payments", + detected_mode: "address_query", + capability_layer: "compute", + capability_route_mode: "exact", + extracted_filters: { + counterparty: "Группа СВК", + organization: "ООО Альтернатива Плюс" + }, + rows_matched: 16, + route_expectation_status: "matched" + }, + groundingStatus: "grounded", + replyType: "factual" + }); + + expect(binding.binding_status).toBe("bound"); + expect(binding.binding_action).toBe("allow"); + expect(binding.capability_contract_id).toBe("address_customer_revenue_and_payments"); + expect(binding.transition_id).toBe("T1"); + expect(binding.transition_allowed).toBe(true); + expect(binding.result_shape).toBe("counterparty_revenue_payment_flow"); + expect(binding.provided_anchors).toEqual(expect.arrayContaining(["counterparty", "organization"])); + expect(binding.violations).toEqual([]); + }); + it("blocks selected-object capabilities when required anchors are missing", () => { const binding = resolveAssistantCapabilityRuntimeBinding({ addressDebug: { diff --git a/llm_normalizer/backend/tests/assistantRuntimeContractRegistry.test.ts b/llm_normalizer/backend/tests/assistantRuntimeContractRegistry.test.ts index 0916fa8..fc93bb0 100644 --- a/llm_normalizer/backend/tests/assistantRuntimeContractRegistry.test.ts +++ b/llm_normalizer/backend/tests/assistantRuntimeContractRegistry.test.ts @@ -95,6 +95,20 @@ describe("assistant runtime contract registry", () => { expect(contract?.execution_adapter).toBe("AddressQueryService"); }); + it("declares counterparty document and value-flow contracts used by semantic dialog authority replay", () => { + const documents = getAssistantCapabilityContractByIntent("list_documents_by_counterparty"); + expect(documents?.capability_id).toBe("documents_drilldown"); + expect(documents?.supported_transition_classes).toEqual(["T1", "T2", "T6", "T7"]); + expect(documents?.result_shape).toBe("counterparty_or_contract_document_list"); + + const revenue = getAssistantCapabilityContractByIntent("customer_revenue_and_payments"); + expect(revenue?.capability_id).toBe("address_customer_revenue_and_payments"); + expect(revenue?.runtime_lane).toBe("address_exact"); + expect(revenue?.requires_focus_object).toBe(false); + expect(revenue?.supported_transition_classes).toEqual(["T1", "T2", "T6", "T7"]); + expect(revenue?.required_scenario_families).toContain("answer_top_block_matches_current_user_intent"); + }); + it("keeps truth semantics outside answer wording for every pilot inventory capability", () => { for (const contract of listInventoryCapabilityContracts()) { expect(contract.coverage_gate_behavior).toBe("partial_or_blocked_if_evidence_insufficient"); @@ -130,6 +144,27 @@ describe("assistant runtime contract registry", () => { expect(decision.carryover_eligibility).toBe("object_only"); }); + it("resolves phase18 counterparty revenue shadow as a root exact contract", () => { + const decision = resolveAssistantRuntimeContractShadow({ + addressDebug: { + capability_id: "address_customer_revenue_and_payments", + detected_intent: "customer_revenue_and_payments", + detected_mode: "address_query", + capability_layer: "compute", + capability_route_mode: "exact", + rows_matched: 16, + route_expectation_status: "matched" + }, + groundingStatus: "grounded" + }); + + expect(decision.transition_contract_id).toBe("T1"); + expect(decision.capability_contract_id).toBe("address_customer_revenue_and_payments"); + expect(decision.capability_contract_reason).toEqual(["debug_capability_id_matched_contract"]); + expect(decision.truth_gate_contract_status).toBe("full_confirmed"); + expect(decision.carryover_eligibility).toBe("root_only"); + }); + it("resolves meta follow-up and blocked route expectation in shadow mode", () => { const metaDecision = resolveAssistantRuntimeContractShadow({ addressRuntimeMeta: {