claude-code-session-manager 0.98.0 → 0.100.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (431) hide show
  1. package/README.md +3 -3
  2. package/bin/cli.cjs +16 -4
  3. package/bin/node-floor.cjs +41 -0
  4. package/dist/assets/{DataModel-DCEgMTjH.js → DataModel-HdR0AxFq.js} +1 -1
  5. package/dist/assets/{History-Bfdoywlh.js → History-Dr4Jr_5z.js} +2 -2
  6. package/dist/assets/{Hooks-CKDJjTJV.js → Hooks-NhrvH3JP.js} +3 -3
  7. package/dist/assets/{Library-7akfgy67.js → Library-Dv_EtIMr.js} +1 -1
  8. package/dist/assets/{MarkdownEditor-DDesiqet.js → MarkdownEditor-DTKDWV_n.js} +1 -1
  9. package/dist/assets/{McpServers-D4lZtXof.js → McpServers-M034G9B3.js} +2 -2
  10. package/dist/assets/{Memory-CeI8G1w5.js → Memory-DzF1B4gx.js} +6 -6
  11. package/dist/assets/{Permissions-BGleLja4.js → Permissions-DrgbSGWl.js} +3 -3
  12. package/dist/assets/{Plugins-P4eCXy_S.js → Plugins-Bd3ef35w.js} +2 -2
  13. package/dist/assets/{ProvenanceBadge-BFczzI1B.js → ProvenanceBadge-DcBHT1Iy.js} +1 -1
  14. package/dist/assets/{SaveBar-l9jnEMES.js → SaveBar-rimJem-Y.js} +1 -1
  15. package/dist/assets/Scheduler-DjgFJl2w.js +16 -0
  16. package/dist/assets/{ScopeSwitcher-CjJi8ST5.js → ScopeSwitcher-CnAzTBrl.js} +1 -1
  17. package/dist/assets/Settings-Dc1F_sSg.js +3 -0
  18. package/dist/assets/{SkillReferenceGraph-CjbO01Sg.js → SkillReferenceGraph-B9aZDecM.js} +1 -1
  19. package/dist/assets/{Skills-BxZ9_fCR.js → Skills-DRXd18gu.js} +2 -2
  20. package/dist/assets/{SystemPrompt-D8vfFb8b.js → SystemPrompt-ahy5-Fwe.js} +1 -1
  21. package/dist/assets/{TagLibrary-2vzUo7YL.js → TagLibrary-C_tToDeL.js} +1 -1
  22. package/dist/assets/{TiptapBody-B8ZfbK90.js → TiptapBody-DvdefS6K.js} +1 -1
  23. package/dist/assets/{Toggle-BbW4nGGf.js → Toggle-6Jl5Njc6.js} +1 -1
  24. package/dist/assets/{index-CKH5Uxik.css → index-Bta-hwud.css} +1 -1
  25. package/dist/assets/{index-Cd98Q1lD.js → index-vx8O73l8.js} +497 -502
  26. package/dist/assets/{settingsSchema-IhUfucCF.js → settingsSchema-D6iRguNV.js} +1 -1
  27. package/dist/index.html +2 -2
  28. package/package.json +25 -29
  29. package/plugins/CLAUDE.md +6 -6
  30. package/plugins/session-manager-dev/skills/builder/4-manual/SKILL.md +21 -19
  31. package/plugins/session-manager-dev/skills/builder/SKILL.md +3 -3
  32. package/plugins/session-manager-dev/skills/develop/SKILL.md +254 -510
  33. package/plugins/session-manager-dev/skills/develop/standards.md +17 -22
  34. package/scripts/README.md +3 -11
  35. package/scripts/audit-ops-hygiene.cjs +3 -3
  36. package/scripts/hooks/lib/guard-prd-writes-policy.cjs +1 -1
  37. package/scripts/hooks/lib/guard-self-schedule-policy.cjs +1 -1
  38. package/scripts/mint-epic.cjs +2 -3
  39. package/scripts/scheduler-mcp-server.cjs +36 -7
  40. package/src/main/agentLibrary.cjs +22 -2
  41. package/src/main/build-info.json +4 -4
  42. package/src/main/config.cjs +41 -38
  43. package/src/main/docEdit.cjs +4 -1
  44. package/src/main/files.cjs +2 -5
  45. package/src/main/health.cjs +1 -1
  46. package/src/main/index.cjs +23 -10
  47. package/src/main/ipcSchemas.cjs +28 -35
  48. package/src/main/lib/agentPersonaSchema.cjs +13 -2
  49. package/src/main/lib/atomicFs.cjs +116 -0
  50. package/src/main/lib/branchSweep.cjs +13 -12
  51. package/src/main/lib/buildTarget.cjs +3 -3
  52. package/src/main/lib/claudeCliCaps.cjs +129 -0
  53. package/src/main/lib/credentials.cjs +2 -4
  54. package/src/main/lib/crossProjectFeedback.cjs +3 -3
  55. package/src/main/lib/cwdClassify.cjs +15 -3
  56. package/src/main/lib/definitionOfDone.cjs +207 -125
  57. package/src/main/lib/dodDrainHook.cjs +11 -5
  58. package/src/main/lib/epicMint.cjs +2 -4
  59. package/src/main/lib/epicStatusMirror.cjs +8 -10
  60. package/src/main/lib/epicWorktreeProjectConfig.cjs +2 -4
  61. package/src/main/lib/gateAuthority.cjs +96 -0
  62. package/src/main/lib/gitExec.cjs +69 -0
  63. package/src/main/lib/gitWorktree.cjs +656 -92
  64. package/src/main/lib/instanceLock.cjs +5 -8
  65. package/src/main/lib/launchFailure.cjs +2 -3
  66. package/src/main/lib/macroLibrary.cjs +386 -0
  67. package/src/main/lib/mcpToolCatalog.cjs +26 -17
  68. package/src/main/lib/needsReviewLedger.cjs +1 -1
  69. package/src/main/lib/opsErrorLog.cjs +11 -1
  70. package/src/main/lib/opsOwnership.cjs +0 -5
  71. package/src/main/lib/pidAlive.cjs +32 -0
  72. package/src/main/lib/prdCreate.cjs +121 -12
  73. package/src/main/lib/prdDisposition.cjs +2 -2
  74. package/src/main/lib/prdGateFiles.cjs +284 -0
  75. package/src/main/lib/prdLocations.cjs +61 -0
  76. package/src/main/lib/prdMigration.cjs +2 -3
  77. package/src/main/lib/prdSizing.cjs +2 -1
  78. package/src/main/lib/promptSessionSchema.cjs +10 -2
  79. package/src/main/lib/rcaReport.cjs +4 -6
  80. package/src/main/lib/reaperHelpers.cjs +8 -1
  81. package/src/main/lib/reservationExpiry.cjs +6 -1
  82. package/src/main/lib/reviewNotice.cjs +263 -0
  83. package/src/main/lib/runClaudeP.cjs +4 -1
  84. package/src/main/lib/schedulerPaths.cjs +22 -2
  85. package/src/main/lib/sessionSlots.cjs +2 -4
  86. package/src/main/lib/shippedPersonaSeeds.cjs +179 -0
  87. package/src/main/lib/timeoutShim.cjs +123 -0
  88. package/src/main/lib/timeoutShimScript.cjs +275 -0
  89. package/src/main/lib/upgradeDrain.cjs +4 -10
  90. package/src/main/lib/watchdogHelpers.cjs +12 -25
  91. package/src/main/lib/workTypeLibrary.cjs +7 -1
  92. package/src/main/promptSessionEvents.cjs +37 -13
  93. package/src/main/scheduler.cjs +910 -198
  94. package/src/main/seedAgentPersonas.cjs +278 -23
  95. package/src/main/supervisor.cjs +4 -1
  96. package/src/main/templates/PRD_AUTHORING.md +126 -406
  97. package/src/preload/__tests__/preload-surface.test.cjs +68 -0
  98. package/src/preload/api.d.ts +41 -94
  99. package/src/preload/index.cjs +14 -9
  100. package/src/seed/agents/architect.md +1 -0
  101. package/src/seed/agents/dev-lead.md +17 -18
  102. package/src/seed/agents/project-home-builder.md +1 -0
  103. package/src/seed/agents/validator.md +10 -4
  104. package/dist/assets/HostBilko-CDVHE0hT.js +0 -1
  105. package/dist/assets/Scheduler-C_phpeUS.js +0 -16
  106. package/dist/assets/Settings-BIwZ3FGQ.js +0 -3
  107. package/src/main/__tests__/activeIndexMerge.test.cjs +0 -235
  108. package/src/main/__tests__/agentEffortResolve.test.cjs +0 -117
  109. package/src/main/__tests__/agentLibrary.test.cjs +0 -276
  110. package/src/main/__tests__/agentModelResolve.test.cjs +0 -311
  111. package/src/main/__tests__/agentOverlayWrite.test.cjs +0 -95
  112. package/src/main/__tests__/bilkoHost-deriveSlug.test.cjs +0 -26
  113. package/src/main/__tests__/bilkoHost-integration.test.cjs +0 -118
  114. package/src/main/__tests__/bilkoHostCore.test.cjs +0 -72
  115. package/src/main/__tests__/broadcastCoalescer.test.cjs +0 -122
  116. package/src/main/__tests__/chat-cancel-terminal.test.cjs +0 -120
  117. package/src/main/__tests__/chat-dead-channels.test.cjs +0 -63
  118. package/src/main/__tests__/chat-exit-close-race.test.cjs +0 -146
  119. package/src/main/__tests__/chat-mcp-consent-notice.test.cjs +0 -139
  120. package/src/main/__tests__/chat-preamble-anchors.test.cjs +0 -100
  121. package/src/main/__tests__/chat-queue.test.cjs +0 -97
  122. package/src/main/__tests__/chat-stop-signal.test.cjs +0 -89
  123. package/src/main/__tests__/chatRunner-epic-worktree-execcwd.test.cjs +0 -125
  124. package/src/main/__tests__/chatRunner-session-flag-retry.test.cjs +0 -252
  125. package/src/main/__tests__/classifyPromptTicket.test.cjs +0 -101
  126. package/src/main/__tests__/classifyTranscriptLine.test.cjs +0 -201
  127. package/src/main/__tests__/computeDepHistorySatisfaction.test.cjs +0 -66
  128. package/src/main/__tests__/config-readText-bounded.test.cjs +0 -84
  129. package/src/main/__tests__/configWriteBoundaryOwners.test.cjs +0 -59
  130. package/src/main/__tests__/crossProjectFeedback.test.cjs +0 -334
  131. package/src/main/__tests__/crossProjectFeedbackRoutes.test.cjs +0 -161
  132. package/src/main/__tests__/dep-orphan-archive-health.test.cjs +0 -77
  133. package/src/main/__tests__/develop-skill-failure-modes.test.cjs +0 -70
  134. package/src/main/__tests__/docEdit.test.cjs +0 -244
  135. package/src/main/__tests__/dod-batchkey.test.cjs +0 -183
  136. package/src/main/__tests__/dod-drain-hook.test.cjs +0 -302
  137. package/src/main/__tests__/dod-report.test.cjs +0 -304
  138. package/src/main/__tests__/dod-reverify.test.cjs +0 -285
  139. package/src/main/__tests__/epicContextDigest.test.cjs +0 -174
  140. package/src/main/__tests__/epicMint.test.cjs +0 -332
  141. package/src/main/__tests__/epicMintTelemetryTap.test.cjs +0 -64
  142. package/src/main/__tests__/epicStatusMirror.test.cjs +0 -110
  143. package/src/main/__tests__/epicValidationHook.test.cjs +0 -291
  144. package/src/main/__tests__/exchanges.test.cjs +0 -122
  145. package/src/main/__tests__/exchangesPromptId.test.cjs +0 -61
  146. package/src/main/__tests__/extractJson.test.cjs +0 -51
  147. package/src/main/__tests__/files-reject-credentials.test.cjs +0 -40
  148. package/src/main/__tests__/fixtures/1218-fo-01-move-scripts-lib-into-src-main-lib.log +0 -556
  149. package/src/main/__tests__/flatPrdTickSweep.test.cjs +0 -110
  150. package/src/main/__tests__/health-build-freshness.test.cjs +0 -39
  151. package/src/main/__tests__/health-claude-md-budget.test.cjs +0 -57
  152. package/src/main/__tests__/health-credentials.test.cjs +0 -81
  153. package/src/main/__tests__/health-delegation-chain.test.cjs +0 -124
  154. package/src/main/__tests__/health-per-project-stall.test.cjs +0 -84
  155. package/src/main/__tests__/health-prd-migration.test.cjs +0 -37
  156. package/src/main/__tests__/health-queue-dispatch.test.cjs +0 -135
  157. package/src/main/__tests__/health-starve-escalation.test.cjs +0 -94
  158. package/src/main/__tests__/health-tick-liveness.test.cjs +0 -179
  159. package/src/main/__tests__/health-usage-poller.test.cjs +0 -144
  160. package/src/main/__tests__/health-worktree-cap-blocked.test.cjs +0 -65
  161. package/src/main/__tests__/heapSnapshot.test.cjs +0 -121
  162. package/src/main/__tests__/historyAggregatorIntraday.test.cjs +0 -313
  163. package/src/main/__tests__/historyDashboard.test.cjs +0 -163
  164. package/src/main/__tests__/historyRollup.test.cjs +0 -333
  165. package/src/main/__tests__/intradayRefresh.test.cjs +0 -39
  166. package/src/main/__tests__/ipcSchemas-dependsOn.test.cjs +0 -39
  167. package/src/main/__tests__/kg-augment.test.cjs +0 -195
  168. package/src/main/__tests__/loadGateDetailTick.test.cjs +0 -31
  169. package/src/main/__tests__/machineProfile.test.cjs +0 -152
  170. package/src/main/__tests__/mcpStatus.test.cjs +0 -61
  171. package/src/main/__tests__/memoryAggregate.test.cjs +0 -109
  172. package/src/main/__tests__/memoryStale.test.cjs +0 -88
  173. package/src/main/__tests__/needsReviewLedger.test.cjs +0 -162
  174. package/src/main/__tests__/openExternalApp-spawn-error.test.cjs +0 -25
  175. package/src/main/__tests__/opsErrorLog.test.cjs +0 -109
  176. package/src/main/__tests__/opsErrorLogTelemetryTap.test.cjs +0 -173
  177. package/src/main/__tests__/personaMerge.test.cjs +0 -169
  178. package/src/main/__tests__/planValidator.test.cjs +0 -125
  179. package/src/main/__tests__/pollLoop-dispatch-on-failure.test.cjs +0 -176
  180. package/src/main/__tests__/prd-group-allocator.test.cjs +0 -119
  181. package/src/main/__tests__/prdAdminRouteParity.test.cjs +0 -70
  182. package/src/main/__tests__/prdAdminRoutes.test.cjs +0 -718
  183. package/src/main/__tests__/prdAgentType.test.cjs +0 -103
  184. package/src/main/__tests__/prdAuthoringSeed.test.cjs +0 -39
  185. package/src/main/__tests__/prdCreate.test.cjs +0 -979
  186. package/src/main/__tests__/prdCreateAdoption.test.cjs +0 -205
  187. package/src/main/__tests__/prdCreateDisposition.test.cjs +0 -201
  188. package/src/main/__tests__/prdCreatePlanId.test.cjs +0 -132
  189. package/src/main/__tests__/prdFrontmatterAgentType.test.cjs +0 -117
  190. package/src/main/__tests__/prdFrontmatterDependsOn.test.cjs +0 -136
  191. package/src/main/__tests__/prdFrontmatterDisposition.test.cjs +0 -125
  192. package/src/main/__tests__/prdFrontmatterQuietMachine.test.cjs +0 -108
  193. package/src/main/__tests__/prdLocations.test.cjs +0 -195
  194. package/src/main/__tests__/prdLocationsArchived.test.cjs +0 -201
  195. package/src/main/__tests__/prdMigration.test.cjs +0 -349
  196. package/src/main/__tests__/prdMigrationLegacyAdopt.test.cjs +0 -91
  197. package/src/main/__tests__/prdParserHighWater.test.cjs +0 -74
  198. package/src/main/__tests__/prdParserSourcePromptId.test.cjs +0 -65
  199. package/src/main/__tests__/prdSetDisposition.test.cjs +0 -222
  200. package/src/main/__tests__/prdSizing.test.cjs +0 -106
  201. package/src/main/__tests__/prdSourcePromptIdBackfill.test.cjs +0 -118
  202. package/src/main/__tests__/prdUpdateDependsOn.test.cjs +0 -160
  203. package/src/main/__tests__/proc-role-env.test.cjs +0 -125
  204. package/src/main/__tests__/procname-claude-spawn-sites.test.cjs +0 -304
  205. package/src/main/__tests__/procname-sm-processes.test.cjs +0 -127
  206. package/src/main/__tests__/projectHomeAdminRoutes.test.cjs +0 -177
  207. package/src/main/__tests__/projectPages.test.cjs +0 -137
  208. package/src/main/__tests__/promptSessionEvents.test.cjs +0 -234
  209. package/src/main/__tests__/promptSessionSchema.test.cjs +0 -101
  210. package/src/main/__tests__/promptSessionTranscript.test.cjs +0 -0
  211. package/src/main/__tests__/promptSessionsCreateEpicHandler.test.cjs +0 -159
  212. package/src/main/__tests__/pty-epic-worktree-spawn-cwd.test.cjs +0 -282
  213. package/src/main/__tests__/pty-session-open-telemetry.test.cjs +0 -96
  214. package/src/main/__tests__/pty-write-result.test.cjs +0 -46
  215. package/src/main/__tests__/queue-health-verdict.test.cjs +0 -170
  216. package/src/main/__tests__/queue-starvation-dispatch-driver.test.cjs +0 -286
  217. package/src/main/__tests__/queue-starvation-per-project.test.cjs +0 -147
  218. package/src/main/__tests__/queueHistory.test.cjs +0 -355
  219. package/src/main/__tests__/queueOps-interactive-ac-lint.test.cjs +0 -65
  220. package/src/main/__tests__/queueOpsArchiveDestination.test.cjs +0 -65
  221. package/src/main/__tests__/queueOpsAutoArchive.test.cjs +0 -211
  222. package/src/main/__tests__/rateLimitPollerStreak.test.cjs +0 -128
  223. package/src/main/__tests__/rcaReport.test.cjs +0 -266
  224. package/src/main/__tests__/reconcileFlatPrdSweep.test.cjs +0 -119
  225. package/src/main/__tests__/reconcileTiming.test.cjs +0 -135
  226. package/src/main/__tests__/runLogRetention.test.cjs +0 -489
  227. package/src/main/__tests__/runVerify-atomic-verdicts.test.cjs +0 -26
  228. package/src/main/__tests__/runVerify-blocked-by-foreign-wip.test.cjs +0 -58
  229. package/src/main/__tests__/runVerify-landed-commit-outranks.test.cjs +0 -191
  230. package/src/main/__tests__/runVerify-policy-denial.test.cjs +0 -89
  231. package/src/main/__tests__/runVerify-transcript-commit-evidence.test.cjs +0 -225
  232. package/src/main/__tests__/runVerify.test.cjs +0 -1784
  233. package/src/main/__tests__/scheduleJobSchema.test.cjs +0 -127
  234. package/src/main/__tests__/scheduleJobStatusDrift.test.cjs +0 -65
  235. package/src/main/__tests__/scheduleJobTransitions.test.cjs +0 -277
  236. package/src/main/__tests__/scheduleJobTransitionsGrep.test.cjs +0 -59
  237. package/src/main/__tests__/scheduleJobTransitionsTelemetryTap.test.cjs +0 -72
  238. package/src/main/__tests__/scheduler-admin-routes.test.cjs +0 -199
  239. package/src/main/__tests__/scheduler-adopted-run-supervision.test.cjs +0 -143
  240. package/src/main/__tests__/scheduler-already-satisfied-on-main.test.cjs +0 -105
  241. package/src/main/__tests__/scheduler-archive-completed-prd.test.cjs +0 -102
  242. package/src/main/__tests__/scheduler-archived-twin-guard.test.cjs +0 -155
  243. package/src/main/__tests__/scheduler-autofix-outcome.test.cjs +0 -188
  244. package/src/main/__tests__/scheduler-autofix-select.test.cjs +0 -439
  245. package/src/main/__tests__/scheduler-autopromote.test.cjs +0 -51
  246. package/src/main/__tests__/scheduler-bash-timeout-env.test.cjs +0 -101
  247. package/src/main/__tests__/scheduler-blocked-by-foreign-wip.test.cjs +0 -107
  248. package/src/main/__tests__/scheduler-boot-orphans.test.cjs +0 -153
  249. package/src/main/__tests__/scheduler-broadcast-reconcile.test.cjs +0 -121
  250. package/src/main/__tests__/scheduler-clear-queue-history.test.cjs +0 -134
  251. package/src/main/__tests__/scheduler-commit-guard-noop.test.cjs +0 -244
  252. package/src/main/__tests__/scheduler-committed-in-window.test.cjs +0 -182
  253. package/src/main/__tests__/scheduler-cross-project-batch.test.cjs +0 -43
  254. package/src/main/__tests__/scheduler-default-eligible-heal.test.cjs +0 -168
  255. package/src/main/__tests__/scheduler-dispatch-loop.test.cjs +0 -58
  256. package/src/main/__tests__/scheduler-effective-concurrency.test.cjs +0 -81
  257. package/src/main/__tests__/scheduler-epic-digest.test.cjs +0 -227
  258. package/src/main/__tests__/scheduler-failed-autoreset.test.cjs +0 -121
  259. package/src/main/__tests__/scheduler-finalize-dispatch-guards.test.cjs +0 -229
  260. package/src/main/__tests__/scheduler-find-prd-dir.test.cjs +0 -75
  261. package/src/main/__tests__/scheduler-fix-plan-path.test.cjs +0 -119
  262. package/src/main/__tests__/scheduler-force-tick-outcome.test.cjs +0 -46
  263. package/src/main/__tests__/scheduler-foreign-wip-manifest.test.cjs +0 -78
  264. package/src/main/__tests__/scheduler-gate-shadow.test.cjs +0 -119
  265. package/src/main/__tests__/scheduler-guard-verdict-autoresolve.test.cjs +0 -390
  266. package/src/main/__tests__/scheduler-heal-refusal.test.cjs +0 -61
  267. package/src/main/__tests__/scheduler-heartbeat-payload.test.cjs +0 -80
  268. package/src/main/__tests__/scheduler-inplace-salvage.test.cjs +0 -323
  269. package/src/main/__tests__/scheduler-integration-failure-stamp.test.cjs +0 -41
  270. package/src/main/__tests__/scheduler-investigation-clean-skip.test.cjs +0 -63
  271. package/src/main/__tests__/scheduler-investigation-prompt.test.cjs +0 -123
  272. package/src/main/__tests__/scheduler-job-budget.test.cjs +0 -172
  273. package/src/main/__tests__/scheduler-job-overrun.test.cjs +0 -175
  274. package/src/main/__tests__/scheduler-launch-failure.test.cjs +0 -199
  275. package/src/main/__tests__/scheduler-leftover-fields.test.cjs +0 -52
  276. package/src/main/__tests__/scheduler-leftover-quarantine.test.cjs +0 -199
  277. package/src/main/__tests__/scheduler-looks-done.test.cjs +0 -537
  278. package/src/main/__tests__/scheduler-manual-pause.test.cjs +0 -118
  279. package/src/main/__tests__/scheduler-mechanical-recovery.test.cjs +0 -245
  280. package/src/main/__tests__/scheduler-meta-code-sha.test.cjs +0 -46
  281. package/src/main/__tests__/scheduler-mutate-reentrancy.test.cjs +0 -117
  282. package/src/main/__tests__/scheduler-needs-review-autoresolve.test.cjs +0 -197
  283. package/src/main/__tests__/scheduler-never-stop.test.cjs +0 -157
  284. package/src/main/__tests__/scheduler-no-dead-end-status.test.cjs +0 -152
  285. package/src/main/__tests__/scheduler-no-orphan-run-dir.test.cjs +0 -81
  286. package/src/main/__tests__/scheduler-notify-originating-tab-transcript.test.cjs +0 -87
  287. package/src/main/__tests__/scheduler-notify-originating-tab.test.cjs +0 -343
  288. package/src/main/__tests__/scheduler-periodic-reverify-guard.test.cjs +0 -217
  289. package/src/main/__tests__/scheduler-porcelain-rename.test.cjs +0 -164
  290. package/src/main/__tests__/scheduler-prd-missing-skip.test.cjs +0 -161
  291. package/src/main/__tests__/scheduler-prd-persona-spawn.test.cjs +0 -178
  292. package/src/main/__tests__/scheduler-quarantine-autoresolve.test.cjs +0 -165
  293. package/src/main/__tests__/scheduler-quiet-machine-lease.test.cjs +0 -257
  294. package/src/main/__tests__/scheduler-rate-limit-cooldown-freshness.test.cjs +0 -123
  295. package/src/main/__tests__/scheduler-rate-limit-pause.test.cjs +0 -152
  296. package/src/main/__tests__/scheduler-rate-limit-spin-guard.test.cjs +0 -156
  297. package/src/main/__tests__/scheduler-reap-dead-running-jobs.test.cjs +0 -754
  298. package/src/main/__tests__/scheduler-reaper-helpers-basics.test.cjs +0 -87
  299. package/src/main/__tests__/scheduler-reconcile-cwd-preserve.test.cjs +0 -100
  300. package/src/main/__tests__/scheduler-reconcile-history-backfill.test.cjs +0 -105
  301. package/src/main/__tests__/scheduler-reconcile-invalid-repair.test.cjs +0 -203
  302. package/src/main/__tests__/scheduler-reconcile-quarantine.test.cjs +0 -247
  303. package/src/main/__tests__/scheduler-reset-job-fields-guard.test.cjs +0 -77
  304. package/src/main/__tests__/scheduler-resume-recovery.test.cjs +0 -254
  305. package/src/main/__tests__/scheduler-shard-quarantine.test.cjs +0 -115
  306. package/src/main/__tests__/scheduler-shared-tree-guard.test.cjs +0 -380
  307. package/src/main/__tests__/scheduler-sigterm-commit.test.cjs +0 -43
  308. package/src/main/__tests__/scheduler-stall-per-project.test.cjs +0 -126
  309. package/src/main/__tests__/scheduler-starve-escalation.test.cjs +0 -154
  310. package/src/main/__tests__/scheduler-stranded-autofix-park.test.cjs +0 -252
  311. package/src/main/__tests__/scheduler-stranded-investigation.test.cjs +0 -185
  312. package/src/main/__tests__/scheduler-stuck-failed-escalation.test.cjs +0 -136
  313. package/src/main/__tests__/scheduler-supervisor-record.test.cjs +0 -81
  314. package/src/main/__tests__/scheduler-tick-cancel-token.test.cjs +0 -54
  315. package/src/main/__tests__/scheduler-tick-wedge.test.cjs +0 -172
  316. package/src/main/__tests__/scheduler-transient-failure.test.cjs +0 -141
  317. package/src/main/__tests__/scheduler-unreadable-queue-guard.test.cjs +0 -62
  318. package/src/main/__tests__/scheduler-utilization-hold.test.cjs +0 -89
  319. package/src/main/__tests__/scheduler-verify-prd-path.test.cjs +0 -109
  320. package/src/main/__tests__/scheduler-worktree-cap-defer.test.cjs +0 -234
  321. package/src/main/__tests__/scheduler-worktree-exec-cwd.test.cjs +0 -120
  322. package/src/main/__tests__/scheduler-writeprd-epic-rollback.test.cjs +0 -105
  323. package/src/main/__tests__/schedulerBatchRootBlocker.test.cjs +0 -117
  324. package/src/main/__tests__/schedulerStateSidecarRestore.test.cjs +0 -110
  325. package/src/main/__tests__/seedAgentPersonas.test.cjs +0 -184
  326. package/src/main/__tests__/seedSchedulerMcp.test.cjs +0 -211
  327. package/src/main/__tests__/seedStatus.test.cjs +0 -100
  328. package/src/main/__tests__/seedValidatorPersona.test.cjs +0 -116
  329. package/src/main/__tests__/stop-signal-anchor.test.cjs +0 -75
  330. package/src/main/__tests__/telemetryClient.test.cjs +0 -1055
  331. package/src/main/__tests__/telemetryContract.test.cjs +0 -930
  332. package/src/main/__tests__/telemetrySettings.test.cjs +0 -210
  333. package/src/main/__tests__/transcripts-batch-flush.test.cjs +0 -249
  334. package/src/main/__tests__/transcripts-doFlush-array.test.cjs +0 -124
  335. package/src/main/__tests__/transcripts-paged-reads.test.cjs +0 -241
  336. package/src/main/__tests__/transcripts-worktree-epic-path.test.cjs +0 -154
  337. package/src/main/__tests__/transcriptsUsageFor.test.cjs +0 -206
  338. package/src/main/__tests__/uniquePrdNumbers.test.cjs +0 -153
  339. package/src/main/__tests__/usageSingleFlight.test.cjs +0 -169
  340. package/src/main/__tests__/validationSentinels.test.cjs +0 -84
  341. package/src/main/__tests__/workTypeLibrary.test.cjs +0 -89
  342. package/src/main/bilkoHost.cjs +0 -314
  343. package/src/main/bilkoHostCore.cjs +0 -89
  344. package/src/main/lib/__tests__/active-sessions.test.cjs +0 -251
  345. package/src/main/lib/__tests__/activeIndexRebuild.test.cjs +0 -179
  346. package/src/main/lib/__tests__/agentPersonaSchema.test.cjs +0 -67
  347. package/src/main/lib/__tests__/auditLog.test.cjs +0 -38
  348. package/src/main/lib/__tests__/bootSelfHeal.test.cjs +0 -107
  349. package/src/main/lib/__tests__/branchSweep.test.cjs +0 -164
  350. package/src/main/lib/__tests__/buildIdentity.test.cjs +0 -121
  351. package/src/main/lib/__tests__/buildTarget.test.cjs +0 -52
  352. package/src/main/lib/__tests__/childWithLog.test.cjs +0 -321
  353. package/src/main/lib/__tests__/coldBootPromptSessionsWrite.test.cjs +0 -87
  354. package/src/main/lib/__tests__/crashTelemetry.test.cjs +0 -103
  355. package/src/main/lib/__tests__/credentials-futile-refresh.test.cjs +0 -115
  356. package/src/main/lib/__tests__/cwdClassify.test.cjs +0 -111
  357. package/src/main/lib/__tests__/definitionOfDoneSequence.test.cjs +0 -95
  358. package/src/main/lib/__tests__/delegationReadiness.test.cjs +0 -1175
  359. package/src/main/lib/__tests__/dispatchLoop.test.cjs +0 -63
  360. package/src/main/lib/__tests__/effectiveModelInfo.test.cjs +0 -244
  361. package/src/main/lib/__tests__/ephemeralCwd.test.cjs +0 -91
  362. package/src/main/lib/__tests__/epicDelegationStats.test.cjs +0 -137
  363. package/src/main/lib/__tests__/epicSpawnCwd.test.cjs +0 -283
  364. package/src/main/lib/__tests__/epicSpawnPlan.test.cjs +0 -196
  365. package/src/main/lib/__tests__/epicTranscriptPath.test.cjs +0 -163
  366. package/src/main/lib/__tests__/epicWorktreeBoot.test.cjs +0 -136
  367. package/src/main/lib/__tests__/epicWorktreeMerge.test.cjs +0 -130
  368. package/src/main/lib/__tests__/epicWorktreeMint.test.cjs +0 -117
  369. package/src/main/lib/__tests__/epicWorktreeProjectConfig.test.cjs +0 -137
  370. package/src/main/lib/__tests__/fixChainDepth.test.cjs +0 -40
  371. package/src/main/lib/__tests__/fixtures/204-mercury-steam-horse.log.txt +0 -13
  372. package/src/main/lib/__tests__/fixtures/scheduler-machine.json.corrupt-1789147548 +0 -34
  373. package/src/main/lib/__tests__/gateFixtures.json +0 -20
  374. package/src/main/lib/__tests__/gitCacheBound.test.cjs +0 -69
  375. package/src/main/lib/__tests__/gitWorktree.test.cjs +0 -1478
  376. package/src/main/lib/__tests__/gitWorktreeSalvage.test.cjs +0 -107
  377. package/src/main/lib/__tests__/gitWorktreeSalvageDelta.test.cjs +0 -153
  378. package/src/main/lib/__tests__/guardShims.test.cjs +0 -158
  379. package/src/main/lib/__tests__/importReferences.spec.cjs +0 -56
  380. package/src/main/lib/__tests__/instanceLock.test.cjs +0 -173
  381. package/src/main/lib/__tests__/jobSupervisorRecord.test.cjs +0 -78
  382. package/src/main/lib/__tests__/jobWorktree.test.cjs +0 -199
  383. package/src/main/lib/__tests__/jobWorktreeBootLive.test.cjs +0 -82
  384. package/src/main/lib/__tests__/landedSinceRun.test.cjs +0 -133
  385. package/src/main/lib/__tests__/launchFailure.test.cjs +0 -220
  386. package/src/main/lib/__tests__/loadGate.test.cjs +0 -303
  387. package/src/main/lib/__tests__/localAdminHttp.test.cjs +0 -214
  388. package/src/main/lib/__tests__/loopDelay.test.cjs +0 -68
  389. package/src/main/lib/__tests__/mcpToolCatalog.test.cjs +0 -107
  390. package/src/main/lib/__tests__/modelCatalog.test.cjs +0 -202
  391. package/src/main/lib/__tests__/opsOwnership.test.cjs +0 -113
  392. package/src/main/lib/__tests__/opsRootAbsoluteCwd.test.cjs +0 -328
  393. package/src/main/lib/__tests__/opsRootNestedWrite.test.cjs +0 -51
  394. package/src/main/lib/__tests__/opsRootResolve.test.cjs +0 -149
  395. package/src/main/lib/__tests__/prdDeclaredPaths.test.cjs +0 -82
  396. package/src/main/lib/__tests__/prdDisposition.test.cjs +0 -224
  397. package/src/main/lib/__tests__/procIdentity.test.cjs +0 -119
  398. package/src/main/lib/__tests__/procName.test.cjs +0 -92
  399. package/src/main/lib/__tests__/projectBriefCore.test.cjs +0 -216
  400. package/src/main/lib/__tests__/projectRootResolve.test.cjs +0 -148
  401. package/src/main/lib/__tests__/queueHealth.test.cjs +0 -58
  402. package/src/main/lib/__tests__/queueStoreAtomicWrite.test.cjs +0 -88
  403. package/src/main/lib/__tests__/queueStoreMachineStateRecovery.test.cjs +0 -190
  404. package/src/main/lib/__tests__/quietMachineLease.test.cjs +0 -39
  405. package/src/main/lib/__tests__/rateLimitWindow.test.cjs +0 -88
  406. package/src/main/lib/__tests__/reaperHelpers.test.cjs +0 -577
  407. package/src/main/lib/__tests__/schedulerBatchDepends.test.cjs +0 -312
  408. package/src/main/lib/__tests__/schedulerBatchFairness.test.cjs +0 -213
  409. package/src/main/lib/__tests__/schedulerBatchLaunchHold.test.cjs +0 -125
  410. package/src/main/lib/__tests__/schedulerBatchProjectCap.test.cjs +0 -127
  411. package/src/main/lib/__tests__/schedulerBatchQuietMachine.test.cjs +0 -109
  412. package/src/main/lib/__tests__/schedulerMcpServerHeadlessRefusal.test.cjs +0 -71
  413. package/src/main/lib/__tests__/schedulerMcpServerHelp.test.cjs +0 -216
  414. package/src/main/lib/__tests__/schedulerMcpServerProjectHome.test.cjs +0 -183
  415. package/src/main/lib/__tests__/schedulerPaths.test.cjs +0 -226
  416. package/src/main/lib/__tests__/schedulerPathsWorktree.test.cjs +0 -133
  417. package/src/main/lib/__tests__/schedulerRuntimeState.test.cjs +0 -56
  418. package/src/main/lib/__tests__/sessionSlots.test.cjs +0 -144
  419. package/src/main/lib/__tests__/telemetryBacklog.test.cjs +0 -626
  420. package/src/main/lib/__tests__/telemetryBoot.test.cjs +0 -134
  421. package/src/main/lib/__tests__/telemetryConsent.test.cjs +0 -136
  422. package/src/main/lib/__tests__/telemetryCounters.test.cjs +0 -57
  423. package/src/main/lib/__tests__/telemetryCountersMetadataColumn.test.cjs +0 -98
  424. package/src/main/lib/__tests__/terminalRunOutcome.test.cjs +0 -200
  425. package/src/main/lib/__tests__/toolUseClassify.test.cjs +0 -53
  426. package/src/main/lib/__tests__/updateCheck.test.cjs +0 -63
  427. package/src/main/lib/__tests__/upgradeDrain.test.cjs +0 -130
  428. package/src/main/lib/__tests__/usageCircuit.test.cjs +0 -354
  429. package/src/main/lib/__tests__/watchdog-helpers.test.cjs +0 -441
  430. package/src/main/lib/__tests__/watchdog-relaunch.test.cjs +0 -266
  431. package/src/main/lib/kgExchangePairing.cjs +0 -75
