Rule — 重构后语义清单¶
触发场景:大重构(hook 抽取 / 组件拆分 / 状态机重写)完成时,写 commit 前必做的对账。
为什么需要这个规则¶
v0.10.79 ISSUE-0059 "useDataSource hook 大重构" 后第 13 轮 agent 一击命中真 bug: server mode 改 keyword 后 page 不 reset → 空表格。
根因:旧版有两个 useEffect:
useEffect(() => {
if (mode === 'server') getTableData(); // 用当前 page
}, [page, pageSize, sort, mode]);
useEffect(() => {
if (mode === 'server') getTableData(1); // ← 强制 page=1(次生副作用!)
}, [filters, keyword, logic, filterTaskId, quickFilter, mode]);
抽 hook 时只搬了"读数据"(getTableData → hook 内部 useEffect),没搬"reset page=1" 的次生副作用。
Agent 元洞察: "Hook 化时只搬了'读数据'路径,没搬'控制 page'的次生路径。重构清单应该不仅列 useEffect, 还列每个 useEffect 的副作用语义('reset page=1'),逐项确认目标位置承接。"
"这种语义流失类 bug,agent 难一眼看出,因为 grep 不到对应 token —— 只有顺着用户视角 '我刚改了 keyword 应该怎样'才能挖出。"
4 步对账(必做)¶
Step 1: 列出原代码所有 useEffect / 事件 handler 的副作用清单¶
不要只看 useEffect 主体的"做了什么"(如 getTableData()),而是问:
这个 useEffect 触发时,除了主操作,还有哪些隐性 setState / state 重置?
例如 v0.10.78 之前的 useEffect [filters, keyword, ...] → getTableData(1):
- 主操作:拉数据(getTableData)
- 隐性副作用:重置 page 到 1((1) 参数即 newPage=1)
- 隐性副作用:dataRun.onSuccess 内 setState({selectKeys: []})
每个 useEffect 列 1+N 条副作用语义。
Step 2: 列出新代码承接位置 + 检查每条副作用¶
对照原清单,新代码每条副作用对应到哪里:
| 原副作用 | 新位置 |
|---|---|
| 拉数据 | hook 内 useEffect ✓ |
| reset page=1 | ???? ← 遗忘点 |
| reset selectKeys=[] | hook onServerSuccess callback ✓ |
任何"????"都是漏。必须在 commit 前补齐。
Step 3: 用户视角 walkthrough¶
不要只想代码层,顺用户行为想:
- 用户 A:在 page=5 → 改 keyword → 期待什么?(page 回 1)
- 用户 B:在 client mode + 选中 5 行 → 切到 server → 期待什么?(选中状态清空)
- 用户 C:删了 10 行 → 期待什么?(列表立即少 10 行)
每个 walkthrough → 在新代码里是不是发生了期待的行为?
Step 4: 独立 agent 审查¶
写 commit 前,强制一次独立 agent 审查(fresh eyes 无 anchor bias):
Agent 找不到?人继续走 Step 1-3。
反模式(必避免)¶
❌ "我把 useEffect A 抽到 hook 里了" — 不够:useEffect A 的副作用 1+N 都搬了吗?
❌ "TypeScript / build 都过了" — 类型对不代表语义对(v0.10.79/80 都过 compile,但漏 page reset)
❌ "agent 1 轮没找到问题就说收敛" — 第 12 轮 agent 没抓到,第 13 轮抓到。至少 2 轮 agent 才能初步信收敛
工作流¶
重构完代码
↓
Step 1: 列原 useEffect 副作用清单(含次生)
↓
Step 2: 列新代码承接,逐项对账
↓
Step 3: 用户视角 walkthrough N 个典型场景
↓
Step 4: 独立 agent 审查
↓
全部 OK → commit
任一项漏 → 补 → 回到 Step 4
案例库¶
| 重构 | 漏的副作用 | 教训 |
|---|---|---|
| v0.10.79 useDataSource hook | reset page=1(仅原 useEffect 1 行 getTableData(1) 的隐性副作用) |
隐性副作用最危险 |
| 未来 | 要么避免要么对照 |