# PROGRESS · 首页 page.tsx 拆分(2026-09-25) ## 当前结论 T1、T2、T3、T5 完成。T4 让步到 1417 行(允许 ≤1450,未到 ≤1300)。没有新 BUG。没有推 `staging`,没有推 `main`。`?settings=chart` 仍打开星盘资料。 - 工作树:`G:\Ferti\Jyotisha\.worktrees\home-page-split-20260925` - 分支:`codex/home-page-split-20260925` - 开工基线:`origin/staging` = `59c23018`(与本分支当时一致) - 开工后 `origin/staging` 又前进了报告读者版 `3c2f7bd5` 和状态板 `9167d75e`。这两笔不碰首页。本分支没有把它们合进来,也没有快进 `staging`。 - 实现提交在变基到 `9167d75e` 之后是 `da35b3e4`。状态板写这个 SHA。 ## 逐项 | 项 | 结果 | 说明 | | --- | --- | --- | | T1 | 完成 | `runHomeBootstrap(deps, signal)` 在 `frontend/src/lib/home-bootstrap-run.ts`。内部再拆成 `runAccountPhase` / `resolveLanding`(注入项超过 15)。新文件 0 个 React hook。`Home()` 只留一个 effect 调用它。 | | T2 | 完成 | `runConsultationRecoveryPoll` 在 `frontend/src/lib/consultation-recovery-poll.ts`。假计时器覆盖 404 第一次只确认、下一次重放一次、第 4 次放弃、reserved 成功把缺失计数清零。 | | T3 | 完成 | `HomeBootstrapError`、`HomeOnboardingShell`。文案和按钮与拆分前一致。「重试」仍走 `retryHomeBootstrap(...reloadSessionList)`。 | | T4 | 让步 | `page.tsx` 1417 行(换行计数,与增长合同相同)。目标 ≤1300 未到,任务书允许先到 ≤1450。import 挤行已还原:`^import .*; import ` 对 `page.tsx` 为 0。增长合同 `PAGE_LINE_COUNT_BASELINE` 下调到 1417,上限 1567。`useState` 仍是 34(上限保持 36),`useRef` 仍是 37(上限保持 37),都没有增加。 | | T5 | 完成 | DESIGN 未改。CHANGELOG 一条「首页内部重构,无用户可见变化」。不新开 BUG。 | ## 还留在 Home() 里的大块 下一步若要压到 1300,还在首页里的是:账户弹窗模型(资料 / 星盘库 / 点数 / 退出)、对话区和 `ChatComposer` 装配、星盘库同步、今日星语、设置深链。设置深链必须留在首页,`query.pane === "chart"` 仍调用 `openAccountDialog("chart-library")`。 ## 断言改写 | 原值 | 新值 | 原因 | | --- | --- | --- | | `new-chat-recovery.test.ts` 把 page.tsx 激活段和 hook 的 restore 放进 vm,断言 active 等于预置本地会话 | 调用 `runHomeBootstrap`,restore 仍是 hook 里的真实函数;new-chat 会新建空咨询,恢复后仍停在这条新会话 | 激活段已搬进函数。直接调用还覆盖「新建落点与 reserved 恢复共存」,比只跑后半段更强 | | `consultation-recovery.test.ts` 按 page.tsx 恢复 effect 切片匹配 404 / 重放 / 第 4 次放弃 | 直接调用 `runConsultationRecoveryPoll`;逐步假计时器在 `consultation-recovery-poll.test.ts` | 轮询搬出后,源码切片不再落在 page.tsx | | 从 page.tsx 切 `storedPending` 分支,并用源码顺序断言它先于全局状态 | 调用 `runHomeBootstrap`:本页 pending 覆盖全局 reserved;无 pending 时不删 key 的反例由「有 pending 且 reserved 则不删 key」锁住。写入 `sessionId/requestId/question` 的 effect 仍留在 page.tsx,继续按该 effect 切片 | 启动流程搬走;优先级改由结果锁定,不弱于源码顺序 | | `homeSurface` 里预留判断、`window.setTimeout(() => void poll(), 1_750)`、完成后的 `fetchSessionDetail` | 预留判断在 `home-bootstrap-run.ts`;1750ms 和详情读取在 `consultation-recovery-poll.ts`,调用变成 `fetchDetail(deps.pendingSessionId, deps.modelCatalog, signal)` | 两段流程搬出首页。状态端点、间隔、完成后拉详情都还在 | | `restoreConsultationRecovery(recoverySession, reservedConsultation.requestId, storedPending)` 在 homeSurface | 同一调用在 `home-bootstrap-run.ts` | 启动恢复搬走,仍把 stored pending 交给真实 restore | | page.tsx 直接调用 `resolveStarterHomeLandingSessionId` / `starterHomeLandingNeedsConsultation`,并引用 `HOME_BOOTSTRAP_SLOW_MS` | 这两个调用和 20 秒预算在 `home-bootstrap-run.ts`;`shouldAutoOpenRectificationSession` 仍在 page.tsx | 落点和超时属于启动流程。揭幕阶段的校正会话判断留在首页 | | `if (bootstrapFailed) setHydrated(true); else setBootstrapPhase("prepare")` 在 page.tsx | 同一分支在 `runHomeBootstrap`,setter 经 `deps.commit` | 失败仍揭幕错误屏,成功仍进入 prepare,不半揭幕 | | 错误屏 `
` 在 page.tsx | 同一段在 `home-bootstrap-error.tsx`。加载中的 polite main 仍在 page.tsx | 错误屏抽成组件,class 与 `role="alert"` 不变 | | homeSurface 两处「回答已恢复,已显示在对话区末尾。」 | `use-consultation-run.ts` 与 `consultation-recovery-poll.ts` 各一处 | 轮询搬走,两处文案都还在 | | page.tsx 里 `clearStaleClientReload(sessionStorage)`,以及从 `loadCloudData` 切登录跳转不关超时 | 首页传入 `storage: sessionStorage`,函数内 `clearStaleClientReload(deps.storage)`;登录跳转的 try/finally 在 `home-bootstrap-run.ts`,仍是 `window.clearTimeout(bootstrapTimeout)` 且 `redirectedToLogin` 时提前 return | 清的仍是会话存储。登录跳转仍不取消 20 秒超时 | | 从 homeSurface 切 `async function loadCloudData()` | 切 `resolveLanding`。`defaultSessionId` 现为 `ready.nextSessions[0].id` | 落点代码原样搬家。清 new-chat URL 仍在 `setActiveSessionId` 之前,默认落点仍不 `push` | | 从 page.tsx 切 `if (previewMode)` 到 `await sessionListReady` | 预览分支在 `home-bootstrap-run.ts`,结束锚点是 `await deps.sessionListReady`。首页把 `sessionListReady` / `sessionListBoot` 传进去 | 预览仍不写会话 URL。`/` 仍不在首页里 `fetchSessions` | | `session-list-provider.test.ts` 匹配 page.tsx 的 `await sessionListReady;` | 匹配首页传入的 `sessionListReady`,以及函数里的 `await deps.sessionListReady` | 会话列表仍只由 provider 拉一次 | ## 验证 开工前,下面四份测试 `npx tsx --test`:**28 pass / 0 fail**。 - `frontend/tests/new-chat-recovery.test.ts` - `frontend/tests/consultation-recovery.test.ts` - `frontend/tests/home-shell-growth-contract.test.ts` - `frontend/tests/home-bootstrap-reveal.test.ts` 改完后同一组,加上本单新测试和会被搬家碰到的合同(`chat-session-url`、`stale-client-recovery`、`chat-navigation-a11y-contract`、`session-list-provider`、`home-bootstrap-run`、`consultation-recovery-poll`、`home-bootstrap-screens`、`settings-url`):**全部通过,没有新增失败**。最后一次合并跑是 81 pass / 0 fail;补上地点卡文案后又跑了 screens + settings-url,7 pass / 0 fail。增长合同与 `tsc --noEmit` 随后再跑,exit 0。 - `npx tsx --test` 使用明确文件路径。没有用全量 `npm test` 当信号。 - `.\node_modules\.bin\tsc --noEmit`:0 错。 - `npm run lint`:0 error,124 warnings。没有去清原本就未使用的 import,也没有为消 warning 改 effect 依赖。 - `npm run build`:**没有通过**。Turbopack 拒绝本工作树的 `frontend/node_modules` junction(指向主检出,超出它认定的文件系统根)。按约束没有删除或重建这个 junction,也没有 `npm install`。因此不能声称 `/` 仍是 Static,也不能报首屏 gzip。 ## 没做的验证 - 没有登录浏览器走首页。本单声称无用户可见变化,靠测试锁行为。 - 没有 `next build`,见上。 - 没有推 `origin/staging`,没有推 `main`,没有改 workflow,没有动 DNS。