梳理按钮与触点的状态变化链路,定位共享状态导致的界面失效与不一致问题
复制安装指令,让 AI 自动完成配置 · 推荐新手
请帮我安装 askskill 上的 "click-path-audit" 技能: 1. 下载 https://raw.githubusercontent.com/affaan-m/ECC/main/docs/zh-CN/skills/click-path-audit/SKILL.md 2. 保存为 ~/.claude/skills/click-path-audit/SKILL.md 3. 装好后重载技能,告诉我可以用了
请对“提交订单”按钮做 click path audit,按用户可见步骤追踪从 hover、点击、加载、成功/失败反馈到全局状态更新的完整状态变化,找出哪些状态被其他组件覆盖,为什么用户看到按钮无响应,并给出复现步骤和修复建议。
一份按时间顺序整理的状态变更分析,指出冲突来源、最终错误状态、复现路径与修复建议。
我们刚把购物车页面重构为共享状态存储架构。请对“增加数量”“删除商品”“应用优惠券”三个触点执行 click path audit,分析它们分别单独正常但组合操作后是否会互相抵消、覆盖数据或让 UI 显示不一致。
一份跨触点的交互审计结果,说明各操作的状态依赖、冲突条件、异常最终状态及测试建议。
用户反馈筛选面板中的“重置”“应用筛选”“关闭面板”偶尔会让结果列表和筛选标签不一致。请执行 click path audit,画出每个按钮触发的状态序列,标出共享状态读写点,并分析导致最终 UI 不一致的根因。
一份按钮级状态流说明,清楚展示读写顺序、竞态或覆盖问题,以及可执行的修复方向。
发现静态代码审查遗漏的缺陷:状态交互副作用、顺序调用间的竞态条件,以及相互静默撤销的处理程序。
传统调试检查:
但未检查:
真实案例:一个"新邮件"按钮依次调用了 setComposeMode(true) 和 selectThread(null)。两者单独工作正常。但 selectThread 有一个副作用重置了 composeMode: false。按钮毫无反应。系统化调试发现了 54 个缺陷——这个被遗漏了。
针对目标区域内的每个交互触点:
1. 识别处理函数(onClick、onSubmit、onChange 等)
2. 按顺序追踪处理函数中的每个函数调用
3. 对于每个函数调用:
a. 它读取了哪些状态?
b. 它写入了哪些状态?
c. 它是否对共享状态产生了副作用?
d. 它是否作为副作用重置/清除了任何状态?
4. 检查:后续调用是否会撤销前面调用的状态变更?
5. 检查:最终状态是否符合用户对按钮标签的预期?
6. 检查:是否存在竞态条件(异步调用以错误顺序解析)?
在审计任何触点之前,构建每个状态存储操作的副作用映射:
对于作用域内的每个 Zustand 存储 / React 上下文:
对于每个操作/设置器:
- 它设置了哪些字段?
- 它是否作为副作用重置了其他字段?
- 文档:actionName → {sets: [...], resets: [...]}
这是关键参考。"新邮件"缺陷在不知道 selectThread 重置了 composeMode 的情况下是不可见的。
输出格式:
STORE: emailStore
setComposeMode(bool) → 设置: {composeMode}
selectThread(thread|null) → 设置: {selectedThread, selectedThreadId, messages, drafts, selectedDraft, summary} 重置: {composeMode: false, composeData: null, redraftOpen: false}
setDraftGenerating(bool) → 设置: {draftGenerating}
...
危险的重置(清除不属于自身状态的操作):
selectThread → 重置 composeMode(由 setComposeMode 拥有)
reset → 重置所有内容
针对目标区域内的每个按钮/开关/表单提交:
TOUCHPOINT: [按钮标签] 在 [组件:行]
HANDLER: onClick → {
调用 1: functionA() → 设置 {X: true}
调用 2: functionB() → 设置 {Y: null} 重置 {X: false} ← 冲突
}
EXPECTED: 用户看到 [按钮标签所承诺的描述]
ACTUAL: X 为 false,因为 functionB 重置了它
VERDICT: BUG — [描述]
检查以下每种缺陷模式:
handler() {
setState_A(true) // 设置 X = true
setState_B(null) // 副作用:重置 X = false
}
// 结果:X 为 false。第一次调用毫无意义。
handler() {
fetchA().then(() => setState({ loading: false }))
fetchB().then(() => setState({ loading: true }))
}
// 结果:最终的 loading 状态取决于哪个先完成
const [count, setCount] = useState(0)
const handler = useCallback(() => {
setCount(count + 1) // 捕获了过时的 count
setCount(count + 1) // 同样的过时 count — 只增加 1,而不是 2
}, [count])
// 按钮显示"保存",但处理程序仅验证,从未实际保存
// 按钮显示"删除",但处理程序设置了一个标志而未调用API
// 按钮显示"发送",但API端点已被移除/损坏
handler() {
if (someState) { // 此时 someState 始终为 false
doTheActualThing() // 永远不会执行到
}
}
// Button 设置 stateX = true
// useEffect 监听 stateX 并将其重置为 false
// 用户看不到任何变化
针对发现的每个缺陷:
CLICK-PATH-NNN: [严重性: 严重/高/中/低]
触点: [按钮标签] 位于 [文件:行号]
模式: [顺序撤销 / 异步竞态 / 过期闭包 / 缺失过渡 / 死路径 / useEffect 干扰]
处理函数: [函数名或内联]
追踪:
1. [调用] → 设置 {字段: 值}
2. [调用] → 重置 {字段: 值} ← 冲突
预期: [用户期望的结果]
实际: [实际发生的结果]
修复: [具体修复方案]
此审计成本较高。请适当限定范围:
Agent 1:映射所有状态存储(步骤 1)——这是所有其他代理的共享上下文
Agent 2:仪表盘(任务、笔记、日志、想法)
Agent 3:聊天(DanteChatColumn、JustChatPage)
Agent 4:邮件(ThreadList、DraftArea、EmailsPage)
Agent 5:项目(ProjectsPage、ProjectOverviewTab、NewProjectWizard)
Agent 6:CRM(所有子标签页)
Agent 7:个人资料、设置、保险库、通知
Agent 8:管理套件(所有页面)
代理 1 必须首先完成。其输出是所有其他代理的输入。
/superpowers:systematic-debugging(发现其他 54 种缺陷类型)之后运行/superpowers:verification-before-completion(验证修复是否有效)之前运行/superpowers:test-driven-development——此处发现的每个缺陷都应添加测试ThreadList.tsx "新邮件"按钮:
onClick={() => {
useEmailStore.getState().setComposeMode(true) // ✓ 设置 composeMode = true
useEmailStore.getState().selectThread(null) // ✗ 重置 composeMode = false
}}
存储定义:
selectThread: (thread) => set({
…
通过双评审智能体对结果进行对抗式校验,提升输出发布前的可靠性
系统性沿调用链逆向排查缺陷,快速定位问题的最初触发点