@smthrs/control 0.0.0-stage → 1.0.0-rc.3

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 (418) hide show
  1. package/CHANGELOG.md +194 -0
  2. package/LICENSE +21 -0
  3. package/README.md +168 -2
  4. package/dist/cjs/ApprovalAuthority.d.ts +73 -0
  5. package/dist/cjs/ApprovalAuthority.d.ts.map +1 -0
  6. package/dist/cjs/ApprovalAuthority.js +62 -0
  7. package/dist/cjs/ApprovalAuthority.js.map +7 -0
  8. package/dist/cjs/Cancellation.d.ts +107 -0
  9. package/dist/cjs/Cancellation.d.ts.map +1 -0
  10. package/dist/cjs/Cancellation.js +72 -0
  11. package/dist/cjs/Cancellation.js.map +7 -0
  12. package/dist/cjs/Channels.d.ts +170 -0
  13. package/dist/cjs/Channels.d.ts.map +1 -0
  14. package/dist/cjs/Channels.js +278 -0
  15. package/dist/cjs/Channels.js.map +7 -0
  16. package/dist/cjs/Control.d.ts +202 -0
  17. package/dist/cjs/Control.d.ts.map +1 -0
  18. package/dist/cjs/Control.js +47 -0
  19. package/dist/cjs/Control.js.map +7 -0
  20. package/dist/cjs/ControlClient.d.ts +52 -0
  21. package/dist/cjs/ControlClient.d.ts.map +1 -0
  22. package/dist/cjs/ControlClient.js +191 -0
  23. package/dist/cjs/ControlClient.js.map +7 -0
  24. package/dist/cjs/ControlError.d.ts +318 -0
  25. package/dist/cjs/ControlError.d.ts.map +1 -0
  26. package/dist/cjs/ControlError.js +249 -0
  27. package/dist/cjs/ControlError.js.map +7 -0
  28. package/dist/cjs/ControlExecutor.d.ts +372 -0
  29. package/dist/cjs/ControlExecutor.d.ts.map +1 -0
  30. package/dist/cjs/ControlExecutor.js +123 -0
  31. package/dist/cjs/ControlExecutor.js.map +7 -0
  32. package/dist/cjs/ControlFacts.d.ts +454 -0
  33. package/dist/cjs/ControlFacts.d.ts.map +1 -0
  34. package/dist/cjs/ControlFacts.js +261 -0
  35. package/dist/cjs/ControlFacts.js.map +7 -0
  36. package/dist/cjs/ControlLive.d.ts +23 -0
  37. package/dist/cjs/ControlLive.d.ts.map +1 -0
  38. package/dist/cjs/ControlLive.js +1280 -0
  39. package/dist/cjs/ControlLive.js.map +7 -0
  40. package/dist/cjs/ControlRpcs.d.ts +1204 -0
  41. package/dist/cjs/ControlRpcs.d.ts.map +1 -0
  42. package/dist/cjs/ControlRpcs.js +247 -0
  43. package/dist/cjs/ControlRpcs.js.map +7 -0
  44. package/dist/cjs/ControlRuntime.d.ts +635 -0
  45. package/dist/cjs/ControlRuntime.d.ts.map +1 -0
  46. package/dist/cjs/ControlRuntime.js +740 -0
  47. package/dist/cjs/ControlRuntime.js.map +7 -0
  48. package/dist/cjs/ControlSchema.d.ts +2642 -0
  49. package/dist/cjs/ControlSchema.d.ts.map +1 -0
  50. package/dist/cjs/ControlSchema.js +634 -0
  51. package/dist/cjs/ControlSchema.js.map +7 -0
  52. package/dist/cjs/ControlServer.d.ts +51 -0
  53. package/dist/cjs/ControlServer.d.ts.map +1 -0
  54. package/dist/cjs/ControlServer.js +121 -0
  55. package/dist/cjs/ControlServer.js.map +7 -0
  56. package/dist/cjs/Credential.d.ts +136 -0
  57. package/dist/cjs/Credential.d.ts.map +1 -0
  58. package/dist/cjs/Credential.js +168 -0
  59. package/dist/cjs/Credential.js.map +7 -0
  60. package/dist/cjs/CredentialCipher.d.ts +90 -0
  61. package/dist/cjs/CredentialCipher.d.ts.map +1 -0
  62. package/dist/cjs/CredentialCipher.js +45 -0
  63. package/dist/cjs/CredentialCipher.js.map +7 -0
  64. package/dist/cjs/CredentialStore.d.ts +97 -0
  65. package/dist/cjs/CredentialStore.d.ts.map +1 -0
  66. package/dist/cjs/CredentialStore.js +81 -0
  67. package/dist/cjs/CredentialStore.js.map +7 -0
  68. package/dist/cjs/DispatchReader.d.ts +112 -0
  69. package/dist/cjs/DispatchReader.d.ts.map +1 -0
  70. package/dist/cjs/DispatchReader.js +45 -0
  71. package/dist/cjs/DispatchReader.js.map +7 -0
  72. package/dist/cjs/Health.d.ts +333 -0
  73. package/dist/cjs/Health.d.ts.map +1 -0
  74. package/dist/cjs/Health.js +311 -0
  75. package/dist/cjs/Health.js.map +7 -0
  76. package/dist/cjs/JevSessionChecker.d.ts +57 -0
  77. package/dist/cjs/JevSessionChecker.d.ts.map +1 -0
  78. package/dist/cjs/JevSessionChecker.js +113 -0
  79. package/dist/cjs/JevSessionChecker.js.map +7 -0
  80. package/dist/cjs/Lineage.d.ts +131 -0
  81. package/dist/cjs/Lineage.d.ts.map +1 -0
  82. package/dist/cjs/Lineage.js +81 -0
  83. package/dist/cjs/Lineage.js.map +7 -0
  84. package/dist/cjs/Migrations.d.ts +34 -0
  85. package/dist/cjs/Migrations.d.ts.map +1 -0
  86. package/dist/cjs/Migrations.js +60 -0
  87. package/dist/cjs/Migrations.js.map +7 -0
  88. package/dist/cjs/Monitor.d.ts +282 -0
  89. package/dist/cjs/Monitor.d.ts.map +1 -0
  90. package/dist/cjs/Monitor.js +283 -0
  91. package/dist/cjs/Monitor.js.map +7 -0
  92. package/dist/cjs/ScopedToken.d.ts +193 -0
  93. package/dist/cjs/ScopedToken.d.ts.map +1 -0
  94. package/dist/cjs/ScopedToken.js +135 -0
  95. package/dist/cjs/ScopedToken.js.map +7 -0
  96. package/dist/cjs/SqlControlRuntime.d.ts +161 -0
  97. package/dist/cjs/SqlControlRuntime.d.ts.map +1 -0
  98. package/dist/cjs/SqlControlRuntime.js +1521 -0
  99. package/dist/cjs/SqlControlRuntime.js.map +7 -0
  100. package/dist/cjs/SqlCredentialStore.d.ts +43 -0
  101. package/dist/cjs/SqlCredentialStore.d.ts.map +1 -0
  102. package/dist/cjs/SqlCredentialStore.js +113 -0
  103. package/dist/cjs/SqlCredentialStore.js.map +7 -0
  104. package/dist/cjs/Steering.d.ts +69 -0
  105. package/dist/cjs/Steering.d.ts.map +1 -0
  106. package/dist/cjs/Steering.js +49 -0
  107. package/dist/cjs/Steering.js.map +7 -0
  108. package/dist/cjs/SystemFlows.d.ts +223 -0
  109. package/dist/cjs/SystemFlows.d.ts.map +1 -0
  110. package/dist/cjs/SystemFlows.js +195 -0
  111. package/dist/cjs/SystemFlows.js.map +7 -0
  112. package/dist/cjs/WebCryptoCipher.d.ts +49 -0
  113. package/dist/cjs/WebCryptoCipher.d.ts.map +1 -0
  114. package/dist/cjs/WebCryptoCipher.js +129 -0
  115. package/dist/cjs/WebCryptoCipher.js.map +7 -0
  116. package/dist/cjs/WebhookChannel.d.ts +113 -0
  117. package/dist/cjs/WebhookChannel.d.ts.map +1 -0
  118. package/dist/cjs/WebhookChannel.js +98 -0
  119. package/dist/cjs/WebhookChannel.js.map +7 -0
  120. package/dist/cjs/index.d.ts +160 -0
  121. package/dist/cjs/index.d.ts.map +1 -0
  122. package/dist/cjs/index.js +91 -0
  123. package/dist/cjs/index.js.map +7 -0
  124. package/dist/cjs/internal/MutationBoundary.d.ts +27 -0
  125. package/dist/cjs/internal/MutationBoundary.d.ts.map +1 -0
  126. package/dist/cjs/internal/MutationBoundary.js +50 -0
  127. package/dist/cjs/internal/MutationBoundary.js.map +7 -0
  128. package/dist/cjs/internal/activeFibers.d.ts +12 -0
  129. package/dist/cjs/internal/activeFibers.d.ts.map +1 -0
  130. package/dist/cjs/internal/activeFibers.js +30 -0
  131. package/dist/cjs/internal/activeFibers.js.map +7 -0
  132. package/dist/cjs/internal/issues.d.ts +28 -0
  133. package/dist/cjs/internal/issues.d.ts.map +1 -0
  134. package/dist/cjs/internal/issues.js +34 -0
  135. package/dist/cjs/internal/issues.js.map +7 -0
  136. package/dist/cjs/internal/planning.d.ts +347 -0
  137. package/dist/cjs/internal/planning.d.ts.map +1 -0
  138. package/dist/cjs/internal/planning.js +137 -0
  139. package/dist/cjs/internal/planning.js.map +7 -0
  140. package/dist/cjs/internal/sqlSchemaErrors.d.ts +18 -0
  141. package/dist/cjs/internal/sqlSchemaErrors.d.ts.map +1 -0
  142. package/dist/cjs/internal/sqlSchemaErrors.js +40 -0
  143. package/dist/cjs/internal/sqlSchemaErrors.js.map +7 -0
  144. package/dist/cjs/migrations/0001_control_tables.d.ts +18 -0
  145. package/dist/cjs/migrations/0001_control_tables.d.ts.map +1 -0
  146. package/dist/cjs/migrations/0001_control_tables.js +115 -0
  147. package/dist/cjs/migrations/0001_control_tables.js.map +7 -0
  148. package/dist/cjs/migrations/0002_run_keys.d.ts +15 -0
  149. package/dist/cjs/migrations/0002_run_keys.d.ts.map +1 -0
  150. package/dist/cjs/migrations/0002_run_keys.js +44 -0
  151. package/dist/cjs/migrations/0002_run_keys.js.map +7 -0
  152. package/dist/cjs/migrations/0003_signal_commands.d.ts +13 -0
  153. package/dist/cjs/migrations/0003_signal_commands.d.ts.map +1 -0
  154. package/dist/cjs/migrations/0003_signal_commands.js +50 -0
  155. package/dist/cjs/migrations/0003_signal_commands.js.map +7 -0
  156. package/dist/cjs/migrations/0004_approval_decisions.d.ts +13 -0
  157. package/dist/cjs/migrations/0004_approval_decisions.d.ts.map +1 -0
  158. package/dist/cjs/migrations/0004_approval_decisions.js +45 -0
  159. package/dist/cjs/migrations/0004_approval_decisions.js.map +7 -0
  160. package/dist/cjs/migrations/0005_signal_principals.d.ts +18 -0
  161. package/dist/cjs/migrations/0005_signal_principals.d.ts.map +1 -0
  162. package/dist/cjs/migrations/0005_signal_principals.js +45 -0
  163. package/dist/cjs/migrations/0005_signal_principals.js.map +7 -0
  164. package/dist/cjs/migrations/0006_run_principals.d.ts +18 -0
  165. package/dist/cjs/migrations/0006_run_principals.d.ts.map +1 -0
  166. package/dist/cjs/migrations/0006_run_principals.js +49 -0
  167. package/dist/cjs/migrations/0006_run_principals.js.map +7 -0
  168. package/dist/cjs/migrations/0007_resume_consent.d.ts +18 -0
  169. package/dist/cjs/migrations/0007_resume_consent.d.ts.map +1 -0
  170. package/dist/cjs/migrations/0007_resume_consent.js +46 -0
  171. package/dist/cjs/migrations/0007_resume_consent.js.map +7 -0
  172. package/dist/cjs/package.json +1 -0
  173. package/dist/cjs/test/TestControl.d.ts +19 -0
  174. package/dist/cjs/test/TestControl.d.ts.map +1 -0
  175. package/dist/cjs/test/TestControl.js +62 -0
  176. package/dist/cjs/test/TestControl.js.map +7 -0
  177. package/dist/esm/ApprovalAuthority.d.ts +73 -0
  178. package/dist/esm/ApprovalAuthority.d.ts.map +1 -0
  179. package/dist/esm/ApprovalAuthority.js +72 -0
  180. package/dist/esm/ApprovalAuthority.js.map +1 -0
  181. package/dist/esm/Cancellation.d.ts +107 -0
  182. package/dist/esm/Cancellation.d.ts.map +1 -0
  183. package/dist/esm/Cancellation.js +116 -0
  184. package/dist/esm/Cancellation.js.map +1 -0
  185. package/dist/esm/Channels.d.ts +170 -0
  186. package/dist/esm/Channels.d.ts.map +1 -0
  187. package/dist/esm/Channels.js +312 -0
  188. package/dist/esm/Channels.js.map +1 -0
  189. package/dist/esm/Control.d.ts +202 -0
  190. package/dist/esm/Control.d.ts.map +1 -0
  191. package/dist/esm/Control.js +42 -0
  192. package/dist/esm/Control.js.map +1 -0
  193. package/dist/esm/ControlClient.d.ts +52 -0
  194. package/dist/esm/ControlClient.d.ts.map +1 -0
  195. package/dist/esm/ControlClient.js +217 -0
  196. package/dist/esm/ControlClient.js.map +1 -0
  197. package/dist/esm/ControlError.d.ts +318 -0
  198. package/dist/esm/ControlError.d.ts.map +1 -0
  199. package/dist/esm/ControlError.js +359 -0
  200. package/dist/esm/ControlError.js.map +1 -0
  201. package/dist/esm/ControlExecutor.d.ts +372 -0
  202. package/dist/esm/ControlExecutor.d.ts.map +1 -0
  203. package/dist/esm/ControlExecutor.js +212 -0
  204. package/dist/esm/ControlExecutor.js.map +1 -0
  205. package/dist/esm/ControlFacts.d.ts +454 -0
  206. package/dist/esm/ControlFacts.d.ts.map +1 -0
  207. package/dist/esm/ControlFacts.js +324 -0
  208. package/dist/esm/ControlFacts.js.map +1 -0
  209. package/dist/esm/ControlLive.d.ts +23 -0
  210. package/dist/esm/ControlLive.d.ts.map +1 -0
  211. package/dist/esm/ControlLive.js +1585 -0
  212. package/dist/esm/ControlLive.js.map +1 -0
  213. package/dist/esm/ControlRpcs.d.ts +1204 -0
  214. package/dist/esm/ControlRpcs.d.ts.map +1 -0
  215. package/dist/esm/ControlRpcs.js +299 -0
  216. package/dist/esm/ControlRpcs.js.map +1 -0
  217. package/dist/esm/ControlRuntime.d.ts +635 -0
  218. package/dist/esm/ControlRuntime.d.ts.map +1 -0
  219. package/dist/esm/ControlRuntime.js +807 -0
  220. package/dist/esm/ControlRuntime.js.map +1 -0
  221. package/dist/esm/ControlSchema.d.ts +2642 -0
  222. package/dist/esm/ControlSchema.d.ts.map +1 -0
  223. package/dist/esm/ControlSchema.js +1030 -0
  224. package/dist/esm/ControlSchema.js.map +1 -0
  225. package/dist/esm/ControlServer.d.ts +51 -0
  226. package/dist/esm/ControlServer.d.ts.map +1 -0
  227. package/dist/esm/ControlServer.js +145 -0
  228. package/dist/esm/ControlServer.js.map +1 -0
  229. package/dist/esm/Credential.d.ts +136 -0
  230. package/dist/esm/Credential.d.ts.map +1 -0
  231. package/dist/esm/Credential.js +190 -0
  232. package/dist/esm/Credential.js.map +1 -0
  233. package/dist/esm/CredentialCipher.d.ts +90 -0
  234. package/dist/esm/CredentialCipher.d.ts.map +1 -0
  235. package/dist/esm/CredentialCipher.js +56 -0
  236. package/dist/esm/CredentialCipher.js.map +1 -0
  237. package/dist/esm/CredentialStore.d.ts +97 -0
  238. package/dist/esm/CredentialStore.d.ts.map +1 -0
  239. package/dist/esm/CredentialStore.js +101 -0
  240. package/dist/esm/CredentialStore.js.map +1 -0
  241. package/dist/esm/DispatchReader.d.ts +112 -0
  242. package/dist/esm/DispatchReader.d.ts.map +1 -0
  243. package/dist/esm/DispatchReader.js +76 -0
  244. package/dist/esm/DispatchReader.js.map +1 -0
  245. package/dist/esm/Health.d.ts +333 -0
  246. package/dist/esm/Health.d.ts.map +1 -0
  247. package/dist/esm/Health.js +400 -0
  248. package/dist/esm/Health.js.map +1 -0
  249. package/dist/esm/JevSessionChecker.d.ts +57 -0
  250. package/dist/esm/JevSessionChecker.d.ts.map +1 -0
  251. package/dist/esm/JevSessionChecker.js +108 -0
  252. package/dist/esm/JevSessionChecker.js.map +1 -0
  253. package/dist/esm/Lineage.d.ts +131 -0
  254. package/dist/esm/Lineage.d.ts.map +1 -0
  255. package/dist/esm/Lineage.js +174 -0
  256. package/dist/esm/Lineage.js.map +1 -0
  257. package/dist/esm/Migrations.d.ts +34 -0
  258. package/dist/esm/Migrations.d.ts.map +1 -0
  259. package/dist/esm/Migrations.js +53 -0
  260. package/dist/esm/Migrations.js.map +1 -0
  261. package/dist/esm/Monitor.d.ts +282 -0
  262. package/dist/esm/Monitor.d.ts.map +1 -0
  263. package/dist/esm/Monitor.js +415 -0
  264. package/dist/esm/Monitor.js.map +1 -0
  265. package/dist/esm/ScopedToken.d.ts +193 -0
  266. package/dist/esm/ScopedToken.d.ts.map +1 -0
  267. package/dist/esm/ScopedToken.js +224 -0
  268. package/dist/esm/ScopedToken.js.map +1 -0
  269. package/dist/esm/SqlControlRuntime.d.ts +161 -0
  270. package/dist/esm/SqlControlRuntime.d.ts.map +1 -0
  271. package/dist/esm/SqlControlRuntime.js +1756 -0
  272. package/dist/esm/SqlControlRuntime.js.map +1 -0
  273. package/dist/esm/SqlCredentialStore.d.ts +43 -0
  274. package/dist/esm/SqlCredentialStore.d.ts.map +1 -0
  275. package/dist/esm/SqlCredentialStore.js +97 -0
  276. package/dist/esm/SqlCredentialStore.js.map +1 -0
  277. package/dist/esm/Steering.d.ts +69 -0
  278. package/dist/esm/Steering.d.ts.map +1 -0
  279. package/dist/esm/Steering.js +89 -0
  280. package/dist/esm/Steering.js.map +1 -0
  281. package/dist/esm/SystemFlows.d.ts +223 -0
  282. package/dist/esm/SystemFlows.d.ts.map +1 -0
  283. package/dist/esm/SystemFlows.js +198 -0
  284. package/dist/esm/SystemFlows.js.map +1 -0
  285. package/dist/esm/WebCryptoCipher.d.ts +49 -0
  286. package/dist/esm/WebCryptoCipher.d.ts.map +1 -0
  287. package/dist/esm/WebCryptoCipher.js +123 -0
  288. package/dist/esm/WebCryptoCipher.js.map +1 -0
  289. package/dist/esm/WebhookChannel.d.ts +113 -0
  290. package/dist/esm/WebhookChannel.d.ts.map +1 -0
  291. package/dist/esm/WebhookChannel.js +109 -0
  292. package/dist/esm/WebhookChannel.js.map +1 -0
  293. package/dist/esm/index.d.ts +160 -0
  294. package/dist/esm/index.d.ts.map +1 -0
  295. package/dist/esm/index.js +160 -0
  296. package/dist/esm/index.js.map +1 -0
  297. package/dist/esm/internal/MutationBoundary.d.ts +27 -0
  298. package/dist/esm/internal/MutationBoundary.d.ts.map +1 -0
  299. package/dist/esm/internal/MutationBoundary.js +40 -0
  300. package/dist/esm/internal/MutationBoundary.js.map +1 -0
  301. package/dist/esm/internal/activeFibers.d.ts +12 -0
  302. package/dist/esm/internal/activeFibers.d.ts.map +1 -0
  303. package/dist/esm/internal/activeFibers.js +17 -0
  304. package/dist/esm/internal/activeFibers.js.map +1 -0
  305. package/dist/esm/internal/issues.d.ts +28 -0
  306. package/dist/esm/internal/issues.d.ts.map +1 -0
  307. package/dist/esm/internal/issues.js +35 -0
  308. package/dist/esm/internal/issues.js.map +1 -0
  309. package/dist/esm/internal/planning.d.ts +347 -0
  310. package/dist/esm/internal/planning.d.ts.map +1 -0
  311. package/dist/esm/internal/planning.js +199 -0
  312. package/dist/esm/internal/planning.js.map +1 -0
  313. package/dist/esm/internal/sqlSchemaErrors.d.ts +18 -0
  314. package/dist/esm/internal/sqlSchemaErrors.d.ts.map +1 -0
  315. package/dist/esm/internal/sqlSchemaErrors.js +38 -0
  316. package/dist/esm/internal/sqlSchemaErrors.js.map +1 -0
  317. package/dist/esm/migrations/0001_control_tables.d.ts +18 -0
  318. package/dist/esm/migrations/0001_control_tables.d.ts.map +1 -0
  319. package/dist/esm/migrations/0001_control_tables.js +96 -0
  320. package/dist/esm/migrations/0001_control_tables.js.map +1 -0
  321. package/dist/esm/migrations/0002_run_keys.d.ts +15 -0
  322. package/dist/esm/migrations/0002_run_keys.d.ts.map +1 -0
  323. package/dist/esm/migrations/0002_run_keys.js +22 -0
  324. package/dist/esm/migrations/0002_run_keys.js.map +1 -0
  325. package/dist/esm/migrations/0003_signal_commands.d.ts +13 -0
  326. package/dist/esm/migrations/0003_signal_commands.d.ts.map +1 -0
  327. package/dist/esm/migrations/0003_signal_commands.js +26 -0
  328. package/dist/esm/migrations/0003_signal_commands.js.map +1 -0
  329. package/dist/esm/migrations/0004_approval_decisions.d.ts +13 -0
  330. package/dist/esm/migrations/0004_approval_decisions.d.ts.map +1 -0
  331. package/dist/esm/migrations/0004_approval_decisions.js +25 -0
  332. package/dist/esm/migrations/0004_approval_decisions.js.map +1 -0
  333. package/dist/esm/migrations/0005_signal_principals.d.ts +18 -0
  334. package/dist/esm/migrations/0005_signal_principals.d.ts.map +1 -0
  335. package/dist/esm/migrations/0005_signal_principals.js +27 -0
  336. package/dist/esm/migrations/0005_signal_principals.js.map +1 -0
  337. package/dist/esm/migrations/0006_run_principals.d.ts +18 -0
  338. package/dist/esm/migrations/0006_run_principals.d.ts.map +1 -0
  339. package/dist/esm/migrations/0006_run_principals.js +30 -0
  340. package/dist/esm/migrations/0006_run_principals.js.map +1 -0
  341. package/dist/esm/migrations/0007_resume_consent.d.ts +18 -0
  342. package/dist/esm/migrations/0007_resume_consent.d.ts.map +1 -0
  343. package/dist/esm/migrations/0007_resume_consent.js +27 -0
  344. package/dist/esm/migrations/0007_resume_consent.js.map +1 -0
  345. package/dist/esm/test/TestControl.d.ts +19 -0
  346. package/dist/esm/test/TestControl.d.ts.map +1 -0
  347. package/dist/esm/test/TestControl.js +30 -0
  348. package/dist/esm/test/TestControl.js.map +1 -0
  349. package/docs/README.md +189 -0
  350. package/docs/api.md +982 -0
  351. package/docs/concepts/authority.md +109 -0
  352. package/docs/concepts/cancellation.md +129 -0
  353. package/docs/concepts/lineage.md +132 -0
  354. package/docs/concepts/ownership.md +139 -0
  355. package/docs/concepts/projections.md +203 -0
  356. package/docs/concepts/receipts.md +128 -0
  357. package/docs/guides/approvals.md +284 -0
  358. package/docs/guides/cancel-and-resume.md +162 -0
  359. package/docs/guides/durable-storage.md +147 -0
  360. package/docs/guides/implement-an-executor.md +173 -0
  361. package/docs/guides/ingest-a-webhook.md +177 -0
  362. package/docs/guides/list-runs.md +160 -0
  363. package/docs/guides/monitor-runs.md +176 -0
  364. package/docs/guides/observe-health.md +147 -0
  365. package/docs/guides/postgres-tests.md +5 -0
  366. package/docs/guides/serve-over-rpc.md +220 -0
  367. package/docs/guides/signal-a-run.md +53 -0
  368. package/docs/guides/steer-a-run.md +138 -0
  369. package/docs/guides/store-credentials.md +164 -0
  370. package/docs/guides/testing.md +139 -0
  371. package/docs/guides/watch-a-run.md +154 -0
  372. package/docs/installation.md +106 -0
  373. package/docs/quickstart.md +163 -0
  374. package/docs/troubleshooting.md +208 -0
  375. package/package.json +405 -3
  376. package/src/ApprovalAuthority.ts +114 -0
  377. package/src/Cancellation.ts +172 -0
  378. package/src/Channels.ts +493 -0
  379. package/src/Control.ts +337 -0
  380. package/src/ControlClient.ts +319 -0
  381. package/src/ControlError.ts +378 -0
  382. package/src/ControlExecutor.ts +490 -0
  383. package/src/ControlFacts.ts +383 -0
  384. package/src/ControlLive.ts +2113 -0
  385. package/src/ControlRpcs.ts +443 -0
  386. package/src/ControlRuntime.ts +1597 -0
  387. package/src/ControlSchema.ts +1380 -0
  388. package/src/ControlServer.ts +182 -0
  389. package/src/Credential.ts +310 -0
  390. package/src/CredentialCipher.ts +110 -0
  391. package/src/CredentialStore.ts +152 -0
  392. package/src/DispatchReader.ts +122 -0
  393. package/src/Health.ts +591 -0
  394. package/src/JevSessionChecker.ts +127 -0
  395. package/src/Lineage.ts +203 -0
  396. package/src/Migrations.ts +56 -0
  397. package/src/Monitor.ts +600 -0
  398. package/src/ScopedToken.ts +306 -0
  399. package/src/SqlControlRuntime.ts +2476 -0
  400. package/src/SqlCredentialStore.ts +148 -0
  401. package/src/Steering.ts +96 -0
  402. package/src/SystemFlows.ts +225 -0
  403. package/src/WebCryptoCipher.ts +169 -0
  404. package/src/WebhookChannel.ts +166 -0
  405. package/src/index.ts +188 -0
  406. package/src/internal/MutationBoundary.ts +46 -0
  407. package/src/internal/activeFibers.ts +22 -0
  408. package/src/internal/issues.ts +40 -0
  409. package/src/internal/planning.ts +262 -0
  410. package/src/internal/sqlSchemaErrors.ts +37 -0
  411. package/src/migrations/0001_control_tables.ts +99 -0
  412. package/src/migrations/0002_run_keys.ts +23 -0
  413. package/src/migrations/0003_signal_commands.ts +27 -0
  414. package/src/migrations/0004_approval_decisions.ts +25 -0
  415. package/src/migrations/0005_signal_principals.ts +27 -0
  416. package/src/migrations/0006_run_principals.ts +31 -0
  417. package/src/migrations/0007_resume_consent.ts +28 -0
  418. package/src/test/TestControl.ts +47 -0
