@yemi33/minions 0.1.2448 → 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 +58 -4
  7. package/dashboard/js/detail-panel.js +36 -0
  8. package/dashboard/js/memory-panel.js +59 -12
  9. package/dashboard/js/qa.js +179 -20
  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 +13 -2
  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
@@ -1,14 +1,24 @@
1
1
  /**
2
- * engine/abandoned-pr-reconciliation.js — One-shot startup reconciliation pass
2
+ * engine/providers/abandoned-pr-reconciliation.js — One-shot startup reconciliation pass
3
3
  * for `abandoned` PRs. Pairs with W-mp5trwh60008386d's forward-looking 404
4
- * hardening in engine/github.js: that fix prevents *future* false-flips, but
4
+ * hardening in engine/providers/github.js: that fix prevents *future* false-flips, but
5
5
  * does nothing for PRs already wrongly marked `abandoned` before it shipped
6
6
  * (e.g. the 16 PRs hit by the 2026-05-14 gh-auth-flip incident).
7
7
  *
8
- * `pollPrStatus` only iterates PRs in `PR_POLLABLE_STATUSES = {active, linked}`
9
- * (engine/shared.js), so once a PR is `abandoned` the regular poll loop never
8
+ * `pollPrStatus` only iterates `PR_POLLABLE_STATUSES = {active, linked}`
9
+ * (engine/core/shared.js), so once a PR is `abandoned` the regular poll loop never
10
10
  * visits it again. This module is the explicit one-time re-probe.
11
11
  *
12
+ * ## Scope after W-msav5edk00g81710
13
+ *
14
+ * That WI gave abandoned records a BOUNDED re-probe window in the normal status
15
+ * poll (`shared.isPrPollable` + `engine.abandonedPrRepollWindowMs`, default 7
16
+ * days), because `abandoned` is the one terminal status a PR can legitimately
17
+ * leave — a reopened PR was otherwise invisible to the pollers forever. That
18
+ * covers the recent case. This module still owns everything OUTSIDE the window:
19
+ * historical false-flips and records with no usable abandonment anchor, which
20
+ * the poller deliberately fails CLOSED on.
21
+ *
12
22
  * ## Why one-shot, not periodic?
13
23
  *
14
24
  * Periodic re-probing of terminal-status PRs has real costs:
@@ -33,11 +43,11 @@
33
43
  *
34
44
  * ## Boot wiring
35
45
  *
36
- * Called from engine/cli.js between the recovery sweep and the initial tick.
46
+ * Called from engine/operations/cli.js between the recovery sweep and the initial tick.
37
47
  * Errors here must NOT block boot — the catch in cli.js logs and continues.
38
48
  */
39
49
 
40
- const shared = require('./shared');
50
+ const shared = require('../core/shared');
41
51
 
42
52
  const { ENGINE_DEFAULTS, log, mutateEngineState, readEngineState } = shared;
43
53
 
@@ -70,7 +80,7 @@ async function runStartupReconciliation(config, options = {}) {
70
80
  }
71
81
 
72
82
  const github = options.githubModule || require('./github');
73
- const ado = options.adoModule || require('./ado');
83
+ const ado = options.adoModule || require('../ado');
74
84
 
75
85
  // Pre-count abandoned PRs (only those without the confirmed-404 marker) so
76
86
  // the start-of-pass log line is meaningful. Wrapped in try/catch — the
