@gotgenes/pi-subagents 18.0.0 → 18.0.2

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.
Files changed (257) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/README.md +22 -31
  3. package/docs/architecture/architecture.md +157 -204
  4. package/docs/architecture/history/phase-19-implement-ui-decisions.md +282 -0
  5. package/package.json +5 -6
  6. package/src/lifecycle/subagent.ts +5 -9
  7. package/src/observation/notification.ts +23 -10
  8. package/src/observation/subagent-events-observer.ts +1 -3
  9. package/src/tools/get-result-report.ts +60 -0
  10. package/src/tools/get-result-tool.ts +25 -40
  11. package/src/types.ts +1 -1
  12. package/src/ui/session-navigator.ts +0 -2
  13. package/.prettierignore +0 -5
  14. package/AGENTS.md +0 -7
  15. package/docs/plans/0048-implement-subagents-api.md +0 -304
  16. package/docs/plans/0049-remove-group-join-output-file-rpc.md +0 -180
  17. package/docs/plans/0051-update-adr-0001-hard-fork.md +0 -77
  18. package/docs/plans/0052-remove-scheduled-subagents.md +0 -133
  19. package/docs/plans/0053-extract-model-resolution-from-execute.md +0 -181
  20. package/docs/plans/0054-decompose-index-into-modules.md +0 -302
  21. package/docs/plans/0057-structured-debug-logging.md +0 -154
  22. package/docs/plans/0061-session-format-transcript.md +0 -284
  23. package/docs/plans/0066-replace-as-any-with-sdk-types.md +0 -215
  24. package/docs/plans/0069-create-subagent-runtime.md +0 -345
  25. package/docs/plans/0070-extract-event-handlers.md +0 -306
  26. package/docs/plans/0071-extract-session-config-assembler.md +0 -362
  27. package/docs/plans/0072-inject-agent-manager-collaborators.md +0 -329
  28. package/docs/plans/0076-inject-cwd-into-agent-manager.md +0 -102
  29. package/docs/plans/0077-inject-project-agents-dir.md +0 -115
  30. package/docs/plans/0080-consolidate-agent-config-lookup.md +0 -247
  31. package/docs/plans/0084-extract-git-worktree-manager.md +0 -142
  32. package/docs/plans/0087-evolve-subagent-runtime-methods.md +0 -240
  33. package/docs/plans/0098-extract-agent-record-state-machine.md +0 -435
  34. package/docs/plans/0099-replace-ctx-with-parent-snapshot.md +0 -488
  35. package/docs/plans/0100-replace-callback-threading-with-session-subscription.md +0 -454
  36. package/docs/plans/0102-consolidate-test-record-factory.md +0 -176
  37. package/docs/plans/0108-extract-agent-type-registry.md +0 -322
  38. package/docs/plans/0109-extract-settings-manager.md +0 -276
  39. package/docs/plans/0110-agent-activity-tracker.md +0 -297
  40. package/docs/plans/0111-split-agent-record-lifecycle.md +0 -582
  41. package/docs/plans/0112-replace-agent-manager-callbacks.md +0 -241
  42. package/docs/plans/0113-disambiguate-spawn-options.md +0 -155
  43. package/docs/plans/0114-narrow-agent-tool-menu-deps.md +0 -279
  44. package/docs/plans/0115-decompose-agent-tool.md +0 -337
  45. package/docs/plans/0116-type-housekeeping.md +0 -351
  46. package/docs/plans/0118-settings-manager-apply-methods.md +0 -271
  47. package/docs/plans/0123-remove-vi-fn-cast-smell.md +0 -179
  48. package/docs/plans/0131-consolidate-shared-test-fixtures.md +0 -207
  49. package/docs/plans/0132-inject-io-into-session-config.md +0 -219
  50. package/docs/plans/0133-inject-sdk-boundary-into-agent-runner.md +0 -373
  51. package/docs/plans/0134-reduce-as-any-casts.md +0 -366
  52. package/docs/plans/0135-extract-display-helpers.md +0 -182
  53. package/docs/plans/0136-decompose-agent-menu.md +0 -300
  54. package/docs/plans/0144-consolidate-observation-model.md +0 -263
  55. package/docs/plans/0145-decompose-execute-push-ctx-to-boundary.md +0 -290
  56. package/docs/plans/0146-narrow-ui-context.md +0 -319
  57. package/docs/plans/0147-inject-wrap-text-into-conversation-viewer.md +0 -166
  58. package/docs/plans/0148-split-agent-widget-rendering.md +0 -255
  59. package/docs/plans/0152-add-prompt-snippet.md +0 -91
  60. package/docs/plans/0164-reorganize-into-domain-directories.md +0 -409
  61. package/docs/plans/0165-decompose-resolved-spawn-config.md +0 -157
  62. package/docs/plans/0166-extract-parent-session-info.md +0 -231
  63. package/docs/plans/0167-narrow-runner-io.md +0 -150
  64. package/docs/plans/0168-extract-tool-filter-config.md +0 -173
  65. package/docs/plans/0169-extract-run-context-from-run-options.md +0 -194
  66. package/docs/plans/0170-reduce-build-content-lines-complexity.md +0 -225
  67. package/docs/plans/0171-reduce-render-result-complexity.md +0 -200
  68. package/docs/plans/0172-extract-turn-formatting.md +0 -206
  69. package/docs/plans/0180-reorder-append-prompt-for-kv-cache.md +0 -100
  70. package/docs/plans/0185-remove-persistent-agent-memory.md +0 -161
  71. package/docs/plans/0188-replace-any-casts-with-sdk-types.md +0 -162
  72. package/docs/plans/0192-define-session-context-interface.md +0 -107
  73. package/docs/plans/0193-runtime-owns-context-queries.md +0 -245
  74. package/docs/plans/0194-align-tool-interfaces-for-structural-typing.md +0 -139
  75. package/docs/plans/0195-convert-tool-factories-to-classes.md +0 -264
  76. package/docs/plans/0196-convert-runner-menu-to-classes.md +0 -268
  77. package/docs/plans/0205-decompose-render-widget-lines.md +0 -140
  78. package/docs/plans/0206-decompose-show-agent-detail.md +0 -211
  79. package/docs/plans/0207-decompose-agent-widget-update.md +0 -242
  80. package/docs/plans/0208-extract-shared-test-fixtures.md +0 -298
  81. package/docs/plans/0214-convert-remaining-closure-factories-to-classes.md +0 -261
  82. package/docs/plans/0215-decompose-build-parent-context.md +0 -166
  83. package/docs/plans/0216-decompose-start-agent.md +0 -255
  84. package/docs/plans/0217-extract-overwrite-guard.md +0 -176
  85. package/docs/plans/0218-push-sdk-boundary-in-settings.md +0 -172
  86. package/docs/plans/0219-reduce-test-duplication-top-3-clone-families.md +0 -162
  87. package/docs/plans/0227-evolve-agent-record-into-agent.md +0 -322
  88. package/docs/plans/0228-async-start-agent-dissolve-run-handle.md +0 -288
  89. package/docs/plans/0229-agent-born-complete.md +0 -564
  90. package/docs/plans/0230-extract-concurrency-queue.md +0 -265
  91. package/docs/plans/0231-push-exec-registry-to-runner.md +0 -245
  92. package/docs/plans/0232-agent-resume-internal-observer-lifecycle.md +0 -180
  93. package/docs/plans/0237-remove-disallowed-tools.md +0 -180
  94. package/docs/plans/0238-remove-extensions-filtering.md +0 -191
  95. package/docs/plans/0239-collapse-filter-active-tools.md +0 -217
  96. package/docs/plans/0242-rename-agent-tool-to-subagent.md +0 -165
  97. package/docs/plans/0256-extract-worktree-isolation.md +0 -256
  98. package/docs/plans/0257-extract-child-session-factory.md +0 -285
  99. package/docs/plans/0262-add-workspace-provider-seam.md +0 -264
  100. package/docs/plans/0264-remove-extension-lifecycle-control.md +0 -277
  101. package/docs/plans/0265-born-complete-subagent-session.md +0 -333
  102. package/docs/plans/0270-type-consumable-public-surface.md +0 -204
  103. package/docs/plans/0272-export-workspace-collaborator-types.md +0 -147
  104. package/docs/plans/0277-encapsulate-agent-session.md +0 -304
  105. package/docs/plans/0280-rename-agent-to-subagent.md +0 -197
  106. package/docs/plans/0360-custom-agents-default-append.md +0 -132
  107. package/docs/plans/0373-extract-subagent-state.md +0 -250
  108. package/docs/plans/0374-encapsulate-subagent-start-notification.md +0 -268
  109. package/docs/plans/0375-extract-run-listener-workspace-bracket.md +0 -300
  110. package/docs/plans/0376-extract-manager-observer.md +0 -232
  111. package/docs/plans/0377-split-widget-delegation-from-runtime.md +0 -254
  112. package/docs/plans/0378-consolidate-lifecycle-test-fixtures.md +0 -243
  113. package/docs/plans/0379-consolidate-ui-tools-test-fixtures.md +0 -335
  114. package/docs/plans/0380-extract-layered-settings-loader.md +0 -223
  115. package/docs/plans/0381-replace-concurrency-queue-with-limiter.md +0 -267
  116. package/docs/plans/0400-include-parent-prompt-in-replace-mode.md +0 -199
  117. package/docs/plans/0403-abort-subagents-on-interrupt.md +0 -180
  118. package/docs/plans/0412-unify-session-mock-builders.md +0 -146
  119. package/docs/plans/0420-fold-run-metrics-live-activity-onto-record.md +0 -228
  120. package/docs/plans/0421-migrate-activity-readers-to-record-getters.md +0 -309
  121. package/docs/plans/0422-delete-activity-tracker-ui-observer.md +0 -204
  122. package/docs/plans/0423-widget-self-drive-from-lifecycle.md +0 -264
  123. package/docs/plans/0424-drop-widget-dep-from-subagent-tool.md +0 -166
  124. package/docs/plans/0425-reconcile-subagent-events-contract.md +0 -144
  125. package/docs/plans/0426-consolidate-residual-test-clone-families.md +0 -235
  126. package/docs/plans/0427-reconsider-ui-direction.md +0 -212
  127. package/docs/plans/0441-remove-agent-definition-management-subtree.md +0 -180
  128. package/docs/plans/0442-dissolve-agents-conversation-viewer.md +0 -195
  129. package/docs/plans/0444-shrink-widget-to-background-agents.md +0 -156
  130. package/docs/plans/0445-native-session-navigation.md +0 -365
  131. package/docs/plans/0446-spike-session-navigation-entry-criteria.md +0 -171
  132. package/docs/plans/0447-extract-subagents-settings-command.md +0 -220
  133. package/docs/plans/0448-block-disabled-agent-spawn.md +0 -207
  134. package/docs/plans/0462-navigation-renderer-tui-components.md +0 -338
  135. package/docs/plans/0463-file-snapshot-source-evicted-agents.md +0 -306
  136. package/docs/retro/0048-implement-subagents-api.md +0 -44
  137. package/docs/retro/0049-remove-group-join-output-file-rpc.md +0 -49
  138. package/docs/retro/0051-update-adr-0001-hard-fork.md +0 -39
  139. package/docs/retro/0053-extract-model-resolution-from-execute.md +0 -40
  140. package/docs/retro/0054-decompose-index-into-modules.md +0 -53
  141. package/docs/retro/0057-structured-debug-logging.md +0 -77
  142. package/docs/retro/0061-session-format-transcript.md +0 -41
  143. package/docs/retro/0066-replace-as-any-with-sdk-types.md +0 -44
  144. package/docs/retro/0069-create-subagent-runtime.md +0 -43
  145. package/docs/retro/0070-extract-event-handlers.md +0 -49
  146. package/docs/retro/0071-extract-session-config-assembler.md +0 -60
  147. package/docs/retro/0072-inject-agent-manager-collaborators.md +0 -46
  148. package/docs/retro/0076-inject-cwd-into-agent-manager.md +0 -35
  149. package/docs/retro/0077-inject-project-agents-dir.md +0 -33
  150. package/docs/retro/0080-consolidate-agent-config-lookup.md +0 -35
  151. package/docs/retro/0084-extract-git-worktree-manager.md +0 -37
  152. package/docs/retro/0087-evolve-subagent-runtime-methods.md +0 -42
  153. package/docs/retro/0098-extract-agent-record-state-machine.md +0 -46
  154. package/docs/retro/0099-replace-ctx-with-parent-snapshot.md +0 -37
  155. package/docs/retro/0100-replace-callback-threading-with-session-subscription.md +0 -36
  156. package/docs/retro/0102-consolidate-test-record-factory.md +0 -30
  157. package/docs/retro/0108-extract-agent-type-registry.md +0 -41
  158. package/docs/retro/0109-extract-settings-manager.md +0 -55
  159. package/docs/retro/0110-agent-activity-tracker.md +0 -44
  160. package/docs/retro/0111-split-agent-record-lifecycle.md +0 -61
  161. package/docs/retro/0112-replace-agent-manager-callbacks.md +0 -35
  162. package/docs/retro/0113-disambiguate-spawn-options.md +0 -29
  163. package/docs/retro/0114-narrow-agent-tool-menu-deps.md +0 -38
  164. package/docs/retro/0115-decompose-agent-tool.md +0 -51
  165. package/docs/retro/0116-type-housekeeping.md +0 -42
  166. package/docs/retro/0118-settings-manager-apply-methods.md +0 -40
  167. package/docs/retro/0123-remove-vi-fn-cast-smell.md +0 -49
  168. package/docs/retro/0131-consolidate-shared-test-fixtures.md +0 -46
  169. package/docs/retro/0132-inject-io-into-session-config.md +0 -33
  170. package/docs/retro/0133-inject-sdk-boundary-into-agent-runner.md +0 -45
  171. package/docs/retro/0134-reduce-as-any-casts.md +0 -56
  172. package/docs/retro/0135-extract-display-helpers.md +0 -38
  173. package/docs/retro/0136-decompose-agent-menu.md +0 -43
  174. package/docs/retro/0144-consolidate-observation-model.md +0 -39
  175. package/docs/retro/0145-decompose-execute-push-ctx-to-boundary.md +0 -56
  176. package/docs/retro/0146-narrow-ui-context.md +0 -70
  177. package/docs/retro/0147-inject-wrap-text-into-conversation-viewer.md +0 -90
  178. package/docs/retro/0148-split-agent-widget-rendering.md +0 -39
  179. package/docs/retro/0152-add-prompt-snippet.md +0 -34
  180. package/docs/retro/0164-reorganize-into-domain-directories.md +0 -82
  181. package/docs/retro/0165-decompose-resolved-spawn-config.md +0 -78
  182. package/docs/retro/0166-extract-parent-session-info.md +0 -66
  183. package/docs/retro/0167-narrow-runner-io.md +0 -63
  184. package/docs/retro/0168-extract-tool-filter-config.md +0 -76
  185. package/docs/retro/0169-extract-run-context-from-run-options.md +0 -68
  186. package/docs/retro/0170-reduce-build-content-lines-complexity.md +0 -72
  187. package/docs/retro/0171-reduce-render-result-complexity.md +0 -77
  188. package/docs/retro/0172-extract-turn-formatting.md +0 -80
  189. package/docs/retro/0180-reorder-append-prompt-for-kv-cache.md +0 -62
  190. package/docs/retro/0185-remove-persistent-agent-memory.md +0 -73
  191. package/docs/retro/0188-replace-any-casts-with-sdk-types.md +0 -69
  192. package/docs/retro/0192-define-session-context-interface.md +0 -59
  193. package/docs/retro/0193-runtime-owns-context-queries.md +0 -75
  194. package/docs/retro/0194-align-tool-interfaces-for-structural-typing.md +0 -62
  195. package/docs/retro/0195-convert-tool-factories-to-classes.md +0 -84
  196. package/docs/retro/0196-convert-runner-menu-to-classes.md +0 -73
  197. package/docs/retro/0205-decompose-render-widget-lines.md +0 -64
  198. package/docs/retro/0206-decompose-show-agent-detail.md +0 -67
  199. package/docs/retro/0207-decompose-agent-widget-update.md +0 -92
  200. package/docs/retro/0208-extract-shared-test-fixtures.md +0 -102
  201. package/docs/retro/0214-convert-remaining-closure-factories-to-classes.md +0 -66
  202. package/docs/retro/0215-decompose-build-parent-context.md +0 -61
  203. package/docs/retro/0216-decompose-start-agent.md +0 -80
  204. package/docs/retro/0217-extract-overwrite-guard.md +0 -63
  205. package/docs/retro/0218-push-sdk-boundary-in-settings.md +0 -72
  206. package/docs/retro/0219-reduce-test-duplication-top-3-clone-families.md +0 -64
  207. package/docs/retro/0227-evolve-agent-record-into-agent.md +0 -80
  208. package/docs/retro/0228-async-start-agent-dissolve-run-handle.md +0 -80
  209. package/docs/retro/0229-agent-born-complete.md +0 -89
  210. package/docs/retro/0230-extract-concurrency-queue.md +0 -38
  211. package/docs/retro/0231-push-exec-registry-to-runner.md +0 -71
  212. package/docs/retro/0232-agent-resume-internal-observer-lifecycle.md +0 -109
  213. package/docs/retro/0237-remove-disallowed-tools.md +0 -78
  214. package/docs/retro/0238-remove-extensions-filtering.md +0 -72
  215. package/docs/retro/0239-collapse-filter-active-tools.md +0 -70
  216. package/docs/retro/0242-rename-agent-tool-to-subagent.md +0 -82
  217. package/docs/retro/0256-extract-worktree-isolation.md +0 -89
  218. package/docs/retro/0257-extract-child-session-factory.md +0 -33
  219. package/docs/retro/0262-add-workspace-provider-seam.md +0 -89
  220. package/docs/retro/0264-remove-extension-lifecycle-control.md +0 -91
  221. package/docs/retro/0265-born-complete-subagent-session.md +0 -97
  222. package/docs/retro/0270-type-consumable-public-surface.md +0 -108
  223. package/docs/retro/0272-export-workspace-collaborator-types.md +0 -38
  224. package/docs/retro/0277-encapsulate-agent-session.md +0 -80
  225. package/docs/retro/0280-rename-agent-to-subagent.md +0 -96
  226. package/docs/retro/0360-custom-agents-default-append.md +0 -81
  227. package/docs/retro/0373-extract-subagent-state.md +0 -94
  228. package/docs/retro/0374-encapsulate-subagent-start-notification.md +0 -171
  229. package/docs/retro/0375-extract-run-listener-workspace-bracket.md +0 -104
  230. package/docs/retro/0376-extract-manager-observer.md +0 -81
  231. package/docs/retro/0377-split-widget-delegation-from-runtime.md +0 -105
  232. package/docs/retro/0378-consolidate-lifecycle-test-fixtures.md +0 -100
  233. package/docs/retro/0379-consolidate-ui-tools-test-fixtures.md +0 -52
  234. package/docs/retro/0380-extract-layered-settings-loader.md +0 -97
  235. package/docs/retro/0381-replace-concurrency-queue-with-limiter.md +0 -95
  236. package/docs/retro/0400-include-parent-prompt-in-replace-mode.md +0 -84
  237. package/docs/retro/0403-abort-subagents-on-interrupt.md +0 -90
  238. package/docs/retro/0412-unify-session-mock-builders.md +0 -80
  239. package/docs/retro/0420-fold-run-metrics-live-activity-onto-record.md +0 -90
  240. package/docs/retro/0421-migrate-activity-readers-to-record-getters.md +0 -105
  241. package/docs/retro/0422-delete-activity-tracker-ui-observer.md +0 -97
  242. package/docs/retro/0423-widget-self-drive-from-lifecycle.md +0 -112
  243. package/docs/retro/0424-drop-widget-dep-from-subagent-tool.md +0 -89
  244. package/docs/retro/0425-reconcile-subagent-events-contract.md +0 -101
  245. package/docs/retro/0426-consolidate-residual-test-clone-families.md +0 -100
  246. package/docs/retro/0427-reconsider-ui-direction.md +0 -102
  247. package/docs/retro/0441-remove-agent-definition-management-subtree.md +0 -45
  248. package/docs/retro/0442-dissolve-agents-conversation-viewer.md +0 -108
  249. package/docs/retro/0444-shrink-widget-to-background-agents.md +0 -81
  250. package/docs/retro/0445-native-session-navigation.md +0 -122
  251. package/docs/retro/0446-spike-session-navigation-entry-criteria.md +0 -99
  252. package/docs/retro/0447-extract-subagents-settings-command.md +0 -91
  253. package/docs/retro/0448-block-disabled-agent-spawn.md +0 -75
  254. package/docs/retro/0462-navigation-renderer-tui-components.md +0 -105
  255. package/docs/retro/0463-file-snapshot-source-evicted-agents.md +0 -103
  256. package/src/observation/notification-state.ts +0 -27
  257. package/vitest.config.ts +0 -14
