@signalridge/pi-workflows 1.4.0 → 1.5.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 CHANGED
@@ -1,5 +1,140 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.5.0
4
+ ### Minor Changes
5
+
6
+ - c1b1741: Protocol v4: workflows use the Agent-tier catalogue directly.
7
+
8
+ The wire contract changes, so the three packages move together: `pi-workflows`
9
+ now declares `"@signalridge/pi-subagents": ">=1.9.0"`, and a peer outside that
10
+ range fails the startup handshake with a diagnostic naming both packages rather
11
+ than failing mid-run.
12
+
13
+ The `small | medium | large` workflow-tier vocabulary and its mapping layer are
14
+ gone. A managed request names an Agent tier — a key in the host's own
15
+ `agentTiers` catalogue — and `resolveAgentTier()` is the single resolver for
16
+ every spawn path. Removed with it: the wire's mapped `agentTier` field, the
17
+ `workflow` settings key (retired with a warning naming it; `agentTiers.defaultTier`
18
+ replaces `workflow.defaultTier`), and the per-call `model`/`thinking` selectors,
19
+ which the validator now rejects rather than accepting and ignoring.
20
+
21
+ Every v4 capability is required and every ping carries the host's tier catalogue,
22
+ so an incomplete peer fails one check instead of five optional ones. Fresh
23
+ installs ship `low`/`medium`/`high` profiles, all inheriting their model, and a
24
+ managed call that names no tier falls back to `medium`, so a workflow runs on an
25
+ unconfigured machine without this package choosing a vendor. Because `medium`
26
+ inherits, that fallback lands on the parent session's model — it commits to an
27
+ effort level, not to a vendor, so it does change the thinking level a managed
28
+ call runs at when the parent session is set higher, and it does not move the
29
+ work anywhere cheaper. What it buys is a named policy with a durable snapshot
30
+ and a scope check; a workspace that wants cheaper managed work names a
31
+ `defaultTier` whose profile pins a model. The fallback is scoped to calls that
32
+ cannot inherit a parent model rather than being installed as
33
+ `agentTiers.defaultTier`: a shipped catalogue default would have applied to
34
+ every ordinary `Agent` spawn as well, silencing `defaultModel` and pinning a
35
+ thinking level on machines that configured neither. The catalogue no longer
36
+ ships a `fast` profile; it was the same (model, thinking) pair as `low`, and the
37
+ one agent that named it (Explore) now names `low`. A user agent file that still
38
+ says `agentTier: fast` is reported by the existing unknown-reference check when
39
+ settings and agents load, rather than failing at its first spawn.
40
+
41
+ `agentTiers.defaultTier` therefore has three states, and `/agents → Settings`
42
+ offers all three rather than rendering two of them as one word: a named tier,
43
+ `unset` (managed calls reach the shipped fallback), and `none` (recorded as
44
+ `noDefaultTier`, which withdraws the fallback so managed calls fail closed).
45
+ `setDefaultAgentTier` takes that choice as a tagged value, so a caller cannot
46
+ express "no default" without saying which of the two it means.
47
+
48
+ `resolveAgentTier` gained `requireTier` for that fallback, and the two things
49
+ pre-resolution callers need are both answered by the resolver rather than
50
+ restated beside it: `agentTierApplies()` for "will a tier own this spawn's
51
+ model?", and `selectAgentTier()` for the tier key itself, which the managed
52
+ path needs to label a tombstone and the lifecycle events before the runner has
53
+ resolved anything.
54
+
55
+ Because a tier now owns model resolution outright, the spawn paths stop
56
+ pre-resolving one — and with it they stopped producing the model name the agent
57
+ UI shows. The resolution callback carries that label back instead, so a tier
58
+ that pins a model still names it in the viewer and the agent list, and a profile
59
+ that inherits correctly shows none. The label rides beside the snapshot rather
60
+ than inside it: the snapshot is a durable policy record a managed tombstone
61
+ persists and revalidates, and a cosmetic string does not belong in it.
62
+
63
+ Workflow resume now keys each cached call on the policy for that call's own tier
64
+ rather than on a whole-catalogue fingerprint, so defining or editing an unrelated
65
+ tier no longer forces a full re-execution — but only for a call that names its
66
+ tier. A call that names none keeps the whole-catalogue key, because the host
67
+ resolves those as `call > agent frontmatter > defaultTier` and frontmatter is
68
+ not on this wire: an agent reaches a managed caller as a name. Keyed on
69
+ `defaultTier`, such a call would be wrong twice over — replaying stale work
70
+ after an edit to the tier its agent actually declares, and re-executing after an
71
+ edit to a default it never reached. `agentTierPolicyIdentity()` therefore
72
+ requires a tier rather than accepting `undefined` and folding the default in, so
73
+ the wrong call cannot be written. Journal schema v4 and managed tombstone schema
74
+ v2 are quarantined from older facts rather than migrated.
75
+
76
+ The tier catalogue belongs to the user, names included. The built-in workflows
77
+ and the ad-hoc script ship with this package, so a tier name they use that the
78
+ host does not define is dropped in favour of the host's default rather than
79
+ failing the run — they cannot assert which names exist on someone else's
80
+ machine. A script the user wrote still fails closed on an undefined tier, since
81
+ that is a typo in a catalogue they own. Shipped-ness travels with the script
82
+ rather than with the frame that called it, so a user script that reaches a
83
+ built-in through `workflow(name)` applies the built-in's rule to the built-in;
84
+ the authoring skill now says outright that `low`/`medium`/`high` are the shipped
85
+ profiles rather than names an authored script may assume.
86
+
87
+ The managed routing policy published on the wire reports the default a managed
88
+ call will actually get, fallback included, so a peer's replay identity cannot
89
+ disagree with the host's selection; its sorts are code-unit rather than
90
+ locale-dependent, so the same catalogue fingerprints identically everywhere. The
91
+ workflow peer re-pings per start and resume instead of pinning the catalogue it
92
+ saw at session start, and reports the host-selected tier back through an
93
+ `onTierResolved` callback rather than by mutating the dispatch options.
94
+
95
+ Fixes: a nested spawn no longer relabels an agent's frontmatter tier as
96
+ caller-requested, which had turned a `scopeModels` warning into a refusal and
97
+ misattributed the choice; the scheduler no longer freezes a frontmatter tier into
98
+ a job, so editing the agent file takes effect at fire time; retired managed spawn
99
+ keys are now bounded instead of growing for the life of a session; a
100
+ scope refusal for a tier whose profile inherits its model now names the model
101
+ that would have run instead of the literal `undefined`; and the retired
102
+ `workflow` key warns once per process rather than on every settings read and
103
+ every project write.
104
+
105
+ The tier-key predicate and its length bound now have a single definition, in
106
+ `pi-subagents-protocol`, which `pi-subagents` re-exports. The wire is the
107
+ narrower of the two gates — a key one accepted and the other rejected could
108
+ never reach a peer — so a second copy could only ever be a way for them to
109
+ disagree. That one definition now also governs `blockedProfiles`, which was
110
+ being validated by a helper that trims first: `" low"` became `"low"` there
111
+ while the same value in `profiles` or `defaultTier` was refused outright. All
112
+ three reject.
113
+
114
+ `/agents → Settings` no longer names the fallback tier in the `unset` row's
115
+ label. Which tier `unset` reaches depends on the catalogue, and a workspace that
116
+ edits or deletes the shipped `medium` profile leaves it reaching nothing — at
117
+ which point `unset` and `none` behave identically. The row's description asks
118
+ the resolver what `unset` would currently resolve to and says that, including
119
+ when the answer is "nothing".
120
+
121
+ `pi-workflows` also gained the session-lifecycle handling this routing work
122
+ needed to sit on. The engine has an explicit dispose lifecycle, so a protocol
123
+ probe or a start that is in flight when a session ends rejects with a disposed
124
+ error instead of resolving against an engine nobody owns. The tool, command and
125
+ widget surfaces register exactly once and resolve the *current* engine on each
126
+ use, rather than closing over the one that existed when they were registered —
127
+ a session replacement previously left them pointing at a disposed engine. Tool
128
+ and command handlers report failures as results rather than throwing out of the
129
+ host's dispatch, and the lifecycle handler accepts `subagents:created` and
130
+ `subagents:started` so a spawn's identity is recorded from the first event that
131
+ carries it rather than only at completion.
132
+
133
+ ### Patch Changes
134
+
135
+ - Updated dependencies [c1b1741]
136
+ - @signalridge/pi-subagents-protocol@1.4.0
137
+
3
138
  ## 1.4.0
