mandrel 1.57.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 (843) hide show
  1. package/.agents/README.md +954 -0
  2. package/.agents/docs/SDLC.md +1420 -0
  3. package/.agents/docs/agentrc-reference.json +278 -0
  4. package/.agents/docs/configuration.md +1040 -0
  5. package/.agents/docs/workflows.md +59 -0
  6. package/.agents/instructions.md +384 -0
  7. package/.agents/personas/architect.md +107 -0
  8. package/.agents/personas/devops-engineer.md +36 -0
  9. package/.agents/personas/engineer-mobile.md +119 -0
  10. package/.agents/personas/engineer-web.md +110 -0
  11. package/.agents/personas/engineer.md +90 -0
  12. package/.agents/personas/product.md +88 -0
  13. package/.agents/personas/project-manager.md +110 -0
  14. package/.agents/personas/qa-engineer.md +91 -0
  15. package/.agents/personas/refactorer.md +110 -0
  16. package/.agents/personas/security-engineer.md +112 -0
  17. package/.agents/personas/sre.md +86 -0
  18. package/.agents/personas/technical-writer.md +100 -0
  19. package/.agents/personas/ux-designer.md +95 -0
  20. package/.agents/rules/api-conventions.md +75 -0
  21. package/.agents/rules/changelog-style.md +238 -0
  22. package/.agents/rules/gherkin-standards.md +146 -0
  23. package/.agents/rules/git-conventions.md +146 -0
  24. package/.agents/rules/orchestration-error-handling.md +35 -0
  25. package/.agents/rules/security-baseline.md +92 -0
  26. package/.agents/rules/shell-conventions.md +70 -0
  27. package/.agents/rules/test-seams.md +59 -0
  28. package/.agents/rules/testing-standards.md +177 -0
  29. package/.agents/runtime-deps.json +18 -0
  30. package/.agents/schemas/acceptance-eval-verdict.schema.json +93 -0
  31. package/.agents/schemas/agentrc.schema.json +1583 -0
  32. package/.agents/schemas/audit-results.schema.json +69 -0
  33. package/.agents/schemas/audit-rules.json +134 -0
  34. package/.agents/schemas/audit-rules.schema.json +69 -0
  35. package/.agents/schemas/baselines/baseline-envelope.schema.json +44 -0
  36. package/.agents/schemas/baselines/bundle-size.schema.json +47 -0
  37. package/.agents/schemas/baselines/coverage.schema.json +50 -0
  38. package/.agents/schemas/baselines/crap.schema.json +52 -0
  39. package/.agents/schemas/baselines/duplication.schema.json +62 -0
  40. package/.agents/schemas/baselines/lighthouse.schema.json +59 -0
  41. package/.agents/schemas/baselines/lint.schema.json +47 -0
  42. package/.agents/schemas/baselines/maintainability.schema.json +71 -0
  43. package/.agents/schemas/baselines/mutation.schema.json +52 -0
  44. package/.agents/schemas/crap-baseline.schema.json +57 -0
  45. package/.agents/schemas/crap-report.schema.json +102 -0
  46. package/.agents/schemas/dispatch-manifest.json +232 -0
  47. package/.agents/schemas/epic-perf-report.schema.json +89 -0
  48. package/.agents/schemas/epic-spec.schema.json +183 -0
  49. package/.agents/schemas/friction-event.schema.json +56 -0
  50. package/.agents/schemas/lifecycle/README.md +18 -0
  51. package/.agents/schemas/lifecycle/acceptance.reconcile.failed.schema.json +13 -0
  52. package/.agents/schemas/lifecycle/acceptance.reconcile.ok.schema.json +13 -0
  53. package/.agents/schemas/lifecycle/acceptance.reconcile.skipped.schema.json +13 -0
  54. package/.agents/schemas/lifecycle/acceptance.reconcile.start.schema.json +12 -0
  55. package/.agents/schemas/lifecycle/acceptance.reconcile.waived.schema.json +13 -0
  56. package/.agents/schemas/lifecycle/checkpoint.written.schema.json +13 -0
  57. package/.agents/schemas/lifecycle/close-validate.end.schema.json +18 -0
  58. package/.agents/schemas/lifecycle/close-validate.start.schema.json +13 -0
  59. package/.agents/schemas/lifecycle/code-review.end.schema.json +30 -0
  60. package/.agents/schemas/lifecycle/code-review.start.schema.json +12 -0
  61. package/.agents/schemas/lifecycle/epic.automerge.end.schema.json +14 -0
  62. package/.agents/schemas/lifecycle/epic.automerge.start.schema.json +13 -0
  63. package/.agents/schemas/lifecycle/epic.blocked.schema.json +13 -0
  64. package/.agents/schemas/lifecycle/epic.cleanup.end.schema.json +12 -0
  65. package/.agents/schemas/lifecycle/epic.cleanup.start.schema.json +12 -0
  66. package/.agents/schemas/lifecycle/epic.close.end.schema.json +12 -0
  67. package/.agents/schemas/lifecycle/epic.complete.schema.json +13 -0
  68. package/.agents/schemas/lifecycle/epic.finalize.end.schema.json +13 -0
  69. package/.agents/schemas/lifecycle/epic.finalize.start.schema.json +12 -0
  70. package/.agents/schemas/lifecycle/epic.merge.armed.schema.json +13 -0
  71. package/.agents/schemas/lifecycle/epic.merge.blocked.schema.json +14 -0
  72. package/.agents/schemas/lifecycle/epic.merge.confirmed.schema.json +17 -0
  73. package/.agents/schemas/lifecycle/epic.merge.ready.schema.json +15 -0
  74. package/.agents/schemas/lifecycle/epic.plan.end.schema.json +18 -0
  75. package/.agents/schemas/lifecycle/epic.plan.start.schema.json +12 -0
  76. package/.agents/schemas/lifecycle/epic.snapshot.end.schema.json +16 -0
  77. package/.agents/schemas/lifecycle/epic.snapshot.start.schema.json +12 -0
  78. package/.agents/schemas/lifecycle/epic.watch.end.schema.json +28 -0
  79. package/.agents/schemas/lifecycle/epic.watch.start.schema.json +16 -0
  80. package/.agents/schemas/lifecycle/intervention.recorded.schema.json +15 -0
  81. package/.agents/schemas/lifecycle/ledger-record.schema.json +59 -0
  82. package/.agents/schemas/lifecycle/notification.emitted.schema.json +18 -0
  83. package/.agents/schemas/lifecycle/pr.created.schema.json +14 -0
  84. package/.agents/schemas/lifecycle/retro.end.schema.json +16 -0
  85. package/.agents/schemas/lifecycle/retro.start.schema.json +12 -0
  86. package/.agents/schemas/lifecycle/story.blocked.schema.json +13 -0
  87. package/.agents/schemas/lifecycle/story.dispatch.end.schema.json +17 -0
  88. package/.agents/schemas/lifecycle/story.dispatch.start.schema.json +15 -0
  89. package/.agents/schemas/lifecycle/story.heartbeat.schema.json +20 -0
  90. package/.agents/schemas/lifecycle/story.merged.schema.json +13 -0
  91. package/.agents/schemas/mi-report.schema.json +58 -0
  92. package/.agents/schemas/model-attribution.schema.json +49 -0
  93. package/.agents/schemas/qa-finding.schema.json +133 -0
  94. package/.agents/schemas/qa-ledger.schema.json +89 -0
  95. package/.agents/schemas/risk-verdict.schema.json +53 -0
  96. package/.agents/schemas/signal-event.schema.json +58 -0
  97. package/.agents/schemas/skill.schema.json +31 -0
  98. package/.agents/schemas/skills-index.schema.json +81 -0
  99. package/.agents/schemas/story-perf-summary.schema.json +73 -0
  100. package/.agents/schemas/validation-evidence.schema.json +78 -0
  101. package/.agents/scripts/README.md +93 -0
  102. package/.agents/scripts/acceptance-eval.js +284 -0
  103. package/.agents/scripts/acceptance-spec-reconciler.js +556 -0
  104. package/.agents/scripts/agents-bootstrap-github.js +634 -0
  105. package/.agents/scripts/analyze-execution.js +369 -0
  106. package/.agents/scripts/assert-branch.js +83 -0
  107. package/.agents/scripts/audit-labels-bootstrap.js +253 -0
  108. package/.agents/scripts/audit-to-stories.js +257 -0
  109. package/.agents/scripts/bootstrap.js +1378 -0
  110. package/.agents/scripts/check-baselines.js +81 -0
  111. package/.agents/scripts/check-dead-exports.js +311 -0
  112. package/.agents/scripts/check-doc-links.js +401 -0
  113. package/.agents/scripts/check-gherkin-placeholders.js +663 -0
  114. package/.agents/scripts/check-lifecycle-doc-drift.js +402 -0
  115. package/.agents/scripts/check-lifecycle-lint.js +379 -0
  116. package/.agents/scripts/check-prepush-recovery.js +90 -0
  117. package/.agents/scripts/check-windows-git-perf.js +138 -0
  118. package/.agents/scripts/cleanup-repo-test-temp.js +67 -0
  119. package/.agents/scripts/coverage-capture.js +112 -0
  120. package/.agents/scripts/detect-merges.js +111 -0
  121. package/.agents/scripts/diagnose-friction.js +257 -0
  122. package/.agents/scripts/diagnose.js +240 -0
  123. package/.agents/scripts/dispatcher.js +295 -0
  124. package/.agents/scripts/drain-pending-cleanup.js +147 -0
  125. package/.agents/scripts/epic-audit-prepare.js +419 -0
  126. package/.agents/scripts/epic-audit-recheck.js +241 -0
  127. package/.agents/scripts/epic-deliver-note-intervention.js +192 -0
  128. package/.agents/scripts/epic-deliver-preflight.js +407 -0
  129. package/.agents/scripts/epic-deliver-prepare.js +383 -0
  130. package/.agents/scripts/epic-execute-record-wave.js +463 -0
  131. package/.agents/scripts/epic-plan-clarity.js +201 -0
  132. package/.agents/scripts/epic-plan-decompose.js +79 -0
  133. package/.agents/scripts/epic-plan-healthcheck.js +363 -0
  134. package/.agents/scripts/epic-plan-spec-validate.js +111 -0
  135. package/.agents/scripts/epic-plan-spec.js +198 -0
  136. package/.agents/scripts/epic-reconcile.js +637 -0
  137. package/.agents/scripts/evidence-gate.js +235 -0
  138. package/.agents/scripts/generate-config-docs.js +516 -0
  139. package/.agents/scripts/generate-lifecycle-docs.js +224 -0
  140. package/.agents/scripts/generate-skills-index.js +252 -0
  141. package/.agents/scripts/generate-workflows-doc.js +168 -0
  142. package/.agents/scripts/git-cleanup.js +124 -0
  143. package/.agents/scripts/git-pr-quality-gate.js +203 -0
  144. package/.agents/scripts/git-rebase-and-resolve.js +234 -0
  145. package/.agents/scripts/hierarchy-gate.js +176 -0
  146. package/.agents/scripts/hydrate-context.js +179 -0
  147. package/.agents/scripts/install-matrix-assert.js +282 -0
  148. package/.agents/scripts/lib/Graph.js +326 -0
  149. package/.agents/scripts/lib/ITicketingProvider.js +349 -0
  150. package/.agents/scripts/lib/Logger.js +194 -0
  151. package/.agents/scripts/lib/audit-suite/cli.js +64 -0
  152. package/.agents/scripts/lib/audit-suite/findings.js +164 -0
  153. package/.agents/scripts/lib/audit-suite/frontmatter-lint.js +32 -0
  154. package/.agents/scripts/lib/audit-suite/frontmatter.js +110 -0
  155. package/.agents/scripts/lib/audit-suite/index.js +22 -0
  156. package/.agents/scripts/lib/audit-suite/runner.js +233 -0
  157. package/.agents/scripts/lib/audit-suite/selector.js +235 -0
  158. package/.agents/scripts/lib/audit-suite/substitutions.js +124 -0
  159. package/.agents/scripts/lib/audit-suite/workflow-loader.js +49 -0
  160. package/.agents/scripts/lib/audit-to-stories/build-story-body.js +130 -0
  161. package/.agents/scripts/lib/audit-to-stories/dedupe-against-github.js +114 -0
  162. package/.agents/scripts/lib/audit-to-stories/finding-adapter.js +93 -0
  163. package/.agents/scripts/lib/audit-to-stories/group-findings.js +265 -0
  164. package/.agents/scripts/lib/audit-to-stories/parse-audit-md.js +246 -0
  165. package/.agents/scripts/lib/audit-to-stories/seed-epic-from-findings.js +160 -0
  166. package/.agents/scripts/lib/auto-refresh-baselines.js +308 -0
  167. package/.agents/scripts/lib/baseline-loader.js +0 -0
  168. package/.agents/scripts/lib/baseline-schema-registry.js +69 -0
  169. package/.agents/scripts/lib/baseline-snapshot.js +716 -0
  170. package/.agents/scripts/lib/baselines/component-matcher.js +21 -0
  171. package/.agents/scripts/lib/baselines/components.js +126 -0
  172. package/.agents/scripts/lib/baselines/diff-scope-cli.js +203 -0
  173. package/.agents/scripts/lib/baselines/duplication-scanner.js +220 -0
  174. package/.agents/scripts/lib/baselines/env-overrides.js +129 -0
  175. package/.agents/scripts/lib/baselines/envelope.js +368 -0
  176. package/.agents/scripts/lib/baselines/exit-codes.js +89 -0
  177. package/.agents/scripts/lib/baselines/git-base.js +0 -0
  178. package/.agents/scripts/lib/baselines/kernel.js +111 -0
  179. package/.agents/scripts/lib/baselines/kinds/_shared-metric.js +220 -0
  180. package/.agents/scripts/lib/baselines/kinds/bundle-size.js +157 -0
  181. package/.agents/scripts/lib/baselines/kinds/coverage.js +194 -0
  182. package/.agents/scripts/lib/baselines/kinds/crap.js +555 -0
  183. package/.agents/scripts/lib/baselines/kinds/duplication.js +197 -0
  184. package/.agents/scripts/lib/baselines/kinds/lighthouse.js +185 -0
  185. package/.agents/scripts/lib/baselines/kinds/lint.js +172 -0
  186. package/.agents/scripts/lib/baselines/kinds/maintainability.js +340 -0
  187. package/.agents/scripts/lib/baselines/kinds/mutation.js +153 -0
  188. package/.agents/scripts/lib/baselines/path-canon.js +279 -0
  189. package/.agents/scripts/lib/baselines/preview-gates.js +298 -0
  190. package/.agents/scripts/lib/baselines/reader.js +321 -0
  191. package/.agents/scripts/lib/baselines/refresh-service.js +733 -0
  192. package/.agents/scripts/lib/baselines/scope.js +291 -0
  193. package/.agents/scripts/lib/baselines/writer.js +312 -0
  194. package/.agents/scripts/lib/bdd-runner-detect.js +417 -0
  195. package/.agents/scripts/lib/bdd-scenario-scanner.js +310 -0
  196. package/.agents/scripts/lib/bootstrap/baselines-layout-migration.js +202 -0
  197. package/.agents/scripts/lib/bootstrap/branch-protection.js +222 -0
  198. package/.agents/scripts/lib/bootstrap/ci-workflow-template.js +171 -0
  199. package/.agents/scripts/lib/bootstrap/commit-push.js +146 -0
  200. package/.agents/scripts/lib/bootstrap/gh-list.js +153 -0
  201. package/.agents/scripts/lib/bootstrap/gh-preflight.js +306 -0
  202. package/.agents/scripts/lib/bootstrap/hitl-confirm.js +89 -0
  203. package/.agents/scripts/lib/bootstrap/install-ledger.js +174 -0
  204. package/.agents/scripts/lib/bootstrap/manifest.js +272 -0
  205. package/.agents/scripts/lib/bootstrap/merge-methods.js +108 -0
  206. package/.agents/scripts/lib/bootstrap/preflight.js +195 -0
  207. package/.agents/scripts/lib/bootstrap/project-bootstrap.js +801 -0
  208. package/.agents/scripts/lib/bootstrap/prompt.js +480 -0
  209. package/.agents/scripts/lib/bootstrap/quality-bootstrap.js +370 -0
  210. package/.agents/scripts/lib/bootstrap/summary.js +75 -0
  211. package/.agents/scripts/lib/bootstrap/workflow-audit.js +256 -0
  212. package/.agents/scripts/lib/branch-name-guard.js +98 -0
  213. package/.agents/scripts/lib/c8-cli-path.js +21 -0
  214. package/.agents/scripts/lib/changed-files.js +184 -0
  215. package/.agents/scripts/lib/checks/baseline-drift-main-checkout.js +104 -0
  216. package/.agents/scripts/lib/checks/core-bare-clean.js +48 -0
  217. package/.agents/scripts/lib/checks/epic-merge-lock-stale.js +54 -0
  218. package/.agents/scripts/lib/checks/index.js +288 -0
  219. package/.agents/scripts/lib/checks/push-hook-parity.js +106 -0
  220. package/.agents/scripts/lib/checks/stale-origin-epic.js +49 -0
  221. package/.agents/scripts/lib/checks/state.js +558 -0
  222. package/.agents/scripts/lib/checks/story-init-not-backgrounded.js +186 -0
  223. package/.agents/scripts/lib/checks/subagent-agent-tool-required.js +182 -0
  224. package/.agents/scripts/lib/checks/windows-coverage-noise-floor.js +92 -0
  225. package/.agents/scripts/lib/checks/worktree-bootstrap-env.js +81 -0
  226. package/.agents/scripts/lib/checks/worktree-residue-biome.js +55 -0
  227. package/.agents/scripts/lib/cli/parse-numeric.js +60 -0
  228. package/.agents/scripts/lib/cli/standard-args.js +351 -0
  229. package/.agents/scripts/lib/cli-args.js +286 -0
  230. package/.agents/scripts/lib/cli-utils.js +69 -0
  231. package/.agents/scripts/lib/close-validation/projections/head-sha.js +44 -0
  232. package/.agents/scripts/lib/close-validation/projections/inputs.js +86 -0
  233. package/.agents/scripts/lib/close-validation/projections/maintainability.js +286 -0
  234. package/.agents/scripts/lib/close-validation.js +897 -0
  235. package/.agents/scripts/lib/codebase-snapshot.js +513 -0
  236. package/.agents/scripts/lib/command-header.js +33 -0
  237. package/.agents/scripts/lib/config/acceptance-eval.js +95 -0
  238. package/.agents/scripts/lib/config/baselines.js +60 -0
  239. package/.agents/scripts/lib/config/ci.js +30 -0
  240. package/.agents/scripts/lib/config/commands.js +36 -0
  241. package/.agents/scripts/lib/config/defaults.js +119 -0
  242. package/.agents/scripts/lib/config/explain.js +348 -0
  243. package/.agents/scripts/lib/config/gates/bundle-size.schema.js +23 -0
  244. package/.agents/scripts/lib/config/gates/coverage.schema.js +18 -0
  245. package/.agents/scripts/lib/config/gates/crap.schema.js +33 -0
  246. package/.agents/scripts/lib/config/gates/duplication.schema.js +26 -0
  247. package/.agents/scripts/lib/config/gates/index.js +36 -0
  248. package/.agents/scripts/lib/config/gates/lighthouse.schema.js +23 -0
  249. package/.agents/scripts/lib/config/gates/lint.schema.js +9 -0
  250. package/.agents/scripts/lib/config/gates/maintainability.schema.js +20 -0
  251. package/.agents/scripts/lib/config/gates/mutation.schema.js +12 -0
  252. package/.agents/scripts/lib/config/gates/shared.js +117 -0
  253. package/.agents/scripts/lib/config/github.js +122 -0
  254. package/.agents/scripts/lib/config/lifecycle.js +40 -0
  255. package/.agents/scripts/lib/config/limits.js +211 -0
  256. package/.agents/scripts/lib/config/paths.js +73 -0
  257. package/.agents/scripts/lib/config/preflight.js +58 -0
  258. package/.agents/scripts/lib/config/quality.js +665 -0
  259. package/.agents/scripts/lib/config/retro.js +77 -0
  260. package/.agents/scripts/lib/config/runners.js +105 -0
  261. package/.agents/scripts/lib/config/runtime.js +167 -0
  262. package/.agents/scripts/lib/config/shared.js +46 -0
  263. package/.agents/scripts/lib/config/sync-agentrc.js +243 -0
  264. package/.agents/scripts/lib/config/temp-paths.js +373 -0
  265. package/.agents/scripts/lib/config/validate-orchestration.js +81 -0
  266. package/.agents/scripts/lib/config/worktree-isolation.js +80 -0
  267. package/.agents/scripts/lib/config-resolver.js +298 -0
  268. package/.agents/scripts/lib/config-schema-shared.js +32 -0
  269. package/.agents/scripts/lib/config-schema.js +20 -0
  270. package/.agents/scripts/lib/config-settings-schema-delivery.js +332 -0
  271. package/.agents/scripts/lib/config-settings-schema-quality.js +165 -0
  272. package/.agents/scripts/lib/config-settings-schema.js +420 -0
  273. package/.agents/scripts/lib/coverage-baseline.js +352 -0
  274. package/.agents/scripts/lib/coverage-capture.js +195 -0
  275. package/.agents/scripts/lib/coverage-utils.js +239 -0
  276. package/.agents/scripts/lib/cpu-pool.js +223 -0
  277. package/.agents/scripts/lib/crap-engine.js +119 -0
  278. package/.agents/scripts/lib/crap-utils.js +479 -0
  279. package/.agents/scripts/lib/degraded-mode.js +69 -0
  280. package/.agents/scripts/lib/dependency-parser.js +129 -0
  281. package/.agents/scripts/lib/duplicate-search.js +189 -0
  282. package/.agents/scripts/lib/dynamic-workflow/architecture-report-contract.js +70 -0
  283. package/.agents/scripts/lib/dynamic-workflow/audit-orchestrator.js +197 -0
  284. package/.agents/scripts/lib/dynamic-workflow/capability.js +396 -0
  285. package/.agents/scripts/lib/dynamic-workflow/clean-code-report-contract.js +80 -0
  286. package/.agents/scripts/lib/dynamic-workflow/performance-report-contract.js +72 -0
  287. package/.agents/scripts/lib/dynamic-workflow/quality-report-contract.js +90 -0
  288. package/.agents/scripts/lib/dynamic-workflow/report-contract-core.js +43 -0
  289. package/.agents/scripts/lib/dynamic-workflow/security-report-contract.js +83 -0
  290. package/.agents/scripts/lib/env-loader.js +52 -0
  291. package/.agents/scripts/lib/epic-merge-lock.js +239 -0
  292. package/.agents/scripts/lib/epic-plan-clarity.js +142 -0
  293. package/.agents/scripts/lib/epic-plan-ideation.js +228 -0
  294. package/.agents/scripts/lib/error-redactor.js +125 -0
  295. package/.agents/scripts/lib/errors/index.js +67 -0
  296. package/.agents/scripts/lib/feedback-loop/audit-results-graduator.js +230 -0
  297. package/.agents/scripts/lib/feedback-loop/code-review-graduator.js +207 -0
  298. package/.agents/scripts/lib/feedback-loop/graduator-core.js +421 -0
  299. package/.agents/scripts/lib/feedback-loop/memory-freshness.js +480 -0
  300. package/.agents/scripts/lib/feedback-loop/prior-feedback-fetcher.js +229 -0
  301. package/.agents/scripts/lib/findings/classify-finding.js +195 -0
  302. package/.agents/scripts/lib/findings/promote-finding.js +353 -0
  303. package/.agents/scripts/lib/findings/route-finding.js +283 -0
  304. package/.agents/scripts/lib/findings/semantic-issue-search.js +179 -0
  305. package/.agents/scripts/lib/findings/severity.js +102 -0
  306. package/.agents/scripts/lib/gates/baseline-store.js +106 -0
  307. package/.agents/scripts/lib/gates/friction.js +43 -0
  308. package/.agents/scripts/lib/gh-exec.js +553 -0
  309. package/.agents/scripts/lib/git/cached-fetch.js +0 -0
  310. package/.agents/scripts/lib/git/sync-from-base.js +162 -0
  311. package/.agents/scripts/lib/git-branch-cleanup.js +213 -0
  312. package/.agents/scripts/lib/git-branch-lifecycle.js +353 -0
  313. package/.agents/scripts/lib/git-merge-orchestrator.js +261 -0
  314. package/.agents/scripts/lib/git-utils.js +363 -0
  315. package/.agents/scripts/lib/github-url.js +29 -0
  316. package/.agents/scripts/lib/install-cmd-parser.js +51 -0
  317. package/.agents/scripts/lib/issue-link-parser.js +74 -0
  318. package/.agents/scripts/lib/json-utils.js +60 -0
  319. package/.agents/scripts/lib/label-constants.js +169 -0
  320. package/.agents/scripts/lib/label-taxonomy.js +200 -0
  321. package/.agents/scripts/lib/maintainability-engine.js +164 -0
  322. package/.agents/scripts/lib/maintainability-utils.js +343 -0
  323. package/.agents/scripts/lib/mandrel-catalog.js +170 -0
  324. package/.agents/scripts/lib/mutation/baseline-snapshot.js +238 -0
  325. package/.agents/scripts/lib/mutation/config-detector.js +119 -0
  326. package/.agents/scripts/lib/mutation/stryker-runner.js +306 -0
  327. package/.agents/scripts/lib/mutation/survivor-report.js +160 -0
  328. package/.agents/scripts/lib/notifications/notifier.js +75 -0
  329. package/.agents/scripts/lib/observability/active-story-env.js +182 -0
  330. package/.agents/scripts/lib/observability/baseline-refresh-rate.js +221 -0
  331. package/.agents/scripts/lib/observability/perf-aggregator.js +887 -0
  332. package/.agents/scripts/lib/observability/perf-report-readers.js +319 -0
  333. package/.agents/scripts/lib/observability/perf-report-render.js +182 -0
  334. package/.agents/scripts/lib/observability/signals-writer.js +296 -0
  335. package/.agents/scripts/lib/observability/source-classifier.js +103 -0
  336. package/.agents/scripts/lib/observability/tool-trace-hook.js +417 -0
  337. package/.agents/scripts/lib/onboard/detect-stack.js +300 -0
  338. package/.agents/scripts/lib/onboard/scaffold-docs.js +128 -0
  339. package/.agents/scripts/lib/orchestration/acceptance-eval-decision.js +173 -0
  340. package/.agents/scripts/lib/orchestration/cascade-grouping.js +275 -0
  341. package/.agents/scripts/lib/orchestration/check-baselines/phases/compare.js +131 -0
  342. package/.agents/scripts/lib/orchestration/check-baselines/phases/evaluate.js +80 -0
  343. package/.agents/scripts/lib/orchestration/check-baselines/phases/floors.js +132 -0
  344. package/.agents/scripts/lib/orchestration/check-baselines/phases/friction.js +142 -0
  345. package/.agents/scripts/lib/orchestration/check-baselines/phases/parse-args.js +149 -0
  346. package/.agents/scripts/lib/orchestration/check-baselines/phases/pipeline.js +158 -0
  347. package/.agents/scripts/lib/orchestration/check-baselines/phases/report.js +56 -0
  348. package/.agents/scripts/lib/orchestration/code-review.js +652 -0
  349. package/.agents/scripts/lib/orchestration/column-sync.js +286 -0
  350. package/.agents/scripts/lib/orchestration/context-envelope.js +280 -0
  351. package/.agents/scripts/lib/orchestration/context-hydration-engine.js +581 -0
  352. package/.agents/scripts/lib/orchestration/dependency-analyzer.js +88 -0
  353. package/.agents/scripts/lib/orchestration/detectors-phase.js +188 -0
  354. package/.agents/scripts/lib/orchestration/dispatch-engine.js +144 -0
  355. package/.agents/scripts/lib/orchestration/dispatch-pipeline.js +206 -0
  356. package/.agents/scripts/lib/orchestration/doc-reader.js +94 -0
  357. package/.agents/scripts/lib/orchestration/epic-cleanup.js +473 -0
  358. package/.agents/scripts/lib/orchestration/epic-deliver-lease-guard.js +310 -0
  359. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/cli.js +167 -0
  360. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/context.js +151 -0
  361. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/creation.js +74 -0
  362. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/dag.js +78 -0
  363. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/diagnostics.js +72 -0
  364. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/persist-helpers.js +155 -0
  365. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/persist.js +321 -0
  366. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/planning-artifacts.js +75 -0
  367. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/reconcile-spawn.js +86 -0
  368. package/.agents/scripts/lib/orchestration/epic-plan-lease-guard.js +235 -0
  369. package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/authoring-context.js +197 -0
  370. package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/cli-args.js +48 -0
  371. package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/drain.js +94 -0
  372. package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/plan-epic.js +414 -0
  373. package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/prompts.js +55 -0
  374. package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/risk-verdict.js +105 -0
  375. package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/run-spec-phase.js +235 -0
  376. package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/spec-freshness.js +120 -0
  377. package/.agents/scripts/lib/orchestration/epic-plan-state-store.js +118 -0
  378. package/.agents/scripts/lib/orchestration/epic-run-state-store.js +295 -0
  379. package/.agents/scripts/lib/orchestration/epic-runner/concurrency-gate.js +186 -0
  380. package/.agents/scripts/lib/orchestration/epic-runner/deliver-phases.js +50 -0
  381. package/.agents/scripts/lib/orchestration/epic-runner/phases/build-wave-dag.js +146 -0
  382. package/.agents/scripts/lib/orchestration/epic-runner/phases/snapshot.js +110 -0
  383. package/.agents/scripts/lib/orchestration/epic-runner/progress-reporter/composition.js +392 -0
  384. package/.agents/scripts/lib/orchestration/epic-runner/progress-reporter/signals.js +217 -0
  385. package/.agents/scripts/lib/orchestration/epic-runner/progress-reporter/transport.js +235 -0
  386. package/.agents/scripts/lib/orchestration/epic-runner/progress-reporter.js +69 -0
  387. package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/_bullet-format.js +32 -0
  388. package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/crap-drift.js +291 -0
  389. package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/maintainability-drift.js +175 -0
  390. package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/stalled-worktree.js +37 -0
  391. package/.agents/scripts/lib/orchestration/epic-runner/story-launcher.js +127 -0
  392. package/.agents/scripts/lib/orchestration/epic-runner/story-run-progress-writer.js +400 -0
  393. package/.agents/scripts/lib/orchestration/epic-runner/sub-agent-return.js +285 -0
  394. package/.agents/scripts/lib/orchestration/epic-runner/wave-scheduler.js +66 -0
  395. package/.agents/scripts/lib/orchestration/epic-spec-reconciler-apply.js +797 -0
  396. package/.agents/scripts/lib/orchestration/epic-spec-reconciler-diff.js +619 -0
  397. package/.agents/scripts/lib/orchestration/epic-spec-reconciler-discriminator.js +335 -0
  398. package/.agents/scripts/lib/orchestration/epic-spec-reconciler-format.js +230 -0
  399. package/.agents/scripts/lib/orchestration/epic-spec-reconciler-ops.js +363 -0
  400. package/.agents/scripts/lib/orchestration/error-journal.js +139 -0
  401. package/.agents/scripts/lib/orchestration/file-assumption-enum.js +31 -0
  402. package/.agents/scripts/lib/orchestration/file-assumptions.js +506 -0
  403. package/.agents/scripts/lib/orchestration/finalize/close-planning-tickets.js +116 -0
  404. package/.agents/scripts/lib/orchestration/finalize/open-or-locate-pr.js +241 -0
  405. package/.agents/scripts/lib/orchestration/finalize/post-handoff-comment.js +489 -0
  406. package/.agents/scripts/lib/orchestration/finalize/sanitize-skip-ci.js +88 -0
  407. package/.agents/scripts/lib/orchestration/git-cleanup/phases/branches-reap.js +219 -0
  408. package/.agents/scripts/lib/orchestration/git-cleanup/phases/branches.js +309 -0
  409. package/.agents/scripts/lib/orchestration/git-cleanup/phases/cli.js +99 -0
  410. package/.agents/scripts/lib/orchestration/git-cleanup/phases/fast-forward.js +123 -0
  411. package/.agents/scripts/lib/orchestration/git-cleanup/phases/filters.js +57 -0
  412. package/.agents/scripts/lib/orchestration/git-cleanup/phases/git-probes-ff.js +114 -0
  413. package/.agents/scripts/lib/orchestration/git-cleanup/phases/git-probes.js +426 -0
  414. package/.agents/scripts/lib/orchestration/git-cleanup/phases/parse-args.js +84 -0
  415. package/.agents/scripts/lib/orchestration/git-cleanup/phases/phase-drivers.js +365 -0
  416. package/.agents/scripts/lib/orchestration/git-cleanup/phases/prompts.js +72 -0
  417. package/.agents/scripts/lib/orchestration/git-cleanup/phases/prune.js +69 -0
  418. package/.agents/scripts/lib/orchestration/git-cleanup/phases/render.js +214 -0
  419. package/.agents/scripts/lib/orchestration/git-cleanup/phases/stashes.js +137 -0
  420. package/.agents/scripts/lib/orchestration/label-transitions.js +43 -0
  421. package/.agents/scripts/lib/orchestration/lifecycle/bus.js +309 -0
  422. package/.agents/scripts/lib/orchestration/lifecycle/emit-story-dispatch-end.js +147 -0
  423. package/.agents/scripts/lib/orchestration/lifecycle/emit-story-heartbeat.js +155 -0
  424. package/.agents/scripts/lib/orchestration/lifecycle/ledger-writer.js +226 -0
  425. package/.agents/scripts/lib/orchestration/lifecycle/listeners/README.md +69 -0
  426. package/.agents/scripts/lib/orchestration/lifecycle/listeners/acceptance-reconciler.js +378 -0
  427. package/.agents/scripts/lib/orchestration/lifecycle/listeners/automerge-armer.js +248 -0
  428. package/.agents/scripts/lib/orchestration/lifecycle/listeners/automerge-predicate.js +527 -0
  429. package/.agents/scripts/lib/orchestration/lifecycle/listeners/branch-cleaner.js +259 -0
  430. package/.agents/scripts/lib/orchestration/lifecycle/listeners/checkpoint-pointer-writer.js +278 -0
  431. package/.agents/scripts/lib/orchestration/lifecycle/listeners/cleaner.js +355 -0
  432. package/.agents/scripts/lib/orchestration/lifecycle/listeners/finalizer.js +647 -0
  433. package/.agents/scripts/lib/orchestration/lifecycle/listeners/index.js +331 -0
  434. package/.agents/scripts/lib/orchestration/lifecycle/listeners/intervention-recorder.js +140 -0
  435. package/.agents/scripts/lib/orchestration/lifecycle/listeners/merge-watcher.js +421 -0
  436. package/.agents/scripts/lib/orchestration/lifecycle/listeners/notify-dispatcher.js +168 -0
  437. package/.agents/scripts/lib/orchestration/lifecycle/listeners/watcher.js +668 -0
  438. package/.agents/scripts/lib/orchestration/lifecycle/trace-logger.js +322 -0
  439. package/.agents/scripts/lib/orchestration/lint-baseline-service.js +114 -0
  440. package/.agents/scripts/lib/orchestration/manifest-builder.js +216 -0
  441. package/.agents/scripts/lib/orchestration/model-attribution.js +390 -0
  442. package/.agents/scripts/lib/orchestration/parked-follow-ons.js +147 -0
  443. package/.agents/scripts/lib/orchestration/phase-runner.js +87 -0
  444. package/.agents/scripts/lib/orchestration/plan-review-routing.js +63 -0
  445. package/.agents/scripts/lib/orchestration/plan-runner/plan-router.js +86 -0
  446. package/.agents/scripts/lib/orchestration/plan-runner/worktree-sweep.js +212 -0
  447. package/.agents/scripts/lib/orchestration/planning-context-budget.js +213 -0
  448. package/.agents/scripts/lib/orchestration/planning-risk.js +155 -0
  449. package/.agents/scripts/lib/orchestration/planning-state-manager.js +318 -0
  450. package/.agents/scripts/lib/orchestration/post-merge/phases/branch-cleanup.js +56 -0
  451. package/.agents/scripts/lib/orchestration/post-merge/phases/dashboard-refresh.js +33 -0
  452. package/.agents/scripts/lib/orchestration/post-merge/phases/notification.js +78 -0
  453. package/.agents/scripts/lib/orchestration/post-merge/phases/temp-cleanup.js +68 -0
  454. package/.agents/scripts/lib/orchestration/post-merge/phases/ticket-closure.js +118 -0
  455. package/.agents/scripts/lib/orchestration/post-merge/phases/worktree-reap.js +396 -0
  456. package/.agents/scripts/lib/orchestration/post-merge-pipeline.js +205 -0
  457. package/.agents/scripts/lib/orchestration/pr-base-guard.js +47 -0
  458. package/.agents/scripts/lib/orchestration/preflight-cache.js +164 -0
  459. package/.agents/scripts/lib/orchestration/reassert-status-column.js +202 -0
  460. package/.agents/scripts/lib/orchestration/reconciler.js +137 -0
  461. package/.agents/scripts/lib/orchestration/recurring-failure-detector.js +152 -0
  462. package/.agents/scripts/lib/orchestration/recut.js +56 -0
  463. package/.agents/scripts/lib/orchestration/resolves-token.js +127 -0
  464. package/.agents/scripts/lib/orchestration/retro/phases/checks.js +94 -0
  465. package/.agents/scripts/lib/orchestration/retro/phases/compose-body.js +448 -0
  466. package/.agents/scripts/lib/orchestration/retro/phases/gather-signals.js +335 -0
  467. package/.agents/scripts/lib/orchestration/retro/phases/post-and-mirror.js +133 -0
  468. package/.agents/scripts/lib/orchestration/retro-heuristics.js +57 -0
  469. package/.agents/scripts/lib/orchestration/retro-perf-heuristics.js +275 -0
  470. package/.agents/scripts/lib/orchestration/retro-proposals.js +395 -0
  471. package/.agents/scripts/lib/orchestration/retro-runner.js +171 -0
  472. package/.agents/scripts/lib/orchestration/review-depth.js +93 -0
  473. package/.agents/scripts/lib/orchestration/review-providers/codex.js +363 -0
  474. package/.agents/scripts/lib/orchestration/review-providers/findings-renderer.js +205 -0
  475. package/.agents/scripts/lib/orchestration/review-providers/native.js +805 -0
  476. package/.agents/scripts/lib/orchestration/review-providers/review-depth.js +73 -0
  477. package/.agents/scripts/lib/orchestration/review-providers/review-provider-factory.js +396 -0
  478. package/.agents/scripts/lib/orchestration/review-providers/security-review.js +373 -0
  479. package/.agents/scripts/lib/orchestration/review-providers/types.js +89 -0
  480. package/.agents/scripts/lib/orchestration/review-providers/ultrareview.js +107 -0
  481. package/.agents/scripts/lib/orchestration/single-story-close/phases/auto-merge.js +159 -0
  482. package/.agents/scripts/lib/orchestration/single-story-close/phases/base-sync.js +194 -0
  483. package/.agents/scripts/lib/orchestration/single-story-close/phases/close-validation.js +81 -0
  484. package/.agents/scripts/lib/orchestration/single-story-close/phases/code-review.js +190 -0
  485. package/.agents/scripts/lib/orchestration/single-story-close/phases/options.js +70 -0
  486. package/.agents/scripts/lib/orchestration/single-story-close/phases/pull-request.js +106 -0
  487. package/.agents/scripts/lib/orchestration/single-story-close/phases/push.js +42 -0
  488. package/.agents/scripts/lib/orchestration/single-story-close/phases/worktree-reap.js +73 -0
  489. package/.agents/scripts/lib/orchestration/single-story-close/phases/wrong-tree-guard.js +225 -0
  490. package/.agents/scripts/lib/orchestration/single-story-close/runner.js +315 -0
  491. package/.agents/scripts/lib/orchestration/single-story-lease-guard.js +149 -0
  492. package/.agents/scripts/lib/orchestration/skill-capsule-loader.js +110 -0
  493. package/.agents/scripts/lib/orchestration/spec-freshness.js +320 -0
  494. package/.agents/scripts/lib/orchestration/spec-renderer.js +456 -0
  495. package/.agents/scripts/lib/orchestration/spec-section-validator.js +80 -0
  496. package/.agents/scripts/lib/orchestration/story-close/auto-refresh-runner.js +797 -0
  497. package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/gate-failure.js +163 -0
  498. package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/pre-merge-attribution.js +152 -0
  499. package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/refresh-commit.js +387 -0
  500. package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/regression-projection.js +266 -0
  501. package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/scope-discovery.js +48 -0
  502. package/.agents/scripts/lib/orchestration/story-close/baseline-attribution-wiring.js +67 -0
  503. package/.agents/scripts/lib/orchestration/story-close/baseline-attribution.js +161 -0
  504. package/.agents/scripts/lib/orchestration/story-close/baseline-friction-body.js +117 -0
  505. package/.agents/scripts/lib/orchestration/story-close/cd-out-guard.js +86 -0
  506. package/.agents/scripts/lib/orchestration/story-close/cleanup-reconciler.js +147 -0
  507. package/.agents/scripts/lib/orchestration/story-close/close-inputs.js +142 -0
  508. package/.agents/scripts/lib/orchestration/story-close/comment-bodies.js +62 -0
  509. package/.agents/scripts/lib/orchestration/story-close/format-autofix-scoped.js +221 -0
  510. package/.agents/scripts/lib/orchestration/story-close/format-autofix-shared.js +123 -0
  511. package/.agents/scripts/lib/orchestration/story-close/format-autofix.js +216 -0
  512. package/.agents/scripts/lib/orchestration/story-close/merge-runner.js +636 -0
  513. package/.agents/scripts/lib/orchestration/story-close/merge-subject.js +198 -0
  514. package/.agents/scripts/lib/orchestration/story-close/phases/branch-restore.js +105 -0
  515. package/.agents/scripts/lib/orchestration/story-close/phases/close.js +222 -0
  516. package/.agents/scripts/lib/orchestration/story-close/phases/code-review.js +220 -0
  517. package/.agents/scripts/lib/orchestration/story-close/phases/gates.js +291 -0
  518. package/.agents/scripts/lib/orchestration/story-close/phases/locked-pipeline.js +234 -0
  519. package/.agents/scripts/lib/orchestration/story-close/phases/preflight.js +110 -0
  520. package/.agents/scripts/lib/orchestration/story-close/phases/refresh.js +86 -0
  521. package/.agents/scripts/lib/orchestration/story-close/phases/timeout-blocked-emitter.js +112 -0
  522. package/.agents/scripts/lib/orchestration/story-close/phases/timeout-blocked.js +157 -0
  523. package/.agents/scripts/lib/orchestration/story-close/post-merge-close.js +434 -0
  524. package/.agents/scripts/lib/orchestration/story-close/pre-merge-validation.js +290 -0
  525. package/.agents/scripts/lib/orchestration/story-close-recovery.js +643 -0
  526. package/.agents/scripts/lib/orchestration/structured-comment-parser.js +67 -0
  527. package/.agents/scripts/lib/orchestration/task-body-validator.js +391 -0
  528. package/.agents/scripts/lib/orchestration/ticket-lease.js +358 -0
  529. package/.agents/scripts/lib/orchestration/ticket-validator-conflicts.js +783 -0
  530. package/.agents/scripts/lib/orchestration/ticket-validator-sizing.js +367 -0
  531. package/.agents/scripts/lib/orchestration/ticket-validator.js +691 -0
  532. package/.agents/scripts/lib/orchestration/ticketing/bulk.js +723 -0
  533. package/.agents/scripts/lib/orchestration/ticketing/reads.js +474 -0
  534. package/.agents/scripts/lib/orchestration/ticketing/state.js +559 -0
  535. package/.agents/scripts/lib/orchestration/ticketing.js +55 -0
  536. package/.agents/scripts/lib/orchestration/wave-marker.js +28 -0
  537. package/.agents/scripts/lib/orchestration/wave-record-io.js +277 -0
  538. package/.agents/scripts/lib/orchestration/wave-record-notifications.js +189 -0
  539. package/.agents/scripts/lib/orchestration/wave-record-projection.js +423 -0
  540. package/.agents/scripts/lib/path-security.js +25 -0
  541. package/.agents/scripts/lib/plan-phase-cleanup.js +125 -0
  542. package/.agents/scripts/lib/preflight-runner.js +196 -0
  543. package/.agents/scripts/lib/presentation/dispatch-manifest-render.js +95 -0
  544. package/.agents/scripts/lib/presentation/manifest-builder.js +245 -0
  545. package/.agents/scripts/lib/presentation/manifest-formatter.js +243 -0
  546. package/.agents/scripts/lib/presentation/manifest-helpers.js +213 -0
  547. package/.agents/scripts/lib/presentation/manifest-persistence.js +262 -0
  548. package/.agents/scripts/lib/presentation/manifest-procedures.js +55 -0
  549. package/.agents/scripts/lib/presentation/manifest-render-waves.js +252 -0
  550. package/.agents/scripts/lib/presentation/manifest-renderer.js +188 -0
  551. package/.agents/scripts/lib/presentation/manifest-story-views.js +119 -0
  552. package/.agents/scripts/lib/provider-factory.js +80 -0
  553. package/.agents/scripts/lib/push-epic-retry.js +209 -0
  554. package/.agents/scripts/lib/qa/console-allowlist.js +151 -0
  555. package/.agents/scripts/lib/qa/coverage-report.js +181 -0
  556. package/.agents/scripts/lib/qa/coverage-verdict.js +296 -0
  557. package/.agents/scripts/lib/qa/propose-missing-test.js +95 -0
  558. package/.agents/scripts/lib/qa/qa-context-hydrator.js +296 -0
  559. package/.agents/scripts/lib/qa/qa-session.js +197 -0
  560. package/.agents/scripts/lib/qa/redact-evidence.js +245 -0
  561. package/.agents/scripts/lib/qa/resolve-qa-contract.js +190 -0
  562. package/.agents/scripts/lib/qa/resolve-selection.js +373 -0
  563. package/.agents/scripts/lib/runtime-deps/ensure-installed.js +100 -0
  564. package/.agents/scripts/lib/runtime-deps/manifest.js +96 -0
  565. package/.agents/scripts/lib/runtime-deps/preflight.js +78 -0
  566. package/.agents/scripts/lib/runtime-deps/scan-imports.js +202 -0
  567. package/.agents/scripts/lib/signals/detectors/common.js +36 -0
  568. package/.agents/scripts/lib/signals/detectors/hotspot.js +298 -0
  569. package/.agents/scripts/lib/signals/detectors/index.js +14 -0
  570. package/.agents/scripts/lib/signals/detectors/retry.js +289 -0
  571. package/.agents/scripts/lib/signals/detectors/rework.js +204 -0
  572. package/.agents/scripts/lib/signals/index.js +39 -0
  573. package/.agents/scripts/lib/signals/read.js +268 -0
  574. package/.agents/scripts/lib/signals/schema.js +225 -0
  575. package/.agents/scripts/lib/signals/span-tree.js +290 -0
  576. package/.agents/scripts/lib/signals/write.js +19 -0
  577. package/.agents/scripts/lib/single-story/confirm-merge.js +201 -0
  578. package/.agents/scripts/lib/single-story/story-merged-notify.js +126 -0
  579. package/.agents/scripts/lib/single-story-sweep/protection.js +274 -0
  580. package/.agents/scripts/lib/single-story-sweep/sweep-lock.js +169 -0
  581. package/.agents/scripts/lib/single-story-sweep.js +329 -0
  582. package/.agents/scripts/lib/skills/parse-skill.js +202 -0
  583. package/.agents/scripts/lib/skills/walk-skill-files.js +56 -0
  584. package/.agents/scripts/lib/spec/index.js +36 -0
  585. package/.agents/scripts/lib/spec/loader.js +425 -0
  586. package/.agents/scripts/lib/spec/state.js +217 -0
  587. package/.agents/scripts/lib/story-body/story-body.js +743 -0
  588. package/.agents/scripts/lib/story-init/blocker-validator.js +68 -0
  589. package/.agents/scripts/lib/story-init/branch-initializer.js +422 -0
  590. package/.agents/scripts/lib/story-init/context-resolver.js +92 -0
  591. package/.agents/scripts/lib/story-init/donor-precheck.js +207 -0
  592. package/.agents/scripts/lib/story-init/hierarchy-tracer.js +36 -0
  593. package/.agents/scripts/lib/story-init/state-transitioner.js +80 -0
  594. package/.agents/scripts/lib/story-init/task-graph-builder.js +114 -0
  595. package/.agents/scripts/lib/story-init/transition-summary.js +34 -0
  596. package/.agents/scripts/lib/story-lifecycle.js +186 -0
  597. package/.agents/scripts/lib/story-plan.js +246 -0
  598. package/.agents/scripts/lib/task-utils.js +26 -0
  599. package/.agents/scripts/lib/templates/decomposer-prompts.js +168 -0
  600. package/.agents/scripts/lib/test-env.js +30 -0
  601. package/.agents/scripts/lib/test-isolate/env-snapshot-loader.js +52 -0
  602. package/.agents/scripts/lib/test-isolate/list-files.js +90 -0
  603. package/.agents/scripts/lib/test-isolate/parse-tap.js +75 -0
  604. package/.agents/scripts/lib/test-isolate/runner.js +483 -0
  605. package/.agents/scripts/lib/test-profile/parse-tap.js +136 -0
  606. package/.agents/scripts/lib/test-profile/render-report.js +45 -0
  607. package/.agents/scripts/lib/test-reserved-epic-temp-ids.js +35 -0
  608. package/.agents/scripts/lib/test-tiers.js +94 -0
  609. package/.agents/scripts/lib/util/concurrent-map.js +59 -0
  610. package/.agents/scripts/lib/util/phase-timer-state.js +72 -0
  611. package/.agents/scripts/lib/util/phase-timer.js +163 -0
  612. package/.agents/scripts/lib/util/poll-loop.js +86 -0
  613. package/.agents/scripts/lib/util/with-timeout.js +32 -0
  614. package/.agents/scripts/lib/validation-evidence.js +323 -0
  615. package/.agents/scripts/lib/wave-runner/tick.js +665 -0
  616. package/.agents/scripts/lib/wave-runner/wave-checkpoint.js +91 -0
  617. package/.agents/scripts/lib/wave-runner/wave-runner-error.js +19 -0
  618. package/.agents/scripts/lib/workers/crap-worker.js +197 -0
  619. package/.agents/scripts/lib/workers/maintainability-report-worker.js +137 -0
  620. package/.agents/scripts/lib/workers/maintainability-worker.js +79 -0
  621. package/.agents/scripts/lib/workspace-provisioner.js +189 -0
  622. package/.agents/scripts/lib/worktree/bootstrapper.js +48 -0
  623. package/.agents/scripts/lib/worktree/inspector.js +140 -0
  624. package/.agents/scripts/lib/worktree/lifecycle/creation.js +118 -0
  625. package/.agents/scripts/lib/worktree/lifecycle/drift-detection.js +62 -0
  626. package/.agents/scripts/lib/worktree/lifecycle/force-drain.js +276 -0
  627. package/.agents/scripts/lib/worktree/lifecycle/gc.js +49 -0
  628. package/.agents/scripts/lib/worktree/lifecycle/merge-reachability.js +178 -0
  629. package/.agents/scripts/lib/worktree/lifecycle/pending-cleanup.js +264 -0
  630. package/.agents/scripts/lib/worktree/lifecycle/precheck.js +100 -0
  631. package/.agents/scripts/lib/worktree/lifecycle/reap.js +588 -0
  632. package/.agents/scripts/lib/worktree/lifecycle/registry-sync.js +124 -0
  633. package/.agents/scripts/lib/worktree/lifecycle/shared.js +26 -0
  634. package/.agents/scripts/lib/worktree/lifecycle-manager.js +40 -0
  635. package/.agents/scripts/lib/worktree/node-modules-strategy.js +349 -0
  636. package/.agents/scripts/lib/worktree-manager.js +243 -0
  637. package/.agents/scripts/lifecycle-diff.js +206 -0
  638. package/.agents/scripts/lifecycle-emit-story-dispatch.js +194 -0
  639. package/.agents/scripts/lifecycle-emit.js +479 -0
  640. package/.agents/scripts/lint-baseline.js +507 -0
  641. package/.agents/scripts/lint-label-vocabulary.js +237 -0
  642. package/.agents/scripts/loc-delta.js +205 -0
  643. package/.agents/scripts/notify.js +307 -0
  644. package/.agents/scripts/package.json +3 -0
  645. package/.agents/scripts/post-structured-comment.js +127 -0
  646. package/.agents/scripts/pr-watch-with-update.js +152 -0
  647. package/.agents/scripts/providers/github/auth.js +65 -0
  648. package/.agents/scripts/providers/github/board-add.js +63 -0
  649. package/.agents/scripts/providers/github/branch-protection.js +186 -0
  650. package/.agents/scripts/providers/github/cache.js +72 -0
  651. package/.agents/scripts/providers/github/comments.js +131 -0
  652. package/.agents/scripts/providers/github/compose.js +111 -0
  653. package/.agents/scripts/providers/github/errors.js +242 -0
  654. package/.agents/scripts/providers/github/issues.js +242 -0
  655. package/.agents/scripts/providers/github/labels.js +179 -0
  656. package/.agents/scripts/providers/github/mappers.js +126 -0
  657. package/.agents/scripts/providers/github/merge-methods.js +82 -0
  658. package/.agents/scripts/providers/github/project-board.js +47 -0
  659. package/.agents/scripts/providers/github/projects-v2-graphql.js +472 -0
  660. package/.agents/scripts/providers/github/prs.js +103 -0
  661. package/.agents/scripts/providers/github/request-helpers.js +110 -0
  662. package/.agents/scripts/providers/github/sub-issues.js +369 -0
  663. package/.agents/scripts/providers/github/tickets.js +381 -0
  664. package/.agents/scripts/providers/github/transient-retry.js +62 -0
  665. package/.agents/scripts/providers/github.js +157 -0
  666. package/.agents/scripts/quality-preview.js +327 -0
  667. package/.agents/scripts/quality-watch.js +223 -0
  668. package/.agents/scripts/render-manifest.js +143 -0
  669. package/.agents/scripts/resync-status-column.js +176 -0
  670. package/.agents/scripts/retro-run.js +167 -0
  671. package/.agents/scripts/run-audit-suite.js +97 -0
  672. package/.agents/scripts/run-coverage.js +103 -0
  673. package/.agents/scripts/run-lint.js +94 -0
  674. package/.agents/scripts/run-test-profile.js +126 -0
  675. package/.agents/scripts/run-tests.js +185 -0
  676. package/.agents/scripts/run-verify.js +56 -0
  677. package/.agents/scripts/select-audits.js +155 -0
  678. package/.agents/scripts/signals-view.js +294 -0
  679. package/.agents/scripts/single-story-close.js +83 -0
  680. package/.agents/scripts/single-story-confirm-merge.js +183 -0
  681. package/.agents/scripts/single-story-init.js +692 -0
  682. package/.agents/scripts/stories-wave-tick.js +415 -0
  683. package/.agents/scripts/story-close.js +246 -0
  684. package/.agents/scripts/story-deliver-prepare.js +267 -0
  685. package/.agents/scripts/story-init.js +516 -0
  686. package/.agents/scripts/story-phase.js +327 -0
  687. package/.agents/scripts/story-plan.js +284 -0
  688. package/.agents/scripts/sync-agentrc.js +71 -0
  689. package/.agents/scripts/sync-branch-from-base.js +138 -0
  690. package/.agents/scripts/sync-claude-commands.js +151 -0
  691. package/.agents/scripts/test-isolate.js +222 -0
  692. package/.agents/scripts/test-wrapper.js +108 -0
  693. package/.agents/scripts/update-coverage-baseline.js +129 -0
  694. package/.agents/scripts/update-crap-baseline.js +177 -0
  695. package/.agents/scripts/update-duplication-baseline.js +134 -0
  696. package/.agents/scripts/update-maintainability-baseline.js +183 -0
  697. package/.agents/scripts/update-mutation-baseline.js +189 -0
  698. package/.agents/scripts/update-ticket-state.js +107 -0
  699. package/.agents/scripts/validate-docs-freshness.js +259 -0
  700. package/.agents/scripts/validate-skills.js +278 -0
  701. package/.agents/scripts/wave-tick.js +335 -0
  702. package/.agents/skills/core/analyze-execution/SKILL.md +98 -0
  703. package/.agents/skills/core/api-and-interface-design/SKILL.md +327 -0
  704. package/.agents/skills/core/baseline-refresh/SKILL.md +181 -0
  705. package/.agents/skills/core/browser-testing-with-devtools/SKILL.md +352 -0
  706. package/.agents/skills/core/ci-cd-and-automation/SKILL.md +274 -0
  707. package/.agents/skills/core/ci-cd-and-automation/examples.md +211 -0
  708. package/.agents/skills/core/code-review-and-quality/SKILL.md +421 -0
  709. package/.agents/skills/core/code-simplification/SKILL.md +389 -0
  710. package/.agents/skills/core/context-engineering/SKILL.md +309 -0
  711. package/.agents/skills/core/context-engineering/examples.md +58 -0
  712. package/.agents/skills/core/debugging-and-error-recovery/SKILL.md +338 -0
  713. package/.agents/skills/core/deprecation-and-migration/SKILL.md +250 -0
  714. package/.agents/skills/core/diagnose-friction/SKILL.md +79 -0
  715. package/.agents/skills/core/documentation-and-adrs/SKILL.md +323 -0
  716. package/.agents/skills/core/epic-plan-consolidate/SKILL.md +145 -0
  717. package/.agents/skills/core/epic-plan-decompose-author/SKILL.md +425 -0
  718. package/.agents/skills/core/epic-plan-spec-author/SKILL.md +393 -0
  719. package/.agents/skills/core/frontend-ui-engineering/SKILL.md +357 -0
  720. package/.agents/skills/core/git-workflow-and-versioning/SKILL.md +352 -0
  721. package/.agents/skills/core/hydrate-context/SKILL.md +118 -0
  722. package/.agents/skills/core/idea-refinement/SKILL.md +317 -0
  723. package/.agents/skills/core/idea-refinement/examples.md +437 -0
  724. package/.agents/skills/core/idea-refinement/frameworks.md +135 -0
  725. package/.agents/skills/core/idea-refinement/refinement-criteria.md +155 -0
  726. package/.agents/skills/core/idea-refinement/scripts/idea-refine.sh +15 -0
  727. package/.agents/skills/core/incremental-implementation/SKILL.md +271 -0
  728. package/.agents/skills/core/introducing-a-baseline-gate/SKILL.md +213 -0
  729. package/.agents/skills/core/knowledge-transfer/SKILL.md +175 -0
  730. package/.agents/skills/core/mutation-survivor-remediation/SKILL.md +117 -0
  731. package/.agents/skills/core/performance-optimization/SKILL.md +314 -0
  732. package/.agents/skills/core/planning-and-task-breakdown/SKILL.md +277 -0
  733. package/.agents/skills/core/property-based-testing/SKILL.md +148 -0
  734. package/.agents/skills/core/qa-coverage-mapping/SKILL.md +105 -0
  735. package/.agents/skills/core/refactoring-discipline/SKILL.md +111 -0
  736. package/.agents/skills/core/scope-triage/SKILL.md +127 -0
  737. package/.agents/skills/core/security-and-hardening/SKILL.md +400 -0
  738. package/.agents/skills/core/shipping-and-launch/SKILL.md +328 -0
  739. package/.agents/skills/core/spec-driven-development/SKILL.md +252 -0
  740. package/.agents/skills/core/test-driven-development/SKILL.md +475 -0
  741. package/.agents/skills/core/using-agent-skills/SKILL.md +232 -0
  742. package/.agents/skills/skills.index.json +596 -0
  743. package/.agents/skills/stack/architecture/monorepo-path-strategist/SKILL.md +31 -0
  744. package/.agents/skills/stack/architecture/structured-output-zod/SKILL.md +51 -0
  745. package/.agents/skills/stack/architecture/subagent-orchestration/SKILL.md +48 -0
  746. package/.agents/skills/stack/backend/cloudflare-hono-architect/SKILL.md +31 -0
  747. package/.agents/skills/stack/backend/cloudflare-hono-architect/examples/route-template.ts +33 -0
  748. package/.agents/skills/stack/backend/cloudflare-queue-manager/SKILL.md +31 -0
  749. package/.agents/skills/stack/backend/cloudflare-workers/SKILL.md +51 -0
  750. package/.agents/skills/stack/backend/highlevel-crm/SKILL.md +54 -0
  751. package/.agents/skills/stack/backend/sqlite-drizzle-expert/SKILL.md +29 -0
  752. package/.agents/skills/stack/backend/sqlite-drizzle-expert/examples/schema-template.ts +30 -0
  753. package/.agents/skills/stack/backend/stripe-integration/SKILL.md +57 -0
  754. package/.agents/skills/stack/backend/stripe-integration/scripts/listen-stripe.sh +9 -0
  755. package/.agents/skills/stack/backend/turso-sqlite/SKILL.md +48 -0
  756. package/.agents/skills/stack/frontend/astro/SKILL.md +62 -0
  757. package/.agents/skills/stack/frontend/astro-react-island-strategist/SKILL.md +30 -0
  758. package/.agents/skills/stack/frontend/expo-react-native-developer/SKILL.md +29 -0
  759. package/.agents/skills/stack/frontend/google-analytics-v4/SKILL.md +50 -0
  760. package/.agents/skills/stack/frontend/tailwind-v4/SKILL.md +58 -0
  761. package/.agents/skills/stack/frontend/ui-accessibility-engineer/SKILL.md +34 -0
  762. package/.agents/skills/stack/qa/audit-accessibility/SKILL.md +51 -0
  763. package/.agents/skills/stack/qa/gherkin-authoring/SKILL.md +257 -0
  764. package/.agents/skills/stack/qa/gherkin-authoring/examples/invoice-issue.feature +41 -0
  765. package/.agents/skills/stack/qa/lighthouse-baseline/SKILL.md +199 -0
  766. package/.agents/skills/stack/qa/playwright/SKILL.md +50 -0
  767. package/.agents/skills/stack/qa/playwright-bdd/SKILL.md +188 -0
  768. package/.agents/skills/stack/qa/qa-explore-driving/SKILL.md +142 -0
  769. package/.agents/skills/stack/qa/qa-harness/SKILL.md +220 -0
  770. package/.agents/skills/stack/qa/vitest/SKILL.md +51 -0
  771. package/.agents/skills/stack/security/backend-security-patterns/SKILL.md +68 -0
  772. package/.agents/starter-agentrc.json +22 -0
  773. package/.agents/templates/agent-protocol.md +72 -0
  774. package/.agents/templates/docs/architecture.md +30 -0
  775. package/.agents/templates/docs/decisions.md +24 -0
  776. package/.agents/templates/epic-from-idea.md +21 -0
  777. package/.agents/templates/single-story-body.md +17 -0
  778. package/.agents/workflows/agents-update.md +415 -0
  779. package/.agents/workflows/audit-architecture.md +312 -0
  780. package/.agents/workflows/audit-clean-code.md +179 -0
  781. package/.agents/workflows/audit-dependencies.md +91 -0
  782. package/.agents/workflows/audit-devops.md +110 -0
  783. package/.agents/workflows/audit-lighthouse.md +260 -0
  784. package/.agents/workflows/audit-performance.md +161 -0
  785. package/.agents/workflows/audit-privacy.md +104 -0
  786. package/.agents/workflows/audit-quality.md +191 -0
  787. package/.agents/workflows/audit-security.md +156 -0
  788. package/.agents/workflows/audit-seo.md +118 -0
  789. package/.agents/workflows/audit-sre.md +139 -0
  790. package/.agents/workflows/audit-to-stories.md +257 -0
  791. package/.agents/workflows/audit-ux-ui.md +102 -0
  792. package/.agents/workflows/epic-deliver.md +864 -0
  793. package/.agents/workflows/epic-plan.md +998 -0
  794. package/.agents/workflows/explain.md +118 -0
  795. package/.agents/workflows/git-cleanup.md +250 -0
  796. package/.agents/workflows/git-commit-all.md +15 -0
  797. package/.agents/workflows/git-merge-pr.md +377 -0
  798. package/.agents/workflows/git-pr-all.md +278 -0
  799. package/.agents/workflows/git-push.md +60 -0
  800. package/.agents/workflows/helpers/_merge-conflict-template.md +54 -0
  801. package/.agents/workflows/helpers/acceptance-self-eval.md +74 -0
  802. package/.agents/workflows/helpers/agents-sync-config.md +129 -0
  803. package/.agents/workflows/helpers/code-quality-guardrails.md +101 -0
  804. package/.agents/workflows/helpers/code-review.md +370 -0
  805. package/.agents/workflows/helpers/diagnose.md +117 -0
  806. package/.agents/workflows/helpers/epic-audit.md +295 -0
  807. package/.agents/workflows/helpers/epic-deliver-story.md +370 -0
  808. package/.agents/workflows/helpers/epic-plan-decompose.md +199 -0
  809. package/.agents/workflows/helpers/epic-plan-spec.md +184 -0
  810. package/.agents/workflows/helpers/epic-testing.md +125 -0
  811. package/.agents/workflows/helpers/parallel-tooling.md +88 -0
  812. package/.agents/workflows/helpers/signals.md +112 -0
  813. package/.agents/workflows/helpers/single-story-deliver.md +636 -0
  814. package/.agents/workflows/helpers/worktree-lifecycle.md +317 -0
  815. package/.agents/workflows/onboard.md +207 -0
  816. package/.agents/workflows/qa-assist.md +293 -0
  817. package/.agents/workflows/qa-explore.md +350 -0
  818. package/.agents/workflows/qa-run-harness.md +288 -0
  819. package/.agents/workflows/story-deliver.md +327 -0
  820. package/.agents/workflows/story-plan.md +233 -0
  821. package/LICENSE +21 -0
  822. package/README.md +193 -0
  823. package/bin/mandrel.js +56 -0
  824. package/bin/postinstall.js +195 -0
  825. package/lib/cli/__tests__/migrate.test.js +268 -0
  826. package/lib/cli/__tests__/sync-local-zone.test.js +247 -0
  827. package/lib/cli/__tests__/sync.test.js +372 -0
  828. package/lib/cli/__tests__/update-major.test.js +217 -0
  829. package/lib/cli/__tests__/update.test.js +696 -0
  830. package/lib/cli/__tests__/version-check.test.js +398 -0
  831. package/lib/cli/doctor.js +124 -0
  832. package/lib/cli/explain.js +107 -0
  833. package/lib/cli/migrate.js +260 -0
  834. package/lib/cli/registry.js +830 -0
  835. package/lib/cli/sync-commands.js +50 -0
  836. package/lib/cli/sync.js +200 -0
  837. package/lib/cli/uninstall.js +795 -0
  838. package/lib/cli/update.js +854 -0
  839. package/lib/cli/version-check.js +206 -0
  840. package/lib/migrations/README.md +69 -0
  841. package/lib/migrations/__tests__/index.test.js +216 -0
  842. package/lib/migrations/index.js +164 -0
  843. package/package.json +105 -0
