@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/gates.md CHANGED
@@ -1,4 +1,10 @@
1
- # Gates — what to run before you push
1
+ ---
2
+ title: Gates
3
+ description: 'I changed some files. Which command do I run before I push?'
4
+ icon: shield-check
5
+ ---
6
+
7
+ If you changed anything at all, run `pnpm format:check && pnpm lint && pnpm typecheck && pnpm test:unit` (about two minutes). If you also touched the tool surface, install, desktop or Rust, section 1 below names the one extra command your change needs. CI runs everything regardless, so routing costs you a slower red, never a missed one.
2
8
 
3
9
  > **One question this file answers:** _I changed some files. Which command do I run?_
4
10
  >
@@ -15,19 +21,19 @@ Every gate below was executed end to end against `main` on **2026-08-12** (macOS
15
21
  | `pnpm build` | ✅ | ~40s |
16
22
  | `pnpm lint` (+ both guard self-tests) | ✅ | ~15s |
17
23
  | `pnpm typecheck` | ✅ | ~10s |
18
- | `pnpm test:unit` | ✅ 4,315 tests / 461 files across 8 packages | ~45s |
24
+ | `pnpm test:unit` | ✅ 5,725 tests / 613 files across 8 packages | ~45s |
19
25
  | `pnpm format:check` | ✅ | ~10s |
20
26
  | `pnpm test:integration` | ✅ 12/12 | 17s |
21
- | `pnpm test:e2e` | ✅ 32/32 specs + soak 60/60 answered | **490s** |
22
- | `pnpm test:e2e:desktop` | ✅ 2/2 (Electron 20, Tauri 14) | 58s |
23
- | `node apps/e2e/soak.mjs --self-check` | ✅ | <1s |
24
- | `node apps/e2e/matrix.mjs --self-check` | ✅ | <1s |
27
+ | `pnpm test:e2e` | ✅ 33/33 specs + soak 60/60 answered | **490s** |
28
+ | `pnpm test:e2e:desktop` | ✅ 2/2 (Electron 20, Tauri 17) | 58s |
29
+ | `node apps/e2e/soak.mjs --self-check` | ✅ | `<1s` |
30
+ | `node apps/e2e/matrix.mjs --self-check` | ✅ | `<1s` |
25
31
  | `pnpm matrix:compat --only cursor` | ✅ 4/4 | ~10s |
26
- | `pnpm bench` | ✅ 10/10 — **was failing before this sweep** (see below) | 279s |
32
+ | `pnpm bench` | ✅ 10/10 (**was failing before this sweep**, see below) | 279s |
27
33
 
28
- **`pnpm bench` was broken and nobody knew.** `suite-rre.mjs` recorded four flows that asserted no observable consequence and then demanded a `pass` verdict from `reticle_flow_verify` — which correctly grades an assertion-free suite `unverifiable`. The product got more honest about false greens; the benchmark measuring it did not follow, so the whole run aborted at script 9 of 10 and `replay-determinism` never ran at all. Each flow now carries a success oracle. This is the failure mode `bench/` is most exposed to: **nothing in CI runs it**, so it can only rot silently.
34
+ **`pnpm bench` was broken and nobody knew.** `suite-rre.mjs` recorded four flows that asserted no observable consequence and then demanded a `pass` verdict from `reticle_flow_verify`, which correctly grades an assertion-free suite `unverifiable`. The product got more honest about false greens; the benchmark measuring it did not follow, so the whole run aborted at script 9 of 10 and `replay-determinism` never ran at all. Each flow now carries a success oracle. This is the failure mode `bench/` is most exposed to: **nothing in CI runs it**, so it can only rot silently.
29
35
 
30
- `pnpm gate:install` (~15 min) and the Windows / Rust jobs were **not** run in this sweep — they are CI-only or network-bound. They are green on `main` per the last CI run, which is a weaker claim than every row above, and is stated that way on purpose.
36
+ `pnpm gate:install` (~15 min) and the Windows / Rust jobs were **not** run in this sweep; they are CI-only or network-bound. They are green on `main` per the last CI run, which is a weaker claim than every row above, and is stated that way on purpose.
31
37
 
32
38
  ---
33
39
 
@@ -37,15 +43,15 @@ Find the row that matches what you changed. Run its commands. That is the whole
37
43
 
38
44
  | You changed | Run | Cost |
39
45
  | --- | --- | --- |
40
- | **Anything at all** | `pnpm lint && pnpm typecheck && pnpm test:unit` | ~2 min |
46
+ | **Anything at all** | `pnpm format:check && pnpm lint && pnpm typecheck && pnpm test:unit` | ~2 min |
41
47
  | The tool surface, the wire contract (`packages/core`), or an observer | ↑ **and** `pnpm test:e2e` | +~8 min |
42
- | `reticle init`, `@reticlehq/vite-plugin`, `@reticlehq/next`, `@reticlehq/babel-plugin` — anything a user runs before their first session | ↑ **and** `pnpm gate:install` | +~15 min |
48
+ | `reticle init`, `@reticlehq/vite-plugin`, `@reticlehq/next`, `@reticlehq/babel-plugin`, anything a user runs before their first session | ↑ **and** `pnpm gate:install` | +~15 min |
43
49
  | `@reticlehq/electron`, `packages/tauri`, the IPC observer, desktop capture | ↑ **and** `pnpm test:e2e:desktop` | +~3 min |
44
- | Telemetry, feedback, or anything that emits an event | ↑ **and** read [`telemetry-contract.md`](./telemetry-contract.md) first. `pnpm test:e2e` covers it (`telemetry-events-test`) | — |
50
+ | Telemetry, feedback, or anything that emits an event | ↑ **and** read [`telemetry-contract.md`](./telemetry-contract.md) first. `pnpm test:e2e` covers it (`telemetry-events-test`) | n/a |
45
51
  | `packages/tauri` (Rust) | ↑ **and** `cd packages/tauri && cargo fmt --check && cargo clippy --all-targets -- -D warnings` | +~2 min |
46
52
  | Docs, README, comments only | `pnpm format:check` | seconds |
47
53
 
48
- **Why routing exists.** The full set is roughly 35 minutes. A gate people resent is a gate people route around, so only the tier that can see your change is worth your time. CI runs everything regardless — routing costs you a slower red, never a missed one.
54
+ **Why routing exists.** The full set is roughly 35 minutes. A gate people resent is a gate people route around, so only the tier that can see your change is worth your time. CI runs everything regardless, so routing costs you a slower red, never a missed one.
49
55
 
50
56
  ---
51
57
 
@@ -59,16 +65,16 @@ Each gate exists because the ones above it are blind to something. That blindnes
59
65
  | **Lint** | `pnpm lint` | style rules, plus the dependency-boundary and lossy-transform guards | anything not expressible as a rule | `verify` |
60
66
  | **Typecheck** | `pnpm typecheck` | types agree across package boundaries | runtime behaviour | `verify` |
61
67
  | **Unit** | `pnpm test:unit` | 4,315 tests, per-package, no browser | anything crossing a package boundary at runtime | `verify` |
62
- | **Format** | `pnpm format:check` | Prettier | — | `verify` |
68
+ | **Format** | `pnpm format:check` | Prettier | n/a | `verify` |
63
69
  | **Integration** | `pnpm test:integration` | real headless Chromium: browser pool, crash isolation, framework adapters, `withReticle` | the MCP surface, the daemon | `e2e` |
64
- | **Web e2e battery** | `pnpm test:e2e` | 30 specs against 3 booted servers and a real browser — the tool surface, the daemon lifecycle, transport faults, telemetry, trace shape — plus the soak | desktop runtimes; the install | `e2e` |
70
+ | **Web e2e battery** | `pnpm test:e2e` | 32 specs against 3 booted servers and a real browser (the tool surface, the daemon lifecycle, transport faults, telemetry, trace shape), plus the soak | desktop runtimes; the install | `e2e` |
65
71
  | **Desktop battery** | `pnpm test:e2e:desktop` | a real Electron main process and a **packaged** Tauri binary, driven headless | web-only paths | `desktop-e2e` |
66
- | **Install gate** | `pnpm gate:install` | scaffolds 3 pristine apps, publishes this checkout to a local Verdaccio, lets `init` install itself, boots each app in a real browser, polls for a session | install _complexity_ — see [`fixtures.md`](./fixtures.md) | `install-gate` |
72
+ | **Install gate** | `pnpm gate:install` | scaffolds 3 pristine apps, publishes this checkout to a local Verdaccio, lets `init` install itself, boots each app in a real browser, polls for a session | install _complexity_; see [`fixtures.md`](./fixtures.md) | `install-gate` |
67
73
  | **Matrix records** | `pnpm matrix:validate` | every submitted client-compat record is well-formed | whether the client actually works | `matrix-records` |
68
74
  | **Windows** | (CI only) | that the code runs at all on the majority platform | e2e; Windows is unit-only | `windows` |
69
75
  | **Rust** | `cargo fmt/clippy/check` | `packages/tauri` compiles and lints on Linux, macOS, and cross-checks Windows | everything JS | `rust`, `rust-macos` |
70
76
 
71
- **The single required status check is `gate`.** It passes when every job above either succeeded or was deliberately skipped by path routing, and fails on anything else. Adding a job to `ci.yml` is half the work; adding it to `gate`'s `needs:` list is the other half — a job missing from that list runs, reports, and is structurally incapable of blocking a merge.
77
+ **The single required status check is `gate`.** It passes when every job above either succeeded or was deliberately skipped by path routing, and fails on anything else. Adding a job to `ci.yml` is half the work; adding it to `gate`'s `needs:` list is the other half. A job missing from that list runs, reports, and is structurally incapable of blocking a merge.
72
78
 
73
79
  ### Guards that self-test
74
80
 
@@ -99,7 +105,7 @@ These are real and they work; they are not on the PR path, so they only run when
99
105
 
100
106
  ## 4. `bench/` is not a gate
101
107
 
102
- `bench/` is **measurement and research**, not a merge check. Nothing there blocks a PR, nothing runs in CI, and it is allowed to bit-rot in a way a gate is not. Read [`bench/README.md`](../bench/README.md) before touching it — it says which scripts are live and which are one-off studies kept as evidence for a published claim.
108
+ `bench/` is **measurement and research**, not a merge check. Nothing there blocks a PR, nothing runs in CI, and it is allowed to bit-rot in a way a gate is not. Read [`bench/README.md`](../bench/README.md) before touching it: it says which scripts are live and which are one-off studies kept as evidence for a published claim.
103
109
 
104
110
  The one exception worth knowing: `pnpm bench` + `pnpm bench:gate` is a working regression gate for the replay numbers, and it is run by hand before a release.
105
111
 
@@ -109,9 +115,9 @@ The one exception worth knowing: `pnpm bench` + `pnpm bench:gate` is a working r
109
115
 
110
116
  Sometimes it is. The specific failures worth recognising:
111
117
 
112
- - **`EADDRINUSE` / "died during boot"** — a previous run left something on `:8787`, `:4310`, or `:3100`. `run-ci.sh` frees these on exit; if it was killed, free them by hand.
113
- - **Killing port 4400 with `lsof -ti tcp:4400 | xargs kill -9`** — this SIGKILLs the `reticle mcp` proxy too, because the proxy holds a _client_ socket on the bridge port. Always add `-sTCP:LISTEN`. This is the root cause of most "the MCP went down" reports.
114
- - **A timing assertion** — if a test asserts `Date.now() - t < N`, that is a bug in the test, not a flake to re-run. Assert the bound (output size, a truncation flag), or use a generous per-test timeout. See [`harness-rules.md`](../apps/e2e/harness-rules.md).
115
- - **An `INCONCLUSIVE` verdict** — the harness is telling you the transport did not stay up, so it is claiming nothing about the product. That is the harness working, not the product failing.
118
+ - **`EADDRINUSE` / "died during boot".** A previous run left something on `:8787`, `:4310`, or `:3100`. `run-ci.sh` frees these on exit; if it was killed, free them by hand.
119
+ - **Killing port 4400 with `lsof -ti tcp:4400 | xargs kill -9`.** This SIGKILLs the `reticle mcp` proxy too, because the proxy holds a _client_ socket on the bridge port. Always add `-sTCP:LISTEN`. This is the root cause of most "the MCP went down" reports.
120
+ - **A timing assertion.** If a test asserts `Date.now() - t < N`, that is a bug in the test, not a flake to re-run. Assert the bound (output size, a truncation flag), or use a generous per-test timeout. See [`harness-rules.md`](../apps/e2e/harness-rules.md).
121
+ - **An `INCONCLUSIVE` verdict.** The harness is telling you the transport did not stay up, so it is claiming nothing about the product. That is the harness working, not the product failing.
116
122
 
117
123
  The four rules every tier obeys, and the incident behind each, are in [`apps/e2e/harness-rules.md`](../apps/e2e/harness-rules.md).
@@ -1,18 +1,28 @@
1
- # Getting Started with Reticle
1
+ ---
2
+ title: Getting started
3
+ description: 'Zero to your agent verifying your real app, step by step, with working code for Vite, Next.js, React and plain HTML.'
4
+ icon: rocket
5
+ ---
6
+
7
+ **To get started with Reticle: run `npx @reticlehq/server init` in your project root, restart your dev server, restart your coding agent, then open your app in a browser and ask the agent to drive a flow.** Reticle is a verification layer that embeds a dev-only SDK in your running web app so an AI coding agent can prove a change works instead of guessing. It needs Node 20 or newer, an app you run locally, and an agent that speaks MCP.
8
+
9
+ > **Looking for the fast path?** [Quickstart](/quickstart) gets you to a real verdict in five minutes, and every response on it was captured live. [Agentic install](/install-agentic) and [Manual install](/install-manual) cover setup in detail, per agent and per framework.
10
+ >
11
+ > This page is the long-form walkthrough: the same ground, more slowly, plus per-framework wiring, multi-app setups and prerelease notes. Start here if you want the whole picture in one file rather than the fast route.
2
12
 
3
- This walks you from zero to your agent verifying your app — step by step, with real code for real frameworks. ~10 minutes.
13
+ This walks you from zero to your agent verifying your app, step by step, with real code for real frameworks. ~10 minutes.
4
14
 
5
15
  - [What you're setting up](#what-youre-setting-up)
6
16
  - [Prerequisites](#prerequisites)
7
- - [Step 1 — Connect your coding agent (MCP)](#step-1--connect-your-coding-agent-mcp)
8
- - [Step 2 — Embed the SDK in your app](#step-2--embed-the-sdk-in-your-app)
17
+ - [Step 1: Connect your coding agent (MCP)](#step-1-connect-your-coding-agent-mcp)
18
+ - [Step 2: Embed the SDK in your app](#step-2-embed-the-sdk-in-your-app)
9
19
  - [Vite + React](#vite--react)
10
20
  - [Next.js](#nextjs)
11
21
  - [Plain / other frameworks](#plain--other-frameworks)
12
- - [Step 3 — (React) component & source-file mapping](#step-3--react-component--source-file-mapping)
13
- - [Step 4 — Run it & verify the connection](#step-4--run-it--verify-the-connection)
14
- - [Step 5 — Your first verification](#step-5--your-first-verification)
15
- - [Step 6 — Make your app agent-legible](#step-6--make-your-app-agent-legible-optional-high-leverage)
22
+ - [Step 3: (React) component and source-file mapping](#step-3-react-component-and-source-file-mapping)
23
+ - [Step 4: Run it and verify the connection](#step-4-run-it-and-verify-the-connection)
24
+ - [Step 5: Your first verification](#step-5-your-first-verification)
25
+ - [Step 6: Make your app agent-legible](#step-6-make-your-app-agent-legible-optional-high-leverage)
16
26
  - [Common setups at a glance](#common-setups-at-a-glance)
17
27
  - [Troubleshooting](#troubleshooting)
18
28
 
@@ -22,58 +32,61 @@ This walks you from zero to your agent verifying your app — step by step, with
22
32
 
23
33
  Three pieces, each tiny:
24
34
 
25
- ```text
26
- ┌─────────────┐ MCP ┌──────────────────────┐ WebSocket ┌─────────────────────┐
27
- │ coding agent │◀───────▶│ reticle bridge + server │◀─────────────▶│ your app + the Reticle │
28
- │ (Claude Code)│ stdio │ (npx @reticlehq/server) │ localhost │ SDK (dev only) │
29
- └─────────────┘ └──────────────────────┘ :4400 └─────────────────────┘
35
+ ```mermaid
36
+ flowchart LR
37
+ A["coding agent<br/>(Claude Code)"]
38
+ S["reticle bridge + server<br/>npx @reticlehq/server"]
39
+ B["your app<br/>+ the Reticle SDK<br/>(dev only)"]
40
+
41
+ A <-- "MCP over stdio" --> S
42
+ S <-- "WebSocket<br/>localhost:4400" --> B
30
43
  ```
31
44
 
32
45
  Three pieces, each from the package for its audience:
33
46
 
34
- 1. **The MCP server** — your agent launches it with `npx @reticlehq/server mcp`; it hosts the tools _and_ the WebSocket bridge your app connects to. You don't run it by hand; the agent does.
35
- 2. **The SDK** — `import { reticle } from '@reticlehq/react'`, a few lines in your app's dev entry point.
36
- 3. **(Optional) React adapter + source-mapping** — so `reticle_inspect` can tell the agent which component/file to edit (also from `@reticlehq/react`).
47
+ 1. **The MCP server.** Your agent launches it with `npx @reticlehq/server mcp`; it hosts the tools _and_ the WebSocket bridge your app connects to. You don't run it by hand; the agent does.
48
+ 2. **The SDK**: `import { reticle } from '@reticlehq/react'`, a few lines in your app's dev entry point.
49
+ 3. **(Optional) React adapter + source-mapping**, so `reticle_inspect` can tell the agent which component/file to edit (also from `@reticlehq/react`).
37
50
 
38
51
  Everything is **dev-only** and **localhost-only**. It's tree-shaken out of production builds.
39
52
 
40
53
  ## Prerequisites
41
54
 
42
- - Node 18+ and a package manager (npm/pnpm/yarn).
55
+ - Node 20 or newer, and a package manager (npm/pnpm/yarn).
43
56
  - A coding agent that speaks MCP: Claude Code, Cursor, Windsurf, Claude Desktop, etc.
44
57
  - A web app you run locally in dev (any framework; React gets the richest features).
45
58
 
46
59
  ---
47
60
 
48
- ## Fastest path — `reticle init`
61
+ ## Fastest path: `reticle init`
49
62
 
50
63
  From your project root:
51
64
 
52
65
  ```bash
53
- npx @reticlehq/server init
66
+ RETICLE_INSTALL_SOURCE=docs_site npx @reticlehq/server init
54
67
  ```
55
68
 
56
69
  It detects your framework, package manager, and React version, then:
57
70
 
58
- - **registers the Reticle MCP server once, globally, for each agent you have installed** — Claude Code (`claude mcp add reticle -s user`) and/or Cursor (`~/.cursor/mcp.json`) — so every project on this machine gets it; you never re-add it per project,
59
- - **writes a verification rule into your agent's instruction file** — `CLAUDE.md`, `.cursor/rules/reticle.mdc`, or `AGENTS.md` — so the agent knows to verify a feature with Reticle _after building it_, not only when you remember to ask (idempotent; appended below anything you already have),
71
+ - **registers the Reticle MCP server once, globally, for each agent you have installed** (Claude Code via `claude mcp add reticle -s user`, and/or Cursor via `~/.cursor/mcp.json`), so every project on this machine gets it; you never re-add it per project,
72
+ - **writes a verification rule into your agent's instruction file** (`CLAUDE.md`, `.cursor/rules/reticle.mdc`, or `AGENTS.md`), so the agent knows to verify a feature with Reticle _after building it_, not only when you remember to ask (idempotent; appended below anything you already have),
60
73
  - installs the SDK kit (`@reticlehq/react`) and the right build plugin (`@reticlehq/vite-plugin` or `@reticlehq/next`) as dev dependencies,
61
- - **Vite:** adds the `reticle()` plugin to your config — which wires source mapping _and_ `reticle.connect()` for you, so there is nothing else to edit,
74
+ - **Vite:** adds the `reticle()` plugin to your config, which wires source mapping _and_ `reticle.connect()` for you, so there is nothing else to edit,
62
75
  - **Next / other:** creates the dev component and prints the exact `withReticle` / mount / connect snippets to paste (it never half-edits a build config).
63
76
 
64
77
  The bridge + MCP server is a single process that serves all your projects, so it's registered at **user scope**, not in a per-project `.mcp.json`. Only the SDK (the `reticle()` plugin / connect call) is added per project.
65
78
 
66
- Re-running is safe (already-registered/already-patched steps are skipped). Preview without writing via `npx @reticlehq/server init --dry-run`. Flags: `--port N`, `--no-mcp`, `--no-install`, `--yes`.
79
+ Re-running is safe (already-registered/already-patched steps are skipped). Preview without writing via `npx @reticlehq/server init --dry-run`. Flags: `--dry-run`, `--port N`, `--no-mcp`, `--no-install`, `--app <dir>`.
67
80
 
68
- Then restart your dev server and skip to [Step 4](#step-4--run-it--verify-the-connection). The manual steps below explain what `init` sets up, if you prefer to wire it yourself.
81
+ Then restart your dev server and skip to [Step 4](#step-4-run-it-and-verify-the-connection). The manual steps below explain what `init` sets up, if you prefer to wire it yourself.
69
82
 
70
83
  ---
71
84
 
72
- ## Step 1 — Connect your coding agent (MCP), once
85
+ ## Step 1: Connect your coding agent (MCP), once
73
86
 
74
- You don't start the server manually — your agent starts it via MCP. Register Reticle **once, at the user (global) scope** so every project picks it up — there's nothing to add per project.
87
+ You don't start the server manually; your agent starts it via MCP. Register Reticle **once, at the user (global) scope** so every project picks it up. There's nothing to add per project.
75
88
 
76
- **Claude Code** — one command:
89
+ **Claude Code**, one command:
77
90
 
78
91
  ```bash
79
92
  claude mcp add reticle -s user -- npx @reticlehq/server mcp
@@ -81,7 +94,7 @@ claude mcp add reticle -s user -- npx @reticlehq/server mcp
81
94
 
82
95
  (`reticle init` runs exactly this for you. `-s user` is what makes it global; drop it for a project-local registration instead.)
83
96
 
84
- **Cursor** — add to your global `~/.cursor/mcp.json` (not per-project; `reticle init` writes this for you):
97
+ **Cursor**: add to your global `~/.cursor/mcp.json` (not per-project; `reticle init` writes this for you):
85
98
 
86
99
  ```jsonc
87
100
  {
@@ -97,7 +110,7 @@ Other MCP clients (Windsurf, Claude Desktop, …) use the same `command`/`args`
97
110
 
98
111
  ---
99
112
 
100
- ## Step 2 — Embed the SDK in your app
113
+ ## Step 2: Embed the SDK in your app
101
114
 
102
115
  Install the SDK kit plus your framework's build plugin as dev dependencies (the kit re-exports the browser sensor, so one install gives both `reticle` and `install`):
103
116
 
@@ -110,7 +123,7 @@ Then call `reticle.connect()` once, in dev only. Where you put it depends on you
110
123
 
111
124
  ### Vite + React
112
125
 
113
- **Recommended — the Vite plugin (one line, does everything).** Add `reticle()` to your `vite.config.ts`:
126
+ **Recommended: the Vite plugin (one line, does everything).** Add `reticle()` to your `vite.config.ts`:
114
127
 
115
128
  ```ts
116
129
  import { defineConfig } from 'vite';
@@ -118,18 +131,18 @@ import react from '@vitejs/plugin-react';
118
131
  import { reticle } from '@reticlehq/vite-plugin';
119
132
 
120
133
  export default defineConfig({
121
- plugins: [react(), reticle()],
134
+ plugins: [reticle(), react()],
122
135
  });
123
136
  ```
124
137
 
125
- This injects `reticle.connect()` for you _and_ handles React 19 source mapping (Step 3) — so there's no entry-file edit and no separate Babel setup. `apply: 'serve'` means it's dropped from `vite build` entirely, so it can never reach production. (This is exactly what `reticle init` adds.)
138
+ This injects `reticle.connect()` for you _and_ handles React 19 source mapping (Step 3), so there's no entry-file edit and no separate Babel setup. `apply: 'serve'` means it's dropped from `vite build` entirely, so it can never reach production. (This is exactly what `reticle init` adds.)
126
139
 
127
140
  <details>
128
141
  <summary>Prefer to wire it by hand instead of the plugin?</summary>
129
142
 
130
143
  In your entry file (`src/main.tsx`), call `connect()` in dev only:
131
144
 
132
- ```ts
145
+ ```tsx
133
146
  import { StrictMode } from 'react';
134
147
  import { createRoot } from 'react-dom/client';
135
148
  import { reticle, SESSION_AUTO } from '@reticlehq/react';
@@ -153,7 +166,7 @@ On React 19 you then also need the source-mapping Babel plugin from Step 3. The
153
166
 
154
167
  ### The pairing token (why some setups need one line more)
155
168
 
156
- The daemon auto-generates a **pairing token** on first run and stores it at `~/.reticle/pairing-token` (owner-only, `0600`). The bridge requires it, so another app running on `http://localhost:<some-other-port>` can't quietly register or drive your session — only code that can read that file (your dev server, not a web page) can present it.
169
+ The daemon auto-generates a **pairing token** on first run and stores it at `~/.reticle/pairing-token` (owner-only, `0600`). The bridge requires it, so another app running on `http://localhost:<some-other-port>` can't quietly register or drive your session. Only code that can read that file (your dev server, not a web page) can present it.
157
170
 
158
171
  - **Vite plugin users:** nothing to do. The plugin reads the token server-side and injects it into `connect()` for you.
159
172
  - **Next.js / hand-wired `connect()`:** your `connect()` runs in the browser and can't read the file, so pass the token in yourself. The simplest path is a shared secret: set `RETICLE_TOKEN` for the daemon (it uses that instead of auto-generating) and expose the same value to the client as `NEXT_PUBLIC_RETICLE_TOKEN`, then pass it to `connect({ token })` (see below). On a single-user machine you can also just read `~/.reticle/pairing-token` in your dev tooling and forward it the same way.
@@ -222,12 +235,12 @@ Or, with no build step, a script tag pointed at the bridge:
222
235
 
223
236
  ### Running multiple apps at once
224
237
 
225
- It's common to have several apps open in dev — a few Next.js and React projects, or multiple tabs of the same app. Reticle handles this cleanly **as long as each connection has a unique session id**, which is exactly what `SESSION_AUTO` gives you (a fresh id per tab). The examples above all use it, so you get this for free. When more than one app is connected, an Reticle tool call targets the focused / most recently active one automatically, or you can pass an explicit `sessionId` to target a specific app.
238
+ It's common to have several apps open in dev: a few Next.js and React projects, or multiple tabs of the same app. Reticle handles this cleanly **as long as each connection has a unique session id**, which is exactly what `SESSION_AUTO` gives you (a fresh id per tab). The examples above all use it, so you get this for free. When more than one app is connected, an Reticle tool call targets the focused / most recently active one automatically, or you can pass an explicit `sessionId` to target a specific app.
226
239
 
227
240
  **Two separate projects, fully isolated.** If you want each repo to have its own independent Reticle bridge (separate sessions, separate `.reticle/` workspace), give each project its own port. Set the same port in both the MCP server config and the app's connection:
228
241
 
229
242
  ```jsonc
230
- // project-b/.mcp.json — give this project its own bridge port
243
+ // project-b/.mcp.json: give this project its own bridge port
231
244
  {
232
245
  "mcpServers": {
233
246
  "reticle": {
@@ -240,33 +253,33 @@ It's common to have several apps open in dev — a few Next.js and React project
240
253
  ```
241
254
 
242
255
  ```ts
243
- // project-b's app — dial the same port
256
+ // project-b's app: dial the same port
244
257
  reticle.connect({ session: SESSION_AUTO, url: 'ws://localhost:4401/reticle' });
245
258
  ```
246
259
 
247
- **On the Vite plugin?** You don't have a hand-written `connect()` to edit — the plugin injects it. Set the port on the plugin instead, and it bakes the matching URL in for you:
260
+ **On the Vite plugin?** You don't have a hand-written `connect()` to edit; the plugin injects it. Set the port on the plugin instead, and it bakes the matching URL in for you:
248
261
 
249
262
  ```ts
250
263
  // project-b/vite.config.ts
251
- plugins: [react(), reticle({ port: 4401 })],
264
+ plugins: [reticle({ port: 4401 }), react()],
252
265
  ```
253
266
 
254
- Either way, the rule is the same: **the app's bridge port must equal the daemon's `RETICLE_PORT`** — and it's the Reticle bridge port, never your dev-server port. Project A stays on the default `4400`, project B on `4401` — they never touch each other. (A port that is already in use now fails fast with a clear error instead of hanging, so a misconfiguration is obvious.)
267
+ Either way, the rule is the same: **the app's bridge port must equal the daemon's `RETICLE_PORT`**, and it's the Reticle bridge port, never your dev-server port. Project A stays on the default `4400`, project B on `4401`; they never touch each other. (A port that is already in use now fails fast with a clear error instead of hanging, so a misconfiguration is obvious.)
255
268
 
256
269
  ---
257
270
 
258
- ## Step 3 — (React) component & source-file mapping
271
+ ## Step 3: (React) component and source-file mapping
259
272
 
260
- This is optional but high-value: it lets `reticle_inspect` map a DOM element back to the **React component and the source file:line** — so when the agent finds a problem, it knows which file to edit. (The React adapter ships in `@reticlehq/react` — nothing extra to install.)
273
+ This is optional but high-value: it lets `reticle_inspect` map a DOM element back to the **React component and the source file:line**, so when the agent finds a problem, it knows which file to edit. (The React adapter ships in `@reticlehq/react`; nothing extra to install.)
261
274
 
262
275
  ```ts
263
276
  import { install as installReticleReact } from '@reticlehq/react';
264
277
  if (import.meta.env.DEV) installReticleReact(); // call before reticle.connect()
265
278
  ```
266
279
 
267
- **React ≤ 18:** that's all — it uses React's dev `_debugSource`.
280
+ **React ≤ 18:** that's all; it uses React's dev `_debugSource`.
268
281
 
269
- **React 19:** React removed `_debugSource`, so the source has to be stamped at build time. **If you added the `reticle()` Vite plugin in Step 2, this is already handled — skip ahead.** Otherwise add the Babel plugin (`@reticlehq/babel-plugin`) to stamp the source onto elements in dev:
282
+ **React 19:** React removed `_debugSource`, so the source has to be stamped at build time. **If you added the `reticle()` Vite plugin in Step 2, this is already handled, so skip ahead.** Otherwise add the Babel plugin (`@reticlehq/babel-plugin`) to stamp the source onto elements in dev:
270
283
 
271
284
  ```ts
272
285
  // vite.config.ts
@@ -278,21 +291,24 @@ export default defineConfig({
278
291
  });
279
292
  ```
280
293
 
281
- > **Next.js:** verified on **Next.js 15 / React 19 (app router, SWC)**. For source-file mapping, use `@reticlehq/next` instead of the Babel plugin — it adds a **dev-only webpack pre-loader that keeps SWC** and stamps `data-reticle-source` so `reticle_inspect` returns `file:line` (e.g. `app/page.tsx:30`):
294
+ > **Next.js:** verified on **Next.js 15 / React 19 (app router, SWC)**. For source-file mapping, use `@reticlehq/next` instead of the Babel plugin. It adds a **dev-only webpack pre-loader that keeps SWC** and stamps `data-reticle-source` so `reticle_inspect` returns `file:line` (e.g. `app/page.tsx:30`):
282
295
  >
283
- > ```js
284
- > // next.config.mjs
285
- > import reticleNext from '@reticlehq/next';
286
- > /** @type {import('next').NextConfig} */
287
- > const nextConfig = {};
288
- > export default reticleNext.withReticle(nextConfig); // no-op in production
296
+ > ```ts
297
+ > // next.config.ts
298
+ > import { withReticle } from '@reticlehq/next';
299
+ > import type { NextConfig } from 'next';
300
+ >
301
+ > const nextConfig: NextConfig = {};
302
+ > export default withReticle(nextConfig); // no-op in production
289
303
  > ```
290
304
  >
305
+ > This is exactly what `reticle init` writes. `@reticlehq/next` is CommonJS, so a default import (`import reticleNext from '@reticlehq/next'` then `reticleNext.withReticle(...)`) and a CJS `const { withReticle } = require('@reticlehq/next')` both work too, if your config is `.mjs` or `.js`.
306
+ >
291
307
  > Component identity works with or without it (Next's internal wrappers are filtered out so you see your components, e.g. just `Page`).
292
308
 
293
309
  ---
294
310
 
295
- ## Step 4 — Run it & verify the connection
311
+ ## Step 4: Run it and verify the connection
296
312
 
297
313
  1. Start your app's dev server as usual (`npm run dev`).
298
314
  2. Open it in the browser (the SDK connects when the page loads).
@@ -310,7 +326,7 @@ If the list is empty, see [Troubleshooting](#troubleshooting).
310
326
 
311
327
  ---
312
328
 
313
- ## Step 5 — Your first verification
329
+ ## Step 5: Your first verification
314
330
 
315
331
  Now just talk to your agent in plain language. For example:
316
332
 
@@ -326,22 +342,22 @@ reticle_query({ by: "role", value: "button", name: "Refresh" }) // → ref e12
326
342
  reticle_act({ ref: "e12", action: "click" }) // → { since: 920 }
327
343
 
328
344
  // verifies the reaction
329
- reticle_assert({ timeout_ms: 2000, predicate: { allOf: [
345
+ reticle_assert({ timeout_ms: 2000, predicate: { kind: "allOf", predicates: [
330
346
  { kind: "net", method: "GET", urlContains: "/api/dashboard", status: 200, since: 920 },
331
347
  { kind: "console", level: "error", absent: true }
332
348
  ]}})
333
349
  // → { pass: true }
334
350
  ```
335
351
 
336
- You get a real, evidence-backed answer — and if it fails, the agent sees the reason (e.g. the call 404'd, or a `TypeError` in `Dashboard.tsx:88`) and can fix it and re-check.
352
+ You get a real, evidence-backed answer. If it fails, the agent sees the reason (e.g. the call 404'd, or a `TypeError` in `Dashboard.tsx:88`) and can fix it and re-check.
337
353
 
338
354
  That's the whole loop. From here, the [Usage Guide](usage.md) covers every tool, the full predicate DSL, and a dozen real situations (login, long lists, eventual consistency, file uploads, LLM calls, regressions, and more).
339
355
 
340
356
  ---
341
357
 
342
- ## Step 6 — Make your app agent-legible (optional, high-leverage)
358
+ ## Step 6: Make your app agent-legible (optional, high-leverage)
343
359
 
344
- The basics above work with zero app changes. These four additions make the agent dramatically faster and let it verify things the DOM can't express — they're what turn Reticle from "usable" into "magic." All are dev-only.
360
+ The basics above work with zero app changes. These four additions make the agent dramatically faster and let it verify things the DOM can't express; they're what turn Reticle from "usable" into "magic." All are dev-only.
345
361
 
346
362
  **1. Stable `data-testid` on key elements.** Agents target testids more reliably than visible text (which changes with copy/i18n). Reticle matches testids _exactly_.
347
363
 
@@ -349,7 +365,7 @@ The basics above work with zero app changes. These four additions make the agent
349
365
  <button data-testid="refresh">Refresh</button>
350
366
  ```
351
367
 
352
- **2. `reticle.signal` for off-DOM facts.** When something matters but isn't visible — a save committed, a webhook arrived, an edit applied, an LLM caption finished — emit a signal the agent can assert on. This is the single highest-value instrumentation.
368
+ **2. `reticle.signal` for off-DOM facts.** When something matters but isn't visible (a save committed, a webhook arrived, an edit applied, an LLM caption finished), emit a signal the agent can assert on. This is the single highest-value instrumentation.
353
369
 
354
370
  ```ts
355
371
  import { reticle } from '@reticlehq/react';
@@ -357,9 +373,9 @@ onSaved(() => reticle.signal('order:saved', { id, total }));
357
373
  // agent: reticle_assert({ predicate: { kind: 'signal', name: 'order:saved', dataMatches: { id: '*' } } })
358
374
  ```
359
375
 
360
- > **Recommended:** instead of importing `reticle` into components, inject a `createReticleEmitter()` emitter and pair each commit with `commitAndSignal(...)` so the mutation↔signal can't drift — `reticle.signal` stays the primitive underneath. See [integration-patterns.md](integration-patterns.md).
376
+ > **Recommended:** instead of importing `reticle` into components, inject a `createReticleEmitter()` emitter and pair each commit with `commitAndSignal(...)` so the mutation↔signal can't drift. `reticle.signal` stays the primitive underneath. See [integration-patterns.md](integration-patterns.md).
361
377
 
362
- **3. `registerStore` so the agent reads state directly.** No need to broadcast a signal for every fact — expose the store and the agent reads it via `reticle_state`.
378
+ **3. `registerStore` so the agent reads state directly.** No need to broadcast a signal for every fact: expose the store and the agent reads it via `reticle_state`.
363
379
 
364
380
  ```ts
365
381
  import { registerStore } from '@reticlehq/react';
@@ -379,11 +395,11 @@ registerCapabilities({
379
395
  // agent: reticle_capabilities() → the whole testable surface
380
396
  ```
381
397
 
382
- > **Multi-domain apps:** prefer `registerReticleDomain({ testids, signals, stores })` co-located in one `reticle.ts` per domain — each self-registers and `reticle_capabilities()` assembles the union, so there's no central map to forget. See [integration-patterns.md](integration-patterns.md).
398
+ > **Multi-domain apps:** prefer `registerReticleDomain({ testids, signals, stores })` co-located in one `reticle.ts` per domain. Each self-registers and `reticle_capabilities()` assembles the union, so there's no central map to forget. See [integration-patterns.md](integration-patterns.md).
383
399
 
384
400
  > Watch the agent work: pass `present: true` to `reticle.connect()` for a glowing border, a cursor that flies to targets, and a HUD; the agent can call `reticle_session {action:"narrate"}({ text })` to show its intent. See [usage §16](usage.md#16-presenter-mode-narration--fake-clock-watch--control).
385
401
 
386
- > **Hover-gated UI (tooltips, hover menus, pointer drag)?** Synthetic events can't trigger native `onMouseEnter`. Enable **real input** by launching your browser with `--remote-debugging-port=9222` and setting `RETICLE_CDP_URL` in the MCP server `env` — Reticle then drives real pointer input and `reticle_act` reports `inputMode:"real"`. See [usage §18](usage.md#18-real-input-mode--native-hover--drag).
402
+ > **Hover-gated UI (tooltips, hover menus, pointer drag)?** Synthetic events can't trigger native `onMouseEnter`. Enable **real input** by launching your browser with `--remote-debugging-port=9222` and setting `RETICLE_CDP_URL` in the MCP server `env`. Reticle then drives real pointer input and `reticle_act` reports `inputMode:"real"`. See [usage §18](usage.md#18-real-input-mode-native-hover--drag).
387
403
 
388
404
  ---
389
405
 
@@ -391,10 +407,10 @@ registerCapabilities({
391
407
 
392
408
  Once the loop works, these turn ad-hoc runs into a maintained suite:
393
409
 
394
- - **[Flows, recorder & self-healing](flows.md)** — record a golden path once; Reticle saves it to a git-checked `.reticle/` flow anchored on testid+signal, replays it (with legible drift), and `reticle_flow_heal` repairs renamed anchors.
395
- - **[Testing with `@reticlehq/test`](testing.md)** — declarative `reticleTest` specs you run headless / in CI; flows can _become_ the specs.
396
- - **[Human-in-the-loop control](human-control.md)** — with `present: true`, pause / message / end the agent from the floating panel.
397
- - **[Integration patterns](integration-patterns.md)** — the recommended zero-prod-bundle emit adapter, store-layer signals, and incremental adoption.
410
+ - **[Flows, recorder & self-healing](flows.md)**: record a golden path once; Reticle saves it to a git-checked `.reticle/` flow anchored on testid+signal, replays it (with legible drift), and `reticle_flow_heal` repairs renamed anchors.
411
+ - **[Testing with `@reticlehq/test`](testing.md)** gives you declarative `reticleTest` specs you run headless / in CI; flows can _become_ the specs.
412
+ - **[Human-in-the-loop control](human-control.md)**: with `present: true`, pause / message / end the agent from the floating panel.
413
+ - **[Integration patterns](integration-patterns.md)** covers the recommended zero-prod-bundle emit adapter, store-layer signals, and incremental adoption.
398
414
 
399
415
  ---
400
416
 
@@ -404,22 +420,22 @@ Everything below comes from the `@reticlehq/react` kit plus your framework's bui
404
420
 
405
421
  | Stack | SDK connect | Source mapping |
406
422
  | --- | --- | --- |
407
- | Vite + React (any) | `reticle()` plugin (auto) — or `connect()` | `reticle()` plugin handles it (incl. React 19) |
423
+ | Vite + React (any) | `reticle()` plugin (auto), or `connect()` | `reticle()` plugin handles it (incl. React 19) |
408
424
  | Next.js (app router) | `ReticleDev` client component in layout (dev) | `@reticlehq/next` (`withReticle`) → component + file:line |
409
425
  | SvelteKit | `src/hooks.client.ts` (written by `reticle init`) | `reticle()` plugin stamps `.svelte` → file:line |
410
- | Vanilla / plain HTML | `reticle.connect()` at boot (dev) | none — refs and testids only |
426
+ | Vanilla / plain HTML | `reticle.connect()` at boot (dev) | none (refs and testids only) |
411
427
 
412
428
  ### What Svelte support is, and what it is not
413
429
 
414
430
  `reticle init` detects SvelteKit and writes both halves: a client hook that calls `connect()` (SvelteKit renders through `app.html`, so the plugin's HTML injection never fires) and `reticle()` in `vite.config`, which is what stamps `data-reticle-source`.
415
431
 
416
- **You get** `file:line` on every element in a `.svelte` component, plus everything the framework-agnostic core already gave you — DOM, network, console, routing, storage, actions — and `svelteStore` for reading a Svelte store (see [usage](usage.md)).
432
+ **You get** `file:line` on every element in a `.svelte` component, plus everything the framework-agnostic core already gave you (DOM, network, console, routing, storage, actions), and `svelteStore` for reading a Svelte store (see [usage](usage.md)).
417
433
 
418
434
  **You do not get** component identity. `@reticlehq/react` walks the fiber tree to answer "which component rendered this element"; there is no Svelte equivalent, so snapshots carry the file and line but no component name. Stamping targets Svelte 5's compiler AST and also accepts Svelte 4's; `.svelte.ts` runes modules are code rather than markup and are not stamped.
419
435
 
420
- **It is still unverified.** There is no SvelteKit app in `apps/` and no CI gate for one, so nothing would tell us when this breaks — `reticle init` says so out loud in its plan. React, Next.js, Remix and Astro each have an app and a gate. Treat SvelteKit as wired and plausible, not as supported.
436
+ **It is still unverified.** There is no SvelteKit app in `apps/` and no CI gate for one, so nothing would tell us when this breaks; `reticle init` says so out loud in its plan. React, Next.js, Remix and Astro each have an app and a gate. Treat SvelteKit as wired and plausible, not as supported.
421
437
 
422
- **Vue is not supported.** The SDK is framework-agnostic so `connect()` may work, and `piniaStore` will read a Pinia store, but there is no detection, no `.vue` source stamping and no CI gate.
438
+ **Vue is install-gated, drive-unverified.** `init` detects a Vue app (as a Vite app with Vue as the UI library) and the install gate scaffolds one from scratch, runs `init`, boots it and waits for a session, so the setup is proven. What is not proven is the drive: there is no Vue example app in CI. `piniaStore` reads a Pinia store, and everything the framework-neutral core provides works. What you do not get is a `source` field, because source stamping covers JSX and Svelte components and a `.vue` single-file component is neither. See [Frameworks](/frameworks) for the full status table.
423
439
 
424
440
  ---
425
441
 
@@ -427,13 +443,13 @@ Everything below comes from the `@reticlehq/react` kit plus your framework's bui
427
443
 
428
444
  **`reticle_sessions` is empty / "no browser session connected"**
429
445
 
430
- - Run **`reticle status`** — it shows whether the daemon is up and which tabs are connected (url, health, pending flagged bugs) at a glance. No connected sessions means the SDK isn't reaching the bridge.
446
+ - Run **`reticle status`**. It shows whether the daemon is up and which tabs are connected (url, health, pending flagged bugs) at a glance. No connected sessions means the SDK isn't reaching the bridge.
431
447
  - Is your app actually running and open in a browser tab?
432
448
  - Is `reticle.connect()` running? (Check it's inside your dev guard and the guard is true.)
433
449
  - Port mismatch? If you set `RETICLE_PORT`, pass the same URL to `reticle.connect({ url: 'ws://localhost:<port>/reticle' })`.
434
- - Need to restart the daemon? **`reticle stop`** cleans it up — no `pkill` needed.
450
+ - Need to restart the daemon? **`reticle stop`** cleans it up, no `pkill` needed.
435
451
 
436
- The errors Reticle returns to the agent now carry a `recovery` hint for this exact situation (and for multiple/unknown sessions, a throttled tab, a missing baseline) — so the agent knows the next move.
452
+ The errors Reticle returns to the agent now carry a `recovery` hint for this exact situation (and for multiple/unknown sessions, a throttled tab, a missing baseline), so the agent knows the next move.
437
453
 
438
454
  **The agent can't find an element**
439
455
 
@@ -452,13 +468,13 @@ The errors Reticle returns to the agent now carry a `recovery` hint for this exa
452
468
 
453
469
  **Nothing should run in production**
454
470
 
455
- - Keep `reticle.connect()` behind a dev guard (`import.meta.env.DEV` / `NODE_ENV`). The package is side-effect free and tree-shakes out when unused. As a backstop, `connect()` also self-disables when the build reports `NODE_ENV=production` (so an SSR healthcheck or a prod bundle opened on localhost won't activate it) — pass `allowInProduction: true` only for a deliberate prod diagnostic.
471
+ - Keep `reticle.connect()` behind a dev guard (`import.meta.env.DEV` / `NODE_ENV`). The package is side-effect free and tree-shakes out when unused. As a backstop, `connect()` also self-disables when the build reports `NODE_ENV=production` (so an SSR healthcheck or a prod bundle opened on localhost won't activate it). Pass `allowInProduction: true` only for a deliberate prod diagnostic.
456
472
 
457
473
  ## Installing alongside a Next.js or React prerelease
458
474
 
459
- `@reticlehq/next` declares `peer next >=13`, and `@reticlehq/react` declares `peer react >=18`. If your app runs a **prerelease** — a Next.js canary/preview (`16.3.0-preview.9`) or a React RC — npm will refuse the install with `ERESOLVE`.
475
+ `@reticlehq/next` declares `peer next >=13`, and `@reticlehq/react` declares `peer react >=18`. If your app runs a **prerelease**, such as a Next.js canary/preview (`16.3.0-preview.9`) or a React RC, npm will refuse the install with `ERESOLVE`.
460
476
 
461
- That is npm's semver rule, not a Reticle restriction: a prerelease version satisfies a range only when some comparator shares its exact `major.minor.patch`. No floor-style range accepts it — verified, including `*`. Marking the peer optional does not help either, because npm still version-checks a peer that is present.
477
+ That is npm's semver rule, not a Reticle restriction: a prerelease version satisfies a range only when some comparator shares its exact `major.minor.patch`. No floor-style range accepts it (verified, including `*`). Marking the peer optional does not help either, because npm still version-checks a peer that is present.
462
478
 
463
479
  Install with either of these instead. Both are safe; the floor is a real minimum, not a maximum:
464
480
 
@@ -467,3 +483,39 @@ npm install @reticlehq/next --legacy-peer-deps
467
483
  # or use pnpm, whose peer resolution does not hard-fail here
468
484
  pnpm add @reticlehq/next
469
485
  ```
486
+
487
+ ---
488
+
489
+ ## Frequently asked questions
490
+
491
+ ### What exactly does `reticle init` change in my project?
492
+
493
+ Four things, and none of them are mysterious: a `.reticle.json` project config, your build config (the `reticle()` Vite plugin, or `withReticle` in `next.config`), a dev-only capabilities file at `src/reticle-dev.ts` (or `app/reticle-dev.tsx` on Next.js), and your agent's rule files. It also registers the MCP server globally, which is a once-per-machine step rather than a per-project one. Run `npx @reticlehq/server init --dry-run` first to see the exact plan before anything is written.
494
+
495
+ ### Do I have to re-register the MCP server for every project?
496
+
497
+ No. The bridge and MCP server are a single process serving all your projects, so `init` registers it at user scope. Every later project starts with the tools already there. Only the SDK wiring is per project.
498
+
499
+ ### Why do the Reticle tools not appear after installing?
500
+
501
+ Because your agent read its MCP server list at startup, before Reticle existed, and no slash command re-reads it. Restart the agent process: quit and reopen Claude Code, reload the window in Cursor, or press Start in `.vscode/mcp.json` in VS Code. `/mcp` only manages servers that are already loaded, so it cannot discover a new one.
502
+
503
+ ### Which Node version do I need?
504
+
505
+ Node 20 or newer.
506
+
507
+ ### Do I need the React adapter?
508
+
509
+ No, it is optional enrichment and the core works without it. What it adds is component identity, meaning `reticle_inspect` can say which React component rendered an element. Combined with a build plugin that stamps source, that is what turns a DOM node into `src/components/Login.tsx:81`, which is the difference between an agent knowing something failed and knowing which file to open.
510
+
511
+ ### Can I run several apps against Reticle at once?
512
+
513
+ Yes, as long as each connection has a unique session id, which `SESSION_AUTO` gives you automatically. Tool calls target the focused or most recently active app, or you can pass an explicit `sessionId`. If you want two projects fully isolated, give each its own bridge port and set the same port in both the MCP server config and the app's connection.
514
+
515
+ ### Will Reticle end up in my production bundle?
516
+
517
+ No. Keep `connect()` behind a dev guard (`import.meta.env.DEV` or a `NODE_ENV` check); the packages are side-effect free and tree-shake out when unused. The Vite plugin uses `apply: 'serve'`, so it is dropped from `vite build` entirely. As a backstop, `connect()` also self-disables when the build reports `NODE_ENV=production`.
518
+
519
+ ### Why is my install failing with `ERESOLVE`?
520
+
521
+ You are almost certainly on a Next.js canary or a React RC. That is npm's semver rule for prereleases, not a Reticle restriction. Install with `--legacy-peer-deps`, or use pnpm, whose peer resolution does not hard-fail here. See [Installing alongside a Next.js or React prerelease](#installing-alongside-a-nextjs-or-react-prerelease) above.