@osolmaz/pi-workflows 0.11.0 → 0.11.2
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.
- package/README.md +14 -2
- package/dist/builtins/autoimplement.workflow.d.ts +2 -0
- package/dist/builtins/autoimplement.workflow.js +186 -9
- package/dist/builtins/autoimplement.workflow.js.map +1 -1
- package/dist/controllers/sqlite.d.ts +55 -7
- package/dist/controllers/sqlite.js +195 -48
- package/dist/controllers/sqlite.js.map +1 -1
- package/dist/extension/index.js +246 -54
- package/dist/extension/index.js.map +1 -1
- package/dist/herdr/setup.d.ts +13 -1
- package/dist/herdr/setup.js +349 -36
- package/dist/herdr/setup.js.map +1 -1
- package/dist/host/runner.js +3 -0
- package/dist/host/runner.js.map +1 -1
- package/dist/viewer/cli.d.ts +1 -0
- package/dist/viewer/cli.js +21 -10
- package/dist/viewer/cli.js.map +1 -1
- package/dist/workflows/migrate-sources.d.ts +1 -1
- package/dist/workflows/migrate-sources.js.map +1 -1
- package/dist/workflows/tool-input.d.ts +1 -0
- package/dist/workflows/tool-input.js +2 -2
- package/dist/workflows/tool-input.js.map +1 -1
- package/docs/2026-08-20-durable-workflow-launch-plan.md +449 -0
- package/docs/plans/2026-08-20-autoimplement-blocker-challenge-plan.md +138 -0
- package/docs/plans/2026-08-20-herdr-plugin-sync-plan.md +104 -0
- package/docs/workflows.md +11 -0
- package/herdr-plugin.toml +1 -1
- package/package.json +1 -1
- package/src/builtins/autoimplement.workflow.ts +212 -9
- package/src/controllers/sqlite.ts +308 -54
- package/src/extension/index.ts +303 -58
- package/src/herdr/setup.ts +429 -39
- package/src/host/runner.ts +3 -0
- package/src/viewer/cli.ts +22 -10
- package/src/workflows/migrate-sources.ts +5 -1
- package/src/workflows/tool-input.ts +5 -2
|
@@ -0,0 +1,449 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Make deferred workflow launches durable
|
|
3
|
+
author: Onur Solmaz <2453968+osolmaz@users.noreply.github.com>
|
|
4
|
+
date: 2026-08-20
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Make deferred workflow launches durable
|
|
8
|
+
|
|
9
|
+
## Goal
|
|
10
|
+
|
|
11
|
+
A successful `workflow start` call must create a real queued workflow before it returns. The model
|
|
12
|
+
must receive the final run ID, and Pi Workflows must start that run only after the current agent turn
|
|
13
|
+
settles.
|
|
14
|
+
|
|
15
|
+
If startup then fails, Pi Workflows must save the failure and start one new model turn with an
|
|
16
|
+
actionable error. The model can correct the request and call `workflow start` again. A failed launch
|
|
17
|
+
must release the session reservation so the corrected run can start.
|
|
18
|
+
|
|
19
|
+
This change stays inside Pi Workflows. It uses the existing project-scoped SQLite controller store
|
|
20
|
+
and documented Pi extension APIs. It does not change Pi, add a service, or add another database.
|
|
21
|
+
|
|
22
|
+
## Current failure
|
|
23
|
+
|
|
24
|
+
The current tool path stores a pending launch only in `pendingToolLaunch`. It returns:
|
|
25
|
+
|
|
26
|
+
```text
|
|
27
|
+
Workflow <name> will start after this turn finishes.
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
After the agent turn settles, the `agent_settled` handler clears `pendingToolLaunch` and calls
|
|
31
|
+
`startRun`. If `startRun` fails, the handler sends only a TUI notification. No run ID was returned,
|
|
32
|
+
`workflow status` has no durable launch to inspect, and the model receives no new turn.
|
|
33
|
+
|
|
34
|
+
This happened twice while starting `autoimplement`. The tool reported a queued launch, but no run
|
|
35
|
+
became active. The initiating model could not see the startup error and incorrectly reported that
|
|
36
|
+
the workflow had started.
|
|
37
|
+
|
|
38
|
+
## Decision
|
|
39
|
+
|
|
40
|
+
Use a durable prepared run and one model-visible failure follow-up.
|
|
41
|
+
|
|
42
|
+
During the `workflow start` tool call, Pi Workflows will:
|
|
43
|
+
|
|
44
|
+
1. Resolve and validate the workflow and all available start conditions.
|
|
45
|
+
2. Allocate the final run ID.
|
|
46
|
+
3. Save a queued record in the existing SQLite workflow queue.
|
|
47
|
+
4. Reserve the initiating Pi session.
|
|
48
|
+
5. Return the run ID and say that the workflow is queued.
|
|
49
|
+
|
|
50
|
+
After the current agent turn settles, Pi Workflows will:
|
|
51
|
+
|
|
52
|
+
1. Claim the queued record.
|
|
53
|
+
2. Change it to `starting`.
|
|
54
|
+
3. Build the engine and executor without running a node.
|
|
55
|
+
4. Change the record to `running`.
|
|
56
|
+
5. Release the engine to run the first node.
|
|
57
|
+
|
|
58
|
+
If startup fails, Pi Workflows will:
|
|
59
|
+
|
|
60
|
+
1. Change the record to `failed`.
|
|
61
|
+
2. Save a bounded safe error.
|
|
62
|
+
3. Release the session reservation.
|
|
63
|
+
4. Queue one durable failure notification for the owning Pi session.
|
|
64
|
+
5. Send that notification through `pi.sendMessage` with `triggerTurn: true` and
|
|
65
|
+
`deliverAs: "followUp"`.
|
|
66
|
+
|
|
67
|
+
The new model turn will contain the failed run ID and an actionable error. The model can fix the
|
|
68
|
+
workflow reference, input, source, or local condition and call `workflow start` again. Each retry is
|
|
69
|
+
an explicit model action with a new run ID. Pi Workflows does not perform a blind automatic retry.
|
|
70
|
+
|
|
71
|
+
## Alpha compatibility contract
|
|
72
|
+
|
|
73
|
+
Pi Workflows is in alpha. Change the current storage and tool contracts in place.
|
|
74
|
+
|
|
75
|
+
- Keep `pi-workflows.controller-store.v1`.
|
|
76
|
+
- Keep existing run-bundle schema identifiers.
|
|
77
|
+
- Do not add a v2 schema.
|
|
78
|
+
- Do not add a compatibility reader, data migration, dual path, alias, feature flag, or fallback to
|
|
79
|
+
`pendingToolLaunch`.
|
|
80
|
+
- Change the SQLite table definitions and TypeScript types directly.
|
|
81
|
+
- Remove the superseded status values and launch path in the same change.
|
|
82
|
+
|
|
83
|
+
An existing controller store with the old alpha table layout is incompatible. On open, Pi Workflows
|
|
84
|
+
must verify the required table columns and status contract. If the layout is old, it must stop with a
|
|
85
|
+
clear instruction to preserve any needed run evidence and reset the project-scoped controller store.
|
|
86
|
+
It must not silently reinterpret or delete old state.
|
|
87
|
+
|
|
88
|
+
Run bundles remain separate evidence. Resetting an incompatible controller queue must not delete run
|
|
89
|
+
bundle directories.
|
|
90
|
+
|
|
91
|
+
## Public behavior
|
|
92
|
+
|
|
93
|
+
### Start
|
|
94
|
+
|
|
95
|
+
A successful tool result becomes:
|
|
96
|
+
|
|
97
|
+
```text
|
|
98
|
+
Workflow autoimplement queued (run autoimplement-...).
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
The structured result contains:
|
|
102
|
+
|
|
103
|
+
```json
|
|
104
|
+
{
|
|
105
|
+
"action": "start",
|
|
106
|
+
"workflow": "autoimplement",
|
|
107
|
+
"runId": "autoimplement-...",
|
|
108
|
+
"queued": true
|
|
109
|
+
}
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Queued means that the durable reservation exists. It does not mean that the engine is running.
|
|
113
|
+
|
|
114
|
+
Every check that can run before acknowledgement must run before the queue record is committed. This
|
|
115
|
+
includes:
|
|
116
|
+
|
|
117
|
+
- Workflow resolution and definition validation.
|
|
118
|
+
- Declared input validation.
|
|
119
|
+
- Parent checkpoint and source checks for continuations.
|
|
120
|
+
- Controller-store access.
|
|
121
|
+
- Existing active or queued session reservation.
|
|
122
|
+
- Pending final presentation conflicts.
|
|
123
|
+
|
|
124
|
+
A preflight error returns through the original tool call. The model can correct it in the same turn.
|
|
125
|
+
No queued record remains after a failed preflight.
|
|
126
|
+
|
|
127
|
+
### Activation
|
|
128
|
+
|
|
129
|
+
Interactive activation starts only from a safe idle boundary:
|
|
130
|
+
|
|
131
|
+
- `agent_settled` after the initiating turn.
|
|
132
|
+
- `session_start` recovery when no agent turn is active.
|
|
133
|
+
- The next `agent_settled` event when recovery starts during an active turn.
|
|
134
|
+
|
|
135
|
+
Repeated lifecycle events must not start the same run twice. Claims use compare-and-set updates, a
|
|
136
|
+
claim token, a bounded lease, and a final fence before the engine starts its first node.
|
|
137
|
+
|
|
138
|
+
No compute node, shell action, function action, agent node, or presentation starts while the
|
|
139
|
+
initiating agent turn is active.
|
|
140
|
+
|
|
141
|
+
### Failure and model iteration
|
|
142
|
+
|
|
143
|
+
A deferred startup failure becomes a terminal queued-run state. The safe follow-up message is:
|
|
144
|
+
|
|
145
|
+
```text
|
|
146
|
+
Workflow autoimplement failed to start (run autoimplement-...): <safe reason>.
|
|
147
|
+
Inspect the error and call workflow start again only after you correct the cause.
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
The message does not contain raw workflow input, prompt text, credentials, request headers, or a
|
|
151
|
+
stack trace.
|
|
152
|
+
|
|
153
|
+
The failure notification has a deterministic ID derived from the run ID. Pi Workflows records its
|
|
154
|
+
delivery in the existing notification outbox. Before a delivery retry, it checks the native Pi
|
|
155
|
+
session for that notification ID. This prevents a duplicate after a crash between session append and
|
|
156
|
+
outbox acknowledgement.
|
|
157
|
+
|
|
158
|
+
A failed launch releases the one-workflow session reservation before it sends the follow-up. The
|
|
159
|
+
model can therefore call `workflow start` during the new turn. If that launch also fails, the same
|
|
160
|
+
process repeats with a new run ID. The regular Pi loop and user control remain the bounds; Pi
|
|
161
|
+
Workflows does not create an internal retry loop.
|
|
162
|
+
|
|
163
|
+
### Status
|
|
164
|
+
|
|
165
|
+
`workflow status` with a run ID reads the queued-run record before a run bundle exists. After the
|
|
166
|
+
engine starts, it also reads the normal run bundle.
|
|
167
|
+
|
|
168
|
+
Status reports these launch states:
|
|
169
|
+
|
|
170
|
+
```text
|
|
171
|
+
queued
|
|
172
|
+
starting
|
|
173
|
+
running
|
|
174
|
+
failed
|
|
175
|
+
cancelled
|
|
176
|
+
parked
|
|
177
|
+
done
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
A failed status includes only the bounded safe error. Status without a run ID shows the current
|
|
181
|
+
session reservation or the displayed run.
|
|
182
|
+
|
|
183
|
+
### Cancellation
|
|
184
|
+
|
|
185
|
+
`workflow cancel` accepts the queued run ID. Cancellation changes `queued` or `starting` to
|
|
186
|
+
`cancelled` atomically and invalidates the activation fence. A race cannot release the executor after
|
|
187
|
+
cancellation wins.
|
|
188
|
+
|
|
189
|
+
A cancelled launch releases the session reservation and sends no failure follow-up. A later start
|
|
190
|
+
can create a new run.
|
|
191
|
+
|
|
192
|
+
## Storage contract
|
|
193
|
+
|
|
194
|
+
Use the existing `workflow_run_queue` table as the source of truth for launch state.
|
|
195
|
+
|
|
196
|
+
Change its alpha v1 layout in place to store:
|
|
197
|
+
|
|
198
|
+
- Final run ID.
|
|
199
|
+
- Workflow name and immutable source identity.
|
|
200
|
+
- Definition digest.
|
|
201
|
+
- Private workflow input while activation needs it.
|
|
202
|
+
- Launch state.
|
|
203
|
+
- Owning Pi session.
|
|
204
|
+
- Claim token and lease expiry.
|
|
205
|
+
- Safe failure code and message.
|
|
206
|
+
- Created, updated, started, and finished times.
|
|
207
|
+
|
|
208
|
+
Replace the current `claimed`, `parked`, and `done` launch-state contract with:
|
|
209
|
+
|
|
210
|
+
```text
|
|
211
|
+
queued | starting | running | parked | done | failed | cancelled
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
Keep `parked` and `done` for current resume and terminal queue behavior. Remove `claimed`; `starting`
|
|
215
|
+
and `running` state its meaning directly.
|
|
216
|
+
|
|
217
|
+
Clear private queue input when the launch becomes `running`, `failed`, or `cancelled`. The normal run
|
|
218
|
+
bundle owns input after the engine starts.
|
|
219
|
+
|
|
220
|
+
Extend the existing `workflow_notifications` table in place so a notification can be run-level.
|
|
221
|
+
Add `launch_failure` to its kind contract and allow node and attempt identity to be absent for a
|
|
222
|
+
run-level notification. Reuse its delivery claim, lease, and delivered timestamp.
|
|
223
|
+
|
|
224
|
+
The project-scoped controller store remains private local state. Tests must verify restrictive file
|
|
225
|
+
and directory permissions.
|
|
226
|
+
|
|
227
|
+
## State transitions
|
|
228
|
+
|
|
229
|
+
Only these launch transitions are valid:
|
|
230
|
+
|
|
231
|
+
```text
|
|
232
|
+
queued -> starting
|
|
233
|
+
queued -> cancelled
|
|
234
|
+
starting -> running
|
|
235
|
+
starting -> failed
|
|
236
|
+
starting -> cancelled
|
|
237
|
+
starting -> queued # expired lease with no run bundle
|
|
238
|
+
running -> parked
|
|
239
|
+
running -> done
|
|
240
|
+
running -> failed
|
|
241
|
+
running -> cancelled
|
|
242
|
+
parked -> starting # explicit resume
|
|
243
|
+
parked -> cancelled
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
Every transition uses an expected current state and claim token when applicable. A stale handler gets
|
|
247
|
+
no ownership and performs no side effect.
|
|
248
|
+
|
|
249
|
+
When recovery finds `starting` after a lease expires:
|
|
250
|
+
|
|
251
|
+
- No run bundle: return to `queued`.
|
|
252
|
+
- Valid running bundle: reconcile to `running` and use existing run recovery.
|
|
253
|
+
- Waiting bundle: reconcile to `parked`.
|
|
254
|
+
- Terminal bundle: reconcile to `done`, `failed`, or `cancelled`.
|
|
255
|
+
- Changed workflow source or unreadable bundle: record `failed` and notify the owning session.
|
|
256
|
+
|
|
257
|
+
## Error contract
|
|
258
|
+
|
|
259
|
+
Classify startup failures into a small set of stable codes, such as:
|
|
260
|
+
|
|
261
|
+
```text
|
|
262
|
+
workflow_not_found
|
|
263
|
+
workflow_invalid
|
|
264
|
+
input_invalid
|
|
265
|
+
source_changed
|
|
266
|
+
store_unavailable
|
|
267
|
+
activation_failed
|
|
268
|
+
cancelled
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
Persist a plain safe message with a strict byte limit. Keep the original error only in transient
|
|
272
|
+
process memory for local debug logging. Never persist or send the stack, causes, raw input, prompt,
|
|
273
|
+
credential, or unbounded provider text.
|
|
274
|
+
|
|
275
|
+
A failure must remain useful. The safe message should name the check that failed and the action that
|
|
276
|
+
can correct it without including secret values.
|
|
277
|
+
|
|
278
|
+
## Implementation plan
|
|
279
|
+
|
|
280
|
+
### 1. Replace the alpha controller-store layout
|
|
281
|
+
|
|
282
|
+
Update `src/controllers/sqlite.ts` and its exported queue and notification types.
|
|
283
|
+
|
|
284
|
+
- Keep `CONTROLLER_STORE_SCHEMA` at `pi-workflows.controller-store.v1`.
|
|
285
|
+
- Change `SCHEMA_SQL` directly.
|
|
286
|
+
- Remove old alpha `ALTER TABLE` compatibility logic for the workflow queue.
|
|
287
|
+
- Add exact-layout validation after table creation.
|
|
288
|
+
- Add the launch states and safe failure fields.
|
|
289
|
+
- Generalize workflow notifications for run-level failure notices.
|
|
290
|
+
- Add a partial unique index for one `queued`, `starting`, or `running` interactive reservation per
|
|
291
|
+
origin session.
|
|
292
|
+
|
|
293
|
+
For an incompatible existing table, return a clear alpha reset error. Do not mutate it.
|
|
294
|
+
|
|
295
|
+
### 2. Add atomic launch operations
|
|
296
|
+
|
|
297
|
+
Add focused SQLite methods:
|
|
298
|
+
|
|
299
|
+
- `reservePreparedRun`
|
|
300
|
+
- `claimPreparedRun`
|
|
301
|
+
- `markPreparedRunRunning`
|
|
302
|
+
- `failPreparedRun`
|
|
303
|
+
- `cancelPreparedRun`
|
|
304
|
+
- `parkRunningRun`
|
|
305
|
+
- `finishRunningRun`
|
|
306
|
+
- `findSessionReservation`
|
|
307
|
+
- `listRecoverableRuns`
|
|
308
|
+
- `recoverExpiredStartingRun`
|
|
309
|
+
|
|
310
|
+
Use `BEGIN IMMEDIATE`, expected states, claim tokens, and lease checks.
|
|
311
|
+
|
|
312
|
+
### 3. Split preparation from activation
|
|
313
|
+
|
|
314
|
+
Refactor `src/extension/index.ts`.
|
|
315
|
+
|
|
316
|
+
Create a small `src/extension/launch-coordinator.ts` if it keeps state transitions and recovery out of
|
|
317
|
+
the extension entry point.
|
|
318
|
+
|
|
319
|
+
Preparation owns resolution, validation, run ID allocation, immutable source identity, definition
|
|
320
|
+
digest, and reservation. Activation owns claims, engine construction, recorder setup, `activeRun`,
|
|
321
|
+
running state, and executor release.
|
|
322
|
+
|
|
323
|
+
Controller child workflows remain on their controller scheduler path. Share pure workflow-resolution
|
|
324
|
+
helpers where useful, but do not make controller children wait for an interactive agent boundary.
|
|
325
|
+
|
|
326
|
+
### 4. Change the start tool
|
|
327
|
+
|
|
328
|
+
Update `src/extension/workflow-tool.ts` and the extension start handler.
|
|
329
|
+
|
|
330
|
+
Return the queued run ID only after the SQLite transaction commits. Remove the old “will start”
|
|
331
|
+
result and every success message that says the workflow already started.
|
|
332
|
+
|
|
333
|
+
### 5. Add the failure follow-up
|
|
334
|
+
|
|
335
|
+
Create a versioned custom message for launch results. Store only:
|
|
336
|
+
|
|
337
|
+
- Notification schema.
|
|
338
|
+
- Notification ID.
|
|
339
|
+
- Run ID.
|
|
340
|
+
- Workflow name.
|
|
341
|
+
- `failed` state.
|
|
342
|
+
- Safe error code and message.
|
|
343
|
+
|
|
344
|
+
Deliver it through public `pi.sendMessage` after settlement with `triggerTurn: true` and
|
|
345
|
+
`deliverAs: "followUp"`.
|
|
346
|
+
|
|
347
|
+
The notification asks the model to inspect and correct the cause. It does not automatically call
|
|
348
|
+
`workflow start`.
|
|
349
|
+
|
|
350
|
+
### 6. Update status, cancellation, and recovery
|
|
351
|
+
|
|
352
|
+
Status must read the queue before a run bundle exists. Cancellation must target queued and starting
|
|
353
|
+
runs by ID. Session startup and agent settlement must ask the launch coordinator for recoverable work
|
|
354
|
+
owned by that session.
|
|
355
|
+
|
|
356
|
+
Delete `pendingToolLaunch` and all status, cancel, shutdown, and `agent_settled` branches that depend
|
|
357
|
+
on it.
|
|
358
|
+
|
|
359
|
+
### 7. Keep rendering secondary
|
|
360
|
+
|
|
361
|
+
Render queued, starting, failed, and cancelled launch state in the TUI from durable storage. A TUI
|
|
362
|
+
notification can announce failure, but it is not the source of truth and is not the only delivery
|
|
363
|
+
surface.
|
|
364
|
+
|
|
365
|
+
### 8. Update documentation
|
|
366
|
+
|
|
367
|
+
Update `docs/workflows.md` with the queued start contract, run ID, status, cancellation, failure
|
|
368
|
+
follow-up, and model retry behavior. Update controller-store documentation with the alpha reset rule.
|
|
369
|
+
Do not document a v2 schema or migration path.
|
|
370
|
+
|
|
371
|
+
## Tests
|
|
372
|
+
|
|
373
|
+
Add or update these tests:
|
|
374
|
+
|
|
375
|
+
1. Clean alpha v1 store creation with the new exact layout.
|
|
376
|
+
2. Old alpha v1 layout rejection with a clear reset instruction.
|
|
377
|
+
3. No automatic table migration or silent deletion.
|
|
378
|
+
4. One-session reservation and different-session independence.
|
|
379
|
+
5. Durable run ID before the start tool returns.
|
|
380
|
+
6. Synchronous preflight failure with no queue record.
|
|
381
|
+
7. Zero engine activity before `agent_settled`.
|
|
382
|
+
8. Duplicate `agent_settled` and `session_start` events with one activation.
|
|
383
|
+
9. Lease expiry and recovery at every activation boundary.
|
|
384
|
+
10. Cancellation before claim, during `starting`, and before executor release.
|
|
385
|
+
11. Source change between preparation and activation.
|
|
386
|
+
12. Safe error redaction and byte bounds.
|
|
387
|
+
13. One durable launch-failure notification.
|
|
388
|
+
14. Crash before session append, after append, and before outbox acknowledgement.
|
|
389
|
+
15. Failed reservation release followed by a corrected model start.
|
|
390
|
+
16. Repeated model correction attempts with one active reservation at a time.
|
|
391
|
+
17. Status for every launch and run state.
|
|
392
|
+
18. Current child workflow, continuation, parking, resume, recorder, widget, and presentation
|
|
393
|
+
behavior.
|
|
394
|
+
|
|
395
|
+
Add a real Pi end-to-end test in `test/e2e/workflow.e2e.test.ts` with the existing mock provider:
|
|
396
|
+
|
|
397
|
+
1. The model calls `workflow start` for a valid file workflow.
|
|
398
|
+
2. The tool returns a durable queued run ID.
|
|
399
|
+
3. The test changes or removes the workflow source before the initiating turn settles.
|
|
400
|
+
4. Activation records `failed`.
|
|
401
|
+
5. Pi sends one follow-up model turn with the safe error.
|
|
402
|
+
6. The model corrects the request and calls `workflow start` again.
|
|
403
|
+
7. The new run starts and reaches its first workflow step.
|
|
404
|
+
8. The first failed run remains inspectable by ID.
|
|
405
|
+
|
|
406
|
+
The test must use the packaged extension and real Pi lifecycle events. It must not call a real model
|
|
407
|
+
or external service.
|
|
408
|
+
|
|
409
|
+
## Acceptance criteria
|
|
410
|
+
|
|
411
|
+
- The start tool never reports success without a committed queued record and final run ID.
|
|
412
|
+
- The model can correct synchronous start errors in the same turn.
|
|
413
|
+
- A deferred startup failure always becomes durable before notification.
|
|
414
|
+
- One failure notification starts one new model turn.
|
|
415
|
+
- The model can correct the cause and start a new run.
|
|
416
|
+
- Failed and cancelled launches release the session reservation.
|
|
417
|
+
- No two runs activate for one reservation.
|
|
418
|
+
- Reload, restart, compaction, and repeated settlement do not lose or duplicate a launch.
|
|
419
|
+
- Status and cancellation work before a run bundle exists.
|
|
420
|
+
- Private input and error details do not leak.
|
|
421
|
+
- No Pi change, new service, new database, v2 schema, compatibility path, or migration exists.
|
|
422
|
+
- The old `pendingToolLaunch` path is gone.
|
|
423
|
+
|
|
424
|
+
## Verification
|
|
425
|
+
|
|
426
|
+
Run the canonical repository gates:
|
|
427
|
+
|
|
428
|
+
```bash
|
|
429
|
+
npm run check
|
|
430
|
+
npm run test:e2e
|
|
431
|
+
npx slophammer-ts@latest dry .
|
|
432
|
+
npx slophammer-ts@latest check . --only ts.dependency-boundaries-required
|
|
433
|
+
git diff --check
|
|
434
|
+
```
|
|
435
|
+
|
|
436
|
+
Run Pi Reviewer against the base branch and fix all valid P0 and P1 findings before release work.
|
|
437
|
+
|
|
438
|
+
Package publication and OnurPi adoption are separate tasks. Do not edit an installed `node_modules`
|
|
439
|
+
copy as the implementation source.
|
|
440
|
+
|
|
441
|
+
## Non-goals
|
|
442
|
+
|
|
443
|
+
- Do not change Pi or propose a new Pi API.
|
|
444
|
+
- Do not add a service, daemon, remote queue, telemetry endpoint, or second database.
|
|
445
|
+
- Do not preserve old alpha controller-store layouts.
|
|
446
|
+
- Do not add a migration or v2 schema.
|
|
447
|
+
- Do not add blind automatic workflow retries.
|
|
448
|
+
- Do not change built-in workflow behavior except for test fixtures needed to verify startup.
|
|
449
|
+
- Do not publish or adopt a package as part of this documentation change.
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Confirm blockers before autoimplement stops
|
|
3
|
+
author: Onur Solmaz <2453968+osolmaz@users.noreply.github.com>
|
|
4
|
+
date: 2026-08-20
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Confirm blockers before autoimplement stops
|
|
8
|
+
|
|
9
|
+
Autoimplement must not stop only because one model says that work is blocked. A separate agent must challenge the claim and confirm that the blocker is real before the workflow uses its terminal blocked result.
|
|
10
|
+
|
|
11
|
+
The canonical workflow behavior is in [Workflow authoring reference](../workflows.md#built-in-planning-and-implementation).
|
|
12
|
+
|
|
13
|
+
## Outcome
|
|
14
|
+
|
|
15
|
+
Add one independent `challengeBlocker` agent node to the built-in autoimplement workflow. Use only existing public Pi Workflows primitives. Keep the graph explicit and reuse the existing redesign include and terminal blocked result.
|
|
16
|
+
|
|
17
|
+
The challenge asks these questions in plain terms:
|
|
18
|
+
|
|
19
|
+
- Are you really blocked?
|
|
20
|
+
- Is this really a blocker right now?
|
|
21
|
+
- Can you find a safe way to move forward and finish this?
|
|
22
|
+
- Are you getting stuck on something trivial, procedural, reversible, or already authorized?
|
|
23
|
+
|
|
24
|
+
The challenge inspects the task, approved plan, current result, evidence, scope, authority, previous attempts, and viable alternatives. It distinguishes a true external blocker from normal rollout work, local implementation work, a design adjustment, a missing verification step, or a reversible operational task.
|
|
25
|
+
|
|
26
|
+
## Output and validation
|
|
27
|
+
|
|
28
|
+
The challenge returns this bounded structured output:
|
|
29
|
+
|
|
30
|
+
```json
|
|
31
|
+
{
|
|
32
|
+
"route": "continue | blocked",
|
|
33
|
+
"blockingNow": true,
|
|
34
|
+
"outsideAuthority": true,
|
|
35
|
+
"canProceed": false,
|
|
36
|
+
"reason": "concise reason",
|
|
37
|
+
"nextAction": "",
|
|
38
|
+
"alternativesChecked": ["checked alternative"],
|
|
39
|
+
"evidence": ["concrete evidence"]
|
|
40
|
+
}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
A terminal blocked result is valid only when all these conditions hold:
|
|
44
|
+
|
|
45
|
+
- `route` is `blocked`.
|
|
46
|
+
- `blockingNow` is `true`.
|
|
47
|
+
- `outsideAuthority` is `true`.
|
|
48
|
+
- `canProceed` is `false`.
|
|
49
|
+
- `nextAction` is empty.
|
|
50
|
+
- `alternativesChecked` contains checked practical alternatives.
|
|
51
|
+
- `evidence` contains concrete evidence.
|
|
52
|
+
|
|
53
|
+
A `continue` result must name the next practical action. Contradictory output, such as `route: blocked` with `canProceed: true`, cannot route to the terminal blocked result.
|
|
54
|
+
|
|
55
|
+
Keep the output concise and bounded.
|
|
56
|
+
|
|
57
|
+
## Routing
|
|
58
|
+
|
|
59
|
+
Route `classifyImplementation.blocked` to `challengeBlocker`. A `continue` challenge result routes to the existing redesign include. Redesign can revise and document the plan, then return through implementation, verification, review, comment handling, CI, and delivery. A valid `blocked` result routes to the existing terminal blocked result.
|
|
60
|
+
|
|
61
|
+
Inspect each later model-produced blocked exit in verification, review, comment handling, CI, delivery, and equivalent current nodes. Route operational or model-judged blockers through the same challenge when it is safe. Use one reusable challenge node and existing edges or includes. Do not copy the prompt into several nodes and do not add an engine-level blocker feature.
|
|
62
|
+
|
|
63
|
+
Preserve direct terminal stops for these hard boundaries:
|
|
64
|
+
|
|
65
|
+
- an explicit human stop;
|
|
66
|
+
- cancellation;
|
|
67
|
+
- an exhausted workflow safety or replan limit;
|
|
68
|
+
- a protected authorization gap;
|
|
69
|
+
- an independent blocked result from redesign when another challenge could make an unsafe or unbounded loop.
|
|
70
|
+
|
|
71
|
+
Limit blocker challenges to three attempts in one run. Include all earlier challenge outputs in each later challenge prompt. If a fourth challenge would be needed, stop with the normal safety-limit reason. Do not repeat an unsupported blocker assertion.
|
|
72
|
+
|
|
73
|
+
Include the latest challenge output in the workflow's latest-issue or context helper. A later redesign must receive the rejected blocker, its evidence, and its required next action.
|
|
74
|
+
|
|
75
|
+
## Required behavior
|
|
76
|
+
|
|
77
|
+
A supported cutover does not become blocked only because an artifact has an ownership or packaging mismatch. In the Bob artifact incident, authorized deployment and rollback make the safe rollout work part of the task. The challenge must return `continue`, name the next rollout action, and route to redesign.
|
|
78
|
+
|
|
79
|
+
A missing external authorization for a prohibited remote mutation can remain blocked when no non-mutating path completes the task.
|
|
80
|
+
|
|
81
|
+
A local test failure, stale package, packaging mismatch, rollback preparation, or deployment procedure is not, by itself, outside the granted authority.
|
|
82
|
+
|
|
83
|
+
## Tests
|
|
84
|
+
|
|
85
|
+
Add focused tests in `test/builtin-autoimplement.test.ts` or the best current autoimplement test file. Prove all these cases:
|
|
86
|
+
|
|
87
|
+
1. A false blocker routes to redesign and continued work.
|
|
88
|
+
2. A confirmed blocker reaches the terminal blocked result.
|
|
89
|
+
3. Contradictory blocked output is rejected or cannot route to the terminal blocked result.
|
|
90
|
+
4. The Bob artifact-ownership mismatch returns `continue` when rollout is authorized.
|
|
91
|
+
5. An explicit human stop bypasses the challenge.
|
|
92
|
+
6. A protected authorization gap remains a hard stop.
|
|
93
|
+
7. An independent `redesign.blocked` path does not create a loop.
|
|
94
|
+
8. Earlier challenge context is present and the three-attempt bound works.
|
|
95
|
+
9. All relevant late-stage model-generated blocker routes use the challenge.
|
|
96
|
+
10. Normal success paths remain unchanged.
|
|
97
|
+
|
|
98
|
+
Keep compute nodes pure. Put the independent reasoning in the challenge agent node.
|
|
99
|
+
|
|
100
|
+
## Documentation
|
|
101
|
+
|
|
102
|
+
Update `docs/workflows.md` with the blocker-confirmation rule, hard-stop exceptions, and bounded routing. Keep the public behavior concise. Do not add internal details that users do not need.
|
|
103
|
+
|
|
104
|
+
## Verification
|
|
105
|
+
|
|
106
|
+
Run focused tests during development. Before completion, run these exact checks:
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
npm run check
|
|
110
|
+
npm run test:e2e
|
|
111
|
+
npx slophammer-ts@latest dry .
|
|
112
|
+
npx slophammer-ts@latest check . --only ts.dependency-boundaries-required
|
|
113
|
+
git diff --check
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
Verify discovery and behavior through the installed package path or the repository's supported real-Pi end-to-end path. A fresh Pi process is required because built-in discovery is process-local.
|
|
117
|
+
|
|
118
|
+
Review the final diff for missing blocker routes, accidental unbounded loops, and false terminal blockers. Fix each valid finding.
|
|
119
|
+
|
|
120
|
+
## Boundaries
|
|
121
|
+
|
|
122
|
+
- Preserve current run-bundle schemas unless a schema change is required. Do not add a persistence layer.
|
|
123
|
+
- Use existing public Pi Workflows primitives only. Do not change Pi core.
|
|
124
|
+
- Keep explicit human and protected authorization boundaries intact.
|
|
125
|
+
- Use a hard cutover. Do not retain a legacy blocker route.
|
|
126
|
+
- Preserve unrelated work and do not modify other repositories.
|
|
127
|
+
- Make only the smallest source-based adjustment needed to preserve the approved behavior.
|
|
128
|
+
- Do not stop for a trivial, reversible, or already authorized issue.
|
|
129
|
+
- Commit coherent changes with a Conventional Commit message and push after all checks pass.
|
|
130
|
+
- Do not publish a package, create a release, or merge anything.
|
|
131
|
+
|
|
132
|
+
## Contract impact
|
|
133
|
+
|
|
134
|
+
- **Session state:** normal workflow messages and tool results only.
|
|
135
|
+
- **Other persistent data:** none beyond the existing run bundle records for normal node outputs.
|
|
136
|
+
- **Pi internals:** none.
|
|
137
|
+
- **Public Pi API:** existing documented extension APIs only.
|
|
138
|
+
- **Public Pi Workflows API:** existing agent, compute, edge, and included-workflow primitives only.
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Keep the Herdr plugin linked after package updates
|
|
3
|
+
author: Onur Solmaz <2453968+osolmaz@users.noreply.github.com>
|
|
4
|
+
date: 2026-08-20
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Keep the Herdr plugin linked after package updates
|
|
8
|
+
|
|
9
|
+
Pi Workflows ships its Herdr plugin inside the npm package. Herdr records the package's absolute path. npm can move an installed package between nested and hoisted `node_modules` directories during an update, which leaves Herdr linked to a path that no longer exists.
|
|
10
|
+
|
|
11
|
+
Pi Workflows will own one explicit command that finds its own package and repairs this link. OnurPi will call that command after it installs an exact reviewed Pi Workflows release. OnurPi will not contain Herdr paths, manifests, or link-repair rules.
|
|
12
|
+
|
|
13
|
+
## Outcome
|
|
14
|
+
|
|
15
|
+
The canonical command is:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
pi-workflows herdr sync --json
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
`pi-workflows herdr setup` remains an alias for compatibility.
|
|
22
|
+
|
|
23
|
+
The command validates the bundled package before it changes Herdr. It then creates a missing link, enables a disabled link, leaves a correct link unchanged, or replaces a link to another package path or version. A successful result requires a new Herdr query that shows the expected plugin ID, package version, package root, manifest path, enabled state, and no warning.
|
|
24
|
+
|
|
25
|
+
## Scope
|
|
26
|
+
|
|
27
|
+
### Pi Workflows
|
|
28
|
+
|
|
29
|
+
- Resolve the installed package root from the running CLI.
|
|
30
|
+
- Validate `package.json`, `herdr-plugin.toml`, and the bundled viewer before changing Herdr.
|
|
31
|
+
- Reconcile the current registration through Herdr's public CLI.
|
|
32
|
+
- Return a versioned JSON result with one of `linked`, `relinked`, `enabled`, `unchanged`, or `unavailable`.
|
|
33
|
+
- Return `unavailable` only when the Herdr executable is absent.
|
|
34
|
+
- Treat malformed package data, malformed Herdr output, command failures, identity conflicts, and failed verification as errors.
|
|
35
|
+
- Keep output bounded and use argument arrays instead of shell commands.
|
|
36
|
+
- Test source and packed-package layouts, including paths that contain spaces.
|
|
37
|
+
|
|
38
|
+
### OnurPi
|
|
39
|
+
|
|
40
|
+
- Keep the Pi Workflows dependency pinned to an exact reviewed release.
|
|
41
|
+
- Invoke the local `pi-workflows herdr sync --json` command from an explicit TypeScript sync script after dependency installation.
|
|
42
|
+
- Accept the versioned result and keep Herdr-specific behavior in Pi Workflows.
|
|
43
|
+
- Keep package installation free of `postinstall` side effects.
|
|
44
|
+
|
|
45
|
+
## Non-goals
|
|
46
|
+
|
|
47
|
+
- Do not create another Herdr plugin package or release.
|
|
48
|
+
- Do not duplicate the manifest or viewer in OnurPi.
|
|
49
|
+
- Do not add hard-coded `node_modules` paths.
|
|
50
|
+
- Do not edit Herdr state files directly.
|
|
51
|
+
- Do not change Herdr core or Pi core.
|
|
52
|
+
- Do not hot-reload running Pi processes. A running process still needs `/reload` or restart after a package update.
|
|
53
|
+
- Do not add a service, watcher, or implicit package-install mutation.
|
|
54
|
+
|
|
55
|
+
## Command contract
|
|
56
|
+
|
|
57
|
+
The JSON result uses schema `pi-workflows.herdr-sync.v1` and contains:
|
|
58
|
+
|
|
59
|
+
- the result status;
|
|
60
|
+
- whether Herdr state changed;
|
|
61
|
+
- the plugin ID;
|
|
62
|
+
- the expected and effective versions when available;
|
|
63
|
+
- the effective enabled state when available;
|
|
64
|
+
- a plain summary; and
|
|
65
|
+
- an advisory that running Pi processes must reload after a package update.
|
|
66
|
+
|
|
67
|
+
A missing Herdr executable returns `unavailable` with exit code zero because Herdr is an optional integration. Every other failure returns a nonzero exit code and does not claim success.
|
|
68
|
+
|
|
69
|
+
The command preflights the new package before unlinking an old registration. Herdr currently exposes separate unlink and link commands, so replacement cannot be atomic. If replacement fails, Pi Workflows makes one restore attempt only when the previous package root still passes the same validation. It then reports the state found by a fresh Herdr query.
|
|
70
|
+
|
|
71
|
+
Concurrent sync commands converge on the same target. After a failed or ambiguous mutation, the command queries Herdr and adopts the result only when another process already reached the exact expected state. It does not repeat the same mutation blindly.
|
|
72
|
+
|
|
73
|
+
## Compatibility
|
|
74
|
+
|
|
75
|
+
Existing `herdr setup` callers use the same implementation. Other Pi Workflows CLI commands do not change. The npm package remains the only source of the plugin manifest and viewer.
|
|
76
|
+
|
|
77
|
+
OnurPi adds only invocation timing and result handling. It does not parse the Herdr manifest or issue link commands.
|
|
78
|
+
|
|
79
|
+
## Verification
|
|
80
|
+
|
|
81
|
+
### Pi Workflows
|
|
82
|
+
|
|
83
|
+
- Test first link, unchanged link, disabled link, moved package path, stale path, and version update.
|
|
84
|
+
- Test missing Herdr, malformed manifests, malformed plugin records, command failures, post-action mismatches, and bounded restore behavior.
|
|
85
|
+
- Test concurrent adoption and paths with spaces.
|
|
86
|
+
- Run the CLI from `npm pack` contents in nested and hoisted layouts.
|
|
87
|
+
- Run `npm run check`, `npm run test:e2e`, Slophammer, SimpleDoc, and diff checks.
|
|
88
|
+
|
|
89
|
+
### OnurPi
|
|
90
|
+
|
|
91
|
+
- Test every structured result and invalid command output with a fake executable.
|
|
92
|
+
- Verify the wrapper contains no plugin ID, manifest copy, Herdr mutation command, or package path.
|
|
93
|
+
- Verify root and wrapper dependency pins remain equal.
|
|
94
|
+
- Run `npm run check`, `npm run slophammer`, SimpleDoc, and diff checks.
|
|
95
|
+
|
|
96
|
+
### Adoption
|
|
97
|
+
|
|
98
|
+
After a separately approved Pi Workflows release, update OnurPi to that exact version and run:
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
npm run workflows:sync
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Then verify the Herdr plugin list, open `piw`, and start or reload Pi to confirm resource discovery. Repeating the sync must return `unchanged`.
|