@reticlehq/server 2.4.0 → 2.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (472) hide show
  1. package/SKILL.md +719 -0
  2. package/dist/bridge/auth-failure-reason.d.ts +19 -0
  3. package/dist/bridge/auth-failure-reason.js +29 -0
  4. package/dist/{bridge-security.d.ts → bridge/bridge-security.d.ts} +1 -1
  5. package/dist/{bridge-security.js → bridge/bridge-security.js} +1 -1
  6. package/dist/{bridge.d.ts → bridge/bridge.d.ts} +13 -1
  7. package/dist/{bridge.js → bridge/bridge.js} +95 -15
  8. package/dist/{bridge.test-harness.d.ts → bridge/bridge.test-harness.d.ts} +9 -11
  9. package/dist/{bridge.test-harness.js → bridge/bridge.test-harness.js} +18 -10
  10. package/dist/{pairing-token.js → bridge/pairing-token.js} +1 -1
  11. package/dist/capsule/capsule-store.d.ts +8 -3
  12. package/dist/capsule/capsule-store.js +7 -2
  13. package/dist/capsule/capsule.js +3 -3
  14. package/dist/capsule/causal-summary.d.ts +8 -0
  15. package/dist/capsule/causal-summary.js +22 -12
  16. package/dist/capsule/divergence.d.ts +9 -4
  17. package/dist/capsule/divergence.js +8 -8
  18. package/dist/capsule/predicate-to-links.js +11 -8
  19. package/dist/cli/cli-doctor.d.ts +1 -0
  20. package/dist/cli/cli-doctor.js +135 -0
  21. package/dist/{cli-flow-commands.d.ts → cli/cli-flow-commands.d.ts} +2 -2
  22. package/dist/{cli-flow-commands.js → cli/cli-flow-commands.js} +15 -15
  23. package/dist/{cli-launch.d.ts → cli/cli-launch.d.ts} +32 -3
  24. package/dist/cli/cli-launch.js +171 -0
  25. package/dist/cli/cli-parse-feedback.d.ts +31 -0
  26. package/dist/cli/cli-parse-feedback.js +56 -0
  27. package/dist/{cli-parse.d.ts → cli/cli-parse.d.ts} +6 -19
  28. package/dist/{cli-parse.js → cli/cli-parse.js} +118 -81
  29. package/dist/{cli-port.d.ts → cli/cli-port.d.ts} +12 -0
  30. package/dist/{cli-port.js → cli/cli-port.js} +9 -9
  31. package/dist/cli/cli-update-commands.d.ts +4 -0
  32. package/dist/cli/cli-update-commands.js +93 -0
  33. package/dist/{cli-verify.js → cli/cli-verify.js} +14 -14
  34. package/dist/{cloud-cli.js → cli/cloud-cli.js} +29 -24
  35. package/dist/cli/doctor-daemon-line.d.ts +37 -0
  36. package/dist/cli/doctor-daemon-line.js +35 -0
  37. package/dist/cli/port-holder.d.ts +43 -0
  38. package/dist/cli/port-holder.js +99 -0
  39. package/dist/cli.d.ts +2 -2
  40. package/dist/cli.js +302 -100
  41. package/dist/cloud/cloud-config.d.ts +0 -1
  42. package/dist/cloud/cloud-config.js +9 -9
  43. package/dist/cloud/cloud-sync.d.ts +1 -7
  44. package/dist/cloud/cloud-sync.js +12 -12
  45. package/dist/crawl/crawl-empty.d.ts +16 -0
  46. package/dist/crawl/crawl-empty.js +24 -0
  47. package/dist/crawl/crawl-tools.js +4 -1
  48. package/dist/crawl/crawl.js +26 -7
  49. package/dist/daemon/daemon-resilience.d.ts +73 -0
  50. package/dist/daemon/daemon-resilience.js +279 -0
  51. package/dist/daemon/daemon-usefulness.d.ts +61 -0
  52. package/dist/daemon/daemon-usefulness.js +62 -0
  53. package/dist/{daemon.d.ts → daemon/daemon.d.ts} +33 -8
  54. package/dist/{daemon.js → daemon/daemon.js} +105 -48
  55. package/dist/daemon/heartbeat.d.ts +115 -0
  56. package/dist/daemon/heartbeat.js +141 -0
  57. package/dist/daemon/idle-grace.d.ts +32 -0
  58. package/dist/daemon/idle-grace.js +41 -0
  59. package/dist/{idle-shutdown.d.ts → daemon/idle-shutdown.d.ts} +23 -2
  60. package/dist/{idle-shutdown.js → daemon/idle-shutdown.js} +37 -6
  61. package/dist/daemon/port-presence.d.ts +76 -0
  62. package/dist/daemon/port-presence.js +93 -0
  63. package/dist/daemon/state-dir.d.ts +7 -0
  64. package/dist/daemon/state-dir.js +59 -0
  65. package/dist/daemon/unclean-predecessor.d.ts +23 -0
  66. package/dist/daemon/unclean-predecessor.js +27 -0
  67. package/dist/domain/domain-model.js +2 -2
  68. package/dist/events/body-failures.js +8 -8
  69. package/dist/events/contradictions.d.ts +1 -1
  70. package/dist/events/contradictions.js +60 -10
  71. package/dist/events/echo-mismatch.js +10 -10
  72. package/dist/events/event-filters.js +6 -6
  73. package/dist/events/json-salvage.d.ts +2 -1
  74. package/dist/events/json-salvage.js +6 -6
  75. package/dist/events/observed-in-window.d.ts +12 -0
  76. package/dist/events/observed-in-window.js +21 -0
  77. package/dist/events/predicate-eval.d.ts +53 -20
  78. package/dist/events/predicate-eval.js +179 -55
  79. package/dist/events/predicate-parse.d.ts +23 -0
  80. package/dist/events/predicate-parse.js +54 -0
  81. package/dist/events/predicate.d.ts +7 -2
  82. package/dist/events/predicate.js +126 -35
  83. package/dist/events/reconcile.js +5 -5
  84. package/dist/events/ring-buffer.js +2 -17
  85. package/dist/events/stale-response.js +6 -6
  86. package/dist/events/testid-near-miss.d.ts +22 -0
  87. package/dist/events/testid-near-miss.js +33 -0
  88. package/dist/events/unit-mismatch.js +8 -8
  89. package/dist/flows/affected-tools.js +19 -4
  90. package/dist/flows/affected.js +1 -1
  91. package/dist/flows/annotate-target.d.ts +1 -0
  92. package/dist/flows/annotate-target.js +20 -0
  93. package/dist/flows/annotate-tools.js +25 -3
  94. package/dist/flows/annotate.js +17 -4
  95. package/dist/flows/assertion-integrity.d.ts +2 -1
  96. package/dist/flows/assertion-tiers-store.d.ts +3 -66
  97. package/dist/flows/assertion-tiers-store.js +2 -2
  98. package/dist/flows/attributed-failure.d.ts +17 -0
  99. package/dist/flows/attributed-failure.js +20 -0
  100. package/dist/flows/buddy-status.d.ts +2 -1
  101. package/dist/flows/buddy-status.js +2 -2
  102. package/dist/flows/coverage.d.ts +3 -2
  103. package/dist/flows/coverage.js +2 -2
  104. package/dist/flows/decision.js +24 -9
  105. package/dist/flows/empty-flow.d.ts +6 -0
  106. package/dist/flows/empty-flow.js +24 -0
  107. package/dist/flows/flake-store.d.ts +0 -1
  108. package/dist/flows/flake-store.js +1 -1
  109. package/dist/flows/flake.d.ts +2 -1
  110. package/dist/flows/flake.js +2 -2
  111. package/dist/flows/flow-classify.js +12 -0
  112. package/dist/flows/flow-replay-run.js +4 -4
  113. package/dist/flows/flow-replay.d.ts +6 -8
  114. package/dist/flows/flow-replay.js +132 -48
  115. package/dist/flows/flow-report.js +1 -1
  116. package/dist/flows/flow-scope.d.ts +1 -6
  117. package/dist/flows/flow-step-runners.d.ts +26 -6
  118. package/dist/flows/flow-step-runners.js +156 -6
  119. package/dist/flows/flow-success.js +20 -15
  120. package/dist/flows/flow-tools.js +63 -21
  121. package/dist/flows/flows.js +15 -11
  122. package/dist/flows/gate.d.ts +4 -3
  123. package/dist/flows/gate.js +1 -1
  124. package/dist/flows/heal-run.d.ts +0 -8
  125. package/dist/flows/heal-run.js +5 -5
  126. package/dist/flows/heal.js +3 -3
  127. package/dist/flows/predicate-to-expect.d.ts +38 -0
  128. package/dist/flows/predicate-to-expect.js +95 -0
  129. package/dist/flows/replay.d.ts +16 -1
  130. package/dist/flows/replay.js +58 -22
  131. package/dist/flows/role-anchor-nearest.d.ts +6 -0
  132. package/dist/flows/role-anchor-nearest.js +45 -0
  133. package/dist/flows/role-drift-reason.d.ts +20 -0
  134. package/dist/flows/role-drift-reason.js +39 -0
  135. package/dist/flows/server-verify.d.ts +1 -1
  136. package/dist/flows/server-verify.js +5 -5
  137. package/dist/flows/suite-flakes.d.ts +33 -0
  138. package/dist/flows/suite-flakes.js +32 -0
  139. package/dist/flows/verify-change-tools.js +32 -10
  140. package/dist/flows/verify-change-verdict.d.ts +15 -0
  141. package/dist/flows/verify-change-verdict.js +25 -0
  142. package/dist/flows/watch-batcher.d.ts +3 -2
  143. package/dist/honesty/already-true.d.ts +2 -0
  144. package/dist/honesty/already-true.js +31 -0
  145. package/dist/honesty/blind-spots.d.ts +35 -3
  146. package/dist/honesty/blind-spots.js +57 -12
  147. package/dist/honesty/honesty.d.ts +3 -2
  148. package/dist/honesty/honesty.js +4 -4
  149. package/dist/honesty/uncaptured-bodies.js +1 -1
  150. package/dist/honesty/unread-outcome.js +2 -2
  151. package/dist/honesty/verified.d.ts +31 -3
  152. package/dist/honesty/verified.js +64 -8
  153. package/dist/http-server.js +23 -7
  154. package/dist/hunt/hunt-report.d.ts +2 -1
  155. package/dist/hunt/hunt-report.js +2 -2
  156. package/dist/index.d.ts +9 -6
  157. package/dist/index.js +41 -25
  158. package/dist/init/agent-rules.d.ts +20 -1
  159. package/dist/init/agent-rules.js +64 -8
  160. package/dist/init/app-choice.d.ts +20 -0
  161. package/dist/init/app-choice.js +25 -0
  162. package/dist/init/astro-host.d.ts +21 -0
  163. package/dist/init/astro-host.js +27 -0
  164. package/dist/init/astro-patch.d.ts +16 -0
  165. package/dist/init/astro-patch.js +240 -0
  166. package/dist/init/capabilities.js +4 -1
  167. package/dist/init/cra.d.ts +50 -0
  168. package/dist/init/cra.js +106 -0
  169. package/dist/init/cursor.js +39 -4
  170. package/dist/init/desktop-doctor.d.ts +3 -2
  171. package/dist/init/desktop-doctor.js +4 -4
  172. package/dist/init/detect.d.ts +9 -2
  173. package/dist/init/detect.js +24 -5
  174. package/dist/init/mcp-clients.d.ts +81 -0
  175. package/dist/init/mcp-clients.js +248 -0
  176. package/dist/init/mcp-registered.d.ts +12 -0
  177. package/dist/init/mcp-registered.js +14 -0
  178. package/dist/init/mcp.d.ts +27 -2
  179. package/dist/init/mcp.js +40 -4
  180. package/dist/init/next-patch.d.ts +2 -4
  181. package/dist/init/next-patch.js +109 -19
  182. package/dist/init/node-io.js +29 -6
  183. package/dist/init/plan-framework.d.ts +16 -1
  184. package/dist/init/plan-framework.js +166 -8
  185. package/dist/init/plan.d.ts +85 -6
  186. package/dist/init/plan.js +196 -33
  187. package/dist/init/project-id.d.ts +5 -9
  188. package/dist/init/project-id.js +10 -22
  189. package/dist/init/run.d.ts +23 -1
  190. package/dist/init/run.js +338 -47
  191. package/dist/init/slash-command.d.ts +9 -0
  192. package/dist/init/slash-command.js +10 -1
  193. package/dist/init/snippets.d.ts +7 -10
  194. package/dist/init/snippets.js +44 -15
  195. package/dist/init/vite-config.js +1 -1
  196. package/dist/init/windows-quote.d.ts +38 -0
  197. package/dist/init/windows-quote.js +70 -0
  198. package/dist/init/workspace-apps.d.ts +23 -0
  199. package/dist/init/workspace-apps.js +68 -0
  200. package/dist/input/driven-redaction.js +1 -1
  201. package/dist/input/network-detail.js +8 -8
  202. package/dist/input/network-mock-tools.js +2 -2
  203. package/dist/input/network-mock.js +4 -4
  204. package/dist/input/real-input.js +8 -3
  205. package/dist/input/scroll-find.js +2 -2
  206. package/dist/input/scroll-tools.d.ts +0 -5
  207. package/dist/input/scroll-tools.js +11 -4
  208. package/dist/input/viewport-tools.js +2 -2
  209. package/dist/journal/ambient-store.d.ts +0 -2
  210. package/dist/journal/ambient-store.js +1 -1
  211. package/dist/journal/ambient.js +1 -1
  212. package/dist/journal/attach-journal.d.ts +2 -1
  213. package/dist/journal/deviation-report.js +6 -6
  214. package/dist/journal/deviation-service.js +1 -1
  215. package/dist/journal/envelope-store.d.ts +0 -2
  216. package/dist/journal/envelope-store.js +1 -1
  217. package/dist/journal/envelope.d.ts +2 -3
  218. package/dist/journal/envelope.js +3 -3
  219. package/dist/journal/journal-query.js +1 -1
  220. package/dist/journal/journal-recorder.d.ts +2 -1
  221. package/dist/journal/journal-recorder.js +2 -2
  222. package/dist/journal/rollups.js +9 -9
  223. package/dist/journal/session-end.d.ts +2 -1
  224. package/dist/journal/session-journal.js +4 -4
  225. package/dist/license/license.d.ts +0 -14
  226. package/dist/license/license.js +8 -8
  227. package/dist/log.d.ts +10 -0
  228. package/dist/log.js +11 -1
  229. package/dist/mcp/mcp-is-error.d.ts +13 -0
  230. package/dist/mcp/mcp-is-error.js +18 -0
  231. package/dist/mcp/mcp-outage.d.ts +32 -0
  232. package/dist/mcp/mcp-outage.js +55 -0
  233. package/dist/mcp/mcp-proxy.d.ts +134 -0
  234. package/dist/mcp/mcp-proxy.js +720 -0
  235. package/dist/{mcp.d.ts → mcp/mcp.d.ts} +13 -10
  236. package/dist/{mcp.js → mcp/mcp.js} +139 -37
  237. package/dist/mcp/proxy-handshake.d.ts +3 -0
  238. package/dist/mcp/proxy-handshake.js +54 -0
  239. package/dist/mcp/proxy-lifecycle.d.ts +64 -0
  240. package/dist/mcp/proxy-lifecycle.js +70 -0
  241. package/dist/mcp/tool-catalog-cache.d.ts +29 -0
  242. package/dist/mcp/tool-catalog-cache.js +55 -0
  243. package/dist/oracles/propose-consequences.d.ts +2 -1
  244. package/dist/oracles/propose-consequences.js +16 -11
  245. package/dist/oracles/self-instrument.d.ts +2 -1
  246. package/dist/oracles/self-instrument.js +1 -1
  247. package/dist/phenomena/phenomena.js +3 -3
  248. package/dist/platform.d.ts +16 -0
  249. package/dist/platform.js +16 -0
  250. package/dist/pool/browser-pool.d.ts +9 -0
  251. package/dist/pool/browser-pool.js +38 -9
  252. package/dist/pool/playwright-launcher.d.ts +15 -2
  253. package/dist/pool/playwright-launcher.js +21 -2
  254. package/dist/project/fs-port.js +1 -1
  255. package/dist/project/project-tools.js +2 -2
  256. package/dist/runs/build-verification-run.js +1 -1
  257. package/dist/runs/repair-prompt.d.ts +0 -6
  258. package/dist/runs/repair-prompt.js +3 -3
  259. package/dist/runs/risk-classify.js +1 -1
  260. package/dist/runs/run-diff.d.ts +0 -2
  261. package/dist/runs/run-diff.js +3 -3
  262. package/dist/runs/run-tools.js +2 -2
  263. package/dist/runs/verification-sync.js +2 -2
  264. package/dist/runs/verify-http.js +1 -1
  265. package/dist/runs/verify-server.js +1 -1
  266. package/dist/session/attachment-history.d.ts +46 -0
  267. package/dist/session/attachment-history.js +65 -0
  268. package/dist/session/command-timeout.d.ts +11 -3
  269. package/dist/session/command-timeout.js +2 -2
  270. package/dist/session/control-envelope.d.ts +6 -0
  271. package/dist/session/control-envelope.js +7 -1
  272. package/dist/session/dev-server-probe.d.ts +13 -0
  273. package/dist/session/dev-server-probe.js +52 -0
  274. package/dist/session/last-act.d.ts +17 -3
  275. package/dist/session/last-act.js +16 -4
  276. package/dist/session/live-control-tools.js +7 -2
  277. package/dist/session/live-control.js +1 -1
  278. package/dist/session/looks-like-dev-server.d.ts +14 -0
  279. package/dist/session/looks-like-dev-server.js +25 -0
  280. package/dist/session/no-session-diagnosis.d.ts +45 -0
  281. package/dist/session/no-session-diagnosis.js +120 -0
  282. package/dist/session/no-session-watch.d.ts +21 -0
  283. package/dist/session/no-session-watch.js +69 -0
  284. package/dist/session/observed-state.d.ts +4 -0
  285. package/dist/session/observed-state.js +19 -1
  286. package/dist/session/output-budget.js +1 -1
  287. package/dist/session/pending-commands.js +1 -0
  288. package/dist/session/session-health.d.ts +8 -1
  289. package/dist/session/session-health.js +8 -7
  290. package/dist/session/session-info.d.ts +43 -8
  291. package/dist/session/session-info.js +46 -1
  292. package/dist/session/session-lease.d.ts +0 -11
  293. package/dist/session/session-lease.js +1 -1
  294. package/dist/session/session-manager.d.ts +6 -0
  295. package/dist/session/session-manager.js +124 -12
  296. package/dist/session/session-readiness.d.ts +1 -1
  297. package/dist/session/session-readiness.js +6 -1
  298. package/dist/session/session-reaper.js +1 -1
  299. package/dist/session/session-reconnect.d.ts +32 -0
  300. package/dist/session/session-reconnect.js +42 -0
  301. package/dist/session/session.d.ts +15 -2
  302. package/dist/session/session.js +77 -26
  303. package/dist/session/state-select.d.ts +1 -1
  304. package/dist/session/state-select.js +1 -1
  305. package/dist/status-payload.d.ts +19 -0
  306. package/dist/status-payload.js +11 -0
  307. package/dist/telemetry/argument-shape.js +10 -1
  308. package/dist/telemetry/browser-mode.d.ts +27 -0
  309. package/dist/telemetry/browser-mode.js +33 -0
  310. package/dist/telemetry/bug-found.d.ts +17 -21
  311. package/dist/telemetry/bug-found.js +120 -8
  312. package/dist/telemetry/cli-telemetry.d.ts +2 -12
  313. package/dist/telemetry/cli-telemetry.js +27 -4
  314. package/dist/telemetry/crash-cause.d.ts +47 -0
  315. package/dist/telemetry/crash-cause.js +104 -0
  316. package/dist/telemetry/daemon-telemetry.d.ts +38 -1
  317. package/dist/telemetry/daemon-telemetry.js +37 -12
  318. package/dist/telemetry/dev-repo.js +4 -4
  319. package/dist/telemetry/error-class.d.ts +26 -0
  320. package/dist/telemetry/error-class.js +52 -0
  321. package/dist/telemetry/error-fingerprint.js +21 -2
  322. package/dist/telemetry/feedback-cli.d.ts +8 -3
  323. package/dist/telemetry/feedback-cli.js +52 -8
  324. package/dist/telemetry/feedback-context.d.ts +23 -6
  325. package/dist/telemetry/feedback-context.js +105 -5
  326. package/dist/telemetry/feedback-delivery.d.ts +20 -0
  327. package/dist/telemetry/feedback-delivery.js +30 -0
  328. package/dist/telemetry/feedback-outbox.d.ts +22 -0
  329. package/dist/telemetry/feedback-outbox.js +87 -0
  330. package/dist/telemetry/feedback.d.ts +22 -1
  331. package/dist/telemetry/feedback.js +67 -8
  332. package/dist/telemetry/git-facts.d.ts +7 -0
  333. package/dist/telemetry/git-facts.js +5 -5
  334. package/dist/telemetry/project-profile.js +2 -1
  335. package/dist/telemetry/session-metrics.d.ts +47 -3
  336. package/dist/telemetry/session-metrics.js +315 -15
  337. package/dist/telemetry/telemetry.d.ts +45 -2
  338. package/dist/telemetry/telemetry.js +150 -37
  339. package/dist/telemetry/verification-of.d.ts +22 -0
  340. package/dist/telemetry/verification-of.js +84 -0
  341. package/dist/tools/act-capsule.js +2 -2
  342. package/dist/tools/act-danger.d.ts +12 -0
  343. package/dist/tools/act-danger.js +20 -3
  344. package/dist/tools/act-helpers.js +4 -4
  345. package/dist/tools/act-tools.d.ts +0 -8
  346. package/dist/tools/act-tools.js +114 -118
  347. package/dist/tools/act-view.js +7 -7
  348. package/dist/tools/alias-args.d.ts +17 -0
  349. package/dist/tools/alias-args.js +25 -0
  350. package/dist/tools/assert-grade.js +9 -9
  351. package/dist/tools/assert-verdict.d.ts +9 -1
  352. package/dist/tools/assert-verdict.js +21 -6
  353. package/dist/tools/browser-tools.js +36 -11
  354. package/dist/tools/contract-tools.d.ts +0 -5
  355. package/dist/tools/contract-tools.js +27 -1
  356. package/dist/tools/coverage-identity.d.ts +34 -0
  357. package/dist/tools/coverage-identity.js +46 -0
  358. package/dist/tools/coverage-tools.js +10 -14
  359. package/dist/tools/dynamic-tools.d.ts +2 -1
  360. package/dist/tools/dynamic-tools.js +96 -19
  361. package/dist/tools/error-recovery.d.ts +34 -2
  362. package/dist/tools/error-recovery.js +255 -4
  363. package/dist/tools/feedback-invite.d.ts +48 -0
  364. package/dist/tools/feedback-invite.js +71 -0
  365. package/dist/tools/feedback-tools.d.ts +12 -1
  366. package/dist/tools/feedback-tools.js +28 -5
  367. package/dist/tools/invoke-tool.d.ts +0 -12
  368. package/dist/tools/invoke-tool.js +126 -33
  369. package/dist/tools/lease-hint.d.ts +17 -0
  370. package/dist/tools/lease-hint.js +24 -0
  371. package/dist/tools/lease-tools.js +6 -5
  372. package/dist/tools/merge-tools.d.ts +10 -1
  373. package/dist/tools/merge-tools.js +9 -3
  374. package/dist/tools/merged-name-redirect.d.ts +13 -0
  375. package/dist/tools/merged-name-redirect.js +55 -0
  376. package/dist/tools/navigate-arrival.d.ts +26 -0
  377. package/dist/tools/navigate-arrival.js +60 -0
  378. package/dist/tools/navigate-result.d.ts +31 -0
  379. package/dist/tools/navigate-result.js +38 -0
  380. package/dist/tools/observe-tools.js +72 -20
  381. package/dist/tools/observed-nothing.d.ts +25 -0
  382. package/dist/tools/observed-nothing.js +33 -0
  383. package/dist/tools/query-paginate.js +2 -2
  384. package/dist/tools/query-shape.d.ts +18 -0
  385. package/dist/tools/query-shape.js +39 -0
  386. package/dist/tools/read-tools.js +81 -20
  387. package/dist/tools/real-input-attempt.d.ts +28 -0
  388. package/dist/tools/real-input-attempt.js +93 -0
  389. package/dist/tools/reconcile-tools.js +7 -7
  390. package/dist/tools/reload-result.d.ts +14 -0
  391. package/dist/tools/reload-result.js +29 -0
  392. package/dist/tools/snapshot-delta.d.ts +4 -5
  393. package/dist/tools/snapshot-delta.js +0 -0
  394. package/dist/tools/tool-kit.d.ts +16 -0
  395. package/dist/tools/tool-surface.d.ts +83 -0
  396. package/dist/tools/tool-surface.js +217 -0
  397. package/dist/tools/tools-helpers.js +6 -6
  398. package/dist/tools/tools.d.ts +15 -0
  399. package/dist/tools/tools.js +53 -17
  400. package/dist/tools/unadvertised-help.d.ts +5 -0
  401. package/dist/tools/unadvertised-help.js +39 -0
  402. package/dist/trace.d.ts +69 -0
  403. package/dist/trace.js +150 -0
  404. package/dist/update/nudge-credit.d.ts +4 -0
  405. package/dist/update/nudge-credit.js +53 -0
  406. package/dist/update/sdk-sync.d.ts +21 -0
  407. package/dist/update/sdk-sync.js +42 -0
  408. package/dist/update/update-checker.js +3 -3
  409. package/dist/update/update-nudge.d.ts +63 -6
  410. package/dist/update/update-nudge.js +128 -15
  411. package/dist/update/updater.d.ts +1 -9
  412. package/dist/update/updater.js +13 -8
  413. package/dist/version/peer-announce.d.ts +4 -0
  414. package/dist/version/peer-announce.js +30 -0
  415. package/dist/{server-version.js → version/server-version.js} +5 -1
  416. package/dist/version/version-nudge.d.ts +28 -0
  417. package/dist/version/version-nudge.js +40 -0
  418. package/dist/version/version-skew.d.ts +66 -0
  419. package/dist/version/version-skew.js +71 -0
  420. package/dist/visual/visual-diff.js +2 -2
  421. package/dist/visual/visual-tools.js +4 -4
  422. package/docs/README.md +55 -0
  423. package/docs/agent-cheatsheet.md +85 -0
  424. package/docs/architecture.md +113 -0
  425. package/docs/benchmarks.md +255 -0
  426. package/docs/debugging.md +102 -0
  427. package/docs/deploy-checks.md +69 -0
  428. package/docs/desktop-apps.md +256 -0
  429. package/docs/enterprise.md +62 -0
  430. package/docs/fixtures-dispatch-receiver.yml +76 -0
  431. package/docs/fixtures.md +45 -0
  432. package/docs/flows.md +166 -0
  433. package/docs/gate-plan.md +98 -0
  434. package/docs/gates.md +117 -0
  435. package/docs/getting-started.md +469 -0
  436. package/docs/human-control.md +63 -0
  437. package/docs/integration-patterns.md +171 -0
  438. package/docs/local-registry.md +126 -0
  439. package/docs/matrix/2.5.0/claude-code-machine.json +19 -0
  440. package/docs/matrix/2.5.0/codex-machine.json +20 -0
  441. package/docs/matrix/2.5.0/cursor-machine.json +22 -0
  442. package/docs/matrix/2.5.0/gemini-machine.json +22 -0
  443. package/docs/matrix/2.5.0/opencode-machine.json +22 -0
  444. package/docs/matrix/2.5.0/vscode-machine.json +22 -0
  445. package/docs/matrix/2.5.0/windsurf-machine.json +22 -0
  446. package/docs/matrix/MATRIX.md +26 -0
  447. package/docs/matrix/README.md +59 -0
  448. package/docs/mint.json +61 -0
  449. package/docs/multi-agent-testing.md +55 -0
  450. package/docs/platform-integration.md +122 -0
  451. package/docs/system-map.md +152 -0
  452. package/docs/telemetry-contract.md +214 -0
  453. package/docs/telemetry.md +191 -0
  454. package/docs/testing.md +82 -0
  455. package/docs/token-efficiency.md +80 -0
  456. package/docs/usage.md +1046 -0
  457. package/package.json +8 -5
  458. package/dist/cli-doctor.d.ts +0 -6
  459. package/dist/cli-doctor.js +0 -60
  460. package/dist/cli-launch.js +0 -112
  461. package/dist/daemon-resilience.d.ts +0 -22
  462. package/dist/daemon-resilience.js +0 -85
  463. package/dist/mcp-proxy.d.ts +0 -67
  464. package/dist/mcp-proxy.js +0 -337
  465. package/dist/tools/profiles.d.ts +0 -37
  466. package/dist/tools/profiles.js +0 -139
  467. /package/dist/{pairing-token.d.ts → bridge/pairing-token.d.ts} +0 -0
  468. /package/dist/{token-auth.d.ts → bridge/token-auth.d.ts} +0 -0
  469. /package/dist/{token-auth.js → bridge/token-auth.js} +0 -0
  470. /package/dist/{cli-verify.d.ts → cli/cli-verify.d.ts} +0 -0
  471. /package/dist/{cloud-cli.d.ts → cli/cloud-cli.d.ts} +0 -0
  472. /package/dist/{server-version.d.ts → version/server-version.d.ts} +0 -0
