claude-code-session-manager 0.87.0 → 0.88.1

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 (291) hide show
  1. package/README.md +26 -53
  2. package/dist/assets/{AgentLibrary-DyLWzZDf.js → AgentLibrary-kQF8_wAw.js} +1 -1
  3. package/dist/assets/{DataModel--mISIJ6h.js → DataModel-CY_mMnsr.js} +1 -1
  4. package/dist/assets/{History-C2ahUXTg.js → History-Sji2brb0.js} +1 -1
  5. package/dist/assets/{Hooks-BiC6oyR2.js → Hooks-CtqpO-p5.js} +1 -1
  6. package/dist/assets/{HostBilko-BPleEOld.js → HostBilko-DN0ydOCr.js} +1 -1
  7. package/dist/assets/{Library-Dc8Qst1R.js → Library-CoiLZTKh.js} +1 -1
  8. package/dist/assets/{ListDetail-DIXh-OLX.js → ListDetail-CmqiQ_io.js} +1 -1
  9. package/dist/assets/{MarkdownEditor-C90bkLXK.js → MarkdownEditor-HPoNeKhT.js} +1 -1
  10. package/dist/assets/{McpServers-DqcbLOLZ.js → McpServers-Bj1X3_NT.js} +1 -1
  11. package/dist/assets/{Memory-CW62MXlh.js → Memory-1PKjsM3b.js} +1 -1
  12. package/dist/assets/{Panel-Bw1FhRuF.js → Panel-I1s6ghPo.js} +1 -1
  13. package/dist/assets/{Permissions-BcUC-5y8.js → Permissions-BhiiZjHQ.js} +1 -1
  14. package/dist/assets/{Plugins-BnKx9flD.js → Plugins-gv3_fkPX.js} +2 -2
  15. package/dist/assets/{ProvenanceBadge-Bw5vNVPT.js → ProvenanceBadge-BGcgBd8V.js} +1 -1
  16. package/dist/assets/{SaveBar-CWr0O_w-.js → SaveBar-BGr9eZ4g.js} +1 -1
  17. package/dist/assets/{Scheduler-DYdLuUqq.js → Scheduler-Bdo_awPq.js} +7 -7
  18. package/dist/assets/{ScopeSwitcher-CrBLbg8s.js → ScopeSwitcher-ukl0qWaA.js} +1 -1
  19. package/dist/assets/{Settings-DluB-vN1.js → Settings-DJkFj22G.js} +1 -1
  20. package/dist/assets/{SkillReferenceGraph-CHLSseay.js → SkillReferenceGraph-0t1Qblp6.js} +1 -1
  21. package/dist/assets/{Skills-gNdo_HNK.js → Skills-CeVdBGE-.js} +1 -1
  22. package/dist/assets/{SystemPrompt-Cru05-Ia.js → SystemPrompt-CfKvNHi3.js} +1 -1
  23. package/dist/assets/{TagLibrary-DNHY0xou.js → TagLibrary-DAzNaUPd.js} +1 -1
  24. package/dist/assets/{TiptapBody-I4lmbCgP.js → TiptapBody--S_rGXoA.js} +1 -1
  25. package/dist/assets/{Toggle-bWMHjmRh.js → Toggle-Dszm0xnp.js} +1 -1
  26. package/dist/assets/{index-DV3PorRY.css → index-BHTX4OTc.css} +1 -1
  27. package/dist/assets/{index-fc_JjdxL.js → index-Dp4Rc--q.js} +356 -356
  28. package/dist/assets/{settingsSchema-BfhtZnGD.js → settingsSchema-BLbssMYo.js} +1 -1
  29. package/dist/assets/{whisperWorker-Dbia1OpC.js → whisperWorker-C7ZGQwKg.js} +7 -7
  30. package/dist/index.html +2 -2
  31. package/dist/vad/ort-wasm-simd-threaded.asyncify.mjs +106 -110
  32. package/dist/vad/ort-wasm-simd-threaded.asyncify.wasm +0 -0
  33. package/dist/vad/ort-wasm-simd-threaded.jsep.mjs +98 -98
  34. package/dist/vad/ort-wasm-simd-threaded.jsep.wasm +0 -0
  35. package/dist/vad/ort-wasm-simd-threaded.jspi.mjs +99 -102
  36. package/dist/vad/ort-wasm-simd-threaded.jspi.wasm +0 -0
  37. package/dist/vad/ort-wasm-simd-threaded.mjs +46 -46
  38. package/dist/vad/ort-wasm-simd-threaded.wasm +0 -0
  39. package/package.json +10 -13
  40. package/scripts/README.md +59 -0
  41. package/scripts/audit-ops-hygiene.cjs +350 -0
  42. package/scripts/hooks/guard-destructive-git.cjs +10 -34
  43. package/scripts/hooks/guard-inline-implementation.cjs +14 -8
  44. package/scripts/hooks/guard-prd-writes.cjs +7 -36
  45. package/scripts/hooks/guard-self-schedule.cjs +175 -0
  46. package/scripts/ops-sweep.cjs +355 -0
  47. package/scripts/scheduler-mcp-server.cjs +28 -71
  48. package/src/main/__tests__/bilkoHost-integration.test.cjs +3 -3
  49. package/src/main/__tests__/chat-cancel-terminal.test.cjs +6 -9
  50. package/src/main/__tests__/chat-exit-close-race.test.cjs +3 -3
  51. package/src/main/__tests__/chat-mcp-consent-notice.test.cjs +4 -5
  52. package/src/main/__tests__/chat-queue.test.cjs +2 -2
  53. package/src/main/__tests__/chat-stop-signal.test.cjs +2 -2
  54. package/src/main/__tests__/dep-orphan-archive-health.test.cjs +77 -0
  55. package/src/main/__tests__/dod-batchkey.test.cjs +2 -2
  56. package/src/main/__tests__/dod-drain-hook.test.cjs +2 -2
  57. package/src/main/__tests__/dod-report.test.cjs +2 -2
  58. package/src/main/__tests__/dod-reverify.test.cjs +2 -2
  59. package/src/main/__tests__/epicMint.test.cjs +2 -2
  60. package/src/main/__tests__/exchanges.test.cjs +2 -2
  61. package/src/main/__tests__/extractJson.test.cjs +2 -2
  62. package/src/main/__tests__/files-reject-credentials.test.cjs +1 -1
  63. package/src/main/__tests__/fixtures/1218-fo-01-move-scripts-lib-into-src-main-lib.log +556 -0
  64. package/src/main/__tests__/health-build-freshness.test.cjs +39 -0
  65. package/src/main/__tests__/health-delegation-chain.test.cjs +15 -1
  66. package/src/main/__tests__/health-queue-dispatch.test.cjs +58 -7
  67. package/src/main/__tests__/health-tick-liveness.test.cjs +27 -3
  68. package/src/main/__tests__/health-usage-poller.test.cjs +70 -23
  69. package/src/main/__tests__/historyRollup.test.cjs +2 -2
  70. package/src/main/__tests__/kg-augment.test.cjs +2 -2
  71. package/src/main/__tests__/mcpStatus.test.cjs +2 -2
  72. package/src/main/__tests__/memoryAggregate.test.cjs +1 -1
  73. package/src/main/__tests__/memoryStale.test.cjs +1 -1
  74. package/src/main/__tests__/opsErrorLogTelemetryTap.test.cjs +25 -1
  75. package/src/main/__tests__/pollLoop-dispatch-on-failure.test.cjs +33 -3
  76. package/src/main/__tests__/prd-group-allocator.test.cjs +2 -2
  77. package/src/main/__tests__/prdAdminRouteParity.test.cjs +2 -0
  78. package/src/main/__tests__/prdAdminRoutes.test.cjs +14 -2
  79. package/src/main/__tests__/prdAuthoringSeed.test.cjs +39 -0
  80. package/src/main/__tests__/prdLocationsArchived.test.cjs +20 -13
  81. package/src/main/__tests__/proc-role-env.test.cjs +125 -0
  82. package/src/main/__tests__/procname-claude-spawn-sites.test.cjs +304 -0
  83. package/src/main/__tests__/procname-sm-processes.test.cjs +127 -0
  84. package/src/main/__tests__/projectHomeAdminRoutes.test.cjs +81 -401
  85. package/src/main/__tests__/projectPages.test.cjs +63 -149
  86. package/src/main/__tests__/queue-health-verdict.test.cjs +9 -9
  87. package/src/main/__tests__/queue-starvation-dispatch-driver.test.cjs +117 -20
  88. package/src/main/__tests__/queueHistory.test.cjs +2 -2
  89. package/src/main/__tests__/rateLimitPollerStreak.test.cjs +38 -3
  90. package/src/main/__tests__/runVerify-landed-commit-outranks.test.cjs +181 -0
  91. package/src/main/__tests__/runVerify.test.cjs +5 -5
  92. package/src/main/__tests__/scheduleJobStatusDrift.test.cjs +3 -3
  93. package/src/main/__tests__/scheduleJobTransitions.test.cjs +2 -2
  94. package/src/main/__tests__/scheduler-adopted-run-supervision.test.cjs +143 -0
  95. package/src/main/__tests__/scheduler-autofix-select.test.cjs +2 -2
  96. package/src/main/__tests__/scheduler-autopromote.test.cjs +2 -2
  97. package/src/main/__tests__/scheduler-bash-timeout-env.test.cjs +3 -5
  98. package/src/main/__tests__/scheduler-boot-orphans.test.cjs +78 -97
  99. package/src/main/__tests__/scheduler-default-eligible-heal.test.cjs +161 -0
  100. package/src/main/__tests__/scheduler-dispatch-loop.test.cjs +58 -0
  101. package/src/main/__tests__/scheduler-epic-digest.test.cjs +3 -5
  102. package/src/main/__tests__/scheduler-force-tick-outcome.test.cjs +2 -2
  103. package/src/main/__tests__/scheduler-gate-shadow.test.cjs +119 -0
  104. package/src/main/__tests__/scheduler-guard-verdict-autoresolve.test.cjs +46 -0
  105. package/src/main/__tests__/scheduler-heartbeat-payload.test.cjs +80 -0
  106. package/src/main/__tests__/scheduler-inplace-salvage.test.cjs +25 -18
  107. package/src/main/__tests__/scheduler-investigation-prompt.test.cjs +4 -4
  108. package/src/main/__tests__/scheduler-launch-failure.test.cjs +3 -5
  109. package/src/main/__tests__/scheduler-looks-done.test.cjs +26 -7
  110. package/src/main/__tests__/scheduler-manual-pause.test.cjs +118 -0
  111. package/src/main/__tests__/scheduler-meta-code-sha.test.cjs +26 -3
  112. package/src/main/__tests__/scheduler-prd-missing-skip.test.cjs +17 -2
  113. package/src/main/__tests__/scheduler-prd-persona-spawn.test.cjs +3 -5
  114. package/src/main/__tests__/scheduler-quiet-machine-lease.test.cjs +41 -6
  115. package/src/main/__tests__/scheduler-rate-limit-pause.test.cjs +62 -6
  116. package/src/main/__tests__/scheduler-rate-limit-spin-guard.test.cjs +3 -5
  117. package/src/main/__tests__/scheduler-reap-dead-running-jobs.test.cjs +23 -4
  118. package/src/main/__tests__/scheduler-reconcile-cwd-preserve.test.cjs +100 -0
  119. package/src/main/__tests__/scheduler-reconcile-invalid-repair.test.cjs +3 -3
  120. package/src/main/__tests__/scheduler-reconcile-quarantine.test.cjs +4 -4
  121. package/src/main/__tests__/scheduler-shard-quarantine.test.cjs +110 -0
  122. package/src/main/__tests__/scheduler-shared-tree-guard.test.cjs +81 -1
  123. package/src/main/__tests__/scheduler-stall-per-project.test.cjs +14 -0
  124. package/src/main/__tests__/scheduler-starve-escalation.test.cjs +4 -4
  125. package/src/main/__tests__/scheduler-stranded-autofix-park.test.cjs +245 -0
  126. package/src/main/__tests__/scheduler-supervisor-record.test.cjs +81 -0
  127. package/src/main/__tests__/scheduler-tick-cancel-token.test.cjs +2 -2
  128. package/src/main/__tests__/scheduler-tick-wedge.test.cjs +172 -0
  129. package/src/main/__tests__/scheduler-transient-failure.test.cjs +2 -2
  130. package/src/main/__tests__/scheduler-worktree-cap-defer.test.cjs +37 -11
  131. package/src/main/__tests__/scheduler-worktree-exec-cwd.test.cjs +3 -5
  132. package/src/main/__tests__/usageSingleFlight.test.cjs +157 -0
  133. package/src/main/__tests__/workTypeLibrary.test.cjs +1 -1
  134. package/src/main/bilkoHost.cjs +16 -7
  135. package/src/main/build-info.json +8 -0
  136. package/src/main/chatRunner.cjs +4 -2
  137. package/src/main/config.cjs +2 -3
  138. package/src/main/docEdit.cjs +4 -2
  139. package/src/main/health.cjs +276 -52
  140. package/src/main/heapSnapshot.cjs +2 -2
  141. package/src/main/historyAggregator.cjs +1 -1
  142. package/src/main/index.cjs +61 -10
  143. package/src/main/ipcSchemas.cjs +6 -17
  144. package/src/main/lib/__tests__/auditLog.test.cjs +38 -0
  145. package/src/main/lib/__tests__/buildIdentity.test.cjs +121 -0
  146. package/src/main/lib/__tests__/cwdClassify.test.cjs +111 -0
  147. package/src/main/lib/__tests__/definitionOfDoneSequence.test.cjs +95 -0
  148. package/src/main/lib/__tests__/delegationReadiness.test.cjs +1 -1
  149. package/src/main/lib/__tests__/dispatchLoop.test.cjs +63 -0
  150. package/src/main/lib/__tests__/gateFixtures.json +20 -0
  151. package/src/main/lib/__tests__/gitWorktree.test.cjs +8 -1
  152. package/src/main/lib/__tests__/instanceLock.test.cjs +93 -7
  153. package/src/main/lib/__tests__/jobSupervisorRecord.test.cjs +78 -0
  154. package/src/main/lib/__tests__/jobWorktreeBootLive.test.cjs +11 -0
  155. package/src/main/lib/__tests__/localAdminHttp.test.cjs +1 -1
  156. package/src/main/lib/__tests__/mcpToolCatalog.test.cjs +6 -1
  157. package/src/main/lib/__tests__/procIdentity.test.cjs +119 -0
  158. package/src/main/lib/__tests__/procName.test.cjs +92 -0
  159. package/src/main/lib/__tests__/queueStoreMachineStateRecovery.test.cjs +67 -0
  160. package/src/main/lib/__tests__/schedulerMcpServerHelp.test.cjs +6 -7
  161. package/src/main/lib/__tests__/schedulerMcpServerProjectHome.test.cjs +37 -204
  162. package/src/main/lib/__tests__/schedulerPaths.test.cjs +226 -0
  163. package/src/main/lib/__tests__/schedulerPathsWorktree.test.cjs +94 -0
  164. package/src/main/lib/__tests__/schedulerRuntimeState.test.cjs +56 -0
  165. package/src/main/lib/__tests__/sessionSlots.test.cjs +45 -0
  166. package/src/main/lib/__tests__/telemetryBacklog.test.cjs +1 -1
  167. package/src/main/lib/__tests__/upgradeDrain.test.cjs +130 -0
  168. package/src/main/lib/__tests__/usageCircuit.test.cjs +61 -0
  169. package/src/main/lib/__tests__/watchdog-helpers.test.cjs +63 -0
  170. package/src/main/lib/__tests__/watchdog-relaunch.test.cjs +73 -0
  171. package/src/main/lib/activeIndexRebuild.cjs +1 -3
  172. package/src/main/lib/activeSessions.cjs +20 -88
  173. package/src/main/lib/adoptedRunSupervisor.cjs +136 -0
  174. package/src/main/lib/agentModelResolve.cjs +33 -1
  175. package/src/main/lib/agentPersonaSchema.cjs +2 -2
  176. package/src/main/lib/auditLog.cjs +30 -5
  177. package/src/main/lib/buildIdentity.cjs +113 -0
  178. package/src/main/lib/classifyPromptTicket.cjs +3 -2
  179. package/src/main/lib/claudeBin.cjs +37 -2
  180. package/src/main/lib/cleanEnv.cjs +27 -1
  181. package/src/main/lib/credentials.cjs +4 -2
  182. package/src/main/lib/cwdClassify.cjs +185 -0
  183. package/src/main/lib/definitionOfDone.cjs +296 -52
  184. package/src/main/lib/dispatchLoop.cjs +40 -0
  185. package/src/main/lib/effectiveModelInfo.cjs +9 -10
  186. package/src/main/lib/ephemeralCwd.cjs +7 -28
  187. package/src/main/lib/gitWorktree.cjs +95 -8
  188. package/src/main/lib/guardShims.cjs +3 -3
  189. package/src/main/lib/historyRollup.cjs +6 -7
  190. package/src/main/lib/instanceLock.cjs +32 -5
  191. package/src/main/lib/jobSupervisorRecord.cjs +147 -0
  192. package/src/main/lib/jobWorktreeBootLive.cjs +9 -5
  193. package/src/main/lib/localAdminHttp.cjs +9 -27
  194. package/src/main/lib/mcpToolCatalog.cjs +29 -77
  195. package/src/main/lib/opsOwnership.cjs +27 -19
  196. package/src/main/lib/prdAuthoringSeed.cjs +36 -0
  197. package/src/main/lib/prdLocations.cjs +48 -1
  198. package/src/main/lib/procIdentity.cjs +126 -0
  199. package/src/main/lib/procName.cjs +98 -0
  200. package/src/main/lib/projectHomeAdminRoutes.cjs +56 -327
  201. package/src/main/lib/queueHistory.cjs +8 -7
  202. package/src/main/lib/queueStore.cjs +57 -37
  203. package/src/main/lib/quietMachineLease.cjs +19 -2
  204. package/src/main/lib/reservationExpiry.cjs +30 -0
  205. package/src/main/lib/runClaudeP.cjs +4 -2
  206. package/src/main/lib/runLogRetention.cjs +3 -2
  207. package/src/main/lib/scheduleJobSchema.cjs +2 -2
  208. package/src/main/lib/scheduleJobTransitions.cjs +29 -2
  209. package/src/main/lib/schedulerBatch.cjs +27 -2
  210. package/src/main/lib/schedulerPaths.cjs +175 -0
  211. package/src/main/lib/schedulerRuntimeState.cjs +59 -0
  212. package/src/main/lib/sessionSlots.cjs +47 -9
  213. package/src/main/lib/smProcNames.cjs +51 -0
  214. package/src/main/lib/upgradeDrain.cjs +188 -0
  215. package/src/main/lib/usageCircuit.cjs +53 -6
  216. package/src/main/lib/watchdogHelpers.cjs +70 -37
  217. package/src/main/lib/withTimeout.cjs +33 -0
  218. package/src/main/mcpStatus.cjs +4 -2
  219. package/src/main/pluginInstall.cjs +6 -2
  220. package/src/main/projectPages.cjs +41 -145
  221. package/src/main/pty.cjs +8 -1
  222. package/src/main/queueOps.cjs +10 -10
  223. package/src/main/runVerify.cjs +69 -3
  224. package/src/main/scheduler.cjs +1607 -383
  225. package/src/main/seedAgentPersonas.cjs +1 -1
  226. package/src/main/seedDevPlugin.cjs +1 -1
  227. package/src/main/seedSchedulerMcp.cjs +7 -6
  228. package/src/main/seedStatus.cjs +1 -1
  229. package/src/main/supervisor.cjs +5 -3
  230. package/src/main/usage.cjs +126 -62
  231. package/src/preload/api.d.ts +18 -64
  232. package/src/preload/index.cjs +2 -4
  233. package/src/seed/agents/project-home-builder.md +31 -48
  234. package/screenshots/.gitkeep +0 -0
  235. package/screenshots/README-screenshots.md +0 -13
  236. package/src/main/lib/projectPageSummarySchema.cjs +0 -181
  237. package/src/main/teams.cjs +0 -95
  238. package/src/main/templates/project-pages-catalog.json +0 -741
  239. package/src/main/templates/project-pages-default-home.html +0 -123
  240. package/src/main/templates/project-pages-pipeline.md +0 -417
  241. package/src/seed/prompts/code-review/ac-coverage-check.md +0 -8
  242. package/src/seed/prompts/code-review/correctness-only.md +0 -8
  243. package/src/seed/prompts/code-review/full-spectrum-high.md +0 -8
  244. package/src/seed/prompts/code-review/hallucination-check.md +0 -8
  245. package/src/seed/prompts/code-review/public-api-compat.md +0 -8
  246. package/src/seed/prompts/code-review/readability-naming.md +0 -8
  247. package/src/seed/prompts/debugging/bug-as-failing-test.md +0 -8
  248. package/src/seed/prompts/debugging/git-bisect-regression.md +0 -8
  249. package/src/seed/prompts/debugging/instrument-intermittent-bug.md +0 -8
  250. package/src/seed/prompts/debugging/localize-pipeline-failure.md +0 -8
  251. package/src/seed/prompts/debugging/reproduce-then-diagnose.md +0 -8
  252. package/src/seed/prompts/documentation/adr-from-change.md +0 -8
  253. package/src/seed/prompts/documentation/module-readme.md +0 -8
  254. package/src/seed/prompts/documentation/onboarding-plan.md +0 -8
  255. package/src/seed/prompts/documentation/refresh-claude-md.md +0 -8
  256. package/src/seed/prompts/documentation/tsdoc-public-exports.md +0 -8
  257. package/src/seed/prompts/git-pr/conventional-commit.md +0 -8
  258. package/src/seed/prompts/git-pr/draft-pr-title-body.md +0 -8
  259. package/src/seed/prompts/git-pr/pre-commit-safety-sweep.md +0 -8
  260. package/src/seed/prompts/git-pr/release-notes-block.md +0 -8
  261. package/src/seed/prompts/git-pr/split-large-pr.md +0 -8
  262. package/src/seed/prompts/performance/bundle-startup-audit.md +0 -8
  263. package/src/seed/prompts/performance/complexity-audit.md +0 -8
  264. package/src/seed/prompts/performance/cpu-profile-hot-path.md +0 -8
  265. package/src/seed/prompts/performance/db-query-plan-review.md +0 -8
  266. package/src/seed/prompts/performance/memory-leak-hunt.md +0 -8
  267. package/src/seed/prompts/qa/api-contract-tests.md +0 -8
  268. package/src/seed/prompts/qa/e2e-critical-path.md +0 -8
  269. package/src/seed/prompts/qa/failing-test-for-bug.md +0 -8
  270. package/src/seed/prompts/qa/find-missing-test-coverage.md +0 -8
  271. package/src/seed/prompts/qa/stabilize-flaky-test.md +0 -8
  272. package/src/seed/prompts/qa/tdd-red-first.md +0 -8
  273. package/src/seed/prompts/qa/visual-regression-review.md +0 -8
  274. package/src/seed/prompts/qa/wcag-axe-scan.md +0 -8
  275. package/src/seed/prompts/refactoring/dead-code-sweep.md +0 -8
  276. package/src/seed/prompts/refactoring/extract-duplicated-pattern.md +0 -8
  277. package/src/seed/prompts/refactoring/modernize-legacy-file.md +0 -8
  278. package/src/seed/prompts/refactoring/reduce-cyclomatic-complexity.md +0 -8
  279. package/src/seed/prompts/refactoring/tighten-module-boundaries.md +0 -8
  280. package/src/seed/prompts/security/authz-audit.md +0 -8
  281. package/src/seed/prompts/security/crypto-correctness.md +0 -8
  282. package/src/seed/prompts/security/cwe-top-25-hunt.md +0 -8
  283. package/src/seed/prompts/security/dependency-audit.md +0 -8
  284. package/src/seed/prompts/security/ipc-boundary-hardening.md +0 -8
  285. package/src/seed/prompts/security/owasp-top-10-staged-diff.md +0 -8
  286. package/src/seed/prompts/security/secret-credential-scan.md +0 -8
  287. package/web/README.md +0 -41
  288. package/web/project-pages/logic/dist/logic.cjs +0 -4709
  289. package/web/project-pages/render.cjs +0 -70
  290. package/web/project-pages/renderer/dist/renderer.cjs +0 -18900
  291. package/web/project-pages/validate-summary.cjs +0 -62
