fix(web): 账户弹窗移出 inert;校正快照失败不再静默(BUG-968/969)

账户弹窗与入门付费墙 portal 到 document.body,SidebarInset 的 inert 不再罩住弹窗。
GET 校正快照每个非 200 打 JSON warn;客户端失败显示「这一问还没读到」和重新读取。
下一题是可渲染选择题时,确定性回复正文带题干,挂卡后去重。
This commit is contained in:
jesse-ux
2026-09-18 19:16:00 +08:00
parent e760b1d610
commit 824ecff026
20 changed files with 471 additions and 42 deletions
+9 -9
View File
@@ -12752,32 +12752,32 @@
## BUG-968 | 账户弹窗打开后整个弹窗点不动,退出登录做不了
- 状态:investigating(根因已由代码确认,修复未做)
- 状态:resolved
- 首次发现:2026-09-18
- 最近更新:2026-09-18
- 影响面:`src/app/(app)/layout.tsx` `AppShell`、`src/app/(app)/page.tsx` `AccountDialogOverlay` 渲染位置、`use-home-shell-registration.ts` `insetInert`;staging `877128ce`
- 用户现象:点侧栏「个人资料 / 退出登录」等任一账户入口,弹窗出现但关闭按钮、表单、确认退出全部点不动;正文区同时点不动。刷新后依旧。
- 触发条件:任何让 `modalOpen` 为真的弹窗(五个账户弹窗、onboarding 付费墙)。
- 根因:`e4e73f56` 把 `SidebarInset` 搬进 `(app)/layout.tsx` 后,`inert={modalOpen}` 罩住了整个 `children`,而 `AccountDialogOverlay` 由 `page.tsx` 渲染、没有 portal,落在 inert 子树里。搬家前弹窗是 `SidebarInset` 的兄弟节点。
- 修复:待做,见 `docs/tasks/TASK-account-dialog-inert-20260918.md` §3.1。
- 验证:待做。临时脱困:按 Esc(`window` 级监听不受 inert 影响)。
- 防复发:合同测试断言 `role="dialog"` 不在 inert 祖先内。
- 修复:选 (a) `createPortal(..., document.body)`。`AccountDialogOverlay` 与 onboarding 付费墙都挂到 `document.body`;`SidebarInset` 的 `inert={modalOpen}` 和移动端抽屉 `isMobile && openMobile` 分支不动。选 portal 而不是把节点交给 layout,是为了不改 `registerShellControls` 协议、并保住现有 DOM 结构与样式。
- 验证:`frontend/tests/account-dialog-inert-20260918.test.ts` 断言 overlay / paywall portal 到 `document.body`、layout 仍 inert `SidebarInset`、`role="dialog"` 渲染不含 inert;现有 overlay / sidebar / a11y 合同见定向测试。真机五弹窗点关闭/提交见 `docs/testing/account-dialog-inert-20260918.md`。
- 防复发:合同测试断言弹窗 portal 到 `document.body`,且 `SidebarInset` 仍吃 `inert`。
- 关联记录:BUG-744~746(侧栏统一)、BUG-926~929(外壳搬家)、BUG-965(跨部署标签页,另一问题)
- 复发自:无
- 修复版本:待发布
- 修复版本:`1ace0e64`
## BUG-969 | 生时校正「换一件事问」之后无下文:快照拿不到,两端都不吭声
- 状态:investigating
- 状态:mitigated(快照失败根因仍未由线上日志证实,不得写 resolved)
- 首次发现:2026-09-18
- 最近更新:2026-09-18
- 影响面:`GET /api/rectification/cases/[caseId]`、`rectification-agentic-chat.tsx` `loadCaseSnapshot`、`rectification-surface-state.ts` `hydrateRectificationCase`、确定性回复的题干投递
- 用户现象:口述两轮经历后说「想不到了」,回复「已记录。这题先不计分,换一件事问。」然后没有题卡;右侧盘面停在占位、时间轴无候选点、输入框占位是「再说一件带年月的事」。
- 触发条件:真实 Case 上服务端已落库下一道财务探针(active,绑在该回复轮),GET 快照本地用真实数据行重算完整、客户端解析函数全部通过;客户端却仍是开场时的快照状态。
- 根因:未定。已确认的是链路两端都静默:GET 路由所有异常 503 不写日志,客户端快照失败一律吞掉,边缘与应用层都无 GET 访问日志,PG 未开函数统计。事后无法证明请求是否到达服务端。
- 修复:待做,见 `docs/tasks/TASK-account-dialog-inert-20260918.md` §3.2(服务端非 200 记日志、客户端失败显示状态、确定性回复正文带题干)。
- 验证:待做。
- 修复:① GET 每一个非 200(401/400/409/404/503,含 supabase 配置失败)`console.warn` 一条 JSON(`event=rectification_case_get_failed`、`case_id`、`status`、`code`、`reason=safeToolErrorCode`,不含用户资料)。② `loadCaseSnapshot` / `hydrateRectificationCase` / `refreshRectificationCase` 把非 2xx 或网络错误带回调用方;校正面显示「这一问还没读到。」+「重新读取」,不再装成采集等待。③ 下一题是可渲染选择题时,`persistApplied` 与 `persistCollectDenialTurn` 用 `composeCollectSpokenAssistantText` 把题干追加进正文并随流发出;卡片仍由 GET 挂上,`attachQuestionsToTurns` 的 `stripQuestionSentences` 去重。
- 验证:`frontend/tests/account-dialog-inert-20260918.test.ts` 覆盖 GET warn、503 文案与按钮、ack+stem 流式及挂卡后题干只出现一次;hydrate 失败带 `failure.status`。真机清单见 `docs/testing/account-dialog-inert-20260918.md`。若真机仍复现,凭服务端 JSON warn 把本条转 confirmed 并补根因。
- 防复发:确定性回复不得只靠 GET 挂卡投递题干;快照失败不得静默。
- 关联记录:BUG-525(题干经 asked_turn_id 挂上)、BUG-930(回答钉顶:只见最后一轮是设计)、BUG-965
- 复发自:待确认
- 修复版本:待发布
- 修复版本:`1ace0e64`