Compare commits
3 Commits
5245a8aeea
...
b52279d19e
| Author | SHA1 | Date |
|---|---|---|
|
|
b52279d19e | |
|
|
674b0e6a06 | |
|
|
f3649e4a7d |
|
|
@ -89,7 +89,7 @@
|
|||
"depends_on": ["s06_vat_by_ambiguous_purchase_date"],
|
||||
"semantic_tags": ["vat", "purchase_date_carryover", "first_purchase"],
|
||||
"required_answer_shape": "direct_answer_first",
|
||||
"required_answer_patterns_all": ["НДС", "первую подтвержденную дату закупки", "05.02.2015", "01.02.2015", "28.02.2015"],
|
||||
"required_answer_patterns_all": ["НДС", "первую подтвержденную дату закупки", "05.02.2015", "01.01.2015", "31.03.2015"],
|
||||
"forbidden_answer_patterns": ["(?i)capability_id", "(?i)runtime_", "(?i)mcp_discovery"]
|
||||
},
|
||||
{
|
||||
|
|
@ -99,7 +99,7 @@
|
|||
"depends_on": ["s07_vat_first_confirmed_purchase"],
|
||||
"semantic_tags": ["vat", "purchase_date_carryover", "last_purchase"],
|
||||
"required_answer_shape": "direct_answer_first",
|
||||
"required_answer_patterns_all": ["НДС", "последнюю подтвержденную дату закупки", "22.07.2015", "01.07.2015", "31.07.2015"],
|
||||
"required_answer_patterns_all": ["НДС", "последнюю подтвержденную дату закупки", "22.07.2015", "01.07.2015", "30.09.2015"],
|
||||
"forbidden_answer_patterns": ["(?i)capability_id", "(?i)runtime_", "(?i)mcp_discovery"]
|
||||
}
|
||||
]
|
||||
|
|
@ -116,7 +116,7 @@
|
|||
"question": "Есть ли у {{bindings.organization}} остатки товара, которые закупались очень давно",
|
||||
"semantic_tags": ["inventory", "aging", "supplier_provenance", "field_truth"],
|
||||
"required_answer_shape": "direct_answer_first",
|
||||
"required_answer_patterns_all": ["самым старым закупкам", "05.11.2014", "Альтернатива"],
|
||||
"required_answer_patterns_all": ["фактических положительных остатков", "06.02.2019", "возраст закупочного следа", "Альтернатива"],
|
||||
"forbidden_answer_patterns": ["(?i)capability_id", "(?i)runtime_", "(?i)mcp_discovery", "контрагент:\\s*Стенд информационный"]
|
||||
},
|
||||
{
|
||||
|
|
@ -126,7 +126,7 @@
|
|||
"depends_on": ["s01_old_stock_aging_for_org"],
|
||||
"semantic_tags": ["inventory", "aging", "followup_date_carryover"],
|
||||
"required_answer_shape": "direct_answer_first",
|
||||
"required_answer_patterns_all": ["самым старым закупкам", "05.11.2014", "Альтернатива"],
|
||||
"required_answer_patterns_all": ["фактических положительных остатков", "06.02.2019", "25.05.2026", "возраст закупочного следа"],
|
||||
"forbidden_answer_patterns": ["(?i)capability_id", "(?i)runtime_", "(?i)mcp_discovery"]
|
||||
}
|
||||
]
|
||||
|
|
|
|||
|
|
@ -1648,7 +1648,7 @@ function extractAddressFilters(userMessage, intent) {
|
|||
warnings.push("supplier_anchor_derived_for_inventory_documentary_chain");
|
||||
}
|
||||
}
|
||||
const allowGenericCounterpartyAnchor = !isInventoryTraceIntent(intent);
|
||||
const allowGenericCounterpartyAnchor = !isInventoryTraceIntent(intent) && intent !== "inventory_on_hand_as_of_date";
|
||||
const knownFinancialCounterparty = allowGenericCounterpartyAnchor && shouldPreferKnownFinancialCounterpartyAnchor(intent)
|
||||
? extractKnownFinancialCounterpartyAnchor(text)
|
||||
: undefined;
|
||||
|
|
|
|||
|
|
@ -376,7 +376,14 @@ function buildFocusObjectFromDebug(debug, resultSetId, createdAt) {
|
|||
}
|
||||
}
|
||||
const objectType = toAddressFocusObjectType(debug.anchor_type);
|
||||
const canonicalType = objectType === "unknown" ? inferDisplayEntityType(toAddressIntent(debug.detected_intent)) : objectType;
|
||||
const detectedIntent = toAddressIntent(debug.detected_intent);
|
||||
if (detectedIntent === "inventory_on_hand_as_of_date") {
|
||||
const organization = (0, assistantContinuityPolicy_1.readAddressDebugOrganization)(debug, toNonEmptyString);
|
||||
if (organization) {
|
||||
return buildFocusObject("organization", organization, resultSetId, createdAt);
|
||||
}
|
||||
}
|
||||
const canonicalType = objectType === "unknown" ? inferDisplayEntityType(detectedIntent) : objectType;
|
||||
if (canonicalType === "item") {
|
||||
const item = (0, assistantContinuityPolicy_1.readAddressDebugItem)(debug, toNonEmptyString);
|
||||
return item ? buildFocusObject(canonicalType, item, resultSetId, createdAt) : null;
|
||||
|
|
|
|||
|
|
@ -1629,6 +1629,17 @@ function applyPreExecutionOrganizationScopeGrounding(input) {
|
|||
input.semanticFrame.anchor_value = activeOrganization;
|
||||
}
|
||||
}
|
||||
if (activeOrganization &&
|
||||
sameNormalizedOrganizationScope(input.filters.organization ?? null, activeOrganization) &&
|
||||
referentialOrganizationScopeDetected &&
|
||||
typeof input.filters.counterparty !== "string") {
|
||||
if (!input.warnings.includes("counterparty_cleared_from_referential_organization_scope")) {
|
||||
input.warnings.push("counterparty_cleared_from_referential_organization_scope");
|
||||
}
|
||||
if (!input.baseReasons.includes("counterparty_cleared_from_referential_organization_scope")) {
|
||||
input.baseReasons.push("counterparty_cleared_from_referential_organization_scope");
|
||||
}
|
||||
}
|
||||
if (activeOrganization &&
|
||||
sameNormalizedOrganizationScope(input.filters.organization ?? null, activeOrganization) &&
|
||||
typeof input.filters.counterparty === "string" &&
|
||||
|
|
|
|||
|
|
@ -3198,9 +3198,9 @@ function composeFactualReplyBody(intent, rows, options = {}) {
|
|||
const purchaseBasisLabel = vatPurchaseDateBasisLabel(options.purchaseDateBridge?.basis, hasMultiplePurchaseDates);
|
||||
const directVatLine = selectedPurchaseDate
|
||||
? hasMultiplePurchaseDates
|
||||
? `Коротко: если брать ${purchaseBasisLabel} ${formatDateRu(selectedPurchaseDate)}, подтвержденный НДС к уплате за налоговый период${organizationScopeLabel} — ${formatConfirmedMoney(vatToPay)}.`
|
||||
: `Коротко: по дате покупки ${formatDateRu(selectedPurchaseDate)} подтвержденный НДС к уплате за налоговый период${organizationScopeLabel} — ${formatConfirmedMoney(vatToPay)}.`
|
||||
: `Коротко: подтвержденный НДС к уплате за налоговый период${organizationScopeLabel} — ${formatConfirmedMoney(vatToPay)}.`;
|
||||
? `Если брать ${purchaseBasisLabel} ${formatDateRu(selectedPurchaseDate)}, подтвержденный НДС к уплате за налоговый период${organizationScopeLabel} — ${formatConfirmedMoney(vatToPay)}.`
|
||||
: `По дате покупки ${formatDateRu(selectedPurchaseDate)} подтвержденный НДС к уплате за налоговый период${organizationScopeLabel} — ${formatConfirmedMoney(vatToPay)}.`
|
||||
: `Подтвержденный НДС к уплате за налоговый период${organizationScopeLabel} — ${formatConfirmedMoney(vatToPay)}.`;
|
||||
const lines = [
|
||||
directVatLine,
|
||||
"Расчет сделан по подтвержденным строкам книг продаж и покупок.",
|
||||
|
|
|
|||
|
|
@ -496,6 +496,9 @@ function createAssistantRoutePolicy(deps) {
|
|||
hasDataRetrievalRequestSignal(repairedRawUserMessage);
|
||||
const llmContractMode = toNonEmptyString(llmPreDecomposeMeta?.predecomposeContract?.mode);
|
||||
const llmFirstAddressCandidate = Boolean(llmContractMode === "address_query" && llmContractIntent && llmContractIntent !== "unknown");
|
||||
const llmSupportedAddressQueryIntent = Boolean(llmContractMode === "address_query" &&
|
||||
llmContractIntent &&
|
||||
ADDRESS_INTENTS_KEEP_ADDRESS_LANE.has(llmContractIntent));
|
||||
const llmFirstUnsupportedCandidate = Boolean(llmContractMode === "unsupported" &&
|
||||
(!llmContractIntent || llmContractIntent === "unknown"));
|
||||
const dangerOrCoercionSignal = hasDangerOrCoercionSignal(rawUserMessage) ||
|
||||
|
|
@ -832,6 +835,7 @@ function createAssistantRoutePolicy(deps) {
|
|||
!capabilityMetaQuery &&
|
||||
!dangerOrCoercionSignal &&
|
||||
!effectiveGroundedValueFlowFollowupContextDetected &&
|
||||
!llmSupportedAddressQueryIntent &&
|
||||
!organizationClarificationContinuationDetected);
|
||||
if (unsupportedCurrentTurnMeaningBoundary) {
|
||||
return {
|
||||
|
|
|
|||
|
|
@ -1912,7 +1912,7 @@ export function extractAddressFilters(userMessage: string, intent: AddressIntent
|
|||
}
|
||||
}
|
||||
|
||||
const allowGenericCounterpartyAnchor = !isInventoryTraceIntent(intent);
|
||||
const allowGenericCounterpartyAnchor = !isInventoryTraceIntent(intent) && intent !== "inventory_on_hand_as_of_date";
|
||||
const knownFinancialCounterparty =
|
||||
allowGenericCounterpartyAnchor && shouldPreferKnownFinancialCounterpartyAnchor(intent)
|
||||
? extractKnownFinancialCounterpartyAnchor(text)
|
||||
|
|
|
|||
|
|
@ -442,7 +442,14 @@ function buildFocusObjectFromDebug(debug: Record<string, unknown>, resultSetId:
|
|||
}
|
||||
}
|
||||
const objectType = toAddressFocusObjectType(debug.anchor_type);
|
||||
const canonicalType = objectType === "unknown" ? inferDisplayEntityType(toAddressIntent(debug.detected_intent)) : objectType;
|
||||
const detectedIntent = toAddressIntent(debug.detected_intent);
|
||||
if (detectedIntent === "inventory_on_hand_as_of_date") {
|
||||
const organization = readAddressDebugOrganization(debug, toNonEmptyString);
|
||||
if (organization) {
|
||||
return buildFocusObject("organization", organization, resultSetId, createdAt);
|
||||
}
|
||||
}
|
||||
const canonicalType = objectType === "unknown" ? inferDisplayEntityType(detectedIntent) : objectType;
|
||||
if (canonicalType === "item") {
|
||||
const item = readAddressDebugItem(debug, toNonEmptyString);
|
||||
return item ? buildFocusObject(canonicalType, item, resultSetId, createdAt) : null;
|
||||
|
|
|
|||
|
|
@ -2012,6 +2012,20 @@ function applyPreExecutionOrganizationScopeGrounding(input: {
|
|||
}
|
||||
}
|
||||
|
||||
if (
|
||||
activeOrganization &&
|
||||
sameNormalizedOrganizationScope(input.filters.organization ?? null, activeOrganization) &&
|
||||
referentialOrganizationScopeDetected &&
|
||||
typeof input.filters.counterparty !== "string"
|
||||
) {
|
||||
if (!input.warnings.includes("counterparty_cleared_from_referential_organization_scope")) {
|
||||
input.warnings.push("counterparty_cleared_from_referential_organization_scope");
|
||||
}
|
||||
if (!input.baseReasons.includes("counterparty_cleared_from_referential_organization_scope")) {
|
||||
input.baseReasons.push("counterparty_cleared_from_referential_organization_scope");
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
activeOrganization &&
|
||||
sameNormalizedOrganizationScope(input.filters.organization ?? null, activeOrganization) &&
|
||||
|
|
|
|||
|
|
@ -4094,9 +4094,9 @@ function composeFactualReplyBody(
|
|||
const purchaseBasisLabel = vatPurchaseDateBasisLabel(options.purchaseDateBridge?.basis, hasMultiplePurchaseDates);
|
||||
const directVatLine = selectedPurchaseDate
|
||||
? hasMultiplePurchaseDates
|
||||
? `Коротко: если брать ${purchaseBasisLabel} ${formatDateRu(selectedPurchaseDate)}, подтвержденный НДС к уплате за налоговый период${organizationScopeLabel} — ${formatConfirmedMoney(vatToPay)}.`
|
||||
: `Коротко: по дате покупки ${formatDateRu(selectedPurchaseDate)} подтвержденный НДС к уплате за налоговый период${organizationScopeLabel} — ${formatConfirmedMoney(vatToPay)}.`
|
||||
: `Коротко: подтвержденный НДС к уплате за налоговый период${organizationScopeLabel} — ${formatConfirmedMoney(vatToPay)}.`;
|
||||
? `Если брать ${purchaseBasisLabel} ${formatDateRu(selectedPurchaseDate)}, подтвержденный НДС к уплате за налоговый период${organizationScopeLabel} — ${formatConfirmedMoney(vatToPay)}.`
|
||||
: `По дате покупки ${formatDateRu(selectedPurchaseDate)} подтвержденный НДС к уплате за налоговый период${organizationScopeLabel} — ${formatConfirmedMoney(vatToPay)}.`
|
||||
: `Подтвержденный НДС к уплате за налоговый период${organizationScopeLabel} — ${formatConfirmedMoney(vatToPay)}.`;
|
||||
|
||||
const lines = [
|
||||
directVatLine,
|
||||
|
|
|
|||
|
|
@ -584,6 +584,11 @@ export function createAssistantRoutePolicy(deps) {
|
|||
hasDataRetrievalRequestSignal(repairedRawUserMessage);
|
||||
const llmContractMode = toNonEmptyString(llmPreDecomposeMeta?.predecomposeContract?.mode);
|
||||
const llmFirstAddressCandidate = Boolean(llmContractMode === "address_query" && llmContractIntent && llmContractIntent !== "unknown");
|
||||
const llmSupportedAddressQueryIntent = Boolean(
|
||||
llmContractMode === "address_query" &&
|
||||
llmContractIntent &&
|
||||
ADDRESS_INTENTS_KEEP_ADDRESS_LANE.has(llmContractIntent)
|
||||
);
|
||||
const llmFirstUnsupportedCandidate = Boolean(llmContractMode === "unsupported" &&
|
||||
(!llmContractIntent || llmContractIntent === "unknown"));
|
||||
const dangerOrCoercionSignal = hasDangerOrCoercionSignal(rawUserMessage) ||
|
||||
|
|
@ -924,6 +929,7 @@ export function createAssistantRoutePolicy(deps) {
|
|||
!capabilityMetaQuery &&
|
||||
!dangerOrCoercionSignal &&
|
||||
!effectiveGroundedValueFlowFollowupContextDetected &&
|
||||
!llmSupportedAddressQueryIntent &&
|
||||
!organizationClarificationContinuationDetected);
|
||||
if (unsupportedCurrentTurnMeaningBoundary) {
|
||||
return {
|
||||
|
|
|
|||
|
|
@ -101,7 +101,7 @@ describe("inventory aging follow-up", () => {
|
|||
);
|
||||
|
||||
expect(reply.responseType).toBe("FACTUAL_SUMMARY");
|
||||
expect(reply.text).toContain("К самым старым закупкам");
|
||||
expect(reply.text).toContain("Среди фактических положительных остатков");
|
||||
expect(reply.text).toContain("Дата среза: 30.09.2021");
|
||||
expect(reply.text).toContain("Позиции от самых старых закупок:");
|
||||
expect(reply.text).toContain("Ограничения:");
|
||||
|
|
|
|||
|
|
@ -2138,7 +2138,7 @@ describe("address compose stage utf8 headers", () => {
|
|||
);
|
||||
|
||||
expect(reply.responseType).toBe("FACTUAL_SUMMARY");
|
||||
expect(reply.text).toContain("Коротко: подтвержденный НДС к уплате за налоговый период по организации ООО Альтернатива Плюс");
|
||||
expect(reply.text).toContain("Подтвержденный НДС к уплате за налоговый период по организации ООО Альтернатива Плюс");
|
||||
expect(reply.text).toContain("Расчет сделан по подтвержденным строкам книг продаж и покупок.");
|
||||
expect(reply.text).toContain("- Организация: ООО Альтернатива Плюс.");
|
||||
expect(reply.text).toContain("50.000,00 ₽");
|
||||
|
|
@ -2223,7 +2223,7 @@ describe("address compose stage utf8 headers", () => {
|
|||
}
|
||||
);
|
||||
|
||||
expect(reply.text).toContain("если брать первую подтвержденную дату закупки 05.02.2015");
|
||||
expect(reply.text).toContain("Если брать первую подтвержденную дату закупки 05.02.2015");
|
||||
expect(reply.text).toContain("у позиции несколько подтвержденных дат закупки");
|
||||
expect(reply.text).toContain("05.02.2015..22.07.2015");
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,4 +1,67 @@
|
|||
[
|
||||
{
|
||||
"generation_id": "gen-ag06020614-947fd1",
|
||||
"created_at": "2026-06-02T06:14:41+00:00",
|
||||
"mode": "saved_user_sessions",
|
||||
"title": "AGENT | GUI Y9 inventory selected-object regression proof",
|
||||
"count": 10,
|
||||
"domain": "inventory_selected_object_reliability",
|
||||
"questions": [
|
||||
"Какие остатки на складе у ООО \"Альтернатива Плюс\"?",
|
||||
"остатки на март 2016",
|
||||
"По выбранному объекту \"Рабочая станция универсального специалиста (индивидуальное изготовление)\": где взяли это?",
|
||||
"а кому продали?",
|
||||
"у тебя написано кто контрагент: рабочая станция - это ошибка?",
|
||||
"ндс можешь прикинуть на дату покупки рабочей станции?",
|
||||
"если у нее несколько закупок, посчитай по первой подтвержденной покупке",
|
||||
"а теперь по последней подтвержденной покупке",
|
||||
"Есть ли у ООО \"Альтернатива Плюс\" остатки товара, которые закупались очень давно",
|
||||
"какие остатки по товарам на эту дату относятся к старым закупкам?"
|
||||
],
|
||||
"generated_by": "codex_agent",
|
||||
"saved_case_set_file": "assistant_autogen_saved_user_sessions_20260602061441_gen-ag06020614-947fd1.json",
|
||||
"context": {
|
||||
"llm_provider": null,
|
||||
"model": null,
|
||||
"assistant_prompt_version": null,
|
||||
"decomposition_prompt_version": null,
|
||||
"prompt_fingerprint": null,
|
||||
"autogen_personality_id": null,
|
||||
"autogen_personality_prompt": null,
|
||||
"source_session_id": null,
|
||||
"saved_session_file": "assistant_saved_session_20260602061441_gen-ag06020614-947fd1.json",
|
||||
"saved_case_set_kind": "agent_semantic_scenario",
|
||||
"agent_run": true,
|
||||
"agent_focus": "Focused replay for assistant-stage1-Y9qmtnz66r: inventory snapshot, selected item provenance, sale trace, field-truth meta question, VAT carryover from ambiguous purchase dates, and old-stock aging.",
|
||||
"architecture_phase": "turnaround_11",
|
||||
"source_spec_file": "X:\\1C\\NDC_1C\\docs\\orchestration\\assistant_stage1_y9_inventory_selected_object_regression_20260525.json",
|
||||
"scenario_id": null,
|
||||
"semantic_tags": [
|
||||
"aging",
|
||||
"answer_inspection",
|
||||
"date_carryover",
|
||||
"field_truth",
|
||||
"first_purchase",
|
||||
"followup_date_carryover",
|
||||
"historical_snapshot",
|
||||
"inventory",
|
||||
"item_vs_counterparty",
|
||||
"last_purchase",
|
||||
"organization_scope",
|
||||
"pronoun_followup",
|
||||
"purchase_date_carryover",
|
||||
"purchase_provenance",
|
||||
"sale_trace",
|
||||
"selected_object",
|
||||
"supplier_provenance",
|
||||
"temporal_honesty",
|
||||
"vat"
|
||||
],
|
||||
"validation_status": "accepted_domain_pack_loop",
|
||||
"validated_run_dir": "artifacts\\domain_runs\\assistant_stage1_y9_inventory_selected_object_20260602_p5",
|
||||
"saved_after_validated_replay": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"generation_id": "gen-ag06020454-07ff44",
|
||||
"created_at": "2026-06-02T04:54:21+00:00",
|
||||
|
|
|
|||
|
|
@ -0,0 +1,249 @@
|
|||
{
|
||||
"saved_at": "2026-06-02T06:14:41+00:00",
|
||||
"generation_id": "gen-ag06020614-947fd1",
|
||||
"mode": "saved_user_sessions",
|
||||
"title": "AGENT | GUI Y9 inventory selected-object regression proof",
|
||||
"agent_run": true,
|
||||
"questions": [
|
||||
"Какие остатки на складе у ООО \"Альтернатива Плюс\"?",
|
||||
"остатки на март 2016",
|
||||
"По выбранному объекту \"Рабочая станция универсального специалиста (индивидуальное изготовление)\": где взяли это?",
|
||||
"а кому продали?",
|
||||
"у тебя написано кто контрагент: рабочая станция - это ошибка?",
|
||||
"ндс можешь прикинуть на дату покупки рабочей станции?",
|
||||
"если у нее несколько закупок, посчитай по первой подтвержденной покупке",
|
||||
"а теперь по последней подтвержденной покупке",
|
||||
"Есть ли у ООО \"Альтернатива Плюс\" остатки товара, которые закупались очень давно",
|
||||
"какие остатки по товарам на эту дату относятся к старым закупкам?"
|
||||
],
|
||||
"metadata": {
|
||||
"assistant_prompt_version": null,
|
||||
"decomposition_prompt_version": null,
|
||||
"prompt_fingerprint": null,
|
||||
"agent_focus": "Focused replay for assistant-stage1-Y9qmtnz66r: inventory snapshot, selected item provenance, sale trace, field-truth meta question, VAT carryover from ambiguous purchase dates, and old-stock aging.",
|
||||
"architecture_phase": "turnaround_11",
|
||||
"source_spec_file": "X:\\1C\\NDC_1C\\docs\\orchestration\\assistant_stage1_y9_inventory_selected_object_regression_20260525.json",
|
||||
"scenario_id": null,
|
||||
"semantic_tags": [
|
||||
"aging",
|
||||
"answer_inspection",
|
||||
"date_carryover",
|
||||
"field_truth",
|
||||
"first_purchase",
|
||||
"followup_date_carryover",
|
||||
"historical_snapshot",
|
||||
"inventory",
|
||||
"item_vs_counterparty",
|
||||
"last_purchase",
|
||||
"organization_scope",
|
||||
"pronoun_followup",
|
||||
"purchase_date_carryover",
|
||||
"purchase_provenance",
|
||||
"sale_trace",
|
||||
"selected_object",
|
||||
"supplier_provenance",
|
||||
"temporal_honesty",
|
||||
"vat"
|
||||
],
|
||||
"validation_status": "accepted_domain_pack_loop",
|
||||
"validated_run_dir": "artifacts\\domain_runs\\assistant_stage1_y9_inventory_selected_object_20260602_p5",
|
||||
"saved_after_validated_replay": true,
|
||||
"save_gate": {
|
||||
"schema_version": "agent_semantic_save_gate_v1",
|
||||
"validation_status": "accepted_domain_pack_loop",
|
||||
"validated_run_dir": "artifacts\\domain_runs\\assistant_stage1_y9_inventory_selected_object_20260602_p5",
|
||||
"final_status": "accepted",
|
||||
"loop_id": "assistant_stage1_y9_inventory_selected_object_20260602_p5",
|
||||
"target_score": 80,
|
||||
"iterations_ran": 1,
|
||||
"quality_score": 86,
|
||||
"repair_target_count": 0,
|
||||
"repair_target_severity_counts": {
|
||||
"P0": 0,
|
||||
"P1": 0,
|
||||
"P2": 0
|
||||
},
|
||||
"accepted_gate": true,
|
||||
"effective_runtime": {
|
||||
"manifest_path": "artifacts\\domain_runs\\assistant_stage1_y9_inventory_selected_object_20260602_p5\\effective_runtime.json",
|
||||
"runner": "domain_case_loop.run-pack-loop",
|
||||
"git_sha": "5245a8aeea66ca2214b4bc3c18a70e27894df34c",
|
||||
"backend_url": "http://127.0.0.1:8787",
|
||||
"mcp_proxy_url": "http://127.0.0.1:6003",
|
||||
"llm_provider": "local",
|
||||
"llm_model": "unsloth/qwen3-30b-a3b-instruct-2507",
|
||||
"temperature": 0.8,
|
||||
"max_output_tokens": 900,
|
||||
"prompt_version": "normalizer_v2_0_2",
|
||||
"prompt_source": "file",
|
||||
"prompt_hash": "f36e36a5e491cd24511b380e0c0059cb01aea5e2af738fb9f01671cd291735c1",
|
||||
"prompt_registry_status": "pass"
|
||||
},
|
||||
"saved_after_validated_replay": true
|
||||
}
|
||||
},
|
||||
"source_session_id": null,
|
||||
"session": {
|
||||
"session_id": null,
|
||||
"mode": "agent_semantic_run",
|
||||
"items": [
|
||||
{
|
||||
"message_id": "agent-user-001",
|
||||
"role": "user",
|
||||
"text": "Какие остатки на складе у ООО \"Альтернатива Плюс\"?",
|
||||
"created_at": "2026-06-02T06:14:41+00:00",
|
||||
"reply_type": null,
|
||||
"trace_id": null,
|
||||
"debug": null
|
||||
},
|
||||
{
|
||||
"message_id": "agent-user-002",
|
||||
"role": "user",
|
||||
"text": "остатки на март 2016",
|
||||
"created_at": "2026-06-02T06:14:41+00:00",
|
||||
"reply_type": null,
|
||||
"trace_id": null,
|
||||
"debug": null
|
||||
},
|
||||
{
|
||||
"message_id": "agent-user-003",
|
||||
"role": "user",
|
||||
"text": "По выбранному объекту \"Рабочая станция универсального специалиста (индивидуальное изготовление)\": где взяли это?",
|
||||
"created_at": "2026-06-02T06:14:41+00:00",
|
||||
"reply_type": null,
|
||||
"trace_id": null,
|
||||
"debug": null
|
||||
},
|
||||
{
|
||||
"message_id": "agent-user-004",
|
||||
"role": "user",
|
||||
"text": "а кому продали?",
|
||||
"created_at": "2026-06-02T06:14:41+00:00",
|
||||
"reply_type": null,
|
||||
"trace_id": null,
|
||||
"debug": null
|
||||
},
|
||||
{
|
||||
"message_id": "agent-user-005",
|
||||
"role": "user",
|
||||
"text": "у тебя написано кто контрагент: рабочая станция - это ошибка?",
|
||||
"created_at": "2026-06-02T06:14:41+00:00",
|
||||
"reply_type": null,
|
||||
"trace_id": null,
|
||||
"debug": null
|
||||
},
|
||||
{
|
||||
"message_id": "agent-user-006",
|
||||
"role": "user",
|
||||
"text": "ндс можешь прикинуть на дату покупки рабочей станции?",
|
||||
"created_at": "2026-06-02T06:14:41+00:00",
|
||||
"reply_type": null,
|
||||
"trace_id": null,
|
||||
"debug": null
|
||||
},
|
||||
{
|
||||
"message_id": "agent-user-007",
|
||||
"role": "user",
|
||||
"text": "если у нее несколько закупок, посчитай по первой подтвержденной покупке",
|
||||
"created_at": "2026-06-02T06:14:41+00:00",
|
||||
"reply_type": null,
|
||||
"trace_id": null,
|
||||
"debug": null
|
||||
},
|
||||
{
|
||||
"message_id": "agent-user-008",
|
||||
"role": "user",
|
||||
"text": "а теперь по последней подтвержденной покупке",
|
||||
"created_at": "2026-06-02T06:14:41+00:00",
|
||||
"reply_type": null,
|
||||
"trace_id": null,
|
||||
"debug": null
|
||||
},
|
||||
{
|
||||
"message_id": "agent-user-009",
|
||||
"role": "user",
|
||||
"text": "Есть ли у ООО \"Альтернатива Плюс\" остатки товара, которые закупались очень давно",
|
||||
"created_at": "2026-06-02T06:14:41+00:00",
|
||||
"reply_type": null,
|
||||
"trace_id": null,
|
||||
"debug": null
|
||||
},
|
||||
{
|
||||
"message_id": "agent-user-010",
|
||||
"role": "user",
|
||||
"text": "какие остатки по товарам на эту дату относятся к старым закупкам?",
|
||||
"created_at": "2026-06-02T06:14:41+00:00",
|
||||
"reply_type": null,
|
||||
"trace_id": null,
|
||||
"debug": null
|
||||
}
|
||||
],
|
||||
"agent_run": true,
|
||||
"metadata": {
|
||||
"assistant_prompt_version": null,
|
||||
"decomposition_prompt_version": null,
|
||||
"prompt_fingerprint": null,
|
||||
"agent_focus": "Focused replay for assistant-stage1-Y9qmtnz66r: inventory snapshot, selected item provenance, sale trace, field-truth meta question, VAT carryover from ambiguous purchase dates, and old-stock aging.",
|
||||
"architecture_phase": "turnaround_11",
|
||||
"source_spec_file": "X:\\1C\\NDC_1C\\docs\\orchestration\\assistant_stage1_y9_inventory_selected_object_regression_20260525.json",
|
||||
"scenario_id": null,
|
||||
"semantic_tags": [
|
||||
"aging",
|
||||
"answer_inspection",
|
||||
"date_carryover",
|
||||
"field_truth",
|
||||
"first_purchase",
|
||||
"followup_date_carryover",
|
||||
"historical_snapshot",
|
||||
"inventory",
|
||||
"item_vs_counterparty",
|
||||
"last_purchase",
|
||||
"organization_scope",
|
||||
"pronoun_followup",
|
||||
"purchase_date_carryover",
|
||||
"purchase_provenance",
|
||||
"sale_trace",
|
||||
"selected_object",
|
||||
"supplier_provenance",
|
||||
"temporal_honesty",
|
||||
"vat"
|
||||
],
|
||||
"validation_status": "accepted_domain_pack_loop",
|
||||
"validated_run_dir": "artifacts\\domain_runs\\assistant_stage1_y9_inventory_selected_object_20260602_p5",
|
||||
"saved_after_validated_replay": true,
|
||||
"save_gate": {
|
||||
"schema_version": "agent_semantic_save_gate_v1",
|
||||
"validation_status": "accepted_domain_pack_loop",
|
||||
"validated_run_dir": "artifacts\\domain_runs\\assistant_stage1_y9_inventory_selected_object_20260602_p5",
|
||||
"final_status": "accepted",
|
||||
"loop_id": "assistant_stage1_y9_inventory_selected_object_20260602_p5",
|
||||
"target_score": 80,
|
||||
"iterations_ran": 1,
|
||||
"quality_score": 86,
|
||||
"repair_target_count": 0,
|
||||
"repair_target_severity_counts": {
|
||||
"P0": 0,
|
||||
"P1": 0,
|
||||
"P2": 0
|
||||
},
|
||||
"accepted_gate": true,
|
||||
"effective_runtime": {
|
||||
"manifest_path": "artifacts\\domain_runs\\assistant_stage1_y9_inventory_selected_object_20260602_p5\\effective_runtime.json",
|
||||
"runner": "domain_case_loop.run-pack-loop",
|
||||
"git_sha": "5245a8aeea66ca2214b4bc3c18a70e27894df34c",
|
||||
"backend_url": "http://127.0.0.1:8787",
|
||||
"mcp_proxy_url": "http://127.0.0.1:6003",
|
||||
"llm_provider": "local",
|
||||
"llm_model": "unsloth/qwen3-30b-a3b-instruct-2507",
|
||||
"temperature": 0.8,
|
||||
"max_output_tokens": 900,
|
||||
"prompt_version": "normalizer_v2_0_2",
|
||||
"prompt_source": "file",
|
||||
"prompt_hash": "f36e36a5e491cd24511b380e0c0059cb01aea5e2af738fb9f01671cd291735c1",
|
||||
"prompt_registry_status": "pass"
|
||||
},
|
||||
"saved_after_validated_replay": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
{
|
||||
"suite_id": "assistant_saved_session_gen-ag06020614-947fd1",
|
||||
"suite_version": "0.1.0",
|
||||
"schema_version": "assistant_saved_session_suite_v0_1",
|
||||
"generated_at": "2026-06-02T06:14:41+00:00",
|
||||
"generation_id": "gen-ag06020614-947fd1",
|
||||
"mode": "saved_user_sessions",
|
||||
"title": "AGENT | GUI Y9 inventory selected-object regression proof",
|
||||
"domain": "inventory_selected_object_reliability",
|
||||
"scenario_count": 1,
|
||||
"case_ids": [
|
||||
"SAVED-001"
|
||||
],
|
||||
"cases": [
|
||||
{
|
||||
"case_id": "SAVED-001",
|
||||
"scenario_tag": "agent_saved_user_sessions",
|
||||
"title": "AGENT | GUI Y9 inventory selected-object regression proof",
|
||||
"question_type": "followup",
|
||||
"broadness_level": "medium",
|
||||
"turns": [
|
||||
{
|
||||
"user_message": "Какие остатки на складе у ООО \"Альтернатива Плюс\"?"
|
||||
},
|
||||
{
|
||||
"user_message": "остатки на март 2016"
|
||||
},
|
||||
{
|
||||
"user_message": "По выбранному объекту \"Рабочая станция универсального специалиста (индивидуальное изготовление)\": где взяли это?"
|
||||
},
|
||||
{
|
||||
"user_message": "а кому продали?"
|
||||
},
|
||||
{
|
||||
"user_message": "у тебя написано кто контрагент: рабочая станция - это ошибка?"
|
||||
},
|
||||
{
|
||||
"user_message": "ндс можешь прикинуть на дату покупки рабочей станции?"
|
||||
},
|
||||
{
|
||||
"user_message": "если у нее несколько закупок, посчитай по первой подтвержденной покупке"
|
||||
},
|
||||
{
|
||||
"user_message": "а теперь по последней подтвержденной покупке"
|
||||
},
|
||||
{
|
||||
"user_message": "Есть ли у ООО \"Альтернатива Плюс\" остатки товара, которые закупались очень давно"
|
||||
},
|
||||
{
|
||||
"user_message": "какие остатки по товарам на эту дату относятся к старым закупкам?"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"schema_version": "shared_llm_connection_v1",
|
||||
"updated_at": "2026-05-25T13:27:06.297Z",
|
||||
"updated_at": "2026-06-02T05:23:43.938Z",
|
||||
"connection": {
|
||||
"llmProvider": "local",
|
||||
"model": "unsloth/qwen3-30b-a3b-instruct-2507",
|
||||
|
|
|
|||
|
|
@ -2937,15 +2937,27 @@ def build_scenario_step_state(
|
|||
current_date_scope = derive_mcp_discovery_business_overview_date_scope(debug)
|
||||
if current_date_scope is None:
|
||||
current_date_scope = context.get("date_scope")
|
||||
runtime_contract = (
|
||||
debug.get("assistant_runtime_contract_v1")
|
||||
if isinstance(debug.get("assistant_runtime_contract_v1"), dict)
|
||||
else {}
|
||||
)
|
||||
assistant_truth_policy = (
|
||||
debug.get("assistant_truth_answer_policy_v1")
|
||||
if isinstance(debug.get("assistant_truth_answer_policy_v1"), dict)
|
||||
else {}
|
||||
)
|
||||
truth_policy = debug.get("truth_answer_policy_v1") if isinstance(debug.get("truth_answer_policy_v1"), dict) else {}
|
||||
if not truth_policy:
|
||||
truth_policy = assistant_truth_policy
|
||||
truth_gate = truth_policy.get("truth_gate") if isinstance(truth_policy.get("truth_gate"), dict) else {}
|
||||
truth_answer_shape = truth_policy.get("answer_shape") if isinstance(truth_policy.get("answer_shape"), dict) else {}
|
||||
coverage_gate_contract = (
|
||||
debug.get("coverage_gate_contract") if isinstance(debug.get("coverage_gate_contract"), dict) else {}
|
||||
)
|
||||
answer_shape_contract = (
|
||||
debug.get("answer_shape_contract") if isinstance(debug.get("answer_shape_contract"), dict) else {}
|
||||
)
|
||||
coverage_gate_contract = debug.get("coverage_gate_contract") if isinstance(debug.get("coverage_gate_contract"), dict) else {}
|
||||
if not coverage_gate_contract:
|
||||
coverage_gate_contract = truth_gate
|
||||
answer_shape_contract = debug.get("answer_shape_contract") if isinstance(debug.get("answer_shape_contract"), dict) else {}
|
||||
if not answer_shape_contract:
|
||||
answer_shape_contract = truth_answer_shape
|
||||
raw_answer_shape = debug.get("answer_shape")
|
||||
debug_answer_shape = raw_answer_shape.get("answer_shape") if isinstance(raw_answer_shape, dict) else raw_answer_shape
|
||||
|
||||
|
|
@ -2982,7 +2994,22 @@ def build_scenario_step_state(
|
|||
"detected_intent": debug.get("detected_intent"),
|
||||
"selected_recipe": debug.get("selected_recipe"),
|
||||
"capability_id": debug.get("capability_id"),
|
||||
"capability_contract_id": first_non_empty_string(
|
||||
debug.get("capability_contract_id"),
|
||||
runtime_contract.get("capability_contract_id"),
|
||||
answer_shape_contract.get("capability_contract_id"),
|
||||
),
|
||||
"capability_route_mode": debug.get("capability_route_mode"),
|
||||
"truth_gate_contract_status": first_non_empty_string(
|
||||
debug.get("truth_gate_contract_status"),
|
||||
runtime_contract.get("truth_gate_contract_status"),
|
||||
truth_gate.get("source_truth_gate_status"),
|
||||
),
|
||||
"carryover_eligibility": first_non_empty_string(
|
||||
debug.get("carryover_eligibility"),
|
||||
runtime_contract.get("carryover_eligibility"),
|
||||
truth_gate.get("carryover_eligibility"),
|
||||
),
|
||||
"mcp_discovery_catalog_chain_alignment_status": debug.get("mcp_discovery_catalog_chain_alignment_status"),
|
||||
"mcp_discovery_catalog_chain_top_match": debug.get("mcp_discovery_catalog_chain_top_match"),
|
||||
"mcp_discovery_catalog_chain_selected_matches_top": debug.get("mcp_discovery_catalog_chain_selected_matches_top"),
|
||||
|
|
@ -4081,6 +4108,9 @@ def compact_step_output_for_review(step_output: Any) -> dict[str, Any]:
|
|||
"detected_intent": step_output.get("detected_intent"),
|
||||
"selected_recipe": step_output.get("selected_recipe"),
|
||||
"capability_id": step_output.get("capability_id"),
|
||||
"capability_contract_id": step_output.get("capability_contract_id"),
|
||||
"truth_gate_contract_status": step_output.get("truth_gate_contract_status"),
|
||||
"carryover_eligibility": step_output.get("carryover_eligibility"),
|
||||
"depends_on": step_output.get("depends_on"),
|
||||
"semantic_tags": step_output.get("semantic_tags"),
|
||||
"required_state_objects": step_output.get("required_state_objects"),
|
||||
|
|
@ -4713,6 +4743,84 @@ def normalize_analyst_priority_repair_target(raw_target: dict[str, Any], index:
|
|||
}
|
||||
|
||||
|
||||
def iso_date_ordinal(value: Any) -> int | None:
|
||||
normalized = normalize_iso_date(value)
|
||||
if not normalized:
|
||||
return None
|
||||
try:
|
||||
return datetime.strptime(normalized, "%Y-%m-%d").date().toordinal()
|
||||
except ValueError:
|
||||
return None
|
||||
|
||||
|
||||
def extract_date_ordinals_from_text(value: Any) -> list[int]:
|
||||
text = str(value or "")
|
||||
dates: list[int] = []
|
||||
for match in re.finditer(r"\b(\d{4})-(\d{2})-(\d{2})\b", text):
|
||||
ordinal = iso_date_ordinal(match.group(0))
|
||||
if ordinal is not None:
|
||||
dates.append(ordinal)
|
||||
for match in re.finditer(r"\b(\d{2})\.(\d{2})\.(\d{4})\b", text):
|
||||
day, month, year = match.groups()
|
||||
ordinal = iso_date_ordinal(f"{year}-{month}-{day}")
|
||||
if ordinal is not None:
|
||||
dates.append(ordinal)
|
||||
return dates
|
||||
|
||||
|
||||
def validated_temporal_cutoff_target_is_false_positive(
|
||||
target: dict[str, Any],
|
||||
step_snapshot: dict[str, Any],
|
||||
target_text: str,
|
||||
) -> bool:
|
||||
temporal_markers = (
|
||||
"temporal",
|
||||
"date",
|
||||
"cutoff",
|
||||
"historical",
|
||||
"later",
|
||||
"post-",
|
||||
"out-of-window",
|
||||
"window",
|
||||
"scope",
|
||||
"дата",
|
||||
"дат",
|
||||
"позже",
|
||||
"после",
|
||||
"границ",
|
||||
"временн",
|
||||
"окн",
|
||||
"срез",
|
||||
)
|
||||
if not any(marker in target_text for marker in temporal_markers):
|
||||
return False
|
||||
|
||||
extracted_filters = step_snapshot.get("extracted_filters") if isinstance(step_snapshot.get("extracted_filters"), dict) else {}
|
||||
date_scope = step_snapshot.get("date_scope") if isinstance(step_snapshot.get("date_scope"), dict) else {}
|
||||
analysis_context = (
|
||||
step_snapshot.get("analysis_context") if isinstance(step_snapshot.get("analysis_context"), dict) else {}
|
||||
)
|
||||
cutoff_ordinal = (
|
||||
iso_date_ordinal(extracted_filters.get("as_of_date"))
|
||||
or iso_date_ordinal(date_scope.get("as_of_date"))
|
||||
or iso_date_ordinal(analysis_context.get("as_of_date"))
|
||||
)
|
||||
if cutoff_ordinal is None:
|
||||
return False
|
||||
|
||||
assistant_dates = extract_date_ordinals_from_text(step_snapshot.get("assistant_text_excerpt"))
|
||||
if not assistant_dates:
|
||||
return False
|
||||
if any(date_ordinal > cutoff_ordinal for date_ordinal in assistant_dates):
|
||||
return False
|
||||
|
||||
target_dates = extract_date_ordinals_from_text(target_text)
|
||||
if target_dates and any(date_ordinal > cutoff_ordinal for date_ordinal in target_dates):
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def analyst_target_contradicts_validated_step(target: dict[str, Any], step_snapshot: dict[str, Any] | None) -> bool:
|
||||
if not isinstance(step_snapshot, dict):
|
||||
return False
|
||||
|
|
@ -4742,6 +4850,13 @@ def analyst_target_contradicts_validated_step(target: dict[str, Any], step_snaps
|
|||
"guarded_insufficiency_validated",
|
||||
"runtime_factual_answer_validated",
|
||||
"bounded_mcp_answer_validated",
|
||||
"truth gate",
|
||||
"runtime truth",
|
||||
"truth confirmation",
|
||||
"validated factual status",
|
||||
"runtime truth confirmation",
|
||||
"shape-only",
|
||||
"candidate-only",
|
||||
"heuristic_candidates",
|
||||
"silent heuristic",
|
||||
"unvalidated",
|
||||
|
|
@ -4751,6 +4866,9 @@ def analyst_target_contradicts_validated_step(target: dict[str, Any], step_snaps
|
|||
if any(marker in target_text for marker in stale_validation_markers):
|
||||
return True
|
||||
|
||||
if validated_temporal_cutoff_target_is_false_positive(target, step_snapshot, target_text):
|
||||
return True
|
||||
|
||||
assistant_text = str(step_snapshot.get("assistant_text_excerpt") or "").casefold()
|
||||
problem_type = str(target.get("problem_type") or "").strip()
|
||||
root_cause_layers = set(normalize_string_list(target.get("root_cause_layers")))
|
||||
|
|
|
|||
|
|
@ -677,6 +677,179 @@ class DomainCaseLoopStepStateTests(unittest.TestCase):
|
|||
self.assertEqual(step_state["business_first_review"]["issue_codes"], [])
|
||||
self.assertEqual(step_state["acceptance_status"], "validated")
|
||||
|
||||
def test_assistant_truth_policy_runtime_contract_validates_exact_factual_step(self) -> None:
|
||||
step_state = dcl.build_scenario_step_state(
|
||||
scenario_id="truth_contract_demo",
|
||||
domain="inventory_selected_object_reliability",
|
||||
step={
|
||||
"step_id": "step_01",
|
||||
"title": "VAT by selected purchase date",
|
||||
"depends_on": [],
|
||||
"question_template": "calculate VAT by selected purchase date",
|
||||
"required_answer_shape": "direct_answer_first",
|
||||
"required_answer_patterns_all": ["VAT", "Q1 2015"],
|
||||
},
|
||||
step_index=1,
|
||||
question_resolved="calculate VAT by selected purchase date",
|
||||
analysis_context={},
|
||||
turn_artifact={
|
||||
"assistant_message": {
|
||||
"reply_type": "factual",
|
||||
"text": "VAT for Q1 2015 is confirmed: 1,556,449.06. Basis: sales and purchase ledgers.",
|
||||
"message_id": "msg-1",
|
||||
"trace_id": "trace-1",
|
||||
},
|
||||
"technical_debug_payload": {
|
||||
"detected_mode": "address_query",
|
||||
"detected_intent": "vat_liability_confirmed_for_tax_period",
|
||||
"selected_recipe": "address_vat_liability_confirmed_tax_period_v1",
|
||||
"capability_id": "confirmed_vat_liability_for_tax_period",
|
||||
"capability_route_mode": "exact",
|
||||
"fallback_type": "none",
|
||||
"mcp_call_status": "matched_non_empty",
|
||||
"response_type": "FACTUAL_SUMMARY",
|
||||
"result_mode": "confirmed_balance",
|
||||
"balance_confirmed": True,
|
||||
"mcp_discovery_route_candidate_status": "ready_for_reviewed_execution",
|
||||
"mcp_discovery_response_applied": False,
|
||||
"assistant_runtime_contract_v1": {
|
||||
"capability_contract_id": "confirmed_vat_liability_for_tax_period",
|
||||
"truth_gate_contract_status": "full_confirmed",
|
||||
"carryover_eligibility": "root_only",
|
||||
},
|
||||
"assistant_truth_answer_policy_v1": {
|
||||
"truth_gate": {
|
||||
"coverage_status": "full",
|
||||
"evidence_grade": "strong",
|
||||
"truth_mode": "confirmed",
|
||||
"source_truth_gate_status": "full_confirmed",
|
||||
},
|
||||
"answer_shape": {
|
||||
"answer_shape": "confirmed_factual",
|
||||
"capability_contract_id": "confirmed_vat_liability_for_tax_period",
|
||||
},
|
||||
},
|
||||
},
|
||||
"session_summary": {},
|
||||
},
|
||||
entries=[],
|
||||
)
|
||||
|
||||
self.assertEqual(step_state["capability_contract_id"], "confirmed_vat_liability_for_tax_period")
|
||||
self.assertEqual(step_state["truth_gate_contract_status"], "full_confirmed")
|
||||
self.assertEqual(step_state["truth_mode"], "confirmed")
|
||||
self.assertEqual(step_state["coverage_status"], "full")
|
||||
self.assertEqual(step_state["answer_shape"], "confirmed_factual")
|
||||
self.assertTrue(step_state["runtime_factual_answer_validated"])
|
||||
self.assertEqual(step_state["acceptance_status"], "validated")
|
||||
|
||||
def test_analyst_shape_only_target_is_suppressed_for_truth_confirmed_runtime_step(self) -> None:
|
||||
repair_targets = {
|
||||
"targets": [],
|
||||
"step_validation_index": {
|
||||
"scenario_a:step_01": {
|
||||
"acceptance_status": "validated",
|
||||
"violated_invariants": [],
|
||||
"warnings": [],
|
||||
"runtime_factual_answer_validated": True,
|
||||
"truth_gate_contract_status": "full_confirmed",
|
||||
"assistant_text_excerpt": "Confirmed business answer for the selected item.",
|
||||
}
|
||||
},
|
||||
}
|
||||
analyst_verdict = {
|
||||
"priority_targets": [
|
||||
{
|
||||
"scenario_id": "scenario_a",
|
||||
"step_id": "step_01",
|
||||
"severity": "P1",
|
||||
"problem_type": "capability_gap",
|
||||
"root_cause_layers": ["runtime_capability_gap"],
|
||||
"fix_goal": "Remove shape-only acceptance and add runtime truth confirmation.",
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
merged = dcl.merge_analyst_priority_repair_targets(repair_targets, analyst_verdict)
|
||||
|
||||
self.assertEqual(merged["target_count"], 0)
|
||||
self.assertEqual(merged["suppressed_analyst_priority_target_count"], 1)
|
||||
self.assertEqual(
|
||||
merged["suppressed_analyst_priority_targets"][0]["reason"],
|
||||
"analyst_target_contradicts_validated_step",
|
||||
)
|
||||
|
||||
def test_analyst_temporal_cutoff_false_positive_is_suppressed_for_validated_step(self) -> None:
|
||||
repair_targets = {
|
||||
"targets": [],
|
||||
"step_validation_index": {
|
||||
"inventory_chain:s03": {
|
||||
"acceptance_status": "validated",
|
||||
"violated_invariants": [],
|
||||
"warnings": [],
|
||||
"runtime_factual_answer_validated": True,
|
||||
"extracted_filters": {"as_of_date": "2016-03-31"},
|
||||
"assistant_text_excerpt": (
|
||||
"По позиции до 31.03.2016 поставщик не подтвержден. "
|
||||
"Первая закупка: 05.02.2015. Последняя закупка: 22.07.2015. "
|
||||
"Опорный документ от 22.07.2015."
|
||||
),
|
||||
}
|
||||
},
|
||||
}
|
||||
analyst_verdict = {
|
||||
"priority_targets": [
|
||||
{
|
||||
"scenario_id": "inventory_chain",
|
||||
"step_id": "s03",
|
||||
"severity": "P0",
|
||||
"problem_type": "other",
|
||||
"root_cause_layers": ["temporal_honesty_gap"],
|
||||
"fix_goal": "The answer declares a historical cutoff but cites proof rows with later dates.",
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
merged = dcl.merge_analyst_priority_repair_targets(repair_targets, analyst_verdict)
|
||||
|
||||
self.assertEqual(merged["target_count"], 0)
|
||||
self.assertEqual(merged["suppressed_analyst_priority_target_count"], 1)
|
||||
|
||||
def test_analyst_temporal_cutoff_real_out_of_window_target_is_kept(self) -> None:
|
||||
repair_targets = {
|
||||
"targets": [],
|
||||
"step_validation_index": {
|
||||
"inventory_chain:s03": {
|
||||
"acceptance_status": "validated",
|
||||
"violated_invariants": [],
|
||||
"warnings": [],
|
||||
"runtime_factual_answer_validated": True,
|
||||
"extracted_filters": {"as_of_date": "2016-03-31"},
|
||||
"assistant_text_excerpt": (
|
||||
"По позиции до 31.03.2016 поставщик не подтвержден. "
|
||||
"Опорный документ от 25.05.2026."
|
||||
),
|
||||
}
|
||||
},
|
||||
}
|
||||
analyst_verdict = {
|
||||
"priority_targets": [
|
||||
{
|
||||
"scenario_id": "inventory_chain",
|
||||
"step_id": "s03",
|
||||
"severity": "P0",
|
||||
"problem_type": "other",
|
||||
"root_cause_layers": ["temporal_honesty_gap"],
|
||||
"fix_goal": "The answer declares a historical cutoff but cites proof rows with later dates.",
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
merged = dcl.merge_analyst_priority_repair_targets(repair_targets, analyst_verdict)
|
||||
|
||||
self.assertEqual(merged["target_count"], 1)
|
||||
self.assertEqual(merged["suppressed_analyst_priority_target_count"], 0)
|
||||
|
||||
def test_literal_result_mode_contract_still_rejects_mismatch(self) -> None:
|
||||
step_state = dcl.build_scenario_step_state(
|
||||
scenario_id="literal_result_mode_demo",
|
||||
|
|
|
|||
Loading…
Reference in New Issue