@yemi33/minions 0.1.2447 → 0.1.2449

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 (315) hide show
  1. package/bin/cli-api-client.js +1 -1
  2. package/bin/install-internal-minions.js +1382 -44
  3. package/bin/install-layout.js +150 -0
  4. package/bin/minions.js +460 -167
  5. package/dashboard/docs/typography.md +65 -12
  6. package/dashboard/js/command-center.js +66 -7
  7. package/dashboard/js/detail-panel.js +36 -0
  8. package/dashboard/js/memory-panel.js +59 -12
  9. package/dashboard/js/qa.js +186 -45
  10. package/dashboard/js/refresh.js +148 -12
  11. package/dashboard/js/render-dispatch.js +3 -4
  12. package/dashboard/js/render-inbox.js +2 -2
  13. package/dashboard/js/render-other.js +3 -3
  14. package/dashboard/js/render-pipelines.js +14 -0
  15. package/dashboard/js/render-plans.js +57 -9
  16. package/dashboard/js/render-prd.js +132 -23
  17. package/dashboard/js/render-prs.js +195 -166
  18. package/dashboard/js/render-schedules.js +63 -3
  19. package/dashboard/js/render-utils.js +3 -3
  20. package/dashboard/js/render-watches.js +19 -3
  21. package/dashboard/js/render-work-items.js +238 -30
  22. package/dashboard/js/settings.js +205 -54
  23. package/dashboard/js/utils.js +51 -1
  24. package/dashboard/pages/home.html +1 -1
  25. package/dashboard/pages/qa.html +1 -16
  26. package/dashboard/pages/work.html +40 -0
  27. package/dashboard/shared/cc-limits.js +79 -0
  28. package/dashboard/shared/pr-filters.js +21 -38
  29. package/dashboard/shared/project-git-summary.js +1 -1
  30. package/dashboard/shared/record-filters.js +169 -0
  31. package/dashboard/shared/watches-source.js +1 -1
  32. package/dashboard/shared/welcome-popup.js +1 -1
  33. package/dashboard/shared/wi-filters.js +302 -0
  34. package/dashboard/slim/body.html +1 -0
  35. package/dashboard/slim/js/command-send.js +26 -0
  36. package/dashboard/slim/js/modals-tiles.js +380 -39
  37. package/dashboard/slim/js/status.js +13 -21
  38. package/dashboard/slim/layout.html +1 -0
  39. package/dashboard/slim/panel-bootstrap.js +6 -2
  40. package/dashboard/slim/styles.css +38 -0
  41. package/dashboard/styles.css +159 -55
  42. package/dashboard-build.js +52 -6
  43. package/dashboard.js +956 -423
  44. package/docs/README.md +11 -6
  45. package/docs/api-errors.md +2 -2
  46. package/docs/architecture-review-2026-07-09.md +1 -1
  47. package/docs/architecture.excalidraw +2 -2
  48. package/docs/auto-discovery.md +18 -9
  49. package/docs/branch-derivation.md +4 -4
  50. package/docs/capture-demos.js +39 -2
  51. package/docs/ci-runner-canary.md +123 -0
  52. package/docs/claude-md-propagation.md +2 -2
  53. package/docs/cloud-agent-dispatch.md +204 -0
  54. package/docs/command-center.md +7 -7
  55. package/docs/completion-reports.md +43 -20
  56. package/docs/constants.md +10 -3
  57. package/docs/constellation-bridge.md +134 -6
  58. package/docs/constellation-style-telemetry.md +4 -4
  59. package/docs/contracts/capability-protocol.v1.json +165 -0
  60. package/docs/cooldown-merge-semantics.md +12 -12
  61. package/docs/copilot-cli-schema.md +7 -7
  62. package/docs/cross-repo-plans.md +10 -10
  63. package/docs/dead-code-audit-retractions.md +5 -5
  64. package/docs/default-branch-ci.md +173 -0
  65. package/docs/deprecated.json +31 -31
  66. package/docs/design-inbox-entries-schema.md +3 -3
  67. package/docs/design-language.md +1051 -0
  68. package/docs/design-state-storage.md +11 -11
  69. package/docs/diagnostics-crash-reports.md +9 -9
  70. package/docs/diagnostics-memory.md +5 -5
  71. package/docs/documentation-audit-2026-07-09.md +7 -7
  72. package/docs/engine-restart.md +90 -5
  73. package/docs/harness-mode.md +1 -1
  74. package/docs/internal-install.md +338 -39
  75. package/docs/kb-dedup-duplicate-pair-investigation.md +5 -5
  76. package/docs/kb-pr3223-cascade-archiving.md +1 -1
  77. package/docs/kb-pr696-merge-conflict-docs.md +6 -6
  78. package/docs/kb-sweep.md +35 -35
  79. package/docs/keep-processes.md +1 -1
  80. package/docs/live-checkout-mode.md +30 -30
  81. package/docs/managed-spawn.md +18 -14
  82. package/docs/named-agents.md +7 -7
  83. package/docs/plan-lifecycle.md +69 -2
  84. package/docs/pr-author-identity.md +114 -0
  85. package/docs/pr-auto-fix-dispatch.md +19 -4
  86. package/docs/pr-comment-followup.md +6 -6
  87. package/docs/pr-review-fix-loop.md +59 -10
  88. package/docs/process-termination.md +40 -0
  89. package/docs/proposals/repo-pool-for-live-checkout.md +13 -13
  90. package/docs/qa-runbook-lifecycle.md +367 -17
  91. package/docs/qa-runbooks.md +3 -3
  92. package/docs/rfc-completion-json.md +18 -18
  93. package/docs/runtime-adapters.md +26 -21
  94. package/docs/security.md +6 -6
  95. package/docs/self-improvement.md +4 -4
  96. package/docs/shared-lifecycle-module-map.md +473 -472
  97. package/docs/skills.md +52 -3
  98. package/docs/slim-ux/concepts.md +121 -116
  99. package/docs/specs/agent-configurability.md +18 -18
  100. package/docs/specs/agent-rename.md +18 -18
  101. package/docs/team-memory.md +38 -21
  102. package/docs/timeouts-and-liveness.md +118 -10
  103. package/docs/tutorials/01-install-and-connect.md +1 -1
  104. package/docs/watches.md +40 -39
  105. package/docs/workspace-manifests.md +4 -4
  106. package/docs/worktree-lifecycle.md +293 -14
  107. package/engine/README.md +46 -0
  108. package/engine/{ado-comment.js → ado/comment.js} +8 -8
  109. package/engine/{ado-git-auth.js → ado/git-auth.js} +4 -4
  110. package/engine/{ado.js → ado/index.js} +417 -63
  111. package/engine/{ado-status.js → ado/status.js} +6 -8
  112. package/engine/{ado-token.js → ado/token.js} +1 -1
  113. package/engine/{acp-transport.js → agents/acp-transport.js} +62 -22
  114. package/engine/{agent-worker-pool.js → agents/agent-worker-pool.js} +17 -8
  115. package/engine/{cc-worker-pool.js → agents/cc-worker-pool.js} +16 -6
  116. package/engine/{claude-md-context.js → agents/claude-md-context.js} +5 -5
  117. package/engine/{harness-context.js → agents/harness-context.js} +5 -5
  118. package/engine/{harness.js → agents/harness.js} +3 -3
  119. package/engine/{llm.js → agents/llm.js} +18 -14
  120. package/engine/{model-discovery.js → agents/model-discovery.js} +2 -2
  121. package/engine/{playbook.js → agents/playbook.js} +155 -22
  122. package/engine/{pooled-agent-process.js → agents/pooled-agent-process.js} +14 -12
  123. package/engine/{preflight.js → agents/preflight.js} +29 -10
  124. package/engine/{spawn-agent.js → agents/spawn-agent.js} +25 -14
  125. package/engine/{spawn-phase-watchdog.js → agents/spawn-phase-watchdog.js} +16 -7
  126. package/engine/{steering.js → agents/steering.js} +5 -5
  127. package/engine/{tools-inventory.js → agents/tools-inventory.js} +2 -2
  128. package/engine/{agent-api-validation.js → api/agent-api-validation.js} +2 -2
  129. package/engine/{api-validation.js → api/api-validation.js} +1 -1
  130. package/engine/api/bridge.js +787 -0
  131. package/engine/{cc-api-validation.js → api/cc-api-validation.js} +1 -1
  132. package/engine/api/companion.js +560 -0
  133. package/engine/{content-api-validation.js → api/content-api-validation.js} +2 -2
  134. package/engine/{pr-issue-validation.js → api/pr-issue-validation.js} +33 -6
  135. package/engine/{settings-validation.js → api/settings-validation.js} +32 -4
  136. package/engine/api-contracts/agent-content.js +4 -4
  137. package/engine/api-contracts/capability-manifest.js +236 -0
  138. package/engine/api-contracts/capability-protocol.js +333 -0
  139. package/engine/api-contracts/cc-ops.js +1 -1
  140. package/engine/api-contracts/config-runtime.js +5 -0
  141. package/engine/api-contracts/core.js +28 -1
  142. package/engine/api-contracts/index.js +100 -0
  143. package/engine/api-contracts/orchestration.js +18 -5
  144. package/engine/api-contracts/pull-requests.js +37 -6
  145. package/engine/api-contracts/qa-process.js +29 -6
  146. package/engine/api-contracts/work-plan-prd.js +21 -1
  147. package/engine/cloud/contract.js +212 -0
  148. package/engine/cloud/index.js +159 -0
  149. package/engine/{execution-model.js → core/execution-model.js} +1 -1
  150. package/engine/{features.js → core/features.js} +4 -4
  151. package/engine/{operator-identity.js → core/operator-identity.js} +1 -1
  152. package/engine/{queries.js → core/queries.js} +201 -36
  153. package/engine/{safe-expr.js → core/safe-expr.js} +1 -1
  154. package/engine/{shared.js → core/shared.js} +1637 -175
  155. package/engine/{stdio-timestamps.js → core/stdio-timestamps.js} +1 -1
  156. package/engine/{untrusted-fence.js → core/untrusted-fence.js} +3 -3
  157. package/engine/db/index.js +11 -2
  158. package/engine/db/migrations/002-dispatches.js +3 -3
  159. package/engine/db/migrations/003-work-items.js +1 -1
  160. package/engine/db/migrations/004-pull-requests.js +1 -1
  161. package/engine/db/migrations/006-metrics.js +1 -1
  162. package/engine/db/migrations/007-watches.js +2 -2
  163. package/engine/db/migrations/008-small-state.js +1 -1
  164. package/engine/db/migrations/009-qa.js +1 -1
  165. package/engine/db/migrations/010-pr-links.js +1 -1
  166. package/engine/db/migrations/011-remaining-state.js +1 -1
  167. package/engine/db/migrations/012-steering-deliveries.js +2 -2
  168. package/engine/db/migrations/013-backfill-broken-note-links.js +1 -1
  169. package/engine/db/migrations/014-pr-fix-target-prefs.js +2 -2
  170. package/engine/db/migrations/015-plans-prds.js +0 -0
  171. package/engine/db/migrations/018-sql-only-cutover.js +2 -2
  172. package/engine/db/migrations/021-archived-work-items.js +1 -1
  173. package/engine/db/migrations/022-global-cc-session.js +1 -1
  174. package/engine/db/migrations/023-engine-state.js +1 -1
  175. package/engine/db/migrations/025-malformed-work-item-phantoms.js +1 -1
  176. package/engine/db/migrations/027-review-learning-lifecycle.js +1 -1
  177. package/engine/db/migrations/029-repair-reused-versions.js +20 -0
  178. package/engine/db/migrations/031-pr-author-identity.js +137 -0
  179. package/engine/{consolidation.js → memory/consolidation.js} +6 -6
  180. package/engine/{kb-sweep-runner.js → memory/kb-sweep-runner.js} +2 -2
  181. package/engine/{kb-sweep.js → memory/kb-sweep.js} +9 -7
  182. package/engine/{memory-retrieval.js → memory/memory-retrieval.js} +46 -4
  183. package/engine/{memory-store.js → memory/memory-store.js} +3 -3
  184. package/engine/{promotion.js → memory/promotion.js} +3 -3
  185. package/engine/{review-learning-backfill.js → memory/review-learning-backfill.js} +6 -6
  186. package/engine/{review-learning.js → memory/review-learning.js} +10 -5
  187. package/engine/{diagnostics-memory.js → observability/diagnostics-memory.js} +1 -1
  188. package/engine/{logs-store.js → observability/logs-store.js} +5 -5
  189. package/engine/{metrics-store.js → observability/metrics-store.js} +4 -4
  190. package/engine/{check-status.js → operations/check-status.js} +3 -3
  191. package/engine/{cli.js → operations/cli.js} +271 -113
  192. package/engine/{distribution.js → operations/distribution.js} +5 -6
  193. package/engine/{cleanup.js → orchestration/cleanup.js} +72 -45
  194. package/engine/{cooldown.js → orchestration/cooldown.js} +5 -5
  195. package/engine/{dispatch-events.js → orchestration/dispatch-events.js} +2 -2
  196. package/engine/{dispatch.js → orchestration/dispatch.js} +129 -36
  197. package/engine/orchestration/failed-scheduled-cleanup.js +274 -0
  198. package/engine/{lifecycle.js → orchestration/lifecycle.js} +198 -90
  199. package/engine/{meeting.js → orchestration/meeting.js} +6 -16
  200. package/engine/{pipeline.js → orchestration/pipeline.js} +12 -12
  201. package/engine/{pre-dispatch-eval.js → orchestration/pre-dispatch-eval.js} +10 -9
  202. package/engine/{routing.js → orchestration/routing.js} +3 -3
  203. package/engine/{schedule-bootstrap.js → orchestration/schedule-bootstrap.js} +4 -4
  204. package/engine/{scheduler.js → orchestration/scheduler.js} +38 -8
  205. package/engine/{timeout.js → orchestration/timeout.js} +158 -109
  206. package/engine/{db-events.js → persistence/db-events.js} +2 -2
  207. package/engine/{dispatch-store.js → persistence/dispatch-store.js} +7 -7
  208. package/engine/{inbox-store.js → persistence/inbox-store.js} +2 -2
  209. package/engine/{note-link-backfill.js → persistence/note-link-backfill.js} +4 -4
  210. package/engine/{pr-fix-target-store.js → persistence/pr-fix-target-store.js} +8 -8
  211. package/engine/{pull-requests-store.js → persistence/pull-requests-store.js} +21 -7
  212. package/engine/{small-state-store.js → persistence/small-state-store.js} +31 -31
  213. package/engine/persistence/state-operations.js +350 -0
  214. package/engine/{steering-store.js → persistence/steering-store.js} +6 -6
  215. package/engine/{issues.js → planning/issues.js} +2 -2
  216. package/engine/{plan-prd-validation.js → planning/plan-prd-validation.js} +8 -2
  217. package/engine/planning/prd-result-sidecar.js +190 -0
  218. package/engine/{prd-store.js → planning/prd-store.js} +17 -17
  219. package/engine/{project-discovery.js → planning/project-discovery.js} +5 -5
  220. package/engine/{projects.js → planning/projects.js} +10 -10
  221. package/engine/{resolve-area.js → planning/resolve-area.js} +1 -1
  222. package/engine/{work-item-validation.js → planning/work-item-validation.js} +39 -3
  223. package/engine/{work-items-store.js → planning/work-items-store.js} +29 -21
  224. package/engine/{keep-process-sweep.js → processes/keep-process-sweep.js} +57 -17
  225. package/engine/{managed-spawn-launcher.js → processes/managed-spawn-launcher.js} +3 -3
  226. package/engine/{managed-spawn.js → processes/managed-spawn.js} +97 -46
  227. package/engine/{process-utils.js → processes/process-utils.js} +599 -55
  228. package/engine/{abandoned-pr-reconciliation.js → providers/abandoned-pr-reconciliation.js} +17 -7
  229. package/engine/{comment-classifier.js → providers/comment-classifier.js} +85 -17
  230. package/engine/{comment-format.js → providers/comment-format.js} +5 -5
  231. package/engine/{gh-comment.js → providers/gh-comment.js} +15 -15
  232. package/engine/{gh-token.js → providers/gh-token.js} +4 -4
  233. package/engine/{github.js → providers/github.js} +131 -54
  234. package/engine/{pr-action.js → providers/pr-action.js} +13 -12
  235. package/engine/{pr-clone-keep.js → providers/pr-clone-keep.js} +7 -7
  236. package/engine/{pr-devbox.js → providers/pr-devbox.js} +6 -6
  237. package/engine/{pr-fix-target.js → providers/pr-fix-target.js} +13 -13
  238. package/engine/{pr-remote-patch.js → providers/pr-remote-patch.js} +4 -4
  239. package/engine/{pr-resolve.js → providers/pr-resolve.js} +7 -7
  240. package/engine/{pr-temp-clone.js → providers/pr-temp-clone.js} +5 -5
  241. package/engine/{pr-track.js → providers/pr-track.js} +11 -13
  242. package/engine/{shared-branch-pr-reconcile.js → providers/shared-branch-pr-reconcile.js} +4 -4
  243. package/engine/qa/auto-prd-qa.js +313 -0
  244. package/engine/{qa-from-prd.js → qa/from-prd.js} +42 -12
  245. package/engine/qa/prd-session.js +240 -0
  246. package/engine/{qa-process-validation.js → qa/process-validation.js} +14 -9
  247. package/engine/{qa-runbooks.js → qa/runbooks.js} +1 -1
  248. package/engine/{qa-runs.js → qa/runs.js} +286 -15
  249. package/engine/{qa-sessions.js → qa/sessions.js} +595 -49
  250. package/engine/qa/visual-journey.js +654 -0
  251. package/engine/{qa-runners.js → qa-runners/index.js} +7 -7
  252. package/engine/qa-runners/maestro.js +3 -3
  253. package/engine/qa-runners/playwright.js +2 -2
  254. package/engine/{restart-health.js → recovery/restart-health.js} +48 -4
  255. package/engine/recovery/stop-stack.js +607 -0
  256. package/engine/{supervisor.js → recovery/supervisor.js} +105 -175
  257. package/engine/{watchdog.js → recovery/watchdog.js} +136 -13
  258. package/engine/runtimes/claude.js +14 -12
  259. package/engine/runtimes/codex.js +8 -6
  260. package/engine/runtimes/copilot.js +17 -16
  261. package/engine/{watch-actions.js → watches/actions.js} +13 -13
  262. package/engine/{watches.js → watches/index.js} +43 -32
  263. package/engine/{watches-store.js → watches/store.js} +4 -4
  264. package/engine/{create-pr-worktree.js → worktrees/create-pr.js} +1 -1
  265. package/engine/{worktree-gc.js → worktrees/gc.js} +70 -22
  266. package/engine/worktrees/inventory.js +671 -0
  267. package/engine/{live-checkout.js → worktrees/live-checkout.js} +4 -4
  268. package/engine/{worktree-pool.js → worktrees/pool.js} +2 -2
  269. package/engine/{worktree-preflight.js → worktrees/preflight.js} +1 -0
  270. package/engine/worktrees/quarantine-refs.js +173 -0
  271. package/engine.js +1137 -208
  272. package/minions.js +147 -77
  273. package/package.json +10 -6
  274. package/playbooks/_pr-description-audit.md +110 -78
  275. package/playbooks/build-fix-complex.md +2 -0
  276. package/playbooks/fix.md +16 -12
  277. package/playbooks/implement-shared.md +2 -0
  278. package/playbooks/implement.md +19 -20
  279. package/playbooks/plan-to-prd.md +18 -3
  280. package/playbooks/qa-session-draft.md +136 -1
  281. package/playbooks/qa-session-execute.md +80 -2
  282. package/playbooks/qa-session-setup.md +17 -1
  283. package/playbooks/qa-validate.md +1 -1
  284. package/playbooks/setup.md +2 -0
  285. package/playbooks/shared-rules.md +25 -32
  286. package/playbooks/templates/followup-dispatch.md +4 -3
  287. package/playbooks/verify.md +1 -1
  288. package/prompts/cc-system.md +19 -27
  289. package/watch-plugins/README.md +92 -0
  290. package/watch-plugins/ado-author-prs.js +336 -0
  291. package/watch-plugins/gh-author-prs.js +375 -0
  292. package/watch-plugins/http.js +474 -0
  293. package/watch-plugins/teams-channel.js +869 -0
  294. package/docs/dev-composite-workflow.md +0 -101
  295. package/docs/pr-screenshots/pr-886/after-single-header.png +0 -0
  296. package/docs/pr-screenshots/pr-886/before-duplicate-header.png +0 -0
  297. package/docs/pr-screenshots/pr-895/01-cancellation-reason-detail.png +0 -0
  298. package/docs/pr-screenshots/pr-899/worker-pool-worktrees-AFTER.png +0 -0
  299. package/docs/pr-screenshots/pr-899/worker-pool-worktrees-BEFORE.png +0 -0
  300. package/docs/pr-screenshots/pr-901/projects-tab-default.png +0 -0
  301. package/docs/pr-screenshots/pr-901/projects-tab-fmf-selected.png +0 -0
  302. package/docs/pr-screenshots/pr-916/model-picker-AFTER-crop.png +0 -0
  303. package/docs/pr-screenshots/pr-916/model-picker-AFTER.png +0 -0
  304. package/docs/pr-screenshots/pr-916/model-picker-BEFORE-crop.png +0 -0
  305. package/docs/pr-screenshots/pr-916/model-picker-BEFORE.png +0 -0
  306. package/docs/pr-screenshots/pr-916/model-picker-dropdown-AFTER.png +0 -0
  307. package/docs/pr-screenshots/pr-979/auto-fix-pane-AFTER.png +0 -0
  308. package/docs/pr-screenshots/pr-979/auto-fix-pane-BEFORE.png +0 -0
  309. package/docs/pr-screenshots/pr-985/pr-column-em-dash-AFTER.png +0 -0
  310. package/docs/pr-screenshots/pr-985/pr-column-em-dash-BEFORE.png +0 -0
  311. package/docs/visual-evidence-ci.md +0 -103
  312. package/engine/bridge.js +0 -379
  313. package/engine/quarantine-refs.js +0 -103
  314. package/engine/state-operations.js +0 -178
  315. /package/engine/{steering-constraints.js → agents/steering-constraints.js} +0 -0
