fix(rectification): keep an open out-of-sample A–D card tappable
GET was dropping choice_card when the recast had no choice_frame, so the embedded options rendered disabled. Replay the persisted reverse-verify / out-of-sample schema instead of silencing the live question. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -524,6 +524,38 @@ export function mergeChoiceCard(
|
||||
};
|
||||
}
|
||||
|
||||
export function choiceCardFromPersistedVerifyCopy(input: {
|
||||
copy: AgentChoiceCopy;
|
||||
questionId: string;
|
||||
methodId: string;
|
||||
scoring: boolean;
|
||||
probeId: string | null;
|
||||
caseRevision: number | null;
|
||||
focusId: string;
|
||||
}): RectificationChoiceCard | null {
|
||||
if (!input.copy.prompt.trim() || !input.questionId.trim()) return null;
|
||||
if (!isPersistedFocusId(input.focusId)) return null;
|
||||
return {
|
||||
question_id: input.questionId,
|
||||
method_id: input.methodId,
|
||||
prompt: input.copy.prompt,
|
||||
why: "",
|
||||
varga: null,
|
||||
choice_mode: CHOICE_MODE,
|
||||
options: input.copy.options.map((option) => ({
|
||||
...option,
|
||||
role: "primary" as const,
|
||||
})),
|
||||
stop_label: CHOICE_SKIP_QUESTION_LABEL,
|
||||
stop_message: CHOICE_SKIP_QUESTION_MESSAGE,
|
||||
scoring: input.scoring,
|
||||
probe_id: input.probeId,
|
||||
case_revision: input.caseRevision,
|
||||
focus_id: input.focusId,
|
||||
choice_kind: "existence",
|
||||
};
|
||||
}
|
||||
|
||||
export function isHoldoutVerificationQuote(quote: string): boolean {
|
||||
return quote.includes(HOLDOUT_MESSAGE_PREFIX);
|
||||
}
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
/**
|
||||
* Public interview projection for the Case GET payload.
|
||||
*
|
||||
* The tap card is shown only when the follow-up is a discriminator
|
||||
* (candidates already diverge or holdout) and the Agent wrote choice copy.
|
||||
* The tap card is shown when the rebuilt follow-up has a matching
|
||||
* discriminator frame, or when the open focus is already a reverse-verify /
|
||||
* out-of-sample A–D schema (even if the recast has no choice_frame).
|
||||
* Card identity is the persisted focus UUID plus the inference revision.
|
||||
*/
|
||||
|
||||
|
||||
@@ -71,6 +71,7 @@ import {
|
||||
parseAgentChoiceCopy,
|
||||
preferConcreteChoicePrompt,
|
||||
mergeChoiceCard,
|
||||
choiceCardFromPersistedVerifyCopy,
|
||||
serverOwnedChoiceCopy,
|
||||
type RectificationChoiceCard,
|
||||
type RectificationChoiceFrame,
|
||||
@@ -2194,11 +2195,37 @@ export function projectRectificationChoiceCard(
|
||||
? input.activeFocus.id.trim()
|
||||
: "";
|
||||
if (!isPersistedFocusId(focusId)) return null;
|
||||
const followup = plan.next_followup ?? plan.deferred_followup ?? null;
|
||||
if (!followup) return null;
|
||||
const frame = followup.choice_frame;
|
||||
if (!frame) return null;
|
||||
const schema = input.activeFocus?.expectedAnswerSchema ?? null;
|
||||
const schemaCopy = parseAgentChoiceCopy(schema);
|
||||
const intent = input.activeFocus?.intent ?? "";
|
||||
const verifyOnly = intent === "reverse_verify" || intent === "out_of_sample_check";
|
||||
// Recast can drop choice_frame when the schema has no probe_year. The open
|
||||
// A–D schema is still the live question; GET must not silence it.
|
||||
const persistedVerifyCard = (): RectificationChoiceCard | null => {
|
||||
if (!verifyOnly || !schemaCopy) return null;
|
||||
const parsed = parsePersistedFollowupQuestionId(input.activeFocus?.questionId);
|
||||
const questionId = input.activeFocus?.questionId?.trim() ?? "";
|
||||
const probeId = schema && typeof schema === "object" && typeof (schema as { probe_id?: unknown }).probe_id === "string"
|
||||
? (schema as { probe_id: string }).probe_id
|
||||
: null;
|
||||
return choiceCardFromPersistedVerifyCopy({
|
||||
copy: {
|
||||
...schemaCopy,
|
||||
prompt: overlayChoicePromptFromSpoken(schemaCopy.prompt, input.latestAssistantText),
|
||||
},
|
||||
questionId,
|
||||
methodId: parsed?.method_id
|
||||
?? (intent === "out_of_sample_check" ? "oos_blind" : "reverse_verify"),
|
||||
scoring: parsed?.scoring !== false,
|
||||
probeId,
|
||||
caseRevision: input.caseRevision ?? null,
|
||||
focusId,
|
||||
});
|
||||
};
|
||||
const followup = plan.next_followup ?? plan.deferred_followup ?? null;
|
||||
if (!followup) return persistedVerifyCard();
|
||||
const frame = followup.choice_frame;
|
||||
if (!frame) return persistedVerifyCard();
|
||||
const schemaRow = schema && typeof schema === "object" && !Array.isArray(schema)
|
||||
? schema as Record<string, unknown>
|
||||
: null;
|
||||
|
||||
Reference in New Issue
Block a user