Архитектура: централизовать navigation focus authority в continuity policy и сделать phase15 replay time-stable
This commit is contained in:
parent
0e098cde38
commit
82cc5cfd1a
|
|
@ -23,7 +23,7 @@ This snapshot is based on:
|
||||||
- live replay comparison between:
|
- live replay comparison between:
|
||||||
- `address_truth_harness_phase12_wider_saved_session_pool_live_20260419_rerun16`
|
- `address_truth_harness_phase12_wider_saved_session_pool_live_20260419_rerun16`
|
||||||
- `address_truth_harness_phase14_counterparty_tail_resume_live_20260418_rerun2`
|
- `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_phase16_multicompany_late_pivot_live_20260419_rerun10`
|
||||||
- `address_truth_harness_phase17_clarification_resume_and_counterparty_tail_live_20260419_rerun5`
|
- `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)
|
- [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:
|
Latest graph rebuild:
|
||||||
|
|
||||||
- `5371 nodes`
|
- `5372 nodes`
|
||||||
- `11523 edges`
|
- `11525 edges`
|
||||||
- `135 communities`
|
- `135 communities`
|
||||||
|
|
||||||
Most relevant current god nodes for turnaround `11`:
|
Most relevant current god nodes for turnaround `11`:
|
||||||
|
|
|
||||||
|
|
@ -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;
|
- `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;
|
- 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.
|
- 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)
|
## 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;
|
- 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.
|
- 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
|
## Ready Signal
|
||||||
|
|
||||||
The project can leave the current breakpoint when:
|
The project can leave the current breakpoint when:
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ Current confidence snapshot:
|
||||||
- turnaround implementation progress: `~96%`
|
- turnaround implementation progress: `~96%`
|
||||||
- exit-from-danger-zone readiness: `~91%`
|
- exit-from-danger-zone readiness: `~91%`
|
||||||
- pre-multidomain readiness: `~78%`
|
- 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
|
## 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
|
## 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:
|
Goal:
|
||||||
|
|
||||||
|
|
@ -149,7 +155,11 @@ Target:
|
||||||
|
|
||||||
- transition / route / clarification should consume one continuity snapshot before making divergent decisions.
|
- 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:
|
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.
|
- 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.
|
### Iteration 3 / Pass 20. Coordinator pressure reduction
|
||||||
|
|
||||||
Target:
|
|
||||||
|
|
||||||
- product-quality business answers on already-correct truth paths.
|
|
||||||
|
|
||||||
### Pass 21. Coordinator pressure reduction
|
|
||||||
|
|
||||||
Goal:
|
Goal:
|
||||||
|
|
||||||
|
|
@ -177,7 +181,30 @@ Goal:
|
||||||
|
|
||||||
Target:
|
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
|
## Final Statement
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,7 @@ Current honest status:
|
||||||
- turnaround implementation progress: `~96%`
|
- turnaround implementation progress: `~96%`
|
||||||
- exit-from-danger-zone readiness: `~91%`
|
- exit-from-danger-zone readiness: `~91%`
|
||||||
- pre-multidomain readiness: `~78%`
|
- 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:
|
- current breakpoint:
|
||||||
- the validated hot paths are no longer structurally broken;
|
- the validated hot paths are no longer structurally broken;
|
||||||
- flagship continuity collapse is no longer the primary risk;
|
- 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_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_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_phase16_multicompany_late_pivot_live_20260419_rerun10` accepted
|
||||||
- `address_truth_harness_phase17_clarification_resume_and_counterparty_tail_live_20260419_rerun5` accepted `10/10`
|
- `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;
|
- 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 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.
|
- 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:
|
For the detailed audit, current percentages, and remaining debt, read:
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -50,12 +50,12 @@
|
||||||
],
|
],
|
||||||
"expected_recipe": "address_inventory_on_hand_as_of_date_v1",
|
"expected_recipe": "address_inventory_on_hand_as_of_date_v1",
|
||||||
"required_filters": {
|
"required_filters": {
|
||||||
"as_of_date": "2026-04-18",
|
"as_of_date": "{{runtime.today_iso}}",
|
||||||
"organization": "ООО Альтернатива Плюс"
|
"organization": "ООО Альтернатива Плюс"
|
||||||
},
|
},
|
||||||
"required_direct_answer_patterns_any": [
|
"required_direct_answer_patterns_any": [
|
||||||
"(?i)на складе|остат",
|
"(?i)на складе|остат",
|
||||||
"18\\.04\\.2026"
|
"{{runtime.today_dot_regex}}"
|
||||||
],
|
],
|
||||||
"criticality": "critical",
|
"criticality": "critical",
|
||||||
"semantic_tags": [
|
"semantic_tags": [
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ exports.readAddressDebugOrganization = readAddressDebugOrganization;
|
||||||
exports.readAddressDebugScopedDate = readAddressDebugScopedDate;
|
exports.readAddressDebugScopedDate = readAddressDebugScopedDate;
|
||||||
exports.readAddressDebugTemporalScope = readAddressDebugTemporalScope;
|
exports.readAddressDebugTemporalScope = readAddressDebugTemporalScope;
|
||||||
exports.resolveAddressDebugAnchorContext = resolveAddressDebugAnchorContext;
|
exports.resolveAddressDebugAnchorContext = resolveAddressDebugAnchorContext;
|
||||||
|
exports.resolveNavigationSessionContextState = resolveNavigationSessionContextState;
|
||||||
exports.resolveAddressDebugContextFacts = resolveAddressDebugContextFacts;
|
exports.resolveAddressDebugContextFacts = resolveAddressDebugContextFacts;
|
||||||
exports.resolveAddressDebugCarryoverFilters = resolveAddressDebugCarryoverFilters;
|
exports.resolveAddressDebugCarryoverFilters = resolveAddressDebugCarryoverFilters;
|
||||||
exports.hydrateInventoryRootFrameState = hydrateInventoryRootFrameState;
|
exports.hydrateInventoryRootFrameState = hydrateInventoryRootFrameState;
|
||||||
|
|
@ -127,6 +128,23 @@ function resolveAddressDebugAnchorContext(debug, toNonEmptyString = fallbackToNo
|
||||||
anchorValue: null
|
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) {
|
function resolveAddressDebugContextFacts(debug, toNonEmptyString = fallbackToNonEmptyString) {
|
||||||
return {
|
return {
|
||||||
item: readAddressDebugItem(debug, toNonEmptyString),
|
item: readAddressDebugItem(debug, toNonEmptyString),
|
||||||
|
|
|
||||||
|
|
@ -104,17 +104,9 @@ function createAssistantTransitionPolicy(deps) {
|
||||||
return computeMonthWindowFromIso(explicitFirstDateIso);
|
return computeMonthWindowFromIso(explicitFirstDateIso);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const sessionContext = addressNavigationState &&
|
const navigationSessionState = (0, assistantContinuityPolicy_1.resolveNavigationSessionContextState)(addressNavigationState, deps.toNonEmptyString, deps.normalizeOrganizationScopeValue);
|
||||||
typeof addressNavigationState === "object" &&
|
const focusObject = navigationSessionState.focusObject;
|
||||||
addressNavigationState.session_context &&
|
const preferredResultSetId = deps.toNonEmptyString(focusObject?.provenanceResultSetId) ?? navigationSessionState.activeResultSetId;
|
||||||
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 resultSets = Array.isArray(addressNavigationState?.result_sets) ? addressNavigationState.result_sets : [];
|
const resultSets = Array.isArray(addressNavigationState?.result_sets) ? addressNavigationState.result_sets : [];
|
||||||
const preferredResultSet = (preferredResultSetId
|
const preferredResultSet = (preferredResultSetId
|
||||||
? resultSets.find((item) => deps.toNonEmptyString(item?.result_set_id) === preferredResultSetId) ?? null
|
? resultSets.find((item) => deps.toNonEmptyString(item?.result_set_id) === preferredResultSetId) ?? null
|
||||||
|
|
@ -331,16 +323,10 @@ function createAssistantTransitionPolicy(deps) {
|
||||||
? deps.isImplicitAddressContinuationByLlm(alternateMessage, llmPreDecomposeMeta)
|
? deps.isImplicitAddressContinuationByLlm(alternateMessage, llmPreDecomposeMeta)
|
||||||
: false));
|
: false));
|
||||||
const sourceIntentHint = deps.toNonEmptyString(carryoverSourceDebug?.detected_intent);
|
const sourceIntentHint = deps.toNonEmptyString(carryoverSourceDebug?.detected_intent);
|
||||||
const navigationFocusObjectHint = addressNavigationState &&
|
const navigationSessionState = (0, assistantContinuityPolicy_1.resolveNavigationSessionContextState)(addressNavigationState, deps.toNonEmptyString, deps.normalizeOrganizationScopeValue);
|
||||||
typeof addressNavigationState === "object" &&
|
const navigationFocusObjectHint = navigationSessionState.focusObject;
|
||||||
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 hasNavigationInventoryItemFocusHint = Boolean(deps.toNonEmptyString(navigationFocusObjectHint?.label) &&
|
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_on_hand_as_of_date" ||
|
||||||
sourceIntentHint === "inventory_supplier_stock_overlap_as_of_date" ||
|
sourceIntentHint === "inventory_supplier_stock_overlap_as_of_date" ||
|
||||||
deps.isInventorySelectedObjectIntent(sourceIntentHint)));
|
deps.isInventorySelectedObjectIntent(sourceIntentHint)));
|
||||||
|
|
@ -499,19 +485,10 @@ function createAssistantTransitionPolicy(deps) {
|
||||||
const previousAnchorContext = (0, assistantContinuityPolicy_1.resolveAddressDebugAnchorContext)(carryoverSourceDebug, deps.toNonEmptyString);
|
const previousAnchorContext = (0, assistantContinuityPolicy_1.resolveAddressDebugAnchorContext)(carryoverSourceDebug, deps.toNonEmptyString);
|
||||||
let previousAnchorType = previousAnchorContext.anchorType;
|
let previousAnchorType = previousAnchorContext.anchorType;
|
||||||
let previousAnchor = previousAnchorContext.anchorValue;
|
let previousAnchor = previousAnchorContext.anchorValue;
|
||||||
const navigationSessionContext = addressNavigationState && typeof addressNavigationState === "object"
|
const navigationDateScope = navigationSessionState.dateScope;
|
||||||
? addressNavigationState.session_context && typeof addressNavigationState.session_context === "object"
|
const navigationOrganization = navigationSessionState.organization;
|
||||||
? addressNavigationState.session_context
|
const navigationFocusObject = navigationSessionState.focusObject;
|
||||||
: null
|
const navigationFocusObjectType = deps.toNonEmptyString(navigationFocusObject?.objectType);
|
||||||
: 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 navigationFocusObjectLabel = deps.toNonEmptyString(navigationFocusObject?.label);
|
const navigationFocusObjectLabel = deps.toNonEmptyString(navigationFocusObject?.label);
|
||||||
const hasInventoryItemFocusCarryover = navigationFocusObjectType === "item" &&
|
const hasInventoryItemFocusCarryover = navigationFocusObjectType === "item" &&
|
||||||
navigationFocusObjectLabel &&
|
navigationFocusObjectLabel &&
|
||||||
|
|
|
||||||
|
|
@ -45,6 +45,19 @@ export interface AssistantNavigationDateScope {
|
||||||
period_to?: unknown;
|
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 {
|
export interface AssistantAddressDebugAnchorContext {
|
||||||
anchorType: string | null;
|
anchorType: string | null;
|
||||||
anchorValue: 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(
|
export function resolveAddressDebugContextFacts(
|
||||||
debug: Record<string, unknown> | null,
|
debug: Record<string, unknown> | null,
|
||||||
toNonEmptyString: (value: unknown) => string | null = fallbackToNonEmptyString
|
toNonEmptyString: (value: unknown) => string | null = fallbackToNonEmptyString
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ import {
|
||||||
readAddressDebugFilters,
|
readAddressDebugFilters,
|
||||||
readAddressDebugItem,
|
readAddressDebugItem,
|
||||||
readAddressDebugTemporalScope,
|
readAddressDebugTemporalScope,
|
||||||
|
resolveNavigationSessionContextState,
|
||||||
resolveAddressDebugCarryoverFilters,
|
resolveAddressDebugCarryoverFilters,
|
||||||
resolveAddressDebugAnchorContext,
|
resolveAddressDebugAnchorContext,
|
||||||
resolveDisplayedEntityFollowupRetarget,
|
resolveDisplayedEntityFollowupRetarget,
|
||||||
|
|
@ -145,20 +146,14 @@ export function createAssistantTransitionPolicy(deps) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const sessionContext =
|
const navigationSessionState = resolveNavigationSessionContextState(
|
||||||
addressNavigationState &&
|
addressNavigationState,
|
||||||
typeof addressNavigationState === "object" &&
|
deps.toNonEmptyString,
|
||||||
addressNavigationState.session_context &&
|
deps.normalizeOrganizationScopeValue
|
||||||
typeof addressNavigationState.session_context === "object"
|
);
|
||||||
? addressNavigationState.session_context
|
const focusObject = navigationSessionState.focusObject;
|
||||||
: null;
|
|
||||||
const focusObject =
|
|
||||||
sessionContext && typeof sessionContext.active_focus_object === "object"
|
|
||||||
? sessionContext.active_focus_object
|
|
||||||
: null;
|
|
||||||
const preferredResultSetId =
|
const preferredResultSetId =
|
||||||
deps.toNonEmptyString(focusObject?.provenance_result_set_id) ??
|
deps.toNonEmptyString(focusObject?.provenanceResultSetId) ?? navigationSessionState.activeResultSetId;
|
||||||
deps.toNonEmptyString(sessionContext?.active_result_set_id);
|
|
||||||
const resultSets = Array.isArray(addressNavigationState?.result_sets) ? addressNavigationState.result_sets : [];
|
const resultSets = Array.isArray(addressNavigationState?.result_sets) ? addressNavigationState.result_sets : [];
|
||||||
const preferredResultSet =
|
const preferredResultSet =
|
||||||
(preferredResultSetId
|
(preferredResultSetId
|
||||||
|
|
@ -431,18 +426,15 @@ export function createAssistantTransitionPolicy(deps) {
|
||||||
? deps.isImplicitAddressContinuationByLlm(alternateMessage, llmPreDecomposeMeta)
|
? deps.isImplicitAddressContinuationByLlm(alternateMessage, llmPreDecomposeMeta)
|
||||||
: false));
|
: false));
|
||||||
const sourceIntentHint = deps.toNonEmptyString(carryoverSourceDebug?.detected_intent);
|
const sourceIntentHint = deps.toNonEmptyString(carryoverSourceDebug?.detected_intent);
|
||||||
const navigationFocusObjectHint =
|
const navigationSessionState = resolveNavigationSessionContextState(
|
||||||
addressNavigationState &&
|
addressNavigationState,
|
||||||
typeof addressNavigationState === "object" &&
|
deps.toNonEmptyString,
|
||||||
addressNavigationState.session_context &&
|
deps.normalizeOrganizationScopeValue
|
||||||
typeof addressNavigationState.session_context === "object" &&
|
);
|
||||||
addressNavigationState.session_context.active_focus_object &&
|
const navigationFocusObjectHint = navigationSessionState.focusObject;
|
||||||
typeof addressNavigationState.session_context.active_focus_object === "object"
|
|
||||||
? addressNavigationState.session_context.active_focus_object
|
|
||||||
: null;
|
|
||||||
const hasNavigationInventoryItemFocusHint = Boolean(
|
const hasNavigationInventoryItemFocusHint = Boolean(
|
||||||
deps.toNonEmptyString(navigationFocusObjectHint?.label) &&
|
deps.toNonEmptyString(navigationFocusObjectHint?.label) &&
|
||||||
deps.toNonEmptyString(navigationFocusObjectHint?.object_type) === "item" &&
|
deps.toNonEmptyString(navigationFocusObjectHint?.objectType) === "item" &&
|
||||||
(sourceIntentHint === "inventory_on_hand_as_of_date" ||
|
(sourceIntentHint === "inventory_on_hand_as_of_date" ||
|
||||||
sourceIntentHint === "inventory_supplier_stock_overlap_as_of_date" ||
|
sourceIntentHint === "inventory_supplier_stock_overlap_as_of_date" ||
|
||||||
deps.isInventorySelectedObjectIntent(sourceIntentHint))
|
deps.isInventorySelectedObjectIntent(sourceIntentHint))
|
||||||
|
|
@ -643,22 +635,10 @@ export function createAssistantTransitionPolicy(deps) {
|
||||||
const previousAnchorContext = resolveAddressDebugAnchorContext(carryoverSourceDebug, deps.toNonEmptyString);
|
const previousAnchorContext = resolveAddressDebugAnchorContext(carryoverSourceDebug, deps.toNonEmptyString);
|
||||||
let previousAnchorType = previousAnchorContext.anchorType;
|
let previousAnchorType = previousAnchorContext.anchorType;
|
||||||
let previousAnchor = previousAnchorContext.anchorValue;
|
let previousAnchor = previousAnchorContext.anchorValue;
|
||||||
const navigationSessionContext =
|
const navigationDateScope = navigationSessionState.dateScope;
|
||||||
addressNavigationState && typeof addressNavigationState === "object"
|
const navigationOrganization = navigationSessionState.organization;
|
||||||
? addressNavigationState.session_context && typeof addressNavigationState.session_context === "object"
|
const navigationFocusObject = navigationSessionState.focusObject;
|
||||||
? addressNavigationState.session_context
|
const navigationFocusObjectType = deps.toNonEmptyString(navigationFocusObject?.objectType);
|
||||||
: 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 navigationFocusObjectLabel = deps.toNonEmptyString(navigationFocusObject?.label);
|
const navigationFocusObjectLabel = deps.toNonEmptyString(navigationFocusObject?.label);
|
||||||
const hasInventoryItemFocusCarryover =
|
const hasInventoryItemFocusCarryover =
|
||||||
navigationFocusObjectType === "item" &&
|
navigationFocusObjectType === "item" &&
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ import {
|
||||||
buildRootScopedCarryoverFilters,
|
buildRootScopedCarryoverFilters,
|
||||||
hydrateInventoryRootFrameState,
|
hydrateInventoryRootFrameState,
|
||||||
readAddressDebugTemporalScope,
|
readAddressDebugTemporalScope,
|
||||||
|
resolveNavigationSessionContextState,
|
||||||
resolveAddressDebugCarryoverFilters,
|
resolveAddressDebugCarryoverFilters,
|
||||||
resolveAddressDebugContextFacts,
|
resolveAddressDebugContextFacts,
|
||||||
resolveAddressDebugAnchorContext,
|
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", () => {
|
it("resolves carryover temporal scope and inferred anchor from debug filters when explicit anchor fields are absent", () => {
|
||||||
const debug = {
|
const debug = {
|
||||||
extracted_filters: {
|
extracted_filters: {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue