@reticlehq/server 2.8.0 → 2.10.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 (318) hide show
  1. package/SKILL.md +130 -813
  2. package/dist/bridge/bridge.d.ts +23 -0
  3. package/dist/bridge/bridge.js +80 -2
  4. package/dist/bridge/bridge.test-harness.d.ts +5 -0
  5. package/dist/bridge/bridge.test-harness.js +6 -4
  6. package/dist/capsule/capsule-store.d.ts +2 -2
  7. package/dist/capsule/causal-summary.d.ts +16 -1
  8. package/dist/capsule/causal-summary.js +2 -1
  9. package/dist/cli/cli-doctor.js +41 -13
  10. package/dist/cli/cli-flow-commands.js +8 -8
  11. package/dist/cli/cli-launch.d.ts +0 -17
  12. package/dist/cli/cli-launch.js +0 -28
  13. package/dist/cli/cli-parse.d.ts +1 -1
  14. package/dist/cli/cli-parse.js +1 -1
  15. package/dist/cli/cli-port.d.ts +24 -0
  16. package/dist/cli/cli-port.js +66 -35
  17. package/dist/cli/cli-verify.d.ts +21 -0
  18. package/dist/cli/cli-verify.js +45 -1
  19. package/dist/cli/config-discovery.d.ts +35 -0
  20. package/dist/cli/config-discovery.js +163 -0
  21. package/dist/cli/doctor-daemon-line.js +4 -1
  22. package/dist/cli/doctor-project-line.d.ts +37 -0
  23. package/dist/cli/doctor-project-line.js +35 -0
  24. package/dist/cli/doctor-rows.d.ts +51 -0
  25. package/dist/cli/doctor-rows.js +54 -0
  26. package/dist/cli/doctor-sessions-line.js +6 -3
  27. package/dist/cli/drive-attach.d.ts +99 -0
  28. package/dist/cli/drive-attach.js +203 -0
  29. package/dist/cli/drive-command.d.ts +13 -0
  30. package/dist/cli/drive-command.js +103 -0
  31. package/dist/cli/open-note.d.ts +20 -0
  32. package/dist/cli/open-note.js +37 -0
  33. package/dist/cli/status-next-action.d.ts +37 -0
  34. package/dist/cli/status-next-action.js +61 -0
  35. package/dist/cli.js +65 -70
  36. package/dist/crawl/crawl.d.ts +7 -2
  37. package/dist/crawl/crawl.js +52 -9
  38. package/dist/ee/audit-log.js +2 -2
  39. package/dist/events/body-failures.d.ts +2 -2
  40. package/dist/events/contradiction-folds.d.ts +45 -0
  41. package/dist/events/contradiction-folds.js +42 -0
  42. package/dist/events/contradictions.d.ts +51 -1
  43. package/dist/events/contradictions.js +26 -7
  44. package/dist/events/declared.d.ts +45 -0
  45. package/dist/events/declared.js +73 -0
  46. package/dist/events/echo-mismatch.d.ts +9 -3
  47. package/dist/events/echo-mismatch.js +33 -5
  48. package/dist/events/event-filters.d.ts +1 -1
  49. package/dist/events/event-filters.js +7 -4
  50. package/dist/events/predicate-eval.d.ts +17 -91
  51. package/dist/events/predicate-eval.js +85 -326
  52. package/dist/events/predicate-parse.js +31 -3
  53. package/dist/events/predicate-route.d.ts +32 -0
  54. package/dist/events/predicate-route.js +102 -0
  55. package/dist/events/predicate-schema.d.ts +99 -0
  56. package/dist/events/predicate-schema.js +309 -0
  57. package/dist/events/predicate.d.ts +8 -0
  58. package/dist/events/predicate.js +127 -5
  59. package/dist/events/stale-response.d.ts +2 -3
  60. package/dist/events/stale-response.js +49 -2
  61. package/dist/events/unit-mismatch.d.ts +2 -2
  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-replay-run.js +8 -1
  69. package/dist/flows/flow-tools.js +9 -1
  70. package/dist/flows/replay.js +3 -0
  71. package/dist/flows/verify-change-tools.js +1 -1
  72. package/dist/honesty/accepted-write.d.ts +3 -0
  73. package/dist/honesty/accepted-write.js +8 -3
  74. package/dist/honesty/already-true.js +15 -0
  75. package/dist/honesty/blind-spots.js +4 -0
  76. package/dist/honesty/honesty.d.ts +11 -0
  77. package/dist/honesty/honesty.js +1 -0
  78. package/dist/honesty/uncaptured-bodies.d.ts +4 -2
  79. package/dist/honesty/uncaptured-bodies.js +9 -2
  80. package/dist/honesty/unread-outcome.d.ts +8 -0
  81. package/dist/honesty/unread-outcome.js +36 -9
  82. package/dist/honesty/unsettled.d.ts +9 -0
  83. package/dist/honesty/unsettled.js +7 -3
  84. package/dist/honesty/verified.d.ts +29 -3
  85. package/dist/honesty/verified.js +67 -8
  86. package/dist/http-server.d.ts +19 -0
  87. package/dist/http-server.js +202 -4
  88. package/dist/impact/impact-recorder.d.ts +23 -0
  89. package/dist/impact/impact-recorder.js +76 -0
  90. package/dist/impact/impact-store.d.ts +47 -0
  91. package/dist/impact/impact-store.js +171 -0
  92. package/dist/index.d.ts +25 -0
  93. package/dist/index.js +60 -10
  94. package/dist/init/agent-rules.d.ts +20 -0
  95. package/dist/init/agent-rules.js +36 -0
  96. package/dist/init/astro-patch.js +27 -1
  97. package/dist/init/confirm.d.ts +21 -3
  98. package/dist/init/confirm.js +32 -5
  99. package/dist/init/connect-steps.d.ts +2 -0
  100. package/dist/init/connect-steps.js +50 -0
  101. package/dist/init/csp-check.d.ts +38 -0
  102. package/dist/init/csp-check.js +92 -0
  103. package/dist/init/csp-doctor.d.ts +25 -0
  104. package/dist/init/csp-doctor.js +49 -0
  105. package/dist/init/cursor.js +6 -9
  106. package/dist/init/detect.d.ts +8 -0
  107. package/dist/init/detect.js +33 -3
  108. package/dist/init/mcp-clients.d.ts +1 -0
  109. package/dist/init/mcp-clients.js +51 -7
  110. package/dist/init/mcp.d.ts +16 -2
  111. package/dist/init/mcp.js +23 -1
  112. package/dist/init/plan-framework.d.ts +10 -0
  113. package/dist/init/plan-framework.js +25 -1
  114. package/dist/init/plan.d.ts +7 -2
  115. package/dist/init/plan.js +66 -30
  116. package/dist/init/run.js +11 -5
  117. package/dist/init/slash-command.d.ts +1 -1
  118. package/dist/init/slash-command.js +12 -4
  119. package/dist/init/snippets.d.ts +1 -1
  120. package/dist/init/snippets.js +35 -7
  121. package/dist/init/vite-config.js +24 -2
  122. package/dist/input/network-mock-tools.js +8 -1
  123. package/dist/input/scroll-find.d.ts +6 -0
  124. package/dist/input/scroll-find.js +17 -2
  125. package/dist/input/scroll-tools.js +4 -0
  126. package/dist/journal/attach-journal.d.ts +9 -1
  127. package/dist/journal/attach-journal.js +13 -2
  128. package/dist/journal/journal-query.d.ts +13 -0
  129. package/dist/journal/journal-query.js +28 -3
  130. package/dist/journal/workspace-gitignore.d.ts +10 -0
  131. package/dist/journal/workspace-gitignore.js +59 -0
  132. package/dist/license/license.d.ts +69 -4
  133. package/dist/license/license.js +84 -15
  134. package/dist/mcp/mcp-proxy.d.ts +7 -93
  135. package/dist/mcp/mcp-proxy.js +183 -250
  136. package/dist/mcp/mcp.d.ts +27 -2
  137. package/dist/mcp/mcp.js +77 -36
  138. package/dist/mcp/proxy-daemon-probe.d.ts +8 -0
  139. package/dist/mcp/proxy-daemon-probe.js +48 -0
  140. package/dist/mcp/proxy-handshake.d.ts +82 -1
  141. package/dist/mcp/proxy-handshake.js +110 -3
  142. package/dist/mcp/proxy-lifecycle.d.ts +10 -1
  143. package/dist/mcp/proxy-lifecycle.js +11 -2
  144. package/dist/mcp/proxy-log.d.ts +63 -0
  145. package/dist/mcp/proxy-log.js +132 -0
  146. package/dist/mcp/server-instructions.d.ts +17 -0
  147. package/dist/mcp/server-instructions.js +56 -0
  148. package/dist/mcp/shared-params.d.ts +27 -0
  149. package/dist/mcp/shared-params.js +36 -0
  150. package/dist/mcp/sse-frame-parser.d.ts +19 -0
  151. package/dist/mcp/sse-frame-parser.js +41 -0
  152. package/dist/project/reticle-dir.d.ts +2 -0
  153. package/dist/project/reticle-dir.js +1 -0
  154. package/dist/session/connection-memory.d.ts +59 -0
  155. package/dist/session/connection-memory.js +108 -0
  156. package/dist/session/dev-command.d.ts +26 -0
  157. package/dist/session/dev-command.js +89 -0
  158. package/dist/session/dev-server-probe.d.ts +68 -0
  159. package/dist/session/dev-server-probe.js +102 -7
  160. package/dist/session/last-act.d.ts +26 -0
  161. package/dist/session/last-act.js +35 -1
  162. package/dist/session/live-control-tools.js +42 -5
  163. package/dist/session/no-session-diagnosis.d.ts +55 -2
  164. package/dist/session/no-session-diagnosis.js +199 -22
  165. package/dist/session/no-session-next-action.d.ts +46 -0
  166. package/dist/session/no-session-next-action.js +85 -0
  167. package/dist/session/no-session-watch.d.ts +40 -1
  168. package/dist/session/no-session-watch.js +172 -11
  169. package/dist/session/pending-commands.d.ts +12 -0
  170. package/dist/session/pending-commands.js +13 -1
  171. package/dist/session/session-manager.d.ts +4 -0
  172. package/dist/session/session-manager.js +35 -5
  173. package/dist/session/session-readiness.js +1 -1
  174. package/dist/session/session.d.ts +10 -1
  175. package/dist/session/session.js +146 -5
  176. package/dist/telemetry/app-instrumented.d.ts +8 -0
  177. package/dist/telemetry/app-instrumented.js +10 -0
  178. package/dist/telemetry/bug-found.d.ts +18 -0
  179. package/dist/telemetry/bug-found.js +14 -0
  180. package/dist/telemetry/daemon-telemetry.js +29 -0
  181. package/dist/telemetry/install-source.d.ts +20 -0
  182. package/dist/telemetry/install-source.js +34 -0
  183. package/dist/telemetry/instrumentation-stall.d.ts +57 -0
  184. package/dist/telemetry/instrumentation-stall.js +103 -0
  185. package/dist/telemetry/license-activation.d.ts +48 -0
  186. package/dist/telemetry/license-activation.js +57 -0
  187. package/dist/telemetry/session-metrics.d.ts +8 -0
  188. package/dist/telemetry/session-metrics.js +10 -0
  189. package/dist/telemetry/telemetry.d.ts +3 -1
  190. package/dist/telemetry/telemetry.js +23 -3
  191. package/dist/tools/act-tools.js +166 -29
  192. package/dist/tools/assert-verdict.js +32 -7
  193. package/dist/tools/browser-tools.js +10 -0
  194. package/dist/tools/dynamic-tools.js +17 -8
  195. package/dist/tools/error-recovery.js +10 -6
  196. package/dist/tools/feedback-tools.js +31 -6
  197. package/dist/tools/invoke-tool.d.ts +1 -1
  198. package/dist/tools/invoke-tool.js +30 -8
  199. package/dist/tools/lease-hint.d.ts +30 -8
  200. package/dist/tools/lease-hint.js +65 -14
  201. package/dist/tools/lease-tools.d.ts +11 -0
  202. package/dist/tools/lease-tools.js +141 -96
  203. package/dist/tools/observe-tools.js +9 -3
  204. package/dist/tools/react-grace.d.ts +4 -0
  205. package/dist/tools/react-grace.js +6 -3
  206. package/dist/tools/read-tools.js +47 -1
  207. package/dist/tools/resolve-target.d.ts +33 -0
  208. package/dist/tools/resolve-target.js +53 -0
  209. package/dist/tools/settle-in-flight.d.ts +15 -0
  210. package/dist/tools/settle-in-flight.js +42 -2
  211. package/dist/tools/snapshot-delta.d.ts +3 -0
  212. package/dist/tools/snapshot-delta.js +67 -6
  213. package/dist/tools/tool-kit.d.ts +38 -3
  214. package/dist/tools/tool-names.d.ts +2 -0
  215. package/dist/tools/tool-names.js +2 -0
  216. package/dist/tools/tool-surface.d.ts +60 -0
  217. package/dist/tools/tool-surface.js +84 -19
  218. package/dist/tools/tools.js +61 -5
  219. package/dist/tools/unadvertised-help.js +9 -6
  220. package/docs/README.md +1 -2
  221. package/docs/actions.mdx +41 -8
  222. package/docs/agent-cheatsheet.md +6 -4
  223. package/docs/architecture.md +6 -0
  224. package/docs/benchmarks.md +60 -12
  225. package/docs/best-practices.mdx +11 -6
  226. package/docs/capabilities.mdx +204 -57
  227. package/docs/cli/affected.mdx +6 -4
  228. package/docs/cli/capsules.mdx +9 -8
  229. package/docs/cli/cloud.mdx +30 -4
  230. package/docs/cli/doctor.mdx +41 -8
  231. package/docs/cli/drive.mdx +34 -15
  232. package/docs/cli/gate.mdx +6 -6
  233. package/docs/cli/help.mdx +8 -6
  234. package/docs/cli/hunt.mdx +20 -4
  235. package/docs/cli/identify.mdx +16 -2
  236. package/docs/cli/init.mdx +35 -8
  237. package/docs/cli/kill.mdx +5 -5
  238. package/docs/cli/license.mdx +1 -1
  239. package/docs/cli/open.mdx +6 -0
  240. package/docs/cli/restart.mdx +6 -6
  241. package/docs/cli/serve.mdx +10 -3
  242. package/docs/cli/status.mdx +29 -5
  243. package/docs/cli/stop.mdx +8 -0
  244. package/docs/cli/verify.mdx +20 -1
  245. package/docs/cli/version.mdx +1 -1
  246. package/docs/cli/watch.mdx +9 -2
  247. package/docs/cli.mdx +44 -22
  248. package/docs/debugging.md +3 -1
  249. package/docs/deploy-checks.md +31 -2
  250. package/docs/desktop.mdx +276 -80
  251. package/docs/docs.json +13 -12
  252. package/docs/enterprise.md +9 -3
  253. package/docs/faq.mdx +49 -35
  254. package/docs/fixtures.md +2 -0
  255. package/docs/flows.md +7 -5
  256. package/docs/for-agents.md +3 -3
  257. package/docs/frameworks.mdx +46 -7
  258. package/docs/gate-plan.md +4 -2
  259. package/docs/gates.md +8 -6
  260. package/docs/getting-started.md +53 -12
  261. package/docs/human-control.md +2 -0
  262. package/docs/index.mdx +43 -1
  263. package/docs/install-agentic.mdx +13 -4
  264. package/docs/install-manual.mdx +6 -4
  265. package/docs/instrumentation.mdx +7 -1
  266. package/docs/integration-patterns.md +3 -1
  267. package/docs/local-registry.md +4 -2
  268. package/docs/multi-agent-testing.md +3 -1
  269. package/docs/packages/babel-plugin.mdx +15 -6
  270. package/docs/packages/browser.mdx +2 -2
  271. package/docs/packages/core.mdx +26 -16
  272. package/docs/packages/electron.mdx +5 -7
  273. package/docs/packages/eslint-plugin.mdx +5 -3
  274. package/docs/packages/next.mdx +3 -5
  275. package/docs/packages/react.mdx +3 -3
  276. package/docs/packages/server.mdx +2 -2
  277. package/docs/packages/tauri.mdx +6 -6
  278. package/docs/packages/test.mdx +18 -7
  279. package/docs/packages/vite-plugin.mdx +4 -6
  280. package/docs/packages.mdx +14 -13
  281. package/docs/platform-integration.md +2 -0
  282. package/docs/predicates.mdx +76 -7
  283. package/docs/quickstart.mdx +51 -5
  284. package/docs/recipes.mdx +63 -23
  285. package/docs/skill-file.mdx +3 -1
  286. package/docs/state-management.mdx +2 -0
  287. package/docs/system-map.md +3 -1
  288. package/docs/telemetry-contract.md +33 -5
  289. package/docs/telemetry-events.mdx +63 -18
  290. package/docs/telemetry.md +8 -5
  291. package/docs/testing.md +35 -1
  292. package/docs/token-efficiency.md +8 -2
  293. package/docs/tools-act-and-wait.mdx +64 -14
  294. package/docs/tools-act-sequence.mdx +34 -17
  295. package/docs/tools-act.mdx +24 -11
  296. package/docs/tools-assert.mdx +69 -10
  297. package/docs/tools-console.mdx +11 -7
  298. package/docs/tools-inspect.mdx +11 -8
  299. package/docs/tools-navigate.mdx +41 -5
  300. package/docs/tools-network.mdx +42 -9
  301. package/docs/tools-observe.mdx +66 -10
  302. package/docs/tools-overview.mdx +26 -23
  303. package/docs/tools-query.mdx +52 -8
  304. package/docs/tools-session-and-feedback.mdx +35 -9
  305. package/docs/tools-sessions.mdx +39 -10
  306. package/docs/tools-snapshot.mdx +57 -3
  307. package/docs/tools-state.mdx +24 -1
  308. package/docs/tools-tools-and-run.mdx +55 -24
  309. package/docs/tools-wait-for.mdx +34 -15
  310. package/docs/troubleshooting.mdx +80 -10
  311. package/docs/usage.md +46 -24
  312. package/docs/vs-chrome-devtools-mcp.mdx +41 -0
  313. package/docs/vs-playwright-mcp.mdx +42 -2
  314. package/docs/vs-screenshots.mdx +42 -1
  315. package/docs/what-is-reticle.mdx +1 -1
  316. package/docs/why-reticle.mdx +3 -1
  317. package/package.json +5 -5
  318. package/docs/desktop-apps.md +0 -265
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  title: reticle doctor
3
- description: Diagnose the Reticle setup in one command: Chromium, daemon, bridge port, logs, and desktop wiring.
3
+ description: 'Diagnose the Reticle setup in one command: Chromium, daemon, bridge port, logs, and desktop wiring.'
4
4
  icon: stethoscope
