@henryqw/pi-subagent 15.1.4 → 16.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CONTEXT.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  ## Purpose
4
4
 
5
- Provide validated built-in and user Roles, shared task-model Pi launch policy, generic `delegate_task` delegation, and package-owned `delegate_flow` Git orchestration. Main plans and orchestrates; `delegate_task` remains a flat bounded delegation tool, while Flow uses the effective Implementer and only invokes its effective Reviewer for explicit judgment criteria after authoritative validation. The bundled Main-side Skill adds no runtime behavior or changes generic fallback.
5
+ Provide validated built-in and user Roles, shared task-model Pi launch policy, generic `delegate_task` delegation, and public executor, worktree, and exact-review-evidence APIs. Main or a consuming package owns any checked implementation protocol.
6
6
 
7
7
  ## Domain glossary
8
8
 
@@ -14,24 +14,21 @@ Provide validated built-in and user Roles, shared task-model Pi launch policy, g
14
14
  - **Delegated Task**: one bounded work request sent from Main to one Role.
15
15
  - **Workflow**: generic orchestration of one or more Delegated Tasks; `delegate_task` owns its selected mode, while library callers compose executor runs in JavaScript.
16
16
  - **Workflow Mode**: `delegate_task` tool policy selected per call for `single`, `parallel`, or `chain` execution; not a Role property or executor API.
17
- - **Flow**: package-owned, memory-only Git implementation and integration workflow started by `delegate_flow`.
18
- - **Unit Worktree**: one Flow-owned worktree and branch for one Flow unit; it is reused for rebase, validation, optional exact review, and one repair.
19
- - **Review Packet**: exact `{base, tip, patchPath}` evidence delivered to the Flow Reviewer only for a unit's explicit `review` criterion.
17
+ - **Exact Review Evidence**: caller-requested private base-to-tip patch plus exact `{base, tip, patchPath}` identity prepared by `prepareExactReviewEvidence`.
20
18
  - **Resource Policy**: Role ownership of base tools, extensions, and Skill names, plus explicit caller additions of tools, extensions, and environment through `createRoleLaunch`.
21
19
  - **Pi Launch**: reusable `{env,args}` policy for one Role, resolved model route, explicit caller resources, and project trust.
22
20
  - **Ephemeral Executor**: mechanism that receives a prepared Pi Launch, runs one bounded Delegated Task in one no-session child process, and returns its result without discovering resources or composing a Workflow.
23
21
 
24
22
  ## Invariants
25
23
 
26
- - One Delegated Task creates one ephemeral child process and no saved session. Execution ends at the first hard budget: attempted turn 51 by default (`maxTurns` is a safe integer >= 1; default 50), attempted continuation after the optional token handoff, or `deadline = min(last recognized Pi JSON event + idle timeout, child start + maximum runtime)` (recognized Pi events renew; raw bytes do not; max always terminates). A terminal turn 50 succeeds; attempted continuation rejects with `turn_limit`, accumulated usage, and bounded output. Optional `maxTokens` is a safe integer >= 1 with an unlimited default. It is one global executor default applied independently to every `delegate_task` child and every Flow Implementer, Reviewer, and repair child; it is neither a shared pool nor a per-call or environment option. Token accounting sums each completed assistant response's `Usage.totalTokens` once, matching the executor's aggregate `Usage`. A terminal response crossing the limit succeeds. A continuing crossing turn and its tools finish, then exactly one extra response turn is permitted. Further continuation rejects with typed `token_limit`, aggregate usage, and bounded last assistant output. This permits a crossing turn plus the final response to overshoot; it is not an exact hard cap. Raw executor launches enforce the extra-turn window but do not guarantee tools are disabled.
27
- - Every Role launch installs the shared tool policy. On a continuing token crossing, or the continuing penultimate `maxTurns` turn, the policy waits for `turn_end`, disables every tool, and steers one structured final report. This covers `delegate_task` and each Flow launch. With `maxTurns` set to 1, tools are disabled during `session_start`, and the sole provider turn is the response-only handoff. The fixed decision packet is the default, but exact task or Role output takes precedence and is returned alone. Terminal boundary responses get no handoff. Before the final boundary, the policy steers the fixed convergence warning once at each 80% threshold for completed turns, aggregate tokens when configured, and maximum runtime. It combines thresholds first due together and starts no timer or extra warning turn. Timeout, provider, or child-process failures can prevent a handoff. After direct Pi exits, inherited stdout/stderr drain until EOF unless an escaped descendant holds them past short inactivity or a one-second hard deadline. Configurable in `~/.pi/agent/config/pi-subagent/config.json` (`maxTurns` defaults to 50; `maxTokens` defaults to unlimited; `timeout.idleMinutes`/`maxMinutes` default to 10/30).
24
+ - One Delegated Task creates one ephemeral child process and no saved session. Execution ends at the first hard budget: attempted turn 51 by default (`maxTurns` is a safe integer >= 1; default 50), attempted continuation after the optional token handoff, or `deadline = min(last recognized Pi JSON event + idle timeout, child start + maximum runtime)` (recognized Pi events renew; raw bytes do not; max always terminates). A terminal turn 50 succeeds; attempted continuation rejects with `turn_limit`, accumulated usage, and bounded output. Optional `maxTokens` is a safe integer >= 1 with an unlimited default. It is one global executor default applied independently to every `delegate_task` child; it is neither a shared pool nor a per-call or environment option. Token accounting sums each completed assistant response's `Usage.totalTokens` once, matching the executor's aggregate `Usage`. A terminal response crossing the limit succeeds. A continuing crossing turn and its tools finish, then exactly one extra response turn is permitted. Further continuation rejects with typed `token_limit`, aggregate usage, and bounded last assistant output. This permits a crossing turn plus the final response to overshoot; it is not an exact hard cap. Raw executor launches enforce the extra-turn window but do not guarantee tools are disabled.
25
+ - Every Role launch installs the shared tool policy. On a continuing token crossing, or the continuing penultimate `maxTurns` turn, the policy waits for `turn_end`, disables every tool, and steers one structured final report. This covers `delegate_task` and library Role launches made with the public launch API. With `maxTurns` set to 1, tools are disabled during `session_start`, and the sole provider turn is the response-only handoff. The fixed decision packet is the default, but exact task or Role output takes precedence and is returned alone. Terminal boundary responses get no handoff. Before the final boundary, the policy steers the fixed convergence warning once at each 80% threshold for completed turns, aggregate tokens when configured, and maximum runtime. It combines thresholds first due together and starts no timer or extra warning turn. Timeout, provider, or child-process failures can prevent a handoff. After direct Pi exits, inherited stdout/stderr drain until EOF unless an escaped descendant holds them past short inactivity or a one-second hard deadline. Configurable in `~/.pi/agent/config/pi-subagent/config.json` (`maxTurns` defaults to 50; `maxTokens` defaults to unlimited; `timeout.idleMinutes`/`maxMinutes` default to 10/30).
28
26
  - Up to five active ephemeral `delegate_task` children run per Main by default, configurable via `maxSubagents` in `~/.pi/agent/config/pi-subagent/config.json` or the `PI_SUBAGENT_MAX_SUBAGENTS` environment variable; excess calls wait FIFO. Queued calls do not start a child or consume child timeout.
