From 82cc5cfd1abefb955dca48acc48af97122f6b684 Mon Sep 17 00:00:00 2001 From: dctouch Date: Sun, 19 Apr 2026 17:44:37 +0300 Subject: [PATCH] =?UTF-8?q?=D0=90=D1=80=D1=85=D0=B8=D1=82=D0=B5=D0=BA?= =?UTF-8?q?=D1=82=D1=83=D1=80=D0=B0:=20=D1=86=D0=B5=D0=BD=D1=82=D1=80?= =?UTF-8?q?=D0=B0=D0=BB=D0=B8=D0=B7=D0=BE=D0=B2=D0=B0=D1=82=D1=8C=20naviga?= =?UTF-8?q?tion=20focus=20authority=20=D0=B2=20continuity=20policy=20?= =?UTF-8?q?=D0=B8=20=D1=81=D0=B4=D0=B5=D0=BB=D0=B0=D1=82=D1=8C=20phase15?= =?UTF-8?q?=20replay=20time-stable?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../08 - current_status_audit_2026-04-17.md | 6 +- ...ontinuity_stabilization_plan_2026-04-17.md | 34 +++++++++++ ..._multidomain_readiness_audit_2026-04-18.md | 53 ++++++++++++----- .../11 - architecture_turnaround/README.md | 5 +- ...ss_phase15_answer_inspection_followup.json | 4 +- .../services/assistantContinuityPolicy.js | 18 ++++++ .../services/assistantTransitionPolicy.js | 43 ++++---------- .../src/services/assistantContinuityPolicy.ts | 35 +++++++++++ .../src/services/assistantTransitionPolicy.ts | 58 ++++++------------- .../tests/assistantContinuityPolicy.test.ts | 35 +++++++++++ 10 files changed, 199 insertions(+), 92 deletions(-) diff --git a/docs/ARCH/11 - architecture_turnaround/08 - current_status_audit_2026-04-17.md b/docs/ARCH/11 - architecture_turnaround/08 - current_status_audit_2026-04-17.md index da19d0b..6b38566 100644 --- a/docs/ARCH/11 - architecture_turnaround/08 - current_status_audit_2026-04-17.md +++ b/docs/ARCH/11 - architecture_turnaround/08 - current_status_audit_2026-04-17.md @@ -23,7 +23,7 @@ This snapshot is based on: - live replay comparison between: - `address_truth_harness_phase12_wider_saved_session_pool_live_20260419_rerun16` - `address_truth_harness_phase14_counterparty_tail_resume_live_20260418_rerun2` - - `address_truth_harness_phase15_answer_inspection_followup_live_20260418_rerun8` + - `address_truth_harness_phase15_answer_inspection_followup_live_20260419_rerun11` - `address_truth_harness_phase16_multicompany_late_pivot_live_20260419_rerun10` - `address_truth_harness_phase17_clarification_resume_and_counterparty_tail_live_20260419_rerun5` - [10 - regression_breakpoint_analysis_2026-04-17.md](./10%20-%20regression_breakpoint_analysis_2026-04-17.md) @@ -33,8 +33,8 @@ This snapshot is based on: Latest graph rebuild: -- `5371 nodes` -- `11523 edges` +- `5372 nodes` +- `11525 edges` - `135 communities` Most relevant current god nodes for turnaround `11`: diff --git a/docs/ARCH/11 - architecture_turnaround/11 - continuity_stabilization_plan_2026-04-17.md b/docs/ARCH/11 - architecture_turnaround/11 - continuity_stabilization_plan_2026-04-17.md index 84fe879..42f86c4 100644 --- a/docs/ARCH/11 - architecture_turnaround/11 - continuity_stabilization_plan_2026-04-17.md +++ b/docs/ARCH/11 - architecture_turnaround/11 - continuity_stabilization_plan_2026-04-17.md @@ -520,6 +520,12 @@ Latest phase17 clarification-resume evidence after the current replay hardening: - `decomposeStage` no longer hydrates inventory follow-up filters with the selected organization alias as a fake counterparty anchor, so hidden carryover state stays truthful; - targeted `assistantRoutePolicy`, `assistantAddressFollowupContext`, `addressImplicitOrganizationScope`, and `addressFollowupTemporalRegression` suites are green after the fix, and backend build stays green; - live replay `address_truth_harness_phase17_clarification_resume_and_counterparty_tail_live_20260419_rerun5` is accepted `10/10`, which is the current proof that clarification-resume, historical inventory continuation, and short counterparty-tail retarget are now semantically clean on a non-flagship saved-session path. +- the next continuity-authority pass now centralizes navigation focus-state parsing for the hot selected-object path: + - `assistantContinuityPolicy` now owns `resolveNavigationSessionContextState(...)`, which extracts `date scope`, `organization`, `active focus object`, and `active result set id` from navigation state through one shared helper; + - `assistantTransitionPolicy` no longer reconstructs `session_context.active_focus_object` in multiple local branches for purchase-date VAT bridge detection and selected-item carryover setup; + - this matters because selected-item continuity, purchase-date VAT bridge, and navigation-driven focus-object hints now read one shared navigation-state interpretation instead of three local parsers inside the transition hot path; + - targeted `assistantContinuityPolicy` and `assistantTransitionPolicy` suites are green after the move (`37/37`), backend build is green, and graphify was rebuilt on the updated codebase; + - phase15 replay has also been made time-stable for the current snapshot step, and live replay `address_truth_harness_phase15_answer_inspection_followup_live_20260419_rerun11` is accepted `9/9`, which is the semantic proof that the selected-item / answer-inspection / VAT bridge contour still survives after the focus-authority convergence pass. ## Next Execution Slice (2026-04-19) @@ -558,6 +564,34 @@ Current remaining heavy fronts before low-risk domain expansion: - complete the missing contour for counterparty shipped-goods / service extraction instead of relying on honest-but-limited document-list fallback; - keep answer shaping as secondary debt only where it materially affects acceptance, not as the primary architecture frontier. +Execution framing for the next level: + +- the working target is now `90%+ pre-multidomain readiness` before controlled domain expansion begins; +- the current honest level is `~78%`, so the remaining gap should be treated as four large iterations, not as a few cosmetic follow-ups. + +Current four-iteration plan: + +1. `Iteration 1 / Continuity authority completion` + Goal: + finish convergence toward one owner for `organization / date / root frame / focus object / clarification state` across the hot runtime path. + Expected gain: + `~+4%`. +2. `Iteration 2 / Wider saved-session replay pool` + Goal: + widen replay breadth beyond the current flagship + phase14 + phase15 + phase16 + phase17 family set. + Expected gain: + `~+4%`. +3. `Iteration 3 / Coordinator pressure reduction` + Goal: + reduce control-plane overload in `assistantService.ts`, `addressQueryService.ts`, and adjacent top-level orchestration seams. + Expected gain: + `~+2-3%`. +4. `Iteration 4 / Critical domain-enablement gaps + final replay proof` + Goal: + close the remaining high-value business gaps such as `counterparty shipped goods/service` and confirm the result under replay until the system crosses the `90%` threshold honestly. + Expected gain: + `~+1-2%`. + ## Ready Signal The project can leave the current breakpoint when: diff --git a/docs/ARCH/11 - architecture_turnaround/13 - pre_multidomain_readiness_audit_2026-04-18.md b/docs/ARCH/11 - architecture_turnaround/13 - pre_multidomain_readiness_audit_2026-04-18.md index 5655332..2554dc0 100644 --- a/docs/ARCH/11 - architecture_turnaround/13 - pre_multidomain_readiness_audit_2026-04-18.md +++ b/docs/ARCH/11 - architecture_turnaround/13 - pre_multidomain_readiness_audit_2026-04-18.md @@ -26,7 +26,7 @@ Current confidence snapshot: - turnaround implementation progress: `~96%` - exit-from-danger-zone readiness: `~91%` - pre-multidomain readiness: `~78%` -- latest graph snapshot: `5371 nodes`, `11523 edges`, `135 communities` +- latest graph snapshot: `5372 nodes`, `11525 edges`, `135 communities` ## What Is Already True @@ -139,7 +139,13 @@ The system should not be considered ready for the next level until all of the fo ## Recommended Next Execution Sequence -### Pass 18. Continuity authority completion +The current planning assumption is: + +- `90%+ pre-multidomain readiness` is the practical threshold for starting controlled domain expansion; +- the project is currently around `78%`; +- therefore the remaining gap should be treated as roughly four large iterations rather than as a handful of cosmetic fixes. + +### Iteration 1 / Pass 18. Continuity authority completion Goal: @@ -149,7 +155,11 @@ Target: - transition / route / clarification should consume one continuity snapshot before making divergent decisions. -### Pass 19. Wider saved-session acceptance pool +Expected readiness gain: + +- `~+4%` toward pre-multidomain readiness. + +### Iteration 2 / Pass 19. Wider saved-session acceptance pool Goal: @@ -159,17 +169,11 @@ Target: - several saved sessions covering inventory, VAT, counterparty, payables/receivables, meta interrupts, and cross-domain pivots beyond phase12 / phase16 / phase17. -### Pass 20. Human answer shaping cleanup +Expected readiness gain: -Goal: +- `~+4%` toward pre-multidomain readiness. -- remove the remaining mechanical, template-heavy feel from long exact answers. - -Target: - -- product-quality business answers on already-correct truth paths. - -### Pass 21. Coordinator pressure reduction +### Iteration 3 / Pass 20. Coordinator pressure reduction Goal: @@ -177,7 +181,30 @@ Goal: Target: -- less policy/service glue concentrated in `assistantService.ts` and adjacent god-modules. +- less policy/service glue concentrated in `assistantService.ts`, `addressQueryService.ts`, and adjacent coordinator-heavy seams. + +Expected readiness gain: + +- `~+2-3%` toward pre-multidomain readiness. + +### Iteration 4 / Pass 21. Critical domain-enablement gaps and final acceptance proof + +Goal: + +- close the remaining high-value domain gaps that still block truthful expansion claims and then prove the result on replay. + +Target: + +- missing business contours such as `counterparty shipped goods/service`, plus the final replay confirmation that the system is over the `90%` threshold. + +Expected readiness gain: + +- `~+1-2%` toward pre-multidomain readiness. + +Secondary cleanup after those four iterations: + +- human answer shaping cleanup where it materially affects acceptance; +- further quality polish that should not be confused with the core readiness gate. ## Final Statement diff --git a/docs/ARCH/11 - architecture_turnaround/README.md b/docs/ARCH/11 - architecture_turnaround/README.md index d80234b..b1f30d8 100644 --- a/docs/ARCH/11 - architecture_turnaround/README.md +++ b/docs/ARCH/11 - architecture_turnaround/README.md @@ -48,7 +48,7 @@ Current honest status: - turnaround implementation progress: `~96%` - exit-from-danger-zone readiness: `~91%` - pre-multidomain readiness: `~78%` -- graph snapshot after latest rebuild: `5371 nodes`, `11523 edges`, `135 communities` +- graph snapshot after latest rebuild: `5372 nodes`, `11525 edges`, `135 communities` - current breakpoint: - the validated hot paths are no longer structurally broken; - flagship continuity collapse is no longer the primary risk; @@ -65,7 +65,7 @@ Latest live proof now includes: - `address_truth_harness_phase12_wider_saved_session_pool_live_20260419_rerun16` accepted `20/20` - `address_truth_harness_phase14_counterparty_tail_resume_live_20260418_rerun2` accepted `10/10` -- `address_truth_harness_phase15_answer_inspection_followup_live_20260418_rerun8` accepted `9/9` +- `address_truth_harness_phase15_answer_inspection_followup_live_20260419_rerun11` accepted `9/9` - `address_truth_harness_phase16_multicompany_late_pivot_live_20260419_rerun10` accepted - `address_truth_harness_phase17_clarification_resume_and_counterparty_tail_live_20260419_rerun5` accepted `10/10` @@ -74,6 +74,7 @@ Current architectural reading: - the system is already materially past the dangerous regression breakpoint; - it is now safe for continued architecture hardening and controlled domain-by-domain enablement under replay gates; - it is now materially closer to pre-multidomain stability, but still not safe to declare broad low-risk multi-domain expansion. +- the practical next target is now `90%+ pre-multidomain readiness`, and the remaining gap should be treated as four large architecture iterations rather than as cosmetic cleanup. For the detailed audit, current percentages, and remaining debt, read: diff --git a/docs/orchestration/address_truth_harness_phase15_answer_inspection_followup.json b/docs/orchestration/address_truth_harness_phase15_answer_inspection_followup.json index 533eef3..6777924 100644 --- a/docs/orchestration/address_truth_harness_phase15_answer_inspection_followup.json +++ b/docs/orchestration/address_truth_harness_phase15_answer_inspection_followup.json @@ -50,12 +50,12 @@ ], "expected_recipe": "address_inventory_on_hand_as_of_date_v1", "required_filters": { - "as_of_date": "2026-04-18", + "as_of_date": "{{runtime.today_iso}}", "organization": "ООО Альтернатива Плюс" }, "required_direct_answer_patterns_any": [ "(?i)на складе|остат", - "18\\.04\\.2026" + "{{runtime.today_dot_regex}}" ], "criticality": "critical", "semantic_tags": [ diff --git a/llm_normalizer/backend/dist/services/assistantContinuityPolicy.js b/llm_normalizer/backend/dist/services/assistantContinuityPolicy.js index 32d8cbe..38e9af0 100644 --- a/llm_normalizer/backend/dist/services/assistantContinuityPolicy.js +++ b/llm_normalizer/backend/dist/services/assistantContinuityPolicy.js @@ -7,6 +7,7 @@ exports.readAddressDebugOrganization = readAddressDebugOrganization; exports.readAddressDebugScopedDate = readAddressDebugScopedDate; exports.readAddressDebugTemporalScope = readAddressDebugTemporalScope; exports.resolveAddressDebugAnchorContext = resolveAddressDebugAnchorContext; +exports.resolveNavigationSessionContextState = resolveNavigationSessionContextState; exports.resolveAddressDebugContextFacts = resolveAddressDebugContextFacts; exports.resolveAddressDebugCarryoverFilters = resolveAddressDebugCarryoverFilters; exports.hydrateInventoryRootFrameState = hydrateInventoryRootFrameState; @@ -127,6 +128,23 @@ function resolveAddressDebugAnchorContext(debug, toNonEmptyString = fallbackToNo anchorValue: null }; } +function resolveNavigationSessionContextState(addressNavigationState, toNonEmptyString = fallbackToNonEmptyString, normalizeOrganizationScopeValue = normalizeOrganizationScopeDefault) { + const sessionContext = toRecordObject(toRecordObject(addressNavigationState)?.session_context); + const rawFocusObject = toRecordObject(sessionContext?.active_focus_object); + const focusObject = rawFocusObject + ? { + objectType: toNonEmptyString(rawFocusObject.object_type), + label: toNonEmptyString(rawFocusObject.label), + provenanceResultSetId: toNonEmptyString(rawFocusObject.provenance_result_set_id) + } + : null; + return { + dateScope: toRecordObject(sessionContext?.date_scope), + organization: normalizeOrganizationScopeValue(sessionContext?.organization_scope), + focusObject, + activeResultSetId: toNonEmptyString(sessionContext?.active_result_set_id) + }; +} function resolveAddressDebugContextFacts(debug, toNonEmptyString = fallbackToNonEmptyString) { return { item: readAddressDebugItem(debug, toNonEmptyString), diff --git a/llm_normalizer/backend/dist/services/assistantTransitionPolicy.js b/llm_normalizer/backend/dist/services/assistantTransitionPolicy.js index c3118da..e596a5a 100644 --- a/llm_normalizer/backend/dist/services/assistantTransitionPolicy.js +++ b/llm_normalizer/backend/dist/services/assistantTransitionPolicy.js @@ -104,17 +104,9 @@ function createAssistantTransitionPolicy(deps) { return computeMonthWindowFromIso(explicitFirstDateIso); } } - const sessionContext = addressNavigationState && - typeof addressNavigationState === "object" && - addressNavigationState.session_context && - typeof addressNavigationState.session_context === "object" - ? addressNavigationState.session_context - : null; - const focusObject = sessionContext && typeof sessionContext.active_focus_object === "object" - ? sessionContext.active_focus_object - : null; - const preferredResultSetId = deps.toNonEmptyString(focusObject?.provenance_result_set_id) ?? - deps.toNonEmptyString(sessionContext?.active_result_set_id); + const navigationSessionState = (0, assistantContinuityPolicy_1.resolveNavigationSessionContextState)(addressNavigationState, deps.toNonEmptyString, deps.normalizeOrganizationScopeValue); + const focusObject = navigationSessionState.focusObject; + const preferredResultSetId = deps.toNonEmptyString(focusObject?.provenanceResultSetId) ?? navigationSessionState.activeResultSetId; const resultSets = Array.isArray(addressNavigationState?.result_sets) ? addressNavigationState.result_sets : []; const preferredResultSet = (preferredResultSetId ? resultSets.find((item) => deps.toNonEmptyString(item?.result_set_id) === preferredResultSetId) ?? null @@ -331,16 +323,10 @@ function createAssistantTransitionPolicy(deps) { ? deps.isImplicitAddressContinuationByLlm(alternateMessage, llmPreDecomposeMeta) : false)); const sourceIntentHint = deps.toNonEmptyString(carryoverSourceDebug?.detected_intent); - const navigationFocusObjectHint = addressNavigationState && - typeof addressNavigationState === "object" && - addressNavigationState.session_context && - typeof addressNavigationState.session_context === "object" && - addressNavigationState.session_context.active_focus_object && - typeof addressNavigationState.session_context.active_focus_object === "object" - ? addressNavigationState.session_context.active_focus_object - : null; + const navigationSessionState = (0, assistantContinuityPolicy_1.resolveNavigationSessionContextState)(addressNavigationState, deps.toNonEmptyString, deps.normalizeOrganizationScopeValue); + const navigationFocusObjectHint = navigationSessionState.focusObject; const hasNavigationInventoryItemFocusHint = Boolean(deps.toNonEmptyString(navigationFocusObjectHint?.label) && - deps.toNonEmptyString(navigationFocusObjectHint?.object_type) === "item" && + deps.toNonEmptyString(navigationFocusObjectHint?.objectType) === "item" && (sourceIntentHint === "inventory_on_hand_as_of_date" || sourceIntentHint === "inventory_supplier_stock_overlap_as_of_date" || deps.isInventorySelectedObjectIntent(sourceIntentHint))); @@ -499,19 +485,10 @@ function createAssistantTransitionPolicy(deps) { const previousAnchorContext = (0, assistantContinuityPolicy_1.resolveAddressDebugAnchorContext)(carryoverSourceDebug, deps.toNonEmptyString); let previousAnchorType = previousAnchorContext.anchorType; let previousAnchor = previousAnchorContext.anchorValue; - const navigationSessionContext = addressNavigationState && typeof addressNavigationState === "object" - ? addressNavigationState.session_context && typeof addressNavigationState.session_context === "object" - ? addressNavigationState.session_context - : null - : null; - const navigationDateScope = navigationSessionContext && typeof navigationSessionContext.date_scope === "object" - ? navigationSessionContext.date_scope - : null; - const navigationOrganization = deps.normalizeOrganizationScopeValue(navigationSessionContext?.organization_scope); - const navigationFocusObject = navigationSessionContext && typeof navigationSessionContext.active_focus_object === "object" - ? navigationSessionContext.active_focus_object - : null; - const navigationFocusObjectType = deps.toNonEmptyString(navigationFocusObject?.object_type); + const navigationDateScope = navigationSessionState.dateScope; + const navigationOrganization = navigationSessionState.organization; + const navigationFocusObject = navigationSessionState.focusObject; + const navigationFocusObjectType = deps.toNonEmptyString(navigationFocusObject?.objectType); const navigationFocusObjectLabel = deps.toNonEmptyString(navigationFocusObject?.label); const hasInventoryItemFocusCarryover = navigationFocusObjectType === "item" && navigationFocusObjectLabel && diff --git a/llm_normalizer/backend/src/services/assistantContinuityPolicy.ts b/llm_normalizer/backend/src/services/assistantContinuityPolicy.ts index 63b1b16..a765055 100644 --- a/llm_normalizer/backend/src/services/assistantContinuityPolicy.ts +++ b/llm_normalizer/backend/src/services/assistantContinuityPolicy.ts @@ -45,6 +45,19 @@ export interface AssistantNavigationDateScope { period_to?: unknown; } +export interface AssistantNavigationFocusObject { + objectType: string | null; + label: string | null; + provenanceResultSetId: string | null; +} + +export interface AssistantNavigationSessionContextState { + dateScope: AssistantNavigationDateScope | null; + organization: string | null; + focusObject: AssistantNavigationFocusObject | null; + activeResultSetId: string | null; +} + export interface AssistantAddressDebugAnchorContext { anchorType: string | null; anchorValue: string | null; @@ -203,6 +216,28 @@ export function resolveAddressDebugAnchorContext( }; } +export function resolveNavigationSessionContextState( + addressNavigationState: unknown, + toNonEmptyString: (value: unknown) => string | null = fallbackToNonEmptyString, + normalizeOrganizationScopeValue: (value: unknown) => string | null = normalizeOrganizationScopeDefault +): AssistantNavigationSessionContextState { + const sessionContext = toRecordObject(toRecordObject(addressNavigationState)?.session_context); + const rawFocusObject = toRecordObject(sessionContext?.active_focus_object); + const focusObject = rawFocusObject + ? { + objectType: toNonEmptyString(rawFocusObject.object_type), + label: toNonEmptyString(rawFocusObject.label), + provenanceResultSetId: toNonEmptyString(rawFocusObject.provenance_result_set_id) + } + : null; + return { + dateScope: toRecordObject(sessionContext?.date_scope) as AssistantNavigationDateScope | null, + organization: normalizeOrganizationScopeValue(sessionContext?.organization_scope), + focusObject, + activeResultSetId: toNonEmptyString(sessionContext?.active_result_set_id) + }; +} + export function resolveAddressDebugContextFacts( debug: Record | null, toNonEmptyString: (value: unknown) => string | null = fallbackToNonEmptyString diff --git a/llm_normalizer/backend/src/services/assistantTransitionPolicy.ts b/llm_normalizer/backend/src/services/assistantTransitionPolicy.ts index 80f0228..400057b 100644 --- a/llm_normalizer/backend/src/services/assistantTransitionPolicy.ts +++ b/llm_normalizer/backend/src/services/assistantTransitionPolicy.ts @@ -10,6 +10,7 @@ import { readAddressDebugFilters, readAddressDebugItem, readAddressDebugTemporalScope, + resolveNavigationSessionContextState, resolveAddressDebugCarryoverFilters, resolveAddressDebugAnchorContext, resolveDisplayedEntityFollowupRetarget, @@ -145,20 +146,14 @@ export function createAssistantTransitionPolicy(deps) { } } - const sessionContext = - addressNavigationState && - typeof addressNavigationState === "object" && - addressNavigationState.session_context && - typeof addressNavigationState.session_context === "object" - ? addressNavigationState.session_context - : null; - const focusObject = - sessionContext && typeof sessionContext.active_focus_object === "object" - ? sessionContext.active_focus_object - : null; + const navigationSessionState = resolveNavigationSessionContextState( + addressNavigationState, + deps.toNonEmptyString, + deps.normalizeOrganizationScopeValue + ); + const focusObject = navigationSessionState.focusObject; const preferredResultSetId = - deps.toNonEmptyString(focusObject?.provenance_result_set_id) ?? - deps.toNonEmptyString(sessionContext?.active_result_set_id); + deps.toNonEmptyString(focusObject?.provenanceResultSetId) ?? navigationSessionState.activeResultSetId; const resultSets = Array.isArray(addressNavigationState?.result_sets) ? addressNavigationState.result_sets : []; const preferredResultSet = (preferredResultSetId @@ -431,18 +426,15 @@ export function createAssistantTransitionPolicy(deps) { ? deps.isImplicitAddressContinuationByLlm(alternateMessage, llmPreDecomposeMeta) : false)); const sourceIntentHint = deps.toNonEmptyString(carryoverSourceDebug?.detected_intent); - const navigationFocusObjectHint = - addressNavigationState && - typeof addressNavigationState === "object" && - addressNavigationState.session_context && - typeof addressNavigationState.session_context === "object" && - addressNavigationState.session_context.active_focus_object && - typeof addressNavigationState.session_context.active_focus_object === "object" - ? addressNavigationState.session_context.active_focus_object - : null; + const navigationSessionState = resolveNavigationSessionContextState( + addressNavigationState, + deps.toNonEmptyString, + deps.normalizeOrganizationScopeValue + ); + const navigationFocusObjectHint = navigationSessionState.focusObject; const hasNavigationInventoryItemFocusHint = Boolean( deps.toNonEmptyString(navigationFocusObjectHint?.label) && - deps.toNonEmptyString(navigationFocusObjectHint?.object_type) === "item" && + deps.toNonEmptyString(navigationFocusObjectHint?.objectType) === "item" && (sourceIntentHint === "inventory_on_hand_as_of_date" || sourceIntentHint === "inventory_supplier_stock_overlap_as_of_date" || deps.isInventorySelectedObjectIntent(sourceIntentHint)) @@ -643,22 +635,10 @@ export function createAssistantTransitionPolicy(deps) { const previousAnchorContext = resolveAddressDebugAnchorContext(carryoverSourceDebug, deps.toNonEmptyString); let previousAnchorType = previousAnchorContext.anchorType; let previousAnchor = previousAnchorContext.anchorValue; - const navigationSessionContext = - addressNavigationState && typeof addressNavigationState === "object" - ? addressNavigationState.session_context && typeof addressNavigationState.session_context === "object" - ? addressNavigationState.session_context - : null - : null; - const navigationDateScope = - navigationSessionContext && typeof navigationSessionContext.date_scope === "object" - ? navigationSessionContext.date_scope - : null; - const navigationOrganization = deps.normalizeOrganizationScopeValue(navigationSessionContext?.organization_scope); - const navigationFocusObject = - navigationSessionContext && typeof navigationSessionContext.active_focus_object === "object" - ? navigationSessionContext.active_focus_object - : null; - const navigationFocusObjectType = deps.toNonEmptyString(navigationFocusObject?.object_type); + const navigationDateScope = navigationSessionState.dateScope; + const navigationOrganization = navigationSessionState.organization; + const navigationFocusObject = navigationSessionState.focusObject; + const navigationFocusObjectType = deps.toNonEmptyString(navigationFocusObject?.objectType); const navigationFocusObjectLabel = deps.toNonEmptyString(navigationFocusObject?.label); const hasInventoryItemFocusCarryover = navigationFocusObjectType === "item" && diff --git a/llm_normalizer/backend/tests/assistantContinuityPolicy.test.ts b/llm_normalizer/backend/tests/assistantContinuityPolicy.test.ts index 67c46e5..d065c1c 100644 --- a/llm_normalizer/backend/tests/assistantContinuityPolicy.test.ts +++ b/llm_normalizer/backend/tests/assistantContinuityPolicy.test.ts @@ -8,6 +8,7 @@ import { buildRootScopedCarryoverFilters, hydrateInventoryRootFrameState, readAddressDebugTemporalScope, + resolveNavigationSessionContextState, resolveAddressDebugCarryoverFilters, resolveAddressDebugContextFacts, resolveAddressDebugAnchorContext, @@ -111,6 +112,40 @@ describe("assistantContinuityPolicy organization authority", () => { }); }); + it("resolves navigation session context through one shared helper", () => { + const state = resolveNavigationSessionContextState({ + session_context: { + organization_scope: "Org Alt", + active_result_set_id: "rs-42", + date_scope: { + as_of_date: "2020-03-31", + period_from: "2020-03-01", + period_to: "2020-03-31" + }, + active_focus_object: { + object_type: "item", + label: "Workstation", + provenance_result_set_id: "rs-focus" + } + } + }); + + expect(state).toEqual({ + organization: "Org Alt", + activeResultSetId: "rs-42", + dateScope: { + as_of_date: "2020-03-31", + period_from: "2020-03-01", + period_to: "2020-03-31" + }, + focusObject: { + objectType: "item", + label: "Workstation", + provenanceResultSetId: "rs-focus" + } + }); + }); + it("resolves carryover temporal scope and inferred anchor from debug filters when explicit anchor fields are absent", () => { const debug = { extracted_filters: {