5
5
  ---
6
6
 
@@ -23,14 +23,28 @@ Human-readable lines on stdout, not the JSON log. Real capture:
23
23
  ```
24
24
  reticle doctor
25
25
  node v22.14.0
26
- chromium ✓ installed (chromium-1223)
27
- daemon ✓ running on :4400 (pid 4795, v2.8.0)
26
+ chromium ✓ installed (chromium-1234)
27
+ daemon ✓ running on :4400 (pid 72698, v2.8.0)
28
28
  sessions ✓ 1 page connected
29
29
  bridge port 4400 (your app must dial THIS port, not your dev-server port)
30
30
  daemon log /Users/you/.reticle/daemon-4400.log
31
31
  tracing RETICLE_TRACE=1 on the daemon for per-stage timings in that log
32
32
  ```
33
33
 
34
+ When nothing has connected, the `sessions` line fails and the daemon's own diagnosis is appended under the checklist. Real capture, against a daemon on a scratch port that no app had dialled, with the diagnosis wrapped for width and trimmed at the ellipsis:
35
+
36
+ ```
37
+ sessions ✗ no page has connected to this daemon
38
+ bridge port 4499 (your app must dial THIS port, not your dev-server port)
39
+ daemon log /Users/you/.reticle/daemon-4499.log
40
+ tracing RETICLE_TRACE=1 on the daemon for per-stage timings in that log
41
+
42
+ nothing has connected. What the daemon can tell from here:
43
+ no browser session connected. Two things to weigh, and neither of them is proof. (1) Nothing is
44
+ listening on the ports Reticle scans (3000, 3001, 4200, 4321, 5000, 5173, 5174, 8000, 8080, 8100,
45
+ 9000, 1420, 3100, 4173, 5175, 7860, 8501), so the dev server may not be running ...
46
+ ```
47
+
34
48
  | Line | What it checks |
