@gotgenes/pi-subagents 6.10.0 → 6.12.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.
@@ -0,0 +1,366 @@
1
+ ---
2
+ issue: 134
3
+ issue_title: "Reduce `as any` casts in test suite"
4
+ ---
5
+
6
+ # Reduce as-any casts in test suite
7
+
8
+ ## Problem Statement
9
+
10
+ The test suite contains 93 `as any` casts (plus 2 `as any[]`).
11
+ These casts silence the type checker, hiding real errors — if a production interface adds a required field, tests silently pass with incomplete mocks instead of failing at compile time.
12
+ The heaviest offenders are `renderer.test.ts` (29), `runtime.test.ts` (10), `agent-menu.test.ts` (8), and `helpers.test.ts` (7).
13
+ The production source has 8 `as any` casts — 2 SDK bridge casts in `index.ts` and 6 message-shape casts in `conversation-viewer.ts` and `agent-runner.ts`.
14
+
15
+ ## Goals
16
+
17
+ - Target near-zero `as any` casts across both source and test code.
18
+ - Widen `CreateSessionOptions` and `ResourceLoaderOptions` to use SDK types, eliminating the SDK bridge casts in `index.ts`.
19
+ - Define local message-content types and type guards in `conversation-viewer.ts` and `agent-runner.ts`, eliminating polymorphic duck-typing casts.
20
+ - Define narrow production interfaces (`MenuCtx`, `WidgetLike`) where SDK types are too wide for test construction.
21
+ - Define typed test factories where partial mocks currently require casting.
22
+ - Preserve existing test coverage — no behavior changes.
23
+
24
+ ## Non-Goals
25
+
26
+ - Changing production behavior or public API shapes.
27
+ - Reaching literally zero — 1–3 casts may remain for private-field test access (`(manager as any).cleanupInterval`) where the alternative (exposing internals) is worse than the cast.
28
+
29
+ ## Background
30
+
31
+ ### Prerequisites
32
+
33
+ Issues #132 and #133 (IO injection) are both closed.
34
+ These eliminated the `vi.mock()`-heavy test patterns and introduced narrow injectable interfaces (`AssemblerIO`, `RunnerIO`), which already removed some `as any` casts from `agent-runner.test.ts` and `session-config.test.ts`.
35
+
36
+ ### Current as-any inventory by pattern
37
+
38
+ | Pattern | Count | Primary files |
39
+ | ------------------------------------------------ | ----- | ------------------------------------------------------------------------------------------------------------------- |
40
+ | Renderer — theme, message, result access | 29 | `renderer.test.ts` |
41
+ | Context — `ctx as any` for handler/menu | 14 | `agent-menu.test.ts`, `agent-manager.test.ts`, `print-mode.test.ts`, `parent-snapshot.test.ts`, `make-deps.test.ts` |
42
+ | Session — `{ session: {} as any }` | 10 | tool tests, `service-adapter.test.ts`, `print-mode.test.ts`, `agent-manager.test.ts` |
43
+ | Runtime/widget — `fakeWidget as any` | 9 | `runtime.test.ts` |
44
+ | Conversation viewer — message shapes | 8 | `conversation-viewer.test.ts`, `src/conversation-viewer.ts` |
45
+ | Helpers — registry, activity, details | 7 | `helpers.test.ts` |
46
+ | RunOptions — `} as any, io)` | 3 | `agent-runner.test.ts` |
47
+ | Tool execute — `{} as any` for ctx | 5 | `steer-tool.test.ts`, `get-result-tool.test.ts`, `make-deps.test.ts` |
48
+ | SDK bridge — `opts as any` in index.ts | 2 | `src/index.ts` |
49
+ | Other — `messages: [] as any[]`, internal access | 6 | `agent-runner*.test.ts`, `usage.test.ts`, `agent-manager.test.ts` |
50
+
51
+ ### Constraints from AGENTS.md
52
+
53
+ - Avoid `any` unless absolutely necessary.
54
+ - Prefer explicit configuration over hidden behavior.
55
+ - Keep scope tight; prefer small, reversible changes.
56
+
57
+ ## Design Overview
58
+
59
+ ### Production changes that make the test changes easy
60
+
61
+ Three targeted source-code changes remove the structural blockers that force casts elsewhere.
62
+
63
+ #### 1. SDK-typed option interfaces (eliminates 2 source casts)
64
+
65
+ `CreateSessionOptions` and `ResourceLoaderOptions` currently use `unknown` for opaque fields (`settingsManager`, `modelRegistry`, `model`).
66
+ Tests never construct these option objects — they mock `io.createSession` and `io.createResourceLoader` at the function level.
67
+ The `unknown` fields don't buy testability; the function-level mock does.
68
+
69
+ The SDK exports `CreateAgentSessionOptions` with all fields optional, using `ModelRegistry`, `Model<any>`, `ResourceLoader`, `SessionManager`, `SettingsManager`.
70
+ Widening `CreateSessionOptions` to use these SDK types makes the `as any` cast in `index.ts` unnecessary while preserving full test mockability.
71
+
72
+ ```typescript
73
+ // Before (agent-runner.ts)
74
+ export interface CreateSessionOptions {
75
+ settingsManager: unknown;
76
+ modelRegistry: unknown;
77
+ model?: unknown;
78
+ // ...
79
+ }
80
+
81
+ // After — use SDK types (type-only imports)
82
+ import type { Model } from "@earendil-works/pi-ai";
83
+ import type {
84
+ ModelRegistry,
85
+ ResourceLoader,
86
+ SessionManager,
87
+ SettingsManager,
88
+ } from "@earendil-works/pi-coding-agent";
89
+
90
+ export interface CreateSessionOptions {
91
+ settingsManager: SettingsManager;
92
+ modelRegistry: ModelRegistry;
93
+ model?: Model<any>;
94
+ resourceLoader: ResourceLoader;
95
+ sessionManager: SessionManager;
96
+ // ...
97
+ }
98
+ ```
99
+
100
+ The `RunnerIO.createSettingsManager` return type also changes from `unknown` to `SettingsManager`.
101
+
102
+ For `ResourceLoaderOptions`, the options are all primitives and callbacks — the SDK's `DefaultResourceLoader` constructor accepts them structurally.
103
+ The fix is to ensure the option interface matches the constructor's parameter type so `new DefaultResourceLoader(opts)` works without `as any`.
104
+
105
+ #### 2. Local message-content types + type guards (eliminates 6 source casts)
106
+
107
+ `conversation-viewer.ts` and `agent-runner.ts` both do:
108
+
109
+ ```typescript
110
+ (c as any).name ?? (c as any).toolName ?? "unknown"
111
+ (msg as any).role === "bashExecution"
112
+ const bash = msg as any;
113
+ ```
114
+
115
+ Fix: define local discriminated-union types for the content items and message roles the code actually handles, plus type guards:
116
+
117
+ ```typescript
118
+ /** Tool-call content item — SDK doesn't export a narrow type for this variant. */
119
+ interface ToolCallContent {
120
+ type: "toolCall";
121
+ name?: string;
122
+ toolName?: string;
123
+ }
124
+
125
+ function getToolCallName(c: { type: string }): string | undefined {
126
+ if (c.type !== "toolCall") return undefined;
127
+ const tc = c as ToolCallContent;
128
+ return tc.name ?? tc.toolName;
129
+ }
130
+
131
+ /** Bash execution message — not in the standard role union. */
132
+ interface BashExecutionMessage {
133
+ role: "bashExecution";
134
+ command: string;
135
+ output?: string;
136
+ }
137
+
138
+ function isBashExecution(msg: { role: string }): msg is BashExecutionMessage {
139
+ return msg.role === "bashExecution";
140
+ }
141
+ ```
142
+
143
+ These are small, file-local types that document the shapes the code already handles at runtime.
144
+ They make the duck-typing explicit and compile-time checked.
145
+
146
+ #### 3. Narrow production interfaces (`MenuCtx`, `WidgetLike`)
147
+
148
+ Already described in the test-side patterns below.
149
+ These are production-code changes that make the test-side casts disappear.
150
+
151
+ ### Test-side patterns
152
+
153
+ #### Pattern A: renderer.test.ts (29 casts → 0)
154
+
155
+ The renderer already defines narrow interfaces (`RendererMessage`, `RendererTheme`, `RenderOptions`).
156
+ The test casts because it doesn't use them — `stubTheme()` and `{ details: makeDetails() }` already satisfy the interfaces structurally.
157
+ The return type `Text` has a `.text` property — `(result as any).text` can become `result!.text` (non-null assertion after `expect(result).toBeDefined()`).
158
+
159
+ Fix: remove the casts; structural typing handles it.
160
+ For result access, define a minimal `{ text: string }` type or use non-null assertion.
161
+
162
+ #### Pattern B: context casts (14 casts → 0)
163
+
164
+ - `agent-menu.test.ts` (8): define `MenuCtx` in production; type `makeCtx()` return.
165
+ - `agent-manager.test.ts` (1): `mockCtx` is consumed by a mocked `buildParentSnapshot` — type as `unknown`.
166
+ - `parent-snapshot.test.ts` (1): define a test-local interface matching the fields `buildParentSnapshot` reads.
167
+ - `print-mode.test.ts` (2): type `makeHeadlessCtx()` return.
168
+ - `runtime.test.ts` (1): pass a structurally valid `UICtx` (fixed by `WidgetLike` step).
169
+ - `make-deps.test.ts` (3): type ctx args or use `unknown`.
170
+
171
+ #### Pattern C: session casts (10 casts → ~1)
172
+
173
+ Most are `{ session: {} as any, outputFile: ... }` for `record.execution`.
174
+ Fix: expand `createMockSession()` to include the fields these tests need (`dispose`, `steer`, `getSessionStats`), then use it.
175
+ One cast may remain for truly minimal session stubs.
176
+
177
+ #### Pattern D: runtime/widget casts (9 casts → 0)
178
+
179
+ Define `WidgetLike` in `runtime.ts`; change the `widget` field type.
180
+ Use real `AgentActivityTracker` instances (constructor takes only `maxTurns?`).
181
+
182
+ #### Pattern E: tool-execute ctx casts (5 casts → 0)
183
+
184
+ Define a `STUB_CTX` constant in `test/helpers/` satisfying the tool execute's context parameter.
185
+
186
+ #### Pattern F: RunOptions casts (3 casts → 0)
187
+
188
+ Check whether `defaultMaxTurns` and `graceTurns` are on `RunOptions` — they are.
189
+ The `as any` casts are vestigial; remove them.
190
+
191
+ #### Pattern G: helpers.test.ts (7 casts → 0)
192
+
193
+ Construct typed `TypeListRegistry` mocks.
194
+ Fix `textResult` return type to avoid `details as any`.
195
+ Use real `AgentActivityTracker` instances.
196
+
197
+ #### Pattern H: conversation-viewer test casts (4 test casts → ~1)
198
+
199
+ Type message mock objects using the local types from production change #2.
200
+ Keep `(viewer as any).buildContentLines()` (private method access — the alternative of exposing the method is worse).
201
+
202
+ #### Pattern I: other (6 casts → ~2)
203
+
204
+ - `messages: [] as any[]` → type as `unknown[]`.
205
+ - `(manager as any).cleanupInterval` → keep (private field assertion is intentional).
206
+ - `usage.test.ts` (2) → type mock objects.
207
+
208
+ ## Module-Level Changes
209
+
210
+ ### Modified source files
211
+
212
+ 1. `src/agent-runner.ts`
213
+ - Import SDK types (`Model`, `ModelRegistry`, `SettingsManager`, `SessionManager`, `ResourceLoader`) as type-only imports.
214
+ - Widen `CreateSessionOptions` fields from `unknown` to SDK types.
215
+ - Change `RunnerIO.createSettingsManager` return type from `unknown` to `SettingsManager`.
216
+ - Define `getToolCallName()` helper and local `ToolCallContent` interface.
217
+ - Replace `(c as any).name ?? (c as any).toolName` with `getToolCallName(c)` in `getAgentConversation`.
218
+
219
+ 2. `src/ui/conversation-viewer.ts`
220
+ - Define local `ToolCallContent`, `BashExecutionMessage` interfaces and type guards.
221
+ - Replace `(c as any).name ?? (c as any).toolName` with typed helper.
222
+ - Replace `(msg as any).role === "bashExecution"` / `const bash = msg as any` with type guard.
223
+
224
+ 3. `src/ui/agent-menu.ts`
225
+ - Define and export `MenuCtx` interface.
226
+ - Change handler parameter type from `ExtensionContext` to `MenuCtx`.
227
+
228
+ 4. `src/runtime.ts`
229
+ - Define and export `WidgetLike` interface.
230
+ - Change `widget` field type from `AgentWidget | null` to `WidgetLike | null`.
231
+
232
+ 5. `src/tools/helpers.ts`
233
+ - Change `textResult` to avoid `details as any` — type the return properly.
234
+
235
+ 6. `src/index.ts`
236
+ - Remove `opts as any` casts in `createResourceLoader` and `createSession` factories (enabled by SDK-typed option interfaces).
237
+
238
+ ### Modified test files
239
+
240
+ 1. `test/renderer.test.ts` — remove all 29 casts.
241
+ 2. `test/runtime.test.ts` — use `WidgetLike` stubs and real `AgentActivityTracker`.
242
+ 3. `test/ui/agent-menu.test.ts` — type `makeCtx()` as `MenuCtx`.
243
+ 4. `test/tools/helpers.test.ts` — typed registry mocks, real `AgentActivityTracker`.
244
+ 5. `test/conversation-viewer.test.ts` — typed message mocks.
245
+ 6. `test/agent-runner.test.ts` — remove `as any` on RunOptions; type messages.
246
+ 7. `test/agent-runner-extension-tools.test.ts` — type messages.
247
+ 8. `test/agent-manager.test.ts` — type `mockCtx`.
248
+ 9. `test/print-mode.test.ts` — type `makeHeadlessCtx()`.
249
+ 10. `test/parent-snapshot.test.ts` — type mock context.
250
+ 11. `test/service-adapter.test.ts` — use `createMockSession()`.
251
+ 12. `test/tools/steer-tool.test.ts` — stub ctx, use `createMockSession()`.
252
+ 13. `test/tools/get-result-tool.test.ts` — stub ctx, use `createMockSession()`.
253
+ 14. `test/tools/foreground-runner.test.ts` — use `createMockSession()`.
254
+ 15. `test/tools/background-spawner.test.ts` — use `createMockSession()`.
255
+ 16. `test/tools/agent-tool.test.ts` — use `createMockSession()`.
256
+ 17. `test/helpers/make-deps.test.ts` — type ctx args.
257
+ 18. `test/usage.test.ts` — type mock objects.
258
+
259
+ ## Test Impact Analysis
260
+
261
+ 1. No new test coverage — this is a type-safety improvement on existing tests.
262
+ 2. No tests become redundant.
263
+ 3. All existing tests stay as-is in terms of assertions; only type annotations and mock construction change.
264
+ 4. `pnpm run check` is the primary validation — every step must pass type checking since the goal is eliminating type holes.
265
+ 5. Expanding `createMockSession()` affects multiple consumers — diff existing defaults before changing (per testing skill).
266
+
267
+ ## TDD Order
268
+
269
+ Steps are ordered by independence and impact (production changes first, then largest cast-count test reductions).
270
+
271
+ 1. **Widen option interfaces to SDK types; remove index.ts casts (2 source casts → 0).**
272
+ Import SDK types as type-only in `agent-runner.ts`.
273
+ Widen `CreateSessionOptions` fields (`settingsManager`, `modelRegistry`, `model`, `resourceLoader`, `sessionManager`) to SDK types.
274
+ Change `RunnerIO.createSettingsManager` return type to `SettingsManager`.
275
+ Remove `as any` casts from `index.ts` factories.
276
+ Run `pnpm run check` + full suite.
277
+ Commit: `feat: use SDK types in CreateSessionOptions (#134)`
278
+
279
+ 2. **Add message-content type guards; remove viewer and runner source casts (6 source casts → 0).**
280
+ Define `ToolCallContent` interface and `getToolCallName()` helper in `conversation-viewer.ts`.
281
+ Define `BashExecutionMessage` interface and `isBashExecution()` guard in `conversation-viewer.ts`.
282
+ Replace all source `as any` casts in `conversation-viewer.ts`.
283
+ Define the same `getToolCallName()` helper (or extract a shared one) in `agent-runner.ts` and replace its cast.
284
+ Run `pnpm run check` + affected tests.
285
+ Commit: `fix: replace message-shape as-any casts with type guards (#134)`
286
+
287
+ 3. **Remove renderer test casts (29 → 0).**
288
+ Remove all `as any` casts on `stubTheme()`, message objects, and result access.
289
+ Use `result!.text` (non-null assertion after `toBeDefined()` guard) for result access.
290
+ Run `pnpm run check` + `pnpm vitest run test/renderer.test.ts`.
291
+ Commit: `test: remove as-any casts in renderer tests (#134)`
292
+
293
+ 4. **Add `MenuCtx` interface; remove menu-test casts (8 → 0).**
294
+ Define `MenuCtx` in `agent-menu.ts`.
295
+ Change handler parameter from `ExtensionContext` to `MenuCtx`.
296
+ Type `makeCtx()` return in test.
297
+ Run `pnpm run check` + `pnpm vitest run test/ui/agent-menu.test.ts`.
298
+ Commit: `feat: narrow menu handler to MenuCtx interface (#134)`
299
+
300
+ 5. **Add `WidgetLike`; remove runtime casts (9 → 0).**
301
+ Define `WidgetLike` in `runtime.ts`.
302
+ Change `widget` field type.
303
+ Update `runtime.test.ts`: typed stubs, real `AgentActivityTracker` instances.
304
+ Run `pnpm run check` + `pnpm vitest run test/runtime.test.ts`.
305
+ Commit: `feat: narrow runtime widget field to WidgetLike interface (#134)`
306
+
307
+ 6. **Expand `createMockSession`; remove session casts (10 → ~1).**
308
+ Add fields to `createMockSession()` that tool/service tests need.
309
+ Use it in tool tests, service-adapter, agent-manager for `record.execution.session`.
310
+ Run `pnpm run check` + full suite.
311
+ Commit: `test: use createMockSession for session execution casts (#134)`
312
+
313
+ 7. **Remove helpers.test.ts casts (7 → 0).**
314
+ Typed `TypeListRegistry` mocks.
315
+ Fix `textResult` return type in `src/tools/helpers.ts`.
316
+ Real `AgentActivityTracker` instances.
317
+ Run `pnpm run check` + `pnpm vitest run test/tools/helpers.test.ts`.
318
+ Commit: `test: remove as-any casts in helpers tests (#134)`
319
+
320
+ 8. **Remove context casts across remaining test files (6 → 0).**
321
+ Type `mockCtx` in `agent-manager.test.ts`.
322
+ Type `makeHeadlessCtx()` in `print-mode.test.ts`.
323
+ Type mock context in `parent-snapshot.test.ts`.
324
+ Type `{} as any` in `make-deps.test.ts`.
325
+ Run `pnpm run check` + full suite.
326
+ Commit: `test: remove remaining context as-any casts (#134)`
327
+
328
+ 9. **Remove RunOptions and message-array casts (5 → 0).**
329
+ Remove vestigial `as any` on RunOptions objects.
330
+ Type `messages` arrays as `unknown[]`.
331
+ Run `pnpm run check` + affected tests.
332
+ Commit: `test: remove RunOptions and message-array casts (#134)`
333
+
334
+ 10. **Remove tool-execute ctx casts (5 → 0).**
335
+ Define `STUB_CTX` in `test/helpers/`.
336
+ Use it in `steer-tool.test.ts`, `get-result-tool.test.ts`, `make-deps.test.ts`.
337
+ Run `pnpm run check` + affected tests.
338
+ Commit: `test: remove tool-execute context casts (#134)`
339
+
340
+ 11. **Clean up conversation-viewer test and usage casts (6 → ~2).**
341
+ Type message mocks in `conversation-viewer.test.ts`.
342
+ Type usage mock objects in `usage.test.ts`.
343
+ Keep `(viewer as any).buildContentLines()` and `(manager as any).cleanupInterval` (private access, intentional).
344
+ Run `pnpm run check` + full suite.
345
+ Commit: `test: remove conversation-viewer and usage as-any casts (#134)`
346
+
347
+ ## Risks and Mitigations
348
+
349
+ | Risk | Mitigation |
350
+ | ----------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
351
+ | Widening `CreateSessionOptions` to SDK types re-couples `agent-runner.ts` to SDK | These are type-only imports — no runtime coupling. Tests still mock at the `RunnerIO` function level. The `RunnerIO` interface is the decoupling boundary, not the option types. |
352
+ | Narrowing `widget` to `WidgetLike` could break callers accessing `AgentWidget`-specific methods | Grep all `runtime.widget` access sites first; verify they only use `update()`, `markFinished()`, `setUICtx()`. |
353
+ | Narrowing menu handler to `MenuCtx` could break callers passing `ExtensionContext` | `ExtensionContext` is structurally a superset of `MenuCtx` — callers pass it unchanged. |
354
+ | Expanding `createMockSession()` could break consumers with different default expectations | Diff existing consumers' default expectations before adding fields (per testing skill). |
355
+ | Message type guards add code to conversation-viewer and agent-runner | Each guard is 2–4 lines; they replace unsafe `as any` access with documented, compile-checked types. Net code stays similar. |
356
+ | 11-step plan is large | Each step is independently committable. No step depends on a subsequent one. Steps can be reordered or skipped. |
357
+
358
+ ## Open Questions
359
+
360
+ - Should `getToolCallName()` be shared between `conversation-viewer.ts` and `agent-runner.ts`, or duplicated?
361
+ Both files handle the same SDK message shape.
362
+ A shared helper in a common module (e.g., `context.ts` which already has `extractText`) avoids duplication.
363
+ Alternatively, the duplication is cheap (3 lines) and the two files have different concerns.
364
+ - Should `STUB_CTX` live in `test/helpers/stub-ctx.ts` or inline?
365
+ Centralize — 3+ tool tests share the pattern.
366
+ - Estimated final count: 2–3 remaining casts (`(viewer as any).buildContentLines()`, `(manager as any).cleanupInterval`, possibly 1 more for an unexported SDK type).
@@ -0,0 +1,33 @@
1
+ ---
2
+ issue: 132
3
+ issue_title: "Inject IO collaborators into `assembleSessionConfig`"
4
+ ---
5
+
6
+ # Retro: #132 — Inject IO collaborators into `assembleSessionConfig`
7
+
8
+ ## Final Retrospective (2026-05-22T12:25:00Z)
9
+
10
+ ### Session summary
11
+
12
+ Defined an `AssemblerIO` interface bundling four IO/prompt collaborators, injected it into `assembleSessionConfig`, and updated `agent-runner.ts` to pass real implementations.
13
+ Eliminated all 4 `vi.mock()` calls in `session-config.test.ts`, flattened the `vi.hoisted()` block into plain `vi.fn()` declarations, and shifted assertions from mock-call verification to output-property checks.
14
+ Released as `pi-subagents-v6.10.0`.
15
+
16
+ ### Observations
17
+
18
+ #### What went well
19
+
20
+ - Perl two-pass replacement (multi-line then single-line) handled 40+ `assembleSessionConfig` call-site updates in one command with zero manual errors.
21
+ - Flattening `vi.hoisted()` into regular `vi.fn()` declarations in step 3 was a clean simplification — hoisting was only needed when the mocks were referenced inside `vi.mock()` factories.
22
+ - Real `getMemoryToolNames` / `getReadOnlyMemoryToolNames` worked as drop-in replacements with no test rework needed — the pure functions' behavior matched what the mocks were configured to return for all existing test scenarios.
23
+
24
+ #### What caused friction (agent side)
25
+
26
+ - `missing-context` — `mockBuildAgentPrompt` was declared as `vi.fn(() => "assembled system prompt")` which inferred `Mock<() => string>`.
27
+ When step 4 used `mockImplementationOnce` with a parameterized function, TypeScript rejected it.
28
+ The testing skill already documents `Mock<specific-signature>` for this exact case.
29
+ Impact: one type-check failure, fixed by adding `Mock<AssemblerIO["buildAgentPrompt"]>` annotation; added friction but no rework.
30
+
31
+ #### What caused friction (user side)
32
+
33
+ - Nothing notable — standard prompt-template workflow with no corrections needed.
@@ -0,0 +1,45 @@
1
+ ---
2
+ issue: 133
3
+ issue_title: "Inject SDK boundary into `agent-runner`"
4
+ ---
5
+
6
+ # Retro: #133 — Inject SDK boundary into agent-runner
7
+
8
+ ## Final Retrospective (2026-05-22T13:15:00Z)
9
+
10
+ ### Session summary
11
+
12
+ Injected all SDK and IO dependencies into `runAgent()` via a `RunnerIO` interface and `createAgentRunner(io)` factory.
13
+ Eliminated all 14 `vi.mock()` calls across `agent-runner.test.ts` (7) and `agent-runner-extension-tools.test.ts` (7).
14
+ Released as `pi-subagents-v6.11.0`.
15
+
16
+ ### Observations
17
+
18
+ #### What went well
19
+
20
+ - The `createAgentRunner(io)` factory pattern was a clean design choice that kept the `AgentRunner` interface and `AgentManager` completely unchanged — zero downstream impact.
21
+ - Folding plan steps 1 and 2 into a single commit was the right call given `tsconfig.json` includes `test/` — recognized the constraint before attempting a broken intermediate commit.
22
+
23
+ #### What caused friction (agent side)
24
+
25
+ 1. `wrong-abstraction` — Annotated `createRunnerIO(): RunnerIO` in the test helper, which erased the `Mock<...>` type information from `vi.fn()` stubs.
26
+ TypeScript then rejected `.mockResolvedValue()` on `io.createSession` across 18 call sites.
27
+ Required removing the annotation plus a follow-up edit to remove the now-unused `type RunnerIO` import flagged by Biome.
28
+ Impact: two extra edit rounds and a type-check cycle before the fix landed.
29
+
30
+ 2. `missing-context` — Added `SettingsManager` to the SDK import block in `index.ts` without checking that the name was already imported from `./settings.js`.
31
+ Biome caught the redeclaration and the `noRedeclare` lint error required an alias fix (`SettingsManager as SdkSettingsManager`).
32
+ Impact: one extra edit round triggered by the autoformat failure.
33
+
34
+ 3. `premature-convergence` — Spent excessive reasoning time deliberating commit-boundary strategy (whether to combine steps 1+2, how to handle broken intermediate states, whether step 4 would have remaining work).
35
+ The answer was straightforward once the `tsconfig` `include` was checked, but the check came late in the deliberation.
36
+ Impact: added friction but no rework — the final decision was correct.
37
+
38
+ #### What caused friction (user side)
39
+
40
+ - None identified.
41
+ The plan and issue were well-specified, and the user's only intervention was "Please, continue" after a message boundary, which was appropriate.
42
+
43
+ ### Changes made
44
+
45
+ 1. `.pi/skills/testing/SKILL.md` — added rule: do not annotate test factory return types with production interface types (erases `Mock<...>` methods).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gotgenes/pi-subagents",
3
- "version": "6.10.0",
3
+ "version": "6.12.0",
4
4
  "exports": {
5
5
  ".": "./src/service.ts"
6
6
  },