docs(chat): add home-split batch-one task brief
Independent Staging Quality Gate / validate (push) Successful in 13m7s
Independent Staging Quality Gate / publish (push) Successful in 50m57s

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LVapmh2oGNyr6ECHKjPJY8
This commit is contained in:
Jesse_Chen
2026-09-01 13:47:24 +00:00
parent ce6a8a7e72
commit 8558900525
+86
View File
@@ -0,0 +1,86 @@
# 任务书 · 拆分首页巨石组件·第一批(2026-09-01)
基线:`origin/staging` @ `ce6a8a7e`(开工时以 `origin/staging` 最新为准)。这是 BUG-464/465/466 三轮之后的收官轮:前三轮把真相收归服务端,本轮开始拆运载这一切的 4,766 行 `frontend/src/app/page.tsx`。与任何同期改 `page.tsx` 的轮次**不得并行**。
**本轮性质是纯搬家,不是重写。** 逐行行为必须保持原样——不改逻辑、不改用户可见文案、不改 DOM 结构与 class 名、不调整时序。这也是 2026-08-17 React Compiler 轮 `BLOCKED.md` 的结论指出的正道("值得做的是按职责把 Home 拆小"),但本轮**不重开 React Compiler**。
---
## 为什么要做(事故实证)
1. `page.tsx` 4,766 行、65 个 `useState`、21 个 `useEffect``Home` 函数体约 3,470 行(1295 行起)。聊天、引导、星盘库、合盘、每日星语、starter 首页、账户弹窗全部同函数。历轮回归(BUG-409、445–448 等)多数落在此文件。
2. React Compiler 轮量化过:全项目 44 个组件可被编译,唯独 `Home` 因体量与结构被拒(`BLOCKED.md` 2026-08-17 记录)。同文件里两个小组件正常编译——问题就是"函数太大"本身。
3. **36 个测试文件用正则直接读 `page.tsx` 源码**`grep -rln 'app/page.tsx' frontend/tests tests` 可证)。这是本轮最大的爆炸半径,红线 3 专门处置。
4. 存量地雷(验收 BUG-466 时发现):`tests/test_supabase_user_data_contract.py`**3 条早已断掉的红**(断言里嵌的源码片段/UI 文案在此前轮次被改走,如 `添加其他星盘`、account 路由的数组守卫表达式、synastry 路由的 `apiBase` 行),而 staging quick gate 的 `CORE_PYTEST_TARGETS` 白名单不含此文件——一份没人运行的契约测试。它恰好大量正则 `page.tsx`,与本轮直接纠缠,必须先修。
## 决策记录(产品授权,2026-09-01)
1. **拆分分批进行,本轮只做第一批**helpers 出仓 + 星盘库/合盘面板 + starter 首页/每日星语)。聊天主链路(composer/transcript/consult 编排)**本轮不动**。
2. **允许成批修改"读 `page.tsx` 源码"的合同测试的读取路径**:仅限其锁的代码被本轮搬走的测试;断言的语义内容不得改变(同一正则、同一 token,换文件路径),逐条在 PROGRESS 登记"原路径 → 新路径"。这是对"不得修改既有测试断言"红线的定向豁免,超出此范围的断言改动仍被禁止。
3. **`test_supabase_user_data_contract.py` 修复后进 quality gate 白名单**`scripts/run_quality_gate.py``CORE_PYTEST_TARGETS`)。修复方向是让断言对齐现状源码、保住其锁的性质(如"数组 payload 仍被拒"),不是删除检查。
4. P1 验收遗留的小项(popstate 回默认会话未走 `selectSession` 副作用)**本轮不修**——纯搬家轮不夹带行为变化,另行小刀。
## 硬红线
1. **行为零变化。** 不改任何逻辑表达式、用户可见文案、class 名、aria 属性、时序。搬家中发现的疑似 bug 一律登记 `BLOCKED.md`,不得顺手修。
2. **状态迁移规则**:只有被抽出 surface **独占**的 state/handler 才随组件下移;被多个 surface 共享的 state 留在 `Home`,用显式 props 传递。**不得引入 context、全局 store 或任何新状态库**props 接口用显式 TypeScript 类型。
3. **测试改动仅限决策记录 2 的定向豁免**。动手前对每个待搬区域先 `grep -rn` 两个测试目录,列出受影响清单再搬;搬完逐条验证断言在新路径上仍然锁住同样内容。
4. `page.tsx` 顶部的 `import "@/app/site-styles"` 与动态 import 结构不动;`site-style-isolation-contract` / admin CSS 隔离测试必须保持绿灯且不被修改。新组件文件不得各自 import 全局样式。
5. 不得手写 `useCallback` / `useMemo`;不重开 React Compiler;不为编译器改写代码。
6. 推 staging 前 `./node_modules/.bin/tsc --noEmit` 通过(**不要 `npx tsc`**,空包坑)。
7. 测试总数不得低于基线 **2427**Docker 环境 fail=0、skipped=0;无 Docker 既有缺口 24 失败 + 10 skipped,清单与 `ce6a8a7e` 一致,逐条比对不得新增)。本轮不动数据库。**Python 侧**:任务 0 完成后 `tests/test_supabase_user_data_contract.py` 必须全绿并附 pytest 输出。
8. `next build``/``○ Static`;首屏 JS gzip 体积与基线差异超过 ±2% 须在 PROGRESS 说明原因。
9. 不得改 `.gitea/workflows/**`(白名单在 `scripts/run_quality_gate.py`,不属于 workflows)。不在脏工作树切分支。不自行提升 main。
让步顺序:功能与测试不回归 > 可验证的拆分 > 拆分行数目标 > 代码整洁。
## 开工前置
```bash
git fetch origin --prune
git worktree add -b codex/home-split-20260901 \
../.worktrees/home-split-20260901 origin/staging
```
基线必须是 `origin/staging`。读 `pre_work_error_ledger.md``scripts/pre_work_check.py``frontend/AGENTS.md``BLOCKED.md` 的 2026-08-17 React Compiler 记录(本轮的"为什么拆"与"怎么不拆"都在里面)。改前在 `docs/BUG_HISTORY.md` 检索。
## 任务分解
### 任务 0(P0,先行)· 修复孤儿契约测试并入白名单
-`tests/test_supabase_user_data_contract.py` 的 3 条红对齐现状(决策记录 3 的方向),全文件跑绿。
- 加入 `scripts/run_quality_gate.py``CORE_PYTEST_TARGETS`,加一行注释说明"此文件正则前端源码,前端搬家轮必须同步它"。
- 先做本任务再搬家:它是本轮搬家正确性的额外安全网。
### 任务 1P0)· 模块级 helpers 出仓(约 950 行)
- `page.tsx` 1–1290 行区间内的纯函数(storage 键、read/normalize 系列、cloud fetch 系列、label/format 系列、`createSession` 等)与三个顶层组件(`BirthLocationFields` 1003、`ProfileFields` 1065、`OnboardingChatMessage` 1094)搬到 `frontend/src/lib/``frontend/src/components/` 下的新文件(按既有命名风格分组,如 `lib/home-profile.ts``lib/home-cloud-sync.ts`)。
- 只搬引用不改实现;`page.tsx` 改为 import。类型随函数走。
### 任务 2(P1)· 星盘库 + 合盘面板抽组件
- 42524380 行的 `chart-library-panel`(含 `synastry-report-card``synastry-history-list`)连同其独占 state`otherProfileDraft``editingChartId``otherChartRelationship` 等)与 handlers`saveOtherChart` / `deleteOtherChart` / `editOtherChart` / `makeDefaultChart`)抽为 `components/chart-library-panel.tsx`
- 共享依赖(`account``profile``chartLibrary``activeChartId`、合盘发起等)走 props;props 类型显式定义。
### 任务 3P1)· starter 首页与每日星语抽组件
- 45414640 行的 starter 区(`starter-hero` / `product-entrypoints` / `starter-themes`,含 `daily-starlanguage-card`)抽为 `components/starter-home.tsx`(或按现状拆两个),独占 state 随行,入口动作走 props 回调。
### 任务 4(P2)· 度量与收尾
- PROGRESS 里给出前后对比:`page.tsx` 行数(目标 **≤ 3,000**,不达标写明哪块没拆及原因)、`useState` / `useEffect` 数、受影响测试清单(原路径 → 新路径)、首屏 gzip 对比。
- 顺手项仅允许:删除搬空后残留的死 import。
## 总验收
1. `tsc --noEmit` 通过;测试满足红线 7(前端 + Python 两侧输出都附)。
2. `next build``/``○ Static`;体积对比在 ±2% 内或有说明。
3. 行为抽查(无登录态则合同测试覆盖 + 如实标注):星盘库增删改、合盘发起与历史回看、starter 首页各入口点击、每日星语加载,与拆前一致。
4. 全部被搬 token 的合同测试逐条在新路径上重新锁定,无一被弱化或删除。
## 明确不做(不要顺手做)
- 不动聊天主链路(composer、transcript、consult 编排、恢复逻辑)——第二批再拆。
- 不引入 context/store/新依赖;不重开 React Compiler。
- 不修 popstate 回默认会话的副作用差异(决策记录 4)。
- 不删 BUG-464 的 PATCH 兼容层、不删 consult 的 `history` 字段(等观测轮)。