@osolmaz/pi-workflows 0.12.0 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (257) hide show
  1. package/README.md +56 -31
  2. package/dist/builtins/autodoc.workflow.d.ts +4 -4
  3. package/dist/builtins/autoimplement.workflow.d.ts +797 -142
  4. package/dist/builtins/autoimplement.workflow.js +17 -105
  5. package/dist/builtins/autoimplement.workflow.js.map +1 -1
  6. package/dist/builtins/autoplan.workflow.d.ts +126 -13
  7. package/dist/builtins/autoplan.workflow.js +200 -29
  8. package/dist/builtins/autoplan.workflow.js.map +1 -1
  9. package/dist/builtins/catalog.js +7 -5
  10. package/dist/builtins/catalog.js.map +1 -1
  11. package/dist/builtins/index.d.ts +4 -2
  12. package/dist/builtins/index.js +3 -1
  13. package/dist/builtins/index.js.map +1 -1
  14. package/dist/builtins/monitor.workflow.d.ts +56 -23
  15. package/dist/builtins/monitor.workflow.js +508 -329
  16. package/dist/builtins/monitor.workflow.js.map +1 -1
  17. package/dist/builtins/pi-agent-group.d.ts +72 -0
  18. package/dist/builtins/pi-agent-group.js +1087 -0
  19. package/dist/builtins/pi-agent-group.js.map +1 -0
  20. package/dist/builtins/plain-summary.workflow.d.ts +32 -0
  21. package/dist/builtins/plain-summary.workflow.js +134 -0
  22. package/dist/builtins/plain-summary.workflow.js.map +1 -0
  23. package/dist/builtins/plan-approval.workflow.d.ts +39 -5
  24. package/dist/builtins/plan-approval.workflow.js +92 -14
  25. package/dist/builtins/plan-approval.workflow.js.map +1 -1
  26. package/dist/builtins/plan-change.workflow.d.ts +449 -0
  27. package/dist/builtins/plan-change.workflow.js +256 -0
  28. package/dist/builtins/plan-change.workflow.js.map +1 -0
  29. package/dist/builtins/plan-presentation.js +2 -2
  30. package/dist/builtins/plan-presentation.js.map +1 -1
  31. package/dist/builtins/sanity-check.workflow.d.ts +50 -6
  32. package/dist/builtins/sanity-check.workflow.js +150 -28
  33. package/dist/builtins/sanity-check.workflow.js.map +1 -1
  34. package/dist/controllers/effects.d.ts +3 -2
  35. package/dist/controllers/effects.js +8 -1
  36. package/dist/controllers/effects.js.map +1 -1
  37. package/dist/controllers/index.d.ts +1 -1
  38. package/dist/controllers/index.js +1 -1
  39. package/dist/controllers/index.js.map +1 -1
  40. package/dist/controllers/manager.d.ts +1 -0
  41. package/dist/controllers/manager.js +41 -17
  42. package/dist/controllers/manager.js.map +1 -1
  43. package/dist/controllers/sqlite.d.ts +107 -85
  44. package/dist/controllers/sqlite.js +1611 -1486
  45. package/dist/controllers/sqlite.js.map +1 -1
  46. package/dist/controllers/store.d.ts +9 -8
  47. package/dist/controllers/store.js +1 -31
  48. package/dist/controllers/store.js.map +1 -1
  49. package/dist/controllers/types.d.ts +3 -0
  50. package/dist/controllers/workflow-engine-scheduler.d.ts +1 -1
  51. package/dist/controllers/workflow-engine-scheduler.js +3 -5
  52. package/dist/controllers/workflow-engine-scheduler.js.map +1 -1
  53. package/dist/controllers/workflows.d.ts +2 -2
  54. package/dist/controllers/workflows.js +11 -10
  55. package/dist/controllers/workflows.js.map +1 -1
  56. package/dist/extension/controller-host.js +2 -2
  57. package/dist/extension/controller-host.js.map +1 -1
  58. package/dist/extension/decision-channels.d.ts +4 -2
  59. package/dist/extension/decision-channels.js +333 -146
  60. package/dist/extension/decision-channels.js.map +1 -1
  61. package/dist/extension/executor.d.ts +9 -2
  62. package/dist/extension/executor.js +89 -2
  63. package/dist/extension/executor.js.map +1 -1
  64. package/dist/extension/herdr-viewer.d.ts +0 -1
  65. package/dist/extension/herdr-viewer.js +0 -4
  66. package/dist/extension/herdr-viewer.js.map +1 -1
  67. package/dist/extension/index.js +209 -127
  68. package/dist/extension/index.js.map +1 -1
  69. package/dist/extension/recorder.d.ts +3 -10
  70. package/dist/extension/recorder.js +13 -22
  71. package/dist/extension/recorder.js.map +1 -1
  72. package/dist/extension/session-events.d.ts +2 -2
  73. package/dist/extension/step-message.d.ts +7 -2
  74. package/dist/extension/step-message.js +94 -2
  75. package/dist/extension/step-message.js.map +1 -1
  76. package/dist/extension/widget.js +29 -3
  77. package/dist/extension/widget.js.map +1 -1
  78. package/dist/host/rpc-executor.d.ts +3 -0
  79. package/dist/host/rpc-executor.js +2 -0
  80. package/dist/host/rpc-executor.js.map +1 -1
  81. package/dist/host/runner.d.ts +3 -6
  82. package/dist/host/runner.js +83 -32
  83. package/dist/host/runner.js.map +1 -1
  84. package/dist/render/graph-render.js +15 -10
  85. package/dist/render/graph-render.js.map +1 -1
  86. package/dist/state/database.d.ts +35 -0
  87. package/dist/state/database.js +287 -0
  88. package/dist/state/database.js.map +1 -0
  89. package/dist/state/index.d.ts +4 -0
  90. package/dist/state/index.js +5 -0
  91. package/dist/state/index.js.map +1 -0
  92. package/dist/state/json.d.ts +6 -0
  93. package/dist/state/json.js +38 -0
  94. package/dist/state/json.js.map +1 -0
  95. package/dist/state/mutation.d.ts +57 -0
  96. package/dist/state/mutation.js +255 -0
  97. package/dist/state/mutation.js.map +1 -0
  98. package/dist/state/schema.d.ts +6 -0
  99. package/dist/state/schema.js +473 -0
  100. package/dist/state/schema.js.map +1 -0
  101. package/dist/viewer/cli.d.ts +2 -2
  102. package/dist/viewer/cli.js +142 -122
  103. package/dist/viewer/cli.js.map +1 -1
  104. package/dist/viewer/render.d.ts +4 -4
  105. package/dist/viewer/render.js +9 -23
  106. package/dist/viewer/render.js.map +1 -1
  107. package/dist/viewer/session-reducer.d.ts +1 -1
  108. package/dist/viewer/session-reducer.js +1 -1
  109. package/dist/viewer/tui.d.ts +3 -3
  110. package/dist/viewer/tui.js +15 -13
  111. package/dist/viewer/tui.js.map +1 -1
  112. package/dist/viewer/watch.d.ts +2 -5
  113. package/dist/viewer/watch.js +13 -13
  114. package/dist/viewer/watch.js.map +1 -1
  115. package/dist/workflows/catalog.js +7 -2
  116. package/dist/workflows/catalog.js.map +1 -1
  117. package/dist/workflows/composition.js +20 -2
  118. package/dist/workflows/composition.js.map +1 -1
  119. package/dist/workflows/decision-presentation.d.ts +1 -1
  120. package/dist/workflows/decision-presentation.js +51 -38
  121. package/dist/workflows/decision-presentation.js.map +1 -1
  122. package/dist/workflows/definition.d.ts +6 -2
  123. package/dist/workflows/definition.js +17 -0
  124. package/dist/workflows/definition.js.map +1 -1
  125. package/dist/workflows/engine.d.ts +7 -7
  126. package/dist/workflows/engine.js +196 -115
  127. package/dist/workflows/engine.js.map +1 -1
  128. package/dist/workflows/errors.d.ts +16 -3
  129. package/dist/workflows/errors.js +18 -3
  130. package/dist/workflows/errors.js.map +1 -1
  131. package/dist/workflows/human-decision.d.ts +45 -8
  132. package/dist/workflows/human-decision.js +641 -274
  133. package/dist/workflows/human-decision.js.map +1 -1
  134. package/dist/workflows/index.d.ts +5 -6
  135. package/dist/workflows/index.js +4 -5
  136. package/dist/workflows/index.js.map +1 -1
  137. package/dist/workflows/progress.d.ts +1 -0
  138. package/dist/workflows/progress.js +15 -3
  139. package/dist/workflows/progress.js.map +1 -1
  140. package/dist/workflows/schema.js +27 -1
  141. package/dist/workflows/schema.js.map +1 -1
  142. package/dist/workflows/store.d.ts +83 -103
  143. package/dist/workflows/store.js +907 -951
  144. package/dist/workflows/store.js.map +1 -1
  145. package/dist/workflows/types.d.ts +88 -126
  146. package/docs/CONTROLLERS.md +10 -8
  147. package/docs/DEFERRED_TURNS.md +2 -2
  148. package/docs/DESIGN_PHILOSOPHY.md +4 -2
  149. package/docs/HUMAN_DECISIONS.md +36 -47
  150. package/docs/HUMAN_DECISION_PRESENTATIONS.md +14 -24
  151. package/docs/MONITOR.md +179 -237
  152. package/docs/SQLITE_STATE.md +219 -0
  153. package/docs/WORKFLOW_COMPOSITION.md +27 -26
  154. package/docs/WORKFLOW_STEP_MESSAGES.md +20 -18
  155. package/docs/WORKFLOW_UPDATES.md +10 -9
  156. package/docs/development.md +9 -9
  157. package/docs/live-replay-protocol.md +28 -37
  158. package/docs/plans/2026-08-16-workflow-updates-plan.md +1 -1
  159. package/docs/plans/2026-08-19-human-decision-gates-plan.md +1 -1
  160. package/docs/plans/2026-08-21-autoimplement-timeout-fallback-plan.md +1 -1
  161. package/docs/plans/2026-08-21-plan-change-approval-policy-plan.md +322 -0
  162. package/docs/plans/2026-08-21-sanity-check-plan.md +217 -95
  163. package/docs/plans/2026-08-22-goal-finishing-monitor-plan.md +200 -0
  164. package/docs/plans/2026-08-23-assistant-agent-completion-plan.md +701 -0
  165. package/docs/plans/2026-08-23-sqlite-state-plan.md +970 -0
  166. package/docs/plans/replayable-run-bundles-implementation-plan.md +2 -2
  167. package/docs/plans/session-event-replay-implementation-plan.md +1 -1
  168. package/docs/plans/tui-viewer-implementation-plan.md +1 -1
  169. package/docs/session-event-journal.md +63 -434
  170. package/docs/tui-viewer.md +13 -15
  171. package/docs/workflows.md +127 -53
  172. package/examples/workflows/approved-plan.workflow.ts +19 -46
  173. package/examples/workflows/plain-summary.workflow.ts +1 -0
  174. package/herdr-plugin.toml +1 -1
  175. package/package.json +7 -7
  176. package/plugins/herdr/viewer.mjs +1 -11
  177. package/schemas/human-decision-accepted-v1.schema.json +15 -3
  178. package/schemas/human-decision-continuation-v1.schema.json +10 -1
  179. package/schemas/human-decision-delivery-v1.schema.json +8 -0
  180. package/schemas/human-decision-receipt-v1.schema.json +8 -0
  181. package/schemas/human-decision-request-v1.schema.json +24 -4
  182. package/skills/autoimplement/SKILL.md +27 -0
  183. package/skills/autoplan/SKILL.md +5 -4
  184. package/skills/monitor/SKILL.md +91 -135
  185. package/skills/pi-workflows/SKILL.md +9 -8
  186. package/skills/sanity-check/SKILL.md +44 -0
  187. package/src/builtins/autoimplement.workflow.ts +19 -118
  188. package/src/builtins/autoplan.workflow.ts +290 -40
  189. package/src/builtins/catalog.ts +7 -5
  190. package/src/builtins/index.ts +18 -1
  191. package/src/builtins/monitor.workflow.ts +673 -382
  192. package/src/builtins/pi-agent-group.ts +1407 -0
  193. package/src/builtins/plain-summary.workflow.ts +185 -0
  194. package/src/builtins/plan-approval.workflow.ts +157 -24
  195. package/src/builtins/plan-change.workflow.ts +321 -0
  196. package/src/builtins/plan-presentation.ts +2 -2
  197. package/src/builtins/sanity-check.workflow.ts +248 -48
  198. package/src/controllers/effects.ts +7 -0
  199. package/src/controllers/index.ts +0 -6
  200. package/src/controllers/manager.ts +72 -32
  201. package/src/controllers/sqlite.ts +2420 -1948
  202. package/src/controllers/store.ts +17 -38
  203. package/src/controllers/types.ts +3 -0
  204. package/src/controllers/workflow-engine-scheduler.ts +4 -8
  205. package/src/controllers/workflows.ts +45 -21
  206. package/src/extension/controller-host.ts +1 -2
  207. package/src/extension/decision-channels.ts +456 -203
  208. package/src/extension/executor.ts +105 -2
  209. package/src/extension/herdr-viewer.ts +0 -5
  210. package/src/extension/index.ts +251 -151
  211. package/src/extension/recorder.ts +13 -22
  212. package/src/extension/session-events.ts +2 -2
  213. package/src/extension/step-message.ts +106 -4
  214. package/src/extension/widget.ts +32 -5
  215. package/src/host/rpc-executor.ts +4 -0
  216. package/src/host/runner.ts +93 -41
  217. package/src/render/graph-render.ts +12 -6
  218. package/src/state/database.ts +375 -0
  219. package/src/state/index.ts +29 -0
  220. package/src/state/json.ts +42 -0
  221. package/src/state/mutation.ts +435 -0
  222. package/src/state/schema.ts +475 -0
  223. package/src/viewer/cli.ts +151 -144
  224. package/src/viewer/render.ts +19 -30
  225. package/src/viewer/session-reducer.ts +1 -1
  226. package/src/viewer/tui.ts +18 -17
  227. package/src/viewer/watch.ts +13 -17
  228. package/src/workflows/catalog.ts +7 -2
  229. package/src/workflows/composition.ts +21 -3
  230. package/src/workflows/decision-presentation.ts +56 -43
  231. package/src/workflows/definition.ts +36 -3
  232. package/src/workflows/engine.ts +228 -130
  233. package/src/workflows/errors.ts +27 -3
  234. package/src/workflows/human-decision.ts +921 -349
  235. package/src/workflows/index.ts +20 -30
  236. package/src/workflows/progress.ts +18 -3
  237. package/src/workflows/schema.ts +36 -1
  238. package/src/workflows/store.ts +1300 -1163
  239. package/src/workflows/types.ts +100 -137
  240. package/dist/builtins/sanity-check-session.d.ts +0 -17
  241. package/dist/builtins/sanity-check-session.js +0 -168
  242. package/dist/builtins/sanity-check-session.js.map +0 -1
  243. package/dist/workflows/artifacts.d.ts +0 -40
  244. package/dist/workflows/artifacts.js +0 -155
  245. package/dist/workflows/artifacts.js.map +0 -1
  246. package/dist/workflows/migrate-sources.d.ts +0 -42
  247. package/dist/workflows/migrate-sources.js +0 -133
  248. package/dist/workflows/migrate-sources.js.map +0 -1
  249. package/docs/run-bundles.md +0 -481
  250. package/schemas/human-decision-accepted-v2.schema.json +0 -50
  251. package/schemas/human-decision-delivery-v2.schema.json +0 -36
  252. package/schemas/human-decision-receipt-v2.schema.json +0 -39
  253. package/schemas/human-decision-request-v2.schema.json +0 -69
  254. package/schemas/human-decision-resolution-v2.schema.json +0 -27
  255. package/src/builtins/sanity-check-session.ts +0 -205
  256. package/src/workflows/artifacts.ts +0 -188
  257. package/src/workflows/migrate-sources.ts +0 -178
