claude-code-session-manager 0.97.0 → 0.100.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (430) 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-D93FoaOm.js → DataModel-HdR0AxFq.js} +1 -1
  5. package/dist/assets/{History-CXxxqNP1.js → History-Dr4Jr_5z.js} +2 -2
  6. package/dist/assets/{Hooks-CzeYFQjq.js → Hooks-NhrvH3JP.js} +3 -3
  7. package/dist/assets/{Library-D2zPkwOC.js → Library-Dv_EtIMr.js} +1 -1
  8. package/dist/assets/{MarkdownEditor-DbinP78A.js → MarkdownEditor-DTKDWV_n.js} +1 -1
  9. package/dist/assets/{McpServers-Bu_sVGLr.js → McpServers-M034G9B3.js} +2 -2
  10. package/dist/assets/{Memory-BgiI6LZ_.js → Memory-DzF1B4gx.js} +6 -6
  11. package/dist/assets/{Permissions-C1MMzZRm.js → Permissions-DrgbSGWl.js} +3 -3
  12. package/dist/assets/{Plugins-Dhu6wAZi.js → Plugins-Bd3ef35w.js} +2 -2
  13. package/dist/assets/{ProvenanceBadge-C3AcWKMG.js → ProvenanceBadge-DcBHT1Iy.js} +1 -1
  14. package/dist/assets/{SaveBar-plwYm0iO.js → SaveBar-rimJem-Y.js} +1 -1
  15. package/dist/assets/Scheduler-DjgFJl2w.js +16 -0
  16. package/dist/assets/{ScopeSwitcher-DFy0vv_o.js → ScopeSwitcher-CnAzTBrl.js} +1 -1
  17. package/dist/assets/Settings-Dc1F_sSg.js +3 -0
  18. package/dist/assets/{SkillReferenceGraph-DsI7hRPB.js → SkillReferenceGraph-B9aZDecM.js} +1 -1
  19. package/dist/assets/{Skills-3E748UfB.js → Skills-DRXd18gu.js} +2 -2
  20. package/dist/assets/{SystemPrompt-BqmfUJOd.js → SystemPrompt-ahy5-Fwe.js} +1 -1
  21. package/dist/assets/{TagLibrary-CPz6fO8Q.js → TagLibrary-C_tToDeL.js} +1 -1
  22. package/dist/assets/{TiptapBody-D9EoBQ1P.js → TiptapBody-DvdefS6K.js} +1 -1
  23. package/dist/assets/{Toggle-BHAmZNDn.js → Toggle-6Jl5Njc6.js} +1 -1
  24. package/dist/assets/{index-CKH5Uxik.css → index-Bta-hwud.css} +1 -1
  25. package/dist/assets/{index-tn5JIVPj.js → index-vx8O73l8.js} +497 -502
  26. package/dist/assets/{settingsSchema-Crqy_aNz.js → settingsSchema-D6iRguNV.js} +1 -1
  27. package/dist/index.html +2 -2
  28. package/package.json +26 -29
  29. package/plugins/CLAUDE.md +6 -6
  30. package/plugins/session-manager-dev/skills/builder/4-manual/SKILL.md +23 -19
  31. package/plugins/session-manager-dev/skills/builder/SKILL.md +3 -3
  32. package/plugins/session-manager-dev/skills/develop/SKILL.md +254 -510
  33. package/plugins/session-manager-dev/skills/develop/standards.md +17 -22
  34. package/scripts/README.md +3 -11
  35. package/scripts/audit-ops-hygiene.cjs +3 -3
  36. package/scripts/hooks/lib/guard-prd-writes-policy.cjs +1 -1
  37. package/scripts/hooks/lib/guard-self-schedule-policy.cjs +1 -1
  38. package/scripts/mint-epic.cjs +2 -3
  39. package/scripts/scheduler-mcp-server.cjs +36 -7
  40. package/src/main/agentLibrary.cjs +22 -2
  41. package/src/main/build-info.json +4 -4
  42. package/src/main/config.cjs +41 -38
  43. package/src/main/docEdit.cjs +4 -1
  44. package/src/main/files.cjs +2 -5
  45. package/src/main/health.cjs +1 -1
  46. package/src/main/index.cjs +23 -10
  47. package/src/main/ipcSchemas.cjs +28 -35
  48. package/src/main/lib/agentPersonaSchema.cjs +13 -2
  49. package/src/main/lib/atomicFs.cjs +116 -0
  50. package/src/main/lib/branchSweep.cjs +13 -12
  51. package/src/main/lib/buildTarget.cjs +3 -3
  52. package/src/main/lib/claudeCliCaps.cjs +129 -0
  53. package/src/main/lib/credentials.cjs +2 -4
  54. package/src/main/lib/crossProjectFeedback.cjs +3 -3
  55. package/src/main/lib/cwdClassify.cjs +15 -3
  56. package/src/main/lib/definitionOfDone.cjs +207 -125
  57. package/src/main/lib/dodDrainHook.cjs +11 -5
  58. package/src/main/lib/epicMint.cjs +2 -4
  59. package/src/main/lib/epicStatusMirror.cjs +8 -10
  60. package/src/main/lib/epicWorktreeProjectConfig.cjs +2 -4
  61. package/src/main/lib/gateAuthority.cjs +96 -0
  62. package/src/main/lib/gitExec.cjs +69 -0
  63. package/src/main/lib/gitWorktree.cjs +656 -92
  64. package/src/main/lib/instanceLock.cjs +5 -8
  65. package/src/main/lib/launchFailure.cjs +2 -3
  66. package/src/main/lib/macroLibrary.cjs +386 -0
  67. package/src/main/lib/mcpToolCatalog.cjs +26 -17
  68. package/src/main/lib/needsReviewLedger.cjs +1 -1
  69. package/src/main/lib/opsErrorLog.cjs +11 -1
  70. package/src/main/lib/opsOwnership.cjs +0 -5
  71. package/src/main/lib/pidAlive.cjs +32 -0
  72. package/src/main/lib/prdCreate.cjs +153 -19
  73. package/src/main/lib/prdDisposition.cjs +2 -2
  74. package/src/main/lib/prdGateFiles.cjs +284 -0
  75. package/src/main/lib/prdLocations.cjs +61 -0
  76. package/src/main/lib/prdMigration.cjs +2 -3
  77. package/src/main/lib/prdSizing.cjs +2 -1
  78. package/src/main/lib/promptSessionSchema.cjs +10 -2
  79. package/src/main/lib/rcaReport.cjs +4 -6
  80. package/src/main/lib/reaperHelpers.cjs +8 -1
  81. package/src/main/lib/reservationExpiry.cjs +6 -1
  82. package/src/main/lib/reviewNotice.cjs +263 -0
  83. package/src/main/lib/runClaudeP.cjs +4 -1
  84. package/src/main/lib/schedulerPaths.cjs +22 -2
  85. package/src/main/lib/sessionSlots.cjs +2 -4
  86. package/src/main/lib/shippedPersonaSeeds.cjs +179 -0
  87. package/src/main/lib/timeoutShim.cjs +123 -0
  88. package/src/main/lib/timeoutShimScript.cjs +275 -0
  89. package/src/main/lib/upgradeDrain.cjs +4 -10
  90. package/src/main/lib/watchdogHelpers.cjs +33 -30
  91. package/src/main/lib/workTypeLibrary.cjs +7 -1
  92. package/src/main/promptSessionEvents.cjs +37 -13
  93. package/src/main/queueOps.cjs +17 -6
  94. package/src/main/scheduler.cjs +975 -203
  95. package/src/main/seedAgentPersonas.cjs +278 -23
  96. package/src/main/supervisor.cjs +4 -1
  97. package/src/main/templates/PRD_AUTHORING.md +126 -406
  98. package/src/preload/__tests__/preload-surface.test.cjs +68 -0
  99. package/src/preload/api.d.ts +41 -94
  100. package/src/preload/index.cjs +14 -9
  101. package/src/seed/agents/architect.md +1 -0
  102. package/src/seed/agents/dev-lead.md +17 -18
  103. package/src/seed/agents/project-home-builder.md +1 -0
  104. package/src/seed/agents/validator.md +10 -4
  105. package/dist/assets/HostBilko-nESrTRIg.js +0 -1
  106. package/dist/assets/Scheduler-BVYTeh39.js +0 -16
  107. package/dist/assets/Settings-DIhGgdfs.js +0 -3
  108. package/src/main/__tests__/activeIndexMerge.test.cjs +0 -235
  109. package/src/main/__tests__/agentEffortResolve.test.cjs +0 -117
  110. package/src/main/__tests__/agentLibrary.test.cjs +0 -276
  111. package/src/main/__tests__/agentModelResolve.test.cjs +0 -311
  112. package/src/main/__tests__/agentOverlayWrite.test.cjs +0 -95
  113. package/src/main/__tests__/bilkoHost-deriveSlug.test.cjs +0 -26
  114. package/src/main/__tests__/bilkoHost-integration.test.cjs +0 -118
  115. package/src/main/__tests__/bilkoHostCore.test.cjs +0 -72
  116. package/src/main/__tests__/broadcastCoalescer.test.cjs +0 -122
  117. package/src/main/__tests__/chat-cancel-terminal.test.cjs +0 -120
  118. package/src/main/__tests__/chat-dead-channels.test.cjs +0 -63
  119. package/src/main/__tests__/chat-exit-close-race.test.cjs +0 -146
  120. package/src/main/__tests__/chat-mcp-consent-notice.test.cjs +0 -139
  121. package/src/main/__tests__/chat-preamble-anchors.test.cjs +0 -100
  122. package/src/main/__tests__/chat-queue.test.cjs +0 -97
  123. package/src/main/__tests__/chat-stop-signal.test.cjs +0 -89
  124. package/src/main/__tests__/chatRunner-epic-worktree-execcwd.test.cjs +0 -125
  125. package/src/main/__tests__/chatRunner-session-flag-retry.test.cjs +0 -252
  126. package/src/main/__tests__/classifyPromptTicket.test.cjs +0 -101
  127. package/src/main/__tests__/classifyTranscriptLine.test.cjs +0 -201
  128. package/src/main/__tests__/computeDepHistorySatisfaction.test.cjs +0 -66
  129. package/src/main/__tests__/config-readText-bounded.test.cjs +0 -84
  130. package/src/main/__tests__/configWriteBoundaryOwners.test.cjs +0 -59
  131. package/src/main/__tests__/crossProjectFeedback.test.cjs +0 -334
  132. package/src/main/__tests__/crossProjectFeedbackRoutes.test.cjs +0 -161
  133. package/src/main/__tests__/dep-orphan-archive-health.test.cjs +0 -77
  134. package/src/main/__tests__/develop-skill-failure-modes.test.cjs +0 -70
  135. package/src/main/__tests__/docEdit.test.cjs +0 -244
  136. package/src/main/__tests__/dod-batchkey.test.cjs +0 -183
  137. package/src/main/__tests__/dod-drain-hook.test.cjs +0 -302
  138. package/src/main/__tests__/dod-report.test.cjs +0 -304
  139. package/src/main/__tests__/dod-reverify.test.cjs +0 -285
  140. package/src/main/__tests__/epicContextDigest.test.cjs +0 -174
  141. package/src/main/__tests__/epicMint.test.cjs +0 -332
  142. package/src/main/__tests__/epicMintTelemetryTap.test.cjs +0 -64
  143. package/src/main/__tests__/epicStatusMirror.test.cjs +0 -110
  144. package/src/main/__tests__/epicValidationHook.test.cjs +0 -291
  145. package/src/main/__tests__/exchanges.test.cjs +0 -122
  146. package/src/main/__tests__/exchangesPromptId.test.cjs +0 -61
  147. package/src/main/__tests__/extractJson.test.cjs +0 -51
  148. package/src/main/__tests__/files-reject-credentials.test.cjs +0 -40
  149. package/src/main/__tests__/fixtures/1218-fo-01-move-scripts-lib-into-src-main-lib.log +0 -556
  150. package/src/main/__tests__/flatPrdTickSweep.test.cjs +0 -110
  151. package/src/main/__tests__/health-build-freshness.test.cjs +0 -39
  152. package/src/main/__tests__/health-claude-md-budget.test.cjs +0 -57
  153. package/src/main/__tests__/health-credentials.test.cjs +0 -81
  154. package/src/main/__tests__/health-delegation-chain.test.cjs +0 -124
  155. package/src/main/__tests__/health-per-project-stall.test.cjs +0 -84
  156. package/src/main/__tests__/health-prd-migration.test.cjs +0 -37
  157. package/src/main/__tests__/health-queue-dispatch.test.cjs +0 -135
  158. package/src/main/__tests__/health-starve-escalation.test.cjs +0 -94
  159. package/src/main/__tests__/health-tick-liveness.test.cjs +0 -179
  160. package/src/main/__tests__/health-usage-poller.test.cjs +0 -144
  161. package/src/main/__tests__/health-worktree-cap-blocked.test.cjs +0 -65
  162. package/src/main/__tests__/heapSnapshot.test.cjs +0 -121
  163. package/src/main/__tests__/historyAggregatorIntraday.test.cjs +0 -313
  164. package/src/main/__tests__/historyDashboard.test.cjs +0 -163
  165. package/src/main/__tests__/historyRollup.test.cjs +0 -333
  166. package/src/main/__tests__/intradayRefresh.test.cjs +0 -39
  167. package/src/main/__tests__/ipcSchemas-dependsOn.test.cjs +0 -39
  168. package/src/main/__tests__/kg-augment.test.cjs +0 -195
  169. package/src/main/__tests__/loadGateDetailTick.test.cjs +0 -31
  170. package/src/main/__tests__/machineProfile.test.cjs +0 -152
  171. package/src/main/__tests__/mcpStatus.test.cjs +0 -61
  172. package/src/main/__tests__/memoryAggregate.test.cjs +0 -109
  173. package/src/main/__tests__/memoryStale.test.cjs +0 -88
  174. package/src/main/__tests__/needsReviewLedger.test.cjs +0 -162
  175. package/src/main/__tests__/openExternalApp-spawn-error.test.cjs +0 -25
  176. package/src/main/__tests__/opsErrorLog.test.cjs +0 -109
  177. package/src/main/__tests__/opsErrorLogTelemetryTap.test.cjs +0 -173
  178. package/src/main/__tests__/personaMerge.test.cjs +0 -169
  179. package/src/main/__tests__/planValidator.test.cjs +0 -125
  180. package/src/main/__tests__/pollLoop-dispatch-on-failure.test.cjs +0 -176
  181. package/src/main/__tests__/prd-group-allocator.test.cjs +0 -119
  182. package/src/main/__tests__/prdAdminRouteParity.test.cjs +0 -70
  183. package/src/main/__tests__/prdAdminRoutes.test.cjs +0 -718
  184. package/src/main/__tests__/prdAgentType.test.cjs +0 -103
  185. package/src/main/__tests__/prdAuthoringSeed.test.cjs +0 -39
  186. package/src/main/__tests__/prdCreate.test.cjs +0 -979
  187. package/src/main/__tests__/prdCreateDisposition.test.cjs +0 -201
  188. package/src/main/__tests__/prdCreatePlanId.test.cjs +0 -132
  189. package/src/main/__tests__/prdFrontmatterAgentType.test.cjs +0 -117
  190. package/src/main/__tests__/prdFrontmatterDependsOn.test.cjs +0 -136
  191. package/src/main/__tests__/prdFrontmatterDisposition.test.cjs +0 -125
  192. package/src/main/__tests__/prdFrontmatterQuietMachine.test.cjs +0 -108
  193. package/src/main/__tests__/prdLocations.test.cjs +0 -195
  194. package/src/main/__tests__/prdLocationsArchived.test.cjs +0 -201
  195. package/src/main/__tests__/prdMigration.test.cjs +0 -349
  196. package/src/main/__tests__/prdMigrationLegacyAdopt.test.cjs +0 -91
  197. package/src/main/__tests__/prdParserHighWater.test.cjs +0 -74
  198. package/src/main/__tests__/prdParserSourcePromptId.test.cjs +0 -65
  199. package/src/main/__tests__/prdSetDisposition.test.cjs +0 -222
  200. package/src/main/__tests__/prdSizing.test.cjs +0 -106
  201. package/src/main/__tests__/prdSourcePromptIdBackfill.test.cjs +0 -118
  202. package/src/main/__tests__/prdUpdateDependsOn.test.cjs +0 -160
  203. package/src/main/__tests__/proc-role-env.test.cjs +0 -125
  204. package/src/main/__tests__/procname-claude-spawn-sites.test.cjs +0 -304
  205. package/src/main/__tests__/procname-sm-processes.test.cjs +0 -127
  206. package/src/main/__tests__/projectHomeAdminRoutes.test.cjs +0 -177
  207. package/src/main/__tests__/projectPages.test.cjs +0 -137
  208. package/src/main/__tests__/promptSessionEvents.test.cjs +0 -234
  209. package/src/main/__tests__/promptSessionSchema.test.cjs +0 -101
  210. package/src/main/__tests__/promptSessionTranscript.test.cjs +0 -0
  211. package/src/main/__tests__/promptSessionsCreateEpicHandler.test.cjs +0 -159
  212. package/src/main/__tests__/pty-epic-worktree-spawn-cwd.test.cjs +0 -282
  213. package/src/main/__tests__/pty-session-open-telemetry.test.cjs +0 -96
  214. package/src/main/__tests__/pty-write-result.test.cjs +0 -46
  215. package/src/main/__tests__/queue-health-verdict.test.cjs +0 -170
  216. package/src/main/__tests__/queue-starvation-dispatch-driver.test.cjs +0 -286
  217. package/src/main/__tests__/queue-starvation-per-project.test.cjs +0 -147
  218. package/src/main/__tests__/queueHistory.test.cjs +0 -355
  219. package/src/main/__tests__/queueOps-interactive-ac-lint.test.cjs +0 -65
  220. package/src/main/__tests__/queueOpsArchiveDestination.test.cjs +0 -65
  221. package/src/main/__tests__/queueOpsAutoArchive.test.cjs +0 -154
  222. package/src/main/__tests__/rateLimitPollerStreak.test.cjs +0 -128
  223. package/src/main/__tests__/rcaReport.test.cjs +0 -266
  224. package/src/main/__tests__/reconcileFlatPrdSweep.test.cjs +0 -119
  225. package/src/main/__tests__/reconcileTiming.test.cjs +0 -135
  226. package/src/main/__tests__/runLogRetention.test.cjs +0 -489
  227. package/src/main/__tests__/runVerify-atomic-verdicts.test.cjs +0 -26
  228. package/src/main/__tests__/runVerify-blocked-by-foreign-wip.test.cjs +0 -58
  229. package/src/main/__tests__/runVerify-landed-commit-outranks.test.cjs +0 -191
  230. package/src/main/__tests__/runVerify-policy-denial.test.cjs +0 -89
  231. package/src/main/__tests__/runVerify-transcript-commit-evidence.test.cjs +0 -225
  232. package/src/main/__tests__/runVerify.test.cjs +0 -1784
  233. package/src/main/__tests__/scheduleJobSchema.test.cjs +0 -127
  234. package/src/main/__tests__/scheduleJobStatusDrift.test.cjs +0 -65
  235. package/src/main/__tests__/scheduleJobTransitions.test.cjs +0 -277
  236. package/src/main/__tests__/scheduleJobTransitionsGrep.test.cjs +0 -59
  237. package/src/main/__tests__/scheduleJobTransitionsTelemetryTap.test.cjs +0 -72
  238. package/src/main/__tests__/scheduler-admin-routes.test.cjs +0 -199
  239. package/src/main/__tests__/scheduler-adopted-run-supervision.test.cjs +0 -143
  240. package/src/main/__tests__/scheduler-already-satisfied-on-main.test.cjs +0 -105
  241. package/src/main/__tests__/scheduler-archive-completed-prd.test.cjs +0 -102
  242. package/src/main/__tests__/scheduler-archived-twin-guard.test.cjs +0 -155
  243. package/src/main/__tests__/scheduler-autofix-outcome.test.cjs +0 -188
  244. package/src/main/__tests__/scheduler-autofix-select.test.cjs +0 -439
  245. package/src/main/__tests__/scheduler-autopromote.test.cjs +0 -51
  246. package/src/main/__tests__/scheduler-bash-timeout-env.test.cjs +0 -101
  247. package/src/main/__tests__/scheduler-blocked-by-foreign-wip.test.cjs +0 -107
  248. package/src/main/__tests__/scheduler-boot-orphans.test.cjs +0 -153
  249. package/src/main/__tests__/scheduler-broadcast-reconcile.test.cjs +0 -121
  250. package/src/main/__tests__/scheduler-clear-queue-history.test.cjs +0 -134
  251. package/src/main/__tests__/scheduler-commit-guard-noop.test.cjs +0 -244
  252. package/src/main/__tests__/scheduler-committed-in-window.test.cjs +0 -182
  253. package/src/main/__tests__/scheduler-cross-project-batch.test.cjs +0 -43
  254. package/src/main/__tests__/scheduler-default-eligible-heal.test.cjs +0 -168
  255. package/src/main/__tests__/scheduler-dispatch-loop.test.cjs +0 -58
  256. package/src/main/__tests__/scheduler-effective-concurrency.test.cjs +0 -81
  257. package/src/main/__tests__/scheduler-epic-digest.test.cjs +0 -227
  258. package/src/main/__tests__/scheduler-failed-autoreset.test.cjs +0 -121
  259. package/src/main/__tests__/scheduler-finalize-dispatch-guards.test.cjs +0 -229
  260. package/src/main/__tests__/scheduler-find-prd-dir.test.cjs +0 -75
  261. package/src/main/__tests__/scheduler-fix-plan-path.test.cjs +0 -119
  262. package/src/main/__tests__/scheduler-force-tick-outcome.test.cjs +0 -46
  263. package/src/main/__tests__/scheduler-foreign-wip-manifest.test.cjs +0 -78
  264. package/src/main/__tests__/scheduler-gate-shadow.test.cjs +0 -119
  265. package/src/main/__tests__/scheduler-guard-verdict-autoresolve.test.cjs +0 -390
  266. package/src/main/__tests__/scheduler-heal-refusal.test.cjs +0 -61
  267. package/src/main/__tests__/scheduler-heartbeat-payload.test.cjs +0 -80
  268. package/src/main/__tests__/scheduler-inplace-salvage.test.cjs +0 -323
  269. package/src/main/__tests__/scheduler-integration-failure-stamp.test.cjs +0 -41
  270. package/src/main/__tests__/scheduler-investigation-clean-skip.test.cjs +0 -63
  271. package/src/main/__tests__/scheduler-investigation-prompt.test.cjs +0 -123
  272. package/src/main/__tests__/scheduler-job-budget.test.cjs +0 -172
  273. package/src/main/__tests__/scheduler-job-overrun.test.cjs +0 -175
  274. package/src/main/__tests__/scheduler-launch-failure.test.cjs +0 -199
  275. package/src/main/__tests__/scheduler-leftover-fields.test.cjs +0 -52
  276. package/src/main/__tests__/scheduler-leftover-quarantine.test.cjs +0 -199
  277. package/src/main/__tests__/scheduler-looks-done.test.cjs +0 -537
  278. package/src/main/__tests__/scheduler-manual-pause.test.cjs +0 -118
  279. package/src/main/__tests__/scheduler-mechanical-recovery.test.cjs +0 -245
  280. package/src/main/__tests__/scheduler-meta-code-sha.test.cjs +0 -46
  281. package/src/main/__tests__/scheduler-needs-review-autoresolve.test.cjs +0 -197
  282. package/src/main/__tests__/scheduler-never-stop.test.cjs +0 -157
  283. package/src/main/__tests__/scheduler-no-dead-end-status.test.cjs +0 -152
  284. package/src/main/__tests__/scheduler-no-orphan-run-dir.test.cjs +0 -81
  285. package/src/main/__tests__/scheduler-notify-originating-tab-transcript.test.cjs +0 -87
  286. package/src/main/__tests__/scheduler-notify-originating-tab.test.cjs +0 -343
  287. package/src/main/__tests__/scheduler-periodic-reverify-guard.test.cjs +0 -217
  288. package/src/main/__tests__/scheduler-porcelain-rename.test.cjs +0 -164
  289. package/src/main/__tests__/scheduler-prd-missing-skip.test.cjs +0 -161
  290. package/src/main/__tests__/scheduler-prd-persona-spawn.test.cjs +0 -178
  291. package/src/main/__tests__/scheduler-quarantine-autoresolve.test.cjs +0 -165
  292. package/src/main/__tests__/scheduler-quiet-machine-lease.test.cjs +0 -257
  293. package/src/main/__tests__/scheduler-rate-limit-cooldown-freshness.test.cjs +0 -123
  294. package/src/main/__tests__/scheduler-rate-limit-pause.test.cjs +0 -152
  295. package/src/main/__tests__/scheduler-rate-limit-spin-guard.test.cjs +0 -156
  296. package/src/main/__tests__/scheduler-reap-dead-running-jobs.test.cjs +0 -754
  297. package/src/main/__tests__/scheduler-reaper-helpers-basics.test.cjs +0 -87
  298. package/src/main/__tests__/scheduler-reconcile-cwd-preserve.test.cjs +0 -100
  299. package/src/main/__tests__/scheduler-reconcile-history-backfill.test.cjs +0 -105
  300. package/src/main/__tests__/scheduler-reconcile-invalid-repair.test.cjs +0 -203
  301. package/src/main/__tests__/scheduler-reconcile-quarantine.test.cjs +0 -247
  302. package/src/main/__tests__/scheduler-reset-job-fields-guard.test.cjs +0 -77
  303. package/src/main/__tests__/scheduler-resume-recovery.test.cjs +0 -254
  304. package/src/main/__tests__/scheduler-shard-quarantine.test.cjs +0 -115
  305. package/src/main/__tests__/scheduler-shared-tree-guard.test.cjs +0 -380
  306. package/src/main/__tests__/scheduler-sigterm-commit.test.cjs +0 -43
  307. package/src/main/__tests__/scheduler-stall-per-project.test.cjs +0 -126
  308. package/src/main/__tests__/scheduler-starve-escalation.test.cjs +0 -154
  309. package/src/main/__tests__/scheduler-stranded-autofix-park.test.cjs +0 -252
  310. package/src/main/__tests__/scheduler-stranded-investigation.test.cjs +0 -185
  311. package/src/main/__tests__/scheduler-stuck-failed-escalation.test.cjs +0 -136
  312. package/src/main/__tests__/scheduler-supervisor-record.test.cjs +0 -81
  313. package/src/main/__tests__/scheduler-tick-cancel-token.test.cjs +0 -54
  314. package/src/main/__tests__/scheduler-tick-wedge.test.cjs +0 -172
  315. package/src/main/__tests__/scheduler-transient-failure.test.cjs +0 -141
  316. package/src/main/__tests__/scheduler-unreadable-queue-guard.test.cjs +0 -62
  317. package/src/main/__tests__/scheduler-utilization-hold.test.cjs +0 -89
  318. package/src/main/__tests__/scheduler-verify-prd-path.test.cjs +0 -109
  319. package/src/main/__tests__/scheduler-worktree-cap-defer.test.cjs +0 -234
  320. package/src/main/__tests__/scheduler-worktree-exec-cwd.test.cjs +0 -120
  321. package/src/main/__tests__/scheduler-writeprd-epic-rollback.test.cjs +0 -105
  322. package/src/main/__tests__/schedulerBatchRootBlocker.test.cjs +0 -117
  323. package/src/main/__tests__/schedulerStateSidecarRestore.test.cjs +0 -110
  324. package/src/main/__tests__/seedAgentPersonas.test.cjs +0 -184
  325. package/src/main/__tests__/seedSchedulerMcp.test.cjs +0 -211
  326. package/src/main/__tests__/seedStatus.test.cjs +0 -100
  327. package/src/main/__tests__/seedValidatorPersona.test.cjs +0 -116
  328. package/src/main/__tests__/stop-signal-anchor.test.cjs +0 -75
  329. package/src/main/__tests__/telemetryClient.test.cjs +0 -1055
  330. package/src/main/__tests__/telemetryContract.test.cjs +0 -930
  331. package/src/main/__tests__/telemetrySettings.test.cjs +0 -210
  332. package/src/main/__tests__/transcripts-batch-flush.test.cjs +0 -249
  333. package/src/main/__tests__/transcripts-doFlush-array.test.cjs +0 -124
  334. package/src/main/__tests__/transcripts-paged-reads.test.cjs +0 -241
  335. package/src/main/__tests__/transcripts-worktree-epic-path.test.cjs +0 -154
  336. package/src/main/__tests__/transcriptsUsageFor.test.cjs +0 -206
  337. package/src/main/__tests__/uniquePrdNumbers.test.cjs +0 -153
  338. package/src/main/__tests__/usageSingleFlight.test.cjs +0 -169
  339. package/src/main/__tests__/validationSentinels.test.cjs +0 -84
  340. package/src/main/__tests__/workTypeLibrary.test.cjs +0 -89
  341. package/src/main/bilkoHost.cjs +0 -314
  342. package/src/main/bilkoHostCore.cjs +0 -89
  343. package/src/main/lib/__tests__/active-sessions.test.cjs +0 -251
  344. package/src/main/lib/__tests__/activeIndexRebuild.test.cjs +0 -179
  345. package/src/main/lib/__tests__/agentPersonaSchema.test.cjs +0 -67
  346. package/src/main/lib/__tests__/auditLog.test.cjs +0 -38
  347. package/src/main/lib/__tests__/bootSelfHeal.test.cjs +0 -107
  348. package/src/main/lib/__tests__/branchSweep.test.cjs +0 -164
  349. package/src/main/lib/__tests__/buildIdentity.test.cjs +0 -121
  350. package/src/main/lib/__tests__/buildTarget.test.cjs +0 -52
  351. package/src/main/lib/__tests__/childWithLog.test.cjs +0 -321
  352. package/src/main/lib/__tests__/coldBootPromptSessionsWrite.test.cjs +0 -87
  353. package/src/main/lib/__tests__/crashTelemetry.test.cjs +0 -103
  354. package/src/main/lib/__tests__/credentials-futile-refresh.test.cjs +0 -115
  355. package/src/main/lib/__tests__/cwdClassify.test.cjs +0 -111
  356. package/src/main/lib/__tests__/definitionOfDoneSequence.test.cjs +0 -95
  357. package/src/main/lib/__tests__/delegationReadiness.test.cjs +0 -1175
  358. package/src/main/lib/__tests__/dispatchLoop.test.cjs +0 -63
  359. package/src/main/lib/__tests__/effectiveModelInfo.test.cjs +0 -244
  360. package/src/main/lib/__tests__/ephemeralCwd.test.cjs +0 -91
  361. package/src/main/lib/__tests__/epicDelegationStats.test.cjs +0 -137
  362. package/src/main/lib/__tests__/epicSpawnCwd.test.cjs +0 -283
  363. package/src/main/lib/__tests__/epicSpawnPlan.test.cjs +0 -196
  364. package/src/main/lib/__tests__/epicTranscriptPath.test.cjs +0 -163
  365. package/src/main/lib/__tests__/epicWorktreeBoot.test.cjs +0 -136
  366. package/src/main/lib/__tests__/epicWorktreeMerge.test.cjs +0 -130
  367. package/src/main/lib/__tests__/epicWorktreeMint.test.cjs +0 -117
  368. package/src/main/lib/__tests__/epicWorktreeProjectConfig.test.cjs +0 -137
  369. package/src/main/lib/__tests__/fixChainDepth.test.cjs +0 -40
  370. package/src/main/lib/__tests__/fixtures/204-mercury-steam-horse.log.txt +0 -13
  371. package/src/main/lib/__tests__/fixtures/scheduler-machine.json.corrupt-1789147548 +0 -34
  372. package/src/main/lib/__tests__/gateFixtures.json +0 -20
  373. package/src/main/lib/__tests__/gitCacheBound.test.cjs +0 -69
  374. package/src/main/lib/__tests__/gitWorktree.test.cjs +0 -1478
  375. package/src/main/lib/__tests__/gitWorktreeSalvage.test.cjs +0 -107
  376. package/src/main/lib/__tests__/gitWorktreeSalvageDelta.test.cjs +0 -153
  377. package/src/main/lib/__tests__/guardShims.test.cjs +0 -158
  378. package/src/main/lib/__tests__/importReferences.spec.cjs +0 -56
  379. package/src/main/lib/__tests__/instanceLock.test.cjs +0 -173
  380. package/src/main/lib/__tests__/jobSupervisorRecord.test.cjs +0 -78
  381. package/src/main/lib/__tests__/jobWorktree.test.cjs +0 -199
  382. package/src/main/lib/__tests__/jobWorktreeBootLive.test.cjs +0 -82
  383. package/src/main/lib/__tests__/landedSinceRun.test.cjs +0 -133
  384. package/src/main/lib/__tests__/launchFailure.test.cjs +0 -220
  385. package/src/main/lib/__tests__/loadGate.test.cjs +0 -303
  386. package/src/main/lib/__tests__/localAdminHttp.test.cjs +0 -214
  387. package/src/main/lib/__tests__/loopDelay.test.cjs +0 -68
  388. package/src/main/lib/__tests__/mcpToolCatalog.test.cjs +0 -107
  389. package/src/main/lib/__tests__/modelCatalog.test.cjs +0 -202
  390. package/src/main/lib/__tests__/opsOwnership.test.cjs +0 -113
  391. package/src/main/lib/__tests__/opsRootAbsoluteCwd.test.cjs +0 -328
  392. package/src/main/lib/__tests__/opsRootNestedWrite.test.cjs +0 -51
  393. package/src/main/lib/__tests__/opsRootResolve.test.cjs +0 -149
  394. package/src/main/lib/__tests__/prdDeclaredPaths.test.cjs +0 -82
  395. package/src/main/lib/__tests__/prdDisposition.test.cjs +0 -224
  396. package/src/main/lib/__tests__/procIdentity.test.cjs +0 -119
  397. package/src/main/lib/__tests__/procName.test.cjs +0 -92
  398. package/src/main/lib/__tests__/projectBriefCore.test.cjs +0 -216
  399. package/src/main/lib/__tests__/projectRootResolve.test.cjs +0 -148
  400. package/src/main/lib/__tests__/queueHealth.test.cjs +0 -58
  401. package/src/main/lib/__tests__/queueStoreAtomicWrite.test.cjs +0 -88
  402. package/src/main/lib/__tests__/queueStoreMachineStateRecovery.test.cjs +0 -190
  403. package/src/main/lib/__tests__/quietMachineLease.test.cjs +0 -39
  404. package/src/main/lib/__tests__/rateLimitWindow.test.cjs +0 -88
  405. package/src/main/lib/__tests__/reaperHelpers.test.cjs +0 -577
  406. package/src/main/lib/__tests__/schedulerBatchDepends.test.cjs +0 -312
  407. package/src/main/lib/__tests__/schedulerBatchFairness.test.cjs +0 -213
  408. package/src/main/lib/__tests__/schedulerBatchLaunchHold.test.cjs +0 -125
  409. package/src/main/lib/__tests__/schedulerBatchProjectCap.test.cjs +0 -127
  410. package/src/main/lib/__tests__/schedulerBatchQuietMachine.test.cjs +0 -109
  411. package/src/main/lib/__tests__/schedulerMcpServerHeadlessRefusal.test.cjs +0 -71
  412. package/src/main/lib/__tests__/schedulerMcpServerHelp.test.cjs +0 -216
  413. package/src/main/lib/__tests__/schedulerMcpServerProjectHome.test.cjs +0 -183
  414. package/src/main/lib/__tests__/schedulerPaths.test.cjs +0 -226
  415. package/src/main/lib/__tests__/schedulerPathsWorktree.test.cjs +0 -133
  416. package/src/main/lib/__tests__/schedulerRuntimeState.test.cjs +0 -56
  417. package/src/main/lib/__tests__/sessionSlots.test.cjs +0 -144
  418. package/src/main/lib/__tests__/telemetryBacklog.test.cjs +0 -626
  419. package/src/main/lib/__tests__/telemetryBoot.test.cjs +0 -134
  420. package/src/main/lib/__tests__/telemetryConsent.test.cjs +0 -136
  421. package/src/main/lib/__tests__/telemetryCounters.test.cjs +0 -57
  422. package/src/main/lib/__tests__/telemetryCountersMetadataColumn.test.cjs +0 -98
  423. package/src/main/lib/__tests__/terminalRunOutcome.test.cjs +0 -200
  424. package/src/main/lib/__tests__/toolUseClassify.test.cjs +0 -53
  425. package/src/main/lib/__tests__/updateCheck.test.cjs +0 -63
  426. package/src/main/lib/__tests__/upgradeDrain.test.cjs +0 -130
  427. package/src/main/lib/__tests__/usageCircuit.test.cjs +0 -354
  428. package/src/main/lib/__tests__/watchdog-helpers.test.cjs +0 -375
  429. package/src/main/lib/__tests__/watchdog-relaunch.test.cjs +0 -266
  430. package/src/main/lib/kgExchangePairing.cjs +0 -75