29
27
  - Ambient child extensions and Skills stay disabled. Every Role requires `tools`, `extensions`, and `skills` YAML arrays, and every launch installs the Role tool policy. `tools: []` activates no base built-ins but does activate all tools from explicitly selected trusted extension bundles and explicit caller tool additions; `skills: []` selects no separately named Role Skills but trusted selected extension Skills still load; `extensions: []` selects no Role extension bundle. A Role/caller explicitly selected extension is a trusted atomic capability bundle: all tools it registers and all Skills supplied through its Pi package metadata or dynamic `resources_discover` load alongside separately named Role Skills. This intentionally includes the extension's executable lifecycle/prompt behavior; pi-subagent does not infer or externally narrow undocumented dependencies, and loading an extension is not sandboxing. Scope children by selecting fewer trusted extensions; finer granularity requires separate entry points/configuration or an upstream split. Explicit Role/caller tool names still verify against the final filtered registry, while parent-only recursive orchestration tools remain excluded.
30
28
  - Role Skill names resolve through Main's effective Pi Skill registry; unavailable names warn and skip without blocking delegation. Explicit Role/caller tool names verify against the final filtered child registry after explicit provider `session_start` handlers, and unavailable names fail before the first turn.
31
29
  - Route precedence is explicit call-level `modelClass` > Role `modelClass` > configured Model Task assignment or declared default. pi-subagent's local `pi-subagent/delegateTask` declaration defaults to `fast`. A direct model replaces only the selected route model and must honor its exact thinking level. Library callers select a Role plus their own Model Task declaration.
32
30
  - The selected profile resolves primary then fallback only before launch when a route, model, or thinking level is unavailable. A direct model never changes the route level and fails before launch if it cannot honor it. A missing local JSON config uses defaults quietly. Missing shared task-model config warns once per session because delegation needs a route. If neither route is usable, launch rejects with `Run /task-models`; a started child is never retried by this package.
33
- - User Role Markdown files and Subagent JSON config (`config/pi-subagent/config.json`) live only in the user `config/pi-subagent` directory; model routes live in shared `config/pi-task-models/config.json`. Package-shipped built-in Roles (`implementer`, `reviewer`, `scout`) resolve from the package's own `examples/roles/` Markdown through the same parser and intentionally leave `modelClass` unset; same-named user files override built-ins for `delegate_task`, while same-named `implementer` and `reviewer` files override Flow defaults. The `synthesizer` Markdown remains the only optional inert sample.
34
- - `delegate_flow` accepts 1–8 independent units with direct validation commands, optional `modelClass`, and optional non-empty `review` judgment text. An explicit unit class overrides both frozen Roles. With no unit class, the frozen Implementer and, when needed, Reviewer each use their own Role default before pi-subagent's local `pi-subagent/delegateTask` assignment or declared `fast` default. At Flow start it always resolves/freezes the effective Implementer and resolves/freezes a Reviewer only when at least one requested unit has `review`. One active Flow creates every Unit Worktree before launching Implementers in parallel, then processes settled units in declared order. For each unit Flow rebases in place when earlier units advanced Main, inspects committed Git state, and runs declared validation. Validation is authoritative for objective verification: units without `review` fast-forward their exact validated tip; units with `review` send the exact Review Packet to the Reviewer in the same worktree and require exact `PASS` before the same guarded `git merge --ff-only` path.
35
- - Flow is memory-only. Only a post-rebase commit drop produces a no-op (`base === tip`); it validates, skips Reviewer and merge, then cleans up ordinarily. Initial zero-commit implementations block. Implementer, validation, or reviewer blocks allow one `delegate_flow_continue({ guidance, modelClass? })` repair in the same worktree; omission retains a supplied Unit class and otherwise lets each frozen Role use its own default, while presence replaces both for that repair. A second block is terminal. Rebase and evidence/Reviewer/infrastructure failures retain worktrees. A reported fast-forward failure retains its worktree unless Main is clean at the exact integrated tip, which completes with the merge diagnostic as a warning. Cleanup uses non-forced worktree removal and branch deletion; cleanup refusal is a completion warning.
36
- - Flow has no graph, saved recovery, automatic retry, aggregate review, or post-merge validation. It never changes generic `delegate_task` Role resolution, isolation, non-Git fallback, or ordinary direct plan/file review.
31
+ - User Role Markdown files and Subagent JSON config (`config/pi-subagent/config.json`) live only in the user `config/pi-subagent` directory; model routes live in shared `config/pi-task-models/config.json`. Package-shipped built-in Roles (`implementer`, `reviewer`, `scout`) resolve from the package's own `examples/roles/` Markdown through the same parser and intentionally leave `modelClass` unset; same-named user files override built-ins for `delegate_task` and public API callers. The `synthesizer` Markdown remains the only optional inert sample.
32
+ - Low-level worktree APIs allocate optional isolated child worktrees and return explicit `pruned`, `retained`, or `recovery` lifecycle payloads. They never force-delete uncertain work or choose integration policy for callers.
33
+ - `prepareExactReviewEvidence` validates Git identity and produces a bounded private patch with exact base and tip OIDs. The caller owns review criteria, verdict validation, integration, cleanup, and durable state.
37
34
  - Numbered Codex routes prefer Main's active account slot and explicitly load the multi-Codex child extension.
package/README.md CHANGED
@@ -1,9 +1,10 @@
1
1
  # `@henryqw/pi-subagent`
2
2
 
3
- Delegate bounded work from Main to isolated Pi Roles, or run independent implementation units through a fixed Git Flow. One Role system covers focused research, review, implementation, parallel work, and ordered chains without loading every detail into Main.
3
+ Delegate bounded work from Main to isolated Pi Roles. `delegate_task` remains lightweight generic delegation. It does not own durable checked implementation graphs.
4
+
5
+ The public API also supplies Role launch, executor, worktree, and exact-evidence support for packages such as `@henryqw/pi-orchestrator`.
4
6
 
5
7
  ![Pi showing six delegated tasks running in parallel](./example.png)
6
- ![Delegate Flow lifecycle](./docs/delegate-flow.svg)
7
8
 
8
9
  ## Install
9
10
 
