Files
Jyotisha/frontend/tests/rectification-window-cluster-cap-20260909.test.ts
T
Jesse_ChenandClaude Opus 5.5 4e6e8d87ef feat(rectification): 少问几道就出卡,交付卡以范围为主(D1–D4)
- D1 引导窗口题每个校正最多 2 道(前端 GUIDED_WINDOW_CASE_LIMIT;引擎
  GUIDED_COLLECT_LIMIT 未动:event_probes.py 属冻结评分身份,改它需重新冻结)
- D2 七条定向线与跳过线重问问完即出卡,没问到的引导窗口不再挡卡,出卡后也不再挂窗口题
- D3 卡头加副标题「最可能 HH:MM」
- D4 前两列相差 ≥5 个百分点才显示相对可能性,否则一句「这几个时刻目前区分不开……」
- 离线回放 scripts/research/fewer_probes_card_replay.py:真值不降、宽度中位 ±1 分钟、提问 11.4→7.8
- Skill 10.0.29 → 10.0.30;DESIGN / VOICE / CHANGELOG / PROGRESS / 真机清单

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017eEAG8HD3mm8gsKXgk8uU8
2026-09-26 14:27:12 +08:00

98 lines
4.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import assert from "node:assert/strict";
import { readFileSync } from "node:fs";
import test from "node:test";
import { buildInferenceState } from "../src/lib/rectification-agentic/core/build-state.ts";
import { unionStillValidRange } from "../src/lib/rectification-agentic/core/credible-range.ts";
import { RECTIFICATION_USER_COPY } from "../src/lib/rectification-agentic/user-copy.ts";
import { MACHINE_VOICE_LEXICON } from "../src/lib/rectification-agentic/v9/agent-voice-lexicon.ts";
import { stripVerbalWindowChange } from "../src/lib/rectification-agentic/v9/collect-prompt.ts";
import { parseDeclaredBirthWindow } from "../src/lib/rectification-agentic/v9/declared-window-utterance.ts";
import { RECTIFICATION_SKILL_VERSION } from "../src/lib/rectification-agentic/v9/case-status.ts";
const ROUTE = readFileSync(
new URL("../src/app/api/rectification/agent/route.ts", import.meta.url),
"utf8",
);
const SKILL = readFileSync(
new URL("../../skills/jyotish-birth-time-rectification/SKILL.md", import.meta.url),
"utf8",
);
test("declared birth-window utterances parse ranges and around-times", () => {
assert.deepEqual(
parseDeclaredBirthWindow("我的出生时间是 14 点 45 到 14 点 50"),
{ kind: "range", start: "14:45", end: "14:50" },
);
assert.deepEqual(
parseDeclaredBirthWindow("14:45–14:50"),
{ kind: "range", start: "14:45", end: "14:50" },
);
assert.deepEqual(
parseDeclaredBirthWindow("14:47 左右"),
{ kind: "around", time: "14:47" },
);
assert.equal(parseDeclaredBirthWindow("没有"), null);
assert.equal(parseDeclaredBirthWindow("2014年入学,大概秋天"), null);
assert.equal(parseDeclaredBirthWindow("继续吧"), null);
assert.equal(parseDeclaredBirthWindow("2024 年 8 月 8 日 20:00 左右分手"), null);
assert.equal(parseDeclaredBirthWindow("下午 3 点到 5 点被车撞"), null);
assert.equal(parseDeclaredBirthWindow("2019 年 10 月 23 日 9 点半入职"), null);
});
test("choice-focus declared window is answered from the route before the model", () => {
const intercept = ROUTE.indexOf('if (action === "message" && parseDeclaredBirthWindow');
const selectedModel = ROUTE.indexOf("const selectedModel = isStructuredChoice");
// 原值: await classifyRectificationTurnIntent
// 新值: await classifyTurnIntentWithRetry(点选题与 discriminator 同一入口)
// 原因: BUG-722 分类器失败不得再走「用户说不清」
const classifyCall = ROUTE.indexOf("await classifyTurnIntentWithRetry");
assert.ok(intercept > 0);
assert.ok(selectedModel > intercept);
assert.ok(classifyCall > intercept);
assert.match(ROUTE, /declaredWindowLockedReply/);
assert.match(ROUTE, /persistV9DeterministicTurn/);
assert.equal(
RECTIFICATION_USER_COPY.declaredWindowLockedReply,
"搜索范围是开始时按你的资料定的,校正过程中不改。想按别的时间段重来,请先到资料里改出生时间,再新建一次校正。",
);
});
test("engine cluster coverage becomes the credible-range right edge", () => {
const state = buildInferenceState({
range_start: "14:00",
range_end: "15:00",
candidates: [{
id: "14:40",
time: "14:40",
relative_support: 40,
cluster_times: ["14:40", "14:41", "14:42", "14:43", "14:44", "14:45"],
cluster_start: "14:40",
cluster_end: "14:45",
}],
events: [],
probes: [],
});
assert.deepEqual(state.candidates[0]?.cluster_range, ["14:40", "14:45"]);
assert.deepEqual(unionStillValidRange(state.candidates), ["14:40", "14:45"]);
});
test("agent body cannot verbally accept a spoken birth window", () => {
assert.ok(MACHINE_VOICE_LEXICON.includes("以你说的为准"));
assert.equal(
stripVerbalWindowChange("明白了,出生时间以你说的 14:45–14:50 为准。"),
"明白了,出生时间。",
);
assert.equal(stripVerbalWindowChange("以你说的为准。"), "");
assert.equal(stripVerbalWindowChange("明白了,以你说的为准。"), "明白了。");
assert.match(SKILL, /不得回答『以你说的为准』或改写搜索窗口/);
// 原值: "10.0.25"
// 原值: "10.0.26"
// 新值: "10.0.27"
// 原因: 出卡精度门槛 + 引导式补经历
// 原因: BUG-668 定向补事第四选项写进 Skill
// 原值: "10.0.28" / 新值: "10.0.29" / 原因: 年月阶段改口述并禁止追问本人
// 原值: "10.0.29" / 新值: "10.0.30" / 原因: D2 出卡句与 D4 卡上百分比规则写进 Skill(2026-09-26)
assert.equal(RECTIFICATION_SKILL_VERSION, "10.0.30");
});