fix(rectification): keep opening greeting across set-focus
set-focus only attaches the question stem. Retracting live Chinese before it wiped the opening hello with an empty replace. Other public tools still retract planning prose. BUG-533. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1,5 +1,9 @@
|
||||
# 印度占星 Skill 更新日志
|
||||
|
||||
## 2026-09-04 — 开场打招呼不再被 set-focus 清掉
|
||||
|
||||
生时校正开场先播出的打招呼会留在同一条消息里。`rectification-set-focus` 只负责题干,不再发空的 `replace` 把正文抹掉。Skill 版本未变。
|
||||
|
||||
## 2026-09-04 — 未确认生时的个人报告不再被候选窗读取打死
|
||||
|
||||
出生时间尚未 confirmed 时,报告会去读校正候选窗。那张表已经收回运行时表权限,读失败却被当成计算不可用,整份报告在排盘前就失败。现在改走只读 RPC;读不到窗口就按无窗口继续生成。Skill 版本未变。
|
||||
|
||||
@@ -8228,3 +8228,19 @@
|
||||
- 相关记录:无
|
||||
- 复发自:无
|
||||
- 修复版本:待发布
|
||||
|
||||
## BUG-533 | 开场打招呼被 set-focus 空 replace 清掉
|
||||
|
||||
- 状态:resolved
|
||||
- 首次发现:2026-09-04
|
||||
- 最近更新:2026-09-04
|
||||
- 影响面:`applyStepAnswerChunk`、`retractSpoken`、开场轮 `answer.delta`
|
||||
- 用户现象:开场先流出「你好,很高兴一起做这次生时校正…」,页面最终只剩「我们慢慢来就好…」和采集题干。
|
||||
- 触发条件:模型在 `rectification-set-focus` 之前写出带句号的中文正文;该步已 `live` 播出。
|
||||
- 根因:任一公开工具的 `tool-call` 都会 `retractLive`,发出 `answer.delta` `{text:"", replace:true}`。set-focus 只是把题干挂到同一条消息,前面的打招呼不是过程自述。
|
||||
- 修复:`rectification-set-focus` 的 call/result 不再收回已播出正文。读诊断、记证据仍收回。
|
||||
- 验证:`rectification-step-answer`:打招呼 + set-focus 为 `none`,其后短句仍 `live`;read-diagnostics 仍 `retract`。
|
||||
- 防复发:set-focus 不得把同轮已播出的正文 replace 成空。不得把这个例外扩到 record/compare/diagnostics。
|
||||
- 相关记录:无
|
||||
- 复发自:无
|
||||
- 修复版本:待发布
|
||||
|
||||
@@ -6,7 +6,8 @@
|
||||
* Once the current step looks like the spoken reply — Chinese sentence end,
|
||||
* or live-unlock after compare/offer/result tools — tokens may stream as
|
||||
* `live`. A later public tool-call retracts that speculative stream so
|
||||
* "Let me set" never stays in `answer.delta`.
|
||||
* "Let me set" never stays in `answer.delta`. Exception: `rectification-set-focus`
|
||||
* only attaches the question stem; live greeting/handoff in the same step stays.
|
||||
*/
|
||||
|
||||
export type StepAnswerChunk = Readonly<{
|
||||
@@ -45,6 +46,11 @@ const SPOKEN_LIVE_UNLOCK_TOOLS = new Set([
|
||||
"rectification-read-diagnostics",
|
||||
]);
|
||||
|
||||
/** set-focus writes the stem onto the same turn; it is not planning prose. */
|
||||
const KEEP_LIVE_SPOKEN_TOOLS = new Set([
|
||||
"rectification-set-focus",
|
||||
]);
|
||||
|
||||
function resetBuffers(state: StepAnswerState): void {
|
||||
state.text = "";
|
||||
state.pieces = [];
|
||||
@@ -153,14 +159,19 @@ export function applyStepAnswerChunk(
|
||||
}
|
||||
case "tool-call":
|
||||
if (isPublicToolCall(chunk, isPublicTool)) {
|
||||
const retracted = retractLive(state);
|
||||
state.calledTool = true;
|
||||
if (KEEP_LIVE_SPOKEN_TOOLS.has(toolName(chunk))) return { kind: "none" };
|
||||
const retracted = retractLive(state);
|
||||
return retracted.kind === "retract" ? retracted : { kind: "none" };
|
||||
}
|
||||
return { kind: "none" };
|
||||
case "tool-result":
|
||||
case "tool-error": {
|
||||
if (isPublicToolCall(chunk, isPublicTool)) state.calledTool = true;
|
||||
if (KEEP_LIVE_SPOKEN_TOOLS.has(toolName(chunk))) {
|
||||
resetBuffers(state);
|
||||
return { kind: "none" };
|
||||
}
|
||||
const discarded = state.calledTool || state.text.length > 0;
|
||||
const retracted = retractLive(state);
|
||||
resetBuffers(state);
|
||||
|
||||
@@ -106,6 +106,42 @@ test("retracts a live spoken prefix if that step later calls a public tool", ()
|
||||
assert.deepEqual(spoken, { kind: "live", text: "诊断已经看过。接下来确认入职年份。" });
|
||||
});
|
||||
|
||||
test("keeps a live opening greeting when the next public tool is set-focus", () => {
|
||||
const state = createStepAnswerState();
|
||||
assert.deepEqual(
|
||||
applyStepAnswerChunk(
|
||||
state,
|
||||
chunk("text-delta", { text: "你好,我们从你最容易想起的经历开始就行。" }),
|
||||
isPublicTool,
|
||||
),
|
||||
{ kind: "live", text: "你好,我们从你最容易想起的经历开始就行。" },
|
||||
);
|
||||
assert.equal(
|
||||
applyStepAnswerChunk(
|
||||
state,
|
||||
chunk("tool-call", { toolName: "rectification-set-focus" }),
|
||||
isPublicTool,
|
||||
).kind,
|
||||
"none",
|
||||
);
|
||||
assert.equal(
|
||||
applyStepAnswerChunk(
|
||||
state,
|
||||
chunk("tool-result", { toolName: "rectification-set-focus" }),
|
||||
isPublicTool,
|
||||
).kind,
|
||||
"none",
|
||||
);
|
||||
assert.deepEqual(
|
||||
applyStepAnswerChunk(
|
||||
state,
|
||||
chunk("text-delta", { text: "我们慢慢来就好——想到哪件就聊哪件,不用一次说完。" }),
|
||||
isPublicTool,
|
||||
),
|
||||
{ kind: "live", text: "我们慢慢来就好——想到哪件就聊哪件,不用一次说完。" },
|
||||
);
|
||||
});
|
||||
|
||||
test("does not live-publish English planning before a tool-call", () => {
|
||||
const state = createStepAnswerState();
|
||||
assert.equal(
|
||||
|
||||
Reference in New Issue
Block a user