From 728ead8844631747b13346354e7a5fc37a72800c Mon Sep 17 00:00:00 2001
From: jesse-ux
Date: Sun, 25 Oct 2026 06:29:38 +0800
Subject: [PATCH] =?UTF-8?q?=E6=A0=A1=E6=AD=A3=E7=9A=84=E9=80=89=E9=A1=B9?=
=?UTF-8?q?=E5=8D=A1=E7=89=87=E6=8D=A2=E6=88=90=E7=BB=84=E4=BB=B6=E5=BA=93?=
=?UTF-8?q?=EF=BC=88#110=EF=BC=89=E3=80=82?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
题目、计分、扣点和保存没有改。明确说要校正的人仍然直接开始,没做完的仍然接着做。选项改用单选标签,确定程度改用圆点,其余按钮改用已有组件。动效只淡入。
自测:tsc 通过;frontend lint 0 error(122 条原有警告);校正前端测试 2291 条,通过 2270、失败 21,与改前名单一致;Python test_rectification_*.py 26 个文件退出码 0;next build 后 / 仍是 Static;首屏 JS gzip-9 644683,相对 1516ad56 记下的 642174 增加 0.39%。隐私标记测试通过。
Co-Authored-By: Grok (agent loop)
---
CHANGELOG.md | 8 ++
docs/BUG_HISTORY.md | 16 ++++
...ROGRESS-ui-kit-6-rectification-20261023.md | 71 +++++++++++++++
docs/testing/ui-kit-migration-20261023.md | 11 +++
frontend/DESIGN.md | 2 +
frontend/eslint/ui-primitive-whitelist.json | 14 +--
frontend/src/app/birth-time-choice.css | 11 +--
frontend/src/app/globals.css | 27 ++++--
.../birth-time-candidate-result.tsx | 15 ++--
.../components/birth-time-choice-question.tsx | 81 ++++++++++-------
.../birth-time-evidence-draft-card.tsx | 41 +++++----
.../src/components/birth-time-guide-turn.tsx | 9 +-
frontend/src/components/birth-time-intake.tsx | 88 +++++++++++--------
.../birth-time-legacy-rectification.tsx | 5 +-
.../components/birth-time-rectification.tsx | 9 +-
.../src/components/rectification-board.tsx | 11 ++-
.../components/rectification-choice-card.tsx | 65 +++++++++-----
.../rectification-composer-reopen.tsx | 5 +-
.../src/components/rectification-fade.tsx | 50 +++++++++++
.../rectification-range-delivery.tsx | 6 +-
.../rectification-segment-delivery.tsx | 5 +-
.../tests/rectification-agentic-entry.test.ts | 5 +-
22 files changed, 400 insertions(+), 155 deletions(-)
create mode 100644 docs/tasks/PROGRESS-ui-kit-6-rectification-20261023.md
create mode 100644 frontend/src/components/rectification-fade.tsx
diff --git a/CHANGELOG.md b/CHANGELOG.md
index f4d7d73c..af8f857e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,13 @@
# 印度占星 Skill 更新日志
+## 2026-10-25 — 生时校正的选项卡片换成组件库
+
+- 校正里的选项仍是圆角卡片。点中之后是浅底描边。确定程度仍是三个圆点,大概范围仍是一排标签。
+- 盘面的关闭仍是一枚叉。重新打开、采用和其余确认都换成了同一套按钮。
+- 题目出现时只淡入。系统开了减少动态效果时同样只淡入,没有位移,也没有来回循环。
+- 题目、计分、扣点和保存都没有改。明确说要校正的人仍然直接开始,没做完的仍然接着做。
+- Skill 版本不变(BUG-1438)。
+
## 2026-10-25 — 报告、星历和登录页换成组件库
- 报告的中文和 English 是一排分段。切换之后,页面上不留下上一种语言的正文。
diff --git a/docs/BUG_HISTORY.md b/docs/BUG_HISTORY.md
index b9c870a7..409be2db 100644
--- a/docs/BUG_HISTORY.md
+++ b/docs/BUG_HISTORY.md
@@ -19494,4 +19494,20 @@
- 复发自:无
- 修复版本:分支 `codex/ui-kit-5-pages-20261023`。
+## BUG-1438 | 生时校正界面换成组件库
+
+- 状态:enhancement
+- 首次发现:2026-10-25
+- 最近更新:2026-10-25
+- 影响面:校正里的选项卡片、确定程度、大概范围、经历草稿的精度、盘面的关闭和重新打开、采用按钮。题目怎么出、怎么计分、怎么落库、怎么扣点,都不在这次的改动里。校正入口也不在这次:明确要求校正的人仍然直接开始,没做完的仍然接着做。
+- 用户现象:选项仍是原来的圆角卡片,点中后浅底描边。确定程度仍是三个圆点。范围仍是一排标签。盘面关闭仍是一枚叉。题目出现时,如果动效已经载入,只淡入;系统开了减少动态效果时同样只淡入,没有位移,也没有循环。
+- 触发条件:打开一次生时校正,或在建档时填写出生时间的确定程度。
+- 根因:不是缺陷。组件库迁移的生时校正这一张(任务书 K6,决策 D4、D7、D9)。
+- 修复:选项卡片和大概范围改用单选标签组。确定程度改用单选圆点。其余确认、关闭、重新打开和采用改用已有按钮。经历草稿的精度改用已有下拉,可见文字仍是「记得的精度」。动效用已有的展开预设,只淡入。校正对话仍在首页里,没有为了体积把整段校正拆出首页。盘面探头仍挂在顶栏原来的位置,没有改成弹窗。
+- 验证:见 `docs/tasks/PROGRESS-ui-kit-6-rectification-20261023.md`。没有登录态,不把浏览器里走完一次校正写成已经做过。
+- 防复发:选项卡片的合同仍锁住选中态、不出现「已选择」、不在卡片上抢焦点。白名单测试锁住名单只减不增。入口行为仍由 #103 的合同锁住,本张不改那条路径。
+- 相关记录:issue #110。任务书 `docs/tasks/TASK-ui-kit-migration-20261023.md` 的 K6。校正入口沿用 #103,不另开编号。
+- 复发自:无
+- 修复版本:分支 `codex/ui-kit-6-rectification-20261023`。
+
diff --git a/docs/tasks/PROGRESS-ui-kit-6-rectification-20261023.md b/docs/tasks/PROGRESS-ui-kit-6-rectification-20261023.md
new file mode 100644
index 00000000..58ffe11b
--- /dev/null
+++ b/docs/tasks/PROGRESS-ui-kit-6-rectification-20261023.md
@@ -0,0 +1,71 @@
+# PROGRESS · 组件库 K6 生时校正界面(2026-10-25)
+
+工单 #110。分支 `codex/ui-kit-6-rectification-20261023`。基线 `origin/staging` `1516ad56ec66c5e80ef2a1f0ef4d82152b163699`(#109 报告、星历、登录和 #103 校正入口都在这个提交里)。只做任务书 **K6 生时校正界面**。K7 没收尾。没有重做校正入口、报告、登录、星盘页。
+
+## 结论
+
+校正的选项卡片换成了组件库的单选标签。题目、计分、扣点和保存都没改。明确说要校正的人仍然直接开始,没做完的仍然接着做。
+
+## 做了什么
+
+- 选项卡片(引导题和对话里的判断卡)改用 `ToggleGroup`,一次只选一个。原来的圆角、浅底描边、题干和选项原文都留着。不写「已选择」,卡片自己也不抢焦点。
+- 「你对这个时间有多确定?」改用 `RadioGroup`,仍是三个圆点。「选一个最接近的范围」改用 `ToggleGroup`。
+- 其余确认、暂停、重试、关闭、重新打开、采用,改用已有的 `Button`。关闭盘面仍是 Lucide 的叉,读屏名仍是「关闭盘面」。没有用字符当图标。
+- 经历草稿的精度改用已有下拉。可见文字仍是「记得的精度」,触发按钮用 `aria-labelledby` 指到这句,方便读屏。对照 `frontend/docs/VOICE.md`:没有新文案,没有改题目。
+- 题目出现时,若 Motion 在这次挂载之前已经载入,用已有的 `disclosure` 预设只淡入。减少动态效果时时长用预设里的 0.15 秒淡入,没有位移。没有新增循环。动效还没载入时不包一层,避免已经在屏幕上的内容再闪一次。
+- 盘面探头仍用原来的 `createPortal` 挂到顶栏,没有改成弹窗。`rectification-agentic-chat.tsx` 因此仍留在白名单里。
+- 白名单去掉 12 个校正文件,没有加长。测试里写死的基线名单没有改。
+- 没有改 `page.tsx`,没有把整段校正拆出首页。`Home()` 的 useState / useRef 没有增长。没有给 `scripts/jyotish_api_server.py` 加方法。没有改 `.gitea/**`,没有改生产部署,没有改数据库,没有升级依赖。
+
+移出白名单的 12 个文件:`birth-time-candidate-result.tsx`、`birth-time-choice-question.tsx`、`birth-time-evidence-draft-card.tsx`、`birth-time-guide-turn.tsx`、`birth-time-intake.tsx`、`birth-time-legacy-rectification.tsx`、`birth-time-rectification.tsx`、`rectification-board.tsx`、`rectification-choice-card.tsx`、`rectification-composer-reopen.tsx`、`rectification-range-delivery.tsx`、`rectification-segment-delivery.tsx`。
+
+## 断言改动(原值 / 新值 / 原因)
+
+| 位置 | 原值 | 新值 | 原因 |
+| --- | --- | --- | --- |
+| `frontend/tests/rectification-agentic-entry.test.ts` 持久化选择题的选项 class | `class="birth-time-choice-option is-primary"` 整段恰好 4 次 | 同一串作为 class 的一部分出现 4 次 | K6 选项改用 ToggleGroup,组件库会合并上自己的类。选项文案和读屏名称不变 |
+
+选中态源码 `data-selected={selectedKey === option.key ? "true" : "false"}` 没有改。卡片仍不得出现 `Check`、「已选择」和 `.focus(`。
+
+## 自测
+
+改前先在同一批测试上记过一笔。Windows 上本来就有 21 条失败。改完再跑,失败名单没有变长,通过的没有变少。
+
+| 检查 | 命令 | 结果 |
+| --- | --- | --- |
+| 类型 | 仓库根 `frontend\node_modules\.bin\tsc --noEmit -p frontend` | 退出码 0 |
+| 检查 | 在 `frontend` 下 `npm run lint` | 退出码 0。122 个警告,0 error。警告数和上一张记下的一样 |
+| 校正前端 | 在 `frontend` 下 `node node_modules/tsx/dist/cli.mjs --test`,233 个 `rectification-*` / `birth-time-*` 测试文件加 `ui-kit-whitelist.test.ts` | 2291 条。通过 2270,失败 21,取消 0。与改前一致 |
+| Python | `.venv\Scripts\python.exe -m pytest`,`tests/test_rectification_*.py` 共 26 个文件 | 退出码 0,进度到 100% |
+| 构建 | 在 `frontend` 下 `npm run build` | 退出码 0。编译 10.3s,TypeScript 30.5s。79 个静态页。`/` 仍是 ○ Static。5 条既有 skill-package-registry 警告,没有改 |
+| 首屏体积 | 首页 `index.html` 引用的 `/_next/static/**.js`,gzip-9 相加,不含 CSS | 基线是 #109 在 `1516ad56` 记下的 35 个文件、642174 字节。本张 37 个文件,缺失 0,644683 字节,多 2509(+0.39%)。±2% 大约是 629331 到 655017。没有把校正拆出首页 |
+
+改前就失败、改完仍是这 21 条(没有新增):
+
+1. public code never imports the internal dynamic choice contract(路径被拼成 `G:\G:\...`)
+2. candidate scores stay out of the specified client ownership boundary(同上)
+3. A · typed answer to a pending choice(BUG-1045,`getComputedStyle`)
+4. A · collect denial with a next choice(BUG-1045)
+5. B · failed choice submit 409(BUG-1046,`window is not defined`)
+6. B · failed choice submit network error(BUG-1046)
+7. B · typed answer that never reaches the server
+8. D3 · live focus moves to the latest message(BUG-635)
+9. D3 · persisted question with no asked turn
+10. D3 · standalone block fallback
+11. D4 · willContinue
+12. D4 · send() settle
+13. C · superseded focus(BUG-1135)
+14. C · client guard(BUG-1135)
+15. C · stem left in the body(BUG-1135)
+16. Skill bump 10.0.32 keeps Cases pinned(BUG-621)
+17. D3 · first stage line
+18. D3 · turn.rejected
+19. opening on the real surface · body only
+20. opening on the real surface · body + stem
+21. agent pins the dedicated rectification skill and its fixed version
+
+没有跑前端全量 `npm test`,没有跑 Python 快速门,没有跑 `test:db`。没有为了让测试通过去改计分或落库。
+
+## 环境缺口
+
+- 没有登录态,没有模型凭据。没有在浏览器里走完一次校正。不写成通过。可照做的步骤在 `docs/testing/ui-kit-migration-20261023.md` 的「生时校正」一节。
diff --git a/docs/testing/ui-kit-migration-20261023.md b/docs/testing/ui-kit-migration-20261023.md
index 48a13bf4..9417ed89 100644
--- a/docs/testing/ui-kit-migration-20261023.md
+++ b/docs/testing/ui-kit-migration-20261023.md
@@ -87,3 +87,14 @@
2. 走到填写六位验证码时,是六格分开的输入。恢复码仍是一条输入框。
3. 注册账号、忘记密码、返回登录仍是这几个字。没勾同意就继续,仍会提示先阅读并勾选。
4. 打开那一刻的天空。星星仍会汇聚到定格。左上关闭、右上分享仍在。没有重画星星。
+
+## 生时校正
+
+没有登录态,没有在浏览器里走完一次校正,也没有模型凭据。下面这些步骤留给有账号的人照做。
+
+1. 在对话里明确说要校正出生时间。界面直接开始一次新的校正,不再先出一张卡让你点。已经开始、还没做完的,接着上次做,不另开一轮。
+2. 题目仍是原来的句子。选项是圆角卡片,点中后变成浅底描边。读屏能读到题干和选项原文,没有多出来的「已选择」。
+3. 建档或改出生时间时,「你对这个时间有多确定?」仍是三个圆点。选了大概时间之后,「选一个最接近的范围」仍是一排标签。
+4. 校正进行中可以打开当前盘面,再关掉。关闭是一枚叉,读屏名是「关闭盘面」。窄屏上的探头仍在顶栏,不是弹窗。
+5. 系统开了减少动态效果时,新出现的题目只淡入,选项不飞,也没有一直转的动画。
+6. 做完或停下来之后,已经记下的范围还在。扣点和保存跟换皮之前一样。
diff --git a/frontend/DESIGN.md b/frontend/DESIGN.md
index 5aa31346..f08658c4 100644
--- a/frontend/DESIGN.md
+++ b/frontend/DESIGN.md
@@ -49,6 +49,8 @@
不新增等待动画;打开校正沿用既有一次揭幕。
+**选项和按钮换成组件库(2026-10-25,K6)**:选项卡片仍是原来的圆角卡片,点中后浅底描边,读屏仍读题干和选项原文,不另写「已选择」。确定程度仍是三个圆点。大概范围仍是一排标签。盘面关闭仍是一枚叉,探头仍挂在顶栏原来的位置,不是弹窗。其余确认、重新打开和采用用组件库按钮,样子沿用原来的实心、描边和文字按钮。题目出现时,若动效已经载入,只淡入;减少动态效果时同样只淡入,没有位移,也没有循环。题目、计分、扣点和保存没有改。明确要求校正仍直接开始,没做完的接着做。
+
**明确要求则直接打开(2026-10-12,BUG-1306)**:用户这一轮明说要做校正时,不再先出卡再点。回答只留一句过渡,界面沿用既有一次揭幕直接打开校正面。没有新的按钮、输入框、等待动画或 Home 状态。推得不准时的主动提议仍用 `.rectification-offer-card`。做不了时只显示一句话,不静默,也不在对话里假装校正。开案还没成功时,同一张提议卡仍是普通提议(不说「已经开始」,按钮仍是「开始核对出生时间」)。开案成功并绑定来源后才换成已开始的文案。打开失败时按钮仍可点。
## 用量页分段计时(2026-10-05,在途)
diff --git a/frontend/eslint/ui-primitive-whitelist.json b/frontend/eslint/ui-primitive-whitelist.json
index 9805b463..e0685741 100644
--- a/frontend/eslint/ui-primitive-whitelist.json
+++ b/frontend/eslint/ui-primitive-whitelist.json
@@ -3,21 +3,9 @@
"src/components/ayanamsa-preference-field.tsx",
"src/components/birth-place-picker.tsx",
"src/components/birth-sky/birth-sky-converge.tsx",
- "src/components/birth-time-candidate-result.tsx",
- "src/components/birth-time-choice-question.tsx",
- "src/components/birth-time-evidence-draft-card.tsx",
- "src/components/birth-time-guide-turn.tsx",
- "src/components/birth-time-intake.tsx",
- "src/components/birth-time-legacy-rectification.tsx",
- "src/components/birth-time-rectification.tsx",
"src/components/consultation-run-timeline.tsx",
"src/components/home-bootstrap-error.tsx",
"src/components/home-onboarding-shell.tsx",
"src/components/personal-report/personal-report-markdown-view.tsx",
- "src/components/rectification-agentic-chat.tsx",
- "src/components/rectification-board.tsx",
- "src/components/rectification-choice-card.tsx",
- "src/components/rectification-composer-reopen.tsx",
- "src/components/rectification-range-delivery.tsx",
- "src/components/rectification-segment-delivery.tsx"
+ "src/components/rectification-agentic-chat.tsx"
]
diff --git a/frontend/src/app/birth-time-choice.css b/frontend/src/app/birth-time-choice.css
index 1c3998e7..29f9cd42 100644
--- a/frontend/src/app/birth-time-choice.css
+++ b/frontend/src/app/birth-time-choice.css
@@ -5,14 +5,15 @@
.birth-time-choice-question legend { width: 100%; margin: 0 0 var(--space-3); color: var(--color-ink); font-family: var(--font-display); font-size: var(--type-title-md); font-weight: 400; line-height: 1.55; text-wrap: pretty; word-break: auto-phrase; }
.birth-time-primary-choices { display: grid; gap: var(--space-2); }
.birth-time-special-choices, .birth-time-reframe-actions, .birth-time-journey-controls { display: flex; flex-wrap: wrap; gap: var(--space-2); }
-.birth-time-choice-option { min-height: 52px; border: 1px solid var(--color-border); border-radius: var(--radius-md); background: var(--color-canvas); color: var(--color-ink); cursor: pointer; font-size: var(--type-body-sm); line-height: 1.5; text-align: left; text-wrap: pretty; word-break: auto-phrase; transition: border-color 120ms ease-out, background-color 120ms ease-out, transform 120ms ease-out; }
+.birth-time-choice-option { min-height: 52px; height: auto; justify-content: flex-start; white-space: normal; border: 1px solid var(--color-border); border-radius: var(--radius-md); background: var(--color-canvas); color: var(--color-ink); cursor: pointer; font-size: var(--type-body-sm); font-weight: 400; line-height: 1.5; text-align: left; text-wrap: pretty; word-break: auto-phrase; transition: border-color 120ms ease-out, background-color 120ms ease-out, transform 120ms ease-out; }
.birth-time-choice-option.is-primary { width: 100%; padding: var(--space-3) var(--space-4); }
-.birth-time-choice-option.is-secondary { min-height: 44px; flex: 1 1 0; padding: var(--space-2) var(--space-4); color: var(--color-ink-secondary); text-align: center; }
-.birth-time-choice-option:hover:not(:disabled) { border-color: var(--color-border-strong); background: var(--color-canvas-soft); }
+.birth-time-choice-option.is-secondary { min-height: 44px; flex: 1 1 0; justify-content: center; padding: var(--space-2) var(--space-4); color: var(--color-ink-secondary); text-align: center; }
+.birth-time-choice-option:hover:not(:disabled) { border-color: var(--color-border-strong); background: var(--color-canvas-soft); color: var(--color-ink); }
+.birth-time-choice-option.is-secondary:hover:not(:disabled) { color: var(--color-ink-secondary); }
.birth-time-choice-option:active:not(:disabled) { transform: scale(.96); }
.birth-time-choice-option[data-selected="true"] { border-color: var(--color-action); background: var(--color-action-soft); color: var(--color-ink); }
-.birth-time-choice-option:focus-visible { outline: 2px solid var(--color-focus); outline-offset: -2px; }
-.birth-time-choice-option[data-selected="true"]:focus-visible { outline: none; }
+.birth-time-choice-option:focus-visible { outline: 2px solid var(--color-focus); outline-offset: -2px; box-shadow: none; border-color: var(--color-border); }
+.birth-time-choice-option[data-selected="true"]:focus-visible { outline: none; box-shadow: none; border-color: var(--color-action); }
@media (forced-colors: active) {
.birth-time-choice-option[data-selected="true"]:focus-visible { outline: 2px solid Highlight; outline-offset: -2px; }
}
diff --git a/frontend/src/app/globals.css b/frontend/src/app/globals.css
index 9151f841..34c2843b 100644
--- a/frontend/src/app/globals.css
+++ b/frontend/src/app/globals.css
@@ -1261,14 +1261,17 @@ section[popover]:has(> [data-sonner-toaster]), section[popover][aria-label^="页
.birth-time-source-option { min-height: 64px; display: grid; grid-template-columns: 18px minmax(0, 1fr); align-items: center; gap: var(--space-3); padding: var(--space-3) var(--space-4); border: 1px solid var(--color-border); border-radius: var(--radius-md); background: var(--color-canvas); cursor: pointer; transition: border-color 120ms ease-out, background-color 120ms ease-out, transform 120ms ease-out; }
.birth-time-source-option.is-selected { border-color: var(--color-action); background: var(--color-action-soft); }
.birth-time-source-option input { width: 16px; height: 16px; margin: 0; accent-color: var(--color-action); }
+.birth-time-source-option [data-slot="radio-group-item"] { width: 16px; height: 16px; margin: 0; accent-color: var(--color-action); }
.birth-time-source-option > span { display: grid; gap: 3px; }
.birth-time-source-option b { color: var(--color-ink); font-size: var(--type-body-sm); font-weight: 600; }
.birth-time-source-option small, .birth-time-detail-note, .birth-time-legacy-note { margin: 0; color: var(--color-ink-secondary); font-size: var(--type-caption); line-height: 1.5; }
.birth-time-uncertainty-fieldset { margin: var(--space-3) 0 0; padding: 0; border: 0; }
.birth-time-uncertainty-fieldset legend { margin-bottom: var(--space-2); color: var(--color-ink-secondary); font-size: var(--type-caption); font-weight: 600; }
-.birth-time-uncertainty-list { display: flex; flex-wrap: wrap; gap: var(--space-2); }
-.birth-time-uncertainty-option { min-height: 44px; padding: 0 var(--space-3); border: 1px solid var(--color-border); border-radius: var(--radius-md); background: var(--color-canvas); color: var(--color-ink); font-size: var(--type-body-sm); cursor: pointer; }
-.birth-time-uncertainty-option.is-selected { border-color: var(--color-action); background: var(--color-action-soft); }
+.birth-time-uncertainty-list { display: flex; flex-wrap: wrap; gap: var(--space-2); width: 100%; }
+.birth-time-uncertainty-option { min-height: 44px; height: auto; justify-content: center; white-space: normal; padding: 0 var(--space-3); border: 1px solid var(--color-border); border-radius: var(--radius-md); background: var(--color-canvas); color: var(--color-ink); font-size: var(--type-body-sm); font-weight: 400; cursor: pointer; }
+.birth-time-uncertainty-option:hover { background: var(--color-canvas); color: var(--color-ink); }
+.birth-time-uncertainty-option.is-selected,
+.birth-time-uncertainty-option.is-selected:hover { border-color: var(--color-action); background: var(--color-action-soft); color: var(--color-ink); }
.birth-time-detail-grid { display: grid; grid-template-columns: minmax(0, 180px) minmax(0, 1fr); align-items: end; gap: var(--space-3); }
.birth-time-detail-grid > label { min-width: 0; }
.birth-time-detail-note { padding-bottom: 12px; }
@@ -1327,8 +1330,9 @@ section[popover]:has(> [data-sonner-toaster]), section[popover][aria-label^="页
.birth-time-guide-composer textarea { min-height: var(--space-24); resize: vertical; line-height: 1.55; }
.birth-time-guide-hint, .birth-time-scoring-status p, .birth-time-candidate-terminal p, .birth-time-success-note { margin: 0; color: var(--color-ink-secondary); font-size: var(--type-body-sm); line-height: 1.6; text-wrap: pretty; word-break: auto-phrase; }
.birth-time-guided-actions { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: var(--space-2); }
-.birth-time-guided-action { min-height: 44px; padding-inline: var(--space-4); font-size: var(--type-body-sm); }
+.birth-time-guided-action { min-height: 44px; height: auto; padding-inline: var(--space-4); font-size: var(--type-body-sm); white-space: normal; }
.button-text.birth-time-guided-action { border: 0; background: transparent; color: var(--color-action); cursor: pointer; }
+.button-text.birth-time-guided-action:hover { background: transparent; color: var(--color-action); }
.birth-time-draft-domain { margin: 0; padding: var(--space-3) var(--space-4); border: 1px solid var(--color-border); border-radius: var(--radius-md); background: var(--color-canvas); }
.birth-time-draft-domain > div { display: grid; gap: var(--space-1); }
.birth-time-draft-domain dt { color: var(--color-ink-tertiary); font-size: var(--type-overline); }
@@ -2893,6 +2897,10 @@ input:not([type="radio"]):not([type="checkbox"]):not([class^="ant-"]):not([class
.birth-time-source-option.is-selected { border-color: var(--color-action); background: var(--color-action-soft); box-shadow: inset 0 0 0 1px var(--color-action); }
.birth-time-source-option input { appearance: none; align-self: center; justify-self: center; position: static; box-sizing: border-box; width: 16px; min-width: 16px; max-width: 16px; height: 16px; min-height: 16px; max-height: 16px; margin: 0; overflow: visible; clip: auto; clip-path: none; border: 1.5px solid var(--color-border-strong); border-radius: 50%; background: var(--color-canvas); }
.birth-time-source-option input:checked { border-color: var(--color-action); background: var(--color-action-strong); box-shadow: inset 0 0 0 4px var(--color-action-soft); }
+.birth-time-source-option [data-slot="radio-group-item"] { appearance: none; align-self: center; justify-self: center; position: static; box-sizing: border-box; width: 16px; min-width: 16px; max-width: 16px; height: 16px; min-height: 16px; max-height: 16px; margin: 0; overflow: visible; clip: auto; clip-path: none; border: 1.5px solid var(--color-border-strong); border-radius: 50%; background: var(--color-canvas); box-shadow: none; }
+.birth-time-source-option [data-slot="radio-group-item"][data-checked] { border-color: var(--color-action); background: var(--color-action-strong); box-shadow: inset 0 0 0 4px var(--color-action-soft); }
+.birth-time-source-option [data-slot="radio-group-item"]:focus-visible { outline: none; box-shadow: none; }
+.birth-time-source-option [data-slot="radio-group-indicator"] { display: none; }
.birth-time-source-option > span { gap: 4px; }
.birth-time-source-option b { font-size: var(--type-body-sm); line-height: 1.35; }
.birth-time-source-option small { font-size: var(--type-caption); line-height: 1.5; }
@@ -2906,7 +2914,7 @@ input:not([type="radio"]):not([type="checkbox"]):not([class^="ant-"]):not([class
font-size: var(--type-caption);
line-height: 1.5;
}
-.birth-time-window-details .birth-time-skip-button { grid-column: 1 / -1; justify-self: start; min-height: 44px; padding-inline: 13px; border-color: transparent; background: transparent; color: var(--color-action); font-size: var(--type-caption); }
+.birth-time-window-details .birth-time-skip-button { grid-column: 1 / -1; justify-self: start; min-height: 44px; height: auto; padding-inline: 13px; border-color: transparent; background: transparent; color: var(--color-action); font-size: var(--type-caption); font-weight: 400; white-space: normal; }
.birth-time-window-details .birth-time-skip-button:hover { background: color-mix(in srgb, var(--color-action-soft) 72%, transparent); }
.birth-time-detail-grid > label { gap: var(--space-2); }
.birth-time-detail-grid > label > span { color: var(--color-ink-secondary); font-size: var(--type-caption); font-weight: 600; }
@@ -3500,6 +3508,7 @@ input:not([type="radio"]):not([type="checkbox"]):not([class^="ant-"]):not([class
justify-content: space-between;
gap: var(--space-3);
width: 100%;
+ height: auto;
min-height: 44px;
margin: 0 0 var(--space-2);
padding: var(--space-2) var(--space-4);
@@ -3507,7 +3516,9 @@ input:not([type="radio"]):not([type="checkbox"]):not([class^="ant-"]):not([class
border-radius: var(--radius-md);
background: var(--color-canvas-soft);
color: var(--color-ink);
+ font-weight: 400;
text-align: start;
+ white-space: normal;
cursor: pointer;
}
.rectification-board-peek strong { font-size: var(--type-body-sm); }
@@ -3805,6 +3816,7 @@ input:not([type="radio"]):not([type="checkbox"]):not([class^="ant-"]):not([class
}
.rectification-range-delivery__accept {
justify-self: stretch;
+ height: auto;
min-height: 44px;
padding: 0 12px;
border: 1px solid var(--color-border);
@@ -3813,8 +3825,13 @@ input:not([type="radio"]):not([type="checkbox"]):not([class^="ant-"]):not([class
background: var(--color-canvas);
font: inherit;
font-weight: 600;
+ white-space: normal;
cursor: pointer;
}
+.rectification-range-delivery__accept:hover {
+ background: var(--color-canvas);
+ color: var(--color-action);
+}
.rectification-range-delivery__accept[aria-pressed="true"] {
border-color: color-mix(in srgb, var(--color-action) 55%, var(--color-border));
}
diff --git a/frontend/src/components/birth-time-candidate-result.tsx b/frontend/src/components/birth-time-candidate-result.tsx
index 369728fb..dd75d069 100644
--- a/frontend/src/components/birth-time-candidate-result.tsx
+++ b/frontend/src/components/birth-time-candidate-result.tsx
@@ -4,6 +4,7 @@ import type { JourneyClientResponse } from "@/lib/birth-time-journey-client";
import type { BirthTimeGuidedController } from "@/hooks/use-birth-time-guided-journey";
import { guidedTerminalPath } from "@/lib/birth-time-guided-terminal";
import { ErrorToast } from "@/components/error-toast";
+import { Button } from "@/components/ui/button";
type CandidateResultProps = {
readonly journey: JourneyClientResponse;
@@ -44,7 +45,7 @@ export function BirthTimeCandidateResult({ journey, controller, error }: Candida
尚未进入分钟计算:还没有可评分的关键经历资料。
当前范围仅为填报范围,不是校正结果;请补充跨领域、可注明年月的重大事件后重新评估。
- {terminalPath &&
}
+ {terminalPath &&
}
);
}
@@ -110,9 +111,9 @@ export function BirthTimeCandidateResult({ journey, controller, error }: Candida
{action.kind === "present_medium_result" && winner && !dynamic && (
可以保存候选时间范围,代表时间不会用于排盘。
-
)}
{action.kind === "candidate_saved" && (
@@ -125,15 +126,15 @@ export function BirthTimeCandidateResult({ journey, controller, error }: Candida
确认当前排盘使用时间
候选时间为 {winner.representativeTime}。确认后它会成为当前排盘使用时间,原始填报时间仍会保留。
-
controller.confirmCandidate(result.resultId, winner.representativeTime)}>
+ controller.confirmCandidate(result.resultId, winner.representativeTime)}>
{controller.pending ? "确认中…" : `确认使用 ${winner.representativeTime}`}
-
+
)}
{action.kind === "ready" && (
当前排盘使用时间已更新为 {action.activeTime},原始填报时间仍已保留。
-
继续使用此排盘
+
继续使用此排盘
)}
{!terminalPath ? : null}
@@ -150,7 +151,7 @@ function TerminalAction({ controller, error, path }: {
尚未达到采用条件
候选范围已保留,但当前证据不足以将具体分钟写入当前排盘时间。补充经历后可重新评估。
-
开始新的评估
+
开始新的评估
会建立新的记录,当前结果仍会保留。
diff --git a/frontend/src/components/birth-time-choice-question.tsx b/frontend/src/components/birth-time-choice-question.tsx
index f2a3b624..7098dc99 100644
--- a/frontend/src/components/birth-time-choice-question.tsx
+++ b/frontend/src/components/birth-time-choice-question.tsx
@@ -2,6 +2,9 @@
import { useEffect, useRef, useState } from "react";
import { CharacterRemaining } from "@/components/character-remaining";
+import { RectificationFade } from "@/components/rectification-fade";
+import { Button } from "@/components/ui/button";
+import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group";
import { characterRemainingVisible } from "@/lib/character-remaining";
import {
choiceQuestionGroups,
@@ -62,41 +65,55 @@ export function BirthTimeChoiceQuestion(props: ChoiceQuestionProps) {
};
return (
+
{props.pending &&
正在缩小候选范围…
}
+
);
}
@@ -111,6 +128,7 @@ export function BirthTimeUnmatchedClarification(props: ClarificationProps) {
}
}, [props.error, props.pending, props.progress.answeredCount]);
return (
+
{props.pending &&
正在换一道更合适的问题…
}
+
);
}
@@ -143,8 +162,8 @@ function JourneyControls(props: SharedQuestionProps) {
return (
<>
- 暂停,稍后继续
- 结束并保存当前范围
+ 暂停,稍后继续
+ 结束并保存当前范围
>
diff --git a/frontend/src/components/birth-time-evidence-draft-card.tsx b/frontend/src/components/birth-time-evidence-draft-card.tsx
index 497db8e6..0c62cabc 100644
--- a/frontend/src/components/birth-time-evidence-draft-card.tsx
+++ b/frontend/src/components/birth-time-evidence-draft-card.tsx
@@ -1,6 +1,8 @@
"use client";
-import { useState } from "react";
+import { useId, useState } from "react";
+import { Button } from "@/components/ui/button";
+import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
import { lifeEventSchema } from "@/lib/birth-time-evidence";
import type { EvidenceDraft } from "@/lib/birth-time-journey-turn";
import type { EvidenceDatePrecision, LegacyGuidedEvidenceDomain } from "@/lib/birth-time-question-planner";
@@ -21,12 +23,8 @@ type DraftCardProps = {
readonly onSkip: () => void;
};
-function parsePrecision(value: string): EvidenceDatePrecision {
- if (value === "year" || value === "month" || value === "day") return value;
- throw new TypeError("Unsupported evidence date precision");
-}
-
export function BirthTimeEvidenceDraftCard(props: DraftCardProps) {
+ const precisionLabelId = useId();
const [precision, setPrecision] = useState(props.draft.precision ?? "year");
const [date, setDate] = useState(props.draft.date ?? "");
const isValid = lifeEventSchema.safeParse({
@@ -43,12 +41,25 @@ export function BirthTimeEvidenceDraftCard(props: DraftCardProps) {
- 经历领域
- {domainLabels[props.draft.domain]}
{!isValid && 请填写与所选精度一致的有效日期;不会自动补猜缺失时间。
}
- 放弃这条并跳过
- props.onConfirm(precision, date)}>
+ 放弃这条并跳过
+ props.onConfirm(precision, date)}>
{props.pending ? "确认中…" : "确认并用于校正"}
-
+
);
diff --git a/frontend/src/components/birth-time-guide-turn.tsx b/frontend/src/components/birth-time-guide-turn.tsx
index fa5c9c46..0785655b 100644
--- a/frontend/src/components/birth-time-guide-turn.tsx
+++ b/frontend/src/components/birth-time-guide-turn.tsx
@@ -2,6 +2,7 @@
import { useState } from "react";
import { CharacterRemaining } from "@/components/character-remaining";
+import { Button } from "@/components/ui/button";
import { characterRemainingVisible } from "@/lib/character-remaining";
import type { JourneyProgress } from "@/lib/birth-time-journey-turn";
import { protectOnboardingPhrases } from "@/lib/onboarding-copy";
@@ -57,11 +58,11 @@ export function BirthTimeGuideTurn(props: BirthTimeGuideTurnProps) {
Enter 发送,Shift+Enter 换行。说出大概年份也可以,不需要为了校正而猜测。
- 暂停,稍后继续
- 不记得,跳过
- props.onSubmit(message.trim())}>
+ 暂停,稍后继续
+ 不记得,跳过
+ props.onSubmit(message.trim())}>
{props.pending ? "整理中…" : "整理为经历草稿"}
-
+
);
diff --git a/frontend/src/components/birth-time-intake.tsx b/frontend/src/components/birth-time-intake.tsx
index 63ffd73a..9e1612cf 100644
--- a/frontend/src/components/birth-time-intake.tsx
+++ b/frontend/src/components/birth-time-intake.tsx
@@ -2,7 +2,10 @@
import { useId, useState } from "react";
import { BirthDatePicker } from "@/components/birth-date-picker";
+import { Button } from "@/components/ui/button";
+import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
+import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group";
import { birthTimeConsultationOptionsCopy } from "@/lib/birth-time-consultation-consent";
import {
approximateUncertaintyOptions,
@@ -83,7 +86,8 @@ export function BirthTimeIntakeFields({ value, onPatch, lockReportedTime = false
if (lockReportedTime) return;
onPatch(patch);
};
- const groupId = useId();
+ const sourceLegendId = useId();
+ const uncertaintyLegendId = useId();
const source = value.birthTimeSource;
const isConfirmed = value.birthTimeStatus === "confirmed";
const displayState = birthTimeDisplayState(value);
@@ -143,33 +147,36 @@ export function BirthTimeIntakeFields({ value, onPatch, lockReportedTime = false
)}
{!isConfirmed && }
{usesClockTime && (
@@ -187,28 +194,38 @@ export function BirthTimeIntakeFields({ value, onPatch, lockReportedTime = false
)}
{(source === "approximate" || source === "family_exact") && (
)}
@@ -239,9 +256,10 @@ export function BirthTimeIntakeFields({ value, onPatch, lockReportedTime = false
onChange={(declaredWindowEnd) => emitTime({ declaredWindowEnd })}
/>
- emitTime({
birthTimeSource: "unknown",
reportedTime: "",
@@ -254,7 +272,7 @@ export function BirthTimeIntakeFields({ value, onPatch, lockReportedTime = false
birthTimeStatus: "reported",
time: "",
})}
- >完全不清楚,跳过出生时间
+ >完全不清楚,跳过出生时间
)}
diff --git a/frontend/src/components/birth-time-legacy-rectification.tsx b/frontend/src/components/birth-time-legacy-rectification.tsx
index 7309b902..dd414311 100644
--- a/frontend/src/components/birth-time-legacy-rectification.tsx
+++ b/frontend/src/components/birth-time-legacy-rectification.tsx
@@ -9,6 +9,7 @@ import type { LegacyJourneyClientResponse } from "@/lib/birth-time-journey-respo
import { guidedTurnIdentity } from "@/lib/birth-time-guided-turn-identity";
import type { NextAction } from "@/lib/birth-time-journey-turn";
import { ErrorToast } from "@/components/error-toast";
+import { Button } from "@/components/ui/button";
type LegacyRectificationProps = {
readonly journey: LegacyJourneyClientResponse;
@@ -75,8 +76,8 @@ export function BirthTimeLegacyRectification(props: LegacyRectificationProps) {
/>
)}
{action.kind === "score_pending" && 正在使用已确认的关键经历评分…
}
- {action.kind === "retry_scoring" && 重新评分}
- {action.kind === "paused" && 继续校正}
+ {action.kind === "retry_scoring" && 重新评分}
+ {action.kind === "paused" && 继续校正}
{showsCandidate ? (
)}
{(action.kind === "generate_dynamic_question" || action.kind === "retry_question_generation") && error && (
- 重试当前问题
+ 重试当前问题
)}
{action.kind === "score_pending" && (
正在缩小候选范围…
- {props.controller.pollRecoverable && 重新检查评分状态}
+ {props.controller.pollRecoverable && 重新检查评分状态}
)}
- {action.kind === "retry_scoring" && 重新评分}
- {action.kind === "paused" && 继续本次评估}
+ {action.kind === "retry_scoring" && 重新评分}
+ {action.kind === "paused" && 继续本次评估}
{showsCandidate ? (
{emptyCopy.clock}
: null}
{compact && (
-
-
+
)}
{stage ? {engineMeaningToDisplayCopy(stage)}
: null}
@@ -338,9 +340,10 @@ export function RectificationBoardPeek({
}>) {
const peek = rectificationBoardPeekCopy(result, declaredTime, birthTimeSource);
return (
-
{peek.title}
{peek.detail}
-
+
);
}
diff --git a/frontend/src/components/rectification-choice-card.tsx b/frontend/src/components/rectification-choice-card.tsx
index 32a540b5..60dc7da3 100644
--- a/frontend/src/components/rectification-choice-card.tsx
+++ b/frontend/src/components/rectification-choice-card.tsx
@@ -2,6 +2,8 @@
import { useState } from "react";
import { InlineSpinner } from "@/components/inline-spinner";
+import { RectificationFade } from "@/components/rectification-fade";
+import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group";
import {
CHOICE_STOP_LABEL,
type RectificationChoiceCard as ChoiceCard,
@@ -40,6 +42,7 @@ export function RectificationChoiceCard(props: RectificationChoiceCardProps) {
const hidePrompt = embedded || props.hidePrompt === true;
return (
+
) : null}
-
- {props.card.options.map((option) => (
- select(option.key)}
- >
- {option.key}. {option.label}
-
- ))}
- {props.card.stop_label !== CHOICE_STOP_LABEL ? (
-
- {props.card.stop_label}
-
- ) : null}
-
+ {
+ const key = next[0];
+ if (!key || key === selectedKey) return;
+ if (key === "stop") {
+ stop();
+ return;
+ }
+ if (props.card.options.some((option) => option.key === key)) select(key as ChoiceKey);
+ }}
+ >
+
+ {props.card.options.map((option) => (
+
+ {option.key}. {option.label}
+
+ ))}
+ {props.card.stop_label !== CHOICE_STOP_LABEL ? (
+
+ {props.card.stop_label}
+
+ ) : null}
+
+
+
);
}
diff --git a/frontend/src/components/rectification-composer-reopen.tsx b/frontend/src/components/rectification-composer-reopen.tsx
index 247dacdb..81a0d383 100644
--- a/frontend/src/components/rectification-composer-reopen.tsx
+++ b/frontend/src/components/rectification-composer-reopen.tsx
@@ -1,3 +1,4 @@
+import { Button } from "@/components/ui/button";
import { RECTIFICATION_REOPEN_LABEL } from "@/lib/rectification-session-composer-guard";
export function RectificationComposerReopen({
@@ -10,9 +11,9 @@ export function RectificationComposerReopen({
return (
{message}
-
+
{RECTIFICATION_REOPEN_LABEL}
-
+
);
}
diff --git a/frontend/src/components/rectification-fade.tsx b/frontend/src/components/rectification-fade.tsx
new file mode 100644
index 00000000..59b2f1c1
--- /dev/null
+++ b/frontend/src/components/rectification-fade.tsx
@@ -0,0 +1,50 @@
+"use client";
+
+import { useEffect, useState, type ReactNode } from "react";
+
+import { loadDomAnimation, loadMotionReact, peekMotionReact, type MotionReact } from "@/lib/motion/load-react";
+import { motionPresets } from "@/lib/motion/presets";
+
+function useMotionReact(): MotionReact | null {
+ const [mod, setMod] = useState(() => peekMotionReact());
+ useEffect(() => {
+ if (mod) return;
+ let live = true;
+ void loadMotionReact().then((next) => {
+ if (live) setMod(next);
+ }, () => undefined);
+ return () => {
+ live = false;
+ };
+ }, [mod]);
+ return mod;
+}
+
+/**
+ * Fade once, and only when Motion was already loaded before this mount.
+ * Otherwise return the children unchanged so a late chunk does not wrap
+ * content that is already on screen. Reduced motion stays a short fade.
+ */
+export function RectificationFade({ children }: { readonly children: ReactNode }) {
+ const [readyAtMount] = useState(() => peekMotionReact() !== null);
+ const motion = useMotionReact();
+ if (!readyAtMount || motion === null) return children;
+ return {children};
+}
+
+function FadeIn({ motion, children }: { readonly motion: MotionReact; readonly children: ReactNode }) {
+ const reduced = motion.useReducedMotion();
+ const preset = motionPresets.disclosure;
+ const duration = reduced ? preset.reduced.duration : preset.duration;
+ return (
+
+
+ {children}
+
+
+ );
+}
diff --git a/frontend/src/components/rectification-range-delivery.tsx b/frontend/src/components/rectification-range-delivery.tsx
index 4c872a53..bdace93a 100644
--- a/frontend/src/components/rectification-range-delivery.tsx
+++ b/frontend/src/components/rectification-range-delivery.tsx
@@ -17,6 +17,7 @@ import {
import { RectificationVerificationReport } from "@/components/rectification-verification-report";
import { RectificationSegmentDelivery } from "@/components/rectification-segment-delivery";
import { sessionOutcomeAllowsAdopt } from "@/lib/rectification-agentic/core/rectification-decision";
+import { Button } from "@/components/ui/button";
import { deliveryWindowFromReceipt, protectVargaReport } from "@/lib/rectification-agentic/v9/delivery-window";
@@ -159,8 +160,9 @@ export function RectificationRangeDelivery({
)}
{adoptAllowed && !adoptLocked ? (
<>
-
+
{recordConflict && !adopted ? (
{switchToRectifiedConfirmCopy(recordConflict.reported_time, column.time)}
diff --git a/frontend/src/components/rectification-segment-delivery.tsx b/frontend/src/components/rectification-segment-delivery.tsx
index e5423b20..42be37a7 100644
--- a/frontend/src/components/rectification-segment-delivery.tsx
+++ b/frontend/src/components/rectification-segment-delivery.tsx
@@ -7,6 +7,7 @@ import { SWITCH_TO_RECTIFIED_LABEL } from "@/lib/rectification-agentic/birth-tim
import { RectificationVerificationReport } from "@/components/rectification-verification-report";
import { protectVargaReport } from "@/lib/rectification-agentic/v9/delivery-window";
import { unknownProductDeliveryCopy } from "@/lib/rectification-agentic/v9/product-delivery";
+import { Button } from "@/components/ui/button";
export function RectificationSegmentDelivery({ summary, delivery, rangeLabel, selection, resultId, busy, accepting, onAccept, adoptLocked, markdown, candidateDate, canAdopt = false }: Readonly<{
summary: SegmentSummary | null;
@@ -57,12 +58,12 @@ export function RectificationSegmentDelivery({ summary, delivery, rangeLabel, se
{showAdopt && adoption && candidateId ? (
<>
采用后按 {adoption.date ? `${adoption.date} ` : ""}{adoption.time} 排盘;原填报资料保留。
- onAccept(candidateId)}>
{accepting ? RECTIFICATION_USER_COPY.rangeDeliveryAdopting : adopted ? RECTIFICATION_USER_COPY.rangeDeliveryAdopted
: readopt && priorAdoption ? `改用 ${readopt}`
: delivery.record_conflict ? SWITCH_TO_RECTIFIED_LABEL : `改用 ${charts.map((chart) => chart.chart).join(" + ")} 盘解读`}
-
+
{readopt && priorAdoption && !adopted ? (
现在按 {priorAdoption.time} 排盘;改用后按 {readopt} 排盘,原填报资料保留。
) : null}
diff --git a/frontend/tests/rectification-agentic-entry.test.ts b/frontend/tests/rectification-agentic-entry.test.ts
index 315a73b9..d063a65f 100644
--- a/frontend/tests/rectification-agentic-entry.test.ts
+++ b/frontend/tests/rectification-agentic-entry.test.ts
@@ -786,7 +786,10 @@ test("time-selection cards use server adoption state and stay mutually exclusive
}));
assert.match(persistedHtml, /data-testid="persisted-question"/);
assert.match(persistedHtml, /class="rectification-choice-card is-embedded"/);
- assert.equal(persistedHtml.match(/class="birth-time-choice-option is-primary"/g)?.length, 4);
+ // 原值: class="birth-time-choice-option is-primary" 整段 class 恰好出现 4 次。
+ // 新值: 同一串作为 class 的一部分出现 4 次。
+ // 原因: K6 选项卡片改用 ToggleGroup,组件库会合并上自己的类。选项文案和读屏名称不变。
+ assert.equal(persistedHtml.match(/birth-time-choice-option is-primary/g)?.length, 4);
assert.doesNotMatch(messageLoop, /rectification-question-slot/);
assert.doesNotMatch(persistedHtml, /rectification-question-slot/);
assert.doesNotMatch(messageEntry, /rectification-question-slot/);