fix(rectification): pin the adopt card and drop casual adopt copy
Independent Staging Quality Gate / validate (push) Successful in 11m34s
Independent Staging Quality Gate / publish (push) Successful in 1m56s

The time card stayed under an earlier collect question, and the close said to just use a time for now. Keep the card on the delivery turn and present the analysis as this round's result.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Jesse_Chen
2026-09-05 02:03:19 +08:00
co-authored by Cursor
parent 7c776bdd3a
commit 329f8a2109
22 changed files with 402 additions and 52 deletions
@@ -95,7 +95,15 @@ import {
import { ModelSelector } from "./model-selector";
import { RectificationBoard, RectificationBoardPeek } from "./rectification-board";
import { RectificationChoiceCard } from "./rectification-choice-card";
import { copyTextForMessage, parseTurnQuestion, questionIsAnswered, type TurnQuestion } from "@/lib/rectification-agentic/v9/turn-question";
import {
applyLiveCandidateOffer,
copyTextForMessage,
interviewQuestionBlocksAdoptOffer,
parseTurnQuestion,
persistedOfferFromTurn,
questionIsAnswered,
type TurnQuestion,
} from "@/lib/rectification-agentic/v9/turn-question";
import { Button } from "@/components/ui/button";
type PersistedTurn = Readonly<{
@@ -264,9 +272,11 @@ function mergeTurnQuestions(current: RenderMessage[], turns: readonly unknown[])
return {
...message,
question: question ?? undefined,
candidateOffer: next?.offerResultId
? { resultId: next.offerResultId }
: message.candidateOffer,
candidateOffer: persistedOfferFromTurn(
next?.offerResultId,
message.candidateOffer,
true,
),
};
});
}
@@ -609,25 +619,11 @@ export function RectificationAgenticChat(props: RectificationAgenticChatProps) {
if (!canOffer && !adopted) return;
const resultId = candidateResult.resultId;
queueMicrotask(() => {
setMessages((current) => {
if (current.some((message) => message.candidateOffer?.resultId === resultId)) return current;
if (adopted && current.some((message) => message.candidateOffer)) {
return current.map((message) => (
message.candidateOffer ? { ...message, candidateOffer: { resultId } } : message
));
}
if (!canOffer || adopted) return current;
const owner = [...current].reverse().find((message) => (
message.role === "assistant"
&& message.state === "settled"
&& Boolean(message.text)
&& !message.failed
));
if (!owner) return current;
return current.map((message) => (
message.renderKey === owner.renderKey ? { ...message, candidateOffer: { resultId } } : message
));
});
setMessages((current) => applyLiveCandidateOffer(current, {
resultId,
canOffer,
adopted,
}));
});
}, [candidateResult]);
@@ -1375,13 +1371,16 @@ export function RectificationAgenticChat(props: RectificationAgenticChatProps) {
&& regeneratingMessageKey === null,
);
const canOfferCards = canShowRectificationSelectionCards(candidateResult);
const persistedOfferKey = messages.find((message) => message.candidateOffer)?.renderKey;
const persistedOfferKey = [...messages].reverse().find((message) => message.candidateOffer)?.renderKey;
const selectionCardMessageKey = persistedOfferKey
?? (canOfferCards && !candidateResult?.selectedTime ? latestSettledAssistant?.renderKey : undefined);
const showSelectionCards = Boolean(
candidateResult
&& selectionCardMessageKey
&& (canOfferCards || Boolean(candidateResult.selectedTime)),
&& (canOfferCards || Boolean(candidateResult.selectedTime))
&& !messages.some((message) => (
interviewQuestionBlocksAdoptOffer(message.question, Boolean(candidateResult.selectedTime))
)),
);
const showReadonlyRange = Boolean(
canShowRectificationReadonlyRange(candidateResult)