@reticlehq/server 3.9.0 → 3.10.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 (351) hide show
  1. package/SKILL.md +13 -12
  2. package/dist/command/cli/adhoc-suite.js +2 -0
  3. package/dist/command/cli/adhoc-verdict.d.ts +2 -43
  4. package/dist/command/cli/adhoc-verdict.js +50 -187
  5. package/dist/command/cli/answers/mcp-connection-hint.d.ts +2 -0
  6. package/dist/command/cli/answers/mcp-connection-hint.js +6 -0
  7. package/dist/command/cli/cli-doctor.js +1 -1
  8. package/dist/command/cli/cli-flow-commands.js +2 -9
  9. package/dist/command/cli/cli-kill.js +1 -1
  10. package/dist/command/cli/cli-parse-compare.d.ts +22 -0
  11. package/dist/command/cli/cli-parse-compare.js +75 -0
  12. package/dist/command/cli/cli-parse-grade.d.ts +36 -0
  13. package/dist/command/cli/cli-parse-grade.js +75 -0
  14. package/dist/command/cli/cli-parse-grammar.d.ts +2 -0
  15. package/dist/command/cli/cli-parse-grammar.js +18 -20
  16. package/dist/command/cli/cli-parse-verify.d.ts +10 -0
  17. package/dist/command/cli/cli-parse-verify.js +63 -14
  18. package/dist/command/cli/cli-parse.d.ts +17 -15
  19. package/dist/command/cli/cli-parse.js +44 -27
  20. package/dist/command/cli/cli-update-commands.d.ts +1 -1
  21. package/dist/command/cli/cli-update-commands.js +29 -20
  22. package/dist/command/cli/cli-usage.js +58 -29
  23. package/dist/command/cli/cli-verify.d.ts +11 -2
  24. package/dist/command/cli/cli-verify.js +27 -8
  25. package/dist/command/cli/cloud-cli.js +2 -119
  26. package/dist/command/cli/daemon-link/daemon-client.d.ts +45 -0
  27. package/dist/command/cli/daemon-link/daemon-client.js +205 -0
  28. package/dist/command/cli/doctor/doctor-daemon-line.js +6 -3
  29. package/dist/command/cli/doctor-repairs.js +1 -1
  30. package/dist/command/cli/ensure-daemon.d.ts +26 -0
  31. package/dist/command/cli/ensure-daemon.js +57 -0
  32. package/dist/command/cli/finished-run/grade-command.d.ts +23 -0
  33. package/dist/command/cli/finished-run/grade-command.js +180 -0
  34. package/dist/command/cli/lifecycle/daemon-lifecycle.d.ts +1 -1
  35. package/dist/command/cli/lifecycle/daemon-lifecycle.js +1 -1
  36. package/dist/command/cli/ports/port-holder.d.ts +9 -0
  37. package/dist/command/cli/ports/port-holder.js +27 -3
  38. package/dist/command/cli/status/status-lines.d.ts +1 -0
  39. package/dist/command/cli/status/status-lines.js +22 -0
  40. package/dist/command/cli/status-fields.d.ts +5 -1
  41. package/dist/command/cli/status-fields.js +7 -3
  42. package/dist/command/cli/two-builds/compare-command.d.ts +48 -0
  43. package/dist/command/cli/two-builds/compare-command.js +314 -0
  44. package/dist/command/cli/verify-against.d.ts +61 -0
  45. package/dist/command/cli/verify-against.js +187 -0
  46. package/dist/command/cli/verify-no-session.d.ts +2 -0
  47. package/dist/command/cli/verify-no-session.js +13 -0
  48. package/dist/command/cli/verify-results.d.ts +35 -2
  49. package/dist/command/cli/verify-results.js +76 -10
  50. package/dist/command/cli.js +46 -41
  51. package/dist/command/daemon/daemon-resolve.d.ts +8 -1
  52. package/dist/command/daemon/daemon-resolve.js +27 -1
  53. package/dist/command/daemon/daemon.d.ts +12 -1
  54. package/dist/command/daemon/daemon.js +35 -4
  55. package/dist/command/daemon/lifetime/death-cause.js +21 -2
  56. package/dist/command/daemon/lifetime/heartbeat.js +7 -2
  57. package/dist/command/daemon/lifetime/startup-failure.d.ts +29 -0
  58. package/dist/command/daemon/lifetime/startup-failure.js +62 -0
  59. package/dist/command/setup/bringup/dev-server-files.d.ts +9 -0
  60. package/dist/command/setup/bringup/dev-server-files.js +17 -0
  61. package/dist/command/setup/bringup/static-page-server.d.ts +14 -2
  62. package/dist/command/setup/bringup/static-page-server.js +43 -9
  63. package/dist/command/setup/init/init-runtime.js +43 -2
  64. package/dist/command/setup/node-effects.d.ts +3 -2
  65. package/dist/command/setup/node-effects.js +9 -3
  66. package/dist/command/setup/remaining-steps.d.ts +8 -0
  67. package/dist/command/setup/remaining-steps.js +10 -0
  68. package/dist/command/setup/run-setup.d.ts +2 -0
  69. package/dist/command/setup/run-setup.js +9 -1
  70. package/dist/command/setup/setup-command.js +6 -3
  71. package/dist/command/update/updater.js +6 -0
  72. package/dist/command/version/version-skew.js +1 -1
  73. package/dist/features/compare/action-plan.d.ts +260 -0
  74. package/dist/features/compare/action-plan.js +543 -0
  75. package/dist/features/compare/behaviour-compare.d.ts +24 -0
  76. package/dist/features/compare/behaviour-compare.js +85 -0
  77. package/dist/features/compare/behaviour-diff.d.ts +97 -0
  78. package/dist/features/compare/behaviour-diff.js +202 -0
  79. package/dist/features/compare/behaviour-report.d.ts +40 -0
  80. package/dist/features/compare/behaviour-report.js +128 -0
  81. package/dist/features/compare/build-readings.d.ts +106 -0
  82. package/dist/features/compare/build-readings.js +250 -0
  83. package/dist/features/compare/compare-diff.d.ts +62 -0
  84. package/dist/features/compare/compare-diff.js +177 -0
  85. package/dist/features/compare/compare-report-html.d.ts +6 -0
  86. package/dist/features/compare/compare-report-html.js +102 -0
  87. package/dist/features/compare/compare-report.d.ts +15 -0
  88. package/dist/features/compare/compare-report.js +73 -0
  89. package/dist/features/compare/compare-run.d.ts +60 -0
  90. package/dist/features/compare/compare-run.js +123 -0
  91. package/dist/features/compare/outside/effect-lines.d.ts +26 -0
  92. package/dist/features/compare/outside/effect-lines.js +103 -0
  93. package/dist/features/compare/outside/state-sources.d.ts +76 -0
  94. package/dist/features/compare/outside/state-sources.js +164 -0
  95. package/dist/features/compare/outside/value-dictionary.d.ts +51 -0
  96. package/dist/features/compare/outside/value-dictionary.js +227 -0
  97. package/dist/features/compare/visible-content.d.ts +20 -0
  98. package/dist/features/compare/visible-content.js +125 -0
  99. package/dist/features/crawl/crawl-empty.js +2 -1
  100. package/dist/features/crawl/crawl-exhaustive.d.ts +55 -0
  101. package/dist/features/crawl/crawl-exhaustive.js +138 -0
  102. package/dist/features/crawl/crawl-tools.d.ts +9 -2
  103. package/dist/features/crawl/crawl-tools.js +28 -12
  104. package/dist/features/crawl/crawl.d.ts +8 -9
  105. package/dist/features/crawl/crawl.js +61 -65
  106. package/dist/features/exhaust/app-coverage.d.ts +5 -1
  107. package/dist/features/exhaust/app-coverage.js +3 -1
  108. package/dist/features/exhaust/code-coverage.js +8 -4
  109. package/dist/features/exhaust/edge-cases.d.ts +133 -0
  110. package/dist/features/exhaust/edge-cases.js +310 -0
  111. package/dist/features/exhaust/edges/edge-drive.d.ts +42 -0
  112. package/dist/features/exhaust/edges/edge-drive.js +364 -0
  113. package/dist/features/exhaust/edges/edge-run.d.ts +59 -0
  114. package/dist/features/exhaust/edges/edge-run.js +169 -0
  115. package/dist/features/exhaust/explorer.d.ts +87 -14
  116. package/dist/features/exhaust/explorer.js +159 -25
  117. package/dist/features/exhaust/failure-branches.d.ts +5 -0
  118. package/dist/features/exhaust/failure-branches.js +29 -11
  119. package/dist/features/exhaust/invariants.d.ts +112 -0
  120. package/dist/features/exhaust/invariants.js +188 -0
  121. package/dist/features/exhaust/ledger.d.ts +68 -0
  122. package/dist/features/exhaust/ledger.js +121 -13
  123. package/dist/features/exhaust/session-fold.d.ts +10 -0
  124. package/dist/features/exhaust/session-fold.js +46 -3
  125. package/dist/features/exhaust/session-port.d.ts +3 -1
  126. package/dist/features/exhaust/session-port.js +97 -28
  127. package/dist/features/grade/grade-answer.d.ts +99 -0
  128. package/dist/features/grade/grade-answer.js +112 -0
  129. package/dist/features/grade/grade-noise.d.ts +27 -0
  130. package/dist/features/grade/grade-noise.js +55 -0
  131. package/dist/features/grade/grade-renumber.d.ts +12 -0
  132. package/dist/features/grade/grade-renumber.js +86 -0
  133. package/dist/features/grade/grade-state.d.ts +50 -0
  134. package/dist/features/grade/grade-state.js +93 -0
  135. package/dist/features/grade/grade-suggest.d.ts +33 -0
  136. package/dist/features/grade/grade-suggest.js +98 -0
  137. package/dist/features/visual/visual-tools.js +66 -19
  138. package/dist/index.js +7 -4
  139. package/dist/judgement/capsule/causal-summary.js +9 -2
  140. package/dist/judgement/runs/artifact/replay-mapping.d.ts +13 -1
  141. package/dist/judgement/runs/artifact/replay-mapping.js +1 -1
  142. package/dist/judgement/runs/reticle-runner.js +16 -2
  143. package/dist/judgement/runs/runner-port.d.ts +2 -0
  144. package/dist/judgement/runs/runner-port.js +23 -5
  145. package/dist/language/flows/affected-tools.d.ts +1 -1
  146. package/dist/language/flows/affected-tools.js +49 -30
  147. package/dist/language/flows/change/git-changed.d.ts +2 -1
  148. package/dist/language/flows/change/git-changed.js +19 -2
  149. package/dist/language/flows/fields/flow-secret-field.d.ts +1 -13
  150. package/dist/language/flows/fields/flow-secret-field.js +77 -21
  151. package/dist/language/flows/flow-anchor.d.ts +60 -2
  152. package/dist/language/flows/flow-anchor.js +125 -20
  153. package/dist/language/flows/flow-classify.d.ts +20 -0
  154. package/dist/language/flows/flow-classify.js +90 -12
  155. package/dist/language/flows/flow-cross-step.d.ts +7 -1
  156. package/dist/language/flows/flow-cross-step.js +25 -1
  157. package/dist/language/flows/flow-journey.d.ts +38 -4
  158. package/dist/language/flows/flow-journey.js +138 -8
  159. package/dist/language/flows/flow-replay-run.d.ts +0 -16
  160. package/dist/language/flows/flow-replay-run.js +76 -14
  161. package/dist/language/flows/flow-replay-types.d.ts +4 -2
  162. package/dist/language/flows/flow-replay.d.ts +19 -1
  163. package/dist/language/flows/flow-replay.js +169 -52
  164. package/dist/language/flows/flow-replayable.js +7 -1
  165. package/dist/language/flows/flow-start-path.d.ts +34 -5
  166. package/dist/language/flows/flow-start-path.js +202 -15
  167. package/dist/language/flows/flow-step-runners.d.ts +14 -13
  168. package/dist/language/flows/flow-step-runners.js +184 -109
  169. package/dist/language/flows/flow-store-for-session.d.ts +26 -25
  170. package/dist/language/flows/flow-store-for-session.js +44 -0
  171. package/dist/language/flows/flow-tools.js +46 -33
  172. package/dist/language/flows/flows.js +176 -76
  173. package/dist/language/flows/recording/tape/recordings.d.ts +19 -3
  174. package/dist/language/flows/recording/tape/recordings.js +71 -5
  175. package/dist/language/flows/replay.d.ts +19 -5
  176. package/dist/language/flows/replay.js +99 -7
  177. package/dist/language/flows/steps/page-steps.d.ts +68 -0
  178. package/dist/language/flows/steps/page-steps.js +198 -0
  179. package/dist/language/flows/suite/sequential-run.d.ts +68 -0
  180. package/dist/language/flows/suite/sequential-run.js +96 -0
  181. package/dist/language/flows/verify-change-tools.js +26 -15
  182. package/dist/memory/cloud/background-sync.d.ts +13 -0
  183. package/dist/memory/cloud/background-sync.js +24 -3
  184. package/dist/memory/cloud/sync-daemon.js +26 -27
  185. package/dist/memory/journal/attach-journal.js +2 -1
  186. package/dist/memory/journal/drive-flow.d.ts +7 -0
  187. package/dist/memory/journal/drive-flow.js +44 -13
  188. package/dist/memory/journal/journal-recorder.d.ts +4 -2
  189. package/dist/memory/journal/session-journal.d.ts +7 -0
  190. package/dist/memory/journal/session-journal.js +58 -10
  191. package/dist/memory/project/root-claims.d.ts +9 -0
  192. package/dist/memory/project/root-claims.js +9 -4
  193. package/dist/portal/input/download-capture.d.ts +7 -0
  194. package/dist/portal/input/download-capture.js +92 -0
  195. package/dist/portal/input/network-detail.d.ts +6 -0
  196. package/dist/portal/input/network-detail.js +12 -1
  197. package/dist/portal/input/network-mock-tools.js +49 -13
  198. package/dist/portal/input/network-mock.d.ts +8 -0
  199. package/dist/portal/input/network-mock.js +28 -9
  200. package/dist/portal/input/page-capture.d.ts +24 -0
  201. package/dist/portal/input/page-capture.js +33 -0
  202. package/dist/portal/input/real-input.d.ts +22 -20
  203. package/dist/portal/input/real-input.js +41 -31
  204. package/dist/portal/input/viewport-tools.d.ts +8 -0
  205. package/dist/portal/input/viewport-tools.js +75 -18
  206. package/dist/portal/pool/browser-pool.d.ts +11 -29
  207. package/dist/portal/pool/browser-pool.js +35 -17
  208. package/dist/portal/pool/context-permissions.d.ts +17 -3
  209. package/dist/portal/pool/context-permissions.js +36 -5
  210. package/dist/portal/pool/determinism.d.ts +52 -0
  211. package/dist/portal/pool/determinism.js +259 -0
  212. package/dist/portal/pool/lease-emulation.d.ts +34 -1
  213. package/dist/portal/pool/lease-emulation.js +18 -0
  214. package/dist/portal/pool/lease-marker-stamp.d.ts +6 -0
  215. package/dist/portal/pool/lease-marker-stamp.js +15 -1
  216. package/dist/portal/pool/lease-page-error.d.ts +44 -17
  217. package/dist/portal/pool/lease-page-error.js +147 -5
  218. package/dist/portal/pool/page-error-journal.d.ts +7 -1
  219. package/dist/portal/pool/page-error-journal.js +27 -0
  220. package/dist/portal/pool/playwright-launcher.js +51 -8
  221. package/dist/portal/pool/pool-contract.d.ts +27 -1
  222. package/dist/portal/pool/pool-lease.d.ts +36 -1
  223. package/dist/portal/pool/zero-install.d.ts +8 -0
  224. package/dist/portal/pool/zero-install.js +23 -1
  225. package/dist/portal/session/dev-server/dev-command.d.ts +7 -6
  226. package/dist/portal/session/dev-server/dev-command.js +61 -15
  227. package/dist/portal/session/dev-server/dev-server-probe.d.ts +7 -0
  228. package/dist/portal/session/dev-server/dev-server-probe.js +9 -0
  229. package/dist/portal/session/dev-server/project-ports.d.ts +21 -0
  230. package/dist/portal/session/dev-server/project-ports.js +62 -0
  231. package/dist/portal/session/facts/departed-network.d.ts +66 -0
  232. package/dist/portal/session/facts/departed-network.js +142 -0
  233. package/dist/portal/session/no-session-diagnosis.d.ts +9 -0
  234. package/dist/portal/session/no-session-diagnosis.js +67 -25
  235. package/dist/portal/session/no-session-next-action.d.ts +2 -0
  236. package/dist/portal/session/no-session-next-action.js +12 -3
  237. package/dist/portal/session/no-session-watch.d.ts +6 -0
  238. package/dist/portal/session/no-session-watch.js +38 -3
  239. package/dist/portal/session/serving-directory.d.ts +10 -0
  240. package/dist/portal/session/serving-directory.js +39 -8
  241. package/dist/portal/session/session-manager.d.ts +10 -0
  242. package/dist/portal/session/session-manager.js +174 -63
  243. package/dist/start-wiring.d.ts +2 -1
  244. package/dist/start-wiring.js +2 -1
  245. package/dist/surface/mcp/mcp.js +3 -2
  246. package/dist/surface/mcp/shared-params.d.ts +2 -0
  247. package/dist/surface/mcp/shared-params.js +31 -0
  248. package/dist/surface/tools/act/act-observation.d.ts +40 -2
  249. package/dist/surface/tools/act/act-observation.js +118 -12
  250. package/dist/surface/tools/act/act-preflight.js +1 -1
  251. package/dist/surface/tools/act/act-sequence-retry.d.ts +23 -2
  252. package/dist/surface/tools/act/act-sequence-retry.js +61 -5
  253. package/dist/surface/tools/act/after-match.d.ts +4 -1
  254. package/dist/surface/tools/act/after-match.js +8 -1
  255. package/dist/surface/tools/act/already-true.js +1 -1
  256. package/dist/surface/tools/act/navigation/reload-result.d.ts +12 -1
  257. package/dist/surface/tools/act/navigation/reload-result.js +25 -2
  258. package/dist/surface/tools/act/resolve-target.d.ts +15 -1
  259. package/dist/surface/tools/act/resolve-target.js +24 -1
  260. package/dist/surface/tools/act/sequence-grade.d.ts +12 -0
  261. package/dist/surface/tools/act/sequence-grade.js +37 -26
  262. package/dist/surface/tools/act/settle-in-flight.js +27 -15
  263. package/dist/surface/tools/act-command.d.ts +23 -0
  264. package/dist/surface/tools/act-command.js +50 -0
  265. package/dist/surface/tools/act-sequence-tool.js +78 -50
  266. package/dist/surface/tools/act-tools.d.ts +1 -13
  267. package/dist/surface/tools/act-tools.js +74 -86
  268. package/dist/surface/tools/agent/build-staleness.d.ts +1 -1
  269. package/dist/surface/tools/agent/build-staleness.js +1 -1
  270. package/dist/surface/tools/assert/assert-verdict.js +16 -37
  271. package/dist/surface/tools/browser-tools.js +12 -6
  272. package/dist/surface/tools/compare-tools.d.ts +11 -0
  273. package/dist/surface/tools/compare-tools.js +94 -0
  274. package/dist/surface/tools/coverage-tools.js +1 -1
  275. package/dist/surface/tools/dynamic-tools.js +9 -2
  276. package/dist/surface/tools/error-recovery.d.ts +12 -1
  277. package/dist/surface/tools/error-recovery.js +91 -21
  278. package/dist/surface/tools/explore-tools.js +1 -1
  279. package/dist/surface/tools/invoke-tool.js +3 -2
  280. package/dist/surface/tools/lease-coverage-tools.d.ts +49 -0
  281. package/dist/surface/tools/lease-coverage-tools.js +128 -0
  282. package/dist/surface/tools/lease-hint.d.ts +35 -1
  283. package/dist/surface/tools/lease-hint.js +42 -0
  284. package/dist/surface/tools/lease-readiness.d.ts +26 -0
  285. package/dist/surface/tools/lease-readiness.js +79 -0
  286. package/dist/surface/tools/lease-seed.d.ts +14 -1
  287. package/dist/surface/tools/lease-seed.js +35 -3
  288. package/dist/surface/tools/lease-tools.d.ts +3 -41
  289. package/dist/surface/tools/lease-tools.js +62 -110
  290. package/dist/surface/tools/lease-url-identity.d.ts +46 -0
  291. package/dist/surface/tools/lease-url-identity.js +97 -0
  292. package/dist/surface/tools/merge-tools.js +99 -2
  293. package/dist/surface/tools/merged-name-redirect.js +5 -1
  294. package/dist/surface/tools/observe-tools.js +15 -9
  295. package/dist/surface/tools/read/console-pre-attach.d.ts +19 -0
  296. package/dist/surface/tools/read/console-pre-attach.js +23 -0
  297. package/dist/surface/tools/read-tools.js +33 -30
  298. package/dist/surface/tools/real-input-attempt.d.ts +0 -8
  299. package/dist/surface/tools/real-input-attempt.js +143 -28
  300. package/dist/surface/tools/session-ports.d.ts +14 -0
  301. package/dist/surface/tools/session-ports.js +53 -0
  302. package/dist/surface/tools/tool-kit.d.ts +6 -0
  303. package/dist/surface/tools/tool-kit.js +2 -3
  304. package/dist/surface/tools/tool-surface.js +0 -1
  305. package/dist/surface/tools/tools.js +28 -5
  306. package/dist/surface/tools/type-spelling.d.ts +13 -0
  307. package/dist/surface/tools/type-spelling.js +36 -0
  308. package/dist/telemetry/agent-client.d.ts +6 -0
  309. package/dist/telemetry/agent-client.js +10 -0
  310. package/dist/telemetry/argument-shape.js +1 -0
  311. package/dist/telemetry/tool-refused.d.ts +6 -3
  312. package/dist/telemetry/tool-refused.js +8 -3
  313. package/docs/actions.mdx +3 -3
  314. package/docs/agent-cheatsheet.md +1 -1
  315. package/docs/capabilities.mdx +61 -1
  316. package/docs/cli/affected.mdx +9 -7
  317. package/docs/cli/cloud.mdx +3 -3
  318. package/docs/cli/compare.mdx +94 -0
  319. package/docs/cli/doctor.mdx +1 -1
  320. package/docs/cli/grade.mdx +97 -0
  321. package/docs/cli/init.mdx +2 -2
  322. package/docs/cli/license.mdx +9 -7
  323. package/docs/cli/restart.mdx +7 -5
  324. package/docs/cli/rollback.mdx +5 -5
  325. package/docs/cli/stop.mdx +3 -3
  326. package/docs/cli/update.mdx +2 -2
  327. package/docs/cli/verify.mdx +23 -1
  328. package/docs/cli.mdx +33 -23
  329. package/docs/deploy-checks.md +1 -1
  330. package/docs/desktop.mdx +1 -1
  331. package/docs/enterprise.md +2 -2
  332. package/docs/environment.mdx +1 -1
  333. package/docs/flows.md +11 -1
  334. package/docs/frameworks.mdx +9 -9
  335. package/docs/getting-started.md +1 -1
  336. package/docs/install-manual.mdx +1 -1
  337. package/docs/license-activation.mdx +3 -3
  338. package/docs/packages/tauri.mdx +1 -1
  339. package/docs/packages.mdx +1 -1
  340. package/docs/predicates.mdx +1 -1
  341. package/docs/quickstart.mdx +68 -49
  342. package/docs/skill-setup.md +1 -1
  343. package/docs/skill.md +14 -13
  344. package/docs/telemetry.md +2 -2
  345. package/docs/tools/navigate.mdx +1 -1
  346. package/docs/tools/verify.mdx +4 -1
  347. package/docs/troubleshooting.mdx +30 -1
  348. package/docs/usage.md +17 -14
  349. package/package.json +11 -14
  350. package/dist/features/exhaust/exhaust-run.d.ts +0 -33
  351. package/dist/features/exhaust/exhaust-run.js +0 -82
@@ -0,0 +1,97 @@
1
+ ---
2
+ title: reticle grade
3
+ description: Grade a finished run of an app from snapshots of its stored data, and check its final answer.
4
+ icon: clipboard-check
5
+ ---
6
+
7
+ `reticle grade` answers one question: did a run leave the app the way the correct steps leave it, and give the correct answer? A run is any sequence of steps on an app you can reset: an agent, a script or a person. The command needs no daemon, no browser and nothing installed in the app. It reads the app's stored data through the [state sources](/cli/compare#state-sources) that `reticle compare --state` uses.
8
+
9
+ ```bash
10
+ npx @reticlehq/server grade snapshot --state <file> --out <file> [--url <url>]
11
+ npx @reticlehq/server grade judge --reference <file> --again <file> --candidate <file> [--expect <file>] [--answer <text>] [--json]
12
+ npx @reticlehq/server grade tables --list <command> --rows <template> [--container <name>] [--skip <prefix,...>] --out <file>
13
+ npx @reticlehq/server grade suggest --state <file> --reference <file> --again <file> --out <file>
14
+ ```
15
+
16
+ ## How to grade a run
17
+
18
+ 1. Reset the app. Do the correct steps. Run `grade snapshot --out reference.json`.
19
+ 2. Reset the app. Do the correct steps again. Run `grade snapshot --out again.json`.
20
+ 3. Reset the app. Do the run to grade. Run `grade snapshot --out candidate.json`.
21
+ 4. Run `grade judge --reference reference.json --again again.json --candidate candidate.json`.
22
+
23
+ Steps 1 and 2 are done one time for each task. Keep the two files, and grade every run of the task against them.
24
+
25
+ `judge` compares the lines of the three snapshots. A line that differs between the two correct runs (a clock, a random id) is noise and is ignored. Any other line that differs between the reference and the candidate is a difference, and the grade fails. So the grade fails when the run changed the wrong record, changed a record the task did not name, or did not change the record at all. A source that could not be read makes the grade unverified, never a pass.
26
+
27
+ A row that the two correct runs wrote with different values in some cells (a time of the run, written into `updated_at`) has noise in those cells only. A candidate row that differs from the reference row only in those cells is not a difference. A change to another cell of that row, or to the same cell of another row, is a difference.
28
+
29
+ Many apps save a record by deleting its child rows and writing them again, with new ids. A run that saves a record two times then has the same data under other ids. `judge` matches these rows: a removed row and an added row of one source are the same record when every cell after the first (the id) is equal, after each id it already matched is replaced by its new id. So a row that links to a written-again row matches too. A match needs an old id that the candidate no longer has, and a new id that the reference did not have, so a changed value in a first column that is not an id is still a difference. `--json` gives the count as `state.renumbered`. This applies to text rows (a `command` source, or a file of plain lines) whose cells are separated by tabs.
30
+
31
+ | Exit code | Verdict | Meaning |
32
+ | --------- | ------------ | ------------------------------------------------------------------ |
33
+ | 0 | `PASS` | The stored data matches, and the answer is correct |
34
+ | 1 | `FAIL` | A line differs, or the answer is wrong |
35
+ | 2 | `UNVERIFIED` | A source or a snapshot could not be read. The grade proves nothing |
36
+
37
+ `--json` prints one JSON object: `verdict`, `state.removed` and `state.added` (every line that differs), `state.ignored`, `answer` and `unread`. The text output shows the first 10 lines on each side and says how many it left out.
38
+
39
+ ## Snapshot
40
+
41
+ `grade snapshot --state <file> --out <file>` reads each declared source and writes its rows as `state <label> <row>` lines, sorted. A snapshot keeps every row: unlike `compare`, it has no 1000-row cap, so a changed row always names its record. A read that fails gives a `state read failed: <label>: <reason>` line, and the command exits 2.
42
+
43
+ `--url <url>` adds the page the run ended on, as a `url` line. Use it for a task whose goal is to open a page. The fragment and a final slash are removed, and the query values are sorted.
44
+
45
+ ## Check the answer
46
+
47
+ A task that asks a question has a final answer. A fixed answer string goes stale when the data changes. Declare a source whose rows are the correct answer instead, and `judge` reads it when it grades:
48
+
49
+ ```json
50
+ {
51
+ "answer": {
52
+ "source": {
53
+ "kind": "command",
54
+ "label": "answer",
55
+ "container": "shop-db",
56
+ "command": "mysql -N shop -e \"select count(*) from sales_order where customer_email = 'jane@example.com'\""
57
+ },
58
+ "match": "number",
59
+ "none": ["N/A"]
60
+ }
61
+ }
62
+ ```
63
+
64
+ Give the file with `--expect`, and the run's answer with `--answer`. Text is folded before it is compared: Unicode normal form, lower case, spacing, quotes and a final full stop.
65
+
66
+ | `match` | The answer passes when |
67
+ | --- | --- |
68
+ | `exact` (default) | It is the one value in the rows. With more than one row, it is checked as `set` |
69
+ | `number` | It is one number and nothing else, equal to the first value within 0.005. `$`, `%` and digit-group commas are removed. `3 orders` and `3 or 4` fail |
70
+ | `set` | It is a list (split on commas, semicolons and new lines, or also on "and") with each value one time and nothing more, in any order |
71
+ | `includes` | It contains each value. An answer that names every choice also passes, so use it only where the run cannot list all choices |
72
+
73
+ A source with no rows means that the task cannot be done. Then the answer must be one of the `none` words. The default is `N/A`. A row with more than one column is one value, with the columns joined by a space.
74
+
75
+ ## Write the declaration
76
+
77
+ The declaration must list every table that a run can change, or a change to that table is not seen. Do not write it by hand. Generate it, then remove the tables that change on every run:
78
+
79
+ ```bash
80
+ # 1. One source for each table.
81
+ npx @reticlehq/server grade tables --container shop-db --skip queue_,cron_ --list "mysql -N shop -e 'show tables'" --rows "mysql -N shop -e 'select * from {table}'" --out wide.json
82
+
83
+ # 2. Two snapshots of the correct steps, from a reset app each time.
84
+ npx @reticlehq/server grade snapshot --state wide.json --out reference.json
85
+ npx @reticlehq/server grade snapshot --state wide.json --out again.json
86
+
87
+ # 3. Keep the tables that read the same on both runs.
88
+ npx @reticlehq/server grade suggest --state wide.json --reference reference.json --again again.json --out state.json
89
+ ```
90
+
91
+ `tables` takes the first column of each line of the `--list` output as a table name. It skips a name with a character other than A-Z, 0-9, `_` or `$`, and says so. `{table}` in `--rows` is the table name.
92
+
93
+ `--skip <prefix,...>` leaves out each table whose name starts with one of the prefixes. Separate the prefixes with commas. Use it for tables that hold no task data and that the app changes at its own time: a message queue that a background worker empties, a cron schedule, a session store. Two correct runs do not always show these tables as noise, because the worker can finish before both snapshots and not before a third. For Magento, `--skip queue_,cron_,session`.
94
+
95
+ Some apps write an empty row that means the same as no row. Magento saves a product form with a `NULL` row for each empty attribute, so a run that saves the form one more time than the reference adds rows with no value. Add an `ignore` pattern that matches all of such a row to the source in `state.json`. A pattern that removes all of a row removes the row. For the Magento attribute tables (`catalog_product_entity_int`, `_decimal`, `_varchar`, `_text`, `_datetime`), use `"ignore": ["^\\d+\\t\\d+\\t\\d+\\t\\d+\\tNULL$"]`.
96
+
97
+ `suggest` keeps a source that reads the same on both runs, also a source the correct steps did not change: a change to it by a run is a change to a record the task did not name. A source that differs between the two runs only by a date and time is kept, with the time removed. Every other source that differs (a session table, a log) is dropped, because each run adds its own new rows to it, and every run would fail. Each dropped source is printed with the first row that differs. A change to a dropped table is not seen.
package/docs/cli/init.mdx CHANGED
@@ -122,8 +122,8 @@ In the two captures above, the punctuation that separates the two halves of the
122
122
 
