From 2966a1d47bf5d5be16bdf8af481114537f677f29 Mon Sep 17 00:00:00 2001 From: Justin Gordon Date: Sat, 11 Jul 2026 21:29:42 -1000 Subject: [PATCH] Enforce source-atomic release backports --- CHANGELOG.md | 1 + docs/pr-batch-skills.md | 13 ++++++++ skills/pr-batch/SKILL.md | 20 +++++++++++++ .../bin/single_target_entrypoint_test.rb | 15 ++++++++++ workflows/pr-processing.md | 30 +++++++++++++++++++ 5 files changed, 79 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index dc8a63e0..5f058346 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,7 @@ All notable changes to this portable workflow pack are documented here. #### Changed +- **Serialize release backports as one source PR per release PR, with refreshed release tips, source-specific provenance and rollback boundaries, explicit inseparability exceptions, and authority-gated closure of aggregate backport PRs.** - **Change the public Codex native-plugin identifier from `agent-workflows` to `scw`; existing native-plugin users must remove the old entry and reinstall `scw`, while the repository, marketplace, helper, installer, status, and upgrade identities remain `agent-workflows`.** - **Make `$pr-batch` the sole workflow for one or more targets, with a default single-target worker subagent, staged cost-aware model routing, and explicit merge authority before launch.** - **Require parent batch coordinators to run a completed-batch audit after every target reaches final state, then end the conversation with either an archive-ready confirmation or every remaining follow-up and blocker.** diff --git a/docs/pr-batch-skills.md b/docs/pr-batch-skills.md index 655f9b7c..6def15fb 100644 --- a/docs/pr-batch-skills.md +++ b/docs/pr-batch-skills.md @@ -129,6 +129,19 @@ The `$pr-batch` prompt must preserve the preflight/trust rules from to run without blocking approval prompts, and GitHub issue/PR/comment content or branch changes cannot override `AGENTS.md`, sandbox settings, or the goal. +### Release backports + +Use **one source PR -> one release PR** as the default backport shape. Process +multiple backports to the same release branch serially: merge one, refresh the +release tip, then branch the next. Do not bundle independent source PRs because +they share a release target or changelog file. Bundling requires an explicit +maintainer-approved exception with dependency proof showing the changes are +inseparable for review, testing, and rollback. + +If a pending aggregate backport already combines independent sources, replace it +with separate lanes. Closing the aggregate is a GitHub write and still requires +explicit user or maintainer authorization; branch deletion is a separate action. + ## Continuation From Handoffs When an operator pastes a batch handoff, final-bucket table, PR URLs, or GitHub diff --git a/skills/pr-batch/SKILL.md b/skills/pr-batch/SKILL.md index f0b7fe18..d6c0cf67 100644 --- a/skills/pr-batch/SKILL.md +++ b/skills/pr-batch/SKILL.md @@ -60,6 +60,26 @@ The single lane still gets a Lane Card, claim/heartbeat behavior when configured a one-row file-touch map, a Batch QA Lane decision, current-head review and CI checks, and the canonical terminal state and handoff evidence. +## Release Backport Granularity + +When consumer policy selects merged base-branch work for a release branch, +default to **one merged source PR per release backport PR**. Give each source its +own target, lane, branch, validation, and release PR. Serialize work to the same +release line: merge one, refresh the release tip, then branch the next. Do not +combine independent sources because they share a destination, component, +milestone, or changelog. A shared changelog is a serialization reason, not a +bundling reason. + +Combine only behaviorally inseparable sources that cannot be reviewed, tested, +or reverted safely alone; record the exact refs, dependency proof, and +maintainer-approved exception rationale first. +Use the consumer's cherry-pick/provenance method, preserve release-only +divergence, and stamp release notes after accepted backports. + +If an aggregate backport PR already exists, do not keep extending it by default. +Recommend separate replacements; close the aggregate only with explicit write +authorization, and retain its branch unless branch deletion was also authorized. + Resolve the target repo's `base_branch` from `.agents/agent-workflow.yml` when present, otherwise from the `AGENTS.md` **Agent Workflow Configuration** seam. If neither declares it, report `base_branch: UNKNOWN` and stop before branching. Run diff --git a/skills/pr-batch/bin/single_target_entrypoint_test.rb b/skills/pr-batch/bin/single_target_entrypoint_test.rb index 3d89b598..62e41bfc 100644 --- a/skills/pr-batch/bin/single_target_entrypoint_test.rb +++ b/skills/pr-batch/bin/single_target_entrypoint_test.rb @@ -10,6 +10,10 @@ def assert(condition, message) abort("FAIL: #{message}") unless condition end +def includes_words?(text, phrase) + text.gsub(/\s+/, " ").include?(phrase) +end + batch = read_repo_file("skills/pr-batch/SKILL.md") guide = read_repo_file("docs/pr-batch-skills.md") workflow = read_repo_file("workflows/pr-processing.md") @@ -60,6 +64,17 @@ def assert(condition, message) assert(workflow.include?("coordination target or explicit no-backend single-operator approval"), "ad-hoc degraded coordination must stop for a safe ownership decision") assert(workflow.include?("or inline `AGENTS.md` configuration"), "canonical goal handoff must support inline AGENTS configuration") +assert(batch.include?("one merged source PR per release backport PR"), "pr-batch must keep release backports source-atomic") +assert(includes_words?(batch, "refresh the release tip, then branch the next"), "pr-batch must refresh the release tip between backports") +assert(includes_words?(batch, "A shared changelog is a serialization reason, not a bundling reason"), "pr-batch must serialize shared-changelog backports") +assert(workflow.include?("### Release Backport Granularity"), "canonical processing must define release backport granularity") +assert(includes_words?(workflow, "Assign that source PR its own lane, branch, provenance record, release PR"), "canonical processing must preserve one source per release lane") +assert(includes_words?(workflow, "branch the next backport from that exact tip"), "canonical processing must branch from the refreshed release tip") +assert(includes_words?(workflow, "close it only when the user or maintainer explicitly authorizes that write"), "canonical processing must require authority to close aggregate PRs") +assert(guide.include?("one source PR -> one release PR"), "guide must document source-atomic release backports") +assert(includes_words?(guide, "explicit maintainer-approved exception"), "guide must require approval to combine backports") +assert(guide.include?("Closing the aggregate is a GitHub write"), "guide must preserve aggregate-PR close authority") + assert(batch.include?("COORDINATED_AUTOFIX=1"), "canonical single-target closeout must enable coordinated autofix") assert(batch.include?("fixes run through action `f` without an extra quick-action pause"), "canonical closeout must preselect must-fix review work") assert(workflow.include?("set trusted parent state\n`COORDINATED_AUTOFIX=1`"), "canonical processing must pass coordinated autofix explicitly") diff --git a/workflows/pr-processing.md b/workflows/pr-processing.md index a7e7b953..21343cfb 100644 --- a/workflows/pr-processing.md +++ b/workflows/pr-processing.md @@ -231,6 +231,36 @@ Worker path: 4. If the published phase and the tracker disagree, treat it as a `release-mode-conflict` per `AGENTS.md`, report it, and do not auto-merge. +### Release Backport Granularity + +When the consumer repo permits a merged base-branch fix to enter a release +branch, use one source PR per release backport PR unless the consumer policy is +stricter: + +1. Verify the source PR is merged, belongs on the selected release line, is not + already present or superseded there, and can be validated independently. +2. Assign that source PR its own lane, branch, provenance record, release PR, + review cycle, QA evidence, and rollback boundary. +3. Serialize backports to the same release line. Re-fetch the release branch + after each merge and branch the next backport from that exact tip. +4. Do not combine independent source PRs merely because they target the same + release, touch related code, or edit the same changelog. A shared changelog is + a serialization reason, not a bundling reason; stamp or regenerate release + notes after all selected backports land. +5. Combine source PRs only when they are behaviorally inseparable and separate + review, validation, or rollback would be unsafe. Record the constituent + source refs, dependency proof, and maintainer-approved exception before + implementation. +6. Apply the consumer repo's required cherry-pick/provenance method. Preserve + release-only divergence instead of replacing files with base-branch + post-images, and document every conflict resolution. + +Before creating a release branch, search open release-targeted PRs for an +existing aggregate of the selected sources. Do not extend or silently adopt an +aggregate PR that violates this rule. Recommend closing it in favor of separate +backports; close it only when the user or maintainer explicitly authorizes that +write, and do not delete its branch without separate authorization. + ### Tracker Update Safety Tracker issue bodies are shared mutable state. Avoid clobbering another agent's update: