@henryqw/pi-subagent 15.1.3 → 16.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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,6 +19,7 @@ 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. |
22
24
 
23
25
  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.
@@ -39,9 +41,6 @@ A separate child returns a bounded report to Main. It creates no saved Pi sessio
39
41
  | Surface | Type | Purpose |
40
42
  | --- | --- | --- |
41
43
  | `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
44
 
46
45
  Pi's built-in tool block shows each call and result.
47
46
 
@@ -68,7 +67,7 @@ Parallel tasks start together, settle together, and report in input order. Chain
68
67
 
69
68
  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
69
 
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.
70
+ 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
71
 
73
72
  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
73
 
@@ -76,66 +75,37 @@ Each entry resolves its own Role, resources, route, and optional isolation. A Ro
76
75
 
77
76
  See the [orchestration guide](./docs/orchestration.md) for full delegation, transport, isolation, and UI behavior.
78
77
 
79
- ### Skills
78
+ ### Delegation guidance
80
79
 
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.
80
+ `delegate_task` is generic delegation, not checked implementation orchestration. Main or a consuming package owns validation, review, integration, recovery, and durable state.
82
81
 
83
82
  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
83
 
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.
84
+ 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
85
 
87
86
  Before delegating:
88
87
 
88
+ - Keep trivial, single-owner, mechanically verifiable edits in Main.
89
+ - For literal UI or copy defects, search the exact text first. Read only its producer and nearby assertions unless ownership remains unclear.
89
90
  - Find concrete outcomes that can ship on their own.
90
91
  - Split only those outcomes. Give each one owner and a focused check.
91
92
  - Run independent work in parallel.
92
93
  - Prefer parallel delegation when at least two outcomes are independent.
93
- - For naturally multi-part work, roughly three to five useful units can help.
94
- - This is a guide, not a quota. Never create units just to reach it.
94
+ - Use only as many units as independent outcomes require. Never create units to reach a count.
95
95
 
96
96
  Its ordinary review loop is optional. Use it only when the caller or repository policy explicitly requires judgment review.
97
97
 
98
98
  - Call `delegate_task` with `role: "reviewer"` to select the effective `reviewer` Role.
99
99
  - Its task packet must state the read-only scope and exact `PASS` or findings contract. Include exact acceptance criteria and validation evidence.
100
- - 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.
100
+ - The Reviewer must receive exact candidate evidence. An ordinary review from Main's unchanged checkout cannot inspect an isolated candidate.
101
101
  - 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.
102
102
  - 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.
103
103
 
104
- Flow is separate. It owns exact review evidence, exact `PASS` approval, validation replay, one repair continuation, and no automatic retry.
105
-
106
- ## Flow
107
-
108
- 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.
109
-
110
- 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.
111
-
112
- 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.
113
-
114
- ```text
115
- delegate_flow({ units: [{ id, name, task, modelClass?, validation: [{ command, args }], review? }] })
116
- delegate_flow_continue({ guidance, modelClass? })
117
- ```
118
-
119
- 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`.
120
-
121
- - Each unit gets one worktree. Implementers run in parallel. Flow handles units in declared order.
122
- - Flow runs each declared command with its arguments. That validation is authoritative for objective checks.
123
- - Without `review`, Flow fast-forwards the exact validated tip.
124
- - 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.
125
-
126
104
  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.
127
105
 
128
106
  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.
129
107
 
130
- 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.
131
-
132
- An explicit unit `modelClass` overrides both frozen Roles. Without one, each Role uses its own `modelClass`, configured `pi-subagent/delegateTask` assignment, or declared default.
133
-
134
- 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.
135
-
136
- A second block is terminal. Rebase and infrastructure failures are terminal.
137
-
138
- Flow has no dependency graph, saved recovery, automatic retry, aggregate review, or post-merge validation.
108
+ 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.
139
109
 
140
110
  ## Config
141
111
 
@@ -149,7 +119,7 @@ pi-subagent owns `~/.pi/agent/config/pi-subagent/config.json`. It is optional. A
149
119
  | `timeout.idleMinutes` | Sets the idle timeout for a child. | Positive minutes; minutes × 60,000 ≤ 2,147,483,647 ms | `10` |
