Files
Jyotisha/docs/tasks/TASK-rectification-year-focus-overlay-20260916.md
T
Jesse_ChenandClaude Fable 5.1 abcfdac523 docs(tasks): 年月焦点单追加产品决策:删掉领域/年月录入卡,年月阶段改打字回答
推翻 precision-gate-guided-collect 单 T3 与 SKILL.md:84 的「不得邀请自由打字」,BUG-750 失去对象。

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JUei7K13cYxLHE3Axe4A45
2026-09-16 14:41:00 +00:00

18 KiB
Raw Blame History

任务书 · 删掉录入卡 + 年月阶段焦点被模型追问覆盖:问题块不对齐、答完撞步骤上限(2026-09-16)

0. 基线

  • 基线 commit05cf20fborigin/staging headstaging 已部署同一 SHA,含 BUG-747~752 的引导式补经历)。
  • 分支:codex/rectification-year-focus-overlay-20260916git worktree add -b codex/rectification-year-focus-overlay-20260916 .worktrees/rectification-year-focus-overlay-20260916 origin/staging
  • 串行:与 TASK-console-noise-20260916.md 无文件交集,可并行。
  • BUG 段:BUG-908 起BUG-906/907 已被控制台噪音单占用;开工时核对 docs/BUG_HISTORY.md 最大号)。
  • Skill:本单要改 SKILL.md 的一句话(§5 T1c),按既有规则 bump 到 10.0.29,历史会话可打开的合同测试照跑(BUG-621 教训)。

1. 事故实证(产品负责人 2026-09-16 staging 真机)

对话顺序(用户原话已脱敏,只保留结构):

  1. 服务器定向补事存在题:「身体上有没有过住院、手术、受伤或特别难熬的一段时间,哪一年都算?」A/B/C/D。用户选 A「有,我来填时间」。
  2. 用户在输入框打字给了「YYYY年M月做过手术」。
  3. 助手正文:「记下了。」+ 「YYYY年M月这次手术,是你本人做的吗?」。时间轴「已完成 4 步 / 记下了:YYYY年M月做过手术。」→ 事件确实入账。
  4. 消息底部只读范围行:「目前范围 04:48–05:07,再说一件带年月的事就能继续」。
  5. 消息之后又出现一个独立问题块:同一句「是你本人做的吗?」,下面挂着「哪一类事(学业/事业/搬家/感情/家人/财务/健康)+ 发生年月」录入卡。产品原话:「为什么下面问问题了又出现个选择时间的卡,这个卡没用啊」「rectification-message-question UI 没跟消息对齐」。
  6. 用户回答了这个追问(是/不是一类的短句)→ 整轮报错 「本轮达到步骤上限,状态已记录。」

代码定位

现象 位置 说明
问题块 + 录入卡 frontend/src/components/rectification-agentic-chat.tsx:1741-1780questionGap === "persisted_question" 分支) 渲染 currentQuestion.prompt,且只要 yearEntry 非空就挂 EventDateEntryCardyearEntry = parseYearEntryQuestionId(currentQuestion.question_id):1427),只看题号是不是 collect:targeted:<domain>:year / collect:guided:window:…:year,不看题干写的是什么
题干被模型覆盖 frontend/src/mastra/rectification-v9-tools.ts:1218-1270rectification-set-focus,活动焦点与 input.questionId 同号的分支) withSpokenPrompt({...activeFocus.expectedAnswerSchema}, spoken.prompt)spoken-prompt.ts:99-113):schema 不是 targeted_collect 就直接把 schema.prompt 换成模型的话,event_date_entry: true 原样保留。于是「大概哪年几月?」变成「是你本人做的吗?」,卡还在
校验拦不住 spoken-prompt.ts:24-97 validateSpokenPrompt choice_frame 的采集题只查长度、单问号、领域关键词;「手术」命中健康关键词即通过
年月焦点为什么还活着 rectification-v9-tools.ts:1628-1646batch 写入后 resolve 活动采集焦点,条件 isCollectFocusSchema);collection-question-pool.ts:881-905 pendingTargetedYearDomain(存在题 resolved、领域未覆盖、年月题未关闭 → 继续出 :year 事件已入账(时间轴有回执)但最终画面仍是 :year 焦点。两条可能:(a) 选 A 后由 answer-choice.ts:934 persistNextInterviewAfterChoice 落的年月焦点 schema 不满足 isCollectFocusSchemabatch 没 resolve(b) 模型先 set-focus 后 batch,或 batch 后计划层因事件 domain/kind 没映射到 health 又出了一次 :year执行方必须用回放测试定出是哪一条(§5 T2
追问本身不该有 scripts/rectification/contracts.py:192-200 subject_for_event_domain 默认 self;存在题题干就是「身体上」;rectification-v9-tools.ts:1515 batch 的 subject 默认 self 服务器从没出过「主体」题。模型是按 SKILL.md:100needs_clarification 不得猜补主体」自己加的追问;年月阶段的 hint(method-followup.ts:2493「若用户已说带年份的经历走 batch;否则继续问一件带大概年份的事」)没有禁止追问
不对齐 frontend/src/app/globals.css:2969-2981 .rectification-message-questionmargin-inline-start;同块里的 .rectification-event-entry:3089)自带 margin-inline-start: var(--assistant-content-inset).rectification-question-gap:2985)也带 消息内的同名块(rectification-message-entry.tsx:169,经 chat-message-row.tsx:148afterAnswer)在消息正文里,天然有缩进;独立的 persisted 块在消息外,题干顶到左边、卡片却缩进了,三者三个左边线
步骤上限 agent-run.ts:733 evidence 动作预算 8 步;run-diagnostic.ts:88-90 无正文即 max_stepsagent-run.ts:898-910 host fallback 只在 batch completed 时兜底 用户回答「是/不是」时活动焦点仍是 :year 录入 schema,模型拿到的是「有年份走 batch,否则再问」的 hint,短句没有年月 → batch 写不进(needs_clarification/rejected)→ set-focus 同号幂等 → 循环到 8 步没正文;batch 没 completed 所以 fallback 也不接。这是推断,不是实证,本单按 investigating 记,T3 的回放测试要么复现要么写明复现不了

