From f0ae8cc45b818a400c2bd1af8b2d1e81d390b5eb Mon Sep 17 00:00:00 2001 From: Jesse_Chen Date: Wed, 9 Sep 2026 22:15:51 +0800 Subject: [PATCH 1/4] =?UTF-8?q?fix(rectification):=20drop=20=E6=B2=A1?= =?UTF-8?q?=E6=9C=89/=E8=AE=B0=E4=B8=8D=E6=B8=85=20collect=20chips=20(BUG-?= =?UTF-8?q?626)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Users consistently rejected skip chips on spoken collect. Typing those phrases still declines or skips the domain. Co-authored-by: Cursor --- CHANGELOG.md | 5 +++ docs/BUG_HISTORY.md | 24 +++++++++++--- .../rectification-scenarios-20260907.md | 3 +- frontend/DESIGN.md | 2 +- frontend/docs/VOICE.md | 2 +- frontend/src/app/globals.css | 12 ------- .../components/rectification-agentic-chat.tsx | 31 ------------------- .../rectification-spoken-collect.test.ts | 28 ++++++++--------- 8 files changed, 43 insertions(+), 64 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0938a63b..15516c7e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # 印度占星 Skill 更新日志 +## 2026-09-09 — 生时校正拿掉「没有 / 记不清」按钮 + +口述采集不再出示「没有」「记不清」快捷按钮。想跳过仍可在输入框打这两句,语义不变。选择题卡选项不动。Skill 版本不变。 + + ## 2026-09-09 — 一小时窗不再丢掉后段簇;中途说出时段固定回复不改窗口 生时校正在一小时搜索窗里不再按时间只留前 12 个签名簇。61 分钟网格上的全部簇都会进入比较;超过 64 个时合并相邻低分簇,不会把窗口尾巴整段扔掉。可信区间按簇实际覆盖的分钟来读,不再缩成代表那一分钟。 diff --git a/docs/BUG_HISTORY.md b/docs/BUG_HISTORY.md index e3968139..1c607979 100644 --- a/docs/BUG_HISTORY.md +++ b/docs/BUG_HISTORY.md @@ -9393,8 +9393,8 @@ - 根因:BUG-595 删掉输入框上方「先这样」后没有替代采集快捷回答。产品不要示例骨架条,只要「没有 / 记不清」。 - 修复:composer-wrap 内两个 44px 次要按钮,类名 `rectification-collect-reply`,不复用 `rectification-step-state`。「没有」与打字「没有」同一 POST,走 declined,回执「记下了,这方面先跳过。」「记不清」走 skipped,回执「记下了,这题先放着。」精确这两句不调模型。skipped 领域本会话不再采集;采用后核对仍可碰到 skipped,不碰 declined。 - 验证:`frontend/tests/rectification-spoken-collect.test.ts`(按钮只在 collect_spoken、精确「没有 / 记不清」短路分类器、skipped 不再采集、reverse-verify 仍可碰)。 -- 防复发:采集快捷回答不得画成「先这样」,不得用 `rectification-step-state`。不得在回答条预填年份。 -- 相关记录:BUG-546 +- 防复发:采集快捷回答不得画成「先这样」,不得用 `rectification-step-state`。不得在回答条预填年份。后续产品否决该芯片,见 BUG-626。 +- 相关记录:BUG-546、BUG-618、BUG-626 - 复发自:无 - 修复版本:`aa7ccb30` @@ -9599,8 +9599,8 @@ - 根因:BUG-605 把快捷回答挂在 `composer-wrap` 输入框上方,没有跟选择题卡一样嵌进同一条助手气泡。 - 修复:两个按钮改到问题 `afterAnswer` 里,紧挨题干;composer 不再渲染它们。点选仍走原来的「没有 / 记不清」短路。 - 验证:`frontend/tests/rectification-spoken-collect.test.ts`(按钮在 afterAnswer,不在 composer-wrap)。 -- 防复发:采集快捷回答不得回到 composer。选择题卡继续用嵌入卡,口述采集用 `rectification-collect-reply`。 -- 相关记录:BUG-605 +- 防复发:采集快捷回答不得回到 composer。后续产品否决该芯片,见 BUG-626;不得按本条把「没有 / 记不清」按钮加回。 +- 相关记录:BUG-605、BUG-626 - 复发自:无 - 修复版本:待发布 @@ -9715,3 +9715,19 @@ - 相关记录:BUG-623、BUG-572 - 复发自:无 - 修复版本:待发布 + +## BUG-626 | 口述采集「没有 / 记不清」按钮被用户差评 + +- 状态:resolved +- 首次发现:2026-09-09 +- 最近更新:2026-09-09 +- 影响面:口述采集快捷回答、`rectification-agentic-chat.tsx`、`globals.css` +- 用户现象:生时校正口述采集题下面挂着「没有」「记不清」,刚进入开场和后续分领域追问都会出现;用户一致觉得像在鼓励关掉采集。 +- 触发条件:任意 live `collect_spoken` 题。 +- 根因:BUG-605 / BUG-618 把拒答芯片做成默认采集 UI。开场是开放叙述,后续口述题也不是点选题;芯片把「没有」做成最显眼动作。 +- 修复:删除 `CollectSpokenReplies` 及其样式。打字「没有」仍走 declined,「记不清」仍走 skipped。选择题卡选项不动。 +- 验证:`frontend/tests/rectification-spoken-collect.test.ts`(组件、样式、composer 均无芯片;打字短路仍在)。 +- 防复发:口述采集不得再渲染「没有」「记不清」按钮,也不得挂回 composer。 +- 相关记录:BUG-605、BUG-618 +- 复发自:无 +- 修复版本:待发布 diff --git a/docs/testing/rectification-scenarios-20260907.md b/docs/testing/rectification-scenarios-20260907.md index fb405c09..b99a8347 100644 --- a/docs/testing/rectification-scenarios-20260907.md +++ b/docs/testing/rectification-scenarios-20260907.md @@ -168,6 +168,7 @@ - 这一条消息只触发一次候选比较,账本写入三件 - 下一问不再是已经说过的那三个领域 - 只报一件时也只比较一次,下一问是既有逐领域采集,正文没有「还有吗 / 还能想起别的吗」;第一道逐领域题干末尾可以有一次「想到别的也可以一起说」 -- 口述采集问题下方有「没有」「记不清」,没有「先这样」,输入框上方不再挂这两个按钮 +- 口述采集没有「没有」「记不清」按钮,也没有「先这样」;输入框上方不再挂快捷回答 +- 想跳过可以打字「没有」或「记不清」 - 每轮助手正文一句复述(可另接一句范围变化);气泡里没有「本轮对照了…」,也没有「很有帮助 / 很有价值 / 很有分量 / 特别有用」 diff --git a/frontend/DESIGN.md b/frontend/DESIGN.md index 65b522fa..9d7c9751 100644 --- a/frontend/DESIGN.md +++ b/frontend/DESIGN.md @@ -252,7 +252,7 @@ The birth-time rectification session is the consultation transcript plus a house - **Life-event evidence:** after deterministic questionnaire completion, render three structured event rows by default and allow up to six. Each row uses a domain select, a precision select, and a matching year/month/day control; free-form descriptions are not part of scoring. - **Candidate result:** keep the reported range, candidate interval, and active-time status visually separate. Delivery shows one range card: title (range + event count), up to three compare columns (time, relative likelihood, D9/D10/nakshatra traits, event-fit counts, next-12-month windows, “更像这个”), and the representative-minute boundary. An eight-method report sits in a closed `
` fold. Support numbers stay on the house board. Low confidence keeps evidence editing open; medium offers save or add evidence; high uses a separate confirmation action and never labels a column as the true birth time. - **Evidence accessibility:** every row keeps visible labels, validation errors use live regions, add/remove controls retain 44px targets, and scoring/confirmation loading states disable duplicate submission without hiding the existing evidence. -- **One-question guide:** the guided journey renders only the persisted `nextAction` and one server-selected question. A deterministic question is visible immediately; Agent wording may replace it without changing the question identity, domain, precision request, progress, or permissions. The composer explicitly permits an approximate year. Spoken collect, when the focus is live and the turn is not busy, shows two 44px secondary buttons under the spoken question in the same assistant bubble, class `rectification-collect-reply` (not `rectification-step-state`): 「没有」 posts the same declined path as typing 没有; 「记不清」 posts skipped. The composer never hosts those chips. Stop on spoken collect is not a composer button: `CHOICE_STOP_LABEL` (“先这样,先看当前范围”) stays on choice cards, and generating turns keep “停止回答”. The readonly range line is a status sentence, not a stop control. Discriminator cards fold “为什么问这题” under the stem. Hovering or selecting an option does not reveal an `answer_impact` time line. The method sentence (`vargaSentence`) lives in the expanded activity timeline, not in the spoken bubble. The composer has no `rectification-step-state` status sentence and no `rectification-composer-meta`. +- **One-question guide:** the guided journey renders only the persisted `nextAction` and one server-selected question. A deterministic question is visible immediately; Agent wording may replace it without changing the question identity, domain, precision request, progress, or permissions. The composer explicitly permits an approximate year. Spoken collect does not render skip chips; typing 「没有」 still declines the domain and 「记不清」 still skips it. Stop on spoken collect is not a composer button: `CHOICE_STOP_LABEL` (“先这样,先看当前范围”) stays on choice cards, and generating turns keep “停止回答”. The readonly range line is a status sentence, not a stop control. Discriminator cards fold “为什么问这题” under the stem. Hovering or selecting an option does not reveal an `answer_impact` time line. The method sentence (`vargaSentence`) lives in the expanded activity timeline, not in the spoken bubble. The composer has no `rectification-step-state` status sentence and no `rectification-composer-meta`. - **Draft review:** natural-language answers become one inline review card. The evidence domain is read-only and uses its Chinese label; precision controls which exact year, month, or day input is available. Incomplete drafts keep edit and skip paths visible, while confirmation is disabled until the structured date is valid. Status and errors use polite or assertive live regions without clearing the persisted journey. - **Scoring and retry:** `score_pending` is a quiet progress surface with cancellable bounded polling and no manual compare control. `retry_scoring` preserves the confirmed evidence and exposes one explicit retry action. Refresh and device changes resume from the persisted action rather than inferring progress from copy. - **Guided candidate states:** low confidence presents the saved candidate range and either another evidence question or a safe finish; medium confidence can save the range but never apply a representative minute; high confidence names both “候选时间” and “当前排盘使用时间” before explicit confirmation; ready states that the current chart time changed while the original report remains preserved. No state calls a candidate the true birth minute. diff --git a/frontend/docs/VOICE.md b/frontend/docs/VOICE.md index d363f6ca..a3e283bc 100644 --- a/frontend/docs/VOICE.md +++ b/frontend/docs/VOICE.md @@ -45,7 +45,7 @@ Jyotisha 的可见文案是产品的一部分。正确性红线(真实性、 | 生成中输入框变灰,回车没反应 | 生成中仍可打字;回车后显示「已排队,回答结束后发出」,可撤回 | 对标主流对话产品,焦点不丢。 | | 停止后出现红色告警条 | 已停止,已生成的内容保留;本次不会扣点。 | 停止是用户动作,不是出错。 | | 也可以再说一件你记得大概时间的事。 | 能分开候选的问题已经问完,先按现有材料给你结果。 / 还差带月份的经历,领域不限。 | 采集问完后交付或说清门槛,不再开放采集。 | -| 口述采集没有停止按钮,范围小字不可点 | 选择题卡内有「先这样,先看当前范围」;生成中有「停止回答」。口述采集问题下方是「没有」「记不清」两个 44px 次要按钮,不是先这样,也不挂在输入框上方。范围小字是状态句,不可点。 | 没有=拒答,记不清=本会话先放着。 | +| 口述采集没有停止按钮,范围小字不可点 | 选择题卡内有「先这样,先看当前范围」;生成中有「停止回答」。口述采集只有输入框,没有「没有」「记不清」芯片,也不挂在输入框上方。范围小字是状态句,不可点。 | 想跳过可以打字「没有」或「记不清」。 | | 只报一件后追问「还有吗,还能想起别的吗」 | 下一问仍是既有逐领域采集。第一道题干末尾由服务端加一次「想到别的也可以一起说。」 | 邀请不是要求;不插追问轮,也不重复开场。 | | 点选后旁白写「04:31–04:39 这段领先,05:00–05:07 这段落后」 | 已记录,范围收到 04:31–04:39。 | 旁白只说范围变没变。领先/落后留在卡内「答了会怎样」,不进气泡。 | | 每轮只有技法清单,不知道现在第几步 | 输入框上方有「第 N 步 · 原因 · 下一步」。 | 用户始终看得见该做什么。 | diff --git a/frontend/src/app/globals.css b/frontend/src/app/globals.css index 42028953..59c00c08 100644 --- a/frontend/src/app/globals.css +++ b/frontend/src/app/globals.css @@ -3105,18 +3105,6 @@ input:not([type="radio"]):not([type="checkbox"]):not([class^="ant-"]):not([class font-weight: 600; line-height: 1.55; } -.rectification-collect-replies { - display: flex; - flex-wrap: wrap; - gap: var(--space-2); -} -.rectification-collect-reply { - min-height: 44px; - min-width: 44px; - width: auto; - flex: 0 0 auto; - padding: 0 var(--space-4); -} /* The gap between a settled turn and its next question, as the last entry of the transcript: a live timeline row while refetching, then copy and a reload. */ .rectification-question-gap { diff --git a/frontend/src/components/rectification-agentic-chat.tsx b/frontend/src/components/rectification-agentic-chat.tsx index 5038ba43..6d4666ae 100644 --- a/frontend/src/components/rectification-agentic-chat.tsx +++ b/frontend/src/components/rectification-agentic-chat.tsx @@ -187,34 +187,6 @@ function RectificationReadonlyRange({ ); } -function CollectSpokenReplies({ - onChoose, -}: Readonly<{ - onChoose: (text: "没有" | "记不清") => void; -}>) { - return ( -
- - -
- ); -} - - type RectificationAgenticChatProps = Readonly<{ caseId: string; sessionId: string; @@ -1701,9 +1673,6 @@ export function RectificationAgenticChat(props: RectificationAgenticChatProps) { onStop={submitStop} /> )} - {liveQuestion && question.kind === "collect_spoken" && ( - void send("message", text)} /> - )} {question.status === "skipped" && (

{savedTime ? `已跳过(已采用 ${savedTime})` : "已跳过"} diff --git a/frontend/tests/rectification-spoken-collect.test.ts b/frontend/tests/rectification-spoken-collect.test.ts index 19c7af66..955a16b6 100644 --- a/frontend/tests/rectification-spoken-collect.test.ts +++ b/frontend/tests/rectification-spoken-collect.test.ts @@ -116,9 +116,9 @@ test("cases current_question remains the submit contract, not a visual slot", () const wrap = chat.slice(chat.indexOf("className=\"composer-wrap\""), chat.indexOf("className=\"composer-footer\"")); assert.doesNotMatch(wrap, /rectification-collect-stop/); assert.doesNotMatch(wrap, /CHOICE_STOP_LABEL/); - // 旧:输入框上方两个 44px 次要按钮「没有」「记不清」(BUG-605) - // 新:同一组按钮挂在问题气泡 afterAnswer 里,composer 不再托管 - // 原因:BUG-618;不得复用 rectification-step-state + // 旧:口述采集挂「没有」「记不清」芯片(BUG-605 / BUG-618) + // 新:芯片全部去掉;打字「没有 / 记不清」仍走短路 + // 原因:BUG-626,用户一致差评 assert.doesNotMatch(wrap, /rectification-collect-replies/); assert.doesNotMatch(wrap, /rectification-collect-reply/); assert.doesNotMatch(wrap, /send\("message", "没有"\)/); @@ -127,18 +127,18 @@ test("cases current_question remains the submit contract, not a visual slot", () chat.indexOf("const afterAnswer = question"), chat.indexOf("key={message.renderKey}"), ); - assert.match(afterAnswer, /CollectSpokenReplies/); - assert.match(afterAnswer, /send\("message", text\)/); - assert.match(chat, /function CollectSpokenReplies/); - assert.match(chat, /rectification-collect-replies/); - assert.match(chat, /rectification-collect-reply/); - assert.match(chat, /onChoose\("没有"\)/); - assert.match(chat, /onChoose\("记不清"\)/); - assert.match(chat, />[\s\n]*没有[\s\n]*[\s\n]*记不清[\s\n]*[\s\n]*没有[\s\n]*[\s\n]*记不清[\s\n]* Date: Wed, 9 Sep 2026 22:20:35 +0800 Subject: [PATCH 2/4] docs: number collect-chip removal as BUG-628 BUG-626 and BUG-627 are already reserved on staging by the skipped-health holdout brief. Co-authored-by: Cursor --- docs/BUG_HISTORY.md | 10 +++++----- frontend/tests/rectification-spoken-collect.test.ts | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/BUG_HISTORY.md b/docs/BUG_HISTORY.md index 1c607979..78397ae3 100644 --- a/docs/BUG_HISTORY.md +++ b/docs/BUG_HISTORY.md @@ -9393,8 +9393,8 @@ - 根因:BUG-595 删掉输入框上方「先这样」后没有替代采集快捷回答。产品不要示例骨架条,只要「没有 / 记不清」。 - 修复:composer-wrap 内两个 44px 次要按钮,类名 `rectification-collect-reply`,不复用 `rectification-step-state`。「没有」与打字「没有」同一 POST,走 declined,回执「记下了,这方面先跳过。」「记不清」走 skipped,回执「记下了,这题先放着。」精确这两句不调模型。skipped 领域本会话不再采集;采用后核对仍可碰到 skipped,不碰 declined。 - 验证:`frontend/tests/rectification-spoken-collect.test.ts`(按钮只在 collect_spoken、精确「没有 / 记不清」短路分类器、skipped 不再采集、reverse-verify 仍可碰)。 -- 防复发:采集快捷回答不得画成「先这样」,不得用 `rectification-step-state`。不得在回答条预填年份。后续产品否决该芯片,见 BUG-626。 -- 相关记录:BUG-546、BUG-618、BUG-626 +- 防复发:采集快捷回答不得画成「先这样」,不得用 `rectification-step-state`。不得在回答条预填年份。后续产品否决该芯片,见 BUG-628。 +- 相关记录:BUG-546、BUG-618、BUG-628 - 复发自:无 - 修复版本:`aa7ccb30` @@ -9599,8 +9599,8 @@ - 根因:BUG-605 把快捷回答挂在 `composer-wrap` 输入框上方,没有跟选择题卡一样嵌进同一条助手气泡。 - 修复:两个按钮改到问题 `afterAnswer` 里,紧挨题干;composer 不再渲染它们。点选仍走原来的「没有 / 记不清」短路。 - 验证:`frontend/tests/rectification-spoken-collect.test.ts`(按钮在 afterAnswer,不在 composer-wrap)。 -- 防复发:采集快捷回答不得回到 composer。后续产品否决该芯片,见 BUG-626;不得按本条把「没有 / 记不清」按钮加回。 -- 相关记录:BUG-605、BUG-626 +- 防复发:采集快捷回答不得回到 composer。后续产品否决该芯片,见 BUG-628;不得按本条把「没有 / 记不清」按钮加回。 +- 相关记录:BUG-605、BUG-628 - 复发自:无 - 修复版本:待发布 @@ -9716,7 +9716,7 @@ - 复发自:无 - 修复版本:待发布 -## BUG-626 | 口述采集「没有 / 记不清」按钮被用户差评 +## BUG-628 | 口述采集「没有 / 记不清」按钮被用户差评 - 状态:resolved - 首次发现:2026-09-09 diff --git a/frontend/tests/rectification-spoken-collect.test.ts b/frontend/tests/rectification-spoken-collect.test.ts index 955a16b6..7eeb1593 100644 --- a/frontend/tests/rectification-spoken-collect.test.ts +++ b/frontend/tests/rectification-spoken-collect.test.ts @@ -118,7 +118,7 @@ test("cases current_question remains the submit contract, not a visual slot", () assert.doesNotMatch(wrap, /CHOICE_STOP_LABEL/); // 旧:口述采集挂「没有」「记不清」芯片(BUG-605 / BUG-618) // 新:芯片全部去掉;打字「没有 / 记不清」仍走短路 - // 原因:BUG-626,用户一致差评 + // 原因:BUG-628,用户一致差评 assert.doesNotMatch(wrap, /rectification-collect-replies/); assert.doesNotMatch(wrap, /rectification-collect-reply/); assert.doesNotMatch(wrap, /send\("message", "没有"\)/); From 4eea9c10edb5e8313e2cfa1c387c313df3938fa5 Mon Sep 17 00:00:00 2001 From: Jesse_Chen Date: Wed, 9 Sep 2026 14:22:20 +0000 Subject: [PATCH 3/4] =?UTF-8?q?docs(tasks):=20domain=20alias=20audit=20bri?= =?UTF-8?q?ef=20=E2=80=94=20health/health=5Fpressure=20and=20occupation/ot?= =?UTF-8?q?her=20(BUG-628)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_0193vBv6w5MV2cifdTUu9H5P --- docs/tasks/README.md | 1 + ...ctification-domain-alias-audit-20260909.md | 74 +++++++++++++++++++ 2 files changed, 75 insertions(+) create mode 100644 docs/tasks/TASK-rectification-domain-alias-audit-20260909.md diff --git a/docs/tasks/README.md b/docs/tasks/README.md index 9bed1bd5..f4698a7f 100644 --- a/docs/tasks/README.md +++ b/docs/tasks/README.md @@ -91,6 +91,7 @@ | `TASK-rectification-history-open-20260909.md` | `PROGRESS-rectification-history-open-20260909.md` | 历史生时校正打不开:`open_agentic_rectification_case_v2` 把 Case 绑定 Skill 身份与当前注册版本做相等比较,两天内 10.0.15→10.0.19 五次 bump 让所有旧 Case 被拒;错误码未映射成 500;错误只画在首页卡下 → 表现为点了没反应。决策:session 打开用绑定身份(v1 取 Case → 读身份 → 注册表核验 → v2),不改迁移 | 待验收 | `a31a5e24`(BUG-621) | | `TASK-rectification-window-cluster-cap-20260909.md` | `PROGRESS-rectification-window-cluster-cap-20260909.md` | **P0** 真实用户:一小时窗口分 17 个签名簇,`select_signature_representatives` 按时间取前 12 个,14:40 之后整簇丢弃(本机复现表);区间按代表分钟跨度而非簇覆盖;中途说出真实时段时助手口头答应却未改 → 改为固定回复"范围开始时按资料定、中途不改"(产品否决口头改范围);intake 自定义范围等产品答复 | 待验收 | `codex/rectification-window-cluster-cap-20260909`(BUG-623~625,Skill 10.0.20);走查 `docs/testing/rectification-window-cluster-cap-20260909.md` | | `TASK-rectification-skipped-health-deadend-20260909.md` | `PROGRESS-rectification-skipped-health-deadend-20260909.md` | 真实用户:健康题「记不清」后职业答完即断(『没有拿到下一个问题』):holdout 的 `declined` 未归并 health/health_pressure,把跳过的健康线再问一次,撞同 id 焦点 `duplicate_focus` 静默;出口闸门把 `exhausted` 当已交付不修复;『重新加载』只重取快照 | 待执行 | `codex/rectification-skipped-health-deadend-20260909`(BUG-626~627) | +| `TASK-rectification-domain-alias-audit-20260909.md` | `PROGRESS-rectification-domain-alias-audit-20260909.md` | 领域命名审计:健康线 `health`(账本/焦点)与 `health_pressure`(计划/引擎)在十处比较里六处未归并(holdout declined、reverse-verify、conflict probes、probeYearAlreadyCovered、引擎 oos_blind_prompts / _event_years / volunteered);职业线焦点存为 `other` 只靠 questionId 兜底。其余六领域三层同名无问题。决策:TS/Python 各一个归并函数 + 源码合同禁字面量比较 | 待执行 | `codex/rectification-domain-alias-audit-20260909`(BUG-628) | ### 聊天主链路与首页 diff --git a/docs/tasks/TASK-rectification-domain-alias-audit-20260909.md b/docs/tasks/TASK-rectification-domain-alias-audit-20260909.md new file mode 100644 index 00000000..aee7ea0a --- /dev/null +++ b/docs/tasks/TASK-rectification-domain-alias-audit-20260909.md @@ -0,0 +1,74 @@ +# TASK · 领域命名审计:health / health_pressure 与 occupation / other 两组别名在十处比较里各走各的,统一成一处归并(2026-09-09) + +- 基线:`origin/staging` @ `4067aff8` +- 分支:`codex/rectification-domain-alias-audit-20260909`,基于 `origin/staging`;与 `codex/rectification-skipped-health-deadend-20260909`(BUG-626/627)串行——**本单包含它的决策 1**,若它先合入则本单只补其余站点 +- 执行方:coding agent;验收:Claude +- 涉及文件:`frontend/src/lib/rectification-agentic/v9/method-followup.ts`、`core/candidate-contrast-packet.ts`、`v9/server-focus.ts`、`scripts/rectification/event_probes.py`、`scripts/rectification/refinement_packet.py`、`scripts/rectification/contracts.py` +- BUG 编号起点:**BUG-628**(`docs/BUG_HISTORY.md` 当前最大 BUG-627) +- 优先级:**P1**(产品追问"其他领域会不会出现同样的问题":健康线还有六处同类漏洞;职业线一处;其余五个领域全线一致,无问题) + +## 1. 审计结论 + +三套词汇并存: + +| 层 | 健康 | 职业 | +| --- | --- | --- | +| 证据账本(`EVIDENCE_DOMAINS`、引擎事件、`contracts.py`) | `health`(`health_pressure` 仅作 v1 兼容) | `occupation` | +| 焦点 / 拒答记录(DB `target_domain` 约束,`persistableFocusDomain`) | `health` | **`other`** | +| 计划层、引擎探针 / 分盘层 / 盘外提示、题干与关键词表 | `health_pressure` | `occupation` | + +教育、事业、感情、迁居、财务、家人六个领域三层同名,逐站核过,**没有同类问题**。 + +## 2. 逐站清单(健康) + +| # | 位置 | 比较的两边 | 后果 | 现状 | +| --- | --- | --- | --- | --- | +| H1 | `method-followup.ts::holdoutFollowupFor` `!declined.has(item.domain)` | 拒答记录 `health` vs 盘外提示 `health_pressure` | 拒答 / 跳过的健康线被当作"没用过"再问,撞同 id 焦点静默 | BUG-626 | +| H2 | 同函数 `occupied` | 账本 `health` vs 提示 `health_pressure` | 已修(BUG-590 决策 4) | 已修 | +| H3 | `remainingReverseVerifyProbes` L718 `declined.has(probe.domain)` | 拒答 `health` vs 引擎探针 `health_pressure` | 采用后核对仍会问拒答过的健康线 | 未修 | +| H4 | `remainingConflictProbes` L789 同上 | 同上 | 区分题仍会问拒答过的健康线 | 未修 | +| H5 | `probeYearAlreadyCovered` L427 `item.domain !== domain` | 账本 `health` vs 探针 `health_pressure` | 用户已报 2024-10 受伤,仍可能被问"2024 年 10 月前后有没有生病受伤" | 未修 | +| H6 | 引擎 `oos_blind_prompts` `covered` 用事件域 | 事件 `health` vs 目录 `health_pressure` | 有健康事件时仍下发"还没用过健康压力这条线"(真实 Case JSON 已见) | 未修 | +| H7 | 引擎 `_event_years(events, "health_pressure")` → `_existence_blocked_years` | 同上 | 已报年份不阻断健康探针,出重复 / 引导题 | 未修 | +| H8 | 引擎 `volunteered` 集合(`VOLUNTEER_ONLY` 含 `health_pressure`) | 事件 `health` 永远不等于 `health_pressure` | 用户主动报了健康事件,D30 层仍被当"未主动提及"永不进入探针顺序——健康线被系统性低用 | 未修 | +| H9 | `candidate-contrast-packet.ts` L237 `item.domain === "health_pressure" \|\| HEALTH_RE.test(summary)` | 用摘要正则补别名 | 能工作,但是靠文案猜;摘要不含关键词就漏 | 改为归并 | +| H10 | `datedCollectDomainBlocked` / `hasConfirmedHealth` / `declinedHealth` | 已归并 | — | 已修 | + +## 3. 逐站清单(职业) + +| # | 位置 | 后果 | 现状 | +| --- | --- | --- | --- | +| O1 | `declinedDomains` 对职业焦点得到 `other`,`declined.has("occupation")` 永远为假(L1348 / L1386 / L1957 三处) | 靠 `occupationCollectFocusClosed` 按 `questionId` 前缀兜底;L1957 的兜底还额外要求 `careerCovered`——事业未覆盖时拒答职业会被再问 | 部分修 | +| O2 | `declinedDomains` 把职业拒答记成 `other` 塞进集合 | 目前无人查 `declined.has("other")`,无害 | 顺手清理 | + +## 4. 决策记录 + +1. **一个归并函数,两侧各一份,语义相同。** TS:`canonicalCollectDomain(raw, questionId?)`:`health` → `health_pressure`;`target_domain === "other"` 且 `questionId` 以 `collect:occupation:` 开头 → `occupation`;其余原样。放在 `v9/domain-alias.ts`,`method-followup.ts`、`candidate-contrast-packet.ts` 全部经它比较;`hasConfirmedHealth` / `declinedHealth` 改为它的薄封装并保留。Python:`event_probes.py` 加 `canonical_domain(raw)`(`health` → `health_pressure`),`_event_years`、`oos_blind_prompts.covered`、`volunteered` 三处经它;`contracts.py` 的 v1 兼容项不动。 +2. **DB 不改**:`target_domain` 约束仍是 `health` / `other`;归并只发生在读取侧。`persistableFocusDomain` 不变。 +3. **H8 单独确认产品口径**:健康线本来就是"只有用户主动说才问"(SKILL.md §5),修完归并后主动报过健康事件的用户会开始收到 D30 层探针——这是原设计意图,不是新行为;进度记录写明。 +4. 不动门槛、四选项合同、`SCORE_DELTA`;Skill 不 bump。 + +## 5. 任务分解 + +- 5.1 TS 归并函数 + H1/H3/H4/H5/H9/O1 六站改用;用例——每站一条:账本 `health` + 拒答 `health` + 探针 `health_pressure` → 不再问;职业拒答(topic `other` + `collect:occupation:` id)→ `declined.has("occupation")` 语义成立且事业未覆盖时也不再问职业。 +- 5.2 Python 归并 + H6/H7/H8;用例(`tests/test_rectification_event_probes.py`)——事件域 `health` 2024-10 → `oos_blind_prompts` 不含 `health_pressure`;`_existence_blocked_years("health_pressure")` 含 2024;`volunteered` 含 `health_pressure`,D30 进入顺序。 +- 5.3 源码合同:`agent-voice-copy-contract` 或新合同测试锁"`method-followup.ts` 里除归并函数外不得出现字面量 `"health"` 比较"(`grep -n '"health"' ` 只允许在 `domain-alias.ts`)。 +- 5.4 记录:BUG-628(关联 BUG-590 决策 4、BUG-626、BUG-586 决策 2);`CHANGELOG.md`;`PROGRESS-…`;`CONTEXT.md` 术语表补一条"领域别名:health ≡ health_pressure,occupation 在焦点层存为 other"。 + +## 6. 让步顺序 + +5.1 与 5.2 必做同一提交;5.3 必做(防第三次复发);5.4 不可省。 + +## 7. 开工前置命令 + +```bash +git fetch origin --prune +git worktree add -b codex/rectification-domain-alias-audit-20260909 .worktrees/rectification-domain-alias-audit-20260909 origin/staging +cd .worktrees/rectification-domain-alias-audit-20260909 +ln -s /workspace/Jyotisha/frontend/node_modules frontend/node_modules +ln -s /workspace/Jyotisha/.venv .venv +grep -n '"health"' frontend/src/lib/rectification-agentic/v9/method-followup.ts frontend/src/lib/rectification-agentic/core/candidate-contrast-packet.ts scripts/rectification/event_probes.py scripts/rectification/refinement_packet.py +cd frontend && ./node_modules/.bin/tsc --noEmit; npm run lint 2>&1 | tail -1 +ls tests/rectification-*.test.ts | grep -v database | xargs npx tsx --test 2>&1 | grep -E "^# (tests|pass|fail)" +cd .. && .venv/bin/python -m pytest tests/test_rectification_event_probes.py tests/test_rectification_v5_services.py -q +``` From f4fc17ac518e8abdba43eb94f612b7df4d5709ab Mon Sep 17 00:00:00 2001 From: Jesse_Chen Date: Wed, 9 Sep 2026 14:28:53 +0000 Subject: [PATCH 4/4] docs(tasks): yearless personality probes downgraded to tie-breakers + offline calibration (BUG-629) Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_0193vBv6w5MV2cifdTUu9H5P --- docs/tasks/README.md | 1 + ...ation-yearless-probe-downgrade-20260909.md | 52 +++++++++++++++++++ 2 files changed, 53 insertions(+) create mode 100644 docs/tasks/TASK-rectification-yearless-probe-downgrade-20260909.md diff --git a/docs/tasks/README.md b/docs/tasks/README.md index f4698a7f..68e20ae8 100644 --- a/docs/tasks/README.md +++ b/docs/tasks/README.md @@ -92,6 +92,7 @@ | `TASK-rectification-window-cluster-cap-20260909.md` | `PROGRESS-rectification-window-cluster-cap-20260909.md` | **P0** 真实用户:一小时窗口分 17 个签名簇,`select_signature_representatives` 按时间取前 12 个,14:40 之后整簇丢弃(本机复现表);区间按代表分钟跨度而非簇覆盖;中途说出真实时段时助手口头答应却未改 → 改为固定回复"范围开始时按资料定、中途不改"(产品否决口头改范围);intake 自定义范围等产品答复 | 待验收 | `codex/rectification-window-cluster-cap-20260909`(BUG-623~625,Skill 10.0.20);走查 `docs/testing/rectification-window-cluster-cap-20260909.md` | | `TASK-rectification-skipped-health-deadend-20260909.md` | `PROGRESS-rectification-skipped-health-deadend-20260909.md` | 真实用户:健康题「记不清」后职业答完即断(『没有拿到下一个问题』):holdout 的 `declined` 未归并 health/health_pressure,把跳过的健康线再问一次,撞同 id 焦点 `duplicate_focus` 静默;出口闸门把 `exhausted` 当已交付不修复;『重新加载』只重取快照 | 待执行 | `codex/rectification-skipped-health-deadend-20260909`(BUG-626~627) | | `TASK-rectification-domain-alias-audit-20260909.md` | `PROGRESS-rectification-domain-alias-audit-20260909.md` | 领域命名审计:健康线 `health`(账本/焦点)与 `health_pressure`(计划/引擎)在十处比较里六处未归并(holdout declined、reverse-verify、conflict probes、probeYearAlreadyCovered、引擎 oos_blind_prompts / _event_years / volunteered);职业线焦点存为 `other` 只靠 questionId 兜底。其余六领域三层同名无问题。决策:TS/Python 各一个归并函数 + 源码合同禁字面量比较 | 待执行 | `codex/rectification-domain-alias-audit-20260909`(BUG-628) | +| `TASK-rectification-yearless-probe-downgrade-20260909.md` | `PROGRESS-rectification-yearless-probe-downgrade-20260909.md` | 产品拍板:无年月性格题(D9/D10 风格、月宿边界)降级为平局裁决——带年月题问完且候选仍分不开才问,分值减半(±1)、不计淘汰,报告标「参考」;三列卡性格描述不动;新增离线命中率测量脚本 + 导出 SQL(有出生证用户,聚合);Skill 10.0.21 | 待执行 | `codex/rectification-yearless-probe-downgrade-20260909`(BUG-629) | ### 聊天主链路与首页 diff --git a/docs/tasks/TASK-rectification-yearless-probe-downgrade-20260909.md b/docs/tasks/TASK-rectification-yearless-probe-downgrade-20260909.md new file mode 100644 index 00000000..9c347000 --- /dev/null +++ b/docs/tasks/TASK-rectification-yearless-probe-downgrade-20260909.md @@ -0,0 +1,52 @@ +# TASK · 无年月的性格题降级为平局裁决:带年月题问完且候选仍分不开时才问,分值减半,永不参与淘汰,报告标「参考」;并加一条离线测量(2026-09-09) + +- 基线:`origin/staging` @ `4eea9c10` +- 分支:`codex/rectification-yearless-probe-downgrade-20260909`,基于 `origin/staging`;与 BUG-626~628 无文件重叠(本单不碰 `holdoutFollowupFor` / 别名归并) +- 执行方:coding agent;验收:Claude +- 涉及文件:`frontend/src/lib/rectification-agentic/core/types.ts`(`SCORE_DELTA`、`STRONG_CONFLICT_ELIMINATION_COUNT` 不改,新增 `PROBE_WEIGHT`)、`core/apply-probe-outcome.ts`(L70 `varga_style` 识别、`directionFor`)、`core/build-state.ts`(淘汰计数)、`v9/method-followup.ts`(L1142 的 ×0.8、无年份题在计划里的位置)、`v9/decision-from-dossier.ts`(`nakshatraProbeIfFollowupCanAsk`、`discriminatorProbeIfFollowupCanAsk`)、`v9/skill-verification-report.ts`(技法审计表)、`v9/divergence-panel.ts`(三列卡不动)、`scripts/rectification/varga_style_calibration_report.py`(新,离线测量)、`skills/jyotish-birth-time-rectification/SKILL.md` §5(一句口径) +- BUG 编号起点:**BUG-629**(`docs/BUG_HISTORY.md` 当前最大 BUG-628) +- 优先级:**P1**(产品 2026-09-09 拍板:无年月性格题的分辨力从未验证,不能与带年月题同权,也不能单独淘汰分钟) + +## 1. 现状 + +| 题型 | 来源 | 现在的地位 | +| --- | --- | --- | +| 婚恋风格(D9 上升星座 → 相处方式三选一) | `varga_style`,需有带年月感情事件锚点(BUG-559),排序分 ×0.8 | 与带年月题同样 ±2,冲突计入淘汰(3 次淘汰) | +| 做事风格(D10) | 同上,需事业事件锚点 | 同上 | +| 月宿边界两组性格 | `nakshatra_boundary` | 同上(只偏置窗口,但分值机制相同) | +| 其他分盘的无年份对照(D24/D12/D11/D7/D2/D4/D5) | `yearless_ungrounded_contrast` | 已丢弃,不问 | + +分盘星座是确定计算;"星座 ↔ 性格"与"用户自评 ↔ 类型"两层从未在任何数据上验证。20 例公开集只测带年月事件的打分。真实用户案例里头两道题就是性格题,参与了把真实分钟排除的过程。 + +## 2. 决策记录(产品同意 2026-09-09) + +1. **只做平局裁决。** 计划层:`varga_style` 与 `nakshatra_boundary` 题只在「可问的带年月区分题(`dasha_boundary / dasha_activation / known_event_quality`)为空」**且**「active 候选 ≥ 2 且未分开」时才成为 `next_followup`;否则记 `dropped_probes(reason="yearless_deferred")`。锚点要求(BUG-559)保留。 +2. **分值减半,不淘汰。** 新增 `PROBE_WEIGHT: { dated: 1, yearless: 0.5 }`;`applyProbeOutcome` 对 `varga_style` / `nakshatra_boundary` 的 delta 乘 0.5(±1),且**不递增 `strong_conflict_count`**(`STRONG_CONFLICT_ELIMINATION_COUNT = 3` 不变,只有带年月题的 conflict 计数)。`rounds.kind` 对这类题记 `tie_break`。 +3. **报告与旁白标「参考」。** 技法审计表里 `D9 / D10 类型对照`、`月宿边界` 的 status 写 `reference`,note"性格自评,只作排序参考,不参与淘汰";点选后旁白维持"已记录,范围…"。Skill §5 加一句:"性格类点选题只在带年月题问完仍分不开时出现,分值减半、不淘汰。"(bump 10.0.21,一句)。 +4. **三列对比卡的性格描述不动**:那是"若是这一分钟你会怎样"的展示,不是证据;边界句已在。 +5. **离线测量。** 新脚本 `scripts/rectification/varga_style_calibration_report.py`:输入一份由管理员从数据库导出的匿名 JSON(字段:`case_id` 哈希、`birth_time_source`、记录分钟、每道 `varga_style` / `nakshatra` 答案的 `answer_class` 与该题各选项对应星座),只对 `birth_time_source = hospital_record` 且 `uncertainty ≤ 2 分钟` 的 Case,计算"用户所选选项的星座 == 记录分钟的分盘星座"的命中率,按 D9 / D10 / 月宿分组,输出 n 与命中率,**不输出任何单人行**。导出 SQL 写在 `docs/operations/varga-style-calibration-export.md`,由产品负责人在有数据库权限的环境跑。命中率 ≈ 1/3(三选一)即随机,届时按产品决定删题。 +6. 不动 `SCORE_DELTA` 数值、四选项合同(BUG-390)、采用/确认门、`MIN_SEPARATION_LEAD`、`_relative_support`。 + +## 3. 任务分解 + +- 3.1 决策 1:计划层排序与守卫;用例——存在可问的 2023.05 事业题时 D9 题不出;带年月题问完、剩 2 候选 → D9 题出;剩 1 候选 → 不出且 `yearless_deferred` 记录。 +- 3.2 决策 2:核心层权重;用例——D9 答 B:命中候选 +1、冲突候选 −1、`strong_conflict_count` 不变;三道性格题全冲突也不淘汰;带年月题行为与今天逐字节相同(`rectification-probe-replay-loss-20260908.test.ts` 不改断言)。三栏:原值"±2、计淘汰"→ 新值"±1、不计"→ 决策 2。 +- 3.3 决策 3:报告行 + Skill 10.0.21(快照、registry、`skill-registry.test.ts`)。 +- 3.4 决策 5:脚本 + 单元测试(合成 JSON:命中率 0.33 与 0.9 两组)+ 导出 SQL 文档。 +- 3.5 记录:BUG-629(关联 BUG-559 锚点、BUG-560 校准、BUG-623 真实用户案例);`CHANGELOG.md`;`PROGRESS-…`;`docs/testing/rectification-scenarios-20260907.md` 加"性格题只在带年月题问完后出现;报告里标参考"。 + +## 4. 让步顺序 + +3.2 最先(一处改动即止损);3.1 必做;3.3 必做;3.4 可后置为 P2 但导出 SQL 文档不可省;3.5 不可省。 + +## 5. 开工前置命令 + +```bash +git fetch origin --prune +git worktree add -b codex/rectification-yearless-probe-downgrade-20260909 .worktrees/rectification-yearless-probe-downgrade-20260909 origin/staging +cd .worktrees/rectification-yearless-probe-downgrade-20260909 +ln -s /workspace/Jyotisha/frontend/node_modules frontend/node_modules +ln -s /workspace/Jyotisha/.venv .venv +cd frontend && ./node_modules/.bin/tsc --noEmit; npm run lint 2>&1 | tail -1 +ls tests/rectification-*.test.ts tests/skill-registry.test.ts | grep -v database | xargs npx tsx --test 2>&1 | grep -E "^# (tests|pass|fail)" +```