Planner Autonomy: выводить lane из metadata surface

This commit is contained in:
dctouch 2026-05-01 13:57:40 +03:00
parent a4db26a76e
commit ccfa9283e9
6 changed files with 249 additions and 53 deletions

1
.gitignore vendored
View File

@ -29,3 +29,4 @@ graphify-out/
# domain-case loop artifacts # domain-case loop artifacts
artifacts/domain_runs/* artifacts/domain_runs/*
!artifacts/domain_runs/.gitkeep !artifacts/domain_runs/.gitkeep
artifacts/runtime_logs/*.log

View File

@ -102,6 +102,14 @@ The runtime answer boundary still makes unsupported or unconfirmed inventory sta
- `must_not_claim` forbids presenting inventory planning as executed stock, supplier, purchase, or sale evidence; - `must_not_claim` forbids presenting inventory planning as executed stock, supplier, purchase, or sale evidence;
- technical unsupported-pilot limitation text is filtered out of user-facing lines, while existing bounded unknowns for lifecycle/value-flow remain intact. - technical unsupported-pilot limitation text is filtered out of user-facing lines, while existing bounded unknowns for lifecycle/value-flow remain intact.
The next local scoring step broadened metadata-surface autonomy without adding a new hard domain route:
- if a confirmed metadata surface is unambiguous and only exposes `Document.*`, `Register.*`, or `Catalog.*` objects, the planner can infer the next reviewed lane even when upstream has not yet filled `downstream_route_family`;
- inferred document surfaces instantiate `document_evidence`;
- inferred register/movement surfaces instantiate `movement_evidence`;
- inferred catalog surfaces instantiate `catalog_drilldown`;
- mixed or ambiguous surfaces still do not guess and continue through clarification / explicit data-need scoring.
## Why This Matters ## Why This Matters
This reduces the pressure to add one hard route per user wording. This reduces the pressure to add one hard route per user wording.
@ -166,16 +174,26 @@ Latest validation after the inventory exact-runtime bridge:
- `npm.cmd run build`: passed - `npm.cmd run build`: passed
- graphify rebuild: `5930 nodes`, `12884 edges`, `135 communities` - graphify rebuild: `5930 nodes`, `12884 edges`, `135 communities`
Latest validation after unambiguous metadata-surface lane inference:
- targeted planner tests: passed, `36 passed`
- full MCP-discovery suite: passed, `281 passed`, `9 skipped`
- `npm.cmd run build`: passed
- graphify rebuild: `5937 nodes`, `12899 edges`, `138 communities`
- live inventory full-pack attempt: `inventory_stock_exact_bridge_live_20260501_after_runtime_bridge`, status `partial`
- live attempt interpretation: route/intent/recipe/capability selection matched, but MCP execution failed with `MCP fetch failed: This operation was aborted`; direct proxy `get_metadata` also timed out while `/health` reported `active_sessions_count=0` and pending commands, so this is an infrastructure/polling-session blocker rather than accepted semantic evidence.
## Next Step ## Next Step
The next safe step is to validate the inventory exact-runtime bridge with live replay and then continue into broader reviewed scoring. The next safe step is to re-run live replay once the 1C side is actively polling the proxy, then continue into broader reviewed scoring.
Recommended order: Recommended order:
1. rerun the inventory canary and a mixed cross-stage canary against live 1C/MCP once the proxy is available; 1. reconnect or restart the 1C toolkit polling side, then rerun the inventory canary against live 1C/MCP;
2. broaden catalog scoring beyond explicit document/movement lane choice into unfamiliar 1C asks; 2. rerun a mixed cross-stage canary after the inventory canary is semantically clean;
3. grow primitive descriptors only where live replay shows a real evidence gap; 3. continue broadening catalog scoring into unfamiliar 1C asks where metadata surface and data-need graph can pick reviewed lanes;
4. keep phase19, phase21, phase22, value-flow, metadata ambiguity, and inventory-stock canaries as regression gates. 4. grow primitive descriptors only where live replay shows a real evidence gap;
5. keep phase19, phase21, phase22, value-flow, metadata ambiguity, and inventory-stock canaries as regression gates.
The key rule remains: The key rule remains:

View File

@ -79,6 +79,7 @@ It now documents a turnaround that is already operational in code, already mater
- inventory stock snapshot, supplier overlap, purchase provenance, and sale trace are now reviewed catalog chain templates; generic free-form inventory execution remains forbidden, and evidence must pass through reviewed exact recipe bridges; - inventory stock snapshot, supplier overlap, purchase provenance, and sale trace are now reviewed catalog chain templates; generic free-form inventory execution remains forbidden, and evidence must pass through reviewed exact recipe bridges;
- runtime bridge and answer adapter now keep unsupported inventory route templates behind an explicit user-facing boundary instead of letting template planning look like confirmed stock/supplier/purchase/sale evidence; - runtime bridge and answer adapter now keep unsupported inventory route templates behind an explicit user-facing boundary instead of letting template planning look like confirmed stock/supplier/purchase/sale evidence;
- 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; - 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;
- live map sync: [20 - planner_autonomy_consolidation_2026-05-01.md](./20%20-%20planner_autonomy_consolidation_2026-05-01.md) - live map sync: [20 - planner_autonomy_consolidation_2026-05-01.md](./20%20-%20planner_autonomy_consolidation_2026-05-01.md)
Current honest status: Current honest status:
@ -90,8 +91,8 @@ Current honest status:
- open-world bounded-autonomy readiness: `~85%` - 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 - 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 - active inventory-stock breadth slice progress: `100%` for the declared scenario pack, not for arbitrary inventory questions
- Planner Autonomy Consolidation progress: `~74%` for the declared module, with catalog-fabric, value-flow arbitration, lifecycle bounded inference, broad-evaluation bridge, inventory catalog templates, inventory runtime-boundary honesty, and exact inventory recipe bridging validated locally, but live replay for the new bridge and broader unfamiliar 1C asks still pending - Planner Autonomy Consolidation progress: `~78%` 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, and unambiguous metadata-surface lane inference 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: `5930 nodes`, `12884 edges`, `135 communities` - graph snapshot after latest rebuild: `5937 nodes`, `12899 edges`, `138 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;
@ -134,6 +135,8 @@ Latest live proof now includes:
- inventory template lift accepted locally: catalog/data-need/planner/turn-input slice passed `139/139` with `6` skipped; full MCP-discovery slice passed `276/276` with `9` skipped; build passed; graphify stayed at `5912 nodes`, `12833 edges`, `138 communities` - inventory template lift accepted locally: catalog/data-need/planner/turn-input slice passed `139/139` with `6` skipped; full MCP-discovery slice passed `276/276` with `9` skipped; build passed; graphify stayed at `5912 nodes`, `12833 edges`, `138 communities`
- inventory runtime-boundary hardening accepted locally: runtime-bridge/answer-adapter/pilot-executor slice passed `68/68` with `1` skipped; full MCP-discovery slice passed `277/277` with `9` skipped; build passed; graphify rebuilt to `5913 nodes`, `12837 edges`, `138 communities` - inventory runtime-boundary hardening accepted locally: runtime-bridge/answer-adapter/pilot-executor slice passed `68/68` with `1` skipped; full MCP-discovery slice passed `277/277` with `9` skipped; build passed; graphify rebuilt to `5913 nodes`, `12837 edges`, `138 communities`
- inventory exact-runtime bridge accepted locally: runtime-bridge/answer-adapter/pilot-executor slice passed `70/70` with `1` skipped; full MCP-discovery slice passed `279/279` with `9` skipped; build passed; graphify rebuilt to `5930 nodes`, `12884 edges`, `135 communities` - inventory exact-runtime bridge accepted locally: runtime-bridge/answer-adapter/pilot-executor slice passed `70/70` with `1` skipped; full MCP-discovery slice passed `279/279` with `9` skipped; build passed; graphify rebuilt to `5930 nodes`, `12884 edges`, `135 communities`
- unambiguous metadata-surface lane inference accepted locally: planner slice passed `36/36`; full MCP-discovery slice passed `281/281` with `9` skipped; build passed; graphify rebuilt to `5937 nodes`, `12899 edges`, `138 communities`
- live inventory exact-bridge rerun `inventory_stock_exact_bridge_live_20260501_after_runtime_bridge` is recorded as infrastructure-blocked, not accepted: route/intent/recipe/capability matched, but MCP calls aborted and direct `get_metadata` timed out while proxy health showed `active_sessions_count=0` with pending commands
Current architectural reading: Current architectural reading:

View File

@ -123,6 +123,59 @@ function mergeCatalogPrimitivesWithFallback(catalogPrimitives, fallbackPrimitive
} }
return result; return result;
} }
function normalizeMetadataSurfaceToken(value) {
return String(value ?? "").trim().toLowerCase().replace(/[\s_.-]+/g, "");
}
function metadataSurfaceValueSuggestsDocument(value) {
const token = normalizeMetadataSurfaceToken(value);
return token.includes("document") || token.includes("invoice") || token.includes("waybill") || token.includes("act");
}
function metadataSurfaceValueSuggestsMovement(value) {
const token = normalizeMetadataSurfaceToken(value);
return token.includes("register") || token.includes("movement") || token.includes("operation") || token.includes("bank");
}
function metadataSurfaceValueSuggestsCatalog(value) {
const token = normalizeMetadataSurfaceToken(value);
return token.includes("catalog") || token.includes("directory");
}
function routeFamilyFromMetadataSurfaceRef(surface) {
if (!surface || surface.ambiguity_detected) {
return null;
}
if (surface.downstream_route_family) {
return surface.downstream_route_family;
}
const values = [surface.selected_entity_set ?? "", ...surface.selected_surface_objects];
const documentHit = values.some(metadataSurfaceValueSuggestsDocument);
const movementHit = values.some(metadataSurfaceValueSuggestsMovement);
const catalogHit = values.some(metadataSurfaceValueSuggestsCatalog);
const hitCount = [documentHit, movementHit, catalogHit].filter(Boolean).length;
if (hitCount !== 1) {
return null;
}
if (documentHit) {
return "document_evidence";
}
if (movementHit) {
return "movement_evidence";
}
return "catalog_drilldown";
}
function inferredRouteFamilyFromMetadataSurfaceRef(surface) {
return Boolean(surface && !surface.downstream_route_family && routeFamilyFromMetadataSurfaceRef(surface));
}
function preferredPrimitiveForRouteFamily(routeFamily) {
if (routeFamily === "document_evidence") {
return "query_documents";
}
if (routeFamily === "movement_evidence") {
return "query_movements";
}
if (routeFamily === "catalog_drilldown") {
return "drilldown_related_objects";
}
return null;
}
function preferredPrimitiveFromMetadataSurface(surface) { function preferredPrimitiveFromMetadataSurface(surface) {
if (surface?.ambiguity_detected) { if (surface?.ambiguity_detected) {
return null; return null;
@ -131,16 +184,7 @@ function preferredPrimitiveFromMetadataSurface(surface) {
if (recommendedPrimitive) { if (recommendedPrimitive) {
return recommendedPrimitive; return recommendedPrimitive;
} }
if (surface?.downstream_route_family === "document_evidence") { return preferredPrimitiveForRouteFamily(routeFamilyFromMetadataSurfaceRef(surface));
return "query_documents";
}
if (surface?.downstream_route_family === "movement_evidence") {
return "query_movements";
}
if (surface?.downstream_route_family === "catalog_drilldown") {
return "drilldown_related_objects";
}
return null;
} }
function preferredPrimitiveFromExplicitDataNeedGraph(graph) { function preferredPrimitiveFromExplicitDataNeedGraph(graph) {
const factFamily = lower(graph?.business_fact_family); const factFamily = lower(graph?.business_fact_family);
@ -289,7 +333,8 @@ function budgetOverrideFor(input, recipe) {
} }
function routeFamilyFromThinMetadataSurfaceInput(input) { function routeFamilyFromThinMetadataSurfaceInput(input) {
const surface = input.metadataSurface ?? null; const surface = input.metadataSurface ?? null;
if (!surface || surface.ambiguity_detected || !surface.downstream_route_family || !surface.recommended_next_primitive) { const surfaceRouteFamily = routeFamilyFromMetadataSurfaceRef(surface);
if (!surface || surface.ambiguity_detected || !surfaceRouteFamily) {
return null; return null;
} }
const meaning = input.turnMeaning ?? null; const meaning = input.turnMeaning ?? null;
@ -305,20 +350,16 @@ function routeFamilyFromThinMetadataSurfaceInput(input) {
return null; return null;
} }
if (graphFactFamily === "document_evidence" || includesAny(combined, ["document", "documents", "list_documents"])) { if (graphFactFamily === "document_evidence" || includesAny(combined, ["document", "documents", "list_documents"])) {
return surface.downstream_route_family === "document_evidence" ? "document_evidence" : null; return surfaceRouteFamily === "document_evidence" ? "document_evidence" : null;
} }
if (graphFactFamily === "movement_evidence" || includesAny(combined, ["movement", "movements", "list_movements", "bank_operations"])) { if (graphFactFamily === "movement_evidence" || includesAny(combined, ["movement", "movements", "list_movements", "bank_operations"])) {
return surface.downstream_route_family === "movement_evidence" ? "movement_evidence" : null; return surfaceRouteFamily === "movement_evidence" ? "movement_evidence" : null;
} }
if (graphFactFamily === "schema_surface" || includesAny(combined, ["catalog", "directory", "inspect_catalog"])) { if (graphFactFamily === "schema_surface" || includesAny(combined, ["catalog", "directory", "inspect_catalog"])) {
return surface.downstream_route_family === "catalog_drilldown" ? "catalog_drilldown" : null; return surfaceRouteFamily === "catalog_drilldown" ? "catalog_drilldown" : null;
} }
if (!graphFactFamily && !domain && !action) { if (!graphFactFamily && !domain && !action) {
if (surface.downstream_route_family === "document_evidence" || return surfaceRouteFamily;
surface.downstream_route_family === "movement_evidence" ||
surface.downstream_route_family === "catalog_drilldown") {
return surface.downstream_route_family;
}
} }
return null; return null;
} }
@ -370,7 +411,12 @@ function recipeFor(input) {
primitives: primitiveSelection.primitives, primitives: primitiveSelection.primitives,
reason: "planner_selected_document_from_confirmed_metadata_surface_ref", reason: "planner_selected_document_from_confirmed_metadata_surface_ref",
chainSummary: "Ground the next checked document lane from the confirmed metadata surface, then fetch scoped document rows and probe coverage before answering.", chainSummary: "Ground the next checked document lane from the confirmed metadata surface, then fetch scoped document rows and probe coverage before answering.",
extraReasons: primitiveSelection.reasonCodes extraReasons: [
...primitiveSelection.reasonCodes,
...(inferredRouteFamilyFromMetadataSurfaceRef(input.metadataSurface)
? ["planner_inferred_next_lane_from_unambiguous_metadata_surface"]
: [])
]
}); });
} }
if (thinSurfaceRouteFamily === "movement_evidence") { if (thinSurfaceRouteFamily === "movement_evidence") {
@ -389,7 +435,12 @@ function recipeFor(input) {
primitives: primitiveSelection.primitives, primitives: primitiveSelection.primitives,
reason: "planner_selected_movement_from_confirmed_metadata_surface_ref", reason: "planner_selected_movement_from_confirmed_metadata_surface_ref",
chainSummary: "Ground the next checked movement lane from the confirmed metadata surface, then fetch scoped movement rows and probe coverage before answering.", chainSummary: "Ground the next checked movement lane from the confirmed metadata surface, then fetch scoped movement rows and probe coverage before answering.",
extraReasons: primitiveSelection.reasonCodes extraReasons: [
...primitiveSelection.reasonCodes,
...(inferredRouteFamilyFromMetadataSurfaceRef(input.metadataSurface)
? ["planner_inferred_next_lane_from_unambiguous_metadata_surface"]
: [])
]
}); });
} }
if (thinSurfaceRouteFamily === "catalog_drilldown") { if (thinSurfaceRouteFamily === "catalog_drilldown") {
@ -408,7 +459,12 @@ function recipeFor(input) {
primitives: primitiveSelection.primitives, primitives: primitiveSelection.primitives,
reason: "planner_selected_catalog_drilldown_from_confirmed_metadata_surface_ref", reason: "planner_selected_catalog_drilldown_from_confirmed_metadata_surface_ref",
chainSummary: "Drill deeper into the confirmed catalog-oriented metadata surface, inspect related metadata objects, and keep the next safe lane grounded in checked schema evidence.", chainSummary: "Drill deeper into the confirmed catalog-oriented metadata surface, inspect related metadata objects, and keep the next safe lane grounded in checked schema evidence.",
extraReasons: primitiveSelection.reasonCodes extraReasons: [
...primitiveSelection.reasonCodes,
...(inferredRouteFamilyFromMetadataSurfaceRef(input.metadataSurface)
? ["planner_inferred_next_lane_from_unambiguous_metadata_surface"]
: [])
]
}); });
} }
if (graphFactFamily === "value_flow") { if (graphFactFamily === "value_flow") {

View File

@ -249,6 +249,73 @@ function mergeCatalogPrimitivesWithFallback(
return result; return result;
} }
function normalizeMetadataSurfaceToken(value: unknown): string {
return String(value ?? "").trim().toLowerCase().replace(/[\s_.-]+/g, "");
}
function metadataSurfaceValueSuggestsDocument(value: unknown): boolean {
const token = normalizeMetadataSurfaceToken(value);
return token.includes("document") || token.includes("invoice") || token.includes("waybill") || token.includes("act");
}
function metadataSurfaceValueSuggestsMovement(value: unknown): boolean {
const token = normalizeMetadataSurfaceToken(value);
return token.includes("register") || token.includes("movement") || token.includes("operation") || token.includes("bank");
}
function metadataSurfaceValueSuggestsCatalog(value: unknown): boolean {
const token = normalizeMetadataSurfaceToken(value);
return token.includes("catalog") || token.includes("directory");
}
function routeFamilyFromMetadataSurfaceRef(
surface: AssistantMcpDiscoveryMetadataSurfaceRef | null | undefined
): AssistantMcpDiscoveryMetadataRouteFamily | null {
if (!surface || surface.ambiguity_detected) {
return null;
}
if (surface.downstream_route_family) {
return surface.downstream_route_family;
}
const values = [surface.selected_entity_set ?? "", ...surface.selected_surface_objects];
const documentHit = values.some(metadataSurfaceValueSuggestsDocument);
const movementHit = values.some(metadataSurfaceValueSuggestsMovement);
const catalogHit = values.some(metadataSurfaceValueSuggestsCatalog);
const hitCount = [documentHit, movementHit, catalogHit].filter(Boolean).length;
if (hitCount !== 1) {
return null;
}
if (documentHit) {
return "document_evidence";
}
if (movementHit) {
return "movement_evidence";
}
return "catalog_drilldown";
}
function inferredRouteFamilyFromMetadataSurfaceRef(
surface: AssistantMcpDiscoveryMetadataSurfaceRef | null | undefined
): boolean {
return Boolean(surface && !surface.downstream_route_family && routeFamilyFromMetadataSurfaceRef(surface));
}
function preferredPrimitiveForRouteFamily(
routeFamily: AssistantMcpDiscoveryMetadataRouteFamily | null
): AssistantMcpDiscoveryPrimitive | null {
if (routeFamily === "document_evidence") {
return "query_documents";
}
if (routeFamily === "movement_evidence") {
return "query_movements";
}
if (routeFamily === "catalog_drilldown") {
return "drilldown_related_objects";
}
return null;
}
function preferredPrimitiveFromMetadataSurface( function preferredPrimitiveFromMetadataSurface(
surface: AssistantMcpDiscoveryMetadataSurfaceRef | null | undefined surface: AssistantMcpDiscoveryMetadataSurfaceRef | null | undefined
): AssistantMcpDiscoveryPrimitive | null { ): AssistantMcpDiscoveryPrimitive | null {
@ -259,16 +326,7 @@ function preferredPrimitiveFromMetadataSurface(
if (recommendedPrimitive) { if (recommendedPrimitive) {
return recommendedPrimitive; return recommendedPrimitive;
} }
if (surface?.downstream_route_family === "document_evidence") { return preferredPrimitiveForRouteFamily(routeFamilyFromMetadataSurfaceRef(surface));
return "query_documents";
}
if (surface?.downstream_route_family === "movement_evidence") {
return "query_movements";
}
if (surface?.downstream_route_family === "catalog_drilldown") {
return "drilldown_related_objects";
}
return null;
} }
function preferredPrimitiveFromExplicitDataNeedGraph( function preferredPrimitiveFromExplicitDataNeedGraph(
@ -466,7 +524,8 @@ function routeFamilyFromThinMetadataSurfaceInput(
input: AssistantMcpDiscoveryPlannerInput input: AssistantMcpDiscoveryPlannerInput
): AssistantMcpDiscoveryMetadataRouteFamily | null { ): AssistantMcpDiscoveryMetadataRouteFamily | null {
const surface = input.metadataSurface ?? null; const surface = input.metadataSurface ?? null;
if (!surface || surface.ambiguity_detected || !surface.downstream_route_family || !surface.recommended_next_primitive) { const surfaceRouteFamily = routeFamilyFromMetadataSurfaceRef(surface);
if (!surface || surface.ambiguity_detected || !surfaceRouteFamily) {
return null; return null;
} }
const meaning = input.turnMeaning ?? null; const meaning = input.turnMeaning ?? null;
@ -483,22 +542,16 @@ function routeFamilyFromThinMetadataSurfaceInput(
return null; return null;
} }
if (graphFactFamily === "document_evidence" || includesAny(combined, ["document", "documents", "list_documents"])) { if (graphFactFamily === "document_evidence" || includesAny(combined, ["document", "documents", "list_documents"])) {
return surface.downstream_route_family === "document_evidence" ? "document_evidence" : null; return surfaceRouteFamily === "document_evidence" ? "document_evidence" : null;
} }
if (graphFactFamily === "movement_evidence" || includesAny(combined, ["movement", "movements", "list_movements", "bank_operations"])) { if (graphFactFamily === "movement_evidence" || includesAny(combined, ["movement", "movements", "list_movements", "bank_operations"])) {
return surface.downstream_route_family === "movement_evidence" ? "movement_evidence" : null; return surfaceRouteFamily === "movement_evidence" ? "movement_evidence" : null;
} }
if (graphFactFamily === "schema_surface" || includesAny(combined, ["catalog", "directory", "inspect_catalog"])) { if (graphFactFamily === "schema_surface" || includesAny(combined, ["catalog", "directory", "inspect_catalog"])) {
return surface.downstream_route_family === "catalog_drilldown" ? "catalog_drilldown" : null; return surfaceRouteFamily === "catalog_drilldown" ? "catalog_drilldown" : null;
} }
if (!graphFactFamily && !domain && !action) { if (!graphFactFamily && !domain && !action) {
if ( return surfaceRouteFamily;
surface.downstream_route_family === "document_evidence" ||
surface.downstream_route_family === "movement_evidence" ||
surface.downstream_route_family === "catalog_drilldown"
) {
return surface.downstream_route_family;
}
} }
return null; return null;
} }
@ -555,7 +608,12 @@ function recipeFor(input: AssistantMcpDiscoveryPlannerInput): PlannerRecipe {
reason: "planner_selected_document_from_confirmed_metadata_surface_ref", reason: "planner_selected_document_from_confirmed_metadata_surface_ref",
chainSummary: chainSummary:
"Ground the next checked document lane from the confirmed metadata surface, then fetch scoped document rows and probe coverage before answering.", "Ground the next checked document lane from the confirmed metadata surface, then fetch scoped document rows and probe coverage before answering.",
extraReasons: primitiveSelection.reasonCodes extraReasons: [
...primitiveSelection.reasonCodes,
...(inferredRouteFamilyFromMetadataSurfaceRef(input.metadataSurface)
? ["planner_inferred_next_lane_from_unambiguous_metadata_surface"]
: [])
]
}); });
} }
if (thinSurfaceRouteFamily === "movement_evidence") { if (thinSurfaceRouteFamily === "movement_evidence") {
@ -575,7 +633,12 @@ function recipeFor(input: AssistantMcpDiscoveryPlannerInput): PlannerRecipe {
reason: "planner_selected_movement_from_confirmed_metadata_surface_ref", reason: "planner_selected_movement_from_confirmed_metadata_surface_ref",
chainSummary: chainSummary:
"Ground the next checked movement lane from the confirmed metadata surface, then fetch scoped movement rows and probe coverage before answering.", "Ground the next checked movement lane from the confirmed metadata surface, then fetch scoped movement rows and probe coverage before answering.",
extraReasons: primitiveSelection.reasonCodes extraReasons: [
...primitiveSelection.reasonCodes,
...(inferredRouteFamilyFromMetadataSurfaceRef(input.metadataSurface)
? ["planner_inferred_next_lane_from_unambiguous_metadata_surface"]
: [])
]
}); });
} }
if (thinSurfaceRouteFamily === "catalog_drilldown") { if (thinSurfaceRouteFamily === "catalog_drilldown") {
@ -595,7 +658,12 @@ function recipeFor(input: AssistantMcpDiscoveryPlannerInput): PlannerRecipe {
reason: "planner_selected_catalog_drilldown_from_confirmed_metadata_surface_ref", reason: "planner_selected_catalog_drilldown_from_confirmed_metadata_surface_ref",
chainSummary: chainSummary:
"Drill deeper into the confirmed catalog-oriented metadata surface, inspect related metadata objects, and keep the next safe lane grounded in checked schema evidence.", "Drill deeper into the confirmed catalog-oriented metadata surface, inspect related metadata objects, and keep the next safe lane grounded in checked schema evidence.",
extraReasons: primitiveSelection.reasonCodes extraReasons: [
...primitiveSelection.reasonCodes,
...(inferredRouteFamilyFromMetadataSurfaceRef(input.metadataSurface)
? ["planner_inferred_next_lane_from_unambiguous_metadata_surface"]
: [])
]
}); });
} }

View File

@ -218,6 +218,31 @@ describe("assistant MCP discovery planner", () => {
expect(result.reason_codes).toContain("planner_selected_catalog_primitives_from_metadata_surface_search"); expect(result.reason_codes).toContain("planner_selected_catalog_primitives_from_metadata_surface_search");
}); });
it("infers document evidence from an unambiguous document metadata surface even before a downstream lane is labeled", () => {
const result = planAssistantMcpDiscovery({
metadataSurface: {
selected_entity_set: "Document",
selected_surface_objects: ["Document.InvoiceIssued"],
downstream_route_family: null,
route_family_selection_basis: null,
recommended_next_primitive: null,
ambiguity_detected: false,
ambiguity_entity_sets: []
},
turnMeaning: {
explicit_entity_candidates: ["SVK"],
explicit_date_scope: "2020"
}
});
expect(result.planner_status).toBe("ready_for_execution");
expect(result.selected_chain_id).toBe("document_evidence");
expect(result.proposed_primitives).toEqual(["resolve_entity_reference", "query_documents", "probe_coverage"]);
expect(result.proposed_primitives).not.toContain("query_movements");
expect(result.reason_codes).toContain("planner_inferred_next_lane_from_unambiguous_metadata_surface");
expect(result.reason_codes).toContain("planner_surface_aware_next_lane_from_confirmed_metadata_objects");
});
it("builds a movement discovery plan without aggregating value-flow totals", () => { it("builds a movement discovery plan without aggregating value-flow totals", () => {
const result = planAssistantMcpDiscovery({ const result = planAssistantMcpDiscovery({
dataNeedGraph: { dataNeedGraph: {
@ -325,6 +350,31 @@ describe("assistant MCP discovery planner", () => {
expect(result.reason_codes).toContain("planner_selected_catalog_primitives_from_metadata_surface_search"); expect(result.reason_codes).toContain("planner_selected_catalog_primitives_from_metadata_surface_search");
}); });
it("infers movement evidence from an unambiguous register metadata surface even before a downstream lane is labeled", () => {
const result = planAssistantMcpDiscovery({
metadataSurface: {
selected_entity_set: "AccumulationRegister",
selected_surface_objects: ["AccumulationRegister.BankOperations"],
downstream_route_family: null,
route_family_selection_basis: null,
recommended_next_primitive: null,
ambiguity_detected: false,
ambiguity_entity_sets: []
},
turnMeaning: {
explicit_entity_candidates: ["SVK"],
explicit_date_scope: "2020"
}
});
expect(result.planner_status).toBe("ready_for_execution");
expect(result.selected_chain_id).toBe("movement_evidence");
expect(result.proposed_primitives).toEqual(["resolve_entity_reference", "query_movements", "probe_coverage"]);
expect(result.proposed_primitives).not.toContain("query_documents");
expect(result.reason_codes).toContain("planner_inferred_next_lane_from_unambiguous_metadata_surface");
expect(result.reason_codes).toContain("planner_surface_aware_next_lane_from_confirmed_metadata_objects");
});
it("can select catalog drilldown directly from a confirmed catalog metadata surface when the follow-up itself is thin", () => { it("can select catalog drilldown directly from a confirmed catalog metadata surface when the follow-up itself is thin", () => {
const result = planAssistantMcpDiscovery({ const result = planAssistantMcpDiscovery({
metadataSurface: { metadataSurface: {