150
120
  | `timeout.maxMinutes` | Sets the maximum runtime for a child. | Positive minutes greater than `idleMinutes`; minutes × 60,000 ≤ 2,147,483,647 ms | `30` |
151
121
 
152
- `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.
122
+ `maxTokens` applies separately to every child. It is not a shared pool or per-call option. Set it only in this file.
153
123
 
154
124
  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.
155
125
 
@@ -159,13 +129,13 @@ Excess children wait FIFO without using a child timeout. A terminal response on
159
129
 
160
130
  ### Final response handoff
161
131
 
162
- 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`.
132
+ 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.
163
133
 
164
134
  With `maxTurns` set to 1, Pi disables tools at startup. The sole provider turn is the response-only handoff.
165
135
 
166
136
  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.
167
137
 
168
- 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.
138
+ 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.
169
139
 
170
140
  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.
171
141
 
@@ -188,19 +158,17 @@ Role Markdown files live beside the config file. They require frontmatter and a
188
158
  | `skills` | Required YAML array of non-empty Skill names. |
189
159
  | body | Required Markdown system prompt after the frontmatter. |
190
160
 
191
- A Role's `modelClass` is a default. A call-level or Flow-unit class wins.
161
+ A Role's `modelClass` is a default. A call-level class wins.
192
162
 
193
163
  An unreadable or invalid Role fails loading fast. Duplicate Role names are rejected. A same-named user file overrides a built-in Role.
194
164
 
195
- 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:
165
+ 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:
196
166
 
197
167
  | Role | Purpose | Isolation/use |
198
168
  | --- | --- | --- |
199
169
  | `implementer` | Make and validate one focused change. | Requests a worktree; commits scoped work locally. Never pushes or opens a PR without permission. |
