Files
Jyotisha/docs/tasks/TASK-birth-sky-followup-20260928.md
T
Jesse_ChenandClaude Opus 5.5 be9e4dbee5
Independent Staging Quality Gate / validate (push) Successful in 13m10s
Independent Staging Quality Gate / publish (push) Successful in 4m5s
docs(tasks): Claude acceptance for birth-sky cover + followup
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N4f2nya58RoRu4yEmJgRGE
2026-09-28 22:19:40 +08:00

8.9 KiB
Raw Blame History

TASK · 天空封面追加:报告封面绑定(方案 A)+ 首次建盘群星汇聚过场(2026-09-28)

基线与依赖

  • 前置任务书:TASK-birth-sky-cover-20260928.md。它的实现在本地分支 codex/birth-sky-cover-20260928,有三个提交(d277cbd8 T1–T4+T7、028aef27 T5 blocked、14ef18ac T6 弹窗),尚未推送、尚未由 Claude 验收。
  • 本单在同一分支上继续,再追加两个提交;工作树 .worktrees/birth-sky-cover-20260928。开工前 git fetch origin --prune:
    • origin/staging 若前进了,先 git rebase origin/staging(这个分支只有本系列在用);
    • 基线 SHA 写进 PROGRESS。
  • 进度记录继续写 docs/tasks/PROGRESS-birth-sky-cover-20260928.md,新增「追加单」一节。
  • 前置的宋体单 TASK-serif-headings-20260928 已在 staging(d670521f)。

事实

  1. 报告封面被挡住:报告没有记录生成时用的是哪一套出生资料,所以无法判断封面和报告是否对得上。
    • personal_reports 行、报告文档、job / section / appendix 都没有出生资料指纹;
    • request_fingerprint 不含出生资料;
    • calculation_hash 是引擎结果或 markdown 的哈希。
    • 这些证据见分支上的 BLOCKED.md「TASK-birth-sky-cover T5」。
  2. 第一次建盘揭幕目前是一个带「继续」按钮的弹窗(components/birth-sky/birth-sky-dialog.tsx 的 reveal 变体,由 hooks/use-birth-sky-reveal.ts 与 lib/birth-sky/reveal.ts 驱动)。

决策记录(产品 2026-09-28 授权)

  1. 报告封面选方案 A:
    • 从现在起,生成报告时写入当时的出生资料指纹;
    • 只有指纹与当前一致的报告才显示封面;
    • 旧报告没有这个字段,永远不显示封面,不补算、不回填;
    • 不改数据库结构。
  2. 第一次建盘的揭幕改成群星汇聚过场,替换现在的弹窗:
    • 星星从散开的位置汇聚到出生那一刻的真实位置;
    • 然后显现文字,文字停留一会儿后整层淡出,进入下一步;
    • 过场里不放任何按钮。
  3. 平时打开首页的加载动画不改。群星过场只在「本人第一次保存出生地」时放一次。

硬红线

  1. 出生资料与指纹都不下发客户端,比对在服务端完成。
  2. 不改表结构、不加迁移。
  3. JyotishAPIHandler 类方法数与 __new__ 伪造点不增长;Home() 的 useState / useRef 数不增长。
  4. 过场不得变成等待:
    • 天空数据必须先就绪才开始放;
    • 沿用现有的 3 秒预算,超时就静默跳过;
    • 不加 spinner 或骨架(DESIGN §9)。
  5. 汇聚完成的最后一帧必须与 drawBirthSky 导出的封面完全一致。复用同一个绘制函数,只对位置做插值。
  6. 不新增 npm 依赖。
  7. 移动或改写组件前,先在 frontend/tests/ 和 tests/ 里 grep 按源码文本断言的合同测试(AGENTS §7.8);任何改动的断言都写「原值 / 新值 / 原因」三栏。
  8. 隐私:只用样稿里的虚构资料(1994-05-18 07:40 UTC+8,30.27N 120.15E)。

任务分解

F1 · 报告写入出生资料指纹(方案 A)

  • 普通报告、长报告,每一条生成路径都在报告文档的 subject 里写入可选字段 birthFingerprint,取值为 resolveServerOwnedChartBirth(...).fingerprint。
  • 先核实它与星盘页下发的 profileFingerprint 是同一种算法;如果不是,两边统一用同一种,并在 PROGRESS 里说明。
  • v2 evidence bundle / 报告文档的 schema 加这个可选字段,旧报告照常能读。

验收:每条生成路径各有一条测试,断言写入了指纹;缺这个字段的旧文档照常能解析。

F2 · 服务端比对,下发封面对象

  • 报告视图(reportView,在 lib/personal-report-route-core.ts)新增 coverSubject 字段:
    • 报告存的指纹与该人物当前的指纹一致时,取值为 subject id(chart_profile_id 为 null 时取 "self");
    • 其余一律为 null,包括不一致、缺字段、不是本人的报告、人物已删除。
  • 不下发指纹原文。

验收:一致、不一致、缺字段、非本人四种情况各一条测试;序列化后的响应里不含指纹与出生资料。

