@event4u/agent-config 9.16.0 → 9.18.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 (227) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/AGENTS.md +1 -1
  3. package/CHANGELOG.md +80 -130
  4. package/README.md +1 -1
  5. package/dist/agent-src/commands/agent-handoff.md +100 -48
  6. package/dist/agent-src/commands/challenge-me/vision.md +5 -0
  7. package/dist/agent-src/commands/commit.md +1 -1
  8. package/dist/agent-src/commands/condense.md +19 -64
  9. package/dist/agent-src/commands/evals/agent-handoff.json +13 -1
  10. package/dist/agent-src/commands/evals/rule-compliance-audit.json +46 -0
  11. package/dist/agent-src/commands/feature/plan.md +20 -0
  12. package/dist/agent-src/commands/feature/roadmap.md +19 -0
  13. package/dist/agent-src/commands/memory/promote.md +14 -1
  14. package/dist/agent-src/commands/mode.md +9 -5
  15. package/dist/agent-src/commands/optimize/deep.md +72 -11
  16. package/dist/agent-src/commands/pr/create.md +29 -0
  17. package/dist/agent-src/commands/roadmap/create.md +29 -0
  18. package/dist/agent-src/commands/rule-compliance-audit.md +28 -5
  19. package/dist/agent-src/contexts/authority/commit-mechanics.md +5 -5
  20. package/dist/agent-src/contexts/communication/rules-auto/guidelines-mechanics.md +1 -0
  21. package/dist/agent-src/contexts/communication/rules-auto/skill-quality-mechanics.md +1 -1
  22. package/dist/agent-src/contexts/communication/rules-auto/think-before-action-mechanics.md +6 -1
  23. package/dist/agent-src/contexts/communication/rules-auto/token-efficiency-mechanics.md +4 -1
  24. package/dist/agent-src/contexts/execution/autonomy-mechanics.md +27 -1
  25. package/dist/agent-src/contexts/execution/plan-confidence-gate.md +133 -0
  26. package/dist/agent-src/contexts/execution/roadmap-process-loop.md +5 -2
  27. package/dist/agent-src/profiles/agency.yml +3 -3
  28. package/dist/agent-src/profiles/content_creator.yml +2 -2
  29. package/dist/agent-src/profiles/developer.yml +2 -2
  30. package/dist/agent-src/profiles/finance.yml +1 -1
  31. package/dist/agent-src/profiles/founder.yml +2 -2
  32. package/dist/agent-src/profiles/ops.yml +2 -2
  33. package/dist/agent-src/rules/active-remediation.md +2 -0
  34. package/dist/agent-src/rules/architecture.md +4 -2
  35. package/dist/agent-src/rules/augment-edit-discipline.md +2 -0
  36. package/dist/agent-src/rules/autonomous-execution.md +1 -1
  37. package/dist/agent-src/rules/brand-consistency.md +10 -62
  38. package/dist/agent-src/rules/brand-source-of-truth.md +62 -11
  39. package/dist/agent-src/rules/broken-access-control.md +4 -0
  40. package/dist/agent-src/rules/cli-output-handling.md +5 -0
  41. package/dist/agent-src/rules/code-comment-discipline.md +3 -0
  42. package/dist/agent-src/rules/commit-conventions.md +2 -0
  43. package/dist/agent-src/rules/context-hygiene.md +3 -2
  44. package/dist/agent-src/rules/cross-source-consistency.md +3 -0
  45. package/dist/agent-src/rules/design-fidelity.md +2 -0
  46. package/dist/agent-src/rules/doc-screenshot-hygiene.md +2 -0
  47. package/dist/agent-src/rules/docker-commands.md +4 -0
  48. package/dist/agent-src/rules/domain-adoption-policy.md +2 -0
  49. package/dist/agent-src/rules/domain-safety-disclaimer.md +5 -2
  50. package/dist/agent-src/rules/domain-safety-pii.md +1 -1
  51. package/dist/agent-src/rules/engineering-safety-floor.md +2 -0
  52. package/dist/agent-src/rules/framework-neutrality-in-generic-skills.md +9 -0
  53. package/dist/agent-src/rules/image-likeness-and-rights.md +4 -0
  54. package/dist/agent-src/rules/improve-before-implement.md +4 -0
  55. package/dist/agent-src/rules/laravel-routing.md +4 -0
  56. package/dist/agent-src/rules/legal-safety-floor.md +3 -0
  57. package/dist/agent-src/rules/lethal-trifecta-guard.md +3 -0
  58. package/dist/agent-src/rules/media-governance-routing.md +5 -0
  59. package/dist/agent-src/rules/minimal-safe-diff.md +2 -0
  60. package/dist/agent-src/rules/no-roadmap-references.md +2 -0
  61. package/dist/agent-src/rules/notes-first-reasoning.md +2 -0
  62. package/dist/agent-src/rules/output-discipline.md +9 -2
  63. package/dist/agent-src/rules/php-coding.md +2 -0
  64. package/dist/agent-src/rules/prefer-enums-over-literals.md +3 -0
  65. package/dist/agent-src/rules/provider-lifecycle-discipline.md +5 -0
  66. package/dist/agent-src/rules/roadmap-ci-steps-policy.md +2 -0
  67. package/dist/agent-src/rules/roadmap-progress-sync.md +2 -0
  68. package/dist/agent-src/rules/rule-type-governance.md +2 -0
  69. package/dist/agent-src/rules/scale-discipline.md +3 -0
  70. package/dist/agent-src/rules/secret-vcs-guard.md +2 -0
  71. package/dist/agent-src/rules/security-sensitive-stop.md +9 -2
  72. package/dist/agent-src/rules/senior-engineering-discipline.md +6 -0
  73. package/dist/agent-src/rules/skill-quality.md +2 -0
  74. package/dist/agent-src/rules/source-confidentiality.md +4 -0
  75. package/dist/agent-src/rules/source-discovery-gate.md +3 -0
  76. package/dist/agent-src/rules/source-of-truth.md +2 -0
  77. package/dist/agent-src/rules/strategy-safety-floor.md +2 -0
  78. package/dist/agent-src/rules/think-before-action.md +5 -0
  79. package/dist/agent-src/rules/token-budget-discipline.md +2 -2
  80. package/dist/agent-src/rules/ui-audit-gate.md +2 -0
  81. package/dist/agent-src/rules/user-interaction.md +3 -4
  82. package/dist/agent-src/skills/build-buy-partner/SKILL.md +1 -1
  83. package/dist/agent-src/skills/copilot-agents-optimization/SKILL.md +4 -4
  84. package/dist/agent-src/skills/copilot-config/SKILL.md +1 -1
  85. package/dist/agent-src/skills/deep-reading-analyst/SKILL.md +7 -4
  86. package/dist/agent-src/skills/description-assist/SKILL.md +1 -1
  87. package/dist/agent-src/skills/design-intelligence/SKILL.md +6 -180
  88. package/dist/agent-src/skills/design-intelligence/references/context-and-registers.md +100 -0
  89. package/dist/agent-src/skills/design-intelligence/references/integration-mapping.md +91 -0
  90. package/dist/agent-src/skills/design-review/SKILL.md +4 -141
  91. package/dist/agent-src/skills/design-review/references/review-communication.md +92 -0
  92. package/dist/agent-src/skills/design-review/references/verification-automation.md +61 -0
  93. package/dist/agent-src/skills/existing-ui-audit/SKILL.md +8 -27
  94. package/dist/agent-src/skills/existing-ui-audit/references/anti-slop-cross-reference.md +31 -0
  95. package/dist/agent-src/skills/fe-design/SKILL.md +4 -289
  96. package/dist/agent-src/skills/fe-design/references/design-patterns.md +227 -0
  97. package/dist/agent-src/skills/fe-design/references/design-read-and-memory.md +75 -0
  98. package/dist/agent-src/skills/market-entry-analysis/SKILL.md +1 -1
  99. package/dist/agent-src/skills/persona-writing/SKILL.md +2 -2
  100. package/dist/agent-src/skills/readme-reviewer/SKILL.md +2 -1
  101. package/dist/agent-src/skills/roadmap-management/SKILL.md +10 -0
  102. package/dist/agent-src/skills/roadmap-writing/SKILL.md +12 -1
  103. package/dist/agent-src/skills/rule-writing/SKILL.md +4 -2
  104. package/dist/agent-src/skills/skill-reviewer/SKILL.md +1 -1
  105. package/dist/agent-src/skills/skill-writing/SKILL.md +2 -2
  106. package/dist/agent-src/skills/tailwind-engineer/SKILL.md +3 -1
  107. package/dist/agent-src/skills/token-optimizer/SKILL.md +2 -2
  108. package/dist/agent-src/skills/ui-component-architect/SKILL.md +6 -3
  109. package/dist/agent-src/templates/agent-settings.md +13 -15
  110. package/dist/agent-src/templates/agents/agent-project-settings.example.yml +6 -2
  111. package/dist/agent-src/templates/persona.md +2 -2
  112. package/dist/agent-src/templates/roadmaps.md +13 -1
  113. package/dist/agent-src/templates/rule.md +1 -1
  114. package/dist/agent-src/templates/scripts/work_engine/directives/ui/_fix_lane.ts +52 -0
  115. package/dist/agent-src/templates/scripts/work_engine/directives/ui/audit.ts +8 -0
  116. package/dist/agent-src/templates/scripts/work_engine/directives/ui/design.ts +6 -0
  117. package/dist/agent-src/templates/scripts/work_engine/intent/classify.ts +56 -4
  118. package/dist/agent-src/templates/skill.md +1 -1
  119. package/dist/cli/registry.js +3 -0
  120. package/dist/cli/registry.js.map +1 -1
  121. package/dist/cli-delegate/chunk-AIIUU73N.js +2040 -0
  122. package/dist/cli-delegate/{chunk-ZXXJLL5K.js → chunk-BSLUHV2F.js} +127 -1487
  123. package/dist/cli-delegate/{chunk-H574KOUH.js → chunk-ELMFOQPW.js} +35 -1
  124. package/dist/cli-delegate/chunk-LF2KKJ4Q.js +183 -0
  125. package/dist/cli-delegate/chunk-VSYT7OHO.js +1382 -0
  126. package/dist/cli-delegate/chunk-XGA2NTU4.js +3727 -0
  127. package/dist/cli-delegate/cmd_conformance.js +4 -3
  128. package/dist/cli-delegate/cmd_converge.js +3 -3
  129. package/dist/cli-delegate/cmd_doctor.js +3 -2
  130. package/dist/cli-delegate/cmd_explain.js +36 -36
  131. package/dist/cli-delegate/cmd_export.js +4 -4
  132. package/dist/cli-delegate/cmd_handoff.js +1169 -0
  133. package/dist/cli-delegate/cmd_memory_get.js +35 -3754
  134. package/dist/cli-delegate/cmd_migrate.js +2 -2
  135. package/dist/cli-delegate/cmd_prune.js +4 -4
  136. package/dist/cli-delegate/cmd_route_audit.js +249 -0
  137. package/dist/cli-delegate/cmd_route_explain.js +285 -0
  138. package/dist/cli-delegate/cmd_settings_migrate.js +4 -4
  139. package/dist/cli-delegate/cmd_sync.js +4 -4
  140. package/dist/cli-delegate/cmd_uninstall.js +4 -4
  141. package/dist/cli-delegate/cmd_update.js +3 -3
  142. package/dist/cli-delegate/cmd_upgrade.js +3 -3
  143. package/dist/cli-delegate/cmd_validate.js +4 -4
  144. package/dist/cli-delegate/cmd_versions.js +3 -3
  145. package/dist/discovery/deprecation-report.md +1 -1
  146. package/dist/discovery/discovery-manifest.json +84 -84
  147. package/dist/discovery/discovery-manifest.json.sha256 +1 -1
  148. package/dist/discovery/discovery-manifest.summary.md +1 -1
  149. package/dist/discovery/orphan-report.md +1 -1
  150. package/dist/discovery/packs.json +2 -2
  151. package/dist/discovery/trust-report.md +1 -1
  152. package/dist/discovery/workspaces.json +2 -2
  153. package/dist/hooks/dispatch.js +602 -507
  154. package/dist/install/install.mjs +34 -0
  155. package/dist/mcp/awesome-mcp-servers.row.md +1 -1
  156. package/dist/mcp/mcp-cloudflare-catalogue.json +2 -2
  157. package/dist/mcp/registry-manifest.json +4 -4
  158. package/dist/mcp/server.json +2 -2
  159. package/dist/mcp/server.mjs +25 -3
  160. package/dist/router.json +1 -1
  161. package/dist/scripts/_lib/agent_src.js +39 -6
  162. package/dist/scripts/_lib/agent_src.js.map +1 -1
  163. package/dist/server/schemas/settings.js +25 -0
  164. package/dist/server/schemas/settings.js.map +1 -1
  165. package/docs/guidelines/abstraction-thresholds.md +53 -0
  166. package/docs/guidelines/agent-infra/agent-interaction-and-decision-quality.md +1 -1
  167. package/docs/guidelines/agent-infra/asking-and-brevity-examples.md +7 -0
  168. package/docs/guidelines/agent-infra/context-hygiene-mechanics.md +2 -2
  169. package/docs/guidelines/agent-infra/minimal-safe-diff-mechanics.md +5 -3
  170. package/docs/guidelines/agent-infra/size-and-scope.md +8 -2
  171. package/docs/guidelines/component-oriented-and-oop-development.md +11 -6
  172. package/docs/guidelines/docs/readme-size-and-splitting.md +5 -2
  173. package/llms.txt +1 -0
  174. package/package.json +1 -1
  175. package/src/agent-src/templates/scripts/work_engine/directives/ui/_fix_lane.ts +52 -0
  176. package/src/agent-src/templates/scripts/work_engine/directives/ui/audit.ts +8 -0
  177. package/src/agent-src/templates/scripts/work_engine/directives/ui/design.ts +6 -0
  178. package/src/agent-src/templates/scripts/work_engine/intent/classify.ts +56 -4
  179. package/src/config/agent-settings.template.yml +68 -0
  180. package/src/config/evaluator-budgets.json +8 -11
  181. package/src/config/gate-coverage.yml +70 -0
  182. package/src/scripts/_cli/cmd_explain.ts +41 -36
  183. package/src/scripts/_cli/cmd_handoff.ts +405 -0
  184. package/src/scripts/_cli/cmd_route_audit.ts +290 -0
  185. package/src/scripts/_cli/cmd_route_explain.ts +269 -0
  186. package/src/scripts/_cli/handoff_generate.ts +453 -0
  187. package/src/scripts/_cli/handoff_sessions.ts +435 -0
  188. package/src/scripts/_dispatch.bash +32 -0
  189. package/src/scripts/_lib/agent_src.ts +36 -6
  190. package/src/scripts/_lib/git_env.ts +40 -0
  191. package/src/scripts/_lib/md_table.ts +27 -0
  192. package/src/scripts/_lib/planning_settings.ts +43 -0
  193. package/src/scripts/_lib/release_material.ts +156 -0
  194. package/src/scripts/_lib/router_match.ts +301 -0
  195. package/src/scripts/annotate_r1_outcomes.ts +258 -0
  196. package/src/scripts/check_completion_review.ts +1140 -0
  197. package/src/scripts/check_condensation.ts +2 -1
  198. package/src/scripts/check_finding_dispositions.ts +278 -0
  199. package/src/scripts/check_pack_size.ts +4 -3
  200. package/src/scripts/check_references.ts +1 -0
  201. package/src/scripts/check_release_highlights.ts +300 -0
  202. package/src/scripts/check_release_pr_shape.ts +4 -0
  203. package/src/scripts/check_release_surface_equality.ts +132 -0
  204. package/src/scripts/check_reply_consistency.ts +187 -138
  205. package/src/scripts/compile_router.ts +20 -3
  206. package/src/scripts/dispatch_r2_reviewer.ts +968 -0
  207. package/src/scripts/eval_ui_triviality.ts +156 -0
  208. package/src/scripts/handoff_context_hook.ts +148 -0
  209. package/src/scripts/hook_manifest.yaml +17 -7
  210. package/src/scripts/hooks/concern_registry.ts +2 -0
  211. package/src/scripts/lint_abstraction_thresholds.ts +327 -0
  212. package/src/scripts/lint_framework_leakage_allowlist.json +2 -2
  213. package/src/scripts/lint_plan_risk_register.ts +931 -0
  214. package/src/scripts/lint_profile_personas.ts +153 -0
  215. package/src/scripts/lint_token_budget_discipline.ts +148 -0
  216. package/src/scripts/lint_trigger_collisions.ts +257 -0
  217. package/src/scripts/release.ts +156 -32
  218. package/src/scripts/release_drill.ts +491 -0
  219. package/src/scripts/router_telemetry.ts +16 -290
  220. package/src/scripts/schemas/command.schema.json +33 -0
  221. package/src/scripts/schemas/rule.schema.json +10 -0
  222. package/src/scripts/self_review_gate.ts +41 -3
  223. package/src/scripts/skill_linter.ts +3 -1
  224. package/src/scripts/smoke/router.sh +1 -1
  225. package/src/scripts/update_counts.ts +60 -12
  226. package/src/server/schemas/settings.ts +41 -0
  227. package/dist/cli-delegate/{chunk-XX6TOPD7.js → chunk-WR6GTRHJ.js} +3 -3