@@ -0,0 +1,723 @@
1
+ /**
2
+ * lib/orchestration/ticketing/bulk.js — Cascade + batch ticketing surface.
3
+ *
4
+ * Owns the multi-ticket, cross-parent operations: the cascade walk that
5
+ * fires when a child ticket reaches `agent::done`, the per-parent
6
+ * sequencing lock, the transient-error classifier and retry budget, and
7
+ * the partial-failure log helper. Pulled out of `../ticketing.js` under
8
+ * Story #1848 so the verb-family split (`reads` / `state` / `bulk`) is
9
+ * complete and the parent collapses to a pure re-export facade.
10
+ *
11
+ * Note on the cycle with `./state.js`: `cascadeCompletion` recursively
12
+ * calls `transitionTicketState`, which in turn — when the cascade flag is
13
+ * on — calls back into `cascadeCompletion`. ESM tolerates the cycle
14
+ * because every binding is resolved at call-time. Both modules complete
15
+ * evaluation before any of their exported functions run.
16
+ */
17
+
18
+ import { Logger } from '../../Logger.js';
19
+ import { TYPE_LABELS } from '../../label-constants.js';
20
+ import { dispatchCascadeGroups, groupByAncestor } from '../cascade-grouping.js';
21
+ import { ALL_STATES, STATE_LABELS } from './reads.js';
22
+ import {
23
+ postStructuredComment,
24
+ toggleTasklistCheckbox,
25
+ transitionTicketState,
26
+ } from './state.js';
27
+
28
+ // Re-export `groupByAncestor` so external callers that imported it from
29
+ // the ticketing facade continue to work after the verb-family split.
30
+ export { groupByAncestor };
31
+
32
+ /**
33
+ * Retry budget for transient `gh` failures (rate limit, secondary rate limit,
34
+ * 5xx, transport timeouts) inside the cascade transition. Three attempts with
35
+ * exponential backoff (250ms / 500ms / 1000ms) mirrors the budget used by
36
+ * `gitFetchWithRetry` (see `lib/git-utils.js`) and the HTTP-client retry path
37
+ * referenced by `epic-plan-decompose.js`. Backoff is overridable via
38
+ * {@link __setCascadeRetryDelays} so tests don't pay real wall-clock time.
39
+ */
40
+ const CASCADE_RETRY_BACKOFF_MS = [250, 500, 1000];
41
+ const defaultCascadeSleep = (ms) =>
42
+ new Promise((resolve) => setTimeout(resolve, ms));
43
+ let _cascadeRetryDelays = CASCADE_RETRY_BACKOFF_MS;
44
+ let _cascadeSleep = defaultCascadeSleep;
45
+
46
+ /**
47
+ * Test seam — replace the backoff schedule and/or the sleep implementation
48
+ * used by the cascade retry loop. Restore by calling with no arguments.
49
+ *
50
+ * @param {{ delays?: number[], sleep?: (ms: number) => Promise<void> }} [opts]
51
+ */
52
+ export function __setCascadeRetryDelays(opts = {}) {
53
+ _cascadeRetryDelays = Array.isArray(opts.delays)
54
+ ? opts.delays
55
+ : CASCADE_RETRY_BACKOFF_MS;
56
+ _cascadeSleep =
57
+ typeof opts.sleep === 'function' ? opts.sleep : defaultCascadeSleep;
58
+ }
59
+
60
+ /**
61
+ * Per-parent serial lock used to prevent two concurrent cascades within the
62
+ * same wave from racing the parent's "all children done?" check (Story
63
+ * #1817). The map is keyed by parent issue number; entries are reclaimed
64
+ * once the last awaiter finishes. The lock is scoped to a single Node
65
+ * process — cross-process races (multiple worktrees closing in parallel)
66
+ * still rely on the retry/idempotency path.
67
+ *
68
+ * @type {Map<number, Promise<unknown>>}
69
+ */
70
+ const parentCascadeLocks = new Map();
71
+
72
+ /**
73
+ * Acquire the per-parent cascade lock, run `fn`, then release. Awaiters
74
+ * queue strictly in invocation order. Failures of prior holders do not
75
+ * propagate — each acquirer sees a clean entry into `fn`.
76
+ *
77
+ * @template R
78
+ * @param {number} parentId
79
+ * @param {() => Promise<R>} fn
80
+ * @returns {Promise<R>}
81
+ */
82
+ async function withParentCascadeLock(parentId, fn) {
83
+ const prev = parentCascadeLocks.get(parentId) ?? Promise.resolve();
84
+ const current = prev.then(
85
+ () => fn(),
86
+ () => fn(),
87
+ );
88
+ parentCascadeLocks.set(parentId, current);
89
+ try {
90
+ return await current;
91
+ } finally {
92
+ if (parentCascadeLocks.get(parentId) === current) {
93
+ parentCascadeLocks.delete(parentId);
94
+ }
95
+ }
96
+ }
97
+
98
+ /**
99
+ * Test seam — clear the per-parent cascade lock map between tests so a
100
+ * pending entry from one scenario does not leak into the next.
101
+ */
102
+ export function __resetParentCascadeLocks() {
103
+ parentCascadeLocks.clear();
104
+ }
105
+
106
+ /**
107
+ * Classifies a thrown cascade error as "transient" (rate limit, secondary
108
+ * rate limit, 5xx, transport timeout / reset) so the retry loop can back
109
+ * off instead of surfacing the failure to the operator. Provider-agnostic:
110
+ * matches on typed error names (`GhRateLimitError`), HTTP status, and
111
+ * conservative regex over stderr + message.
112
+ *
113
+ * @param {unknown} err
114
+ * @returns {boolean}
115
+ */
116
+ function isTransientCascadeError(err) {
117
+ if (!err || typeof err !== 'object') return false;
118
+ if (err.name === 'GhRateLimitError') return true;
119
+ const status = typeof err.status === 'number' ? err.status : null;
120
+ if (status === 429) return true;
121
+ if (status !== null && status >= 500) return true;
122
+ const haystack = `${err.message ?? ''}\n${err.stderr ?? ''}`.toLowerCase();
123
+ if (
124
+ /secondary rate limit|api rate limit exceeded|rate limit exceeded/.test(
125
+ haystack,
126
+ )
127
+ )
128
+ return true;
129
+ if (/abuse detection/.test(haystack)) return true;
130
+ if (/econnreset|etimedout|enotfound|eai_again|abort_err/.test(haystack))
131
+ return true;
132
+ if (/timed out|timeout|aborted|fetch failed|network/.test(haystack))
133
+ return true;
134
+ return false;
135
+ }
136
+
137
+ /**
138
+ * Render a cascade-failure error into a single log-friendly string that
139
+ * preserves stderr and exit-code context. The legacy log line collapsed
140
+ * `gh-exec`-thrown errors to a bare "exit 1" message, which made the
141
+ * failure mode unclassifiable post-hoc (see Story #1817).
142
+ *
143
+ * @param {unknown} err
144
+ * @returns {string}
145
+ */
146
+ function formatCascadeError(err) {
147
+ if (!err) return 'unknown error';
148
+ if (typeof err !== 'object') return String(err);
149
+ const parts = [];
150
+ if (err.name && err.name !== 'Error') parts.push(`${err.name}`);
151
+ if (err.message) parts.push(err.message);
152
+ if (typeof err.code === 'number') parts.push(`exit=${err.code}`);
153
+ if (typeof err.status === 'number') parts.push(`http=${err.status}`);
154
+ if (typeof err.stderr === 'string' && err.stderr.trim()) {
155
+ const trimmed = err.stderr.trim().replace(/\s+/g, ' ');
156
+ const capped = trimmed.length > 400 ? `${trimmed.slice(0, 400)}…` : trimmed;
157
+ parts.push(`stderr=${capped}`);
158
+ }
159
+ return parts.join(' | ') || String(err);
160
+ }
161
+
162
+ /**
163
+ * Run `fn` with exponential backoff on transient errors. Non-transient
164
+ * errors propagate immediately on the first attempt. The total attempt
165
+ * count is `delays.length + 1` (one initial attempt plus one retry per
166
+ * delay).
167
+ *
168
+ * @template R
169
+ * @param {() => Promise<R>} fn
170
+ * @param {{ onRetry?: (err: unknown, attempt: number, delayMs: number) => void }} [opts]
171
+ * @returns {Promise<R>}
172
+ */
173
+ async function retryTransient(fn, opts = {}) {
174
+ const delays = _cascadeRetryDelays;
175
+ let attempt = 0;
176
+ for (;;) {
177
+ try {
178
+ return await fn();
179
+ } catch (err) {
180
+ if (!isTransientCascadeError(err)) throw err;
181
+ if (attempt >= delays.length) throw err;
182
+ const delay = delays[attempt];
183
+ if (typeof opts.onRetry === 'function') {
184
+ try {
185
+ opts.onRetry(err, attempt + 1, delay);
186
+ } catch {
187
+ // listener failures must not abort the retry
188
+ }
189
+ }
190
+ await _cascadeSleep(delay);
191
+ attempt += 1;
192
+ }
193
+ }
194
+ }
195
+
196
+ /**
197
+ * Emit a warn line for every per-parent cascade failure captured by
198
+ * {@link cascadeCompletion}. Each `error` string is pre-formatted with
199
+ * stderr + exit-code by `formatCascadeError`, so callers can pass the
200
+ * raw envelope through without further wrapping. Called from
201
+ * `state.js`'s `transitionTicketState` to keep its cyclomatic
202
+ * complexity inside the project's per-method CRAP ceiling (Story #1817,
203
+ * Story #1848).
204
+ *
205
+ * @param {number} ticketId The ticket whose `agent::done` transition
206
+ * triggered the cascade.
207
+ * @param {{ failed?: Array<{ parentId: number, error: string }> } | null} cascade
208
+ */
209
+ export function logCascadePartialFailures(ticketId, cascade) {
210
+ const cascadeFailures = cascade?.failed ?? [];
211
+ for (const { parentId, error } of cascadeFailures) {
212
+ Logger.warn(
213
+ `[Ticketing] Cascade from #${ticketId} hit partial-failure on parent #${parentId}: ${error}`,
214
+ );
215
+ }
216
+ }
217
+
218
+ /**
219
+ * Per-parent body of {@link cascadeCompletion}. Pulled out so the outer
220
+ * function can dispatch disjoint groups in parallel while each parent
221
+ * still runs against its own captured logger, ensuring byte-identical
222
+ * log output across serial and parallel execution paths.
223
+ *
224
+ * @param {import('../../ITicketingProvider.js').ITicketingProvider} provider
225
+ * @param {number} ticketId - The ticket whose `agent::done` transition
226
+ * triggered the cascade.
227
+ * @param {number} parentId - The parent currently being processed.
228
+ * @param {{ notify?: Function, _logger?: object }} opts
229
+ * @returns {Promise<{ cascadedTo: number[], failed: Array<{ parentId: number, error: string }>, error?: Error }>}
230
+ */
231
+ async function processCascadeParent(provider, ticketId, parentId, opts) {
232
+ const logger = opts._logger ?? Logger;
233
+ return withParentCascadeLock(parentId, () =>
234
+ processCascadeParentLocked(provider, ticketId, parentId, opts, logger),
235
+ );
236
+ }
237
+
238
+ /**
239
+ * Body of {@link processCascadeParent} that runs under the per-parent lock.
240
+ * Split out so the lock-acquire scaffolding stays a one-liner and the
241
+ * cyclomatic complexity of the per-parent worker doesn't drift over the
242
+ * project's CRAP ceiling once the retry / idempotency branches are added.
243
+ *
244
+ * @returns {Promise<{ cascadedTo: number[], failed: Array<{ parentId: number, error: string }> }>}
245
+ */
246
+ async function processCascadeParentLocked(
247
+ provider,
248
+ ticketId,
249
+ parentId,
250
+ opts,
251
+ logger,
252
+ ) {
253
+ const cascadedTo = [];
254
+ const failed = [];
255
+ try {
256
+ await toggleTasklistCheckbox(provider, parentId, ticketId, {
257
+ checked: true,
258
+ });
259
+
260
+ // Idempotency check (Story #1817): re-fetch the parent under the lock
261
+ // so a concurrent cascade winner that already flipped this parent to
262
+ // `agent::done` short-circuits us without re-running the transition.
263
+ // The provider cache may still hold a stale row from before the
264
+ // winner's PATCH — invalidate first when supported.
265
+ if (typeof provider.invalidateTicket === 'function') {
266
+ try {
267
+ provider.invalidateTicket(parentId);
268
+ } catch {
269
+ // best-effort cache invalidation
270
+ }
271
+ }
272
+ const parentSnapshot = await provider.getTicket(parentId);
273
+ if (parentSnapshot?.labels?.includes(STATE_LABELS.DONE)) {
274
+ logger.debug(
275
+ `[Ticketing] Cascade to parent #${parentId} skipped: already agent::done (concurrent winner).`,
276
+ );
277
+ return { cascadedTo, failed };
278
+ }
279
+
280
+ // Fetch siblings with fresh reads to defend against stale-CLOSED entries.
281
+ // `{ fresh: true }` threads into the per-child `getTicket` inside
282
+ // `getSubTickets`, bypassing the cache in one pass rather than two.
283
+ // The per-child try/catch fallback-to-null is preserved by `getSubTickets`
284
+ // itself, so a transient read failure cannot silently flip the all-done
285
+ // check. The concurrency cap (SUBTICKET_HYDRATION_CONCURRENCY = 8) is
286
+ // applied inside `getSubTickets`.
287
+ const freshSubTickets = await provider.getSubTickets(parentId, {
288
+ fresh: true,
289
+ });
290
+ const allDone = freshSubTickets.every(
291
+ (st) => st.labels.includes(STATE_LABELS.DONE) || st.state === 'closed',
292
+ );
293
+ if (!allDone) return { cascadedTo, failed };
294
+
295
+ // EXCLUSION: Epics do not auto-close via cascade. Epics close via
296
+ // formal /epic-deliver (its own machinery handles branch merges,
297
+ // PR-driven `Closes #N` auto-close, and a recovery transition in
298
+ // `epic-deliver-finalize.js`).
299
+ //
300
+ // Planning tickets (context::prd, context::tech-spec) DO close via
301
+ // cascade now (Story #1951). Previously they were excluded under
302
+ // the assumption that the operator would close them manually
303
+ // post-merge — but that step never reliably happened and leaving
304
+ // them open as native sub-issues of the Epic blocks GitHub from
305
+ // honoring the Epic's `Closes #N` footer. The Epic finalize phase
306
+ // also closes them explicitly; this cascade branch is the
307
+ // defense-in-depth path when a Story's tasklist references a
308
+ // planning ticket directly.
309
+ //
310
+ // Features auto-close via cascade. A Feature is a purely
311
+ // hierarchical grouping — no standalone branch, no merge step.
312
+ // When its last child Story closes, the Feature is complete by
313
+ // definition. Operators who need Feature-level AC verification
314
+ // should encode it in the final child Story, not rely on a manual
315
+ // close step.
316
+ //
317
+ // Reuse the parentSnapshot from the idempotency check above — it is
318
+ // a fresh read (cache was invalidated before the getTicket call) and
319
+ // the parent's type label is invariant within a single cascade lock
320
+ // hold, so a second getTicket is wasteful and redundant.
321
+ const isEpic = parentSnapshot.labels.includes(TYPE_LABELS.EPIC);
322
+ if (isEpic) {
323
+ logger.warn(
324
+ `[Ticketing] Cascade reached Epic #${parentId}. Skipping auto-close (Epics close via the operator's PR merge).`,
325
+ );
326
+ return { cascadedTo, failed };
327
+ }
328
+
329
+ // Retry the parent transition on transient `gh` failures (rate limit,
330
+ // 5xx, transport timeouts). Permanent failures fall through to the
331
+ // outer catch on the first attempt so the operator sees the real
332
+ // error rather than three retries' worth of noise.
333
+ await retryTransient(
334
+ () =>
335
+ transitionTicketState(provider, parentId, STATE_LABELS.DONE, {
336
+ notify: opts.notify,
337
+ }),
338
+ {
339
+ onRetry: (err, attempt, delayMs) => {
340
+ logger.warn(
341
+ `[Ticketing] Cascade to parent #${parentId} hit transient ${err?.name ?? 'error'} ` +
342
+ `(attempt ${attempt}); retrying in ${delayMs}ms. ${formatCascadeError(err)}`,
343
+ );
344
+ },
345
+ },
346
+ );
347
+ await postStructuredComment(
348
+ provider,
349
+ parentId,
350
+ 'progress',
351
+ 'All child tickets completed via recursive cascade.',
352
+ );
353
+ cascadedTo.push(parentId);
354
+
355
+ const nested = await cascadeCompletion(provider, parentId, {
356
+ notify: opts.notify,
357
+ _logger: logger,
358
+ });
359
+ cascadedTo.push(...nested.cascadedTo);
360
+ failed.push(...nested.failed);
361
+ } catch (err) {
362
+ const detail = formatCascadeError(err);
363
+ failed.push({ parentId, error: detail });
364
+ logger.warn(`[Ticketing] Cascade to parent #${parentId} failed: ${detail}`);
365
+ }
366
+ return { cascadedTo, failed };
367
+ }
368
+
369
+ /**
370
+ * Recursively cascade upward.
371
+ * If ticket reaches DONE, it toggles its checkbox in its parent.
372
+ * Then checks if parent's sub-tickets are ALL DONE.
373
+ * If yes, transitions parent to DONE and cascades up.
374
+ *
375
+ * Parents are partitioned into disjoint groups by shared ancestor
376
+ * ({@link groupByAncestor}). Groups run in parallel via `Promise.all`,
377
+ * but parents **within** a group run strictly sequentially in input
378
+ * order — concurrent transitions against a shared ancestor would race
379
+ * the "all children done?" check. Within each parent, sibling reads
380
+ * fan out via `getSubTickets(parentId, { fresh: true })` with the
381
+ * concurrency cap (8) applied inside `getSubTickets`.
382
+ *
383
+ * Log output is captured per parent into a buffered logger and flushed
384
+ * to the real {@link Logger} after all groups resolve, in the original
385
+ * `parsedParents` order. The visible log stream is therefore
386
+ * byte-identical to a serial baseline; only the I/O between parents in
387
+ * disjoint groups overlaps.
388
+ *
389
+ * Per-parent errors are isolated: a failure updating one parent (network,
390
+ * permission, stale ticket) never discards progress on sibling parents.
391
+ * Failures are collected and returned so callers can log them with full
392
+ * ticket context instead of seeing a single rejection.
393
+ *
394
+ * @param {import('../../ITicketingProvider.js').ITicketingProvider} provider
395
+ * @param {number} ticketId
396
+ * @param {{ notify?: Function, _logger?: object }} [opts] - `notify` is
397
+ * forwarded to any recursive `transitionTicketState` fired on parent
398
+ * tickets. `_logger` is an internal hook used by nested cascade calls
399
+ * to keep buffered output coherent — external callers should leave it
400
+ * unset so the module-level {@link Logger} is used.
401
+ * @returns {Promise<{ cascadedTo: number[], failed: Array<{ parentId: number, error: string }> }>}
402
+ */
403
+ export async function cascadeCompletion(provider, ticketId, opts = {}) {
404
+ const ticket = await provider.getTicket(ticketId);
405
+
406
+ // Determine if this ticket is agent::done
407
+ if (!ticket.labels.includes(STATE_LABELS.DONE)) {
408
+ return { cascadedTo: [], failed: [] };
409
+ }
410
+
411
+ const { blocks: parentIds } = await provider.getTicketDependencies(ticketId);
412
+
413
+ // Fallback: parse `parent: #NNN` from the body when `blocks` syntax isn't used (C-5).
414
+ let parsedParents = parentIds;
415
+ if (!parsedParents || parsedParents.length === 0) {
416
+ const parentMatch = ticket.body
417
+ ? [...ticket.body.matchAll(/parent:\s*#(\d+)/gi)]
418
+ : [];
419
+ parsedParents = parentMatch.map((m) => Number.parseInt(m[1], 10));
420
+ }
421
+
422
+ // Story #2982 — third fallback: GitHub's native Sub-Issues API. The
423
+ // resume reconciler can strip the `parent: #N` orchestrator footer
424
+ // from a Story body (see Issue 2 in #2982); without the body marker
425
+ // the cascade silently returned `{ cascadedTo: [], failed: [] }` and
426
+ // left intermediate Feature tickets stranded OPEN. The native link is
427
+ // independent of body text, so consult it when the first two
428
+ // strategies came back empty.
429
+ if (
430
+ parsedParents.length === 0 &&
431
+ typeof provider._getNativeParent === 'function' &&
432
+ ticket.nodeId
433
+ ) {
434
+ try {
435
+ const nativeParent = await provider._getNativeParent(
436
+ ticket.nodeId,
437
+ ticketId,
438
+ );
439
+ if (typeof nativeParent === 'number') {
440
+ parsedParents = [nativeParent];
441
+ }
442
+ } catch (err) {
443
+ Logger.warn(
444
+ `[cascadeCompletion] native parent lookup failed for #${ticketId}: ${err.message}`,
445
+ );
446
+ }
447
+ }
448
+
449
+ if (parsedParents.length === 0) {
450
+ return { cascadedTo: [], failed: [] };
451
+ }
452
+
453
+ // Partition parents by shared ancestor (disjoint groups run in
454
+ // parallel; within-group parents stay sequential to avoid racing the
455
+ // shared ancestor's "all children done?" check), then dispatch the
456
+ // per-parent work via `dispatchCascadeGroups` so the buffered-flush
457
+ // bookkeeping lives in `cascade-grouping.js`.
458
+ const groups = await groupByAncestor(parsedParents, provider);
459
+ const results = await dispatchCascadeGroups({
460
+ parsedParents,
461
+ groups,
462
+ flushLogger: opts._logger ?? Logger,
463
+ processParent: (parentId, logger) =>
464
+ processCascadeParent(provider, ticketId, parentId, {
465
+ notify: opts.notify,
466
+ _logger: logger,
467
+ }),
468
+ });
469
+
470
+ const cascadedTo = [];
471
+ const failed = [];
472
+ for (const r of results) {
473
+ cascadedTo.push(...r.cascadedTo);
474
+ failed.push(...r.failed);
475
+ }
476
+ return { cascadedTo, failed };
477
+ }
478
+
479
+ /**
480
+ * Derive the parent `agent::*` state from the composition of its children.
481
+ *
482
+ * Rules (Story #2676):
483
+ * - Any child carrying `agent::blocked` → parent should be `agent::blocked`.
484
+ * - Otherwise, every child is `agent::done` (or closed) → parent should be
485
+ * `agent::done`.
486
+ * - Otherwise, any child carrying `agent::executing` or `agent::closing` →
487
+ * parent should be `agent::executing`.
488
+ * - Otherwise (e.g. all children still `agent::ready`) → return `null` to
489
+ * signal "leave the parent unchanged". A parent already partway through
490
+ * the lifecycle MUST NOT be downgraded just because one child reverted.
491
+ *
492
+ * The function is pure and exported so the rule can be exercised in
493
+ * isolation by unit tests without dragging the cascade I/O surface in.
494
+ *
495
+ * @param {Array<{ labels?: string[], state?: string }>} siblings
496
+ * @returns {string|null} A `STATE_LABELS.*` value, or `null` for no-op.
497
+ */
498
+ export function deriveParentState(siblings) {
499
+ if (!Array.isArray(siblings) || siblings.length === 0) return null;
500
+ const labelsOf = (s) => (Array.isArray(s?.labels) ? s.labels : []);
501
+ if (siblings.some((s) => labelsOf(s).includes(STATE_LABELS.BLOCKED))) {
502
+ return STATE_LABELS.BLOCKED;
503
+ }
504
+ const allDone = siblings.every(
505
+ (s) => labelsOf(s).includes(STATE_LABELS.DONE) || s?.state === 'closed',
506
+ );
507
+ if (allDone) return STATE_LABELS.DONE;
508
+ const anyActive = siblings.some(
509
+ (s) =>
510
+ labelsOf(s).includes(STATE_LABELS.EXECUTING) ||
511
+ labelsOf(s).includes(STATE_LABELS.CLOSING),
512
+ );
513
+ if (anyActive) return STATE_LABELS.EXECUTING;
514
+ return null;
515
+ }
516
+
517
+ /**
518
+ * Parent-state cascade for non-terminal transitions. Story #2676.
519
+ *
520
+ * When a child ticket transitions to any `agent::*` state, this function
521
+ * walks the parent chain and updates each parent's state to the value
522
+ * derived by {@link deriveParentState} — so that moving a Task to
523
+ * `agent::executing` propagates "in progress" up to the Story and the
524
+ * Epic on the Project board, and moving a Task to `agent::blocked`
525
+ * surfaces the HITL signal at every ancestor.
526
+ *
527
+ * For `agent::done` transitions, propagation is delegated to the
528
+ * existing {@link cascadeCompletion} so the long-standing semantics
529
+ * (tasklist checkbox toggling, the "All child tickets completed via
530
+ * recursive cascade" progress comment, Epic exclusion) are preserved
531
+ * verbatim.
532
+ *
533
+ * Resilience matches {@link cascadeCompletion}: disjoint parent groups
534
+ * run in parallel via {@link dispatchCascadeGroups}; parents within a
535
+ * group run sequentially; the per-parent lock from
536
+ * {@link withParentCascadeLock} prevents races on shared ancestors; and
537
+ * per-parent errors are isolated so a sibling parent's failure does not
538
+ * discard work on the others.
539
+ *
540
+ * @param {import('../../ITicketingProvider.js').ITicketingProvider} provider
541
+ * @param {number} ticketId
542
+ * @param {{ notify?: Function, _logger?: object }} [opts]
543
+ * @returns {Promise<{ cascadedTo: number[], failed: Array<{ parentId: number, error: string }> }>}
544
+ */
545
+ export async function cascadeParentState(provider, ticketId, opts = {}) {
546
+ // Provider-capability guard. Cascade derivation needs both
547
+ // `getTicketDependencies` (to walk the `blocks:` parent edge) and
548
+ // `getSubTickets` (to inspect the parent's children). Test fakes
549
+ // that stub only the single-ticket surface (e.g. the column-sync
550
+ // sibling tests) MUST still be able to drive `transitionTicketState`
551
+ // without the cascade blowing up. Silently no-op when the surface
552
+ // is missing — propagation is best-effort, matching the contract
553
+ // already documented for the column-sync mirror.
554
+ if (
555
+ typeof provider?.getTicketDependencies !== 'function' ||
556
+ typeof provider?.getSubTickets !== 'function'
557
+ ) {
558
+ return { cascadedTo: [], failed: [] };
559
+ }
560
+ const ticket = await provider.getTicket(ticketId);
561
+ const labels = Array.isArray(ticket?.labels) ? ticket.labels : [];
562
+ const childState = labels.find((l) => ALL_STATES.includes(l));
563
+ if (!childState) return { cascadedTo: [], failed: [] };
564
+
565
+ // DONE-cascade keeps the existing path: tasklist checkbox toggle,
566
+ // progress comment, Epic-close exclusion, and the legacy log shape are
567
+ // all encoded in `cascadeCompletion` and externally observed by tests.
568
+ if (childState === STATE_LABELS.DONE) {
569
+ return cascadeCompletion(provider, ticketId, opts);
570
+ }
571
+
572
+ const parsedParents = await resolveParentIds(provider, ticket, ticketId);
573
+ if (parsedParents.length === 0) return { cascadedTo: [], failed: [] };
574
+
575
+ const groups = await groupByAncestor(parsedParents, provider);
576
+ const results = await dispatchCascadeGroups({
577
+ parsedParents,
578
+ groups,
579
+ flushLogger: opts._logger ?? Logger,
580
+ processParent: (parentId, logger) =>
581
+ processStateCascadeParent(provider, parentId, {
582
+ notify: opts.notify,
583
+ _logger: logger,
584
+ }),
585
+ });
586
+
587
+ const cascadedTo = [];
588
+ const failed = [];
589
+ for (const r of results) {
590
+ cascadedTo.push(...r.cascadedTo);
591
+ failed.push(...r.failed);
592
+ }
593
+ return { cascadedTo, failed };
594
+ }
595
+
596
+ /**
597
+ * Resolve the parent issue ids for a ticket: native `blocks:` dependency
598
+ * annotations first, then `parent: #NNN` body references as a fallback.
599
+ * Mirrors the resolution path used by {@link cascadeCompletion}.
600
+ *
601
+ * @param {import('../../ITicketingProvider.js').ITicketingProvider} provider
602
+ * @param {object} ticket
603
+ * @param {number} ticketId
604
+ * @returns {Promise<number[]>}
605
+ */
606
+ async function resolveParentIds(provider, ticket, ticketId) {
607
+ const { blocks: parentIds } = await provider.getTicketDependencies(ticketId);
608
+ if (Array.isArray(parentIds) && parentIds.length > 0) return parentIds;
609
+ const parentMatch = ticket?.body
610
+ ? [...ticket.body.matchAll(/parent:\s*#(\d+)/gi)]
611
+ : [];
612
+ return parentMatch.map((m) => Number.parseInt(m[1], 10));
613
+ }
614
+
615
+ /**
616
+ * Per-parent worker for {@link cascadeParentState}. Acquires the shared
617
+ * per-parent cascade lock so concurrent transitions on sibling children
618
+ * cannot race on the parent's derived-state check.
619
+ *
620
+ * @returns {Promise<{ cascadedTo: number[], failed: Array<{ parentId: number, error: string }> }>}
621
+ */
622
+ async function processStateCascadeParent(provider, parentId, opts) {
623
+ const logger = opts._logger ?? Logger;
624
+ return withParentCascadeLock(parentId, () =>
625
+ processStateCascadeParentLocked(provider, parentId, opts, logger),
626
+ );
627
+ }
628
+
629
+ /**
630
+ * Body of {@link processStateCascadeParent} under the per-parent lock.
631
+ * Computes the derived state from a fresh sibling read, applies the
632
+ * idempotency guard, and recurses upward.
633
+ */
634
+ async function processStateCascadeParentLocked(
635
+ provider,
636
+ parentId,
637
+ opts,
638
+ logger,
639
+ ) {
640
+ const cascadedTo = [];
641
+ const failed = [];
642
+ try {
643
+ if (typeof provider.invalidateTicket === 'function') {
644
+ try {
645
+ provider.invalidateTicket(parentId);
646
+ } catch {
647
+ // best-effort cache invalidation
648
+ }
649
+ }
650
+ // Fetch siblings fresh in one pass — `{ fresh: true }` threads into the
651
+ // per-child `getTicket` inside `getSubTickets`, replacing the previous
652
+ // two-step pattern of `getSubTickets` + a second `concurrentMap` fan-out
653
+ // with `invalidateTicket` + `getTicket(id, { fresh:true })`.
654
+ const freshSubs = await provider.getSubTickets(parentId, { fresh: true });
655
+
656
+ const derived = deriveParentState(freshSubs);
657
+ if (derived === null) return { cascadedTo, failed };
658
+
659
+ // Defer the all-done case to the legacy DONE-cascade so it
660
+ // owns the Epic exclusion, the tasklist toggle, the progress
661
+ // comment, and the recursive walk that already pin its
662
+ // behaviour via the existing test surface. The closing child's
663
+ // id is taken from `freshSubs` — any DONE child suffices because
664
+ // cascadeCompletion only uses the child to look up its parents.
665
+ if (derived === STATE_LABELS.DONE) {
666
+ const doneChild = freshSubs.find(
667
+ (s) => Array.isArray(s?.labels) && s.labels.includes(STATE_LABELS.DONE),
668
+ );
669
+ if (!doneChild) return { cascadedTo, failed };
670
+ const nested = await cascadeCompletion(provider, doneChild.id, {
671
+ notify: opts.notify,
672
+ _logger: logger,
673
+ });
674
+ cascadedTo.push(...nested.cascadedTo);
675
+ failed.push(...nested.failed);
676
+ return { cascadedTo, failed };
677
+ }
678
+
679
+ const parent = await provider.getTicket(parentId);
680
+ const parentLabels = Array.isArray(parent?.labels) ? parent.labels : [];
681
+ const currentState = parentLabels.find((l) => ALL_STATES.includes(l));
682
+ if (currentState === derived) {
683
+ // Idempotency guard — Project board is already in the derived
684
+ // column. Skip the transition entirely so we do not burn a
685
+ // GraphQL write for a no-op.
686
+ return { cascadedTo, failed };
687
+ }
688
+
689
+ await retryTransient(
690
+ () =>
691
+ transitionTicketState(provider, parentId, derived, {
692
+ notify: opts.notify,
693
+ // Recursion is handled explicitly below — passing cascade:false
694
+ // prevents state.js from firing its own cascadeParentState on the
695
+ // parent and double-walking the tree.
696
+ cascade: false,
697
+ }),
698
+ {
699
+ onRetry: (err, attempt, delayMs) => {
700
+ logger.warn(
701
+ `[Ticketing] State cascade to parent #${parentId} hit transient ${err?.name ?? 'error'} ` +
702
+ `(attempt ${attempt}); retrying in ${delayMs}ms. ${formatCascadeError(err)}`,
703
+ );
704
+ },
705
+ },
706
+ );
707
+ cascadedTo.push(parentId);
708
+
709
+ const nested = await cascadeParentState(provider, parentId, {
710
+ notify: opts.notify,
711
+ _logger: logger,
712
+ });
713
+ cascadedTo.push(...nested.cascadedTo);
714
+ failed.push(...nested.failed);
715
+ } catch (err) {
716
+ const detail = formatCascadeError(err);
717
+ failed.push({ parentId, error: detail });
718
+ logger.warn(
719
+ `[Ticketing] State cascade to parent #${parentId} failed: ${detail}`,
720
+ );
721
+ }
722
+ return { cascadedTo, failed };
723
+ }