Review-only snapshot for BUG-1115 through BUG-1117; not merge-ready. New opening and append-turn PostgreSQL permission failures remain blocked. Persisted joint replay has zero completed questions; segment ordering remains off by default. The existing offline replay JSON is retained stale and unchanged after a denied overwrite, including its CRLF line endings. Browser/provider validation and final serial gates remain pending. No deployment, role permission changes, or staging/main push. Co-Authored-By: Claude Code <noreply@anthropic.com>
137 lines
9.9 KiB
TypeScript
137 lines
9.9 KiB
TypeScript
import assert from "node:assert/strict";
|
|
import { readFileSync } from "node:fs";
|
|
import test from "node:test";
|
|
import { scoreAndPersistCurrentEvidence } from "../src/lib/rectification-agentic/v9/score-persist.ts";
|
|
import { buildProductCaseInferenceState } from "../src/lib/rectification-agentic/v9/product-inference.ts";
|
|
import { buildCaseInferenceState } from "../src/lib/rectification-agentic/v9/inference-adapter.ts";
|
|
import { asInferenceState } from "../src/lib/rectification-agentic/core/compose-receipt.ts";
|
|
import { rangeDeliveryForSnapshot } from "../src/lib/rectification-agentic/v9/divergence-panel.ts";
|
|
import { productDeliveryForDomain } from "../src/lib/rectification-agentic/v9/product-delivery.ts";
|
|
import { parseV9CaseDossier } from "../src/lib/rectification-agentic/v9/tool-service.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 { latestResultToolProjection } from "../src/mastra/rectification-v9-tools.ts";
|
|
import { parseRectificationCandidateResult } from "../src/lib/rectification-candidate-result.ts";
|
|
import { deliveryTurnNarration, nonConvergingRangeNarration } from "../src/lib/rectification-agentic/user-copy.ts";
|
|
import { buildGoldenLatest } from "./rectification-grounding-support.ts";
|
|
import { runV9CandidateScore } from "../src/lib/rectification-agentic/v9/engine-client.ts";
|
|
import { refinementFromDecisionReceipt } from "../src/lib/rectification-agentic/v9/refinement-packet.ts";
|
|
import { CASE_ID, USER_ID, TURN_ID, computeFixture, dossierFixture, fakeAccounting } from "./rectification-v9-test-support.ts";
|
|
|
|
// Unmodified real native API captures for a public AA case; persistence doubles
|
|
// below are not SQL, Skill-upgrade history or six-question calibration evidence.
|
|
const golden = JSON.parse(readFileSync(new URL("./fixtures/varga-api-response.golden.json", import.meta.url), "utf8"));
|
|
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 snapshot = { birth_date: req.birth_date, reported_birth_time: "09:09", active_birth_time: null,
|
|
birth_time_source: "approximate", latitude: req.lat, longitude: req.lon, timezone_id: "America/Los_Angeles", timezone_offset: req.tz };
|
|
|
|
for (const domain of [null, "marriage", "career", "general", "report", "other"] as const) {
|
|
for (const scanFails of [false, true]) {
|
|
test(`production rescore keeps persisted opt-in delivery boundary: ${domain ?? "legacy-null"}${scanFails ? " with failed scan" : ""}`, async (t) => {
|
|
const keys = ["RECTIFICATION_ALGORITHM_VERSION", "RECTIFICATION_DECISION_POLICY_VERSION", "RECTIFICATION_SEGMENT_ORDER"];
|
|
const saved = keys.map(key => [key, process.env[key]] as const);
|
|
process.env.RECTIFICATION_ALGORITHM_VERSION = golden.response.algorithm_version;
|
|
process.env.RECTIFICATION_DECISION_POLICY_VERSION = golden.response.decision_policy_version;
|
|
process.env.RECTIFICATION_SEGMENT_ORDER = "on";
|
|
t.after(() => { for (const [key, value] of saved) { if (value === undefined) delete process.env[key]; else process.env[key] = value; } });
|
|
const requests: string[] = [];
|
|
t.mock.method(globalThis, "fetch", async (endpoint: unknown) => {
|
|
requests.push(String(endpoint));
|
|
if (String(endpoint).includes("varga-segments")) {
|
|
assert.ok(domain, "nullable history must not trigger a product segment scan");
|
|
if (scanFails) throw new Error("controlled optional scan failure");
|
|
return Response.json(golden.scan);
|
|
}
|
|
return Response.json(golden.response);
|
|
});
|
|
const raw = dossierFixture({ candidateRange: range, latestResult: null, evidence: req.events.map((event: Record<string, string>) => ({
|
|
id: event.id, source_turn_id: TURN_ID, subject: "self", event_kind: event.event_kind, domain: event.domain,
|
|
occurred_from: event.date_start, occurred_to: event.date_end, date_precision: event.precision, summary: event.summary,
|
|
status: "confirmed", supersedes_evidence_id: null, created_at: "2026-09-30T00:00:00Z",
|
|
})) });
|
|
Object.assign(raw.case, { rectification_domain: domain });
|
|
const accounting = fakeAccounting({
|
|
get_agentic_rectification_case_dossier: () => raw,
|
|
get_agentic_rectification_case_compute: () => ({ ...computeFixture({ baselineBirthSnapshot: snapshot }), candidate_range: range }),
|
|
persist_agentic_rectification_candidate_v2: (_fn, args) => ({
|
|
...golden.response.decision_receipt, result_id: args.p_engine_result_id, candidates: args.p_candidates,
|
|
algorithm_version: args.p_algorithm_version, decision_policy_version: args.p_decision_policy_version,
|
|
decision_receipt: args.p_decision_receipt,
|
|
}),
|
|
});
|
|
const scored = await scoreAndPersistCurrentEvidence({ accounting: accounting.client, userId: USER_ID, caseId: CASE_ID, keepAnswers: false });
|
|
const inference = asInferenceState(scored.decisionReceipt.inference_state);
|
|
assert.ok(inference);
|
|
const packet = rangeDeliveryForSnapshot({ decisionReceipt: scored.decisionReceipt, candidateRange: range });
|
|
assert.ok(scored.score.candidates.some(candidate => candidate.rawScore !== undefined && candidate.signature), "raw native audit remains available");
|
|
if (!domain) {
|
|
assert.equal(inference.segment_summary, undefined);
|
|
assert.equal(inference.segment_targets, undefined);
|
|
assert.equal(inference.segment_minutes, undefined);
|
|
assert.equal(packet.segment_summary, null, "freshly scored legacy packet does not render a new product card");
|
|
assert.equal(requests.filter(endpoint => endpoint.includes("varga-segments")).length, 0);
|
|
} else {
|
|
const targets = domain === "marriage" ? ["D1", "D9"] : domain === "career" ? ["D1", "D10"] : ["D1", "D9", "D10"];
|
|
assert.deepEqual(inference.segment_targets, targets);
|
|
assert.ok(inference.segment_summary);
|
|
assert.deepEqual(packet.segment_summary?.targets, targets);
|
|
assert.equal(requests.filter(endpoint => endpoint.includes("varga-segments")).length, 1);
|
|
}
|
|
const persist = accounting.calls.find(call => call.fn === "persist_agentic_rectification_candidate_v2");
|
|
assert.ok(persist);
|
|
const savedReceipt = persist.args.p_decision_receipt as Record<string, unknown>;
|
|
assert.deepEqual(savedReceipt.inference_state, scored.decisionReceipt.inference_state);
|
|
assert.deepEqual(savedReceipt.product_delivery ?? null, productDeliveryForDomain(domain));
|
|
assert.deepEqual(packet.product_delivery, productDeliveryForDomain(domain));
|
|
const identityGolden = await buildGoldenLatest();
|
|
const reloadRaw = dossierFixture({ candidateRange: range, latestResult: { ...identityGolden.latest,
|
|
candidates: persist.args.p_candidates, representative_time: scored.score.representativeTime,
|
|
decision_receipt: savedReceipt } });
|
|
Object.assign(reloadRaw.case, { rectification_domain: domain });
|
|
const reloaded = parseV9CaseDossier(reloadRaw)!;
|
|
const decision = decideFromDossier(reloaded);
|
|
const tools = latestResultToolProjection(reloaded.latestResult as never, decision, productDeliveryForDomain(domain));
|
|
const publicDossier = dossierResponse(reloaded, [], { status: "verified", requiresSkillAdoption: false } as never,
|
|
{ liveIdentity: { algorithmVersion: identityGolden.scored.algorithmVersion, policyVersion: identityGolden.scored.policyVersion } });
|
|
const parsed = parseRectificationCandidateResult(publicDossier.latest_result);
|
|
assert.ok(parsed);
|
|
assert.deepEqual(parsed.rangeDelivery?.product_delivery, productDeliveryForDomain(domain));
|
|
assert.deepEqual(tools.product_delivery, productDeliveryForDomain(domain));
|
|
if (domain && scanFails) {
|
|
assert.equal(inference.segment_summary?.scan_complete, false);
|
|
assert.equal(inference.segment_summary?.no_rectification_needed, false);
|
|
assert.equal(inference.segment_summary?.adoption_minute, null);
|
|
assert.equal(parsed.rangeDelivery?.segment_summary?.scan_complete, false);
|
|
const report = tools.skill_verification_report as { markdown: string; segment_summary: { scan_complete: boolean } };
|
|
assert.equal(report.segment_summary.scan_complete, false);
|
|
assert.match(report.markdown, /完整窗口扫描暂不可用|完整扫描缺失/);
|
|
assert.match(parsed.verificationReportMarkdown!, /只供引擎诊断/);
|
|
assert.doesNotMatch(deliveryTurnNarration(decision) + nonConvergingRangeNarration(decision) + report.markdown,
|
|
/目前范围|最可能|眼下更像|盘型一致,不用校正/);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
test("legacy inference clears optional segment carryover without changing frozen minute convergence", async (t) => {
|
|
t.mock.method(globalThis, "fetch", async () => Response.json(golden.response));
|
|
const score = await runV9CandidateScore({ baselineBirthSnapshot: snapshot, candidateRange: range, events: req.events });
|
|
const input = { range, candidates: score.candidates, evidence: [],
|
|
probes: refinementFromDecisionReceipt(score.decisionReceipt).discriminating_event_probes };
|
|
const legacy = buildProductCaseInferenceState(input, null);
|
|
const withoutMetadata = buildCaseInferenceState({ ...input, candidates: input.candidates.map(candidate => {
|
|
const minuteCandidate = { ...candidate };
|
|
delete minuteCandidate.rawScore;
|
|
delete minuteCandidate.signature;
|
|
return minuteCandidate;
|
|
}) });
|
|
assert.deepEqual(legacy, withoutMetadata, "minute prior/posterior, holdout, stops and original probes remain unchanged");
|
|
const dirtyPrevious = { ...legacy, segment_targets: ["D1", "D9", "D10"] as const,
|
|
segment_minutes: golden.scan.minutes, segment_order_enabled: true, segment_scan_complete: true };
|
|
const restored = buildProductCaseInferenceState({ ...input, previous: dirtyPrevious }, null);
|
|
assert.equal(restored.segment_summary, undefined);
|
|
assert.equal(restored.segment_targets, undefined);
|
|
assert.equal(restored.segment_minutes, undefined);
|
|
});
|