@osolmaz/pi-workflows 0.16.2 → 0.16.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 (315) hide show
  1. package/README.md +41 -41
  2. package/dist/builtins/monitor.workflow.d.ts +1 -1
  3. package/dist/builtins/monitor.workflow.js +8 -8
  4. package/dist/builtins/pi-agent-group.js +3 -3
  5. package/dist/builtins/pi-agent-group.js.map +1 -1
  6. package/dist/channels/adapter-entry.js +14 -14
  7. package/dist/channels/adapter-entry.js.map +1 -1
  8. package/dist/client/client.d.ts +6 -6
  9. package/dist/client/client.js +27 -27
  10. package/dist/client/client.js.map +1 -1
  11. package/dist/client/protocol.d.ts +4 -1
  12. package/dist/client/protocol.js +17 -11
  13. package/dist/client/protocol.js.map +1 -1
  14. package/dist/client/resolver.d.ts +4 -4
  15. package/dist/client/view.d.ts +1 -1
  16. package/dist/extension/index.d.ts +1 -1
  17. package/dist/extension/index.js +32 -32
  18. package/dist/extension/index.js.map +1 -1
  19. package/dist/extension/recorder.d.ts +1 -1
  20. package/dist/extension/recorder.js +1 -1
  21. package/dist/extension/recorder.js.map +1 -1
  22. package/dist/extension/remote-recorder-store.d.ts +1 -1
  23. package/dist/extension/remote-recorder-store.js +2 -2
  24. package/dist/extension/remote-recorder-store.js.map +1 -1
  25. package/dist/extension/{controller-command.d.ts → resource-manager-command.d.ts} +6 -6
  26. package/dist/extension/{controller-command.js → resource-manager-command.js} +7 -7
  27. package/dist/extension/resource-manager-command.js.map +1 -0
  28. package/dist/extension/session-delivery.d.ts +1 -1
  29. package/dist/extension/session-delivery.js +2 -2
  30. package/dist/extension/session-delivery.js.map +1 -1
  31. package/dist/extension/session-view.d.ts +1 -1
  32. package/dist/extension/session-view.js +1 -1
  33. package/dist/extension/session-view.js.map +1 -1
  34. package/dist/extension/workflow-message-coordinator.d.ts +1 -1
  35. package/dist/extension/workflow-message-coordinator.js +6 -6
  36. package/dist/extension/workflow-message-coordinator.js.map +1 -1
  37. package/dist/resource-managers/conditions.d.ts +6 -0
  38. package/dist/{controllers → resource-managers}/conditions.js +3 -3
  39. package/dist/resource-managers/conditions.js.map +1 -0
  40. package/dist/resource-managers/definition.d.ts +6 -0
  41. package/dist/resource-managers/definition.js +45 -0
  42. package/dist/resource-managers/definition.js.map +1 -0
  43. package/dist/resource-managers/effects.d.ts +15 -0
  44. package/dist/{controllers → resource-managers}/effects.js +2 -2
  45. package/dist/resource-managers/effects.js.map +1 -0
  46. package/dist/resource-managers/errors.d.ts +12 -0
  47. package/dist/resource-managers/errors.js +25 -0
  48. package/dist/resource-managers/errors.js.map +1 -0
  49. package/dist/resource-managers/index.d.ts +12 -0
  50. package/dist/resource-managers/index.js +12 -0
  51. package/dist/resource-managers/index.js.map +1 -0
  52. package/dist/resource-managers/json.js.map +1 -0
  53. package/dist/resource-managers/loader.d.ts +23 -0
  54. package/dist/resource-managers/loader.js +75 -0
  55. package/dist/resource-managers/loader.js.map +1 -0
  56. package/dist/resource-managers/results.d.ts +5 -0
  57. package/dist/resource-managers/results.js.map +1 -0
  58. package/dist/resource-managers/runtime.d.ts +59 -0
  59. package/dist/{controllers/manager.js → resource-managers/runtime.js} +57 -57
  60. package/dist/resource-managers/runtime.js.map +1 -0
  61. package/dist/{controllers → resource-managers}/sqlite.d.ts +44 -44
  62. package/dist/{controllers → resource-managers}/sqlite.js +139 -130
  63. package/dist/resource-managers/sqlite.js.map +1 -0
  64. package/dist/{controllers → resource-managers}/store.d.ts +33 -33
  65. package/dist/{controllers → resource-managers}/store.js.map +1 -1
  66. package/dist/{controllers → resource-managers}/types.d.ts +41 -41
  67. package/dist/{controllers → resource-managers}/types.js.map +1 -1
  68. package/dist/resource-managers/workflows.d.ts +34 -0
  69. package/dist/{controllers → resource-managers}/workflows.js +15 -15
  70. package/dist/resource-managers/workflows.js.map +1 -0
  71. package/dist/server/channel-effects.js.map +1 -0
  72. package/dist/{host → server}/channel-supervisor.d.ts +6 -6
  73. package/dist/{host → server}/channel-supervisor.js +3 -3
  74. package/dist/server/channel-supervisor.js.map +1 -0
  75. package/dist/{host/child-worker-supervisor.d.ts → server/child-runner-supervisor.d.ts} +15 -13
  76. package/dist/{host/child-worker-supervisor.js → server/child-runner-supervisor.js} +6 -5
  77. package/dist/server/child-runner-supervisor.js.map +1 -0
  78. package/dist/{host → server}/processes.d.ts +3 -3
  79. package/dist/{host → server}/processes.js +3 -3
  80. package/dist/server/processes.js.map +1 -0
  81. package/dist/{host → server}/resolver-entry.d.ts +6 -6
  82. package/dist/{host → server}/resolver-entry.js +14 -14
  83. package/dist/server/resolver-entry.js.map +1 -0
  84. package/dist/server/resource-runner-entry.d.ts +2 -0
  85. package/dist/{host/controller-worker-entry.js → server/resource-runner-entry.js} +33 -33
  86. package/dist/server/resource-runner-entry.js.map +1 -0
  87. package/dist/server/resource-runner-protocol.d.ts +36 -0
  88. package/dist/server/resource-runner-protocol.js +49 -0
  89. package/dist/server/resource-runner-protocol.js.map +1 -0
  90. package/dist/server/resource-runner-supervisor.d.ts +21 -0
  91. package/dist/{host/worker-supervisor.js → server/resource-runner-supervisor.js} +17 -17
  92. package/dist/server/resource-runner-supervisor.js.map +1 -0
  93. package/dist/{host → server}/rpc-bridge.d.ts +3 -3
  94. package/dist/{host → server}/rpc-bridge.js +3 -3
  95. package/dist/server/rpc-bridge.js.map +1 -0
  96. package/dist/{host → server}/rpc-executor.d.ts +1 -1
  97. package/dist/{host → server}/rpc-executor.js +3 -3
  98. package/dist/server/rpc-executor.js.map +1 -0
  99. package/dist/{host/host-entry.js → server/server-entry.js} +6 -6
  100. package/dist/server/server-entry.js.map +1 -0
  101. package/dist/{host/runner.d.ts → server/server.d.ts} +39 -38
  102. package/dist/{host/runner.js → server/server.js} +408 -374
  103. package/dist/server/server.js.map +1 -0
  104. package/dist/{host → server}/state.d.ts +25 -25
  105. package/dist/{host → server}/state.js +49 -49
  106. package/dist/server/state.js.map +1 -0
  107. package/dist/{host → server}/view.d.ts +7 -7
  108. package/dist/{host → server}/view.js +13 -13
  109. package/dist/server/view.js.map +1 -0
  110. package/dist/server/workflow-runner-content.d.ts +6 -0
  111. package/dist/server/workflow-runner-content.js +96 -0
  112. package/dist/server/workflow-runner-content.js.map +1 -0
  113. package/dist/{host/worker-entry.d.ts → server/workflow-runner-entry.d.ts} +3 -3
  114. package/dist/{host/worker-entry.js → server/workflow-runner-entry.js} +68 -35
  115. package/dist/server/workflow-runner-entry.js.map +1 -0
  116. package/dist/server/workflow-runner-protocol.d.ts +69 -0
  117. package/dist/server/workflow-runner-protocol.js +176 -0
  118. package/dist/server/workflow-runner-protocol.js.map +1 -0
  119. package/dist/{host/worker-store.d.ts → server/workflow-runner-store.d.ts} +11 -11
  120. package/dist/{host/worker-store.js → server/workflow-runner-store.js} +8 -8
  121. package/dist/server/workflow-runner-store.js.map +1 -0
  122. package/dist/server/workflow-runner-supervisor.d.ts +22 -0
  123. package/dist/server/workflow-runner-supervisor.js +56 -0
  124. package/dist/server/workflow-runner-supervisor.js.map +1 -0
  125. package/dist/state/prune.d.ts +1 -1
  126. package/dist/state/prune.js +17 -10
  127. package/dist/state/prune.js.map +1 -1
  128. package/dist/state/schema.js +2 -2
  129. package/dist/state/workflow-messages.d.ts +1 -1
  130. package/dist/state/workflow-messages.js +1 -1
  131. package/dist/state/workflow-messages.js.map +1 -1
  132. package/dist/viewer/backup.js +1 -1
  133. package/dist/viewer/backup.js.map +1 -1
  134. package/dist/viewer/cli.d.ts +2 -2
  135. package/dist/viewer/cli.js +46 -32
  136. package/dist/viewer/cli.js.map +1 -1
  137. package/dist/viewer/tui.d.ts +1 -1
  138. package/dist/viewer/tui.js +1 -1
  139. package/dist/viewer/tui.js.map +1 -1
  140. package/dist/workflows/command-batch.js +2 -2
  141. package/dist/workflows/engine.js +14 -15
  142. package/dist/workflows/engine.js.map +1 -1
  143. package/dist/workflows/store.d.ts +12 -11
  144. package/dist/workflows/store.js +24 -18
  145. package/dist/workflows/store.js.map +1 -1
  146. package/dist/workflows/types.d.ts +2 -2
  147. package/docs/2026-08-20-durable-workflow-launch-plan.md +12 -12
  148. package/docs/2026-08-25-workflow-follow-ups.md +7 -7
  149. package/docs/2026-08-25-workflow-settings.md +3 -3
  150. package/docs/2026-08-30-out-of-process-workflow-host-plan.md +3 -3
  151. package/docs/2026-09-01-restore-session-delivery-controls-plan.md +5 -5
  152. package/docs/2026-09-01-unified-workflow-client-plan.md +6 -6
  153. package/docs/2026-09-02-installed-live-e2e-plan.md +3 -3
  154. package/docs/2026-09-02-unify-workflow-messages-plan.md +8 -8
  155. package/docs/2026-09-04-workflow-run-state-plan.md +6 -6
  156. package/docs/DEFERRED_TURNS.md +7 -7
  157. package/docs/DESIGN_PHILOSOPHY.md +1 -1
  158. package/docs/HUMAN_DECISIONS.md +11 -11
  159. package/docs/HUMAN_DECISION_PRESENTATIONS.md +4 -4
  160. package/docs/MONITOR.md +1 -1
  161. package/docs/RESOURCE_MANAGERS.md +221 -0
  162. package/docs/SQLITE_STATE.md +35 -33
  163. package/docs/WORKFLOW_COMPOSITION.md +1 -1
  164. package/docs/{WORKFLOW_HOST.md → WORKFLOW_SERVER.md} +125 -119
  165. package/docs/WORKFLOW_STEP_MESSAGES.md +18 -18
  166. package/docs/WORKFLOW_UPDATES.md +16 -16
  167. package/docs/development.md +15 -14
  168. package/docs/live-replay-protocol.md +20 -20
  169. package/docs/plans/2026-08-04-controller-runtime-plan.md +25 -25
  170. package/docs/plans/2026-08-05-always-on-workflows-plan.md +8 -8
  171. package/docs/plans/2026-08-10-agent-managed-monitor-workflows-plan.md +2 -2
  172. package/docs/plans/2026-08-13-session-addressed-workflow-notifications-plan.md +1 -1
  173. package/docs/plans/2026-08-16-workflow-updates-plan.md +5 -5
  174. package/docs/plans/2026-08-19-human-decision-gates-plan.md +1 -1
  175. package/docs/plans/2026-08-20-bounded-command-batches-plan.md +1 -1
  176. package/docs/plans/2026-08-21-deferred-turn-intents-plan.md +3 -3
  177. package/docs/plans/2026-08-21-sanity-check-plan.md +2 -2
  178. package/docs/plans/2026-08-23-assistant-agent-completion-plan.md +5 -5
  179. package/docs/plans/2026-08-23-sqlite-state-plan.md +25 -25
  180. package/docs/plans/2026-08-25-live-workflow-settings-plan.md +4 -4
  181. package/docs/plans/2026-08-27-workflow-terminal-restart-plan.md +3 -3
  182. package/docs/plans/2026-09-04-workflow-runner-resume-state-plan.md +318 -0
  183. package/docs/plans/piw-viewer-experience-implementation-plan.md +1 -1
  184. package/docs/tui-viewer.md +9 -9
  185. package/docs/workflows.md +72 -66
  186. package/examples/{controllers/pull-request.controller.ts → resource-managers/pull-request.resource-manager.ts} +12 -12
  187. package/herdr-plugin.toml +1 -1
  188. package/package.json +5 -5
  189. package/protocol/client.v1.schema.json +7 -7
  190. package/protocol/fixtures/client-v1.json +1 -1
  191. package/src/builtins/monitor.workflow.ts +9 -9
  192. package/src/builtins/pi-agent-group.ts +3 -3
  193. package/src/channels/adapter-entry.ts +14 -14
  194. package/src/client/client.ts +33 -32
  195. package/src/client/protocol.ts +16 -11
  196. package/src/client/resolver.ts +4 -4
  197. package/src/client/view.ts +1 -1
  198. package/src/extension/index.ts +44 -34
  199. package/src/extension/recorder.ts +1 -1
  200. package/src/extension/remote-recorder-store.ts +2 -2
  201. package/src/extension/resource-manager-command.ts +45 -0
  202. package/src/extension/session-delivery.ts +2 -2
  203. package/src/extension/session-view.ts +1 -1
  204. package/src/extension/workflow-message-coordinator.ts +6 -6
  205. package/src/{controllers → resource-managers}/conditions.ts +23 -23
  206. package/src/resource-managers/definition.ts +71 -0
  207. package/src/{controllers → resource-managers}/effects.ts +9 -9
  208. package/src/resource-managers/errors.ts +27 -0
  209. package/src/resource-managers/index.ts +88 -0
  210. package/src/resource-managers/loader.ts +111 -0
  211. package/src/{controllers → resource-managers}/results.ts +4 -4
  212. package/src/{controllers/manager.ts → resource-managers/runtime.ts} +95 -92
  213. package/src/{controllers → resource-managers}/sqlite.ts +198 -186
  214. package/src/{controllers → resource-managers}/store.ts +46 -38
  215. package/src/{controllers → resource-managers}/types.ts +50 -41
  216. package/src/{controllers → resource-managers}/workflows.ts +43 -41
  217. package/src/{host → server}/channel-supervisor.ts +8 -8
  218. package/src/{host/child-worker-supervisor.ts → server/child-runner-supervisor.ts} +22 -18
  219. package/src/{host → server}/processes.ts +3 -3
  220. package/src/{host → server}/resolver-entry.ts +28 -25
  221. package/src/{host/controller-worker-entry.ts → server/resource-runner-entry.ts} +67 -65
  222. package/src/server/resource-runner-protocol.ts +103 -0
  223. package/src/server/resource-runner-supervisor.ts +76 -0
  224. package/src/{host → server}/rpc-bridge.ts +3 -3
  225. package/src/{host → server}/rpc-executor.ts +4 -4
  226. package/src/{host/host-entry.ts → server/server-entry.ts} +5 -5
  227. package/src/{host/runner.ts → server/server.ts} +526 -461
  228. package/src/{host → server}/state.ts +78 -67
  229. package/src/{host → server}/view.ts +15 -15
  230. package/src/server/workflow-runner-content.ts +119 -0
  231. package/src/{host/worker-entry.ts → server/workflow-runner-entry.ts} +98 -57
  232. package/src/server/workflow-runner-protocol.ts +241 -0
  233. package/src/{host/worker-store.ts → server/workflow-runner-store.ts} +18 -20
  234. package/src/server/workflow-runner-supervisor.ts +79 -0
  235. package/src/state/prune.ts +27 -9
  236. package/src/state/schema.ts +2 -2
  237. package/src/state/workflow-messages.ts +1 -1
  238. package/src/viewer/backup.ts +1 -1
  239. package/src/viewer/cli.ts +50 -36
  240. package/src/viewer/tui.ts +1 -1
  241. package/src/workflows/command-batch.ts +2 -2
  242. package/src/workflows/engine.ts +14 -15
  243. package/src/workflows/store.ts +31 -28
  244. package/src/workflows/types.ts +2 -2
  245. package/dist/controllers/conditions.d.ts +0 -6
  246. package/dist/controllers/conditions.js.map +0 -1
  247. package/dist/controllers/definition.d.ts +0 -6
  248. package/dist/controllers/definition.js +0 -45
  249. package/dist/controllers/definition.js.map +0 -1
  250. package/dist/controllers/effects.d.ts +0 -15
  251. package/dist/controllers/effects.js.map +0 -1
  252. package/dist/controllers/errors.d.ts +0 -12
  253. package/dist/controllers/errors.js +0 -25
  254. package/dist/controllers/errors.js.map +0 -1
  255. package/dist/controllers/index.d.ts +0 -12
  256. package/dist/controllers/index.js +0 -12
  257. package/dist/controllers/index.js.map +0 -1
  258. package/dist/controllers/json.js.map +0 -1
  259. package/dist/controllers/loader.d.ts +0 -23
  260. package/dist/controllers/loader.js +0 -74
  261. package/dist/controllers/loader.js.map +0 -1
  262. package/dist/controllers/manager.d.ts +0 -59
  263. package/dist/controllers/manager.js.map +0 -1
  264. package/dist/controllers/results.d.ts +0 -5
  265. package/dist/controllers/results.js.map +0 -1
  266. package/dist/controllers/sqlite.js.map +0 -1
  267. package/dist/controllers/workflows.d.ts +0 -34
  268. package/dist/controllers/workflows.js.map +0 -1
  269. package/dist/extension/controller-command.js.map +0 -1
  270. package/dist/host/channel-effects.js.map +0 -1
  271. package/dist/host/channel-supervisor.js.map +0 -1
  272. package/dist/host/child-worker-supervisor.js.map +0 -1
  273. package/dist/host/controller-worker-entry.d.ts +0 -2
  274. package/dist/host/controller-worker-entry.js.map +0 -1
  275. package/dist/host/controller-worker-protocol.d.ts +0 -36
  276. package/dist/host/controller-worker-protocol.js +0 -49
  277. package/dist/host/controller-worker-protocol.js.map +0 -1
  278. package/dist/host/controller-worker-supervisor.d.ts +0 -21
  279. package/dist/host/controller-worker-supervisor.js +0 -54
  280. package/dist/host/controller-worker-supervisor.js.map +0 -1
  281. package/dist/host/host-entry.js.map +0 -1
  282. package/dist/host/processes.js.map +0 -1
  283. package/dist/host/resolver-entry.js.map +0 -1
  284. package/dist/host/rpc-bridge.js.map +0 -1
  285. package/dist/host/rpc-executor.js.map +0 -1
  286. package/dist/host/runner.js.map +0 -1
  287. package/dist/host/state.js.map +0 -1
  288. package/dist/host/view.js.map +0 -1
  289. package/dist/host/worker-entry.js.map +0 -1
  290. package/dist/host/worker-protocol.d.ts +0 -46
  291. package/dist/host/worker-protocol.js +0 -122
  292. package/dist/host/worker-protocol.js.map +0 -1
  293. package/dist/host/worker-store.js.map +0 -1
  294. package/dist/host/worker-supervisor.d.ts +0 -22
  295. package/dist/host/worker-supervisor.js.map +0 -1
  296. package/docs/CONTROLLERS.md +0 -217
  297. package/src/controllers/definition.ts +0 -65
  298. package/src/controllers/errors.ts +0 -27
  299. package/src/controllers/index.ts +0 -88
  300. package/src/controllers/loader.ts +0 -104
  301. package/src/extension/controller-command.ts +0 -45
  302. package/src/host/controller-worker-protocol.ts +0 -104
  303. package/src/host/controller-worker-supervisor.ts +0 -79
  304. package/src/host/worker-protocol.ts +0 -176
  305. package/src/host/worker-supervisor.ts +0 -74
  306. /package/dist/{controllers → resource-managers}/json.d.ts +0 -0
  307. /package/dist/{controllers → resource-managers}/json.js +0 -0
  308. /package/dist/{controllers → resource-managers}/results.js +0 -0
  309. /package/dist/{controllers → resource-managers}/store.js +0 -0
  310. /package/dist/{controllers → resource-managers}/types.js +0 -0
  311. /package/dist/{host → server}/channel-effects.d.ts +0 -0
  312. /package/dist/{host → server}/channel-effects.js +0 -0
  313. /package/dist/{host/host-entry.d.ts → server/server-entry.d.ts} +0 -0
  314. /package/src/{controllers → resource-managers}/json.ts +0 -0
  315. /package/src/{host → server}/channel-effects.ts +0 -0
