@reticlehq/server 3.7.0 → 3.8.1

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 (578) hide show
  1. package/SKILL.md +59 -68
  2. package/bin/reticle.js +9 -2
  3. package/dist/command/cli/adhoc-suite.js +1 -1
  4. package/dist/command/cli/adhoc-verdict.d.ts +12 -0
  5. package/dist/command/cli/adhoc-verdict.js +62 -4
  6. package/dist/command/cli/answers/gate-hook-message.d.ts +2 -0
  7. package/dist/command/cli/answers/gate-hook-message.js +5 -1
  8. package/dist/command/cli/auth/cloud-session.d.ts +11 -19
  9. package/dist/command/cli/auth/cloud-session.js +23 -0
  10. package/dist/command/cli/auth/device-flow.d.ts +3 -37
  11. package/dist/command/cli/cli-doctor.js +66 -21
  12. package/dist/command/cli/cli-flow-commands.js +53 -11
  13. package/dist/command/cli/cli-kill.d.ts +8 -0
  14. package/dist/command/cli/cli-kill.js +43 -3
  15. package/dist/command/cli/cli-parse-feedback.d.ts +4 -0
  16. package/dist/command/cli/cli-parse-feedback.js +13 -1
  17. package/dist/command/cli/cli-parse-grammar.d.ts +1 -2
  18. package/dist/command/cli/cli-parse-grammar.js +1 -6
  19. package/dist/command/cli/cli-parse-serve.d.ts +24 -0
  20. package/dist/command/cli/cli-parse-serve.js +124 -0
  21. package/dist/command/cli/cli-parse.d.ts +12 -11
  22. package/dist/command/cli/cli-parse.js +100 -123
  23. package/dist/command/cli/cli-usage.d.ts +0 -11
  24. package/dist/command/cli/cli-usage.js +43 -16
  25. package/dist/command/cli/cli-verify.d.ts +1 -1
  26. package/dist/command/cli/cli-verify.js +29 -14
  27. package/dist/command/cli/cloud-cli.js +27 -9
  28. package/dist/command/cli/cloud-kit.js +3 -3
  29. package/dist/command/cli/cloud-link.d.ts +1 -20
  30. package/dist/command/cli/cloud-link.js +2 -0
  31. package/dist/command/cli/cloud-login.d.ts +6 -2
  32. package/dist/command/cli/cloud-login.js +13 -41
  33. package/dist/command/cli/config/checkout.d.ts +10 -0
  34. package/dist/command/cli/config/checkout.js +78 -0
  35. package/dist/command/cli/doctor/doctor-agent-link.d.ts +23 -0
  36. package/dist/command/cli/doctor/doctor-agent-link.js +28 -0
  37. package/dist/command/cli/doctor/doctor-coverage-line.js +1 -7
  38. package/dist/command/cli/doctor/doctor-rows.d.ts +4 -0
  39. package/dist/command/cli/doctor/doctor-rows.js +4 -0
  40. package/dist/command/cli/drive/drive-attach.d.ts +5 -0
  41. package/dist/command/cli/drive/drive-attach.js +21 -1
  42. package/dist/command/cli/hook/codex-transcript.d.ts +20 -0
  43. package/dist/command/cli/hook/codex-transcript.js +224 -0
  44. package/dist/command/cli/hook/episode-structure.d.ts +24 -0
  45. package/dist/command/cli/hook/episode-structure.js +138 -0
  46. package/dist/command/cli/hook/hook-command.d.ts +9 -0
  47. package/dist/command/cli/hook/hook-command.js +211 -0
  48. package/dist/command/cli/hook/hook-fast.d.ts +31 -0
  49. package/dist/command/cli/hook/hook-fast.js +136 -0
  50. package/dist/command/cli/hook/prompt-hook.d.ts +30 -0
  51. package/dist/command/cli/hook/prompt-hook.js +199 -0
  52. package/dist/command/cli/hook/tool-hook.d.ts +21 -0
  53. package/dist/command/cli/hook/tool-hook.js +259 -0
  54. package/dist/command/cli/launch/cli-launch.d.ts +2 -0
  55. package/dist/command/cli/launch/cli-launch.js +22 -2
  56. package/dist/command/cli/lifecycle/daemon-lifecycle.js +28 -2
  57. package/dist/command/cli/mcp-command.d.ts +9 -0
  58. package/dist/command/cli/mcp-command.js +127 -41
  59. package/dist/command/cli/open-navigate.d.ts +43 -0
  60. package/dist/command/cli/open-navigate.js +85 -0
  61. package/dist/command/cli/ports/port-holder.d.ts +16 -4
  62. package/dist/command/cli/ports/port-holder.js +58 -12
  63. package/dist/command/cli/ports/resolve/cli-port.js +18 -2
  64. package/dist/command/cli/project-name.d.ts +1 -7
  65. package/dist/command/cli/project-name.js +1 -0
  66. package/dist/command/cli/report-command.d.ts +8 -2
  67. package/dist/command/cli/report-command.js +95 -18
  68. package/dist/command/cli/status-fields.d.ts +9 -2
  69. package/dist/command/cli/status-fields.js +148 -4
  70. package/dist/command/cli/try-command.d.ts +3 -4
  71. package/dist/command/cli/try-command.js +6 -11
  72. package/dist/command/cli/verify-results.d.ts +4 -1
  73. package/dist/command/cli/verify-results.js +3 -1
  74. package/dist/command/cli.d.ts +1 -7
  75. package/dist/command/cli.js +79 -160
  76. package/dist/command/daemon/daemon-resilience.d.ts +6 -1
  77. package/dist/command/daemon/daemon-resilience.js +31 -18
  78. package/dist/command/daemon/daemon.d.ts +11 -0
  79. package/dist/command/daemon/daemon.js +70 -2
  80. package/dist/command/daemon/lifetime/daemon-usefulness.d.ts +2 -0
  81. package/dist/command/daemon/lifetime/daemon-usefulness.js +13 -2
  82. package/dist/command/daemon/wake-decision.js +4 -2
  83. package/dist/command/daemon/win32-escape.d.ts +32 -0
  84. package/dist/command/daemon/win32-escape.js +119 -0
  85. package/dist/command/setup/agent-configs.d.ts +9 -0
  86. package/dist/command/setup/agent-configs.js +9 -0
  87. package/dist/command/setup/auto-approve.d.ts +10 -3
  88. package/dist/command/setup/auto-approve.js +51 -3
  89. package/dist/command/setup/bringup/dev-server-files.d.ts +1 -9
  90. package/dist/command/setup/bringup/dev-server-wait.js +5 -1
  91. package/dist/command/setup/bringup/ensure-daemon.js +4 -7
  92. package/dist/command/setup/init/init-host.js +6 -0
  93. package/dist/command/setup/init/init-port.js +5 -4
  94. package/dist/command/setup/init/init-runtime.js +7 -1
  95. package/dist/command/setup/node-effects.d.ts +8 -0
  96. package/dist/command/setup/node-effects.js +36 -4
  97. package/dist/command/setup/probe/dev-port.d.ts +26 -0
  98. package/dist/command/setup/probe/dev-port.js +117 -0
  99. package/dist/command/setup/probe/page-probe.d.ts +1 -1
  100. package/dist/command/setup/probe/page-probe.js +11 -3
  101. package/dist/command/setup/remaining-steps.d.ts +0 -7
  102. package/dist/command/setup/remaining-steps.js +3 -2
  103. package/dist/command/setup/run-setup.d.ts +11 -0
  104. package/dist/command/setup/run-setup.js +33 -5
  105. package/dist/command/setup/setup-command.d.ts +2 -0
  106. package/dist/command/setup/setup-command.js +22 -4
  107. package/dist/command/update/updater.d.ts +1 -6
  108. package/dist/command/update/updater.js +4 -5
  109. package/dist/command/update/version-seen.d.ts +24 -0
  110. package/dist/command/update/version-seen.js +65 -0
  111. package/dist/command/version/peer-announce.d.ts +9 -1
  112. package/dist/command/version/peer-announce.js +25 -2
  113. package/dist/command/version/sdk-fix.d.ts +5 -6
  114. package/dist/command/version/sdk-fix.js +17 -32
  115. package/dist/command/version/version-skew.d.ts +16 -1
  116. package/dist/command/version/version-skew.js +84 -10
  117. package/dist/features/crawl/crawl-tools.js +2 -2
  118. package/dist/features/crawl/crawl.js +2 -0
  119. package/dist/features/exhaust/ledger.d.ts +15 -96
  120. package/dist/features/exhaust/ledger.js +3 -3
  121. package/dist/features/harness/drive-runs.d.ts +28 -4
  122. package/dist/features/harness/drive-runs.js +99 -7
  123. package/dist/features/harness/drivers.d.ts +1 -1
  124. package/dist/features/harness/drivers.js +1 -1
  125. package/dist/features/harness/harness.d.ts +5 -0
  126. package/dist/features/harness/harness.js +24 -12
  127. package/dist/features/harness/platform/platform-drives.d.ts +7 -1
  128. package/dist/features/harness/platform/platform-drives.js +12 -7
  129. package/dist/features/harness/platform/refusal.d.ts +1 -1
  130. package/dist/features/harness/platform/refusal.js +4 -4
  131. package/dist/features/harness/platform/remote-drive.js +70 -28
  132. package/dist/features/harness/platform/server-driver.js +22 -1
  133. package/dist/features/harness/platform-config.d.ts +1 -10
  134. package/dist/features/harness/platform-config.js +0 -20
  135. package/dist/features/license/license.d.ts +2 -14
  136. package/dist/features/visual/step-shots.d.ts +33 -1
  137. package/dist/features/visual/step-shots.js +155 -1
  138. package/dist/features/visual/visual-tools.js +12 -4
  139. package/dist/hooks/coding-agents.d.ts +6 -0
  140. package/dist/hooks/coding-agents.js +24 -3
  141. package/dist/hooks/episode-stamp.d.ts +32 -0
  142. package/dist/hooks/episode-stamp.js +61 -0
  143. package/dist/hooks/hook-commands.js +19 -9
  144. package/dist/index.d.ts +5 -1
  145. package/dist/index.js +66 -24
  146. package/dist/judgement/app-graph/journal-steps.d.ts +8 -0
  147. package/dist/judgement/app-graph/journal-steps.js +92 -0
  148. package/dist/judgement/capsule/capsule-store.d.ts +2 -39
  149. package/dist/judgement/domain/domain-model.js +1 -10
  150. package/dist/judgement/domain/domain-tools.js +4 -2
  151. package/dist/judgement/runs/artifact/run-store.d.ts +28 -8
  152. package/dist/judgement/runs/artifact/run-store.js +153 -28
  153. package/dist/judgement/runs/default-run-id.d.ts +3 -1
  154. package/dist/judgement/runs/default-run-id.js +15 -3
  155. package/dist/judgement/runs/drive-run.d.ts +3 -3
  156. package/dist/judgement/runs/drive-run.js +11 -7
  157. package/dist/judgement/runs/run-ledger.d.ts +21 -0
  158. package/dist/judgement/runs/run-ledger.js +143 -0
  159. package/dist/judgement/runs/runner-port.d.ts +1 -1
  160. package/dist/judgement/runs/runner-port.js +8 -2
  161. package/dist/judgement/runs/verification-sync.d.ts +4 -3
  162. package/dist/judgement/runs/verification-sync.js +27 -5
  163. package/dist/language/flows/affected-tools.js +2 -1
  164. package/dist/language/flows/annotate-notes/annotate.js +8 -3
  165. package/dist/language/flows/annotate-tools.js +1 -1
  166. package/dist/language/flows/change/affected.d.ts +8 -0
  167. package/dist/language/flows/change/affected.js +20 -7
  168. package/dist/language/flows/decision.js +19 -4
  169. package/dist/language/flows/fields/flow-secret-field.d.ts +9 -0
  170. package/dist/language/flows/fields/flow-secret-field.js +67 -5
  171. package/dist/language/flows/flow-anchor.d.ts +5 -0
  172. package/dist/language/flows/flow-anchor.js +4 -0
  173. package/dist/language/flows/flow-cross-step.d.ts +7 -1
  174. package/dist/language/flows/flow-cross-step.js +9 -0
  175. package/dist/language/flows/flow-expect-grammar.js +15 -10
  176. package/dist/language/flows/flow-intent.js +6 -1
  177. package/dist/language/flows/flow-journey.d.ts +8 -0
  178. package/dist/language/flows/flow-journey.js +13 -0
  179. package/dist/language/flows/flow-lease.d.ts +27 -0
  180. package/dist/language/flows/flow-lease.js +59 -0
  181. package/dist/language/flows/flow-replay-run.d.ts +1 -87
  182. package/dist/language/flows/flow-replay-run.js +111 -310
  183. package/dist/language/flows/flow-replay.js +21 -19
  184. package/dist/language/flows/flow-replayable.d.ts +38 -0
  185. package/dist/language/flows/flow-replayable.js +179 -0
  186. package/dist/language/flows/flow-start-path.d.ts +108 -0
  187. package/dist/language/flows/flow-start-path.js +313 -0
  188. package/dist/language/flows/flow-step-runners.d.ts +23 -2
  189. package/dist/language/flows/flow-step-runners.js +191 -32
  190. package/dist/language/flows/flow-store-for-session.d.ts +8 -1
  191. package/dist/language/flows/flow-store-for-session.js +6 -0
  192. package/dist/language/flows/flow-tools.d.ts +2 -23
  193. package/dist/language/flows/flow-tools.js +63 -113
  194. package/dist/language/flows/flows.d.ts +4 -6
  195. package/dist/language/flows/flows.js +90 -15
  196. package/dist/language/flows/heal-run.d.ts +10 -4
  197. package/dist/language/flows/heal-run.js +26 -9
  198. package/dist/language/flows/recording/flake.d.ts +3 -27
  199. package/dist/language/flows/recording/flake.js +1 -1
  200. package/dist/language/flows/recording/recording-backtrack.js +1 -4
  201. package/dist/language/flows/recording/tape/recordings.d.ts +11 -1
  202. package/dist/language/flows/recording/tape/recordings.js +9 -3
  203. package/dist/language/flows/replay.d.ts +25 -5
  204. package/dist/language/flows/replay.js +83 -16
  205. package/dist/language/flows/stores/assertion-tiers-store.js +1 -1
  206. package/dist/language/flows/suite/coverage.d.ts +9 -0
  207. package/dist/language/flows/suite/coverage.js +12 -1
  208. package/dist/language/flows/suite-selection.d.ts +25 -0
  209. package/dist/language/flows/suite-selection.js +41 -9
  210. package/dist/machine/git-tracks.d.ts +6 -0
  211. package/dist/machine/git-tracks.js +36 -0
  212. package/dist/memory/cloud/background-sync.d.ts +25 -0
  213. package/dist/memory/cloud/background-sync.js +79 -0
  214. package/dist/memory/cloud/cloud-config.d.ts +12 -1
  215. package/dist/memory/cloud/cloud-config.js +39 -13
  216. package/dist/memory/cloud/cloud-sync.d.ts +3 -24
  217. package/dist/memory/cloud/cloud-sync.js +5 -2
  218. package/dist/memory/cloud/harness-config.d.ts +0 -7
  219. package/dist/memory/cloud/memory-scope.d.ts +19 -0
  220. package/dist/memory/cloud/memory-scope.js +48 -0
  221. package/dist/memory/cloud/sync-answer.d.ts +43 -0
  222. package/dist/memory/cloud/sync-answer.js +42 -0
  223. package/dist/memory/cloud/sync-cycle.d.ts +40 -10
  224. package/dist/memory/cloud/sync-cycle.js +97 -144
  225. package/dist/memory/cloud/sync-daemon.d.ts +50 -3
  226. package/dist/memory/cloud/sync-daemon.js +473 -83
  227. package/dist/memory/cloud/sync-describe.d.ts +28 -0
  228. package/dist/memory/cloud/sync-describe.js +92 -0
  229. package/dist/memory/cloud/sync-disk.d.ts +9 -1
  230. package/dist/memory/cloud/sync-disk.js +137 -33
  231. package/dist/memory/cloud/sync-flow-versions.d.ts +26 -0
  232. package/dist/memory/cloud/sync-flow-versions.js +42 -0
  233. package/dist/memory/impact/impact-store.js +32 -15
  234. package/dist/memory/intent/intent-shard.d.ts +41 -266
  235. package/dist/memory/intent/intent-tools.js +20 -61
  236. package/dist/memory/intent/record-request.d.ts +27 -0
  237. package/dist/memory/intent/record-request.js +88 -0
  238. package/dist/memory/intent/settled-words.js +9 -1
  239. package/dist/memory/journal/drive-flow.d.ts +3 -0
  240. package/dist/memory/journal/drive-flow.js +11 -41
  241. package/dist/memory/journal/envelope-store.js +1 -1
  242. package/dist/memory/journal/envelope.d.ts +6 -154
  243. package/dist/memory/journal/episode-log.d.ts +107 -0
  244. package/dist/memory/journal/episode-log.js +302 -0
  245. package/dist/memory/journal/journal-recorder.d.ts +7 -0
  246. package/dist/memory/journal/journal-recorder.js +22 -9
  247. package/dist/memory/journal/on-disk/ambient-file.d.ts +1 -7
  248. package/dist/memory/journal/on-disk/ambient-file.js +1 -1
  249. package/dist/memory/journal/on-disk/workspace-gitignore.d.ts +24 -3
  250. package/dist/memory/journal/on-disk/workspace-gitignore.js +38 -7
  251. package/dist/memory/journal/on-disk/workspace-tiers.js +7 -0
  252. package/dist/memory/journal/self-observation.js +3 -1
  253. package/dist/memory/journal/session-end.js +23 -9
  254. package/dist/memory/journal/session-journal.js +39 -20
  255. package/dist/memory/project/artifact-root-resolver.d.ts +14 -1
  256. package/dist/memory/project/artifact-root-resolver.js +68 -23
  257. package/dist/memory/project/artifact-root.d.ts +17 -3
  258. package/dist/memory/project/artifact-root.js +47 -9
  259. package/dist/memory/project/fs/fs-port.d.ts +5 -0
  260. package/dist/memory/project/fs/fs-port.js +13 -0
  261. package/dist/memory/project/learned-routes.d.ts +3 -4
  262. package/dist/memory/project/learned-routes.js +3 -11
  263. package/dist/memory/project/link-directory.js +5 -2
  264. package/dist/memory/project/root-claims.d.ts +3 -1
  265. package/dist/memory/project/root-claims.js +41 -7
  266. package/dist/memory/project/session-root.d.ts +1 -1
  267. package/dist/memory/project/session-root.js +1 -15
  268. package/dist/memory/project/sync-status.js +5 -6
  269. package/dist/memory/recall/memory-tools.js +2 -2
  270. package/dist/memory/recall/platform-memory.d.ts +3 -1
  271. package/dist/memory/recall/platform-memory.js +9 -1
  272. package/dist/memory/recall/project-memory.js +15 -19
  273. package/dist/portal/bridge/auth-failure-reason.d.ts +13 -0
  274. package/dist/portal/bridge/auth-failure-reason.js +19 -0
  275. package/dist/portal/bridge/bridge.d.ts +19 -4
  276. package/dist/portal/bridge/bridge.js +150 -177
  277. package/dist/portal/bridge/harness-request.d.ts +15 -0
  278. package/dist/portal/bridge/harness-request.js +19 -0
  279. package/dist/portal/bridge/origin/origin-follow.d.ts +46 -0
  280. package/dist/portal/bridge/origin/origin-follow.js +110 -0
  281. package/dist/portal/bridge/pairing-token.d.ts +6 -0
  282. package/dist/portal/bridge/pairing-token.js +47 -16
  283. package/dist/portal/bridge/protocol-skew.d.ts +27 -2
  284. package/dist/portal/bridge/protocol-skew.js +66 -2
  285. package/dist/portal/bridge/session-nonces.d.ts +46 -0
  286. package/dist/portal/bridge/session-nonces.js +72 -0
  287. package/dist/portal/input/viewport-tools.d.ts +31 -0
  288. package/dist/portal/input/viewport-tools.js +106 -15
  289. package/dist/portal/pool/bounded-close.d.ts +23 -0
  290. package/dist/portal/pool/bounded-close.js +38 -0
  291. package/dist/portal/pool/browser-pool.d.ts +44 -39
  292. package/dist/portal/pool/browser-pool.js +247 -95
  293. package/dist/portal/pool/context-permissions.d.ts +9 -1
  294. package/dist/portal/pool/context-permissions.js +26 -0
  295. package/dist/portal/pool/lease-emulation.d.ts +37 -0
  296. package/dist/portal/pool/lease-emulation.js +29 -0
  297. package/dist/portal/pool/playwright-launcher.d.ts +6 -0
  298. package/dist/portal/pool/playwright-launcher.js +74 -7
  299. package/dist/portal/pool/pool-contract.d.ts +23 -1
  300. package/dist/portal/pool/pool-lease.d.ts +128 -0
  301. package/dist/portal/pool/pool-lease.js +49 -0
  302. package/dist/portal/pool/zero-install.d.ts +8 -1
  303. package/dist/portal/pool/zero-install.js +24 -2
  304. package/dist/portal/production/production-activation.d.ts +136 -0
  305. package/dist/portal/production/production-activation.js +173 -0
  306. package/dist/portal/production/production-drive.d.ts +52 -0
  307. package/dist/portal/production/production-drive.js +301 -0
  308. package/dist/portal/production/production-hello.d.ts +23 -0
  309. package/dist/portal/production/production-hello.js +23 -0
  310. package/dist/portal/realm/web-realm.js +42 -15
  311. package/dist/portal/session/control-envelope.d.ts +1 -1
  312. package/dist/portal/session/dev-server/served-document.d.ts +0 -5
  313. package/dist/portal/session/dev-server/served-document.js +6 -8
  314. package/dist/portal/session/facts/handshake-facts.d.ts +8 -1
  315. package/dist/portal/session/first-run-wiring.d.ts +3 -36
  316. package/dist/portal/session/first-run-wiring.js +10 -103
  317. package/dist/portal/session/live-control-tools.js +61 -19
  318. package/dist/portal/session/no-session-next-action.d.ts +11 -0
  319. package/dist/portal/session/no-session-next-action.js +40 -15
  320. package/dist/portal/session/no-session-watch.d.ts +7 -0
  321. package/dist/portal/session/no-session-watch.js +67 -23
  322. package/dist/portal/session/output-budget.d.ts +10 -0
  323. package/dist/portal/session/output-budget.js +16 -0
  324. package/dist/portal/session/presence/session-lease.d.ts +2 -0
  325. package/dist/portal/session/presence/session-lease.js +8 -0
  326. package/dist/portal/session/recorded-gaps.d.ts +15 -11
  327. package/dist/portal/session/recorded-gaps.js +39 -18
  328. package/dist/portal/session/serving-directory.d.ts +24 -2
  329. package/dist/portal/session/serving-directory.js +93 -5
  330. package/dist/portal/session/session-info.d.ts +26 -0
  331. package/dist/portal/session/session-info.js +20 -0
  332. package/dist/portal/session/session-manager.d.ts +35 -3
  333. package/dist/portal/session/session-manager.js +69 -14
  334. package/dist/portal/session/session-successor.js +1 -9
  335. package/dist/portal/session/session-tools.js +22 -4
  336. package/dist/portal/session/session.d.ts +10 -10
  337. package/dist/portal/session/session.js +37 -29
  338. package/dist/portal/session/zero-install-first-call.d.ts +7 -2
  339. package/dist/portal/session/zero-install-first-call.js +43 -20
  340. package/dist/start-wiring.d.ts +2 -2
  341. package/dist/start-wiring.js +17 -3
  342. package/dist/status-payload.d.ts +11 -1
  343. package/dist/status-payload.js +7 -1
  344. package/dist/surface/agent-presence-grace.d.ts +11 -0
  345. package/dist/surface/agent-presence-grace.js +34 -0
  346. package/dist/surface/http-server.d.ts +12 -1
  347. package/dist/surface/http-server.js +64 -15
  348. package/dist/surface/mcp/faults/mcp-outage.d.ts +12 -2
  349. package/dist/surface/mcp/faults/mcp-outage.js +19 -1
  350. package/dist/surface/mcp/mcp-proxy.d.ts +17 -74
  351. package/dist/surface/mcp/mcp-proxy.js +194 -267
  352. package/dist/surface/mcp/mcp.d.ts +20 -3
  353. package/dist/surface/mcp/mcp.js +88 -25
  354. package/dist/surface/mcp/proxy/drive-calls.d.ts +44 -0
  355. package/dist/surface/mcp/proxy/drive-calls.js +114 -5
  356. package/dist/surface/mcp/proxy/proxy-boot.d.ts +11 -0
  357. package/dist/surface/mcp/proxy/proxy-boot.js +1 -0
  358. package/dist/surface/mcp/proxy/proxy-daemon-probe.d.ts +6 -0
  359. package/dist/surface/mcp/proxy/proxy-daemon-probe.js +14 -1
  360. package/dist/surface/mcp/proxy/proxy-handshake.d.ts +34 -2
  361. package/dist/surface/mcp/proxy/proxy-handshake.js +76 -8
  362. package/dist/surface/mcp/server-instructions.js +35 -16
  363. package/dist/surface/mcp/shared-params.d.ts +8 -0
  364. package/dist/surface/mcp/shared-params.js +15 -1
  365. package/dist/surface/mcp/sse-frame-parser.d.ts +11 -0
  366. package/dist/surface/mcp/sse-frame-parser.js +18 -0
  367. package/dist/surface/mcp/surface-vocabulary.d.ts +2 -0
  368. package/dist/surface/mcp/surface-vocabulary.js +13 -0
  369. package/dist/surface/mcp/tool-catalog-cache.d.ts +2 -0
  370. package/dist/surface/mcp/tool-catalog-cache.js +14 -4
  371. package/dist/surface/tools/account-tool.d.ts +4 -3
  372. package/dist/surface/tools/account-tool.js +20 -31
  373. package/dist/surface/tools/act/act-danger.d.ts +2 -0
  374. package/dist/surface/tools/act/act-danger.js +41 -14
  375. package/dist/surface/tools/act/act-observation.d.ts +11 -0
  376. package/dist/surface/tools/act/act-observation.js +33 -0
  377. package/dist/surface/tools/act/act-preflight.js +10 -3
  378. package/dist/surface/tools/act/act-sequence-retry.js +2 -1
  379. package/dist/surface/tools/act/act-target.js +19 -5
  380. package/dist/surface/tools/act/durable.js +1 -1
  381. package/dist/surface/tools/act/navigation/navigate-arrival.d.ts +9 -7
  382. package/dist/surface/tools/act/navigation/navigate-arrival.js +11 -14
  383. package/dist/surface/tools/act/unsettled-find.d.ts +36 -0
  384. package/dist/surface/tools/act/unsettled-find.js +71 -0
  385. package/dist/surface/tools/act-sequence-tool.js +53 -24
  386. package/dist/surface/tools/act-tools.d.ts +2 -3
  387. package/dist/surface/tools/act-tools.js +49 -47
  388. package/dist/surface/tools/agent/compact-envelope.d.ts +2 -2
  389. package/dist/surface/tools/agent/for-the-agent.js +41 -3
  390. package/dist/surface/tools/assert/assert-grade.js +8 -3
  391. package/dist/surface/tools/assert/assert-verdict.js +4 -2
  392. package/dist/surface/tools/browser-tools.js +7 -4
  393. package/dist/surface/tools/chat-drives.js +50 -25
  394. package/dist/surface/tools/coverage-tools.js +1 -1
  395. package/dist/surface/tools/dynamic-tools.d.ts +8 -0
  396. package/dist/surface/tools/dynamic-tools.js +89 -34
  397. package/dist/surface/tools/empty-session-list.d.ts +18 -25
  398. package/dist/surface/tools/empty-session-list.js +67 -38
  399. package/dist/surface/tools/error-recovery.d.ts +17 -7
  400. package/dist/surface/tools/error-recovery.js +53 -20
  401. package/dist/surface/tools/explore-tools.d.ts +10 -0
  402. package/dist/surface/tools/explore-tools.js +72 -19
  403. package/dist/surface/tools/feedback-tools.js +23 -2
  404. package/dist/surface/tools/gap-novelty.d.ts +6 -0
  405. package/dist/surface/tools/gap-novelty.js +9 -0
  406. package/dist/surface/tools/gaps/capability-absences.js +4 -3
  407. package/dist/surface/tools/harness-explore.d.ts +1 -1
  408. package/dist/surface/tools/harness-explore.js +6 -4
  409. package/dist/surface/tools/harness-gate.d.ts +43 -21
  410. package/dist/surface/tools/harness-gate.js +55 -81
  411. package/dist/surface/tools/harness-script.d.ts +2 -0
  412. package/dist/surface/tools/harness-script.js +11 -6
  413. package/dist/surface/tools/harness-toolset.js +3 -4
  414. package/dist/surface/tools/invoke-tool.d.ts +0 -8
  415. package/dist/surface/tools/invoke-tool.js +164 -178
  416. package/dist/surface/tools/lease-hint.d.ts +9 -0
  417. package/dist/surface/tools/lease-hint.js +22 -3
  418. package/dist/surface/tools/lease-permissions.d.ts +1 -1
  419. package/dist/surface/tools/lease-readiness.d.ts +55 -0
  420. package/dist/surface/tools/lease-readiness.js +150 -2
  421. package/dist/surface/tools/lease-tools.d.ts +3 -75
  422. package/dist/surface/tools/lease-tools.js +124 -188
  423. package/dist/surface/tools/map-tools.d.ts +20 -0
  424. package/dist/surface/tools/map-tools.js +145 -0
  425. package/dist/surface/tools/merge-tools.js +11 -7
  426. package/dist/surface/tools/next-step.js +6 -3
  427. package/dist/surface/tools/observe-tools.js +46 -35
  428. package/dist/surface/tools/platform-moment.d.ts +3 -12
  429. package/dist/surface/tools/platform-moment.js +12 -24
  430. package/dist/surface/tools/read/complete-snapshot.d.ts +7 -1
  431. package/dist/surface/tools/read/complete-snapshot.js +13 -2
  432. package/dist/surface/tools/read/query-paginate.js +6 -0
  433. package/dist/surface/tools/read/query-shape.d.ts +9 -0
  434. package/dist/surface/tools/read/query-shape.js +15 -0
  435. package/dist/surface/tools/read/snapshot-unmounted-note.d.ts +1 -0
  436. package/dist/surface/tools/read/snapshot-unmounted-note.js +73 -0
  437. package/dist/surface/tools/read-tools.js +5 -5
  438. package/dist/surface/tools/real-input-attempt.d.ts +11 -0
  439. package/dist/surface/tools/real-input-attempt.js +206 -38
  440. package/dist/surface/tools/reconcile-tools.js +21 -8
  441. package/dist/surface/tools/reticle-result.d.ts +3 -0
  442. package/dist/surface/tools/reticle-result.js +8 -1
  443. package/dist/surface/tools/run-report.d.ts +22 -0
  444. package/dist/surface/tools/run-report.js +141 -0
  445. package/dist/surface/tools/session-ports.d.ts +57 -0
  446. package/dist/surface/tools/session-ports.js +102 -0
  447. package/dist/surface/tools/tool-kit.d.ts +58 -4
  448. package/dist/surface/tools/tool-kit.js +140 -6
  449. package/dist/surface/tools/tool-surface.d.ts +2 -0
  450. package/dist/surface/tools/tool-surface.js +45 -0
  451. package/dist/surface/tools/tools.js +49 -87
  452. package/dist/telemetry/automation-hint.d.ts +5 -0
  453. package/dist/telemetry/automation-hint.js +7 -0
  454. package/dist/telemetry/cli-telemetry.d.ts +22 -0
  455. package/dist/telemetry/cli-telemetry.js +80 -11
  456. package/dist/telemetry/daemon-telemetry.d.ts +3 -1
  457. package/dist/telemetry/daemon-telemetry.js +9 -2
  458. package/dist/telemetry/feedback-cli.d.ts +1 -1
  459. package/dist/telemetry/feedback-cli.js +8 -3
  460. package/dist/telemetry/feedback.d.ts +1 -1
  461. package/dist/telemetry/feedback.js +51 -4
  462. package/dist/telemetry/harness-run.d.ts +3 -3
  463. package/dist/telemetry/harness-run.js +18 -7
  464. package/dist/telemetry/hud-events.d.ts +1 -1
  465. package/dist/telemetry/hud-metrics.d.ts +6 -3
  466. package/dist/telemetry/hud-metrics.js +20 -8
  467. package/dist/telemetry/install-source.js +1 -1
  468. package/dist/telemetry/mcp-connection.d.ts +1 -1
  469. package/dist/telemetry/mcp-connection.js +21 -2
  470. package/dist/telemetry/onboarding-firsts.d.ts +6 -1
  471. package/dist/telemetry/onboarding-firsts.js +41 -1
  472. package/dist/telemetry/proxy-telemetry.d.ts +42 -0
  473. package/dist/telemetry/proxy-telemetry.js +70 -1
  474. package/dist/telemetry/session-metrics.d.ts +1 -3
  475. package/dist/telemetry/session-metrics.js +16 -12
  476. package/dist/telemetry/telemetry.d.ts +9 -1
  477. package/dist/telemetry/telemetry.js +16 -1
  478. package/dist/telemetry/verification-of.d.ts +3 -1
  479. package/dist/telemetry/verification-of.js +7 -1
  480. package/dist/wire-journal.js +12 -2
  481. package/docs/README.md +4 -2
  482. package/docs/actions.mdx +7 -6
  483. package/docs/adapters.md +2 -1
  484. package/docs/agent-cheatsheet.md +19 -5
  485. package/docs/architecture.md +13 -10
  486. package/docs/autodrive.md +6 -5
  487. package/docs/benchmarks.md +2 -2
  488. package/docs/capabilities.mdx +1 -1
  489. package/docs/cli/affected.mdx +1 -1
  490. package/docs/cli/cloud.mdx +6 -6
  491. package/docs/cli/doctor.mdx +3 -1
  492. package/docs/cli/feedback.mdx +11 -7
  493. package/docs/cli/gate.mdx +1 -1
  494. package/docs/cli/help.mdx +1 -1
  495. package/docs/cli/init.mdx +20 -16
  496. package/docs/cli/mcp.mdx +5 -2
  497. package/docs/cli/open.mdx +5 -2
  498. package/docs/cli/report.mdx +1 -1
  499. package/docs/cli/restart.mdx +1 -1
  500. package/docs/cli/rollback.mdx +4 -4
  501. package/docs/cli/serve.mdx +7 -5
  502. package/docs/cli/setup.mdx +1 -1
  503. package/docs/cli/status.mdx +6 -2
  504. package/docs/cli/stop.mdx +3 -3
  505. package/docs/cli/telemetry.mdx +5 -1
  506. package/docs/cli/try.mdx +8 -8
  507. package/docs/cli/update.mdx +8 -5
  508. package/docs/cli/verify.mdx +12 -5
  509. package/docs/cli/version.mdx +1 -1
  510. package/docs/cli.mdx +10 -6
  511. package/docs/debugging.md +1 -1
  512. package/docs/deploy-checks.md +8 -6
  513. package/docs/desktop.mdx +14 -18
  514. package/docs/enterprise.md +6 -6
  515. package/docs/environment.mdx +5 -3
  516. package/docs/faq.mdx +9 -6
  517. package/docs/first-drive.md +19 -9
  518. package/docs/flows.md +3 -4
  519. package/docs/for-agents.md +1 -1
  520. package/docs/frameworks.mdx +34 -11
  521. package/docs/getting-started.md +46 -29
  522. package/docs/http-transport.md +2 -2
  523. package/docs/human-control.md +8 -8
  524. package/docs/index.mdx +2 -2
  525. package/docs/install-agentic.mdx +18 -19
  526. package/docs/install-manual.mdx +8 -4
  527. package/docs/integration-patterns.md +5 -5
  528. package/docs/multi-agent-testing.md +7 -5
  529. package/docs/packages/browser.mdx +34 -2
  530. package/docs/packages/core.mdx +2 -1
  531. package/docs/packages/electron.mdx +15 -3
  532. package/docs/packages/eslint-plugin.mdx +1 -1
  533. package/docs/packages/next.mdx +17 -5
  534. package/docs/packages/react.mdx +8 -1
  535. package/docs/packages/server.mdx +3 -3
  536. package/docs/packages/tauri.mdx +3 -3
  537. package/docs/packages/vite-plugin.mdx +1 -1
  538. package/docs/packages.mdx +19 -6
  539. package/docs/platform-integration.md +12 -4
  540. package/docs/predicates.mdx +18 -7
  541. package/docs/production-mode.md +108 -0
  542. package/docs/prompt-capture.md +99 -0
  543. package/docs/quickstart.mdx +14 -12
  544. package/docs/recipes.mdx +9 -8
  545. package/docs/required-checks.md +3 -2
  546. package/docs/skill-file.mdx +4 -3
  547. package/docs/skill-setup.md +2 -2
  548. package/docs/skill.md +62 -70
  549. package/docs/state-files.md +1 -0
  550. package/docs/state-management.mdx +11 -9
  551. package/docs/system-map.md +14 -13
  552. package/docs/telemetry.md +32 -24
  553. package/docs/testing.md +9 -5
  554. package/docs/tools/act-and-wait.mdx +17 -13
  555. package/docs/tools/act-sequence.mdx +40 -66
  556. package/docs/tools/act.mdx +7 -5
  557. package/docs/tools/assert.mdx +1 -1
  558. package/docs/tools/look.mdx +1 -1
  559. package/docs/tools/navigate.mdx +1 -1
  560. package/docs/tools/network.mdx +13 -11
  561. package/docs/tools/overview.mdx +3 -3
  562. package/docs/tools/query.mdx +3 -3
  563. package/docs/tools/session-and-feedback.mdx +9 -6
  564. package/docs/tools/sessions.mdx +7 -0
  565. package/docs/tools/snapshot.mdx +9 -7
  566. package/docs/tools/tools-and-run.mdx +11 -10
  567. package/docs/tools/verify.mdx +1 -1
  568. package/docs/troubleshooting.mdx +21 -9
  569. package/docs/usage.md +58 -43
  570. package/docs/use-cases.mdx +2 -2
  571. package/docs/vs-chrome-devtools-mcp.mdx +5 -5
  572. package/docs/vs-playwright-mcp.mdx +5 -4
  573. package/docs/what-is-recorded.md +12 -7
  574. package/docs/what-is-reticle.mdx +2 -2
  575. package/docs/why-reticle.mdx +1 -1
  576. package/package.json +9 -12
  577. package/dist/command/dev/stale-issue-guard.d.ts +0 -34
  578. package/dist/command/dev/stale-issue-guard.js +0 -69
