docs(rectification): record the surface redo — BUG-505..509, DESIGN.md rectification surface, walkthrough section 8
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JUei7K13cYxLHE3Axe4A45
This commit is contained in:
@@ -0,0 +1,93 @@
|
||||
# PROGRESS · 生时校正会话面:消除空白假死与交互摩擦(重做,2026-09-03)
|
||||
|
||||
工作树:`.worktrees/rectification-ux-20260903`
|
||||
分支:`codex/rectification-ux-20260903`
|
||||
基线:`origin/staging` @ `af411f59`(任务书重启说明所在),收尾时 rebase 到 `0cefaea6`(两次均为文档提交:任务书搬家、任务状态板;无代码变化)。
|
||||
任务书:`docs/tasks/TASK-rectification-ux-20260902.md`,以其「2026-09-03 重启说明」为准。第一次执行的分支 `codex/rectification-ux-20260902` @ `fec000f7` 只作参考,未 cherry-pick。
|
||||
|
||||
未改服务端与 SQL。未改 `.gitea/workflows/**`。未合并 staging,未碰 main。未手写 `useCallback` / `useMemo`(`rectification-agentic-chat.tsx` 既有的原样保留;新增的 `refetchQuestion` / `reloadQuestion` / `startOpeningManually` 是普通函数)。BUG 编号:开工确认远端最大号 504,本轮 505–509。未复活问题槽、未复活 consult handoff。
|
||||
|
||||
| 任务 | 状态 | commit | BUG |
|
||||
| --- | --- | --- | --- |
|
||||
| 0.2 一次揭幕、面板不重挂、深链/popstate 并入 prepare 阶段 | 完成 | `53017d41` | BUG-505 |
|
||||
| 0.1 入口有反馈不转圈 | 完成(同 commit) | `53017d41` | BUG-505 |
|
||||
| 0.3 问题槽只有生成中态、在对话流内 | 前半由 staging `d9404976` 完成(题目内嵌消息,槽已删);后半"没有等待快照这一态"随 0.2 成立 | — | — |
|
||||
| 0.4 「等待服务端更新」→ live 行 + 重试 + 重新加载 | 完成 | `360989ca` | BUG-505 |
|
||||
| 0.6 空 turns 会话的起点 | 完成 | `360989ca` | BUG-507 |
|
||||
| 0.7 停止后的文案 | 完成(现状是静默返回,已补通知) | `360989ca` | — |
|
||||
| 1.4 开场 live 文案 | 完成 | `360989ca` | — |
|
||||
| 1.5 402 跳转前提示 | 完成 | `360989ca` | — |
|
||||
| 0.5 选择题/采用不留缝不闪卡 | 完成(接 `beginLiveRun` / `rememberLiveActivity`) | `1e50007c` | BUG-506 |
|
||||
| 1.1 选择卡确认感 | 补差额完成(选中态进按钮由 `35688015`、内嵌变体由 `d9404976` 已做;本轮补对勾「已选择」与卡顶「正在记录…」行) | `3c33e598` | BUG-508 |
|
||||
| 1.2 用户选择回显 | **跳过**:`d9404976` 已把题目内嵌进 assistant 消息,答过的卡以 `question.answer_option` 标出所选项,持久化 turn 的 `question` 也带 `answer_option`,刷新前后都能看到自己选了什么;再加一条用户气泡会重复 | — | — |
|
||||
| 1.3 盘面首态 | 完成(文案 + 收窄 + peek;宫位表 API 不提供,未造数据) | `3c33e598` | BUG-509 |
|
||||
| 2 DESIGN.md | 完成 | 本 docs commit | — |
|
||||
|
||||
## 六段空白与死角:各自的消除证据
|
||||
|
||||
| 空白 | 消除方式 | 证据 |
|
||||
| --- | --- | --- |
|
||||
| 1 首页卡片无反馈 | `rectificationCardLabel` 在 `rectificationLoading` 时为「正在打开…」;卡片 `data-opening` + `cursor: progress`;侧栏目标行「打开中」+ `aria-busy` | `rectification-surface-contract` → "entry feedback is static…" |
|
||||
| 2 侧栏进入:普通对话 → 空白 → 重挂 | `selectSession` 对未打开的校正会话不先 `setActiveSessionId`/写 URL;hook 在 `/cases/open` 后 `hydrateRectificationCase`(一次 Case 读取 turns + 快照,`BOOTSTRAP_PREPARE_TIMEOUT_MS` 上限)再一次性切换;面板 key 去掉 `-ready/-loading` | 同上 → "…revealed once…";`rectification-surface-state` → hydration 两条 |
|
||||
| 2′ 深链 / 刷新 / popstate | page 的 resume effect 从"揭幕后"改为"prepare 阶段起";`bootstrapPrepareSettled` 新增 `rectificationApplicable/Settled`,校正会话 hydration 完成(或 open 报错 / 资料不全)才揭幕;popstate 走 `selectSession`,同样延后切换 | `rectification-surface-contract` → "…hydrated before the reveal, and popstate defers…";`home-bootstrap-reveal` 既有用例不变(新字段可选) |
|
||||
| 3 第一轮结束整面重挂 | key 只剩 `${sessionId}-${caseId}`;后到的 turns 经渲染期按上一 prop 调整只填空 transcript;卸载 `abort()` | 同上(`doesNotMatch(page, /"ready" : "loading"/)`、`seededTurns` 锁) |
|
||||
| 4 快照回来前问题区空 | 快照随揭幕到位(`initialSnapshot` 初始化 state,`caseSnapshotLoaded` 初值 `initialSnapshot !== null`);面板不再挂载自拉;hydration 超时时走 0.4 的缺口重试 | 同上(`useState(initialSnapshot !== null)`、无挂载 fetch);`rectification-surface-state` → "a snapshot that never arrived is a gap…" |
|
||||
| 5 选择题两段缝 + 闪卡 | `send(action, text, continuation)` 续接同一条 live 行并跳过 busy 守卫;`willContinue` 在同一 async 链里 `await send("read_only", …)`;`choiceContinuationPending` 与 effect 删除;`busy` 全程 true | 同上 → "…keeps one live row through the follow-up turn" |
|
||||
| 6 采用只有按钮变灰 | 点击即追加「正在采用 HH:MM…」行,POST → 快照 → 复用该行续接 read_only;失败移除该行 | 同上;`rectification-surface-state` → `rectificationAdoptingLabel` |
|
||||
| 死角 空 turns 会话 | `rectificationConversationState` 判 `empty` → 「这段校正还没有开始。」+「开始提问」(`send("opening")`,`openingStarted` 守卫) | `rectification-surface-state` → "conversation state…";契约 → "an empty Case…" |
|
||||
| 文案 「等待服务端更新」 | `rectificationQuestionGapState`:`preparing`(transcript 末尾一条时间线 live 行「正在准备下一个问题…」+ 2s 定时重拉 ≤2 次)→ `unavailable`(「没有拿到下一个问题。」+ 44px「重新加载」);`question_source: "unavailable"` 直接 `unavailable`;候选卡待采用时不算缺口 | `rectification-surface-state` → 缺口三条;契约 → "…never tells the reader to wait for the server" |
|
||||
|
||||
## 改动文件
|
||||
|
||||
- 新建:`frontend/src/lib/rectification-surface-state.ts`(常量、hydration、turn 解析含 `question`/`offer_result_id`/`tool_activities`、会话态与缺口态纯函数、板文案、填报时间派生)、`frontend/tests/rectification-surface-state.test.ts`(10 用例)、`frontend/tests/rectification-surface-contract.test.ts`(8 用例)。
|
||||
- 改动:`use-rectification-surface.ts`、`use-session-management.ts`、`home-bootstrap.ts`(可选字段)、`page.tsx`(2 个 state、hook 参数、卡片文案、resume effect 门、prepare 判定、面板 key/props、侧栏 prop)、`app-sidebar.tsx`、`sidebar-session-row.tsx`、`starter-home.tsx`、`conversational-birth-time-rectification.tsx`(props 与 turn 类型)、`rectification-agentic-chat.tsx`、`consultation-run-timeline.tsx`(导出 `ConsultationTimelineLiveRow`)、`rectification-choice-card.tsx`、`rectification-board.tsx`、`rectification-board-model.ts`(`rectificationBoardPeekCopy` 第二参可选)、`globals.css`、`DESIGN.md`、`docs/BUG_HISTORY.md`、`docs/testing/staging-manual-walkthrough-20260901.md`(第 8 节)、`docs/tasks/README.md`(索引)、`BLOCKED.md`。
|
||||
|
||||
## 与任务书的书面偏差(均登记 BLOCKED.md)
|
||||
|
||||
1. hydration 是一次 `GET /api/rectification/cases/:id` 套 4 秒 `Promise.race`,不是 `Promise.allSettled([turns, snapshot])`(同一端点,两次请求会重复);上限常量直接等于 `BOOTSTRAP_PREPARE_TIMEOUT_MS`,只有一个。
|
||||
2. turns 后到的填充与重试计数复位不用 `useEffect`(`react-hooks/set-state-in-effect`):前者渲染期按上一 prop 调整,后者在 `applyCaseSnapshot`(有问题到达)与 `setPending(true)` 里复位。
|
||||
3. 首页卡片不带 `aria-busy`:`tests/home-bootstrap-reveal.test.ts` 锁死 `starter-home.tsx` 不得出现 `aria-busy={`。卡片只用 `data-opening` + 静态文案 + `cursor: progress`;侧栏行仍带 `aria-busy`。
|
||||
4. 缺口的 live 行放在 transcript 末尾(`.rectification-message-wrap.rectification-question-gap`),不是 staging 原来的 composer 上方状态行;`.rectification-composer-status` 样式删除。
|
||||
|
||||
## 1.3 核实
|
||||
|
||||
`frontend/src/app/api/rectification/cases/[caseId]/route.ts` 的 dossier 响应只带 `accepted_time` / `confirmed_time` / `candidate_range`,无 natal/declared 宫位表;按任务书只做文案与收窄,`declaredTime` 由 `page.tsx` 从 profile(`reportedTime || time`,须为 `H:MM`)派生。
|
||||
|
||||
## 被触碰的既有断言(红线 4,每处源码上方均注明原值与错因)
|
||||
|
||||
| 文件 | 原断言 | 现在 | 理由 |
|
||||
| --- | --- | --- | --- |
|
||||
| `tests/rectification-agentic-entry.test.ts` :85、:194 附近 | `key={…-${rectificationTurns.length > 0 ? "ready" : "loading"}}` | key 只含 session/Case;`doesNotMatch /"ready" : "loading"/` | 锁的正是重挂缺陷(BUG-505) |
|
||||
| 同上 :87 | hook 内联 `methods: Array.isArray((turn.receipt as { methods?: unknown }).methods)` | `parsePersistedRectificationTurns(payload?.turns)` + lib 里 `methods: stringList(receipt.methods)` | 解析器搬到 lib 供 hydration 与刷新共用(BUG-505) |
|
||||
| 同上 :233–:236 | `await loadCaseSnapshot(); choiceContinuationPending.current = true;`、effect 内 `void send("read_only", "")` | `await loadCaseSnapshot(); … await send("read_only", "", { reuseAssistantRenderKey: assistantRenderKey, label: adoptingLabel })`;`doesNotMatch /choiceContinuationPending/`;原 `doesNotMatch` 裸调用锁保留 | 锁的是 effect 中转(BUG-506) |
|
||||
| 同上 workspace class | 无 `is-board-empty` 的类模板 | 含 `${candidateResult ? "" : " is-board-empty"}` | 板收窄(BUG-509) |
|
||||
| `tests/rectification-question-in-message.test.ts` :136–137 | `/当前没有可回答的问题,正在等待服务端更新/`、`/题目加载失败,请刷新/` | `doesNotMatch` 两句 + 锁 `RECTIFICATION_QUESTION_PREPARING_LABEL` / `_UNAVAILABLE_COPY` | 锁的是裸等待文案(BUG-505) |
|
||||
| `tests/rectification-spoken-collect.test.ts` "missing current_question…" | `showMissingQuestion = Boolean(…)` 与同两句文案 | `rectificationQuestionGapState({ … questionMissing: currentQuestion === null … resumableCase })`;`doesNotMatch` 两句 | 同上;仍锁 resumable 门 |
|
||||
| `tests/rectification-answer-choice.test.ts` :1201 | `/send\("read_only", ""\)/` | `send("read_only", "", { reuseAssistantRenderKey: assistantRenderKey, label: recordingLabel })` | 后续轮带续接行(BUG-506) |
|
||||
|
||||
新增测试:`tests/rectification-surface-state.test.ts`(10)、`tests/rectification-surface-contract.test.ts`(8)。
|
||||
|
||||
## 命令输出
|
||||
|
||||
`./node_modules/.bin/tsc --noEmit` → **0**(每个 commit 前各一次,rebase 后再一次)。
|
||||
|
||||
`npm run lint` → **0 error,73 warning**;触及文件里唯一的 warning 是 `rectification-agentic-chat.tsx` `submitStructuredChoice` 的既有 `exhaustive-deps`(staging 同在,依赖表原本就缺 `busy` / `actionIdForChoice`,本轮未动);新建的三个文件 0 warning。
|
||||
|
||||
`./node_modules/.bin/tsx --test tests/*.test.ts`(基线在干净 `af411f59` 上跑):
|
||||
|
||||
| | 基线 `af411f59` | 现在 |
|
||||
| --- | ---: | ---: |
|
||||
| tests | 2584 | 2602 |
|
||||
| pass | 2550 | 2568 |
|
||||
| fail | 24 | 24 |
|
||||
| skipped | 10 | 10 |
|
||||
|
||||
24 条失败与基线逐条比对**完全同一集合**(`comm -3` 差集为空):全部是本机无 Docker / PostgreSQL / 部署夹具的既有缺口。10 条 skipped 同基线。rebase 到 `8db71aaf` / `0cefaea6` 只改了 md 文件,src/tests 逐字节相同,全量数字沿用;rebase 后复跑 tsc 与校正相关聚焦套件(见下)。
|
||||
|
||||
聚焦套件(rebase 后,15 个文件):`rectification-surface-state`、`rectification-surface-contract`、`rectification-agentic-entry`、`rectification-question-in-message`、`rectification-spoken-collect`、`rectification-answer-choice`、`rectification-board-model`、`rectification-varga-style-copy`、`home-bootstrap-reveal`、`chat-session-url`、`chat-notice-and-scroll-contract`、`starter-questions`、`class-name-definition-contract`、`touch-target-contract`、`dark-theme-contract` → **173 / 173**。
|
||||
|
||||
`./node_modules/.bin/next build` → **0**。`✓ Compiled successfully in 17.8s`、`Finished TypeScript in 41s`、`Generating static pages (62/62)`,`/` 仍是 `○ (Static)`。
|
||||
|
||||
## 未做与原因
|
||||
|
||||
见 `BLOCKED.md` 顶部:浏览器级手工验收(六条手工项 + 深链/刷新 + 缺口重试 + 整轮录屏)无登录态无 Chrome,已写进 `docs/testing/staging-manual-walkthrough-20260901.md` 第 8 节;1.3 的宫位表 API 不提供;四条书面偏差。
|
||||
@@ -44,7 +44,7 @@
|
||||
| `TASK-rectification-adopt-flow-20260902.md` | — | 采用流程整治 | 已验收 | `35e5781e`(BUG-497~500) |
|
||||
| `TASK-rectification-adopt-flow-fix-20260903.md` | — | 采用流程修复单(含删「用这个时间看盘」) | 已验收 | `e8c98c37`(BUG-501/502) |
|
||||
| `TASK-rectification-uncertainty-stop-20260903.md` | — | 不确定度停止规则加样本下限 | 已验收 | `0c0df426`(BUG-503) |
|
||||
| `TASK-rectification-ux-20260902.md` | — | 会话面空白假死与交互摩擦 | 执行中 | 分支 `codex/rectification-ux-20260903`(09-03 在新基线重做,BUG-505 起) |
|
||||
| `TASK-rectification-ux-20260902.md` | `PROGRESS-rectification-ux-20260903.md` | 会话面空白假死与交互摩擦 | 待合并 | 分支 `codex/rectification-ux-20260903`(09-03 在新基线重做,BUG-505–509,已推送待验收) |
|
||||
|
||||
### 聊天主链路与首页
|
||||
|
||||
|
||||
Reference in New Issue
Block a user