@gotgenes/pi-subagents 6.9.3 → 6.9.4

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,13 @@ 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
+ ## [6.9.4](https://github.com/gotgenes/pi-packages/compare/pi-subagents-v6.9.3...pi-subagents-v6.9.4) (2026-05-22)
9
+
10
+
11
+ ### Documentation
12
+
13
+ * plan consolidate shared test fixtures ([#131](https://github.com/gotgenes/pi-packages/issues/131)) ([2fe1e65](https://github.com/gotgenes/pi-packages/commit/2fe1e65024743384981c057b405f97f9c76f9b05))
14
+
8
15
  ## [6.9.3](https://github.com/gotgenes/pi-packages/compare/pi-subagents-v6.9.2...pi-subagents-v6.9.3) (2026-05-22)
9
16
 
10
17
 
@@ -0,0 +1,207 @@
1
+ ---
2
+ issue: 131
3
+ issue_title: Consolidate shared test fixtures
4
+ ---
5
+
6
+ # Consolidate shared test fixtures
7
+
8
+ ## Problem Statement
9
+
10
+ Three `mockSession()` factories and three `makeDeps()` factories are duplicated across the test suite.
11
+ Each copy drifts independently when production interfaces change, creating maintenance burden and inconsistent mock shapes.
12
+ The architecture doc (Phase 8, Step F) identifies this as the first testability improvement before the IO-injection steps (G and H).
13
+
14
+ ## Goals
15
+
16
+ - Extract `createMockSession()` into `test/helpers/mock-session.ts` — single source of truth for the subscribable session mock.
17
+ - Extract `createToolDeps()` into `test/helpers/make-deps.ts` — builds `AgentToolDeps` with sensible defaults and override support.
18
+ - Update all six test files to use the shared factories and remove their local copies.
19
+ - Keep existing test behavior unchanged — this is a pure refactor with no production code changes.
20
+
21
+ ## Non-Goals
22
+
23
+ - IO injection into `session-config` (Step G, #132) — deferred.
24
+ - SDK boundary injection into `agent-runner` (Step H, #133) — deferred.
25
+ - Consolidating `makeCtx()` or `makeParams()` helpers — those are specific to each tool's parameter shape and do not share enough structure to justify extraction.
26
+
27
+ ## Background
28
+
29
+ ### Existing helper
30
+
31
+ `test/helpers/make-record.ts` exports `createTestRecord()`, which builds an `AgentRecord` with sensible defaults and override support.
32
+ It has its own unit test file (`test/helpers/make-record.test.ts`).
33
+ The two new factories follow the same pattern.
34
+
35
+ ### `mockSession()` — 3 copies
36
+
37
+ | File | Shape |
38
+ | ------------------------- | ------------------------------------------------------------------------------------------------- |
39
+ | `agent-manager.test.ts` | `subscribe` (vi.fn), `emit`, `dispose` (vi.fn), `steer` (vi.fn), `sessionManager` — cast as `any` |
40
+ | `record-observer.test.ts` | `subscribe` (vi.fn), `emit` |
41
+ | `ui/ui-observer.test.ts` | `subscribe` (plain fn), `emit` |
42
+
43
+ The common core is `subscribe` + `emit` (the subscribable event bus).
44
+ The `agent-manager` copy adds extra properties the other two don't need.
45
+
46
+ ### `makeDeps()` — 3 copies
47
+
48
+ | File | Type | Manager methods | Widget methods | Extra fields |
49
+ | ---------------------------------- | ---------------- | -------------------------------------------------------- | ------------------------------------------- | ---------------------------- |
50
+ | `tools/agent-tool.test.ts` | `AgentToolDeps` | spawn, spawnAndWait, resume, getRecord, getMaxConcurrent | setUICtx, ensureTimer, update, markFinished | registry, agentDir, settings |
51
+ | `tools/background-spawner.test.ts` | `BackgroundDeps` | spawn, getRecord, getMaxConcurrent | ensureTimer, update | — |
52
+ | `tools/foreground-runner.test.ts` | `ForegroundDeps` | spawnAndWait | ensureTimer, markFinished | — |
53
+
54
+ `AgentToolDeps` is a structural superset of both `BackgroundDeps` and `ForegroundDeps`.
55
+ TypeScript's structural type system allows an `AgentToolDeps` value to be passed where `BackgroundDeps` or `ForegroundDeps` is expected — the narrower interfaces require a strict subset of the methods present on the wider one.
56
+
57
+ ## Design Overview
58
+
59
+ ### `createMockSession(overrides?)`
60
+
61
+ Returns the subscribable event bus (core shape) merged with optional overrides.
62
+ The core shape includes:
63
+
64
+ ```typescript
65
+ interface MockSession {
66
+ subscribe: Mock<[fn: (event: any) => void], () => void>;
67
+ emit(event: any): void; // test-only helper, not on production Session
68
+ dispose: Mock;
69
+ steer: Mock;
70
+ sessionManager: { getSessionFile: Mock };
71
+ }
72
+ ```
73
+
74
+ All fields are present in every call — callers that don't need `dispose` or `steer` simply ignore them.
75
+ This avoids a discriminated "minimal vs. full" shape that would reintroduce the divergence problem.
76
+ The `subscribe` spy is wired to a `Set<fn>` internally so `emit()` broadcasts to all subscribers, matching the existing hand-rolled pattern.
77
+ The return type is `MockSession & Record<string, unknown>` so call sites can pass it as `any`-typed session parameters without explicit casts.
78
+
79
+ Override support lets `agent-manager.test.ts` customize `steer` behavior or add fields:
80
+
81
+ ```typescript
82
+ const session = createMockSession({ steer: vi.fn().mockRejectedValue(new Error("fail")) });
83
+ ```
84
+
85
+ ### `createToolDeps(overrides?)`
86
+
87
+ Builds a full `AgentToolDeps` with mock manager, widget, activity map, registry, agent dir, and settings.
88
+ Accepts `Partial<AgentToolDeps>` for overrides, following the same pattern as `createTestRecord()`.
89
+
90
+ ```typescript
91
+ function createToolDeps(overrides?: Partial<AgentToolDeps>): AgentToolDeps;
92
+ ```
93
+
94
+ Consumer call sites:
95
+
96
+ ```typescript
97
+ // agent-tool.test.ts — uses the full type directly
98
+ const deps = createToolDeps();
99
+ const tool = createAgentTool(deps);
100
+
101
+ // background-spawner.test.ts — structural typing narrows automatically
102
+ const deps = createToolDeps();
103
+ spawnBackground(deps, makeParams());
104
+
105
+ // foreground-runner.test.ts — same structural narrowing
106
+ const deps = createToolDeps({ manager: { spawnAndWait: vi.fn().mockResolvedValue(customRecord) } });
107
+ await runForeground(deps, makeParams(), undefined, undefined);
108
+ ```
109
+
110
+ The background and foreground tests gain unused mock methods on `manager` and `widget`, but this is harmless — the production code's ISP compliance ensures only the narrow interface methods are called.
111
+ Tests that assert specific mock interactions (e.g., `expect(deps.manager.spawn).toHaveBeenCalled()`) continue to work because every method is a distinct `vi.fn()`.
112
+
113
+ When a test needs to override a single manager method, it spreads into the nested object:
114
+
115
+ ```typescript
116
+ createToolDeps({
117
+ manager: { ...createToolDeps().manager, spawnAndWait: vi.fn().mockRejectedValue(err) },
118
+ });
119
+ ```
120
+
121
+ This is slightly more verbose than today's flat override, but it happens rarely and the tradeoff is worthwhile for a single source of truth.
122
+
123
+ Alternatively, `createToolDeps` can accept a `managerOverrides` shorthand if the nested-spread pattern proves too noisy during implementation.
124
+
125
+ ## Module-Level Changes
126
+
127
+ ### New files
128
+
129
+ 1. `test/helpers/mock-session.ts` — exports `createMockSession(overrides?)`.
130
+ 2. `test/helpers/mock-session.test.ts` — unit tests for `createMockSession`: verifies event broadcasting, subscribe/unsubscribe, and override merging.
131
+ 3. `test/helpers/make-deps.ts` — exports `createToolDeps(overrides?)`.
132
+ 4. `test/helpers/make-deps.test.ts` — unit tests for `createToolDeps`: verifies default shape satisfies `AgentToolDeps`, `BackgroundDeps`, and `ForegroundDeps`; verifies override merging.
133
+
134
+ ### Modified files
135
+
136
+ 1. `test/agent-manager.test.ts` — remove local `mockSession()`, import `createMockSession` from helpers.
137
+ 2. `test/record-observer.test.ts` — remove local `mockSession()`, import `createMockSession` from helpers.
138
+ 3. `test/ui/ui-observer.test.ts` — remove local `mockSession()`, import `createMockSession` from helpers.
139
+ 4. `test/tools/agent-tool.test.ts` — remove local `makeDeps()`, import `createToolDeps` from helpers.
140
+ 5. `test/tools/background-spawner.test.ts` — remove local `makeDeps()`, import `createToolDeps` from helpers.
141
+ 6. `test/tools/foreground-runner.test.ts` — remove local `makeDeps()`, import `createToolDeps` from helpers.
142
+
143
+ ## Test Impact Analysis
144
+
145
+ 1. The new factory unit tests (`mock-session.test.ts`, `make-deps.test.ts`) verify the shared fixture behavior that was previously only implicitly tested through the consumer test files.
146
+ This enables targeted debugging when a mock shape drifts from the production interface.
147
+ 2. No existing tests become redundant — the consumer tests exercise distinct production behavior that the factory tests do not cover.
148
+ 3. All existing tests stay as-is in terms of assertions.
149
+ Only the setup code (local factory → shared import) changes.
150
+
151
+ ## TDD Order
152
+
153
+ 1. **Red → Green: `createMockSession` factory.**
154
+ Write `test/helpers/mock-session.test.ts` — verify subscribe/emit broadcasting, unsubscribe, dispose/steer are vi.fn stubs, override merging.
155
+ Implement `test/helpers/mock-session.ts`.
156
+ Commit: `test: add createMockSession shared test fixture`
157
+
158
+ 2. **Green: migrate `record-observer.test.ts` to `createMockSession`.**
159
+ Replace local `mockSession()` with import from helpers.
160
+ Run test file — all tests pass unchanged.
161
+ Commit: `test: use createMockSession in record-observer tests`
162
+
163
+ 3. **Green: migrate `ui/ui-observer.test.ts` to `createMockSession`.**
164
+ Replace local `mockSession()` with import from helpers.
165
+ Run test file — all tests pass unchanged.
166
+ Commit: `test: use createMockSession in ui-observer tests`
167
+
168
+ 4. **Green: migrate `agent-manager.test.ts` to `createMockSession`.**
169
+ Replace local `mockSession()` with import from helpers.
170
+ This file uses extra fields (`sessionManager`, `steer`, `dispose`) — verify overrides or defaults cover them.
171
+ Run test file — all tests pass unchanged.
172
+ Commit: `test: use createMockSession in agent-manager tests`
173
+
174
+ 5. **Red → Green: `createToolDeps` factory.**
175
+ Write `test/helpers/make-deps.test.ts` — verify default shape, override merging, structural compatibility with `BackgroundDeps` and `ForegroundDeps`.
176
+ Implement `test/helpers/make-deps.ts`.
177
+ Commit: `test: add createToolDeps shared test fixture`
178
+
179
+ 6. **Green: migrate `tools/agent-tool.test.ts` to `createToolDeps`.**
180
+ Replace local `makeDeps()` with import from helpers.
181
+ Run test file — all tests pass unchanged.
182
+ Commit: `test: use createToolDeps in agent-tool tests`
183
+
184
+ 7. **Green: migrate `tools/background-spawner.test.ts` to `createToolDeps`.**
185
+ Replace local `makeDeps()` with import from helpers.
186
+ Adjust any override patterns for the wider type.
187
+ Run test file — all tests pass unchanged.
188
+ Commit: `test: use createToolDeps in background-spawner tests`
189
+
190
+ 8. **Green: migrate `tools/foreground-runner.test.ts` to `createToolDeps`.**
191
+ Replace local `makeDeps()` with import from helpers.
192
+ Adjust any override patterns for the wider type.
193
+ Run test file — all tests pass unchanged.
194
+ Commit: `test: use createToolDeps in foreground-runner tests`
195
+
196
+ ## Risks and Mitigations
197
+
198
+ | Risk | Mitigation |
199
+ | ------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
200
+ | Wider mock shape causes false-positive tests (tests pass even when production code calls wrong method) | The production interfaces are already ISP-narrow; the mock width only affects tests. Existing assertions on specific mock calls catch regressions. |
201
+ | Override merging doesn't handle nested objects (e.g., overriding a single manager method) | Factory uses shallow merge for top-level fields; document that nested overrides require spreading the default nested object. Evaluate a `managerOverrides` shorthand during implementation if the pattern is too noisy. |
202
+ | `createMockSession` return type is too loose (`any`) and hides type errors in tests | Return a named `MockSession` interface rather than `any`. Consumer sites that pass the mock as `any`-typed SDK parameters are already untyped at that boundary. |
203
+
204
+ ## Open Questions
205
+
206
+ - Should `createToolDeps` accept a flat `managerOverrides` shorthand or require the caller to spread the nested object?
207
+ Decide during step 5 based on how verbose the migration turns out in steps 6–8.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gotgenes/pi-subagents",
3
- "version": "6.9.3",
3
+ "version": "6.9.4",
4
4
  "exports": {
5
5
  ".": "./src/service.ts"
6
6
  },