35
49
  | --- | --- |
36
50
  | `node` | The Node version running the CLI |
@@ -38,6 +52,7 @@ reticle doctor
38
52
  | `daemon` | Whether the port holds a Reticle daemon, a foreign process (named where possible), or nothing. Adds a `version` line when the daemon's version or contract fingerprint disagrees with the CLI's |
39
53
  | `sessions` | Whether any page has actually connected. Zero is a FAILING check rather than a neutral note, because it is the state the setup most often stalls in: wired correctly, daemon up, nothing dialled in. When it is zero the daemon's own no-session diagnosis prints below the checklist |
40
54
  | `bridge port` | The port your app must dial, spelled out because mixing it up with the dev server port is the most common setup failure |
55
+ | `port check` | Only when your project's configured port disagrees with the one the daemon is on. Prints the mismatch rather than leaving you to spot it across two lines |
41
56
  | `daemon log` | Where the structured daemon log lives |
42
57
  | `tracing` | How to turn that log into a per-stage trace |
43
58
  | `desktop` | Only on a desktop project. Every Electron and Tauri misconfiguration here fails silently, so it is diagnosed explicitly |
@@ -46,11 +61,29 @@ reticle doctor
46
61
 
47
62
  Playwright pins one Chromium revision per version, and the daemon launches that one and no other. So the check is not "is a Chromium there" but "is _this_ revision there", and the three answers have three different fixes.
