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>
102 lines
6.8 KiB
TypeScript
102 lines
6.8 KiB
TypeScript
import assert from "node:assert/strict";
|
|
import test from "node:test";
|
|
import { buildSegmentSummary, targetChartsForDomain } from "../src/lib/rectification-agentic/core/segment-summary.ts";
|
|
import { parseCaseSegmentChecks, prepareCaseSegmentChecks } from "../src/lib/rectification-agentic/v9/segment-checks.ts";
|
|
import { finalizeSuccessfulTurnExit } from "../src/lib/rectification-agentic/v9/turn-exit.ts";
|
|
import { messagesFromTurns } from "../src/lib/rectification-chat-messages.ts";
|
|
import { mergeTurnQuestions, type SnapshotTurnMessage } from "../src/lib/rectification-snapshot-messages.ts";
|
|
import type { RectificationRpcClient } from "../src/lib/rectification-agentic/v9/tool-service.ts";
|
|
|
|
// Fictional complete-window controls, not engine calibration goldens.
|
|
const range = { start_time: "09:00", end_time: "09:20" };
|
|
const snapshot = { birth_date: "2000-06-14", latitude: 40, longitude: -74, timezone_offset: -4,
|
|
reported_birth_time: "09:10", birth_time_source: "family_exact" };
|
|
const minutes = Array.from({ length: 21 }, (_, offset) => ({ offset, date: "2000-06-14",
|
|
time: `09:${String(offset).padStart(2, "0")}`, signs: { D1: 1, D9: 2, D10: 3 } }));
|
|
function summary(domain = "general", unique = true) {
|
|
return buildSegmentSummary({ minutes: minutes.map(row => unique || row.offset < 10 ? row
|
|
: { ...row, signs: { ...row.signs, D9: 4 } }), candidates: [], targets: targetChartsForDomain(domain),
|
|
windowMinutes: 21, scanComplete: true });
|
|
}
|
|
function control(domain: string | null = "general", unique = true) {
|
|
const calls: string[] = [];
|
|
let checks: unknown = null;
|
|
let status = "draft";
|
|
let turnCount = 0;
|
|
const accounting = { rpc: async (name: string, args: Record<string, unknown>) => {
|
|
calls.push(name);
|
|
if (name === "get_agentic_rectification_case_dossier") return { error: null, data: {
|
|
case: { case_id: "synthetic-case", session_id: "synthetic-session", status,
|
|
skill_name: "jyotish-birth-time-rectification", skill_version: "10.0.32", stage: "minute",
|
|
candidate_range: range, rectification_domain: domain, checks, turn_count: turnCount },
|
|
turns: [], evidence: [], conversation_summary: {}, latest_result: null } };
|
|
if (name === "get_agentic_rectification_case_compute") return { error: null, data: {
|
|
case_id: "synthetic-case", skill_version: "10.0.32", baseline_profile_fingerprint: "a".repeat(64),
|
|
baseline_birth_snapshot: snapshot, candidate_range: range } };
|
|
if (name === "write_agentic_rectification_segment_checks_v1") {
|
|
checks = args.p_checks; return { error: null, data: checks };
|
|
}
|
|
if (name === "finalize_agentic_rectification_segment_consistency_v1") {
|
|
status = "closed"; turnCount = 1;
|
|
checks = { ...(checks as object), delivered_turn_id: "synthetic-turn" };
|
|
return { error: null, data: { turn_id: "synthetic-turn" } };
|
|
}
|
|
throw new Error(`unexpected RPC ${name}`);
|
|
} } as unknown as RectificationRpcClient;
|
|
const fetchScan = (async (_url: unknown, init?: RequestInit) => {
|
|
const body = JSON.parse(String(init?.body));
|
|
assert.deepEqual(body.target_charts, targetChartsForDomain(domain));
|
|
return Response.json({ start_at: "2000-06-14T09:00", end_at: "2000-06-14T09:20", ayanamsa: "raman",
|
|
node_mode: "mean", step_minutes: 1, window_minutes: 21,
|
|
minutes: minutes.map(row => unique || row.offset < 10 ? row : { ...row, signs: { ...row.signs, D9: 4 } }) });
|
|
}) as typeof fetch;
|
|
return { accounting, calls, fetchScan };
|
|
}
|
|
|
|
test("opening checks accept only the persisted product targets, never a D1-only opt-out", () => {
|
|
for (const domain of ["education", "career", "relationship", "relocation", "finance", "health", "family"]) {
|
|
const expected = domain === "career" ? ["D1", "D10"] : domain === "relationship" ? ["D1", "D9"] : ["D1", "D9", "D10"];
|
|
assert.deepEqual(targetChartsForDomain(domain), expected);
|
|
const checks = { contract: "segment-opening-v1", domain, range_fingerprint: "a".repeat(64), status: "complete", summary: summary(domain) };
|
|
assert.ok(parseCaseSegmentChecks(checks, domain));
|
|
assert.equal(parseCaseSegmentChecks({ ...checks, summary: buildSegmentSummary({ minutes, candidates: [], targets: ["D1"],
|
|
windowMinutes: 21, scanComplete: true }) }, domain), null);
|
|
}
|
|
assert.equal(summary("general", false).no_rectification_needed, false);
|
|
});
|
|
|
|
test("full target uniqueness exits through the shared writer before any collect focus, with read-only retry", async () => {
|
|
const c = control(); const previous = globalThis.fetch; globalThis.fetch = c.fetchScan;
|
|
try {
|
|
const input = { accounting: c.accounting, userId: "synthetic-user", caseId: "synthetic-case", action: "opening" as const, segmentOpening: true as const };
|
|
const result = await finalizeSuccessfulTurnExit(input);
|
|
assert.equal(result?.segmentConsistency, true);
|
|
assert.match(result?.text ?? "", /盘型一致,不用校正/);
|
|
const scans = c.calls.filter(name => name === "write_agentic_rectification_segment_checks_v1").length;
|
|
assert.equal((await finalizeSuccessfulTurnExit(input))?.turnId, result?.turnId);
|
|
assert.equal(c.calls.filter(name => name === "write_agentic_rectification_segment_checks_v1").length, scans);
|
|
assert.ok(!c.calls.some(name => /focus|next_interview/.test(name)));
|
|
} finally { globalThis.fetch = previous; }
|
|
});
|
|
|
|
test("a nonunique target persists checks but does not claim no rectification; legacy does not scan", async () => {
|
|
const c = control("general", false); const previous = globalThis.fetch; globalThis.fetch = c.fetchScan;
|
|
try {
|
|
const checks = await prepareCaseSegmentChecks({ accounting: c.accounting, userId: "synthetic-user", caseId: "synthetic-case" });
|
|
assert.equal(checks?.summary?.no_rectification_needed, false);
|
|
assert.equal(await finalizeSuccessfulTurnExit({ accounting: c.accounting, userId: "synthetic-user", caseId: "synthetic-case", action: "opening", segmentOpening: true }), undefined);
|
|
assert.ok(!c.calls.includes("finalize_agentic_rectification_segment_consistency_v1"));
|
|
const legacy = control(null);
|
|
assert.equal(await prepareCaseSegmentChecks({ accounting: legacy.accounting, userId: "synthetic-user", caseId: "synthetic-case" }), null);
|
|
assert.deepEqual(legacy.calls, ["get_agentic_rectification_case_dossier"]);
|
|
} finally { globalThis.fetch = previous; }
|
|
});
|
|
|
|
test("consistency card hydrates onto the same server turn live and after reload", () => {
|
|
const value = summary();
|
|
const turns = [{ id: "synthetic-turn", role: "assistant" as const, text: "盘型一致,不用校正。", status: "completed", segment_consistency: value }];
|
|
const messages = messagesFromTurns(turns);
|
|
assert.deepEqual(messages[0]?.segmentConsistency, value);
|
|
assert.deepEqual(mergeTurnQuestions<SnapshotTurnMessage>([{ role: "assistant", renderKey: "live", turnId: "synthetic-turn", text: "盘型一致,不用校正。" }], turns)[0]?.segmentConsistency, value);
|
|
});
|