@@ -1,4 +1,5 @@
1
1
  import { z } from 'zod';
2
+ import { aliasParam } from '../tools/alias-args.js';
2
3
  import { AnnotationErrorCode, AnnotationSchema } from '@reticlehq/core';
3
4
  import { RECOVERY } from '../tools/error-recovery.js';
4
5
  /**
@@ -12,7 +13,7 @@ const UNKNOWN_KIND_RECOVERY = 'Annotations are structured, never free text. Pass
12
13
  import { ReticleTool } from '../tools/tool-names.js';
13
14
  import { asString } from '../tools/tools-helpers.js';
14
15
  import { compileAnnotation } from './annotate.js';
15
- const DEFAULT_RECORDING = 'default';
16
+ import { resolveAnnotateTarget } from './annotate-target.js';
16
17
  /**
17
18
  * The reticle_annotate tool. A STRUCTURED annotation (the AnnotationSchema
18
19
  * discriminated union) is compiled into the live recording's per-step expect / flow dynamic[] /
@@ -48,6 +49,12 @@ export const ANNOTATE_TOOLS = [
48
49
  inputSchema: {
49
50
  // `flow` selects the recording; `name`/`signal`/`testid`/`dataMatches` are the annotation fields.
50
51
  flow: z.string().optional().describe("Named recording to annotate. Defaults to 'default'."),
52
+ // Every other flow tool says `flowName`, and reticle_flow_save's own description sends the
53
+ // agent here — so the spelling it arrives with is the one this tool did not accept.
54
+ flowName: z
55
+ .string()
56
+ .optional()
57
+ .describe('Alias for `flow` (the name every other flow tool uses).'),
51
58
  kind: z
52
59
  .string()
53
60
  .describe('Annotation kind: assert-signal | assert-visible | assert-state | mark-dynamic | success-state | intent.'),
@@ -114,7 +121,10 @@ export const ANNOTATE_TOOLS = [
114
121
  code: z.string().optional(),
115
122
  },
116
123
  handler: (deps, args) => {
117
- const name = asString(args['flow']) ?? DEFAULT_RECORDING;
124
+ // The recording actually RUNNING when there is exactly one, not the literal name `default`.
125
+ // Reproduced on three apps: a named recording plus an unnamed annotate reported
126
+ // `annotate_no_step` while the steps sat in the recording the agent had started.
127
+ const name = resolveAnnotateTarget(asString(aliasParam(args, 'flow', ['flowName'])['flow']), deps.recordings.active());
118
128
  // Structured boundary: a free NL string / unknown kind fails the schema → UNKNOWN_KIND.
119
129
  const parsed = AnnotationSchema.safeParse(args);
120
130
  if (!parsed.success) {
@@ -134,8 +144,20 @@ export const ANNOTATE_TOOLS = [
134
144
  });
135
145
  }
136
146
  const outcome = compileAnnotation(parsed.data, stepCount ?? 0);
137
- if (!outcome.result.ok)
147
+ if (!outcome.result.ok) {
148
+ // Name what IS recording. `annotate_no_step` alone sent the agent off to record MORE steps —
149
+ // into the same empty recording — when the steps it already has are in another one.
150
+ const active = deps.recordings.active();
151
+ const elsewhere = active.filter((r) => r !== name && (deps.recordings.stepCount(r) ?? 0) > 0);
152
+ if (outcome.result.code === AnnotationErrorCode.NO_STEP_TO_ANNOTATE &&
153
+ elsewhere.length > 0) {
154
+ return Promise.resolve({
155
+ ...outcome.result,
156
+ recovery: `'${name}' has no steps yet, but ${elsewhere.map((r) => `'${r}'`).join(', ')} does. Pass \`flow\` to say which recording this belongs to, or drive an action first.`,
157
+ });
158
+ }
138
159
  return Promise.resolve(outcome.result);
160
+ }
139
161
  const patch = outcome.patch;
140
162
  if (patch !== undefined) {
141
163
  if (patch.dynamicAdd !== undefined)
@@ -16,7 +16,7 @@ import { AnnotationErrorCode, AnnotationKind, AnnotationTarget, COMPILED_PREDICA
16
16
  export function compileAnnotation(a, stepCount) {
17
17
  switch (a.kind) {
18
18
  case AnnotationKind.ASSERT_SIGNAL: {
19
- if (stepCount === 0)
19
+ if (0 === stepCount)
20
20
  return noStep();
21
21
  const expect = { signal: a.name };
22
22
  if (a.dataMatches !== undefined)
@@ -24,12 +24,12 @@ export function compileAnnotation(a, stepCount) {
24
24
  return stepPatch(a, stepCount, expect);
25
25
  }
26
26
  case AnnotationKind.ASSERT_VISIBLE: {
27
- if (stepCount === 0)
27
+ if (0 === stepCount)
28
28
  return noStep();
29
29
  return stepPatch(a, stepCount, { element: { testid: a.testid } });
30
30
  }
31
31
  case AnnotationKind.ASSERT_STATE: {
32
- if (stepCount === 0)
32
+ if (0 === stepCount)
33
33
  return noStep();
34
34
  const state = { path: a.statePath };
35
35
  if (a.store !== undefined)
@@ -38,6 +38,11 @@ export function compileAnnotation(a, stepCount) {
38
38
  state.equals = a.equals;
39
39
  return stepPatch(a, stepCount, { state });
40
40
  }
41
+ case AnnotationKind.ASSERT_NET: {
42
+ if (0 === stepCount)
43
+ return noStep();
44
+ return stepPatch(a, stepCount, { net: a.net });
45
+ }
41
46
  case AnnotationKind.MARK_DYNAMIC:
42
47
  return {
43
48
  result: { ok: true, target: AnnotationTarget.FLOW, compiled: describeCompiled(a) },
@@ -109,6 +114,14 @@ export function describeCompiled(a) {
109
114
  return `${COMPILED_PREDICATE_PREFIX} assert ${a.testid} visible`;
110
115
  case AnnotationKind.ASSERT_STATE:
111
116
  return `${COMPILED_PREDICATE_PREFIX} assert state ${a.statePath}${a.equals !== undefined ? ` == ${JSON.stringify(a.equals)}` : ''}`;
117
+ case AnnotationKind.ASSERT_NET: {
118
+ // Say the COUNT out loud when there is one. "will assert POST /refund" and "will assert
119
+ // exactly 1x POST /refund" are different gates, and the confirmation is the only place the
120
+ // author sees which one they just attached.
121
+ const what = `${a.net.method ?? 'any'} ${a.net.urlContains ?? '(any url)'}`;
122
+ const times = a.net.count === undefined ? '' : ` exactly ${String(a.net.count)}x`;
123
+ return `${COMPILED_PREDICATE_PREFIX} assert${times} request ${what}`;
124
+ }
112
125
  case AnnotationKind.MARK_DYNAMIC:
113
126
  return `${COMPILED_PREDICATE_PREFIX} ignore ${a.testid} (dynamic)`;
114
127
  case AnnotationKind.SUCCESS_STATE:
@@ -124,7 +137,7 @@ export function describeCompiled(a) {
124
137
  }
125
138
  if (a.console !== undefined) {
126
139
  const level = a.console.level ?? 'error';
127
- return `${COMPILED_PREDICATE_PREFIX} succeed when ${a.console.absent === true ? `no console.${level}` : `console.${level}`}`;
140
+ return `${COMPILED_PREDICATE_PREFIX} succeed when ${true === a.console.absent ? `no console.${level}` : `console.${level}`}`;
128
141
  }
129
142
  return `${COMPILED_PREDICATE_PREFIX} succeed when ${a.testid ?? ''} visible`;
130
143
  case AnnotationKind.INTENT:
@@ -12,7 +12,7 @@ export interface StepExpect {
12
12
  step: number;
13
13
  expect?: FlowExpect;
14
14
  }
15
- export interface DowngradeFinding {
15
+ interface DowngradeFinding {
16
16
  step: number;
17
17
  }
18
18
  /**
@@ -21,3 +21,4 @@ export interface DowngradeFinding {
21
21
  * lives with the affected index.
22
22
  */
