@reticlehq/server 2.6.0 → 2.8.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 (276) hide show
  1. package/README.md +14 -5
  2. package/SKILL.md +251 -33
  3. package/dist/bridge/bridge.d.ts +6 -1
  4. package/dist/bridge/bridge.js +27 -4
  5. package/dist/capsule/causal-summary.d.ts +20 -0
  6. package/dist/capsule/causal-summary.js +31 -6
  7. package/dist/capsule/divergence.js +7 -1
  8. package/dist/cli/chromium-hint.d.ts +68 -0
  9. package/dist/cli/chromium-hint.js +145 -0
  10. package/dist/cli/cli-doctor.js +31 -36
  11. package/dist/cli/cli-kill.d.ts +58 -0
  12. package/dist/cli/cli-kill.js +162 -0
  13. package/dist/cli/cli-launch.d.ts +18 -0
  14. package/dist/cli/cli-launch.js +32 -1
  15. package/dist/cli/cli-parse.d.ts +16 -1
  16. package/dist/cli/cli-parse.js +32 -9
  17. package/dist/cli/cli-port.d.ts +8 -0
  18. package/dist/cli/cli-port.js +36 -1
  19. package/dist/cli/cli-verify.js +2 -2
  20. package/dist/cli/doctor-daemon-line.js +10 -1
  21. package/dist/cli/doctor-sessions-line.d.ts +12 -0
  22. package/dist/cli/doctor-sessions-line.js +66 -0
  23. package/dist/cli/port-holder.d.ts +10 -0
  24. package/dist/cli/port-holder.js +26 -2
  25. package/dist/cli.js +139 -23
  26. package/dist/crawl/crawl-tools.js +12 -2
  27. package/dist/crawl/crawl.d.ts +13 -0
  28. package/dist/crawl/crawl.js +50 -2
  29. package/dist/daemon/daemon-resilience.js +2 -2
  30. package/dist/daemon/loopback-alias.d.ts +32 -0
  31. package/dist/daemon/loopback-alias.js +80 -0
  32. package/dist/events/contradictions.d.ts +14 -0
  33. package/dist/events/contradictions.js +26 -17
  34. package/dist/events/predicate-eval.d.ts +26 -3
  35. package/dist/events/predicate-eval.js +284 -95
  36. package/dist/events/predicate-parse.d.ts +1 -1
  37. package/dist/events/predicate-parse.js +18 -4
  38. package/dist/events/predicate.js +170 -18
  39. package/dist/events/ring-buffer.d.ts +8 -0
  40. package/dist/events/ring-buffer.js +34 -1
  41. package/dist/flows/flow-classify.js +1 -1
  42. package/dist/flows/flow-tools.js +12 -4
  43. package/dist/flows/heal-run.js +1 -1
  44. package/dist/honesty/blind-spots.js +6 -2
  45. package/dist/honesty/honesty.d.ts +11 -0
  46. package/dist/honesty/honesty.js +9 -1
  47. package/dist/honesty/unsettled.d.ts +40 -0
  48. package/dist/honesty/unsettled.js +78 -0
  49. package/dist/honesty/verified.d.ts +7 -0
  50. package/dist/honesty/verified.js +27 -4
  51. package/dist/http-server.js +11 -1
  52. package/dist/index.js +29 -2
  53. package/dist/init/agent-rules.d.ts +16 -0
  54. package/dist/init/agent-rules.js +96 -17
  55. package/dist/init/astro-patch.d.ts +2 -1
  56. package/dist/init/astro-patch.js +11 -5
  57. package/dist/init/capabilities.d.ts +22 -0
  58. package/dist/init/capabilities.js +76 -0
  59. package/dist/init/confirm.d.ts +96 -0
  60. package/dist/init/confirm.js +138 -0
  61. package/dist/init/cra.js +3 -2
  62. package/dist/init/detect.d.ts +8 -0
  63. package/dist/init/detect.js +15 -0
  64. package/dist/init/non-js-project.d.ts +27 -0
  65. package/dist/init/non-js-project.js +84 -0
  66. package/dist/init/plan-framework.d.ts +9 -0
  67. package/dist/init/plan-framework.js +72 -16
  68. package/dist/init/plan.d.ts +40 -4
  69. package/dist/init/plan.js +183 -43
  70. package/dist/init/run.d.ts +24 -2
  71. package/dist/init/run.js +181 -46
  72. package/dist/init/slash-command.d.ts +1 -1
  73. package/dist/init/slash-command.js +46 -6
  74. package/dist/init/snippets.d.ts +64 -5
  75. package/dist/init/snippets.js +220 -31
  76. package/dist/init/vite-config.js +29 -3
  77. package/dist/input/network-mock-tools.d.ts +19 -0
  78. package/dist/input/network-mock-tools.js +29 -4
  79. package/dist/license/license.js +1 -1
  80. package/dist/mcp/mcp-proxy.d.ts +69 -3
  81. package/dist/mcp/mcp-proxy.js +215 -23
  82. package/dist/mcp/mcp.js +58 -7
  83. package/dist/mcp/proxy-lifecycle.d.ts +20 -1
  84. package/dist/mcp/proxy-lifecycle.js +22 -1
  85. package/dist/pool/browser-pool.d.ts +22 -2
  86. package/dist/pool/browser-pool.js +44 -4
  87. package/dist/pool/playwright-launcher.js +10 -2
  88. package/dist/project/learned-routes.d.ts +24 -0
  89. package/dist/project/learned-routes.js +63 -0
  90. package/dist/project/project-store.d.ts +6 -0
  91. package/dist/project/project-store.js +25 -1
  92. package/dist/session/live-control-tools.js +39 -2
  93. package/dist/session/live-control.d.ts +7 -9
  94. package/dist/session/live-control.js +34 -1
  95. package/dist/session/no-session-diagnosis.d.ts +23 -4
  96. package/dist/session/no-session-diagnosis.js +128 -44
  97. package/dist/session/no-session-watch.js +12 -0
  98. package/dist/session/ref-provenance.d.ts +54 -0
  99. package/dist/session/ref-provenance.js +61 -0
  100. package/dist/session/review-store.d.ts +19 -0
  101. package/dist/session/review-store.js +34 -5
  102. package/dist/session/session-lease.js +1 -1
  103. package/dist/session/session-manager.d.ts +9 -0
  104. package/dist/session/session-manager.js +11 -0
  105. package/dist/session/session.d.ts +8 -0
  106. package/dist/session/session.js +12 -0
  107. package/dist/status-payload.d.ts +13 -1
  108. package/dist/status-payload.js +6 -1
  109. package/dist/telemetry/app-instrumented.d.ts +37 -0
  110. package/dist/telemetry/app-instrumented.js +54 -0
  111. package/dist/telemetry/automation-hint.d.ts +17 -0
  112. package/dist/telemetry/automation-hint.js +71 -0
  113. package/dist/telemetry/bug-found.d.ts +0 -20
  114. package/dist/telemetry/bug-found.js +38 -21
  115. package/dist/telemetry/cli-telemetry.js +7 -1
  116. package/dist/telemetry/daemon-telemetry.d.ts +4 -4
  117. package/dist/telemetry/daemon-telemetry.js +8 -4
  118. package/dist/telemetry/feedback.js +1 -1
  119. package/dist/telemetry/init-telemetry.js +9 -1
  120. package/dist/telemetry/install-source.d.ts +36 -0
  121. package/dist/telemetry/install-source.js +40 -0
  122. package/dist/telemetry/run-telemetry.js +4 -1
  123. package/dist/telemetry/session-metrics.d.ts +3 -3
  124. package/dist/telemetry/session-metrics.js +9 -4
  125. package/dist/telemetry/telemetry.d.ts +7 -1
  126. package/dist/telemetry/telemetry.js +17 -2
  127. package/dist/telemetry/tool-refused.d.ts +19 -0
  128. package/dist/telemetry/tool-refused.js +55 -0
  129. package/dist/telemetry/verification-of.js +72 -8
  130. package/dist/temp-dir.d.ts +27 -0
  131. package/dist/temp-dir.js +46 -0
  132. package/dist/tools/act-tools.js +77 -11
  133. package/dist/tools/assert-grade.d.ts +1 -1
  134. package/dist/tools/assert-grade.js +27 -5
  135. package/dist/tools/assert-verdict.js +25 -1
  136. package/dist/tools/contract-tools.js +4 -0
  137. package/dist/tools/dynamic-tools.js +1 -1
  138. package/dist/tools/error-recovery.d.ts +10 -0
  139. package/dist/tools/error-recovery.js +70 -5
  140. package/dist/tools/feedback-invite.js +1 -1
  141. package/dist/tools/invoke-tool.js +149 -32
  142. package/dist/tools/lease-tools.d.ts +29 -1
  143. package/dist/tools/lease-tools.js +58 -5
  144. package/dist/tools/navigate-result.js +1 -1
  145. package/dist/tools/observe-tools.js +1 -1
  146. package/dist/tools/observed-nothing.js +1 -1
  147. package/dist/tools/react-grace.d.ts +48 -0
  148. package/dist/tools/react-grace.js +87 -0
  149. package/dist/tools/read-tools.js +20 -4
  150. package/dist/tools/settle-in-flight.d.ts +79 -0
  151. package/dist/tools/settle-in-flight.js +106 -0
  152. package/dist/tools/snapshot-delta.d.ts +7 -0
  153. package/dist/tools/snapshot-delta.js +130 -6
  154. package/dist/tools/tool-kit.d.ts +52 -5
  155. package/dist/tools/tool-kit.js +55 -24
  156. package/dist/tools/tool-surface.js +28 -9
  157. package/dist/tools/tools.js +38 -4
  158. package/dist/visual/capture-cleanup.d.ts +16 -0
  159. package/dist/visual/capture-cleanup.js +39 -0
  160. package/dist/visual/visual-tools.d.ts +18 -0
  161. package/dist/visual/visual-tools.js +24 -5
  162. package/docs/README.md +56 -11
  163. package/docs/actions.mdx +87 -0
  164. package/docs/agent-cheatsheet.md +102 -26
  165. package/docs/architecture.md +46 -35
  166. package/docs/benchmarks.md +76 -72
  167. package/docs/best-practices.mdx +211 -0
  168. package/docs/capabilities.mdx +387 -0
  169. package/docs/cli/affected.mdx +51 -0
  170. package/docs/cli/capsules.mdx +69 -0
  171. package/docs/cli/cloud.mdx +127 -0
  172. package/docs/cli/doctor.mdx +76 -0
  173. package/docs/cli/drive.mdx +55 -0
  174. package/docs/cli/feedback.mdx +67 -0
  175. package/docs/cli/gate.mdx +64 -0
  176. package/docs/cli/help.mdx +92 -0
  177. package/docs/cli/hunt.mdx +57 -0
  178. package/docs/cli/identify.mdx +62 -0
  179. package/docs/cli/init.mdx +94 -0
  180. package/docs/cli/kill.mdx +91 -0
  181. package/docs/cli/license.mdx +48 -0
  182. package/docs/cli/mcp.mdx +61 -0
  183. package/docs/cli/open.mdx +68 -0
  184. package/docs/cli/restart.mdx +60 -0
  185. package/docs/cli/rollback.mdx +35 -0
  186. package/docs/cli/serve.mdx +62 -0
  187. package/docs/cli/status.mdx +64 -0
  188. package/docs/cli/stop.mdx +56 -0
  189. package/docs/cli/telemetry.mdx +57 -0
  190. package/docs/cli/update.mdx +52 -0
  191. package/docs/cli/verify.mdx +57 -0
  192. package/docs/cli/version.mdx +39 -0
  193. package/docs/cli/watch.mdx +55 -0
  194. package/docs/cli.mdx +219 -0
  195. package/docs/debugging.md +13 -9
  196. package/docs/deploy-checks.md +15 -11
  197. package/docs/desktop-apps.md +49 -40
  198. package/docs/desktop.mdx +188 -0
  199. package/docs/docs.json +363 -0
  200. package/docs/enterprise.md +20 -16
  201. package/docs/faq.mdx +88 -0
  202. package/docs/fixtures.md +11 -7
  203. package/docs/flows.md +26 -22
  204. package/docs/for-agents.md +71 -0
  205. package/docs/frameworks.mdx +199 -0
  206. package/docs/gate-plan.md +45 -41
  207. package/docs/gates.md +23 -19
  208. package/docs/getting-started.md +76 -65
  209. package/docs/human-control.md +22 -18
  210. package/docs/index.mdx +101 -0
  211. package/docs/install-agentic.mdx +215 -0
  212. package/docs/install-manual.mdx +194 -0
  213. package/docs/instrumentation.mdx +232 -0
  214. package/docs/integration-patterns.md +38 -34
  215. package/docs/local-registry.md +16 -12
  216. package/docs/multi-agent-testing.md +16 -12
  217. package/docs/packages/babel-plugin.mdx +70 -0
  218. package/docs/packages/browser.mdx +111 -0
  219. package/docs/packages/core.mdx +106 -0
  220. package/docs/packages/electron.mdx +87 -0
  221. package/docs/packages/eslint-plugin.mdx +84 -0
  222. package/docs/packages/next.mdx +91 -0
  223. package/docs/packages/react.mdx +91 -0
  224. package/docs/packages/server.mdx +98 -0
  225. package/docs/packages/tauri.mdx +63 -0
  226. package/docs/packages/test.mdx +108 -0
  227. package/docs/packages/vite-plugin.mdx +96 -0
  228. package/docs/packages.mdx +217 -0
  229. package/docs/platform-integration.md +33 -26
  230. package/docs/predicates.mdx +179 -0
  231. package/docs/quickstart.mdx +211 -0
  232. package/docs/recipes.mdx +242 -0
  233. package/docs/skill-file.mdx +105 -0
  234. package/docs/state-management.mdx +197 -0
  235. package/docs/style.css +14 -0
  236. package/docs/system-map.md +47 -32
  237. package/docs/telemetry-contract.md +101 -46
  238. package/docs/telemetry-events.mdx +430 -0
  239. package/docs/telemetry.md +63 -50
  240. package/docs/testing.md +10 -6
  241. package/docs/token-efficiency.md +30 -26
  242. package/docs/tools-act-and-wait.mdx +123 -0
  243. package/docs/tools-act-sequence.mdx +120 -0
  244. package/docs/tools-act.mdx +82 -0
  245. package/docs/tools-assert.mdx +70 -0
  246. package/docs/tools-console.mdx +59 -0
  247. package/docs/tools-inspect.mdx +85 -0
  248. package/docs/tools-navigate.mdx +63 -0
  249. package/docs/tools-network.mdx +86 -0
  250. package/docs/tools-observe.mdx +89 -0
  251. package/docs/tools-overview.mdx +157 -0
  252. package/docs/tools-query.mdx +82 -0
  253. package/docs/tools-session-and-feedback.mdx +91 -0
  254. package/docs/tools-sessions.mdx +65 -0
  255. package/docs/tools-snapshot.mdx +75 -0
  256. package/docs/tools-state.mdx +81 -0
  257. package/docs/tools-tools-and-run.mdx +125 -0
  258. package/docs/tools-wait-for.mdx +66 -0
  259. package/docs/troubleshooting.mdx +193 -0
  260. package/docs/usage.md +175 -153
  261. package/docs/vs-chrome-devtools-mcp.mdx +82 -0
  262. package/docs/vs-playwright-mcp.mdx +85 -0
  263. package/docs/vs-screenshots.mdx +96 -0
  264. package/docs/what-is-reticle.mdx +63 -0
  265. package/docs/why-reticle.mdx +107 -0
  266. package/package.json +2 -2
  267. package/docs/matrix/2.5.0/claude-code-machine.json +0 -19
  268. package/docs/matrix/2.5.0/codex-machine.json +0 -20
  269. package/docs/matrix/2.5.0/cursor-machine.json +0 -22
  270. package/docs/matrix/2.5.0/gemini-machine.json +0 -22
  271. package/docs/matrix/2.5.0/opencode-machine.json +0 -22
  272. package/docs/matrix/2.5.0/vscode-machine.json +0 -22
  273. package/docs/matrix/2.5.0/windsurf-machine.json +0 -22
  274. package/docs/matrix/MATRIX.md +0 -26
  275. package/docs/matrix/README.md +0 -59
  276. package/docs/mint.json +0 -61
