claude-code-session-manager 0.97.0 → 0.100.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (430) hide show
  1. package/README.md +3 -3
  2. package/bin/cli.cjs +16 -4
  3. package/bin/node-floor.cjs +41 -0
  4. package/dist/assets/{DataModel-D93FoaOm.js → DataModel-HdR0AxFq.js} +1 -1
  5. package/dist/assets/{History-CXxxqNP1.js → History-Dr4Jr_5z.js} +2 -2
  6. package/dist/assets/{Hooks-CzeYFQjq.js → Hooks-NhrvH3JP.js} +3 -3
  7. package/dist/assets/{Library-D2zPkwOC.js → Library-Dv_EtIMr.js} +1 -1
  8. package/dist/assets/{MarkdownEditor-DbinP78A.js → MarkdownEditor-DTKDWV_n.js} +1 -1
  9. package/dist/assets/{McpServers-Bu_sVGLr.js → McpServers-M034G9B3.js} +2 -2
  10. package/dist/assets/{Memory-BgiI6LZ_.js → Memory-DzF1B4gx.js} +6 -6
  11. package/dist/assets/{Permissions-C1MMzZRm.js → Permissions-DrgbSGWl.js} +3 -3
  12. package/dist/assets/{Plugins-Dhu6wAZi.js → Plugins-Bd3ef35w.js} +2 -2
  13. package/dist/assets/{ProvenanceBadge-C3AcWKMG.js → ProvenanceBadge-DcBHT1Iy.js} +1 -1
  14. package/dist/assets/{SaveBar-plwYm0iO.js → SaveBar-rimJem-Y.js} +1 -1
  15. package/dist/assets/Scheduler-DjgFJl2w.js +16 -0
  16. package/dist/assets/{ScopeSwitcher-DFy0vv_o.js → ScopeSwitcher-CnAzTBrl.js} +1 -1
  17. package/dist/assets/Settings-Dc1F_sSg.js +3 -0
  18. package/dist/assets/{SkillReferenceGraph-DsI7hRPB.js → SkillReferenceGraph-B9aZDecM.js} +1 -1
  19. package/dist/assets/{Skills-3E748UfB.js → Skills-DRXd18gu.js} +2 -2
  20. package/dist/assets/{SystemPrompt-BqmfUJOd.js → SystemPrompt-ahy5-Fwe.js} +1 -1
  21. package/dist/assets/{TagLibrary-CPz6fO8Q.js → TagLibrary-C_tToDeL.js} +1 -1
  22. package/dist/assets/{TiptapBody-D9EoBQ1P.js → TiptapBody-DvdefS6K.js} +1 -1
  23. package/dist/assets/{Toggle-BHAmZNDn.js → Toggle-6Jl5Njc6.js} +1 -1
  24. package/dist/assets/{index-CKH5Uxik.css → index-Bta-hwud.css} +1 -1
  25. package/dist/assets/{index-tn5JIVPj.js → index-vx8O73l8.js} +497 -502
  26. package/dist/assets/{settingsSchema-Crqy_aNz.js → settingsSchema-D6iRguNV.js} +1 -1
  27. package/dist/index.html +2 -2
  28. package/package.json +26 -29
  29. package/plugins/CLAUDE.md +6 -6
  30. package/plugins/session-manager-dev/skills/builder/4-manual/SKILL.md +23 -19
  31. package/plugins/session-manager-dev/skills/builder/SKILL.md +3 -3
  32. package/plugins/session-manager-dev/skills/develop/SKILL.md +254 -510
  33. package/plugins/session-manager-dev/skills/develop/standards.md +17 -22
  34. package/scripts/README.md +3 -11
  35. package/scripts/audit-ops-hygiene.cjs +3 -3
  36. package/scripts/hooks/lib/guard-prd-writes-policy.cjs +1 -1
  37. package/scripts/hooks/lib/guard-self-schedule-policy.cjs +1 -1
  38. package/scripts/mint-epic.cjs +2 -3
  39. package/scripts/scheduler-mcp-server.cjs +36 -7
  40. package/src/main/agentLibrary.cjs +22 -2
  41. package/src/main/build-info.json +4 -4
  42. package/src/main/config.cjs +41 -38
  43. package/src/main/docEdit.cjs +4 -1
  44. package/src/main/files.cjs +2 -5
  45. package/src/main/health.cjs +1 -1
  46. package/src/main/index.cjs +23 -10
  47. package/src/main/ipcSchemas.cjs +28 -35
  48. package/src/main/lib/agentPersonaSchema.cjs +13 -2
  49. package/src/main/lib/atomicFs.cjs +116 -0
  50. package/src/main/lib/branchSweep.cjs +13 -12
  51. package/src/main/lib/buildTarget.cjs +3 -3
  52. package/src/main/lib/claudeCliCaps.cjs +129 -0
  53. package/src/main/lib/credentials.cjs +2 -4
  54. package/src/main/lib/crossProjectFeedback.cjs +3 -3
  55. package/src/main/lib/cwdClassify.cjs +15 -3
  56. package/src/main/lib/definitionOfDone.cjs +207 -125
  57. package/src/main/lib/dodDrainHook.cjs +11 -5
  58. package/src/main/lib/epicMint.cjs +2 -4
  59. package/src/main/lib/epicStatusMirror.cjs +8 -10
  60. package/src/main/lib/epicWorktreeProjectConfig.cjs +2 -4
  61. package/src/main/lib/gateAuthority.cjs +96 -0
  62. package/src/main/lib/gitExec.cjs +69 -0
  63. package/src/main/lib/gitWorktree.cjs +656 -92
  64. package/src/main/lib/instanceLock.cjs +5 -8
  65. package/src/main/lib/launchFailure.cjs +2 -3
  66. package/src/main/lib/macroLibrary.cjs +386 -0
  67. package/src/main/lib/mcpToolCatalog.cjs +26 -17
  68. package/src/main/lib/needsReviewLedger.cjs +1 -1
  69. package/src/main/lib/opsErrorLog.cjs +11 -1
  70. package/src/main/lib/opsOwnership.cjs +0 -5
  71. package/src/main/lib/pidAlive.cjs +32 -0
  72. package/src/main/lib/prdCreate.cjs +153 -19
  73. package/src/main/lib/prdDisposition.cjs +2 -2
  74. package/src/main/lib/prdGateFiles.cjs +284 -0
  75. package/src/main/lib/prdLocations.cjs +61 -0
  76. package/src/main/lib/prdMigration.cjs +2 -3
  77. package/src/main/lib/prdSizing.cjs +2 -1
  78. package/src/main/lib/promptSessionSchema.cjs +10 -2
  79. package/src/main/lib/rcaReport.cjs +4 -6
  80. package/src/main/lib/reaperHelpers.cjs +8 -1
  81. package/src/main/lib/reservationExpiry.cjs +6 -1
  82. package/src/main/lib/reviewNotice.cjs +263 -0
  83. package/src/main/lib/runClaudeP.cjs +4 -1
  84. package/src/main/lib/schedulerPaths.cjs +22 -2
  85. package/src/main/lib/sessionSlots.cjs +2 -4
  86. package/src/main/lib/shippedPersonaSeeds.cjs +179 -0
  87. package/src/main/lib/timeoutShim.cjs +123 -0
  88. package/src/main/lib/timeoutShimScript.cjs +275 -0
  89. package/src/main/lib/upgradeDrain.cjs +4 -10
  90. package/src/main/lib/watchdogHelpers.cjs +33 -30
  91. package/src/main/lib/workTypeLibrary.cjs +7 -1
  92. package/src/main/promptSessionEvents.cjs +37 -13
  93. package/src/main/queueOps.cjs +17 -6
  94. package/src/main/scheduler.cjs +975 -203
  95. package/src/main/seedAgentPersonas.cjs +278 -23
  96. package/src/main/supervisor.cjs +4 -1
  97. package/src/main/templates/PRD_AUTHORING.md +126 -406
  98. package/src/preload/__tests__/preload-surface.test.cjs +68 -0
  99. package/src/preload/api.d.ts +41 -94
  100. package/src/preload/index.cjs +14 -9
  101. package/src/seed/agents/architect.md +1 -0
  102. package/src/seed/agents/dev-lead.md +17 -18
  103. package/src/seed/agents/project-home-builder.md +1 -0
  104. package/src/seed/agents/validator.md +10 -4
  105. package/dist/assets/HostBilko-nESrTRIg.js +0 -1
  106. package/dist/assets/Scheduler-BVYTeh39.js +0 -16
  107. package/dist/assets/Settings-DIhGgdfs.js +0 -3
  108. package/src/main/__tests__/activeIndexMerge.test.cjs +0 -235
  109. package/src/main/__tests__/agentEffortResolve.test.cjs +0 -117
  110. package/src/main/__tests__/agentLibrary.test.cjs +0 -276
  111. package/src/main/__tests__/agentModelResolve.test.cjs +0 -311
  112. package/src/main/__tests__/agentOverlayWrite.test.cjs +0 -95
  113. package/src/main/__tests__/bilkoHost-deriveSlug.test.cjs +0 -26
  114. package/src/main/__tests__/bilkoHost-integration.test.cjs +0 -118
  115. package/src/main/__tests__/bilkoHostCore.test.cjs +0 -72
  116. package/src/main/__tests__/broadcastCoalescer.test.cjs +0 -122
  117. package/src/main/__tests__/chat-cancel-terminal.test.cjs +0 -120
  118. package/src/main/__tests__/chat-dead-channels.test.cjs +0 -63
  119. package/src/main/__tests__/chat-exit-close-race.test.cjs +0 -146
  120. package/src/main/__tests__/chat-mcp-consent-notice.test.cjs +0 -139
  121. package/src/main/__tests__/chat-preamble-anchors.test.cjs +0 -100
  122. package/src/main/__tests__/chat-queue.test.cjs +0 -97
  123. package/src/main/__tests__/chat-stop-signal.test.cjs +0 -89
  124. package/src/main/__tests__/chatRunner-epic-worktree-execcwd.test.cjs +0 -125
  125. package/src/main/__tests__/chatRunner-session-flag-retry.test.cjs +0 -252
  126. package/src/main/__tests__/classifyPromptTicket.test.cjs +0 -101
  127. package/src/main/__tests__/classifyTranscriptLine.test.cjs +0 -201
  128. package/src/main/__tests__/computeDepHistorySatisfaction.test.cjs +0 -66
  129. package/src/main/__tests__/config-readText-bounded.test.cjs +0 -84
  130. package/src/main/__tests__/configWriteBoundaryOwners.test.cjs +0 -59
  131. package/src/main/__tests__/crossProjectFeedback.test.cjs +0 -334
  132. package/src/main/__tests__/crossProjectFeedbackRoutes.test.cjs +0 -161
  133. package/src/main/__tests__/dep-orphan-archive-health.test.cjs +0 -77
  134. package/src/main/__tests__/develop-skill-failure-modes.test.cjs +0 -70
  135. package/src/main/__tests__/docEdit.test.cjs +0 -244
  136. package/src/main/__tests__/dod-batchkey.test.cjs +0 -183
  137. package/src/main/__tests__/dod-drain-hook.test.cjs +0 -302
  138. package/src/main/__tests__/dod-report.test.cjs +0 -304
  139. package/src/main/__tests__/dod-reverify.test.cjs +0 -285
  140. package/src/main/__tests__/epicContextDigest.test.cjs +0 -174
  141. package/src/main/__tests__/epicMint.test.cjs +0 -332
  142. package/src/main/__tests__/epicMintTelemetryTap.test.cjs +0 -64
  143. package/src/main/__tests__/epicStatusMirror.test.cjs +0 -110
  144. package/src/main/__tests__/epicValidationHook.test.cjs +0 -291
  145. package/src/main/__tests__/exchanges.test.cjs +0 -122
  146. package/src/main/__tests__/exchangesPromptId.test.cjs +0 -61
  147. package/src/main/__tests__/extractJson.test.cjs +0 -51
  148. package/src/main/__tests__/files-reject-credentials.test.cjs +0 -40
  149. package/src/main/__tests__/fixtures/1218-fo-01-move-scripts-lib-into-src-main-lib.log +0 -556
  150. package/src/main/__tests__/flatPrdTickSweep.test.cjs +0 -110
  151. package/src/main/__tests__/health-build-freshness.test.cjs +0 -39
  152. package/src/main/__tests__/health-claude-md-budget.test.cjs +0 -57
  153. package/src/main/__tests__/health-credentials.test.cjs +0 -81
  154. package/src/main/__tests__/health-delegation-chain.test.cjs +0 -124
  155. package/src/main/__tests__/health-per-project-stall.test.cjs +0 -84
  156. package/src/main/__tests__/health-prd-migration.test.cjs +0 -37
  157. package/src/main/__tests__/health-queue-dispatch.test.cjs +0 -135
  158. package/src/main/__tests__/health-starve-escalation.test.cjs +0 -94
  159. package/src/main/__tests__/health-tick-liveness.test.cjs +0 -179
  160. package/src/main/__tests__/health-usage-poller.test.cjs +0 -144
  161. package/src/main/__tests__/health-worktree-cap-blocked.test.cjs +0 -65
  162. package/src/main/__tests__/heapSnapshot.test.cjs +0 -121
  163. package/src/main/__tests__/historyAggregatorIntraday.test.cjs +0 -313
  164. package/src/main/__tests__/historyDashboard.test.cjs +0 -163
  165. package/src/main/__tests__/historyRollup.test.cjs +0 -333
  166. package/src/main/__tests__/intradayRefresh.test.cjs +0 -39
  167. package/src/main/__tests__/ipcSchemas-dependsOn.test.cjs +0 -39
  168. package/src/main/__tests__/kg-augment.test.cjs +0 -195
  169. package/src/main/__tests__/loadGateDetailTick.test.cjs +0 -31
  170. package/src/main/__tests__/machineProfile.test.cjs +0 -152
  171. package/src/main/__tests__/mcpStatus.test.cjs +0 -61
  172. package/src/main/__tests__/memoryAggregate.test.cjs +0 -109
  173. package/src/main/__tests__/memoryStale.test.cjs +0 -88
  174. package/src/main/__tests__/needsReviewLedger.test.cjs +0 -162
  175. package/src/main/__tests__/openExternalApp-spawn-error.test.cjs +0 -25
  176. package/src/main/__tests__/opsErrorLog.test.cjs +0 -109
  177. package/src/main/__tests__/opsErrorLogTelemetryTap.test.cjs +0 -173
  178. package/src/main/__tests__/personaMerge.test.cjs +0 -169
  179. package/src/main/__tests__/planValidator.test.cjs +0 -125
  180. package/src/main/__tests__/pollLoop-dispatch-on-failure.test.cjs +0 -176
  181. package/src/main/__tests__/prd-group-allocator.test.cjs +0 -119
  182. package/src/main/__tests__/prdAdminRouteParity.test.cjs +0 -70
  183. package/src/main/__tests__/prdAdminRoutes.test.cjs +0 -718
  184. package/src/main/__tests__/prdAgentType.test.cjs +0 -103
  185. package/src/main/__tests__/prdAuthoringSeed.test.cjs +0 -39
  186. package/src/main/__tests__/prdCreate.test.cjs +0 -979
  187. package/src/main/__tests__/prdCreateDisposition.test.cjs +0 -201
  188. package/src/main/__tests__/prdCreatePlanId.test.cjs +0 -132
  189. package/src/main/__tests__/prdFrontmatterAgentType.test.cjs +0 -117
  190. package/src/main/__tests__/prdFrontmatterDependsOn.test.cjs +0 -136
  191. package/src/main/__tests__/prdFrontmatterDisposition.test.cjs +0 -125
  192. package/src/main/__tests__/prdFrontmatterQuietMachine.test.cjs +0 -108
  193. package/src/main/__tests__/prdLocations.test.cjs +0 -195
  194. package/src/main/__tests__/prdLocationsArchived.test.cjs +0 -201
  195. package/src/main/__tests__/prdMigration.test.cjs +0 -349
  196. package/src/main/__tests__/prdMigrationLegacyAdopt.test.cjs +0 -91
  197. package/src/main/__tests__/prdParserHighWater.test.cjs +0 -74
  198. package/src/main/__tests__/prdParserSourcePromptId.test.cjs +0 -65
  199. package/src/main/__tests__/prdSetDisposition.test.cjs +0 -222
  200. package/src/main/__tests__/prdSizing.test.cjs +0 -106
  201. package/src/main/__tests__/prdSourcePromptIdBackfill.test.cjs +0 -118
  202. package/src/main/__tests__/prdUpdateDependsOn.test.cjs +0 -160
  203. package/src/main/__tests__/proc-role-env.test.cjs +0 -125
  204. package/src/main/__tests__/procname-claude-spawn-sites.test.cjs +0 -304
  205. package/src/main/__tests__/procname-sm-processes.test.cjs +0 -127
  206. package/src/main/__tests__/projectHomeAdminRoutes.test.cjs +0 -177
  207. package/src/main/__tests__/projectPages.test.cjs +0 -137
  208. package/src/main/__tests__/promptSessionEvents.test.cjs +0 -234
  209. package/src/main/__tests__/promptSessionSchema.test.cjs +0 -101
  210. package/src/main/__tests__/promptSessionTranscript.test.cjs +0 -0
  211. package/src/main/__tests__/promptSessionsCreateEpicHandler.test.cjs +0 -159
  212. package/src/main/__tests__/pty-epic-worktree-spawn-cwd.test.cjs +0 -282
  213. package/src/main/__tests__/pty-session-open-telemetry.test.cjs +0 -96
  214. package/src/main/__tests__/pty-write-result.test.cjs +0 -46
  215. package/src/main/__tests__/queue-health-verdict.test.cjs +0 -170
  216. package/src/main/__tests__/queue-starvation-dispatch-driver.test.cjs +0 -286
  217. package/src/main/__tests__/queue-starvation-per-project.test.cjs +0 -147
  218. package/src/main/__tests__/queueHistory.test.cjs +0 -355
  219. package/src/main/__tests__/queueOps-interactive-ac-lint.test.cjs +0 -65
  220. package/src/main/__tests__/queueOpsArchiveDestination.test.cjs +0 -65
  221. package/src/main/__tests__/queueOpsAutoArchive.test.cjs +0 -154
  222. package/src/main/__tests__/rateLimitPollerStreak.test.cjs +0 -128
  223. package/src/main/__tests__/rcaReport.test.cjs +0 -266
  224. package/src/main/__tests__/reconcileFlatPrdSweep.test.cjs +0 -119
  225. package/src/main/__tests__/reconcileTiming.test.cjs +0 -135
  226. package/src/main/__tests__/runLogRetention.test.cjs +0 -489
  227. package/src/main/__tests__/runVerify-atomic-verdicts.test.cjs +0 -26
  228. package/src/main/__tests__/runVerify-blocked-by-foreign-wip.test.cjs +0 -58
  229. package/src/main/__tests__/runVerify-landed-commit-outranks.test.cjs +0 -191
  230. package/src/main/__tests__/runVerify-policy-denial.test.cjs +0 -89
  231. package/src/main/__tests__/runVerify-transcript-commit-evidence.test.cjs +0 -225
  232. package/src/main/__tests__/runVerify.test.cjs +0 -1784
  233. package/src/main/__tests__/scheduleJobSchema.test.cjs +0 -127
  234. package/src/main/__tests__/scheduleJobStatusDrift.test.cjs +0 -65
  235. package/src/main/__tests__/scheduleJobTransitions.test.cjs +0 -277
  236. package/src/main/__tests__/scheduleJobTransitionsGrep.test.cjs +0 -59
  237. package/src/main/__tests__/scheduleJobTransitionsTelemetryTap.test.cjs +0 -72
  238. package/src/main/__tests__/scheduler-admin-routes.test.cjs +0 -199
  239. package/src/main/__tests__/scheduler-adopted-run-supervision.test.cjs +0 -143
  240. package/src/main/__tests__/scheduler-already-satisfied-on-main.test.cjs +0 -105
  241. package/src/main/__tests__/scheduler-archive-completed-prd.test.cjs +0 -102
  242. package/src/main/__tests__/scheduler-archived-twin-guard.test.cjs +0 -155
  243. package/src/main/__tests__/scheduler-autofix-outcome.test.cjs +0 -188
  244. package/src/main/__tests__/scheduler-autofix-select.test.cjs +0 -439
  245. package/src/main/__tests__/scheduler-autopromote.test.cjs +0 -51
  246. package/src/main/__tests__/scheduler-bash-timeout-env.test.cjs +0 -101
  247. package/src/main/__tests__/scheduler-blocked-by-foreign-wip.test.cjs +0 -107
  248. package/src/main/__tests__/scheduler-boot-orphans.test.cjs +0 -153
  249. package/src/main/__tests__/scheduler-broadcast-reconcile.test.cjs +0 -121
  250. package/src/main/__tests__/scheduler-clear-queue-history.test.cjs +0 -134
  251. package/src/main/__tests__/scheduler-commit-guard-noop.test.cjs +0 -244
  252. package/src/main/__tests__/scheduler-committed-in-window.test.cjs +0 -182
  253. package/src/main/__tests__/scheduler-cross-project-batch.test.cjs +0 -43
  254. package/src/main/__tests__/scheduler-default-eligible-heal.test.cjs +0 -168
  255. package/src/main/__tests__/scheduler-dispatch-loop.test.cjs +0 -58
  256. package/src/main/__tests__/scheduler-effective-concurrency.test.cjs +0 -81
  257. package/src/main/__tests__/scheduler-epic-digest.test.cjs +0 -227
  258. package/src/main/__tests__/scheduler-failed-autoreset.test.cjs +0 -121
  259. package/src/main/__tests__/scheduler-finalize-dispatch-guards.test.cjs +0 -229
  260. package/src/main/__tests__/scheduler-find-prd-dir.test.cjs +0 -75
  261. package/src/main/__tests__/scheduler-fix-plan-path.test.cjs +0 -119
  262. package/src/main/__tests__/scheduler-force-tick-outcome.test.cjs +0 -46
  263. package/src/main/__tests__/scheduler-foreign-wip-manifest.test.cjs +0 -78
  264. package/src/main/__tests__/scheduler-gate-shadow.test.cjs +0 -119
  265. package/src/main/__tests__/scheduler-guard-verdict-autoresolve.test.cjs +0 -390
  266. package/src/main/__tests__/scheduler-heal-refusal.test.cjs +0 -61
  267. package/src/main/__tests__/scheduler-heartbeat-payload.test.cjs +0 -80
  268. package/src/main/__tests__/scheduler-inplace-salvage.test.cjs +0 -323
  269. package/src/main/__tests__/scheduler-integration-failure-stamp.test.cjs +0 -41
  270. package/src/main/__tests__/scheduler-investigation-clean-skip.test.cjs +0 -63
  271. package/src/main/__tests__/scheduler-investigation-prompt.test.cjs +0 -123
  272. package/src/main/__tests__/scheduler-job-budget.test.cjs +0 -172
  273. package/src/main/__tests__/scheduler-job-overrun.test.cjs +0 -175
  274. package/src/main/__tests__/scheduler-launch-failure.test.cjs +0 -199
  275. package/src/main/__tests__/scheduler-leftover-fields.test.cjs +0 -52
  276. package/src/main/__tests__/scheduler-leftover-quarantine.test.cjs +0 -199
  277. package/src/main/__tests__/scheduler-looks-done.test.cjs +0 -537
  278. package/src/main/__tests__/scheduler-manual-pause.test.cjs +0 -118
  279. package/src/main/__tests__/scheduler-mechanical-recovery.test.cjs +0 -245
  280. package/src/main/__tests__/scheduler-meta-code-sha.test.cjs +0 -46
  281. package/src/main/__tests__/scheduler-needs-review-autoresolve.test.cjs +0 -197
  282. package/src/main/__tests__/scheduler-never-stop.test.cjs +0 -157
  283. package/src/main/__tests__/scheduler-no-dead-end-status.test.cjs +0 -152
  284. package/src/main/__tests__/scheduler-no-orphan-run-dir.test.cjs +0 -81
  285. package/src/main/__tests__/scheduler-notify-originating-tab-transcript.test.cjs +0 -87
  286. package/src/main/__tests__/scheduler-notify-originating-tab.test.cjs +0 -343
  287. package/src/main/__tests__/scheduler-periodic-reverify-guard.test.cjs +0 -217
  288. package/src/main/__tests__/scheduler-porcelain-rename.test.cjs +0 -164
  289. package/src/main/__tests__/scheduler-prd-missing-skip.test.cjs +0 -161
  290. package/src/main/__tests__/scheduler-prd-persona-spawn.test.cjs +0 -178
  291. package/src/main/__tests__/scheduler-quarantine-autoresolve.test.cjs +0 -165
  292. package/src/main/__tests__/scheduler-quiet-machine-lease.test.cjs +0 -257
  293. package/src/main/__tests__/scheduler-rate-limit-cooldown-freshness.test.cjs +0 -123
  294. package/src/main/__tests__/scheduler-rate-limit-pause.test.cjs +0 -152
  295. package/src/main/__tests__/scheduler-rate-limit-spin-guard.test.cjs +0 -156
  296. package/src/main/__tests__/scheduler-reap-dead-running-jobs.test.cjs +0 -754
  297. package/src/main/__tests__/scheduler-reaper-helpers-basics.test.cjs +0 -87
  298. package/src/main/__tests__/scheduler-reconcile-cwd-preserve.test.cjs +0 -100
  299. package/src/main/__tests__/scheduler-reconcile-history-backfill.test.cjs +0 -105
  300. package/src/main/__tests__/scheduler-reconcile-invalid-repair.test.cjs +0 -203
  301. package/src/main/__tests__/scheduler-reconcile-quarantine.test.cjs +0 -247
  302. package/src/main/__tests__/scheduler-reset-job-fields-guard.test.cjs +0 -77
  303. package/src/main/__tests__/scheduler-resume-recovery.test.cjs +0 -254
  304. package/src/main/__tests__/scheduler-shard-quarantine.test.cjs +0 -115
  305. package/src/main/__tests__/scheduler-shared-tree-guard.test.cjs +0 -380
  306. package/src/main/__tests__/scheduler-sigterm-commit.test.cjs +0 -43
  307. package/src/main/__tests__/scheduler-stall-per-project.test.cjs +0 -126
  308. package/src/main/__tests__/scheduler-starve-escalation.test.cjs +0 -154
  309. package/src/main/__tests__/scheduler-stranded-autofix-park.test.cjs +0 -252
  310. package/src/main/__tests__/scheduler-stranded-investigation.test.cjs +0 -185
  311. package/src/main/__tests__/scheduler-stuck-failed-escalation.test.cjs +0 -136
  312. package/src/main/__tests__/scheduler-supervisor-record.test.cjs +0 -81
  313. package/src/main/__tests__/scheduler-tick-cancel-token.test.cjs +0 -54
  314. package/src/main/__tests__/scheduler-tick-wedge.test.cjs +0 -172
  315. package/src/main/__tests__/scheduler-transient-failure.test.cjs +0 -141
  316. package/src/main/__tests__/scheduler-unreadable-queue-guard.test.cjs +0 -62
  317. package/src/main/__tests__/scheduler-utilization-hold.test.cjs +0 -89
  318. package/src/main/__tests__/scheduler-verify-prd-path.test.cjs +0 -109
  319. package/src/main/__tests__/scheduler-worktree-cap-defer.test.cjs +0 -234
  320. package/src/main/__tests__/scheduler-worktree-exec-cwd.test.cjs +0 -120
  321. package/src/main/__tests__/scheduler-writeprd-epic-rollback.test.cjs +0 -105
  322. package/src/main/__tests__/schedulerBatchRootBlocker.test.cjs +0 -117
  323. package/src/main/__tests__/schedulerStateSidecarRestore.test.cjs +0 -110
  324. package/src/main/__tests__/seedAgentPersonas.test.cjs +0 -184
  325. package/src/main/__tests__/seedSchedulerMcp.test.cjs +0 -211
  326. package/src/main/__tests__/seedStatus.test.cjs +0 -100
  327. package/src/main/__tests__/seedValidatorPersona.test.cjs +0 -116
  328. package/src/main/__tests__/stop-signal-anchor.test.cjs +0 -75
  329. package/src/main/__tests__/telemetryClient.test.cjs +0 -1055
  330. package/src/main/__tests__/telemetryContract.test.cjs +0 -930
  331. package/src/main/__tests__/telemetrySettings.test.cjs +0 -210
  332. package/src/main/__tests__/transcripts-batch-flush.test.cjs +0 -249
  333. package/src/main/__tests__/transcripts-doFlush-array.test.cjs +0 -124
  334. package/src/main/__tests__/transcripts-paged-reads.test.cjs +0 -241
  335. package/src/main/__tests__/transcripts-worktree-epic-path.test.cjs +0 -154
  336. package/src/main/__tests__/transcriptsUsageFor.test.cjs +0 -206
  337. package/src/main/__tests__/uniquePrdNumbers.test.cjs +0 -153
  338. package/src/main/__tests__/usageSingleFlight.test.cjs +0 -169
  339. package/src/main/__tests__/validationSentinels.test.cjs +0 -84
  340. package/src/main/__tests__/workTypeLibrary.test.cjs +0 -89
  341. package/src/main/bilkoHost.cjs +0 -314
  342. package/src/main/bilkoHostCore.cjs +0 -89
  343. package/src/main/lib/__tests__/active-sessions.test.cjs +0 -251
  344. package/src/main/lib/__tests__/activeIndexRebuild.test.cjs +0 -179
  345. package/src/main/lib/__tests__/agentPersonaSchema.test.cjs +0 -67
  346. package/src/main/lib/__tests__/auditLog.test.cjs +0 -38
  347. package/src/main/lib/__tests__/bootSelfHeal.test.cjs +0 -107
  348. package/src/main/lib/__tests__/branchSweep.test.cjs +0 -164
  349. package/src/main/lib/__tests__/buildIdentity.test.cjs +0 -121
  350. package/src/main/lib/__tests__/buildTarget.test.cjs +0 -52
  351. package/src/main/lib/__tests__/childWithLog.test.cjs +0 -321
  352. package/src/main/lib/__tests__/coldBootPromptSessionsWrite.test.cjs +0 -87
  353. package/src/main/lib/__tests__/crashTelemetry.test.cjs +0 -103
  354. package/src/main/lib/__tests__/credentials-futile-refresh.test.cjs +0 -115
  355. package/src/main/lib/__tests__/cwdClassify.test.cjs +0 -111
  356. package/src/main/lib/__tests__/definitionOfDoneSequence.test.cjs +0 -95
  357. package/src/main/lib/__tests__/delegationReadiness.test.cjs +0 -1175
  358. package/src/main/lib/__tests__/dispatchLoop.test.cjs +0 -63
  359. package/src/main/lib/__tests__/effectiveModelInfo.test.cjs +0 -244
  360. package/src/main/lib/__tests__/ephemeralCwd.test.cjs +0 -91
  361. package/src/main/lib/__tests__/epicDelegationStats.test.cjs +0 -137
  362. package/src/main/lib/__tests__/epicSpawnCwd.test.cjs +0 -283
  363. package/src/main/lib/__tests__/epicSpawnPlan.test.cjs +0 -196
  364. package/src/main/lib/__tests__/epicTranscriptPath.test.cjs +0 -163
  365. package/src/main/lib/__tests__/epicWorktreeBoot.test.cjs +0 -136
  366. package/src/main/lib/__tests__/epicWorktreeMerge.test.cjs +0 -130
  367. package/src/main/lib/__tests__/epicWorktreeMint.test.cjs +0 -117
  368. package/src/main/lib/__tests__/epicWorktreeProjectConfig.test.cjs +0 -137
  369. package/src/main/lib/__tests__/fixChainDepth.test.cjs +0 -40
  370. package/src/main/lib/__tests__/fixtures/204-mercury-steam-horse.log.txt +0 -13
  371. package/src/main/lib/__tests__/fixtures/scheduler-machine.json.corrupt-1789147548 +0 -34
  372. package/src/main/lib/__tests__/gateFixtures.json +0 -20
  373. package/src/main/lib/__tests__/gitCacheBound.test.cjs +0 -69
  374. package/src/main/lib/__tests__/gitWorktree.test.cjs +0 -1478
  375. package/src/main/lib/__tests__/gitWorktreeSalvage.test.cjs +0 -107
  376. package/src/main/lib/__tests__/gitWorktreeSalvageDelta.test.cjs +0 -153
  377. package/src/main/lib/__tests__/guardShims.test.cjs +0 -158
  378. package/src/main/lib/__tests__/importReferences.spec.cjs +0 -56
  379. package/src/main/lib/__tests__/instanceLock.test.cjs +0 -173
  380. package/src/main/lib/__tests__/jobSupervisorRecord.test.cjs +0 -78
  381. package/src/main/lib/__tests__/jobWorktree.test.cjs +0 -199
  382. package/src/main/lib/__tests__/jobWorktreeBootLive.test.cjs +0 -82
  383. package/src/main/lib/__tests__/landedSinceRun.test.cjs +0 -133
  384. package/src/main/lib/__tests__/launchFailure.test.cjs +0 -220
  385. package/src/main/lib/__tests__/loadGate.test.cjs +0 -303
  386. package/src/main/lib/__tests__/localAdminHttp.test.cjs +0 -214
  387. package/src/main/lib/__tests__/loopDelay.test.cjs +0 -68
  388. package/src/main/lib/__tests__/mcpToolCatalog.test.cjs +0 -107
  389. package/src/main/lib/__tests__/modelCatalog.test.cjs +0 -202
  390. package/src/main/lib/__tests__/opsOwnership.test.cjs +0 -113
  391. package/src/main/lib/__tests__/opsRootAbsoluteCwd.test.cjs +0 -328
  392. package/src/main/lib/__tests__/opsRootNestedWrite.test.cjs +0 -51
  393. package/src/main/lib/__tests__/opsRootResolve.test.cjs +0 -149
  394. package/src/main/lib/__tests__/prdDeclaredPaths.test.cjs +0 -82
  395. package/src/main/lib/__tests__/prdDisposition.test.cjs +0 -224
  396. package/src/main/lib/__tests__/procIdentity.test.cjs +0 -119
  397. package/src/main/lib/__tests__/procName.test.cjs +0 -92
  398. package/src/main/lib/__tests__/projectBriefCore.test.cjs +0 -216
  399. package/src/main/lib/__tests__/projectRootResolve.test.cjs +0 -148
  400. package/src/main/lib/__tests__/queueHealth.test.cjs +0 -58
  401. package/src/main/lib/__tests__/queueStoreAtomicWrite.test.cjs +0 -88
  402. package/src/main/lib/__tests__/queueStoreMachineStateRecovery.test.cjs +0 -190
  403. package/src/main/lib/__tests__/quietMachineLease.test.cjs +0 -39
  404. package/src/main/lib/__tests__/rateLimitWindow.test.cjs +0 -88
  405. package/src/main/lib/__tests__/reaperHelpers.test.cjs +0 -577
  406. package/src/main/lib/__tests__/schedulerBatchDepends.test.cjs +0 -312
  407. package/src/main/lib/__tests__/schedulerBatchFairness.test.cjs +0 -213
  408. package/src/main/lib/__tests__/schedulerBatchLaunchHold.test.cjs +0 -125
  409. package/src/main/lib/__tests__/schedulerBatchProjectCap.test.cjs +0 -127
  410. package/src/main/lib/__tests__/schedulerBatchQuietMachine.test.cjs +0 -109
  411. package/src/main/lib/__tests__/schedulerMcpServerHeadlessRefusal.test.cjs +0 -71
  412. package/src/main/lib/__tests__/schedulerMcpServerHelp.test.cjs +0 -216
  413. package/src/main/lib/__tests__/schedulerMcpServerProjectHome.test.cjs +0 -183
  414. package/src/main/lib/__tests__/schedulerPaths.test.cjs +0 -226
  415. package/src/main/lib/__tests__/schedulerPathsWorktree.test.cjs +0 -133
  416. package/src/main/lib/__tests__/schedulerRuntimeState.test.cjs +0 -56
  417. package/src/main/lib/__tests__/sessionSlots.test.cjs +0 -144
  418. package/src/main/lib/__tests__/telemetryBacklog.test.cjs +0 -626
  419. package/src/main/lib/__tests__/telemetryBoot.test.cjs +0 -134
  420. package/src/main/lib/__tests__/telemetryConsent.test.cjs +0 -136
  421. package/src/main/lib/__tests__/telemetryCounters.test.cjs +0 -57
  422. package/src/main/lib/__tests__/telemetryCountersMetadataColumn.test.cjs +0 -98
  423. package/src/main/lib/__tests__/terminalRunOutcome.test.cjs +0 -200
  424. package/src/main/lib/__tests__/toolUseClassify.test.cjs +0 -53
  425. package/src/main/lib/__tests__/updateCheck.test.cjs +0 -63
  426. package/src/main/lib/__tests__/upgradeDrain.test.cjs +0 -130
  427. package/src/main/lib/__tests__/usageCircuit.test.cjs +0 -354
  428. package/src/main/lib/__tests__/watchdog-helpers.test.cjs +0 -375
  429. package/src/main/lib/__tests__/watchdog-relaunch.test.cjs +0 -266
  430. package/src/main/lib/kgExchangePairing.cjs +0 -75
