Files
Jyotisha/frontend/tests/rectification-product-delivery.test.ts
T
Jesse_ChenandClaude Opus 5.5 b62308c4ad
Independent Staging Quality Gate / validate (push) Successful in 33m11s
Independent Staging Quality Gate / publish (push) Successful in 3m46s
fix(rectification): segment verification report drops the minute leaderboard (BUG-1151)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017eEAG8HD3mm8gsKXgk8uU8
2026-10-01 21:14:57 +08:00

145 lines
10 KiB
TypeScript

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/);
// BUG-1151: the minute product keeps its leaderboard.
assert.match(report, /\| 排名 \| 时间 \| 相对支持 \|/);
assert.match(report, /本命上升(该分钟)/);
} else {
assert.deepEqual(delivery.product_delivery, productDeliveryForDomain(domain));
assert.match(markup, /用哪张盘解读/);
assert.doesNotMatch(markup, /最可能/);
assert.doesNotMatch(narration + stopped + timeline?.workingLabel, /目前范围|眼下更像|代表分钟 \d/);
assert.match(report, /只供引擎诊断/);
// BUG-1151 (product 10-01 option A): no minute leaderboard in the segment report.
assert.doesNotMatch(report, /当前候选基本并列|当前代表分钟|本命上升(该分钟)|相对支持|候选窗/);
assert.match(report, /不可分宽度/);
assert.match(report, /Technique Audit Table/);
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, /<button|分盘解读需要更准的出生时间/);
} else {
assert.ok(delivery.segment_summary);
assert.equal(delivery.segment_summary.scan_complete, scan === "complete");
assert.deepEqual(delivery.segment_summary.targets, productDeliveryForDomain(domain)?.targets);
if (scan === "partial") {
assert.match(markup, /候选盘型不能证明全窗一致/);
for (const copy of [narration, stopped, timeline?.workingLabel ?? "", report]) {
assert.match(copy, /完整窗口扫描暂不可用/);
assert.match(copy, /仅是候选盘型,不能证明全窗一致/);
}
assert.equal(delivery.segment_summary.adoption_minute, null);
assert.doesNotMatch(markup, /<button/);
}
}
}
});
}
}
test("product identity covers every SQL-legal persisted domain without opting legacy in", () => {
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"));
});