@osolmaz/pi-workflows 0.9.0 → 0.10.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.
- package/README.md +50 -16
- package/dist/builtins/autodevise.workflow.d.ts +58 -0
- package/dist/builtins/autodevise.workflow.js +190 -0
- package/dist/builtins/autodevise.workflow.js.map +1 -0
- package/dist/builtins/autoimplement.workflow.d.ts +154 -0
- package/dist/builtins/autoimplement.workflow.js +729 -0
- package/dist/builtins/autoimplement.workflow.js.map +1 -0
- package/dist/builtins/catalog.js +5 -1
- package/dist/builtins/catalog.js.map +1 -1
- package/dist/builtins/index.d.ts +3 -0
- package/dist/builtins/index.js +4 -0
- package/dist/builtins/index.js.map +1 -0
- package/dist/builtins/monitor.workflow.d.ts +25 -3
- package/dist/builtins/monitor.workflow.js +200 -13
- package/dist/builtins/monitor.workflow.js.map +1 -1
- package/dist/extension/herdr-viewer.js +2 -6
- package/dist/extension/herdr-viewer.js.map +1 -1
- package/dist/render/graph-render.js +13 -2
- package/dist/render/graph-render.js.map +1 -1
- package/dist/workflows/catalog.d.ts +1 -0
- package/dist/workflows/catalog.js +6 -0
- package/dist/workflows/catalog.js.map +1 -1
- package/dist/workflows/composition.d.ts +45 -0
- package/dist/workflows/composition.js +471 -0
- package/dist/workflows/composition.js.map +1 -0
- package/dist/workflows/decision.d.ts +11 -5
- package/dist/workflows/decision.js.map +1 -1
- package/dist/workflows/definition.d.ts +22 -3
- package/dist/workflows/definition.js +46 -3
- package/dist/workflows/definition.js.map +1 -1
- package/dist/workflows/engine.js +115 -16
- package/dist/workflows/engine.js.map +1 -1
- package/dist/workflows/graph.js +8 -6
- package/dist/workflows/graph.js.map +1 -1
- package/dist/workflows/index.d.ts +3 -2
- package/dist/workflows/index.js +2 -1
- package/dist/workflows/index.js.map +1 -1
- package/dist/workflows/loader.d.ts +5 -4
- package/dist/workflows/loader.js +118 -18
- package/dist/workflows/loader.js.map +1 -1
- package/dist/workflows/schema.d.ts +3 -1
- package/dist/workflows/schema.js +49 -2
- package/dist/workflows/schema.js.map +1 -1
- package/dist/workflows/store.js +32 -2
- package/dist/workflows/store.js.map +1 -1
- package/dist/workflows/types.d.ts +77 -2
- package/docs/CONTROLLERS.md +1 -1
- package/docs/DESIGN_PHILOSOPHY.md +1 -1
- package/docs/MONITOR.md +35 -18
- package/docs/WORKFLOW_COMPOSITION.md +326 -0
- package/docs/plans/2026-08-19-workflow-composition-plan.md +300 -0
- package/docs/run-bundles.md +24 -10
- package/docs/workflows.md +65 -12
- package/examples/workflows/autodevise.workflow.ts +1 -0
- package/examples/workflows/autoimplement.workflow.ts +1 -92
- package/herdr-plugin.toml +1 -1
- package/package.json +5 -1
- package/skills/monitor/SKILL.md +6 -1
- package/skills/pi-workflows/SKILL.md +3 -1
- package/src/builtins/autodevise.workflow.ts +231 -0
- package/src/builtins/autoimplement.workflow.ts +856 -0
- package/src/builtins/catalog.ts +5 -1
- package/src/builtins/index.ts +13 -0
- package/src/builtins/monitor.workflow.ts +242 -15
- package/src/extension/herdr-viewer.ts +1 -6
- package/src/render/graph-render.ts +14 -2
- package/src/workflows/catalog.ts +7 -0
- package/src/workflows/composition.ts +627 -0
- package/src/workflows/decision.ts +12 -5
- package/src/workflows/definition.ts +118 -8
- package/src/workflows/engine.ts +151 -18
- package/src/workflows/graph.ts +8 -6
- package/src/workflows/index.ts +20 -0
- package/src/workflows/loader.ts +186 -18
- package/src/workflows/schema.ts +62 -2
- package/src/workflows/store.ts +37 -2
- package/src/workflows/types.ts +109 -2
- package/examples/workflows/elegant-solution.workflow.ts +0 -95
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Add typed workflow composition and automatic repair
|
|
3
|
+
author: Onur Solmaz <2453968+osolmaz@users.noreply.github.com>
|
|
4
|
+
date: 2026-08-19
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Add typed workflow composition and automatic repair
|
|
8
|
+
|
|
9
|
+
Pi Workflows needs reusable nested workflows with normal TypeScript imports. Monitor must be able to devise and implement a repair when mutation is authorized. Autoimplement must move back to solution design when new evidence invalidates its plan, correct failed reviewer commands, track P0, P1, and P2 findings, and use long CI waits for additional local testing.
|
|
10
|
+
|
|
11
|
+
The canonical behavior is in [Workflow composition](../WORKFLOW_COMPOSITION.md).
|
|
12
|
+
|
|
13
|
+
## Outcome
|
|
14
|
+
|
|
15
|
+
A workflow can run alone or as a typed child in one durable parent run. Authors can import a child definition, map its input, and route from named exits. Dynamic project, global, built-in, and path references remain available.
|
|
16
|
+
|
|
17
|
+
The package will ship `autodevise`, `autoimplement`, and `monitor` as compatible built-ins. Monitor will remain observation-only by default. An explicitly authorized monitor can run outer solution design, autoimplementation, internal redesign, and a fresh target check without copying workflow nodes.
|
|
18
|
+
|
|
19
|
+
## Scope
|
|
20
|
+
|
|
21
|
+
### Public workflow API
|
|
22
|
+
|
|
23
|
+
- Add generic workflow input and named-exit contracts.
|
|
24
|
+
- Add runtime input and exit validation.
|
|
25
|
+
- Add direct typed `includeWorkflow(child, options)` composition.
|
|
26
|
+
- Keep dynamic `includeWorkflow({ workflow, input })` references.
|
|
27
|
+
- Add `defineWorkflowRegistry()` for shipped typed definitions.
|
|
28
|
+
- Type-check child input mappers and parent exit names where the child is imported directly.
|
|
29
|
+
|
|
30
|
+
### Resolution and execution
|
|
31
|
+
|
|
32
|
+
- Resolve all direct and dynamic includes before `run_started`.
|
|
33
|
+
- Reject direct and indirect source cycles.
|
|
34
|
+
- Compile one qualified executable graph.
|
|
35
|
+
- Give child callbacks a local invocation view.
|
|
36
|
+
- Support repeated and nested mounts.
|
|
37
|
+
- Apply root and per-invocation step limits.
|
|
38
|
+
- Record named entry and exit transitions.
|
|
39
|
+
- Keep one pause, cancellation, checkpoint, update, notification, and presentation lifecycle.
|
|
40
|
+
|
|
41
|
+
### Persistence and display
|
|
42
|
+
|
|
43
|
+
- Record all child sources and their mount paths.
|
|
44
|
+
- Record a digest of the resolved definition.
|
|
45
|
+
- Refuse normal resume when any source or digest changes.
|
|
46
|
+
- Add mount data to the definition snapshot.
|
|
47
|
+
- Add include entry and exit trace events.
|
|
48
|
+
- Group qualified child nodes in viewers while preserving flat replay.
|
|
49
|
+
|
|
50
|
+
### Workflow library
|
|
51
|
+
|
|
52
|
+
- Make `autodevise` accept an existing plan and new evidence.
|
|
53
|
+
- Add `ready` and `blocked` exits to `autodevise`.
|
|
54
|
+
- Rebuild `autoimplement` around explicit issue routes.
|
|
55
|
+
- Include `autodevise` inside `autoimplement` for redesign.
|
|
56
|
+
- Track P0, P1, and P2 review findings by round.
|
|
57
|
+
- Rerun review only after P0 or P1 work.
|
|
58
|
+
- Permit P2 work without another reviewer round.
|
|
59
|
+
- Generate, validate, execute, and correct exact Pi Reviewer commands.
|
|
60
|
+
- Track PR comments, CI, merge, and final PR reporting.
|
|
61
|
+
- Bound one CI watch to five minutes.
|
|
62
|
+
- Route a long CI wait to useful local testing before checking CI again.
|
|
63
|
+
- Add authorized repair to monitor through outer `autodevise` and `autoimplement` mounts.
|
|
64
|
+
- Detect repeated repair with no changed issue, plan, implementation, evidence, or target state.
|
|
65
|
+
|
|
66
|
+
## Non-goals
|
|
67
|
+
|
|
68
|
+
- Do not change Pi core or use private Pi APIs.
|
|
69
|
+
- Do not create another run store, service, or child Pi session.
|
|
70
|
+
- Do not let a child jump into a parent or sibling node.
|
|
71
|
+
- Do not permit recursive include graphs.
|
|
72
|
+
- Do not add unrestricted model-selected node names.
|
|
73
|
+
- Do not let monitor mutate a target without explicit authorization.
|
|
74
|
+
- Do not replace Pi Reviewer with another reviewer after an invocation failure.
|
|
75
|
+
- Do not merge before required gates pass or an allowed unrelated failure is recorded.
|
|
76
|
+
- Do not rewrite existing terminal run bundles.
|
|
77
|
+
|
|
78
|
+
## Data contracts
|
|
79
|
+
|
|
80
|
+
### Child result
|
|
81
|
+
|
|
82
|
+
A completed child mount exposes a discriminated result:
|
|
83
|
+
|
|
84
|
+
```typescript
|
|
85
|
+
type IncludedResult<TExits> = {
|
|
86
|
+
[K in keyof TExits]: { exit: K; output: TExits[K] };
|
|
87
|
+
}[keyof TExits];
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### Source record
|
|
91
|
+
|
|
92
|
+
```typescript
|
|
93
|
+
type WorkflowMountedSource = {
|
|
94
|
+
mountPath: string[];
|
|
95
|
+
workflowName: string;
|
|
96
|
+
source: WorkflowSource;
|
|
97
|
+
};
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### Review round
|
|
101
|
+
|
|
102
|
+
```typescript
|
|
103
|
+
type ReviewRound = {
|
|
104
|
+
command: StructuredCommand;
|
|
105
|
+
p0: ReviewFinding[];
|
|
106
|
+
p1: ReviewFinding[];
|
|
107
|
+
p2: ReviewFinding[];
|
|
108
|
+
lower: ReviewFinding[];
|
|
109
|
+
invocationSucceeded: boolean;
|
|
110
|
+
};
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
P0 or P1 findings route to redesign or fix, then verification, push, and another review. P2-only work routes through a separate verification path and then continues to PR comments without another review.
|
|
114
|
+
|
|
115
|
+
### CI decision
|
|
116
|
+
|
|
117
|
+
```typescript
|
|
118
|
+
type CiDecision = {
|
|
119
|
+
status: "green" | "failed" | "pending" | "unavailable";
|
|
120
|
+
relatedFailures: string[];
|
|
121
|
+
unrelatedFailures: string[];
|
|
122
|
+
trackingCommand?: StructuredCommand;
|
|
123
|
+
};
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
A pending result must include a validated `gh` tracking command. The shell action stops after five minutes. A timeout routes to opportunistic local testing. The workflow checks CI again after that work.
|
|
127
|
+
|
|
128
|
+
## Implementation sequence
|
|
129
|
+
|
|
130
|
+
### 1. Typed contracts
|
|
131
|
+
|
|
132
|
+
- Make workflow definitions generic over input and exits.
|
|
133
|
+
- Add source identity, input parser, exit parser, and include declarations.
|
|
134
|
+
- Add direct and dynamic `includeWorkflow()` overloads.
|
|
135
|
+
- Add registry helpers and compile-time contract tests.
|
|
136
|
+
- Keep old workflow definitions valid.
|
|
137
|
+
|
|
138
|
+
### 2. Composition resolver
|
|
139
|
+
|
|
140
|
+
- Resolve nested direct and dynamic definitions.
|
|
141
|
+
- Preserve parent-relative paths.
|
|
142
|
+
- Build the complete source list.
|
|
143
|
+
- Reject source cycles with the mount chain.
|
|
144
|
+
- Validate child exit nodes and parent include edges.
|
|
145
|
+
- Compile qualified nodes and internal entry and exit transitions.
|
|
146
|
+
- Attach immutable composition metadata to the resolved definition.
|
|
147
|
+
|
|
148
|
+
### 3. Local execution context
|
|
149
|
+
|
|
150
|
+
- Project each child callback to its mount-local input, outputs, results, and steps.
|
|
151
|
+
- Hide parent and sibling data.
|
|
152
|
+
- Start each re-entry after the latest mount-entry step.
|
|
153
|
+
- Expose only the latest named child result to the parent.
|
|
154
|
+
- Enforce per-invocation and root step limits.
|
|
155
|
+
- Emit include entry and exit events.
|
|
156
|
+
|
|
157
|
+
### 4. Persistence and resume
|
|
158
|
+
|
|
159
|
+
- Add mounted sources and the resolved digest to state and manifest projections.
|
|
160
|
+
- Add mount metadata to definition snapshots.
|
|
161
|
+
- Compare the complete identity during resume and continuation.
|
|
162
|
+
- Keep old single-source bundles readable.
|
|
163
|
+
- Add trace reconstruction and torn-tail tests for include transitions.
|
|
164
|
+
|
|
165
|
+
### 5. Viewer grouping
|
|
166
|
+
|
|
167
|
+
- Derive groups from snapshot mount metadata.
|
|
168
|
+
- Keep qualified names in replay and details.
|
|
169
|
+
- Update TypeScript and Rust fixtures together.
|
|
170
|
+
|
|
171
|
+
### 6. Autodevise
|
|
172
|
+
|
|
173
|
+
- Move the current workflow into the built-in library.
|
|
174
|
+
- Add typed input, `ready`, and `blocked` exits.
|
|
175
|
+
- Accept a previous plan and new evidence.
|
|
176
|
+
- Calculate a plan digest.
|
|
177
|
+
- Return whether the selected plan changed.
|
|
178
|
+
- Keep the current practical, in-scope selection rule.
|
|
179
|
+
|
|
180
|
+
### 7. Autoimplement
|
|
181
|
+
|
|
182
|
+
Build this graph:
|
|
183
|
+
|
|
184
|
+
```text
|
|
185
|
+
prepare plan
|
|
186
|
+
-> implement
|
|
187
|
+
-> verify
|
|
188
|
+
-> classify issue
|
|
189
|
+
-> redesign -> autodevise -> implement
|
|
190
|
+
-> fix -> verify
|
|
191
|
+
-> publish -> write reviewer command -> run reviewer
|
|
192
|
+
|
|
193
|
+
review result
|
|
194
|
+
-> command error -> rewrite command -> run reviewer
|
|
195
|
+
-> P0/P1 -> classify issue
|
|
196
|
+
-> P2 -> address P2 -> verify P2 -> PR comments
|
|
197
|
+
-> clean -> PR comments
|
|
198
|
+
|
|
199
|
+
PR comments
|
|
200
|
+
-> redesign
|
|
201
|
+
-> fix
|
|
202
|
+
-> CI
|
|
203
|
+
|
|
204
|
+
CI
|
|
205
|
+
-> related design failure -> redesign
|
|
206
|
+
-> related local failure -> fix
|
|
207
|
+
-> pending -> track for at most five minutes
|
|
208
|
+
-> tracking timeout -> run additional useful tests -> check CI
|
|
209
|
+
-> green or allowed unrelated failure -> merge
|
|
210
|
+
-> unavailable or forbidden -> blocked
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
The workflow will collect all review rounds in its final output. It will never rerun Pi Reviewer solely because P2 work changed files.
|
|
214
|
+
|
|
215
|
+
### 8. Monitor repair
|
|
216
|
+
|
|
217
|
+
- Add explicit repair authorization to monitor input.
|
|
218
|
+
- Add `repair` to the check result only when authorization is present.
|
|
219
|
+
- Mount outer `autodevise` and `autoimplement`.
|
|
220
|
+
- Pass the outer plan into autoimplement.
|
|
221
|
+
- Check the target again after a reported repair.
|
|
222
|
+
- Stop on repeated no-progress evidence.
|
|
223
|
+
- Keep ordinary monitor calls observation-only and backward compatible.
|
|
224
|
+
|
|
225
|
+
### 9. Skills and docs
|
|
226
|
+
|
|
227
|
+
- Update workflow authoring, persistence, monitor, and controller-boundary docs.
|
|
228
|
+
- Align the public monitor and autoimplement skills with the new workflows.
|
|
229
|
+
- Run the normal agent synchronization command in the source repository after skill edits.
|
|
230
|
+
- Record any implementation departure in this plan and the canonical specification.
|
|
231
|
+
|
|
232
|
+
## Acceptance criteria
|
|
233
|
+
|
|
234
|
+
- Direct imports infer child input and named exits.
|
|
235
|
+
- Invalid direct input mappings fail type checking.
|
|
236
|
+
- Invalid child exit names fail type checking or definition validation.
|
|
237
|
+
- Dynamic references resolve through existing precedence rules.
|
|
238
|
+
- Two mounts of one child do not share invocation state.
|
|
239
|
+
- Nested redesign uses a fresh autodevise invocation.
|
|
240
|
+
- Source cycles fail before the run bundle is created.
|
|
241
|
+
- Included checkpoints, updates, notifications, pause, cancellation, and resume behave like root nodes.
|
|
242
|
+
- Every mounted source and the resolved digest is durable.
|
|
243
|
+
- P0 and P1 work triggers another review round.
|
|
244
|
+
- P2-only work can be addressed without another review round.
|
|
245
|
+
- A failed reviewer command can be corrected and rerun without changing reviewer tools.
|
|
246
|
+
- A pending CI decision supplies an exact command.
|
|
247
|
+
- A CI wait longer than five minutes routes to useful local testing.
|
|
248
|
+
- Monitor mutates only when repair is explicitly authorized.
|
|
249
|
+
- A repaired monitor target is checked again.
|
|
250
|
+
- Repeated repair without changed evidence stops as blocked.
|
|
251
|
+
- Existing workflows and terminal run bundles remain valid.
|
|
252
|
+
|
|
253
|
+
## Verification
|
|
254
|
+
|
|
255
|
+
Run focused checks while implementing:
|
|
256
|
+
|
|
257
|
+
```bash
|
|
258
|
+
npx vitest run test/composition.test.ts test/graph.test.ts test/loader.test.ts
|
|
259
|
+
npx vitest run test/engine.test.ts test/run-resume.test.ts test/store.test.ts
|
|
260
|
+
npx vitest run test/builtin-autodevise.test.ts test/builtin-autoimplement.test.ts test/builtin-monitor.test.ts
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
Run all repository gates before review:
|
|
264
|
+
|
|
265
|
+
```bash
|
|
266
|
+
npm run check
|
|
267
|
+
npm run test:e2e
|
|
268
|
+
npx slophammer-ts@latest dry .
|
|
269
|
+
npx slophammer-ts@latest check . --only ts.dependency-boundaries-required
|
|
270
|
+
npx -y @simpledoc/simpledoc check
|
|
271
|
+
git diff --check
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
Run Pi Reviewer against the pushed branch. Fix every P0 and P1 finding and rerun it. P2-only changes do not require another reviewer run unless they expose a new P0 or P1 concern. Check PR comments and CI after review passes.
|
|
275
|
+
|
|
276
|
+
## Release
|
|
277
|
+
|
|
278
|
+
This adds compatible public APIs to a pre-1.0 package. The planned version is `0.10.0`. Existing `0.9.x` run bundles remain readable, and no published version is rewritten.
|
|
279
|
+
|
|
280
|
+
## Implementation record
|
|
281
|
+
|
|
282
|
+
The implementation follows the canonical specification with one visible detail: compiled include entry and exit transitions have durable internal step records so resume can reconstruct the active invocation. They do not consume root or child `maxSteps` limits. Viewers label them as entry and named-exit transitions.
|
|
283
|
+
|
|
284
|
+
Direct imports check mapped input and exit names. `includedResult()` recovers the discriminated child result type from a parent output. Dynamic overrides carry a direct contract definition and must match its stable `contractId`, input presence, and named exits.
|
|
285
|
+
|
|
286
|
+
The shipped workflows are registered built-ins:
|
|
287
|
+
|
|
288
|
+
- `autodevise` returns `ready` or `blocked` with plan lineage.
|
|
289
|
+
- `autoimplement` supports redesign, implementation fixes, exact reviewer and CI commands, P0 through P2 history, bounded CI watches, PR comments, merge, and final reporting.
|
|
290
|
+
- `monitor` remains observation-only by default and enables composed repair only through an explicit repair policy.
|
|
291
|
+
|
|
292
|
+
The package and Rust viewer version is `0.10.0`.
|
|
293
|
+
|
|
294
|
+
## Contract impact
|
|
295
|
+
|
|
296
|
+
- **Session state:** normal workflow messages and tool results only.
|
|
297
|
+
- **Other persistent data:** additive mount, source, digest, review, and CI evidence in existing run bundles.
|
|
298
|
+
- **Pi internals:** none.
|
|
299
|
+
- **Public Pi API:** existing documented extension APIs only.
|
|
300
|
+
- **Public Pi Workflows API:** generic input and exits, direct and dynamic `includeWorkflow()`, and `defineWorkflowRegistry()`.
|
package/docs/run-bundles.md
CHANGED
|
@@ -142,11 +142,13 @@ Identity and pointers, kept in sync with the state on every snapshot:
|
|
|
142
142
|
}
|
|
143
143
|
```
|
|
144
144
|
|
|
145
|
-
`workflowSource` identifies the definition used by the run. User workflow
|
|
145
|
+
`workflowSource` identifies the root definition used by the run. User workflow
|
|
146
146
|
files use an absolute path and SHA-256 hash. Package-provided workflows use a
|
|
147
|
-
stable identity such as `{ "kind": "builtin", "id": "monitor", "revision": "
|
|
147
|
+
stable identity such as `{ "kind": "builtin", "id": "monitor", "revision": "4" }`.
|
|
148
148
|
A built-in identity does not contain an installation path.
|
|
149
149
|
|
|
150
|
+
A composed run also records `workflowSources`, sorted by mount path, and `definitionDigest`. Each mounted source has `mountPath`, `workflowName`, and the same file or built-in source identity. The digest is SHA-256 over the resolved definition snapshot.
|
|
151
|
+
|
|
150
152
|
`paths.artifacts` is declared from bundle creation so a live session-event
|
|
151
153
|
patch can safely reference a newly written artifact before the next workflow
|
|
152
154
|
state projection. The directory itself is created only when needed.
|
|
@@ -160,8 +162,7 @@ A serializable snapshot of the graph taken at run start
|
|
|
160
162
|
(`pi-workflows.definition-snapshot.v1`). Functions such as prompts and
|
|
161
163
|
validators are not serialized. Each node keeps only its metadata (`nodeType`,
|
|
162
164
|
`timeoutMs`, `statusDetail`, `expectedOutput`, `summary`, `actionExecution`),
|
|
163
|
-
and edges are copied verbatim. The snapshot is what lets viewers draw all
|
|
164
|
-
nodes, including ones that have not run yet. It is immutable after run start.
|
|
165
|
+
and edges are copied verbatim. Included nodes also record `mountPath`, `localNodeId`, and internal entry or exit status. The top-level `composition.mounts` list records every mount, entry, named exit, and child step limit. The snapshot is what lets viewers draw all nodes, including ones that have not run yet. It is immutable after run start.
|
|
165
166
|
|
|
166
167
|
## Resume and repair
|
|
167
168
|
|
|
@@ -176,10 +177,12 @@ instead of failing. Resume is a named operation with strict rules:
|
|
|
176
177
|
`state.traceSeq` and the trace agree again before any new event.
|
|
177
178
|
3. Completed nodes replay from the projection. The in-flight node reruns with
|
|
178
179
|
a fresh attempt; a `run_resumed` trace event marks the boundary.
|
|
179
|
-
4. `state.workflowSource` pins the workflow source from run start. File
|
|
180
|
+
4. `state.workflowSource` pins the root workflow source from run start. File
|
|
180
181
|
sources require the same hash. Built-in sources require the same catalog
|
|
181
|
-
id and revision.
|
|
182
|
-
|
|
182
|
+
id and revision.
|
|
183
|
+
5. Composed runs also require the same sorted `workflowSources` and
|
|
184
|
+
`definitionDigest`. A changed or missing child refuses normal resume.
|
|
185
|
+
6. A forced resume records the identity mismatch in the `run_resumed` payload.
|
|
183
186
|
|
|
184
187
|
Continuation runs (answering a checkpoint) are new bundles, not resumed ones.
|
|
185
188
|
They link back through `state.parentRunId`, carry the parent's outputs,
|
|
@@ -204,6 +207,14 @@ The full run projection (`WorkflowRunState` in
|
|
|
204
207
|
"path": "/repo/.pi/workflows/autoimplement.workflow.ts",
|
|
205
208
|
"hash": "2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae"
|
|
206
209
|
},
|
|
210
|
+
"workflowSources": [
|
|
211
|
+
{
|
|
212
|
+
"mountPath": ["redesign"],
|
|
213
|
+
"workflowName": "autodevise",
|
|
214
|
+
"source": { "kind": "builtin", "id": "autodevise", "revision": "1" }
|
|
215
|
+
}
|
|
216
|
+
],
|
|
217
|
+
"definitionDigest": "sha256:0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
|
|
207
218
|
"startedAt": "…",
|
|
208
219
|
"updatedAt": "…",
|
|
209
220
|
"status": "running",
|
|
@@ -215,9 +226,10 @@ The full run projection (`WorkflowRunState` in
|
|
|
215
226
|
}
|
|
216
227
|
```
|
|
217
228
|
|
|
218
|
-
- `workflowSource` is the canonical source identity. Resuming a file requires
|
|
219
|
-
the same hash. Resuming a built-in requires the same catalog revision.
|
|
220
|
-
|
|
229
|
+
- `workflowSource` is the canonical root source identity. Resuming a file requires
|
|
230
|
+
the same hash. Resuming a built-in requires the same catalog revision.
|
|
231
|
+
- `workflowSources` and `definitionDigest` attest the complete composed graph.
|
|
232
|
+
A mismatch refuses resume instead of loading another child definition.
|
|
221
233
|
- `status` is one of `running`, `waiting`, `completed`, `failed`, `timed_out`,
|
|
222
234
|
or `cancelled`. A controller host records an abandoned bundle as `failed`
|
|
223
235
|
with a final `run_interrupted` trace event. Before doing that, recovery checks
|
|
@@ -315,6 +327,8 @@ Event catalog and payload contracts:
|
|
|
315
327
|
| `agent_prompt_sent` | agent | `prompt` |
|
|
316
328
|
| `node_finished` | node | `outcome: "ok"`, `durationMs`, `output`, `conversation?`, `action?` |
|
|
317
329
|
| `node_failed` | node | `outcome`, `durationMs`, `error`, `conversation?`, `action?` |
|
|
330
|
+
| `include_entered` | run | `mountPath`, `workflowName`, `invocation` |
|
|
331
|
+
| `include_exited` | run | `mountPath`, `workflowName`, `invocation`, `exit`, `output` |
|
|
318
332
|
| `run_paused` | run | _(empty)_ |
|
|
319
333
|
| `run_resumed` | run | _(empty)_ |
|
|
320
334
|
| `run_completed` | run | `status`, `finalOutput` |
|
package/docs/workflows.md
CHANGED
|
@@ -53,15 +53,20 @@ export default defineWorkflow({
|
|
|
53
53
|
|
|
54
54
|
Top-level fields:
|
|
55
55
|
|
|
56
|
-
| Field | Type
|
|
57
|
-
| -------------------- |
|
|
58
|
-
| `name` | `string`
|
|
59
|
-
| `
|
|
60
|
-
| `
|
|
61
|
-
| `
|
|
62
|
-
| `
|
|
63
|
-
| `
|
|
64
|
-
| `
|
|
56
|
+
| Field | Type | Notes |
|
|
57
|
+
| -------------------- | ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
58
|
+
| `name` | `string` | Required. Used in run ids and the step contract. `answer`, `cancel`, `list`, `pause`, `resume`, and `status` are reserved for `/workflow` subcommands. |
|
|
59
|
+
| `source` | `string` | Optional `import.meta.url` for exact provenance when another TypeScript workflow imports this definition directly. |
|
|
60
|
+
| `contractId` | `string` | Optional stable input-and-exit contract identity. Dynamic overrides must match it. |
|
|
61
|
+
| `input` | `function` | Optional runtime input normalizer and validator. Its return type is the workflow input type. |
|
|
62
|
+
| `title` | `string` or function | Optional run title, resolved once at start from `{ input, workflowName }`. Async resolution is bounded (30s) and cancellable. |
|
|
63
|
+
| `presentationPrompt` | `string` or function | Optional instructions for a normal assistant response after the run. A function receives `{ state, finalOutput, signal }` and may return a prompt or `undefined`. See [Result presentation](#result-presentation). |
|
|
64
|
+
| `startAt` | `string` | Required. Id of the first node. |
|
|
65
|
+
| `nodes` | `Record<string, node>` | Required, non-empty. Node ids must match `[A-Za-z_][A-Za-z0-9_-]*`. |
|
|
66
|
+
| `includes` | `Record<string, include>` | Optional imported or dynamically resolved child workflows. |
|
|
67
|
+
| `exits` | `Record<string, exit>` | Optional named successful terminal nodes used when another workflow includes this workflow. |
|
|
68
|
+
| `edges` | `WorkflowEdge[]` | Required. See routing below. |
|
|
69
|
+
| `maxSteps` | `number` | Optional loop bound, default 100. The run fails when exceeded. |
|
|
65
70
|
|
|
66
71
|
`defineWorkflow` validates the shape eagerly (node ids, edge shapes, function
|
|
67
72
|
fields) and validates the graph (unknown targets, duplicate outgoing edges,
|
|
@@ -314,6 +319,43 @@ A missing case for the resolved value fails the run with a routing error. A
|
|
|
314
319
|
node with no outgoing edge (or no matching failure route) ends the run:
|
|
315
320
|
`completed` on success, `failed`/`timed_out`/`cancelled` otherwise.
|
|
316
321
|
|
|
322
|
+
## Included workflows
|
|
323
|
+
|
|
324
|
+
Use `includeWorkflow()` to mount a standalone workflow under a parent name:
|
|
325
|
+
|
|
326
|
+
```typescript
|
|
327
|
+
import repair from "./repair.workflow.js";
|
|
328
|
+
|
|
329
|
+
includes: {
|
|
330
|
+
repair: includeWorkflow(repair, {
|
|
331
|
+
input: ({ outputs }) => ({ issue: outputs.check }),
|
|
332
|
+
}),
|
|
333
|
+
},
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
The child declares named exits:
|
|
337
|
+
|
|
338
|
+
```typescript
|
|
339
|
+
exits: {
|
|
340
|
+
completed: { from: "finalize", validate: parseCompleted },
|
|
341
|
+
blocked: { from: "blocked", validate: parseBlocked },
|
|
342
|
+
},
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
Enter through the mount and leave through `<mount>.<exit>`:
|
|
346
|
+
|
|
347
|
+
```typescript
|
|
348
|
+
{ from: "check", to: "repair" }
|
|
349
|
+
{ from: "repair.completed", to: "check" }
|
|
350
|
+
{ from: "repair.blocked", to: "finish" }
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
Direct imports check child input and exit names in TypeScript. Use `includedResult(child, outputs.mount)` to recover the child's discriminated exit output without a cast. Dynamic discovered names, built-in references, and file paths are also supported. Every reference resolves before the run starts.
|
|
354
|
+
|
|
355
|
+
Child callbacks receive local input, outputs, results, and steps from their current invocation. Persisted node identities include the mount path. Re-entry starts with empty child-local state. Root and child step limits both apply. Internal entry and exit transitions do not consume those limits.
|
|
356
|
+
|
|
357
|
+
The run records every mounted source and a digest of the resolved graph. Resume refuses a changed child source. Source cycles are rejected before the run starts. See [Workflow composition](WORKFLOW_COMPOSITION.md) for typing, persistence, nesting, and viewer rules.
|
|
358
|
+
|
|
317
359
|
## Model workflow control
|
|
318
360
|
|
|
319
361
|
The model sees one `workflow` tool. Its `action` field supports:
|
|
@@ -333,6 +375,12 @@ The normal extension offers all actions. The headless RPC bridge offers only
|
|
|
333
375
|
`update` and `submit`, so a workflow child cannot recursively control other
|
|
334
376
|
runs.
|
|
335
377
|
|
|
378
|
+
### Built-in planning and implementation
|
|
379
|
+
|
|
380
|
+
The built-in `autodevise` workflow selects a practical in-scope solution and writes a detailed plan. The built-in `autoimplement` workflow implements a supplied plan and returns to its internal `autodevise` mount when new evidence invalidates that plan.
|
|
381
|
+
|
|
382
|
+
Autoimplement writes and runs the exact Pi Reviewer command. It records P0 through P2 by review round. P0 or P1 work requires another review. P2-only work can be addressed and verified without another reviewer run. CI tracking commands are also explicit. One CI watch lasts at most five minutes, after which the model runs more useful local tests before checking CI again.
|
|
383
|
+
|
|
336
384
|
### Built-in monitor
|
|
337
385
|
|
|
338
386
|
The built-in `monitor` workflow turns a plain request for repeated checks into
|
|
@@ -341,12 +389,17 @@ one looping workflow run. Its input is:
|
|
|
341
389
|
```json
|
|
342
390
|
{
|
|
343
391
|
"task": "Check pull request 123",
|
|
344
|
-
"stopWhen": "The pull request is merged or closed"
|
|
392
|
+
"stopWhen": "The pull request is merged or closed",
|
|
393
|
+
"repair": {
|
|
394
|
+
"authorized": true,
|
|
395
|
+
"scope": "the current repository"
|
|
396
|
+
}
|
|
345
397
|
}
|
|
346
398
|
```
|
|
347
399
|
|
|
348
|
-
The first check runs immediately. `
|
|
349
|
-
|
|
400
|
+
The first check runs immediately. Omit `repair` for observation-only monitoring. An authorized repair can route through outer `autodevise`, `autoimplement`, and internal redesign before the monitor checks the target again. A repeated issue with unchanged target evidence stops as blocked.
|
|
401
|
+
|
|
402
|
+
`everyMinutes` defaults to 30. Each accepted check must provide one concise report and choose `continue`, `repair` when authorized, or `stop`. The
|
|
350
403
|
runtime queues that report as a workflow notification with `triggerTurn:
|
|
351
404
|
false`, so it does not cause an assistant reply. A check can also provide
|
|
352
405
|
independent progress tracks. The regular Pi model running the check observes
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { autodeviseWorkflow as default } from "@osolmaz/pi-workflows/builtins";
|
|
@@ -1,92 +1 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
type AutoimplementInput = {
|
|
4
|
-
task?: string;
|
|
5
|
-
};
|
|
6
|
-
|
|
7
|
-
const reviewChoices = ["clean", "issues_found"] as const;
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Implement, verify, then loop a self-review until it comes back clean. The
|
|
11
|
-
* decision edge routes `issues_found` back to the fix step, and the engine's
|
|
12
|
-
* maxSteps guard bounds the loop.
|
|
13
|
-
*/
|
|
14
|
-
export default defineWorkflow({
|
|
15
|
-
name: "autoimplement",
|
|
16
|
-
title: ({ input }) => {
|
|
17
|
-
const task = (input as AutoimplementInput).task;
|
|
18
|
-
return task ? `autoimplement: ${task.slice(0, 60)}` : undefined;
|
|
19
|
-
},
|
|
20
|
-
presentationPrompt:
|
|
21
|
-
"Summarize what was implemented, what verification passed, and any remaining limitation. Be concise and direct.",
|
|
22
|
-
maxSteps: 20,
|
|
23
|
-
startAt: "implement",
|
|
24
|
-
nodes: {
|
|
25
|
-
implement: agent({
|
|
26
|
-
timeoutMs: 60 * 60_000,
|
|
27
|
-
statusDetail: "implementing",
|
|
28
|
-
prompt: ({ input }) => {
|
|
29
|
-
const task =
|
|
30
|
-
(input as AutoimplementInput).task ?? "the plan discussed so far in this conversation";
|
|
31
|
-
return [
|
|
32
|
-
`Implement ${task} end-to-end.`,
|
|
33
|
-
"Aim for the most elegant, long-term production-ready solution without gold-plating.",
|
|
34
|
-
].join("\n");
|
|
35
|
-
},
|
|
36
|
-
expectedOutput: `{ "summary": "what was implemented", "files": ["changed file", "changed file"] }`,
|
|
37
|
-
}),
|
|
38
|
-
verify: agent({
|
|
39
|
-
timeoutMs: 30 * 60_000,
|
|
40
|
-
statusDetail: "verifying",
|
|
41
|
-
prompt: () =>
|
|
42
|
-
[
|
|
43
|
-
"Verify the implementation.",
|
|
44
|
-
"Run the test suite plus any relevant builds, linters, or local smoke tests.",
|
|
45
|
-
"Do not run destructive commands.",
|
|
46
|
-
].join("\n"),
|
|
47
|
-
expectedOutput: `{ "passed": true | false, "details": "what was run and what happened" }`,
|
|
48
|
-
}),
|
|
49
|
-
review: decision({
|
|
50
|
-
choices: reviewChoices,
|
|
51
|
-
question: ({ outputs }) =>
|
|
52
|
-
[
|
|
53
|
-
"Critically review your implementation as a strict reviewer.",
|
|
54
|
-
"Look for correctness bugs, missed requirements, and failing checks.",
|
|
55
|
-
"Pick `issues_found` if anything must be fixed, otherwise `clean`.",
|
|
56
|
-
"",
|
|
57
|
-
`Verification: ${JSON.stringify(outputs.verify)}`,
|
|
58
|
-
].join("\n"),
|
|
59
|
-
}),
|
|
60
|
-
fix: agent({
|
|
61
|
-
timeoutMs: 30 * 60_000,
|
|
62
|
-
statusDetail: "fixing",
|
|
63
|
-
prompt: ({ outputs }) =>
|
|
64
|
-
[
|
|
65
|
-
"Fix the issues you found in review, then stop.",
|
|
66
|
-
"",
|
|
67
|
-
`Review: ${JSON.stringify(outputs.review)}`,
|
|
68
|
-
].join("\n"),
|
|
69
|
-
expectedOutput: `{ "fixed": "what was changed" }`,
|
|
70
|
-
}),
|
|
71
|
-
finalize: compute({
|
|
72
|
-
run: ({ outputs }) => ({
|
|
73
|
-
implementation: outputs.implement,
|
|
74
|
-
verification: outputs.verify,
|
|
75
|
-
review: outputs.review,
|
|
76
|
-
}),
|
|
77
|
-
}),
|
|
78
|
-
},
|
|
79
|
-
edges: [
|
|
80
|
-
{ from: "implement", to: "verify" },
|
|
81
|
-
{ from: "verify", to: "review" },
|
|
82
|
-
decisionEdge({
|
|
83
|
-
from: "review",
|
|
84
|
-
choices: reviewChoices,
|
|
85
|
-
cases: {
|
|
86
|
-
clean: "finalize",
|
|
87
|
-
issues_found: "fix",
|
|
88
|
-
},
|
|
89
|
-
}),
|
|
90
|
-
{ from: "fix", to: "verify" },
|
|
91
|
-
],
|
|
92
|
-
});
|
|
1
|
+
export { autoimplementWorkflow as default } from "@osolmaz/pi-workflows/builtins";
|
package/herdr-plugin.toml
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@osolmaz/pi-workflows",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.0",
|
|
4
4
|
"description": "Workflow and controller runtime with a live terminal viewer for the pi coding agent",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pi-package"
|
|
@@ -41,6 +41,10 @@
|
|
|
41
41
|
"./controllers": {
|
|
42
42
|
"types": "./dist/controllers/index.d.ts",
|
|
43
43
|
"default": "./dist/controllers/index.js"
|
|
44
|
+
},
|
|
45
|
+
"./builtins": {
|
|
46
|
+
"types": "./dist/builtins/index.d.ts",
|
|
47
|
+
"default": "./dist/builtins/index.js"
|
|
44
48
|
}
|
|
45
49
|
},
|
|
46
50
|
"publishConfig": {
|
package/skills/monitor/SKILL.md
CHANGED
|
@@ -29,11 +29,14 @@ Derive the workflow input from the full conversation:
|
|
|
29
29
|
- `task`: State the complete objective, the exact current target and stable identifiers, authoritative status sources, durable progress and final-output surfaces, routine actions authorized by the monitor request, other recorded approvals, immutable boundaries, cost and credential rules, and required validation or downstream operations.
|
|
30
30
|
- `everyMinutes`: Use the user's interval when present. Use `30` when the user gives no interval. The built-in workflow accepts intervals from 1 minute through 24 hours.
|
|
31
31
|
- `stopWhen`: Infer verified completion from the full conversation. Describe completion of the complete objective, not only the end of one physical process. Also name material blockers that require human intervention.
|
|
32
|
+
- `repair`: Include this object only when the request or an existing approval authorizes mutation. Set `authorized: true` and record the repository, scope, base branch, merge policy, and constraints that apply. Omit it for observation-only work.
|
|
32
33
|
|
|
33
34
|
When the conversation gives no clear finish criterion, set `stopWhen` to `Stop only when the user explicitly asks to stop.` Do not use that fallback when a broader implementation, repair, publication, or deployment objective is clear from context.
|
|
34
35
|
|
|
35
36
|
Do not invent a finite check count. Omit `maxChecks` unless the user explicitly requests one. The workflow host can apply its own safety upper bound. Disclose that bound if it appears.
|
|
36
37
|
|
|
38
|
+
When repair is authorized, route a concrete code or design defect through the monitor's composed repair path. Supply the problem, observed evidence, and a stable fingerprint of the issue plus target state. The workflow runs outer `autodevise`, `autoimplement`, and internal redesign when needed, then checks the target again. Do not copy their prompts into the monitor task.
|
|
39
|
+
|
|
37
40
|
## Keep routine work moving
|
|
38
41
|
|
|
39
42
|
While the workflow is active, do routine, bounded work required by the exact objective without asking for another confirmation. This includes:
|
|
@@ -140,7 +143,9 @@ Stop only for a material blocker, such as:
|
|
|
140
143
|
- a cost, time, or resource ceiling that cannot safely contain the remaining work;
|
|
141
144
|
- evidence that the requested result cannot be made truthful or valid under the current contract.
|
|
142
145
|
|
|
143
|
-
Never keep paid workers retrying a deterministic shared failure.
|
|
146
|
+
Never keep paid workers retrying a deterministic shared failure. When repair is authorized and the defect is inside scope, stop affected work, preserve the evidence, and use the composed repair path. Stop for a decision when repair is outside scope or would change a protected contract.
|
|
147
|
+
|
|
148
|
+
If the same issue and target-state fingerprint return after a completed repair, report the no-progress result and stop. Do not start the same repair again.
|
|
144
149
|
|
|
145
150
|
### Status unavailable
|
|
146
151
|
|