@@ -1,515 +1,235 @@
1
- <!-- PRD_AUTHORING.md v3 -->
1
+ <!-- PRD_AUTHORING.md v4 -->
2
2
  # PRD Authoring Guide — Scheduler Safety Rules
3
3
 
4
- This guide codifies lessons from two stuck-job incidents (fizzpop poll-hang, etch-engine post-AC overrun) into enforceable rules every PRD MUST follow. Violating these rules costs real money and wastes hours waiting at a terminal.
4
+ Rule: every PRD you queue follows the rules below.
5
+ Why: two real stuck jobs (fizzpop poll-hang, etch-engine post-AC overrun) cost hours of wall-clock time and real money.
5
6
 
6
- Before queueing a new PRD, run through the §10 checklist at the bottom.
7
+ Before queueing, run the §15 checklist at the bottom. It is last on purpose — check it last.
7
8
 
8
9
  ---
9
10
 
10
11
  ## §1 Bounded waits, never unbounded polls
11
12
 
12
- **Summary:** Every `until`/`while` loop that makes network calls or waits for external state MUST have a hard iteration cap that surfaces non-zero on exhaustion.
13
+ Rule: every `until`/`while` loop that polls a network call or external state needs a hard iteration cap. On exhaustion, print a diagnostic line and move on — never spin forever.
14
+ Why: `106-fizzpop-publish` polled `curl .../health | jq .uptime` for a value that could never drop (a static-content deploy doesn't restart the API). It hung 2h47m until the 4h watchdog killed it.
13
15
 
14
- **Anti-example** (verbatim from `106-fizzpop-publish.md`):
16
+ Pattern:
15
17
  ```bash
16
- # WRONG — what 106-fizzpop-publish did
17
- PREV=288694
18
- until [ "$(curl -s https://bilko.run/api/health | jq .uptime)" -lt "$PREV" ]; do
19
- sleep 15
20
- done
21
- # Outcome: 2h47m hang because static-content Render deploys never restart the Node API
22
- # so `uptime` never dropped. Loop hung until the 4h watchdog SIGKILLed.
23
- ```
24
-
25
- **Recommended pattern:**
26
- ```bash
27
- # RIGHT — bounded, surfaces failure cleanly
28
18
  for i in $(seq 1 20); do
29
- if curl -sf https://bilko.run/projects/fizzpop/ > /dev/null; then
30
- echo "deploy live (attempt $i)"; break
19
+ if curl -sf https://example.com/health > /dev/null; then
20
+ echo "live (attempt $i)"; break
31
21
  fi
32
- echo "waiting for deploy ($i/20)..."
33
- sleep 15
22
+ echo "waiting ($i/20)..."; sleep 15
34
23
  done
35
- # Smoke test downstream will diagnose the real failure if the deploy didn't land.
24
+ # Continue regardless — the smoke test (§3) catches a real failure.
36
25
  ```
37
26
 
38
- **Rule:** Every `until`/`while` network-or-state poll MUST use `for i in $(seq 1 N); do ...; done` with a hard cap. Recommended cap: 20 × 15 s = 5 min for HTTP polls. On exhaustion, print a diagnostic line and continue (let the smoke test below catch the real failure).
27
+ Recommended cap: 20 × 15s = 5 min for HTTP polls. Never use an uptime/restart signal to detect a static-content deploy — check the actual URL that should be live.
39
28
 
40
29
  ---
41
30
 
42
- ## §2 Don't add work past the acceptance checklist
31
+ ## §2 Stop at the acceptance checklist
43
32
 
44
- **Summary:** Once every AC line is ticked, write the result and exit. Do not add polish, fixtures, generators, or "while we're here" improvements not enumerated in the PRD.
45
-
46
- **Anti-example** (verbatim from `112-etch-engine.md`):
47
- ```bash
48
- # WRONG — what 112-etch-engine did after declaring success
49
- for (let seed = 100; seed < 50000 && !found; seed++) {
50
- const result = generateRandom({ size, rng, maxAttempts: 3 });
51
- if (result.ok && result.solution) { found = true; ... }
52
- }
53
- # Outcome: 2h44m of token burn looking for fixtures the AC did not require.
54
- # The agent had emitted result-success at 17:44 UTC; the bonus loop ran until 20:28.
55
- ```
33
+ Rule: once every acceptance-criteria line is checked, follow the finish protocol (§11) and stop. Do not add polish, fixtures, or "while we're here" work that isn't an AC line.
34
+ Why: `112-etch-engine` declared success at 17:44 UTC, then ran an unbounded fixture search until a user killed it at 20:28 — 2h44m of token burn on work no AC line asked for.
56
35
 
57
- **Rule:** Once every AC line is checked, write the result and exit 0. If bonus work seems genuinely valuable, write a follow-up PRD and reference it in the result. Do not add any work not explicitly enumerated in an AC line.
36
+ Executor: if more work seems valuable, name it in your report as a follow-up. Do not do it, and do not queue it. Why: only the planner queues work.
58
37
 
59
38
  ---
60
39
 
61
- ## §3 Smoke tests verify; spin-waits don't
62
-
63
- **Summary:** Prefer "do thing, run a test that asserts thing happened" over "do thing, spin until I detect thing happened."
40
+ ## §3 Verify with a real test, not a spin-wait
64
41
 
65
- **Rule:** After a deployment, migration, or build step, run an actual test command (`curl -sf`, `npm test`, `pnpm typecheck`) that exits non-zero on failure. A spin-wait that polls for a condition hides the failure mode; a test command surfaces the exact error.
42
+ Rule: after a deploy, migration, or build step, run a command that exits non-zero on failure (`curl -sf`, `npm test`, `tsc --noEmit`).
43
+ Why: a poll that waits for a condition hides the real error; a test command shows it.
66
44
 
67
- **Pattern:**
68
45
  ```bash
69
- # Deploy step above (bounded, §1)
70
- # Smoke test — will exit 1 with a clear message if deploy failed:
71
- curl -sf https://bilko.run/projects/fizzpop/ > /dev/null || { echo "smoke test FAILED: fizzpop not reachable"; exit 1; }
46
+ curl -sf https://example.com/projects/foo/ > /dev/null \
47
+ || { echo "smoke test FAILED: foo not reachable"; exit 1; }
72
48
  ```
73
49
 
74
50
  ---
75
51
 
76
- ## §4 Bound any generator/search loop with max-attempts and surface-on-exhaustion
77
-
78
- **Summary:** When iterating a search space (fixtures, seeds, brute-force), declare the maximum search size in the PRD AC and surface and HALT on exhaustion.
79
-
80
- **Anti-example:** Same etch-engine fixture generator from §2 — `for (let seed = 100; seed < 50000 ...)` with no AC line constraining it.
52
+ ## §4 Bound search and generator loops
81
53
 
82
- **Rule:** When iterating a search space, the PRD AC MUST state the bound explicitly ("up to 1000 seeds; if exhausted, surface and HALT"). The executor knows when to give up and moves on rather than burning tokens indefinitely.
54
+ Rule: when you iterate a search space (fixtures, seeds, brute force), the acceptance criteria must state the max attempts. On exhaustion, print a HALT line and exit 1 — never loop past the stated bound.
55
+ Why: the same etch-engine loop (§2) had no bound in its AC, so nothing told the executor when to stop.
83
56
 
84
- **Pattern:**
85
57
  ```ts
86
- let found = false;
87
- for (let seed = 0; seed < MAX_SEEDS && !found; seed++) {
88
- // ...
89
- }
58
+ for (let seed = 0; seed < MAX_SEEDS && !found; seed++) { /* ... */ }
90
59
  if (!found) {
91
- console.error(`HALT: exhausted ${MAX_SEEDS} seeds without finding a valid fixture`);
60
+ console.error(`HALT: exhausted ${MAX_SEEDS} seeds without a valid fixture`);
92
61
  process.exit(1);
93
62
  }
94
63
  ```
95
64
 
96
65
  ---
97
66
 
98
- ## §5 Render/deploy waits are bounded and always followed by a smoke test
99
-
100
- **Summary:** Render (and similar) deploys may take 2–10 minutes and may silently fail. Always bound the wait and follow it with a live endpoint test.
67
+ ## §5 Frontmatter
101
68
 
102
- **Pattern:**
103
- ```bash
104
- DEPLOY_OK=0
105
- for i in $(seq 1 20); do
106
- if curl -sf https://bilko.run/projects/<slug>/ > /dev/null; then
107
- DEPLOY_OK=1; echo "deploy live (attempt $i)"; break
108
- fi
109
- echo "waiting for deploy ($i/20)..."
110
- sleep 15
111
- done
112
- # Continue regardless. Smoke test below catches the real failure.
113
- if [ $DEPLOY_OK -eq 0 ]; then
114
- echo "WARNING: deploy not detected after 20 attempts; continuing to smoke test"
115
- fi
116
- curl -sf https://bilko.run/projects/<slug>/ > /dev/null || { echo "SMOKE TEST FAILED"; exit 1; }
117
- ```
69
+ Rule: every PRD needs these fields.
70
+ 1. `title` — one line, plain English.
71
+ 2. `cwd` — the Epic's project root, as an absolute path or `~/…`. The API always sets it to the Epic's project, whatever you pass. To work in another project, open an Epic in that project. The folder must exist when you queue.
72
+ 3. `estimateMinutes` — a realistic integer. Most PRDs take 5–10 minutes (§8) — don't inflate it.
118
73
 
119
- **Rule:** A static-content deploy on Render does NOT restart the backend API. Never use `uptime` or process-restart signals to detect a static-content deploy — use the actual URL that should be live.
74
+ `parallelGroup` is deprecated and ignored. `dependsOn: [<slug>, ...]` is the only ordering primitive.
120
75
 
121
- ---
76
+ `planId` is written by the API, never by you. It groups PRDs into the plan (wave) they belong to: an `append` PRD, or one with an explicit `dependsOn`, inherits the `planId` of the PRD it attaches behind; a fresh PRD mints a new one.
122
77
 
123
- ## §6 Frontmatter rules
78
+ Artifact-only PRDs (`deliverable: artifact` + `artifactPaths: [...]`) — use this ONLY when every deliverable is a file the repo deliberately git-excludes, so "no commit" is the correct outcome, not a miss.
79
+ 1. Every artifact path is listed in `artifactPaths` and is relative, never `..`.
80
+ 2. Each file must be non-empty and written during the run window — the verifier stat-checks it on disk.
81
+ 3. The tree must still end clean. A declared artifact excuses the missing commit, not a stray tracked edit.
82
+ 4. Both fields are required together — passing one without the other is refused.
124
83
 
125
- **Summary:** Required keys are `title`, `cwd` (absolute path), `estimateMinutes`. Default to letting the filename `NN-` prefix drive grouping.
84
+ Why: PRD `816-prepare-157-copy-citation-extras-patch` wrote its patch into a git-excluded folder with no way to declare that, and parked `needs_review` twice.
126
85
 
127
- **Required frontmatter:**
128
- ```yaml
129
- ---
130
- title: <one line, plain English>
131
- cwd: ~/Projects/<target-repo>
132
- estimateMinutes: 10
133
86
  ---
134
- ```
135
87
 
136
- **Cross-machine portability:** Write `cwd` as `~/Projects/<name>` — the parser expands `~` to `os.homedir()` at ingest time, so the same PRD file works on Linux (`/home/<u>/...`) and macOS (`/Users/<u>/...`). Absolute paths (e.g. `/home/bilko/Projects/foo`) are passed through unchanged and will break on any machine with a different home directory.
88
+ ## §6 Gate and files
89
+
90
+ Both fields are required in every `scheduler_create_prd` call.
137
91
 
138
- **Rules:**
139
- - `cwd` MUST point to the target project. Prefer `~/...` for portability; only use an absolute path if you have a specific reason to pin to one machine.
140
- - **`cwd` MUST already exist on disk at queue time.** The scheduler runs a dead-cwd guard (`fs.accessSync(cwd, fs.constants.X_OK)` in `src/main/scheduler.cjs:669-680`) *before* spawning the child, so a PRD whose `cwd` references a not-yet-created directory will exit with `-1: cwd no longer exists` and the body will never run — even if the first step of the body would have created the directory. If the PRD's purpose is to create a brand-new sibling project at `~/Projects/<new-slug>/`, point `cwd` at the parent (`~/Projects`) and make the first executable step `mkdir -p ~/Projects/<new-slug> && cd ~/Projects/<new-slug>`.
141
- - `estimateMinutes` is used for ETA display; include a realistic estimate (note: empirical median is ~10 min, p90 ~20 min — avoid wildly inflated estimates that hide real outliers).
142
- - `parallelGroup` is DEPRECATED and ignored (PRD 832) — `dependsOn: [<slug>, …]` is the only ordering primitive; numbers are unique per project.
92
+ ### gate
143
93
 
144
- ### `planId` (API-owned — never pass it)
94
+ 1. 1–10 commands. The scheduler re-runs them, in order, after the executor finishes. Each must exit 0.
95
+ 2. Start each `&&` step with `timeout <seconds>`, for example `timeout 300 npm test && timeout 120 npm run lint`. Why: a command without a timeout can hang the run.
96
+ 3. Join steps inside one entry with `&&`.
97
+ 4. The scheduler runs gate commands without a shell, so shell syntax is refused. Outside single quotes, do not use `|` `<` `>` `;` `&` (only `&&` between steps), backticks, `$`, `\`, `*`, `?`, `[`, `]`, `(`, `)`, `{`, `}` or `!`. Inside double quotes, `$`, backticks and `\` are refused too.
98
+ 5. Do not start a word with `#` or `~`, and do not put `~` right after `=` or `:`. `HEAD~1` is fine.
99
+ 6. Put text with these characters inside single quotes, for example `rg -n 'a|b' src/`. A check that needs a shell belongs in a test file that the gate runs.
100
+ 7. Keep each entry on one line, at most 500 chars, with plain spaces between words.
101
+ 8. Use `["none"]` only for docs or config with no runnable check. Write exactly `none`. Never mix `none` with commands.
102
+ 9. Put `NAME=value` words before `timeout`, never after it, for example `CI=1 timeout 300 npm test`. A leading `TMPDIR=$(mktemp -d) ` is allowed but not needed: the scheduler gives each gate its own TMPDIR.
145
103
 
146
- `planId` is a frontmatter key that `createPrd` stamps on every PRD: the durable identity of the plan (wave) the PRD belongs to. An `append` PRD (or one with an explicit `dependsOn`) inherits the planId of the PRD(s) it attaches behind; a first-ever or `new-head` PRD mints a fresh one. The Scheduler tracker groups by it, so a second plan in one Epic is a recorded fact, not a re-derivation. Callers never pass or hand-write it — `scheduler_create_prd` ignores it. PRDs without one (pre-stamp) fall back to dependency-graph grouping.
104
+ ### files
147
105
 
148
- ### Artifact-only PRDs
106
+ 1. 1–50 repo-relative paths. A folder ends with `/`.
107
+ 2. No absolute paths, no `~` at the start, no `..` or `.` segments, no `*` or `?`, no `\` or backticks, no leading `-`. Use `/` between folders.
108
+ 3. PRDs that can run at the same time must not share a file. If two PRDs touch the same file, chain them with `dependsOn`.
149
109
 
150
- Use `deliverable: artifact` + `artifactPaths: [a, b]` (both passed to `scheduler_create_prd`) ONLY when every deliverable is a file the target repo deliberately git-excludes (e.g. patches/notes under `session-manager-operations/review-records/`, matched by `.git/info/exclude`), so "no commit" is the correct outcome.
110
+ ### What the API writes
151
111
 
152
- - Every artifact path must be named in `artifactPaths`; an unlisted file is invisible to the verifier. Each path is relative, never contains `..`.
153
- - The artifact must be non-empty and written during the run window — the verifier stat-checks it on disk (verdict `pass_no_commit_artifact_verified`; window = start−60s..finish+120s).
154
- - The run must still leave the working tree clean — declaring artifacts excuses the missing commit, not stray tracked edits. The commit guard enforces this: the `pass_no_commit_artifact_verified` verdict stands the guard down only when nothing tracked was left dirty; any uncommitted tracked change still parks as `uncommitted_changes`.
155
- - Both fields are required together: the write is refused if only one is given.
112
+ The API writes the frontmatter and renders two body sections from the fields above: `# Files` (right after `# Acceptance criteria`) and `# Gate` (after `# Out of scope`, before `## Engineering standards`). Do not write those two sections yourself. Do not put a gate fence (three backticks + `gate`), a `# Gate` heading or a `# Files` heading in any text field — the API rejects that. For `["none"]`, the Gate section says the PRD has no runnable check and to verify each AC line by reading the files, with the fence holding the single word `none`.
156
113
 
157
- Incident: sigma PRD `816-prepare-157-copy-citation-extras-patch` (2026-09-20) wrote its patch into a git-excluded folder; with no way to declare that, two runs both parked in `needs_review`.
114
+ ### Acceptance criteria
115
+
116
+ Plain, checkable statements. Commands go in `gate`, not in the criteria.
158
117
 
159
118
  ---
160
119
 
161
120
  ## §7 Self-containment
162
121
 
163
- **Summary:** The PRD body is the executor's entire context. It runs as `claude -p "<body>"` with no conversation history.
122
+ Rule: the PRD body is the executor's entire context — it runs as `claude -p "<body>"` with no conversation history. Include exact file paths, signatures, library versions, and any sibling PRD not to duplicate. Never write "the conversation" or "the design doc" — if the executor would need to search for an answer, put the answer in the PRD.
164
123
 
165
- **Rule:** Include exact file paths, function signatures if they save a Read, library versions, and the name of any sibling PRD the executor must NOT duplicate. Do not reference "the conversation", "what we discussed", "the design doc", or any other external context. If the executor would need to search for something, include the answer.
124
+ A short Epic-context digest is prepended automatically for orientation only — never load-bearing; write the body as if it won't be there.
166
125
 
167
- **Epic context digest is additive, not a dependency (PRD 958):** when a job's `epicId` resolves to a known Epic in that project's `active-index.json`, the scheduler prepends a short digest of the Epic's own session (goal text + recent turns, built by `src/main/lib/epicContextDigest.cjs`'s `buildContextDigest`) to the `-p` prompt sent to the executor — the on-disk PRD `.md` file itself is never rewritten. This exists purely to orient the executor faster; it is never load-bearing. The digest is a silent no-op when the Epic doesn't resolve, and any failure building it is caught and logged, never blocking dispatch. Every PRD body must still stand on its own per the rule above — write it as if the digest will not be there.
126
+ The executor can't ask you anything: no `ScheduleWakeup`, Cron, Monitor, `AskUserQuestion`, plan mode, or worktree tools; background tasks are off (a `timeout` command is always on PATH — the app installs a shim on macOS). It must never stop to ask a question — it makes the safest reasonable call and reports it, so write the PRD so that call is obvious.
168
127
 
