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 53c4956..d7db39b 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 @@ -434,6 +434,12 @@ Still open after the accepted phase12 replay: - this matters because navigation date scope and continuity temporal scope are now merged through one owner before transition decides pivots, instead of being backfilled ad hoc inside the hot path; - targeted `assistantContinuityPolicy` and `assistantTransitionPolicy` suites are green after the move, with direct helper coverage for navigation-first temporal precedence and for non-applicable intent families staying untouched; - a fresh live rerun of `address_truth_harness_phase12_wider_saved_session_pool` on `2026-04-19` stayed semantically stable and again failed only on the already-known date-sensitive `today` expectations, not on the new shared temporal carryover authority. +- the next continuity-authority pass now centralizes organization carryover precedence for follow-up filters: + - transition no longer owns a local cascade of `historical organization -> shared authority -> continuity snapshot -> navigation organization -> clarification selection`; + - shared continuity now owns that merge through `applyOrganizationCarryoverFilters(...)`, so organization hydration in follow-up filters has a single explicit precedence contract; + - this matters because `previous_filters.organization` is now aligned with the same continuity authority story that already drives route, living-chat, and data-scope, instead of keeping one more hot-path-only merge order inside transition glue; + - targeted `assistantContinuityPolicy` and `assistantTransitionPolicy` suites are green after the move, with direct helper coverage for organization precedence and for preserving an already grounded organization value; + - a fresh live rerun of `address_truth_harness_phase12_wider_saved_session_pool` on `2026-04-19` remained semantically stable and again failed only on the already-known date-sensitive `today` expectations, not on the new shared organization carryover authority. ## Next Execution Slice (2026-04-18) diff --git a/llm_normalizer/backend/dist/services/assistantContinuityPolicy.js b/llm_normalizer/backend/dist/services/assistantContinuityPolicy.js index b2e228d..5009bbc 100644 --- a/llm_normalizer/backend/dist/services/assistantContinuityPolicy.js +++ b/llm_normalizer/backend/dist/services/assistantContinuityPolicy.js @@ -13,6 +13,7 @@ exports.hydrateInventoryRootFrameState = hydrateInventoryRootFrameState; exports.buildRootScopedCarryoverFilters = buildRootScopedCarryoverFilters; exports.shouldUseNavigationTemporalCarryover = shouldUseNavigationTemporalCarryover; exports.applyTemporalCarryoverFilters = applyTemporalCarryoverFilters; +exports.applyOrganizationCarryoverFilters = applyOrganizationCarryoverFilters; exports.buildInventoryRootFrameFromAddressDebug = buildInventoryRootFrameFromAddressDebug; exports.isGroundedAddressDebug = isGroundedAddressDebug; exports.resolveAssistantContinuitySnapshot = resolveAssistantContinuitySnapshot; @@ -267,6 +268,19 @@ function applyTemporalCarryoverFilters(previousFilters, navigationDateScope, con } return nextFilters; } +function applyOrganizationCarryoverFilters(previousFilters, historicalOrganization, authorityActiveOrganization, continuityActiveOrganization, navigationOrganization, organizationClarificationSelection, toNonEmptyString = fallbackToNonEmptyString) { + const nextFilters = previousFilters && typeof previousFilters === "object" ? { ...previousFilters } : {}; + if (!toNonEmptyString(nextFilters.organization)) { + nextFilters.organization = + toNonEmptyString(historicalOrganization) ?? + toNonEmptyString(authorityActiveOrganization) ?? + toNonEmptyString(continuityActiveOrganization) ?? + toNonEmptyString(navigationOrganization) ?? + toNonEmptyString(organizationClarificationSelection) ?? + undefined; + } + return nextFilters; +} function buildInventoryRootFrameFromAddressDebug(debug, toNonEmptyString = fallbackToNonEmptyString) { if (!debug || typeof debug !== "object") { return null; diff --git a/llm_normalizer/backend/dist/services/assistantTransitionPolicy.js b/llm_normalizer/backend/dist/services/assistantTransitionPolicy.js index b2c36af..f390e1b 100644 --- a/llm_normalizer/backend/dist/services/assistantTransitionPolicy.js +++ b/llm_normalizer/backend/dist/services/assistantTransitionPolicy.js @@ -614,26 +614,10 @@ function createAssistantTransitionPolicy(deps) { previousFilters.counterparty = historicalCounterparty; } } - if (!deps.toNonEmptyString(previousFilters.organization)) { - const historicalOrganization = deps.findRecentAddressFilterValue(items, "organization"); - if (historicalOrganization) { - previousFilters.organization = historicalOrganization; - } - } + const historicalOrganization = deps.findRecentAddressFilterValue(items, "organization"); const authorityActiveOrganization = deps.normalizeOrganizationScopeValue(organizationAuthority.activeOrganization) ?? deps.normalizeOrganizationScopeValue(organizationAuthority.continuityActiveOrganization); - if (!deps.toNonEmptyString(previousFilters.organization) && authorityActiveOrganization) { - previousFilters.organization = authorityActiveOrganization; - } - if (!deps.toNonEmptyString(previousFilters.organization) && continuitySnapshot.activeOrganization) { - previousFilters.organization = continuitySnapshot.activeOrganization; - } - if (!deps.toNonEmptyString(previousFilters.organization) && navigationOrganization) { - previousFilters.organization = navigationOrganization; - } - if (!deps.toNonEmptyString(previousFilters.organization) && organizationClarificationSelection) { - previousFilters.organization = organizationClarificationSelection; - } + previousFilters = (0, assistantContinuityPolicy_1.applyOrganizationCarryoverFilters)(previousFilters, historicalOrganization, authorityActiveOrganization, continuitySnapshot.activeOrganization, navigationOrganization, organizationClarificationSelection, deps.toNonEmptyString); if (inventoryPurchaseDateVatBridge) { const purchaseBridgeItem = previousAddressItem && deps.toNonEmptyString(previousAddressDebug?.detected_intent) === "inventory_purchase_provenance_for_item" diff --git a/llm_normalizer/backend/src/services/assistantContinuityPolicy.ts b/llm_normalizer/backend/src/services/assistantContinuityPolicy.ts index 049a385..5e477a5 100644 --- a/llm_normalizer/backend/src/services/assistantContinuityPolicy.ts +++ b/llm_normalizer/backend/src/services/assistantContinuityPolicy.ts @@ -413,6 +413,29 @@ export function applyTemporalCarryoverFilters( return nextFilters; } +export function applyOrganizationCarryoverFilters( + previousFilters: Record | null, + historicalOrganization: unknown, + authorityActiveOrganization: unknown, + continuityActiveOrganization: unknown, + navigationOrganization: unknown, + organizationClarificationSelection: unknown, + toNonEmptyString: (value: unknown) => string | null = fallbackToNonEmptyString +): Record { + const nextFilters = + previousFilters && typeof previousFilters === "object" ? { ...previousFilters } : {}; + if (!toNonEmptyString(nextFilters.organization)) { + nextFilters.organization = + toNonEmptyString(historicalOrganization) ?? + toNonEmptyString(authorityActiveOrganization) ?? + toNonEmptyString(continuityActiveOrganization) ?? + toNonEmptyString(navigationOrganization) ?? + toNonEmptyString(organizationClarificationSelection) ?? + undefined; + } + return nextFilters; +} + export function buildInventoryRootFrameFromAddressDebug( 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 d32d5a9..018f405 100644 --- a/llm_normalizer/backend/src/services/assistantTransitionPolicy.ts +++ b/llm_normalizer/backend/src/services/assistantTransitionPolicy.ts @@ -1,5 +1,6 @@ // @ts-nocheck import { + applyOrganizationCarryoverFilters, applyTemporalCarryoverFilters, buildRootScopedCarryoverFilters, buildInventoryRootFrameFromAddressDebug, @@ -770,27 +771,19 @@ export function createAssistantTransitionPolicy(deps) { previousFilters.counterparty = historicalCounterparty; } } - if (!deps.toNonEmptyString(previousFilters.organization)) { - const historicalOrganization = deps.findRecentAddressFilterValue(items, "organization"); - if (historicalOrganization) { - previousFilters.organization = historicalOrganization; - } - } + const historicalOrganization = deps.findRecentAddressFilterValue(items, "organization"); const authorityActiveOrganization = deps.normalizeOrganizationScopeValue(organizationAuthority.activeOrganization) ?? deps.normalizeOrganizationScopeValue(organizationAuthority.continuityActiveOrganization); - if (!deps.toNonEmptyString(previousFilters.organization) && authorityActiveOrganization) { - previousFilters.organization = authorityActiveOrganization; - } - if (!deps.toNonEmptyString(previousFilters.organization) && continuitySnapshot.activeOrganization) { - previousFilters.organization = continuitySnapshot.activeOrganization; - } - if (!deps.toNonEmptyString(previousFilters.organization) && navigationOrganization) { - previousFilters.organization = navigationOrganization; - } - if (!deps.toNonEmptyString(previousFilters.organization) && organizationClarificationSelection) { - previousFilters.organization = organizationClarificationSelection; - } + previousFilters = applyOrganizationCarryoverFilters( + previousFilters, + historicalOrganization, + authorityActiveOrganization, + continuitySnapshot.activeOrganization, + navigationOrganization, + organizationClarificationSelection, + deps.toNonEmptyString + ); if (inventoryPurchaseDateVatBridge) { const purchaseBridgeItem = previousAddressItem && diff --git a/llm_normalizer/backend/tests/assistantContinuityPolicy.test.ts b/llm_normalizer/backend/tests/assistantContinuityPolicy.test.ts index d538731..c562d5a 100644 --- a/llm_normalizer/backend/tests/assistantContinuityPolicy.test.ts +++ b/llm_normalizer/backend/tests/assistantContinuityPolicy.test.ts @@ -1,5 +1,6 @@ import { describe, expect, it } from "vitest"; import { + applyOrganizationCarryoverFilters, applyTemporalCarryoverFilters, buildRootScopedCarryoverFilters, hydrateInventoryRootFrameState, @@ -248,4 +249,36 @@ describe("assistantContinuityPolicy organization authority", () => { organization: "Org Alt" }); }); + + it("applies organization carryover precedence from historical to shared authority to navigation and clarification", () => { + const filters = applyOrganizationCarryoverFilters( + {}, + null, + "Org Authority", + "Org Continuity", + "Org Navigation", + "Org Clarification" + ); + + expect(filters).toEqual({ + organization: "Org Authority" + }); + }); + + it("keeps existing organization and does not overwrite it during organization carryover backfill", () => { + const filters = applyOrganizationCarryoverFilters( + { + organization: "Org Existing" + }, + "Org Historical", + "Org Authority", + "Org Continuity", + "Org Navigation", + "Org Clarification" + ); + + expect(filters).toEqual({ + organization: "Org Existing" + }); + }); });