package/docs/gates.md CHANGED
@@ -1,4 +1,8 @@
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
+ ---
2
6
 
3
7
  > **One question this file answers:** _I changed some files. Which command do I run?_
4
8
  >
@@ -8,7 +12,7 @@
8
12
 
9
13
  ## 0. Last verified
10
14
 
11
- Every gate below was executed end to end against `main` on **2026-08-11** (macOS, M-series, `v2.5.0`). A green row means somebody watched it go green, not that it is supposed to be green.
15
+ Every gate below was executed end to end against `main` on **2026-08-12** (macOS, M-series, `v2.6.0`). A green row means somebody watched it go green, not that it is supposed to be green.
12
16
 
13
17
  | Gate | Result | Wall clock |
14
18
  | --- | --- | --- |
@@ -20,14 +24,14 @@ Every gate below was executed end to end against `main` on **2026-08-11** (macOS
20
24
  | `pnpm test:integration` | ✅ 12/12 | 17s |
21
25
  | `pnpm test:e2e` | ✅ 32/32 specs + soak 60/60 answered | **490s** |
22
26
  | `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
+ | `node apps/e2e/soak.mjs --self-check` | ✅ | `<1s` |
28
+ | `node apps/e2e/matrix.mjs --self-check` | ✅ | `<1s` |
25
29
  | `pnpm matrix:compat --only cursor` | ✅ 4/4 | ~10s |
26
- | `pnpm bench` | ✅ 10/10 — **was failing before this sweep** (see below) | 279s |
30
+ | `pnpm bench` | ✅ 10/10 (**was failing before this sweep**, see below) | 279s |
27
31
 
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.
32
+ **`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
33
 
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.
34
+ `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
35
 
32
36
  ---
33
37
 
@@ -39,13 +43,13 @@ Find the row that matches what you changed. Run its commands. That is the whole
39
43
  | --- | --- | --- |
40
44
  | **Anything at all** | `pnpm lint && pnpm typecheck && pnpm test:unit` | ~2 min |
41
45
  | 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 |
46
+ | `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
47
  | `@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`) | — |
