@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
package/docs/flows.md ADDED
@@ -0,0 +1,166 @@
1
+ # Flows, the recorder & self-healing — record once, run forever
2
+
3
+ Reticle turns an interactive run into a **git-checked, replayable program** stored under `.reticle/`. Flows are anchored on **meaning** (testid + signal), not volatile element refs or coordinates, so they survive refactors — and when an anchor does drift, Reticle tells you _why_ and can repair it. This is what makes Reticle "the project's living test suite a human seeds and an agent maintains."
4
+
5
+ > All of this is on-disk and human-readable, so flows are reviewed in PRs and diffed like code.
6
+
7
+ ## The `.reticle/` directory
8
+
9
+ When you record or save a contract, Reticle writes a git-checked workspace next to your app:
10
+
11
+ ```
12
+ .reticle/
13
+ contract.json # the app's testable surface (testids, signals, stores)
14
+ flows/
15
+ create-drop.json # a recorded, anchored, replayable flow
16
+ baselines/ # snapshot baselines (co-located)
17
+ ```
18
+
19
+ The server resolves `.reticle/` from the working directory it runs in (your project root). A fresh agent can read `.reticle/contract.json` to learn the testable surface **without grepping your source**.
20
+
21
+ ## The contract — advertise the testable surface
22
+
23
+ In your app, declare what's testable (see [Step 6 in Getting Started](getting-started.md)):
24
+
25
+ ```ts
26
+ import { registerCapabilities } from '@reticlehq/react';
27
+ registerCapabilities({
28
+ testids: ['add-task', 'checkout'],
29
+ signals: ['order:saved'],
30
+ stores: ['cart'],
31
+ });
32
+ ```
33
+
34
+ Then persist it to disk so it's committed and any agent can read it:
35
+
36
+ | Tool | What it does |
37
+ | --- | --- |
38
+ | `reticle_capabilities()` | the live testable surface `{ testids, signals, stores, flows }` |
39
+ | `reticle_contract_save()` | write the live capabilities to `.reticle/contract.json` (versioned, diffable) |
40
+
41
+ ## Create a flow
42
+
43
+ **(a) Agent-recorded** — the agent drives, then saves:
44
+
45
+ ```jsonc
46
+ reticle_record {action:"start"}({ recordingName: "create-task" })
47
+ reticle_act({ ref: "e7", action: "click" }) // … drive the golden path …
48
+ reticle_record {action:"stop"}({ recordingName: "create-task" })
49
+ reticle_flow_save({ flowName: "create-task" }) // → .reticle/flows/create-task.json
50
+ ```
51
+
52
+ **(b) Human-recorded (the recorder toolbar)** — with the presenter on (`present: true`), the floating panel hosts a recorder: a human clicks the golden path in the page and Reticle captures each interaction as a **semantic-anchored** step (testid, else role+name), then persists it via `reticle_flow_save_recorded`. The agent then runs and maintains it. _(First cut: structured annotations only — see below; free natural-language annotations are future work.)_
53
+
54
+ ### What a flow file looks like
55
+
56
+ ```jsonc
57
+ {
58
+ "version": 1,
59
+ "name": "create-task",
60
+ "steps": [
61
+ { "tool": "reticle_act", "anchor": { "testid": "add-task" }, "action": "click" },
62
+ {
63
+ "tool": "reticle_act",
64
+ "anchor": { "testid": "add-task" },
65
+ "action": "click",
66
+ "expect": { "signal": "task:added" },
67
+ },
68
+ ],
69
+ "dynamic": [], // anchors whose CONTENT must not be asserted (LLM output)
70
+ "success": { "signal": "saved" },
71
+ }
72
+ ```
73
+
74
+ 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. Only when none of those resolve is a step kept `degraded: true` (a last-resort "add a testid here" marker) rather than silently dropped.
75
+
76
+ ## Run a flow
77
+
78
+ ```jsonc
79
+ reticle_flow {action:"list"}() // → flows on disk
80
+ reticle_flow {action:"load"}({ flowName: "create-task" }) // → the flow JSON
81
+ reticle_flow_replay({ flowName: "create-task" }) // re-resolve each anchor against the LIVE DOM, run it
82
+ ```
83
+
84
+ **Watch it replay on the page.** When the presenter is on (`present: true`), a replay isn't silent — each step drives the real page, so the synthetic cursor flies to the element, the focus ring lands, and the activity log streams the journey live. You (or a teammate) literally watch the saved journey re-walk itself on your app, then see the verdict land. It's the fastest way to _see_ that a flow still works — not just read a green checkmark.
85
+
86
+ `reticle_flow_replay` returns a status:
87
+
88
+ - `ok` — every anchor resolved and every `expect` held.
89
+ - `drift` — an anchor missed (a testid was renamed, or a signal never fired). The result is **legible**: `{ step, anchor, drift: { reasonKind: "testid_not_found", nearest: "send-message" } }` — never a blind failure. (This is the "whose fault is it" principle.)
90
+ - `error` — the flow file is missing/invalid, or a resolved action failed. Runtime failures include the failed step and a top-level error envelope.
91
+
92
+ A testid-_preserving_ refactor (you moved markup but kept the testids) still replays green. A step whose element has **no testid** is anchored on its component + source location (`{ kind: "component", component, source: { file, line } }`) — an auto-derived stable anchor, so a flow records cleanly with zero hand-added testids and replay re-resolves it via `reticle_query by:'component'`.
93
+
94
+ ### The decision envelope — what to do next, not just pass/fail
95
+
96
+ On a `drift` or `error`, the replay result carries a `decision` an agent can act on directly:
97
+
98
+ ```jsonc
99
+ decision: {
100
+ verdict: "drift",
101
+ whatChanged: "testid \"fault-500\" not found",
102
+ whereInSource: "src/Diagnostics.tsx:16", // file:line, from the component/source anchor
103
+ suggestedFix: "rebind the anchor to \"fault-404\" (closest survivor)",
104
+ nextAction: "rebind the anchor to \"fault-404\", or update the flow if the change was intended."
105
+ }
106
+ ```
107
+
108
+ This is the feedback a human reviewer used to give — made machine-actionable, so the agent decides its next move without one.
109
+
110
+ ## Verify the whole suite in one call
111
+
112
+ `reticle_flow_verify` replays **every** saved flow (or a named subset) deterministically — no LLM per flow — and returns one consolidated verdict. This is the regression check to run after any change:
113
+
114
+ ```jsonc
115
+ reticle_flow_verify()
116
+ // → { status: "fail", total: 4, passed: 3, failed: 1,
117
+ // summary: "3/4 flows pass — 1 needs attention: ship-deploy",
118
+ // failures: [{ flow: "ship-deploy", verdict: "drift",
119
+ // whatChanged: "...", whereInSource: "src/...:NN", nextAction: "..." }] }
120
+ ```
121
+
122
+ Passing flows are counted; only failures carry detail (token-cheap). Build → `reticle_flow_verify` → fix from each failure's `nextAction` → repeat — the autonomous regression loop.
123
+
124
+ ## Self-healing — the agent maintains the flow
125
+
126
+ When a testid is renamed, the flow drifts. `reticle_flow_heal` proposes — and optionally applies — the nearest-match rebind, so flows don't rot:
127
+
128
+ ```jsonc
129
+ reticle_flow_heal({ flowName: "create-task" }) // PROPOSE only — never writes
130
+ // → { status: "drift", applied: false,
131
+ // proposals: [{ step: 0, from: "add-tassk", to: "add-task", confidence: 0.8 }] }
132
+
133
+ reticle_flow_heal({ flowName: "create-task", apply: true }) // rewrite the anchor on disk
134
+ // → { status: "healed", applied: true, proposals: [...] }
135
+ ```
136
+
137
+ With `apply: false` the flow file is **never modified** — you get the proposed diff to review. With `apply: true` Reticle rewrites the drifted anchor(s) to the confident nearest match and a subsequent replay passes. A drift with **no** confident nearest match leaves the file untouched.
138
+
139
+ ## Annotations (structured)
140
+
141
+ `reticle_annotate` attaches a structured annotation that compiles into the flow, so replay is a _checked_ re-run, not a blind macro:
142
+
143
+ - `assert-signal` / `assert-visible` → a step `expect` predicate (the invariant).
144
+ - `mark-dynamic` → a `flow.dynamic[]` entry — replay asserts the region's _presence_ but **not its words** (the LLM-output case: assert `caption:generated`, ignore the caption text).
145
+ - `success-state` → `flow.success` (the golden end condition). Pass `signal`/`testid`, or `statePath` (+ `store`, `equals`) to make the golden condition a **store-truth** assertion — the app's own source of truth, which no DOM read can reach (e.g. `statePath: "deployments.0.status", equals: "live"` fails the flow if a deploy only _looks_ shipped on screen). State assertions are graded as consequences, so they satisfy the business-outcome oracle.
146
+
147
+ ## Flows are your test suite
148
+
149
+ `.reticle/` flows can be executed as CI specs — replayed with their `expect`/`success` predicates, skipping `dynamic` regions — via `@reticlehq/test`'s `flowsAsSpecs`. See [Testing with Reticle](testing.md).
150
+
151
+ ## Tool reference
152
+
153
+ | Tool | Args | Returns |
154
+ | --- | --- | --- |
155
+ | `reticle_contract_save` | `{ sessionId? }` | writes `.reticle/contract.json` |
156
+ | `reticle_record {action:"start"}` / `reticle_record {action:"stop"}` | `{ recordingName }` | start/stop capturing the agent's acts |
157
+ | `reticle_flow_save` | `{ flowName }` | persist the recording → `.reticle/flows/<flowName>.json` |
158
+ | `reticle_flow_save_recorded` | `{ flowName? }` | persist a human-recorded (toolbar) flow |
159
+ | `reticle_flow {action:"list"}` | `{}` | flows on disk |
160
+ | `reticle_flow {action:"load"}` | `{ flowName }` | the flow JSON |
161
+ | `reticle_flow_replay` | `{ flowName }` | `{ status, steps, decision? }` (decision on drift/fail) |
162
+ | `reticle_flow_verify` | `{ names?, sessionId? }` | suite verdict `{ status, passed, failed, failures[] }` |
163
+ | `reticle_flow_heal` | `{ flowName, apply? }` | propose / apply nearest-match rebind |
164
+ | `reticle_annotate` | `{ kind, … }` | compile a structured annotation into the flow |
165
+
166
+ > Flow `name` must be a single safe path segment (no `/`, `\`, `..`, or leading dot).
@@ -0,0 +1,98 @@
1
+ # The release gate — plan and status
2
+
3
+ > What has to be true before a PR merges and before a release ships, why each piece exists, and what is built so far. Derived from [`system-map.md`](./system-map.md); the harness rules every tier obeys are in [`harness-rules.md`](../apps/e2e/harness-rules.md).
4
+
5
+ ## Two gates, not one
6
+
7
+ | | Merge gate | Release gate |
8
+ | ------- | ---------------------------- | ------------------------------------------------- |
9
+ | Answers | does this PR break anything? | does this version work in the clients people use? |
10
+ | Runs | every PR | on a release candidate |
11
+ | Who | machines only | machines + humans |
12
+ | Budget | minutes | hours |
13
+
14
+ **They must stay separate.** A merge that waits on a human client matrix stalls every contribution until three people with three editors happen to be awake.
15
+
16
+ ## Tiers
17
+
18
+ | Tier | Question | Who | When | Blocks |
19
+ | --- | --- | --- | --- | --- |
20
+ | 0 — repo | is the code internally consistent | Actions | every PR | merge |
21
+ | 1 — install | does it install into an app nobody instrumented | Actions, scaffolded apps | PRs touching install/wire | merge |
22
+ | 2 — ecosystem | does it survive third-party apps at real scale | `reticle-fixtures`, dispatch | 3×/week + release | release |
23
+ | 3 — conformance | does each MCP client actually work | humans + headless CLIs | release candidate | the tag |
24
+ | 4 — canary | does a real model still succeed | nightly, live LLM | nightly | nothing (alerts) |
25
+
26
+ **Tier 1 vs 2.** Tier 1 scaffolds a pristine app at CI time (`npm create vite`) — clean by construction, seconds to produce, catches install _regressions_, fast enough to block a PR. Tier 2 uses vendored production apps and catches install _complexity_. Conflating them produces a gate too slow to block and too shallow to trust.
27
+
28
+ **Tier 3 is the [CNCF conformance](https://github.com/cncf/k8s-conformance) model:** contributors do not _claim_ a client works, they run one command and submit its machine-generated output as a PR, which a bot validates before a human looks. Self-reported pass/fail cannot gate anything.
29
+
30
+ **No LLM in the merge path.** Deterministic tests on every commit; live-model evals nightly and alert-only. The `record → save → verify → heal` chain is what makes this possible — a flow is recorded once by an agent, then replayed forever with no model.
31
+
32
+ ## The matrix, factored
33
+
34
+ 3 OS × 6 clients × 8 frameworks × 2 runtimes = 288 combinations. Nobody runs that. Test each axis against a fixed baseline of the others instead: 3 + 6 + 8 + 2 = **19 runs, plus ~4 deliberately chosen full-stack combos** where axis interaction is most likely (Windows+Cursor+Next, macOS+Claude Code+Tauri, …). Publish the factoring, and say plainly which interactions are untested.
35
+
36
+ ## Phases
37
+
38
+ ### Phase 0 — the harness contract and the map — **done**
39
+
40
+ Nothing below is trustworthy until a gate result can be distinguished from a gate artifact.
41
+
42
+ - [x] `docs/system-map.md` — topology, connection sequence, tool graph, fragility inventory
43
+ - [x] `apps/e2e/harness-rules.md` — the four rules, with the incidents that produced each
44
+ - [x] `apps/e2e/gate-harness.mjs` — the rules as code: `portHolders`, `freePortSafely`, `startOwnedDaemon`, `watchTransport`, `attributeOutcome`, plus a self-check
45
+ - [x] `run.mjs` and `ci.yml` no longer kill client sockets on the bridge port
46
+
47
+ ### Phase 1 — close the silent gaps already in reach
48
+
49
+ - [x] **Trace-shape assertions** — `apps/e2e/trace-shape.mjs` + `trace-shape-test`. One root span per callId, no nested span without a completed parent (the hang signature), no undeclared parentless `browser.command`, no `ok:false` without an error. Calibrated against a real 494-span run: silent there, fires on all four fault shapes.
50
+ - [x] **Daemon truthfulness (the port half)** — `daemon/port-presence.ts` gives a three-state answer (`daemon` / `foreign` / `free`) from two probes and no platform code. `serve` now refuses a foreign port and waits for a real bind before claiming success; `status` and `doctor` name the obstacle. Guarded by `daemon-port-honesty-test`, which squats the port and includes the free-port control. (#105, #112, #115)
51
+ - [x] **`daemon_alive` heartbeat** — `daemon/heartbeat.ts` beats unconditionally on a fixed cadence (30s, `RETICLE_HEARTBEAT_MS` to override) and `classifyDaemonLife` reads a log back into `alive` / `clean` / `signalled` / `died_silently` / `unknown`. A heartbeat nobody interprets is only log volume, so the reader ships with it and is exported for the gate. Guarded by `daemon-heartbeat-test`, which SIGKILLs a real daemon and includes the tidy-shutdown control. (#123)
52
+ - [x] **`observation_lost`** — a new `VerifiedReason`, matched above the `pass === false` clause, so a lost connection grades UNKNOWN instead of being reported as a failed assertion. Signalled by a structured flag from `waitForPredicate`, never by matching on `failureReason` (which is free prose about the app everywhere else it is produced). Threaded through the assert and wait paths too, not just act. (#124)
53
+ - [x] **Telemetry chokepoint coverage** — `tools/dispatch-chokepoint.test.ts` scans for any `.handler(` call outside `runTool` and fails unless it is declared with a stated reason and cost. Three exist today: family folding in `merge-tools.ts` (counted as the family, not the member), `verify-change-tools.ts` (inner `flow_verify` uncounted), and the bridge test harness. Two of those are known observability gaps — the value is that they are now _known_ and a fourth cannot arrive unnoticed. Verified by introducing a bypass and watching it redden.
54
+ - [x] **Specified transport faults** in place of `kill -9` — `apps/e2e/fault-proxy.mjs` (no dependency; toxiproxy is not installed and the battery is deliberately dependency-free) gives none / reset-peer / blackhole / latency / truncate over `node:net`, each proven distinguishable by its own self-check. `transport-faults-test` puts it between the MCP proxy and a real daemon and asserts the product claim: every call is ANSWERED and the stdio server survives. It also separates the two unanswered-call populations by timing — a queued call answered by the 20s queue timer, a call broken in flight answered in ~600ms via `sse_aborted`. A toxic breaks the connection and never the process, so the self-inflicted `kill -9` confusion is unreachable here.
55
+
56
+ ### Phase 2 — the install gate
57
+
58
+ - [x] **Tier 1 — all three `init` paths** — `apps/e2e/install-gate.mjs` (`pnpm gate:install`), wired into CI as its own `install-gate` job. Scaffolds `npm create vite`, `create-next-app` (app router) and `create-next-app --no-app` (pages router), publishes this checkout to a local Verdaccio, lets **`init` do its own dependency install** from it, boots each app, opens it in a real browser and POLLS for a session. **3/3 scaffolds, 8/8 assertions each.** The pages-router path — no `app/` root layout, so connect must mount via `pages/_app` — is the one that once did nothing at all, silently.
59
+ - `⚠` is an absolute zero, not a tolerated exemption. The earlier `file:`-wired version had to pass `--no-install` and then argue away the `⚠` it produced, which left the step most likely to regress as the one step untested.
60
+ - A `package-lock.json` check confirms the SDK came from the local registry. Without it a scope typo silently measures PUBLISHED code — which is exactly the defect this work found in `docs/local-registry.md` (`@reticle:` vs `@reticlehq:`).
61
+ - **Negative control, run FIRST in CI** (`pnpm gate:install:self-test`): every scaffold is mis-wired to a port the daemon is not on and every one must go RED. Verified — all three fail on the session assertion alone (7 passed, 1 failed), attributed FAIL rather than INCONCLUSIVE because the bridge was provably up. A guard that has never failed is not a guard.
62
+ - `file:` wiring is a dead end and the reason is worth keeping: npm symlinks it, which Vite resolves and Next does not; `--install-links` copies instead and then cannot resolve `workspace:*` at all.
63
+ - [◐] **Tier 2 — built, NOT live.** The sending half is `fixtures-dispatch` in `ci.yml` (on `main` only, never fails the workflow — it is a notification, and the verdict lands in the fixtures repo's run). The receiving half is a template at [`docs/fixtures-dispatch-receiver.yml`](./fixtures-dispatch-receiver.yml), kept in the _sending_ repo so the two ends of the contract cannot drift apart.
64
+ - **Blocked on two things neither of which can be done from inside this repo:** a fine-grained PAT with Actions:write on `reticle-fixtures` added as `FIXTURES_DISPATCH_TOKEN`, and the template copied into that repo. Until the token exists the job says so and exits 0.
65
+ - **It has never run.** Every line is unverified. Tier 2 appearing in this plan is not Tier 2 being live, and the checkbox is deliberately ◐ rather than ✗ or ✓.
66
+ - [x] **Committed baseline of what `init` plans** — `apps/e2e/install-baseline.json`, one line per step (`mark title → target`) per scaffold, diffed on every run; `--update-baseline` re-records it so a change arrives as a reviewable diff. "Zero ⚠" is an absolute and cannot carry this alone: a step silently changing mark (`✓ → ℹ`) still passes it, and a step DISAPPEARING from the plan passes it most comfortably of all, because the thing that would have warned you is the thing that is gone. A threshold answers "is this bad"; a baseline answers "is this DIFFERENT", and silent regressions are almost always the second question.
67
+ - The `target` is in the fingerprint and is the load-bearing field. Leaving it out — which the first version did, on an assumption about absolute paths that was simply wrong — made the app-router and pages-router baselines byte-identical, so a regression mounting the pages-router app into the wrong file would have diffed clean. `Mount ReticleDev → app/layout.tsx` vs `→ pages/_app.tsx` is the whole distinction.
68
+ - Proven by tampering: changing one committed entry to `vite.config.js` reddens with a readable expected/got, while every other assertion stays green — which is the point, since a one-character target change is invisible to both "zero ⚠" and "a session appeared".
69
+ - [x] **Risk routing** — a `changes` job computes the diff with plain `git diff --name-only` (no third-party action: the question is "what changed", and a paths-filter dependency for a one-line diff is a supply-chain decision for nothing) and gates `install-gate` on it. It **fails open** — an unresolvable base ref runs every tier, because the cost of a false positive is CI time and the cost of a false negative is the 0%-connect class shipping again.
70
+ - The reason this was deferred is now solved rather than ignored: a job skipped by an `if:` reports **skipped**, and a branch-protection rule requiring `install-gate` directly would block every docs-only PR forever, waiting on a job that will never run. So nothing is required directly — a single aggregate `gate` job is, and it passes when every dependency succeeded **or was deliberately skipped**, and fails on anything else. `always()` on that job is load-bearing; without it the aggregate is itself skipped whenever a dependency is, reproducing the problem one level up.
71
+ - Routing verified against twelve representative paths without touching CI.
72
+
73
+ ### Phase 3 — the client matrix
74
+
75
+ **Blocked on `init` learning each client.** Today only Claude Code (`claude mcp add`) and Cursor (`~/.cursor/mcp.json`) are first-class; everything else gets a printed JSON snippet, so a `v2.5.0-<client>` artifact would measure the contributor's copy-paste rather than the product.
76
+
77
+ - [x] **`init` support per client** — `init/mcp-clients.ts` registers Reticle with **seven** clients, not two. Every path and shape was read from that client's own documentation rather than recalled: Cursor/Windsurf/Gemini share `mcpServers`, VS Code uses `servers`, OpenCode uses `mcp` with the command as an **array**, Codex is TOML. A single "write mcp.json" assuming the Cursor shape produces a file three of them ignore — an install that reports success and registers nothing.
78
+ - Detection is conservative and one-directional: we write into a config a client **already has**, never creating `~/.gemini` or `~/.codeium` for somebody who does not use them.
79
+ - Codex is deliberately not auto-written. Editing TOML without a parser risks every _other_ server in the user's file; it returns MANUAL with an exact block.
80
+ - [x] **Per-client compat script** — `apps/e2e/client-compat.mjs`. Extracts the command from the config **exactly the way that client would**, runs it, and speaks MCP to it. 7/7 clients, 23 assertions, each advertising 17 tools.
81
+ - It proves only the half a machine can prove, and the verdict is named `runnable-unverified` so the distinction survives into the matrix. Whether the client _reads_ that path needs the real client, which is the submitted half.
82
+ - [x] **Submission flow** — `apps/e2e/matrix.mjs --validate`, run by CI's `matrix-records` job on every PR. The validator refuses what cannot be acted on: `works` without `checks.toolsVisible` (the self-report the flow exists to replace), `broken` without the client's verbatim error, or any record with no host or version. Its self-check runs first, because a validator that has never refused anything is not a validator. Contributor guide in [`docs/matrix/README.md`](./matrix/README.md).
83
+ - [x] **Generated `MATRIX.md`** — from submitted records, prettier-ignored because a generated file that fails `format:check` teaches people to skip the gate. **The current honest state is seven clients and ZERO ticks**: `◐` means a runnable entry was written where the client documents it and _nobody has run that client_, and the generated legend says so rather than rounding `◐` up to `✅`.
84
+
85
+ ### Phase 4 — rates, not booleans
86
+
87
+ Connection stability is a rate. A pass/fail cannot express "breaks a lot", and a number that is not recorded cannot regress.
88
+
89
+ - [x] **Soak with a recorded stability rate** — `apps/e2e/soak.mjs` (`pnpm gate:soak`), run inside the e2e battery where a paired app already exists, and appended to `bench/soak-history.jsonl` so the rate can regress against itself. One held-open MCP link for the whole run, never a reconnect per call (harness rule 3). First recorded row: **120/120 answered, 0 link drops over 39s**.
90
+ - **Idle time between rounds is the point**, not a pause for politeness. 150 back-to-back calls finish in seconds and measure throughput; the disconnect users actually report happens while nobody is calling — the agent is thinking and the next call finds the link gone. Keep-alives, idle shutdown and proxy timeouts all live in that gap, so a soak with no idle never enters it. `--idle-ms 30000 --rounds 60` is the half-hour release soak.
91
+ - Attribution is 3-valued: a transport that did not stay up reports INCONCLUSIVE and claims nothing about the product (harness rule 4).
92
+ - [x] **Per-tool latency and failure budgets** — same run, `bench/TOOL-PROFILE.md`. Answers "which tool is breaking": `tool-surface-sweep` proves each tool is callable **once**, and one call cannot have a failure rate.
93
+ - **Latency is recorded, NOT gated, and that is a deliberate deviation from the wording of this plan.** CLAUDE.md: timing assertions are a bug. A p95 budget in wall-clock ms is a statement about the machine, goes red only under parallel CI load, and teaches everyone to re-run — a gate people re-run has stopped working. A >4x p95 jump prints a loud WARN instead. What IS gated: answer rate (absolute) and per-tool failure rate (vs the recorded baseline), both deterministic.
94
+ - The warn carries a 50ms floor, added because the first real run shouted `reticle_sessions p95 8ms vs 1ms (>4x)` — true, imperceptible, and exactly how a warning channel dies.
95
+ - It profiles the **6 repeatable read-only tools, not all 48 advertised**, and says so in the generated file. Repeating a mutating tool measures the fixture drifting rather than the tool degrading.
96
+ - Eleven-case self-check (`node apps/e2e/soak.mjs --self-check`), in CI's `matrix-records` job. It needs no build and no app — proven by running it in an empty directory, which is how a static import of the built CLI was caught.
97
+ - [ ] false-green scorecard as a standing gate against apps we did not write (#130)
98
+ - **Genuinely blocked here, not deferred by choice.** "Apps we did not write" is the whole content of the check, and this repo contains only apps we wrote. It belongs to the fixtures repo (Tier 2) and needs `FIXTURES_DISPATCH_TOKEN`.
package/docs/gates.md ADDED
@@ -0,0 +1,117 @@
1
+ # Gates — what to run before you push
2
+
3
+ > **One question this file answers:** _I changed some files. Which command do I run?_
4
+ >
5
+ > The _why_ behind the gate design (tiers, the merge-gate/release-gate split, what is still unbuilt) lives in [`gate-plan.md`](./gate-plan.md). This file is the routing table.
6
+
7
+ ---
8
+
9
+ ## 0. Last verified
10
+
11
+ Every gate below was executed end to end against `main` on **2026-08-11** (macOS, M-series, `v2.5.0`). A green row means somebody watched it go green, not that it is supposed to be green.
12
+
13
+ | Gate | Result | Wall clock |
14
+ | --- | --- | --- |
15
+ | `pnpm build` | ✅ | ~40s |
16
+ | `pnpm lint` (+ both guard self-tests) | ✅ | ~15s |
17
+ | `pnpm typecheck` | ✅ | ~10s |
18
+ | `pnpm test:unit` | ✅ 4,315 tests / 461 files across 8 packages | ~45s |
19
+ | `pnpm format:check` | ✅ | ~10s |
20
+ | `pnpm test:integration` | ✅ 12/12 | 17s |
21
+ | `pnpm test:e2e` | ✅ 32/32 specs + soak 60/60 answered | **490s** |
22
+ | `pnpm test:e2e:desktop` | ✅ 2/2 (Electron 20, Tauri 14) | 58s |
23
+ | `node apps/e2e/soak.mjs --self-check` | ✅ | <1s |
24
+ | `node apps/e2e/matrix.mjs --self-check` | ✅ | <1s |
25
+ | `pnpm matrix:compat --only cursor` | ✅ 4/4 | ~10s |
26
+ | `pnpm bench` | ✅ 10/10 — **was failing before this sweep** (see below) | 279s |
27
+
28
+ **`pnpm bench` was broken and nobody knew.** `suite-rre.mjs` recorded four flows that asserted no observable consequence and then demanded a `pass` verdict from `reticle_flow_verify` — which correctly grades an assertion-free suite `unverifiable`. The product got more honest about false greens; the benchmark measuring it did not follow, so the whole run aborted at script 9 of 10 and `replay-determinism` never ran at all. Each flow now carries a success oracle. This is the failure mode `bench/` is most exposed to: **nothing in CI runs it**, so it can only rot silently.
29
+
30
+ `pnpm gate:install` (~15 min) and the Windows / Rust jobs were **not** run in this sweep — they are CI-only or network-bound. They are green on `main` per the last CI run, which is a weaker claim than every row above, and is stated that way on purpose.
31
+
32
+ ---
33
+
34
+ ## 1. The routing table
35
+
36
+ Find the row that matches what you changed. Run its commands. That is the whole rule.
37
+
38
+ | You changed | Run | Cost |
39
+ | --- | --- | --- |
40
+ | **Anything at all** | `pnpm lint && pnpm typecheck && pnpm test:unit` | ~2 min |
41
+ | The tool surface, the wire contract (`packages/core`), or an observer | ↑ **and** `pnpm test:e2e` | +~8 min |
42
+ | `reticle init`, `@reticlehq/vite-plugin`, `@reticlehq/next`, `@reticlehq/babel-plugin` — anything a user runs before their first session | ↑ **and** `pnpm gate:install` | +~15 min |
43
+ | `@reticlehq/electron`, `packages/tauri`, the IPC observer, desktop capture | ↑ **and** `pnpm test:e2e:desktop` | +~3 min |
44
+ | Telemetry, feedback, or anything that emits an event | ↑ **and** read [`telemetry-contract.md`](./telemetry-contract.md) first. `pnpm test:e2e` covers it (`telemetry-events-test`) | — |
45
+ | `packages/tauri` (Rust) | ↑ **and** `cd packages/tauri && cargo fmt --check && cargo clippy --all-targets -- -D warnings` | +~2 min |
46
+ | Docs, README, comments only | `pnpm format:check` | seconds |
47
+
48
+ **Why routing exists.** The full set is roughly 35 minutes. A gate people resent is a gate people route around, so only the tier that can see your change is worth your time. CI runs everything regardless — routing costs you a slower red, never a missed one.
49
+
50
+ ---
51
+
52
+ ## 2. Every gate, and what each one can actually see
53
+
54
+ Each gate exists because the ones above it are blind to something. That blindness is the column that matters.
55
+
56
+ | Gate | Command | Proves | Blind to | CI job |
57
+ | --- | --- | --- | --- | --- |
58
+ | **Build** | `pnpm build` | every package compiles and emits | anything at runtime | `verify` |
59
+ | **Lint** | `pnpm lint` | style rules, plus the dependency-boundary and lossy-transform guards | anything not expressible as a rule | `verify` |
60
+ | **Typecheck** | `pnpm typecheck` | types agree across package boundaries | runtime behaviour | `verify` |
61
+ | **Unit** | `pnpm test:unit` | 4,315 tests, per-package, no browser | anything crossing a package boundary at runtime | `verify` |
62
+ | **Format** | `pnpm format:check` | Prettier | — | `verify` |
63
+ | **Integration** | `pnpm test:integration` | real headless Chromium: browser pool, crash isolation, framework adapters, `withReticle` | the MCP surface, the daemon | `e2e` |
64
+ | **Web e2e battery** | `pnpm test:e2e` | 30 specs against 3 booted servers and a real browser — the tool surface, the daemon lifecycle, transport faults, telemetry, trace shape — plus the soak | desktop runtimes; the install | `e2e` |
65
+ | **Desktop battery** | `pnpm test:e2e:desktop` | a real Electron main process and a **packaged** Tauri binary, driven headless | web-only paths | `desktop-e2e` |
66
+ | **Install gate** | `pnpm gate:install` | scaffolds 3 pristine apps, publishes this checkout to a local Verdaccio, lets `init` install itself, boots each app in a real browser, polls for a session | install _complexity_ — see [`fixtures.md`](./fixtures.md) | `install-gate` |
67
+ | **Matrix records** | `pnpm matrix:validate` | every submitted client-compat record is well-formed | whether the client actually works | `matrix-records` |
68
+ | **Windows** | (CI only) | that the code runs at all on the majority platform | e2e; Windows is unit-only | `windows` |
69
+ | **Rust** | `cargo fmt/clippy/check` | `packages/tauri` compiles and lints on Linux, macOS, and cross-checks Windows | everything JS | `rust`, `rust-macos` |
70
+
71
+ **The single required status check is `gate`.** It passes when every job above either succeeded or was deliberately skipped by path routing, and fails on anything else. Adding a job to `ci.yml` is half the work; adding it to `gate`'s `needs:` list is the other half — a job missing from that list runs, reports, and is structurally incapable of blocking a merge.
72
+
73
+ ### Guards that self-test
74
+
75
+ Four checks prove they can still fail before they are trusted. A guard that has never refused anything is not a guard, so each has a negative control that CI runs **first**:
76
+
77
+ ```bash
78
+ node scripts/check-boundaries.mjs --self-test # catches a synthetic bad dependency graph
79
+ node scripts/check-lossy-transforms.mjs --self-test # catches an unclassified lossy export
80
+ node apps/e2e/soak.mjs --self-check # the soak gate still reddens on an unanswered call
81
+ node apps/e2e/matrix.mjs --self-check # the validator still refuses a malformed record
82
+ pnpm gate:install:self-test # mis-wires every scaffold; the gate MUST go red
83
+ ```
84
+
85
+ ---
86
+
87
+ ## 3. Gates that are not automatic
88
+
89
+ These are real and they work; they are not on the PR path, so they only run when somebody runs them.
90
+
91
+ | Command | What it is | When |
92
+ | --- | --- | --- |
93
+ | `pnpm gate:soak:record` | the half-hour release soak (20 rounds, 2s idle), re-records `bench/soak-history.jsonl` and `bench/TOOL-PROFILE.md` | before a release |
94
+ | `pnpm matrix:compat` | drives each MCP client's config exactly the way that client would; writes a machine record for `docs/matrix/` | before a release, per client |
95
+ | `pnpm knip` | unused files, exports, and dependencies | when the repo feels heavy |
96
+ | `node scripts/verify.mjs` in the sibling **`reticle-fixtures`** repo | installs this commit into nine real third-party apps (install _complexity_, not regressions) | see [`fixtures.md`](./fixtures.md) |
97
+
98
+ ---
99
+
100
+ ## 4. `bench/` is not a gate
101
+
102
+ `bench/` is **measurement and research**, not a merge check. Nothing there blocks a PR, nothing runs in CI, and it is allowed to bit-rot in a way a gate is not. Read [`bench/README.md`](../bench/README.md) before touching it — it says which scripts are live and which are one-off studies kept as evidence for a published claim.
103
+
104
+ The one exception worth knowing: `pnpm bench` + `pnpm bench:gate` is a working regression gate for the replay numbers, and it is run by hand before a release.
105
+
106
+ ---
107
+
108
+ ## 5. When a gate fails and you think it is the gate's fault
109
+
110
+ Sometimes it is. The specific failures worth recognising:
111
+
112
+ - **`EADDRINUSE` / "died during boot"** — a previous run left something on `:8787`, `:4310`, or `:3100`. `run-ci.sh` frees these on exit; if it was killed, free them by hand.
113
+ - **Killing port 4400 with `lsof -ti tcp:4400 | xargs kill -9`** — this SIGKILLs the `reticle mcp` proxy too, because the proxy holds a _client_ socket on the bridge port. Always add `-sTCP:LISTEN`. This is the root cause of most "the MCP went down" reports.
114
+ - **A timing assertion** — if a test asserts `Date.now() - t < N`, that is a bug in the test, not a flake to re-run. Assert the bound (output size, a truncation flag), or use a generous per-test timeout. See [`harness-rules.md`](../apps/e2e/harness-rules.md).
115
+ - **An `INCONCLUSIVE` verdict** — the harness is telling you the transport did not stay up, so it is claiming nothing about the product. That is the harness working, not the product failing.
116
+
117
+ The four rules every tier obeys, and the incident behind each, are in [`apps/e2e/harness-rules.md`](../apps/e2e/harness-rules.md).