@@ -118,6 +118,12 @@ import {
118
118
  perform_split,
119
119
  plan_split,
120
120
  } from './_lib/changelog_eras.js';
121
+ import {
122
+ extract_changelog_section,
123
+ pr_body_from_section,
124
+ release_notes_from_section,
125
+ tag_message_from_section,
126
+ } from './_lib/release_material.js';
121
127
 
122
128
  // `__doc__.splitlines()[0]` in `_parse_args` — the argparse description. Kept
123
129
  // as a referenceable constant so the first docstring line is preserved exactly.
@@ -517,6 +523,22 @@ interface RunResult {
517
523
  * stderr so release preflight failures are diagnosable without re-running with
518
524
  * a debugger.
519
525
  */
526
+ /**
527
+ * Drill/test seam — when set, every external command run() would spawn is
528
+ * answered by this function instead. The check/die/CalledProcessError
529
+ * semantics of run() still apply to the simulated result, so the drill
530
+ * exercises the REAL error paths of the orchestration (release_drill.ts).
531
+ */
532
+ type ExecOverride = (args: readonly string[]) => {
533
+ status: number;
534
+ stdout: string;
535
+ stderr: string;
536
+ };
537
+ let _exec_override: ExecOverride | null = null;
538
+ function _set_exec_override(fn: ExecOverride | null): void {
539
+ _exec_override = fn;
540
+ }
541
+
520
542
  function run(
521
543
  args: readonly string[],
522
544
  opts: { check?: boolean; capture?: boolean; cwd?: string | null } = {},
@@ -526,13 +548,15 @@ function run(
526
548
  const cwd = opts.cwd ?? REPO_ROOT;
527
549
 
528
550
  const [cmd, ...rest] = args;
529
- const res = spawnSync(cmd as string, rest, {
530
- cwd,
531
- encoding: 'utf-8',
532
- // capture_output=True → pipe; else inherit so child writes straight to
533
- // this process's stdout/stderr (text mode, matching subprocess text=True).
534
- stdio: capture ? ['ignore', 'pipe', 'pipe'] : ['inherit', 'inherit', 'inherit'],
535
- });
551
+ const res = _exec_override
552
+ ? { ..._exec_override(args), error: undefined as Error | undefined }
553
+ : spawnSync(cmd as string, rest, {
554
+ cwd,
555
+ encoding: 'utf-8',
556
+ // capture_output=True → pipe; else inherit so child writes straight to
557
+ // this process's stdout/stderr (text mode, matching subprocess text=True).
558
+ stdio: capture ? ['ignore', 'pipe', 'pipe'] : ['inherit', 'inherit', 'inherit'],
559
+ });
536
560
 
537
561
  if (res.error) {
538
562
  // FileNotFoundError analogue (ENOENT) and other spawn failures — Python
@@ -619,6 +643,31 @@ export function _failed_checks_report(names: readonly string[]): string {
619
643
  'empty (docs/contracts/release-pr-gating.md § Mid-release fixes).',
620
644
  );
621
645
  }
646
+ if (names.includes('PR body equals CHANGELOG entry')) {
647
+ lines.push(
648
+ '',
649
+ 'Surface equality: the PR body drifted from the CHANGELOG entry. A',
650
+ 'resume run refreshes it from the branch head (release-truth Phase 1);',
651
+ 'never hand-edit the PR body away from the changelog section.',
652
+ );
653
+ }
654
+ if (names.includes('Curated highlights plausible against the span')) {
655
+ lines.push(
656
+ '',
657
+ 'Highlights: a curated `_none_` field contradicts the release span.',
658
+ 'Fill the curated head in CHANGELOG.md on the release branch — the',
659
+ 'gate output lists the deriving commits (release-truth Phase 2).',
660
+ );
661
+ }
662
+ if (names.includes('Blocking review findings dispositioned')) {
663
+ lines.push(
664
+ '',
665
+ 'Dispositions: a blocking/high self-review finding has no committed',
666
+ 'disposition. Ingest + adjudicate it in',
667
+ 'agents/evidence/release-findings/<version>.json (release-truth Phase 3):',
668
+ ' ./scripts-run src/scripts/check_finding_dispositions --ingest <findings.json>',
669
+ );
670
+ }
622
671
  lines.push('', 'After fixing, resume with: task release -- --resume --yes', '');
623
672
  return lines.join('\n');
624
673
  }
@@ -635,24 +684,24 @@ export function _failed_checks_report(names: readonly string[]): string {
635
684
  * freshly-pushed branch.
636
685
  */
637
686
  function watch_pr_checks(branch: string): void {
638
- // time.sleep(5) — blocking grace period. Never reached on any test path.
639
- const until = Date.now() + 5000;
640
- while (Date.now() < until) {
641
- // busy-wait stand-in for time.sleep(5) without an event-loop yield.
687
+ if (_exec_override === null) {
688
+ // time.sleep(5) — blocking grace period for GitHub to register runs
689
+ // on a freshly-pushed branch. Skipped under the drill seam: there is
690
+ // no GitHub to wait for, and 5s per simulated wait adds up.
691
+ const until = Date.now() + 5000;
692
+ while (Date.now() < until) {
693
+ // busy-wait stand-in for time.sleep(5) without an event-loop yield.
694
+ }
642
695
  }
643
696
  // The branch is passed explicitly — `gh` inferring the PR from HEAD
644
697
  // resolved to `main` mid-run once (2026-08-03, 9.16.0 resume) and the
645
698
  // whole release died on "no pull requests found for branch main".
646
- const proc = spawnSync('gh', ['pr', 'checks', branch, '--watch'], {
647
- cwd: REPO_ROOT,
648
- encoding: 'utf-8',
649
- stdio: ['ignore', 'pipe', 'pipe'],
699
+ const proc = run(['gh', 'pr', 'checks', branch, '--watch'], {
700
+ check: false,
701
+ capture: true,
650
702
  });
651
- if (proc.error) {
652
- throw proc.error;
653
- }
654
703
  const output = ((proc.stdout || '') + (proc.stderr || '')).trim();
655
- const returncode = proc.status ?? 0;
704
+ const returncode = proc.returncode;
656
705
  if (returncode === 0) {
657
706
  if (output) {
658
707
  process.stdout.write(output + '\n');
@@ -670,10 +719,9 @@ function watch_pr_checks(branch: string): void {
670
719
  if (output) {
671
720
  process.stderr.write(output + '\n');
672
721
  }
673
- const summary = spawnSync('gh', ['pr', 'checks', branch, '--json', 'name,bucket'], {
674
- cwd: REPO_ROOT,
675
- encoding: 'utf-8',
676
- stdio: ['ignore', 'pipe', 'pipe'],
722
+ const summary = run(['gh', 'pr', 'checks', branch, '--json', 'name,bucket'], {
723
+ check: false,
724
+ capture: true,
677
725
  });
678
726
  const report = _failed_checks_report(_failed_check_names(summary.stdout ?? ''));
679
727
  if (report) {
@@ -734,11 +782,45 @@ const _MERGE_UPDATE_ROUNDS = 3;
734
782
  * version-bump allowlist (docs/contracts/release-pr-gating.md § Mid-release
735
783
  * fixes) — do it here instead of aborting the run.
736
784
  */
785
+ /**
786
+ * Re-derive the release-PR body from the CHANGELOG section at the current
787
+ * release-branch head and push it to the PR (release-truth Phase 1: one
788
+ * final source — late commits and maintainer edits on the branch must reach
789
+ * the PR body instead of leaving it frozen at plan time). Best-effort sync
790
+ * of the local branch first; a no-op when the derived body is unchanged.
791
+ */
792
+ function _target_from_branch(branch: string): string {
793
+ return branch.replace(/^release\//u, '');
794
+ }
795
+
796
+ function _refresh_pr_body_from_head(branch: string, target: string): void {
797
+ run(['git', 'pull', '--ff-only', REMOTE, branch], { check: false });
798
+ const changelog = fs.readFileSync(CHANGELOG, 'utf-8');
799
+ const section = extract_changelog_section(changelog, target);
800
+ if (!section) {
801
+ die(`CHANGELOG.md at ${branch} head carries no section for ${target} — cannot derive PR body`);
802
+ }
803
+ const capped = _cap_body(section!.body, GH_PR_BODY_LIMIT - 200, '`CHANGELOG.md` in this PR');
804
+ const body = pr_body_from_section(capped, target);
805
+ const live = run(['gh', 'pr', 'view', branch, '--json', 'body', '-q', '.body'], {
806
+ check: false,
807
+ capture: true,
808
+ });
809
+ if (live.returncode === 0 && live.stdout.replace(/\r\n/gu, '\n').trim() === body.trim()) {
810
+ return;
811
+ }
812
+ run(['gh', 'pr', 'edit', branch, '--body', body]);
813
+ }
814
+
737
815
  function merge_release_pr(branch: string, wait_for_checks: boolean): void {
738
816
  if (git(['rev-parse', '--abbrev-ref', 'HEAD'], { capture: true }) !== branch) {
739
817
  run(['git', 'checkout', branch]);
740
818
  }
741
819
  for (let round = 0; round <= _MERGE_UPDATE_ROUNDS; round++) {
820
+ // One final generation step at the final head: every merge attempt
821
+ // first re-derives the PR body from the branch-head CHANGELOG, so
822
+ // update rounds and maintainer edits can never desynchronize it.
823
+ _refresh_pr_body_from_head(branch, _target_from_branch(branch));
742
824
  if (_pr_merge_state(branch) !== 'BEHIND') {
743
825
  const merged = run(['gh', 'pr', 'merge', branch, '--merge', '--delete-branch'], {
744
826
  check: false,
@@ -1708,18 +1790,27 @@ function execute(
1708
1790
  if (pr_merged) {
1709
1791
  _step(5, total, `PR #${pr_info!['number']} already merged — skip`);
1710
1792
  } else if (resume && pr_state === 'OPEN') {
1711
- _step(5, total, `PR already open: ${pr_info!['url']}`);
1793
+ // One final source: a resumed run re-derives the body from the
1794
+ // branch-head CHANGELOG instead of leaving the plan-time snapshot —
1795
+ // this was the 9.14.0 test-count desync (resume re-rendered from a
1796
+ // later HEAD while the changelog kept the branch's entry).
1797
+ _step(5, total, `PR already open: ${pr_info!['url']} — refresh body from branch head`);
1798
+ _refresh_pr_body_from_head(branch, plan.target);
1712
1799
  } else {
1713
1800
  _step(5, total, 'Open pull request');
1801
+ // Derive the PR body from the CHANGELOG section step 2 just wrote —
1802
+ // the file is the single source; the in-memory plan is only its
1803
+ // author (release-truth Phase 1).
1804
+ const section = extract_changelog_section(fs.readFileSync(CHANGELOG, 'utf-8'), plan.target);
1805
+ if (!section) {
1806
+ die(`CHANGELOG.md carries no section for ${plan.target} — cannot derive PR body`);
1807
+ }
1714
1808
  const pr_changelog = _cap_body(
1715
- plan.changelog_body,
1809
+ section!.body,
1716
1810
  GH_PR_BODY_LIMIT - 200, // leave room for the prefix + footer
1717
1811
  '`CHANGELOG.md` in this PR',
1718
1812
  );
1719
- const pr_body =
1720
- `Release ${plan.target}.\n\n` +
1721
- `${pr_changelog}\n\n` +
1722
- 'Created by `scripts/release.py`.';
1813
+ const pr_body = pr_body_from_section(pr_changelog, plan.target);
1723
1814
  run([
1724
1815
  'gh',
1725
1816
  'pr',
@@ -1770,8 +1861,21 @@ function execute(
1770
1861
  run(['git', 'push', REMOTE, plan.target]);
1771
1862
  }
1772
1863
  } else {
1773
- _step(8, total, `Tag merge commit and push ${plan.target}`);
1774
- run(['git', 'tag', plan.target]);
1864
+ // Sequencing is load-bearing (release-truth Phase 1, council
1865
+ // 2026-08-03): merge FIRST (step 7), pull main (above), THEN derive
1866
+ // the tag message from the MERGED changelog — tagging before the
1867
+ // merge would read a section that does not exist yet. The annotated
1868
+ // tag replaces the previous lightweight one so tag metadata is a
1869
+ // fourth surface carrying the same single-source content.
1870
+ _step(8, total, `Tag merge commit (annotated, from merged CHANGELOG) and push ${plan.target}`);
1871
+ const merged = extract_changelog_section(fs.readFileSync(CHANGELOG, 'utf-8'), plan.target);
1872
+ if (!merged) {
1873
+ die(
1874
+ `CHANGELOG.md on ${MAIN_BRANCH} carries no section for ${plan.target} — ` +
1875
+ 'refusing to tag a release whose changelog entry is missing',
1876
+ );
1877
+ }
1878
+ run(['git', 'tag', '-a', plan.target, '-m', tag_message_from_section(merged!.body, plan.target)]);
1775
1879
  run(['git', 'push', REMOTE, plan.target]);
1776
1880
  }
1777
1881
 
@@ -1786,8 +1890,19 @@ function execute(
1786
1890
  ? 'Create GitHub Release (tag push under GITHUB_TOKEN triggers nothing — dispatching next)'
1787
1891
  : 'Create GitHub Release (triggers publish-npm on the tag)',
1788
1892
  );
1893
+ // One final source: the notes come from the CHANGELOG as recorded AT
1894
+ // THE TAG — never from the plan-time render (whose scope can differ
1895
+ // from the merged content; that was the 9.14.0 desync).
1896
+ const tagged_changelog = git(['show', `${plan.target}:CHANGELOG.md`], { capture: true });
1897
+ const tagged_section = extract_changelog_section(tagged_changelog, plan.target);
1898
+ if (!tagged_section) {
1899
+ die(
1900
+ `CHANGELOG.md at tag ${plan.target} carries no section for ${plan.target} — ` +
1901
+ 'refusing to publish release notes from a different source',
1902
+ );
1903
+ }
1789
1904
  const notes = _cap_body(
1790
- plan.changelog_body || `Release ${plan.target}`,
1905
+ release_notes_from_section(tagged_section!.body, plan.target),
1791
1906
  GH_RELEASE_NOTES_LIMIT,
1792
1907
  '`CHANGELOG.md`',
1793
1908
  );
@@ -2363,5 +2478,14 @@ export {
2363
2478
  SEMVER_RE,
2364
2479
  _RELEASE_BRANCH_RE,
2365
2480
  MODULE_DOC_FIRST_LINE,
2481
+ // Drill seam (release_drill.ts) — the step machinery under a simulated
2482
+ // git/gh world, so orchestration bugs surface in vitest, not mid-release.
2483
+ execute,
2484
+ _set_exec_override,
2485
+ push_release_branch,
2486
+ merge_release_pr,
2487
+ watch_pr_checks,
2488
+ _pr_merge_state,
2489
+ _MERGE_UPDATE_ROUNDS,
2366
2490
  };
2367
2491
  export type { Args };