@@ -0,0 +1,139 @@
1
+ ---
2
+ title: "Test against the control plane"
3
+ description: "Use the deterministic in-memory stack, swap one collaborator at a time, exercise the pure projections with no stack at all, and hold your own runtime to the contract both shipped ones satisfy."
4
+ sidebar:
5
+ order: 13
6
+ ---
7
+
8
+ Nothing about the control plane is hard to test, because every nondeterministic
9
+ input is a service: the clock, the runtime, the journal, the notification
10
+ queue, and the executor. A test swaps the service, not the verb.
11
+
12
+ ## Start with the whole stack
13
+
14
+ `TestControl.layer` bundles the four collaborators `ControlLive` requires,
15
+ already deterministic:
16
+
17
+ ```ts
18
+ import { Control } from "@smthrs/control/Control"
19
+ import type * as ControlRuntime from "@smthrs/control/ControlRuntime"
20
+ import * as TestControl from "@smthrs/control/test/TestControl"
21
+ import * as Effect from "effect/Effect"
22
+
23
+ const Deploy: ControlRuntime.MemoryFlow = {
24
+ flowId: "ops/Deploy",
25
+ description: "Deploys one build",
26
+ deployClass: true,
27
+ envelope: { capabilities: [], flows: [], budget: {} }
28
+ }
29
+
30
+ const stack = TestControl.layer({ flows: [Deploy], now: () => 0 })
31
+ ```
32
+
33
+ It provides `Control` together with every collaborator it built:
34
+ `ControlRuntime`, the in-memory journal bundle, a notification queue over that
35
+ journal, an executor, and an empty registry. Ids are derived from counters, so
36
+ `plan-1`, `run-1`, and `fence-1` are stable across runs.
37
+
38
+ `MemoryOptions` is the whole configuration surface:
39
+
40
+ | Option | Effect |
41
+ | ----------- | ------------------------------------------------------------------------- |
42
+ | `flows` | What the plane may plan. Defaults to the plannable reserved system flows. |
43
+ | `now` | The clock every timestamp reads. Pass `() => 0` for stable output. |
44
+ | `principal` | The identity stamped when a caller names none. |
45
+
46
+ ## Swap the executor
47
+
48
+ `TestControl.layer` takes an executor as its second argument, so a test states
49
+ exactly what the engine did:
50
+
51
+ ```ts
52
+ import * as ControlExecutor from "@smthrs/control/ControlExecutor"
53
+
54
+ const launched: Array<string> = []
55
+
56
+ const executor = ControlExecutor.makeNoop({
57
+ launch: ({ run }) =>
58
+ Effect.sync(() => {
59
+ launched.push(run.runId)
60
+ return "accepted" as const
61
+ })
62
+ })
63
+
64
+ const stack = TestControl.layer({ flows: [Deploy], now: () => 0 }, executor)
65
+ ```
66
+
67
+ Each answer in the executor's vocabulary is a distinct composition worth a
68
+ test: `pending` releases the row, `accepted` promotes it to `running`, a
69
+ `LaunchFailed` settles it as `failed`, and a `CancelTerminal` reconciles the
70
+ control row onto the engine's status. See
71
+ [Connect an execution engine](./implement-an-executor.md).
72
+
73
+ The default is `ControlExecutor.makeNoop()`, which answers the honest absence
74
+ for every method, and _no executor at all_ is a different composition again:
75
+ `ControlLive` reads the port optionally, so a plane that starts nothing is a
76
+ supported shape rather than a broken one.
77
+
78
+ ## Test the projections with no stack
79
+
80
+ `classify`, `remedyFor`, `originOf`, `derive`, `expand`, `attribute`, and
81
+ `steerItem` are pure functions of their arguments. Enumerate them directly:
82
+
83
+ ```ts
84
+ import * as Monitor from "@smthrs/control/Monitor"
85
+
86
+ expect(Monitor.classify({
87
+ summary: { runId: "run-1", flowId: "ops/Deploy", status: "running", createdAt: 0, updatedAt: 0 },
88
+ events: [{ sequence: 1, kind: Monitor.attemptStartedEventType, runId: "run-1", occurredAt: 0, payload: {} }],
89
+ beatsWithoutProgress: 3,
90
+ stallBeats: 3
91
+ })).toBe("wedged-node")
92
+ ```
93
+
94
+ Splitting `beatsWithoutProgress` from `stallBeats` is what lets one pure
95
+ function serve a monitor that beats every second and one that beats every hour,
96
+ and it is what lets a test reach a stall without waiting for one.
97
+
98
+ ## Assert on durable evidence
99
+
100
+ The plane's promises are visible in the journal, so assert there rather than on
101
+ internal state:
102
+
103
+ ```ts
104
+ const kinds = yield * control.watch({ runId, follow: false }).pipe(
105
+ Stream.map((event) => event.kind),
106
+ Stream.runCollect
107
+ )
108
+ expect([...kinds]).toEqual(["control.run.accepted", "control.run.pending"])
109
+ ```
110
+
111
+ Use `follow: false`. It ends; the live stream does not.
112
+
113
+ ## Hold your own runtime to the contract
114
+
115
+ `ControlRuntime.layerMemory` and `SqlControlRuntime.layer` are both held to one
116
+ [shared contract suite](https://github.com/smithersai/smithers/blob/main/packages/smithers/control/test/ControlContract.ts).
117
+ It is not part of the published tarball, so a third implementation copies it
118
+ from the repository and runs it against its own layer. That is what makes
119
+ "behaves like the memory runtime" a checkable claim rather than a hope.
120
+
121
+ ## Assert on the refusals too
122
+
123
+ Several behaviors are refusals, and they carry the sentence an operator reads:
124
+
125
+ ```ts
126
+ const error = yield * Effect.flip(control.list({ _tag: "runs", limit: 0 }))
127
+ expect(error.issue).toBe("limit: must be an integer between 1 and 500, received 0")
128
+ ```
129
+
130
+ `ControlClient.isControlError` narrows an unknown value to the declared union,
131
+ derived from the same schema the errors are declared in.
132
+
133
+ ## Where to go next
134
+
135
+ - [Quickstart](../quickstart.md): the smallest complete program on this stack.
136
+ - [Troubleshooting](../troubleshooting.md): the refusals worth a test of their
137
+ own.
138
+ - [Testing flows on smithers.sh](/docs/guides/testing-flows/): the same habit,
139
+ one layer down.
@@ -0,0 +1,154 @@
1
+ ---
2
+ title: "Watch a run's events"
3
+ description: "Read a finite snapshot or follow a live stream of control events, resume at a cursor without seeing an entry twice, and read the lineage and steer-delivery deltas the plane derives."
4
+ sidebar:
5
+ order: 3
6
+ ---
7
+
8
+ `watch` streams `ControlEvent` values projected from committed journal entries.
9
+ It is a read: nothing you do with the stream changes a run.
10
+
11
+ ## Take a finite snapshot
12
+
13
+ `follow: false` asks for what is durable when the request is handled. The
14
+ stream ends, which is what makes it assertable in a test or usable in a
15
+ one-shot report:
16
+
17
+ ```ts
18
+ import { Control } from "@smthrs/control/Control"
19
+ import * as Effect from "effect/Effect"
20
+ import * as Stream from "effect/Stream"
21
+
22
+ const kinds = Effect.gen(function*() {
23
+ const control = yield* Control
24
+ return yield* control.watch({ runId: "run-17", follow: false }).pipe(
25
+ Stream.map((event) => event.kind),
26
+ Stream.runCollect
27
+ )
28
+ })
29
+ // [ "control.run.accepted", "control.run.running", "flows.engine.attempt-started", ... ]
30
+ ```
31
+
32
+ ## Follow a live stream
33
+
34
+ Omit `follow` and the stream stays open. This is what a UI subscribes to:
35
+
36
+ ```ts
37
+ const tail = control.watch({ runId: "run-17" }).pipe(
38
+ Stream.runForEach((event) => Effect.log(`${event.sequence} ${event.kind}`))
39
+ )
40
+ ```
41
+
42
+ A subscriber that arrives late still receives the entries it missed. The
43
+ projection pins a high-water sequence per partition, reads everything at or
44
+ below it from a finite snapshot, and takes everything above it from the
45
+ buffered tail. It is a handoff rather than a deduplicated overlap, so an
46
+ arbitrarily long history cannot make an old entry reappear.
47
+
48
+ ## Resume at a cursor
49
+
50
+ Store `event.cursor` after processing each event, then pass it back unchanged
51
+ as `afterCursor` with the same `runId`:
52
+
53
+ ```ts
54
+ const resumed = control.watch({ runId: "run-17", afterCursor: lastSeen })
55
+ ```
56
+
57
+ A cursor is `{ sequence: number; offset?: number }`. `sequence` identifies the
58
+ source journal entry. A present `offset` is the zero-based index of the last
59
+ consumed member of that entry's expansion. An absent offset means the entire
60
+ entry was consumed. The last member always carries this completed-entry cursor,
61
+ so the next watch starts after the source row without rereading it.
62
+
63
+ For a promotion at sequence 12 that delivers two messages, the emitted cursors
64
+ are `{ sequence: 12, offset: 0 }` for the source,
65
+ `{ sequence: 12, offset: 1 }` for the first delivery, and `{ sequence: 12 }`
66
+ for the second. Reconnecting after the source still yields both deliveries.
67
+ This works for finite snapshots and live streams while the source row remains
68
+ in the journal. Commit the checkpoint with your event processing to avoid
69
+ reprocessing an event after a consumer crash.
70
+
71
+ `afterSequence` remains available to skip a fully processed source entry and
72
+ all its derived events. It cannot checkpoint progress inside an expansion.
73
+ Do not combine it with `afterCursor`.
74
+
75
+ Both cursor forms require `runId`. Sequences are partition-local:
76
+
77
+ ```text
78
+ InvalidInput: afterCursor: a watch cursor resumes one run, so it requires runId
79
+ ```
80
+
81
+ Raw `Lineage` and `Steering` projections and older providers can omit `cursor`.
82
+ `ControlLive.watch` assigns a cursor to every emitted event.
83
+
84
+ ## Watch everything
85
+
86
+ Omit `runId` and the stream merges every partition the plane knows: each run,
87
+ and each plan under `plan:<planId>`. Eight partition snapshots are read at a
88
+ time, and the live tail runs beside them so snapshot work never starves it.
89
+
90
+ The plane lists partitions in insertion order, 100 ids per inventory query,
91
+ and reads the next page only when the snapshot needs it. Each page is one
92
+ indexed seek on the table's row key, so a page costs the same at any table size:
93
+
94
+ | Resource | Bound |
95
+ | ------------------------ | ------------------------------------------ |
96
+ | Rows per inventory query | 101 (one page plus the continuation key) |
97
+ | Rows scanned per query | the rows returned; no table scan or sort |
98
+ | Inventory queries | `2 + ceil(plans / 100) + ceil(runs / 100)` |
99
+ | Run summaries decoded | 0 |
100
+ | Follow-mode state | one pinned sequence per partition seen |
101
+
102
+ The first page of each inventory pins its newest entry, and the walk stops
103
+ there. A finite watch (`follow: false`) therefore ends even while runs keep
104
+ arriving. A partition that exists when the watch starts is read exactly once;
105
+ one created during the walk is not listed. A followed watch still delivers its
106
+ entries, because the first tail entry for a partition that nothing has pinned
107
+ pins it and reads its history first.
108
+
109
+ An unscoped watch is the right shape for a dashboard. For a run you can name,
110
+ scope it: the scoped watch is one partition read and it is the only form that
111
+ can resume.
112
+
113
+ ## Read the derived deltas
114
+
115
+ Two kinds are computed rather than recorded, and arrive beside the entry they
116
+ were derived from:
117
+
118
+ | Kind | Payload | Means |
119
+ | ------------------------- | ----------------------------------------------------------- | -------------------------------------------- |
120
+ | `control.run.lineage` | `{ runId, parentRunId, lineageId?, roundOrdinal?, origin }` | A run was spawned, forked, or handed off to. |
121
+ | `control.steer.delivered` | `{ runId, messageId, boundary }` | A turn boundary took your steer. |
122
+
123
+ Both projections are exported, so a client reading the journal directly reaches
124
+ the same conclusions the server does:
125
+
126
+ ```ts
127
+ import * as Lineage from "@smthrs/control/Lineage"
128
+ import * as Steering from "@smthrs/control/Steering"
129
+
130
+ const expanded = [...Lineage.expand(event), ...Steering.derive(event)]
131
+ ```
132
+
133
+ `expand` returns the entry plus any delta it discloses; `derive` returns the
134
+ delta alone. See [Journal projections](../concepts/projections.md) for why
135
+ delivery is derived rather than written.
136
+
137
+ ## Failures
138
+
139
+ Every member of `ControlError` can reach a watch stream. In practice you will
140
+ meet three:
141
+
142
+ - `InvalidInput` for an unscoped or malformed cursor, or both cursor forms together.
143
+ - `PersistenceError` with operation `watch` when a journal read fails. Its
144
+ `cause` is the journal's own error.
145
+ - `Unavailable` with feature `watch` when the composition has no open journal.
146
+
147
+ A watch of a run that does not exist is not an error. The partition is empty.
148
+
149
+ ## Where to go next
150
+
151
+ - [Journal projections](../concepts/projections.md): partitions, the handoff,
152
+ and the full list of kinds the plane writes.
153
+ - [Find runs and page through them](./list-runs.md): the point-in-time view.
154
+ - [`smthrs runs logs`](/cli/runs): the operator surface over this verb.
@@ -0,0 +1,106 @@
1
+ ---
2
+ title: "Installation"
3
+ description: "Install @smthrs/control, its runtime requirements, its import forms, and the collaborator packages an in-memory, durable, or remote composition adds."
4
+ sidebar:
5
+ order: 1
6
+ ---
7
+
8
+ ## Install the package
9
+
10
+ Not on npm yet; see [Installation](https://github.com/smithersai/smithers/blob/main/packages/smithers/flows/flow/docs/installation.md#use-the-libraries).
11
+
12
+ The package requires Node.js 26.4.0 or later and ships as both ESM and
13
+ CommonJS with TypeScript declarations. Its runtime dependencies install with
14
+ it: [`effect`](https://effect.website) and the `@smthrs/*` packages the plane
15
+ composes.
16
+
17
+ The package imports no `node:*` module. Identity comes from
18
+ `globalThis.crypto`, encryption comes from Web Crypto, and persistence speaks
19
+ the driver-neutral SQL contract, so the same modules run in Node.js and in a
20
+ browser that supplies a SQL driver. That is a statement about the imports, not
21
+ a tested guarantee: nothing here is exercised in a browser, so verify your own
22
+ bundle before you depend on it.
23
+
24
+ ## Import forms
25
+
26
+ The root entry point re-exports every module as a namespace:
27
+
28
+ ```ts
29
+ import { Control, ControlLive, Monitor, SqlControlRuntime } from "@smthrs/control"
30
+ ```
31
+
32
+ Each module is also importable from its own subpath, which is the form the
33
+ [API reference](./api.md) uses:
34
+
35
+ ```ts
36
+ import * as ControlExecutor from "@smthrs/control/ControlExecutor"
37
+ import * as ControlSchema from "@smthrs/control/ControlSchema"
38
+ ```
39
+
40
+ The deterministic test stack has its own subpath:
41
+
42
+ ```ts
43
+ import * as TestControl from "@smthrs/control/test/TestControl"
44
+ ```
45
+
46
+ Two subpath families are not public and are blocked in the export map:
47
+ `@smthrs/control/internal/*` and `@smthrs/control/migrations/*`, along with
48
+ every nested `*/index`. `@smthrs/control/package.json` is exported.
49
+
50
+ ## What a composition adds
51
+
52
+ `ControlLive.layer` requires four collaborators, and a host provides all four:
53
+
54
+ | Requirement | Package | What it does here |
55
+ | ------------------- | --------------------------------------------- | ---------------------------------------------------------------------------------- |
56
+ | `ControlRuntime` | this package | Stores plans, tokens, grants, idempotency records, and run rows. |
57
+ | `Journal` | [`@smthrs/journal`](/api/journal) | Records every decision beside the state change it caused, and backs `watch`. |
58
+ | `NotificationQueue` | [`@smthrs/notifications`](/api/notifications) | Carries a steer to the turn boundary that delivers it, and counts what is pending. |
59
+ | `Registry` | [`@smthrs/registry`](/api/registry) | Answers `list({ _tag: "flows" })` with the flows this host discovered. |
60
+
61
+ `ControlExecutor` is optional. A composition that provides none observes and
62
+ records but starts nothing, which is the correct shape for a monitor or a
63
+ read-only dashboard.
64
+
65
+ ### An in-memory composition
66
+
67
+ `TestControl.layer` bundles all four collaborators with the deterministic
68
+ runtime. Its journal uses a real in-memory SQLite database, so the
69
+ [Quickstart](./quickstart.md) adds the optional Node driver:
70
+
71
+ ```bash
72
+ pnpm add effect@4.0.0-rc.115 @effect/sql-sqlite-node@4.0.0-rc.115
73
+ ```
74
+
75
+ ### A durable composition
76
+
77
+ - [`@smthrs/database`](/api/database) supplies the SQL client and the
78
+ `DurableWriter` every control write serializes through.
79
+ - [`@smthrs/run-store`](/api/run-store) supplies the fenced run store
80
+ `SqlControlRuntime` maps the control lifecycle onto.
81
+
82
+ See [Store control state in a database](./guides/durable-storage.md) for the
83
+ layer stack and the migration order.
84
+
85
+ ### A remote composition
86
+
87
+ The RPC boundary uses Effect's own HTTP, WebSocket, and RPC modules, which ship
88
+ inside `effect`. A Node host adds the platform bindings and a serialization
89
+ format:
90
+
91
+ ```bash
92
+ pnpm add @effect/platform-node@4.0.0-rc.115 @effect/platform-node-shared@4.0.0-rc.115
93
+ ```
94
+
95
+ See [Serve the control plane over RPC](./guides/serve-over-rpc.md).
96
+
97
+ ### Credential storage
98
+
99
+ The credential boundary needs a store and a cipher. `CredentialStore.layerMemory`
100
+ and `WebCryptoCipher.layer` need nothing beyond this package;
101
+ `SqlCredentialStore.layer` needs the same database packages a durable
102
+ composition adds. See [Store and resolve a credential](./guides/store-credentials.md).
103
+
104
+ ## Next step
105
+
106
+ Run one plan through approval and launch in the [Quickstart](./quickstart.md).
@@ -0,0 +1,163 @@
1
+ ---
2
+ title: "Quickstart"
3
+ description: "Plan a flow, watch the launch park for approval, approve it, launch it, list the run, and replay the journal, in one in-memory program with no database and no engine."
4
+ sidebar:
5
+ order: 2
6
+ ---
7
+
8
+ This quickstart drives one plan through the whole gate: a plan card, a refused
9
+ launch, an approval, an accepted launch, a listing, and a replay of the run's
10
+ journal. The `Control` service is the production one. Only its collaborators
11
+ are in memory, so the program is deterministic, needs no database, and starts
12
+ no real work.
13
+
14
+ By the end you will have seen the two answers that make the control plane
15
+ usable from a script: a `Receipt` that says what happened, and a `ControlEvent`
16
+ stream that says it again from durable evidence.
17
+
18
+ ## Prerequisites
19
+
20
+ - Node.js 26.4.0 or later.
21
+ - A package with the dependency installed. Not on npm yet; see [Installation](https://github.com/smithersai/smithers/blob/main/packages/smithers/flows/flow/docs/installation.md#use-the-libraries).
22
+
23
+ ## Declare the flow the plane may plan
24
+
25
+ A control plane plans what its runtime knows about. `MemoryFlow` is that
26
+ entry: an id, a description, whether the flow is deploy class, and the
27
+ capability envelope an approval binds to.
28
+
29
+ Create `quickstart.ts`:
30
+
31
+ ```ts
32
+ import type * as ControlRuntime from "@smthrs/control/ControlRuntime"
33
+
34
+ /** One flow this plane may be asked to plan. */
35
+ const Deploy: ControlRuntime.MemoryFlow = {
36
+ flowId: "quickstart/Deploy",
37
+ description: "Deploys one build",
38
+ deployClass: true,
39
+ envelope: {
40
+ capabilities: ["process:spawn"],
41
+ flows: [],
42
+ budget: { milliseconds: 60_000 }
43
+ }
44
+ }
45
+ ```
46
+
47
+ The envelope is the authority a reviewer is being asked to grant. It is part of
48
+ the plan digest, so an approval taken on this envelope cannot authorize a
49
+ wider one later.
50
+
51
+ ## Plan, launch, approve, launch again
52
+
53
+ `plan` returns a `PlanCard`: the flow, a canonical summary of the input, the
54
+ envelope, the keyed node graph, and a digest over all of it. The card starts
55
+ undecided, so the first `run` parks:
56
+
57
+ ```ts
58
+ import { Control } from "@smthrs/control/Control"
59
+ import * as Effect from "effect/Effect"
60
+ import * as Stream from "effect/Stream"
61
+
62
+ const program = Effect.gen(function*() {
63
+ const control = yield* Control
64
+
65
+ const card = yield* control.plan({
66
+ flowId: "quickstart/Deploy",
67
+ input: { build: "v1.4.0" }
68
+ })
69
+
70
+ /** The exact plan, digest, and envelope every launch attempt resubmits. */
71
+ const launch = {
72
+ _tag: "Plan" as const,
73
+ planId: card.planId,
74
+ digest: card.digest,
75
+ envelope: card.envelope
76
+ }
77
+
78
+ // Nothing is approved yet, so this starts nothing and says why.
79
+ const parked = yield* control.run({ ...launch, idempotencyKey: "deploy:v1.4.0" })
80
+
81
+ // The card carries the exact payload an approval is taken on, including a
82
+ // default idempotency key, so a reviewer resubmits it unchanged.
83
+ yield* control.approve(card.approval)
84
+
85
+ // The same call, the same key. Now it launches.
86
+ const accepted = yield* control.run({ ...launch, idempotencyKey: "deploy:v1.4.0" })
87
+
88
+ // And once more, to show what a retry is worth.
89
+ const replayed = yield* control.run({ ...launch, idempotencyKey: "deploy:v1.4.0" })
90
+
91
+ const listed = yield* control.list({ _tag: "runs", filters: {} })
92
+ const runs = listed._tag === "runs" ? listed.items : []
93
+
94
+ const events = yield* control.watch({ runId: runs[0]!.runId, follow: false }).pipe(
95
+ Stream.map((event) => event.kind),
96
+ Stream.runCollect
97
+ )
98
+
99
+ return { parked, accepted, replayed, runs, events: [...events] }
100
+ })
101
+ ```
102
+
103
+ ## Provide the in-memory stack and run it
104
+
105
+ `TestControl.layer` bundles the four collaborators `ControlLive` requires: the
106
+ deterministic runtime, an in-memory journal, a notification queue over that
107
+ journal, and an empty registry. Its executor accepts nothing, which is exactly
108
+ the composition a host that only records has.
109
+
110
+ ```ts
111
+ import * as TestControl from "@smthrs/control/test/TestControl"
112
+
113
+ console.log(
114
+ await Effect.runPromise(
115
+ program.pipe(Effect.provide(TestControl.layer({ flows: [Deploy], now: () => 0 })))
116
+ )
117
+ )
118
+ ```
119
+
120
+ Run the file with your TypeScript runner. The receipts, the listing, and the
121
+ journal replay come back like this:
122
+
123
+ ```text
124
+ {
125
+ parked: { _tag: 'Parked', receiptId: 'deploy:v1.4.0', planId: 'plan-1', status: 'waiting-approval' },
126
+ accepted: { _tag: 'Accepted', receiptId: 'deploy:v1.4.0', runId: 'run-1' },
127
+ replayed: { _tag: 'AlreadyApplied', receiptId: 'deploy:v1.4.0', runId: 'run-1' },
128
+ runs: [ { runId: 'run-1', flowId: 'quickstart/Deploy', status: 'accepted', ... } ],
129
+ events: [ 'control.run.accepted', 'control.run.pending' ]
130
+ }
131
+ ```
132
+
133
+ ## What just happened
134
+
135
+ Four things worth naming, because each is a promise the plane keeps everywhere:
136
+
137
+ - **A launch is not a start.** An undecided plan answers `Parked` with the
138
+ status it is waiting in. Nothing was created, so nothing has to be cleaned
139
+ up. See [Gate work behind an approval](./guides/approvals.md).
140
+ - **The same key means the same mutation, once.** The launch and the retry
141
+ carry one `idempotencyKey`, and the retry answers `AlreadyApplied` with the
142
+ run the first call created. A parked receipt is deliberately not recorded, so
143
+ the key was still free when the plan became approvable. See
144
+ [Receipts and idempotency](./concepts/receipts.md).
145
+ - **An approval is bound to what was reviewed.** `card.approval` carries the
146
+ target, the digest, the envelope, and a default key. Submit a different
147
+ digest or a different envelope and the decision is refused rather than
148
+ re-aimed.
149
+ - **Every decision left evidence.** `watch` replayed the run's journal from
150
+ durable rows. `control.run.pending` is there because this composition's
151
+ executor declined the launch, so the plane released the run rather than
152
+ claiming to drive it. See [Journal projections](./concepts/projections.md).
153
+
154
+ ## Next steps
155
+
156
+ - [Connect an execution engine](./guides/implement-an-executor.md): make
157
+ `control.run` start something real.
158
+ - [Store control state in a database](./guides/durable-storage.md): keep the
159
+ plans, tokens, and runs across a restart.
160
+ - [Serve the control plane over RPC](./guides/serve-over-rpc.md): hand this
161
+ same program a client instead of a layer.
162
+ - [Authority, not execution](./concepts/authority.md): the model the rest of
163
+ the package is built on.