feat(birth-sky): poster-only dialog, date/time/city caption, slower reveal, paywall on first refused send (P1–P5, BUG-1080)

TASK-birth-sky-polish-20260928.
- P1: chart page dialog shows only the picture on a dimmed stage, 「保存图片」 below, icon × in the corner.
- P2: BFF returns one server-built `caption` (「1994年5月18日 07:40 · 杭州」); printed under the fact line in all three places; adopted time adds 「 · 校正后」.
- P3: convergence 3 s + words 2.5 s + fade 1 s; settled frame still equals the exported cover.
- P4: redeem paywall no longer opens when the starter home appears; first send refused for credits opens it, later ones open billing; convergence dialog portalled to <body>. BUG-1080 investigating (Chrome repro rules out the overlay covering the convergence).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N4f2nya58RoRu4yEmJgRGE
This commit is contained in:
Jesse_Chen
2026-09-29 00:03:46 +08:00
co-authored by Claude Opus 5.5
parent 1a4f77584b
commit dec36bce01
30 changed files with 670 additions and 165 deletions
+16
View File
@@ -14531,3 +14531,19 @@
- 复发自:BUG-1078(同一根因,1078 只修了报告作用域)
- 修复版本:`12aac87b` + 文档 `fa58df16`,随 `dc640931` 部署 staging。
## BUG-1080 | 新账号首次建盘:看不到群星过场,只看到「解锁完整咨询」兑换弹窗
- 状态:investigating(修复已在分支 `codex/birth-sky-pacing-20260928`,待部署 staging 后真机确认;过场没出现的确切原因未在真实环境取证)
- 首次发现 / 最近更新:2026-09-28 / 2026-09-28
- 影响面:0 点数、无订阅的新账号,首页 onboarding 保存本人出生地之后的那一刻(`TASK-birth-sky-followup-20260928` F4 群星过场)。
- 现象:产品在 staging 真机新建账号、走完 onboarding 后,没有看到群星汇聚过场,只看到「解锁完整咨询」兑换弹窗。
- 触发条件:`saveOnboardingPlace` 成功 → `onOwnBirthPlaceSaved` 开始为过场取数(3 秒预算);同一刻首页变成 starter home,`page.tsx` 里「`starterHomeVisible` 且 0 点数且无订阅」的 effect 立即打开兑换弹窗,`modalOpen` 使 `SidebarInset` 变 `inert`,而过场 `<dialog>` 渲染在 `Home()` 里、也就是这棵 inert 子树内部。
- 已排除 / 已确认:
1. **「兑换弹窗盖住过场」在 Chrome 里不成立(已用无头 Chrome 151 最小复现排除)**:`inert` 容器里先放兑换弹层(portal 到 body、z-index 40、输入框已聚焦),再对容器内的 `<dialog>` 调 `showModal()`:不抛错、`:modal` 为真,截图里过场在最上层完整可见,`elementFromPoint` 命中过场画布,CDP 真实鼠标按下与 Esc 都送达过场(`pointerdown`、`cancel` 事件都触发)。与 BUG-968 不同:BUG-968 的账户弹窗是普通 `div`,不进顶层,所以会被 `inert` 锁住;`<dialog>.showModal()` 进顶层。复现页与截图:`scratchpad/polish/bug1080/repro.html`、`inert.png`、`plain.png`(会话 scratchpad,不进仓库)。Safari 未能验证。
2. 仍可能的原因(未取证):① 3 秒预算内没拿齐数据(`/api/birth-sky` 冷启动、宋体切片等待最多 1.5 秒、过场 chunk 下载),过场被静默跳过,页面上只剩兑换弹窗;② Safari / iOS 对 inert 子树内顶层 `<dialog>` 的处理与 Chrome 不同;③ 产品当时的账号不是在首页 onboarding 里保存出生地(例如从设置或星盘档案补资料),本来就不触发。
- 修复(`TASK-birth-sky-polish-20260928` P4,不依赖根因结论):① 删掉「进入 starter home 就弹兑换弹窗」的 effect;兑换弹窗改为本页第一次因点数不足发不出消息(前置余额检查或 402)时弹,之后照旧打开 billing(`lib/redeem-paywall-once.ts` + `useConsultationRun`),草稿留在输入框。建盘后首页不再有任何自动弹窗与过场抢位置。② 过场 `<dialog>` 改为 `createPortal(…, document.body)`,挂在 `SidebarInset` 之外,与账户弹窗、兑换弹窗同一做法,任何浏览器都不再受 `inert` 影响。③ 过场放慢到约 6.5 秒(产品同批要求)。
- 验证:`frontend/tests/birth-sky-polish-20260928.test.ts`(兑换弹窗只由被拒的发送打开、第一次兑换弹窗第二次 billing、UI 预览不弹、402 后草稿退回、过场 portal 到 body);`membership-page.test.ts` 改写的时机断言;全量前端测试失败清单与基线逐条相同。无头 Chrome 最小复现见上。
- 防复发:首页不得在「本人刚保存出生地」这一刻自动弹出任何弹窗;顶层 `<dialog>` 也要 portal 到 `SidebarInset` 之外;过场是否真的放了,部署后按 `docs/testing/birth-sky-cover-checklist.md` 新账号条目真机走一遍(若仍看不到过场,优先查 3 秒预算内 `/api/birth-sky` 的耗时)。
- 相关记录:BUG-968(`SidebarInset` inert 与 portal 约定)、BUG-434(兑换弹窗样式);`TASK-birth-sky-followup-20260928` F4、`TASK-birth-sky-polish-20260928` P4。
- 修复版本:分支 `codex/birth-sky-pacing-20260928`(本条所在提交),未部署。