peaks-loop 4.0.0-beta.9 → 4.0.3

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 (552) hide show
  1. package/CHANGELOG.md +572 -234
  2. package/README-en.md +67 -30
  3. package/README.md +67 -30
  4. package/agents/karpathy-reviewer.md +33 -0
  5. package/bin/peaks.js +0 -0
  6. package/dist/cli/cli-helpers.d.ts +31 -6
  7. package/dist/cli/cli-helpers.js +48 -25
  8. package/dist/cli/commands/_register.d.ts +3 -0
  9. package/dist/cli/commands/_register.js +139 -0
  10. package/dist/cli/commands/_super.d.ts +10 -0
  11. package/dist/cli/commands/_super.js +97 -0
  12. package/dist/cli/commands/adapter-commands-s2a.js +1 -1
  13. package/dist/cli/commands/asset-commands.js +33 -16
  14. package/dist/cli/commands/audit-commands.js +2 -2
  15. package/dist/cli/commands/autonomous-swarm-commands.d.ts +12 -0
  16. package/dist/cli/commands/autonomous-swarm-commands.js +120 -0
  17. package/dist/cli/commands/bee-commands.js +1 -1
  18. package/dist/cli/commands/capability-commands.js +1 -1
  19. package/dist/cli/commands/capability-worker-config-sc-commands.js +0 -2
  20. package/dist/cli/commands/changeset-commands.d.ts +13 -0
  21. package/dist/cli/commands/changeset-commands.js +46 -0
  22. package/dist/cli/commands/code-commands.d.ts +11 -1
  23. package/dist/cli/commands/code-commands.js +19 -699
  24. package/dist/cli/commands/code-job-shape-commands.d.ts +9 -0
  25. package/dist/cli/commands/code-job-shape-commands.js +164 -0
  26. package/dist/cli/commands/code-mode-gate-commands.d.ts +9 -0
  27. package/dist/cli/commands/code-mode-gate-commands.js +223 -0
  28. package/dist/cli/commands/code-review-commands.js +2 -2
  29. package/dist/cli/commands/code-run-command.d.ts +12 -0
  30. package/dist/cli/commands/code-run-command.js +68 -0
  31. package/dist/cli/commands/code-runtime-commands.d.ts +11 -0
  32. package/dist/cli/commands/code-runtime-commands.js +391 -0
  33. package/dist/cli/commands/codegraph-commands.js +1 -1
  34. package/dist/cli/commands/compact-command.js +53 -1
  35. package/dist/cli/commands/complexity-commands.js +1 -1
  36. package/dist/cli/commands/config-commands.js +4 -75
  37. package/dist/cli/commands/container-commands.d.ts +28 -0
  38. package/dist/cli/commands/container-commands.js +221 -0
  39. package/dist/cli/commands/context-commands.js +3 -2
  40. package/dist/cli/commands/core/artifacts-command.js +1 -1
  41. package/dist/cli/commands/core/binding-commands.js +1 -1
  42. package/dist/cli/commands/core/doctor-command.js +63 -3
  43. package/dist/cli/commands/core/memory-command.js +1 -1
  44. package/dist/cli/commands/core/profile-command.js +1 -1
  45. package/dist/cli/commands/core/proxy-command.js +1 -1
  46. package/dist/cli/commands/core/session-command.js +9 -7
  47. package/dist/cli/commands/core/skill-command.js +7 -4
  48. package/dist/cli/commands/core/standards-command.js +1 -1
  49. package/dist/cli/commands/cron-commands.d.ts +57 -0
  50. package/dist/cli/commands/cron-commands.js +257 -0
  51. package/dist/cli/commands/cron-scheduler-commands.d.ts +47 -0
  52. package/dist/cli/commands/cron-scheduler-commands.js +284 -0
  53. package/dist/cli/commands/dashboard-commands.d.ts +4 -0
  54. package/dist/cli/commands/dashboard-commands.js +10 -0
  55. package/dist/cli/commands/dashboard-long-run.d.ts +25 -0
  56. package/dist/cli/commands/dashboard-long-run.js +122 -0
  57. package/dist/cli/commands/dashboard-summary.d.ts +12 -0
  58. package/dist/cli/commands/dashboard-summary.js +58 -0
  59. package/dist/cli/commands/dispatch-commands.d.ts +0 -7
  60. package/dist/cli/commands/dispatch-commands.js +372 -8
  61. package/dist/cli/commands/dispatch-from-dag.js +4 -1
  62. package/dist/cli/commands/doc-commands.js +1 -1
  63. package/dist/cli/commands/ecc-commands.d.ts +30 -0
  64. package/dist/cli/commands/ecc-commands.js +119 -0
  65. package/dist/cli/commands/evolution-commands.js +1 -1
  66. package/dist/cli/commands/feedback-commands.js +1 -1
  67. package/dist/cli/commands/final-review-commands.js +1 -1
  68. package/dist/cli/commands/fixture-commands.js +1 -1
  69. package/dist/cli/commands/fork-commands.js +1 -1
  70. package/dist/cli/commands/gate-commands.js +128 -6
  71. package/dist/cli/commands/governance-classify-contract-commands.d.ts +21 -0
  72. package/dist/cli/commands/governance-classify-contract-commands.js +273 -0
  73. package/dist/cli/commands/heartbeat-commands.js +76 -5
  74. package/dist/cli/commands/heartbeat-watch-command.d.ts +31 -0
  75. package/dist/cli/commands/heartbeat-watch-command.js +146 -0
  76. package/dist/cli/commands/hook-handle.js +1 -1
  77. package/dist/cli/commands/hooks-commands.js +126 -10
  78. package/dist/cli/commands/ide-commands.js +1 -1
  79. package/dist/cli/commands/impact-commands.js +1 -1
  80. package/dist/cli/commands/job-commands.js +34 -1
  81. package/dist/cli/commands/lease-metrics-commands.d.ts +67 -0
  82. package/dist/cli/commands/lease-metrics-commands.js +231 -0
  83. package/dist/cli/commands/lease-stats-commands.d.ts +65 -0
  84. package/dist/cli/commands/lease-stats-commands.js +107 -0
  85. package/dist/cli/commands/legacy-commands.js +1 -1
  86. package/dist/cli/commands/log-commands.js +1 -1
  87. package/dist/cli/commands/loop-commands.js +1 -1
  88. package/dist/cli/commands/loop-eval-commands.js +1 -1
  89. package/dist/cli/commands/memory-commands.js +1 -1
  90. package/dist/cli/commands/migrate-1-4-1-command.js +1 -1
  91. package/dist/cli/commands/migrate-v2-10-to-v2-11-command.js +1 -1
  92. package/dist/cli/commands/mut-commands.d.ts +1 -1
  93. package/dist/cli/commands/mut-commands.js +1 -5
  94. package/dist/cli/commands/observability-commands.js +1 -1
  95. package/dist/cli/commands/openspec-commands.js +65 -4
  96. package/dist/cli/commands/perf-audit-commands.js +3 -3
  97. package/dist/cli/commands/perf-commands.js +1 -1
  98. package/dist/cli/commands/polyrepo-commands.js +1 -1
  99. package/dist/cli/commands/prd-blocks-commands.js +1 -1
  100. package/dist/cli/commands/prd-commands.js +4 -2
  101. package/dist/cli/commands/preferences-commands.js +23 -3
  102. package/dist/cli/commands/project-commands.js +1 -1
  103. package/dist/cli/commands/qa-business-review-commands.js +1 -1
  104. package/dist/cli/commands/qa-commands.d.ts +1 -1
  105. package/dist/cli/commands/qa-commands.js +39 -3
  106. package/dist/cli/commands/release-commands.d.ts +15 -0
  107. package/dist/cli/commands/release-commands.js +113 -21
  108. package/dist/cli/commands/request-commands.js +1 -1
  109. package/dist/cli/commands/retrospective-commands.js +1 -1
  110. package/dist/cli/commands/reviewer-commands.js +1 -1
  111. package/dist/cli/commands/role-commands.js +1 -1
  112. package/dist/cli/commands/runtime-commands-helpers.d.ts +7 -0
  113. package/dist/cli/commands/runtime-commands-helpers.js +24 -0
  114. package/dist/cli/commands/runtime-commands.js +1 -2
  115. package/dist/cli/commands/sc-commands.js +4 -2
  116. package/dist/cli/commands/scan-commands.js +1 -1
  117. package/dist/cli/commands/security-audit-commands.js +3 -3
  118. package/dist/cli/commands/sediment-commands.js +10 -1
  119. package/dist/cli/commands/session-24h-mode.d.ts +26 -0
  120. package/dist/cli/commands/session-24h-mode.js +196 -0
  121. package/dist/cli/commands/share-commands.d.ts +19 -0
  122. package/dist/cli/commands/share-commands.js +121 -2
  123. package/dist/cli/commands/skill-conformance-commands.js +1 -1
  124. package/dist/cli/commands/skill-loop-engineering-readiness-commands.js +1 -1
  125. package/dist/cli/commands/skill-search-commands.js +4 -1
  126. package/dist/cli/commands/slice-commands.js +1 -1
  127. package/dist/cli/commands/slice-integrate-commands.js +1 -1
  128. package/dist/cli/commands/slice-review-commands.js +1 -1
  129. package/dist/cli/commands/smoke-commands.js +1 -1
  130. package/dist/cli/commands/sop-commands.js +1 -1
  131. package/dist/cli/commands/spill-demo-command.d.ts +7 -0
  132. package/dist/cli/commands/spill-demo-command.js +29 -0
  133. package/dist/cli/commands/statusline-commands.d.ts +12 -0
  134. package/dist/cli/commands/statusline-commands.js +79 -17
  135. package/dist/cli/commands/sub-agent-commands.js +2 -1
  136. package/dist/cli/commands/sub-agent-shared.d.ts +10 -0
  137. package/dist/cli/commands/sub-agent-shared.js +24 -1
  138. package/dist/cli/commands/swarm-commands.d.ts +13 -0
  139. package/dist/cli/commands/swarm-commands.js +77 -0
  140. package/dist/cli/commands/tech-commands.d.ts +64 -0
  141. package/dist/cli/commands/tech-commands.js +195 -0
  142. package/dist/cli/commands/understand-commands.js +1 -1
  143. package/dist/cli/commands/upgrade-commands.d.ts +2 -0
  144. package/dist/cli/commands/upgrade-commands.js +40 -5
  145. package/dist/cli/commands/user-touchpoint-commands.js +1 -1
  146. package/dist/cli/commands/verdict-aggregate-command.js +1 -1
  147. package/dist/cli/commands/vm-commands.d.ts +31 -0
  148. package/dist/cli/commands/vm-commands.js +274 -0
  149. package/dist/cli/commands/workflow-commands.js +24 -1
  150. package/dist/cli/commands/workflow-plan-commands.js +1 -1
  151. package/dist/cli/commands/workspace/init-command.js +1 -1
  152. package/dist/cli/commands/workspace/migrate-command.js +1 -1
  153. package/dist/cli/commands/workspace/reconcile-command.js +1 -1
  154. package/dist/cli/commands/worktree-auth-commands.d.ts +29 -0
  155. package/dist/cli/commands/worktree-auth-commands.js +652 -0
  156. package/dist/cli/index.js +58 -13
  157. package/dist/cli/program.d.ts +2 -2
  158. package/dist/cli/program.js +86 -281
  159. package/dist/services/24h-mode/decider.d.ts +103 -0
  160. package/dist/services/24h-mode/decider.js +144 -0
  161. package/dist/services/24h-mode/index.d.ts +12 -0
  162. package/dist/services/24h-mode/index.js +12 -0
  163. package/dist/services/24h-mode/state.d.ts +48 -0
  164. package/dist/services/24h-mode/state.js +51 -0
  165. package/dist/services/24h-mode/store.d.ts +27 -0
  166. package/dist/services/24h-mode/store.js +119 -0
  167. package/dist/services/artifacts/artifact-prerequisites.js +1 -1
  168. package/dist/services/artifacts/request-artifact-service.js +1 -1
  169. package/dist/services/artifacts/workspace-service.js +1 -1
  170. package/dist/services/audit/enforcers/lint-bee-runtime-contract.d.ts +25 -0
  171. package/dist/services/audit/enforcers/lint-bee-runtime-contract.js +91 -0
  172. package/dist/services/audit/enforcers/lint-peaks-code-runtime.d.ts +16 -0
  173. package/dist/services/audit/enforcers/lint-peaks-code-runtime.js +31 -0
  174. package/dist/services/audit/enforcers/lint-peaks-skill-runtime.d.ts +23 -0
  175. package/dist/services/audit/enforcers/lint-peaks-skill-runtime.js +59 -0
  176. package/dist/services/audit/enforcers/lint-peaks-ui-sc-txt-runtime.d.ts +28 -0
  177. package/dist/services/audit/enforcers/lint-peaks-ui-sc-txt-runtime.js +86 -0
  178. package/dist/services/audit/enforcers/lint-prd-artifact-handoff.d.ts +16 -0
  179. package/dist/services/audit/enforcers/lint-prd-artifact-handoff.js +41 -0
  180. package/dist/services/audit/enforcers/lint-prd-source-snapshot.d.ts +27 -0
  181. package/dist/services/audit/enforcers/lint-prd-source-snapshot.js +47 -0
  182. package/dist/services/audit/enforcers/lint-qa-gateguard-and-runtime.d.ts +19 -0
  183. package/dist/services/audit/enforcers/lint-qa-gateguard-and-runtime.js +47 -0
  184. package/dist/services/audit/enforcers/lint-rd-handoff-coverage.d.ts +18 -0
  185. package/dist/services/audit/enforcers/lint-rd-handoff-coverage.js +70 -0
  186. package/dist/services/audit/enforcers/lint-skill-presence-mandatory.d.ts +25 -0
  187. package/dist/services/audit/enforcers/lint-skill-presence-mandatory.js +53 -0
  188. package/dist/services/audit/red-line-catalog-p2-a.d.ts +0 -6
  189. package/dist/services/audit/red-line-catalog-p2-a.js +230 -1
  190. package/dist/services/audit/static-service.d.ts +32 -25
  191. package/dist/services/audit/static-service.js +30 -109
  192. package/dist/services/audit-independent/index.d.ts +2 -0
  193. package/dist/services/audit-independent/index.js +2 -0
  194. package/dist/services/autonomous-swarm/autonomous-swarm-service.d.ts +127 -0
  195. package/dist/services/autonomous-swarm/autonomous-swarm-service.js +446 -0
  196. package/dist/services/changeset/changeset-check-service.d.ts +22 -0
  197. package/dist/services/changeset/changeset-check-service.js +47 -0
  198. package/dist/services/code/auto-compact-modes.d.ts +30 -0
  199. package/dist/services/code/auto-compact-modes.js +38 -0
  200. package/dist/services/code/auto-compact-orchestrator.d.ts +43 -5
  201. package/dist/services/code/auto-compact-orchestrator.js +101 -21
  202. package/dist/services/code/dag-orchestrator.d.ts +30 -1
  203. package/dist/services/code/dag-orchestrator.js +106 -1
  204. package/dist/services/code/status-line-renderer.d.ts +2 -0
  205. package/dist/services/code/status-line-renderer.js +21 -3
  206. package/dist/services/code-review/ocr-service.d.ts +1 -1
  207. package/dist/services/code-review/ocr-service.js +6 -3
  208. package/dist/services/compact-history/compact-history-service.d.ts +41 -0
  209. package/dist/services/compact-history/compact-history-service.js +62 -0
  210. package/dist/services/compact-statusline/compact-statusline-service.d.ts +16 -0
  211. package/dist/services/compact-statusline/compact-statusline-service.js +83 -0
  212. package/dist/services/config/config-service.d.ts +1 -12
  213. package/dist/services/config/config-service.js +3 -72
  214. package/dist/services/config/config-types.d.ts +2 -4
  215. package/dist/services/config/config-types.js +1 -1
  216. package/dist/services/container/container-lease.d.ts +67 -0
  217. package/dist/services/container/container-lease.js +135 -0
  218. package/dist/services/context/auto-compact-dispatcher.d.ts +40 -5
  219. package/dist/services/context/auto-compact-dispatcher.js +32 -102
  220. package/dist/services/context/auto-compact-reader.js +1 -1
  221. package/dist/services/context/auto-compact-types.d.ts +27 -2
  222. package/dist/services/context/auto-compact-types.js +8 -1
  223. package/dist/services/context/build-dispatch-system-prompt.d.ts +75 -0
  224. package/dist/services/context/build-dispatch-system-prompt.js +117 -0
  225. package/dist/services/context/collector.d.ts +2 -2
  226. package/dist/services/context/context-builder.d.ts +4 -4
  227. package/dist/services/context/context-schema.d.ts +48 -48
  228. package/dist/services/context/ide-detect.d.ts +4 -0
  229. package/dist/services/context/ide-detect.js +24 -0
  230. package/dist/services/context/main-session-monitor.d.ts +1 -4
  231. package/dist/services/context/main-session-monitor.js +1 -24
  232. package/dist/services/context/memory-index-reader.d.ts +8 -0
  233. package/dist/services/context/memory-index-reader.js +57 -0
  234. package/dist/services/context/memory-lru-cache.d.ts +12 -0
  235. package/dist/services/context/memory-lru-cache.js +60 -0
  236. package/dist/services/context/memory-preflight-config.d.ts +19 -0
  237. package/dist/services/context/memory-preflight-config.js +25 -0
  238. package/dist/services/context/memory-preflight-service.d.ts +19 -0
  239. package/dist/services/context/memory-preflight-service.js +120 -0
  240. package/dist/services/context/spillover-store.d.ts +23 -0
  241. package/dist/services/context/spillover-store.js +155 -0
  242. package/dist/services/context/spillover-types.d.ts +18 -0
  243. package/dist/services/context/spillover-types.js +1 -0
  244. package/dist/services/crystallization/crystallization-service.d.ts +22 -5
  245. package/dist/services/crystallization/crystallization-service.js +8 -14
  246. package/dist/services/crystallization/crystallization-types.d.ts +6 -6
  247. package/dist/services/crystallization/evidence-brief-builder.d.ts +1 -1
  248. package/dist/services/crystallization/index.d.ts +13 -0
  249. package/dist/services/crystallization/index.js +13 -0
  250. package/dist/services/dashboard/project-dashboard-service.js +2 -2
  251. package/dist/services/dispatch/await-batch.d.ts +70 -0
  252. package/dist/services/dispatch/await-batch.js +272 -0
  253. package/dist/services/dispatch/batch-counter.js +1 -1
  254. package/dist/services/dispatch/dispatch-record-writer.d.ts +126 -4
  255. package/dist/services/dispatch/dispatch-record-writer.js +239 -9
  256. package/dist/services/dispatch/slice-dag.d.ts +46 -0
  257. package/dist/services/dispatch/slice-dag.js +6 -0
  258. package/dist/services/dispatch/stage-enum.d.ts +27 -0
  259. package/dist/services/dispatch/stage-enum.js +48 -0
  260. package/dist/services/dispatch/startup-timeout.d.ts +86 -0
  261. package/dist/services/dispatch/startup-timeout.js +97 -0
  262. package/dist/services/dispatch/sub-agent-dispatcher.js +40 -89
  263. package/dist/services/dispatch/test-tool-detection.d.ts +25 -1
  264. package/dist/services/dispatch/test-tool-detection.js +80 -0
  265. package/dist/services/doctor/doctor-service/checks/check-id-schema.d.ts +18 -0
  266. package/dist/services/doctor/doctor-service/checks/check-id-schema.js +64 -0
  267. package/dist/services/doctor/doctor-service/checks/codegraph-capability.d.ts +14 -0
  268. package/dist/services/doctor/doctor-service/checks/codegraph-capability.js +69 -0
  269. package/dist/services/doctor/doctor-service/checks/dist-source-version.d.ts +32 -0
  270. package/dist/services/doctor/doctor-service/checks/dist-source-version.js +110 -0
  271. package/dist/services/doctor/doctor-service/checks/gateguard-conflict.d.ts +24 -0
  272. package/dist/services/doctor/doctor-service/checks/gateguard-conflict.js +164 -0
  273. package/dist/services/doctor/doctor-service/checks/l3-memory-health.d.ts +16 -0
  274. package/dist/services/doctor/doctor-service/checks/l3-memory-health.js +57 -0
  275. package/dist/services/doctor/doctor-service/checks/l3-orphan-sessions.d.ts +19 -0
  276. package/dist/services/doctor/doctor-service/checks/l3-orphan-sessions.js +66 -0
  277. package/dist/services/doctor/doctor-service/checks/schema-validity.d.ts +10 -0
  278. package/dist/services/doctor/doctor-service/checks/schema-validity.js +33 -0
  279. package/dist/services/doctor/doctor-service/checks/skill-apply-note.d.ts +20 -0
  280. package/dist/services/doctor/doctor-service/checks/skill-apply-note.js +85 -0
  281. package/dist/services/doctor/doctor-service/checks/skill-existence.d.ts +13 -0
  282. package/dist/services/doctor/doctor-service/checks/skill-existence.js +30 -0
  283. package/dist/services/doctor/doctor-service/checks/skill-name-match.d.ts +10 -0
  284. package/dist/services/doctor/doctor-service/checks/skill-name-match.js +25 -0
  285. package/dist/services/doctor/doctor-service/checks/skill-parse.d.ts +10 -0
  286. package/dist/services/doctor/doctor-service/checks/skill-parse.js +19 -0
  287. package/dist/services/doctor/doctor-service/checks/skill-presence.d.ts +21 -0
  288. package/dist/services/doctor/doctor-service/checks/skill-presence.js +75 -0
  289. package/dist/services/doctor/doctor-service/checks/skill-runbook.d.ts +14 -0
  290. package/dist/services/doctor/doctor-service/checks/skill-runbook.js +47 -0
  291. package/dist/services/doctor/doctor-service/checks/statusline-install.d.ts +14 -0
  292. package/dist/services/doctor/doctor-service/checks/statusline-install.js +32 -0
  293. package/dist/services/doctor/doctor-service/checks/statusline-runtime.d.ts +13 -0
  294. package/dist/services/doctor/doctor-service/checks/statusline-runtime.js +30 -0
  295. package/dist/services/doctor/doctor-service/checks/user-config.d.ts +11 -0
  296. package/dist/services/doctor/doctor-service/checks/user-config.js +27 -0
  297. package/dist/services/doctor/doctor-service/checks/workspace-init.d.ts +16 -0
  298. package/dist/services/doctor/doctor-service/checks/workspace-init.js +34 -0
  299. package/dist/services/doctor/doctor-service/checks/workspace-layout.d.ts +33 -0
  300. package/dist/services/doctor/doctor-service/checks/workspace-layout.js +180 -0
  301. package/dist/services/doctor/doctor-service/index.d.ts +48 -0
  302. package/dist/services/doctor/doctor-service/index.js +254 -0
  303. package/dist/services/doctor/doctor-service/plugin-registry.d.ts +41 -0
  304. package/dist/services/doctor/doctor-service/plugin-registry.js +77 -0
  305. package/dist/services/doctor/doctor-service/report/final-summary.d.ts +20 -0
  306. package/dist/services/doctor/doctor-service/report/final-summary.js +29 -0
  307. package/dist/services/doctor/doctor-service/types.d.ts +245 -0
  308. package/dist/services/doctor/doctor-service/types.js +15 -0
  309. package/dist/services/doctor/doctor-service.d.ts +15 -146
  310. package/dist/services/doctor/doctor-service.js +22 -926
  311. package/dist/services/doctor/index.d.ts +22 -0
  312. package/dist/services/doctor/index.js +22 -0
  313. package/dist/services/env/shell-probe.d.ts +71 -0
  314. package/dist/services/env/shell-probe.js +161 -0
  315. package/dist/services/evolution/evolution-types.d.ts +39 -39
  316. package/dist/services/final-review/final-review-service.d.ts +11 -1
  317. package/dist/services/final-review/index.d.ts +2 -0
  318. package/dist/services/final-review/index.js +1 -0
  319. package/dist/services/hooks/auto-compact-hook-install.d.ts +4 -5
  320. package/dist/services/hooks/auto-compact-hook-install.js +4 -5
  321. package/dist/services/hooks/worktree-authorization-gate.d.ts +205 -0
  322. package/dist/services/hooks/worktree-authorization-gate.js +492 -0
  323. package/dist/services/ide/adapters/claude-code-adapter.js +2 -2
  324. package/dist/services/ide/adapters/qoder-adapter.d.ts +25 -0
  325. package/dist/services/ide/adapters/qoder-adapter.js +57 -0
  326. package/dist/services/ide/adapters/tongyi-lingma-adapter.d.ts +16 -0
  327. package/dist/services/ide/adapters/tongyi-lingma-adapter.js +45 -0
  328. package/dist/services/ide/ide-registry.js +6 -0
  329. package/dist/services/ide/ide-types.d.ts +1 -1
  330. package/dist/services/job/job-types.d.ts +56 -56
  331. package/dist/services/job-snapshot/index.d.ts +8 -0
  332. package/dist/services/job-snapshot/index.js +7 -0
  333. package/dist/services/{job/job-resource-snapshot.d.ts → job-snapshot/snapshot.d.ts} +1 -1
  334. package/dist/services/job-snapshot/types.d.ts +7 -0
  335. package/dist/services/job-snapshot/types.js +1 -0
  336. package/dist/services/karpathy-cost/karpathy-cost-check-service.d.ts +67 -0
  337. package/dist/services/karpathy-cost/karpathy-cost-check-service.js +108 -0
  338. package/dist/services/log/retention.d.ts +16 -0
  339. package/dist/services/log/retention.js +17 -0
  340. package/dist/services/loop/loop-bee-relation-types.d.ts +6 -6
  341. package/dist/services/loop/loop-release-types.d.ts +24 -24
  342. package/dist/services/loop/monotonic-runner.d.ts +17 -11
  343. package/dist/services/loop/monotonic-runner.js +118 -79
  344. package/dist/services/memory/project-memory-service/index/kind-dispatch.d.ts +8 -0
  345. package/dist/services/memory/project-memory-service/index/kind-dispatch.js +253 -0
  346. package/dist/services/memory/project-memory-service/index/ranking.d.ts +6 -0
  347. package/dist/services/memory/project-memory-service/index/ranking.js +171 -0
  348. package/dist/services/memory/project-memory-service/index/search.d.ts +34 -0
  349. package/dist/services/memory/project-memory-service/index/search.js +194 -0
  350. package/dist/services/memory/project-memory-service/index.d.ts +9 -0
  351. package/dist/services/memory/project-memory-service/index.js +24 -0
  352. package/dist/services/memory/project-memory-service/parsers/frontmatter.d.ts +9 -0
  353. package/dist/services/memory/project-memory-service/parsers/frontmatter.js +106 -0
  354. package/dist/services/memory/project-memory-service/parsers/markdown-pure.d.ts +7 -0
  355. package/dist/services/memory/project-memory-service/parsers/markdown-pure.js +96 -0
  356. package/dist/services/memory/project-memory-service/store/atomic-write.d.ts +5 -0
  357. package/dist/services/memory/project-memory-service/store/atomic-write.js +56 -0
  358. package/dist/services/memory/project-memory-service/store/paths.d.ts +8 -0
  359. package/dist/services/memory/project-memory-service/store/paths.js +116 -0
  360. package/dist/services/memory/project-memory-service/types.d.ts +128 -0
  361. package/dist/services/memory/project-memory-service/types.js +9 -0
  362. package/dist/services/memory/project-memory-service.d.ts +1 -167
  363. package/dist/services/memory/project-memory-service.js +19 -816
  364. package/dist/services/migrate-skill-name/schema.d.ts +2 -2
  365. package/dist/services/observability/aggregation.d.ts +27 -0
  366. package/dist/services/observability/aggregation.js +72 -0
  367. package/dist/services/observability/observability-service.d.ts +73 -7
  368. package/dist/services/observability/observability-service.js +87 -1
  369. package/dist/services/openspec/artifact-boundary.d.ts +138 -0
  370. package/dist/services/openspec/artifact-boundary.js +150 -0
  371. package/dist/services/openspec/coverage-evidence-reader.d.ts +191 -0
  372. package/dist/services/openspec/coverage-evidence-reader.js +386 -0
  373. package/dist/services/openspec/openspec-archive-service.d.ts +70 -0
  374. package/dist/services/openspec/openspec-archive-service.js +320 -8
  375. package/dist/services/openspec/openspec-bridge-service.js +1 -1
  376. package/dist/services/openspec/openspec-init-service.js +1 -1
  377. package/dist/services/openspec/openspec-propose-from-doctor-service.js +1 -1
  378. package/dist/services/openspec/openspec-render-service.js +7 -4
  379. package/dist/services/openspec/openspec-scan-service.js +1 -1
  380. package/dist/services/openspec/openspec-validate-service.js +5 -4
  381. package/dist/services/preferences/preferences-service.js +10 -0
  382. package/dist/services/preferences/preferences-types.d.ts +13 -0
  383. package/dist/services/preferences/preferences-types.js +11 -0
  384. package/dist/services/qa/screenshot-archive-service.d.ts +51 -0
  385. package/dist/services/qa/screenshot-archive-service.js +134 -0
  386. package/dist/services/rd/rd-service.js +10 -11
  387. package/dist/services/rd/types.d.ts +32 -32
  388. package/dist/services/rd-swarm/rd-swarm-service.d.ts +60 -0
  389. package/dist/services/rd-swarm/rd-swarm-service.js +93 -0
  390. package/dist/services/recommendations/capability-seed-items.js +0 -1
  391. package/dist/services/recommendations/capability-seed-mappings.js +0 -1
  392. package/dist/services/recommendations/capability-seed-sources.js +0 -1
  393. package/dist/services/release/version-precheck-service.d.ts +51 -0
  394. package/dist/services/release/version-precheck-service.js +257 -0
  395. package/dist/services/scan/acceptance-coverage-service.js +1 -1
  396. package/dist/services/scan/archetype-service.js +1 -1
  397. package/dist/services/scan/existing-system-service.js +1 -1
  398. package/dist/services/scan/libraries-service.js +1 -1
  399. package/dist/services/sediment/json-schema.d.ts +40 -40
  400. package/dist/services/share/bundle-types.d.ts +12 -12
  401. package/dist/services/share/run-state-contract.d.ts +2 -2
  402. package/dist/services/skill/skill-search-service.d.ts +12 -7
  403. package/dist/services/skill/skill-search-service.js +8 -4
  404. package/dist/services/skillhub/migrations/001-initial.sql +71 -0
  405. package/dist/services/skillhub/migrations/002-loop-release.sql +32 -0
  406. package/dist/services/skillhub/migrations/003-loop-bee-relation.sql +35 -0
  407. package/dist/services/skillhub/migrations/004-loop-bee-extension.sql +65 -0
  408. package/dist/services/skillhub/migrations/005-evolution-evaluation.sql +81 -0
  409. package/dist/services/skillhub/migrations/006-crystallization-event.sql +92 -0
  410. package/dist/services/skills/hooks-settings-service.d.ts +75 -0
  411. package/dist/services/skills/hooks-settings-service.js +230 -2
  412. package/dist/services/skills/skill-registry.d.ts +1 -0
  413. package/dist/services/skills/skill-registry.js +6 -4
  414. package/dist/services/skills/skill-runbook-service.js +1 -1
  415. package/dist/services/slice/slice-check-service.js +1 -1
  416. package/dist/services/standards/main-session-monitor-ban-hook.d.ts +14 -0
  417. package/dist/services/standards/main-session-monitor-ban-hook.js +19 -0
  418. package/dist/services/standards/missing-standards-detector.d.ts +25 -0
  419. package/dist/services/standards/missing-standards-detector.js +43 -12
  420. package/dist/services/tech/tech-change-id-service.d.ts +82 -0
  421. package/dist/services/tech/tech-change-id-service.js +117 -0
  422. package/dist/services/understand/understand-hybrid-service.js +1 -1
  423. package/dist/services/understand/understand-scan-service.js +2 -2
  424. package/dist/services/upgrade/upgrade-service.js +4 -3
  425. package/dist/services/verdict/envelopes.d.ts +2 -2
  426. package/dist/services/verdict/envelopes.js +2 -2
  427. package/dist/services/verdict/verdict-aggregator.d.ts +3 -3
  428. package/dist/services/vm/vm-lease.d.ts +40 -0
  429. package/dist/services/vm/vm-lease.js +137 -0
  430. package/dist/services/workflow/autonomous-resume-writer.js +1 -1
  431. package/dist/services/workflow/pipeline-verify-gate-support.d.ts +47 -0
  432. package/dist/services/workflow/pipeline-verify-gate-support.js +194 -0
  433. package/dist/services/workflow/pipeline-verify-service.d.ts +17 -81
  434. package/dist/services/workflow/pipeline-verify-service.js +42 -218
  435. package/dist/services/workflow/pipeline-verify-types.d.ts +90 -0
  436. package/dist/services/workflow/pipeline-verify-types.js +10 -0
  437. package/dist/services/workflow/plan-reader.d.ts +1 -1
  438. package/dist/services/workflow/plan-reader.js +1 -1
  439. package/dist/services/workflow/plan-refresher.d.ts +1 -1
  440. package/dist/services/workflow/plan-refresher.js +1 -1
  441. package/dist/services/workflow/plan-trigger-detector.d.ts +1 -1
  442. package/dist/services/workflow/plan-trigger-detector.js +1 -1
  443. package/dist/services/workflow/workflow-autonomous-resume-helpers.d.ts +1 -1
  444. package/dist/services/workflow/workflow-autonomous-service.d.ts +2 -98
  445. package/dist/services/workflow/workflow-autonomous-types.d.ts +114 -0
  446. package/dist/services/workflow/workflow-autonomous-types.js +21 -0
  447. package/dist/services/workflow/workflow-router-service.js +18 -16
  448. package/dist/services/workflow/workflow-spec-lint.d.ts +13 -0
  449. package/dist/services/workflow/workflow-spec-lint.js +75 -0
  450. package/dist/services/workflow/workflow-spec-types.d.ts +100 -0
  451. package/dist/services/workflow/workflow-spec-types.js +13 -0
  452. package/dist/services/workflow/workflow-spec-yaml.d.ts +17 -0
  453. package/dist/services/workflow/workflow-spec-yaml.js +88 -0
  454. package/dist/services/workflow/workflow-spec.d.ts +11 -91
  455. package/dist/services/workflow/workflow-spec.js +16 -145
  456. package/dist/services/workspace/migrate-service.js +1 -1
  457. package/dist/services/workspace/workspace-service.js +1 -1
  458. package/dist/services/worktree/worktree-lease.d.ts +170 -0
  459. package/dist/services/worktree/worktree-lease.js +242 -0
  460. package/package.json +91 -51
  461. package/scripts/clean-dist.mjs +31 -1
  462. package/scripts/copy-templates.mjs +16 -0
  463. package/scripts/sync-version.mjs +53 -2
  464. package/skills/bee/peaks-perf-audit/SKILL.md +17 -0
  465. package/skills/bee/peaks-prd/SKILL.md +16 -0
  466. package/skills/bee/peaks-prd/test-prompts.json +12 -0
  467. package/skills/bee/peaks-qa/SKILL.md +16 -1
  468. package/skills/bee/peaks-qa/references/qa-sub-agent-dispatch.md +22 -0
  469. package/skills/bee/peaks-qa/test-prompts.json +12 -0
  470. package/skills/bee/peaks-rd/SKILL.md +29 -20
  471. package/skills/bee/peaks-rd/references/rd-sub-agent-dispatch.md +39 -0
  472. package/skills/bee/peaks-rd/test-prompts.json +12 -0
  473. package/skills/bee/peaks-reviewer/SKILL.md +17 -0
  474. package/skills/bee/peaks-sc/SKILL.md +16 -0
  475. package/skills/bee/peaks-security-audit/SKILL.md +17 -0
  476. package/skills/bee/peaks-txt/SKILL.md +16 -0
  477. package/skills/bee/peaks-txt/test-prompts.json +12 -0
  478. package/skills/bee/peaks-ui/SKILL.md +49 -43
  479. package/skills/bee/peaks-ui/test-prompts.json +12 -0
  480. package/skills/peaks-code/SKILL.md +54 -109
  481. package/skills/peaks-code/references/boundaries.md +15 -1
  482. package/skills/peaks-code/references/cron-scheduler-deployment.md +313 -0
  483. package/skills/peaks-code/references/external-skill-invocation.md +27 -0
  484. package/skills/peaks-code/references/lease-dashboard.html +224 -0
  485. package/skills/peaks-code/references/lease-stats.sample.json +26 -0
  486. package/skills/peaks-code/references/peaks-worktree-l2-enforcers.md +208 -0
  487. package/skills/peaks-code/references/periodic-checkpoint.md +4 -4
  488. package/skills/peaks-code/references/runbook.md +63 -1
  489. package/skills/peaks-code/references/startup-sequence.md +110 -0
  490. package/skills/peaks-code/references/step-0-8-gate.md +1 -1
  491. package/skills/peaks-code/references/sub-agent-dispatch.md +92 -1
  492. package/skills/peaks-code/references/worktree-governance.md +123 -0
  493. package/skills/peaks-code/test-prompts.json +12 -0
  494. package/skills/peaks-doctor/SKILL.md +1 -1
  495. package/skills/peaks-ide/SKILL.md +1 -1
  496. package/skills/peaks-solo/SKILL.md +21 -0
  497. package/skills/peaks-solo/references/triage-decision-table.md +6 -0
  498. package/skills/peaks-sop/test-prompts.json +17 -0
  499. package/dist/cli/commands/agent-commands.d.ts +0 -20
  500. package/dist/cli/commands/agent-commands.js +0 -48
  501. package/dist/cli/commands/classify-classify-commands.d.ts +0 -19
  502. package/dist/cli/commands/classify-classify-commands.js +0 -151
  503. package/dist/cli/commands/contract-commands.d.ts +0 -26
  504. package/dist/cli/commands/contract-commands.js +0 -114
  505. package/dist/cli/commands/session-auto-compact-hook-command.d.ts +0 -37
  506. package/dist/cli/commands/session-auto-compact-hook-command.js +0 -127
  507. package/dist/cli/commands/worker-commands.d.ts +0 -3
  508. package/dist/cli/commands/worker-commands.js +0 -52
  509. package/dist/services/agent/ecc-agent-service.d.ts +0 -47
  510. package/dist/services/agent/ecc-agent-service.js +0 -143
  511. package/dist/services/config/provider-service.d.ts +0 -48
  512. package/dist/services/config/provider-service.js +0 -143
  513. package/dist/services/context/dispatch-context-guard.d.ts +0 -27
  514. package/dist/services/context/dispatch-context-guard.js +0 -192
  515. package/dist/services/context/shared-channel.d.ts +0 -93
  516. package/dist/services/context/shared-channel.js +0 -308
  517. package/dist/services/filesystem/file-lock.d.ts +0 -11
  518. package/dist/services/filesystem/file-lock.js +0 -152
  519. package/dist/services/mut/assert-scanner.d.ts +0 -6
  520. package/dist/services/mut/assert-scanner.js +0 -116
  521. package/dist/services/mut/index.d.ts +0 -8
  522. package/dist/services/mut/index.js +0 -7
  523. package/dist/services/mut/mut-runner.d.ts +0 -39
  524. package/dist/services/mut/mut-runner.js +0 -29
  525. package/dist/services/mut/production-stryker.d.ts +0 -2
  526. package/dist/services/mut/production-stryker.js +0 -75
  527. package/dist/services/mut/report-builder.d.ts +0 -11
  528. package/dist/services/mut/report-builder.js +0 -110
  529. package/dist/services/mut/report-loader.d.ts +0 -31
  530. package/dist/services/mut/report-loader.js +0 -78
  531. package/dist/services/mut/thresholds.d.ts +0 -21
  532. package/dist/services/mut/thresholds.js +0 -22
  533. package/dist/services/mut/types.d.ts +0 -336
  534. package/dist/services/mut/types.js +0 -59
  535. package/dist/services/providers/minimax-provider-service.d.ts +0 -24
  536. package/dist/services/providers/minimax-provider-service.js +0 -143
  537. package/dist/services/providers/minimax-worker-service.d.ts +0 -21
  538. package/dist/services/providers/minimax-worker-service.js +0 -83
  539. package/dist/shared/fs.d.ts +0 -4
  540. package/dist/shared/fs.js +0 -26
  541. package/dist/shared/paths.d.ts +0 -6
  542. package/dist/shared/paths.js +0 -50
  543. package/dist/shared/result.d.ts +0 -22
  544. package/dist/shared/result.js +0 -46
  545. package/dist/shared/version.d.ts +0 -1
  546. package/dist/shared/version.js +0 -1
  547. package/examples/video-demo/README.md +0 -142
  548. package/examples/video-demo/out/en-closing-960.png +0 -0
  549. package/examples/video-demo/out/zh-closing-960.png +0 -0
  550. package/examples/video-demo/preview/peaks-loop-demo-en.mp4 +0 -0
  551. package/examples/video-demo/preview/peaks-loop-demo.mp4 +0 -0
  552. /package/dist/services/{job/job-resource-snapshot.js → job-snapshot/snapshot.js} +0 -0
