54269fcfcc
page.tsx still owns the chat main chain, but the first product surfaces now live in their own modules so later splits can land without editing the 4k-line Home. Source-lock tests follow the moved tokens; the orphan user-data contract is aligned and added to the quick gate. Co-authored-by: Cursor <cursoragent@cursor.com>
104 lines
8.0 KiB
Markdown
104 lines
8.0 KiB
Markdown
# PROGRESS · 拆分首页巨石组件·第一批(2026-09-01)
|
||
|
||
工作树:`.worktrees/home-split-20260901`
|
||
分支:`codex/home-split-20260901`(跟踪 `origin/staging`)
|
||
基线:`origin/staging` @ `85589005`(任务书提交;产品代码与 P2a `ce6a8a7e` 相同)
|
||
|
||
不要写成 `PROGRESS.md`。本轮未提交、未推送。
|
||
|
||
## 度量
|
||
|
||
| 项 | 拆前 `85589005` | 拆后 | 说明 |
|
||
| --- | ---: | ---: | --- |
|
||
| `frontend/src/app/page.tsx` 行数 | 4766 | 3505 | 目标 ≤3000,未达标 |
|
||
| `useState(` | 26 | 26 | 见下方 overlay 说明 |
|
||
| `useEffect(` | 20 | 20 | 拉取/重试 effect 仍在 Home |
|
||
| `/` 路由 | `○ Static` | `○ Static` | webpack `next build` |
|
||
| 首屏 JS gzip -9 | 500262 B = 488.5 KB | 501315 B = 489.6 KB | +1053 B / **+0.21%**(±2% 内) |
|
||
| 前端测试 | 2427 | 2427 | fail=0 skipped=0(见验证) |
|
||
|
||
未达标原因:聊天主链路(composer / transcript / `send` / 停止与恢复 / 引导对话)按任务书本批不动。抽走的是 helpers + 星盘库/合盘面板 + starter/每日星语。剩余约 505 行要等第二批。
|
||
|
||
`useState` 未下降:`otherProfileDraft` / `editingChartId` / `otherChartRelationship` / `editingSelfChart` 仍留在 `Home`。`AccountDialogOverlay` 在关闭时 `return null`,把独占 state 下移进面板会在关弹窗时丢掉未保存草稿,属于行为变化,本轮禁止。handlers(`saveOtherChart` 等)已随 JSX 进面板,state 经显式 props 传入。
|
||
|
||
## 任务 0 · 孤儿契约测试
|
||
|
||
`tests/test_supabase_user_data_contract.py` 对齐现状源码(性质保留:浏览器不直写 `profiles` / `chat_sessions`,账户 PATCH 仍拒数组,合盘仍走 `postPython("/api/chart")`)。任务书写的三处之外,同一文件里还有过期 token,一并换成现码并在上方注释原值:
|
||
|
||
- 首页不再 `.from("profiles")` / `.from("chat_sessions")`,锁 `fetch("/api/account"` / `fetch("/api/sessions"`
|
||
- 账户 PATCH:`accountProfilePatchSchema.safeParse` + `z.object({`(`account-profile-patch.ts`)
|
||
- 文案「添加其他人的星盘」
|
||
- 合盘:`postPython("/api/chart", selfPayload)` / `partnerPayload`
|
||
- 用户消息:`messages: questionAlreadyPresent ? preservedMessages : [...preservedMessages, { role: "user", text: question }]`
|
||
- 中断路径:源码已无 `await persistSession(interruptedSession)`;停止文案「正在停止回答并申请退回本次点数…」
|
||
|
||
搬家后该文件改读 `_home_surface()`(`page.tsx` + 抽出文件拼接),正则/token 未改。
|
||
|
||
`scripts/run_quality_gate.py` 的 `CORE_PYTEST_TARGETS` 已列入此文件,注释写明前端搬家轮必须带着它跑。
|
||
|
||
`pytest tests/test_supabase_user_data_contract.py tests/test_daily_and_rectification_entrypoints.py -q` → 12 passed。
|
||
|
||
## 任务 1–3 · 搬家
|
||
|
||
新文件(实现从 `page.tsx` 原样搬出,加 `export`):
|
||
|
||
| 文件 | 行 | 内容 |
|
||
| --- | ---: | --- |
|
||
| `frontend/src/lib/home-types.ts` | 254 | 类型、空资料、themes、storage 键常量 |
|
||
| `frontend/src/lib/home-profile.ts` | 323 | 地点/资料/标签/`readProfile`/`upsertSelfChart` |
|
||
| `frontend/src/lib/home-cloud-sync.ts` | 499 | 云端 fetch、登录跳转、每日星语读写 |
|
||
| `frontend/src/components/birth-location-fields.tsx` | 40 | `BirthLocationFields` |
|
||
| `frontend/src/components/profile-fields.tsx` | 38 | `ProfileFields` |
|
||
| `frontend/src/components/onboarding-chat-message.tsx` | 28 | `OnboardingChatMessage` |
|
||
| `frontend/src/components/chart-library-panel.tsx` | 269 | 星盘库 + 合盘卡/历史/他人表单 |
|
||
| `frontend/src/components/starter-home.tsx` | 159 | starter hero / 每日星语 / 生时入口 / 主题卡 |
|
||
|
||
`page.tsx` 仍 `"use client"`、`import "@/app/site-styles"`、`const BirthTimeRectification = dynamic(...)`。新组件未 import 全局样式。无 context/store、无手写 `useCallback`/`useMemo`、未重开 React Compiler。
|
||
|
||
本人星盘表单仍 `onSubmit={saveProfile}`(面板 prop 名就是 `saveProfile`)。starter 回调 prop 保持原标识符:`startDailyStarlanguageConsultation`、`openRectificationFromHomepage`、`startSuggestedConsultation`。
|
||
|
||
## 合同测试路径(同一 token,只换读取面)
|
||
|
||
共用拼接件 `frontend/tests/home-surface.ts`:`page.tsx` + 上表抽出文件。Python 侧 `_home_surface()` / 显式 concat 同理。
|
||
|
||
| 测试 | 原路径 | 新路径 |
|
||
| --- | --- | --- |
|
||
| `birth-place-picker.test.ts` | `src/app/page.tsx` | `homeSurface`(`BirthPlacePicker` 在 `birth-location-fields.tsx`) |
|
||
| `birth-time-consultation-consent.test.ts` | `page.tsx` | `homeSurface`(`readProfile` 在 `home-profile.ts`) |
|
||
| `character-remaining-contract.test.ts` | `page.tsx` | `homeSurface`(姓名字数 `aria-describedby` 在 `profile-fields.tsx`) |
|
||
| `chart-library-other-profile.test.ts` | `page.tsx` | `homeSurface` |
|
||
| `chat-navigation-a11y-contract.test.ts` | `page.tsx` | `homeSurface` |
|
||
| `chat-session-authority.test.ts` | `page.tsx` | `homeSurface` |
|
||
| `chat-session-url.test.ts` | `page.tsx` | `homeSurface` |
|
||
| `chat-session-write.test.ts` | `page.tsx` | `homeSurface`(`fetch("/api/sessions"` 在 `home-cloud-sync.ts`) |
|
||
| `composer-isolation-contract.test.ts` | `page.tsx` | `homeSurface`(`pendingConsultationStorageKey` 在 `home-types.ts`) |
|
||
| `consultation-entrypoint.test.ts` | `page.tsx` | `homeSurface`(starter 卡在 `starter-home.tsx`) |
|
||
| `consultation-recovery.test.ts` | `page.tsx` | `homeSurface` |
|
||
| `daily-starlanguage.test.ts` | `page.tsx` | `homeSurface` |
|
||
| `membership-page.test.ts`(home 段) | `page.tsx` | `homeSurface`(`LoginRedirectError` 在 `home-cloud-sync.ts`) |
|
||
| `onboarding-presentation.test.ts` | `page.tsx` | `homeSurface` |
|
||
| `rectification-agentic-entry.test.ts` | `page.tsx` | `homeSurface` |
|
||
| `settings-mvp-contract.test.ts` `renderProfile` | `page.tsx` 切片 | `homeSurface`(切片标记仍在 `page.tsx`) |
|
||
| `settings-mvp-contract.test.ts` 星盘库表单 | `page.tsx` 的 `renderChartLibrary`→`renderGeneral` 切片 | `chart-library-panel.tsx`(切片里只剩 `<ChartLibraryPanel />`,token 已搬走) |
|
||
| `starter-questions.test.ts` | `page.tsx` | `homeSurface` |
|
||
| `tests/test_supabase_user_data_contract.py` | `PAGE.read_text()` | `_home_surface()` |
|
||
| `tests/test_daily_and_rectification_entrypoints.py` | `PAGE` | `PAGE` + `starter-home.tsx` + `home-cloud-sync.ts` |
|
||
| `tests/test_birth_time_journey_contract.py` 网页引导条 | `page.tsx` | `page.tsx` + `home-profile.ts`(`reported_birth_time` / `active_birth_time`) |
|
||
|
||
未改断言语义。锁聊天主链路、且 token 仍在 `page.tsx` 的测试继续只读 `page.tsx`(如 `site-style-isolation-contract`、composer/sidebar 壳、`rectification-lazy-loading`)。
|
||
|
||
## 验证
|
||
|
||
- `./node_modules/.bin/tsc --noEmit`:通过。
|
||
- `npm test`:2427 条。并发下 2 条 Docker 库测 flake(`admin-database` / `model-configuration-security`,`database migration failed` / `Connection terminated unexpectedly`)。`--test-concurrency=1` 重跑这 8 条:pass 8 fail 0。与产品搬家无关。skipped=0。
|
||
- `./node_modules/.bin/next build --webpack`:`┌ ○ /`。本树 `node_modules` 仍是指向 `session-url-20260901` 的符号链接,默认 Turbopack 会拒。gzip 口径:预渲染 `index.html` 引用的 `/_next/static/**.js` 去重后 `gzipSync` level 9。基线用同 SHA 干净 worktree 同口径对照。
|
||
- 行为抽查:无登录态,未在浏览器点星盘库增删改、合盘、starter 入口、每日星语。由合同测试覆盖。
|
||
|
||
## 明确未做 / 未修
|
||
|
||
- 聊天主链路第二批再拆。
|
||
- popstate 回默认会话未走 `selectSession`(任务书决策 4)。
|
||
- `tests/test_session_management_entrypoints.py::test_archiving_never_calls_the_delete_endpoint` 仍锁已删除的 `setArchivedSessionIds`(P2a 遗留,不在 `CORE_PYTEST_TARGETS`,本轮不顺手修)。
|
||
- `tests/test_birth_time_journey_contract.py::test_web_onboarding_uses_the_deterministic_free_journey` 在 mastra 断言 `'entry_mode: entryMode'` 上仍红(与搬家无关、不在 quick gate);本轮只补了 `page.tsx` 侧 token 路径。
|
||
- 未改 `.gitea/workflows/**`。未提升 `main`。
|