@@ -0,0 +1,219 @@
1
+ # SQLite state
2
+
3
+ Pi Workflows stores all live durable state in one database:
4
+
5
+ ```text
6
+ ~/.pi/agent/workflows/state.sqlite
7
+ ```
8
+
9
+ There is one database for the user installation. Project and run IDs separate data inside it. Workflow targets do not read or write this database.
10
+
11
+ ## Storage boundary
12
+
13
+ The database stores:
14
+
15
+ - workflow definitions, runs, events, node attempts, outputs, and updates
16
+ - captured Pi session entries and events
17
+ - run and controller queues, claims, retries, and continuations
18
+ - human-decision requests, submissions, resolutions, and cancellations
19
+ - controller resources, finalizers, effects, and child workflows
20
+ - notifications and deferred turns
21
+ - nonsecret channel cursors, inbox records, messages, and settlement receipts
22
+ - canonical JSON, text, and large text values
23
+
24
+ Credentials and raw secrets must not enter the database. Channel credential files stay in their existing private configuration directory.
25
+
26
+ The implementation does not create live run directories, artifact files, decision directories, project databases, controller databases, or channel databases.
27
+
28
+ ## Database settings
29
+
30
+ Every writer enables:
31
+
32
+ ```sql
33
+ PRAGMA foreign_keys = ON;
34
+ PRAGMA journal_mode = WAL;
35
+ PRAGMA synchronous = FULL;
36
+ PRAGMA busy_timeout = 5000;
37
+ ```
38
+
39
+ The database also uses:
40
+
41
+ - `STRICT` tables
42
+ - application ID `0x50495746`
43
+ - user version `1`
44
+ - a bounded WAL checkpoint and journal size
45
+ - directory mode `0700`
46
+ - database and backup mode `0600`
47
+
48
+ Read-only tools open the same file with SQLite read-only mode and `PRAGMA query_only = ON`.
49
+
50
+ Opening code verifies the application ID, user version, schema metadata, compiled DDL digest, and exact SQLite schema shape. An incompatible database fails with an instruction to clear the incompatible alpha state. Pi Workflows does not import, reinterpret, or delete that state.
51
+
52
+ ## Shared records
53
+
54
+ Four record groups provide the common lifecycle rules.
55
+
56
+ ### Resources
57
+
58
+ `resources` identifies each mutable aggregate and holds its current revision. Runs, decisions, controller resources, effects, channels, notifications, deferred turns, and session segments have stable resource identities.
59
+
60
+ Every accepted domain command compares its expected revision and increments it once.
61
+
62
+ ### Leases
63
+
64
+ `leases` holds the current owner, a hash of its random claim token, lease times, and a monotonically increasing generation.
65
+
66
+ A new owner always receives a new generation. Releasing a lease clears the owner fields but does not reduce or reuse the generation. A stale token or generation cannot write after ownership changes.
67
+
68
+ ### Events
69
+
70
+ `events` is the immutable audit history. Each event records the resource revision, event type, actor class, private local actor ID when applicable, lease generation, payload blob, and commit time.
71
+
72
+ A domain row and its event are written in one transaction. Normal APIs never update or delete events.
73
+
74
+ ### Effects
75
+
76
+ `effects` is the transactional outbox. A domain transaction records required follow-up work before it commits. Each effect has:
77
+
78
+ - a deterministic effect ID
79
+ - a source resource and exact source revision
80
+ - an effect type and idempotency key
81
+ - a canonical payload blob
82
+ - an owner scope
83
+ - a status and retry time
84
+ - a result, error, external reference, and settlement time when applicable
85
+
86
+ `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.
87
+
88
+ Local effects use deterministic transactions. 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.
89
+
90
+ ## Domain tables
91
+
92
+ The shared records do not replace domain schemas. The following `STRICT` tables keep the state explicit:
93
+
94
+ | Area | Tables |
95
+ | ------------------- | ------------------------------------------------------------------------------------------------ |
96
+ | Schema and projects | `schema_meta`, `projects` |
97
+ | Content | `blobs` |
98
+ | Shared lifecycle | `resources`, `leases`, `events` |
99
+ | Workflows | `workflow_definitions`, `runs`, `run_bindings`, `run_queue`, `node_attempts`, `workflow_updates` |
100
+ | Session capture | `session_segments`, `session_entries`, `session_events` |
101
+ | Human decisions | `human_decisions`, `human_decision_resolutions`, `human_decision_submissions`, `continuations` |
102
+ | Controllers | `controller_resources`, `controller_finalizers`, `controller_queue`, `controller_workflows` |
103
+ | Effects | `effects`, `effect_attempts` |
104
+ | Pi delivery | `notifications`, `turn_intents` |
105
+ | Channels | `channels`, `channel_cursors`, `channel_inbox`, `channel_messages`, `channel_message_parts` |
106
+
107
+ Foreign keys join projects, runs, attempts, decisions, controllers, effects, and channel records. Partial unique indexes enforce one active node attempt per run, one queued or running reservation per Pi session, one decision winner, and one deterministic effect key. A parked waiting parent does not block its continuation. Reserving that continuation settles the parked parent queue in the same transaction, so a failed reservation leaves the parent recoverable.
108
+
109
+ ## Content-addressed values
110
+
111
+ `blobs` stores canonical JSON and UTF-8 text as bytes. Its primary key is the 32-byte SHA-256 digest of the bytes.
112
+
113
+ Insertion verifies the digest, media type, byte length, and exact bytes. Repeated content adopts the existing row. This replaces separate artifact files while keeping large prompts, outputs, errors, session payloads, and rendered channel text deduplicated.
114
+
115
+ ### Assistant-message attempts
116
+
117
+ An agent definition records `expectedOutput` as either a submitted-output description or `{ "kind": "assistant-message", "maxChars"?: number }`. Omitted `maxChars` means that Pi Workflows adds no character limit.
118
+
119
+ A completed assistant-message attempt stores the exact text through the normal output blob. Its result record also stores a receipt with the text digest, final Pi session entry ID when available, optional author-supplied limit, and whether recovery adopted an existing response. Session tables keep the prompt-to-response entry range and the normal Pi message events.
120
+
121
+ An interrupted assistant-message attempt keeps its attempt ID when the origin Pi session resumes it. The executor adopts a matching completed assistant child from the active Pi branch instead of displaying the response twice. Submitted and non-agent attempts keep their normal fresh-attempt resume behavior.
122
+
123
+ ## Write contract
124
+
125
+ A write command uses this order:
126
+
127
+ ```text
128
+ BEGIN IMMEDIATE
129
+ verify the exact schema
130
+ verify the actor and operation
131
+ verify the expected resource revision
132
+ verify the claim token, generation, and expiry when ownership is required
133
+ verify the domain transition
134
+ write content-addressed values
135
+ increment the resource revision
136
+ write the immutable event
137
+ update the domain row
138
+ insert deterministic follow-up effects
139
+ COMMIT
140
+ ```
141
+
142
+ Any failed check rolls back the complete command.
143
+
144
+ A TypeScript write permit carries the expected facts between layers. It is not authority by itself. The store verifies durable ownership and revision data again inside the transaction.
145
+
146
+ ## Ownership
147
+
148
+ Reading or finding a row never gives write authority.
149
+
150
+ - A run owner may advance the run, apply automatic decision policy, create its continuation, settle its parent, and complete its queue work.
151
+ - A controller claim owner may update controller status, reserve effects, and start child workflows for that resource.
152
+ - A verified human channel actor may submit one answer candidate for the named decision. It does not gain run ownership.
153
+ - A channel lease owner may update only its channel cursor, inbox, delivery, and settlement records.
154
+ - Control commands have narrow explicit operations, such as requesting cancellation or deletion.
155
+ - Model-originated workflow answers cannot resolve protected human decisions.
156
+
157
+ Stores check ownership in the same transaction as the write. Shared scans, status commands, lists, viewers, and the Rust `piw` program are read-only.
158
+
159
+ ## Competing outcomes
160
+
161
+ Each domain has one atomic winning fact.
162
+
163
+ Human answers, timeout policy, explicit cancellation, and no-default expiry compete through `human_decision_resolutions`. Its decision primary key allows one immutable winner.
164
+
165
+ A deadline with a validated default response is timeout-policy acceptance. It cannot become an expiry cancellation. Expiry cancellation is valid only when there is no default response.
166
+
167
+ Late or repeated commands return or adopt the durable winner. They do not overwrite it.
168
+
169
+ The same rule applies to run terminal outcomes, continuation admission, queue settlement, controller effects, retry scheduling, and channel settlement through their domain constraints and expected revisions.
170
+
171
+ ## Read contract
172
+
173
+ Status is a pure projection of domain rows, immutable facts, current leases, and effect receipts.
174
+
175
+ - A terminal run fact overrides stale queue presentation.
176
+ - An accepted decision is accepted even if its continuation effect is still pending.
177
+ - A cancelled decision is cancelled even if parent cleanup is still pending.
178
+ - A stale owner is not shown as current.
179
+ - An ambiguous external effect is shown as unresolved.
180
+
181
+ Read paths do not repair state. Owner reconcilers apply pending effects and write receipts.
182
+
183
+ ## Projects and concurrency
184
+
185
+ All projects use the same file. `projects` stores a stable ID and canonical path. Project-scoped controller and run queries use that key. Standalone host lock and child-process registry files use a project hash under the workflow state directory, so different projects can run hosts concurrently while two hosts for one project still conflict.
186
+
187
+ SQLite WAL permits concurrent readers while one writer commits. Writers are serialized by SQLite and must keep transactions short. Hashing, model calls, shell work, and external requests happen outside write transactions.
188
+
189
+ This contract is for local storage on one machine. It does not claim distributed consensus or network-filesystem safety.
190
+
191
+ ## Backup and verification
192
+
193
+ An active database must be backed up with the SQLite backup API. Copying only `state.sqlite` while WAL writes are active is not supported.
194
+
195
+ Verification checks:
196
+
197
+ ```sql
198
+ PRAGMA integrity_check;
199
+ PRAGMA foreign_key_check;
200
+ ```
201
+
202
+ A backup is opened read-only and checked before it is accepted.
203
+
204
+ Supported commands are:
205
+
206
+ ```bash
207
+ pi-workflows state status
208
+ pi-workflows state verify
209
+ pi-workflows state backup /absolute/path/to/state-backup.sqlite
210
+ ```
211
+
212
+ `status` reports only safe counts, file size, active leases, and unsettled effects.
213
+ It does not print actor IDs, channel references, payloads, or credentials.
214
+
215
+ ## Alpha cutover
216
+
217
+ This 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.
218
+
219
+ Older state remains untouched. If it is present when a new database would be created, Pi Workflows fails with a clear instruction instead of guessing or deleting data.
@@ -72,29 +72,29 @@ import repair from "./repair.workflow.js";
72
72
  export default defineWorkflow({
73
73
  source: import.meta.url,
74
74
  name: "monitor-with-repair",
75
- startAt: "check",
75
+ startAt: "observe",
76
76
  includes: {
77
77
  repair: includeWorkflow(repair, {
78
78
  input: ({ outputs }) => ({
79
- task: (outputs.check as { issue: string }).issue,
79
+ task: (outputs.observe as { action: { issue: string } }).action.issue,
80
80
  }),
81
81
  }),
82
82
  },
83
83
  nodes: {
84
- check: agent({ prompt: () => "Check the target." }),
84
+ observe: agent({ prompt: () => "Observe the target without changing it." }),
85
85
  wait: compute({ run: () => ({}) }),
86
- finish: compute({ run: ({ outputs }) => outputs.check }),
86
+ finish: compute({ run: ({ outputs }) => outputs.observe }),
87
87
  },
88
88
  edges: [
89
89
  {
90
- from: "check",
90
+ from: "observe",
91
91
  switch: {
92
92
  on: "$.route",
93
- cases: { continue: "wait", repair: "repair", stop: "finish" },
93
+ cases: { wait: "wait", act: "repair", stop: "finish" },
94
94
  },
95
95
  },
96
- { from: "wait", to: "check" },
97
- { from: "repair.fixed", to: "check" },
96
+ { from: "wait", to: "observe" },
97
+ { from: "repair.fixed", to: "observe" },
98
98
  { from: "repair.blocked", to: "finish" },
99
99
  ],
100
100
  });
@@ -204,11 +204,11 @@ A checkpoint inside a child uses the normal continuation behavior and resumes at
204
204
 
205
205
  ### Reports and presentation
206
206
 
207
- Notify nodes and updates keep qualified node identities. Only the root workflow produces final presentation. A child's `presentationPrompt` applies when the child runs alone and is ignored when included.
207
+ Notify nodes and updates keep qualified node identities. Only the root workflow produces final `presentationPrompt` output. A child's `presentationPrompt` applies when the child runs alone and is ignored when included. An agent with `expectedOutput: assistantMessage()` is part of the graph, so its normal assistant response remains visible when included. The parent continues only after that turn settles.
208
208
 
209
209
  ## Persistence
210
210
 
211
- Composition extends the existing run bundle.
211
+ Composition extends the existing SQLite run state.
212
212
 
213
213
  The manifest and state record:
214
214
 
@@ -259,13 +259,13 @@ monitor
259
259
 
260
260
  ## Autoplan, autodoc, and autoimplement
261
261
 
262
- `autoplan` accepts the problem, scope, constraints, an optional previous plan, and new evidence. It automatically selects the best practical in-scope solution. The ideal end state can win when it is feasible, but an unavailable upstream change cannot block a valid practical solution. It exits through `ready` or `blocked` and returns a plan digest and change status.
262
+ `autoplan` accepts the problem, scope, constraints, an optional previous plan, and new evidence. It records two through four practical candidates plus the ideal, automatically selects the best in-scope solution, and records one rejection reason for every other explicit candidate. It includes `plain-summary` on ready and blocked routes, so the user sees one short normal assistant response before the parent continues. The response calls the choice a plan selected for approval and cannot satisfy the later human decision. Autoplan exits through `ready` or `blocked` and returns the full candidates, selection, detailed plan when ready, plain summary, plan digest, and change status.
263
263
 
264
264
  `autodoc` accepts an already selected plan or finds it in the active conversation and referenced canonical documents. It adopts current documentation or updates the canonical specification and implementation plan, runs documentation checks, and returns a documented-plan record. It never selects a solution or implements one.
265
265
 
266
266
  `autoimplement` requires a clear existing plan, but the structured `plan` input is optional because the plan can already be in conversation context or canonical documentation. It blocks when it cannot find a clear plan. It skips autodoc when documentation is current and includes autodoc when documentation is missing or stale. The absence of `input.plan` never routes to initial autoplan.
267
267
 
268
- Autoimplement includes `autoplan` only as evidence-driven `redesign`. When implementation, verification, review, comments, or CI proves that the approach is wrong, the revised plan passes through autodoc before implementation resumes. Local bugs go to a fix step instead.
268
+ Autoimplement includes the shared plan-change workflow only as evidence-driven `redesign`. When implementation, verification, review, comments, or CI proves that the approach is wrong, the shared workflow runs Autoplan, Autodoc, the configured plan decision, and bounded replanning before implementation resumes. Local bugs go to a fix step instead. Existing supplied or discovered plans bypass the decision.
269
269
 
270
270
  Review rounds record findings at every severity from P0 through P2. P0 or P1 findings require another implementation and review round. A P2-only round can be addressed, but the workflow does not run the reviewer again solely because P2 work changed files.
271
271
 
@@ -277,36 +277,37 @@ Autoimplement prepares a ready PR by default. It merges only when its input expl
277
277
 
278
278
  ## Monitor repair
279
279
 
280
- Monitor remains observation-only unless its input explicitly authorizes mutation. An authorized repair path is:
280
+ Monitor authorizes routine bounded repair by default. Set `repair: false` when a run must remain observation-only. The repair path is:
281
281
 
282
282
  ```text
283
283
  check
284
- -> initialDesign: autoplan
285
- -> documentation: autodoc
286
- -> approval: plan-approval when requested
287
- -> replan: initialDesign
284
+ -> planChange
285
+ -> autoplan
286
+ -> autodoc
287
+ -> plan-approval
288
+ -> replan: autoplan
288
289
  -> implementation: autoimplement
289
- -> redesign: autoplan -> autodoc when needed
290
+ -> redesign: planChange when needed
290
291
  -> check
291
292
  ```
292
293
 
293
- The outer `autoplan` creates the first plan. Autodoc records it before implementation. An optional plan approval gate can continue, stop, or return exact replan instructions to autoplan. The inner redesign mount revises a plan only when new evidence invalidates it and records the revision through autodoc. The monitor checks the target again after implementation and does not trust a repair claim by itself.
294
+ The shared plan-change workflow creates, records, and gates each repair plan. Its default policy asks the `operator` audience and continues after 10 minutes without an answer. Required mode waits for an explicit answer. Skip mode creates no decision. Monitor passes the selected plan into Autoimplement, so Autoimplement does not ask about that digest again. A later Autoimplement redesign uses the same shared workflow for the changed digest. The monitor checks the target again after implementation and does not trust a repair claim by itself.
294
295
 
295
296
  A protected change to model choice, benchmark method, credentials, hardware, spending authority, or another user decision exits as blocked. The workflow never changes the protected part of the task silently.
296
297
 
297
- ## Compatibility and release
298
+ ## Compatibility
298
299
 
299
- Workflows without inputs, exits, or includes run unchanged. Existing controller child workflows remain unchanged. Existing terminal run bundles remain readable.
300
+ Workflows without inputs, exits, or includes run unchanged. Existing controller child workflows remain unchanged. Existing terminal SQLite runs remain readable.
300
301
 
301
- This is a compatible public API addition under the project's pre-1.0 policy. It targets `0.10.0` if no earlier release changes the next version.
302
+ Composition and assistant-message output are compatible public API additions under the project's pre-1.0 policy.
302
303
 
303
304
  ## Contract impact
304
305
 
305
- - **Session state:** normal workflow messages and tool results only.
306
- - **Other persistent data:** additive source and mount data, definition digests, and include events in existing run bundles.
306
+ - **Session state:** normal workflow messages, tool results, and visible assistant-message node outputs.
307
+ - **Other persistent data:** additive source and mount data, definition digests, and include events in existing SQLite runs.
307
308
  - **Pi internals:** none.
308
309
  - **Public Pi API:** existing extension APIs only.
309
- - **Public pi-workflows API:** typed workflow inputs and exits, `includeWorkflow()`, direct imports, dynamic references, and `defineWorkflowRegistry()`.
310
+ - **Public pi-workflows API:** typed workflow inputs and exits, `includeWorkflow()`, direct imports, dynamic references, `defineWorkflowRegistry()`, and `expectedOutput: assistantMessage()`.
310
311
 
311
312
  ## Required tests
312
313
 
@@ -328,6 +329,6 @@ The implementation must cover:
328
329
  - reviewer command correction;
329
330
  - P0 through P2 plus clean review routes;
330
331
  - five-minute CI wait and opportunistic testing routes;
331
- - monitor observation-only and authorized repair modes;
332
+ - monitor default repair and explicit observation-only modes;
332
333
  - repeated repair with no progress;
333
334
  - real-Pi execution of nested monitor repair.
@@ -2,15 +2,13 @@
2
2
 
3
3
  This specification defines how pi-workflows shows agent-step instructions in an interactive Pi session. The model receives the complete step prompt, while the user sees a small workflow card that can be expanded.
4
4
 
5
- This contract is implemented for the release after `0.5.3`.
6
-
7
5
  ## Goal
8
6
 
9
- Agent-step prompts contain the task, workflow identity, attempt identity, output shape, and submission rules. This information is required by the model, but showing it as a large user message makes the conversation hard to read.
7
+ Agent-step prompts contain the task, workflow identity, attempt identity, output form, and completion rules. Submitted steps call the workflow tool. Assistant-message steps reply normally. This information is required by the model, but showing it as a large user message makes the conversation hard to read.
10
8
 
11
- pi-workflows will send the same prompt as a custom Pi message. A custom renderer will show a compact summary by default and the full content when expanded.
9
+ pi-workflows sends the same prompt as a custom Pi message. A custom renderer shows a compact summary by default and the full content when expanded.
12
10
 
13
- This is a presentation change. It does not add a workflow primitive, change graph execution, or change the step completion contract.
11
+ Both output forms use the existing `agent` node. The completion form changes through `expectedOutput`; no new node type is added.
14
12
 
15
13
  ## Message contract
16
14
 
@@ -45,7 +43,7 @@ pi.sendMessage(
45
43
 
46
44
  `content` is the complete prompt that the existing executor would send as a user message. It remains available to the model and in session history.
47
45
 
48
- `details` contains structured display data. The renderer reads this object directly and never parses the prompt text. `AgentStepContract` remains the source of every identity field and the expected-output field.
46
+ `details` contains structured display data. The renderer reads this object directly and never parses the prompt text. `AgentStepContract` remains the source of every identity field, the completion form, the submitted output description, and any explicit assistant character limit.
49
47
 
50
48
  `kind` distinguishes the first delivery from a reminder or a resume that must repeat the instructions. An ordinary resume that can continue without another prompt does not create a message.
51
49
 
@@ -53,7 +51,7 @@ pi.sendMessage(
53
51
 
54
52
  The workflow engine remains independent of Pi. It continues to produce an `AgentStepRequest` with a complete prompt and structured contract.
55
53
 
56
- The request gains optional presentation data for the run title and node status detail. The conversation executor passes the prompt, contract, presentation data, delivery kind, and streaming state to the Pi extension. The RPC executor sends the same complete prompt to headless Pi without TUI metadata.
54
+ The request carries optional presentation data for the run title and node status detail. The conversation executor passes the prompt, contract, presentation data, delivery kind, and streaming state to the Pi extension. The RPC executor handles submitted steps. An assistant-message step parks for the origin Pi session; a detached run with no origin session fails before prompting.
57
55
 
58
56
  One pure formatter remains responsible for the model prompt used by both executors. Interactive delivery must not shorten, summarize, or rebuild the model prompt from display fields.
59
57
 
@@ -82,16 +80,17 @@ The expanded card shows:
82
80
  - node id
83
81
  - attempt id
84
82
  - delivery kind
85
- - expected output
83
+ - completion form
84
+ - expected output and optional character limit
86
85
  - full model prompt
87
86
 
88
87
  Expansion uses Pi's existing custom-message expansion state and keys. pi-workflows does not add another toggle or store separate expansion state.
89
88
 
90
89
  ## Reminders and resumes
91
90
 
92
- The existing bounded reminder behavior stays in place. A reminder uses the same custom message type and renderer. It keeps the contract and sets `kind: "reminder"`.
91
+ The existing bounded reminder behavior stays in place for submitted steps. A reminder uses the same custom message type and renderer. It keeps the contract and sets `kind: "reminder"`. Assistant-message steps do not nudge or retry after a visible response.
93
92
 
94
- A resumed step uses `kind: "resume"` only when the executor must send the instructions again. The attempt id must still identify the active attempt. Stale attempts remain invalid.
93
+ A resumed step uses `kind: "resume"` only when the executor must send the instructions again. An interrupted assistant-message step keeps its attempt id. If its matching prompt already has a completed assistant child on the active branch, the executor adopts that exact response instead of displaying it again. Stale attempts and responses from another branch remain invalid.
95
94
 
96
95
  ## Notifications
97
96
 
@@ -103,9 +102,9 @@ The two message types must not share delivery code that can accidentally change
103
102
 
104
103
  ## Session and persistence impact
105
104
 
106
- New interactive step deliveries replace `sendUserMessage` with `sendMessage`. Existing session entries remain readable and are not rewritten.
105
+ Interactive step deliveries use `sendMessage` instead of `sendUserMessage`. Existing session entries remain readable and are not rewritten.
107
106
 
108
- The custom message is a normal documented Pi session message. pi-workflows adds no Pi session schema, private entry type, or separate persistent store. Run bundles keep the existing full prompt and structured step contract, so this change does not alter the run-bundle schema.
107
+ The custom prompt and visible assistant response are normal documented Pi session messages. pi-workflows adds no Pi session schema, private entry type, or separate persistent store. SQLite stores the full prompt, exact assistant text, conversation range, and additive digest receipt in existing content-addressed records, so this behavior adds no durable format.
109
108
 
110
109
  If the renderer is unavailable, Pi still retains the custom message content. pi-workflows does not add a fallback path that sends a duplicate user message.
111
110
 
@@ -115,11 +114,11 @@ This design uses the documented `pi.sendMessage()` and `pi.registerMessageRender
115
114
 
116
115
  It does not require a Pi core change or private Pi API.
117
116
 
118
- The workflow package adds no node, graph action, tool action, or workflow-file field for this feature. Workflow authors do not configure message rendering.
117
+ The workflow package adds `assistantMessage()` as an `expectedOutput` value for the existing `agent` node. It adds no node type, graph action, Pi tool, private API, or message-rendering option.
119
118
 
120
119
  ## Validation and tests
121
120
 
122
- The implementation must verify:
121
+ Tests verify:
123
122
 
124
123
  - interactive and RPC executors give the model the same complete prompt
125
124
  - one step message starts one model turn
@@ -127,12 +126,15 @@ The implementation must verify:
127
126
  - expanded rendering shows the full prompt and exact contract ids
128
127
  - long and missing display fields render safely
129
128
  - reminders and resumed deliveries keep the active attempt id
130
- - stale attempts remain rejected after timeout or cancellation
131
- - session replay restores the same custom message
129
+ - submitted steps still reject stale attempts after timeout or cancellation
130
+ - assistant steps wait for `agent_settled` and capture only visible text
131
+ - empty, failed, aborted, tool-only, and explicitly over-limit responses fail once
132
+ - session replay restores the same custom prompt and adopts an existing response once
133
+ - detached execution parks for the origin session or fails clearly when none exists
132
134
  - notifications still enter context without starting a model turn