@@ -59,7 +59,7 @@ A request uses `pi-workflows.human-decision-request.v1`. It contains:
59
59
  | `title` | Yes | Short decision title shown by every channel. |
60
60
  | `subject` | Yes | Canonical JSON data used by the workflow. |
61
61
  | `presentation` | Yes | Human-readable content defined below. |
62
- | `audience` | Yes | Named audience resolved by the host. |
62
+ | `audience` | Yes | Named audience resolved by the server. |
63
63
  | `choices` | Yes | Typed choices and optional input contracts. |
64
64
  | `revision` | Yes | Positive decision revision. |
65
65
  | `subjectDigest` | Yes | SHA-256 digest of the canonical subject. |
@@ -216,13 +216,13 @@ The renderer:
216
216
 
217
217
  The renderer never adds an ellipsis in place of omitted decision content.
218
218
 
219
- The host saves each channel message before the adapter sends it. It records each part after an unambiguous response. If a send result is uncertain, the host marks that channel message `ambiguous` and does not retry that part or later parts automatically. Another configured channel can still answer the decision.
219
+ The server saves each channel message before the adapter sends it. It records each part after an unambiguous response. If a send result is uncertain, the server marks that channel message `ambiguous` and does not retry that part or later parts automatically. Another configured channel can still answer the decision.
220
220
 
