claude-code-session-manager 0.87.0 → 0.88.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 (290) hide show
  1. package/README.md +26 -53
  2. package/dist/assets/{AgentLibrary-DyLWzZDf.js → AgentLibrary-BZ6IY2g1.js} +1 -1
  3. package/dist/assets/{DataModel--mISIJ6h.js → DataModel-dAoGAODX.js} +1 -1
  4. package/dist/assets/{History-C2ahUXTg.js → History-CF157HyX.js} +1 -1
  5. package/dist/assets/{Hooks-BiC6oyR2.js → Hooks-CCPPipBk.js} +1 -1
  6. package/dist/assets/{HostBilko-BPleEOld.js → HostBilko-Dlmz2m06.js} +1 -1
  7. package/dist/assets/{Library-Dc8Qst1R.js → Library-BaXcboWX.js} +1 -1
  8. package/dist/assets/{ListDetail-DIXh-OLX.js → ListDetail-CoUCzG5S.js} +1 -1
  9. package/dist/assets/{MarkdownEditor-C90bkLXK.js → MarkdownEditor-DTg3JlOT.js} +1 -1
  10. package/dist/assets/{McpServers-DqcbLOLZ.js → McpServers-DdjY1sA3.js} +1 -1
  11. package/dist/assets/{Memory-CW62MXlh.js → Memory-BJ3Jlw0C.js} +1 -1
  12. package/dist/assets/{Panel-Bw1FhRuF.js → Panel-ygRSgc41.js} +1 -1
  13. package/dist/assets/{Permissions-BcUC-5y8.js → Permissions-m0KAnccF.js} +1 -1
  14. package/dist/assets/{Plugins-BnKx9flD.js → Plugins-CJRl6B3Q.js} +2 -2
  15. package/dist/assets/{ProvenanceBadge-Bw5vNVPT.js → ProvenanceBadge-w8cnlVvW.js} +1 -1
  16. package/dist/assets/{SaveBar-CWr0O_w-.js → SaveBar-BpyPcUIk.js} +1 -1
  17. package/dist/assets/{Scheduler-DYdLuUqq.js → Scheduler-D2ppxSfy.js} +7 -7
  18. package/dist/assets/{ScopeSwitcher-CrBLbg8s.js → ScopeSwitcher-EyuIwt-j.js} +1 -1
  19. package/dist/assets/{Settings-DluB-vN1.js → Settings-BHSGGFEc.js} +1 -1
  20. package/dist/assets/{SkillReferenceGraph-CHLSseay.js → SkillReferenceGraph-BNaZ2JCF.js} +1 -1
  21. package/dist/assets/{Skills-gNdo_HNK.js → Skills-Dl3q75QV.js} +1 -1
  22. package/dist/assets/{SystemPrompt-Cru05-Ia.js → SystemPrompt-Bg6F-Cyp.js} +1 -1
  23. package/dist/assets/{TagLibrary-DNHY0xou.js → TagLibrary-BSutflCy.js} +1 -1
  24. package/dist/assets/{TiptapBody-I4lmbCgP.js → TiptapBody-CXih4xrc.js} +1 -1
  25. package/dist/assets/{Toggle-bWMHjmRh.js → Toggle-2qtUHhA9.js} +1 -1
  26. package/dist/assets/{index-fc_JjdxL.js → index-B3emy2oI.js} +356 -356
  27. package/dist/assets/{index-DV3PorRY.css → index-BHTX4OTc.css} +1 -1
  28. package/dist/assets/{settingsSchema-BfhtZnGD.js → settingsSchema-CdzxaqwM.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 +11 -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-starve-escalation.test.cjs +4 -4
  124. package/src/main/__tests__/scheduler-stranded-autofix-park.test.cjs +245 -0
  125. package/src/main/__tests__/scheduler-supervisor-record.test.cjs +81 -0
  126. package/src/main/__tests__/scheduler-tick-cancel-token.test.cjs +2 -2
  127. package/src/main/__tests__/scheduler-tick-wedge.test.cjs +172 -0
  128. package/src/main/__tests__/scheduler-transient-failure.test.cjs +2 -2
  129. package/src/main/__tests__/scheduler-worktree-cap-defer.test.cjs +37 -11
  130. package/src/main/__tests__/scheduler-worktree-exec-cwd.test.cjs +3 -5
  131. package/src/main/__tests__/usageSingleFlight.test.cjs +157 -0
  132. package/src/main/__tests__/workTypeLibrary.test.cjs +1 -1
  133. package/src/main/bilkoHost.cjs +16 -7
  134. package/src/main/build-info.json +8 -0
  135. package/src/main/chatRunner.cjs +4 -2
  136. package/src/main/config.cjs +2 -3
  137. package/src/main/docEdit.cjs +4 -2
  138. package/src/main/health.cjs +267 -51
  139. package/src/main/heapSnapshot.cjs +2 -2
  140. package/src/main/historyAggregator.cjs +1 -1
  141. package/src/main/index.cjs +61 -10
  142. package/src/main/ipcSchemas.cjs +6 -17
  143. package/src/main/lib/__tests__/auditLog.test.cjs +38 -0
  144. package/src/main/lib/__tests__/buildIdentity.test.cjs +121 -0
  145. package/src/main/lib/__tests__/cwdClassify.test.cjs +111 -0
  146. package/src/main/lib/__tests__/definitionOfDoneSequence.test.cjs +95 -0
  147. package/src/main/lib/__tests__/delegationReadiness.test.cjs +1 -1
  148. package/src/main/lib/__tests__/dispatchLoop.test.cjs +63 -0
  149. package/src/main/lib/__tests__/gateFixtures.json +20 -0
  150. package/src/main/lib/__tests__/gitWorktree.test.cjs +8 -1
  151. package/src/main/lib/__tests__/instanceLock.test.cjs +93 -7
  152. package/src/main/lib/__tests__/jobSupervisorRecord.test.cjs +78 -0
  153. package/src/main/lib/__tests__/jobWorktreeBootLive.test.cjs +11 -0
  154. package/src/main/lib/__tests__/localAdminHttp.test.cjs +1 -1
  155. package/src/main/lib/__tests__/mcpToolCatalog.test.cjs +6 -1
  156. package/src/main/lib/__tests__/procIdentity.test.cjs +119 -0
  157. package/src/main/lib/__tests__/procName.test.cjs +92 -0
  158. package/src/main/lib/__tests__/queueStoreMachineStateRecovery.test.cjs +67 -0
  159. package/src/main/lib/__tests__/schedulerMcpServerHelp.test.cjs +6 -7
  160. package/src/main/lib/__tests__/schedulerMcpServerProjectHome.test.cjs +37 -204
  161. package/src/main/lib/__tests__/schedulerPaths.test.cjs +226 -0
  162. package/src/main/lib/__tests__/schedulerPathsWorktree.test.cjs +94 -0
  163. package/src/main/lib/__tests__/schedulerRuntimeState.test.cjs +56 -0
  164. package/src/main/lib/__tests__/sessionSlots.test.cjs +45 -0
  165. package/src/main/lib/__tests__/telemetryBacklog.test.cjs +1 -1
  166. package/src/main/lib/__tests__/upgradeDrain.test.cjs +130 -0
  167. package/src/main/lib/__tests__/usageCircuit.test.cjs +61 -0
  168. package/src/main/lib/__tests__/watchdog-helpers.test.cjs +63 -0
  169. package/src/main/lib/__tests__/watchdog-relaunch.test.cjs +73 -0
  170. package/src/main/lib/activeIndexRebuild.cjs +1 -3
  171. package/src/main/lib/activeSessions.cjs +20 -88
  172. package/src/main/lib/adoptedRunSupervisor.cjs +136 -0
  173. package/src/main/lib/agentModelResolve.cjs +33 -1
  174. package/src/main/lib/agentPersonaSchema.cjs +2 -2
  175. package/src/main/lib/auditLog.cjs +30 -5
  176. package/src/main/lib/buildIdentity.cjs +113 -0
  177. package/src/main/lib/classifyPromptTicket.cjs +3 -2
  178. package/src/main/lib/claudeBin.cjs +37 -2
  179. package/src/main/lib/cleanEnv.cjs +27 -1
  180. package/src/main/lib/credentials.cjs +4 -2
  181. package/src/main/lib/cwdClassify.cjs +185 -0
  182. package/src/main/lib/definitionOfDone.cjs +296 -52
  183. package/src/main/lib/dispatchLoop.cjs +40 -0
  184. package/src/main/lib/effectiveModelInfo.cjs +9 -10
  185. package/src/main/lib/ephemeralCwd.cjs +7 -28
  186. package/src/main/lib/gitWorktree.cjs +95 -8
  187. package/src/main/lib/guardShims.cjs +3 -3
  188. package/src/main/lib/historyRollup.cjs +6 -7
  189. package/src/main/lib/instanceLock.cjs +32 -5
  190. package/src/main/lib/jobSupervisorRecord.cjs +147 -0
  191. package/src/main/lib/jobWorktreeBootLive.cjs +9 -5
  192. package/src/main/lib/localAdminHttp.cjs +9 -27
  193. package/src/main/lib/mcpToolCatalog.cjs +29 -77
  194. package/src/main/lib/opsOwnership.cjs +27 -19
  195. package/src/main/lib/prdAuthoringSeed.cjs +36 -0
  196. package/src/main/lib/prdLocations.cjs +48 -1
  197. package/src/main/lib/procIdentity.cjs +126 -0
  198. package/src/main/lib/procName.cjs +98 -0
  199. package/src/main/lib/projectHomeAdminRoutes.cjs +56 -327
  200. package/src/main/lib/queueHistory.cjs +8 -7
  201. package/src/main/lib/queueStore.cjs +57 -37
  202. package/src/main/lib/quietMachineLease.cjs +19 -2
  203. package/src/main/lib/reservationExpiry.cjs +30 -0
  204. package/src/main/lib/runClaudeP.cjs +4 -2
  205. package/src/main/lib/runLogRetention.cjs +3 -2
  206. package/src/main/lib/scheduleJobSchema.cjs +2 -2
  207. package/src/main/lib/scheduleJobTransitions.cjs +29 -2
  208. package/src/main/lib/schedulerBatch.cjs +27 -2
  209. package/src/main/lib/schedulerPaths.cjs +175 -0
  210. package/src/main/lib/schedulerRuntimeState.cjs +59 -0
  211. package/src/main/lib/sessionSlots.cjs +47 -9
  212. package/src/main/lib/smProcNames.cjs +51 -0
  213. package/src/main/lib/upgradeDrain.cjs +188 -0
  214. package/src/main/lib/usageCircuit.cjs +53 -6
  215. package/src/main/lib/watchdogHelpers.cjs +70 -37
  216. package/src/main/lib/withTimeout.cjs +33 -0
  217. package/src/main/mcpStatus.cjs +4 -2
  218. package/src/main/pluginInstall.cjs +6 -2
  219. package/src/main/projectPages.cjs +41 -145
  220. package/src/main/pty.cjs +8 -1
  221. package/src/main/queueOps.cjs +10 -10
  222. package/src/main/runVerify.cjs +69 -3
  223. package/src/main/scheduler.cjs +1598 -381
  224. package/src/main/seedAgentPersonas.cjs +1 -1
  225. package/src/main/seedDevPlugin.cjs +1 -1
  226. package/src/main/seedSchedulerMcp.cjs +7 -6
  227. package/src/main/seedStatus.cjs +1 -1
  228. package/src/main/supervisor.cjs +5 -3
  229. package/src/main/usage.cjs +126 -62
  230. package/src/preload/api.d.ts +18 -64
  231. package/src/preload/index.cjs +2 -4
  232. package/src/seed/agents/project-home-builder.md +31 -48
  233. package/screenshots/.gitkeep +0 -0
  234. package/screenshots/README-screenshots.md +0 -13
  235. package/src/main/lib/projectPageSummarySchema.cjs +0 -181
  236. package/src/main/teams.cjs +0 -95
  237. package/src/main/templates/project-pages-catalog.json +0 -741
  238. package/src/main/templates/project-pages-default-home.html +0 -123
  239. package/src/main/templates/project-pages-pipeline.md +0 -417
  240. package/src/seed/prompts/code-review/ac-coverage-check.md +0 -8
  241. package/src/seed/prompts/code-review/correctness-only.md +0 -8
  242. package/src/seed/prompts/code-review/full-spectrum-high.md +0 -8
  243. package/src/seed/prompts/code-review/hallucination-check.md +0 -8
  244. package/src/seed/prompts/code-review/public-api-compat.md +0 -8
  245. package/src/seed/prompts/code-review/readability-naming.md +0 -8
  246. package/src/seed/prompts/debugging/bug-as-failing-test.md +0 -8
  247. package/src/seed/prompts/debugging/git-bisect-regression.md +0 -8
  248. package/src/seed/prompts/debugging/instrument-intermittent-bug.md +0 -8
  249. package/src/seed/prompts/debugging/localize-pipeline-failure.md +0 -8
  250. package/src/seed/prompts/debugging/reproduce-then-diagnose.md +0 -8
  251. package/src/seed/prompts/documentation/adr-from-change.md +0 -8
  252. package/src/seed/prompts/documentation/module-readme.md +0 -8
  253. package/src/seed/prompts/documentation/onboarding-plan.md +0 -8
  254. package/src/seed/prompts/documentation/refresh-claude-md.md +0 -8
  255. package/src/seed/prompts/documentation/tsdoc-public-exports.md +0 -8
  256. package/src/seed/prompts/git-pr/conventional-commit.md +0 -8
  257. package/src/seed/prompts/git-pr/draft-pr-title-body.md +0 -8
  258. package/src/seed/prompts/git-pr/pre-commit-safety-sweep.md +0 -8
  259. package/src/seed/prompts/git-pr/release-notes-block.md +0 -8
  260. package/src/seed/prompts/git-pr/split-large-pr.md +0 -8
  261. package/src/seed/prompts/performance/bundle-startup-audit.md +0 -8
  262. package/src/seed/prompts/performance/complexity-audit.md +0 -8
  263. package/src/seed/prompts/performance/cpu-profile-hot-path.md +0 -8
  264. package/src/seed/prompts/performance/db-query-plan-review.md +0 -8
  265. package/src/seed/prompts/performance/memory-leak-hunt.md +0 -8
  266. package/src/seed/prompts/qa/api-contract-tests.md +0 -8
  267. package/src/seed/prompts/qa/e2e-critical-path.md +0 -8
  268. package/src/seed/prompts/qa/failing-test-for-bug.md +0 -8
  269. package/src/seed/prompts/qa/find-missing-test-coverage.md +0 -8
  270. package/src/seed/prompts/qa/stabilize-flaky-test.md +0 -8
  271. package/src/seed/prompts/qa/tdd-red-first.md +0 -8
  272. package/src/seed/prompts/qa/visual-regression-review.md +0 -8
  273. package/src/seed/prompts/qa/wcag-axe-scan.md +0 -8
  274. package/src/seed/prompts/refactoring/dead-code-sweep.md +0 -8
  275. package/src/seed/prompts/refactoring/extract-duplicated-pattern.md +0 -8
  276. package/src/seed/prompts/refactoring/modernize-legacy-file.md +0 -8
  277. package/src/seed/prompts/refactoring/reduce-cyclomatic-complexity.md +0 -8
  278. package/src/seed/prompts/refactoring/tighten-module-boundaries.md +0 -8
  279. package/src/seed/prompts/security/authz-audit.md +0 -8
  280. package/src/seed/prompts/security/crypto-correctness.md +0 -8
  281. package/src/seed/prompts/security/cwe-top-25-hunt.md +0 -8
  282. package/src/seed/prompts/security/dependency-audit.md +0 -8
  283. package/src/seed/prompts/security/ipc-boundary-hardening.md +0 -8
  284. package/src/seed/prompts/security/owasp-top-10-staged-diff.md +0 -8
  285. package/src/seed/prompts/security/secret-credential-scan.md +0 -8
  286. package/web/README.md +0 -41
  287. package/web/project-pages/logic/dist/logic.cjs +0 -4709
  288. package/web/project-pages/render.cjs +0 -70
  289. package/web/project-pages/renderer/dist/renderer.cjs +0 -18900
  290. package/web/project-pages/validate-summary.cjs +0 -62
@@ -0,0 +1,157 @@
1
+ /**
2
+ * usageSingleFlight.test.cjs — usage.cjs's fetchUsage() is the single owner
3
+ * of caching, single-flight de-duplication, and the shared usageCircuit for
4
+ * the /api/oauth/usage meter. Before this, the renderer's billing:fetch IPC
5
+ * handler owned the 30s OK-cache alone and the scheduler's pollLoop called
6
+ * fetchUsage() directly with no cache/dedup — two independent request
7
+ * streams against a rate-limiting endpoint, and a success on one path never
8
+ * reset the other's failure streak. This suite proves: (1) concurrent
9
+ * fetchUsage() callers coalesce into exactly one network fetch, (2) a
10
+ * success resets the shared circuit's streak regardless of which "caller"
11
+ * triggered the preceding failures, and (3) a 429's Retry-After suppresses
12
+ * the next request until it elapses.
13
+ *
14
+ * Never hits the live billing endpoint — global.fetch is stubbed throughout,
15
+ * and HOME is pointed at a scratch dir so no real credentials/cache file on
16
+ * this machine is ever read.
17
+ *
18
+ * usage.cjs (and config.cjs/credentials.cjs, which it requires) are loaded
19
+ * via Node's own require() — this is a .cjs test, not transformed ESM — so
20
+ * vi.mock()/vi.resetModules() do NOT intercept them the way they would for
21
+ * an ESM import graph; both also bake os.homedir() into module-level
22
+ * constants at first require (config.cjs's allowedRoots/WRITE_PREFIXES,
23
+ * usage.cjs's CACHE_PATH, credentials.cjs's CREDS_PATH). So this suite
24
+ * follows historyRollup.test.cjs's pattern instead: swap $HOME to a fresh
25
+ * tmp dir with a valid, far-from-expiry fake credentials file, purge
26
+ * Node's require.cache for every affected module, and re-require fresh
27
+ * each test.
28
+ *
29
+ * Run: timeout 120 npx vitest run src/main/__tests__/usageSingleFlight.test.cjs
30
+ */
31
+
32
+ 'use strict';
33
+
34
+ import { test, expect, vi, beforeEach, afterEach } from 'vitest';
35
+ import fs from 'node:fs';
36
+ import os from 'node:os';
37
+ import path from 'node:path';
38
+
39
+ function jsonResponse(status, body, headers = {}) {
40
+ return {
41
+ status,
42
+ ok: status >= 200 && status < 300,
43
+ text: async () => JSON.stringify(body),
44
+ json: async () => body,
45
+ headers: { get: (k) => headers[k.toLowerCase()] ?? null },
46
+ };
47
+ }
48
+
49
+ const usagePayload = { five_hour: { utilization: 10, resets_at: null }, seven_day: { utilization: 10, resets_at: null }, seven_day_sonnet: null, seven_day_opus: null, extra_usage: null };
50
+
51
+ const MODULES_TO_RELOAD = [
52
+ '../usage.cjs',
53
+ '../config.cjs',
54
+ '../lib/credentials.cjs',
55
+ '../lib/usageCircuit.cjs',
56
+ ];
57
+
58
+ function purgeRequireCache() {
59
+ for (const m of MODULES_TO_RELOAD) {
60
+ try { delete require.cache[require.resolve(m)]; } catch { /* not loaded yet */ }
61
+ }
62
+ }
63
+
64
+ let realHome;
65
+ let tmpHome;
66
+
67
+ beforeEach(() => {
68
+ realHome = process.env.HOME;
69
+ tmpHome = fs.mkdtempSync(path.join(os.tmpdir(), 'usage-single-flight-test-'));
70
+ process.env.HOME = tmpHome;
71
+ fs.mkdirSync(path.join(tmpHome, '.claude'), { recursive: true });
72
+ fs.writeFileSync(
73
+ path.join(tmpHome, '.claude', '.credentials.json'),
74
+ JSON.stringify({
75
+ claudeAiOauth: {
76
+ accessToken: 'test-access-token',
77
+ refreshToken: 'test-refresh-token',
78
+ expiresAt: Date.now() + 1_000 * 60 * 60 * 24, // 24h out — never "expiring soon"
79
+ subscriptionType: 'max',
80
+ rateLimitTier: 'default_claude_max_20x',
81
+ },
82
+ }),
83
+ );
84
+ purgeRequireCache();
85
+ });
86
+
87
+ afterEach(() => {
88
+ process.env.HOME = realHome;
89
+ vi.unstubAllGlobals();
90
+ vi.restoreAllMocks();
91
+ purgeRequireCache();
92
+ fs.rmSync(tmpHome, { recursive: true, force: true });
93
+ });
94
+
95
+ test('two concurrent fetchUsage() calls produce exactly one HTTP fetch', async () => {
96
+ const { fetchUsage } = require('../usage.cjs');
97
+ let fetchCalls = 0;
98
+ vi.stubGlobal('fetch', vi.fn(async () => {
99
+ fetchCalls++;
100
+ // Slow enough that both callers are guaranteed to be in-flight together.
101
+ await new Promise((resolve) => setTimeout(resolve, 20));
102
+ return jsonResponse(200, usagePayload);
103
+ }));
104
+
105
+ const [a, b] = await Promise.all([fetchUsage(), fetchUsage()]);
106
+
107
+ expect(fetchCalls).toBe(1);
108
+ expect(a.kind).toBe('ok');
109
+ expect(b.kind).toBe('ok');
110
+ });
111
+
112
+ test('an ok result resets the shared circuit streak built up by prior failures', async () => {
113
+ const usage = require('../usage.cjs');
114
+ const circuit = usage.__usageCircuitForTest;
115
+
116
+ let call = 0;
117
+ vi.stubGlobal('fetch', vi.fn(async () => {
118
+ call++;
119
+ if (call <= 3) return jsonResponse(500, { error: 'boom' });
120
+ return jsonResponse(200, usagePayload);
121
+ }));
122
+
123
+ // Three consecutive transient failures — simulating the scheduler's poller
124
+ // failing repeatedly — trip the circuit open.
125
+ await usage.fetchUsage();
126
+ await usage.fetchUsage();
127
+ await usage.fetchUsage();
128
+ expect(circuit.state()).toBe('open');
129
+
130
+ // A later success — as if it came from the renderer's independent
131
+ // billing:fetch call, but reached through the SAME shared fetchUsage() —
132
+ // must reset the streak the scheduler's calls built up, since both
133
+ // callers share one circuit instance.
134
+ const ok = await usage.fetchUsage();
135
+ expect(ok.kind).toBe('ok');
136
+ expect(circuit.state()).toBe('closed');
137
+ });
138
+
139
+ test('a 429 with Retry-After suppresses the next request until it elapses', async () => {
140
+ const usage = require('../usage.cjs');
141
+
142
+ let fetchCalls = 0;
143
+ vi.stubGlobal('fetch', vi.fn(async () => {
144
+ fetchCalls++;
145
+ return jsonResponse(429, { error: { type: 'rate_limit_error' } }, { 'retry-after': '60' });
146
+ }));
147
+
148
+ const first = await usage.fetchUsage();
149
+ expect(first.kind).toBe('meter_rate_limited');
150
+ expect(first.retryAfterMs).toBe(60_000);
151
+
152
+ const second = await usage.fetchUsage();
153
+ expect(second.kind).toBe('meter_rate_limited');
154
+ expect(second.suppressed).toBe(true);
155
+ // No second network call — the suppression window hasn't elapsed.
156
+ expect(fetchCalls).toBe(1);
157
+ });
@@ -46,7 +46,7 @@ test('WORK_TYPES matches the tag ids declared in tagLibrary.ts TAG_LIBRARY, in o
46
46
  expect(tagMatches).toEqual(WORK_TYPES);
47
47
  });