123
123
  | Code | Meaning |
124
124
  | --- | --- |
125
- | `0` | The app connected. Steps marked `⚠` still need a hand, and do not change the code |
126
- | `1` | No session connected, init failed, an unknown flag was passed, or several apps were found and none was chosen |
125
+ | `0` | The app connected. Or the files are written and the machine kept the dev server from starting (its port was taken, or it answered on one loopback address only): init prints the steps to confirm the connection (`--json` answers `"confirmed": false`). Steps marked `⚠` still need a hand, and do not change the code |
126
+ | `1` | The dev server exited, hung or does not exist, a running app did not connect, init failed, an unknown flag was passed, or several apps were found and none was chosen |
127
127
 
128
128
  An unknown flag is rejected by name: `reticle init --bogus` prints `unknown argument '--bogus'`, then the short usage (`reticle --help`), and exits `1`.
129
129
 
@@ -1,20 +1,22 @@
1
1
  ---
2
- title: reticle license
3
- description: Show the enterprise license status resolved from the environment, offline.
2
+ title: License status
3
+ description: Show the enterprise license status resolved from the environment, offline, with reticle status.
4
4
  icon: key
5
5
  ---
6
6
 
7
- `reticle license` reports whether an enterprise license is active. It resolves entirely from the environment. Nothing leaves the machine.
7
+ [`reticle status`](/cli/status) reports whether an enterprise license is active. It resolves the license entirely from the environment. Nothing leaves the machine.
8
8
 
