fix(rectification): keep choice options and scoring on the server
Independent Staging Quality Gate / validate (push) Successful in 9m30s
Independent Staging Quality Gate / publish (push) Successful in 1m50s

Agent set-focus was writing or dropping choice schema, so probes never scored. Force server-owned options, fail closed when focus list RPC errors, and require the probe year in spokenPrompt.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Jesse_Chen
2026-09-02 20:47:16 +08:00
parent 679d773b28
commit 8dfe457f21
17 changed files with 942 additions and 117 deletions
+48
View File
@@ -7569,3 +7569,51 @@
- 复发自:无
- 修复版本:待发布
## BUG-492 | 消息路径 set-focus 用 Agent 选项覆盖服务端 choice schema,探针不计分
- 状态:resolved
- 首次发现:2026-09-02
- 最近更新:2026-09-02
- 影响面:`rectification-set-focus``expectedAnswerSchemaFor``projectCurrentQuestion`、method-followup Agent hint
- 用户现象:答完采集题后的区分题没有 A–D,或点选后候选比较不更新。刷新后同一条助手消息里仍可能只有题干没有选项。
- 触发条件:message 路径 Agent 只传 `spokenPrompt`(或只传 `choice.option_a..d`、或自写 `options[].answer_class`)调用 `rectification-set-focus`,且 `next_followup``choice_frame`
- 根因:`d9404976` 把 set-focus 重新交给 Agent 写题干,但 `execute` 仍用 `parseAgentChoiceCopy(Agent expectedAnswerSchema)` 建 choice。只写 `spokenPrompt` 时 schema=`{prompt}`,投影成采集/空题,idle persist 见 active focus 早退,服务端 choice 永远建不出,该探针不计分。Agent 自写 `answer_class` 则模型拥有映射,违反选项与计分服务端所有。
- 修复:set-focus 按 `next_followup.choice_frame` 强制 `expectedAnswerSchemaFor` / collect schema,忽略 Agent `choice`。无下一问返回 `no_pending_question`。hint 改为「你只写 spokenPrompt」。同探针再 set-focus 走 prompt 外 identity 幂等。
- 验证:`rectification-question-ownership` 锁只传 spokenPrompt → 四选项与 `serverOwnedChoiceCopy` 相等、`probe_id` 非空、`kind===choice`;额外 Agent choice 被忽略;采集题 `collect:true`;无下一问不写 focus;第二次 set-focus 幂等且选项不丢。`rectification-question-in-message` 的 choice fixture 补 `options[].answer_class` 并断言 `kind` 与四选项。
- 防复发:任何路径不得再从 Agent 输入读 `choice`。choice fixture 必须能过 `parseAgentChoiceCopy`。已知边界:用户不答上一题直接发新消息时,旧 focus 仍挂在旧消息、新 turn 无 question(不变量 1 短暂不成立),旧卡仍可点,属可接受。
- 相关记录:BUG-490、BUG-491
- 复发自:BUG-490(题目进消息后开放 Agent set-focus,未改 schema 所有权)
- 修复版本:待发布
## BUG-493 | 题目列表 RPC 失败时 fail-open,聊天里全部题目消失
- 状态:resolved
- 首次发现:2026-09-02
- 最近更新:2026-09-02
- 影响面:`listV10ConversationFocuses`、GET `/api/rectification/cases/[caseId]` `question_source``rectification-agentic-chat`
- 用户现象:迁移未跑或 RPC 权限错误时,历史消息上的题干和选项全部消失,界面只剩「等待服务端更新」,没有失败提示。
- 触发条件:`list_agentic_rectification_conversation_focuses` 返回 error 或抛异常。
- 根因:列表函数失败时静默 `return []`,GET 把空列表当成「没有题」,前端无法区分加载失败与真的缺题。
- 修复:失败时 `console.warn` 并返回 `{ focuses: [], available: false }`。GET 带 `question_source: "unavailable"|"focus"`。前端文案「题目加载失败,请刷新。」与「正在等待服务端更新」分开。
- 验证:`rectification-question-ownership` 锁 RPC error → 列表 `available===false`、turns 上 question 全 null、`question_source==="unavailable"`、warn 含 case 与 reason。GET 仍走 200 组装路径,不把列表失败提升为 503。
- 防复发:题目加载失败不得再吞成空数组。`question_source``unavailable` 时不得显示「服务端更新中」。
- 相关记录:BUG-490、BUG-492
- 复发自:无
- 修复版本:待发布
## BUG-494 | 题干可省略探针年份,嵌入卡又隐藏 why
- 状态:resolved
- 首次发现:2026-09-02
- 最近更新:2026-09-02
- 影响面:`validateSpokenPrompt``rectification-set-focus``VOICE.md`、嵌入 `RectificationChoiceCard`
- 用户现象:区分题看起来像「你有没有一段认真开始或结束的关系?」,用户看不到是哪一年,答错年份会记到错误探针上。
- 触发条件:`followup.probe_year``choice_frame.period` 含年份,但 Agent `spokenPrompt` 不写该年份。嵌入卡 `variant="embedded"` 隐藏 `why`
- 根因:`year_mismatch` 只在题干已经写出 19xx/20xx 时比对;缺年份直接放过。
- 修复:有 `probe_year` 时题干必须包含该年,否则 `year_missing`;无 `probe_year` 但 period 含年份时每个年份都要出现。采集题不加年份要求。VOICE 增加漏写年份坏例。
- 验证:`rectification-spoken-prompt``year_missing` / `year_mismatch` / 通过;嵌入卡测试断言 `question.prompt` 含探针年份(结构断言,不扫正文)。
- 防复发:区分题题干必须能单独看出年份/期间,不得依赖已隐藏的 `why`
- 相关记录:BUG-490、BUG-492
- 复发自:无
- 修复版本:待发布