mandrel 2.59.0 → 2.61.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (678) hide show
  1. package/.agents/README.md +21 -19
  2. package/.agents/agents/acceptance-critic.md +24 -43
  3. package/.agents/agents/story-worker.md +20 -21
  4. package/.agents/docs/agentrc-reference.json +5 -84
  5. package/.agents/docs/configuration.md +48 -132
  6. package/.agents/docs/workflows.md +2 -2
  7. package/.agents/instructions.md +5 -8
  8. package/.agents/rules/ci-remediation.md +41 -8
  9. package/.agents/rules/gherkin-standards.md +4 -0
  10. package/.agents/schemas/acceptance-eval-verdict.schema.json +1 -1
  11. package/.agents/schemas/agentrc.schema.json +16 -496
  12. package/.agents/schemas/story-deliver-terminal.schema.json +13 -3
  13. package/.agents/schemas/validation-evidence.schema.json +2 -1
  14. package/.agents/scripts/README.md +23 -13
  15. package/.agents/scripts/acceptance-eval.js +35 -169
  16. package/.agents/scripts/agents-bootstrap-github.js +27 -129
  17. package/.agents/scripts/apply-quality-bootstrap.js +4 -40
  18. package/.agents/scripts/audit-baselines.js +6 -27
  19. package/.agents/scripts/audit-labels-bootstrap.js +5 -29
  20. package/.agents/scripts/audit-to-stories.js +91 -361
  21. package/.agents/scripts/boot-sweep.js +19 -72
  22. package/.agents/scripts/bootstrap.js +74 -395
  23. package/.agents/scripts/ceremony-derive.js +10 -41
  24. package/.agents/scripts/check-arch-cycles.js +14 -64
  25. package/.agents/scripts/check-baselines.js +8 -48
  26. package/.agents/scripts/check-context-budget.js +105 -311
  27. package/.agents/scripts/check-cyclomatic.js +12 -44
  28. package/.agents/scripts/check-dead-exports.js +13 -64
  29. package/.agents/scripts/check-doc-links.js +38 -186
  30. package/.agents/scripts/check-gherkin-corpus.js +24 -127
  31. package/.agents/scripts/check-test-temp-hygiene.js +28 -169
  32. package/.agents/scripts/coverage-capture.js +34 -98
  33. package/.agents/scripts/deliver-light.js +25 -171
  34. package/.agents/scripts/deliver-recover.js +6 -42
  35. package/.agents/scripts/deliver-run.js +534 -0
  36. package/.agents/scripts/diagnose-friction.js +24 -116
  37. package/.agents/scripts/drain-pending-cleanup.js +1 -1
  38. package/.agents/scripts/evidence-gate.js +23 -85
  39. package/.agents/scripts/file-ci-gap.js +57 -49
  40. package/.agents/scripts/generate-config-docs.js +30 -170
  41. package/.agents/scripts/generate-lens-checklists.js +8 -52
  42. package/.agents/scripts/generate-skills-index.js +14 -112
  43. package/.agents/scripts/generate-workflows-doc.js +10 -70
  44. package/.agents/scripts/git-cleanup.js +2 -34
  45. package/.agents/scripts/lib/Graph.js +24 -81
  46. package/.agents/scripts/lib/ITicketingProvider.js +47 -143
  47. package/.agents/scripts/lib/Logger.js +14 -76
  48. package/.agents/scripts/lib/audit-baselines/engine.js +11 -31
  49. package/.agents/scripts/lib/audit-baselines/gate-surface.js +3 -17
  50. package/.agents/scripts/lib/audit-baselines/headroom.js +4 -20
  51. package/.agents/scripts/lib/audit-baselines/hotspots.js +3 -15
  52. package/.agents/scripts/lib/audit-baselines/kinds.js +23 -89
  53. package/.agents/scripts/lib/audit-baselines/outliers.js +6 -23
  54. package/.agents/scripts/lib/audit-baselines/read.js +3 -16
  55. package/.agents/scripts/lib/audit-baselines/staleness.js +7 -26
  56. package/.agents/scripts/lib/audit-baselines/surface-entry.js +7 -28
  57. package/.agents/scripts/lib/audit-baselines/trend.js +7 -20
  58. package/.agents/scripts/lib/audit-baselines/weights.js +9 -37
  59. package/.agents/scripts/lib/audit-suite/audit-rules-reader.js +3 -18
  60. package/.agents/scripts/lib/audit-suite/checklist-threading.js +26 -124
  61. package/.agents/scripts/lib/audit-suite/dispatch-checklist.js +14 -45
  62. package/.agents/scripts/lib/audit-suite/findings.js +12 -52
  63. package/.agents/scripts/lib/audit-suite/frontmatter.js +5 -29
  64. package/.agents/scripts/lib/audit-suite/index.js +1 -15
  65. package/.agents/scripts/lib/audit-suite/lens-checklist.js +10 -45
  66. package/.agents/scripts/lib/audit-suite/lens-diff-floor.js +11 -76
  67. package/.agents/scripts/lib/audit-suite/runner.js +13 -43
  68. package/.agents/scripts/lib/audit-suite/selector.js +49 -349
  69. package/.agents/scripts/lib/audit-suite/substitutions.js +12 -40
  70. package/.agents/scripts/lib/audit-suite/workflow-loader.js +3 -15
  71. package/.agents/scripts/lib/audit-to-stories/audit-label-taxonomy.js +11 -73
  72. package/.agents/scripts/lib/audit-to-stories/audit-lenses.js +8 -37
  73. package/.agents/scripts/lib/audit-to-stories/build-story-body.js +25 -143
  74. package/.agents/scripts/lib/audit-to-stories/dedupe-against-github.js +14 -80
  75. package/.agents/scripts/lib/audit-to-stories/epic-grouping-directive.js +4 -18
  76. package/.agents/scripts/lib/audit-to-stories/finding-adapter.js +7 -40
  77. package/.agents/scripts/lib/audit-to-stories/group-findings.js +9 -39
  78. package/.agents/scripts/lib/audit-to-stories/issue-corpus.js +10 -66
  79. package/.agents/scripts/lib/audit-to-stories/issue-index.js +6 -30
  80. package/.agents/scripts/lib/audit-to-stories/issues-file.js +6 -36
  81. package/.agents/scripts/lib/audit-to-stories/ledger-commit.js +17 -69
  82. package/.agents/scripts/lib/audit-to-stories/ledger-pr.js +17 -85
  83. package/.agents/scripts/lib/audit-to-stories/ledger-record.js +10 -48
  84. package/.agents/scripts/lib/audit-to-stories/parse-audit-md.js +38 -146
  85. package/.agents/scripts/lib/audit-to-stories/seed-from-findings.js +7 -42
  86. package/.agents/scripts/lib/audit-to-stories/wire-dependencies.js +10 -57
  87. package/.agents/scripts/lib/baseline-loader.js +12 -51
  88. package/.agents/scripts/lib/baseline-schema-registry.js +5 -22
  89. package/.agents/scripts/lib/baselines/component-matcher.js +3 -12
  90. package/.agents/scripts/lib/baselines/components.js +10 -61
  91. package/.agents/scripts/lib/baselines/coverage-updater-cli.js +7 -36
  92. package/.agents/scripts/lib/baselines/crap-preview-incremental.js +7 -19
  93. package/.agents/scripts/lib/baselines/crap-preview-scan.js +19 -45
  94. package/.agents/scripts/lib/baselines/crap-updater-cli.js +16 -65
  95. package/.agents/scripts/lib/baselines/diff-scope-cli.js +5 -33
  96. package/.agents/scripts/lib/baselines/duplication-scanner.js +11 -60
  97. package/.agents/scripts/lib/baselines/env-overrides.js +12 -67
  98. package/.agents/scripts/lib/baselines/envelope.js +13 -129
  99. package/.agents/scripts/lib/baselines/exit-codes.js +6 -49
  100. package/.agents/scripts/lib/baselines/git-base.js +23 -142
  101. package/.agents/scripts/lib/baselines/kernel.js +11 -111
  102. package/.agents/scripts/lib/baselines/kinds/_crap-new-method-gate.js +9 -47
  103. package/.agents/scripts/lib/baselines/kinds/_crap-read.js +8 -51
  104. package/.agents/scripts/lib/baselines/kinds/_shared-metric.js +7 -62
  105. package/.agents/scripts/lib/baselines/kinds/bundle-size.js +8 -29
  106. package/.agents/scripts/lib/baselines/kinds/coverage.js +3 -15
  107. package/.agents/scripts/lib/baselines/kinds/crap.js +94 -415
  108. package/.agents/scripts/lib/baselines/kinds/duplication.js +4 -30
  109. package/.agents/scripts/lib/baselines/kinds/kind-factory.js +7 -53
  110. package/.agents/scripts/lib/baselines/kinds/maintainability.js +15 -80
  111. package/.agents/scripts/lib/baselines/kinds/mutation.js +12 -72
  112. package/.agents/scripts/lib/baselines/maintainability-baseline-io.js +4 -11
  113. package/.agents/scripts/lib/baselines/merge-envelopes.js +30 -146
  114. package/.agents/scripts/lib/baselines/path-canon.js +19 -128
  115. package/.agents/scripts/lib/baselines/preview-gates.js +8 -37
  116. package/.agents/scripts/lib/baselines/reader.js +14 -107
  117. package/.agents/scripts/lib/baselines/refresh-service.js +35 -252
  118. package/.agents/scripts/lib/baselines/scope.js +11 -135
  119. package/.agents/scripts/lib/baselines/writer.js +16 -149
  120. package/.agents/scripts/lib/bdd-runner-detect.js +18 -113
  121. package/.agents/scripts/lib/bdd-scenario-budget.js +9 -36
  122. package/.agents/scripts/lib/bdd-scenario-scanner.js +11 -73
  123. package/.agents/scripts/lib/bdd-step-index.js +26 -100
  124. package/.agents/scripts/lib/bootstrap/baseline-merge-driver.js +23 -103
  125. package/.agents/scripts/lib/bootstrap/branch-protection.js +2 -5
  126. package/.agents/scripts/lib/bootstrap/commit-push.js +12 -50
  127. package/.agents/scripts/lib/bootstrap/gh-preflight.js +14 -101
  128. package/.agents/scripts/lib/bootstrap/hitl-confirm.js +5 -30
  129. package/.agents/scripts/lib/bootstrap/install-ledger.js +17 -73
  130. package/.agents/scripts/lib/bootstrap/issue-forms-template.js +17 -121
  131. package/.agents/scripts/lib/bootstrap/manifest.js +15 -86
  132. package/.agents/scripts/lib/bootstrap/merge-methods.js +4 -33
  133. package/.agents/scripts/lib/bootstrap/preflight.js +12 -61
  134. package/.agents/scripts/lib/bootstrap/project-bootstrap.js +34 -235
  135. package/.agents/scripts/lib/bootstrap/prompt.js +21 -129
  136. package/.agents/scripts/lib/bootstrap/quality-bootstrap.js +27 -141
  137. package/.agents/scripts/lib/bootstrap/summary.js +1 -8
  138. package/.agents/scripts/lib/bootstrap/workflow-audit.js +11 -84
  139. package/.agents/scripts/lib/branch-name-guard.js +4 -23
  140. package/.agents/scripts/lib/changed-files.js +39 -121
  141. package/.agents/scripts/lib/checks/core-bare-clean.js +4 -24
  142. package/.agents/scripts/lib/checks/index.js +18 -101
  143. package/.agents/scripts/lib/checks/loop-health.js +11 -53
  144. package/.agents/scripts/lib/checks/state.js +16 -91
  145. package/.agents/scripts/lib/checks/story-init-not-backgrounded.js +10 -61
  146. package/.agents/scripts/lib/checks/subagent-agent-tool-required.js +14 -83
  147. package/.agents/scripts/lib/child-exec.js +39 -108
  148. package/.agents/scripts/lib/cli/standard-args.js +9 -116
  149. package/.agents/scripts/lib/cli-args.js +31 -146
  150. package/.agents/scripts/lib/cli-usage.js +5 -27
  151. package/.agents/scripts/lib/cli-utils.js +2 -12
  152. package/.agents/scripts/lib/close-validation/commands.js +13 -76
  153. package/.agents/scripts/lib/close-validation/gates.js +92 -331
  154. package/.agents/scripts/lib/close-validation/process.js +121 -151
  155. package/.agents/scripts/lib/close-validation/projections/advisories.js +5 -31
  156. package/.agents/scripts/lib/close-validation/projections/crap.js +19 -67
  157. package/.agents/scripts/lib/close-validation/projections/head-sha.js +1 -17
  158. package/.agents/scripts/lib/close-validation/projections/inputs.js +4 -34
  159. package/.agents/scripts/lib/close-validation/projections/maintainability.js +11 -58
  160. package/.agents/scripts/lib/close-validation/runner.js +53 -110
  161. package/.agents/scripts/lib/command-header.js +6 -28
  162. package/.agents/scripts/lib/config/acceptance-eval.js +4 -30
  163. package/.agents/scripts/lib/config/baselines.js +3 -14
  164. package/.agents/scripts/lib/config/ci.js +8 -38
  165. package/.agents/scripts/lib/config/commands.js +1 -15
  166. package/.agents/scripts/lib/config/defaults.js +4 -33
  167. package/.agents/scripts/lib/config/delivery-routing.js +6 -34
  168. package/.agents/scripts/lib/config/explain.js +10 -83
  169. package/.agents/scripts/lib/config/gates/coverage.schema.js +0 -12
  170. package/.agents/scripts/lib/config/gates/crap-incremental-coverage.schema.js +3 -31
  171. package/.agents/scripts/lib/config/gates/crap.schema.js +1 -34
  172. package/.agents/scripts/lib/config/gates/duplication.schema.js +1 -8
  173. package/.agents/scripts/lib/config/gates/index.js +2 -17
  174. package/.agents/scripts/lib/config/gates/maintainability.schema.js +1 -20
  175. package/.agents/scripts/lib/config/gates/mutation.schema.js +1 -7
  176. package/.agents/scripts/lib/config/gates/shared.js +4 -64
  177. package/.agents/scripts/lib/config/github.js +4 -22
  178. package/.agents/scripts/lib/config/limits.js +4 -57
  179. package/.agents/scripts/lib/config/paths.js +2 -21
  180. package/.agents/scripts/lib/config/qa.js +4 -38
  181. package/.agents/scripts/lib/config/quality.js +76 -432
  182. package/.agents/scripts/lib/config/runners.js +12 -56
  183. package/.agents/scripts/lib/config/runtime.js +13 -57
  184. package/.agents/scripts/lib/config/shared.js +2 -16
  185. package/.agents/scripts/lib/config/sync-agentrc.js +9 -50
  186. package/.agents/scripts/lib/config/temp-paths.js +40 -280
  187. package/.agents/scripts/lib/config/validate-orchestration.js +5 -17
  188. package/.agents/scripts/lib/config/worktree-isolation.js +9 -28
  189. package/.agents/scripts/lib/config-resolver.js +11 -52
  190. package/.agents/scripts/lib/config-schema-shared.js +1 -4
  191. package/.agents/scripts/lib/config-settings-schema-delivery.js +16 -291
  192. package/.agents/scripts/lib/config-settings-schema-quality.js +7 -219
  193. package/.agents/scripts/lib/config-settings-schema.js +37 -274
  194. package/.agents/scripts/lib/coverage-baseline.js +20 -110
  195. package/.agents/scripts/lib/coverage-capture-fullscope.js +18 -35
  196. package/.agents/scripts/lib/coverage-capture-incremental.js +15 -45
  197. package/.agents/scripts/lib/coverage-capture-usage.js +8 -25
  198. package/.agents/scripts/lib/coverage-capture.js +99 -224
  199. package/.agents/scripts/lib/coverage-utils.js +14 -79
  200. package/.agents/scripts/lib/cpu-pool.js +17 -123
  201. package/.agents/scripts/lib/crap-baseline-join.js +16 -88
  202. package/.agents/scripts/lib/crap-coordinates.js +6 -25
  203. package/.agents/scripts/lib/crap-engine.js +40 -153
  204. package/.agents/scripts/lib/crap-method-identity.js +16 -78
  205. package/.agents/scripts/lib/crap-utils.js +31 -155
  206. package/.agents/scripts/lib/cyclomatic-ceiling.js +14 -87
  207. package/.agents/scripts/lib/cyclomatic-scope.js +10 -50
  208. package/.agents/scripts/lib/dead-exports-knip.js +18 -56
  209. package/.agents/scripts/lib/dead-exports-mode.js +5 -24
  210. package/.agents/scripts/lib/degraded-mode.js +3 -26
  211. package/.agents/scripts/lib/dependency-parser.js +8 -40
  212. package/.agents/scripts/lib/dependency-version.js +13 -35
  213. package/.agents/scripts/lib/detect-package-manager.js +6 -36
  214. package/.agents/scripts/lib/doc-tiers.js +22 -124
  215. package/.agents/scripts/lib/duplicate-search.js +8 -72
  216. package/.agents/scripts/lib/env-loader.js +5 -27
  217. package/.agents/scripts/lib/error-redactor.js +6 -31
  218. package/.agents/scripts/lib/errors/index.js +2 -22
  219. package/.agents/scripts/lib/escomplex-ast-compat.js +22 -163
  220. package/.agents/scripts/lib/escomplex-kernel.js +21 -123
  221. package/.agents/scripts/lib/feedback-loop/graduator-core.js +94 -419
  222. package/.agents/scripts/lib/feedback-loop/prior-feedback-fetcher.js +20 -98
  223. package/.agents/scripts/lib/feedback-loop/retro-proposals-graduator.js +27 -149
  224. package/.agents/scripts/lib/findings/audit-ledger.js +23 -97
  225. package/.agents/scripts/lib/findings/classify-finding.js +14 -69
  226. package/.agents/scripts/lib/findings/promote-finding.js +14 -112
  227. package/.agents/scripts/lib/findings/provenance-field.js +11 -52
  228. package/.agents/scripts/lib/findings/route-finding.js +37 -255
  229. package/.agents/scripts/lib/findings/semantic-issue-search.js +14 -59
  230. package/.agents/scripts/lib/findings/severity.js +18 -104
  231. package/.agents/scripts/lib/format-generated-json.js +12 -44
  232. package/.agents/scripts/lib/full-suite-lock.js +160 -429
  233. package/.agents/scripts/lib/full-suite-queue.js +213 -0
  234. package/.agents/scripts/lib/gates/baseline-store.js +5 -9
  235. package/.agents/scripts/lib/gates/friction.js +1 -3
  236. package/.agents/scripts/lib/generated/agentrc-validator.js +2 -2
  237. package/.agents/scripts/lib/gh-exec.js +123 -191
  238. package/.agents/scripts/lib/git/cached-fetch.js +9 -52
  239. package/.agents/scripts/lib/git/sync-from-base.js +15 -107
  240. package/.agents/scripts/lib/git-branch-cleanup.js +14 -61
  241. package/.agents/scripts/lib/git-branch-lifecycle.js +16 -87
  242. package/.agents/scripts/lib/git-utils.js +37 -161
  243. package/.agents/scripts/lib/github/framework-repo.js +8 -71
  244. package/.agents/scripts/lib/github-url.js +2 -17
  245. package/.agents/scripts/lib/import-graph.js +11 -38
  246. package/.agents/scripts/lib/install-cmd-parser.js +4 -13
  247. package/.agents/scripts/lib/json-utils.js +3 -18
  248. package/.agents/scripts/lib/label-constants.js +16 -111
  249. package/.agents/scripts/lib/label-taxonomy.js +4 -30
  250. package/.agents/scripts/lib/maintainability-engine.js +12 -80
  251. package/.agents/scripts/lib/maintainability-unscorable.js +5 -25
  252. package/.agents/scripts/lib/maintainability-utils.js +16 -92
  253. package/.agents/scripts/lib/mandrel-catalog.js +12 -70
  254. package/.agents/scripts/lib/notifications/notifier.js +13 -38
  255. package/.agents/scripts/lib/npm-scripts.js +4 -23
  256. package/.agents/scripts/lib/observability/metrics-ledger.js +16 -76
  257. package/.agents/scripts/lib/observability/runtime-friction.js +44 -286
  258. package/.agents/scripts/lib/observability/signal-validator.js +5 -37
  259. package/.agents/scripts/lib/observability/signals-writer.js +11 -121
  260. package/.agents/scripts/lib/observability/source-classifier.js +22 -203
  261. package/.agents/scripts/lib/observability/terse-result.js +14 -47
  262. package/.agents/scripts/lib/onboard/init-tail.js +13 -74
  263. package/.agents/scripts/lib/onboard/scaffold-docs.js +11 -34
  264. package/.agents/scripts/lib/orchestration/acceptance-eval-decision.js +25 -139
  265. package/.agents/scripts/lib/orchestration/auto-merge-cwd.js +11 -66
  266. package/.agents/scripts/lib/orchestration/behind-recovery.js +10 -55
  267. package/.agents/scripts/lib/orchestration/ceremony-routing.js +28 -156
  268. package/.agents/scripts/lib/orchestration/change-set.js +7 -36
  269. package/.agents/scripts/lib/orchestration/check-baselines/phases/compare.js +9 -46
  270. package/.agents/scripts/lib/orchestration/check-baselines/phases/evaluate.js +11 -44
  271. package/.agents/scripts/lib/orchestration/check-baselines/phases/floors.js +5 -24
  272. package/.agents/scripts/lib/orchestration/check-baselines/phases/friction.js +3 -12
  273. package/.agents/scripts/lib/orchestration/check-baselines/phases/parse-args.js +5 -38
  274. package/.agents/scripts/lib/orchestration/check-baselines/phases/pipeline.js +3 -8
  275. package/.agents/scripts/lib/orchestration/check-baselines/phases/refresh-ack.js +28 -136
  276. package/.agents/scripts/lib/orchestration/check-baselines/phases/report.js +3 -13
  277. package/.agents/scripts/lib/orchestration/check-state.js +92 -0
  278. package/.agents/scripts/lib/orchestration/ci-gap-intake.js +37 -144
  279. package/.agents/scripts/lib/orchestration/ci-rerun-guard.js +121 -157
  280. package/.agents/scripts/lib/orchestration/code-review.js +24 -128
  281. package/.agents/scripts/lib/orchestration/column-sync.js +23 -111
  282. package/.agents/scripts/lib/orchestration/complexity-gate.js +118 -462
  283. package/.agents/scripts/lib/orchestration/deliver-recover.js +36 -168
  284. package/.agents/scripts/lib/orchestration/dependency-analyzer.js +4 -35
  285. package/.agents/scripts/lib/orchestration/dependency-candidates.js +9 -41
  286. package/.agents/scripts/lib/orchestration/diff-magnitude.js +25 -115
  287. package/.agents/scripts/lib/orchestration/doc-reader.js +2 -6
  288. package/.agents/scripts/lib/orchestration/docs-digest.js +13 -51
  289. package/.agents/scripts/lib/orchestration/epic-candidates.js +13 -53
  290. package/.agents/scripts/lib/orchestration/epic-checklist.js +9 -33
  291. package/.agents/scripts/lib/orchestration/epic-container.js +41 -167
  292. package/.agents/scripts/lib/orchestration/epic-expansion.js +10 -40
  293. package/.agents/scripts/lib/orchestration/epic-rollup.js +55 -207
  294. package/.agents/scripts/lib/orchestration/file-assumption-enum.js +2 -22
  295. package/.agents/scripts/lib/orchestration/file-assumptions.js +45 -253
  296. package/.agents/scripts/lib/orchestration/git-cleanup/phases/branches-detect.js +9 -46
  297. package/.agents/scripts/lib/orchestration/git-cleanup/phases/branches-reap.js +7 -57
  298. package/.agents/scripts/lib/orchestration/git-cleanup/phases/branches.js +21 -126
  299. package/.agents/scripts/lib/orchestration/git-cleanup/phases/cli.js +2 -6
  300. package/.agents/scripts/lib/orchestration/git-cleanup/phases/fast-forward.js +1 -4
  301. package/.agents/scripts/lib/orchestration/git-cleanup/phases/filters.js +1 -5
  302. package/.agents/scripts/lib/orchestration/git-cleanup/phases/git-probes-ff.js +9 -34
  303. package/.agents/scripts/lib/orchestration/git-cleanup/phases/git-probes.js +41 -190
  304. package/.agents/scripts/lib/orchestration/git-cleanup/phases/merged-tip.js +9 -45
  305. package/.agents/scripts/lib/orchestration/git-cleanup/phases/parse-args.js +1 -4
  306. package/.agents/scripts/lib/orchestration/git-cleanup/phases/phase-drivers.js +19 -85
  307. package/.agents/scripts/lib/orchestration/git-cleanup/phases/prompts.js +3 -17
  308. package/.agents/scripts/lib/orchestration/git-cleanup/phases/prune.js +1 -5
  309. package/.agents/scripts/lib/orchestration/git-cleanup/phases/render.js +16 -100
  310. package/.agents/scripts/lib/orchestration/git-cleanup/phases/stashes.js +1 -4
  311. package/.agents/scripts/lib/orchestration/lease-guard-shared.js +16 -67
  312. package/.agents/scripts/lib/orchestration/lifecycle/emit-ledger-event.js +9 -33
  313. package/.agents/scripts/lib/orchestration/lifecycle/emit-merge-flip-failed.js +8 -29
  314. package/.agents/scripts/lib/orchestration/lifecycle/emit-merge-unlanded.js +15 -68
  315. package/.agents/scripts/lib/orchestration/light-backstop.js +11 -49
  316. package/.agents/scripts/lib/orchestration/light-escalation.js +21 -89
  317. package/.agents/scripts/lib/orchestration/light-suitability.js +37 -307
  318. package/.agents/scripts/lib/orchestration/merge-block-class.js +39 -216
  319. package/.agents/scripts/lib/orchestration/merge-poll.js +104 -377
  320. package/.agents/scripts/lib/orchestration/pinned-identifier-lint.js +17 -57
  321. package/.agents/scripts/lib/orchestration/plan-context.js +55 -318
  322. package/.agents/scripts/lib/orchestration/plan-critic-conditions.js +22 -79
  323. package/.agents/scripts/lib/orchestration/plan-critics-evaluate.js +5 -31
  324. package/.agents/scripts/lib/orchestration/plan-metrics.js +29 -108
  325. package/.agents/scripts/lib/orchestration/plan-navigation.js +8 -27
  326. package/.agents/scripts/lib/orchestration/plan-persist/acceptance-handle-repair.js +12 -49
  327. package/.agents/scripts/lib/orchestration/plan-persist/audit-provenance.js +18 -73
  328. package/.agents/scripts/lib/orchestration/plan-persist/changes-repair.js +16 -78
  329. package/.agents/scripts/lib/orchestration/plan-persist/cross-plan-links.js +6 -30
  330. package/.agents/scripts/lib/orchestration/plan-persist/epic-adoption.js +14 -65
  331. package/.agents/scripts/lib/orchestration/plan-persist/epic-ops.js +21 -76
  332. package/.agents/scripts/lib/orchestration/plan-persist/external-deps.js +6 -43
  333. package/.agents/scripts/lib/orchestration/plan-persist/persist-helpers.js +14 -68
  334. package/.agents/scripts/lib/orchestration/plan-persist/plan-context-source.js +9 -41
  335. package/.agents/scripts/lib/orchestration/plan-persist/run-plan-persist.js +71 -289
  336. package/.agents/scripts/lib/orchestration/plan-persist/soft-findings.js +2 -12
  337. package/.agents/scripts/lib/orchestration/plan-persist/story-ops.js +96 -362
  338. package/.agents/scripts/lib/orchestration/plan-persist/summary.js +9 -39
  339. package/.agents/scripts/lib/orchestration/plan-persist/supersede-ops.js +60 -186
  340. package/.agents/scripts/lib/orchestration/plan-persist/wave-collision-gate.js +9 -49
  341. package/.agents/scripts/lib/orchestration/plan-persist/wave-serialisation.js +7 -28
  342. package/.agents/scripts/lib/orchestration/plan-reachability.js +13 -42
  343. package/.agents/scripts/lib/orchestration/plan-run-labels/reap.js +16 -91
  344. package/.agents/scripts/lib/orchestration/plan-runner/worktree-sweep.js +16 -54
  345. package/.agents/scripts/lib/orchestration/plan-text-hygiene.js +10 -52
  346. package/.agents/scripts/lib/orchestration/planning/authoring-context.js +14 -109
  347. package/.agents/scripts/lib/orchestration/planning/memory-pool-advisory.js +11 -83
  348. package/.agents/scripts/lib/orchestration/pr-watch.js +48 -306
  349. package/.agents/scripts/lib/orchestration/project-meta-cache.js +15 -80
  350. package/.agents/scripts/lib/orchestration/project-meta-resolver.js +10 -51
  351. package/.agents/scripts/lib/orchestration/reassert-status-column.js +12 -76
  352. package/.agents/scripts/lib/orchestration/remote-verifier.js +12 -36
  353. package/.agents/scripts/lib/orchestration/resolve-stories.js +33 -164
  354. package/.agents/scripts/lib/orchestration/retro-proposals.js +66 -361
  355. package/.agents/scripts/lib/orchestration/review-base-ref.js +9 -43
  356. package/.agents/scripts/lib/orchestration/review-depth.js +12 -91
  357. package/.agents/scripts/lib/orchestration/review-providers/codex.js +10 -111
  358. package/.agents/scripts/lib/orchestration/review-providers/degraded-gates.js +13 -71
  359. package/.agents/scripts/lib/orchestration/review-providers/findings-renderer.js +7 -51
  360. package/.agents/scripts/lib/orchestration/review-providers/mi-exemptions.js +8 -53
  361. package/.agents/scripts/lib/orchestration/review-providers/native.js +30 -214
  362. package/.agents/scripts/lib/orchestration/review-providers/parse-findings.js +7 -52
  363. package/.agents/scripts/lib/orchestration/review-providers/review-depth.js +4 -33
  364. package/.agents/scripts/lib/orchestration/review-providers/review-provider-factory.js +11 -71
  365. package/.agents/scripts/lib/orchestration/review-providers/scoped-lint.js +19 -131
  366. package/.agents/scripts/lib/orchestration/review-providers/security-review.js +6 -89
  367. package/.agents/scripts/lib/orchestration/review-providers/types.js +26 -56
  368. package/.agents/scripts/lib/orchestration/review-providers/ultrareview.js +3 -46
  369. package/.agents/scripts/lib/orchestration/run-epilogue.js +245 -326
  370. package/.agents/scripts/lib/orchestration/run-scoped-config.js +55 -159
  371. package/.agents/scripts/lib/orchestration/single-story-close/close-note.js +7 -34
  372. package/.agents/scripts/lib/orchestration/single-story-close/failed-terminal.js +16 -89
  373. package/.agents/scripts/lib/orchestration/single-story-close/gate-log.js +18 -109
  374. package/.agents/scripts/lib/orchestration/single-story-close/phases/auto-merge.js +83 -239
  375. package/.agents/scripts/lib/orchestration/single-story-close/phases/base-sync.js +20 -82
  376. package/.agents/scripts/lib/orchestration/single-story-close/phases/close-validation.js +103 -97
  377. package/.agents/scripts/lib/orchestration/single-story-close/phases/code-review.js +8 -64
  378. package/.agents/scripts/lib/orchestration/single-story-close/phases/confirm-merge.js +136 -486
  379. package/.agents/scripts/lib/orchestration/single-story-close/phases/conventional-subject.js +30 -134
  380. package/.agents/scripts/lib/orchestration/single-story-close/phases/graphql-preflight.js +96 -0
  381. package/.agents/scripts/lib/orchestration/single-story-close/phases/lock-wait-pending.js +49 -0
  382. package/.agents/scripts/lib/orchestration/single-story-close/phases/normalize-pr-title.js +13 -86
  383. package/.agents/scripts/lib/orchestration/single-story-close/phases/options.js +20 -95
  384. package/.agents/scripts/lib/orchestration/single-story-close/phases/post-land.js +46 -203
  385. package/.agents/scripts/lib/orchestration/single-story-close/phases/pre-gate-steps.js +8 -50
  386. package/.agents/scripts/lib/orchestration/single-story-close/phases/pull-request.js +12 -83
  387. package/.agents/scripts/lib/orchestration/single-story-close/phases/push.js +6 -39
  388. package/.agents/scripts/lib/orchestration/single-story-close/phases/review-block.js +1 -6
  389. package/.agents/scripts/lib/orchestration/single-story-close/phases/review-outcome.js +4 -28
  390. package/.agents/scripts/lib/orchestration/single-story-close/phases/review-override.js +6 -51
  391. package/.agents/scripts/lib/orchestration/single-story-close/phases/worktree-reap.js +6 -34
  392. package/.agents/scripts/lib/orchestration/single-story-close/phases/wrong-tree-guard.js +23 -121
  393. package/.agents/scripts/lib/orchestration/single-story-close/runner.js +180 -273
  394. package/.agents/scripts/lib/orchestration/single-story-lease-guard.js +15 -69
  395. package/.agents/scripts/lib/orchestration/story-body-gate.js +4 -23
  396. package/.agents/scripts/lib/orchestration/story-close/baseline-upward-writeback.js +50 -179
  397. package/.agents/scripts/lib/orchestration/story-close/context-budget-writeback.js +11 -41
  398. package/.agents/scripts/lib/orchestration/story-close/format-autofix.js +23 -311
  399. package/.agents/scripts/lib/orchestration/story-close/phases/local-lens-review.js +22 -147
  400. package/.agents/scripts/lib/orchestration/story-close/phases/review-core.js +16 -67
  401. package/.agents/scripts/lib/orchestration/story-deliver-terminal-schema.js +12 -68
  402. package/.agents/scripts/lib/orchestration/story-deliver-terminal.js +42 -200
  403. package/.agents/scripts/lib/orchestration/story-follow-ups.js +167 -252
  404. package/.agents/scripts/lib/orchestration/story-init-envelope.js +52 -0
  405. package/.agents/scripts/lib/orchestration/story-init-remote.js +2 -6
  406. package/.agents/scripts/lib/orchestration/story-reachability.js +3 -23
  407. package/.agents/scripts/lib/orchestration/task-body-validator.js +12 -135
  408. package/.agents/scripts/lib/orchestration/ticket-lease.js +28 -131
  409. package/.agents/scripts/lib/orchestration/ticket-validator-conflicts.js +15 -149
  410. package/.agents/scripts/lib/orchestration/ticket-validator-sizing.js +5 -33
  411. package/.agents/scripts/lib/orchestration/ticket-validator.js +63 -344
  412. package/.agents/scripts/lib/orchestration/ticketing/bulk.js +51 -244
  413. package/.agents/scripts/lib/orchestration/ticketing/reads.js +39 -261
  414. package/.agents/scripts/lib/orchestration/ticketing/state.js +14 -68
  415. package/.agents/scripts/lib/orchestration/ticketing/transition.js +46 -263
  416. package/.agents/scripts/lib/orchestration/ticketing.js +2 -26
  417. package/.agents/scripts/lib/orchestration/verify-credit.js +18 -79
  418. package/.agents/scripts/lib/orchestration/worktree-dirty.js +5 -30
  419. package/.agents/scripts/lib/path-security.js +3 -6
  420. package/.agents/scripts/lib/plan-phase-cleanup.js +9 -49
  421. package/.agents/scripts/lib/preflight-runner.js +13 -69
  422. package/.agents/scripts/lib/process-group.js +143 -0
  423. package/.agents/scripts/lib/project-root.js +2 -8
  424. package/.agents/scripts/lib/provider-factory.js +2 -25
  425. package/.agents/scripts/lib/qa/console-allowlist.js +10 -59
  426. package/.agents/scripts/lib/qa/qa-session.js +15 -69
  427. package/.agents/scripts/lib/qa/redact-evidence.js +18 -129
  428. package/.agents/scripts/lib/qa/resolve-qa-contract.js +26 -135
  429. package/.agents/scripts/lib/qa/resolve-selection.js +16 -84
  430. package/.agents/scripts/lib/reserved-test-ids.js +9 -47
  431. package/.agents/scripts/lib/runtime-deps/dep-resolution.js +13 -53
  432. package/.agents/scripts/lib/runtime-deps/ensure-installed.js +10 -44
  433. package/.agents/scripts/lib/runtime-deps/manifest.js +10 -30
  434. package/.agents/scripts/lib/runtime-deps/parser-major.js +15 -51
  435. package/.agents/scripts/lib/runtime-deps/preflight.js +4 -22
  436. package/.agents/scripts/lib/runtime-deps/scan-imports.js +11 -55
  437. package/.agents/scripts/lib/signals/detectors/common.js +12 -38
  438. package/.agents/scripts/lib/signals/index.js +2 -19
  439. package/.agents/scripts/lib/signals/schema.js +14 -109
  440. package/.agents/scripts/lib/signals/write.js +2 -11
  441. package/.agents/scripts/lib/single-story/confirm-merge.js +31 -72
  442. package/.agents/scripts/lib/single-story/story-merged-notify.js +8 -49
  443. package/.agents/scripts/lib/single-story-sweep/protection-ctx.js +4 -26
  444. package/.agents/scripts/lib/single-story-sweep/protection.js +13 -100
  445. package/.agents/scripts/lib/single-story-sweep/sweep-lock.js +60 -258
  446. package/.agents/scripts/lib/single-story-sweep.js +14 -97
  447. package/.agents/scripts/lib/skills/parse-skill.js +13 -59
  448. package/.agents/scripts/lib/skills/skills-index.js +8 -29
  449. package/.agents/scripts/lib/skills/walk-skill-files.js +17 -78
  450. package/.agents/scripts/lib/source-extensions.js +11 -42
  451. package/.agents/scripts/lib/source-text/strip-js-comments.js +8 -41
  452. package/.agents/scripts/lib/stdio-flush.js +9 -36
  453. package/.agents/scripts/lib/story-adjacency.js +8 -40
  454. package/.agents/scripts/lib/story-body/body-format-lints.js +51 -66
  455. package/.agents/scripts/lib/story-body/footer-block.js +11 -44
  456. package/.agents/scripts/lib/story-body/story-body.js +123 -344
  457. package/.agents/scripts/lib/temp-retention.js +42 -143
  458. package/.agents/scripts/lib/templates/decomposer-prompts.js +18 -80
  459. package/.agents/scripts/lib/test-env.js +12 -65
  460. package/.agents/scripts/lib/test-run-credit.js +10 -65
  461. package/.agents/scripts/lib/test-runner-contract.js +19 -74
  462. package/.agents/scripts/lib/test-temp.js +38 -184
  463. package/.agents/scripts/lib/test-tiers.js +12 -96
  464. package/.agents/scripts/lib/ticket-body-sections.js +21 -96
  465. package/.agents/scripts/lib/transpile.js +15 -74
  466. package/.agents/scripts/lib/util/concurrent-map.js +6 -25
  467. package/.agents/scripts/lib/util/parse-id-list.js +8 -34
  468. package/.agents/scripts/lib/util/poll-loop.js +8 -28
  469. package/.agents/scripts/lib/util/with-timeout.js +2 -11
  470. package/.agents/scripts/lib/validation-evidence.js +21 -96
  471. package/.agents/scripts/lib/wave-runner/footprint.js +17 -70
  472. package/.agents/scripts/lib/wave-runner/live-probe.js +67 -209
  473. package/.agents/scripts/lib/wave-runner/ready-set.js +72 -301
  474. package/.agents/scripts/lib/workers/crap-worker.js +11 -64
  475. package/.agents/scripts/lib/workers/maintainability-report-worker.js +8 -45
  476. package/.agents/scripts/lib/workers/maintainability-worker.js +5 -21
  477. package/.agents/scripts/lib/workers/serve-worker-messages.js +2 -18
  478. package/.agents/scripts/lib/workflow-closure.js +22 -105
  479. package/.agents/scripts/lib/workspace-provisioner.js +16 -54
  480. package/.agents/scripts/lib/worktree/git-hooks.js +15 -60
  481. package/.agents/scripts/lib/worktree/lifecycle/force-drain.js +20 -60
  482. package/.agents/scripts/lib/worktree/lifecycle/merge-reachability.js +10 -62
  483. package/.agents/scripts/lib/worktree/lifecycle/pending-cleanup.js +22 -86
  484. package/.agents/scripts/lib/worktree/lifecycle/reap.js +24 -126
  485. package/.agents/scripts/lib/worktree/lifecycle-manager.js +2 -20
  486. package/.agents/scripts/lib/worktree/node-modules-strategy.js +46 -180
  487. package/.agents/scripts/lib/worktree-manager.js +15 -40
  488. package/.agents/scripts/lint-issue-body.js +12 -68
  489. package/.agents/scripts/mandrel-update-preflight.js +10 -70
  490. package/.agents/scripts/merge-baseline.js +30 -105
  491. package/.agents/scripts/nav-registry-diff.js +22 -108
  492. package/.agents/scripts/notify.js +12 -69
  493. package/.agents/scripts/plan-context.js +97 -92
  494. package/.agents/scripts/plan-critics.js +12 -66
  495. package/.agents/scripts/plan-persist.js +71 -137
  496. package/.agents/scripts/plan-run-epilogue.js +20 -43
  497. package/.agents/scripts/pr-watch-with-update.js +79 -263
  498. package/.agents/scripts/providers/github/auth.js +3 -8
  499. package/.agents/scripts/providers/github/blocked-by-add.js +14 -61
  500. package/.agents/scripts/providers/github/board-add.js +5 -21
  501. package/.agents/scripts/providers/github/branch-protection.js +11 -43
  502. package/.agents/scripts/providers/github/cache.js +2 -14
  503. package/.agents/scripts/providers/github/comments.js +7 -40
  504. package/.agents/scripts/providers/github/compose.js +3 -19
  505. package/.agents/scripts/providers/github/errors.js +22 -125
  506. package/.agents/scripts/providers/github/issues.js +31 -123
  507. package/.agents/scripts/providers/github/labels.js +24 -124
  508. package/.agents/scripts/providers/github/mappers.js +5 -27
  509. package/.agents/scripts/providers/github/merge-methods.js +3 -22
  510. package/.agents/scripts/providers/github/project-board.js +2 -17
  511. package/.agents/scripts/providers/github/projects-v2-graphql.js +3 -6
  512. package/.agents/scripts/providers/github/request-helpers.js +5 -32
  513. package/.agents/scripts/providers/github/search-budget.js +9 -39
  514. package/.agents/scripts/providers/github/search-query.js +5 -26
  515. package/.agents/scripts/providers/github/sub-issue-add.js +15 -65
  516. package/.agents/scripts/providers/github/sub-issues.js +7 -36
  517. package/.agents/scripts/providers/github/tickets.js +32 -134
  518. package/.agents/scripts/providers/github.js +18 -59
  519. package/.agents/scripts/prune-plan-run-labels.js +12 -46
  520. package/.agents/scripts/quality-preview.js +35 -220
  521. package/.agents/scripts/resolve-doc-tiers.js +3 -26
  522. package/.agents/scripts/resolve-stories.js +16 -67
  523. package/.agents/scripts/resync-status-column.js +6 -25
  524. package/.agents/scripts/run-tests.js +29 -104
  525. package/.agents/scripts/single-story-close.js +23 -135
  526. package/.agents/scripts/single-story-confirm-merge.js +32 -139
  527. package/.agents/scripts/single-story-init.js +47 -259
  528. package/.agents/scripts/stories-wave-tick.js +179 -415
  529. package/.agents/scripts/sync-agentrc.js +3 -21
  530. package/.agents/scripts/sync-claude-agents.js +9 -55
  531. package/.agents/scripts/sync-claude-commands.js +20 -110
  532. package/.agents/scripts/test-wrapper.js +6 -43
  533. package/.agents/scripts/update-coverage-baseline.js +5 -22
  534. package/.agents/scripts/update-crap-baseline.js +7 -30
  535. package/.agents/scripts/update-duplication-baseline.js +16 -92
  536. package/.agents/scripts/update-maintainability-baseline.js +8 -62
  537. package/.agents/scripts/update-ticket-state.js +1 -7
  538. package/.agents/scripts/validate-skills.js +6 -26
  539. package/.agents/skills/core/gates-and-baselines/reference.md +0 -1
  540. package/.agents/skills/skills.index.json +2 -2
  541. package/.agents/skills/stack/qa/playwright/SKILL.md +26 -0
  542. package/.agents/templates/agent-protocol.md +2 -2
  543. package/.agents/templates/docs/audit-sweep-runbook.md +3 -4
  544. package/.agents/workflows/audit-accessibility.md +4 -7
  545. package/.agents/workflows/audit-adrs.md +3 -6
  546. package/.agents/workflows/audit-architecture.md +3 -11
  547. package/.agents/workflows/audit-baselines.md +6 -9
  548. package/.agents/workflows/audit-clean-code.md +8 -12
  549. package/.agents/workflows/audit-data-model.md +3 -7
  550. package/.agents/workflows/audit-dependencies.md +4 -7
  551. package/.agents/workflows/audit-devops.md +4 -8
  552. package/.agents/workflows/audit-documentation.md +1 -4
  553. package/.agents/workflows/audit-mobile.md +4 -7
  554. package/.agents/workflows/audit-navigability.md +3 -6
  555. package/.agents/workflows/audit-performance.md +2 -4
  556. package/.agents/workflows/audit-privacy.md +4 -7
  557. package/.agents/workflows/audit-quality.md +4 -7
  558. package/.agents/workflows/audit-security.md +4 -7
  559. package/.agents/workflows/audit-seo.md +4 -7
  560. package/.agents/workflows/audit-sre.md +4 -7
  561. package/.agents/workflows/audit-to-stories.md +17 -30
  562. package/.agents/workflows/audit-ux-ui.md +4 -7
  563. package/.agents/workflows/helpers/acceptance-self-eval.md +84 -157
  564. package/.agents/workflows/helpers/audit-lens-core.md +1 -1
  565. package/.agents/workflows/helpers/code-quality-guardrails.md +12 -15
  566. package/.agents/workflows/helpers/code-review.md +4 -2
  567. package/.agents/workflows/helpers/deliver-digest.md +37 -28
  568. package/.agents/workflows/helpers/deliver-light.md +92 -101
  569. package/.agents/workflows/helpers/deliver-reference.md +162 -220
  570. package/.agents/workflows/helpers/deliver-story-reference.md +197 -607
  571. package/.agents/workflows/helpers/deliver-story.md +17 -18
  572. package/.agents/workflows/helpers/plan-reference.md +125 -167
  573. package/.agents/workflows/helpers/qa-core.md +12 -0
  574. package/.agents/workflows/mandrel-deliver.md +47 -31
  575. package/.agents/workflows/mandrel-plan.md +22 -21
  576. package/.agents/workflows/mandrel-update.md +36 -21
  577. package/.agents/workflows/memory-consolidate.md +1 -1
  578. package/.agents/workflows/qa-assist.md +27 -71
  579. package/.agents/workflows/qa-explore.md +19 -59
  580. package/.agents/workflows/qa-run.md +11 -33
  581. package/README.md +1 -1
  582. package/bin/mandrel.js +5 -46
  583. package/bin/postinstall.js +18 -107
  584. package/docs/CHANGELOG.md +60 -0
  585. package/lib/cli/doctor.js +11 -71
  586. package/lib/cli/init.js +26 -165
  587. package/lib/cli/migrate.js +5 -64
  588. package/lib/cli/registry.js +84 -467
  589. package/lib/cli/sync-agents.js +4 -64
  590. package/lib/cli/sync-commands.js +11 -64
  591. package/lib/cli/sync.js +34 -213
  592. package/lib/cli/uninstall.js +40 -219
  593. package/lib/cli/update.js +326 -523
  594. package/lib/cli/version-check.js +8 -72
  595. package/lib/cli/version-helpers.js +14 -87
  596. package/lib/migrations/helpers/retire-agentrc-key.js +11 -52
  597. package/lib/migrations/index.js +17 -92
  598. package/lib/migrations/steps/2.1.0-retire-mi-drop-knobs.js +2 -15
  599. package/lib/migrations/steps/2.1.0-retire-verify-concurrency-cap.js +1 -13
  600. package/lib/migrations/steps/2.11.0-retire-max-seed-words.js +2 -15
  601. package/lib/migrations/steps/2.2.0-retire-epic-ac-tags.js +9 -38
  602. package/lib/migrations/steps/2.20.0-retire-codebase-snapshot.js +1 -13
  603. package/lib/migrations/steps/2.32.0-retire-lint-baseline-command.js +2 -27
  604. package/lib/migrations/steps/2.57.0-retire-delivery-limit-knobs.js +2 -23
  605. package/lib/migrations/steps/2.57.0-retire-planning-limit-knobs.js +2 -27
  606. package/lib/migrations/steps/2.60.0-retire-audit-results-autofile.js +21 -0
  607. package/lib/migrations/steps/strip-removed-agentrc-keys.js +333 -0
  608. package/package.json +17 -14
  609. package/.agents/docs/SDLC.md +0 -590
  610. package/.agents/docs/quality-gates.md +0 -1179
  611. package/.agents/rules/known-tooling-behavior.md +0 -150
  612. package/.agents/rules/orchestration-error-handling.md +0 -61
  613. package/.agents/rules/test-seams.md +0 -59
  614. package/.agents/schemas/baselines/lighthouse.schema.json +0 -59
  615. package/.agents/schemas/baselines/lint.schema.json +0 -47
  616. package/.agents/schemas/model-attribution.schema.json +0 -53
  617. package/.agents/scripts/check-action-pinning.js +0 -260
  618. package/.agents/scripts/check-audit-attribution.js +0 -302
  619. package/.agents/scripts/check-baseline-drift.js +0 -211
  620. package/.agents/scripts/check-baseline-scope.js +0 -362
  621. package/.agents/scripts/check-generated-validator.js +0 -202
  622. package/.agents/scripts/check-knip-entries.js +0 -159
  623. package/.agents/scripts/check-lifecycle-lint.js +0 -294
  624. package/.agents/scripts/check-pinned-override-notes.js +0 -102
  625. package/.agents/scripts/check-schema-references.js +0 -366
  626. package/.agents/scripts/check-test-portability.js +0 -512
  627. package/.agents/scripts/check-workflow-citations.js +0 -332
  628. package/.agents/scripts/check-workflow-cli-lint.js +0 -299
  629. package/.agents/scripts/check-workflow-timeouts.js +0 -291
  630. package/.agents/scripts/install-matrix-assert.js +0 -326
  631. package/.agents/scripts/lib/audit-advisories.js +0 -195
  632. package/.agents/scripts/lib/audit-attribution.js +0 -134
  633. package/.agents/scripts/lib/baselines/drift-detector.js +0 -351
  634. package/.agents/scripts/lib/baselines/kinds/lighthouse.js +0 -87
  635. package/.agents/scripts/lib/baselines/kinds/lint.js +0 -184
  636. package/.agents/scripts/lib/baselines/orphan-pruner.js +0 -233
  637. package/.agents/scripts/lib/baselines/scope-assert.js +0 -223
  638. package/.agents/scripts/lib/baselines/scope-inventory.js +0 -314
  639. package/.agents/scripts/lib/c8-cli-path.js +0 -21
  640. package/.agents/scripts/lib/config/gates/lighthouse.schema.js +0 -51
  641. package/.agents/scripts/lib/config/gates/lint.schema.js +0 -18
  642. package/.agents/scripts/lib/dynamic-workflow/architecture-report-contract.js +0 -70
  643. package/.agents/scripts/lib/dynamic-workflow/audit-orchestrator.js +0 -284
  644. package/.agents/scripts/lib/dynamic-workflow/clean-code-report-contract.js +0 -80
  645. package/.agents/scripts/lib/dynamic-workflow/degraded-coverage.js +0 -81
  646. package/.agents/scripts/lib/dynamic-workflow/documentation-report-contract.js +0 -87
  647. package/.agents/scripts/lib/dynamic-workflow/performance-report-contract.js +0 -74
  648. package/.agents/scripts/lib/dynamic-workflow/quality-report-contract.js +0 -90
  649. package/.agents/scripts/lib/dynamic-workflow/report-contract-core.js +0 -43
  650. package/.agents/scripts/lib/dynamic-workflow/security-report-contract.js +0 -83
  651. package/.agents/scripts/lib/fs-walk.js +0 -52
  652. package/.agents/scripts/lib/knip-config-resolver.js +0 -181
  653. package/.agents/scripts/lib/knip-entry-sync.js +0 -452
  654. package/.agents/scripts/lib/orchestration/model-attribution.js +0 -418
  655. package/.agents/scripts/lib/orchestration/story-plan-state.js +0 -33
  656. package/.agents/scripts/lib/orchestration/structured-comment-parser.js +0 -67
  657. package/.agents/scripts/lib/pinned-override-notes.js +0 -88
  658. package/.agents/scripts/lib/pinned-override-resolve.js +0 -212
  659. package/.agents/scripts/lib/test-isolate/cli-options.js +0 -93
  660. package/.agents/scripts/lib/test-isolate/env-snapshot-loader.js +0 -52
  661. package/.agents/scripts/lib/test-isolate/list-files.js +0 -90
  662. package/.agents/scripts/lib/test-isolate/parse-tap.js +0 -75
  663. package/.agents/scripts/lib/test-isolate/progress-log.js +0 -45
  664. package/.agents/scripts/lib/test-isolate/render-report.js +0 -97
  665. package/.agents/scripts/lib/test-isolate/run-isolate.js +0 -87
  666. package/.agents/scripts/lib/test-isolate/runner.js +0 -483
  667. package/.agents/scripts/lib/test-profile/parse-tap.js +0 -136
  668. package/.agents/scripts/lib/test-profile/render-report.js +0 -45
  669. package/.agents/scripts/lint-label-vocabulary.js +0 -214
  670. package/.agents/scripts/post-structured-comment.js +0 -127
  671. package/.agents/scripts/provision-git-hooks.js +0 -85
  672. package/.agents/scripts/prune-baseline-orphans.js +0 -181
  673. package/.agents/scripts/run-coverage.js +0 -197
  674. package/.agents/scripts/run-lint.js +0 -133
  675. package/.agents/scripts/run-test-profile.js +0 -129
  676. package/.agents/scripts/run-verify.js +0 -118
  677. package/.agents/scripts/test-isolate.js +0 -55
  678. package/.agents/scripts/update-dead-exports-baseline.js +0 -321
