@gotgenes/pi-subagents 10.1.0 → 10.2.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.
@@ -0,0 +1,245 @@
1
+ ---
2
+ issue: 231
3
+ issue_title: "Push exec/registry relay deps to runner construction (Phase 15, Step 3)"
4
+ ---
5
+
6
+ # Push exec/registry relay deps to runner construction
7
+
8
+ ## Problem Statement
9
+
10
+ `AgentManager` receives `exec` and `registry` in its constructor but never uses them directly.
11
+ They are stored as fields solely to relay them into `runner.run()` via the `RunContext` parameter.
12
+ This makes `AgentManager` wider than necessary and prevents the runner from being self-contained — a prerequisite for #229 (Agent.run() absorbs startAgent).
13
+
14
+ ## Goals
15
+
16
+ - Move `exec` and `registry` from `AgentManager` construction to `ConcreteAgentRunner` construction.
17
+ - Remove `exec` and `registry` from `AgentManagerOptions` (7 → 5 fields).
18
+ - Remove `exec` and `registry` from `RunContext` (4 → 2 fields).
19
+ - Group runner-owned dependencies in a `RunnerDeps` interface: `{ io, exec, registry }`.
20
+ - Replace `runAgent()`'s `io: RunnerIO` parameter with `deps: RunnerDeps`.
21
+
22
+ ## Non-Goals
23
+
24
+ - Dissolving `RunContext` entirely — it shrinks to `{ cwd?, parentSession? }`, which is still a coherent per-call grouping.
25
+ Issue #229 will likely dissolve it when `Agent.run()` calls the runner directly.
26
+ - Changing the `AgentRunner` interface's `run()` signature — callers continue to pass `RunOptions` with `context: RunContext`.
27
+ `ConcreteAgentRunner` merges its stored deps before calling `runAgent()`.
28
+ - Touching `resume()` or `resumeAgent()` — they don't use `exec` or `registry`.
29
+
30
+ ## Background
31
+
32
+ Issue #169 extracted `RunContext` from `RunOptions` to group the 4 parent-context fields: `exec`, `registry`, `cwd`, `parentSession`.
33
+ The doc comment describes them as "parent environment and identity" fields.
34
+ However, 2 of the 4 fields (`exec`, `registry`) are static — identical across every `run()` call — while the other 2 (`cwd`, `parentSession`) vary per spawn.
35
+ The static pair are relay-only dependencies on `AgentManager`: stored at construction, never read, only forwarded.
36
+
37
+ From the code-design skill, this is a **parameter relay** smell: intermediaries (`AgentManager`) carry fields they never use, only to thread them to the endpoint (`runAgent`).
38
+ The fix: put them on the object the endpoint owns — the runner.
39
+
40
+ ### Key references
41
+
42
+ - `src/lifecycle/agent-manager.ts` — stores `exec` and `registry`, relays them at lines 193–194.
43
+ - `src/lifecycle/agent-runner.ts` — `RunContext` interface (line 125), `ConcreteAgentRunner` class (line 189), `runAgent()` free function (line 236).
44
+ - `src/index.ts` — constructs both `ConcreteAgentRunner` and `AgentManager` (lines 148–157).
45
+ - Phase 15 roadmap in `docs/architecture/architecture.md` § Step 3.
46
+
47
+ ## Design Overview
48
+
49
+ ### RunnerDeps — grouping runner-owned dependencies
50
+
51
+ A new `RunnerDeps` interface groups the three dependencies that the runner owns:
52
+
53
+ ```typescript
54
+ export interface RunnerDeps {
55
+ io: RunnerIO;
56
+ exec: ShellExec;
57
+ registry: AgentConfigLookup;
58
+ }
59
+ ```
60
+
61
+ `ConcreteAgentRunner` takes `RunnerDeps` at construction:
62
+
63
+ ```typescript
64
+ export class ConcreteAgentRunner implements AgentRunner {
65
+ constructor(private readonly deps: RunnerDeps) {}
66
+
67
+ run(snapshot, type, prompt, options) {
68
+ return runAgent(snapshot, type, prompt, options, this.deps);
69
+ }
70
+ }
71
+ ```
72
+
73
+ `runAgent()` changes its last parameter from `io: RunnerIO` to `deps: RunnerDeps`:
74
+
75
+ ```typescript
76
+ export async function runAgent(
77
+ snapshot: ParentSnapshot,
78
+ type: SubagentType,
79
+ prompt: string,
80
+ options: RunOptions,
81
+ deps: RunnerDeps,
82
+ ): Promise<RunResult> {
83
+ const effectiveCwd = options.context?.cwd ?? snapshot.cwd;
84
+ const env = await deps.io.detectEnv(deps.exec, effectiveCwd);
85
+ // ...
86
+ const cfg = assembleSessionConfig(type, ..., deps.registry, deps.io.assemblerIO);
87
+ // ...
88
+ }
89
+ ```
90
+
91
+ ### RunContext shrinks
92
+
93
+ `RunContext` loses `exec` and `registry`:
94
+
95
+ ```typescript
96
+ export interface RunContext {
97
+ /** Override working directory (e.g. for worktree isolation). */
98
+ cwd?: string;
99
+ /** Parent session identity (file path + session ID). */
100
+ parentSession?: ParentSessionInfo;
101
+ }
102
+ ```
103
+
104
+ The `AgentRunner.run()` interface is unchanged — callers still pass `RunOptions` with `context: RunContext`.
105
+ `ConcreteAgentRunner.run()` reads `exec` and `registry` from its own `deps` instead of from `options.context`.
106
+
107
+ ### AgentManager loses 2 fields
108
+
109
+ `AgentManagerOptions` removes `exec` and `registry`.
110
+ `AgentManager` removes the corresponding private fields and the `this.exec` / `this.registry` relay in `startAgent()`.
111
+ The `context` object constructed in `startAgent()` shrinks from 4 fields to 2:
112
+
113
+ ```typescript
114
+ context: {
115
+ cwd: record.worktreeState?.path,
116
+ parentSession: options.parentSession,
117
+ },
118
+ ```
119
+
120
+ ### Wiring in index.ts
121
+
122
+ ```typescript
123
+ const runner = new ConcreteAgentRunner({
124
+ io: runnerIO,
125
+ exec: (cmd, args, opts) => pi.exec(cmd, args, opts),
126
+ registry,
127
+ });
128
+
129
+ const manager = new AgentManager({
130
+ runner,
131
+ worktrees: new GitWorktreeManager(process.cwd()),
132
+ observer,
133
+ getMaxConcurrent: () => settings.maxConcurrent,
134
+ getRunConfig: () => settings,
135
+ });
136
+ ```
137
+
138
+ ## Module-Level Changes
139
+
140
+ ### `src/lifecycle/agent-runner.ts`
141
+
142
+ 1. Add `RunnerDeps` interface (exported): `{ io: RunnerIO; exec: ShellExec; registry: AgentConfigLookup }`.
143
+ 2. Remove `exec` and `registry` from `RunContext`.
144
+ Update doc comment to reflect the 2 remaining per-call fields.
145
+ 3. Update `ConcreteAgentRunner` constructor: accept `RunnerDeps` instead of `RunnerIO`.
146
+ 4. Update `ConcreteAgentRunner.run()`: pass `this.deps` to `runAgent()`.
147
+ 5. Update `runAgent()`: change last parameter from `io: RunnerIO` to `deps: RunnerDeps`.
148
+ Replace `io.` references with `deps.io.`, `options.context.exec` with `deps.exec`, `options.context.registry` with `deps.registry`.
149
+
150
+ ### `src/lifecycle/agent-manager.ts`
151
+
152
+ 1. Remove `exec: ShellExec` and `registry: AgentTypeRegistry` from `AgentManagerOptions`.
153
+ 2. Remove `private readonly exec` and `private readonly registry` fields from `AgentManager`.
154
+ 3. Remove assignment of `this.exec` and `this.registry` in the constructor.
155
+ 4. Remove `exec: this.exec` and `registry: this.registry` from the `context` object in `startAgent()`.
156
+ 5. Remove `ShellExec` and `AgentTypeRegistry` imports (verify no other references first).
157
+
158
+ ### `src/index.ts`
159
+
160
+ 1. Move `exec` and `registry` from the `AgentManager` constructor call to `ConcreteAgentRunner`:
161
+ `new ConcreteAgentRunner({ io: runnerIO, exec: ..., registry })`.
162
+ 2. Remove `exec` and `registry` from the `AgentManager({...})` constructor argument.
163
+
164
+ ### `test/lifecycle/agent-runner.test.ts`
165
+
166
+ 1. Update all `runAgent(..., io)` calls to `runAgent(..., { io, exec, registry: mockAgentLookup })`.
167
+ 2. Remove `exec` and `registry` from `context:` objects in `RunOptions`.
168
+ `context: { exec, registry: mockAgentLookup }` → `context: {}` or `{}`.
169
+
170
+ ### `test/lifecycle/agent-runner-extension-tools.test.ts`
171
+
172
+ 1. Same pattern as `agent-runner.test.ts`: update `runAgent(..., io)` last param and strip `exec`/`registry` from `context:`.
173
+
174
+ ### `test/lifecycle/concrete-agent-runner.test.ts`
175
+
176
+ 1. Update `new ConcreteAgentRunner(io)` → `new ConcreteAgentRunner({ io, exec: vi.fn(), registry })`.
177
+ 2. Remove `exec` and `registry` from the `context:` in `runner.run()` call options.
178
+
179
+ ### `test/lifecycle/agent-manager.test.ts`
180
+
181
+ 1. Remove `exec: vi.fn()` and `registry: testRegistry` from `createManager()` factory.
182
+ 2. Remove the `testRegistry` construction and `AgentTypeRegistry` import if no other references exist.
183
+
184
+ ### `test/helpers/runner-io.ts`
185
+
186
+ 1. No structural changes needed — `createRunnerIO()` returns the `RunnerIO` shape, which is unchanged.
187
+ However, add a `createRunnerDeps()` convenience factory that bundles `{ io: createRunnerIO(), exec: vi.fn(), registry: createAgentLookup() }` for runner test files.
188
+
189
+ ### `docs/architecture/architecture.md`
190
+
191
+ 1. Update the `RunContext` code block in § "RunOptions (12 fields → extract RunContext)" to show only `cwd` and `parentSession`.
192
+ 2. Update the field-count description (4 → 2 per-call fields).
193
+ 3. Mark Step 3 as complete in the Phase 15 roadmap.
194
+
195
+ ## Test Impact Analysis
196
+
197
+ 1. No new test surfaces are needed — this is a pure mechanical refactoring (moving constructor parameters).
198
+ The existing runner and manager test suites fully cover the behavior.
199
+ 2. No existing tests become redundant — all tests exercise the same interactions, just with deps flowing through a different path.
200
+ 3. Existing `agent-manager.test.ts` tests remain as-is in coverage scope.
201
+ They verify `AgentManager` behavior (spawning, queueing, abort, etc.) independent of runner deps.
202
+ 4. Existing `agent-runner.test.ts` and `concrete-agent-runner.test.ts` tests remain.
203
+ They verify `runAgent()` and `ConcreteAgentRunner` behavior.
204
+ Call-site patterns change but assertions stay the same.
205
+
206
+ ## TDD Order
207
+
208
+ 1. **Add `RunnerDeps` interface and update `runAgent()` parameter** — define `RunnerDeps`, change `runAgent()`'s last param from `io` to `deps`, update internal references.
209
+ Update `agent-runner.test.ts` and `agent-runner-extension-tools.test.ts` call sites.
210
+ Commit: `refactor: add RunnerDeps and update runAgent parameter (#231)`
211
+
212
+ 2. **Update `ConcreteAgentRunner` to accept `RunnerDeps`** — change constructor from `RunnerIO` to `RunnerDeps`, update `.run()` to pass `this.deps`.
213
+ Update `concrete-agent-runner.test.ts`.
214
+ Add `createRunnerDeps()` helper to `test/helpers/runner-io.ts`.
215
+ Commit: `refactor: ConcreteAgentRunner accepts RunnerDeps (#231)`
216
+
217
+ 3. **Remove `exec` and `registry` from `RunContext`** — shrink the interface to 2 fields, update doc comment.
218
+ Strip `exec`/`registry` from `context:` in all runner test call sites.
219
+ Run `pnpm run check` to verify no stale references.
220
+ Commit: `refactor: remove exec and registry from RunContext (#231)`
221
+
222
+ 4. **Remove `exec` and `registry` from `AgentManager`** — remove from `AgentManagerOptions`, remove class fields, remove relay in `startAgent()`, clean up imports.
223
+ Update `agent-manager.test.ts` factory.
224
+ Commit: `refactor: remove relay deps from AgentManager (#231)`
225
+
226
+ 5. **Update wiring in `index.ts`** — move `exec` and `registry` from `AgentManager` construction to `ConcreteAgentRunner` construction.
227
+ Commit: `refactor: wire exec and registry to ConcreteAgentRunner (#231)`
228
+
229
+ 6. **Update architecture docs** — update `RunContext` description and field counts, mark Step 3 complete.
230
+ Commit: `docs: update architecture for runner self-contained (#231)`
231
+
232
+ ## Risks and Mitigations
233
+
234
+ 1. **Test churn** — ~20 `runAgent()` call sites change their last parameter pattern.
235
+ Mitigation: mechanical find-and-replace; assertions stay identical.
236
+ 2. **Step ordering** — Steps 3 and 4 both remove `exec`/`registry` from different types.
237
+ If done in the wrong order, intermediate commits may not type-check.
238
+ Mitigation: Step 1–2 add the new path (`deps`), Step 3 removes from `RunContext` (runner side), Step 4 removes from `AgentManager` (manager side), Step 5 wires them together.
239
+ Each commit is independently valid.
240
+ 3. **Import cleanup** — removing `exec`/`registry` from `AgentManager` may leave unused imports (`ShellExec`, `AgentTypeRegistry`).
241
+ Mitigation: grep for other usages before removing; `pnpm run check` catches unused imports.
242
+
243
+ ## Open Questions
244
+
245
+ - None — the issue scope is narrow and the design is straightforward.
@@ -35,3 +35,46 @@ Test count went from 977 to 986 across 62 test files.
35
35
  - Biome auto-formatted several test files during the rename commit; re-staged and re-committed.