@@ -1,122 +0,0 @@
1
- /**
2
- * broadcastCoalescer.test.cjs — unit tests for src/main/lib/broadcastCoalescer.cjs,
3
- * the trailing-edge debounce used by scheduler.cjs's broadcast().
4
- *
5
- * Run: timeout 300 npx vitest run src/main/__tests__/broadcastCoalescer.test.cjs
6
- */
7
-
8
- 'use strict';
9
-
10
- import { test, expect, vi, beforeEach, afterEach } from 'vitest';
11
-
12
- const { createBroadcastCoalescer } = require('../lib/broadcastCoalescer.cjs');
13
-
14
- beforeEach(() => {
15
- vi.useFakeTimers();
16
- });
17
-
18
- afterEach(() => {
19
- vi.useRealTimers();
20
- });
21
-
22
- function makeCoalescer(delayMs = 200) {
23
- const sent = [];
24
- let counter = 0;
25
- const getPayload = vi.fn(() => `state-${counter}`);
26
- const send = vi.fn((payload) => sent.push(payload));
27
- const coalescer = createBroadcastCoalescer({ delayMs, send, getPayload });
28
- return {
29
- coalescer,
30
- sent,
31
- send,
32
- getPayload,
33
- bump: () => { counter += 1; },
34
- };
35
- }
36
-
37
- test('5 rapid schedule() calls within the window produce exactly 1 send', async () => {
38
- const { coalescer, sent, bump } = makeCoalescer(200);
39
-
40
- bump(); coalescer.schedule();
41
- bump(); coalescer.schedule();
42
- bump(); coalescer.schedule();
43
- bump(); coalescer.schedule();
44
- bump(); coalescer.schedule();
45
-
46
- await vi.advanceTimersByTimeAsync(200);
47
-
48
- expect(sent.length).toBe(1);
49
- });
50
-
51
- test('the trailing send reflects the LATEST state, not the state at the first schedule() call', async () => {
52
- const { coalescer, sent, bump } = makeCoalescer(200);
53
-
54
- bump(); // state-1
55
- coalescer.schedule();
56
- bump(); // state-2
57
- coalescer.schedule(); // no-op — timer already armed
58
- bump(); // state-3
59
- coalescer.schedule(); // no-op
60
-
61
- await vi.advanceTimersByTimeAsync(200);
62
-
63
- expect(sent).toEqual(['state-3']);
64
- });
65
-
66
- test('flush() cancels the pending timer and sends immediately with the current payload', async () => {
67
- const { coalescer, sent, bump } = makeCoalescer(200);
68
-
69
- bump();
70
- coalescer.schedule();
71
-
72
- // Flush before the 200ms window elapses.
73
- await vi.advanceTimersByTimeAsync(50);
74
- await coalescer.flush();
75
-
76
- expect(sent).toEqual(['state-1']);
77
-
78
- // The window's original timer must not fire a second, stale send.
79
- await vi.advanceTimersByTimeAsync(200);
80
- expect(sent).toEqual(['state-1']);
81
- });
82
-
83
- test('a schedule() call after a quiet period still arrives (not dropped)', async () => {
84
- const { coalescer, sent, bump } = makeCoalescer(200);
85
-
86
- bump();
87
- coalescer.schedule();
88
- await vi.advanceTimersByTimeAsync(200);
89
- expect(sent).toEqual(['state-1']);
90
-
91
- bump();
92
- coalescer.schedule();
93
- await vi.advanceTimersByTimeAsync(200);
94
- expect(sent).toEqual(['state-1', 'state-2']);
95
- });
96
-
97
- test('flush() with no pending timer still sends (no-arm case)', async () => {
98
- const { coalescer, sent, bump } = makeCoalescer(200);
99
- bump();
100
- await coalescer.flush();
101
- expect(sent).toEqual(['state-1']);
102
- });
103
-
104
- test('a getPayload() that throws does not wedge the coalescer — the next schedule() still sends', async () => {
105
- const sent = [];
106
- const send = vi.fn((payload) => sent.push(payload));
107
- const getPayload = vi.fn()
108
- .mockRejectedValueOnce(new Error('boom'))
109
- .mockResolvedValue('state-recovered');
110
- const coalescer = createBroadcastCoalescer({ delayMs: 200, send, getPayload });
111
-
112
- coalescer.schedule();
113
- await vi.advanceTimersByTimeAsync(200);
114
- expect(sent).toEqual([]);
115
-
116
- coalescer.schedule();
117
- await vi.advanceTimersByTimeAsync(200);
118
- expect(sent).toEqual(['state-recovered']);
119
- expect(getPayload).toHaveBeenCalledTimes(2);
120
- });
121
-
122
- console.log('broadcastCoalescer tests: PASS');
@@ -1,120 +0,0 @@
1
- /**
2
- * chat-cancel-terminal.test.cjs — regression for the "stuck thinking" hang.
3
- *
4
- * Bug (2026-07-05): cancelling an active chat run SIGTERM'd the child, but the
5
- * exit handler's `if (!gotResult && !killed)` guard skipped every broadcast
6
- * because `killed === true`. No terminal IPC event ever reached the renderer,
7
- * so the chat store stayed `running:true` forever — disabled textarea, endless
8
- * "running…" spinner, unresponsive chat UI.
9
- *
10
- * Fix: every run emits EXACTLY ONE terminal event (complete/needs-input/error),
11
- * and the exit path emits a fallback `chat:run:error` when a run settled
12
- * without one (cancel, crash, silent exit). This test drives the REAL spawn
13
- * path (via SM_CLAUDE_BIN pointing at a stub process) and asserts a terminal
14
- * event is broadcast on cancel.
15
- *
16
- * Run: timeout 300 npx vitest run src/main/__tests__/chat-cancel-terminal.test.cjs
17
- */
18
-
19
- 'use strict';
20
-
21
- delete process.env.SM_CHAT_CONCURRENCY;
22
-
23
- import { test, afterAll } from 'vitest';
24
- const assert = require('node:assert/strict');
25
- const fs = require('node:fs');
26
- const os = require('node:os');
27
- const path = require('node:path');
28
-
29
- // A stub "claude" that ignores stdin/args and stays alive until killed —
30
- // mimics a long-running headless run so cancel() has something to SIGTERM.
31
- // Shebang + chmod makes it directly spawnable as a single executable (spawn
32
- // does not split a path on spaces, so SM_CLAUDE_BIN must be one binary).
33
- const stubPath = require('../../../tests/helpers/claudeStub.cjs').writeClaudeStub({
34
- body: 'setInterval(() => {}, 1000);',
35
- });
36
- process.env.SM_CLAUDE_BIN = stubPath;
37
-
38
- const cr = require('../chatRunner.cjs');
39
-
40
- // Scratch project dir under os.tmpdir() — never the real repo root (opsErrorLog would pollute it).
41
- const scratchCwd = fs.mkdtempSync(path.join(os.tmpdir(), 'sm-chat-cwd-'));
42
-
43
- const tick = () => new Promise((r) => setImmediate(r));
44
-
45
- test('cancelling an ACTIVE run broadcasts a terminal event (unsticks the UI)', async () => {
46
- const events = [];
47
- // Fake BrowserWindow that captures every broadcast.
48
- cr.attachWindow({
49
- isDestroyed: () => false,
50
- webContents: {
51
- isDestroyed: () => false,
52
- send: (channel, payload) => events.push({ channel, payload }),
53
- },
54
- });
55
-
56
- cr.run({ tabId: 'T', sessionId: 'S', prompt: 'hello', cwd: scratchCwd, resume: false });
57
-
58
- // Let the spawn happen and started event fire.
59
- for (let i = 0; i < 6; i++) await tick();
60
- assert.ok(
61
- events.some((e) => e.channel === 'chat:run:started'),
62
- 'started event should fire once the run launches',
63
- );
64
-
65
- cr.cancel('T');
66
-
67
- // Wait for SIGTERM → child exit → fallback terminal broadcast.
68
- for (let i = 0; i < 40 && !events.some((e) => isTerminal(e.channel)); i++) {
69
- await new Promise((r) => setTimeout(r, 25));
70
- }
71
-
72
- const terminal = events.filter((e) => isTerminal(e.channel));
73
- assert.equal(terminal.length, 1, 'exactly one terminal event is emitted');
74
- assert.equal(terminal[0].channel, 'chat:run:error', 'cancel surfaces as an error turn');
75
- assert.match(terminal[0].payload.message, /cancel/i, 'message names the cancellation');
76
- });
77
-
78
- test('cancel() resolves only AFTER the terminal broadcast fires, not immediately on SIGTERM (PRD 718)', async () => {
79
- // Order-of-events assertion (not timing-based — a real SIGTERM can be fast
80
- // enough on a lightweight stub child that a fixed-delay race would flake).
81
- const order = [];
82
- cr.attachWindow({
83
- isDestroyed: () => false,
84
- webContents: {
85
- isDestroyed: () => false,
86
- send: (channel, payload) => {
87
- if (isTerminal(channel) && payload.tabId === 'T2') order.push('terminal');
88
- },
89
- },
90
- });
91
-
92
- cr.run({ tabId: 'T2', sessionId: 'S2', prompt: 'hello', cwd: scratchCwd, resume: false });
93
-
94
- for (let i = 0; i < 6; i++) await tick();
95
-
96
- await cr.cancel('T2').then(() => order.push('resolved'));
97
-
98
- assert.deepEqual(order, ['terminal', 'resolved'], 'cancel() resolves strictly after the terminal broadcast');
99
- });
100
-
101
- test('cancel() on a tab with no in-flight or waiting run resolves immediately (no-op)', async () => {
102
- cr.attachWindow({
103
- isDestroyed: () => false,
104
- webContents: { isDestroyed: () => false, send: () => {} },
105
- });
106
- await cr.cancel('no-such-tab'); // must resolve, not hang
107
- });
108
-
109
- function isTerminal(channel) {
110
- return (
111
- channel === 'chat:run:complete' ||
112
- channel === 'chat:run:needs-input' ||
113
- channel === 'chat:run:error'
114
- );
115
- }
116
-
117
- afterAll(() => {
118
- try { fs.unlinkSync(stubPath); } catch { /* already gone */ }
119
- delete process.env.SM_CLAUDE_BIN;
120
- });
@@ -1,63 +0,0 @@
1
- /**
2
- * chat-dead-channels.test.cjs — regression guard for the removed
3
- * chat:probe-context / chat:context-usage channels (PRD 897).
4
- *
5
- * These IPC channels were deliberately deleted (handler, broadcast, preload
6
- * and zod schema all removed) rather than left half-wired. This test walks
7
- * the source tree and fails if either literal string reappears anywhere
8
- * under src/main, src/preload, or src/renderer — such a reintroduction must
9
- * be wired end-to-end (handler + preload + zod schema + broadcast) or not
10
- * added at all.
11
- *
12
- * Run: timeout 120 npx vitest run src/main/__tests__/chat-dead-channels.test.cjs
13
- */
14
-
15
- 'use strict';
16
-
17
- const fs = require('fs');
18
- const path = require('path');
19
-
20
- import { test, expect } from 'vitest';
21
-
22
- const REPO_ROOT = path.resolve(__dirname, '..', '..', '..');
23
- const SCAN_DIRS = ['src/main', 'src/preload', 'src/renderer'];
24
- const FORBIDDEN_STRINGS = ['chat:probe-context', 'chat:context-usage'];
25
- const SELF_PATH = path.resolve(__filename);
26
-
27
- function walk(dir, files) {
28
- for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
29
- if (entry.name === 'node_modules') continue;
30
- if (entry.isSymbolicLink()) continue;
31
- const full = path.join(dir, entry.name);
32
- if (entry.isDirectory()) {
33
- walk(full, files);
34
- } else {
35
- files.push(full);
36
- }
37
- }
38
- }
39
-
40
- test('chat:probe-context / chat:context-usage channels stay removed', () => {
41
- const files = [];
42
- for (const dir of SCAN_DIRS) {
43
- walk(path.join(REPO_ROOT, dir), files);
44
- }
45
-
46
- const hits = [];
47
- for (const file of files) {
48
- if (path.resolve(file) === SELF_PATH) continue;
49
- const contents = fs.readFileSync(file, 'utf8');
50
- for (const needle of FORBIDDEN_STRINGS) {
51
- if (contents.includes(needle)) {
52
- hits.push(`${path.relative(REPO_ROOT, file)}: contains "${needle}"`);
53
- }
54
- }
55
- }
56
-
57
- expect(
58
- hits,
59
- 'chat:probe-context / chat:context-usage were deliberately removed as a dead/half-wired ' +
60
- 'channel (PRD 897). If reintroducing this feature, wire it end-to-end (handler, preload, ' +
61
- 'zod schema, broadcast) in the same change — do not add just one side.\n' + hits.join('\n'),
62
- ).toEqual([]);
63
- });
@@ -1,146 +0,0 @@
1
- /**
2
- * chat-exit-close-race.test.cjs — regression for the exit/close race that
3
- * silently dropped a pure-text-only final turn (PRD 716).
4
- *
5
- * Bug: the terminal-event fallback was wired to `child.on('exit', ...)`
6
- * instead of `child.on('close', ...)`. Node only guarantees all buffered
7
- * stdout 'data' events have been delivered by 'close', not 'exit' — 'exit'
8
- * can fire before the final stdout chunk (last assistant text + terminating
9
- * `result` line) reaches the parent's 'data' handler. When that happened,
10
- * the exit fallback ran first, set the one-shot terminalSent latch, and
11
- * broadcast a misleading chat:run:error. The real result line then parsed
12
- * moments later, but emitTerminal silently no-op'd and the genuine
13
- * chat:run:complete was dropped.
14
- *
15
- * Fix: use `child.on('close', ...)` for the fallback so it only fires once
16
- * Node guarantees stdout is fully drained — a real result event that arrives
17
- * (even "late", right up against process exit) always wins.
18
- *
19
- * This test mocks node:child_process.spawn so it can deterministically
20
- * reproduce the exact race ordering (exit fires, THEN the final stdout chunk
21
- * arrives, THEN close fires) rather than relying on real OS/pipe timing,
22
- * which is not reliably reproducible from a real child process.
23
- *
24
- * Run: timeout 300 npx vitest run src/main/__tests__/chat-exit-close-race.test.cjs
25
- */
26
-
27
- 'use strict';
28
-
29
- delete process.env.SM_CHAT_CONCURRENCY;
30
-
31
- import { test, afterAll } from 'vitest';
32
- const assert = require('node:assert/strict');
33
- const EventEmitter = require('node:events');
34
- const cp = require('node:child_process');
35
- const fs = require('node:fs');
36
- const os = require('node:os');
37
- const path = require('node:path');
38
-
39
- // Replace spawn BEFORE chatRunner.cjs is first required, so the module's
40
- // top-level `const { spawn } = require('node:child_process')` destructuring
41
- // captures this mock instead of the real implementation.
42
- const originalSpawn = cp.spawn;
43
- let nextChild = null;
44
- cp.spawn = () => {
45
- const child = new EventEmitter();
46
- child.stdout = new EventEmitter();
47
- child.stderr = new EventEmitter();
48
- child.pid = 424242;
49
- child.kill = () => {};
50
- nextChild = child;
51
- return child;
52
- };
53
-
54
- const cr = require('../chatRunner.cjs');
55
-
56
- // Scratch project dir under os.tmpdir() — never the real repo root (opsErrorLog would pollute it).
57
- const scratchCwd = fs.mkdtempSync(path.join(os.tmpdir(), 'sm-chat-cwd-'));
58
-
59
- const tick = () => new Promise((r) => setImmediate(r));
60
-
61
- test('a final-text-only turn whose stdout chunk lands after exit still surfaces chat:run:complete', async () => {
62
- nextChild = null;
63
- const events = [];
64
- cr.attachWindow({
65
- isDestroyed: () => false,
66
- webContents: {
67
- isDestroyed: () => false,
68
- send: (channel, payload) => events.push({ channel, payload }),
69
- },
70
- });
71
-
72
- cr.run({ tabId: 'T2', sessionId: 'S2', prompt: 'show me a sample post', cwd: scratchCwd, resume: false });
73
-
74
- for (let i = 0; i < 20 && !nextChild; i++) await tick();
75
- const child = nextChild;
76
- assert.ok(child, 'spawn should have been invoked');
77
-
78
- const text = 'Here is your sample post, all in one go.';
79
- const lines =
80
- JSON.stringify({ type: 'assistant', message: { content: [{ type: 'text', text }] } }) +
81
- '\n' +
82
- JSON.stringify({ type: 'result', subtype: 'success', result: text }) +
83
- '\n';
84
-
85
- // The exact race: 'exit' fires first (process already gone from the OS's
86
- // perspective), the final stdout chunk is delivered to the 'data' handler
87
- // only after that, and 'close' — which Node guarantees fires after all
88
- // stdout data has been delivered — fires last.
89
- child.emit('exit', 0, null);
90
- child.stdout.emit('data', Buffer.from(lines));
91
- child.emit('close', 0, null);
92
-
93
- await tick();
94
- await tick();
95
-
96
- const terminal = events.filter((e) => isTerminal(e.channel));
97
- assert.equal(terminal.length, 1, 'exactly one terminal event is emitted');
98
- assert.equal(
99
- terminal[0].channel,
100
- 'chat:run:complete',
101
- 'the real result event must win over the generic exit-fallback error',
102
- );
103
- assert.equal(terminal[0].payload.finalMessage, text);
104
- });
105
-
106
- test('a crashed process with no result event ever emitted still fires the fallback chat:run:error', async () => {
107
- nextChild = null;
108
- const events = [];
109
- cr.attachWindow({
110
- isDestroyed: () => false,
111
- webContents: {
112
- isDestroyed: () => false,
113
- send: (channel, payload) => events.push({ channel, payload }),
114
- },
115
- });
116
-
117
- cr.run({ tabId: 'T3', sessionId: 'S3', prompt: 'anything', cwd: scratchCwd, resume: false });
118
-
119
- for (let i = 0; i < 20 && !nextChild; i++) await tick();
120
- const child = nextChild;
121
- assert.ok(child, 'spawn should have been invoked');
122
-
123
- // Crashes with no stdout at all — 'close' is the only event that fires.
124
- child.emit('close', 1, null);
125
-
126
- await tick();
127
- await tick();
128
-
129
- const terminal = events.filter((e) => isTerminal(e.channel));
130
- assert.equal(terminal.length, 1, 'exactly one terminal event is emitted');
131
- assert.equal(terminal[0].channel, 'chat:run:error', 'a crash with no result surfaces as an error turn');
132
- assert.match(terminal[0].payload.message, /process exited without a result event/);
133
- });
134
-
135
- function isTerminal(channel) {
136
- return (
137
- channel === 'chat:run:complete' ||
138
- channel === 'chat:run:needs-input' ||
139
- channel === 'chat:run:error'
140
- );
141
- }
142
-
143
- afterAll(() => {
144
- cp.spawn = originalSpawn;
145
- delete process.env.SM_CHAT_CONCURRENCY;
146
- });
@@ -1,139 +0,0 @@
1
- /**
2
- * chat-mcp-consent-notice.test.cjs — regression for the headless MCP-consent
3
- * hang: a chat run with stdin closed can never answer an MCP server's own
4
- * interactive consent flow (e.g. `/design consent`). This drives the real
5
- * stdout parser with a synthetic stream-json `tool_result` denial line and
6
- * asserts a `chat:run:notice` event is broadcast — informational, not a
7
- * terminal event.
8
- *
9
- * Run: timeout 300 npx vitest run src/main/__tests__/chat-mcp-consent-notice.test.cjs
10
- */
11
-
12
- 'use strict';
13
-
14
- delete process.env.SM_CHAT_CONCURRENCY;
15
-
16
- import { test } from 'vitest';
17
- const assert = require('node:assert/strict');
18
- const fs = require('node:fs');
19
- const os = require('node:os');
20
- const path = require('node:path');
21
-
22
- // Scratch project dir under os.tmpdir() — never the real repo root (opsErrorLog would pollute it).
23
- const scratchCwd = fs.mkdtempSync(path.join(os.tmpdir(), 'sm-chat-cwd-'));
24
- const claudeStub = require('../../../tests/helpers/claudeStub.cjs');
25
-
26
- function writeStub(lines) {
27
- const body = lines.map((l) => `process.stdout.write(${JSON.stringify(JSON.stringify(l))} + "\\n");`).join('\n');
28
- return claudeStub.writeClaudeStub({ body: `${body}\nprocess.exit(0);` });
29
- }
30
-
31
- function isTerminal(channel) {
32
- return (
33
- channel === 'chat:run:complete' ||
34
- channel === 'chat:run:needs-input' ||
35
- channel === 'chat:run:error'
36
- );
37
- }
38
-
39
- test('MCP consent denial in a tool_result surfaces a chat:run:notice event', async () => {
40
- const stubPath = writeStub([
41
- {
42
- type: 'user',
43
- message: {
44
- content: [
45
- {
46
- type: 'tool_result',
47
- tool_use_id: 'toolu_1',
48
- is_error: true,
49
- content: [
50
- {
51
- type: 'text',
52
- text:
53
- "Claude Design: write_files The user hasn't granted this — run /design consent " +
54
- "to grant it (it can't be approved automatically in this permission mode).",
55
- },
56
- ],
57
- },
58
- ],
59
- },
60
- },
61
- { type: 'result', subtype: 'success', result: 'done anyway' },
62
- ]);
63
- process.env.SM_CLAUDE_BIN = stubPath;
64
- delete require.cache[require.resolve('../chatRunner.cjs')];
65
- const cr = require('../chatRunner.cjs');
66
-
67
- const events = [];
68
- cr.attachWindow({
69
- isDestroyed: () => false,
70
- webContents: {
71
- isDestroyed: () => false,
72
- send: (channel, payload) => events.push({ channel, payload }),
73
- },
74
- });
75
-
76
- cr.run({ tabId: 'T-notice', sessionId: 'S-notice', prompt: 'do a design thing', cwd: scratchCwd, resume: false });
77
-
78
- for (let i = 0; i < 60 && !events.some((e) => isTerminal(e.channel)); i++) {
79
- await new Promise((r) => setTimeout(r, 25));
80
- }
81
-
82
- const notice = events.find((e) => e.channel === 'chat:run:notice');
83
- assert.ok(notice, 'chat:run:notice should be broadcast');
84
- assert.match(notice.payload.message, /consent/i, 'message mentions consent');
85
- assert.match(notice.payload.message, /terminal/i, 'message mentions the raw terminal session');
86
-
87
- // Notice is informational, not terminal — the normal result still fires.
88
- const terminal = events.filter((e) => isTerminal(e.channel));
89
- assert.equal(terminal.length, 1, 'exactly one terminal event still fires');
90
- assert.equal(terminal[0].channel, 'chat:run:complete');
91
-
92
- try { fs.unlinkSync(stubPath); } catch { /* already gone */ }
93
- delete process.env.SM_CLAUDE_BIN;
94
- });
95
-
96
- test('a normal tool_result with no consent marker does not fire chat:run:notice', async () => {
97
- const stubPath = writeStub([
98
- {
99
- type: 'user',
100
- message: {
101
- content: [
102
- {
103
- type: 'tool_result',
104
- tool_use_id: 'toolu_2',
105
- is_error: false,
106
- content: [{ type: 'text', text: 'Wrote 3 files successfully.' }],
107
- },
108
- ],
109
- },
110
- },
111
- { type: 'result', subtype: 'success', result: 'all good' },
112
- ]);
113
- process.env.SM_CLAUDE_BIN = stubPath;
114
- delete require.cache[require.resolve('../chatRunner.cjs')];
115
- const cr = require('../chatRunner.cjs');
116
-
117
- const events = [];
118
- cr.attachWindow({
119
- isDestroyed: () => false,
120
- webContents: {
121
- isDestroyed: () => false,
122
- send: (channel, payload) => events.push({ channel, payload }),
123
- },
124
- });
125
-
126
- cr.run({ tabId: 'T-clean', sessionId: 'S-clean', prompt: 'do a normal thing', cwd: scratchCwd, resume: false });
127
-
128
- for (let i = 0; i < 60 && !events.some((e) => isTerminal(e.channel)); i++) {
129
- await new Promise((r) => setTimeout(r, 25));
130
- }
131
-
132
- assert.ok(
133
- !events.some((e) => e.channel === 'chat:run:notice'),
134
- 'no chat:run:notice should fire for a clean tool_result (no false positive)',
135
- );
136
-
137
- try { fs.unlinkSync(stubPath); } catch { /* already gone */ }
138
- delete process.env.SM_CLAUDE_BIN;
139
- });
@@ -1,100 +0,0 @@
1
- /**
2
- * chat-preamble-anchors.test.cjs — drift guard between chatRunner's injected
3
- * instruction blocks and the renderer's splitter that hides them.
4
- *
5
- * `src/renderer/lib/promptPreamble.ts` anchors each block on its FIRST and
6
- * LAST sentence only, because it cannot import this main-process .cjs. That
7
- * indirection is safe ONLY while those anchors still bracket the real text —
8
- * so this test composes the exact `fullPrompt` chatRunner builds and asserts
9
- * the splitter recovers the human's words exactly. Reword either block's
10
- * opening or closing sentence and this fails here, loudly, instead of the
11
- * boilerplate silently reappearing in every user bubble.
12
- *
13
- * Run: timeout 120 npx vitest run src/main/__tests__/chat-preamble-anchors.test.cjs
14
- */
15
-
16
- 'use strict';
17
-
18
- const path = require('path');
19
-
20
- import { test, expect } from 'vitest';
21
-
22
- const { STOP_SIGNAL_INSTRUCTION, CHAT_MODE_TRUTH_INSTRUCTION } = require('../chatRunner.cjs');
23
-
24
- const HUMAN = 'Review the Alpaca limits and report back what we can trade for free.';
25
-
26
- async function loadSplitter() {
27
- const mod = await import(
28
- /* @vite-ignore */ path.resolve(__dirname, '..', '..', 'renderer', 'lib', 'promptPreamble.ts')
29
- );
30
- return mod;
31
- }
32
-
33
- test('the real fullPrompt splits back into exactly the human’s words', async () => {
34
- const { splitInjectedPreamble } = await loadSplitter();
35
- // Mirrors chatRunner's own composition at its `-p` call site.
36
- const fullPrompt = STOP_SIGNAL_INSTRUCTION + CHAT_MODE_TRUTH_INSTRUCTION + HUMAN;
37
-
38
- const split = splitInjectedPreamble(fullPrompt);
39
- expect(split.body).toBe(HUMAN);
40
- expect(split.blockKeys).toEqual(['stop-signal', 'chat-mode-truth']);
41
- expect(split.preamble).toBeTruthy();
42
- // Nothing is invented or lost: preamble + body reconstitute the input.
43
- expect(split.preamble.length + split.body.length).toBeLessThanOrEqual(fullPrompt.length);
44
- expect(fullPrompt.startsWith(split.preamble)).toBe(true);
45
- expect(fullPrompt.endsWith(split.body)).toBe(true);
46
- });
47
-
48
- test('each block strips independently, so one being absent still works', async () => {
49
- const { splitInjectedPreamble } = await loadSplitter();
50
-
51
- const onlyStop = splitInjectedPreamble(STOP_SIGNAL_INSTRUCTION + HUMAN);
52
- expect(onlyStop.body).toBe(HUMAN);
53
- expect(onlyStop.blockKeys).toEqual(['stop-signal']);
54
-
55
- const onlyTruth = splitInjectedPreamble(CHAT_MODE_TRUTH_INSTRUCTION + HUMAN);
56
- expect(onlyTruth.body).toBe(HUMAN);
57
- expect(onlyTruth.blockKeys).toEqual(['chat-mode-truth']);
58
- });
59
-
60
- test('a prompt with no injected blocks is returned untouched', async () => {
61
- const { splitInjectedPreamble } = await loadSplitter();
62
- const split = splitInjectedPreamble(HUMAN);
63
- expect(split.preamble).toBe(null);
64
- expect(split.body).toBe(HUMAN);
65
- expect(split.blockKeys).toEqual([]);
66
- });
67
-
68
- test('EDGE: a boilerplate-only prompt is NOT stripped to an empty bubble', async () => {
69
- const { splitInjectedPreamble } = await loadSplitter();
70
- const onlyBoilerplate = STOP_SIGNAL_INSTRUCTION + CHAT_MODE_TRUTH_INSTRUCTION;
71
- const split = splitInjectedPreamble(onlyBoilerplate);
72
- expect(split.preamble).toBe(null);
73
- expect(split.body).toBe(onlyBoilerplate);
74
- });
75
-
76
- test('EDGE: a truncated block (opening present, closing missing) is left fully intact', async () => {
77
- const { splitInjectedPreamble } = await loadSplitter();
78
- // Simulates a reworded/cut-off block: better to over-show boilerplate than
79
- // to guess at a boundary and eat the human's words.
80
- const truncated = `${STOP_SIGNAL_INSTRUCTION.slice(0, 120)}\n\n${HUMAN}`;
81
- const split = splitInjectedPreamble(truncated);
82
- expect(split.preamble).toBe(null);
83
- expect(split.body).toBe(truncated);
84
- });
85
-
86
- test('the two blocks strip in either order', async () => {
87
- const { splitInjectedPreamble } = await loadSplitter();
88
- const reversed = CHAT_MODE_TRUTH_INSTRUCTION + STOP_SIGNAL_INSTRUCTION + HUMAN;
89
- const split = splitInjectedPreamble(reversed);
90
- expect(split.body).toBe(HUMAN);
91
- expect(split.blockKeys).toEqual(['chat-mode-truth', 'stop-signal']);
92
- });
93
-
94
- test('EDGE: boilerplate quoted inside the human’s own message is never stripped', async () => {
95
- const { splitInjectedPreamble } = await loadSplitter();
96
- const quoting = `Why does chat prepend this?\n\n${STOP_SIGNAL_INSTRUCTION}`;
97
- const split = splitInjectedPreamble(quoting);
98
- expect(split.preamble).toBe(null);
99
- expect(split.body).toBe(quoting);
100
- });