@@ -1,12 +1,12 @@
1
1
  /**
2
- * engine/comment-classifier.js — P-f23classifier (F2 + F3)
2
+ * engine/providers/comment-classifier.js — P-f23classifier (F2 + F3)
3
3
  *
4
- * Host-agnostic PR-comment body classifiers shared by engine/github.js and
5
- * engine/ado.js pollers. Three pure body-only predicates:
4
+ * Host-agnostic PR-comment body classifiers shared by engine/providers/github.js and
5
+ * engine/ado/index.js pollers. Three pure body-only predicates:
6
6
  *
7
7
  * isPreviewStatusBody(body) — Firebase / Appetize / Deploy / Preview bot
8
8
  * signatures + Coverage/Build/Test/Deploy/Pipeline report headings +
9
- * shields.io-style badge images. Ported from engine/github.js
9
+ * shields.io-style badge images. Ported from engine/providers/github.js
10
10
  * `_isPreviewStatusComment` body checks. ADO has no `user.type === 'bot'`
11
11
  * equivalent, so only the narrower `_isPreviewStatusComment` helper keeps
12
12
  * a GH-side bot-author gate; both pollers consume this body match directly.
@@ -35,24 +35,42 @@
35
35
  * so a copied marker cannot suppress actionable feedback.
36
36
  *
37
37
  * IMPORTANT — author identity is intentionally NOT consulted by the three
38
- * body-only predicates above. Callers MUST combine Minions marker matches with
39
- * their own identity gates before treating a comment as agent-authored:
38
+ * body-only predicates above. Callers resolve marker authorship through
39
+ * `classifyMarkerAuthorship()`, which combines `hasMinionsMarker()` with the
40
+ * caller's own host-specific identity signal:
40
41
  *
41
- * GitHub: `comment.viewerDidAuthor === true`, backfilled via `gh api user`
42
- * by engine/github.js `_backfillViewerDidAuthor`. The combined
43
- * gate (`viewerDidAuthor && hasMinionsMarker`) is what makes the
44
- * marker non-spoofable on GH.
42
+ * GitHub: `comment.viewerDidAuthor`, backfilled via `gh api user` by
43
+ * engine/providers/github.js `_backfillViewerDidAuthor`. Available
44
+ * when the field is a strict boolean.
45
45
  *
46
46
  * ADO: `comment.author.uniqueName` (or displayName / id / descriptor)
47
47
  * matched case-insensitively against
48
- * `config.engine.minionsAdoIdentities`. ADO has no
49
- * `viewerDidAuthor` equivalent, so a human pasting our marker
50
- * would otherwise be misclassified as agent-authored. The combined
51
- * gate (`isMinionsAdoAuthor && hasMinionsMarker`) is the
52
- * structural complement that prevents marker spoofing.
48
+ * `config.engine.minionsAdoIdentities`. ADO has no `viewerDidAuthor`
49
+ * equivalent. Available only when that allowlist is non-empty.
53
50
  *
54
- * Marker-presence ALONE never classifies a comment as agent on either
55
- * platform — that contract is enforced at the caller, not here.
51
+ * W-msbf1kf6033f46cb — the identity gate distinguishes "identity says this is
52
+ * someone else" from "there is no identity signal at all", and those two must
53
+ * NOT collapse to the same answer:
54
+ *
55
+ * - MISMATCH → not agent-authored. This is the spoof guard at full
56
+ * strength: a human who pasted our marker keeps their
57
+ * feedback actionable.
58
+ * - UNAVAILABLE → agent-authored (fail-SAFE). Previously this collapsed
59
+ * into "not agent-authored", which on ADO (where the
60
+ * allowlist is empty by default) gave the marker ZERO
61
+ * filtering power: every agent fix-summary was reclassified
62
+ * as new human feedback and re-triggered a fix dispatch on
63
+ * the same PR. Observed on ADO PR 5530936 as ten
64
+ * consecutive zero-change dispatches.
65
+ *
66
+ * The fail-safe direction is chosen on cost asymmetry. Failing open produced
67
+ * an unbounded, self-sustaining dispatch loop that burns agent capacity and
68
+ * is triggered by the engine's own traffic. Failing safe can at worst drop a
69
+ * human comment whose FIRST line is a verbatim copy of our HTML marker — a
70
+ * body shape `hasMinionsMarker` already restricts hard (own first line, no
71
+ * blockquote, no trailing content) and that carries no independent feedback
72
+ * signal. Callers that fail safe should say so out loud (see the ADO
73
+ * poller's one-time spoof-guard warning).
56
74
  */
57
75
 
58
76
  const { MINIONS_COMMENT_MARKER_RE } = require('./gh-comment');
@@ -210,10 +228,60 @@ function hasVerdictPrefix(body) {
210
228
  return _VERDICT_PREFIX_RE.test(String(body || ''));
211
229
  }
