fix(rectification): require a renderable card before holdout validation
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -17,6 +17,7 @@ import {
|
||||
import {
|
||||
decideRectification,
|
||||
publicDecisionFields,
|
||||
type HoldoutValidationStatus,
|
||||
type RectificationDecision,
|
||||
} from "../core/rectification-decision.ts";
|
||||
import type { InferenceState } from "../core/types.ts";
|
||||
@@ -110,25 +111,51 @@ export function discriminatorCandidateTimes(
|
||||
return candidateScoresFromDossier(latest).map((item) => item.time);
|
||||
}
|
||||
|
||||
function holdoutStatusFromInference(inference: ReturnType<typeof previousInferenceFromReceipt>) {
|
||||
if (!inference) return "unavailable" as const;
|
||||
const hasHoldout = inference.events.some((item) => item.usage === "holdout");
|
||||
if (!hasHoldout) return "unavailable" as const;
|
||||
if (inference.holdout_passed === true) return "passed" as const;
|
||||
if (inference.holdout_passed === false || inference.result_status === "validation_failed") {
|
||||
return "failed" as const;
|
||||
}
|
||||
return "not_started" as const;
|
||||
function canAskHoldout(input: {
|
||||
events?: readonly Readonly<{ usage: string; year: number | null }>[];
|
||||
oosBlindPrompts?: readonly unknown[] | null;
|
||||
}): boolean {
|
||||
if ((input.oosBlindPrompts?.length ?? 0) > 0) return true;
|
||||
return Boolean(input.events?.some((item) => item.usage === "holdout" && item.year !== null));
|
||||
}
|
||||
|
||||
function holdoutStatusFromState(state: InferenceState) {
|
||||
const hasHoldout = state.events.some((item) => item.usage === "holdout");
|
||||
if (state.holdout_passed === true) return "passed" as const;
|
||||
if (state.holdout_passed === false || state.result_status === "validation_failed") {
|
||||
return "failed" as const;
|
||||
function holdoutValidationStatus(input: {
|
||||
events?: readonly Readonly<{ usage: string; year: number | null }>[];
|
||||
holdoutPassed?: boolean | null;
|
||||
resultStatus?: string | null;
|
||||
oosBlindPrompts?: readonly unknown[] | null;
|
||||
}): HoldoutValidationStatus {
|
||||
if (input.holdoutPassed === true) return "passed";
|
||||
if (input.holdoutPassed === false || input.resultStatus === "validation_failed") {
|
||||
return "failed";
|
||||
}
|
||||
if (hasHoldout) return "not_started" as const;
|
||||
return "unavailable" as const;
|
||||
if (canAskHoldout(input)) return "not_started";
|
||||
return "unavailable";
|
||||
}
|
||||
|
||||
function holdoutStatusFromInference(
|
||||
inference: ReturnType<typeof previousInferenceFromReceipt>,
|
||||
oosBlindPrompts?: readonly unknown[] | null,
|
||||
) {
|
||||
if (!inference) return "unavailable" as const;
|
||||
return holdoutValidationStatus({
|
||||
events: inference.events,
|
||||
holdoutPassed: inference.holdout_passed,
|
||||
resultStatus: inference.result_status,
|
||||
oosBlindPrompts,
|
||||
});
|
||||
}
|
||||
|
||||
function holdoutStatusFromState(
|
||||
state: InferenceState,
|
||||
oosBlindPrompts?: readonly unknown[] | null,
|
||||
) {
|
||||
return holdoutValidationStatus({
|
||||
events: state.events,
|
||||
holdoutPassed: state.holdout_passed,
|
||||
resultStatus: state.result_status,
|
||||
oosBlindPrompts,
|
||||
});
|
||||
}
|
||||
|
||||
export function contrastPacketFromLatestResult(
|
||||
@@ -294,6 +321,9 @@ export function decideFromDossier(
|
||||
options?: { currentEvidenceFingerprint?: string | null },
|
||||
): RectificationDecision {
|
||||
const inference = previousInferenceFromReceipt(dossier.latestResult?.decisionReceipt ?? null);
|
||||
const oosBlindPrompts = refinementFromDecisionReceipt(
|
||||
dossier.latestResult?.decisionReceipt ?? null,
|
||||
).oos_blind_prompts;
|
||||
const trainingGate = trainingScoreableGate(dossier.evidence);
|
||||
const collecting = buildMethodFollowupPlan({
|
||||
evidence: dossier.evidence,
|
||||
@@ -326,7 +356,7 @@ export function decideFromDossier(
|
||||
snapshotCurrent,
|
||||
candidateScores: candidateScoresFromDossier(dossier.latestResult),
|
||||
discriminatorProbe: inspected.selected,
|
||||
holdoutValidation: holdoutStatusFromInference(inference),
|
||||
holdoutValidation: holdoutStatusFromInference(inference, oosBlindPrompts),
|
||||
accepted: Boolean(dossier.case.acceptedTime),
|
||||
inferenceCredibleRange: inference?.credible_range ?? null,
|
||||
}),
|
||||
@@ -366,7 +396,10 @@ export function decideAfterInferenceChange(input: {
|
||||
.filter((item) => item.status !== "eliminated")
|
||||
.map((item) => ({ time: item.time, score: item.posterior_score })),
|
||||
discriminatorProbe: inspected.selected,
|
||||
holdoutValidation: holdoutStatusFromState(input.state),
|
||||
holdoutValidation: holdoutStatusFromState(
|
||||
input.state,
|
||||
refinementFromDecisionReceipt(input.dossier.latestResult?.decisionReceipt ?? null).oos_blind_prompts,
|
||||
),
|
||||
inferenceCredibleRange: input.state.credible_range,
|
||||
userStopped: input.userStopped,
|
||||
accepted: Boolean(input.dossier.case.acceptedTime),
|
||||
|
||||
@@ -74,6 +74,7 @@ import {
|
||||
completeStyleOptions,
|
||||
isRenderableProbe,
|
||||
rankDiscriminatorScore,
|
||||
EXISTENCE_STYLE_OPTIONS,
|
||||
type ProbeStyleOption,
|
||||
} from "./probe-question-contract.ts";
|
||||
import type { SessionOutcomeKind } from "./confirmation-gate.ts";
|
||||
@@ -1032,6 +1033,39 @@ export function buildNextUserAction(input: {
|
||||
return { id: explain.id, user_meaning: explain.user_meaning, on_user_stop: explain };
|
||||
}
|
||||
|
||||
function holdoutAskFields(
|
||||
prompt: OosBlindPrompt | null | undefined,
|
||||
reserved: Readonly<{ domain: string; year: number | null }> | null,
|
||||
): Omit<MethodFollowup, "must_not_label" | "choice_frame"> | null {
|
||||
if (prompt) {
|
||||
return {
|
||||
method_id: "oos_blind",
|
||||
intent: "out_of_sample_check",
|
||||
ask_theme: "holdout",
|
||||
domain: prompt.domain,
|
||||
kind_hint: null,
|
||||
user_prompt_hint: prompt.user_meaning,
|
||||
source: "oos_blind",
|
||||
choice_kind: "existence",
|
||||
style_options: EXISTENCE_STYLE_OPTIONS,
|
||||
};
|
||||
}
|
||||
if (reserved?.year == null) return null;
|
||||
return {
|
||||
method_id: "holdout_validation",
|
||||
intent: "out_of_sample_check",
|
||||
ask_theme: "holdout",
|
||||
domain: reserved.domain,
|
||||
kind_hint: null,
|
||||
user_prompt_hint: `${reserved.year} 年前后这件事还要单独核对一次,不计入候选分数。`,
|
||||
source: "oos_blind",
|
||||
probe_year: reserved.year,
|
||||
year_label: `${reserved.year} 年前后`,
|
||||
choice_kind: "existence",
|
||||
style_options: EXISTENCE_STYLE_OPTIONS,
|
||||
};
|
||||
}
|
||||
|
||||
export function buildMethodFollowupPlan(input: {
|
||||
evidence: readonly MethodFollowupEvidence[];
|
||||
activeFocus?: MethodFollowupFocus | null;
|
||||
@@ -1230,32 +1264,13 @@ export function buildMethodFollowupPlan(input: {
|
||||
}
|
||||
|
||||
if (sessionOutcome === "validate_holdout") {
|
||||
const prompt = input.oosBlindPrompts?.[0] ?? null;
|
||||
const reserved = (input.holdoutEvents ?? []).find((item) => item.year !== null) ?? null;
|
||||
const holdoutNext = prompt
|
||||
? makeFollowup({
|
||||
method_id: "oos_blind",
|
||||
intent: "out_of_sample_check",
|
||||
ask_theme: "holdout",
|
||||
domain: prompt.domain,
|
||||
kind_hint: null,
|
||||
user_prompt_hint: prompt.user_meaning,
|
||||
source: "oos_blind",
|
||||
}, false, true)
|
||||
: reserved
|
||||
? makeFollowup({
|
||||
method_id: "holdout_validation",
|
||||
intent: "out_of_sample_check",
|
||||
ask_theme: "holdout",
|
||||
domain: reserved.domain,
|
||||
kind_hint: null,
|
||||
user_prompt_hint: `${reserved.year} 年前后这件事还要单独核对一次,不计入候选分数。`,
|
||||
source: "oos_blind",
|
||||
}, false, true)
|
||||
: null;
|
||||
const fields = holdoutAskFields(
|
||||
input.oosBlindPrompts?.[0],
|
||||
(input.holdoutEvents ?? []).find((item) => item.year !== null) ?? null,
|
||||
);
|
||||
return {
|
||||
methods,
|
||||
next_followup: holdoutNext,
|
||||
next_followup: fields ? makeFollowup(fields, false, true) : null,
|
||||
deferred_followup: null,
|
||||
session_outcome: sessionOutcome ?? "collect_evidence",
|
||||
stop_domain_rotation: true,
|
||||
@@ -1663,29 +1678,11 @@ export function buildMethodFollowupPlan(input: {
|
||||
source: "nakshatra_boundary",
|
||||
});
|
||||
} else if (input.holdoutValidation === "not_started") {
|
||||
const prompt = input.oosBlindPrompts?.[0];
|
||||
const reserved = (input.holdoutEvents ?? []).find((item) => item.year !== null);
|
||||
if (prompt) {
|
||||
next = makeFollowup({
|
||||
method_id: "oos_blind",
|
||||
intent: "out_of_sample_check",
|
||||
ask_theme: "holdout",
|
||||
domain: prompt.domain,
|
||||
kind_hint: null,
|
||||
user_prompt_hint: prompt.user_meaning,
|
||||
source: "oos_blind",
|
||||
}, false, true);
|
||||
} else if (reserved) {
|
||||
next = makeFollowup({
|
||||
method_id: "holdout_validation",
|
||||
intent: "out_of_sample_check",
|
||||
ask_theme: "holdout",
|
||||
domain: reserved.domain,
|
||||
kind_hint: null,
|
||||
user_prompt_hint: `${reserved.year} 年前后这件事还要单独核对一次,不计入候选分数。`,
|
||||
source: "oos_blind",
|
||||
}, false, true);
|
||||
}
|
||||
const fields = holdoutAskFields(
|
||||
input.oosBlindPrompts?.[0],
|
||||
(input.holdoutEvents ?? []).find((item) => item.year !== null) ?? null,
|
||||
);
|
||||
if (fields) next = makeFollowup(fields, false, true);
|
||||
} else if (horaryStatus === "uncovered") {
|
||||
next = makeFollowup({
|
||||
method_id: "horary",
|
||||
|
||||
Reference in New Issue
Block a user