@henryqw/pi-subagent 4.0.1 → 4.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CONTEXT.md CHANGED
@@ -2,13 +2,13 @@
2
2
 
3
3
  ## Purpose
4
4
 
5
- Provide validated user Roles, shared task-model Pi launch policy, generic managed Herdr Subagent hosting, and a `delegate_task` extension that runs one selected bounded workflow in one or more ephemeral child processes.
5
+ Provide validated built-in and user Roles, shared task-model Pi launch policy, generic managed Herdr Subagent hosting, a `delegate_task` extension that runs one selected bounded workflow in one or more ephemeral child processes, and a bundled Main-side `pi-subagent-delegated-development` policy Skill. The Skill requires a clean committed Git `HEAD`, exact clean committed-diff review through a private byte-verified patch file reference, and post-validation recovery cleanup; it adds no runtime behavior or changes generic fallback.
6
6
 
7
7
  ## Domain glossary
8
8
 
9
9
  - **Main**: Pi session delegating work.
10
10
  - **Subagent**: isolated Pi child process handling one task.
11
- - **Role**: user-owned Markdown definition of a reusable responsibility, with a name, description, system instructions, optional base tool allowlist, extensions, and Skill names.
11
+ - **Role**: package-shipped built-in or user-owned Markdown definition of a reusable responsibility, with a name, description, system instructions, optional base tool allowlist, extensions, and Skill names.
12
12
  - **Model Class**: `fast`, `balanced`, `frontier`, or `fav`, assigned in shared task-model settings or overridden by Main from task complexity.
13
13
  - **Route**: configured model and thinking-level pair selected from a shared Model Class profile; the primary route precedes its optional fallback.
14
14
  - **Delegated Task**: one bounded work request sent from Main to one Role.
@@ -27,6 +27,6 @@ Provide validated user Roles, shared task-model Pi launch policy, generic manage
27
27
  - Role Skill names resolve through Main's effective Pi Skill registry; unavailable names warn and skip without blocking delegation.
28
28
  - Main selects Role and may override Model Class and thinking level per task; omitted class uses shared `pi-subagent/delegateTask` assignment, initially `balanced`. Library callers select Role plus their own shared task ID.
29
29
  - The selected profile resolves primary then fallback only before launch when a route, model, or thinking level is unavailable. If neither route is usable, launch rejects with `Run /task-models`; a started child is never retried by this package.
30
- - Role Markdown files and Subagent JSON config live only in the user `config/pi-subagent` directory; model routes live in shared `config/pi-task-models.json`; repository roles do not execute.
30
+ - User Role Markdown files and Subagent JSON config live only in the user `config/pi-subagent` directory; model routes live in shared `config/pi-task-models.json`. Package-shipped built-in Roles (`implementer`, `reviewer`) resolve from the package's own `examples/roles/` Markdown through the same parser; a same-named user file explicitly overrides a built-in.
31
31
  - Numbered Codex routes prefer Main's active account slot and explicitly load the multi-Codex child extension.
32
32
  - Generic Herdr host functions validate workspace ownership and provisioning identity while callers retain domain state, prompts, and lifecycle decisions.
package/README.md CHANGED
@@ -49,41 +49,71 @@ Parallel mode starts entries concurrently, waits for every entry, and reports th
49
49
 
50
50
  Background workflows are session-scoped. Session shutdown or reload aborts them and may deliver only recoverable-work evidence or no follow-up message.
51
51
 
52
- Each delegation resolves its own user Role, resources, route, and optional worktree request. When available, `isolation: worktree` gives each entry a deterministic separate worktree; non-Git or unborn-`HEAD` contexts may use Main's cwd. Siblings and chain steps never implicitly share one created worktree.
52
+ Each delegation resolves its own Role, resources, route, and optional worktree request. When available, `isolation: worktree` gives each entry a deterministic separate worktree; non-Git or unborn-`HEAD` contexts may use Main's cwd. Siblings and chain steps never implicitly share one created worktree.
53
53
 
54
54
  See [Orchestration, isolation, and the public API](./docs/orchestration.md) for the complete contract and JavaScript composition examples.
55
55
 
56
56
  ## Config
57
57
 
