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,89 +0,0 @@
1
- /**
2
- * scheduler-utilization-hold.test.cjs — the when-available gate holds on the
3
- * BINDING window and records a machine-readable hold (window/percent/
4
- * threshold/reset) on the snapshot; under threshold it dispatches.
5
- * Run: timeout 180 npx vitest run src/main/__tests__/scheduler-utilization-hold.test.cjs
6
- */
7
- 'use strict';
8
-
9
- import { test, expect, beforeAll, afterAll, afterEach } from 'vitest';
10
- const fs = require('node:fs');
11
- const os = require('node:os');
12
- const path = require('node:path');
13
-
14
- let tmpHome;
15
- let originalHome;
16
- let scheduler;
17
- let billing;
18
- let originalFetchUsage;
19
-
20
- beforeAll(() => {
21
- originalHome = process.env.HOME;
22
- tmpHome = fs.mkdtempSync(path.join(os.tmpdir(), 'sm-utilhold-'));
23
- process.env.HOME = tmpHome;
24
- fs.mkdirSync(path.join(tmpHome, '.claude', 'session-manager'), { recursive: true });
25
- fs.mkdirSync(path.join(tmpHome, '.claude', 'projects'), { recursive: true });
26
- scheduler = require('../scheduler.cjs');
27
- billing = require('../usage.cjs');
28
- originalFetchUsage = billing.fetchUsage;
29
- });
30
-
31
- afterAll(() => {
32
- billing.fetchUsage = originalFetchUsage;
33
- process.env.HOME = originalHome;
34
- fs.rmSync(tmpHome, { recursive: true, force: true });
35
- });
36
-
37
- afterEach(() => { billing.fetchUsage = originalFetchUsage; });
38
-
39
- const RESET = '2026-09-24T17:00:00Z';
40
- function usageWith(weeklyPct) {
41
- return {
42
- kind: 'ok',
43
- data: {
44
- usage: {
45
- five_hour: { utilization: 10, resets_at: '2026-09-21T20:00:00Z' },
46
- limits: [
47
- { kind: 'session', percent: 10, scope: null, resets_at: '2026-09-21T20:00:00Z' },
48
- { kind: 'weekly_all', percent: weeklyPct, scope: null, resets_at: RESET },
49
- ],
50
- },
51
- },
52
- };
53
- }
54
-
55
- // dependsOn never resolves so a tick reaches the picker (stamping
56
- // lastDispatchAttemptAt) without ever spawning a real job.
57
- function state() {
58
- return {
59
- config: { firePolicy: 'when-available', utilizationThreshold: 90 },
60
- paused: null,
61
- jobs: [{ slug: '9002-hold', title: 'x', cwd: tmpHome, status: 'pending', dependsOn: ['never'] }],
62
- };
63
- }
64
-
65
- test('holds (no tick) and records the hold when the binding weekly window is over threshold', async () => {
66
- billing.fetchUsage = async () => usageWith(91);
67
- await scheduler.refreshNextReset();
68
- const logs = [];
69
- const orig = console.log;
70
- console.log = (...a) => { logs.push(a.join(' ')); };
71
- try { await scheduler.maybeLaunchWhenAvailable(state()); } finally { console.log = orig; }
72
- const payload = scheduler.buildScheduleStatePayload(state());
73
- expect(payload.utilizationHold).toEqual({ window: 'weekly_all', percent: 91, threshold: 90, resetsAt: RESET });
74
- expect(payload.utilizationWindow).toBe('weekly_all');
75
- expect(logs.some((l) => l.includes('utilization-held') && l.includes('weekly_all'))).toBe(true);
76
- expect(logs.some((l) => l.includes('— ticking'))).toBe(false);
77
- });
78
-
79
- test('ticks and clears the hold when the binding window is under threshold (weekly_all 64)', async () => {
80
- billing.fetchUsage = async () => usageWith(64);
81
- await scheduler.refreshNextReset();
82
- const logs = [];
83
- const orig = console.log;
84
- console.log = (...a) => { logs.push(a.join(' ')); };
85
- try { await scheduler.maybeLaunchWhenAvailable(state()); } finally { console.log = orig; }
86
- await scheduler.tickQueue();
87
- expect(scheduler.buildScheduleStatePayload(state()).utilizationHold).toBeNull();
88
- expect(logs.some((l) => l.includes('— ticking'))).toBe(true);
89
- });
@@ -1,109 +0,0 @@
1
- /**
2
- * scheduler-verify-prd-path.test.cjs — regression test for PRD 991: the
3
- * verifier's `prdPath` must resolve to a REAL, existing file for an
4
- * Epic-scoped PRD, both live and archived.
5
- *
6
- * Before this PRD, `verifyRun`'s two call sites (the post-run verify in
7
- * spawnJob and reverifyNeedsReview) built `prdPath` via `prdPathForJob`,
8
- * which resolves to the RETIRED flat
9
- * `<cwd>/session-manager-operations/scheduler/prds/` dir — a dir that holds
10
- * only zero-byte `.reserved-NNN` stubs for every project that has migrated
11
- * to Epic-scoped PRDs (i.e. every project today). The verifier therefore
12
- * silently lost access to the PRD body (dependsOn checks, soak-floor dates,
13
- * deliverable-path checks, the merge-main PR-number exemption) on every
14
- * single run.
15
- *
16
- * This test exercises actual path resolution against a temp tree — no
17
- * resolver-stubbing — the same class of gap that let this bug (and PRD
18
- * 985's sibling notify-path bug) ship green twice.
19
- *
20
- * Run: timeout 120 npx vitest run src/main/__tests__/scheduler-verify-prd-path.test.cjs
21
- */
22
-
23
- 'use strict';
24
-
25
- import { test, expect, beforeAll, afterAll } from 'vitest';
26
- const fs = require('node:fs');
27
- const os = require('node:os');
28
- const path = require('node:path');
29
-
30
- let tmpHome;
31
- let originalHome;
32
- let resolveVerifyPrdPath;
33
- let archivedPrdPathForJob;
34
- let PRDS_DIR;
35
-
36
- beforeAll(() => {
37
- originalHome = process.env.HOME;
38
- tmpHome = fs.mkdtempSync(path.join(os.tmpdir(), 'sm-verify-prd-path-'));
39
- process.env.HOME = tmpHome;
40
-
41
- // Load AFTER HOME is stubbed — scheduler.cjs snapshots os.homedir() into
42
- // PRDS_DIR at module load, not lazily (see scheduler-find-prd-dir.test.cjs).
43
- ({ resolveVerifyPrdPath, archivedPrdPathForJob, PRDS_DIR } = require('../scheduler.cjs'));
44
-
45
- if (!PRDS_DIR.startsWith(tmpHome)) {
46
- throw new Error(`refusing to run: PRDS_DIR (${PRDS_DIR}) is not under the temp HOME (${tmpHome})`);
47
- }
48
-
49
- // allProjectCwds()/activeProjectCwds() discover project cwds by scanning
50
- // ~/.claude/projects/*/*.jsonl for a `cwd` field — fake one project with a
51
- // transcript pointing at a real on-disk cwd.
52
- const projectsDir = path.join(tmpHome, '.claude', 'projects');
53
- const projDir = path.join(projectsDir, 'fake-project-slug');
54
- fs.mkdirSync(projDir, { recursive: true });
55
- fs.writeFileSync(
56
- path.join(projDir, 'session.jsonl'),
57
- `${JSON.stringify({ cwd: projectCwd() })}\n`,
58
- 'utf8',
59
- );
60
- });
61
-
62
- afterAll(() => {
63
- process.env.HOME = originalHome;
64
- fs.rmSync(tmpHome, { recursive: true, force: true });
65
- });
66
-
67
- function projectCwd() {
68
- return path.join(tmpHome, 'fake-project-cwd');
69
- }
70
-
71
- test('resolveVerifyPrdPath resolves a LIVE Epic-scoped PRD to a real existing path', async () => {
72
- const cwd = projectCwd();
73
- const epicId = 'test-epic-live';
74
- const slug = `991-test-live-${process.pid}-${Math.floor(Math.random() * 1e6)}`;
75
- const prdsDir = path.join(cwd, 'session-manager-operations', 'scheduler', 'epics', epicId, 'prds');
76
- fs.mkdirSync(prdsDir, { recursive: true });
77
- const prdPath = path.join(prdsDir, `${slug}.md`);
78
- fs.writeFileSync(prdPath, '---\ntitle: live fixture\n---\n\n# Goal\n\ntest\n', 'utf8');
79
-
80
- const resolved = await resolveVerifyPrdPath({ slug, cwd });
81
- expect(resolved).toBe(prdPath);
82
- expect(fs.existsSync(resolved)).toBe(true);
83
- });
84
-
85
- test('resolveVerifyPrdPath returns null for an ARCHIVED-only PRD; archivedPrdPathForJob fills the gap', async () => {
86
- const cwd = projectCwd();
87
- const epicId = 'test-epic-archived';
88
- const slug = `991-test-archived-${process.pid}-${Math.floor(Math.random() * 1e6)}`;
89
- const epicDir = path.join(cwd, 'session-manager-operations', 'scheduler', 'epics', epicId);
90
- const archiveDir = path.join(epicDir, 'prds-archived');
91
- fs.mkdirSync(archiveDir, { recursive: true });
92
- const archivedPath = path.join(archiveDir, `${slug}.md`);
93
- fs.writeFileSync(archivedPath, '---\ntitle: archived fixture\n---\n\n# Goal\n\ntest\n', 'utf8');
94
-
95
- const job = { slug, cwd };
96
- const live = await resolveVerifyPrdPath(job);
97
- expect(live).toBe(null);
98
-
99
- const fallback = live ?? archivedPrdPathForJob(job);
100
- expect(fallback).toBe(archivedPath);
101
- expect(fs.existsSync(fallback)).toBe(true);
102
- });
103
-
104
- test('resolveVerifyPrdPath returns null for a slug that exists nowhere (never falls back to the retired flat dir)', async () => {
105
- const cwd = projectCwd();
106
- const slug = `991-test-nonexistent-${process.pid}-${Math.floor(Math.random() * 1e6)}`;
107
- const resolved = await resolveVerifyPrdPath({ slug, cwd });
108
- expect(resolved).toBe(null);
109
- });
@@ -1,234 +0,0 @@
1
- /**
2
- * scheduler-worktree-cap-defer.test.cjs — PRD 1112: a job that cannot get
3
- * worktree isolation because the concurrency cap is currently exhausted must
4
- * be DEFERRED (left 'pending', retried on the next dispatch pass), never
5
- * spawned in a shared working tree the way every OTHER worktree.ok===false
6
- * reason (e.g. "not a git repository") still falls back to running in place.
7
- *
8
- * Drives spawnJob end-to-end against a real git fixture repo, forcing the
9
- * cap via SM_JOB_WORKTREE_MAX=1 plus a real occupying worktree (so the cap
10
- * check itself, not a mock, is what defers the job).
11
- *
12
- * Run: timeout 120 npx vitest run src/main/__tests__/scheduler-worktree-cap-defer.test.cjs
13
- */
14
-
15
- 'use strict';
16
-
17
- import { test, expect, beforeAll, afterAll, afterEach } from 'vitest';
18
- const fs = require('node:fs');
19
- const os = require('node:os');
20
- const path = require('node:path');
21
- const claudeStub = require('../../../tests/helpers/claudeStub.cjs');
22
- const { execFileSync } = require('node:child_process');
23
-
24
- let tmpHome;
25
- let originalHome;
26
- let originalMax;
27
- let spawnJob;
28
- let gitWorktree;
29
-
30
- function git(args, cwd) {
31
- return execFileSync('git', args, { cwd, encoding: 'utf8' });
32
- }
33
-
34
- // A completed job's PRD is archived immediately (archiveCompletedPrd, called
35
- // for EVERY job that reaches effectiveStatus:'completed' — not just a
36
- // retention-aged one), and the very next reconcile() pass (spawnJob's own
37
- // broadcast({flush:true}) right after finishing) then finds the row's PRD
38
- // gone and backfills+drops it from queue.json into history.jsonl in that
39
- // SAME pass — see scheduler-reconcile-history-backfill.test.cjs. So a job
40
- // that actually completed is found in EITHER queue.json OR history.jsonl,
41
- // never guaranteed to still be the former.
42
- function readTerminalRow(queuePath, projectCwd, slug) {
43
- const jobs = JSON.parse(fs.readFileSync(queuePath, 'utf8')).jobs;
44
- const row = jobs.find((j) => j.slug === slug);
45
- if (row) return row;
46
- const historyPath = path.join(projectCwd, 'session-manager-operations', 'scheduler', 'state', 'history.jsonl');
47
- if (!fs.existsSync(historyPath)) return undefined;
48
- const lines = fs.readFileSync(historyPath, 'utf8').trim().split('\n').filter(Boolean);
49
- return lines.map((l) => JSON.parse(l)).reverse().find((j) => j.slug === slug);
50
- }
51
-
52
- function initRepo(dir) {
53
- fs.mkdirSync(dir, { recursive: true });
54
- git(['init', '-q'], dir);
55
- git(['config', 'user.email', 'test@example.com'], dir);
56
- git(['config', 'user.name', 'Test'], dir);
57
- fs.writeFileSync(path.join(dir, 'README.md'), 'hello\n', 'utf8');
58
- git(['add', '-A'], dir);
59
- git(['commit', '-q', '-m', 'initial'], dir);
60
- }
61
-
62
- // See scheduler-quiet-machine-lease.test.cjs's identical helper: readQueue()'s
63
- // per-project discovery walks ~/.claude/projects/*/*.jsonl transcripts for a
64
- // `cwd` field to find known project queue.json shards.
65
- function registerActiveProject(cwd) {
66
- const projectsDir = path.join(tmpHome, '.claude', 'projects');
67
- const slugDir = path.join(projectsDir, `fake-project-slug-${path.basename(cwd)}`);
68
- fs.mkdirSync(slugDir, { recursive: true });
69
- fs.writeFileSync(path.join(slugDir, 'transcript.jsonl'), JSON.stringify({ cwd }) + '\n');
70
- }
71
-
72
- function writeProjectQueue(cwd, jobs) {
73
- const stateDir = path.join(cwd, 'session-manager-operations', 'scheduler', 'state');
74
- fs.mkdirSync(stateDir, { recursive: true });
75
- fs.writeFileSync(path.join(stateDir, 'queue.json'), JSON.stringify({ jobs }, null, 2));
76
- return path.join(stateDir, 'queue.json');
77
- }
78
-
79
- // Stub `claude` binary: writes a marker file into its OWN process.cwd() so a
80
- // test can prove whether — and where — it was actually spawned, then emits a
81
- // clean stream-json success result.
82
- function writeClaudeStub() {
83
- return claudeStub.writeClaudeStub({
84
- allowCommitUnder: os.tmpdir(),
85
- body: `
86
- const fs = require('fs');
87
- const path = require('path');
88
- const { execFileSync } = require('child_process');
89
- fs.writeFileSync(path.join(process.cwd(), 'ran-here.marker'), 'yes', 'utf8');
90
- // Land a real commit + finish-protocol sentinel when possible, so a run
91
- // that actually reaches the finish protocol lands 'completed' — a
92
- // best-effort no-op when the cwd isn't a git repo (e.g. the non-git
93
- // fallback case), where the run parks in needs_review instead, which is
94
- // fine — this test only cares whether it ran in place, not its verdict.
95
- try {
96
- runGit(['add', '-A']);
97
- runGit(['commit', '-q', '-m', 'stub commit']);
98
- } catch { /* not a git repo — best-effort only */ }
99
- process.stdout.write(JSON.stringify({ type: 'result', subtype: 'success', result: 'ok\\nSCHEDULER_VERDICT: PASS' }) + '\\n');
100
- process.exit(0);
101
- `,
102
- });
103
- }
104
-
105
- beforeAll(() => {
106
- originalHome = process.env.HOME;
107
- tmpHome = fs.mkdtempSync(path.join(os.tmpdir(), 'sm-worktree-cap-defer-'));
108
- process.env.HOME = tmpHome;
109
- originalMax = process.env.SM_JOB_WORKTREE_MAX;
110
- ({ spawnJob } = require('../scheduler.cjs'));
111
- gitWorktree = require('../lib/gitWorktree.cjs');
112
- });
113
-
114
- afterAll(() => {
115
- process.env.HOME = originalHome;
116
- if (originalMax === undefined) delete process.env.SM_JOB_WORKTREE_MAX;
117
- else process.env.SM_JOB_WORKTREE_MAX = originalMax;
118
- fs.rmSync(tmpHome, { recursive: true, force: true });
119
- });
120
-
121
- afterEach(() => {
122
- delete process.env.SM_CLAUDE_BIN;
123
- delete process.env.SM_JOB_WORKTREE_MAX;
124
- gitWorktree._resetActiveWorktreeCountForTests('job', 0);
125
- });
126
-
127
- test('a job held behind the worktree cap stays pending with a heldReason, is never spawned in place, and dispatches once the cap frees up', async () => {
128
- const projectCwd = fs.mkdtempSync(path.join(os.tmpdir(), 'sm-worktree-cap-defer-project-'));
129
- initRepo(projectCwd);
130
- registerActiveProject(projectCwd);
131
-
132
- const slug = `1112-test-held-${process.pid}-${Math.floor(Math.random() * 1e6)}`;
133
- // Epic-scoped, not the legacy flat prds/ dir: the flat dir is retired and
134
- // swept into prds-archived/ by consolidateFlatPrds on every reconcile()
135
- // pass once a row is no longer 'pending'/'running' — which would strand
136
- // this test's row the moment it completes (reconcile()'s terminal-job
137
- // "PRD gone -> archived on purpose" drop, see
138
- // scheduler-reconcile-history-backfill.test.cjs).
139
- const prdsDir = path.join(projectCwd, 'session-manager-operations', 'scheduler', 'epics', 'test-fixture-epic', 'prds');
140
- fs.mkdirSync(prdsDir, { recursive: true });
141
- fs.writeFileSync(path.join(prdsDir, `${slug}.md`), 'Held behind the worktree cap.', 'utf8');
142
- const queuePath = writeProjectQueue(projectCwd, [{ slug, status: 'pending', cwd: projectCwd }]);
143
-
144
- process.env.SM_JOB_WORKTREE_MAX = '1';
145
- process.env.SM_CLAUDE_BIN = writeClaudeStub();
146
-
147
- // Occupy the single worktree slot with a REAL checkout, so the cap check
148
- // spawnJob relies on is exercising the actual gitWorktree plumbing, not a
149
- // mock.
150
- const occupier = await gitWorktree.createJobWorktree({ cwd: projectCwd, slug: 'occupier' });
151
- expect(occupier.ok).toBe(true);
152
-
153
- const runId = `run-${slug}`;
154
- const runDir = path.join(tmpHome, '.claude', 'session-manager', 'scheduled-plans', 'runs', runId);
155
- fs.mkdirSync(runDir, { recursive: true });
156
-
157
- try {
158
- await spawnJob({ slug, cwd: projectCwd }, runId, runDir, projectCwd);
159
-
160
- // Held: still pending, never spawned, heldReason visible on the row.
161
- let jobs = JSON.parse(fs.readFileSync(queuePath, 'utf8')).jobs;
162
- let row = jobs.find((j) => j.slug === slug);
163
- expect(row.status).toBe('pending');
164
- expect(row.heldReason).toMatch(/worktree cap reached/);
165
- expect(fs.existsSync(path.join(projectCwd, 'ran-here.marker'))).toBe(false);
166
- expect(fs.existsSync(path.join(occupier.dir, 'ran-here.marker'))).toBe(false);
167
-
168
- // Free the cap (mirrors cleanupJobWorktree releasing the counter), then
169
- // retry — the held job dispatches on this next pass, exactly like a
170
- // sessionSlots-blocked job retries once a slot frees up.
171
- await gitWorktree.cleanupJobWorktree({ cwd: projectCwd, dir: occupier.dir, branch: occupier.branch });
172
-
173
- await spawnJob({ slug, cwd: projectCwd }, `${runId}-2`, runDir, projectCwd);
174
-
175
- row = readTerminalRow(queuePath, projectCwd, slug);
176
- expect(row).toBeTruthy();
177
- expect(row.status).toBe('completed');
178
- expect(row.heldReason).toBeUndefined();
179
- } finally {
180
- fs.rmSync(projectCwd, { recursive: true, force: true });
181
- fs.rmSync(runDir, { recursive: true, force: true });
182
- }
183
- }, 30_000);
184
-
185
- test('a non-git cwd still falls back to running in place (unchanged behaviour — only cap-reached is a deferral)', async () => {
186
- const projectCwd = fs.mkdtempSync(path.join(os.tmpdir(), 'sm-worktree-cap-defer-nongit-'));
187
- fs.mkdirSync(projectCwd, { recursive: true }); // deliberately NOT a git repo
188
- registerActiveProject(projectCwd);
189
-
190
- const slug = `1112-test-nongit-${process.pid}-${Math.floor(Math.random() * 1e6)}`;
191
- // Epic-scoped — see the sibling test above for why not the flat prds/ dir.
192
- const prdsDir = path.join(projectCwd, 'session-manager-operations', 'scheduler', 'epics', 'test-fixture-epic', 'prds');
193
- fs.mkdirSync(prdsDir, { recursive: true });
194
- fs.writeFileSync(path.join(prdsDir, `${slug}.md`), 'Non-git cwd, must run in place.', 'utf8');
195
- const queuePath = writeProjectQueue(projectCwd, [{ slug, status: 'pending', cwd: projectCwd }]);
196
-
197
- process.env.SM_CLAUDE_BIN = writeClaudeStub();
198
-
199
- const runId = `run-${slug}`;
200
- const runDir = path.join(tmpHome, '.claude', 'session-manager', 'scheduled-plans', 'runs', runId);
201
- fs.mkdirSync(runDir, { recursive: true });
202
-
203
- try {
204
- await spawnJob({ slug, cwd: projectCwd }, runId, runDir, projectCwd);
205
-
206
- const jobs = JSON.parse(fs.readFileSync(queuePath, 'utf8')).jobs;
207
- const row = jobs.find((j) => j.slug === slug);
208
- // Not held/deferred — it actually ran (a non-git cwd can't commit, so it
209
- // parks in needs_review rather than completed; this test only cares that
210
- // it was dispatched in place, not its exact verdict).
211
- expect(row.status).not.toBe('pending');
212
- expect(row.heldReason).toBeUndefined();
213
- expect(row.worktreeFallbackReason).toMatch(/not a git repository/);
214
- // Ran IN PLACE — the marker landed directly in projectCwd, not a worktree.
215
- expect(fs.existsSync(path.join(projectCwd, 'ran-here.marker'))).toBe(true);
216
-
217
- // A job losing worktree isolation must never be a silent downgrade
218
- // discoverable only by reading queue.json afterwards — it must also land
219
- // as a warn line in the durable ops error log, naming the slug, cwd, and
220
- // reason.
221
- const { todayFile } = require('../lib/opsErrorLog.cjs');
222
- const errorLogPath = todayFile(projectCwd);
223
- expect(fs.existsSync(errorLogPath)).toBe(true);
224
- const logLines = fs.readFileSync(errorLogPath, 'utf8').trim().split('\n').map((l) => JSON.parse(l));
225
- const fallbackLine = logLines.find((l) => l.scope === 'scheduler' && l.level === 'warn' && l.message.includes(slug));
226
- expect(fallbackLine).toBeTruthy();
227
- expect(fallbackLine.message).toMatch(/SHARED working tree/);
228
- expect(fallbackLine.message).toMatch(/not a git repository/);
229
- expect(fallbackLine.meta).toMatchObject({ slug, cwd: projectCwd, reason: expect.stringMatching(/not a git repository/) });
230
- } finally {
231
- fs.rmSync(projectCwd, { recursive: true, force: true });
232
- fs.rmSync(runDir, { recursive: true, force: true });
233
- }
234
- }, 30_000);
@@ -1,120 +0,0 @@
1
- /**
2
- * scheduler-worktree-exec-cwd.test.cjs — PRD 994, LOAD-BEARING acceptance
3
- * criterion: when a job is isolated into a worktree, its PRD file, queue
4
- * state, and run logs must still resolve to the MAIN tree, never the
5
- * worktree — `session-manager-operations/` is resolved from a `cwd` param
6
- * (lib/prdLocations.cjs), so a naive cwd swap would give the job a
7
- * different, empty ops root and orphan its own PRD.
8
- *
9
- * Drives executeJob's real `execCwd` parameter (the 5th arg scheduler.cjs
10
- * passes when a worktree was created) with a stub `claude` binary, proving:
11
- * 1. the child process's OWN cwd is the worktree dir (execCwd) — verified
12
- * by having the stub write a marker file to `process.cwd()`;
13
- * 2. the PRD source is still found via job.cwd (the main tree) even though
14
- * the worktree dir has no PRD file in it at all;
15
- * 3. the run's meta.json still records job.cwd, not the worktree dir.
16
- *
17
- * Run: timeout 120 npx vitest run src/main/__tests__/scheduler-worktree-exec-cwd.test.cjs
18
- */
19
-
20
- 'use strict';
21
-
22
- import { test, expect, beforeAll, afterAll, afterEach } from 'vitest';
23
- const fs = require('node:fs');
24
- const os = require('node:os');
25
- const path = require('node:path');
26
- const claudeStub = require('../../../tests/helpers/claudeStub.cjs');
27
-
28
- let tmpHome;
29
- let originalHome;
30
- let executeJob;
31
-
32
- beforeAll(() => {
33
- originalHome = process.env.HOME;
34
- tmpHome = fs.mkdtempSync(path.join(os.tmpdir(), 'sm-worktree-execcwd-'));
35
- process.env.HOME = tmpHome;
36
- ({ executeJob } = require('../scheduler.cjs'));
37
- });
38
-
39
- afterAll(() => {
40
- process.env.HOME = originalHome;
41
- fs.rmSync(tmpHome, { recursive: true, force: true });
42
- });
43
-
44
- afterEach(() => {
45
- delete process.env.SM_CLAUDE_BIN;
46
- });
47
-
48
- // Stub `claude` binary: writes a marker file into its OWN process.cwd() (proving
49
- // where it actually ran), captures the '-p' prompt, then emits a stream-json
50
- // success result and exits 0.
51
- function writeClaudeStub() {
52
- return claudeStub.writeClaudeStub({ body: `
53
- const fs = require('fs');
54
- const path = require('path');
55
- fs.writeFileSync(path.join(process.cwd(), 'ran-here.marker'), 'yes', 'utf8');
56
- process.stdout.write(JSON.stringify({ type: 'result', subtype: 'success', result: 'ok' }) + '\\n');
57
- process.exit(0);
58
- ` });
59
- }
60
-
61
- test('executeJob spawns the child in execCwd (worktree) while resolving the PRD from job.cwd (main tree)', async () => {
62
- const mainCwd = fs.mkdtempSync(path.join(os.tmpdir(), 'sm-worktree-main-'));
63
- const worktreeCwd = fs.mkdtempSync(path.join(os.tmpdir(), 'sm-worktree-exec-'));
64
- fs.mkdirSync(path.join(tmpHome, '.claude'), { recursive: true });
65
- const runDir = fs.mkdtempSync(path.join(tmpHome, '.claude', 'sm-worktree-run-'));
66
-
67
- try {
68
- const slug = `994-test-${process.pid}-${Math.floor(Math.random() * 1e6)}`;
69
- const prdsDir = path.join(mainCwd, 'session-manager-operations', 'scheduler', 'prds');
70
- fs.mkdirSync(prdsDir, { recursive: true });
71
- const bodyText = 'Do the isolated-worktree thing.';
72
- fs.writeFileSync(path.join(prdsDir, `${slug}.md`), bodyText, 'utf8');
73
-
74
- process.env.SM_CLAUDE_BIN = writeClaudeStub();
75
-
76
- const job = { slug, cwd: mainCwd };
77
- const result = await executeJob(job, runDir, mainCwd, () => Promise.resolve(), worktreeCwd);
78
-
79
- expect(result.exitCode).toBe(0);
80
-
81
- // 1. The child process really ran with cwd = worktreeCwd, not mainCwd.
82
- expect(fs.existsSync(path.join(worktreeCwd, 'ran-here.marker'))).toBe(true);
83
- expect(fs.existsSync(path.join(mainCwd, 'ran-here.marker'))).toBe(false);
84
-
85
- // 2. The PRD source itself is untouched — resolved via job.cwd, not execCwd.
86
- const onDiskPrd = fs.readFileSync(path.join(prdsDir, `${slug}.md`), 'utf8');
87
- expect(onDiskPrd).toBe(bodyText);
88
-
89
- // 3. The run's meta.json records job.cwd (the main tree), not the worktree.
90
- const metaPath = path.join(runDir, `${slug}.meta.json`);
91
- const meta = JSON.parse(fs.readFileSync(metaPath, 'utf8'));
92
- expect(meta.cwd).toBe(mainCwd);
93
- } finally {
94
- fs.rmSync(mainCwd, { recursive: true, force: true });
95
- fs.rmSync(worktreeCwd, { recursive: true, force: true });
96
- fs.rmSync(runDir, { recursive: true, force: true });
97
- }
98
- });
99
-
100
- test('executeJob falls back to job.cwd for the spawn cwd when no execCwd is supplied (unchanged behaviour)', async () => {
101
- const mainCwd = fs.mkdtempSync(path.join(os.tmpdir(), 'sm-worktree-fallback-'));
102
- fs.mkdirSync(path.join(tmpHome, '.claude'), { recursive: true });
103
- const runDir = fs.mkdtempSync(path.join(tmpHome, '.claude', 'sm-worktree-run2-'));
104
- try {
105
- const slug = `994-test-fallback-${process.pid}-${Math.floor(Math.random() * 1e6)}`;
106
- const prdsDir = path.join(mainCwd, 'session-manager-operations', 'scheduler', 'prds');
107
- fs.mkdirSync(prdsDir, { recursive: true });
108
- fs.writeFileSync(path.join(prdsDir, `${slug}.md`), 'no worktree here', 'utf8');
109
- process.env.SM_CLAUDE_BIN = writeClaudeStub();
110
-
111
- const job = { slug, cwd: mainCwd };
112
- const result = await executeJob(job, runDir, mainCwd, () => Promise.resolve());
113
-
114
- expect(result.exitCode).toBe(0);
115
- expect(fs.existsSync(path.join(mainCwd, 'ran-here.marker'))).toBe(true);
116
- } finally {
117
- fs.rmSync(mainCwd, { recursive: true, force: true });
118
- fs.rmSync(runDir, { recursive: true, force: true });
119
- }
120
- });
@@ -1,105 +0,0 @@
1
- /**
2
- * scheduler-writeprd-epic-rollback.test.cjs — remote.writePrd may only JOIN
3
- * an existing, already-human-approved Epic (via `sourcePromptId`); it must
4
- * never mint a new one (no mintAuthority, see epicMint.cjs's ensureEpic).
5
- * A write with no resolvable Epic fails outright with zero residual
6
- * active-index.json entries — no orphaned Epic can ever be left behind by
7
- * this path because none is ever created by it.
8
- *
9
- * A PRD write that joins a pre-existing Epic and then fails (e.g. an invalid
10
- * slug) must leave that Epic's history untouched.
11
- *
12
- * Run: timeout 120 npx vitest run src/main/__tests__/scheduler-writeprd-epic-rollback.test.cjs
13
- */
14
-
15
- 'use strict';
16
-
17
- import { test, expect, afterEach } from 'vitest';
18
- const fs = require('node:fs');
19
- const os = require('node:os');
20
- const path = require('node:path');
21
- const { remote } = require('../scheduler.cjs');
22
- const { ensureEpic, readActiveIndex, MINT_AUTHORITY_NEW_EPIC_UI } = require('../lib/epicMint.cjs');
23
- const config = require('../config.cjs');
24
-
25
- const tmpDirs = [];
26
-
27
- function makeFixtureCwd() {
28
- const cwd = fs.mkdtempSync(path.join(os.tmpdir(), 'sm-writeprd-epic-rollback-'));
29
- tmpDirs.push(cwd);
30
- return cwd;
31
- }
32
-
33
- afterEach(() => {
34
- while (tmpDirs.length) {
35
- const dir = tmpDirs.pop();
36
- fs.rmSync(dir, { recursive: true, force: true });
37
- }
38
- });
39
-
40
- // A slug that escapes safeSlugPathIn's containment check, tripping the
41
- // "invalid slug" branch of writePrd (scheduler.cjs's writePrd). Only the
42
- // second test below (joining a pre-existing Epic) actually reaches this
43
- // check — the first test fails earlier, at Epic resolution, before the slug
44
- // is ever considered.
45
- const ESCAPING_SLUG = '../escape-attempt';
46
-
47
- test('writePrd: no sourcePromptId refuses to write (no Epic to join) and mints nothing', async () => {
48
- const cwd = makeFixtureCwd();
49
- const body = '---\ntitle: No epic given\n---\n\nbody text\n';
50
-
51
- const result = await remote.writePrd(ESCAPING_SLUG, body, cwd);
52
-
53
- expect(result.ok).toBe(false);
54
- expect(result.error).toMatch(/no existing Epic to join/);
55
-
56
- const index = readActiveIndex(cwd);
57
- expect(Object.keys(index.sessions)).toHaveLength(0);
58
- expect(Object.keys(index.events)).toHaveLength(0);
59
- });
60
-
61
- test('writePrd: invalid-slug failure when joining a pre-existing Epic leaves that Epic untouched', async () => {
62
- const cwd = makeFixtureCwd();
63
-
64
- const preexisting = await ensureEpic(cwd, { goalText: 'pre-existing epic goal', mintAuthority: MINT_AUTHORITY_NEW_EPIC_UI });
65
- expect(preexisting.created).toBe(true);
66
-
67
- const beforeIndex = readActiveIndex(cwd);
68
- expect(Object.keys(beforeIndex.sessions)).toEqual([preexisting.epicId]);
69
-
70
- const body = `---\ntitle: Joins the existing epic\nsourcePromptId: ${preexisting.epicId}\n---\n\nbody text\n`;
71
- const result = await remote.writePrd(ESCAPING_SLUG, body, cwd);
72
-
73
- expect(result.ok).toBe(false);
74
- expect(result.error).toBe('invalid slug');
75
-
76
- const afterIndex = readActiveIndex(cwd);
77
- expect(Object.keys(afterIndex.sessions)).toEqual([preexisting.epicId]);
78
- expect(afterIndex.sessions[preexisting.epicId]).toEqual(beforeIndex.sessions[preexisting.epicId]);
79
- expect(afterIndex.events[preexisting.epicId]).toEqual(beforeIndex.events[preexisting.epicId]);
80
- });
81
-
82
- // Provenance (PRD 905): writePrd's ensureEpic call passes
83
- // source: { producer: 'scheduler-dispatch', prdSlug }, but this call is
84
- // join-only (no mintAuthority, explicit epicId) — ensureEpic's
85
- // explicitEpicId branch returns before ever reading `source`, so a
86
- // successful join must NOT write/alter the joined Epic's `source` field.
87
- test('writePrd: successful join does not write a source field onto the pre-existing Epic (join-only no-op)', async () => {
88
- // Must live under the home dir — config.cjs's writeTextAtomic validates
89
- // paths against allowedRoots=home dir, unlike the failure-path tests above
90
- // (which never reach the write) os.tmpdir() is not sufficient here.
91
- const cwd = fs.mkdtempSync(path.join(os.homedir(), '.sm-writeprd-epic-source-'));
92
- tmpDirs.push(cwd);
93
- config.addAllowedRoot(cwd);
94
-
95
- const preexisting = await ensureEpic(cwd, { goalText: 'pre-existing epic goal for join', mintAuthority: MINT_AUTHORITY_NEW_EPIC_UI });
96
- expect(preexisting.created).toBe(true);
97
-
98
- const body = `---\ntitle: Joins the existing epic\nsourcePromptId: ${preexisting.epicId}\n---\n\nbody text\n`;
99
- const result = await remote.writePrd('valid-slug', body, cwd);
100
-
101
- expect(result.ok).toBe(true);
102
-
103
- const index = readActiveIndex(cwd);
104
- expect('source' in index.sessions[preexisting.epicId]).toBe(false);
105
- });