169
128
  ---
170
129
 
171
- ## §8 Scope sizing — target ≤10 min, ceiling 15 (data-driven, 2026-09)
130
+ ## §8 Scope sizing — target ≤10 min, ceiling 15
172
131
 
173
- **Summary:** One PRD ≈ **≤10 wall-clock minutes** of work. Empirically (2026-09 calibration) wall p50 = **7.8 min**, 60% of runs ≤ 10 min; authored estimates ran 4× too high. **If you project >15 min, SPLIT.**
132
+ Rule: one PRD is ≤10 wall-clock minutes of work. If you project more than 15, split it into sequential PRDs and link them with `dependsOn`.
133
+ Why: 2026-09 data put wall p50 at 7.8 min, with 60% of runs ≤10 min — authored estimates ran 4× too high.
174
134
 
175
- **Rule:** Split larger work into sequential PRDs; reference the dependency in `# Implementation notes`. e2e/publish work is the failure tail: **shard test suites to one spec per PRD; never run a full suite or an endpoint-polling publish in a single PRD** (§1/§5).
135
+ e2e and publish work is the long tail: shard test suites to one spec per PRD. Never run a full suite, or an endpoint-polling publish, in a single PRD (see §1/§3).
176
136
 
177
137
  ---
178
138
 
179
139
  ## §9 Failure surfacing
