From fe5a54e015d1b90761db1b2b83ab287718011c07 Mon Sep 17 00:00:00 2001 From: Jesse_Chen Date: Sun, 13 Sep 2026 12:19:10 +0000 Subject: [PATCH] docs(tasks): P0 brief for dead targeted collect cards (BUG-669~671) Reproduced: with a collect_method_evidence focus open, the plan rebuilds an active_focus followup without a choice_frame, so GET projects no choice_card and the card renders disabled while the page waits for a dated event. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_0193vBv6w5MV2cifdTUu9H5P --- docs/tasks/README.md | 2 + ...ctification-targeted-card-dead-20260913.md | 65 +++++++++++++++++++ 2 files changed, 67 insertions(+) create mode 100644 docs/tasks/TASK-rectification-targeted-card-dead-20260913.md diff --git a/docs/tasks/README.md b/docs/tasks/README.md index 26dc17ed..9d3fe979 100644 --- a/docs/tasks/README.md +++ b/docs/tasks/README.md @@ -182,6 +182,8 @@ | `TASK-rectification-tie-break-entry-fix-20260913.md` | — | 修复单:参考题入口只读 `window_scan` 标志位 → 两道答完后仍显示、再点弹「现在没有可答的参考题」;并按产品拍板改成点一次连出 D9+D10(BUG-666~668) | 待执行 | `codex/rectification-tie-break-entry-fix-20260913` | +| `TASK-rectification-targeted-card-dead-20260913.md` | — | **P0**:定向补事卡在快照投影里拿不到 `choice_card`(承接焦点分支不重建 `choice_frame`),卡片看得见点不动、流程停在采集等待态;模型还会把定向题改写成口述题(BUG-669~671)。先于 tie-break 修复单执行 | 待执行 | `codex/rectification-targeted-card-dead-20260913` | + ## 命名与归档 - 文件名:`TASK--.md`;同主题的修复单加 `-fix`;进度记录同名换前缀。 diff --git a/docs/tasks/TASK-rectification-targeted-card-dead-20260913.md b/docs/tasks/TASK-rectification-targeted-card-dead-20260913.md new file mode 100644 index 00000000..e1b711cd --- /dev/null +++ b/docs/tasks/TASK-rectification-targeted-card-dead-20260913.md @@ -0,0 +1,65 @@ +# TASK · 定向补事卡刷新后点不动(P0,2026-09-13) + +- 基线:`origin/staging` @ `ab1ade59`(已部署) +- 分支:`codex/rectification-targeted-card-dead-20260913`,基于 `origin/staging` +- 执行方:coding agent;验收:Claude +- BUG 编号起点:**BUG-669**(当前最大号 665;666~668 已被 `TASK-rectification-tie-break-entry-fix-20260913.md` 占用) +- 串行:**本单先做**,`TASK-rectification-tie-break-entry-fix-20260913.md` 排在其后(两单都改 `projectRectificationChoiceCard` 相邻逻辑) +- 上游:`TASK-rectification-targeted-collect-cards-20260913.md`(BUG-661~663,`530f260f`)引入的新形态 + +## 1. 事故实证(2026-09-13 真机,`ab1ade59`) + +用户六道带年月题答完后: + +1. 先冒出一道「和页面上其它题 UI 都不一样」的问题,回答后问题消失; +2. 接着出现 `记下了,这方面先跳过。` + `现在还剩 04:51–05:06 里 5 个候选,能把它们分开的是这几条线:…` + 定向卡「收入明显变过或有过大笔进出吗?A/B/C/D」; +3. 这张卡**点不动**,时间轴显示「目前范围 04:48–05:07,再说一件带年月的事就能继续」,流程停住。搬家、添丁两条线再也没被问。 + +## 2. 根因(已用脚本复现,非推测) + +`buildMethodFollowupPlan` 里的「承接当前焦点」分支(`method-followup.ts`,`method_id: "active_focus"` 那段)对 `intent = collect_method_evidence` 的活动焦点重建的 followup **没有 `choice_frame`**——该分支只为 `reverse_verify` / `out_of_sample_check` / `distinguish_candidates` 重建点选。定向补事的存在题是**史上第一个「采集焦点 + 点选卡」**的组合,正好落进这个洞。 + +于是: + +- `projectRectificationChoiceCard` 走到 `if (!frame) return persistedVerifyCard()`,而 `persistedVerifyCard()` 只认 `reverse_verify` / `out_of_sample_check`,返回 `null` → GET 的 `choice_card` 为空。 +- `projectCurrentQuestion` 仍按已持久化 schema 给出 `kind: "choice"` 的 `current_question` → 前端 `liveQuestion` 要求 `choiceCard.focus_id === question.focus_id`,拿不到卡 → `RectificationChoiceCard` 以 `disabled` 渲染。**卡看得见、点不动**,与用户描述一致。 +- 第一次能答,是因为 POST 应答里带着刚落库的焦点直接渲染;一旦页面重新拉快照(轮询/刷新/再进一次会话),卡就死了。 +- 同一分支还要求模型「用 spokenPrompt 自己写题干」,这就是第 1 步里「UI 不一样的那道题」——模型把定向题重写成了口述题。 + +复现(`tsx` 直接调用,`ab1ade59`):活动焦点 = `collect:targeted:relocation`(schema 为服务端四点选、`targeted_collect: true`)时,`plan.next_followup` 返回 `{"method_id":"active_focus", … "choice_frame":null}`,`projectRectificationChoiceCard` 返回 `null`;换成 `:next` 后缀或换成别的线,同样 `null`。 + +## 3. 硬红线 + +- 卡片文案仍是服务端自有(`serverOwnedChoiceCopy`),模型不得改写定向题题干与选项。 +- 不得放宽区分题(`distinguish_candidates`)的身份校验:`semantic_key` / `candidate_split_hash` 全等判定保持不变(BUG-559 / BUG-581 防复发)。 +- 不改 `SCORE_DELTA`、确认门;定向存在题仍 `scoring: false`。 +- 采集焦点撞号仍可落 `:next`(BUG-654),因此投影不得再用「`questionId` 与计划层 frame 全等」做定向卡的唯一身份判据。 + +## 4. 任务分解 + +- **T1(BUG-669)投影按已持久化文案出卡**:`projectRectificationChoiceCard` 增加一条与 `persistedVerifyCard()` 平行的路径——活动焦点 schema 满足「`targeted_collect === true` 且能 `parseAgentChoiceCopy` 出四点选」时,直接按持久化文案投影 `choice_card`(`question_id` 用焦点的、`focus_id` 用焦点的、`scoring: false`、`stop_label` 用「这题跳过」),不依赖计划层重建 frame,也不做 `questionId` 全等比较(`:next` 后缀必须照样出卡)。 + 验收:GET 快照在「定向存在题为活动焦点」时 `choice_card.focus_id === current_question.focus_id`、四个选项、`scoring:false`;`collect:targeted:finance:next` 同样出卡。 +- **T2(BUG-670)承接分支别让模型改写定向题**:`buildMethodFollowupPlan` 的承接焦点分支,识别 `targeted_collect` schema 时重建 `choice_frame`(`buildTargetedCollectExistenceFrame` + 持久化文案),`user_prompt_hint` 改成「照发服务端卡片,不要自己写题干」。 + 验收:活动焦点为定向存在题时 `plan.next_followup.choice_frame` 非空且 `question_id` 等于焦点题号;不再出现模型自写的口述版定向题。 +- **T3(BUG-671)死角要能自救**:`current_question` 是 `choice` 但 `choice_card` 拿不到时,前端不得静默停在采集等待态——进入 `unavailable` 分支给出修复按钮(现有 `repair-exit` 路径),或由服务端重发焦点。 + 验收:人为让 `choice_card` 为空时,界面出现可点的修复入口,不是一条「再说一件带年月的事就能继续」。 +- **T4 回归与记录**:新增 `frontend/tests/rectification-targeted-card-live-20260913.test.ts` 锁 T1/T2(含 `:next`);BUG-669~671 入历史(关联 BUG-661、BUG-654、BUG-591、reverse_verify 投影那条);`docs/testing/rectification-scenarios-20260907.md` 场景 0d 加:**答完一张定向卡后刷新页面,卡片仍可点**、**整轮不得出现看得见点不动的卡**。 + +## 5. 让步顺序 + +T1 不可省(没有它流程仍然死)。T2 次之(不做会继续出现模型自写的口述定向题)。T3 可拆下一单。 + +## 6. 开工前置命令 + +```bash +git fetch origin --prune +git worktree add -b codex/rectification-targeted-card-dead-20260913 .worktrees/rectification-targeted-card-dead-20260913 origin/staging +grep -o "^## BUG-[0-9]*" docs/BUG_HISTORY.md | sort -t- -k2 -n | tail -1 # 应为 665 +cd frontend && npm test -- tests/rectification-targeted-collect-cards-20260913.test.ts tests/rectification-question-ownership.test.ts tests/rectification-v9-contracts.test.ts +``` + +## 7. 验收口径 + +- `tsc --noEmit` 0 错;`npm run lint` 0 error;相关套件 fail=0;测试总数 ≥ `ab1ade59` 实测 3139(其中 27 项为无 Docker 的数据库/部署套件)。 +- `next build` 后 `/` 仍 `○ Static`;首屏 `rootMainFiles` gzip-9 与 `ab1ade59` 的 130872 B 相差 ±2% 内。 +- 部署后真机按场景 0d 走一遍,含刷新页面后继续答。