@@ -18,7 +19,9 @@ Run `/task-models` and configure the `fast` profile before delegating. Open `/ta
18
19
 
19
20
  | Package | Relationship | Purpose |
20
21
  | --- | --- | --- |
22
+ | [`@henryqw/pi-orchestrator`](https://pi.henry.wang/extensions/pi-orchestrator) | Consumer | Owns durable checked local implementation graphs. |
21
23
  | [`@henryqw/pi-task-models`](https://pi.henry.wang/extensions/pi-task-models) | Required | Supplies `fast`, `balanced`, `frontier`, and `fav` model routes. |
24
+ | [`@henryqw/pi-process`](https://pi.henry.wang/packages/pi-process) | Required | Runs bounded captured Git commands. |
22
25
 
23
26
  Routes come from `~/.pi/agent/config/pi-task-models/config.json`. It stores explicit task overrides. Missing shared model config warns once because delegation needs a route.
24
27
 
@@ -39,9 +42,6 @@ A separate child returns a bounded report to Main. It creates no saved Pi sessio
39
42
  | Surface | Type | Purpose |
40
43
  | --- | --- | --- |
41
44
  | `delegate_task` | tool | Run one bounded task, independent tasks in parallel, or dependent tasks in a chain. |
42
- | `delegate_flow` | tool | Implement and integrate 1–8 independent Git units. |
43
- | `delegate_flow_continue` | tool | Repair the one blocked Flow unit once. |
44
- | `pi-subagent-delegated-development` | skill | Guide Main's planning and orchestration. |
45
45
 
46
46
  Pi's built-in tool block shows each call and result.
47
47
 
@@ -68,7 +68,7 @@ Parallel tasks start together, settle together, and report in input order. Chain
68
68
 
69
69
  Foreground failures throw after keeping bounded sibling and recovery evidence. One call has one aggregate 50 KiB cap for Main-visible text. Final results show summaries first and full evidence below.
70
70
 
71
- The status widget shows each task group name above at most three indented child rows. Each row shows a one-letter Role badge, status, activity, usage, and duration. Flow stages for one unit share that unit's heading.
71
+ The status widget shows each task group name above at most three indented child rows. Each row shows a one-letter Role badge, status, activity, usage, and duration.
72
72
 
73
73
  Background work belongs to its launching session. Shutdown or reload aborts it and may leave only recoverable-work evidence or no follow-up message.
74
74
 
@@ -76,13 +76,13 @@ Each entry resolves its own Role, resources, route, and optional isolation. A Ro
76
76
 
77
77
  See the [orchestration guide](./docs/orchestration.md) for full delegation, transport, isolation, and UI behavior.
78
78
 
79
- ### Skills
79
+ ### Delegation guidance
80
80
 
81
- The bundled [`pi-subagent-delegated-development`](./skills/pi-subagent-delegated-development/SKILL.md) Skill guides Main's planning and orchestration. It adds no runtime code, config, or Role installation.
81
+ `delegate_task` is generic delegation, not checked implementation orchestration. Main or a consuming package owns validation, review, integration, recovery, and durable state.
82
82
 
83
83
  Implementers remove only task-created temporary, generated, or ignored artifacts. Required deliverables and unrelated files stay intact. They never use `git clean` or blanket deletion, and unclear paths block.
84
84
 
85
- For known regressions with a runner that supports test-name filtering, use a test-name filter. Keep broad package or workspace checks to one caller-owned final validation after relevant units integrate. Flow itself does not run that check.
85
+ For known regressions with a runner that supports test-name filtering, use a test-name filter. Keep broad package or workspace checks to one caller-owned final validation after relevant changes integrate. Generic delegation does not run that check.
86
86
 
87
87
  Before delegating:
88
88
 
@@ -98,46 +98,15 @@ Its ordinary review loop is optional. Use it only when the caller or repository
98
98
 
99
99
  - Call `delegate_task` with `role: "reviewer"` to select the effective `reviewer` Role.
100
100
  - Its task packet must state the read-only scope and exact `PASS` or findings contract. Include exact acceptance criteria and validation evidence.
101
- - The Reviewer must see exact candidate evidence from its working directory. Use `delegate_flow` for an isolated candidate, not an ordinary review from Main's unchanged checkout.
101
+ - The Reviewer must receive exact candidate evidence. An ordinary review from Main's unchanged checkout cannot inspect an isolated candidate.
102
102
  - Fix initial findings together. Validate repaired inputs once before focused re-review. Include the original findings and acceptance criteria, exact repaired-candidate evidence, and validation evidence.
103
103
  - Only `PASS` completes the loop. Surface and block on re-review findings or empty output. Retry empty output only when explicit caller policy requires one. A second empty result blocks. Do not add another round.
104
104
 
105
- Flow is separate. It owns exact review evidence, exact `PASS` approval, validation replay, one repair continuation, and no automatic retry.
106
-
107
- ## Flow
108
-
109
- Flow requires a clean Main worktree on an attached branch with a committed `HEAD`. Use it only for independent Git changes that can merge in any order.
110
-
111
- Keep work together or run it in order when a split divides an invariant or adds coordination. Do not split units with overlapping mutable ownership. Do not split units that overlap files, APIs, schemas, generated output, package metadata, lockfiles, or invariants.
112
-
113
- One Implementer launch must plausibly finish before the configured maximum runtime. Cohesion is not enough when work has several preservable, separately verifiable milestones. Split oversized dependent work into serial one-unit Flows after each milestone integrates. Units in one Flow stay independent and commuting.
114
-
115
- ```text
116
- delegate_flow({ units: [{ id, name, task, modelClass?, validation: [{ command, args }], review? }] })
117
- delegate_flow_continue({ guidance, modelClass? })
118
- ```
119
-
120
- A Flow has 1–8 units with unique non-empty IDs and allows one active Flow. It freezes the effective Implementer at start. It freezes the Reviewer only when a unit requests `review`.
121
-
122
- - Each unit gets one worktree. Implementers run in parallel. Flow handles units in declared order.
123
- - Flow runs each declared command with its arguments. That validation is authoritative for objective checks.
124
- - Without `review`, Flow fast-forwards the exact validated tip.
125
- - With `review`, the Reviewer receives the exact `{base, tip, patchPath}` packet and must return exactly `PASS` before the same integration path. Use `review` only for stated judgment that validation cannot decide.
126
- - Trust a successful Flow result. Do not re-read integrated files or repeat its validation merely to confirm it.
127
-
128
105
  A Role selects base tools, extensions, named Skills, instructions, and optional worktree isolation. Named Skills resolve from Main's effective Pi registry. Unavailable names warn and skip.
129
106
 
130
107
  Children disable ambient extension and Skill discovery. `tools: []` adds no base tools, but selected extension tools and caller tools still activate. `extensions: []` adds no Role extension bundle. `skills: []` adds no separately named Role Skills, but selected extension Skills still load.
131
108
 
132
- Parent-only delegation tools and `ask_question` are always excluded. Requested Role or caller tool names are checked after provider loading. Unavailable tools fail before the first model turn.
133
-
134
- An explicit unit `modelClass` overrides both frozen Roles. Without one, each Role uses its own `modelClass`, configured `pi-subagent/delegateTask` assignment, or declared default.
135
-
136
- One `delegate_flow_continue` can repair an Implementer, validation, or review block in the same worktree. Omitting its class keeps the unit's explicit class and frozen Role defaults. Supplying one replaces both Role defaults for that repair and its later Reviewer launch.
137
-
138
- A second block is terminal. Rebase and infrastructure failures are terminal.
139
-
140
- Flow has no dependency graph, saved recovery, automatic retry, aggregate review, or post-merge validation.
109
+ Main-only delegation and orchestration tools, plus `ask_question`, are always excluded. Requested Role or caller tool names are checked after provider loading. Unavailable tools fail before the first model turn.
141
110
 
142
111
  ## Config
143
112
 
@@ -151,7 +120,7 @@ pi-subagent owns `~/.pi/agent/config/pi-subagent/config.json`. It is optional. A
151
120
  | `timeout.idleMinutes` | Sets the idle timeout for a child. | Positive minutes; minutes × 60,000 ≤ 2,147,483,647 ms | `10` |
152
121
  | `timeout.maxMinutes` | Sets the maximum runtime for a child. | Positive minutes greater than `idleMinutes`; minutes × 60,000 ≤ 2,147,483,647 ms | `30` |
153
122
 
154
- `maxTokens` applies separately to every child. This includes `delegate_task` and each Flow Implementer, Reviewer, and repair launch. It is not a shared pool or per-call option. Set it only in this file.
123
+ `maxTokens` applies separately to every child. It is not a shared pool or per-call option. Set it only in this file.
155
124
 
156
125
  Pi adds each completed assistant response's `Usage.totalTokens` once. This matches the executor's aggregate `Usage`. At 80%, a Role receives one convergence warning.
157
126
 
@@ -161,13 +130,13 @@ Excess children wait FIFO without using a child timeout. A terminal response on
161
130
 
162
131
  ### Final response handoff
163
132
 
164
- Role launches reserve a response-only handoff at a continuing `maxTokens` crossing or the penultimate `maxTurns` turn. This includes `delegate_task` and every Implementer or Reviewer launch within `delegate_flow`.
133
+ Role launches reserve a response-only handoff at a continuing `maxTokens` crossing or the penultimate `maxTurns` turn. This includes `delegate_task` and Role launches made through the public API.
165
134
 
166
135
  With `maxTurns` set to 1, Pi disables tools at startup. The sole provider turn is the response-only handoff.
167
136
 
168
137
  Pi waits for the current turn's tools. It then disables all tools and requests a final report. A terminal boundary response gets no handoff.
169
138
 
170
- The fixed decision packet asks for Status (completed, blocked, or incomplete), one-sentence Outcome, up to three concrete Evidence facts, Blocker, one material Risk, and one Suggested next action. It is the default. Exact output required by the assigned task or Role takes precedence. The child returns only that output, such as a Flow Reviewer's exact `PASS` or caller-required structured output. The handoff stays within `maxTurns`, but it is the one allowed turn after a token crossing. Commits, validation, and retained-worktree facts from executor/Flow structured evidence remain authoritative; the model handoff supplies semantic context and a suggested next action.
139
+ The fixed decision packet asks for Status (completed, blocked, or incomplete), one-sentence Outcome, up to three concrete Evidence facts, Blocker, one material Risk, and one Suggested next action. It is the default. Exact output required by the assigned task or Role takes precedence. The child returns only that caller-required exact output, such as structured JSON or `PASS`. The handoff stays within `maxTurns`, but it is the one allowed turn after a token crossing. Structured executor and retained-worktree facts remain authoritative; the model handoff supplies semantic context and a suggested next action.
171
140
 
172
141
  A raw `createEphemeralSubagentExecutor` launch can enforce the extra-turn window. It cannot guarantee disabled tools or the final handoff. A timeout, provider failure, or child-process failure can also end a Role launch before handoff.
173
142
 
@@ -190,19 +159,17 @@ Role Markdown files live beside the config file. They require frontmatter and a
190
159
  | `skills` | Required YAML array of non-empty Skill names. |
191
160
  | body | Required Markdown system prompt after the frontmatter. |
192
161
 
193
- A Role's `modelClass` is a default. A call-level or Flow-unit class wins.
162
+ A Role's `modelClass` is a default. A call-level class wins.
194
163
 
195
164
  An unreadable or invalid Role fails loading fast. Duplicate Role names are rejected. A same-named user file overrides a built-in Role.
196
165
 
197
- The package always provides these built-in Roles. Their files leave `modelClass` unset, so they use the configured `pi-subagent/delegateTask` assignment or declared default unless a call or Flow unit overrides it:
166
+ The package always provides these built-in Roles. Their files leave `modelClass` unset, so they use the configured `pi-subagent/delegateTask` assignment or declared default unless a call overrides it:
198
167
 
199
168
  | Role | Purpose | Isolation/use |
200
169
  | --- | --- | --- |
201
170
  | `implementer` | Make and validate one focused change. | Requests a worktree; commits scoped work locally. Never pushes or opens a PR without permission. |
202
- | `reviewer` | Review supplied plans or files for correctness. | Read-only. For Flow review, reads the exact packet in the Unit Worktree; never edits or commits. |
203
- | `scout` | Map code and evidence for one bounded task. | Read-only and generic `delegate_task` only; never changes files. |
204
-
205
- Flow uses the effective Implementer and, only when requested, Reviewer. The Scout is not part of Flow.
171
+ | `reviewer` | Review supplied plans, files, or caller-prepared exact evidence. | Read-only; never edits or commits. |
172
+ | `scout` | Map code and evidence for one bounded task. | Read-only; never changes files. |
206
173
 
207
174
  ## API
208
175
 
@@ -216,6 +183,7 @@ The package root includes these main exports:
216
183
  | `createRoleLaunch` | function | Builds launch arguments from a resolved route. |
217
184
  | `createEphemeralSubagentExecutor` | function | Creates the bounded child-process executor. |
218
185
  | Worktree helpers | functions | Create, inspect, finalize, and report child worktrees. |
186
+ | `prepareExactReviewEvidence` | function | Create a bounded private base-to-tip patch with exact Git identity for caller-owned review. |
219
187
 
220
188
  The executor works only inside the active Pi process. It does not discover or start a standalone Node.js Pi installation.
221
189
 
@@ -227,6 +195,6 @@ See the [public Role and executor API](./docs/orchestration.md#public-role-and-e
227
195
 
228
196
  An explicitly selected extension is trusted, not sandboxed. Its tools, Skills, and executable behavior load together. Select fewer trusted extensions to reduce scope. pi-subagent does not guess or remove undocumented dependencies.
229
197
 
230
- Flow never force-deletes recoverable work. Failed or uncertain units, and cleanup refusals after integration, retain their worktree path or branch for recovery.
198
+ Worktree cleanup never force-deletes recoverable work. Retained and recovery payloads report the worktree path and branch.
231
199
 
232
- See [Flow mechanics and recovery](./docs/orchestration.md#delegate_flow) for retained-work recovery.
200
+ See the [public Role and executor API](./docs/orchestration.md#public-role-and-executor-api) for worktree and exact-review-evidence contracts.
@@ -0,0 +1,8 @@
1
+ export type GitResult = {
2
+ code: number;
3
+ stdout: string;
4
+ stderr: string;
5
+ };
6
+ export type GitRunner = (args: string[], cwd: string, signal?: AbortSignal) => Promise<GitResult>;
7
+ /** Run one ordinary Git command with bounded output; transport failures never throw. */
8
+ export declare const runGit: GitRunner;
@@ -0,0 +1,32 @@
1
+ import { spawnBounded } from "@henryqw/pi-process";
2
+ const GIT_TIMEOUT_MS = 30_000;
3
+ const GIT_OUTPUT_LIMIT_BYTES = 1 * 1024 * 1024;
4
+ const GIT_DIAGNOSTIC_LIMIT = 200;
5
+ /** Run one ordinary Git command with bounded output; transport failures never throw. */
6
+ export const runGit = async (args, cwd, signal) => {
7
+ try {
8
+ const result = await spawnBounded("git", ["--no-pager", ...args], {
9
+ cwd,
10
+ signal,
11
+ timeoutMs: GIT_TIMEOUT_MS,
12
+ stdoutLimitBytes: GIT_OUTPUT_LIMIT_BYTES,
13
+ stderrLimitBytes: GIT_OUTPUT_LIMIT_BYTES,
14
+ });
15
+ if (result.killed) {
16
+ return {
17
+ code: -1,
18
+ stdout: "",
19
+ stderr: result.stderr.trim().slice(0, GIT_DIAGNOSTIC_LIMIT) || "git was killed",
20
+ };
21
+ }
22
+ return { code: result.code, stdout: result.stdout, stderr: result.stderr };
23
+ }
24
+ catch (error) {
25
+ const reason = error instanceof Error ? error.message : String(error);
26
+ return {
27
+ code: -1,
28
+ stdout: "",
29
+ stderr: reason.slice(0, GIT_DIAGNOSTIC_LIMIT) || "git execution failed",
30
+ };
31
+ }
32
+ };
package/dist/index.d.ts CHANGED
@@ -4,8 +4,9 @@ export { DISPLAY_TEXT_CONTRACT, hasDisplayControlCharacters } from "./display-te
4
4
  export { addUsage, capEphemeralSubagentOutput, createEphemeralSubagentExecutor, DEFAULT_MAX_TURNS, EphemeralSubagentError, EXECUTION_BUDGET_ENV, formatDuration, type EphemeralSubagentActivityEvent, type EphemeralSubagentErrorCode, type EphemeralSubagentExecutionBudget, type EphemeralSubagentExecutor, type EphemeralSubagentExecutorOptions, type EphemeralSubagentResult, type EphemeralSubagentRunInput, type EphemeralSubagentTimeout, } from "./ephemeral.ts";
5
5
  export { createChildWorktree, finalizeChildWorktree, inspectIndexFlags, inspectWorktreeDirty, WorktreeSetupError, worktreeContextNote, type WorktreeDirtyInspection, type WorktreeInfo, type WorktreePayload, } from "./worktree.ts";
6
6
  export { prepareExactReviewEvidence, REVIEW_MAX_PATCH_BYTES, REVIEW_MAX_PATHS, type PreparedReviewEvidence, type PrepareExactReviewEvidenceInput, } from "./review-evidence.ts";
7
+ export declare const PI_ORCHESTRATOR_PROCESS_LEASE = "PI_ORCHESTRATOR_PROCESS_LEASE";
7
8
  export declare const ROLE_TOOL_POLICY_FLAG = "pi-subagent-role-tools";
8
- export declare const CHILD_EXCLUDED_TOOL_NAMES: readonly ["delegate_task", "delegate_flow", "delegate_flow_continue", "ask_question"];
9
+ export declare const CHILD_EXCLUDED_TOOL_NAMES: readonly ["delegate_task", "ask_question", "orchestrate_execute", "orchestrate_status", "orchestrate_resume", "orchestrate_abort"];
9
10
  export declare const CHILD_EXCLUDED_TOOLS: string;
10
11
  export declare const DELEGATE_TASK: {
11
12
  readonly id: "pi-subagent/delegateTask";
package/dist/index.js CHANGED
@@ -12,8 +12,16 @@ export { prepareExactReviewEvidence, REVIEW_MAX_PATCH_BYTES, REVIEW_MAX_PATHS, }
12
12
  const CODEX_ALIAS = /^openai-codex-(?:[2-9]|[1-9]\d+)$/;
13
13
  const MULTI_CODEX_EXTENSION = fileURLToPath(import.meta.resolve("@henryqw/pi-multi-codex/extensions/multi-codex.ts"));
14
14
  const ROLE_TOOLS_EXTENSION = fileURLToPath(new URL("../extensions/role-tools.ts", import.meta.url));
15
+ export const PI_ORCHESTRATOR_PROCESS_LEASE = "PI_ORCHESTRATOR_PROCESS_LEASE";
15
16
  export const ROLE_TOOL_POLICY_FLAG = "pi-subagent-role-tools";
16
- export const CHILD_EXCLUDED_TOOL_NAMES = ["delegate_task", "delegate_flow", "delegate_flow_continue", "ask_question"];
17
+ export const CHILD_EXCLUDED_TOOL_NAMES = [
18
+ "delegate_task",
19
+ "ask_question",
20
+ "orchestrate_execute",
21
+ "orchestrate_status",
22
+ "orchestrate_resume",
23
+ "orchestrate_abort",
24
+ ];
17
25
  export const CHILD_EXCLUDED_TOOLS = CHILD_EXCLUDED_TOOL_NAMES.join(",");
18
26
  const CHILD_IDENTITY_POLICY = "You are a delegated Pi Subagent, not Main. Execute the assigned Role and task directly. Main-only delegation rules do not apply. Recursive delegation is unavailable; do not seek or invoke delegation tools.";
19
27
  export const DELEGATE_TASK = {
@@ -13,5 +13,5 @@ export interface PreparedReviewEvidence {
13
13
  patchPath: string;
14
14
  cleanup: () => Promise<void>;
15
15
  }
16
- /** Prepares the one exact private patch Flow supplies to its Reviewer. */
16
+ /** Prepare one exact private patch for caller-owned review. */
17
17
  export declare function prepareExactReviewEvidence(request: PrepareExactReviewEvidenceInput, signal?: AbortSignal): Promise<PreparedReviewEvidence>;
@@ -1,28 +1,20 @@
1
- import { execFile, spawn } from "node:child_process";
1
+ import { spawn } from "node:child_process";
2
2
  import fs from "node:fs/promises";
3
3
  import { tmpdir } from "node:os";
4
4
  import { isAbsolute, join } from "node:path";
5
5
  import { inspectIndexFlags } from "./worktree.js";
6
+ import { runGit as runCapturedGit } from "./git-process.js";
6
7
  const GIT_TIMEOUT_MS = 30_000;
7
8
  export const REVIEW_MAX_PATHS = 1_000;
8
9
  export const REVIEW_MAX_PATCH_BYTES = 512 * 1024;
9
10
  const STDERR_LIMIT = 200;
10
- const git = (args, cwd, signal) => new Promise((resolve) => {
11
- execFile("git", ["--no-pager", ...args], { cwd, signal, timeout: GIT_TIMEOUT_MS }, (error, stdout, stderr) => {
12
- resolve({
13
- code: error ? (typeof error.code === "number" ? error.code : -1) : 0,
14
- stdout: String(stdout),
15
- stderr: String(stderr).slice(0, STDERR_LIMIT),
16
- });
17
- });
18
- });
19
11
  function failure(args, result) {
20
- const detail = result.stderr.trim();
12
+ const detail = result.stderr.slice(0, STDERR_LIMIT).trim();
21
13
  return new Error(`git ${args.join(" ")} failed with exit ${result.code}${detail ? `: ${detail}` : ""}`);
22
14
  }
23
15
  async function runGit(args, cwd, signal) {
24
16
  signal?.throwIfAborted();
25
- const result = await git(args, cwd, signal);
17
+ const result = await runCapturedGit(args, cwd, signal);
26
18
  signal?.throwIfAborted();
27
19
  if (result.code !== 0)
28
20
  throw failure(args, result);
@@ -126,7 +118,7 @@ async function assertCleanRegisteredWorktree(worktree, tip, signal) {
126
118
  if (await runGit(["status", "--porcelain=v1", "--untracked-files=all", "--ignore-submodules=none"], worktree, signal)) {
127
119
  throw new Error("Review evidence worktree is not clean.");
128
120
  }
129
- const flags = await inspectIndexFlags(worktree, git, signal);
121
+ const flags = await inspectIndexFlags(worktree, runCapturedGit, signal);
130
122
  if (flags.failure)
131
123
  throw new Error(`Review evidence index inspection failed: ${flags.failure}`);
132
124
  if (flags.hidden)
@@ -200,7 +192,7 @@ async function makeEvidenceDirectory() {
200
192
  cleanup: () => cleanup ??= fs.rm(directory, { recursive: true, force: true }),
201
193
  };
202
194
  }
203
- /** Prepares the one exact private patch Flow supplies to its Reviewer. */
195
+ /** Prepare one exact private patch for caller-owned review. */
204
196
  export async function prepareExactReviewEvidence(request, signal) {
205
197
  const requestedWorktree = requestText(request.worktree, "review worktree");
206
198
  const worktree = await fs.realpath(requestedWorktree);
@@ -1,3 +1,4 @@
1
+ import { type GitRunner } from "./git-process.ts";
1
2
  export interface WorktreeInfo {
2
3
  path: string;
3
4
  cwd: string;
@@ -23,11 +24,6 @@ export type WorktreePayload = {
23
24
  commits?: number;
24
25
  dirty?: boolean;
25
26
  };
26
- export type GitRunner = (args: string[], cwd: string, signal?: AbortSignal) => Promise<{
27
- code: number;
28
- stdout: string;
29
- stderr: string;
30
- }>;
31
27
  export declare class WorktreeSetupError extends Error {
32
28
  name: string;
33
29
  readonly worktree: WorktreeInfo;
@@ -40,7 +36,7 @@ export declare class WorktreeSetupError extends Error {
40
36
  * repository (unwritable path, stale branch, git lock) throw so an isolated
41
37
  * role never silently loses its isolation.
42
38
  */
43
- export declare function createChildWorktree(cwd: string, childId: string, run?: GitRunner, signal?: AbortSignal): Promise<WorktreeInfo | undefined>;
39
+ export declare function createChildWorktree(cwd: string, childId: string, run?: GitRunner, signal?: AbortSignal, onPrepared?: (worktree: WorktreeInfo) => Promise<void>): Promise<WorktreeInfo | undefined>;
44
40
  export interface WorktreeDirtyInspection {
45
41
  dirty: boolean;
46
42
  failure?: string;
package/dist/worktree.js CHANGED
@@ -1,9 +1,8 @@
1
- import { execFile } from "node:child_process";
2
1
  import { createHash } from "node:crypto";
3
2
  import { existsSync, lstatSync } from "node:fs";
4
3
  import { appendFile, mkdir, readFile } from "node:fs/promises";
5
4
  import { dirname, isAbsolute, join, resolve } from "node:path";
6
- const GIT_TIMEOUT_MS = 30_000;
5
+ import { runGit } from "./git-process.js";
7
6
  const WORKTREES_DIRNAME = ".worktrees";
8
7
  const BRANCH_NAMESPACE = "pi-subagent";
9
8
  export class WorktreeSetupError extends Error {
@@ -14,16 +13,6 @@ export class WorktreeSetupError extends Error {
14
13
  this.worktree = worktree;
15
14
  }
16
15
  }
17
- /** Runs git, capturing output; never throws on non-zero exit or spawn failure. */
18
- const runGit = (args, cwd, signal) => new Promise((resolve) => {
19
- execFile("git", args, { cwd, timeout: GIT_TIMEOUT_MS, signal }, (error, stdout, stderr) => {
20
- resolve({
21
- code: error ? (typeof error.code === "number" ? error.code : -1) : 0,
22
- stdout: String(stdout),
23
- stderr: String(stderr),
24
- });
25
- });
26
- });
27
16
  const sanitizeShortId = (childId) => createHash("sha256").update(childId).digest("hex").slice(0, 24);
28
17
  const stripGitLineEnd = (value) => value.replace(/\r?\n$/, "");
29
18
  function hasRepositoryMarker(cwd) {
@@ -69,12 +58,17 @@ async function ensureLocalExclude(gitDir) {
69
58
  * repository (unwritable path, stale branch, git lock) throw so an isolated
70
59
  * role never silently loses its isolation.
71
60
  */
72
- export async function createChildWorktree(cwd, childId, run = runGit, signal) {
61
+ export async function createChildWorktree(cwd, childId, run = runGit, signal, onPrepared) {
73
62
  const root = await run(["rev-parse", "--show-toplevel"], cwd, signal);
63
+ signal?.throwIfAborted();
64
+ if (root.code === -1)
65
+ throw new Error(`git rev-parse failed (${root.stderr.trim().slice(0, 200)})`);
74
66
  if (root.code !== 0) {
75
67
  signal?.throwIfAborted();
76
68
  const repository = await run(["-c", "safe.directory=*", "rev-parse", "--show-toplevel"], cwd, signal);
77
69
  signal?.throwIfAborted();
70
+ if (repository.code === -1)
71
+ throw new Error(`git rev-parse failed (${repository.stderr.trim().slice(0, 200)})`);
78
72
  if (repository.code !== 0 && !hasRepositoryMarker(cwd) && !process.env.GIT_DIR && !process.env.GIT_WORK_TREE)
79
73
  return undefined;
80
74
  throw new Error(`git rev-parse failed (${root.stderr.trim().slice(0, 200)})`); // dubious ownership, timeout, …
@@ -87,6 +81,9 @@ export async function createChildWorktree(cwd, childId, run = runGit, signal) {
87
81
  throw new Error(`git rev-parse --show-prefix failed (${prefix.stderr.trim().slice(0, 200)})`);
88
82
  const relativeCwd = stripGitLineEnd(prefix.stdout);
89
83
  const base = await run(["rev-parse", "HEAD"], repoRoot, signal);
84
+ signal?.throwIfAborted();
85
+ if (base.code === -1)
86
+ throw new Error(`git rev-parse HEAD failed (${base.stderr.trim().slice(0, 200)})`);
90
87
  if (base.code !== 0) {
91
88
  signal?.throwIfAborted();
92
89
  const head = await run(["symbolic-ref", "--quiet", "HEAD"], repoRoot, signal);
@@ -134,6 +131,10 @@ export async function createChildWorktree(cwd, childId, run = runGit, signal) {
134
131
  const name = `subagent-${sanitizeShortId(childId)}`;
135
132
  const branch = `${BRANCH_NAMESPACE}/${name}`;
136
133
  const path = join(worktreesRoot, name);
134
+ const worktree = { path, cwd: join(path, relativeCwd), branch, repoRoot: stableRepoRoot, baseCommit };
135
+ if (onPrepared)
136
+ await onPrepared(worktree);
137
+ signal?.throwIfAborted();
137
138
  try {
138
139
  await mkdir(worktreesRoot, { recursive: true });
139
140
  }
@@ -142,7 +143,6 @@ export async function createChildWorktree(cwd, childId, run = runGit, signal) {
142
143
  }
143
144
  await ensureLocalExclude(gitDir);
144
145
  signal?.throwIfAborted();
145
- const worktree = { path, cwd: join(path, relativeCwd), branch, repoRoot: stableRepoRoot, baseCommit };
146
146
  const added = await run(["worktree", "add", path, "-b", branch, baseCommit], repoRoot, signal);
147
147
  if (added.code !== 0) {
148
148
  throw new WorktreeSetupError(`git worktree add failed after attempting path=${JSON.stringify(path)} branch=${JSON.stringify(branch)} base=${baseCommit}: ${added.stderr.trim().slice(0, 200)}`, worktree);
@@ -18,8 +18,8 @@ An explicit call `modelClass` selects a task-model route. Without one, an option
18
18
 
19
19
  ## Scope boundary
20
20
 
21
- `delegate_flow` is a fixed package-owned Git workflow, documented in [ADR 002](./002-package-owned-delegate-flow-orchestration.md). It reuses the prepared-child runner but is not a general executor workflow primitive: it has its own fixed unit, worktree, validation, review, integration, and cleanup contract. `delegate_task` and library callers remain generic.
21
+ `delegate_task` and library callers remain generic. The package does not own checked implementation orchestration.
22
22
 
23
23
  ## Consequences
24
24
 
25
- The executor remains a stable mechanism while generic callers own semantic protocols, shared workspace/state, retry decisions, and bounds. The package-owned Flow removes only its repeated deterministic Git mechanics; it does not turn the executor into a workflow language.
25
+ The executor remains a stable mechanism while callers own semantic protocols, shared workspace and state, validation, review, retry decisions, integration, cleanup, and bounds.
@@ -10,9 +10,7 @@ caller-owned Model Task declaration, cwd, signal ────────┤
10
10
  active-Pi ephemeral executor
11
11
  ```
