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
@@ -0,0 +1,181 @@
1
+ #!/usr/bin/env node
2
+
3
+ // .agents/scripts/prune-baseline-orphans.js — Story #5012.
4
+ //
5
+ // Delete the baseline rows that are provably inert, and nothing else.
6
+ //
7
+ // This is the cheap remedy that makes `check-baseline-scope.js` fair. A gate
8
+ // that hard-fails on a stale row is only defensible while clearing one costs a
9
+ // command rather than a full re-score: the honest alternative — re-derive the
10
+ // whole baseline — spends a coverage run or a full-tree MI pass to express a
11
+ // deletion, which is why stale rows accumulate instead.
12
+ //
13
+ // Two row classes qualify, both decidable without measuring anything:
14
+ //
15
+ // - `absent` — the row's file is gone from disk.
16
+ // - `out-of-scope` — the file is still there, but the gate's own
17
+ // `targetDirs` / `ignoreGlobs` no longer match it.
18
+ //
19
+ // The pruner never adds a row, never restamps `generatedAt` (a fresh stamp
20
+ // over unmeasured rows is the exact failure an age check exists to catch), and
21
+ // recomputes `rollup` through the kind's own arithmetic so the pruned envelope
22
+ // still validates against its schema. An unreadable scope config degrades to
23
+ // orphan-only pruning rather than treating unknown scope as empty scope.
24
+ //
25
+ // Exit codes:
26
+ // 0 nothing to prune (or, without `--check`, the prune was written)
27
+ // 1 `--check` found rows that would be pruned
28
+ // 2 the pruner could not run
29
+
30
+ // Fail-fast if the framework's runtime deps are not installed — must be the
31
+ // first import so the check runs before any third-party-importing sibling
32
+ // module is evaluated (Story #3432).
33
+ import './lib/runtime-deps/ensure-installed.js';
34
+ import { PRUNABLE_KINDS, runPrune } from './lib/baselines/orphan-pruner.js';
35
+ import { runAsCli } from './lib/cli-utils.js';
36
+ import { getQuality } from './lib/config/quality.js';
37
+ import { resolveConfig } from './lib/config-resolver.js';
38
+
39
+ const EXIT_CLEAN = 0;
40
+ const EXIT_WOULD_PRUNE = 1;
41
+ const EXIT_CANNOT_RUN = 2;
42
+
43
+ const HELP = {
44
+ invocation:
45
+ 'node .agents/scripts/prune-baseline-orphans.js [--check] [--kind <kind>] [--json]',
46
+ summary:
47
+ "Remove baseline rows whose file is gone from disk or has left the gate's scope. Never adds a row, never restamps generatedAt, never re-scores.",
48
+ flags: [
49
+ ['--check', 'Report what would be pruned, write nothing, exit 1 if any.'],
50
+ ['--kind <kind>', 'Prune one kind only (repeatable). Default: all.'],
51
+ ['--json', 'Emit the report as JSON instead of text.'],
52
+ ['--cwd <dir>', 'Repository root to prune. Default: process.cwd().'],
53
+ ],
54
+ notes: [
55
+ 'Exit codes:\n 0 clean, or pruned\n 1 --check found prunable rows\n 2 the pruner could not run',
56
+ ],
57
+ };
58
+
59
+ /**
60
+ * Parse argv into an options bag. An unknown flag is an error, never a silent
61
+ * no-op — a typo'd `--dry-run` must not read as "write the files".
62
+ *
63
+ * @param {string[]} argv
64
+ * @returns {{ kinds: string[], check: boolean, json: boolean, cwd: string }}
65
+ */
66
+ export function parseArgs(argv = []) {
67
+ const out = { kinds: [], check: false, json: false, cwd: null };
68
+ let i = 0;
69
+ while (i < argv.length) {
70
+ const arg = argv[i];
71
+ const value = argv[i + 1];
72
+ i += 1;
73
+ if (arg === '--check') out.check = true;
74
+ else if (arg === '--json') out.json = true;
75
+ else if (arg === '--kind') {
76
+ out.kinds.push(value);
77
+ i += 1;
78
+ } else if (arg === '--cwd') {
79
+ out.cwd = value;
80
+ i += 1;
81
+ } else throw new Error(`unknown flag "${arg}" (try --help)`);
82
+ }
83
+ const unknown = out.kinds.filter((k) => !PRUNABLE_KINDS.includes(k));
84
+ if (unknown.length > 0) {
85
+ throw new Error(
86
+ `unknown --kind ${unknown.join(', ')}; expected one of ${PRUNABLE_KINDS.join(', ')}`,
87
+ );
88
+ }
89
+ if (out.kinds.length === 0) out.kinds = [...PRUNABLE_KINDS];
90
+ out.cwd = out.cwd ?? process.cwd();
91
+ return out;
92
+ }
93
+
94
+ /**
95
+ * Render one kind's outcome as indented text lines.
96
+ *
97
+ * @param {object} entry
98
+ * @param {boolean} check
99
+ * @returns {string[]}
100
+ */
101
+ function renderKind(entry, check) {
102
+ if (!entry.present) return [];
103
+ if (entry.skipped)
104
+ return [` - ${entry.kind}: skipped (${entry.skipReason})`];
105
+ const suffix = entry.degraded
106
+ ? ` [orphan-only: ${entry.degradedReason}]`
107
+ : '';
108
+ if (entry.removed.length === 0) {
109
+ return [` - ${entry.kind}: clean${suffix}`];
110
+ }
111
+ const verb = check ? 'would prune' : 'pruned';
112
+ return [
113
+ ` - ${entry.kind}: ${verb} ${entry.removed.length} row(s)${suffix}`,
114
+ ...entry.removed.map((row) => ` · ${row.reason}: ${row.path}`),
115
+ ];
116
+ }
117
+
118
+ /**
119
+ * Render the whole report as text.
120
+ *
121
+ * @param {object} report
122
+ * @returns {string}
123
+ */
124
+ export function formatReport(report) {
125
+ const verb = report.check ? 'would prune' : 'pruned';
126
+ const lines = [
127
+ `[prune-baseline-orphans] ${verb} ${report.removedCount} row(s) across ` +
128
+ `${report.writtenCount} file(s)`,
129
+ ...report.kinds.flatMap((entry) => renderKind(entry, report.check)),
130
+ ];
131
+ if (report.check && report.removedCount > 0) {
132
+ lines.push(
133
+ '',
134
+ 'Run without --check to write the prune:',
135
+ ' node .agents/scripts/prune-baseline-orphans.js',
136
+ );
137
+ }
138
+ return lines.join('\n');
139
+ }
140
+
141
+ /**
142
+ * CLI entry point. Returns its exit code rather than calling `process.exit()`,
143
+ * so `runAsCli`'s `propagateExitCode` path settles it through `flushStdio` and
144
+ * an unbounded report is not truncated at a pipe boundary (Story #4783).
145
+ *
146
+ * @returns {Promise<number>}
147
+ */
148
+ async function main() {
149
+ let opts;
150
+ let report;
151
+ try {
152
+ opts = parseArgs(process.argv.slice(2));
153
+ const config = resolveConfig({ cwd: opts.cwd });
154
+ report = runPrune({
155
+ cwd: opts.cwd,
156
+ kinds: opts.kinds,
157
+ check: opts.check,
158
+ quality: getQuality(config) ?? { gates: {} },
159
+ });
160
+ } catch (err) {
161
+ const message = err?.message ?? String(err);
162
+ process.stdout.write(
163
+ `${JSON.stringify({ schemaVersion: '1', error: message }, null, 2)}\n`,
164
+ );
165
+ return EXIT_CANNOT_RUN;
166
+ }
167
+ process.stdout.write(
168
+ opts.json
169
+ ? `${JSON.stringify(report, null, 2)}\n`
170
+ : `${formatReport(report)}\n`,
171
+ );
172
+ return report.check && report.removedCount > 0
173
+ ? EXIT_WOULD_PRUNE
174
+ : EXIT_CLEAN;
175
+ }
176
+
177
+ runAsCli(import.meta.url, main, {
178
+ source: 'prune-baseline-orphans',
179
+ usage: HELP,
180
+ propagateExitCode: true,
181
+ });
@@ -11,8 +11,11 @@
11
11
  * What it resolves, per Story:
12
12
  * - the issue itself, fetched with **state=all** so an already-landed
13
13
  * sibling is present rather than silently dropped;
14
- * - its dependency edges: the union of body-parsed `blocked by #N` /
15
- * `depends on #N` and native GitHub `blocked_by` edges;
14
+ * - its dependency edges: the union of the body's `---` footer
15
+ * (`blocked by #N`, footer-scoped and strict prose mentioning a blocker
16
+ * elsewhere in the body declares nothing) and native GitHub `blocked_by`
17
+ * edges, read to exhaustion and failing loud rather than degrading to
18
+ * "no edges";
16
19
  * - its declared file footprint, as plain path strings, so the scheduler's
17
20
  * co-dispatch overlap guard has something to work with.
18
21
  *
@@ -45,7 +48,7 @@ import {
45
48
  } from './lib/orchestration/resolve-stories.js';
46
49
  import { createProvider } from './lib/provider-factory.js';
47
50
  import { concurrentMap } from './lib/util/concurrent-map.js';
48
- import { parseApiJson } from './providers/github/request-helpers.js';
51
+ import { paginateRest } from './providers/github/request-helpers.js';
49
52
 
50
53
  export { buildStoriesEnvelope, parseIds, readNativeBlockedBy, toStoryRecord };
51
54
 