23
23
  export declare function detectDowngrades(before: readonly StepExpect[], after: readonly StepExpect[]): DowngradeFinding[];
24
+ export {};
@@ -1,74 +1,10 @@
1
- import { z } from 'zod';
2
1
  import type { FileSystemPort } from '../project/fs-port.js';
3
2
  import type { StepExpect } from './assertion-integrity.js';
4
- /**
5
- * The anti-reward-hacking baseline. Detecting an assertion DOWNGRADE requires knowing what the
6
- * flow asserted the last time it PASSED — otherwise a weakened flow simply looks like a flow that always
7
- * asserted little. Flows are git-checked and agent-editable, so we cannot trust the file itself as its own
8
- * history.
9
- *
10
- * So: on every passing replay we record that flow's per-step assertion shape here, and the gate diffs the
11
- * current file against it. Recorded only on PASS, deliberately — a failing run must never become the
12
- * baseline a later weakening is measured against.
13
- *
14
- * Loads never throw: a missing/corrupt ledger degrades to "no baseline", which means no downgrade can be
15
- * claimed. Failing OPEN is right here — accusing an agent of gaming the gate on the strength of an
16
- * unreadable file would be worse than missing one downgrade.
17
- */
18
- export declare const AssertionTiersFileSchema: z.ZodObject<{
19
- version: z.ZodLiteral<1>;
20
- flows: z.ZodRecord<z.ZodString, z.ZodObject<{
21
- steps: z.ZodArray<z.ZodObject<{
22
- step: z.ZodNumber;
23
- expect: z.ZodOptional<z.ZodUnknown>;
24
- }, "strip", z.ZodTypeAny, {
25
- step: number;
26
- expect?: unknown;
27
- }, {
28
- step: number;
29
- expect?: unknown;
30
- }>, "many">;
31
- /** Source files this flow covered when it last passed — lets the gate scope DELETION findings to
32
- * the files that actually changed, instead of flagging every intentionally-removed flow. */
33
- sources: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
34
- }, "strip", z.ZodTypeAny, {
35
- steps: {
36
- step: number;
37
- expect?: unknown;
38
- }[];
39
- sources: string[];
40
- }, {
41
- steps: {
42
- step: number;
43
- expect?: unknown;
44
- }[];
45
- sources?: string[] | undefined;
46
- }>>;
47
- }, "strip", z.ZodTypeAny, {
48
- version: 1;
49
- flows: Record<string, {
50
- steps: {
51
- step: number;
52
- expect?: unknown;
53
- }[];
54
- sources: string[];
55
- }>;
56
- }, {
57
- version: 1;
58
- flows: Record<string, {
59
- steps: {
60
- step: number;
61
- expect?: unknown;
62
- }[];
63
- sources?: string[] | undefined;
64
- }>;
65
- }>;
66
- export interface FlowBaseline {
3
+ interface FlowBaseline {
67
4
  steps: StepExpect[];
68
5
  sources: string[];
69
6
  }
70
- export type FlowTiers = Record<string, FlowBaseline>;
71
- export declare const ASSERTION_TIERS_VERSION = 1;
7
+ type FlowTiers = Record<string, FlowBaseline>;
72
8
  export declare class AssertionTiersStore {
73
9
  #private;
74
10
  constructor(fs: FileSystemPort, root: string);
@@ -77,3 +13,4 @@ export declare class AssertionTiersStore {
77
13
  /** Record one flow's assertion shape + covered sources after it PASSED. Never breaks a replay. */
78
14
  recordPassing(name: string, steps: readonly StepExpect[], sources?: readonly string[]): Promise<void>;
79
15
  }
16
+ export {};
@@ -16,7 +16,7 @@ import { withFileLock } from '../project/file-lock.js';
16
16
  * claimed. Failing OPEN is right here — accusing an agent of gaming the gate on the strength of an
17
17
  * unreadable file would be worse than missing one downgrade.
18
18
  */
19
- export const AssertionTiersFileSchema = z.object({
19
+ const AssertionTiersFileSchema = z.object({
20
20
  version: z.literal(1),
21
21
  flows: z.record(z.object({
22
22
  steps: z.array(z.object({ step: z.number(), expect: z.unknown().optional() })),
@@ -25,7 +25,7 @@ export const AssertionTiersFileSchema = z.object({
25
25
  sources: z.array(z.string()).default([]),
26
26
  })),
27
27
  });
28
- export const ASSERTION_TIERS_VERSION = 1;
28
+ const ASSERTION_TIERS_VERSION = 1;
29
29
  export class AssertionTiersStore {
30
30
  #fs;
31
31
  #path;
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Which failing flows can support a verdict ABOUT THE CHANGED FILES.
3
+ *
4
+ * `affectedSavedFlows` deliberately re-runs any flow whose covered sources it cannot determine —
5
+ * over-running beats silently skipping — and reports those names in `unknownProvenance`. The verdict
6
+ * then ignored that and answered NO whenever anything failed, producing the observed
7
+ *
8
+ * verified: "no", because "1 of 1 covering flows failed
9
+ * (1 of them re-run only because Reticle cannot tell which sources they cover)"
10
+ *
11
+ * — a negative claim about `src/App.tsx` whose own explanation admits the evidence is not tied to it.
12
+ *
13
+ * This tool already refuses the mirror-image error: an uncovered change is UNKNOWN, never a green,
14
+ * because "nothing ran, so nothing was proved". A red nothing earned is the same mistake reversed.
15
+ */
16
+ /** Failing flows minus the ones that only ran because their provenance is unknown. */
17
+ export declare function attributedFailures(failingFlows: readonly string[], unknownProvenance: readonly string[]): string[];
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Which failing flows can support a verdict ABOUT THE CHANGED FILES.
3
+ *
4
+ * `affectedSavedFlows` deliberately re-runs any flow whose covered sources it cannot determine —
5
+ * over-running beats silently skipping — and reports those names in `unknownProvenance`. The verdict
6
+ * then ignored that and answered NO whenever anything failed, producing the observed
7
+ *
8
+ * verified: "no", because "1 of 1 covering flows failed
9
+ * (1 of them re-run only because Reticle cannot tell which sources they cover)"
10
+ *
11
+ * — a negative claim about `src/App.tsx` whose own explanation admits the evidence is not tied to it.
12
+ *
13
+ * This tool already refuses the mirror-image error: an uncovered change is UNKNOWN, never a green,
14
+ * because "nothing ran, so nothing was proved". A red nothing earned is the same mistake reversed.
15
+ */
16
+ /** Failing flows minus the ones that only ran because their provenance is unknown. */
17
+ export function attributedFailures(failingFlows, unknownProvenance) {
18
+ const unattributable = new Set(unknownProvenance);
19
+ return failingFlows.filter((flow) => !unattributable.has(flow));
20
+ }
@@ -6,7 +6,7 @@
6
6
  * nominal" needs no detail; a deviation needs its name, because a status line that says only "1 deviation"
7
7
  * forces the human to go digging, which is exactly the work the buddy exists to remove.
8
8
  */
9
- export interface BuddyStatusInput {
9
+ interface BuddyStatusInput {
10
10
  /** Every saved flow. */
11
11
  total: number;
12
12
  /** Flows with a passing artifact. */
@@ -22,3 +22,4 @@ export interface BuddyStatusInput {
22
22
  * `✗ 2 deviations: checkout, billing · 39 nominal · 1 flaky quarantined`
23
23
  */
24
24
  export declare function formatBuddyStatus(input: BuddyStatusInput): string;
25
+ export {};
@@ -16,13 +16,13 @@ const MAX_NAMED = 2;
16
16
  export function formatBuddyStatus(input) {
17
17
  const parts = [];
18
18
  const deviations = input.deviations.length;
19
- if (deviations === 0) {
19
+ if (0 === deviations) {
20
20
  parts.push(`✓ ${String(input.passing)}/${String(input.total)} flows nominal`);
21
21
  }
22
22
  else {
23
23
  const named = input.deviations.slice(0, MAX_NAMED).join(', ');
24
24
  const rest = deviations - Math.min(MAX_NAMED, deviations);
25
- parts.push(`✗ ${String(deviations)} deviation${deviations === 1 ? '' : 's'}: ${named}${rest > 0 ? ` +${String(rest)} more` : ''}`);
25
+ parts.push(`✗ ${String(deviations)} deviation${1 === deviations ? '' : 's'}: ${named}${rest > 0 ? ` +${String(rest)} more` : ''}`);
26
26
  parts.push(`${String(input.passing)} nominal`);
27
27
  }
28
28
  if (input.quarantined.length > 0) {
@@ -4,12 +4,12 @@
4
4
  * (contract testids/signals/flows) actually touched by passing verification, so "everything" is honest
5
5
  * about what everything means. Pure set math; the deviation report + gate carry the number.
6
6
  */
7
- export interface DeclaredSurface {
7
+ interface DeclaredSurface {
8
8
  testids: readonly string[];
9
9
  signals: readonly string[];
10
10
  flows: readonly string[];
11
11
  }
12
- export interface CoverageDimension {
12
+ interface CoverageDimension {
13
13
  total: number;
14
14
  covered: number;
15
15
  /** Percent covered, rounded; 100 when nothing is declared (vacuously complete). */
@@ -25,3 +25,4 @@ export interface Coverage {
25
25
  overallPct: number;
26
26
  }
27
27
  export declare function computeCoverage(declared: DeclaredSurface, exercised: DeclaredSurface): Coverage;
28
+ export {};
@@ -7,7 +7,7 @@
7
7
  function dimension(declared, exercised) {
8
8
  const uncovered = declared.filter((d) => !exercised.has(d));
9
9
  const covered = declared.length - uncovered.length;
10
- const pct = declared.length === 0 ? 100 : Math.round((covered / declared.length) * 100);
10
+ const pct = 0 === declared.length ? 100 : Math.round((covered / declared.length) * 100);
11
11
  return { total: declared.length, covered, pct, uncovered };
12
12
  }
13
13
  export function computeCoverage(declared, exercised) {
@@ -16,6 +16,6 @@ export function computeCoverage(declared, exercised) {
16
16
  const flows = dimension(declared.flows, new Set(exercised.flows));
17
17
  const total = testids.total + signals.total + flows.total;
18
18
  const covered = testids.covered + signals.covered + flows.covered;
19
- const overallPct = total === 0 ? 100 : Math.round((covered / total) * 100);
19
+ const overallPct = 0 === total ? 100 : Math.round((covered / total) * 100);
20
20
  return { testids, signals, flows, overallPct };
21
21
  }
@@ -35,7 +35,7 @@ export function buildDecision(result, flow) {
35
35
  // Green — but is it green-for-the-right-reason? A flow that asserts no consequence can pass while
36
36
  // broken, so the honest next action is to add one.
37
37
  const grade = flow !== undefined ? classifyFlowAssertions(flow) : undefined;
38
- const verifiesOutcome = grade?.hasConsequenceAssertion === true;
38
+ const verifiesOutcome = true === grade?.hasConsequenceAssertion;
39
39
  const intent = flow?.intent;
40
40
  return {
41
41
  verdict: 'pass',
@@ -103,7 +103,7 @@ function suiteVerdictOf(status) {
103
103
  function unverifiableReason(flow) {
104
104
  if (flow === undefined)
105
105
  return undefined;
106
- if (flow.steps.length === 0) {
106
+ if (0 === flow.steps.length) {
107
107
  return 'the flow has no steps — it replays green whatever the app does. Record it again, or add steps with reticle_annotate.';
108
108
  }
109
109
  const c = classifyFlowAssertions(flow);
@@ -129,6 +129,14 @@ export function buildSuiteVerdict(runs) {
129
129
  }
130
130
  const decision = replay.decision ?? buildDecision(replay, flow);
131
131
  const row = { flow: replay.name, verdict: suiteVerdictOf(replay.status) };
132
+ // Zero step results PLUS an error is the signature of a replay that never STARTED — the flow file
133
+ // failed to load (before the session is even resolved), or its leased context never came up. A
134
+ // flow that ran and failed always carries its failing step; a flow with no steps replays OK and
135
+ // never reaches here. Both arrive as ReplayStatus.ERROR, so without this the suite cannot tell
136
+ // "the app broke" from "Reticle could not run this" — and the bug metric reported the second as
137
+ // the first, 8 times in one sweep.
138
+ if (0 === replay.steps.length && replay.error !== undefined)
139
+ row.couldNotRun = true;
132
140
  if (decision.whatChanged !== undefined)
133
141
  row.whatChanged = decision.whatChanged;
134
142
  if (decision.whereInSource !== undefined)
@@ -139,15 +147,22 @@ export function buildSuiteVerdict(runs) {
139
147
  const total = runs.length;
140
148
  const failed = failures.length;
141
149
  // A real failure outranks an unverifiable flow: a broken flow is worse news than an empty one.
142
- const status = failed > 0 ? 'fail' : unverifiable.length > 0 ? 'unverifiable' : 'pass';
143
- const cannotFail = unverifiable.length === 0
150
+ // An EMPTY suite is unverifiable for the same reason a flow that asserts nothing is — it is the
151
+ // purest green that cannot go red. It used to report `pass` with the summary "all 0 flows pass",
152
+ // which meant `reticle verify` went green on every project that had not written a flow yet, and on
153
+ // any project where the flows directory failed to resolve. Found by the adversarial MCP sweep; it
154
+ // was the only invented answer in 994 calls.
155
+ const status = failed > 0 ? 'fail' : unverifiable.length > 0 || 0 === total ? 'unverifiable' : 'pass';
156
+ const cannotFail = 0 === unverifiable.length
144
157
  ? ''
145
158
  : ` — ${String(unverifiable.length)} verified nothing (${unverifiable.map((u) => u.flow).join(', ')})`;
146
- const summary = failed === 0
147
- ? unverifiable.length === 0
148
- ? `all ${total} flow${total === 1 ? '' : 's'} pass`
149
- : `${String(passed)}/${String(total)} flows verified${cannotFail}`
150
- : `${passed}/${total} flows pass — ${failed} need attention: ${failures.map((f) => f.flow).join(', ')}${cannotFail}`;
159
+ const summary = 0 === total
160
+ ? 'no flows to verify — nothing was checked. Record one with reticle_record { action: "start" }, then reticle_flow_save.'
161
+ : 0 === failed
162
+ ? 0 === unverifiable.length
163
+ ? `all ${total} flow${1 === total ? '' : 's'} pass`
164
+ : `${String(passed)}/${String(total)} flows verified${cannotFail}`
165
+ : `${passed}/${total} flows pass — ${failed} need attention: ${failures.map((f) => f.flow).join(', ')}${cannotFail}`;
151
166
  return {
152
167
  status,
153
168
  total,
@@ -0,0 +1,6 @@
1
+ interface EmptyFlowRefusal {
2
+ error: string;
3
+ recovery: string;
4
+ }
5
+ export declare function emptyFlowRefusal(stepCount: number, name: string): EmptyFlowRefusal | undefined;
6
+ export {};
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Refuse to save a flow with no steps.
3
+ *
4
+ * It used to succeed: `flow_save` returned `{ stepCount: 0, grade: "assertion-free" }` with no error,
5
+ * `flow_list` then showed one more flow, and `flow_verify` called it `unverifiable` — forever. The
6
+ * agent believes it saved a regression test; what it wrote is a permanent suite entry that can never
7
+ * go green or red.
8
+ *
9
+ * Zero steps is not a degraded flow, it is the absence of one. And the refusal matters most on the
10
+ * stacks where the recorder is known to capture nothing, because there the empty save is the only
11
+ * signal the agent would ever get that something went wrong.
12
+ */
13
+ import { ReticleTool } from '../tools/tool-names.js';
14
+ export function emptyFlowRefusal(stepCount, name) {
15
+ if (stepCount > 0)
16
+ return undefined;
17
+ return {
18
+ error: `'${name}' has no steps, so there is nothing to save — a flow with no steps can never pass or fail, and would sit in the suite reporting "unverifiable" forever`,
19
+ recovery: `Nothing was captured between ${ReticleTool.RECORD} { action: "start" } and { action: "stop" }. ` +
20
+ `Drive at least one action with ${ReticleTool.ACT} or ${ReticleTool.ACT_AND_WAIT} while the ` +
21
+ `recording is open, then save. If you DID act and this still says zero, the recorder captured ` +
22
+ `nothing for this stack — report it with ${ReticleTool.FEEDBACK}.`,
23
+ };
24
+ }
@@ -1,6 +1,5 @@
1
1
  import type { FileSystemPort } from '../project/fs-port.js';
2
2
  import { type FlakeLedger } from './flake.js';
3
- export declare const FLAKE_FILE_VERSION = 1;
4
3
  /**
5
4
  * Persists the per-flow flake ledger at `.reticle/flake.json`. Local single-project memory (stays OSS —
6
5
  * OSS-VS-SERVER). Loads never throw: missing/malformed/wrong-version degrades to an empty ledger, so
@@ -1,7 +1,7 @@
1
1
  import { dirname } from 'node:path';
2
2
  import { reticleDirPaths } from '../project/reticle-dir.js';
3
3
  import { FlakeFileSchema, emptyRecord, isFlaky, recordOutcome } from './flake.js';
4
- export const FLAKE_FILE_VERSION = 1;
4
+ const FLAKE_FILE_VERSION = 1;
5
5
  /**
6
6
  * Persists the per-flow flake ledger at `.reticle/flake.json`. Local single-project memory (stays OSS —
7
7
  * OSS-VS-SERVER). Loads never throw: missing/malformed/wrong-version degrades to an empty ledger, so
@@ -5,7 +5,7 @@ import { z } from 'zod';
5
5
  * (excluded from gate-blocking) and surfaced for repair, never silently blocking. The caller accrues an
6
6
  * outcome ONLY for unchanged-code replays (a fail after a code change is a legitimate red, not flake).
7
7
  */
8
- export declare const FlakeRecordSchema: z.ZodObject<{
8
+ declare const FlakeRecordSchema: z.ZodObject<{
9
9
  runs: z.ZodNumber;
10
10
  fails: z.ZodNumber;
11
11
  }, "strip", z.ZodTypeAny, {
@@ -55,3 +55,4 @@ export declare function recordOutcome(record: FlakeRecord, passed: boolean): Fla
55
55
  export declare function isFlaky(record: FlakeRecord, minRuns?: number): boolean;
56
56
  /** Observed intermittent-failure rate (0 when too few runs to judge). */
57
57
  export declare function flakeRate(record: FlakeRecord): number;
58
+ export {};
@@ -5,7 +5,7 @@ import { z } from 'zod';
5
5
  * (excluded from gate-blocking) and surfaced for repair, never silently blocking. The caller accrues an
6
6
  * outcome ONLY for unchanged-code replays (a fail after a code change is a legitimate red, not flake).
7
7
  */
8
- export const FlakeRecordSchema = z.object({
8
+ const FlakeRecordSchema = z.object({
9
9
  runs: z.number().int().min(0),
10
10
  fails: z.number().int().min(0),
11
11
  });
@@ -31,5 +31,5 @@ export function isFlaky(record, minRuns = DEFAULT_MIN_FLAKE_RUNS) {
31
31
  }
32
32
  /** Observed intermittent-failure rate (0 when too few runs to judge). */
33
33
  export function flakeRate(record) {
34
- return record.runs === 0 ? 0 : record.fails / record.runs;
34
+ return 0 === record.runs ? 0 : record.fails / record.runs;
35
35
  }
@@ -33,6 +33,18 @@ const INTENT_WITHOUT_OUTCOME_WARNING = 'This flow declares a business intent but
33
33
  // Consequence vs presence-only classification lives in @reticlehq/core (one source for all graders).
34
34
  const expectIsConsequence = flowExpectHasConsequence;
35
35
  const expectIsWeak = flowExpectIsPresenceOnly;
36
+ /**
37
+ * A step expect counts the same as a flow-level one, because replay now evaluates the same set.
38
+ *
39
+ * This was briefly narrowed to `state` only — correctly, at the time: replay checked element presence
40
+ * and state and nothing else, so grading a step signal/net as a consequence produced a flow that
41
+ * reported `grade: "asserted"` and could not go red. `assertStepExpect` closed that by compiling
42
+ * every step expect through the same `successToPredicate` the flow-level path uses, so the narrow
43
+ * rule is no longer true and would now UNDER-grade real assertions.
44
+ *
45
+ * The invariant to keep: this function and what replay enforces must move together. If one ever
46
+ * describes more than the other, the difference is a false green or a lost verification.
47
+ */
36
48
  /** Walk steps + act_sequence sub-steps so an expect on either level is counted. */
37
49
  function flattenSteps(steps) {
38
50
  const out = [];
@@ -64,7 +64,7 @@ async function recordReplayRun(deps, name, status, driftSteps, durationMs, proje
64
64
  });
65
65
  // Per-project cloud: push memory outcomes only when cloud is attached AND memory sync is enabled.
66
66
  const cloud = await resolveProjectCloud(deps.fs, deps.reticleRoot, homedir(), process.env);
67
- if (cloud.config === null || !cloud.policy.memory)
67
+ if (null === cloud.config || !cloud.policy.memory)
68
68
  return; // not attached / memory disabled → local only
69
69
  const result = await syncRunRecordToCloud({ kind: RunKind.FLOW_REPLAY, name, status: runStatus, at: deps.now(), durationMs }, projectId, cloud.config, (url, init) => fetch(url, init));
70
70
  if (result.outcome !== SyncOutcome.SYNCED) {
@@ -82,7 +82,7 @@ async function recordReplayRun(deps, name, status, driftSteps, durationMs, proje
82
82
  */
83
83
  export function startPathMismatchHint(flow, session) {
84
84
  const startPath = flow.startPath;
85
- if (startPath === undefined || startPath.length === 0)
85
+ if (startPath === undefined || 0 === startPath.length)
86
86
  return undefined;
87
87
  const routes = session.eventsSince(0).filter((e) => e.type === EventType.ROUTE_CHANGE);
88
88
  const last = routes.at(-1);
@@ -127,7 +127,7 @@ export async function replayNamedFlow(deps, args) {
127
127
  // Floor the success oracle at the start of THIS replay so a stale signal from a prior run
128
128
  // in the same session can't fake a pass.
129
129
  const replayFloor = session.elapsed();
130
- const steps = await replayFlow(session, loaded.value, waitForPredicate, FLOW_SIGNAL_TIMEOUT_MS, args['confirmDangerous'] === true);
130
+ const steps = await replayFlow(session, loaded.value, waitForPredicate, FLOW_SIGNAL_TIMEOUT_MS, true === args['confirmDangerous']);
131
131
  // "green means intent satisfied": when every step ran clean, assert the flow's success
132
132
  // end-condition as a real consequence. A signal/net success that never fires FAILS the replay
133
133
  // even though all locators resolved — the regression a healed-but-wrong locator ships green.
@@ -188,7 +188,7 @@ export async function replayNamedFlow(deps, args) {
188
188
  async function computeReplayDeviation(deps, session, floor) {
189
189
  try {
190
190
  const segments = computeSegments(session.eventsSince(floor));
191
- if (segments.length === 0)
191
+ if (0 === segments.length)
192
192
  return undefined;
193
193
  return await reportAndAccumulate(new EnvelopeStore(deps.fs, deps.reticleRoot), segments);
194
194
  }
@@ -1,4 +1,4 @@
1
- import { AnchorKind, type CommandResult, type FlowAnchor, type FlowFile, type FlowStepResult, type ReticleEvent, type QueryEmptyHint } from '@reticlehq/core';
1
+ import { AnchorKind, type CommandResult, type Drift, type FlowAnchor, type FlowFile, type FlowStepResult, type ReticleEvent, type QueryEmptyHint } from '@reticlehq/core';
2
2
  import type { EvalResult, Predicate } from '../events/predicate.js';
3
3
  /**
4
4
  * The session surface flow-replay needs: QUERY to re-resolve a testid anchor against the live
@@ -47,13 +47,9 @@ export type Sleep = (ms: number) => Promise<void>;
47
47
  * Such a drift is surfaced (with a nearest) but never auto-healed.
48
48
  */
49
49
  export declare function nearestIsAmbiguous(missing: string, present: string[]): boolean;
50
- /**
51
- * Re-resolve any QUERY against the live DOM, tolerating an in-flight render: QUERY, and while it
52
- * returns zero refs, sleep and retry up to ANCHOR_SETTLE_ATTEMPTS. Returns as soon as refs appear,
53
- * so a present anchor costs one query; a genuinely missing one costs the full (bounded) settle and
54
- * then drifts. The last result's near-miss hint is returned for the drift record.
55
- */
56
- export declare function resolveQuery(session: FlowReplaySession, queryArgs: Record<string, unknown>, sleep: Sleep): Promise<{
50
+ /** Build the legible-drift record for a testid anchor that resolved to zero live elements. */
51
+ export declare function testidDrift(value: string, hint: QueryEmptyHint | undefined): Drift;
52
+ export declare function resolveQuery(session: FlowReplaySession, queryArgs: Record<string, unknown>, sleep: Sleep, now?: () => number): Promise<{
57
53
  refs: string[];
58
54
  hint?: QueryEmptyHint;
59
55
  }>;
@@ -61,6 +57,8 @@ export declare function resolveQuery(session: FlowReplaySession, queryArgs: Reco
61
57
  export declare function componentLabel(anchor: Extract<FlowAnchor, {
62
58
  kind: typeof AnchorKind.COMPONENT;
63
59
  }>): string;
60
+ /** The value of a step's primary anchor, for labelling the result row. */
61
+ export declare function anchorLabel(anchor: FlowAnchor): string;
64
62
  /** QUERY args for a component auto-anchor — source (precise) + component name (coarse) as given. */
65
63
  export declare function componentQueryArgs(anchor: Extract<FlowAnchor, {
66
64
  kind: typeof AnchorKind.COMPONENT;