12
12
 
13
- Main plans and orchestrates. `delegate_task` owns its flat single/parallel/chain policy, while the public executor runs one prepared delegation. Downstream packages compose their own workflows with ordinary JavaScript and own semantic protocols, shared workspace/state, retry decisions, and bounds. There is no recursive workflow AST.
14
-
15
- `delegate_flow` is the exception: it is a fixed package-owned Git workflow, not an executor primitive or general workflow language. It uses the effective `implementer` Role and, only for explicit judgment review, the effective `reviewer` Role through the same prepared-child runner; its contract is below.
13
+ Main plans and orchestrates. `delegate_task` remains lightweight generic delegation with flat single, parallel, and chain modes. The public executor runs one prepared delegation. Downstream packages compose their own workflows with ordinary JavaScript and own semantic protocols, shared workspace and state, checks, integration, retry decisions, and bounds. There is no recursive workflow AST.
16
14
 
17
15
  ## Frozen `delegate_task` contract
18
16
 
@@ -81,33 +79,6 @@ Any foreground entry failure makes the tool call throw. Parallel mode first sett
81
79
 
82
80
  All Main-visible text for one tool call shares one aggregate 50 KiB UTF-8 transport cap, including child output, sibling failures, and worktree/recovery evidence. Parallel execution does not multiply the cap by its entry count. Truncation is explicit; internal bookkeeping is not made visible by bypassing the cap.
