三个"点了没反应"的真实 bug,给 Claude 的 prompt 少一句关键描述就定不了位。
Bug 分两种。报错的那种,把 stack trace 扔给 Claude,大概率 30 秒给答案。不报错的那种——点按钮没反应、页面不动、表单静默失败——Claude 第一反应往往也不对。不是它笨,是它看不到。
最近改 how2claude 的付费流,连续踩了三个这种 bug。复盘一遍,顺便记录下来调玄学 bug 时的 prompt 模式。
x402 加密支付接进去,本地跑通了。上生产一测,每次点"支付"都失败:前端 console 报 invalid_string at payTo。signing flow 都没启动,facilitator 的 Zod schema 先拒了。
钱包地址是 0x... 42 字符,肉眼看规规矩矩。我让 Claude 查 config/credentials/production.yml.enc 里的 wallet 字段。
w = Rails.application.credentials.dig(:x402, :wallet_address).to_s
puts "length: #{w.length}"
# => 43
43 个字符。EVM 地址应该是 42。第 43 个字符是个中文全角问号 ?(U+FF1F),复制粘贴时从中文输入法带进来的。
Claude 第一次扫这个字段时没发现异常——它看到的是 0x... 开头的字符串,长度对不对它没主动量。prompt 补一句:「这个地址比预期长 1 位,逐字符打印 codepoint」。0xFF1F 一出来,问题立刻定位。
Pricing 页的 Subscribe 按钮,点下去页面不跳转。没报错,network tab 里 POST 发出去了,Stripe 正常返回 302 到 checkout.stripe.com——然后页面就是不动。
我先让 Claude 看 controller,逻辑正常:redirect_to session.url, allow_other_host: true。看 JS,没有相关 listener。
盯了半天响应才发现:Content-Type: text/vnd.turbo-stream.html。Turbo 把 button_to 的提交拦截成了 Turbo Stream 请求,而 Turbo Stream 不跟随跨源 302——302 被吃了,页面静默保持不动。
修复一行:
<%= button_to "Subscribe", ..., data: { turbo: false } %>
同一个 bug 一个月后又在 Google OAuth 按钮上重现过一次。框架级的拦截器是玄学 bug 的高发区——Claude 默认按"请求-响应"线性推理,不会主动去查有没有中间层改了语义。prompt 补:「这个点击行为经过了哪些框架级拦截?把请求在浏览器→服务器→浏览器全链路上被哪些中间件/JS 处理过的列一下」。
Pricing 页月付/年付切换的 Stimulus controller,点按钮不切。Controller 的方法被触发了(console.log 有输出),但 this.monthlyTarget 是 undefined。
Claude 第一反应:target 名字拼错了。检查——没拼错。data-pricing-target="monthly" 也在 DOM 里。
问题在 scope。data-controller="pricing" 挂在按钮容器上,monthly/yearly 那两块 grid 在容器外面。Stimulus 只在 controller 元素内部的子树里找 target,外面的找不到。把 data-controller 上提到包裹整个 section 的 <section>,OK。
这个 bug 特别像"代码没错"——所有名字对、所有属性在、功能就是坏的。Claude 默认逐行检查代码,不会主动去可视化 DOM 结构。prompt 补:「把 data-controller="pricing" 这个元素的祖先和后代画出来,标出所有 data-pricing-target 在不在子树里」。
三个 bug 症状都是"点了没反应 / 行为不对 / 没报错"。Claude 第一反应都不对,但补一句精确的 prompt 之后秒定位。共通的套路:
1. 告诉它"预期 vs 实际"的量化差异,别只说"不对"
不是「钱包地址有问题」,是「长度比预期多 1 个字符」。
不是「按钮不动」,是「响应是 302,但浏览器没跳转」。
不是「切换失效」,是「controller 方法触发了,但 target 是 undefined」。
差异越量化,Claude 的搜索空间越窄。
2. 指向框架级、浏览器级、编码级这些"看不见的层"
玄学 bug 基本都不在业务代码里。在 Turbo、在 Stimulus scope、在字符编码、在 CSP、在 CORS、在 service worker。Claude 默认只看业务代码。要在 prompt 里显式叫它去看这些层。
3. 让它输出中间状态,不是推理结论
让 Claude「逐字符打印 codepoint」「列出响应头」「dump 整棵 DOM 子树」——把中间状态物化出来,比让它推理结论靠谱。玄学 bug 的"玄",就玄在推理链某一步有个隐藏前提不成立。物化中间状态,等于强制把那个隐藏前提暴露出来。
报错的 bug 考验 Claude 的知识储备,不报错的 bug 考验你给它的信号质量。你给的越具体,它找得越快。