48
+ | 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
49
  | `packages/tauri` (Rust) | ↑ **and** `cd packages/tauri && cargo fmt --check && cargo clippy --all-targets -- -D warnings` | +~2 min |
46
50
  | Docs, README, comments only | `pnpm format:check` | seconds |
47
51
 
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.
52
+ **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
53
 
50
54
  ---
51
55
 
@@ -59,16 +63,16 @@ Each gate exists because the ones above it are blind to something. That blindnes
59
63
  | **Lint** | `pnpm lint` | style rules, plus the dependency-boundary and lossy-transform guards | anything not expressible as a rule | `verify` |
60
64
  | **Typecheck** | `pnpm typecheck` | types agree across package boundaries | runtime behaviour | `verify` |
61
65
  | **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` |
66
+ | **Format** | `pnpm format:check` | Prettier | n/a | `verify` |
63
67
  | **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` |
68
+ | **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` |
65
69
  | **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` |
70
+ | **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
71
  | **Matrix records** | `pnpm matrix:validate` | every submitted client-compat record is well-formed | whether the client actually works | `matrix-records` |
68
72
  | **Windows** | (CI only) | that the code runs at all on the majority platform | e2e; Windows is unit-only | `windows` |
69
73
  | **Rust** | `cargo fmt/clippy/check` | `packages/tauri` compiles and lints on Linux, macOS, and cross-checks Windows | everything JS | `rust`, `rust-macos` |