133
- - no duplicate user message is sent
135
+ - no duplicate prompt or assistant response is sent
134
136
 
135
- The end-to-end test must inspect the provider-facing prompt as well as the TUI message record. A correct card with missing model instructions is a failure.
137
+ The end-to-end test inspects the provider-facing prompt as well as the TUI message record. A correct card with missing model instructions is a failure.
136
138
 
137
139
  ## Security
138
140
 
@@ -50,7 +50,7 @@ An agent publishes the same update through the existing `workflow` tool:
50
50
  }
51
51
  ```
52
52
 
53
- The `update` tool action does not complete the agent step. The agent still calls `submit` once with the final step output.
53
+ The `update` tool action does not complete the agent step. A submitted agent still calls `submit` once with its final output. An assistant-message agent completes with its settled visible reply instead.
54
54
 
55
55
  ## Place in the workflow model
56
56
 
@@ -176,7 +176,7 @@ Keys are case-sensitive. A publisher must reuse the same key for the same item t
176
176
 
177
177
  The generic update layer allows fields it does not understand inside `data`. A package-defined update type may reject unknown fields.
178
178
 
179
- The encoded object must not exceed 64 KiB. Large logs and binary data do not belong in updates. Nodes should return large final values through normal outputs, where the run-bundle artifact rules apply.
179
+ The encoded object must not exceed 64 KiB. Large logs and binary data do not belong in updates. Nodes should return large final values through normal outputs, which the content-addressed SQLite blob store handles.
180
180
 
181
181
  ### Runtime-owned fields
182
182
 
@@ -256,7 +256,8 @@ Controller resource events remain controller events. They become workflow update
256
256
 
257
257
  ## Persistence
258
258
 
259
- Each accepted update appends one `update_published` event to `trace.ndjson`:
259
+ Each accepted update transaction inserts one `workflow_updates` row and one
260
+ `update_published` event:
260
261
 
261
262
  ```json