48
63
 
49
- | Verdict | Real capture | What it means |
50
- | --- | --- | --- |
51
- | installed | `chromium ✓ installed (chromium-1223)` | The wanted revision is on disk. Nothing to do |
52
- | wrong revision | `chromium ✗ wrong revision — the bundled playwright wants chromium-1223; /tmp/pwroot holds chromium-1194, chromium-1208. run: npx playwright@1.60.0 install chromium` | Builds are installed, none of them the wanted one. One pinned download away |
53
- | missing | `chromium ✗ missing — looked for /tmp/emptyroot/chromium-1223/chrome-mac-arm64/…; run: npx playwright@1.60.0 install chromium` | The browsers root holds no Chromium at all. If that root is not where your browsers live, `PLAYWRIGHT_BROWSERS_PATH` is what moves it |
64
+ All three captures below are real, taken by pointing `PLAYWRIGHT_BROWSERS_PATH` at a full root, a stale root, and an empty one.
65
+
66
+ **`✓ installed`** means the wanted revision is on disk. Nothing to do.
67
+
68
+ ```
69
+ chromium ✓ installed (chromium-1234)
70
+ ```
71
+
72
+ **`✗ wrong revision`** means builds are installed and none of them is the wanted one. One pinned download away. The line names the revision Playwright wants, then every revision the root actually holds, then the exact command:
73
+
74
+ ```
75
+ chromium ✗ wrong revision ... the bundled playwright wants chromium-1234;
76
+ /tmp/pwroot holds chromium-1194, chromium-1208. run: npx playwright@1.62.1 install chromium
77
+ ```
78
+
79
+ **`✗ missing`** means the browsers root holds no Chromium at all. It prints the full executable path it looked for, so you can see which root it searched. If that is not where your browsers live, `PLAYWRIGHT_BROWSERS_PATH` is what moves it:
80
+
81
+ ```
82
+ chromium ✗ missing ... looked for /tmp/emptyroot/chromium-1234/chrome-mac-arm64/Google Chrome
83
+ for Testing.app/Contents/MacOS/Google Chrome for Testing; run: npx playwright@1.62.1 install chromium
84
+ ```
85
+
86
+ Both failing lines are one line in the terminal, wrapped here for width, and the punctuation that separates the verdict from its explanation is shown as `...`. The revision number and the pinned Playwright version come from the build you are running, so yours may differ from these; use the ones your own `doctor` prints.
54
87
 
55
88
  The wanted revision and the root are both read back off the path Playwright itself resolves, so `PLAYWRIGHT_BROWSERS_PATH` and every platform default (including `%LOCALAPPDATA%\ms-playwright` on Windows) are honoured without this command keeping its own table of roots.
56
89
 
@@ -22,34 +22,53 @@ npx @reticlehq/server drive <url> [--headless] [--headed]
22
22
 
23
23
  ## What it prints
24
24
 
25
- `reticle_started` with the port on success, `reticle_start_failed` with the error otherwise.
25
+ `reticle_started` with the port on success, `reticle_start_failed` with the error otherwise. When a daemon already owns the port it prints `reticle_drive_attached` with the session it opened instead. See [When a daemon already has the port](#when-a-daemon-already-has-the-port).
26
26
 
27
- Omitting the URL is a usage error. Real capture:
27
+ Omitting the URL is a usage error. Real capture, with the usage block that follows it trimmed:
28
28
 
29
29
  ```
30
- {"t":"2026-08-14T19:38:46.975Z","event":"reticle_usage_error","message":"drive needs a url"}
30
+ {"t":"2026-08-15T18:43:06.977Z","event":"reticle_usage_error","message":"drive needs a url"}
31
31
  drive needs a url
32
32
 
33
- usage:
34
- reticle init [--dry-run] [--port N] ...
33
+ usage: npx @reticlehq/server <command> (or `reticle <command>` once the bin is on your PATH)
34
+ ...
35
35
  ```
36
36
 
37
+ <Note>
38
+ `drive` takes **no `--port` flag**. Any flag other than `--headed` or `--headless` is rejected as
39
+ `unknown argument '--port'`. To drive on a different port, set `RETICLE_PORT` or put the port in
40
+ `.reticle.json`.
41
+ </Note>
42
+
37
43
  ## Exit codes
38
44
 
39
- | Code | Meaning |
40
- | ---- | ------------------------------------------------------ |
41
- | `0` | Running in the foreground |
42
- | `1` | No URL, an unknown flag, or the engine failed to start |
45
+ | Code | Meaning |
46
+ | ---- | --------------------------------------------------------------------- |
47
+ | `0` | Running in the foreground, or attached with a connected session |
48
+ | `1` | No URL, an unknown flag, the engine failed to start, or attach failed |
49
+
50
+ ## When a daemon already has the port
51
+
52
+ A daemon on `:4400` is the normal state the moment any agent connects, and `drive` does not fight it for the port: it **asks that daemon** for a browser instead. The daemon opens a fresh isolated, never-throttled context, the same one `reticle_lease` hands an agent, and `drive` prints the session id and exits.
53
+
54
+ ```bash
55
+ $ npx @reticlehq/server drive http://localhost:5173
56
+ {"event":"reticle_drive_attached","port":4400,"url":"http://localhost:5173","sessionId":"lease-9f2b…","ready":true}
57
+ ```
58
+
59
+ A plain-text line follows it naming the same session, how long it survives untouched (any tool call against it resets that), and, if the page never dialled the bridge, why.
60
+
61
+ That session lives in the daemon, so the tools your agent already has open can address it by id. You do **not** need `reticle stop` first, and should not use it: the MCP proxy respawns a daemon as soon as the next tool call arrives, which is a race a manual `stop` loses.
62
+
63
+ Attaching means the browser is the daemon's pooled, headless one, so `--headed` cannot apply. To watch a browser instead, drive on a port no daemon owns (`RETICLE_PORT=4460`).
43
64
 
44
- <Warning>
45
- `drive` binds the bridge port itself. If a daemon is already listening, it fails with an
46
- `EADDRINUSE` stack trace, which is the normal state once your agent has started one. [`reticle
47
- mcp`](/cli/mcp) reuses a running daemon; `drive` does not.
48
- </Warning>
65
+ <Note>
66
+ If the port is held by something that is **not** a Reticle daemon, `drive` refuses and names the
67
+ holder. It can neither bind nor attach. [`reticle verify`](/cli/verify) still binds its own port.
68
+ </Note>
49
69
 
50
70
  ## Worked example
51
71
 
52
72
  ```bash
53
- npx @reticlehq/server stop
54
73
  npx @reticlehq/server drive http://localhost:5173
55
74
  ```
package/docs/cli/gate.mdx CHANGED
@@ -32,16 +32,16 @@ One `reticle_gate` line. Real capture from this repository, with the flow lists
32
32
 
33
33
  ```json
34
34
  {
35
- "t": "2026-08-14T19:38:56.353Z",
35
+ "t": "2026-08-15T18:42:58.434Z",
36
36
  "event": "reticle_gate",
37
37
  "pass": false,
38
- "uncovered": ["deploy-journey", "verify-route", "verify-500"],
39
- "quarantined": ["asserted-demo", "blast-radius-compose"],
40
- "coverage": { "pct": 28, "covered": 13, "total": 47 }
38
+ "uncovered": ["WEB-flow", "deploy-journey", "verify-route", "verify-500"],
39
+ "quarantined": ["asserted-demo", "blast-radius-compose", "drift-demo", "qa2-flow"],
40
+ "coverage": { "pct": 0, "covered": 0, "total": 47 }
41
41
  }
42
42
  ```
43
43
 
44
- `downgraded` and `deletedCoverage` appear only when they are non-empty.
44
+ `downgraded` and `deletedCoverage` appear only when they are non-empty, and neither was present in that run. The CLI prints this as a single line of JSON; it is pretty-printed here to be readable.
45
45
 
46
46
  ## Exit codes
47
47
 
@@ -50,7 +50,7 @@ One `reticle_gate` line. Real capture from this repository, with the flow lists
50
50
  | `0` | Every affected, non-flaky flow has a passing artifact and nothing was weakened or deleted |
51
51
  | `1` | Something is uncovered, downgraded or deleted. Also `1` on any internal fault: the gate fails closed |
52
52
 
53
- Measured on this repository, `npx @reticlehq/server gate --since HEAD~1` exited `1` with the output above.
53
+ Measured on this repository, `npx @reticlehq/server gate --since HEAD~1` exited `1` with the output above: 0 of 47 flows covered, 39 uncovered and 8 quarantined.
54
54
 
55
55
  ## Worked example
56
56
 
package/docs/cli/help.mdx CHANGED
@@ -16,8 +16,8 @@ All three are the same command.
16
16
 
17
17
  <Warning>
18
18
  There is no per-command help. `reticle init --help` is not a help request, it is an unknown
19
- argument: the parser rejects it, prints `unknown argument '--help'`, and exits `1`. This page and
20
- the rest of this section are the per-command reference.
19
+ argument: the parser rejects it, prints `unknown argument '--help'` followed by this same block,
20
+ and exits `1`. This page and the rest of this section are the per-command reference.
21
21
  </Warning>
22
22
 
23
23
  ## What it prints
@@ -33,7 +33,9 @@ usage: npx @reticlehq/server <command> (or `reticle <command>` once the bin i
33
33
  (CLAUDE.md / AGENTS.md / .cursor) and the /reticle command, because all three
34
34
  only make sense once the tools are reachable.
35
35
  reticle serve [--port N] [--drive <url>] [--headless] [--http] [--http-port N] [--http-token T]
36
- reticle stop [--port N] [--quiet]
36
+ reticle stop [--port N] [--quiet] (stop the daemon we started, by its recorded pid)
37
+ reticle kill [--port N] [--force] (free the port by its LISTENER, never the agent's mcp proxy)
38
+ reticle restart [--port N] [--force] (kill, then start a daemon and wait for a real bind)
37
39
  reticle status [--port N]
38
40
  reticle doctor [--port N] (one command to diagnose setup: Chromium, daemon, port)
39
41
  reticle open [url] [--port N] (show the app: reuse the connected tab, else open one)
@@ -71,11 +73,11 @@ and the spec runner: batch work). --headed opts any of them in, --headless hides
71
73
  Any usage error prints the offending thing first, then this same block on stderr, and exits `1`:
72
74
 
73
75
  ```
74
- {"t":"2026-08-14T19:38:47.402Z","event":"reticle_usage_error","message":"unknown command 'nonsense-cmd'"}
76
+ {"t":"2026-08-15T18:42:48.476Z","event":"reticle_usage_error","message":"unknown command 'nonsense-cmd'"}
75
77
  unknown command 'nonsense-cmd'
76
78
 
77
- usage:
78
- ...
79
+ usage: npx @reticlehq/server <command> (or `reticle <command>` once the bin is on your PATH)
80
+ ... the whole block above ...
79
81
  ```
80
82
 
81
83
  Two audiences, two channels: the structured line for logs and scripts, the plain text for the person who just typed the command.
package/docs/cli/hunt.mdx CHANGED
@@ -4,7 +4,7 @@ description: Aggregate many crawl reports into one false-green rate across alrea
4
4
  icon: crosshairs
5
5
  ---
6
6
 
7
- `reticle hunt` is arithmetic, not driving. You point it at a directory of crawl reports (one JSON file per checkout, each produced by `reticle_crawl`) and it turns the pile into the one number the core claim needs: across N already-merged, already-green changes, how many carried a candidate false green?
7
+ `reticle hunt` is arithmetic, not driving. You point it at a directory of crawl reports (one JSON file per checkout, each produced by `reticle_verify {action:"crawl"}`) and it turns the pile into the one number the core claim needs: across N already-merged, already-green changes, how many carried a candidate false green?
8
8
 
9
9
  ```bash
10
10
  npx @reticlehq/server hunt <dir-of-crawl-reports>
@@ -22,7 +22,22 @@ Each input file is read for `label` (falling back to the filename), `anomalies[]
22
22
 
23
23
  ## What it prints
24
24
 
25
- One `reticle_hunt` line with the summary:
25
+ One `reticle_hunt` line with the summary. Real capture over a directory of two reports, one of them carrying a contradiction and a console error, with the `headline` sentence trimmed at the ellipsis:
26
+
27
+ ```json
28
+ {
29
+ "t": "2026-08-15T18:43:18.333Z",
30
+ "event": "reticle_hunt",
31
+ "runs": 2,
32
+ "runsWithCoverage": 2,
33
+ "runsWithContradictions": 1,
34
+ "contradictions": 1,
35
+ "singleChannelFaults": 1,
36
+ "byKind": { "ui-advanced-request-failed": 1, "console-error": 1 },
37
+ "flagged": ["abc123"],
38
+ "headline": "1 of 2 merged, already-green changes carried a candidate false green (1 contradictions) ..."
39
+ }
40
+ ```
26
41
 
27
42
  | Field | Meaning |
28
43
  | --- | --- |
@@ -40,7 +55,8 @@ One `reticle_hunt` line with the summary:
40
55
  Always `0`. A read failure is logged as `reticle_hunt_failed`. Omitting the directory is a usage error:
41
56
 
42
57
  ```
43
- {"t":"2026-08-14T19:38:47.116Z","event":"reticle_usage_error","message":"usage: reticle hunt <dir-of-crawl-reports>"}
58
+ {"t":"2026-08-15T18:43:06.525Z","event":"reticle_usage_error","message":"usage: reticle hunt <dir-of-crawl-reports>"}
59
+ usage: reticle hunt <dir-of-crawl-reports>
44
60
  ```
45
61
 
46
62
  That path exits `1`.
@@ -50,7 +66,7 @@ That path exits `1`.
50
66
  ```bash
51
67
  for sha in $(git log --format=%H -n 30 main); do
52
68
  git checkout "$sha" && npm run dev &
53
- # drive reticle_crawl over the app, write the JSON:
69
+ # drive reticle_verify {action:"crawl"} over the app, write the JSON:
54
70
  # .../reports/$sha.json
55
71
  done
56
72
  npx @reticlehq/server hunt ./reports
@@ -4,7 +4,9 @@ description: Opt in to telling the Reticle maintainers who you are, and undo it
4
4
  icon: id-card
5
5
  ---
6
6
 
7
- `reticle identify` is opt-in and deliberately not interactive. A prompt in the middle of someone's work is pressure, and this is the one command where the choice has to be free. You type what you want to share, or you never run it at all.
7
+ `reticle identify` attaches a name, a company, an email and a usage context to this machine's Reticle install and sends them to the maintainers, so support or an enterprise trial has someone to talk to. It is off unless you run it, and `reticle identify --forget` undoes it.
8
+
9
+ It is opt-in and deliberately not interactive. A prompt in the middle of someone's work is pressure, and this is the one command where the choice has to be free. You type what you want to share, or you never run it at all.
8
10
 
9
11
  ```bash
10
12
  npx @reticlehq/server identify --context <company|side_project|open_source|learning> [--company "Acme"] [--email you@acme.com] [--forget]
@@ -39,7 +41,19 @@ forgotten the local identity file is deleted; nothing further is sent.
39
41
  to have what was already sent removed, email support@reticlehq.com
40
42
  ```
41
43
 
42
- Run with no valid `--context`, it prints the current identity (if any), the usage, and the full consent notice.
44
+ Run with no valid `--context`, it prints the current identity (if any), the usage, and the full consent notice, and exits `1`. Real capture on a machine with no identity on file:
45
+
46
+ ```
47
+ usage: reticle identify --context <company|side_project|open_source|learning>
48
+ [--company "Acme"] [--email you@acme.com] [--forget]
49
+
50
+ This sends the details you type here to the Reticle maintainers, so we can contact you and
51
+ understand how teams use Reticle. It is linked to this machine's anonymous id, which means it
52
+ also connects the anonymous usage already recorded from this machine to what you enter now.
53
+ Nothing about your app, your code, or your repository is included, and nothing else changes.
54
+ Undo it any time with `reticle identify --forget` (that stops future sends and deletes the local file;
55
+ email support@reticlehq.com to have what was already sent removed).
56
+ ```
43
57
 
44
58
  <Warning>
45
59
  Identifying links this machine's anonymous id to what you type, which also connects the anonymous
package/docs/cli/init.mdx CHANGED
@@ -17,7 +17,7 @@ npx @reticlehq/server init [--dry-run] [--port N] [--no-mcp] [--no-install] [--a
17
17
  | `--dry-run` | boolean | `false` | Print the full plan and write nothing |
18
18
  | `--port N` | number | unset (resolves to `4400`) | Bridge port to write into the config |
19
19
  | `--no-mcp` | boolean | `false` (MCP is registered) | Skip the MCP registration **and** the agent rule files (`CLAUDE.md`, `AGENTS.md`, `.cursor`) and the `/reticle` command |
20
- | `--no-install` | boolean | `false` (deps are installed) | Skip the dependency install step |
20
+ | `--no-install` | boolean | `false` (deps are installed) | Do not run the install. The step becomes `[⚠]` and prints the `add -D` command for you to run by hand, rather than vanishing from the plan |
21
21
  | `--app <dir>` | string | unset | Pick which app to wire in a monorepo, without changing directory |
22
22
  | `--yes` | boolean | `false` | Accepted for scripting. `init` has no interactive prompts today, so it is a no-op |
23
23
 
@@ -28,7 +28,7 @@ npx @reticlehq/server init [--dry-run] [--port N] [--no-mcp] [--no-install] [--a
28
28
 
29
29
  ## What it prints
30
30
 
31
- A plan, one line per step, each with a status mark: `✓` written, `·` already in place or left alone, `⚠` needs a hand. Captured from a real run inside `apps/bench-app`:
31
+ A plan, one line per step, each with a status mark: `✓` written, `·` already in place or left alone, `⚠` needs a hand, `–` deliberately skipped by a flag. Captured from a real `--dry-run` inside `apps/bench-app`:
32
32
 
33
33
  ```
34
34
  reticle init (dry run, no files written)
@@ -46,6 +46,10 @@ reticle init (dry run, no files written)
46
46
 
47
47
  [✓] Agent verification rule → CLAUDE.md
48
48
  teach the agent to verify features with Reticle after building them
49
+ [✓] Agent verification rule → AGENTS.md
50
+ teach the agent to verify features with Reticle after building them
51
+ [✓] Full agent rules → RETICLE.md
52
+ the reference the always-loaded rule points at (when NOT to verify, recovery, feedback)
49
53
  [✓] The /reticle command → .claude/commands/reticle.md
50
54
  type /reticle to verify one flow in the browser
51
55
  [✓] Install dependencies → package.json
@@ -60,7 +64,16 @@ reticle init (dry run, no files written)
60
64
  Restart `vite`.
61
65
  ```
62
66
 
63
- Run from a workspace root with several apps, it refuses to guess and names them instead:
67
+ Below the plan it also prints the two things that actually finish the install: restart your agent so it re-reads its MCP server list, then run `npx @reticlehq/server status` to confirm the app connected.
68
+
69
+ With `--no-mcp`, the whole MCP and rules group collapses into one skipped line:
70
+
71
+ ```
72
+ [–] MCP server (global) → global (claude user scope)
73
+ --no-mcp ... also skips the agent rule files (CLAUDE.md / AGENTS.md / .cursor) and the /reticle command
74
+ ```
75
+
76
+ Run from a workspace root with several apps, it refuses to guess, names them, and exits `1`:
64
77
 
65
78
  ```
66
79
  Several apps found in this workspace. Re-run `reticle init` inside the one you want:
@@ -68,17 +81,31 @@ Several apps found in this workspace. Re-run `reticle init` inside the one you w
68
81
  packages/vite-plugin
69
82
  apps/atlas
70
83
  apps/bench-app
71
- ...
84
+ apps/electron-smoke
85
+ apps/large-dom-bench
86
+ apps/next-smoke
87
+ apps/tauri-smoke
88
+ apps/vibe-builder-demo
72
89
 
73
90
  Or name one without changing directory: reticle init --app packages/next
74
91
  ```
75
92
 
93
+ Given `--app`, it says which directory it moved to before printing the plan:
94
+
95
+ ```
96
+ No app in this directory ... wiring apps/bench-app instead.
97
+ ```
98
+
99
+ In the two captures above, the punctuation that separates the two halves of the sentence is shown as `...`.
100
+
76
101
  ## Exit codes
77
102
 
78
- | Code | Meaning |
79
- | ---- | ---------------------------------------------------------------------------- |
80
- | `0` | The plan ran. Steps marked `⚠` still need a hand, and do not change the code |
81
- | `1` | Init failed, or an unknown flag was passed |
103
+ | Code | Meaning |
104
+ | ---- | --------------------------------------------------------------------------------------- |
105
+ | `0` | The plan ran. Steps marked `⚠` still need a hand, and do not change the code |
106
+ | `1` | Init failed, an unknown flag was passed, or several apps were found and none was chosen |
107
+
108
+ An unknown flag is rejected by name: `reticle init --bogus` prints `unknown argument '--bogus'`, then the full usage block, and exits `1`.
82
109
 
83
110
  ## Worked example
84
111
 
package/docs/cli/kill.mdx CHANGED
@@ -47,24 +47,24 @@ Reach for `stop` in a script that shuts down a daemon it started. Reach for `kil
47
47
 
48
48
  ## What it prints
49
49
 
50
- Real captures.
50
+ Real captures, taken against a daemon started on a scratch port. The long `note` and `reason` strings are trimmed at the ellipsis. Measured exit codes: `0` for killed and nothing-to-do, `1` for refused.
51
51
 
52
52
  Killed:
53
53
 
54
54
  ```
55
- {"event":"reticle_killed","port":4499,"pid":84420,"escalated":true,"listenerIdentified":true,"note":"only the listener was signalled. Any `reticle mcp` proxy on this port holds a client connection, not the port, and was left alone — it goes dormant and starts a fresh daemon on the next tool call."}
55
+ {"t":"2026-08-15T18:43:43.590Z","event":"reticle_killed","port":4499,"pid":78621,"escalated":false,"listenerIdentified":true,"note":"only the listener was signalled. Any `reticle mcp` proxy on this port holds a client connection, not the port, and was left alone ..."}
56
56
  ```
57
57
 
58
- Refused, because the listener is not ours:
58
+ Refused, because the listener is not ours. This one was captured with `python3 -m http.server 4498` holding the port:
59
59
 
60
60
  ```
61
- {"event":"reticle_kill_refused","port":4498,"reason":"pid 84346 (\"Python\") is listening, and it is not a Reticle daemon — it never answered /status and it is not the pid Reticle recorded. Killing it would be killing someone else's process. Run it again with --force if that is what you meant, or start Reticle on another port.","holder":"Python"}
61
+ {"t":"2026-08-15T18:43:57.128Z","event":"reticle_kill_refused","port":4498,"reason":"pid 78795 (\"Python\") is listening, and it is not a Reticle daemon ... it never answered /status and it is not the pid Reticle recorded. Killing it would be killing someone else's process. Run it again with --force if that is what you meant, or start Reticle on another port.","holder":"Python"}
62
62
  ```
63
63
 
64
64
  Nothing to free:
65
65
 
66
66
  ```
67
- {"event":"reticle_kill_nothing_to_do","port":4499,"presence":"free"}
67
+ {"t":"2026-08-15T18:43:43.784Z","event":"reticle_kill_nothing_to_do","port":4499,"presence":"free"}
68
68
  ```
69
69
 
70
70
  | Event | When |
@@ -18,7 +18,7 @@ One `reticle_license` line. Real capture on a checkout with no issuer key config
18
18
 
19
19
  ```json
20
20
  {
21
- "t": "2026-08-14T19:38:38.331Z",
21
+ "t": "2026-08-15T18:42:26.758Z",
22
22
  "event": "reticle_license",
23
23
  "status": "eval",
24
24
  "detail": "evaluation mode: enterprise features run free (no issuer key configured)"
package/docs/cli/open.mdx CHANGED
@@ -40,6 +40,12 @@ One `reticle_open` line, whose fields say which of four things happened.
40
40
  {"event":"reticle_open","port":4499,"error":"could not launch a browser: spawn open ENOENT","recovery":"Nothing was opened. This command asks the OS to open a url in your default browser (`open` on this platform) and that failed, so the fix is on the OS side: open the url yourself, or set a default browser."}
41
41
  ```
42
42
 
43
+ Calling it with no URL when nothing has connected is the other `error` shape. It exits `0`, because reporting "there is nothing to show you" is a complete answer. Real capture, with the sentence break shown as `...`:
44
+
45
+ ```
46
+ {"t":"2026-08-15T18:43:41.651Z","event":"reticle_open","port":4499,"error":"no app connected ... pass a url: reticle open <url>. Desktop app (Electron/Tauri)? There is no url to open: start it as you normally would and it connects to this bridge itself."}
47
+ ```
48
+
43
49
  </Note>
44
50
 
45
51
  <Warning>
@@ -21,18 +21,18 @@ npx @reticlehq/server restart [--port N] [--force]
21
21
 
22
22
  A restart that killed the old daemon and then could not start a new one leaves you somewhere quite different from one that could not kill anything at all, and a single `ok: false` cannot tell you which happened. So each half prints its own line.
23
23
 
24
- Real capture, restarting over a running daemon:
24
+ Real capture, restarting over a running daemon (exit `0`), with the long `note` trimmed at the ellipsis:
25
25
 
26
26
  ```
27
- {"event":"reticle_killed","port":4499,"pid":84513,"escalated":false,"listenerIdentified":true,"note":"only the listener was signalled. Any `reticle mcp` proxy on this port holds a client connection, not the port, and was left alone — it goes dormant and starts a fresh daemon on the next tool call."}
28
- {"event":"reticle_daemon_spawned","port":4499}
27
+ {"t":"2026-08-15T18:43:41.949Z","event":"reticle_killed","port":4499,"pid":78137,"escalated":false,"listenerIdentified":true,"note":"only the listener was signalled. Any `reticle mcp` proxy on this port holds a client connection, not the port, and was left alone ..."}
28
+ {"t":"2026-08-15T18:43:42.258Z","event":"reticle_daemon_spawned","port":4499}
29
29
  ```
30
30
 
31
- Real capture, when the port is held by something that is not ours:
31
+ Real capture, when the port is held by something that is not ours (exit `1`). Both `reason` strings are trimmed at the ellipsis:
32
32
 
33
33
  ```
34
- {"event":"reticle_kill_refused","port":4498,"reason":"pid 84537 (\"Python\") is listening, and it is not a Reticle daemon — it never answered /status and it is not the pid Reticle recorded. Killing it would be killing someone else's process. Run it again with --force if that is what you meant, or start Reticle on another port.","holder":"Python"}
35
- {"event":"reticle_restart_aborted","port":4498,"reason":"the port was not freed, so nothing was started — the old holder is still there"}
34
+ {"t":"2026-08-15T18:43:57.317Z","event":"reticle_kill_refused","port":4498,"reason":"pid 78795 (\"Python\") is listening, and it is not a Reticle daemon ... it never answered /status and it is not the pid Reticle recorded. Killing it would be killing someone else's process. Run it again with --force if that is what you meant, or start Reticle on another port.","holder":"Python"}
35
+ {"t":"2026-08-15T18:43:57.318Z","event":"reticle_restart_aborted","port":4498,"reason":"the port was not freed, so nothing was started ... the old holder is still there"}
36
36
  ```
37
37
 
38
38
  <Note>
@@ -29,13 +29,20 @@ Most people never type this. Your agent runs [`reticle mcp`](/cli/mcp), which st
29
29
  One structured line. When a daemon already owns the port, it says so and does nothing:
30
30
 
31
31
  ```json
32
- { "t": "2026-08-14T19:39:13.230Z", "event": "reticle_daemon_already_running", "port": 4400 }
32
+ { "t": "2026-08-15T18:43:33.065Z", "event": "reticle_daemon_already_running", "port": 4499 }
33
33
  ```
34
34
 
35
- A successful spawn logs `reticle_daemon_spawned` with the port. A port held by something that is not a Reticle daemon is refused up front rather than spawned into:
35
+ A successful spawn logs `reticle_daemon_spawned` with the port:
36
+
37
+ ```json
38
+ { "t": "2026-08-15T18:43:32.604Z", "event": "reticle_daemon_spawned", "port": 4499 }
39
+ ```
40
+
41
+ A port held by something that is not a Reticle daemon is refused up front rather than spawned into. The reason is repeated as plain text on stderr for the person who typed the command. Real capture, with `python3 -m http.server 4498` holding the port and the punctuation between the two halves of the reason shown as `...`:
36
42
 
37
43
  ```
38
- reticle_daemon_start_refused
44
+ {"t":"2026-08-15T18:43:57.478Z","event":"reticle_daemon_start_refused","port":4498,"presence":"foreign","reason":"port 4498 is held by another process that is not a Reticle daemon ... a daemon cannot bind it. Stop that process, or run Reticle on a different port (`--port`), then retry."}
45
+ port 4498 is held by another process that is not a Reticle daemon ... a daemon cannot bind it. Stop that process, or run Reticle on a different port (`--port`), then retry.
39
46
  ```
40
47
 
41
48
  ## Exit codes
@@ -22,23 +22,47 @@ One JSON line on stdout. Real capture, daemon up and no app connected:
22
22
 
23
23
  ```json
24
24
  {
25
- "t": "2026-08-14T19:38:37.737Z",
25
+ "t": "2026-08-15T18:43:33.724Z",
26
26
  "event": "reticle_status",
27
- "port": 4400,
27
+ "port": 4499,
28
28
  "running": true,
29
- "pid": 4795,
29
+ "pid": 78137,
30
30
  "sessionCount": 0,
31
31
  "sessions": [],
32
- "why": "no browser session connected. Two things to weigh, and neither of them is proof. (1) Nothing is listening on the ports Reticle scans ... (2) There is no `.reticle.json` in the directory this daemon is running in ..."
32
+ "why": "no browser session connected. Two things to weigh, and neither of them is proof. (1) Nothing is listening on the ports Reticle scans (3000, 3001, 4200, 4321, 5000, 5173, 5174, 8000, 8080, 8100, 9000, 1420, 3100, 4173, 5175, 7860, 8501), so the dev server may not be running ... (2) There is no `.reticle.json` in the directory this daemon is running in ..."
33
33
  }
34
34
  ```
35
35
 
36
+ With an app connected, `why` is absent and each session carries its own fields:
37
+
38
+ ```json
39
+ {
40
+ "t": "2026-08-15T18:45:58.190Z",
41
+ "event": "reticle_status",
42
+ "port": 4400,
43
+ "running": true,
44
+ "pid": 72698,
45
+ "sessionCount": 1,
46
+ "sessions": [
47
+ {
48
+ "sessionId": "s223e0fad-e012-43f9-a23d-6d585470c90d",
49
+ "url": "http://localhost:4310/",
50
+ "throttled": false,
51
+ "stale": false,
52
+ "pendingMarks": 0
53
+ }
54
+ ]
55
+ }
56
+ ```
57
+
58
+ Both are one line of JSON in the terminal, pretty-printed here, with `why` trimmed at the ellipses.
59
+
36
60
  | Field | Meaning |
37
61
  | --- | --- |
38
62
  | `running` | Whether a usable daemon owns the port |
39
63
  | `presence` | Present when the pid file is dead: `daemon`, `foreign`, or `absent`. The port is asked, not just the pid file |
40
64
  | `pid` | The daemon process |
41
- | `sessionCount` / `sessions` | Connected app sessions, each with `sessionId`, `url`, `throttled`, `stale` |
65
+ | `sessionCount` / `sessions` | Connected app sessions, each with `sessionId`, `url`, `throttled`, `stale`, `pendingMarks` |
42
66
  | `why` | Present when zero sessions connected. Names the two likely causes and refuses to assert either |
43
67
  | `updateAvailable` | Present when a newer server version is on the registry |
44
68
 
package/docs/cli/stop.mdx CHANGED
@@ -31,6 +31,14 @@ npx @reticlehq/server stop [--port N] [--quiet]
31
31
  | `reticle_daemon_not_running` | No live pid for that port. The stale pid file is removed |
32
32
  | `reticle_daemon_stop_timeout` | It survived everything. Exits `1` |
33
33
 
34
+ Real capture, stopping a port with no live pid recorded for it. It exits `0`, because "already not running" is the state you asked for:
35
+
36
+ ```
37
+ {"t":"2026-08-15T18:43:43.943Z","event":"reticle_daemon_not_running","port":4499}
38
+ ```
39
+
40
+ With `--quiet` that same run prints nothing at all and still exits `0`.
41
+
34
42
  ## Exit codes
35
43
 
36
44
  | Code | Meaning |
@@ -27,17 +27,36 @@ The rendered run report on stdout. On failure it names the cause on stderr inste
27
27
 
28
28
  ```
29
29
  No app connected: Reticle drove the URL but no @reticlehq/browser session dialed back.
30
- Make sure the SDK is in the build and reticle.connect() runs on the preview page
30
+ Make sure the SDK is in the build and reticle.connect() runs on the preview page (for a non-localhost preview: allowNonLocalhost + a pairing token).
31
31
  ```
32
32
 
33
33
  ```
34
34
  No saved flows to verify (.reticle/flows is empty), so refusing to report a pass for verifying nothing.
35
+ Flows are recorded interactively by an agent (reticle_record{action:"start"} → act → reticle_flow_save via the
36
+ MCP tools), then committed to .reticle/flows/. In CI, check those files in and re-run `reticle verify`.
35
37
  ```
36
38
 
37
39
  <Warning>
38
40
  Both of those are honesty guards, and both exit `1`. Verifying nothing is never a pass.
39
41
  </Warning>
40
42
 
43
+ A URL that does not answer is not one of those two. It surfaces as the underlying Playwright error, prefixed with `verify failed:`, and also exits `1`. Real capture:
44
+
45
+ ```
46
+ verify failed: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:59999/
47
+ Call log:
48
+ - navigating to "http://localhost:59999/", waiting until "domcontentloaded"
49
+ ```
50
+
51
+ <Warning>
52
+ `verify` binds the bridge port itself and has **no `--port` flag**: passing one is rejected as
53
+ `unknown argument '--port'`. If a daemon is already on the port, `verify` dies with a raw `Error:
54
+ listen EADDRINUSE: address already in use 127.0.0.1:4400` stack trace before it drives anything.
55
+ Stop the daemon first, or move `verify` with the `RETICLE_PORT` environment variable. This bites
56
+ most often on a developer machine, where an agent has usually left a daemon running; in CI the
57
+ port is normally free.
58
+ </Warning>
59
+
41
60
  ## Exit codes
42
61
 
43
62
  | Code | Meaning |
@@ -19,7 +19,7 @@ All three are the same command. No flags.
19
19
  One `reticle_version` line. Real capture:
20
20
 
21
21
  ```json
22
- { "t": "2026-08-14T19:38:37.595Z", "event": "reticle_version", "version": "2.8.0" }
22
+ { "t": "2026-08-15T18:42:26.275Z", "event": "reticle_version", "version": "2.8.0" }
23
23
  ```
24
24
 
25
25
  ## Exit codes