docs(tasks): birth-sky polish — poster-only dialog, date/time/city caption, slower reveal, paywall on first 402

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-28 23:27:30 +08:00
co-authored by Claude Opus 5.5
parent be9e4dbee5
commit 84df129972
2 changed files with 142 additions and 0 deletions
+1
View File
@@ -250,6 +250,7 @@
| `TASK-site-button-contrast-20260928.md` | `PROGRESS-site-button-contrast-20260928.md` | **全站实心按钮配色与禁用态统一**:层外 `button { color: inherit }` 压掉 Tailwind 文字色(61 个 `<Button>` 浅 3.33 / 深 2.57),`.button-primary` 30 个浅色 3.14,全部禁用态 45% 透明;产品选 A:全站用报告页配色(浅 4.85 / 深 5.77),推翻 DESIGN「已知缺口」;禁用改灰;删 BUG-1078 局部覆盖(BUG-1079) | 已验收、已部署 staging `dc640931`(真机欠) | `12aac87b` + `fa58df16`;BUG-1079 |
| `TASK-birth-sky-cover-20260928.md` | `PROGRESS-birth-sky-cover-20260928.md` | **「那一刻的天空」封面**:出生那一刻头顶的真实天空做成 3:4 封面(产品从 A 年轮 / B 一笔画 / C 天空三版样稿中选 C,样稿 `assets/birth-sky-cover-sample-20260928.html`);星盘页按钮 + 保存图片、报告封面(须对得上报告出生资料)、首次建盘揭幕;不印出生资料、句库只描述天空、无新依赖 | 已验收(Claude 2026-09-28),随分支五个提交推 staging;T5 由追加单解除 | 新功能,不开 BUG |
| `TASK-birth-sky-followup-20260928.md` | `PROGRESS-birth-sky-cover-20260928.md`(追加单) | **天空封面追加**:报告封面按生成时出生资料指纹绑定(方案 A,旧报告永不显示);首次建盘揭幕改群星汇聚过场(无按钮、3 秒预算、可点跳过、减弱动态只淡入淡出) | 已验收(Claude 2026-09-28),已推 staging | 新功能,不开 BUG |
| `TASK-birth-sky-polish-20260928.md` | `PROGRESS-birth-sky-polish-20260928.md` | **天空封面第三轮**:星盘页弹层只留图;封面印「日期 时刻 · 城市」(推翻「不印出生资料」);过场放慢到约 6.5 秒;兑换弹窗改到首条付费消息被拒(402)时再弹;BUG-1080 过场被兑换弹窗挡住 | 待领取(分支 `codex/birth-sky-pacing-20260928` 已建) | BUG-1080 |
| `TASK-serif-headings-20260928.md` | `PROGRESS-serif-headings-20260928.md` | **全站标题改用自托管宋体、正文保持黑体**:产品推翻 BUG-737「CJK 不用衬线」结论(保留「声明的字体必须可加载」「不落系统宋体」两条);Noto Serif SC SemiBold 按通用规范汉字表 6500 字 unicode-range 切片自托管(改名 Jyotisha Serif SC),swap 不 preload;首页宋体流量 ≤300 KB;先于天空封面单 | 已实现待验收(分支 `codex/serif-headings-20260928`,未推送) | 不开新 BUG;BUG-737 追加说明 |
| `TASK-cend-ui-claude-alignment-20260916.md` | `PROGRESS-cend-ui-r1/r2/r3-20260916.md` | **C 端界面向 claude.ai 产品界面对齐(三轮串行 R1→R2→R3,都动 `globals.css`,不得并行)**:根因是 `frontend/CLAUDE_DESIGN.md` 扒的是 **claude.com 营销官网**,它自己在 Known Gaps 里写明 claude.ai 产品界面不在范围内,而 `DESIGN.md:3` 把它当成了产品界面的实现契约。**R1**:`--font-display` 里 Tiempos Headline / StyreneB **从未加载**(无 `@font-face`、`public/` 无字体、`layout.tsx` 只 vendor 了 Inter),中文标题全站落到 **宋体 / SimSun**,波及 20 处含助手回答的 h2/h3(BUG-737);亮色强调色 `#85432f` 与暗色 `#d78064` 不同源,产品拍板亮色换 **Claude coral `#cc785c`**,**易漏点**是 `globals.css:16` 的 `--color-ring` 硬编码在 `@theme inline` 里不跟随 `:root`,另有第四个 `:root` 亮色块(`:4358`)必须同步(BUG-738);`.composer-footer` 常驻 44px + 顶栏 68px + `--composer-reserve` 148px,每屏固定吃掉 216px,模型选择器移进输入框内部、删掉底栏、顶栏收到 46px 并删「分析对象」副标题。**R2**:空状态是营销落地页(hero 卡 + 两张 132px 入口大卡 + 3 列 156px 主题卡),输入框被压在 **800px 以上**内容之下,重排成「问候 + 居中输入框 + 两枚入口 pill + 一排 chip」。**R3**:侧栏两个 `<details>` 拍平成一条「最近」、星盘的两个入口(侧栏分组 + 账户菜单)收敛到一处、删掉逐条助手头像。**决策记录 D3 推翻 DESIGN.md「报告强调色与应用同源」一句**(报告刻意保留深棕)。原型图 https://claude.ai/code/artifact/da275da6-2954-4f50-99aa-32bb8694d38b(三套画面 + 明暗,页面标题就是建议字体栈的实际渲染)。环境缺口:无登录态无 Chrome,四项真机观感留 `docs/testing/`。BUG 段 737–738 | 待领取 | — |
| `TASK-cend-surfaces-claude-alignment-20260916.md` | `PROGRESS-cend-shell-20260916.md`、`PROGRESS-cend-report-20260916.md`、`PROGRESS-cend-rectification-20260916.md`、`PROGRESS-cend-chart-eph-20260916.md` | **次级页面对齐(上一单的续篇,R4→R5/R6,R7、R8 可并行)**:星盘 `/chart`、星历 `/ephemeris`、报告 `/reports` **各是脱离 app 外壳的独立全屏页**,顶部只有一个「返回对话」链接、侧栏整个消失,且三家各写了一套一模一样的 `*-shell`/`*-topbar`/`*-hero` 骨架——与上一单 E5 同根因(营销站 band 结构被套到产品界面)。**R4** 抽只读导航外壳 `AppNavRail`(只用现成的 `GET /api/sessions` + `GET /api/account`,会话行走 `sessionHref` 跳 `/?c=<uuid>`;**刻意不带**重命名/删除/收藏/归档——那套连着 `Home()` 的乐观更新与回滚,搬过来会撞 useState 增长门禁)。**R5** 星盘五 tab 下划线化 + 参数合表 + 行星表横向滚动;星历日期导航改 `‹ 日期 ›`。**R6** 报告中心卡片网格改行式列表;阅读页加常驻目录。**R7** 生时校正把可信区间从盘面板标题行提成常驻条(窄屏 `.is-compact` 下盘面板是 overlay,现在默认看不到区间),五个 `technique-audit` 折叠块收成两段。**R8** 设置内容区收窄(880px 弹窗里表单铺了 690px)、套餐卡三修饰符收敛成两态。**已解锁**:原挡路的设置单已于 `111b4a84`(BUG-698)合入。**两条不得回退**:BUG-698 的 `@supports (height: 1dvh)` 写法(重复声明回退会被 Lightning CSS 折叠)、BUG-616/617 的报告盘面 grid 实现。默认不占 BUG 号 | **R4–R8 全部已实现并验收合入** | R4:抽出 `AppNavRail`(只读,两个 GET,零写操作)+ `SecondaryShell`,三个次级页并入 app 外壳并删掉各自的 shell/topbar/hero;四个路由渲染标记**完全不变**(`/` `/chart` `/ephemeris` 仍 Static);CSS gzip −0.25%。`/reports/[reportId]` 留给 R6 与目录一起做。两处自身健壮性问题被测试抓到:`usePathname()` 可为 null、`fetch` 可能不存在。差点弄丢 BUG-717 的 eyebrow 文案(已放回)。R8:表单分区收窄到 440px(列表分区不变)、套餐卡三修饰符收敛成互斥的 `is-current` / `is-recommended`,`--highlighted` 删除改为滚动定位;手机端 `order:-1` 改挂 `[data-plan-alias]`(版位不是状态)。测试 3350→3354(净增 4),失败清单与基线逐条一致;`/` 仍 Static;我的干净构建实测 CSS gzip −3 字节。**遗留待产品拍板**:`?plan=` 深链现在完全没有视觉指向,只有滚动位置。R6:报告中心卡片网格改行式列表、阅读页并入外壳并把目录挪到右侧常驻。**任务书 E10 过期**——目录在 `cfcd369d` 就已存在,本轮是挪位置定稿而非从零加。挂外壳带出一个真实打印风险已处理:`.chat-app`/`.chat-panel` 是 `height:100%;overflow:hidden`,裸 `window.print()` 会把九节报告裁成一页,阅读页因此多挂一条只在挂载期生效的 print 样式解锁外壳。「生成中的分节进度」做不了——`REPORT_LIST_COLUMNS` 不返回节数,按 VOICE.md 不许前端编。R7:区间常驻条与盘面折叠收敛。**任务书 E9 也不准确**——对话区顶部早有常驻条 `RectificationTimeline` 且窄屏可见,真正只在盘面标题行的是**代表分钟**;因此没另造第二条,在既有条上补齐代表分钟与已答题数(与盘面同一次 `workingRectificationTime()` 调用)。折叠块实际是 **8 个**不是 5 个。**触发让步顺序第 5 条**:收窄进度未做——服务端无该字段,且 `candidate_range` 会放宽(BUG-572),前端相减会把一次放宽报成收窄,已写进 `BLOCKED.md`。顺带修掉一个**静默失效的旧断言**(`slice(indexOf(A), indexOf(B))` 在 B 改名后变成几乎整份文件,四条 `doesNotMatch` 假通过)|
@@ -0,0 +1,141 @@
# TASK · 天空封面第三轮:弹层只留图、印出生日期时刻城市、过场放慢、兑换弹窗改到首条付费消息(2026-09-28)
## 基线与依赖
- 基线:`origin/staging` = `be9e4dbe`(天空封面五个提交 + 验收文档,已部署 staging)。开工前 `git fetch origin --prune`,基线 SHA 写进 PROGRESS。
- 分支 / 工作树:`codex/birth-sky-pacing-20260928` / `.worktrees/birth-sky-pacing-20260928`(Claude 已从 `be9e4dbe` 建好,空分支)。
- 进度记录:`docs/tasks/PROGRESS-birth-sky-polish-20260928.md`。
- 前置任务书:`TASK-birth-sky-cover-20260928.md`、`TASK-birth-sky-followup-20260928.md`。
- 本单会改 `frontend/src/app/(app)/page.tsx`,与其他同日改 `page.tsx` 的单**串行**:本单开工时若有别的分支在改 `page.tsx`,先停下告知 Claude。
## 事实(产品 2026-09-28 staging 真机反馈)
1. **星盘页「那一刻的天空」弹层难看**(截图,桌面窄窗口):
- 结构是灰色毛玻璃大面板 + 标题栏「那一刻的天空 / 关闭」+ 图 + 「保存图片」;
- 面板被拉得比图高很多,图下方一大片空白灰底;
- 实现:`frontend/src/components/birth-sky/birth-sky-dialog.tsx`(`.birth-sky-panel` / `.birth-sky-header`),样式 `birth-sky.css`。
2. **封面底部没有出生资料**:`drawBirthSky` 在事实句下只画 footnote「按你填写的时间 / 按校正后的时间」。这是第一份任务书「不印任何出生资料」的结果。
3. **新账号第一次建盘看不到群星过场,只看到「解锁完整咨询」弹窗**:
- 兑换弹窗由 `page.tsx` 里 `setOnboardingPaywallOpen(true)` 所在的 effect 打开,条件是 `starterHomeVisible && credits <= 0 && 无有效订阅`;
- 本人保存出生地后,首页立刻变成 starter home,所以兑换弹窗和过场**在同一刻**被触发;
- 兑换弹窗打开时 `modalOpen` 为真,`SidebarInset` 变 `inert`(BUG-968 的设计),而过场的 `<dialog>` 渲染在 `Home()` 里,也就是在这个 `inert` 子树**内部**;
- DESIGN §17 写「过场在兑换弹窗之上」,但这一点只在单元测试里断言过,**没有在浏览器里验证过**。真实原因要执行方在无头 Chrome 里复现后才能定(见 P4)。
4. **过场太快**:`lib/birth-sky/converge.ts` 里汇聚 1.5 s、文字 1 s、淡出 0.5 s,总长 3 s。
## 决策记录(产品 2026-09-28 授权)
1. **星盘页弹层只留图**:去掉灰色面板、标题栏、「关闭」文字按钮。点开后,压暗的页面上只放这张图本身,按视口完整显示;图下方一个小的「保存图片」。点图外、按 Esc 关闭。
2. **封面印出生资料:日期 + 时刻 + 城市。**
- **这推翻了 `TASK-birth-sky-cover-20260928` 的「不印任何出生资料」和对应的 BFF 合同测试断言**。执行方不得以既有红线为由拒改。
- 只印到城市;不印区县、经纬度、时区、名字。
- 格式:`1994年5月18日 07:40 · 杭州`。取不到可靠的城市就只印 `1994年5月18日 07:40`,不猜。
- 三处都印:星盘页图片、报告封面、首次建盘过场(它们共用 `drawBirthSky`)。
3. **过场放慢**:汇聚 3 s → 文字淡入并停留 2.5 s → 淡出 1 s,总长约 6.5 s。随时点一下或按 Esc 仍然立刻结束。「天空数据 3 秒内就绪才放,否则静默跳过」这条**不变**。
4. **「解锁完整咨询」弹窗改到第一次付费消息被拒时再弹**:
- 删掉「进入 starter home 就弹」的 effect;
- 用户发消息后,服务端以 402(点数不足)拒绝时:本页第一次弹兑换弹窗(`OnboardingRedeemPaywall`),之后再遇到 402 照旧打开设置弹窗的 billing 页;
- 以服务端 402 为准,前端不自己预判点数。这样免费的闲聊轮(「你好」,BUG-976)和入门问题照常免费,不会被弹窗拦住;
- 402 时草稿照旧退回输入框(现有 rollback 逻辑),兑换成功后用户点发送即可,不自动重发。
## 硬红线
1. **BFF 只下发一个已格式化的 `caption` 字符串**,例如 `1994年5月18日 07:40 · 杭州`。仍然不得下发经纬度、时区、名字、区县、原始日期字段。
2. `page.tsx` 行数不增长;`Home()` 的 `useState` / `useRef` 数不增长。兑换弹窗沿用现有的 `onboardingPaywallOpen` / `onboardingPaywallShown`,不新增状态。
3. 过场、报告封面**不得变成等待**,沿用原预算,不加 spinner / 骨架(DESIGN §9)。
4. 汇聚最后一帧仍与导出封面逐调用一致(同一个 `drawBirthSky`,只插值位置)。
5. 不新增 npm 依赖,不改表、不加迁移。
6. 改任何既有断言都写「原值 / 新值 / 原因」三栏;动组件前先 grep `frontend/tests/` 与 `tests/` 里按源码文本断言的合同测试。
7. 隐私:测试与样张只用虚构资料(1994-05-18 07:40 UTC+8,杭州,30.27N 120.15E)。
## 任务分解
### P1 · 星盘页弹层只留图
- 改 `birth-sky-dialog.tsx` 与 `birth-sky.css`:
- 原生 `<dialog>` 保留(焦点与 Esc 都靠它);`::backdrop` 压暗(深色半透明),dialog 本身透明、无边框、无面板。
- 图 `<img>`(仍是画好的 PNG,iOS 长按可存):3:4,按视口取最大,宽高都不超过视口减去上下留白和按钮高度;圆角沿用 DESIGN token。
- 图下方居中一个「保存图片」,在深色底上可读(用 DESIGN 已有的深色描边按钮样式,不新造颜色);保存失败的提示文案不变。
- 右上角保留一个只有图标的 × 关闭(`aria-label="关闭"`),给键盘和读屏用;点图外、按 Esc 也关闭。
- 删掉 `.birth-sky-panel`、`.birth-sky-header` 及其样式,确认没有别处引用。
- **验收**:
- 无头 Chrome 在 390×844、1280×900、1280×600 三个视口各截一张(样稿虚构资料),图完整可见、下方没有空白面板,放进 scratchpad,路径写 PROGRESS;
- 测试:弹层源码不再有标题栏与面板;有 × 和「保存图片」;点 backdrop 与 Esc 关闭。
### P2 · 封面印日期 + 时刻 + 城市
- 服务端(`lib/birth-sky/service-core.ts`):从服务端读到的出生资料生成 `caption`,加进 `lib/birth-sky/contract.ts` 的合同。
- 日期与时刻用出生地当地时间(就是用户填的那个),24 小时制,时刻补零:`1994年5月18日 07:40`。
- 城市:写一个纯函数从地点标签里取城市,规则写在函数注释里,并各有测试:
- `河北省 · 邯郸市 · 峰峰矿区` → `邯郸`;
- 直辖市(如 `北京市 · 朝阳区`)→ `北京`;
- 取不到可靠城市(空、只有省、看不懂的格式)→ 不带城市。
- 海外地名按现有标签格式定一条保守规则;拿不准就不带城市。
- 城市去掉末尾的「市」。
- 画法(`lib/birth-sky/draw.ts`):`caption` 画在事实句下面,**替换**原来 footnote 那一行的位置。
- 时间来源只在采用了校正时间时出现:caption 后面接 ` · 校正后`;按填写时间的不加后缀(「按你填写的时间」这句去掉)。
- 字号、颜色与原 footnote 一致;`paper` palette 同样生效。
- `VOICE.md` 更新:去掉「按你填写的时间 / 按校正后的时间」,写入 caption 格式与「· 校正后」后缀。
- **合同测试改动**:`frontend/tests/birth-sky-route.test.ts` 里「响应不含 1994、07:40、placeLabel……」的断言,改成「只在 `caption` 里出现日期、时刻、城市;不含经纬度、时区、名字、区县、原始日期字段」,三栏说明写进 PROGRESS。`tests/test_birth_sky.py` 如有相关断言同样处理。
- golden:`scripts/generate_birth_sky_golden.py` 重新生成夹具,前端测试读新夹具。
- **验收**:caption 各格式一条测试;导出 PNG 与过场停留帧里都有 caption;报告封面 paper palette 有 caption;重新截三张海报(白天 / 夜里 adopted / 傍晚 paper)放 scratchpad。
### P3 · 过场放慢
- `converge.ts`:`BIRTH_SKY_CONVERGE_MS = 3_000`、`BIRTH_SKY_TEXT_MS = 2_500`、`BIRTH_SKY_FADE_MS = 1_000`。文字淡入从 400 ms 放到 600 ms;整层前段淡入从 200 ms 放到 400 ms;reduced-motion 淡入 600 ms、淡出 1 s。
- 时间轴测试随之改数值(三栏说明);「t=0 散开、t=1 真实位置」和「停留帧 = 导出封面」两条测试原样保留。
- DESIGN §17 时序段落同步。
- **验收**:重新取 t=0、汇聚过半、汇聚完成、停留四帧,路径写 PROGRESS。
### P4 · 兑换弹窗改到首条付费消息被拒(含 BUG-1080)
- **先复现,再定根因**(写进 BUG-1080):
- 在无头 Chrome 里搭一个最小页面:一个带 `inert` 的容器,里面 `showModal()` 一个 `<dialog>`,外面 portal 一个 z-index 很高的兑换弹层。截图看过场是否可见、`pointerdown` 能不能收到。
- 同时查首页在「保存出生地 → starter home」这一刻:兑换 effect 和 `startBirthSkyReveal` 各自何时触发、有没有别的原因让过场被跳过(例如 3 秒预算内拿不到数据)。
- 结论写进 BUG-1080:确认的就写确认,查不清的写 `investigating` 并列出剩下的可能。
- **修复(不依赖根因结论,都要做)**:
1. 删掉 `page.tsx` 里「进入 starter home 就弹兑换弹窗」的 effect。
2. 402 分流:`hooks/use-consultation-run.ts` 里 402 时现在调用 `openAccountDialog("billing", { source: "insufficient-credits" })`(`rejectRun` 与另一处 `source: "insufficient-credits"`)。改成:本页第一次 402 打开 `OnboardingRedeemPaywall`,并置 `onboardingPaywallShown`;之后的 402 照旧打开 billing。
- 建议做法:给 `useConsultationRun` 加一个可选依赖,例如 `onInsufficientCredits`,返回是否已处理;`page.tsx` 传一个复用现有 ref / state 的回调,同时删掉旧 effect,保证行数不增长。执行方可换更简单的写法,但不得新增 `useState` / `useRef`。
3. 过场的 `<dialog>` 用 `createPortal(…, document.body)` 挂到 `SidebarInset` 之外,与兑换弹窗、账户弹窗同一做法(BUG-968),让它不受 `inert` 影响。
4. 兑换弹窗里「入门问题已经准备好。」这句要改,因为弹出的时机已经不是入门问题做完的时候。先对照 VOICE 改成与新时机相符的一句,写进 PROGRESS 让 Claude 验收。
- 生时校正对话里的 402(`lib/rectification-chat-turn-run.ts`)**不在本单范围**,保持现状。
- **验收**:
- 测试:`page.tsx` 不再有进入首页就弹的 effect;第一次 402 打开兑换弹窗、第二次打开 billing;402 后草稿退回输入框;免费轮不触发。
- `membership-page.test.ts` 的 “finished starter questions open the redeem paywall…” 按新时机改写,三栏说明。
- 过场 portal 到 `document.body` 有源码合同测试。
- 无头 Chrome 最小复现的截图与结论写进 PROGRESS 与 BUG-1080。
### P5 · 文档
- `frontend/DESIGN.md` §17:弹层只留图;caption 格式;新时序;删掉「过场在兑换弹窗之上」这句,改写兑换弹窗的新时机。
- `frontend/docs/VOICE.md`:caption 格式与「· 校正后」后缀;兑换弹窗新文案。
- `CHANGELOG.md`:一条。
- `docs/BUG_HISTORY.md`:BUG-1080(过场被兑换弹窗挡住)。
- `docs/testing/birth-sky-cover-checklist.md` 追加真机条目:
- 新账号(0 点)首次建盘:完整看到约 6.5 秒的过场,期间不弹兑换弹窗;
- 之后发一条正式问题:弹「解锁完整咨询」,草稿还在输入框;关掉后再发一次,打开的是 billing;
- 发「你好」这类闲聊:不弹;
- 星盘页点「那一刻的天空」:只看到图和「保存图片」,没有灰色面板;手机和电脑各看一次;
- 图底部是「日期 时刻 · 城市」;用直辖市和海外地点各试一次。
## 验证
- `tsc --noEmit` 0 错;`npm run lint` 0 error。
- `npm test` 全量:与基线 `be9e4dbe` 比**测试名清单**,不得有测试名消失;失败清单与基线(24 条 Docker / 部署套件)逐条同名。
- Python:`python3 -m pytest tests/test_birth_sky.py tests/test_api_server_growth_contract.py tests/test_repo_privacy_markers.py`;`python3 scripts/run_quality_gate.py --profile quick`。
- `rm -rf .next && next build`:`/` 仍 `○ Static`;首屏 JS / CSS gzip 与基线 `be9e4dbe` **同口径**对比(同一台机器、同一脚本各建一次),变化在 ±2% 内。
## 让步顺序
1. P4 第 4 条(兑换弹窗文案)可以先保留原句,写进 PROGRESS 留给产品。
2. 海外地名的城市规则可以一律不带城市。
3. P1 弹层只留图、P2 caption(至少日期 + 时刻)、P3 放慢、P4 首页不再自动弹兑换弹窗,**都不可让步**。
## 交付
- 一个或两个提交,推到本地分支即可,**不推送**;由 Claude 独立验收后快进推 staging。
- 提交信息与 PROGRESS 引用本任务书。
## BUG 编号
开工时核对 `docs/BUG_HISTORY.md` 最大号(写作时 BUG-1079)。过场被兑换弹窗挡住记为 **BUG-1080**;实现中再发现的既有缺陷从 BUG-1081 起编。