8558900525
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LVapmh2oGNyr6ECHKjPJY8
8.2 KiB
8.2 KiB
任务书 · 拆分首页巨石组件·第一批(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。
为什么要做(事故实证)
page.tsx4,766 行、65 个useState、21 个useEffect,Home函数体约 3,470 行(1295 行起)。聊天、引导、星盘库、合盘、每日星语、starter 首页、账户弹窗全部同函数。历轮回归(BUG-409、445–448 等)多数落在此文件。- React Compiler 轮量化过:全项目 44 个组件可被编译,唯独
Home因体量与结构被拒(BLOCKED.md2026-08-17 记录)。同文件里两个小组件正常编译——问题就是"函数太大"本身。 - 36 个测试文件用正则直接读
page.tsx源码(grep -rln 'app/page.tsx' frontend/tests tests可证)。这是本轮最大的爆炸半径,红线 3 专门处置。 - 存量地雷(验收 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)
- 拆分分批进行,本轮只做第一批(helpers 出仓 + 星盘库/合盘面板 + starter 首页/每日星语)。聊天主链路(composer/transcript/consult 编排)本轮不动。
- 允许成批修改"读
page.tsx源码"的合同测试的读取路径:仅限其锁的代码被本轮搬走的测试;断言的语义内容不得改变(同一正则、同一 token,换文件路径),逐条在 PROGRESS 登记"原路径 → 新路径"。这是对"不得修改既有测试断言"红线的定向豁免,超出此范围的断言改动仍被禁止。 test_supabase_user_data_contract.py修复后进 quality gate 白名单(scripts/run_quality_gate.py的CORE_PYTEST_TARGETS)。修复方向是让断言对齐现状源码、保住其锁的性质(如"数组 payload 仍被拒"),不是删除检查。- P1 验收遗留的小项(popstate 回默认会话未走
selectSession副作用)本轮不修——纯搬家轮不夹带行为变化,另行小刀。
硬红线
- 行为零变化。 不改任何逻辑表达式、用户可见文案、class 名、aria 属性、时序。搬家中发现的疑似 bug 一律登记
BLOCKED.md,不得顺手修。 - 状态迁移规则:只有被抽出 surface 独占的 state/handler 才随组件下移;被多个 surface 共享的 state 留在
Home,用显式 props 传递。不得引入 context、全局 store 或任何新状态库;props 接口用显式 TypeScript 类型。 - 测试改动仅限决策记录 2 的定向豁免。动手前对每个待搬区域先
grep -rn两个测试目录,列出受影响清单再搬;搬完逐条验证断言在新路径上仍然锁住同样内容。 page.tsx顶部的import "@/app/site-styles"与动态 import 结构不动;site-style-isolation-contract/ admin CSS 隔离测试必须保持绿灯且不被修改。新组件文件不得各自 import 全局样式。- 不得手写
useCallback/useMemo;不重开 React Compiler;不为编译器改写代码。 - 推 staging 前
./node_modules/.bin/tsc --noEmit通过(不要npx tsc,空包坑)。 - 测试总数不得低于基线 2427(Docker 环境 fail=0、skipped=0;无 Docker 既有缺口 24 失败 + 10 skipped,清单与
ce6a8a7e一致,逐条比对不得新增)。本轮不动数据库。Python 侧:任务 0 完成后tests/test_supabase_user_data_contract.py必须全绿并附 pytest 输出。 next build后/仍○ Static;首屏 JS gzip 体积与基线差异超过 ±2% 须在 PROGRESS 说明原因。- 不得改
.gitea/workflows/**(白名单在scripts/run_quality_gate.py,不属于 workflows)。不在脏工作树切分支。不自行提升 main。
让步顺序:功能与测试不回归 > 可验证的拆分 > 拆分行数目标 > 代码整洁。
开工前置
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.tsx1–1290 行区间内的纯函数(storage 键、read/normalize 系列、cloud fetch 系列、label/format 系列、createSession等)与三个顶层组件(BirthLocationFields1003、ProfileFields1065、OnboardingChatMessage1094)搬到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。
总验收
tsc --noEmit通过;测试满足红线 7(前端 + Python 两侧输出都附)。next build:/仍○ Static;体积对比在 ±2% 内或有说明。- 行为抽查(无登录态则合同测试覆盖 + 如实标注):星盘库增删改、合盘发起与历史回看、starter 首页各入口点击、每日星语加载,与拆前一致。
- 全部被搬 token 的合同测试逐条在新路径上重新锁定,无一被弱化或删除。
明确不做(不要顺手做)
- 不动聊天主链路(composer、transcript、consult 编排、恢复逻辑)——第二批再拆。
- 不引入 context/store/新依赖;不重开 React Compiler。
- 不修 popstate 回默认会话的副作用差异(决策记录 4)。
- 不删 BUG-464 的 PATCH 兼容层、不删 consult 的
history字段(等观测轮)。