4
139
  ### Minor Changes
5
140
 
package/README.md CHANGED
@@ -12,8 +12,8 @@ isolation, retry, and session policy.
12
12
 
13
13
  ## Install
14
14
 
15
- `@signalridge/pi-workflows` requires `@signalridge/pi-subagents` `>=1.5.0`
16
- with protocol v3 managed-spawn, workflow-tier, and managed-policy support. They are separate Pi packages: the peer
15
+ `@signalridge/pi-workflows` requires `@signalridge/pi-subagents` `>=1.9.0`
16
+ with protocol v4 managed-spawn, Agent-tier, and managed-policy support. They are separate Pi packages: the peer
17
17
  dependency documents the requirement but intentionally does not auto-load or
18
18
  duplicate the subagent extension.
19
19
 
@@ -54,9 +54,9 @@ export const meta = {
54
54
 
55
55
  phase("scan");
56
56
  const findings = await parallel([
57
- () => agent("Check for cross-package imports", { label: "imports", tier: "small" }),
58
- () => agent("Check for secrets", { label: "secrets", tier: "small" }),
59
- () => agent("Check for dead code", { label: "dead-code", tier: "small" }),
57
+ () => agent("Check for cross-package imports", { label: "imports", tier: "low" }),
58
+ () => agent("Check for secrets", { label: "secrets", tier: "low" }),
59
+ () => agent("Check for dead code", { label: "dead-code", tier: "low" }),
60
60
  ]);
61
61
 
62
62
  phase("synthesize");
@@ -77,15 +77,17 @@ user's own LLM and run on the user's own machine.
77
77
 
78
78
  ### Runtime globals
79
79
 
80
- - `agent(prompt, { label, phase, schema, model, tier, isolation, thread, agentType, toolset, excludeTools, timeoutMs, retries })`
80
+ - `agent(prompt, { label, phase, schema, tier, isolation, thread, agentType, toolset, excludeTools, timeoutMs, retries })`
81
81
  — dispatch one subagent through the managed spawn protocol. Returns text, a
82
82
  schema-validated value, or recoverable `null` after retries. Replayed journal
83
83
  calls consume no real-dispatch cap or token/phase budget. `schema` is a
84
84
  plain JSON Schema; the reply is parsed and validated client-side with bounded
85
85
  repair across attempts, and exhaustion throws `SCHEMA_NONCOMPLIANCE`.
86
- Explicit `model` and `isolation: "worktree"` are resolved and enforced by pi-subagents; `tier` remains the provider-neutral route.
87
- `tier`; see "Model routing"). `thread` chains sequential turns within a named
88
- conversation thread.
86
+ `tier` is the provider-neutral workflow route; `thread` chains sequential turns
87
+ within a named conversation thread. A legacy programmatic `model` option may
88
+ still be accepted by non-managed callers, but workflow meta/phase model or
89
+ thinking fields are rejected and are never a workflow policy source. Pre-schema-v4
90
+ journal runs are quarantined and cannot be resumed.
89
91
  - `parallel(thunks)` — run thunks concurrently, preserve input order. Recoverable