@@ -0,0 +1,181 @@
1
+ /**
2
+ * runVerify-landed-commit-outranks.test.cjs — ground truth (exit 0 + a commit
3
+ * landed THIS run + no explicit FAIL sentinel) outranks the transcript-
4
+ * pattern heuristics (final-20% is_error, FAIL/Traceback,
5
+ * ModuleNotFoundError), so a self-recovered tool error no longer parks a
6
+ * green, committed run.
7
+ *
8
+ * Fixture: the REAL run log for 1218-fo-01-move-scripts-lib-into-src-main-lib
9
+ * (session-manager-operations/reviews/2026-09-13-scheduler-stability-
10
+ * investigation.md), copied verbatim from
11
+ * ~/.claude/session-manager/scheduled-plans/runs/2026-09-13T21-52-51-192Z/ —
12
+ * a `git apply` "patch does not apply" tool error at event 179/209 (inside
13
+ * the final 20% of the transcript) that the agent retried and resolved 3
14
+ * events later, in a run that landed commit d1edf15 and was STILL parked
15
+ * needs_review (verdicts.json: verdict transcript_errors, reason
16
+ * "is_error=true in final 20% of transcript (event 179/209)"), spawning a
17
+ * $3.74 fix-plan investigation into work that needed no fix.
18
+ *
19
+ * The real run's own SCHEDULER_VERDICT sentinel is an explicit FAIL — an
20
+ * honest, UNRELATED report that test:unit was red for proven pre-existing/
21
+ * environmental reasons (shared /tmp contention), not a regression from this
22
+ * PRD's move. Per this PRD's own AC, an explicit FAIL sentinel must keep
23
+ * today's behavior byte-for-byte, so scenarios (a)/(b)/(d) below use a
24
+ * variant of the fixture with that trailing SCHEDULER_VERDICT line stripped
25
+ * (sentinel === null) — the majority real-world park shape this rule targets
26
+ * (the findings report measured 77 of 212 parks as transcript_errors with NO
27
+ * sentinel at all, vs. this one specific row's own additional, unrelated
28
+ * FAIL claim). Scenario (c) reuses the pristine, byte-for-byte-unmodified
29
+ * fixture to prove the real, still-present FAIL sentinel is honored exactly
30
+ * as before this change.
31
+ *
32
+ * Run standalone: timeout 120 npx vitest run src/main/__tests__/runVerify-landed-commit-outranks.test.cjs
33
+ */
34
+
35
+ import { test } from 'vitest';
36
+ const assert = require('node:assert/strict');
37
+ const os = require('node:os');
38
+ const fs = require('node:fs');
39
+ const path = require('node:path');
40
+ const { execFileSync } = require('node:child_process');
41
+ const { verifyRun } = require('../runVerify.cjs');
42
+
43
+ const SLUG = '1218-fo-01-move-scripts-lib-into-src-main-lib';
44
+ const FIXTURE_PATH = path.join(__dirname, 'fixtures', `${SLUG}.log`);
45
+ const LANDED_COMMIT = 'd1edf15be50a783f08a55e6de328780f0feb3cb0';
46
+
47
+ function makeTmpDir() {
48
+ return fs.mkdtempSync(path.join(os.tmpdir(), 'run-verify-landed-commit-outranks-test-'));
49
+ }
50
+
51
+ function rmdir(dir) {
52
+ try { fs.rmSync(dir, { recursive: true, force: true }); } catch { /* */ }
53
+ }
54
+
55
+ function makeTmpGitRepo() {
56
+ const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'run-verify-landed-commit-outranks-repo-'));
57
+ execFileSync('git', ['init', '-q'], { cwd: dir });
58
+ execFileSync('git', ['config', 'user.email', 'test@example.com'], { cwd: dir });
59
+ execFileSync('git', ['config', 'user.name', 'Test'], { cwd: dir });
60
+ fs.writeFileSync(path.join(dir, 'README.md'), 'init\n');
61
+ execFileSync('git', ['add', 'README.md'], { cwd: dir });
62
+ execFileSync('git', ['commit', '-q', '-m', 'init'], { cwd: dir });
63
+ return dir;
64
+ }
65
+
66
+ function writePrd(dir, slug) {
67
+ const text = '---\ntitle: Test PRD\ncwd: /tmp\nestimateMinutes: 30\n---\n# Move scripts/lib into src/main/lib\n';
68
+ fs.writeFileSync(path.join(dir, `${slug}.md`), text);
69
+ return path.join(dir, `${slug}.md`);
70
+ }
71
+
72
+ /**
73
+ * Load the real fixture log, optionally stripping the trailing
74
+ * `SCHEDULER_VERDICT: FAIL ...` line from the final `{"type":"result",...}`
75
+ * event's `result` field so the sentinel scan sees no sentinel at all.
76
+ */
77
+ function loadFixture({ stripFailSentinel }) {
78
+ const raw = fs.readFileSync(FIXTURE_PATH, 'utf8');
79
+ if (!stripFailSentinel) return raw;
80
+ const lines = raw.split('\n');
81
+ const idx = lines.findIndex((l) => l.includes('"type":"result"'));
82
+ assert.ok(idx !== -1, 'fixture must contain a type:result line');
83
+ const obj = JSON.parse(lines[idx]);
84
+ assert.ok(/SCHEDULER_VERDICT:\s*FAIL/.test(obj.result), 'fixture result must carry the real FAIL sentinel before stripping');
85
+ obj.result = obj.result.replace(/\n?SCHEDULER_VERDICT:\s*FAIL[^\n]*$/, '');
86
+ assert.ok(!/SCHEDULER_VERDICT/.test(obj.result), 'sentinel must be fully removed from the stripped variant');
87
+ lines[idx] = JSON.stringify(obj);
88
+ return lines.join('\n');
89
+ }
90
+
91
+ function writeLog(dir, slug, text) {
92
+ fs.writeFileSync(path.join(dir, `${slug}.log`), text);
93
+ }
94
+
95
+ // (a) exit 0 + commit landed THIS run + no sentinel at all → clean, demoted to annotation.
96
+ test('exit 0 + landed commit this run + no FAIL sentinel → clean with annotation (1218-fo-01)', async () => {
97
+ const tmp = makeTmpDir();
98
+ const repo = makeTmpGitRepo();
99
+ try {
100
+ writeLog(tmp, SLUG, loadFixture({ stripFailSentinel: true }));
101
+ const prdPath = writePrd(tmp, SLUG);
102
+ const verdict = await verifyRun({
103
+ runDir: tmp,
104
+ prdPath,
105
+ queueEntry: { slug: SLUG, status: 'running', cwd: repo },
106
+ allJobs: [],
107
+ committedDuringRun: false,
108
+ jobLandedCommitThisRun: LANDED_COMMIT,
109
+ exitCode: 0,
110
+ });
111
+ assert.equal(verdict.verdict, 'clean', `expected clean, got ${verdict.verdict}: ${verdict.reason}`);
112
+ assert.equal(verdict.downgradeTo, null);
113
+ const sidecar = JSON.parse(fs.readFileSync(path.join(tmp, `${SLUG}.verdicts.json`), 'utf8'));
114
+ assert.ok(Array.isArray(sidecar.annotations) && sidecar.annotations.length > 0, 'the demoted is_error hit must be recorded as an annotation');
115
+ assert.ok(sidecar.annotations.some((a) => a.verdict === 'transcript_errors'), 'annotation must carry the original transcript_errors verdict');
116
+ } finally { rmdir(tmp); rmdir(repo); }
117
+ });
118
+
119
+ // (b) the same (stripped-sentinel) log, but exitCode !== 0 → keeps today's behavior.
120
+ test('non-zero exit → transcript_errors (evidence never applies to a failing run)', async () => {
121
+ const tmp = makeTmpDir();
122
+ const repo = makeTmpGitRepo();
123
+ try {
124
+ writeLog(tmp, SLUG, loadFixture({ stripFailSentinel: true }));
125
+ const prdPath = writePrd(tmp, SLUG);
126
+ const verdict = await verifyRun({
127
+ runDir: tmp,
128
+ prdPath,
129
+ queueEntry: { slug: SLUG, status: 'running', cwd: repo },
130
+ allJobs: [],
131
+ committedDuringRun: false,
132
+ jobLandedCommitThisRun: LANDED_COMMIT,
133
+ exitCode: 1,
134
+ });
135
+ assert.equal(verdict.verdict, 'transcript_errors', `expected transcript_errors, got ${verdict.verdict}: ${verdict.reason}`);
136
+ assert.equal(verdict.downgradeTo, 'needs_review');
137
+ } finally { rmdir(tmp); rmdir(repo); }
138
+ });
139
+
140
+ // (c) the pristine, byte-for-byte-unmodified fixture — its own real FAIL sentinel — keeps today's behavior.
141
+ test('explicit FAIL sentinel (the real, unmodified fixture) → transcript_errors byte-for-byte', async () => {
142
+ const tmp = makeTmpDir();
143
+ const repo = makeTmpGitRepo();
144
+ try {
145
+ writeLog(tmp, SLUG, loadFixture({ stripFailSentinel: false }));
146
+ const prdPath = writePrd(tmp, SLUG);
147
+ const verdict = await verifyRun({
148
+ runDir: tmp,
149
+ prdPath,
150
+ queueEntry: { slug: SLUG, status: 'running', cwd: repo },
151
+ allJobs: [],
152
+ committedDuringRun: false,
153
+ jobLandedCommitThisRun: LANDED_COMMIT,
154
+ exitCode: 0,
155
+ });
156
+ assert.equal(verdict.verdict, 'transcript_errors', `expected transcript_errors, got ${verdict.verdict}: ${verdict.reason}`);
157
+ assert.equal(verdict.downgradeTo, 'needs_review');
158
+ assert.equal(verdict.sentinel, 'fail');
159
+ } finally { rmdir(tmp); rmdir(repo); }
160
+ });
161
+
162
+ // (d) no landed-commit evidence at all → transcript_errors (evidence gate requires attribution).
163
+ test('no landed commit evidence → transcript_errors (evidence gate requires attribution)', async () => {
164
+ const tmp = makeTmpDir();
165
+ const repo = makeTmpGitRepo();
166
+ try {
167
+ writeLog(tmp, SLUG, loadFixture({ stripFailSentinel: true }));
168
+ const prdPath = writePrd(tmp, SLUG);
169
+ const verdict = await verifyRun({
170
+ runDir: tmp,
171
+ prdPath,
172
+ queueEntry: { slug: SLUG, status: 'running', cwd: repo },
173
+ allJobs: [],
174
+ committedDuringRun: false,
175
+ jobLandedCommitThisRun: null,
176
+ exitCode: 0,
177
+ });
178
+ assert.equal(verdict.verdict, 'transcript_errors', `expected transcript_errors, got ${verdict.verdict}: ${verdict.reason}`);
179
+ assert.equal(verdict.downgradeTo, 'needs_review');
180
+ } finally { rmdir(tmp); rmdir(repo); }
181
+ });
@@ -1140,7 +1140,7 @@ function noOpRunEvents(resultText) {
1140
1140
  test('no_verdict_sentinel: no tool_result issues, no sentinel, no commit → needs_review', async () => {
1141
1141
  const tmp = makeTmpDir();
1142
1142
  try {
1143
- const slug = '406-browser-capture-panel-ui';
1143
+ const slug = '406-capture-panel-ui';
1144
1144
  writeLog(tmp, slug, noOpRunEvents('I need clarification on which capture API to target before proceeding.'));
1145
1145
  const prdPath = writePrd(tmp, slug, '# Browser capture panel UI');
1146
1146
  const verdict = await verifyRun({
@@ -1160,7 +1160,7 @@ test('no_verdict_sentinel: no tool_result issues, no sentinel, no commit → nee
1160
1160
  test('no_verdict_sentinel guard: same run but committedDuringRun:true → stays clean', async () => {
1161
1161
  const tmp = makeTmpDir();
1162
1162
  try {
1163
- const slug = '406-browser-capture-panel-ui-committed';
1163
+ const slug = '406-capture-panel-ui-committed';
1164
1164
  writeLog(tmp, slug, noOpRunEvents('I need clarification on which capture API to target before proceeding.'));
1165
1165
  const prdPath = writePrd(tmp, slug, '# Browser capture panel UI');
1166
1166
  const verdict = await verifyRun({
@@ -1180,7 +1180,7 @@ test('no_verdict_sentinel guard: same run but committedDuringRun:true → stays
1180
1180
  test('no_verdict_sentinel guard: same run but with SCHEDULER_VERDICT: PASS sentinel + a commit → stays clean', async () => {
1181
1181
  const tmp = makeTmpDir();
1182
1182
  try {
1183
- const slug = '406-browser-capture-panel-ui-sentinel';
1183
+ const slug = '406-capture-panel-ui-sentinel';
1184
1184
  writeLog(tmp, slug, noOpRunEvents('All acceptance criteria verified.\nSCHEDULER_VERDICT: PASS'));
1185
1185
  const prdPath = writePrd(tmp, slug, '# Browser capture panel UI');
1186
1186
  const verdict = await verifyRun({
@@ -1634,7 +1634,7 @@ test('isMergeMainSlug matches the NN-prXXX-merge-main and NN-fix-prXXX-merge-mai
1634
1634
  assert.equal(isMergeMainSlug('565-pr188-merge-main'), true);
1635
1635
  assert.equal(isMergeMainSlug('571-fix-pr141-merge-main'), true);
1636
1636
  assert.equal(isMergeMainSlug('523-fix-bounded-fix-plan-retry'), false);
1637
- assert.equal(isMergeMainSlug('406-browser-capture-panel-ui'), false);
1637
+ assert.equal(isMergeMainSlug('406-capture-panel-ui'), false);
1638
1638
  assert.equal(isMergeMainSlug(''), false);
1639
1639
  assert.equal(isMergeMainSlug(undefined), false);
1640
1640
  });
@@ -1727,7 +1727,7 @@ test('merge-main exemption: gh call throws → falls through to pass_no_commit,
1727
1727
  test('merge-main exemption: non-merge-main slug never attempts a gh call', async () => {
1728
1728
  const tmp = makeTmpDir();
1729
1729
  try {
1730
- const slug = '406-browser-capture-panel-ui';
1730
+ const slug = '406-capture-panel-ui';
1731
1731
  writeLog(tmp, slug, noOpRunEvents('All acceptance criteria verified.\nSCHEDULER_VERDICT: PASS'));
1732
1732
  const prdPath = writePrd(tmp, slug, '# Browser capture panel UI');
1733
1733
  let ghCalls = 0;
@@ -6,7 +6,7 @@
6
6
  * is types-only (erased at build time, nothing to import at test runtime), so
7
7
  * each consumer keeps its own hand-written mirror of the status enum:
8
8
  * - src/preload/api.d.ts's `ScheduleJobStatus` union
9
- * - src/renderer/components/ui/StatusBadge.tsx's `JobStatus` union
9
+ * - src/renderer/lib/jobStatus.ts's `JobStatus` union
10
10
  * - src/renderer/components/SchedulePanel.tsx's `FilterStatus` union
11
11
  * (which also carries the UI-only 'all' meta-value)
12
12
  *
@@ -52,8 +52,8 @@ test('src/preload/api.d.ts ScheduleJobStatus matches JOB_STATUSES', () => {
52
52
  expect(sorted(values)).toEqual(sorted(JOB_STATUSES));
53
53
  });
54
54
 
55
- test('src/renderer/components/ui/StatusBadge.tsx JobStatus matches JOB_STATUSES', () => {
56
- const source = readSource('src/renderer/components/ui/StatusBadge.tsx');
55
+ test('src/renderer/lib/jobStatus.ts JobStatus matches JOB_STATUSES', () => {
56
+ const source = readSource('src/renderer/lib/jobStatus.ts');
57
57
  const values = extractQuotedList(source, /export type JobStatus = ([^\n]+)/);
58
58
  expect(sorted(values)).toEqual(sorted(JOB_STATUSES));
59
59
  });
@@ -24,8 +24,8 @@ function uniqueSlug(label) {
24
24
  }
25
25
 
26
26
  function readAuditRecordsForSlug(slug) {
27
- if (!fs.existsSync(auditLog.AUDIT_LOG_PATH)) return [];
28
- return fs.readFileSync(auditLog.AUDIT_LOG_PATH, 'utf8')
27
+ if (!fs.existsSync(auditLog.auditLogPath())) return [];
28
+ return fs.readFileSync(auditLog.auditLogPath(), 'utf8')
29
29
  .split('\n')
30
30
  .filter(Boolean)
31
31
  .map((line) => {
@@ -0,0 +1,143 @@
1
+ /**
2
+ * scheduler-adopted-run-supervision.test.cjs — an executor the boot spared
3
+ * (`adoptedAtBoot`) is supervised like a freshly spawned one: budget, idle-tail
4
+ * and deadman re-armed from the durable record, quietMachine lease re-acquired,
5
+ * and a budget kill parks needs_review/budget_exceeded via classifyBudgetKill.
6
+ *
7
+ * Run: timeout 120 npx vitest run src/main/__tests__/scheduler-adopted-run-supervision.test.cjs
8
+ */
9
+
10
+ 'use strict';
11
+
12
+ import { test, expect, vi, beforeEach, afterEach } from 'vitest';
13
+ const { superviseAdoptedRuns, evaluateAdoptedRun } = require('../lib/adoptedRunSupervisor.cjs');
14
+ const quietMachineLease = require('../lib/quietMachineLease.cjs');
15
+ const { classifyBudgetKill } = require('../scheduler.cjs');
16
+
17
+ const MIN = 60_000;
18
+ const T0 = Date.parse('2026-09-18T12:00:00Z');
19
+
20
+ beforeEach(() => {
21
+ vi.useFakeTimers();
22
+ vi.setSystemTime(T0);
23
+ quietMachineLease.__resetForTests();
24
+ });
25
+ afterEach(() => {
26
+ vi.useRealTimers();
27
+ quietMachineLease.__resetForTests();
28
+ });
29
+
30
+ function harness({ rows, records, logMtimeMs = () => Date.now(), alive = () => true }) {
31
+ const kills = [];
32
+ const stamps = [];
33
+ const supervised = [];
34
+ const registry = new Map();
35
+ const ctx = {
36
+ registry,
37
+ runDir: (j) => `/runs/${j.runId}`,
38
+ readRecord: (_dir, slug) => records[slug] ?? null,
39
+ lease: quietMachineLease,
40
+ markSupervised: async (j) => { supervised.push(j.slug); },
41
+ makeDeps: (j, record) => ({
42
+ logPath: `/runs/${j.runId}/${j.slug}.log`,
43
+ statLogMtimeMs: () => logMtimeMs(j.slug),
44
+ pidAlive: alive,
45
+ identityOf: () => record.identity,
46
+ isDifferentProcess: () => false,
47
+ killGroup: (pgid, signal) => { kills.push({ slug: j.slug, pgid, signal }); },
48
+ stampKill: async (kind, reason) => { stamps.push({ slug: j.slug, kind, reason }); },
49
+ checkIntervalMs: MIN,
50
+ sigkillAfterMs: 30_000,
51
+ defaultMaxDurationMs: 4 * 60 * MIN,
52
+ }),
53
+ };
54
+ return { ctx, kills, stamps, supervised, registry, run: () => superviseAdoptedRuns(rows, ctx) };
55
+ }
56
+
57
+ const row = (slug, extra = {}) => ({ slug, status: 'running', runId: 'r1', adoptedAtBoot: new Date(T0).toISOString(), ...extra });
58
+ const record = (slug, extra = {}) => ({
59
+ slug, pid: 4242, pgid: 4242, identity: { startTicks: 1 }, startedAt: T0, budgetMs: 45 * MIN, maxDurationMs: null, idleKillMs: 20 * MIN, ...extra,
60
+ });
61
+
62
+ test('an adopted run past its budget is killed and its budget kill classifies needs_review/budget_exceeded', async () => {
63
+ const h = harness({ rows: [row('over')], records: { over: record('over', { startedAt: T0 - 50 * MIN }) } });
64
+ expect(await h.run()).toEqual(['over']);
65
+ await vi.advanceTimersByTimeAsync(0);
66
+ expect(h.stamps).toHaveLength(1);
67
+ expect(h.stamps[0].kind).toBe('budget');
68
+ expect(h.kills).toEqual([{ slug: 'over', pgid: 4242, signal: 'SIGTERM' }]);
69
+ // SIGKILL follows if the group survives SIGTERM.
70
+ await vi.advanceTimersByTimeAsync(30_000);
71
+ expect(h.kills.map((k) => k.signal)).toEqual(['SIGTERM', 'SIGKILL']);
72
+ // Same classifier as a native budget kill.
73
+ const outcome = classifyBudgetKill({ killedByWatchdog: h.stamps[0].kind, budgetKillReason: h.stamps[0].reason, exitCode: null }, null);
74
+ expect(outcome.status).toBe('needs_review');
75
+ expect(outcome.reason).toMatch(/wall-clock budget exceeded/);
76
+ });
77
+
78
+ test('a run within budget is untouched until the interval crosses the budget', async () => {
79
+ const h = harness({ rows: [row('ok')], records: { ok: record('ok', { startedAt: T0 - 10 * MIN }) } });
80
+ await h.run();
81
+ await vi.advanceTimersByTimeAsync(5 * MIN);
82
+ expect(h.kills).toEqual([]);
83
+ expect(h.stamps).toEqual([]);
84
+ // 10m + 36m elapsed > 45m budget: the re-armed interval now fires.
85
+ await vi.advanceTimersByTimeAsync(31 * MIN);
86
+ expect(h.stamps.map((s) => s.kind)).toEqual(['budget']);
87
+ expect(h.kills[0].signal).toBe('SIGTERM');
88
+ });
89
+
90
+ test('idle-tail: a stalled log past idleKillMs is SIGTERMed; deadman SIGKILLs at maxDurationMs', async () => {
91
+ const idle = harness({
92
+ rows: [row('idle')], records: { idle: record('idle', { startedAt: T0 - 5 * MIN }) },
93
+ logMtimeMs: () => T0 - 25 * MIN,
94
+ });
95
+ await idle.run();
96
+ await vi.advanceTimersByTimeAsync(0);
97
+ expect(idle.stamps.map((s) => s.kind)).toEqual(['idle-tail']);
98
+ expect(idle.kills[0].signal).toBe('SIGTERM');
99
+
100
+ const dead = harness({
101
+ rows: [row('dm')], records: { dm: record('dm', { startedAt: T0 - 5 * MIN, budgetMs: null, maxDurationMs: 4 * 60 * MIN }) },
102
+ });
103
+ await dead.run();
104
+ await vi.advanceTimersByTimeAsync(4 * 60 * MIN);
105
+ expect(dead.stamps.map((s) => s.kind)).toEqual(['deadman']);
106
+ expect(dead.kills.map((k) => k.signal)).toEqual(['SIGKILL']);
107
+ });
108
+
109
+ test('a null record.maxDurationMs falls back to the native 4h deadman', () => {
110
+ const rec = record('x', { budgetMs: null, startedAt: T0 - 5 * 60 * MIN });
111
+ expect(evaluateAdoptedRun(rec, { now: T0, logMtimeMs: T0, defaultMaxDurationMs: 4 * 60 * MIN })?.kind).toBe('deadman');
112
+ });
113
+
114
+ test('the quietMachine lease is re-acquired, supervisedAt stamped once, and an exempt (null budget) run is not budget-killed', async () => {
115
+ const h = harness({
116
+ rows: [row('quiet', { quietMachine: true })],
117
+ records: { quiet: record('quiet', { budgetMs: null, startedAt: T0 - 90 * MIN }) },
118
+ });
119
+ expect(await h.run()).toEqual(['quiet']);
120
+ expect(quietMachineLease.holder()).toBe('quiet');
121
+ expect(h.supervised).toEqual(['quiet']);
122
+ await vi.advanceTimersByTimeAsync(10 * MIN);
123
+ expect(h.kills).toEqual([]);
124
+ // A second dispatch-loop pass does not re-arm or re-stamp.
125
+ expect(await h.run()).toEqual([]);
126
+ expect(h.supervised).toEqual(['quiet']);
127
+ });
128
+
129
+ test('a pid that is gone is never signalled; rows that stop running drop their supervisor', async () => {
130
+ const h = harness({
131
+ rows: [row('gone')], records: { gone: record('gone', { startedAt: T0 - 90 * MIN }) }, alive: () => false,
132
+ });
133
+ await h.run();
134
+ await vi.advanceTimersByTimeAsync(2 * MIN);
135
+ expect(h.kills).toEqual([]);
136
+ expect(h.stamps).toEqual([]);
137
+
138
+ const live = harness({ rows: [row('fin')], records: { fin: record('fin') } });
139
+ await live.run();
140
+ expect(live.registry.size).toBe(1);
141
+ await superviseAdoptedRuns([{ slug: 'fin', status: 'completed', runId: 'r1' }], live.ctx);
142
+ expect(live.registry.size).toBe(0);
143
+ });
@@ -1,12 +1,12 @@
1
1
  /**
2
2
  * scheduler-autofix-select.test.cjs — unit tests for selectAutoFixTargets.
3
3
  *
4
- * Run: timeout 120 node --test src/main/__tests__/scheduler-autofix-select.test.cjs
4
+ * Run: timeout 300 npx vitest run src/main/__tests__/scheduler-autofix-select.test.cjs
5
5
  */
6
6
 
7
7
  'use strict';
8
8
 
9
- const { test } = require('node:test');
9
+ import { test } from 'vitest';
10
10
  const assert = require('node:assert/strict');
11
11
  const {
12
12
  selectAutoFixTargets, isUnresolvableNeedsReview, isRescanCandidate,
@@ -1,12 +1,12 @@
1
1
  /**
2
2
  * scheduler-autopromote.test.cjs — unit tests for isPromotableOriginal.
3
3
  *
4
- * Run: timeout 120 node --test src/main/__tests__/scheduler-autopromote.test.cjs
4
+ * Run: timeout 300 npx vitest run src/main/__tests__/scheduler-autopromote.test.cjs
5
5
  */
6
6
 
7
7
  'use strict';
8
8
 
9
- const { test } = require('node:test');
9
+ import { test } from 'vitest';
10
10
  const assert = require('node:assert/strict');
11
11
  const { isPromotableOriginal, healTargetForFix } = require('../scheduler.cjs');
12
12
 
@@ -26,6 +26,7 @@ import { test, expect, beforeAll, afterAll, afterEach } from 'vitest';
26
26
  const fs = require('node:fs');
27
27
  const os = require('node:os');
28
28
  const path = require('node:path');
29
+ const claudeStub = require('../../../tests/helpers/claudeStub.cjs');
29
30
 
30
31
  let tmpHome;
31
32
  let originalHome;
@@ -53,8 +54,7 @@ afterEach(() => {
53
54
  // Stub `claude` binary: dumps the two env vars it received into a marker
54
55
  // file, then emits a stream-json success result and exits 0.
55
56
  function writeClaudeStub() {
56
- const stubPath = path.join(os.tmpdir(), `sm-claude-stub-env-${process.pid}-${Math.floor(Math.random() * 1e9)}.cjs`);
57
- const body = `
57
+ return claudeStub.writeClaudeStub({ body: `
58
58
  const fs = require('fs');
59
59
  const path = require('path');
60
60
  fs.writeFileSync(path.join(process.cwd(), 'env.marker'), JSON.stringify({
@@ -63,9 +63,7 @@ function writeClaudeStub() {
63
63
  }), 'utf8');
64
64
  process.stdout.write(JSON.stringify({ type: 'result', subtype: 'success', result: 'ok' }) + '\\n');
65
65
  process.exit(0);
66
- `;
67
- fs.writeFileSync(stubPath, `#!${process.execPath}\n${body}\n`, { mode: 0o755 });
68
- return stubPath;
66
+ ` });
69
67
  }
70
68
 
71
69
  test('BASH_MAX_TIMEOUT_MS stays strictly below IDLE_OUTPUT_KILL_MS with real margin', () => {