@reticlehq/server 2.7.0 → 2.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (357) hide show
  1. package/README.md +14 -5
  2. package/SKILL.md +130 -778
  3. package/dist/bridge/bridge.d.ts +23 -0
  4. package/dist/bridge/bridge.js +80 -2
  5. package/dist/bridge/bridge.test-harness.d.ts +5 -0
  6. package/dist/bridge/bridge.test-harness.js +6 -4
  7. package/dist/capsule/causal-summary.d.ts +36 -1
  8. package/dist/capsule/causal-summary.js +33 -7
  9. package/dist/capsule/divergence.js +7 -1
  10. package/dist/cli/chromium-hint.d.ts +33 -5
  11. package/dist/cli/chromium-hint.js +82 -12
  12. package/dist/cli/cli-doctor.js +65 -34
  13. package/dist/cli/cli-flow-commands.js +8 -8
  14. package/dist/cli/cli-kill.d.ts +58 -0
  15. package/dist/cli/cli-kill.js +162 -0
  16. package/dist/cli/cli-parse.d.ts +16 -1
  17. package/dist/cli/cli-parse.js +32 -9
  18. package/dist/cli/cli-port.d.ts +32 -0
  19. package/dist/cli/cli-port.js +82 -35
  20. package/dist/cli/cli-verify.d.ts +21 -0
  21. package/dist/cli/cli-verify.js +47 -3
  22. package/dist/cli/config-discovery.d.ts +35 -0
  23. package/dist/cli/config-discovery.js +163 -0
  24. package/dist/cli/doctor-daemon-line.js +4 -1
  25. package/dist/cli/doctor-project-line.d.ts +37 -0
  26. package/dist/cli/doctor-project-line.js +35 -0
  27. package/dist/cli/doctor-rows.d.ts +51 -0
  28. package/dist/cli/doctor-rows.js +54 -0
  29. package/dist/cli/doctor-sessions-line.d.ts +12 -0
  30. package/dist/cli/doctor-sessions-line.js +69 -0
  31. package/dist/cli/drive-attach.d.ts +99 -0
  32. package/dist/cli/drive-attach.js +203 -0
  33. package/dist/cli/drive-command.d.ts +13 -0
  34. package/dist/cli/drive-command.js +103 -0
  35. package/dist/cli/open-note.d.ts +20 -0
  36. package/dist/cli/open-note.js +37 -0
  37. package/dist/cli/port-holder.d.ts +10 -0
  38. package/dist/cli/port-holder.js +26 -2
  39. package/dist/cli/status-next-action.d.ts +37 -0
  40. package/dist/cli/status-next-action.js +61 -0
  41. package/dist/cli.js +119 -39
  42. package/dist/crawl/crawl-tools.js +12 -2
  43. package/dist/crawl/crawl.d.ts +13 -0
  44. package/dist/crawl/crawl.js +50 -2
  45. package/dist/events/contradictions.d.ts +46 -0
  46. package/dist/events/contradictions.js +35 -22
  47. package/dist/events/declared.d.ts +45 -0
  48. package/dist/events/declared.js +73 -0
  49. package/dist/events/echo-mismatch.d.ts +8 -2
  50. package/dist/events/echo-mismatch.js +33 -5
  51. package/dist/events/event-filters.d.ts +1 -1
  52. package/dist/events/event-filters.js +7 -4
  53. package/dist/events/predicate-eval.d.ts +17 -72
  54. package/dist/events/predicate-eval.js +117 -236
  55. package/dist/events/predicate-parse.js +31 -3
  56. package/dist/events/predicate-route.d.ts +32 -0
  57. package/dist/events/predicate-route.js +102 -0
  58. package/dist/events/predicate-schema.d.ts +99 -0
  59. package/dist/events/predicate-schema.js +309 -0
  60. package/dist/events/predicate.d.ts +8 -0
  61. package/dist/events/predicate.js +296 -22
  62. package/dist/flows/annotate-tools.js +3 -0
  63. package/dist/flows/annotate.js +18 -4
  64. package/dist/flows/coverage.d.ts +38 -5
  65. package/dist/flows/coverage.js +30 -4
  66. package/dist/flows/decision.d.ts +15 -0
  67. package/dist/flows/decision.js +1 -1
  68. package/dist/flows/flow-classify.js +1 -1
  69. package/dist/flows/flow-replay-run.js +8 -1
  70. package/dist/flows/flow-tools.js +21 -5
  71. package/dist/flows/heal-run.js +1 -1
  72. package/dist/flows/replay.js +3 -0
  73. package/dist/honesty/accepted-write.d.ts +3 -0
  74. package/dist/honesty/accepted-write.js +8 -3
  75. package/dist/honesty/already-true.js +15 -0
  76. package/dist/honesty/blind-spots.js +10 -2
  77. package/dist/honesty/honesty.d.ts +11 -0
  78. package/dist/honesty/honesty.js +1 -0
  79. package/dist/honesty/uncaptured-bodies.d.ts +4 -2
  80. package/dist/honesty/uncaptured-bodies.js +9 -2
  81. package/dist/honesty/unread-outcome.d.ts +8 -0
  82. package/dist/honesty/unread-outcome.js +36 -9
  83. package/dist/honesty/unsettled.d.ts +49 -0
  84. package/dist/honesty/unsettled.js +82 -0
  85. package/dist/honesty/verified.d.ts +36 -3
  86. package/dist/honesty/verified.js +54 -11
  87. package/dist/http-server.d.ts +19 -0
  88. package/dist/http-server.js +202 -4
  89. package/dist/impact/impact-recorder.d.ts +23 -0
  90. package/dist/impact/impact-recorder.js +76 -0
  91. package/dist/impact/impact-store.d.ts +47 -0
  92. package/dist/impact/impact-store.js +171 -0
  93. package/dist/index.d.ts +7 -0
  94. package/dist/index.js +47 -10
  95. package/dist/init/agent-rules.d.ts +36 -0
  96. package/dist/init/agent-rules.js +132 -22
  97. package/dist/init/astro-patch.d.ts +2 -1
  98. package/dist/init/astro-patch.js +38 -6
  99. package/dist/init/capabilities.d.ts +22 -0
  100. package/dist/init/capabilities.js +76 -0
  101. package/dist/init/confirm.d.ts +114 -0
  102. package/dist/init/confirm.js +165 -0
  103. package/dist/init/connect-steps.d.ts +2 -0
  104. package/dist/init/connect-steps.js +50 -0
  105. package/dist/init/cra.js +3 -2
  106. package/dist/init/csp-check.d.ts +38 -0
  107. package/dist/init/csp-check.js +92 -0
  108. package/dist/init/csp-doctor.d.ts +25 -0
  109. package/dist/init/csp-doctor.js +49 -0
  110. package/dist/init/cursor.js +6 -9
  111. package/dist/init/detect.d.ts +8 -0
  112. package/dist/init/detect.js +33 -3
  113. package/dist/init/mcp-clients.d.ts +1 -0
  114. package/dist/init/mcp-clients.js +51 -7
  115. package/dist/init/mcp.d.ts +15 -0
  116. package/dist/init/mcp.js +19 -0
  117. package/dist/init/non-js-project.js +29 -5
  118. package/dist/init/plan-framework.d.ts +10 -0
  119. package/dist/init/plan-framework.js +78 -16
  120. package/dist/init/plan.d.ts +47 -6
  121. package/dist/init/plan.js +228 -69
  122. package/dist/init/run.d.ts +24 -2
  123. package/dist/init/run.js +157 -45
  124. package/dist/init/slash-command.d.ts +1 -1
  125. package/dist/init/slash-command.js +54 -6
  126. package/dist/init/snippets.d.ts +45 -5
  127. package/dist/init/snippets.js +194 -42
  128. package/dist/init/vite-config.js +53 -5
  129. package/dist/input/network-mock-tools.d.ts +19 -0
  130. package/dist/input/network-mock-tools.js +37 -5
  131. package/dist/input/scroll-find.d.ts +6 -0
  132. package/dist/input/scroll-find.js +17 -2
  133. package/dist/input/scroll-tools.js +4 -0
  134. package/dist/journal/attach-journal.d.ts +9 -1
  135. package/dist/journal/attach-journal.js +13 -2
  136. package/dist/journal/journal-query.d.ts +13 -0
  137. package/dist/journal/journal-query.js +28 -3
  138. package/dist/journal/workspace-gitignore.d.ts +10 -0
  139. package/dist/journal/workspace-gitignore.js +59 -0
  140. package/dist/license/license.js +1 -1
  141. package/dist/mcp/mcp-proxy.d.ts +35 -30
  142. package/dist/mcp/mcp-proxy.js +306 -104
  143. package/dist/mcp/mcp.d.ts +10 -1
  144. package/dist/mcp/mcp.js +70 -34
  145. package/dist/mcp/proxy-handshake.d.ts +82 -1
  146. package/dist/mcp/proxy-handshake.js +109 -2
  147. package/dist/mcp/proxy-lifecycle.d.ts +11 -2
  148. package/dist/mcp/proxy-lifecycle.js +12 -3
  149. package/dist/mcp/proxy-log.d.ts +63 -0
  150. package/dist/mcp/proxy-log.js +132 -0
  151. package/dist/mcp/server-instructions.d.ts +17 -0
  152. package/dist/mcp/server-instructions.js +56 -0
  153. package/dist/mcp/shared-params.d.ts +27 -0
  154. package/dist/mcp/shared-params.js +36 -0
  155. package/dist/pool/browser-pool.d.ts +22 -2
  156. package/dist/pool/browser-pool.js +44 -4
  157. package/dist/project/learned-routes.d.ts +24 -0
  158. package/dist/project/learned-routes.js +63 -0
  159. package/dist/project/project-store.d.ts +6 -0
  160. package/dist/project/project-store.js +25 -1
  161. package/dist/project/reticle-dir.d.ts +2 -0
  162. package/dist/project/reticle-dir.js +1 -0
  163. package/dist/session/connection-memory.d.ts +59 -0
  164. package/dist/session/connection-memory.js +108 -0
  165. package/dist/session/dev-command.d.ts +26 -0
  166. package/dist/session/dev-command.js +89 -0
  167. package/dist/session/dev-server-probe.d.ts +68 -0
  168. package/dist/session/dev-server-probe.js +102 -7
  169. package/dist/session/last-act.d.ts +26 -0
  170. package/dist/session/last-act.js +35 -1
  171. package/dist/session/live-control-tools.js +42 -5
  172. package/dist/session/no-session-diagnosis.d.ts +67 -2
  173. package/dist/session/no-session-diagnosis.js +236 -27
  174. package/dist/session/no-session-next-action.d.ts +46 -0
  175. package/dist/session/no-session-next-action.js +85 -0
  176. package/dist/session/no-session-watch.d.ts +40 -1
  177. package/dist/session/no-session-watch.js +174 -8
  178. package/dist/session/pending-commands.d.ts +12 -0
  179. package/dist/session/pending-commands.js +13 -1
  180. package/dist/session/ref-provenance.d.ts +54 -0
  181. package/dist/session/ref-provenance.js +61 -0
  182. package/dist/session/session-lease.js +1 -1
  183. package/dist/session/session-manager.d.ts +4 -0
  184. package/dist/session/session-manager.js +35 -5
  185. package/dist/session/session.d.ts +10 -1
  186. package/dist/session/session.js +146 -5
  187. package/dist/telemetry/app-instrumented.d.ts +8 -0
  188. package/dist/telemetry/app-instrumented.js +10 -0
  189. package/dist/telemetry/automation-hint.d.ts +17 -0
  190. package/dist/telemetry/automation-hint.js +71 -0
  191. package/dist/telemetry/bug-found.d.ts +15 -17
  192. package/dist/telemetry/bug-found.js +52 -21
  193. package/dist/telemetry/cli-telemetry.js +7 -1
  194. package/dist/telemetry/daemon-telemetry.js +24 -0
  195. package/dist/telemetry/feedback.js +1 -1
  196. package/dist/telemetry/init-telemetry.js +9 -1
  197. package/dist/telemetry/install-source.d.ts +56 -0
  198. package/dist/telemetry/install-source.js +74 -0
  199. package/dist/telemetry/instrumentation-stall.d.ts +50 -0
  200. package/dist/telemetry/instrumentation-stall.js +90 -0
  201. package/dist/telemetry/run-telemetry.js +4 -1
  202. package/dist/telemetry/session-metrics.d.ts +8 -0
  203. package/dist/telemetry/session-metrics.js +15 -0
  204. package/dist/telemetry/telemetry.d.ts +7 -1
  205. package/dist/telemetry/telemetry.js +26 -2
  206. package/dist/telemetry/tool-refused.d.ts +19 -0
  207. package/dist/telemetry/tool-refused.js +55 -0
  208. package/dist/telemetry/verification-of.js +46 -7
  209. package/dist/temp-dir.d.ts +27 -0
  210. package/dist/temp-dir.js +46 -0
  211. package/dist/tools/act-tools.js +209 -28
  212. package/dist/tools/assert-grade.d.ts +1 -1
  213. package/dist/tools/assert-grade.js +27 -5
  214. package/dist/tools/assert-verdict.js +49 -7
  215. package/dist/tools/browser-tools.js +10 -0
  216. package/dist/tools/contract-tools.js +4 -0
  217. package/dist/tools/dynamic-tools.js +16 -7
  218. package/dist/tools/error-recovery.d.ts +10 -0
  219. package/dist/tools/error-recovery.js +73 -9
  220. package/dist/tools/feedback-invite.js +1 -1
  221. package/dist/tools/invoke-tool.js +159 -26
  222. package/dist/tools/lease-hint.d.ts +30 -8
  223. package/dist/tools/lease-hint.js +65 -14
  224. package/dist/tools/lease-tools.d.ts +40 -1
  225. package/dist/tools/lease-tools.js +181 -83
  226. package/dist/tools/navigate-result.js +1 -1
  227. package/dist/tools/observe-tools.js +10 -4
  228. package/dist/tools/observed-nothing.js +1 -1
  229. package/dist/tools/react-grace.d.ts +52 -0
  230. package/dist/tools/react-grace.js +90 -0
  231. package/dist/tools/read-tools.js +67 -5
  232. package/dist/tools/resolve-target.d.ts +33 -0
  233. package/dist/tools/resolve-target.js +53 -0
  234. package/dist/tools/settle-in-flight.d.ts +94 -0
  235. package/dist/tools/settle-in-flight.js +146 -0
  236. package/dist/tools/snapshot-delta.d.ts +10 -0
  237. package/dist/tools/snapshot-delta.js +194 -9
  238. package/dist/tools/tool-kit.d.ts +8 -0
  239. package/dist/tools/tool-surface.d.ts +60 -0
  240. package/dist/tools/tool-surface.js +86 -1
  241. package/dist/tools/tools.js +61 -8
  242. package/dist/tools/unadvertised-help.js +9 -6
  243. package/docs/README.md +55 -11
  244. package/docs/actions.mdx +120 -0
  245. package/docs/agent-cheatsheet.md +95 -29
  246. package/docs/architecture.md +52 -35
  247. package/docs/benchmarks.md +127 -75
  248. package/docs/best-practices.mdx +216 -0
  249. package/docs/capabilities.mdx +534 -0
  250. package/docs/cli/affected.mdx +53 -0
  251. package/docs/cli/capsules.mdx +70 -0
  252. package/docs/cli/cloud.mdx +153 -0
  253. package/docs/cli/doctor.mdx +109 -0
  254. package/docs/cli/drive.mdx +74 -0
  255. package/docs/cli/feedback.mdx +67 -0
  256. package/docs/cli/gate.mdx +64 -0
  257. package/docs/cli/help.mdx +94 -0
  258. package/docs/cli/hunt.mdx +73 -0
  259. package/docs/cli/identify.mdx +76 -0
  260. package/docs/cli/init.mdx +121 -0
  261. package/docs/cli/kill.mdx +91 -0
  262. package/docs/cli/license.mdx +48 -0
  263. package/docs/cli/mcp.mdx +61 -0
  264. package/docs/cli/open.mdx +74 -0
  265. package/docs/cli/restart.mdx +60 -0
  266. package/docs/cli/rollback.mdx +35 -0
  267. package/docs/cli/serve.mdx +69 -0
  268. package/docs/cli/status.mdx +88 -0
  269. package/docs/cli/stop.mdx +64 -0
  270. package/docs/cli/telemetry.mdx +57 -0
  271. package/docs/cli/update.mdx +52 -0
  272. package/docs/cli/verify.mdx +76 -0
  273. package/docs/cli/version.mdx +39 -0
  274. package/docs/cli/watch.mdx +62 -0
  275. package/docs/cli.mdx +241 -0
  276. package/docs/debugging.md +16 -10
  277. package/docs/deploy-checks.md +45 -12
  278. package/docs/desktop.mdx +384 -0
  279. package/docs/docs.json +364 -0
  280. package/docs/enterprise.md +22 -16
  281. package/docs/faq.mdx +102 -0
  282. package/docs/fixtures.md +13 -7
  283. package/docs/flows.md +29 -23
  284. package/docs/for-agents.md +71 -0
  285. package/docs/frameworks.mdx +238 -0
  286. package/docs/gate-plan.md +48 -42
  287. package/docs/gates.md +28 -22
  288. package/docs/getting-started.md +129 -77
  289. package/docs/human-control.md +24 -18
  290. package/docs/index.mdx +143 -0
  291. package/docs/install-agentic.mdx +224 -0
  292. package/docs/install-manual.mdx +196 -0
  293. package/docs/instrumentation.mdx +238 -0
  294. package/docs/integration-patterns.md +41 -35
  295. package/docs/local-registry.md +19 -13
  296. package/docs/multi-agent-testing.md +19 -13
  297. package/docs/packages/babel-plugin.mdx +79 -0
  298. package/docs/packages/browser.mdx +111 -0
  299. package/docs/packages/core.mdx +116 -0
  300. package/docs/packages/electron.mdx +85 -0
  301. package/docs/packages/eslint-plugin.mdx +86 -0
  302. package/docs/packages/next.mdx +89 -0
  303. package/docs/packages/react.mdx +91 -0
  304. package/docs/packages/server.mdx +98 -0
  305. package/docs/packages/tauri.mdx +63 -0
  306. package/docs/packages/test.mdx +119 -0
  307. package/docs/packages/vite-plugin.mdx +94 -0
  308. package/docs/packages.mdx +218 -0
  309. package/docs/platform-integration.md +35 -26
  310. package/docs/predicates.mdx +248 -0
  311. package/docs/quickstart.mdx +257 -0
  312. package/docs/recipes.mdx +282 -0
  313. package/docs/skill-file.mdx +107 -0
  314. package/docs/state-management.mdx +199 -0
  315. package/docs/style.css +14 -0
  316. package/docs/system-map.md +50 -33
  317. package/docs/telemetry-contract.md +106 -52
  318. package/docs/telemetry-events.mdx +475 -0
  319. package/docs/telemetry.md +63 -55
  320. package/docs/testing.md +44 -6
  321. package/docs/token-efficiency.md +37 -27
  322. package/docs/tools-act-and-wait.mdx +173 -0
  323. package/docs/tools-act-sequence.mdx +137 -0
  324. package/docs/tools-act.mdx +95 -0
  325. package/docs/tools-assert.mdx +129 -0
  326. package/docs/tools-console.mdx +63 -0
  327. package/docs/tools-inspect.mdx +88 -0
  328. package/docs/tools-navigate.mdx +99 -0
  329. package/docs/tools-network.mdx +119 -0
  330. package/docs/tools-observe.mdx +145 -0
  331. package/docs/tools-overview.mdx +160 -0
  332. package/docs/tools-query.mdx +126 -0
  333. package/docs/tools-session-and-feedback.mdx +112 -0
  334. package/docs/tools-sessions.mdx +94 -0
  335. package/docs/tools-snapshot.mdx +129 -0
  336. package/docs/tools-state.mdx +104 -0
  337. package/docs/tools-tools-and-run.mdx +156 -0
  338. package/docs/tools-wait-for.mdx +85 -0
  339. package/docs/troubleshooting.mdx +263 -0
  340. package/docs/usage.md +209 -165
  341. package/docs/vs-chrome-devtools-mcp.mdx +123 -0
  342. package/docs/vs-playwright-mcp.mdx +125 -0
  343. package/docs/vs-screenshots.mdx +137 -0
  344. package/docs/what-is-reticle.mdx +63 -0
  345. package/docs/why-reticle.mdx +109 -0
  346. package/package.json +5 -5
  347. package/docs/desktop-apps.md +0 -256
  348. package/docs/matrix/2.5.0/claude-code-machine.json +0 -19
  349. package/docs/matrix/2.5.0/codex-machine.json +0 -20
  350. package/docs/matrix/2.5.0/cursor-machine.json +0 -22
  351. package/docs/matrix/2.5.0/gemini-machine.json +0 -22
  352. package/docs/matrix/2.5.0/opencode-machine.json +0 -22
  353. package/docs/matrix/2.5.0/vscode-machine.json +0 -22
  354. package/docs/matrix/2.5.0/windsurf-machine.json +0 -22
  355. package/docs/matrix/MATRIX.md +0 -26
  356. package/docs/matrix/README.md +0 -59
  357. package/docs/mint.json +0 -61
