mandrel 2.0.0 → 2.1.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 (311) hide show
  1. package/.agents/README.md +59 -28
  2. package/.agents/agents/acceptance-critic.md +9 -7
  3. package/.agents/agents/story-worker.md +41 -46
  4. package/.agents/audit-checklists/performance.md +1 -1
  5. package/.agents/docs/SDLC.md +51 -44
  6. package/.agents/docs/agentrc-reference.json +8 -13
  7. package/.agents/docs/configuration.md +32 -56
  8. package/.agents/docs/execution-reference.md +39 -10
  9. package/.agents/docs/quality-gates.md +14 -16
  10. package/.agents/docs/workflows.md +6 -6
  11. package/.agents/instructions.md +64 -79
  12. package/.agents/rules/ci-remediation.md +3 -3
  13. package/.agents/rules/git-conventions-reference.md +42 -51
  14. package/.agents/schemas/agentrc.schema.json +34 -45
  15. package/.agents/schemas/audit-rules.json +59 -1
  16. package/.agents/schemas/audit-rules.schema.json +33 -1
  17. package/.agents/schemas/lifecycle/README.md +1 -2
  18. package/.agents/schemas/lifecycle/ledger-record.schema.json +1 -1
  19. package/.agents/schemas/lifecycle/merge.flip-failed.schema.json +33 -0
  20. package/.agents/schemas/lifecycle/merge.unlanded.schema.json +1 -0
  21. package/.agents/schemas/lifecycle/story.merged.schema.json +1 -1
  22. package/.agents/schemas/signal-event.schema.json +3 -3
  23. package/.agents/schemas/story-deliver-terminal.schema.json +152 -0
  24. package/.agents/schemas/validation-evidence.schema.json +1 -1
  25. package/.agents/scripts/acceptance-eval.js +22 -66
  26. package/.agents/scripts/agents-bootstrap-github.js +1 -1
  27. package/.agents/scripts/bootstrap.js +3 -3
  28. package/.agents/scripts/check-dead-exports.js +43 -104
  29. package/.agents/scripts/check-doc-links.js +2 -2
  30. package/.agents/scripts/check-lifecycle-lint.js +1 -1
  31. package/.agents/scripts/check-workflow-cli-lint.js +91 -0
  32. package/.agents/scripts/deliver-recover.js +122 -0
  33. package/.agents/scripts/drain-pending-cleanup.js +1 -1
  34. package/.agents/scripts/evidence-gate.js +20 -50
  35. package/.agents/scripts/generate-skills-index.js +17 -1
  36. package/.agents/scripts/generate-workflows-doc.js +4 -4
  37. package/.agents/scripts/lib/ITicketingProvider.js +1 -19
  38. package/.agents/scripts/lib/audit-suite/selector.js +323 -23
  39. package/.agents/scripts/lib/baselines/kinds/maintainability.js +0 -11
  40. package/.agents/scripts/lib/bootstrap/ci-workflow-template.js +28 -33
  41. package/.agents/scripts/lib/bootstrap/manifest.js +8 -11
  42. package/.agents/scripts/lib/bootstrap/project-bootstrap.js +30 -53
  43. package/.agents/scripts/lib/bootstrap/quality-bootstrap.js +0 -2
  44. package/.agents/scripts/lib/checks/core-bare-clean.js +4 -1
  45. package/.agents/scripts/lib/checks/index.js +1 -1
  46. package/.agents/scripts/lib/checks/loop-health.js +12 -11
  47. package/.agents/scripts/lib/checks/state.js +17 -248
  48. package/.agents/scripts/lib/checks/story-init-not-backgrounded.js +3 -3
  49. package/.agents/scripts/lib/checks/subagent-agent-tool-required.js +3 -4
  50. package/.agents/scripts/lib/checks/worktree-bootstrap-env.js +2 -2
  51. package/.agents/scripts/lib/checks/worktree-residue-biome.js +3 -3
  52. package/.agents/scripts/lib/cli-args.js +23 -2
  53. package/.agents/scripts/lib/close-validation/gates.js +13 -13
  54. package/.agents/scripts/lib/close-validation/projections/inputs.js +7 -7
  55. package/.agents/scripts/lib/close-validation/projections/maintainability.js +12 -12
  56. package/.agents/scripts/lib/close-validation/runner.js +13 -21
  57. package/.agents/scripts/lib/close-validation/telemetry.js +17 -8
  58. package/.agents/scripts/lib/config/delivery-routing.js +7 -6
  59. package/.agents/scripts/lib/config/explain.js +10 -16
  60. package/.agents/scripts/lib/config/github.js +7 -5
  61. package/.agents/scripts/lib/config/limits.js +15 -25
  62. package/.agents/scripts/lib/config/quality.js +11 -14
  63. package/.agents/scripts/lib/config/runners.js +8 -21
  64. package/.agents/scripts/lib/config/temp-paths.js +18 -56
  65. package/.agents/scripts/lib/config-settings-schema-delivery.js +31 -13
  66. package/.agents/scripts/lib/config-settings-schema-quality.js +9 -2
  67. package/.agents/scripts/lib/config-settings-schema.js +48 -22
  68. package/.agents/scripts/lib/dead-exports-knip.js +105 -0
  69. package/.agents/scripts/lib/dead-exports-mode.js +51 -0
  70. package/.agents/scripts/lib/duplicate-search.js +38 -7
  71. package/.agents/scripts/lib/findings/promote-finding.js +23 -14
  72. package/.agents/scripts/lib/format-generated-json.js +97 -0
  73. package/.agents/scripts/lib/framework-version.js +19 -189
  74. package/.agents/scripts/lib/gh-exec.js +8 -0
  75. package/.agents/scripts/lib/git-branch-lifecycle.js +0 -158
  76. package/.agents/scripts/lib/git-utils.js +0 -14
  77. package/.agents/scripts/lib/json-utils.js +1 -2
  78. package/.agents/scripts/lib/label-constants.js +0 -15
  79. package/.agents/scripts/lib/label-taxonomy.js +1 -12
  80. package/.agents/scripts/lib/observability/active-story-env.js +42 -163
  81. package/.agents/scripts/lib/observability/runtime-friction.js +243 -0
  82. package/.agents/scripts/lib/observability/signal-validator.js +4 -4
  83. package/.agents/scripts/lib/observability/signals-writer.js +6 -82
  84. package/.agents/scripts/lib/observability/source-classifier.js +2 -2
  85. package/.agents/scripts/lib/observability/tool-trace-hook.js +2 -12
  86. package/.agents/scripts/lib/orchestration/acceptance-clusters.js +1 -1
  87. package/.agents/scripts/lib/orchestration/acceptance-eval-decision.js +2 -2
  88. package/.agents/scripts/lib/orchestration/ceremony-routing.js +43 -45
  89. package/.agents/scripts/lib/orchestration/code-review.js +58 -168
  90. package/.agents/scripts/lib/orchestration/consolidation-precondition.js +3 -3
  91. package/.agents/scripts/lib/orchestration/deliver-recover.js +328 -0
  92. package/.agents/scripts/lib/orchestration/detectors-phase.js +12 -6
  93. package/.agents/scripts/lib/orchestration/git-cleanup/phases/fast-forward.js +34 -0
  94. package/.agents/scripts/lib/orchestration/lease-guard-shared.js +3 -2
  95. package/.agents/scripts/lib/orchestration/lifecycle/emit-ledger-event.js +142 -0
  96. package/.agents/scripts/lib/orchestration/lifecycle/emit-loop-tick.js +9 -11
  97. package/.agents/scripts/lib/orchestration/lifecycle/emit-merge-flip-failed.js +86 -0
  98. package/.agents/scripts/lib/orchestration/lifecycle/emit-merge-unlanded.js +37 -103
  99. package/.agents/scripts/lib/orchestration/lifecycle/listeners/README.md +7 -3
  100. package/.agents/scripts/lib/orchestration/lifecycle/listeners/watcher.js +50 -85
  101. package/.agents/scripts/lib/orchestration/lifecycle/trace-logger.js +3 -14
  102. package/.agents/scripts/lib/orchestration/merge-block-class.js +76 -20
  103. package/.agents/scripts/lib/orchestration/merge-poll.js +104 -0
  104. package/.agents/scripts/lib/orchestration/plan-context.js +114 -24
  105. package/.agents/scripts/lib/orchestration/plan-critic-conditions.js +11 -22
  106. package/.agents/scripts/lib/orchestration/plan-critics-evaluate.js +3 -7
  107. package/.agents/scripts/lib/orchestration/plan-metrics.js +38 -6
  108. package/.agents/scripts/lib/orchestration/plan-persist/fan-out-gate.js +16 -6
  109. package/.agents/scripts/lib/orchestration/plan-persist/persist-helpers.js +173 -25
  110. package/.agents/scripts/lib/orchestration/plan-persist/plan-context-source.js +116 -0
  111. package/.agents/scripts/lib/orchestration/plan-persist/run-plan-persist.js +280 -75
  112. package/.agents/scripts/lib/orchestration/plan-persist/story-ops.js +472 -55
  113. package/.agents/scripts/lib/orchestration/plan-persist/summary.js +21 -16
  114. package/.agents/scripts/lib/orchestration/plan-persist/supersede-ops.js +509 -0
  115. package/.agents/scripts/lib/orchestration/planning/authoring-context.js +41 -40
  116. package/.agents/scripts/lib/orchestration/planning/spec-authoring-grounding.js +1 -1
  117. package/.agents/scripts/lib/orchestration/resolve-stories.js +344 -0
  118. package/.agents/scripts/lib/orchestration/retro-proposals.js +7 -7
  119. package/.agents/scripts/lib/orchestration/review-depth.js +105 -40
  120. package/.agents/scripts/lib/orchestration/review-providers/findings-renderer.js +3 -13
  121. package/.agents/scripts/lib/orchestration/review-providers/native.js +1 -154
  122. package/.agents/scripts/lib/orchestration/review-providers/review-depth.js +3 -2
  123. package/.agents/scripts/lib/orchestration/review-providers/security-review.js +1 -1
  124. package/.agents/scripts/lib/orchestration/review-providers/types.js +5 -4
  125. package/.agents/scripts/lib/orchestration/review-providers/ultrareview.js +1 -1
  126. package/.agents/scripts/lib/orchestration/run-epilogue.js +374 -16
  127. package/.agents/scripts/lib/orchestration/single-story-close/phases/auto-merge.js +24 -0
  128. package/.agents/scripts/lib/orchestration/single-story-close/phases/base-sync.js +11 -9
  129. package/.agents/scripts/lib/orchestration/single-story-close/phases/close-validation.js +4 -4
  130. package/.agents/scripts/lib/orchestration/single-story-close/phases/code-review.js +3 -12
  131. package/.agents/scripts/lib/orchestration/single-story-close/phases/confirm-merge.js +608 -152
  132. package/.agents/scripts/lib/orchestration/single-story-close/phases/options.js +72 -30
  133. package/.agents/scripts/lib/orchestration/single-story-close/phases/post-land.js +305 -0
  134. package/.agents/scripts/lib/orchestration/single-story-close/phases/pull-request.js +1 -1
  135. package/.agents/scripts/lib/orchestration/single-story-close/phases/review-block.js +12 -8
  136. package/.agents/scripts/lib/orchestration/single-story-close/phases/worktree-reap.js +37 -4
  137. package/.agents/scripts/lib/orchestration/single-story-close/phases/wrong-tree-guard.js +2 -2
  138. package/.agents/scripts/lib/orchestration/single-story-close/runner.js +264 -43
  139. package/.agents/scripts/lib/orchestration/single-story-lease-guard.js +1 -1
  140. package/.agents/scripts/lib/orchestration/story-close/format-autofix.js +10 -10
  141. package/.agents/scripts/lib/orchestration/story-close/phases/code-review.js +15 -32
  142. package/.agents/scripts/lib/orchestration/story-deliver-terminal.js +360 -0
  143. package/.agents/scripts/lib/orchestration/story-follow-ups.js +75 -14
  144. package/.agents/scripts/lib/orchestration/story-init-remote.js +12 -8
  145. package/.agents/scripts/lib/orchestration/story-plan-state.js +14 -29
  146. package/.agents/scripts/lib/orchestration/task-body-validator.js +52 -7
  147. package/.agents/scripts/lib/orchestration/ticket-lease.js +27 -74
  148. package/.agents/scripts/lib/orchestration/ticket-validator-conflicts.js +119 -14
  149. package/.agents/scripts/lib/orchestration/ticket-validator-sizing.js +3 -4
  150. package/.agents/scripts/lib/orchestration/ticket-validator.js +121 -18
  151. package/.agents/scripts/lib/orchestration/ticketing/bulk.js +14 -47
  152. package/.agents/scripts/lib/orchestration/ticketing/reads.js +19 -32
  153. package/.agents/scripts/lib/orchestration/ticketing/transition.js +61 -1
  154. package/.agents/scripts/lib/orchestration/ticketing.js +0 -1
  155. package/.agents/scripts/lib/plan-phase-cleanup.js +12 -14
  156. package/.agents/scripts/lib/planning-corpus.js +12 -286
  157. package/.agents/scripts/lib/preflight-runner.js +2 -2
  158. package/.agents/scripts/lib/qa/qa-context-hydrator.js +5 -5
  159. package/.agents/scripts/lib/signals/index.js +4 -17
  160. package/.agents/scripts/lib/signals/read.js +35 -35
  161. package/.agents/scripts/lib/signals/schema.js +8 -11
  162. package/.agents/scripts/lib/signals/span-tree.js +7 -7
  163. package/.agents/scripts/lib/signals/write.js +0 -1
  164. package/.agents/scripts/lib/single-story/story-merged-notify.js +13 -2
  165. package/.agents/scripts/lib/skills/parse-skill.js +16 -3
  166. package/.agents/scripts/lib/story-adjacency.js +8 -7
  167. package/.agents/scripts/lib/story-body/story-body.js +6 -5
  168. package/.agents/scripts/lib/templates/decomposer-prompts.js +7 -3
  169. package/.agents/scripts/lib/test-env.js +14 -1
  170. package/.agents/scripts/lib/test-tiers.js +0 -3
  171. package/.agents/scripts/lib/ticket-body-sections.js +0 -14
  172. package/.agents/scripts/lib/validation-evidence.js +31 -59
  173. package/.agents/scripts/lib/wave-runner/ready-set.js +32 -6
  174. package/.agents/scripts/lib/worktree/lifecycle/pending-cleanup.js +1 -1
  175. package/.agents/scripts/lib/worktree/lifecycle/reap.js +68 -19
  176. package/.agents/scripts/lib/worktree/lifecycle-manager.js +1 -2
  177. package/.agents/scripts/plan-context.js +38 -6
  178. package/.agents/scripts/plan-persist.js +145 -35
  179. package/.agents/scripts/plan-run-epilogue.js +83 -38
  180. package/.agents/scripts/post-structured-comment.js +0 -38
  181. package/.agents/scripts/pr-watch-with-update.js +43 -22
  182. package/.agents/scripts/providers/github/compose.js +0 -1
  183. package/.agents/scripts/providers/github/errors.js +0 -19
  184. package/.agents/scripts/providers/github/issues.js +1 -11
  185. package/.agents/scripts/providers/github/mappers.js +5 -0
  186. package/.agents/scripts/providers/github/sub-issues.js +0 -47
  187. package/.agents/scripts/providers/github/tickets.js +33 -153
  188. package/.agents/scripts/providers/github.js +17 -6
  189. package/.agents/scripts/resolve-stories.js +236 -0
  190. package/.agents/scripts/run-coverage.js +4 -1
  191. package/.agents/scripts/run-lint.js +2 -2
  192. package/.agents/scripts/run-verify.js +31 -2
  193. package/.agents/scripts/signals-view.js +9 -10
  194. package/.agents/scripts/single-story-close.js +173 -18
  195. package/.agents/scripts/single-story-confirm-merge.js +288 -15
  196. package/.agents/scripts/single-story-init.js +6 -10
  197. package/.agents/scripts/stories-wave-tick.js +79 -4
  198. package/.agents/scripts/story-plan.js +3 -3
  199. package/.agents/scripts/update-ticket-state.js +8 -50
  200. package/.agents/skills/core/code-review-and-quality/SKILL.md +28 -450
  201. package/.agents/skills/core/code-review-and-quality/reference.md +458 -0
  202. package/.agents/skills/core/debugging-and-error-recovery/SKILL.md +22 -315
  203. package/.agents/skills/core/debugging-and-error-recovery/reference.md +323 -0
  204. package/.agents/skills/core/diagnose-friction/SKILL.md +14 -18
  205. package/.agents/skills/core/documentation-and-adrs/SKILL.md +25 -397
  206. package/.agents/skills/core/documentation-and-adrs/reference.md +403 -0
  207. package/.agents/skills/core/gates-and-baselines/SKILL.md +12 -12
  208. package/.agents/skills/core/idea-refinement/SKILL.md +3 -3
  209. package/.agents/skills/core/scope-triage/SKILL.md +3 -0
  210. package/.agents/skills/core/security-and-hardening/SKILL.md +22 -367
  211. package/.agents/skills/core/security-and-hardening/reference.md +375 -0
  212. package/.agents/skills/skills.index.json +2 -12
  213. package/.agents/skills/stack/qa/playwright-bdd/SKILL.md +2 -4
  214. package/.agents/skills/stack/qa/qa-explore-driving/SKILL.md +1 -1
  215. package/.agents/skills/stack/qa/qa-harness/SKILL.md +1 -3
  216. package/.agents/workflows/audit-architecture.md +3 -4
  217. package/.agents/workflows/audit-clean-code.md +4 -4
  218. package/.agents/workflows/audit-documentation.md +4 -5
  219. package/.agents/workflows/audit-lighthouse.md +8 -0
  220. package/.agents/workflows/audit-navigability.md +10 -0
  221. package/.agents/workflows/audit-performance.md +2 -3
  222. package/.agents/workflows/audit-quality.md +8 -9
  223. package/.agents/workflows/audit-security.md +1 -2
  224. package/.agents/workflows/audit-seo.md +10 -0
  225. package/.agents/workflows/audit-ux-ui.md +7 -0
  226. package/.agents/workflows/deliver.md +98 -45
  227. package/.agents/workflows/git-cleanup.md +2 -2
  228. package/.agents/workflows/git-deliver.md +1 -1
  229. package/.agents/workflows/helpers/acceptance-self-eval.md +21 -13
  230. package/.agents/workflows/helpers/code-quality-guardrails.md +7 -7
  231. package/.agents/workflows/helpers/code-review.md +12 -10
  232. package/.agents/workflows/helpers/deliver-story-reference.md +73 -32
  233. package/.agents/workflows/helpers/deliver-story.md +193 -118
  234. package/.agents/workflows/helpers/parallel-tooling.md +2 -2
  235. package/.agents/workflows/helpers/worktree-lifecycle.md +28 -32
  236. package/.agents/workflows/plan.md +184 -19
  237. package/.agents/workflows/qa-assist.md +6 -6
  238. package/.agents/workflows/qa-explore.md +3 -3
  239. package/.agents/workflows/qa-run.md +1 -5
  240. package/bin/mandrel.js +12 -1
  241. package/docs/CHANGELOG.md +40 -0
  242. package/lib/cli/registry.js +262 -19
  243. package/lib/cli/sync-agents.js +157 -0
  244. package/lib/cli/sync-commands.js +115 -6
  245. package/lib/cli/sync.js +168 -6
  246. package/lib/cli/update.js +105 -8
  247. package/lib/cli/version-helpers.js +131 -0
  248. package/lib/migrations/README.md +7 -5
  249. package/lib/migrations/index.js +12 -9
  250. package/lib/migrations/steps/2.1.0-retire-mi-drop-knobs.js +100 -0
  251. package/lib/migrations/steps/2.1.0-retire-verify-concurrency-cap.js +101 -0
  252. package/package.json +1 -1
  253. package/.agents/schemas/epic-perf-report.schema.json +0 -89
  254. package/.agents/schemas/lifecycle/acceptance.reconcile.failed.schema.json +0 -13
  255. package/.agents/schemas/lifecycle/acceptance.reconcile.ok.schema.json +0 -13
  256. package/.agents/schemas/lifecycle/acceptance.reconcile.skipped.schema.json +0 -13
  257. package/.agents/schemas/lifecycle/acceptance.reconcile.start.schema.json +0 -12
  258. package/.agents/schemas/lifecycle/acceptance.reconcile.waived.schema.json +0 -13
  259. package/.agents/schemas/lifecycle/epic.automerge.end.schema.json +0 -15
  260. package/.agents/schemas/lifecycle/epic.automerge.start.schema.json +0 -13
  261. package/.agents/schemas/lifecycle/epic.blocked.schema.json +0 -13
  262. package/.agents/schemas/lifecycle/epic.cleanup.end.schema.json +0 -12
  263. package/.agents/schemas/lifecycle/epic.cleanup.start.schema.json +0 -12
  264. package/.agents/schemas/lifecycle/epic.close.end.schema.json +0 -12
  265. package/.agents/schemas/lifecycle/epic.complete.schema.json +0 -13
  266. package/.agents/schemas/lifecycle/epic.finalize.end.schema.json +0 -13
  267. package/.agents/schemas/lifecycle/epic.finalize.start.schema.json +0 -12
  268. package/.agents/schemas/lifecycle/epic.merge.armed.schema.json +0 -13
  269. package/.agents/schemas/lifecycle/epic.merge.blocked.schema.json +0 -14
  270. package/.agents/schemas/lifecycle/epic.merge.confirmed.schema.json +0 -17
  271. package/.agents/schemas/lifecycle/epic.merge.ready.schema.json +0 -15
  272. package/.agents/schemas/lifecycle/epic.plan.end.schema.json +0 -18
  273. package/.agents/schemas/lifecycle/epic.plan.start.schema.json +0 -12
  274. package/.agents/schemas/lifecycle/epic.snapshot.end.schema.json +0 -16
  275. package/.agents/schemas/lifecycle/epic.snapshot.start.schema.json +0 -12
  276. package/.agents/schemas/lifecycle/epic.watch.end.schema.json +0 -29
  277. package/.agents/schemas/lifecycle/epic.watch.start.schema.json +0 -16
  278. package/.agents/schemas/lifecycle/story.heartbeat.schema.json +0 -20
  279. package/.agents/schemas/risk-verdict.schema.json +0 -53
  280. package/.agents/schemas/story-perf-summary.schema.json +0 -73
  281. package/.agents/scripts/analyze-execution.js +0 -444
  282. package/.agents/scripts/check-prepush-recovery.js +0 -90
  283. package/.agents/scripts/lib/git-merge-orchestrator.js +0 -261
  284. package/.agents/scripts/lib/observability/baseline-refresh-rate.js +0 -221
  285. package/.agents/scripts/lib/observability/hook-heartbeat.js +0 -187
  286. package/.agents/scripts/lib/observability/perf-aggregator.js +0 -813
  287. package/.agents/scripts/lib/observability/perf-report-readers.js +0 -328
  288. package/.agents/scripts/lib/observability/perf-report-render.js +0 -182
  289. package/.agents/scripts/lib/orchestration/audit-lens-routing.js +0 -128
  290. package/.agents/scripts/lib/orchestration/bookkeeping-outbox.js +0 -273
  291. package/.agents/scripts/lib/orchestration/error-journal.js +0 -139
  292. package/.agents/scripts/lib/orchestration/lifecycle/emit-story-heartbeat.js +0 -155
  293. package/.agents/scripts/lib/orchestration/lifecycle/ledger-diff.js +0 -140
  294. package/.agents/scripts/lib/orchestration/lifecycle/listeners/merge-watcher.js +0 -665
  295. package/.agents/scripts/lib/orchestration/plan-review-routing.js +0 -63
  296. package/.agents/scripts/lib/orchestration/planning/risk-verdict.js +0 -104
  297. package/.agents/scripts/lib/orchestration/planning-context-budget.js +0 -213
  298. package/.agents/scripts/lib/orchestration/planning-risk.js +0 -194
  299. package/.agents/scripts/lib/orchestration/post-merge/phases/branch-cleanup.js +0 -56
  300. package/.agents/scripts/lib/orchestration/post-merge/phases/dashboard-refresh.js +0 -21
  301. package/.agents/scripts/lib/orchestration/post-merge/phases/notification.js +0 -78
  302. package/.agents/scripts/lib/orchestration/post-merge/phases/temp-cleanup.js +0 -68
  303. package/.agents/scripts/lib/orchestration/post-merge/phases/ticket-closure.js +0 -118
  304. package/.agents/scripts/lib/orchestration/post-merge/phases/worktree-reap.js +0 -397
  305. package/.agents/scripts/lib/orchestration/preflight-cache.js +0 -187
  306. package/.agents/scripts/lib/orchestration/resolve-plan-run.js +0 -155
  307. package/.agents/scripts/lib/orchestration/retro-perf-heuristics.js +0 -275
  308. package/.agents/scripts/lib/orchestration/story-progress/story-run-progress-writer.js +0 -400
  309. package/.agents/scripts/lib/single-story/confirm-merge-follow-ups.js +0 -36
  310. package/.agents/scripts/resolve-plan-run.js +0 -117
  311. package/.agents/skills/core/analyze-execution/SKILL.md +0 -101
