Skip to content

canary-rollout: auto-cut a new version when a reusable changes on main (close the last manual seam) #1069

Description

@don-petry

Problem

The canary automation promotes a candidate once it's on <agent>/next, but it never cuts a new version. Today a human runs cut-release.sh <agent> <X.Y.Z> --ref origin/main --channel next --push after a reusable's change merges. That's the last manual seam in an otherwise hands-off pipeline (#1045 armed the promoter; this closes the front end).

Evidence the gap is live: auto-rebase's reusable blob at .github main (ece45480) already differs from what auto-rebase/next points at (27637e50) — un-cut changes are sitting on main.

Goal

When a registered reusable's content on its host main differs from the version currently on next, automatically cut a new immutable <agent>/vX.Y.Z tag at that commit and move <agent>/next onto it — seeding the candidate into the existing soak/promote pipeline. No manual cut-release.sh.

Recommended design — poll-based, on the existing timer (NOT per-merge events)

The soak dwell floors are already 4–12h, so sub-4h latency to enter next buys nothing — and polling on the existing schedule avoids all cross-repo Actions plumbing (the 6 reusables live in .github, but cut-release.sh + canary-rings.json live here in .github-private). Do the detection in .github-private where everything is local, via the App token that already reads every host.

New subcommand canary-rollout.sh autocut [--dry-run], run at the START of the scheduled job (before promote-all). For each registered agent:

  1. Resolve host main HEAD: gh api repos/<host>/commits/<default_branch> --jq .sha.
  2. Reusable blob at main: gh api repos/<host>/contents/<reusable>?ref=<mainsha> --jq .sha (the registry's .agents[a].reusable path).
  3. Reusable blob at the current next candidate commit (same call, ref=<next commit>).
  4. If the blobs differ (and main HEAD ≠ next commit): compute the next version and cut:
    cut-release.sh <agent> <newver> --ref <mainsha> --channel next --push (already cross-repo aware).
  5. Else no-op.

Version bump

Add a --bump <patch|minor|major> mode to cut-release.sh (or a next-version <agent> <level> helper): read the highest existing <agent>/vMAJOR.MINOR.PATCH on the host, bump. Default patch. Override per agent via a registry knob .agents[a].autocut.bump (patch|minor), and/or the merge commit trailer Release-Bump: minor. Keep v1 minimal: patch default + registry knob.

Arming + safety (mirror #1045)

  • Gate behind org variable CANARY_AUTO_CUT (default off — the single kill-switch). Only run autocut when == 'true'.
  • Idempotent: no-op when main's reusable blob == next's (no duplicate cuts); cut-release.sh already refuses to overwrite an existing immutable vX.Y.Z.
  • Only cut from the host's default branch HEAD.
  • Best-effort (|| ::warning), never fails the scheduled run.
  • --dry-run prints the intended cut without pushing.
  • Ordering: autocut → promote-all → sync-issues in the scheduled job. A freshly cut candidate begins soaking the same tick (dwell measured from the new tag; it will SOAK, not promote, because dwell=0 < floor).

Scope / limitations (note in v1)

  • Detection is on the reusable file blob only (the registry reusable path). If the reusable sources a shared lib that changed but the reusable file didn't, this won't detect it — acceptable for v1; note it. (Could extend to a path-set later.)
  • Applies to every registered agent, both hosts (.github reusables + .github-private dev-lead).

Acceptance criteria

  1. autocut cuts a new vX.Y.Z (patch bump) + moves next when a reusable's main blob differs from its next candidate; cross-repo aware; verified against auto-rebase (should cut v2.1.1 from ece45480).
  2. Idempotent — a second run with no new change is a clean no-op.
  3. Gated behind CANARY_AUTO_CUT (default off); --dry-run supported; best-effort.
  4. Runs before promote-all; freshly-cut candidate SOAKs (does not promote) the same tick.
  5. Bump default patch; registry autocut.bump override honored.
  6. bats coverage (version-bump math, blob-diff detection, cut invocation stubbed, dry-run, kill-switch off); bash -n + shellcheck + YAML clean.
  7. Docs: note the closed seam in the canary-rollout header + a one-line registry note.

Alternative (documented, not recommended for v1)

Event-based: a push-to-main path-filtered stub in each host repo calls a .github-private reusable that cuts. Sub-4h latency, but needs cross-repo private-Actions access config + per-host stubs. Only worth it if immediate cut is ever required.

Refs

#1045 (armed promoter — this is its front end) · #495 (epic) · #548 (gate) · #959/#992 (cut-release cross-repo + --promote) · #993 (autonomous promoter design).

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    dev-leadFor dev-lead agent pickupenhancementFeature requests

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions