docs(tasks): onboarding journey brief (who-chart step, first ask free, checklist, hints, motion)

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014Ad9rqAB8pXXvyzh7f4X8v
This commit is contained in:
Jesse_Chen
2026-10-22 23:05:52 +08:00
co-authored by Claude Opus 5.5
parent c1aa72fcbf
commit 8743f61512
2 changed files with 319 additions and 0 deletions
+1
View File
@@ -37,6 +37,7 @@
| 任务书 | 进度 | 主题 | 状态 | 落点 |
| --- | --- | --- | --- | --- |
| `TASK-onboarding-journey-20261022.md` | — | **新用户启程**:建档加「先看谁的盘」(自己 / 帮别人看,本人资料可后补)、人物关系字段、首问免费(服务端原子)、起步三问、启程清单(七项从数据推导,收费项标价,老用户可见)、八条一次性提示(含星盘四页签)、启程流程专用动效。产品 2026-10-22 拍板 D1~D6;动表,不进 agent 循环。BUG-1420 起 | 待领取 | 原型 https://claude.ai/artifact/PcTwMVb9RD611R3qStxv3U |
| `TASK-rectification-narrower-delivery-20261016.md` | `PROGRESS-rectification-narrower-delivery-fix-20261017.md` | **交付区间收窄**:显示改按活着的簇分段。第 1 次打回只补两处显示漏段(取整差 8 分、缺区间整段退回)。对半出题和门槛 8 改 6 仍不做。基线与对照各 924 条,12 格包含率都过红线。BUG-1367 起,本轮原记 BUG-1370,因与婚恋合盘保留号冲突改为 BUG-1392 | 待验收 | `codex/rectification-narrower-delivery-20261016` |
| —(子代理直接执行,无任务书) | `PROGRESS-bug-1060-d9-double-transit-20260927.md` | **双重过运 D9 目标错配(BUG-1060)**:D9 第 N 宫目标误用 D9 上升、D9 宫主目标误用 D1 度数,改为标签所写的 D9 星座;D1 / CL 层逐字节不变;数据卡 golden 重采集(一张盘跨层结论 9→10);另记 BUG-1061 跨层数字比对(未修) | 已验收(待部署) | `codex/bug-1060-d9-double-transit-20260927`(未推送) |
| —(子代理直接执行,无任务书;产品 2026-09-27 授权) | `PROGRESS-bug-1061-cross-layer-pairing-20260927.md` | **双重过运跨层配对按名字数字比对(BUG-1061)**:改为结构化身份配对(D1 第 N 宫↔D9 第 N 宫;D1 第 N 宫主↔同一颗星的 D9 目标);各层结果与 D1/CL golden 逐字节不变;48 样本跨层条目 31→38;数据卡 golden 重采集(乔布斯 6→9、泰勒 9→10) | 已验收(待部署) | `codex/bug-1061-cross-layer-pairing-20260927`(未推送) |
@@ -0,0 +1,318 @@
# TASK · 新用户启程:先看谁的盘、首问免费、起步三问、启程清单、一次性提示与动效(2026-10-22)
设计原型(可点,含两条路径与全部动效):https://claude.ai/artifact/PcTwMVb9RD611R3qStxv3U 。原型与本任务书不一致时,以本任务书为准。原型里的名字、日期、星位都是虚构示例。
## 基线
- `origin/staging` = `c1aa72fcb`(写本单时)。开工时以当时的 `origin/staging` 为准,在进度记录里写下实际基线 SHA。
- BUG 编号起点:**BUG-1420**。开工时运行 `grep -oE "BUG-[0-9]+" docs/BUG_HISTORY.md | sort -t- -k2 -n | tail -1` 核对最大号;若已被占用就顺延,并在进度记录里写明。
- 本单动表,**不进 agent 循环**。执行方在 `codex/onboarding-journey-20261022` 分支上串行做 T1→T8。
## 事故实证(行号按符号定位,以开工时为准)
1. **新账户第一次提问必定被拦。**
- 新账户点数默认是 0(`frontend/supabase/migrations/20260715000000_account_credits.sql` 中 `credits` 的默认值)。
- 前端:`hooks/use-consultation-run.ts` 发送前判断 `account.credits <= 0 && !account.activeSubscription`,成立就调 `openRedeemPaywallOnce()` 弹兑换窗。
- 服务端:`lib/consult/settlement.ts` 的 `reserveCredit` 按 `chat.standard` 预扣,余额不足返回 402。
- 结果:用户建完档、看完「那一刻的天空」,第一次提问就撞上付费。
2. **过场之后没有下一步。** `components/birth-sky/birth-sky-reveal.tsx` 播完后落到 `components/starter-home.tsx` 的空白首页,只有一行问候、输入框和「今日星语」。新用户不知道能问什么。
3. **没有任何功能介绍。** 星盘页(四个页签)、星盘档案、婚恋合盘、个人报告、星历只出现在侧栏,没有任何地方告诉新用户它们存在。仓库里也没有引导组件或引导库。
4. **只能先排自己的盘。** 建档只认本人:`lib/home-profile.ts` 的 `missingProfileStep()` 检查 `profiles` 行,资料不全就进不了首页。想先给伴侣、孩子看的用户必须先填自己。下列文件都依赖这个判断,T3 要逐个核对:
- `hooks/use-profile-onboarding.ts`、`lib/home-bootstrap-run.ts`、`lib/home-warm-start.ts`、`lib/session-list-context.tsx`
- `hooks/use-consultation-run.ts`、`hooks/use-rectification-surface.ts`、`lib/rectification-session-composer-guard.ts`
- `components/daily-starlanguage-binder.tsx`、`components/home-onboarding-shell.tsx`、`app/(app)/page.tsx`
5. **档案没有关系。** 人物只分 `self` / `other`:`lib/subject-birth.ts` 的 `role`,以及 `chat_sessions.chart_profile_role` 的 check 约束(`20260830010000_chat_session_chart_profile_binding.sql`)。`chart_profiles` 上没有伴侣、孩子、父母、朋友之分。
6. **残留死代码。**
- `app/(app)/page.tsx` 的 `onboardingJustCompleted` 从来没有被设为 `true`,所以 `lib/home-profile.ts` 的 `completedOnboardingMessage` 永远不会显示。
- `components/home-onboarding-shell.tsx` 保留着一个走不到的 `"rectification"` 分支。
- `frontend/DESIGN.md` §9「首页只揭幕一次」那一段仍写着首页有「生时校正卡」(BUG-1247 已经删掉)。
7. **`profiles` 上有三列废列。** `onboarding_payload / onboarding_version / onboarding_generated_at` 在前后端都已没有引用。
## 根因
- 首登流程只做到“排好自己的盘”,没有设计“第一次拿到价值”这一步;计费又默认 0 点,两者叠加,激活在第一问就断了。
- 建档默认用户来看自己,但很多人第一次来是想看身边的人。
- 各功能逐个上线,入口只放进侧栏,一直没有统一的“新用户怎么认识它们”的机制。
## 决策记录(产品负责人 2026-10-22 拍板)
| # | 问题 | 决定 |
| --- | --- | --- |
| D1 | 新账户第一个问题要不要免费 | **免费**,每个账户一次,由服务端判定 |
| D2 | 清单里放不放收费项(合盘 19 点、个人报告) | **放**,价格写在清单那一行 |
| D3 | 老用户看不看得到清单 | **能看到**。勾从真实数据推出;进站时七项已满的账户整张清单不出现 |
| D4 | 先帮别人看的用户,自己的资料要不要必填 | **不必填**。先排 TA 的盘、问 TA 的事;清单多一项「加上你自己」,加完才解锁今日星语和合盘 |
| D5 | 关系分类做不做 | **做**:伴侣、孩子、父母、朋友、其他,外加本人。老档案的关系为空,显示「其他」,用户可改 |
| D6 | 动效 | 产品要求加强(“动画太少了”)。按原型的动效规范做,四处超出现行规范,**只限启程流程**:① 转场、飞勾 650–700 ms,合盘靠拢约 1.2 s;② 提示和勾用回弹曲线;③ 七项全满时迸一次星点;④ 侧栏和页签上的新小点闪两次 |
**这份任务书推翻以下既有约束。执行方必须照改,不得以“与 VOICE / DESIGN / 既有 Bug 历史冲突”为由拒改:**
- `frontend/docs/VOICE.md` 规定「首页开场语只有一行,不带追问句」「入口按钮下面平时不写字」。**仅在起步三问出现时放宽**:问候下面可以加一行引导、一枚「这一次不扣点」、三个起步问题,帮别人看时再加一枚「正在看:{名字} · {关系}」。起步三问消失后,首页恢复原规则。在 VOICE.md 写明这条例外和日期。
- `frontend/DESIGN.md` §6 规定「微交互 120 ms、弹窗 180 ms、只用一条缓动、除加载外不循环」。启程流程按 D6 放宽,DESIGN.md 新增「新用户启程」一节,写明例外和适用范围;产品其他地方不变。
- 建档“必须先有本人资料才进首页”(`missingProfileStep` 的现行语义):改为“本人资料完整,**或**档案里至少有一位资料完整的人”,见 T3。
- `DESIGN.md` 首次建盘过场一节「过场正常结束或被点掉后直接露出下一步,不弹任何提示」:**不推翻**。起步三问是首页内容,不是提示;过场结束那一刻仍然不弹任何浮层。
- 兑换窗「只在第一次因点数不足发不出消息时弹」:**不变**。首问免费后,这一刻自然后移到第二问。
## 硬红线
1. 首问免费只能由**服务端**判定和记账,前端只读一个布尔值。前端改余额、跳过检查、本地记标记,都不算完成。
2. 免费额度**原子**消耗:
- 同一账户并发两条请求,最多一条免费。
- 回答失败、被撤回、被停止、退了点,额度都**还在**。
- 寒暄不消耗额度(寒暄本来就不扣点)。
- 给谁问(本人或档案里的人)都算同一份额度。
3. 免费只覆盖 `chat.standard` 的标准档价格。用户在首问前换了高档模型,按正常价扣或只扣差价,二选一,在进度记录里写明选了哪种、为什么。
4. 揭幕后不得出现新的等待动画、骨架或转圈(AGENTS.md §6、DESIGN.md §9)。D6 的动效全部是对用户操作的回应,不能用来填补等待。
5. 起步问题在揭幕那一帧就能渲染,**不得为生成它多调一次模型、多等一个接口**。
6. 一次性提示不得和以下任何一样同时出现:天空过场、兑换窗、任何弹窗或抽屉、回答流式生成中。同一时刻最多一个提示。
7. 遵守冻结规则:`Home()` 的 `useState` / `useRef` 数不得增长(`frontend/tests/home-shell-growth-contract.test.ts`);`scripts/jyotish_api_server.py` 的类方法数不得增长。新逻辑放进 `hooks/`、`lib/` 和组件。
8. 不引第三方引导库或动画库。动效用 CSS 和 Web Animations API;提示用现有的 `@base-ui/react` Popover。
9. 系统开了 `prefers-reduced-motion` 时,D6 的全部动效退成淡入或直接出现,星点和闪烁去掉。
10. 清单的“做没做”**一律从真实数据推导**。唯一例外是“进过星盘页”(没有数据可推),见 T2。
11. 生时校正仍只支持本人。帮别人看时,任何地方都不出现校正提议。
12. 迁移只加不删(AGENTS.md §7.6):三列废列本单不删,关系列可为空。必须真跑 `npm run test:db`;本机没有 Docker 时,按已记录的无 Docker PG17 做法跑;仍做不了就写进 `BLOCKED.md`,不得写成通过。
13. 测试、fixture、文档里不得出现任何真实用户资料。
## 任务分解
### T1 首问免费(服务端,动表)
- 新增迁移,在账户上记录首问免费是否已用(例如 `profiles.first_consult_free_used_at timestamptz`),列名由执行方定。
- `reserve_consultation_usage`(或新 RPC)在同一事务里判断并占用免费额度:
- 可用:本次 `credit_cost = 0`,并标记占用。
- 撤销、退点路径(`cancel_consultation_credit` 等):把额度还回去。
- 回答完成后才算真正用掉。
- 账户接口新增 `firstConsultFree: boolean`。
- 前端发送前检查改为 `credits <= 0 && !activeSubscription && !firstConsultFree` 才拦;输入框占位的 `creditsEmpty` 同样排除 `firstConsultFree`。
- 计费台账照记一条 0 点记录,标明来源是首问免费,后台用量页能看到。
**验收**
- 新账户 0 点:第一问正常发出,余额仍为 0;第二问弹兑换窗,文案沿用原来的。
- 并发两条首问,只有一条是 0 点(DB 测试)。
- 首问中途停止或失败后,`firstConsultFree` 仍为 `true`(DB 测试)。
- 首问是寒暄时,`firstConsultFree` 仍为 `true`。
- 首问选了高档模型:按红线 3 选定的规则扣点,有测试。
- 迁移前已有完成咨询的老账户,迁移后 `firstConsultFree = false`。
### T2 引导状态与关系字段(动表)
- `profiles` 新增 `onboarding_state jsonb`:
- 加对象类型 check;写权限只给服务端路由,照 `20260715040000_agent_onboarding_cache.sql` 的授权写法。
- 内容只有三样:`checklistDismissedAt`、`hintsSeen: string[]`、`chartVisitedAt`。
- 新增一条已登录接口读写 `onboarding_state`:
- 写入只接受上面三个键。
- `hintsSeen` 只接受 T6 表里列出的 id。
- `chart_profiles` 新增 `relation text null`,check 取值为 `partner | child | parent | friend | other`。本人不写这一列,仍由 `profiles` 表示。
- `lib/subject-birth.ts` 的人物类型加上 `relation`,读写接口同步。
- 星盘档案的新增和编辑表单都加关系选择,文案是「TA 是你的」,选项为伴侣、孩子、父母、朋友、其他。
- 人物卡在名字后显示关系小标签;本人显示「本人」;`relation` 为空时显示「其他」。
**验收**
- DB 测试覆盖:`onboarding_state` 读、写、白名单拒绝、未登录 401、不能写别人的。
- `relation` 非法值被拒;为空的老行正常读出。
- 档案页新增一个人(选「孩子」),刷新后关系仍在。
- 编辑一个老档案,可以补上关系。
### T3 建档第二步「这次先看谁的盘」,本人资料可选(D4)
- 建档从三步变成四步:称呼 → **看谁的盘** → 出生日期与时间 → 出生地。顶部进度条四格。
- 第二步的助手消息:「好的,{称呼}。这次想先看谁的盘?以后随时可以再加别人。」消息下面是两个大选项:
- 「我自己」,副文案「看看自己的性格、事业和这几年的运势」。
- 「帮别人看」,副文案「伴侣、孩子、父母或朋友」。选中后平滑展开两样:「TA 叫什么」输入框,以及「TA 是你的」关系选择(五个选项);下面写「你自己的资料可以以后再补。」
- 选「我自己」:后续流程和现在一致,第三步文案改为「好,先看你自己的。……」。
- 选「帮别人看」:
- 出生两步的资料写进 `chart_profiles` 的新行,带名字和关系;本人 `profiles` 只存称呼。
- 第三步文案:「好,先看{名字}的。请告诉我 TA 的出生日期,以及你对 TA 出生时间知道到什么程度。家人朋友的出生时间常常记不准,选个大概时段就行。」
- 第四步:「最后一个问题,{名字}出生在哪里?」
- 天空过场照常,用 TA 的数据;副句写「那一刻,{名字}的上升在……」。
- 建完后,当前人物设为 TA,首页和对话都以 TA 为当前人物。
- `missingProfileStep()` 改为:本人资料完整,**或**档案里至少有一位资料完整的人,就算建完档。中途退出的,下次从缺的那一步接着问。
- 逐个核对事故实证 4 列出的调用点,按“当前人物”而不是“本人”判断:
- 今日星语:本人没有出生资料时,首页不显示「今日星语」按钮。
- 生时校正:当前人物不是本人时,不出现校正提议(红线 11)。
- 「和我合盘」:本人没有出生资料时,按钮位置改为文字「加上你自己后可合盘」。
- 顺手清理事故实证 6 的死代码,并修正 DESIGN.md §9 那一段。
**验收**
- 选「我自己」走完四步,进首页,行为与基线一致(截图或测试断言)。
- 选「帮别人看」走完四步:
- `chart_profiles` 新增一行,名字和关系正确。
- 本人没有出生资料,照样进首页。
- 首页、聊天顶栏的当前人物都是 TA。
- 首页不显示今日星语;对话里不出现校正提议。
- `missingProfileStep` 的单测覆盖:本人完整 / 只有他人完整 / 都不完整 / 中途退出。
- 帮别人看的账户之后补上本人:今日星语出现,「和我合盘」可点。
### T4 起步三问(首页)
- 显示条件:已建完档、`firstConsultFree === true`、当前是空白首页。用掉首问后不再出现。
- 布局:
- 问候下面一行:本人路径写「盘排好了。先问一个问题吧。」;帮别人看写「{名字}的盘排好了。先问一个关于 TA 的问题吧。」,并加一枚「正在看:{名字} · {关系}」。
- 然后一枚「这一次不扣点」,再是三个起步问题,排在输入框上方。
- 三个问题由 `lib/` 里的新函数按当前人物的盘拼出来,数据取自 `lib/daily-starlanguage.ts` 已有的 `ascendantSign` / `moonSign`,不得新增请求:
| # | 本人 | 帮别人看 |
| --- | --- | --- |
| 1 | 「上升{上}、月亮{月},我是个什么样的人」;没有上升时「月亮在{月},我是个什么样的人」;两样都没有时「从我的盘看,我是个什么样的人」 | 同样三档,「我」换成「{名字}」 |
| 2 | 「接下来这两年,事业上该注意什么」 | 「{名字}接下来这两年,事业上该注意什么」 |
| 3 | 「我想问一件具体的事」 | 「我想问一件关于 TA 的事」 |
- 点 1、2 直接发送,和用户自己打字发送走同一条路径。
- 点 3 不发送,只让输入框亮一下并获得焦点。
**验收**
- 两条路径 × 三种盘数据(全有 / 无上升 / 都无),各一条单测,文案逐字匹配上表。
- 点 3 后不发送,输入框获得焦点。
- 首问回答完成后回到首页,起步三问消失,首页回到原样。
### T5 启程清单
- 七项和“做过”的判定,全部从服务端或现有数据推导:
| id | 本人路径文案 | 帮别人看文案 | 做过 = |
| --- | --- | --- | --- |
| `self` | 排好自己的盘 | 排好{名字}的盘 | 已建完档(T3 口径) |
| `ask` | 问第一个问题 | 同左 | 至少一条完整的非寒暄回答 |
| `daily` | 打开今日星语 | 同左(本人没资料时「去做」跳到档案页) | 至少一次今日星语咨询 |
| `chart` | 看看自己的星盘 | 看看星盘 | `onboarding_state.chartVisitedAt` 有值 |
| `person` | 加一位家人或朋友 | **加上你自己** | 本人路径:档案 ≥ 2 人;帮别人看:本人资料完整 |
| `syn` | 和 TA 合一次盘 · {价} 点 | 和{名字}合一次盘 · {价} 点 | 至少一份合盘报告 |
| `report` | 生成个人报告 | 同左 | 至少一份个人报告 |
- 帮别人看的账户,清单顺序是 `self, ask, person, daily, chart, syn, report`。
- 本人路径 / 帮别人看,按“本人资料是否在档案里第一个建档”判断,不另存标记。如果本人和他人都已完整,用本人路径的文案。
- 合盘价格从计价表读(现为 19),不写死;报告行有价格就显示。
- 功能开关关闭时对应行不显示:
- `product.compatibility` / `account.compatibility` 关闭 → 没有 `syn`。
- 个人报告开关关闭(`product.report_center` 与 `PERSONAL_REPORT_ENABLED`,见 `lib/personal-report-entitlement.ts`)→ 没有 `report`。
- 入口:首页和聊天顶栏右侧一枚胶囊「启程 n/N」,带进度环。
- 点开后,手机上是底部抽屉,桌面上是弹出层。
- 每行未完成的有「去做」,直接跳到真功能:
| 项 | 「去做」跳到哪里 |
| --- | --- |
| `ask` | 首页,聚焦输入框 |
| `daily` | 触发今日星语;本人没资料时跳到档案页 |
| `chart` | `/chart` |
| `person` | `/people` 并打开新增表单(帮别人看时打开「加上你自己」) |
| `syn` | `/people` |
| `report` | `/reports` |
- 抽屉底部「先不看了」→ 写 `checklistDismissedAt`,胶囊此后不再出现。
- 七项全满时,抽屉最后展开一次:标题「都走过一遍了」,正文「以后有新东西,我会在用到的时候再告诉你。」。关掉后写 `checklistDismissedAt`。
- D3:老用户照常显示,勾从数据推导;进站时已经全满的账户,胶囊和「都走过一遍了」都不出现。
**验收**
- 七项判定在两条路径下各一条单测,含边界:寒暄不算 `ask`;本人一人不算 `person`;帮别人看时加了第二个他人也不算 `person`。
- 功能开关关闭时行数变化有测试。
- 新账户进首页时 `self` 已勾,显示「启程 1/7」。
- 老账户 fixture 只差 `chart`、`daily` 时显示「5/7」;全满时不显示。
- 点「先不看了」后刷新、在另一会话打开,都不再出现。
- 手机 375 px 宽无横向滚动,胶囊不挤占输入框。
### T6 一次性提示
一律用一个通用的 `useOneTimeHint(id, condition)`(或同等实现)。id 固定,即 T2 的白名单:
| id | 何时出现 | 指向 | 文案 |
| --- | --- | --- | --- |
| `follow-up` | 第一条完整回答流式结束后 | 追问胶囊 | 想接着聊,点下面任一个就直接问。 |
| `sidebar-chart` | `follow-up` 已看过、`chartVisitedAt` 为空,第一次打开侧栏时 | 侧栏「星盘」 | 你的盘、大运和分盘都在这里。 |
| `tab-chart` | 第一次看到星盘页的「星盘」页签 | 页签内容上方 | 这是你的本命盘,北印度画法。最上面的菱形是第一宫,上升就在这里,往左逆时针依次是二宫到十二宫。 |
| `tab-info` | 第一次打开「基础信息」 | 同上 | 排盘用的原始资料和每颗星的位置都在这里。出生时间不准时,这一页最上面会写明误差。 |
| `tab-dasha` | 第一次打开「大运」 | 同上 | 一段大运管几年到十几年,主色那段是你现在走的。点开任意一段,能看到里面的小运。 |
| `tab-west` | 第一次打开「西洋盘」 | 同上 | 同一时刻的天空,换成西洋占星的算法。两种黄道相差约 24 度,所以星座可能和印度盘不一样。 |
| `syn-ready` | 档案里第一次有了两个人(含本人),且还没有合盘 | 「和我合盘」按钮 | 有了你们两个人,就能看你们俩的关系画像。价格写在按钮上。 |
| `ephemeris` | 第三次打开今日星语之后,下一次打开侧栏时 | 侧栏「星历」 | 每天的天象和行运在这里,可以按日期翻。 |
- 当前人物不是本人时,`tab-chart` 文案里的「你的」改成「{名字}的」。
- 侧栏「星盘」在 `ask` 完成、`chart` 未完成时带一个小圆点;手机上的汉堡按钮也带。`chart` 完成后圆点消失。
- 星盘页四个页签,没看过的右上角带小点,看过就消失(跟随 `hintsSeen` 中 `tab-*` 是否存在)。
- 每条提示点「知道了」、点页面任何地方或按 Esc 即消失,写入 `hintsSeen`,之后不再出现。
- 被红线 6 挡住的提示排队,条件解除后再出,不丢。
**验收**
- 每条提示的出现条件和互斥(流式中、弹窗开着、过场中都不出)各有测试。
- 看过后刷新不再出现;四个页签各自记。
- 提示出现时不抢焦点;Esc 可关闭。
### T7 动效(D6)
照原型「动效规范」表实现,令牌写进 DESIGN.md「新用户启程」一节:
| 类别 | 用在哪 | 时长 | 曲线 |
| --- | --- | --- | --- |
| 按下反馈 | 按钮、问题卡、选项、关系标签被按下时轻微缩小 | 120 ms | `--ease-out` |
| 进场 | 建档消息与卡片、起步问题、追问胶囊、档案卡、目录行;依次上浮,间隔 50–80 ms | 260 ms | `--ease-out` |
| 弹出 | 一次性提示、「这一次不扣点」、勾 | 380 ms | 回弹 `cubic-bezier(.34,1.56,.64,1)` |
| 跟手 | 称呼从输入框飞成气泡;起步问题飞成气泡 | 380 ms | `--ease-out` |
| 勾飞进进度环 | 清单每完成一项:从操作位置沿弧线飞向胶囊,进度环走一格,胶囊轻跳一下 | 700 ms | 弧线 |
| 画出来 | 建档进度条;星盘格线、西洋盘圆与分隔线、大运条 | 520–900 ms | `--ease-out` |
| 场景转换 | 出生地→天空:从点击处圆形展开;天空→首页:淡出放大,首页同时上浮 | 650–700 ms | `--ease-out` |
| 合盘 | 两个头像从两侧靠拢,泛起一圈光,再展开关系画像 | 约 1.2 s | `--ease-out` |
| 庆祝 | 七项全满时,进度环处迸一次星点 | 1000 ms,只一次 | 减速 |
- 每段动效只在元素第一次出现时播放,重绘和来回切换不重播。
- 除回答流式中的呼吸点外,不得有任何无限循环。
- 星点、闪烁在 `prefers-reduced-motion` 下去掉,其余退成淡入(红线 9)。
- 天空过场本身的加强(星星先亮起再汇聚、星体逐个点亮并从上升连线、两行字依次浮出)在 `components/birth-sky/` 内实现,沿用现有的“3 秒内素材没到齐就静默跳过”。
- 动效只在 `opacity`、`transform`、颜色、`clip-path` 上做,不触发布局。
**验收**
- `prefers-reduced-motion` 下没有位移动画(测试或录屏)。
- 本机 Chrome 性能面板录一次完整新用户流程,没有超过 50 ms 的长任务由动效引起;做不了就写环境缺口。
- 真机清单里逐条列出动效,由产品负责人核对。
### T8 文档
- `CONTEXT.md` glossary 加:启程清单、起步问题、一次性提示、关系(人物)。
- `frontend/DESIGN.md`:
- 新增「新用户启程」一节,写胶囊、抽屉、提示的尺寸,T7 的动效令牌,D6 例外的范围,以及互斥规则。
- 修正 §9 那一段。
- `frontend/docs/VOICE.md`:写明本单的例外,收录全部新文案。
- `CHANGELOG.md`:写一条用户可感知的变更。
- `docs/BUG_HISTORY.md`,状态、现象、根因、修复、验证、防复发写全:
- BUG-1420:新账户首问必撞付费,激活断点。
- BUG-1421:首登过场后无下一步,加上死代码。
- BUG-1422:建档只能先看本人。
- `docs/testing/onboarding-journey-20261022.md` 真机清单:
- 新邮箱注册,两条路径各走一遍全程。
- 老账户看清单。
- 手机和桌面各走一遍。
- 开启减少动态效果再走一遍。
- 逐条动效核对。
## 让步顺序
做不完时按这个顺序砍,越靠前越先砍:
1. `ephemeris` 提示
2. 合盘靠拢动画(直接展开关系画像)
3. 七项全满的星点
4. 桌面版清单的弹出层(先统一用抽屉)
5. 老用户清单(D3)可以晚一个版本上,但不得改成其他形态
**不可让:** T1、T2、T3、T4,以及 T5 的新用户路径。
## 开工前置命令
```bash
git fetch origin --prune
git worktree add -b codex/onboarding-journey-20261022 .worktrees/onboarding-journey-20261022 origin/staging
cd .worktrees/onboarding-journey-20261022
git status -sb
grep -oE "BUG-[0-9]+" docs/BUG_HISTORY.md | sort -t- -k2 -n | tail -1
cd frontend && npm ci && ./node_modules/.bin/tsc --noEmit && npm run lint && npm test 2>&1 | tail -8 # 记下基线测试数
npm run build # 记下 / 是否 Static、首屏 gzip
```
## 交付验收口径(Claude 验收时逐条核)
- `tsc --noEmit` 0 错;`npm run lint` 0 error。
- `npm test` fail=0,且总数不低于基线;改了任何既有断言,写明「原值 / 新值 / 原因」。
- `npm run test:db` 真跑通过,或按红线 12 写明环境缺口。
- 改动含 `scripts/**` 或 `tests/**` 时跑 Python 快速门。
- `next build` 后 `/` 仍是 `○ Static`;首屏 gzip 变化在 ±2% 内,超出要写原因(动效代码应按需加载,不进首屏)。
- 冻结合同测试全过。
- 部署后 `https://staging.jyotisha.chat/api/health` 的 `deployment.gitCommit` 等于交付 SHA。上生产前必须先跑本单的迁移。
- 真机清单交给产品负责人,不得写成“已通过”。