mandrel 2.31.0 → 2.33.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 (267) hide show
  1. package/.agents/README.md +13 -17
  2. package/.agents/agents/acceptance-critic.md +1 -2
  3. package/.agents/docs/SDLC.md +12 -9
  4. package/.agents/docs/agentrc-reference.json +63 -58
  5. package/.agents/docs/configuration.md +275 -227
  6. package/.agents/docs/execution-reference.md +13 -14
  7. package/.agents/docs/quality-gates.md +195 -23
  8. package/.agents/instructions.md +2 -5
  9. package/.agents/rules/git-conventions-reference.md +27 -27
  10. package/.agents/rules/git-conventions.md +4 -2
  11. package/.agents/rules/known-tooling-behavior.md +66 -30
  12. package/.agents/rules/testing-standards.md +35 -71
  13. package/.agents/runtime-deps.json +2 -2
  14. package/.agents/schemas/agentrc.schema.json +1955 -1410
  15. package/.agents/schemas/lifecycle/README.md +21 -14
  16. package/.agents/schemas/lifecycle/ledger-record.schema.json +76 -22
  17. package/.agents/schemas/story-deliver-terminal.schema.json +2 -2
  18. package/.agents/scripts/README.md +16 -29
  19. package/.agents/scripts/apply-quality-bootstrap.js +27 -34
  20. package/.agents/scripts/audit-to-stories.js +160 -41
  21. package/.agents/scripts/bootstrap.js +28 -26
  22. package/.agents/scripts/check-baseline-drift.js +73 -13
  23. package/.agents/scripts/check-baseline-scope.js +362 -0
  24. package/.agents/scripts/check-dead-exports.js +9 -1
  25. package/.agents/scripts/check-gherkin-corpus.js +508 -0
  26. package/.agents/scripts/check-knip-entries.js +159 -0
  27. package/.agents/scripts/check-lifecycle-lint.js +108 -124
  28. package/.agents/scripts/check-schema-references.js +1 -1
  29. package/.agents/scripts/diagnose-friction.js +7 -4
  30. package/.agents/scripts/generate-config-docs.js +263 -171
  31. package/.agents/scripts/install-matrix-assert.js +0 -1
  32. package/.agents/scripts/lib/ITicketingProvider.js +0 -58
  33. package/.agents/scripts/lib/audit-baselines/staleness.js +6 -6
  34. package/.agents/scripts/lib/audit-baselines/trend.js +7 -8
  35. package/.agents/scripts/lib/audit-baselines/weights.js +4 -5
  36. package/.agents/scripts/lib/audit-suite/checklist-threading.js +1 -1
  37. package/.agents/scripts/lib/audit-to-stories/build-story-body.js +81 -35
  38. package/.agents/scripts/lib/audit-to-stories/wire-dependencies.js +185 -0
  39. package/.agents/scripts/lib/baselines/envelope.js +41 -60
  40. package/.agents/scripts/lib/baselines/git-base.js +30 -37
  41. package/.agents/scripts/lib/baselines/kinds/_crap-new-method-gate.js +103 -0
  42. package/.agents/scripts/lib/baselines/kinds/_crap-read.js +150 -0
  43. package/.agents/scripts/lib/baselines/kinds/crap.js +25 -65
  44. package/.agents/scripts/lib/baselines/orphan-pruner.js +233 -0
  45. package/.agents/scripts/lib/baselines/refresh-service.js +6 -8
  46. package/.agents/scripts/lib/baselines/scope-assert.js +223 -0
  47. package/.agents/scripts/lib/baselines/scope-inventory.js +314 -0
  48. package/.agents/scripts/lib/bdd-step-index.js +326 -0
  49. package/.agents/scripts/lib/bootstrap/install-ledger.js +5 -3
  50. package/.agents/scripts/lib/bootstrap/issue-forms-template.js +4 -6
  51. package/.agents/scripts/lib/bootstrap/manifest.js +17 -40
  52. package/.agents/scripts/lib/bootstrap/project-bootstrap.js +12 -59
  53. package/.agents/scripts/lib/bootstrap/quality-bootstrap.js +62 -2
  54. package/.agents/scripts/lib/checks/loop-health.js +9 -37
  55. package/.agents/scripts/lib/child-exec.js +193 -0
  56. package/.agents/scripts/lib/cli/standard-args.js +1 -1
  57. package/.agents/scripts/lib/cli-args.js +64 -0
  58. package/.agents/scripts/lib/close-validation/gates.js +2 -2
  59. package/.agents/scripts/lib/close-validation/runner.js +3 -3
  60. package/.agents/scripts/lib/config/acceptance-eval.js +5 -52
  61. package/.agents/scripts/lib/config/commands.js +3 -5
  62. package/.agents/scripts/lib/config/explain.js +5 -7
  63. package/.agents/scripts/lib/config/gates/bundle-size.schema.js +32 -6
  64. package/.agents/scripts/lib/config/gates/coverage.schema.js +25 -5
  65. package/.agents/scripts/lib/config/gates/crap-incremental-coverage.schema.js +12 -2
  66. package/.agents/scripts/lib/config/gates/crap.schema.js +68 -23
  67. package/.agents/scripts/lib/config/gates/duplication.schema.js +29 -17
  68. package/.agents/scripts/lib/config/gates/index.js +5 -2
  69. package/.agents/scripts/lib/config/gates/lighthouse.schema.js +34 -6
  70. package/.agents/scripts/lib/config/gates/lint.schema.js +11 -2
  71. package/.agents/scripts/lib/config/gates/maintainability.schema.js +37 -15
  72. package/.agents/scripts/lib/config/gates/mutation.schema.js +15 -3
  73. package/.agents/scripts/lib/config/gates/shared.js +58 -9
  74. package/.agents/scripts/lib/config/github.js +0 -1
  75. package/.agents/scripts/lib/config/limits.js +3 -48
  76. package/.agents/scripts/lib/config/qa.js +105 -0
  77. package/.agents/scripts/lib/config/runners.js +38 -16
  78. package/.agents/scripts/lib/config/temp-paths.js +6 -5
  79. package/.agents/scripts/lib/config-settings-schema-delivery.js +245 -56
  80. package/.agents/scripts/lib/config-settings-schema-quality.js +209 -29
  81. package/.agents/scripts/lib/config-settings-schema.js +386 -39
  82. package/.agents/scripts/lib/crap-baseline-join.js +126 -9
  83. package/.agents/scripts/lib/crap-utils.js +84 -520
  84. package/.agents/scripts/lib/dead-exports-knip.js +79 -10
  85. package/.agents/scripts/lib/degraded-mode.js +2 -2
  86. package/.agents/scripts/lib/dependency-parser.js +20 -7
  87. package/.agents/scripts/lib/doc-tiers.js +3 -3
  88. package/.agents/scripts/lib/feedback-loop/graduator-core.js +46 -104
  89. package/.agents/scripts/lib/feedback-loop/retro-proposals-graduator.js +10 -8
  90. package/.agents/scripts/lib/findings/provenance-field.js +135 -0
  91. package/.agents/scripts/lib/findings/route-finding.js +57 -8
  92. package/.agents/scripts/lib/fs-walk.js +52 -0
  93. package/.agents/scripts/lib/git-branch-lifecycle.js +2 -2
  94. package/.agents/scripts/lib/git-utils.js +16 -36
  95. package/.agents/scripts/lib/knip-config-resolver.js +181 -0
  96. package/.agents/scripts/lib/knip-entry-sync.js +508 -0
  97. package/.agents/scripts/lib/observability/metrics-ledger.js +1 -1
  98. package/.agents/scripts/lib/observability/runtime-friction.js +10 -0
  99. package/.agents/scripts/lib/observability/signal-validator.js +5 -85
  100. package/.agents/scripts/lib/observability/signals-writer.js +19 -62
  101. package/.agents/scripts/lib/observability/source-classifier.js +5 -7
  102. package/.agents/scripts/lib/observability/terse-result.js +3 -3
  103. package/.agents/scripts/lib/orchestration/behind-recovery.js +114 -0
  104. package/.agents/scripts/lib/orchestration/ceremony-routing.js +7 -8
  105. package/.agents/scripts/lib/orchestration/ci-rerun-guard.js +34 -33
  106. package/.agents/scripts/lib/orchestration/code-review.js +2 -2
  107. package/.agents/scripts/lib/orchestration/complexity-gate.js +43 -161
  108. package/.agents/scripts/lib/orchestration/diff-magnitude.js +4 -4
  109. package/.agents/scripts/lib/orchestration/label-transitions.js +3 -2
  110. package/.agents/scripts/lib/orchestration/lease-guard-shared.js +12 -38
  111. package/.agents/scripts/lib/orchestration/lifecycle/emit-merge-unlanded.js +5 -6
  112. package/.agents/scripts/lib/orchestration/plan-metrics.js +2 -3
  113. package/.agents/scripts/lib/orchestration/plan-persist/persist-helpers.js +7 -26
  114. package/.agents/scripts/lib/orchestration/plan-persist/run-plan-persist.js +69 -5
  115. package/.agents/scripts/lib/orchestration/plan-persist/story-ops.js +69 -13
  116. package/.agents/scripts/lib/orchestration/plan-persist/summary.js +49 -0
  117. package/.agents/scripts/lib/orchestration/{lifecycle/listeners/watcher.js → pr-watch.js} +58 -208
  118. package/.agents/scripts/lib/orchestration/resolve-stories.js +77 -50
  119. package/.agents/scripts/lib/orchestration/review-providers/codex.js +1 -1
  120. package/.agents/scripts/lib/orchestration/review-providers/mi-exemptions.js +130 -0
  121. package/.agents/scripts/lib/orchestration/review-providers/native.js +30 -16
  122. package/.agents/scripts/lib/orchestration/single-story-close/phases/code-review.js +1 -1
  123. package/.agents/scripts/lib/orchestration/single-story-close/phases/confirm-merge.js +37 -26
  124. package/.agents/scripts/lib/orchestration/single-story-close/phases/conventional-subject.js +376 -0
  125. package/.agents/scripts/lib/orchestration/single-story-close/phases/normalize-pr-title.js +161 -151
  126. package/.agents/scripts/lib/orchestration/single-story-close/phases/options.js +15 -3
  127. package/.agents/scripts/lib/orchestration/single-story-close/phases/pull-request.js +10 -15
  128. package/.agents/scripts/lib/orchestration/single-story-close/phases/review-block.js +5 -0
  129. package/.agents/scripts/lib/orchestration/single-story-close/phases/review-override.js +157 -0
  130. package/.agents/scripts/lib/orchestration/single-story-close/phases/worktree-reap.js +0 -14
  131. package/.agents/scripts/lib/orchestration/single-story-close/runner.js +59 -25
  132. package/.agents/scripts/lib/orchestration/single-story-lease-guard.js +20 -31
  133. package/.agents/scripts/lib/orchestration/spec-spill.js +17 -3
  134. package/.agents/scripts/lib/orchestration/story-close/phases/review-core.js +7 -6
  135. package/.agents/scripts/lib/orchestration/story-deliver-terminal.js +2 -1
  136. package/.agents/scripts/lib/orchestration/task-body-validator.js +4 -1
  137. package/.agents/scripts/lib/orchestration/ticket-lease.js +28 -127
  138. package/.agents/scripts/lib/orchestration/ticket-validator-conflicts.js +116 -1
  139. package/.agents/scripts/lib/orchestration/ticket-validator-sizing.js +1 -1
  140. package/.agents/scripts/lib/orchestration/ticket-validator.js +38 -0
  141. package/.agents/scripts/lib/orchestration/ticketing/reads.js +5 -5
  142. package/.agents/scripts/lib/orchestration/ticketing/transition.js +5 -4
  143. package/.agents/scripts/lib/orchestration/verify-tier-repair.js +107 -0
  144. package/.agents/scripts/lib/qa/coverage-verdict.js +5 -87
  145. package/.agents/scripts/lib/signals/detectors/common.js +1 -1
  146. package/.agents/scripts/lib/signals/index.js +8 -6
  147. package/.agents/scripts/lib/signals/schema.js +20 -25
  148. package/.agents/scripts/lib/signals/write.js +8 -8
  149. package/.agents/scripts/lib/story-body/footer-block.js +97 -0
  150. package/.agents/scripts/lib/story-body/story-body.js +18 -81
  151. package/.agents/scripts/lib/temp-retention.js +1 -1
  152. package/.agents/scripts/lib/templates/decomposer-prompts.js +16 -14
  153. package/.agents/scripts/lib/ticket-body-sections.js +4 -5
  154. package/.agents/scripts/lib/wave-runner/footprint.js +306 -0
  155. package/.agents/scripts/lib/wave-runner/ready-set.js +198 -181
  156. package/.agents/scripts/lib/worktree/lifecycle/merge-reachability.js +13 -45
  157. package/.agents/scripts/lib/worktree/lifecycle/reap.js +4 -5
  158. package/.agents/scripts/lib/worktree-manager.js +2 -3
  159. package/.agents/scripts/lint-label-vocabulary.js +2 -24
  160. package/.agents/scripts/pr-watch-with-update.js +7 -5
  161. package/.agents/scripts/providers/github/blocked-by-add.js +25 -10
  162. package/.agents/scripts/providers/github/cache.js +2 -2
  163. package/.agents/scripts/providers/github/comments.js +6 -28
  164. package/.agents/scripts/providers/github/compose.js +0 -15
  165. package/.agents/scripts/providers/github/errors.js +10 -27
  166. package/.agents/scripts/providers/github/request-helpers.js +1 -2
  167. package/.agents/scripts/providers/github/sub-issues.js +10 -218
  168. package/.agents/scripts/providers/github.js +4 -7
  169. package/.agents/scripts/prune-baseline-orphans.js +181 -0
  170. package/.agents/scripts/resolve-stories.js +21 -7
  171. package/.agents/scripts/run-lint.js +61 -61
  172. package/.agents/scripts/run-test-profile.js +6 -6
  173. package/.agents/scripts/run-verify.js +48 -30
  174. package/.agents/scripts/single-story-close.js +20 -0
  175. package/.agents/scripts/single-story-init.js +12 -35
  176. package/.agents/scripts/stories-wave-tick.js +192 -9
  177. package/.agents/scripts/update-dead-exports-baseline.js +321 -0
  178. package/.agents/skills/core/gates-and-baselines/SKILL.md +2 -2
  179. package/.agents/skills/skills.index.json +1 -11
  180. package/.agents/workflows/audit-documentation.md +5 -6
  181. package/.agents/workflows/audit-to-stories.md +28 -2
  182. package/.agents/workflows/helpers/audit-lens-core.md +11 -12
  183. package/.agents/workflows/helpers/code-quality-guardrails.md +15 -14
  184. package/.agents/workflows/helpers/code-review.md +3 -8
  185. package/.agents/workflows/helpers/deliver-reference.md +30 -2
  186. package/.agents/workflows/helpers/deliver-story-reference.md +84 -16
  187. package/.agents/workflows/helpers/plan-reference.md +76 -0
  188. package/.agents/workflows/helpers/worktree-lifecycle.md +1 -2
  189. package/.agents/workflows/mandrel-update.md +10 -10
  190. package/.agents/workflows/qa-assist.md +15 -20
  191. package/.agents/workflows/qa-explore.md +9 -8
  192. package/README.md +1 -1
  193. package/docs/CHANGELOG.md +58 -0
  194. package/lib/migrations/index.js +2 -0
  195. package/lib/migrations/steps/2.32.0-retire-lint-baseline-command.js +127 -0
  196. package/package.json +14 -5
  197. package/.agents/schemas/lifecycle/checkpoint.written.schema.json +0 -13
  198. package/.agents/schemas/lifecycle/close-validate.end.schema.json +0 -18
  199. package/.agents/schemas/lifecycle/close-validate.start.schema.json +0 -13
  200. package/.agents/schemas/lifecycle/code-review.end.schema.json +0 -30
  201. package/.agents/schemas/lifecycle/code-review.start.schema.json +0 -12
  202. package/.agents/schemas/lifecycle/intervention.recorded.schema.json +0 -15
  203. package/.agents/schemas/lifecycle/loop.tick.schema.json +0 -20
  204. package/.agents/schemas/lifecycle/notification.emitted.schema.json +0 -18
  205. package/.agents/schemas/lifecycle/pr.created.schema.json +0 -14
  206. package/.agents/schemas/lifecycle/retro.end.schema.json +0 -16
  207. package/.agents/schemas/lifecycle/retro.start.schema.json +0 -12
  208. package/.agents/schemas/lifecycle/story.blocked.schema.json +0 -13
  209. package/.agents/schemas/lifecycle/story.dispatch.end.schema.json +0 -17
  210. package/.agents/schemas/lifecycle/story.dispatch.start.schema.json +0 -15
  211. package/.agents/schemas/lifecycle/story.merged.schema.json +0 -13
  212. package/.agents/scripts/check-gherkin-placeholders.js +0 -663
  213. package/.agents/scripts/check-lifecycle-doc-drift.js +0 -411
  214. package/.agents/scripts/lib/audit-suite/cli.js +0 -64
  215. package/.agents/scripts/lib/bootstrap/baselines-layout-migration.js +0 -202
  216. package/.agents/scripts/lib/bootstrap/ci-workflow-template.js +0 -212
  217. package/.agents/scripts/lib/checks/baseline-drift-main-checkout.js +0 -104
  218. package/.agents/scripts/lib/checks/push-hook-parity.js +0 -106
  219. package/.agents/scripts/lib/checks/windows-coverage-noise-floor.js +0 -92
  220. package/.agents/scripts/lib/checks/worktree-bootstrap-env.js +0 -81
  221. package/.agents/scripts/lib/checks/worktree-residue-biome.js +0 -55
  222. package/.agents/scripts/lib/crap-baseline-index.js +0 -46
  223. package/.agents/scripts/lib/crap-utils-incremental.js +0 -113
  224. package/.agents/scripts/lib/dynamic-workflow/capability.js +0 -396
  225. package/.agents/scripts/lib/feedback-loop/audit-results-graduator.js +0 -335
  226. package/.agents/scripts/lib/mutation/baseline-snapshot.js +0 -239
  227. package/.agents/scripts/lib/mutation/config-detector.js +0 -119
  228. package/.agents/scripts/lib/mutation/stryker-runner.js +0 -306
  229. package/.agents/scripts/lib/mutation/survivor-report.js +0 -160
  230. package/.agents/scripts/lib/observability/active-story-env.js +0 -170
  231. package/.agents/scripts/lib/observability/tool-trace-hook.js +0 -456
  232. package/.agents/scripts/lib/orchestration/acceptance-clusters.js +0 -111
  233. package/.agents/scripts/lib/orchestration/context-envelope.js +0 -277
  234. package/.agents/scripts/lib/orchestration/detectors-phase.js +0 -194
  235. package/.agents/scripts/lib/orchestration/lifecycle/bus.js +0 -309
  236. package/.agents/scripts/lib/orchestration/lifecycle/emit-loop-tick.js +0 -181
  237. package/.agents/scripts/lib/orchestration/lifecycle/ledger-writer.js +0 -229
  238. package/.agents/scripts/lib/orchestration/lifecycle/listeners/README.md +0 -54
  239. package/.agents/scripts/lib/orchestration/lifecycle/trace-logger.js +0 -344
  240. package/.agents/scripts/lib/orchestration/lint-baseline-service.js +0 -114
  241. package/.agents/scripts/lib/orchestration/pr-base-guard.js +0 -37
  242. package/.agents/scripts/lib/orchestration/resolves-token.js +0 -127
  243. package/.agents/scripts/lib/orchestration/spec-section-validator.js +0 -130
  244. package/.agents/scripts/lib/orchestration/story-close/emit-blocked.js +0 -55
  245. package/.agents/scripts/lib/orchestration/story-close/phases/code-review.js +0 -211
  246. package/.agents/scripts/lib/planning-corpus.js +0 -37
  247. package/.agents/scripts/lib/qa/coverage-report.js +0 -181
  248. package/.agents/scripts/lib/qa/propose-missing-test.js +0 -95
  249. package/.agents/scripts/lib/qa/qa-context-hydrator.js +0 -217
  250. package/.agents/scripts/lib/signals/detectors/index.js +0 -14
  251. package/.agents/scripts/lib/signals/detectors/retry.js +0 -253
  252. package/.agents/scripts/lib/signals/detectors/rework.js +0 -167
  253. package/.agents/scripts/lib/signals/read.js +0 -268
  254. package/.agents/scripts/lib/signals/span-tree.js +0 -291
  255. package/.agents/scripts/lib/story-lifecycle.js +0 -194
  256. package/.agents/scripts/lib/story-plan.js +0 -379
  257. package/.agents/scripts/lib/util/phase-timer-state.js +0 -72
  258. package/.agents/scripts/lib/util/phase-timer.js +0 -163
  259. package/.agents/scripts/lib/workers/combined-mi-crap-worker.js +0 -169
  260. package/.agents/scripts/lint-baseline.js +0 -507
  261. package/.agents/scripts/providers/github/prs.js +0 -103
  262. package/.agents/scripts/signals-view.js +0 -309
  263. package/.agents/scripts/story-plan.js +0 -370
  264. package/.agents/scripts/sync-branch-from-base.js +0 -149
  265. package/.agents/scripts/validate-docs-freshness.js +0 -314
  266. package/.agents/skills/core/diagnose-friction/SKILL.md +0 -78
  267. package/.agents/workflows/helpers/signals.md +0 -112
