ARCH: восстановить годовое покрытие MCP discovery помесячными пробами
This commit is contained in:
@@ -233,6 +233,45 @@ describe("assistant MCP discovery answer adapter", () => {
|
||||
expect(draft.reason_codes).toContain("answer_contains_monthly_breakdown");
|
||||
});
|
||||
|
||||
it("keeps recovered yearly coverage out of the unknown block and explains the recovery as bounded inference", async () => {
|
||||
const planner = planAssistantMcpDiscovery({
|
||||
turnMeaning: {
|
||||
asked_domain_family: "counterparty_value",
|
||||
asked_action_family: "payout",
|
||||
explicit_entity_candidates: ["SVK"],
|
||||
explicit_date_scope: "2020",
|
||||
unsupported_but_understood_family: "counterparty_payouts_or_outflow"
|
||||
}
|
||||
});
|
||||
const broadRows = Array.from({ length: 100 }, (_, index) => ({
|
||||
Period: `2020-01-${String((index % 28) + 1).padStart(2, "0")}T00:00:00`,
|
||||
Amount: 10,
|
||||
Counterparty: "SVK"
|
||||
}));
|
||||
const monthlyResults = Array.from({ length: 12 }, (_, index) => ({
|
||||
rows: [
|
||||
{
|
||||
Period: `2020-${String(index + 1).padStart(2, "0")}-05T00:00:00`,
|
||||
Amount: (index + 1) * 100,
|
||||
Counterparty: "SVK"
|
||||
}
|
||||
]
|
||||
}));
|
||||
const pilot = await executeAssistantMcpDiscoveryPilot(
|
||||
planner,
|
||||
buildSequentialDeps([{ rows: broadRows }, ...monthlyResults])
|
||||
);
|
||||
|
||||
const draft = buildAssistantMcpDiscoveryAnswerDraft(pilot);
|
||||
|
||||
expect(draft.inference_lines).toContain(
|
||||
"Requested period coverage was recovered through monthly 1C value-flow probes after the broad probe hit the row limit"
|
||||
);
|
||||
expect(draft.unknown_lines).not.toContain(
|
||||
"Complete requested-period coverage is not proven because the MCP discovery probe row limit was reached"
|
||||
);
|
||||
});
|
||||
|
||||
it("does not leak primitive names or query text into user-facing lines", async () => {
|
||||
const planner = planAssistantMcpDiscovery({
|
||||
turnMeaning: {
|
||||
|
||||
@@ -199,6 +199,56 @@ describe("assistant MCP discovery pilot executor", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("recovers yearly value-flow coverage by splitting a limited broad probe into monthly subprobes", async () => {
|
||||
const planner = planAssistantMcpDiscovery({
|
||||
turnMeaning: {
|
||||
asked_domain_family: "counterparty_value",
|
||||
asked_action_family: "payout",
|
||||
explicit_entity_candidates: ["SVK"],
|
||||
explicit_date_scope: "2020",
|
||||
unsupported_but_understood_family: "counterparty_payouts_or_outflow"
|
||||
}
|
||||
});
|
||||
const broadRows = Array.from({ length: 100 }, (_, index) => ({
|
||||
Period: `2020-01-${String((index % 28) + 1).padStart(2, "0")}T00:00:00`,
|
||||
Amount: 10,
|
||||
Counterparty: "SVK"
|
||||
}));
|
||||
const monthlyResults = Array.from({ length: 12 }, (_, index) => ({
|
||||
rows: [
|
||||
{
|
||||
Period: `2020-${String(index + 1).padStart(2, "0")}-05T00:00:00`,
|
||||
Amount: (index + 1) * 100,
|
||||
Counterparty: "SVK"
|
||||
}
|
||||
]
|
||||
}));
|
||||
|
||||
const result = await executeAssistantMcpDiscoveryPilot(
|
||||
planner,
|
||||
buildSequentialDeps([{ rows: broadRows }, ...monthlyResults])
|
||||
);
|
||||
|
||||
expect(result.derived_value_flow).toMatchObject({
|
||||
value_flow_direction: "outgoing_supplier_payout",
|
||||
coverage_limited_by_probe_limit: false,
|
||||
coverage_recovered_by_period_chunking: true,
|
||||
period_chunking_granularity: "month",
|
||||
rows_matched: 12,
|
||||
rows_with_amount: 12,
|
||||
total_amount: 7800,
|
||||
first_movement_date: "2020-01-05",
|
||||
latest_movement_date: "2020-12-05"
|
||||
});
|
||||
expect(result.evidence.inferred_facts).toContain(
|
||||
"Requested period coverage was recovered through monthly 1C value-flow probes after the broad probe hit the row limit"
|
||||
);
|
||||
expect(result.evidence.unknown_facts).not.toContain(
|
||||
"Complete requested-period coverage is not proven because the MCP discovery probe row limit was reached"
|
||||
);
|
||||
expect(result.reason_codes).toContain("pilot_monthly_period_chunking_recovered_coverage");
|
||||
});
|
||||
|
||||
it("executes bidirectional value-flow queries and derives guarded net cash flow", async () => {
|
||||
const planner = planAssistantMcpDiscovery({
|
||||
turnMeaning: {
|
||||
@@ -319,6 +369,69 @@ describe("assistant MCP discovery pilot executor", () => {
|
||||
expect(result.reason_codes).toContain("pilot_derived_bidirectional_monthly_breakdown_from_confirmed_rows");
|
||||
});
|
||||
|
||||
it("recovers bidirectional yearly coverage when one side is rebuilt from monthly subprobes", async () => {
|
||||
const planner = planAssistantMcpDiscovery({
|
||||
turnMeaning: {
|
||||
asked_domain_family: "counterparty_value",
|
||||
asked_action_family: "net_value_flow",
|
||||
explicit_entity_candidates: ["SVK"],
|
||||
explicit_date_scope: "2020",
|
||||
unsupported_but_understood_family: "counterparty_bidirectional_value_flow_or_netting"
|
||||
}
|
||||
});
|
||||
const outgoingBroadRows = Array.from({ length: 100 }, (_, index) => ({
|
||||
Period: `2020-01-${String((index % 28) + 1).padStart(2, "0")}T00:00:00`,
|
||||
Amount: 10,
|
||||
Counterparty: "SVK"
|
||||
}));
|
||||
const outgoingMonthlyResults = Array.from({ length: 12 }, (_, index) => ({
|
||||
rows: [
|
||||
{
|
||||
Period: `2020-${String(index + 1).padStart(2, "0")}-10T00:00:00`,
|
||||
Amount: (index + 1) * 50,
|
||||
Counterparty: "SVK"
|
||||
}
|
||||
]
|
||||
}));
|
||||
const deps = buildSequentialDeps([
|
||||
{
|
||||
rows: [
|
||||
{ Period: "2020-01-15T00:00:00", Amount: 10000, Counterparty: "SVK" },
|
||||
{ Period: "2020-02-20T00:00:00", Amount: 10000, Counterparty: "SVK" }
|
||||
]
|
||||
},
|
||||
{ rows: outgoingBroadRows },
|
||||
...outgoingMonthlyResults
|
||||
]);
|
||||
|
||||
const result = await executeAssistantMcpDiscoveryPilot(planner, deps);
|
||||
|
||||
expect(result.derived_bidirectional_value_flow).toMatchObject({
|
||||
coverage_limited_by_probe_limit: false,
|
||||
coverage_recovered_by_period_chunking: true,
|
||||
period_chunking_granularity: "month",
|
||||
net_amount: 16100,
|
||||
incoming_customer_revenue: {
|
||||
total_amount: 20000,
|
||||
coverage_limited_by_probe_limit: false
|
||||
},
|
||||
outgoing_supplier_payout: {
|
||||
total_amount: 3900,
|
||||
coverage_limited_by_probe_limit: false,
|
||||
coverage_recovered_by_period_chunking: true,
|
||||
period_chunking_granularity: "month"
|
||||
}
|
||||
});
|
||||
expect(result.evidence.inferred_facts).toContain(
|
||||
"Requested period coverage for bidirectional value-flow was recovered through monthly 1C side probes after a broad probe hit the row limit"
|
||||
);
|
||||
expect(result.evidence.unknown_facts).not.toContain(
|
||||
"Complete requested-period coverage for bidirectional value-flow is not proven because at least one MCP discovery probe row limit was reached"
|
||||
);
|
||||
expect(result.reason_codes).toContain("pilot_bidirectional_outgoing_monthly_period_chunking_recovered_coverage");
|
||||
expect(deps.executeAddressMcpQuery).toHaveBeenCalledTimes(14);
|
||||
});
|
||||
|
||||
it("keeps non-lifecycle ready plans unsupported until a dedicated pilot exists", async () => {
|
||||
const planner = planAssistantMcpDiscovery({
|
||||
turnMeaning: {
|
||||
|
||||
@@ -23,6 +23,8 @@ describe("assistant MCP discovery planner", () => {
|
||||
expect(result.required_axes).toEqual(["counterparty", "period", "aggregate_axis", "amount", "coverage_target"]);
|
||||
expect(result.catalog_review.review_status).toBe("catalog_compatible");
|
||||
expect(result.discovery_plan.answer_may_use_raw_model_claims).toBe(false);
|
||||
expect(result.discovery_plan.execution_budget.max_probe_count).toBe(30);
|
||||
expect(result.reason_codes).toContain("planner_enabled_chunked_coverage_probe_budget");
|
||||
});
|
||||
|
||||
it("keeps a value-flow plan in clarification state when period axis is missing", () => {
|
||||
@@ -67,6 +69,7 @@ describe("assistant MCP discovery planner", () => {
|
||||
"calendar_month"
|
||||
]);
|
||||
expect(result.reason_codes).toContain("planner_selected_monthly_value_flow_recipe");
|
||||
expect(result.discovery_plan.execution_budget.max_probe_count).toBe(30);
|
||||
});
|
||||
|
||||
it("builds a document discovery plan without falling back to movement primitives", () => {
|
||||
|
||||
@@ -26,7 +26,7 @@ describe("assistant MCP discovery policy", () => {
|
||||
expect(plan.rejected_primitives).toEqual(["drop_database"]);
|
||||
expect(plan.requires_evidence_gate).toBe(true);
|
||||
expect(plan.answer_may_use_raw_model_claims).toBe(false);
|
||||
expect(plan.execution_budget).toEqual({ max_probe_count: 6, max_rows_per_probe: 500 });
|
||||
expect(plan.execution_budget).toEqual({ max_probe_count: 36, max_rows_per_probe: 500 });
|
||||
expect(plan.reason_codes).toContain("model_proposed_unregistered_mcp_primitive");
|
||||
});
|
||||
|
||||
|
||||
@@ -182,6 +182,33 @@ describe("assistant MCP discovery response candidate", () => {
|
||||
expect(candidate.reply_text).not.toContain("Counterparty monthly net value-flow breakdown");
|
||||
});
|
||||
|
||||
it("localizes recovered coverage facts without leaking broad-probe wording", () => {
|
||||
const candidate = buildAssistantMcpDiscoveryResponseCandidate(
|
||||
entryPoint({
|
||||
bridge: {
|
||||
bridge_status: "answer_draft_ready",
|
||||
user_facing_response_allowed: true,
|
||||
business_fact_answer_allowed: true,
|
||||
requires_user_clarification: false,
|
||||
answer_draft: {
|
||||
answer_mode: "confirmed_with_bounded_inference",
|
||||
headline: "По данным 1С найдены строки исходящих платежей/списаний; сумму можно называть только в рамках проверенного периода и найденных строк.",
|
||||
confirmed_lines: ["1C supplier-payout rows were found for counterparty SVK"],
|
||||
inference_lines: [
|
||||
"Requested period coverage was recovered through monthly 1C value-flow probes after the broad probe hit the row limit"
|
||||
],
|
||||
unknown_lines: [],
|
||||
limitation_lines: [],
|
||||
next_step_line: null
|
||||
}
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
expect(candidate.reply_text).toContain("Покрытие запрошенного периода восстановлено помесячными проверками 1С");
|
||||
expect(candidate.reply_text).not.toContain("broad probe hit the row limit");
|
||||
});
|
||||
|
||||
it("returns not applicable when discovery was skipped for an exact supported route", () => {
|
||||
const candidate = buildAssistantMcpDiscoveryResponseCandidate({
|
||||
schema_version: "assistant_mcp_discovery_runtime_entry_point_v1",
|
||||
|
||||
Reference in New Issue
Block a user