83
81
 
84
- ## `delegate_flow`
85
-
86
- `delegate_flow({ units })` accepts 1–8 units with unique non-empty `id`, a required Main-supplied short `name` (about five words and fewer than 30 characters, without C0/C1 control characters), a non-empty `task`, one or more direct `{command, args}` validation commands, optional `modelClass`, and optional non-empty `review` text. `delegate_flow_continue({ guidance, modelClass? })` is available only for the one blocked unit of the active Flow.
87
-
88
- A Flow is memory-only and permits one active Flow. At start it always resolves/freezes the effective `implementer` Role, including a same-named user override. It resolves/freezes the effective `reviewer` only if at least one requested unit declares `review`. An explicit unit class overrides both frozen Roles. Without one, the Implementer and Reviewer each use their own optional Role default; a Role without one uses pi-subagent's local `pi-subagent/delegateTask` assignment or declared `fast` default. The selected class resolves through its existing `pi-task-models` profile model-and-thinking route. It requires clean committed Git Main and creates every Unit Worktree before launching work; setup failure launches no Implementer. Each unit gets exactly one worktree and one Implementer. Implementers run in parallel and all settle. Flow then processes units in declared order:
89
-
90
- ```text
91
- Implementers (parallel, one Unit Worktree each)
92
- │ all settle
93
- v
94
- for each declared unit:
95
- rebase in its Unit Worktree when earlier units advanced Main
96
- inspect committed state; run declared validation (objective authority)
97
- ├─ no review: git merge --ff-only <exact validated tip>
98
- └─ review: Reviewer receives exact {base, tip, patchPath}
99
- exact PASS → git merge --ff-only <full reviewed OID>
100
- git worktree remove; git branch -d
101
- ```
102
-
103
- Flow derives identity from Git, not child output. Add `review` only for an explicit judgment criterion that automated validation cannot establish; it is not a second generic verification pass. The Reviewer reads the exact patch as authoritative and may use the same worktree only for referenced context. A full-OID fast-forward is the only integration path. Cleanup is non-forced; after a successful integration, cleanup refusal returns `completed` with a retained path/branch warning.
104
-
105
- If rebase drops all unit commits, `base === tip` is a no-op: Flow validates current state, skips Reviewer and merge, then cleans up ordinarily. Implementer failure, dirty or missing committed work, validation failure, or reviewer findings block the first affected declared unit. `delegate_flow_continue({ guidance, modelClass? })` reruns the Flow's frozen Implementer Role in that same worktree once, then repeats derivation, validation, and conditional review with fresh exact evidence. An omitted continuation class retains a supplied Unit class; when no Unit class was supplied, each frozen Role uses its own default. A supplied continuation class replaces both Role defaults for that repair and any subsequent Reviewer launch. A second block is terminal. A failed rebase is aborted and terminates as an infrastructure failure with Git diagnostics; other infrastructure failures are terminal. A reported fast-forward failure completes with its diagnostic as a warning only when Main is clean at the exact integrated tip; otherwise it is terminal. Terminal outcomes retain worktrees for Main to reslice. Earlier integrated units are never rolled back.
106
-
107
- Flow has no dependency graph, saved state, automatic retry, aggregate review, or post-merge validation. Use it only for commuting changes; combine or sequence units that overlap files, APIs, schemas, generated output, package metadata, lockfiles, or invariants.
108
-
109
- `delegate_task` remains generic: its optional worktree isolation, non-Git behavior, and direct plan/file review are unchanged. The built-in `scout` is available only through generic `delegate_task`; Flow uses package-shipped Implementer and conditional Reviewer Roles as defaults while retaining same-named user Role overrides.
110
-
111
82
  ## Per-delegation resources and isolation
