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 e4092d3..ca821e6 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 @@ -57,7 +57,7 @@ The planner now instantiates selected evidence chains from this catalog for the The follow-up consolidation step moved the value-flow planner seams onto the same catalog fabric: -- bidirectional incoming-vs-outgoing comparison now instantiates `value_flow_comparison`; +- bidirectional incoming-vs-outgoing comparison now instantiates `value_flow_comparison`, including explicit-counterparty comparison graphs rather than only subjectless organization-scope graphs; - ranked revenue/payment questions now instantiate `value_flow_ranking`; - organization-scoped open totals now instantiate `value_flow` with subjectless primitives but catalog-owned axes and evidence-gate semantics; - heuristic fallback routes for value-flow, lifecycle, metadata, movement, document, entity, and unclassified metadata inspection now also use catalog chain templates. @@ -213,6 +213,13 @@ Latest validation after runtime/debug propagation of structured chain matches: - `npm.cmd run build`: passed - graphify rebuild: `5940 nodes`, `12909 edges`, `137 communities` +Latest validation after subject-aware bidirectional comparison arbitration: + +- targeted planner tests: passed, `36 passed` +- full MCP-discovery suite: passed, `282 passed`, `9 skipped` +- `npm.cmd run build`: passed +- graphify rebuild: `5940 nodes`, `12909 edges`, `137 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. diff --git a/docs/ARCH/11 - architecture_turnaround/README.md b/docs/ARCH/11 - architecture_turnaround/README.md index daeaf02..229492b 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; + - 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) Current honest status: @@ -92,7 +93,7 @@ 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: `~82%` 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, and runtime/debug propagation 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 +- 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` - current breakpoint: - the validated hot paths are no longer structurally broken; @@ -141,6 +142,7 @@ Latest live proof now includes: - catalog chain-template scoring accepted locally: catalog/planner slice passed `54/54`; full MCP-discovery slice passed `282/282` with `9` skipped; build passed; graphify rebuilt to `5938 nodes`, `12903 edges`, `139 communities` - 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` Current architectural reading: diff --git a/llm_normalizer/backend/dist/services/assistantMcpDiscoveryPlanner.js b/llm_normalizer/backend/dist/services/assistantMcpDiscoveryPlanner.js index dafbf8f..d923ac7 100644 --- a/llm_normalizer/backend/dist/services/assistantMcpDiscoveryPlanner.js +++ b/llm_normalizer/backend/dist/services/assistantMcpDiscoveryPlanner.js @@ -496,16 +496,19 @@ function recipeFor(input) { }); } if (graphFactFamily === "value_flow") { - if (dataNeedGraph?.comparison_need === "incoming_vs_outgoing" && !hasSubjectCandidates(dataNeedGraph)) { + if (dataNeedGraph?.comparison_need === "incoming_vs_outgoing") { pushUnique(axes, "amount"); pushUnique(axes, "coverage_target"); if (requestedAggregationAxis === "month" || graphAggregation === "by_month") { pushUnique(axes, "calendar_month"); } const template = (0, assistantMcpCatalogIndex_1.getAssistantMcpCatalogChainTemplate)("value_flow_comparison"); + const fallbackPrimitives = hasSubjectCandidates(dataNeedGraph) + ? ["resolve_entity_reference", ...template.fallback_primitives] + : template.fallback_primitives; const primitiveSelection = selectPrimitivesFromGraphAndCatalog({ dataNeedGraph, - fallbackPrimitives: template.fallback_primitives, + fallbackPrimitives, requiredAxes: axes, metadataSurface: input.metadataSurface, actionFamily: action, diff --git a/llm_normalizer/backend/src/services/assistantMcpDiscoveryPlanner.ts b/llm_normalizer/backend/src/services/assistantMcpDiscoveryPlanner.ts index 9a74e4a..ebafc55 100644 --- a/llm_normalizer/backend/src/services/assistantMcpDiscoveryPlanner.ts +++ b/llm_normalizer/backend/src/services/assistantMcpDiscoveryPlanner.ts @@ -703,16 +703,19 @@ function recipeFor(input: AssistantMcpDiscoveryPlannerInput): PlannerRecipe { } if (graphFactFamily === "value_flow") { - if (dataNeedGraph?.comparison_need === "incoming_vs_outgoing" && !hasSubjectCandidates(dataNeedGraph)) { + if (dataNeedGraph?.comparison_need === "incoming_vs_outgoing") { pushUnique(axes, "amount"); pushUnique(axes, "coverage_target"); if (requestedAggregationAxis === "month" || graphAggregation === "by_month") { pushUnique(axes, "calendar_month"); } const template = getAssistantMcpCatalogChainTemplate("value_flow_comparison"); + const fallbackPrimitives = hasSubjectCandidates(dataNeedGraph) + ? (["resolve_entity_reference", ...template.fallback_primitives] as AssistantMcpDiscoveryPrimitive[]) + : template.fallback_primitives; const primitiveSelection = selectPrimitivesFromGraphAndCatalog({ dataNeedGraph, - fallbackPrimitives: template.fallback_primitives, + fallbackPrimitives, requiredAxes: axes, metadataSurface: input.metadataSurface, actionFamily: action, diff --git a/llm_normalizer/backend/tests/assistantMcpDiscoveryPlanner.test.ts b/llm_normalizer/backend/tests/assistantMcpDiscoveryPlanner.test.ts index 81c454e..b50c786 100644 --- a/llm_normalizer/backend/tests/assistantMcpDiscoveryPlanner.test.ts +++ b/llm_normalizer/backend/tests/assistantMcpDiscoveryPlanner.test.ts @@ -520,7 +520,7 @@ describe("assistant MCP discovery planner", () => { expect(result.reason_codes).not.toContain("planner_selected_movement_from_confirmed_metadata_surface_ref"); }); - it("can select value-flow chain from data need graph even when turn meaning family is still under-specified", () => { + it("can select bidirectional value-flow comparison from data need graph even when turn meaning family is still under-specified", () => { const result = planAssistantMcpDiscovery({ dataNeedGraph: { schema_version: "assistant_data_need_graph_v1", @@ -552,23 +552,18 @@ describe("assistant MCP discovery planner", () => { }); expect(result.planner_status).toBe("ready_for_execution"); - expect(result.selected_chain_id).toBe("value_flow"); - expect(result.proposed_primitives).toEqual([ - "resolve_entity_reference", - "query_movements", - "aggregate_by_axis", - "probe_coverage" - ]); + expect(result.selected_chain_id).toBe("value_flow_comparison"); + expect(result.proposed_primitives).toEqual(["resolve_entity_reference", "query_movements", "probe_coverage"]); expect(result.required_axes).toEqual([ "counterparty", "period", - "aggregate_axis", "amount", "coverage_target", "calendar_month" ]); - expect(result.reason_codes).toContain("planner_selected_monthly_value_flow_from_data_need_graph"); - expect(result.reason_codes).toContain("planner_instantiated_catalog_chain_template_value_flow"); + 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"); }); it("does not collapse a ranking-shaped value graph into entity-resolution just because no subject is preselected", () => {