import type { RectificationRpcClient } from "../src/lib/rectification-agentic/v9/tool-service.ts"; export type FakeRpcHandler = ( fn: string, args: Record, ) => unknown | Promise; export type FakeAccounting = { calls: Array<{ fn: string; args: Record }>; client: RectificationRpcClient; }; export function fakeAccounting( handlers: Partial>, options: { fallback?: FakeRpcHandler } = {}, ): FakeAccounting { const calls: Array<{ fn: string; args: Record }> = []; const client: RectificationRpcClient = { rpc(fn, args) { calls.push({ fn, args }); const handler = handlers[fn] ?? options.fallback; if (!handler) { return Promise.resolve({ data: null, error: { message: `unexpected rpc ${fn}` }, }); } return Promise.resolve() .then(() => handler(fn, args)) .then( (data) => ({ data, error: null }), (error: unknown) => ({ data: null, error: { message: error instanceof Error ? error.message : String(error) }, }), ); }, }; return { calls, client }; } export const CASE_ID = "11111111-1111-4111-8111-111111111111"; export const SESSION_ID = "22222222-2222-4222-8222-222222222222"; export const TURN_ID = "33333333-3333-4333-8333-333333333333"; export const ATTEMPT_ID = "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa"; export const FOCUS_ID = "abababab-abab-4bab-8bab-abababababab"; export const EVIDENCE_ID = "44444444-4444-4444-8444-444444444444"; export const RESULT_ID = "55555555-5555-4555-8555-555555555555"; export const CANDIDATE_ID = "88888888-8888-4888-8888-888888888881"; export const SECOND_CANDIDATE_ID = "88888888-8888-4888-8888-888888888882"; export const USER_ID = "66666666-6666-4666-8666-666666666666"; export const SOURCE_TURN_ID = "77777777-7777-4777-8777-777777777777"; export const RECTIFICATION_SKILL_SHA256 = "5acb3103e80993ea611b93d2c1746e70b74aff8f8636bcffc80a837b954b470d"; export const RECTIFICATION_SKILL_SOURCE_COMMIT = "0fd111d16b45796086a6c1d0945dbd3de6755d8a"; export const CANDIDATE_RANGE = { start_time: "04:50", end_time: "05:10" }; export function dossierFixture(overrides: { status?: string; turnCount?: number; evidenceCount?: number; latestResult?: unknown; evidence?: unknown[]; turns?: unknown[]; candidateRange?: { start_time: string; end_time: string } | null; conversationSummary?: unknown; } = {}) { return { case: { case_id: CASE_ID, session_id: SESSION_ID, status: overrides.status ?? "collecting_evidence", skill_name: "jyotish-birth-time-rectification", skill_version: "9.0.0", candidate_range: overrides.candidateRange ?? CANDIDATE_RANGE, accepted_time: null, confirmed_time: null, completed_at: null, closed_reason: null, last_activity_at: "2026-08-12T10:00:00.000Z", evidence_count: overrides.evidenceCount ?? 1, turn_count: overrides.turnCount ?? 2, }, turns: overrides.turns ?? [ { id: TURN_ID, role: "user", text: "2016年9月离开家去北京开始工作", status: "completed", created_at: "2026-08-12T10:00:00.000Z", completed_at: "2026-08-12T10:00:05.000Z", }, ], evidence: overrides.evidence ?? [ { id: EVIDENCE_ID, source_turn_id: TURN_ID, subject: "self", event_kind: "career_entry", domain: "career", occurred_from: "2016-09-01", occurred_to: null, date_precision: "month", summary: "2016年9月离家去北京开始工作", status: "confirmed", supersedes_evidence_id: null, created_at: "2026-08-12T10:00:06.000Z", }, ], conversation_summary: overrides.conversationSummary ?? { confirmed_evidence_summary: [], pending_revisions: [], active_focus: null, declined_skipped_topics: [], candidate_divergence_summary: null, missing_evidence_categories: [], last_result_policy: null, summary_version: 1, updated_at: "2026-08-12T10:00:06.000Z", }, latest_result: overrides.latestResult ?? null, }; } export function conversationSummaryFixture(overrides: { activeFocus?: unknown; declinedSkippedTopics?: unknown[]; pendingRevisions?: unknown[]; } = {}) { return { confirmed_evidence_summary: [], pending_revisions: overrides.pendingRevisions ?? [], active_focus: overrides.activeFocus ?? null, declined_skipped_topics: overrides.declinedSkippedTopics ?? [], candidate_divergence_summary: null, missing_evidence_categories: [], last_result_policy: null, summary_version: 1, updated_at: "2026-08-12T10:00:06.000Z", }; } export function activeFocusFixture(overrides: { id?: string; targetEvidenceId?: string | null; intent?: string; targetDomain?: string | null; targetKind?: string | null; expectedAnswerSchema?: Record; } = {}) { return { id: overrides.id ?? FOCUS_ID, case_id: CASE_ID, question_id: "question-1", intent: overrides.intent ?? "confirm_revision", target_evidence_id: overrides.targetEvidenceId ?? null, target_domain: overrides.targetDomain ?? "career", target_kind: overrides.targetKind ?? "career_entry", expected_answer_schema: overrides.expectedAnswerSchema ?? {}, status: "active", asked_at: "2026-08-12T10:00:05.000Z", resolved_at: null, }; } export function computeFixture(overrides: { baselineBirthSnapshot?: Record; baselineProfileFingerprint?: string; } = {}) { return { case_id: CASE_ID, skill_version: "9.0.0", baseline_profile_fingerprint: overrides.baselineProfileFingerprint ?? "a".repeat(64), baseline_birth_snapshot: overrides.baselineBirthSnapshot ?? { birth_date: "1997-08-08", birth_place_label: "河北省邯郸市", latitude: 36.420487, longitude: 114.209936, timezone_id: "Asia/Shanghai", timezone_offset: 8, birth_time_source: "family_exact", reported_birth_time: "05:00", active_birth_time: null, uncertainty_before_minutes: 10, uncertainty_after_minutes: 10, }, candidate_range: CANDIDATE_RANGE, }; } /** Tool receipt RPCs always succeed. */ export const receiptHandlers: Partial> = { get_agentic_rectification_skill_identity: () => ({ skill_name: "jyotish-birth-time-rectification", skill_version: "9.0.0", skill_sha256: RECTIFICATION_SKILL_SHA256, skill_source_commit: RECTIFICATION_SKILL_SOURCE_COMMIT, }), create_agentic_rectification_run_attempt: (_fn, args) => ({ attempt_id: Number(args.p_attempt_number) === 2 ? "bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb" : ATTEMPT_ID, idempotent: false, }), finalize_agentic_rectification_run_attempt: (_fn, args) => ({ attempt_id: args.p_attempt_id ?? ATTEMPT_ID, status: args.p_status ?? "completed", }), insert_agentic_rectification_skill_run_receipt: () => ({ receipt_id: "88888888-8888-4888-8888-888888888888", skill_name: "jyotish-birth-time-rectification", skill_version: "9.0.0", skill_sha256: RECTIFICATION_SKILL_SHA256, source_commit: RECTIFICATION_SKILL_SOURCE_COMMIT, }), insert_agentic_rectification_tool_receipt: () => ({ receipt_id: "99999999-9999-4999-8999-999999999999" }), insert_agentic_rectification_run_phase: () => ({ phase_id: "99999999-9999-4999-8999-999999999999" }), transition_agentic_rectification_case_status: () => ({ case_id: CASE_ID, status: "collecting_evidence", idempotent: false }), }; export function candidateSnapshotFixture(overrides: { confirmationAllowed?: boolean; selectionAllowed?: boolean; representativeTime?: string | null; selectedTime?: string | null; selectionKind?: string | null; candidates?: unknown[]; } = {}) { return { result_id: RESULT_ID, candidates: overrides.candidates ?? [ { candidate_id: CANDIDATE_ID, rank: 1, time: "05:02", relative_support: 58, tied_minute_count: 2 }, { candidate_id: SECOND_CANDIDATE_ID, rank: 2, time: "04:55", relative_support: 42, tied_minute_count: 3 }, ], overall_confidence: "medium", selection_allowed: overrides.selectionAllowed ?? true, confirmation_allowed: overrides.confirmationAllowed ?? false, decision_receipt: { receipt_version: "candidate-decision-receipt-v2", policy_version: "rectification-candidate-policy-v2", selection_allowed: overrides.selectionAllowed ?? true, acceptance_allowed: overrides.selectionAllowed ?? true, confirmation_allowed: overrides.confirmationAllowed ?? false, representative_candidate_id: overrides.representativeTime ? CANDIDATE_ID : null, overall_confidence: "medium", }, execution_ledger: [{ method: "d1-rashi", status: "executed" }], representative_time: overrides.representativeTime ?? null, selected_time: overrides.selectedTime ?? null, selection_kind: overrides.selectionKind ?? null, evidence_ledger_fingerprint: "b".repeat(64), candidate_range_fingerprint: "c".repeat(64), skill_version: "9.0.0", algorithm_version: "rectification-v5", created_at: "2026-08-12T10:05:00.000Z", invalidated_at: null, }; }