221
221
  ### Pi
222
222
 
223
- The host creates one `decision` workflow message. The shared extension coordinator sends it through documented `pi.sendMessage()` without starting a model turn. Its custom renderer shows the complete presentation and fingerprint with the choices, input rules, request ID, and deadline. It wraps and scrolls with normal Pi custom-message behavior.
223
+ The server creates one `decision` workflow message. The shared extension coordinator sends it through documented `pi.sendMessage()` without starting a model turn. Its custom renderer shows the complete presentation and fingerprint with the choices, input rules, request ID, and deadline. It wraps and scrolls with normal Pi custom-message behavior.
224
224
 
225
- A verified operator answers through `/workflow answer` or the matching `piw` control. When Telegram or another channel accepts the decision, the host cancels an unsent Pi decision message. A sent card remains normal conversation history, while later answer controls return the saved winner. Pi Workflows does not open a blocking dialog, modify Pi core, or use undocumented TUI state.
225
+ A verified operator answers through `/workflow answer` or the matching `piw` control. When Telegram or another channel accepts the decision, the server cancels an unsent Pi decision message. A sent card remains normal conversation history, while later answer controls return the saved winner. Pi Workflows does not open a blocking dialog, modify Pi core, or use undocumented TUI state.
226
226
 
227
227
  ### Other channels
228
228
 
package/docs/MONITOR.md CHANGED
@@ -125,7 +125,7 @@ The action step returns whether the action succeeded, failed, or was blocked, wi
125
125
 
126
126
  The repair input preserves the action request, target evidence, authority, constraints, repository, and delivery limits. Existing plan approval rules still apply when the recorded contract requires them.
127
127
 
128
- Paid workers affected by a shared code or data defect must stop at safe boundaries before repair starts. Monitor preserves their durable outputs and failure evidence.
128
+ Paid runners affected by a shared code or data defect must stop at safe boundaries before repair starts. Monitor preserves their durable outputs and failure evidence.
129
129
 
130
130
  After a completed repair, Monitor runs `observe` immediately. If the same failure ID and target-state ID return, Monitor stops. It does not run the same repair cycle again.
131
131
 
@@ -0,0 +1,221 @@
1
+ # Resource manager specification
2
+
3
+ pi-workflows runs finite TypeScript graphs. A graph starts with an input, follows explicit edges, and ends with a result or checkpoint. This works well for one bounded task.
4
+
5
+ Long-running automation has a different job. It must keep comparing a requested state with the current state of another system. Events can arrive more than once, processes can stop between an external request and its local receipt, and the external state can change while work is running.
6
+
7
+ This specification adds Kubernetes-style resource management to pi-workflows. The resource manager runtime sits beside the graph engine. Resource managers manage durable resources, while workflows remain finite jobs that a resource manager can start and observe.
8
+
9
+ The design follows the Kubernetes [controller pattern](https://kubernetes.io/docs/concepts/architecture/controller/), its [`spec` and `status` split](https://kubernetes.io/docs/concepts/overview/working-with-objects/), and the [idempotent reconciliation guidance](https://book.kubebuilder.io/reference/good-practices).
10
+
11
+ ## Scope
12
+
13
+ The resource manager runtime provides:
14
+
15
+ - Durable desired and observed state.
16
+ - Level-based reconciliation from current facts.
17
+ - A deduplicated work queue with delayed retries.
18
+ - Compare-and-swap writes for concurrent resource runners.
19
+ - Recoverable records for external effects.
20
+ - Child workflow runs with stable request keys.
21
+ - Conditions and generations, with cleanup and structured events.
22
+ - Local resource control through the Pi extension and reconciliation through the global server.
23
+
24
+ The first production use case is pull request automation. A resource manager can observe a pull request, start a review or repair workflow, wait for checks, validate the current head, and apply an approved change through deterministic code.
25
+
26
+ ## Boundaries
27
+
28
+ The graph engine remains the execution layer for finite work. A finite workflow can include another finite workflow in the same run through `includeWorkflow()`. Use a resource manager child run when work needs an independent retry history, stable request key, parallel lifecycle, or indefinite reconciliation. The graph engine does not import the resource manager runtime. The resource manager runtime may start workflows through a narrow scheduler interface.
29
+
30
+ The Pi extension is a thin client. It resolves resource manager initialization in a dedicated child process and sends declarative resource commands to the one global server. The server schedules reconciliation but does not load resource manager definitions in its event loop. A supervised resource runner loads one definition and proposes bounded state changes through the server.
31
+
32
+ External events are wake-up hints. An event enqueues a resource key and carries no transition command. The reconciler reloads the resource and the external system before deciding what to do.
33
+
34
+ ## Resource model
35
+
36
+ A managed resource is the durable record of one requested outcome. The resource manager owns `status`; callers own `spec`.
37
+
38
+ ```ts
39
+ export type ManagedResource<TSpec, TStatus> = {
40
+ metadata: {
41
+ uid: string;
42
+ resourceManager: string;
43
+ key: string;
44
+ resourceVersion: number;
45
+ generation: number;
46
+ deletionTimestamp?: string;
47
+ finalizers: string[];
48
+ };
49
+ spec: TSpec;
50
+ status: {
51
+ observedGeneration: number;
52
+ conditions: ManagedResourceCondition[];
53
+ workflowRun?: {
54
+ requestId: string;
55
+ runId?: string;
56
+ state: "pending" | "running" | "waiting" | "succeeded" | "failed" | "interrupted";
57
+ attempt: number;
58
+ };
59
+ resourceManagerStatus: TStatus;
60
+ };
61
+ };
62
+
63
+ export type ManagedResourceCondition = {
64
+ type: string;
65
+ status: true | false | "unknown";
66
+ reason: string;
67
+ message?: string;
68
+ observedGeneration: number;
69
+ lastTransitionTime: string;
70
+ };
71
+ ```
72
+
73
+ `uid` stays stable for the life of the resource and is never reused. `resourceVersion` changes after every write and acts as the compare-and-swap token. `generation` changes only when `spec` changes. A condition describes the latest known state for one stable condition type.
74
+
75
+ `observedGeneration` shows which desired state produced the current status. A resource manager must not report a resource as ready when its conditions came from an older generation.
76
+
77
+ ## Resource manager contract
78
+
79
+ A resource manager receives the latest resource, a cancellation signal, and runtime services. It returns after one bounded reconciliation pass.
80
+
81
+ ```ts
82
+ import {
83
+ conditionFalse,
84
+ conditionTrue,
85
+ defineResourceManager,
86
+ } from "@osolmaz/pi-workflows/resource-managers";
87
+
88
+ export default defineResourceManager<PullRequestSpec, PullRequestStatus>({
89
+ name: "pull-request",
90
+ initialStatus: () => ({ phase: "observing" }),
91
+
92
+ async reconcile(ctx, resource) {
93
+ const pullRequest = await github.getPullRequest(resource.spec, ctx.signal);
94
+
95
+ if (pullRequest.merged) {
96
+ return ctx.settled({
97
+ resourceManagerStatus: { phase: "merged" },
98
+ conditions: [conditionTrue("Ready", "Merged")],
99
+ });
100
+ }
101
+ if (pullRequest.headSha !== resource.spec.expectedHeadSha) {
102
+ return ctx.settled({
103
+ resourceManagerStatus: { phase: "blocked" },
104
+ conditions: [conditionFalse("Ready", "HeadChanged")],
105
+ });
106
+ }
107
+
108
+ const run = await ctx.workflows.ensure({
109
+ requestKey: `repair:${resource.metadata.generation}:${pullRequest.headSha}`,
110
+ workflow: "repair-pull-request",
111
+ input: { repository: resource.spec.repository, number: pullRequest.number },
112
+ });
113
+ if (run.state !== "succeeded") {
114
+ return ctx.requeueAfter(30_000);
115
+ }
116
+
117
+ await ctx.effects.ensure({
118
+ key: `merge:${resource.metadata.generation}:${pullRequest.headSha}`,
119
+ kind: "github-merge",
120
+ request: { number: pullRequest.number, expectedHeadSha: pullRequest.headSha },
121
+ observe: (signal) => github.observeMerge(resource.spec, signal),
122
+ apply: (signal) => github.merge(resource.spec, pullRequest.headSha, signal),
123
+ });
124
+ return ctx.requeue();
125
+ },
126
+ });
127
+ ```
128
+
129
+ The runtime supports three normal results. `settled` removes the key from the queue until another event arrives. `requeue` asks for another pass as soon as capacity is available. `requeueAfter` schedules a later pass.
130
+
131
+ A returned error receives exponential backoff with jitter. A resource manager records a durable condition and returns `settled` for a problem that requires new input. Reconciliation retries do not depend on the event that caused the first attempt.
132
+
133
+ ## Work queue
134
+
135
+ The queue contains one row for each resource manager and resource key. Repeated enqueue calls update that row instead of adding copies. A resource runner claims a key with an opaque claim token and an expiry time. The runtime prevents concurrent reconciliation of the same key.
136
+
137
+ A claim that expires returns to the queue. A successful settled result removes the queue row. A requested delay updates its available time. Consecutive errors increase an internal retry counter used for backoff.
138
+
139
+ The local implementation uses `better-sqlite3` in WAL mode. Transactions cover resource compare-and-swap writes, queue claims, and effect claims. `ResourceManagerStore` remains an interface so another server can supply a remote implementation. Pi servers use a store scoped to the canonical project directory, which prevents a same-named resource manager in another project from claiming its resources. The store limits each resource spec and status value to 1 MiB. Event payloads are limited to 64 KiB.
140
+
141
+ The resource store is the source of truth. Queue rows only describe delivery. A repair can rebuild the queue by enqueuing every resource; each reconciler then computes any needed delay again.
142
+
143
+ ## External effects
144
+
145
+ An external effect can succeed while the local process is unable to save the response. The runtime records each effect before calling the provider.
146
+
147
+ ```ts
148
+ export type EffectRecord = {
149
+ key: string;
150
+ resourceUid: string;
151
+ generation: number;
152
+ requestFingerprint: string;
153
+ state: "pending" | "applied" | "rejected" | "indeterminate";
154
+ externalRef?: string;
155
+ startedAt: string;
156
+ completedAt?: string;
157
+ };
158
+ ```
159
+
160
+ The key names one intended effect. Reusing the key with another request fingerprint is an error. The next reconciliation observes the external system before retrying an existing pending or indeterminate effect. The effect can be treated as effectively once when the provider offers an idempotency token, a conditional request, or a reliable way to observe the requested result. The runtime does not promise generic exactly-once execution.
161
+
162
+ Mutation policy stays in deterministic effect drivers. Agent workflows return findings or artifacts for deterministic code to check and apply. Runner processes run as the same operating-system user and are not credential sandboxes. Deployments that require credential isolation should put authenticated effects behind a separate broker.
163
+
164
+ ## Child workflows
165
+
166
+ `ctx.workflows.ensure()` creates or finds a workflow run by a stable request key and input fingerprint. Repeated reconciliations find the same active or completed request. A changed input must use a new key. An asynchronous child completion validates the reserved request and run IDs through a separate scheduler-completion command; it never reuses the resource manager claim that launched the child. The resource manager transaction reserves and saves each attempt's run ID before the scheduler starts it, so recovery can find the run row.
167
+
168
+ A child run has one durable execution record. The parent resource points to the current run, and workflow completion enqueues the parent key. The global server runs it through the same queue and supervised runner protocol as other workflows.
169
+
170
+ A stopped runner does not make the child failed by itself. The server reads the last committed node and effect state. It resumes pure or idempotent work in a new runner epoch. An uncertain manual effect becomes ambiguous and blocks automatic retry. A changed input still requires a new stable request key.
171
+
172
+ ## Deletion and cleanup
173
+
174
+ Setting `deletionTimestamp` requests deletion. A resource manager with a finalizer first removes external resources it owns, then removes its finalizer. The store deletes the resource after the finalizer list becomes empty.
175
+
176
+ Resource managers should add finalizers only when they own something that needs cleanup, such as an isolated worktree or a remote action session. Ordinary completed resources can remain as history or be removed by a separate retention policy.
177
+
178
+ ## Sources and runners
179
+
180
+ A source maps an external event to one or more resource keys. Sources include filesystem watches, webhooks, scheduled polling, and child workflow completion. They share the same enqueue API.
181
+
182
+ The global server claims resource manager keys and starts a supervised resource runner for each active reconciliation. A reconciliation deadline stops and requeues the child even when resource manager code ignores its abort signal. Reconciler code must still pass the signal to provider calls and keep consequential writes inside guarded effect drivers. One server owns the local database. Distributed leader election remains outside this local runtime.
183
+
184
+ The extension starts the package server on demand. Reconciliation continues when the Pi session closes. The package installs no operating-system service.
185
+
186
+ ## Observability
187
+
188
+ Every reconciliation emits structured records with the resource manager name, resource key, generation, reconcile ID, outcome and duration, plus the requeue reason. Effect state changes and child workflow links are also recorded. Logs and viewer projections remain secondary to the resource and effect stores.
189
+
190
+ `pi-workflows resource-managers` lists resources and their current readiness condition. `pi-workflows resource-manager <resource-manager> <key>` prints one resource together with its effects, child workflows, and recent events. Run views read the same database through query-only connections.
191
+
192
+ ## Safety rules
193
+
194
+ A production resource manager must follow these rules:
195
+
196
+ - Read current external state on every reconciliation.
197
+ - Check authorization and target boundaries in deterministic code.
198
+ - Use provider-side preconditions for consequential writes when available.
199
+ - Save status with the resource version that was read.
200
+ - Reconcile again after each consequential external effect.
201
+ - Keep model output separate from mutation authority.
202
+ - Bound runner counts and retry rates. Also bound timeouts and stored payload sizes.
203
+ - Redact credentials and private provider responses from logs and SQLite runs.
204
+
205
+ ## Package and Pi integration
206
+
207
+ The resource manager API is exported from `@osolmaz/pi-workflows/resource-managers`. Resource manager definitions use a `.resource-manager.ts` suffix. Project definitions live under `.pi/resource-managers/`; global definitions live under `~/.pi/agent/resource-managers/`.
208
+
209
+ The implementation uses documented Pi extension APIs only. `/resource-manager` lists and inspects resources, applies specs, and requests reconciliation or deletion. There are no extension-local runner start or stop controls.
210
+
211
+ For `apply`, a source resolver child discovers the named resource manager, verifies its exported name, computes `initialStatus(spec)`, and hashes the source. The server accepts the proposal only while the path still follows discovery rules and the exact digest still matches. Reconcile code runs only in a supervised resource runner.
212
+
213
+ Managed resources use the canonical [SQLite state](SQLITE_STATE.md) database. `projects` separates repository-local resources by canonical project path. ResourceManager claims use the shared lease generation, token, expiry, and expected resource revision. Effects and child workflows use durable request keys and receipts.
214
+
215
+ One global server owns that database for all Pi sessions and projects. The extension and mutating CLI paths are local protocol clients; viewers remain read-only. A resource manager child without an origin session uses a headless `pi --mode rpc` process for structured agent steps.
216
+
217
+ Normal workflow prompts, tool calls, and replies remain part of the Pi session. No Pi internal type, private API, or persistent Pi schema changes.
218
+
219
+ ## Exclusions
220
+
221
+ This specification does not add Kubernetes API compatibility, YAML resources, a cluster scheduler, or a general distributed database. GitHub policy and credentials belong in a provider adapter, leaving the resource manager core independent of GitHub. The first release also excludes automatic service installation and generic exactly-once claims.
@@ -1,6 +1,6 @@
1
1
  # SQLite state
2
2
 
3
- Status: this is the implemented single-host database contract. The [workflow-message plan](2026-09-02-unify-workflow-messages-plan.md) records the schema version 1 hard cut that unified Pi message state and restored hosted behavior.
3
+ Status: this is the implemented single-server database contract. The [workflow-message plan](2026-09-02-unify-workflow-messages-plan.md) records the schema version 1 hard cut that unified Pi message state and restored hosted behavior.
4
4
 
5
5
  Pi Workflows stores all live durable state in one database:
6
6
 
@@ -8,11 +8,11 @@ Pi Workflows stores all live durable state in one database:
8
8
  ~/.pi/agent/workflows/state.sqlite
9
9
  ```
10
10
 
11
- There is one database for the user installation. Project and run IDs separate data inside it. The host is the only production process that opens this live database. Workflow targets, extensions, CLI clients, Herdr adapters, and `piw` do not open it. Live clients use `pi-workflows.client.v1`.
11
+ There is one database for the user installation. Project and run IDs separate data inside it. The server is the only production process that opens this live database. Workflow targets, extensions, CLI clients, Herdr adapters, and `piw` do not open it. Live clients use `pi-workflows.client.v1`.
12
12
 
13
13
  ## Viewer projection
14
14
 
15
- The database includes the [incremental and virtualized viewer design](plans/2026-08-28-piw-incremental-viewer-plan.md). The host owns this projection and exposes it as the canonical live run view. Local and remote renderers do not recreate it or validate its SQLite tables.
15
+ The database includes the [incremental and virtualized viewer design](plans/2026-08-28-piw-incremental-viewer-plan.md). The server owns this projection and exposes it as the canonical live run view. Local and remote renderers do not recreate it or validate its SQLite tables.
16
16
 
17
17
  `viewer_runs` stores one presentation revision and retained revision floor for each run. `viewer_deltas` stores ordered target patches by run, presentation revision, and delta index. `viewer_session_checkpoints` stores the bounded active message and tool state at each 256-event boundary. `run_view_content` stores generated reference bytes under the exact run ID, content digest, and media type. It is separate from general state blobs, and content reads require all three identities. A viewer-visible transaction writes the domain change, advances the presentation revision, and writes its patch blobs before the same commit. Session-event transactions write each reached replay checkpoint in that transaction.
18
18
 
@@ -27,12 +27,12 @@ This is an in-place alpha schema change. The schema name and version remain `pi-
27
27
  The database stores:
28
28
 
29
29
  - workflow definitions, runs, events, node attempts, outputs, and updates
30
- - global host epochs, command receipts, worker epochs, and worker messages
30
+ - global server epochs, command receipts, runner epochs, and runner messages
31
31
  - durable origin-session interaction requests and submissions
32
32
  - captured Pi session entries and events
33
- - run and controller queues, claims, retries, and continuations
33
+ - run and resource manager queues, claims, retries, and continuations
34
34
  - human-decision requests, submissions, resolutions, and cancellations
35
- - controller resources, finalizers, effects, and child workflows
35
+ - managed resources, finalizers, effects, and child workflows
36
36
  - workflow messages that Pi must add to origin conversations
37
37
  - workflow settings, accepted JSON Patch changes, and post-completion follow-up prompts
38
38
  - nonsecret channel cursors, inbox records, messages, and settlement receipts
@@ -40,7 +40,7 @@ The database stores:
40
40
 
41
41
  Credentials and raw secrets must not enter the database. Channel credential files stay in their existing private configuration directory.
42
42
 
43
- The implementation does not create live run directories, artifact files, decision directories, project databases, controller databases, or channel databases.
43
+ The implementation does not create live run directories, artifact files, decision directories, project databases, resource manager databases, or channel databases.
44
44
 
45
45
  ## Database settings
46
46
 
@@ -62,9 +62,9 @@ The database also uses:
62
62
  - directory mode `0700`
63
63
  - database and backup mode `0600`
64
64
 
65
- The host opens the active database. It verifies the application ID, user version, schema metadata, compiled DDL digest, and exact SQLite schema shape. An incompatible database fails with the standard backup-and-reset instruction. Pi Workflows does not import, reinterpret, or delete that state.
65
+ The server opens the active database. It verifies the application ID, user version, schema metadata, compiled DDL digest, and exact SQLite schema shape. An incompatible database fails with the standard backup-and-reset instruction. Pi Workflows does not import, reinterpret, or delete that state.
66
66
 
67
- The host completes this verification before it serves any client, and no other production process opens the active database. A maintenance verifier may open an explicit inactive backup with SQLite read-only mode and `PRAGMA query_only = ON`. That offline verification path is not a live client and cannot select the active state database. TypeScript and Rust clients validate the client protocol and package versions, not the SQLite DDL digest.
67
+ The server completes this verification before it serves any client, and no other production process opens the active database. A maintenance verifier may open an explicit inactive backup with SQLite read-only mode and `PRAGMA query_only = ON`. That offline verification path is not a live client and cannot select the active state database. TypeScript and Rust clients validate the client protocol and package versions, not the SQLite DDL digest.
68
68
 
69
69
  The normalized run layout is an in-place alpha cutover. It keeps SQLite user version `1` and the current `v1` public record identifiers. A database with the former nested run-snapshot layout is incompatible and must be moved or removed. There is no migration, compatibility reader, dual write, alias, or second schema generation.
70
70
 
@@ -74,7 +74,7 @@ Four record groups provide the common lifecycle rules.
74
74
 
75
75
  ### Resources
76
76
 
77
- `resources` identifies each mutable aggregate and holds its current revision. Runs, settings scopes, follow-up items, decisions, controller resources, effects, channels, workflow messages, and session segments have stable resource identities.
77
+ `resources` identifies each mutable aggregate and holds its current revision. Runs, settings scopes, follow-up items, decisions, managed resources, effects, channels, workflow messages, and session segments have stable resource identities.
78
78
 
79
79
  Every accepted domain command compares its expected revision and increments it once.
80
80
 
@@ -104,9 +104,9 @@ A domain row and its event are written in one transaction. Normal APIs never upd
104
104
 
105
105
  `effect_attempts` records each application attempt and ownership generation. A matching repeated request adopts the existing effect. A different request under the same key is a conflict.
106
106
 
107
- Local effects use deterministic transactions. For workflow action nodes, the engine creates the idempotency key from the run ID, effect type, full compiled node path, and node visit number. A projected child-workflow view cannot replace that full identity. Run queue settlement effects are created only for runs that have a `run_queue` row; direct engine and controller-child runs do not create phantom queue work. External effects use provider idempotency or observation when available. An uncertain result becomes `ambiguous` and is not repeated without evidence.
107
+ Local effects use deterministic transactions. For workflow action nodes, the engine creates the idempotency key from the run ID, effect type, full compiled node path, and node visit number. A projected child-workflow view cannot replace that full identity. Run queue settlement effects are created only for runs that have a `run_queue` row; direct engine and resource-manager child runs do not create phantom queue work. External effects use provider idempotency or observation when available. An uncertain result becomes `ambiguous` and is not repeated without evidence.
108
108
 
109
- Telegram delivery and settlement use these shared effect records. The host records one numbered attempt before it tells the supervised adapter child to act. A confirmed result stores Telegram message references in the effect result. `channel_messages` stores the decision feature's delivery or settlement receipt; it does not copy effect state or external message references.
109
+ Telegram delivery and settlement use these shared effect records. The server records one numbered attempt before it tells the supervised adapter child to act. A confirmed result stores Telegram message references in the effect result. `channel_messages` stores the decision feature's delivery or settlement receipt; it does not copy effect state or external message references.
110
110
 
111
111
  ## Domain tables
112
112
 
@@ -117,34 +117,36 @@ The shared records do not replace domain schemas. The following `STRICT` tables
117
117
  | Schema and projects | `schema_meta`, `projects` |
118
118
  | Content | `blobs`, `run_view_content` |
119
119
  | Shared lifecycle | `resources`, `leases`, `events`, `workflow_host_state` |
120
- | Host protocol | `host_commands`, `run_workers`, `worker_messages`, `interactive_requests`, `interactive_submissions` |
120
+ | Server protocol | `host_commands`, `run_workers`, `worker_messages`, `interactive_requests`, `interactive_submissions` |
121
121
  | Workflows | `workflow_definitions`, `runs`, `run_sources`, `run_steps`, `run_bindings`, `run_queue`, `node_attempts`, `workflow_updates` |
122
122
  | Live settings | `workflow_settings`, `workflow_setting_changes` |
123
123
  | Post-run follow-ups | `workflow_follow_ups` |
124
124
  | Session capture | `session_segments`, `session_entries`, `attempt_entries`, `session_events` |
125
125
  | Human decisions | `human_decisions`, `human_decision_resolutions`, `human_decision_submissions`, `continuations` |
126
- | Controllers | `controller_resources`, `controller_finalizers`, `controller_queue`, `controller_workflows` |
126
+ | Managed resources | `controller_resources`, `controller_finalizers`, `controller_queue`, `controller_workflows` |
127
127
  | Effects | `effects`, `effect_attempts` |
128
128
  | Pi messages | `workflow_messages`, `workflow_turns` |
129
129
  | Channels | `channels`, `channel_cursors`, `channel_messages` |
130
130
 
131
+ The `host_*`, `run_workers`, `worker_*`, and `controller_*` names remain version-1 internal SQLite identifiers. The `host` and `controller` actor and owner values and the `~/.pi/agent/workflows/host/` state directory also remain internal identifiers. Public APIs and documentation call these components the workflow server, workflow runner, resource manager, resource runner, and managed resource. The alpha hard cut adds no alias or second storage path.
132
+
131
133
  `workflow_messages` is the only table that owns adding workflow content to Pi. It stores the target session, message kind, source record, content digest, session order, `pending`, `sent`, or `cancelled` state, confirmed Pi entry ID, and creation and update times. The table stores no sender, send lease, `sending` state, or separate sent time. Active-branch evidence changes `pending` or `cancelled` to `sent`. Initial, reminder, and resumed prompts are all `step` messages; their custom details contain the reason. Interactive requests, decisions, terminal runs, notifications, follow-ups, and settings keep their own domain state.
132
134
 
133
- `workflow_turns` stores the host-approved ownership of one Pi model turn. Each row names the exact workflow message, run, session, and turn ID. A partial unique index permits only one open turn for a message. The host checks for an exact saved turn or another open turn before insertion, so a normal conflict returns a controlled protocol error instead of a raw SQLite error. Terminalization ends every open turn for that run as `lost` in the same transaction. It cancels pending step and decision messages, plus follow-ups when the run did not complete successfully. Committed notifications remain eligible. Matching late reports adopt the saved result, while conflicting identities remain errors.
135
+ `workflow_turns` stores the server-approved ownership of one Pi model turn. Each row names the exact workflow message, run, session, and turn ID. A partial unique index permits only one open turn for a message. The server checks for an exact saved turn or another open turn before insertion, so a normal conflict returns a controlled protocol error instead of a raw SQLite error. Terminalization ends every open turn for that run as `lost` in the same transaction. It cancels pending step and decision messages, plus follow-ups when the run did not complete successfully. Committed notifications remain eligible. Matching late reports adopt the saved result, while conflicting identities remain errors.
134
136
 
135
137
  `channels` stores configured channel resource identities. `channel_cursors` stores the last accepted external polling position. `channel_messages` stores immutable decision delivery and settlement records for audit and duplicate evidence. External application state and Telegram message references belong to `effects` and `effect_attempts`.
136
138
 
137
- Foreign keys join projects, runs, attempts, decisions, controllers, effects, and channel records. Partial unique indexes enforce one active node attempt per run, one pending step message per interaction request, one nonterminal interactive continuation-chain reservation per Pi session, one decision winner, and one deterministic effect key. A run waiting for a checkpoint or protected decision keeps that chain reservation. A parked waiting parent does not block its own continuation. Reserving that continuation transfers the reservation and settles the parked parent queue in the same transaction, so a failed reservation leaves the parent recoverable.
139
+ Foreign keys join projects, runs, attempts, decisions, managed resources, effects, and channel records. Partial unique indexes enforce one active node attempt per run, one pending step message per interaction request, one nonterminal interactive continuation-chain reservation per Pi session, one decision winner, and one deterministic effect key. A run waiting for a checkpoint or protected decision keeps that chain reservation. A parked waiting parent does not block its own continuation. Reserving that continuation transfers the reservation and settles the parked parent queue in the same transaction, so a failed reservation leaves the parent recoverable.
138
140
 
139
- ### Hosted commands and interactions
141
+ ### ServerBacked commands and interactions
140
142
 
141
- `workflow_host_state` stores the one current host epoch and its live local
143
+ `workflow_host_state` stores the one current server epoch and its live local
142
144
  claim. `host_commands` stores each client request fingerprint, operation,
143
145
  outcome, revision, and receipt or error. Repeating an exact request adopts the
144
146
  stored receipt. Reusing an ID or idempotency key for another request is a
145
147
  conflict.
146
148
 
147
- `run_workers` records each worker epoch before spawn and later records its exact
149
+ `run_workers` records each runner epoch before spawn and later records its exact
148
150
  process identity and terminal outcome. `worker_messages` deduplicates accepted
149
151
  state-changing child messages.
150
152
 
@@ -152,9 +154,9 @@ state-changing child messages.
152
154
 
153
155
  ## Content-addressed values
154
156
 
155
- `blobs` stores canonical JSON and UTF-8 text as bytes. Its primary key is the 32-byte SHA-256 digest of the bytes. `run_view_content` keeps host-generated large view values reachable for the life of the run, including aggregate outputs that do not exist as one source record. The host creates this link before it sends a content reference. Deleting the run removes the link, and normal blob pruning can then remove unreferenced content.
157
+ `blobs` stores canonical JSON and UTF-8 text as bytes. Its primary key is the 32-byte SHA-256 digest of the bytes. An oversized required runner result uses this same content-addressed store, and the runner reads and verifies it in bounded parts. It does not copy session history into runner resume state. `run_view_content` separately keeps server-generated large view values reachable for the life of the run, including aggregate outputs that do not exist as one source record. The server creates this link before it sends a content reference. Deleting the run removes the link, and normal blob pruning can then remove unreferenced content.
156
158
 
157
- Insertion verifies the digest, media type, byte length, and exact bytes. Repeated content adopts the existing row. This replaces separate artifact files while keeping outputs, errors, settled Pi entries, and rendered channel text deduplicated. Opening the database never deletes blobs. The explicit prune command removes unreferenced blobs after it deletes safe old run trees.
159
+ Insertion verifies the digest, media type, byte length, and exact bytes. Repeated content adopts the existing row. This replaces separate artifact files while keeping outputs, errors, settled Pi entries, and rendered channel text deduplicated. Opening the database never deletes blobs. The explicit prune command removes unreferenced blobs after it deletes safe old run trees. It retains each blob referenced by an active runner transfer until that runner exits.
158
160
 
159
161
  Runs do not store a nested `WorkflowRunState` blob. `runs` stores run-level facts and hashes for independent values. `run_sources` stores source identity without source JSON blobs. `node_attempts` stores structured workflow outputs and small execution receipts. `session_entries` is the only stored copy of each settled Pi entry. `attempt_entries` links an attempt to its prompt, response, first, and last Pi entries. `run_steps` stores ordered attempt membership and only stores an output override when a continuation changes a carried checkpoint answer.
160
162
 
@@ -168,7 +170,7 @@ An agent definition records `expectedOutput` as either a submitted-output descri
168
170
 
169
171
  A completed interactive assistant-message attempt stores the accepted visible text as its node output. Its small receipt keeps the text digest, final Pi session entry ID, optional author-supplied limit, and whether recovery adopted an existing response.
170
172
 
171
- An interrupted assistant-message attempt keeps its attempt ID. The extension adopts a matching durable request and existing Pi branch entry instead of displaying the prompt or accepting the response twice. Submitted and non-agent attempts use a fresh execution attempt after an uncommitted worker exit.
173
+ An interrupted assistant-message attempt keeps its attempt ID. The extension adopts a matching durable request and existing Pi branch entry instead of displaying the prompt or accepting the response twice. Submitted and non-agent attempts use a fresh execution attempt after an uncommitted runner exit.
172
174
 
173
175
  ## Write contract
174
176
 
@@ -201,13 +203,13 @@ A TypeScript write permit carries the expected facts between layers. It is not a
201
203
  Reading or finding a row never gives write authority.
202
204
 
203
205
  - A run owner may advance the run, apply automatic decision policy, create its continuation, settle its parent, and complete its queue work.
204
- - A controller claim owner may update controller status, reserve effects, and start child workflows for that resource.
206
+ - A resource manager claim owner may update resource manager status, reserve effects, and start child workflows for that resource.
205
207
  - A verified human channel actor may submit one answer candidate for the named decision. It does not gain run ownership.
206
- - The host-owned channel adapter path may update only its channel cursor, decision delivery and settlement records, and exact managed effects.
208
+ - The server-owned channel adapter path may update only its channel cursor, decision delivery and settlement records, and exact managed effects.
207
209
  - Control commands have narrow explicit operations, such as requesting cancellation or deletion.
208
210
  - Model-originated workflow answers cannot resolve protected human decisions.
209
211
 
210
- The global host is the sole live database owner and the normal state writer. Its protected stores check ownership and renew the exact live claim in the same transaction as the write. A stale or expired owner cannot renew itself. Pi extensions, CLI commands, Herdr adapters, and the Rust `piw` program use the versioned client protocol for live reads and controls. They do not open the active database. Only explicit inactive backup verification remains a direct read-only SQLite operation.
212
+ The global server is the sole live database owner and the normal state writer. Its protected stores check ownership and renew the exact live claim in the same transaction as the write. A stale or expired owner cannot renew itself. Pi extensions, CLI commands, Herdr adapters, and the Rust `piw` program use the versioned client protocol for live reads and controls. They do not open the active database. Only explicit inactive backup verification remains a direct read-only SQLite operation.
211
213
 
212
214
  ## Competing outcomes
213
215
 
@@ -219,15 +221,15 @@ A deadline with a validated default response is timeout-policy acceptance. It ca
219
221
 
220
222
  Late or repeated commands return or adopt the durable winner. They do not overwrite it.
221
223
 
222
- The same rule applies to run terminal outcomes, continuation admission, queue settlement, controller effects, retry scheduling, channel settlement, and workflow-turn reports through their domain constraints and expected revisions. A matching turn report adopts the saved ownership result. A different report for the same turn ID remains a conflict.
224
+ The same rule applies to run terminal outcomes, continuation admission, queue settlement, resource manager effects, retry scheduling, channel settlement, and workflow-turn reports through their domain constraints and expected revisions. A matching turn report adopts the saved ownership result. A different report for the same turn ID remains a conflict.
223
225
 
224
226
  ## Read contract
225
227
 
226
- Durable status is a pure projection of domain rows, immutable facts, current leases, effect results, and exact workflow-turn start and end reports. The host uses these facts to produce one live run view. An open workflow turn can change display status only. It cannot change workflow authority. Every renderer consumes the host-produced display status and allowed controls without running another status reducer.
228
+ Durable status is a pure projection of domain rows, immutable facts, current leases, effect results, and exact workflow-turn start and end reports. The server uses these facts to produce one live run view. An open workflow turn can change display status only. It cannot change workflow authority. Every renderer consumes the server-produced display status and allowed controls without running another status reducer.
227
229
 
228
230
  A settings scope uses its resource revision as its public change number. Each accepted patch, current value, and node binding is saved in one transaction. A checkpoint continuation keeps the same settings resources and transfers them to the continuation run.
229
231
 
230
- `workflow_follow_ups` records source acceptance order, removal, and cancellation. The source and message stay attached to the continuation-chain member that accepted them; rows are not rewritten when the chain continues. The host walks the chain to find its final outcome. `workflow_messages` owns message state and Pi entry evidence. Failure, timeout, and cancellation cancel unsent follow-up messages.
232
+ `workflow_follow_ups` records source acceptance order, removal, and cancellation. The source and message stay attached to the continuation-chain member that accepted them; rows are not rewritten when the chain continues. The server walks the chain to find its final outcome. `workflow_messages` owns message state and Pi entry evidence. Failure, timeout, and cancellation cancel unsent follow-up messages.
231
233
 
232
234
  - A terminal run fact overrides stale message state and has no open workflow turn.
233
235
  - An accepted decision is accepted even if its continuation effect is still pending.
@@ -239,9 +241,9 @@ Read paths do not repair state. Owner reconcilers apply pending effects and writ
239
241
 
240
242
  ## Projects and concurrency
241
243
 
242
- All projects use the same file. `projects` stores a stable ID and canonical path. Project-scoped controller and run queries use that key. One global host owns the file for the user installation. Its socket, lock, and exact child-process registry are under `~/.pi/agent/workflows/host/`. A second live host is rejected even when it was started from another project.
244
+ All projects use the same file. `projects` stores a stable ID and canonical path. Project-scoped resource manager and run queries use that key. One global server owns the file for the user installation. Its socket, lock, and exact child-process registry are under `~/.pi/agent/workflows/host/`. A second live server is rejected even when it was started from another project.
243
245
 
244
- SQLite WAL keeps bounded projection reads consistent with commits. Writers are serialized by SQLite and must keep transactions short. Hashing, model calls, shell work, and external requests happen outside write transactions. Production clients receive revisioned snapshots, patches, and pages from the host instead of opening concurrent SQLite readers.
246
+ SQLite WAL keeps bounded projection reads consistent with commits. Writers are serialized by SQLite and must keep transactions short. Hashing, model calls, shell work, and external requests happen outside write transactions. Production clients receive revisioned snapshots, patches, and pages from the server instead of opening concurrent SQLite readers.
245
247
 
246
248
  This contract is for local storage on one machine. It does not claim distributed consensus or network-filesystem safety.
247
249
 
@@ -268,15 +270,15 @@ pi-workflows state prune --before 2026-08-01T00:00:00Z --dry-run
268
270
  pi-workflows state prune --before 2026-08-01T00:00:00Z --backup /absolute/path/to/before-prune.sqlite --apply
269
271
  ```
270
272
 
271
- These commands send maintenance operations to the host when they target the active database. Only `pi-workflows state verify` with an explicit inactive backup opens SQLite in the command process. It rejects the active database, including another path to the same file.
273
+ These commands send maintenance operations to the server when they target the active database. Only `pi-workflows state verify` with an explicit inactive backup opens SQLite in the command process. It rejects the active database, including another path to the same file.
272
274
 
273
275
  `status` reports only safe counts, file size, active leases, and unsettled effects.
274
276
  It does not print actor IDs, channel references, payloads, or credentials.
275
277
 
276
- `prune --dry-run` reports complete terminal run trees older than the cutoff and the trees that safety checks block. It does not change the database. `prune --apply` requires a new absolute backup path. It verifies the backup, locks maintenance, rechecks the same selection in an exclusive transaction, and refuses trees with live queues, active leases, unsettled effects, controller references, channel references, or step links from runs outside the tree. It deletes the safe aggregates, removes blobs with no remaining foreign-key reference, checkpoints the WAL, vacuums the file, and runs integrity and foreign-key checks. Pi Workflows never runs prune at startup.
278
+ `prune --dry-run` reports complete terminal run trees older than the cutoff and the trees that safety checks block. It does not change the database. `prune --apply` requires a new absolute backup path. It verifies the backup, locks maintenance, rechecks the same selection in an exclusive transaction, and refuses trees with live queues, active leases, unsettled effects, managed resource references, channel references, or step links from runs outside the tree. It deletes the safe aggregates, removes blobs with no remaining foreign-key reference, checkpoints the WAL, vacuums the file, and runs integrity and foreign-key checks. Pi Workflows never runs prune at startup.
277
279
 
278
280
  ## Alpha cutover
279
281
 
280
- The persisted-state alpha boundary is a hard cut. Pi Workflows has no normal reader or writer for older live storage. It does not use dual reads, dual writes, aliases, versioned state roots, or automatic import. No direct live-state client, replay server reader, or Rust SQLite fallback remains outside the host.
282
+ The persisted-state alpha boundary is a hard cut. Pi Workflows has no normal reader or writer for older live storage. It does not use dual reads, dual writes, aliases, versioned state roots, or automatic import. No direct live-state client, replay server reader, or Rust SQLite fallback remains outside the server.
281
283
 
282
284
  Older state remains untouched. Pi Workflows fails before mutation with this instruction: “Pi Workflows durable state is incompatible. Back up and move state.sqlite with its -wal and -shm files, then start Pi Workflows to create a new state.sqlite database. The incompatible state was not changed.”
@@ -320,7 +320,7 @@ A protected change to model choice, benchmark method, credentials, hardware, spe
320
320
 
321
321
  ## Compatibility
322
322
 
323
- Workflows without inputs, exits, or includes run unchanged. Existing controller child workflows remain unchanged. Existing terminal SQLite runs remain readable.
323
+ Workflows without inputs, exits, or includes run unchanged. Existing resource manager child workflows remain unchanged. Existing terminal SQLite runs remain readable.
324
324
 
325
325
  Composition and assistant-message output are compatible public API additions under the project's pre-1.0 policy.
326
326