fix(rectification): invite-first collect, holdout at 4 events, Skill 10.0.23 (BUG-646–648)

Stop domain-wheel collecting and age-band years in prompts. Ask until the training gate, then discriminate until convergence, then deliver a range plus a concrete follow-up. Reserve holdout only with four dated events.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Jesse_Chen
2026-09-11 01:43:02 +08:00
co-authored by Cursor
parent cb04529728
commit dd8f35f7ba
70 changed files with 2576 additions and 1183 deletions
+48
View File
@@ -10036,3 +10036,51 @@
- 相关记录:BUG-644、BUG-626、BUG-627
- 复发自:无
- 修复版本:`a3a51c32`
## BUG-646 | 采集穷尽后无下一问,却显示「没有拿到下一个问题」并说做不了
- 状态:resolved
- 首次发现:2026-09-10
- 最近更新:2026-09-10
- 影响面:`persistExhaustionCollect`、收集问题池、`rectificationQuestionGapState`、Skill 10.0.23
- 用户现象:各领域采集都答完或只有一两件带年月经历时,助手停在门槛句或交付口吻,下面是「没有拿到下一个问题。」,没有结果卡,也没有可继续说的入口。
- 触发条件:训练门仍关且收集池已空;或训练门已开、选择题增益见底。
- 根因:穷尽出口只写「还差 N 件,领域不限」且不落焦点。客户端把「无焦点 + 列表成功」当成问题加载失败。产品不允许「做不了」,也不允许固定题数。
- 修复:训练门关且池空时只写精确缺口句,Case 保持开放,占位「再说一件带年月的事」,客户端进入 `collect_waiting`,不显示「没有拿到下一个问题」。训练门开且增益见底时交付区间卡加一句具体「再补什么」。Skill 升 10.0.23。
- 验证:`rectification-exhaustion-exit-20260906.test.ts``rectification-surface-state.test.ts``rectification-collection-question-pool.test.ts`
- 防复发:训练门关不得出卡、不得出现禁词;无焦点且训练门关不得走 `unavailable`
- 相关记录:BUG-558、BUG-627、BUG-645、BUG-647、BUG-648
- 复发自:BUG-558(关顶不落焦点)
- 修复版本:待提交
## BUG-647 | 恰好三件带月经历时一件被留作 holdout,训练门永远差一件
- 状态:resolved
- 首次发现:2026-09-10
- 最近更新:2026-09-10
- 影响面:`split-holdout.ts``scripts/rectification/case_holdout.py``trainingScoreableGate`、验证报告 Real Case Calibration 行
- 用户现象:按要求给了三件带年月经历,仍到不了选择题,看起来像还差一件。
- 触发条件:带月事件恰好 3 件、2 个种类;旧规则从第 2 件起就留 holdout。
- 根因:`MIN_EVENTS_TO_RESERVE_HOLDOUT=2`,3 件里留 1 件后训练只剩 2,达不到 `MIN_ACCEPTANCE_EVENTS=3`
- 修复:≥4 件才留 1 件 holdout;恰好 3 件全部训练。回执 `holdout.status="not_reserved_min_events"`,报告 Real Case Calibration 写 `not reserved`。确认门与引擎计分不变。
- 验证:`rectification-split-holdout.test.ts``tests/test_candidate_discriminator_contract.py`
- 防复发:3 件必须全进训练;4 件才允许 holdout。
- 相关记录:BUG-646、BUG-648
- 复发自:无
- 修复版本:待提交
## BUG-648 | 开场后按领域轮转盘问,题干年份来自生日而不是用户说过的事
- 状态:resolved
- 首次发现:2026-09-10
- 最近更新:2026-09-10
- 影响面:`collection-question-pool.ts``method-followup.ts``user-copy.ts`、Skill 10.0.23
- 用户现象:刚说完一两件,系统立刻按家人/学业/财务轮着问,题干还带着「2020 年前后」这类不是用户说的年份。
- 触发条件:开场写入第一件带年月经历后,`DATED_COLLECT_ORDER``COLLECT_QUESTION_YEAR_CUE`(年龄段中点)生效。
- 根因:采集顺序按方法覆盖轮转,年份线索来自出生年+典型年龄段。这是上午写进 BUG-642 的口径,产品已撤回。
- 修复:收集池按信息价值排序:邀请「还有吗」→ 用户年份锚定追问 → 无年份通用补问。题干不得出现年龄段年份。邀请拒答不把整个 `other` 列入禁问。
- 验证:`rectification-spoken-collect.test.ts``rectification-collection-question-pool.test.ts``agent-voice-copy-contract.test.ts`
- 防复发:`user-copy.ts` 不得再出现无用户年份的「年前后」;邀请在产出期间必须排第一。
- 相关记录:BUG-642、BUG-546、BUG-641、BUG-646
- 复发自:BUG-642(年份线索口径错误)
- 修复版本:待提交
@@ -0,0 +1,65 @@
# PROGRESS · 生时校正采集流程重设计(2026-09-10)
工作树:`.worktrees/rectification-collection-redesign-20260910`
分支:`codex/rectification-collection-redesign-20260910`
任务书:`docs/tasks/TASK-rectification-collection-redesign-20260910.md`
基线:任务书写 `origin/staging` @ `3cd7a95d`(代码 `52db714b`);开工接到当前 `origin/staging` @ `cb045297`(任务书文档提交,尚未含本单代码)。
代码提交:未提交。
## 开工回执
- 编号 **BUG-646 / 647 / 648**。开工时最大已占用号是 648(646/647 已在任务书占用)。
- 顺序:T1 收集池 → T2 锚定模板 → T3 删年龄段年份线索 → T4 holdout ≥4 → T5 缺口/S3/collect_waiting → T6 Skill 10.0.23 与测试改写。
## 已完成
- **T1 / BUG-648**`collection-question-pool.ts`。S1 按信息价值排序:邀请 `collect:invite:more`(价值 2.0)→ 用户年份锚定 → 无年份 generic。训练门关时 `next_followup` 取池首。邀请拒答不把整个 `other` 列入禁问。
- **T2**:锚定规则用任务书概率表,不是让步里的统一 0.5:毕业→工作 0.8、上大学→搬家 0.6、感情跟进 / 事后追问 0.5。年份只来自账本。焦点 id `collect:anchor:<kind>:<year>``kind_hint` 写成 `anchor:…` / `generic:…` / `invite_more` 以便无 `question_id` 的 SQL 重建仍能记住已问键。
- **T3**:删除 `COLLECT_QUESTION_YEAR_CUE``user-copy.ts` 无「年前后」。年龄段探针年份不入口述题干。
- **T4 / BUG-647**`MIN_EVENTS_TO_RESERVE_HOLDOUT = 4`TS + `case_holdout.py`)。3 件全训练;回执 `not_reserved_min_events`;报告 Real Case Calibration = `not reserved`。确认门与引擎计分未改。
- **T5 / BUG-646**:训练门关且池空 → 精确缺口句、无卡、占位「再说一件带年月的事」、客户端 `collect_waiting`。训练门开且增益见底 → 区间卡 + 「再补什么」。不做暂定采用。职业/占问层在训练门开后仍可问,不恢复家人/财务领域轮转。
- **T6**Skill **10.0.23** live 与 `versions/10.0.23` 逐字节相等。sha256 `91f5839e15514b80fa0370bb8b539686af211082e83bc951dc0e8250cbdbadcb`。10.0.22 仍 deprecated、`e3765b20…`10.0.21 仍 `3ced1073…``resolveExactSkillPackage` 可打开 10.0.21 与 10.0.22。CHANGELOG、BUG-646648、走查 1921、VOICE、DESIGN 已写。
## 三栏(既有断言)
| 位置 | 原值 | 新值 | 原因 |
| --- | --- | --- | --- |
| Skill 当前钉 | 10.0.22 / sha256 `e3765b20…` | 10.0.23 / `91f5839e…` | 改 SKILL.md 必须升版 |
| 方法覆盖顺序(用户可见) | 先走完感情→事业→家人→职业→占问再精度追问 | S1 收集池到训练门 → S2 选择题到收敛/增益见底 → S3 交付 | 产品口径 1 |
| 采集题干年份 | `COLLECT_QUESTION_YEAR_CUE` / 「2021 年前后」 | 删除;只有用户账本年份进锚定题 | 撤回 BUG-642 |
| holdout 起留件数 | `MIN_EVENTS_TO_RESERVE_HOLDOUT=2` | 4 | 3 件全训练(BUG-647 |
| 3 件带月、2 类后的下一动 | `ask_fact_collection`1 件 holdout | 可 `ask_candidate_discriminator` | 同上 |
| 训练门开后 leftover dated collect | 家人/财务轮转,可挡 adopt | `exhaustionSpokenCollectFollowup` 返回 null | BUG-648 |
| 池空且训练门关 | 「还差 N 件,领域不限」或「没有拿到下一个问题」 | 精确缺口句 + `collect_waiting` | BUG-646 |
| 开场后下一问 | 家人/学业领域轮转 | 邀请 `collect:invite:more` | 先等用户说完 |
| 事故夹具 5 件后公开 `can_adopt` | false(财务口述未问完) | true;确认门仍关 | 训练门开后不再轮转采集 |
## 验收命令(实测)
工作树 `codex/rectification-collection-redesign-20260910`,相对 `origin/staging` @ `cb045297`
| 命令 | 结果 |
| --- | --- |
| `cmp skills/jyotish-birth-time-rectification/SKILL.md skills/jyotish-birth-time-rectification/versions/10.0.23/SKILL.md` | 相等 |
| `computeSkillPackageSha256(versions/10.0.23)` | `91f5839e15514b80fa0370bb8b539686af211082e83bc951dc0e8250cbdbadcb` |
| 10.0.22 / 10.0.21 包哈希 | 未改:`e3765b20…` / `3ced1073…` |
| `cd frontend && ./node_modules/.bin/tsc --noEmit` | **0 error** |
| `cd frontend && npm run lint` | **0 error**121 warning(既有债务 + 本单未做无关清理) |
| 相关 glob(97 个文件,分三批,`--test-force-exit --test-timeout=20000` | **1163 pass / 0 fail**417 + 322 + 424)。同类 glob 采集语义单曾记 1156;本单新增 `rectification-collection-question-pool.test.ts`。 |
| `.venv/bin/python -m pytest tests/test_candidate_discriminator_contract.py -q` | **8 passed / 0 fail** |
| `run_quality_gate.py --profile quick --skip-frontend-runtime` | **Quality gate passed.** pytest **731 passed / 1 skipped / 0 fail**263s)。 |
| `git diff --check` | 干净 |
| `cd frontend && npm run build` | **blocked**Turbopack `Symlink [project]/frontend/node_modules is invalid, it points out of the filesystem root`。未核首页 Static / gzip。 |
## 环境缺口
- 无 Docker:未跑 `npm run test:db`。本单未动表。
- 无登录态、无 Chrome:走查第 19~21 条未做。部署后须先打开绑定 10.0.22 的历史校正(BUG-621)。
- `next build` 在本 worktree 因 `node_modules` 软链被 Turbopack 拒绝;未核首页 Static / gzip。
- 未提升 `main`,未 push。
## 未做 / 未改
- 未改引擎计分、未改确认门(唯一分钟仍关)。
- 未加数据库迁移。已问键靠 `kind_hint` 进现有 `target_kind`
- 未改冻结历史包 10.0.21 / 10.0.22 字节。
+1 -1
View File
@@ -100,7 +100,7 @@
| `TASK-rectification-collect-semantics-20260910.md` | `PROGRESS-rectification-collect-semantics-20260910.md` | 产品决策三改:Skill 去掉「财务/健康只有主动说才问」并清空 Python `VOLUNTEER_ONLY`,升 10.0.22(BUG-641);带年份线索的采集题优先、题干带线索不矛盾,推翻 BUG-539 的整句去前缀(BUG-642);删掉「没有/记不清」原字匹配,分类器分 no/unsure 两类(BUG-643 | 待验收 | `52db714b`BUG-641643 |
| `TASK-rectification-superseded-focus-duplicate-20260910.md` | `PROGRESS-rectification-superseded-focus-duplicate-20260910.md` | P0:一轮两次 batch 让感情采集题被顶替成 superseded 行;题号唯一约束含已结束行、汇总只聚合 declined/skipped,计划层看不见→撞号 `duplicate_focus` 被当「已问过」→出口误走交付旁白无卡(BUG-644);交付口吻与 `can_adopt=false` 矛盾(BUG-645) | 待验收(环境缺口:`next build` / 真人走查) | `a3a51c32`BUG-644/645 |
| `TASK-rectification-exhausted-gate-exit-20260910.md` | `PROGRESS-rectification-exhausted-gate-exit-20260910.md` | 五领域全拒答 + 三件带月经历:门槛句后无载体、客户端误报「没有拿到下一个问题」并循环「接着问」(BUG-646);第三件被留作 holdout 导致永远没有选择题(BUG-647)。含产品建议:一次性开放追问 + 终态标记 + 穷尽后释放 holdout | 已被取代(并入采集重设计单) | — |
| `TASK-rectification-collection-redesign-20260910.md` | `PROGRESS-rectification-collection-redesign-20260910.md` | 产品口径:用户先说、系统从已说的事锚定追问、不得用生日推年份、不得说「领域」、永远交付结果。无固定题数:收集问题池按信息价值排序问到训练门开;选择题问到收敛门开或增益见底;门关不出结果只出精确缺口句并保持开放;≥4 件才留 holdout;撤回 BUG-642 年份线索、BUG-646 单终态与「暂定采用」;Skill 10.0.23BUG-646648 | 待执行 | `codex/rectification-collection-redesign-20260910` |
| `TASK-rectification-collection-redesign-20260910.md` | `PROGRESS-rectification-collection-redesign-20260910.md` | 产品口径:用户先说、系统从已说的事锚定追问、不得用生日推年份、不得说「领域」、永远交付结果。无固定题数:收集问题池按信息价值排序问到训练门开;选择题问到收敛门开或增益见底;门关不出结果只出精确缺口句并保持开放;≥4 件才留 holdout;撤回 BUG-642 年份线索、BUG-646 单终态与「暂定采用」;Skill 10.0.23BUG-646648 | 待验收 | `codex/rectification-collection-redesign-20260910` |
### 聊天主链路与首页
@@ -233,23 +233,56 @@
- 「不记得了」看到「记下了,这题先放着。」
- 不得只在整句恰好等于「没有」或「记不清」时才有回执
## 17. 家人采集题带年份线索,别的年份也能答
## 17. (已撤回)家人采集题不再带生日推出来的年份线索
资料与开场同第 0 条。虚构经历。走到家人采集题
原期望「题干含某年前后」已于 2026-09-10 被 `TASK-rectification-collection-redesign-20260910.md` 推翻(撤回 BUG-642)。采集题不得再写年龄段年份。改测第 19–21 条
## 18. 打开绑定 10.0.21 或 10.0.22 的历史校正
从历史对话打开一个创建时绑定 Skill 10.0.21 或 10.0.22 的生时校正。
期望:
- 题干含「某年前后有没有…」,例如「家里在 2021 年前后有没有结婚、添丁或住院?有就说个大概年月,别的年份也行。」
- 不得同时出现「记得大概哪年就行」
- 用另一个年份回答(例如 2018 年家里有人住院)可以记上,不得被题干年份挡掉
## 18. 打开绑定 10.0.21 的历史校正
从历史对话打开一个创建时绑定 Skill 10.0.21 的生时校正。
期望:
- 能打开,不要求等于当前 10.0.22
- 能打开,不要求等于当前 10.0.23
- 错误若出现,画在被点的那一行下面,不像没点到
- **部署后先做这一条**BUG-621
## 19. 只有学业:精确缺口、无卡、输入框仍开放
资料:家人记得大概时间,钟点任意,范围「差不多准」。地点任意公开城市。虚构经历。
开场后只说两件上学的事,例如「2016 年 9 月上大学」和「2020 年 6 月毕业」。再对「还有吗」回「没有了」。后续锚定如果问毕业后工作,也回「没有」或「记不清」,直到不再出新采集题。
期望:
- 助手写精确缺口:记下的是上学的两件,再来一件不是上学的、带年月的事,并给出至少两个具体例子
- 不出结果卡,不说「做不了」「材料不够」「领域不限」「还差 N 件」
- 不出现「没有拿到下一个问题。」
- 输入框占位「再说一件带年月的事」
- Case 仍开放,再说一件带年月的非上学经历后应继续
## 20. 正常路径:三件两领域后进入选择题或交付
资料与开场同第 19 条。虚构经历依次说:
1. 2016 年 9 月上大学
2. 2018 年 7 月第一份工作
3. 2023 年夏天搬家
对「还有吗」可以答「就这些」。
期望:
- 三件记上后出现选择题,或直接给出区间卡
- 不得再按家人→学业→财务轮转盘问
- 题干若有年份,必须来自用户说过的年,不得是生日推出来的「某年前后」
- 交付时有区间卡 + 一句「再补什么能收窄」,不写「任何领域」
## 21. 中途再补一件带年月的事
走完第 20 条出现选择题或区间卡后,再说一件带年月的虚构经历(例如 2024 年 5 月搬家或换工作)。
期望:
- 记下后重算,继续选择题或更新卡片
- 不得把流程关掉,不得说「做不了」