262
263
  {
@@ -282,17 +283,17 @@ Each accepted update appends one `update_published` event to `trace.ndjson`:
282
283
  }
283
284
  ```
284
285
 
285
- `state.json` adds an optional `updates` array containing only the latest record for each `(type, key)` pair. The array is sorted by `seq`. Omission means that the run has never published an update. New runs write an empty array from their first state projection. The trace remains the full history and source of truth.
286
+ The `runs` state blob contains only the latest record for each `(type, key)` pair. The array is sorted by event revision. `workflow_updates` and `events` retain the complete history.
286
287
 
287
- A resumed run keeps its latest projection and appends new update events. A checkpoint continuation starts a new run with an empty update projection. Updates are scoped to one run and are not carried into continuation bundles.
288
+ A continued run keeps its latest projection and appends new update events. A checkpoint continuation starts a new run with an empty update projection. Updates are scoped to one run and are not carried into continuation runs.
288
289
 
289
- The state projection supports at most 1,024 current `(type, key)` pairs. A publication that would exceed the limit is rejected. Trace history remains append-only.
290
+ The state projection supports at most 1,024 current `(type, key)` pairs. A publication that would exceed the limit is rejected. Event history remains append-only.
290
291
 
291
- The update envelope is an additive part of `pi-workflows.trace-event.v1` and `pi-workflows.run-state.v1`. Existing bundles without `updates` remain valid under the omission rule. The implementation uses one current schema and does not add migration files, fallback formats, or dual writes. New bundles that contain updates are not required to work with older package releases.
292
+ The update envelope remains part of `pi-workflows.run-state.v1`. The SQLite schema is the only live durable format.
292
293
 
293
294
  ## Ordering and limits
294
295
 
295
- Updates use the run store's existing serialized write chain and claim fence. Their trace sequence defines their total order.
296
+ Updates use one `BEGIN IMMEDIATE` transaction with the run's expected revision and claim fence. Their accepted update sequence defines their total order.
296
297
 
297
298
  Default safety limits per run are:
298
299
 
@@ -442,6 +443,6 @@ Validation errors must identify the field and rule. They must not include unrela
442
443
 
443
444
  Updates are data. They do not grant permission to execute a command, retry work, change a target, publish an artifact, or increase spending.
444
445
 
445
- Run bundles are private and may contain update data from external systems. Existing bundle permissions and export warnings apply.
446
+ SQLite runs are private and may contain update data from external systems. The database permission and export warnings in [SQLite state](SQLITE_STATE.md) apply.
446
447
 
447
448
  This feature does not add remote transports, a metrics database, global aggregation, automatic polling, or a new Pi core API. Workflow authors remain responsible for the trust and cost of their agent, action, shell, and controller code.
@@ -6,7 +6,7 @@ authoring workflows, see [workflows.md](workflows.md).
6
6
  ## Layout and boundaries
7
7
 
8
8
  ```