90
92
  thunk failures become `null`; non-recoverable failures (token budget, agent
91
93
  limit) halt the run after the batch barrier settles. Fan-out that breaches
@@ -184,12 +186,52 @@ written atomically with a backup.
184
186
 
185
187
  ## Model routing
186
188
 
187
- The managed protocol carries semantic tiers plus optional exact model/thinking, toolset, denylist, thread, and worktree requests. pi-subagents resolves model references, validates scope, creates/cleans worktrees, and owns tool/session policy. Workflows fail closed with a diagnostic when the peer does not advertise managed-policy support; explicit selectors otherwise fail closed when unavailable, and workflows never bypass the companion's policy.
189
+ `tier` names a key in the host's own `agentTiers` catalogue — the same catalogue an
190
+ ordinary `Agent` call uses. There is no separate workflow-tier vocabulary and no
191
+ mapping layer: a workflow that wants cheap work asks for the tier the user defined
192
+ for cheap work.
193
+
194
+ ```js
195
+ await agent("summarize this diff", { tier: "low" })
196
+ await agent("design the migration", { tier: "high" })
197
+ ```
198
+
199
+ The host's catalogue arrives with the protocol handshake, so a tier this host does
200
+ not define is rejected before any dispatch, naming the ones it does. Resolution
201
+ itself is entirely pi-subagents' `resolveAgentTier()`: it owns the final model,
202
+ thinking level, clamping, availability checks, and the immutable resolution
203
+ snapshot.
204
+
205
+ The catalogue belongs to whoever runs the workflow — the names included. The
206
+ built-in workflows and the ad-hoc script ship with this package, so they cannot
207
+ assert that `low` exists on a machine whose tiers are called `cheap` and `deep`:
208
+ a name they use that the host does not define is dropped, with a log line, and
209
+ the host's own default applies. A script you wrote names tiers from your own
210
+ catalogue, so an undefined one there is a typo and fails closed instead.
211
+
212
+ There is no per-call `model` or `thinking`, and `meta`/phase metadata may not carry
213
+ them either — both are rejected explicitly rather than accepted and ignored. A tier
214
+ is the only model policy a workflow can express, which is what keeps a script from
215
+ pinning a vendor the host did not choose.
216
+
217
+ Omitting `tier` uses the agent's own tier, then `agentTiers.defaultTier`. Fresh
218
+ installs ship `low`/`medium`/`high` profiles, all inheriting their model, and a
219
+ managed call that still names no tier falls back to `medium` — so the built-ins run
220
+ on a new machine without any configuration. That fallback is scoped to managed
221
+ calls: it is not the catalogue's `defaultTier`, so an ordinary `Agent` spawn on the
222
+ same machine keeps falling through to `defaultModel` and the parent session. A host
223
+ whose default has been cleared (`noDefaultTier`) rejects an untiered managed call
224
+ rather than quietly inheriting the parent session's model.
225
+
226
+ The managed wire, invocation record, journal, tombstone, and resume state all carry
227
+ the one resolved tier — including one the host defaulted to, so a run's record says
228
+ what actually ran. Resume keys each cached call on the policy for *that call's*
229
+ tier: editing an unrelated tier does not invalidate work that never used it.
188
230
 