9
9
  ```bash
10
- npx @reticlehq/server license
10
+ npx @reticlehq/server status --json
11
11
  ```
12
12
 
13
- No flags.
13
+ The block that `status` prints has a `license` row only when a key is set. `--json` always carries the `license` object.
14
+
15
+ `reticle license` is the old name. It still works for one release, prints one line naming `status`, and prints the `reticle_license` line below.
14
16
 
15
17
  ## What it prints
16
18
 
17
- One `reticle_license` JSON line, on stderr. On a checkout with no issuer key configured:
19
+ The `license` object of `status --json`. `reticle license` printed the same fields as one `reticle_license` JSON line on stderr. On a checkout with no issuer key configured:
18
20
 
19
21
  ```json
20
22
  {
@@ -43,7 +45,7 @@ Always `0`.
43
45
  ## Worked example
44
46
 
45
47
  ```bash
46
- npx @reticlehq/server license 2>&1 | jq -r .status
48
+ npx @reticlehq/server status --json | jq -r .license.status
47
49
  ```
48
50
 
49
51
  <Card title="Enterprise" icon="building" href="/enterprise">
@@ -1,15 +1,17 @@
1
1
  ---
2
- title: reticle restart
2
+ title: reticle serve --restart
3
3
  description: Free the bridge port and put a daemon back on it, reporting each half separately.
4
4
  icon: rotate
5
5
  ---
6
6
 
7
- `reticle restart` is [`reticle stop --force`](/cli/stop#freeing-the-port-force) followed by a daemon start that waits for a real bind.
7
+ `reticle serve --restart` is [`reticle stop --force`](/cli/stop#freeing-the-port-force) followed by a daemon start that waits for a real bind.
8
8
 
9
9
  ```bash