112
83
 
113
84
  `delegate_task` first preflights every requested Role name, so an initially unknown Role starts no sibling. Then, after receiving an executor permit, every single entry, parallel sibling, and chain step independently:
@@ -119,7 +90,7 @@ Flow has no dependency graph, saved state, automatic retry, aggregate review, or
119
90
 
120
91
  Separate deterministic identities produce separate worktree paths and branches. Parallel siblings cannot collide, and a chain does not base one step's worktree on the preceding step's branch. `{previous}` passes text only. There is no implicit shared worktree or hidden workflow state.
121
92
 
122
- A worktree starts from Main's current `HEAD`. Clean worktrees with no child commits are pruned; committed, dirty, switched, unmeasurable, or otherwise recoverable work is preserved and reported. For `delegate_task`, non-git directories and repositories with an unborn `HEAD` use Main's working directory. Git submodules reject worktree isolation, and setup failure in a real repository throws rather than silently sharing Main's checkout. `delegate_flow` requires a Git repository with a committed `HEAD`.
93
+ A worktree starts from Main's current `HEAD`. Clean worktrees with no child commits are pruned; committed, dirty, switched, unmeasurable, or otherwise recoverable work is preserved and reported. For `delegate_task`, non-git directories and repositories with an unborn `HEAD` use Main's working directory. Git submodules reject worktree isolation, and setup failure in a real repository throws rather than silently sharing Main's checkout.
123
94
 