48
48
 
49
- test('api.d.ts tag unions (line ~1166 and ~1687) both contain exactly the same 6 values', () => {
49
+ test('api.d.ts tag unions both contain exactly the same 6 values', () => {
50
50
  const src = fs.readFileSync(API_D_TS_PATH, 'utf8');
51
51
  const unionMatches = [...src.matchAll(/tag\?:\s*((?:'[^']+'\s*\|?\s*)+);/g)].map((m) =>
52
52
  m[1]
@@ -46,6 +46,10 @@ const {
46
46
  const WRITER = 'bilko-host';
47
47
  const PROJECT_PAGE_LENSES = new Set(['home', 'marketing', 'feature', 'architecture']);
48
48
 
49
+ function homePagePath(cwd) {
50
+ return opsPath(cwd, 'project-pages', 'home.html');
51
+ }
52
+
49
53
  function bilkoHostDir(cwd) {
50
54
  return opsPath(cwd, 'bilko-host');
51
55
  }
@@ -103,9 +107,10 @@ async function writeDocuments(cwd, documents) {
103
107
  async function get({ cwd }) {
104
108
  const realCwd = config.validatePath(cwd);
105
109
 
106
- const marketingResult = await config.readText(
107
- opsPath(realCwd, 'project-pages', 'output', 'marketing.html'),
108
- );
110
+ // The single Project Home page (project-pages/home.html) seeds the root
111
+ // document. `hasMarketingPage` is the renderer's legacy field name for
112
+ // "a root-document source page exists".
113
+ const marketingResult = await config.readText(homePagePath(realCwd));
109
114
  const pkgResult = await config.readJson(path.join(realCwd, 'package.json'));
110
115
  const pkg = pkgResult.exists && !pkgResult.parseError ? pkgResult.data : null;
111
116
 
@@ -142,15 +147,15 @@ async function get({ cwd }) {
142
147
  };
143
148
  }
144
149
 
145
- /** Seeds documents.json with a single root document (the Marketing Project Page) if it doesn't exist yet. */
150
+ /** Seeds documents.json with a single root document (the Project Home page, home.html) if it doesn't exist yet. */
146
151
  async function ensureSeededDocuments(cwd) {
147
152
  const existing = await readDocuments(cwd);
148
153
  if (existing) return existing;
149
154
  const seeded = [{
150
155
  id: crypto.randomUUID(),
151
156
  subpath: '',
152
- title: 'Project (Marketing)',
153
- source: { kind: 'project-page-lens', lens: 'marketing' },
157
+ title: 'Project (Home)',
158
+ source: { kind: 'project-page-lens', lens: 'home' },
154
159
  addedAt: new Date().toISOString(),
155
160
  }];
156
161
  await writeDocuments(cwd, seeded);
@@ -215,8 +220,12 @@ async function removeDocument({ cwd, id }) {
215
220
 
216
221
  async function resolveDocumentHtml(cwd, doc) {
217
222
  if (doc.source.kind === 'project-page-lens') {
223
+ // Only `home` is generated now (one home.html); the other lens names
224
+ // are legacy and resolve to their old output/<lens>.html if one exists.
218
225
  const result = await config.readText(
219
- opsPath(cwd, 'project-pages', 'output', `${doc.source.lens}.html`),
226
+ doc.source.lens === 'home'
227
+ ? homePagePath(cwd)
228
+ : opsPath(cwd, 'project-pages', 'output', `${doc.source.lens}.html`),
220
229
  );
221
230
  if (!result.exists) {
222
231
  throw new Error(`document "${doc.title}" points at the ${doc.source.lens} Project Page, but it hasn't been generated yet`);
@@ -0,0 +1,8 @@
1
+ {
2
+ "version": "0.88.0",
3
+ "gitSha": "8b723b765b5cb9f867b2ae244011b54ab3c81ef0",
4
+ "gitShortSha": "8b723b7",
5
+ "gitBranch": "HEAD",
6
+ "builtAt": "2026-09-19T14:31:26.080Z",
7
+ "dirty": false
8
+ }
@@ -44,7 +44,7 @@
44
44
 
45
45
  const { spawn } = require('node:child_process');
46
46
  const { ipcMain } = require('electron');
47
- const { resolveClaudeBin } = require('./lib/claudeBin.cjs');
47
+ const { resolveClaudeBin, claudeSpawnTarget } = require('./lib/claudeBin.cjs');
48
48
  const { cleanChildEnv, pathWithUserBins } = require('./lib/cleanEnv.cjs');
49
49
  const { recordExchange } = require('./exchanges.cjs');
50
50
  const { classifyToolUse } = require('./lib/toolUseClassify.cjs');
@@ -571,7 +571,9 @@ function executeRun({ tabId, sessionId, prompt, cwd, resume, silent, onSilentRes
571
571
  // real-time NDJSON streaming; stderr piped for error-message capture.
572
572
  let child;
573
573
  try {
574
- child = spawn(claudeBin, args, {
574
+ const target = claudeSpawnTarget('chat', sessionId, claudeBin);
575
+ child = spawn(target.command, args, {
576
+ ...(target.argv0 ? { argv0: target.argv0 } : {}),
575
577
  cwd: execCwd,
576
578
  env: childEnv,
577
579
  stdio: ['ignore', 'pipe', 'pipe'],
@@ -148,9 +148,8 @@ function validateWrite(realAbs) {
148
148
  // (PRD 825; scheduler.cjs remote.writePrd)
149
149
  // project-brief projectBrief.cjs's brief.json (PRD 837)
150
150
  // bilko-host bilkoHost.cjs's bundle prep
151
- // project-pages projectHomeAdminRoutes.cjs's app-side render path
152
- // (summary.json/picks.json/output/*; PRD:
153
- // project-home-admin-routes)
151
+ // project-pages projectHomeAdminRoutes.cjs's project_home_write
152
+ // path (home.html)
154
153
  // Paths come from opsOwnership.opsPath (PRD 1082) — the one ops-root
155
154
  // resolver — which THROWS for an ephemeral root (a linked worktree that
156
155
  // pty.cjs registered as an allowed root for its spawn cwd, or
@@ -17,7 +17,8 @@
17
17
  const { ipcMain } = require('electron');
18
18
  const { spawn } = require('node:child_process');
19
19
  const crypto = require('node:crypto');
20
- const { resolveClaudeBin } = require('./lib/claudeBin.cjs');
20
+ const { resolveClaudeBin, claudeSpawnTarget } = require('./lib/claudeBin.cjs');
21
+ const { withProcRole } = require('./lib/cleanEnv.cjs');
21
22
  const { extractJson } = require('./lib/extractJson.cjs');
22
23
  const { assertInsideHome } = require('./lib/insideHome.cjs');
23
24
  const { expandHome } = require('./lib/expandHome.cjs');
@@ -99,7 +100,8 @@ function runClaude(prompt, { model = 'sonnet', timeoutMs = 90_000, systemPrompt
99
100
  // stdin MUST be closed ('ignore') — `claude -p` otherwise blocks waiting
100
101
  // for piped stdin and returns empty. SM_KG_INTERNAL=1 tells the
101
102
  // prompt-logging hook to skip this invocation.
102
- const child = spawn(bin, args, { env: { ...process.env, SM_KG_INTERNAL: '1' }, stdio: ['ignore', 'pipe', 'pipe'] });
103
+ const target = claudeSpawnTarget('aux', 'docedit', bin);
104
+ const child = spawn(target.command, args, { env: withProcRole({ ...process.env, SM_KG_INTERNAL: '1' }, 'aux'), stdio: ['ignore', 'pipe', 'pipe'], ...(target.argv0 ? { argv0: target.argv0 } : {}) });
103
105
  let out = '';
104
106
  let err = '';
105
107
  // Cap accumulated stdout — the model output shouldn't grow `out` without