docs(chat): add contract-repairs and home-split batch-two task briefs
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LVapmh2oGNyr6ECHKjPJY8
This commit is contained in:
@@ -0,0 +1,63 @@
|
||||
# 任务书 · 小刀轮:孤儿契约红修复 + popstate 副作用对齐(2026-09-01)
|
||||
|
||||
基线:`origin/staging` @ `54269fcf`(开工时以 `origin/staging` 最新为准)。**本轮必须先于 `TASK-home-split-batch2-20260901.md` 执行**——第二批会再次搬动本轮涉及的函数,顺序反了会白修一遍。与其它改 `page.tsx` 的轮次不得并行。
|
||||
|
||||
这是一轮小刀:三个任务互相独立、单个都很小,但每个都有明确的性质要保住。
|
||||
|
||||
---
|
||||
|
||||
## 为什么要做(事故实证)
|
||||
|
||||
1. **`tests/test_session_management_entrypoints.py::test_archiving_never_calls_the_delete_endpoint` 红**(P2a 起)。它按 `function toggleArchivedSession` → `async function shareSession` 切片,断言切片含 `setArchivedSessionIds` 且不含 `/api/sessions/`。P2a 后归档改走 PATCH `archived_at`:旧符号没了,而且切片里**合法地**出现了对 `/api/sessions/[id]` 的 PATCH。断言锁的形而不是质。它锁的真正性质——**归档动作绝不能变成删除**——是数据安全级的,值得进 gate。
|
||||
2. **`tests/test_birth_time_journey_contract.py::test_web_onboarding_uses_the_deterministic_free_journey` 红**(更早轮次起)。`assert 'entry_mode: entryMode' in mastra` 的源码 token 已过期;配套的反向断言 `'entry_mode: "direct_chart"' not in mastra` 仍成立。性质——**引导旅程透传入口模式、不硬编码 direct_chart**——还在,表达式变了。
|
||||
3. 两条都不在 `scripts/run_quality_gate.py` 的 `CORE_PYTEST_TARGETS`,所以 CI 从不运行——又是"没人跑的契约测试"(同类问题上轮刚在 `test_supabase_user_data_contract.py` 处理过一次)。
|
||||
4. **popstate 回默认态副作用缺失**(P1 验收记录的遗留)。`page.tsx` 里 `applySessionPopStateRef` 的 `if (!requestedId) { setActiveSessionId(""); return; }` 分支:用 back 键回到无 `?c=` 的默认态时,只重置了选中会话,**没有**走 `selectSession` 的清草稿、清提示、切换绑定星盘等副作用——和用户在侧栏点击同一会话的行为不一致。
|
||||
|
||||
## 决策记录(产品授权,2026-09-01)
|
||||
|
||||
1. **任务 3 是行为变化**(三个搬家轮里唯一授权的一处):back 键回默认态时执行与主动点击选择相同的副作用。`chat-session-url.test.ts` 中锁旧行为的断言按例外条款修改。
|
||||
2. `test_session_management_entrypoints.py` 修复后**加入** `CORE_PYTEST_TARGETS`。`test_birth_time_journey_contract.py` 修复后**评估**加入:整文件在纯源码/SQL 正则内且无重依赖则加入;有运行时依赖导致 gate 环境跑不了,则不加入并在 PROGRESS 写明依赖是什么。
|
||||
3. 修断言的方向一律是**对齐现状、保住性质**:归档测试改为断言切片走 `archived_at` 的 PATCH 且不含 `"DELETE"` 方法(token 由执行方按现码确定);journey 测试找到现码里透传入口模式的等价表达式,正负两条断言都保留。**不得删除检查了事。**
|
||||
4. 修好的两个测试文件的读取面要**经得起第二批搬家**:涉及 `page.tsx` 函数切片的断言,改用上轮 `_home_surface()` 式的拼接读取(`page.tsx` + 已抽出文件),切片标记若在第二批要搬的函数上,选择对搬家稳健的定位方式。
|
||||
|
||||
## 硬红线
|
||||
|
||||
1. 不得修改本任务书范围外的任何测试断言;范围内的修改逐条在 PROGRESS 注明原值与新值。
|
||||
2. 任务 3 之外不得有任何行为变化;popstate 修改不得引入二次 pushState(P1 的防翻倍合同测试必须保持绿灯)。
|
||||
3. 推 staging 前 `./node_modules/.bin/tsc --noEmit` 通过(不要 `npx tsc`)。
|
||||
4. 前端测试总数不低于 **2427**(Docker 环境 fail=0、skipped=0;无 Docker 既有缺口 24+10,清单与 `54269fcf` 一致逐条比对)。Python 侧:两个目标文件修复后全文件 pytest 输出附进 PROGRESS。
|
||||
5. 不改 `.gitea/workflows/**`(白名单在 `scripts/run_quality_gate.py`)。`next build` 后 `/` 仍 `○ Static`。不动数据库。
|
||||
6. 不在脏工作树切分支。不自行提升 main。
|
||||
|
||||
让步顺序:性质不丢失 > 功能与测试不回归 > 代码整洁。
|
||||
|
||||
## 开工前置
|
||||
|
||||
```bash
|
||||
git fetch origin --prune
|
||||
git worktree add -b codex/contract-repairs-20260901 \
|
||||
../.worktrees/contract-repairs-20260901 origin/staging
|
||||
```
|
||||
|
||||
读 `pre_work_error_ledger.md`、`scripts/pre_work_check.py`、`frontend/AGENTS.md`。参考上轮 `PROGRESS-home-split-20260901.md` 的任务 0 —— 同类修复的完整样板。
|
||||
|
||||
## 任务分解
|
||||
|
||||
### 任务 1 · 归档契约修复入 gate
|
||||
|
||||
- 修 `test_archiving_never_calls_the_delete_endpoint`(决策记录 3/4 的方向),全文件跑绿。
|
||||
- 加入 `CORE_PYTEST_TARGETS`,注释说明它锁"归档绝不删除"。
|
||||
|
||||
### 任务 2 · 引导旅程契约修复
|
||||
|
||||
- 修 `test_web_onboarding_uses_the_deterministic_free_journey`,全文件跑绿。
|
||||
- 按决策记录 2 评估白名单。
|
||||
|
||||
### 任务 3 · popstate 副作用对齐
|
||||
|
||||
- `applySessionPopStateRef` 的无参数分支:有可选会话时经与主动选择一致的路径切换(不 push,来源标志保持 history 语义);无会话时维持现状。
|
||||
- `chat-session-url.test.ts` 相应断言按例外条款更新,并补一条锁新行为的断言。
|
||||
|
||||
## 总验收
|
||||
|
||||
`tsc` 通过;前端测试满足红线 4 并附失败清单比对;两个 Python 文件 pytest 全绿输出;`/` 仍 Static;PROGRESS 列出全部断言改动的原值/新值与白名单决定。
|
||||
@@ -0,0 +1,69 @@
|
||||
# 任务书 · 拆分首页巨石组件·第二批:聊天主链路(2026-09-01)
|
||||
|
||||
基线:**`TASK-contract-repairs-20260901.md`(小刀轮)落地后的 `origin/staging` 最新提交**。小刀轮未落地不得开工——它会修改本轮要搬的函数区域的契约测试读取面,顺序反了两边都返工。与其它改 `page.tsx` 的轮次不得并行。
|
||||
|
||||
**本轮性质仍是纯搬家。** 第一批(`54269fcf`)的全部红线原样适用;本任务书只写增量。第一批的 PROGRESS 与 `tests/home-surface.ts` 拼接件是本轮的直接样板。
|
||||
|
||||
---
|
||||
|
||||
## 为什么要做(事故实证)
|
||||
|
||||
行号基于 `54269fcf`,按符号定位。
|
||||
|
||||
1. 第一批后 `page.tsx` 还有 3,505 行,其中 `Home` 内的**咨询引擎**是最大的一坨:`send`(2340–2907,约 570 行)、`restoreConsultationRecovery`(623 起)、`requestCancellation` / `confirmCancellation` / `stopResponse`(2152–2329)、`completeConsultationInterface`(2330),连同其独占的 streaming/pending/cancellation state 与 refs,合计约 1,000+ 行。
|
||||
2. **会话管理簇**次之:`persistSession` / `ensureSessionMessages` / `continueInNewChat` / `renameSession` / `deleteSession` / `togglePinnedSession` / `toggleArchivedSession` / `shareSession` / `startNewChat` / `selectSession` / `selectSessionModel`(1279–1581)加 URL/popstate 胶水,约 300 行。
|
||||
3. 第一批目标 ≤3,000 未达标的声明原因正是这两簇"本批不动"。现在动。
|
||||
|
||||
## 决策记录(产品授权,2026-09-01)
|
||||
|
||||
1. **允许以自定义 hook 形式整体搬移**(`frontend/src/hooks/use-consultation-run.ts`、`use-session-management.ts`)。自定义 hook 不在"不得手写 `useCallback`/`useMemo`"红线内——那条红线继续禁的是记忆化原语,不是 hook 抽取。
|
||||
2. 搬移的正确性标准与第一批相同:**函数体逐行一致**(允许的差异仅:缩进、`export`、跨边界标识符经参数/返回值改道)。改名清单应为零或接近零,全部列进 PROGRESS。
|
||||
3. 状态归属规则沿用第一批:仅引擎独占的 state/refs 进 hook;与 JSX 共享的由 hook 返回或经参数传入。**不得引入 context/store**;参数与返回值显式类型。
|
||||
4. 第一批"useState 不下移"的星盘库草稿等结论不受本轮影响——那些 state 不在本轮范围。
|
||||
|
||||
## 硬红线(在第一批红线之上追加)
|
||||
|
||||
1. **hook 调用顺序不变**:被搬 state/refs 必须整块连续搬移、保持相对声明顺序;hook 只能无条件调用。搬完后 `Home` 内剩余 hook 与新 hook 内部的声明顺序串接起来必须与搬前逐一对应,PROGRESS 给出对应表或说明核对方式。
|
||||
2. **测试重指仍走定向豁免**:新文件加入 `tests/home-surface.ts` 拼接清单与 Python 侧 `_home_surface()`;仍直接读 `page.tsx` 且其 token 被搬走的测试(动手前 `grep -rn 'app/page.tsx' frontend/tests tests` 列全量清单)只许换读取面,断言内容不变。小刀轮刚修好的两个文件必须保持全绿。
|
||||
3. 目标:`page.tsx` **≤ 2,600 行**;不达标写明剩余块与原因。
|
||||
4. 测试基线以开工时 `origin/staging` 实测为准(小刀轮可能加了测试数;fail=0、skipped=0 于 Docker 环境,无 Docker 逐条比对既有缺口清单)。
|
||||
5. 其余同第一批:行为零变化、疑似 bug 登记 `BLOCKED.md`、`site-styles` import 结构不动、`/` 保持 `○ Static`、首屏 gzip ±2%、tsc、不改 `.gitea/workflows/**`、不动数据库、不重开 React Compiler。
|
||||
|
||||
让步顺序:功能与测试不回归 > 可验证的拆分 > 拆分行数目标 > 代码整洁。
|
||||
|
||||
## 开工前置
|
||||
|
||||
```bash
|
||||
git fetch origin --prune
|
||||
git worktree add -b codex/home-split-batch2-20260901 \
|
||||
../.worktrees/home-split-batch2-20260901 origin/staging
|
||||
```
|
||||
|
||||
确认 `origin/staging` 已包含小刀轮提交。读第一批 `PROGRESS-home-split-20260901.md` 全篇、`pre_work_error_ledger.md`、`frontend/AGENTS.md`、`BLOCKED.md` 2026-08-17 记录。
|
||||
|
||||
## 任务分解
|
||||
|
||||
### 任务 1(P0)· 咨询引擎出仓
|
||||
|
||||
- `send` / `stopResponse` / `requestCancellation` / `confirmCancellation` / `completeConsultationInterface` / `restoreConsultationRecovery` 及其独占 state/refs(streamingReply、pendingConsultation、consultationPhase、cancellation 系等,以实际独占性为准)整体搬入 `hooks/use-consultation-run.ts`。
|
||||
- 跨边界依赖(router、composerInput、profile、sessions 写入器、rectification 胶水回调等)经显式参数传入。
|
||||
|
||||
### 任务 2(P1)· 会话管理出仓
|
||||
|
||||
- 1279–1581 的会话管理簇 + URL/popstate 胶水(含小刀轮改过的 `applySessionPopStateRef` 分支,**原样搬**)搬入 `hooks/use-session-management.ts`。
|
||||
- 与任务 1 的边界:`send` 需要的 `updateSession` / `persistSession` 等由会话管理 hook 返回、经 Home 传入咨询引擎 hook,方向单一,不得互相 import。
|
||||
|
||||
### 任务 3(P1)· 读取面与度量
|
||||
|
||||
- `home-surface.ts` / `_home_surface()` 扩容;受影响测试清单(原路径 → 新路径)逐条登记。
|
||||
- PROGRESS 度量表同第一批口径:行数、useState/useEffect 分布(Home 与两个 hook 分列)、首屏 gzip 对比、`/` 路由模式。
|
||||
|
||||
## 总验收
|
||||
|
||||
同第一批四条,另加:hook 顺序核对说明(红线 1);两个新 hook 文件的函数体与搬前逐行 diff 说明(决策记录 2);小刀轮两个 Python 文件保持全绿的 pytest 输出。
|
||||
|
||||
## 明确不做
|
||||
|
||||
- 不动 onboarding/profile 簇(1582–1835)与 rectification 胶水(1888–2075)——若第二批顺利,另行第三批。
|
||||
- 不做任何行为修正(popstate 已在小刀轮完成,本轮原样搬)。
|
||||
- 不引入 context/store/新依赖;不重开 React Compiler。
|
||||
Reference in New Issue
Block a user