@signalridge/pi-workflows 1.2.0 → 1.3.1

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 (49) hide show
  1. package/CHANGELOG.md +45 -0
  2. package/README.md +142 -17
  3. package/package.json +14 -8
  4. package/skills/workflow-authoring/SKILL.md +30 -0
  5. package/skills/workflow-authoring/examples/adversarial-verification.js +63 -0
  6. package/skills/workflow-authoring/examples/bounded-semantic-retry.js +56 -0
  7. package/skills/workflow-authoring/examples/classify-and-act.js +67 -0
  8. package/skills/workflow-authoring/examples/defensive-json-parsing.js +63 -0
  9. package/skills/workflow-authoring/examples/fan-out-and-synthesize.js +47 -0
  10. package/skills/workflow-authoring/examples/generate-and-filter.js +68 -0
  11. package/skills/workflow-authoring/examples/loop-until-done.js +68 -0
  12. package/skills/workflow-authoring/examples/phased-budgets.js +75 -0
  13. package/skills/workflow-authoring/examples/saved-nested-workflows.js +44 -0
  14. package/skills/workflow-authoring/examples/structured-output.js +37 -0
  15. package/skills/workflow-authoring/examples/tournament.js +81 -0
  16. package/skills/workflow-authoring/examples/validated-gate.js +63 -0
  17. package/skills/workflow-authoring/references/capabilities.md +44 -0
  18. package/skills/workflow-authoring/references/capability-details.md +294 -0
  19. package/skills/workflow-authoring/references/common-helpers.md +4 -0
  20. package/skills/workflow-authoring/references/debugging.md +29 -0
  21. package/skills/workflow-authoring/references/focused-recipes.md +12 -0
  22. package/skills/workflow-authoring/references/helpers.md +9 -0
  23. package/skills/workflow-authoring/references/lifecycle.md +37 -0
  24. package/skills/workflow-authoring/references/pattern-selection.md +15 -0
  25. package/skills/workflow-authoring/references/quality-helpers.md +8 -0
  26. package/skills/workflow-authoring/references/registry-ownership.md +17 -0
  27. package/skills/workflow-authoring/references/retry-helper.md +5 -0
  28. package/skills/workflow-authoring/references/review.md +43 -0
  29. package/skills/workflow-authoring/references/runtime.md +29 -0
  30. package/skills/workflow-authoring/references/specialized-helpers.md +19 -0
  31. package/skills/workflow-authoring/references/versions.md +13 -0
  32. package/skills/workflow-patterns/SKILL.md +51 -0
  33. package/src/arming.ts +69 -0
  34. package/src/builtins.ts +331 -0
  35. package/src/capabilities.ts +318 -0
  36. package/src/code-review-scope.ts +144 -0
  37. package/src/engine.ts +1158 -2561
  38. package/src/errors.ts +136 -0
  39. package/src/index.ts +750 -103
  40. package/src/journal.ts +594 -1001
  41. package/src/rpc-client.ts +95 -24
  42. package/src/runtime.ts +1708 -0
  43. package/src/saved-workflows.ts +121 -0
  44. package/src/shared-store.ts +132 -0
  45. package/src/ui.ts +77 -40
  46. package/src/workflow-settings.ts +100 -0
  47. package/src/schema.ts +0 -215
  48. package/src/state-machine.ts +0 -79
  49. package/src/synthesis.ts +0 -140