212
230
 
231
+ // ── Marker authorship (marker + host identity signal) ──────────────────────
232
+ //
233
+ // W-msbf1kf6033f46cb — the single seam both pollers use to answer "did a
234
+ // Minions agent author this comment?". Kept here rather than duplicated in
235
+ // each poller so the ADO and GitHub answers cannot drift (see the
236
+ // mirror-ado-github-poll-fix convention).
237
+ const MARKER_AUTHORSHIP = Object.freeze({
238
+ // No engine-owned marker on the body's own first line.
239
+ NOT_MARKED: 'not-marked',
240
+ // Marker + the host identity signal confirms our own identity.
241
+ IDENTITY_CONFIRMED: 'identity-confirmed',
242
+ // Marker + the host identity signal names someone else → spoof guard.
243
+ IDENTITY_MISMATCH: 'identity-mismatch',
244
+ // Marker + no usable identity signal → fail-safe, trust the marker.
245
+ IDENTITY_UNAVAILABLE: 'identity-unavailable',
246
+ });
247
+
248
+ const _MARKER_AUTHORED_STATES = new Set([
249
+ MARKER_AUTHORSHIP.IDENTITY_CONFIRMED,
250
+ MARKER_AUTHORSHIP.IDENTITY_UNAVAILABLE,
251
+ ]);
252
+
253
+ /**
254
+ * Resolve marker authorship for one comment.
255
+ *
256
+ * @param {object} opts
257
+ * @param {string} opts.body Raw comment body.
258
+ * @param {boolean} opts.identityAvailable Whether the caller has a usable
259
+ * identity signal at all (GH: viewerDidAuthor is a strict boolean;
260
+ * ADO: config.engine.minionsAdoIdentities is non-empty).
261
+ * @param {boolean} opts.identityMatches Whether that signal names us.
262
+ * Ignored when `identityAvailable` is false.
263
+ * @returns {string} one of MARKER_AUTHORSHIP.
264
+ */
265
+ function classifyMarkerAuthorship({ body, identityAvailable, identityMatches } = {}) {
266
+ if (!hasMinionsMarker(body)) return MARKER_AUTHORSHIP.NOT_MARKED;
267
+ if (identityAvailable !== true) return MARKER_AUTHORSHIP.IDENTITY_UNAVAILABLE;
268
+ return identityMatches === true
269
+ ? MARKER_AUTHORSHIP.IDENTITY_CONFIRMED
270
+ : MARKER_AUTHORSHIP.IDENTITY_MISMATCH;
271
+ }
272
+
273
+ /** True only for the two states that mean "a Minions agent wrote this". */
274
+ function isMarkerAuthored(state) {
275
+ return _MARKER_AUTHORED_STATES.has(state);
276
+ }
277
+
213
278
  module.exports = {
214
279
  isPreviewStatusBody,
215
280
  hasMinionsMarker,
216
281
  hasVerdictPrefix,
282
+ classifyMarkerAuthorship,
283
+ isMarkerAuthored,
284
+ MARKER_AUTHORSHIP,
217
285
  classifyNonActionableAutomationComment,
218
286
  NON_ACTIONABLE_AUTOMATION_REASONS,
219
287
  };