70
74
 
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.
75
+ **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
76
 
73
77
  ### Guards that self-test
74
78
 
@@ -99,7 +103,7 @@ These are real and they work; they are not on the PR path, so they only run when
99
103
 
100
104
  ## 4. `bench/` is not a gate
101
105
 
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.
106
+ `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
107
 
104
108
  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
109
 
@@ -109,9 +113,9 @@ The one exception worth knowing: `pnpm bench` + `pnpm bench:gate` is a working r
109
113
 
110
114
  Sometimes it is. The specific failures worth recognising:
111
115
 
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.
116
+ - **`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.
117
+ - **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.
118
+ - **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).
119
+ - **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
120
 
117
121
  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,26 @@
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
+ ---
2
6
 
3
- This walks you from zero to your agent verifying your app — step by step, with real code for real frameworks. ~10 minutes.
7
+ > **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.
8
+ >
9
+ > 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.
10
+
11
+ This walks you from zero to your agent verifying your app, step by step, with real code for real frameworks. ~10 minutes.
4
12
 
5
13
  - [What you're setting up](#what-youre-setting-up)
6
14
  - [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)
15
+ - [Step 1: Connect your coding agent (MCP)](#step-1-connect-your-coding-agent-mcp)
16
+ - [Step 2: Embed the SDK in your app](#step-2-embed-the-sdk-in-your-app)
9
17
  - [Vite + React](#vite--react)
10
18
  - [Next.js](#nextjs)
11
19
  - [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)
20
+ - [Step 3: (React) component and source-file mapping](#step-3-react-component-and-source-file-mapping)
21
+ - [Step 4: Run it and verify the connection](#step-4-run-it-and-verify-the-connection)
22
+ - [Step 5: Your first verification](#step-5-your-first-verification)
23
+ - [Step 6: Make your app agent-legible](#step-6-make-your-app-agent-legible-optional-high-leverage)
16
24
  - [Common setups at a glance](#common-setups-at-a-glance)
17
25
  - [Troubleshooting](#troubleshooting)
18
26
 
@@ -22,18 +30,21 @@ This walks you from zero to your agent verifying your app — step by step, with
22
30
 
23
31
  Three pieces, each tiny:
24
32
 
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 └─────────────────────┘
33
+ ```mermaid
34
+ flowchart LR
35
+ A["coding agent<br/>(Claude Code)"]
36
+ S["reticle bridge + server<br/>npx @reticlehq/server"]
37
+ B["your app<br/>+ the Reticle SDK<br/>(dev only)"]
38
+
39
+ A <-- "MCP over stdio" --> S
40
+ S <-- "WebSocket<br/>localhost:4400" --> B
30
41
  ```
31
42
 
32
43
  Three pieces, each from the package for its audience:
33
44
 
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`).
45
+ 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.
46
+ 2. **The SDK**: `import { reticle } from '@reticlehq/react'`, a few lines in your app's dev entry point.
47
+ 3. **(Optional) React adapter + source-mapping**, so `reticle_inspect` can tell the agent which component/file to edit (also from `@reticlehq/react`).
37
48
 
38
49
  Everything is **dev-only** and **localhost-only**. It's tree-shaken out of production builds.
39
50
 
@@ -45,35 +56,35 @@ Everything is **dev-only** and **localhost-only**. It's tree-shaken out of produ
45
56
 
46
57
  ---
47
58
 
48
- ## Fastest path — `reticle init`
59
+ ## Fastest path: `reticle init`
49
60
 
50
61
  From your project root:
51
62
 
52
63
  ```bash
53
- npx @reticlehq/server init
64
+ RETICLE_INSTALL_SOURCE=docs_site npx @reticlehq/server init
54
65
  ```
55
66
 
56
67
  It detects your framework, package manager, and React version, then:
57
68
 
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),
69
+ - **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,
70
+ - **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
71
  - 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,
72
+ - **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
73
  - **Next / other:** creates the dev component and prints the exact `withReticle` / mount / connect snippets to paste (it never half-edits a build config).
63
74
 
64
75
  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
76
 
66
77
  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`.
67
78
 
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.
79
+ 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
80
 
70
81
  ---
71
82
 
72
- ## Step 1 — Connect your coding agent (MCP), once
83
+ ## Step 1: Connect your coding agent (MCP), once
73
84
 
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.
85
+ 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
86
 
76
- **Claude Code** — one command:
87
+ **Claude Code**, one command:
77
88
 
78
89
  ```bash
79
90
  claude mcp add reticle -s user -- npx @reticlehq/server mcp
@@ -81,7 +92,7 @@ claude mcp add reticle -s user -- npx @reticlehq/server mcp
81
92
 
82
93
  (`reticle init` runs exactly this for you. `-s user` is what makes it global; drop it for a project-local registration instead.)
83
94
 
84
- **Cursor** — add to your global `~/.cursor/mcp.json` (not per-project; `reticle init` writes this for you):
95
+ **Cursor**: add to your global `~/.cursor/mcp.json` (not per-project; `reticle init` writes this for you):
85
96
 
86
97
  ```jsonc
87
98
  {
@@ -97,7 +108,7 @@ Other MCP clients (Windsurf, Claude Desktop, …) use the same `command`/`args`
97
108
 
98
109
  ---
99
110
 
100
- ## Step 2 — Embed the SDK in your app
111
+ ## Step 2: Embed the SDK in your app
101
112
 
102
113
  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
114
 
@@ -110,7 +121,7 @@ Then call `reticle.connect()` once, in dev only. Where you put it depends on you
110
121
 
111
122
  ### Vite + React
112
123
 
113
- **Recommended — the Vite plugin (one line, does everything).** Add `reticle()` to your `vite.config.ts`:
124
+ **Recommended: the Vite plugin (one line, does everything).** Add `reticle()` to your `vite.config.ts`:
114
125
 
115
126
  ```ts
116
127
  import { defineConfig } from 'vite';
@@ -122,7 +133,7 @@ export default defineConfig({
122
133
  });
123
134
  ```
124
135
 
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.)
136
+ 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
137
 
127
138
  <details>
128
139
  <summary>Prefer to wire it by hand instead of the plugin?</summary>
@@ -153,7 +164,7 @@ On React 19 you then also need the source-mapping Babel plugin from Step 3. The
153
164
 
154
165
  ### The pairing token (why some setups need one line more)
155
166
 
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.
167
+ 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
168
 
158
169
  - **Vite plugin users:** nothing to do. The plugin reads the token server-side and injects it into `connect()` for you.
159
170
  - **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 +233,12 @@ Or, with no build step, a script tag pointed at the bridge:
222
233
 
223
234
  ### Running multiple apps at once
224
235
 
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.
236
+ 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
237
 
227
238
  **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
239
 
229
240
  ```jsonc
230
- // project-b/.mcp.json — give this project its own bridge port
241
+ // project-b/.mcp.json: give this project its own bridge port
231
242
  {
232
243
  "mcpServers": {
233
244
  "reticle": {
@@ -240,33 +251,33 @@ It's common to have several apps open in dev — a few Next.js and React project
240
251
  ```
241
252
 
242
253
  ```ts
243
- // project-b's app — dial the same port
254
+ // project-b's app: dial the same port
244
255
  reticle.connect({ session: SESSION_AUTO, url: 'ws://localhost:4401/reticle' });
245
256
  ```
246
257
 
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:
258
+ **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
259
 
249
260
  ```ts
250
261
  // project-b/vite.config.ts
251
262
  plugins: [react(), reticle({ port: 4401 })],
252
263
  ```
253
264
 
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.)
265
+ 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
266
 
256
267
  ---
257
268
 
258
- ## Step 3 — (React) component & source-file mapping
269
+ ## Step 3: (React) component and source-file mapping
259
270
 
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.)
271
+ 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
272
 
262
273
  ```ts
263
274
  import { install as installReticleReact } from '@reticlehq/react';
264
275
  if (import.meta.env.DEV) installReticleReact(); // call before reticle.connect()
265
276
  ```
266
277
 
267
- **React ≤ 18:** that's all — it uses React's dev `_debugSource`.
278
+ **React ≤ 18:** that's all; it uses React's dev `_debugSource`.
268
279
 
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:
280
+ **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
281
 
271
282
  ```ts
272
283
  // vite.config.ts
@@ -278,7 +289,7 @@ export default defineConfig({
278
289
  });
279
290
  ```
280
291
 
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`):
292
+ > **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
293
  >
283
294
  > ```js
284
295
  > // next.config.mjs
@@ -292,7 +303,7 @@ export default defineConfig({
292
303
 
293
304
  ---
294
305
 
295
- ## Step 4 — Run it & verify the connection
306
+ ## Step 4: Run it and verify the connection
296
307
 
297
308
  1. Start your app's dev server as usual (`npm run dev`).
298
309
  2. Open it in the browser (the SDK connects when the page loads).
@@ -310,7 +321,7 @@ If the list is empty, see [Troubleshooting](#troubleshooting).
310
321
 
311
322
  ---
312
323
 
313
- ## Step 5 — Your first verification
324
+ ## Step 5: Your first verification
314
325
 
315
326
  Now just talk to your agent in plain language. For example:
316
327
 
@@ -326,22 +337,22 @@ reticle_query({ by: "role", value: "button", name: "Refresh" }) // → ref e12
326
337
  reticle_act({ ref: "e12", action: "click" }) // → { since: 920 }
327
338
 
328
339
  // verifies the reaction
329
- reticle_assert({ timeout_ms: 2000, predicate: { allOf: [
340
+ reticle_assert({ timeout_ms: 2000, predicate: { kind: "allOf", predicates: [
330
341
  { kind: "net", method: "GET", urlContains: "/api/dashboard", status: 200, since: 920 },
331
342
  { kind: "console", level: "error", absent: true }
332
343
  ]}})
333
344
  // → { pass: true }
334
345
  ```
335
346
 
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.
347
+ 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
348
 
338
349
  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
350
 
340
351
  ---
341
352
 
342
- ## Step 6 — Make your app agent-legible (optional, high-leverage)
353
+ ## Step 6: Make your app agent-legible (optional, high-leverage)
343
354
 
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.
355
+ 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
356
 
346
357
  **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
358
 
@@ -349,7 +360,7 @@ The basics above work with zero app changes. These four additions make the agent
349
360
  <button data-testid="refresh">Refresh</button>
350
361
  ```
351
362
 
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.
363
+ **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
364
 
354
365
  ```ts
355
366
  import { reticle } from '@reticlehq/react';
@@ -357,9 +368,9 @@ onSaved(() => reticle.signal('order:saved', { id, total }));
357
368
  // agent: reticle_assert({ predicate: { kind: 'signal', name: 'order:saved', dataMatches: { id: '*' } } })
358
369
  ```
359
370
 
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).
371
+ > **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
372
 
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`.
373
+ **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
374
 
364
375
  ```ts
365
376
  import { registerStore } from '@reticlehq/react';
@@ -379,11 +390,11 @@ registerCapabilities({
379
390
  // agent: reticle_capabilities() → the whole testable surface
380
391
  ```
381
392
 
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).
393
+ > **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
394
 
384
395
  > 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
396
 
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).
397
+ > **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
398
 
388
399
  ---
389
400
 
@@ -391,10 +402,10 @@ registerCapabilities({
391
402
 
392
403
  Once the loop works, these turn ad-hoc runs into a maintained suite:
393
404
 
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.
405
+ - **[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.
406
+ - **[Testing with `@reticlehq/test`](testing.md)** gives you declarative `reticleTest` specs you run headless / in CI; flows can _become_ the specs.
407
+ - **[Human-in-the-loop control](human-control.md)**: with `present: true`, pause / message / end the agent from the floating panel.
408
+ - **[Integration patterns](integration-patterns.md)** covers the recommended zero-prod-bundle emit adapter, store-layer signals, and incremental adoption.
398
409
 
399
410
  ---
400
411
 
@@ -404,20 +415,20 @@ Everything below comes from the `@reticlehq/react` kit plus your framework's bui
404
415
 
405
416
  | Stack | SDK connect | Source mapping |
406
417
  | --- | --- | --- |
407
- | Vite + React (any) | `reticle()` plugin (auto) — or `connect()` | `reticle()` plugin handles it (incl. React 19) |
418
+ | Vite + React (any) | `reticle()` plugin (auto), or `connect()` | `reticle()` plugin handles it (incl. React 19) |
408
419
  | Next.js (app router) | `ReticleDev` client component in layout (dev) | `@reticlehq/next` (`withReticle`) → component + file:line |
409
420
  | 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 |
421
+ | Vanilla / plain HTML | `reticle.connect()` at boot (dev) | none (refs and testids only) |
411
422
 
412
423
  ### What Svelte support is, and what it is not
413
424
 
414
425
  `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
426
 
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)).
427
+ **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
428
 
418
429
  **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
430
 
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.
431
+ **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
432
 
422
433
  **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.
423
434
 
@@ -427,13 +438,13 @@ Everything below comes from the `@reticlehq/react` kit plus your framework's bui
427
438
 
428
439
  **`reticle_sessions` is empty / "no browser session connected"**
429
440
 
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.
441
+ - 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
442
  - Is your app actually running and open in a browser tab?
432
443
  - Is `reticle.connect()` running? (Check it's inside your dev guard and the guard is true.)
433
444
  - 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.
445
+ - Need to restart the daemon? **`reticle stop`** cleans it up, no `pkill` needed.
435
446
 
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.
447
+ 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
448
 
438
449
  **The agent can't find an element**
439
450
 
@@ -452,13 +463,13 @@ The errors Reticle returns to the agent now carry a `recovery` hint for this exa
452
463
 
453
464
  **Nothing should run in production**
454
465
 
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.
466
+ - 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
467
 
457
468
  ## Installing alongside a Next.js or React prerelease
458
469
 
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`.
470
+ `@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
471
 
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.
472
+ 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
473
 
463
474
  Install with either of these instead. Both are safe; the floor is a real minimum, not a maximum:
464
475
 
@@ -1,39 +1,43 @@
1
- # Human-in-the-loop control — watch, steer, end the session
1
+ ---
2
+ title: 'Human-in-the-loop control'
3
+ description: 'Watch the agent’s live transcript, steer it mid-run, or end the session, all from inside the page itself.'
4
+ icon: hand
5
+ ---
2
6
 
3
- When you run with the presenter on (`reticle.connect({ present: true })`), the floating panel is a **two-way control surface**. You watch the agent's live transcript _and_ can steer it — pause it, send it a correction, or end the session — without leaving the page. There's also a clean, unmistakable "session ended" signal for both you and the agent.
7
+ When you run with the presenter on (`reticle.connect({ present: true })`), the floating panel is a **two-way control surface**. You watch the agent's live transcript _and_ can steer it (pause it, send it a correction, or end the session) without leaving the page. There's also a clean, unmistakable "session ended" signal for both you and the agent.
4
8
 
5
9
  ## Watch the testing score climb (the live verdict tally)
6
10
 
7
- The panel header shows a running **✓N ✗M** tally — the verdict score, live. It's hidden until the first verdict lands, then climbs as the agent verifies: every `reticle_assert`, every replayed flow step, every pass/fail outcome bumps it, and the side that just grew **pops** so you _feel_ the green (or red) arrive. The cursor flies, the log streams the journey, and the score ticks up — you see the agent working and the result landing, not just a final checkmark. It persists when the session ends, so the last thing on screen is what this run actually verified. Green that's earned, not asserted.
11
+ The panel header shows a running **✓N ✗M** tally: the verdict score, live. It's hidden until the first verdict lands, then climbs as the agent verifies: every `reticle_assert`, every replayed flow step, every pass/fail outcome bumps it, and the side that just grew **pops** so you _feel_ the green (or red) arrive. The cursor flies, the log streams the journey, and the score ticks up. You see the agent working and the result landing, not just a final checkmark. It persists when the session ends, so the last thing on screen is what this run actually verified. Green that's earned, not asserted.
8
12
 
9
13
  ## The constraint that shapes the design
10
14
 
11
- MCP is **pull-based**: the agent only receives data when it calls a tool. Reticle can't freeze the model mid-thought — but it **intercepts the agent's next tool call**, which is exactly when the agent would touch the page. That makes steering reliable:
15
+ MCP is **pull-based**: the agent only receives data when it calls a tool. Reticle can't freeze the model mid-thought, but it **intercepts the agent's next tool call**, which is exactly when the agent would touch the page. That makes steering reliable:
12
16
 
13
- - **Nudge** (a message, no pause) — your text is queued and rides back on the agent's **next** tool result as `guidance` (delivered once). The agent reads it and adjusts.
14
- - **Pause** — the agent's next `reticle_act` / `reticle_act_and_wait` / `reticle_act_sequence` **refuses to execute** and returns `{ paused: true, guidance: ["<your message>"], hint: "…" }`. The page is untouched until resume. _Read-only tools (snapshot/query/observe) still work_, so the agent can look while paused.
15
- - **End** — the session is over; the panel shows "Session ended" and clears.
17
+ - **Nudge** (a message, no pause). Your text is queued and rides back on the agent's **next** tool result as `guidance` (delivered once). The agent reads it and adjusts.
18
+ - **Pause.** The agent's next `reticle_act` / `reticle_act_and_wait` / `reticle_act_sequence` **refuses to execute** and returns `{ paused: true, guidance: ["<your message>"], hint: "…" }`. The page is untouched until resume. _Read-only tools (snapshot/query/observe) still work_, so the agent can look while paused.
19
+ - **End.** The session is over; the panel shows "Session ended" and clears.
16
20
 
17
21
  ## From the panel (the human)
18
22
 
19
23
  The floating panel (bottom-center, `present: true`) gives you:
20
24
 
21
- - **Pause / Resume** — one toggle. Paused turns the panel + page border **amber** with a `PAUSED` badge.
22
- - **Message box + send** — type a correction ("check the error state first"), hit Send (or Enter). Your message shows as a `🧑 you:` bubble in the transcript **and** is delivered to the agent.
23
- - **End** — ends the session: panel turns **emerald**, shows `Session ended · <summary>`, then fades away.
24
- - **Minimise (▾)** — collapse the panel to a bar that streams only the live line; click the bar to restore.
25
- - **Flag a bug** — the button in the corner. Toggle it on, click the element that looks wrong, type what's wrong (⌘/Ctrl+Enter to send, Esc to back out). Reticle pins a numbered marker, logs your flag in the panel, and hands the agent a structured mark.
25
+ - **Pause / Resume**: one toggle. Paused turns the panel + page border **amber** with a `PAUSED` badge.
26
+ - **Message box + send.** Type a correction ("check the error state first"), hit Send (or Enter). Your message shows as a `🧑 you:` bubble in the transcript **and** is delivered to the agent.
27
+ - **End** ends the session: panel turns **emerald**, shows `Session ended · <summary>`, then fades away.
28
+ - **Minimise (▾)** collapses the panel to a bar that streams only the live line; click the bar to restore.
29
+ - **Flag a bug**, the button in the corner. Toggle it on, click the element that looks wrong, type what's wrong (⌘/Ctrl+Enter to send, Esc to back out). Reticle pins a numbered marker, logs your flag in the panel, and hands the agent a structured mark.
26
30
 
27
- ## Flag a bug — annotate the mistake where you see it
31
+ ## Flag a bug: annotate the mistake where you see it
28
32
 
29
- You don't have to describe a bug in prose. **Point at it.** The flag captures the element's re-resolvable anchor _and_ the source `file:line` (when the framework stamped one), so the agent fixes the exact element and code — not a guess. The loop:
33
+ You don't have to describe a bug in prose. **Point at it.** The flag captures the element's re-resolvable anchor _and_ the source `file:line` (when the framework stamped one), so the agent fixes the exact element and code, not a guess. The loop:
30
34
 
31
35
  1. **You** flag the element and type the problem → Reticle emits a `HUMAN_MARK`.
32
- 2. **The agent** drains it with `reticle_session {action:"review"}` — note + element label + `source: { file, line }` + a ready-to-act `fix` hint. `reticle_sessions` also reports `pendingMarks` so the agent notices flags.
36
+ 2. **The agent** drains it with `reticle_session {action:"review"}`, getting note + element label + `source: { file, line }` + a ready-to-act `fix` hint. `reticle_sessions` also reports `pendingMarks` so the agent notices flags.
33
37
  3. **The agent** opens the file, fixes it, and calls `reticle_session {action:"review"}({ resolve: "m1" })`.
34
38
  4. **You** see **"✓ fixed: \<your note\>"** land in the panel. Flag → fix → confirmation.
35
39
 
36
- See [`reticle_session {action:"review"}` in the usage guide](usage.md#reticle_session {action:"review"}--drain-the-bugs-the-human-flagged-on-the-page) for the tool shape. Suppress the button with `annotate: false` if you don't want it.
40
+ See [`reticle_session {action:"review"}` in the usage guide](/usage) for the tool shape. Suppress the button with `annotate: false` if you don't want it.
37
41
 
38
42
  ## From the agent (the tools)
39
43
 
@@ -44,7 +48,7 @@ See [`reticle_session {action:"review"}` in the usage guide](usage.md#reticle_se
44
48
  | `reticle_session {action:"messages"}` | `{ sessionId? }` | drain + read pending human messages (explicit poll) |
45
49
  | `reticle_session {action:"review"}` | `{ resolve?, all?, sessionId? }` | list the bugs the human flagged; resolve one once fixed |
46
50
 
47
- When paused, every action tool short-circuits with the human's guidance, so the agent learns of the pause on its very next action. The agent's expected behavior: **read the guidance, adjust the plan, then call `reticle_session {action:"resume"}`** (or wait for the human to click Resume). The agent can also end the run itself with `reticle_session {action:"end"}({ summary })` when it's done — either path shows the same "ended" state.
51
+ When paused, every action tool short-circuits with the human's guidance, so the agent learns of the pause on its very next action. The agent's expected behavior: **read the guidance, adjust the plan, then call `reticle_session {action:"resume"}`** (or wait for the human to click Resume). The agent can also end the run itself with `reticle_session {action:"end"}({ summary })` when it's done. Either path shows the same "ended" state.
48
52
 
49
53
  ## Piggybacked guidance
50
54
 
@@ -54,7 +58,7 @@ Even without a pause, action/observe/assert results carry a `control` block when
54
58
  { since, dispatched, settled, result, control: { state: "active", guidance: ["looks good, keep going"] } }
55
59
  ```
56
60
 
57
- `guidance` is drained (delivered once), so a hint never repeats. When the session is clean and the inbox empty, no `control` field is added — zero overhead on the happy path.
61
+ `guidance` is drained (delivered once), so a hint never repeats. When the session is clean and the inbox empty, no `control` field is added: zero overhead on the happy path.
58
62
 
59
63
  ## Tell your agent to honor it
60
64