200
- | `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. |
201
- | `scout` | Map code and evidence for one bounded task. | Read-only and generic `delegate_task` only; never changes files. |
202
-
203
- Flow uses the effective Implementer and, only when requested, Reviewer. The Scout is not part of Flow.
170
+ | `reviewer` | Review supplied plans, files, or caller-prepared exact evidence. | Read-only; never edits or commits. |
171
+ | `scout` | Map code and evidence for one bounded task. | Read-only; never changes files. |
204
172
 
205
173
  ## API
206
174
 
@@ -214,6 +182,7 @@ The package root includes these main exports:
214
182
  | `createRoleLaunch` | function | Builds launch arguments from a resolved route. |
215
183
  | `createEphemeralSubagentExecutor` | function | Creates the bounded child-process executor. |
216
184
  | Worktree helpers | functions | Create, inspect, finalize, and report child worktrees. |
185
+ | `prepareExactReviewEvidence` | function | Create a bounded private base-to-tip patch with exact Git identity for caller-owned review. |
217
186
 
218
187
  The executor works only inside the active Pi process. It does not discover or start a standalone Node.js Pi installation.
219
188
 
@@ -225,6 +194,6 @@ See the [public Role and executor API](./docs/orchestration.md#public-role-and-e
225
194
 
226
195
  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.
227
196
 
228
- Flow never force-deletes recoverable work. Failed or uncertain units, and cleanup refusals after integration, retain their worktree path or branch for recovery.
197
+ Worktree cleanup never force-deletes recoverable work. Retained and recovery payloads report the worktree path and branch.
229
198
 
230
- See [Flow mechanics and recovery](./docs/orchestration.md#delegate_flow) for retained-work recovery.
199
+ See the [public Role and executor API](./docs/orchestration.md#public-role-and-executor-api) for worktree and exact-review-evidence contracts.
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>;
@@ -200,7 +200,7 @@ async function makeEvidenceDirectory() {
200
200
  cleanup: () => cleanup ??= fs.rm(directory, { recursive: true, force: true }),
201
201
  };
202
202
  }
203
- /** Prepares the one exact private patch Flow supplies to its Reviewer. */
203
+ /** Prepare one exact private patch for caller-owned review. */
204
204
  export async function prepareExactReviewEvidence(request, signal) {
205
205
  const requestedWorktree = requestText(request.worktree, "review worktree");
206
206
  const worktree = await fs.realpath(requestedWorktree);
@@ -40,7 +40,7 @@ export declare class WorktreeSetupError extends Error {
40
40
  * repository (unwritable path, stale branch, git lock) throw so an isolated
41
41
  * role never silently loses its isolation.
42
42
  */
43
- export declare function createChildWorktree(cwd: string, childId: string, run?: GitRunner, signal?: AbortSignal): Promise<WorktreeInfo | undefined>;
43
+ export declare function createChildWorktree(cwd: string, childId: string, run?: GitRunner, signal?: AbortSignal, onPrepared?: (worktree: WorktreeInfo) => Promise<void>): Promise<WorktreeInfo | undefined>;
44
44
  export interface WorktreeDirtyInspection {
45
45
  dirty: boolean;
46
46
  failure?: string;
package/dist/worktree.js CHANGED
@@ -69,7 +69,7 @@ async function ensureLocalExclude(gitDir) {
69
69
  * repository (unwritable path, stale branch, git lock) throw so an isolated
70
70
  * role never silently loses its isolation.
71
71
  */
72
- export async function createChildWorktree(cwd, childId, run = runGit, signal) {
72
+ export async function createChildWorktree(cwd, childId, run = runGit, signal, onPrepared) {
73
73
  const root = await run(["rev-parse", "--show-toplevel"], cwd, signal);
74
74
  if (root.code !== 0) {
75
75
  signal?.throwIfAborted();
@@ -134,6 +134,10 @@ export async function createChildWorktree(cwd, childId, run = runGit, signal) {
134
134
  const name = `subagent-${sanitizeShortId(childId)}`;
135
135
  const branch = `${BRANCH_NAMESPACE}/${name}`;
136
136
  const path = join(worktreesRoot, name);
137
+ const worktree = { path, cwd: join(path, relativeCwd), branch, repoRoot: stableRepoRoot, baseCommit };
138
+ if (onPrepared)
139
+ await onPrepared(worktree);
140
+ signal?.throwIfAborted();
137
141
  try {
138
142
  await mkdir(worktreesRoot, { recursive: true });
139
143
  }
@@ -142,7 +146,6 @@ export async function createChildWorktree(cwd, childId, run = runGit, signal) {
142
146
  }
143
147
  await ensureLocalExclude(gitDir);
144
148
  signal?.throwIfAborted();
145
- const worktree = { path, cwd: join(path, relativeCwd), branch, repoRoot: stableRepoRoot, baseCommit };
146
149
  const added = await run(["worktree", "add", path, "-b", branch, baseCommit], repoRoot, signal);
147
150
  if (added.code !== 0) {
148
151
  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. 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. This generic fallback remains unchanged: the bundled delegated-development Skill separately refuses to begin without a committed Git `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.
@@ -14,12 +14,12 @@ skills: []
14
14
  isolation: worktree
15
15
  ---
16
16
 
17
- Implement the bounded outcome, not a preassigned file list. Work in the assigned cwd. Read applicable repository instructions and domain context first; inspect the relevant flow, callers, and tests before editing. Preserve unrelated work. Fix the root cause with the smallest complete diff, reusing existing patterns and dependencies. Do not add speculative work. Stop when the outcome is complete or blocked.
17
+ Implement the bounded outcome, not a preassigned file list. Work in the assigned cwd. Read applicable repository instructions and domain context; inspect the relevant flow, callers, and tests before editing. Preserve unrelated work. Fix the root cause with the smallest complete diff using existing patterns and dependencies. Add no speculative work. Stop when complete or blocked.
18
18
 