@@ -22,7 +22,7 @@
22
22
 
23
23
  import { Logger } from '../../lib/Logger.js';
24
24
  import { concurrentMap } from '../../lib/util/concurrent-map.js';
25
- import { parseApiJson } from './request-helpers.js';
25
+ import { paginateRest } from './request-helpers.js';
26
26
 
27
27
  /**
28
28
  * Bounded concurrency for the GitHub dependency-edge round-trips. Kept modest
@@ -32,22 +32,37 @@ import { parseApiJson } from './request-helpers.js';
32
32
  const EDGE_CONCURRENCY = 5;
33
33
 
34
34
  /**
35
- * Fetch the existing blocked-by issue numbers for a given issue.
35
+ * Fetch the existing blocked-by database ids for a given issue, **paginated
36
+ * to exhaustion**.
37
+ *
38
+ * This read is the idempotency check: an edge it fails to see is re-POSTed.
39
+ * Reading only the first page therefore made the writer non-idempotent past
40
+ * the page boundary — every edge beyond it looked missing on every run
41
+ * (Story #5046). `paginateRest` walks the pages and carries the shared
42
+ * transient-retry and page-cap guards.
36
43
  *
37
44
  * Returns `[]` on any error so the caller falls back to posting the full
38
45
  * set of missing edges (worst case: a duplicate POST, which GitHub
39
- * handles idempotently).
46
+ * handles idempotently). That non-fatal contract is deliberate and is the
47
+ * inverse of the READ path in `lib/orchestration/resolve-stories.js`: a lost
48
+ * write-side edge is cosmetic, a lost read-side edge removes a dispatch gate.
40
49
  *
41
- * @param {{ gh: object, owner: string, repo: string, issueNumber: number }} opts
50
+ * @param {{ gh: object, owner: string, repo: string, issueNumber: number, paginate?: Function }} opts
42
51
  * @returns {Promise<number[]>} Database ids of the issues that currently block `issueNumber`.
43
52
  */