@@ -111,9 +114,21 @@ export async function fetchStories(provider, ids) {
111
114
  /**
112
115
  * Read native blocked_by edges for every Story in the set.
113
116
  *
117
+ * `paginate` is injected rather than imported inside the lib layer so
118
+ * `readNativeBlockedBy` stays provider-agnostic and unit-testable; production
119
+ * passes `paginateRest`, which walks every page (the read used to stop at the
120
+ * first, silently truncating a Story's gates — Story #5046).
121
+ *
114
122
  * @returns {Promise<Map<number, number[]>>}
115
123
  */
116
- export async function readNativeEdges({ provider, stories, owner, repo }) {
124
+ export async function readNativeEdges({
125
+ provider,
126
+ stories,
127
+ owner,
128
+ repo,
129
+ paginate = paginateRest,
130
+ warn = (m) => Logger.warn(m),
131
+ }) {
117
132
  const entries = await concurrentMap(
118
133
  stories,
119
134
  async (story) => [
@@ -123,7 +138,8 @@ export async function readNativeEdges({ provider, stories, owner, repo }) {
123
138
  owner,
124
139
  repo,
125
140
  issueNumber: story.id,
126
- parseJson: parseApiJson,
141
+ paginate,
142
+ warn,
127
143
  }),
128
144
  ],
129
145
  { concurrency: FETCH_CONCURRENCY },
@@ -197,7 +213,6 @@ export async function runResolveStories(
197
213
  stories,
198
214
  nativeEdges,
199
215
  warn: (m) => Logger.warn(m),
200
- config,
201
216
  });
202
217
  const foreignDone = await resolveForeignDone({
203
218
  provider,
@@ -209,7 +224,6 @@ export async function runResolveStories(
209
224
  nativeEdges,
210
225
  foreignDone,
211
226
  warn: () => {},
212
- config,
213
227
  });
214
228
 
215
229
  stdout.write(
@@ -21,6 +21,32 @@ import process from 'node:process';
21
21
  // can spawn directly.
22
22
  const useShell = process.platform === 'win32';
23
23
 
24
+ /**
25
+ * A Node gate spelled `node .agents/scripts/<script>` — the shape six of the
26
+ * eight tasks share. Spelling it once keeps this list readable as what it
27
+ * actually is (a fan-out roster) instead of a wall of spawn tuples, and keeps
28
+ * adding a gate from costing the module a maintainability regression for the
29
+ * boilerplate rather than the behaviour. Mirrors `run-verify.js`'s `gate()`.
30
+ *
31
+ * Use bare `node` (PATH-resolved) rather than `process.execPath`:
32
+ * `process.execPath` on Windows often expands to `C:\Program
33
+ * Files\nodejs\node.exe`, and spawn(..., { shell: true }) does not quote the
34
+ * executable, so the space breaks invocation.
35
+ *
36
+ * The path is passed WHOLE rather than assembled from a basename. Composing
37
+ * it (`` `.agents/scripts/${script}` ``) reads better and is wrong:
38
+ * `check-knip-entries.js` derives which CLIs something actually invokes by
39
+ * scanning this file's source text for literal script paths, and four of the
40
+ * gates below are invoked from nowhere else. Interpolating the path made them
41
+ * invisible to that derivation, so knip called them dead and the ratchet
42
+ * offered to record live gates as expected-dead — the Story #5012 trap.
43
+ *
44
+ * @param {string} name reported in this driver's own error prefix
45
+ * @param {string} script repo-relative path, spelled as one literal
46
+ * @returns {{ name: string, cmd: string, args: string[] }}
47
+ */
48
+ const nodeGate = (name, script) => ({ name, cmd: 'node', args: [script] });
49
+
24
50
  const tasks = [
25
51
  {
26
52
  name: 'biome',
@@ -47,67 +73,41 @@ const tasks = [
47
73
  '!.worktrees/**',
48
74
  ],
49
75
  },
50
- {
51
- // Custom Node-based lint for the lifecycle bus surface (Story #2227).
52
- // Enforces:
53
- // 1. No `Promise.all` over listener arrays under
54
- // `.agents/scripts/lib/orchestration/lifecycle/**`.
55
- // 2. Wildcard-observer firewall: no state-mutating imports in
56
- // listener modules that register on `'*'`.
57
- // Both rules are mandated by Tech Spec #2189 and have no biome
58
- // equivalent.
59
- //
60
- // Use bare `node` (PATH-resolved) rather than `process.execPath`:
61
- // `process.execPath` on Windows often expands to `C:\Program
62
- // Files\nodejs\node.exe`, and spawn(..., { shell: true }) does not
63
- // quote the executable, so the space breaks invocation.
64
- name: 'lifecycle-lint',
65
- cmd: 'node',
66
- args: ['.agents/scripts/check-lifecycle-lint.js'],
67
- },
68
- {
69
- // Custom Node-based lint for the workflow prose surface (Epic #4474
70
- // PR5). Enforces: no workflow may instruct calling an exported
71
- // library function that has no CLI entrypoint the measured
72
- // shim-writing failure mode the /plan collapse killed. See
73
- // check-workflow-cli-lint.js for the paragraph-level heuristic.
74
- name: 'workflow-cli-lint',
75
- cmd: 'node',
76
- args: ['.agents/scripts/check-workflow-cli-lint.js'],
77
- },
78
- {
79
- // Custom Node-based lint for label-vocabulary citations in
80
- // `.agents/docs/SDLC.md` and `.agents/workflows/**/*.md` (Story #2892,
81
- // Tech Spec F9 under Epic #2880). Greps inline backtick code
82
- // spans for axis-shaped tokens (`type/epic`, etc.) and asserts
83
- // only the canonical `<axis>::<value>` separator from
84
- // `lib/label-constants.js` appears. Closes the drift gap that
85
- // let the original `type/epic` typo land in a since-retired
86
- // planning workflow helper.
87
- name: 'label-vocabulary',
88
- cmd: 'node',
89
- args: ['.agents/scripts/lint-label-vocabulary.js'],
90
- },
91
- {
92
- // GitHub Actions job-timeout gate (Story #4936). Enumerates every
93
- // `jobs.<id>` key across `.github/workflows/*.yml` and fails when one
94
- // sets no `timeout-minutes` (inheriting GitHub's 360-minute default) or
95
- // sets one above the ceiling. A deadlocked Windows job burned 44 minutes
96
- // of a runner and withheld the failing required check's logs for the
97
- // whole time; nothing but this check would have caught the gap.
98
- name: 'workflow-timeouts',
99
- cmd: 'node',
100
- args: ['.agents/scripts/check-workflow-timeouts.js'],
101
- },
102
- {
103
- // Architecture cycle ratchet (Story #3991). Detects directed import
104
- // cycles under `.agents/scripts/` and fails on any cycle not in the
105
- // committed allowlist (`baselines/arch-cycles.json`). Mirrors the
106
- // ratchet-down semantics of `check-dead-exports.js`.
107
- name: 'arch-cycles',
108
- cmd: 'node',
109
- args: ['.agents/scripts/check-arch-cycles.js'],
110
- },
76
+ // Lifecycle surface (Story #2227). Enforces (1) no `Promise.all` under
77
+ // `.agents/scripts/lib/orchestration/lifecycle/**` the ledger is
78
+ // append-only and a parallel write interleaves records; and (2) the
79
+ // auto-merge lockout: the `gh pr merge` literal appears only in
80
+ // `single-story-close/phases/auto-merge.js`. Neither has a biome
81
+ // equivalent. Story #5024 dropped the third rule (the wildcard-observer
82
+ // firewall) with the bus and the `listeners/` directory its predicate
83
+ // required.
84
+ nodeGate('lifecycle-lint', '.agents/scripts/check-lifecycle-lint.js'),
85
+ // Workflow prose surface (Epic #4474 PR5). No workflow may instruct calling
86
+ // an exported library function that has no CLI entrypoint — the measured
87
+ // shim-writing failure mode the /plan collapse killed. See
88
+ // check-workflow-cli-lint.js for the paragraph-level heuristic.
89
+ nodeGate('workflow-cli-lint', '.agents/scripts/check-workflow-cli-lint.js'),
90
+ // Label-vocabulary citations in `.agents/docs/SDLC.md` and
91
+ // `.agents/workflows/**/*.md` (Story #2892). Greps inline backtick code
92
+ // spans for axis-shaped tokens (`type/epic`, etc.) and asserts only the
93
+ // canonical `<axis>::<value>` separator from `lib/label-constants.js`
94
+ // appears. Closes the drift gap that let the original `type/epic` typo land.
95
+ nodeGate('label-vocabulary', '.agents/scripts/lint-label-vocabulary.js'),
96
+ // GitHub Actions job-timeout gate (Story #4936). Fails a `jobs.<id>` that
97
+ // sets no `timeout-minutes` (inheriting GitHub's 360-minute default) or sets
98
+ // one above the ceiling. A deadlocked Windows job burned 44 minutes of a
99
+ // runner and withheld the failing required check's logs for the whole time.
100
+ nodeGate('workflow-timeouts', '.agents/scripts/check-workflow-timeouts.js'),
101
+ // Architecture cycle ratchet (Story #3991). Detects directed import cycles
102
+ // under `.agents/scripts/` and fails on any cycle not in the committed
103
+ // allowlist (`baselines/arch-cycles.json`).
104
+ nodeGate('arch-cycles', '.agents/scripts/check-arch-cycles.js'),
105
+ // Static Gherkin corpus gate (Story #5013): must-compile with the real
106
+ // parser, then must-bind scoped per step root. Opt-in behind
107
+ // `qa.gherkinLint`, so it exits 0 as "not configured" in this repo. Here
108
+ // rather than in run-verify.js so it reaches CI through the same `lint`
109
+ // required check as arch-cycles; listing it in both would double-pay it.
110
+ nodeGate('gherkin-corpus', '.agents/scripts/check-gherkin-corpus.js'),
111
111
  ];
112
112
 
113
113
  function runTask({ name, cmd, args }) {
@@ -8,10 +8,10 @@
8
8
  * runner only — set SKIP_PREFLIGHT=0 to include preflight if desired.
9
9
  */
10
10
 
11
- import { spawnSync } from 'node:child_process';
12
11
  import fs from 'node:fs';
13
12
  import path from 'node:path';
14
13
  import { fileURLToPath } from 'node:url';
14
+ import { spawnChild } from './lib/child-exec.js';
15
15
  import { runAsCli } from './lib/cli-utils.js';
16
16
  import { buildWebhookSafeTestEnv } from './lib/test-env.js';
17
17
  import { parseTapOutput } from './lib/test-profile/parse-tap.js';
@@ -56,13 +56,14 @@ export function parseProfileArgv(argv) {
56
56
  * @param {object} [opts]
57
57
  * @param {string[]} [opts.argv]
58
58
  * @param {string} [opts.cwd]
59
- * @param {typeof spawnSync} [opts.spawn]
59
+ * @param {Function} [opts.spawn] - Injected child runner; defaults to the
60
+ * shared surface's real `spawnSync`.
60
61
  * @param {typeof fs} [opts.fs]
61
62
  */
62
63
  export function runTestProfile({
63
64
  argv = process.argv.slice(2),
64
65
  cwd = ROOT,
65
- spawn = spawnSync,
66
+ spawn,
66
67
  fs: fsLike = fs,
67
68
  } = {}) {
68
69
  const { outDir, topN, testArgv } = parseProfileArgv(argv);
@@ -75,7 +76,8 @@ export function runTestProfile({
75
76
  ];
76
77
 
77
78
  const started = Date.now();
78
- const result = spawn(process.execPath, nodeArgs, {
79
+ const result = spawnChild(process.execPath, nodeArgs, {
80
+ run: spawn,
79
81
  cwd,
80
82
  encoding: 'utf8',
81
83
  env: {
@@ -84,8 +86,6 @@ export function runTestProfile({
84
86
  FORCE_COLOR: '0',
85
87
  NO_COLOR: '1',
86
88
  },
87
- maxBuffer: 64 * 1024 * 1024,
88
- shell: false,
89
89
  });
90
90
  const wallDurationMs = Date.now() - started;
91
91
 
@@ -6,8 +6,8 @@
6
6
  * locally, without epic-scoped MI projection or push semantics.
7
7
  *
8
8
  * Order: audit (SCA) → lint (includes docs:check + the arch-cycles ratchet) →
9
- * full test suite → unified baselines → the dead-exports and context-budget
10
- * ratchets.
9
+ * full test suite → unified baselines → the standalone ratchets
10
+ * (dead-exports ×2, context-budget, cyclomatic, schema-references).
11
11
  *
12
12
  * The `audit` step runs `npm audit --audit-level=high`, matching CI's
13
13
  * "Dependency Vulnerability Audit (SCA)" gate so a local green no longer hides
@@ -27,42 +27,60 @@
27
27
  * full-tree scan in check-dead-exports.js) to a command that already carries
28
28
  * the full test suite.
29
29
  *
30
- * A handful of CI gates cannot be reproduced by this command (action pinning,
31
- * TruffleHog secret scan, the BASELINE_SCOPE=full push-scoped maintainability
32
- * run) those are catalogued in docs/ci-contract.md.
30
+ * Story #5004 closed the last two mirror gaps that were pure omission.
31
+ * `check-cyclomatic.js` (#4923) and `check-schema-references.js` (#4938) were
32
+ * each added to the `baselines` job's standalone-ratchet slot without ever
33
+ * being added here, so a green `verify` still hid both. Like their neighbours
34
+ * they are pure-Node and cost milliseconds.
35
+ *
36
+ * Still NOT mirrored: `check-workflow-citations.js` and
37
+ * `check-baseline-scope.js` run in CI's `baselines` job only —
38
+ * `.agents/rules/known-tooling-behavior.md` entry 2 carries the current
39
+ * coverage table. (`prune-baseline-orphans.js --check` used to sit in that
40
+ * list; it no longer runs in CI at all — the un-attributed duplicate of the
41
+ * scope gate's `extra` direction reds every open PR on inherited rows.) Nor are the CI gates this command structurally cannot
42
+ * reproduce (action pinning, TruffleHog secret scan) — those are catalogued
43
+ * in docs/ci-contract.md. The nightly full-scope re-score
44
+ * (.github/workflows/baseline-drift.yml) is deliberately outside this
45
+ * mirror too: it re-scores the whole tree, which is the cost `verify` exists
46
+ * to avoid paying on every run.
33
47
  */
34
48
 
35
49
  import { spawnSync } from 'node:child_process';
36
50
  import { runAsCli } from './lib/cli-utils.js';
37
51
 
52
+ /**
53
+ * A gate step: `node .agents/scripts/<script>` plus any extra args. Seven of
54
+ * the ten steps share exactly that shape, so spelling it once leaves the list
55
+ * below readable as what it actually is — a gate *order* — instead of a wall
56
+ * of spawn tuples.
57
+ *
58
+ * @param {string} label reported as `failedStep` when the gate exits non-zero
59
+ * @param {string} script basename under `.agents/scripts/`
60
+ * @param {...string} args extra CLI args
61
+ * @returns {{ label: string, cmd: string, args: string[] }}
62
+ */
63
+ const gate = (label, script, ...args) => ({
64
+ label,
65
+ cmd: 'node',
66
+ args: [`.agents/scripts/${script}`, ...args],
67
+ });
68
+
38
69
  const STEPS = [
39
- {
40
- label: 'audit',
41
- cmd: 'npm',
42
- args: ['audit', '--audit-level=high'],
43
- },
70
+ { label: 'audit', cmd: 'npm', args: ['audit', '--audit-level=high'] },
44
71
  { label: 'lint', cmd: 'npm', args: ['run', 'lint'] },
45
72
  { label: 'test', cmd: 'npm', args: ['test'] },
46
- {
47
- label: 'baselines',
48
- cmd: 'node',
49
- args: ['.agents/scripts/check-baselines.js'],
50
- },
51
- {
52
- label: 'dead-exports',
53
- cmd: 'node',
54
- args: ['.agents/scripts/check-dead-exports.js'],
55
- },
56
- {
57
- label: 'dead-exports-production',
58
- cmd: 'node',
59
- args: ['.agents/scripts/check-dead-exports.js', '--production'],
60
- },
61
- {
62
- label: 'context-budget',
63
- cmd: 'node',
64
- args: ['.agents/scripts/check-context-budget.js'],
65
- },
73
+ gate('baselines', 'check-baselines.js'),
74
+ // Ordered ahead of the dead-exports pair deliberately. When a new CLI is
75
+ // missing from knip.json's entry list, both gates fail — but only this one
76
+ // names the cause. Seeing the ratchet's whole-file diff first is what made
77
+ // "accept the diff" look like the fix during Story #5012.
78
+ gate('knip-entries', 'check-knip-entries.js'),
79
+ gate('dead-exports', 'check-dead-exports.js'),
80
+ gate('dead-exports-production', 'check-dead-exports.js', '--production'),
81
+ gate('context-budget', 'check-context-budget.js'),
82
+ gate('cyclomatic', 'check-cyclomatic.js'),
83
+ gate('schema-references', 'check-schema-references.js'),
66
84
  ];
67
85
 
68
86
  export function runVerifySteps({
@@ -44,6 +44,18 @@
44
44
  * [--no-auto-merge]
45
45
  * [--wait-merge | --no-wait-merge]
46
46
  * [--merge-watch-mode <sync|async>]
47
+ * [--override-review-block <reason>]
48
+ *
49
+ * `--override-review-block <reason>` is the one sanctioned way
50
+ * past a code-review CRITICAL blocker. A critical finding halts this script
51
+ * before auto-merge, and until this flag existed there was no override at all —
52
+ * so an operator who had read a finding and judged it wrong could only land by
53
+ * running `gh pr merge` themselves, bypassing the gate with nothing written
54
+ * down. The flag does not weaken the gate; it moves that escape hatch into a
55
+ * mandatory-reason audit trail (Story comment + PR comment + a
56
+ * `review-block-overridden` friction signal) and reports
57
+ * `gates.codeReview: "overridden"` on the terminal envelope. A bare or
58
+ * too-short reason fails during option parsing, before any phase runs.
47
59
  *
48
60
  * `--merge-watch-mode` (Story #4949) overrides `delivery.mergeWatch.mode` for
49
61
  * one invocation, on the same explicit-wins-over-config precedence
@@ -217,6 +229,14 @@ runAsCli(import.meta.url, main, {
217
229
  '--merge-watch-mode <sync|async>',
218
230
  'Override delivery.mergeWatch.mode for this invocation only. `async` caps the merge wait to a short probe window and returns the resumable `pending` terminal instead of holding the foreground slot — pass it on every close of a multi-Story run. An invalid value exits non-zero before any phase runs.',
219
231
  ],
232
+ [
233
+ '--override-review-block <reason>',
234
+ // Deliberately does not spell the merge CLI invocation: the
235
+ // merge-lockout rule in `check-lifecycle-lint.js` forbids that literal
236
+ // in any string outside `phases/auto-merge.js`, and it is right to —
237
+ // the point of this flag is that arming stays on the one code path.
238
+ 'Land despite a Story-scope code-review CRITICAL blocker you have reviewed and judged wrong. The reason is mandatory (≥12 chars) and is recorded on the Story, on the PR, and as a `review-block-overridden` friction signal; the terminal envelope reports `gates.codeReview: "overridden"`. Use this instead of merging the PR by hand with the GitHub CLI — a hand-merge bypasses the gate with no record at all.',
239
+ ],
220
240
  ['--no-evidence', 'Do not reuse or write gate evidence stamps.'],
221
241
  ['--dry-run', 'Report the plan; mutate nothing.'],
222
242
  ],