debug-broken-ui

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

The app is broken and the code looks fine

应用故障但代码看起来无问题

Reading the source again will not tell you why. The evidence is in the running app, and most of it never reaches the screen: the request that failed, the store that never moved, the handler that was never bound, the element covered by something invisible.
Reticle reads all of it from inside the page. Not installed?
RETICLE_INSTALL_SOURCE=npx_skill npx @reticlehq/server@latest init
, then the
install-and-verify
skill.
反复查看源代码无法找到原因。故障证据存在于运行中的应用里,且大部分不会显示在界面上:失败的请求、从未更新的状态存储、未绑定的事件处理器、被不可见元素遮挡的组件。
Reticle 会从页面内部读取所有这些信息。还未安装?执行
RETICLE_INSTALL_SOURCE=npx_skill npx @reticlehq/server@latest init
,然后使用
install-and-verify
技能。

Do not start by guessing. Start by reproducing.

不要靠猜测开始,先复现问题

reticle_snapshot({ sessionId, mode: "interactive" })   // controls only, with refs
reticle_act_and_wait({ sessionId, ref, action: "click", until: { kind: "element", query: { testid: "..." } } })
The verdict already narrows it:
no
/
contradicted
means a channel saw something incompatible with the UI: you have the bug.
unknown
means Reticle drove it and could not tell, which is a different investigation from "it failed".
Read the act result before you call anything else. Its
summary
block already carries the whole causal window of that one click:
net {total, errors, headline}
,
consoleErrors
,
stateDiffs [{path, from, to}]
,
storageDiffs [{key, from, to}]
,
route
,
signals
,
layoutShift
,
longTasks
: real before→after diffs, not counts. The console, network, storage and state calls below are for going DEEPER into something the summary already pointed at. Calling all three straight after an act pays three model round trips (and, in an approval-gated client, three human clicks) for evidence you were already holding.
summary.stateUnwatched: true
means the state channel is dark: no subscribable store is registered, so an empty
stateDiffs
means unwatched, not unchanged, and no state-based conclusion is available until someone registers the store. It is the one field here that is about your instrumentation rather than about the app.
When the summary does point somewhere, read that channel scoped to what you just did: pass
since
from the act result, or you are reading a buffer that predates the click:
reticle_console({ sessionId, since })
reticle_network({ sessionId, since })
reticle_state({ sessionId, store })
reticle_snapshot({ sessionId, mode: "interactive" })   // controls only, with refs
reticle_act_and_wait({ sessionId, ref, action: "click", until: { kind: "element", query: { testid: "..." } } })
判定结果会缩小排查范围:
no
/
contradicted
表示某个渠道发现了与UI不一致的情况:你找到了bug。
unknown
表示Reticle执行了操作但无法判断,这与“操作失败”是不同的排查方向。
先查看操作结果再调用其他命令。 结果中的
summary
块已经包含了此次点击的完整因果链信息:
net {total, errors, headline}
、
consoleErrors
、
stateDiffs [{path, from, to}]
、
storageDiffs [{key, from, to}]
、
route
、
signals
、
layoutShift
、
longTasks
:这些是真实的前后差异数据,而非统计计数。下方的控制台、网络、状态调用命令用于深入分析
summary
已指向的问题。操作后直接调用这三个命令会产生三次模型往返请求(在需要人工审批的客户端中,还需要三次人工点击),但其实你已经持有相关证据了。
summary.stateUnwatched: true
表示状态渠道未启用:没有注册可订阅的状态存储,因此空的
stateDiffs
意味着“未被监控”而非“未变更”,在有人注册状态存储之前,无法得出基于状态的结论。这是唯一与你的埋点配置相关而非应用本身的字段。
当
summary
指向某个方向时,针对你刚执行的操作查看对应渠道的数据:传入操作结果中的
since
参数,否则你读取的是点击之前的缓存数据:
reticle_console({ sessionId, since })
reticle_network({ sessionId, since })
reticle_state({ sessionId, store })

The diagnosis table

诊断对照表

