fix(rectification): keep range-card tie-break entry honest after D9/D10 answers (BUG-666~668)
Hide the button once both personality cards are answered, persist the second card in the same opt-in round, and freeze Skill 10.0.26. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1387,6 +1387,10 @@ export function RectificationAgenticChat(props: RectificationAgenticChatProps) {
|
||||
},
|
||||
);
|
||||
const payload = await response.json().catch(() => null);
|
||||
if (payload?.code === "tie_break_unavailable") {
|
||||
await loadCaseSnapshot();
|
||||
return;
|
||||
}
|
||||
if (!response.ok || payload?.ok !== true) {
|
||||
throw new Error(payload?.error || payload?.message || "暂时无法开始参考题");
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@ import {
|
||||
type PersistChoiceActionInput,
|
||||
type V9CaseDossier,
|
||||
} from "./tool-service";
|
||||
import { CHOICE_SKIP_QUESTION_LABEL, isPersistedFocusId, type ChoiceKey } from "./choice-card";
|
||||
import { CHOICE_SKIP_QUESTION_LABEL, isPersistedFocusId, isTieBreakRoundSchema, type ChoiceKey } from "./choice-card";
|
||||
import { clusterScoreDeltas } from "./probe-explain.ts";
|
||||
import {
|
||||
adoptDeliveryFacts,
|
||||
@@ -923,6 +923,7 @@ export async function persistNextInterviewAfterChoice(input: {
|
||||
askedTurnId?: string | null;
|
||||
narrateAdopt?: AdoptNarrationWriter;
|
||||
skipRefresh?: boolean;
|
||||
continueTieBreakRound?: boolean;
|
||||
}): Promise<{
|
||||
hostNarration: string;
|
||||
choiceReady: boolean;
|
||||
@@ -966,7 +967,7 @@ export async function persistNextInterviewAfterChoice(input: {
|
||||
const sessionOutcome = typeof nextAction.session_outcome === "string"
|
||||
? nextAction.session_outcome as SessionOutcomeKind
|
||||
: "collect_evidence";
|
||||
const plan = planWithDateReliability(buildMethodFollowupPlan({
|
||||
const planInput = {
|
||||
evidence: liveDossier.evidence,
|
||||
activeFocus: null,
|
||||
declinedTopics: liveDossier.conversationSummary.declinedSkippedTopics,
|
||||
@@ -983,7 +984,32 @@ export async function persistNextInterviewAfterChoice(input: {
|
||||
reportedBirthTime: liveDossier.case.reportedBirthTime,
|
||||
candidateRange: liveDossier.case.candidateRange,
|
||||
}),
|
||||
}), liveDossier.evidence, input.askedTurnId);
|
||||
};
|
||||
const roundFollowup = input.continueTieBreakRound === true
|
||||
? tieBreakPersonalityFollowup(planInput)
|
||||
: null;
|
||||
if (roundFollowup) {
|
||||
const persistedFocus = await persistFocusAfterChoice({
|
||||
accounting: input.accounting,
|
||||
userId: input.userId,
|
||||
caseId: input.caseId,
|
||||
decisionReceipt: liveDossier.latestResult?.decisionReceipt,
|
||||
followup: roundFollowup,
|
||||
askedTurnId: input.askedTurnId ?? null,
|
||||
});
|
||||
const open = openQuestionFromPersistedFocus(persistedFocus);
|
||||
if (isRenderableChoiceOpenQuestion(open) && open.prompt) {
|
||||
return {
|
||||
hostNarration: RECTIFICATION_USER_COPY.rangeDeliveryTieBreakAck,
|
||||
choiceReady: true,
|
||||
persisted: true,
|
||||
focusId: persistedFocus.focus?.id ?? null,
|
||||
focus: persistedFocus.focus,
|
||||
followup: roundFollowup,
|
||||
};
|
||||
}
|
||||
}
|
||||
const plan = planWithDateReliability(buildMethodFollowupPlan(planInput), liveDossier.evidence, input.askedTurnId);
|
||||
const followup = interviewToPersist(plan);
|
||||
if (shouldSkipFollowupPersist({
|
||||
canAdopt: nextAction.can_adopt,
|
||||
@@ -1388,6 +1414,7 @@ export async function applyCollectFocusDenial(
|
||||
decision,
|
||||
birthDate,
|
||||
narrateAdopt: input.narrateAdopt,
|
||||
continueTieBreakRound: isTieBreakRoundSchema(focus.expectedAnswerSchema),
|
||||
});
|
||||
return {
|
||||
narration: nextInterview.hostNarration,
|
||||
@@ -1684,19 +1711,20 @@ export async function persistNextInterviewIfIdle(input: {
|
||||
}));
|
||||
}
|
||||
const nextAction = publicNextAction(decision);
|
||||
const nextInterview = await persistNextInterviewAfterChoice({
|
||||
accounting: input.accounting,
|
||||
userId: input.userId,
|
||||
caseId: input.caseId,
|
||||
dossier,
|
||||
decisionState: previousInferenceFromReceipt(dossier.latestResult?.decisionReceipt ?? null),
|
||||
nextAction,
|
||||
decision,
|
||||
birthDate,
|
||||
askedTurnId: input.askedTurnId ?? null,
|
||||
narrateAdopt: input.narrateAdopt,
|
||||
skipRefresh: true,
|
||||
});
|
||||
const nextInterview = await persistNextInterviewAfterChoice({
|
||||
accounting: input.accounting,
|
||||
userId: input.userId,
|
||||
caseId: input.caseId,
|
||||
dossier,
|
||||
decisionState: previousInferenceFromReceipt(dossier.latestResult?.decisionReceipt ?? null),
|
||||
nextAction,
|
||||
decision,
|
||||
birthDate,
|
||||
askedTurnId: input.askedTurnId ?? null,
|
||||
narrateAdopt: input.narrateAdopt,
|
||||
skipRefresh: true,
|
||||
continueTieBreakRound: isTieBreakRoundSchema(dossier.conversationSummary.activeFocus?.expectedAnswerSchema),
|
||||
});
|
||||
return finishIdle({
|
||||
persisted: Boolean(nextInterview.hostNarration) || nextInterview.choiceReady,
|
||||
choiceReady: nextInterview.choiceReady,
|
||||
@@ -2032,6 +2060,9 @@ async function persistApplied(
|
||||
decision: nextDecision,
|
||||
birthDate,
|
||||
narrateAdopt: command.narrateAdopt,
|
||||
continueTieBreakRound: isTieBreakRoundSchema(
|
||||
input.dossier.conversationSummary.activeFocus?.expectedAnswerSchema,
|
||||
),
|
||||
});
|
||||
nextChoiceReady = nextInterview.choiceReady;
|
||||
skippedNextInterview = nextInterview.persisted === false;
|
||||
|
||||
@@ -15,6 +15,8 @@ import { publicDecisionFields } from "@/lib/rectification-agentic/core/rectifica
|
||||
import { collectionProgressFromReceipt } from "@/lib/rectification-agentic/v9/evidence-model";
|
||||
import { slimDecisionReceipt } from "@/lib/rectification-agentic/v9/case-receipt-projection";
|
||||
import { rangeDeliveryForSnapshot } from "@/lib/rectification-agentic/v9/divergence-panel";
|
||||
import { rectificationFollowupCatalog } from "@/lib/rectification-agentic/v9/decision-from-dossier";
|
||||
import { tieBreakPersonalityAvailable } from "@/lib/rectification-agentic/v9/method-followup";
|
||||
import { latestResultToolProjection } from "@/mastra/rectification-v9-tools";
|
||||
|
||||
export function dossierResponse(
|
||||
@@ -92,6 +94,7 @@ function publicLatestResult(
|
||||
if (!latest) return null;
|
||||
const projected = overlayPublicDecision(latest, decision);
|
||||
const toolProjection = latestResultToolProjection(latest, decision);
|
||||
const catalog = rectificationFollowupCatalog(latest, dossier.evidence);
|
||||
const rangeDelivery = rangeDeliveryForSnapshot({
|
||||
decisionReceipt: projected.decisionReceipt,
|
||||
candidates: projected.candidates,
|
||||
@@ -102,6 +105,16 @@ function publicLatestResult(
|
||||
skill_verification_report: toolProjection.skill_verification_report,
|
||||
evidence: dossier.evidence,
|
||||
declinedTopics: dossier.conversationSummary.declinedSkippedTopics,
|
||||
accepted: Boolean(dossier.case.acceptedTime),
|
||||
tieBreakAvailable: tieBreakPersonalityAvailable({
|
||||
evidence: dossier.evidence,
|
||||
declinedTopics: dossier.conversationSummary.declinedSkippedTopics,
|
||||
closedCollectFocuses: dossier.conversationSummary.declinedSkippedTopics,
|
||||
sessionOutcome: "discriminate_candidates",
|
||||
candidatesSeparated: false,
|
||||
...catalog,
|
||||
accepted: Boolean(dossier.case.acceptedTime),
|
||||
}),
|
||||
});
|
||||
const withDelivery = {
|
||||
...projected,
|
||||
|
||||
@@ -89,4 +89,4 @@ export function evidenceWritesAllowed(
|
||||
export const MAX_RESUMABLE_CASES_PER_USER = 1;
|
||||
|
||||
export const RECTIFICATION_SKILL_NAME = "jyotish-birth-time-rectification";
|
||||
export const RECTIFICATION_SKILL_VERSION = "10.0.25";
|
||||
export const RECTIFICATION_SKILL_VERSION = "10.0.26";
|
||||
|
||||
@@ -40,6 +40,15 @@ export function isPersistedFocusId(value: string | null | undefined): value is s
|
||||
return typeof value === "string" && FOCUS_ID_PATTERN.test(value.trim());
|
||||
}
|
||||
|
||||
export function isTieBreakRoundSchema(value: unknown): boolean {
|
||||
return Boolean(
|
||||
value
|
||||
&& typeof value === "object"
|
||||
&& !Array.isArray(value)
|
||||
&& (value as { tie_break_round?: unknown }).tie_break_round === true,
|
||||
);
|
||||
}
|
||||
|
||||
export type ChoiceKey = "A" | "B" | "C" | "D";
|
||||
|
||||
export type RectificationChoiceOption = Readonly<{
|
||||
|
||||
@@ -639,31 +639,6 @@ export function isTargetedCollectFollowup(followup: {
|
||||
|| hint.startsWith("targeted:");
|
||||
}
|
||||
|
||||
export function isTargetedCollectYearFollowup(followup: {
|
||||
collection_key?: string;
|
||||
kind_hint?: string | null;
|
||||
} | null | undefined): boolean {
|
||||
if (!followup) return false;
|
||||
const key = followup.collection_key ?? "";
|
||||
const hint = followup.kind_hint ?? "";
|
||||
return parseTargetedCollectQuestionId(key)?.stage === "year"
|
||||
|| parseTargetedCollectKind(hint)?.stage === "year";
|
||||
}
|
||||
|
||||
/**
|
||||
* True when at least one targeted line was declined or skipped.
|
||||
* Per-domain cards must not treat this as “the whole targeted pool is done”.
|
||||
*/
|
||||
export function isTargetedCollectDeclined(
|
||||
topics: readonly CollectionTopic[] = [],
|
||||
): boolean {
|
||||
return topics.some((topic) => {
|
||||
const status = topicStatus(topic);
|
||||
if (status !== "declined" && status !== "skipped") return false;
|
||||
return isTargetedCollectTopic(topic);
|
||||
});
|
||||
}
|
||||
|
||||
function collectDeclinedKinds(topics: readonly CollectionTopic[]): ReadonlySet<CollectKind> {
|
||||
const declined = new Set<CollectKind>();
|
||||
for (const topic of topics) {
|
||||
|
||||
@@ -451,16 +451,6 @@ function rangeFromUnknown(value: unknown): readonly [string, string] | null {
|
||||
return start && end ? [start, end] : null;
|
||||
}
|
||||
|
||||
function rawVargaStyleTieBreak(value: unknown): boolean {
|
||||
if (!value || typeof value !== "object" || Array.isArray(value)) return false;
|
||||
const row = value as Record<string, unknown>;
|
||||
const nested = row.window_scan;
|
||||
const scan = nested && typeof nested === "object" && !Array.isArray(nested)
|
||||
? nested as Record<string, unknown>
|
||||
: row;
|
||||
return scan.d9_candidates_differ === true || scan.d10_candidates_differ === true;
|
||||
}
|
||||
|
||||
export function rangeDeliveryForSnapshot(snapshot: {
|
||||
decisionReceipt?: Readonly<Record<string, unknown>> | null;
|
||||
decision_receipt?: Readonly<Record<string, unknown>> | null;
|
||||
@@ -484,6 +474,8 @@ export function rangeDeliveryForSnapshot(snapshot: {
|
||||
}>[];
|
||||
declinedTopics?: readonly Readonly<Record<string, unknown>>[];
|
||||
window_scan?: unknown;
|
||||
accepted?: boolean;
|
||||
tieBreakAvailable?: boolean;
|
||||
} | null | undefined): RangeDeliveryProjection {
|
||||
const receipt = snapshot?.decisionReceipt ?? snapshot?.decision_receipt ?? null;
|
||||
const inference = previousInferenceFromReceipt(receipt);
|
||||
@@ -510,12 +502,10 @@ export function rangeDeliveryForSnapshot(snapshot: {
|
||||
eventDashaLedger: refinement.event_dasha_ledger,
|
||||
evidence: snapshot?.evidence,
|
||||
declinedTopics: snapshot?.declinedTopics,
|
||||
tieBreakAvailable: Boolean(
|
||||
windowScan?.d9_candidates_differ
|
||||
|| windowScan?.d10_candidates_differ
|
||||
|| rawVargaStyleTieBreak(snapshot)
|
||||
|| rawVargaStyleTieBreak(receipt)
|
||||
),
|
||||
// BUG-666: the GET flag is the same unasked D9/D10 varga_style
|
||||
// judgment the tie-break route uses. Callers pass it; window_scan
|
||||
// alone must not keep the button after both cards are answered.
|
||||
tieBreakAvailable: snapshot?.accepted !== true && snapshot?.tieBreakAvailable === true,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -218,6 +218,7 @@ export type MethodFollowup = Readonly<{
|
||||
invite_more_once?: boolean;
|
||||
spoken_prompt?: string;
|
||||
collection_key?: string;
|
||||
tie_break_round?: boolean;
|
||||
block_periods?: Readonly<Record<"A" | "B" | "C", BlockScanBlock>>;
|
||||
widen_windows?: Readonly<Record<"A" | "B", import("./window-widen.ts").WidenWindowOption>>;
|
||||
date_reliability_evidence_id?: string;
|
||||
@@ -2975,7 +2976,14 @@ export function tieBreakPersonalityFollowup(
|
||||
if (kind !== "varga_style") return null;
|
||||
const key = followup.semantic_key ?? "";
|
||||
if (!key.startsWith("varga.d9") && !key.startsWith("varga.d10")) return null;
|
||||
return followup;
|
||||
return { ...followup, tie_break_round: true };
|
||||
}
|
||||
|
||||
export function tieBreakPersonalityAvailable(
|
||||
input: Parameters<typeof tieBreakPersonalityFollowup>[0] & { accepted?: boolean },
|
||||
): boolean {
|
||||
if (input.accepted) return false;
|
||||
return tieBreakPersonalityFollowup(input) !== null;
|
||||
}
|
||||
|
||||
export function projectRectificationChoiceCard(
|
||||
|
||||
@@ -105,6 +105,7 @@ export function expectedAnswerSchemaFor(
|
||||
collect_kind: followup.kind_hint ?? null,
|
||||
}
|
||||
: {}),
|
||||
...(followup.tie_break_round ? { tie_break_round: true } : {}),
|
||||
...(followup.block_periods ? { block_periods: followup.block_periods } : {}),
|
||||
...(followup.widen_windows ? { widen_windows: followup.widen_windows } : {}),
|
||||
};
|
||||
|
||||
@@ -77,6 +77,7 @@ import {
|
||||
buildNextUserAction,
|
||||
spokenCollectFallbackFollowup,
|
||||
collectQuestionDomain,
|
||||
tieBreakPersonalityAvailable,
|
||||
} from "@/lib/rectification-agentic/v9/method-followup";
|
||||
import { dashaAgreementAmongActive, refinementFromDecisionReceipt } from "@/lib/rectification-agentic/v9/refinement-packet";
|
||||
import { rangeDeliveryForSnapshot } from "@/lib/rectification-agentic/v9/divergence-panel";
|
||||
@@ -580,6 +581,12 @@ export function latestResultToolProjection(
|
||||
credibleRange: decision.credibleRange,
|
||||
credible_range: decision.credibleRange,
|
||||
skill_verification_report: base.skill_verification_report,
|
||||
tieBreakAvailable: tieBreakPersonalityAvailable({
|
||||
evidence: [],
|
||||
sessionOutcome: "discriminate_candidates",
|
||||
candidatesSeparated: false,
|
||||
...rectificationFollowupCatalog(latest, []),
|
||||
}),
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -458,11 +458,11 @@ function rpcDossier(decision: DecisionDossier, activeFocus?: Record<string, unkn
|
||||
});
|
||||
}
|
||||
|
||||
test("skill version is 10.0.25 after the targeted-collect-cards bump", () => {
|
||||
// 原值: "10.0.24"
|
||||
// 新值: "10.0.25"
|
||||
// 原因: BUG-661/662/663 定向补事改逐条点选,Skill 升版
|
||||
assert.equal(RECTIFICATION_SKILL_VERSION, "10.0.25");
|
||||
test("skill version is 10.0.26 after the targeted-collect-cards bump", () => {
|
||||
// 原值: "10.0.25"
|
||||
// 新值: "10.0.26"
|
||||
// 原因: BUG-668 定向补事第四选项写进 Skill
|
||||
assert.equal(RECTIFICATION_SKILL_VERSION, "10.0.26");
|
||||
});
|
||||
|
||||
test("revision 5 with uncovered relatives asks the dated family collect, not a yearless D12 card", () => {
|
||||
|
||||
@@ -360,10 +360,10 @@ test("holdout not_ready forbids unique-minute copy and still blocks confirm", as
|
||||
assert.match(agentSource, /不得宣称唯一出生分钟/);
|
||||
assert.doesNotMatch(agentSource, /±2 分钟/);
|
||||
assert.equal(PUBLIC_RECTIFICATION_TOOLS.length, 14);
|
||||
// 原值: "10.0.24"
|
||||
// 新值: "10.0.25"
|
||||
// 原因: BUG-661/662/663 定向补事改逐条点选,Skill 升版
|
||||
assert.equal(RECTIFICATION_SKILL_VERSION, "10.0.25");
|
||||
// 原值: "10.0.25"
|
||||
// 新值: "10.0.26"
|
||||
// 原因: BUG-668 定向补事第四选项写进 Skill
|
||||
assert.equal(RECTIFICATION_SKILL_VERSION, "10.0.26");
|
||||
|
||||
const accounting = fakeAccounting({
|
||||
...receiptHandlers,
|
||||
|
||||
@@ -249,18 +249,18 @@ test("public house table follows the inference representative minute", () => {
|
||||
assert.match(source, /natal_recast_pre_inference: _preNatal/);
|
||||
});
|
||||
|
||||
test("skill 10.0.25 forbids computing varga signs from transition times", () => {
|
||||
test("skill 10.0.26 forbids computing varga signs from transition times", () => {
|
||||
const skillDir = fileURLToPath(new URL("../../skills/jyotish-birth-time-rectification", import.meta.url));
|
||||
const skill = readFileSync(`${skillDir}/SKILL.md`, "utf8");
|
||||
const comparison = readFileSync(`${skillDir}/references/candidate-comparison.md`, "utf8");
|
||||
// 原值: "10.0.24"
|
||||
// 新值: "10.0.25"
|
||||
// 原因: BUG-661/662/663 定向补事改逐条点选,Skill 升版
|
||||
assert.equal(RECTIFICATION_SKILL_VERSION, "10.0.25");
|
||||
// 原值: "10.0.24"
|
||||
// 新值: "10.0.25"
|
||||
// 原因: BUG-661/662/663 定向补事改逐条点选,Skill 升版
|
||||
assert.match(skill, /^version: 10\.0\.25$/m);
|
||||
// 原值: "10.0.25"
|
||||
// 新值: "10.0.26"
|
||||
// 原因: BUG-668 定向补事第四选项写进 Skill
|
||||
assert.equal(RECTIFICATION_SKILL_VERSION, "10.0.26");
|
||||
// 原值: "10.0.25"
|
||||
// 新值: "10.0.26"
|
||||
// 原因: BUG-668 定向补事第四选项写进 Skill
|
||||
assert.match(skill, /^version: 10\.0\.26$/m);
|
||||
assert.match(skill, new RegExp(SKILL_SIGN_SENTENCE.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")));
|
||||
assert.match(comparison, new RegExp(SKILL_SIGN_SENTENCE.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")));
|
||||
});
|
||||
|
||||
@@ -1445,12 +1445,12 @@ test("rescore failure does not fail the evidence write", async () => {
|
||||
assert.ok(result.rescore.error_code);
|
||||
});
|
||||
|
||||
test("public tool surface stays at 14 and new cases bind 10.0.25", () => {
|
||||
test("public tool surface stays at 14 and new cases bind 10.0.26", () => {
|
||||
assert.equal(PUBLIC_RECTIFICATION_TOOLS.length, 14);
|
||||
// 原值: "10.0.24"
|
||||
// 新值: "10.0.25"
|
||||
// 原因: BUG-661/662/663 定向补事改逐条点选,Skill 升版
|
||||
assert.equal(RECTIFICATION_SKILL_VERSION, "10.0.25");
|
||||
// 原值: "10.0.25"
|
||||
// 新值: "10.0.26"
|
||||
// 原因: BUG-668 定向补事第四选项写进 Skill
|
||||
assert.equal(RECTIFICATION_SKILL_VERSION, "10.0.26");
|
||||
const deprecated = resolveExactSkillPackage(
|
||||
"jyotish-birth-time-rectification",
|
||||
"10.0.2",
|
||||
|
||||
@@ -591,11 +591,11 @@ function warnLines(run: () => Promise<unknown> | unknown) {
|
||||
}).then((result) => ({ result, lines }));
|
||||
}
|
||||
|
||||
test("skill version is 10.0.25 after the targeted-collect-cards bump", () => {
|
||||
// 原值: "10.0.24"
|
||||
// 新值: "10.0.25"
|
||||
// 原因: BUG-661/662/663 定向补事改逐条点选,Skill 升版
|
||||
assert.equal(RECTIFICATION_SKILL_VERSION, "10.0.25");
|
||||
test("skill version is 10.0.26 after the targeted-collect-cards bump", () => {
|
||||
// 原值: "10.0.25"
|
||||
// 新值: "10.0.26"
|
||||
// 原因: BUG-668 定向补事第四选项写进 Skill
|
||||
assert.equal(RECTIFICATION_SKILL_VERSION, "10.0.26");
|
||||
});
|
||||
|
||||
test("USER_COLLECT_QUESTION no longer has an other fallback", () => {
|
||||
|
||||
@@ -213,15 +213,15 @@ test("read-case evidence context keeps day labels and confirm does not rewrite d
|
||||
assert.equal("p_occurred_from" in confirmCall.args, false);
|
||||
});
|
||||
|
||||
test("new-case skill identity is 10.0.25 and the prompt prefers batch ingest", () => {
|
||||
// 原值: "10.0.24"
|
||||
// 新值: "10.0.25"
|
||||
// 原因: BUG-661/662/663 定向补事改逐条点选,Skill 升版
|
||||
assert.equal(RECTIFICATION_SKILL_VERSION, "10.0.25");
|
||||
// 原值: "10.0.24"
|
||||
// 新值: "10.0.25"
|
||||
// 原因: BUG-661/662/663 定向补事改逐条点选,Skill 升版
|
||||
assert.match(skill, /^version: 10\.0\.25$/m);
|
||||
test("new-case skill identity is 10.0.26 and the prompt prefers batch ingest", () => {
|
||||
// 原值: "10.0.25"
|
||||
// 新值: "10.0.26"
|
||||
// 原因: BUG-668 定向补事第四选项写进 Skill
|
||||
assert.equal(RECTIFICATION_SKILL_VERSION, "10.0.26");
|
||||
// 原值: "10.0.25"
|
||||
// 新值: "10.0.26"
|
||||
// 原因: BUG-668 定向补事第四选项写进 Skill
|
||||
assert.match(skill, /^version: 10\.0\.26$/m);
|
||||
assert.match(skill, /不要对同一句用户消息里的多件事件逐条 propose\+confirm/);
|
||||
assert.match(agentSource, /新事件走 rectification-record-evidence-batch/);
|
||||
assert.doesNotMatch(agentSource, /分别调用 rectification-propose-evidence 和 rectification-confirm-evidence/);
|
||||
|
||||
@@ -184,11 +184,11 @@ const CANDIDATE_IDS = [
|
||||
"88888888-8888-4888-8888-888888888882",
|
||||
] as const;
|
||||
|
||||
test("skill version is 10.0.25 after the targeted-collect-cards bump", () => {
|
||||
// 原值: "10.0.24"
|
||||
// 新值: "10.0.25"
|
||||
// 原因: BUG-661/662/663 定向补事改逐条点选,Skill 升版
|
||||
assert.equal(RECTIFICATION_SKILL_VERSION, "10.0.25");
|
||||
test("skill version is 10.0.26 after the targeted-collect-cards bump", () => {
|
||||
// 原值: "10.0.25"
|
||||
// 新值: "10.0.26"
|
||||
// 原因: BUG-668 定向补事第四选项写进 Skill
|
||||
assert.equal(RECTIFICATION_SKILL_VERSION, "10.0.26");
|
||||
});
|
||||
|
||||
test("nineteen-row ledger opens the training gate with four scoreable domains", () => {
|
||||
|
||||
@@ -424,11 +424,11 @@ function rpcDossier(decision: DecisionDossier) {
|
||||
});
|
||||
}
|
||||
|
||||
test("skill version is 10.0.25 after the targeted-collect-cards bump", () => {
|
||||
// 原值: "10.0.24"
|
||||
// 新值: "10.0.25"
|
||||
// 原因: BUG-661/662/663 定向补事改逐条点选,Skill 升版
|
||||
assert.equal(RECTIFICATION_SKILL_VERSION, "10.0.25");
|
||||
test("skill version is 10.0.26 after the targeted-collect-cards bump", () => {
|
||||
// 原值: "10.0.25"
|
||||
// 新值: "10.0.26"
|
||||
// 原因: BUG-668 定向补事第四选项写进 Skill
|
||||
assert.equal(RECTIFICATION_SKILL_VERSION, "10.0.26");
|
||||
});
|
||||
|
||||
test("pre-fix dual-exit constant is gone; range narration carries numbers and the disclaimer", () => {
|
||||
|
||||
@@ -450,11 +450,11 @@ function warnLines(run: () => Promise<unknown> | unknown) {
|
||||
}).then((result) => ({ result, lines }));
|
||||
}
|
||||
|
||||
test("skill version is 10.0.25", () => {
|
||||
// 原值: "10.0.24"
|
||||
// 新值: "10.0.25"
|
||||
// 原因: BUG-661/662/663 定向补事改逐条点选,Skill 升版
|
||||
assert.equal(RECTIFICATION_SKILL_VERSION, "10.0.25");
|
||||
test("skill version is 10.0.26", () => {
|
||||
// 原值: "10.0.25"
|
||||
// 新值: "10.0.26"
|
||||
// 原因: BUG-668 定向补事第四选项写进 Skill
|
||||
assert.equal(RECTIFICATION_SKILL_VERSION, "10.0.26");
|
||||
});
|
||||
|
||||
test("two education events do not spawn birth-year reverse questions", () => {
|
||||
|
||||
@@ -94,11 +94,11 @@ function collectPersistResult(overrides: {
|
||||
};
|
||||
}
|
||||
|
||||
test("skill version is 10.0.25 after the targeted-collect-cards bump", () => {
|
||||
// 原值: "10.0.24"
|
||||
// 新值: "10.0.25"
|
||||
// 原因: BUG-661/662/663 定向补事改逐条点选,Skill 升版
|
||||
assert.equal(RECTIFICATION_SKILL_VERSION, "10.0.25");
|
||||
test("skill version is 10.0.26 after the targeted-collect-cards bump", () => {
|
||||
// 原值: "10.0.25"
|
||||
// 新值: "10.0.26"
|
||||
// 原因: BUG-668 定向补事第四选项写进 Skill
|
||||
assert.equal(RECTIFICATION_SKILL_VERSION, "10.0.26");
|
||||
});
|
||||
|
||||
test("cases current_question remains the submit contract, not a visual slot", () => {
|
||||
|
||||
@@ -14,6 +14,7 @@ import { rangeDeliveryForSnapshot } from "../src/lib/rectification-agentic/v9/di
|
||||
import {
|
||||
buildMethodFollowupPlan,
|
||||
targetedCollectFollowup,
|
||||
tieBreakPersonalityAvailable,
|
||||
tieBreakPersonalityFollowup,
|
||||
} from "../src/lib/rectification-agentic/v9/method-followup.ts";
|
||||
|
||||
@@ -151,6 +152,7 @@ test("personality D9/D10 stay out of the scoring pool until the range-card opt-i
|
||||
const opted = tieBreakPersonalityFollowup(planInput);
|
||||
assert.equal(opted?.choice_kind, "varga_style");
|
||||
assert.match(opted?.semantic_key ?? "", /varga\.d9/);
|
||||
assert.equal(opted?.tie_break_round, true);
|
||||
const d10: ConflictProbe = {
|
||||
id: "probe:varga.d10.狮子座/处女座",
|
||||
semantic_key: "varga.d10.狮子座/处女座",
|
||||
@@ -201,6 +203,7 @@ test("personality D9/D10 stay out of the scoring pool until the range-card opt-i
|
||||
d2_candidates_differ: true,
|
||||
d11_candidates_differ: true,
|
||||
},
|
||||
tieBreakAvailable: tieBreakPersonalityAvailable(planInput),
|
||||
});
|
||||
assert.match(delivery.narrow_hint ?? "", /还能再收窄/);
|
||||
assert.doesNotMatch(delivery.narrow_hint ?? "", SPLIT_ENDPOINT_PHRASE);
|
||||
@@ -217,6 +220,9 @@ test("personality D9/D10 stay out of the scoring pool until the range-card opt-i
|
||||
d10_candidates_differ: false,
|
||||
},
|
||||
});
|
||||
assert.equal(incompleteScan.tie_break_available, true);
|
||||
// 原值: true(window_scan.d9_candidates_differ 为真就显示入口)
|
||||
// 新值: false(没有未问的 D9/D10 varga_style 时不显示)
|
||||
// 原因: BUG-666 入口与 tie-break 路由共用「还有未答参考题」判定
|
||||
assert.equal(incompleteScan.tie_break_available, false);
|
||||
assert.equal(RECTIFICATION_USER_COPY.rangeDeliveryTieBreakEntry, "再答两道参考题微调排序");
|
||||
});
|
||||
|
||||
@@ -0,0 +1,193 @@
|
||||
import assert from "node:assert/strict";
|
||||
import { readFileSync } from "node:fs";
|
||||
import test from "node:test";
|
||||
|
||||
import { applyProbeOutcome } from "../src/lib/rectification-agentic/core/apply-probe-outcome.ts";
|
||||
import type { ConflictProbe } from "../src/lib/rectification-agentic/core/types.ts";
|
||||
import { isTieBreakRoundSchema } from "../src/lib/rectification-agentic/v9/choice-card.ts";
|
||||
import { rangeDeliveryForSnapshot } from "../src/lib/rectification-agentic/v9/divergence-panel.ts";
|
||||
import {
|
||||
buildMethodFollowupPlan,
|
||||
tieBreakPersonalityAvailable,
|
||||
tieBreakPersonalityFollowup,
|
||||
} from "../src/lib/rectification-agentic/v9/method-followup.ts";
|
||||
import { expectedAnswerSchemaFor, stableFollowupQuestionId } from "../src/lib/rectification-agentic/v9/server-focus.ts";
|
||||
|
||||
const TIMES = ["04:48", "04:53", "05:06", "05:07"] as const;
|
||||
|
||||
const LEDGER = [
|
||||
{
|
||||
status: "confirmed",
|
||||
domain: "education",
|
||||
datePrecision: "month",
|
||||
occurredFrom: "2016-09-01",
|
||||
occurredTo: "2016-09-30",
|
||||
eventKind: "education_start",
|
||||
summary: "上大学",
|
||||
},
|
||||
{
|
||||
status: "confirmed",
|
||||
domain: "career",
|
||||
datePrecision: "month",
|
||||
occurredFrom: "2020-04-01",
|
||||
occurredTo: null,
|
||||
eventKind: "career_entry",
|
||||
summary: "入职实习",
|
||||
},
|
||||
{
|
||||
status: "confirmed",
|
||||
domain: "relationship",
|
||||
datePrecision: "month",
|
||||
occurredFrom: "2024-05-01",
|
||||
occurredTo: null,
|
||||
eventKind: "relationship_start",
|
||||
summary: "确定关系",
|
||||
},
|
||||
] as const;
|
||||
|
||||
function contrastProbe(layer: "d9" | "d10") {
|
||||
const signs = layer === "d9" ? ["巨蟹座", "狮子座"] as const : ["狮子座", "处女座"];
|
||||
return {
|
||||
probeId: `contrast:varga.${layer}.${signs[0]}/${signs[1]}`,
|
||||
candidateSetVersion: "04:48-05:07",
|
||||
question: layer === "d9"
|
||||
? "亲密关系里更接近下面哪一种相处方式?"
|
||||
: "平时做事,你更接近下面哪一种?",
|
||||
expectedOutcomes: [
|
||||
{ outcomeId: "yes", supportsCandidateIds: ["04:53"], conflictsCandidateIds: ["05:06"] },
|
||||
{ outcomeId: "weak_yes", supportsCandidateIds: ["05:06"], conflictsCandidateIds: ["04:53"] },
|
||||
],
|
||||
candidateSplitHash: `varga.${layer}.${signs[0]}/${signs[1]}`,
|
||||
informationGain: layer === "d9" ? 1.4 : 0.9,
|
||||
sourceFeatures: [{ technique: layer.toUpperCase(), calculationResultId: null }],
|
||||
domain: layer === "d9" ? "relationship" : "career",
|
||||
year: null,
|
||||
semanticKey: `varga.${layer}.${signs[0]}/${signs[1]}`,
|
||||
choiceKind: "varga_style" as const,
|
||||
styleOptions: [
|
||||
{ label: "相处里更在意照顾对方的感受", answerClass: "yes" as const, sign: signs[0] },
|
||||
{ label: "习惯带头,也不排斥站到台前", answerClass: "weak_yes" as const, sign: signs[1] },
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
const D9 = contrastProbe("d9");
|
||||
const D10 = contrastProbe("d10");
|
||||
|
||||
function planInput(askedProbeKeys: readonly string[] = []) {
|
||||
return {
|
||||
evidence: LEDGER,
|
||||
declinedTopics: [],
|
||||
sessionOutcome: "discriminate_candidates" as const,
|
||||
candidatesSeparated: false,
|
||||
remainingLayers: ["d9", "d10"],
|
||||
remainingSplitTimes: ["04:48", "05:07"] as const,
|
||||
remainingCandidateCount: 4,
|
||||
topCandidateTimes: [...TIMES],
|
||||
askedProbeKeys,
|
||||
contrastPacket: {
|
||||
candidateSetVersion: "04:48-05:07",
|
||||
vargaDifferences: [],
|
||||
probes: [D9, D10],
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
test("GET availability matches unasked D9/D10 varga_style, not window_scan", () => {
|
||||
const open = planInput();
|
||||
assert.equal(buildMethodFollowupPlan(open).next_followup?.choice_kind, undefined);
|
||||
assert.equal(tieBreakPersonalityAvailable(open), true);
|
||||
assert.match(tieBreakPersonalityFollowup(open)?.semantic_key ?? "", /varga\.d9/);
|
||||
assert.equal(rangeDeliveryForSnapshot({
|
||||
window_scan: { d9_candidates_differ: true, d10_candidates_differ: true },
|
||||
tieBreakAvailable: tieBreakPersonalityAvailable(open),
|
||||
}).tie_break_available, true);
|
||||
|
||||
const afterD9 = planInput([D9.semanticKey]);
|
||||
const second = tieBreakPersonalityFollowup(afterD9);
|
||||
assert.match(second?.semantic_key ?? "", /varga\.d10/);
|
||||
assert.equal(second?.tie_break_round, true);
|
||||
assert.equal(tieBreakPersonalityAvailable(afterD9), true);
|
||||
|
||||
const bothAsked = planInput([D9.semanticKey, D10.semanticKey]);
|
||||
assert.equal(tieBreakPersonalityFollowup(bothAsked), null);
|
||||
assert.equal(tieBreakPersonalityAvailable(bothAsked), false);
|
||||
assert.equal(rangeDeliveryForSnapshot({
|
||||
window_scan: { d9_candidates_differ: true, d10_candidates_differ: true },
|
||||
tieBreakAvailable: tieBreakPersonalityAvailable(bothAsked),
|
||||
}).tie_break_available, false);
|
||||
assert.equal(tieBreakPersonalityAvailable({ ...open, accepted: true }), false);
|
||||
});
|
||||
|
||||
test("one click stamps a round schema; scoring stays tie_break without elimination", () => {
|
||||
const followup = tieBreakPersonalityFollowup(planInput());
|
||||
assert.ok(followup?.choice_frame);
|
||||
const schema = expectedAnswerSchemaFor(
|
||||
followup.choice_frame,
|
||||
stableFollowupQuestionId(followup),
|
||||
null,
|
||||
followup,
|
||||
);
|
||||
assert.equal(schema?.tie_break_round, true);
|
||||
assert.equal(isTieBreakRoundSchema(schema), true);
|
||||
assert.equal(isTieBreakRoundSchema({ choice_kind: "varga_style" }), false);
|
||||
|
||||
const d10: ConflictProbe = {
|
||||
id: "probe:varga.d10.狮子座/处女座",
|
||||
semantic_key: "varga.d10.狮子座/处女座",
|
||||
candidate_split_hash: "04:48-05:07:varga.d10",
|
||||
domain: "career",
|
||||
year: 0,
|
||||
question: "平时做事,你更接近下面哪一种?",
|
||||
candidate_ids: [...TIMES],
|
||||
expected_outcomes: [
|
||||
{ answer_class: "yes", supports: ["04:53"], conflicts: ["05:06"] },
|
||||
{ answer_class: "weak_yes", supports: ["05:06"], conflicts: ["04:53"] },
|
||||
{ answer_class: "no", supports: [], conflicts: [] },
|
||||
{ answer_class: "unsure", supports: [], conflicts: [] },
|
||||
],
|
||||
information_gain: 0.9,
|
||||
source: "varga_contrast",
|
||||
choice_kind: "varga_style",
|
||||
style_options: [
|
||||
{ label: "相处里更在意照顾对方的感受", answer_class: "yes", sign: "巨蟹座" },
|
||||
{ label: "习惯带头,也不排斥站到台前", answer_class: "weak_yes", sign: "狮子座" },
|
||||
],
|
||||
};
|
||||
const scored = applyProbeOutcome(
|
||||
{ "04:48": 10, "04:53": 12, "05:06": 11, "05:07": 10 },
|
||||
d10,
|
||||
"yes",
|
||||
);
|
||||
assert.equal(scored.kind, "tie_break");
|
||||
assert.equal(scored.deltas["04:53"], 1);
|
||||
assert.equal(scored.eliminated_ids.length, 0);
|
||||
});
|
||||
|
||||
test("click path does not surface 409 copy; round continue is opt-in only", () => {
|
||||
const chat = readFileSync(new URL("../src/components/rectification-agentic-chat.tsx", import.meta.url), "utf8");
|
||||
const answer = readFileSync(new URL("../src/lib/rectification-agentic/v9/answer-choice.ts", import.meta.url), "utf8");
|
||||
const panel = readFileSync(new URL("../src/lib/rectification-agentic/v9/divergence-panel.ts", import.meta.url), "utf8");
|
||||
const pool = readFileSync(new URL("../src/lib/rectification-agentic/v9/collection-question-pool.ts", import.meta.url), "utf8");
|
||||
assert.match(chat, /payload\?\.code === "tie_break_unavailable"/);
|
||||
assert.match(chat, /await loadCaseSnapshot\(\);\s*\n\s*return;/);
|
||||
const unavailableBranch = chat.slice(
|
||||
chat.indexOf('payload?.code === "tie_break_unavailable"'),
|
||||
chat.indexOf("暂时无法开始参考题"),
|
||||
);
|
||||
assert.doesNotMatch(unavailableBranch, /setError/);
|
||||
assert.match(answer, /continueTieBreakRound\?: boolean/);
|
||||
assert.match(answer, /continueTieBreakRound === true/);
|
||||
assert.match(answer, /isTieBreakRoundSchema/);
|
||||
assert.doesNotMatch(panel, /d9_candidates_differ[\s\S]{0,80}tieBreakAvailable/);
|
||||
assert.doesNotMatch(pool, /export function isTargetedCollectDeclined/);
|
||||
assert.doesNotMatch(pool, /export function isTargetedCollectYearFollowup/);
|
||||
});
|
||||
|
||||
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");
|
||||
assert.match(skill, /^version: 10\.0\.26$/m);
|
||||
assert.match(skill, /有 \/ 没有 \/ 记不清 \/ 这条先跳过/);
|
||||
assert.match(strategy, /有 \/ 没有 \/ 记不清 \/ 这条先跳过/);
|
||||
});
|
||||
@@ -98,17 +98,17 @@ test("system prompt carries only high-priority boundaries, never the method copy
|
||||
test("agent pins the dedicated rectification skill and its fixed version", () => {
|
||||
assert.equal(RECTIFICATION_V9_SKILL_NAME, "jyotish-birth-time-rectification");
|
||||
assert.equal(basename(RECTIFICATION_V9_SKILL_PATH), RECTIFICATION_V9_SKILL_NAME);
|
||||
// 原值: "10.0.24"
|
||||
// 新值: "10.0.25"
|
||||
// 原因: BUG-661/662/663 定向补事改逐条点选,Skill 升版
|
||||
assert.ok(RECTIFICATION_V9_PACKAGE_PATH.endsWith("skills/jyotish-birth-time-rectification/versions/10.0.25"));
|
||||
// 原值: "10.0.25"
|
||||
// 新值: "10.0.26"
|
||||
// 原因: BUG-668 定向补事第四选项写进 Skill
|
||||
assert.ok(RECTIFICATION_V9_PACKAGE_PATH.endsWith("skills/jyotish-birth-time-rectification/versions/10.0.26"));
|
||||
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");
|
||||
// 原值: "10.0.24"
|
||||
// 新值: "10.0.25"
|
||||
// 原因: BUG-661/662/663 定向补事改逐条点选,Skill 升版
|
||||
assert.equal(RECTIFICATION_SKILL_VERSION, "10.0.25");
|
||||
// 原值: "10.0.25"
|
||||
// 新值: "10.0.26"
|
||||
// 原因: BUG-668 定向补事第四选项写进 Skill
|
||||
assert.equal(RECTIFICATION_SKILL_VERSION, "10.0.26");
|
||||
});
|
||||
|
||||
test("step budgets are bounded per action with a hard ceiling", () => {
|
||||
|
||||
@@ -95,15 +95,15 @@ test("terminal transitions are one-way and evidence writes stop at terminal", ()
|
||||
|
||||
test("the active rectification skill pins the v10 identity and lives in the right directory", () => {
|
||||
assert.equal(RECTIFICATION_SKILL_NAME, "jyotish-birth-time-rectification");
|
||||
// 原值: "10.0.24"
|
||||
// 新值: "10.0.25"
|
||||
// 原因: BUG-661/662/663 定向补事改逐条点选,Skill 升版
|
||||
assert.equal(RECTIFICATION_SKILL_VERSION, "10.0.25");
|
||||
// 原值: "10.0.25"
|
||||
// 新值: "10.0.26"
|
||||
// 原因: BUG-668 定向补事第四选项写进 Skill
|
||||
assert.equal(RECTIFICATION_SKILL_VERSION, "10.0.26");
|
||||
assert.match(skill, /^---\nname: jyotish-birth-time-rectification/m);
|
||||
// 原值: "10.0.24"
|
||||
// 新值: "10.0.25"
|
||||
// 原因: BUG-661/662/663 定向补事改逐条点选,Skill 升版
|
||||
assert.match(skill, /^version: 10\.0\.25$/m);
|
||||
// 原值: "10.0.25"
|
||||
// 新值: "10.0.26"
|
||||
// 原因: BUG-668 定向补事第四选项写进 Skill
|
||||
assert.match(skill, /^version: 10\.0\.26$/m);
|
||||
assert.match(skill, /至多一个主问题且唯一来源:[\s\S]*不得自行提出、复述、改写或预告问题/);
|
||||
for (const reference of references) {
|
||||
const content = readFileSync(`${skillDirectory}/references/${reference}`, "utf8");
|
||||
|
||||
@@ -209,8 +209,8 @@ test("open RPC passes the pinned skill and server-derived baseline only", async
|
||||
session_id: SESSION_ID,
|
||||
status: "draft",
|
||||
should_start_opening: true,
|
||||
// 原值: "10.0.24";新值: "10.0.25";原因: 新案绑定现行 Skill
|
||||
skill_version: "10.0.25",
|
||||
// 原值: "10.0.25";新值: "10.0.26";原因: BUG-668 新案绑定现行 Skill
|
||||
skill_version: "10.0.26",
|
||||
};
|
||||
}
|
||||
return null;
|
||||
@@ -248,17 +248,17 @@ test("open RPC passes the pinned skill and server-derived baseline only", async
|
||||
});
|
||||
assert.equal(response.disposition, "created");
|
||||
assert.equal(response.shouldStartOpening, true);
|
||||
// 原值: "10.0.24"
|
||||
// 新值: "10.0.25"
|
||||
// 原因: BUG-661/662/663 定向补事改逐条点选,Skill 升版
|
||||
assert.equal(response.skillVersion, "10.0.25");
|
||||
// 原值: "10.0.25"
|
||||
// 新值: "10.0.26"
|
||||
// 原因: BUG-668 定向补事第四选项写进 Skill
|
||||
assert.equal(response.skillVersion, "10.0.26");
|
||||
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");
|
||||
// 原值: "10.0.24"
|
||||
// 新值: "10.0.25"
|
||||
// 原因: BUG-661/662/663 定向补事改逐条点选,Skill 升版
|
||||
assert.equal(openCall.args.p_skill_version, "10.0.25");
|
||||
// 原值: "10.0.25"
|
||||
// 新值: "10.0.26"
|
||||
// 原因: BUG-668 定向补事第四选项写进 Skill
|
||||
assert.equal(openCall.args.p_skill_version, "10.0.26");
|
||||
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);
|
||||
|
||||
@@ -83,8 +83,8 @@ test("agent body cannot verbally accept a spoken birth window", () => {
|
||||
assert.equal(stripVerbalWindowChange("以你说的为准。"), "");
|
||||
assert.equal(stripVerbalWindowChange("明白了,以你说的为准。"), "明白了。");
|
||||
assert.match(SKILL, /不得回答『以你说的为准』或改写搜索窗口/);
|
||||
// 原值: "10.0.24"
|
||||
// 新值: "10.0.25"
|
||||
// 原因: BUG-661/662/663 定向补事改逐条点选,Skill 升版
|
||||
assert.equal(RECTIFICATION_SKILL_VERSION, "10.0.25");
|
||||
// 原值: "10.0.25"
|
||||
// 新值: "10.0.26"
|
||||
// 原因: BUG-668 定向补事第四选项写进 Skill
|
||||
assert.equal(RECTIFICATION_SKILL_VERSION, "10.0.26");
|
||||
});
|
||||
|
||||
@@ -161,10 +161,10 @@ test("SCORE_DELTA stays ±2/±1 and yearless weight is half", () => {
|
||||
assert.equal(PROBE_WEIGHT.dated, 1);
|
||||
assert.equal(PROBE_WEIGHT.yearless, 0.5);
|
||||
assert.equal(STRONG_CONFLICT_ELIMINATION_COUNT, 3);
|
||||
// 原值: "10.0.24"
|
||||
// 新值: "10.0.25"
|
||||
// 原因: BUG-661/662/663 定向补事改逐条点选,Skill 升版
|
||||
assert.equal(RECTIFICATION_SKILL_VERSION, "10.0.25");
|
||||
// 原值: "10.0.25"
|
||||
// 新值: "10.0.26"
|
||||
// 原因: BUG-668 定向补事第四选项写进 Skill
|
||||
assert.equal(RECTIFICATION_SKILL_VERSION, "10.0.26");
|
||||
});
|
||||
|
||||
test("D9 answer B moves scores by ±1 and does not count toward elimination", () => {
|
||||
|
||||
@@ -85,8 +85,8 @@ test("checked-in registry verifies hashed product packages and leaves consult on
|
||||
[
|
||||
{
|
||||
name: "jyotish-birth-time-rectification",
|
||||
version: "10.0.25",
|
||||
sha256: "263ab00d5a62ee38dde1e13474b50a5e194e412b742d1693acaa9974db9f02e8",
|
||||
version: "10.0.26",
|
||||
sha256: "bb9cf1623db43980568738f5c011d5aae5b1aa4a94c267fad0a85ca276a5ff39",
|
||||
},
|
||||
{
|
||||
name: "jyotish-personal-report",
|
||||
@@ -206,6 +206,17 @@ test("checked-in registry verifies hashed product packages and leaves consult on
|
||||
deprecated1024.sha256,
|
||||
"f4267cb93b459a84ec0006b51501b174d5bbc809b571168307affbaede22e4e7",
|
||||
);
|
||||
const deprecated1025 = resolveExactSkillPackage(
|
||||
"jyotish-birth-time-rectification",
|
||||
"10.0.25",
|
||||
"263ab00d5a62ee38dde1e13474b50a5e194e412b742d1693acaa9974db9f02e8",
|
||||
{ projectRoot },
|
||||
);
|
||||
assert.equal(deprecated1025.status, "deprecated");
|
||||
assert.equal(
|
||||
deprecated1025.sha256,
|
||||
"263ab00d5a62ee38dde1e13474b50a5e194e412b742d1693acaa9974db9f02e8",
|
||||
);
|
||||
});
|
||||
|
||||
test("exact resolution of an old deprecated package is independent of active switches", (t) => {
|
||||
|
||||
Reference in New Issue
Block a user