180
140
 
181
- **Summary:** Prefer `exit 1` with a one-line diagnosis over silent retries. Note: a `rateLimited` exit-1 is the scheduler's benign auto-pause (it auto-resumes at the next 5h reset), NOT an authoring failure — don't engineer retry logic for it.
182
-
183
- **Rule:** When a step fails, print a single diagnostic line and exit 1. The scheduler marks the job `failed` and the investigator Claude reads the log. A clean failure message is worth more than a 15-minute silent retry loop. Do not swallow errors with `|| true` unless the failure is genuinely non-fatal and you explain why.
141
+ Rule: when a step fails, print one diagnostic line and exit 1. Don't swallow errors with `|| true` unless the failure is genuinely non-fatal — say why inline when you do.
142
+ Why: a clean failure is worth more than a 15-minute silent retry loop; the scheduler marks the job `failed` and the investigator reads the log.
184
143
 
185
144
  ```bash
186
- # RIGHT
187
145
  npm test || { echo "HALT: npm test failed — see above"; exit 1; }
188
-
189
- # WRONG
190
- npm test || true # silently continues even if tests are broken
191
146
  ```
192
147
 
148
+ Note: a `rateLimited` exit-1 is the scheduler's own benign auto-pause (it resumes at the next 5h reset) — don't engineer retry logic for it.
149
+
193
150
  ---