124
95
  If steps must share files, make that an explicit caller decision: use an intentionally shared workspace, merge preserved child commits, or pass state through a caller-owned store. Do not rely on chain order to imply filesystem sharing.
125
96
 
@@ -138,19 +109,19 @@ A Role file requires:
138
109
 
139
110
  Every launch installs the Role tool policy. At launch, a package caller may add `tools`, `extensions`, and `env`; caller tools are unioned into the Role base list and loaded extension tools activate in every case. Caller `env` adds to or overrides the active Pi process environment for the child.
140
111
 
141
- Children start with ambient extension and Skill discovery disabled. Only explicit Role/caller extensions, explicitly resolved Skill paths, resources supplied by those extension packages, and any required internal tool-policy or Codex adapter load. Loaded extension tools activate even when the Role base list is empty. Child-inappropriate parent tools are always excluded: `delegate_task`, `delegate_flow`, `delegate_flow_continue`, and `ask_question`. Explicit Role/caller tool names are verified against the final filtered active child registry after every explicit provider extension completes `session_start`; unavailable names fail before the first model turn and identify the missing names with provider-extension guidance.
112
+ Children start with ambient extension and Skill discovery disabled. Only explicit Role or caller extensions, resolved Skill paths, extension package resources, and required internal tool-policy or Codex adapters load. Loaded extension tools activate even when the Role base list is empty. Child-inappropriate parent tools are always excluded: `delegate_task`, `orchestrate_execute`, `orchestrate_status`, `orchestrate_resume`, `orchestrate_abort`, and `ask_question`. Explicit Role or caller tool names are verified against the final filtered registry after each provider extension completes `session_start`. Unavailable names fail before the first model turn and identify the missing names with provider guidance.
142
113
 