package/docs/README.md CHANGED
@@ -1,31 +1,72 @@
1
1
  # Docs index
2
2
 
3
- Two audiences share this directory, which is why it looked like twenty-five unsorted files. Every page states its own audience in the first blockquote; this index is the shortcut.
3
+ Two audiences share this directory: people using Reticle and people working on it. Every page carries a `description` in its frontmatter saying what it answers; this index is the shortcut when you want to scan all of them at once.
4
4
 
5
- **User docs are published** to [reticle.sh](https://reticle.sh) via `mint.json`. **Contributor docs are not** — they are for people working _on_ Reticle, and they live here because they belong next to what they describe.
5
+ Everything here is published to [docs.reticle.sh](https://docs.reticle.sh) by `docs.json`, which is the Mintlify config for this directory. Five tabs. **Guides** to learn in order, grouped by what you are doing (get started, install, instrument, verify and ship, work with agents, and when it goes wrong). **Reference** to look one thing up. **Desktop** because Electron and Tauri are a different shape of problem and deserve their own front door, **Resources** to decide or to check a claim, and **Contributing** for people working on Reticle. Contributor docs sit in a tab rather than hidden, since they were reachable by URL anyway, and an unlisted page is a page nobody finds.
6
6
 
7
7
  ---
8
8
 
9
9
  ## For people using Reticle
10
10
 
11
- **Start:** [getting-started.md](getting-started.md) → [usage.md](usage.md) (the full tool reference).
11
+ **Start:** [quickstart.mdx](quickstart.mdx) → [usage.md](usage.md) (the full tool reference).
12
12
 
13
13
  | Page | What it answers |
14
14
  | --- | --- |
15
+ | [quickstart.mdx](quickstart.mdx) | five minutes to a real verdict; every response on it was captured live |
16
+ | [what-is-reticle.mdx](what-is-reticle.mdx) | the definition, what Reticle is NOT, and what it needs to run |
17
+ | [why-reticle.mdx](why-reticle.mdx) | the false-green problem, the measured case, and where we lose |
18
+ | [install-agentic.mdx](install-agentic.mdx) | what `npx @reticlehq/server init` writes, and how to read its marks |
19
+ | [install-manual.mdx](install-manual.mdx) | wiring the MCP server and SDK by hand, per agent and framework |
15
20
  | [getting-started.md](getting-started.md) | install it, connect an agent, verify something |
16
- | [usage.md](usage.md) | every tool, every argument — the reference |
21
+ | [usage.md](usage.md) | the long-form narrative; most of it now has a focused page, and it says so at the top |
22
+ | [predicates.mdx](predicates.mdx) | the predicate grammar, and which kinds actually prove something |
23
+ | [capabilities.mdx](capabilities.mdx) | coverage, storage, fake clocks, crawl, visual diff, network mocking |
24
+ | [recipes.mdx](recipes.mdx) | worked examples for real situations, with the response each returned |
25
+ | [actions.mdx](actions.mdx) | every action and its arguments, including `press` and its history |
26
+ | [faq.mdx](faq.mdx) | production, frameworks, comparisons, and the honest limits |
27
+ | [tools-overview.mdx](tools-overview.mdx) | the 18 advertised tools, the 30 in the cold tail, and why |
28
+ | [tools-snapshot.mdx](tools-snapshot.mdx) | `reticle_snapshot`: three modes, from full tree to a 25-token route check |
29
+ | [tools-query.mdx](tools-query.mdx) | `reticle_query` finds elements by testing-library semantics |
30
+ | [tools-inspect.mdx](tools-inspect.mdx) | `reticle_inspect`: one element, down to source and design tokens |
31
+ | [tools-navigate.mdx](tools-navigate.mdx) | `reticle_navigate`, and why `ok` does not mean the page arrived |
32
+ | [tools-act.mdx](tools-act.mdx) | `reticle_act` acts, proves nothing, and says so |
33
+ | [tools-act-and-wait.mdx](tools-act-and-wait.mdx) | `reticle_act_and_wait`, the tool that produces a verdict |
34
+ | [tools-act-sequence.mdx](tools-act-sequence.mdx) | `reticle_act_sequence`: batch a form into one round trip |
35
+ | [tools-observe.mdx](tools-observe.mdx) | `reticle_observe`, the whole timeline when you don't know what broke |
36
+ | [tools-network.mdx](tools-network.mdx) | `reticle_network`: the request log, redaction, and buffer honesty |
37
+ | [tools-console.mdx](tools-console.mdx) | `reticle_console`, and an empty result that proves it looked |
38
+ | [tools-state.mdx](tools-state.mdx) | `reticle_state`: what the app believes, not what it drew |
39
+ | [tools-wait-for.mdx](tools-wait-for.mdx) | `reticle_wait_for`, for consequences you did not cause |
40
+ | [tools-assert.mdx](tools-assert.mdx) | `reticle_assert`: verdicts, and a real failure explained |
41
+ | [tools-sessions.mdx](tools-sessions.mdx) | `reticle_sessions`: the health fields that decide if driving works |
42
+ | [tools-tools-and-run.mdx](tools-tools-and-run.mdx) | `reticle_tools` / `reticle_run`, reaching the other 30 |
43
+ | [tools-session-and-feedback.mdx](tools-session-and-feedback.mdx) | `reticle_session` / `reticle_feedback`, the human boundary |
44
+ | [packages.mdx](packages.mdx) | every library at a glance: what it does, why it exists, when you need it |
45
+ | [packages/](packages/) | one page per published package, with its real exported API and dependencies |
46
+ | [best-practices.mdx](best-practices.mdx) | the habits that make a verdict worth trusting |
47
+ | [skill-file.mdx](skill-file.mdx) | the paste-one-URL skill that teaches an agent Reticle |
17
48
  | [agent-cheatsheet.md](agent-cheatsheet.md) | the condensed version an agent keeps in context |
49
+ | [for-agents.md](for-agents.md) | how to fetch these docs as Markdown or `llms.txt` |
18
50
  | [architecture.md](architecture.md) | how it works, and why it is built this way |
19
51
  | [platform-integration.md](platform-integration.md) | Vite, Next, Remix, Astro, plain HTML |
20
- | [desktop-apps.md](desktop-apps.md) | Electron and Tauri |
52
+ | [desktop.mdx](desktop.mdx) | Electron and Tauri in full: setup, IPC, screenshots, headless, troubleshooting |
53
+ | [frameworks.mdx](frameworks.mdx) | what is supported, what is wired-but-unverified, and the wiring |
54
+ | [state-management.mdx](state-management.mdx) | zustand, Redux, and eight adapters |
55
+ | [instrumentation.mdx](instrumentation.mdx) | stores, signals, testids, and how verdicts get stronger |
56
+ | [troubleshooting.mdx](troubleshooting.mdx) | nothing connected, click did nothing, verdict says unknown |
57
+ | [cli.mdx](cli.mdx) | the CLI at a glance, with real output and measured exit codes |
58
+ | [cli/](cli/) | one page per command: every flag, what it prints, and its exit codes |
21
59
  | [integration-patterns.md](integration-patterns.md) | wiring it into a real codebase |
22
60
  | [flows.md](flows.md) | record → save → replay → heal |
23
- | [testing.md](testing.md) | `@reticlehq/test` — turning a session into a CI suite |
61
+ | [testing.md](testing.md) | `@reticlehq/test`, turning a session into a CI suite |
24
62
  | [multi-agent-testing.md](multi-agent-testing.md) | more than one agent on one app |
25
63
  | [human-control.md](human-control.md) | taking the wheel back from the agent |
26
64
  | [deploy-checks.md](deploy-checks.md) | running Reticle against a deployed build |
27
65
  | [token-efficiency.md](token-efficiency.md) | why it costs less than a screenshot loop |
28
66
  | [benchmarks.md](benchmarks.md) | how the numbers were measured, including where Reticle loses |
67
+ | [vs-playwright-mcp.mdx](vs-playwright-mcp.mdx) | outside-in vs inside-out, and when Playwright is right |
68
+ | [vs-chrome-devtools-mcp.mdx](vs-chrome-devtools-mcp.mdx) | cheaper per look, catches less: the trade, measured |
69
+ | [vs-screenshots.mdx](vs-screenshots.mdx) | why a better vision model does not fix a non-visual bug |
29
70
  | [telemetry.md](telemetry.md) | what is collected, and how to turn it off |
30
71
  | [local-registry.md](local-registry.md) | installing an unpublished build |
31
72
  | [enterprise.md](enterprise.md) | the licensed surface |
@@ -36,20 +77,23 @@ Two audiences share this directory, which is why it looked like twenty-five unso
36
77
 
37
78
  | Page | What it answers |
38
79
  | --- | --- |
39
- | [gates.md](gates.md) | **I changed some files — which gate do I run?** |
80
+ | [gates.md](gates.md) | **I changed some files. Which gate do I run?** |
40
81
  | [gate-plan.md](gate-plan.md) | why the gates are shaped this way, and what is still unbuilt |
41
82
  | [system-map.md](system-map.md) | how a tool call reaches the app, and which failures are silent |
42
83
  | [telemetry-contract.md](telemetry-contract.md) | required reading before touching anything that emits |
43
- | [debugging.md](debugging.md) | debugging Reticle itself — the four signals and what each answers |
84
+ | [telemetry-events.mdx](telemetry-events.mdx) | every event kind and its payload, and the question each one exists to answer |
85
+ | [debugging.md](debugging.md) | debugging Reticle itself: the four signals and what each answers |
44
86
  | [fixtures.md](fixtures.md) | the sibling `reticle-fixtures` repo: install complexity, not regressions |
45
- | [matrix/README.md](matrix/README.md) | submitting an MCP-client compatibility record — the best first contribution |
87
+ | [matrix/README.md](matrix/README.md) | submitting an MCP-client compatibility record, the best first contribution |
46
88
  | [matrix/MATRIX.md](matrix/MATRIX.md) | which clients are known to work (generated; do not hand-edit) |
47
89
  | [`../apps/README.md`](../apps/README.md) | what belongs in `apps/`, and what does not |
48
90
  | [`../apps/e2e/harness-rules.md`](../apps/e2e/harness-rules.md) | the four rules every gate obeys, and the incident behind each |
49
91
  | [`../bench/README.md`](../bench/README.md) | what in `bench/` is live and what is a kept one-off study |
50
92
 
51
- `fixtures-dispatch-receiver.yml` is not a doc — it is the workflow template `reticle-fixtures` needs, kept in this repo so the two ends of the contract cannot drift apart.
93
+ `fixtures-dispatch-receiver.yml` is not a doc; it is the workflow template `reticle-fixtures` needs, kept in this repo so the two ends of the contract cannot drift apart.
52
94
 
53
95
  ---
54
96
 
55
- **Adding a page?** Put it in the right table above. If it is user-facing, add it to `mint.json` too — a page that is published but unlisted is a page nobody finds, and a page listed here but absent from `mint.json` is one users cannot reach at all.
97
+ **Adding a page?** Three things, all required. Give it `title` + `description` frontmatter and no `# H1`. Mintlify renders the frontmatter title as the heading, so an H1 shows up twice. Add its slug to the right tab in `docs.json`. Add a row to the right table above. A page missing from `docs.json` is one nobody can reach; a page missing from this index is one no contributor knows exists.
98
+
99
+ **Adding a second product?** Do not nest these files under a product folder pre-emptively; that renames every live URL to buy nothing. Mintlify supports `navigation.products`, where each product declares its own `directory`. When the second product exists, put its pages in `docs/<product>/`, switch `docs.json` from `tabs` to `products`, and give this product `"directory": ""` so today's URLs keep working.
@@ -0,0 +1,120 @@
1
+ ---
2
+ title: 'Action reference: lights, camera, click'
3
+ sidebarTitle: Actions
4
+ description: Every action reticle_act and reticle_act_and_wait can perform, with its arguments and the one that has a history.
5
+ icon: hand-pointer
6
+ ---
7
+
8
+ Every action Reticle can perform takes the same shape: a `ref` from [`reticle_snapshot`](/tools-snapshot) or [`reticle_query`](/tools-query), an `action` name, and an optional `args` object. The same seventeen actions are available on [`reticle_act`](/tools-act), which proves nothing, and on [`reticle_act_and_wait`](/tools-act-and-wait), which returns a verdict.
9
+
10
+ ```json
11
+ { "ref": "e104", "action": "fill", "args": { "value": "reticle" } }
12
+ ```
13
+
14
+ | Action | Arguments | Notes |
15
+ | --- | --- | --- |
16
+ | `click` / `dblclick` | none | Dispatches a real click |
17
+ | `hover` | none | `mouseover` + `mouseenter`, so JS hover state triggers |
18
+ | `focus` / `blur` | none | |
19
+ | `fill` | `{ value }` | Sets value via a React-safe native setter, then `input` and `change` |
20
+ | `type` | `{ text }` | **Appends** to the current value |
21
+ | `clear` | none | Empties an input |
22
+ | `select` | `{ value }` | For `<select>` |
23
+ | `check` / `uncheck` | none | Checkbox and radio |
24
+ | `submit` | none | Submits the element's `<form>` |
25
+ | `press` | `{ text }` | The key **name**: `Escape`, `Tab`, `Enter` |
26
+ | `scrollIntoView` | none | |
27
+ | `upload` | `{ content?, name?, type? }` | Sets a file on `<input type="file">`. All three default, to `"reticle test file"`, `"file.txt"` and `"text/plain"` |
28
+ | `drag` | `{ toRef }` | Pointer-based drag (dnd-kit, react-beautiful-dnd) plus HTML5 DnD. `target` is an alias |
29
+ | `webmcp` | `{ tool, params }` | Calls a `navigator.modelContext` tool, if the site exposes one |
30
+
31
+ ### Arguments that are not actions
32
+
33
+ Three `args` keys apply across actions rather than belonging to one:
34
+
35
+ | Key | What it does |
36
+ | --- | --- |
37
+ | `native` | Forces a trusted native click instead of the occlusion-honest synthetic path. For file pickers and clipboard access |
38
+ | `holdMs` | Keeps the pointer **down** for that long between mousedown and mouseup. The only way to drive a hold-to-confirm control. `effect.heldMs` reports the hold actually achieved |
39
+ | `confirmDangerous` | A permission gate for a control Reticle judged destructive. It is **not** a duration |
40
+
41
+ <Warning>
42
+ `confirmDangerous` and `holdMs` are routinely confused because both show up around delete buttons.
43
+ `holdMs` is how long the button is held; `confirmDangerous` is permission to press it at all.
44
+ Passing the wrong one gets you either a refusal or a press that was never held.
45
+ </Warning>
46
+
47
+ ### The refusal you will meet first
48
+
49
+ Reticle blocks a control that looks destructive before acting on it:
50
+
51
+ ```json
52
+ {
53
+ "error": "potentially destructive action blocked; retry with args.confirmDangerous=true",
54
+ "recovery": "Reticle blocked a potentially destructive control (delete, remove, revoke…) on purpose. If you mean it, retry the same action with args.confirmDangerous set to true. This is a deliberate refusal, not a defect: there is nothing to report."
55
+ }
56
+ ```
57
+
58
+ A stale ref is refused the same way, with `"ref 'e303' no longer resolves to an element"` and a recovery note telling you to re-query. Both refusals cost a turn and prevent a false green, which is the trade.
59
+
60
+ ## `fill` versus `type`
61
+
62
+ `fill` replaces. `type` appends. Reaching for `type` when you meant `fill` is how a field ends up containing `hellohello`, and neither call will complain.
63
+
64
+ ## `press` takes `text`, not `key`
65
+
66
+ Both are accepted today, and `text` wins when both are present. Use `text`. It is what the tool description documents.
67
+
68
+ <Warning>
69
+ This one has history worth knowing. The implementation used to read `args.key` and **default to
70
+ `Enter`**, so the documented call, `{ action: "press", args: { text: "Escape" } }`, silently sent
71
+ `Enter` and reported success.
72
+
73
+ Three consequences, worst last: the requested key never arrived, so Escape-to-close and Tab-traversal went unverified while looking verified; nothing in the result said the argument had been ignored; and `Enter` is not a neutral substitute. On a focused field inside a form it **submits it**. A request to close a dialog could file the form behind it.
74
+
75
+ Two field reports were exactly this, both diagnosed as "synthetic events don't reach the app". Wrong root cause: the event reached the app perfectly well and simply said `Enter`.
76
+
77
+ </Warning>
78
+
79
+ That bug is fixed, and the reason it is written down here is that it is the exact shape of failure Reticle exists to catch. An action that reports success while doing something else.
80
+
81
+ ## Batching
82
+
83
+ Several actions in one round trip:
84
+
85
+ ```json
86
+ {
87
+ "steps": [
88
+ { "ref": "e603", "action": "fill", "args": { "value": "admin@reticle.dev" } },
89
+ { "ref": "e604", "action": "fill", "args": { "value": "hunter2" } },
90
+ { "ref": "e605", "action": "click" }
91
+ ]
92
+ }
93
+ ```
94
+
95
+ <Warning>
96
+ [`reticle_act_sequence`](/tools-act-sequence) returns `ok: true` when the steps were
97
+ **dispatched**, not when they worked. Batch the setup, then prove the outcome with
98
+ [`reticle_act_and_wait`](/tools-act-and-wait) on the final step.
99
+ </Warning>
100
+
101
+ ## Real versus synthetic input
102
+
103
+ `inputMode` in the response says which you got. `synthetic` means events dispatched programmatically. Correct for the overwhelming majority of apps. `real` means native CDP input, available under `reticle drive`, and needed by a few drag-and-drop and pointer-gesture libraries.
104
+
105
+ <Warning>
106
+ On a throttled tab, synthetic timers and pointer gestures can silently no-op. Pass
107
+ `refuseWhenThrottled: true` to fail loudly instead of acting into a tab the browser has paused.
108
+ </Warning>
109
+
110
+ ## What every action reports
111
+
112
+ `dispatched`, `targetMatched`, `visible`, `enabled`, `valueChanged`, `focusMoved`, `domMutatedWithin`, `defaultPrevented`, `occluded`, `occludedBy` and `scrolledIntoView`. Plus the element's `testid`, `component`, `role`, `name` and `source` file and line.
113
+
114
+ Fields sitting at their uninformative default are **omitted**, so a clean action collapses to its consequence. A missing `visible` means `true`, not `false`. Read an absence as "nothing to report" rather than as a failure.
115
+
116
+ `occluded` is the answer to a large share of "the button doesn't work" reports: the click landed on the modal overlay sitting on top of it.
117
+
118
+ <Card title="Full field-by-field walkthrough" icon="hand-pointer" href="/tools-act">
119
+ What each effect field tells you, with a real response.
120
+ </Card>
@@ -1,29 +1,35 @@
1
- # Reticle — agent cheat-sheet
1
+ ---
2
+ title: Agent cheat sheet
3
+ description: 'One screen to get fluent: the look → act → observe → assert loop, with the exact calls to make.'
4
+ icon: bolt
5
+ ---
2
6
 
3
- One screen to get fluent. Reticle is the **proof layer for AI agents** — no screenshots, no vision model, evidence not prose. Everything below returns structured data. Full guide: [usage.md](usage.md).
7
+ The loop is **look → act → observe → assert**, and only `reticle_act_and_wait` and `reticle_assert` produce a verdict. Reach for `reticle_act_and_wait({ ref, action, until })` first: it names the expected consequence before the action, which is the difference between a check and a rationalisation. `verified: "unknown"` is not a pass.
8
+
9
+ One screen to get fluent. Reticle is the **proof layer for AI agents**: no screenshots, no vision model, evidence not prose. Everything below returns structured data. Full guide: [usage.md](usage.md).
4
10
 
5
11
  ## The core loop: look → act → observe → assert
6
12
 
7
13
  | Verb | Tool | One-liner |
8
14
  | --- | --- | --- |
9
15
  | **look** | `reticle_snapshot` / `reticle_query` | See the page (semantic tree) / find one specific element. |
10
- | **act** | **`reticle_act_and_wait`** / `reticle_act_sequence` / `reticle_act` | **Act + name the consequence, one hop — reach for this first.** / batch a whole journey in one hop / move the app and prove nothing. |
16
+ | **act** | **`reticle_act_and_wait`** / `reticle_act_sequence` / `reticle_act` | **Act + name the consequence, one hop. Reach for this first.** / batch a whole journey in one hop / move the app and prove nothing. |
11
17
  | **observe** | `reticle_observe` / `reticle_wait_for` | Everything the app did after `since` / block until true. |
12
18
  | **assert** | `reticle_assert` | Evaluate a predicate → `{ pass, evidence, failureReason? }`. The end of every loop. |
13
19
 
14
- > **Only `reticle_act_and_wait` and `reticle_assert` produce a verdict.** Everything else moves or reads the app and proves nothing, so a drive ending without one of those two has no result however many tools it used. `reticle_act` is the tool agents reach for by habit and it catches nothing; `act_and_wait` is where defects actually surface. And `verified: "unknown"` is not a pass — it means Reticle drove the app and could not tell what happened. Report it as unknown.
20
+ > **Only `reticle_act_and_wait` and `reticle_assert` produce a verdict.** Everything else moves or reads the app and proves nothing, so a drive ending without one of those two has no result however many tools it used. `reticle_act` is the tool agents reach for by habit and it catches nothing; `act_and_wait` is where defects actually surface. And `verified: "unknown"` is not a pass: it means Reticle drove the app and could not tell what happened. Report it as unknown.
15
21
 
16
- `reticle_act` returns a `since` cursor — pass it to `reticle_observe({ since })` to scope the window. Elements are addressed by stable refs (`e7`) from `snapshot`/`query`; they re-resolve across re-renders.
22
+ `reticle_act` returns a `since` cursor; pass it to `reticle_observe({ since })` to scope the window. Elements are addressed by stable refs (`e7`) from `snapshot`/`query`; they re-resolve across re-renders.
17
23
 
18
- **`assert`/`wait_for` are auto-scoped to your last act.** By default they only count events buffered _since_ the most recent act, so a stale signal from a previous step can't fake a pass — pass an explicit `since` to override. **Clicks run the code, not pixels:** `reticle_act` click fires the full pointer sequence on the element (no coordinate gesture for the HUD to intercept), reports `occluded:true` when something covers the target, and stays synthetic even with CDP configured (use `args:{ native:true }` for a trusted native click).
24
+ **`assert`/`wait_for` are auto-scoped to your last act.** By default they only count events buffered _since_ the most recent act, so a stale signal from a previous step can't fake a pass. Pass an explicit `since` to override. **Clicks run the code, not pixels:** `reticle_act` click fires the full pointer sequence on the element (no coordinate gesture for the HUD to intercept), reports `occluded:true` when something covers the target, and stays synthetic even with CDP configured (use `args:{ native:true }` for a trusted native click).
19
25
 
20
- **Never sleep — wait deterministically.** Fixed sleeps are the #1 cause of flaky agent tests. Instead:
26
+ **Never sleep. Wait deterministically.** Fixed sleeps are the #1 cause of flaky agent tests. Instead:
21
27
 
22
- - `reticle_act_and_wait({ ref, action })` with **no `until`** waits for the page to _settle_ (network + structural DOM idle; ambient count-up/spinner churn is ignored so an animated page still settles) before returning — the one-call replacement for "click then sleep 500ms".
28
+ - `reticle_act_and_wait({ ref, action })` with **no `until`** waits for the page to _settle_ (network + structural DOM idle; ambient count-up/spinner churn is ignored so an animated page still settles) before returning: the one-call replacement for "click then sleep 500ms".
23
29
  - Need to wait without acting? `reticle_wait_for({ predicate: { kind: "settled", quietMs } })`.
24
30
  - Waiting for a specific outcome? Pass that consequence as the predicate (`{ signal }` / `{ net }`), or `allOf` it with `{ kind: "settled" }` to wait for both the event _and_ the page going quiet.
25
31
 
26
- **A predicate that does not parse produces NO verdict — not a failing one.** Nothing runs, so the drive ends with no result at all, which is strictly worse than a failure. The shapes below are the ones agents reach for most; the first column now works, but knowing the second saves you the round trip:
32
+ **A predicate that does not parse produces NO verdict, not a failing one.** Nothing runs, so the drive ends with no result at all, which is strictly worse than a failure. The shapes below are the ones agents reach for most; the first column now works, but knowing the second saves you the round trip:
27
33
 
28
34
  | You may write | It means |
29
35
  | --- | --- |
@@ -31,11 +37,54 @@ One screen to get fluent. Reticle is the **proof layer for AI agents** — no sc
31
37
  | `{ kind: "element", role: "button", text: "Save" }` | `{ kind: "element", query: { role, text } }` |
32
38
  | `{ kind: "route", url: "/checkout" }` | `{ kind: "route", contains: "/checkout" }` |
33
39
 
34
- If a predicate is still rejected, the error names the fields **that kind** accepts — read it rather than guessing again, and note `state` spells its selector `path` while `route` spells its `pathname`.
40
+ **New in 2.8.0, and it changes what some existing assertions mean.** An element predicate now CHECKS `value` and `text` instead of quietly folding them into the locator and ignoring them.
41
+
42
+ ```jsonc
43
+ // Now a real assertion about the field's contents. Before 2.8.0 this passed
44
+ // whatever the input held, because `value` was read as a locator operand and,
45
+ // with no `by`, silently did nothing.
46
+ { "kind": "element", "role": "textbox", "name": "GST amount", "value": "274.58" }
47
+ ```
48
+
49
+ Two consequences worth knowing before you write your next predicate:
50
+
51
+ - **You can now assert what a field contains.** That was not possible before; agents worked around it by reading the value out of band with `reticle_query` and comparing in prose, which produces no verdict and therefore does not count as verification.
52
+ - **`{ role, text }` now checks the text.** It used to match on role alone, so it matched every button on the page. If an assertion you have used for months starts failing, that is the likely reason, and the failure is the truth arriving late.
53
+
54
+ Fields that nothing can check are refused rather than ignored: `by` without a `value`, and `label`, `placeholder`, `testid`, `alt` or `component` when a higher-precedence field already selected the element. An element query is a first-match dispatch, not a conjunction.
55
+
56
+ **Combinators take `predicates`, not a bare array.** This is the shape most often got wrong, and it is the one that produces no verdict at all:
57
+
58
+ ```jsonc
59
+ // WRONG: neither of these parses
60
+ { "allOf": [ … ] }
61
+ { "kind": "allOf", "allOf": [ … ] }
62
+
63
+ // RIGHT
64
+ { "kind": "allOf", "predicates": [ … ] }
65
+ { "kind": "anyOf", "predicates": [ … ] }
66
+ { "kind": "not", "predicate": { … } }
67
+ ```
68
+
69
+ Each child reports its own evidence, in order:
35
70
 
36
- **Assert a consequence, not just presence.** `{ signal }` / `{ net }` prove the feature actually did something; `{ element }` / `{ text }` only prove something is on screen — which a stale render or a locator healed to the wrong element can fake. A _passing_ presence-only `reticle_assert` returns `advice` nudging you to a consequence; heed it on anything that matters.
71
+ ```jsonc
72
+ {
73
+ "kind": "allOf",
74
+ "predicates": [
75
+ { "kind": "signal", "name": "auth:granted" },
76
+ { "kind": "net", "method": "POST", "urlContains": "/api/login", "count": 1 },
77
+ { "kind": "console", "level": "error", "absent": true },
78
+ ],
79
+ }
80
+ // -> evidence: [ { name: "auth:granted", … }, { matched: 1 }, { absent: true } ]
81
+ ```
37
82
 
38
- ## The 4-layer cross-check — never trust a green the state contradicts
83
+ If a predicate is still rejected, the error names the fields **that kind** accepts. Read it rather than guessing again, and note `state` spells its selector `path` while `route` spells its `pathname`.
84
+
85
+ **Assert a consequence, not just presence.** `{ signal }` / `{ net }` prove the feature actually did something; `{ element }` / `{ text }` only prove something is on screen, which a stale render or a locator healed to the wrong element can fake. A _passing_ presence-only `reticle_assert` returns `advice` nudging you to a consequence; heed it on anything that matters.
86
+
87
+ ## The 4-layer cross-check: never trust a green the state contradicts
39
88
 
40
89
  A claim is real only when the layers agree. Check more than the UI:
41
90
 
@@ -48,50 +97,67 @@ A claim is real only when the layers agree. Check more than the UI:
48
97
 
49
98
  > **Rule:** a passing UI assert that the store, network, or signal contradicts is a **false green**.
50
99
 
51
- **Session health is universal.** Every live-session tool result carries a `session` block (`throttled`, `focused`, `lastSeenMs`); when `throttled:true` it also adds a `warning` + `recommendation` (refocus, or `reticle drive`). A throttled/backgrounded tab can silently no-op timers/rAF/pointer gestures — if you see `session.throttled`, distrust a green and refocus first.
100
+ **Session health is universal.** Every live-session tool result carries a `session` block (`throttled`, `focused`, `lastSeenMs`); when `throttled:true` it also adds a `warning` + `recommendation` (refocus, or `reticle drive`). A throttled/backgrounded tab can silently no-op timers/rAF/pointer gestures. If you see `session.throttled`, distrust a green and refocus first.
52
101
 
53
102
  > Store reads (`reticle_state`) are the reliable path; the DOM can lie (optimistic UI, stale render).
54
103
 
55
- **Truncation and coverage are declared, never silent.** A big `reticle_state` read carries `truncation` when the transport caps dropped items — its presence means "this is NOT the whole value", so an absence assertion over a truncated read proves nothing; scope with `path`/`depth` instead. A `reticle_assert` verdict carries `coverage` when part of the page was unobservable (cross-origin iframe, closed shadow root) — a green then means "nothing failed in the part I could see", not "the page is correct". Both fields are **omitted when everything was fine**, so their presence is the warning.
104
+ **Truncation and coverage are declared, never silent.** A big `reticle_state` read carries `truncation` when the transport caps dropped items; its presence means "this is NOT the whole value", so an absence assertion over a truncated read proves nothing; scope with `path`/`depth` instead. A `reticle_assert` verdict carries `coverage` when part of the page was unobservable (cross-origin iframe, closed shadow root); a green then means "nothing failed in the part I could see", not "the page is correct". Both fields are **omitted when everything was fine**, so their presence is the warning.
56
105
 
57
- **Charts report their own geometry faults.** Any element descriptor containing a broken plot carries `chart: [{ kind, tag, attr, sample }]` — `non-finite-coordinates` (a zero-range scale divided by zero; always a bug), `empty-geometry` (mounted but no data reached it), `degenerate-geometry` (every point identical). No extra call and no flag: query the chart as you would anyway. A healthy chart adds nothing. This matters because a chart is the one widget whose correctness is _geometry_ — the store can be right while the polyline is blank, and neither a state read nor a screenshot catches it. Canvas charts are pixels, not DOM: read their data with `canvasChartData(canvasEl, window)` instead.
106
+ **Charts report their own geometry faults.** Any element descriptor containing a broken plot carries `chart: [{ kind, tag, attr, sample }]`, one of `non-finite-coordinates` (a zero-range scale divided by zero; always a bug), `empty-geometry` (mounted but no data reached it), `degenerate-geometry` (every point identical). No extra call and no flag: query the chart as you would anyway. A healthy chart adds nothing. This matters because a chart is the one widget whose correctness is _geometry_: the store can be right while the polyline is blank, and neither a state read nor a screenshot catches it. Canvas charts are pixels, not DOM: read their data with `canvasChartData(canvasEl, window)` instead.
58
107
 
59
108
  **Reads never go silently empty.** A zero-result read returns a `hint`, not a bare `[]`: `reticle_query` → `{ route, presentTestids, knownEmptyState }`; `reticle_network` → `{ totalInWindow, present[] }` (what DID fire); `reticle_console` → `{ totalInWindow, byLevel }` (so "0 errors" ≠ "silent page"); `reticle_state` lists `storeNames` when a store isn't found. Read the hint before assuming "not there." Scope big stores with `reticle_state({ store, path:"a.b.0", depth })` instead of paying for the whole tree; a wrong `path` returns `{ found:false, availableKeys }` so it's self-correcting.
60
109
 
61
110
  ## Core tool set
62
111
 
63
- The tools advertised DIRECTLY — what you'll use 90% of the time. Everything else is one `reticle_run` away (see Token note below).
112
+ The tools advertised DIRECTLY, i.e. what you'll use 90% of the time. Everything else is one `reticle_run` away (see Token note below).
64
113
 
65
114
  `reticle_sessions` · `reticle_navigate` · `reticle_snapshot` · `reticle_query` · `reticle_act` · `reticle_act_sequence` · `reticle_act_and_wait` · `reticle_observe` · `reticle_network` · `reticle_console` · `reticle_wait_for` · `reticle_assert` · `reticle_state` · `reticle_inspect` (DOM node → `src/App.tsx:104`) · `reticle_feedback` (tell the maintainers what is missing) · `reticle_session` (hand back: `{action:"yield"}` the moment you stop driving, `{action:"resume"}` after a human pause).
66
115
 
67
- Frequently useful but NOT core, so reach them through `reticle_run({ tool, args })`: `reticle_domain` (learn the app + gaps), `reticle_capabilities` (the app's declared testable surface), `reticle_baseline {action:"diff"}`, `reticle_project` (run history).
116
+ Frequently useful but NOT core, so reach them through `reticle_run({ tool, args })`: `reticle_capabilities` (the app's whole testable surface in one call; `{fromDisk:true}` needs no browser), `reticle_domain` (learn the app + gaps), `reticle_baseline {action:"diff"}`, `reticle_project` (run history).
68
117
 
69
- **Reach past core when…** you need to record/replay a journey (`reticle_record {action:"start"}/stop`, `reticle_replay`), persist a self-healing golden flow (`reticle_flow_save*` / `reticle_flow_replay` / `reticle_flow_heal`), compile annotations (`reticle_annotate`), explore autonomously (`reticle_explore` lists controls; `reticle_crawl` clicks them all and reports anomalies — **destructive**), reveal a virtualized off-screen row (`reticle_scroll_to` — when `reticle_query` finds nothing because a windowed list hasn't rendered it yet), visual-check (`reticle_screenshot` / `reticle_visual_diff`, pinned with `reticle_viewport` for reproducible baselines), test error/edge states by stubbing the network (`reticle_network_mock` — 500 / offline / delay, driven mode), control time for toasts/debounces/auto-dismiss (`reticle_clock { freeze | advanceMs | reset }`), or work with a human (`reticle_session {action:"end"}` / `reticle_session {action:"resume"}` / `reticle_session {action:"messages"}`, and **`reticle_session {action:"review"}`** to drain + fix the bugs the human flagged from the panel).
118
+ **Reach past core when…** you need to record/replay a journey (`reticle_record {action:"start"}/stop`, `reticle_replay`), persist a self-healing golden flow (`reticle_flow_save*` / `reticle_flow_replay` / `reticle_flow_heal`), compile annotations (`reticle_annotate`), explore autonomously (`reticle_explore` lists controls; `reticle_crawl` clicks them all and reports anomalies, and is **destructive**), reveal a virtualized off-screen row (`reticle_scroll_to`, for when `reticle_query` finds nothing because a windowed list hasn't rendered it yet), visual-check (`reticle_screenshot` / `reticle_visual_diff`, pinned with `reticle_viewport` for reproducible baselines), test error/edge states by stubbing the network (`reticle_network_mock`: 500 / offline / delay, driven mode), control time for toasts/debounces/auto-dismiss (`reticle_clock { freeze | advanceMs | reset }`), or work with a human (`reticle_session {action:"end"}` / `reticle_session {action:"resume"}` / `reticle_session {action:"messages"}`, and **`reticle_session {action:"review"}`** to drain + fix the bugs the human flagged from the panel).
70
119
 
71
120
  ## flows vs baselines vs project.json (the persistence layers)
72
121
 
73
122
  | Artifact | Tool(s) | What it is |
74
123
  | --- | --- | --- |
75
- | **flows** | `reticle_flow_save*` / `reticle_flow_replay` / `reticle_flow_heal` | Replayable **golden journeys**, anchored to testids/signals — drift is legible and self-heals. |
124
+ | **flows** | `reticle_flow_save*` / `reticle_flow_replay` / `reticle_flow_heal` | Replayable **golden journeys**, anchored to testids/signals; drift is legible and self-heals. |
76
125
  | **baselines** | `reticle_baseline {action:"save"}` / `reticle_baseline {action:"diff"}` | Structural **"before" snapshots**; `reticle_baseline {action:"diff"}` flags regressions against them. |
77
- | **project.json** | `reticle_project` | Cross-run **run-history** — "did it behave like last run?" read via `reticle_project`. |
126
+ | **project.json** | `reticle_project` | Cross-run **run-history**: "did it behave like last run?" read via `reticle_project`. |
78
127
 
79
128
  > `reticle_project` / `project.json` are the **run-history layer**. flows answer "does the journey still work?"; baselines answer "did the structure change?"; project.json answers "is this run consistent with prior runs?".
80
129
 
81
- **Visual layer (opt-in).** `reticle_screenshot` saves a PNG baseline to `.reticle/visual/<name>.png`; `reticle_visual_diff` perceptually compares the live page to it (`{ masks }` to ignore volatile regions, `{ maxRatio }` tolerance) → `{ matched, changedPixels, ratio, region, diffPath }`. It answers "does it **look** right" — complementary to the behavioral layers, never a replacement. Both need a **driven browser** (`reticle drive <url>` / `RETICLE_CDP_URL`); without one they return `{ ok:false, reason:"no-visual-provider" }` (the always-on SDK ships no screenshotter).
130
+ **Visual layer (opt-in).** `reticle_screenshot` saves a PNG baseline to `.reticle/visual/<name>.png`; `reticle_visual_diff` perceptually compares the live page to it (`{ masks }` to ignore volatile regions, `{ maxRatio }` tolerance) → `{ matched, changedPixels, ratio, region, diffPath }`. It answers "does it **look** right", complementary to the behavioral layers, never a replacement. Both need a **driven browser** (`reticle drive <url>` / `RETICLE_CDP_URL`); without one they return `{ ok:false, reason:"no-visual-provider" }` (the always-on SDK ships no screenshotter).
131
+
132
+ ## Fields worth reading that you may not know are there
133
+
134
+ Recent additions, each of which answers a question agents were previously asking a second call to resolve:
135
+
136
+ | Field | On | What it tells you |
137
+ | --- | --- | --- |
138
+ | `expiresInMs` | `reticle_lease { action: "acquire" }` | How long the lease lives if untouched, reset by every call that targets it. Plan a pass to finish inside it, or re-acquire deliberately, rather than losing a measurement to a silent expiry. |
139
+ | `scroll` | `reticle_inspect` | `scrollTop`, `scrollHeight`, `clientHeight`, `overflowY`. Whether the element scrolls, which you cannot infer from geometry alone. |
140
+ | `timeline_omitted` | `reticle_record { action: "stop" }` | The raw event timeline is not in the response. It says how many events there were and names the call that returns them, with the cursor filled in. |
141
+ | `elided` | `reticle_act_and_wait` | Some diff arrays were capped. The count is real even when the list is trimmed, so a small array does not mean a quiet app. |
142
+ | `colorTokens`, `themeScope` | `reticle_inspect` | Every design token matching a colour, not one arbitrary winner, and which theme the reading was taken under. The singular `colorToken` is `null` when several tokens share a colour, because naming one of them was the defect. |
143
+ | `why` | `reticle_sessions`, when the list is empty | Why nothing is connected, and the next action. An empty list is never the end of the road. |
144
+
145
+ **When a verdict comes back `unsettled`,** it now names what it was waiting for and what the window actually held. Read those before retrying: an `unknown` that explains itself is a retry with a better `until`, and one that does not is a dead end.
146
+
147
+ **If the port is taken,** `reticle kill` frees it and leaves your own MCP proxy alone. Do not reach for `lsof -i :4400` and kill what it lists; that matches every process on the port, including the proxy you are talking through.
82
148
 
83
149
  ## Start here
84
150
 
85
- 0. Just ran `reticle init` / started the dev server? Poll `reticle_sessions()` until your tab appears — readiness is server-internal now, so the first live call already blocks until the SDK connects.
86
- 1. `reticle_sessions` — find the connected tab (omit `sessionId` if there's only one). **An empty list is not a dead end: read the `why` field.** It names which case this is — no app running, an app running that has never dialled this daemon, a project that never went through `init`, or a tab that closed — and the fix for each. Do not fall back to static reasoning until you have read it.
87
- 2. `reticle_domain` — learn the app BEFORE testing: the saved flows, what each asserts, and the **gaps** (declared signals/testids that no flow verifies — untested intent). Tells you what to test and where the real risk is without crawling the whole app. Falls back to `reticle_capabilities` for the raw testable surface (`testids`, `signals`, `stores`, `flows`).
151
+ 0. Just ran `reticle init` / started the dev server? Poll `reticle_sessions()` until your tab appears. Readiness is server-internal now, so the first live call already blocks until the SDK connects.
152
+ 1. `reticle_sessions` finds the connected tab (omit `sessionId` if there's only one). **An empty list is not a dead end: read the `why` field.** It names which case this is (no app running, an app running that has never dialled this daemon, a project that never went through `init`, or a tab that closed) and the fix for each. Do not fall back to static reasoning until you have read it.
153
+ 2. `reticle_domain` learns the app BEFORE testing: the saved flows, what each asserts, and the **gaps** (declared signals/testids that no flow verifies, i.e. untested intent). Tells you what to test and where the real risk is without crawling the whole app. Falls back to `reticle_capabilities` for the raw testable surface (`testids`, `signals`, `stores`, `flows`).
88
154
  3. Run the loop: **look → act → observe → assert**, cross-checking the 4 layers on anything that matters.
89
155
 
90
156
  ## Token note
91
157
 
92
158
  - **Keep observation cheap.** Prefer `reticle_query` / scoped or `interactive` `reticle_snapshot` / `reticle_assert` over dumping the full tree. A full verify loop is ~100 tokens; see [token-efficiency.md](token-efficiency.md) (~73× leaner than full-tree snapshots).
93
- - **Re-look with `reticle_snapshot({ diff:true })`** after an action — it returns only what changed (`mode:delta`/`unchanged`), ~99% fewer tokens than a full re-snapshot and no stale tree to mis-read. Every snapshot/query result carries `cost:{ bytes, tokens }` — re-scope before reading if it's large.
94
- - **Cap broad reads.** `reticle_query` takes `limit` (caps descriptors; reports `total`/`truncated`) and `count_only` (just the match count). `reticle_network` / `reticle_console` take `limit` (most-recent-N, reports `droppedOldest`) and carry the same `cost` hint — so a busy page or wide window never floods your context unnoticed.
95
- - **A saved flow tells you if it's a real test.** `reticle_flow_save` returns `assertions.grade` (`asserted` / `presence-only` / `assertion-free`); if it's not `asserted`, add a consequence (`reticle_annotate` assert-signal/assert-net or a success-state) so it can't pass while broken. On replay, an ambiguous heal (two testids tie) is surfaced, never auto-applied — and an `apply` heal re-replays the rebound flow and **refuses to write** if the success consequence no longer fires (`status:consequence_broken`): it heals the locator, never the intent.
96
- - **Predicate schema is not bloated.** The recursive predicate DSL used by `reticle_assert` / `reticle_wait_for` / `reticle_act_and_wait` is **factored, not inlined**: when converted to the JSON Schema MCP sends, the predicate body is emitted **once** (~2.7k chars ≈ **~685 tokens** per tool) and recursion is handled by self-`$ref` (`#/properties/predicate`) — no per-recursion duplication. No action needed.
97
- - **One tool surface.** Reticle advertises the core set directly (sessions/navigate/snapshot/query/act/act_and_wait/observe/network/console/wait_for/assert/state/inspect/feedback/session — the whole detect loop, plus the file-pointer, the feedback channel and the handback) PLUS two meta-tools that keep every other tool one call away: `reticle_tools` (discover — no args lists every tool name + summary; `names:[…]` loads full params on demand) and `reticle_run({ tool, args })` (invoke any tool by name; a top-level `sessionId` is forwarded to the target, so you need not nest it in `args`). So to record/replay/verify a flow, call `reticle_run({ tool:"reticle_flow_verify", sessionId })` (or `reticle_tools` first to see params). There is nothing to pick. `RETICLE_ADVERTISE_ALL_TOOLS=1` (read by the daemon at startup, so restart it) advertises everything with output schemas — a verification switch for suites, not a way to run agents. **Sizes are deliberately not quoted here** — a count in prose goes stale and has three times already. `reticle_tools` reports the live surface, and SKILL.md carries the one gated table.
159
+ - **Re-look with `reticle_snapshot({ diff:true })`** after an action; it returns only what changed (`mode:delta`/`unchanged`), ~99% fewer tokens than a full re-snapshot and no stale tree to mis-read. Every snapshot/query result carries `cost:{ bytes, tokens }`, so re-scope before reading if it's large.
160
+ - **Cap broad reads.** `reticle_query` takes `limit` (caps descriptors; reports `total`/`truncated`) and `count_only` (just the match count). `reticle_network` / `reticle_console` take `limit` (most-recent-N, reports `droppedOldest`) and carry the same `cost` hint, so a busy page or wide window never floods your context unnoticed.
161
+ - **A saved flow tells you if it's a real test.** `reticle_flow_save` returns `assertions.grade` (`asserted` / `presence-only` / `assertion-free`); if it's not `asserted`, add a consequence (`reticle_annotate` assert-signal/assert-net or a success-state) so it can't pass while broken. On replay, an ambiguous heal (two testids tie) is surfaced, never auto-applied. And an `apply` heal re-replays the rebound flow and **refuses to write** if the success consequence no longer fires (`status:consequence_broken`): it heals the locator, never the intent.
162
+ - **Predicate schema is not bloated.** The recursive predicate DSL used by `reticle_assert` / `reticle_wait_for` / `reticle_act_and_wait` is **factored, not inlined**: when converted to the JSON Schema MCP sends, the predicate body is emitted **once** (~2.7k chars ≈ **~685 tokens** per tool) and recursion is handled by self-`$ref` (`#/properties/predicate`), with no per-recursion duplication. No action needed.
163
+ - **One tool surface.** Reticle advertises the core set directly (sessions/navigate/snapshot/query/act/act_and_wait/observe/network/console/wait_for/assert/state/inspect/feedback/session: the whole detect loop, plus the file-pointer, the feedback channel and the handback) PLUS two meta-tools that keep every other tool one call away: `reticle_tools` (discover; no args lists every tool name + summary, `names:[…]` loads full params on demand) and `reticle_run({ tool, args })` (invoke any tool by name; a top-level `sessionId` is forwarded to the target, so you need not nest it in `args`). So to record/replay/verify a flow, call `reticle_run({ tool:"reticle_flow_verify", sessionId })` (or `reticle_tools` first to see params). There is nothing to pick. `RETICLE_ADVERTISE_ALL_TOOLS=1` (read by the daemon at startup, so restart it) advertises everything with output schemas: a verification switch for suites, not a way to run agents. **Sizes are deliberately not quoted here.** A count in prose goes stale, and has three times already. `reticle_tools` reports the live surface, and SKILL.md carries the one gated table.