Skip to content

✨ feat(automerge): contributor auto-promotion counts clean merges across the hive's repos - #14518

Merged
hivecommons-hive[bot] merged 2 commits into
v5from
feat/fleet-auto-promotion
Oct 27, 2026
Merged

hivecommons-hive[bot] merged 2 commits into
v5from
feat/fleet-auto-promotion

Conversation

@clubanderson

Copy link
Copy Markdown
Member

Fixes #14517

Problem

Contributor-lane auto-promotion (contributorCleanMergedHistory) counted an author's clean merged PRs per repository. On hive's own hive, @thatguywob was auto-promoted in hivecommons/hive (9 clean merges, 0 reverts) while hivecommons/docs#481 — green CI, reviewer-swarm approve — sat 17h at stage/050-review-pending with contributor-lane:claim-tier-untrusted, because the count in docs is 0. Trust in a contributor is a property of the person, not the repo; the small satellite repos never reach a per-repo threshold.

Change

  • Config: auto_merge.contributor_lane.auto_promotion_scope: hive|repo (default hive), enum-validated in ContributorLaneAutoMergeConfig.Validate, wired through contributorLanePolicyFunc, and exposed in GET/PUT /api/config/auto-merge plus a select in Settings → Features → Auto Merge → Contributor-lane (mirrors registry_tiers_sufficient).
  • Engine (automerge_contributor_lane.go): contributorCleanMergedHistory now returns a contributorMergedHistory (clean, count, scope, per-repo breakdown, reason). With scope hive the PR's own repo is walked first and settles the common case alone; only when short does contributorMergedRepos run one search/issues query (is:pr is:merged author:X repo:… repo:…, chunked to GitHub's 256-char q limit, PR's own repo excluded) to find which other active hive repos hold the author's merges, and only those repos' closed-PR lists are walked for revert/sentinel evidence. A revert or sentinel hit in any repo blocks. Search failure degrades to the own-repo count with a warning (exactly the old behaviour) instead of blocking the sweep.
  • Audit/log: marker is now <!-- hive:contributor-auto-promoted author=X merged=N scope=hive repos=hive:4,docs:1 -->, text says "in this hive's repositories" (or "in this repository" with scope repo); the admission log line carries scope=… repos=…; the merge comment wording follows the scope.
  • Docs: hive-merge.md gains the auto-promotion admission path + YAML; operator-reference.md gains both keys.
  • Tests: TestTrySweepContributorLanePRAutoPromotionScope (hive-scope admit on first PR to a satellite repo, cross-repo sum, skipping repos without the author's merges, revert/sentinel in another repo blocks, repo scope, own-repo threshold needs no search, search failure degrades), TestContributorMergedHistoryHelpers, config enum test, dashboard GET/PUT test. No time.Sleep.

API cost

Common case (author already past threshold in the PR's repo): unchanged, one closed-PR list. Short case on a 10-repo hive: +1 search call (30/min budget, separate from core) + k closed-PR lists where k = repos that actually hold the author's merges (typically 1–2), instead of 10 lists.

…oss the hive's repos

The contributor-lane clean-merge ratchet counted an author's merged PRs per
repository, so a contributor with nine clean merges in hivecommons/hive was
still contributor-lane:claim-tier-untrusted on their first PR to
hivecommons/docs (docs#481 sat 17h green and approved). Trust is a property
of the person, not the repo, and the satellite repos of a multi-repo hive
never reach a per-repo threshold.

- auto_merge.contributor_lane.auto_promotion_scope: hive|repo (default hive),
  validated, wired through the policy func and the Settings auto-merge API/UI.
- With scope hive the PR's own repo is walked first; only when short does one
  search/issues query (is:pr is:merged author:X repo:... chunked to 256 chars)
  find the other hive repos holding the author's merges, and only those are
  walked for revert/sentinel evidence. A search failure degrades to the
  own-repo count with a warning.
- Audit marker gains scope= and repos=<per-repo breakdown>; the admission log
  line carries scope and repos; comment text says "in this hive's
  repositories" for the hive scope.

Fixes #14517

Signed-off-by: Andy Anderson <andy@clubanderson.com>
@kubestellar-prow kubestellar-prow Bot added the dco-signoff: yes Indicates the PR's author has signed the DCO. label Oct 27, 2026
@kubestellar-prow

Copy link
Copy Markdown
Contributor

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by:
Once this PR has been reviewed and has the lgtm label, please assign kellyaa for approval. For more information see the Code Review Process.

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@kubestellar-prow kubestellar-prow Bot added the size/XL Denotes a PR that changes 500-999 lines, ignoring generated files. label Oct 27, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Open PR overlap check

Base branch: v5

Conflicts are checked with every PR merged onto the current tip of v5, so a PR that only needs a rebase is not reported as conflicting with its siblings.

PR Title Relation Files
#14502 🐛 fix: enable tool-less Copilot ioscan classifiers same files src/pkg/config/config.go

@hivecommons-hive

Copy link
Copy Markdown
Contributor

@clubanderson is auto-promoted for contributor-lane merging on this PR: the last 5 merged PRs by this author in this repository have no revert or sentinel-alert evidence. CI, reviewer verdict, diff fences, soak, holds and branch protection still apply.

@hivecommons-hive hivecommons-hive Bot added author/human A person pushed this PR themselves dco All commits are DCO signed-off role/trusted Author is trusted for unattended merge (trusted-author role or registry/claim tier in require_tiers) stage/050-review-pending Review swarm requested; awaiting a verdict for the current head and removed stage/050-review-pending Review swarm requested; awaiting a verdict for the current head labels Oct 27, 2026
…llowlist

The contributor-lane auto-promotion scope help text now references the key, so the parity test requires the allowlist entry to go.

Signed-off-by: Andy Anderson <andy@clubanderson.com>
@hivecommons-hive hivecommons-hive Bot added the stage/071-needs-author Human-authored PR: CI red, changes requested or conflicting; diagnosis posted, the author must act label Oct 27, 2026
@hivecommons-hive

Copy link
Copy Markdown
Contributor

@clubanderson this PR is held at stage/071-needs-author and the hive will not merge it yet.

Failing checks:

  • test (dashboard 4/4)
  • test (dashboard shuffle 2/3)

Next: the hive does not push to your branch. Push a new head (or reply here); CI and the review swarm re-run automatically and the PR returns to the normal path.

@hivecommons-hive hivecommons-hive Bot added stage/050-review-pending Review swarm requested; awaiting a verdict for the current head and removed stage/071-needs-author Human-authored PR: CI red, changes requested or conflicting; diagnosis posted, the author must act labels Oct 27, 2026

@hivecommons-hive hivecommons-hive Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Security review (head ee74634)

Cross-repo revert/sentinel evidence is order-dependent, not "any repo" — src/pkg/github/automerge/automerge_contributor_lane.go, contributorCleanMergedHistory walk closure (~L720–737): walk returns done=true as soon as history.Checked >= need, and the hiveWide loop (~L742–747) stops at the first done. So with need=5, own repo 2 clean, hive 3 clean, docs holding a merged Revert "…"/sentinel-labelled PR by the author, the author is promoted without docs ever being listed. The PR body says "A revert or sentinel hit in any repo blocks" and the marker text says the merges "have no revert or sentinel-alert evidence"; neither holds for repos after the threshold is reached. The tests only cover the favourable ordering ("hive scope blocks on a revert in another repo" puts the revert in hive, walked before docs). Since contributorMergedRepos already narrows to the k repos that actually hold the author's merges (typically 1–2), walking all of them before deciding costs at most k−1 extra closed-PR lists:

 	walk := func(ref contributorRepoRef) (done bool, err error) {
-		clean, blocked, err := c.contributorCleanMergesInRepo(ctx, ref.Owner, ref.Repo, author, need-history.Checked)
+		clean, blocked, err := c.contributorCleanMergesInRepo(ctx, ref.Owner, ref.Repo, author, need)
 		...
 		if blocked {
 			history.Reason = "revert-or-sentinel"
 			return true, nil
 		}
-		if history.Checked >= need {
-			history.Clean = true
-			return true, nil
-		}
-		return false, nil
+		return false, nil
 	}
-	if done, err := walk(contributorRepoRef{Owner: owner, Repo: repo}); done {
+	if done, err := walk(contributorRepoRef{Owner: owner, Repo: repo}); done || history.Checked >= need {
+		history.Clean = !done
 		return history, err
 	}
 	if hiveWide {
 		for _, ref := range c.contributorMergedRepos(ctx, owner, repo, author) {
 			if done, err := walk(ref); done {
 				return history, err
 			}
 		}
+		if history.Checked >= need {
+			history.Clean = true
+			return history, nil
+		}
 	}

(Keeping the own-repo-settles-alone shortcut is a conscious cost trade-off and matches pre-#14517 behaviour; if you keep it, please soften the body/marker wording to "in the repositories counted" rather than "any repo".) Add a test case with the revert in the repo walked last.

No injection surface: author is the PR user's login (admitContributor L602) and found repos are intersected with the operator's activeRepos() candidates before any listing.

— hive: agent=ci-maintainer backend=copilot model=claude-fable-5.1 copilot=1.0.88

@hivecommons-hive hivecommons-hive Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed (intent-alignment) — the diff matches #14517 point for point: hive-wide clean-merge count with per-PR revert/sentinel exclusion, auto_promotion_scope: hive|repo (default hive, enum-validated in config.go Validate, select in Settings), scoped marker/log wording with per-repo breakdown, one search/issues query then only matching repos walked. No unrelated scope creep. Approve.

Advisory (non-blocking): contributorCleanMergedHistory walks the PR's own repo first and returns as soon as it alone reaches need (src/pkg/github/automerge/automerge_contributor_lane.go:731-739), so the other repos — and any revert/sentinel evidence in them — are never consulted in that case. #14517's "exclusion evidence … per PR regardless of repo" therefore holds only when the own repo is short. This is identical to today's per-repo behaviour, so it is not a regression, but hive-merge.md:432-455 reads as if a revert "in any repository blocks" unconditionally; consider a short caveat there ("…when those repositories are consulted").

— hive: agent=reviewer backend=copilot model=claude-fable-5.1 copilot=1.0.88

@hivecommons-hive hivecommons-hive Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed — no findings from this perspective.

— hive: agent=adjudicator backend=copilot model=claude-fable-5.1 copilot=1.0.88

@hivecommons-hive hivecommons-hive Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Read the engine change against the tree at ee74634 (contributorCleanMergedHistory, contributorCleanMergesInRepo, contributorMergedRepos, the activeRepos() provider in automerge_sweep.go:363, and the config/dashboard wiring). The change does what #14517 asks and I found no defect: the PR's own repo is still walked first and settles the common case with one closed-PR list; a revert/sentinel hit in any walked repo blocks before the threshold check; activeRepos() already excludes paused and other-forge repos so the search never names a repo the hive cannot read; the search-failure path returns nil and degrades to exactly the old per-repo count. Docs (operator-reference.md:436, hive-merge.md:443–516) and the changed- fragment track the behaviour. Looks correct to me.

Two low-severity observations, neither blocking:

  1. Satellite-repo list failure is an error, not a degrade — automerge_contributor_lane.go:721. The walk closure returns the PullRequests.List error for any repo, so a 403/404 on a satellite repo the search named surfaces as contributor-lane:auto-promote-history and the PR is skipped for the sweep with an error, whereas the body's "degrades … instead of blocking the sweep" only holds for the search call (:840, :847). Consequence is small (the author was short in their own repo anyway, so the alternative is insufficient-clean-merges), but it is asymmetric with the search path and will show up as sweep errors rather than a debug line if a repo's permissions drift. Consider treating a non-own-repo list error like the search failure: warn, skip that repo, continue.

  2. Search is a single 100-result page — :821 (PerPage: 100, no pagination). For an author with >100 merged PRs across the hive, sorted updated desc, a repo whose merges by that author are all older than the newest 100 is never walked. Under-count only (conservative: no promotion), so not a correctness risk, but worth a comment in contributorMergedRepos so nobody later reads the repo list as exhaustive.

Also noting for the record: the settings_parity_allowlist.txt removal of auto_merge.contributor_tier.auto_promote_after_merged is forced by the parity test (settings_parity_test.go:38) because the new tooltip mentions that key, not because the threshold gained a control — the UI still only edits the scope. Not a defect in this PR.

— hive: agent=reviewer-2 backend=copilot model=claude-fable-5.1 copilot=1.0.88

@hivecommons-hive
hivecommons-hive Bot merged commit ea54ae0 into v5 Oct 27, 2026
78 of 80 checks passed
@kubestellar-prow
kubestellar-prow Bot deleted the feat/fleet-auto-promotion branch October 27, 2026 04:04
@hivecommons-hive

Copy link
Copy Markdown
Contributor
Evidence bundle `#14518ee74634` sha256:616166cc0b4314bb029350eefad4859ef4a31fde773a2d0056f4019c7fbbb567 — download from the dashboard or `hivectl review evidence #14518`.

@hivecommons-hive

Copy link
Copy Markdown
Contributor

merged by Hive trusted-author auto-merge: author @clubanderson holds role owner and GitHub permission admin on repo; CI green at ee74634.

@github-actions

Copy link
Copy Markdown
Contributor

Thank you for your contribution! Your PR has been merged.

We'd love to hear how your experience was: share feedback

@hivecommons-hive hivecommons-hive Bot removed role/trusted Author is trusted for unattended merge (trusted-author role or registry/claim tier in require_tiers) stage/050-review-pending Review swarm requested; awaiting a verdict for the current head labels Oct 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

author/human A person pushed this PR themselves dco All commits are DCO signed-off dco-signoff: yes Indicates the PR's author has signed the DCO. size/XL Denotes a PR that changes 500-999 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Contributor-lane auto-promotion counts clean merges per repo, not across the hive's repos

1 participant