2. 根因(按层)

  1. 数据层:年月阶段焦点是服务器题(题干固定「大概哪年几月?」,答案走录入卡或带年月的自由文本),但 set-focus 允许模型用同号覆盖它的题干。定向存在题在 BUG-673 已经堵了这条(targeted_collect 只存 spoken_prompt 侧字段,不动 prompt),年月阶段没堵。
  2. 计划层:事件入账后年月焦点没有随之关闭(§1 表 (a)/(b) 二选一,待回放定)。
  3. 提示层:年月阶段的 hint 没说「用户给了年月就到此为止,不追问主体/原因/细节」;SKILL.md:100 的「不得猜补主体」被模型读成「要去问主体」。
  4. 表现层:persisted 问题块没有和消息同一条左边线。

3. 决策记录

  • 产品负责人 2026-09-16 授权:追问句下面不得出现录入卡;问题块与消息对齐;答完不得撞步骤上限。
  • 产品负责人 2026-09-16 追加拍板:「哪一类事 + 发生年月」录入卡整个删掉,年月阶段直接打字回答。 这条推翻 TASK-rectification-precision-gate-guided-collect-20260916.md T3「引导题用选项 + 年/月选择器录入,不得邀请自由打字」与 SKILL.md:84 同一句,也让 BUG-750(录入卡提交文本格式)失去对象。执行方据此删除,不得以旧任务书为由拒改。原则沿用用户偏好「多余入口宁可删除也不修」。
  • 年月阶段的题干归服务器:模型的 spokenPrompt 只能作旁白侧字段,不能替换 prompt。这不推翻 BUG-673(同一原则扩到年月阶段)。
  • 定向健康题主体默认本人,不追问truth-consent-boundaries.md:30「亲属主体必须显式标记」指的是用户自己说了「我妈住院」这类话时要标 family,不是让模型去问。用户原话没有亲属就是 self
  • 用户在年月阶段用自由文本给了年月,等同于录入卡提交;不得再要求用卡。
  • 不改出卡时机、不改 precision_gate、不改 BUG-751 的「线没问完不出卡」。

4. 硬红线

  1. page.tsx 不增长;rectification-agentic-chat.tsx 不新增 hook。
  2. BUG-673 的防复发(定向存在题焦点只允许点选形态;persisted_question 块不得画成卡片绕数据层)原样保留;本单是补年月阶段,不是重写焦点层。
  3. BUG-750 的录入卡提交文本格式不变;BUG-749 的 GUIDED_ANY_DOMAIN 解析不变。
  4. 既有断言只允许因 schema 形状变化而更新,写「原值 / 新值 / 原因」三栏;不得静默弱化。
  5. Skill bump 后 frontend/tests/ 里的历史会话可打开合同测试必须绿(BUG-621)。
  6. 不得用生日推年份;不得因为本单把 subject 追问改成服务器题。

5. 任务分解