194
151
 
195
- ## §10 Pre-queue checklist (the litany)
152
+ ## §10 Negative-assertion checks must exit 0 on the clean case
196
153
 
197
- Before queueing a new PRD, verify each of these:
154
+ Rule: a check that asserts something is ABSENT must exit 0 when it's absent. Write it as `if <detector>; then echo HALT...; exit 1; fi` — never leave the no-match path carrying the non-zero exit.
155
+ Why: `grep` exits 1 when it finds nothing. PRD `62-x-trader-doctrine` cleaned up correctly, but its sanity `grep` for a banned phrase found nothing, exited 1, and a perfect run was flagged `needs_review` by the verifier's `transcript_errors` check.
198
156
 
199
- - [ ] **§1 Bounded waits:** Every `until`/`while` poll has a `for i in $(seq 1 N)` cap ≤ 20 iterations.
200
- - [ ] **Every command bounded:** Every test/build/dev-server/deploy command is wrapped in `timeout` (typecheck/unit 300s, e2e 120s, `curl --max-time 15`). No bare `playwright test` / `vite` / `pnpm dev` / `curl … | head`.
201
- - [ ] **§2 No bonus work:** AC list is the only source of work. No "while we're here" additions.
202
- - [ ] **§3 Smoke tests + verify-before-done:** Every deploy/migration step is followed by a test command that exits 1 on failure. Run the AC test command once before declaring done; never end the run on a red test.
203
- - [ ] **§4 Bounded generators:** Any search/seed loop has an explicit `MAX_ATTEMPTS` constant and surfaces failure on exhaustion.
204
- - [ ] **§5 Render deploys:** Deploy waits use a live URL check, not uptime/restart signals.
205
- - [ ] **§6 Frontmatter:** `title`, `cwd` (`~/Projects/<name>` preferred; path MUST exist on this machine), `estimateMinutes` present. `parallelGroup` is deprecated — use `dependsOn` for ordering.
206
- - [ ] **§7 Self-contained:** No references to "the conversation" or external context. Paths and identifiers are inline. Body is clean UTF-8 — **no NUL/control bytes** (paste-from-PDF crashes the spawn). Quick check: `grep -qP '\x00' file && echo BAD`.
207
- - [ ] **§8 Scope:** Targets ≤10 min, ceiling 15. If projected larger, split. e2e/publish sharded to one spec per PRD.
208
- - [ ] **§9 Failure surfacing:** Errors exit 1 with a diagnostic line. No silent `|| true` swallows. (`rateLimited` exit-1 is benign auto-pause, not a failure.)
209
- - [ ] **§11 Negative-assertion checks:** Any "this should produce NO output / NO match" check (a `grep` that should find nothing, a "no leftover X" guard) is written as an inverted conditional that exits 0 on the clean case. A bare `grep` whose success is "no match" exits 1 and trips the verifier `transcript_errors` downgrade even when the run is perfect.
210
- - [ ] **§12 End green:** The acceptance/test gate is the LAST thing the run does; any intentionally-failing step (TDD red test, expected-nonzero probe) runs EARLY, never after the gate, and is captured (`2>&1 | tail` inside a conditional) so it doesn't surface as a bare `is_error`/`Traceback` in the final portion of the transcript.
211
- - [ ] **§13 Recover/annotate errors & expected timeouts:** A throwaway probe that errors is re-run corrected (or annotated `# expected/handled`) right after — never left stranded; prefer a temp `.py` over a fragile inline `python -c`. An *expected* `timeout` cap (a long ingest/scan) handles exit 124 explicitly as success-with-note, not a bare `Exit code 124`. Both prevent the `transcript_errors` downgrade of a green deliverable.
157
+ ```bash
158
+ if grep -rniE "banned phrase" path/; then
159
+ echo "HALT: banned phrase still present"; exit 1
160
+ fi
161
+ echo "clean"
162
+ ```
212
163
 
