claude-code-session-manager 0.98.0 → 0.101.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 (441) 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-CXaH2GED.js +1 -0
  5. package/dist/assets/{History-Bfdoywlh.js → History-Cerii-HA.js} +2 -2
  6. package/dist/assets/{Hooks-CKDJjTJV.js → Hooks-aXMb5LDy.js} +3 -3
  7. package/dist/assets/{Library-7akfgy67.js → Library-WrJFKEOJ.js} +1 -1
  8. package/dist/assets/{MarkdownEditor-DDesiqet.js → MarkdownEditor-D1BMCXS7.js} +1 -1
  9. package/dist/assets/{McpServers-D4lZtXof.js → McpServers-Cf0davLd.js} +2 -2
  10. package/dist/assets/{Memory-CeI8G1w5.js → Memory-Dn6NHWPe.js} +6 -6
  11. package/dist/assets/Permissions-CTVuFPjg.js +3 -0
  12. package/dist/assets/{Plugins-P4eCXy_S.js → Plugins-C2TyF8Mn.js} +2 -2
  13. package/dist/assets/ProvenanceBadge-F_jwHSwE.js +1 -0
  14. package/dist/assets/{SaveBar-l9jnEMES.js → SaveBar-DA1KVJST.js} +1 -1
  15. package/dist/assets/Scheduler-Bm6Q_hlu.js +16 -0
  16. package/dist/assets/{ScopeSwitcher-CjJi8ST5.js → ScopeSwitcher-BygZlJca.js} +1 -1
  17. package/dist/assets/Settings-BYCiv5KV.js +3 -0
  18. package/dist/assets/{SkillReferenceGraph-CjbO01Sg.js → SkillReferenceGraph-CcLnl9np.js} +1 -1
  19. package/dist/assets/{Skills-BxZ9_fCR.js → Skills-ChffsdTD.js} +2 -2
  20. package/dist/assets/{SystemPrompt-D8vfFb8b.js → SystemPrompt-dNkfHE_M.js} +1 -1
  21. package/dist/assets/{TagLibrary-2vzUo7YL.js → TagLibrary-jVlEuB0m.js} +1 -1
  22. package/dist/assets/{TiptapBody-B8ZfbK90.js → TiptapBody-6_cdwcP0.js} +1 -1
  23. package/dist/assets/{Toggle-BbW4nGGf.js → Toggle-CVXqGpAP.js} +1 -1
  24. package/dist/assets/ViewTabs-DO1YVbpc.js +1 -0
  25. package/dist/assets/{index-CKH5Uxik.css → index-D89EUizB.css} +1 -1
  26. package/dist/assets/{index-Cd98Q1lD.js → index-DbCQPWWY.js} +747 -752
  27. package/dist/assets/{settingsSchema-IhUfucCF.js → settingsSchema-Wrvn-Yz1.js} +1 -1
  28. package/dist/index.html +2 -2
  29. package/package.json +25 -29
  30. package/plugins/CLAUDE.md +6 -6
  31. package/plugins/session-manager-dev/skills/builder/4-manual/SKILL.md +21 -19
  32. package/plugins/session-manager-dev/skills/builder/SKILL.md +3 -3
  33. package/plugins/session-manager-dev/skills/develop/SKILL.md +254 -510
  34. package/plugins/session-manager-dev/skills/develop/standards.md +17 -22
  35. package/scripts/README.md +3 -11
  36. package/scripts/audit-ops-hygiene.cjs +3 -3
  37. package/scripts/hooks/lib/guard-prd-writes-policy.cjs +1 -1
  38. package/scripts/hooks/lib/guard-self-schedule-policy.cjs +1 -1
  39. package/scripts/mint-epic.cjs +2 -3
  40. package/scripts/scheduler-mcp-server.cjs +64 -7
  41. package/src/main/agentLibrary.cjs +22 -2
  42. package/src/main/build-info.json +4 -4
  43. package/src/main/chatRunner.cjs +4 -3
  44. package/src/main/config.cjs +41 -38
  45. package/src/main/docEdit.cjs +4 -1
  46. package/src/main/files.cjs +2 -5
  47. package/src/main/health.cjs +1 -1
  48. package/src/main/index.cjs +40 -11
  49. package/src/main/ipcSchemas.cjs +35 -34
  50. package/src/main/lib/agentPersonaSchema.cjs +13 -2
  51. package/src/main/lib/atomicFs.cjs +116 -0
  52. package/src/main/lib/branchSweep.cjs +13 -12
  53. package/src/main/lib/buildTarget.cjs +3 -3
  54. package/src/main/lib/builtinMacros.cjs +39 -0
  55. package/src/main/lib/claudeCliCaps.cjs +129 -0
  56. package/src/main/lib/credentials.cjs +2 -4
  57. package/src/main/lib/crossProjectFeedback.cjs +3 -3
  58. package/src/main/lib/cwdClassify.cjs +15 -3
  59. package/src/main/lib/definitionOfDone.cjs +207 -125
  60. package/src/main/lib/dodDrainHook.cjs +11 -5
  61. package/src/main/lib/epicMint.cjs +2 -4
  62. package/src/main/lib/epicStatusMirror.cjs +8 -10
  63. package/src/main/lib/epicWorktreeProjectConfig.cjs +2 -4
  64. package/src/main/lib/gateAuthority.cjs +96 -0
  65. package/src/main/lib/gitExec.cjs +69 -0
  66. package/src/main/lib/gitWorktree.cjs +656 -92
  67. package/src/main/lib/instanceLock.cjs +5 -8
  68. package/src/main/lib/launchFailure.cjs +2 -3
  69. package/src/main/lib/macroLibrary.cjs +480 -0
  70. package/src/main/lib/mcpToolCatalog.cjs +49 -17
  71. package/src/main/lib/needsReviewLedger.cjs +1 -1
  72. package/src/main/lib/opsErrorLog.cjs +11 -1
  73. package/src/main/lib/opsOwnership.cjs +5 -9
  74. package/src/main/lib/pidAlive.cjs +32 -0
  75. package/src/main/lib/prdCreate.cjs +121 -12
  76. package/src/main/lib/prdDisposition.cjs +2 -2
  77. package/src/main/lib/prdGateFiles.cjs +284 -0
  78. package/src/main/lib/prdLocations.cjs +61 -0
  79. package/src/main/lib/prdMigration.cjs +2 -3
  80. package/src/main/lib/prdSizing.cjs +2 -1
  81. package/src/main/lib/projectHomeAdminRoutes.cjs +136 -21
  82. package/src/main/lib/promptSessionSchema.cjs +10 -2
  83. package/src/main/lib/rcaReport.cjs +4 -6
  84. package/src/main/lib/reaperHelpers.cjs +8 -1
  85. package/src/main/lib/reservationExpiry.cjs +6 -1
  86. package/src/main/lib/reviewNotice.cjs +263 -0
  87. package/src/main/lib/runClaudeP.cjs +4 -1
  88. package/src/main/lib/schedulerPaths.cjs +22 -2
  89. package/src/main/lib/sessionSlots.cjs +2 -4
  90. package/src/main/lib/shippedPersonaSeeds.cjs +226 -0
  91. package/src/main/lib/smfileHeaders.cjs +66 -0
  92. package/src/main/lib/timeoutShim.cjs +123 -0
  93. package/src/main/lib/timeoutShimScript.cjs +275 -0
  94. package/src/main/lib/upgradeDrain.cjs +4 -10
  95. package/src/main/lib/watchdogHelpers.cjs +12 -25
  96. package/src/main/lib/workTypeLibrary.cjs +7 -1
  97. package/src/main/projectPages.cjs +43 -13
  98. package/src/main/promptSessionEvents.cjs +37 -13
  99. package/src/main/scheduler.cjs +910 -198
  100. package/src/main/seedAgentPersonas.cjs +279 -24
  101. package/src/main/supervisor.cjs +4 -1
  102. package/src/main/templates/PRD_AUTHORING.md +126 -406
  103. package/src/preload/__tests__/preload-surface.test.cjs +68 -0
  104. package/src/preload/api.d.ts +53 -94
  105. package/src/preload/index.cjs +14 -9
  106. package/src/seed/agents/architect.md +2 -0
  107. package/src/seed/agents/demo-video-builder.md +84 -0
  108. package/src/seed/agents/dev-lead.md +18 -18
  109. package/src/seed/agents/project-home-builder.md +66 -35
  110. package/src/seed/agents/validator.md +10 -4
  111. package/dist/assets/DataModel-DCEgMTjH.js +0 -1
  112. package/dist/assets/HostBilko-CDVHE0hT.js +0 -1
  113. package/dist/assets/Permissions-BGleLja4.js +0 -3
  114. package/dist/assets/ProvenanceBadge-BFczzI1B.js +0 -1
  115. package/dist/assets/Scheduler-C_phpeUS.js +0 -16
  116. package/dist/assets/Settings-BIwZ3FGQ.js +0 -3
  117. package/src/main/__tests__/activeIndexMerge.test.cjs +0 -235
  118. package/src/main/__tests__/agentEffortResolve.test.cjs +0 -117
  119. package/src/main/__tests__/agentLibrary.test.cjs +0 -276
  120. package/src/main/__tests__/agentModelResolve.test.cjs +0 -311
  121. package/src/main/__tests__/agentOverlayWrite.test.cjs +0 -95
  122. package/src/main/__tests__/bilkoHost-deriveSlug.test.cjs +0 -26
  123. package/src/main/__tests__/bilkoHost-integration.test.cjs +0 -118
  124. package/src/main/__tests__/bilkoHostCore.test.cjs +0 -72
  125. package/src/main/__tests__/broadcastCoalescer.test.cjs +0 -122
  126. package/src/main/__tests__/chat-cancel-terminal.test.cjs +0 -120
  127. package/src/main/__tests__/chat-dead-channels.test.cjs +0 -63
  128. package/src/main/__tests__/chat-exit-close-race.test.cjs +0 -146
  129. package/src/main/__tests__/chat-mcp-consent-notice.test.cjs +0 -139
  130. package/src/main/__tests__/chat-preamble-anchors.test.cjs +0 -100
  131. package/src/main/__tests__/chat-queue.test.cjs +0 -97
  132. package/src/main/__tests__/chat-stop-signal.test.cjs +0 -89
  133. package/src/main/__tests__/chatRunner-epic-worktree-execcwd.test.cjs +0 -125
  134. package/src/main/__tests__/chatRunner-session-flag-retry.test.cjs +0 -252
  135. package/src/main/__tests__/classifyPromptTicket.test.cjs +0 -101
  136. package/src/main/__tests__/classifyTranscriptLine.test.cjs +0 -201
  137. package/src/main/__tests__/computeDepHistorySatisfaction.test.cjs +0 -66
  138. package/src/main/__tests__/config-readText-bounded.test.cjs +0 -84
  139. package/src/main/__tests__/configWriteBoundaryOwners.test.cjs +0 -59
  140. package/src/main/__tests__/crossProjectFeedback.test.cjs +0 -334
  141. package/src/main/__tests__/crossProjectFeedbackRoutes.test.cjs +0 -161
  142. package/src/main/__tests__/dep-orphan-archive-health.test.cjs +0 -77
  143. package/src/main/__tests__/develop-skill-failure-modes.test.cjs +0 -70
  144. package/src/main/__tests__/docEdit.test.cjs +0 -244
  145. package/src/main/__tests__/dod-batchkey.test.cjs +0 -183
  146. package/src/main/__tests__/dod-drain-hook.test.cjs +0 -302
  147. package/src/main/__tests__/dod-report.test.cjs +0 -304
  148. package/src/main/__tests__/dod-reverify.test.cjs +0 -285
  149. package/src/main/__tests__/epicContextDigest.test.cjs +0 -174
  150. package/src/main/__tests__/epicMint.test.cjs +0 -332
  151. package/src/main/__tests__/epicMintTelemetryTap.test.cjs +0 -64
  152. package/src/main/__tests__/epicStatusMirror.test.cjs +0 -110
  153. package/src/main/__tests__/epicValidationHook.test.cjs +0 -291
  154. package/src/main/__tests__/exchanges.test.cjs +0 -122
  155. package/src/main/__tests__/exchangesPromptId.test.cjs +0 -61
  156. package/src/main/__tests__/extractJson.test.cjs +0 -51
  157. package/src/main/__tests__/files-reject-credentials.test.cjs +0 -40
  158. package/src/main/__tests__/fixtures/1218-fo-01-move-scripts-lib-into-src-main-lib.log +0 -556
  159. package/src/main/__tests__/flatPrdTickSweep.test.cjs +0 -110
  160. package/src/main/__tests__/health-build-freshness.test.cjs +0 -39
  161. package/src/main/__tests__/health-claude-md-budget.test.cjs +0 -57
  162. package/src/main/__tests__/health-credentials.test.cjs +0 -81
  163. package/src/main/__tests__/health-delegation-chain.test.cjs +0 -124
  164. package/src/main/__tests__/health-per-project-stall.test.cjs +0 -84
  165. package/src/main/__tests__/health-prd-migration.test.cjs +0 -37
  166. package/src/main/__tests__/health-queue-dispatch.test.cjs +0 -135
  167. package/src/main/__tests__/health-starve-escalation.test.cjs +0 -94
  168. package/src/main/__tests__/health-tick-liveness.test.cjs +0 -179
  169. package/src/main/__tests__/health-usage-poller.test.cjs +0 -144
  170. package/src/main/__tests__/health-worktree-cap-blocked.test.cjs +0 -65
  171. package/src/main/__tests__/heapSnapshot.test.cjs +0 -121
  172. package/src/main/__tests__/historyAggregatorIntraday.test.cjs +0 -313
  173. package/src/main/__tests__/historyDashboard.test.cjs +0 -163
  174. package/src/main/__tests__/historyRollup.test.cjs +0 -333
  175. package/src/main/__tests__/intradayRefresh.test.cjs +0 -39
  176. package/src/main/__tests__/ipcSchemas-dependsOn.test.cjs +0 -39
  177. package/src/main/__tests__/kg-augment.test.cjs +0 -195
  178. package/src/main/__tests__/loadGateDetailTick.test.cjs +0 -31
  179. package/src/main/__tests__/machineProfile.test.cjs +0 -152
  180. package/src/main/__tests__/mcpStatus.test.cjs +0 -61
  181. package/src/main/__tests__/memoryAggregate.test.cjs +0 -109
  182. package/src/main/__tests__/memoryStale.test.cjs +0 -88
  183. package/src/main/__tests__/needsReviewLedger.test.cjs +0 -162
  184. package/src/main/__tests__/openExternalApp-spawn-error.test.cjs +0 -25
  185. package/src/main/__tests__/opsErrorLog.test.cjs +0 -109
  186. package/src/main/__tests__/opsErrorLogTelemetryTap.test.cjs +0 -173
  187. package/src/main/__tests__/personaMerge.test.cjs +0 -169
  188. package/src/main/__tests__/planValidator.test.cjs +0 -125
  189. package/src/main/__tests__/pollLoop-dispatch-on-failure.test.cjs +0 -176
  190. package/src/main/__tests__/prd-group-allocator.test.cjs +0 -119
  191. package/src/main/__tests__/prdAdminRouteParity.test.cjs +0 -70
  192. package/src/main/__tests__/prdAdminRoutes.test.cjs +0 -718
  193. package/src/main/__tests__/prdAgentType.test.cjs +0 -103
  194. package/src/main/__tests__/prdAuthoringSeed.test.cjs +0 -39
  195. package/src/main/__tests__/prdCreate.test.cjs +0 -979
  196. package/src/main/__tests__/prdCreateAdoption.test.cjs +0 -205
  197. package/src/main/__tests__/prdCreateDisposition.test.cjs +0 -201
  198. package/src/main/__tests__/prdCreatePlanId.test.cjs +0 -132
  199. package/src/main/__tests__/prdFrontmatterAgentType.test.cjs +0 -117
  200. package/src/main/__tests__/prdFrontmatterDependsOn.test.cjs +0 -136
  201. package/src/main/__tests__/prdFrontmatterDisposition.test.cjs +0 -125
  202. package/src/main/__tests__/prdFrontmatterQuietMachine.test.cjs +0 -108
  203. package/src/main/__tests__/prdLocations.test.cjs +0 -195
  204. package/src/main/__tests__/prdLocationsArchived.test.cjs +0 -201
  205. package/src/main/__tests__/prdMigration.test.cjs +0 -349
  206. package/src/main/__tests__/prdMigrationLegacyAdopt.test.cjs +0 -91
  207. package/src/main/__tests__/prdParserHighWater.test.cjs +0 -74
  208. package/src/main/__tests__/prdParserSourcePromptId.test.cjs +0 -65
  209. package/src/main/__tests__/prdSetDisposition.test.cjs +0 -222
  210. package/src/main/__tests__/prdSizing.test.cjs +0 -106
  211. package/src/main/__tests__/prdSourcePromptIdBackfill.test.cjs +0 -118
  212. package/src/main/__tests__/prdUpdateDependsOn.test.cjs +0 -160
  213. package/src/main/__tests__/proc-role-env.test.cjs +0 -125
  214. package/src/main/__tests__/procname-claude-spawn-sites.test.cjs +0 -304
  215. package/src/main/__tests__/procname-sm-processes.test.cjs +0 -127
  216. package/src/main/__tests__/projectHomeAdminRoutes.test.cjs +0 -177
  217. package/src/main/__tests__/projectPages.test.cjs +0 -137
  218. package/src/main/__tests__/promptSessionEvents.test.cjs +0 -234
  219. package/src/main/__tests__/promptSessionSchema.test.cjs +0 -101
  220. package/src/main/__tests__/promptSessionTranscript.test.cjs +0 -0
  221. package/src/main/__tests__/promptSessionsCreateEpicHandler.test.cjs +0 -159
  222. package/src/main/__tests__/pty-epic-worktree-spawn-cwd.test.cjs +0 -282
  223. package/src/main/__tests__/pty-session-open-telemetry.test.cjs +0 -96
  224. package/src/main/__tests__/pty-write-result.test.cjs +0 -46
  225. package/src/main/__tests__/queue-health-verdict.test.cjs +0 -170
  226. package/src/main/__tests__/queue-starvation-dispatch-driver.test.cjs +0 -286
  227. package/src/main/__tests__/queue-starvation-per-project.test.cjs +0 -147
  228. package/src/main/__tests__/queueHistory.test.cjs +0 -355
  229. package/src/main/__tests__/queueOps-interactive-ac-lint.test.cjs +0 -65
  230. package/src/main/__tests__/queueOpsArchiveDestination.test.cjs +0 -65
  231. package/src/main/__tests__/queueOpsAutoArchive.test.cjs +0 -211
  232. package/src/main/__tests__/rateLimitPollerStreak.test.cjs +0 -128
  233. package/src/main/__tests__/rcaReport.test.cjs +0 -266
  234. package/src/main/__tests__/reconcileFlatPrdSweep.test.cjs +0 -119
  235. package/src/main/__tests__/reconcileTiming.test.cjs +0 -135
  236. package/src/main/__tests__/runLogRetention.test.cjs +0 -489
  237. package/src/main/__tests__/runVerify-atomic-verdicts.test.cjs +0 -26
  238. package/src/main/__tests__/runVerify-blocked-by-foreign-wip.test.cjs +0 -58
  239. package/src/main/__tests__/runVerify-landed-commit-outranks.test.cjs +0 -191
  240. package/src/main/__tests__/runVerify-policy-denial.test.cjs +0 -89
  241. package/src/main/__tests__/runVerify-transcript-commit-evidence.test.cjs +0 -225
  242. package/src/main/__tests__/runVerify.test.cjs +0 -1784
  243. package/src/main/__tests__/scheduleJobSchema.test.cjs +0 -127
  244. package/src/main/__tests__/scheduleJobStatusDrift.test.cjs +0 -65
  245. package/src/main/__tests__/scheduleJobTransitions.test.cjs +0 -277
  246. package/src/main/__tests__/scheduleJobTransitionsGrep.test.cjs +0 -59
  247. package/src/main/__tests__/scheduleJobTransitionsTelemetryTap.test.cjs +0 -72
  248. package/src/main/__tests__/scheduler-admin-routes.test.cjs +0 -199
  249. package/src/main/__tests__/scheduler-adopted-run-supervision.test.cjs +0 -143
  250. package/src/main/__tests__/scheduler-already-satisfied-on-main.test.cjs +0 -105
  251. package/src/main/__tests__/scheduler-archive-completed-prd.test.cjs +0 -102
  252. package/src/main/__tests__/scheduler-archived-twin-guard.test.cjs +0 -155
  253. package/src/main/__tests__/scheduler-autofix-outcome.test.cjs +0 -188
  254. package/src/main/__tests__/scheduler-autofix-select.test.cjs +0 -439
  255. package/src/main/__tests__/scheduler-autopromote.test.cjs +0 -51
  256. package/src/main/__tests__/scheduler-bash-timeout-env.test.cjs +0 -101
  257. package/src/main/__tests__/scheduler-blocked-by-foreign-wip.test.cjs +0 -107
  258. package/src/main/__tests__/scheduler-boot-orphans.test.cjs +0 -153
  259. package/src/main/__tests__/scheduler-broadcast-reconcile.test.cjs +0 -121
  260. package/src/main/__tests__/scheduler-clear-queue-history.test.cjs +0 -134
  261. package/src/main/__tests__/scheduler-commit-guard-noop.test.cjs +0 -244
  262. package/src/main/__tests__/scheduler-committed-in-window.test.cjs +0 -182
  263. package/src/main/__tests__/scheduler-cross-project-batch.test.cjs +0 -43
  264. package/src/main/__tests__/scheduler-default-eligible-heal.test.cjs +0 -168
  265. package/src/main/__tests__/scheduler-dispatch-loop.test.cjs +0 -58
  266. package/src/main/__tests__/scheduler-effective-concurrency.test.cjs +0 -81
  267. package/src/main/__tests__/scheduler-epic-digest.test.cjs +0 -227
  268. package/src/main/__tests__/scheduler-failed-autoreset.test.cjs +0 -121
  269. package/src/main/__tests__/scheduler-finalize-dispatch-guards.test.cjs +0 -229
  270. package/src/main/__tests__/scheduler-find-prd-dir.test.cjs +0 -75
  271. package/src/main/__tests__/scheduler-fix-plan-path.test.cjs +0 -119
  272. package/src/main/__tests__/scheduler-force-tick-outcome.test.cjs +0 -46
  273. package/src/main/__tests__/scheduler-foreign-wip-manifest.test.cjs +0 -78
  274. package/src/main/__tests__/scheduler-gate-shadow.test.cjs +0 -119
  275. package/src/main/__tests__/scheduler-guard-verdict-autoresolve.test.cjs +0 -390
  276. package/src/main/__tests__/scheduler-heal-refusal.test.cjs +0 -61
  277. package/src/main/__tests__/scheduler-heartbeat-payload.test.cjs +0 -80
  278. package/src/main/__tests__/scheduler-inplace-salvage.test.cjs +0 -323
  279. package/src/main/__tests__/scheduler-integration-failure-stamp.test.cjs +0 -41
  280. package/src/main/__tests__/scheduler-investigation-clean-skip.test.cjs +0 -63
  281. package/src/main/__tests__/scheduler-investigation-prompt.test.cjs +0 -123
  282. package/src/main/__tests__/scheduler-job-budget.test.cjs +0 -172
  283. package/src/main/__tests__/scheduler-job-overrun.test.cjs +0 -175
  284. package/src/main/__tests__/scheduler-launch-failure.test.cjs +0 -199
  285. package/src/main/__tests__/scheduler-leftover-fields.test.cjs +0 -52
  286. package/src/main/__tests__/scheduler-leftover-quarantine.test.cjs +0 -199
  287. package/src/main/__tests__/scheduler-looks-done.test.cjs +0 -537
  288. package/src/main/__tests__/scheduler-manual-pause.test.cjs +0 -118
  289. package/src/main/__tests__/scheduler-mechanical-recovery.test.cjs +0 -245
  290. package/src/main/__tests__/scheduler-meta-code-sha.test.cjs +0 -46
  291. package/src/main/__tests__/scheduler-mutate-reentrancy.test.cjs +0 -117
  292. package/src/main/__tests__/scheduler-needs-review-autoresolve.test.cjs +0 -197
  293. package/src/main/__tests__/scheduler-never-stop.test.cjs +0 -157
  294. package/src/main/__tests__/scheduler-no-dead-end-status.test.cjs +0 -152
  295. package/src/main/__tests__/scheduler-no-orphan-run-dir.test.cjs +0 -81
  296. package/src/main/__tests__/scheduler-notify-originating-tab-transcript.test.cjs +0 -87
  297. package/src/main/__tests__/scheduler-notify-originating-tab.test.cjs +0 -343
  298. package/src/main/__tests__/scheduler-periodic-reverify-guard.test.cjs +0 -217
  299. package/src/main/__tests__/scheduler-porcelain-rename.test.cjs +0 -164
  300. package/src/main/__tests__/scheduler-prd-missing-skip.test.cjs +0 -161
  301. package/src/main/__tests__/scheduler-prd-persona-spawn.test.cjs +0 -178
  302. package/src/main/__tests__/scheduler-quarantine-autoresolve.test.cjs +0 -165
  303. package/src/main/__tests__/scheduler-quiet-machine-lease.test.cjs +0 -257
  304. package/src/main/__tests__/scheduler-rate-limit-cooldown-freshness.test.cjs +0 -123
  305. package/src/main/__tests__/scheduler-rate-limit-pause.test.cjs +0 -152
  306. package/src/main/__tests__/scheduler-rate-limit-spin-guard.test.cjs +0 -156
  307. package/src/main/__tests__/scheduler-reap-dead-running-jobs.test.cjs +0 -754
  308. package/src/main/__tests__/scheduler-reaper-helpers-basics.test.cjs +0 -87
  309. package/src/main/__tests__/scheduler-reconcile-cwd-preserve.test.cjs +0 -100
  310. package/src/main/__tests__/scheduler-reconcile-history-backfill.test.cjs +0 -105
  311. package/src/main/__tests__/scheduler-reconcile-invalid-repair.test.cjs +0 -203
  312. package/src/main/__tests__/scheduler-reconcile-quarantine.test.cjs +0 -247
  313. package/src/main/__tests__/scheduler-reset-job-fields-guard.test.cjs +0 -77
  314. package/src/main/__tests__/scheduler-resume-recovery.test.cjs +0 -254
  315. package/src/main/__tests__/scheduler-shard-quarantine.test.cjs +0 -115
  316. package/src/main/__tests__/scheduler-shared-tree-guard.test.cjs +0 -380
  317. package/src/main/__tests__/scheduler-sigterm-commit.test.cjs +0 -43
  318. package/src/main/__tests__/scheduler-stall-per-project.test.cjs +0 -126
  319. package/src/main/__tests__/scheduler-starve-escalation.test.cjs +0 -154
  320. package/src/main/__tests__/scheduler-stranded-autofix-park.test.cjs +0 -252
  321. package/src/main/__tests__/scheduler-stranded-investigation.test.cjs +0 -185
  322. package/src/main/__tests__/scheduler-stuck-failed-escalation.test.cjs +0 -136
  323. package/src/main/__tests__/scheduler-supervisor-record.test.cjs +0 -81
  324. package/src/main/__tests__/scheduler-tick-cancel-token.test.cjs +0 -54
  325. package/src/main/__tests__/scheduler-tick-wedge.test.cjs +0 -172
  326. package/src/main/__tests__/scheduler-transient-failure.test.cjs +0 -141
  327. package/src/main/__tests__/scheduler-unreadable-queue-guard.test.cjs +0 -62
  328. package/src/main/__tests__/scheduler-utilization-hold.test.cjs +0 -89
  329. package/src/main/__tests__/scheduler-verify-prd-path.test.cjs +0 -109
  330. package/src/main/__tests__/scheduler-worktree-cap-defer.test.cjs +0 -234
  331. package/src/main/__tests__/scheduler-worktree-exec-cwd.test.cjs +0 -120
  332. package/src/main/__tests__/scheduler-writeprd-epic-rollback.test.cjs +0 -105
  333. package/src/main/__tests__/schedulerBatchRootBlocker.test.cjs +0 -117
  334. package/src/main/__tests__/schedulerStateSidecarRestore.test.cjs +0 -110
  335. package/src/main/__tests__/seedAgentPersonas.test.cjs +0 -184
  336. package/src/main/__tests__/seedSchedulerMcp.test.cjs +0 -211
  337. package/src/main/__tests__/seedStatus.test.cjs +0 -100
  338. package/src/main/__tests__/seedValidatorPersona.test.cjs +0 -116
  339. package/src/main/__tests__/stop-signal-anchor.test.cjs +0 -75
  340. package/src/main/__tests__/telemetryClient.test.cjs +0 -1055
  341. package/src/main/__tests__/telemetryContract.test.cjs +0 -930
  342. package/src/main/__tests__/telemetrySettings.test.cjs +0 -210
  343. package/src/main/__tests__/transcripts-batch-flush.test.cjs +0 -249
  344. package/src/main/__tests__/transcripts-doFlush-array.test.cjs +0 -124
  345. package/src/main/__tests__/transcripts-paged-reads.test.cjs +0 -241
  346. package/src/main/__tests__/transcripts-worktree-epic-path.test.cjs +0 -154
  347. package/src/main/__tests__/transcriptsUsageFor.test.cjs +0 -206
  348. package/src/main/__tests__/uniquePrdNumbers.test.cjs +0 -153
  349. package/src/main/__tests__/usageSingleFlight.test.cjs +0 -169
  350. package/src/main/__tests__/validationSentinels.test.cjs +0 -84
  351. package/src/main/__tests__/workTypeLibrary.test.cjs +0 -89
  352. package/src/main/bilkoHost.cjs +0 -314
  353. package/src/main/bilkoHostCore.cjs +0 -89
  354. package/src/main/lib/__tests__/active-sessions.test.cjs +0 -251
  355. package/src/main/lib/__tests__/activeIndexRebuild.test.cjs +0 -179
  356. package/src/main/lib/__tests__/agentPersonaSchema.test.cjs +0 -67
  357. package/src/main/lib/__tests__/auditLog.test.cjs +0 -38
  358. package/src/main/lib/__tests__/bootSelfHeal.test.cjs +0 -107
  359. package/src/main/lib/__tests__/branchSweep.test.cjs +0 -164
  360. package/src/main/lib/__tests__/buildIdentity.test.cjs +0 -121
  361. package/src/main/lib/__tests__/buildTarget.test.cjs +0 -52
  362. package/src/main/lib/__tests__/childWithLog.test.cjs +0 -321
  363. package/src/main/lib/__tests__/coldBootPromptSessionsWrite.test.cjs +0 -87
  364. package/src/main/lib/__tests__/crashTelemetry.test.cjs +0 -103
  365. package/src/main/lib/__tests__/credentials-futile-refresh.test.cjs +0 -115
  366. package/src/main/lib/__tests__/cwdClassify.test.cjs +0 -111
  367. package/src/main/lib/__tests__/definitionOfDoneSequence.test.cjs +0 -95
  368. package/src/main/lib/__tests__/delegationReadiness.test.cjs +0 -1175
  369. package/src/main/lib/__tests__/dispatchLoop.test.cjs +0 -63
  370. package/src/main/lib/__tests__/effectiveModelInfo.test.cjs +0 -244
  371. package/src/main/lib/__tests__/ephemeralCwd.test.cjs +0 -91
  372. package/src/main/lib/__tests__/epicDelegationStats.test.cjs +0 -137
  373. package/src/main/lib/__tests__/epicSpawnCwd.test.cjs +0 -283
  374. package/src/main/lib/__tests__/epicSpawnPlan.test.cjs +0 -196
  375. package/src/main/lib/__tests__/epicTranscriptPath.test.cjs +0 -163
  376. package/src/main/lib/__tests__/epicWorktreeBoot.test.cjs +0 -136
  377. package/src/main/lib/__tests__/epicWorktreeMerge.test.cjs +0 -130
  378. package/src/main/lib/__tests__/epicWorktreeMint.test.cjs +0 -117
  379. package/src/main/lib/__tests__/epicWorktreeProjectConfig.test.cjs +0 -137
  380. package/src/main/lib/__tests__/fixChainDepth.test.cjs +0 -40
  381. package/src/main/lib/__tests__/fixtures/204-mercury-steam-horse.log.txt +0 -13
  382. package/src/main/lib/__tests__/fixtures/scheduler-machine.json.corrupt-1789147548 +0 -34
  383. package/src/main/lib/__tests__/gateFixtures.json +0 -20
  384. package/src/main/lib/__tests__/gitCacheBound.test.cjs +0 -69
  385. package/src/main/lib/__tests__/gitWorktree.test.cjs +0 -1478
  386. package/src/main/lib/__tests__/gitWorktreeSalvage.test.cjs +0 -107
  387. package/src/main/lib/__tests__/gitWorktreeSalvageDelta.test.cjs +0 -153
  388. package/src/main/lib/__tests__/guardShims.test.cjs +0 -158
  389. package/src/main/lib/__tests__/importReferences.spec.cjs +0 -56
  390. package/src/main/lib/__tests__/instanceLock.test.cjs +0 -173
  391. package/src/main/lib/__tests__/jobSupervisorRecord.test.cjs +0 -78
  392. package/src/main/lib/__tests__/jobWorktree.test.cjs +0 -199
  393. package/src/main/lib/__tests__/jobWorktreeBootLive.test.cjs +0 -82
  394. package/src/main/lib/__tests__/landedSinceRun.test.cjs +0 -133
  395. package/src/main/lib/__tests__/launchFailure.test.cjs +0 -220
  396. package/src/main/lib/__tests__/loadGate.test.cjs +0 -303
  397. package/src/main/lib/__tests__/localAdminHttp.test.cjs +0 -214
  398. package/src/main/lib/__tests__/loopDelay.test.cjs +0 -68
  399. package/src/main/lib/__tests__/mcpToolCatalog.test.cjs +0 -107
  400. package/src/main/lib/__tests__/modelCatalog.test.cjs +0 -202
  401. package/src/main/lib/__tests__/opsOwnership.test.cjs +0 -113
  402. package/src/main/lib/__tests__/opsRootAbsoluteCwd.test.cjs +0 -328
  403. package/src/main/lib/__tests__/opsRootNestedWrite.test.cjs +0 -51
  404. package/src/main/lib/__tests__/opsRootResolve.test.cjs +0 -149
  405. package/src/main/lib/__tests__/prdDeclaredPaths.test.cjs +0 -82
  406. package/src/main/lib/__tests__/prdDisposition.test.cjs +0 -224
  407. package/src/main/lib/__tests__/procIdentity.test.cjs +0 -119
  408. package/src/main/lib/__tests__/procName.test.cjs +0 -92
  409. package/src/main/lib/__tests__/projectBriefCore.test.cjs +0 -216
  410. package/src/main/lib/__tests__/projectRootResolve.test.cjs +0 -148
  411. package/src/main/lib/__tests__/queueHealth.test.cjs +0 -58
  412. package/src/main/lib/__tests__/queueStoreAtomicWrite.test.cjs +0 -88
  413. package/src/main/lib/__tests__/queueStoreMachineStateRecovery.test.cjs +0 -190
  414. package/src/main/lib/__tests__/quietMachineLease.test.cjs +0 -39
  415. package/src/main/lib/__tests__/rateLimitWindow.test.cjs +0 -88
  416. package/src/main/lib/__tests__/reaperHelpers.test.cjs +0 -577
  417. package/src/main/lib/__tests__/schedulerBatchDepends.test.cjs +0 -312
  418. package/src/main/lib/__tests__/schedulerBatchFairness.test.cjs +0 -213
  419. package/src/main/lib/__tests__/schedulerBatchLaunchHold.test.cjs +0 -125
  420. package/src/main/lib/__tests__/schedulerBatchProjectCap.test.cjs +0 -127
  421. package/src/main/lib/__tests__/schedulerBatchQuietMachine.test.cjs +0 -109
  422. package/src/main/lib/__tests__/schedulerMcpServerHeadlessRefusal.test.cjs +0 -71
  423. package/src/main/lib/__tests__/schedulerMcpServerHelp.test.cjs +0 -216
  424. package/src/main/lib/__tests__/schedulerMcpServerProjectHome.test.cjs +0 -183
  425. package/src/main/lib/__tests__/schedulerPaths.test.cjs +0 -226
  426. package/src/main/lib/__tests__/schedulerPathsWorktree.test.cjs +0 -133
  427. package/src/main/lib/__tests__/schedulerRuntimeState.test.cjs +0 -56
  428. package/src/main/lib/__tests__/sessionSlots.test.cjs +0 -144
  429. package/src/main/lib/__tests__/telemetryBacklog.test.cjs +0 -626
  430. package/src/main/lib/__tests__/telemetryBoot.test.cjs +0 -134
  431. package/src/main/lib/__tests__/telemetryConsent.test.cjs +0 -136
  432. package/src/main/lib/__tests__/telemetryCounters.test.cjs +0 -57
  433. package/src/main/lib/__tests__/telemetryCountersMetadataColumn.test.cjs +0 -98
  434. package/src/main/lib/__tests__/terminalRunOutcome.test.cjs +0 -200
  435. package/src/main/lib/__tests__/toolUseClassify.test.cjs +0 -53
  436. package/src/main/lib/__tests__/updateCheck.test.cjs +0 -63
  437. package/src/main/lib/__tests__/upgradeDrain.test.cjs +0 -130
  438. package/src/main/lib/__tests__/usageCircuit.test.cjs +0 -354
  439. package/src/main/lib/__tests__/watchdog-helpers.test.cjs +0 -441
  440. package/src/main/lib/__tests__/watchdog-relaunch.test.cjs +0 -266
  441. package/src/main/lib/kgExchangePairing.cjs +0 -75
