Files
Jyotisha/PROGRESS-contract-repairs-20260901.md
T
Jesse_Chen 551d6317ae
Independent Staging Quality Gate / validate (push) Has been cancelled
Independent Staging Quality Gate / publish (push) Has been cancelled
refactor(chat): extract consultation and session hooks after contract repairs
Keep archive as a PATCH of archived_at rather than DELETE, pass entry_mode through onboarding, and let popstate to the default chat reuse selectSession. Then move send/stop/recovery and session management out of page.tsx so the home surface stays within the batch-two line budget.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-02 04:34:14 +08:00

72 lines
5.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# PROGRESS · 小刀轮:孤儿契约红修复 + popstate 副作用对齐(2026-09-01
工作树:`.worktrees/contract-repairs-20260901`
分支:`codex/contract-repairs-20260901`(跟踪 `origin/staging`
基线:`origin/staging` @ `a6148af8`(任务书提交;产品代码与第一批 `54269fcf` 相同,另加两份任务书)
不要写成 `PROGRESS.md`。小刀轮与第二批在同一工作树落地(第二批任务书写「小刀轮落地后的 origin/staging」;小刀当时尚未推送,故未另开 `home-split-batch2` 工作树)。交付为快进推 `origin/staging`
开工后 `origin/staging` 又快进了 4 个无关文档提交(`ff97de5e``8b7eda9f`),未并入本树。
## 任务 1 · 归档契约
文件:`tests/test_session_management_entrypoints.py::test_archiving_never_calls_the_delete_endpoint`
| 项 | 原值 | 新值 |
| --- | --- | --- |
| 读取面 | `PAGE.read_text()`(仅 `page.tsx` | `_home_surface()``page.tsx` + 第一批抽出文件 + 若存在则拼接 `hooks/use-session-management.ts``hooks/use-consultation-run.ts` |
| 切片 | `function toggleArchivedSession``async function shareSession` | 同左(两函数第二批一起搬进 session hook,切片标记仍相邻) |
| 正断言 | `"setArchivedSessionIds" in archive_action` | `'writeChatSession(sessionId, { archived_at: nextArchivedAt }, "update")' in archive_action``"archived_at: nextArchivedAt" in archive_action` |
| 负断言 | `"/api/sessions/" not in archive_action` | `'method: "DELETE"' not in archive_action``"DELETE" not in archive_action` |
| PATCH | (无) | `chat-session-write-contract.ts``method: mode === "create" ? "POST" : "PATCH"` |
同文件 `test_chat_history_management_actions_are_exposed` 的读取面从 `PAGE + STYLES + SESSION_ROW` 改为 `_home_surface() + STYLES + SESSION_ROW`,断言 token 未改。`test_chat_session_delete_is_server_controlled_and_granted` 仍读路由/迁移。
`CORE_PYTEST_TARGETS`:加入 `tests/test_session_management_entrypoints.py`,注释「Locks archive as PATCH archived_at, never HTTP DELETE (data-safety).」
## 任务 2 · 引导旅程契约
文件:`tests/test_birth_time_journey_contract.py::test_web_onboarding_uses_the_deterministic_free_journey`
| 项 | 原值 | 新值 |
| --- | --- | --- |
| page 读取面 | `page.tsx` + `home-profile.ts` | `_home_surface()`(同上,含可选 hook 文件) |
| mastra 读取面 | `frontend/src/mastra/index.ts` | `_mastra_consultation_surface()` = `index.ts` + `consultation-workflow.ts``index.ts` 只再导出;token 在 workflow |
| 正断言 | `'entry_mode: entryMode' in mastra` | 不变(现码 `consultation-workflow.ts` |
| 负断言 | `'entry_mode: "direct_chart"' not in mastra` | 不变(schema 默认值是 `entryMode: z.enum(...).default("direct_chart")`,不是 `entry_mode: "direct_chart"``consultation-tools.ts` 未拼进此读取面) |
白名单决定:**加入** `CORE_PYTEST_TARGETS`。整文件只读 SQL 迁移与 TS 源码,无 subprocess、无网络、无 Docker。注释「Pure source/SQL regex for the birth-time journey; no runtime services.」
## 任务 3 · popstate 副作用对齐
`applySessionPopStateRef`(现位于 `use-session-management.ts`,小刀改完后原样搬):
| 项 | 原值 | 新值 |
| --- | --- | --- |
| `requestedId` | `query.present ? query.sessionId : fallbackId`(无 `?c=` 时直接等于列表首项,空 id 分支实际上只有「列表为空」才会进) | `query.sessionId`(无 `?c=` 时进空 id 分支) |
| 空 id 分支 | `setActiveSessionId(""); return;` | 有 `fallbackId``sessionSelectionSource.current = "history"; selectSession(fallbackId);`;否则维持 `setActiveSessionId("")` |
有会话时 back 到 `/` 仍走 `selectSession`(清草稿/提示、切绑定星盘),来源保持 history,不二次 `pushState`。无会话时行为不变。
`frontend/tests/chat-session-url.test.ts`
- 原 P1 合同「popstate reuses selectSession without a second push」未改断言;切片里 `writeSessionUrl(..., "push")` 仍只有 `selectSession` 内那一处。
- 新增「popstate to a missing session query reuses selectSession side effects for the default chat」,锁空 id 分支源码形态,且 popstate 切片内 `writeSessionUrl(..., "push")` 次数为 0。
`frontend/tests/home-surface.ts` 同步加入可选 hook 文件(`existsSync`),避免第二批把 `selectSession` / popstate 搬走后本文件立刻再红。
## 前瞻(第二批)
两份 Python 合同与 `chat-session-url.test.ts` 都走拼接读取。第二批把 `toggleArchivedSession` / `applySessionPopStateRef` 搬进 `use-session-management.ts` 后,hook 文件进可选清单即可。切片标记保持相邻。
## 验证
- `./node_modules/.bin/tsc --noEmit`:通过(小刀后与第二批抽取后各跑一次)。
- `python3.12 -m pytest tests/test_session_management_entrypoints.py tests/test_birth_time_journey_contract.py -q`12 passed3 + 9)。
- 连同 `tests/test_supabase_user_data_contract.py` `tests/test_daily_and_rectification_entrypoints.py`24 passed。
- `tsx --test tests/chat-session-url.test.ts`12 pass / 0 fail / 0 skipped。
- 前端全量 `npm test`2428 pass / 0 fail / 0 skipped(小刀新增 1 条 popstate 源码合同;相对第一批 2427)。
- `./node_modules/.bin/next build --webpack``┌ ○ /` Static。首屏 JS gzip-9 = 501104 B(第一批后 501315 B,−0.04%)。
未改 `.gitea/workflows/**`。未动数据库。未提升 `main`