44
- async function fetchExistingBlockedBy({ gh, owner, repo, issueNumber }) {
53
+ async function fetchExistingBlockedBy({
54
+ gh,
55
+ owner,
56
+ repo,
57
+ issueNumber,
58
+ paginate = paginateRest,
59
+ }) {
45
60
  try {
46
- const result = await gh.api({
47
- method: 'GET',
48
- endpoint: `/repos/${owner}/${repo}/issues/${issueNumber}/dependencies/blocked_by`,
49
- });
50
- const data = parseApiJson(result);
61
+ const data = await paginate(
62
+ gh,
63
+ `/repos/${owner}/${repo}/issues/${issueNumber}/dependencies/blocked_by`,
64
+ { label: `[blocked-by-add] blocked_by #${issueNumber}` },
65
+ );
51
66
  if (!Array.isArray(data)) return [];
52
67
  return data.map((item) => item?.id).filter((id) => typeof id === 'number');
53
68
  } catch (err) {
@@ -5,8 +5,8 @@
5
5
  * single `GitHubProvider`, shared by dispatcher, reconciler, and cascade.
6
6
  * The outer TTL wrapper is deliberately omitted because `peekFresh` already
7
7
  * bounds entries by a caller-supplied `maxAgeMs`, and every other reader
8
- * trusts the ticket mutators (`updateTicket` / `postComment` /
9
- * `addSubIssue` / `removeSubIssue`) to call `invalidate` explicitly.
8
+ * trusts the ticket mutators (`updateTicket` / `postComment`) to call
9
+ * `invalidate` explicitly.
10
10
  *
11
11
  * Surface is intentionally narrow: only the methods the provider itself
12
12
  * reaches for live here (`has` / `peek` / `peekFresh` / `set` /
@@ -7,20 +7,16 @@
7
7
  * the upstream `upsertStructuredComment` ticketing helper before the body
8
8
  * lands here.
9
9
  *
10
- * Extracted from `../github.js` in Story #2462 / Task #2480. Public
11
- * surface on `GitHubProvider` is unchanged — `postComment`,
12
- * `getRecentComments`, `getTicketComments`, and `deleteComment` all
13
- * delegate here.
10
+ * Extracted from `../github.js` in Story #2462 / Task #2480. `postComment`,
11
+ * `getTicketComments`, and `deleteComment` on `GitHubProvider` all delegate
12
+ * here. Story #5008 dropped the repo-wide `getRecentComments` feed — the
13
+ * telemetry dashboard that read it was retired and nothing else polls a
14
+ * cross-issue comment stream.
14
15
  *
15
16
  * @see Story #2462 — Split GitHubProvider god class into seven composed gateways.
16
17
  */
17
18
 
18
- import { withTransientRetry } from './errors.js';
19
- import {
20
- defaultRetryWarn,
21
- paginateRest,
22
- parseApiJson,
23
- } from './request-helpers.js';
19
+ import { paginateRest, parseApiJson } from './request-helpers.js';
24
20
 
25
21
  // Structured-comment badge — preserved verbatim from the legacy
26
22
  // `./github/comments.js`. The upstream `upsertStructuredComment` ticketing
@@ -51,24 +47,6 @@ export class CommentGateway {
51
47
  this._hooks = hooks;
52
48
  }
53
49
 
54
- /**
55
- * Recent comments across all issues in the repo (sorted newest first).
56
- *
57
- * @field-manifest /repos/{owner}/{repo}/issues/comments?sort=created:
58
- * id, body, created_at, user, issue_url
59
- */
60
- async getRecentComments(limit = 100) {
61
- const result = await withTransientRetry(
62
- () =>
63
- this._gh.api({
64
- method: 'GET',
65
- endpoint: `/repos/${this.owner}/${this.repo}/issues/comments?sort=created&direction=desc&per_page=${limit}`,
66
- }),
67
- { label: 'getRecentComments', onRetry: defaultRetryWarn },
68
- );
69
- return parseApiJson(result) ?? [];
70
- }
71
-
72
50
  /**
73
51
  * All comments on a single ticket. Used by the upstream
74
52
  * `findStructuredComment` ticketing helper, which greps each comment
@@ -23,7 +23,6 @@ import { LabelGateway } from './labels.js';
23
23
  import { MergeMethodsGateway } from './merge-methods.js';
24
24
  import { ProjectBoardGateway } from './project-board.js';
25
25
  import * as projects from './projects-v2-graphql.js';
26
- import { PullRequestGateway } from './prs.js';
27
26
  import { SubIssueGateway } from './sub-issues.js';
28
27
  import { TicketGateway } from './tickets.js';
29
28
 
@@ -50,12 +49,6 @@ export function composeGateways(provider) {
50
49
  ghGraphql: (q, v, o) => p.graphql(q, v, o),
51
50
  cache: p._cache,
52
51
  classifyGithubError,
53
- hooks: {
54
- getTicket: (id, o) => p.getTicket(id, o),
55
- getTickets: (id) => p.getTickets(id),
56
- primeTicketCache: (t) => p.primeTicketCache(t),
57
- invalidateTicket: (id) => p.invalidateTicket(id),
58
- },
59
52
  });
60
53
  p.comments = new CommentGateway({
61
54
  ...ghDeps,
@@ -64,14 +57,6 @@ export function composeGateways(provider) {
64
57
  p.labels = new LabelGateway(ghDeps);
65
58
  p.branchProtection = new BranchProtectionGateway(ghDeps);
66
59
  p.mergeMethods = new MergeMethodsGateway(ghDeps);
67
- p.pullRequests = new PullRequestGateway({
68
- gh: p._gh,
69
- hooks: {
70
- getTicket: (id) => p.getTicket(id),
71
- addItemToProject,
72
- getProjectNumber,
73
- },
74
- });
75
60
  p.issues = new IssuesGateway({
76
61
  ...ghDeps,
77
62
  hooks: {
@@ -1,16 +1,16 @@
1
1
  /**
2
- * GitHub Provider — error classifier + sub-issues GraphQL shapes.
2
+ * GitHub Provider — error classifier + sub-issues GraphQL shape.
3
3
  *
4
4
  * `classifyGithubError` buckets `gh-exec`-thrown errors into 4 categories
5
5
  * (`feature-disabled` / `permission` / `transient` / `permanent`) so the
6
- * sub-issues fallback and the addSubIssue retry loop have a deterministic
6
+ * sub-issues fallback and the shared retry loop have a deterministic
7
7
  * switch. Rate-limit detection wins over the 401/403 → permission rule
8
8
  * because GitHub's secondary rate limit is delivered as HTTP 403 with a
9
9
  * known message; if we bucketed it as 'permission' it would never be
10
10
  * retried.
11
11
  *
12
- * `SUB_ISSUES_QUERY` / `ADD_SUB_ISSUE_MUTATION` / `REMOVE_SUB_ISSUE_MUTATION`
13
- * are the three GraphQL shapes the sub-issues feature reads/writes.
12
+ * `SUB_ISSUES_QUERY` is the GraphQL shape the sub-issues read path uses.
13
+ * Story #5008 removed the add/remove mutations with the write surface.
14
14
  *
15
15
  * Extracted from `../github.js` in Story #1846 / Task #1857.
16
16
  */
@@ -153,11 +153,11 @@ export function classifyGithubError(err) {
153
153
  // transient network/connectivity errors — so every former consumer of either
154
154
  // module keeps (or gains) its prior retry coverage with no shim.
155
155
  //
156
- // Mirrors the addSubIssue retry contract in `sub-issues.js` so read-path
157
- // callers (paginateRest, getTicket, getNativeSubIssues, …) absorb the same
158
- // jittered exponential backoff on transient GitHub errors instead of
159
- // bubbling a one-shot 502/429/ECONNRESET that kills a longer pipeline
160
- // (e.g. the /deliver Phase E retro). The network consumers repointed here
156
+ // Read-path callers (paginateRest, getTicket, getNativeSubIssues, …) all
157
+ // absorb the same jittered exponential backoff on transient GitHub errors
158
+ // through this one primitive, instead of bubbling a one-shot
159
+ // 502/429/ECONNRESET that kills a longer pipeline (e.g. the /deliver
160
+ // Phase E retro). The network consumers repointed here
161
161
  // (branch-protection, labels, projects-v2-graphql) call with no opts, so
162
162
  // they adopt these defaults; their retry *classes* (the network blips) are
163
163
  // preserved via the unified classifier above.
@@ -174,8 +174,7 @@ export const TRANSIENT_RETRY_DEFAULTS = Object.freeze({
174
174
  * `classify` (defaults to `classifyGithubError`) decides whether each
175
175
  * failure is retry-eligible: only `'transient'` retries; every other
176
176
  * bucket (`feature-disabled` / `permission` / `permanent`) bubbles on the
177
- * first failure. The retry shape matches `addSubIssue` in
178
- * `sub-issues.js:119-167`.
177
+ * first failure.
179
178
  *
180
179
  * `sleep` and `random` are injectable so tests can drive deterministic
181
180
  * retry paths without real-world timing.
@@ -256,19 +255,3 @@ export const SUB_ISSUES_QUERY = `query($id: ID!, $cursor: String) {
256
255
  }
257
256
  }
258
257
  }`;
259
-
260
- export const ADD_SUB_ISSUE_MUTATION = `
261
- mutation($parentId: ID!, $subIssueId: ID!, $replaceParent: Boolean) {
262
- addSubIssue(input: { issueId: $parentId, subIssueId: $subIssueId, replaceParent: $replaceParent }) {
263
- issue { number }
264
- subIssue { number }
265
- }
266
- }`;
267
-
268
- export const REMOVE_SUB_ISSUE_MUTATION = `
269
- mutation($parentId: ID!, $subIssueId: ID!) {
270
- removeSubIssue(input: { issueId: $parentId, subIssueId: $subIssueId }) {
271
- issue { number }
272
- subIssue { number }
273
- }
274
- }`;
@@ -9,8 +9,7 @@
9
9
  * 1. **Transient retry** — each underlying `gh.api({...})` call inside
10
10
  * `paginateRest` is wrapped in `withTransientRetry` (from `./errors.js`),
11
11
  * so a single 502 / 429 / ECONNRESET on page N of M does not lose
12
- * pages 1..N-1 of work. The retry shape matches the existing
13
- * `addSubIssue` mutation retry contract in `sub-issues.js`.
12
+ * pages 1..N-1 of work.
14
13
  * 2. **Page cap** — `paginateRest` enforces a hard ceiling (default
15
14
  * `DEFAULT_PAGE_CAP = 50` → 5000 items at `per_page=100`). Exceeding
16
15
  * the cap throws a clear error naming the endpoint, cap, and items
@@ -1,21 +1,19 @@
1
1
  /**
2
2
  * GitHub Provider — SubIssueGateway.
3
3
  *
4
- * Owns the native GitHub Sub-Issues link surface (the GraphQL `subIssues`
5
- * field on an Issue, plus the add/remove mutations) along with the
6
- * `reconcileSubIssueLinks` walker that backfills missing parent/child
7
- * links across an Epic's child set.
4
+ * Owns the **read** side of the native GitHub Sub-Issues surface: the
5
+ * GraphQL `subIssues` field on an Issue, paginated into a child-number
6
+ * list. Story #5008 removed the write side (`addSubIssue`,
7
+ * `removeSubIssue`) and the `reconcileSubIssueLinks` walker along with the
8
+ * rest of the Epic-hierarchy provider surface — v2 delivery is Story-only,
9
+ * so nothing establishes or repairs parent/child links any more.
8
10
  *
9
- * Extracted from `../github.js` in Story #2462 / Task #2480. Public
10
- * surface on `GitHubProvider` is unchanged — `addSubIssue`,
11
- * `removeSubIssue`, and `reconcileSubIssueLinks` all delegate here.
11
+ * Extracted from `../github.js` in Story #2462 / Task #2480. The read path
12
+ * still reaches `GitHubProvider` as `_getNativeSubIssues`, and
13
+ * `IssuesGateway` calls it directly through its `getNativeSubIssues` hook.
12
14
  *
13
- * Constructed with `{ ghGraphql, hooks, classifyGithubError, cache, queries }`:
15
+ * Constructed with `{ ghGraphql, cache, classifyGithubError }`:
14
16
  * - `ghGraphql(query, variables, opts)` — bound `_ghGraphql` from the parent.
15
- * - `hooks.getTicket(id, opts)` — bound to the TicketGateway.
16
- * - `hooks.getTickets(parentId)` — bound to the TicketGateway (for the reconciler).
17
- * - `hooks.primeTicketCache(tickets)` — bound to the TicketGateway (reconciler priming).
18
- * - `hooks.invalidateTicket(id)` — bound to the TicketGateway (post-mutation invalidate).
19
17
  * - `cache.primeIfAbsent(ticket)` — bound to the shared cache (native-walk priming).
20
18
  *
21
19
  * The gateway holds **no** transport state of its own — every call goes
@@ -26,25 +24,14 @@
26
24
  */
27
25
 
28
26
  import { Logger } from '../../lib/Logger.js';
29
- import { concurrentMap } from '../../lib/util/concurrent-map.js';
30
27
  import {
31
- ADD_SUB_ISSUE_MUTATION,
32
28
  classifyGithubError as defaultClassifyGithubError,
33
- REMOVE_SUB_ISSUE_MUTATION,
34
29
  SUB_ISSUES_QUERY,
35
30
  withTransientRetry,
36
31
  } from './errors.js';
37
32
  import { subIssueNodeToTicket } from './mappers.js';
38
33
  import { defaultRetryWarn } from './request-helpers.js';
39
34
 
40
- // Concurrency + retry budgets — preserved from `../github.js` so dispatch
41
- // fan-out and sub-issue reconciliation keep their established shapes.
42
- const SUB_ISSUE_RECONCILE_CONCURRENCY = 4;
43
- const SUB_ISSUE_RETRY_MAX_ATTEMPTS = 6;
44
- const SUB_ISSUE_RETRY_BASE_DELAY_MS = 1000;
45
- const SUB_ISSUE_RETRY_MAX_DELAY_MS = 30000;
46
- const SUB_ISSUE_RETRY_JITTER_MS = 500;
47
-
48
35
  // Story #2852: cap the native sub-issue cursor walk so a runaway pagination
49
36
  // (e.g. an upstream API regression that never sets `hasNextPage = false`)
50
37
  // fails fast instead of looping forever. 50 cursor pages × 100 nodes per
@@ -55,24 +42,16 @@ export class SubIssueGateway {
55
42
  /**
56
43
  * @param {{
57
44
  * ghGraphql: (query: string, variables?: object, opts?: object) => Promise<object>,
58
- * hooks?: {
59
- * getTicket?: (id: number, opts?: object) => Promise<object>,
60
- * getTickets?: (parentId: number) => Promise<object[]>,
61
- * primeTicketCache?: (tickets: object[]) => void,
62
- * invalidateTicket?: (id: number) => void,
63
- * },
64
45
  * cache?: { primeIfAbsent: (ticket: object) => void },
65
46
  * classifyGithubError?: (err: unknown) => string,
66
47
  * }} deps
67
48
  */
68
49
  constructor({
69
50
  ghGraphql,
70
- hooks = {},
71
51
  cache,
72
52
  classifyGithubError = defaultClassifyGithubError,
73
53
  } = {}) {
74
54
  this._ghGraphql = ghGraphql;
75
- this._hooks = hooks;
76
55
  this._cache = cache;
77
56
  this._classify = classifyGithubError;
78
57
  }
@@ -132,191 +111,4 @@ export class SubIssueGateway {
132
111
  }
133
112
  return childIds;
134
113
  }
135
-
136
- /**
137
- * Establish the native sub-issue link between `parentNumber` and the
138
- * child identified by `childNodeId`. Retries on transient errors with
139
- * jittered exponential backoff before re-throwing.
140
- */
141
- async addSubIssue(
142
- parentNumber,
143
- childNodeId,
144
- opts = { replaceParent: false },
145
- ) {
146
- const getTicket = this._hooks.getTicket;
147
- if (typeof getTicket !== 'function') {
148
- throw new Error(
149
- '[SubIssueGateway] addSubIssue requires a getTicket hook',
150
- );
151
- }
152
- const parentTicket = await getTicket(parentNumber);
153
- let lastErr;
154
- for (let attempt = 0; attempt < SUB_ISSUE_RETRY_MAX_ATTEMPTS; attempt++) {
155
- try {
156
- const result = await this._ghGraphql(
157
- ADD_SUB_ISSUE_MUTATION,
158
- {
159
- parentId: parentTicket.nodeId,
160
- subIssueId: childNodeId,
161
- replaceParent: opts.replaceParent,
162
- },
163
- { headers: { 'GraphQL-Features': 'sub_issues' } },
164
- );
165
- // Sub-issue link mutates the parent's sub-issue list. Invalidate so
166
- // the next `getTicket(parentNumber)` re-fetches a coherent view.
167
- if (typeof this._hooks.invalidateTicket === 'function') {
168
- this._hooks.invalidateTicket(parentNumber);
169
- }
170
- return result;
171
- } catch (err) {
172
- lastErr = err;
173
- const category = this._classify(err);
174
- const isFinalAttempt = attempt === SUB_ISSUE_RETRY_MAX_ATTEMPTS - 1;
175
- if (category !== 'transient' || isFinalAttempt) throw err;
176
- const base = Math.min(
177
- SUB_ISSUE_RETRY_MAX_DELAY_MS,
178
- SUB_ISSUE_RETRY_BASE_DELAY_MS * 2 ** attempt,
179
- );
180
- const delay =
181
- base + Math.floor(Math.random() * SUB_ISSUE_RETRY_JITTER_MS);
182
- Logger.warn(
183
- `[GitHubProvider] sub-issue link transient error for parent #${parentNumber} (attempt ${attempt + 1}/${SUB_ISSUE_RETRY_MAX_ATTEMPTS}); retrying in ${delay}ms: ${err.message}`,
184
- );
185
- await new Promise((r) => setTimeout(r, delay));
186
- }
187
- }
188
- throw lastErr;
189
- }
190
-
191
- async removeSubIssue(parentNumber, subIssueNumber) {
192
- const getTicket = this._hooks.getTicket;
193
- if (typeof getTicket !== 'function') {
194
- throw new Error(
195
- '[SubIssueGateway] removeSubIssue requires a getTicket hook',
196
- );
197
- }
198
- const parentTicket = await getTicket(parentNumber);
199
- const childTicket = await getTicket(subIssueNumber);
200
- const result = await this._ghGraphql(
201
- REMOVE_SUB_ISSUE_MUTATION,
202
- { parentId: parentTicket.nodeId, subIssueId: childTicket.nodeId },
203
- { headers: { 'GraphQL-Features': 'sub_issues' } },
204
- );
205
- if (typeof this._hooks.invalidateTicket === 'function') {
206
- this._hooks.invalidateTicket(parentNumber);
207
- this._hooks.invalidateTicket(subIssueNumber);
208
- }
209
- return result;
210
- }
211
-
212
- /**
213
- * Walk every child of `epicId` whose body footer carries `parent: #N` and
214
- * verify the native sub-issue link is present. Re-establish missing links
215
- * via `addSubIssue` (which retries internally). Idempotent.
216
- */
217
- async reconcileSubIssueLinks(epicId) {
218
- const PARENT_RE = /(?:^|\n)parent:\s*#(\d+)/;
219
- const getTicket = this._hooks.getTicket;
220
- const getTickets = this._hooks.getTickets;
221
- if (typeof getTicket !== 'function' || typeof getTickets !== 'function') {
222
- throw new Error(
223
- '[SubIssueGateway] reconcileSubIssueLinks requires getTicket + getTickets hooks',
224
- );
225
- }
226
- const allChildren = await getTickets(epicId);
227
- const parentByChild = new Map();
228
- for (const child of allChildren) {
229
- const match = (child.body ?? '').match(PARENT_RE);
230
- if (!match) continue;
231
- parentByChild.set(child.id, Number.parseInt(match[1], 10));
232
- }
233
-
234
- const childrenByParent = new Map();
235
- for (const [childId, parentId] of parentByChild) {
236
- if (!childrenByParent.has(parentId)) childrenByParent.set(parentId, []);
237
- childrenByParent.get(parentId).push(childId);
238
- }
239
-
240
- let alreadyLinked = 0;
241
- let reconciled = 0;
242
- let failed = 0;
243
- const failures = [];
244
-
245
- const parentEntries = Array.from(childrenByParent.entries());
246
-
247
- await concurrentMap(
248
- parentEntries,
249
- async ([parentId, childIds]) => {
250
- let parent;
251
- try {
252
- parent = await getTicket(parentId);
253
- } catch (err) {
254
- for (const childId of childIds) {
255
- failures.push({ parentId, childId, reason: err.message });
256
- }
257
- failed += childIds.length;
258
- return;
259
- }
260
- if (!parent?.nodeId) {
261
- for (const childId of childIds) {
262
- failures.push({
263
- parentId,
264
- childId,
265
- reason: 'parent missing nodeId',
266
- });
267
- }
268
- failed += childIds.length;
269
- return;
270
- }
271
-
272
- const linked = new Set(
273
- await this.getNativeSubIssues(parent.nodeId, parentId),
274
- );
275
-
276
- await concurrentMap(
277
- childIds,
278
- async (childId) => {
279
- if (linked.has(childId)) {
280
- alreadyLinked++;
281
- return;
282
- }
283
- let childTicket;
284
- try {
285
- childTicket = await getTicket(childId);
286
- } catch (err) {
287
- failures.push({ parentId, childId, reason: err.message });
288
- failed++;
289
- return;
290
- }
291
- if (!childTicket?.nodeId) {
292
- failures.push({
293
- parentId,
294
- childId,
295
- reason: 'child missing nodeId',
296
- });
297
- failed++;
298
- return;
299
- }
300
- try {
301
- await this.addSubIssue(parentId, childTicket.nodeId);
302
- reconciled++;
303
- } catch (err) {
304
- failures.push({ parentId, childId, reason: err.message });
305
- failed++;
306
- }
307
- },
308
- { concurrency: SUB_ISSUE_RECONCILE_CONCURRENCY },
309
- );
310
- },
311
- { concurrency: SUB_ISSUE_RECONCILE_CONCURRENCY },
312
- );
313
-
314
- return {
315
- totalExpected: parentByChild.size,
316
- alreadyLinked,
317
- reconciled,
318
- failed,
319
- failures,
320
- };
321
- }
322
114
  }
@@ -19,8 +19,10 @@
19
19
  * module.
20
20
  *
21
21
  * Gateway map: tickets, sub-issues, comments, labels, branch-protection,
22
- * merge-methods, prs, project-board, issues (epics + sub-tickets +
23
- * branch probe + raw GraphQL).
22
+ * merge-methods, project-board, issues (epics + sub-tickets + branch probe
23
+ * + raw GraphQL). Story #5008 retired the `prs` gateway — `single-story-close`
24
+ * drives `gh pr create` directly — along with the sub-issue write surface and
25
+ * the repo-wide recent-comments feed.
24
26
  */
25
27
 
26
28
  import { createGh, gh as defaultGh } from '../lib/gh-exec.js';
@@ -125,16 +127,11 @@ const DELEGATIONS = [
125
127
  ['updateTicket', 'tickets.updateTicket'],
126
128
  ['_applyLabelMutations', 'tickets._applyLabelMutations'],
127
129
  ['_getNativeSubIssues', 'subIssues.getNativeSubIssues'],
128
- ['addSubIssue', 'subIssues.addSubIssue'],
129
- ['removeSubIssue', 'subIssues.removeSubIssue'],
130
- ['reconcileSubIssueLinks', 'subIssues.reconcileSubIssueLinks'],
131
- ['getRecentComments', 'comments.getRecentComments'],
132
130
  ['getTicketComments', 'comments.getTicketComments'],
133
131
  ['deleteComment', 'comments.deleteComment'],
134
132
  ['postComment', 'comments.postComment'],
135
133
  ['getBranchProtection', 'branchProtection.getBranchProtection'],
136
134
  ['setBranchProtection', 'branchProtection.setBranchProtection'],
137
- ['createPullRequest', 'pullRequests.createPullRequest'],
138
135
  ['ensureLabels', 'labels.ensureLabels'],
139
136
  ['_reconcileLabelsPresence', 'labels._reconcileLabelsPresence'],
140
137
  ['getMergeMethods', 'mergeMethods.getMergeMethods'],