import assert from "node:assert/strict"; import { readFileSync } from "node:fs"; import test from "node:test"; import { createElement } from "react"; import { renderToStaticMarkup } from "react-dom/server"; import { RectificationRangeDelivery } from "../src/components/rectification-range-delivery.tsx"; import { parseRectificationCandidateResult } from "../src/lib/rectification-candidate-result.ts"; import { buildRectificationTimeline } from "../src/lib/rectification-timeline-scale.ts"; import { deliveryTurnNarration, nonConvergingRangeNarration } from "../src/lib/rectification-agentic/user-copy.ts"; import { buildProductCaseInferenceState } from "../src/lib/rectification-agentic/v9/product-inference.ts"; import { productDeliveryForDomain, parseProductDelivery } from "../src/lib/rectification-agentic/v9/product-delivery.ts"; import { runV9CandidateScore } from "../src/lib/rectification-agentic/v9/engine-client.ts"; import { dossierResponse } from "../src/lib/rectification-agentic/v9/case-dossier-response.ts"; import { decideFromDossier } from "../src/lib/rectification-agentic/v9/decision-from-dossier.ts"; import { parseV9CaseDossier } from "../src/lib/rectification-agentic/v9/tool-service.ts"; import { dossierFixture } from "./rectification-v9-test-support.ts"; import { buildGoldenLatest } from "./rectification-grounding-support.ts"; // Unmodified native API capture; absent/partial scans are fault-injection // controls. These test real projection/render/copy, not SQL or calibration. const golden = JSON.parse(readFileSync(new URL("./fixtures/varga-api-response.golden.json", import.meta.url), "utf8")); for (const domain of [null, "marriage", "career", "general", "report", "other"] as const) { for (const scan of ["missing", "partial", "complete"] as const) { test(`actual product delivery keeps ${domain ?? "legacy-null"} separate with ${scan} scan`, async (t) => { t.mock.method(globalThis, "fetch", async () => Response.json(golden.response)); const req = golden.request; const range = { start_time: req.start_time, end_time: req.end_time, candidate_intervals: golden.response.decision_receipt.candidate_intervals }; const score = await runV9CandidateScore({ baselineBirthSnapshot: { birth_date: req.birth_date, latitude: req.lat, longitude: req.lon, timezone_offset: req.tz, }, candidateRange: range, events: req.events }); const identityGolden = await buildGoldenLatest(); const inference = buildProductCaseInferenceState({ range, candidates: score.candidates, evidence: [], probes: [], ...(scan === "complete" ? { segmentMinutes: golden.scan.minutes, segmentScanComplete: true } : {}), segmentTargets: productDeliveryForDomain(domain)?.targets, }, domain); const withoutScan = { ...inference }; delete withoutScan.segment_summary; delete withoutScan.segment_minutes; delete withoutScan.segment_targets; delete withoutScan.segment_scan_complete; delete withoutScan.segment_order_enabled; const receipt = { ...score.decisionReceipt, inference_state: scan === "missing" ? withoutScan : inference }; const raw = dossierFixture({ candidateRange: range, latestResult: { ...identityGolden.latest, candidates: score.candidates.map(candidate => ({ ...candidate, candidate_id: candidate.candidateId, time: candidate.time, rank: candidate.rank, relative_support: candidate.relativeSupport, tied_minute_count: candidate.tiedMinuteCount })), representative_time: score.representativeTime, decision_receipt: receipt, } }); Object.assign(raw.case, { rectification_domain: domain }); const dossier = parseV9CaseDossier(raw)!; const publicDossier = dossierResponse(dossier, [], { status: "verified", requiresSkillAdoption: false } as never, { liveIdentity: { algorithmVersion: identityGolden.scored.algorithmVersion, policyVersion: identityGolden.scored.policyVersion } }); const projected = publicDossier.latest_result!; const parsed = parseRectificationCandidateResult(projected); assert.ok(parsed); const delivery = parsed.rangeDelivery!; const decision = decideFromDossier(dossier); const markup = renderToStaticMarkup(createElement(RectificationRangeDelivery, { result: parsed, acceptingCandidateId: null, readonly: false, onAccept: () => assert.fail("render cannot adopt") })); const timeline = buildRectificationTimeline({ searchWindow: [range.start_time, range.end_time], candidateTimes: parsed.candidates.map(candidate => candidate.time), credibleRange: parsed.credibleRange, stage: "minute", workingTime: parsed.representativeTime, segmentSummary: delivery.segment_summary, productDelivery: delivery.product_delivery }); const narration = deliveryTurnNarration(decision); const stopped = nonConvergingRangeNarration(decision); const report = parsed.verificationReportMarkdown ?? ""; if (domain === null) { assert.equal(delivery.product_delivery, null); assert.equal(delivery.segment_summary, null); assert.match(markup, /目前范围/); assert.doesNotMatch(markup, /用哪张盘解读/); assert.doesNotMatch(narration + stopped + report, /上升盘型暂未知|representative_diagnostic_not_adopted_chart/); } else { assert.deepEqual(delivery.product_delivery, productDeliveryForDomain(domain)); assert.match(markup, /用哪张盘解读/); assert.doesNotMatch(markup, /最可能/); assert.doesNotMatch(narration + stopped + timeline?.workingLabel, /目前范围|眼下更像|代表分钟 \d/); assert.match(report, /只供引擎诊断/); const nestedMissing = { ...delivery }; delete nestedMissing.product_delivery; const recovered = parseRectificationCandidateResult({ ...projected, rangeDelivery: nestedMissing }); assert.deepEqual(recovered?.rangeDelivery?.product_delivery, productDeliveryForDomain(domain), "persisted identity survives missing nested contract"); const wrongSummary = delivery.segment_summary ? { ...delivery.segment_summary, targets: ["D1"] } : null; const mismatch = parseRectificationCandidateResult({ ...projected, rangeDelivery: { ...delivery, segment_summary: wrongSummary } }); assert.equal(mismatch?.rangeDelivery?.segment_summary, null, "mismatched targets cannot render or adopt a product summary"); assert.deepEqual(mismatch?.rangeDelivery?.product_delivery, productDeliveryForDomain(domain)); const explicitlyLegacy = parseRectificationCandidateResult({ ...projected, productDelivery: null }); assert.equal(explicitlyLegacy?.rangeDelivery?.product_delivery, null, "explicit server legacy null overrides receipt and nested opt-in identity"); assert.equal(explicitlyLegacy?.rangeDelivery?.segment_summary, null); if (scan === "missing") { assert.equal(delivery.segment_summary, null); assert.match(markup + narration + stopped + timeline?.workingLabel + report, /上升盘型暂未知/); assert.doesNotMatch(markup, / { const sql = readFileSync(new URL("../supabase/migrations/20261001020000_rectification_segment_checks.sql", import.meta.url), "utf8"); const domainRows = ["general", "report", "education", "career", "relationship", "marriage", "relocation", "finance", "health", "family", "other"]; assert.deepEqual([...sql.matchAll(/p_(?:product_)?domain not in \(([^)]+)\)/g)].map(match => [...match[1].matchAll(/'([^']+)'/g)].map(domain => domain[1])), [domainRows, domainRows]); for (const domain of domainRows) { const targets = ["marriage", "relationship"].includes(domain) ? ["D1", "D9"] : domain === "career" ? ["D1", "D10"] : ["D1", "D9", "D10"]; const expected = { contract: "segment-product-v1", domain, targets }; assert.deepEqual(productDeliveryForDomain(domain), expected); assert.deepEqual(parseProductDelivery(expected), expected); } assert.equal(productDeliveryForDomain(null), null); assert.equal(productDeliveryForDomain(undefined), null); assert.equal(productDeliveryForDomain("invented"), null); }); test("product delivery parser accepts only the persisted domain target contract", () => { assert.equal(parseProductDelivery(null), null); assert.equal(parseProductDelivery({ contract: "segment-product-v1", domain: "career", targets: ["D1", "D9"] }), null); assert.equal(parseProductDelivery({ contract: "segment-product-v1", domain: "general", targets: ["D1", "D7"] }), null); assert.equal(parseProductDelivery({ contract: "segment-product-v1", domain: "invented", targets: ["D1"] }), null); assert.deepEqual(parseProductDelivery(productDeliveryForDomain("marriage")), productDeliveryForDomain("marriage")); });