What you seeWhat it isNext
No request fired at allthe handler is not bound, or a client cache served a stale value
reticle_inspect
the element for its
file:line
; check the cache if it is TanStack Query
Request fired,
4xx
/
5xx
a real backend failure the UI swallowedthe response body is in the network entry
2xx
but nothing changed
the app never read the response: verdict says
outcome_unread
usually a real bug in the success path
Request fine, DOM fine, store staleUI-vs-state desync, invisible to any screenshot
reticle_state
is the only witness; this is the highest-value read here
Click did nothing, no errorthe control is dead, occluded, or disabled
reticle_inspect
returns
occluded
,
box
(0×0),
styles.cursor
,
opacity
Element "not found" that you can seevirtualised list has not mounted it
reticle_scroll_to
. A
query
that misses is not evidence of absence
Looks logged in, behaves logged outthe token never persisted
reticle_storage
Console error that predates your clicka pre-existing fault, not your bugcall it out before continuing
现象原因下一步操作
完全没有发送请求事件处理器未绑定,或客户端缓存返回了过时值使用
reticle_inspect
查看元素对应的
file:line
;如果是TanStack Query则检查缓存
请求已发送,返回
4xx
/
5xx
UI吞掉了真实的后端错误响应体在网络记录中
返回
2xx
但无任何变化
应用从未读取响应:判定结果显示
outcome_unread
通常是成功路径中的真实bug
请求正常,DOM正常,状态存储过时UI与状态不同步,截图无法发现
reticle_state
是唯一的证据;这是价值最高的排查点
点击无响应,无错误控件失效、被遮挡或已禁用
reticle_inspect
会返回
occluded
、
box
(0×0)、
styles.cursor
、
opacity
可见元素提示“未找到”虚拟列表尚未挂载该元素使用
reticle_scroll_to
。查询不到不代表元素不存在
显示已登录,但表现为未登录token从未持久化存储使用
reticle_storage
控制台错误早于你的点击操作预先存在的故障,与当前bug无关在继续排查前先指出该问题

Get the file, not a theory

获取具体文件位置,而非理论推测

reticle_inspect({ sessionId, ref })
Returns the component and
source: { file, line }
for the element you are looking at, plus whether it is occluded, sized, disabled or off-theme. That is the pointer to open. End the investigation with a location, not a hypothesis.
reticle_inspect({ sessionId, ref })
返回你查看的元素对应的组件及
source: { file, line }
,还会告知元素是否被遮挡、尺寸、是否禁用或偏离主题。这就是你需要打开的文件位置。以定位结果结束排查,而非停留在假设阶段。

When the flow is long

当流程较长时

Do not ping-pong act → snapshot → act → snapshot to find the failing step. Drive the journey with
reticle_act_sequence
and assert once; the first step whose consequence fails is the one to look at, and you paid a fraction of the calls to find it.
不要通过反复执行 act → snapshot → act → snapshot 来寻找失败步骤。使用
reticle_act_sequence
执行完整流程并一次性断言;第一个未达预期的步骤就是需要排查的对象,这样能大幅减少调用次数。

Before you say it is fixed

在确认修复前

Re-run the reproduction as a verdict (
reticle_act_and_wait
with the consequence named up front) rather than eyeballing the page. A fix confirmed by looking at it is the failure mode that created this ticket.
And never weaken the check to make it pass.

Troubleshooting Reticle itself (as opposed to the app):
curl https://docs.reticle.sh/troubleshooting.md
. Everything else:
curl https://docs.reticle.sh/llms.txt
. If Reticle could not see something you needed,
reticle_feedback
with
kind: "gap"
.
不要通过肉眼观察页面来确认修复,而是将复现流程作为判定依据(使用
reticle_act_and_wait
并提前指定预期结果)。靠肉眼确认修复正是导致此类工单出现的常见问题。
永远不要为了让检查通过而降低校验标准。

排查Reticle自身问题(而非应用问题):执行
curl https://docs.reticle.sh/troubleshooting.md
。其他问题:执行
curl https://docs.reticle.sh/llms.txt
。如果Reticle未能捕捉到你需要的信息,使用
reticle_feedback
并设置
kind: "gap"
。