T0 删掉录入卡,年月阶段改成打字回答(BUG-908 的一部分,不另占号)

  • 删除文件:frontend/src/components/event-date-entry-card.tsxfrontend/src/components/event-date-picker.tsxglobals.css:3089-3130 一带 .rectification-event-entry* 与选择器样式整段删;rectification-agentic-chat.tsx:1427yearEntry / yearRange:1760-1775 的卡片分支删,persisted 块只剩题干(题干 + 点选卡的分支不动)。
  • 数据层:server-focus.ts:345-352event_date_entry / default_domain / default_year / default_month 不再写;collection-question-pool.tsparseYearEntryQuestionIdeventDateYearRangeformatEventDateEntryMessage 删除。:year 题号与 TARGETED_YEAR_PROMPT「大概哪年几月?」保留,它就是一道口述题,用户打字答;引导窗口题(collect:guided:window:…:year)同样口述,题干里已带年份和月份区间。
  • 文案:choice-card.ts:30/34 选项 A「有,我来填时间」改「有,我来说」;TARGETED_COLLECT_WHY_USER「答有的话再用选择器填年月」改「答有的话直接打字说大概年月」。输入框占位 RECTIFICATION_COLLECT_WAITING_PLACEHOLDER 若提到选择器一并改。对照 frontend/docs/VOICE.md
  • SkillSKILL.md:84「引导题用选项 + 年/月选择器录入,不得邀请自由打字」改为「引导题答『有』后,服务器口述题『大概哪年几月?』,用户打字回答;不要再出点选卡」。与 T1c 同一次 bump 到 10.0.29。
  • 记录:frontend/DESIGN.md 删录入卡段落;docs/BUG_HISTORY.md 的 BUG-750 补一行「2026-09-16 录入卡按产品决策删除,本条防复发『账本文本不得复用题干例子列表』仍适用于任何用户可见文本」;frontend/tests/rectification-guided-collect-20260916.test.ts 里断言卡片的用例删除并在进度记录写「原值 / 新值 / 原因」,总数不减的要求用新增的 T1~T4 契约测试补足。
  • 验收:git grep -n "event_date_entry\|EventDateEntryCard\|EventDatePicker\|rectification-event-entry" frontend/src 为 0 命中;选项 A 点击后快照活动问题为 :year 口述题,题干「大概哪年几月?」,无卡;/ 仍 Static,首屏 gzip 只降不升。

T1 年月阶段焦点不可被模型覆盖(BUG-908)

  • T1a spoken-prompt.ts withSpokenPromptT0 删掉 event_date_entry 后,以题号为准——set-focusquestionIdparseTargetedCollectQuestionId / parseGuidedWindowQuestionId 解析出 stage === "year" 时,与 targeted_collect === true 同等对待,只写 spoken_prompt,不动 prompt(识别以题号前缀为准,与 BUG-673 同口径)。
  • T1b turn-decision.ts projectCurrentQuestion:85 起):年月阶段题投影 prompt 时只用服务器 prompt,忽略 spoken_prompt。前端 persisted_question 分支的题干因此恒为「大概哪年几月?」类服务器题干。
  • T1c 提示层:method-followup.ts:2493 那条 active_focus hint 与 TARGETED_YEAR_PROMPT 所在的年月 followup user_prompt_hint,补一句「用户给了年月就走 batch 写入并结束本题;不追问主体、原因、细节;定向健康题主体默认本人」。SKILL.md:100 改为「needs_clarification 不得猜补日期、主体……;用户原话没有亲属时主体就是本人,不要追问『是不是你本人』」。Skill bump 10.0.29。
  • 验收:
    • 新契约测试:活动焦点为 collect:targeted:health:year 时调用 set-focus,同号、spokenPrompt 为一句不含年月的追问 → 焦点 expected_answer_schema.prompt 不变,spoken_prompt 为模型句;projectCurrentQuestion 得到的 prompt 是服务器题干。
    • rectification-guided-collect-20260916.test.tsrectification-targeted-*.test.ts 原断言绿。

T2 事件入账后年月焦点必须关闭(BUG-909)

  • 先写回放测试定根因:存在题答 A → persistNextInterviewAfterChoice 落年月焦点 → 用 batch 写一件同领域带年月事件 → 断言活动焦点 status=resolved,且 buildMethodFollowupPlannext_followup 不再是该领域 :year
    • 若红在 resolve:修 answer-choice.ts 落年月焦点的 schema,使其满足 isCollectFocusSchema(带 COLLECT_FOCUS_SCHEMA_KEYprompt),或修 batch 的 resolve 条件按题号前缀识别 :year(与 BUG-673「识别以题号前缀为准」同口径)。
    • 若红在计划层:修 coveredCollectKinds / 事件 kind→CollectKind 映射,让「手术/住院」类事件覆盖 health;并在 pendingTargetedYearDomain 里把「该领域已有本轮之后写入的带年月事件」也算 yearClosed
  • 顺带:rectification-propose-evidence(单条写入,rectification-v9-tools.ts:1708 起)现在 resolve 采集焦点,只有 batch 会。让两条写入路径走同一段 resolve 逻辑(抽函数),否则模型走单条工具时 T2 复发。
  • 验收:回放测试绿;两条写入路径都有 resolve 断言;进度记录写明定出的是 (a) 还是 (b)。

