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,718 +0,0 @@
1
- /**
2
- * prdAdminRoutes.test.cjs — unit tests for PRD 1024's admin HTTP routes
3
- * (list/get/update/archive/cancel/retag) and the scheduler.cjs `remote`
4
- * methods they delegate to. Exercises real HTTP round-trips against a real
5
- * (temp-HOME) filesystem, matching prdCreate.test.cjs's pattern for the
6
- * create-prd route.
7
- *
8
- * Run: timeout 120 npx vitest run src/main/__tests__/prdAdminRoutes.test.cjs
9
- */
10
-
11
- 'use strict';
12
-
13
- import { test, expect, beforeAll, afterAll, beforeEach } from 'vitest';
14
- const fs = require('node:fs');
15
- const fsp = require('node:fs/promises');
16
- const os = require('node:os');
17
- const path = require('node:path');
18
- const http = require('node:http');
19
-
20
- let tmpHome;
21
- let originalHome;
22
- let scheduler;
23
- let prdAdminRoutes;
24
- let config;
25
- let createAdminHttp;
26
-
27
- beforeAll(() => {
28
- originalHome = process.env.HOME;
29
- tmpHome = fs.mkdtempSync(path.join(os.tmpdir(), 'sm-admin-prd-routes-'));
30
- process.env.HOME = tmpHome;
31
-
32
- // Load AFTER HOME is stubbed — scheduler.cjs/config.cjs snapshot
33
- // os.homedir() into module-level constants at load time, not lazily.
34
- scheduler = require('../scheduler.cjs');
35
- prdAdminRoutes = require('../lib/prdAdminRoutes.cjs');
36
- config = require('../config.cjs');
37
- ({ createAdminHttp } = require('../lib/localAdminHttp.cjs'));
38
-
39
- if (!scheduler.PRDS_DIR.startsWith(tmpHome)) {
40
- throw new Error(`refusing to run: PRDS_DIR (${scheduler.PRDS_DIR}) is not under the temp HOME (${tmpHome})`);
41
- }
42
-
43
- // allProjectCwds()/activeProjectCwds() (queueStore.cjs's stateCwds, and
44
- // prdLocations.cjs's resolvePrdsDirs) discover project cwds by scanning
45
- // ~/.claude/projects/*/*.jsonl for a `cwd` field — fake one project
46
- // transcript pointing at the real on-disk fixture cwd this file uses,
47
- // same pattern as scheduler-verify-prd-path.test.cjs.
48
- const projDir = path.join(tmpHome, '.claude', 'projects', 'fake-project-slug');
49
- fs.mkdirSync(projDir, { recursive: true });
50
- fs.writeFileSync(
51
- path.join(projDir, 'session.jsonl'),
52
- `${JSON.stringify({ cwd: path.join(tmpHome, 'fake-project-cwd') })}\n`,
53
- 'utf8',
54
- );
55
- });
56
-
57
- afterAll(() => {
58
- process.env.HOME = originalHome;
59
- fs.rmSync(tmpHome, { recursive: true, force: true });
60
- });
61
-
62
- function projectCwd() {
63
- const cwd = path.join(tmpHome, 'fake-project-cwd');
64
- fs.mkdirSync(cwd, { recursive: true });
65
- config.addAllowedRoot(cwd);
66
- return cwd;
67
- }
68
-
69
- function uniqueSlug(prefix) {
70
- return `${prefix}-${process.pid}-${Math.floor(Math.random() * 1e6)}`;
71
- }
72
-
73
- async function writePrdFixture({ cwd, epicId, slug, extra = '' }) {
74
- const prdsDir = path.join(cwd, 'session-manager-operations', 'scheduler', 'epics', epicId, 'prds');
75
- await fsp.mkdir(prdsDir, { recursive: true });
76
- const filePath = path.join(prdsDir, `${slug}.md`);
77
- const body = `---\ntitle: fixture title\ncwd: ${cwd}\nestimateMinutes: 10\n${extra}---\n\n# Goal\n\ndo the thing\n`;
78
- await fsp.writeFile(filePath, body, 'utf8');
79
- return filePath;
80
- }
81
-
82
- function request(port, { method = 'GET', path: reqPath, token, body }) {
83
- return new Promise((resolve, reject) => {
84
- const headers = {};
85
- if (token !== undefined) headers.Authorization = `Bearer ${token}`;
86
- let payload;
87
- if (body !== undefined) {
88
- payload = JSON.stringify(body);
89
- headers['Content-Type'] = 'application/json';
90
- headers['Content-Length'] = Buffer.byteLength(payload);
91
- }
92
- const req = http.request({ hostname: '127.0.0.1', port, method, path: reqPath, headers }, (res) => {
93
- const chunks = [];
94
- res.on('data', (c) => chunks.push(c));
95
- res.on('end', () => {
96
- const text = Buffer.concat(chunks).toString('utf8');
97
- let json = null;
98
- try { json = JSON.parse(text); } catch { /* leave null */ }
99
- resolve({ status: res.statusCode, json, text });
100
- });
101
- });
102
- req.on('error', reject);
103
- if (payload) req.write(payload);
104
- req.end();
105
- });
106
- }
107
-
108
- async function startAdmin() {
109
- const prevE2e = process.env.SM_E2E;
110
- process.env.SM_E2E = '1';
111
- const admin = createAdminHttp();
112
- prdAdminRoutes.registerAdminRoute(admin, scheduler.remote);
113
- // Also wires reset-job/jobs (scheduler.cjs's own admin routes) onto the
114
- // SAME admin instance/port, against the real (not faked) `remote` — this
115
- // file's real-filesystem setup is what the reset-job/cancel-job/archive-prd
116
- // slug-vs-not-found split needs, unlike scheduler-admin-routes.test.cjs's
117
- // fake remote.
118
- scheduler.registerAdminRoutes(admin, scheduler.remote);
119
- const { port, token } = await admin.start();
120
- if (prevE2e === undefined) delete process.env.SM_E2E; else process.env.SM_E2E = prevE2e;
121
- return { admin, port, token };
122
- }
123
-
124
- test('GET /admin/scheduler/prds without token returns 401', async () => {
125
- const { admin, port } = await startAdmin();
126
- try {
127
- const res = await request(port, { path: '/admin/scheduler/prds' });
128
- expect(res.status).toBe(401);
129
- } finally {
130
- await admin.stop();
131
- }
132
- });
133
-
134
- test('GET /admin/scheduler/prds lists a live PRD with status null (no queue row yet), filterable by cwd', async () => {
135
- const cwd = projectCwd();
136
- const epicId = 'epic-list';
137
- const slug = uniqueSlug('list-prds');
138
- await writePrdFixture({ cwd, epicId, slug });
139
-
140
- const { admin, port, token } = await startAdmin();
141
- try {
142
- const res = await request(port, { path: `/admin/scheduler/prds?cwd=${encodeURIComponent(cwd)}`, token });
143
- expect(res.status).toBe(200);
144
- expect(res.json.ok).toBe(true);
145
- const entry = res.json.prds.find((p) => p.slug === slug);
146
- expect(entry).toBeTruthy();
147
- expect(entry.status).toBe(null);
148
- } finally {
149
- await admin.stop();
150
- }
151
- });
152
-
153
- test('GET /admin/scheduler/prds defaults to limit=100/offset=0, reports total/hasMore, omits secondary fields', async () => {
154
- const cwd = projectCwd();
155
- const epicId = 'epic-list-defaults';
156
- const slug = uniqueSlug('list-prds-defaults');
157
- await writePrdFixture({ cwd, epicId, slug });
158
-
159
- const { admin, port, token } = await startAdmin();
160
- try {
161
- const res = await request(port, { path: `/admin/scheduler/prds?cwd=${encodeURIComponent(cwd)}`, token });
162
- expect(res.status).toBe(200);
163
- expect(res.json.ok).toBe(true);
164
- expect(res.json.limit).toBe(100);
165
- expect(res.json.offset).toBe(0);
166
- expect(res.json.total).toBe(res.json.prds.length);
167
- expect(res.json.hasMore).toBe(false);
168
- const entry = res.json.prds.find((p) => p.slug === slug);
169
- expect(entry).toBeTruthy();
170
- expect('epicId' in entry).toBe(false);
171
- expect('sourcePromptId' in entry).toBe(false);
172
- } finally {
173
- await admin.stop();
174
- }
175
- });
176
-
177
- test('GET /admin/scheduler/prds?fields=full restores secondary fields', async () => {
178
- const cwd = projectCwd();
179
- const epicId = 'epic-list-full';
180
- const slug = uniqueSlug('list-prds-full');
181
- await writePrdFixture({ cwd, epicId, slug });
182
-
183
- const { admin, port, token } = await startAdmin();
184
- try {
185
- const res = await request(port, { path: `/admin/scheduler/prds?cwd=${encodeURIComponent(cwd)}&fields=full`, token });
186
- expect(res.status).toBe(200);
187
- const entry = res.json.prds.find((p) => p.slug === slug);
188
- expect(entry.epicId).toBe(epicId);
189
- expect(entry.sourcePromptId).toBeTruthy();
190
- } finally {
191
- await admin.stop();
192
- }
193
- });
194
-
195
- test('GET /admin/scheduler/prds pages disjointly by limit/offset in stable slug order', async () => {
196
- const cwd = projectCwd();
197
- const epicId = 'epic-list-paged';
198
- const base = uniqueSlug('list-prds-paged');
199
- const slugs = [];
200
- for (let i = 0; i < 5; i++) {
201
- const slug = `${base}-${i}`;
202
- slugs.push(slug);
203
- await writePrdFixture({ cwd, epicId, slug });
204
- }
205
-
206
- const { admin, port, token } = await startAdmin();
207
- try {
208
- const qs = `cwd=${encodeURIComponent(cwd)}&epicId=${encodeURIComponent(epicId)}`;
209
- const page1 = await request(port, { path: `/admin/scheduler/prds?${qs}&limit=2&offset=0`, token });
210
- const page2 = await request(port, { path: `/admin/scheduler/prds?${qs}&limit=2&offset=2`, token });
211
- const page3 = await request(port, { path: `/admin/scheduler/prds?${qs}&limit=2&offset=4`, token });
212
-
213
- expect(page1.json.prds).toHaveLength(2);
214
- expect(page1.json.total).toBe(5);
215
- expect(page1.json.hasMore).toBe(true);
216
- expect(page2.json.prds).toHaveLength(2);
217
- expect(page2.json.hasMore).toBe(true);
218
- expect(page3.json.prds).toHaveLength(1);
219
- expect(page3.json.hasMore).toBe(false);
220
-
221
- const seenSlugs = [...page1.json.prds, ...page2.json.prds, ...page3.json.prds].map((p) => p.slug);
222
- expect(new Set(seenSlugs).size).toBe(5);
223
- const expectedOrder = [...seenSlugs].sort((a, b) => a.localeCompare(b, undefined, { numeric: true }));
224
- expect(seenSlugs).toEqual(expectedOrder);
225
- } finally {
226
- await admin.stop();
227
- }
228
- });
229
-
230
- test('GET /admin/scheduler/prds rejects limit over the hard max with a 400', async () => {
231
- const cwd = projectCwd();
232
- const { admin, port, token } = await startAdmin();
233
- try {
234
- const res = await request(port, { path: `/admin/scheduler/prds?cwd=${encodeURIComponent(cwd)}&limit=501`, token });
235
- expect(res.status).toBe(400);
236
- expect(res.json.ok).toBe(false);
237
- } finally {
238
- await admin.stop();
239
- }
240
- });
241
-
242
- test('GET /admin/scheduler/prds rejects a negative offset with a 400', async () => {
243
- const cwd = projectCwd();
244
- const { admin, port, token } = await startAdmin();
245
- try {
246
- const res = await request(port, { path: `/admin/scheduler/prds?cwd=${encodeURIComponent(cwd)}&offset=-1`, token });
247
- expect(res.status).toBe(400);
248
- expect(res.json.ok).toBe(false);
249
- } finally {
250
- await admin.stop();
251
- }
252
- });
253
-
254
- test('GET /admin/scheduler/prd?slug=&cwd= returns parsed frontmatter + body', async () => {
255
- const cwd = projectCwd();
256
- const epicId = 'epic-get';
257
- const slug = uniqueSlug('get-prd');
258
- await writePrdFixture({ cwd, epicId, slug });
259
-
260
- const { admin, port, token } = await startAdmin();
261
- try {
262
- const res = await request(port, { path: `/admin/scheduler/prd?slug=${slug}&cwd=${encodeURIComponent(cwd)}`, token });
263
- expect(res.status).toBe(200);
264
- expect(res.json.ok).toBe(true);
265
- expect(res.json.frontmatter.title).toBe('fixture title');
266
- expect(res.json.body).toMatch(/do the thing/);
267
- } finally {
268
- await admin.stop();
269
- }
270
- });
271
-
272
- test('GET /admin/scheduler/prd for a nonexistent slug returns 404', async () => {
273
- const cwd = projectCwd();
274
- const { admin, port, token } = await startAdmin();
275
- try {
276
- const res = await request(port, { path: `/admin/scheduler/prd?slug=${uniqueSlug('nope')}&cwd=${encodeURIComponent(cwd)}`, token });
277
- expect(res.status).toBe(404);
278
- expect(res.json.ok).toBe(false);
279
- } finally {
280
- await admin.stop();
281
- }
282
- });
283
-
284
- test('POST /admin/scheduler/update-prd edits a not-yet-queued PRD and preserves unrecognized frontmatter keys', async () => {
285
- const cwd = projectCwd();
286
- const epicId = 'epic-update';
287
- const slug = uniqueSlug('update-prd');
288
- await writePrdFixture({ cwd, epicId, slug, extra: 'dependsOn: [some-other-slug]\n' });
289
-
290
- const { admin, port, token } = await startAdmin();
291
- try {
292
- const res = await request(port, {
293
- method: 'POST', path: '/admin/scheduler/update-prd', token,
294
- body: { slug, cwd, frontmatter: { title: 'edited title', estimateMinutes: 25 } },
295
- });
296
- expect(res.status).toBe(200);
297
- expect(res.json.ok).toBe(true);
298
-
299
- const raw = await fsp.readFile(path.join(cwd, 'session-manager-operations', 'scheduler', 'epics', epicId, 'prds', `${slug}.md`), 'utf8');
300
- expect(raw).toMatch(/title: edited title/);
301
- expect(raw).toMatch(/estimateMinutes: 25/);
302
- // Unrecognized key round-trips verbatim.
303
- expect(raw).toMatch(/dependsOn: \[some-other-slug\]/);
304
- } finally {
305
- await admin.stop();
306
- }
307
- });
308
-
309
- test('POST /admin/scheduler/update-prd refuses a slug whose job is running, naming the current status', async () => {
310
- const cwd = projectCwd();
311
- const epicId = 'epic-update-running';
312
- const slug = uniqueSlug('update-prd-running');
313
- await writePrdFixture({ cwd, epicId, slug });
314
-
315
- await scheduler.writeQueue({
316
- jobs: [{ slug, title: 'x', cwd, status: 'running', runtime: {} }],
317
- config: {},
318
- paused: null,
319
- });
320
-
321
- const { admin, port, token } = await startAdmin();
322
- try {
323
- const res = await request(port, {
324
- method: 'POST', path: '/admin/scheduler/update-prd', token,
325
- body: { slug, cwd, frontmatter: { title: 'should not land' } },
326
- });
327
- expect(res.status).toBe(409);
328
- expect(res.json.ok).toBe(false);
329
- expect(res.json.error).toMatch(/running/);
330
- } finally {
331
- await admin.stop();
332
- await scheduler.writeQueue({ jobs: [], config: {}, paused: null });
333
- }
334
- });
335
-
336
- test('POST /admin/scheduler/cancel-job cancels a pending job (no cancelled status exists — lands as failed)', async () => {
337
- const cwd = projectCwd();
338
- const slug = uniqueSlug('cancel-pending');
339
- await scheduler.writeQueue({
340
- jobs: [{ slug, title: 'x', cwd, status: 'pending' }],
341
- config: {},
342
- paused: null,
343
- });
344
-
345
- const { admin, port, token } = await startAdmin();
346
- try {
347
- const res = await request(port, { method: 'POST', path: '/admin/scheduler/cancel-job', token, body: { slug } });
348
- expect(res.status).toBe(200);
349
- expect(res.json.ok).toBe(true);
350
- expect(res.json.status).toBe('failed');
351
- expect(res.json.wasRunning).toBe(false);
352
- } finally {
353
- await admin.stop();
354
- await scheduler.writeQueue({ jobs: [], config: {}, paused: null });
355
- }
356
- });
357
-
358
- test('POST /admin/scheduler/cancel-job refuses an already-terminal job', async () => {
359
- const cwd = projectCwd();
360
- const slug = uniqueSlug('cancel-terminal');
361
- await scheduler.writeQueue({
362
- jobs: [{ slug, title: 'x', cwd, status: 'completed' }],
363
- config: {},
364
- paused: null,
365
- });
366
-
367
- const { admin, port, token } = await startAdmin();
368
- try {
369
- const res = await request(port, { method: 'POST', path: '/admin/scheduler/cancel-job', token, body: { slug } });
370
- expect(res.status).toBe(409);
371
- expect(res.json.ok).toBe(false);
372
- expect(res.json.error).toMatch(/terminal/);
373
- } finally {
374
- await admin.stop();
375
- await scheduler.writeQueue({ jobs: [], config: {}, paused: null });
376
- }
377
- });
378
-
379
- test('POST /admin/scheduler/archive-prd moves a PRD to prds-archived/', async () => {
380
- const cwd = projectCwd();
381
- const epicId = 'epic-archive';
382
- const slug = uniqueSlug('archive-prd');
383
- const filePath = await writePrdFixture({ cwd, epicId, slug });
384
-
385
- const { admin, port, token } = await startAdmin();
386
- try {
387
- const res = await request(port, { method: 'POST', path: '/admin/scheduler/archive-prd', token, body: { slugs: [slug] } });
388
- expect(res.status).toBe(200);
389
- expect(res.json.ok).toBe(true);
390
- expect(res.json.archived).toBe(1);
391
- expect(fs.existsSync(filePath)).toBe(false);
392
- } finally {
393
- await admin.stop();
394
- }
395
- });
396
-
397
- // ─────────────────────────────────────── invalid-slug vs unknown-slug split
398
- //
399
- // PRD: safeSlugPath used to collapse "malformed slug" and "well-formed slug
400
- // found nowhere" into the same `null` -> "invalid slug" response, so an
401
- // agent that typed a real slug for a PRD in the wrong project (or a since-
402
- // archived one) read the error as "I typed it wrong" and retried instead of
403
- // listing PRDs. These tests pin the split for all three verbs plus the new
404
- // optional `cwd` narrowing.
405
-
406
- test('POST /admin/scheduler/reset-job with a malformed slug returns "invalid slug"', async () => {
407
- const { admin, port, token } = await startAdmin();
408
- try {
409
- const res = await request(port, {
410
- method: 'POST', path: '/admin/scheduler/reset-job', token, body: { slug: 'not a slug!!' },
411
- });
412
- expect(res.status).toBe(200);
413
- expect(res.json.ok).toBe(false);
414
- expect(res.json.error).toBe('invalid slug');
415
- } finally {
416
- await admin.stop();
417
- }
418
- });
419
-
420
- test('POST /admin/scheduler/reset-job with a well-formed slug that exists nowhere returns a distinct unknown-slug error', async () => {
421
- const { admin, port, token } = await startAdmin();
422
- try {
423
- const slug = uniqueSlug('definitely-not-a-real-prd');
424
- const res = await request(port, {
425
- method: 'POST', path: '/admin/scheduler/reset-job', token, body: { slug },
426
- });
427
- expect(res.status).toBe(200);
428
- expect(res.json.ok).toBe(false);
429
- expect(res.json.error).not.toBe('invalid slug');
430
- expect(res.json.error).toMatch(/unknown slug/);
431
- expect(res.json.error).toMatch(/scheduler_list_prds/);
432
- } finally {
433
- await admin.stop();
434
- }
435
- });
436
-
437
- test('POST /admin/scheduler/reset-job with a cwd naming a project with no PRD dir yields the unknown-slug error, not a crash', async () => {
438
- const cwd = projectCwd();
439
- const epicId = 'epic-reset-cwd-miss';
440
- const slug = uniqueSlug('reset-cwd-miss');
441
- await writePrdFixture({ cwd, epicId, slug }); // exists under `cwd`, not under `otherCwd`
442
-
443
- const otherCwd = path.join(tmpHome, 'fake-project-cwd-empty');
444
- fs.mkdirSync(otherCwd, { recursive: true });
445
-
446
- const { admin, port, token } = await startAdmin();
447
- try {
448
- const res = await request(port, {
449
- method: 'POST', path: '/admin/scheduler/reset-job', token, body: { slug, cwd: otherCwd },
450
- });
451
- expect(res.status).toBe(200);
452
- expect(res.json.ok).toBe(false);
453
- expect(res.json.error).toMatch(/unknown slug/);
454
- } finally {
455
- await admin.stop();
456
- }
457
- });
458
-
459
- test('POST /admin/scheduler/reset-job with a matching cwd resets a known job', async () => {
460
- const cwd = projectCwd();
461
- const epicId = 'epic-reset-cwd-hit';
462
- const slug = uniqueSlug('reset-cwd-hit');
463
- await writePrdFixture({ cwd, epicId, slug });
464
- await scheduler.writeQueue({
465
- jobs: [{ slug, title: 'x', cwd, status: 'needs_review' }],
466
- config: {},
467
- paused: null,
468
- });
469
-
470
- const { admin, port, token } = await startAdmin();
471
- try {
472
- const res = await request(port, {
473
- method: 'POST', path: '/admin/scheduler/reset-job', token, body: { slug, cwd },
474
- });
475
- expect(res.status).toBe(200);
476
- expect(res.json).toEqual({ ok: true, slug, status: 'pending' });
477
- } finally {
478
- await admin.stop();
479
- await scheduler.writeQueue({ jobs: [], config: {}, paused: null });
480
- }
481
- });
482
-
483
- test('POST /admin/scheduler/reset-job with cwd resets the SAME-slug job belonging to that project, not a same-slug job from another project (cwd narrows the job lookup, not just the file lookup)', async () => {
484
- // Slugs derive from title text with no cwd salt, so two different projects
485
- // can legitimately queue an identically-slugged job. `cwd` must disambiguate
486
- // which one gets reset, not just which PRD file gets read for existence.
487
- const cwdA = projectCwd();
488
- const cwdB = path.join(tmpHome, 'fake-project-cwd-collision-b');
489
- fs.mkdirSync(cwdB, { recursive: true });
490
- config.addAllowedRoot(cwdB);
491
- // queueStore.cjs's readMerged only reads back a project's shard if that
492
- // project's cwd is discoverable via allProjectCwds (scans ~/.claude/projects/
493
- // *.jsonl for a `cwd` field) — same fake-transcript trick beforeAll used for
494
- // the shared fixture cwd.
495
- const projDirB = path.join(tmpHome, '.claude', 'projects', 'fake-project-slug-b');
496
- fs.mkdirSync(projDirB, { recursive: true });
497
- fs.writeFileSync(path.join(projDirB, 'session.jsonl'), `${JSON.stringify({ cwd: cwdB })}\n`, 'utf8');
498
- const { bustCwdCache } = require('../lib/queueStore.cjs');
499
- bustCwdCache();
500
- const slug = uniqueSlug('collision-slug');
501
- const epicId = 'epic-reset-collision';
502
- // Both projects' rows need a real PRD source — a 'needs_review' row whose
503
- // PRD is invisible to reconcile()'s onDisk scan is silently dropped (never
504
- // backfilled to history.jsonl the way a completed/failed/skipped orphan
505
- // is), which would make jobA vanish for a reason unrelated to what this
506
- // test actually verifies (cwd-scoped job lookup).
507
- await writePrdFixture({ cwd: cwdA, epicId, slug });
508
- await writePrdFixture({ cwd: cwdB, epicId, slug });
509
- await scheduler.writeQueue({
510
- jobs: [
511
- { slug, title: 'project A job', cwd: cwdA, status: 'needs_review' },
512
- { slug, title: 'project B job', cwd: cwdB, status: 'needs_review' },
513
- ],
514
- config: {},
515
- paused: null,
516
- });
517
-
518
- const { admin, port, token } = await startAdmin();
519
- try {
520
- const res = await request(port, {
521
- method: 'POST', path: '/admin/scheduler/reset-job', token, body: { slug, cwd: cwdB },
522
- });
523
- expect(res.status).toBe(200);
524
- expect(res.json).toEqual({ ok: true, slug, status: 'pending' });
525
-
526
- const state = await scheduler.remote.listJobs();
527
- // Filter by slug too, not just cwd: cwdA is a shared fixture directory
528
- // reused by many tests in this file, each of which leaves its own
529
- // never-archived PRD file behind (only the queue row is cleared in
530
- // `finally`) — reconcile()'s fresh-discovery pass can quarantine one of
531
- // those leftovers as a NEW row under this same cwd on any later pass, so
532
- // `cwd` alone no longer uniquely identifies this test's own job.
533
- const jobA = state.find((j) => j.cwd === cwdA && j.slug === slug);
534
- const jobB = state.find((j) => j.cwd === cwdB && j.slug === slug);
535
- expect(jobA.status).toBe('needs_review'); // untouched
536
- expect(jobB.status).toBe('pending'); // the one actually targeted
537
- } finally {
538
- await admin.stop();
539
- await scheduler.writeQueue({ jobs: [], config: {}, paused: null });
540
- }
541
- });
542
-
543
- test('POST /admin/scheduler/cancel-job with a malformed slug is rejected at the request-schema layer (400) — never reaches "not found"', async () => {
544
- const { admin, port, token } = await startAdmin();
545
- try {
546
- const res = await request(port, {
547
- method: 'POST', path: '/admin/scheduler/cancel-job', token, body: { slug: 'not a slug!!' },
548
- });
549
- expect(res.status).toBe(400);
550
- expect(res.json.ok).toBe(false);
551
- expect(res.json.error).not.toMatch(/unknown slug|not found/i);
552
- } finally {
553
- await admin.stop();
554
- }
555
- });
556
-
557
- test('remote.cancelJob itself (bypassing the HTTP schema layer) returns "invalid slug" for a malformed slug, distinct from "unknown slug"', async () => {
558
- const malformed = await scheduler.remote.cancelJob('not a slug!!');
559
- expect(malformed).toEqual({ ok: false, error: 'invalid slug' });
560
-
561
- const wellFormedMissing = await scheduler.remote.cancelJob(uniqueSlug('cancel-remote-missing'));
562
- expect(wellFormedMissing.ok).toBe(false);
563
- expect(wellFormedMissing.error).not.toBe('invalid slug');
564
- expect(wellFormedMissing.error).toMatch(/unknown slug/);
565
- });
566
-
567
- test('POST /admin/scheduler/cancel-job with a well-formed slug that has no queued job returns a distinct unknown-slug error (404)', async () => {
568
- const { admin, port, token } = await startAdmin();
569
- try {
570
- const slug = uniqueSlug('cancel-missing');
571
- const res = await request(port, {
572
- method: 'POST', path: '/admin/scheduler/cancel-job', token, body: { slug },
573
- });
574
- expect(res.status).toBe(404);
575
- expect(res.json.ok).toBe(false);
576
- expect(res.json.error).not.toBe('invalid slug');
577
- expect(res.json.error).toMatch(/unknown slug/);
578
- } finally {
579
- await admin.stop();
580
- }
581
- });
582
-
583
- test('POST /admin/scheduler/cancel-job with cwd narrows job lookup to that project', async () => {
584
- const cwd = projectCwd();
585
- const slug = uniqueSlug('cancel-cwd-scope');
586
- await scheduler.writeQueue({
587
- jobs: [{ slug, title: 'x', cwd, status: 'pending' }],
588
- config: {},
589
- paused: null,
590
- });
591
- const otherCwd = path.join(tmpHome, 'fake-project-cwd-cancel-miss');
592
- fs.mkdirSync(otherCwd, { recursive: true });
593
-
594
- const { admin, port, token } = await startAdmin();
595
- try {
596
- const missRes = await request(port, {
597
- method: 'POST', path: '/admin/scheduler/cancel-job', token, body: { slug, cwd: otherCwd },
598
- });
599
- expect(missRes.status).toBe(404);
600
- expect(missRes.json.ok).toBe(false);
601
-
602
- const hitRes = await request(port, {
603
- method: 'POST', path: '/admin/scheduler/cancel-job', token, body: { slug, cwd },
604
- });
605
- expect(hitRes.status).toBe(200);
606
- expect(hitRes.json.ok).toBe(true);
607
- } finally {
608
- await admin.stop();
609
- await scheduler.writeQueue({ jobs: [], config: {}, paused: null });
610
- }
611
- });
612
-
613
- test('POST /admin/scheduler/archive-prd with a malformed slug is rejected at the request-schema layer (400) — never reaches "not found"', async () => {
614
- const { admin, port, token } = await startAdmin();
615
- try {
616
- const res = await request(port, {
617
- method: 'POST', path: '/admin/scheduler/archive-prd', token, body: { slugs: ['not a slug!!'] },
618
- });
619
- expect(res.status).toBe(400);
620
- expect(res.json.ok).toBe(false);
621
- } finally {
622
- await admin.stop();
623
- }
624
- });
625
-
626
- test('queueOps.archiveMany itself (bypassing the HTTP schema layer) splits "invalid slug" from "not found in any PRDs dir"', async () => {
627
- const queueOps = require('../queueOps.cjs');
628
- const malformed = await queueOps.archiveMany(['not a slug!!']);
629
- expect(malformed.results[0]).toMatchObject({ ok: false, error: 'invalid slug' });
630
-
631
- const missing = await queueOps.archiveMany([uniqueSlug('archive-remote-missing')]);
632
- expect(missing.results[0]).toMatchObject({ ok: false, error: 'not found in any PRDs dir' });
633
- });
634
-
635
- test('POST /admin/scheduler/archive-prd with a well-formed slug found nowhere returns a distinct not-found error', async () => {
636
- const { admin, port, token } = await startAdmin();
637
- try {
638
- const slug = uniqueSlug('archive-missing');
639
- const res = await request(port, {
640
- method: 'POST', path: '/admin/scheduler/archive-prd', token, body: { slugs: [slug] },
641
- });
642
- expect(res.status).toBe(200);
643
- expect(res.json.results[0].ok).toBe(false);
644
- expect(res.json.results[0].error).toBe('not found in any PRDs dir');
645
- expect(res.json.results[0].error).not.toBe('invalid slug');
646
- } finally {
647
- await admin.stop();
648
- }
649
- });
650
-
651
- test('POST /admin/scheduler/archive-prd with cwd narrows the search: a cwd with no PRD dir misses, the real cwd hits', async () => {
652
- const cwd = projectCwd();
653
- const epicId = 'epic-archive-cwd';
654
- const slug = uniqueSlug('archive-cwd-scope');
655
- const filePath = await writePrdFixture({ cwd, epicId, slug });
656
-
657
- const otherCwd = path.join(tmpHome, 'fake-project-cwd-archive-miss');
658
- fs.mkdirSync(otherCwd, { recursive: true });
659
-
660
- const { admin, port, token } = await startAdmin();
661
- try {
662
- const missRes = await request(port, {
663
- method: 'POST', path: '/admin/scheduler/archive-prd', token, body: { slugs: [slug], cwd: otherCwd },
664
- });
665
- expect(missRes.json.results[0].ok).toBe(false);
666
- expect(missRes.json.results[0].error).toBe('not found in any PRDs dir');
667
- expect(fs.existsSync(filePath)).toBe(true);
668
-
669
- const hitRes = await request(port, {
670
- method: 'POST', path: '/admin/scheduler/archive-prd', token, body: { slugs: [slug], cwd },
671
- });
672
- expect(hitRes.json.results[0].ok).toBe(true);
673
- expect(fs.existsSync(filePath)).toBe(false);
674
- } finally {
675
- await admin.stop();
676
- }
677
- });
678
-
679
- test('GET /admin/mcp/readiness requires cwd and returns the delegation-readiness checks for it', async () => {
680
- const cwd = projectCwd();
681
- const { admin, port, token } = await startAdmin();
682
- try {
683
- const missing = await request(port, { path: '/admin/mcp/readiness', token });
684
- expect(missing.status).toBe(400);
685
- expect(missing.json.ok).toBe(false);
686
-
687
- const res = await request(port, { path: `/admin/mcp/readiness?cwd=${encodeURIComponent(cwd)}`, token });
688
- expect(res.status).toBe(200);
689
- expect(res.json.ok).toBe(true);
690
- expect(typeof res.json.ready).toBe('boolean');
691
- expect(Array.isArray(res.json.checks)).toBe(true);
692
- expect(res.json.checks.some((c) => c.id === 'scheduler-mcp')).toBe(true);
693
- } finally {
694
- await admin.stop();
695
- }
696
- });
697
-
698
- test('POST /admin/scheduler/retag-prd rewrites estimateMinutes frontmatter', async () => {
699
- const cwd = projectCwd();
700
- const epicId = 'epic-retag';
701
- const slug = uniqueSlug('retag-prd');
702
- const filePath = await writePrdFixture({ cwd, epicId, slug });
703
-
704
- const { admin, port, token } = await startAdmin();
705
- try {
706
- const res = await request(port, {
707
- method: 'POST', path: '/admin/scheduler/retag-prd', token,
708
- body: { items: [{ slug, estimateMinutes: 42 }] },
709
- });
710
- expect(res.status).toBe(200);
711
- expect(res.json.ok).toBe(true);
712
- expect(res.json.retagged).toBe(1);
713
- const raw = await fsp.readFile(filePath, 'utf8');
714
- expect(raw).toMatch(/estimateMinutes: 42/);
715
- } finally {
716
- await admin.stop();
717
- }
718
- });