Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017eEAG8HD3mm8gsKXgk8uU8
97 lines
10 KiB
Markdown
97 lines
10 KiB
Markdown
# PROGRESS · 登录后 / 裸 `/` 落空白首页,删除登录返回存根(BUG-1052)· 2026-09-27
|
||
|
||
- 执行方式:直接执行(产品负责人授权子代理执行;Claude 事后独立验收)
|
||
- 基线:`origin/staging` `5f2e007f` 开工;提交前变基到 `76924e33`(只多一份任务书文档,代码与测试结果不变)
|
||
- 分支:`codex/home-landing-blank-20260927`(本地提交,未推送)
|
||
- BUG 编号:1052(开工时最大号 1051)
|
||
- 决策:产品 2026-09-27 D1–D4,推翻 BUG-1038 登录返回存根与「裸 `/` 默认落最近会话」,写在 `docs/BUG_HISTORY.md` BUG-1052 决策记录
|
||
|
||
## 结论
|
||
|
||
| 项 | 结果 |
|
||
| --- | --- |
|
||
| 根因 | 与任务单诊断一致:① 登录返回存根(401 / 侧栏次级页与「去登录」链接写入,登录后 `replace-selected` 写回 `?c=` 并打开)——真机「落在旧校正里」只能来自这条;② 默认落点取最近一条,只从生时校正换到「空咨询或任意咨询」,所以裸 `/` 通常开最近一条旧咨询。暖返回逐条镜像。补充:旧空咨询判定只看 `messages.length`,未加载消息的列表行也会被当草稿,已收紧 |
|
||
| D1 裸 `/` → 空白首页 | 完成。none / replace-clear 只复用当前人物的空草稿(`isStarterHomeDraft`),否则本地新建;冷启动用 `replaceUnsavedEmptyConsultations` 前插不留重复;暖返回同一套函数 |
|
||
| D2 `?c=` / `?new=1` | 不变,有生命周期用例锁住 |
|
||
| D3 删除存根 | 完成。删 `persistLoginSessionReturn` / `readLoginSessionReturn`、`storedReturnId`、`clearStoredReturn`、`replace-selected`、lookup 来源参数与 `other-subject`、暖返回 `resumeRectification` 依赖;`redirectToLogin` / `leaveChat` 不写、`startNewChat` / `openChatBoundToProfile` 不清;冷启动无条件 `clearLoginSessionReturn()` 清旧值。人物范围:空白首页绑定当前人物、只复用本人草稿 |
|
||
| D4 对话内刷新 | 核实成立(URL 带 `?c=` → keep),生命周期用例 + Chrome 截图 |
|
||
| 执行方解读(待验收确认) | 裸 `/` 时若有旧回答在后台生成:恢复继续,但不抢空白首页(`landingYieldsToRecovery`:只有 keep / lookup 让步),与 BUG-1015 `?new=1` 同规则 |
|
||
| 未改 | 站内浏览器「返回」退到不带 `?c=` 的历史条目(popstate)仍选列表第一条非校正会话;不是整页打开 `/`,不在 D1 列举内 |
|
||
|
||
## 改动文件
|
||
|
||
| 文件 | 改动 |
|
||
| --- | --- |
|
||
| `frontend/src/lib/chat-session-url.ts` | 选择函数去存根;lookup 结果只剩 found / missing / unavailable;删写 / 读存根,保留清理 |
|
||
| `frontend/src/lib/home-bootstrap.ts` | `isStarterHomeDraft`、新落点规则、`landingYieldsToRecovery`;`LandingSession` 加可选 `messagesHydrated` / `archivedAt` |
|
||
| `frontend/src/lib/home-bootstrap-run.ts` | 冷落点按当前人物范围、去重前插、无条件清旧存根、恢复让步规则;暖提交删存根分支与 `resumeRectification` |
|
||
| `frontend/src/lib/home-warm-start.ts` | 去 `storedReturnId` 与存根分支 |
|
||
| `frontend/src/lib/home-cloud-sync.ts` | `redirectToLogin` 不写存根;`resolveLookupBootstrap` 去存根来源 |
|
||
| `frontend/src/components/app-sidebar.tsx` | `leaveChat` 只关抽屉 |
|
||
| `frontend/src/hooks/use-session-management.ts` | 新建对话不再清存根 |
|
||
| `frontend/src/app/(app)/page.tsx` | 删暖刷新的 `resumeRectification` 一行(1370 → 1369 行;`Home()` useState 33 / useRef 37 不变) |
|
||
|
||
## 改动的既有断言(原值 / 新值 / 原因均已写在测试行内注释)
|
||
|
||
| 文件 | 用例(原标题 → 新标题) | 摘要 |
|
||
| --- | --- | --- |
|
||
| `chat-session-url.test.ts` | new chat intent overrides … plus login return → … invalid c | 去 `storedReturnId` / `clearStoredReturn` |
|
||
| 同上 | new chat bootstrap consumes the URL … | `clearStoredReturn` 条件清 → 无条件清;直接前插 → `replaceUnsavedEmptyConsultations` |
|
||
| 同上 | bootstrap reads a listed ?c= session and ignores login storage → bootstrap reads a listed ?c= session | 去存根字段 |
|
||
| 同上 | bootstrap restores a stored login return only when the URL has no c → bare / after login never returns to a stored conversation (BUG-1052) | `replace-selected` → `none`,源码反向锁 |
|
||
| 同上 | bootstrap clears an illegal or unknown session query | 去存根字段(2 处) |
|
||
| 同上 | default bootstrap selection does not write a session URL | 去存根字段;`replace-selected` 正向 → 反向 |
|
||
| 同上 | 401 login redirect stashes the current session id … → 401 login redirect stashes nothing … | 正向 `persistLoginSessionReturn` → 反向 |
|
||
| 同上 | login return storage only accepts a UUID session id → a leftover login-return stash from an older build is only ever cleared | 写 / 读 → 只清、隐私模式不抛 |
|
||
| `chat-navigation-a11y-contract.test.ts` | auth redirects stay hard document loads … | `persistLoginSessionReturn();\n replace` → `clearHomeWarmSnapshot();\n replace` + 反向 |
|
||
| `sidebar-contract.test.ts` | reaches chart, ephemeris and reports with links … | `leaveChat` 先存根 → 只关抽屉 + 反向 |
|
||
| `home-bootstrap-reveal.test.ts` | default landing replaces a latest rectification session … | `replace-selected` → `lookup`;none 选普通咨询 → 选空草稿 |
|
||
| 同上 | bare home still opens the selected ordinary chat without creating another → bare home with a latest ordinary chat lands on a new blank starter home (BUG-1052) | NeedsConsultation false → true |
|
||
| `session-lookup-unlisted.test.ts` | lookup results … | 去 `clearStoredReturn` |
|
||
| 同上 | a looked-up stashed return writes its URL back … → only a ?c= is ever looked up … (BUG-1052) | 来源参数删除,源码反向锁 |
|
||
| `home-warm-snapshot.test.ts` | warm landing follows the cold rules: ?c= keeps, stash writes back, … → … bare / is the blank starter home, ?new=1 is a new chat | bare CONS → 新建空对话;新增复用本人草稿 / 不复用他人草稿 |
|
||
| `new-chat-recovery.test.ts` | ordinary home landing still activates and announces … → bare / keeps the blank starter home while … recovers (BUG-1052) | active 旧会话 → 空白首页、notice 清空、storedCleared true;另加 `?c=` 仍被接管 |
|
||
| `new-chat-from-people-lifecycle.test.tsx` | 侧栏新建对话 ×5、他人存根不落锁死页 | 离开时存根 RECT → null(落点断言不变) |
|
||
| 同上 | a stashed session of the same person found beyond the first page opens … → a leftover stash … is ignored and cleared … (BUG-1052) | 打开校正并写回 `?c=` → 空白首页、不 lookup、清旧值 |
|
||
| 同上 | an in-page new chat drops the stashed return … → an in-page new chat leaves no return trail … | 不预置存根,断言新建不写 |
|
||
| `home-warm-return-lifecycle.test.tsx` | a stashed return to a rectification session reopens it … → a warm return to bare / after a rectification session lands on the blank starter home … | 写回 `?c=` 打开校正 → 空白首页、首帧无加载环 |
|
||
| fixtures(非断言) | `home-bootstrap-run` / `session-list-lifecycle` / `consultation-recovery` / `new-chat-recovery` / `home-warm-snapshot` | 去掉 selection 里的 `clearStoredReturn` 字段 |
|
||
|
||
新增:生命周期 5 条(裸 `/` 最近普通咨询 / 最近生时校正、`?c=` + 刷新、校正内刷新、校正内 401 → 重登)、暖返回冷启动裸 `/` 1 条、纯函数 2 条(未加载消息的行不算草稿、恢复让步规则)、`?c=` 恢复接管 1 条。改写与新增的生命周期用例放到 `origin/staging` 代码上跑:12 条失败(`?c=` 与刷新两条本就应通过,作 D2 / D4 护栏);修复后全绿。
|
||
|
||
## 验证
|
||
|
||
| 项 | 结果 |
|
||
| --- | --- |
|
||
| `tsc --noEmit` | 0 错 |
|
||
| `npm run lint` | 0 error(128 warning,改动文件无新增与本单标识符相关的 warning) |
|
||
| 全量 `npm test`(Node 22.14) | 4078 / 24 fail / 28 skip;基线 4069 / 24 / 28。失败名单与基线逐条一致(全部 Docker/DB);消失的 12 个用例名全部是改名,三栏说明在行内,新增 21 个名 |
|
||
| Python 门禁集合 | 948 passed / 1 skipped(与基线一致) |
|
||
| `npm run build -- --webpack` | `/`、`/chart`、`/ephemeris`、`/people` 均 ○ Static;已删构建产生的 `frontend/frontend/` |
|
||
| 首屏 gzip(`rootMainFiles` 4 个文件 zlib 默认级别求和) | 131,253 B;基线 130,933 B,+0.24%,在 ±2% 内 |
|
||
| 定向 13 个文件 | 167 / 0 fail |
|
||
|
||
## 本地 Chrome(headless,CDP 拦截 `/api/*` 返回虚构数据)
|
||
|
||
`next start`(本单构建)+ Chrome `--headless=new`,截图在 `docs/testing/home-landing-blank-20260927/`:
|
||
|
||
| 截图 | 场景 | 结果 |
|
||
| --- | --- | --- |
|
||
| `1-bare-latest-rectification.png` | 列表 = [生时校正(最近), 普通咨询],打开 `/` | 空白首页「新对话」、地址 `/`、无转录、未开校正 |
|
||
| `2-bare-latest-consultation.png` | 列表 = [普通咨询(最近,有消息), 生时校正],打开 `/` | 空白首页 |
|
||
| `3-bare-leftover-stash.png` | sessionStorage 预置旧存根 = 校正会话,打开 `/` | 空白首页,存根被清 |
|
||
| `4-c-link-consultation.png` | 打开 `/?c=<咨询>` | 进该对话、有问答 |
|
||
| `5-c-link-rectification.png` | 打开 `/?c=<校正>` | 进校正界面 |
|
||
| `6-refresh-in-rectification.png` | 在 5 上刷新 | 仍在校正界面、地址不变 |
|
||
|
||
`POST /api/rectification/cases/open` 共 2 次,只出现在 5 / 6。右下角「合盘历史暂时无法读取」提示是本 harness 未 mock `/api/synastry-reports`(404)所致,与本单无关。
|
||
|
||
## 环境缺口
|
||
|
||
- 无受控登录账号:真实邮箱验证码登录、真实 401 过期后重登、iPhone Safari 走查留给 `docs/testing/home-landing-blank-20260927.md`。
|
||
- 无 Docker:24 条 DB/部署测试失败,与基线名单逐条一致。
|
||
|
||
## 验收(Claude,2026-09-27)
|
||
|
||
独立复跑(Node 22.14,基于 `76924e33`):tsc 0;lint 0 error;`npm test` 4078 / 24 fail / 28 skip,失败名单与基线 `ct-test.log`(4069 / 24)逐条一致;12 个测试改名均有三栏说明;Python 门禁集退出 0;`/`、`/chart`、`/ephemeris`、`/people` ○ Static;rootMainFiles gzip(level 9)130933 B,±0%。提交无删除、未碰 `.gitea/`、`vendor/`、`deploy/`。截图核对:最近一条是生时校正时,裸 `/` 落在空白首页,侧栏历史仍在。认可两处执行方判断:裸 `/` 时后台恢复中的旧回答不再抢占首页(与 BUG-1015 `?new=1` 同规则);站内浏览器后退到无 `?c=` 的历史项不在本单范围。真实 OTP 登录、401 过期重登、iPhone 走查按清单待产品走。
|