@@ -1,93 +1,21 @@
1
1
  /**
2
- * phases/confirm-merge.js — the close-and-land merge wait (Story #4428,
3
- * reworked into a resumable, checks-aware wait by Story #4543).
2
+ * phases/confirm-merge.js — the close-and-land merge wait, the default
3
+ * terminal step of every close (`--no-wait-merge` opts out).
4
4
  *
5
- * This is the **default terminal step for every run** — attended and
6
- * headless alike — because `waitForMerge` defaults from
7
- * `delivery.routing.closeAndLand` (`true`); `--no-wait-merge` is the opt-out,
8
- * and a PR the operator deliberately left un-armed (`--no-auto-merge` /
9
- * `autoMerge: "strict"`) resolves to no-wait and rests at `agent::closing`
10
- * for the human.
5
+ * Two timing domains: `maxWaitSeconds` bounds THIS invocation (it must fit
6
+ * the host's ~10-min tool ceiling after the close gates) and expires to a
7
+ * resumable `pending` with no mutation; `maxBudgetSeconds` bounds the
8
+ * cumulative wait, anchored at the PR's `createdAt` so resumes don't restart
9
+ * it, and exhausting it is the real give-up (classify, emit, block). The
10
+ * poll is stateless and re-entrant. Async mode only shortens the
11
+ * per-invocation window. Checks are probed every iteration so a red required
12
+ * check fails fast instead of burning the budget.
11
13
  *
12
- * ## The timing model (Story #4543 — the load-bearing design decision)
13
- *
14
- * The original wait polled a single budget: `maxBudgetSeconds`, one hour.
15
- * The host caps a single tool invocation at ~10 minutes, and the close gates
16
- * burn minutes of that before the wait even starts. So a close-and-land
17
- * whose CI took longer than roughly eight minutes was **killed mid-poll**
18
- * with no terminal path taken — no `merge.unlanded` event, no `agent::blocked`
19
- * flip, the Story parked at `agent::closing`: precisely the strand the
20
- * must-land contract exists to eliminate.
21
- *
22
- * The fix splits the two timing domains that were conflated:
23
- *
24
- * - **`maxWaitSeconds`** bounds THIS invocation (default 300s, comfortably
25
- * inside the host ceiling). On expiry the wait returns
26
- * `terminal: 'pending'` — **no label mutation, no `merge.unlanded`
27
- * event** — and the caller surfaces a resumable terminal with its own
28
- * exit code. Merely shrinking `maxBudgetSeconds` instead would have been
29
- * wrong: that path conflates slow CI with a hard block, so most runs
30
- * would have been misfiled as blocked.
31
- * - **`maxBudgetSeconds`** bounds the CUMULATIVE wait, anchored at the
32
- * PR's `createdAt` rather than this invocation's start, so resumes do not
33
- * restart the clock. Exhausting it is the genuine give-up: classify,
34
- * emit, block. `agent::blocked` stays reserved for hard blocks.
35
- *
36
- * Backgrounding is not a workaround here and does not need to be: an
37
- * interrupted poll is stateless and re-entrant by construction.
38
- *
39
- * ## Async mode (Story #4698 — a designed short probe window, not an accident)
40
- *
41
- * `maxWaitSeconds` (default 300s) still routinely EXPIRES on a slow-CI
42
- * consumer: the median PR-create→merge time can be minutes, so nearly every
43
- * close burns its whole foreground slot polling and then returns `pending`
44
- * anyway. `delivery.mergeWatch.mode: "async"` makes that async confirm a
45
- * designed mode rather than an expiry accident. It caps the per-invocation
46
- * wait to a short probe window (`ASYNC_PROBE_WINDOW_SECONDS`, ~60s) — long
47
- * enough for the loop's existing checks to catch an instant merge and, via the
48
- * imported {@link decideMergeWaitFailFast} decision (Story #4695/#4710), an
49
- * instantly-red required check — then returns the SAME resumable `pending`
50
- * terminal, whose `nextCommand` the worker launches in the background. Nothing
51
- * else changes: the cumulative `maxBudgetSeconds` anchor is untouched, and
52
- * `sync` mode (the default) is byte-compatible. An explicit `--max-wait-seconds`
53
- * override wins over the async cap so a headless caller can still land in one
54
- * block. The clamp lives entirely in `resolveMergeWaitConfig`; the poll loop is
55
- * mode-agnostic.
56
- *
57
- * ## The wait is not weaker than the watch it displaced
58
- *
59
- * The pre-#4543 poll read only `state` / `mergedAt`. A check that went red
60
- * at minute one therefore burned the full hour and then classified as
61
- * `branch-protection-human-required` (the exhaustion probe sees
62
- * `mergeStateStatus: BLOCKED` with checks settled) — sending the operator to
63
- * diagnose branch protection instead of their red check. This wait probes the
64
- * checks every iteration: it fails fast on `checks-failed`, and runs a
65
- * bounded `gh pr update-branch` on a BEHIND PR instead of waiting out the
66
- * budget behind a base it could have caught up to.
67
- *
68
- * The per-iteration `provider.getTicket` is also gone. It was re-fetched
69
- * every poll for an idempotence check whose answer cannot change mid-poll —
70
- * ~240 reads per Story per hour. The loop now probes the PR only, and calls
71
- * the shared `confirmStoryMerged` exactly once, after a merge is observed.
72
- *
73
- * Terminal outcomes:
74
- * - `{ confirmed: true, action, tail }` — the PR merged; `confirmStoryMerged`
75
- * flipped `agent::closing → agent::done` and closed the issue, and the
76
- * shared post-land tail ran.
77
- * - `{ confirmed: false, terminal: 'pending', waitBudget }` — this
78
- * invocation's bound expired with the PR still in flight. Resumable;
79
- * nothing was mutated.
80
- * - `{ confirmed: false, terminal: 'blocked', blockClass, reason }` — the
81
- * arm failed outright, the PR closed without merging, a required check
82
- * went red, or the cumulative budget was exhausted. Classified via the
83
- * shared `classifyMergeBlock`, emitted as `merge.unlanded`, friction
84
- * posted, Story transitioned to `agent::blocked`.
85
- * - `{ confirmed: false, terminal: 'blocked', blockClass: 'merged-flip-failed' }`
86
- * — the PR merged but the `agent::done` label write failed. Its own
87
- * `merge.flip-failed` event and friction wording (Story #4539): the merge
88
- * landed, so attributing it to an unlanded merge would send the operator
89
- * to diagnose branch protection instead of re-running the idempotent
90
- * confirm.
14
+ * Terminals: `landed` (confirmed, post-land tail ran); `pending` (nothing
15
+ * mutated); `blocked` (classified, `merge.unlanded` emitted, friction posted,
16
+ * Story → `agent::blocked`); `blocked`/`merged-flip-failed` (merged but the
17
+ * `agent::done` write failed — its own event and wording, since the merge
18
+ * is not in question).
91
19
  */
