fix(rectification): ask remaining split lines one card at a time (BUG-661~663)
Independent Staging Quality Gate / validate (push) Successful in 12m9s
Independent Staging Quality Gate / publish (push) Successful in 2m17s

Dated-pool empty now yields existence cards per remaining layer, leftover-candidate copy, and an optional D9/D10 tie-break on the range card. Skill 10.0.25.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Jesse_Chen
2026-09-13 16:50:25 +08:00
co-authored by Cursor
parent b063c66835
commit 530f260f76
63 changed files with 2184 additions and 323 deletions
@@ -1368,6 +1368,35 @@ export function RectificationAgenticChat(props: RectificationAgenticChatProps) {
}
}, [acceptingCandidateId, beginLiveRun, busy, candidateResult, caseId, loadCaseSnapshot, onCompleted, onSaved, readonly, send, sessionId, setPending]);
const requestTieBreak = useCallback(async () => {
if (!candidateResult || acceptingCandidateId || busy || readonly) return;
if (!candidateResult.rangeDelivery?.tie_break_available) return;
setError("");
setPending(true);
try {
const response = await fetch(
`/api/rectification/cases/${encodeURIComponent(caseId)}/tie-break`,
{
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({
sessionId,
requestId: globalThis.crypto.randomUUID(),
}),
},
);
const payload = await response.json().catch(() => null);
if (!response.ok || payload?.ok !== true) {
throw new Error(payload?.error || payload?.message || "暂时无法开始参考题");
}
await loadCaseSnapshot();
} catch (caught) {
setError(caught instanceof Error ? caught.message : "暂时无法开始参考题");
} finally {
setPending(false);
}
}, [acceptingCandidateId, busy, candidateResult, caseId, loadCaseSnapshot, readonly, sessionId, setPending]);
async function copyMessage(message: RenderMessage) {
try {
await navigator.clipboard.writeText(copyTextForMessage(message.text, message.question));
@@ -1795,6 +1824,7 @@ export function RectificationAgenticChat(props: RectificationAgenticChatProps) {
acceptingCandidateId={acceptingCandidateId}
readonly={readonly || regeneratingMessageKey !== null || (busy && !acceptingCandidateId)}
onAccept={(candidateId) => void acceptCandidate(candidateId)}
onTieBreak={() => void requestTieBreak()}
/>
{verifiedIdleCopy && (
<p className="rectification-pending-note" role="status">
@@ -18,11 +18,13 @@ export function RectificationRangeDelivery({
acceptingCandidateId,
readonly,
onAccept,
onTieBreak,
}: Readonly<{
result: RectificationCandidateResult;
acceptingCandidateId: string | null;
readonly: boolean;
onAccept: (candidateId: string) => void;
onTieBreak?: () => void;
}>) {
const delivery: RangeDeliveryProjection | null = result.rangeDelivery;
const range = delivery?.range ?? result.credibleRange;
@@ -47,6 +49,16 @@ export function RectificationRangeDelivery({
{delivery?.narrow_hint ? (
<p className="rectification-range-delivery__narrow">{delivery.narrow_hint}</p>
) : null}
{delivery?.tie_break_available && onTieBreak && !readonly ? (
<button
type="button"
className="rectification-range-delivery__tie-break"
disabled={busy}
onClick={onTieBreak}
>
{RECTIFICATION_USER_COPY.rangeDeliveryTieBreakEntry}
</button>
) : null}
{sharedTraits.length > 0 ? (
<ul className="rectification-range-delivery__shared">
{sharedTraits.map((line) => (