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
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
7ddce2c96a
commit
4e6e8d87ef
@@ -465,7 +465,8 @@ test("skill version is 10.0.26 after the targeted-collect-cards bump", () => {
|
||||
// 原因: 出卡精度门槛 + 引导式补经历
|
||||
// 原因: BUG-668 定向补事第四选项写进 Skill
|
||||
// 原值: "10.0.28" / 新值: "10.0.29" / 原因: 年月阶段改口述并禁止追问本人
|
||||
assert.equal(RECTIFICATION_SKILL_VERSION, "10.0.29");
|
||||
// 原值: "10.0.29" / 新值: "10.0.30" / 原因: D2 出卡句与 D4 卡上百分比规则写进 Skill(2026-09-26)
|
||||
assert.equal(RECTIFICATION_SKILL_VERSION, "10.0.30");
|
||||
});
|
||||
|
||||
test("revision 5 with uncovered relatives asks the dated family collect, not a yearless D12 card", () => {
|
||||
|
||||
@@ -382,7 +382,8 @@ test("holdout not_ready forbids unique-minute copy and still blocks confirm", as
|
||||
// 原因: 出卡精度门槛 + 引导式补经历
|
||||
// 原因: BUG-668 定向补事第四选项写进 Skill
|
||||
// 原值: "10.0.28" / 新值: "10.0.29" / 原因: 年月阶段改口述并禁止追问本人
|
||||
assert.equal(RECTIFICATION_SKILL_VERSION, "10.0.29");
|
||||
// 原值: "10.0.29" / 新值: "10.0.30" / 原因: D2 出卡句与 D4 卡上百分比规则写进 Skill(2026-09-26)
|
||||
assert.equal(RECTIFICATION_SKILL_VERSION, "10.0.30");
|
||||
|
||||
const accounting = fakeAccounting({
|
||||
...receiptHandlers,
|
||||
|
||||
@@ -259,14 +259,16 @@ test("skill 10.0.26 forbids computing varga signs from transition times", () =>
|
||||
// 原因: 出卡精度门槛 + 引导式补经历
|
||||
// 原因: BUG-668 定向补事第四选项写进 Skill
|
||||
// 原值: "10.0.28" / 新值: "10.0.29" / 原因: 年月阶段改口述并禁止追问本人
|
||||
assert.equal(RECTIFICATION_SKILL_VERSION, "10.0.29");
|
||||
// 原值: "10.0.29" / 新值: "10.0.30" / 原因: D2 出卡句与 D4 卡上百分比规则写进 Skill(2026-09-26)
|
||||
assert.equal(RECTIFICATION_SKILL_VERSION, "10.0.30");
|
||||
// 原值: "10.0.25"
|
||||
// 原值: "10.0.26"
|
||||
// 新值: "10.0.27"
|
||||
// 原因: 出卡精度门槛 + 引导式补经历
|
||||
// 原因: BUG-668 定向补事第四选项写进 Skill
|
||||
// 原值: /^version: 10\.0\.28$/ / 新值: /^version: 10\.0\.29$/ / 原因: 年月阶段改口述并禁止追问本人
|
||||
assert.match(skill, /^version: 10\.0\.29$/m);
|
||||
// 原值: /^version: 10\.0\.29$/ / 新值: /^version: 10\.0\.30$/ / 原因: D2 出卡句与 D4 卡上百分比规则写进 Skill(2026-09-26)
|
||||
assert.match(skill, /^version: 10\.0\.30$/m);
|
||||
assert.match(skill, new RegExp(SKILL_SIGN_SENTENCE.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")));
|
||||
assert.match(comparison, new RegExp(SKILL_SIGN_SENTENCE.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")));
|
||||
});
|
||||
|
||||
@@ -1456,7 +1456,8 @@ test("public tool surface stays at 14 and new cases bind 10.0.26", () => {
|
||||
// 原因: 出卡精度门槛 + 引导式补经历
|
||||
// 原因: BUG-668 定向补事第四选项写进 Skill
|
||||
// 原值: "10.0.28" / 新值: "10.0.29" / 原因: 年月阶段改口述并禁止追问本人
|
||||
assert.equal(RECTIFICATION_SKILL_VERSION, "10.0.29");
|
||||
// 原值: "10.0.29" / 新值: "10.0.30" / 原因: D2 出卡句与 D4 卡上百分比规则写进 Skill(2026-09-26)
|
||||
assert.equal(RECTIFICATION_SKILL_VERSION, "10.0.30");
|
||||
const deprecated = resolveExactSkillPackage(
|
||||
"jyotish-birth-time-rectification",
|
||||
"10.0.2",
|
||||
|
||||
@@ -603,7 +603,8 @@ test("skill version is 10.0.26 after the targeted-collect-cards bump", () => {
|
||||
// 原因: 出卡精度门槛 + 引导式补经历
|
||||
// 原因: BUG-668 定向补事第四选项写进 Skill
|
||||
// 原值: "10.0.28" / 新值: "10.0.29" / 原因: 年月阶段改口述并禁止追问本人
|
||||
assert.equal(RECTIFICATION_SKILL_VERSION, "10.0.29");
|
||||
// 原值: "10.0.29" / 新值: "10.0.30" / 原因: D2 出卡句与 D4 卡上百分比规则写进 Skill(2026-09-26)
|
||||
assert.equal(RECTIFICATION_SKILL_VERSION, "10.0.30");
|
||||
});
|
||||
|
||||
test("USER_COLLECT_QUESTION no longer has an other fallback", () => {
|
||||
|
||||
@@ -0,0 +1,301 @@
|
||||
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 { INFERENCE_ALGORITHM_VERSION, type InferenceCandidate, type InferenceState } from "../src/lib/rectification-agentic/core/types.ts";
|
||||
import { buildRangeDelivery } from "../src/lib/rectification-agentic/v9/divergence-panel.ts";
|
||||
import {
|
||||
RANGE_DELIVERY_PERCENT_MIN_GAP,
|
||||
RECTIFICATION_USER_COPY,
|
||||
listUserVisibleCopy,
|
||||
rangeDeliveryMostLikely,
|
||||
rangeDeliveryShowsPercents,
|
||||
} from "../src/lib/rectification-agentic/user-copy.ts";
|
||||
import {
|
||||
GUIDED_WINDOW_CASE_LIMIT,
|
||||
cardHoldingLinesExhausted,
|
||||
guidedCollectExhausted,
|
||||
guidedWindowPool,
|
||||
type CollectionEvidence,
|
||||
type GuidedCollectWindow,
|
||||
} from "../src/lib/rectification-agentic/v9/collection-question-pool.ts";
|
||||
import { buildMethodFollowupPlan, targetedCollectFollowup } from "../src/lib/rectification-agentic/v9/method-followup.ts";
|
||||
import { decideRectification } from "../src/lib/rectification-agentic/core/rectification-decision.ts";
|
||||
import { RectificationRangeDelivery } from "../src/components/rectification-range-delivery.tsx";
|
||||
import type { RectificationCandidateResult } from "../src/lib/rectification-candidate-result.ts";
|
||||
import { OPEN_ENGINE_CAPABILITY_CEILING } from "./rectification-v9-test-support.ts";
|
||||
|
||||
// Fictional data only. Three columns inside 05:00–05:15.
|
||||
|
||||
function cand(time: string, probability: number): InferenceCandidate {
|
||||
return {
|
||||
id: time,
|
||||
time,
|
||||
cluster_range: [time, time],
|
||||
prior_score: probability * 100,
|
||||
posterior_score: probability * 100,
|
||||
probability,
|
||||
status: "active",
|
||||
rank: 1,
|
||||
strong_conflict_count: 0,
|
||||
};
|
||||
}
|
||||
|
||||
function inference(probabilities: readonly [number, number, number]): InferenceState {
|
||||
return {
|
||||
algorithm_version: INFERENCE_ALGORITHM_VERSION,
|
||||
candidate_set_id: "05:00-05:15:05:02,05:07,05:13",
|
||||
revision: 1,
|
||||
phase: "discrimination",
|
||||
result_status: "credible_range",
|
||||
range_start: "05:00",
|
||||
range_end: "05:15",
|
||||
candidates: [
|
||||
cand("05:07", probabilities[0]),
|
||||
cand("05:02", probabilities[1]),
|
||||
cand("05:13", probabilities[2]),
|
||||
],
|
||||
events: [
|
||||
{ id: "e1", domain: "career", year: 2018, precision: "month", usage: "training" },
|
||||
{ id: "e2", domain: "education", year: 2012, precision: "month", usage: "training" },
|
||||
],
|
||||
probes: [],
|
||||
answered_probes: [],
|
||||
rounds: [],
|
||||
entropy: 1,
|
||||
representative_time: "05:07",
|
||||
credible_range: ["05:00", "05:15"],
|
||||
};
|
||||
}
|
||||
|
||||
const PUBLIC = [
|
||||
{ candidateId: "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaa1", time: "05:07" },
|
||||
{ candidateId: "bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbb2", time: "05:02" },
|
||||
{ candidateId: "cccccccc-cccc-4ccc-8ccc-ccccccccccc3", time: "05:13" },
|
||||
];
|
||||
|
||||
function result(probabilities: readonly [number, number, number]): RectificationCandidateResult {
|
||||
const delivery = buildRangeDelivery({
|
||||
inference: inference(probabilities),
|
||||
publicCandidates: PUBLIC,
|
||||
credibleRange: ["05:00", "05:15"],
|
||||
representativeTime: "05:07",
|
||||
});
|
||||
return {
|
||||
resultId: "result-1",
|
||||
candidates: delivery.columns.map((column, index) => ({
|
||||
candidateId: column.candidate_id,
|
||||
rank: index + 1,
|
||||
time: column.time,
|
||||
relativeSupport: 10,
|
||||
tiedMinuteCount: 1,
|
||||
})),
|
||||
overallConfidence: "medium",
|
||||
selectionAllowed: true,
|
||||
canAdopt: true,
|
||||
confirmationAllowed: false,
|
||||
decisionReceipt: null,
|
||||
representativeTime: delivery.representative_time,
|
||||
selectedTime: null,
|
||||
selectionKind: null,
|
||||
houseTable: null,
|
||||
houseTablesByTime: {},
|
||||
natalRecast: null,
|
||||
techniqueAudit: [],
|
||||
windowTransitions: [],
|
||||
eventDashaLedger: [],
|
||||
dashaAgreement: null,
|
||||
lagnaContrast: null,
|
||||
nakshatraBoundary: null,
|
||||
precisionStage: null,
|
||||
oosBlindPrompts: [],
|
||||
confirmationGate: { confirmation_allowed: false } as RectificationCandidateResult["confirmationGate"],
|
||||
validated: false,
|
||||
completionStatus: null,
|
||||
sessionOutcome: "adopt_representative",
|
||||
credibleRange: delivery.range,
|
||||
rangeDelivery: delivery,
|
||||
verificationReportMarkdown: null,
|
||||
};
|
||||
}
|
||||
|
||||
function render(probabilities: readonly [number, number, number]): string {
|
||||
return renderToStaticMarkup(createElement(RectificationRangeDelivery, {
|
||||
result: result(probabilities),
|
||||
acceptingCandidateId: null,
|
||||
readonly: false,
|
||||
onAccept: () => undefined,
|
||||
}));
|
||||
}
|
||||
|
||||
test("D4 threshold is 5 points between the first and second column", () => {
|
||||
assert.equal(RANGE_DELIVERY_PERCENT_MIN_GAP, 5);
|
||||
assert.equal(rangeDeliveryShowsPercents([{ probability_percent: 40 }, { probability_percent: 35 }]), true);
|
||||
assert.equal(rangeDeliveryShowsPercents([{ probability_percent: 30 }, { probability_percent: 26 }]), false);
|
||||
assert.equal(rangeDeliveryShowsPercents([
|
||||
{ probability_percent: 26 },
|
||||
{ probability_percent: 26 },
|
||||
{ probability_percent: 20 },
|
||||
]), false);
|
||||
// Order-independent: the gap is first − second after ranking.
|
||||
assert.equal(rangeDeliveryShowsPercents([{ probability_percent: 30 }, { probability_percent: 45 }]), true);
|
||||
// One column has no second place to be clearly ahead of.
|
||||
assert.equal(rangeDeliveryShowsPercents([{ probability_percent: 100 }]), false);
|
||||
});
|
||||
|
||||
test("D3 range is the title and the representative minute is the subtitle", () => {
|
||||
const html = render([0.45, 0.3, 0.25]);
|
||||
assert.match(html, /<strong>目前范围 05:00–05:15(对照了 2 件经历)<\/strong>/);
|
||||
assert.match(html, /rectification-range-delivery__most-likely">最可能 05:07</);
|
||||
assert.ok(html.indexOf("最可能 05:07") < html.indexOf("rectification-range-delivery__columns"));
|
||||
assert.equal(rangeDeliveryMostLikely("05:07"), "最可能 05:07");
|
||||
});
|
||||
|
||||
test("D4 gap ≥ 5 shows every column's percentage and no indistinct line", () => {
|
||||
const html = render([0.45, 0.3, 0.25]);
|
||||
assert.match(html, /相对可能性 45%/);
|
||||
assert.match(html, /相对可能性 30%/);
|
||||
assert.match(html, /相对可能性 25%/);
|
||||
assert.doesNotMatch(html, new RegExp(RECTIFICATION_USER_COPY.rangeDeliveryIndistinct));
|
||||
});
|
||||
|
||||
test("D4 gap < 5 shows no numbers and one indistinct sentence; columns and order unchanged", () => {
|
||||
const html = render([0.35, 0.33, 0.32]);
|
||||
assert.doesNotMatch(html, /相对可能性/);
|
||||
assert.doesNotMatch(html, /\d+%/);
|
||||
assert.equal(html.split(RECTIFICATION_USER_COPY.rangeDeliveryIndistinct).length - 1, 1);
|
||||
assert.equal(
|
||||
RECTIFICATION_USER_COPY.rangeDeliveryIndistinct,
|
||||
"这几个时刻目前区分不开,补一件带年月的经历能帮助分开。",
|
||||
);
|
||||
assert.equal([...html.matchAll(/<button\b/g)].length, 3);
|
||||
const order = ["05:07", "05:02", "05:13"].map((time) => html.indexOf(`__time">${time}`));
|
||||
assert.deepEqual([...order].sort((left, right) => left - right), order);
|
||||
assert.ok(order.every((index) => index > 0));
|
||||
// The subtitle stays: it names the representative minute, not a confidence.
|
||||
assert.match(html, /最可能 05:07/);
|
||||
});
|
||||
|
||||
test("D3/D4 copy is registered as user-visible copy", () => {
|
||||
const visible = listUserVisibleCopy();
|
||||
assert.ok(visible.includes(RECTIFICATION_USER_COPY.rangeDeliveryIndistinct));
|
||||
assert.ok(visible.includes(rangeDeliveryMostLikely("05:07")));
|
||||
const voice = readFileSync(new URL("../docs/VOICE.md", import.meta.url), "utf8");
|
||||
assert.ok(voice.includes(RECTIFICATION_USER_COPY.rangeDeliveryIndistinct));
|
||||
assert.ok(voice.includes("最可能 HH:MM"));
|
||||
});
|
||||
|
||||
const FRESH_WINDOWS: readonly GuidedCollectWindow[] = [
|
||||
{ year: 2019, month_lo: 4, month_hi: 6, domain: "any", split: { left: 2, right: 1 } },
|
||||
{ year: 2015, month_lo: 9, month_hi: 9, domain: "any", split: { left: 1, right: 2 } },
|
||||
];
|
||||
|
||||
function askedWindow(year: number, lo: number, hi: number, status = "declined") {
|
||||
return {
|
||||
questionId: `collect:guided:window:${year}:${lo}:${hi}:any`,
|
||||
target_domain: "any",
|
||||
status,
|
||||
intent: "collect_method_evidence",
|
||||
target_kind: "guided:window:any",
|
||||
};
|
||||
}
|
||||
|
||||
test("D1 at most two guided windows per Case even when a new receipt brings fresh ones", () => {
|
||||
assert.equal(GUIDED_WINDOW_CASE_LIMIT, 2);
|
||||
const oneAsked = [askedWindow(2021, 1, 3)];
|
||||
assert.equal(guidedWindowPool(FRESH_WINDOWS, [], oneAsked).length, 2);
|
||||
const twoAsked = [askedWindow(2021, 1, 3), askedWindow(2017, 5, 5)];
|
||||
assert.equal(guidedWindowPool(FRESH_WINDOWS, [], twoAsked).length, 0);
|
||||
// The same window re-asked under `:next` is still one window.
|
||||
const sameTwice = [askedWindow(2021, 1, 3), { ...askedWindow(2021, 1, 3), questionId: "collect:guided:window:2021:1:3:any:next" }];
|
||||
assert.equal(guidedWindowPool(FRESH_WINDOWS, [], sameTwice).length, 2);
|
||||
// An active (being asked) window does not count as asked yet.
|
||||
assert.equal(guidedWindowPool(FRESH_WINDOWS, [], [askedWindow(2021, 1, 3), askedWindow(2017, 5, 5, "active")]).length, 2);
|
||||
// With the cap reached the next question falls through to the targeted lines.
|
||||
const followup = targetedCollectFollowup(["d9"], [], twoAsked, ["05:00", "05:15"], 3, ["05:00", "05:15"], FRESH_WINDOWS);
|
||||
assert.ok(followup?.collection_key?.startsWith("collect:targeted:"), followup?.collection_key);
|
||||
});
|
||||
|
||||
const ALL_SEVEN: CollectionEvidence[] = [
|
||||
{ status: "confirmed", domain: "education", datePrecision: "month", occurredFrom: "2012-09-01", occurredTo: "2012-09-01" },
|
||||
{ status: "confirmed", domain: "career", datePrecision: "month", occurredFrom: "2018-07-01", occurredTo: "2018-07-01" },
|
||||
{ status: "confirmed", domain: "relocation", datePrecision: "month", occurredFrom: "2016-08-01", occurredTo: "2016-08-01" },
|
||||
{ status: "confirmed", domain: "relationship", datePrecision: "month", occurredFrom: "2021-08-01", occurredTo: "2021-08-01" },
|
||||
{ status: "confirmed", domain: "family", datePrecision: "month", occurredFrom: "2019-01-01", occurredTo: "2019-01-01" },
|
||||
{ status: "confirmed", domain: "finance", datePrecision: "month", occurredFrom: "2020-03-01", occurredTo: "2020-03-01" },
|
||||
{ status: "confirmed", domain: "health_pressure", datePrecision: "month", occurredFrom: "2022-04-01", occurredTo: "2022-04-01" },
|
||||
];
|
||||
const LAYERS = ["d9", "d10", "d4", "d5", "d7", "d2", "d30"];
|
||||
|
||||
test("D2 unasked guided windows no longer hold the card once the targeted seven are asked", () => {
|
||||
// The whole guided pool still reports a window left…
|
||||
assert.equal(guidedCollectExhausted(LAYERS, ALL_SEVEN, [], FRESH_WINDOWS, ["05:00", "05:15"], 3), false);
|
||||
// …but the lines that may hold the card are asked out.
|
||||
assert.equal(cardHoldingLinesExhausted(LAYERS, ALL_SEVEN, [], ["05:00", "05:15"], 3), true);
|
||||
});
|
||||
|
||||
test("D2 the skip re-ask and a pending year answer still hold the card", () => {
|
||||
const skipped = [{
|
||||
questionId: "collect:targeted:family",
|
||||
target_domain: "family",
|
||||
status: "skipped",
|
||||
intent: "collect_method_evidence",
|
||||
target_kind: "targeted:family",
|
||||
}];
|
||||
const withoutFamily = ALL_SEVEN.filter((row) => row.domain !== "family");
|
||||
assert.equal(cardHoldingLinesExhausted(LAYERS, withoutFamily, skipped, ["05:00", "05:15"], 3), false);
|
||||
const yesOnWindow = [askedWindow(2019, 4, 6, "resolved")];
|
||||
assert.equal(cardHoldingLinesExhausted(LAYERS, ALL_SEVEN, yesOnWindow, ["05:00", "05:15"], 3), false);
|
||||
// Targeted lines still open: keep asking.
|
||||
assert.equal(cardHoldingLinesExhausted(LAYERS, ALL_SEVEN.slice(0, 3), [], ["05:00", "05:15"], 3), false);
|
||||
});
|
||||
|
||||
test("D2 gate unmet with every card-holding line asked delivers; D5 range rules unchanged", () => {
|
||||
const decision = decideRectification({
|
||||
engineCeiling: OPEN_ENGINE_CAPABILITY_CEILING,
|
||||
methodCoverageAll: true,
|
||||
trainingGateOpen: true,
|
||||
candidateScores: [
|
||||
{ time: "05:02", score: 13 },
|
||||
{ time: "05:07", score: 13.4 },
|
||||
{ time: "05:13", score: 13.1 },
|
||||
],
|
||||
holdoutValidation: "unavailable",
|
||||
datedEventCount: 7,
|
||||
datedDomainCount: 7,
|
||||
discriminatorProbe: null,
|
||||
targetedCollectExhausted: true,
|
||||
refreshExhausted: true,
|
||||
inferenceCredibleRange: ["05:00", "05:15"],
|
||||
openingCandidateRange: ["04:45", "05:15"],
|
||||
precisionGateMet: false,
|
||||
guidedCollectExhausted: cardHoldingLinesExhausted(LAYERS, ALL_SEVEN, [], ["05:00", "05:15"], 3),
|
||||
});
|
||||
assert.equal(decision.canOfferRange, true);
|
||||
assert.deepEqual(decision.credibleRange, ["05:00", "05:15"]);
|
||||
assert.equal(decision.precisionGateMet, false);
|
||||
assert.equal(decision.canConfirmExactMinute, false);
|
||||
});
|
||||
|
||||
test("D2 a delivered card does not carry an unasked guided window as the next question", () => {
|
||||
const base = {
|
||||
evidence: ALL_SEVEN,
|
||||
declinedTopics: [],
|
||||
candidatesSeparated: false,
|
||||
remainingLayers: LAYERS,
|
||||
remainingSplitTimes: ["05:00", "05:15"] as const,
|
||||
remainingCandidateCount: 3,
|
||||
remainingCredibleRange: ["05:00", "05:15"] as const,
|
||||
guidedWindows: FRESH_WINDOWS,
|
||||
};
|
||||
for (const sessionOutcome of ["completed_with_range", "provisional_range", "adopt_representative"] as const) {
|
||||
const plan = buildMethodFollowupPlan({ ...base, sessionOutcome });
|
||||
const key = plan.next_followup?.collection_key ?? "";
|
||||
assert.equal(key.startsWith("collect:guided:window:"), false, `${sessionOutcome}: ${key}`);
|
||||
}
|
||||
// While still collecting, the same windows are asked (at most two per Case).
|
||||
const collecting = buildMethodFollowupPlan({ ...base, sessionOutcome: "collect_evidence" });
|
||||
assert.match(collecting.next_followup?.collection_key ?? "", /^collect:guided:window:/);
|
||||
});
|
||||
@@ -220,14 +220,16 @@ test("new-case skill identity is 10.0.26 and the prompt prefers batch ingest", (
|
||||
// 原因: 出卡精度门槛 + 引导式补经历
|
||||
// 原因: BUG-668 定向补事第四选项写进 Skill
|
||||
// 原值: "10.0.28" / 新值: "10.0.29" / 原因: 年月阶段改口述并禁止追问本人
|
||||
assert.equal(RECTIFICATION_SKILL_VERSION, "10.0.29");
|
||||
// 原值: "10.0.29" / 新值: "10.0.30" / 原因: D2 出卡句与 D4 卡上百分比规则写进 Skill(2026-09-26)
|
||||
assert.equal(RECTIFICATION_SKILL_VERSION, "10.0.30");
|
||||
// 原值: "10.0.25"
|
||||
// 原值: "10.0.26"
|
||||
// 新值: "10.0.27"
|
||||
// 原因: 出卡精度门槛 + 引导式补经历
|
||||
// 原因: BUG-668 定向补事第四选项写进 Skill
|
||||
// 原值: /^version: 10\.0\.28$/ / 新值: /^version: 10\.0\.29$/ / 原因: 年月阶段改口述并禁止追问本人
|
||||
assert.match(skill, /^version: 10\.0\.29$/m);
|
||||
// 原值: /^version: 10\.0\.29$/ / 新值: /^version: 10\.0\.30$/ / 原因: D2 出卡句与 D4 卡上百分比规则写进 Skill(2026-09-26)
|
||||
assert.match(skill, /^version: 10\.0\.30$/m);
|
||||
assert.match(skill, /不要对同一句用户消息里的多件事件逐条 propose\+confirm/);
|
||||
assert.match(agentSource, /新事件走 rectification-record-evidence-batch/);
|
||||
assert.doesNotMatch(agentSource, /分别调用 rectification-propose-evidence 和 rectification-confirm-evidence/);
|
||||
|
||||
@@ -191,7 +191,8 @@ test("skill version is 10.0.26 after the targeted-collect-cards bump", () => {
|
||||
// 原因: 出卡精度门槛 + 引导式补经历
|
||||
// 原因: BUG-668 定向补事第四选项写进 Skill
|
||||
// 原值: "10.0.28" / 新值: "10.0.29" / 原因: 年月阶段改口述并禁止追问本人
|
||||
assert.equal(RECTIFICATION_SKILL_VERSION, "10.0.29");
|
||||
// 原值: "10.0.29" / 新值: "10.0.30" / 原因: D2 出卡句与 D4 卡上百分比规则写进 Skill(2026-09-26)
|
||||
assert.equal(RECTIFICATION_SKILL_VERSION, "10.0.30");
|
||||
});
|
||||
|
||||
test("nineteen-row ledger opens the training gate with four scoreable domains", () => {
|
||||
|
||||
@@ -431,7 +431,8 @@ test("skill version is 10.0.26 after the targeted-collect-cards bump", () => {
|
||||
// 原因: 出卡精度门槛 + 引导式补经历
|
||||
// 原因: BUG-668 定向补事第四选项写进 Skill
|
||||
// 原值: "10.0.28" / 新值: "10.0.29" / 原因: 年月阶段改口述并禁止追问本人
|
||||
assert.equal(RECTIFICATION_SKILL_VERSION, "10.0.29");
|
||||
// 原值: "10.0.29" / 新值: "10.0.30" / 原因: D2 出卡句与 D4 卡上百分比规则写进 Skill(2026-09-26)
|
||||
assert.equal(RECTIFICATION_SKILL_VERSION, "10.0.30");
|
||||
});
|
||||
|
||||
test("pre-fix dual-exit constant is gone; range narration carries numbers and the disclaimer", () => {
|
||||
|
||||
@@ -464,7 +464,8 @@ test("skill version is 10.0.26", () => {
|
||||
// 原因: 出卡精度门槛 + 引导式补经历
|
||||
// 原因: BUG-668 定向补事第四选项写进 Skill
|
||||
// 原值: "10.0.28" / 新值: "10.0.29" / 原因: 年月阶段改口述并禁止追问本人
|
||||
assert.equal(RECTIFICATION_SKILL_VERSION, "10.0.29");
|
||||
// 原值: "10.0.29" / 新值: "10.0.30" / 原因: D2 出卡句与 D4 卡上百分比规则写进 Skill(2026-09-26)
|
||||
assert.equal(RECTIFICATION_SKILL_VERSION, "10.0.30");
|
||||
});
|
||||
|
||||
test("two education events do not spawn birth-year reverse questions", () => {
|
||||
|
||||
@@ -101,7 +101,8 @@ test("skill version is 10.0.26 after the targeted-collect-cards bump", () => {
|
||||
// 原因: 出卡精度门槛 + 引导式补经历
|
||||
// 原因: BUG-668 定向补事第四选项写进 Skill
|
||||
// 原值: "10.0.28" / 新值: "10.0.29" / 原因: 年月阶段改口述并禁止追问本人
|
||||
assert.equal(RECTIFICATION_SKILL_VERSION, "10.0.29");
|
||||
// 原值: "10.0.29" / 新值: "10.0.30" / 原因: D2 出卡句与 D4 卡上百分比规则写进 Skill(2026-09-26)
|
||||
assert.equal(RECTIFICATION_SKILL_VERSION, "10.0.30");
|
||||
});
|
||||
|
||||
test("cases current_question remains the submit contract, not a visual slot", () => {
|
||||
|
||||
@@ -197,7 +197,8 @@ test("Skill 10.0.26 lists the fourth targeted-collect skip option", () => {
|
||||
const skill = readFileSync(new URL("../../skills/jyotish-birth-time-rectification/SKILL.md", import.meta.url), "utf8");
|
||||
const strategy = readFileSync(new URL("../../skills/jyotish-birth-time-rectification/references/conversation-strategy.md", import.meta.url), "utf8");
|
||||
// 原值: /^version: 10\.0\.28$/ / 新值: /^version: 10\.0\.29$/ / 原因: 年月阶段改口述并禁止追问本人
|
||||
assert.match(skill, /^version: 10\.0\.29$/m);
|
||||
// 原值: /^version: 10\.0\.29$/ / 新值: /^version: 10\.0\.30$/ / 原因: D2 出卡句与 D4 卡上百分比规则写进 Skill(2026-09-26)
|
||||
assert.match(skill, /^version: 10\.0\.30$/m);
|
||||
assert.match(skill, /已拒绝(没有发生过 \/ 这类事都没有过)的目标不得换词重问;跳过的按服务器计划最多重问一次/);
|
||||
assert.match(strategy, /跳过的线按服务器计划最多换一种问法再问一次/);
|
||||
});
|
||||
|
||||
@@ -104,7 +104,8 @@ test("agent pins the dedicated rectification skill and its fixed version", () =>
|
||||
// 原因: 出卡精度门槛 + 引导式补经历
|
||||
// 原因: BUG-668 定向补事第四选项写进 Skill
|
||||
// 原值: "10.0.28" / 新值: "10.0.29" / 原因: 年月阶段改口述并禁止追问本人
|
||||
assert.ok(RECTIFICATION_V9_PACKAGE_PATH.endsWith("skills/jyotish-birth-time-rectification/versions/10.0.29"));
|
||||
// 原值: "10.0.29" / 新值: "10.0.30" / 原因: D2 出卡句与 D4 卡上百分比规则写进 Skill(2026-09-26)
|
||||
assert.ok(RECTIFICATION_V9_PACKAGE_PATH.endsWith("skills/jyotish-birth-time-rectification/versions/10.0.30"));
|
||||
assert.notEqual(RECTIFICATION_V9_SKILL_PATH, RECTIFICATION_V9_PACKAGE_PATH);
|
||||
assert.equal(realpathSync(RECTIFICATION_V9_SKILL_PATH), RECTIFICATION_V9_PACKAGE_PATH);
|
||||
assert.equal(RECTIFICATION_SKILL_NAME, "jyotish-birth-time-rectification");
|
||||
@@ -114,7 +115,8 @@ test("agent pins the dedicated rectification skill and its fixed version", () =>
|
||||
// 原因: 出卡精度门槛 + 引导式补经历
|
||||
// 原因: BUG-668 定向补事第四选项写进 Skill
|
||||
// 原值: "10.0.28" / 新值: "10.0.29" / 原因: 年月阶段改口述并禁止追问本人
|
||||
assert.equal(RECTIFICATION_SKILL_VERSION, "10.0.29");
|
||||
// 原值: "10.0.29" / 新值: "10.0.30" / 原因: D2 出卡句与 D4 卡上百分比规则写进 Skill(2026-09-26)
|
||||
assert.equal(RECTIFICATION_SKILL_VERSION, "10.0.30");
|
||||
});
|
||||
|
||||
test("step budgets are bounded per action with a hard ceiling", () => {
|
||||
|
||||
@@ -101,7 +101,8 @@ test("the active rectification skill pins the v10 identity and lives in the righ
|
||||
// 原因: 出卡精度门槛 + 引导式补经历
|
||||
// 原因: BUG-668 定向补事第四选项写进 Skill
|
||||
// 原值: "10.0.28" / 新值: "10.0.29" / 原因: 年月阶段改口述并禁止追问本人
|
||||
assert.equal(RECTIFICATION_SKILL_VERSION, "10.0.29");
|
||||
// 原值: "10.0.29" / 新值: "10.0.30" / 原因: D2 出卡句与 D4 卡上百分比规则写进 Skill(2026-09-26)
|
||||
assert.equal(RECTIFICATION_SKILL_VERSION, "10.0.30");
|
||||
assert.match(skill, /^---\nname: jyotish-birth-time-rectification/m);
|
||||
// 原值: "10.0.25"
|
||||
// 原值: "10.0.26"
|
||||
@@ -109,7 +110,8 @@ test("the active rectification skill pins the v10 identity and lives in the righ
|
||||
// 原因: 出卡精度门槛 + 引导式补经历
|
||||
// 原因: BUG-668 定向补事第四选项写进 Skill
|
||||
// 原值: /^version: 10\.0\.28$/ / 新值: /^version: 10\.0\.29$/ / 原因: 年月阶段改口述并禁止追问本人
|
||||
assert.match(skill, /^version: 10\.0\.29$/m);
|
||||
// 原值: /^version: 10\.0\.29$/ / 新值: /^version: 10\.0\.30$/ / 原因: D2 出卡句与 D4 卡上百分比规则写进 Skill(2026-09-26)
|
||||
assert.match(skill, /^version: 10\.0\.30$/m);
|
||||
assert.match(skill, /至多一个主问题且唯一来源:[\s\S]*不得自行提出、复述、改写或预告问题/);
|
||||
for (const reference of references) {
|
||||
const content = readFileSync(`${skillDirectory}/references/${reference}`, "utf8");
|
||||
|
||||
@@ -211,7 +211,8 @@ test("open RPC passes the pinned skill and server-derived baseline only", async
|
||||
should_start_opening: true,
|
||||
// 原值: "10.0.25";新值: "10.0.26";原因: BUG-668 新案绑定现行 Skill
|
||||
// 原值: "10.0.28" / 新值: "10.0.29" / 原因: 年月阶段改口述并禁止追问本人
|
||||
skill_version: "10.0.29",
|
||||
// 原值: "10.0.29" / 新值: "10.0.30" / 原因: D2 出卡句与 D4 卡上百分比规则写进 Skill(2026-09-26)
|
||||
skill_version: "10.0.30",
|
||||
};
|
||||
}
|
||||
return null;
|
||||
@@ -255,7 +256,8 @@ test("open RPC passes the pinned skill and server-derived baseline only", async
|
||||
// 原因: 出卡精度门槛 + 引导式补经历
|
||||
// 原因: BUG-668 定向补事第四选项写进 Skill
|
||||
// 原值: "10.0.28" / 新值: "10.0.29" / 原因: 年月阶段改口述并禁止追问本人
|
||||
assert.equal(response.skillVersion, "10.0.29");
|
||||
// 原值: "10.0.29" / 新值: "10.0.30" / 原因: D2 出卡句与 D4 卡上百分比规则写进 Skill(2026-09-26)
|
||||
assert.equal(response.skillVersion, "10.0.30");
|
||||
const openCall = accounting.calls.find((call) => call.fn === "open_agentic_rectification_case_v2");
|
||||
assert.ok(openCall);
|
||||
assert.equal(openCall.args.p_skill_name, "jyotish-birth-time-rectification");
|
||||
@@ -265,7 +267,8 @@ test("open RPC passes the pinned skill and server-derived baseline only", async
|
||||
// 原因: 出卡精度门槛 + 引导式补经历
|
||||
// 原因: BUG-668 定向补事第四选项写进 Skill
|
||||
// 原值: "10.0.28" / 新值: "10.0.29" / 原因: 年月阶段改口述并禁止追问本人
|
||||
assert.equal(openCall.args.p_skill_version, "10.0.29");
|
||||
// 原值: "10.0.29" / 新值: "10.0.30" / 原因: D2 出卡句与 D4 卡上百分比规则写进 Skill(2026-09-26)
|
||||
assert.equal(openCall.args.p_skill_version, "10.0.30");
|
||||
assert.equal(openCall.args.p_user_id, "user-1");
|
||||
// The server derives the baseline; the request never carries it from the browser.
|
||||
assert.equal("birth_date" in openCall.args, false);
|
||||
|
||||
@@ -92,5 +92,6 @@ test("agent body cannot verbally accept a spoken birth window", () => {
|
||||
// 原因: 出卡精度门槛 + 引导式补经历
|
||||
// 原因: BUG-668 定向补事第四选项写进 Skill
|
||||
// 原值: "10.0.28" / 新值: "10.0.29" / 原因: 年月阶段改口述并禁止追问本人
|
||||
assert.equal(RECTIFICATION_SKILL_VERSION, "10.0.29");
|
||||
// 原值: "10.0.29" / 新值: "10.0.30" / 原因: D2 出卡句与 D4 卡上百分比规则写进 Skill(2026-09-26)
|
||||
assert.equal(RECTIFICATION_SKILL_VERSION, "10.0.30");
|
||||
});
|
||||
|
||||
@@ -167,7 +167,8 @@ test("SCORE_DELTA stays ±2/±1 and yearless weight is half", () => {
|
||||
// 原因: 出卡精度门槛 + 引导式补经历
|
||||
// 原因: BUG-668 定向补事第四选项写进 Skill
|
||||
// 原值: "10.0.28" / 新值: "10.0.29" / 原因: 年月阶段改口述并禁止追问本人
|
||||
assert.equal(RECTIFICATION_SKILL_VERSION, "10.0.29");
|
||||
// 原值: "10.0.29" / 新值: "10.0.30" / 原因: D2 出卡句与 D4 卡上百分比规则写进 Skill(2026-09-26)
|
||||
assert.equal(RECTIFICATION_SKILL_VERSION, "10.0.30");
|
||||
});
|
||||
|
||||
test("D9 answer B moves scores by ±1 and does not count toward elimination", () => {
|
||||
|
||||
@@ -85,11 +85,11 @@ test("checked-in registry verifies hashed product packages and leaves consult on
|
||||
[
|
||||
{
|
||||
name: "jyotish-birth-time-rectification",
|
||||
// 原值: 10.0.28 / f0bb8295…cffe
|
||||
// 新值: 10.0.29 / 750a0d58…724c
|
||||
// 原因: 年月阶段改口述、禁止追问本人
|
||||
version: "10.0.29",
|
||||
sha256: "750a0d58c6731c0dbdeb5c785cea1bf8a3f73aaf94a560a86397805b7037724c",
|
||||
// 原值: 10.0.29 / 750a0d58…724c
|
||||
// 新值: 10.0.30 / 926db1ab…3a13
|
||||
// 原因: D2 出卡句(引导窗口题不挡出卡、一次校正最多两道)与 D4 卡上百分比规则写进 Skill
|
||||
version: "10.0.30",
|
||||
sha256: "926db1ab20e7990ca0585037b6595691a2db0da363b4ab433f2fb98b2d223a13",
|
||||
},
|
||||
{
|
||||
name: "jyotish-personal-report",
|
||||
|
||||
Reference in New Issue
Block a user