189
231
  ## Intentional adaptations from upstream
190
232
 
191
233
  - **No host-side web tools.** Agents reach the web through configured pi-web-access/MCP tools or an agent type's tool policy.
192
- - **Agent registry and tier catalogue ownership.** `agentType` and `small|medium|large` profiles resolve in pi-subagents, avoiding a second registry/config schema.
234
+ - **Agent registry and tier catalogue ownership.** `agentType` and the tier catalogue resolve in pi-subagents; the workflow package supplies only a tier key, avoiding a second model/thinking policy.
193
235
  - **Schema validation is client-side.** The managed request does not assume a structured-output tool; the runtime asks for JSON, parses, validates (including `additionalProperties`), and repairs across bounded attempts.
194
236
 
195
- State is persisted as `pi.appendEntry("pi-workflows:journal", ...)` custom entries (schema v3), including frozen invocation args, terminal result previews, script revisions, named nested-workflow result boundaries, and durable removals. Interrupted/provider-limited runs replay from the journal; schema-v2 declarative journals are quarantined rather than replayed. Foreground `AbortSignal`s stop owned children through explicit owner-scoped stop/quiescence, and dispose/branch replacement reject stale waiters.
237
+ State is persisted as `pi.appendEntry("pi-workflows:journal", ...)` custom entries (schema v4), including frozen invocation args, resolved tier identity, terminal result previews, script revisions, named nested-workflow result boundaries, and durable removals. Interrupted/provider-limited runs replay from the journal; pre-schema-v4 journals are quarantined rather than replayed. Foreground `AbortSignal`s stop owned children through explicit owner-scoped stop/quiescence, and dispose/branch replacement reject stale waiters.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@signalridge/pi-workflows",
3
- "version": "1.4.0",
3
+ "version": "1.5.0",
4
4
  "description": "Deterministic JavaScript orchestration workflows backed by pi-subagents managed spawning.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -17,10 +17,10 @@