F3 · 报告封面(原任务书 T5)

  • 屏幕上:coverSubject 不为 null 时,在正文最上方放封面块。
    • 用 night palette,宽度随正文列,比例 3:4;
    • 不放按钮;
    • 正文出现之后才取数;取数失败就不显示封面,不留占位,不加等待态。
  • 打印 / PDF:
    • 封面改用 paper palette;
    • 封面独占 A4 第一页(break-after: page);
    • 不新增打印入口。
  • 把分支上 BLOCKED.md 的 T5 条目划掉(不删),并注明「→ 已解除:产品 2026-09-28 选 A」。

验收:

  • 指纹一致时有封面;不一致或缺字段时无封面;取数失败时正文不受影响;
  • 打印样式断言 paper palette 与分页。

F4 · 首次建盘群星汇聚过场(替换 T6 弹窗)

  • 触发条件不变:只在首页 onboarding 里本人的出生地保存成功后触发一次。以下情况都不触发:新建家人档案、修改资料、平时打开首页。
  • 时序(总长约 3 秒):
    1. 汇聚约 1.5 秒:可见的恒星与行星从随机散开的位置出发,ease-out 汇聚到真实投影位置。散开位置由固定种子生成,同一张盘每次一样;地平线圆、方位字、高度圈同步淡入。
    2. 显现文字约 1 秒:主句与事实句淡入,并停留。
    3. 淡出约 0.5 秒:整层淡出,露出原本的下一步(问候 / 首页,或者出生时间校正引导),不改变下一步是什么。
  • 全屏覆盖层,不放按钮:
    • 点击、触摸或按 Esc,立刻结束并进入下一步;
    • 系统开了 prefers-reduced-motion 时,不做汇聚,只整体淡入、停留、淡出。
  • 实现要求:
    • 画布用 requestAnimationFrame,按视口尺寸与 DPR 绘制;
    • 组件卸载时取消动画;
    • 插值写成纯函数:t=0 是散开位置,t=1 是真实位置。
  • 结束后的提示:在首页轻提示一次「这片天空在星盘页可以保存。」。复用已有的 sonner Toaster,不写新组件;这句收进 VOICE.md。
  • 删除不再使用的弹窗 reveal 变体(「继续」按钮、is-reveal 样式)。星盘页那个「那一刻的天空」弹窗不动。

验收:

  • 插值纯函数有测试:t=0 等于种子决定的散开位置,t=1 与真实位置逐点相等;
  • 点击或 Esc 能立即结束;reduced-motion 走淡入淡出;
  • 触发条件与原 T6 相同(本人保存出生地才触发,家人档案、saveProfile、chart-profile-form 都不触发);3 秒内数据没到就不放;
  • 提示只出现一次;
  • 用无头 Chrome 从 golden 夹具取 t=0、0.5、1.0 与停留四帧 PNG,放进 scratchpad,路径写进 PROGRESS。

F5 · 文档

  • frontend/DESIGN.md §17:
    • 报告封面一段,写屏幕与打印的做法;
    • 群星过场一段,写时序、跳过、减少动态效果、「平时的首页加载动画不改」。
  • frontend/docs/VOICE.md:收录提示句。
  • CHANGELOG.md:追加一条。
  • docs/testing/birth-sky-cover-checklist.md,追加以下真机条目:
    • 新账号首次建盘,完整看一遍过场;
    • 过场中点一下能跳过;
    • 手机开「减少动态效果」后只淡入淡出;
    • 断网或慢网时过场不出现,也不卡住;
    • 新生成的报告有封面、打印 PDF 第一页是封面;
    • 旧报告没有封面;
    • 改过出生时间后,改之前生成的报告没有封面。

验证(两个提交都完成后)

  • tsc --noEmit 0 错;npm run lint 0 error。
  • npm test 全量:
    • 与分支 PROGRESS 记录的基线对比测试名清单,不得有测试名消失(被删除的弹窗测试逐条列出原因);
    • 失败清单与基线逐条相同。
  • Python:
    • python3 -m pytest tests/test_birth_sky.py tests/test_api_server_growth_contract.py;
    • python3 scripts/run_quality_gate.py --profile quick。
  • rm -rf .next && next build:/ 仍是 ○ Static;首屏 JS / CSS gzip 与基线 651,590 / 66,346 对比,变化在 ±2% 内。
  • 隐私:python3 -m pytest tests/test_repo_privacy_markers.py -q。

让步顺序

  1. 结束后的提示可以砍。
  2. 过场里地平线、方位字的淡入可以简化成与星点同时出现。
  3. F1~F3 的方案 A 绑定、「最后一帧与导出封面一致」、「不等待」不可让步。

交付

  • 在同一分支上再加两个提交:F1–F3 + 对应文档;F4 + 对应文档。
  • 不推送,由 Claude 独立验收后把整个分支(五个提交)一起快进推上 staging。

BUG 编号

这是新功能,不开 BUG。实现中若发现既有缺陷,从 docs/BUG_HISTORY.md 开工时的最大号 +1 起编(写作时是 BUG-1079)。