fix(rectification): drop 没有/记不清 collect chips (BUG-626)

Users consistently rejected skip chips on spoken collect. Typing those phrases still declines or skips the domain.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Jesse_Chen
2026-09-09 22:20:35 +08:00
co-authored by Cursor
parent 4067aff86c
commit f0ae8cc45b
8 changed files with 43 additions and 64 deletions
+5
View File
@@ -1,5 +1,10 @@
# 印度占星 Skill 更新日志
## 2026-09-09 — 生时校正拿掉「没有 / 记不清」按钮
口述采集不再出示「没有」「记不清」快捷按钮。想跳过仍可在输入框打这两句,语义不变。选择题卡选项不动。Skill 版本不变。
## 2026-09-09 — 一小时窗不再丢掉后段簇;中途说出时段固定回复不改窗口
生时校正在一小时搜索窗里不再按时间只留前 12 个签名簇。61 分钟网格上的全部簇都会进入比较;超过 64 个时合并相邻低分簇,不会把窗口尾巴整段扔掉。可信区间按簇实际覆盖的分钟来读,不再缩成代表那一分钟。
+20 -4
View File
@@ -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
- 复发自:无
- 修复版本:待发布
@@ -168,6 +168,7 @@
- 这一条消息只触发一次候选比较,账本写入三件
- 下一问不再是已经说过的那三个领域
- 只报一件时也只比较一次,下一问是既有逐领域采集,正文没有「还有吗 / 还能想起别的吗」;第一道逐领域题干末尾可以有一次「想到别的也可以一起说」
- 口述采集问题下方有「没有」「记不清」没有「先这样」输入框上方不再挂这两个按钮
- 口述采集有「没有」「记不清」按钮,也没有「先这样」输入框上方不再挂快捷回答
- 想跳过可以打字「没有」或「记不清」
- 每轮助手正文一句复述(可另接一句范围变化);气泡里没有「本轮对照了…」,也没有「很有帮助 / 很有价值 / 很有分量 / 特别有用」
+1 -1
View File
@@ -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 `<details>` 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.
+1 -1
View File
@@ -45,7 +45,7 @@ Jyotisha 的可见文案是产品的一部分。正确性红线(真实性、
| 生成中输入框变灰,回车没反应 | 生成中仍可打字;回车后显示「已排队,回答结束后发出」,可撤回 | 对标主流对话产品,焦点不丢。 |
| 停止后出现红色告警条 | 已停止,已生成的内容保留;本次不会扣点。 | 停止是用户动作,不是出错。 |
| 也可以再说一件你记得大概时间的事。 | 能分开候选的问题已经问完,先按现有材料给你结果。 / 还差带月份的经历,领域不限。 | 采集问完后交付或说清门槛,不再开放采集。 |
| 口述采集没有停止按钮,范围小字不可点 | 选择题卡内有「先这样,先看当前范围」;生成中有「停止回答」。口述采集问题下方是「没有」「记不清」两个 44px 次要按钮,不是先这样,也不挂在输入框上方。范围小字是状态句,不可点。 | 没有=拒答,记不清=本会话先放着。 |
| 口述采集没有停止按钮,范围小字不可点 | 选择题卡内有「先这样,先看当前范围」;生成中有「停止回答」。口述采集只有输入框,没有「没有」「记不清」芯片,也不挂在输入框上方。范围小字是状态句,不可点。 | 想跳过可以打字「没有」或「记不清」。 |
| 只报一件后追问「还有吗,还能想起别的吗」 | 下一问仍是既有逐领域采集。第一道题干末尾由服务端加一次「想到别的也可以一起说。」 | 邀请不是要求;不插追问轮,也不重复开场。 |
| 点选后旁白写「04:3104:39 这段领先,05:0005:07 这段落后」 | 已记录,范围收到 04:31–04:39。 | 旁白只说范围变没变。领先/落后留在卡内「答了会怎样」,不进气泡。 |
| 每轮只有技法清单,不知道现在第几步 | 输入框上方有「第 N 步 · 原因 · 下一步」。 | 用户始终看得见该做什么。 |
-12
View File
@@ -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 {
@@ -187,34 +187,6 @@ function RectificationReadonlyRange({
);
}
function CollectSpokenReplies({
onChoose,
}: Readonly<{
onChoose: (text: "没有" | "记不清") => void;
}>) {
return (
<div className="rectification-collect-replies" role="group" aria-label="采集题快捷回答">
<Button
type="button"
variant="outline"
className="rectification-collect-reply"
onClick={() => onChoose("没有")}
>
</Button>
<Button
type="button"
variant="outline"
className="rectification-collect-reply"
onClick={() => onChoose("记不清")}
>
</Button>
</div>
);
}
type RectificationAgenticChatProps = Readonly<{
caseId: string;
sessionId: string;
@@ -1701,9 +1673,6 @@ export function RectificationAgenticChat(props: RectificationAgenticChatProps) {
onStop={submitStop}
/>
)}
{liveQuestion && question.kind === "collect_spoken" && (
<CollectSpokenReplies onChoose={(text) => void send("message", text)} />
)}
{question.status === "skipped" && (
<p className="rectification-question-skipped" role="status">
{savedTime ? `已跳过(已采用 ${savedTime}` : "已跳过"}
@@ -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]*</);
assert.match(chat, />[\s\n]*记不清[\s\n]*</);
assert.match(chat, /liveQuestion && question.kind === "collect_spoken"/);
assert.doesNotMatch(afterAnswer, /CollectSpokenReplies/);
assert.doesNotMatch(afterAnswer, /shouldShowCollectSpokenReplies/);
assert.doesNotMatch(chat, /function CollectSpokenReplies/);
assert.doesNotMatch(chat, /rectification-collect-replies/);
assert.doesNotMatch(chat, /rectification-collect-reply/);
assert.doesNotMatch(chat, /onChoose\("没有"\)/);
assert.doesNotMatch(chat, /onChoose\("记不清"\)/);
assert.doesNotMatch(chat, />[\s\n]*没有[\s\n]*</);
assert.doesNotMatch(chat, />[\s\n]*记不清[\s\n]*</);
const styles = readFileSync(new URL("../src/app/globals.css", import.meta.url), "utf8");
assert.match(styles, /\.rectification-collect-reply \{[\s\S]*min-height:\s*44px/);
assert.doesNotMatch(styles, /\.rectification-collect-replies/);
assert.doesNotMatch(styles, /\.rectification-collect-reply/);
assert.equal(parseRectificationChoiceCard(COLLECT_GET_QUESTION), null);
});