17
17
  "peerDependencies": {
18
18
  "@earendil-works/pi-coding-agent": "^0.84.0",
19
19
  "@earendil-works/pi-tui": "^0.84.0",
20
- "@signalridge/pi-subagents": ">=1.5.0"
20
+ "@signalridge/pi-subagents": ">=1.9.0"
21
21
  },
22
22
  "dependencies": {
23
- "@signalridge/pi-subagents-protocol": "^1.3.0",
23
+ "@signalridge/pi-subagents-protocol": "^1.4.0",
24
24
  "@sinclair/typebox": "^0.34.50",
25
25
  "acorn": "^8.16.0"
26
26
  },
@@ -2,7 +2,7 @@
2
2
  name: workflow-authoring
3
3
  description: Design, write, review, and debug Pi JavaScript workflows that coordinate multiple subagents. Use when a request needs named dependencies, bounded fan-out, per-item pipelines, quality gates, saved or nested workflows, pause/resume, or workflow progress; do not use it merely to run an existing named workflow.
4
4
  metadata:
5
- version: "1.4.0"
5
+ version: "1.5.0"
6
6
  ---
7
7
 
8
8
  # Workflow authoring
@@ -28,10 +28,13 @@ JavaScript.
28
28
  - Bound graph tasks, fan-out items, loops, retries, agents, concurrency, and
29
29
  evidence size. Invocation-level token and time caps are user constraints, not
30
30
  substitutes for algorithmic bounds.
31
- - Route an agent call with `tier: "small"`, `"medium"`, or `"large"`; omit
32
- `tier` to use the pi-subagents workflow default. Never guess nonstandard model
33
- routes, tiers, or agent types—use a name supplied by the current environment
34
- and follow [registry ownership](references/registry-ownership.md).
31
+ - Route an agent call with `tier: "<key>"`, naming a tier the destination host
32
+ defines. Omitting `tier` uses the agent's own tier, then the host's configured
33
+ default; a host whose default has been cleared rejects the call rather than
34
+ inheriting the parent model. There is no per-call `model`/`thinking`, and
35
+ neither may appear in workflow meta or phase profiles. Never guess a tier or
36
+ agent-type name—use names supplied by the current environment and follow
37
+ [registry ownership](references/registry-ownership.md).
35
38
  - Workflows have no imports, filesystem/network APIs, timers, or unrestricted
36
39
  Node APIs. Pass timestamps, randomness, and external decisions through `args`.
37
40
 
@@ -173,6 +176,8 @@ Before shipping a workflow change:
173
176
  - read [debugging](references/debugging.md) when reproducing a failure;
174
177
  - read [the review checklist](references/review.md) before accepting topology;
175
178
  - read [the generated capability index](references/capabilities.md) when a
176
- signature, default, or support boundary is disputed;
179
+ signature, default, or support boundary is disputed; remember that a tier
180
+ names a key in the host catalogue while model/thinking resolution stays in
181
+ pi-subagents;
177
182
  - ensure every package-relative example/reference link remains inside the
178
183
  publishable package.
@@ -1,8 +1,8 @@
1
1
  <!-- GENERATED from WORKFLOW_CAPABILITY_CONTRACT; do not edit by hand. -->
2
2
  # Workflow capability index
3
3
 
4
- Contract format: `1.0.0`<br>
5
- Contract content / skill / extension: `1.4.0`
4
+ Contract format: `2.0.0`<br>
5
+ Contract content / skill / extension: `1.5.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,7 +11,7 @@ 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>`model`: string (optional); default: pi-subagents model resolver<br>`tier`: small\|medium\|large (optional); default: pi-subagents workflow tier<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: 300000 (5min); null disables<br>`retries`: number (optional); default: run retry count |
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>`tier`: agent tier key (optional); default: 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: 300000 (5min); 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" |
@@ -1,8 +1,8 @@
1
1
  <!-- GENERATED from WORKFLOW_CAPABILITY_CONTRACT; do not edit by hand. -->
2
2
  # Exhaustive workflow capability facts
3
3
 
4
- Contract format: `1.0.0`<br>
5
- Contract content / skill / extension: `1.4.0`
4
+ Contract format: `2.0.0`<br>
5
+ Contract content / skill / extension: `1.5.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,8 +16,7 @@ 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
- - `model`: string (optional); default: pi-subagents model resolver
20
- - `tier`: small\|medium\|large (optional); default: pi-subagents workflow tier
19
+ - `tier`: agent tier key (optional); default: the agent's own tier, else agentTiers.defaultTier
21
20
  - `isolation`: "worktree" (optional); default: pi-subagents worktree policy
