diff --git a/docs/ARCH/11 - architecture_turnaround/21 - current_status_canon_2026-05-01.md b/docs/ARCH/11 - architecture_turnaround/21 - current_status_canon_2026-05-01.md index c53f6ff..d7aee09 100644 --- a/docs/ARCH/11 - architecture_turnaround/21 - current_status_canon_2026-05-01.md +++ b/docs/ARCH/11 - architecture_turnaround/21 - current_status_canon_2026-05-01.md @@ -38,8 +38,9 @@ If another document says `78%`, `87%`, `92%`, or `85%` for a module that is now - Completed active slice: `Business Overview Supplier/Procurement Quality Boundary Bridge`: organization-level supplier concentration, vendor-risk, dependency, and procurement-quality wording now routes to `business_overview`, while supplier payment/open-settlement/doc questions stay in exact supplier/payables routes with a vendor-risk proof boundary. - Completed active slice: `Business Overview Document/Account Activity Profile Bridge`: business overview now executes the reviewed `document_type_and_account_section_profile` recipe and surfaces confirmed operational activity mix without claiming process quality, accounting correctness, or complete 1C activity coverage. - Completed active slice: `Business Overview Counterparty/Contract Profile Bridge`: business overview now executes reviewed `counterparty_population_and_roles` and `contract_usage_overview` recipes, surfacing active counterparty role split and contract usage without claiming CRM quality, counterparty due diligence, legal completeness, or contract-risk. +- Completed active slice: `Business Overview Missing Proof Ledger`: business overview now records machine-readable hard proof gaps for accounting profit/margin, due-date debt aging, inventory reserve/liquidation quality, and vendor/procurement quality, distinguishing proxy-only evidence from reviewed routes that are not wired yet. - Next active slice: continue breadth into exact company-wide accounting profit/margin, real due-date debt aging, confirmed inventory reserve/write-off/liquidation evidence, and broader unfamiliar 1C route families only where reviewed evidence routes exist. -- Active module progress: `~98% (Open-World Bounded Autonomy Breadth)`. +- Active module progress: `~99% (Open-World Bounded Autonomy Breadth)`. ## Reporting Rule @@ -76,7 +77,7 @@ The project is not yet a universal arbitrary-1C agent. Remaining work belongs to the next breadth module: -- extend `business_overview` beyond money-flow/activity, customer and supplier concentration, document/account-section activity mix, counterparty role split, contract usage, yearly operating-flow dynamics, explicit profit/margin wording boundaries, explicit debt due-date wording boundaries, explicit inventory reserve/liquidation wording boundaries, explicit supplier/procurement-quality wording boundaries, explicit-period VAT/tax, as-of-date debt position, open-settlement concentration, contract-date debt age, debt staleness-risk proxy, as-of-date inventory position, trading-margin proxy, sales-to-stock inventory proxy, and warehouse staleness-risk proxy into separately proven exact accounting profit/margin, due-date debt aging/overdue, confirmed vendor-risk/procurement-quality analysis, and confirmed reserve/write-off/liquidation inventory evidence families; +- extend `business_overview` beyond money-flow/activity, customer and supplier concentration, document/account-section activity mix, counterparty role split, contract usage, yearly operating-flow dynamics, explicit profit/margin wording boundaries, explicit debt due-date wording boundaries, explicit inventory reserve/liquidation wording boundaries, explicit supplier/procurement-quality wording boundaries, explicit-period VAT/tax, as-of-date debt position, open-settlement concentration, contract-date debt age, debt staleness-risk proxy, as-of-date inventory position, trading-margin proxy, sales-to-stock inventory proxy, warehouse staleness-risk proxy, and the missing-proof ledger into separately proven exact accounting profit/margin, due-date debt aging/overdue, confirmed vendor-risk/procurement-quality analysis, and confirmed reserve/write-off/liquidation inventory evidence families; - broader dynamic schema traversal for unfamiliar 1C asks; - more primitive descriptors where live evidence proves a real gap; - more replay-backed domain packs that start from user business meaning, not from route convenience; diff --git a/docs/ARCH/11 - architecture_turnaround/22 - open_world_bounded_autonomy_breadth_2026-05-01.md b/docs/ARCH/11 - architecture_turnaround/22 - open_world_bounded_autonomy_breadth_2026-05-01.md index fa897ad..485707c 100644 --- a/docs/ARCH/11 - architecture_turnaround/22 - open_world_bounded_autonomy_breadth_2026-05-01.md +++ b/docs/ARCH/11 - architecture_turnaround/22 - open_world_bounded_autonomy_breadth_2026-05-01.md @@ -697,3 +697,34 @@ Local validation is accepted for this slice: - `npm.cmd run build`: passed. Graphify rebuild after Slice 24 code/doc sync: `6069 nodes`, `13230 edges`, `140 communities`. + +## Slice 25 - Business Overview Missing Proof Ledger + +This slice hardens the last high-risk semantic edge before the module can be treated as nearly closed: broad company analysis now carries a machine-readable ledger of the proof families that are still missing, instead of relying only on prose boundaries. + +The important distinction is not "can we say something useful?" because the current overview already can. The distinction is whether downstream answer drafting and future route expansion can reliably tell: + +- what was actually checked in the current turn; +- what is only a bounded proxy; +- what still has no reviewed route wired into runtime; +- what must never be promoted into a confirmed business-audit conclusion. + +Implemented now: + +- `derived_business_overview.missing_proof_families` records the remaining hard proof families for accounting profit/margin, due-date debt aging, inventory reserve/liquidation quality, and vendor-risk/procurement quality; +- each missing proof family exposes `current_status`, `current_supported_evidence`, `next_required_evidence`, and `must_not_claim`; +- trading-margin evidence is explicitly recorded as `proxy_only_currently` for accounting profit/margin, not as clean profit or P&L; +- open-settlement/debt-staleness evidence is explicitly recorded as proxy-only for due-date aging, not as contractual overdue debt; +- inventory stock/turnover/staleness evidence is explicitly recorded as proxy-only for reserves, write-offs, obsolescence, and liquidation value; +- supplier concentration is explicitly recorded as proxy-only for vendor/procurement quality, not as supplier reliability or full expense-structure proof; +- the answer adapter adds a guard reason code and a `must_not_claim` rule so the proof ledger itself is not mistaken for an executed route. + +This is not a new exact P&L, payment-term, reserve/write-off, or vendor-risk engine. It is a semantic safety ledger for the remaining exact-route work: future slices can either wire a reviewed evidence route or keep the family honestly unproved. + +Local validation is accepted for this slice: + +- `npm.cmd test -- assistantMcpDiscoveryPilotExecutor.test.ts assistantMcpDiscoveryAnswerAdapter.test.ts`: passed `66` with `1` skipped. +- `npm.cmd test -- addressQueryRuntimeM23.test.ts`: passed `416`. +- `npm.cmd run build`: passed. + +Graphify rebuild after Slice 25 code/doc sync: `6070 nodes`, `13233 edges`, `135 communities`. diff --git a/docs/ARCH/11 - architecture_turnaround/README.md b/docs/ARCH/11 - architecture_turnaround/README.md index 592e5d4..66059e2 100644 --- a/docs/ARCH/11 - architecture_turnaround/README.md +++ b/docs/ARCH/11 - architecture_turnaround/README.md @@ -73,6 +73,7 @@ Status canon for planning: - The current completed breadth slice is `Business Overview Supplier/Procurement Quality Boundary Bridge`: organization-level supplier concentration, vendor-risk, dependency, and procurement-quality wording now reaches `business_overview`, while supplier payment/open-settlement/doc questions stay in exact supplier/payables routes with a vendor-risk proof boundary. - The current completed breadth slice is `Business Overview Document/Account Activity Profile Bridge`: business overview now executes the reviewed document-type/account-section profile and can surface confirmed operational activity mix without claiming process quality, accounting correctness, or full 1C coverage. - The current completed breadth slice is `Business Overview Counterparty/Contract Profile Bridge`: business overview now executes reviewed counterparty population/roles and contract usage profiles, while CRM quality, counterparty due diligence, legal completeness, and contract-risk remain unclaimed. +- The current completed breadth slice is `Business Overview Missing Proof Ledger`: business overview now records hard remaining proof gaps as machine-readable `missing_proof_families`, separating proxy-only evidence from reviewed routes that are not wired yet. - The next active breadth slice continues breadth into exact company-wide accounting profit/margin, real due-date debt aging, confirmed reserve/write-off/liquidation inventory evidence, and broader unfamiliar 1C route families without relaxing truth boundaries. - The short source of truth for status wording is [21 - current_status_canon_2026-05-01.md](./21%20-%20current_status_canon_2026-05-01.md). @@ -138,11 +139,11 @@ Current honest status: - pre-multidomain readiness: `~90%` - bounded-autonomy foundation readiness: `~89%` - open-world bounded-autonomy readiness: `~87%` -- active Open-World Bounded Autonomy Breadth progress: `~98%`, with business-overview evidence fusion, the reviewed `business_overview` catalog/data-need/planner route-fabric slice, the fresh multi-probe runtime bridge, the explicit-period VAT/tax fact-family bridge, the explicit-period debt-position bridge, the explicit-date inventory-position bridge, the open-settlement quality bridge accepted by live semantic replay, selected-item profitability bridged by local semantic/runtime regression tests, contract-date debt age bridged locally, debt staleness-risk proxy bridged locally, debt due-date boundary arbitration bridged locally, inventory reserve/liquidation boundary arbitration bridged locally, supplier/procurement-quality boundary arbitration bridged locally, supplier concentration proxy bridged locally, document/account-section activity profile bridged locally, counterparty population/roles and contract usage profiles bridged locally, yearly operating-flow proxy bridged locally, earnings/best-year wording arbitration bridged locally, profit/margin wording boundary arbitration bridged locally, analyst synthesis added to business-overview answer drafting, company-period trading margin proxy bridged locally, inventory sales-to-stock proxy bridged locally, inventory staleness-risk proxy bridged locally, and gap-specific answer shaping bridged locally; exact accounting profit/margin, true due-date debt aging/overdue, confirmed vendor-risk/procurement-quality analysis, and confirmed reserve/write-off/liquidation inventory evidence are still pending +- active Open-World Bounded Autonomy Breadth progress: `~99%`, with business-overview evidence fusion, the reviewed `business_overview` catalog/data-need/planner route-fabric slice, the fresh multi-probe runtime bridge, the explicit-period VAT/tax fact-family bridge, the explicit-period debt-position bridge, the explicit-date inventory-position bridge, the open-settlement quality bridge accepted by live semantic replay, selected-item profitability bridged by local semantic/runtime regression tests, contract-date debt age bridged locally, debt staleness-risk proxy bridged locally, debt due-date boundary arbitration bridged locally, inventory reserve/liquidation boundary arbitration bridged locally, supplier/procurement-quality boundary arbitration bridged locally, supplier concentration proxy bridged locally, document/account-section activity profile bridged locally, counterparty population/roles and contract usage profiles bridged locally, yearly operating-flow proxy bridged locally, earnings/best-year wording arbitration bridged locally, profit/margin wording boundary arbitration bridged locally, analyst synthesis added to business-overview answer drafting, company-period trading margin proxy bridged locally, inventory sales-to-stock proxy bridged locally, inventory staleness-risk proxy bridged locally, gap-specific answer shaping bridged locally, and missing proof families recorded as runtime evidence ledger; exact accounting profit/margin, true due-date debt aging/overdue, confirmed vendor-risk/procurement-quality analysis, and confirmed reserve/write-off/liquidation inventory evidence are still pending - Post-F semantic integrity module progress: `~99%` operationally closed, with remaining risk now treated as next-slice discovery rather than an open blocker inside the closed slice - active inventory-stock breadth slice progress: `100%` for the declared scenario pack, not for arbitrary inventory questions - Planner Autonomy Consolidation progress: `100%` for the declared module, with catalog-fabric, value-flow arbitration, lifecycle bounded inference, broad-evaluation bridge, inventory catalog templates, inventory runtime-boundary honesty, exact inventory recipe bridging, unambiguous metadata-surface lane inference, catalog chain-template scoring, structured chain-match contract exposure, runtime/debug propagation, subject-aware bidirectional comparison arbitration, structured catalog-alignment verdicts, representative alignment regression guard, catalog-alignment reason-code telemetry, explicit `alignment_status` propagation, truth-harness/acceptance-matrix surfacing, soft divergence warning, `catalog_alignment_ok` acceptance invariant, step-level expected catalog-alignment assertions, phase66 and phase32 spec alignment expectations, AGENT source-catalog surfacing, generated phase83 mixed planner-brain replay spec, checked-source user-facing error sanitation, surface-grounded catalog promotion, and guarded live phase83 acceptance validated. Broader unfamiliar 1C asks are now next-module breadth work rather than an open blocker inside this declared slice -- graph snapshot after latest rebuild: `6069 nodes`, `13230 edges`, `140 communities` +- graph snapshot after latest rebuild: `6070 nodes`, `13233 edges`, `135 communities` - current regression-gate breakpoint: - the validated hot paths are no longer structurally broken; - flagship continuity collapse is no longer the primary risk; @@ -196,6 +197,7 @@ Latest live proof now includes: - business-overview debt staleness-risk proxy accepted locally: targeted executor/answer-adapter slice passed `66/66` with `1` skipped; M23 route/runtime regression passed `412/412`; build passed; graphify rebuilt to `6040 nodes`, `13158 edges`, `135 communities`; the proxy combines contract-date age and open-balance concentration while confirmed overdue debt, contractual delinquency, credit risk, and due-date aging remain unclaimed - business-overview supplier concentration proxy accepted locally: targeted executor/answer-adapter slice passed `66/66` with `1` skipped; M23 route/runtime regression passed `412/412`; build passed; graphify rebuilt to `6041 nodes`, `13162 edges`, `136 communities`; the proxy ranks confirmed outgoing payment counterparties while vendor risk, procurement quality, and full expense structure remain unclaimed - business-overview yearly operating-flow proxy accepted locally: targeted executor/answer-adapter slice passed `66/66` with `1` skipped; M23 route/runtime regression passed `412/412`; build passed; graphify rebuilt to `6047 nodes`, `13177 edges`, `139 communities`; the proxy builds annual incoming/outgoing/net buckets from confirmed money-flow rows while profit, финрезультат, and full P&L remain unclaimed +- business-overview missing proof ledger accepted locally: targeted executor/answer-adapter slice passed `66/66` with `1` skipped; M23 route/runtime regression passed `416/416`; build passed; graphify count is recorded in the current graph snapshot; hard remaining proof gaps are now visible as machine-readable `missing_proof_families` rather than only prose warnings - business-overview earnings wording arbitration accepted locally: turn-meaning/turn-input slice passed `85/85` with `6` skipped; M23 route/runtime regression passed `412/412`; runtime-entry/pilot/answer slice passed `85/85` with `3` skipped; build passed; graphify rebuilt to `6052 nodes`, `13187 edges`, `138 communities`; organization-level earnings/best-year wording now reaches `business_overview` while explicit customer/counterparty ranking remains in exact customer value routes - inventory template lift accepted locally: catalog/data-need/planner/turn-input slice passed `139/139` with `6` skipped; full MCP-discovery slice passed `276/276` with `9` skipped; build passed; graphify stayed at `5912 nodes`, `12833 edges`, `138 communities` - inventory runtime-boundary hardening accepted locally: runtime-bridge/answer-adapter/pilot-executor slice passed `68/68` with `1` skipped; full MCP-discovery slice passed `277/277` with `9` skipped; build passed; graphify rebuilt to `5913 nodes`, `12837 edges`, `138 communities` diff --git a/llm_normalizer/backend/dist/services/assistantMcpDiscoveryAnswerAdapter.js b/llm_normalizer/backend/dist/services/assistantMcpDiscoveryAnswerAdapter.js index 6fee3c7..3ac4724 100644 --- a/llm_normalizer/backend/dist/services/assistantMcpDiscoveryAnswerAdapter.js +++ b/llm_normalizer/backend/dist/services/assistantMcpDiscoveryAnswerAdapter.js @@ -639,6 +639,9 @@ function buildMustNotClaim(pilot) { claims.push("Do not present an inventory snapshot or purchase-date aging signal as turnover, obsolescence, liquidation value, or full inventory health."); claims.push("Do not present business overview inventory turnover proxy as full inventory liquidity, FIFO turnover, obsolescence analysis, or liquidation value."); claims.push("Do not present business overview inventory staleness risk proxy as confirmed obsolete stock, reserve, write-off, or liquidation value."); + if (pilot.derived_business_overview?.missing_proof_families?.length) { + claims.push("Do not present business overview missing proof families as checked, executed, or confirmed routes."); + } claims.push("Do not expose business_overview_route_template_v1 or MCP primitive names in the user answer."); } if (pilot.derived_ranked_value_flow) { @@ -1352,6 +1355,9 @@ function buildAssistantMcpDiscoveryAnswerDraft(pilot) { if (pilot.derived_business_overview?.inventory_staleness_risk_proxy) { pushReason(reasonCodes, "answer_contains_business_overview_inventory_staleness_risk_proxy"); } + if (pilot.derived_business_overview?.missing_proof_families?.length) { + pushReason(reasonCodes, "answer_contains_business_overview_missing_proof_ledger"); + } const confirmedLines = businessOverviewLines.length > 0 ? businessOverviewLines : pilot.derived_ranked_value_flow && derivedValueLine diff --git a/llm_normalizer/backend/dist/services/assistantMcpDiscoveryPilotExecutor.js b/llm_normalizer/backend/dist/services/assistantMcpDiscoveryPilotExecutor.js index 62a9999..51d9bca 100644 --- a/llm_normalizer/backend/dist/services/assistantMcpDiscoveryPilotExecutor.js +++ b/llm_normalizer/backend/dist/services/assistantMcpDiscoveryPilotExecutor.js @@ -2740,6 +2740,75 @@ function inventoryStalenessRiskBandRu(riskBand) { } return "низкий видимый риск"; } +function buildBusinessOverviewMissingProofFamilies(input) { + const missing = new Set(input.missingSignalFamilies); + const items = []; + const pushUnique = (item) => { + if (!items.some((existing) => existing.family === item.family)) { + items.push(item); + } + }; + if (missing.has("profit_margin") || missing.has("accounting_profit_margin")) { + pushUnique({ + family: "accounting_profit_margin", + current_status: input.tradingMarginProxy ? "proxy_only_currently" : "reviewed_route_not_wired", + current_supported_evidence: input.tradingMarginProxy + ? "trading_margin_proxy_from_sales_and_purchase_document_rows" + : null, + next_required_evidence: "reviewed_pnl_route_with_cost_of_sales_expenses_period_close_and_financial_result", + must_not_claim: "clean_profit_accounting_margin_or_full_pnl" + }); + } + if (missing.has("debt_due_date_aging_quality") || missing.has("debt_open_settlement_quality")) { + pushUnique({ + family: "debt_due_date_aging_quality", + current_status: input.debtStalenessRiskProxy + ? "proxy_only_currently" + : input.debtOpenSettlementQuality + ? "proxy_only_currently" + : "not_checked_in_current_turn", + current_supported_evidence: input.debtStalenessRiskProxy + ? "debt_staleness_risk_proxy_from_contract_dates_and_open_balance_concentration" + : input.debtOpenSettlementQuality + ? "open_settlement_quality_from_60_62_76_balances_without_payment_terms" + : null, + next_required_evidence: "reviewed_payment_term_or_due_date_route_with_contractual_due_dates_and_settlement_status", + must_not_claim: "confirmed_overdue_debt_credit_risk_or_due_date_aging" + }); + } + if (missing.has("inventory_position") || + missing.has("inventory_turnover_quality") || + missing.has("inventory_liquidity_quality") || + missing.has("inventory_reserve_liquidation_quality")) { + pushUnique({ + family: "inventory_reserve_liquidation_quality", + current_status: input.inventoryStalenessRiskProxy + ? "proxy_only_currently" + : input.inventoryTurnoverProxy || input.inventoryPosition + ? "proxy_only_currently" + : "not_checked_in_current_turn", + current_supported_evidence: input.inventoryStalenessRiskProxy + ? "inventory_staleness_risk_proxy_from_purchase_age_and_sales_to_stock" + : input.inventoryTurnoverProxy + ? "inventory_turnover_proxy_from_sales_documents_and_stock_balance" + : input.inventoryPosition + ? "inventory_on_hand_snapshot_without_reserve_writeoff_or_liquidation_evidence" + : null, + next_required_evidence: "reviewed_inventory_quality_route_with_reserves_writeoffs_obsolescence_and_liquidation_value", + must_not_claim: "confirmed_obsolete_stock_reserve_writeoff_or_liquidation_value" + }); + } + if (input.hasSupplierConcentrationSignal) { + pushUnique({ + family: "vendor_risk_procurement_quality", + current_status: "proxy_only_currently", + current_supported_evidence: "supplier_concentration_proxy_from_confirmed_outgoing_payment_rows", + next_required_evidence: "reviewed_vendor_risk_route_with_supplier_reliability_terms_quality_and_full_expense_structure", + must_not_claim: "vendor_risk_procurement_quality_supplier_reliability_or_full_expense_structure" + }); + } + return items; +} function deriveBusinessOverview(input) { const incoming = deriveValueFlowSideSummary(input.incomingResult); const outgoing = deriveValueFlowSideSummary(input.outgoingResult); @@ -2809,6 +2878,30 @@ function deriveBusinessOverview(input) { } const netAmount = incoming.total_amount - outgoing.total_amount; const hasBusinessOverviewProfileSignal = Boolean(documentActivityProfile || counterpartyProfile || contractUsageProfile); + const missingSignalFamilies = [ + tradingMarginProxy ? "accounting_profit_margin" : "profit_margin", + debtPosition ? null : "debt_position", + debtOpenSettlementQuality ? "debt_due_date_aging_quality" : "debt_open_settlement_quality", + taxPosition ? null : "tax_position", + inventoryPosition + ? inventoryStalenessRiskProxy + ? "inventory_reserve_liquidation_quality" + : inventoryTurnoverProxy + ? "inventory_liquidity_quality" + : "inventory_turnover_quality" + : "inventory_position", + inventoryPosition?.aging_signal ? null : "inventory_aging_quality" + ].filter((item) => Boolean(item)); + const missingProofFamilies = buildBusinessOverviewMissingProofFamilies({ + missingSignalFamilies, + tradingMarginProxy, + debtOpenSettlementQuality, + debtStalenessRiskProxy, + inventoryPosition, + inventoryTurnoverProxy, + inventoryStalenessRiskProxy, + hasSupplierConcentrationSignal: (rankedOutgoing?.ranked_values.length ?? 0) > 0 + }); return { organization_scope: input.organizationScope, period_scope: input.periodScope, @@ -2834,20 +2927,8 @@ function deriveBusinessOverview(input) { contract_usage_profile: contractUsageProfile, coverage_limited_by_probe_limit: incoming.coverage_limited_by_probe_limit || outgoing.coverage_limited_by_probe_limit, checked_signal_count: checkedSignalCount, - missing_signal_families: [ - tradingMarginProxy ? "accounting_profit_margin" : "profit_margin", - debtPosition ? null : "debt_position", - debtOpenSettlementQuality ? "debt_due_date_aging_quality" : "debt_open_settlement_quality", - taxPosition ? null : "tax_position", - inventoryPosition - ? inventoryStalenessRiskProxy - ? "inventory_reserve_liquidation_quality" - : inventoryTurnoverProxy - ? "inventory_liquidity_quality" - : "inventory_turnover_quality" - : "inventory_position", - inventoryPosition?.aging_signal ? null : "inventory_aging_quality" - ].filter((item) => Boolean(item)), + missing_signal_families: missingSignalFamilies, + missing_proof_families: missingProofFamilies, inference_basis: hasBusinessOverviewProfileSignal || inventoryPosition ? "business_overview_from_confirmed_1c_multi_family_rows" : debtOpenSettlementQuality @@ -4177,6 +4258,9 @@ async function executeAssistantMcpDiscoveryPilot(planner, deps = DEFAULT_DEPS) { if (derivedBusinessOverview.inventory_staleness_risk_proxy) { pushReason(reasonCodes, "pilot_derived_business_overview_inventory_staleness_risk_proxy_from_confirmed_rows"); } + if (derivedBusinessOverview.missing_proof_families.length > 0) { + pushReason(reasonCodes, "pilot_business_overview_missing_proof_families_recorded"); + } } const sourceRowsSummary = summarizeBusinessOverviewRows({ incomingResult, diff --git a/llm_normalizer/backend/src/services/assistantMcpDiscoveryAnswerAdapter.ts b/llm_normalizer/backend/src/services/assistantMcpDiscoveryAnswerAdapter.ts index 26eff98..60f4aa0 100644 --- a/llm_normalizer/backend/src/services/assistantMcpDiscoveryAnswerAdapter.ts +++ b/llm_normalizer/backend/src/services/assistantMcpDiscoveryAnswerAdapter.ts @@ -748,6 +748,9 @@ function buildMustNotClaim(pilot: AssistantMcpDiscoveryPilotExecutionContract): claims.push("Do not present an inventory snapshot or purchase-date aging signal as turnover, obsolescence, liquidation value, or full inventory health."); claims.push("Do not present business overview inventory turnover proxy as full inventory liquidity, FIFO turnover, obsolescence analysis, or liquidation value."); claims.push("Do not present business overview inventory staleness risk proxy as confirmed obsolete stock, reserve, write-off, or liquidation value."); + if (pilot.derived_business_overview?.missing_proof_families?.length) { + claims.push("Do not present business overview missing proof families as checked, executed, or confirmed routes."); + } claims.push("Do not expose business_overview_route_template_v1 or MCP primitive names in the user answer."); } if (pilot.derived_ranked_value_flow) { @@ -1569,6 +1572,9 @@ export function buildAssistantMcpDiscoveryAnswerDraft( if (pilot.derived_business_overview?.inventory_staleness_risk_proxy) { pushReason(reasonCodes, "answer_contains_business_overview_inventory_staleness_risk_proxy"); } + if (pilot.derived_business_overview?.missing_proof_families?.length) { + pushReason(reasonCodes, "answer_contains_business_overview_missing_proof_ledger"); + } const confirmedLines = businessOverviewLines.length > 0 ? businessOverviewLines : pilot.derived_ranked_value_flow && derivedValueLine diff --git a/llm_normalizer/backend/src/services/assistantMcpDiscoveryPilotExecutor.ts b/llm_normalizer/backend/src/services/assistantMcpDiscoveryPilotExecutor.ts index 77af3da..6ed9596 100644 --- a/llm_normalizer/backend/src/services/assistantMcpDiscoveryPilotExecutor.ts +++ b/llm_normalizer/backend/src/services/assistantMcpDiscoveryPilotExecutor.ts @@ -182,6 +182,23 @@ export interface AssistantMcpDiscoveryDerivedBusinessOverviewContractUsageProfil inference_basis: "contract_usage_overview_confirmed_1c_rows"; } +export type AssistantMcpDiscoveryBusinessOverviewMissingProofFamilyId = + | "accounting_profit_margin" + | "debt_due_date_aging_quality" + | "inventory_reserve_liquidation_quality" + | "vendor_risk_procurement_quality"; + +export interface AssistantMcpDiscoveryBusinessOverviewMissingProofFamily { + family: AssistantMcpDiscoveryBusinessOverviewMissingProofFamilyId; + current_status: + | "not_checked_in_current_turn" + | "proxy_only_currently" + | "reviewed_route_not_wired"; + current_supported_evidence: string | null; + next_required_evidence: string; + must_not_claim: string; +} + export interface AssistantMcpDiscoveryDerivedBidirectionalValueFlow { counterparty: string | null; period_scope: string | null; @@ -224,6 +241,7 @@ export interface AssistantMcpDiscoveryDerivedBusinessOverview { coverage_limited_by_probe_limit: boolean; checked_signal_count: number; missing_signal_families: string[]; + missing_proof_families: AssistantMcpDiscoveryBusinessOverviewMissingProofFamily[]; inference_basis: | "business_overview_from_confirmed_1c_money_and_activity_rows" | "business_overview_from_confirmed_1c_money_activity_and_tax_rows" @@ -3717,6 +3735,92 @@ function inventoryStalenessRiskBandRu( return "низкий видимый риск"; } +function buildBusinessOverviewMissingProofFamilies(input: { + missingSignalFamilies: string[]; + tradingMarginProxy: AssistantMcpDiscoveryDerivedBusinessOverviewTradingMarginProxy | null; + debtOpenSettlementQuality: AssistantMcpDiscoveryDerivedBusinessOverviewDebtOpenSettlementQuality | null; + debtStalenessRiskProxy: AssistantMcpDiscoveryDerivedBusinessOverviewDebtStalenessRiskProxy | null; + inventoryPosition: AssistantMcpDiscoveryDerivedBusinessOverviewInventoryPosition | null; + inventoryTurnoverProxy: AssistantMcpDiscoveryDerivedBusinessOverviewInventoryTurnoverProxy | null; + inventoryStalenessRiskProxy: AssistantMcpDiscoveryDerivedBusinessOverviewInventoryStalenessRiskProxy | null; + hasSupplierConcentrationSignal: boolean; +}): AssistantMcpDiscoveryBusinessOverviewMissingProofFamily[] { + const missing = new Set(input.missingSignalFamilies); + const items: AssistantMcpDiscoveryBusinessOverviewMissingProofFamily[] = []; + const pushUnique = (item: AssistantMcpDiscoveryBusinessOverviewMissingProofFamily) => { + if (!items.some((existing) => existing.family === item.family)) { + items.push(item); + } + }; + + if (missing.has("profit_margin") || missing.has("accounting_profit_margin")) { + pushUnique({ + family: "accounting_profit_margin", + current_status: input.tradingMarginProxy ? "proxy_only_currently" : "reviewed_route_not_wired", + current_supported_evidence: input.tradingMarginProxy + ? "trading_margin_proxy_from_sales_and_purchase_document_rows" + : null, + next_required_evidence: "reviewed_pnl_route_with_cost_of_sales_expenses_period_close_and_financial_result", + must_not_claim: "clean_profit_accounting_margin_or_full_pnl" + }); + } + + if (missing.has("debt_due_date_aging_quality") || missing.has("debt_open_settlement_quality")) { + pushUnique({ + family: "debt_due_date_aging_quality", + current_status: input.debtStalenessRiskProxy + ? "proxy_only_currently" + : input.debtOpenSettlementQuality + ? "proxy_only_currently" + : "not_checked_in_current_turn", + current_supported_evidence: input.debtStalenessRiskProxy + ? "debt_staleness_risk_proxy_from_contract_dates_and_open_balance_concentration" + : input.debtOpenSettlementQuality + ? "open_settlement_quality_from_60_62_76_balances_without_payment_terms" + : null, + next_required_evidence: "reviewed_payment_term_or_due_date_route_with_contractual_due_dates_and_settlement_status", + must_not_claim: "confirmed_overdue_debt_credit_risk_or_due_date_aging" + }); + } + + if ( + missing.has("inventory_position") || + missing.has("inventory_turnover_quality") || + missing.has("inventory_liquidity_quality") || + missing.has("inventory_reserve_liquidation_quality") + ) { + pushUnique({ + family: "inventory_reserve_liquidation_quality", + current_status: input.inventoryStalenessRiskProxy + ? "proxy_only_currently" + : input.inventoryTurnoverProxy || input.inventoryPosition + ? "proxy_only_currently" + : "not_checked_in_current_turn", + current_supported_evidence: input.inventoryStalenessRiskProxy + ? "inventory_staleness_risk_proxy_from_purchase_age_and_sales_to_stock" + : input.inventoryTurnoverProxy + ? "inventory_turnover_proxy_from_sales_documents_and_stock_balance" + : input.inventoryPosition + ? "inventory_on_hand_snapshot_without_reserve_writeoff_or_liquidation_evidence" + : null, + next_required_evidence: "reviewed_inventory_quality_route_with_reserves_writeoffs_obsolescence_and_liquidation_value", + must_not_claim: "confirmed_obsolete_stock_reserve_writeoff_or_liquidation_value" + }); + } + + if (input.hasSupplierConcentrationSignal) { + pushUnique({ + family: "vendor_risk_procurement_quality", + current_status: "proxy_only_currently", + current_supported_evidence: "supplier_concentration_proxy_from_confirmed_outgoing_payment_rows", + next_required_evidence: "reviewed_vendor_risk_route_with_supplier_reliability_terms_quality_and_full_expense_structure", + must_not_claim: "vendor_risk_procurement_quality_supplier_reliability_or_full_expense_structure" + }); + } + + return items; +} + function deriveBusinessOverview(input: { incomingResult: AssistantMcpDiscoveryCoverageAwareQueryResult | null; outgoingResult: AssistantMcpDiscoveryCoverageAwareQueryResult | null; @@ -3816,6 +3920,30 @@ function deriveBusinessOverview(input: { const hasBusinessOverviewProfileSignal = Boolean( documentActivityProfile || counterpartyProfile || contractUsageProfile ); + const missingSignalFamilies = [ + tradingMarginProxy ? "accounting_profit_margin" : "profit_margin", + debtPosition ? null : "debt_position", + debtOpenSettlementQuality ? "debt_due_date_aging_quality" : "debt_open_settlement_quality", + taxPosition ? null : "tax_position", + inventoryPosition + ? inventoryStalenessRiskProxy + ? "inventory_reserve_liquidation_quality" + : inventoryTurnoverProxy + ? "inventory_liquidity_quality" + : "inventory_turnover_quality" + : "inventory_position", + inventoryPosition?.aging_signal ? null : "inventory_aging_quality" + ].filter((item): item is string => Boolean(item)); + const missingProofFamilies = buildBusinessOverviewMissingProofFamilies({ + missingSignalFamilies, + tradingMarginProxy, + debtOpenSettlementQuality, + debtStalenessRiskProxy, + inventoryPosition, + inventoryTurnoverProxy, + inventoryStalenessRiskProxy, + hasSupplierConcentrationSignal: (rankedOutgoing?.ranked_values.length ?? 0) > 0 + }); return { organization_scope: input.organizationScope, period_scope: input.periodScope, @@ -3842,20 +3970,8 @@ function deriveBusinessOverview(input: { coverage_limited_by_probe_limit: incoming.coverage_limited_by_probe_limit || outgoing.coverage_limited_by_probe_limit, checked_signal_count: checkedSignalCount, - missing_signal_families: [ - tradingMarginProxy ? "accounting_profit_margin" : "profit_margin", - debtPosition ? null : "debt_position", - debtOpenSettlementQuality ? "debt_due_date_aging_quality" : "debt_open_settlement_quality", - taxPosition ? null : "tax_position", - inventoryPosition - ? inventoryStalenessRiskProxy - ? "inventory_reserve_liquidation_quality" - : inventoryTurnoverProxy - ? "inventory_liquidity_quality" - : "inventory_turnover_quality" - : "inventory_position", - inventoryPosition?.aging_signal ? null : "inventory_aging_quality" - ].filter((item): item is string => Boolean(item)), + missing_signal_families: missingSignalFamilies, + missing_proof_families: missingProofFamilies, inference_basis: hasBusinessOverviewProfileSignal || inventoryPosition ? "business_overview_from_confirmed_1c_multi_family_rows" @@ -5359,6 +5475,9 @@ export async function executeAssistantMcpDiscoveryPilot( if (derivedBusinessOverview.inventory_staleness_risk_proxy) { pushReason(reasonCodes, "pilot_derived_business_overview_inventory_staleness_risk_proxy_from_confirmed_rows"); } + if (derivedBusinessOverview.missing_proof_families.length > 0) { + pushReason(reasonCodes, "pilot_business_overview_missing_proof_families_recorded"); + } } const sourceRowsSummary = summarizeBusinessOverviewRows({ incomingResult, diff --git a/llm_normalizer/backend/tests/assistantMcpDiscoveryAnswerAdapter.test.ts b/llm_normalizer/backend/tests/assistantMcpDiscoveryAnswerAdapter.test.ts index 77198b2..627bca4 100644 --- a/llm_normalizer/backend/tests/assistantMcpDiscoveryAnswerAdapter.test.ts +++ b/llm_normalizer/backend/tests/assistantMcpDiscoveryAnswerAdapter.test.ts @@ -290,12 +290,14 @@ describe("assistant MCP discovery answer adapter", () => { expect(draft.must_not_claim).toContain("Do not present business overview supplier concentration as vendor-risk audit, procurement quality, or full expense structure."); expect(draft.must_not_claim).toContain("Do not present business overview document/account-section activity profile as process quality, accounting correctness, or completeness of all 1C activity."); expect(draft.must_not_claim).toContain("Do not present business overview counterparty or contract profile as CRM quality, counterparty due diligence, contract-risk audit, or legal completeness."); + expect(draft.must_not_claim).toContain("Do not present business overview missing proof families as checked, executed, or confirmed routes."); expect(draft.reason_codes).toContain("answer_contains_business_overview"); expect(draft.reason_codes).toContain("answer_contains_business_overview_supplier_concentration"); expect(draft.reason_codes).toContain("answer_contains_business_overview_yearly_operating_breakdown"); expect(draft.reason_codes).toContain("answer_contains_business_overview_document_activity_profile"); expect(draft.reason_codes).toContain("answer_contains_business_overview_counterparty_profile"); expect(draft.reason_codes).toContain("answer_contains_business_overview_contract_usage_profile"); + expect(draft.reason_codes).toContain("answer_contains_business_overview_missing_proof_ledger"); expect(draft.reason_codes).toContain("answer_contains_business_overview_analyst_synthesis"); }); diff --git a/llm_normalizer/backend/tests/assistantMcpDiscoveryPilotExecutor.test.ts b/llm_normalizer/backend/tests/assistantMcpDiscoveryPilotExecutor.test.ts index 8b58efd..febcbfc 100644 --- a/llm_normalizer/backend/tests/assistantMcpDiscoveryPilotExecutor.test.ts +++ b/llm_normalizer/backend/tests/assistantMcpDiscoveryPilotExecutor.test.ts @@ -258,6 +258,20 @@ describe("assistant MCP discovery pilot executor", () => { unused_contracts: 372, used_contract_share_pct: 28.46 }); + expect(result.derived_business_overview?.missing_proof_families).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + family: "accounting_profit_margin", + current_status: "reviewed_route_not_wired", + current_supported_evidence: null + }), + expect.objectContaining({ + family: "vendor_risk_procurement_quality", + current_status: "proxy_only_currently", + current_supported_evidence: "supplier_concentration_proxy_from_confirmed_outgoing_payment_rows" + }) + ]) + ); expect(result.evidence.confirmed_facts.join("\n")).toContain("В 1С подтверждены входящие поступления"); expect(result.evidence.confirmed_facts.join("\n")).toContain("Самый крупный подтвержденный поставщик"); expect(result.evidence.confirmed_facts.join("\n")).toContain("Годовая раскладка операционного денежного потока"); @@ -275,6 +289,7 @@ describe("assistant MCP discovery pilot executor", () => { expect(result.reason_codes).toContain("pilot_derived_business_overview_document_activity_profile_from_confirmed_rows"); expect(result.reason_codes).toContain("pilot_derived_business_overview_counterparty_profile_from_confirmed_rows"); expect(result.reason_codes).toContain("pilot_derived_business_overview_contract_usage_profile_from_confirmed_rows"); + expect(result.reason_codes).toContain("pilot_business_overview_missing_proof_families_recorded"); expect(deps.executeAddressMcpQuery).toHaveBeenCalledTimes(6); }); @@ -370,6 +385,15 @@ describe("assistant MCP discovery pilot executor", () => { expect(result.derived_business_overview?.missing_signal_families).not.toContain("tax_position"); expect(result.derived_business_overview?.missing_signal_families).not.toContain("profit_margin"); expect(result.derived_business_overview?.missing_signal_families).toContain("accounting_profit_margin"); + expect(result.derived_business_overview?.missing_proof_families).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + family: "accounting_profit_margin", + current_status: "proxy_only_currently", + current_supported_evidence: "trading_margin_proxy_from_sales_and_purchase_document_rows" + }) + ]) + ); expect(result.evidence.confirmed_facts.join("\n")).toContain("НДС-позиция за 2020 подтверждена"); expect(result.evidence.confirmed_facts.join("\n")).toContain("Торговый margin proxy за 2020"); expect(result.evidence.unknown_facts.join("\n")).toContain("Чистая прибыль");