Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017eEAG8HD3mm8gsKXgk8uU8
8.4 KiB
8.4 KiB
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):
frontend/src/app/(app)/page.tsxbootstrapuseEffect内联8000ms 定时器:controller.abort(); setAccountError("连接云端服务超时。…"); setHydrated(true);。无命名常量、无重试(effect 依赖[],只有全屏错误页的「重试」=location.reload())。- 8 秒要覆盖的是串行链:
await sessionListReady(SessionListProvider的/api/sessions+/api/account)→fetchModelCatalog(/api/models)→/api/consult/status→ 可选 lookup /fetchSessionDetail。慢网每跳 ≥2s,必然超时。 SessionListProvider(frontend/src/lib/session-list-context.tsxloadSessionList)用自己的AbortController,不受 8 秒 abort 影响,稍后成功 →setAccount/setSessions。于是渲染门!hydrated || (!account && !accountError)走到"正常首页 + 报错条",而不是错误屏。BUG-927 把账户 / 会话读取搬进 provider 时,没有和这个 8 秒超时对齐。setModelCatalog/setProfile/setStartGreeting/setOnboardingStep只在成功路径最后执行,超时后永远不执行:modelCatalog = null→model-selector.tsx「暂不可用」、use-home-shell-registration.tsnewChatDisabled: … || !modelCatalog;profile = emptyProfile、onboardingStep初值"name"→home-types.tspresetOnboardingMessage。
「账户与点数」:
page.tsxconst BillingPanel = dynamic(() => import("@/components/billing-panel"), { ssr: false }),无loading、无预取 → 首次打开整块空白直到 chunk 下完。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)。- 每次打开重挂、
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。
硬红线
/保持○ Static;首屏 gzip ±2%。- 揭幕后不得出现 spinner / 骨架 / 「正在加载」;等待期仍只用现有
AppLoadingIndicator,不造第二套。 - 不得整页自动刷新(
location.reload只允许用户点击);D2 的局部重试是重新发请求,不是刷新。 Home()不新增 useState / useRef(home-shell-growth-contract);新逻辑进frontend/src/lib/或 hook。page.tsx不得净增行。- 不改
/api/account、/api/sessions的响应结构;不改数据库。 first-paint-fallback的六类失败分类(hydrate-timeout…api-error)保持可区分。- 既有断言改动写三栏;测试总数不低于基线。
任务分解
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 秒后错误屏,恢复网络点重试 → 进入;设置 → 账户与点数首次 / 第二次打开耗时。
让步顺序
- 若 provider 无法干净地"重新执行",允许重试时重建
SessionListProvider的读取 key,但不得整页刷新。 /api/payment/packages并行若受事务约束,允许保持串行,只做客户端缓存与去掉多余请求。- 不得让步:D3(不再半揭幕)、红线 3(不自动整页刷新)、红线 1(Static)。
开工前置命令
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,开工时核对。