@@ -1,179 +0,0 @@
1
- ---
2
- issue: 123
3
- issue_title: "refactor(pi-subagents): remove vi.fn() cast smell from test helpers"
4
- ---
5
-
6
- # Remove vi.fn() cast smell from test helpers
7
-
8
- ## Problem Statement
9
-
10
- Several test files construct mock objects typed to narrow interfaces (`AgentManagerLike`, `LifecycleRuntime`, `LifecycleManager`, `ToolStartRuntime`).
11
- Because the returned objects are typed to the interface — not to Vitest's mock types — tests that need to configure individual method stubs are forced to cast:
12
-
13
- ```typescript
14
- (deps.manager.abort as ReturnType<typeof vi.fn>).mockReturnValue(false);
15
- (deps.manager.getRecord as ReturnType<typeof vi.fn>).mockReturnValue(record);
16
- ```
17
-
18
- This silences TypeScript without constraining the call's return type — if `getRecord`'s return type changes, the cast won't catch it.
19
- Nine occurrences exist across three test files.
20
-
21
- ## Goals
22
-
23
- - Eliminate all `as ReturnType<typeof vi.fn>` casts from the test suite.
24
- - Preserve type safety: mock configuration calls should be checked against the real method signatures.
25
- - Keep the change minimal — this is a test hygiene fix, not a structural redesign.
26
-
27
- ## Non-Goals
28
-
29
- - Changing `AgentManagerLike`, `LifecycleRuntime`, `LifecycleManager`, `ToolStartRuntime`, or any production code.
30
- - Restructuring test layout or merging describe blocks.
31
-
32
- ## Background
33
-
34
- The cast pattern was noted during #111 implementation and preserved to keep scope tight.
35
- Issue #111 (split `AgentRecord` lifecycle state) is now closed and implemented.
36
-
37
- ### Affected files
38
-
39
- | File | Occurrences | Interface |
40
- | ---------------------------------- | ----------- | -------------------------------------- |
41
- | `test/service-adapter.test.ts` | 5 | `AgentManagerLike` |
42
- | `test/handlers/lifecycle.test.ts` | 2 | `LifecycleRuntime`, `LifecycleManager` |
43
- | `test/handlers/tool-start.test.ts` | 2 | `ToolStartRuntime` |
44
-
45
- ### Cast sites by file
46
-
47
- `service-adapter.test.ts` — the "steer, abort, waitForAll, hasRunning" block's `createDeps` returns `AdapterDeps` directly.
48
- Five casts reconfigure `getRecord`, `abort`, or `queueSteer` after construction:
49
-
50
- 1. `(deps.manager.abort as ReturnType<typeof vi.fn>).mockReturnValue(false)`
51
- 2. `(deps.manager.getRecord as ReturnType<typeof vi.fn>).mockReturnValue({...})` (×4)
52
-
53
- `lifecycle.test.ts` — mock objects are assigned to `let` variables in `beforeEach`, typed to `LifecycleRuntime` and `LifecycleManager`.
54
- Two casts reconfigure methods to track call order:
55
-
56
- 1. `(runtime.setSessionContext as ReturnType<typeof vi.fn>).mockImplementation(...)`
57
- 2. `(manager.clearCompleted as ReturnType<typeof vi.fn>).mockImplementation(...)`
58
-
59
- Note: the same file already uses `vi.mocked()` in the shutdown-order test — both patterns coexist, which is itself a consistency smell.
60
-
61
- `tool-start.test.ts` — mock object assigned to a `let` variable typed to `ToolStartRuntime`.
62
- Two casts reconfigure methods to track call order:
63
-
64
- 1. `(runtime.setUICtx as ReturnType<typeof vi.fn>).mockImplementation(...)`
65
- 2. `(runtime.onTurnStart as ReturnType<typeof vi.fn>).mockImplementation(...)`
66
-
67
- ### Approach: named-variable extraction
68
-
69
- Extract individual `vi.fn()` stubs into named variables.
70
- This is the approach the issue recommends and it aligns with the testing skill's guidance on extractable stubs.
71
-
72
- The alternative — `vi.mocked()` — is already used in `lifecycle.test.ts` for the shutdown-order test and works for hand-built mocks, but is semantically less clean: `vi.mocked()` asserts that a value is already a mock, which is true here but opaque to readers.
73
- Named variables make the mock-ness explicit at the construction site.
74
-
75
- For `lifecycle.test.ts`, the named-variable approach also eliminates the inconsistency between the two ordering tests — one currently uses `vi.mocked()` and the other uses casts.
76
- After this change both will use named stubs.
77
-
78
- ## Design Overview
79
-
80
- ### service-adapter.test.ts
81
-
82
- Refactor the "steer, abort, waitForAll, hasRunning" block's `createDeps` to return named stubs:
83
-
84
- ```typescript
85
- function createDeps(overrides: Partial<AdapterDeps> = {}) {
86
- const mockGetRecord = vi.fn<AgentManagerLike["getRecord"]>();
87
- const mockAbort = vi.fn<AgentManagerLike["abort"]>(() => true);
88
- const mockQueueSteer = vi.fn<AgentManagerLike["queueSteer"]>(() => true);
89
-
90
- const deps: AdapterDeps = {
91
- manager: {
92
- spawn: vi.fn(() => "id"),
93
- getRecord: mockGetRecord,
94
- listAgents: vi.fn(() => []),
95
- abort: mockAbort,
96
- waitForAll: vi.fn(async () => {}),
97
- hasRunning: vi.fn(() => true),
98
- queueSteer: mockQueueSteer,
99
- },
100
- resolveModel: vi.fn(),
101
- getCtx: () => ({ pi: {}, ctx: {} }),
102
- getModelRegistry: () => ({ find: () => null, getAll: () => [] }),
103
- ...overrides,
104
- };
105
-
106
- return { deps, mockGetRecord, mockAbort, mockQueueSteer };
107
- }
108
- ```
109
-
110
- Callers destructure what they need:
111
-
112
- ```typescript
113
- const { deps, mockAbort } = createDeps();
114
- mockAbort.mockReturnValue(false); // ← type-checked, no cast
115
- ```
116
-
117
- ### lifecycle.test.ts
118
-
119
- Promote the `beforeEach`-scoped `runtime` and `manager` mock construction to use named stubs.
120
- The stubs that need reconfiguration (`setSessionContext`, `clearCompleted`) become named `let` variables alongside the existing `runtime`/`manager` lets, reset in `beforeEach`:
121
-
122
- ```typescript
123
- let mockSetSessionContext: MockInstance<LifecycleRuntime["setSessionContext"]>;
124
- let mockClearCompleted: MockInstance<LifecycleManager["clearCompleted"]>;
125
- // ...assigned in beforeEach when building runtime/manager
126
- ```
127
-
128
- Also convert the shutdown-order test's `vi.mocked()` calls to the same pattern for consistency — `unpublishService`, `clearSessionContext`, `abortAll`, `disposeNotifications`, `dispose` all become named stubs.
129
-
130
- ### tool-start.test.ts
131
-
132
- Same pattern: promote `setUICtx` and `onTurnStart` to named `let` variables:
133
-
134
- ```typescript
135
- let mockSetUICtx: MockInstance<ToolStartRuntime["setUICtx"]>;
136
- let mockOnTurnStart: MockInstance<ToolStartRuntime["onTurnStart"]>;
137
- ```
138
-
139
- ## Module-Level Changes
140
-
141
- | File | Change |
142
- | ---------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
143
- | `test/service-adapter.test.ts` | Refactor `createDeps` in the "steer, abort, waitForAll, hasRunning" block to return named mock stubs alongside `deps`. Update all 5 cast sites to use named stubs. |
144
- | `test/handlers/lifecycle.test.ts` | Extract `mockSetSessionContext`, `mockClearCompleted`, `mockAbortAll`, `mockDispose`, `mockClearSessionContext` as named `let` variables. Replace 2 casts and 5 `vi.mocked()` calls with named stubs. |
145
- | `test/handlers/tool-start.test.ts` | Extract `mockSetUICtx` and `mockOnTurnStart` as named `let` variables. Replace 2 casts with named stubs. |
146
-
147
- No production files are changed.
148
-
149
- ## Test Impact Analysis
150
-
151
- 1. No new tests are added — this is a refactoring of existing test infrastructure.
152
- 2. No tests become redundant — every existing assertion stays.
153
- 3. All existing tests must pass unchanged; only the mock-wiring changes.
154
-
155
- ## TDD Order
156
-
157
- 1. **Commit:** Refactor `createDeps` in `service-adapter.test.ts` to return named stubs; update all 5 cast sites.
158
- All tests pass before and after.
159
- Commit: `test: remove vi.fn() cast smell from service-adapter tests (#123)`
160
- 2. **Commit:** Extract named stubs in `lifecycle.test.ts`; replace 2 casts and 5 `vi.mocked()` calls.
161
- All tests pass.
162
- Commit: `test: remove vi.fn() cast smell from lifecycle tests (#123)`
163
- 3. **Commit:** Extract named stubs in `tool-start.test.ts`; replace 2 casts.
164
- All tests pass.
165
- Commit: `test: remove vi.fn() cast smell from tool-start tests (#123)`
166
-
167
- Each step is an independent file — order doesn't matter, but one-file-per-commit keeps diffs reviewable.
168
-
169
- ## Risks and Mitigations
170
-
171
- | Risk | Mitigation |
172
- | ----------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------- |
173
- | Overrides via `...overrides` in `service-adapter.test.ts` could replace a manager method, leaving the named stub disconnected | Only `manager`-level overrides are spread; individual method overrides aren't used in this block. |
174
- | Named stubs add return-surface to helpers | Each helper is test-local and the extra names are self-documenting. The alternative (casting) is worse. |
175
- | Converting `vi.mocked()` in `lifecycle.test.ts` shutdown test expands scope slightly beyond the cast pattern | Worth it for consistency — mixing `vi.mocked()` and named stubs in the same file is a different smell. |
176
-
177
- ## Open Questions
178
-
179
- None — the issue is fully scoped and the approach is established in the codebase.
@@ -1,207 +0,0 @@
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.
@@ -1,219 +0,0 @@
1
- ---
2
- issue: 132
3
- issue_title: "Inject IO collaborators into `assembleSessionConfig`"
4
- ---
5
-
6
- # Inject IO collaborators into session-config
7
-
8
- ## Problem Statement
9
-
10
- `assembleSessionConfig` is described as a pure configuration assembler, but it directly imports three IO-touching functions (`preloadSkills`, `buildMemoryBlock`, `buildReadOnlyMemoryBlock`) and one pure function (`buildAgentPrompt`).
11
- This forces `session-config.test.ts` to use 4 `vi.mock()` calls, 8 hoisted mock functions, and assertions that verify internal call sequences rather than output properties.
12
- The result is fragile tests that break on any internal restructuring even when observable behavior is unchanged.
13
-
14
- ## Goals
15
-
16
- - Define an `AssemblerIO` interface bundling the four collaborators.
17
- - Add `io: AssemblerIO` as a parameter to `assembleSessionConfig()`.
18
- - Replace direct imports of the four functions with calls through `io`.
19
- - Update the single production call site in `agent-runner.ts` to pass real implementations.
20
- - Eliminate all 4 `vi.mock()` calls in `session-config.test.ts`.
21
- - Shift test assertions toward output-property verification.
22
-
23
- ## Non-Goals
24
-
25
- - SDK boundary injection into `agent-runner` (Step H, #133) — depends on this change but is deferred to its own issue.
26
- - Consolidating shared test fixtures (#131) — independent refactor that can land before or after.
27
- - Changing the behavior of `assembleSessionConfig` — this is a pure structural refactor.
28
- - Injecting `getMemoryToolNames` / `getReadOnlyMemoryToolNames` — these are pure utility functions with no IO; they stay as direct imports.
29
-
30
- ## Background
31
-
32
- ### Current state
33
-
34
- `session-config.ts` imports four functions used during assembly:
35
-
36
- | Function | Module | IO? | Purpose in assembler |
37
- | -------------------------- | ----------------- | ------------------------------ | --------------------------------------- |
38
- | `preloadSkills` | `skill-loader.ts` | Yes (reads `.pi/skills` files) | Loads skill content into prompt extras |
39
- | `buildMemoryBlock` | `memory.ts` | Yes (reads `MEMORY.md`) | Builds read-write memory prompt section |
40
- | `buildReadOnlyMemoryBlock` | `memory.ts` | Yes (reads `MEMORY.md`) | Builds read-only memory prompt section |
41
- | `buildAgentPrompt` | `prompts.ts` | No (pure) | Assembles final system prompt string |
42
-
43
- The test file mocks all four via `vi.mock()` plus mocks `getMemoryToolNames` and `getReadOnlyMemoryToolNames` from `agent-types.ts` (pure functions that are mocked only for call-argument verification).
44
-
45
- ### Established DI pattern
46
-
47
- `AgentManager` already injects `AgentRunner` via its constructor options — the same tell-don't-ask pattern used here.
48
- `assembleSessionConfig` already receives an `AgentConfigLookup` registry by parameter (migrated in #80/#108), demonstrating the incremental injection approach.
49
-
50
- ### Architecture reference
51
-
52
- Phase 8, Step G in `docs/architecture/architecture.md`.
53
-
54
- ### Constraints from AGENTS.md
55
-
56
- - Keep scope tight; prefer small, reversible changes.
57
- - Prefer explicit configuration over hidden behavior.
58
- - Business logic should be pure functions — keep IO at the edges.
59
-
60
- ## Design Overview
61
-
62
- ### `AssemblerIO` interface
63
-
64
- Defined in `session-config.ts` alongside the existing assembler types:
65
-
66
- ```typescript
67
- export interface AssemblerIO {
68
- preloadSkills: (skills: string[], cwd: string) => PreloadedSkill[];
69
- buildMemoryBlock: (
70
- name: string,
71
- scope: MemoryScope,
72
- cwd: string,
73
- ) => string;
74
- buildReadOnlyMemoryBlock: (
75
- name: string,
76
- scope: MemoryScope,
77
- cwd: string,
78
- ) => string;
79
- buildAgentPrompt: (
80
- config: AgentPromptConfig,
81
- cwd: string,
82
- env: EnvInfo,
83
- parentPrompt?: string,
84
- extras?: PromptExtras,
85
- ) => string;
86
- }
87
- ```
88
-
89
- The interface uses the same parameter types as the real functions.
90
- The assembler calls `io.preloadSkills(...)` etc. instead of the direct imports.
91
-
92
- ### Call site in `agent-runner.ts`
93
-
94
- ```typescript
95
- import { preloadSkills } from "./skill-loader.js";
96
- import { buildMemoryBlock, buildReadOnlyMemoryBlock } from "./memory.js";
97
- import { buildAgentPrompt } from "./prompts.js";
98
-
99
- const io: AssemblerIO = {
100
- preloadSkills,
101
- buildMemoryBlock,
102
- buildReadOnlyMemoryBlock,
103
- buildAgentPrompt,
104
- };
105
-
106
- const cfg = assembleSessionConfig(type, ctx, options, env, registry, io);
107
- ```
108
-
109
- The runner constructs the real IO object once and passes it through.
110
- This keeps IO at the edge (runner) and makes the assembler a genuine pure function.
111
-
112
- ### Test-side stubs
113
-
114
- Tests create a plain object with `vi.fn()` stubs satisfying `AssemblerIO`:
115
-
116
- ```typescript
117
- const io: AssemblerIO = {
118
- preloadSkills: vi.fn(() => []),
119
- buildMemoryBlock: vi.fn(() => "memory block"),
120
- buildReadOnlyMemoryBlock: vi.fn(() => "read-only memory block"),
121
- buildAgentPrompt: vi.fn(() => "assembled system prompt"),
122
- };
123
- ```
124
-
125
- This replaces all four `vi.mock()` calls and the hoisted mocks for those modules.
126
-
127
- ### Pure utility functions stay as direct imports
128
-
129
- `getMemoryToolNames` and `getReadOnlyMemoryToolNames` from `agent-types.ts` are pure functions (no IO, no filesystem access).
130
- After the IO injection, the test's `vi.mock("../src/agent-types.js", ...)` can be removed and real implementations used.
131
- Tests that previously controlled these mocks to verify call arguments will instead set up input tool names to produce the desired output from the real functions, then assert on the returned `SessionConfig.toolNames`.
132
-
133
- ## Module-Level Changes
134
-
135
- ### Modified files
136
-
137
- 1. `src/session-config.ts`
138
- - Add `AssemblerIO` interface export.
139
- - Add `io: AssemblerIO` parameter to `assembleSessionConfig()` (after `registry`).
140
- - Replace `preloadSkills(...)` with `io.preloadSkills(...)`.
141
- - Replace `buildMemoryBlock(...)` with `io.buildMemoryBlock(...)`.
142
- - Replace `buildReadOnlyMemoryBlock(...)` with `io.buildReadOnlyMemoryBlock(...)`.
143
- - Replace `buildAgentPrompt(...)` with `io.buildAgentPrompt(...)`.
144
- - Remove imports of `preloadSkills`, `buildMemoryBlock`, `buildReadOnlyMemoryBlock`, `buildAgentPrompt`.
145
- - Keep imports of `getMemoryToolNames`, `getReadOnlyMemoryToolNames` (pure, no change).
146
-
147
- 2. `src/agent-runner.ts`
148
- - Add imports for `preloadSkills`, `buildMemoryBlock`, `buildReadOnlyMemoryBlock`, `buildAgentPrompt`.
149
- - Import `AssemblerIO` type from `session-config.ts`.
150
- - Construct `AssemblerIO` object from real implementations.
151
- - Pass `io` to `assembleSessionConfig()`.
152
-
153
- 3. `test/session-config.test.ts`
154
- - Remove all 4 `vi.mock()` calls and the corresponding hoisted mocks.
155
- - Create `io` stub object with `vi.fn()` implementations.
156
- - Pass `io` to every `assembleSessionConfig()` call.
157
- - Update memory-section tests to use real `getMemoryToolNames` / `getReadOnlyMemoryToolNames`.
158
- - Migrate mock-call assertions to output-property assertions where the output already captures the information.
159
-
160
- ## Test Impact Analysis
161
-
162
- 1. The IO injection enables testing `assembleSessionConfig` without any module mocking.
163
- Tests can choose to inject real implementations with controlled inputs (integration-style) or stubs (unit-style).
164
- Previously this was impossible without `vi.mock()`.
165
-
166
- 2. Several existing tests that only verified mock-call arguments become redundant once we verify the same information through output properties (e.g., "calls buildAgentPrompt with env, cwd, parentSystemPrompt, and extras" is redundant if we verify `result.systemPrompt` reflects those inputs).
167
- These can be simplified or removed.
168
-
169
- 3. Tests for model resolution, isolated mode, thinking level, and unknown-type fallback stay as-is — they already assert output properties and are unaffected by the IO injection.
170
-
171
- ## TDD Order
172
-
173
- 1. **Define `AssemblerIO` and inject into `assembleSessionConfig`.**
174
- Add the `AssemblerIO` interface to `session-config.ts`.
175
- Add `io: AssemblerIO` as a required parameter.
176
- Replace the 4 direct function calls with `io.*` calls.
177
- Remove the 4 function imports from `session-config.ts`.
178
- Add the 4 imports to `agent-runner.ts` and construct the `io` object at the call site.
179
- Run `pnpm run check` to verify types compile.
180
- Commit: `feat: inject IO collaborators into assembleSessionConfig (#132)`
181
-
182
- 2. **Migrate test file to use injected IO stubs.**
183
- Create an `io` stub object with `vi.fn()` stubs matching the existing hoisted mocks' default return values.
184
- Pass `io` to all `assembleSessionConfig()` calls.
185
- Remove the 3 `vi.mock()` calls for `prompts.js`, `memory.js`, and `skill-loader.js`.
186
- Remove the corresponding hoisted mock variables (`mockBuildAgentPrompt`, `mockBuildMemoryBlock`, `mockBuildReadOnlyMemoryBlock`, `mockPreloadSkills`).
187
- Update `beforeEach` to reset the `io` stubs instead.
188
- All existing tests pass with the same assertions (io stubs replace module mocks).
189
- Commit: `test: replace vi.mock with injected IO stubs in session-config tests`
190
-
191
- 3. **Drop the `agent-types.js` mock; use real pure functions.**
192
- Remove the `vi.mock("../src/agent-types.js", ...)` call and the `importOriginal` pattern.
193
- Remove hoisted `mockGetMemoryToolNames` and `mockGetReadOnlyMemoryToolNames`.
194
- Update memory-section tests to set up `mockGetToolNamesForType` return values that produce the desired output from the real `getMemoryToolNames` / `getReadOnlyMemoryToolNames`.
195
- Assertions shift from "mock was called with Set" to "result.toolNames contains expected names".
196
- Commit: `test: use real getMemoryToolNames in session-config tests`
197
-
198
- 4. **Shift remaining mock-call assertions to output-property checks.**
199
- Replace `expect(io.buildAgentPrompt).toHaveBeenCalledWith(...)` with assertions on `result.systemPrompt` (requires io.buildAgentPrompt stub to echo identifying values).
200
- Replace `expect(io.preloadSkills).toHaveBeenCalledWith(skillList, "/tmp")` with `result.extras.skillBlocks` checks (already partially present).
201
- Remove test cases that are now fully redundant with output-based tests in the same describe block.
202
- Clean up any unused imports and variables.
203
- Commit: `test: verify output properties in session-config tests (#132)`
204
-
205
- ## Risks and Mitigations
206
-
207
- | Risk | Mitigation |
208
- | --------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
209
- | Adding a parameter to `assembleSessionConfig` breaks the `agent-runner.ts` call site | Only one production call site exists; updated in the same commit (step 1). `pnpm run check` verifies. |
210
- | Removing `vi.mock()` causes tests to accidentally call real IO functions | The real functions are no longer imported by `session-config.ts` after step 1. The module simply doesn't reach them. Vitest will error if any unmocked import is called. |
211
- | Using real `getMemoryToolNames` / `getReadOnlyMemoryToolNames` makes tests depend on their implementation | These are pure, stable utility functions (return tool names from a set). Their behavior is well-defined and unlikely to change. Using real implementations is more robust than mocking. |
212
- | Step 2 touches 40+ call sites in the test file | All changes are mechanical (add `, io` argument). A find-and-replace handles it. Each call already passes `mockAgentLookup` as the last arg; the new arg follows the same pattern. |
213
-
214
- ## Open Questions
215
-
216
- - Should `AssemblerIO` be co-located in `session-config.ts` or extracted to a separate `session-config-types.ts`?
217
- The interface is small (4 methods) and tightly coupled to the assembler.
218
- Co-location in `session-config.ts` follows the existing pattern (`AssemblerContext`, `AssemblerOptions`, `SessionConfig` are all in the same file).
219
- Extract only if it grows or gains consumers beyond `agent-runner.ts`.