import { describe, expect, it } from "vitest"; import { buildDeepAnalysisDebugPayload } from "../src/services/assistantDebugPayloadAssembler"; function baseInput() { return { traceId: "trace-1", promptVersion: "normalizer_v2_0_2", schemaVersion: "normalized_query_v2_0_2", fallbackType: "none", routeSummary: { mode: "deterministic_v2" }, fragments: [{ fragment_id: "F1" }], requirementsExtracted: [{ requirement_id: "R1", status: "covered" }], coverageReport: { requirements_total: 1, requirements_covered: 1 }, routes: [{ fragment_id: "F1", route: "hybrid_store_plus_live" }], retrievalStatus: [ { fragment_id: "F1", requirement_ids: ["R1"], route: "hybrid_store_plus_live", status: "ok", result_type: "summary" } ], retrievalResults: [{ fragment_id: "F1", status: "ok" }], groundingCheck: { status: "grounded" }, droppedIntentSegments: [], questionTypeClass: "factual_lookup", companyAnchors: { companies: ["demo"] }, runtimeAnalysisContext: { active: true, as_of_date: "2020-07-31", period_from: null, period_to: null, source: "eval_analysis_date", snapshot_mode: "auto" as const }, businessScopeResolution: { business_scope_raw: ["company_specific_accounting"], business_scope_resolved: ["company_specific_accounting"], company_grounding_applied: true, scope_resolution_reason: ["resolved"] }, temporalGuard: { raw_time_anchor: "2020-07", raw_time_scope: "month", resolved_time_anchor: "2020-07", resolved_primary_period: { from: "2020-07-01", to: "2020-07-31", granularity: "day" }, effective_primary_period: { from: "2020-07-01", to: "2020-07-31", granularity: "day" }, temporal_guard_input: "2020-07", temporal_alignment_status: "aligned", temporal_resolution_source: "analysis_context", temporal_guard_basis: "analysis_context", temporal_guard_applied: true, temporal_guard_outcome: "pass" }, polarityAudit: { raw_numeric_tokens: ["60.01"], classified_numeric_tokens: [{ token: "60.01" }], rejected_as_non_accounts: [], resolved_account_anchors: ["60.01"] }, claimAnchorAudit: { settlement_role: "supplier", settlement_role_resolution_reason: ["account_60_detected"], polarity_resolution_status: "resolved" }, targetedEvidenceAudit: { targeted_evidence_hit_rate: 1 }, evidenceAdmissibilityGateAudit: { admissible_evidence_count: 1 }, rbpLiveRouteAudit: null, faLiveRouteAudit: null, groundedAnswerEligibilityGuard: { eligibility_time_basis: "analysis_context", eligible: true }, followupStateUsage: null, compositionDebug: { problem_centric_answer_applied: true, problem_units_used_count: 2, problem_answer_mode: "stage3_lifecycle_aware_v1", problem_unit_ids_used: ["pu-1", "pu-2"] }, addressRuntimeMetaForDeep: { attempted: true, applied: true, reason: "ok", provider: "openai", fallbackRuleHit: null, toolGateDecision: "run_address_lane", toolGateReason: "detected", predecomposeContract: { schema_version: "x" }, orchestrationContract: { schema_version: "y" } }, outcomeClassV1: "FULLY_ANSWERED", assistantOrchestrationContractsV1: { query_frame: {}, execution_plan: {}, evidence_bundle: {}, coverage: {} }, answerStructureV11: { schema_version: "answer_structure_v1_1" }, assistantReply: [ "Коротко: Признак проблемы подтвержден частично.", "Что именно проверено:\n- Проверен учетный контур.", "Что найдено:\n- Есть признак разрыва цепочки.", "Что пока не доказано:\n- Не хватает части подтверждений.", "Что проверить первым:\n- Уточнить период." ].join("\n\n"), investigationStateSnapshot: { status: "active" }, normalizedPayload: { schema_version: "normalized_query_v2_0_2" } }; } describe("assistant debug payload assembler", () => { it("builds deep debug payload with analysis context and optional sections", () => { const payload = buildDeepAnalysisDebugPayload(baseInput()); expect(payload.trace_id).toBe("trace-1"); expect(payload.analysis_context_applied).toBe(true); expect(payload.analysis_context).toMatchObject({ as_of_date: "2020-07-31", source: "eval_analysis_date" }); expect(payload.problem_unit_ids_used).toEqual(["pu-1", "pu-2"]); expect(payload.address_llm_predecompose_applied).toBe(true); expect(payload.assistant_outcome_class_v1).toBe("FULLY_ANSWERED"); expect(payload.answer_contract_stage4_v1?.is_stage4_shape).toBe(true); expect(payload.assistant_truth_answer_policy_v1).toEqual( expect.objectContaining({ schema_version: "assistant_truth_answer_policy_runtime_v1", policy_owner: "assistantTruthAnswerPolicyRuntimeAdapter" }) ); expect(payload.coverage_gate_contract).toEqual( expect.objectContaining({ coverage_status: "full", truth_mode: "confirmed" }) ); expect(payload.answer_shape_contract).toEqual( expect.objectContaining({ answer_shape: "confirmed_factual", reply_type: "deep_analysis" }) ); expect(payload.assistant_state_transition_v1).toEqual( expect.objectContaining({ schema_version: "assistant_state_transition_runtime_v1", state_owner: "assistantStateTransitionRuntimeAdapter", application_status: "unresolved" }) ); expect(payload.state_transition_contract).toEqual( expect.objectContaining({ effective_carryover_depth: "none" }) ); expect(payload.assistant_capability_binding_v1).toEqual( expect.objectContaining({ schema_version: "assistant_capability_runtime_binding_v1", binding_owner: "assistantCapabilityRuntimeBindingAdapter", binding_status: "not_applicable", binding_action: "observe_only" }) ); expect(payload.assistant_mcp_discovery_entry_point_v1).toBeNull(); expect(payload.mcp_discovery_attempted).toBe(false); expect(payload.mcp_discovery_hot_runtime_wired).toBe(false); }); it("omits optional fields when they are not provided", () => { const input = baseInput(); input.runtimeAnalysisContext.active = false; input.followupStateUsage = null; input.compositionDebug.problem_unit_ids_used = []; input.rbpLiveRouteAudit = null; input.faLiveRouteAudit = null; input.addressRuntimeMetaForDeep = null; const payload = buildDeepAnalysisDebugPayload(input); expect(payload.analysis_context).toBeNull(); expect(Object.prototype.hasOwnProperty.call(payload, "followup_state_usage")).toBe(false); expect(Object.prototype.hasOwnProperty.call(payload, "problem_unit_ids_used")).toBe(false); expect(payload.address_llm_predecompose_applied).toBe(false); expect(payload.address_llm_predecompose_contract).toBeNull(); }); it("marks non-stage4 answer shapes in contract audit", () => { const input = baseInput(); input.assistantReply = [ "Коротко: Есть проблема.", "Что сломано:\n- Разрыв перехода.", "Ограничения:\n- Частичная опора." ].join("\n\n"); const payload = buildDeepAnalysisDebugPayload(input); expect(payload.answer_contract_stage4_v1?.is_stage4_shape).toBe(false); expect(payload.answer_contract_stage4_v1?.legacy_blocks_present).toContain("Что сломано"); expect(payload.answer_contract_stage4_v1?.legacy_blocks_present).toContain("Ограничения"); }); it("attaches MCP discovery entry point summary when runtime meta provides it", () => { const input = baseInput(); input.addressRuntimeMetaForDeep = { ...input.addressRuntimeMetaForDeep, mcpDiscoveryRuntimeEntryPoint: { schema_version: "assistant_mcp_discovery_runtime_entry_point_v1", policy_owner: "assistantMcpDiscoveryRuntimeEntryPoint", entry_status: "bridge_executed", hot_runtime_wired: false, discovery_attempted: true, turn_input: { adapter_status: "ready" }, 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" } }, reason_codes: ["runtime_entry_point_bridge_executed"] } }; const payload = buildDeepAnalysisDebugPayload(input); expect(payload.mcp_discovery_entry_status).toBe("bridge_executed"); expect(payload.mcp_discovery_attempted).toBe(true); expect(payload.mcp_discovery_bridge_status).toBe("answer_draft_ready"); expect(payload.mcp_discovery_answer_mode).toBe("confirmed_with_bounded_inference"); expect(payload.mcp_discovery_business_fact_answer_allowed).toBe(true); }); });