fix(rectification): keep a live analyzing step and drop the range-stop exits
A completed tool left only checkmarks under 正在分析, so the run looked stuck. The composer and scoring-card 先这样 buttons were unused early exits. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -4,7 +4,7 @@ import test from "node:test";
|
||||
import { createElement } from "react";
|
||||
import { renderToString } from "react-dom/server";
|
||||
|
||||
import { ConsultationRunTimeline, timelineSummaryLabel } from "../src/components/consultation-run-timeline.tsx";
|
||||
import { ConsultationRunTimeline, timelineSummaryLabel, visibleTimelineRows } from "../src/components/consultation-run-timeline.tsx";
|
||||
import { latestAssistantView, type ChatMessage } from "../src/lib/chat-message-view.ts";
|
||||
|
||||
const read = (path: string) => readFileSync(new URL(`../${path}`, import.meta.url), "utf8");
|
||||
@@ -69,6 +69,17 @@ test("the timeline collapses in place with a 180ms height transition and honours
|
||||
assert.match(live, /aria-expanded="true"/);
|
||||
assert.match(live, /正在处理…/);
|
||||
assert.match(live, /inline-spinner/);
|
||||
const liveAfterTool = renderToString(createElement(ConsultationRunTimeline, {
|
||||
rows: [{ id: "read", kind: "calculate", status: "done", label: "读取校正记录" }],
|
||||
live: true,
|
||||
}));
|
||||
assert.match(liveAfterTool, /读取校正记录/);
|
||||
assert.match(liveAfterTool, /正在分析…/);
|
||||
assert.match(liveAfterTool, /inline-spinner/);
|
||||
assert.deepEqual(
|
||||
visibleTimelineRows([{ id: "read", kind: "calculate", status: "done", label: "读取校正记录" }], true).map((row) => row.status),
|
||||
["done", "live"],
|
||||
);
|
||||
const settled = renderToString(createElement(ConsultationRunTimeline, {
|
||||
rows: [{ id: "method", kind: "method", status: "done", label: "已加载方法" }],
|
||||
live: false,
|
||||
|
||||
@@ -404,6 +404,11 @@ test("chat owns adopt cards on the offering message and drops the list-end hando
|
||||
assert.doesNotMatch(chat, /用这个时间看盘/);
|
||||
assert.match(chat, /改选/);
|
||||
assert.match(chat, /CHOICE_STOP_LABEL/);
|
||||
assert.doesNotMatch(chat, /rectification-collect-stop/);
|
||||
assert.match(
|
||||
readFileSync(new URL("../src/components/rectification-choice-card.tsx", import.meta.url), "utf8"),
|
||||
/stop_label !== CHOICE_STOP_LABEL/,
|
||||
);
|
||||
assert.match(chat, /已跳过(已采用/);
|
||||
assert.match(chat, /rectificationLiveProgressLabel/);
|
||||
assert.doesNotMatch(chat, /rectification-consult-handoff/);
|
||||
|
||||
@@ -101,6 +101,7 @@ test("cases current_question remains the submit contract, not a visual slot", ()
|
||||
assert.doesNotMatch(chat, /finalizeRectificationSpokenAndThinking/);
|
||||
assert.doesNotMatch(chat, /rectification-agentic\/v9\/spoken-answer/);
|
||||
assert.doesNotMatch(chat, /message\.text\.(?:includes|match|search)\(/);
|
||||
assert.doesNotMatch(chat, /rectification-collect-stop|showCollectStop|submitCollectStop/);
|
||||
assert.equal(parseRectificationChoiceCard(COLLECT_GET_QUESTION), null);
|
||||
});
|
||||
|
||||
|
||||
@@ -119,13 +119,17 @@ test("stopping keeps what streamed and says so; a 402 explains itself before lea
|
||||
});
|
||||
|
||||
test("the choice card confirms the tap and the board's first state shows the declared minute", () => {
|
||||
// BUG-508
|
||||
assert.match(choiceCard, /<Check aria-hidden="true" \/>/);
|
||||
assert.match(choiceCard, /已选择/);
|
||||
// BUG-508 / BUG-517: selected fill stays on the option; the Check + 已选择 badge is gone.
|
||||
assert.doesNotMatch(choiceCard, /<Check\b/);
|
||||
assert.doesNotMatch(choiceCard, /已选择/);
|
||||
assert.doesNotMatch(choiceCard, /rectification-choice-card__selected/);
|
||||
assert.match(choiceCard, /data-selected=\{selectedKey === option\.key \? "true" : "false"\}/);
|
||||
assert.match(choiceCard, /rectification-choice-card__pending/);
|
||||
assert.match(choiceCard, /InlineSpinner size=\{12\}/);
|
||||
assert.match(styles, /\.rectification-choice-card__selected \{/);
|
||||
assert.doesNotMatch(styles, /\.rectification-choice-card__selected/);
|
||||
assert.match(styles, /\.rectification-choice-card__pending \{/);
|
||||
// BUG-518: scoring cards do not render 先这样,先看当前范围; reverse_verify still has 这题跳过.
|
||||
assert.match(choiceCard, /stop_label !== CHOICE_STOP_LABEL/);
|
||||
// BUG-509
|
||||
assert.match(board, /declaredTime: string \| null;/);
|
||||
assert.match(board, /rectificationBoardEmptyCopy\(declaredTime\)/);
|
||||
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
RECTIFICATION_TIMELINE_SOURCE_LIMIT,
|
||||
rectificationTimelineRows,
|
||||
} from "../src/lib/rectification-timeline-adapter.ts";
|
||||
import { RECTIFICATION_ANALYZING_LIVE_LABEL } from "../src/lib/rectification-activity-labels.ts";
|
||||
import { PUBLIC_RECTIFICATION_METHODS } from "../src/lib/rectification-agentic/v9/public-receipt.ts";
|
||||
|
||||
const read = (path: string) => readFileSync(new URL(`../${path}`, import.meta.url), "utf8");
|
||||
@@ -93,7 +94,8 @@ test("a phase label that names work under way becomes the live row when no tool
|
||||
});
|
||||
assert.equal(composing[0]?.kind, "write");
|
||||
|
||||
// A done label left on the activity after a tool completed is not a live step.
|
||||
// A done label left on the activity after a tool completed is not a live step
|
||||
// when there is no completed trail yet.
|
||||
const doneLabel = rectificationTimelineRows({
|
||||
trace: emptyActivityTrace(),
|
||||
receipt: undefined,
|
||||
@@ -112,6 +114,42 @@ test("a phase label that names work under way becomes the live row when no tool
|
||||
}).length, 1);
|
||||
});
|
||||
|
||||
test("a completed collect tool keeps a live analyzing row until the next tool or answer", () => {
|
||||
let trace = startActivityTraceStep(emptyActivityTrace(), "rectification-read-case", "正在读取校正记录…", 1);
|
||||
trace = completeActivityTraceStep(trace, "rectification-read-case", "读取校正记录");
|
||||
const rows = rectificationTimelineRows({
|
||||
trace,
|
||||
receipt: { steps: ["rectification-read-case"], methods: [] },
|
||||
activity: { phase: "loading-method", label: "读取校正记录" },
|
||||
settled: false,
|
||||
});
|
||||
assert.equal(rows.length, 2);
|
||||
assert.equal(rows[0]?.status, "done");
|
||||
assert.equal(rows[0]?.label, "读取校正记录");
|
||||
assert.deepEqual(rows[1], {
|
||||
id: RECTIFICATION_TIMELINE_LIVE_ID,
|
||||
kind: "think",
|
||||
status: "live",
|
||||
label: RECTIFICATION_ANALYZING_LIVE_LABEL,
|
||||
});
|
||||
|
||||
const withProgress = rectificationTimelineRows({
|
||||
trace,
|
||||
receipt: { steps: ["rectification-read-case"], methods: [] },
|
||||
activity: { phase: "evidence-validation", label: RECTIFICATION_ANALYZING_LIVE_LABEL },
|
||||
settled: false,
|
||||
});
|
||||
assert.equal(withProgress[1]?.kind, "think");
|
||||
assert.equal(withProgress[1]?.label, RECTIFICATION_ANALYZING_LIVE_LABEL);
|
||||
|
||||
assert.equal(rectificationTimelineRows({
|
||||
trace,
|
||||
receipt: { steps: ["rectification-read-case"], methods: [] },
|
||||
activity: { phase: "evidence-validation", label: RECTIFICATION_ANALYZING_LIVE_LABEL },
|
||||
settled: true,
|
||||
}).length, 1);
|
||||
});
|
||||
|
||||
test("after attempt.reset the timeline is empty, and a settled reply without a receipt renders no rows", () => {
|
||||
assert.deepEqual(rectificationTimelineRows({ trace: emptyActivityTrace(), receipt: undefined, activity: undefined, settled: false }), []);
|
||||
assert.deepEqual(rectificationTimelineRows({ trace: undefined, receipt: { steps: [], methods: [] }, activity: undefined, settled: true }), []);
|
||||
@@ -122,10 +160,13 @@ test("both chat surfaces render one timeline, one live marker, and no second rec
|
||||
assert.match(chatSource, /timeline: rectificationTimelineRows/);
|
||||
assert.doesNotMatch(chatSource, /rectification-activity-failure|<CompletedActivityReceipt/);
|
||||
assert.doesNotMatch(chatSource, /label: "正在组织回答…",\s*\}\),\s*\}\s*:\s*message;/);
|
||||
assert.match(chatSource, /rememberLiveActivity\(RECTIFICATION_ANALYZING_LIVE_LABEL, null\)/);
|
||||
assert.doesNotMatch(chatSource, /rectification-collect-stop|showCollectStop|submitCollectStop/);
|
||||
assert.doesNotMatch(rowSource, /ConsultationThinkingReport|showReport|activityTrace/);
|
||||
assert.doesNotMatch(activitySource, /thinking-orbs|ThinkingOrb|\bTraceStep\b|activityTrace/);
|
||||
assert.match(activitySource, /<InlineSpinner size=\{12\} \/>/);
|
||||
assert.doesNotMatch(packageJson, /thinking-orbs/);
|
||||
assert.doesNotMatch(globalStyles, /\.conversation\.is-rectification \.message-assistant \.agent-thinking-(step|marker)/);
|
||||
assert.doesNotMatch(globalStyles, /rectification-activity-receipt|rectification-activity-failure|consultation-step-tree/);
|
||||
assert.doesNotMatch(globalStyles, /rectification-collect-stop/);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user