9
- src/workflows/ finite graph engine: definitions, execution, bundles, loader
9
+ src/workflows/ finite graph engine: definitions, execution, SQLite stores, loader
10
10
  src/builtins/ default workflows shipped at lowest discovery precedence
11
11
  src/controllers/ durable resources, queue, reconciliation, effects, child runs
12
12
  src/extension/ pi integration: commands, workflow tool, controller host, widget
@@ -22,14 +22,14 @@ package-owned definitions and imports only the public workflow engine.
22
22
  `src/controllers` may import the public workflow engine for child-run
23
23
  scheduling. `src/extension` and `src/host` may also import the built-in catalog.
24
24
  The extension and viewer never import each other. The viewer reads
25
- run bundles and opens the controller SQLite database read-only, so it works
25
+ SQLite runs and opens the controller SQLite database read-only, so it works
26
26
  from any process.
27
27
 
28
28
  Within `src/render`, `graph.ts` computes a pure layered layout (ported from
29
29
  the acpx replay viewer: labelled switch expansion, DFS back-edge detection,
30
30
  longest-path layering, barycenter ordering, virtual pass-through cells for
31
31
  long edges), `canvas.ts` is a character grid that merges box-drawing
32
- characters by connectivity, and `graph-render.ts` turns a run bundle plus a
32
+ characters by connectivity, and `graph-render.ts` turns a SQLite run state plus a
33
33
  replay position into the drawn graph in one of two node styles: `box`
