@signalridge/pi-workflows 1.5.0 → 1.7.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/CHANGELOG.md +227 -0
- package/README.md +207 -48
- package/package.json +2 -2
- package/skills/workflow-authoring/SKILL.md +44 -14
- package/skills/workflow-authoring/examples/defensive-json-parsing.js +1 -1
- package/skills/workflow-authoring/examples/phased-budgets.js +1 -1
- package/skills/workflow-authoring/examples/saved-nested-workflows.js +1 -1
- package/skills/workflow-authoring/examples/structured-output.js +1 -1
- package/skills/workflow-authoring/references/capabilities.md +7 -7
- package/skills/workflow-authoring/references/capability-details.md +24 -10
- package/skills/workflow-authoring/references/debugging.md +1 -1
- package/skills/workflow-authoring/references/lifecycle.md +5 -1
- package/skills/workflow-authoring/references/quality-helpers.md +2 -2
- package/skills/workflow-authoring/references/registry-ownership.md +4 -4
- package/skills/workflow-authoring/references/review.md +8 -4
- package/skills/workflow-authoring/references/runtime.md +2 -2
- package/skills/workflow-authoring/references/specialized-helpers.md +1 -1
- package/skills/workflow-authoring/references/versions.md +1 -1
- package/skills/workflow-patterns/SKILL.md +1 -1
- package/skills/workflow-review/SKILL.md +6 -4
- package/src/builtins.ts +23 -23
- package/src/capabilities.ts +37 -12
- package/src/engine.ts +70 -26
- package/src/index.ts +243 -70
- package/src/journal.ts +0 -14
- package/src/rpc-client.ts +14 -1
- package/src/runtime.ts +452 -103
- package/src/strengths.ts +93 -0
- package/src/workflow-settings.ts +95 -1
|
@@ -31,7 +31,7 @@ function parseOrFlag(text, requiredKeys) {
|
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
// ADAPT: validate and bound work, then keep required fields as small as downstream JavaScript needs.
|
|
34
|
-
const work = args && Array.isArray(args.work) ? args.work.slice(0,
|
|
34
|
+
const work = args && Array.isArray(args.work) ? args.work.slice(0, 24) : [{ id: "sample" }];
|
|
35
35
|
const requiredKeys = ["verdict", "reason"];
|
|
36
36
|
const outputs = [];
|
|
37
37
|
const missing = [];
|
|
@@ -5,7 +5,7 @@ export const meta = {
|
|
|
5
5
|
};
|
|
6
6
|
|
|
7
7
|
// ADAPT: validate the work and choose phase budgets, prompts, and schemas; add invocation-level tokenBudget only when the user explicitly requests a cap.
|
|
8
|
-
const work = args && Array.isArray(args.work) ? args.work.slice(0,
|
|
8
|
+
const work = args && Array.isArray(args.work) ? args.work.slice(0, 24) : [{ id: "sample" }];
|
|
9
9
|
const phaseBudget =
|
|
10
10
|
args && Number.isFinite(args.phaseBudget) ? Math.max(1, Math.min(args.phaseBudget, 100000)) : 2000;
|
|
11
11
|
const resultSchema = {
|
|
@@ -7,7 +7,7 @@ export const meta = {
|
|
|
7
7
|
// ADAPT: accept only a saved workflow name supplied in context; never guess an installed name.
|
|
8
8
|
const savedWorkflowName = args && typeof args.savedWorkflowName === "string" ? args.savedWorkflowName : null;
|
|
9
9
|
if (!savedWorkflowName) throw new Error("args.savedWorkflowName must be supplied by context");
|
|
10
|
-
const jobs = args && Array.isArray(args.jobs) ? args.jobs.slice(0,
|
|
10
|
+
const jobs = args && Array.isArray(args.jobs) ? args.jobs.slice(0, 24) : [{ id: "sample" }];
|
|
11
11
|
const preparationSchema = {
|
|
12
12
|
type: "object",
|
|
13
13
|
properties: { ready: { type: "boolean" } },
|
|
@@ -5,7 +5,7 @@ export const meta = {
|
|
|
5
5
|
};
|
|
6
6
|
|
|
7
7
|
// ADAPT: validate and bound work, then keep the schema as small as downstream JavaScript needs.
|
|
8
|
-
const work = args && Array.isArray(args.work) ? args.work.slice(0,
|
|
8
|
+
const work = args && Array.isArray(args.work) ? args.work.slice(0, 24) : [{ id: "sample" }];
|
|
9
9
|
const outputSchema = {
|
|
10
10
|
type: "object",
|
|
11
11
|
properties: {
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
# Workflow capability index
|
|
3
3
|
|
|
4
4
|
Contract format: `2.0.0`<br>
|
|
5
|
-
Contract content / skill / extension: `1.
|
|
5
|
+
Contract content / skill / extension: `1.7.0`
|
|
6
6
|
|
|
7
7
|
This compact generated index covers supported runtime globals and workflow-tool inputs. For constraints, compatibility behavior, internal boundaries, and dynamic-reference ownership, follow the [exhaustive generated facts](capability-details.md).
|
|
8
8
|
|
|
@@ -11,15 +11,15 @@ This compact generated index covers supported runtime globals and workflow-tool
|
|
|
11
11
|
<!-- BEGIN GENERATED SUPPORTED WORKFLOW CAPABILITIES -->
|
|
12
12
|
| Name | Classification | Signature | Options and defaults |
|
|
13
13
|
| --- | --- | --- | --- |
|
|
14
|
-
| agent | runtime-global | `agent(prompt, options?) => Promise<string \| structured value \| null>` | `label`: string (optional); default: derived from phase and call count<br>`phase`: string (optional); default: current phase<br>`schema`: plain JSON Schema (optional)<br>`
|
|
14
|
+
| agent | runtime-global | `agent(prompt, options?) => Promise<string \| structured value \| null>` | `label`: string (optional); default: derived from phase and call count<br>`phase`: string (optional); default: current phase<br>`schema`: plain JSON Schema (optional)<br>`strength`: "low" \| "medium" \| "high" (optional); default: no tier is sent; the agent's own tier, else agentTiers.defaultTier<br>`isolation`: "worktree" (optional); default: pi-subagents worktree policy<br>`thread`: string (optional); default: fresh session per call<br>`toolset`: string (optional); default: configured host/agent toolset hint<br>`excludeTools`: string[] (optional)<br>`agentType`: string (optional); default: general-purpose<br>`timeoutMs`: number \| null (optional); default: 1800000 (30min); null disables<br>`retries`: number (optional); default: run retry count |
|
|
15
15
|
| parallel | runtime-global | `parallel(thunks) => Promise<Array<unknown \| null>>` | — |
|
|
16
16
|
| pipeline | runtime-global | `pipeline(items, ...stages) => Promise<Array<unknown \| null>>` | — |
|
|
17
17
|
| orchestrate | runtime-global | `orchestrate(tasks, options?) => Promise<{ results, tasks }>` | `onError`: "skip-dependents"\|"continue"\|"fail-fast" (optional); default: "skip-dependents" |
|
|
18
18
|
| workflow | runtime-global | `workflow(savedName, childArgs?) => Promise<unknown>` | — |
|
|
19
|
-
| verify | runtime-global | `verify(item, options?) => Promise<{ real, realCount, total, votes }>` | `reviewers`: number (optional); default: 2<br>`threshold`: number (optional); default: 0.5<br>`lens`: string \| string[] (optional) |
|
|
20
|
-
| judgePanel | runtime-global | `judgePanel(attempts, options?) => Promise<{ index, attempt, score, judgments } \| undefined>` | `judges`: number (optional); default: 3<br>`rubric`: string (optional); default: overall quality and correctness |
|
|
19
|
+
| verify | runtime-global | `verify(item, options?) => Promise<{ real, realCount, total, votes }>` | `reviewers`: number (optional); default: 2<br>`threshold`: number (optional); default: 0.5<br>`lens`: string \| string[] (optional)<br>`strength`: "low" \| "medium" \| "high" (optional); default: "low" |
|
|
20
|
+
| judgePanel | runtime-global | `judgePanel(attempts, options?) => Promise<{ index, attempt, score, judgments } \| undefined>` | `judges`: number (optional); default: 3<br>`rubric`: string (optional); default: overall quality and correctness<br>`strength`: "low" \| "medium" \| "high" (optional); default: "low" |
|
|
21
21
|
| loopUntilDry | runtime-global | `loopUntilDry({ round, key?, consecutiveEmpty?, maxRounds? }) => Promise<unknown[]>` | `round`: (roundIndex) => unknown[]; default: required<br>`key`: (item) => string (optional); default: JSON.stringify<br>`consecutiveEmpty`: number (optional); default: 2<br>`maxRounds`: number (optional); default: 50 |
|
|
22
|
-
| completenessCheck | runtime-global | `completenessCheck(taskArgs, results) => Promise<{ complete, missing? } \| null>` |
|
|
22
|
+
| completenessCheck | runtime-global | `completenessCheck(taskArgs, results, options?) => Promise<{ complete, missing? } \| null>` | `strength`: "low" \| "medium" \| "high" (optional); default: "medium" |
|
|
23
23
|
| retry | runtime-global | `retry(thunk, options?) => Promise<unknown>` | `attempts`: number (optional); default: 3<br>`until`: (result) => boolean (optional); default: accept first result |
|
|
24
24
|
| gate | runtime-global | `gate(thunk, validator, options?) => Promise<{ ok, value, attempts }>` | `attempts`: number (optional); default: 3 |
|
|
25
25
|
| checkpoint | runtime-global | `checkpoint(prompt, options?) => Promise<unknown>` | `default`: unknown (optional); default: true when no UI and omitted<br>`headless`: "default" \| "abort" (optional); default: "default"<br>`kind`: "confirm" \| "input" \| "select" (optional); default: "confirm"<br>`choices`: string[] (optional)<br>`timeoutMs`: number (optional) |
|
|
@@ -35,9 +35,9 @@ This compact generated index covers supported runtime globals and workflow-tool
|
|
|
35
35
|
| args | workflow-tool-input | `args?: unknown` | — |
|
|
36
36
|
| background | workflow-tool-input | `background?: boolean = true` | — |
|
|
37
37
|
| maxAgents | workflow-tool-input | `maxAgents?: number = 1000` | — |
|
|
38
|
-
| concurrency | workflow-tool-input | `concurrency?: number` | — |
|
|
38
|
+
| concurrency | workflow-tool-input | `concurrency?: number = the host's pi-subagents maxConcurrent` | — |
|
|
39
39
|
| agentRetries | workflow-tool-input | `agentRetries?: number = 0` | — |
|
|
40
|
-
| agentTimeoutMs | workflow-tool-input | `agentTimeoutMs?: number =
|
|
40
|
+
| agentTimeoutMs | workflow-tool-input | `agentTimeoutMs?: number = 1800000` | — |
|
|
41
41
|
| tokenBudget | workflow-tool-input | `tokenBudget?: number` | — |
|
|
42
42
|
| resumeFromRunId | workflow-tool-input | `resumeFromRunId?: string` | — |
|
|
43
43
|
| export const meta | script-contract | `export const meta = { name, description, phases? }` | — |
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
# Exhaustive workflow capability facts
|
|
3
3
|
|
|
4
4
|
Contract format: `2.0.0`<br>
|
|
5
|
-
Contract content / skill / extension: `1.
|
|
5
|
+
Contract content / skill / extension: `1.7.0`
|
|
6
6
|
|
|
7
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
8
|
|
|
@@ -16,24 +16,28 @@ Every exact fact below is projected from the installed extension's capability co
|
|
|
16
16
|
- `label`: string (optional); default: derived from phase and call count
|
|
17
17
|
- `phase`: string (optional); default: current phase
|
|
18
18
|
- `schema`: plain JSON Schema (optional)
|
|
19
|
-
- `
|
|
19
|
+
- `strength`: "low" \| "medium" \| "high" (optional); default: no tier is sent; the agent's own tier, else agentTiers.defaultTier
|
|
20
20
|
- `isolation`: "worktree" (optional); default: pi-subagents worktree policy
|
|
21
21
|
- `thread`: string (optional); default: fresh session per call
|
|
22
22
|
- `toolset`: string (optional); default: configured host/agent toolset hint
|
|
23
23
|
- `excludeTools`: string[] (optional)
|
|
24
24
|
- `agentType`: string (optional); default: general-purpose
|
|
25
|
-
- `timeoutMs`: number \| null (optional); default:
|
|
25
|
+
- `timeoutMs`: number \| null (optional); default: 1800000 (30min); null disables
|
|
26
26
|
- `retries`: number (optional); default: run retry count
|
|
27
27
|
- Constraint: recoverable failures return null after retries; nonrecoverable failures throw
|
|
28
28
|
- Constraint: schema noncompliance after bounded repair attempts is nonrecoverable
|
|
29
29
|
- Constraint: resume replays only the longest unchanged prefix; the first miss and every later call execute live
|
|
30
30
|
- Constraint: replayed calls do not consume the real-dispatch maxAgents or token/phase budgets
|
|
31
31
|
- Constraint: spawnKeys rotate by generation on resume so pi-subagents never raises a fingerprint conflict
|
|
32
|
-
- Constraint:
|
|
32
|
+
- Constraint: strength is this package's own vocabulary, not an agentTiers key; a word outside low/medium/high is rejected before dispatch
|
|
33
|
+
- Constraint: a strengths table alone binds a strength to an agent tier; an unmapped strength dispatches with no tier and takes the agent's own default
|
|
34
|
+
- Constraint: a call that names no strength also dispatches with no tier; there is no default strength, so an unlabelled call never outranks the agent type's own tier
|
|
35
|
+
- Constraint: an unconfigured host uses a shipped default table: each strength on the catalogue tier of the same name, where the host defines one
|
|
33
36
|
- Constraint: resolveAgentTier in pi-subagents owns model, thinking, clamping, availability, and the resolution snapshot
|
|
34
|
-
- Constraint: there is no per-call model or
|
|
37
|
+
- Constraint: there is no per-call model, thinking, or tier: a strength is the only model policy a workflow can express
|
|
35
38
|
- Constraint: same-thread calls must be sequential and preserve a stable workflow thread name
|
|
36
39
|
- Constraint: threads cannot be combined with worktree isolation
|
|
40
|
+
- Constraint: an option name outside this list is rejected before dispatch, not ignored; tier, model, and thinking name their replacement
|
|
37
41
|
|
|
38
42
|
<a id="runtime-global-parallel"></a>
|
|
39
43
|
## parallel
|
|
@@ -91,6 +95,7 @@ Every exact fact below is projected from the installed extension's capability co
|
|
|
91
95
|
- `reviewers`: number (optional); default: 2
|
|
92
96
|
- `threshold`: number (optional); default: 0.5
|
|
93
97
|
- `lens`: string \| string[] (optional)
|
|
98
|
+
- `strength`: "low" \| "medium" \| "high" (optional); default: "low"
|
|
94
99
|
- Constraint: threshold comparison is inclusive; real is false when no reviewer succeeds
|
|
95
100
|
|
|
96
101
|
<a id="runtime-global-judgepanel"></a>
|
|
@@ -101,6 +106,7 @@ Every exact fact below is projected from the installed extension's capability co
|
|
|
101
106
|
- Signature: `judgePanel(attempts, options?) => Promise<{ index, attempt, score, judgments } \| undefined>`
|
|
102
107
|
- `judges`: number (optional); default: 3
|
|
103
108
|
- `rubric`: string (optional); default: overall quality and correctness
|
|
109
|
+
- `strength`: "low" \| "medium" \| "high" (optional); default: "low"
|
|
104
110
|
- Constraint: highest mean score wins with stable input index as the tie-break; empty input returns undefined
|
|
105
111
|
|
|
106
112
|
<a id="runtime-global-loopuntildry"></a>
|
|
@@ -120,7 +126,8 @@ Every exact fact below is projected from the installed extension's capability co
|
|
|
120
126
|
|
|
121
127
|
- Classification: `runtime-global`
|
|
122
128
|
- Support: `supported`
|
|
123
|
-
- Signature: `completenessCheck(taskArgs, results) => Promise<{ complete, missing? } \| null>`
|
|
129
|
+
- Signature: `completenessCheck(taskArgs, results, options?) => Promise<{ complete, missing? } \| null>`
|
|
130
|
+
- `strength`: "low" \| "medium" \| "high" (optional); default: "medium"
|
|
124
131
|
- Constraint: only the first 4,000 characters of serialized result evidence are sent to the critic
|
|
125
132
|
|
|
126
133
|
<a id="runtime-global-retry"></a>
|
|
@@ -155,6 +162,7 @@ Every exact fact below is projected from the installed extension's capability co
|
|
|
155
162
|
- `timeoutMs`: number (optional)
|
|
156
163
|
- Constraint: consumes one agent slot and no tokens
|
|
157
164
|
- Constraint: journaled answers replay only within an unchanged resume prefix
|
|
165
|
+
- Constraint: an option name outside this list is rejected rather than ignored, since a dropped headless silently auto-approves
|
|
158
166
|
|
|
159
167
|
<a id="runtime-global-log"></a>
|
|
160
168
|
## log
|
|
@@ -254,8 +262,12 @@ Every exact fact below is projected from the installed extension's capability co
|
|
|
254
262
|
|
|
255
263
|
- Classification: `workflow-tool-input`
|
|
256
264
|
- Support: `supported`
|
|
257
|
-
- Signature: `concurrency?: number`
|
|
258
|
-
- Constraint: runtime clamps to 1..
|
|
265
|
+
- Signature: `concurrency?: number = the host's pi-subagents maxConcurrent`
|
|
266
|
+
- Constraint: runtime clamps to 1..64
|
|
267
|
+
- Constraint: read live from the peer at each start and resume, and also the ceiling: a request above the pool is clamped to it, and the run log records the clamp
|
|
268
|
+
- Constraint: 16 is only the fallback for a peer that publishes no pool size
|
|
269
|
+
- Constraint: pi-subagents maxConcurrent stays the authoritative throttle; one setting governs the whole fleet
|
|
270
|
+
- Constraint: that pool is typically small (4 slots by default), so a wide fan-out runs in waves rather than all at once
|
|
259
271
|
|
|
260
272
|
<a id="workflow-tool-input-agentretries"></a>
|
|
261
273
|
## agentRetries
|
|
@@ -270,8 +282,10 @@ Every exact fact below is projected from the installed extension's capability co
|
|
|
270
282
|
|
|
271
283
|
- Classification: `workflow-tool-input`
|
|
272
284
|
- Support: `supported`
|
|
273
|
-
- Signature: `agentTimeoutMs?: number =
|
|
274
|
-
- Constraint: null disables the hard timeout; default
|
|
285
|
+
- Signature: `agentTimeoutMs?: number = 1800000`
|
|
286
|
+
- Constraint: null disables the hard timeout; default 1800000ms (30min)
|
|
287
|
+
- Constraint: it bounds a hang only; an agent's real budget is the host's turn, tool and token ceiling
|
|
288
|
+
- Constraint: the clock restarts when the host reports the agent left its queue, so waiting for a slot is not charged as work
|
|
275
289
|
|
|
276
290
|
<a id="workflow-tool-input-tokenbudget"></a>
|
|
277
291
|
## tokenBudget
|
|
@@ -8,7 +8,7 @@ Start from the symptom, then reproduce through the real workflow runtime with de
|
|
|
8
8
|
| `parallel()` rejects input | Promises were passed instead of thunks | [runtime](runtime.md#topology) |
|
|
9
9
|
| Synthesis starts early | Fan-out was not awaited as one complete result set | [pattern selection](pattern-selection.md#fan-out-and-synthesize) |
|
|
10
10
|
| Coverage silently disappears | `null` results were filtered before IDs were ledgered | [lifecycle](lifecycle.md#retry-and-recoverable-failure) |
|
|
11
|
-
| Wrong model or thinking is used | The call
|
|
11
|
+
| Wrong model or thinking is used | The call's strength is unmapped and fell to the agent's default, or the active `strengths` table — the user's, or the shipped default — points it at a profile that is not what you expected | [registry ownership](registry-ownership.md#priority) |
|
|
12
12
|
| Unknown `agentType` log | A live registry name was guessed or is unavailable | [registry ownership](registry-ownership.md#agent-types) |
|
|
13
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
14
|
| Later calls rerun on resume | An earlier call missed or changed, ending the replayable prefix | [lifecycle](lifecycle.md#resume) |
|
|
@@ -2,7 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
## Bounds and budget
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Bound the work where the work is decided: graph tasks, fan-out items, loop rounds, semantic retries, and evidence size, all inside the script. The invocation-level limits are not that place. `maxAgents` (1000), `concurrency` (the host's pool, and 16 only when it publishes none, clamped either way to 64), `agentRetries` (0) and `agentTimeoutMs` (1800000ms, `null` to disable) are tuned defaults, and `tokenBudget` is unlimited unless set; each one stops or fails a run that outgrows it, so a precautionary value removes coverage rather than cost. Pass one only when the user asked for that limit — a `maxAgents` under the script's own dispatch count ends the run with `AGENT_LIMIT_EXCEEDED` partway, and an `agentTimeoutMs` shorter than the host's turn/tool/token budget fails a whole fan-out as timeouts. A journal replay is logical work and does not consume the real-dispatch `maxAgents` cap, but it still counts toward reported call totals. If the user does ask for a token cap, allow for every planned agent call, retry, synthesis, and verification pass, with headroom: a tight gate terminates coverage and does not reduce work already in flight.
|
|
6
|
+
|
|
7
|
+
`concurrency` needs no value at all: it defaults to the host's pi-subagents `maxConcurrent`, read live at each start and resume, and a larger request is clamped to it (the run log records the clamp). Dispatching wider would not run wider — a workflow agent takes one of those background slots like any other, so the surplus would only wait in the host's queue, ahead of the host's own spawns. Name a number here only to run *narrower* than the host allows, and only when the user asked for that.
|
|
8
|
+
|
|
9
|
+
That pool is typically small — 4 slots unless the host was configured otherwise — so assume a wide fan-out runs in waves, not all at once. This does not change how wide to make it: the agents all run either way, and the reason to split work is coverage rather than wall-clock. It does mean a script should not promise the user a fast answer because it fanned out, and should not add a checkpoint or a progress phase that only makes sense if everything starts together.
|
|
6
10
|
|
|
7
11
|
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
12
|
|
|
@@ -4,5 +4,5 @@ Keep work IDs outside helper results that may omit failed agents.
|
|
|
4
4
|
|
|
5
5
|
| Call | Contract |
|
|
6
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
|
|
7
|
+
| `verify(item, { reviewers: number, threshold: number, lens: string | string[], strength })` | Defaults: 2 reviewers, inclusive `0.5`, one lens or a cycled array, and `strength: "low"` — reviewers are numerous and narrow, and the vote carries the gate rather than any one reviewer's depth. 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, strength })` | Defaults: 3 judges, `"overall quality and correctness"`, and `strength: "low"`. Failed judgments are omitted. Returns the highest mean `{ index, attempt, score, judgments }`; input order wins ties; empty input returns `undefined`. |
|
|
@@ -4,9 +4,9 @@ Model routes and agent types are dynamic references. Their shape and owner are d
|
|
|
4
4
|
|
|
5
5
|
## Model routes
|
|
6
6
|
|
|
7
|
-
pi-subagents owns the agent-tier catalogue
|
|
7
|
+
pi-subagents owns the agent-tier catalogue. A workflow never names a key from it: a script names a *strength* (`low`/`medium`/`high`), and the user's `strengths` table is the only thing that binds one to a tier. Tier names depend on active configuration and are intentionally absent from static skill files; you do not need them, because you never write one.
|
|
8
8
|
|
|
9
|
-
A
|
|
9
|
+
A strength is a statement about the work, not a claim about the host. Every script — shipped or yours — names the same three, and there is no shipped/user distinction, because a strength can never reach the catalogue by itself: the binding is always an entry in a `strengths` table. A host that configured none uses the shipped default table, which is identity wherever the host defines a tier of the same name, so a stock machine honours your `low`/`medium` distinctions without configuration and a host with differently-named tiers leaves every strength unmapped. A user table replaces that default outright, and a strength it omits is unmapped even where a same-named tier exists — which is what lets workflow work be re-priced without touching the tier every ordinary spawn also names.
|
|
10
10
|
|
|
11
11
|
## Agent types
|
|
12
12
|
|
|
@@ -14,6 +14,6 @@ The agent registry owns agent-type names and their bound instructions, tools, Ag
|
|
|
14
14
|
|
|
15
15
|
## Priority
|
|
16
16
|
|
|
17
|
-
A call's `
|
|
17
|
+
A call's strength resolves through the user's `strengths` table into at most one tier key. That key, when there is one, takes precedence over an agent type's own tier default, which takes precedence over `agentTiers.defaultTier`. A call that names no strength, or one whose strength the table does not define, sends no key and leaves that precedence to the host — which is why there is no default strength: one would overrule every agent type's own tier on calls the script never meant to route. `resolveAgentTier()` in pi-subagents then resolves model, thinking, clamping, availability, and the immutable snapshot as one decision. Neither a call, nor workflow meta, nor a phase may carry model, thinking, or tier fields.
|
|
18
18
|
|
|
19
|
-
Unavailability is fail-closed: pi-subagents names the tier and does not silently run a different model. A managed call with no tier and no applicable default is rejected rather than inheriting the parent session.
|
|
19
|
+
Unavailability is fail-closed: pi-subagents names the tier and does not silently run a different model. A managed call with no tier and no applicable default is rejected rather than inheriting the parent session — which an unmapped strength can reach on a host whose default has been cleared.
|
|
@@ -22,14 +22,18 @@ Review author-visible behavior, not formatting preferences. When behavior depend
|
|
|
22
22
|
|
|
23
23
|
- Does JavaScript consume structured fields only after a small plain JSON Schema guarantees them?
|
|
24
24
|
- Does synthesis receive complete coverage and failure ledgers?
|
|
25
|
-
- Does every
|
|
26
|
-
-
|
|
27
|
-
-
|
|
25
|
+
- Does every call name a `strength` from `low`/`medium`/`high`, chosen for the effort the step deserves rather than copied from a tier name?
|
|
26
|
+
- Does every helper that dispatches for you (`verify`, `judgePanel`, `completenessCheck`) either take its documented default deliberately or pass an explicit `strength`?
|
|
27
|
+
- Is the strength the only model/thinking policy source, resolved through the active `strengths` table and then pi-subagents' `resolveAgentTier()`?
|
|
28
|
+
- Did every agent-type name come from context with a name and purpose?
|
|
28
29
|
|
|
29
30
|
## Lifecycle
|
|
30
31
|
|
|
31
32
|
- Are runtime retries and semantic retries separately bounded?
|
|
32
|
-
- Are loops,
|
|
33
|
+
- Are loops, graph tasks, fan-out items, and semantic retries bounded inside the script, where the topology is decided?
|
|
34
|
+
- Does the invocation leave `maxAgents`, `concurrency`, `agentRetries`, `tokenBudget` and `agentTimeoutMs` at their defaults unless the user asked for that limit? A precautionary value there removes coverage, not cost.
|
|
35
|
+
- Is the fan-out as wide as the evidence the task has, rather than narrowed by habit?
|
|
36
|
+
- Does `high` appear only on a step whose error is expensive to reverse — normally nowhere, at most once — rather than across a fan-out?
|
|
33
37
|
- Are budget claims honest about soft gates and in-flight overshoot?
|
|
34
38
|
- Are checkpoints limited to implemented confirmation/headless behavior?
|
|
35
39
|
- Does nesting stay one level, allow only safe sibling concurrency, and account for shared limits/store?
|
|
@@ -17,7 +17,7 @@ The runtime supplies `agent`, `parallel`, `pipeline`, `orchestrate`, `workflow`,
|
|
|
17
17
|
|
|
18
18
|
## Data and failure
|
|
19
19
|
|
|
20
|
-
Call `agent(prompt, { label, schema?,
|
|
20
|
+
Call `agent(prompt, { label, schema?, strength? })`; it returns text, a schema-validated value, or recoverable `null`. `strength` is `low`/`medium`/`high` and has no default — omitting it sends no tier. Nonrecoverable limit, validation, budget, unknown-strength, and unavailable-tier failures throw. Record each intended work ID before filtering. A `null` means missing coverage, never a negative finding.
|
|
21
21
|
|
|
22
22
|
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.
|
|
23
23
|
|
|
@@ -25,6 +25,6 @@ When JavaScript reads fields, pass a small plain JSON Schema. Schema noncomplian
|
|
|
25
25
|
|
|
26
26
|
## Routing and support
|
|
27
27
|
|
|
28
|
-
`
|
|
28
|
+
`strength` is this package's own vocabulary — `low`, `medium`, `high` — not a model ID and not an agent-tier key. A `strengths` table is the only thing that binds a strength to a tier: the user's, or a shipped default (identity, wherever the host defines a tier of the same name) on a host that configured none. A strength no table defines dispatches with no tier and takes the agent's own default, then `agentTiers.defaultTier`; a call that names no strength takes that same untiered path, because a default strength would have outranked the agent type's own tier on every unlabelled call. A word outside the three is rejected before dispatch, and so is any option name `agent()` does not read — `tier`, `model`, and `thinking` answer by naming their replacement, anything else lists the legal keys. `checkpoint()` gates its own options the same way. pi-subagents' `resolveAgentTier()` alone resolves the final model, thinking, clamping, availability, and snapshot. A host whose default has been cleared rejects an untiered call instead of inheriting the parent model. There is no per-call model, thinking, or tier, and workflow meta and phases may not carry them either. Worktree isolation remains owned by pi-subagents. See [registry ownership](registry-ownership.md).
|
|
29
29
|
|
|
30
30
|
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.
|
|
@@ -6,7 +6,7 @@ Preserve candidate or work identity outside helper results that may omit failed
|
|
|
6
6
|
|
|
7
7
|
| Helper | Authoring contract |
|
|
8
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. |
|
|
9
|
+
| `completenessCheck(args, results, { strength })` | Returns `{ complete, missing? }` or recoverable `null`. Defaults to `strength: "medium"`, not `low` like the other gates: one call, no vote to average over, and an open-ended read of the whole task. The critic sees only the first 4,000 serialized characters, so chunk or summarize larger evidence. Treat the verdict as advisory. |
|
|
10
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
11
|
|
|
12
12
|
## Control
|
|
@@ -9,5 +9,5 @@ When copying a workflow between installations:
|
|
|
9
9
|
1. Check the destination's generated version and exact capability facts.
|
|
10
10
|
2. Re-check every supported global, option, and constraint used by the script.
|
|
11
11
|
3. Treat compatibility entries as preservation aids, not portable recommendations.
|
|
12
|
-
4. Re-resolve
|
|
12
|
+
4. Re-resolve agent types from destination context; they come from the destination host's configuration, and static docs intentionally contain no live registry entries. Strengths need no re-resolution — the vocabulary is fixed and the mapping belongs to the destination, whether that is the user's table or the shipped default.
|
|
13
13
|
5. Prefer runtime behavior when prose and execution disagree, and report the documentation mismatch rather than changing behavior during an authoring fix.
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
name: workflow-patterns
|
|
3
3
|
description: Choose and run one of Pi's built-in multi-agent workflows for research, adversarial review, code review, multi-perspective analysis, or codebase auditing. Use when the request matches a named pattern; use workflow-authoring instead for a new script or custom topology.
|
|
4
4
|
metadata:
|
|
5
|
-
version: "1.
|
|
5
|
+
version: "1.7.0"
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# Built-in workflow patterns
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
name: workflow-review
|
|
3
3
|
description: Audit an existing Pi JavaScript workflow for topology, prompt/data flow, limits, failure handling, cancellation, resume safety, and publication compatibility. Use when reviewing or debugging a workflow script or saved run; do not use to run a standard named workflow.
|
|
4
4
|
metadata:
|
|
5
|
-
version: "1.
|
|
5
|
+
version: "1.7.0"
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# Workflow review
|
|
@@ -25,9 +25,11 @@ they are available.
|
|
|
25
25
|
values and their bounds. Confirm that structured fields have a schema and
|
|
26
26
|
that failed/null results remain visible in a ledger.
|
|
27
27
|
4. **Check admission.** Verify finite bounds for task count, fan-out, loops,
|
|
28
|
-
retries,
|
|
29
|
-
|
|
30
|
-
|
|
28
|
+
retries, and evidence size inside the script, and that the invocation leaves
|
|
29
|
+
`maxAgents`, `concurrency`, `tokenBudget` and `agentTimeoutMs` at their
|
|
30
|
+
defaults unless the user asked for that limit — a precautionary value there
|
|
31
|
+
removes coverage rather than cost. Check that a graph validates IDs,
|
|
32
|
+
references, duplicate edges, and cycles before any callback runs.
|
|
31
33
|
5. **Check failure policy.** Distinguish recoverable child `null` from fatal
|
|
32
34
|
workflow errors. For graphs, verify whether the default
|
|
33
35
|
`skip-dependents`, explicit `continue`, or `fail-fast` policy matches the
|
package/src/builtins.ts
CHANGED
|
@@ -55,7 +55,7 @@ const plan = await agent(
|
|
|
55
55
|
'You are planning web research for this question:\\n' + question +
|
|
56
56
|
'\\n\\nProduce ' + angles + ' diverse, specific search queries that together cover the question from different angles. ' +
|
|
57
57
|
'Use your web search tools when helpful to refine them.',
|
|
58
|
-
{ label: 'plan queries',
|
|
58
|
+
{ label: 'plan queries', strength: 'medium', schema: { type: 'object', properties: { queries: { type: 'array', items: { type: 'string' } } }, required: ['queries'] } }
|
|
59
59
|
)
|
|
60
60
|
const planned = plan && Array.isArray(plan.queries) ? plan.queries.filter((q) => typeof q === 'string' && q.trim().length > 0) : []
|
|
61
61
|
const queries = (planned.length > 0 ? planned : [question]).slice(0, angles)
|
|
@@ -67,7 +67,7 @@ const gathered = await parallel(queries.map((q, i) => () =>
|
|
|
67
67
|
'\\n\\nSteps: (1) run a web search for the query; (2) fetch the most relevant result URLs; ' +
|
|
68
68
|
'(3) extract concrete, verifiable factual claims, each tagged with the exact source URL it came from. ' +
|
|
69
69
|
'Do NOT invent sources or claims — report only what the fetched pages actually say.',
|
|
70
|
-
{ label: 'research ' + (i + 1),
|
|
70
|
+
{ label: 'research ' + (i + 1), strength: 'low', schema: { type: 'object', properties: { sources: { type: 'array', items: { type: 'object', properties: { url: { type: 'string' }, claims: { type: 'array', items: { type: 'string' } } }, required: ['url', 'claims'] } } }, required: ['sources'] } }
|
|
71
71
|
)
|
|
72
72
|
))
|
|
73
73
|
const allSources = gathered.filter(Boolean).flatMap((g) => (g && g.sources) || [])
|
|
@@ -77,7 +77,7 @@ const verdict = await agent(
|
|
|
77
77
|
'Cross-check these research sources. Group claims that assert the same fact across different source URLs. ' +
|
|
78
78
|
'Keep a claim only if it is supported by at least ' + minSupport + ' distinct source URLs OR by one clearly authoritative source. ' +
|
|
79
79
|
'Discard claims found in a single weak source or that conflict with others.\\n\\nSOURCES JSON:\\n' + JSON.stringify(allSources),
|
|
80
|
-
{ label: 'cross-check',
|
|
80
|
+
{ label: 'cross-check', strength: 'medium', schema: { type: 'object', properties: { supported: { type: 'array', items: { type: 'object', properties: { claim: { type: 'string' }, sources: { type: 'array', items: { type: 'string' } } }, required: ['claim', 'sources'] } }, discarded: { type: 'array', items: { type: 'string' } } }, required: ['supported'] } }
|
|
81
81
|
)
|
|
82
82
|
|
|
83
83
|
phase('Report')
|
|
@@ -85,7 +85,7 @@ const report = await agent(
|
|
|
85
85
|
'Write a concise, well-structured research report that answers the question using ONLY the supported claims below. ' +
|
|
86
86
|
'Cite source URLs inline next to each claim. If the evidence is thin, say so explicitly.\\n\\n' +
|
|
87
87
|
'QUESTION: ' + question + '\\n\\nSUPPORTED CLAIMS JSON:\\n' + JSON.stringify((verdict && verdict.supported) || []),
|
|
88
|
-
{ label: 'write report',
|
|
88
|
+
{ label: 'write report', strength: 'medium' }
|
|
89
89
|
)
|
|
90
90
|
|
|
91
91
|
return { question, queries, supported: (verdict && verdict.supported) || [], report }`;
|
|
@@ -107,7 +107,7 @@ const threshold = (args && args.threshold) || 0.5
|
|
|
107
107
|
phase('Investigate')
|
|
108
108
|
const investigation = await agent(
|
|
109
109
|
'Investigate the following and list concrete, individually-checkable findings:\\n' + task,
|
|
110
|
-
{ label: 'investigate',
|
|
110
|
+
{ label: 'investigate', strength: 'medium', schema: { type: 'object', properties: { findings: { type: 'array', items: { type: 'string' } } }, required: ['findings'] } }
|
|
111
111
|
)
|
|
112
112
|
const findings = (investigation && Array.isArray(investigation.findings) ? investigation.findings : [])
|
|
113
113
|
|
|
@@ -118,7 +118,7 @@ const judged = await parallel(findings.map((f, i) => () =>
|
|
|
118
118
|
'You are a skeptical reviewer. Try to REFUTE this finding for the task below. ' +
|
|
119
119
|
'Default to real=false when uncertain. Investigate with the available tools if needed.\\n\\n' +
|
|
120
120
|
'TASK: ' + task + '\\nFINDING: ' + f,
|
|
121
|
-
{ label: 'refute ' + (i + 1) + '.' + (r + 1),
|
|
121
|
+
{ label: 'refute ' + (i + 1) + '.' + (r + 1), strength: 'low', schema: { type: 'object', properties: { real: { type: 'boolean' }, reason: { type: 'string' } }, required: ['real'] } }
|
|
122
122
|
)
|
|
123
123
|
)).then((votes) => {
|
|
124
124
|
const valid = votes.filter(Boolean)
|
|
@@ -134,7 +134,7 @@ const report = await agent(
|
|
|
134
134
|
'Synthesize the surviving findings into a concise review. For each finding, note the skeptic agreement ' +
|
|
135
135
|
'(realVotes/totalVotes). Do not include findings that failed the refutation pass.\\n\\n' +
|
|
136
136
|
'TASK: ' + task + '\\n\\nSURVIVING FINDINGS JSON:\\n' + JSON.stringify(survivors),
|
|
137
|
-
{ label: 'consensus report',
|
|
137
|
+
{ label: 'consensus report', strength: 'medium' }
|
|
138
138
|
)
|
|
139
139
|
|
|
140
140
|
return { findings, judged: judged.filter(Boolean), survivors, report }`;
|
|
@@ -154,13 +154,13 @@ const diffSource = (args && args.diffSource) || 'provided diff'
|
|
|
154
154
|
|
|
155
155
|
phase('Scan')
|
|
156
156
|
const finders = await parallel([
|
|
157
|
-
() => agent('You are a correctness reviewer. Find bugs, race conditions, error-handling gaps, and edge-case failures in this diff.\\n\\n' + diff, { label: 'correctness',
|
|
158
|
-
() => agent('You are a reuse reviewer. Find duplicated logic, reinvented wheels, and missed opportunities to reuse existing code or libraries.\\n\\n' + diff, { label: 'reuse',
|
|
159
|
-
() => agent('You are a simplification reviewer. Find over-engineering, unnecessary complexity, and simpler alternatives.\\n\\n' + diff, { label: 'simplification',
|
|
160
|
-
() => agent('You are an efficiency reviewer. Find performance problems, avoidable allocations, and scaling risks.\\n\\n' + diff, { label: 'efficiency',
|
|
161
|
-
() => agent('You are an architecture reviewer. Assess the change at the right altitude: design fit, layering, boundaries, and future maintenance.\\n\\n' + diff, { label: 'altitude',
|
|
162
|
-
() => agent('You are a security reviewer. Find trust-boundary, injection, secret-leak, permission, and unsafe-input risks in this diff.\\n\\n' + diff, { label: 'security',
|
|
163
|
-
() => agent('You are a testability reviewer. Find missing regression tests, observability gaps, and rollout/recovery risks in this diff.\\n\\n' + diff, { label: 'testability',
|
|
157
|
+
() => agent('You are a correctness reviewer. Find bugs, race conditions, error-handling gaps, and edge-case failures in this diff.\\n\\n' + diff, { label: 'correctness', strength: 'low', schema: { type: 'object', properties: { findings: { type: 'array', items: { type: 'string' } } }, required: ['findings'] } }),
|
|
158
|
+
() => agent('You are a reuse reviewer. Find duplicated logic, reinvented wheels, and missed opportunities to reuse existing code or libraries.\\n\\n' + diff, { label: 'reuse', strength: 'low', schema: { type: 'object', properties: { findings: { type: 'array', items: { type: 'string' } } }, required: ['findings'] } }),
|
|
159
|
+
() => agent('You are a simplification reviewer. Find over-engineering, unnecessary complexity, and simpler alternatives.\\n\\n' + diff, { label: 'simplification', strength: 'low', schema: { type: 'object', properties: { findings: { type: 'array', items: { type: 'string' } } }, required: ['findings'] } }),
|
|
160
|
+
() => agent('You are an efficiency reviewer. Find performance problems, avoidable allocations, and scaling risks.\\n\\n' + diff, { label: 'efficiency', strength: 'low', schema: { type: 'object', properties: { findings: { type: 'array', items: { type: 'string' } } }, required: ['findings'] } }),
|
|
161
|
+
() => agent('You are an architecture reviewer. Assess the change at the right altitude: design fit, layering, boundaries, and future maintenance.\\n\\n' + diff, { label: 'altitude', strength: 'medium', schema: { type: 'object', properties: { findings: { type: 'array', items: { type: 'string' } } }, required: ['findings'] } }),
|
|
162
|
+
() => agent('You are a security reviewer. Find trust-boundary, injection, secret-leak, permission, and unsafe-input risks in this diff.\\n\\n' + diff, { label: 'security', strength: 'low', schema: { type: 'object', properties: { findings: { type: 'array', items: { type: 'string' } } }, required: ['findings'] } }),
|
|
163
|
+
() => agent('You are a testability reviewer. Find missing regression tests, observability gaps, and rollout/recovery risks in this diff.\\n\\n' + diff, { label: 'testability', strength: 'low', schema: { type: 'object', properties: { findings: { type: 'array', items: { type: 'string' } } }, required: ['findings'] } }),
|
|
164
164
|
])
|
|
165
165
|
const candidates = finders.filter(Boolean).flatMap((f) => (f && f.findings) || [])
|
|
166
166
|
|
|
@@ -168,7 +168,7 @@ phase('Verify')
|
|
|
168
168
|
const verified = await parallel(candidates.map((finding, i) => () =>
|
|
169
169
|
agent('You are a skeptical verifier. For this candidate finding, check whether it is a REAL issue in the diff. ' +
|
|
170
170
|
'Default to real=false when uncertain.\\n\\nCANDIDATE: ' + finding + '\\n\\nDIFF:\\n' + diff,
|
|
171
|
-
{ label: 'verify ' + (i + 1),
|
|
171
|
+
{ label: 'verify ' + (i + 1), strength: 'low', schema: { type: 'object', properties: { real: { type: 'boolean' }, reason: { type: 'string' } }, required: ['real'] } })
|
|
172
172
|
))
|
|
173
173
|
|
|
174
174
|
phase('Report')
|
|
@@ -179,7 +179,7 @@ const report = await agent(
|
|
|
179
179
|
'Write a ranked code review report for this diff (' + diffSource + '). ' +
|
|
180
180
|
'Include only verified findings, ordered by severity, each with a concrete location and fix suggestion.\\n\\n' +
|
|
181
181
|
'VERIFIED FINDINGS JSON:\\n' + JSON.stringify(ranked),
|
|
182
|
-
{ label: 'final report',
|
|
182
|
+
{ label: 'final report', strength: 'medium' }
|
|
183
183
|
)
|
|
184
184
|
|
|
185
185
|
return { candidates, ranked, report }`;
|
|
@@ -199,13 +199,13 @@ const perspectives = (args && Array.isArray(args.perspectives) && args.perspecti
|
|
|
199
199
|
phase('Perspectives')
|
|
200
200
|
const views = await parallel(perspectives.map((p, i) => () =>
|
|
201
201
|
agent('Evaluate this topic from the perspective of: ' + p + '. Be concrete and specific; note risks, trade-offs, and what matters most from this angle.\\n\\nTOPIC: ' + topic,
|
|
202
|
-
{ label: 'perspective ' + (i + 1),
|
|
202
|
+
{ label: 'perspective ' + (i + 1), strength: 'low', schema: { type: 'object', properties: { angle: { type: 'string' }, points: { type: 'array', items: { type: 'string' } }, risks: { type: 'array', items: { type: 'string' } } }, required: ['angle', 'points', 'risks'] } })
|
|
203
203
|
))
|
|
204
204
|
|
|
205
205
|
phase('Synthesis')
|
|
206
206
|
const synthesis = await agent(
|
|
207
207
|
'Synthesize these perspectives into a balanced assessment. Identify where perspectives agree, where they conflict, and give a bottom-line recommendation with the key trade-offs.\\n\\nTOPIC: ' + topic + '\\n\\nPERSPECTIVES JSON:\\n' + JSON.stringify(views.filter(Boolean)),
|
|
208
|
-
{ label: 'synthesis',
|
|
208
|
+
{ label: 'synthesis', strength: 'medium' }
|
|
209
209
|
)
|
|
210
210
|
|
|
211
211
|
return { views: views.filter(Boolean), synthesis }`;
|
|
@@ -229,21 +229,21 @@ const map = await agent(
|
|
|
229
229
|
'Explore the codebase at ' + root + ' and produce a structural map: top-level modules, entry points, ' +
|
|
230
230
|
'data flow, and where complexity concentrates. Use the available file tools.\\n' +
|
|
231
231
|
(focus ? 'Focus/checks requested: ' + focus + '\\n' : ''),
|
|
232
|
-
{ label: 'map',
|
|
232
|
+
{ label: 'map', strength: 'medium', schema: { type: 'object', properties: { modules: { type: 'array', items: { type: 'object', properties: { name: { type: 'string' }, purpose: { type: 'string' }, risk: { type: 'string' } }, required: ['name', 'purpose'] } }, entryPoints: { type: 'array', items: { type: 'string' } }, complexityHotspots: { type: 'array', items: { type: 'string' } } }, required: ['modules'] } }
|
|
233
233
|
)
|
|
234
234
|
|
|
235
235
|
phase('Audit')
|
|
236
236
|
const audits = await parallel([
|
|
237
|
-
() => agent('Audit this codebase for correctness risks: error handling, boundary conditions, concurrency, security. Use file tools.\\n\\nSTRUCTURE:\\n' + JSON.stringify(map), { label: 'correctness',
|
|
238
|
-
() => agent('Audit this codebase for maintainability: coupling, duplication, naming, test coverage, documentation. Use file tools.\\n\\nSTRUCTURE:\\n' + JSON.stringify(map), { label: 'maintainability',
|
|
239
|
-
() => agent('Audit this codebase for operational health: dependencies, build, deployment, observability, secrets handling. Use file tools.\\n\\nSTRUCTURE:\\n' + JSON.stringify(map), { label: 'operations',
|
|
237
|
+
() => agent('Audit this codebase for correctness risks: error handling, boundary conditions, concurrency, security. Use file tools.\\n\\nSTRUCTURE:\\n' + JSON.stringify(map), { label: 'correctness', strength: 'low', schema: { type: 'object', properties: { findings: { type: 'array', items: { type: 'string' } }, severity: { type: 'string' } }, required: ['findings'] } }),
|
|
238
|
+
() => agent('Audit this codebase for maintainability: coupling, duplication, naming, test coverage, documentation. Use file tools.\\n\\nSTRUCTURE:\\n' + JSON.stringify(map), { label: 'maintainability', strength: 'low', schema: { type: 'object', properties: { findings: { type: 'array', items: { type: 'string' } }, severity: { type: 'string' } }, required: ['findings'] } }),
|
|
239
|
+
() => agent('Audit this codebase for operational health: dependencies, build, deployment, observability, secrets handling. Use file tools.\\n\\nSTRUCTURE:\\n' + JSON.stringify(map), { label: 'operations', strength: 'low', schema: { type: 'object', properties: { findings: { type: 'array', items: { type: 'string' } }, severity: { type: 'string' } }, required: ['findings'] } }),
|
|
240
240
|
])
|
|
241
241
|
|
|
242
242
|
phase('Report')
|
|
243
243
|
const report = await agent(
|
|
244
244
|
'Write a codebase health audit report: overall health score, ranked findings across dimensions with concrete locations, ' +
|
|
245
245
|
'and a prioritized remediation roadmap.\\n\\nSTRUCTURE:\\n' + JSON.stringify(map) + '\\n\\nAUDITS JSON:\\n' + JSON.stringify(audits.filter(Boolean)),
|
|
246
|
-
{ label: 'audit report',
|
|
246
|
+
{ label: 'audit report', strength: 'medium' }
|
|
247
247
|
)
|
|
248
248
|
|
|
249
249
|
return { map, audits: audits.filter(Boolean), report }`;
|