213
- ---
164
+ Applies to `grep`, `rg`, `diff` (exits 1 on any difference), and any custom detector.
214
165
 
215
- ## §11 Negative-assertion checks must exit 0 on the clean case
166
+ ---
216
167
 
217
- **Summary:** A check that asserts the *absence* of something must return exit 0 when the
218
- thing is absent. The classic trap is `grep`: it exits **1 when it finds no match**. If your
219
- AC says "verify no banned phrase remains" and you write a bare `grep`, the *success* path
220
- (nothing found) surfaces as `is_error=true` in the transcript — and the verifier's
221
- `transcript_errors` heuristic downgrades the whole run to `needs_review` even though it did
222
- everything right.
168
+ ## §11 End green, and trust the verdict line
223
169
 
224
- **This actually happened** (PRD `62-x-trader-doctrine`, 2026-06-13): the doctrine was cleaned
225
- correctly and committed, but the AC's sanity grep —
226
- `grep -rniE "building in public|..." data/pipelines/x_session/` — found nothing, exited 1,
227
- and a perfect run was flagged for review. Self-inflicted, by the PRD author.
170
+ Rule: order the run so the acceptance/test gate is the LAST command. Run any intentionally-failing step (a TDD red test, an expected-nonzero probe) EARLY, and capture its output (`2>&1 | tail` inside a conditional) so a bare `Traceback`/`is_error` never lands in the final part of the transcript.
171
+ Why: the post-run verifier scans the transcript and downgrades to `needs_review` on error markers — it can't tell an intentional failure from a real one.
228
172
 
229
173
  ```bash
230
- # WRONG — exits 1 (is_error) exactly when the check PASSES
231
- grep -rniE "building in public|indie hacker" data/pipelines/x_session/
232
-
233
- # RIGHT — inverted: "found banned phrase" is the failure, "clean" exits 0
234
- if grep -rniE "building in public|indie hacker" data/pipelines/x_session/; then
235
- echo "HALT: banned builder framing still present (see matches above)"; exit 1
236
- fi
237
- echo "clean: no banned framing"
238
-
239
- # ALSO RIGHT — grep -q with negation, when you don't need to see the matches
240
- grep -rqniE "building in public|indie hacker" data/pipelines/x_session/ \
241
- && { echo "HALT: banned framing present"; exit 1; } || echo "clean"
174
+ # RIGHT — red demo first and captured, green gate last
175
+ timeout 120 python -m pytest tests/test_repro.py::test_bug 2>&1 | tail -3 || true # expected red
176
+ # ... implement the fix ...
177
+ timeout 300 pytest -q # LAST thing the run does
242
178
  ```
243
179
 
244
- **Rule:** Whenever an AC line is phrased as "verify there are no…", "confirm X does not
245
- appear", "no leftover…", write it as `if <detector>; then echo HALT…; exit 1; fi`. Never let
246
- the no-match/empty-output path be the one that carries a non-zero exit. Applies to `grep`,
247
- `rg`, `find ... | grep`, `diff` (exits 1 on differences), and any custom detector.
180
+ The scheduler appends a finish protocol after your PRD's own steps — you never write it: review, then the `# Gate` commands (§6), then commit only the exact paths you created or changed for this PRD, then the verdict line — `SCHEDULER_VERDICT: PASS` once the gate is green and the commit landed, else `SCHEDULER_VERDICT: FAIL <one-line reason>`. The verifier trusts a truthful `PASS` plus a landed commit over stray transcript markers. Never print `PASS` on a red gate.
248
181
 
249
182
  ---
250
183
 
