@gotgenes/pi-subagents 7.3.0 → 7.3.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/CHANGELOG.md CHANGED
@@ -5,6 +5,22 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [7.3.1](https://github.com/gotgenes/pi-packages/compare/pi-subagents-v7.3.0...pi-subagents-v7.3.1) (2026-05-25)
9
+
10
+
11
+ ### Bug Fixes
12
+
13
+ * add parameter type annotations to shared test fixture factories ([2953adc](https://github.com/gotgenes/pi-packages/commit/2953adc5761dd4aa2d3c592ea5f8856161e01e3a))
14
+
15
+
16
+ ### Documentation
17
+
18
+ * plan extract shared test fixtures ([#208](https://github.com/gotgenes/pi-packages/issues/208)) ([22fafed](https://github.com/gotgenes/pi-packages/commit/22fafed860134f00f1b5d6cb7c87fa42be7dcf09))
19
+ * **retro:** add planning stage notes for issue [#208](https://github.com/gotgenes/pi-packages/issues/208) ([aaa9d5d](https://github.com/gotgenes/pi-packages/commit/aaa9d5d0a4654b2ebb25a7ad4d11a3a54db7c206))
20
+ * **retro:** add retro notes for issue [#207](https://github.com/gotgenes/pi-packages/issues/207) ([3b97a5c](https://github.com/gotgenes/pi-packages/commit/3b97a5c5b3fbb98a78ba280fbdbbdf55f61d82fc))
21
+ * **retro:** add TDD stage notes for issue [#208](https://github.com/gotgenes/pi-packages/issues/208) ([65d0606](https://github.com/gotgenes/pi-packages/commit/65d0606167e531518bd8de4d33f91c6080e21723))
22
+ * update Phase 12 Step 4 to reference test/helpers/ ([8e9e406](https://github.com/gotgenes/pi-packages/commit/8e9e406bfe12487f67363b223d037664f842acce))
23
+
8
24
  ## [7.3.0](https://github.com/gotgenes/pi-packages/compare/pi-subagents-v7.2.8...pi-subagents-v7.3.0) (2026-05-25)
9
25
 
10
26
 
@@ -637,10 +637,10 @@ The 3 heaviest clone families:
637
637
  - `agent-menu.test.ts` + `agent-creation-wizard.test.ts` + `agent-config-editor.test.ts` (54+51+24 lines)
638
638
  - `agent-manager.test.ts` (18 internal clone groups, 210 duplicated lines)
639
639
 
640
- Extract shared factories into `test/fixtures/` modules.
640
+ Extract shared factories into `test/helpers/` modules.
641
641
 
642
- - Target: new `test/fixtures/` modules
643
- - Outcome: test duplication reduced by ~400 lines
642
+ - Target: new `test/helpers/runner-io.ts` and `test/helpers/ui-stubs.ts` modules
643
+ - Outcome: test duplication reduced by ~250 lines
644
644
 
645
645
  ## Refactoring history
646
646
 
@@ -0,0 +1,298 @@
1
+ ---
2
+ issue: 208
3
+ issue_title: "Extract shared test fixtures to reduce test duplication"
4
+ ---
5
+
6
+ # Extract shared test fixtures
7
+
8
+ ## Problem Statement
9
+
10
+ Test duplication across the pi-subagents package is 1,367 lines (7.2%) across 23 files (67 clone groups per fallow analysis).
11
+ The three heaviest clone families are:
12
+
13
+ 1. Runner IO tests (`agent-runner.test.ts` + `agent-runner-extension-tools.test.ts` + `concrete-agent-runner.test.ts`): 60-line shared setup — `createRunnerIO()`, `AgentConfigLookup` stub, `ParentSnapshot` constant.
14
+ 2. Menu/wizard UI tests (`agent-menu.test.ts` + `agent-creation-wizard.test.ts` + `agent-config-editor.test.ts`): 54+51+24 lines — `makeFileOps()`, `makeUI()`, `makeManager()`, `AgentConfig` defaults, `ParentSnapshot` constant.
15
+ 3. `agent-manager.test.ts` internal: 18 clone groups, 210 duplicated lines — repetitive `manager.spawn(...)` call patterns.
16
+
17
+ Issue #131 (closed) already extracted `createMockSession`, `createToolDeps`, and `createTestRecord` into `test/helpers/`.
18
+ This issue targets the remaining duplication families.
19
+
20
+ ## Goals
21
+
22
+ - Extract `createRunnerIO()` and `createAgentLookup()` into `test/helpers/runner-io.ts` — single source of truth for the `RunnerIO` and `AgentConfigLookup` stubs used by runner tests.
23
+ - Extract `makeFileOps()`, `makeMenuUI()`, `makeMenuManager()`, and `createTestAgentConfig()` into `test/helpers/ui-stubs.ts` — shared factories for the three UI test files.
24
+ - Consolidate `agent-manager.test.ts` internal duplication with local helper functions (`spawnBg`, `spawnFg`).
25
+ - Replace local `ParentSnapshot` definitions with the existing `STUB_SNAPSHOT` from `test/helpers/stub-ctx.ts` where possible.
26
+ - Remove stale `buildMemoryBlock` and `buildReadOnlyMemoryBlock` stubs from the `createRunnerIO` factory (these methods no longer exist on `AssemblerIO`).
27
+ - Target: ~250 lines of test duplication removed.
28
+
29
+ ## Non-Goals
30
+
31
+ - Extracting session mock factories from the runner tests — each file's session factory serves a specialized purpose (`createSession(finalText)`, `createSessionWithExtensionToolRegistration(beforeBind, afterBind)`, `makeSession(text)`) and the variance is structural, not incidental.
32
+ - Extracting `makeSettings()` from `agent-menu.test.ts` — only used in one file.
33
+ - Extracting `makeHandler()` / `makeEditor()` / `makeDeps()` wrapper functions that compose collaborator stubs for specific handlers — too tightly coupled to each file's test structure.
34
+ - Extracting the mutable `agentConfigMock` pattern from `agent-runner-extension-tools.test.ts` — its per-test config mutation is inherently local.
35
+ - Decomposing UI complexity (Steps 1–3 of Phase 12, issues #205, #206, #207) — separate issues.
36
+
37
+ ## Background
38
+
39
+ ### Existing test helpers
40
+
41
+ `test/helpers/` already contains shared factories from issue #131:
42
+
43
+ | File | Exports | Used by |
44
+ | ----------------- | ----------------------------------------- | ------------------------------------------------------- |
45
+ | `mock-session.ts` | `createMockSession()`, `toAgentSession()` | agent-manager, record-observer, ui-observer tests |
46
+ | `make-deps.ts` | `createToolDeps()` | agent-tool, background-spawner, foreground-runner tests |
47
+ | `make-record.ts` | `createTestRecord()` | tool tests, UI tests |
48
+ | `stub-ctx.ts` | `STUB_CTX`, `STUB_SNAPSHOT` | tool tests (via `make-deps.ts`) |
49
+
50
+ The new factories follow the same pattern: shared files in `test/helpers/` with optional unit tests.
51
+
52
+ ### Architecture doc reference
53
+
54
+ Phase 12, Step 4 in `docs/architecture/architecture.md` (lines 637–644) calls for `test/fixtures/` modules.
55
+ We use `test/helpers/` instead to follow the existing convention established in issue #131.
56
+ The architecture doc reference will be updated as part of this work.
57
+
58
+ ### Interface shapes
59
+
60
+ `RunnerIO` = `EnvironmentIO & SessionFactoryIO` (in `src/lifecycle/agent-runner.ts`):
61
+
62
+ ```typescript
63
+ interface EnvironmentIO {
64
+ detectEnv: (exec: ShellExec, cwd: string) => Promise<EnvInfo>;
65
+ getAgentDir: () => string;
66
+ deriveSessionDir: (parentSessionFile: string | undefined, cwd: string) => string;
67
+ }
68
+
69
+ interface SessionFactoryIO {
70
+ createResourceLoader: (opts: ResourceLoaderOptions) => ResourceLoaderLike;
71
+ createSessionManager: (cwd: string, sessionDir: string) => SessionManagerLike;
72
+ createSettingsManager: (cwd: string, agentDir: string) => SettingsManager;
73
+ createSession: (opts: CreateSessionOptions) => Promise<{ session: AgentSession }>;
74
+ assemblerIO: AssemblerIO;
75
+ }
76
+ ```
77
+
78
+ `AssemblerIO` (in `src/session/session-config.ts`) has only `preloadSkills` and `buildAgentPrompt`.
79
+ The existing test factories in `agent-runner.test.ts` and `agent-runner-extension-tools.test.ts` include stale `buildMemoryBlock` and `buildReadOnlyMemoryBlock` stubs that no longer match the interface — the shared factory will omit them.
80
+
81
+ ### Duplication diff: default values across copies
82
+
83
+ Before extracting, the following differences across copies were identified:
84
+
85
+ **`createRunnerIO` / `makeIO`:**
86
+
87
+ | Field | agent-runner | extension-tools | concrete-agent-runner |
88
+ | -------------------------------------- | --------------- | --------------- | --------------------- |
89
+ | `assemblerIO.buildMemoryBlock` | present (stale) | present (stale) | absent |
90
+ | `assemblerIO.buildReadOnlyMemoryBlock` | present (stale) | absent | absent |
91
+ | Other fields | identical | identical | identical |
92
+
93
+ **`ParentSnapshot` stubs:**
94
+
95
+ | Field | agent-runner | extension-tools | concrete-agent-runner | agent-manager | agent-menu/wizard | STUB_SNAPSHOT |
96
+ | ---------------------------- | --------------- | --------------- | --------------------- | --------------- | ----------------- | ----------------- |
97
+ | `cwd` | "/tmp" | "/tmp" | "/workspace" | "/tmp" | "/test" | "/test" |
98
+ | `systemPrompt` | "parent prompt" | "parent prompt" | "" | "parent prompt" | "" | "test prompt" |
99
+ | `model` | undefined | undefined | {} | undefined | {} | undefined |
100
+ | `modelRegistry.find` | vi.fn() | vi.fn() | vi.fn() | vi.fn() | `() => undefined` | `() => undefined` |
101
+ | `modelRegistry.getAvailable` | vi.fn() | vi.fn() | — | — | — | — |
102
+
103
+ None of the consumer tests assert on `cwd`, `systemPrompt`, `model`, or `modelRegistry.find` return values — these fields are passed through to functions that are already mocked.
104
+ Using `STUB_SNAPSHOT` is safe for all consumers.
105
+
106
+ **`makeFileOps`:** character-for-character identical in all three UI test files.
107
+
108
+ **`makeUI`:** identical core in wizard and config-editor; agent-menu wraps it in an outer `{ ui, modelRegistry, parentSnapshot }` object.
109
+
110
+ **`makeManager`:** identical in agent-menu and wizard.
111
+
112
+ **`testDefaultAgentConfig`:** identical in agent-menu and config-editor (same 9 fields).
113
+
114
+ ## Design Overview
115
+
116
+ ### `test/helpers/runner-io.ts`
117
+
118
+ Two exports:
119
+
120
+ ```typescript
121
+ /** Shared RunnerIO stub factory for agent-runner tests. */
122
+ function createRunnerIO(assemblerOverrides?: Partial<AssemblerIOStub>): RunnerIOStub;
123
+
124
+ /** Shared AgentConfigLookup stub. Returns a static Explore config by default. */
125
+ function createAgentLookup(config?: Partial<AgentConfig>): AgentLookupStub;
126
+ ```
127
+
128
+ `createRunnerIO` builds the full `RunnerIO` stub shape.
129
+ The `assemblerIO` sub-object defaults to stubs for `preloadSkills` and `buildAgentPrompt` only (matching the current `AssemblerIO` interface).
130
+ `assemblerOverrides` lets tests customize individual methods without rebuilding the entire factory.
131
+
132
+ `createAgentLookup` returns `{ resolveAgentConfig, getToolNamesForType }` wrapping a static config.
133
+ The default config is the Explore agent used in `agent-runner.test.ts` and `concrete-agent-runner.test.ts`.
134
+ Tests that need per-test config mutation (extension-tools) keep their local mutable wrapper but use the default config as a starting point.
135
+
136
+ Return types are deliberately unannotated (per testing skill) so `vi.fn()` stubs retain their `Mock<...>` methods.
137
+
138
+ ### `test/helpers/ui-stubs.ts`
139
+
140
+ Four exports:
141
+
142
+ ```typescript
143
+ /** FileOps stub — identical across all three UI test files. */
144
+ function makeFileOps(): FileOpsStub;
145
+
146
+ /** MenuUI stub with sequential select responses. */
147
+ function makeMenuUI(selectResults?: (string | undefined)[]): MenuUIStub;
148
+
149
+ /** Manager stub for UI tests (listAgents, getRecord, spawnAndWait). */
150
+ function makeMenuManager(): MenuManagerStub;
151
+
152
+ /** AgentConfig factory with sensible defaults and override support. */
153
+ function createTestAgentConfig(overrides?: Partial<AgentConfig>): AgentConfig;
154
+ ```
155
+
156
+ `makeMenuUI` returns the flat UI shape (select, input, confirm, editor, notify, custom).
157
+ `agent-menu.test.ts` wraps this locally into its `{ ui, modelRegistry, parentSnapshot }` structure — the wrapping stays in the test file because it's specific to `AgentsMenuHandler`'s interface.
158
+
159
+ ### `agent-manager.test.ts` local helpers
160
+
161
+ Two local helpers reduce the 42 repetitive `manager.spawn(...)` calls:
162
+
163
+ ```typescript
164
+ function spawnBg(mgr: AgentManager, prompt = "test", desc = prompt) {
165
+ return mgr.spawn(STUB_SNAPSHOT, "general-purpose", prompt, {
166
+ description: desc,
167
+ isBackground: true,
168
+ });
169
+ }
170
+
171
+ function spawnFg(mgr: AgentManager, prompt = "test", desc = prompt) {
172
+ return mgr.spawnAndWait(STUB_SNAPSHOT, "general-purpose", prompt, {
173
+ description: desc,
174
+ });
175
+ }
176
+ ```
177
+
178
+ These stay local because they are only needed in this file.
179
+ They also replace the local `mockSnapshot` with the imported `STUB_SNAPSHOT`.
180
+
181
+ ## Module-Level Changes
182
+
183
+ ### New files
184
+
185
+ 1. `test/helpers/runner-io.ts` — exports `createRunnerIO()`, `createAgentLookup()`.
186
+ 2. `test/helpers/runner-io.test.ts` — unit tests: verifies stub shape satisfies `RunnerIO`, override merging, default config shape.
187
+ 3. `test/helpers/ui-stubs.ts` — exports `makeFileOps()`, `makeMenuUI()`, `makeMenuManager()`, `createTestAgentConfig()`.
188
+ 4. `test/helpers/ui-stubs.test.ts` — unit tests: verifies stub shapes, sequential select behavior, config override merging.
189
+
190
+ ### Modified files
191
+
192
+ 1. `test/lifecycle/agent-runner.test.ts` — remove local `createRunnerIO()`, `mockAgentLookup`, `snapshot`, `exec`; import from helpers.
193
+ 2. `test/lifecycle/agent-runner-extension-tools.test.ts` — remove local `createRunnerIO()`, `snapshot`; import from helpers; keep local mutable `agentConfigMock` and `mockAgentLookup` (they use the mutable wrapper pattern).
194
+ 3. `test/lifecycle/concrete-agent-runner.test.ts` — remove local `makeIO()`, `registry`, `snapshot`; import from helpers.
195
+ 4. `test/lifecycle/agent-manager.test.ts` — remove local `mockSnapshot`; import `STUB_SNAPSHOT`; add local `spawnBg()` and `spawnFg()` helpers; update all `manager.spawn(mockSnapshot, ...)` calls.
196
+ 5. `test/ui/agent-menu.test.ts` — remove local `makeFileOps()`, `makeManager()`, `testDefaultAgentConfig`, `stubParentSnapshot`; import from helpers; keep local `makeSettings()` and `makeHandler()`.
197
+ 6. `test/ui/agent-creation-wizard.test.ts` — remove local `makeFileOps()`, `makeManager()`, `stubParentSnapshot`; import from helpers; keep local `makeDeps()`.
198
+ 7. `test/ui/agent-config-editor.test.ts` — remove local `makeFileOps()`, `testDefaultConfig`, `testCustomConfig`; import from helpers; keep local `makeEditor()`.
199
+
200
+ ### Doc updates
201
+
202
+ 1. `docs/architecture/architecture.md` — update Phase 12 Step 4 reference from `test/fixtures/` to `test/helpers/` (lines 640, 642).
203
+
204
+ ## Test Impact Analysis
205
+
206
+ 1. The new factory unit tests (`runner-io.test.ts`, `ui-stubs.test.ts`) verify shared fixture behavior that was previously only implicitly tested through consumer test files.
207
+ This enables targeted debugging when a mock shape drifts from the production interface.
208
+ 2. No existing tests become redundant — consumer tests exercise distinct production behavior that the factory tests do not cover.
209
+ 3. All existing tests stay as-is in terms of assertions.
210
+ Only the setup code (local factory → shared import) changes.
211
+ 4. The removal of stale `buildMemoryBlock` and `buildReadOnlyMemoryBlock` stubs from `createRunnerIO` is safe because `AssemblerIO` no longer declares these methods — structural typing means they were always no-ops.
212
+
213
+ ## TDD Order
214
+
215
+ 1. **Red → Green: `createRunnerIO` and `createAgentLookup` factories.**
216
+ Write `test/helpers/runner-io.test.ts` — verify `createRunnerIO()` returns a shape satisfying `RunnerIO` (`EnvironmentIO & SessionFactoryIO`), verify `assemblerIO` override merging, verify `createAgentLookup()` returns the default Explore config and accepts overrides.
217
+ Implement `test/helpers/runner-io.ts`.
218
+ Run: `pnpm vitest run test/helpers/runner-io.test.ts`.
219
+ Commit: `test: add createRunnerIO and createAgentLookup shared test fixtures`
220
+
221
+ 2. **Green: migrate `agent-runner.test.ts` to shared runner-io factories.**
222
+ Import `createRunnerIO`, `createAgentLookup` from helpers.
223
+ Import `STUB_SNAPSHOT` from `stub-ctx.ts`.
224
+ Remove local `createRunnerIO()`, `mockAgentLookup`, `snapshot`.
225
+ Run: `pnpm vitest run test/lifecycle/agent-runner.test.ts`.
226
+ Commit: `test: use shared runner-io fixtures in agent-runner tests`
227
+
228
+ 3. **Green: migrate `agent-runner-extension-tools.test.ts` to shared `createRunnerIO`.**
229
+ Import `createRunnerIO` from helpers.
230
+ Import `STUB_SNAPSHOT` from `stub-ctx.ts`.
231
+ Remove local `createRunnerIO()` and `snapshot`.
232
+ Keep local `agentConfigMock` and `mockAgentLookup` (mutable wrapper pattern).
233
+ Run: `pnpm vitest run test/lifecycle/agent-runner-extension-tools.test.ts`.
234
+ Commit: `test: use shared createRunnerIO in extension-tools tests`
235
+
236
+ 4. **Green: migrate `concrete-agent-runner.test.ts` to shared factories.**
237
+ Import `createRunnerIO`, `createAgentLookup` from helpers.
238
+ Import `STUB_SNAPSHOT` from `stub-ctx.ts`.
239
+ Remove local `makeIO()`, `registry`, `snapshot`.
240
+ Run: `pnpm vitest run test/lifecycle/concrete-agent-runner.test.ts`.
241
+ Commit: `test: use shared runner-io fixtures in concrete-agent-runner tests`
242
+
243
+ 5. **Red → Green: UI stub factories.**
244
+ Write `test/helpers/ui-stubs.test.ts` — verify `makeFileOps()` shape, `makeMenuUI()` sequential select behavior, `makeMenuManager()` shape, `createTestAgentConfig()` default and override merging.
245
+ Implement `test/helpers/ui-stubs.ts`.
246
+ Run: `pnpm vitest run test/helpers/ui-stubs.test.ts`.
247
+ Commit: `test: add shared UI stub factories`
248
+
249
+ 6. **Green: migrate `agent-config-editor.test.ts` to shared UI stubs.**
250
+ Import `makeFileOps`, `makeMenuUI`, `createTestAgentConfig` from helpers.
251
+ Remove local `makeFileOps()`, `makeUI()`, `testDefaultConfig`; derive `testCustomConfig` from `createTestAgentConfig`.
252
+ Run: `pnpm vitest run test/ui/agent-config-editor.test.ts`.
253
+ Commit: `test: use shared UI stubs in agent-config-editor tests`
254
+
255
+ 7. **Green: migrate `agent-creation-wizard.test.ts` to shared UI stubs.**
256
+ Import `makeFileOps`, `makeMenuUI`, `makeMenuManager` from helpers.
257
+ Import `STUB_SNAPSHOT` from `stub-ctx.ts`.
258
+ Remove local `makeFileOps()`, `makeUI()`, `makeManager()`, `stubParentSnapshot`.
259
+ Run: `pnpm vitest run test/ui/agent-creation-wizard.test.ts`.
260
+ Commit: `test: use shared UI stubs in agent-creation-wizard tests`
261
+
262
+ 8. **Green: migrate `agent-menu.test.ts` to shared UI stubs.**
263
+ Import `makeFileOps`, `makeMenuUI`, `makeMenuManager`, `createTestAgentConfig` from helpers.
264
+ Import `STUB_SNAPSHOT` from `stub-ctx.ts`.
265
+ Remove local `makeFileOps()`, `makeManager()`, `testDefaultAgentConfig`, `stubParentSnapshot`.
266
+ Adapt `makeHandler()` to wrap `makeMenuUI()` into its `{ ui, modelRegistry, parentSnapshot }` shape.
267
+ Keep local `makeSettings()`.
268
+ Run: `pnpm vitest run test/ui/agent-menu.test.ts`.
269
+ Commit: `test: use shared UI stubs in agent-menu tests`
270
+
271
+ 9. **Green: consolidate `agent-manager.test.ts` internal duplication.**
272
+ Import `STUB_SNAPSHOT` from `stub-ctx.ts`.
273
+ Remove local `mockSnapshot`.
274
+ Add local `spawnBg()` and `spawnFg()` helpers.
275
+ Update all ~42 `manager.spawn(mockSnapshot, ...)` calls to use `spawnBg()`.
276
+ Update `spawnAndWait` calls to use `spawnFg()`.
277
+ Run: `pnpm vitest run test/lifecycle/agent-manager.test.ts`.
278
+ Commit: `test: consolidate agent-manager spawn patterns with local helpers`
279
+
280
+ 10. **Docs: update architecture doc reference.**
281
+ Update `docs/architecture/architecture.md` Phase 12 Step 4 to reference `test/helpers/` instead of `test/fixtures/`.
282
+ Commit: `docs: update Phase 12 Step 4 to reference test/helpers/`
283
+
284
+ ## Risks and Mitigations
285
+
286
+ | Risk | Mitigation |
287
+ | ------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
288
+ | `STUB_SNAPSHOT` shape differs from local snapshots (`cwd`, `model`, `systemPrompt` values) | Verified: no consumer tests assert on these field values. The snapshot is passed through to fully-mocked functions. Run full test suite after each migration step. |
289
+ | Removing stale `buildMemoryBlock`/`buildReadOnlyMemoryBlock` stubs breaks a test that somehow depends on them | These methods don't exist on `AssemblerIO` — any code accessing them would be a TypeScript error in production. Vitest's esbuild won't catch this, so run `pnpm run check` after step 2. |
290
+ | Wider mock shape in `createRunnerIO` causes false-positive tests (runner tests pass when they should fail) | The production `RunnerIO` interface is already narrow; extra mock methods are harmless. Existing assertions on specific mock calls catch regressions. |
291
+ | `makeMenuUI` sequential-select pattern breaks when agent-menu wraps it differently | Agent-menu's `makeHandler()` composes the wrapping locally. The shared factory returns only the flat UI shape, avoiding coupling to any specific consumer's wrapping structure. |
292
+ | `agent-manager.test.ts` `spawnBg()` helper hides important spawn options from test readers | Helper uses default values matching the most common pattern. Tests that need non-default options (e.g., `description: "first"`) pass explicit arguments, preserving readability. |
293
+
294
+ ## Open Questions
295
+
296
+ - Should `STUB_SNAPSHOT` be updated to use `vi.fn()` for `modelRegistry.find` instead of `() => undefined`?
297
+ Currently it uses plain functions, but some runner tests use `vi.fn()`.
298
+ Decide during implementation — if no test asserts on `find` call counts, plain functions are fine.
@@ -54,3 +54,39 @@ Test count went from 868 to 884 (+16 tests across 55 files, up from 54).
54
54
  - The complexity hotspots table in `architecture.md` now has no rows (both `renderWidgetLines` from #205 and `update` from this issue are resolved).
55
55
  The section note was updated to reflect that Phase 12 cleared all critical hotspots.
56
56
  - `pnpm fallow dead-code` (from repo root) passed with no issues.
57
+
58
+ ## Stage: Final Retrospective (2026-05-25T13:15:00Z)
59
+
60
+ ### Session summary
61
+
62
+ Issue #207 shipped as `pi-subagents-v7.3.0` with zero deviations from the revised plan.
63
+ The session covered plan revision, TDD implementation (16 new tests, 868 → 884), shipping, and CI verification.
64
+
65
+ ### Observations
66
+
67
+ #### What went well
68
+
69
+ - The plan revision caught three real design issues before implementation started: ISP violation in the function parameter type, incorrect `dispose` → `clearWidget` delegation, and missing complexity budget.
70
+ Fixing these upfront meant the TDD execution had zero deviations and zero rework.
71
+ - The narrow `AgentSummary` type (3 fields) made test fixtures trivial plain objects — no `createTestRecord` or factory infrastructure needed.
72
+ This validated the ISP improvement concretely.
73
+ - The `dispose` independence decision (Sandi Metz principle applied to lifecycle semantics) kept `dispose` at its current 10-line simplicity while `clearWidget` got its own guarded teardown logic.
74
+
75
+ #### What caused friction (agent side)
76
+
77
+ - `missing-context` — The original planning session (prior to this one) used `WidgetAgent[]` as the input type without checking which fields `assembleWidgetState` actually reads.
78
+ The `code-design` skill already says "do not pass a shared dependency bag to functions that only use a subset of it" but the principle wasn't applied to the proposed function signature.
79
+ Impact: required a full plan revision session; no rework in implementation because it was caught before TDD started.
80
+ - `wrong-abstraction` — The original plan proposed `dispose` → `clearWidget` delegation as "eliminating duplication" without evaluating whether the two methods have the same lifecycle semantics.
81
+ `dispose` uses unconditional teardown (shutdown correctness); `clearWidget` uses guarded calls (avoiding redundant SDK calls during repeated timer ticks).
82
+ Impact: same as above — caught in revision, no implementation rework.
83
+
84
+ #### What caused friction (user side)
85
+
86
+ - The user's "I don't yet trust the plan" intervention was the key moment that improved the design.
87
+ Without it, the plan would have been implemented with the wider type and the `dispose` delegation.
88
+ This was effective judgment — the user identified that a mechanical plan for a mechanical refactoring still warranted critical design review.
89
+
90
+ ### Changes made
91
+
92
+ 1. Added two sentences to `.pi/prompts/plan-issue.md` Design Overview section: ISP check for new function parameter types, and structural-duplication check when consolidating methods into a shared helper.
@@ -0,0 +1,45 @@
1
+ ---
2
+ issue: 208
3
+ issue_title: "Extract shared test fixtures to reduce test duplication"
4
+ ---
5
+
6
+ # Retro: #208 — Extract shared test fixtures to reduce test duplication
7
+
8
+ ## Stage: Implementation — TDD (2026-05-25T21:00:00Z)
9
+
10
+ ### Session summary
11
+
12
+ Completed all 10 TDD steps plus a type-fix commit.
13
+ Created 4 new files (`runner-io.ts`, `runner-io.test.ts`, `ui-stubs.ts`, `ui-stubs.test.ts`) and migrated 7 existing test files.
14
+ Test count grew from 884 → 913 (+29 tests in new helper unit tests).
15
+
16
+ ### Observations
17
+
18
+ - Vitest v4 changed `vi.fn()` without implementation annotation to type `Mock<Procedure | Constructable>`, which is NOT assignable to specific function signatures in production interfaces.
19
+ The fix was to add typed implementation annotations (`vi.fn((_path: string): boolean => false)`) to all vi.fn() stubs in the shared factories.
20
+ This was a new friction point not anticipated in the plan.
21
+ - The plan's `assemblerOverrides` parameter in `createRunnerIO()` was removed because the `??` union typing caused `Mock<Procedure | Constructable> | Mock<specific-fn>` which TypeScript couldn't resolve as assignable to `RunnerIO`.
22
+ No consumer test actually used the override parameter, so removing it simplified both the implementation and the type story.
23
+ - The `findAgentFile` signature in `AgentFileOps` is `(name: string, dirs: string[])` — the second parameter is `string[]`, not a second string as initially assumed from test patterns.
24
+ This was caught by `pnpm run check`.
25
+ - The `agent-config-editor.test.ts` migration removed the `import type { AgentConfig }` import that was still needed by `buildEjectContent` tests further down the file.
26
+ Also caught by `pnpm run check`.
27
+ - `STUB_SNAPSHOT` replacement was safe: no consumer test asserts on snapshot field values.
28
+ The `mockSnapshot` in `agent-manager.test.ts` had `systemPrompt: "parent prompt"` vs `STUB_SNAPSHOT`'s `"test prompt"` but this caused no test failures.
29
+ - Architecture doc was updated to reference `test/helpers/` (correcting `test/fixtures/` from the original entry).
30
+
31
+ ## Stage: Planning (2026-05-25T20:00:00Z)
32
+
33
+ ### Session summary
34
+
35
+ Analyzed the three heaviest test clone families identified by fallow and designed a 10-step TDD plan to extract shared factories into `test/helpers/`.
36
+ Decided to follow the existing `test/helpers/` convention rather than the `test/fixtures/` directory mentioned in the issue and architecture doc.
37
+
38
+ ### Observations
39
+
40
+ - Issue #131 (closed) already extracted `createMockSession`, `createToolDeps`, and `createTestRecord` — this issue targets the remaining duplication.
41
+ - The `createRunnerIO` factory in `agent-runner.test.ts` and `agent-runner-extension-tools.test.ts` includes stale `buildMemoryBlock` and `buildReadOnlyMemoryBlock` stubs that no longer match the `AssemblerIO` interface — the shared factory will clean these up as a side benefit.
42
+ - Session mock factories in the runner tests are structurally specialized (each serves a different test purpose) and were explicitly scoped as non-goals — extracting them would create a confusing multi-mode factory.
43
+ - The `agent-runner-extension-tools.test.ts` uses a mutable `agentConfigMock.current` pattern that doesn't fit into a shared static factory — only `createRunnerIO` is shared from that file.
44
+ - `STUB_SNAPSHOT` from `stub-ctx.ts` can replace all 5 local `ParentSnapshot` definitions — verified no test asserts on the specific field values.
45
+ - The `agent-manager.test.ts` internal duplication (~42 repetitive spawn calls) is best handled with local `spawnBg()`/`spawnFg()` helpers rather than cross-file extraction.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gotgenes/pi-subagents",
3
- "version": "7.3.0",
3
+ "version": "7.3.1",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": "./src/service.ts"