34
34
  (bordered nodes, used by the viewer and the in-pi widget) or `line`
35
35
  (single-line nodes). The widget windows the boxed graph around the active
@@ -41,7 +41,7 @@ composes the full detail view (header, graph, step timeline, step inspector)
41
41
  and stays pure so tests can assert on rendered lines.
42
42
 
43
43
  The TypeScript and Rust viewers both show progress tracks, sample counts,
44
- confidence, update time, and ETA from bundle data. The Rust viewer also keeps
44
+ confidence, update time, and ETA from SQLite data. The Rust viewer also keeps
45
45
  graph layout and plain rendering in parity with `src/render`, then applies
46
46
  ratatui-only presentation through semantic canvas
47
47
  roles and `tui/src/theme`. Catppuccin is the default. Theme colors must be
@@ -74,7 +74,7 @@ Pi's documented `turn_*`, `message_*`, and `tool_execution_*` hooks and
74
74
  normalizes them before passing records to `WorkflowRunStore`. The workflows layer owns persisted shapes and schema validation. It also owns
75
75
  ordered append chains but never imports Pi types. High-rate hooks only stamp and enqueue bounded records;
76
76
  disk writes run on a separate chain. Capture failures are explicit in
77
- `session/capture.json` and never fail workflow execution.
77
+ the `session_segments` capture status and never fail workflow execution.
78
78
 
79
79
  `src/viewer/session-reducer.ts` and `tui/src/session.rs` implement the same
80
80
  sequence-ordered fold. Shared fixtures in `fixtures/session-events/` pin their
@@ -117,11 +117,11 @@ the real pi CLI from `devDependencies` in RPC mode with:
117
117
 
118
118
  - `PI_CODING_AGENT_DIR` pointed at a temp agent dir containing a `models.json`
119
119
  for the mock provider,
120
- - `PI_WORKFLOWS_RUNS_DIR` pointed at a temp runs dir,
120
+ - `HOME` pointed at a temporary home containing the canonical workflow database,
121
121
  - the extension loaded from source with `-e src/extension/index.ts`.
122
122
 
123
- It drives `/workflow` over the RPC protocol and asserts on the resulting run
124
- bundle, including temporal events, final entry linkage, capture integrity, and
123
+ It drives `/workflow` over the RPC protocol and asserts on the resulting SQLite
124
+ rows, including temporal events, final entry linkage, capture integrity, and
125
125
  terminal immutability, then renders the finished run through the viewer CLI.
126
126
  Nothing outside the temp directories is touched, and no real model is called.
127
127
 
@@ -156,7 +156,7 @@ before publishing.
156
156
 
157
157
  - Conventional Commits for commit messages and PR titles.
158
158
  - Persisted JSON uses camelCase keys and versioned `schema` identifiers; see
159
- [run-bundles.md](run-bundles.md). Breaking a persisted shape means bumping
159
+ [SQLITE_STATE.md](SQLITE_STATE.md). Breaking a persisted shape means bumping
160
160
  the schema version string.
161
161
  - Every exported API of the engine (`src/workflows/index.ts`) is covered by
162
162
  unit tests; new node types or edge semantics need tests in `test/` and a