@@ -1,134 +0,0 @@
1
- /**
2
- * telemetryBoot.test.cjs — unit tests for the boot-time telemetry send rules:
3
- * the machine-profile heartbeat (version-change OR 30-day liveness) and the
4
- * boot/version-change flush triggers.
5
- *
6
- * Run: timeout 120 npx vitest run src/main/lib/__tests__/telemetryBoot.test.cjs
7
- */
8
- 'use strict';
9
-
10
- import { test, expect } from 'vitest';
11
- const { bootSequence } = require('../telemetryBoot.cjs');
12
- const telemetrySettings = require('../telemetrySettings.cjs');
13
-
14
- const DAY_MS = 24 * 60 * 60 * 1000;
15
-
16
- function fakeDeps({ settings } = {}) {
17
- let saved = { ...telemetrySettings.DEFAULTS, ...settings };
18
- const flushCalls = [];
19
- return {
20
- state: () => saved,
21
- flushCalls,
22
- deps: {
23
- telemetrySettings: {
24
- load: async () => saved,
25
- save: async (next) => { saved = { ...saved, ...next }; return saved; },
26
- isMachineReportDue: telemetrySettings.isMachineReportDue,
27
- },
28
- telemetryClient: {
29
- flush: async (reason) => { flushCalls.push(reason); return { sent: [], failed: [], reason }; },
30
- reportInstall: async (profile) => ({ accepted: true, profile }),
31
- },
32
- buildMachineProfile: async () => ({ appVersion: '1.0.0', platform: 'linux', arch: 'x64', machineDigest: 'abc123' }),
33
- telemetryCounters: { trackAppLaunch: () => {} },
34
- },
35
- };
36
- }
37
-
38
- test('a second boot at the same version within 30 days sends nothing', async () => {
39
- const now = Date.now();
40
- const priorReportAt = new Date(now - DAY_MS).toISOString();
41
- const { deps, state } = fakeDeps({
42
- settings: { lastMachineReportAt: priorReportAt, lastMachineReportVersion: '1.0.0' },
43
- });
44
- const seen = [];
45
- deps.telemetryClient.reportInstall = async (profile) => { seen.push(profile); return { accepted: true }; };
46
-
47
- await bootSequence({ now, appVersion: '1.0.0', deps });
48
-
49
- expect(seen).toHaveLength(0);
50
- expect(state().lastMachineReportAt).toBe(priorReportAt);
51
- });
52
-
53
- test('a version bump sends the machine profile exactly once', async () => {
54
- const now = Date.now();
55
- const { deps, state } = fakeDeps({
56
- settings: { lastMachineReportAt: new Date(now - DAY_MS).toISOString(), lastMachineReportVersion: '1.0.0' },
57
- });
58
- const seen = [];
59
- deps.telemetryClient.reportInstall = async (profile) => { seen.push(profile); return { accepted: true }; };
60
-
61
- await bootSequence({ now, appVersion: '2.0.0', deps });
62
-
63
- expect(seen.length).toBe(1);
64
- expect(state().lastMachineReportVersion).toBe('2.0.0');
65
- });
66
-
67
- test('31 days on an unchanged version sends the machine profile exactly once (liveness heartbeat)', async () => {
68
- const now = Date.now();
69
- const { deps, state } = fakeDeps({
70
- settings: { lastMachineReportAt: new Date(now - 31 * DAY_MS).toISOString(), lastMachineReportVersion: '1.0.0' },
71
- });
72
- const seen = [];
73
- deps.telemetryClient.reportInstall = async (profile) => { seen.push(profile); return { accepted: true }; };
74
-
75
- await bootSequence({ now, appVersion: '1.0.0', deps });
76
-
77
- expect(seen.length).toBe(1);
78
- expect(state().lastMachineReportAt).toBe(new Date(now).toISOString());
79
- });
80
-
81
- test('the install report appVersion equals the appVersion stamped on a sibling error record', async () => {
82
- const now = Date.now();
83
- const { deps } = fakeDeps({ settings: { lastMachineReportVersion: '', lastMachineReportAt: null } });
84
- const seen = [];
85
- deps.telemetryClient.reportInstall = async (profile) => { seen.push(profile); return { accepted: true }; };
86
- // Sibling error record stamped by the SAME process's telemetryClient with the same appVersion.
87
- const siblingErrorStampAppVersion = '1.0.0';
88
-
89
- await bootSequence({ now, appVersion: '1.0.0', deps });
90
-
91
- const [install] = seen;
92
- expect(install.appVersion).toBe(siblingErrorStampAppVersion);
93
- });
94
-
95
- test('on app ready, flush("boot") always runs', async () => {
96
- const now = Date.now();
97
- const { deps, flushCalls } = fakeDeps({ settings: { lastMachineReportVersion: '1.0.0', lastMachineReportAt: new Date(now).toISOString() } });
98
- await bootSequence({ now, appVersion: '1.0.0', deps });
99
- expect(flushCalls).toContain('boot');
100
- });
101
-
102
- test('on a detected app-version change, flush("version-change") also runs', async () => {
103
- const now = Date.now();
104
- const { deps, flushCalls } = fakeDeps({ settings: { lastMachineReportVersion: '1.0.0', lastMachineReportAt: new Date(now).toISOString() } });
105
- await bootSequence({ now, appVersion: '2.0.0', deps });
106
- expect(flushCalls).toEqual(['boot', 'version-change']);
107
- });
108
-
109
- test('with no version change, flush("version-change") does NOT run', async () => {
110
- const now = Date.now();
111
- const { deps, flushCalls } = fakeDeps({ settings: { lastMachineReportVersion: '1.0.0', lastMachineReportAt: new Date(now).toISOString() } });
112
- await bootSequence({ now, appVersion: '1.0.0', deps });
113
- expect(flushCalls).toEqual(['boot']);
114
- });
115
-
116
- test('a rejected reportInstall leaves lastMachineReportAt unset so the next boot retries', async () => {
117
- const now = Date.now();
118
- const { deps, state } = fakeDeps({ settings: { lastMachineReportVersion: '', lastMachineReportAt: null } });
119
- deps.telemetryClient.reportInstall = async () => ({ accepted: false, reason: 'error' });
120
-
121
- await bootSequence({ now, appVersion: '1.0.0', deps });
122
-
123
- expect(state().lastMachineReportAt).toBe(null);
124
- expect(state().lastMachineReportVersion).toBe('');
125
- });
126
-
127
- test('fires app.launch via telemetryCounters with installChannel + appVersion', async () => {
128
- const now = Date.now();
129
- const { deps } = fakeDeps({ settings: { lastMachineReportVersion: '1.0.0', lastMachineReportAt: new Date(now).toISOString() } });
130
- const calls = [];
131
- deps.telemetryCounters = { trackAppLaunch: (props) => calls.push(props) };
132
- await bootSequence({ now, appVersion: '1.0.0', installChannel: 'npx', deps });
133
- expect(calls).toEqual([{ installChannel: 'npx', appVersion: '1.0.0' }]);
134
- });
@@ -1,136 +0,0 @@
1
- /**
2
- * telemetryConsent.test.cjs — unit tests for the telemetry:set-config
3
- * handler's write path (PRD 1142).
4
- *
5
- * Run: timeout 300 npx vitest run src/main/lib/__tests__/telemetryConsent.test.cjs
6
- */
7
- 'use strict';
8
-
9
- import { test, expect, beforeEach, afterEach, vi } from 'vitest';
10
- const fsp = require('node:fs/promises');
11
- const os = require('node:os');
12
- const path = require('node:path');
13
-
14
- const tmpDirs = [];
15
- let originalHome;
16
- let originalSmTelemetrySpool;
17
-
18
- beforeEach(() => {
19
- originalHome = process.env.HOME;
20
- originalSmTelemetrySpool = process.env.SM_TELEMETRY_SPOOL;
21
- });
22
-
23
- afterEach(async () => {
24
- if (originalHome !== undefined) process.env.HOME = originalHome; else delete process.env.HOME;
25
- if (originalSmTelemetrySpool === undefined) delete process.env.SM_TELEMETRY_SPOOL; else process.env.SM_TELEMETRY_SPOOL = originalSmTelemetrySpool;
26
- vi.unstubAllGlobals();
27
- while (tmpDirs.length) {
28
- const d = tmpDirs.pop();
29
- await fsp.rm(d, { recursive: true, force: true });
30
- }
31
- });
32
-
33
- async function mkHome() {
34
- const dir = await fsp.mkdtemp(path.join(os.tmpdir(), 'sm-telemetry-consent-home-'));
35
- tmpDirs.push(dir);
36
- return dir;
37
- }
38
-
39
- function freshModules(home) {
40
- process.env.HOME = home;
41
- // Explicit opt-in: overrides the test-environment no-op guard so this
42
- // suite's real telemetryClient calls actually persist into an isolated dir.
43
- process.env.SM_TELEMETRY_SPOOL = path.join(home, '.claude', 'session-manager');
44
- for (const p of ['../telemetryConsent.cjs', '../telemetryClient.cjs', '../telemetrySettings.cjs', '../machineProfile.cjs', '../../config.cjs']) {
45
- const resolved = require.resolve(p);
46
- delete require.cache[resolved];
47
- }
48
- const telemetryConsent = require('../telemetryConsent.cjs');
49
- const telemetrySettings = require('../telemetrySettings.cjs');
50
- const telemetryClient = require('../telemetryClient.cjs');
51
- telemetryClient._setMachineProfileBuilder(async () => ({
52
- appVersion: '0.83.0', platform: 'linux', arch: 'x64', machineDigest: 'abc123', installChannel: 'dev',
53
- }));
54
- return { telemetryConsent, telemetrySettings, telemetryClient };
55
- }
56
-
57
- test('turning telemetry off persists enabled:false and telemetrySettings.isEnabled() reflects it', async () => {
58
- const home = await mkHome();
59
- const { telemetryConsent, telemetrySettings } = freshModules(home);
60
- const cfg = await telemetrySettings.load();
61
- expect(cfg.enabled).toBe(true);
62
-
63
- const saved = await telemetryConsent.applyConsentUpdate({ ...cfg, enabled: false });
64
- expect(saved.enabled).toBe(false);
65
- expect(telemetrySettings.isEnabled(saved)).toBe(false);
66
- });
67
-
68
- test('after turning off, telemetryClient drops subsequent records at ingress', async () => {
69
- const home = await mkHome();
70
- const { telemetryConsent, telemetrySettings, telemetryClient } = freshModules(home);
71
- const cfg = await telemetrySettings.load();
72
- await telemetryConsent.applyConsentUpdate({ ...cfg, enabled: false });
73
-
74
- const res = await telemetryClient.track('some.event', { a: 1 });
75
- expect(res).toEqual({ accepted: false, reason: 'disabled' });
76
- telemetryClient.shutdown();
77
- });
78
-
79
- test('turning off clears the pending queue file so a re-enable never resends it', async () => {
80
- const home = await mkHome();
81
- const { telemetryConsent, telemetrySettings, telemetryClient } = freshModules(home);
82
-
83
- // Accumulate a record while still enabled.
84
- const accepted = await telemetryClient.reportError({ name: 'BoomError', msg: 'kaboom', stack: 'at x' });
85
- expect(accepted.accepted).toBe(true);
86
- const beforeQueue = await fsp.readFile(telemetryClient.queuePath(), 'utf8');
87
- expect(beforeQueue.trim().length).toBeGreaterThan(0);
88
-
89
- const cfg = await telemetrySettings.load();
90
- await telemetryConsent.applyConsentUpdate({ ...cfg, enabled: false });
91
-
92
- const afterQueue = await fsp.readFile(telemetryClient.queuePath(), 'utf8');
93
- expect(afterQueue.trim()).toBe('');
94
- telemetryClient.shutdown();
95
- });
96
-
97
- test('turning off does NOT clear telemetry-sent.json or the watermarks — re-enabling later does not resend delivered history', async () => {
98
- const home = await mkHome();
99
- const { telemetryConsent, telemetrySettings, telemetryClient } = freshModules(home);
100
-
101
- telemetryClient._setFetchImpl(async () => ({ ok: true, status: 200 }));
102
- await telemetryClient.reportError({ name: 'SentError', msg: 'already delivered', stack: 'at y' });
103
- await telemetryClient.flush('manual');
104
- const sentPathContent = await fsp.readFile(telemetryClient.sentPath(), 'utf8');
105
- expect(JSON.parse(sentPathContent).ids.length).toBeGreaterThan(0);
106
-
107
- // Also seed a watermarks file to prove opt-out leaves it untouched.
108
- const watermarksDir = path.join(home, '.claude', 'session-manager');
109
- await fsp.mkdir(watermarksDir, { recursive: true });
110
- const watermarksPath = path.join(watermarksDir, 'telemetry-watermarks.json');
111
- const watermarksBefore = { 'abc123/errors-2026-01-01.jsonl': { bytesEnqueued: 100, bytesConfirmed: 100 } };
112
- await fsp.writeFile(watermarksPath, JSON.stringify(watermarksBefore));
113
-
114
- const cfg = await telemetrySettings.load();
115
- await telemetryConsent.applyConsentUpdate({ ...cfg, enabled: false });
116
-
117
- const sentAfter = await fsp.readFile(telemetryClient.sentPath(), 'utf8');
118
- expect(sentAfter).toBe(sentPathContent);
119
- const watermarksAfter = await fsp.readFile(watermarksPath, 'utf8');
120
- expect(JSON.parse(watermarksAfter)).toEqual(watermarksBefore);
121
- telemetryClient.shutdown();
122
- });
123
-
124
- test('turning telemetry on (already enabled) is a no-op on the queue', async () => {
125
- const home = await mkHome();
126
- const { telemetryConsent, telemetrySettings, telemetryClient } = freshModules(home);
127
- await telemetryClient.reportError({ name: 'KeptError', msg: 'still queued', stack: 'at z' });
128
- const before = await fsp.readFile(telemetryClient.queuePath(), 'utf8');
129
-
130
- const cfg = await telemetrySettings.load();
131
- await telemetryConsent.applyConsentUpdate({ ...cfg, enabled: true });
132
-
133
- const after = await fsp.readFile(telemetryClient.queuePath(), 'utf8');
134
- expect(after).toBe(before);
135
- telemetryClient.shutdown();
136
- });
@@ -1,57 +0,0 @@
1
- /**
2
- * telemetryCounters.test.cjs — unit tests for the single shared emission
3
- * point behind 'app.launch' / 'session.open' / 'epic.create' /
4
- * 'scheduler.job.finish'. Each real call site (pty.cjs, epicMint.cjs,
5
- * scheduleJobTransitions.cjs, telemetryBoot.cjs) delegates here, so this is
6
- * where the event name + prop-key shape is verified once.
7
- *
8
- * Run: timeout 120 npx vitest run src/main/lib/__tests__/telemetryCounters.test.cjs
9
- */
10
- 'use strict';
11
-
12
- import { test, expect } from 'vitest';
13
- const counters = require('../telemetryCounters.cjs');
14
-
15
- function fakeClient() {
16
- const calls = [];
17
- return { calls, deps: { telemetryClient: { track: (name, props) => { calls.push({ name, props }); } } } };
18
- }
19
-
20
- test('trackAppLaunch fires app.launch once with installChannel + appVersion', () => {
21
- const { calls, deps } = fakeClient();
22
- counters.trackAppLaunch({ installChannel: 'npx', appVersion: '0.83.0' }, deps);
23
- expect(calls).toHaveLength(1);
24
- expect(calls[0].name).toBe('app.launch');
25
- expect(Object.keys(calls[0].props).sort()).toEqual(['appVersion', 'installChannel'].sort());
26
- });
27
-
28
- test('trackSessionOpen fires session.open once', () => {
29
- const { calls, deps } = fakeClient();
30
- counters.trackSessionOpen(deps);
31
- expect(calls).toHaveLength(1);
32
- expect(calls[0].name).toBe('session.open');
33
- });
34
-
35
- test('trackEpicCreate fires epic.create once', () => {
36
- const { calls, deps } = fakeClient();
37
- counters.trackEpicCreate(deps);
38
- expect(calls).toHaveLength(1);
39
- expect(calls[0].name).toBe('epic.create');
40
- });
41
-
42
- test('trackSchedulerJobFinish fires scheduler.job.finish once with a status-only prop', () => {
43
- const { calls, deps } = fakeClient();
44
- counters.trackSchedulerJobFinish({ status: 'completed' }, deps);
45
- expect(calls).toHaveLength(1);
46
- expect(calls[0].name).toBe('scheduler.job.finish');
47
- expect(Object.keys(calls[0].props)).toEqual(['status']);
48
- expect(calls[0].props.status).toBe('completed');
49
- });
50
-
51
- test('a throwing telemetryClient.track never escapes any counter function', () => {
52
- const deps = { telemetryClient: { track: () => { throw new Error('boom'); } } };
53
- expect(() => counters.trackAppLaunch({ installChannel: 'dev', appVersion: '1.0.0' }, deps)).not.toThrow();
54
- expect(() => counters.trackSessionOpen(deps)).not.toThrow();
55
- expect(() => counters.trackEpicCreate(deps)).not.toThrow();
56
- expect(() => counters.trackSchedulerJobFinish({ status: 'failed' }, deps)).not.toThrow();
57
- });
@@ -1,98 +0,0 @@
1
- /**
2
- * telemetryCountersMetadataColumn.test.cjs — the event channel has no version
3
- * column (telemetryClient.test.cjs's "funnel_events has no version column"
4
- * test covers the generic case); this asserts the same survives specifically
5
- * for each of the four counter events this PRD adds, once serialized the way
6
- * the /api/telemetry/event route persists it into `metadata`.
7
- *
8
- * Run: timeout 120 npx vitest run src/main/lib/__tests__/telemetryCountersMetadataColumn.test.cjs
9
- */
10
- 'use strict';
11
-
12
- import { test, expect, afterEach } from 'vitest';
13
- const fsp = require('node:fs/promises');
14
- const os = require('node:os');
15
- const path = require('node:path');
16
-
17
- const counters = require('../telemetryCounters.cjs');
18
-
19
- const tmpDirs = [];
20
- let originalHome;
21
- let originalSmTelemetrySpool;
22
-
23
- afterEach(async () => {
24
- if (originalHome !== undefined) process.env.HOME = originalHome;
25
- if (originalSmTelemetrySpool === undefined) delete process.env.SM_TELEMETRY_SPOOL; else process.env.SM_TELEMETRY_SPOOL = originalSmTelemetrySpool;
26
- while (tmpDirs.length) {
27
- const d = tmpDirs.pop();
28
- await fsp.rm(d, { recursive: true, force: true });
29
- }
30
- });
31
-
32
- async function mkHome() {
33
- originalHome = process.env.HOME;
34
- originalSmTelemetrySpool = process.env.SM_TELEMETRY_SPOOL;
35
- const dir = await fsp.mkdtemp(path.join(os.tmpdir(), 'sm-telemetry-counters-home-'));
36
- tmpDirs.push(dir);
37
- process.env.HOME = dir;
38
- // Explicit opt-in: overrides the test-environment no-op guard so this
39
- // suite's real telemetryClient calls actually persist into an isolated dir.
40
- process.env.SM_TELEMETRY_SPOOL = path.join(dir, '.claude', 'session-manager');
41
- return dir;
42
- }
43
-
44
- function freshClient() {
45
- for (const p of ['../telemetryClient.cjs', '../../config.cjs', '../telemetrySettings.cjs', '../machineProfile.cjs']) {
46
- const resolved = require.resolve(p);
47
- delete require.cache[resolved];
48
- }
49
- const client = require('../telemetryClient.cjs');
50
- client._setMachineProfileBuilder(async () => ({
51
- appVersion: '0.83.0', platform: 'linux', arch: 'x64', machineDigest: 'deadbeefcafe',
52
- }));
53
- return client;
54
- }
55
-
56
- async function readQueueLines(client) {
57
- const fs = require('node:fs/promises');
58
- try {
59
- const raw = await fs.readFile(client.queuePath(), 'utf8');
60
- return raw.split('\n').filter(Boolean).map((l) => JSON.parse(l));
61
- } catch (e) {
62
- if (e.code === 'ENOENT') return [];
63
- throw e;
64
- }
65
- }
66
-
67
- function asPersistedMetadata(rec) {
68
- // Mirrors exactly what the deployed /api/telemetry/event INSERT persists
69
- // into the `metadata` column (see telemetryClient.test.cjs).
70
- return JSON.parse(JSON.stringify(rec.wire.props));
71
- }
72
-
73
- test('every counter event still yields appVersion + machineDigest once persisted into metadata', async () => {
74
- await mkHome();
75
- const client = freshClient();
76
- const deps = { telemetryClient: client };
77
-
78
- counters.trackAppLaunch({ installChannel: 'npx', appVersion: '0.83.0' }, deps);
79
- counters.trackSessionOpen(deps);
80
- counters.trackEpicCreate(deps);
81
- counters.trackSchedulerJobFinish({ status: 'completed' }, deps);
82
-
83
- // track() ingress does real fs I/O and the counter functions fire it without
84
- // awaiting the promise, so poll (bounded) for all 4 records rather than a fixed
85
- // sleep — a fixed 50ms lost the race under CPU contention.
86
- let recs = [];
87
- for (let i = 0; i < 200 && recs.length < 4; i++) {
88
- await new Promise((r) => setTimeout(r, 25));
89
- recs = await readQueueLines(client);
90
- }
91
- expect(recs.length).toBe(4);
92
- for (const rec of recs) {
93
- const meta = asPersistedMetadata(rec);
94
- expect(meta.appVersion).toBe('0.83.0');
95
- expect(meta.machineDigest).toBe('deadbeefcafe');
96
- }
97
- client.shutdown();
98
- });
@@ -1,200 +0,0 @@
1
- /**
2
- * terminalRunOutcome.test.cjs — unit tests for the history-independent
3
- * terminal-run-outcome probe (PRD 812-689-fix-fix-distribute-adminserver-routes).
4
- *
5
- * Run: timeout 120 npx vitest run src/main/lib/__tests__/terminalRunOutcome.test.cjs
6
- */
7
-
8
- 'use strict';
9
-
10
- import { test, expect } from 'vitest';
11
- const fs = require('node:fs');
12
- const os = require('node:os');
13
- const path = require('node:path');
14
- const {
15
- latestTerminalOutcomeForSlug,
16
- MAX_DIRS_SCANNED,
17
- } = require('../terminalRunOutcome.cjs');
18
-
19
- function mkTmpRunsDir() {
20
- return fs.mkdtempSync(path.join(os.tmpdir(), 'terminal-run-outcome-'));
21
- }
22
-
23
- function writeRun(runsDir, runId, slug, meta, verdicts) {
24
- const dir = path.join(runsDir, runId);
25
- fs.mkdirSync(dir, { recursive: true });
26
- if (meta !== undefined) {
27
- fs.writeFileSync(path.join(dir, `${slug}.meta.json`), typeof meta === 'string' ? meta : JSON.stringify(meta));
28
- }
29
- if (verdicts !== undefined) {
30
- fs.writeFileSync(path.join(dir, `${slug}.verdicts.json`), typeof verdicts === 'string' ? verdicts : JSON.stringify(verdicts));
31
- }
32
- }
33
-
34
- test('returns completed for newest run with exitCode 0 + clean verdict', () => {
35
- const runsDir = mkTmpRunsDir();
36
- writeRun(runsDir, '2026-07-31T07-38-29-081Z', 'my-slug', { exitCode: 0, finishedAt: 1785483574748 }, { verdict: 'clean' });
37
- const result = latestTerminalOutcomeForSlug('my-slug', { runsDir });
38
- expect(result).toEqual({ status: 'completed', runId: '2026-07-31T07-38-29-081Z', finishedAt: new Date(1785483574748).toISOString() });
39
- });
40
-
41
- test('returns completed for pass_no_commit_already_shipped verdict', () => {
42
- const runsDir = mkTmpRunsDir();
43
- writeRun(runsDir, '2026-07-31T07-38-29-081Z', 'my-slug', { exitCode: 0, finishedAt: 1785483574749 }, { verdict: 'pass_no_commit_already_shipped' });
44
- const result = latestTerminalOutcomeForSlug('my-slug', { runsDir });
45
- expect(result).toEqual({ status: 'completed', runId: '2026-07-31T07-38-29-081Z', finishedAt: new Date(1785483574749).toISOString() });
46
- });
47
-
48
- test('returns failed for non-zero exitCode', () => {
49
- const runsDir = mkTmpRunsDir();
50
- writeRun(runsDir, '2026-07-31T07-38-29-081Z', 'my-slug', { exitCode: 1, finishedAt: 1785483574750 });
51
- const result = latestTerminalOutcomeForSlug('my-slug', { runsDir });
52
- expect(result).toEqual({ status: 'failed', runId: '2026-07-31T07-38-29-081Z', finishedAt: new Date(1785483574750).toISOString() });
53
- });
54
-
55
- test('returns failed for exitCode 0 with a non-completed-equivalent verdict', () => {
56
- const runsDir = mkTmpRunsDir();
57
- writeRun(runsDir, '2026-07-31T07-38-29-081Z', 'my-slug', { exitCode: 0, finishedAt: 1785483574751 }, { verdict: 'transcript_errors' });
58
- const result = latestTerminalOutcomeForSlug('my-slug', { runsDir });
59
- expect(result).toEqual({ status: 'failed', runId: '2026-07-31T07-38-29-081Z', finishedAt: new Date(1785483574751).toISOString() });
60
- });
61
-
62
- test('returns null when there is no run dir for the slug', () => {
63
- const runsDir = mkTmpRunsDir();
64
- writeRun(runsDir, '2026-07-31T07-38-29-081Z', 'other-slug', { exitCode: 0 }, { verdict: 'clean' });
65
- expect(latestTerminalOutcomeForSlug('my-slug', { runsDir })).toBeNull();
66
- });
67
-
68
- test('returns null on malformed meta.json', () => {
69
- const runsDir = mkTmpRunsDir();
70
- writeRun(runsDir, '2026-07-31T07-38-29-081Z', 'my-slug', '{not json', { verdict: 'clean' });
71
- expect(latestTerminalOutcomeForSlug('my-slug', { runsDir })).toBeNull();
72
- });
73
-
74
- test('returns null on malformed verdicts.json', () => {
75
- const runsDir = mkTmpRunsDir();
76
- writeRun(runsDir, '2026-07-31T07-38-29-081Z', 'my-slug', { exitCode: 0 }, '{not json');
77
- expect(latestTerminalOutcomeForSlug('my-slug', { runsDir })).toBeNull();
78
- });
79
-
80
- test('returns null when runsDir does not exist', () => {
81
- expect(latestTerminalOutcomeForSlug('my-slug', { runsDir: '/nonexistent/path/xyz' })).toBeNull();
82
- });
83
-
84
- test('picks the newest of several run dirs for the same slug', () => {
85
- const runsDir = mkTmpRunsDir();
86
- writeRun(runsDir, '2026-07-01T00-00-00-000Z', 'my-slug', { exitCode: 1, finishedAt: 10 });
87
- writeRun(runsDir, '2026-07-31T07-38-29-081Z', 'my-slug', { exitCode: 0, finishedAt: 1785483574752 }, { verdict: 'clean' });
88
- writeRun(runsDir, '2026-06-01T00-00-00-000Z', 'my-slug', { exitCode: 1, finishedAt: 5 });
89
- const result = latestTerminalOutcomeForSlug('my-slug', { runsDir });
90
- expect(result).toEqual({ status: 'completed', runId: '2026-07-31T07-38-29-081Z', finishedAt: new Date(1785483574752).toISOString() });
91
- });
92
-
93
- test('stats at most the newest few run dirs (bound enforced)', () => {
94
- const runsDir = mkTmpRunsDir();
95
- // Create many more matching run dirs than MAX_DIRS_SCANNED, all with a
96
- // non-terminal-equivalent verdict so the loop never early-returns before
97
- // exhausting the candidate slice — proves the bound is actually applied.
98
- const total = MAX_DIRS_SCANNED + 10;
99
- for (let i = 0; i < total; i++) {
100
- const ts = `2026-07-${String(i + 1).padStart(2, '0')}T00-00-00-000Z`;
101
- writeRun(runsDir, ts, 'my-slug', { exitCode: 0, finishedAt: i }, { verdict: 'transcript_errors' });
102
- }
103
-
104
- let readFileCalls = 0;
105
- const fsImpl = {
106
- readdirSync: (...a) => fs.readdirSync(...a),
107
- existsSync: (...a) => fs.existsSync(...a),
108
- readFileSync: (...a) => {
109
- readFileCalls++;
110
- return fs.readFileSync(...a);
111
- },
112
- };
113
-
114
- latestTerminalOutcomeForSlug('my-slug', { runsDir, fsImpl });
115
- // Each scanned dir reads meta.json, then (since exitCode===0) verdicts.json
116
- // too, so at most MAX_DIRS_SCANNED * 2 readFileSync calls.
117
- expect(readFileCalls).toBeLessThanOrEqual(MAX_DIRS_SCANNED * 2);
118
- });
119
-
120
- // ─── sidecar-resilience (2026-08-13) ────────────────────────────────────────
121
- //
122
- // Every branch used to `return` on the FIRST candidate dir, so an unreadable
123
- // or half-written sidecar in the newest run erased an older, complete terminal
124
- // record — the anti-resurrection guard switching itself off on exactly the
125
- // input it exists to survive, which lets reconcile re-derive a fresh queue row
126
- // and re-run an already-completed PRD. It also made MAX_DIRS_SCANNED a no-op.
127
-
128
- function memFs(files) {
129
- return {
130
- readdirSync: (d) => Object.keys(files[d] ?? {}),
131
- existsSync: (p) => {
132
- const dir = path.dirname(p);
133
- const base = path.basename(p);
134
- return Boolean(files[path.dirname(dir)]?.[path.basename(dir)]?.[base]);
135
- },
136
- readFileSync: (p) => {
137
- const dir = path.dirname(p);
138
- const base = path.basename(p);
139
- const content = files[path.dirname(dir)]?.[path.basename(dir)]?.[base];
140
- if (content === undefined) throw new Error(`ENOENT: ${p}`);
141
- return content;
142
- },
143
- };
144
- }
145
-
146
- const RUNS = '/runs';
147
- const SLUG = '900-thing';
148
-
149
- function tree(dirs) {
150
- return { [RUNS]: dirs };
151
- }
152
-
153
- test('an unreadable meta sidecar in the newest dir falls through to an older complete run', () => {
154
- const fsImpl = memFs(tree({
155
- '2026-08-14T02-00-00-000Z': { [`${SLUG}.meta.json`]: '{ not json' },
156
- '2026-08-14T01-00-00-000Z': {
157
- [`${SLUG}.meta.json`]: JSON.stringify({ exitCode: 0, finishedAt: 1786669013583 }),
158
- [`${SLUG}.verdicts.json`]: JSON.stringify({ verdict: 'clean' }),
159
- },
160
- }));
161
- const out = latestTerminalOutcomeForSlug(SLUG, { runsDir: RUNS, fsImpl });
162
- expect(out).toBeTruthy();
163
- expect(out.status).toBe('completed');
164
- expect(out.runId).toBe('2026-08-14T01-00-00-000Z');
165
- });
166
-
167
- test('a newest run missing its verdicts sidecar falls through instead of erasing the older record', () => {
168
- const fsImpl = memFs(tree({
169
- '2026-08-14T02-00-00-000Z': { [`${SLUG}.meta.json`]: JSON.stringify({ exitCode: 0, finishedAt: 1 }) },
170
- '2026-08-14T01-00-00-000Z': {
171
- [`${SLUG}.meta.json`]: JSON.stringify({ exitCode: 0, finishedAt: 1786669013583 }),
172
- [`${SLUG}.verdicts.json`]: JSON.stringify({ verdict: 'pass_no_commit_prior_run_verified' }),
173
- },
174
- }));
175
- const out = latestTerminalOutcomeForSlug(SLUG, { runsDir: RUNS, fsImpl });
176
- expect(out).toBeTruthy();
177
- expect(out.status).toBe('completed');
178
- expect(out.runId).toBe('2026-08-14T01-00-00-000Z');
179
- });
180
-
181
- test('a non-zero exit in the newest dir is still authoritative — fall-through only covers UNREADABLE sidecars', () => {
182
- const fsImpl = memFs(tree({
183
- '2026-08-14T02-00-00-000Z': { [`${SLUG}.meta.json`]: JSON.stringify({ exitCode: 1, finishedAt: 2 }) },
184
- '2026-08-14T01-00-00-000Z': {
185
- [`${SLUG}.meta.json`]: JSON.stringify({ exitCode: 0, finishedAt: 1 }),
186
- [`${SLUG}.verdicts.json`]: JSON.stringify({ verdict: 'clean' }),
187
- },
188
- }));
189
- const out = latestTerminalOutcomeForSlug(SLUG, { runsDir: RUNS, fsImpl });
190
- expect(out.status).toBe('failed');
191
- expect(out.runId).toBe('2026-08-14T02-00-00-000Z');
192
- });
193
-
194
- test('every candidate being unreadable still yields null (no invented outcome)', () => {
195
- const fsImpl = memFs(tree({
196
- '2026-08-14T02-00-00-000Z': { [`${SLUG}.meta.json`]: '{ nope' },
197
- '2026-08-14T01-00-00-000Z': { [`${SLUG}.meta.json`]: '{ also nope' },
198
- }));
199
- expect(latestTerminalOutcomeForSlug(SLUG, { runsDir: RUNS, fsImpl })).toBeNull();
200
- });
@@ -1,53 +0,0 @@
1
- /**
2
- * toolUseClassify.test.cjs — unit tests for src/main/lib/toolUseClassify.cjs.
3
- *
4
- * Run: timeout 120 npx vitest run src/main/lib/__tests__/toolUseClassify.test.cjs
5
- */
6
-
7
- 'use strict';
8
-
9
- import { test, expect } from 'vitest';
10
-
11
- const { classifyToolUse, MAX_DIFF_STR } = require('../toolUseClassify.cjs');
12
-
13
- test('Edit tool_use produces a diff payload with filePath/oldText/newText', () => {
14
- const block = {
15
- name: 'Edit',
16
- input: { file_path: '/tmp/foo.js', old_string: 'a', new_string: 'b' },
17
- };
18
- const result = classifyToolUse(block);
19
- expect(result.kind).toBe('tool');
20
- expect(result.diff).toEqual({ filePath: '/tmp/foo.js', oldText: 'a', newText: 'b' });
21
- });
22
-
23
- test('Write tool_use produces a diff payload with filePath/newText only', () => {
24
- const block = { name: 'Write', input: { file_path: '/tmp/bar.js', content: 'hello' } };
25
- const result = classifyToolUse(block);
26
- expect(result.diff).toEqual({ filePath: '/tmp/bar.js', newText: 'hello' });
27
- });
28
-
29
- test('non-Edit/Write tool_use has no diff field', () => {
30
- const block = { name: 'Bash', input: { command: 'ls' } };
31
- const result = classifyToolUse(block);
32
- expect(result.diff).toBeUndefined();
33
- expect('diff' in result).toBe(false);
34
- });
35
-
36
- test('Skill/mcp tool_use never get a diff field', () => {
37
- expect(classifyToolUse({ name: 'Skill', input: { skill: 'x' } }).diff).toBeUndefined();
38
- expect(classifyToolUse({ name: 'mcp__foo__bar', input: {} }).diff).toBeUndefined();
39
- });
40
-
41
- test('oversized Edit strings are capped at MAX_DIFF_STR', () => {
42
- const big = 'x'.repeat(MAX_DIFF_STR + 100);
43
- const block = { name: 'Edit', input: { file_path: '/tmp/foo.js', old_string: big, new_string: big } };
44
- const result = classifyToolUse(block);
45
- expect(result.diff.oldText.length).toBe(MAX_DIFF_STR + 1); // + ellipsis char
46
- expect(result.diff.oldText.endsWith('…')).toBe(true);
47
- });
48
-
49
- test('Edit block missing file_path yields no diff', () => {
50
- const block = { name: 'Edit', input: { old_string: 'a', new_string: 'b' } };
51
- const result = classifyToolUse(block);
52
- expect(result.diff).toBeUndefined();
53
- });