import assert from "node:assert/strict"; import test from "node:test"; import { readFileSync } from "node:fs"; import { parseWindowScanTransitions } from "../src/lib/rectification-agentic/v9/refinement-packet.ts"; import { signFromTransitions } from "../src/lib/rectification-agentic/core/sign-from-transitions.ts"; import { remainingLayerGroups } from "../src/lib/rectification-agentic/core/candidate-contrast-packet.ts"; import { remainingSplitLayers } from "../src/lib/rectification-agentic/v9/collection-question-pool.ts"; test("native dated scan preserves every candidate's actual sign across midnight", () => { const fixture = JSON.parse(readFileSync(new URL("./fixtures/rectification-dated-transitions.native.json", import.meta.url), "utf8")) as { scan: { transitions: readonly Record[] }; expected_signs: Record>; }; const parsed = parseWindowScanTransitions(fixture.scan.transitions); assert.equal(parsed.length, fixture.scan.transitions.length); assert.ok(parsed.some((row) => row.segment_start)); for (const [time, signs] of Object.entries(fixture.expected_signs)) { for (const [layer, sign] of Object.entries(signs)) { assert.equal(signFromTransitions(parsed, layer, time), sign, `${layer} at ${time}`); } } assert.equal(signFromTransitions(parsed, "d9", "12:00"), null); }); // Synthetic lookup arithmetic, not a fabricated engine response/golden. const bounds = { segment_start_at: "2000-06-14T23:58", segment_end_at: "2000-06-15T00:01" }; const raw = [ { layer: "d9", at: "23:58", candidate_date: "2000-06-14", window_index: 0, window_offset_minutes: 0, segment_index: 0, segment_start: true, from_sign: "白羊", to_sign: "白羊", ...bounds }, { layer: "d9", at: "23:59", candidate_date: "2000-06-14", window_index: 1, window_offset_minutes: 1, segment_index: 0, from_sign: "白羊", to_sign: "金牛", ...bounds }, { layer: "d9", at: "00:00", candidate_date: "2000-06-15", window_index: 2, window_offset_minutes: 2, segment_index: 0, from_sign: "金牛", to_sign: "双子", ...bounds }, ]; test("dated transition parser retains civil order and midnight sign lookup", () => { const parsed = parseWindowScanTransitions(raw); assert.equal(signFromTransitions(parsed, "d9", "23:58"), "白羊"); assert.equal(signFromTransitions(parsed, "d9", "23:59"), "金牛"); assert.equal(signFromTransitions(parsed, "d9", "00:00"), "双子"); assert.equal(signFromTransitions(parsed, "d9", "00:01"), "双子"); assert.equal(signFromTransitions(parsed, "d9", "12:00"), null); assert.deepEqual(remainingLayerGroups(["00:01", "23:59", "00:00", "23:58"], parsed, "d9"), [["23:58"], ["23:59"], ["00:00", "00:01"]]); }); test("declared segment gap is not inferred as a transition or a covered minute", () => { const parsed = parseWindowScanTransitions([ { ...raw[0], at: "00:00", candidate_date: "2000-06-15", segment_start_at: "2000-06-15T00:00", segment_end_at: "2000-06-15T00:01" }, { ...raw[0], at: "23:00", candidate_date: "2000-06-15", window_index: 2, window_offset_minutes: 1380, segment_index: 1, from_sign: "处女", to_sign: "处女", segment_start_at: "2000-06-15T23:00", segment_end_at: "2000-06-15T23:01" }, ]); assert.equal(signFromTransitions(parsed, "d9", "00:01"), "白羊"); assert.equal(signFromTransitions(parsed, "d9", "23:01"), "处女"); assert.equal(signFromTransitions(parsed, "d9", "12:00"), null); assert.deepEqual(remainingLayerGroups(["23:01", "00:01", "12:00"], parsed, "d9"), [["00:01"], ["23:01"]]); }); test("malformed or partial dated transition contract fails closed", () => { for (const patch of [{ candidate_date: "2000-02-30" }, { window_index: -1 }, { segment_end_at: null }, { candidate_date: undefined }, { at: "24:00" }, { layer: "unknown" }]) { assert.deepEqual(parseWindowScanTransitions([{ ...raw[0], ...patch }]), []); assert.deepEqual(parseWindowScanTransitions([raw[0], { ...raw[1], ...patch }]), []); } }); test("dated transitions reject inconsistent offsets, missing starts and mixed historical rows", () => { assert.deepEqual(parseWindowScanTransitions(raw.slice(1)), []); assert.deepEqual(parseWindowScanTransitions([raw[0], { ...raw[1], window_offset_minutes: 2 }]), []); assert.deepEqual(parseWindowScanTransitions([raw[0], { ...raw[1], window_index: 0 }]), []); assert.deepEqual(parseWindowScanTransitions([raw[0], { layer: "d9", at: "00:00", from_sign: "金牛", to_sign: "双子" }]), []); }); test("same-day dated changes keep legacy sign assignments and grouping", () => { const dated = parseWindowScanTransitions(raw.map((row, i) => ({ ...row, at: `10:0${i}`, candidate_date: "2000-06-15", segment_start_at: "2000-06-15T10:00", segment_end_at: "2000-06-15T10:03" }))); const legacy = parseWindowScanTransitions(dated.filter((row) => !row.segment_start).map((row) => ({ layer: row.layer, at: row.at, from_sign: row.from_sign, to_sign: row.to_sign }))); const times = ["10:00", "10:01", "10:02", "10:03"]; assert.deepEqual(times.map((time) => signFromTransitions(dated, "d9", time)), times.map((time) => signFromTransitions(legacy, "d9", time))); assert.deepEqual(remainingLayerGroups(times, dated, "d9"), remainingLayerGroups(times, legacy, "d9")); }); test("segment initial states do not trigger a remaining-layer question", () => { const markers = parseWindowScanTransitions([raw[0]]); assert.deepEqual(remainingSplitLayers({ transitions: markers, activeTimes: ["23:58", "00:01"] }), []); assert.deepEqual(remainingSplitLayers({ transitions: parseWindowScanTransitions(raw), activeTimes: ["23:58", "00:01"] }), ["d9"]); }); test("legacy transitions retain old lookup and grouping", () => { const parsed = parseWindowScanTransitions([{ layer: "d9", at: "10:01", from_sign: "白羊", to_sign: "金牛" }]); assert.equal(signFromTransitions(parsed, "d9", "10:00"), "白羊"); assert.equal(signFromTransitions(parsed, "d9", "10:02"), "金牛"); assert.deepEqual(remainingLayerGroups(["10:00", "10:01", "10:02"], parsed, "d9"), [["10:00"], ["10:01", "10:02"]]); assert.equal("candidate_date" in parsed[0]!, false); });