# PROGRESS · 首页加载统一:一次等待、一次揭幕(2026-09-02) 分支:`codex/unified-loading-20260902` 基线:`origin/staging` @ `02f06255`,并先行合入 `codex/streaming-ux-20260901` @ `c846c44a`(任务书前置条件;合并无冲突,merge commit `25e3094f`)。本轮改动写在合并后的代码形态上。 执行方:Claude(用户 2026-09-02 明示"直接开始执行")。 ## 结论 登录后只有一次揭幕。加载屏两阶段(载入账户 → 准备对话),阶段二并行取推荐问题、今日星语、校正入口摘要并预热校正分包;全部就绪或 4 秒到期才揭幕;揭幕后首页无任何组件级 spinner / busy / "正在…"文案(流式生成中除外)。 ## 与任务书的差异 - **当前会话消息**:合入的流式分支已把 `fetchSessionDetail` 前移到 `setHydrated` 之前(`page.tsx` 启动 effect),本轮不再重复做,只把它计入"阶段二"的语义说明。 - **阶段二实现方式**:没有把三个请求搬进启动 effect 的 `Promise.allSettled`,而是把三个既有 effect 的门槛从 `hydrated` 改为 `bootstrapPhase`(`"account" → "prepare"`),再用一个揭幕 effect 汇总就绪状态。理由:三个 effect 各自带重试/身份守卫/缓存逻辑(推荐问题的 `onboardingRequestIdentity`、今日星语的当日缓存与一次重试),原样复用不会产生重复请求,也不必复制这些逻辑。合同测试锁 `bootstrapPhase === "account" || !accountId` 门槛恰为 3 处。 - **今日星语的 pending 文案**:任务书写"非个性化文案";实际采用与 unavailable 相同的静态句「今天的星语还没写出来。」,到达后静默替换。原「正在写下今天的星语。」删除。 ## 改动清单 | 文件 | 内容 | | --- | --- | | `src/lib/home-bootstrap.ts`(新) | `BootstrapPhase`、`BOOTSTRAP_PREPARE_TIMEOUT_MS = 4000`、`SESSION_PREFETCH_COUNT = 5`、`bootstrapPrepareSettled`、`bootstrapRevealDelayMs`、`bootstrapLoadingCopy`、`sessionIdsToPrefetch` | | `src/app/page.tsx` | `bootstrapPhase` / `rectificationEntrySummarySettled` state、`prepareStartedAt` ref;`finally` 里成功路径改 `setBootstrapPhase("prepare")`(失败仍 `setHydrated(true)`);三个 effect 门槛与依赖改为 `bootstrapPhase`;入口摘要 effect 加 `finally` 置 settled;新增揭幕 effect(就绪或超时)、分包预热 effect、揭幕后预取 effect;加载屏文案按阶段;删 `starter-loading` 分支、`dailyStarlanguageBusy`、`InlineSpinner` import 与会话切换 spinner | | `src/components/starter-home.tsx` | 删 `dailyStarlanguageBusy` prop 与 `aria-busy` | | `src/app/globals.css` | 删两处 `.starter-loading` 定义(已无使用) | | `DESIGN.md` §9 | 整页阻塞行改为"两阶段载入";补首页揭幕规则段 | | `tests/home-bootstrap-reveal.test.ts`(新) | 7 条:纯函数 + 源码合同 | | `tests/daily-starlanguage.test.ts` | 例外条款:effect 切片标记改为新门槛与新依赖(原值 `!hydrated ...` / `[..., hydrated, ...]`);`aria-busy={dailyStarlanguageBusy}` 正断言改为不存在(原值见注释) | | `tests/starter-questions.test.ts` | 例外条款:守卫切片终点由 `"(onboardingPending ?"` 改为 `"