22
21
  - `thread`: string (optional); default: fresh session per call
23
22
  - `toolset`: string (optional); default: configured host/agent toolset hint
@@ -30,7 +29,9 @@ Every exact fact below is projected from the installed extension's capability co
30
29
  - Constraint: resume replays only the longest unchanged prefix; the first miss and every later call execute live
31
30
  - Constraint: replayed calls do not consume the real-dispatch maxAgents or token/phase budgets
32
31
  - Constraint: spawnKeys rotate by generation on resume so pi-subagents never raises a fingerprint conflict
33
- - Constraint: explicit model is resolved and scope-checked by pi-subagents; unavailable selectors fail closed
32
+ - Constraint: tier names a key in the host's agentTiers catalogue; a key it does not define is rejected before dispatch
33
+ - Constraint: resolveAgentTier in pi-subagents owns model, thinking, clamping, availability, and the resolution snapshot
34
+ - Constraint: there is no per-call model or thinking: a tier is the only model policy a workflow can express
34
35
  - Constraint: same-thread calls must be sequential and preserve a stable workflow thread name
35
36
  - Constraint: threads cannot be combined with worktree isolation
36
37
 
@@ -299,6 +300,7 @@ Every exact fact below is projected from the installed extension's capability co
299
300
  - Signature: `export const meta = { name, description, phases? }`
300
301
  - Constraint: must be the first statement
301
302
  - Constraint: name and description must be nonblank literals
303
+ - Constraint: workflow meta and phase metadata cannot contain model or thinking fields
302
304
  - Constraint: spread, computed keys, methods, template interpolation, and __proto__/constructor/prototype are rejected
303
305
 
304
306
  <a id="script-contract-determinism"></a>
@@ -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 is used | A higher-priority selector overrides the expected route | [registry ownership](registry-ownership.md#priority) |
11
+ | Wrong model or thinking is used | The call named no tier and fell to a default, or named one whose profile 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) |
@@ -23,7 +23,7 @@ Start from the symptom, then reproduce through the real workflow runtime with de
23
23
 
24
24
  1. Reduce to the smallest script that preserves metadata, labels, work IDs, and the failing topology.
25
25
  2. Replace provider calls with deterministic, schema-aware fake agents.
26
- 3. Record call order, labels, phases, prompts, results, and `null` entries.
26
+ 3. Record call order, labels, phases, prompts, resolved tiers, results, and `null` entries.
27
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
28
  5. Separate runtime defects from unsupported authoring assumptions and compatibility-only behavior.
29
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.
@@ -4,14 +4,16 @@ Model routes and agent types are dynamic references. Their shape and owner are d
4
4
 
5
5
  ## Model routes
6
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.
7
+ pi-subagents owns the agent-tier catalogue; a workflow names a key from it and nothing else. Available tier names depend on active configuration and are intentionally absent from static skill files — read them from the destination context.
8
+
9
+ A name a script writes is not a claim that the name exists. Scripts that ship with this package name `low`/`medium`/`high` — the profiles a fresh install receives — and a host that does not define one of those drops it in favour of that host's default, with a log line. A script you write is held to the stricter rule: an undefined tier is a typo in a catalogue you own, so it is rejected before dispatch rather than rerouted. Write the tier you mean for the host you are targeting; do not copy `low`/`medium`/`high` in on the assumption that every machine has them.
8
10
 
9
11
  ## Agent types
10
12
 
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.
13
+ The agent registry owns agent-type names and their bound instructions, tools, Agent-tier defaults, 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
14
 
13
15
  ## Priority
14
16
 