19
- For ordinary delegation, run focused validation needed to establish correctness. For Flow, the declared validation gate is authoritative: run only narrow development checks while implementing and do not duplicate that final gate. Before reporting ordinary or Flow completion, remove only task-created non-deliverable temporary, generated, or ignored artifacts. Preserve required deliverables, unrelated files, pre-existing files, and user data. Never use `git clean` or blanket deletion. If a path's ownership or necessity is uncertain, report its exact path as a blocker.
19
+ Run focused checks required by the task. Before reporting, remove only task-created, non-deliverable temporary, generated, or ignored files. Preserve required deliverables, unrelated and pre-existing files, and user data. Never use `git clean` or blanket deletion. If ownership or necessity is uncertain, report the exact path as a blocker.
20
20
 
21
- Do not access credentials, use the network, generate artifacts, or broaden scope unless the task explicitly requires it. Never invoke external LLM APIs, SDKs, agent harnesses, or model CLIs.
21
+ Access credentials or the network, create extra artifacts, or broaden scope only when the task requires it. Never invoke external LLM APIs, SDKs, agent harnesses, or model CLIs.
22
22
 
23
- Commit completed scoped changes locally unless the task says otherwise. Do not create or manage another worktree. Never push or open a pull request without explicit authorization. For Flow, leave the assigned worktree and branch intact.
23
+ Commit completed scoped changes unless the task says otherwise. Do not create or manage another worktree. Leave the assigned worktree and branch intact. Never push or open a pull request without explicit authorization.
24
24
 
25
- Report briefly: outcome, commit, checks run, and remaining risks. Do not repeat Flow's Git-derived evidence.
25
+ Report briefly: outcome, commit, checks, and remaining risks.
@@ -10,11 +10,8 @@ extensions: []
10
10
  skills: []
11
11
  ---
12
12
 
13
- Review read-only in exactly two modes:
13
+ Review the supplied candidate read-only. Use only supplied requirements and named files or evidence; do not prepare Git or broaden discovery. If evidence is insufficient, say so and stop.
14
14
 
15
- 1. Ordinary delegation: use supplied requirements and named files/evidence only. Do not prepare Git, require a commit/Review Packet, or broaden discovery. If evidence is insufficient, say so and stop.
16
- 2. Flow exact review: only with an explicit judgment criterion, use the same assigned Unit Worktree and exact Review Packet `{base, tip, patchPath}`. Treat the exact patch at `patchPath` as authoritative; read only referenced files/context. Declared validation is authoritative for objective verification. Judge only the explicit criterion; never infer a diff from another branch/worktree.
15
+ Report only actionable correctness risks introduced by the change, not style preferences, speculative hypotheticals, or unrelated pre-existing issues. Run no commands or tests. Never edit, write, commit, push, manage Git or worktrees, or invoke external LLM APIs, SDKs, agent harnesses, or model CLIs.
17
16
 
18
- Report only actionable correctness risks introduced by the change—not style preferences, speculative hypotheticals, or unrelated pre-existing issues. Use only `read`, `grep`, `find`, and `ls`; run no commands/tests and never edit, write, commit, push, or manage Git/worktrees. Never invoke external LLM APIs, SDKs, agent harnesses, or model CLIs.
19
-
20
- Output exactly `PASS` when there are no findings. Otherwise output findings only, ordered by severity, with file:line evidence, impact, and smallest valid fix; any finding blocks approval. Never combine `PASS` with findings. Stop when supplied evidence is covered; in Flow, stop after its criterion.
17
+ Output exactly `PASS` when there are no findings. Otherwise output findings only, ordered by severity, with file:line evidence, impact, and the smallest valid fix. Any finding blocks approval; never combine `PASS` with findings. Stop when the supplied evidence is covered.
@@ -1,7 +1,10 @@
1
- import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
1
+ import { lstatSync } from "node:fs";
2
+ import { isAbsolute } from "node:path";
3
+ import { isToolCallEventType, type ExtensionAPI } from "@earendil-works/pi-coding-agent";
2
4
  import {
3
5
  CHILD_EXCLUDED_TOOL_NAMES,
4
6
  EXECUTION_BUDGET_ENV,
7
+ PI_ORCHESTRATOR_PROCESS_LEASE,
5
8
  ROLE_TOOL_POLICY_FLAG,
6
9
  type EphemeralSubagentExecutionBudget,
7
10
  } from "@henryqw/pi-subagent";
