# TASK · 慢网首页半残 + 「账户与点数」打开慢(2026-09-24) ## 基线 - `origin/staging = 9c9b66d6`(代码同已部署 `1420471a`)。 - 分支 `codex/home-slow-network-20260924`,worktree `.worktrees/home-slow-network-20260924`。 - **串行**:本单改 `frontend/src/app/(app)/page.tsx`;`TASK-chart-ephemeris-fixes-20260924.md` 的 T3(设置深链)也碰 `page.tsx` / `settings-url.ts`,**本单先合,那一项后合**。 ## 事故实证 产品真机:Chrome DevTools「Slow 3G」强刷 `/` → 先转加载环 → 揭幕后出现报错条「连接云端服务超时。请检查网络后重试,或返回登录页重新建立会话。」→ 历史列表稍后自己出来,但模型显示「暂不可用」、「新建对话」灰掉、开场又问「请问我该怎么称呼你?」(账号早有称呼与资料)。 代码链(`origin/staging`): 1. `frontend/src/app/(app)/page.tsx` bootstrap `useEffect` 内联 `8000` ms 定时器:`controller.abort(); setAccountError("连接云端服务超时。…"); setHydrated(true);`。无命名常量、无重试(effect 依赖 `[]`,只有全屏错误页的「重试」= `location.reload()`)。 2. 8 秒要覆盖的是**串行链**:`await sessionListReady`(`SessionListProvider` 的 `/api/sessions` + `/api/account`)→ `fetchModelCatalog`(`/api/models`)→ `/api/consult/status` → 可选 lookup / `fetchSessionDetail`。慢网每跳 ≥2s,必然超时。 3. `SessionListProvider`(`frontend/src/lib/session-list-context.tsx` `loadSessionList`)用自己的 `AbortController`,**不受 8 秒 abort 影响**,稍后成功 → `setAccount` / `setSessions`。于是渲染门 `!hydrated || (!account && !accountError)` 走到"正常首页 + 报错条",而不是错误屏。BUG-927 把账户 / 会话读取搬进 provider 时,没有和这个 8 秒超时对齐。 4. `setModelCatalog` / `setProfile` / `setStartGreeting` / `setOnboardingStep` 只在成功路径最后执行,超时后永远不执行:`modelCatalog = null` → `model-selector.tsx`「暂不可用」、`use-home-shell-registration.ts` `newChatDisabled: … || !modelCatalog`;`profile = emptyProfile`、`onboardingStep` 初值 `"name"` → `home-types.ts` `presetOnboardingMessage`。 「账户与点数」: 5. `page.tsx` `const BillingPanel = dynamic(() => import("@/components/billing-panel"), { ssr: false })`,**无 `loading`、无预取** → 首次打开整块空白直到 chunk 下完。 6. `frontend/src/hooks/use-billing-panel.ts` 挂载时 `Promise.allSettled([fetchAccountData(), fetchPackages()])`:`fetchAccountData` 请求完整 `/api/account`(auth + 4–6 次串行查询),**结果被丢弃**(面板用的是 Home 传入的 `account`);`/api/payment/packages` 串行 4 次 DB 查询(`readEpayAvailability` → feature flags → `billing_products` → `product_entitlements`)。 7. 每次打开重挂、`cache: "no-store"`,零缓存。 ## 根因 - A:首页账户阶段用一个固定 8 秒总预算包住多跳串行请求,超时后放弃且不补拉;账户 / 会话由独立 provider 读取,与该预算脱节,产生"半揭幕"状态。 - B:账单面板懒加载无占位无预取 + 多一次无用的完整账户请求 + 套餐接口串行查库 + 零缓存。 ## 决策记录(产品 2026-09-24) - **D1 推翻 8 秒硬超时(BUG-479 "8 秒 bootstrap 超时不变")与 09-22 `TASK-home-bootstrap-reliability` 的"不自动重试"在本场景的适用**:首页账户阶段**不设 8 秒硬超时**,加载环一直转到账户、模型列表、资料都到齐再一次揭幕("一次等待一次揭幕"不变)。 - **D2 兜底**:等待超过 `HOME_BOOTSTRAP_SLOW_MS = 20_000` 时切到现有全屏错误屏(「暂时无法进入 Jyotisha」+ 现有文案),「重试」改为**局部重新执行 bootstrap**(重新发请求,不 `location.reload()`);「返回登录」保留。首屏内联 13 秒 ES5 兜底(BUG-936,`first-paint-fallback.ts`)只管 bundle 没跑起来的情况,**阈值需同步上调到 > 20 秒 + 余量**,保证 bundle 正常运行时不会被它抢先。 - **D3 不再出现半揭幕**:任何情况下都不得出现"首页已揭幕但 `modelCatalog` / `profile` 未填"的组合。若 provider 已成功而模型列表失败,走 D2 错误屏,不走报错条。 - **D4 模型列表与账户并行**:`/api/models` 不再等 `sessionListReady`,与 provider 并行发出;`/api/consult/status` 同理并行。 - **D5 账单面板**:去掉 `use-billing-panel` 里的 `fetchAccountData`(无用请求);`BillingPanel` 在设置弹窗打开(或指针移到「账户与点数」导航)时预取 chunk;`/api/payment/packages` 结果做模块级缓存(同会话 60 秒内复用、后台刷新,参考 `secondary-page-data.ts` 的 `createMemoryCache`);服务端 4 次查询中无依赖的并行(`Promise.all`)。chunk 未到时面板区域显示现有 `SecondaryPageShell` 同类静态句「账户信息还没拿到。」,不加 spinner。 ## 硬红线 1. `/` 保持 `○ Static`;首屏 gzip ±2%。 2. 揭幕后不得出现 spinner / 骨架 / 「正在加载」;等待期仍只用现有 `AppLoadingIndicator`,不造第二套。 3. 不得整页自动刷新(`location.reload` 只允许用户点击);D2 的局部重试是重新发请求,不是刷新。 4. `Home()` 不新增 useState / useRef(`home-shell-growth-contract`);新逻辑进 `frontend/src/lib/` 或 hook。`page.tsx` 不得净增行。 5. 不改 `/api/account`、`/api/sessions` 的响应结构;不改数据库。 6. `first-paint-fallback` 的六类失败分类(`hydrate-timeout` … `api-error`)保持可区分。 7. 既有断言改动写三栏;测试总数不低于基线。 ## 任务分解 ### T1 · 首页启动(`page.tsx` + `frontend/src/lib/home-bootstrap.ts` + `session-list-context.tsx`) - 删除内联 8000 定时器;新增 `HOME_BOOTSTRAP_SLOW_MS`;模型列表 / consult status 与 provider 并行;到齐后一次性 `set*`;超过 20 秒 → 错误屏;「重试」调用可重入的 bootstrap 函数(需要 abort 上一轮、重置 provider 读取)。 - `first-paint-fallback.ts` 阈值同步调整并更新其合同测试(三栏)。 验收:新测试(真实 React 生命周期或纯函数状态机)覆盖——慢网 15 秒全部到齐 → 正常揭幕、无报错条、模型可选、不问称呼;模型接口失败而 provider 成功 → 错误屏而不是半揭幕;>20 秒 → 错误屏,点重试重新请求且成功后揭幕,`location.reload` 未被调用。`first-paint-fallback-contract.test.ts` 更新阈值断言。 ### T2 · 账单面板(`use-billing-panel.ts`、`billing-panel.tsx`、`account-dialog-overlay.tsx`、`app/api/payment/packages/route.ts`) - 删 `fetchAccountData`;预取 chunk;packages 模块缓存;服务端并行查询;静态占位句。 验收:打开面板时网络只有 1 个请求(`/api/payment/packages`,缓存命中时 0 个);第二次打开立即显示套餐;`use-billing-panel` 源码不再请求 `/api/account`;packages route 测试断言并行后结果不变。 ### T3 · 记录 - `docs/BUG_HISTORY.md`:**BUG-1021** 慢网首页半揭幕(关联 BUG-479、927、936;复发自:无,BUG-927 引入的时序脱节);**BUG-1022** 账户与点数打开慢。 - `frontend/DESIGN.md` §9 首页揭幕规则更新(无硬超时、20 秒错误屏、局部重试);`CHANGELOG.md`;PROGRESS;`docs/testing/home-slow-network-20260924.md`:Slow 3G 强刷首页 → 只有加载环、揭幕后正常;Offline 强刷 → 20 秒后错误屏,恢复网络点重试 → 进入;设置 → 账户与点数首次 / 第二次打开耗时。 ## 让步顺序 1. 若 provider 无法干净地"重新执行",允许重试时重建 `SessionListProvider` 的读取 key,但不得整页刷新。 2. `/api/payment/packages` 并行若受事务约束,允许保持串行,只做客户端缓存与去掉多余请求。 3. 不得让步:D3(不再半揭幕)、红线 3(不自动整页刷新)、红线 1(Static)。 ## 开工前置命令 ```bash git fetch origin --prune git worktree add -b codex/home-slow-network-20260924 .worktrees/home-slow-network-20260924 origin/staging cd .worktrees/home-slow-network-20260924/frontend ./node_modules/.bin/tsc --noEmit && npm run lint && npm test 2>&1 | tail -5 ``` 完成后 tsc 0 / lint 0 error / 失败名单同基线 / `next build` `/` 仍 `○` / gzip ±2%。 ## BUG 编号 写单时最大 BUG-1020;本单 **BUG-1021 / 1022**,开工时核对。