251
- ## §12 End green, and trust the verdict sentinel
184
+ ## §12 Don't strand mid-run probe errors; annotate expected timeouts
252
185
 
253
- **Summary:** The post-run verifier (`runVerify.cjs`) scans the transcript and downgrades to
254
- `needs_review` on error markers (`Traceback`+`Error`, `FAIL`/`FATAL`, a tool `is_error` in the
255
- final portion of the run). It cannot tell an *intentional* failure from a real one. Two rules
256
- keep legitimate runs from false-tripping it.
257
-
258
- **12a — Run the green gate LAST.** Order the run so the final command is the acceptance/test
259
- gate. Do any intentionally-failing step EARLY:
186
+ Rule: a throwaway probe that errors (a bad quote, a wrong kwarg) must be re-run corrected right after, or annotated `# expected/handled: <why>` on the next line — never left stranded. Prefer a temp `.py` file over a fragile inline `python -c` one-liner.
260
187
 
188
+ Rule: an *expected* `timeout` cap (a long ingest/scan you expect to hit it) is success-with-note, not a bare `Exit code 124` — branch on it explicitly:
261
189
  ```bash
262
- # WRONG — red test reproduced AFTER the work; its Traceback lands late in the transcript
263
- pytest -q # all green
264
- python -m pytest tests/test_repro.py::test_bug # ← TDD red demo, errors, trips verifier
265
-
266
- # RIGHT — red demo first (and captured), green gate last
267
- python -m pytest tests/test_repro.py::test_bug 2>&1 | tail -3 || true # expected red, captured
268
- # ... implement the fix ...
269
- timeout 300 pytest -q # ← LAST thing the run does; ends green
190
+ timeout 120 python -m project.ingest --all || { rc=$?
191
+ [ $rc -eq 124 ] && echo "hit time cap — partial, rows persist; OK" \
192
+ || { echo "HALT: ingest failed rc=$rc"; exit 1; }; }
270
193
  ```
194
+ Why: both a stranded probe traceback and a bare `Exit code 124` read as failure to the verifier even when the committed work is correct and green (PRDs 77, 80 — 2026-06-13).
271
195
 
272
- If you must show a failure late, capture it (`… 2>&1 | tail` inside a conditional, or assert
273
- on the captured text) so a raw `Traceback`/`is_error` never hits the transcript bare.
196
+ ---
197
+
198
+ ## §13 Queueing PRDs from external automation
274
199
 
275
- **12b — The `SCHEDULER_VERDICT` sentinel is authoritative; emit it truthfully.** The scheduler's
276
- FINISH PROTOCOL ends by printing `SCHEDULER_VERDICT: PASS` once the AC gate is green AND the
277
- commit landed (else `SCHEDULER_VERDICT: FAIL <reason>` + `exit 1`). The verifier treats
278
- `PASS` + a commit landed during the run as the **authoritative** signal and overrides incidental
279
- transcript markers — this is what lets a deliberately-reproduced red test (PRD 77) or a grep
280
- result containing "Error" (PRD 68) finish `completed` instead of `needs_review`. **Never print
281
- `PASS` on a red gate.** The sentinel is only a safety net while it tells the truth; a lying
282
- `PASS` converts the verifier from "catches false failures" into "ships silent failures."
200
+ Decision: is the session-manager app running on this machine right now?
201
+ - Yes → call the `scheduler_create_prd` MCP tool, with the usual fields (`title`, `cwd`, `estimateMinutes`, `sourcePromptId`, `goal`, `acceptanceCriteria`, `implementationNotes`) plus `gate` and `files` (§6).
202
+ - No → stop. Ask the human to start the app, then call the tool again. Never write the PRD file by hand: the scheduler quarantines a file the API did not write, and it never runs.
283
203
 
284
- **This actually happened** (PRDs 68 + 77, 2026-06-13): both committed correct work with green
285
- suites, but 77's `systematic-debugging` red-test repro and 68's grep-"Error" substring each
286
- tripped `transcript_errors → needs_review`, and the self-heal pass kept re-deriving the same
287
- verdict from the immutable log — stuck indefinitely. §12 (end-green + authoritative sentinel)
288
- is the structural fix.
204
+ It only works while the app is running — closing it removes the admin port/token, and the call fails with `session-manager app is not running (admin API unreachable)`.
289
205
 
290
206
  ---
291
207
 
292
- ## §13 Don't strand mid-run probe errors; annotate expected timeouts
208
+ ## §14 A parked job may resolve itself now
293
209
 
294
- **Summary:** §12 keeps the *final* portion of the transcript green. §13 covers the *middle* —
295
- two executor habits that strand a bare `Traceback`/`Error`/`Exit code` the verifier then flags,
296
- even when the deliverable is correct and committed.
210
+ Rule: if a job parks `needs_review` with verdict `transcript_errors`, `no_verdict_sentinel`, or `abandoned_background_task`, do nothing first. The scheduler re-runs the gate on its own and completes the job once the gate is green, the commit is on HEAD, and the tracked tree is clean.
211
+ Why: those three verdicts mean the transcript looked noisy, not that the work was wrong.
297
212
 
298
- **13a — A throwaway probe that errors must recover or be annotated in place.** Exploratory
299
- `python -c`/`bash` probes that error (a quoting/f-string slip, a wrong kwarg, a bad path) leave a
300
- bare traceback. Re-run the corrected probe immediately, or print `# expected/handled: <why>` on
301
- the next line, so recovery is adjacent (the heuristic looks for recovery within ~10 lines).
302
- Prefer a small temp `.py` file over a fragile multi-quote `python -c` one-liner — inline
303
- f-string/quoting errors are the top source of stranded probe tracebacks.
213
+ Rule: a park caused by a DIFFERENT actor already finishing the same objective (a sibling PRD, a human) does not self-heal this way. Why: the job has no landed commit of its own. Confirm in the tree that the work is really done, then call `scheduler_archive_prd` with the slug. Archiving marks the job completed and frees the PRDs that depend on it. Never hand-edit `queue.json`: the scheduler and the watchdog both write it. Archiving clears a stale label; it does not fix a real bug.
304
214
 
305
- ```bash
306
- # WRONG — inline f-string slip strands a SyntaxError, then you move on
307
- python -c 'print(f"{p["title"]!r[:40]}")' # SyntaxError, bare in transcript
308
-
309
- # RIGHT — write the probe to a temp file (no shell-quote minefield), or annotate
310
- cat > /tmp/probe.py <<'PY'
311
- print(repr(p["title"])[:40])
312
- PY
313
- python /tmp/probe.py || echo "# expected/handled: probe only, not part of the deliverable"
314
- ```
215
+ Known gap: `pass_no_commit` ("already correct, nothing to commit") isn't yet in the self-heal list above for the general case.
315
216
 
316
- **13b — An *expected* `timeout` cap is success-with-note, not a bare `Exit code 124`.** Capping a
317
- genuinely long task you expect to hit the cap (a full-universe ingest, a long scan) is the
318
- correct §1/§8 behavior — but a bare `Exit code 124` reads as failure to the verifier. Branch on
319
- 124 explicitly:
217
+ ---
320
218
 
321
- ```bash
322
- timeout 120 python -m project.ingest --all || { rc=$?
323
- [ $rc -eq 124 ] && echo "hit time cap — idempotent/partial; rows persist incrementally; OK" \
324
- || { echo "HALT: ingest failed rc=$rc"; exit 1; }; }
325
- ```
219
+ ## §15 Pre-queue checklist (the litany)
326
220
 