58
- `~/.pi/agent/config/pi-subagent/pi-subagent.json` controls the ephemeral child pool and timeouts:
58
+ pi-subagent owns the extension-named config directory `~/.pi/agent/config/pi-subagent/`, which holds two kinds of user-owned configuration: one Markdown file per Role (see [Roles](#roles)) and its own optional JSON file below. Model routing is *not* configured here; children resolve routes through the shared `@henryqw/pi-task-models` config at `~/.pi/agent/config/pi-task-models.json`.
59
59
 
60
- ```json
61
- {
62
- "maxSubagents": 5,
63
- "timeout": { "idleMinutes": 10, "maxMinutes": 30 }
64
- }
65
- ```
60
+ `~/.pi/agent/config/pi-subagent/pi-subagent.json` controls the ephemeral child pool and timeouts. All fields are optional; a missing file uses defaults.
61
+
62
+ | Field | Required | Possible values | Default |
63
+ | --- | --- | --- | --- |
64
+ | `maxSubagents` | No | Safe integer ≥ 1 | `5` |
65
+ | `timeout.idleMinutes` | No | Positive number of minutes where minutes × 60 000 ms ≤ 2,147,483,647 | `10` |
66
+ | `timeout.maxMinutes` | No | Positive number within the same ms cap that must be greater than `timeout.idleMinutes`, otherwise the whole `timeout` object falls back to defaults | `30` |
67
+
68
+ Excess children wait FIFO without consuming child timeout. `PI_SUBAGENT_MAX_SUBAGENTS` overrides `maxSubagents` for the session (positive integer; an invalid value prevents the extension from loading, leaving `delegate_task` unavailable).
69
+
70
+ This JSON is read leniently: malformed JSON, a non-object root, unknown keys, or invalid values are collected into one warning and the affected settings fall back to defaults; the file is never rewritten.
71
+
72
+ ### Role frontmatter
73
+
74
+ Each Role `.md` file in the same directory accepts these frontmatter fields:
66
75
 
67
- Excess children wait FIFO without consuming child timeout. `PI_SUBAGENT_MAX_SUBAGENTS` overrides the configured concurrency for the session.
76
+ | Field | Required | Possible values | Default |
77
+ | --- | --- | --- | --- |
78
+ | `name` | Yes | Non-empty text; unique across roles | — |
79
+ | `description` | Yes | Non-empty text | — |
80
+ | `tools` | No | Array or comma-separated string of tool names | Omitted: caller tools win, else Pi defaults apply |
81
+ | `isolation` | No | `worktree` | None |
82
+ | `extensions` | No | Absolute paths, `~/…`, `file://`, or package sources (`npm:`, `git:`, `github:`, `https?:`, `ssh:`) | None |
83
+ | `skills` | No | Array or comma-separated string of skill names | None; unavailable names warn and skip |
84
+ | body | Yes | System-prompt Markdown after the frontmatter | — |
85
+
86
+ An unreadable or invalid Role file fails role loading fast; duplicate role names are rejected.
68
87
 
69
88
  ## Roles
70
89
 
71
- Roles are user-owned Markdown files in `~/.pi/agent/config/pi-subagent/`. This repository includes inert samples:
90
+ The package ships two working built-in Roles, always available without any configuration:
91
+
92
+ - `implementer`: focused edits requesting worktree isolation; commits completed scoped changes locally and never pushes or opens PRs without authorization
93
+ - `reviewer`: read-only correctness review from a supplied exact patch file reference (path, bytes, SHA-256) and referenced files; never edits or commits
94
+
95
+ A same-named Markdown file in `~/.pi/agent/config/pi-subagent/` explicitly overrides the built-in default.
96
+
97
+ The repository also includes optional inert samples:
72
98
 
73
99
  - [`scout`](./examples/roles/scout.md): read-only discovery
74
- - [`implementer`](./examples/roles/implementer.md): focused edits requesting worktree isolation
75
- - [`reviewer`](./examples/roles/reviewer.md): read-only correctness review
76
100
  - [`synthesizer`](./examples/roles/synthesizer.md): reconcile supplied reports
77
101
 
78
- Copy them manually from the repository root:
102
+ Copy them manually from your installed `@henryqw/pi-subagent` package (npm installs ship the `examples/roles/` directory) if you want them as a starting point:
79
103
 
80
104
  ```bash
81
105
  mkdir -p ~/.pi/agent/config/pi-subagent
82
- cp packages/pi-subagent/examples/roles/*.md ~/.pi/agent/config/pi-subagent/
106
+ cp <package-install-dir>/examples/roles/scout.md ~/.pi/agent/config/pi-subagent/
83
107
  ```
84
108
 
109
+ Locate the install directory with `npm root` inside your project, or via Pi's package installation path.
110
+
85
111
  The package never installs or writes Role configuration. Sample names are not built-ins; after copying, edit or replace them as your own Roles.
86
112
 
113
+ ## Skill
114
+
115
+ The bundled [`pi-subagent-delegated-development`](./skills/pi-subagent-delegated-development/SKILL.md) Skill is opinionated orchestration policy for Main. It fails before delegation unless Main's cwd is a clean Git working tree with a committed `HEAD`; then it requires a clean retained implementer worktree, a verified base/branch/tip committed diff, and review from a privately generated, byte-verified exact patch file reference (path, bytes, SHA-256) plus referenced files—not inline patch text. It re-checks Main before integrating the reviewed commit, preserves artifacts on integration or validation failure, and removes eligible retained worktrees, task branches, and temporary patch files only after focused validation passes. Dirty or incomplete work goes to a fresh repair delegation. This leaves the generic `delegate_task` non-Git/unborn-`HEAD` fallback unchanged. All model and agent work stays in Pi via `delegate_task`; deterministic developer tools remain allowed. It is guidance only — it adds no runtime code, config, or Role installation, and preserves the [composition-outside-the-executor](./docs/adr/001-composable-ephemeral-execution.md) boundary.
116
+
87
117
  A Role owns its base tools, extensions, named Skills, instructions, and optional `isolation: worktree`. Ambient extension and Skill discovery is disabled in children. With neither Role tools nor caller tools, Pi defaults remain; caller tools with omitted Role tools snapshot Main's effective active built-ins and install the child policy. Loaded Role/caller extension tools still activate, parent-only tools stay excluded, and unavailable named Skills warn and skip.
88
118
 
89
119
  ## Library API
@@ -91,17 +121,3 @@ A Role owns its base tools, extensions, named Skills, instructions, and optional
91
121
  The package root exports Role loading and launch resolution, `createEphemeralSubagentExecutor`, worktree helpers, and generic managed Herdr lifecycle helpers. The ephemeral executor is for code already running inside active Pi; it does not provide standalone Node.js Pi discovery or launch support.
92
122
 
93
123
  Use [`docs/orchestration.md`](./docs/orchestration.md#public-role-and-executor-api) for exact API behavior and a post-permit `prepare` example using `resolveRoleLaunch` against the latest Pi context.
94
-
95
- ## Remove
96
-
97
- ```bash
98
- pi remove npm:@henryqw/pi-subagent
99
- ```
100
-
101
- ## Development
102
-
103
- ```bash
104
- npm test --workspace @henryqw/pi-subagent
105
- npm run typecheck --workspace @henryqw/pi-subagent
106
- npm run pack:check --workspace @henryqw/pi-subagent
107
- ```
package/dist/index.d.ts CHANGED
@@ -38,6 +38,12 @@ export interface ResolvedRoleSkills {
38
38
  paths: string[];
39
39
  missing: string[];
40
40
  }
41
+ /**
42
+ * Validated built-in implementer/reviewer Roles plus valid user roles from
43
+ * `config/pi-subagent`. A user role with a built-in name overrides the default;
44
+ * duplicate names among user files are an error. Missing user directory returns
45
+ * the built-ins.
46
+ */
41
47
  export declare function loadRoles(agentDir?: string): Role[];
42
48
  export declare function resolveTaskRoute(ctx: ExtensionContext, profileName: ProfileName, agentDir?: string, thinking?: ThinkingLevel): ResolvedTaskRoute;
43
49
  export declare function resolveRoleSkills(pi: Pick<ExtensionAPI, "getCommands">, role: Role): ResolvedRoleSkills;
package/dist/index.js CHANGED
@@ -42,7 +42,53 @@ function validateExtension(extension, source) {
42
42
  function extensionList(value, source) {
43
43
  return stringList(value, "extensions", source).map((extension) => validateExtension(extension, source));
44
44
  }
45
+ // Built-in Roles required by the bundled pi-subagent-delegated-development Skill;
46
+ // resolved from the package-shipped Markdown relative to this module.
47
+ const BUILTIN_ROLE_FILES = ["implementer.md", "reviewer.md"];
48
+ /** Single-file Role parser shared by built-in and user roles. */
49
+ function parseRoleFile(file, raw) {
50
+ let parsed;
51
+ try {
52
+ parsed = parseFrontmatter(raw);
53
+ }
54
+ catch (error) {
55
+ throw new Error(`${file}: ${error instanceof Error ? error.message : String(error)}`);
56
+ }
57
+ const frontmatter = parsed.frontmatter;
58
+ const isolation = frontmatter.isolation === undefined ? undefined : cleanText(frontmatter.isolation, "isolation", file);
59
+ if (isolation !== undefined && isolation !== "worktree")
60
+ throw new Error(`${file}: isolation must be "worktree".`);
61
+ return {
62
+ name: cleanText(frontmatter.name, "name", file),
63
+ description: cleanText(frontmatter.description, "description", file),
64
+ tools: frontmatter.tools === undefined ? undefined : stringList(frontmatter.tools, "tools", file, true),
65
+ isolation,
66
+ extensions: extensionList(frontmatter.extensions, file),
67
+ skills: stringList(frontmatter.skills, "skills", file),
68
+ systemPrompt: cleanText(parsed.body, "system prompt", file),
69
+ };
70
+ }
71
+ function readRoleFile(file) {
72
+ try {
73
+ return parseRoleFile(file, readFileSync(file, "utf8"));
74
+ }
75
+ catch (error) {
76
+ if (error instanceof Error && error.message.startsWith(`${file}: `))
77
+ throw error;
78
+ throw new Error(`${file}: ${error instanceof Error ? error.message : String(error)}`);
79
+ }
80
+ }
81
+ function builtinRoles() {
82
+ return BUILTIN_ROLE_FILES.map((name) => readRoleFile(fileURLToPath(new URL(`../examples/roles/${name}`, import.meta.url))));
83
+ }
84
+ /**
85
+ * Validated built-in implementer/reviewer Roles plus valid user roles from
86
+ * `config/pi-subagent`. A user role with a built-in name overrides the default;
87
+ * duplicate names among user files are an error. Missing user directory returns
88
+ * the built-ins.
89
+ */
45
90
  export function loadRoles(agentDir = getAgentDir()) {
91
+ const byName = new Map(builtinRoles().map((role) => [role.name, role]));
46
92
  const dir = join(agentDir, "config", "pi-subagent");
47
93
  let entries;
48
94
  try {
@@ -50,42 +96,21 @@ export function loadRoles(agentDir = getAgentDir()) {
50
96
  }
51
97
  catch (error) {
52
98
  if (error && typeof error === "object" && "code" in error && error.code === "ENOENT")
53
- return [];
99
+ return [...byName.values()];
54
100
  throw error;
55
101
  }
56
- const roles = entries
102
+ const userRoles = entries
57
103
  .filter((entry) => entry.name.endsWith(".md") && (entry.isFile() || entry.isSymbolicLink()))
58
104
  .sort((a, b) => a.name.localeCompare(b.name))
59
- .map((entry) => {
60
- const file = join(dir, entry.name);
61
- let parsed;
62
- try {
63
- parsed = parseFrontmatter(readFileSync(file, "utf8"));
64
- }
65
- catch (error) {
66
- throw new Error(`${file}: ${error instanceof Error ? error.message : String(error)}`);
67
- }
68
- const frontmatter = parsed.frontmatter;
69
- const isolation = frontmatter.isolation === undefined ? undefined : cleanText(frontmatter.isolation, "isolation", file);
70
- if (isolation !== undefined && isolation !== "worktree")
71
- throw new Error(`${file}: isolation must be "worktree".`);
72
- return {
73
- name: cleanText(frontmatter.name, "name", file),
74
- description: cleanText(frontmatter.description, "description", file),
75
- tools: frontmatter.tools === undefined ? undefined : stringList(frontmatter.tools, "tools", file, true),
76
- isolation,
77
- extensions: extensionList(frontmatter.extensions, file),
78
- skills: stringList(frontmatter.skills, "skills", file),
79
- systemPrompt: cleanText(parsed.body, "system prompt", file),
80
- };
81
- });
105
+ .map((entry) => readRoleFile(join(dir, entry.name)));
82
106
  const names = new Set();
83
- for (const role of roles) {
107
+ for (const role of userRoles) {
84
108
  if (names.has(role.name))
85
109
  throw new Error(`Duplicate Subagent role: ${role.name}.`);
86
110
  names.add(role.name);
111
+ byName.set(role.name, role);
87
112
  }
88
- return roles;
113
+ return [...byName.values()];
89
114
  }
90
115
  export function resolveTaskRoute(ctx, profileName, agentDir = getAgentDir(), thinking) {
91
116
  let config;
@@ -12,7 +12,7 @@ Resource Policy is split at launch preparation:
12
12
  - Caller may add explicit tools, extensions, and environment through `createRoleLaunch`.
13
13
  - The executor receives the resulting Pi Launch and does not discover resources.
14
14
 
15
- Repository Role samples are inert and user-owned only after manual copying to `~/.pi/agent/config/pi-subagent/`. This package does not install, copy, or write user configuration; copy instructions belong in downstream user documentation.
15
+ Built-in `implementer` and `reviewer` Roles ship with the package as Markdown in `examples/roles/` and resolve through the same single-file parser at load time; a same-named user file in `~/.pi/agent/config/pi-subagent/` explicitly overrides a built-in. Other repository samples are inert and user-owned only after manual copying to `~/.pi/agent/config/pi-subagent/`. This package does not install, copy, or write user configuration; copy instructions belong in downstream user documentation.
16
16
 
17
17
  ## Consequences
18
18
 
@@ -3,7 +3,7 @@
3
3
  `pi-subagent` separates tool policy from execution mechanism:
4
4
 
5
5
  ```text
6
- user Role + latest Pi registries ── resolveRoleLaunch ──> PiLaunch
6
+ Role (built-in or user override) + latest Pi registries ── resolveRoleLaunch ──> PiLaunch
7
7
 
8
8
  caller-owned task, cwd, signal ─────────────────────────┤
9
9
  v
@@ -63,7 +63,7 @@ Single mode puts one delegation's fields at the top level.
63
63
 
64
64
  | Field | Required | Contract |
65
65
  | --- | --- | --- |
66
- | `role` | yes | Name of a Role in the user's effective `config/pi-subagent` directory. There are no package-owned Role names. |
66
+ | `role` | yes | Name of a Role in the user's effective `config/pi-subagent` directory or a package-shipped built-in (`implementer`, `reviewer`); a same-named user file overrides the built-in. |
67
67
  | `task` | yes | Non-empty bounded task packet. |
68
68
  | `model` | no | Designated `provider/modelId`; takes precedence over `modelClass`. |
69
69
  | `modelClass` | no | `fast`, `balanced`, `frontier`, or `fav`; omission uses shared task assignment. |
@@ -83,14 +83,14 @@ All Main-visible text for one tool call shares one aggregate 50 KiB UTF-8 transp
83
83
 
84
84
  Every single entry, parallel sibling, and chain step independently:
85
85
 
86
- 1. loads its selected user Role;
86
+ 1. loads its selected Role;
87
87
  2. resolves its route and named Skills from the latest effective Pi context after receiving an executor permit;
88
88
  3. creates its Role launch policy; and
89
89
  4. when the Role requests `isolation: worktree`, creates a worktree identified by the tool call, mode, and input index.
90
90
 
91
91
  Separate deterministic identities produce separate hashed 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.
92
92
 
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. 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.
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. 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`.
94
94
 
95
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.
96
96
 
@@ -116,7 +116,7 @@ The package root exports the following mechanism-level APIs:
116
116
 
117
117
  | API | Responsibility |
118
118
  | --- | --- |
119
- | `loadRoles(agentDir?)` | Validate and load user Role Markdown. |
119
+ | `loadRoles(agentDir?)` | Validate and load package-shipped built-in and user Role Markdown. |
120
120
  | `resolveRoleSkills(pi, role)` | Resolve Role Skill names from Pi's effective registry. |
121
121
  | `resolveRoleLaunch(pi, ctx, input)` | Resolve a shared task route and produce `ResolvedRoleLaunch`. |
122
122
  | `createRoleLaunch(pi, ctx, input)` | Produce the same launch from a caller-supplied resolved route. |
@@ -319,24 +319,33 @@ if (!approved) throw new Error(`Review did not pass after ${maxReviewRounds} rou
319
319
 
320
320
  The verdict schema, parser, round state, shared workspace, and terminal decision all belong to the caller. Add a richer protocol only when the workflow requires one; do not encode it as a recursive package workflow definition.
321
321
 
322
- ## Role samples
322
+ ## Built-in Roles and samples
323
323
 
324
- Repository samples are documentation, not installed configuration:
324
+ The package ships two working built-in Roles, validated by the same parser as user roles and always present even with no `config/pi-subagent` directory:
325
+
326
+ | Built-in | Behavior |
327
+ | --- | --- |
328
+ | `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. |
329
+ | `reviewer` | Read-only correctness review from a supplied exact patch file reference (path, bytes, SHA-256) and referenced files; never edits or commits. |
330
+
331
+ A same-named Markdown file in `config/pi-subagent/` explicitly overrides the built-in default.
332
+
333
+ Additional repository samples are inert starting points, not installed configuration:
325
334
 
326
335
  | Sample | Intended starting point |
327
336
  | --- | --- |
328
337
  | [`scout`](../examples/roles/scout.md) | Read-only code/evidence mapping. |
329
- | [`implementer`](../examples/roles/implementer.md) | Focused implementation requesting `isolation: worktree`; non-Git or unborn-`HEAD` contexts may use Main's cwd. |
330
- | [`reviewer`](../examples/roles/reviewer.md) | Read-only correctness review. |
331
338
  | [`synthesizer`](../examples/roles/synthesizer.md) | Reconcile supplied reports without broad discovery. |
332
339
 
333
- From the repository root, opt in explicitly:
340
+ Copy the package-shipped samples from your installed `@henryqw/pi-subagent` package (npm installs include `examples/roles/`) if you want them:
334
341
 
335
342
  ```bash
336
343
  mkdir -p ~/.pi/agent/config/pi-subagent
337
- cp packages/pi-subagent/examples/roles/*.md ~/.pi/agent/config/pi-subagent/
344
+ cp <package-install-dir>/examples/roles/scout.md ~/.pi/agent/config/pi-subagent/
338
345
  ```
339
346
 
340
347
  The package never creates, copies, updates, or removes files in `~/.pi/agent/config/pi-subagent/`. Once copied, the files and their names are entirely user-owned.
341
348
 
349
+ The bundled [`pi-subagent-delegated-development`](../skills/pi-subagent-delegated-development/SKILL.md) Skill is Main-side orchestration policy only. It prescribes an implement → review → merge loop over the built-in `implementer` and `reviewer` Roles, with private byte-verified exact-patch file references rather than inline patch text and cleanup only after integration validation. It defines no workflow AST, runtime code, or configuration; `delegate_task` remains the flat single/parallel/chain mechanism.
350
+
342
351
  See the architectural decision: [Compose workflows outside the ephemeral executor](./adr/001-composable-ephemeral-execution.md).
@@ -1,7 +1,14 @@
1
1
  ---
2
2
  name: implementer
3
3
  description: Implements and validates one bounded change, requesting worktree isolation
4
- tools: [read, bash, edit, write, grep, find, ls]
4
+ tools:
5
+ - read
6
+ - bash
7
+ - edit
8
+ - write
9
+ - grep
10
+ - find
11
+ - ls
5
12
  isolation: worktree
6
13
  ---
7
14
 
@@ -9,6 +16,8 @@ Implement one bounded task.
9
16
 
10
17
  Read applicable repository instructions and domain context first. Inspect the existing flow and its callers before editing. Work only in explicitly assigned files and preserve unrelated changes. Fix the root cause with the smallest complete diff, reusing existing patterns and dependencies.
11
18
 
12
- Run focused validation that would fail if the change were wrong. Do not access credentials, use the network, generate artifacts, or broaden scope unless the task explicitly requires it.
19
+ Run focused validation that would fail if the change were wrong. 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; deterministic developer tools remain allowed.
13
20
 
14
- Return changed files, validation results, and remaining risks.
21
+ Commit completed scoped changes locally unless the task forbids it. Never push or open pull requests without explicit authorization.
22
+
23
+ Return the retained worktree path, branch, base commit, tip commit, changed files from the base-to-tip committed diff, clean `git status --porcelain=v1 --untracked-files=all` result, validation results, and remaining risks. Do not remove the retained worktree or task branch; Main cleans them only after successful integration and validation.
@@ -1,11 +1,19 @@
1
1
  ---
2
2
  name: reviewer
3
3
  description: Reviews one bounded change for correctness without changing files
4
- tools: [read, grep, find, ls]
4
+ tools:
5
+ - read
6
+ - grep
7
+ - find
8
+ - ls
5
9
  ---
6
10
 
7
11
  Perform a read-only correctness review of one bounded change.
8
12
 
9
- Review only the requirements and changed files named in the task, plus directly relevant callers, contracts, and tests. Check correctness, regressions, trust-boundary validation, error handling, and missing high-value tests. Do not edit files, run shell commands, or propose unrelated refactors.
13
+ Refuse review unless the task supplies the base commit, tip commit, complete exact base-to-tip patch file reference (path, byte count, SHA-256), and explicit review context: `{type:'child_branch', branch}` or `{type:'integration_head'}`. Review from that supplied file and the files it references only; do not infer a diff from a branch or worktree. Read the patch in bounded chunks when needed and use only `read`, `grep`, `find`, or `ls` for referenced files.
14
+
15
+ Review only the supplied requirements, exact patch file, and explicitly referenced files, including any relevant callers, contracts, and tests. Check correctness, regressions, trust-boundary validation, error handling, and missing high-value tests. Do not run commands or tests. Never edit files, commit, push, or otherwise modify state.
16
+
17
+ Never invoke external LLM APIs, SDKs, agent harnesses, or model CLIs.
10
18
 
11
19
  Return findings first, ordered by severity. Every finding must include file and line evidence, impact, and the smallest valid fix. If there are no findings, say so and list any unvalidated risk.
@@ -178,8 +178,7 @@ function failedToolPatch(error: WorkflowFailureError | WorkflowAbortedError) {
178
178
 
179
179
  const roleSummary = (): string => {
180
180
  try {
181
- const roles = loadRoles();
182
- return roles.length ? roles.map((role) => `${role.name}: ${role.description}`).join("; ") : "none configured";
181
+ return loadRoles().map((role) => `${role.name}: ${role.description}`).join("; ");
183
182
  } catch (error) {
184
183
  return `configuration error: ${error instanceof Error ? error.message : String(error)}`;
185
184
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@henryqw/pi-subagent",
3
- "version": "4.0.1",
3
+ "version": "4.1.0",
4
4
  "description": "Delegate bounded single, parallel, or chained tasks to isolated Pi roles.",
5
5
  "keywords": [
6
6
  "pi-package",
@@ -18,6 +18,7 @@
18
18
  "extensions",
19
19
  "docs",
20
20
  "examples",
21
+ "skills",
21
22
  "README.md",
22
23
  "CONTEXT.md",
23
24
  "LICENSE"
@@ -57,6 +58,9 @@
57
58
  "pi": {
58
59
  "extensions": [
59
60
  "./extensions/subagent.ts"
61
+ ],
62
+ "skills": [
63
+ "./skills"
60
64
  ]
61
65
  },
62
66
  "dependencies": {
@@ -0,0 +1,54 @@
1
+ ---
2
+ name: pi-subagent-delegated-development
3
+ description: Orchestrate delegated development with pi-subagent delegate_task. Use when splitting implementation work into bounded units for isolated implementer children reviewed by a read-only reviewer before merging.
4
+ ---
5
+
6
+ # Delegated Development
7
+
8
+ You are Main. Decompose work into bounded units and coordinate; never edit files yourself.
9
+
10
+ All model and agent work in this workflow goes through Pi's `delegate_task` and Pi-managed children. Do not invoke external LLM APIs, SDKs, agent harnesses, or model CLIs; ordinary deterministic developer tools such as `git`, npm, test runners, and compilers remain allowed.
11
+
12
+ ## Required preflight
13
+
14
+ Before any delegation, require that Main's cwd is a Git working tree with a committed `HEAD`:
15
+
16
+ ```bash
17
+ test "$(git rev-parse --is-inside-work-tree)" = true &&
18
+ git rev-parse --verify -q HEAD^{commit} >/dev/null
19
+ ```
20
+
21
+ If this fails, stop before delegation and report that this Skill requires a Git repository with a committed `HEAD`; do not rely on the generic worktree fallback to Main's cwd.
22
+
23
+ Before recording each unit's `base=$(git rev-parse HEAD)`, and again immediately before integration and validation, require `git status --porcelain=v1 --untracked-files=all` to be empty. If Git cannot inspect status or any tracked or untracked change is present, stop and ask the user to preserve the changes; never stash, discard, hide, or work around them.
24
+
25
+ ## Roles
26
+
27
+ The package-shipped built-in Roles `implementer` (`isolation: worktree`) and `reviewer` (read-only patch-and-file review) are always available; no installation step is required. A same-name user override replaces the built-in entirely. Before using this workflow, Main must verify that each override preserves implementer worktree isolation and reviewer read-only patch-and-file constraints; fail clearly if it does not. Do not invent substitute Roles.
28
+
29
+ ## Per-unit loop
30
+
31
+ For each bounded unit:
32
+
33
+ 1. **Implement** — one `delegate_task` single call to `implementer`. The packet states the objective, touched scope, required validation, and recorded base commit. Require its output to identify the retained worktree path, branch, base commit, tip commit, changed files from the base-to-tip committed diff, and clean `git status --porcelain=v1 --untracked-files=all` result.
34
+ 2. **Verify and review** — refuse review or merge unless Main independently verifies that the worktree was retained and is clean (including untracked files), the reported base/branch/tip identities are complete and match Git, the tip is descended from the recorded base, and the reported changed files equal `git diff --name-only "$base" "$tip"`. All intended changes must be in that committed base-to-tip diff. If any evidence is missing or any check fails, send the work to a fresh implementer repair; never review dirty or uncommitted work.
35
+
36
+ Create a private temporary exact-patch artifact outside the repository from `git diff --no-textconv --no-ext-diff --ignore-submodules=none --binary "$base" "$tip"`. Independently regenerate that same diff and byte-compare it with the artifact, then record its path, byte count, and SHA-256. If creation, regeneration, comparison, byte count, or checksum fails, stop and report the artifact failure clearly; do not review or merge. Do not put any complete patch content in `delegate_task` text or argv.
37
+
38
+ Make one `delegate_task` single call to `reviewer` with only a bounded metadata packet: base, tip, review context `{type:'child_branch', branch}`, the verified complete patch file reference (`path`, `bytes`, `sha256`), and the verified changed paths. If that complete metadata cannot fit the task transport bound, stop and report it rather than truncating or inlining patch content. Chain entries do not share files: `{previous}` passes text only.
39
+ 3. **Merge** — only after an approving review, re-check Main's clean status, verify the branch tip still equals the reviewed tip commit, then merge that exact commit (not the branch name) into Main's current worktree and run focused validation there. Never merge on unresolved findings.
40
+ 4. **Clean up after success** — only after the exact reviewed tip is integrated and focused validation passes, remove each reported retained worktree, then safely delete its task branch. Include a superseded repair-round worktree only when its exact tip is an ancestor of integrated `HEAD`. For each candidate, verify ancestry first, use non-forced worktree removal followed by `git branch -d`, and stop/report cleanup failure without deleting later evidence. On any integration or validation failure, preserve every temporary patch artifact, retained worktree, and task branch for recovery. After integration and validation succeed, remove the temporary reviewer patch artifacts.
41
+
42
+ ## Findings
43
+
44
+ Any reviewer finding goes back as a **fresh** `implementer` delegation containing the findings plus the reviewed base/branch/tip identities and complete exact patch file reference, followed by a fresh review of the new state. A fresh repair implementer starts in a new worktree from Main HEAD and does not contain the prior unit commit: the repair packet must first bring the whole reviewed `$base..$tip` range into its fresh worktree by merging the exact `$tip`, or cherry-picking every range commit in order. Cherry-pick `$tip` alone only after `git rev-list --count "$base..$tip"` verifies the range is exactly one commit; then address the findings. Dirty or uncommitted work also goes only to this fresh repair path. Bound the loop (e.g. three rounds); past the bound, stop and report to the user instead of merging.
45
+
46
+ ## Parallelism
47
+
48
+ Independent units may run concurrently via one `delegate_task` parallel call (max 8 entries) or concurrent single calls, each still following its own implement → review cycle. Never parallelize a unit's review ahead of its implementation. Each child gets its own deterministic worktree; they never share files implicitly.
49
+
50
+ ## Boundaries
51
+
52
+ - Never bypass review, edit inside a child's worktree, or re-implement a child's work yourself.
53
+ - On child failure, recover from the reported preserved-worktree evidence; retry at most once per unit before escalating to the user.
54
+ - Do not push, publish, release, or open PRs without explicit user authorization.