@@ -1,115 +0,0 @@
1
- /**
2
- * scheduler-shard-quarantine.test.cjs — a torn per-project queue shard
3
- * quarantines THAT project only. tickQueue keeps dispatching the healthy
4
- * project, never scans/mints rows for the torn project's PRDs, never touches
5
- * the torn file, and health names the quarantined cwd (non-GREEN).
6
- *
7
- * Everything runs under a temp HOME; the stub `claude` writes a marker into
8
- * its own cwd so the test can prove where a spawn did (not) happen.
9
- *
10
- * Run: timeout 120 npx vitest run src/main/__tests__/scheduler-shard-quarantine.test.cjs
11
- */
12
-
13
- 'use strict';
14
-
15
- import { test, expect, beforeAll, afterAll } from 'vitest';
16
- const fs = require('node:fs');
17
- const os = require('node:os');
18
- const path = require('node:path');
19
- const claudeStub = require('../../../tests/helpers/claudeStub.cjs');
20
-
21
- let tmpHome;
22
- let originalHome;
23
- let originalWt;
24
- let scheduler;
25
-
26
- function registerActiveProject(cwd) {
27
- const slugDir = path.join(tmpHome, '.claude', 'projects', `fake-project-slug-${path.basename(cwd)}`);
28
- fs.mkdirSync(slugDir, { recursive: true });
29
- fs.writeFileSync(path.join(slugDir, 'transcript.jsonl'), JSON.stringify({ cwd }) + '\n');
30
- }
31
-
32
- function makeProject(name) {
33
- const cwd = path.join(tmpHome, 'Projects', name);
34
- const stateDir = path.join(cwd, 'session-manager-operations', 'scheduler', 'state');
35
- const prdsDir = path.join(cwd, 'session-manager-operations', 'scheduler', 'epics', 'fixture-epic', 'prds');
36
- fs.mkdirSync(stateDir, { recursive: true });
37
- fs.mkdirSync(prdsDir, { recursive: true });
38
- registerActiveProject(cwd);
39
- return { cwd, prdsDir, queueFile: path.join(stateDir, 'queue.json') };
40
- }
41
-
42
- function writeClaudeStub() {
43
- return claudeStub.writeClaudeStub({ body: `
44
- require('fs').writeFileSync(require('path').join(process.cwd(), 'ran-here.marker'), 'yes');
45
- process.stdout.write(JSON.stringify({ type: 'result', subtype: 'success', result: 'ok\\nSCHEDULER_VERDICT: PASS' }) + '\\n');
46
- process.exit(0);
47
- ` });
48
- }
49
-
50
- beforeAll(() => {
51
- originalHome = process.env.HOME;
52
- originalWt = process.env.SM_JOB_WORKTREE_DISABLE;
53
- tmpHome = fs.mkdtempSync(path.join(os.tmpdir(), 'sm-shard-quarantine-'));
54
- process.env.HOME = tmpHome;
55
- process.env.SM_JOB_WORKTREE_DISABLE = '1';
56
- process.env.SM_AUTOFIX_DISABLE = '1';
57
- fs.mkdirSync(path.join(tmpHome, '.claude', 'session-manager'), { recursive: true });
58
- scheduler = require('../scheduler.cjs');
59
- });
60
-
61
- afterAll(() => {
62
- process.env.HOME = originalHome;
63
- if (originalWt === undefined) delete process.env.SM_JOB_WORKTREE_DISABLE;
64
- else process.env.SM_JOB_WORKTREE_DISABLE = originalWt;
65
- delete process.env.SM_AUTOFIX_DISABLE;
66
- delete process.env.SM_CLAUDE_BIN;
67
- fs.rmSync(tmpHome, { recursive: true, force: true });
68
- });
69
-
70
- test('tickQueue dispatches the healthy project while the torn project is skipped; health names it', async () => {
71
- const good = makeProject('good-proj');
72
- const torn = makeProject('torn-proj');
73
- const goodSlug = `9101-good-${process.pid}`;
74
- const tornSlug = `9102-torn-${process.pid}`;
75
- fs.writeFileSync(path.join(good.prdsDir, `${goodSlug}.md`), 'Healthy PRD.', 'utf8');
76
- fs.writeFileSync(path.join(torn.prdsDir, `${tornSlug}.md`), 'PRD in the torn project.', 'utf8');
77
- fs.writeFileSync(good.queueFile, JSON.stringify({
78
- jobs: [{ slug: goodSlug, title: 'good', status: 'pending', cwd: good.cwd, dependsOn: [], createdAt: new Date().toISOString() }],
79
- }));
80
- // A running row was in flight when the shard tore — must never become pending.
81
- const TORN = '{"jobs":[{"slug":"' + tornSlug + '","status":"running"}]}{"jobs":[{"slug":"';
82
- fs.writeFileSync(torn.queueFile, TORN);
83
- const before = fs.readFileSync(torn.queueFile);
84
- process.env.SM_CLAUDE_BIN = writeClaudeStub();
85
-
86
- const result = await scheduler.tickQueue({ bypassLoadGate: true });
87
- expect(result.reason).not.toBe('unreadable');
88
- expect(result.fired).toBe(true);
89
- // flagUnreadable (via tickQueue's readQueue) snapshots the torn shard beside itself.
90
- const snaps = fs.readdirSync(path.dirname(torn.queueFile)).filter((f) => f.startsWith('queue.json.corrupt-'));
91
- expect(snaps).toHaveLength(1);
92
-
93
- // 60s, not 20s: under npm run test:unit's full-suite parallel-worker load
94
- // this occasionally needed more than 20s for the real child-process
95
- // dispatch to land (PRD 1414 — confirmed flaky only inside the full suite,
96
- // never in isolation or under synthetic single-file CPU load). The
97
- // surrounding vitest test timeout below (120s) already budgets for this.
98
- await new Promise((resolve, reject) => {
99
- const t0 = Date.now();
100
- const poll = () => {
101
- if (fs.existsSync(path.join(good.cwd, 'ran-here.marker'))) return resolve();
102
- if (Date.now() - t0 > 60_000) return reject(new Error('healthy project never dispatched'));
103
- setTimeout(poll, 100);
104
- };
105
- poll();
106
- });
107
-
108
- expect(fs.existsSync(path.join(torn.cwd, 'ran-here.marker'))).toBe(false);
109
- expect(fs.readFileSync(torn.queueFile).equals(before)).toBe(true);
110
-
111
- const health = await require('../health.cjs').check({ skipTypecheck: true });
112
- expect(health.components.scheduler_queue.ok).toBe(false);
113
- expect(health.components.scheduler_queue.quarantinedCwds).toEqual([torn.cwd]);
114
- expect(health.issues.some((i) => i.includes(torn.cwd))).toBe(true);
115
- }, 120_000); // skipTypecheck: this asserts scheduler_queue only; tsc was the tens of seconds
@@ -1,380 +0,0 @@
1
- /**
2
- * scheduler-shared-tree-guard.test.cjs — the shared-tree stash guard added
3
- * for the 2026-09-01 social-signals-trader incident: a headless PRD executor
4
- * ran a blanket `git stash` in a working tree shared with a live trading
5
- * service, silently reverting an uncommitted operator config edit with no
6
- * error anywhere.
7
- *
8
- * evaluateSharedTreeGuard is the pure diff (no I/O); checkSharedTreeGuard is
9
- * the orchestrator that also restores a single unambiguous stash, using the
10
- * same module.exports spy pattern computeCommittedDuringRun/committedInWindow
11
- * use so the underlying git calls never actually run.
12
- *
13
- * Run: timeout 120 npx vitest run src/main/__tests__/scheduler-shared-tree-guard.test.cjs
14
- */
15
-
16
- 'use strict';
17
-
18
- import { test, expect, vi, afterEach } from 'vitest';
19
- const fs = require('node:fs');
20
- const scheduler = require('../scheduler.cjs');
21
- const { evaluateSharedTreeGuard, checkSharedTreeGuard } = scheduler;
22
-
23
- afterEach(() => {
24
- vi.restoreAllMocks();
25
- });
26
-
27
- test('evaluateSharedTreeGuard: no-op when nothing changed', () => {
28
- const result = evaluateSharedTreeGuard({
29
- stashBefore: ['aaa stash@{0} WIP on main: aaa old'],
30
- stashAfter: ['aaa stash@{0} WIP on main: aaa old'],
31
- dirtyBefore: ['data/spread_config.json'],
32
- dirtyAfter: ['data/spread_config.json'],
33
- pathsCommittedDuringRun: [],
34
- });
35
- expect(result.newStashes).toEqual([]);
36
- expect(result.reverted).toEqual([]);
37
- });
38
-
39
- test('evaluateSharedTreeGuard: flags one new stash entry not present in the baseline', () => {
40
- const result = evaluateSharedTreeGuard({
41
- stashBefore: [],
42
- stashAfter: ['bbb stash@{0} WIP on fix/x: bbb f5ae98f'],
43
- dirtyBefore: [],
44
- dirtyAfter: [],
45
- pathsCommittedDuringRun: [],
46
- });
47
- expect(result.newStashes).toEqual([{ hash: 'bbb', ref: 'stash@{0}', subject: 'WIP on fix/x: bbb f5ae98f' }]);
48
- });
49
-
50
- test('evaluateSharedTreeGuard: two new stash entries both surface (caller decides ambiguity, not this function)', () => {
51
- const result = evaluateSharedTreeGuard({
52
- stashBefore: [],
53
- stashAfter: [
54
- 'bbb stash@{0} WIP on fix/x: bbb newest',
55
- 'ccc stash@{1} WIP on fix/x: ccc older',
56
- ],
57
- dirtyBefore: [],
58
- dirtyAfter: [],
59
- pathsCommittedDuringRun: [],
60
- });
61
- expect(result.newStashes).toHaveLength(2);
62
- expect(result.newStashes.map((e) => e.ref)).toEqual(['stash@{0}', 'stash@{1}']);
63
- });
64
-
65
- test('evaluateSharedTreeGuard: baseline-dirty path clean after the run with no commit to explain it is flagged reverted', () => {
66
- const result = evaluateSharedTreeGuard({
67
- stashBefore: [],
68
- stashAfter: [],
69
- dirtyBefore: ['data/spread_config.json'],
70
- dirtyAfter: [],
71
- pathsCommittedDuringRun: [],
72
- });
73
- expect(result.reverted).toEqual(['data/spread_config.json']);
74
- });
75
-
76
- test('evaluateSharedTreeGuard: baseline-dirty path clean after the run but touched by a commit made during the run is NOT flagged', () => {
77
- const result = evaluateSharedTreeGuard({
78
- stashBefore: [],
79
- stashAfter: [],
80
- dirtyBefore: ['src/foo.js'],
81
- dirtyAfter: [],
82
- pathsCommittedDuringRun: ['src/foo.js'],
83
- });
84
- expect(result.reverted).toEqual([]);
85
- });
86
-
87
- test('checkSharedTreeGuard: no-op when nothing changed returns null', async () => {
88
- vi.spyOn(scheduler, 'stashList').mockResolvedValue([]);
89
- vi.spyOn(scheduler, 'uncommittedChanges').mockResolvedValue([]);
90
- vi.spyOn(scheduler, 'gitHead').mockResolvedValue('sha1');
91
- vi.spyOn(scheduler, 'pathsChangedSince').mockResolvedValue([]);
92
- const restoreSpy = vi.spyOn(scheduler, 'restoreSpecificStash');
93
-
94
- const result = await checkSharedTreeGuard({
95
- cwd: '/repo',
96
- stashBaseline: [],
97
- dirtyBaseline: [],
98
- headBefore: 'sha1',
99
- slug: 'job-1',
100
- });
101
- expect(result).toBeNull();
102
- expect(restoreSpy).not.toHaveBeenCalled();
103
- });
104
-
105
- test('checkSharedTreeGuard: a single new stash is restored via apply+drop of that specific ref', async () => {
106
- vi.spyOn(scheduler, 'stashList').mockResolvedValue(['newhash stash@{0} WIP on main: f5ae98f']);
107
- vi.spyOn(scheduler, 'uncommittedChanges').mockResolvedValue([]);
108
- vi.spyOn(scheduler, 'gitHead').mockResolvedValue('sha1');
109
- vi.spyOn(scheduler, 'pathsChangedSince').mockResolvedValue([]);
110
- const restoreSpy = vi.spyOn(scheduler, 'restoreSpecificStash').mockResolvedValue({ ok: true });
111
-
112
- const result = await checkSharedTreeGuard({
113
- cwd: '/repo',
114
- stashBaseline: [],
115
- dirtyBaseline: [],
116
- headBefore: 'sha1',
117
- slug: 'job-1',
118
- });
119
- expect(restoreSpy).toHaveBeenCalledWith('/repo', 'stash@{0}');
120
- expect(result).toEqual({ restoredStash: 'stash@{0}' });
121
- });
122
-
123
- test('checkSharedTreeGuard: a baseline-dirty path stashed by the job and successfully restored is not ALSO flagged reverted', async () => {
124
- vi.spyOn(scheduler, 'stashList').mockResolvedValue(['newhash stash@{0} WIP on main: f5ae98f']);
125
- vi.spyOn(scheduler, 'gitHead').mockResolvedValue('sha1');
126
- vi.spyOn(scheduler, 'pathsChangedSince').mockResolvedValue([]);
127
- vi.spyOn(scheduler, 'restoreSpecificStash').mockResolvedValue({ ok: true });
128
- // uncommittedChanges is queried AFTER the restore attempt in the fixed
129
- // implementation — simulate the stashed file reappearing dirty once the
130
- // apply+drop succeeds (this is the exact 2026-09-01 incident shape: an
131
- // operator's pre-existing dirty file gets swept into the job's stash).
132
- vi.spyOn(scheduler, 'uncommittedChanges').mockResolvedValue(['data/spread_config.json']);
133
-
134
- const result = await checkSharedTreeGuard({
135
- cwd: '/repo',
136
- stashBaseline: [],
137
- dirtyBaseline: ['data/spread_config.json'],
138
- headBefore: 'sha1',
139
- slug: 'job-1',
140
- });
141
- expect(result).toEqual({ restoredStash: 'stash@{0}' });
142
- });
143
-
144
- test('checkSharedTreeGuard: two new stashes are reported, not restored (ambiguous)', async () => {
145
- vi.spyOn(scheduler, 'stashList').mockResolvedValue([
146
- 'aaa stash@{0} WIP on main: newest',
147
- 'bbb stash@{1} WIP on main: older',
148
- ]);
149
- vi.spyOn(scheduler, 'uncommittedChanges').mockResolvedValue([]);
150
- vi.spyOn(scheduler, 'gitHead').mockResolvedValue('sha1');
151
- vi.spyOn(scheduler, 'pathsChangedSince').mockResolvedValue([]);
152
- const restoreSpy = vi.spyOn(scheduler, 'restoreSpecificStash');
153
-
154
- const result = await checkSharedTreeGuard({
155
- cwd: '/repo',
156
- stashBaseline: [],
157
- dirtyBaseline: [],
158
- headBefore: 'sha1',
159
- slug: 'job-1',
160
- });
161
- expect(restoreSpy).not.toHaveBeenCalled();
162
- expect(result.ambiguousStashes).toEqual(['stash@{0}', 'stash@{1}']);
163
- expect(result.restoredStash).toBeUndefined();
164
- });
165
-
166
- test('checkSharedTreeGuard: a baseline-dirty path reverted with no stash and no commit is flagged reverted', async () => {
167
- vi.spyOn(scheduler, 'stashList').mockResolvedValue([]);
168
- vi.spyOn(scheduler, 'uncommittedChanges').mockResolvedValue([]);
169
- vi.spyOn(scheduler, 'gitHead').mockResolvedValue('sha1');
170
- vi.spyOn(scheduler, 'pathsChangedSince').mockResolvedValue([]);
171
-
172
- const result = await checkSharedTreeGuard({
173
- cwd: '/repo',
174
- stashBaseline: [],
175
- dirtyBaseline: ['data/spread_config.json'],
176
- headBefore: 'sha1',
177
- slug: 'job-1',
178
- });
179
- expect(result).toEqual({ reverted: ['data/spread_config.json'] });
180
- });
181
-
182
- test('checkSharedTreeGuard: a failed restore is reported as restoreFailed, entry left in place', async () => {
183
- vi.spyOn(scheduler, 'stashList').mockResolvedValue(['newhash stash@{0} WIP on main: f5ae98f']);
184
- vi.spyOn(scheduler, 'uncommittedChanges').mockResolvedValue([]);
185
- vi.spyOn(scheduler, 'gitHead').mockResolvedValue('sha1');
186
- vi.spyOn(scheduler, 'pathsChangedSince').mockResolvedValue([]);
187
- vi.spyOn(scheduler, 'restoreSpecificStash').mockResolvedValue({ ok: false, error: 'conflict applying stash' });
188
-
189
- const result = await checkSharedTreeGuard({
190
- cwd: '/repo',
191
- stashBaseline: [],
192
- dirtyBaseline: [],
193
- headBefore: 'sha1',
194
- slug: 'job-1',
195
- });
196
- expect(result.restoreFailed).toMatch(/stash@\{0\}.*conflict applying stash/);
197
- });
198
-
199
- test('checkSharedTreeGuard never throws when the underlying git calls reject', async () => {
200
- vi.spyOn(scheduler, 'stashList').mockRejectedValue(new Error('boom'));
201
- vi.spyOn(scheduler, 'uncommittedChanges').mockResolvedValue([]);
202
- vi.spyOn(scheduler, 'gitHead').mockResolvedValue('sha1');
203
- vi.spyOn(scheduler, 'pathsChangedSince').mockResolvedValue([]);
204
-
205
- const result = await checkSharedTreeGuard({
206
- cwd: '/repo',
207
- stashBaseline: [],
208
- dirtyBaseline: [],
209
- headBefore: 'sha1',
210
- slug: 'job-1',
211
- });
212
- expect(result).toBeNull();
213
- });
214
-
215
- // --- 2026-09-12 false-positive: an untracked path that merely became
216
- // IGNORED during the run (a .gitignore edit) is not a revert. See the
217
- // evaluateSharedTreeGuard doc comment for the full incident writeup. Each
218
- // test below is written to FAIL against the pre-fix implementation (which
219
- // computed `reverted = dirtyBefore - dirtyAfter - committed` with no
220
- // existence/tracked-status distinction at all) and PASS after it.
221
-
222
- test('evaluateSharedTreeGuard: an untracked path that disappears from git status but STILL EXISTS on disk is nowIgnored, not reverted', () => {
223
- // Pre-fix behavior: this path would land in `reverted` (dirtyBefore minus
224
- // dirtyAfter minus committed, no existence check at all) — FAILS pre-fix.
225
- const result = evaluateSharedTreeGuard({
226
- stashBefore: [],
227
- stashAfter: [],
228
- dirtyBefore: [{ code: '??', path: 'session-manager-operations/logs/errors-2026-09-12.jsonl' }],
229
- dirtyAfter: [],
230
- pathsCommittedDuringRun: ['.gitignore'],
231
- existsAfter: ['session-manager-operations/logs/errors-2026-09-12.jsonl'],
232
- });
233
- expect(result.reverted).toEqual([]);
234
- expect(result.nowIgnored).toEqual(['session-manager-operations/logs/errors-2026-09-12.jsonl']);
235
- });
236
-
237
- test('evaluateSharedTreeGuard: an untracked path that disappears from git status AND no longer exists on disk is still reverted', () => {
238
- // Proves the fix is not a blanket "stop reporting reverted": a genuinely
239
- // deleted untracked file (the guard's real purpose — catching a job that
240
- // discarded uncommitted work) must still surface. If the fix had simply
241
- // stopped reporting any untracked-and-missing path as reverted, this test
242
- // would fail (reverted would come back empty instead of containing the
243
- // path).
244
- const result = evaluateSharedTreeGuard({
245
- stashBefore: [],
246
- stashAfter: [],
247
- dirtyBefore: [{ code: '??', path: 'data/scratch.txt' }],
248
- dirtyAfter: [],
249
- pathsCommittedDuringRun: [],
250
- existsAfter: [], // file no longer exists on disk
251
- });
252
- expect(result.reverted).toEqual(['data/scratch.txt']);
253
- expect(result.nowIgnored).toEqual([]);
254
- });
255
-
256
- test('evaluateSharedTreeGuard: a TRACKED path clean after the run is still reverted even though it exists on disk', () => {
257
- // A tracked file leaving the dirty set means its content was restored to
258
- // HEAD — exactly the discard this guard exists to catch — regardless of
259
- // whether the file is still present. Pre-fix this already worked by
260
- // accident (no existence check existed at all); this test pins the
261
- // behavior now that existence checks exist for the untracked branch, so a
262
- // future change can't accidentally start trusting `existsAfter` for
263
- // tracked paths too.
264
- const result = evaluateSharedTreeGuard({
265
- stashBefore: [],
266
- stashAfter: [],
267
- dirtyBefore: [{ code: ' M', path: 'src/config.js' }],
268
- dirtyAfter: [],
269
- pathsCommittedDuringRun: [],
270
- existsAfter: ['src/config.js'], // file exists, but content was reset to HEAD
271
- });
272
- expect(result.reverted).toEqual(['src/config.js']);
273
- expect(result.nowIgnored).toEqual([]);
274
- });
275
-
276
- test('checkSharedTreeGuard end-to-end: the real 2026-09-12 incident shape — untracked logs path ignored by a committed .gitignore change, file still on disk — must NOT verdict shared_tree_reverted', async () => {
277
- // PRD 1181's exact shape: a bare `logs/` pattern lands in .gitignore during
278
- // the run (committed), eleven untracked log paths vanish from `git status`,
279
- // and the files are untouched on disk. Pre-fix, checkSharedTreeGuard's
280
- // `reverted` would contain this path (no existsAfter computation existed),
281
- // driving the caller's `verdict: 'shared_tree_reverted'` downgrade — FAILS
282
- // pre-fix.
283
- const logPath = 'session-manager-operations/logs/errors-2026-09-12.jsonl';
284
- vi.spyOn(scheduler, 'stashList').mockResolvedValue([]);
285
- vi.spyOn(scheduler, 'gitHead').mockResolvedValue('sha-after');
286
- vi.spyOn(scheduler, 'pathsChangedSince').mockResolvedValue(['.gitignore']);
287
- vi.spyOn(scheduler, 'uncommittedChanges').mockResolvedValue([]); // the log path no longer shows up at all
288
- // checkSharedTreeGuard's existence check runs concurrently via
289
- // fs.promises.access (fsp === fs.promises — same object) rather than
290
- // fs.existsSync, so a large untracked baseline doesn't block the event
291
- // loop with hundreds of synchronous stats back-to-back.
292
- vi.spyOn(fs.promises, 'access').mockImplementation((p) => (
293
- String(p).includes('errors-2026-09-12.jsonl') ? Promise.resolve() : Promise.reject(new Error('ENOENT'))
294
- ));
295
-
296
- const result = await checkSharedTreeGuard({
297
- cwd: '/repo',
298
- stashBaseline: [],
299
- dirtyBaseline: [{ code: '??', path: logPath }],
300
- headBefore: 'sha-before',
301
- slug: 'job-1181',
302
- });
303
-
304
- expect(result.reverted).toBeUndefined();
305
- expect(result.nowIgnored).toEqual([logPath]);
306
- });
307
-
308
- // --- landedCommit ground truth (2026-09-18, 1229-fo-03 false positive) ---
309
- // Real temp git fixture, never the live repo. Baseline-dirty path `cfg.txt`
310
- // goes clean with no commit covering it (the shape that makes the baseline
311
- // diff say "reverted"); only the landedCommit ancestry decides the verdict.
312
- const { execFileSync } = require('node:child_process');
313
- const os = require('node:os');
314
- const nodePath = require('node:path');
315
-
316
- function gitIn(dir, ...args) {
317
- return execFileSync('git', args, { cwd: dir, encoding: 'utf8' }).trim();
318
- }
319
-
320
- function fixtureRepo() {
321
- const dir = fs.mkdtempSync(nodePath.join(os.tmpdir(), 'shared-tree-landed-'));
322
- gitIn(dir, 'init', '-q');
323
- gitIn(dir, 'config', 'user.email', 't@example.com');
324
- gitIn(dir, 'config', 'user.name', 'T');
325
- fs.writeFileSync(nodePath.join(dir, 'cfg.txt'), 'base\n');
326
- gitIn(dir, 'add', '-A');
327
- gitIn(dir, 'commit', '-q', '-m', 'base');
328
- const base = gitIn(dir, 'rev-parse', 'HEAD');
329
- fs.writeFileSync(nodePath.join(dir, 'cfg.txt'), 'human wip\n'); // baseline-dirty
330
- return { dir, base };
331
- }
332
-
333
- function commitPath(dir, rel, msg) {
334
- fs.writeFileSync(nodePath.join(dir, rel), `${msg}\n`);
335
- gitIn(dir, 'add', rel);
336
- gitIn(dir, 'commit', '-q', '-m', msg);
337
- return gitIn(dir, 'rev-parse', 'HEAD');
338
- }
339
-
340
- test('checkSharedTreeGuard: landedCommit still an ancestor of HEAD plus an unrelated later commit is NOT reverted', async () => {
341
- const { dir, base } = fixtureRepo();
342
- const landed = commitPath(dir, 'job.txt', 'job work');
343
- gitIn(dir, 'checkout', '--', 'cfg.txt'); // baseline-dirty path goes clean, no commit covers it
344
- commitPath(dir, 'later.txt', 'unrelated human commit');
345
-
346
- const result = await checkSharedTreeGuard({
347
- cwd: dir, stashBaseline: [], dirtyBaseline: [{ code: ' M', path: 'cfg.txt' }],
348
- headBefore: base, slug: 'job-a', landedCommit: landed,
349
- });
350
- expect(result).toBeNull();
351
- });
352
-
353
- test('checkSharedTreeGuard: landedCommit absent from history still parks as reverted', async () => {
354
- const { dir, base } = fixtureRepo();
355
- const landed = commitPath(dir, 'job.txt', 'job work');
356
- gitIn(dir, 'reset', '-q', '--hard', base); // job commit discarded
357
- commitPath(dir, 'later.txt', 'unrelated human commit');
358
-
359
- const result = await checkSharedTreeGuard({
360
- cwd: dir, stashBaseline: [], dirtyBaseline: [{ code: ' M', path: 'cfg.txt' }],
361
- headBefore: base, slug: 'job-b', landedCommit: landed,
362
- });
363
- expect(result).toEqual({ reverted: ['cfg.txt'] });
364
- });
365
-
366
- test('checkSharedTreeGuard: no landedCommit at all still parks as reverted', async () => {
367
- const { dir, base } = fixtureRepo();
368
- gitIn(dir, 'checkout', '--', 'cfg.txt');
369
- const result = await checkSharedTreeGuard({
370
- cwd: dir, stashBaseline: [], dirtyBaseline: [{ code: ' M', path: 'cfg.txt' }],
371
- headBefore: base, slug: 'job-c', landedCommit: null,
372
- });
373
- expect(result).toEqual({ reverted: ['cfg.txt'] });
374
- });
375
-
376
- test('landedCommitIsAncestorOfHead: empty/unknown sha is false, never throws', async () => {
377
- const { dir } = fixtureRepo();
378
- expect(await scheduler.landedCommitIsAncestorOfHead(dir, '')).toBe(false);
379
- expect(await scheduler.landedCommitIsAncestorOfHead(dir, 'deadbeefdeadbeef')).toBe(false);
380
- });
@@ -1,43 +0,0 @@
1
- /**
2
- * scheduler-sigterm-commit.test.cjs — a run SIGTERM'd (exit 143) mid an
3
- * interactive/headless-incompatible AC step (xvfb Electron screenshot,
4
- * playwright electron.launch — see the 776/779 incidents, 2026-07-30) can
5
- * still have landed its deliverable commit before it died. classifySigtermWithCommit
6
- * decides whether that run should be routed to needs_review (commit found —
7
- * a human should verify the rest of the AC) instead of staying `failed`
8
- * forever, without ever silently auto-promoting to `completed`.
9
- *
10
- * Run: timeout 120 npx vitest run src/main/__tests__/scheduler-sigterm-commit.test.cjs
11
- */
12
-
13
- 'use strict';
14
-
15
- import { test, expect } from 'vitest';
16
- const { classifySigtermWithCommit } = require('../scheduler.cjs');
17
-
18
- test('classifySigtermWithCommit: exit 143 + commit found in window → needs_review', () => {
19
- const decision = classifySigtermWithCommit(143, true);
20
- expect(decision.status).toBe('needs_review');
21
- expect(decision.reason).toMatch(/SIGTERM/);
22
- expect(decision.reason).toMatch(/verify AC/i);
23
- });
24
-
25
- test('classifySigtermWithCommit: exit 143 + no commit found in window → still failed (regression check)', () => {
26
- const decision = classifySigtermWithCommit(143, false);
27
- expect(decision).toBeNull();
28
- });
29
-
30
- test('classifySigtermWithCommit: exit 1 (non-143 code) is unaffected even with a commit found', () => {
31
- const decision = classifySigtermWithCommit(1, true);
32
- expect(decision).toBeNull();
33
- });
34
-
35
- test('classifySigtermWithCommit: exit 137 (SIGKILL) is unaffected — scoped narrowly to 143', () => {
36
- const decision = classifySigtermWithCommit(137, true);
37
- expect(decision).toBeNull();
38
- });
39
-
40
- test('classifySigtermWithCommit: exit 0 is unaffected', () => {
41
- const decision = classifySigtermWithCommit(0, true);
42
- expect(decision).toBeNull();
43
- });
@@ -1,126 +0,0 @@
1
- /**
2
- * scheduler-stall-per-project.test.cjs — unit tests for computeStallSummary's
3
- * per-project `stalled` verdict and findStaleQuarantinedJobs' age-based
4
- * escalation.
5
- *
6
- * Covers the exact burrow-vs-others shape observed live: one project (burrow)
7
- * holds only quarantined rows (0 running, 0 pending) while another project
8
- * has running + pending work — the machine-wide `stalled` boolean reads
9
- * false (masking burrow), but burrow's own byProject entry must read true.
10
- *
11
- * Run: timeout 120 npx vitest run src/main/__tests__/scheduler-stall-per-project.test.cjs
12
- */
13
-
14
- 'use strict';
15
-
16
- // vitest, NOT node:test — this repo's suite is vitest-only (see CLAUDE.md:
17
- // "This repo does not use `node --test`"). A node:test file loads under
18
- // vitest as "No test suite found" and contributes zero tests, so the guard
19
- // silently doesn't exist. node:assert works fine inside a vitest run.
20
- import { test } from 'vitest';
21
- const assert = require('node:assert/strict');
22
- const { computeStallSummary, findStaleQuarantinedJobs, QUARANTINE_ESCALATE_MS } = require('../scheduler.cjs');
23
-
24
- test('computeStallSummary: burrow fully stalled (quarantined only) while another project is busy — machine-wide false, burrow true', () => {
25
- const state = {
26
- paused: null,
27
- jobs: [
28
- { slug: '821-a', cwd: '/home/bilko/Projects/burrow', status: 'quarantined' },
29
- { slug: '822-b', cwd: '/home/bilko/Projects/burrow', status: 'quarantined' },
30
- { slug: '823-c', cwd: '/home/bilko/Projects/burrow', status: 'quarantined' },
31
- { slug: '824-d', cwd: '/home/bilko/Projects/burrow', status: 'quarantined' },
32
- { slug: '900-e', cwd: '/home/bilko/Projects/other', status: 'running' },
33
- { slug: '901-f', cwd: '/home/bilko/Projects/other', status: 'pending' },
34
- ],
35
- invalidJobs: [],
36
- };
37
- const summary = computeStallSummary(state);
38
- assert.strictEqual(summary.stalled, false, 'machine-wide must NOT read stalled — other project is busy');
39
- assert.strictEqual(summary.byProject['/home/bilko/Projects/burrow'].stalled, true, 'burrow must read stalled on its own');
40
- assert.strictEqual(summary.byProject['/home/bilko/Projects/other'].stalled, false);
41
- });
42
-
43
- test('computeStallSummary: both projects busy — neither stalled', () => {
44
- const state = {
45
- paused: null,
46
- jobs: [
47
- { slug: 'a', cwd: '/p1', status: 'running' },
48
- { slug: 'b', cwd: '/p2', status: 'pending' },
49
- ],
50
- invalidJobs: [],
51
- };
52
- const summary = computeStallSummary(state);
53
- assert.strictEqual(summary.stalled, false);
54
- assert.strictEqual(summary.byProject['/p1'].stalled, false);
55
- assert.strictEqual(summary.byProject['/p2'].stalled, false);
56
- });
57
-
58
- test('computeStallSummary: both projects stalled — machine-wide AND both per-project read true', () => {
59
- const state = {
60
- paused: null,
61
- jobs: [
62
- { slug: 'a', cwd: '/p1', status: 'failed' },
63
- { slug: 'b', cwd: '/p2', status: 'needs_review' },
64
- ],
65
- invalidJobs: [],
66
- };
67
- const summary = computeStallSummary(state);
68
- assert.strictEqual(summary.stalled, true);
69
- assert.strictEqual(summary.byProject['/p1'].stalled, true);
70
- assert.strictEqual(summary.byProject['/p2'].stalled, true);
71
- });
72
-
73
- test('computeStallSummary: paused scheduler never reads stalled, machine-wide or per-project', () => {
74
- const state = {
75
- paused: { reason: 'rate_limit' },
76
- jobs: [{ slug: 'a', cwd: '/p1', status: 'quarantined' }],
77
- invalidJobs: [],
78
- };
79
- const summary = computeStallSummary(state);
80
- assert.strictEqual(summary.stalled, false);
81
- assert.strictEqual(summary.byProject['/p1'].stalled, false);
82
- });
83
-
84
- test('findStaleQuarantinedJobs: fires past threshold, not before', () => {
85
- const now = Date.parse('2026-08-08T12:00:00.000Z');
86
- const jobs = [
87
- {
88
- slug: 'fresh',
89
- cwd: '/p1',
90
- status: 'quarantined',
91
- statusHistory: [{ to: 'quarantined', at: new Date(now - 1 * 60 * 60_000).toISOString() }],
92
- },
93
- {
94
- slug: 'stale',
95
- cwd: '/p1',
96
- status: 'quarantined',
97
- statusHistory: [{ to: 'quarantined', at: new Date(now - 25 * 60 * 60_000).toISOString() }],
98
- },
99
- {
100
- slug: 'exactly-at-threshold',
101
- cwd: '/p1',
102
- status: 'quarantined',
103
- statusHistory: [{ to: 'quarantined', at: new Date(now - QUARANTINE_ESCALATE_MS).toISOString() }],
104
- },
105
- { slug: 'no-history', cwd: '/p1', status: 'quarantined' },
106
- { slug: 'not-quarantined', cwd: '/p1', status: 'pending' },
107
- ];
108
- const stale = findStaleQuarantinedJobs(jobs, now, QUARANTINE_ESCALATE_MS);
109
- const slugs = stale.map((s) => s.slug).sort();
110
- assert.deepStrictEqual(slugs, ['exactly-at-threshold', 'stale']);
111
- assert.strictEqual(stale.find((s) => s.slug === 'stale').cwd, '/p1');
112
- });
113
-
114
- test('computeStallSummary: a fully drained project (all completed) is idle, not stalled', () => {
115
- const state = {
116
- paused: null,
117
- jobs: [
118
- { slug: 'a', cwd: '/p1', status: 'completed' },
119
- { slug: 'b', cwd: '/p2', status: 'running' },
120
- ],
121
- invalidJobs: [],
122
- };
123
- const summary = computeStallSummary(state);
124
- assert.strictEqual(summary.byProject['/p1'].stalled, false);
125
- assert.strictEqual(computeStallSummary({ paused: null, jobs: [{ slug: 'a', cwd: '/p1', status: 'completed' }], invalidJobs: [] }).stalled, false);
126
- });