36
36
  - Pre-completion reviewer returned **WARN** for 4 stale diagram/table references in `architecture.md` and the `package-pi-subagents` skill table; all fixed before the final commit.
37
37
  - No deviations from the plan's behavior design; the `queueSteer` removal from manager interfaces worked exactly as anticipated in the retro notes.
38
+
39
+ ## Stage: Final Retrospective (2026-05-27T17:22:00Z)
40
+
41
+ ### Session summary
42
+
43
+ Completed all stages in a single session: planning, 8 TDD steps, pre-completion review, shipping, and release as `pi-subagents-v10.1.0`.
44
+ Three behaviors (`abort`, steer buffering, worktree setup) moved from `AgentManager` to `Agent`, followed by a codebase-wide rename (33 files).
45
+
46
+ ### Observations
47
+
48
+ #### What went well
49
+
50
+ - The "add behavior first, rename last" strategy kept behavior-adding commits small (1–2 files each) and the rename commit purely mechanical.
51
+ - Planning identified that `queueSteer` could be removed from `AgentManagerLike` and `SteerToolManager` entirely — this simplified the delegation step and eliminated an unnecessary indirection layer.
52
+ - Pre-completion reviewer caught 4 stale Mermaid diagram references and a skill table entry that the plan's step 8 did not anticipate; all fixed before shipping.
53
+
54
+ #### What caused friction (agent side)
55
+
56
+ 1. `scope-drift` — Added `AgentInit` and `AgentStatus` to the `types.ts` re-export barrel during the rename step without verifying any file imports them from that path.
57
+ Impact: fallow flagged dead code, triggering a 4-call suppression trial (`unused-export` → `unused-types` → `unused-type`), then the user identified the real fix (remove the speculative re-exports entirely), requiring a follow-up `fix:` commit after docs were already done.
58
+ 2. `missing-context` — During the mechanical rename (step 7), `sed` commands matched `#test/helpers/make-record` but missed the relative import `"./helpers/make-record"` in `conversation-viewer.test.ts`.
59
+ Impact: `pnpm run check` caught it in 1 tool call; minimal rework.
60
+ 3. `missing-context` — The fallow skill documents `unused-export` as a suppression kind but not `unused-type`.
61
+ Impact: 3 wrong guesses before the correct suppression syntax.
62
+ Self-identified after fallow's error message suggested the correct kind name.
63
+
64
+ #### What caused friction (user side)
65
+
66
+ - The user's question about whether the fallow suppressions could be removed in a future step was a valuable prompt — it surfaced that the re-exports were speculative and could be removed immediately.
67
+ Earlier intervention (e.g., during the TDD stage when the suppressions were added) would have avoided the `fix:` commit.
68
+
69
+ ### Diagnostic details
70
+
71
+ - **Model-performance correlation** — Pre-completion reviewer ran as `pre-completion-reviewer` subagent (default model); appropriate for judgment-heavy work (doc staleness, code design review).
72
+ No model mismatches.
73
+ - **Feedback-loop gap analysis** — `pnpm run check` was run after every delegation step (steps 2, 4, 6, 7) and after every behavior-adding step (steps 1, 3, 5).
74
+ Verification was incremental throughout, not deferred to the end.
75
+ The `conversation-viewer.test.ts` import miss in step 7 was caught immediately by the type checker.
76
+
77
+ ### Changes made
78
+
79
+ 1. `.pi/skills/fallow/SKILL.md` — Added `unused-type` suppression example alongside existing `unused-export` example.
80
+ 2. `AGENTS.md` — Added "no speculative re-exports" rule to Code Style section.
@@ -0,0 +1,80 @@
1
+ ---
2
+ issue: 228
3
+ issue_title: "Convert startAgent to async/await, move run lifecycle to Agent (Phase 15, Step 2)"
4
+ ---
5
+
6
+ # Retro: #228 — Convert startAgent to async/await, move run lifecycle to Agent
7
+
8
+ ## Stage: Planning (2026-05-27T20:00:00Z)
9
+
10
+ ### Session summary
11
+
12
+ Planned the async `startAgent` conversion and decided to dissolve `RunHandle` into Agent methods rather than moving it as a separate class.
13
+ Identified three preparatory steps (narrow promise type, add Agent methods, hoist worktree setup) that make the final async conversion a minimal diff.
14
+
15
+ ### Observations
16
+
17
+ - The original issue proposed `Agent.createRunHandle()` as a factory, keeping RunHandle as a separate class.
18
+ Analysis showed 5 of 6 RunHandle concerns are Agent state mutations — RunHandle is doing work that belongs on Agent.
19
+ The clincher was `resume()` in `agent-manager.ts`: it duplicates RunHandle's pattern manually, and #232 wants to unify them.
20
+ Dissolving RunHandle gives both `startAgent` and `resume` the same primitives (`completeRun`, `failRun`, `releaseListeners`).
21
+ - The synchronous-throw contract in `spawn()` for worktree failures requires hoisting `record.setupWorktree()` out of `startAgent` before the async conversion.
22
+ Without this prep step, async `startAgent` would turn the throw into a rejected promise that `spawn()` doesn't catch.
23
+ - `promise: Promise<string>` → `Promise<void>` is safe because the resolved string is dead — every consumer reads `record.result` instead.
24
+ Only one test assertion reads the resolved value.
25
+ - `completeRun`/`failRun` take `worktrees: WorktreeManager` as a parameter rather than storing it on Agent (ISP — only needed at run end, exactly two callers).
26
+
27
+ ## Stage: Implementation — TDD (2026-05-27T20:40:00Z)
28
+
29
+ ### Session summary
30
+
31
+ Implemented all 6 TDD steps: narrowed `promise` to `Promise<void>`, added 6 run lifecycle methods to Agent (+19 tests), replaced `RunHandle` with Agent methods (-85 LOC), hoisted worktree setup to callers, converted `startAgent` to async/await, and updated architecture docs.
32
+ Test count: 986 → 1005.
33
+
34
+ ### Observations
35
+
36
+ - Step 1 (promise narrowing) required fixing 3 additional test files not listed in the plan: `make-agent.test.ts`, `service-adapter.test.ts`, `get-result-tool.test.ts`.
37
+ All were trivial `Promise.resolve("done")` → `Promise.resolve()` changes and a cast removal.
38
+ - The lift-and-shift approach worked cleanly — each of the 5 implementation commits was small and independently green.
39
+ The most impactful commit was step 3 (replace RunHandle, -96/+6 lines) which was risk-free because step 2 had already introduced the Agent methods.
40
+ - Pre-completion reviewer returned WARN for stale `AgentRecord` and `run-handle.ts` references in `architecture.md` class diagram and layout listing.
41
+ These were pre-existing staleness from #227's rename that wasn't fully propagated to Mermaid diagrams.
42
+ Fixed by amending the docs commit.
43
+
44
+ ## Stage: Final Retrospective (2026-05-27T21:46:00Z)
45
+
46
+ ### Session summary
47
+
48
+ Completed all stages (plan, TDD, ship, retro) in a single session.
49
+ Dissolved `RunHandle` into 6 Agent methods, converted `startAgent` to async/await, released as `pi-subagents-v10.2.0`.
50
+ Test delta: 986 → 1005 (+19).
51
+
52
+ ### Observations
53
+
54
+ #### What went well
55
+
56
+ - The user's two redirecting questions during planning ("What's the change that makes this easier?"
57
+ and "Tell me more about RunHandle — is there something that should replace it?") transformed a mechanical "move the class" plan into a "dissolve the abstraction" plan.
58
+ The dissolve approach is architecturally superior and sets up #232 (resume unification) for free.
59
+ - The lift-and-shift decomposition (introduce Agent methods alongside `RunHandle`, then swap and delete) produced 5 independently-green commits.
60
+ The riskiest commit (step 3: delete `RunHandle`, -96/+6 lines) was trivially safe because step 2 had already proven the replacement methods.
61
+
62
+ #### What caused friction (agent side)
63
+
64
+ - `premature-convergence` — The agent planned around the issue's proposed `Agent.createRunHandle()` factory without questioning whether `RunHandle` should exist as a separate class.
65
+ The user had to ask two redirecting questions to push the analysis deeper.
66
+ Impact: plan was rewritten before commit (no wasted implementation), but the user spent two turns guiding analysis the agent should have done proactively.
67
+ - `missing-context` — Plan step 1 (narrow `Promise<string>` to `Promise<void>`) listed only `agent-manager.test.ts` for updates but missed 3 additional test files (`make-agent.test.ts`, `service-adapter.test.ts`, `get-result-tool.test.ts`) that construct `Promise<string>` values.
68
+ The testing skill says "grep for all test files" for type changes — this was not applied during planning.
69
+ Impact: caught by `pnpm run check` in the same step, no rework.
70
+
71
+ #### What caused friction (user side)
72
+
73
+ - No friction observed.
74
+ The user's questioning style (asking "what does it do?
75
+ who needs it?"
76
+ rather than prescribing the solution) was collaborative and effective.
77
+
78
+ ### Changes made
79
+
80
+ 1. `.pi/prompts/plan-issue.md` — added relocation-dissolution heuristic: when an issue proposes moving a class, list callers and fields touched to check if it should be dissolved into the owner instead.
@@ -0,0 +1,40 @@
1
+ ---
2
+ issue: 231
3
+ issue_title: "Push exec/registry relay deps to runner construction (Phase 15, Step 3)"
4
+ ---
5
+
6
+ # Retro: #231 — Push exec/registry relay deps to runner construction
7
+
8
+ ## Stage: Planning (2026-05-27T21:53:10Z)
9
+
10
+ ### Session summary
11
+
12
+ Produced a 6-step TDD plan to move `exec` and `registry` from `AgentManager` to `ConcreteAgentRunner` via a new `RunnerDeps` interface.
13
+ The plan keeps `RunContext` (shrunk to 2 per-call fields) rather than dissolving it — #229 will likely dissolve it when `Agent.run()` calls the runner directly.
14
+
15
+ ### Observations
16
+
17
+ - Confirmed `exec` and `registry` are pure relay deps on `AgentManager` — stored at construction, used only at lines 193–194 to forward into `runner.run()`.
18
+ - Chose `RunnerDeps` bag over separate positional params on `ConcreteAgentRunner` and `runAgent()` — groups all three runner-owned deps (`io`, `exec`, `registry`) in one interface, and `runAgent()` stays at 5 parameters.
19
+ - `AgentManagerOptions.registry` uses the concrete `AgentTypeRegistry` class; `RunContext.registry` uses the narrow `AgentConfigLookup` interface.
20
+ The new `RunnerDeps.registry` uses `AgentConfigLookup` (ISP).
21
+ - Test churn is moderate (~20 `runAgent()` call sites change last param pattern) but mechanical — assertions stay identical.
22
+ - Added a `createRunnerDeps()` test helper to `runner-io.ts` to reduce per-file boilerplate in runner tests.
23
+
24
+ ## Stage: Implementation — TDD (2026-05-27T22:05:32Z)
25
+
26
+ ### Session summary
27
+
28
+ Implemented the 6-step plan in 4 commits (steps 3–5 merged).
29
+ All 1005 tests pass; no test count change.
30
+ Pre-completion reviewer returned PASS.
31
+
32
+ ### Observations
33
+
34
+ - Plan steps 3, 4, and 5 could not be separate commits: removing `exec`/`registry` from `RunContext` (step 3) immediately caused TypeScript excess-property errors in `AgentManager` (step 4) and `index.ts` (step 5).
35
+ Merged all three into one commit.
36
+ The testing skill’s rule “when a TDD step changes an interface that has a single call site, the step must include updating that call site” applies.
37
+ - Shrinking `RunContext` to all-optional fields made pre-existing `as never` casts in `test/helpers/manager-stubs.test.ts` unnecessary (eslint `no-unnecessary-type-assertion`).
38
+ Fixed as a lint cleanup in the doc commit.
39
+ - The `sed`-based bulk replacement for `runAgent(..., io)` → `runAgent(..., { io, exec, registry: mockAgentLookup })` missed one multi-line call site (the `rejects.toThrow` test wrapping the call in `expect()`).
40
+ Caught immediately by the test run.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gotgenes/pi-subagents",
3
- "version": "10.1.0",
3
+ "version": "10.2.1",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": "./src/service.ts"
package/src/index.ts CHANGED
@@ -24,7 +24,7 @@ import { AgentTypeRegistry } from "#src/config/agent-types";
24
24
  import { loadCustomAgents } from "#src/config/custom-agents";
