Free

Để Claude gỡ những bug im lặng

Ba bug thực tế kiểu "click không phản ứng" — Claude đoán sai hoài cho đến khi thêm một câu chính xác vào prompt.


Bug có hai loại. Loại có báo lỗi — đưa stack trace cho Claude là 30 giây sau có câu trả lời. Loại không báo lỗi — nút bấm không phản hồi, trang không chuyển, form fail im lặng — loại này Claude hay đoán sai ngay lần đầu. Không phải nó ngu. Là nó không nhìn thấy.

Gần đây khi làm luồng thanh toán cho how2claude, tôi dẫm phải ba bug kiểu này liên tiếp. Ghi lại post-mortem, và các mẫu prompt tôi dùng cho bug im lặng.

Bug 1: Dấu chấm hỏi fullwidth ẩn trong địa chỉ ví

Tôi gắn thanh toán crypto x402 vào. Local chạy ngon. Click lần đầu trên production: invalid_string at payTo xuất hiện trong console. Signing flow còn chưa kịp bắt đầu — Zod schema của facilitator đã từ chối trước.

Ví là địa chỉ 0x... 42 ký tự, nhìn mắt thường rất chuẩn. Tôi bảo Claude kiểm tra field wallet trong config/credentials/production.yml.enc:

w = Rails.application.credentials.dig(:x402, :wallet_address).to_s
puts "length: #{w.length}"
# => 43

43 ký tự. Địa chỉ EVM phải là 42. Ký tự thứ 43 là dấu chấm hỏi fullwidth tiếng Trung (U+FF1F), dính vào khi copy-paste từ input method tiếng Trung.

Lần quét đầu tiên của Claude không phát hiện bất thường — với nó đó là chuỗi bắt đầu bằng 0x và trông đúng. Nó không tự đếm độ dài. Thêm câu này vào prompt: "địa chỉ này dài hơn 1 ký tự so với dự kiến — in từng codepoint ra". 0xFF1F vừa hiện ra, xong.

Bug 2: Nút Stripe Checkout click không phản ứng

Nút Subscribe ở trang pricing — bấm, trang không chuyển. Không báo lỗi. Tab network cho thấy POST được gửi đi, Stripe trả 302 về checkout.stripe.com — rồi... không gì cả.

Tôi bảo Claude xem controller trước. Logic đúng: redirect_to session.url, allow_other_host: true. JS — không có listener liên quan.

Nhìn response mãi mới để ý: Content-Type: text/vnd.turbo-stream.html. Turbo đã bắt submit của button_to thành request Turbo Stream, mà Turbo Stream không follow 302 cross-origin — nên redirect bị nuốt, trang im lặng đứng nguyên.

Fix:

<%= button_to "Subscribe", ..., data: { turbo: false } %>

Một tháng sau, cùng bug đó lại đánh tôi ở nút Google OAuth. Interceptor ở tầng framework là đất màu của bug im lặng — Claude mặc định suy luận kiểu "request → response" tuyến tính, nó không chủ động tìm xem có tầng trung gian nào viết lại ngữ nghĩa không. Thêm vào prompt: "liệt kê mọi interceptor ở tầng framework mà cú click này đi qua — kể ra mọi middleware/tầng JS xử lý request này trong đoạn browser → server → browser".

Bug 3: Toggle Monthly/Yearly click không phản ứng

Stimulus controller cho toggle tháng/năm ở trang pricing — click nút, không đổi. Method của controller có fire (console.log xác nhận), nhưng this.monthlyTargetundefined.

Đoán đầu tiên của Claude: sai tên target. Không phải. data-pricing-target="monthly" có trong DOM.

Vấn đề là scope. data-controller="pricing" đặt trên container của nút toggle, còn hai khối grid nằm ngoài container đó. Stimulus chỉ tìm target trong subtree của phần tử controller; cái ngoài không tồn tại với nó. Đẩy data-controller lên <section> bọc cả phần đó — xong.

Bug này đúng kiểu "code đúng" — tên đều khớp, attribute đủ cả, riêng chức năng hỏng. Claude mặc định đọc code từng dòng, không chủ động hình dung cấu trúc DOM. Thêm vào prompt: "vẽ cây tổ tiên và hậu duệ của phần tử có data-controller='pricing' — đánh dấu data-pricing-target nào nằm trong subtree, cái nào nằm ngoài".

Ba mẫu prompt cho bug im lặng

Cả ba bug nhìn từ ngoài giống hệt nhau: click, không phản ứng, không báo lỗi. Claude đoán sai lần đầu cả ba, nhưng mỗi lần thêm một câu chính xác vào prompt là trúng ngay. Mẫu chung:

1. Nói với nó chênh lệch định lượng giữa kỳ vọng và thực tế — không chỉ "sai rồi"

Không phải "địa chỉ ví có vấn đề", mà là "dài hơn 1 ký tự so với dự kiến".
Không phải "nút không hoạt động", mà là "response là 302, nhưng browser không follow".
Không phải "toggle hỏng", mà là "method của controller fire, nhưng target là undefined".

Chênh lệch càng hẹp, không gian tìm kiếm của Claude càng nhỏ.

2. Chỉ nó vào những tầng không nhìn thấy — framework, browser, encoding

Bug im lặng gần như không sống trong business code. Chúng sống trong Turbo, trong scope Stimulus, trong character encoding, trong CSP, trong CORS, trong service worker. Mặc định của Claude là đọc code của bạn. Phải nói rõ cho nó đi soi những tầng đó.

3. Yêu cầu nó in trạng thái trung gian, đừng hỏi kết luận

"In từng codepoint." "Liệt kê response header." "Dump subtree DOM." Vật chất hoá trạng thái trung gian, thay vì bảo Claude suy luận tới câu trả lời. Phần "im lặng" của bug im lặng là có một bước trong chuỗi suy luận có giả định ẩn không đúng. Vật chất hoá trạng thái trung gian là cách ép giả định đó lộ ra.


Bug báo lỗi kiểm tra Claude biết gì. Bug im lặng kiểm tra chất lượng tín hiệu bạn cho nó. Bạn càng cụ thể, nó tìm càng nhanh.