92
20
 
93
21
  import { getCiDelivery } from '../../../config/ci.js';
@@ -98,6 +26,7 @@ import {
98
26
  } from '../../../single-story/confirm-merge.js';
99
27
  import { pollUntil } from '../../../util/poll-loop.js';
100
28
  import { applyBehindUpdate } from '../../behind-recovery.js';
29
+ import { isRerunPermitted } from '../../check-state.js';
101
30
  import {
102
31
  emitMergeFlipFailed as defaultEmitMergeFlipFailed,
103
32
  MERGED_FLIP_FAILED_BLOCK_CLASS,
@@ -114,6 +43,7 @@ import {
114
43
  deriveChecksStatus,
115
44
  deriveRedHeadRuns,
116
45
  deriveRequiredRunEvidence,
46
+ isPrMerged,
117
47
  MERGE_WAIT_GH_TIMEOUT_MS,
118
48
  parseWorkflowRunId,
119
49
  readRunSummary,
@@ -128,77 +58,38 @@ import {
128
58
  import { disarmAutoMerge } from './auto-merge.js';
129
59
  import { runPostLandTail as defaultRunPostLandTail } from './post-land.js';
130
60
 
131
- /**
132
- * Per-invocation merge-wait bound. 300s fits inside a single host tool
133
- * invocation (~10 min ceiling) with room for the close gates that precede
134
- * the wait. A headless caller with no such ceiling raises
135
- * `delivery.mergeWatch.maxWaitSeconds` to keep single-block semantics.
136
- */
61
+ /** Per-invocation bound; fits the host's ~10-min tool ceiling after the gates. */
137
62
  export const DEFAULT_MAX_WAIT_SECONDS = 300;
138
63
 
139
- /**
140
- * Async-mode per-invocation probe window (Story #4698). When
141
- * `delivery.mergeWatch.mode` is `"async"`, `resolveMergeWaitConfig` caps the
142
- * per-invocation wait to this many seconds so close returns the resumable
143
- * `pending` terminal fast instead of burning the foreground host slot. Sized
144
- * to catch an instant merge and — via the head-anchored required-check
145
- * predicate — an instantly-red required check, while staying far inside the
146
- * cumulative `maxBudgetSeconds` give-up bound.
147
- */
64
+ /** Async-mode per-invocation cap: long enough to catch an instant merge or red check. */
148
65
  export const ASYNC_PROBE_WINDOW_SECONDS = 60;
149
66
 
150
- /** Bounded `gh pr update-branch` attempts for a BEHIND PR. */
151
67
  export const DEFAULT_UPDATE_ATTEMPTS = 3;
152
68
 
153
69
  /**
154
- * Minimum polls before the CUMULATIVE budget may block.
155
- *
156
- * The cumulative clock is anchored at the PR's `createdAt` so resumes do not
157
- * restart it — but that alone means a PR older than `maxBudgetSeconds` (1h by
158
- * default) is already over budget on its very first probe. Resuming a Story
159
- * the next morning, or landing a long-open PR, would then flip
160
- * `agent::blocked` and emit `merge.unlanded` against a perfectly healthy PR
161
- * that was seconds from merging, without ever having waited.
162
- *
163
- * The floor gives every invocation at least one real poll cycle before the
164
- * cumulative bound can fire. A genuinely stuck PR still blocks within one
165
- * interval (~30s), so the give-up bound keeps its meaning; a PR about to go
166
- * green gets the chance it earned.
70
+ * Minimum polls before the cumulative budget may block: the clock is anchored
71
+ * at `createdAt`, so a long-open PR is over budget on its first probe and
72
+ * would otherwise block without ever having waited.
167
73
  */
168
74
  export const MIN_POLLS_BEFORE_BUDGET_BLOCK = 2;
169
75
 
76
+ /** Verdict for a PR closed without merging, decided by the wait itself. */
77
+ const CLOSED_UNMERGED_BLOCK_CLASS = 'api-race-other';
78
+ const CLOSED_UNMERGED_REASON =
79
+ 'PR probe error: PR closed without merging (state=CLOSED)';
80
+
170
81
  function defaultSleep(ms) {
171
82
  return new Promise((resolve) => setTimeout(resolve, ms));
172
83
  }
173
84
 
174
- /**
175
- * The wait's default `gh` facade, bound to a spawn-level timeout (Story
176
- * #4710): every subprocess the wait launches through it carries
177
- * `MERGE_WAIT_GH_TIMEOUT_MS`, so a wedged `gh` child is killed rather than
178
- * stranding an unattended async-mode wait forever. Callers that inject their
179
- * own `gh` (tests, the resume CLI) are bounded by {@link withGhTimeout} at
180
- * the call sites instead.
181
- */
85
+ /** Spawn-level timeout kills a wedged `gh` child; injected `gh`s are bounded by withGhTimeout. */
182
86
  const defaultGh = createGh(undefined, { timeoutMs: MERGE_WAIT_GH_TIMEOUT_MS });
183
87
 
184
88
  /**
185
- * Bound an arbitrary `gh` call with a wall-clock timeout (Story #4710). The
186
- * spawn-level `timeoutMs` on {@link defaultGh} already kills a wedged real
187
- * subprocess, but an injected `gh` implementation (a test stub, a facade
188
- * built without defaults) can still return a promise that never settles —
189
- * and the merge wait must never hang on any of them. Rejection maps to the
190
- * caller's existing error handling: the probe degrades to its conservative
191
- * pending shape, the update-branch attempt logs and continues.
192
- *
193
- * A late settlement of the losing promise is explicitly absorbed so a
194
- * post-timeout rejection cannot surface as an unhandled rejection.
195
- *
196
- * The timeout timer is deliberately NOT `unref`'d: when the awaited call is a
197
- * promise that never settles (a hung stub, or a real gh child whose I/O has
198
- * gone quiet), the timer is the ONLY handle keeping the event loop alive, so
199
- * unref'ing it would let the process/test exit before the timeout ever fires —
200
- * exactly the hang this guard exists to prevent. It is short-lived and always
201
- * cleared in `finally`, so keeping it referenced costs nothing.
89
+ * Wall-clock bound for any `gh` call, including injected ones that may never
90
+ * settle. The losing promise's late rejection is absorbed. The timer is NOT
91
+ * unref'd: for a never-settling call it is the only handle keeping the event
92
+ * loop alive until the timeout fires.
202
93
  */
203
94
  function withGhTimeout(promise, timeoutMs, label) {
204
95
  let timer;
@@ -215,19 +106,11 @@ function withGhTimeout(promise, timeoutMs, label) {
215
106
  }
216
107
 
217
108
  /**
218
- * One string field off a `gh pr view` payload, or `absent` when the API did
219
- * not return one. Deduplicated out of the probe below (Story #5266): six
220
- * identical `typeof x === 'string'` ternaries put that one function over the
221
- * CRAP ratchet the moment a seventh field was needed.
222
- *
223
- * An empty string counts as absent — `gh` returns `""` for a field it cannot
224
- * read, and every caller treats that exactly as "not there".
109
+ * A string field off a `gh pr view` payload; `""` (gh's unreadable field)
110
+ * counts as absent.
225
111
  *
226
112
  * @param {unknown} value
227
- * @param {null|undefined} [absent] What to report when the field is missing.
228
- * `null` for the three fields the poll loop compares against null; the
229
- * `undefined` default for the ones whose absence must not shadow a
230
- * downstream default.
113
+ * @param {null|undefined} [absent] `undefined` where absence must not shadow a downstream default.
231
114
  * @returns {string|null|undefined}
232
115
  */
233
116
  function readString(value, absent = undefined) {
@@ -235,17 +118,8 @@ function readString(value, absent = undefined) {
235
118
  }
236
119
 
237
120
  /**
238
- * One probe per poll iteration, carrying every field the loop and the
239
- * terminal classifier need: merge state, the checks rollup, the merge-state
240
- * status (for BEHIND recovery and human-required classification), and
241
- * `createdAt` (the cumulative-budget anchor).
242
- *
243
- * Returns a degraded `{ checksStatus: 'pending', error }` probe when the read
244
- * itself fails, preserving the conservative classification on probe errors —
245
- * a flaky API read must not be mistaken for a definitive verdict. A probe
246
- * that exceeds `ghTimeoutMs` (Story #4710) takes the SAME degraded path: a
247
- * hung subprocess must surface as a probe error within the bound, never
248
- * strand the wait.
121
+ * One probe per poll iteration. A failed or timed-out read degrades to
122
+ * `{ checksStatus: 'pending', error }` so a flaky read is never a verdict.
249
123
  *
250
124
  * @returns {Promise<object>}
251
125
  */
@@ -263,8 +137,6 @@ export async function readPrWaitProbe({
263
137
  'mergeStateStatus',
264
138
  'reviewDecision',
265
139
  'statusCheckRollup',
266
- // Story #5266 — the head the red advisory runs belong to, so their
267
- // check-run output can be read back and classified.
268
140
  'headRefOid',
269
141
  ]),
270
142
  ghTimeoutMs,
@@ -277,14 +149,9 @@ export async function readPrWaitProbe({
277
149
  mergeStateStatus: readString(view?.mergeStateStatus),
278
150
  reviewDecision: readString(view?.reviewDecision),
279
151
  checksStatus: deriveChecksStatus(view?.statusCheckRollup),
280
- // Head-anchored per-run evidence (Story #4695): distinguishes a
281
- // genuinely red required run from the superseded / still-pending noise
282
- // the aggregate `checksStatus` folds together. `null` when the rollup is
283
- // absent/empty — the loop's consecutive-probe fallback owns that path.
152
+ // Head-anchored, so superseded/pending runs don't read as red; `null`
153
+ // when the rollup is empty (the consecutive-probe fallback owns that).
284
154
  requiredRunEvidence: deriveRequiredRunEvidence(view?.statusCheckRollup),
285
- // The named red head runs (Story #5096), so an `advisory-gate-red`
286
- // verdict can name the offending job and match the allowlist. Same
287
- // red-ness test as `requiredRunEvidence`, so the two cannot disagree.
288
155
  redHeadRuns: deriveRedHeadRuns(view?.statusCheckRollup),
289
156
  headSha: readString(view?.headRefOid),
290
157
  };
@@ -301,31 +168,11 @@ export async function readPrWaitProbe({
301
168
  }
302
169
 
303
170
  /**
304
- * Resolve the wait cadence and both budgets from `delivery.mergeWatch.*`,
305
- * falling back to the framework defaults when a key is absent or invalid.
306
- *
307
- * `maxWaitSecondsOverride` is the per-run `--max-wait-seconds` flag and wins
308
- * over the config: a headless caller with no host tool-invocation ceiling
309
- * raises the per-invocation bound to keep single-block semantics without
310
- * editing the consumer's config.
311
- *
312
- * `mode` (Story #4698) selects the close-time merge posture. `async` caps the
313
- * per-invocation wait to `ASYNC_PROBE_WINDOW_SECONDS` so close returns the
314
- * resumable `pending` terminal fast; `sync` (the default) is unchanged. An
315
- * explicit `maxWaitSecondsOverride` still wins over the async cap — a headless
316
- * caller with no host ceiling opts back into single-block waiting.
317
- *
318
- * `modeOverride` is the per-invocation `--merge-watch-mode` flag (Story #4949)
319
- * and wins over `delivery.mergeWatch.mode` on exactly the precedence
320
- * `maxWaitSecondsOverride` already uses. It exists because run topology is
321
- * knowable only to the orchestrator: close sees one Story and cannot tell a
322
- * solo delivery (where a foreground wait is the cheapest ending) from the Nth
323
- * close of a wave (where each foreground wait is serialized dead time). The
324
- * config default therefore stays `sync`, and the caller that knows better says
325
- * so per invocation. The two flags remain composable — `--merge-watch-mode
326
- * async --max-wait-seconds 900` selects the async posture and then overrides
327
- * its probe cap, because the cap check below keys on the override's presence,
328
- * not on where the mode came from.
171
+ * Resolve posture and budgets from `delivery.mergeWatch.*`. Both overrides
172
+ * (`--max-wait-seconds`, `--merge-watch-mode`) win over config; the mode flag
173
+ * exists because only the orchestrator knows whether a foreground wait is
174
+ * cheap (solo) or serialized dead time (a wave). An explicit max-wait
175
+ * override also beats the async cap.
329
176
  *
330
177
  * @param {object} [config]
331
178
  * @param {number} [maxWaitSecondsOverride]
@@ -338,37 +185,21 @@ export function resolveMergeWaitConfig(
338
185
  modeOverride,
339
186
  ) {
340
187
  const mergeWatch = config?.delivery?.mergeWatch ?? {};
341
- const int = (value, fallback, min = 1) =>
342
- Number.isInteger(value) && value >= min ? value : fallback;
188
+ const int = (value, fallback) =>
189
+ Number.isInteger(value) && value >= 1 ? value : fallback;
343
190
  const requestedMode = modeOverride ?? mergeWatch.mode;
344
191
  const mode = requestedMode === 'async' ? 'async' : 'sync';
345
192
  const configuredMaxWait = int(
346
193
  maxWaitSecondsOverride,
347
194
  int(mergeWatch.maxWaitSeconds, DEFAULT_MAX_WAIT_SECONDS),
348
195
  );
349
- // Async mode caps the per-invocation wait to a short probe window so close
350
- // returns `pending` fast instead of burning the foreground host slot on a
351
- // merge that lands after the wait would have expired anyway. The window is
352
- // long enough for the loop's existing checks to catch an instant merge and —
353
- // via the imported `decideMergeWaitFailFast` decision — an instantly
354
- // red required check. An explicit `--max-wait-seconds` override still wins so
355
- // a headless caller with no host ceiling opts back into single-block waiting.
356
196
  const maxWaitSeconds =
357
197
  mode === 'async' && maxWaitSecondsOverride == null
358
198
  ? Math.min(configuredMaxWait, ASYNC_PROBE_WINDOW_SECONDS)
359
199
  : configuredMaxWait;
360
- // A poll interval longer than the wait bound is incoherent, and silently
361
- // harmful: the pending check would fire on poll 1 every time, so the wait
362
- // could never sleep, `polls` could never reach
363
- // MIN_POLLS_BEFORE_BUDGET_BLOCK, and the cumulative budget would become
364
- // unreachable across ANY number of resumes — a Story stuck in permanent
365
- // `pending` that never escalates. Clamping the interval to the bound keeps
366
- // at least one real poll cycle possible, which is what both the floor and
367
- // the give-up bound depend on.
368
- const intervalSeconds = Math.min(
369
- int(mergeWatch.intervalSeconds, DEFAULT_INTERVAL_SECONDS),
370
- maxWaitSeconds,
371
- );
200
+ // An interval longer than the bound would expire every invocation on poll
201
+ // 1, so MIN_POLLS_BEFORE_BUDGET_BLOCK and the budget could never be reached.
202
+ const intervalSeconds = Math.min(DEFAULT_INTERVAL_SECONDS, maxWaitSeconds);
372
203
  return {
373
204
  mode,
374
205
  intervalSeconds,
@@ -377,16 +208,13 @@ export function resolveMergeWaitConfig(
377
208
  mergeWatch.maxBudgetSeconds,
378
209
  DEFAULT_MAX_BUDGET_SECONDS,
379
210
  ),
380
- updateAttempts: int(mergeWatch.updateAttempts, DEFAULT_UPDATE_ATTEMPTS, 0),
211
+ updateAttempts: DEFAULT_UPDATE_ATTEMPTS,
381
212
  };
382
213
  }
383
214
 
384
215
  /**
385
- * Anchor the cumulative budget at the PR's `createdAt` so a resumed wait
386
- * does not restart the clock. Falls back to this invocation's start when the
387
- * probe carried no timestamp — a conservative degrade: the worst case is a
388
- * resume getting a fresh cumulative budget, which is exactly the pre-#4543
389
- * behaviour, never a premature block.
216
+ * Budget anchor; without `createdAt` it falls back to this invocation's start
217
+ * (worst case a fresh budget, never a premature block).
390
218
  *
391
219
  * @returns {number} epoch ms
392
220
  */
@@ -397,22 +225,9 @@ export function resolveBudgetAnchorMs({ createdAt, fallbackMs }) {
397
225
  }
398
226
 
399
227
  /**
400
- * The advisory-gate half of {@link unlandedRemedy} (Story #5279).
401
- *
402
- * Both advisory classes used to fall through to the generic remedy, which
403
- * opens by telling the operator to resolve "branch protection, required
404
- * checks, or a manual merge" — a diagnosis of a fault that provably does not
405
- * exist here. An advisory gate blocks precisely BECAUSE GitHub reports the PR
406
- * mergeable (`mergeStateStatus=UNSTABLE`) over a NON-required red check, so
407
- * close refused to let native auto-merge land it. Saying so is the paragraph
408
- * that stops the operator hunting a protection rule that is working fine.
409
- *
410
- * The two classes then part on what the evidence authorises. `inconclusive`
411
- * is a job that failed WITHOUT FINISHING and reported no violation — nothing
412
- * says the change is bad — so the proportionate act is a re-run, named here
413
- * as `--rerun-advisory`; the permanent global exemption is deliberately
414
- * mentioned last. `advisory-gate-red` reported a real violation, so the
415
- * change is implicated and landing over it is a deliberate override.
228
+ * Advisory-gate remedy: the PR is mergeable (UNSTABLE over a non-required
229
+ * red), so branch protection is not the fault. `inconclusive` never finished
230
+ * and implicates nothing (re-run first); `red` reported a real violation.
416
231
  *
417
232
  * @param {{ storyId: number, blockClass: string }} args
418
233
  * @returns {string}
@@ -444,10 +259,6 @@ function advisoryGateRemedy({ storyId, blockClass }) {
444
259
  }
445
260
 
446
261
  /**
447
- * The class-specific remediation paragraph of the unlanded friction comment.
448
- * Split out of {@link formatUnlandedFriction} so each class's wording is one
449
- * named branch rather than a nested ternary.
450
- *
451
262
  * @param {{ storyId: number, prNumber: number|null, blockClass: string }} args
452
263
  * @returns {string}
453
264
  */
@@ -473,10 +284,6 @@ function unlandedRemedy({ storyId, prNumber, blockClass }) {
473
284
  );
474
285
  }
475
286
 
476
- /**
477
- * Format the `friction` comment body posted alongside the `agent::blocked`
478
- * transition when a landing attempt gives up without a confirmed merge.
479
- */
480
287
  function formatUnlandedFriction({
481
288
  storyId,
482
289
  prNumber,
@@ -500,13 +307,7 @@ function formatUnlandedFriction({
500
307
  );
501
308
  }
502
309
 
503
- /**
504
- * Format the `friction` comment for a merge that **landed** while the
505
- * `agent::done` label write failed. Deliberately not the unlanded wording:
506
- * the merge is not in question, so pointing the operator at branch
507
- * protection and required checks would send them to diagnose a fault that
508
- * does not exist. Name the actual remedy instead.
509
- */
310
+ /** Friction for a merge that landed but whose `agent::done` write failed. */
510
311
  function formatFlipFailedFriction({
511
312
  storyId,
512
313
  prNumber,
@@ -535,10 +336,7 @@ function formatFlipFailedFriction({
535
336
  }
536
337
 
537
338
  /**
538
- * Post a friction comment best-effort and return its id when the provider
539
- * surfaces one. The id is the terminal envelope's `frictionCommentId`
540
- * pointer, so a caller can link the operator straight at the remediation
541
- * instead of telling them to go find it.
339
+ * Best-effort; the id becomes the envelope's `frictionCommentId`.
542
340
  *
543
341
  * @returns {Promise<string|null>}
544
342
  */
@@ -561,12 +359,7 @@ async function postFriction({ provider, storyId, body, progress }) {
561
359
  }
562
360
  }
563
361
 
564
- /**
565
- * Terminal for a confirmed merge whose `agent::done` flip failed. Emits
566
- * `merge.flip-failed` (NOT `merge.unlanded` — the merge landed), posts the
567
- * flip-failed friction, and blocks explicitly. Best-effort throughout: the
568
- * caller owns the non-zero exit.
569
- */
362
+ /** Emits `merge.flip-failed` (not `merge.unlanded`); best-effort throughout. */
570
363
  async function blockOnFlipFailed({
571
364
  storyId,
572
365
  prNumber,
@@ -628,25 +421,17 @@ async function blockOnFlipFailed({
628
421
  reason,
629
422
  frictionCommentId,
630
423
  elapsedSeconds,
631
- // The merge is CONFIRMED here — only the label write failed — so the
632
- // envelope must say MERGED even when the probe that got us here was read
633
- // before the merge landed. Reporting the stale OPEN (or null) would tell
634
- // the operator to chase a merge that already happened.
424
+ // The merge is confirmed even if the probe was read before it landed.
635
425
  prProbe: { ...(prProbe ?? {}), state: 'MERGED' },
636
426
  };
637
427
  }
638
428
 
639
429
  /**
640
- * Story #5266 — the per-invocation advisory rerun allowance.
430
+ * `--rerun-advisory` beats `delivery.ci.rerunAdvisory`; both default 0
431
+ * because a rerun spends CI minutes. An invalid override falls back to config.
641
432
  *
642
- * `--rerun-advisory <n>` wins over `delivery.ci.rerunAdvisory` on exactly the
643
- * precedence `--max-wait-seconds` already uses. Both default to **0**: a
644
- * rerun spends CI minutes and mutates GitHub state, so close does neither
645
- * unasked. A non-integer or negative override is not an instruction to guess
646
- * — it degrades to the config value.
647
- *
648
- * @param {object|null} config Resolved config (or any `delivery.ci` bag).
649
- * @param {number} [override] The `--rerun-advisory` value, when supplied.
433
+ * @param {object|null} config
434
+ * @param {number} [override]
650
435
  * @returns {number} allowance ≥ 0
651
436
  */
652
437
  export function resolveAdvisoryRerunAllowance(config, override) {
@@ -655,10 +440,8 @@ export function resolveAdvisoryRerunAllowance(config, override) {
655
440
  }
656
441
 
657
442
  /**
658
- * Identify ONE observation of a red run: the job, the workflow run behind it,
659
- * and when that run finished. A rerun changes `completedAt` (and eventually
660
- * the conclusion), so this is what lets the wait tell a re-run's verdict apart
661
- * from the stale pre-rerun snapshot it will keep seeing for a poll or two.
443
+ * One observation of a red run; a rerun changes `completedAt`, which tells a
444
+ * fresh verdict from the stale pre-rerun snapshot.
662
445
  *
663
446
  * @param {{name?: string|null, runId?: number, completedAt?: string}} run
664
447
  * @returns {string}
@@ -668,17 +451,9 @@ function advisoryRunSignature(run) {
668
451
  }
669
452
 
670
453
  /**
671
- * Story #5266 — read each red advisory run's own account of WHY it failed.
672
- *
673
- * `gh pr view --json statusCheckRollup` has a fixed projection that carries no
674
- * output text for a CheckRun, so on the rollup alone every red advisory run
675
- * looks identical — which is the defect. The check-runs API for the head SHA
676
- * carries `output.title` / `output.summary`, and ONE call for the whole head
677
- * is enough to classify every red run on it.
678
- *
679
- * Called only on the block path (never per poll), and **fails open**: any
680
- * error, timeout, or missing head SHA returns the runs unchanged, which
681
- * classifies them as `advisory-gate-red` — the pre-#5266 verdict.
454
+ * Attach each red run's check-run output (the rollup carries none), in one
455
+ * call per head. Block path only; fails open to the unenriched runs, which
456
+ * classify as `advisory-gate-red`.
682
457
  *
683
458
  * @returns {Promise<Array<object>>} the runs, enriched where output was found
684
459
  */
@@ -720,17 +495,10 @@ async function enrichRedRunsWithOutput({
720
495
  }
721
496
 
722
497
  /**
723
- * Story #5266 — re-run the failed advisory workflow run(s), once, within the
724
- * caller's remaining allowance.
725
- *
726
- * Requests `rerun-failed-jobs` per distinct workflow run rather than per job:
727
- * one advisory workflow commonly fans out, and re-running the whole failed set
728
- * is both cheaper in API calls and what an operator means by "re-run it".
498
+ * `rerun-failed-jobs` once per distinct workflow run (not per job).
729
499
  *
730
- * @returns {Promise<boolean>} whether every rerun request succeeded. `false`
731
- * (including "no run id to re-run") leaves the caller on the block path,
732
- * because an allowance that cannot be spent must not silently suppress the
733
- * gate.
500
+ * @returns {Promise<boolean>} `false` (incl. no run id) keeps the caller on
501
+ * the block path — an unspendable allowance must not suppress the gate.
734
502
  */
735
503
  async function rerunAdvisoryRuns({ blockingRuns, gh, ghTimeoutMs, progress }) {
736
504
  const runIds = [
@@ -775,16 +543,20 @@ async function rerunAdvisoryRuns({ blockingRuns, gh, ghTimeoutMs, progress }) {
775
543
  }
776
544
 
777
545
  /**
778
- * Spend one unit of the rerun allowance, if there is one and the rerun takes.
779
- * Records the observation signature of every run it re-ran, so the stale
780
- * pre-rerun snapshot the next poll reads does not re-block on the same job.
546
+ * Advisory runs are non-required by construction; a red required check
547
+ * fails fast as `checks-failed` and never reaches the rerun path.
781
548
  *
782
- * Deliberately does NOT disarm first, unlike the block path: the whole point
783
- * of a rerun is that a green re-run lands the PR on its own. The cost is an
784
- * armed window in which GitHub could land the PR over the still-red advisory
785
- * if the required contexts go green before the re-run reports — which is
786
- * exactly what opting in to `--rerun-advisory` buys and accepts. At the
787
- * default 0 there is no such window.
549
+ * @param {{ remaining: number }} rerunState
550
+ * @returns {number}
551
+ */
552
+ function advisoryRerunsLeft(rerunState) {
553
+ return isRerunPermitted({ required: false }) ? rerunState.remaining : 0;
554
+ }
555
+
556
+ /**
557
+ * Spend one rerun unit, recording each re-run's signature so the stale
558
+ * snapshot doesn't re-block. Does NOT disarm: a green re-run should land on
559
+ * its own; the armed window is what opting in to `--rerun-advisory` accepts.
788
560
  *
789
561
  * @returns {Promise<boolean>} `true` when the caller should keep polling.
790
562
  */
@@ -795,7 +567,7 @@ async function maybeRerunAdvisory({
795
567
  ghTimeoutMs,
796
568
  progress,
797
569
  }) {
798
- if (rerunState.remaining <= 0) return false;
570
+ if (advisoryRerunsLeft(rerunState) <= 0) return false;
799
571
  const rerun = await rerunAdvisoryRuns({
800
572
  blockingRuns,
801
573
  gh,
@@ -811,22 +583,10 @@ async function maybeRerunAdvisory({
811
583
  }
812
584
 
813
585
  /**
814
- * Story #5096 — resolve the advisory-gate terminal for one poll.
815
- *
816
- * Takes the poll's current `unlanded` and returns it unchanged when a terminal
817
- * is already decided, so the caller is a single assignment with NO added
818
- * branch. `runMergePoll` is already above `check-cyclomatic`'s ceiling; the
819
- * three decision points this would otherwise cost inline are a real gate
820
- * regression, and they belong with the policy either way.
821
- *
822
- * Disarms BEFORE returning the terminal: an armed PR can merge out from under
823
- * the block the caller is about to record.
824
- *
825
- * Story #5266 threads three more steps through the same single assignment:
826
- * runs this invocation already re-ran (and has no fresh verdict for) are
827
- * skipped rather than re-blocked; the survivors are enriched with their own
828
- * check-run output so the class can be `advisory-gate-inconclusive`; and a
829
- * remaining rerun allowance is spent before any block is recorded.
586
+ * Advisory-gate terminal for one poll; passes a decided `unlanded` through so
587
+ * the caller stays branch-free (`runMergePoll` is at the cyclomatic ceiling).
588
+ * Skips runs already re-ran, enriches the rest, spends any rerun allowance,
589
+ * and disarms BEFORE returning a block so the PR cannot merge out from under it.
830
590
  */
831
591
  async function resolveAdvisoryUnlanded({
832
592
  unlanded,
@@ -848,9 +608,6 @@ async function resolveAdvisoryUnlanded({
848
608
  advisoryAllowlist,
849
609
  });
850
610
  if (!advisory) return null;
851
- // Every blocking run is one this invocation already re-ran and has not seen
852
- // a fresh verdict for yet (Story #5266) — keep polling rather than blocking
853
- // on the snapshot the rerun was meant to replace.
854
611
  const pending = advisory.blockingRuns.filter(
855
612
  (run) => !rerunState.issued.has(advisoryRunSignature(run)),
856
613
  );
@@ -887,12 +644,7 @@ async function resolveAdvisoryUnlanded({
887
644
  };
888
645
  }
889
646
 
890
- /**
891
- * Classify the unlanded merge, emit `merge.unlanded`, post a `friction`
892
- * comment, and transition the Story to `agent::blocked`. Every side effect
893
- * is best-effort logged rather than thrown — the caller owns surfacing the
894
- * non-zero exit once this returns.
895
- */
647
+ /** Classify, emit `merge.unlanded`, post friction, block — all best-effort. */
896
648
  async function blockOnUnlanded({
897
649
  storyId,
898
650
  prNumber,
@@ -907,11 +659,9 @@ async function blockOnUnlanded({
907
659
  blockClassOverride,
908
660
  reasonOverride,
909
661
  }) {
910
- // Story #5096 — `advisory-gate-red` is emitted DIRECTLY, never derived.
911
- // `classifyMergeBlock` cannot produce it: by construction GitHub is NOT
912
- // gating this merge (`mergeStateStatus: UNSTABLE`), which is the entire
913
- // condition the class names, so every classifier heuristic reads the PR as
914
- // healthy.
662
+ // A verdict decided at detection is emitted as-is, never re-derived (the
663
+ // classifier reads an advisory-gate PR as healthy); the classifier runs
664
+ // only for arm-failure and budget exhaustion.
915
665
  const { blockClass, reason } = blockClassOverride
916
666
  ? {
917
667
  blockClass: blockClassOverride,
@@ -923,11 +673,7 @@ async function blockOnUnlanded({
923
673
  budget,
924
674
  });
925
675
  const elapsedSeconds = budget?.elapsedSeconds ?? 0;
926
- // Which evidence path produced a `checks-failed` verdict (Story #4695):
927
- // `per-run` (head-anchored required-run evidence) or `consecutive-probe`
928
- // (the evidence-unavailable fallback). Named on the emitted record so the
929
- // `merge.unlanded` telemetry attributes the fail-fast to the path that
930
- // fired it. Absent for every other block class.
676
+ // `per-run` | `consecutive-probe` for `checks-failed`; absent otherwise.
931
677
  const evidencePath = prProbe?.evidencePath;
932
678
 
933
679
  if (Number.isInteger(prNumber) && prNumber > 0) {
@@ -988,27 +734,14 @@ async function blockOnUnlanded({
988
734
  reason,
989
735
  frictionCommentId,
990
736
  elapsedSeconds,
991
- // The probe the classifier just read. The terminal envelope reports
992
- // `pr.state` / `pr.checksStatus` from here; dropping it made every
993
- // blocked envelope claim `null` for facts we had just observed — a
994
- // `checks-failed` envelope reporting `checksStatus: null` contradicts
995
- // itself. Schema wants "live PR facts as observed at terminal time".
737
+ // The envelope's `pr.state` / `pr.checksStatus` come from here.
996
738
  prProbe,
997
739
  };
998
740
  }
999
741
 
1000
742
  /**
1001
- * Bring a BEHIND PR up to date, bounded by `updateAttempts`. Best-effort:
1002
- * a failed update is not itself a terminal — the next poll re-reads the
1003
- * real state and lets the normal classification decide, which is why a
1004
- * failed attempt still counts against the wait's tick.
1005
- *
1006
- * The BEHIND / budget / did-it-land decision itself lives in the shared
1007
- * {@link applyBehindUpdate} (Story #5006) — the CI-watch loop in
1008
- * `lib/orchestration/pr-watch.js` runs the same one. This wrapper supplies
1009
- * the merge wait's probe source, its `gh` facade (bounded by
1010
- * {@link withGhTimeout}, so a wedged child cannot strand an unattended
1011
- * async-mode wait), and its operator wording.
743
+ * Update a BEHIND PR, bounded by `updateAttempts`; a failed update is not a
744
+ * terminal (the next poll re-reads) but still counts as an attempt.
1012
745
  *
1013
746
  * @returns {Promise<boolean>} whether an update was actually attempted.
1014
747
  */
@@ -1050,11 +783,7 @@ async function maybeUpdateBehindPr({
1050
783
  return recovery.attempted;
1051
784
  }
1052
785
 
1053
- /**
1054
- * Handle an observed merge: run the shared `confirmStoryMerged` flip, then
1055
- * the shared post-land tail. Called at most once per wait — the loop probes
1056
- * the PR, not the ticket.
1057
- */
786
+ /** Called at most once per wait: the flip, then the post-land tail. */
1058
787
  async function onMergeObserved({
1059
788
  storyId,
1060
789
  storyBranch,
@@ -1085,15 +814,13 @@ async function onMergeObserved({
1085
814
  injectedGh,
1086
815
  injectedNotify,
1087
816
  readPrMergeStateFn,
817
+ // Saves confirmation a second `gh pr view`.
818
+ prState: prProbe,
1088
819
  });
1089
820
 
1090
821
  if (confirmation.merged && confirmation.action === 'flip-failed') {
1091
- // The PR merged but the agent::closing → agent::done label flip itself
1092
- // threw. Blocking explicitly is right — reporting confirmed:true would
1093
- // strand the Story at agent::closing with no notification. Reporting it
1094
- // as UNLANDED was not (Story #4539): the merge landed, so the
1095
- // merge.unlanded event would be false and its friction would send the
1096
- // operator to branch protection instead of the one-line remedy.
822
+ // Block explicitly (confirmed:true would strand it at agent::closing),
823
+ // but not as unlanded — the merge landed.
1097
824
  progress?.(
1098
825
  'CONFIRM',
1099
826
  `⚠️ Story #${storyId} merge confirmed but the agent::done flip failed — blocking explicitly.`,
@@ -1128,11 +855,7 @@ async function onMergeObserved({
1128
855
  terminal: 'landed',
1129
856
  action: confirmation.action,
1130
857
  tail,
1131
- // Carry the OBSERVED rollup rather than stamping 'success'. A merge landed
1132
- // by admin override, or with non-required checks red, must not be reported
1133
- // as a green run nobody actually saw — that is the same
1134
- // report-an-outcome-you-never-checked shape the land tail's per-step
1135
- // booleans exist to prevent.
858
+ // The observed rollup, not a stamped 'success' (admin merges exist).
1136
859
  prProbe,
1137
860
  };
1138
861
  }
@@ -1152,29 +875,20 @@ async function onMergeObserved({
1152
875
  * @param {string|null} args.autoMergeReason
1153
876
  * @param {object} args.provider
1154
877
  * @param {object} [args.config]
1155
- * @param {'sync'|'async'} [args.mergeWatchMode] Per-invocation
1156
- * `--merge-watch-mode` override (Story #4949); wins over
1157
- * `delivery.mergeWatch.mode`.
878
+ * @param {'sync'|'async'} [args.mergeWatchMode]
1158
879
  * @param {(tag: string, msg: string) => void} [args.progress]
1159
- * @param {number} [args.rerunAdvisory] `--rerun-advisory <n>` — the
1160
- * per-invocation override of `delivery.ci.rerunAdvisory` (both default 0).
880
+ * @param {number} [args.rerunAdvisory]
1161
881
  * @param {object} [args.injectedGh]
1162
882
  * @param {Function} [args.injectedNotify]
1163
- * @param {Function} [args.confirmStoryMergedFn] Test seam — defaults to the
1164
- * SAME `confirmStoryMerged` export `single-story-confirm-merge.js` calls
1165
- * (Story #4428 AC4: one merged/`agent::done` implementation).
1166
- * @param {Function} [args.readPrWaitProbeFn] Test seam for the poll probe.
1167
- * @param {Function} [args.readPrMergeStateFn] Test seam for the PR-state reader.
1168
- * @param {Function} [args.classifyMergeBlockFn] Test seam for the classifier.
1169
- * @param {Function} [args.emitMergeUnlandedFn] Test seam for the emitter.
1170
- * @param {Function} [args.runPostLandTailFn] Test seam for the land tail.
1171
- * @param {(ms: number) => Promise<void>} [args.sleepFn] Test seam so the
1172
- * suite does not actually wait.
1173
- * @param {() => number} [args.nowMsFn] Test seam; returns epoch ms.
1174
- * @param {number} [args.ghTimeoutMs] Wall-clock bound for each `gh` call the
1175
- * wait makes (Story #4710). A framework constant
1176
- * (`MERGE_WAIT_GH_TIMEOUT_MS`), overridable only as a test seam — not
1177
- * config.
883
+ * @param {Function} [args.confirmStoryMergedFn] The one shared merged/`agent::done` implementation.
884
+ * @param {Function} [args.readPrWaitProbeFn]
885
+ * @param {Function} [args.readPrMergeStateFn]
886
+ * @param {Function} [args.classifyMergeBlockFn]
887
+ * @param {Function} [args.emitMergeUnlandedFn]
888
+ * @param {Function} [args.runPostLandTailFn]
889
+ * @param {(ms: number) => Promise<void>} [args.sleepFn]
890
+ * @param {() => number} [args.nowMsFn]
891
+ * @param {number} [args.ghTimeoutMs] Test seam only, not config.
1178
892
  * @returns {Promise<object>}
1179
893
  */
1180
894
  export async function runConfirmMergePhase({
@@ -1207,10 +921,7 @@ export async function runConfirmMergePhase({
1207
921
  nowMsFn = Date.now,
1208
922
  ghTimeoutMs = MERGE_WAIT_GH_TIMEOUT_MS,
1209
923
  }) {
1210
- // The arm itself never succeeded (gh failure, unparseable PR number, or a
1211
- // deliberate disablement) — there is no "armed but unconfirmed" PR to
1212
- // poll. An explicit terminal state is still required, so classify and
1213
- // block immediately rather than resting silently.
924
+ // Never armed: nothing to poll, but a terminal is still required.
1214
925
  if (!autoMergeEnabled) {
1215
926
  progress?.(
1216
927
  'CONFIRM',
@@ -1226,12 +937,8 @@ export async function runConfirmMergePhase({
1226
937
  progress,
1227
938
  classifyMergeBlockFn,
1228
939
  emitMergeUnlandedFn,
1229
- // Story #5096 — the arm phase already refused over a red advisory gate;
1230
- // carry its verdict through instead of letting the classifier read this
1231
- // as a generic `arm-failure`.
1232
- // Story #5266 — carry the arm phase's CLASS too: a pre-arm refusal over
1233
- // a scan that never finished is `advisory-gate-inconclusive`, and
1234
- // hard-coding the red class here would relabel it at the terminal.
940
+ // Carry the arm phase's advisory verdict and class (which may be
941
+ // `inconclusive`) rather than a generic `arm-failure`.
1235
942
  ...(autoMergeReason === 'advisory-gate-red'
1236
943
  ? {
1237
944
  blockClassOverride: advisoryGate?.blockClass ?? 'advisory-gate-red',
@@ -1252,11 +959,8 @@ export async function runConfirmMergePhase({
1252
959
  maxWaitSecondsOverride,
1253
960
  mergeWatchModeOverride,
1254
961
  );
1255
- // Story #5096 — the advisory-gate knobs, read once for the whole wait.
1256
962
  const { blockOnAdvisoryFailure, advisoryAllowlist } = getCiDelivery(config);
1257
- // Story #5266 — the rerun allowance and its ledger, spent across the whole
1258
- // wait rather than per poll, so `n` bounds the CI minutes this invocation
1259
- // can cost no matter how many times the gate is observed red.
963
+ // Spent across the whole wait, not per poll, so `n` bounds CI minutes.
1260
964
  const rerunAllowance = resolveAdvisoryRerunAllowance(
1261
965
  config,
1262
966
  rerunAdvisoryOverride,
@@ -1271,11 +975,8 @@ export async function runConfirmMergePhase({
1271
975
  let anchorMs = startedAtMs;
1272
976
  let updatesUsed = 0;
1273
977
  let polls = 0;
1274
- // Consecutive failing check probes observed WITHOUT per-run evidence
1275
- // (Story #4695). The evidence-unavailable fallback: a single failing rollup
1276
- // snapshot never fail-fasts — two consecutive failing probes at least one
1277
- // poll interval apart are required. Reset on any non-failing (or genuinely
1278
- // evidenced) probe.
978
+ // Without per-run evidence, fail fast only after two consecutive failing
979
+ // probes an interval apart; reset on any other probe.
1279
980
  let consecutiveRequiredFailSnapshots = 0;
1280
981
 
1281
982
  progress?.(
@@ -1285,19 +986,7 @@ export async function runConfirmMergePhase({
1285
986
  `cumulative budget=${maxBudgetSeconds}s)...`,
1286
987
  );
1287
988
 
1288
- /**
1289
- * One poll iteration. Returns `{ done: false }` to keep polling, or
1290
- * `{ done: true, outcome }` with the phase's terminal. Story #4873 lifted
1291
- * this body out of a bespoke unbounded loop so the cadence is owned by the
1292
- * shared {@link pollUntil} primitive — the loop below sleeps, aborts, and
1293
- * counts ticks in exactly one place for every wait in the codebase. Every
1294
- * budget, floor, and classification decision is unchanged; only who owns the
1295
- * `await sleep(...)` moved.
1296
- *
1297
- * A throw from any of the terminal handlers is captured rather than allowed
1298
- * to escape into `pollUntil` (which treats a throwing `fn` as a non-match
1299
- * and would spin forever on it); the caller re-throws it after the loop.
1300
- */
989
+ /** One iteration: `{ done: false }` or `{ done: true, outcome }`. */
1301
990
  async function runMergePoll() {
1302
991
  const probe = await readPrWaitProbeFn({
1303
992
  prNumber,
@@ -1306,8 +995,6 @@ export async function runConfirmMergePhase({
1306
995
  });
1307
996
  polls += 1;
1308
997
 
1309
- // Anchor the cumulative budget at the PR's creation the first time we
1310
- // learn it, so a resumed wait continues the clock instead of restarting.
1311
998
  anchorMs = resolveBudgetAnchorMs({
1312
999
  createdAt: probe.createdAt,
1313
1000
  fallbackMs: startedAtMs,
@@ -1322,12 +1009,7 @@ export async function runConfirmMergePhase({
1322
1009
  maxBudgetSeconds,
1323
1010
  };
1324
1011
 
1325
- // Heartbeat (Story #4873). A backgrounded close writes this phase's
1326
- // progress to its own output file, and between the opening banner and the
1327
- // terminal there used to be NOTHING for minutes at a time — so an
1328
- // orchestrator watching that file could not tell a healthy in-flight wait
1329
- // from a wedged process without going back to GitHub itself. One line per
1330
- // poll makes the file's own growth the liveness signal.
1012
+ // Heartbeat: a backgrounded close's output-file growth is its liveness signal.
1331
1013
  progress?.(
1332
1014
  'CONFIRM',
1333
1015
  `⏱ poll ${polls}: PR #${prNumber} state=${probe.state ?? 'unknown'} ` +
@@ -1338,7 +1020,7 @@ export async function runConfirmMergePhase({
1338
1020
  (probe.error ? ` — probe error: ${probe.error}` : ''),
1339
1021
  );
1340
1022
 
1341
- if (probe.state === 'MERGED' || probe.mergedAt) {
1023
+ if (isPrMerged(probe)) {
1342
1024
  return doneWith(
1343
1025
  await onMergeObserved({
1344
1026
  storyId,
@@ -1362,38 +1044,21 @@ export async function runConfirmMergePhase({
1362
1044
  );
1363
1045
  }
1364
1046
 
1365
- // Everything below funnels into ONE terminal exit (Story #4710): each
1366
- // definitive condition fills `unlanded` and the single call site at the
1367
- // bottom classifies, emits, and blocks — the fail-fast tree used to
1368
- // duplicate that block twice inline.
1047
+ // Each definitive condition fills `unlanded`; one call site below blocks.
1369
1048
  let unlanded = null;
1370
1049
 
1371
1050
  if (probe.state === 'CLOSED') {
1372
- // Closed without merging — a definitive terminal, not a "still
1373
- // pending" condition the budget should keep waiting on. checksStatus
1374
- // MUST be a non-pending, non-undefined value here: the classifier's
1375
- // budget-exhausted branch treats an undefined checksStatus as "still
1376
- // pending", which would misclassify this definitive case as
1377
- // checks-pending-timeout instead of reaching the api-race-other
1378
- // reason built from prProbe.error.
1379
1051
  unlanded = {
1380
- prProbe: {
1381
- checksStatus: 'closed',
1382
- error: 'PR closed without merging (state=CLOSED)',
1383
- },
1052
+ prProbe: probe,
1384
1053
  budget: {
1385
1054
  exhausted: true,
1386
1055
  elapsedSeconds: Math.round(waitedMs / 1000),
1387
1056
  },
1057
+ blockClassOverride: CLOSED_UNMERGED_BLOCK_CLASS,
1058
+ reasonOverride: CLOSED_UNMERGED_REASON,
1388
1059
  };
1389
1060
  } else {
1390
- // Fail fast on a GENUINELY red REQUIRED check — head-anchored (Story
1391
- // #4695), decided by the extracted `decideMergeWaitFailFast` (Story
1392
- // #4710): per-run evidence decides on a single probe; without evidence
1393
- // two consecutive failing probes are required. No remaining budget
1394
- // turns a failed check green, and waiting it out is what made the
1395
- // pre-#4543 wait report the operator's red test run as a
1396
- // branch-protection block.
1061
+ // No remaining budget turns a red required check green; fail fast.
1397
1062
  const decision = decideMergeWaitFailFast({
1398
1063
  probe,
1399
1064
  consecutiveRequiredFailSnapshots,
@@ -1413,14 +1078,13 @@ export async function runConfirmMergePhase({
1413
1078
  exhausted: false,
1414
1079
  elapsedSeconds: Math.round(waitedMs / 1000),
1415
1080
  },
1081
+ blockClassOverride: decision.blockClass,
1082
+ reasonOverride: decision.reason,
1416
1083
  };
1417
1084
  }
1418
1085
 
1419
- // Story #5096 — the ADVISORY counterpart, and the half that catches the
1420
- // common shape. Close arms immediately after opening the PR, while the
1421
- // gate is still QUEUED, so the pre-arm refusal in `auto-merge.js` sees
1422
- // nothing; the gate reddens here, mid-wait, and native auto-merge would
1423
- // land the PR the moment the REQUIRED contexts go green.
1086
+ // Advisory gates are usually still QUEUED at arm time, so they redden
1087
+ // here, mid-wait, before auto-merge lands over them.
1424
1088
  unlanded = await resolveAdvisoryUnlanded({
1425
1089
  unlanded,
1426
1090
  probe,
@@ -1451,11 +1115,7 @@ export async function runConfirmMergePhase({
1451
1115
  updatesUsed += 1;
1452
1116
  }
1453
1117
 
1454
- // Cumulative budget exhausted → the genuine give-up. Classify from the
1455
- // probe we already hold. Gated behind the poll floor so an
1456
- // already-over-budget PR (anchored at a createdAt older than the budget
1457
- // — a resume the next day, or a long-open PR) still gets a real poll
1458
- // cycle instead of being blocked before this invocation waited at all.
1118
+ // Cumulative budget exhausted, behind the poll floor.
1459
1119
  if (
1460
1120
  polls >= MIN_POLLS_BEFORE_BUDGET_BLOCK &&
1461
1121
  cumulativeMs + intervalMs > maxBudgetSeconds * 1000
@@ -1485,11 +1145,7 @@ export async function runConfirmMergePhase({
1485
1145
  );
1486
1146
  }
1487
1147
 
1488
- // This invocation's bound expired → PENDING. Deliberately NOT a block:
1489
- // nothing is wrong, the run simply reached the edge of its host slot.
1490
- // No label mutation, no merge.unlanded event — the caller surfaces a
1491
- // resumable terminal and the next invocation continues the cumulative
1492
- // clock from the PR's createdAt.
1148
+ // Invocation bound reached: resumable `pending`, no mutation.
1493
1149
  if (waitedMs + intervalMs > maxWaitSeconds * 1000) {
1494
1150
  progress?.(
1495
1151
  'CONFIRM',
@@ -1515,25 +1171,19 @@ export async function runConfirmMergePhase({
1515
1171
  try {
1516
1172
  return await runMergePoll();
1517
1173
  } catch (err) {
1518
- // A terminal handler threw. `pollUntil` treats a throwing `fn` as a
1519
- // non-match and would poll forever on it, so the throw is carried out
1520
- // as a match and re-raised below.
1174
+ // pollUntil treats a throw as a non-match and would spin; carry it out.
1521
1175
  return { done: true, thrown: err };
1522
1176
  }
1523
1177
  },
1524
1178
  predicate: (result) => result?.done === true,
1525
1179
  intervalMs,
1526
- // The wait owns its own bounds (`maxWaitSeconds` → `pending`,
1527
- // `maxBudgetSeconds` → blocked), and both are decided from the probe
1528
- // inside the tick. A second, cruder wall-clock timeout here would throw
1529
- // past those classifications.
1180
+ // No pollUntil timeout: the tick owns both bounds and their classification.
1530
1181
  sleepFn: (ms) => sleepFn(ms),
1531
1182
  });
1532
1183
  if (tick.thrown) throw tick.thrown;
1533
1184
  return tick.outcome;
1534
1185
  }
1535
1186
 
1536
- /** Wrap a phase terminal as the poll loop's match. */
1537
1187
  function doneWith(outcome) {
1538
1188
  return { done: true, outcome };
1539
1189
  }