@@ -30,7 +30,7 @@ import { EVOLUTION_TARGET_KINDS, } from "../../services/evolution/evolution-type
30
30
  import { findProjectRoot } from "../../services/config/config-safety.js";
31
31
  import { runIndependentEvaluator, } from "../../services/evolution/independent-evaluator-runner.js";
32
32
  import { addJsonOption, getErrorMessage, printResult } from "../cli-helpers.js";
33
- import { fail, ok } from "../../shared/result.js";
33
+ import { fail, ok } from 'peaks-loop-shared/result';
34
34
  /**
35
35
  * Parse `--target <kind:id>`. Returns the kind + id, or `null` on
36
36
  * failure. Validates `kind` against the EVOLUTION_TARGET_KINDS
@@ -17,7 +17,7 @@ import { resolve as resolvePath } from 'node:path';
17
17
  import { findProjectRoot } from '../../services/config/config-safety.js';
18
18
  import { getCurrentSessionId } from '../../services/skills/skill-presence-service.js';
19
19
  import { generatePromotionStub, isPromotionLayer, listUnpromotedFeedback, parseFeedbackMemory, PROMOTION_LAYER_DETAILS, PROMOTION_LAYERS, promoteFeedback } from '../../services/feedback/feedback-promotion-service.js';
20
- import { fail, ok } from '../../shared/result.js';
20
+ import { fail, ok } from 'peaks-loop-shared/result';
21
21
  import { addJsonOption, getErrorMessage, printResult } from '../cli-helpers.js';
22
22
  export function registerFeedbackCommands(program, io) {
23
23
  const feedback = program
@@ -20,7 +20,7 @@
20
20
  import { existsSync, statSync } from 'node:fs';
21
21
  import { join, resolve } from 'node:path';
22
22
  import { addJsonOption, getErrorMessage, printResult } from '../cli-helpers.js';
23
- import { fail, ok } from '../../shared/result.js';
23
+ import { fail, ok } from 'peaks-loop-shared/result';
24
24
  /** Whitelist of supported `--llm-provider` values for `peaks prepare-final-review`. */