15
- An explicit per-call `model` wins. A named `agentType` model overrides a tier; an explicit `tier` suppresses the phase/run model so pi-subagents can apply the agent definition and then the selected workflow tier. Without a per-call model or tier, the phase/run model is used when present, followed by pi-subagents defaults and the parent session. Model and thinking fields are resolved independently. Avoid specifying competing selectors unless the override is deliberate.
17
+ A call's `tier` takes precedence over an agent type's own tier default, which takes precedence over `agentTiers.defaultTier`. `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 or thinking fields.
16
18
 
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.
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.
@@ -6,6 +6,7 @@ Review author-visible behavior, not formatting preferences. When behavior depend
6
6
 
7
7
  - Is literal `export const meta` the first statement, with a short unique name and useful description?
8
8
  - Are only used phases declared, and does each named phase begin at the intended boundary?
9
+ - Are workflow meta and phase objects free of retired `model`/`thinking` fields?
9
10
  - Does the script call at least one agent and explicitly return JSON-serializable data?
10
11
  - Are imports and nondeterministic APIs absent?
11
12
 
@@ -21,8 +22,9 @@ Review author-visible behavior, not formatting preferences. When behavior depend
21
22
 
22
23
  - Does JavaScript consume structured fields only after a small plain JSON Schema guarantees them?
23
24
  - 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?
25
+ - Does every `tier` name a key the destination host actually defines, rather than a guessed one?
26
+ - Is the tier the only model/thinking policy source, via pi-subagents' `resolveAgentTier()`?
27
+ - Did every tier or agent-type name come from context with a name and purpose?
26
28
 
27
29
  ## Lifecycle
28
30
 
@@ -33,6 +35,7 @@ Review author-visible behavior, not formatting preferences. When behavior depend
33
35
  - Does nesting stay one level, allow only safe sibling concurrency, and account for shared limits/store?
34
36
  - Are helper attempts and graph retries finite and separately visible?
35
37
  - Would lexical call order and generated graph order remain stable under resume?
38
+ - Does the journal/tombstone retain the resolved `tier`, including one the host defaulted to?
36
39
 
37
40
  ## Compatibility and publication
38
41
 
@@ -4,7 +4,7 @@ Use this page for routine scripts. Open the generated capability index only when
4
4
 
5
5
  ## Script envelope
6
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.
7
+ Start with the only legal export: `export const meta = { name, description, phases?: [{ title, detail? }] }`. Values are nonblank literals; declare only used phases and call `phase()` before each phase's work. Workflow meta and phase objects must not contain `model` or `thinking`; those retired fields are rejected. 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
8
 
9
9
  The runtime supplies `agent`, `parallel`, `pipeline`, `orchestrate`, `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
10
 
@@ -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? })`; 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
+ Call `agent(prompt, { label, schema?, tier? })`; it returns text, a schema-validated value, or recoverable `null`. `tier` names a key in the host's agent-tier catalogue. Nonrecoverable limit, validation, budget, unknown-tier, 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
- An explicit per-call `model` wins. A named `agentType` model overrides `tier`; specifying `tier` suppresses the phase/run model and delegates model/thinking resolution to pi-subagents. Without a per-call selector, the phase/run model is used when present, followed by pi-subagents defaults and the parent session. Explicit unavailable models and tiers fail closed. Worktree isolation is also fail-closed and remains owned by pi-subagents. See [registry ownership](registry-ownership.md).
28
+ `tier` is a key in the host's agent-tier catalogue, not a model ID. The catalogue and its names belong to the user running the workflow; read the available keys from the destination context rather than assuming any particular name exists. A tier this host does not define is rejected before dispatch — only the scripts shipped with this package reroute to the host default instead, because they cannot know the catalogue they will land in. pi-subagents' `resolveAgentTier()` alone resolves the final model, thinking, clamping, availability, and snapshot. A tier the host does not define is rejected before dispatch. Omitting `tier` uses the agent's own tier, then `agentTiers.defaultTier`; a host whose default has been cleared rejects the call instead of inheriting the parent model. There is no per-call model or thinking, 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.
@@ -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 dynamic model routes and agent types from destination context; static docs intentionally contain no live entries.
12
+ 4. Re-resolve tier keys and agent types from destination context; both come from the destination host's configuration, and static docs intentionally contain no live catalogue entries.
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.4.0"
5
+ version: "1.5.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.4.0"
5
+ version: "1.5.0"
6
6
  ---
7
7
 
8
8
  # Workflow review