diff --git a/docs/ARCH/11 - architecture_turnaround/20 - planner_autonomy_consolidation_2026-05-01.md b/docs/ARCH/11 - architecture_turnaround/20 - planner_autonomy_consolidation_2026-05-01.md index ca821e6..44d48b4 100644 --- a/docs/ARCH/11 - architecture_turnaround/20 - planner_autonomy_consolidation_2026-05-01.md +++ b/docs/ARCH/11 - architecture_turnaround/20 - planner_autonomy_consolidation_2026-05-01.md @@ -118,6 +118,7 @@ The following consolidation step added catalog-level chain-template scoring: - document/movement/inventory/lifecycle templates can now be inspected as catalog search results, not only as local planner branch constants; - `assistantMcpDiscoveryPlanner` records the top catalog chain-template match in reason codes and exposes the ranked matches as `catalog_chain_template_matches` in the planner contract while preserving existing guarded execution behavior. - the ranked chain-template matches are now propagated into runtime loop state and debug attachment fields, so replay analysis can inspect catalog-fabric intent without parsing reason-code strings. +- `catalog_chain_template_alignment` now records whether the selected chain is the top catalog match, its rank, and whether it appeared in the catalog search results; runtime loop state and debug summary expose the same verdict. ## Why This Matters @@ -220,9 +221,16 @@ Latest validation after subject-aware bidirectional comparison arbitration: - `npm.cmd run build`: passed - graphify rebuild: `5940 nodes`, `12909 edges`, `137 communities` +Latest validation after structured catalog chain-template alignment verdict: + +- targeted planner/runtime/debug tests: passed, `54 passed` +- full MCP-discovery suite: passed, `282 passed`, `9 skipped` +- `npm.cmd run build`: passed +- graphify rebuild: `5941 nodes`, `12911 edges`, `136 communities` + ## Next Step -The next safe step is still to re-run live replay once the 1C side is actively polling the proxy. In parallel, local-only consolidation can continue by letting selected downstream arbitration consume `catalog_chain_template_matches` where this can be done without changing exact runtime behavior. +The next safe step is still to re-run live replay once the 1C side is actively polling the proxy. In parallel, local-only consolidation can continue by using the alignment verdict to find remaining manual branches where selected chains diverge from reviewed catalog-fabric intent. Recommended order: diff --git a/docs/ARCH/11 - architecture_turnaround/README.md b/docs/ARCH/11 - architecture_turnaround/README.md index 229492b..56b15e0 100644 --- a/docs/ARCH/11 - architecture_turnaround/README.md +++ b/docs/ARCH/11 - architecture_turnaround/README.md @@ -81,6 +81,7 @@ It now documents a turnaround that is already operational in code, already mater - inventory catalog templates now bridge through existing exact inventory recipes (`41.01` scoped stock, supplier overlap, purchase provenance, and sale trace) inside the bounded MCP discovery pilot, while missing selected-item anchors still clarify instead of guessing; - unambiguous metadata surfaces can now infer the next reviewed lane from `Document.*`, `Register.*`, or `Catalog.*` objects even before upstream labels `downstream_route_family`, while mixed surfaces still do not guess; - catalog index now scores reviewed chain templates directly from fact/action/axis/comparison/ranking needs, and planner/runtime/debug surfaces expose ranked catalog chain matches through the structured `catalog_chain_template_matches` contract path instead of relying only on reason-code strings; + - planner/runtime/debug surfaces now expose `catalog_chain_template_alignment`, so semantic replay can see whether selected chains match the catalog top match, fall back to a lower-ranked template, or bypass catalog search; - explicit-counterparty incoming-vs-outgoing data-need graphs now select the reviewed `value_flow_comparison` chain instead of falling back to generic `value_flow`; - live map sync: [20 - planner_autonomy_consolidation_2026-05-01.md](./20%20-%20planner_autonomy_consolidation_2026-05-01.md) @@ -93,8 +94,8 @@ Current honest status: - open-world bounded-autonomy readiness: `~85%` - 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: `~83%` 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, and subject-aware bidirectional comparison arbitration validated locally, but live replay for the new bridge is currently blocked by missing active 1C polling and broader unfamiliar 1C asks still need replay-backed growth -- graph snapshot after latest rebuild: `5940 nodes`, `12909 edges`, `137 communities` +- Planner Autonomy Consolidation progress: `~84%` 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, and structured catalog-alignment verdicts validated locally, but live replay for the new bridge is currently blocked by missing active 1C polling and broader unfamiliar 1C asks still need replay-backed growth +- graph snapshot after latest rebuild: `5941 nodes`, `12911 edges`, `136 communities` - current breakpoint: - the validated hot paths are no longer structurally broken; - flagship continuity collapse is no longer the primary risk; @@ -143,6 +144,7 @@ Latest live proof now includes: - structured chain-template planner contract accepted locally: planner slice passed `36/36`; full MCP-discovery slice passed `282/282` with `9` skipped; build passed; graphify rebuilt to `5939 nodes`, `12906 edges`, `138 communities` - structured chain-template runtime/debug propagation accepted locally: runtime/debug slice passed `18/18`; full MCP-discovery slice passed `282/282` with `9` skipped; build passed; graphify rebuilt to `5940 nodes`, `12909 edges`, `137 communities` - subject-aware bidirectional comparison arbitration accepted locally: planner slice passed `36/36`; full MCP-discovery slice passed `282/282` with `9` skipped; build passed; graphify rebuilt to `5940 nodes`, `12909 edges`, `137 communities` +- structured catalog-alignment verdict accepted locally: planner/runtime/debug slice passed `54/54`; full MCP-discovery slice passed `282/282` with `9` skipped; build passed; graphify rebuilt to `5941 nodes`, `12911 edges`, `136 communities` Current architectural reading: diff --git a/llm_normalizer/backend/dist/services/assistantMcpDiscoveryDebugAttachment.js b/llm_normalizer/backend/dist/services/assistantMcpDiscoveryDebugAttachment.js index 8c24fd2..a30f397 100644 --- a/llm_normalizer/backend/dist/services/assistantMcpDiscoveryDebugAttachment.js +++ b/llm_normalizer/backend/dist/services/assistantMcpDiscoveryDebugAttachment.js @@ -46,6 +46,7 @@ function buildAssistantMcpDiscoveryDebugAttachmentFields(input) { const entryPoint = resolveEntryPoint(input); const bridge = toRecordObject(entryPoint?.bridge); const planner = toRecordObject(bridge?.planner); + const chainAlignment = toRecordObject(planner?.catalog_chain_template_alignment); const answerDraft = toRecordObject(bridge?.answer_draft); return { assistant_mcp_discovery_entry_point_v1: entryPoint, @@ -55,6 +56,8 @@ function buildAssistantMcpDiscoveryDebugAttachmentFields(input) { mcp_discovery_bridge_status: toNonEmptyString(bridge?.bridge_status), mcp_discovery_selected_chain_id: toNonEmptyString(planner?.selected_chain_id), mcp_discovery_catalog_chain_template_matches: toStringArray(planner?.catalog_chain_template_matches), + mcp_discovery_catalog_chain_top_match: toNonEmptyString(chainAlignment?.top_chain_template_match), + mcp_discovery_catalog_chain_selected_matches_top: chainAlignment?.selected_chain_matches_top === true, mcp_discovery_answer_mode: toNonEmptyString(answerDraft?.answer_mode), mcp_discovery_business_fact_answer_allowed: bridge?.business_fact_answer_allowed === true, mcp_discovery_user_facing_response_allowed: bridge?.user_facing_response_allowed === true, diff --git a/llm_normalizer/backend/dist/services/assistantMcpDiscoveryPlanner.js b/llm_normalizer/backend/dist/services/assistantMcpDiscoveryPlanner.js index d923ac7..ad4583d 100644 --- a/llm_normalizer/backend/dist/services/assistantMcpDiscoveryPlanner.js +++ b/llm_normalizer/backend/dist/services/assistantMcpDiscoveryPlanner.js @@ -359,6 +359,17 @@ function catalogChainTemplateMatchesForContract(input, recipe) { aggregation_need: dataNeedGraph.aggregation_need }); } +function catalogChainTemplateAlignmentForContract(recipe, matches) { + const selectedChainIsCatalogTemplate = recipe.chainId !== "metadata_lane_clarification"; + const selectedIndex = matches.indexOf(recipe.chainId); + return { + top_chain_template_match: matches[0] ?? null, + selected_chain_template_rank: selectedIndex >= 0 ? selectedIndex + 1 : null, + selected_chain_is_catalog_template: selectedChainIsCatalogTemplate, + selected_chain_in_catalog_matches: selectedIndex >= 0, + selected_chain_matches_top: selectedIndex === 0 + }; +} function routeFamilyFromThinMetadataSurfaceInput(input) { const surface = input.metadataSurface ?? null; const surfaceRouteFamily = routeFamilyFromMetadataSurfaceRef(surface); @@ -974,6 +985,7 @@ function planAssistantMcpDiscovery(input) { const dataNeedGraph = input.dataNeedGraph ?? null; const metadataSurface = input.metadataSurface ?? null; const catalogChainTemplateMatches = catalogChainTemplateMatchesForContract(input, recipe); + const catalogChainTemplateAlignment = catalogChainTemplateAlignmentForContract(recipe, catalogChainTemplateMatches); const reasonCodes = []; pushReason(reasonCodes, recipe.reason); for (const reason of recipe.extraReasons ?? []) { @@ -1037,6 +1049,7 @@ function planAssistantMcpDiscovery(input) { selected_chain_id: recipe.chainId, selected_chain_summary: recipe.chainSummary, catalog_chain_template_matches: catalogChainTemplateMatches, + catalog_chain_template_alignment: catalogChainTemplateAlignment, proposed_primitives: recipe.primitives, required_axes: recipe.axes, discovery_plan: plan, diff --git a/llm_normalizer/backend/dist/services/assistantMcpDiscoveryRuntimeBridge.js b/llm_normalizer/backend/dist/services/assistantMcpDiscoveryRuntimeBridge.js index 73e9eb6..8951d78 100644 --- a/llm_normalizer/backend/dist/services/assistantMcpDiscoveryRuntimeBridge.js +++ b/llm_normalizer/backend/dist/services/assistantMcpDiscoveryRuntimeBridge.js @@ -91,6 +91,7 @@ function buildLoopState(planner, pilot, bridgeStatus) { loop_status: loopStatusFor(bridgeStatus), selected_chain_id: planner.selected_chain_id, catalog_chain_template_matches: [...planner.catalog_chain_template_matches], + catalog_chain_template_alignment: planner.catalog_chain_template_alignment, pilot_scope: pilot.pilot_scope, asked_domain_family: planner.discovery_plan.turn_meaning_ref?.asked_domain_family ?? null, asked_action_family: planner.discovery_plan.turn_meaning_ref?.asked_action_family ?? null, diff --git a/llm_normalizer/backend/src/services/assistantMcpDiscoveryDebugAttachment.ts b/llm_normalizer/backend/src/services/assistantMcpDiscoveryDebugAttachment.ts index e4f7751..27b5fc6 100644 --- a/llm_normalizer/backend/src/services/assistantMcpDiscoveryDebugAttachment.ts +++ b/llm_normalizer/backend/src/services/assistantMcpDiscoveryDebugAttachment.ts @@ -8,6 +8,8 @@ export interface AssistantMcpDiscoveryDebugAttachmentFields { mcp_discovery_bridge_status: string | null; mcp_discovery_selected_chain_id: string | null; mcp_discovery_catalog_chain_template_matches: string[]; + mcp_discovery_catalog_chain_top_match: string | null; + mcp_discovery_catalog_chain_selected_matches_top: boolean; mcp_discovery_answer_mode: string | null; mcp_discovery_business_fact_answer_allowed: boolean; mcp_discovery_user_facing_response_allowed: boolean; @@ -73,6 +75,7 @@ export function buildAssistantMcpDiscoveryDebugAttachmentFields( const entryPoint = resolveEntryPoint(input); const bridge = toRecordObject(entryPoint?.bridge); const planner = toRecordObject(bridge?.planner); + const chainAlignment = toRecordObject(planner?.catalog_chain_template_alignment); const answerDraft = toRecordObject(bridge?.answer_draft); return { @@ -83,6 +86,8 @@ export function buildAssistantMcpDiscoveryDebugAttachmentFields( mcp_discovery_bridge_status: toNonEmptyString(bridge?.bridge_status), mcp_discovery_selected_chain_id: toNonEmptyString(planner?.selected_chain_id), mcp_discovery_catalog_chain_template_matches: toStringArray(planner?.catalog_chain_template_matches), + mcp_discovery_catalog_chain_top_match: toNonEmptyString(chainAlignment?.top_chain_template_match), + mcp_discovery_catalog_chain_selected_matches_top: chainAlignment?.selected_chain_matches_top === true, mcp_discovery_answer_mode: toNonEmptyString(answerDraft?.answer_mode), mcp_discovery_business_fact_answer_allowed: bridge?.business_fact_answer_allowed === true, mcp_discovery_user_facing_response_allowed: bridge?.user_facing_response_allowed === true, diff --git a/llm_normalizer/backend/src/services/assistantMcpDiscoveryPlanner.ts b/llm_normalizer/backend/src/services/assistantMcpDiscoveryPlanner.ts index ebafc55..57a84bf 100644 --- a/llm_normalizer/backend/src/services/assistantMcpDiscoveryPlanner.ts +++ b/llm_normalizer/backend/src/services/assistantMcpDiscoveryPlanner.ts @@ -38,6 +38,14 @@ export interface AssistantMcpDiscoveryMetadataSurfaceRef { ambiguity_entity_sets: string[]; } +export interface AssistantMcpDiscoveryCatalogChainTemplateAlignment { + top_chain_template_match: AssistantMcpCatalogChainTemplateId | null; + selected_chain_template_rank: number | null; + selected_chain_is_catalog_template: boolean; + selected_chain_in_catalog_matches: boolean; + selected_chain_matches_top: boolean; +} + export type AssistantMcpDiscoveryChainId = | "metadata_inspection" | "catalog_drilldown" @@ -71,6 +79,7 @@ export interface AssistantMcpDiscoveryPlannerContract { selected_chain_id: AssistantMcpDiscoveryChainId; selected_chain_summary: string; catalog_chain_template_matches: AssistantMcpCatalogChainTemplateId[]; + catalog_chain_template_alignment: AssistantMcpDiscoveryCatalogChainTemplateAlignment; proposed_primitives: AssistantMcpDiscoveryPrimitive[]; required_axes: string[]; discovery_plan: AssistantMcpDiscoveryPlanContract; @@ -555,6 +564,21 @@ function catalogChainTemplateMatchesForContract( }); } +function catalogChainTemplateAlignmentForContract( + recipe: PlannerRecipe, + matches: AssistantMcpCatalogChainTemplateId[] +): AssistantMcpDiscoveryCatalogChainTemplateAlignment { + const selectedChainIsCatalogTemplate = recipe.chainId !== "metadata_lane_clarification"; + const selectedIndex = matches.indexOf(recipe.chainId as AssistantMcpCatalogChainTemplateId); + return { + top_chain_template_match: matches[0] ?? null, + selected_chain_template_rank: selectedIndex >= 0 ? selectedIndex + 1 : null, + selected_chain_is_catalog_template: selectedChainIsCatalogTemplate, + selected_chain_in_catalog_matches: selectedIndex >= 0, + selected_chain_matches_top: selectedIndex === 0 + }; +} + function routeFamilyFromThinMetadataSurfaceInput( input: AssistantMcpDiscoveryPlannerInput ): AssistantMcpDiscoveryMetadataRouteFamily | null { @@ -1216,6 +1240,7 @@ export function planAssistantMcpDiscovery( const dataNeedGraph = input.dataNeedGraph ?? null; const metadataSurface = input.metadataSurface ?? null; const catalogChainTemplateMatches = catalogChainTemplateMatchesForContract(input, recipe); + const catalogChainTemplateAlignment = catalogChainTemplateAlignmentForContract(recipe, catalogChainTemplateMatches); const reasonCodes: string[] = []; pushReason(reasonCodes, recipe.reason); for (const reason of recipe.extraReasons ?? []) { @@ -1283,6 +1308,7 @@ export function planAssistantMcpDiscovery( selected_chain_id: recipe.chainId, selected_chain_summary: recipe.chainSummary, catalog_chain_template_matches: catalogChainTemplateMatches, + catalog_chain_template_alignment: catalogChainTemplateAlignment, proposed_primitives: recipe.primitives, required_axes: recipe.axes, discovery_plan: plan, diff --git a/llm_normalizer/backend/src/services/assistantMcpDiscoveryRuntimeBridge.ts b/llm_normalizer/backend/src/services/assistantMcpDiscoveryRuntimeBridge.ts index 23babfc..d013f70 100644 --- a/llm_normalizer/backend/src/services/assistantMcpDiscoveryRuntimeBridge.ts +++ b/llm_normalizer/backend/src/services/assistantMcpDiscoveryRuntimeBridge.ts @@ -46,6 +46,7 @@ export interface AssistantMcpDiscoveryLoopStateContract { loop_status: AssistantMcpDiscoveryLoopStatus; selected_chain_id: AssistantMcpDiscoveryChainId; catalog_chain_template_matches: AssistantMcpDiscoveryPlannerContract["catalog_chain_template_matches"]; + catalog_chain_template_alignment: AssistantMcpDiscoveryPlannerContract["catalog_chain_template_alignment"]; pilot_scope: AssistantMcpDiscoveryPilotExecutionContract["pilot_scope"]; asked_domain_family: string | null; asked_action_family: string | null; @@ -179,6 +180,7 @@ function buildLoopState( loop_status: loopStatusFor(bridgeStatus), selected_chain_id: planner.selected_chain_id, catalog_chain_template_matches: [...planner.catalog_chain_template_matches], + catalog_chain_template_alignment: planner.catalog_chain_template_alignment, pilot_scope: pilot.pilot_scope, asked_domain_family: planner.discovery_plan.turn_meaning_ref?.asked_domain_family ?? null, asked_action_family: planner.discovery_plan.turn_meaning_ref?.asked_action_family ?? null, diff --git a/llm_normalizer/backend/tests/assistantMcpDiscoveryDebugAttachment.test.ts b/llm_normalizer/backend/tests/assistantMcpDiscoveryDebugAttachment.test.ts index 8f4294a..5e62f8d 100644 --- a/llm_normalizer/backend/tests/assistantMcpDiscoveryDebugAttachment.test.ts +++ b/llm_normalizer/backend/tests/assistantMcpDiscoveryDebugAttachment.test.ts @@ -16,7 +16,14 @@ function entryPointContract(overrides: Record = {}) { requires_user_clarification: false, planner: { selected_chain_id: "value_flow_ranking", - catalog_chain_template_matches: ["value_flow_ranking", "value_flow"] + catalog_chain_template_matches: ["value_flow_ranking", "value_flow"], + catalog_chain_template_alignment: { + top_chain_template_match: "value_flow_ranking", + selected_chain_template_rank: 1, + selected_chain_is_catalog_template: true, + selected_chain_in_catalog_matches: true, + selected_chain_matches_top: true + } }, answer_draft: { answer_mode: "confirmed_with_bounded_inference" @@ -43,6 +50,8 @@ describe("assistant MCP discovery debug attachment", () => { expect(debug.mcp_discovery_bridge_status).toBe("answer_draft_ready"); expect(debug.mcp_discovery_selected_chain_id).toBe("value_flow_ranking"); expect(debug.mcp_discovery_catalog_chain_template_matches).toEqual(["value_flow_ranking", "value_flow"]); + expect(debug.mcp_discovery_catalog_chain_top_match).toBe("value_flow_ranking"); + expect(debug.mcp_discovery_catalog_chain_selected_matches_top).toBe(true); expect(debug.mcp_discovery_answer_mode).toBe("confirmed_with_bounded_inference"); expect(debug.mcp_discovery_business_fact_answer_allowed).toBe(true); expect(debug.mcp_discovery_user_facing_response_allowed).toBe(true); @@ -62,6 +71,8 @@ describe("assistant MCP discovery debug attachment", () => { expect(debug.mcp_discovery_bridge_status).toBeNull(); expect(debug.mcp_discovery_selected_chain_id).toBeNull(); expect(debug.mcp_discovery_catalog_chain_template_matches).toEqual([]); + expect(debug.mcp_discovery_catalog_chain_top_match).toBeNull(); + expect(debug.mcp_discovery_catalog_chain_selected_matches_top).toBe(false); expect(debug.mcp_discovery_answer_mode).toBeNull(); expect(debug.mcp_discovery_business_fact_answer_allowed).toBe(false); expect(debug.mcp_discovery_user_facing_response_allowed).toBe(false); diff --git a/llm_normalizer/backend/tests/assistantMcpDiscoveryPlanner.test.ts b/llm_normalizer/backend/tests/assistantMcpDiscoveryPlanner.test.ts index b50c786..6d21e03 100644 --- a/llm_normalizer/backend/tests/assistantMcpDiscoveryPlanner.test.ts +++ b/llm_normalizer/backend/tests/assistantMcpDiscoveryPlanner.test.ts @@ -48,6 +48,13 @@ describe("assistant MCP discovery planner", () => { expect(result.discovery_plan.answer_may_use_raw_model_claims).toBe(false); expect(result.data_need_graph?.business_fact_family).toBe("value_flow"); expect(result.catalog_chain_template_matches[0]).toBe("value_flow"); + expect(result.catalog_chain_template_alignment).toMatchObject({ + top_chain_template_match: "value_flow", + selected_chain_template_rank: 1, + selected_chain_is_catalog_template: true, + selected_chain_in_catalog_matches: true, + selected_chain_matches_top: true + }); expect(result.discovery_plan.execution_budget.max_probe_count).toBe(30); expect(result.reason_codes).toContain("planner_enabled_chunked_coverage_probe_budget"); expect(result.reason_codes).toContain("planner_consumed_data_need_graph_v1"); @@ -150,6 +157,7 @@ describe("assistant MCP discovery planner", () => { expect(result.reason_codes).toContain("planner_instantiated_catalog_chain_template_document_evidence"); expect(result.reason_codes).toContain("planner_catalog_chain_template_search_top_document_evidence"); expect(result.catalog_chain_template_matches[0]).toBe("document_evidence"); + expect(result.catalog_chain_template_alignment.selected_chain_matches_top).toBe(true); expect(result.reason_codes).not.toContain("planner_fell_back_to_recipe_primitives_after_empty_catalog_search"); }); @@ -564,6 +572,7 @@ describe("assistant MCP discovery planner", () => { expect(result.reason_codes).toContain("planner_selected_bidirectional_value_flow_comparison_from_data_need_graph"); expect(result.reason_codes).toContain("planner_instantiated_catalog_chain_template_value_flow_comparison"); expect(result.catalog_chain_template_matches[0]).toBe("value_flow_comparison"); + expect(result.catalog_chain_template_alignment.selected_chain_matches_top).toBe(true); }); it("does not collapse a ranking-shaped value graph into entity-resolution just because no subject is preselected", () => { @@ -635,6 +644,7 @@ describe("assistant MCP discovery planner", () => { expect(result.reason_codes).toContain("planner_selected_top_ranked_value_flow_from_data_need_graph"); expect(result.reason_codes).toContain("planner_instantiated_catalog_chain_template_value_flow_ranking"); expect(result.catalog_chain_template_matches[0]).toBe("value_flow_ranking"); + expect(result.catalog_chain_template_alignment.selected_chain_matches_top).toBe(true); }); it("does not collapse incoming-vs-outgoing comparison into entity-resolution when no counterparty is preselected", () => { @@ -942,6 +952,12 @@ describe("assistant MCP discovery planner", () => { expect(result.planner_status).toBe("needs_clarification"); expect(result.discovery_plan.plan_status).toBe("needs_clarification"); expect(result.catalog_chain_template_matches).toEqual([]); + expect(result.catalog_chain_template_alignment).toMatchObject({ + top_chain_template_match: null, + selected_chain_template_rank: null, + selected_chain_in_catalog_matches: false, + selected_chain_matches_top: false + }); expect(result.reason_codes).toContain("planner_needs_more_user_or_scope_context"); }); diff --git a/llm_normalizer/backend/tests/assistantMcpDiscoveryRuntimeBridge.test.ts b/llm_normalizer/backend/tests/assistantMcpDiscoveryRuntimeBridge.test.ts index 6bfb916..7c205e3 100644 --- a/llm_normalizer/backend/tests/assistantMcpDiscoveryRuntimeBridge.test.ts +++ b/llm_normalizer/backend/tests/assistantMcpDiscoveryRuntimeBridge.test.ts @@ -148,6 +148,7 @@ describe("assistant MCP discovery runtime bridge", () => { expect(result.loop_state.pending_axes).toContain("organization"); expect(result.loop_state.provided_axes).toContain("aggregate_axis"); expect(result.loop_state.catalog_chain_template_matches[0]).toBe("value_flow_ranking"); + expect(result.loop_state.catalog_chain_template_alignment.selected_chain_matches_top).toBe(true); expect(result.reason_codes).toContain("runtime_bridge_loop_state_awaiting_clarification"); });