@@ -0,0 +1,294 @@
1
+ <!-- GENERATED from WORKFLOW_CAPABILITY_CONTRACT; do not edit by hand. -->
2
+ # Exhaustive workflow capability facts
3
+
4
+ Contract format: `1.0.0`<br>
5
+ Contract content / skill / extension: `1.3.1`
6
+
7
+ Every exact fact below is projected from the installed extension's capability contract. Explanatory judgment belongs in the hand-written references next to this file.
8
+
9
+ <!-- BEGIN GENERATED WORKFLOW CAPABILITY FACTS -->
10
+ <a id="runtime-global-agent"></a>
11
+ ## agent
12
+
13
+ - Classification: `runtime-global`
14
+ - Support: `supported`
15
+ - Signature: `agent(prompt, options?) => Promise<string \| structured value \| null>`
16
+ - `label`: string (optional); default: derived from phase and call count
17
+ - `phase`: string (optional); default: current phase
18
+ - `schema`: plain JSON Schema (optional)
19
+ - `model`: string (optional); default: pi-subagents model resolver
20
+ - `tier`: small\|medium\|large (optional); default: pi-subagents workflow tier
21
+ - `isolation`: "worktree" (optional); default: pi-subagents worktree policy
22
+ - `thread`: string (optional); default: fresh session per call
23
+ - `toolset`: string (optional); default: configured host/agent toolset hint
24
+ - `excludeTools`: string[] (optional)
25
+ - `agentType`: string (optional); default: general-purpose
26
+ - `timeoutMs`: number \| null (optional); default: 300000 (5min); null disables
27
+ - `retries`: number (optional); default: run retry count
28
+ - Constraint: recoverable failures return null after retries; nonrecoverable failures throw
29
+ - Constraint: schema noncompliance after bounded repair attempts is nonrecoverable
30
+ - Constraint: resume replays only the longest unchanged prefix; the first miss and every later call execute live
31
+ - Constraint: spawnKeys rotate by generation on resume so pi-subagents never raises a fingerprint conflict
32
+ - Constraint: explicit model is resolved and scope-checked by pi-subagents; unavailable selectors fail closed
33
+ - Constraint: same-thread calls must be sequential and preserve a stable workflow thread name
34
+ - Constraint: threads cannot be combined with worktree isolation
35
+
36
+ <a id="runtime-global-parallel"></a>
37
+ ## parallel
38
+
39
+ - Classification: `runtime-global`
40
+ - Support: `supported`
41
+ - Signature: `parallel(thunks) => Promise<Array<unknown \| null>>`
42
+ - Constraint: requires functions rather than promises
43
+ - Constraint: result order matches input order
44
+ - Constraint: recoverable thunk failures become null; nonrecoverable failures throw
45
+ - Constraint: a breached agent cap cancels only its own batch
46
+
47
+ <a id="runtime-global-pipeline"></a>
48
+ ## pipeline
49
+
50
+ - Classification: `runtime-global`
51
+ - Support: `supported`
52
+ - Signature: `pipeline(items, ...stages) => Promise<Array<unknown \| null>>`
53
+ - Constraint: items run concurrently while stages per item run sequentially
54
+ - Constraint: each stage receives previousValue, originalItem, and zero-based index
55
+ - Constraint: a null stage result is passed to the next stage
56
+
57
+ <a id="runtime-global-workflow"></a>
58
+ ## workflow
59
+
60
+ - Classification: `runtime-global`
61
+ - Support: `supported`
62
+ - Signature: `workflow(savedName, childArgs?) => Promise<unknown>`
63
+ - Constraint: one nested level
64
+ - Constraint: shares limiter, counters, token accounting, and store
65
+ - Constraint: nested runs get unique runIds via nestedCallSeq
66
+
67
+ <a id="runtime-global-verify"></a>
68
+ ## verify
69
+
70
+ - Classification: `runtime-global`
71
+ - Support: `supported`
72
+ - Signature: `verify(item, options?) => Promise<{ real, realCount, total, votes }>`
73
+ - `reviewers`: number (optional); default: 2
74
+ - `threshold`: number (optional); default: 0.5
75
+ - `lens`: string \| string[] (optional)
76
+ - Constraint: threshold comparison is inclusive; real is false when no reviewer succeeds
77
+
78
+ <a id="runtime-global-judgepanel"></a>
79
+ ## judgePanel
80
+
81
+ - Classification: `runtime-global`
82
+ - Support: `supported`
83
+ - Signature: `judgePanel(attempts, options?) => Promise<{ index, attempt, score, judgments } \| undefined>`
84
+ - `judges`: number (optional); default: 3
85
+ - `rubric`: string (optional); default: overall quality and correctness
86
+ - Constraint: highest mean score wins with stable input index as the tie-break; empty input returns undefined
87
+
88
+ <a id="runtime-global-loopuntildry"></a>
89
+ ## loopUntilDry
90
+
91
+ - Classification: `runtime-global`
92
+ - Support: `supported`
93
+ - Signature: `loopUntilDry({ round, key?, consecutiveEmpty?, maxRounds? }) => Promise<unknown[]>`
94
+ - `round`: (roundIndex) => unknown[]; default: required
95
+ - `key`: (item) => string (optional); default: JSON.stringify
96
+ - `consecutiveEmpty`: number (optional); default: 2
97
+ - `maxRounds`: number (optional); default: 50
98
+ - Constraint: budget/agent-limit exhaustion returns the partial array instead of throwing
99
+
100
+ <a id="runtime-global-completenesscheck"></a>
101
+ ## completenessCheck
102
+
103
+ - Classification: `runtime-global`
104
+ - Support: `supported`
105
+ - Signature: `completenessCheck(taskArgs, results) => Promise<{ complete, missing? } \| null>`
106
+ - Constraint: only the first 4,000 characters of serialized result evidence are sent to the critic
107
+
108
+ <a id="runtime-global-retry"></a>
109
+ ## retry
110
+
111
+ - Classification: `runtime-global`
112
+ - Support: `supported`
113
+ - Signature: `retry(thunk, options?) => Promise<unknown>`
114
+ - `attempts`: number (optional); default: 3
115
+ - `until`: (result) => boolean (optional); default: accept first result
116
+ - Constraint: until must be synchronous
117
+
118
+ <a id="runtime-global-gate"></a>
119
+ ## gate
120
+
121
+ - Classification: `runtime-global`
122
+ - Support: `supported`
123
+ - Signature: `gate(thunk, validator, options?) => Promise<{ ok, value, attempts }>`
124
+ - `attempts`: number (optional); default: 3
125
+ - Constraint: a value is accepted only when the validator returns an object with a truthy ok
126
+
127
+ <a id="runtime-global-checkpoint"></a>
128
+ ## checkpoint
129
+
130
+ - Classification: `runtime-global`
131
+ - Support: `supported`
132
+ - Signature: `checkpoint(prompt, options?) => Promise<unknown>`
133
+ - `default`: unknown (optional); default: true when no UI and omitted
134
+ - `headless`: "default" \| "abort" (optional); default: "default"
135
+ - `kind`: "confirm" \| "input" \| "select" (optional); default: "confirm"
136
+ - `choices`: string[] (optional)
137
+ - `timeoutMs`: number (optional)
138
+ - Constraint: consumes one agent slot and no tokens
139
+ - Constraint: journaled answers replay only within an unchanged resume prefix
140
+
141
+ <a id="runtime-global-log"></a>
142
+ ## log
143
+
144
+ - Classification: `runtime-global`
145
+ - Support: `supported`
146
+ - Signature: `log(message) => void`
147
+
148
+ <a id="runtime-global-console"></a>
149
+ ## console
150
+
151
+ - Classification: `runtime-global`
152
+ - Support: `supported`
153
+ - Signature: `console: { log, info, warn, error }`
154
+ - Constraint: compatibility only — new workflows should use log()
155
+
156
+ <a id="runtime-global-phase"></a>
157
+ ## phase
158
+
159
+ - Classification: `runtime-global`
160
+ - Support: `supported`
161
+ - Signature: `phase(title, options?) => void`
162
+ - `budget`: number (optional); default: positive soft pre-call token gate
163
+ - Constraint: phase budgets are soft pre-call gates
164
+
165
+ <a id="runtime-global-args"></a>
166
+ ## args
167
+
168
+ - Classification: `runtime-global`
169
+ - Support: `supported`
170
+ - Signature: `args: unknown`
171
+
172
+ <a id="runtime-global-cwd"></a>
173
+ ## cwd
174
+
175
+ - Classification: `runtime-global`
176
+ - Support: `supported`
177
+ - Signature: `cwd: string`
178
+
179
+ <a id="runtime-global-process"></a>
180
+ ## process
181
+
182
+ - Classification: `runtime-global`
183
+ - Support: `supported`
184
+ - Signature: `process: { cwd(): string }`
185
+ - Constraint: restricted: only cwd() is exposed
186
+
187
+ <a id="runtime-global-budget"></a>
188
+ ## budget
189
+
190
+ - Classification: `runtime-global`
191
+ - Support: `supported`
192
+ - Signature: `budget: { total, spent(), remaining() }`
193
+ - Constraint: frozen view over shared soft token accounting; nested workflows share the same accounting
194
+
195
+ <a id="workflow-tool-input-script"></a>
196
+ ## script
197
+
198
+ - Classification: `workflow-tool-input`
199
+ - Support: `supported`
200
+ - Signature: `script?: string`
201
+ - Constraint: required raw JavaScript workflow source unless `name` is given
202
+
203
+ <a id="workflow-tool-input-name"></a>
204
+ ## name
205
+
206
+ - Classification: `workflow-tool-input`
207
+ - Support: `supported`
208
+ - Signature: `name?: string`
209
+ - Constraint: resolves a saved workflow first, then one of the 5 built-in patterns
210
+ - Constraint: mutually exclusive with script and resumeFromRunId
211
+
212
+ <a id="workflow-tool-input-args"></a>
213
+ ## args
214
+
215
+ - Classification: `workflow-tool-input`
216
+ - Support: `supported`
217
+ - Signature: `args?: unknown`
218
+
219
+ <a id="workflow-tool-input-background"></a>
220
+ ## background
221
+
222
+ - Classification: `workflow-tool-input`
223
+ - Support: `supported`
224
+ - Signature: `background?: boolean = true`
225
+ - Constraint: background workflows are headless; use background false when checkpoint must show foreground confirmation
226
+
227
+ <a id="workflow-tool-input-maxagents"></a>
228
+ ## maxAgents
229
+
230
+ - Classification: `workflow-tool-input`
231
+ - Support: `supported`
232
+ - Signature: `maxAgents?: number = 1000`
233
+
234
+ <a id="workflow-tool-input-concurrency"></a>
235
+ ## concurrency
236
+
237
+ - Classification: `workflow-tool-input`
238
+ - Support: `supported`
239
+ - Signature: `concurrency?: number`
240
+ - Constraint: runtime clamps to 1..16
241
+
242
+ <a id="workflow-tool-input-agentretries"></a>
243
+ ## agentRetries
244
+
245
+ - Classification: `workflow-tool-input`
246
+ - Support: `supported`
247
+ - Signature: `agentRetries?: number = 0`
248
+ - Constraint: floored and clamped to 0..3
249
+
250
+ <a id="workflow-tool-input-agenttimeoutms"></a>
251
+ ## agentTimeoutMs
252
+
253
+ - Classification: `workflow-tool-input`
254
+ - Support: `supported`
255
+ - Signature: `agentTimeoutMs?: number = 300000`
256
+ - Constraint: null disables the hard timeout; default 300000ms (5min)
257
+
258
+ <a id="workflow-tool-input-tokenbudget"></a>
259
+ ## tokenBudget
260
+
261
+ - Classification: `workflow-tool-input`
262
+ - Support: `supported`
263
+ - Signature: `tokenBudget?: number`
264
+ - Constraint: soft pre-call gate; in-flight work can overshoot
265
+
266
+ <a id="workflow-tool-input-resumefromrunid"></a>
267
+ ## resumeFromRunId
268
+
269
+ - Classification: `workflow-tool-input`
270
+ - Support: `supported`
271
+ - Signature: `resumeFromRunId?: string`
272
+ - Constraint: resumes a prior incomplete run with an edited script
273
+ - Constraint: unchanged positional calls replay from cache until the first changed call
274
+ - Constraint: always runs in the background
275
+
276
+ <a id="script-contract-export-const-meta"></a>
277
+ ## export const meta
278
+
279
+ - Classification: `script-contract`
280
+ - Support: `supported`
281
+ - Signature: `export const meta = { name, description, phases? }`
282
+ - Constraint: must be the first statement
283
+ - Constraint: name and description must be nonblank literals
284
+ - Constraint: spread, computed keys, methods, template interpolation, and __proto__/constructor/prototype are rejected
285
+
286
+ <a id="script-contract-determinism"></a>
287
+ ## determinism
288
+
289
+ - Classification: `script-contract`
290
+ - Support: `supported`
291
+ - Signature: `Date.now(), Math.random(), no-argument new Date() unavailable`
292
+ - Constraint: pass timestamps and randomness through args
293
+
294
+ <!-- END GENERATED WORKFLOW CAPABILITY FACTS -->
@@ -0,0 +1,4 @@
1
+ # Common helper index
2
+
3
+ - Read [quality helpers](quality-helpers.md) for `verify()` or `judgePanel()`.
4
+ - Read the [retry helper](retry-helper.md) for `retry()`.
@@ -0,0 +1,29 @@
1
+ # Workflow debugging map
2
+
3
+ Start from the symptom, then reproduce through the real workflow runtime with deterministic fake agents.
4
+
5
+ | Symptom | Likely authoring cause | Check |
6
+ | --- | --- | --- |
7
+ | Parser says metadata is missing | `export const meta` is not the first statement or is nonliteral | [runtime](runtime.md#script-envelope) |
8
+ | `parallel()` rejects input | Promises were passed instead of thunks | [runtime](runtime.md#topology) |
9
+ | Synthesis starts early | Fan-out was not awaited as one complete result set | [pattern selection](pattern-selection.md#fan-out-and-synthesize) |
10
+ | Coverage silently disappears | `null` results were filtered before IDs were ledgered | [lifecycle](lifecycle.md#retry-and-recoverable-failure) |
11
+ | Wrong model is used | A higher-priority selector overrides the expected route | [registry ownership](registry-ownership.md#priority) |
12
+ | Unknown `agentType` log | A live registry name was guessed or is unavailable | [registry ownership](registry-ownership.md#agent-types) |
13
+ | Budget exceeds the number shown | The budget is a soft pre-call gate and work was in flight | [lifecycle](lifecycle.md#bounds-and-budget) |
14
+ | Later calls rerun on resume | An earlier call missed or changed, ending the replayable prefix | [lifecycle](lifecycle.md#resume) |
15
+ | Resume reruns a call that already "succeeded" once before | Its only prior attempt(s) ended in a recoverable failure (e.g. `AGENT_EMPTY_OUTPUT`) and were never journaled | [lifecycle](lifecycle.md#resume) |
16
+ | A field read off `agent()`'s result is `undefined` | No `schema` was set; the prompt asking for JSON does not change that `agent()` returns raw text | [lifecycle](lifecycle.md#serialization) |
17
+ | Nested workflow fails | Nesting exceeded one level or shared limits were exhausted | [lifecycle](lifecycle.md#nesting-and-shared-state) |
18
+ | Checkpoint does not show a form | Input/select/timeout behavior is declared-only | [lifecycle](lifecycle.md#checkpoints) |
19
+ | Returned result cannot cross boundary | It contains a function, promise, cycle, `BigInt`, or runtime object | [lifecycle](lifecycle.md#serialization) |
20
+ | `Date.now()`/randomness is rejected | Resume requires deterministic call structure | [lifecycle](lifecycle.md#resume) |
21
+
22
+ ## Debugging procedure
23
+
24
+ 1. Reduce to the smallest script that preserves metadata, labels, work IDs, and the failing topology.
25
+ 2. Replace provider calls with deterministic, schema-aware fake agents.
26
+ 3. Record call order, labels, phases, prompts, results, and `null` entries.
27
+ 4. When the failure turns on a disputed signature or default, compare it with the compact [capability index](capabilities.md); follow its exhaustive-facts pointer only when needed.
28
+ 5. Separate runtime defects from unsupported authoring assumptions and compatibility-only behavior.
29
+ 6. Fix only the demonstrated authoring issue; do not rely on known out-of-scope gaps in nested persistence, resume accounting, checkpoint forms/timeouts, metadata validation, or budget overshoot.
@@ -0,0 +1,12 @@
1
+ # Focused recipes
2
+
3
+ Read only the recipe matching the concern. Change task prompts, schemas, bounds, and context-supplied inputs; preserve the listed contract.
4
+
5
+ | Concern | Preserve when adapting | Tested recipe |
6
+ | --- | --- | --- |
7
+ | Phased budgets | Phase and run budgets are soft pre-call gates; active work can overshoot. Report shared spend and bound calls independently. | [Phased budgets](../examples/phased-budgets.js) |
8
+ | Saved workflows | Use a context-supplied name, await jobs sequentially, nest one level, and treat shared limits, counters, tokens, limiter, and store as parent capacity. | [Saved nested workflows](../examples/saved-nested-workflows.js) |
9
+ | Semantic retry | Separate it from recoverable runtime retries. Use a new unique label per bounded attempt and return the attempt ledger plus exhausted outcome. | [Bounded semantic retry](../examples/bounded-semantic-retry.js) |
10
+ | Validator feedback | Follow the exact `gate()` callbacks in [specialized helpers](specialized-helpers.md). Return the gate outcome and attempt ledger so feedback and exhaustion remain visible. | [Validated gate](../examples/validated-gate.js) |
11
+ | Structured fields | Pass a small plain JSON Schema before reading fields. Ledger recoverable `null`; treat exhausted schema repair as a nonrecoverable failure. | [Structured output](../examples/structured-output.js) |
12
+ | Defensive text parsing | Without `schema`, `agent()` always resolves to raw text, even when the prompt asks for JSON. Parse and validate shape before reading fields; ledger unparseable text separately from recoverable `null`. Prefer `schema` when the shape matters. | [Defensive JSON parsing](../examples/defensive-json-parsing.js) |
@@ -0,0 +1,9 @@
1
+ # Helper reference index
2
+
3
+ Use a helper only when it expresses the task's policy. Preserve candidate or work identity outside helper results that may omit failed agents.
4
+
5
+ - Read [quality helpers](quality-helpers.md) for `verify()` or `judgePanel()`.
6
+ - Read the [retry helper](retry-helper.md) for `retry()`.
7
+ - Read [specialized helpers](specialized-helpers.md) for `completenessCheck()`, `loopUntilDry()`, `gate()`, or `checkpoint()`.
8
+
9
+ Runtime agent retries repeat recoverable execution failures; helper attempts are new semantic calls. Bound both layers and ledger exhaustion.
@@ -0,0 +1,37 @@
1
+ # Lifecycle, limits, and resume
2
+
3
+ ## Bounds and budget
4
+
5
+ Set finite bounds that match the work for `maxAgents`, `concurrency`, and `agentRetries`; bound loops and semantic retries inside the script. Treat invocation-level `agentTimeoutMs` and `tokenBudget` as opt-in user constraints, not precautionary defaults. Omit `tokenBudget` unless the user supplies a cap or explicitly asks you to choose one. If asked to choose, allow for every planned agent call, retry, synthesis, and verification pass, with headroom. A tight gate can terminate coverage; it does not reduce work already in flight. An omitted `agentTimeoutMs` uses the configured `defaultAgentTimeoutMs` and is otherwise unbounded. An omitted `tokenBudget` uses the configured `defaultTokenBudget` and is otherwise unlimited.
6
+
7
+ Enter a phase budget with `phase("Name", { budget: N })`; phase metadata does not carry budgets. `N` is a token allowance, not a call or round count: size it for the intended agent work instead of copying a small iteration limit. Token and phase budgets are soft pre-call gates. Spend lands after agents finish, so concurrent work can overshoot. A phase budget gates later calls in that phase; it neither reserves tokens nor cancels active calls. `budget.spent()` and `budget.remaining()` include nested work.
8
+
9
+ ## Checkpoints
10
+
11
+ A checkpoint consumes an agent slot but no tokens. A workflow invocation is backgrounded by default, and background workflows are headless: they cannot display checkpoint confirmation. Use `background: false` when a checkpoint must reach the foreground host confirmation interface. Without a UI, a checkpoint returns the declared default (or `true` when omitted) unless `headless: "abort"` is selected. Confirm is implemented. Input, select, and timeout fields are declared for compatibility/future behavior but are not authoring promises.
12
+
13
+ Checkpoint answers are journaled and can replay during an unchanged resume prefix. Do not describe checkpoints as guaranteed arbitrary forms or as remote steering.
14
+
15
+ ## Retry and recoverable failure
16
+
17
+ Recoverable execution failures retry according to the per-agent option or invocation-time tool input, then return `null`. Nonrecoverable failures throw without becoming `null`. The logical `retry()` combinator is separate: it performs new agent calls and returns its last result when exhausted unless the script records and handles that outcome.
18
+
19
+ Always retain `{ id, status, result }` or an equivalent ledger for each intended work unit. Filtering `null` before recording identity turns an execution failure into invisible missing coverage.
20
+
21
+ `AGENT_EMPTY_OUTPUT` (whitespace-only text from a schema-less call) is recoverable and retries like any other transient failure. Some models occasionally produce it on an otherwise-working first attempt; a fleet built on such a model should set `agentRetries: 1-2` rather than treat one occurrence as a failed run. A `schema` call never trips this check — schema noncompliance is its own, nonrecoverable failure (see [serialization](#serialization)).
22
+
23
+ ## Resume
24
+
25
+ Resume replays only the longest unchanged prefix of journaled calls. Once one call is new, changed, or unusable, that call and all later calls execute live. Stable lexical call ordering, prompts, labels, routing options, and inputs therefore matter. Retry chains can cascade after an upstream miss. Nested workflows do not reuse the parent's resume journal.
26
+
27
+ Only a call that finishes with a real result is journaled. A call whose every attempt was recoverable (including one that only ever produced `AGENT_EMPTY_OUTPUT`) contributes no journal entry, so resuming that run reruns exactly that call and everything lexically after it live; the earlier, already-succeeded prefix still replays from cache.
28
+
29
+ The runtime blocks common accidental nondeterminism, but this is not a security boundary. Pass timestamps, randomness, and external decisions through `args`.
30
+
31
+ ## Nesting and shared state
32
+
33
+ `workflow(savedName, childArgs)` runs sequentially inline, allows one nested level, and shares limiter, counters, token accounting, and shared store with the parent. It is not independent capacity. Use only a saved-workflow name provided by context; do not guess registry entries or pass raw scripts as a new authoring pattern even where compatibility behavior accepts them.
34
+
35
+ ## Serialization
36
+
37
+ The workflow's explicit return value crosses the tool boundary. Keep it JSON-serializable and preserve coverage ledgers in the returned data. Structured agent schemas must be plain JSON Schema. Schema success guarantees the downstream field shape expected by JavaScript; without a schema, treat output as text or `null`. A prompt that asks the model to "return JSON" does not change this — without `schema`, parse and validate that text defensively before reading a field, and ledger an unparseable result instead of reading `undefined` off it (see [defensive text parsing](focused-recipes.md)).
@@ -0,0 +1,15 @@
1
+ # Pattern selection
2
+
3
+ Choose from data dependencies, then read only the matching example. JavaScript owns enumeration, identity, ordering, deduplication, bounds, stopping, brackets, and failure ledgers. Agents own semantic work.
4
+
5
+ <a id="fan-out-and-synthesize"></a>
6
+ | Dependency shape | Pattern | Preserve when adapting | Example |
7
+ | --- | --- | --- | --- |
8
+ | Heterogeneous items need different handling | Classify and act | Finish all classification before routed action; ledger classification and action failures by item ID | [Adapt](../examples/classify-and-act.js) |
9
+ | Independent work needs whole-set judgment | Fan out and synthesize | Pass thunks; await the full set; give synthesis every intended ID, including `null` | [Adapt](../examples/fan-out-and-synthesize.js) |
10
+ | Claims need skeptical checks | Adversarial verification | Use separate producer and skeptic calls; start skepticism after production; ledger both failures | [Adapt](../examples/adversarial-verification.js) |
11
+ | Exploration should diverge before one rubric | Generate and filter | Finish generation; deterministically deduplicate and bound candidates before filter calls | [Adapt](../examples/generate-and-filter.js) |
12
+ | Pairwise comparison beats absolute scoring | Tournament | Let JavaScript run the bounded bracket and byes; agents compare one pair; ledger match failures | [Adapt](../examples/tournament.js) |
13
+ | Work cardinality is unknown | Loop until done | Deduplicate by stable key; count only successful empty rounds as dry; cap rounds; retain failed rounds | [Adapt](../examples/loop-until-done.js) |
14
+
15
+ For every pattern, validate and bound input before fan-out, use stable IDs and unique labels, preserve missing coverage, and return plain JSON data. Combine patterns only when the task has both dependency shapes. Direct work needs no orchestration.
@@ -0,0 +1,8 @@
1
+ # Verify and judge
2
+
3
+ Keep work IDs outside helper results that may omit failed agents.
4
+
5
+ | Call | Contract |
6
+ | --- | --- |
7
+ | `verify(item, { reviewers: number, threshold: number, lens: string | string[] })` | Defaults: 2 reviewers, inclusive `0.5`, one lens or a cycled array. Returns `{ real, realCount, total, votes }`. Failed reviewers are omitted; successful votes are the denominator; zero survivors means `real: false`. |
8
+ | `judgePanel(attempts, { judges: number, rubric: string })` | Defaults: 3 judges and `"overall quality and correctness"`. Failed judgments are omitted. Returns the highest mean `{ index, attempt, score, judgments }`; input order wins ties; empty input returns `undefined`. |
@@ -0,0 +1,17 @@
1
+ # Dynamic registry ownership
2
+
3
+ Model routes and agent types are dynamic references. Their shape and owner are documented, but available names depend on active user/project configuration and are intentionally absent from static skill files.
4
+
5
+ ## Model routes
6
+
7
+ The `pi-subagents` workflow-tier settings own route names. Standard routes are `small`, `medium`, and `large`; use another route only when its name and purpose are supplied in context. A route is selected with `tier`. An exact user-requested model is selected with `model` and is resolved/scope-checked by pi-subagents.
8
+
9
+ ## Agent types
10
+
11
+ The agent registry owns agent-type names and their bound instructions, tools, model, and isolation policy. Use `agentType` only when context supplies both its name and purpose. Do not infer an agent type from a role-like label.
12
+
13
+ ## Priority
14
+
15
+ Routing priority is explicit `model` > named `tier` > phase/run model > pi-subagents agent configuration > parent session. Higher priority means selection, not "try this then fall back to the next selector." Avoid specifying competing selectors unless deliberately overriding a lower-priority default.
16
+
17
+ Unavailability is fail-closed for explicit `model` and named tiers: pi-subagents reports the selector and does not silently run a different model. An omitted selector may inherit the parent session according to the companion's current settings.
@@ -0,0 +1,5 @@
1
+ # Retry
2
+
3
+ `retry(thunk, { attempts, until })` takes exactly these 2 arguments; `until` belongs in the options object. It calls `thunk(attempt)` with a zero-based index and defaults to 3 attempts. `until` is synchronous—a Promise or omitted predicate accepts the first result. Exhaustion returns only the last result, so keep an attempt ledger.
4
+
5
+ Always `await retry()`. A thunk containing `await` must be `async`; await `agent()` before adding its resolved value to the ledger. Runtime retries repeat recoverable execution failures; helper attempts are new semantic calls. Bound both layers and ledger exhaustion.
@@ -0,0 +1,43 @@
1
+ # Workflow review checklist
2
+
3
+ Review author-visible behavior, not formatting preferences. When behavior depends on a quality or control combinator, consult only its exact [quality](quality-helpers.md) or [specialized](specialized-helpers.md) helper contract before correcting the script.
4
+
5
+ ## Envelope and contract
6
+
7
+ - Is literal `export const meta` the first statement, with a short unique name and useful description?
8
+ - Are only used phases declared, and does each named phase begin at the intended boundary?
9
+ - Does the script call at least one agent and explicitly return JSON-serializable data?
10
+ - Are imports and nondeterministic APIs absent?
11
+
12
+ ## Topology and identity
13
+
14
+ - Does topology match dependencies: thunks for independent parallel work, stages for per-item pipelines, barriers before whole-set synthesis?
15
+ - Is cardinality bounded before fan-out?
16
+ - Is every agent label short and unique?
17
+ - Are stable work-unit IDs retained beside ordered results?
18
+ - Are failed/null identities recorded before any filtering?
19
+
20
+ ## Data and routing
21
+
22
+ - Does JavaScript consume structured fields only after a small plain JSON Schema guarantees them?
23
+ - Does synthesis receive complete coverage and failure ledgers?
24
+ - Are `model`, `tier`, and `agentType` used according to selector priority?
25
+ - Did every nonstandard route or agent type come from context with a name and purpose?
26
+
27
+ ## Lifecycle
28
+
29
+ - Are runtime retries and semantic retries separately bounded?
30
+ - Are loops, agents, concurrency, timeout, and token spend bounded appropriately?
31
+ - Are budget claims honest about soft gates and in-flight overshoot?
32
+ - Are checkpoints limited to implemented confirmation/headless behavior?
33
+ - Does nesting stay one level and account for shared limits/store?
34
+ - Would lexical call order remain stable under resume?
35
+
36
+ ## Compatibility and publication
37
+
38
+ - Does new code use `log()` rather than compatibility-only `console`?
39
+ - Is compatibility behavior clearly distinguished from supported authoring behavior and VM substrate?
40
+ - Do package, skill, and generated contract versions match?
41
+ - Do all relative links resolve within the publishable package?
42
+
43
+ Use [lifecycle](lifecycle.md) for lifecycle reasoning. Open the compact [capability index](capabilities.md) only when the review turns on a disputed signature, default, support boundary, or installed version; follow its exhaustive-facts pointer only when the index is insufficient.
@@ -0,0 +1,29 @@
1
+ # Runtime authoring
2
+
3
+ Use this page for routine scripts. Open the generated capability index only when a signature, default, support boundary, or installed-version fact is missing here.
4
+
5
+ ## Script envelope
6
+
7
+ Start with the only legal export: `export const meta = { name, description, phases?: [{ title, detail?, model? }] }`. Values are nonblank literals; declare only used phases and call `phase()` before each phase's work. The remaining body already runs inside an async function: write helpers as ordinary declarations; `export default` and other exports are invalid. Return the result explicitly.
8
+
9
+ The runtime supplies `agent`, `parallel`, `pipeline`, `workflow`, quality/control helpers, `phase`, `log`, `args`, `cwd`, restricted `process.cwd()`, and `budget`. Imports, `require()`, filesystem modules, `Date.now()`, `Math.random()`, and no-argument `new Date()` are unavailable. The Node VM realm is implementation substrate, not a security boundary or public API.
10
+
11
+ ## Topology
12
+
13
+ - `parallel()` takes thunks, runs independent work, and preserves input order. Await the whole array before whole-set synthesis.
14
+ - `pipeline()` runs stages sequentially per item while items proceed concurrently. Each stage receives `(previousValue, originalItem, index)` and forwards `null` to the next stage, so guard missing coverage first.
15
+ - `workflow(name, childArgs?)` runs a context-supplied saved workflow. Nesting is one level and shares limits, counters, tokens, and store.
16
+
17
+ ## Data and failure
18
+
19
+ Call `agent(prompt, { label, schema? })`; it returns text, a schema-validated value, or recoverable `null`. Nonrecoverable limit, validation, and budget failures throw. Record each intended work ID before filtering. A `null` means missing coverage, never a negative finding.
20
+
21
+ Use `agent(prompt, { thread: "implementer" })` when the same managed subagent must receive a later follow-up with its complete conversation intact. Reuse a thread name sequentially; never put same-thread calls in one `parallel()` batch. Threads are scoped to one workflow run/session and cannot use worktree isolation. A pause or process restart may require a fresh thread session.
22
+
23
+ When JavaScript reads fields, pass a small plain JSON Schema. Schema noncompliance after repair throws and bypasses agent retries. Catch it only to return an explicit incomplete outcome without reading missing fields. Return objects, arrays, strings, numbers, booleans, and `null`—not functions, promises, cycles, `BigInt`, or runtime handles.
24
+
25
+ ## Routing and support
26
+
27
+ Selector priority is explicit `model` > named `tier` > phase/run model > pi-subagents agent configuration > parent session. An unavailable explicit selector fails closed rather than silently choosing another model. Worktree isolation is also fail-closed and remains owned by pi-subagents. See [registry ownership](registry-ownership.md).
28
+
29
+ Generated entries marked `supported` are authoring API. `console` and whole-script Markdown fences are compatibility-only. VM realm facilities are internal. Active model routes and agent types are dynamic. Use `log()` in new scripts.
@@ -0,0 +1,19 @@
1
+ # Specialized helpers
2
+
3
+ Preserve candidate or work identity outside helper results that may omit failed agents.
4
+
5
+ ## Quality
6
+
7
+ | Helper | Authoring contract |
8
+ | --- | --- |
9
+ | `completenessCheck(args, results)` | Returns `{ complete, missing? }` or recoverable `null`. The critic sees only the first 4,000 serialized characters, so chunk or summarize larger evidence. Treat the verdict as advisory. |
10
+ | `loopUntilDry({ round, key, consecutiveEmpty, maxRounds })` | `round(index)` is zero-based. Defaults: `JSON.stringify` key, two dry rounds, 50 rounds. Null, non-array, and duplicate-only rounds are dry. Token-budget or agent-limit exhaustion returns the partial array without a termination reason; keep failed-round identity and stopping state outside the helper. |
11
+
12
+ ## Control
13
+
14
+ | Helper | Authoring contract |
15
+ | --- | --- |
16
+ | `gate(thunk, validator, { attempts })` | Calls `thunk(feedback, attempt)` with initial `undefined` feedback and a zero-based attempt. `validator(value)` returns `{ ok, feedback? }`, synchronously or asynchronously; a bare boolean is not accepted. Three attempts by default. Returns `{ ok, value, attempts }`, including the last value on exhaustion. See [validated gate](../examples/validated-gate.js). |
17
+ | `checkpoint(prompt, options?)` | Journals a human/default decision. Only foreground confirm and documented headless behavior work; input, select, and timeout are declared-only. |
18
+
19
+ Always `await gate()`. A thunk containing `await` must itself be declared `async`; await `agent()` before adding its resolved value to a ledger. Runtime agent retries repeat recoverable execution failures; helper attempts are new semantic calls. Bound both layers and ledger exhaustion.
@@ -0,0 +1,13 @@
1
+ # Version compatibility
2
+
3
+ The installed package, capability-contract content, and this skill must describe the same extension version. Read the version line generated at the top of [capabilities](capabilities.md); the skill's frontmatter version and package version must match it.
4
+
5
+ `present-at` means a capability is confirmed in the recorded version. It is an honest baseline, not a claim about the first historical release that introduced the capability. The contract format has its own version because descriptor shape can evolve separately from extension content.
6
+
7
+ When copying a workflow between installations:
8
+
9
+ 1. Check the destination's generated version and exact capability facts.
10
+ 2. Re-check every supported global, option, and constraint used by the script.
11
+ 3. Treat compatibility entries as preservation aids, not portable recommendations.
12
+ 4. Re-resolve dynamic model routes and agent types from destination context; static docs intentionally contain no live entries.
13
+ 5. Prefer runtime behavior when prose and execution disagree, and report the documentation mismatch rather than changing behavior during an authoring fix.
@@ -0,0 +1,51 @@
1
+ ---
2
+ name: workflow-patterns
3
+ description: Argument shapes for the 5 built-in workflow patterns — deep-research, adversarial-review, code-review, multi-perspective, codebase-audit — runnable via the `workflow` tool's `name` input, without slash-command syntax. Use for requests like "research X", "fact-check/adversarially review this", "review this diff/PR", "analyze from multiple perspectives", or "audit the codebase for Y". Not for authoring a new workflow script — see workflow-authoring.
4
+ metadata:
5
+ version: "1.2.1"
6
+ ---
7
+
8
+ # Built-in workflow patterns
9
+
10
+ @signalridge/pi-workflows ships 5 curated, tested workflow patterns. Each is also a
11
+ slash command (`/deep-research`, `/adversarial-review`, `/code-review`,
12
+ `/multi-perspective`, `/codebase-audit`), but they are equally reachable from
13
+ the `workflow` tool directly: call it with `name` set to the pattern name
14
+ below and `args` matching its shape, instead of writing an equivalent script
15
+ from scratch. Prefer this over authoring a new script whenever the request
16
+ fits one of these shapes — the curated version is already reviewed and tested.
17
+
18
+ A project or user saved workflow of the same name always takes precedence
19
+ over a built-in of that name — on the slash command, too.
20
+
21
+ These 5 names are reachable only at the `workflow` tool's top-level `name`
22
+ input, not via the in-script `await workflow(savedName, childArgs)` helper —
23
+ that helper resolves saved workflows only. Calling `workflow('deep-research')`
24
+ from inside a script fails as an unknown saved workflow; use the top-level
25
+ `name` input instead.
26
+
27
+ ## Patterns
28
+
29
+ | `name` | When to reach for it | `args` |
30
+ | --- | --- | --- |
31
+ | `deep-research` | Research a question across the web with cross-checked sources | `{ question: string, angles?: number, minSupport?: number }` — `angles` (default 4) is the number of distinct search queries; `minSupport` (default 2) is the minimum distinct sources required for a claim to survive cross-checking |
32
+ | `adversarial-review` | Investigate a task/claim, then cross-check each finding with skeptical reviewers | `{ task: string, reviewers?: number, threshold?: number }` |
33
+ | `code-review` | Multi-angle review of a diff (correctness, reuse, simplification, efficiency, altitude) | `{ diff: string, diffSource?: string }` — get `diff` yourself first (e.g. `git diff`, `gh pr diff <n>`); this path does not fetch it for you |
34
+ | `multi-perspective` | Analyze a topic from several independent perspectives in parallel, then synthesize | `{ topic: string, perspectives?: string[] }` — omit or give fewer than 2 to use the default set (technical, product, security, user experience, maintainability) |
35
+ | `codebase-audit` | Audit a codebase for health, risk, and debt across dimensions | `{ scope?: string, root?: string, focus?: string, checks?: string[] }` — `scope`/`root` chooses the directory and `focus`/`checks` refines what the agents inspect |
36
+
37
+ ## Example
38
+
39
+ ```json
40
+ { "name": "deep-research", "args": { "question": "What are the tradeoffs of X vs Y?" } }
41
+ ```
42
+
43
+ This is a `workflow` tool call, not a script — omit `script` entirely. The run
44
+ starts in the background exactly like the slash-command form; `background`,
45
+ `maxAgents`, `concurrency`, `agentRetries`, `agentTimeoutMs`, and `tokenBudget`
46
+ all still apply.
47
+
48
+ ## Writing a new workflow instead
49
+
50
+ If the request doesn't fit one of these 5 shapes, author a script with
51
+ `script` as usual — see the workflow-authoring skill.