@@ -191,6 +191,29 @@ async function openSettings() {
191
191
  settingsField('CC Turn Timeout', 'set-ccTurnTimeoutMs', (e.ccTurnTimeoutMs || 300000) / 1000, 's', 'Per-turn no-progress watchdog for CC + doc-chat. Resets on every liveness signal (token chunk, tool call, tool update) so an actively-streaming turn — long shell command, deep search, sub-agent loop — survives indefinitely up to the outer 1h hard ceiling. Only true silence past this window fires event: error with code: cc-turn-timeout, stops the spinner, and offers Retry. Clamped to 10–3600 s.', '0.001') +
192
192
  '</div>' +
193
193
  '</details>' +
194
+ // Cloud dispatch (Agency / Copilot cloud) — the fleet-wide default
195
+ // provider for work items that declare `meta.executionSurface: "cloud"`.
196
+ // Collapsed by default: this is opt-in, and a fleet with no cloud
197
+ // provider configured behaves exactly as it always has.
198
+ '<details id="set-cloud-dispatch-details"' + (e.cloudProvider ? ' open' : '') + ' style="margin-top:8px;border-top:1px solid var(--border);padding-top:8px">' +
199
+ '<summary style="cursor:pointer;font-size:var(--text-base);color:var(--text);user-select:none">Cloud dispatch ' +
200
+ '<span style="font-size:var(--text-sm);color:var(--muted)">(hand a work item to a remote coding agent)</span>' +
201
+ '</summary>' +
202
+ '<div style="display:grid;grid-template-columns:1fr;gap:8px;margin-top:8px">' +
203
+ '<div data-search="cloud provider agency remote coding agent dispatch">' +
204
+ '<label style="font-size:var(--text-sm);color:var(--muted);display:block;margin-bottom:2px">Default Cloud Provider</label>' +
205
+ // Populated from /api/cloud-providers on open, and DISABLED with an
206
+ // explanatory hint when the registry is empty. The server validates
207
+ // against the same registry regardless — this is an affordance, not
208
+ // the gate — but a free-text input whose every non-empty value the
209
+ // server rejects is a control that cannot do what it advertises.
210
+ '<select id="set-cloudProvider" style="width:100%;padding:4px 6px;background:var(--surface);border:1px solid var(--border);border-radius:4px;color:var(--text);font-size:var(--text-md)">' +
211
+ '<option value="">Loading…</option>' +
212
+ '</select>' +
213
+ '<div id="set-cloudProvider-hint" style="font-size:var(--text-sm);color:var(--muted);margin-top:1px">Fleet-wide default for work items declaring <code>meta.executionSurface: "cloud"</code> — Minions investigates locally, renders the scoped dispatch instructions, and hands them to a REMOTE coding agent that authors its own PR. Overridden by <code>project.cloudProvider</code>, then <code>meta.cloudProvider</code>. Only PR-producing types (implement, fix, test, docs) may declare the cloud surface.</div>' +
214
+ '</div>' +
215
+ '</div>' +
216
+ '</details>' +
194
217
  '</div>' +
195
218
  '<h4>Agents</h4>' +
196
219
  '<div style="font-size:var(--text-sm);color:var(--muted);margin-bottom:6px">CLI / Model placeholders show the fleet default each agent will inherit. Pick a value to pin per-agent; clear to re-inherit. Per-agent monthly budget overrides the fleet ceiling.</div>' +
@@ -199,14 +222,45 @@ async function openSettings() {
199
222
  agentRows +
200
223
  '</table>';
201
224
 
202
- const paneAutoFix =
203
- '<h3>Auto-fix &amp; Review Loop</h3>' +
204
- '<div class="settings-pane-sub">Everything in this pane is <strong>automatic</strong>. Each tile below enables or disables one automatic PR-triggered dispatch trigger (build failure, merge conflict, review verdict, human comment) — the section framing carries the "auto", so the tiles just name the cause. All require the matching provider polling (Polling tab). The <em>Pause ALL</em> kill-switch below inerts every trigger at once. Non-dispatch knobs (auto-merge, auto-vote, plan/decompose defaults) moved to the new <strong>PR Lifecycle</strong> and <strong>Workflow Defaults</strong> panes.</div>' +
225
+ // W-ms4o7y1g007jdb2d — merged "Plan & PR Workflow" pane. Collapses the former
226
+ // sibling panes 'Auto-fix & Review Loop', 'PR Lifecycle', and 'Workflow
227
+ // Defaults' into ONE tab with three in-order subsections — (1) Plan,
228
+ // (2) PR creation, (3) Monitor. Every set-* id is preserved verbatim so the
229
+ // save/collect handler and the cross-tab settings search are untouched.
230
+ const panePlanPrWorkflow =
231
+ '<h3>Plan &amp; PR Workflow</h3>' +
232
+ '<div class="settings-pane-sub">Everything here is <strong>automatic</strong> — each tile toggles one behavior in the plan→PR→monitor flow. Read top to bottom: what happens when a <strong>plan</strong> lands, what Minions does when it <strong>opens a PR</strong>, and how it <strong>monitors</strong> the PR watch→react loop afterward. Click a tile to enable/disable; enabled tiles are highlighted.</div>' +
233
+
234
+ // ── (1) PLAN ──
235
+ '<div class="settings-section-head"><span class="settings-section-num">1</span><span class="settings-section-title">Plan</span></div>' +
236
+ '<div class="settings-pane-sub">What happens automatically as a plan moves from draft → work items → archive.</div>' +
237
+ '<div class="settings-tile-grid" style="margin-bottom:20px">' +
238
+ settingsTile('Auto-approve Plans', 'set-autoApprovePlans', !!e.autoApprovePlans, 'PRDs are approved automatically without human review.', '✅', 'auto-approve plans') +
239
+ settingsTile('Auto-decompose', 'set-autoDecompose', e.autoDecompose !== false, 'Large implement items are auto-split into sub-tasks.', '🧩', 'auto-decompose large items') +
240
+ settingsTile('Auto-archive Plans', 'set-autoArchive', !!e.autoArchive, 'Automatically archive plans after verify completes (off = manual archive via dashboard).', '📦', 'auto-archive plans') +
241
+ settingsTile('Auto-QA verified PRDs', 'set-autoQaCompletedPrds', !!e.autoQaCompletedPrds, 'Opt-in: once a PRD\'s verify work item(s) complete SUCCESSFULLY and the canonical prd/guides/verify-*.md exists, automatically start a PRD-driven QA Session (mode "auto", so SETUP → DRAFT → EXECUTE runs without approval) through the same path as POST /api/qa/from-prd. Cross-repo plans wait for every per-project verify to succeed and launch ONE session. Failed/cancelled verification never launches QA. OFF (default) = today\'s behavior; the manual "QA this PRD" action is unaffected either way.', '🧪', 'auto qa verified completed prds qa session from prd') +
242
+ '</div>' +
243
+
244
+ // ── (2) PR CREATION ──
245
+ '<div class="settings-section-head"><span class="settings-section-num">2</span><span class="settings-section-title">PR Create</span></div>' +
246
+ '<div class="settings-pane-sub">What Minions does when it opens a PR. The Draft ⇄ Publish switch sets the fleet-wide default.</div>' +
247
+ '<div class="settings-stack" style="margin-bottom:12px">' +
248
+ settingsDraftPublishToggle('set-createPrsAsDraft', !!e.createPrsAsDraft, 'Create PRs as', 'Sets the fleet-wide default for how Minions-created PRs open. Draft = GitHub `gh pr create --draft`; ADO `az repos pr create --draft true`. Publish (default) opens PRs ready-for-review as today. Aggregate/E2E verify PRs already open as draft regardless of this flag.') +
249
+ '</div>' +
250
+ '<div class="settings-tile-grid" style="margin-bottom:20px">' +
251
+ settingsTile('Harnesses in PR body', 'set-includeHarnessSectionInPrBody', e.includeHarnessSectionInPrBody !== false, 'When ON (default), Minions-authored PRs get a fenced "Repo harnesses used" section listing the harness assets in effect for the dispatch\'s runtime + project — runtime, injected CLAUDE.md files, AGENTS.md discovery, MCP servers, and skills. Pure reporting from the same read-only source as `minions doctor --harness`; it does not change harness propagation. OFF suppresses the section.', '🧰', 'surface repo harnesses in pr body') +
252
+ settingsTile('Auto-apply review vote', 'set-autoApplyReviewVote', !!e.autoApplyReviewVote, 'When ON, Minions review verdicts (APPROVE / REQUEST_CHANGES) automatically flip the platform vote on ADO/GitHub. When OFF (default), verdicts are informational only and the human casts the final vote.', '🗳️', 'auto-apply review vote to pr') +
253
+ settingsTile('Auto-complete PRs', 'set-autoCompletePrs', !!e.autoCompletePrs, 'Auto-merge PRs when builds pass and review is approved (opt-in). Independent of the per-cause auto-fix gates above.', '🎯', 'auto-complete prs auto-merge') +
254
+ '</div>' +
255
+
256
+ // ── (3) MONITOR ──
257
+ '<div class="settings-section-head"><span class="settings-section-num">3</span><span class="settings-section-title">Monitor</span></div>' +
258
+ '<div class="settings-pane-sub">The PR watch→react loop. Everything here is <strong>automatic</strong>. The <em>Pause ALL</em> kill-switch inerts every per-cause trigger at once; all PR triggers also require the matching provider polling (Polling tab).</div>' +
205
259
  '<div class="settings-stack" style="margin-bottom:16px">' +
206
260
  settingsToggle('🛑 Pause ALL PR auto-fix dispatches', 'set-autoFixPaused', !!e.autoFixPaused, 'Halts every PR-triggered dispatch (review, re-review, build-failure fix, review-feedback fix, human-comment fix, merge-conflict fix) on the next tick. In-flight agents keep running. Reversible without restart. Per-cause tiles below are inert while this is set.') +
207
261
  '</div>' +
208
- '<div class="settings-pane-sub" style="margin-bottom:8px">Per-cause triggers — each tile automatically dispatches an agent for one PR signal (it names the exact discoverFromPrs site it gates). Enabled tiles are highlighted; disabled tiles are dimmed. When <em>Pause ALL</em> is ON every tile is inert; when it is OFF, each tile decides independently whether its site fires.</div>' +
209
- '<div class="settings-tile-grid" style="margin-bottom:16px">' +
262
+ '<div class="settings-pane-sub" style="margin-bottom:8px">Per-cause triggers — each tile automatically dispatches an agent for one PR signal. Enabled tiles are highlighted; disabled tiles are dimmed. When <em>Pause ALL</em> is ON every tile is inert.</div>' +
263
+ '<div class="settings-tile-grid" style="margin-bottom:20px">' +
210
264
  settingsTile('Fix Builds', 'set-autoFixBuilds', e.autoFixBuilds !== false, 'Shared dispatch gate: auto-fix agent when a PR build fails; also requires that PR provider polling is enabled', '🔨', 'auto-fix builds') +
211
265
  settingsTile('Conflicts', 'set-autoFixConflicts', e.autoFixConflicts !== false, 'Shared dispatch gate: auto-fix agent when a PR merge conflict is detected; also requires that PR provider polling is enabled', '🔀', 'auto-fix conflicts') +
212
266
  settingsTile('Review PRs', 'set-autoReviewPrs', e.autoReviewPrs !== false, 'Shared dispatch gate: review agent for newly opened agent PRs; also requires that PR provider polling is enabled', '🔍', 'auto-review prs') +
@@ -214,46 +268,15 @@ async function openSettings() {
214
268
  settingsTile('Review Feedback', 'set-autoFixReviewFeedback', e.autoFixReviewFeedback !== false, 'Shared dispatch gate: fix agent for minions changes-requested verdicts; also requires that PR provider polling is enabled', '✍️', 'auto-fix review feedback') +
215
269
  settingsTile('Address Human Comments', 'set-autoFixHumanComments', e.autoFixHumanComments !== false, 'Shared dispatch gate: fix agent for actionable human PR comments; also requires that PR provider polling is enabled', '💬', 'auto-fix human comments') +
216
270
  '</div>' +
217
- '<div class="settings-stack" style="margin-bottom:16px">' +
218
- settingsToggle('Eval Loop', 'set-evalLoop', e.evalLoop !== false, 'Gates the review→fix iteration loop only (first review, re-review, review-feedback fix). Does NOT gate build-failure, merge-conflict, or human-comment fixes. Use the emergency stop above to halt everything.') +
219
- settingsToggle('Pre-dispatch Eval (cheap LLM gate)', 'set-enablePreDispatchEval', e.enablePreDispatchEval !== false, 'P-d2a9f6e5: cheap LLM gate that screens work items for clear/actionable/testable criteria BEFORE queueing the agent. Catches noop dispatches authored from impossible/ambiguous WIs. Fail-open on any validator error.') +
220
- settingsToggle('Pre-dispatch Eval: skip PRD-sourced items', 'set-preDispatchEvalSkipPrdSourced', e.preDispatchEvalSkipPrdSourced !== false, 'W-mq9acoo800177bcb: short-circuit the validator for items materialized from an approved/active PRD — plan-to-prd already LLM-vets them. Reduces queue-time on an N-item approved PRD from ~N×25s to nearly instant. OFF = always re-validate even PRD-sourced items.') +
271
+ '<div class="settings-pane-sub" style="margin-bottom:8px">Review→fix loop gating — the cheap-LLM screens that decide whether the review/fix loop runs at all.</div>' +
272
+ '<div class="settings-tile-grid" style="margin-bottom:12px">' +
273
+ settingsTile('Eval Loop', 'set-evalLoop', e.evalLoop !== false, 'Gates the review→fix iteration loop only (first review, re-review, review-feedback fix). Does NOT gate build-failure, merge-conflict, or human-comment fixes. Use the emergency stop above to halt everything.', '🔁', 'eval loop review fix iteration') +
274
+ settingsTile('Pre-dispatch Eval', 'set-enablePreDispatchEval', e.enablePreDispatchEval !== false, 'P-d2a9f6e5: cheap LLM gate that screens work items for clear/actionable/testable criteria BEFORE queueing the agent. Catches noop dispatches authored from impossible/ambiguous WIs. Fail-open on any validator error.', '🧪', 'pre-dispatch eval cheap llm gate') +
275
+ settingsTile('Skip PRD-sourced', 'set-preDispatchEvalSkipPrdSourced', e.preDispatchEvalSkipPrdSourced !== false, 'W-mq9acoo800177bcb: short-circuit the validator for items materialized from an approved/active PRD — plan-to-prd already LLM-vets them. Reduces queue-time on an N-item approved PRD from ~N×25s to nearly instant. OFF = always re-validate even PRD-sourced items.', '⏭️', 'pre-dispatch eval skip prd sourced items') +
221
276
  '</div>' +
222
- '<div class="settings-grid-2">' +
277
+ '<div class="settings-grid-2" style="margin-bottom:20px">' +
223
278
  settingsField('Eval Max Cost', 'set-evalMaxCost', e.evalMaxCost === null || e.evalMaxCost === undefined ? '' : e.evalMaxCost, '$', 'USD ceiling per work item across all eval iterations (blank = no limit)') +
224
279
  settingsField('Pre-dispatch Eval Concurrency', 'set-preDispatchEvalConcurrency', e.preDispatchEvalConcurrency || 6, '', 'W-mq9acoo800177bcb: max parallel pre-dispatch validator calls per discovery tick. Default 6. Clamped to [1, 20]. Raises throughput when multiple PRDs queue at once; lower if the LLM provider throttles. 1 = sequential (pre-fix behavior).') +
225
- '</div>' +
226
- '<div style="margin-top:12px;padding:6px 8px;border:1px solid var(--border);border-radius:4px;background:rgba(130,160,210,0.06);font-size:var(--text-sm);color:var(--muted)">' +
227
- 'Moved to <strong>PR Lifecycle</strong>: Auto-complete PRs, Auto-apply review vote. ' +
228
- 'Moved to <strong>Workflow Defaults</strong>: Auto-archive Plans, Auto-approve Plans, Auto-decompose, Auto-consolidate Memory.' +
229
- '</div>';
230
-
231
- // P-g7a2b4c5 — new "PR Lifecycle" pane. Owns the two PR-lifecycle knobs
232
- // that historically lived under Auto-fix but do NOT gate any dispatch:
233
- // autoApplyReviewVote (lifecycle vote-posting), autoCompletePrs (auto-merge).
234
- const paneLifecycle =
235
- '<h3>PR Lifecycle</h3>' +
236
- '<div class="settings-pane-sub">Post-merge / post-review lifecycle knobs. These are not dispatch gates — they control what Minions does with a PR once review or build state lands.</div>' +
237
- '<div class="settings-stack">' +
238
- settingsToggle('Auto-apply review vote to PR', 'set-autoApplyReviewVote', !!e.autoApplyReviewVote, 'When ON, Minions review verdicts (APPROVE / REQUEST_CHANGES) automatically flip the platform vote on ADO/GitHub. When OFF (default), verdicts are informational only and the human casts the final vote.') +
239
- settingsToggle('Create PRs as draft', 'set-createPrsAsDraft', !!e.createPrsAsDraft, 'When ON, Minions-created PRs open as DRAFT (GitHub `gh pr create --draft`; ADO `az repos pr create --draft true`). When OFF (default), PRs open ready-for-review as today. Aggregate/E2E verify PRs already open as draft regardless of this flag.') +
240
- settingsToggle('Auto-complete PRs', 'set-autoCompletePrs', !!e.autoCompletePrs, 'Auto-merge PRs when builds pass and review is approved (opt-in). Independent of the per-cause auto-fix gates above.') +
241
- settingsToggle('Surface repo harnesses in PR body', 'set-includeHarnessSectionInPrBody', e.includeHarnessSectionInPrBody !== false, 'When ON (default), Minions-authored PRs get a fenced "Repo harnesses used" section listing the harness assets in effect for the dispatch\'s runtime + project — runtime, injected CLAUDE.md files, AGENTS.md discovery, MCP servers, and skills. Pure reporting from the same read-only source as `minions doctor --harness`; it does not change harness propagation. OFF suppresses the section.') +
242
- '</div>';
243
-
244
- // P-g7a2b4c5 — new "Workflow Defaults" pane. Owns the four workflow-level
245
- // automation defaults that historically lived under Auto-fix: plan-flow
246
- // (approve / decompose / archive) and memory consolidation. None of these
247
- // gate a PR-triggered dispatch.
248
- const paneWorkflow =
249
- '<h3>Workflow Defaults</h3>' +
250
- '<div class="settings-pane-sub">Workflow-level automation defaults. Independent of PR dispatch and PR lifecycle — these control plan flow and memory upkeep.</div>' +
251
- '<div class="settings-stack">' +
252
- settingsToggle('Auto-approve Plans', 'set-autoApprovePlans', !!e.autoApprovePlans, 'PRDs are approved automatically without human review.') +
253
- settingsToggle('Auto-decompose', 'set-autoDecompose', e.autoDecompose !== false, 'Large implement items are auto-split into sub-tasks.') +
254
- settingsToggle('Auto-archive Plans', 'set-autoArchive', !!e.autoArchive, 'Automatically archive plans after verify completes (off = manual archive via dashboard).') +
255
- settingsToggle('Auto-consolidate Memory', 'set-autoConsolidateMemory', !!e.autoConsolidateMemory, 'Enabled by default. Periodically spawn the KB sweep (dedup + compress + normalize knowledge/) from the engine tick on a 4h cadence. Inbox→notes consolidation runs independently every tick.') +
256
- settingsToggle('Bootstrap Default Schedules', 'set-bootstrapDefaultSchedules', e.bootstrapDefaultSchedules !== false, 'Auto-create the per-project git-fetch-freshness schedule (every 12h) when a project is added, and ensure one shared daily harness-sweep schedule exists engine-wide. Idempotent and non-destructive — never overwrites a schedule you have customized. Turn off to manage these schedules entirely by hand.') +
257
280
  '</div>';
258
281
 
259
282
  // W-mpmwxkrw000872ec — Appearance pane. Hosts dashboard-wide visual
@@ -445,7 +468,7 @@ async function openSettings() {
445
468
  '<option value="draft"' + (createPrsAsDraftRaw === 'draft' ? ' selected' : '') + '>Draft — open PRs as draft</option>' +
446
469
  '<option value="ready"' + (createPrsAsDraftRaw === 'ready' ? ' selected' : '') + '>Not draft — open PRs ready</option>' +
447
470
  '</select>' +
448
- '<div style="font-size:var(--text-xs);color:var(--muted);margin-top:2px;line-height:1.4">Overrides the fleet-wide "Create PRs as draft" setting under PR Lifecycle for THIS project only. Draft = Minions-created PRs open as draft (GitHub `--draft`; ADO `--draft true`). Inherit follows the engine flag. Aggregate/E2E verify PRs and the shared-branch aggregate PR are unaffected.</div>' +
471
+ '<div style="font-size:var(--text-sm);color:var(--muted);margin-top:2px;line-height:1.4">Overrides the fleet-wide "Create PRs as draft" setting under Plan &amp; PR Workflow → PR creation for THIS project only. Draft = Minions-created PRs open as draft (GitHub `--draft`; ADO `--draft true`). Inherit follows the engine flag. Aggregate/E2E verify PRs and the shared-branch aggregate PR are unaffected.</div>' +
449
472
  '</div>';
450
473
  // W-mrchrfe100067d3a — per-project explicit allowlist of secondary repo
451
474
  // scopes this project's checkout is also allowed to track PRs for (e.g.
@@ -510,6 +533,10 @@ async function openSettings() {
510
533
  settingsToggle('ADO Polling', 'set-adoPollEnabled', e.adoPollEnabled !== false, 'Legacy bundle macro — when OFF, silences all three ADO axes (status, comments, reconcile) and ADO PR dispatch gates are inert when this is off. Per-axis flags below take priority when explicitly set. Keep ON unless you want a one-knob ADO kill.') +
511
534
  settingsToggle('GitHub Polling', 'set-ghPollEnabled', e.ghPollEnabled !== false, 'Legacy bundle macro — when OFF, silences all three GitHub axes (status, comments, reconcile) and GitHub PR dispatch gates are inert when this is off. Per-axis flags below take priority when explicitly set. Keep ON unless you want a one-knob GitHub kill.') +
512
535
  '</div>' +
536
+ '<div class="settings-pane-sub" style="margin-bottom:8px">Poll-side actions — these ride on the PR status poll (so they honor <em>Pause ALL polling</em> and the matching provider toggle above), but they write to the platform instead of dispatching a Minions agent. They are therefore <strong>not</strong> covered by the <em>Pause ALL PR auto-fix</em> switch on the Plan &amp; PR Workflow tab, which by design leaves polling and build-status acquisition running.</div>' +
537
+ '<div class="settings-stack" style="margin-bottom:12px">' +
538
+ settingsToggle('Queue Draft PR Builds (ADO)', 'set-adoQueueDraftPrBuilds', e.adoQueueDraftPrBuilds !== false, 'ADO-only: proactively queue the required branch-policy validation builds for DRAFT pull requests. Azure DevOps does NOT auto-run branch build policies while a PR is a draft, so a Minions-managed draft PR never gets a build status and the build-fix loop has nothing to act on. When ON (default), Minions discovers the required Build policies from the target branch and queues a fresh build per definition against refs/pull/<n>/merge (rate-limited to once per PR / 30 min). Queues builds only — it never dispatches an agent. Inert for GitHub PRs (GitHub already fires CI on drafts) and whenever ADO PR status polling is off or paused.', { blockHint: true }) +
539
+ '</div>' +
513
540
  '<details class="settings-collapsible" style="margin-bottom:12px"><summary>Granular per-poller controls (P-c4d8e1a3)</summary>' +
514
541
  '<div class="settings-pane-sub" style="margin-top:8px">Override individual ADO/GitHub poll axes + the pending-rebase processor. Explicit values here win; if unset, the legacy ADO/GitHub Polling macros above apply; otherwise the default is ON. <strong>Status</strong>, <strong>Comments</strong> and <strong>Reconcile</strong> all honor the <em>Pause All Polling</em> kill-switch above (reconcile included since W-ms5eljc60090f0d5 — it issues provider API calls); only <strong>Process Pending Rebases</strong> ignores it (by design — it makes no provider poll call).</div>' +
515
542
  '<div class="settings-stack" style="margin-top:8px">' +
@@ -533,6 +560,7 @@ async function openSettings() {
533
560
  '<div class="settings-grid-2">' +
534
561
  settingsField('PR Status Poll Frequency', 'set-prPollStatusEvery', e.prPollStatusEvery ?? 12, 'ticks', 'Poll PR build/review/merge status every N ticks for both ADO and GitHub (~12 min at default tick rate)') +
535
562
  settingsField('PR Comments Poll Frequency', 'set-prPollCommentsEvery', e.prPollCommentsEvery ?? 12, 'ticks', 'Poll PR human comments every N ticks for both ADO and GitHub (~12 min at default tick rate)') +
563
+ settingsField('Abandoned PR Re-probe Window', 'set-abandonedPrRepollWindowDays', Number((((e.abandonedPrRepollWindowMs ?? (7 * 24 * 60 * 60 * 1000)) / (24 * 60 * 60 * 1000))).toFixed(2)), 'days', 'How long after a PR is stamped `abandoned` the STATUS poll keeps re-probing it, so a PR that was closed and then REOPENED can leave the terminal stamp instead of being invisible to the pollers forever. Only the status poll re-probes (never the comment poll), a host-confirmed deletion closes the window early, and records with no known abandonment time are never re-probed. Set 0 to disable re-probing entirely (strict pre-W-msav5edk00g81710 behavior). Clamped 0–90 days.', '0.01') +
536
564
  '</div>';
537
565
 
538
566
  const paneConcurrency =
@@ -591,10 +619,12 @@ async function openSettings() {
591
619
  '</div>' +
592
620
  '<div class="settings-grid-2">' +
593
621
  settingsField('CC Session Idle Timeout', 'set-ccWorkerIdleTimeoutMs', Math.round((e.ccWorkerIdleTimeoutMs || 1800000) / 60000), 'minutes', 'How long a persistent `copilot --acp` CC worker stays warm with no activity before the idle reaper kills it. After a reap the next message cold-spawns a FRESH session with no memory of prior turns and CC shows a "context cleared after inactivity" notice. Shorter = less idle memory/process footprint; longer = more context durability across gaps between messages. Only affects the Copilot worker-pool path (CC Worker Pool ON). Clamped 1–480 minutes. Ignored when "Never idle-reap CC workers" below is ON.') +
622
+ settingsField('ACP Handshake Timeout', 'set-acpHandshakeTimeoutMs', Math.round((e.acpHandshakeTimeoutMs || 180000) / 1000), 's', 'Budget for a COLD `copilot --acp` worker handshake — the `initialize` call plus the follow-up `session/new` / `session/load`. Applies to both ACP worker pools (Command Center and the per-agent fleet pool). A cold start that loads your MCP configuration routinely needs ~95 s, and the old hardcoded 60 s surfaced as "Command Center returned no output / handshake timed out after 60000ms" (issue #1105). Warm-worker reuse is unaffected — this only bounds the cold startup handshake. On expiry the pool still raises the typed, retriable ACP handshake error. Clamped 10–900 s.', '0.001') +
594
623
  settingsField('Worktree Create Timeout', 'set-worktreeCreateTimeout', (e.worktreeCreateTimeout || 300000) / 1000, 's', 'Timeout for git worktree add (increase for large repos/Windows)', '0.001') +
595
624
  settingsField('Worktree Create Retries', 'set-worktreeCreateRetries', e.worktreeCreateRetries || 1, '', 'Retry count for transient worktree add failures (0-3)') +
596
625
  settingsField('Worktree Root', 'set-worktreeRoot', e.worktreeRoot || '../worktrees', '', 'Relative or absolute path for git worktrees; on Windows prefer a short path like C:\\wt') +
597
626
  settingsField('Minimum Free Space', 'set-worktreeMinFreeSpaceGiB', Number(((e.worktreeMinFreeSpaceBytes ?? (10 * 1024 * 1024 * 1024)) / (1024 * 1024 * 1024)).toFixed(2)), 'GiB', 'Defer worktree-mode dispatch before git worktree add when the target filesystem has less free space than this reserve. The pending item is re-checked every tick and does not consume an agent retry. Set 0 to disable.') +
627
+ settingsField('Protected Quarantine Retention', 'set-worktreeProtectedQuarantineRetentionDays', Number((((e.worktreeProtectedQuarantineRetentionMs ?? (30 * 24 * 60 * 60 * 1000)) / (24 * 60 * 60 * 1000))).toFixed(2)), 'days', 'How long the GC keeps a PROTECTED quarantine directory — one whose dirty WIP could not be snapshotted to a durable refs/minions/quarantine-wip/* ref, so the directory itself is the only recovery artifact. Defaults to the shared 30-day quarantine-REF retention window (engine/core/shared.js#QUARANTINE_REF_RETENTION_MS) and also DRIVES that recovery-REF sweep, so changing this value moves directories and their refs together instead of shortening only one side. These were previously kept forever, which is how a busy install accumulates quarantine residue. Set 0 to restore indefinite retention for both. Clamped 0–365 days.', '0.01') +
598
628
  settingsField('Assert-Clean Status Probe Timeout', 'set-assertCleanStatusTimeoutMs', (e.assertCleanStatusTimeoutMs || 10000) / 1000, 's', 'Timeout for the `git status --porcelain` preflight probe in assertCleanSharedWorktree. Raise this (e.g. 60) on GVFS/Plastic-backed monorepos where sparse hydration runs longer than the 10s default. On timeout the engine quarantines the bad worktree and emits a RETRYABLE failure so the next dispatch starts fresh. Clamped 1–120 s.', '0.001') +
599
629
  settingsField('Restart-health Timeout', 'set-restartHealthTimeoutMs', (e.restartHealthTimeoutMs || 60000) / 1000, 's', 'Cold-start budget for `minions restart` / post-update restart verification. The dashboard runs node:sqlite migrations + SPA HTML assembly + initial state reads synchronously BEFORE it binds its port, so right after `npm install` on a slow/loaded box the PID is alive but the port is unbound for tens of seconds. Below this budget that no longer prints a false "Restart verification failed" + exit(1) — the verifier keeps polling until the port listens. A genuinely crashed spawn (PID dies) still fails fast, well under the full budget. Clamped 15–300 s.', '0.001') +
600
630
  settingsField('Orphan-holder scan timeout', 'set-orphanHolderScanTimeoutMs', (e.orphanHolderScanTimeoutMs || 5000) / 1000, 's', 'Cap on the cross-platform scan (PowerShell / /proc walk / lsof) that identifies the OS process holding a stuck worktree\'s cwd. Bumps up only matter on heavily-loaded hosts where the holder scan races the sweep tick. Clamped 1–30 s.', '0.001') +
@@ -649,7 +679,7 @@ async function openSettings() {
649
679
  '<option value="workspace-write"' + (e.codexCcSandbox === 'workspace-write' ? ' selected' : '') + '>workspace-write</option>' +
650
680
  '<option value="read-only"' + (e.codexCcSandbox === 'read-only' ? ' selected' : '') + '>read-only</option>' +
651
681
  '</select>' +
652
- '<div style="font-size:var(--text-xs);color:var(--muted);margin-top:4px"><strong>Security:</strong> <code>danger-full-access</code> lets Codex CC reach localhost Minions APIs and operator-authorized paths outside <code>~/.minions</code>. Dispatched Codex agents and non-CC calls remain <code>workspace-write</code>. The adapter emits an explicit <code>--sandbox</code> CLI flag, which takes precedence over Codex config-file values.</div>' +
682
+ '<div style="font-size:var(--text-sm);color:var(--muted);margin-top:4px"><strong>Security:</strong> <code>danger-full-access</code> lets Codex CC reach localhost Minions APIs and operator-authorized paths outside <code>~/.minions</code>. Dispatched Codex agents and non-CC calls remain <code>workspace-write</code>. The adapter emits an explicit <code>--sandbox</code> CLI flag, which takes precedence over Codex config-file values.</div>' +
653
683
  '</div>' +
654
684
  '</div>';
655
685
 
@@ -693,13 +723,13 @@ async function openSettings() {
693
723
  const displayFeatures = featuresList.filter(function(f) { return f.id !== 'slim-ux'; });
694
724
  const paneFeatures =
695
725
  '<h3>Feature Flags</h3>' +
696
- '<div class="settings-pane-sub">In-progress UX or behavior gates. Toggles persist immediately. Registry: <code>engine/features.js</code>. Env override: <code>MINIONS_FEATURE_&lt;NAME&gt;=1</code>.</div>' +
726
+ '<div class="settings-pane-sub">In-progress UX or behavior gates. Toggles persist immediately. Registry: <code>engine/core/features.js</code>. Env override: <code>MINIONS_FEATURE_&lt;NAME&gt;=1</code>.</div>' +
697
727
  '<details id="settings-features-details" open style="border:1px solid var(--border);border-radius:6px;padding:12px">' +
698
728
  '<summary style="cursor:pointer;font-size:var(--text-md);color:var(--text);user-select:none;margin-bottom:8px">Show experimental flags ' +
699
729
  '<span style="font-size:var(--text-sm);color:var(--muted)">(' + displayFeatures.length + ' registered)</span>' +
700
730
  '</summary>' +
701
731
  (displayFeatures.length === 0
702
- ? '<div style="font-size:var(--text-base);color:var(--muted);padding:12px;border:1px dashed var(--border);border-radius:4px;text-align:center">No experimental features registered. Add entries to <code>engine/features.js</code> to gate new work.</div>'
732
+ ? '<div style="font-size:var(--text-base);color:var(--muted);padding:12px;border:1px dashed var(--border);border-radius:4px;text-align:center">No experimental features registered. Add entries to <code>engine/core/features.js</code> to gate new work.</div>'
703
733
  : '<div style="display:flex;flex-direction:column;gap:8px">' +
704
734
  displayFeatures.map(function(f) {
705
735
  const checked = f.enabled ? ' checked' : '';
@@ -755,6 +785,15 @@ async function openSettings() {
755
785
  settingsToggle('Allow Temp Agents', 'set-allowTempAgents', !!e.allowTempAgents, 'Spawn ephemeral agents when all permanent agents are busy') +
756
786
  settingsToggle('Disable model discovery', 'set-disableModelDiscovery', !!e.disableModelDiscovery, 'Skip /api/runtimes/<name>/models REST calls fleet-wide. Settings UI falls back to free-text.') +
757
787
  '</div>' +
788
+ // W-ms4o7y1g007jdb2d follow-up — engine-tick upkeep automation defaults moved
789
+ // here out of the Plan & PR Workflow tab (they are engine maintenance, not the
790
+ // per-PR watch→react loop). set-* ids are unchanged so save/collect + search
791
+ // are untouched.
792
+ '<h4>Engine automation</h4>' +
793
+ '<div class="settings-stack">' +
794
+ settingsToggle('Auto-consolidate Memory', 'set-autoConsolidateMemory', !!e.autoConsolidateMemory, 'Enabled by default. Periodically spawn the KB sweep (dedup + compress + normalize knowledge/) from the engine tick on a 4h cadence. Inbox→notes consolidation runs independently every tick.') +
795
+ settingsToggle('Bootstrap Default Schedules', 'set-bootstrapDefaultSchedules', e.bootstrapDefaultSchedules !== false, 'Auto-create the per-project git-fetch-freshness schedule (every 12h) when a project is added, and ensure one shared daily harness-sweep schedule exists engine-wide. Idempotent and non-destructive — never overwrites a schedule you have customized. Turn off to manage these schedules entirely by hand.') +
796
+ '</div>' +
758
797
  // W-mqk2s8q1 — dashboard refresh diagnostics, relocated here from the
759
798
  // floating bottom-left "diag" chip so it sits alongside the other
760
799
  // diagnostic/admin controls. Opens the same ring-buffer modal defined in
@@ -767,10 +806,9 @@ async function openSettings() {
767
806
 
768
807
  // Section registry — order is intentional (Runtime + Auto-fix surface first
769
808
  // per Caleb's feedback). Each entry maps a rail-button id → pane HTML.
770
- // P-g7a2b4c5 — added "PR Lifecycle" and "Workflow Defaults" panes for the
771
- // non-dispatch toggles that historically lived under Auto-fix; placed
772
- // adjacent to Auto-fix so operators following the legacy mental model find
773
- // the moved flags quickly.
809
+ // W-ms4o7y1g007jdb2d — the former 'autofix' / 'lifecycle' / 'workflow' panes
810
+ // are merged into ONE featured 'plan-pr-workflow' section (Plan → PR creation
811
+ // → Monitor). It sits where 'autofix' was (near the rail top, featured).
774
812
  // W-mryz3r7h — a section may opt into a collapsible rail fold by declaring a
775
813
  // `group`. Grouped sections render as an indented child list beneath a fold
776
814
  // header (see RAIL_GROUPS + the railHtml builder below); their PANES stay in
@@ -780,9 +818,7 @@ async function openSettings() {
780
818
  // only ever one thing labeled "Advanced".
781
819
  const sections = [
782
820
  { id: 'runtime', label: 'Agents Runtime & Models', featured: true, html: paneRuntime },
783
- { id: 'autofix', label: 'Auto-fix & Review Loop', featured: true, html: paneAutoFix },
784
- { id: 'lifecycle', label: 'PR Lifecycle', html: paneLifecycle },
785
- { id: 'workflow', label: 'Workflow Defaults', html: paneWorkflow },
821
+ { id: 'plan-pr-workflow', label: 'Plan & PR Workflow', featured: true, html: panePlanPrWorkflow },
786
822
  { id: 'appearance', label: 'Appearance', html: paneAppearance },
787
823
  { id: 'projects', label: 'Projects', html: paneProjects },
788
824
  { id: 'polling', label: 'Polling', html: panePolling },
@@ -807,7 +843,12 @@ async function openSettings() {
807
843
  const SAVED_TAB_KEY = 'minions.settings.activeTab';
808
844
  let activeTab = sections[0].id;
809
845
  try {
810
- const saved = localStorage.getItem(SAVED_TAB_KEY);
846
+ let saved = localStorage.getItem(SAVED_TAB_KEY);
847
+ // W-ms4o7y1g007jdb2d — the 'autofix' / 'lifecycle' / 'workflow' panes merged
848
+ // into 'plan-pr-workflow'. Redirect a stale saved tab (or old deep-link)
849
+ // so no operator lands on a dead rail id and silently falls back to the
850
+ // first section.
851
+ if (saved === 'autofix' || saved === 'lifecycle' || saved === 'workflow') saved = 'plan-pr-workflow';
811
852
  if (saved && sections.some(s => s.id === saved)) activeTab = saved;
812
853
  } catch { /* localStorage may be blocked in some embeds */ }
813
854
 
@@ -1047,7 +1088,79 @@ async function _toggleSettingsCharter(agentId) {
1047
1088
  }
1048
1089
  }
1049
1090
 
1091
+ /**
1092
+ * Populate the Cloud Provider selector from /api/cloud-providers.
1093
+ *
1094
+ * Cloud dispatch ships with an EMPTY provider registry until a provider lands,
1095
+ * and `engine.cloudProvider` is validated server-side against that same
1096
+ * registry — so a free-text input would reject every value an operator could
1097
+ * type. Instead the control offers exactly what is registered, and when nothing
1098
+ * is, it disables itself and says why. The server re-validates fail-closed
1099
+ * regardless; this is an affordance, not the gate.
1100
+ *
1101
+ * A stale stored value (a provider that was registered when it was saved and
1102
+ * is not now) is preserved as a selected option and flagged, so opening
1103
+ * Settings can never silently clear a config value the operator did not touch.
1104
+ */
1105
+ async function _hydrateCloudProviderSelect(engineCfg) {
1106
+ const select = document.getElementById('set-cloudProvider');
1107
+ if (!select) return;
1108
+ const hint = document.getElementById('set-cloudProvider-hint');
1109
+ const current = (engineCfg && engineCfg.cloudProvider) || '';
1110
+
1111
+ let providers = [];
1112
+ let reachable = true;
1113
+ try {
1114
+ const res = await fetch('/api/cloud-providers');
1115
+ const data = await res.json();
1116
+ providers = Array.isArray(data.providers) ? data.providers.map(p => p.name) : [];
1117
+ } catch {
1118
+ reachable = false; // older dashboard or transient failure — degrade, don't clear
1119
+ }
1120
+
1121
+ const stale = current && !providers.includes(current);
1122
+ const options = ['<option value=""' + (!current ? ' selected' : '') + '>None — cloud dispatch disabled</option>']
1123
+ .concat(providers.map(n =>
1124
+ '<option value="' + escHtml(n) + '"' + (n === current ? ' selected' : '') + '>' + escHtml(n) + '</option>'
1125
+ ));
1126
+ if (stale) {
1127
+ options.push(
1128
+ '<option value="' + escHtml(current) + '" selected>' + escHtml(current) + ' (not registered)</option>'
1129
+ );
1130
+ }
1131
+ // eslint-disable-next-line no-unsanitized/property -- reason: structural HTML is a string literal; all user data wrapped in escHtml() (fields: provider names, stored provider id)
1132
+ select.innerHTML = options.join('');
1133
+
1134
+ // Disable only when there is genuinely nothing to choose AND nothing stored:
1135
+ // a stale stored value must stay editable so the operator can clear it.
1136
+ select.disabled = providers.length === 0 && !current;
1137
+
1138
+ if (!hint) return;
1139
+ if (!reachable) {
1140
+ hint.textContent = 'Could not reach /api/cloud-providers — the stored value is unchanged. '
1141
+ + 'Cloud dispatch hands a work item to a remote coding agent that authors its own PR.';
1142
+ } else if (providers.length === 0) {
1143
+ hint.textContent = 'No cloud providers are registered on this build, so cloud dispatch is unavailable '
1144
+ + 'and any work item declaring meta.executionSurface: "cloud" is refused (never run locally instead). '
1145
+ + 'This control activates when a provider ships.';
1146
+ } else if (stale) {
1147
+ hint.textContent = 'The stored provider "' + current + '" is not registered on this build — '
1148
+ + 'cloud-declared work items will be refused until you pick a registered provider or clear this field.';
1149
+ } else {
1150
+ hint.textContent = 'Fleet-wide default for work items declaring meta.executionSurface: "cloud" — Minions '
1151
+ + 'investigates locally, renders the scoped dispatch instructions, and hands them to a REMOTE coding agent '
1152
+ + 'that authors its own PR. Overridden by project.cloudProvider, then meta.cloudProvider. '
1153
+ + 'Only PR-producing types (implement, fix, test, docs) may declare the cloud surface.';
1154
+ }
1155
+ }
1156
+
1050
1157
  async function initRuntimeFleetUI(engineCfg, agentsCfg) {
1158
+ // Cloud dispatch providers come from a DIFFERENT registry than runtimes
1159
+ // (/api/cloud-providers) and ship empty until a provider lands. Hydrated
1160
+ // FIRST, ahead of the runtime-select guard below, so the cloud control is
1161
+ // never left showing "Loading…" just because the CLI selects are absent.
1162
+ await _hydrateCloudProviderSelect(engineCfg);
1163
+
1051
1164
  const cliSelect = document.getElementById('set-defaultCli');
1052
1165
  const ccCliSelect = document.getElementById('set-ccCli');
1053
1166
  if (!cliSelect || !ccCliSelect) return;
@@ -1064,6 +1177,9 @@ async function initRuntimeFleetUI(engineCfg, agentsCfg) {
1064
1177
  runtimes = Array.isArray(d.runtimes) ? d.runtimes : [];
1065
1178
  } catch { /* ignore — we'll surface a free-text-only path below */ }
1066
1179
 
1180
+ // Cloud dispatch providers come from a DIFFERENT registry (/api/cloud-providers)
1181
+ // and ship empty until a provider lands. Hydrated at the top of this function.
1182
+
1067
1183
  // Always include 'copilot' as a fallback option even if /api/runtimes is empty;
1068
1184
  // legacy installs without the registry endpoint should still see something pickable.
1069
1185
  const names = runtimes.length ? runtimes.map(rt => rt.name) : ['copilot'];
@@ -1364,6 +1480,27 @@ function settingsToggle(label, id, checked, hint, opts) {
1364
1480
  '</div>';
1365
1481
  }
1366
1482
 
1483
+ // W-ms4o7y1g007jdb2d — two-state DRAFT ⇄ PUBLISH switch backing a real
1484
+ // `<input type=checkbox id="set-…">` so the existing id-based save/collect path
1485
+ // (getElementById('set-…').checked) is untouched: checked = Draft, unchecked =
1486
+ // Publish (ready-for-review). Renders both ends ('Publish' / 'Draft') with a
1487
+ // pure-CSS sliding knob driven by the `:checked` sibling selector (.settings-
1488
+ // switch* in styles.css) — no JS change handler. `extraSearch` folds the
1489
+ // legacy label into data-search so the cross-tab search box still finds it.
1490
+ function settingsDraftPublishToggle(id, checked, label, hint) {
1491
+ const searchKey = (String(label || '') + ' ' + String(hint || '') + ' create prs as draft publish ready for review pull request lifecycle').toLowerCase().replace(/"/g, '');
1492
+ return '<div class="settings-row" data-search="' + escHtml(searchKey) + '">' +
1493
+ '<label for="' + id + '" style="font-size:var(--text-sm);color:var(--muted);display:block;margin-bottom:4px">' + escHtml(label) + '</label>' +
1494
+ '<label class="settings-switch" title="' + escHtml(hint || '') + '">' +
1495
+ '<input type="checkbox" id="' + id + '" class="settings-switch__input"' + (checked ? ' checked' : '') + '>' +
1496
+ '<span class="settings-switch__end settings-switch__end--publish">Publish</span>' +
1497
+ '<span class="settings-switch__track" aria-hidden="true"><span class="settings-switch__knob"></span></span>' +
1498
+ '<span class="settings-switch__end settings-switch__end--draft">Draft</span>' +
1499
+ '</label>' +
1500
+ (hint ? '<div style="font-size:var(--text-sm);color:var(--muted);margin-top:4px;line-height:1.4">' + escHtml(hint) + '</div>' : '') +
1501
+ '</div>';
1502
+ }
1503
+
1367
1504
  // W-mryyzhug000454b7 — enable/disabled TILE for a boolean per-cause auto-fix
1368
1505
  // gate. Renders a real (visually-hidden but focusable) `<input type=checkbox
1369
1506
  // id="set-...">` as the source of truth so the existing id-based save/collect
@@ -1574,6 +1711,7 @@ async function saveSettings() {
1574
1711
  worktreeCreateRetries: document.getElementById('set-worktreeCreateRetries').value,
1575
1712
  worktreeRoot: document.getElementById('set-worktreeRoot').value,
1576
1713
  worktreeMinFreeSpaceBytes: Math.round(Math.max(0, Number(document.getElementById('set-worktreeMinFreeSpaceGiB').value) || 0) * 1024 * 1024 * 1024),
1714
+ worktreeProtectedQuarantineRetentionMs: Math.round(Math.max(0, Number(document.getElementById('set-worktreeProtectedQuarantineRetentionDays').value) || 0) * 24 * 60 * 60 * 1000),
1577
1715
  assertCleanStatusTimeoutMs: secToMs('set-assertCleanStatusTimeoutMs'),
1578
1716
  restartHealthTimeoutMs: secToMs('set-restartHealthTimeoutMs'),
1579
1717
  idleAlertMinutes: document.getElementById('set-idleAlertMinutes').value,
@@ -1591,11 +1729,13 @@ async function saveSettings() {
1591
1729
  autoDecompose: document.getElementById('set-autoDecompose').checked,
1592
1730
  allowTempAgents: document.getElementById('set-allowTempAgents').checked,
1593
1731
  autoArchive: document.getElementById('set-autoArchive').checked,
1732
+ autoQaCompletedPrds: !!document.getElementById('set-autoQaCompletedPrds')?.checked,
1594
1733
  autoConsolidateMemory: document.getElementById('set-autoConsolidateMemory').checked,
1595
1734
  bootstrapDefaultSchedules: document.getElementById('set-bootstrapDefaultSchedules').checked,
1596
1735
  autoApplyReviewVote: document.getElementById('set-autoApplyReviewVote').checked,
1597
1736
  createPrsAsDraft: document.getElementById('set-createPrsAsDraft').checked,
1598
1737
  autoFixBuilds: document.getElementById('set-autoFixBuilds').checked,
1738
+ adoQueueDraftPrBuilds: document.getElementById('set-adoQueueDraftPrBuilds').checked,
1599
1739
  autoFixConflicts: document.getElementById('set-autoFixConflicts').checked,
1600
1740
  autoFixPaused: document.getElementById('set-autoFixPaused').checked,
1601
1741
  autoReviewPrs: document.getElementById('set-autoReviewPrs').checked,
@@ -1636,6 +1776,9 @@ async function saveSettings() {
1636
1776
  planMaterializationEnabled: document.getElementById('set-planMaterializationEnabled').checked,
1637
1777
  prPollStatusEvery: document.getElementById('set-prPollStatusEvery').value,
1638
1778
  prPollCommentsEvery: document.getElementById('set-prPollCommentsEvery').value,
1779
+ // W-msav5edk00g81710 — UI is days, engine stores ms (mirrors the
1780
+ // protected-quarantine retention field). 0 disables the re-probe.
1781
+ abandonedPrRepollWindowMs: Math.round(Math.max(0, Number(document.getElementById('set-abandonedPrRepollWindowDays').value) || 0) * 24 * 60 * 60 * 1000),
1639
1782
  evalMaxCost: document.getElementById('set-evalMaxCost').value || null,
1640
1783
  enablePreDispatchEval: document.getElementById('set-enablePreDispatchEval').checked,
1641
1784
  preDispatchEvalSkipPrdSourced: document.getElementById('set-preDispatchEvalSkipPrdSourced').checked,
@@ -1651,8 +1794,16 @@ async function saveSettings() {
1651
1794
  ccCli: (document.getElementById('set-ccCli')?.value ?? '').trim(),
1652
1795
  ccModel: (document.getElementById('set-ccModel')?.value ?? '').trim(),
1653
1796
  ccEffort: document.getElementById('set-ccEffort').value || null,
1797
+ // Cloud dispatch default provider. Empty string clears the override, so
1798
+ // the fleet goes back to "no cloud provider" and every cloud-declared
1799
+ // work item is refused (never silently downgraded to a local dispatch).
1800
+ cloudProvider: (document.getElementById('set-cloudProvider')?.value ?? '').trim(),
1654
1801
  codexCcSandbox: document.getElementById('set-codexCcSandbox')?.value || 'danger-full-access',
1655
1802
  ccTurnTimeoutMs: secToMs('set-ccTurnTimeoutMs'),
1803
+ // Issue #1105 — cold ACP handshake budget. UI is seconds, engine stores
1804
+ // ms; the server validates against [10000, 900000] ms. undefined when
1805
+ // blank so the server keeps the prior value.
1806
+ acpHandshakeTimeoutMs: secToMs('set-acpHandshakeTimeoutMs'),
1656
1807
  // Settings UI presents the idle timeout in minutes for readability; the
1657
1808
  // engine stores ms (ENGINE_DEFAULTS.ccWorkerIdleTimeoutMs). Convert here;
1658
1809
  // the server validates against [60000, 28800000] ms. undefined when blank so the
@@ -902,6 +902,56 @@ async function submitBugReport() {
902
902
  }
903
903
  }
904
904
 
905
+ // ── Slim "+ Add" create deep-link (W-ms258yw0000ffd8a) ─────────────────────
906
+ // Shared boot hook for the classic /watches, /schedule and /pipelines screens
907
+ // when the Slim Automation tile embeds them at `?embed=1#create`.
908
+ //
909
+ // Waiting only for `typeof openCreate*Modal === 'function'` is NOT sufficient:
910
+ // the create function is a hoisted declaration in the same concatenated bundle,
911
+ // so it already exists synchronously — but the Project / Agent / Owner <option>
912
+ // lists are built from the `cmdProjects` / `cmdAgents` globals that refresh.js
913
+ // only fills once the first /api/status + /api/agents fetch resolves. This hook
914
+ // runs one macrotask after DOMContentLoaded and always wins that race, and the
915
+ // create forms are one-shot innerHTML renders with no re-render hook — so the
916
+ // dialog would open with empty dropdowns and never self-heal. Screens whose
917
+ // create form depends on that data therefore pass an `isReady` predicate.
918
+ //
919
+ // The retry budget is bounded so a page whose data never lands cannot spin the
920
+ // 30 ms timer forever; on exhaustion we open best-effort (a degraded dialog
921
+ // still beats a "+ Add" click that silently does nothing).
922
+ function classicCreateFormDataReady() {
923
+ // The first refresh() tick has landed for BOTH slices cmdProjects/cmdAgents
924
+ // are derived from. Deliberately not `cmdProjects.length` — a valid install
925
+ // can have zero projects or zero agents, and that must not stall the dialog.
926
+ return !!(window._lastStatus && window._lastAgents);
927
+ }
928
+
929
+ // opts: { route: '/watches', resolve: () => fn|null, isReady?: () => boolean }
930
+ function bootCreateDeepLink(opts) {
931
+ if (typeof window === 'undefined' || typeof document === 'undefined') return;
932
+ if (!opts || typeof opts.resolve !== 'function') return;
933
+ var RETRY_MS = 30;
934
+ var MAX_ATTEMPTS = 200; // ~6s at RETRY_MS — bounded, never an unbounded loop
935
+ try {
936
+ if (window.location.pathname.replace(/\/+$/, '') !== opts.route) return;
937
+ if (window.location.hash !== '#create') return;
938
+ } catch (e) { return; }
939
+ function attempt(n) {
940
+ var open = null;
941
+ try { open = opts.resolve(); } catch (e) { open = null; }
942
+ var ready = true;
943
+ if (opts.isReady) { try { ready = opts.isReady() === true; } catch (e) { ready = false; } }
944
+ if (typeof open === 'function' && ready) { open(); return; }
945
+ if (n >= MAX_ATTEMPTS) { if (typeof open === 'function') open(); return; }
946
+ setTimeout(function() { attempt(n + 1); }, RETRY_MS);
947
+ }
948
+ if (document.readyState === 'loading') {
949
+ document.addEventListener('DOMContentLoaded', function() { setTimeout(function() { attempt(0); }, 0); });
950
+ } else {
951
+ setTimeout(function() { attempt(0); }, 0);
952
+ }
953
+ }
954
+
905
955
  // ── Send-shortcut helpers (Classic UX) ──────────────────────────────────
906
956
  // macOS submits with Command+Enter; Windows/Linux keep Control+Enter. These are
907
957
  // the single source of truth for both the key handler and the visible hint so
@@ -930,4 +980,4 @@ function sendShortcutAriaLabel() { return isMacPlatform() ? 'Command+Enter' : 'C
930
980
  // aria-keyshortcuts uses the DOM modifier names, so macOS reports "Meta".
931
981
  function sendShortcutAriaKeys() { return isMacPlatform() ? 'Meta+Enter' : 'Control+Enter'; }
932
982
 
933
- window.MinionsUtils = { wakeEngine, markDeleted, clearDeleted, escapeHtml, escHtml, renderMd, normalizePlanFile, timeAgo, statusColor, shouldIgnoreSelectionClick, llmCopyBtn, copyLlmText, openBugReport, submitBugReport, isMacPlatform, isSendShortcutEvent, sendShortcutLabel, sendShortcutAriaLabel, sendShortcutAriaKeys };
983
+ window.MinionsUtils = { wakeEngine, markDeleted, clearDeleted, escapeHtml, escHtml, renderMd, normalizePlanFile, timeAgo, statusColor, shouldIgnoreSelectionClick, llmCopyBtn, copyLlmText, openBugReport, submitBugReport, isMacPlatform, isSendShortcutEvent, sendShortcutLabel, sendShortcutAriaLabel, sendShortcutAriaKeys, bootCreateDeepLink, classicCreateFormDataReady };
@@ -16,7 +16,7 @@
16
16
  </div>
17
17
  </section>
18
18
  <section>
19
- <h2>Minions Members <span class="qa-section-subtitle">click for details</span></h2>
19
+ <h2>Minions Members</h2>
20
20
  <div class="agents" id="agents-grid">Loading...</div>
21
21
  </section>
22
22
  <section>
@@ -1,24 +1,9 @@
1
1
  <section>
2
2
  <h2>QA</h2>
3
- <p class="empty" style="margin:4px 0 12px 0">Passive monitor for QA sessions, live targets, runs, and saved runbooks. Create new sessions or runbooks from <button type="button" class="qa-inline-link" onclick="qaFocusCommandCenter()">Command Center</button>.</p>
3
+ <p class="empty" style="margin:4px 0 12px 0">Passive monitor for QA sessions, live targets, runs, and saved runbooks. Create new sessions or runbooks from <button type="button" class="qa-inline-link" onclick="qaFocusCommandCenter()">Command Center</button>. <a href="/state/docs/qa-runbook-lifecycle.md" target="_blank" rel="noopener" class="qa-inline-link">Docs →</a></p>
4
4
  </section>
5
5
  <section id="qa-sessions-section" class="qa-section">
6
6
  <h2>Active QA Sessions <span class="qa-section-subtitle">live, non-terminal runs — engine sets up a target, drafts a runner-native test, and (with your approval) executes it</span></h2>
7
- <div id="qa-cc-pitch" class="qa-cc-pitch" style="border:1px solid var(--border);border-radius:var(--radius-md);background:var(--surface2);padding:var(--space-5);margin-bottom:var(--space-5);display:flex;flex-direction:column;gap:var(--space-3)">
8
- <div style="font-weight:600;color:var(--text);display:flex;align-items:center;gap:var(--space-2)">💬 Ask Command Center to start a QA session</div>
9
- <div style="color:var(--muted);font-size:var(--text-base);line-height:1.5">
10
- Examples:
11
- <ul style="margin:var(--space-2) 0 0 var(--space-5);padding:0;color:var(--muted)">
12
- <li><code>QA the login flow on PR #1234</code></li>
13
- <li><code>Smoke test the checkout flow on develop</code></li>
14
- <li><code>Validate the signup journey on my current worktree</code></li>
15
- </ul>
16
- </div>
17
- <div style="display:flex;gap:var(--space-3);align-items:center;margin-top:var(--space-2)">
18
- <button type="button" id="qa-cc-pitch-open" class="btn-primary-solid" onclick="qaFocusCommandCenter()">Open Command Center →</button>
19
- <a href="/state/docs/qa-runbook-lifecycle.md" target="_blank" rel="noopener" class="qa-cc-pitch-docs" style="color:var(--blue);font-size:var(--text-base)">Docs →</a>
20
- </div>
21
- </div>
22
7
  <div id="qa-sessions-content" class="qa-sessions-list">
23
8
  <p class="empty">Loading sessions…</p>
24
9
  </div>
@@ -4,6 +4,46 @@
4
4
  <button id="work-archive-toggle" type="button" class="pr-pager-btn" style="font-size:var(--text-sm);padding:2px 8px;margin-left:4px" onclick="toggleWorkItemArchive()" aria-expanded="false" aria-controls="work-items-archive">See Archive</button>
5
5
  <span style="font-size:var(--text-sm);color:var(--muted);font-weight:400;text-transform:none;letter-spacing:0">tasks dispatched to agents — auto-created from PRDs or added manually</span>
6
6
  </h2>
7
+ <div class="pr-filters" role="group" aria-label="Work item filters and sort">
8
+ <label class="pr-filter">Status
9
+ <select id="wi-filter-status" onchange="applyWiFilters()">
10
+ <option value="">All statuses</option>
11
+ </select>
12
+ </label>
13
+ <label class="pr-filter">Type
14
+ <select id="wi-filter-type" onchange="applyWiFilters()">
15
+ <option value="">All types</option>
16
+ </select>
17
+ </label>
18
+ <label class="pr-filter">Project
19
+ <select id="wi-filter-project" onchange="applyWiFilters()">
20
+ <option value="">All projects</option>
21
+ </select>
22
+ </label>
23
+ <label class="pr-filter">Agent
24
+ <select id="wi-filter-agent" onchange="applyWiFilters()">
25
+ <option value="">All agents</option>
26
+ </select>
27
+ </label>
28
+ <label class="pr-filter">Priority
29
+ <select id="wi-filter-priority" onchange="applyWiFilters()">
30
+ <option value="">All priorities</option>
31
+ </select>
32
+ </label>
33
+ <label class="pr-filter">Sort by
34
+ <select id="wi-sort-by" onchange="applyWiFilters()">
35
+ <option value="lifecycle" selected>Lifecycle (active first)</option>
36
+ <option value="updated">Last updated</option>
37
+ <option value="created">Created</option>
38
+ <option value="priority">Priority</option>
39
+ <option value="status">Status</option>
40
+ <option value="type">Type</option>
41
+ </select>
42
+ </label>
43
+ <button id="wi-sort-dir" class="pr-filter-reset" type="button" value="desc" title="Descending — click for ascending" aria-label="Sort descending (newest first)" onclick="toggleWiSortDir()">&#8595; Desc</button>
44
+ <button class="pr-filter-reset" type="button" onclick="resetWiFilters()">Reset filters</button>
45
+ <span class="pr-filter-summary" id="wi-filter-summary" aria-live="polite"></span>
46
+ </div>
7
47
  <div id="work-items-content"><p class="empty">No work items yet.</p></div>
8
48
  <div id="work-items-archive" style="display:none;margin-top:12px"></div>
9
49
  </section>