@@ -2,13 +2,14 @@
2
2
  title: System map
3
3
  description: 'How a tool call travels from a coding agent to your app and back, and which of those failures are silent.'
4
4
  icon: map
5
+ noindex: true
5
6
  ---
6
7
 
7
- A tool call crosses five processes and four hops: coding agent → `reticle mcp` proxy (stdio) → daemon on `:4400` (HTTP POST out, SSE back) → browser SDK (WebSocket) → your app. Each hop has its own failure vocabulary and its own way of lying, and the ones marked **silent** below fail with no error reaching either the agent or you.
8
+ A tool call crosses five processes and four hops: coding agent → `reticle mcp` proxy (stdio) → daemon on port `4400` by default (HTTP POST out, SSE back) → browser SDK (WebSocket) → your app. When another project's daemon holds that port, the proxy finds this project's daemon where it moved, or starts one on a free port. Each hop has its own failure vocabulary and its own way of lying, and the ones marked **silent** below fail with no error reaching either the agent or you.
8
9
 
9
10
  > How a tool call gets from a coding agent to your app and back, what each hop can do wrong, and which of those failures are **silent**. Written for anyone touching the transport, the daemon, the bridge, or a gate, and for contributors trying to work out where their change lives.
10
11
  >
11
- > Companion pages: [`harness-rules.md`](https://github.com/reticlehq/reticle/blob/main/apps/e2e/harness-rules.md) (what a gate must do about all this) and [`telemetry-contract.md`](https://github.com/reticlehq/reticle/blob/main/docs/telemetry-contract.md) (the rules for anything that emits).
12
+ > Companion pages: [`harness-rules.md`](https://github.com/reticlehq/reticle/blob/main/apps/e2e/harness-rules.md) (what a gate must do about all this) and [`contributing/telemetry/contract.md`](https://github.com/reticlehq/reticle/blob/main/contributing/telemetry/contract.md) (the rules for anything that emits).
12
13
 
13
14
  ## 1. Topology: 5 processes, 4 hops
14
15
 
@@ -37,13 +38,13 @@ Each hop has its own failure vocabulary, its own recovery, and its own way of ly
37
38
  | # | Step | Owner | Fails how |
38
39
  | --- | --- | --- | --- |
39
40
  | 1 | client spawns `reticle mcp` over stdio | client | the client's own startup timeout; **no host respawns a dead stdio server** |
40
- | 2 | proxy probes `:4400` (`probeDaemon`, plain TCP, 500ms) | proxy | a squatter that accepts and never serves looks alive |
41
+ | 2 | proxy probes the bridge port, `4400` by default (`probeDaemon`, plain TCP, 500ms) | proxy | a squatter that accepts and never serves looks alive |
41
42
  | 3 | absent → spawn daemon detached, poll to `DAEMON_READY_TIMEOUT_MS` (10s) | proxy | slow CI; raise with `RETICLE_DAEMON_READY_TIMEOUT_MS` |
42
43
  | 4 | proxy opens SSE, waits for the `endpoint` frame → POST URL | proxy | an empty frame used to read as "connected"; now refused (`buildSessionUrl`) |
43
44
  | 5 | `initialize`, answered **locally** after `LOCAL_HANDSHAKE_MS` (12s) when the daemon cannot | proxy | any client with a startup budget under ~12s loses Reticle for the whole session |
44
45
  | 6 | daemon binds `:4400`, writes the pid file, starts bridge + idle watcher | daemon | `EADDRINUSE` → child exits 1, **parent already exited 0** |
45
46
  | 7 | app calls `reticle.connect()` → `ws://localhost:4400/reticle` | SDK | wrong port; missing pairing token; mixed content (Safari throws synchronously on `new WebSocket`) |
46
- | 8 | HELLO: `sessionId`, `projectId`, `protocolVersion`, capabilities | SDK → bridge | version mismatch → `1008` close → **the SDK stops retrying, permanently** |
47
+ | 8 | HELLO: `sessionId`, `projectId`, `protocolVersion`, capabilities | SDK → bridge | version mismatch → `1008` close → **the SDK stops its timed retries**. It dials once more when the person returns to the tab |
47
48
  | 9 | bridge authenticates: pairing token / loopback tier / Origin allow-list | bridge | another project's daemon answers → `authFailureReason` names it |
48
49
  | 10 | Session registered, `everConnected = true` | daemon | n/a |
49
50
  | 11 | tool call → `runTool` → `session.send` → WS command → SDK → reply | all | any hop above, mid-flight |
@@ -56,13 +57,13 @@ Three different counts, all measured, and the difference between them matters:
56
57
 
57
58
  | Count | What it is |
58
59
  | --- | --- |
59
- | **77** | name constants in `ReticleTool` |
60
+ | **78** | name constants in `ReticleTool` |
60
61
  | **30** | tools advertised under `RETICLE_ADVERTISE_ALL_TOOLS=1`, i.e. what `tool-surface-sweep-test` drives |
61
62
  | **10** | advertised by default; the rest are reached through `reticle_run { tool, args }` |
62
63
 
63
64
  The middle number is **family folding**: `reticle_baseline`, `reticle_session`, `reticle_record`, `reticle_flow` and `reticle_lease` each absorb their members behind an `action` parameter. So "every tool is callable" is asserted over the advertised surfaces, not over every named behaviour; a family member reachable only through an `action` value the sweep never passes is not covered by it. Worth closing when the per-tool budget lands (see [`gate-plan.md`](https://github.com/reticlehq/reticle/blob/main/docs/gate-plan.md), Phase 4).
64
65
 
65
- These three were each wrong by the time somebody checked: the table read 68 / 48 / 19 against a measured 76 / 30 / 10 (77 constants since), and the middle one had a derived token cost hanging off it. `surface-sizes.test.ts` is the authority and fails when a surface changes size, so prefer reading it to trusting this row.
66
+ These three were each wrong by the time somebody checked: the table read 68 / 48 / 19 against a measured 76 / 30 / 10 (78 constants since), and the middle one had a derived token cost hanging off it. `surface-sizes.test.ts` is the authority and fails when a surface changes size, so prefer reading it to trusting this row.
66
67
 
67
68
  | Token | Produced by | Consumed by | Invalidated by |
68
69
  | --- | --- | --- | --- |
@@ -116,7 +117,7 @@ Still fragile:
116
117
 
117
118
  | Failure | Silent | Note |
118
119
  | --- | --- | --- |
119
- | `serve` exits 0 on `EADDRINUSE` | **yes** | #115: the parent reports the spawn, the child reports the bind, nothing joins them |
120
+ | `serve` exits 0 on `EADDRINUSE` | **fixed** | `port-presence.ts` tells `daemon`, `foreign` and `free` apart. `serve` refuses a port that a stranger holds, so it does not report a daemon that never bound. Guarded by `daemon-port-honesty-test` (#115) |
120
121
  | liveness is the pid file, never the port | **yes** | `isRunning` in `server/src/command/daemon/daemon.ts` |
121
122
  | idle shutdown fires mid-install | **yes** | 5min base / 30min attached; already scored three fixtures as install failures |
122
123
  | `isUselessDaemon` ends a daemon that served nothing | **yes** | lifetime facts, but a gate that installs for six minutes before its first call is that daemon |
@@ -128,7 +129,7 @@ Still fragile:
128
129
 
129
130
  | Failure | Silent | Note |
130
131
  | --- | --- | --- |
131
- | protocol mismatch → `1008` | no, but **terminal** | the SDK stops retrying by design; the app is dark until reload (#127) |
132
+ | protocol mismatch → `1008` | no, but **terminal** | the SDK stops its timed retries by design. It dials once more when the person returns to the tab, and the app stays dark until then (#127) |
132
133
  | another project's daemon answers | no | `authFailureReason` is evidence-based, only claiming it when the daemon has demonstrably served another project |
133
134
  | `rejectAll('session disconnected')` | **fixed** | used to become `verified:"no" / assertion_failed`, i.e. Reticle blaming the app, by file and line, for its own lost connection. Now `verified:"unknown" / observation_lost` (#124) |
134
135
  | reaper ends a quiet session | no | revivable on the agent's next action |
@@ -148,13 +149,13 @@ Still fragile:
148
149
  | Layer | Class | Covered by | Gap |
149
150
  | --- | --- | --- | --- |
150
151
  | stdio | proxy survival | `mcp-survives-test` | external SIGKILL |
151
- | SSE/POST | unanswered calls | `mcp-stress-test` | fault injection is `kill -9`, not specified toxics |
152
- | daemon | lifecycle, idle, port | `daemon-lifecycle-test`, `status-honesty-test` | port arbitration, heartbeat |
152
+ | SSE/POST | unanswered calls | `mcp-stress-test`, `transport-faults-test` (`fault-proxy.mjs`) | n/a |
153
+ | daemon | lifecycle, idle, port | `daemon-lifecycle-test`, `status-honesty-test`, `daemon-port-honesty-test`, `daemon-heartbeat-test` | port arbitration |
153
154
  | bridge | auth, origins, version | `version-skew-test` | desktop origins under install |
154
155
  | SDK | queue, gaps, reconnect | browser unit tests | mid-session outage → verdict |
155
- | tools | surface, schema, refusals | `tool-surface-sweep-test`, `tool-fuzz-test` | latency/failure budget |
156
+ | tools | surface, schema, refusals | `tool-surface-sweep-test`, `tool-fuzz-test`, the soak (`bench/TOOL-PROFILE.md`) | latency is recorded, not gated |
156
157
  | verdict | honesty | `release-smoke-test` | transport ≠ app (#124); false-green rate |
157
- | telemetry | silent absence | `telemetry-events-test`, `telemetry-contract.test.ts` | chokepoint coverage |
158
+ | telemetry | silent absence | `telemetry-events-test`, `telemetry-contract.test.ts`, `dispatch-chokepoint.test.ts` | n/a |
158
159
  | install | pristine-surface wiring | `pnpm gate:install` (thirteen pristine scaffolds) | apps with many plugins: the `reticle-fixtures` repo |
159
160
  | client | per-agent MCP compatibility | nothing | the whole class |
160
161
 
@@ -168,4 +169,4 @@ Nine of ten layers have a gate. The one that does not, per-agent MCP compatibili
168
169
  - **zero orphan spans**; 23 orphans on one healthy run is the documented signature of a hung call
169
170
  - `settleTiming` always fires; it sits in a `finally` so a thrown call cannot leak a concurrency slot
170
171
 
171
- Today that output is produced and discarded.
172
+ `trace-shape-test` (`apps/e2e/trace-shape.mjs`) checks the first two on every battery run. The `settleTiming` invariant has no check yet.
package/docs/telemetry.md CHANGED
@@ -1,16 +1,16 @@
1
1
  ---
2
2
  title: Telemetry
3
- description: 'The complete description of the anonymous usage data Reticle collects, what it is for, and how to turn it off.'
3
+ description: 'The complete description of the usage data Reticle collects, what it is for, and how to turn it off.'
4
4
  icon: chart-line
5
5
  ---
6
6
 
7
- Reticle sends anonymous usage counters (which commands ran, which tools an agent called, whether a verification produced a verdict) and nothing from the app under test. To turn it off permanently, run `npx @reticlehq/server telemetry disable`.
7
+ Reticle sends usage counters (which commands ran, which tools an agent called, whether a verification produced a verdict) and nothing from the app under test. To turn it off permanently, run `npx @reticlehq/server telemetry disable`.
8
8
 
9
9
  This page is the complete description of what is collected. If something is not listed here, it is not sent.
10
10
 
11
11
  ## The short version
12
12
 
13
- - **Anonymous.** We cannot tell who you are, and we do not try.
13
+ - **No name, no email, no account.** The events do not carry who you are, and we do not try to find out. Two fields can connect events to an organization: the workspace id of a project you linked, and the licence id of a licensed build. See [the fields on every event](#what-is-sent).
14
14
  - **No code, no app data.** Nothing from the app under test (no DOM, no network traffic, no console output, no source, no file paths) ever leaves your machine.
15
15
  - **The one exception is feedback you deliberately send us**, and it is never collected passively. See [Feedback](#feedback) below.
16
16
  - **Opt out any time**, permanently, with one command:
@@ -21,38 +21,41 @@ This page is the complete description of what is collected. If something is not
21
21
 
22
22
  ## What is sent
23
23
 
24
- Twenty-two kinds of events, each a single small JSON object. This is the whole list, and it is the same list the code enumerates:
24
+ Twenty-five kinds of events, each a single small JSON object. This is the whole list, and it is the same list the code enumerates:
25
25
 
26
26
  | Event | When | Extra data |
27
27
  | --- | --- | --- |
28
- | `reticle_installed` | The first time Reticle runs on a machine | n/a |
28
+ | `reticle_installed` | The first real command Reticle runs on a machine. `--version`, `--help` and `telemetry` do not count | Which published install route you came through, declared by that route with `RETICLE_INSTALL_SOURCE` and never inferred, and `unknown` when nothing declared one |
29
29
  | `cli_command_run` | You run a `reticle` command | Which subcommand (`verify`, `status`, …) and which flags were present, by name |
30
30
  | `daemon_started` | The local daemon starts | n/a |
31
31
  | `daemon_stopped` | The local daemon stops | A summary of the session; see below |
32
32
  | `session_progress` | Periodically, from a daemon that is still running | The same summary as `daemon_stopped`, marked `final: false`. It exists so a long session is not invisible until it exits |
33
- | `app_instrumented` | The first time your app's SDK connects in a given daemon run | Nothing beyond the common fields. It is the second half of the install funnel: registering the MCP server is one thing, getting the SDK into a running page is another |
34
- | `verification_completed` | A verification produces a verdict | Whether it passed, whether Reticle refused to call a passing check verified, and **why** the verdict came out that way; see below |
33
+ | `app_instrumented` | The first time your app's SDK connects in a given daemon run | Whether `reticle init` ran in this folder, whether an agent was already attached, how long the daemon ran before the app connected, and whether this is the first app ever on this machine and project. It is the second half of the install funnel: registering the MCP server is one thing, getting the SDK into a running page is another |
34
+ | `verification_completed` | A verification produces a verdict | Whether it passed, whether Reticle refused to call a passing check verified, **why** the verdict came out that way, and the version of the Reticle SDK in the page; see below |
35
35
  | `project_profiled` | Once per daemon start | The shape of the project; see below |
36
- | `version_changed` | You update or roll back | The two version numbers, and which direction |
36
+ | `version_changed` | Reticle starts on a version that differs from the last start on this machine, or you update or roll back | The two version numbers, which direction, and whether an agent had been told about the new version |
37
37
  | `runtime_crashed` | The daemon hits an uncaught error | The error's type, **Reticle's own** stack frames, and the message with variables stripped; see below |
38
- | `mcp_client_connected` | An agent attaches to the daemon | Whether it is a reconnect, and how long the daemon had been idle |
39
- | `mcp_connection_lost` | The agent's MCP tools go away | Which stage (`first`, or `budget_spent` when it stopped retrying), the cause, and the attempt count. **At most twice per session**, because a reconnect loop can fire many times in a row and an event each would bill for the pathology instead of measuring it |
38
+ | `mcp_client_connected` | An agent's MCP server attaches to the daemon. A proxy that reconnects does not send it again | Whether this daemon had an agent before, how long the daemon had been up, the client's name from its handshake, whether an app was already connected, and the random id of the process that gives the agent its tools |
39
+ | `mcp_connection_lost` | The agent's MCP tools go away, or come back | Which stage (`first`, `recovered` when the link came back on its own, or `budget_spent` when it stopped retrying), the cause from a closed list, the socket error code from a closed list, the attempt count, how many tool calls in flight the drop killed, and the random id of the process that gives the agent its tools. **Each stage is sent at most once per proxy process** (once for a planned daemon exit and once for a fault), because a reconnect loop can fire many times in a row and an event each would bill for the pathology instead of measuring it. The planned idle exit of the daemon sends no `first` row |
40
+ | `mcp_proxy_exited` | The process that gives your agent its Reticle tools ends | Why it ended (`stdin_end`, `idle`, `client_gone_storm`, `wake_failed`), how long it ran, and two counts: how many times it lost the daemon and how many tool calls it carried, with its random id. Never what the calls were |
41
+ | `daemon_died_unclean` | Reticle finds that an earlier daemon stopped without a clean exit | How long that daemon had run. Nothing else |
42
+ | `wake_refused` | Reticle will not use the daemon port it found, because something else holds it | Whether the holder was another program (`foreign`) or a Reticle daemon for another project (`other_project`). Never the port or the project |
40
43
  | `onboarding_step` | One step of getting set up begins, finishes, fails, is skipped, or is walked away from | Which phase (`install`, `onboard`, `first_run`), which step, what became of it, how long it took, and the framework. The step name comes from a closed list we define -- it can only ever be one of the names in `core/src/onboarding.ts`, so nothing you typed or any path on your disk can arrive in it. `unattended` says whether anybody was asked anything |
41
44
  | `init_completed` | `reticle init` finishes | Whether it worked, a classified reason when it did not, and which published install route you came through, DECLARED by that route via `RETICLE_INSTALL_SOURCE` and never inferred, and `unknown` when nothing declared one |
42
45
  | `bug_found` | Reticle finds a defect in the app under test | The **kind** of defect (`signal-contradicted`, `console-error`, …) and how it was found, never what it was found in |
43
46
  | `tool_refused` | A tool cannot do what an agent asked | Which tool, why (one of six buckets: no session, no match, unsupported, bad arguments, not ready, other), and whether the same call was retried. **Never the message**, because a refusal interpolates whatever you asked for, so only the tool name and the bucket are sent. Capped at 50 per daemon run |
44
47
  | `feedback_submitted` | **Only** when you or your agent explicitly send feedback | The report; see [Feedback](#feedback). The AGENT's call does not wait for the network: the receipt says `accepted` (validated, redacted, queued), never `sent`, and a delivery that then fails is reported back on the agent's next tool result. `reticle feedback` typed by a human still waits, because a person at a terminal is owed the real answer |
45
- | `hud_viewed` | You open a page of the in-page HUD (Agent Log, Flows, Notes, Harness, Impact, Settings) or its account menu | Which page, from a closed list, and how many tabs had a HUD (a bucket: `1`, `2-3`, and so on). The same page reopened in the same tab within 30 seconds counts once |
46
- | `hud_signin_prompted` | The HUD shows you a sign-in or "link this project" prompt | Which prompt (`rail`, `harness` or `header`), once per prompt per tab |
48
+ | `hud_viewed` | You open a page of the in-page HUD (Agent Log, Flows, Notes, Harness, Impact, Settings) or its account menu | Which page, from a closed list, and how many tabs had a HUD (a bucket: `1`, `2-3`, and so on). The same page opened again within 30 seconds counts once for the whole daemon run, also after a reload. The Agent Log that the HUD opens by itself when a page loads is not a view |
49
+ | `hud_signin_prompted` | The HUD shows you a sign-in or "link this project" prompt | Which prompt (`rail`, `harness` or `header`), once per prompt for each daemon run |
47
50
  | `platform_linked` | A project on this machine is linked to a Reticle workspace | How (`connect`, `hud` or `link`) and the workspace's id; see [The platform side](#the-platform-side) |
48
- | `harness_run` | A Reticle Harness run starts, and again when it ends | Who started it (`agent`, `hud`, `dashboard`, `cli_try`, `plan`), whether the platform drove it, and at the end how it went (`proved`, `not_proved`, `failed`, `blocked`, `stopped`) and how many journeys it answered and replayed, as buckets |
51
+ | `harness_run` | A Reticle Harness run starts, and again when it ends | Who started it (`agent`, `hud`, `dashboard`, `cli_try`, `plan`), whether the platform drove it, and at the end how it went (`proved`, `not_proved`, `failed`, `blocked`, `stopped`), how many journeys it answered and replayed, as buckets, and for a `blocked` or `failed` run why it did not drive, from a closed list (`not_linked`, `harness_off`, `no_credits`, and so on) |
49
52
  | `identified` | **Never** from this version. Only an earlier version's `reticle identify`, which has been removed | What you chose to tell us then; see [Telling us who you are](#telling-us-who-you-are) |
50
53
 
51
54
  **There is no per-tool-call event.** Tool usage is counted in memory and leaves once, inside `daemon_stopped`, as a histogram like `{"reticle_act": 40, "reticle_assert": 12}`. That is counts of tool NAMES from a fixed list we define, never arguments, results, selectors, or URLs.
52
55
 
53
56
  **Parameter and flag NAMES are collected; their VALUES are not.** We record that `reticle_act` was called with `ref` and `action`, and that you ran `reticle serve --headed --port`. We do not record what you set them to. This distinction is the whole safety property, and it is absolute for CLI flags: `--http-token` holds a secret, `--drive` holds a URL, `--storage-state` holds a file path, so no flag value is ever sent. For tool parameters there is one narrow exception: a short, explicit allowlist of parameters whose values are enums _we_ defined (`action: "click"`), listed in [`argument-shape.ts`](https://github.com/reticlehq/reticle/blob/main/server/src/telemetry/argument-shape.ts). A value outside that allowlist reports as `other`, so a future schema change cannot quietly start forwarding free text. `reticle_act`'s `args` (the text being typed into your app, which on a login form is a password) is **never** in that allowlist.
54
57
 
55
- `daemon_stopped` carries: how long the session ran, how many tool calls and of which tools, how long each tool took (total and worst case), how many failed, how many verifications ran, browser/lease connection attempts with their failure causes, and which MCP clients connected (`claude-code`, `cursor`). It also carries how the in-page HUD was used: presses per control (`hudControls`, by the control's name from a closed list Reticle defines, a toggle as `name:on` / `name:off`), how long the HUD sat as a bubble, collapsed or open and on which page (`hudViewMs`, `hudPanelMs`), which rail slides were shown (`hudSlides`), and the order of those presses and views, cut at 60 (`hudJourney`). Control names, never anything typed or shown on the page.
58
+ `daemon_stopped` carries: how long the session ran, how many tool calls and of which tools, how many of those calls came before an app connected, the version of the Reticle SDK in the last page that connected, how long each tool took (total and worst case), how many failed, how many verifications ran, browser/lease connection attempts with their failure causes, and which MCP clients connected (`claude-code`, `cursor`). It also carries how the in-page HUD was used: presses per control (`hudControls`, by the control's name from a closed list Reticle defines, a toggle as `name:on` / `name:off`), how long the HUD sat as a bubble, collapsed or open and on which page (`hudViewMs`, `hudPanelMs`), which rail slides were shown (`hudSlides`), and the order of those presses and views, cut at 60 (`hudJourney`). Control names, never anything typed or shown on the page.
56
59
 
57
60
  It also carries a snapshot of the **machine's** state (our own process's memory, free and total system RAM, load average and CPU count), taken at shutdown and again on any crash. This is what separates "your machine ran out of memory" from "Reticle has a bug", which otherwise produce identical-looking failures. No hostname, no username, no paths, no process list.
58
61
 
@@ -98,11 +101,12 @@ Every event carries the same few fields:
98
101
  | Field | What it is | What it is not |
99
102
  | --- | --- | --- |
100
103
  | `anonymousId` | A random UUID minted locally on first run, stored at `~/.reticle/telemetry-id` | Not derived from your name, email, hardware, or anything else about you |
101
- | `projectId` | A one-way SHA-256 hash of the git origin URL, or of the directory path outside a repo | Not reversible: we can count _distinct_ projects, but not learn any project's name, URL, or location. Hashing the origin means one repo counts once instead of once per teammate and per clone |
104
+ | `projectId` | A SHA-256 hash of the git origin URL, or of the directory path outside a repo | Not the URL or the path. We use it to count _distinct_ projects. A hash cannot be turned back into a URL, but anyone who has a URL can hash it and compare: for a public repository, the id can be matched to the repository. Hashing the origin means one repo counts once instead of once per teammate and per clone |
102
105
  | `actor` | Whether a person or an agent caused this: a typed `reticle` command is a person, an MCP tool call is the agent | Not a claim about _why_. Whether you asked your agent to verify, or it decided to, lives in a prompt Reticle never sees, and we do not guess |
103
106
  | `sessionId` | A random id for this daemon run, held in memory and never written to disk | Not a device id and not persistent: a restarted daemon gets a new one. It only groups one run's own events together |
104
107
  | `accountState` / `linked` | Whether this machine is signed out, signed in, or signed in with this project linked to a workspace | Not who is signed in: read from whether a sign-in file exists, never from what is in it |
105
108
  | `workspaceId` | Only once this project is linked: the Reticle workspace's id, the `orgId` in `.reticle/cloud.json` | Not the workspace's name and not your email. It is the one key the Reticle platform can join this usage to the workspace you created there; see [The platform side](#the-platform-side) |
109
+ | `licenseId` | Only on a licensed (enterprise) build: the id of the licence key it runs with, sent as the event's `organization` group | Not sent by an open-source install. Our licence records connect each licence id to the company that holds the key, so these events are not anonymous to us |
106
110
  | `client` | Which coding agent the event came from, from a closed list (`claude_code`, `cursor`, `codex`, and so on, or `other`) | Not the name your agent sent: a name we do not list arrives as `other` |
107
111
  | `launch` | How `reticle` was started: `npx`, `global`, `project` or `other` | Not the path it was started from; only the bucket leaves |
108
112
  | `projectIdSource` | Whether `projectId` came from a shared git origin or from the local directory path | Not the origin or the path. It exists so we can tell when "how many people share this project" is a real measurement. Outside a pushed repo there is nothing shared to hash, so those rows always show one user |
@@ -110,7 +114,9 @@ Every event carries the same few fields:
110
114
  | `os` | The platform (`darwin` / `linux` / `win32`) | Not the OS version, hostname, or hardware |
111
115
  | `ci` | Whether the run is inside CI | n/a |
112
116
 
113
- There are no IP-based profiles, no cookies, no fingerprinting, and no person profiles: events are processed in "personless" mode, so they are never joined into an identity.
117
+ There are no IP-based profiles, no cookies, no fingerprinting, and no person profiles: events are processed in "personless" mode, so they are never joined into a person profile.
118
+
119
+ Your IP address is not stored, and no location is found from it. Each event tells PostHog not to look up a location (`$geoip_disable: true`). Each event also sets `$ip` to `0.0.0.0`, so PostHog does not store the address the request came from. Events from older versions do not do this.
114
120
 
115
121
  ## The platform side
116
122
 
@@ -120,11 +126,11 @@ The sign-in page Reticle opens also says where the sign-in started (`from=termin
120
126
 
121
127
  ## What is never sent
122
128
 
123
- Your code. Your app's DOM, network requests or responses, console logs, application state, or screenshots. File paths, project names, or git remote URLs. Your name, email or employer. Any account identifier, with one exception you opt into by linking a project: the workspace id, above. Environment variables. Anything typed into the app under test. The names of your flows, baselines, or tests. Error messages, and any stack frame belonging to your application.
129
+ Your code. Your app's DOM, network requests or responses, console logs, application state, or screenshots. File paths, project names, or git remote URLs. Your name or email, except a reply address that you give with [feedback](#feedback). Any account identifier, with two exceptions: the workspace id of a project you linked, and the licence id of a licensed build, both above. Environment variables. Anything typed into the app under test. The names of your flows, baselines, or tests. Error messages, and any stack frame belonging to your application.
124
130
 
125
131
  A few of these are worth being explicit about, because they are the ones a product team is most tempted by:
126
132
 
127
- - **We do not send your project's name or its GitHub URL.** `projectId` is a one-way hash. It lets us count distinct projects and see that a project came back next week; it cannot be turned back into a repository, and we cannot look you up from it.
133
+ - **We do not send your project's name or its GitHub URL.** `projectId` is a hash of the URL. It lets us count distinct projects and see that a project came back next week. We do not use it to find the repository. The hash is not a secret: for a public repository, anyone who has the URL can compute the same id.
128
134
  - **We do not try to work out who you work for.** No domain sniffing, no email inference, no matching a repo against a company. If you _want_ us to know, [email us](#telling-us-who-you-are).
129
135
  - **We do not record what you asked your agent to verify.** The `verification_completed` event knows that a verification happened and how it turned out. The prompt behind it is not something Reticle can see, and we do not reconstruct it.
130
136
 
@@ -136,14 +142,16 @@ Reticle verifies apps for AI agents, and for a long time it had no way to hear w
136
142
 
137
143
  | Who | How | What it carries |
138
144
  | --- | --- | --- |
139
- | Your agent | Calls the `reticle_session { action: "feedback" }` MCP tool (after a failure, **or to ask for a feature**) | What it wrote: an analysis, or a request with the goal behind it, what would improve, and how it works around the gap today. Plus the model it is running, which it tells us because MCP cannot |
140
- | You | Run `npx @reticlehq/server feedback [--rating 1-5] [--bug] "your words"` | Your words and your rating |
145
+ | Your agent | Calls the `reticle_session { action: "feedback" }` MCP tool (after a failure, **or to ask for a feature**) | What it wrote: an analysis, or a request with the goal behind it, what would improve, and how it works around the gap today. Plus the model it is running, which it tells us because MCP cannot. If it gives a call trail, only the Reticle tool names and verdict words in it are kept. Everything else in the trail is removed before it is sent |
146
+ | You | Run `npx @reticlehq/server feedback [--rating 1-5] [--bug] [--contact you@example.com] "your words"` | Your words and your rating, and your reply address if you give one |
141
147
  | Your agent, before Reticle works | Runs `npx @reticlehq/server feedback --agent --kind <bug\|gap\|ambiguity\|feature_request\|improvement\|experience> "what happened"` | The same words, filed from the shell. This exists for the phase where there is no daemon and no MCP tools (a failed `init`, half-finished wiring), which is the failure we are otherwise never told about |
142
148
 
143
149
  Agents are instructed, in the tool description itself, never to include app source, secrets, or user data, and to describe the failure in their own words instead.
144
150
 
145
151
  **It is redacted before it leaves your machine.** Emails, credentials in URLs, `Authorization` headers and API-key assignments, recognizable vendor tokens (`sk-…`, `ghp_…`, `AKIA…`, `xoxb-…`, JWTs), and home-directory paths are stripped and replaced with `[redacted]`. This runs client-side, in [`feedback.ts`](https://github.com/reticlehq/reticle/blob/main/server/src/telemetry/feedback.ts), so you can read exactly what it removes rather than take our word for it. It is a safety net, not a guarantee: it cannot catch a secret that looks like an ordinary sentence, which is why the instruction above comes first.
146
152
 
153
+ **A reply address is sent only when you give it.** `--contact you@example.com` on `reticle feedback` adds one email address so that the maintainers can answer you. Your agent can pass one on with `contact`, but only an address that you typed for this purpose. It must not guess one or reuse one from somewhere else. The address goes as `feedback_contact` on that one report. It is not redacted, because you gave it to be read. Without `--contact` or `contact`, no address is sent.
154
+
147
155
  **It shows you what it sends.** `reticle feedback` prints the exact payload before transmitting, and names any redaction rule that fired.
148
156
 
149
157
  Alongside the report, a feedback event carries context about the environment it came from, never about you:
@@ -154,13 +162,13 @@ Alongside the report, a feedback event carries context about the environment it
154
162
  | `runtime` | `web`, `electron`, `tauri` | Desktop bugs look nothing like browser bugs |
155
163
  | `engine` | `blink`, `gecko`, `webkit` | Coarse bucket, never the user-agent string |
156
164
  | `driver` | `cdp`, `sdk` | Whether Reticle drove the page or observed your own browser |
157
- | `client` | `claude-code`, `cursor` | The MCP client's own name from its handshake |
165
+ | `client` / `clientVersion` | `claude-code` / `1.0.0` | The MCP client's own name and version from its handshake |
158
166
  | `mcpScope` | `user`, `project` | How Reticle is registered |
159
167
  | `kind` | `bug`, `gap`, `ambiguity`, `feature_request`, `improvement`, `experience` | A defect, a blind spot, an undecidable verdict, a thing that does not exist, a thing that exists but is awkward, or a human's overall take |
160
168
 
161
169
  Plus the `version`, `os`, and `ci` fields every event carries. No paths, no project name, no dependency list, no user-agent string.
162
170
 
163
- **It has its own off switch.** To keep the anonymous counters but never send free text:
171
+ **It has its own off switch.** To keep the usage counters but never send free text:
164
172
 
165
173
  ```bash
166
174
  RETICLE_FEEDBACK=0
@@ -170,9 +178,9 @@ Every switch in [Your choices](#your-choices) disables feedback too. If telemetr
170
178
 
171
179
  ## Telling us who you are
172
180
 
173
- Everything above is anonymous, and stays that way. There is no command that sends who you are: `reticle identify`, which used to, has been removed, and this version never sends an `identified` event. If you want support, an enterprise licence, or to be a design partner, email support@reticlehq.com.
181
+ Nothing above names you, and no command adds your name: `reticle identify`, which used to, has been removed, and this version never sends an `identified` event. If you want support, an enterprise licence, or to be a design partner, email hey@reticle.sh.
174
182
 
175
- An identity sent by an earlier version with `reticle identify` stays linked to that machine's anonymous id until you ask for it to be removed: email support@reticlehq.com.
183
+ An identity sent by an earlier version with `reticle identify` stays linked to that machine's anonymous id until you ask for it to be removed: email hey@reticle.sh.
176
184
 
177
185
  ## Where it goes
178
186
 
package/docs/testing.md CHANGED
@@ -55,7 +55,7 @@ Any failed matcher throws with the structured evidence (near-miss, failure reaso
55
55
  ## Deterministic + honest
56
56
 
57
57
  - **`t.clock`** bakes `reticle_clock` into the spec, so time-gated UI (a 5s auto-dismiss, a 500ms hover dwell) is tested deterministically instead of racing real timers.
58
- - **`t.expectInputModeReal()`**: a hover/drag spec asserts native input is active; if it's running synthetic (no CDP), the spec is **skipped with a reason**, never silently passing on a no-op. Enable real input headless with `reticle drive` (see [usage §18](usage.md#18-real-input-mode-native-hover--drag)).
58
+ - **`t.expectInputModeReal()`**: a hover/drag spec asserts native input is active; if it's running synthetic (no CDP), the spec is **skipped with a reason**, never silently passing on a no-op. Enable real input headless with `reticle drive` (see [usage §18](usage.md#18-real-input-mode-native-hover-&-drag)).
59
59
 
60
60
  ## Run a suite (headless, the same path CI uses)
61
61
 
@@ -70,7 +70,7 @@ const booted = await bootSession({ driveUrl: 'http://localhost:4310', headless:
70
70
  const { summary } = await runSpecs({
71
71
  invoke: booted.invoke,
72
72
  now: () => Date.now(),
73
- buildContext: (invoke) => createTestContext(invoke, { sessionId: 'my-app' }),
73
+ buildContext: (invoke) => createTestContext(invoke), // one tab: omit sessionId
74
74
  print: (line) => process.stdout.write(line + '\n'),
75
75
  });
76
76
  await booted.close();
@@ -88,10 +88,14 @@ import { toJUnitXml, writeJUnit } from '@reticlehq/test';
88
88
  `.reticle/` flows (see [Flows](flows.md)) can be executed directly as specs, replayed with their `expect`/`success` predicates and skipping `dynamic` (LLM-output) regions, so the recorded map and the suite can't drift apart:
89
89
 
90
90
  ```ts
91
- import { flowsAsSpecs } from '@reticlehq/test';
92
- // register one reticleTest per flow under .reticle/flows/
91
+ import { registerFlowSpecs } from '@reticlehq/test';
92
+ // In a vitest file: one test per flow under .reticle/flows/. Await it at the top level.
93
+ // `session` is the FlowReplaySession (from @reticlehq/server) for the app under test.
94
+ await registerFlowSpecs('.reticle/flows', () => session);
93
95
  ```
94
96
 
97
+ Without vitest, `flowsAsSpecs('.reticle/flows')` returns the specs. Call `await spec.run(session)` on each one, and read `outcome` (`pass` or `fail`).
98
+
95
99
  ## Authoring tip: record → prune → commit
96
100
 
97
101
  You don't have to hand-write steps. Drive the flow once (or record it via the panel), let Reticle emit the program, trim it, and commit it as a spec. The regression test is a byproduct of testing, not separate work.
@@ -126,4 +130,4 @@ Yes, `toJUnitXml` and `writeJUnit` are both exported from `@reticlehq/test`. Pas
126
130
 
127
131
  ### Do I have to write specs by hand if I already have flows?
128
132
 
129
- No. `flowsAsSpecs` registers one `reticleTest` per flow under `.reticle/flows/`, replayed with that flow's own `expect` and `success` predicates and skipping its `dynamic` regions. That keeps the recorded map and the CI suite from drifting apart, because they are the same artifact.
133
+ No. `registerFlowSpecs` registers one vitest test for each flow under `.reticle/flows/`. Each test replays the flow's own `expect` and `success` predicates and skips its `dynamic` regions. These are vitest tests, not `reticleTest` specs, so `runSpecs` does not run them. That keeps the recorded map and the CI suite from drifting apart, because they are the same artifact.
@@ -90,29 +90,32 @@ Real response. The `initiatorStack`, `storageDiffs` and per-event trace detail a
90
90
  "coverage": { "pct": 100, "partial": false },
91
91
  "integrity": { "clean": true, "issues": [] }
92
92
  },
93
+ "engineBlindSpots": [],
93
94
  "since": 44395
94
95
  }
95
96
  ```
96
97
 
97
98
  `trace` is a digest of what the app did in the window. When the counts are not enough, pass the top-level `since` cursor to [`reticle_observe`](/tools/observe) for the per-event timeline.
98
99
 
100
+ `engineBlindSpots` lists what Reticle could not see in this session: `pixels-unobserved` once an action ran with a `<canvas>` on screen, and `claim-arithmetic-unchecked` while a claim states an amount and no `compare` ran. Do not call a run clean while it holds an entry.
101
+
99
102
  ## The predicate grammar
100
103
 
101
104
  `until` takes `{ kind, ...fields }`. The kinds:
102
105
 
103
- | Kind | Asserts |
104
- | ------------------------- | -------------------------------------------------------------- |
105
- | `signal` | The app fired a named signal, the strongest evidence available |
106
- | `state` | A store path reached a value |
107
- | `net` | A request matched; `count` asserts an **exact** number |
108
- | `route` | The route **changed** |
109
- | `element` | An element appeared; `absent: true` asserts it went away |
110
- | `text` | Text appeared or, with `absent`, disappeared |
111
- | `console` | Something logged; `absent: true` asserts a clean console |
112
- | `animation` | An animation ran or finished |
113
- | `settled` | The page went quiet |
114
- | `compare` | Two readings agree |
115
- | `allOf` / `anyOf` / `not` | Combine the above |
106
+ | Kind | Asserts |
107
+ | --- | --- |
108
+ | `signal` | The app fired a signal, the strongest evidence available. With no `name`, only the app's own signals count, not Reticle's `reticle:` signals. `count` is exact |
109
+ | `state` | A store path reached a value |
110
+ | `net` | A request matched. `count` is **exact**, `count: 0` means it never fired, and no `count` means at least one |
111
+ | `route` | The route **changed** |
112
+ | `element` | An element appeared; `absent: true` asserts it went away |
113
+ | `text` | Text appeared or, with `absent`, disappeared |
114
+ | `console` | Something logged; `absent: true` asserts a clean console |
115
+ | `animation` | An animation ran or finished |
116
+ | `settled` | The page went quiet |
117
+ | `compare` | Two readings agree |
118
+ | `allOf` / `anyOf` / `not` | Combine the above |
116
119
 
117
120
  ### Predicates that catch real bugs
118
121
 
@@ -169,6 +172,7 @@ A verification tool that cannot describe the quality of its own evidence is aski
169
172
  | `timeout_ms` | Maximum wait, defaulting to 4000. `0` evaluates the predicate once, no wait |
170
173
  | `refuseWhenThrottled` | Throw rather than act on a throttled tab |
171
174
  | `intent` | What the change is supposed to make true, in prose, or the id of an intent declared earlier. Recorded on the verdict |
175
+ | `detail` | `"compact"` keeps only the deciding fields. Omit it for the full result |
172
176
  | `durable` | After a `yes`, reload the page and require the element, text and state parts of `until` to hold again. A consequence that is gone after the reload turns the verdict into `no`: the UI advanced, the change did not persist |
173
177
 
174
178
  Omitting `until` waits for network and DOM idle, which is the deterministic replacement for a fixed sleep. To assert a consequence **and** settle, combine them:
@@ -45,72 +45,50 @@ Steps may also name a `target` instead of a `ref`, the same locator [`reticle_ac
45
45
  }
46
46
  ```
47
47
 
48
- Real response. Each `effects[]` entry also carries `targetMatched`, `visible`, `enabled`, `defaultPrevented`, `occluded`, `occludedBy` and `scrolledIntoView`, and each `steps[]` entry also carries `dispatched`, `settled`, `settleReason`, `component`, `role` and `name`. Those are trimmed here for length:
48
+ Real response shape, trimmed. Each `steps[]` entry also carries `settled`, `settleReason`, `component`, `role` and `name`, and `window`, `digest` and `contradictions` for that step:
49
49
 
50
50
  ```json
51
51
  {
52
52
  "since": 3915,
53
53
  "dispatched": true,
54
- "result": {
55
- "ok": true,
56
- "count": 3,
57
- "effects": [
58
- {
59
- "dispatched": true,
60
- "focusMoved": "null->e603",
61
- "valueChanged": false,
62
- "domMutatedWithin": 0
63
- },
64
- {
65
- "dispatched": true,
66
- "focusMoved": "e603->e604",
67
- "valueChanged": true,
68
- "domMutatedWithin": 9
69
- },
70
- {
71
- "dispatched": true,
72
- "focusMoved": "e604->null",
73
- "domMutatedWithin": 15,
74
- "appeared": "Signing in… | Invalid email or password | Sign in"
75
- }
76
- ],
77
- "steps": [
78
- {
79
- "ref": "e603",
80
- "action": "fill",
81
- "testid": "login-email",
82
- "source": { "file": "src/components/Login.tsx", "line": 48 }
83
- },
84
- {
85
- "ref": "e604",
86
- "action": "fill",
87
- "testid": "login-password",
88
- "source": { "file": "src/components/Login.tsx", "line": 59 }
89
- },
90
- {
91
- "ref": "e605",
92
- "action": "click",
93
- "testid": "login-submit",
94
- "source": { "file": "src/components/Login.tsx", "line": 81 }
95
- }
96
- ]
97
- }
54
+ "completed": 3,
55
+ "recorded": 3,
56
+ "verified": "unknown",
57
+ "because": "all 3 step(s) declared nothing, so the app was driven but not verified: give each step an `expect` naming the consequence it causes",
58
+ "coverage": { "declared": 0, "total": 3 },
59
+ "steps": [
60
+ {
61
+ "ref": "e603",
62
+ "action": "fill",
63
+ "dispatched": true,
64
+ "testid": "login-email",
65
+ "source": { "file": "src/components/Login.tsx", "line": 48 }
66
+ },
67
+ {
68
+ "ref": "e604",
69
+ "action": "fill",
70
+ "dispatched": true,
71
+ "testid": "login-password",
72
+ "source": { "file": "src/components/Login.tsx", "line": 59 }
73
+ },
74
+ {
75
+ "ref": "e605",
76
+ "action": "click",
77
+ "dispatched": true,
78
+ "testid": "login-submit",
79
+ "source": { "file": "src/components/Login.tsx", "line": 81 }
80
+ }
81
+ ]
98
82
  }
99
83
  ```
100
84
 
101
85
  ## Read that response again
102
86
 
103
- `ok: true`. `count: 3`. Every step dispatched. And the login **failed**, look at `appeared`:
104
-
105
- ```
106
- "appeared": "Signing in… | Invalid email or password | Sign in"
107
- ```
108
-
109
- That password was wrong. The app rejected it. And this tool still reported `ok: true`, correctly, because `ok` means _the three actions were dispatched_, which they were.
87
+ Every step dispatched. In this run the password was wrong, and the login **failed**. No step declared an `expect`, so the sequence cannot know that. It does not claim a pass: it answers `verified: "unknown"`, and `because` says that the app was driven but not verified.
110
88
 
111
89
  <Warning>
112
- `ok: true` is a statement about the input, not about the outcome. A step with no `expect` proves
113
- nothing. Put an `expect` on the step that ends the journey, or finish with
90
+ `dispatched: true` is a statement about the input, not about the outcome. A step with no `expect`
91
+ proves nothing. Put an `expect` on the step that ends the journey, or finish with
114
92
  [`reticle_act_and_wait`](/tools/act-and-wait) or [`reticle_assert`](/tools/assert).
115
93
  </Warning>
116
94
 
@@ -136,24 +114,20 @@ which returns the verdict. Putting `"expect": { "kind": "signal", "name": "auth:
136
114
  ```json
137
115
  {
138
116
  "verified": "yes",
139
- "verifiedReason": "proved",
140
- "because": "assertion held at signal grade over a clean capture with no channel disagreeing",
141
- "verdict": {
142
- "pass": true,
143
- "evidence": { "name": "auth:granted", "data": { "email": "admin@reticle.dev" } }
144
- },
145
- "honesty": { "grade": "signal", "coverage": { "pct": 100, "partial": false } }
117
+ "because": "every declared consequence held, but only 1 of 3 steps declared one; the rest were driven, not verified",
118
+ "coverage": { "declared": 1, "total": 3 }
146
119
  }
147
120
  ```
148
121
 
149
- Two calls instead of four, and the last one returns a verdict. Note `grade: "signal"`, the strongest Reticle awards, because the app said so itself.
122
+ Two calls instead of four, and the last one returns a verdict. A proved plan also carries `keep`, which is the call that saves it as a regression flow.
150
123
 
151
124
  ## What each step reports
152
125
 
153
- Every step comes back with the same `effect` fields as [`reticle_act`](/tools/act): `valueChanged`, `focusMoved`, `domMutatedWithin`, `occluded`. Plus its `testid`, `component` and `source`. When a sequence goes wrong, that per-step detail tells you which step, and which line of which file.
126
+ Each step comes back with its `testid`, `component` and `source`, its `digest` (what the app did, as counts) and any `contradictions`. A step with an `expect` also carries `held`, and `observed` when it missed. When a sequence goes wrong, this tells you which step, and which line of which file.
154
127
 
155
128
  <Note>
156
- Steps run in order and do not stop on a soft failure, because "the field rejected the value" is
157
- not an exception. It's a result you need to see. Read `effects[]` rather than assuming `ok: true`
158
- means all three did what you wanted.
129
+ A step that cannot be performed stops the sequence: `stalled_at` names it, and the step's `error`
130
+ says why. A step whose `expect` does not hold stops it too (`stopped_at`), unless you pass
131
+ `onDeviation: "continue"`. The steps that did not run come back unchanged in `tail`. Read
132
+ `verified` and `because`, not `dispatched`.
159
133
  </Note>
@@ -61,6 +61,8 @@ Real response:
61
61
 
62
62
  `click` · `dblclick` · `hover` · `focus` · `blur` · `fill` · `type` · `clear` · `select` · `check` · `uncheck` · `submit` · `press` · `upload` · `scrollIntoView` · `scroll` · `drag` · `tap` · `zoom`
63
63
 
64
+ Name the element with `ref`, or with `target` to find it in the same call: `{ testid }`, `{ text }`, `{ role, name }` or `{ label }`. A `target` that matches more than one element is refused. A press of Escape, Tab or a shortcut such as Cmd+K needs neither.
65
+
64
66
  | Action | Arguments |
65
67
  | ---------------- | --------------------------------------------------------------- |
66
68
  | `fill`, `select` | `{ value }` |
@@ -72,11 +74,11 @@ Real response:
72
74
 
73
75
  This is where `act` earns its place even though it proves nothing.
74
76
 
75
- - **`valueChanged: true`**. The field actually took the value. `false` on a `fill` usually means the input is controlled and rejected it, or it's readonly.
76
- - **`focusMoved: "null->e104"`**, focus went where you expected.
77
- - **`domMutatedWithin: 9`**, something re-rendered within 9ms. A `fill` that mutates nothing often means no handler is attached.
78
- - **`defaultPrevented`**. A handler called `preventDefault()`, which is why your form didn't submit.
79
- - **`occluded` / `occludedBy`**. The click landed on something else. This is the answer to at least half of all "the button doesn't work" reports.
77
+ - **`valueChanged: true`**: the field took the value. `false` on a `fill` usually means that the input is controlled and rejected it, or that it is readonly.
78
+ - **`focusMoved: "null->e104"`**: focus went where you expected.
79
+ - **`domMutatedWithin: 9`**: something re-rendered within 9 ms. A `fill` that changes nothing often means that no handler is attached.
80
+ - **`defaultPrevented`**: a handler called `preventDefault()`, which is why your form did not submit.
81
+ - **`occluded` / `occludedBy`**: the click landed on something else. This is the answer to at least half of all "the button does not work" reports.
80
82
 
81
83
  ## `settled: false` is not a failure
82
84
 
@@ -10,7 +10,7 @@ Verdicts come from [`reticle_act_and_wait`](/tools/act-and-wait), `reticle_asser
10
10
 
11
11
  ## A real failure
12
12
 
13
- Failures are more instructive than passes, so here is one. I asserted that signing in lands on `/dashboard`, after a click that did not navigate. The page was still on `/login`:
13
+ Failures teach more than passes. This assert claims that signing in lands on `/dashboard`, after a click that did not navigate. The page was still on `/login`:
14
14
 
15
15
  ```json
16
16
  { "predicate": { "kind": "route", "pathname": "/dashboard" } }
@@ -12,7 +12,7 @@ icon: magnifying-glass
12
12
 
13
13
  | `action` | What it returns | Unmerged name | Details |
14
14
  | --- | --- | --- | --- |
15
- | `page` (default) | The semantic snapshot of what is rendered, with a `ref` for each control. `mode: "interactive"` keeps only what you can act on; `diff: true` returns only what changed since the last look | `reticle_snapshot` | [Snapshot](/tools/snapshot) |
15
+ | `page` (default) | The semantic snapshot of what is rendered, with a `ref` for each control. `mode: "interactive"` keeps only what you can act on; `diff: true` returns only what changed since the last look; `ids: true` adds each control's test id; `audit: true` also reports clipped text, overlapping controls and low contrast | `reticle_snapshot` | [Snapshot](/tools/snapshot) |
16
16
  | `find` | A locator (testid, role, text, label, component) resolved to refs you can act on. `limit` caps the list; `count_only` returns just the count | `reticle_query` | [Query](/tools/query) |
17
17
  | `element` | Everything about one `ref`: geometry, scroll, design tokens, and on React the source `file:line` | `reticle_inspect` | [Inspect](/tools/inspect) |
18
18
  | `state` | A registered store's value. Scope it with `store`, `path` and `depth` | `reticle_state` | [State](/tools/state) |
@@ -44,7 +44,7 @@ Most tools would return `ok: true` and let you assume arrival. When the page the
44
44
 
45
45
  `confirmed: false` with a `landedOn` URL is a different case: the page arrived, but the app sent it somewhere else, such as a login or route-guard redirect. `landedOn` says where the browser actually is.
46
46
 
47
- `waitedMs` says which of two things you are looking at. Reticle stopped waiting at the budget it had, or the page never came back. The first is fixed by asking for more: a single-page app reattaching under HMR has been measured at 30–60s, which no default should wait for on every call, but which the caller who knows the app can grant.
47
+ `waitedMs` is how long Reticle waited for the page to come back before it stopped. The page may still arrive after that. A single-page app that reattaches under HMR can take 30–60 s, so give a slow app a larger `timeout_ms`:
48
48
 
49
49
  ```json
50
50
  { "url": "/deployments", "timeout_ms": 45000 }