@@ -1,35 +1,52 @@
1
1
  /**
2
- * lib/orchestration/review-depth.js — Shared review-depth resolver combining
3
- * judged risk and mechanical diff width (Story #3938).
2
+ * lib/orchestration/review-depth.js — the review-depth authority: derives a
3
+ * change level from observable facts and folds it with diff width into a tier
4
+ * (Story #3938, re-based on a derived signal by Story #4542).
4
5
  *
5
- * The single depth authority for the code-review pipeline. It folds the two
6
- * signals the framework already computes — the planner-judged risk
7
- * (`planningRisk.overallLevel`) and the mechanical changed-file count of the
8
- * diff under review — into one tier (`light` / `standard` / `deep`) without
9
- * inventing a new complexity score or any new config knob.
6
+ * ## Why the level is derived, not authored
10
7
  *
11
- * Tier rules:
12
- * - `deep` — `overallLevel === 'high'` OR the changed-file count exceeds
13
- * the wide-change scale (`diffWidth.hardFiles`). A wide-
14
- * footprint Epic whose risk was judged low (or never judged)
15
- * still earns a deep pass on size alone.
16
- * - `light` — `overallLevel === 'low'` AND the changed-file count is at or
17
- * below the small-change scale (`diffWidth.softFiles`). An
18
- * *unknown* count does NOT block `light`: a missing diff width
19
- * is treated as "not wide", so a low-risk Epic with no count
20
- * still resolves to `light` (preserves the #3937 producer
21
- * contract where depth is resolved from risk before any diff
22
- * is enumerated).
23
- * - `standard` — everything else, including absent/malformed risk envelopes
24
- * and a `medium` level. Fail toward the middle, never toward
25
- * `light`: an Epic that skipped `/plan` has no risk
26
- * verdict, and treating it as `light` would under-review
27
- * unjudged work while `standard` preserves today's behaviour.
8
+ * This module used to consume `planningRisk.overallLevel` — a level the planner
9
+ * asserted about the plan it had just written. That signal was self-refereed and
10
+ * its fail-safe ran backwards: an *absent* verdict degraded toward MORE ceremony
11
+ * (standard depth), while a schema-valid all-low verdict bought `light`. Nothing
12
+ * cross-checked the claimed axes against the files actually touched, so the one
13
+ * input that could *reduce* review was exactly the one nobody verified. Story
14
+ * #4542 inverted that: the level is now derived at close time from the changed
15
+ * files themselves, which makes it a fact rather than a claim — and therefore
16
+ * trustworthy enough to reduce ceremony on.
28
17
  *
29
- * Pure and total: inputs in, tier out. No I/O, no throws. `null` / `undefined`
30
- * / malformed inputs all degrade to `standard` (or `deep`/`light` only when the
31
- * width signal unambiguously says so). Callers (e.g. `runCodeReview`) enumerate
32
- * the changed-file count from the diff they already run and supply it here.
18
+ * ## The two signals
19
+ *
20
+ * - **Sensitive paths** ({@link deriveChangeLevel}) the changed-file set
21
+ * intersected with the `sensitivePaths` classes registered in
22
+ * `.agents/schemas/audit-rules.json` (auth, migrations, billing, destructive
23
+ * mutation, public API). Registered as **configuration**, matched with the
24
+ * audit-suite's existing picomatch machinery. This is what keeps a *narrow*
25
+ * high-stakes diff — a three-file auth fix, a small migration — at `deep`,
26
+ * the one thing a pure width heuristic would miss.
27
+ * - **Diff width** — the mechanical changed-file count, unchanged in role.
28
+ *
29
+ * ## Tier rules ({@link resolveDepth})
30
+ *
31
+ * - `deep` — `derivedLevel === 'high'` (a sensitive path was touched) OR
32
+ * the changed-file count exceeds the wide-change scale
33
+ * (`diffWidth.hardFiles`). A wide diff earns a deep pass on
34
+ * size alone even when it touches nothing sensitive.
35
+ * - `light` — `derivedLevel === 'low'` (the change set is known and touches
36
+ * no sensitive path) AND the count is at or below the
37
+ * small-change scale (`diffWidth.softFiles`). An *unknown*
38
+ * count does not block `light`: a missing width is treated as
39
+ * "not wide".
40
+ * - `standard` — everything else, including an absent/underivable level. Fail
41
+ * toward the middle, never toward `light`: when the diff cannot
42
+ * be enumerated there is no evidence the change is safe, and
43
+ * `standard` preserves today's behaviour.
44
+ *
45
+ * {@link resolveDepth} is pure and total: inputs in, tier out. No I/O, no
46
+ * throws. `null` / `undefined` / malformed inputs degrade to `standard` (or
47
+ * `deep`/`light` only when a signal unambiguously says so). {@link
48
+ * deriveChangeLevel} owns the one manifest read and is likewise total — any
49
+ * failure degrades to the `null` (fail-safe) level.
33
50
  *
34
51
  * v2 Stage 2: review depth is **decoupled** from the planning model-capacity
35
52
  * advisory. Diff width is a mechanical review signal (files in the landed
@@ -37,8 +54,11 @@
37
54
  * The two no longer share a constant.
38
55
  *
39
56
  * @typedef {'light'|'standard'|'deep'} ReviewDepth
57
+ * @typedef {'low'|'high'} ChangeLevel
40
58
  */
41
59
 
60
+ import { selectSensitivePathClasses } from '../audit-suite/selector.js';
61
+
42
62
  /**
43
63
  * Mechanical diff-width scales for review-depth tiering. These are **not**
44
64
  * planning Story-sizing ceilings (those are gone in v2) — they only classify
@@ -49,6 +69,50 @@ export const DEFAULT_DIFF_WIDTH = Object.freeze({
49
69
  hardFiles: 30,
50
70
  });
51
71
 
72
+ /**
73
+ * Derive the change level for a change set from observable facts: does the
74
+ * changed-file set intersect any sensitive-path class registered in
75
+ * `audit-rules.json`?
76
+ *
77
+ * This is the **single source** of the derived level — both the review depth
78
+ * ({@link resolveDepth}) and the acceptance-critic fresh-vs-inline routing
79
+ * (`ceremony-routing.js#resolveCeremonyForRisk`) consume what this returns, so
80
+ * the two ceremony decisions can never disagree about how risky a change is.
81
+ *
82
+ * Returns `null` — the fail-safe "no derivable signal" level — when the change
83
+ * set is empty/unknown or the manifest cannot be read. Both downstream
84
+ * consumers treat `null` as the more thorough posture (`standard` depth, a
85
+ * `fresh` critic), so a derivation failure never buys a change less checking.
86
+ *
87
+ * Total: never throws.
88
+ *
89
+ * @param {{
90
+ * changedFiles?: string[]|null,
91
+ * injectedRules?: object,
92
+ * selectSensitivePathClassesFn?: typeof selectSensitivePathClasses,
93
+ * }} [input]
94
+ * @returns {{ level: ChangeLevel|null, classes: string[] }}
95
+ */
96
+ export function deriveChangeLevel(input = {}) {
97
+ const changedFiles =
98
+ input && typeof input === 'object' ? input.changedFiles : null;
99
+ if (!Array.isArray(changedFiles) || changedFiles.length === 0) {
100
+ return { level: null, classes: [] };
101
+ }
102
+
103
+ const select =
104
+ input.selectSensitivePathClassesFn ?? selectSensitivePathClasses;
105
+ let classes;
106
+ try {
107
+ classes = select({ changedFiles, injectedRules: input.injectedRules });
108
+ } catch {
109
+ // An unreadable/invalid manifest is not evidence the change is safe.
110
+ return { level: null, classes: [] };
111
+ }
112
+ const matched = Array.isArray(classes) ? classes : [];
113
+ return { level: matched.length > 0 ? 'high' : 'low', classes: matched };
114
+ }
115
+
52
116
  /**
53
117
  * Coerce an arbitrary input into a non-negative integer changed-file count, or
54
118
  * `null` when the count is unknown / unusable. A `null` count is the neutral
@@ -65,20 +129,20 @@ function normalizeChangedFileCount(value) {
65
129
  }
66
130
 
67
131
  /**
68
- * Resolve the review depth for a change set from its judged risk and diff
69
- * width. See the module header for the tier rules.
132
+ * Resolve the review depth for a change set from its derived level (see
133
+ * {@link deriveChangeLevel}) and diff width. See the module header for the tier
134
+ * rules.
70
135
  *
71
136
  * @param {{
72
- * overallLevel?: ('low'|'medium'|'high'|string|null|undefined),
137
+ * derivedLevel?: (ChangeLevel|string|null|undefined),
73
138
  * changedFileCount?: (number|null|undefined),
74
- * sizing?: { softFiles?: number, hardFiles?: number }|null,
75
139
  * diffWidth?: { softFiles?: number, hardFiles?: number }|null,
76
140
  * }} [input]
77
141
  * @returns {ReviewDepth}
78
142
  */
79
143
  export function resolveDepth(input = {}) {
80
- const overallLevel =
81
- input && typeof input === 'object' ? input.overallLevel : undefined;
144
+ const derivedLevel =
145
+ input && typeof input === 'object' ? input.derivedLevel : undefined;
82
146
  const changedFileCount =
83
147
  input && typeof input === 'object'
84
148
  ? normalizeChangedFileCount(input.changedFileCount)
@@ -97,11 +161,12 @@ export function resolveDepth(input = {}) {
97
161
  // An unknown count is treated as "not wide" — it does not block `light`.
98
162
  const isSmall = changedFileCount === null || changedFileCount <= softFiles;
99
163
 
100
- // deep — high judged risk OR a wide diff.
101
- if (overallLevel === 'high' || isWide) return 'deep';
102
- // light — low judged risk AND a (known-or-unknown-but-not-wide) small diff.
103
- if (overallLevel === 'low' && isSmall) return 'light';
104
- // standard everything else (medium, absent/malformed level, or a low-risk
105
- // diff whose width is between softFiles and hardFiles).
164
+ // deep — a sensitive path was touched OR the diff is wide.
165
+ if (derivedLevel === 'high' || isWide) return 'deep';
166
+ // light — nothing sensitive touched AND a (known-or-unknown-but-not-wide)
167
+ // small diff.
168
+ if (derivedLevel === 'low' && isSmall) return 'light';
169
+ // standard everything else: an underivable level, or a non-sensitive diff
170
+ // whose width sits between softFiles and hardFiles.
106
171
  return 'standard';
107
172
  }
@@ -13,7 +13,6 @@
13
13
  *
14
14
  * @typedef {import('./types.js').Finding} Finding
15
15
  * @typedef {import('./types.js').Severity} Severity
16
- * @typedef {import('./types.js').ReviewScope} ReviewScope
17
16
  */
18
17
 
19
18
  /**
@@ -117,7 +116,6 @@ export function renderManualPromptsSection(messages) {
117
116
  * non-empty.
118
117
  *
119
118
  * @param {{
120
- * scope: ReviewScope,
121
119
  * ticketId: number,
122
120
  * baseRef: string,
123
121
  * headRef: string,
@@ -128,26 +126,18 @@ export function renderManualPromptsSection(messages) {
128
126
  * @returns {string}
129
127
  */
130
128
  export function renderFindings(input) {
131
- const {
132
- scope,
133
- ticketId,
134
- baseRef,
135
- headRef,
136
- findings,
137
- provider,
138
- promptMessages,
139
- } = input;
129
+ const { ticketId, baseRef, headRef, findings, provider, promptMessages } =
130
+ input;
140
131
  const counts = countBySeverity(findings);
141
132
  const totalKnown =
142
133
  counts.critical + counts.high + counts.medium + counts.suggestion;
143
134
 
144
- const scopeLabel = scope === 'epic' ? 'Epic' : 'Story';
145
135
  const providerLine = provider
146
136
  ? `**Provider**: \`${provider}\``
147
137
  : '**Provider**: _(unspecified)_';
148
138
 
149
139
  const lines = [
150
- `## 🔬 Code Review — ${scopeLabel} #${ticketId}`,
140
+ `## 🔬 Code Review — Story #${ticketId}`,
151
141
  '',
152
142
  `**Comparison**: \`${baseRef}\` … \`${headRef}\``,
153
143
  providerLine,
@@ -50,11 +50,6 @@ import {
50
50
  } from '../../maintainability-engine.js';
51
51
  import { PROJECT_ROOT } from '../../project-root.js';
52
52
  import { transpileIfNeeded } from '../../transpile.js';
53
- import {
54
- hashCommandConfig,
55
- recordPass,
56
- shouldSkip,
57
- } from '../../validation-evidence.js';
58
53
 
59
54
  /** Worker entry that scores one file into a full maintainability report. */
60
55
  const MAINTAINABILITY_REPORT_WORKER_URL = new URL(
@@ -141,13 +136,6 @@ export function partitionFilesForLint(changedFiles) {
141
136
  return { code, md };
142
137
  }
143
138
 
144
- function resolveCurrentSha(cwd, gitSpawnFn = gitSpawn) {
145
- const res = gitSpawnFn(cwd, 'rev-parse', 'HEAD');
146
- if (res.status !== 0) return null;
147
- const sha = (res.stdout || '').trim();
148
- return sha.length > 0 ? sha : null;
149
- }
150
-
151
139
  /**
152
140
  * Read a changed file's content as it exists at `headRef` via
153
141
  * `git show <headRef>:<relPath>`, rather than reading the on-disk copy at
@@ -260,28 +248,6 @@ export function runScopedLint(changedFiles, cwd, runnerFn = spawnLintRunner) {
260
248
  return { ...summary, skipped: false, mode: 'changed-only' };
261
249
  }
262
250
 
263
- /**
264
- * Compute the canonical command-config hash for the scoped-lint runner. The
265
- * caller passes the partitioned biome + markdown lists so the hash captures
266
- * the exact set of files; any change to that set invalidates the prior
267
- * evidence and forces a re-lint.
268
- *
269
- * Exported for testing.
270
- */
271
- export function buildLintEvidenceConfig(changedFiles, cwd) {
272
- const { code, md } = partitionFilesForLint(changedFiles);
273
- const args = [];
274
- if (code.length > 0) args.push('biome', 'lint', ...code);
275
- if (md.length > 0) {
276
- args.push('markdownlint', ...md, '--ignore', 'node_modules');
277
- }
278
- return hashCommandConfig({
279
- cmd: 'epic-code-review/scoped-lint',
280
- args,
281
- cwd,
282
- });
283
- }
284
-
285
251
  /**
286
252
  * Pure: classify a single file's maintainability report into a row + optional
287
253
  * Finding-shaped entries. `reportFn` is the thunk that produces the file's
@@ -548,87 +514,9 @@ function _emptyResults() {
548
514
  };
549
515
  }
550
516
 
551
- function tryEvidenceSkip({
552
- storyId,
553
- epicId,
554
- useEvidence,
555
- headSha,
556
- evidenceCfg,
557
- shouldSkipFn,
558
- logger,
559
- }) {
560
- if (!(useEvidence && storyId && epicId && headSha)) return null;
561
- const verdict = shouldSkipFn(
562
- {
563
- storyId,
564
- gateName: 'epic-code-review/lint',
565
- currentSha: headSha,
566
- configHash: evidenceCfg,
567
- },
568
- { cwd: PROJECT_ROOT, epicId },
569
- );
570
- if (!verdict.skip) return null;
571
- logger?.info?.(
572
- `[native-review] Scoped lint skipped (evidence match: SHA=${headSha.slice(
573
- 0,
574
- 7,
575
- )}, recorded ${verdict.record?.timestamp ?? 'n/a'}).`,
576
- );
577
- return {
578
- errors: 0,
579
- warnings: 0,
580
- parsed: false,
581
- skipped: true,
582
- mode: 'changed-only',
583
- evidenceSkipped: true,
584
- };
585
- }
586
-
587
- function maybeRecordLintEvidence({
588
- storyId,
589
- epicId,
590
- useEvidence,
591
- headSha,
592
- evidenceCfg,
593
- lintSummary,
594
- recordPassFn,
595
- logger,
596
- }) {
597
- const eligible =
598
- useEvidence &&
599
- storyId &&
600
- epicId &&
601
- headSha &&
602
- lintSummary.errors === 0 &&
603
- !lintSummary.skipped;
604
- if (!eligible) return;
605
- try {
606
- recordPassFn(
607
- {
608
- storyId,
609
- gateName: 'epic-code-review/lint',
610
- sha: headSha,
611
- configHash: evidenceCfg,
612
- exitCode: 0,
613
- },
614
- { cwd: PROJECT_ROOT, epicId },
615
- );
616
- } catch (err) {
617
- logger?.warn?.(
618
- `[native-review] Failed to record lint evidence: ${err?.message ?? err}`,
619
- );
620
- }
621
- }
622
-
623
517
  async function runLintPhase({
624
518
  scopeLint,
625
519
  changedFiles,
626
- storyId,
627
- epicId,
628
- useEvidence,
629
- gitSpawnFn,
630
- shouldSkipFn,
631
- recordPassFn,
632
520
  runScopedLintFn,
633
521
  logger,
634
522
  }) {
@@ -644,34 +532,10 @@ async function runLintPhase({
644
532
  mode: 'off',
645
533
  };
646
534
  }
647
- const evidenceCfg = buildLintEvidenceConfig(changedFiles, PROJECT_ROOT);
648
- const headSha = resolveCurrentSha(PROJECT_ROOT, gitSpawnFn);
649
- const skipSummary = tryEvidenceSkip({
650
- storyId,
651
- epicId,
652
- useEvidence,
653
- headSha,
654
- evidenceCfg,
655
- shouldSkipFn,
656
- logger,
657
- });
658
- if (skipSummary) return skipSummary;
659
-
660
535
  logger?.info?.(
661
536
  '[native-review] Linting changed files only (biome + markdownlint, scoped to diff)...',
662
537
  );
663
- const lintSummary = runScopedLintFn(changedFiles, PROJECT_ROOT);
664
- maybeRecordLintEvidence({
665
- storyId,
666
- epicId,
667
- useEvidence,
668
- headSha,
669
- evidenceCfg,
670
- lintSummary,
671
- recordPassFn,
672
- logger,
673
- });
674
- return lintSummary;
538
+ return runScopedLintFn(changedFiles, PROJECT_ROOT);
675
539
  }
676
540
 
677
541
  /**
@@ -686,12 +550,8 @@ async function runLintPhase({
686
550
  * runScopedLintFn?: typeof runScopedLint,
687
551
  * analyzeChangedFilesFn?: typeof analyzeChangedFiles,
688
552
  * buildLintFindingsFn?: typeof buildLintFindings,
689
- * shouldSkipFn?: typeof shouldSkip,
690
- * recordPassFn?: typeof recordPass,
691
553
  * logger?: { info?: Function, warn?: Function, error?: Function },
692
554
  * scopeLint?: 'changed-only'|'off',
693
- * storyId?: number|null,
694
- * useEvidence?: boolean,
695
555
  * }} [deps]
696
556
  * @returns {ReviewProvider}
697
557
  */
@@ -701,12 +561,8 @@ export function createNativeProvider(deps = {}) {
701
561
  runScopedLintFn = runScopedLint,
702
562
  analyzeChangedFilesFn = analyzeChangedFiles,
703
563
  buildLintFindingsFn = buildLintFindings,
704
- shouldSkipFn = shouldSkip,
705
- recordPassFn = recordPass,
706
564
  logger,
707
565
  scopeLint = 'changed-only',
708
- storyId = null,
709
- useEvidence = true,
710
566
  } = deps;
711
567
 
712
568
  return {
@@ -761,18 +617,9 @@ export function createNativeProvider(deps = {}) {
761
617
  gitSpawnFn,
762
618
  });
763
619
 
764
- // Epic-scope reviews flow through validation-evidence; story-scope
765
- // reviews currently share the same gate name, keyed on the storyId.
766
- const epicId = scope === 'epic' ? ticketId : null;
767
620
  const lintSummary = await runLintPhase({
768
621
  scopeLint,
769
622
  changedFiles,
770
- storyId: storyId ?? (scope === 'story' ? ticketId : null),
771
- epicId,
772
- useEvidence,
773
- gitSpawnFn,
774
- shouldSkipFn,
775
- recordPassFn,
776
623
  runScopedLintFn,
777
624
  logger,
778
625
  });
@@ -3,8 +3,9 @@
3
3
  * LLM-backed review providers.
4
4
  *
5
5
  * Story #3937 / #3938 — the depth lever (`light` / `standard` / `deep`,
6
- * resolved from judged risk AND diff width by `resolveDepth` in
7
- * `lib/orchestration/review-depth.js`) is threaded into every
6
+ * derived from the diff's observable width and sensitive-path hits by
7
+ * `lib/orchestration/review-depth.js`; #4542 retired the judged-risk
8
+ * input) is threaded into every
8
9
  * provider's `runReview` input. LLM-backed providers (codex, security-review,
9
10
  * ultrareview) must render that lever into the prompt/instructions they emit so
10
11
  * the model actually changes its thoroughness; the native (mechanical) provider
@@ -185,7 +185,7 @@ export const SECURITY_REVIEW_INVOKE_PROMPT =
185
185
  * @returns {string}
186
186
  */
187
187
  export function buildSecurityReviewPrompt(input) {
188
- const scopeLabel = input?.scope === 'epic' ? 'Epic' : 'Story';
188
+ const scopeLabel = 'Story';
189
189
  const baseRef = typeof input?.baseRef === 'string' ? input.baseRef : '?';
190
190
  const headRef = typeof input?.headRef === 'string' ? input.headRef : '?';
191
191
  const ticketId =
@@ -26,7 +26,7 @@
26
26
  * @property {number=} line - 1-based line number, when attributable.
27
27
  * @property {string=} category - Free-form tag (e.g. 'security', 'docs', 'lint').
28
28
  *
29
- * @typedef {'story'|'epic'} ReviewScope
29
+ * @typedef {'story'} ReviewScope
30
30
  *
31
31
  * @typedef {'light'|'standard'|'deep'} ReviewDepth
32
32
  *
@@ -39,9 +39,10 @@
39
39
  * lever (Story #3876/#3937/#3938). `light` → single-pass review focused on
40
40
  * Pillar 1 (spec adherence) with Pillars 2–3 reduced to a quick scan;
41
41
  * `standard` → all pillars at baseline depth; `deep` → all pillars plus a
42
- * second adversarial pass over the diff. Resolved from the judged risk
43
- * envelope AND the changed-file count of the diff by `resolveDepth`
44
- * (`lib/orchestration/review-depth.js`) and threaded into `runReview`.
42
+ * second adversarial pass over the diff. Derived from the diff's
43
+ * changed-file count and sensitive-path hits
44
+ * (`lib/orchestration/review-depth.js`; the judged-risk input was
45
+ * retired in #4542) and threaded into `runReview`.
45
46
  * LLM-backed
46
47
  * providers MUST render this into the prompt/instructions they emit; the
47
48
  * native provider documents why its mechanical sweep ignores it. Absent →
@@ -55,7 +55,7 @@ export const ULTRAREVIEW_PROMPT_TEMPLATE =
55
55
  * @returns {string}
56
56
  */
57
57
  export function buildUltrareviewMessage(input) {
58
- const scopeLabel = input?.scope === 'epic' ? 'Epic' : 'Story';
58
+ const scopeLabel = 'Story';
59
59
  const baseRef = typeof input?.baseRef === 'string' ? input.baseRef : '?';
60
60
  const headRef = typeof input?.headRef === 'string' ? input.headRef : '?';
61
61
  return ULTRAREVIEW_PROMPT_TEMPLATE.replace('{scopeLabel}', scopeLabel)