@@ -1,5 +1,5 @@
1
1
  /**
2
- * engine/comment-format.js — platform-neutral PR-comment body fragments.
2
+ * engine/providers/comment-format.js — platform-neutral PR-comment body fragments.
3
3
  *
4
4
  * Owns the platform-neutral Minions marker and brand-link formatting shared by
5
5
  * GitHub and Azure DevOps comment transports.
@@ -41,8 +41,8 @@ function linkifyBrandTrailer(text) {
41
41
  // ── Minions marker + neutral comment-body builder ───────────────────────────
42
42
  //
43
43
  // The hidden HTML-comment marker and the body composition (marker + harness
44
- // brand link) are platform-NEUTRAL: GitHub (engine/gh-comment.js) and
45
- // Azure DevOps (engine/ado-comment.js) both post the byte-identical body built
44
+ // brand link) are platform-NEUTRAL: GitHub (engine/providers/gh-comment.js) and
45
+ // Azure DevOps (engine/ado/comment.js) both post the byte-identical body built
46
46
  // here, so there is one builder and no drift. The host modules own only the
47
47
  // transport (gh CLI vs ADO REST) and host-specific validation (repo slug vs
48
48
  // org/project/repoId).
@@ -57,7 +57,7 @@ function linkifyBrandTrailer(text) {
57
57
  // - model concrete runtime execution model (optional)
58
58
  // - no field may contain `--` (would close the HTML comment early)
59
59
 
60
- const { normalizeExecutionModel, UNKNOWN_MODEL } = require('./execution-model');
60
+ const { normalizeExecutionModel, UNKNOWN_MODEL } = require('../core/execution-model');
61
61
 
62
62
  const AGENT_ID_RE = /^[a-z][a-z0-9-]{0,30}$/;
63
63
  const KIND_RE = /^[a-z][a-z0-9-]{0,30}$/;
@@ -218,7 +218,7 @@ module.exports = {
218
218
  linkifyBrandTrailer,
219
219
  stampExecutionModel,
220
220
  MINIONS_BRAND_URL,
221
- // Neutral marker + body builder (consumed by gh-comment.js and ado-comment.js).
221
+ // Neutral marker + body builder (consumed by gh-comment.js and engine/ado/comment.js).
222
222
  buildMinionsCommentBody,
223
223
  parseMinionsMarker,
224
224
  MINIONS_COMMENT_MARKER,
@@ -1,6 +1,6 @@
1
1
  /**
2
- * engine/gh-comment.js — wraps `gh pr comment` / `gh pr review` with a hidden
3
- * HTML-comment marker so downstream classifiers (engine/github.js) can identify
2
+ * engine/providers/gh-comment.js — wraps `gh pr comment` / `gh pr review` with a hidden
3
+ * HTML-comment marker so downstream classifiers (engine/providers/github.js) can identify
4
4
  * minions-authored PR comments by structure rather than by body shape.
5
5
  *
6
6
  * Marker format (single line, ASCII):
@@ -28,7 +28,7 @@
28
28
  * backticks, or `$(…)`. Temp files are cleaned up in `finally`.
29
29
  *
30
30
  * Per-slug PAT routing (P-bfa2a): each public function resolves a per-slug
31
- * GitHub token via `engine/gh-token.js#resolveTokenForSlug(repo)` and threads
31
+ * GitHub token via `engine/providers/gh-token.js#resolveTokenForSlug(repo)` and threads
32
32
  * it into the spawned `gh` process via `env.GH_TOKEN`. When the slug has no
33
33
  * mapping in `engine.ghAccounts`, we fall back to the ambient `gh` identity
34
34
  * (inherited via `process.env`) and emit a one-shot `console.warn` per slug
@@ -46,15 +46,15 @@ const {
46
46
  linkifyBrandTrailer,
47
47
  MINIONS_BRAND_URL,
48
48
  // Neutral marker + body builder now lives in comment-format.js so the GitHub
49
- // (this module) and ADO (engine/ado-comment.js) posters share one builder.
50
- buildMinionsCommentBody, // re-export of engine/comment-format.js (neutral builder)
51
- parseMinionsMarker, // re-export of engine/comment-format.js
52
- MINIONS_COMMENT_MARKER, // re-export of engine/comment-format.js
53
- MINIONS_COMMENT_MARKER_RE, // re-export of engine/comment-format.js
54
- AGENT_ID_RE, // re-export of engine/comment-format.js
55
- KIND_RE, // re-export of engine/comment-format.js
56
- WORK_ITEM_ID_RE, // re-export of engine/comment-format.js
57
- _buildMarker, // re-export of engine/comment-format.js
49
+ // (this module) and ADO (engine/ado/comment.js) posters share one builder.
50
+ buildMinionsCommentBody, // re-export of engine/providers/comment-format.js (neutral builder)
51
+ parseMinionsMarker, // re-export of engine/providers/comment-format.js
52
+ MINIONS_COMMENT_MARKER, // re-export of engine/providers/comment-format.js
53
+ MINIONS_COMMENT_MARKER_RE, // re-export of engine/providers/comment-format.js
54
+ AGENT_ID_RE, // re-export of engine/providers/comment-format.js
55
+ KIND_RE, // re-export of engine/providers/comment-format.js
56
+ WORK_ITEM_ID_RE, // re-export of engine/providers/comment-format.js
57
+ _buildMarker, // re-export of engine/providers/comment-format.js
58
58
  } = require('./comment-format');
59
59
 
60
60
  // Module-level dedupe set so the "no token mapping for <slug>" warning fires
@@ -65,7 +65,7 @@ const _warnedUnresolvedSlugs = new Set();
65
65
 
66
66
  // ── Validation (GitHub-specific) ─────────────────────────────────────────────
67
67
  // The marker field validators + neutral body builder now live in
68
- // engine/comment-format.js (shared with engine/ado-comment.js). Only the
68
+ // engine/providers/comment-format.js (shared with engine/ado/comment.js). Only the
69
69
  // GitHub repo-slug + PR-number validators remain host-specific here.
70
70
 
71
71
  const REPO_SLUG_RE = /^[A-Za-z0-9._-]+\/[A-Za-z0-9._-]+$/;
@@ -210,8 +210,8 @@ function postPrReviewComment({
210
210
  module.exports = {
211
211
  // Builders / parsers (pure functions — usable from anywhere)
212
212
  buildMinionsCommentBody,
213
- linkifyBrandTrailer, // re-export of engine/comment-format.js for comment callers
214
- MINIONS_BRAND_URL, // re-export of engine/comment-format.js for comment callers
213
+ linkifyBrandTrailer, // re-export of engine/providers/comment-format.js for comment callers
214
+ MINIONS_BRAND_URL, // re-export of engine/providers/comment-format.js for comment callers
215
215
  parseMinionsMarker,
216
216
  MINIONS_COMMENT_MARKER,
217
217
  MINIONS_COMMENT_MARKER_RE,
@@ -1,5 +1,5 @@
1
1
  /**
2
- * engine/gh-token.js — Per-slug GitHub PAT resolution.
2
+ * engine/providers/gh-token.js — Per-slug GitHub PAT resolution.
3
3
  *
4
4
  * Resolves the PAT for a given owner/repo slug by consulting
5
5
  * `config.engine.ghAccounts` and shelling out to `gh auth token --user <account>
@@ -13,14 +13,14 @@
13
13
  * 4. null → caller falls back to the ambient `gh` identity, preserving
14
14
  * legacy behavior for unmapped slugs.
15
15
  *
16
- * Mirrors the shape of `engine/ado-token.js` (separate sync token acquisition
16
+ * Mirrors the shape of `engine/ado/token.js` (separate sync token acquisition
17
17
  * helper, in-memory cache, narrow exec timeout). Tests can prime the cache
18
18
  * via `_setTokenForTest(slug, token)` and clear it via `_clearTokenCache()`.
19
19
  */
20
20
 
21
21
  const { execFileSync } = require('child_process');
22
22
  const path = require('path');
23
- const shared = require('./shared');
23
+ const shared = require('../core/shared');
24
24
  const { safeJsonObj, MINIONS_DIR, log } = shared;
25
25
 
26
26
  const TOKEN_TTL_MS = 30 * 60 * 1000; // 30 minutes
@@ -112,7 +112,7 @@ function resolveTokenForSlug(slug, opts = {}) {
112
112
  /**
113
113
  * Resolve a GitHub PAT for a named account directly (bypassing slug→account
114
114
  * mapping). Used by the projectless PR resolver's unknown-slug fallback
115
- * (engine/pr-resolve.js) to try each authed account in turn WITHOUT touching
115
+ * (engine/providers/pr-resolve.js) to try each authed account in turn WITHOUT touching
116
116
  * the global active `gh auth` profile. Returns the token string or null.
117
117
  */
118
118
  function tokenForAccount(account, opts = {}) {