10
- npx @reticlehq/server restart [--port N] [--force]
10
+ npx @reticlehq/server serve --restart [--port N] [--force]
11
11
  ```
12
12
 
13
+ `reticle restart` is the old name. It still works for one release and prints one line naming `serve --restart`.
14
+
13
15
  ## Flags
14
16
 
15
17
  | Flag | Type | Default | What it does |
@@ -31,7 +33,7 @@ Real capture, restarting over a running daemon (exit `0`), with the long `note`
31
33
  Real capture, when the port is held by something that is not ours (exit `1`). Both `reason` strings are trimmed at the ellipsis:
32
34
 
33
35
  ```
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. If that is what you meant, `reticle restart --force` stops it and starts Reticle there (`stop --force` never does); or start Reticle on another port.","holder":"Python"}
36
+ {"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. If that is what you meant, `reticle serve --restart --force` stops it and starts Reticle there (`stop --force` never does); or start Reticle on another port.","holder":"Python"}
35
37
  {"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
38
  ```
37
39
 
@@ -51,7 +53,7 @@ Real capture, when the port is held by something that is not ours (exit `1`). Bo
51
53
  ## Worked example
52
54
 
53
55
  ```bash
54
- npx @reticlehq/server restart # after an upgrade, so the new version is the one serving
56
+ npx @reticlehq/server serve --restart # after an upgrade, so the new version is the one serving
55
57
  npx @reticlehq/server status
56
58
  ```
57
59
 
@@ -1,16 +1,16 @@
1
1
  ---
2
- title: reticle rollback
2
+ title: reticle update --rollback
3
3
  description: Reinstall the previously installed Reticle server version, then exit so your agent restarts it.
4
4
  icon: arrow-rotate-left
5
5
  ---
6
6
 
7
- `reticle rollback` is the undo for [`reticle update`](/cli/update). It reinstalls the version `update` replaced (recorded in the update manifest), then exits so the next start runs it.
7
+ `reticle update --rollback` is the undo for [`reticle update`](/cli/update). It reinstalls the version `update` replaced (recorded in the update manifest), then exits so the next start runs it.
8
8
 
9
9
  ```bash
10
- npx @reticlehq/server rollback
10
+ npx @reticlehq/server update --rollback
11
11
  ```
12
12
 
13
- No flags.
13
+ `reticle rollback` is the old name. It still works for one release and prints one line naming `update --rollback`.
14
14
 
15
15
  ## What it prints
16
16
 
@@ -36,6 +36,6 @@ On the global install the installer puts on your PATH (under `npx` there is noth
36
36
  ```text
37
37
  reticle update
38
38
  # the new build misbehaves:
39
- reticle rollback
39
+ reticle update --rollback
40
40
  reticle version
41
41
  ```
package/docs/cli/stop.mdx CHANGED
@@ -10,7 +10,7 @@ icon: circle-stop
10
10
  npx @reticlehq/server stop [--port N] [--quiet] [--force]
11
11
  ```
12
12
 
13
- `reticle kill` is the old name for `stop --force`. It still works for one release and prints one line naming the new spelling.
13
+ `reticle kill` was removed. It prints that `stop --force` replaces it and exits `1`.
14
14
 
15
15
  ## Flags
16
16
 
@@ -94,7 +94,7 @@ Killed:
94
94
  Refused, because the listener is not ours. This one was captured with `python3 -m http.server 4498` holding the port:
95
95
 
96
96
  ```
97
- {"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. If that is what you meant, `reticle restart --force` stops it and starts Reticle there (`stop --force` never does); or start Reticle on another port.","holder":"Python"}
97
+ {"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. If that is what you meant, `reticle serve --restart --force` stops it and starts Reticle there (`stop --force` never does); or start Reticle on another port.","holder":"Python"}
98
98
  ```
99
99
 
100
100
  Nothing to free:
@@ -118,6 +118,6 @@ With `--force`, exit `0` means the port is free and `1` means it is still held:
118
118
 
119
119
  ```bash
120
120
  npx @reticlehq/server stop --port 4400 --quiet && npx @reticlehq/server serve --port 4400
121
- npx @reticlehq/server restart --port 4400 # the same thing, honest about each half
121
+ npx @reticlehq/server serve --restart --port 4400 # the same thing, honest about each half
122
122
  npx @reticlehq/server stop --force # the port is held and the pid file says nothing
123
123
  ```
@@ -61,6 +61,6 @@ npx @reticlehq/server update
61
61
  <Warning>
62
62
  A daemon that is already running keeps its own version. When no tab and no other agent is attached
63
63
  to it, your agent's new MCP server stops it and starts the new version. When a tab or another
64
- agent is attached, the old daemon keeps running. Run [`reticle restart`](/cli/restart) to start
65
- the new version now.
64
+ agent is attached, the old daemon keeps running. Run [`reticle serve --restart`](/cli/restart) to
65
+ start the new version now.
66
66
  </Warning>
@@ -20,12 +20,13 @@ It uses the same runner and the same verdict as the MCP and HTTP paths, so a pla
20
20
  | `--headed` | boolean | `false` (headless) | Show the browser |
21
21
  | `--timeout N` | number, ms | `15000` | How long to wait for a browser session to dial back. With `--expect`, how long the predicate may take to come true instead, default `4000` |
22
22
  | `--storage-state <file>` | string | unset | A Playwright storage state file. This is how you verify flows behind a login without scripting the login every time |
23
- | `--results-json <file>` | string | unset | Also write one verdict per journey to this file: `{ verdict, results: [{ journey, verdict, reason?, runId? }] }`, each verdict `yes`, `no` or `unknown`. A skipped journey is `unknown`, and the run is `yes` only when every journey is. `runId` names the run the journey came from, and is set only when that run reached the platform. Written when verify boots its own browser and a run finishes; a refusal before that writes nothing. The [GitHub Action](https://github.com/reticlehq/reticle/tree/main/action) posts this file |
23
+ | `--results-json <file>` | string | unset | Also write one verdict per journey to this file: `{ schemaVersion, verdict, results: [{ journey, verdict, reason?, runId?, steps }], score }`, each verdict `yes`, `no` or `unknown`. `schemaVersion` is 2 (a file without it has only `verdict` and `results`). `steps` is `{ passed, failed, unknown }` for the journey, and `score` is the total with `value` = passed / total. A step counts only when every earlier step of its journey passed; a step never reached is `unknown` and stays in the total. With `--sweep`, verdicts after a failed step are reported apart as `steps.swept` and never raise the score. A skipped journey is `unknown`, and the run is `yes` only when every journey is. `runId` names the run the journey came from, and is set only when that run reached the platform. Written when verify boots its own browser and a run finishes; a refusal before that writes nothing. The [GitHub Action](https://github.com/reticlehq/reticle/tree/main/action) posts this file |
24
24
  | `--port N` | number | `RETICLE_PORT`, else the project's port, else `4400` | The bridge port the app dials |
25
25
  | `--session-id <id>` | string | unset | Use a tab that is already connected |
26
26
  | `--select <label>` | string, repeatable | unset | Verify only flows carrying this label |
27
27
  | `--expect '<json predicate>'` / `--expect-file <path>` | string | unset | One verdict on one predicate, no saved flows needed. Asks the daemon that owns the port and never starts one; with no daemon it refuses. Waits up to `--timeout` for the predicate, the way `reticle_assert { action: "wait" }` does, so a request still in flight after the page loads is waited for. Exits `0` only on `verified: "yes"`. Cannot be combined with `--storage-state` |
28
28
  | `--explore` / `--persona "<who does what>"` | boolean / string | off | No saved flows yet? The Harness drives the app and records them. `--persona` implies `--explore`. Needs a linked project (`reticle connect`, or `RETICLE_API_KEY` in CI). It runs at any instrumentation score |
29
+ | `--against <reference-url>` | string | unset | Compare the URL with a reference build, for example a pull request preview with production. See [Compare two builds](#compare-two-builds). Cannot be combined with `--expect`, `--expect-file`, `--explore` or `--persona` |
29
30
  | `--confirm-dangerous` | boolean | off | Let this run press the controls the replay guard holds as destructive, such as a "Place order" or "Delete" button in a saved flow. Without it such a step is refused, so a checkout flow cannot pass. It covers this run only and is never saved into a flow. |
30
31
 
31
32
  On Windows PowerShell, write the predicate to a file and pass `--expect-file` so the JSON never goes through shell quoting:
@@ -35,6 +36,27 @@ On Windows PowerShell, write the predicate to a file and pass `--expect-file` so
35
36
  npx @reticlehq/server verify http://localhost:3000 --expect-file expect.json
36
37
  ```
37
38
 
39
+ ## Compare two builds
40
+
41
+ ```bash
42
+ npx @reticlehq/server verify https://pr-42.preview.example.com --against https://example.com
43
+ ```
44
+
45
+ `--against` answers one question: did the change alter what users see? It drives both running builds with the same actions and reports each difference in what a person can read and use.
46
+
47
+ - **The actions.** Each saved flow in `.reticle/flows/` (with `--select`, only the flows with those labels) is replayed one step at a time, and the page is read after every step. Then the explorer of `reticle_verify { action: "crawl", exhaustive: true }` walks the reference breadth first. It fills each empty text field with its own value (`text 1`, `text 2`), clicks each control, and waits a short time after each action. Both builds then get the same list of actions.
48
+ - **What is compared.** The accessibility tree, reduced to what a person sees: names, text, values, and the checked, selected, disabled, expanded and pressed states, in page order. Case and spacing are folded. Refs, focus, layout, test ids, source locations and component names are removed.
49
+ - **Noise.** The reference is driven two times, each time in a new browser. A line that differs between those two runs (a clock, a random id) is ignored. A step that the reference did on only one of its runs is not compared.
50
+ - **A step the candidate cannot do.** When the reference did a step and the candidate could not (the control is gone, or a flow anchor does not resolve), that is a difference.
51
+
52
+ The command uses the daemon on `--port`, and starts one when there is none. Each run is a leased browser tab, so the apps do not need `reticle init`. Each replayed flow step is written as a run, the same as any replay.
53
+
54
+ The explorer clicks every control it finds. The action guard refuses a control it holds as destructive, and `--confirm-dangerous` removes that guard. Point `--against` at a build whose data you can change, not at a production database.
55
+
56
+ Each difference prints the actions that first reached it, then `-` for the lines only the reference shows and `+` for the lines only the candidate shows. Differences with the same lines are printed one time, with the number of steps they were seen at. Exit `0` only when steps were compared and nothing differs. A run that compared no step exits `1`, because it proved nothing.
57
+
58
+ `--results-json <file>` writes `{ schemaVersion: 1, reference, candidate, identical, differences: [{ id, kind, actions, removed, added, seen }], journeys, compared, unstable, ignored, explored }`. `kind` is `changed` or `unreached`. `id` is a hash of the difference's lines, so the same difference has the same id on every run. `ignored` counts the lines skipped as noise, and `unstable` counts the steps not compared.
59
+
38
60
  ## What it prints
39
61
 
40
62
  The rendered run report on stdout. On failure it names the cause on stderr instead, and there are two refusals worth knowing:
package/docs/cli.mdx CHANGED
@@ -41,18 +41,20 @@ npx @reticlehq/server <command>
41
41
  | Command | What it does | Page |
42
42
  | --- | --- | --- |
43
43
  | `reticle verify <url>` | One-shot: drive the URL, verify saved flows or one predicate, exit 0 on pass | [verify](/cli/verify) |
44
+ | `reticle compare <reference-url> <candidate-url> [--base <url>] [--state <file>] [--json] [--report <file>]` | Drive two running builds the same way and list each difference in what users see; exit 0 same, 1 different, 2 invalid | [compare](/cli/compare) |
45
+ | `reticle grade <snapshot\|judge\|tables\|suggest> [flags]` | Grade a finished run of an app from snapshots of its stored data, and check its final answer; exit 0 pass, 1 fail, 2 unverified | [grade](/cli/grade) |
44
46
  | `reticle gate [--since <ref>] [--accept-coverage] [--hook]` | Exit non-zero unless passing artifacts cover the affected flows | [gate](/cli/gate) |
47
+ | `reticle gate --list [--since <ref>]` | Which saved flows must re-verify for the changed files | [affected](/cli/affected) |
45
48
 
46
49
  ### Reticle on this machine
47
50
 
48
51
  | Command | What it does | Page |
49
52
  | --- | --- | --- |
50
- | `reticle status [--json]` | Is the daemon up, which pages connected, who is signed in, is this repo linked | [status](/cli/status) |
53
+ | `reticle status [--json]` | Is the daemon up, which pages connected, who is signed in, is this repo linked, which license is active | [status](/cli/status) |
51
54
  | `reticle doctor` | Find what is wrong with the setup (Chromium, daemon, port, agent link), and what is missing from coverage | [doctor](/cli/doctor) |
52
55
  | `reticle open [url]` | Show the app: reuse the connected tab, else open one | [open](/cli/open) |
53
56
  | `reticle stop [--force]` | Stop the daemon we started; `--force` frees the port by its listener, never the agent's MCP proxy | [stop](/cli/stop) |
54
- | `reticle restart` | Free the port, then start a daemon and wait for a real bind | [restart](/cli/restart) |
55
- | `reticle update` | Install the latest server version and restart | [update](/cli/update) |
57
+ | `reticle update [--rollback]` | Install the latest server version and restart; `--rollback` restores the previous one | [update](/cli/update) |
56
58
 
57
59
  ### Account
58
60
 
@@ -75,14 +77,11 @@ npx @reticlehq/server <command>
75
77
  | `reticle config` | Choose what syncs: runs, memory, flows; where verification runs | [cloud](/cli/cloud#reticle-config) |
76
78
  | `reticle hook [status\|install\|uninstall]` | The prompt hook: is each prompt captured here, add it, or remove exactly what was added | [prompt capture](/prompt-capture#reticle-hook) |
77
79
  | `reticle setup mcp` | Register the MCP server with your coding agents (the installer's registration half) | [setup](/cli/setup) |
78
- | `reticle serve` | Start the daemon and wait for it to bind | [serve](/cli/serve) |
80
+ | `reticle serve [--restart]` | Start the daemon and wait for it to bind; `--restart` frees the port first | [serve](/cli/serve) |
79
81
  | `reticle mcp` | The MCP stdio proxy your agent runs; starts the daemon if needed | [mcp](/cli/mcp) |
80
82
  | `reticle drive <url>` | Foreground mode, for debugging | [drive](/cli/drive) |
81
- | `reticle affected [--since <ref>]` | Which saved flows must re-verify for the changed files | [affected](/cli/affected) |
82
83
  | `reticle context --compact [--session <id>]` | Resume after a compaction: what was proved, failed and is still owed | none |
83
84
  | `reticle report [--session <id>] [--hook]` | What the latest session claimed, and what held | [report](/cli/report) |
84
- | `reticle rollback` | Restore the previous server version and restart | [rollback](/cli/rollback) |
85
- | `reticle license` | Enterprise license status: active, eval or missing | [license](/cli/license) |
86
85
  | `reticle version` | Print the version | [version](/cli/version) |
87
86
  | `reticle help [all\|<command>]` | Usage: the short list, every command, or one command | [help](/cli/help) |
88
87
 
@@ -90,16 +89,27 @@ npx @reticlehq/server <command>
90
89
 
91
90
  The old names below still work for one release and print one line naming the new one.
92
91
 
93
- | Old | Now |
92
+ | Old | Now |
93
+ | ------------------ | --------------------------- |
94
+ | `reticle affected` | `reticle gate --list` |
95
+ | `reticle license` | `reticle status` |
96
+ | `reticle rollback` | `reticle update --rollback` |
97
+ | `reticle restart` | `reticle serve --restart` |
98
+
99
+ These names were removed. Each prints what to use instead and exits `1`.
100
+
101
+ | Removed | Use |
94
102
  | --- | --- |
95
103
  | `reticle kill` | `reticle stop --force` |
96
- | `reticle login`, `reticle link` | `reticle connect`. `reticle login --email` is gone: sign-in is only in the browser, with Google or GitHub. Without a browser, set `RETICLE_API_KEY` |
104
+ | `reticle login`, `reticle link` | `reticle connect`. Sign-in is only in the browser, with Google or GitHub. Without a browser, set `RETICLE_API_KEY` |
97
105
  | `reticle project` | `reticle connect --project <name\|id>` |
98
106
  | `reticle whoami` | `reticle status` |
99
107
  | `reticle push` | `reticle sync` |
100
108
  | `reticle regression`, `share`, `issues`, `memory` | `reticle runs regression`, `runs share`, `runs issues`, `runs memory` |
101
-
102
- `hunt`, `watch`, `capsules`, `tutorial` and `identify` were removed. Each prints what to use instead and exits `1`: `reticle affected` or `reticle gate` for `watch`, `reticle init` or `reticle try <url>` for `tutorial`, `reticle feedback` for `identify`. Saved capsules stay in `.reticle/capsules`.
109
+ | `reticle watch` | `reticle gate --list` or `reticle gate` |
110
+ | `reticle tutorial` | `reticle init` or `reticle try <url>` |
111
+ | `reticle identify` | `reticle feedback` |
112
+ | `reticle hunt`, `reticle capsules` | nothing. Saved capsules stay in `.reticle/capsules` |
103
113
 
104
114
  ## Setup and diagnosis
105
115
 
@@ -192,7 +202,7 @@ policy https://github.com/reticlehq/reticle/blob/main/docs/telemetry.md
192
202
  | `reticle serve` | Start the daemon and wait for it to bind. `--drive <url>`, `--headless`, `--headed`, `--http`, `--http-port N`, `--http-token T` |
193
203
  | `reticle stop` | Stop the daemon we started, by its recorded pid. `--quiet` |
194
204
  | `reticle stop --force` | Free the port by asking who is **listening** on it |
195
- | `reticle restart` | Free the port, then start a daemon and wait for a real bind. `--force` |
205
+ | `reticle serve --restart` | Free the port, then start a daemon and wait for a real bind. `--force` |
196
206
  | `reticle open [url]` | Show the app. Reuse the connected tab, or open one |
197
207
  | `reticle drive <url>` | Foreground mode, for debugging |
198
208
  | `reticle mcp` | The MCP stdio proxy. Auto-starts the daemon if needed. This is what your agent runs |
@@ -203,7 +213,7 @@ A bare `reticle` with no arguments is not help. In a project that `init` wired,
203
213
  When the Reticle SDK installed in the project is a different **major** version from the CLI you ran,
204
214
  the CLI hands the same command to `npx @reticlehq/server@<that version>`, so an unpinned
205
215
  `npx @reticlehq/server` never pairs a daemon with an SDK it cannot talk to. `init`, `update`,
206
- `rollback`, `setup install`, `version` and `help` always run as typed, and so does a command whose
216
+ `update --rollback`, `setup install`, `version` and `help` always run as typed, and so does a command whose
207
217
  version you pinned yourself (`npx @reticlehq/server@<version> …`). Set `RETICLE_NO_VERSION_MATCH=1` to
208
218
  turn it off. Because `--help` runs as typed too, it describes the CLI you ran. When the command it
209
219
  describes would run at the project's version instead, the help opens with a line naming that version.
@@ -238,12 +248,12 @@ Options:
238
248
  - `--results-json <file>`: also write one verdict per journey as JSON, for CI.
239
249
  - `--confirm-dangerous`: let this run press what the replay guard holds as destructive.
240
250
 
241
- ### `reticle affected`
251
+ ### `reticle gate --list`
242
252
 
243
- Which saved flows must re-verify for a set of changed files.
253
+ Which saved flows must re-verify for a set of changed files. It checks nothing.
244
254
 
245
255
  ```bash
246
- npx @reticlehq/server affected --since HEAD~1
256
+ npx @reticlehq/server gate --list --since HEAD~1
247
257
  ```
248
258
 
249
259
  ```json
@@ -290,13 +300,13 @@ Measured exit code: **`1`**, with the flow lists trimmed above. That run genuine
290
300
 
291
301
  ## Maintenance
292
302
 
293
- | Command | What it does |
294
- | ------------------ | --------------------------------------------------- |
295
- | `reticle update` | Install the latest server version and restart |
296
- | `reticle rollback` | Restore the previous version and restart |
297
- | `reticle license` | Enterprise license status: active, eval, or missing |
298
- | `reticle version` | Print the version |
299
- | `reticle help` | The short list; `help all` for every command |
303
+ | Command | What it does |
304
+ | --------------------------- | ------------------------------------------------------------ |
305
+ | `reticle update` | Install the latest server version and restart |
306
+ | `reticle update --rollback` | Restore the previous version and restart |
307
+ | `reticle status --json` | Carries `license`: active, eval, missing, expired or invalid |
308
+ | `reticle version` | Print the version |
309
+ | `reticle help` | The short list; `help all` for every command |
300
310
 
301
311
  <Tip>
302
312
  `rollback` exists because an auto-updating dev tool that cannot go back is a dev tool that can
@@ -70,7 +70,7 @@ jobs:
70
70
  runs-on: ubuntu-latest
71
71
  steps:
72
72
  - uses: actions/checkout@v4
73
- - uses: reticlehq/reticle/action@v3.9.0
73
+ - uses: reticlehq/reticle/action@v3.10.1
74
74
  with:
75
75
  api-key: ${{ secrets.RETICLE_API_KEY }}
76
76
  # Only for a Vercel preview behind Deployment Protection.
package/docs/desktop.mdx CHANGED
@@ -224,7 +224,7 @@ Alternatively, since an Electron renderer _is_ Chromium, `--remote-debugging-por
224
224
 
225
225
  ```toml
226
226
  [dependencies]
227
- reticle-tauri = "3.9"
227
+ reticle-tauri = "3.10"
228
228
  ```
229
229
 
230
230
  ```rust
@@ -22,7 +22,7 @@ No feature in the open packages needs a key. A key identifies your organisation
22
22
  export RETICLE_LICENSE_KEY="<your key>"
23
23
  # the release already bakes the issuer public key (RETICLE_LICENSE_PUBLIC_KEY)
24
24
  ```
25
- 3. **Verify.** `reticle license` prints one JSON line whose `status` and `detail` say where you stand:
25
+ 3. **Verify.** `reticle status --json` carries a `license` object whose `status` and `detail` say where you stand:
26
26
 
27
27
  ```
28
28
  active licensed to Acme Corp (enterprise), expires 2027-06-20T00:00:00.000Z
@@ -36,7 +36,7 @@ No feature in the open packages needs a key. A key identifies your organisation
36
36
 
37
37
  4. **Renew.** Keys carry an expiry, shown as `expiresAt`; once it passes, the status is `expired`.
38
38
 
39
- > Procuring a license: contact **[hey@reticle.sh](mailto:hey@reticle.sh)**. Keys are issued offline and signed with Ed25519; the activation you run (`reticle license`) verifies them locally with no network call.
39
+ > Procuring a license: contact **[hey@reticle.sh](mailto:hey@reticle.sh)**. Keys are issued offline and signed with Ed25519; the activation check (`reticle status`) verifies them locally with no network call.
40
40
 
41
41
  ## Roadmap
42
42
 
@@ -53,7 +53,7 @@ Reticle works with none of these set. Each one changes a single behaviour, and m
53
53
  | Variable | Read by | What it does |
54
54
  | --- | --- | --- |
55
55
  | `RETICLE_SKIP_GATE` | `reticle gate --hook` | Lets an agent's stop hook proceed past a failing gate. The skip is printed and recorded, never silent. It does not affect `reticle gate` without `--hook`, which is what CI runs. |
56
- | `RETICLE_NO_CI_HINT` | `reticle gate`, `reticle verify`, `reticle affected` | `1` stops the one line these commands write to stderr at the end of a run in a CI runner or a container. The line names the pull request check and `https://reticle.sh/ci`. It never prints in an interactive terminal or for `gate --hook`, and it makes no network call. |
56
+ | `RETICLE_NO_CI_HINT` | `reticle gate`, `reticle verify` | `1` stops the one line these commands write to stderr at the end of a run in a CI runner or a container. The line names the pull request check and `https://reticle.sh/ci`. It never prints in an interactive terminal or for `gate --hook`, and it makes no network call. |
57
57
  | `RETICLE_VERIFY_TOKEN` | `reticle serve --http` | The bearer token the optional HTTP verify endpoint requires. |
58
58
  | `RETICLE_SECRET_<FIELD>` | replay, verify, crawl | The value typed into a password or secret field at replay. A recorded flow never stores it. See [what is recorded](/what-is-recorded). |
59
59
 
package/docs/flows.md CHANGED
@@ -80,7 +80,7 @@ reticle_run({ tool: "reticle_flow_save", args: { flowName: "create-task" } })
80
80
  }
81
81
  ```
82
82
 
83
- Each step binds to a **semantic anchor**, never a `eXX` ref: a `testid`/`signal` when available, else an auto-derived `component` anchor (component name + source `file:line`) for an element with no testid, so the flow stays stable with zero hand-added testids. A press of a document key (Escape, Tab, a shortcut) with no target is aimed at no element, so it is saved with a `{ "kind": "role", "role": "document" }` anchor and replayed at the document, as it ran. Only when none of those resolve is a step kept `degraded: true` (a last-resort "add a testid here" marker) rather than silently dropped.
83
+ Each step binds to a **semantic anchor**, never a `eXX` ref: a `testid`/`signal` when available, else an auto-derived `component` anchor (component name + source `file:line`) for an element with no testid, so the flow stays stable with zero hand-added testids. A press of a document key (Escape, Tab, an arrow key, a shortcut) with no target is aimed at no element, so it is saved with a `{ "kind": "role", "role": "document" }` anchor and replayed at the document, as it ran. Only when none of those resolve is a step kept `degraded: true` (a last-resort "add a testid here" marker) rather than silently dropped.
84
84
 
85
85
  ## Run a flow
86
86
 
@@ -110,6 +110,16 @@ Every session's driving is saved as a flow at teardown when it declared a conseq
110
110
  - declare `requires` (the start page) and `ensures` (the end page) as route claims, so `canFollow` can tell which flows chain. Replay treats a required start page as met by navigating there;
111
111
  - carry `author: { agent, person }`: the MCP client that drove it and the email of whoever signed in with `reticle connect` on that machine. Either is left out when it is not known.
112
112
 
113
+ ### A route in `requires`
114
+
115
+ A route claim in `requires` says which page the flow needs. Replay puts the tab there before step 1, so a suite does not run the flow on the page that the previous flow left open. It goes to `startPath` when that is on the route, else to the route itself.
116
+
117
+ A route can name an entity id with a `:name` segment, for example `{ "kind": "route", "pathname": "/cases/:id/notes" }`. When the tab is already on a page that matches, replay keeps that page and its id. When the tab is on another page of the same record (`/cases/c-42/files`), replay opens the route with that id (`/cases/c-42/notes`). It opens the recorded address only when the tab has no id to give. The recorded id can be a record that was deleted since.
118
+
119
+ Replay checks every route claim in `requires`, its `contains` too, on the page it arrives at. When the tab is already on the route, replay reloads it, the same reset a `startPath` gets. State that the previous flow left in memory is lost. A flow that needs that state declares it with a claim that is not a route.
120
+
121
+ When replay cannot put the tab on the route (the page refuses, the app redirects to another page, or a pattern has no recorded address to open), the flow is `unverifiable` with a reason that names the route. No step runs.
122
+
113
123
  ## Delete a flow
114
124
 
115
125
  ```jsonc
@@ -5,7 +5,7 @@ description: Which frameworks Reticle supports, which are wired but unverified,
5
5
  icon: layer-group
6
6
  ---
7
7
 
8
- **React + Vite, Next.js, Remix and Astro are fully supported, each with an example app in CI: React + Vite and Next.js are driven to a verdict by the e2e battery, and the Remix and Astro examples are booted and must register a session. SvelteKit, Nuxt, React Router framework mode, Create React App, Vue and Angular have their INSTALL gated: the install gate scaffolds each from scratch whenever the install path changes and requires a real session, but no gate drives them to a verdict. TanStack Start, Preact and plain HTML are wired by `init` and documented but not gated at all, so treat them as plausible rather than proven.** The exact wiring for each is below.
8
+ **Reticle works on React, Next.js, Remix, Astro, Vue, SvelteKit and Angular. React + Vite, Next.js, Remix and Astro each have an example app in CI: React + Vite and Next.js are driven to a verdict by the e2e battery, and the Remix and Astro examples are booted and must register a session. Vue, SvelteKit and Angular are supported with the framework-neutral sensor; their install is gated (scaffolded from scratch whenever the install path changes, and a real session is required), and no CI gate drives them to a verdict yet. The `file:line` source pointer works on React and Svelte, not on Vue or Angular. Nuxt, React Router framework mode and Create React App have their install gated the same way. TanStack Start, Preact and plain HTML are wired by `init` and documented but not gated at all, so treat them as plausible rather than proven.** The exact wiring for each is below.
9
9
 
10
10
  Reticle is a verification layer that embeds a dev-only SDK in your running web app so an AI coding agent can prove a change works. Its core is framework-neutral: DOM, network, console, routing, animations and source mapping work anywhere JavaScript runs.
11
11
 
@@ -15,7 +15,7 @@ What varies is two things: how `connect()` gets into your app, and whether you g
15
15
 
16
16
  ## Where each framework stands
17
17
 
18
- We separate "we tested this" from "this should work". Four frameworks have an example app and a CI gate. The rest are wired and documented but nothing proves they still work.
18
+ We separate "supported" from "what CI proves". Four frameworks have an example app and a CI gate. Vue, SvelteKit and Angular are supported and install gated. The rest are wired and documented, and nothing in CI proves they still work.
19
19
 
20
20
  Two columns say different things and it is worth keeping them apart. **CI gate** `yes` means an example app runs in CI: React + Vite and Next.js are driven to a verdict, Remix and Astro are booted and must register a session. **install only** means the install gate scaffolds that stack from scratch, runs `init`, boots it and waits for a session, so the setup is proven and the drive is not. Vue is there because a release once nearly shipped an installer that left a Vue app connecting 0% of the time, and the gate that exists for exactly that had only React scaffolds.
21
21
 
@@ -27,14 +27,14 @@ Two columns say different things and it is worth keeping them apart. **CI gate**
27
27
  | **Next.js** | yes | yes | yes | yes | Supported |
28
28
  | **Remix** | yes | yes | yes | yes | Supported |
29
29
  | **Astro** | yes | yes | yes | yes | Supported |
30
- | **SvelteKit** | yes | no | install only | unproven | Install gated, drive unverified |
30
+ | **SvelteKit** | yes | no | install only | yes | Supported |
31
31
  | **TanStack Start** | yes | no | no | unproven | Wired, unverified |
32
32
  | **Nuxt** | yes | no | install only | no | Install gated, drive unverified |
33
33
  | **React Router (framework mode)** | yes | no | install only | yes | Install gated, drive unverified |
34
34
  | **Create React App** | yes | no | install only | unproven | Install gated, drive unverified |
35
- | **Angular 17+** | yes | no | install only | no | Install gated, drive unverified |
35
+ | **Angular 17+** | yes | no | install only | no | Supported (no `file:line`) |
36
36
  | **Plain HTML** | yes | no | no | no | Wired, unverified |
37
- | **Vue (non-Nuxt)** | as Vite | no | install only | no | Install gated, drive unverified |
37
+ | **Vue (non-Nuxt)** | as Vite | no | install only | no | Supported (no `file:line`) |
38
38
  | **Preact** | as Vite | no | no | transform-tested; drive unproven | Sensor, React adapter via `preact/compat` |
39
39
  | **Server-rendered (Django, Flask, FastAPI, Streamlit, Rails)** | yes | no | no | no | Wired, unverified |
40
40
 
@@ -392,10 +392,10 @@ Solid appears in dependency detection but has no wiring, no adapter and no gate.
392
392
  </Accordion>
393
393
 
394
394
  <Accordion title="Does Reticle work with Vue?">
395
- Partly, and the limit is specific. Install `@reticlehq/browser` rather than the React kit, and you
396
- get DOM, network, console, routing, actions and Pinia state. What you do not get is a `source`
397
- field: source stamping covers JSX and Svelte components, and a Vue single-file component is
398
- neither. The install path is covered by a gate; the drive is not.
395
+ Yes. Install `@reticlehq/browser` rather than the React kit, and you get DOM, network, console,
396
+ routing, actions, verdicts and Pinia state. The one thing you do not get is a `source` field:
397
+ source stamping covers JSX and Svelte components, and a Vue single-file component is neither. The
398
+ install path is gated in CI; no example app is driven to a verdict there yet.
399
399
  </Accordion>
400
400
 
401
401
  <Accordion title="Which framework should I pick if I want the strongest verdicts?">
@@ -287,7 +287,7 @@ if (process.env.NODE_ENV !== 'production') {
287
287
 
288
288
  Change the `./reticle.local.js` path so that it points to the project root from your entry module. Do not commit `reticle.local.js`: the token is for this machine only. A teammate runs `reticle init` to get their own.
289
289
 
290
- Plain HTML with no build step: run `npx @reticlehq/server init` in the folder that holds `index.html`. It adds a dev-only snippet that connects only on localhost, and puts this machine's pairing token in a gitignored `reticle.local.js` beside the page, so the token never lands in anything you publish. A hand-written script tag without that token is refused by the bridge.
290
+ Plain HTML with no build step: run `npx @reticlehq/server init` in the folder that holds `index.html`. Init does not change `index.html`. It serves the folder with Reticle's static server, which adds a dev-only connect snippet to the page that it serves. With `--url` or `--files-only`, nothing of Reticle's serves the page, so init writes the snippet into `index.html`. That snippet connects only on localhost. Init puts this machine's pairing token in a gitignored `reticle.local.js` beside the page, so the token never lands in anything you publish. A hand-written script tag without that token is refused by the bridge.
291
291
 
292
292
  > **Want to watch the agent work?** Add `present: true` to `reticle.connect()` for a glowing border, a synthetic cursor that flies to targets, click/hover effects, and a narration HUD. See [usage §16](usage.md#16-presenter-mode-narration-&-fake-clock-watch-+-control).
293
293
 
@@ -202,7 +202,7 @@ Screenshots and headless mode need the Rust crate, which is versioned independen
202
202
 
203
203
  ```toml
204
204
  [dependencies]
205
- reticle-tauri = "3.9"
205
+ reticle-tauri = "3.10"
206
206
  ```
207
207
 
208
208
  ```rust
@@ -67,10 +67,10 @@ Put the key in the MCP server's own `env` block instead, then restart the editor
67
67
  ## Confirm it worked
68
68
 
69
69
  ```bash
70
- npx @reticlehq/server license
70
+ npx @reticlehq/server status --json
71
71
  ```
72
72
 
73
- You do not need `NODE_ENV` set, a project, a dev server, or a running app. This reads the key and nothing else.
73
+ Read `license.status` in the output. You do not need `NODE_ENV` set, a project, a dev server, or a running app. The license check reads the key and nothing else.
74
74
 
75
75
  | `status` | What it means | What to do |
76
76
  | --- | --- | --- |
@@ -142,4 +142,4 @@ export RETICLE_TELEMETRY=0
142
142
 
143
143
  [hey@reticle.sh](mailto:hey@reticle.sh) for activation problems, renewals and re-issues.
144
144
 
145
- Include the output of `npx @reticlehq/server license`. It carries your license id and no secrets, and it usually tells us the answer immediately.
145
+ Include the output of `npx @reticlehq/server status --json`. It carries your license id and no secrets, and it usually tells us the answer immediately.