25
25
  const SUPPORTED_LLM_PROVIDERS = ['stub'];
26
26
  function isSupportedLlmProvider(value) {
@@ -1,7 +1,7 @@
1
1
  import { resolve } from 'node:path';
2
2
  import { captureDerivedVariant, captureHistoricalFixture, EDGE_CASE_VARIANTS, ENVELOPE_KINDS } from '../../services/fixture/index.js';
3
3
  import { addJsonOption, printResult } from '../cli-helpers.js';
4
- import { fail, ok } from '../../shared/result.js';
4
+ import { fail, ok } from 'peaks-loop-shared/result';
5
5
  function isEnvelopeKind(v) {
6
6
  return ENVELOPE_KINDS.includes(v);
7
7
  }
@@ -13,7 +13,7 @@
13
13
  */
14
14
  import { findProjectRoot } from '../../services/config/config-safety.js';
15
15
  import { appendSyncRecord, buildForkStatusReport, makeSyncId, readForkState, recommendStableTags, updateSyncRecordStatus, writeForkState } from '../../services/fork/fork-sync-state.js';
16
- import { fail, ok } from '../../shared/result.js';
16
+ import { fail, ok } from 'peaks-loop-shared/result';
17
17
  import { addJsonOption, printResult } from '../cli-helpers.js';
18
18
  export function registerForkCommands(program, io) {
19
19
  const fork = program
@@ -1,9 +1,11 @@
1
1
  import { enforceBashCommand, recordGateBypass, GateBypassError } from '../../services/sop/gate-enforce-service.js';
2
- import { fail, ok } from '../../shared/result.js';
2
+ import { evaluateWorktreeAuth } from '../../services/hooks/worktree-authorization-gate.js';
3
+ import { getCurrentSessionId } from '../../services/skills/skill-presence-service.js';
4
+ import { fail, ok } from 'peaks-loop-shared/result';
3
5
  import { addJsonOption, getErrorMessage, printResult } from '../cli-helpers.js';
4
- import { detectIdeFromContext, parseClaudeShapeStdin } from '../../services/ide/hook-translator.js';
6
+ import { detectIdeFromContext, parseClaudeShapeStdin, pluckObject, pluckString } from '../../services/ide/hook-translator.js';
5
7
  import { getAdapter } from '../../services/ide/ide-registry.js';
6
- import { emitBlock, emitHint } from '../../services/hooks/output.js';
8
+ import { emitBlock, emitDecision, emitHint } from '../../services/hooks/output.js';
7
9
  /**
8
10
  * Read the PreToolUse hook payload. `PEAKS_HOOK_STDIN` is a test seam; production
9
11
  * reads stdin. The CLI-side stdin reader is intentionally kept here (not in
@@ -28,6 +30,35 @@ async function readHookPayload() {
28
30
  process.stdin.on('error', () => resolveStdin(data));
29
31
  });
30
32
  }
33
+ /**
34
+ * Map Claude Code tool name to the gate's internal `ToolCallKind`. Anything we don't recognize
35
+ * returns `'Other'` — the worktree gate is opt-in by tool, so unknown tools short-circuit to allow.
36
+ */
37
+ function classifyTool(toolName) {
38
+ if (toolName === 'Bash')
39
+ return 'Bash';
40
+ if (toolName === 'Agent' || toolName === 'Task')
41
+ return 'Agent';
42
+ if (toolName === 'EnterWorktree')
43
+ return 'EnterWorktree';
44
+ if (toolName === 'Workflow')
45
+ return 'Workflow';
46
+ return 'Other';
47
+ }
48
+ /**
49
+ * Best-effort extraction of the `isolation` field for `Agent` / `Task` tool calls. The Claude
50
+ * hook payload puts args under `tool_input`, the Cursor/Trae sibling puts them under `toolInput`.
51
+ * Both shapes are accepted; everything else returns null.
52
+ */
53
+ function extractIsolation(parsed) {
54
+ if (parsed === null || typeof parsed !== 'object')
55
+ return null;
56
+ const input = pluckObject(parsed, ['tool_input']) ?? pluckObject(parsed, ['toolInput']);
57
+ if (input === undefined)
58
+ return null;
59
+ const value = pluckString(input, ['isolation']);
60
+ return value === undefined ? null : value;
61
+ }
31
62
  export function registerGateCommands(program, io) {
32
63
  const gate = program.command('gate').description('SOP gate enforcement (PreToolUse hook handler and bypass)');
33
64
  addJsonOption(gate
@@ -57,12 +88,69 @@ export function registerGateCommands(program, io) {
57
88
  // change required.
58
89
  const { toolName, command } = parseClaudeShapeStdin(parsedStdin);
59
90
  if (toolName !== adapter.toolMatcher || typeof command !== 'string' || command.trim().length === 0) {
60
- // Not a guarded surface — allow (no output = normal permission flow).
91
+ // Not a guarded surface — allow. Emit minimal JSON on stdout so Claude
92
+ // Code's PreToolUse hook validator accepts the response. Empty stdout
93
+ // is rejected with "Hook JSON output validation failed — Invalid input"
94
+ // in Claude Code 2.x; `{}` is the canonical no-op marker.
61
95
  if (options.json === true) {
62
96
  printResult(io, ok('gate.enforce', { decision: 'allow', skipped: true }), true);
63
97
  }
98
+ else {
99
+ emitDecision(io, {});
100
+ }
64
101
  return;
65
102
  }
103
+ // slice 2026-07-27-worktree-user-auth: BEFORE the SOP gate runs, check the worktree
104
+ // authorization gate. The worktree gate is narrower than the SOP gate (it only inspects
105
+ // a small set of worktree-mutating operations) and is fail-CLOSED. The two layers are
106
+ // complementary: SOP gates decide "may this command run under this SOP's state", the
107
+ // worktree gate decides "did the user explicitly authorize this worktree-mutating operation
108
+ // in the current task". The worktree gate is cheap and self-contained, so it runs first
109
+ // to give the LLM a clear error reason before the SOP gate would otherwise allow.
110
+ //
111
+ // We extract the tool kind from the hook payload (Bash/Agent/EnterWorktree/Workflow).
112
+ // For Agent/Task we also pull `isolation` from the tool input — only "worktree" isolation
113
+ // is gated. For all other tool kinds the worktree gate is a no-op.
114
+ const toolKind = classifyTool(toolName);
115
+ if (toolKind !== 'Other') {
116
+ const sessionId = getCurrentSessionId(options.project) ?? 'unknown-sid';
117
+ // slice 2026-07-29-worktree-l2-extended Part 2.B: the gate consults
118
+ // the lease file referenced by `PEAKS_WORKTREE_LEASE_ID` as a
119
+ // second authorization path when no `peaks worktree auth grant`
120
+ // is on file. Dispatch (Part 2.C) injects the env var on every
121
+ // sub-agent spawn so worktree-mutating tool calls from inside
122
+ // the sub-agent process auto-authorize via the lease instead
123
+ // of requiring a separate grant.
124
+ const leaseId = process.env.PEAKS_WORKTREE_LEASE_ID ?? null;
125
+ // Part 19: container lease (L4) is the parallel path for
126
+ // `--isolation container` sub-agents. The env is set by the
127
+ // dispatch command (Part 8 + Part 12) at spawn time.
128
+ const containerLeaseId = process.env.PEAKS_CONTAINER_LEASE_ID ?? null;
129
+ const wtDecision = evaluateWorktreeAuth({
130
+ projectRoot: options.project,
131
+ sessionId,
132
+ toolName: toolKind,
133
+ command: toolKind === 'Bash' ? command : null,
134
+ isolation: toolKind === 'Agent' || toolKind === 'EnterWorktree' ? extractIsolation(parsedStdin) : null,
135
+ requestId: null,
136
+ leaseId: leaseId !== null && /^[a-f0-9]{16}$/.test(leaseId) ? leaseId : null,
137
+ containerLeaseId: containerLeaseId !== null && /^[a-f0-9]{16}$/.test(containerLeaseId) ? containerLeaseId : null
138
+ });
139
+ if (!wtDecision.allow) {
140
+ // Hard block: a worktree-mutating tool call without a current-task user grant.
141
+ // emitBlock writes the Claude Code permissionDecision:"deny" envelope and exits 2.
142
+ // The reason includes the remediation hint so the LLM can run `peaks worktree auth grant`
143
+ // and retry, and the user can read the deny text in the next-turn stderr.
144
+ emitBlock(io, `[worktree-gate:${wtDecision.code}] ${wtDecision.reason} — ${wtDecision.remediation}`);
145
+ if (options.json === true) {
146
+ emitHint(io, JSON.stringify(ok('gate.enforce', { decision: 'deny', layer: 'worktree-auth', ...wtDecision })));
147
+ }
148
+ return;
149
+ }
150
+ // allow: continue to the SOP gate (the regular enforcement path).
151
+ // wtDecision may have arrived via lease (viaLease != null); the SOP gate does not
152
+ // care which path granted, only that the worktree gate said allow.
153
+ }
66
154
  const decision = await enforceBashCommand(options.project, command);
67
155
  if (decision.decision === 'deny') {
68
156
  // PRD#2 (2026-06-16-fact-forcing-gate-format): a true SOP gate failure is
@@ -83,14 +171,48 @@ export function registerGateCommands(program, io) {
83
171
  }
84
172
  }
85
173
  if (options.json === true) {
86
- emitHint(io, JSON.stringify(ok('gate.enforce', decision)));
174
+ // --json mode: emit the canonical envelope on stdout (the
175
+ // documented contract for downstream tooling). The hook
176
+ // decision (allow/deny) is still signalled via process.exitCode
177
+ // + the minimal `{}` on stdout in non-`--json` mode.
178
+ // Slice 2026-07-30-nightshift: emitHint here was wrong —
179
+ // emitHint writes to stderr, but the AC5 contract expects
180
+ // the JSON envelope on stdout. Switch to printResult(asJson=true).
181
+ printResult(io, ok('gate.enforce', decision), true);
182
+ }
183
+ else {
184
+ // allow: emit minimal JSON on stdout so Claude Code's PreToolUse hook
185
+ // validator accepts the response (see comment above).
186
+ emitDecision(io, {});
87
187
  }
88
- // allow: emit nothing on stdout → normal permission flow.
89
188
  }
90
189
  catch (error) {
91
190
  // Fail-open: a bug in enforcement must not brick Claude Code.
92
191
  emitHint(io, `gate enforce: internal error, allowing command (${getErrorMessage(error)})`);
93
192
  }
193
+ // Slice 2026-07-29-windows-console-flash Part 46: actively
194
+ // exit the process when the gate-enforce handler returns.
195
+ // The default behavior is to wait for stdin EOF before
196
+ // exiting, but the hook's stdin pipe may not close promptly
197
+ // (the parent can keep the handle open even after it has
198
+ // finished reading the JSON decision), which causes 4
199
+ // Node.js processes to accumulate in the user's task
200
+ // manager. An explicit process.exit here forces the gate
201
+ // process to release all handles and exit immediately.
202
+ //
203
+ // Slice 2026-07-30-nightshift: gate the `process.exit` on
204
+ // being invoked as a real CLI hook (no PEAKS_HOOK_STDIN test
205
+ // seam, no PEAKS_TEST_SEAM env var). The test harness calls
206
+ // `runCommand` which invokes the same action handler in
207
+ // process — calling `process.exit` inside the test would
208
+ // kill the vitest runner before assertions complete. The
209
+ // `process.exitCode` set above is sufficient for the test
210
+ // path: Commander's `parseAsync` resolves the action, then
211
+ // the harness reads the captured stdout/stderr before the
212
+ // runner exits normally with the recorded exit code.
213
+ if (process.env.PEAKS_HOOK_STDIN === undefined && process.env.PEAKS_TEST_SEAM === undefined) {
214
+ process.exit(process.exitCode || 0);
215
+ }
94
216
  });
95
217
  addJsonOption(gate
96
218
  .command('bypass')
@@ -0,0 +1,21 @@
1
+ /**
2
+ * rid-007 Family 1 merge: contract-commands + classify-classify-commands.
3
+ *
4
+ * Pre-merge originals (now deleted):
5
+ * - src/cli/commands/contract-commands.ts (167 lines)
6
+ * - src/cli/commands/classify-classify-commands.ts (190 lines)
7
+ *
8
+ * Both exported `register*Commands` functions are preserved **verbatim**
9
+ * (function name, signature, body). The merge is a structural refactor
10
+ * only — no behavior change. `autoRegisterAllCommands` discovers both
11
+ * exports from this single file.
12
+ *
13
+ * Why one file: the RD slice rid-007 user-confirmed one-family merge
14
+ * minimum; Family 1 is the cleanest demonstration because both inputs
15
+ * sit under 200 lines and the merged file lands at ~360 lines — well
16
+ * below the 400-line AC-9 sanity guard and the 800-line scan gate.
17
+ */
18
+ import { Command } from 'commander';
19
+ import { type ProgramIO } from '../cli-helpers.js';
20
+ export declare function registerContractCommands(program: Command, io: ProgramIO): void;
21
+ export declare function registerClassifyCommands(program: Command, io: ProgramIO): void;
@@ -0,0 +1,273 @@
1
+ /**
2
+ * rid-007 Family 1 merge: contract-commands + classify-classify-commands.
3
+ *
4
+ * Pre-merge originals (now deleted):
5
+ * - src/cli/commands/contract-commands.ts (167 lines)
6
+ * - src/cli/commands/classify-classify-commands.ts (190 lines)
7
+ *
8
+ * Both exported `register*Commands` functions are preserved **verbatim**
9
+ * (function name, signature, body). The merge is a structural refactor
10
+ * only — no behavior change. `autoRegisterAllCommands` discovers both
11
+ * exports from this single file.
12
+ *
13
+ * Why one file: the RD slice rid-007 user-confirmed one-family merge
14
+ * minimum; Family 1 is the cleanest demonstration because both inputs
15
+ * sit under 200 lines and the merged file lands at ~360 lines — well
16
+ * below the 400-line AC-9 sanity guard and the 800-line scan gate.
17
+ */
18
+ // ===========================================================================
19
+ // Shared imports (consolidated; both originals pulled names from these
20
+ // same modules — `cli-helpers.ts` re-exports `getErrorMessage` from
21
+ // `peaks-loop-shared/result`, so the single canonical import is
22
+ // functionally identical to either original line.)
23
+ // ===========================================================================
24
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
25
+ import { join } from 'node:path';
26
+ import { fail, ok, getErrorMessage } from 'peaks-loop-shared/result';
27
+ import { addJsonOption, printResult } from '../cli-helpers.js';
28
+ import { writeContract } from '../../services/dispatch/contract-store.js';
29
+ import { getCurrentSessionId } from '../../services/skills/skill-presence-service.js';
30
+ import { classifyTask } from '../../services/classify/classify-service.js';
31
+ import { TASK_LEVELS } from '../../services/classify/classify-types.js';
32
+ import { loadPreferences } from '../../services/preferences/preferences-service.js';
33
+ // ===========================================================================
34
+ // Section 1: `peaks contract` CLI commands (was contract-commands.ts).
35
+ // ===========================================================================
36
+ const INPUT_LIMIT_BYTES = 256 * 1024;
37
+ /** Split a comma-separated flag value into a trimmed string array. */
38
+ function splitCsv(value) {
39
+ if (value === undefined)
40
+ return [];
41
+ return value
42
+ .split(',')
43
+ .map((s) => s.trim())
44
+ .filter((s) => s.length > 0);
45
+ }
46
+ export function registerContractCommands(program, io) {
47
+ const contract = program
48
+ .command('contract')
49
+ .description('Slice contract store (skill-first / CLI-auxiliary). These commands ' +
50
+ 'are primitives that peaks-code / peaks-rd SKILL.md compose. The LLM-side ' +
51
+ 'runner (the IDE-resident sub-agent that finished a slice) calls ' +
52
+ '`peaks contract write` to persist the slice\'s public surface; the ' +
53
+ 'orchestrator picks it up on the next dispatch run via listContracts() ' +
54
+ 'and splices it into downstream prompts via formatContractInjection().');
55
+ // ─────────────────────────────────────────────────────────────────
56
+ // peaks contract write --project <root> --session-id <sid>
57
+ // --slice-id <id> --exports <a,b> --types <x,y> --signatures <s1,s2>
58
+ // [--broadcast-to <b1,b2>] [--completed-at <iso>]
59
+ // ─────────────────────────────────────────────────────────────────
60
+ addJsonOption(contract
61
+ .command('write')
62
+ .description('2.7.0 slice-dag-dispatcher MVP: persist a finished slice\'s public ' +
63
+ 'surface (exports / types / publicSignatures) to disk at ' +
64
+ '.peaks/_runtime/<sessionId>/dispatch/contracts/<slice-id>.json. ' +
65
+ 'The orchestrator picks it up on the next dispatch run. Idempotent: ' +
66
+ 're-running with the same inputs overwrites in place; the SHA-256 ' +
67
+ 'contractHash is content-derived so a contract write from a different ' +
68
+ 'runner (re-execution) is detected as a content change.')
69
+ .option('--project <path>', 'target project root (defaults to cwd)')
70
+ .option('--session-id <sid>', 'session id (default: resolve from .peaks/_runtime/session.json; falls back to PEAKS_SESSION_ID env var; final fallback "unknown-sid")')
71
+ .requiredOption('--slice-id <id>', 'slice id; must be non-empty; used as the contract filename basename')
72
+ .option('--exports <list>', 'comma-separated public export names (e.g. "validateDag,topologicalLevels")')
73
+ .option('--types <list>', 'comma-separated public type names (e.g. "SliceDag,SliceNode")')
74
+ .option('--signatures <list>', 'comma-separated public function/method signatures (e.g. "validateDag(dag: SliceDag): void")')
75
+ .option('--broadcast-to <list>', 'comma-separated downstream slice ids that should auto-inherit this contract (e.g. "B,C")')
76
+ .option('--completed-at <iso>', 'ISO 8601 timestamp; defaults to now()')).action((options) => {
77
+ const asJson = options.json === true;
78
+ const projectRoot = options.project ?? process.cwd();
79
+ // Slice 2026-06-26-unknown-sid-fallback-fix: see dispatch-commands.ts.
80
+ const sid = options.sessionId
81
+ ?? process.env.PEAKS_SESSION_ID
82
+ ?? getCurrentSessionId(projectRoot)
83
+ ?? 'unknown-sid';
84
+ const sliceId = options.sliceId;
85
+ if (sliceId === undefined || sliceId.length === 0) {
86
+ printResult(io, fail('contract.write', 'MISSING_SLICE_ID', '--slice-id is required', { path: null, contract: null }, [
87
+ 'Re-run with --slice-id <id> (must be non-empty; used as the contract filename basename).'
88
+ ]), asJson);
89
+ process.exitCode = 1;
90
+ return;
91
+ }
92
+ const exports = splitCsv(options.exports);
93
+ const types = splitCsv(options.types);
94
+ const signatures = splitCsv(options.signatures);
95
+ const broadcastTo = splitCsv(options.broadcastTo);
96
+ // Cap the combined input to protect the file IO from runaway values
97
+ // (e.g. a 10MB comma-separated --signatures flag).
98
+ const inputSize = sliceId.length +
99
+ sid.length +
100
+ exports.join(',').length +
101
+ types.join(',').length +
102
+ signatures.join(',').length +
103
+ broadcastTo.join(',').length +
104
+ (options.completedAt?.length ?? 0);
105
+ if (inputSize > INPUT_LIMIT_BYTES) {
106
+ printResult(io, fail('contract.write', 'INPUT_TOO_LARGE', `combined input size ${inputSize} bytes exceeds ${INPUT_LIMIT_BYTES} (likely oversized --exports/--types/--signatures lists)`, { path: null, contract: null }, [
107
+ 'Split the slice into smaller surfaces or omit optional fields.'
108
+ ]), asJson);
109
+ process.exitCode = 1;
110
+ return;
111
+ }
112
+ try {
113
+ const input = {
114
+ sliceId,
115
+ sessionId: sid,
116
+ exports,
117
+ types,
118
+ publicSignatures: signatures,
119
+ ...(broadcastTo.length > 0 ? { broadcastTo } : {}),
120
+ ...(options.completedAt !== undefined ? { completedAt: options.completedAt } : {})
121
+ };
122
+ const result = writeContract(projectRoot, sid, input);
123
+ printResult(io, ok('contract.write', {
124
+ path: result.path,
125
+ contractHash: result.contract.contractHash,
126
+ sliceId: result.contract.sliceId,
127
+ sessionId: result.contract.sessionId,
128
+ completedAt: result.contract.completedAt,
129
+ exportCount: result.contract.exports.length,
130
+ typeCount: result.contract.types.length,
131
+ signatureCount: result.contract.publicSignatures.length,
132
+ broadcastTo: result.contract.broadcastTo ?? []
133
+ }, [], [
134
+ `Contract written; orchestrator will pick it up on the next \`peaks sub-agent dispatch --from-dag\` run.`,
135
+ `Re-running with the same inputs is idempotent (overwrites in place).`
136
+ ]), asJson);
137
+ }
138
+ catch (err) {
139
+ printResult(io, fail('contract.write', 'WRITE_ERROR', getErrorMessage(err), { path: null, contract: null }, [
140
+ 'See error message; check that --project is a writable directory and --slice-id is a valid filename basename (no path separators).'
141
+ ]), asJson);
142
+ process.exitCode = 1;
143
+ }
144
+ });
145
+ }
146
+ const CLASSIFY_AUDIT_FILE = 'classify-audit.jsonl';
147
+ function getSignalsFromGitDiff(projectRoot) {
148
+ // Use git diff --stat to extract file count + line count. Fall back to
149
+ // zeros if git is unavailable (e.g. fresh repo with no commits).
150
+ let stdout;
151
+ try {
152
+ const { execFileSync } = require('node:child_process');
153
+ stdout = execFileSync('git', ['diff', '--shortstat', 'HEAD'], {
154
+ cwd: projectRoot,
155
+ stdio: ['ignore', 'pipe', 'ignore'],
156
+ maxBuffer: 32 * 1024 * 1024,
157
+ }).toString('utf8');
158
+ }
159
+ catch {
160
+ return { filesChanged: 0, linesChanged: 0, touchesDependencies: false, touchesMigrationScripts: false, isPureRefactor: true, keywords: [] };
161
+ }
162
+ const lines = stdout.split('\n').filter((l) => l.trim().length > 0);
163
+ const filesChanged = lines.length;
164
+ let added = 0;
165
+ let removed = 0;
166
+ let touchesDependencies = false;
167
+ let touchesMigrationScripts = false;
168
+ for (const line of lines) {
169
+ const match = /(\d+)\s+insertion.*?(\d+)\s+deletion/.exec(line);
170
+ if (match) {
171
+ added += Number(match[1]);
172
+ removed += Number(match[2]);
173
+ }
174
+ if (/(package\.json|pnpm-lock\.yaml|yarn\.lock|requirements\.txt|go\.mod)/.test(line)) {
175
+ touchesDependencies = true;
176
+ }
177
+ if (/(migrate|codemod|backfill|schema)/.test(line)) {
178
+ touchesMigrationScripts = true;
179
+ }
180
+ }
181
+ // isPureRefactor: heuristic — if added lines / removed lines < 0.1 OR
182
+ // no new exports were added, treat as refactor. For L2.2 the signal is
183
+ // binary (true/false). Default: true (no behavior change is the safe
184
+ // assumption; flip to false when keyword 'add' / 'new' / 'feature' present).
185
+ const isPureRefactor = true;
186
+ return {
187
+ filesChanged,
188
+ linesChanged: added + removed,
189
+ touchesDependencies,
190
+ touchesMigrationScripts,
191
+ isPureRefactor,
192
+ keywords: [],
193
+ };
194
+ }
195
+ function appendAuditEntry(projectRoot, entry) {
196
+ const auditDir = join(projectRoot, '.peaks/_runtime');
197
+ if (!existsSync(auditDir)) {
198
+ try {
199
+ mkdirSync(auditDir, { recursive: true });
200
+ }
201
+ catch { /* ignore */ } // TODO(g2): legacy silent catch — grace: 1 minor release (v2.14.0)
202
+ }
203
+ const auditPath = join(auditDir, CLASSIFY_AUDIT_FILE);
204
+ let body = '';
205
+ try {
206
+ if (existsSync(auditPath)) {
207
+ body = readFileSync(auditPath, 'utf8');
208
+ }
209
+ }
210
+ catch { /* ignore */ } // TODO(g2): legacy silent catch — grace: 1 minor release (v2.14.0)
211
+ body += JSON.stringify(entry) + '\n';
212
+ try {
213
+ writeFileSync(auditPath, body);
214
+ }
215
+ catch { /* best-effort */ } // TODO(g2): legacy silent catch — grace: 1 minor release (v2.14.0)
216
+ }
217
+ function isTaskLevel(value) {
218
+ return TASK_LEVELS.includes(value);
219
+ }
220
+ export function registerClassifyCommands(program, io) {
221
+ const classify = program
222
+ .command('classify')
223
+ .description('L1a task classification: 5-level heuristic (typo/bug/feature/refactor/migration) + override/upgrade + audit log');
224
+ addJsonOption(classify
225
+ .command('run')
226
+ .description('Classify the current diff (git diff HEAD) into one of 5 task levels')
227
+ .requiredOption('--project <path>', 'target project root')
228
+ .option('--override <level>', 'force a level (one of typo|bug|feature|refactor|migration); requires --reason')
229
+ .option('--reason <text>', 'reason for the override (mandatory when --override is set)')).action(async (options) => {
230
+ try {
231
+ const prefs = await loadPreferences(options.project);
232
+ const signals = getSignalsFromGitDiff(options.project);
233
+ let override;
234
+ if (options.override !== undefined) {
235
+ if (!isTaskLevel(options.override)) {
236
+ printResult(io, fail('classify.run', 'INVALID_LEVEL', `level must be one of: ${TASK_LEVELS.join(', ')}`, { provided: options.override }, ['Pass one of typo, bug, feature, refactor, migration']), options.json);
237
+ process.exitCode = 1;
238
+ return;
239
+ }
240
+ if (options.reason === undefined || options.reason.length === 0) {
241
+ printResult(io, fail('classify.run', 'REASON_REQUIRED', '--reason is required when --override is set', {}, ['Provide a non-empty reason for the override']), options.json);
242
+ process.exitCode = 1;
243
+ return;
244
+ }
245
+ override = { level: options.override, reason: options.reason };
246
+ }
247
+ const result = classifyTask(override !== undefined
248
+ ? { signals, conservatism: prefs.classifyConservatism, override }
249
+ : { signals, conservatism: prefs.classifyConservatism }, prefs.classifyRules.feature_threshold_files, prefs.classifyRules.feature_threshold_lines);
250
+ appendAuditEntry(options.project, result.audit);
251
+ printResult(io, ok('classify.run', result, [], [
252
+ `gate set for level "${result.level}": ${result.gateSet.stages.join(', ')}`,
253
+ `audit log: .peaks/_runtime/${CLASSIFY_AUDIT_FILE}`,
254
+ ]), options.json);
255
+ }
256
+ catch (error) {
257
+ printResult(io, fail('classify.run', 'CLASSIFY_RUN_FAILED', getErrorMessage(error), { projectRoot: options.project }, ['Run peaks classify --help for usage']), options.json);
258
+ process.exitCode = 1;
259
+ }
260
+ });
261
+ // Downgrade is REFUSED per spec §4. Surface this as a hard fail.
262
+ classify
263
+ .command('downgrade')
264
+ .description('REFUSED per spec §4 — peaks-loop never downgrades a classification; ask the user to override explicitly')
265
+ .requiredOption('--level <level>', 'attempted level')
266
+ .requiredOption('--reason <text>', 'reason for the attempt (always rejected)')
267
+ .requiredOption('--project <path>', 'target project root')
268
+ .option('--json', 'print machine-readable JSON envelope')
269
+ .action(async (options) => {
270
+ printResult(io, fail('classify.downgrade', 'DOWNGRADE_REFUSED', 'peaks classify downgrade is refused per spec §4. Use --override (with reason) on `classify run` to force a level; the CLI never downgrades a classification unilaterally.', { attemptedLevel: options.level, reason: options.reason }, ['Use `peaks classify run --override <level> --reason "<text>"` instead']), options.json);
271
+ process.exitCode = 2;
272
+ });
273
+ }
@@ -12,9 +12,10 @@
12
12
  * point at a sensitive file outside the runtime tree.
13
13
  */
14
14
  import { existsSync } from 'node:fs';
15
- import { fail, getErrorMessage, ok } from '../../shared/result.js';
15
+ import { fail, getErrorMessage, ok } from 'peaks-loop-shared/result';
16
16
  import { addJsonOption, printResult } from '../cli-helpers.js';
17
- import { appendHeartbeat } from '../../services/dispatch/dispatch-record-writer.js';
17
+ import { appendHeartbeat, setStage, tryAutoReleaseLease } from '../../services/dispatch/dispatch-record-writer.js';
18
+ import { isStageLabel } from '../../services/dispatch/stage-enum.js';
18
19
  import { assertSafeDispatchRecordPath } from '../../services/security/safe-settings-path.js';
19
20
  import { writeLogEntry } from '../../services/log/logger.js';
20
21
  import { HEARTBEAT_STATUSES } from './sub-agent-shared.js';
@@ -26,9 +27,15 @@ export function registerHeartbeatCommand(parent, io) {
26
27
  'wait and renders a status line. Sub-agents should call this at ' +
27
28
  'least every 30s (configurable via SKILL.md heartbeatIntervalSec).')
28
29
  .requiredOption('--record <path>', 'absolute path to a dispatch record JSON')
29
- .requiredOption('--status <state>', 'queued | running | finalizing | done | failed | stale')
30
+ .requiredOption('--status <state>', 'queued | running | finalizing | done | failed | stale | cancelled | no-execution | never-started | unreadable')
30
31
  .requiredOption('--progress <pct>', 'integer 0-100')
31
32
  .option('--note <text>', 'free-form progress note (≤ 200 chars)')
33
+ // Slice 2026-07-29-dispatch-stall-governance / S5 (AC-5.2) —
34
+ // optional --stage flag. The label is bounded (see
35
+ // src/services/dispatch/stage-enum.ts); an unknown value is
36
+ // rejected with INVALID_STAGE so the watch surface never
37
+ // accumulates typo'd labels.
38
+ .option('--stage <label>', 'bounded stage label (intake | planning | gathering | analyzing | writing | testing | reviewing | finalizing)')
32
39
  .option('--project <path>', 'trusted project root (defaults to cwd); used for the R-2 path guard so a malicious --record cannot point at another project\'s dispatch record')).action((options) => {
33
40
  const asJson = options.json === true;
34
41
  if (!options.record || !existsSync(options.record)) {
@@ -69,21 +76,85 @@ export function registerHeartbeatCommand(parent, io) {
69
76
  // root's .peaks/_sub_agents/ subdir.
70
77
  const trustedRoot = options.project ?? process.cwd();
71
78
  assertSafeDispatchRecordPath(options.record, trustedRoot);
79
+ // Slice 2026-07-29-dispatch-stall-governance / S5 (AC-5.2) —
80
+ // validate the optional --stage against the bounded enum
81
+ // BEFORE the heartbeat write so the caller gets a specific
82
+ // INVALID_STAGE error rather than a generic HEARTBEAT_ERROR.
83
+ let stageLabel = null;
84
+ if (options.stage !== undefined && options.stage.length > 0) {
85
+ if (!isStageLabel(options.stage)) {
86
+ printResult(io, fail('sub-agent.heartbeat', 'INVALID_STAGE', `--stage must be one of intake | planning | gathering | analyzing | writing | testing | reviewing | finalizing (got ${options.stage})`, { recordPath: options.record, truncated: false }, [
87
+ 'Use one of the bounded stage labels; free-form text is not accepted.'
88
+ ]), asJson);
89
+ process.exitCode = 1;
90
+ return;
91
+ }
92
+ stageLabel = options.stage;
93
+ }
72
94
  const result = appendHeartbeat({
73
95
  recordPath: options.record,
74
96
  status: options.status,
75
97
  progress,
76
98
  ...(options.note !== undefined ? { note: options.note } : {})
77
99
  });
100
+ // S5 — promote the stage if --stage was supplied. setStage
101
+ // re-validates and is a no-op when stage is null. The
102
+ // appendHeartbeat call above already wrote the heartbeat;
103
+ // setStage is a separate file-locked read-modify-write on the
104
+ // same record.
105
+ if (stageLabel !== null) {
106
+ setStage({ recordPath: options.record, stage: stageLabel });
107
+ }
108
+ // Slice 2026-07-29-worktree-l2-extended Part 24: the
109
+ // heartbeat envelope surfaces a `leaseHint` field when the
110
+ // dispatch owns a lease. The hint is a one-line reminder
111
+ // for the sub-agent that the lease is the L2 surface it
112
+ // should clean up before exiting (or, per Part 3.A.2, the
113
+ // auto-release hook will fire on the next terminal status).
114
+ // The hint is opt-in via a flag so non-lease dispatches
115
+ // do not pollute the envelope.
116
+ const leaseId = result.record.leaseId ?? null;
117
+ const leaseHint = leaseId !== null
118
+ ? `You own lease \`${leaseId}\`. Run \`peaks worktree release --lease-id ${leaseId}\` before exit, or report a terminal status (done/failed/cancelled) to let peaks-loop auto-release.`
119
+ : null;
78
120
  printResult(io, ok('sub-agent.heartbeat', {
79
121
  // Slice 2026-06-23-audit-4th #E1: envelopeVersion marker
80
- envelopeVersion: '2.1.0',
122
+ // Part 24: bumped to 2.2.0 to advertise the new `leaseHint`
123
+ // field; readers from 2.1.0 ignore the unknown field.
124
+ envelopeVersion: '2.2.0',
81
125
  recordPath: options.record,
82
126
  heartbeatCount: result.record.heartbeats.length,
83
127
  lastBeatAt: result.record.lastBeatAt,
84
128
  status: result.record.status,
85
- truncated: result.truncated
129
+ truncated: result.truncated,
130
+ leaseHint
86
131
  }, [], ['Continue business logic; heartbeat is fire-and-forget.']), asJson);
132
+ // Slice 2026-07-29-worktree-l2-extended Part 3.A.2: when the
133
+ // heartbeat reports a TERMINAL status (done / failed / cancelled
134
+ // / no-execution) AND the dispatch owns a lease, fire the
135
+ // detached release. This is the common path: most sub-agents
136
+ // finalize via heartbeat before the share-reducer calls
137
+ // markCompleted, so the heartbeat path is the more frequent
138
+ // release trigger in practice. markCompleted's hook (Part 3.A.1)
139
+ // remains as the safety net for sub-agents that finalize via
140
+ // share without a terminal heartbeat. The release is detached
141
+ // + best-effort; a failure here cannot roll back the heartbeat
142
+ // write (the printResult above already shipped the response).
143
+ const terminalHeartbeatStatuses = new Set([
144
+ 'done', 'failed', 'cancelled', 'no-execution'
145
+ ]);
146
+ if (terminalHeartbeatStatuses.has(options.status) && result.record.leaseId !== null) {
147
+ try {
148
+ tryAutoReleaseLease({
149
+ projectRoot: trustedRoot,
150
+ sessionId: result.record.sessionId,
151
+ leaseId: result.record.leaseId
152
+ });
153
+ }
154
+ catch { // best-effort
155
+ /* swallow */
156
+ }
157
+ }
87
158
  // Slice 2026-06-23-audit-4th #B1: structured log on success.
88
159
  try {
89
160
  writeLogEntry({