T3 年月阶段收到无年月短句不得烧完步骤(BUG-910,先 investigating

  • agent-run.ts:309-335 日期可靠性分类器旁边加同形状的 host 前置:活动焦点题号为年月阶段(:year)且用户消息里没有年份(19xx/20xx 或「x 年」)→ 不进模型,host 直接以服务器题干再问一次(复用 emitVisibleSpoken 路径),不扣点、不占步骤;有年份则照常进模型。
  • 用回放测试复现 §1 的循环:活动 :year 焦点 + 消息「是」→ 修前应到 max_steps(或写明复现不了,改记 investigating 并保留 host 前置作为防御);修后 host 前置接住。
  • 卡片消失的原因也在这条链上:录入卡只跟着快照里活动焦点的 :year 题号走;失败轮的工具写入不回滚(agent-run.ts:1084-1087 直接 failedAttemptpreviousFocusId 只上报不恢复),模型在那 8 步里 set-focus 换题号(supersede)、resolve-focus 或 batch 关掉了年月焦点,轮结束 loadCaseSnapshot 重读后活动焦点已不是它,卡随之消失,而接替的题又没落成。回放测试要断言:失败轮不得只关掉年月焦点而不留下可答的题——要么焦点原样保留(卡还在),要么接替题已持久化。
  • 验收:run-diagnosticmax_steps 映射不变;applyHostFallback 不改条件;新测试绿。

T4 persisted 问题块与消息同一条左边线(BUG-911)

  • globals.css:给 rectification-agentic-chat.tsx:1741 的独立块加修饰类(如 .rectification-message-question.is-standalone),margin-inline-start: var(--assistant-content-inset);块内的 .rectification-choice-card.is-embedded 把自己的 margin-inline-start 归零,避免双重缩进(录入卡已在 T0 删除)。消息内的同名块不动。
  • 验收:chart-page-view.test.tsx 同款的 CSS 声明断言:独立块有 inset、块内卡片 inset 为 0frontend/DESIGN.md 记一句「校正独立问题块与助手正文共用 --assistant-content-inset」。

T5 记录

  • docs/BUG_HISTORY.mdBUG-908911T3 按实证状态写 resolvedinvestigating)。
  • CHANGELOG.md:年月阶段不再被追问覆盖;问题块对齐;Skill 10.0.29。
  • docs/tasks/PROGRESS-rectification-year-focus-overlay-20260916.mdtsc / lint / test 数字与基线失败清单逐条比对、gzip、/ Static。
  • docs/testing/rectification-year-focus-overlay-20260916.md 真机清单(§8)。

6. 让步顺序

  1. T3 的 host 前置若与 date_reliability 分类器抢同一段(两者都看活动焦点 schema),按「先日期可靠性、后年月录入」排序,写进注释。
  2. T2 若两条根因都红,先修 resolve(数据层),计划层的 yearClosed 扩展作第二提交。
  3. T1c 的 Skill 改动若因 hash 合同测试卡住,可只改 hint 不改 SKILL.md,进度记录写明,产品下一轮再 bump。
  4. T4、T5 不可让步。

7. 开工前置命令

git fetch origin --prune
git worktree add -b codex/rectification-year-focus-overlay-20260916 .worktrees/rectification-year-focus-overlay-20260916 origin/staging
cd .worktrees/rectification-year-focus-overlay-20260916/frontend
./node_modules/.bin/tsc --noEmit && npm run lint && npm test 2>&1 | tail -20   # 记基线总数与失败清单
grep -o "BUG-[0-9]\+" ../docs/BUG_HISTORY.md | sort -t- -k2 -n | tail -1      # 核对起始号
grep -rn "10.0.28" ../skills/jyotish-birth-time-rectification/SKILL.md ../frontend/src/lib/rectification-agentic/*.ts | head   # Skill 版本落点

验收口径:tsc 0 错;lint 0 errornpm test 失败清单与基线逐条一致且总数不减;next build/ 仍 Static;首屏 gzip ±2%Python 不动则不跑快速门。

8. 环境缺口(留给产品负责人真机)

  • 定向健康题选 A 后,出现的是一句「大概哪年几月?」,没有「哪一类事 + 发生年月」的卡;在输入框打字给年月后助手只说「记下了」,不追问「是不是你本人」;如有下一题则是下一题。
  • 若助手仍口头追问了别的,回一句短句不应再出现「本轮达到步骤上限」。
  • 任何独立问题块(题干 + 卡)左边线与助手正文对齐。