From 8558900525db4c48b20e6f0eca67bd3d45d1961d Mon Sep 17 00:00:00 2001 From: Jesse_Chen Date: Tue, 1 Sep 2026 13:47:24 +0000 Subject: [PATCH] docs(chat): add home-split batch-one task brief Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01LVapmh2oGNyr6ECHKjPJY8 --- TASK-home-split-20260901.md | 86 +++++++++++++++++++++++++++++++++++++ 1 file changed, 86 insertions(+) create mode 100644 TASK-home-split-20260901.md diff --git a/TASK-home-split-20260901.md b/TASK-home-split-20260901.md new file mode 100644 index 00000000..f75f3bd0 --- /dev/null +++ b/TASK-home-split-20260901.md @@ -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`,加一行注释说明"此文件正则前端源码,前端搬家轮必须同步它"。 +- 先做本任务再搬家:它是本轮搬家正确性的额外安全网。 + +### 任务 1(P0)· 模块级 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)· 星盘库 + 合盘面板抽组件 + +- 4252–4380 行的 `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 类型显式定义。 + +### 任务 3(P1)· starter 首页与每日星语抽组件 + +- 4541–4640 行的 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` 字段(等观测轮)。