# TASK · 把校正面的 15 个状态从首页搬下去(page.tsx 状态下沉 · 第一簇) - 日期:2026-09-15 - 基线 commit:`origin/staging` @ `6b3248bf`(开工时以最新 `origin/staging` 为准,见 §7) - 执行分支:`codex/home-state-lowering-20260915` - 落点:`frontend/src/app/page.tsx`、`frontend/src/hooks/use-rectification-surface.ts`、`frontend/src/components/conversational-birth-time-rectification.tsx`(或新建的容器组件) - **串行依赖(三条,缺一不可)**: 1. `TASK-freeze-metric-change-20260915` —— 本单要让抽出去的 hook 持有 React 状态,那正是现行 `AGENTS.md §6` 禁止的;必须等新口径生效 2. `TASK-consultation-context-memory-20260915`(C2)—— 它在改 `use-session-management.ts` / `use-consultation-run.ts`,与本单同一片状态层 3. `TASK-rectification-settled-render-split-20260915`(R3)—— 它在改 `rectification-agentic-chat.tsx`,与本单同一片子树 - 规模:一簇状态换个住处。**零行为变化、零文案变化。** --- ## 1. 为什么是这一簇 `page.tsx` 现在 1,951 行,行数上限 1,951,**余量 0**。但行数不是病根: | 指标 | BUG-249 当时 | 今天 | | --- | ---: | ---: | | 文件行数 | 4,766 | 1,951 | | `Home()` 的 `useState` | 56 | **66** | | `useEffect` | 18 | 22 | | `useRef` | — | 41 | | `useCallback` / `useMemo` | 0 / 0 | 0 / 0 | **行数砍掉 59%,状态反而从 56 涨到 66。** 前几轮拆的是代码不是状态:抽出去的 hook 是参数式的,`useSessionManagement(params)` 开头要解构约 40 个参数,`useRectificationSurface(params)` 约 56 个——状态所有权一个都没搬,每搬一次还要新增一批传参。 66 个 state 按归属分群: | 群 | 个数 | | --- | ---: | | **`rectification*`** | **15** | | `session*` | 10 | | `profile*` | 6 | | `synastry*` | 4 | | `account*` | 4 | | 其余分散 | 27 | 最大的一簇服务的是一个**已经是 `dynamic()` 懒加载的子树**:`` 挂着 **24 个 props**,而喂它们的 15 个 state 全住在 `Home()` 里。边界最清楚、收益最大,所以第一簇搬它。 那 15 个是: ``` rectificationSessionId rectificationShouldStartOpening rectificationCaseId rectificationTurns rectificationHeaderSlot rectificationSnapshot rectificationPendingQuestion rectificationOpeningSessionId rectificationLoading rectificationEntrySummary rectificationMutationPending rectificationEntrySummarySettled rectificationError rectificationErrorSessionId rectificationReadonly ``` ## 2. 根因 `AGENTS.md §6` 现行那句「参数式 hook 内部保持 0 个 React hook 的既定模式」把状态钉死在 `Home()` 里;同一节的行数冻结又不许 `page.tsx` 增长。两条合起来等于「不许再加状态」,而每一轮新功能都要加。`TASK-freeze-metric-change-20260915` 已经拿到产品授权推翻前半句,本单是第一个吃到新口径的轮次。 ## 3. 决策记录 产品 2026-09-15 拍板: 1. **`page.tsx` 走「状态下沉」这条路**,不引外部 store、不铺全局 Context Provider。理由:不引新依赖、可以一簇一簇增量做,每搬一簇 `page.tsx` 就真降一截。 2. **第一簇搬校正面的 15 个。** 后续 `session*` / `profile*` / `synastry*` 各自另开单,本单不碰。 3. **抽出去的 hook 与子组件从此持有自己的状态**(推翻 §6 旧红线,措辞由 freeze-metric-change 单落地)。执行方不得以「AGENTS 说参数式 hook 里不能有 React hook」为由拒改。 4. **零行为变化。** 本单不修任何已知交互缺陷,发现了写进进度记录。 ## 4. 硬红线 1. **不是 15 个都能搬。** 有几个外壳自己要读(例如 `rectificationSessionId` / `rectificationCaseId` 参与决定显示哪个界面、侧栏会话列表也要知道)。**第一步必须逐个分类**:「只服务子树 → 搬下去」「外壳也要读 → 留在外壳,但收敛成一个对象,不再是散装的多个 `useState`」。分类表写进进度记录,不许含糊。 2. **零行为变化**是唯一成败判据:进入校正面、退出、刷新、从首页卡片打开、打开历史校正、开场自动触发、只读态、报错态、换模型、采用后回首页——逐条与改前一致。 3. `next build` 后 `/` 仍须 `○ Static`;首屏 JS gzip 变化在 ±2 % 内(上次实测 584,413 B)。 4. 测试总数不得低于开工时 `origin/staging` 的实测;改任何既有断言必须写「原值 / 新值 / 原因」三栏(AGENTS §7.3)。 5. 不得新写第二个聊天输入框、第二套滚动跟随、第二套加载动画(§6 第三条原样有效)。 6. 不得顺手升级依赖、不得顺手修不在本单里的 warning。 7. **不得改数据库、不得改任何 API 路由。** 本单只动前端状态的住处。 ## 5. 任务分解 ### 5.1 先分类,再动手 把 15 个逐个归类并写进进度记录: | 类别 | 处置 | | --- | --- | | 只服务校正子树 | 搬进子树(`useRectificationSurface` 变成真 hook,或由容器组件持有) | | 外壳也要读 | 留在外壳,但合并成**一个**状态对象,散装 `useState` 数下降 | - 验收:分类表在进度记录里,15 个一个不漏,每个写明依据(谁在读它)。 ### 5.2 `useRectificationSurface` 变成真 hook 现在它要解构约 56 个参数。改成自己 `useState` / `useEffect` 持有第一类状态,对外只暴露子树真正需要的接口;`page.tsx` 侧的传参随之消失。 - 验收:`useRectificationSurface` 的参数个数显著下降,新值写进进度记录(改前约 56)。 - 验收:`` 的 props 个数下降,新值写进进度记录(改前 24)。 ### 5.3 `Home()` 的状态计数必须真降 - 验收:`Home()` 的 `useState` 数从 **66** 降到 **≤ 53**(搬走至少 13 个;允许留 2 个在外壳,多留必须逐个说明理由)。 - 验收:`useRef` 数不得上升(改前 41)——不许把 state 改写成 ref 来凑数字。 - 验收:`frontend/tests/home-shell-growth-contract.test.ts`(由 freeze-metric-change 单建立)在本单收尾时更新为新基线,并贴一次反向验证。 ### 5.4 行为等价证明 - 验收:改动前后各跑一次全量前端套件,失败清单逐条一致(无 Docker 时数据库套件照常红)。 - 验收:`npx tsx --test tests/rectification-*.test.ts` 全绿,断言零改动——如果必须改,按 §4.4 写三栏说明。 - 验收:`docs/testing/` 下留一份真人走查清单,覆盖 §4.2 那十条路径(本仓没有浏览器与登录态,这一项只能人工)。 ### 5.5 记录 本单不产生 Bug 记录(不是缺陷,是结构改造),不进 `CHANGELOG.md`(无用户可感知变化)。若过程中确实改了任何可见样式,必须同提交更新 `frontend/DESIGN.md`(AGENTS §7.5)。 ## 6. 让步顺序 1. 5.1(分类)**不得砍**——没有分类表就动手,一定会把外壳要读的状态搬下去然后再搬回来。 2. 5.2 + 5.3 是主体。 3. 5.3 的目标值可以让步(比如只搬到 ≤ 56),但让步幅度和原因必须写进进度记录,**不得静默降低**。 4. 5.4 不得砍。 5. 5.5 不得砍。 ## 7. 开工前置命令 ```bash git fetch origin --prune # 三条串行依赖必须都已合入 staging,逐个确认 git log --oneline origin/staging | head -20 git worktree add -b codex/home-state-lowering-20260915 \ .worktrees/home-state-lowering-20260915 origin/staging cd .worktrees/home-state-lowering-20260915/frontend git status -sb | head -1 npm ci # 取当时实测基线,不要抄本任务书里的数字(七条在飞分支合并后会变) grep -cE '\buseState[<(]' src/app/page.tsx grep -cE '\buseRef[<(]' src/app/page.tsx grep -oE 'const \[rectification[A-Za-z]+' src/app/page.tsx | wc -l ``` 验收命令: ```bash ./node_modules/.bin/tsc --noEmit npm run lint # 0 error npx tsx --test tests/rectification-*.test.ts tests/home-shell-growth-contract.test.ts npx tsx --test tests/*.test.ts # 与基线逐条比对失败清单 npm run build # `/` 仍须 ○ Static,首屏 gzip ±2% ``` ## 8. BUG 编号起点 本单不占 BUG 号。基线 `6b3248bf` 上最大号 **BUG-720**,721–732 已被两轮审计七单预占。 ## 9. 不在本单范围 - `session*` / `profile*` / `synastry*` / `account*` 四簇(各自另开单) - Context Provider 或外部 store(§3.1 已否决这一轮走这两条路) - 校正面自身的任何交互缺陷(本单零行为变化) - API server 拆解(另一条线)