docs(tasks): 次级页进入抖动定位,出 BUG-966 任务书

三页都是等待文案块换成正文块的一次布局推挤;use-chart-page 每次进入
重新 fetch、无缓存,所以每次都抖。默认方案不加 spinner(撞「揭幕后不得
出现 spinner/骨架」红线),改统一外壳 + 缓存 + 预取消灭中间态;要动画
则走延迟揭幕,并需产品授权改红线。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0193vBv6w5MV2cifdTUu9H5P
This commit is contained in:
Jesse_Chen
2026-09-18 09:57:24 +00:00
co-authored by Claude Opus 5
parent 4190206715
commit bfd6184e71
2 changed files with 61 additions and 0 deletions
+1
View File
@@ -141,6 +141,7 @@
| `TASK-window-consult-contract-20260918.md` | `PROGRESS-window-consult-contract-20260918.md` | **P0 线上**:申报时段模式**自 2026-08-21 起每轮秒败**69ms、0 token、模型从未被调用)。服务端日志实证根因:窗口 Agent attach 了 `jyotishSkillBinding()`,但 `windowJyotishInstructions` 从来不含方法块 marker,输入处理器直接 abortBUG-954)。abort 与「模型没调工具」同码,是它藏四周的原因(955);另含合同未绿不得丢正文(956)、计算不该由模型触发(957,须等 954 上线后另轮)、窗口指令应期冲突(958)。基线 `9cdcf96b` | 已验收通过(Claudetsc 0 / lint 0 error / npm test 3501 条 31 红同基线 / `/` Static / gzip 无变化;实跑确认窗口 Agent 指令已含 marker);review 另出 BUG-959~961 见下一行 | `5b6abc23` |
| `TASK-contract-degraded-pass4-20260918.md` | — | 验收 review:BUG-956 新增的降级交付路径绕过 Pass 4,保证句原样送达(BUG-959);`uncontractedText` 跨 attempt 不清零,同一轮正文说两遍(960);降级后还空跑一轮 compose(961)。基线 `877128ce` | 待领取 | — |
| `TASK-chat-markdown-list-20260918.md` | — | 真机排版反馈:聊天正文列表**没有项目符号**Tailwind v4 preflight 清了 `list-style``.markdown-list` 没恢复,BUG-962);`promoteDefinitionLists` 把四标题口语体的散文误判成并列项,三段正文被改写成列表(BUG-963,判据太松,收紧而非删除——BUG-356 的场景要留);思考条与正文之间实测 56px=报告 grid gap 24 + 首标题 margin-top 32,后者是被特指度压掉的「首元素清零」规则(BUG-964)。基线 `ea0280c1` | 待领取 | — |
| `TASK-secondary-page-entry-20260918.md` | — | 真机反馈:星盘 / 星历 / 报告进入时抖一下——三页都是「矮的等待文案块 → 高的正文块」一次推挤,且 `use-chart-page` 无缓存所以每次进入都抖(BUG-966)。默认方案是统一外壳 + 缓存 + 预取把中间态消灭,**不加 spinner**(撞 §6 红线,要加需产品授权并改红线)。基线 `41902067` | 待领取 | — |
| `TASK-first-paint-dead-screen-fallback-20260917.md` | — | 真机:首页永远停在「正在载入账户」,兜底全在没跑起来的 bundle 里(BUG-936 investigating)。根 layout 加与 bundle 无关的内联兜底 + 去掉本仓正则后行断言 | 待领取 | — |
| `TASK-consultation-answer-start-anchor-20260917.md` | `PROGRESS-consultation-answer-start-anchor-20260917.md` | 主会话回答落在结尾:`useConversationScrollAnchor` 是贴底跟随,流式期间视口钉在最后一个字,回答开头滚出视口;改为发送后问题钉顶、回答向下长、长出视口显示「跳到最新」、末尾动态留白;产品追加拍板:校正面同一语义(推翻 BUG-041/048 贴底),本轮开头 = 用户行或新助手行。BUG 段 930 起 | 已验收(经修复单) | `worktree/green-harbor-5be3` |
| `TASK-consultation-answer-start-anchor-fix-20260917.md` | `PROGRESS-consultation-answer-start-anchor-fix-20260917.md` | 验收修复单:F1 头就是留白行时留白按整视口算(BUG-931);F2 留白只在钉住期间存在(BUG-932);前置:先修 e4e73f56 的两处 TS 错否则门禁不过 | 已验收 | `cc1a8980`Claude 验收:tsc 0 / lint 0 error / npm test 3457 条 39 红与 11c0028d 逐条一致、新增 2 条绿 / `next build --webpack` 通过、`/` Static、首屏 gzip 591,242(较 09-16 基线 582,800 +1.45%,含会话列表单)/ Chrome 真实布局 S1–S6 全部通过,S6 新助手行距顶 16px 且增高不动,S5 不再写留白);真机六条欠 |
@@ -0,0 +1,60 @@
# TASK · 次级页进入时的等待态与抖动(2026-09-18 第七轮)
> 基线:`origin/staging` @ `41902067`(代码部分 = 已部署的 `877128ce`)。
> 触发:产品真机反馈「星盘 / 星历 / 我的报告进去时需要一个统一的加载动画,现在进去的时候有一瞬间抖动」。
> BUG 编号起点:当前最大号 **BUG-965**investigating),本单占 **BUG-966**。
## 0. 抖动是怎么来的(已定位)
三页是同一套形状:先渲染标题 + 一行等待文案,数据到达后**整块换成完整内容**。
| 位置 | 事实 |
| --- | --- |
| `src/components/chart-page/chart-page-view.tsx:65-77` | `view == null` 时渲染 `<SecondaryHeader>` + `<section class="chart-page-message">` 一行文案;数据到达后换成 `chart-page-tabs` + 盘图正文 |
| `src/components/ephemeris/ephemeris-page.tsx:70` | 同形状(`ephemeris-message` |
| `src/hooks/use-chart-page.ts:22-45` | 进页 `useEffect` 才发 `fetchChartView`,**没有任何缓存**:每次进入都重新请求、都重放一次中间态 |
| `SecondaryHeader``note={birthline}` | 出生行是后到的,标题区的文字二次填充 |
所以「一瞬间抖动」= 矮的等待块 → 高的正文块,一次布局推挤;而且因为没有缓存,**每次进入都抖一次**。
## 1. 与既有红线的冲突(必须先由产品裁决)
产品要的是「统一的加载动画」。但 `AGENTS.md §6``frontend/DESIGN.md` 的现行红线是:
> 揭幕后不得出现 spinner / 骨架 / 「正在加载」(流式生成中除外);一次等待一次揭幕。
直接加 spinner 会推翻这条。本单**默认不加 spinner**,按下面 §2 做「稳定容器 + 缓存 + 预取」,效果是把中间态消灭掉而不是把它做得好看。若产品坚持要可见动画,选 §3 的 B 方案(延迟揭幕),并在本节写明授权。
## 2. BUG-966(P1)默认方案:把中间态消灭掉
1. **统一外壳**:三页共用一个 `SecondaryPageShell`(标题 + 内容区)。标题区高度固定,`note` 未到时保留占位不塌;内容区 `min-height` 撑到视口剩余高度,等待文案居中在这块区域里,**从等待态换到内容态不推挤布局**。
2. **缓存**:星盘 / 星历 / 报告的首屏数据进模块级缓存(与 `use-session-list` 的缓存同一口径),同一会话内再次进入直接用上次结果并在后台静默刷新。第二次起没有中间态。
3. **预取**:侧栏三个入口在 `pointerenter` / `pointerdown` 时预热数据(`<Link>` 只预取路由代码,不预取数据)。第一次进入也大概率在数据到位后才看到内容。
4. 验收标准:
- 用 harness 或真机量「进入 → 内容出现」期间 `.secondary-page` 容器的高度变化:**首屏容器高度不得变化**(等待态与内容态同高);
- 同一会话内第二次进入三页,不得再出现等待文案;
- `next build``/``○ Static`,首屏 gzip ±2%。
## 3. 方案 B(仅在产品明确授权后做):延迟揭幕
不显示半成品:点击后**留在当前页**,数据到位再一次性切换到次级页(等待期间只有侧栏入口的按下态)。这与首页「一次等待一次揭幕」同构,不引入 spinner。实现上是把首屏数据取到路由切换之前(预取 + `useTransition`,或把首屏数据改成服务端取、用 Suspense 让路由切换等数据)。
代价:慢网络下点击后有一段"没反应",需要按下态兜住。
## 4. 硬红线
1. 不得在揭幕后引入 spinner / 骨架 / 「正在加载」文案——除非 §1 里有产品授权,且同一提交更新 `AGENTS.md §6``frontend/DESIGN.md` 的对应条款。
2. 三页必须共用同一个外壳与同一套等待口径,不得各写各的(这次抖动就是三页各写各的结果)。
3. `tsc --noEmit` 0 错、`npm run lint` 0 error、`npm test` 失败数不超过基线 31 条且清单一致;测试总数不低于 3501。
4. 改 UI 的同一提交更新 `frontend/DESIGN.md`;新文案对照 `frontend/docs/VOICE.md`
## 5. 开工前置
```bash
git fetch origin --prune
git worktree add -b codex/secondary-page-entry-20260918 \
.worktrees/secondary-page-entry-20260918 origin/staging
cd .worktrees/secondary-page-entry-20260918/frontend
npm test 2>&1 | grep -E "^# (tests|pass|fail)" # 开工基线:tests 3501 / pass 3455 / fail 31
```
收工:`docs/tasks/PROGRESS-secondary-page-entry-20260918.md`(附进入三页的改前改后录屏或逐帧截图)+ `docs/BUG_HISTORY.md`BUG-966+ `frontend/DESIGN.md`,与代码同一批推 `staging`