@@ -9,12 +12,27 @@ import {
9
12
  const childExcludedTools: ReadonlySet<string> = new Set(CHILD_EXCLUDED_TOOL_NAMES);
10
13
  const WARNING_RATIO = 0.8;
11
14
  const WARNING_MESSAGE_TYPE = "pi-subagent-execution-budget";
15
+ const PROCESS_LEASE_ERROR = `${PI_ORCHESTRATOR_PROCESS_LEASE} must be a nonempty NUL/newline-free absolute path naming a regular non-symlink file owned by the current uid with mode 0600.`;
12
16
  const FINAL_HANDOFF_MESSAGE = {
13
17
  customType: "pi-subagent-final-handoff",
14
18
  content: "**Final handoff required.** Tools are disabled. If your assigned task or Role requires exact output, reply only with that output instead; it takes precedence over this decision packet. Otherwise, reply only with this decision packet:\n\n**Status:** completed | blocked | incomplete\n**Outcome:** one sentence describing what is now true\n**Evidence:** up to three concrete findings, changes, or checks; include an attempted approach only when it prevents Main from repeating failed work\n**Blocker:** none or the exact blocker\n**Risk:** none or one material risk\n**Suggested next:** none or one concrete action",
15
19
  display: true,
16
20
  };
17
21
 
22
+ function validateProcessLease(path: string): void {
23
+ if (!path || /[\0\r\n]/.test(path) || !isAbsolute(path)) throw new Error(PROCESS_LEASE_ERROR);
24
+ let stats;
25
+ try {
26
+ stats = lstatSync(path);
27
+ } catch (error) {
28
+ throw new Error(PROCESS_LEASE_ERROR, { cause: error });
29
+ }
30
+ const uid = typeof process.getuid === "function" ? process.getuid() : undefined;
31
+ if (uid === undefined || !stats.isFile() || stats.isSymbolicLink() || stats.uid !== uid || (stats.mode & 0o7777) !== 0o600) {
32
+ throw new Error(PROCESS_LEASE_ERROR);
33
+ }
34
+ }
35
+
18
36
  function configuredTools(value: unknown): string[] {
19
37
  if (typeof value !== "string") throw new Error(`${ROLE_TOOL_POLICY_FLAG} must be JSON tool names.`);
20
38
  let parsed: unknown;
@@ -80,6 +98,8 @@ function joinBudgetParts(parts: string[]): string {
80
98
  }
81
99
 
82
100
  export default function roleTools(pi: ExtensionAPI): void {
101
+ const processLease = process.env[PI_ORCHESTRATOR_PROCESS_LEASE];
102
+ if (processLease !== undefined) validateProcessLease(processLease);
83
103
  pi.registerFlag(ROLE_TOOL_POLICY_FLAG, {
84
104
  description: "Internal Pi Subagent Role tool policy",
85
105
  type: "string",
@@ -106,6 +126,18 @@ export default function roleTools(pi: ExtensionAPI): void {
106
126
  }
107
127
  });
108
128
 
129
+ if (processLease !== undefined) {
130
+ pi.on("tool_call", (event) => {
131
+ if (!isToolCallEventType("bash", event)) return;
132
+ event.input.command = `__pi_subagent_process_lease_fd=10
133
+ while : 2>/dev/null <&"$__pi_subagent_process_lease_fd" || : 2>/dev/null >&"$__pi_subagent_process_lease_fd"; do
134
+ __pi_subagent_process_lease_fd=$((__pi_subagent_process_lease_fd + 1))
135
+ done
136
+ eval 'exec '"$__pi_subagent_process_lease_fd"'>>"$${PI_ORCHESTRATOR_PROCESS_LEASE}"' || exit $?
137
+ unset __pi_subagent_process_lease_fd
138
+ ${event.input.command}`;
139
+ });
140
+ }
109
141
  if (!budget) return;
110
142
  const warningTurn = Math.ceil(budget.maxTurns * WARNING_RATIO);
111
143
  const warningTokens = budget.maxTokens === undefined ? undefined : Math.ceil(budget.maxTokens * WARNING_RATIO);