327
- For work that legitimately needs longer than a safe cap, run it in the background and poll a
328
- bounded number of times (§1) rather than capping the foreground command.
329
-
330
- **This actually happened** (PRDs 77 + 80, 2026-06-13): 77 stranded an inline-`python -c` f-string
331
- `SyntaxError` from a throwaway permalink probe; 80 surfaced a bare `Exit code 124` from a
332
- `timeout`-capped full-universe EDGAR ingest. Both committed correct, green, AC-complete work
333
- (77's cursor-hold fix; 80's 6 EDGAR rows + installed cron) yet were downgraded to `needs_review`
334
- on the incidental middle-of-run markers. 13a/13b keep the middle of the transcript clean.
335
-
336
- ## §14 Queueing PRDs from external automation
337
-
338
- **Summary:** A downstream project (Connector Atlas, `gh-issue-5`) wanted a Slack-feedback → PRD
339
- loop but had no documented programmatic queueing path. This section is that path.
340
-
341
- **Decision, up front:** Is the session-manager Electron app running on this machine right now?
342
- - **Yes** → call the `scheduler_create_prd` MCP tool.
343
- - **No** → write the PRD file by hand into the prds directory, and accept the collision risk
344
- described below.
345
-
346
- ### The `scheduler_create_prd` MCP tool
347
-
348
- Wraps `POST /admin/scheduler/create-prd` on the loopback admin API
349
- (`src/main/lib/localAdminHttp.cjs` + `src/main/lib/prdCreate.cjs`, PRD 549/688) via `scripts/scheduler-mcp-server.cjs`, registered in this
350
- repo's `.mcp.json` as the `session-manager-scheduler` MCP server. An external project wanting to
351
- call it from its own automation needs the equivalent MCP server registration pointing at this
352
- repo's `scripts/scheduler-mcp-server.cjs`, or can call the admin HTTP route directly (same
353
- request/response shape) using the token at `~/.claude/session-manager/admin-api.json`.
354
-
355
- **Input** (validated server-side by `ipcSchemas.cjs`'s `schemas.schedulerCreatePrd`):
356
-
357
- | field | type | required | notes |
358
- |---|---|---|---|
359
- | `title` | string | yes | one-line title, no newlines |
360
- | `cwd` | string | yes | absolute path to the target project; validated via `config.cjs`'s `validatePath` (allowedRoots = home dir) |
361
- | `estimateMinutes` | number | yes | integer wall-clock estimate |
362
- | `goal` | string | yes | 2–4 sentences: what the executor builds and why |
363
- | `acceptanceCriteria` | string[] | yes | 1–100 entries, each one verifiable checklist line |
364
- | `implementationNotes` | string | yes | file paths, patterns, constraints the executor needs |
365
- | `outOfScope` | string[] | no | what NOT to build |
366
- | `slug` | string | no | kebab-case; derived from `title` if omitted |
367
- | `parallelGroup` | number | no | opt into an existing `NN` group instead of allocating a new one |
368
-
369
- **Return** (`{nn, filename, status}`, per `prdCreate.cjs`'s registerAdminRoute):
370
- ```json
371
- { "nn": 550, "filename": "550-my-feature.md", "status": "queued" }
372
- ```
373
- On failure the tool returns `{ ok: false, error: "..." }` (e.g. `409` if `filename` already
374
- exists, `400` if `cwd` is rejected by `validatePath` or the payload fails schema validation).
375
-
376
- **Worked example** (MCP tool call, e.g. from Claude Code or any MCP client):
377
- ```json
378
- {
379
- "tool": "scheduler_create_prd",
380
- "arguments": {
381
- "title": "Sync Slack #feedback channel into feedback intake",
382
- "cwd": "~/Projects/connector-atlas",
383
- "estimateMinutes": 20,
384
- "goal": "Pull unread messages from the #feedback Slack channel and materialize each as a feedback file, deduped against already-tracked message ts.",
385
- "acceptanceCriteria": [
386
- "New feedback files land in connector-atlas's own feedback intake folder, one per undeduped message",
387
- "Each file's `source` field is `slack-<channel>-<ts>` for future dedup",
388
- "timeout 300 npm run typecheck passes"
389
- ],
390
- "implementationNotes": "Use the Slack Web API conversations.history endpoint; token lives in connector-atlas's own secrets store, not session-manager's."
391
- }
392
- }
393
- ```
394
- Server-side, this atomically allocates the `NN` prefix (`allocateParallelGroup()`, PRD 548),
395
- appends the engineering standards block, and writes the PRD file — the same shape `/develop`
396
- produces by hand.
397
-
398
- ### The app-must-be-running caveat (read this first)
399
-
400
- **`scheduler_create_prd` only works while the session-manager Electron app is running on this
401
- machine.** The admin server it depends on (`src/main/lib/localAdminHttp.cjs`) is hosted *inside* the
402
- Electron process — it binds a loopback port and writes its token to
403
- `~/.claude/session-manager/admin-api.json` on app boot (see `CLAUDE.md`'s `localAdminHttp.cjs`
404
- architecture entry) and stops existing the moment the app quits — it is not a standalone daemon.
405
- If the app is closed, `scheduler-mcp-server.cjs` cannot read a live port/token and every call
406
- returns the error `session-manager app is not running (admin API unreachable) — start it first`.
407
- This is the single most likely point of confusion for an automation author who assumes the tool
408
- is a normal always-on API — it is not; it is a convenience surface hosted by a desktop app that
409
- the user may or may not have open.
410
-
411
- ### Fallback: writing the PRD file directly
412
-
413
- When the app is not running, first join the EXISTING, already-human-approved Epic you're already
414
- working inside — `node <session-manager-repo>/scripts/mint-epic.cjs <cwd> <epic-id>`; its last
415
- stdout line is the prds dir. This only joins; it never creates an Epic, and errors out if
416
- `<epic-id>` doesn't already exist — get a human to create/approve the Epic first (New Epic UI, or
417
- `/propose-epic` + Approve & start) if it doesn't. Then write `<NN>-<slug>.md` by hand into that
418
- `<cwd>/session-manager-operations/scheduler/epics/<epic-id>/prds/` dir (the flat `scheduler/prds/` is RETIRED and auto-archived unexecuted at boot), add `sourcePromptId: <epic-id>` to the frontmatter so the job keeps its Epic linkage, following the frontmatter rules in §6 and the
419
- body conventions the rest of this guide describes (`# Goal`, `# Acceptance criteria`,
420
- `# Implementation notes`, `## Engineering standards` inlined verbatim — see `/develop`'s output
421
- for the exact shape). **Trade-off:** this path has no atomic `NN` allocation. The tool's
422
- `allocateParallelGroup()` (PRD 548) exists specifically to close a race where two writers pick
423
- the same `NN` at once; a hand-written file bypasses that reservation entirely, so if another
424
- writer (a human, `/develop`, or another automation) picks the same `NN` around the same time, one
425
- file silently shadows or is shadowed by the other's number. Pick an `NN` by scanning ALL of the
426
- project's prds dirs (`scheduler/epics/*/prds/` and `prds-archived/`) for the current max and
427
- incrementing, and treat a collision as possible, not merely theoretical. NN is strictly unique
428
- per project (PRD 832) — NEVER reuse an existing number to signal "runs in parallel"; that
429
- convention is retired. Express ordering with `dependsOn: [<slug>, ...]` frontmatter (the job is
430
- eligible once every listed slug's queue row is completed); independent PRDs omit it and the
431
- scheduler may run them concurrently.
432
-
433
- ### Ownership boundary
434
-
435
- Projects own their own source adapters — Slack, GitHub, Linear, email, whatever inbound channel
436
- they read. Session-manager owns the queueing API (`scheduler_create_prd` / the admin route) and
437
- nothing upstream of it. Session-manager does not host, run, or import another project's adapter
438
- code; the adapter runs entirely inside the calling project (its own cron, its own credentials,
439
- its own filtering/triage logic) and only reaches into session-manager at the single, narrow
440
- `scheduler_create_prd` call.
441
-
442
- ### Why project-supplied `automation-hooks.js` was declined
443
-
444
- Connector Atlas's third ask was a mechanism to drop a project-supplied `automation-hooks.js` file
445
- that session-manager would load and execute in-process on a timer. This was declined, and should
446
- not be re-proposed:
447
-
448
- - It would grant main-process privileges (full filesystem access, IPC, the admin server's own
449
- token) to arbitrary code from any project directory, invoked on a schedule the *project*
450
- controls rather than the *user*.
451
- - It inverts this project's core invariants: `config.cjs`'s `validatePath` gate on every
452
- filesystem path, `ipcSchemas.cjs`'s zod-validated IPC boundary, and `CLAUDE.md`'s Avoid-list
453
- ban on `shell: true` outside the two features that legitimately need it. A loaded-and-executed
454
- project JS file has no equivalent boundary to pass through — it *is* the process.
455
- - The supported extension point is the MCP tool described above, called from *outside* the
456
- session-manager process by the project's own cron/automation. That keeps the privilege boundary
457
- where it already is (the loopback admin server, token-authed, narrow three-route surface) rather
458
- than dissolving it.
459
-
460
- ### Reference implementation of this exact loop
461
-
462
- The proposal → approve → `/develop` → queue path is a working example of this
463
- shape, landed 2026-07-14 (`feat(process-feedback): sync open GitHub issues into the feedback
464
- intake`, commit `352b89c`). It syncs open GitHub issues into `session-manager-operations/feedback/`
465
- (deduped on a `gh-issue-<N>` token), then processes each item through the same triage → `/develop`
466
- → queue path the rest of this guide documents. An external project building a Slack (or any
467
- other) adapter should follow the same source → triage → queue shape, ending at
468
- `scheduler_create_prd` (app running) or a hand-written PRD file (app closed) as described above.
469
-
470
- ## §15 Resolving a `failed`/`needs_review` job whose target work is already done
471
-
472
- **Symptom:** a job lands in `failed` or `needs_review` even though its actual objective was
473
- already satisfied — either by a sibling/concurrent job that finished the same work first (a
474
- duplicate PRD racing another one), or by an out-of-band actor (a human, another agent) reaching
475
- the same target state before the scheduler's run even started. The job's own transcript may be
476
- completely accurate (`SCHEDULER_VERDICT: PASS but no commit landed during the run window`) — the
477
- "failure" is a stale queue entry, not broken work.
478
-
479
- **Do NOT hand-edit `queue.json` to fix the status.** It's a live file the running Electron
480
- scheduler process (and the external watchdog) both read and write; a manual edit races the app's
481
- own save cycle and risks a torn write. There is also no supported IPC/CLI surface today to flip a
482
- single job's `status` field directly.
483
-
484
- **The safe, supported remediation — confirmed working live (2026-07-18):** archive the job's PRD
485
- *source file*, not the queue entry. `reconcile()` (`src/main/scheduler.cjs`) runs on every queue
486
- read/tick and drops any `queue.json` job entry whose PRD `.md` no longer exists in `prds/` — so
487
- archiving the file is sufficient; you never touch `queue.json` yourself.
488
-
489
- ```js
490
- // From this repo's root (session-manager), or anywhere queueOps.cjs is reachable:
491
- const q = require('./src/main/queueOps.cjs');
492
- await q.archiveMany(['<slug-of-the-stale-job>']);
493
- // Atomic rename to prds-archived/<ISO>/<slug>.md — reversible, path-contained,
494
- // no queue.json write. The next reconcile() (within one scheduler tick, ~10-15s
495
- // observed) drops the matching queue.json job entry automatically.
496
- ```
221
+ Before queueing a new PRD, verify each of these:
497
222
 
498
- This is the exact mechanism `schedule:clear-queue`'s IPC handler uses internally, just scoped to
499
- one slug instead of "every non-running job" — safe to call from outside Electron (no admin API
500
- needed) since `queueOps.cjs`'s `archiveMany` is a plain exported function, not IPC-gated.
501
-
502
- **When to use this vs. requeueing a retry:** only when you've confirmed the target state is
503
- already correct (read the failed job's own transcript/log — did it conclude "nothing left to
504
- do"? did a sibling job's commit already land the same objective? does an independent check like
505
- `gh pr view <n> --json mergeable` already show the desired end state?). If the target state is
506
- genuinely NOT yet reached, fix and requeue instead — archiving does not fix an actual bug, it
507
- only clears a stale status label for already-completed work.
508
-
509
- **Known gap this doesn't cover:** the verifier's `pass_no_commit` classification (a run that
510
- concludes "PASS, no code change needed" gets flagged `needs_review` as if it were suspicious)
511
- does not yet special-case "another actor already satisfied this PRD's postcondition" for
512
- externally-checkable targets (e.g. a `gh pr view`-mergeable branch). `RESCANNABLE_VERDICTS`
513
- already includes `pass_no_commit` for one narrow case (fix-plan jobs, exempted 2026-07-12) but
514
- not the general case. See `session-manager-operations/feedback/processed/` for the tracked
515
- follow-up on softening this classification for merge-style PRDs with a checkable postcondition.
223
+ - [ ] **§1 Bounded waits:** every poll loop has a `for i in $(seq 1 N)` cap ≤20 iterations; no uptime/restart signal used to detect a static-content deploy.
224
+ - [ ] **Every command bounded:** every test/build/deploy command is wrapped in `timeout` (typecheck/unit 300s, e2e 120s, `curl --max-time 15`).
225
+ - [ ] **§2 No bonus work:** the AC list is the only source of work.
226
+ - [ ] **§3 Verify, don't poll:** every deploy/migration step is followed by a test command that exits 1 on failure; the AC test command ran green once before you declare done.
227
+ - [ ] **§4 Bounded generators:** any search/seed loop has an explicit max and surfaces failure on exhaustion.
228
+ - [ ] **§5 Frontmatter:** `title`, `cwd` (exists on this machine), `estimateMinutes` present; `dependsOn` used for ordering, not `parallelGroup`.
229
+ - [ ] **§6 Gate and files:** `gate` follows every rule in §6 (or is `["none"]` alone); `files` is 1–50 repo-relative paths, no overlap with a concurrent PRD.
230
+ - [ ] **§7 Self-contained:** no reference to "the conversation" or outside context; paths/identifiers inline; clean UTF-8, no NUL bytes (`grep -qP '\x00' file && echo BAD`).
231
+ - [ ] **§8 Scope:** targets ≤10 min, ceiling 15; e2e/publish sharded to one spec per PRD.
232
+ - [ ] **§9 Failure surfacing:** errors exit 1 with a diagnostic line; no silent `|| true`.
233
+ - [ ] **§10 Negative assertions:** every "should find nothing" check is inverted to exit 0 on the clean case.
234
+ - [ ] **§11 End green:** the gate runs last; any intentional failure runs early and is captured.
235
+ - [ ] **§12 No stranded probes:** a probe error is corrected or annotated; an expected `timeout` cap branches on exit 124.