143
114
  Role Skill names resolve through Main's effective Pi Skill registry at launch. Missing names are returned in `ResolvedRoleLaunch.missingSkills`; `delegate_task` warns and skips them. Library callers must surface that warning themselves. Missing Skills do not block launch.
144
115
 
145
116
  ### Role final-turn handoff
146
117
 
147
- Role launches made by `createRoleLaunch` reserve a response-only handoff at either boundary. They use the penultimate `maxTurns` turn or a continuing `maxTokens` crossing. This includes `delegate_task` and every Flow Implementer, Reviewer, and repair launch.
118
+ Role launches made by `createRoleLaunch` reserve a response-only handoff at either boundary. They use the penultimate `maxTurns` turn or a continuing `maxTokens` crossing. This includes `delegate_task` and library Role launches made with the public launch API.
148
119
 
149
120
  With `maxTurns` set to 1, Pi disables tools during `session_start`. The sole provider turn is the response-only handoff.
150
121
 
151
122
  After a continuing boundary turn, Pi completes its tools. It then disables every active tool and queues one structured final handoff. A terminal boundary response succeeds without a handoff. If the child continues after the token handoff, the executor rejects with `token_limit`.
152
123
 
153
- The handoff requests a fixed Markdown decision packet with Status (`completed`, `blocked`, or `incomplete`), one-sentence Outcome, up to three concrete Evidence facts, Blocker, one material Risk, and one Suggested next action. It is the default. Exact output required by the assigned task or Role takes precedence, and the child replies only with it. This preserves a Flow Reviewer's exact `PASS` and caller-required structured output. The handoff remains inside `maxTurns`, but it is the one permitted response after crossing `maxTokens`. A timeout, provider failure, or child-process failure can end a Role launch before handoff. Commits, validation, and retained-worktree facts from executor/Flow structured evidence remain authoritative; the model handoff supplies semantic context and a suggested next action.
124
+ The handoff requests a fixed Markdown decision packet with Status (`completed`, `blocked`, or `incomplete`), one-sentence Outcome, up to three concrete Evidence facts, Blocker, one material Risk, and one Suggested next action. It is the default. Exact output required by the assigned task or Role takes precedence, and the child replies only with it. This preserves caller-required exact structured output. The handoff remains inside `maxTurns`, but it is the one permitted response after crossing `maxTokens`. A timeout, provider failure, or child-process failure can end a Role launch before handoff. Structured executor and retained-worktree facts remain authoritative; the model handoff supplies semantic context and a suggested next action.
154
125
 
155
126
  A raw `createEphemeralSubagentExecutor` launch enforces the token extra-turn window. It does not guarantee disabled tools or the handoff message. Only Role launches install that policy.
156
127
 
@@ -165,7 +136,8 @@ The package root exports the following mechanism-level APIs:
165
136
  | `resolveRoleLaunch(pi, ctx, input)` | Resolve a caller-owned Model Task route, applying call-level then Role `modelClass` precedence, and produce `ResolvedRoleLaunch`. |
166
137
  | `createRoleLaunch(pi, ctx, input)` | Produce the same launch from a caller-supplied resolved route. |
167
138
  | `createEphemeralSubagentExecutor(options)` | Queue and run one prepared no-session child per `run`. |
168
- | `createChildWorktree` / `finalizeChildWorktree` | Optional caller-managed worktree lifecycle. |
139
+ | `createChildWorktree` / `finalizeChildWorktree` | Optional caller-managed worktree lifecycle; `createChildWorktree` can prepare exact metadata before allocation. |
140
+ | `prepareExactReviewEvidence` | Validate Git identity and create a bounded private base-to-tip patch with exact `{base, tip, patchPath}` evidence. |
169
141
 
170
142
  `finalizeChildWorktree` returns the breaking `WorktreePayload` lifecycle union:
171
143
 
@@ -425,11 +397,11 @@ The package ships three working built-in Roles, validated by the same parser as
425
397
  | Built-in | Behavior |
426
398
  | --- | --- |
427
399
  | `implementer` | Focused implementation requesting `isolation: worktree`; commits scoped changes locally, never pushes or opens PRs without authorization. Non-Git or unborn-`HEAD` contexts may use Main's cwd. |
428
- | `reviewer` | Read-only correctness review of supplied plans/files, or—when a Flow unit declares `review`—Flow's exact `{base, tip, patchPath}` packet in its Unit Worktree; never edits or commits. |
400
+ | `reviewer` | Read-only correctness review of supplied plans, files, or caller-prepared exact evidence; never edits or commits. |
429
401
  | `scout` | Read-only code and evidence mapping for one bounded task; never changes files. |
430
402
 
431
403
  A same-named Markdown file in `config/pi-subagent/` explicitly overrides the built-in default.
432
404
 
433
- The bundled [`pi-subagent-delegated-development`](../skills/pi-subagent-delegated-development/SKILL.md) Skill is Main-side planner/orchestrator policy only. `delegate_flow` owns its fixed Git mechanics and objective validation authority; the Skill defines no runtime code or configuration. `delegate_task` remains the generic flat single/parallel/chain mechanism.
405
+ `delegate_task` remains the generic flat single, parallel, and chain mechanism. Library callers own any richer protocol.
434
406
 
435
- See [ADR 001](./adr/001-composable-ephemeral-execution.md) for the executor boundary and [ADR 002](./adr/002-package-owned-delegate-flow-orchestration.md) for Flow.
407
+ See [ADR 001](./adr/001-composable-ephemeral-execution.md) for the executor boundary.