25
25
  import { SessionLifecycleHandler, ToolStartHandler } from "#src/handlers/index";
26
26
  import { AgentManager, type AgentManagerObserver } from "#src/lifecycle/agent-manager";
27
- import { ConcreteAgentRunner, type RunnerIO } from "#src/lifecycle/agent-runner";
27
+ import { ConcreteAgentRunner, type RunnerDeps } from "#src/lifecycle/agent-runner";
28
28
  import { buildParentSnapshot } from "#src/lifecycle/parent-snapshot";
29
29
  import { GitWorktreeManager } from "#src/lifecycle/worktree";
30
30
  import { buildEventData, type NotificationDetails, NotificationManager } from "#src/observation/notification";
@@ -132,25 +132,27 @@ export default function (pi: ExtensionAPI) {
132
132
  },
133
133
  };
134
134
 
135
- const runnerIO: RunnerIO = {
136
- detectEnv,
137
- getAgentDir,
138
- createResourceLoader: (opts) => new DefaultResourceLoader(opts),
139
- deriveSessionDir: deriveSubagentSessionDir,
140
- createSessionManager: (cwd, dir) => SessionManager.create(cwd, dir),
141
- createSettingsManager: (cwd, dir) => SdkSettingsManager.create(cwd, dir),
142
- createSession: (opts) => createAgentSession(opts as any),
143
- assemblerIO: {
144
- preloadSkills,
145
- buildAgentPrompt,
135
+ const runnerDeps: RunnerDeps = {
136
+ io: {
137
+ detectEnv,
138
+ getAgentDir,
139
+ createResourceLoader: (opts) => new DefaultResourceLoader(opts),
140
+ deriveSessionDir: deriveSubagentSessionDir,
141
+ createSessionManager: (cwd, dir) => SessionManager.create(cwd, dir),
142
+ createSettingsManager: (cwd, dir) => SdkSettingsManager.create(cwd, dir),
143
+ createSession: (opts) => createAgentSession(opts as any),
144
+ assemblerIO: {
145
+ preloadSkills,
146
+ buildAgentPrompt,
147
+ },
146
148
  },
149
+ exec: (cmd, args, opts) => pi.exec(cmd, args, opts),
150
+ registry,
147
151
  };
148
152
 
149
153
  const manager = new AgentManager({
150
- runner: new ConcreteAgentRunner(runnerIO),
154
+ runner: new ConcreteAgentRunner(runnerDeps),
151
155
  worktrees: new GitWorktreeManager(process.cwd()),
152
- exec: (cmd, args, opts) => pi.exec(cmd, args, opts),
153
- registry,
154
156
  observer,
155
157
  getMaxConcurrent: () => settings.maxConcurrent,
156
158
  getRunConfig: () => settings,