@gotgenes/pi-subagents 18.0.1 → 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 (260) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/docs/architecture/architecture.md +157 -204
  3. package/docs/architecture/history/phase-19-implement-ui-decisions.md +282 -0
  4. package/package.json +5 -6
  5. package/src/lifecycle/subagent.ts +5 -9
  6. package/src/observation/notification.ts +23 -10
  7. package/src/observation/subagent-events-observer.ts +1 -3
  8. package/src/tools/get-result-report.ts +60 -0
  9. package/src/tools/get-result-tool.ts +25 -40
  10. package/src/types.ts +1 -1
  11. package/src/ui/session-navigator.ts +0 -2
  12. package/.prettierignore +0 -5
  13. package/AGENTS.md +0 -7
  14. package/docs/plans/0048-implement-subagents-api.md +0 -304
  15. package/docs/plans/0049-remove-group-join-output-file-rpc.md +0 -180
  16. package/docs/plans/0051-update-adr-0001-hard-fork.md +0 -77
  17. package/docs/plans/0052-remove-scheduled-subagents.md +0 -133
  18. package/docs/plans/0053-extract-model-resolution-from-execute.md +0 -181
  19. package/docs/plans/0054-decompose-index-into-modules.md +0 -302
  20. package/docs/plans/0057-structured-debug-logging.md +0 -154
  21. package/docs/plans/0061-session-format-transcript.md +0 -284
  22. package/docs/plans/0066-replace-as-any-with-sdk-types.md +0 -215
  23. package/docs/plans/0069-create-subagent-runtime.md +0 -345
  24. package/docs/plans/0070-extract-event-handlers.md +0 -306
  25. package/docs/plans/0071-extract-session-config-assembler.md +0 -362
  26. package/docs/plans/0072-inject-agent-manager-collaborators.md +0 -329
  27. package/docs/plans/0076-inject-cwd-into-agent-manager.md +0 -102
  28. package/docs/plans/0077-inject-project-agents-dir.md +0 -115
  29. package/docs/plans/0080-consolidate-agent-config-lookup.md +0 -247
  30. package/docs/plans/0084-extract-git-worktree-manager.md +0 -142
  31. package/docs/plans/0087-evolve-subagent-runtime-methods.md +0 -240
  32. package/docs/plans/0098-extract-agent-record-state-machine.md +0 -435
  33. package/docs/plans/0099-replace-ctx-with-parent-snapshot.md +0 -488
  34. package/docs/plans/0100-replace-callback-threading-with-session-subscription.md +0 -454
  35. package/docs/plans/0102-consolidate-test-record-factory.md +0 -176
  36. package/docs/plans/0108-extract-agent-type-registry.md +0 -322
  37. package/docs/plans/0109-extract-settings-manager.md +0 -276
  38. package/docs/plans/0110-agent-activity-tracker.md +0 -297
  39. package/docs/plans/0111-split-agent-record-lifecycle.md +0 -582
  40. package/docs/plans/0112-replace-agent-manager-callbacks.md +0 -241
  41. package/docs/plans/0113-disambiguate-spawn-options.md +0 -155
  42. package/docs/plans/0114-narrow-agent-tool-menu-deps.md +0 -279
  43. package/docs/plans/0115-decompose-agent-tool.md +0 -337
  44. package/docs/plans/0116-type-housekeeping.md +0 -351
  45. package/docs/plans/0118-settings-manager-apply-methods.md +0 -271
  46. package/docs/plans/0123-remove-vi-fn-cast-smell.md +0 -179
  47. package/docs/plans/0131-consolidate-shared-test-fixtures.md +0 -207
  48. package/docs/plans/0132-inject-io-into-session-config.md +0 -219
  49. package/docs/plans/0133-inject-sdk-boundary-into-agent-runner.md +0 -373
  50. package/docs/plans/0134-reduce-as-any-casts.md +0 -366
  51. package/docs/plans/0135-extract-display-helpers.md +0 -182
  52. package/docs/plans/0136-decompose-agent-menu.md +0 -300
  53. package/docs/plans/0144-consolidate-observation-model.md +0 -263
  54. package/docs/plans/0145-decompose-execute-push-ctx-to-boundary.md +0 -290
  55. package/docs/plans/0146-narrow-ui-context.md +0 -319
  56. package/docs/plans/0147-inject-wrap-text-into-conversation-viewer.md +0 -166
  57. package/docs/plans/0148-split-agent-widget-rendering.md +0 -255
  58. package/docs/plans/0152-add-prompt-snippet.md +0 -91
  59. package/docs/plans/0164-reorganize-into-domain-directories.md +0 -409
  60. package/docs/plans/0165-decompose-resolved-spawn-config.md +0 -157
  61. package/docs/plans/0166-extract-parent-session-info.md +0 -231
  62. package/docs/plans/0167-narrow-runner-io.md +0 -150
  63. package/docs/plans/0168-extract-tool-filter-config.md +0 -173
  64. package/docs/plans/0169-extract-run-context-from-run-options.md +0 -194
  65. package/docs/plans/0170-reduce-build-content-lines-complexity.md +0 -225
  66. package/docs/plans/0171-reduce-render-result-complexity.md +0 -200
  67. package/docs/plans/0172-extract-turn-formatting.md +0 -206
  68. package/docs/plans/0180-reorder-append-prompt-for-kv-cache.md +0 -100
  69. package/docs/plans/0185-remove-persistent-agent-memory.md +0 -161
  70. package/docs/plans/0188-replace-any-casts-with-sdk-types.md +0 -162
  71. package/docs/plans/0192-define-session-context-interface.md +0 -107
  72. package/docs/plans/0193-runtime-owns-context-queries.md +0 -245
  73. package/docs/plans/0194-align-tool-interfaces-for-structural-typing.md +0 -139
  74. package/docs/plans/0195-convert-tool-factories-to-classes.md +0 -264
  75. package/docs/plans/0196-convert-runner-menu-to-classes.md +0 -268
  76. package/docs/plans/0205-decompose-render-widget-lines.md +0 -140
  77. package/docs/plans/0206-decompose-show-agent-detail.md +0 -211
  78. package/docs/plans/0207-decompose-agent-widget-update.md +0 -242
  79. package/docs/plans/0208-extract-shared-test-fixtures.md +0 -298
  80. package/docs/plans/0214-convert-remaining-closure-factories-to-classes.md +0 -261
  81. package/docs/plans/0215-decompose-build-parent-context.md +0 -166
  82. package/docs/plans/0216-decompose-start-agent.md +0 -255
  83. package/docs/plans/0217-extract-overwrite-guard.md +0 -176
  84. package/docs/plans/0218-push-sdk-boundary-in-settings.md +0 -172
  85. package/docs/plans/0219-reduce-test-duplication-top-3-clone-families.md +0 -162
  86. package/docs/plans/0227-evolve-agent-record-into-agent.md +0 -322
  87. package/docs/plans/0228-async-start-agent-dissolve-run-handle.md +0 -288
  88. package/docs/plans/0229-agent-born-complete.md +0 -564
  89. package/docs/plans/0230-extract-concurrency-queue.md +0 -265
  90. package/docs/plans/0231-push-exec-registry-to-runner.md +0 -245
  91. package/docs/plans/0232-agent-resume-internal-observer-lifecycle.md +0 -180
  92. package/docs/plans/0237-remove-disallowed-tools.md +0 -180
  93. package/docs/plans/0238-remove-extensions-filtering.md +0 -191
  94. package/docs/plans/0239-collapse-filter-active-tools.md +0 -217
  95. package/docs/plans/0242-rename-agent-tool-to-subagent.md +0 -165
  96. package/docs/plans/0256-extract-worktree-isolation.md +0 -256
  97. package/docs/plans/0257-extract-child-session-factory.md +0 -285
  98. package/docs/plans/0262-add-workspace-provider-seam.md +0 -264
  99. package/docs/plans/0264-remove-extension-lifecycle-control.md +0 -277
  100. package/docs/plans/0265-born-complete-subagent-session.md +0 -333
  101. package/docs/plans/0270-type-consumable-public-surface.md +0 -204
  102. package/docs/plans/0272-export-workspace-collaborator-types.md +0 -147
  103. package/docs/plans/0277-encapsulate-agent-session.md +0 -304
  104. package/docs/plans/0280-rename-agent-to-subagent.md +0 -197
  105. package/docs/plans/0360-custom-agents-default-append.md +0 -132
  106. package/docs/plans/0373-extract-subagent-state.md +0 -250
  107. package/docs/plans/0374-encapsulate-subagent-start-notification.md +0 -268
  108. package/docs/plans/0375-extract-run-listener-workspace-bracket.md +0 -300
  109. package/docs/plans/0376-extract-manager-observer.md +0 -232
  110. package/docs/plans/0377-split-widget-delegation-from-runtime.md +0 -254
  111. package/docs/plans/0378-consolidate-lifecycle-test-fixtures.md +0 -243
  112. package/docs/plans/0379-consolidate-ui-tools-test-fixtures.md +0 -335
  113. package/docs/plans/0380-extract-layered-settings-loader.md +0 -223
  114. package/docs/plans/0381-replace-concurrency-queue-with-limiter.md +0 -267
  115. package/docs/plans/0400-include-parent-prompt-in-replace-mode.md +0 -199
  116. package/docs/plans/0403-abort-subagents-on-interrupt.md +0 -180
  117. package/docs/plans/0412-unify-session-mock-builders.md +0 -146
  118. package/docs/plans/0420-fold-run-metrics-live-activity-onto-record.md +0 -228
  119. package/docs/plans/0421-migrate-activity-readers-to-record-getters.md +0 -309
  120. package/docs/plans/0422-delete-activity-tracker-ui-observer.md +0 -204
  121. package/docs/plans/0423-widget-self-drive-from-lifecycle.md +0 -264
  122. package/docs/plans/0424-drop-widget-dep-from-subagent-tool.md +0 -166
  123. package/docs/plans/0425-reconcile-subagent-events-contract.md +0 -144
  124. package/docs/plans/0426-consolidate-residual-test-clone-families.md +0 -235
  125. package/docs/plans/0427-reconsider-ui-direction.md +0 -212
  126. package/docs/plans/0441-remove-agent-definition-management-subtree.md +0 -180
  127. package/docs/plans/0442-dissolve-agents-conversation-viewer.md +0 -195
  128. package/docs/plans/0443-consolidate-remaining-test-clone-families.md +0 -268
  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/plans/0470-refresh-readme-command-surface.md +0 -159
  137. package/docs/retro/0048-implement-subagents-api.md +0 -44
  138. package/docs/retro/0049-remove-group-join-output-file-rpc.md +0 -49
  139. package/docs/retro/0051-update-adr-0001-hard-fork.md +0 -39
  140. package/docs/retro/0053-extract-model-resolution-from-execute.md +0 -40
  141. package/docs/retro/0054-decompose-index-into-modules.md +0 -53
  142. package/docs/retro/0057-structured-debug-logging.md +0 -77
  143. package/docs/retro/0061-session-format-transcript.md +0 -41
  144. package/docs/retro/0066-replace-as-any-with-sdk-types.md +0 -44
  145. package/docs/retro/0069-create-subagent-runtime.md +0 -43
  146. package/docs/retro/0070-extract-event-handlers.md +0 -49
  147. package/docs/retro/0071-extract-session-config-assembler.md +0 -60
  148. package/docs/retro/0072-inject-agent-manager-collaborators.md +0 -46
  149. package/docs/retro/0076-inject-cwd-into-agent-manager.md +0 -35
  150. package/docs/retro/0077-inject-project-agents-dir.md +0 -33
  151. package/docs/retro/0080-consolidate-agent-config-lookup.md +0 -35
  152. package/docs/retro/0084-extract-git-worktree-manager.md +0 -37
  153. package/docs/retro/0087-evolve-subagent-runtime-methods.md +0 -42
  154. package/docs/retro/0098-extract-agent-record-state-machine.md +0 -46
  155. package/docs/retro/0099-replace-ctx-with-parent-snapshot.md +0 -37
  156. package/docs/retro/0100-replace-callback-threading-with-session-subscription.md +0 -36
  157. package/docs/retro/0102-consolidate-test-record-factory.md +0 -30
  158. package/docs/retro/0108-extract-agent-type-registry.md +0 -41
  159. package/docs/retro/0109-extract-settings-manager.md +0 -55
  160. package/docs/retro/0110-agent-activity-tracker.md +0 -44
  161. package/docs/retro/0111-split-agent-record-lifecycle.md +0 -61
  162. package/docs/retro/0112-replace-agent-manager-callbacks.md +0 -35
  163. package/docs/retro/0113-disambiguate-spawn-options.md +0 -29
  164. package/docs/retro/0114-narrow-agent-tool-menu-deps.md +0 -38
  165. package/docs/retro/0115-decompose-agent-tool.md +0 -51
  166. package/docs/retro/0116-type-housekeeping.md +0 -42
  167. package/docs/retro/0118-settings-manager-apply-methods.md +0 -40
  168. package/docs/retro/0123-remove-vi-fn-cast-smell.md +0 -49
  169. package/docs/retro/0131-consolidate-shared-test-fixtures.md +0 -46
  170. package/docs/retro/0132-inject-io-into-session-config.md +0 -33
  171. package/docs/retro/0133-inject-sdk-boundary-into-agent-runner.md +0 -45
  172. package/docs/retro/0134-reduce-as-any-casts.md +0 -56
  173. package/docs/retro/0135-extract-display-helpers.md +0 -38
  174. package/docs/retro/0136-decompose-agent-menu.md +0 -43
  175. package/docs/retro/0144-consolidate-observation-model.md +0 -39
  176. package/docs/retro/0145-decompose-execute-push-ctx-to-boundary.md +0 -56
  177. package/docs/retro/0146-narrow-ui-context.md +0 -70
  178. package/docs/retro/0147-inject-wrap-text-into-conversation-viewer.md +0 -90
  179. package/docs/retro/0148-split-agent-widget-rendering.md +0 -39
  180. package/docs/retro/0152-add-prompt-snippet.md +0 -34
  181. package/docs/retro/0164-reorganize-into-domain-directories.md +0 -82
  182. package/docs/retro/0165-decompose-resolved-spawn-config.md +0 -78
  183. package/docs/retro/0166-extract-parent-session-info.md +0 -66
  184. package/docs/retro/0167-narrow-runner-io.md +0 -63
  185. package/docs/retro/0168-extract-tool-filter-config.md +0 -76
  186. package/docs/retro/0169-extract-run-context-from-run-options.md +0 -68
  187. package/docs/retro/0170-reduce-build-content-lines-complexity.md +0 -72
  188. package/docs/retro/0171-reduce-render-result-complexity.md +0 -77
  189. package/docs/retro/0172-extract-turn-formatting.md +0 -80
  190. package/docs/retro/0180-reorder-append-prompt-for-kv-cache.md +0 -62
  191. package/docs/retro/0185-remove-persistent-agent-memory.md +0 -73
  192. package/docs/retro/0188-replace-any-casts-with-sdk-types.md +0 -69
  193. package/docs/retro/0192-define-session-context-interface.md +0 -59
  194. package/docs/retro/0193-runtime-owns-context-queries.md +0 -75
  195. package/docs/retro/0194-align-tool-interfaces-for-structural-typing.md +0 -62
  196. package/docs/retro/0195-convert-tool-factories-to-classes.md +0 -84
  197. package/docs/retro/0196-convert-runner-menu-to-classes.md +0 -73
  198. package/docs/retro/0205-decompose-render-widget-lines.md +0 -64
  199. package/docs/retro/0206-decompose-show-agent-detail.md +0 -67
  200. package/docs/retro/0207-decompose-agent-widget-update.md +0 -92
  201. package/docs/retro/0208-extract-shared-test-fixtures.md +0 -102
  202. package/docs/retro/0214-convert-remaining-closure-factories-to-classes.md +0 -66
  203. package/docs/retro/0215-decompose-build-parent-context.md +0 -61
  204. package/docs/retro/0216-decompose-start-agent.md +0 -80
  205. package/docs/retro/0217-extract-overwrite-guard.md +0 -63
  206. package/docs/retro/0218-push-sdk-boundary-in-settings.md +0 -72
  207. package/docs/retro/0219-reduce-test-duplication-top-3-clone-families.md +0 -64
  208. package/docs/retro/0227-evolve-agent-record-into-agent.md +0 -80
  209. package/docs/retro/0228-async-start-agent-dissolve-run-handle.md +0 -80
  210. package/docs/retro/0229-agent-born-complete.md +0 -89
  211. package/docs/retro/0230-extract-concurrency-queue.md +0 -38
  212. package/docs/retro/0231-push-exec-registry-to-runner.md +0 -71
  213. package/docs/retro/0232-agent-resume-internal-observer-lifecycle.md +0 -109
  214. package/docs/retro/0237-remove-disallowed-tools.md +0 -78
  215. package/docs/retro/0238-remove-extensions-filtering.md +0 -72
  216. package/docs/retro/0239-collapse-filter-active-tools.md +0 -70
  217. package/docs/retro/0242-rename-agent-tool-to-subagent.md +0 -82
  218. package/docs/retro/0256-extract-worktree-isolation.md +0 -89
  219. package/docs/retro/0257-extract-child-session-factory.md +0 -33
  220. package/docs/retro/0262-add-workspace-provider-seam.md +0 -89
  221. package/docs/retro/0264-remove-extension-lifecycle-control.md +0 -91
  222. package/docs/retro/0265-born-complete-subagent-session.md +0 -97
  223. package/docs/retro/0270-type-consumable-public-surface.md +0 -108
  224. package/docs/retro/0272-export-workspace-collaborator-types.md +0 -38
  225. package/docs/retro/0277-encapsulate-agent-session.md +0 -80
  226. package/docs/retro/0280-rename-agent-to-subagent.md +0 -96
  227. package/docs/retro/0360-custom-agents-default-append.md +0 -81
  228. package/docs/retro/0373-extract-subagent-state.md +0 -94
  229. package/docs/retro/0374-encapsulate-subagent-start-notification.md +0 -171
  230. package/docs/retro/0375-extract-run-listener-workspace-bracket.md +0 -104
  231. package/docs/retro/0376-extract-manager-observer.md +0 -81
  232. package/docs/retro/0377-split-widget-delegation-from-runtime.md +0 -105
  233. package/docs/retro/0378-consolidate-lifecycle-test-fixtures.md +0 -100
  234. package/docs/retro/0379-consolidate-ui-tools-test-fixtures.md +0 -52
  235. package/docs/retro/0380-extract-layered-settings-loader.md +0 -97
  236. package/docs/retro/0381-replace-concurrency-queue-with-limiter.md +0 -95
  237. package/docs/retro/0400-include-parent-prompt-in-replace-mode.md +0 -84
  238. package/docs/retro/0403-abort-subagents-on-interrupt.md +0 -90
  239. package/docs/retro/0412-unify-session-mock-builders.md +0 -80
  240. package/docs/retro/0420-fold-run-metrics-live-activity-onto-record.md +0 -90
  241. package/docs/retro/0421-migrate-activity-readers-to-record-getters.md +0 -105
  242. package/docs/retro/0422-delete-activity-tracker-ui-observer.md +0 -97
  243. package/docs/retro/0423-widget-self-drive-from-lifecycle.md +0 -112
  244. package/docs/retro/0424-drop-widget-dep-from-subagent-tool.md +0 -89
  245. package/docs/retro/0425-reconcile-subagent-events-contract.md +0 -101
  246. package/docs/retro/0426-consolidate-residual-test-clone-families.md +0 -100
  247. package/docs/retro/0427-reconsider-ui-direction.md +0 -102
  248. package/docs/retro/0441-remove-agent-definition-management-subtree.md +0 -97
  249. package/docs/retro/0442-dissolve-agents-conversation-viewer.md +0 -108
  250. package/docs/retro/0443-consolidate-remaining-test-clone-families.md +0 -28
  251. package/docs/retro/0444-shrink-widget-to-background-agents.md +0 -81
  252. package/docs/retro/0445-native-session-navigation.md +0 -122
  253. package/docs/retro/0446-spike-session-navigation-entry-criteria.md +0 -99
  254. package/docs/retro/0447-extract-subagents-settings-command.md +0 -91
  255. package/docs/retro/0448-block-disabled-agent-spawn.md +0 -75
  256. package/docs/retro/0462-navigation-renderer-tui-components.md +0 -105
  257. package/docs/retro/0463-file-snapshot-source-evicted-agents.md +0 -103
  258. package/docs/retro/0470-refresh-readme-command-surface.md +0 -40
  259. package/src/observation/notification-state.ts +0 -27
  260. 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`.